@hanzlaa/rcode 4.3.2 → 4.4.0

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.
Files changed (130) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +2 -1
  3. package/README.md +45 -10
  4. package/cli/doctor.js +21 -3
  5. package/cli/index.js +5 -1
  6. package/cli/install.js +16 -2
  7. package/cli/postinstall.js +1 -1
  8. package/cli/uninstall.js +48 -6
  9. package/dist/rcode.js +178 -174
  10. package/package.json +12 -22
  11. package/rcode/agents/rcode-codebase-mapper.md +12 -0
  12. package/rcode/agents/rcode-phase-researcher.md +13 -0
  13. package/rcode/agents/rcode-project-researcher.md +32 -0
  14. package/rcode/agents/rcode-roadmapper.md +15 -0
  15. package/rcode/agents/rules/planner/common-patterns.md +40 -0
  16. package/rcode/bin/lib/roadmap.cjs +184 -11
  17. package/rcode/bin/rcode-hooks.cjs +303 -14
  18. package/rcode/bin/rcode-tools.cjs +669 -55
  19. package/rcode/commands/lens-audit.md +8 -6
  20. package/rcode/data/intent-table.json +87 -0
  21. package/rcode/modules/seo.yaml +22 -0
  22. package/rcode/references/project-types.yaml +75 -0
  23. package/rcode/skills/SKILLS_INDEX.md +20 -2
  24. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/SKILL.md +16 -21
  25. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/backlog-building.md +13 -0
  26. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/merge-strategy.md +17 -0
  27. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/orchestrator-rhythm.md +9 -0
  28. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/wave-design.md +21 -0
  29. package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/templates/heartbeat.sh +0 -0
  30. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +60 -1
  31. package/rcode/skills/core/rcode-lazy/SKILL.md +149 -0
  32. package/rcode/skills/seo/module.yaml +20 -0
  33. package/rcode/skills/seo/on-page-seo-auditor/SKILL.md +308 -0
  34. package/rcode/skills/seo/on-page-seo-auditor/references/audit-example.md +109 -0
  35. package/rcode/skills/seo/on-page-seo-auditor/references/audit-templates.md +427 -0
  36. package/rcode/skills/seo/on-page-seo-auditor/references/bulk-audit-playbook.md +116 -0
  37. package/rcode/skills/seo/on-page-seo-auditor/references/scoring-rubric.md +525 -0
  38. package/rcode/skills/seo/rank-and-rent-local-seo/SKILL.md +94 -0
  39. package/rcode/skills/seo/rank-and-rent-local-seo/rules/city-matrix-and-pages.md +76 -0
  40. package/rcode/skills/seo/rank-and-rent-local-seo/rules/monetization.md +70 -0
  41. package/rcode/skills/seo/rank-and-rent-local-seo/rules/niche-selection.md +60 -0
  42. package/rcode/skills/seo/rank-and-rent-local-seo/rules/subniche-discovery.md +66 -0
  43. package/rcode/skills/seo/rank-and-rent-local-seo/templates/service-city-page.md +36 -0
  44. package/rcode/skills/seo/rank-and-rent-local-seo/templates/subniche-research-prompt.md +34 -0
  45. package/rcode/skills/seo/seo-audit/SKILL.md +412 -0
  46. package/rcode/skills/seo/seo-audit/evals/evals.json +136 -0
  47. package/rcode/skills/seo/seo-audit/references/ai-writing-detection.md +200 -0
  48. package/rcode/skills/seo/seo-content-factory/SKILL.md +130 -0
  49. package/rcode/skills/seo/seo-content-factory/rules/agents.md +80 -0
  50. package/rcode/skills/seo/seo-content-factory/rules/herdr-wave-mapping.md +80 -0
  51. package/rcode/skills/seo/seo-content-factory/rules/pipeline.md +102 -0
  52. package/rcode/skills/seo/seo-content-factory/rules/programmatic-pages.md +107 -0
  53. package/rcode/skills/seo/seo-content-factory/rules/quality-gates.md +68 -0
  54. package/rcode/skills/seo/seo-content-factory/rules/weekly-cadence.md +61 -0
  55. package/rcode/skills/seo/seo-content-factory/templates/BACKLOG.md +23 -0
  56. package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/01-funnel-A1-A4.md +63 -0
  57. package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/02-fanout-editorial-A5-A6-A8.md +28 -0
  58. package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/03-fanout-programmatic-A7-A6-A8.md +25 -0
  59. package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/04-loop-A9-A10.md +34 -0
  60. package/rcode/skills/seo/seo-content-factory/templates/article.mdx +39 -0
  61. package/rcode/skills/seo/seo-content-factory/templates/cluster.schema.json +52 -0
  62. package/rcode/skills/seo/seo-content-factory/templates/content-brief.md +51 -0
  63. package/rcode/skills/seo/seo-content-factory/templates/jsonld-builders.ts +49 -0
  64. package/rcode/skills/seo/seo-content-factory/templates/keywords.csv +13 -0
  65. package/rcode/skills/seo/seo-content-factory/templates/programmatic-page.tsx +58 -0
  66. package/rcode/skills/seo/seo-content-writer/SKILL.md +263 -0
  67. package/rcode/skills/seo/seo-content-writer/references/content-structure-templates.md +875 -0
  68. package/rcode/skills/seo/seo-content-writer/references/instructions-detail.md +239 -0
  69. package/rcode/skills/seo/seo-content-writer/references/seo-writing-checklist.md +250 -0
  70. package/rcode/skills/seo/seo-content-writer/references/title-formulas.md +339 -0
  71. package/rcode/skills/seo/seo-growth-orchestrator/SKILL.md +79 -0
  72. package/rcode/skills/seo/seo-growth-orchestrator/rules/backlinks.md +29 -0
  73. package/rcode/skills/seo/seo-growth-orchestrator/rules/content-engine.md +27 -0
  74. package/rcode/skills/seo/seo-growth-orchestrator/rules/dos-and-donts.md +40 -0
  75. package/rcode/skills/seo/seo-growth-orchestrator/rules/goals-protocol.md +34 -0
  76. package/rcode/skills/seo/seo-growth-orchestrator/rules/local-seo-stack.md +35 -0
  77. package/rcode/skills/seo/seo-growth-orchestrator/rules/mrr-sprint.md +27 -0
  78. package/rcode/skills/seo/seo-growth-orchestrator/rules/tools.md +19 -0
  79. package/rcode/skills/seo/seo-growth-orchestrator/templates/backlink-research.md +29 -0
  80. package/rcode/skills/seo/seo-growth-orchestrator/templates/goal-protocol.md +35 -0
  81. package/rcode/skills/seo/seo-growth-orchestrator/templates/local-citations.md +24 -0
  82. package/rcode/skills/seo/seo-growth-orchestrator/templates/local-city-pages.md +27 -0
  83. package/rcode/skills/seo/seo-growth-orchestrator/templates/local-gbp-categories.md +23 -0
  84. package/rcode/skills/seo/seo-growth-orchestrator/templates/local-gsc-goldmine.md +27 -0
  85. package/rcode/skills/seo/seo-growth-orchestrator/templates/local-review-mining.md +26 -0
  86. package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-30day-plan.md +19 -0
  87. package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-cut.md +13 -0
  88. package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-opportunity-finder.md +17 -0
  89. package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-role-model.md +18 -0
  90. package/rcode/skills/seo/seo-site-builder/SKILL.md +149 -0
  91. package/rcode/skills/seo/seo-site-builder/rules/01-niche-discovery.md +64 -0
  92. package/rcode/skills/seo/seo-site-builder/rules/02-deep-research.md +164 -0
  93. package/rcode/skills/seo/seo-site-builder/rules/03-keyword-strategy.md +82 -0
  94. package/rcode/skills/seo/seo-site-builder/rules/04-content-plan.md +173 -0
  95. package/rcode/skills/seo/seo-site-builder/rules/05-site-build.md +187 -0
  96. package/rcode/skills/seo/seo-site-builder/rules/06-yolo.md +127 -0
  97. package/rcode/skills/seo/seo-site-builder/templates/article-stub.mdx +62 -0
  98. package/rcode/skills/seo/seo-site-builder/templates/image-prompt.md +97 -0
  99. package/rcode/skills/seo/seo-site-builder/templates/keyword-export.md +45 -0
  100. package/rcode/skills/seo/seo-site-builder/templates/niche-scorecard.md +28 -0
  101. package/rcode/skills/seo/technical-seo-checker/SKILL.md +329 -0
  102. package/rcode/skills/seo/technical-seo-checker/references/bulk-audit-playbook.md +118 -0
  103. package/rcode/skills/seo/technical-seo-checker/references/ecommerce-platform-patterns.md +166 -0
  104. package/rcode/skills/seo/technical-seo-checker/references/http-status-codes.md +705 -0
  105. package/rcode/skills/seo/technical-seo-checker/references/llm-crawler-handling.md +257 -0
  106. package/rcode/skills/seo/technical-seo-checker/references/pre-migration-playbook.md +137 -0
  107. package/rcode/skills/seo/technical-seo-checker/references/robots-txt-reference.md +717 -0
  108. package/rcode/skills/seo/technical-seo-checker/references/technical-audit-example.md +169 -0
  109. package/rcode/skills/seo/technical-seo-checker/references/technical-audit-templates.md +436 -0
  110. package/rcode/templates/settings-hooks.json +16 -1
  111. package/rcode/workflows/do.md +17 -0
  112. package/rcode/workflows/enable-hooks.md +3 -2
  113. package/rcode/workflows/execute-sprint.md +22 -0
  114. package/rcode/workflows/execute.md +17 -3
  115. package/rcode/workflows/lens-audit.md +49 -8
  116. package/rcode/workflows/new-milestone.md +21 -9
  117. package/rcode/workflows/new-project-create-roadmap.md +2 -0
  118. package/rcode/workflows/new-project-roadmap.md +2 -0
  119. package/rcode/workflows/new-project.md +3 -0
  120. package/rcode/workflows/plan.md +15 -2
  121. package/server/dashboard.js +6 -2
  122. package/server/lib/api.js +35 -20
  123. package/server/lib/html/client/components/OrchPanel.js +43 -2
  124. package/server/lib/html/client/components/dashboard/InProgress.js +5 -4
  125. package/server/lib/html/client/components/shared.js +14 -0
  126. package/server/lib/html/client/orchestrator.js +13 -0
  127. package/server/lib/html/client/views/KanbanView.js +33 -4
  128. package/server/lib/html/css.js +57 -3
  129. package/server/lib/scanner.js +40 -4
  130. package/server/orchestrator.js +42 -0
