#!/usr/bin/env python3 """Seed a target project with the workflow's standard files and directories.""" from __future__ import annotations import argparse import json import shutil from datetime import datetime, timezone from pathlib import Path PLAYBOOK_FILES = { "new-project-from-scaffold": "new-project-from-scaffold.md", "existing-project-spec-backfill-and-refactor": "existing-project-spec-backfill-and-refactor.md", "spec-code-alignment-gap-closure": "spec-code-alignment-gap-closure.md", } def copy_file(source: Path, target: Path, force: bool) -> str: if target.exists() and not force: return f"skip {target}" target.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, target) return f"write {target}" def write_text(target: Path, content: str, force: bool) -> str: if target.exists() and not force: return f"skip {target}" target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content.rstrip() + "\n", encoding="utf-8") return f"write {target}" def build_minimal_ralphy_config(playbook: str) -> str: return f"""project: workflow: cc-switch-dev-workflow playbook: {playbook} commands: test: pnpm test lint: pnpm lint build: pnpm build typecheck: pnpm exec tsc --noEmit rules: - Read CLAUDE.md, AGENTS.md, ANALYSIS.md, TODO.yaml, and .workflow/selected-playbook.md before implementation. - Keep one primary stack track; do not introduce a second router, auth, ORM, or styling system. - Keep task titles self-contained with path and reference. - Use handoff summaries instead of forwarding raw chat history. """ def seed_workspace( target_dir: Path, playbook: str, force: bool, include_core_docs: bool = True, include_ralphy_config: bool = True, ) -> list[str]: skill_root = Path(__file__).resolve().parent.parent knowledge_base = skill_root / "references" / "knowledge-base" templates_root = knowledge_base / "templates" playbooks_root = knowledge_base / "playbooks" workflows_root = knowledge_base / "workflows" required_dirs = [ target_dir / ".meetings", target_dir / ".research", target_dir / ".plans", target_dir / ".handoff", target_dir / ".acceptance", target_dir / ".workflow", target_dir / ".decisions", target_dir / ".ralphy", target_dir / "SPECS", ] for directory in required_dirs: directory.mkdir(parents=True, exist_ok=True) writes: list[str] = [] template_map = { templates_root / "spec-template.md": target_dir / "SPECS" / "_template.md", templates_root / "tdd-plan-template.md": target_dir / ".plans" / "_tdd-plan-template.md", templates_root / "agent-handoff-template.md": target_dir / ".handoff" / "_agent-handoff-template.md", templates_root / "acceptance-checklist-template.md": target_dir / ".acceptance" / "_acceptance-checklist.md", playbooks_root / PLAYBOOK_FILES[playbook]: target_dir / ".workflow" / "selected-playbook.md", workflows_root / "README.md": target_dir / ".workflow" / "stage-index.md", knowledge_base / "README.md": target_dir / ".workflow" / "knowledge-base-overview.md", } if include_core_docs: template_map.update( { templates_root / "claude-md-template.md": target_dir / "CLAUDE.md", templates_root / "agents-md-template.md": target_dir / "AGENTS.md", templates_root / "analysis-template.md": target_dir / "ANALYSIS.md", templates_root / "todo-yaml-template.md": target_dir / "TODO.yaml", } ) for source, target in template_map.items(): writes.append(copy_file(source, target, force)) if include_ralphy_config: writes.append(write_text(target_dir / ".ralphy" / "config.yaml", build_minimal_ralphy_config(playbook), force)) return writes def main() -> int: parser = argparse.ArgumentParser(description="Bootstrap a repo with the CC Switch workflow files.") parser.add_argument("--target-dir", required=True, help="Absolute or relative project directory to seed.") parser.add_argument("--playbook", required=True, choices=sorted(PLAYBOOK_FILES)) parser.add_argument("--force", action="store_true", help="Overwrite existing files.") args = parser.parse_args() target_dir = Path(args.target_dir).expanduser().resolve() writes = seed_workspace(target_dir, args.playbook, args.force) manifest = { "skill": "cc-switch-dev-workflow", "playbook": args.playbook, "generated_at": datetime.now(timezone.utc).isoformat(), "target_dir": str(target_dir), } manifest_path = target_dir / ".workflow" / "bootstrap-manifest.json" if not manifest_path.exists() or args.force: manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") writes.append(f"write {manifest_path}") else: writes.append(f"skip {manifest_path}") print("\n".join(writes)) return 0 if __name__ == "__main__": raise SystemExit(main())