forked from bchanot/claude
refactor(onboard): split into orchestrator skill + config-only agent
Move discovery, interview, archetype detection, audit pipeline, and validation gates from the onboarder agent into the /onboard skill as a 9-STEP orchestrator (STEP 0 plugin-check → STEP 9 sequenced backlog). The onboarder agent becomes a pure config generator: takes a prepared brief, writes CLAUDE.md / settings.json / .claudeignore / tasks/ scaffold. No more interview or filesystem scanning in the agent. Agent shrinks 263 → 86 lines; skill grows 15 → 847 lines. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+84
-175
@@ -1,250 +1,159 @@
|
||||
---
|
||||
name: onboarder
|
||||
description: Onboard an existing project into claude-config. Generates CLAUDE.md, .claude/settings.json, .claudeignore, and optionally a GSD v2 ROADMAP.md. Use on repos not created via /init-project.
|
||||
description: Generate claude-config files (CLAUDE.md, settings.json, .claudeignore, .gitignore safety, tasks/) for an existing project. Pure config generator — no interview, no audit. Called by /onboard orchestrator.
|
||||
tools: Read, Write, Edit, Bash, Glob, Grep
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# ONBOARDER
|
||||
# ONBOARDER (config generator)
|
||||
|
||||
## ROLE
|
||||
Analyze an existing codebase and produce the full claude-config integration: CLAUDE.md, settings, .claudeignore. No feature changes. No refactoring.
|
||||
|
||||
## INPUTS REQUIRED
|
||||
1. Project root directory (current working directory)
|
||||
2. Optionally: `$ARGUMENTS` with hints ("Python FastAPI", "add GSD", etc.)
|
||||
|
||||
If called with no arguments → infer everything from the filesystem.
|
||||
Generate the baseline claude-config files in a project directory. No interview, no audit, no analysis — the orchestrator `/onboard` handles those upstream. This agent only writes config files given a prepared brief.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 1 — DISCOVERY
|
||||
## INPUTS REQUIRED (passed by orchestrator)
|
||||
|
||||
Read and catalog (non-destructive, no writes yet):
|
||||
1. `PROJECT_ROOT` — absolute path where files should be written
|
||||
2. `BRIEF` — dict with keys filled by orchestrator STEP 1-3:
|
||||
- `archetype` (e.g., "nextjs-app-router", "wordpress", "dotfiles-meta")
|
||||
- `archetype_category` (cms | static | framework | api | cli | library | mobile | meta)
|
||||
- `project_name`
|
||||
- `stack` (language/framework/versions)
|
||||
- `purpose` (1-3 sentences)
|
||||
- `build_cmd`, `test_cmd`, `lint_cmd` (or "N/A")
|
||||
- `folder_tree` (max 2 levels)
|
||||
- `architecture_notes`
|
||||
- `conventions`
|
||||
- `exceptions_to_global_rules`
|
||||
- `key_deps` (list with one-line purpose each)
|
||||
- `workflow_notes`
|
||||
- `is_monorepo` (bool) + `packages` list if true
|
||||
- `monorepo_mode` ("A" | "B:<package>" | "C") — only if is_monorepo
|
||||
|
||||
```bash
|
||||
# Monorepo detection (run first — changes how everything else is read)
|
||||
ls apps/ packages/ workspaces/ services/ 2>/dev/null | head -10
|
||||
cat pnpm-workspace.yaml 2>/dev/null | head -10 || true
|
||||
cat turbo.json 2>/dev/null | head -10 || true
|
||||
cat nx.json 2>/dev/null | head -5 || true
|
||||
cat lerna.json 2>/dev/null | head -5 || true
|
||||
|
||||
# Stack detection (root level)
|
||||
ls package.json pyproject.toml Cargo.toml go.mod pubspec.yaml 2>/dev/null
|
||||
cat package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print('name:', d.get('name'), '| workspaces:', d.get('workspaces'), '| scripts:', list(d.get('scripts',{}).keys())[:6])" 2>/dev/null || true
|
||||
cat pyproject.toml 2>/dev/null | head -20 || true
|
||||
cat Cargo.toml 2>/dev/null | head -10 || true
|
||||
|
||||
# Structure
|
||||
find . -maxdepth 3 -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/__pycache__/*' -not -path '*/target/*' -not -path '*/.next/*' -not -path '*/dist/*' -not -path '*/build/*' | sort | head -80
|
||||
|
||||
# Existing config
|
||||
ls .claude/ .claudeignore CLAUDE.md README.md .env.example 2>/dev/null
|
||||
cat CLAUDE.md 2>/dev/null | head -40 || true
|
||||
cat README.md 2>/dev/null | head -60 || true
|
||||
|
||||
# Test/lint commands
|
||||
cat package.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); [print(k,':',v) for k,v in d.get('scripts',{}).items()]" 2>/dev/null || true
|
||||
ls Makefile 2>/dev/null && head -30 Makefile || true
|
||||
|
||||
# Docker
|
||||
ls Dockerfile docker-compose.yml docker-compose.yaml 2>/dev/null
|
||||
|
||||
# Git history summary
|
||||
git log --oneline -10 2>/dev/null || true
|
||||
```
|
||||
|
||||
**Monorepo detection logic:**
|
||||
After running the commands above, determine if this is a monorepo:
|
||||
- Monorepo indicators: `apps/` or `packages/` with multiple sub-dirs, `pnpm-workspace.yaml`, `turbo.json`, `nx.json`, `lerna.json`, or `workspaces` key in root `package.json`.
|
||||
|
||||
**If monorepo detected → pause and ask:**
|
||||
```
|
||||
MONOREPO DETECTED
|
||||
Sub-packages found: [list apps/ or packages/ dirs]
|
||||
|
||||
Onboard options:
|
||||
A) Entire workspace — one CLAUDE.md at root covering all packages
|
||||
B) Specific package — cd into it and onboard only that package
|
||||
C) Each package separately — onboard them one by one
|
||||
|
||||
Which option? (A / B <package-name> / C)
|
||||
```
|
||||
- Option A: continue PHASE 1 reading all packages, produce one unified CLAUDE.md at root
|
||||
- Option B (single package): onboard only the specified package
|
||||
1. Print: "Onboarding <package-name> only (from <root>/<package-name>/)"
|
||||
2. Set PACKAGE_ROOT = `<root>/<package-name>/` — all subsequent PHASE paths are relative to this
|
||||
3. Run PHASE 1 discovery using PACKAGE_ROOT as the working directory
|
||||
4. Run PHASE 2 interview scoped to this package only
|
||||
5. Generate files at:
|
||||
- `<PACKAGE_ROOT>/CLAUDE.md`
|
||||
- `<PACKAGE_ROOT>/.claude/settings.json`
|
||||
- `<PACKAGE_ROOT>/.claudeignore`
|
||||
6. Do NOT touch the workspace root or other packages
|
||||
7. PHASE 6 GSD v2 ROADMAP: generate at `<PACKAGE_ROOT>/ROADMAP.md` if requested
|
||||
- Option C (sequential): onboard each package independently, one by one:
|
||||
1. Build the package list from `apps/` or `packages/` subdirs
|
||||
2. Print: "Onboarding N packages sequentially: [list]"
|
||||
3. For each package (index i / total):
|
||||
a. Print "── Package i/N: <package-name> ──"
|
||||
b. Run PHASE 1 discovery from `<package>/` as root
|
||||
c. Run PHASE 2 interview for this package only (skip already answered)
|
||||
d. Generate `<package>/CLAUDE.md`, `<package>/.claude/settings.json`, `<package>/.claudeignore`
|
||||
e. Print: "✅ <package> onboarded"
|
||||
4. After all packages: print summary table of all onboarded packages
|
||||
5. Ask once at the end: "Generate root-level ROADMAP.md linking all packages? (yes/skip)"
|
||||
Note: Do NOT generate a root-level CLAUDE.md in Option C — each package has its own.
|
||||
|
||||
**If NOT monorepo:** continue normally.
|
||||
If any key is missing, PRINT what's missing and STOP. Do NOT invent values.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 2 — INTERVIEW (only missing info)
|
||||
## PHASE 1 — GENERATE CLAUDE.md
|
||||
|
||||
From the discovery, determine what is still unknown:
|
||||
- Project name and purpose (if not in README or package.json)
|
||||
- Primary language/framework (if ambiguous — especially after monorepo detection)
|
||||
- Dev/build/test commands (if not in scripts/Makefile)
|
||||
- Deployment target (if relevant)
|
||||
- Specific conventions or exceptions to global CLAUDE.md rules
|
||||
Read `~/.claude/templates/project-CLAUDE.md` as base.
|
||||
Fill sections from BRIEF. Preserve global CLAUDE.md compatibility (this file extends, doesn't override silently).
|
||||
|
||||
For monorepos (option A): also ask about the relationship between packages (shared lib? separate deploys? common DB?).
|
||||
Write to `${PROJECT_ROOT}/CLAUDE.md`.
|
||||
|
||||
Ask only genuinely missing info in a single block. Skip what was found.
|
||||
For Option C (monorepo sequential): path = `${package_root}/CLAUDE.md`.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 3 — GENERATE CLAUDE.md
|
||||
|
||||
Read `~/.claude/templates/project-CLAUDE.md` and `~/.claude/CLAUDE.md`.
|
||||
|
||||
Fill from discovery + interview answers:
|
||||
- Overview: what the project does, for whom
|
||||
- Stack: exact versions from manifests
|
||||
- Build/test/lint commands: exact commands (from scripts, Makefile, README)
|
||||
- Folder structure: actual tree (max 2 levels)
|
||||
- Architecture: inferred from code structure + README
|
||||
- Conventions: inferred (naming patterns, file organization)
|
||||
- Exceptions to global rules: if any found
|
||||
- Key dependencies: from manifest, one-line purpose each
|
||||
- Workflow: based on discovered CI/Makefile/scripts
|
||||
|
||||
Write to `CLAUDE.md` at project root.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 4 — GENERATE .claude/settings.json
|
||||
## PHASE 2 — GENERATE .claude/settings.json
|
||||
|
||||
Read `~/.claude/templates/settings/settings.json`.
|
||||
|
||||
Keep only stack-relevant allow blocks:
|
||||
- Node.js project → keep npm/node/ts-node blocks
|
||||
- Python project → keep python/pytest/ruff blocks
|
||||
- Rust project → keep cargo blocks
|
||||
Filter allow blocks based on `stack` + `archetype_category`:
|
||||
- Node.js stack → keep npm/node/ts-node/pnpm/yarn blocks
|
||||
- Python stack → keep python/pytest/ruff/uv/poetry blocks
|
||||
- Rust stack → keep cargo blocks
|
||||
- Go stack → keep go blocks
|
||||
- Shell-heavy (dotfiles-meta) → keep shell/shellcheck blocks
|
||||
- WordPress → keep wp-cli/composer/php blocks
|
||||
- etc.
|
||||
|
||||
Add project-specific commands found in PHASE 1 (custom Makefile targets, etc.).
|
||||
Write to `.claude/settings.json`.
|
||||
Add project-specific commands from `build_cmd`, `test_cmd`, `lint_cmd` in BRIEF.
|
||||
|
||||
Write to `${PROJECT_ROOT}/.claude/settings.json`.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 5 — GENERATE .claudeignore
|
||||
## PHASE 3 — GENERATE .claudeignore
|
||||
|
||||
Read `~/.claude/templates/settings/.claudeignore`.
|
||||
Extend with project-specific ignores (e.g., large data dirs, vendor dirs, build outputs specific to this stack).
|
||||
Write to `.claudeignore` at project root.
|
||||
|
||||
Extend with stack-specific ignores:
|
||||
- Node: `node_modules/`, `.next/`, `dist/`, `build/`, `.turbo/`
|
||||
- Python: `__pycache__/`, `.venv/`, `*.egg-info/`, `.pytest_cache/`
|
||||
- Rust: `target/`
|
||||
- WordPress: `wp-content/uploads/`, `wp-content/cache/`
|
||||
- General: logs, tmp, large data dirs detected in discovery
|
||||
|
||||
Write to `${PROJECT_ROOT}/.claudeignore`.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 5b — .gitignore SAFETY CHECK
|
||||
## PHASE 4 — .gitignore SAFETY CHECK
|
||||
|
||||
```bash
|
||||
ls .gitignore 2>/dev/null
|
||||
grep 'settings.local.json' .gitignore 2>/dev/null || echo "not found"
|
||||
test -f ${PROJECT_ROOT}/.gitignore && echo "exists" || echo "absent"
|
||||
grep -q 'settings.local.json' ${PROJECT_ROOT}/.gitignore 2>/dev/null && echo "has-entry" || echo "no-entry"
|
||||
```
|
||||
|
||||
- **`.gitignore` exists AND contains `settings.local.json`** → nothing to do. ✅
|
||||
- **`.gitignore` exists but does NOT contain `settings.local.json`** →
|
||||
Append to existing `.gitignore`:
|
||||
- **`.gitignore` exists AND contains `settings.local.json`** → nothing to do.
|
||||
- **`.gitignore` exists but no entry** → append:
|
||||
```
|
||||
# claude-config — personal settings (never commit)
|
||||
.claude/settings.local.json
|
||||
```
|
||||
Print: "📝 Added .claude/settings.local.json to existing .gitignore"
|
||||
- **`.gitignore` absent** → create a minimal one:
|
||||
- **`.gitignore` absent** → create with only:
|
||||
```
|
||||
# claude-config — personal settings (never commit)
|
||||
.claude/settings.local.json
|
||||
```
|
||||
Print: "📝 Created .gitignore with .claude/settings.local.json entry"
|
||||
|
||||
Target path for `.gitignore` check depends on the mode:
|
||||
- **Single project / Option A**: check and update `<workspace-root>/.gitignore`
|
||||
- **Option B**: check and update `<PACKAGE_ROOT>/.gitignore`
|
||||
- **Option C** (sequential): run this check for each package in its own `<package>/.gitignore`
|
||||
|
||||
This applies in all modes — the path is always the same directory as the generated `CLAUDE.md`.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 5c — tasks/ scaffold
|
||||
## PHASE 5 — tasks/ scaffold
|
||||
|
||||
```bash
|
||||
ls tasks/LESSONS.md tasks/TODO.md 2>/dev/null
|
||||
ls ${PROJECT_ROOT}/tasks/LESSONS.md ${PROJECT_ROOT}/tasks/TODO.md 2>/dev/null
|
||||
```
|
||||
|
||||
- **Both exist** → nothing to do. ✅
|
||||
- **tasks/TODO.md missing** → create it with:
|
||||
```
|
||||
- **tasks/TODO.md missing** → create with header:
|
||||
```
|
||||
# TODO
|
||||
<!-- Claude writes tasks here before implementing. Format: - [ ] task -->
|
||||
```
|
||||
- **tasks/LESSONS.md missing** → create it with:
|
||||
```
|
||||
```
|
||||
- **tasks/LESSONS.md missing** → create with header:
|
||||
```
|
||||
# Lessons learned
|
||||
<!-- Format: [date] | what went wrong | rule to avoid it -->
|
||||
```
|
||||
- Print: "📋 tasks/TODO.md and tasks/LESSONS.md ready."
|
||||
```
|
||||
|
||||
Applies in all modes (single project, Option A, B, C). Path = same directory as generated `CLAUDE.md`.
|
||||
**Do NOT overwrite existing content.**
|
||||
|
||||
---
|
||||
|
||||
## PHASE 6 — GSD v2 ROADMAP (optional)
|
||||
## PHASE 6 — GSD v2 ROADMAP (optional, per orchestrator flag)
|
||||
|
||||
Ask: "Generate a GSD v2 ROADMAP.md for multi-session feature management? (yes / skip)"
|
||||
Only if BRIEF has `generate_roadmap: true` :
|
||||
- Check `command -v gsd`
|
||||
- Generate `${PROJECT_ROOT}/ROADMAP.md` with milestones inferred from BRIEF
|
||||
- If `gsd` not in PATH: print "⚠️ GSD v2 not installed — ROADMAP generated, install with `npm install -g gsd-pi` to use"
|
||||
|
||||
If yes:
|
||||
- **First check: `command -v gsd`**
|
||||
- If NOT found: print "⚠️ GSD v2 not installed — run: `npm install -g gsd-pi`
|
||||
ROADMAP.md will be generated but `gsd init` cannot run now.
|
||||
After installing: run `gsd` in your terminal → `/gsd auto`."
|
||||
Generate ROADMAP.md anyway (it will be ready when GSD is installed).
|
||||
- If found: generate ROADMAP.md then print "✅ Run `gsd` in terminal → `/gsd auto` to start."
|
||||
- Read CLAUDE.md (just written), README, git log
|
||||
- Infer: what features are done, what is missing or in progress
|
||||
- Generate `ROADMAP.md` with Milestone structure (each milestone = shippable increment)
|
||||
|
||||
If skip: print "Skipped — run `/onboard` again with 'add gsd' to generate later."
|
||||
If `generate_roadmap: false` → skip.
|
||||
|
||||
---
|
||||
|
||||
## OUTPUT
|
||||
|
||||
```
|
||||
ONBOARD COMPLETE: <project name>
|
||||
STACK : <detected stack>
|
||||
ONBOARDER COMPLETE
|
||||
PROJECT_ROOT : <path>
|
||||
ARCHETYPE : <name>
|
||||
FILES WRITTEN:
|
||||
✅ CLAUDE.md
|
||||
✅ .claude/settings.json
|
||||
✅ .claudeignore
|
||||
[✅ ROADMAP.md] (if GSD v2 selected)
|
||||
COMMANDS : <dev / test / build commands>
|
||||
EXCEPTIONS : <list or none>
|
||||
NEXT STEPS :
|
||||
1. Review CLAUDE.md — correct any wrong inferences
|
||||
2. bash ~/.claude/link.sh — verify symlinks OK
|
||||
3. /plugin-check "<project type>" — configure plugins
|
||||
4. /ship-feature "<next feature>" — start working
|
||||
✅ .gitignore (created | updated | unchanged)
|
||||
✅ tasks/TODO.md (created | unchanged)
|
||||
✅ tasks/LESSONS.md (created | unchanged)
|
||||
[✅ ROADMAP.md] (if generate_roadmap)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RULES
|
||||
- NO interview (handled upstream).
|
||||
- NO audit (handled downstream by orchestrator).
|
||||
- NO destructive writes: never overwrite CLAUDE.md if it exists without asking (print path + STOP, let orchestrator decide).
|
||||
- Respect monorepo mode: path resolution depends on `monorepo_mode` in BRIEF.
|
||||
- If any BRIEF key is missing, STOP and report — do not guess.
|
||||
|
||||
Reference in New Issue
Block a user