@@ -375,6 +375,22 @@ If new untracked files appeared after running scripts or tools, decide for each:
375
375
 
376
376
  </task_commit>
377
377
 
378
+ <hook_revert_detection_gate>
379
+ ## Post-Step Hook Revert Detection Gate
380
+
381
+ After each task that writes or edits files, run:
382
+ ```bash
383
+ git diff --name-only HEAD
384
+ ```
385
+ If a file that was supposed to be written by this step shows NO diff (i.e. git thinks it's clean), that file was reverted by a hook. STOP — do NOT mark the step done. Report:
386
+ ```
387
+ ⚠ Revert detected: <filename> was written by this step but shows no diff vs HEAD.
388
+ Likely cause: a linter or pre-commit hook reverted the file.
389
+ Action required: inspect the hook output in the last git commit attempt, fix the root cause, then re-run this task.
390
+ ```
391
+ Do not proceed to the next task until all expected file changes survive the git diff check.
392
+ </hook_revert_detection_gate>
393
+
378
394
  <post_step_revert_gate>
379
395
  ## Post-Step Revert Detection Gate
380
396
 
@@ -497,6 +513,12 @@ If user_setup exists: create `{phase}-USER-SETUP.md` using template `.rcode/temp
497
513
  </step>
498
514
 
499
515
  <step name="create_summary">
516
+ **Overwrite guard:** If `{phase}-{plan}-SUMMARY.md` already exists at `.planning/phases/XX-name/`, delete it first before writing:
517
+ ```bash
518
+ rm -f ".planning/phases/XX-name/{phase}-{plan}-SUMMARY.md"
519
+ ```
520
+ Never append to or skip an existing SUMMARY.md — always overwrite with the current sprint's completion data. Silently continuing with a stale SUMMARY.md is a critical bug.
521
+
500
522
  Create `{phase}-{plan}-SUMMARY.md` at `.planning/phases/XX-name/`. Use `.rcode/templates/summary.md`.
501
523
 
502
524
  **Frontmatter:** phase, plan, subsystem, tags | requires/provides/affects | tech-stack.added/patterns | key-files.created/modified | key-decisions | requirements-completed (**MUST** copy `requirements` array from SPRINT.md frontmatter verbatim) | duration ($DURATION), completed ($PLAN_END_TIME date).
@@ -2,6 +2,16 @@
2
2
  Execute all plans in a phase using wave-based parallel execution. Orchestrator stays lean — delegates plan execution to subagents.
3
3
  </purpose>
4
4
 
5
+ ## Orchestrator Constraint — No Inline Implementation
6
+
7
+ **The execute orchestrator MUST NOT implement code directly.** Its only role is to dispatch, monitor, and checkpoint. All implementation is delegated to `rcode-executor` subagents.
8
+
9
+ If you are reading this as the main conversation loop and are tempted to write code, create files, or make commits directly instead of spawning a subagent:
10
+
11
+ > **STOP.** Spawn `rcode-executor` as a subagent with the sprint plan as context. The main loop's job is to call the agent, present checkpoints, and update state — not to implement.
12
+
13
+ Bypassing this constraint produces a built project with no execution trace, no SUMMARY.md, and a dashboard frozen at `planned`. See issue #915.
14
+
5
15
  <pre_flight>
6
16
  **Mandatory before execution begins.** Run these checks first and surface
7
17
  findings BEFORE any subagents are spawned. If any check fails, stop and
@@ -173,11 +183,11 @@ via filesystem and git state.
173
183
  @.rcode/references/git-preflight.md
174
184
  Read STATE.md before any operation to load project context.
175
185
 
176
- @.rcode/references/agent-contracts.md
177
- @.rcode/references/context-budget.md
178
- @.rcode/references/gates.md
179
186
  @.rcode/references/karpathy-guidelines.md
180
187
  @.rcode/references/execution-protocol.md
188
+ <!-- Read .rcode/references/agent-contracts.md only if debugging agent contract violations -->
189
+ <!-- Read .rcode/references/context-budget.md only if context degradation guidance is needed -->
190
+ <!-- Read .rcode/references/gates.md only if implementing or troubleshooting gate logic -->
181
191
  </required_reading>
182
192
 
183
193
  <available_agent_types>
@@ -356,6 +366,7 @@ checkpoints between tasks. The user can review, modify, or redirect work at any
356
366
  their feedback before continuing. Otherwise proceed to next task.
357
367
 
358
368
  e. **After plan complete:** Show results, commit, create SUMMARY.md, then present next plan.
369
+ **Overwrite guard:** If the SUMMARY.md file already exists from a previous run, delete it first with `rm -f <path>` before writing the new version. Never append to or skip an existing SUMMARY.md — always overwrite with the current sprint's completion data.
359
370
 
360
371
  3. After all plans: proceed to verification (same as normal mode).
361
372
 
@@ -1036,6 +1047,9 @@ Read and follow `.rcode/workflows/transition.md`, passing through the `--auto` f
1036
1047
  /rcode-execute {next} — execute next phase
1037
1048
  ```
1038
1049
 
1050
+ **Next step — paste this to verify:**
1051
+ > /rcode-verify {X}
1052
+
1039
1053
  Only suggest the commands listed above. Do not invent or hallucinate command names.
1040
1054
  </step>
1041
1055
 
@@ -1,7 +1,7 @@
1
1
  # Workflow: rcode-lens-audit
2
2
 
3
3
  <purpose>
4
- Run a structured 15-lens code audit against the current project. Each lens
4
+ Run a structured 16-lens code audit against the current project. Each lens
5
5
  delegates to its mapped primary skill via Task() subagent dispatch — the skill
6
6
  provides domain expertise; this workflow handles orchestration, aggregation,
7
7
  and GH issue body generation. Never fixes anything; audit-first, fix-second.
@@ -26,6 +26,7 @@ and GH issue body generation. Never fixes anything; audit-first, fix-second.
26
26
  | 13 | Observability | `rcode-observability-auditor` | — |
27
27
  | 14 | Naming | `rcode-codebase-mapper` | `rcode-reviewer` |
28
28
  | 15 | Coverage | `rcode-nyquist-auditor` | `rcode-fatima` |
29
+ | 16 | YAGNI / Over-engineering | `rcode-reviewer` | `rcode-lazy` |
29
30
 
30
31
  ## Step 0 — Usage check
31
32
 
@@ -33,8 +34,8 @@ If `$ARGUMENTS` is `--help` or `-h`:
33
34
 
34
35
  ```
35
36
  /rcode-lens-audit # interactive — asks which lens
36
- /rcode-lens-audit all # run all 15 lenses sequentially
37
- /rcode-lens-audit <N> # run lens N (1-15) only
37
+ /rcode-lens-audit all # run all 16 lenses sequentially
38
+ /rcode-lens-audit <N> # run lens N (1-16) only
38
39
  /rcode-lens-audit <name> # run by name, e.g. "security", "performance"
39
40
 
40
41
  Lenses and their primary skills:
@@ -53,6 +54,7 @@ Lenses and their primary skills:
53
54
  13. observability — rcode-observability-auditor
54
55
  14. naming — rcode-codebase-mapper + rcode-reviewer
55
56
  15. coverage — rcode-nyquist-auditor + rcode-fatima
57
+ 16. yagni — rcode-reviewer + rcode-lazy (speculative abstractions, unused config, deps stdlib covers)
56
58
  ```
57
59
 
58
60
  STOP after printing help.
@@ -83,15 +85,15 @@ rcode-tools not found. Run: npx @hanzlaa/rcode install .
83
85
  ```
84
86
 
85
87
  Parse `$ARGUMENTS`:
86
- - `all` → `LENSES=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)`
87
- - digit 1–15 → `LENSES=(<N>)`
88
+ - `all` → `LENSES=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)`
89
+ - digit 1–16 → `LENSES=(<N>)`
88
90
  - known lens name → map to number → `LENSES=(<N>)`
89
91
  - empty → continue to Step 2 (interactive picker)
90
92
 
91
93
  Name → number mapping:
92
94
  `security=1, performance=2, testability=3, extensibility=4, dep-health=5,`
93
95
  `error-recovery=6, state-machine=7, i18n=8, documentation=9, cross-platform=10,`
94
- `karpathy=11, sxo=12, observability=13, naming=14, coverage=15`
96
+ `karpathy=11, sxo=12, observability=13, naming=14, coverage=15, yagni=16`
95
97
 
96
98
  ## Step 2 — Interactive picker (when no argument given)
97
99
 
@@ -117,7 +119,8 @@ Options:
117
119
  13. observability — rcode-observability-auditor (unguarded calls, silent fails)
118
120
  14. naming — rcode-codebase-mapper (naming drift, PLAN.md vs SPRINT.md)
119
121
  15. coverage — rcode-nyquist-auditor (parity gaps, untested commands)
120
- 16. all run all 15 lenses
122
+ 16. yagni rcode-reviewer (speculative abstractions, single-impl interfaces, deps stdlib covers)
123
+ 17. all — run all 16 lenses
121
124
  0. cancel
122
125
  ```
