@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.
- package/AGENTS.md +1 -1
- package/CONTRIBUTING.md +2 -1
- package/README.md +45 -10
- package/cli/doctor.js +21 -3
- package/cli/index.js +5 -1
- package/cli/install.js +16 -2
- package/cli/postinstall.js +1 -1
- package/cli/uninstall.js +48 -6
- package/dist/rcode.js +178 -174
- package/package.json +12 -22
- package/rcode/agents/rcode-codebase-mapper.md +12 -0
- package/rcode/agents/rcode-phase-researcher.md +13 -0
- package/rcode/agents/rcode-project-researcher.md +32 -0
- package/rcode/agents/rcode-roadmapper.md +15 -0
- package/rcode/agents/rules/planner/common-patterns.md +40 -0
- package/rcode/bin/lib/roadmap.cjs +184 -11
- package/rcode/bin/rcode-hooks.cjs +303 -14
- package/rcode/bin/rcode-tools.cjs +669 -55
- package/rcode/commands/lens-audit.md +8 -6
- package/rcode/data/intent-table.json +87 -0
- package/rcode/modules/seo.yaml +22 -0
- package/rcode/references/project-types.yaml +75 -0
- package/rcode/skills/SKILLS_INDEX.md +20 -2
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/SKILL.md +16 -21
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/backlog-building.md +13 -0
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/merge-strategy.md +17 -0
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/orchestrator-rhythm.md +9 -0
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/rules/wave-design.md +21 -0
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/templates/heartbeat.sh +0 -0
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +60 -1
- package/rcode/skills/core/rcode-lazy/SKILL.md +149 -0
- package/rcode/skills/seo/module.yaml +20 -0
- package/rcode/skills/seo/on-page-seo-auditor/SKILL.md +308 -0
- package/rcode/skills/seo/on-page-seo-auditor/references/audit-example.md +109 -0
- package/rcode/skills/seo/on-page-seo-auditor/references/audit-templates.md +427 -0
- package/rcode/skills/seo/on-page-seo-auditor/references/bulk-audit-playbook.md +116 -0
- package/rcode/skills/seo/on-page-seo-auditor/references/scoring-rubric.md +525 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/SKILL.md +94 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/rules/city-matrix-and-pages.md +76 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/rules/monetization.md +70 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/rules/niche-selection.md +60 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/rules/subniche-discovery.md +66 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/templates/service-city-page.md +36 -0
- package/rcode/skills/seo/rank-and-rent-local-seo/templates/subniche-research-prompt.md +34 -0
- package/rcode/skills/seo/seo-audit/SKILL.md +412 -0
- package/rcode/skills/seo/seo-audit/evals/evals.json +136 -0
- package/rcode/skills/seo/seo-audit/references/ai-writing-detection.md +200 -0
- package/rcode/skills/seo/seo-content-factory/SKILL.md +130 -0
- package/rcode/skills/seo/seo-content-factory/rules/agents.md +80 -0
- package/rcode/skills/seo/seo-content-factory/rules/herdr-wave-mapping.md +80 -0
- package/rcode/skills/seo/seo-content-factory/rules/pipeline.md +102 -0
- package/rcode/skills/seo/seo-content-factory/rules/programmatic-pages.md +107 -0
- package/rcode/skills/seo/seo-content-factory/rules/quality-gates.md +68 -0
- package/rcode/skills/seo/seo-content-factory/rules/weekly-cadence.md +61 -0
- package/rcode/skills/seo/seo-content-factory/templates/BACKLOG.md +23 -0
- package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/01-funnel-A1-A4.md +63 -0
- package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/02-fanout-editorial-A5-A6-A8.md +28 -0
- package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/03-fanout-programmatic-A7-A6-A8.md +25 -0
- package/rcode/skills/seo/seo-content-factory/templates/agent-prompts/04-loop-A9-A10.md +34 -0
- package/rcode/skills/seo/seo-content-factory/templates/article.mdx +39 -0
- package/rcode/skills/seo/seo-content-factory/templates/cluster.schema.json +52 -0
- package/rcode/skills/seo/seo-content-factory/templates/content-brief.md +51 -0
- package/rcode/skills/seo/seo-content-factory/templates/jsonld-builders.ts +49 -0
- package/rcode/skills/seo/seo-content-factory/templates/keywords.csv +13 -0
- package/rcode/skills/seo/seo-content-factory/templates/programmatic-page.tsx +58 -0
- package/rcode/skills/seo/seo-content-writer/SKILL.md +263 -0
- package/rcode/skills/seo/seo-content-writer/references/content-structure-templates.md +875 -0
- package/rcode/skills/seo/seo-content-writer/references/instructions-detail.md +239 -0
- package/rcode/skills/seo/seo-content-writer/references/seo-writing-checklist.md +250 -0
- package/rcode/skills/seo/seo-content-writer/references/title-formulas.md +339 -0
- package/rcode/skills/seo/seo-growth-orchestrator/SKILL.md +79 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/backlinks.md +29 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/content-engine.md +27 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/dos-and-donts.md +40 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/goals-protocol.md +34 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/local-seo-stack.md +35 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/mrr-sprint.md +27 -0
- package/rcode/skills/seo/seo-growth-orchestrator/rules/tools.md +19 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/backlink-research.md +29 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/goal-protocol.md +35 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/local-citations.md +24 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/local-city-pages.md +27 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/local-gbp-categories.md +23 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/local-gsc-goldmine.md +27 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/local-review-mining.md +26 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-30day-plan.md +19 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-cut.md +13 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-opportunity-finder.md +17 -0
- package/rcode/skills/seo/seo-growth-orchestrator/templates/mrr-role-model.md +18 -0
- package/rcode/skills/seo/seo-site-builder/SKILL.md +149 -0
- package/rcode/skills/seo/seo-site-builder/rules/01-niche-discovery.md +64 -0
- package/rcode/skills/seo/seo-site-builder/rules/02-deep-research.md +164 -0
- package/rcode/skills/seo/seo-site-builder/rules/03-keyword-strategy.md +82 -0
- package/rcode/skills/seo/seo-site-builder/rules/04-content-plan.md +173 -0
- package/rcode/skills/seo/seo-site-builder/rules/05-site-build.md +187 -0
- package/rcode/skills/seo/seo-site-builder/rules/06-yolo.md +127 -0
- package/rcode/skills/seo/seo-site-builder/templates/article-stub.mdx +62 -0
- package/rcode/skills/seo/seo-site-builder/templates/image-prompt.md +97 -0
- package/rcode/skills/seo/seo-site-builder/templates/keyword-export.md +45 -0
- package/rcode/skills/seo/seo-site-builder/templates/niche-scorecard.md +28 -0
- package/rcode/skills/seo/technical-seo-checker/SKILL.md +329 -0
- package/rcode/skills/seo/technical-seo-checker/references/bulk-audit-playbook.md +118 -0
- package/rcode/skills/seo/technical-seo-checker/references/ecommerce-platform-patterns.md +166 -0
- package/rcode/skills/seo/technical-seo-checker/references/http-status-codes.md +705 -0
- package/rcode/skills/seo/technical-seo-checker/references/llm-crawler-handling.md +257 -0
- package/rcode/skills/seo/technical-seo-checker/references/pre-migration-playbook.md +137 -0
- package/rcode/skills/seo/technical-seo-checker/references/robots-txt-reference.md +717 -0
- package/rcode/skills/seo/technical-seo-checker/references/technical-audit-example.md +169 -0
- package/rcode/skills/seo/technical-seo-checker/references/technical-audit-templates.md +436 -0
- package/rcode/templates/settings-hooks.json +16 -1
- package/rcode/workflows/do.md +17 -0
- package/rcode/workflows/enable-hooks.md +3 -2
- package/rcode/workflows/execute-sprint.md +22 -0
- package/rcode/workflows/execute.md +17 -3
- package/rcode/workflows/lens-audit.md +49 -8
- package/rcode/workflows/new-milestone.md +21 -9
- package/rcode/workflows/new-project-create-roadmap.md +2 -0
- package/rcode/workflows/new-project-roadmap.md +2 -0
- package/rcode/workflows/new-project.md +3 -0
- package/rcode/workflows/plan.md +15 -2
- package/server/dashboard.js +6 -2
- package/server/lib/api.js +35 -20
- package/server/lib/html/client/components/OrchPanel.js +43 -2
- package/server/lib/html/client/components/dashboard/InProgress.js +5 -4
- package/server/lib/html/client/components/shared.js +14 -0
- package/server/lib/html/client/orchestrator.js +13 -0
- package/server/lib/html/client/views/KanbanView.js +33 -4
- package/server/lib/html/css.js +57 -3
- package/server/lib/scanner.js +40 -4
- 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
|
|
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
|
|
37
|
-
/rcode-lens-audit <N> # run lens N (1-
|
|
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–
|
|
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.
|
|
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–
|
|
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
|
|
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
|
```
|
package/rcode/workflows/plan.md
CHANGED
|
@@ -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:**
|
package/server/dashboard.js
CHANGED
|
@@ -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
|
-
|
|
133
|
-
if (!
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
35
|
-
...${pressable(() =>
|
|
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=${
|
|
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
|
-
|
|
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 ----
|