claude-skills/engineering/skills/agent-workflow-designer/scripts/workflow_scaffolder.py
Reza Rezvani 1851c8fb09 fix(plugins): restructure 9 multi-skill domain plugins into ./skills/ layout
Same root cause as #587/#591 — Claude Code's runtime loader rejects
array-form skills paths like ["./content-production", "./ai-seo", ...]
even when each entry is a valid subdirectory containing SKILL.md.
`claude plugin validate` accepts them but the loader does not.

The proven canonical layout (used by self-improving-agent in #536):

  <plugin>/
  ├── .claude-plugin/plugin.json    skills: "./skills"
  └── skills/
      ├── <skill-1>/SKILL.md
      ├── <skill-2>/SKILL.md
      └── ...

Restructured 9 multi-skill domain plugins:
- business-growth (4 skills moved)
- c-level-advisor (28)
- engineering (36)
- engineering-team (32)
- finance (2)
- marketing-skill (43)
- product-team (12)
- project-management (8)
- ra-qm-team (13)

Also fixed standalone plugins that had root SKILL.md + ./skills/ subdir
(agenthub, autoresearch-agent, executive-mentor, playwright-pro). The
loader rejected them despite skills="./skills" because of the conflicting
root SKILL.md (compare self-improving-agent which works because PR #536
moved its root SKILL.md). Moved each root SKILL.md into ./skills/<name>/.

Restored standalone plugin folders to their original paths after the
multi-skill restructure swept them into parent skills/ directories
(marketplace.json source paths require original locations).

Removed 7 orphaned marketplace entries that pointed to skill folders
without their own plugin.json (content-creator, demand-gen,
fullstack-engineer, aws-architect, product-manager, scrum-master,
skill-security-auditor) — these were already non-functional.

Bumped patch versions on every changed plugin and synced
marketplace.json. Marketplace now lists 29 working plugins (down
from 36).

After merge: users run `/plugin marketplace update claude-code-skills`
followed by `/plugin update --all` to pick up the working layout.
2026-05-02 22:51:20 +02:00

113 lines
3.3 KiB
Python
Executable file

#!/usr/bin/env python3
"""Generate workflow skeleton configs from common multi-agent patterns."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Dict, List
def sequential_template(name: str) -> Dict:
return {
"name": name,
"pattern": "sequential",
"steps": [
{"id": "research", "agent": "researcher", "next": "draft"},
{"id": "draft", "agent": "writer", "next": "review"},
{"id": "review", "agent": "reviewer", "next": None},
],
"retry": {"max_attempts": 2, "backoff_seconds": 2},
}
def parallel_template(name: str) -> Dict:
return {
"name": name,
"pattern": "parallel",
"fan_out": {
"tasks": ["research_a", "research_b", "research_c"],
"agent": "analyst",
},
"fan_in": {"agent": "synthesizer", "output": "combined_report"},
"timeouts": {"per_task_seconds": 180, "fan_in_seconds": 120},
}
def router_template(name: str) -> Dict:
return {
"name": name,
"pattern": "router",
"router": {"agent": "router", "routes": ["sales", "support", "engineering"]},
"handlers": {
"sales": {"agent": "sales_specialist"},
"support": {"agent": "support_specialist"},
"engineering": {"agent": "engineering_specialist"},
},
"fallback": {"agent": "generalist"},
}
def orchestrator_template(name: str) -> Dict:
return {
"name": name,
"pattern": "orchestrator",
"orchestrator": {"agent": "orchestrator", "planning": "dynamic"},
"specialists": ["researcher", "coder", "analyst", "writer"],
"execution": {
"dependency_mode": "dag",
"max_parallel": 3,
"completion_policy": "all_required",
},
}
def evaluator_template(name: str) -> Dict:
return {
"name": name,
"pattern": "evaluator",
"generator": {"agent": "generator"},
"evaluator": {"agent": "evaluator", "criteria": ["accuracy", "format", "safety"]},
"loop": {
"max_iterations": 3,
"pass_threshold": 0.8,
"on_fail": "revise_and_retry",
},
}
PATTERNS = {
"sequential": sequential_template,
"parallel": parallel_template,
"router": router_template,
"orchestrator": orchestrator_template,
"evaluator": evaluator_template,
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Generate a workflow skeleton config from a pattern.")
parser.add_argument("pattern", choices=sorted(PATTERNS.keys()), help="Workflow pattern")
parser.add_argument("--name", default="new-workflow", help="Workflow name")
parser.add_argument("--output", help="Optional output path for JSON config")
return parser.parse_args()
def main() -> int:
args = parse_args()
config = PATTERNS[args.pattern](args.name)
payload = json.dumps(config, indent=2)
if args.output:
out = Path(args.output)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(payload + "\n", encoding="utf-8")
print(f"Wrote workflow config to {out}")
else:
print(payload)
return 0
if __name__ == "__main__":
raise SystemExit(main())