123
126
 
@@ -612,6 +615,44 @@ FINDINGS[coverage] = merge(PRIMARY, SECONDARY)
612
615
 
613
616
  ---
614
617
 
618
+ ### Lens 16 — YAGNI / Over-engineering
619
+
620
+ ```
621
+ PRIMARY = Task(
622
+ subagent_type="rcode-code-reviewer",
623
+ model="{lens_model}",
624
+ prompt="YAGNI / over-engineering audit — do NOT fix code. {CONTEXT}
625
+
626
+ Run Lens 16 (YAGNI) audit against recent changes (HEAD~20..HEAD), using the
627
+ rcode-lazy ladder as the rubric (@.rcode/skills/core/rcode-lazy/SKILL.md).
628
+ This lens is narrower than Lens 11 (Karpathy): hunt ONLY for code that exists
629
+ but should not, or is bigger than the job needs. Flag:
630
+
631
+ - Speculative features / config / params for needs that do not exist yet
632
+ - Abstractions with a single implementation: an interface, factory, or
633
+ strategy with exactly one concrete user — inline it
634
+ - Wrapper layers that add a hop for one caller
635
+ - 'For later' scaffolding: extension points, plugin registries, generic
636
+ handlers with one case
637
+ - A new dependency for what stdlib or a native platform feature already does
638
+ (e.g. a date-picker lib vs <input type=\"date\">, a deep-clone lib vs
639
+ structuredClone)
640
+ - Config for a value that never changes (hardcode it)
641
+ - Hand-rolled code reproducing an already-installed dependency
642
+
643
+ Do NOT flag: validation at trust boundaries, error handling that prevents
644
+ data loss, security, accessibility, or anything explicitly requested — those
645
+ are never YAGNI violations even when verbose.
646
+
647
+ Return: file:line — YAGNI smell — what to delete/inline + the simpler form [critical|warn|info]
648
+ If clean: PASS"
649
+ )
650
+
651
+ FINDINGS[yagni] = PRIMARY
652
+ ```
653
+
654
+ ---
655
+
615
656
  ## Step 5 — Compile findings per lens
