ragarciaruben 1.20.26 → 1.20.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/install.js +8 -1
- package/get-shit-done/templates/opencode/agents/codebase-mapper.md +94 -0
- package/get-shit-done/templates/opencode/agents/executor.md +2 -3
- package/get-shit-done/templates/opencode/agents/orchestrator.md +128 -0
- package/get-shit-done/templates/opencode/agents/planner.md +0 -1
- package/get-shit-done/templates/opencode/agents/product-owner.md +0 -1
- package/get-shit-done/templates/opencode/agents/verifier.md +0 -1
- package/get-shit-done/templates/opencode/commands/execute-phase.md +163 -64
- package/get-shit-done/templates/opencode/commands/map-codebase.md +96 -50
- package/get-shit-done/templates/opencode/tools/plan-index.ts +196 -0
- package/get-shit-done/templates/opencode.json +1 -1
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -160,6 +160,7 @@ const SRC = {
|
|
|
160
160
|
opencodeJson: path.join(PKG_ROOT, 'get-shit-done', 'templates', 'opencode.json'),
|
|
161
161
|
opencodeAgents: path.join(PKG_ROOT, 'get-shit-done', 'templates', 'opencode', 'agents'),
|
|
162
162
|
opencodeCommands: path.join(PKG_ROOT, 'get-shit-done', 'templates', 'opencode', 'commands'),
|
|
163
|
+
opencodeTools: path.join(PKG_ROOT, 'get-shit-done', 'templates', 'opencode', 'tools'),
|
|
163
164
|
|
|
164
165
|
// Shared
|
|
165
166
|
planning: path.join(PKG_ROOT, '.planning'),
|
|
@@ -368,7 +369,12 @@ function installOpencode() {
|
|
|
368
369
|
copyDir(SRC.opencodeCommands, path.join(TARGET, '.opencode', 'commands'), results);
|
|
369
370
|
}
|
|
370
371
|
|
|
371
|
-
// 5. .
|
|
372
|
+
// 5. .opencode/tools/
|
|
373
|
+
if (fs.existsSync(SRC.opencodeTools)) {
|
|
374
|
+
copyDir(SRC.opencodeTools, path.join(TARGET, '.opencode', 'tools'), results);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 6. .planning/
|
|
372
378
|
if (fs.existsSync(SRC.planning)) {
|
|
373
379
|
copyDir(SRC.planning, path.join(TARGET, '.planning'), results);
|
|
374
380
|
} else {
|
|
@@ -412,6 +418,7 @@ function uninstallOpencode() {
|
|
|
412
418
|
if (removeFile(path.join(TARGET, 'opencode.json'))) removed++;
|
|
413
419
|
if (removeDir(path.join(TARGET, '.opencode', 'agents'))) removed++;
|
|
414
420
|
if (removeDir(path.join(TARGET, '.opencode', 'commands'))) removed++;
|
|
421
|
+
if (removeDir(path.join(TARGET, '.opencode', 'tools'))) removed++;
|
|
415
422
|
|
|
416
423
|
console.log(removed > 0
|
|
417
424
|
? c.green(` ✓ Removed ${removed} OpenCode items`)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Analyzes a specific focus area of the codebase and writes structured documents to .planning/codebase/. Invoked by the orchestrator for parallel codebase mapping.
|
|
3
|
+
mode: subagent
|
|
4
|
+
hidden: true
|
|
5
|
+
tools:
|
|
6
|
+
write: true
|
|
7
|
+
edit: true
|
|
8
|
+
bash: true
|
|
9
|
+
permission:
|
|
10
|
+
bash:
|
|
11
|
+
"*": ask
|
|
12
|
+
"grep *": allow
|
|
13
|
+
"find *": allow
|
|
14
|
+
"cat *": allow
|
|
15
|
+
"ls *": allow
|
|
16
|
+
"head *": allow
|
|
17
|
+
"tail *": allow
|
|
18
|
+
"wc *": allow
|
|
19
|
+
"git log*": allow
|
|
20
|
+
"git status*": allow
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Codebase Mapper Agent
|
|
24
|
+
|
|
25
|
+
I am a codebase analysis specialist. I explore a specific focus area of the codebase and **write structured documents directly** to `.planning/codebase/`. I return only confirmation with file paths and line counts — no document content.
|
|
26
|
+
|
|
27
|
+
## Focus Areas
|
|
28
|
+
|
|
29
|
+
I handle one of 4 focus areas per invocation:
|
|
30
|
+
|
|
31
|
+
### Focus: tech
|
|
32
|
+
**Explore:**
|
|
33
|
+
```bash
|
|
34
|
+
cat package.json 2>/dev/null | head -100
|
|
35
|
+
cat requirements.txt 2>/dev/null || cat pyproject.toml 2>/dev/null | head -50
|
|
36
|
+
cat go.mod 2>/dev/null | head -30
|
|
37
|
+
ls -la *.config.* tsconfig.json .nvmrc .python-version 2>/dev/null
|
|
38
|
+
ls .env* 2>/dev/null # Note existence only — NEVER read .env contents
|
|
39
|
+
grep -r "import.*stripe\|import.*supabase\|import.*aws\|import.*prisma\|import.*redis\|import.*sendgrid\|import.*twilio\|import.*openai" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -40
|
|
40
|
+
```
|
|
41
|
+
**Write:** `.planning/codebase/STACK.md` + `.planning/codebase/INTEGRATIONS.md`
|
|
42
|
+
|
|
43
|
+
### Focus: arch
|
|
44
|
+
**Explore:**
|
|
45
|
+
```bash
|
|
46
|
+
find . -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/.planning/*' -not -path '*/.opencode/*' | sort | head -60
|
|
47
|
+
ls src/index.* src/main.* src/app.* src/server.* app/page.* 2>/dev/null
|
|
48
|
+
grep -r "^import\|^from" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -80
|
|
49
|
+
```
|
|
50
|
+
**Write:** `.planning/codebase/ARCHITECTURE.md` + `.planning/codebase/STRUCTURE.md`
|
|
51
|
+
|
|
52
|
+
### Focus: quality
|
|
53
|
+
**Explore:**
|
|
54
|
+
```bash
|
|
55
|
+
cat .prettierrc .eslintrc.js .eslintrc.json biome.json 2>/dev/null | head -60
|
|
56
|
+
cat pyproject.toml 2>/dev/null | grep -A 20 "\[tool\."
|
|
57
|
+
ls jest.config.* vitest.config.* pytest.ini setup.cfg 2>/dev/null
|
|
58
|
+
find . -name "*.test.*" -o -name "*.spec.*" | grep -v node_modules | head -20
|
|
59
|
+
find src/ -name "*.ts" -not -name "*.test.*" 2>/dev/null | head -5 | xargs head -40 2>/dev/null
|
|
60
|
+
find tests/ -name "*.test.*" 2>/dev/null | head -3 | xargs head -60 2>/dev/null
|
|
61
|
+
```
|
|
62
|
+
**Write:** `.planning/codebase/CONVENTIONS.md` + `.planning/codebase/TESTING.md`
|
|
63
|
+
|
|
64
|
+
### Focus: concerns
|
|
65
|
+
**Explore:**
|
|
66
|
+
```bash
|
|
67
|
+
grep -rn "TODO\|FIXME\|HACK\|XXX\|TEMP\|@deprecated" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -40
|
|
68
|
+
grep -rn "any\b\|@ts-ignore\|@ts-nocheck\|eslint-disable\|# type: ignore\|# noqa" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -30
|
|
69
|
+
grep -rn "eval\|innerHTML\|dangerouslySetInnerHTML\|exec\|shell=True" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -20
|
|
70
|
+
find src/ -name "*.ts" -o -name "*.py" 2>/dev/null | xargs wc -l 2>/dev/null | sort -rn | head -15
|
|
71
|
+
```
|
|
72
|
+
**Write:** `.planning/codebase/CONCERNS.md`
|
|
73
|
+
|
|
74
|
+
## My Rules
|
|
75
|
+
|
|
76
|
+
- **Write documents directly** — don't return content to the orchestrator
|
|
77
|
+
- **Include file paths** — always use backtick-formatted paths: `src/services/user.ts`
|
|
78
|
+
- **Include real code examples** — especially in CONVENTIONS.md
|
|
79
|
+
- **Be prescriptive** — state what the pattern IS, not what it might be
|
|
80
|
+
- **NEVER read .env file contents** — only note existence
|
|
81
|
+
|
|
82
|
+
## Return Format
|
|
83
|
+
|
|
84
|
+
When done, return ONLY:
|
|
85
|
+
```
|
|
86
|
+
## Mapping Complete
|
|
87
|
+
|
|
88
|
+
**Focus:** {focus}
|
|
89
|
+
**Documents written:**
|
|
90
|
+
- `.planning/codebase/{DOC1}.md` ({N} lines)
|
|
91
|
+
- `.planning/codebase/{DOC2}.md` ({N} lines)
|
|
92
|
+
|
|
93
|
+
Ready for orchestrator summary.
|
|
94
|
+
```
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Implements planned phases — writes code, runs tests, and makes atomic commits following established conventions.
|
|
3
|
-
mode:
|
|
4
|
-
model: anthropic/claude-sonnet-4-20250514
|
|
2
|
+
description: Implements planned phases — writes code, runs tests, and makes atomic commits following established conventions. Invoked by orchestrator for parallel plan execution or used directly for single plans.
|
|
3
|
+
mode: all
|
|
5
4
|
tools:
|
|
6
5
|
write: true
|
|
7
6
|
edit: true
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Coordinates phase execution by discovering plans, grouping into waves, and spawning parallel executor subagents. Use me to run a full phase with wave-based execution.
|
|
3
|
+
mode: primary
|
|
4
|
+
tools:
|
|
5
|
+
write: true
|
|
6
|
+
edit: true
|
|
7
|
+
bash: true
|
|
8
|
+
permission:
|
|
9
|
+
task:
|
|
10
|
+
"executor": "allow"
|
|
11
|
+
"verifier": "allow"
|
|
12
|
+
"codebase-mapper": "allow"
|
|
13
|
+
"*": "deny"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Orchestrator Agent
|
|
17
|
+
|
|
18
|
+
I am a coordination specialist. I **never write implementation code** — I discover plans, group them into dependency waves, spawn executor subagents in parallel, collect results, and verify outcomes.
|
|
19
|
+
|
|
20
|
+
## Core Principle
|
|
21
|
+
|
|
22
|
+
**Orchestrator coordinates, not executes.** Each subagent gets fresh context. I stay lean (~10-15% context) by passing **file paths only** — subagents read files themselves.
|
|
23
|
+
|
|
24
|
+
## How I Execute a Phase
|
|
25
|
+
|
|
26
|
+
### 1. Initialize
|
|
27
|
+
|
|
28
|
+
Read `.planning/ROADMAP.md` to find the target phase and its plans. Read `.planning/STATE.md` for current position.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ls .planning/phases/[NN]-[phase-name]/*-PLAN.md 2>/dev/null | sort
|
|
32
|
+
ls .planning/phases/[NN]-[phase-name]/*-SUMMARY.md 2>/dev/null | sort
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Discover & Group Plans
|
|
36
|
+
|
|
37
|
+
For each plan file, read its frontmatter to find `wave:` and `depends_on:` fields.
|
|
38
|
+
|
|
39
|
+
**Wave assignment rules:**
|
|
40
|
+
- Plans with no `depends_on` → Wave 1
|
|
41
|
+
- Plans depending on Wave 1 plans → Wave 2
|
|
42
|
+
- Wave N = max(wave of dependencies) + 1
|
|
43
|
+
- If no `wave:` frontmatter, treat all plans as Wave 1 (sequential fallback)
|
|
44
|
+
|
|
45
|
+
**Skip completed plans:** Any plan that already has a matching SUMMARY.md is skipped.
|
|
46
|
+
|
|
47
|
+
Report the execution plan:
|
|
48
|
+
```
|
|
49
|
+
## Execution Plan
|
|
50
|
+
|
|
51
|
+
**Phase {X}: {Name}** — {total} plans across {wave_count} waves
|
|
52
|
+
|
|
53
|
+
| Wave | Plans | What it builds |
|
|
54
|
+
|------|-------|----------------|
|
|
55
|
+
| 1 | 01-01, 01-02 | {from plan objectives} |
|
|
56
|
+
| 2 | 01-03 | {depends on wave 1 outputs} |
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Execute Waves
|
|
60
|
+
|
|
61
|
+
Execute each wave in sequence. **Within a wave: spawn all executors in parallel** using the Task tool.
|
|
62
|
+
|
|
63
|
+
**For each wave:**
|
|
64
|
+
|
|
65
|
+
1. **Describe what's being built** — read each plan's objective, explain what it does and why it matters
|
|
66
|
+
|
|
67
|
+
2. **Spawn `@executor` for each plan in the wave:**
|
|
68
|
+
|
|
69
|
+
Pass paths only — executors read files themselves with their fresh context.
|
|
70
|
+
|
|
71
|
+
For each plan, invoke `@executor` with:
|
|
72
|
+
```
|
|
73
|
+
Execute plan {plan_number} of phase {phase_number}-{phase_name}.
|
|
74
|
+
|
|
75
|
+
Files to read at start:
|
|
76
|
+
- {phase_dir}/{plan_file} (the plan)
|
|
77
|
+
- .planning/STATE.md (current state)
|
|
78
|
+
- .planning/codebase/CONVENTIONS.md (coding standards)
|
|
79
|
+
- .planning/codebase/STRUCTURE.md (file placement)
|
|
80
|
+
- .planning/codebase/CONCERNS.md (fragile areas)
|
|
81
|
+
|
|
82
|
+
Success criteria:
|
|
83
|
+
- All tasks executed per the plan
|
|
84
|
+
- Tests pass after each task
|
|
85
|
+
- Atomic commit per completed plan
|
|
86
|
+
- ROADMAP.md updated with plan progress
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
3. **Wait for all agents in wave to complete**
|
|
90
|
+
|
|
91
|
+
4. **Spot-check each completed plan:**
|
|
92
|
+
- Verify SUMMARY.md or commit exists: `git log --oneline --grep="{phase}-{plan}" | head -3`
|
|
93
|
+
- Check test suite still passes: `npm test 2>&1 | tail -5`
|
|
94
|
+
|
|
95
|
+
5. **Report wave completion** — what was built, notable deviations, what it enables for next wave
|
|
96
|
+
|
|
97
|
+
6. **Proceed to next wave**
|
|
98
|
+
|
|
99
|
+
### 4. Post-Execution
|
|
100
|
+
|
|
101
|
+
After all waves complete:
|
|
102
|
+
- Invoke `@verifier` to verify the phase against requirements
|
|
103
|
+
- Report aggregate results
|
|
104
|
+
|
|
105
|
+
## Spot-Check Protocol
|
|
106
|
+
|
|
107
|
+
After each wave, for each completed plan:
|
|
108
|
+
- Check `git log --oneline --grep="{plan-id}"` returns ≥1 commit
|
|
109
|
+
- Run `npm test` to confirm no regressions
|
|
110
|
+
- If any check fails: report which plan failed, ask "Retry?" or "Continue?"
|
|
111
|
+
|
|
112
|
+
## Failure Handling
|
|
113
|
+
|
|
114
|
+
- **Agent fails mid-plan:** Report which plan failed, ask user how to proceed
|
|
115
|
+
- **Dependency chain breaks:** Wave N fails → Wave N+1 dependents likely fail → user chooses attempt or skip
|
|
116
|
+
- **All agents in wave fail:** Stop, report for investigation
|
|
117
|
+
|
|
118
|
+
## Resumption
|
|
119
|
+
|
|
120
|
+
Re-running skips completed plans (those with SUMMARY.md). I resume from the first incomplete plan in the first incomplete wave.
|
|
121
|
+
|
|
122
|
+
## Context Efficiency
|
|
123
|
+
|
|
124
|
+
I stay at ~10-15% context usage. Subagents get fresh context each. No content transfer back — I only read confirmations and check file existence.
|
|
125
|
+
|
|
126
|
+
## Trigger Phrases
|
|
127
|
+
|
|
128
|
+
"Execute phase [N]", "Run phase [N] with parallel agents", "Orchestrate phase [N]"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Creates and refines Jira tickets from ideas, epics, or requirements — turns vague requests into well-structured specs. Use me to define WHAT to build.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model: anthropic/claude-sonnet-4-20250514
|
|
5
4
|
tools:
|
|
6
5
|
write: false
|
|
7
6
|
edit: false
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Verifies completed work against requirements — checks tests, inspects implementation, and produces a verification report. Use me after implementing a phase.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model: anthropic/claude-sonnet-4-20250514
|
|
5
4
|
tools:
|
|
6
5
|
write: true
|
|
7
6
|
edit: true
|
|
@@ -1,120 +1,219 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Execute a
|
|
3
|
-
agent:
|
|
2
|
+
description: Execute all plans in a phase using wave-based parallel execution with the orchestrator agent
|
|
3
|
+
agent: orchestrator
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Execute Phase
|
|
7
7
|
|
|
8
8
|
**Usage:** `/execute-phase [phase-number]` — e.g., `/execute-phase 1`
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Execute a planned phase using wave-based parallel execution. The orchestrator discovers plans, groups them by dependency waves, and spawns executor subagents in parallel.
|
|
11
11
|
|
|
12
|
-
## Step 1:
|
|
12
|
+
## Step 1: Initialize
|
|
13
13
|
|
|
14
|
-
Read
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
Read all context in a single pass:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
ls .planning/phases/ 2>/dev/null | sort
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Find the target phase directory matching `$ARGUMENTS`. Then load:
|
|
21
|
+
- `.planning/ROADMAP.md` — phase structure and success criteria
|
|
20
22
|
- `.planning/STATE.md` — current position and any blockers
|
|
21
23
|
|
|
22
24
|
If `.planning/continue-here.md` exists, read it first — it tells you exactly where to resume.
|
|
23
25
|
|
|
24
|
-
## Step 2:
|
|
26
|
+
## Step 2: Discover & Group Plans
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
```bash
|
|
29
|
+
# List all plans and completed summaries
|
|
30
|
+
ls .planning/phases/[NN]-[phase-name]/*-PLAN.md 2>/dev/null | sort
|
|
31
|
+
ls .planning/phases/[NN]-[phase-name]/*-SUMMARY.md 2>/dev/null | sort
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For each plan file:
|
|
35
|
+
1. Read frontmatter for `wave:` and `depends_on:` fields
|
|
36
|
+
2. **Skip** any plan that already has a matching SUMMARY.md (already complete)
|
|
37
|
+
|
|
38
|
+
**Wave assignment:**
|
|
39
|
+
- Plans with no `depends_on` → **Wave 1**
|
|
40
|
+
- Plans depending on Wave 1 plans → **Wave 2**
|
|
41
|
+
- Wave N = max(wave of dependencies) + 1
|
|
42
|
+
- If no `wave:` frontmatter exists, treat all plans as **Wave 1** (sequential within single wave)
|
|
43
|
+
|
|
44
|
+
Report the execution plan:
|
|
45
|
+
```
|
|
46
|
+
## Execution Plan
|
|
47
|
+
|
|
48
|
+
**Phase {X}: {Name}** — {total} plans across {wave_count} waves
|
|
49
|
+
|
|
50
|
+
| Wave | Plans | What it builds |
|
|
51
|
+
|------|-------|----------------|
|
|
52
|
+
| 1 | 01-01, 01-02 | {from plan objectives} |
|
|
53
|
+
| 2 | 01-03 | {depends on wave 1 outputs} |
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If all plans have SUMMARY.md: "All plans already complete. Run `/verify-work {phase}` to verify."
|
|
57
|
+
|
|
58
|
+
## Step 3: Pre-flight Check
|
|
59
|
+
|
|
60
|
+
Before spawning any agents:
|
|
27
61
|
```bash
|
|
28
62
|
npm test 2>&1 | tail -20
|
|
29
|
-
npx tsc --noEmit 2>&1 | head -20
|
|
30
63
|
git status
|
|
31
64
|
git branch --show-current
|
|
32
65
|
```
|
|
33
66
|
|
|
34
|
-
If tests are already failing
|
|
67
|
+
If tests are already failing, note them in STATE.md and confirm with user before proceeding.
|
|
35
68
|
|
|
36
|
-
## Step
|
|
69
|
+
## Step 4: Execute Waves
|
|
37
70
|
|
|
38
|
-
|
|
71
|
+
Execute each wave in sequence. **Within a wave: spawn all executor subagents in parallel.**
|
|
39
72
|
|
|
40
|
-
###
|
|
73
|
+
### For each wave:
|
|
41
74
|
|
|
42
|
-
|
|
75
|
+
#### 4a. Describe what's being built (BEFORE spawning)
|
|
43
76
|
|
|
44
|
-
|
|
77
|
+
Read each plan's objective. Extract what's being built and why.
|
|
45
78
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Before touching any file listed in `.planning/codebase/CONCERNS.md` fragile areas — re-read that section
|
|
50
|
-
- Write tests for each piece of functionality before moving to the next
|
|
79
|
+
```
|
|
80
|
+
---
|
|
81
|
+
## Wave {N}
|
|
51
82
|
|
|
52
|
-
|
|
83
|
+
**{Plan ID}: {Plan Name}**
|
|
84
|
+
{2-3 sentences: what this builds, technical approach, why it matters}
|
|
53
85
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
npx tsc --noEmit
|
|
86
|
+
Spawning {count} agent(s)...
|
|
87
|
+
---
|
|
57
88
|
```
|
|
58
89
|
|
|
59
|
-
|
|
90
|
+
- Bad: "Executing terrain generation plan"
|
|
91
|
+
- Good: "Procedural terrain generator using Perlin noise — creates height maps, biome zones, and collision meshes. Required before vehicle physics can interact with ground."
|
|
60
92
|
|
|
61
|
-
|
|
93
|
+
#### 4b. Spawn `@executor` for each plan
|
|
62
94
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
Pass paths only — executors read files themselves with their fresh context. This keeps orchestrator context lean (~10-15%).
|
|
96
|
+
|
|
97
|
+
For each plan in the wave, invoke `@executor` with:
|
|
66
98
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
```
|
|
100
|
+
Execute plan {plan_number} of phase {phase_number}-{phase_name}.
|
|
101
|
+
Commit each task atomically. Update ROADMAP.md with plan progress.
|
|
102
|
+
|
|
103
|
+
Files to read at execution start:
|
|
104
|
+
- {phase_dir}/{plan_file} (the plan)
|
|
105
|
+
- .planning/STATE.md (current state)
|
|
106
|
+
- .planning/codebase/CONVENTIONS.md (coding standards)
|
|
107
|
+
- .planning/codebase/STRUCTURE.md (file placement)
|
|
108
|
+
- .planning/codebase/CONCERNS.md (fragile areas)
|
|
109
|
+
- .planning/codebase/TESTING.md (test patterns)
|
|
110
|
+
|
|
111
|
+
Success criteria:
|
|
112
|
+
- [ ] All tasks executed per the plan
|
|
113
|
+
- [ ] Tests pass after each task
|
|
114
|
+
- [ ] Atomic commit per completed plan
|
|
115
|
+
- [ ] ROADMAP.md updated — mark plan as [x]
|
|
70
116
|
```
|
|
71
117
|
|
|
72
|
-
|
|
118
|
+
**Spawn ALL plans in the wave simultaneously** — multiple `@executor` invocations in a single turn for parallel execution.
|
|
73
119
|
|
|
74
|
-
|
|
120
|
+
#### 4c. Wait for all agents in wave to complete
|
|
75
121
|
|
|
76
|
-
|
|
122
|
+
#### 4d. Spot-check completed plans
|
|
77
123
|
|
|
78
|
-
|
|
124
|
+
For each completed plan:
|
|
125
|
+
- Check `git log --oneline --grep="{plan-id}" | head -3` returns ≥1 commit
|
|
126
|
+
- Run `npm test 2>&1 | tail -5` to confirm no regressions
|
|
79
127
|
|
|
80
|
-
|
|
81
|
-
npm test
|
|
82
|
-
npm run test:coverage
|
|
83
|
-
npx tsc --noEmit
|
|
84
|
-
npm run lint
|
|
128
|
+
If ANY spot-check fails:
|
|
85
129
|
```
|
|
130
|
+
⚠ Plan {plan-id} spot-check failed:
|
|
131
|
+
- Missing commit / Tests failing
|
|
86
132
|
|
|
87
|
-
|
|
133
|
+
Retry this plan? | Continue with remaining waves? | Stop?
|
|
134
|
+
```
|
|
88
135
|
|
|
89
|
-
|
|
136
|
+
#### 4e. Report wave completion
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
---
|
|
140
|
+
## Wave {N} Complete
|
|
141
|
+
|
|
142
|
+
**{Plan ID}: {Plan Name}**
|
|
143
|
+
{What was built — substantive description}
|
|
144
|
+
{Notable deviations, if any}
|
|
145
|
+
|
|
146
|
+
{If more waves: what this enables for next wave}
|
|
147
|
+
---
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- Bad: "Wave 2 complete. Proceeding to Wave 3."
|
|
151
|
+
- Good: "Auth system complete — JWT tokens, refresh flow, role-based middleware. API routes (Wave 3) can now enforce authentication."
|
|
152
|
+
|
|
153
|
+
#### 4f. Proceed to next wave
|
|
154
|
+
|
|
155
|
+
## Step 5: Post-Phase Verification
|
|
156
|
+
|
|
157
|
+
After all waves complete, invoke `@verifier`:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Verify phase {phase_number} goal achievement.
|
|
161
|
+
Phase directory: {phase_dir}
|
|
162
|
+
Phase goal: {goal from ROADMAP.md}
|
|
163
|
+
Check requirements against actual codebase.
|
|
164
|
+
Create VERIFICATION.md.
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| Verifier Result | Action |
|
|
168
|
+
|----------------|--------|
|
|
169
|
+
| **passed** | Update roadmap, mark phase complete |
|
|
170
|
+
| **gaps_found** | Present gap summary, suggest `/plan-phase {X} --gaps` |
|
|
171
|
+
|
|
172
|
+
## Step 6: Update State
|
|
90
173
|
|
|
91
174
|
Update `.planning/STATE.md`:
|
|
92
|
-
- Mark phase complete if
|
|
93
|
-
- Update progress percentage
|
|
175
|
+
- Mark phase complete (if verification passed)
|
|
94
176
|
- Advance to next phase
|
|
95
177
|
|
|
96
178
|
Update `.planning/ROADMAP.md`:
|
|
97
179
|
- Mark phase checkbox: `- [x] **Phase N: ...**`
|
|
180
|
+
- Record completion date
|
|
98
181
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
2. Run `/pause-work` to save session state
|
|
104
|
-
3. Describe the blocker clearly for the user to resolve
|
|
182
|
+
```bash
|
|
183
|
+
git add -A
|
|
184
|
+
git commit -m "docs(phase-{X}): complete phase execution"
|
|
185
|
+
```
|
|
105
186
|
|
|
106
|
-
##
|
|
187
|
+
## Step 7: Aggregate Results
|
|
107
188
|
|
|
108
|
-
Print a summary:
|
|
109
189
|
```
|
|
110
|
-
|
|
190
|
+
## Phase {X}: {Name} Execution Complete
|
|
191
|
+
|
|
192
|
+
**Waves:** {N} | **Plans:** {M}/{total} complete
|
|
111
193
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
194
|
+
| Wave | Plans | Status |
|
|
195
|
+
|------|-------|--------|
|
|
196
|
+
| 1 | plan-01, plan-02 | ✓ Complete |
|
|
197
|
+
| 2 | plan-03 | ✓ Complete |
|
|
115
198
|
|
|
116
|
-
|
|
117
|
-
|
|
199
|
+
### Plan Details
|
|
200
|
+
1. **{plan-id}**: {one-liner — what was built}
|
|
201
|
+
2. **{plan-id}**: {one-liner — what was built}
|
|
118
202
|
|
|
119
|
-
|
|
203
|
+
### Issues Encountered
|
|
204
|
+
{Aggregate from execution, or "None"}
|
|
120
205
|
```
|
|
206
|
+
|
|
207
|
+
## Failure Handling
|
|
208
|
+
|
|
209
|
+
- **Agent fails mid-plan:** Report which plan failed, ask user how to proceed
|
|
210
|
+
- **Dependency chain breaks:** Wave N fails → Wave N+1 dependents likely fail → user chooses attempt or skip
|
|
211
|
+
- **All agents in wave fail:** Stop, report for investigation
|
|
212
|
+
|
|
213
|
+
## Resumption
|
|
214
|
+
|
|
215
|
+
Re-running `/execute-phase {phase}` discovers completed SUMMARYs, skips them, and resumes from the first incomplete plan in the first incomplete wave.
|
|
216
|
+
|
|
217
|
+
## Context Efficiency
|
|
218
|
+
|
|
219
|
+
Orchestrator: ~10-15% context. Subagents: fresh context each. No content transfer — only file paths and confirmations.
|
|
@@ -1,86 +1,132 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Analyze the codebase
|
|
3
|
-
agent:
|
|
2
|
+
description: Analyze the codebase with parallel mapper agents to produce structured context documents in .planning/codebase/
|
|
3
|
+
agent: orchestrator
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Map Codebase
|
|
7
7
|
|
|
8
|
-
Analyze this codebase
|
|
8
|
+
Analyze this codebase using **parallel `@codebase-mapper` subagents** to generate 7 structured context documents in `.planning/codebase/`. Each mapper explores a specific focus area and writes documents directly — the orchestrator only collects confirmations.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Step 1: Check Existing
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
```bash
|
|
13
|
+
ls -la .planning/codebase/ 2>/dev/null
|
|
14
|
+
```
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
**If `.planning/codebase/` exists with documents:**
|
|
17
|
+
```
|
|
18
|
+
.planning/codebase/ already exists with these documents:
|
|
19
|
+
{list files found}
|
|
20
|
+
|
|
21
|
+
1. Refresh — Delete existing and remap codebase
|
|
22
|
+
2. Update — Keep existing, only update specific documents
|
|
23
|
+
3. Skip — Use existing codebase map as-is
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Wait for user response. If "Refresh": delete `.planning/codebase/` and continue. If "Skip": exit.
|
|
27
|
+
|
|
28
|
+
**If doesn't exist:** Continue.
|
|
15
29
|
|
|
16
|
-
|
|
30
|
+
## Step 2: Create Structure
|
|
17
31
|
|
|
18
|
-
**Explore:**
|
|
19
32
|
```bash
|
|
20
|
-
|
|
21
|
-
cat requirements.txt 2>/dev/null || cat pyproject.toml 2>/dev/null | head -50
|
|
22
|
-
cat go.mod 2>/dev/null | head -30
|
|
23
|
-
ls -la *.config.* tsconfig.json .nvmrc .python-version 2>/dev/null
|
|
24
|
-
ls .env* 2>/dev/null # Note existence only — NEVER read .env contents
|
|
25
|
-
grep -r "import.*stripe\|import.*supabase\|import.*aws\|import.*prisma\|import.*redis\|import.*sendgrid\|import.*twilio\|import.*openai" src/ --include="*.ts" --include="*.py" 2>/dev/null | head -40
|
|
33
|
+
mkdir -p .planning/codebase
|
|
26
34
|
```
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
## Step 3: Spawn 4 Parallel Mapper Agents
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
Spawn all 4 `@codebase-mapper` subagents **simultaneously** — invoke them all in the same turn for parallel execution.
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
**Agent 1: Tech Focus** — invoke `@codebase-mapper` with:
|
|
41
|
+
```
|
|
42
|
+
Focus: tech
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
Analyze this codebase for technology stack and external integrations.
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
Write these documents to .planning/codebase/:
|
|
47
|
+
- STACK.md — Languages, runtime, frameworks, dependencies, configuration
|
|
48
|
+
- INTEGRATIONS.md — External APIs, databases, auth providers, webhooks
|
|
49
|
+
|
|
50
|
+
Explore thoroughly. Write documents directly. Return confirmation only.
|
|
41
51
|
```
|
|
42
52
|
|
|
43
|
-
**
|
|
53
|
+
**Agent 2: Architecture Focus** — invoke `@codebase-mapper` with:
|
|
54
|
+
```
|
|
55
|
+
Focus: arch
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
Analyze this codebase architecture and directory structure.
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
Write these documents to .planning/codebase/:
|
|
60
|
+
- ARCHITECTURE.md — Pattern, layers, data flow, abstractions, entry points
|
|
61
|
+
- STRUCTURE.md — Directory layout, key locations, naming conventions
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
Explore thoroughly. Write documents directly. Return confirmation only.
|
|
64
|
+
```
|
|
50
65
|
|
|
51
|
-
**
|
|
52
|
-
```bash
|
|
53
|
-
cat .prettierrc .eslintrc.js .eslintrc.json biome.json 2>/dev/null | head -60
|
|
54
|
-
cat pyproject.toml 2>/dev/null | grep -A 20 "\[tool\."
|
|
55
|
-
ls jest.config.* vitest.config.* pytest.ini setup.cfg 2>/dev/null
|
|
56
|
-
find . -name "*.test.*" -o -name "*.spec.*" | grep -v node_modules | head -20
|
|
57
|
-
find src/ -name "*.ts" -not -name "*.test.*" 2>/dev/null | head -5 | xargs head -40 2>/dev/null
|
|
58
|
-
find tests/ -name "*.test.*" 2>/dev/null | head -3 | xargs head -60 2>/dev/null
|
|
66
|
+
**Agent 3: Quality Focus** — invoke `@codebase-mapper` with:
|
|
59
67
|
```
|
|
68
|
+
Focus: quality
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
Analyze this codebase for coding conventions and testing patterns.
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
Write these documents to .planning/codebase/:
|
|
73
|
+
- CONVENTIONS.md — Code style, naming, patterns, error handling
|
|
74
|
+
- TESTING.md — Framework, structure, mocking, coverage
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
Explore thoroughly. Write documents directly. Return confirmation only.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Agent 4: Concerns Focus** — invoke `@codebase-mapper` with:
|
|
80
|
+
```
|
|
81
|
+
Focus: concerns
|
|
82
|
+
|
|
83
|
+
Analyze this codebase for technical debt, known issues, and areas of concern.
|
|
84
|
+
|
|
85
|
+
Write this document to .planning/codebase/:
|
|
86
|
+
- CONCERNS.md — Tech debt, bugs, security, performance, fragile areas
|
|
87
|
+
|
|
88
|
+
Explore thoroughly. Write document directly. Return confirmation only.
|
|
89
|
+
```
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
## Step 4: Collect Confirmations & Verify
|
|
92
|
+
|
|
93
|
+
Wait for all 4 agents to complete. Then verify all documents exist:
|
|
68
94
|
|
|
69
|
-
**Explore:**
|
|
70
95
|
```bash
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
echo "=== Codebase Map Verification ==="
|
|
97
|
+
for f in STACK.md INTEGRATIONS.md ARCHITECTURE.md STRUCTURE.md CONVENTIONS.md TESTING.md CONCERNS.md; do
|
|
98
|
+
if [ -f ".planning/codebase/$f" ]; then
|
|
99
|
+
echo "✓ $f ($(wc -l < .planning/codebase/$f) lines)"
|
|
100
|
+
else
|
|
101
|
+
echo "✗ $f MISSING"
|
|
102
|
+
fi
|
|
103
|
+
done
|
|
75
104
|
```
|
|
76
105
|
|
|
77
|
-
|
|
106
|
+
If any document is missing, report which agent failed and offer to retry that focus area.
|
|
78
107
|
|
|
79
|
-
|
|
108
|
+
## Step 5: Commit & Report
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
```bash
|
|
111
|
+
git add .planning/codebase/
|
|
112
|
+
git commit -m "docs: map codebase — 7 structured documents in .planning/codebase/"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
✅ Codebase mapping complete
|
|
117
|
+
|
|
118
|
+
Documents generated in .planning/codebase/:
|
|
119
|
+
- STACK.md ({N} lines) — technology stack
|
|
120
|
+
- INTEGRATIONS.md ({N} lines) — external services
|
|
121
|
+
- ARCHITECTURE.md ({N} lines) — system design
|
|
122
|
+
- STRUCTURE.md ({N} lines) — file organization
|
|
123
|
+
- CONVENTIONS.md ({N} lines) — coding standards
|
|
124
|
+
- TESTING.md ({N} lines) — test patterns
|
|
125
|
+
- CONCERNS.md ({N} lines) — tech debt & risks
|
|
126
|
+
|
|
127
|
+
Next: /new-project or /plan-phase to continue
|
|
128
|
+
```
|
|
82
129
|
|
|
83
|
-
|
|
84
|
-
- Fill in the "What This Project Is" section from what you learned
|
|
130
|
+
## Context Efficiency
|
|
85
131
|
|
|
86
|
-
|
|
132
|
+
Orchestrator only dispatches and collects confirmations (file paths + line counts). Each mapper agent gets fresh context for deep exploration. No document content flows back to the orchestrator.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin"
|
|
2
|
+
import fs from "fs"
|
|
3
|
+
import path from "path"
|
|
4
|
+
|
|
5
|
+
export default tool({
|
|
6
|
+
description:
|
|
7
|
+
"Index phase plans, parse wave/dependency frontmatter, and return wave-grouped execution order. Use before executing a phase to discover plan structure.",
|
|
8
|
+
args: {
|
|
9
|
+
phase: tool.schema
|
|
10
|
+
.string()
|
|
11
|
+
.describe(
|
|
12
|
+
"Phase number or name to index (e.g., '1', '01', '01-auth-system')"
|
|
13
|
+
),
|
|
14
|
+
},
|
|
15
|
+
async execute(args, context) {
|
|
16
|
+
const planningDir = path.join(context.directory, ".planning", "phases")
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(planningDir)) {
|
|
19
|
+
return JSON.stringify({ error: ".planning/phases/ directory not found" })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Find phase directory
|
|
23
|
+
const dirs = fs.readdirSync(planningDir).filter((d) => {
|
|
24
|
+
const full = path.join(planningDir, d)
|
|
25
|
+
return fs.statSync(full).isDirectory() && d.includes(args.phase)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (dirs.length === 0) {
|
|
29
|
+
return JSON.stringify({
|
|
30
|
+
error: `No phase directory matching "${args.phase}" found`,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const phaseDir = path.join(planningDir, dirs[0])
|
|
35
|
+
const phaseName = dirs[0]
|
|
36
|
+
|
|
37
|
+
// Find all PLAN and SUMMARY files
|
|
38
|
+
const files = fs.readdirSync(phaseDir)
|
|
39
|
+
const planFiles = files
|
|
40
|
+
.filter((f) => f.endsWith("-PLAN.md"))
|
|
41
|
+
.sort()
|
|
42
|
+
const summaryFiles = files
|
|
43
|
+
.filter((f) => f.endsWith("-SUMMARY.md"))
|
|
44
|
+
.sort()
|
|
45
|
+
|
|
46
|
+
const summarySet = new Set(
|
|
47
|
+
summaryFiles.map((f) => f.replace("-SUMMARY.md", ""))
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
// Parse each plan
|
|
51
|
+
const plans = []
|
|
52
|
+
for (const planFile of planFiles) {
|
|
53
|
+
const planId = planFile.replace("-PLAN.md", "")
|
|
54
|
+
const content = fs.readFileSync(path.join(phaseDir, planFile), "utf8")
|
|
55
|
+
|
|
56
|
+
// Parse YAML frontmatter
|
|
57
|
+
const fm = parseFrontmatter(content)
|
|
58
|
+
|
|
59
|
+
plans.push({
|
|
60
|
+
id: planId,
|
|
61
|
+
file: planFile,
|
|
62
|
+
wave: fm.wave ? parseInt(fm.wave, 10) : null,
|
|
63
|
+
depends_on: fm.depends_on
|
|
64
|
+
? Array.isArray(fm.depends_on)
|
|
65
|
+
? fm.depends_on
|
|
66
|
+
: [fm.depends_on]
|
|
67
|
+
: [],
|
|
68
|
+
autonomous: fm.autonomous !== false,
|
|
69
|
+
objective: fm.objective || extractFirstHeading(content) || planId,
|
|
70
|
+
has_summary: summarySet.has(planId),
|
|
71
|
+
gap_closure: fm.gap_closure === true,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Auto-assign waves if not specified
|
|
76
|
+
const hasWaves = plans.some((p) => p.wave !== null)
|
|
77
|
+
if (!hasWaves) {
|
|
78
|
+
// Simple: plans with no deps → wave 1, compute rest from dependency graph
|
|
79
|
+
assignWaves(plans)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Group by wave
|
|
83
|
+
const waves = {}
|
|
84
|
+
for (const plan of plans) {
|
|
85
|
+
const w = plan.wave || 1
|
|
86
|
+
if (!waves[w]) waves[w] = []
|
|
87
|
+
waves[w].push(plan.id)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Count incomplete
|
|
91
|
+
const incomplete = plans.filter((p) => !p.has_summary)
|
|
92
|
+
|
|
93
|
+
return JSON.stringify(
|
|
94
|
+
{
|
|
95
|
+
phase: phaseName,
|
|
96
|
+
phase_dir: phaseDir,
|
|
97
|
+
plan_count: plans.length,
|
|
98
|
+
incomplete_count: incomplete.length,
|
|
99
|
+
has_checkpoints: plans.some((p) => !p.autonomous),
|
|
100
|
+
plans,
|
|
101
|
+
waves,
|
|
102
|
+
incomplete: incomplete.map((p) => p.id),
|
|
103
|
+
},
|
|
104
|
+
null,
|
|
105
|
+
2
|
|
106
|
+
)
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
function parseFrontmatter(content) {
|
|
111
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/)
|
|
112
|
+
if (!match) return {}
|
|
113
|
+
|
|
114
|
+
const fm = {}
|
|
115
|
+
const lines = match[1].split("\n")
|
|
116
|
+
let currentKey = null
|
|
117
|
+
let currentList = null
|
|
118
|
+
|
|
119
|
+
for (const line of lines) {
|
|
120
|
+
const kvMatch = line.match(/^(\w[\w_-]*):\s*(.*)$/)
|
|
121
|
+
if (kvMatch) {
|
|
122
|
+
if (currentKey && currentList) {
|
|
123
|
+
fm[currentKey] = currentList
|
|
124
|
+
}
|
|
125
|
+
currentKey = kvMatch[1]
|
|
126
|
+
const val = kvMatch[2].trim()
|
|
127
|
+
if (val === "") {
|
|
128
|
+
currentList = []
|
|
129
|
+
} else if (val === "true") {
|
|
130
|
+
fm[currentKey] = true
|
|
131
|
+
currentKey = null
|
|
132
|
+
currentList = null
|
|
133
|
+
} else if (val === "false") {
|
|
134
|
+
fm[currentKey] = false
|
|
135
|
+
currentKey = null
|
|
136
|
+
currentList = null
|
|
137
|
+
} else {
|
|
138
|
+
fm[currentKey] = val
|
|
139
|
+
currentKey = null
|
|
140
|
+
currentList = null
|
|
141
|
+
}
|
|
142
|
+
} else if (currentList !== null && line.match(/^\s*-\s+(.+)/)) {
|
|
143
|
+
currentList.push(line.match(/^\s*-\s+(.+)/)[1].trim())
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (currentKey && currentList) {
|
|
147
|
+
fm[currentKey] = currentList
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return fm
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function extractFirstHeading(content) {
|
|
154
|
+
// Skip frontmatter
|
|
155
|
+
let text = content
|
|
156
|
+
if (text.startsWith("---")) {
|
|
157
|
+
const end = text.indexOf("---", 3)
|
|
158
|
+
if (end !== -1) text = text.slice(end + 3)
|
|
159
|
+
}
|
|
160
|
+
const match = text.match(/^#+\s+(.+)/m)
|
|
161
|
+
return match ? match[1].trim() : null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function assignWaves(plans) {
|
|
165
|
+
const planMap = new Map(plans.map((p) => [p.id, p]))
|
|
166
|
+
|
|
167
|
+
// Plans with no dependencies → wave 1
|
|
168
|
+
for (const plan of plans) {
|
|
169
|
+
if (plan.depends_on.length === 0) {
|
|
170
|
+
plan.wave = 1
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Iteratively assign waves based on dependency graph
|
|
175
|
+
let changed = true
|
|
176
|
+
let iterations = 0
|
|
177
|
+
while (changed && iterations < 20) {
|
|
178
|
+
changed = false
|
|
179
|
+
iterations++
|
|
180
|
+
for (const plan of plans) {
|
|
181
|
+
if (plan.wave !== null) continue
|
|
182
|
+
const depWaves = plan.depends_on
|
|
183
|
+
.map((dep) => planMap.get(dep)?.wave)
|
|
184
|
+
.filter((w) => w !== null && w !== undefined)
|
|
185
|
+
if (depWaves.length === plan.depends_on.length) {
|
|
186
|
+
plan.wave = Math.max(...depWaves) + 1
|
|
187
|
+
changed = true
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Remaining unassigned (circular deps or missing refs) → wave 1
|
|
193
|
+
for (const plan of plans) {
|
|
194
|
+
if (plan.wave === null) plan.wave = 1
|
|
195
|
+
}
|
|
196
|
+
}
|
package/package.json
CHANGED