616
657
 
617
658
  For each lens that was run, print:
@@ -689,7 +730,7 @@ Next: file the GH issues above, then run /rcode-audit fix to address them.
689
730
  ## On Error
690
731
 
691
732
  - **rcode-tools not found**: print `Run: npx @hanzlaa/rcode install .` and STOP.
692
- - **Lens N out of range (not 1–15)**: print valid range and STOP.
733
+ - **Lens N out of range (not 1–16)**: print valid range and STOP.
693
734
  - **Subagent skill not installed**: note `(skill not available — skipping)`, continue.
694
735
  - **Scope dirs empty**: note `(no source files in scope)` per lens, still run dispatch.
695
736
  - **Karpathy dispatch fails** (Lens 11): note failure, continue with remaining lenses.
@@ -17,6 +17,24 @@ Valid rcode subagent types (use exact names — do not fall back to 'general-pur
17
17
 
18
18
  <process>
19
19
 
20
+ ## --from-draft mode
21
+
22
+ When `--from-draft <path>` is passed:
23
+ 1. Check for the file at `<path>`. Accept any markdown file — MILESTONE-CONTEXT.md, a scratch doc, or a ROADMAP partial.
24
+ 2. If found: read it as the milestone definition — do NOT re-run the interactive goal-gathering interview (steps 2–4). Jump directly to step 5 (Requirements scoping) using the draft as the source of truth.
25
+ 3. If not found: abort with:
26
+ ```
27
+ Error: --from-draft file not found: {path}
28
+ ```
29
+ Do not fall back to standard mode — an explicit path that does not exist is a user error, not a signal to start fresh.
30
+
31
+ Surface a one-line banner when the draft is loaded:
32
+ ```
33
+ ◆ From-draft mode: using {path} as milestone definition (skipping goal interview)
34
+ ```
35
+
36
+ This mode exists to skip expensive re-derivation when a human or prior agent has already drafted the milestone scope.
37
+
20
38
  ## 1. Parse arguments and load context
21
39
 
22
40
  Parse `$ARGUMENTS` before anything else:
@@ -27,20 +45,14 @@ Parse `$ARGUMENTS` before anything else:
27
45
 
28
46
  If `--from-draft` is absent, continue phase numbering from the previous milestone.
29
47
 
30
- **From-draft mode:**
48
+ **From-draft mode (see dedicated section above):**
31
49
 
32
50
  When `FROM_DRAFT_MODE=true`:
33
51
  1. Read the draft file at `DRAFT_FILE`. Accept any markdown file — MILESTONE-CONTEXT.md, a scratch doc, or a ROADMAP partial.
34
52
  2. Extract milestone name (first `# Heading` or the filename stem), goals list, and any explicit phase list.
35
53
  3. Skip the interactive goal-gathering interview (steps 2–4). Jump directly to step 5 (Requirements scoping) using the draft as the source of truth.
36
- 4. Surface a one-line banner:
37
- ```
38
- ◆ From-draft mode: using {DRAFT_FILE} as milestone definition (skipping goal interview)
39
- ```
40
- 5. If `DRAFT_FILE` does not exist, abort:
41
- ```
42
- Error: --from-draft file not found: {DRAFT_FILE}
43
- ```
54
+ 4. Surface the one-line banner (see `## --from-draft mode` above).
55
+ 5. If `DRAFT_FILE` does not exist, abort (see `## --from-draft mode` above).
44
56
 
45
57
  Read these files in parallel:
46
58
  - `.planning/PROJECT.md` — existing project, validated requirements, decisions
@@ -41,6 +41,8 @@ Create roadmap:
41
41
  6. Return ROADMAP CREATED with summary
42
42
 
43
43
  Write files first, then return. This ensures artifacts persist even if context is lost.
44
+
45
+ **Scope constraint:** rcode-roadmapper creates ONLY: ROADMAP.md, STATE.md, and one PHASE.md per phase directory (if requested). It MUST NOT create SPRINT.md, PLAN.md, or any sprint-level planning files. Sprint planning is handled exclusively by `/rcode-plan`. If you find yourself about to write a SPRINT.md file, STOP — that file is out of scope for this agent.
44
46
  </instructions>
45
47
  ", subagent_type="rcode-roadmapper", model="${ROADMAPPER_MODEL}", description="Create roadmap")
46
48
  ```
@@ -199,6 +199,8 @@ Create roadmap:
199
199
  6. Return ROADMAP CREATED with summary
200
200
 
201
201
  Write files first, then return. This ensures artifacts persist even if context is lost.
202
+
203
+ **Scope constraint:** rcode-roadmapper creates ONLY: ROADMAP.md, STATE.md, and one PHASE.md per phase directory (if requested). It MUST NOT create SPRINT.md, PLAN.md, or any sprint-level planning files. Sprint planning is handled exclusively by `/rcode-plan`. If you find yourself about to write a SPRINT.md file, STOP — that file is out of scope for this agent.
202
204
  </instructions>
203
205
  ", subagent_type="rcode-roadmapper", model="{roadmapper_model}", description="Create roadmap")
204
206
  ```
@@ -885,6 +885,9 @@ Present completion summary:
885
885
  > (a) `.planning/ROADMAP.md` contains real `## Phase N` headings — open it and add them if the file is still a stub.
886
886
  > (b) Run `npx rcode state sync --from-disk` to populate `.rcode/state.json` with the phases array so downstream commands (`/rcode-plan`, `/rcode-execute-sprint`, etc.) can read the phase list.
887
887
 
888
+ **Next step — paste this to start planning:**
889
+ > /rcode-plan 1 — [Phase 1 Name from ROADMAP.md]
890
+
888
891
  **If auto mode:**
889
892
 
890
893
  ```
@@ -49,9 +49,9 @@ Read all files referenced by the invoking prompt's execution_context before star
49
49
  ${PHASE_GOAL_HAS_UI ? '@.rcode/references/ui-brand.md' : ''}
50
50
  @.rcode/references/revision-loop.md
51
51
  @.rcode/references/gate-prompts.md
52
- @.rcode/references/agent-contracts.md
53
- @.rcode/references/gates.md
54
52
  @.rcode/references/karpathy-guidelines.md
53
+ <!-- Read .rcode/references/agent-contracts.md only if defining or debugging agent contracts -->
54
+ <!-- Read .rcode/references/gates.md only if implementing or troubleshooting gate logic -->
55
55
  @.rcode/references/thinking-models-planning.md
56
56
  </required_reading>
57
57
 
@@ -64,6 +64,16 @@ Valid rcode subagent types (use exact names — do not fall back to 'general-pur
64
64
 
65
65
  <process>
66
66
 
67
+ ## --from-stub mode
68
+
69
+ When `--from-stub` is passed:
70
+ 1. Check for existing `PLAN.md` in the phase directory
71
+ 2. If found: read it as the planning skeleton — do NOT re-derive phase goals or re-research the phase
72
+ 3. Expand the stub into full SPRINT.md files using the stories already listed in PLAN.md
73
+ 4. If no PLAN.md exists: fall back to standard planning mode (derive from ROADMAP + RESEARCH.md)
74
+
75
+ This mode exists to skip expensive re-derivation when a human or prior agent has already produced a planning skeleton.
76
+
67
77
  ## 0. Project-Status Preflight
68
78
 
69
79
  ```bash
@@ -1023,6 +1033,9 @@ Verification: {Passed | Passed with override | Skipped}
1023
1033
 
1024
1034
  /rcode-execute {X} ${RCODE_WS}
1025
1035
 
1036
+ **Next step — paste this to execute:**
1037
+ > /rcode-execute {X}
1038
+
1026
1039
  ───────────────────────────────────────────────────────────────
1027
1040
 
1028
1041
  **Also available:**
@@ -38,6 +38,10 @@ const { renderHtml } = require('./lib/html/shell');
38
38
  const PORT = parseInt(process.env.PORT || '7717', 10);
39
39
  const RCODE_DIR = process.env.RCODE_DIR || path.join(process.cwd(), '.rcode');
40
40
  const PROJECT_ROOT = path.dirname(RCODE_DIR);
41
+ // Fallback root for agent prompts when rcode is installed as a package (not run
42
+ // from inside the rcode source repo). server/dashboard.js lives at <pkg>/server/,
43
+ // so <pkg> is one level up.
44
+ const PACKAGE_ROOT = path.join(__dirname, '..');
41
45
 
42
46
  // Shared orchestrator token — passed to the orchestrator via env and embedded
43
47
  // in the HTML. Persisted to ~/.rcode/orch-token so it stays STABLE across
@@ -93,12 +97,12 @@ function handleRequest(req, res) {
93
97
  }
94
98
 
95
99
  if (url === '/api/agents') {
96
- handleApiAgents(req, res, PROJECT_ROOT);
100
+ handleApiAgents(req, res, PROJECT_ROOT, PACKAGE_ROOT);
97
101
  return;
98
102
  }
99
103
 
100
104
  if (url.startsWith('/api/file')) {
101
- handleApiFile(req, res, PROJECT_ROOT);
105
+ handleApiFile(req, res, PROJECT_ROOT, PACKAGE_ROOT);
102
106
  return;
103
107
  }
104
108
 
package/server/lib/api.js CHANGED
@@ -120,7 +120,7 @@ function handleApiFiles(req, res, projectRoot) {
120
120
  res.end(JSON.stringify(groups));
121
121
  }
122
122
 
123
- function handleApiFile(req, res, projectRoot) {
123
+ function handleApiFile(req, res, projectRoot, packageRoot) {
124
124
  const url = req.url || '';
125
125
  const params = new URLSearchParams(url.split('?')[1] || '');
126
126
  const relPath = params.get('path') || '';
@@ -128,24 +128,33 @@ function handleApiFile(req, res, projectRoot) {
128
128
  res.writeHead(400); res.end('Missing path parameter'); return;
129
129
  }
130
130
  // Fix #321: decode URL-encoded characters before resolving
131
- const decoded = decodeURIComponent(relPath);
132
- const resolved = path.resolve(projectRoot, decoded.replace(/^\//, ''));
133
- if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {
134
- res.writeHead(403); res.end('Forbidden'); return;
131
+ const decoded = decodeURIComponent(relPath).replace(/^\//, '');
132
+
133
+ if (!decoded.endsWith('.md')) {
134
+ res.writeHead(403); res.end('Forbidden: only .md files'); return;
135
135
  }
136
- // Dereference symlinks so a symlink outside projectRoot cannot bypass the guard
137
- let realResolved;
138
- try { realResolved = fs.realpathSync(resolved); }
139
- catch { res.writeHead(404); res.end('File not found'); return; }
140
- if (!realResolved.startsWith(projectRoot + path.sep) && realResolved !== projectRoot) {
141
- res.writeHead(403); res.end('Forbidden'); return;
136
+
137
+ // Helper: try to read `decoded` relative to `root`; returns content string or null.
138
+ function tryRead(root) {
139
+ const resolved = path.resolve(root, decoded);
140
+ if (!resolved.startsWith(root + path.sep) && resolved !== root) return null;
141
+ let real;
142
+ try { real = fs.realpathSync(resolved); } catch { return null; }
143
+ if (!real.startsWith(root + path.sep) && real !== root) return null;
144
+ try { return fs.readFileSync(resolved, 'utf8'); } catch { return null; }
142
145
  }
143
- if (!resolved.endsWith('.md')) {
144
- res.writeHead(403); res.end('Forbidden: only .md files'); return;
146
+
147
+ // For rcode/agents/ paths fall back to the package install dir when the file
148
+ // doesn't exist under the project root (rcode installed as a dependency).
149
+ const isAgentPath = decoded.startsWith('rcode/agents/');
150
+ let content = tryRead(projectRoot);
151
+ if (content === null && isAgentPath && packageRoot && packageRoot !== projectRoot) {
152
+ content = tryRead(packageRoot);
153
+ }
154
+
155
+ if (content === null) {
156
+ res.writeHead(404); res.end('File not found'); return;
145
157
  }
146
- let content;
147
- try { content = fs.readFileSync(resolved, 'utf8'); }
148
- catch { res.writeHead(404); res.end('File not found'); return; }
149
158
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
150
159
  res.end(content);
151
160
  }
@@ -242,11 +251,17 @@ function parseAgentFrontmatter(raw) {
242
251
  // returns one small frontmatter summary per agent .md file. Full prompt
243
252
  // bodies are NOT included; the client fetches those lazily per agent via the
244
253
  // existing /api/file handler when a card is opened.
245
- function handleApiAgents(req, res, projectRoot) {
246
- const agentsDir = path.join(projectRoot, 'rcode', 'agents');
254
+ function handleApiAgents(req, res, projectRoot, packageRoot) {
255
+ // Prefer the project's own rcode/agents/ dir; fall back to the package install
256
+ // dir so the drawer works in any project where rcode is an npm dependency.
257
+ let agentsDir = path.join(projectRoot, 'rcode', 'agents');
247
258
  let entries = [];
248
- try { entries = fs.readdirSync(agentsDir, { withFileTypes: true }); }
249
- catch { /* no agents dir return an empty roster */ }
259
+ try { entries = fs.readdirSync(agentsDir, { withFileTypes: true }); } catch { /* ignore */ }
260
+ const mdEntries = entries.filter(e => e.isFile() && e.name.endsWith('.md'));
261
+ if (mdEntries.length === 0 && packageRoot && packageRoot !== projectRoot) {
262
+ agentsDir = path.join(packageRoot, 'rcode', 'agents');
263
+ try { entries = fs.readdirSync(agentsDir, { withFileTypes: true }); } catch { entries = []; }
264
+ }
250
265
  const agents = [];
251
266
  for (const e of entries) {
252
267
  if (!e.isFile() || !e.name.endsWith('.md')) continue;
@@ -50,6 +50,9 @@ function closeStream(storyId) {
50
50
 
51
51
  // ── Component ─────────────────────────────────────────────────────────────────
52
52
 
53
+ const STORAGE_KEY = 'rcode-orch-panel-w';
54
+ const MIN_W = 360;
55
+
53
56
  export function OrchPanel() {
54
57
  const { orchPanel, activeSessions } = useStore();
55
58
  const open = !!(orchPanel && orchPanel.open);
@@ -60,7 +63,15 @@ export function OrchPanel() {
60
63
  // sessionsMap: { [storyId]: { title, lines, fileOps, status } }
61
64
  const [sessionsMap, setSessionsMap] = useState({});
62
65
  const [activeTab, setActiveTab ] = useState(null);
63
- const bodyRef = useRef(null);
66
+ const bodyRef = useRef(null);
67
+ const panelRef = useRef(null);
68
+
69
+ // Restore saved width on open
70
+ useEffect(() => {
71
+ if (!open || !panelRef.current) return;
72
+ const saved = localStorage.getItem(STORAGE_KEY);
73
+ if (saved) panelRef.current.style.setProperty('--orch-w', saved + 'px');
74
+ }, [open]);
64
75
 
65
76
  // Scroll to bottom whenever lines change for the active tab
66
77
  useEffect(() => {
@@ -167,6 +178,35 @@ export function OrchPanel() {
167
178
  setState({ orchPanel: null });
168
179
  }, []);
169
180
 
181
+ // Horizontal resize via the left-edge drag handle
182
+ function handleResizeDown(e) {
183
+ e.preventDefault();
184
+ const startX = e.clientX;
185
+ const el = panelRef.current;
186
+ if (!el) return;
187
+ const startW = el.getBoundingClientRect().width;
188
+ const handle = e.currentTarget;
189
+ handle.classList.add('dragging');
190
+
191
+ function onMove(ev) {
192
+ const delta = startX - ev.clientX;
193
+ const maxW = Math.floor(window.innerWidth * 0.7);
194
+ const w = Math.min(maxW, Math.max(MIN_W, startW + delta));
195
+ el.style.setProperty('--orch-w', w + 'px');
196
+ }
197
+
198
+ function onUp(ev) {
199
+ document.removeEventListener('mousemove', onMove);
200
+ document.removeEventListener('mouseup', onUp);
201
+ handle.classList.remove('dragging');
202
+ const w = Math.round(el.getBoundingClientRect().width);
203
+ localStorage.setItem(STORAGE_KEY, w);
204
+ }
205
+
206
+ document.addEventListener('mousemove', onMove);
207
+ document.addEventListener('mouseup', onUp);
208
+ }
209
+
170
210
  // Open (or focus) a session tab and attach its live stream. Used both for
171
211
  // locally-opened tabs and for sessions discovered via the orchestrator API.
172
212
  function handleTabClick(storyId) {
@@ -221,7 +261,8 @@ export function OrchPanel() {
221
261
  const panelCls = 'orch-panel' + (open ? ' open' : '');
222
262
 
223
263
  return html`
224
- <div class=${panelCls}>
264
+ <div class=${panelCls} ref=${panelRef}>
265
+ <div class="orch-panel-resize" onMouseDown=${handleResizeDown}></div>
225
266
  <div class="orch-panel-header">
226
267
  <div class="orch-panel-title">
227
268
  <span class=${'orch-status-dot' + (runningCount > 0 ? ' up' : '')}></span>
@@ -7,7 +7,8 @@
7
7
  * 1. Live orchestrator sessions (store.activeSessions, status==='running') —
8
8
  * pulsing dot, title from storyId (or the command for cmd-* runs),
9
9
  * elapsed time since startTime; clicking opens the session's orchestrator
10
- * panel (existing openOrchPanel mechanism).
10
+ * panel opens the interactive xterm terminal (openTermPanel) since
11
+ * these rows are always live sessions you may want to talk to.
11
12
  * 2. Scanned tasks from `tasks.inProgress[{ title, pct }]` (pct null → the
12
13
  * percent pill is omitted).
13
14
  *
@@ -19,7 +20,7 @@
19
20
  import { html } from '../../preact.js';
20
21
  import { useStore } from '../../store.js';
21
22
  import { orchElapsed, rowLink } from '../../util.js';
22
- import { openOrchPanel } from '../../orchestrator.js';
23
+ import { openTermPanel } from '../../orchestrator.js';
23
24
  import { pressable } from '../shared.js';
24
25
  import { TaskPipeline } from '../TaskPipeline.js';
25
26
 
@@ -31,8 +32,8 @@ function sessionTitle(s) {
31
32
 
32
33
  function LiveRow({ session: s }) {
33
34
  return html`
34
- <li class="ip-row ip-live-row" title=${'Open session ' + s.storyId}
35
- ...${pressable(() => openOrchPanel(s.storyId))}>
35
+ <li class="ip-row ip-live-row" title=${'Open terminal for ' + s.storyId}
36
+ ...${pressable(() => openTermPanel(s.storyId, sessionTitle(s)))}>
36
37
  <span class="live-dot"></span>
37
38
  <span class="ip-title ip-live-title">${sessionTitle(s)}</span>
38
39
  <span class="ip-live-elapsed">${orchElapsed(s.startTime)}</span>
@@ -354,6 +354,20 @@ export function TaskCard({ task: t }) {
354
354
  ` : null}
355
355
  ${t.acceptance ? html`<div class="task-detail-row"><strong>Acceptance:</strong> ${t.acceptance}</div>` : null}
356
356
  ${t.assignee ? html`<div class="task-detail-row"><strong>Assignee:</strong> ${t.assignee}</div>` : null}
357
+ ${(t.actions && t.actions.length) ? html`
358
+ <div class="task-actions">
359
+ <div class="task-actions-title">Actions performed</div>
360
+ <ol class="task-actions-list">
361
+ ${t.actions.map((step, i) => html`<li key=${i} class="task-action-step">${step}</li>`)}
362
+ </ol>
363
+ </div>
364
+ ` : null}
365
+ ${t.outcome ? html`
366
+ <div class="task-outcome">
367
+ <span class="task-outcome-label">Result</span>
368
+ <span class="task-outcome-text">${t.outcome}</span>
369
+ </div>
370
+ ` : null}
357
371
  ${taskCmds.length ? html`
358
372
  <div class="task-detail-cmds">
359
373
  ${taskCmds.map(([cmd, desc]) => html`<${CmdHint} key=${cmd} cmd=${cmd} desc=${desc}/>`)}
@@ -178,6 +178,19 @@ export function fetchRejections() {
178
178
  .catch(() => []);
179
179
  }
180
180
 
181
+ /**
182
+ * POST /api/task-status — persist a kanban column move for storyId.
183
+ * status is the target column id (todo | in_progress | blocked | done).
184
+ */
185
+ export function setTaskStatus(storyId, status) {
186
+ const tok = orchToken();
187
+ return fetch(ORCH_HTTP + '/api/task-status', {
188
+ method: 'POST',
189
+ headers: { 'Authorization': 'Bearer ' + tok, 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({ storyId, status }),
191
+ }).then(r => r.json()).catch(() => ({}));
192
+ }
193
+
181
194
  /**
182
195
  * POST /api/clean-sessions — remove ended sessions.
183
196
  * olderThanDays = 0 removes all ended sessions; > 0 keeps recent ones.
@@ -10,7 +10,7 @@
10
10
  import { html, useState, useCallback } from '../preact.js';
11
11
  import { useStore, refresh } from '../store.js';
12
12
  import { allTasks, currentPhaseName } from '../util.js';
13
- import { stopStory, openOrchPanel } from '../orchestrator.js';
13
+ import { stopStory, openOrchPanel, openTermPanel, setTaskStatus } from '../orchestrator.js';
14
14
  import { openRunnerPicker } from '../components/RunnerPicker.js';
15
15
  import { showToast } from '../components/shared.js';
16
16
 
@@ -56,6 +56,12 @@ function KanbanCard({ task, col, live, orchDown, onDragStart, onDragEnd }) {
56
56
  e.stopPropagation();
57
57
  stopStory(sid);
58
58
  }
59
+ // Live session → full interactive xterm terminal (you can type to the agent).
60
+ function handleTerm(e) {
61
+ e.stopPropagation();
62
+ openTermPanel(sid, sid);
63
+ }
64
+ // Ended session → lightweight read-only log view (no live TUI to mangle).
59
65
  function handleView(e) {
60
66
  e.stopPropagation();
61
67
  openOrchPanel(sid);
@@ -92,7 +98,7 @@ function KanbanCard({ task, col, live, orchDown, onDragStart, onDragEnd }) {
92
98
  onClick=${handleRun}>▶ Run</button>
93
99
  ` : isRunning ? html`
94
100
  <button class="kanban-stop-btn" onClick=${handleStop}>■ Stop</button>
95
- <button class="kanban-view-btn" onClick=${handleView}>↗ View</button>
101
+ <button class="kanban-view-btn" onClick=${handleTerm}>↗ Terminal</button>
96
102
  ` : html`
97
103
  <button class="kanban-view-btn" onClick=${handleView}>↗ Logs</button>
98
104
  `}
@@ -188,9 +194,32 @@ export function KanbanView() {
188
194
 
189
195
  function handleDrop(e, colId) {
190
196
  if (!dragging || !dragging.id) return;
191
- setVisualMoves(prev => ({ ...prev, [dragging.id]: colId }));
197
+ const taskId = dragging.id;
198
+ const prevCol = effCol(dragging, runningByStory);
199
+ const isRunning = !!(runningByStory && runningByStory[taskId]);
200
+
201
+ // Optimistic visual move
202
+ setVisualMoves(prev => ({ ...prev, [taskId]: colId }));
203
+
204
+ const anchor = document.querySelector('[data-story-id="' + taskId + '"]') || e.currentTarget;
205
+
206
+ if (colId === 'in_progress' && (prevCol === 'todo' || prevCol === 'blocked') && !isRunning) {
207
+ openRunnerPicker(anchor, {
208
+ kind: 'session', storyId: taskId, cmd: '/rcode-dev-story ' + taskId, title: taskId,
209
+ });
210
+ setTaskStatus(taskId, 'in_progress').then(() => {
211
+ refresh();
212
+ setVisualMoves(prev => { const n = { ...prev }; delete n[taskId]; return n; });
213
+ });
214
+ } else {
215
+ setTaskStatus(taskId, colId).then(() => {
216
+ refresh();
217
+ setVisualMoves(prev => { const n = { ...prev }; delete n[taskId]; return n; });
218
+ });
219
+ showToast('Moved to ' + (COLS.find(c => c.id === colId)?.label || colId));
220
+ }
221
+
192
222
  setDragging(null);
193
- showToast('Moved (visual only — not persisted)'); // visual only — not persisted
194
223
  }
195
224
 
196
225
  // ---- Manual refresh ----