@hanzlaa/rcode 4.7.3 → 4.9.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 (95) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/CONTRIBUTING.md +3 -0
  4. package/README.md +7 -5
  5. package/cli/github-sync.js +17 -201
  6. package/cli/install.js +16 -13
  7. package/cli/lib/config.cjs +1 -1
  8. package/cli/lib/github-sync-discover.cjs +304 -0
  9. package/cli/lib/namespace-migrate.cjs +16 -1
  10. package/cli/uninstall.js +7 -2
  11. package/dist/rcode.js +197 -197
  12. package/package.json +1 -1
  13. package/rcode/agents/rcode-code-reviewer.md +1 -1
  14. package/rcode/agents/rcode-docs-auditor.md +1 -1
  15. package/rcode/agents/rcode-edge-case-hunter.md +1 -1
  16. package/rcode/agents/rcode-haitham.md +1 -1
  17. package/rcode/agents/rcode-hanzla.md +1 -1
  18. package/rcode/agents/rcode-hussain-pm.md +1 -1
  19. package/rcode/agents/rcode-nyquist-auditor.md +1 -1
  20. package/rcode/agents/rcode-omar.md +1 -1
  21. package/rcode/agents/rcode-remediation-planner.md +1 -1
  22. package/rcode/agents/rcode-security-adversary.md +1 -1
  23. package/rcode/agents/rcode-security-auditor.md +1 -1
  24. package/rcode/agents/rcode-sprint-checker.md +1 -1
  25. package/rcode/agents/rcode-verifier.md +1 -1
  26. package/rcode/agents/rcode-waleed.md +1 -1
  27. package/rcode/agents/rcode-yousef.md +1 -1
  28. package/rcode/agents/rules/planner/task-templates.md +24 -22
  29. package/rcode/agents/rules/verifier/verification-report.md +2 -2
  30. package/rcode/bin/lib/roadmap.cjs +80 -4
  31. package/rcode/bin/rcode-tools.cjs +124 -1
  32. package/rcode/brain/best-practices/state-sync-rule.md +1 -1
  33. package/rcode/references/council-protocol.md +9 -4
  34. package/rcode/references/execute-auto-copy-learnings.md +15 -0
  35. package/rcode/references/execute-close-parent-artifacts.md +49 -0
  36. package/rcode/references/execute-interactive-mode.md +44 -0
  37. package/rcode/references/execute-notify-webhooks.md +22 -0
  38. package/rcode/references/execution-protocol.md +11 -11
  39. package/rcode/references/git-preflight.md +7 -4
  40. package/rcode/references/output-format.md +5 -5
  41. package/rcode/references/plan-gaps-mode.md +89 -0
  42. package/rcode/references/plan-thinking-partner.md +20 -0
  43. package/rcode/references/plan-windows-troubleshooting.md +25 -0
  44. package/rcode/references/planner-playbook.md +23 -29
  45. package/rcode/skills/_shared/state-sync-rule.md +1 -1
  46. package/rcode/skills/actions/1-analysis/rcode-document-project/SKILL.md +2 -0
  47. package/rcode/skills/actions/1-analysis/rcode-prfaq/SKILL.md +2 -0
  48. package/rcode/skills/actions/2-plan/rcode-create-epics-and-stories/SKILL.md +11 -0
  49. package/rcode/skills/actions/2-plan/rcode-create-prd/SKILL.md +2 -0
  50. package/rcode/skills/actions/2-plan/rcode-create-story/SKILL.md +11 -0
  51. package/rcode/skills/actions/2-plan/rcode-edit-prd/SKILL.md +2 -0
  52. package/rcode/skills/actions/2-plan/rcode-validate-prd/SKILL.md +2 -0
  53. package/rcode/skills/actions/3-solutioning/rcode-check-implementation-readiness/SKILL.md +2 -0
  54. package/rcode/skills/actions/3-solutioning/rcode-create-architecture/SKILL.md +2 -0
  55. package/rcode/skills/actions/4-implementation/rcode-checkpoint-preview/SKILL.md +2 -0
  56. package/rcode/skills/actions/4-implementation/rcode-code-review/SKILL.md +2 -0
  57. package/rcode/skills/actions/4-implementation/rcode-correct-course/SKILL.md +2 -0
  58. package/rcode/skills/actions/4-implementation/rcode-debug/SKILL.md +1 -1
  59. package/rcode/skills/actions/4-implementation/rcode-dev-story/SKILL.md +2 -0
  60. package/rcode/skills/actions/4-implementation/rcode-retrospective/SKILL.md +2 -0
  61. package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +2 -0
  62. package/rcode/skills/actions/4-implementation/rcode-sprint-planning/SKILL.md +3 -2
  63. package/rcode/skills/actions/4-implementation/rcode-sprint-status/SKILL.md +2 -0
  64. package/rcode/skills/agents/haitham-frontend/SKILL.md +6 -0
  65. package/rcode/skills/agents/hanzla-engineer/SKILL.md +6 -0
  66. package/rcode/skills/agents/hussain-pm/SKILL.md +1 -1
  67. package/rcode/skills/agents/hussain-sm/SKILL.md +3 -3
  68. package/rcode/skills/agents/majlis-council/references.md +17 -7
  69. package/rcode/skills/agents/raees-orchestrator/SKILL.md +13 -11
  70. package/rcode/skills/agents/yousef-backend/SKILL.md +6 -0
  71. package/rcode/templates/github/epic-template.md +1 -1
  72. package/rcode/templates/github/feature-template.md +1 -1
  73. package/rcode/templates/github/task-template.md +1 -1
  74. package/rcode/templates/sprint.md +14 -3
  75. package/rcode/templates/verification-report.md +7 -4
  76. package/rcode/workflows/add-phase.md +33 -14
  77. package/rcode/workflows/code-review-fix.md +5 -5
  78. package/rcode/workflows/code-review.md +1 -1
  79. package/rcode/workflows/council.md +1 -0
  80. package/rcode/workflows/create-epics-and-stories.md +12 -6
  81. package/rcode/workflows/dev-story.md +12 -14
  82. package/rcode/workflows/do.md +12 -9
  83. package/rcode/workflows/execute-sprint.md +7 -4
  84. package/rcode/workflows/execute-waves.md +38 -2
  85. package/rcode/workflows/execute.md +42 -138
  86. package/rcode/workflows/init.md +10 -2
  87. package/rcode/workflows/lens-audit.md +7 -7
  88. package/rcode/workflows/plan-research-validation.md +10 -5
  89. package/rcode/workflows/plan-spawn-planner.md +9 -14
  90. package/rcode/workflows/plan.md +27 -138
  91. package/rcode/workflows/quick.md +1 -1
  92. package/rcode/workflows/scaffold-skill.md +19 -1
  93. package/rcode/workflows/secure-phase.md +7 -1
  94. package/rcode/workflows/sprint-planning.md +14 -37
  95. package/rcode/workflows/validate-phase.md +7 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzlaa/rcode",
3
- "version": "4.7.3",
3
+ "version": "4.9.0",
4
4
  "description": "rcode — the AI team that never forgets. Persistent memory, specialist agents, and slash commands for AI IDEs. Works in Claude Code, Cursor, Gemini, VS Code, and Antigravity.",
5
5
  "main": "cli/index.js",
6
6
  "bin": {
@@ -6,7 +6,7 @@ color: purple
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/code-reviewer-playbook.md
12
12
 
@@ -6,7 +6,7 @@ color: yellow
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/auditor-shared-checklists.md
12
12
  @.rcode/references/docs-auditor-playbook.md
@@ -6,7 +6,7 @@ color: red
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/auditor-shared-checklists.md
12
12
 
@@ -12,7 +12,7 @@ description: |
12
12
  typography / colour system (use Zahra), architecture decisions (use Waleed),
13
13
  backend / API (use Yousef), test strategy (use Fatima), strategic priority
14
14
  (use Sadiq).
15
- tools: Read, Grep, Glob, Bash, WebFetch
15
+ tools: Read, Grep, Glob, Bash, WebFetch, Write, Edit
16
16
  color: cyan
17
17
  ---
18
18
 
@@ -8,7 +8,7 @@ description: |
8
8
  Do NOT use for: architecture (Waleed), backend perf / queues (Yousef),
9
9
  frontend / RTL / a11y (Haitham), UX flows (Layla), test strategy (Fatima),
10
10
  deployment / CI (Khalid), scope (Hussain-PM).
11
- tools: Read, Grep, Glob, Bash
11
+ tools: Read, Grep, Glob, Bash, Write, Edit
12
12
  color: green
13
13
  ---
14
14
 
@@ -9,7 +9,7 @@ description: |
9
9
  Do NOT use for: technical feasibility (Waleed), implementation (Hanzla /
10
10
  Yousef / Haitham), market positioning (Mariam), strategic go/no-go and
11
11
  kill criteria (Sadiq), QA test strategy (Fatima), sprint scrum ops (Hussain-SM).
12
- tools: Read, Grep, Glob, WebFetch
12
+ tools: Read, Grep, Glob, WebFetch, Write, Edit
13
13
  color: orange
14
14
  ---
15
15
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: rcode-nyquist-auditor
3
3
  description: Fills Nyquist validation gaps by generating tests and verifying coverage for phase requirements
4
- tools: Read, Grep, Glob, Bash
4
+ tools: Read, Grep, Glob, Bash, Write, Edit
5
5
  color: purple
6
6
  ---
7
7
 
@@ -10,7 +10,7 @@ description: |
10
10
  deep frontend (use Haitham), deep backend perf (use Yousef), test strategy
11
11
  (use Fatima), scope / PRD (use Hussain-PM), strategic priority (use Sadiq),
12
12
  ML / RAG / embeddings (use Zayd), DevOps / deployment (use Khalid).
13
- tools: Read, Grep, Glob, Bash
13
+ tools: Read, Grep, Glob, Bash, Write, Edit
14
14
  color: green
15
15
  ---
16
16
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: rcode-remediation-planner
3
3
  description: Remediation Planner — spawned to plan remediation for issues, blockers, and failures. Creates action plans to recover from deviations, resolve blockers, and get back on track.
4
- tools: Read, Grep, Glob, Bash, Edit
4
+ tools: Read, Grep, Glob, Bash
5
5
  color: orange
6
6
  ---
7
7
 
@@ -6,7 +6,7 @@ color: red
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/auditor-shared-checklists.md
12
12
 
@@ -6,7 +6,7 @@ color: purple
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/auditor-shared-checklists.md
12
12
 
@@ -6,7 +6,7 @@ color: green
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/sprint-checker-playbook.md
11
11
 
12
12
  <role>
@@ -6,7 +6,7 @@ color: green
6
6
  ---
7
7
 
8
8
  @.rcode/references/response-style.md
9
- @.rcode/references/karpathy-guidelines-full.md
9
+ @.rcode/references/karpathy-guidelines.md
10
10
  @.rcode/references/no-unauthorized-git-ops.md
11
11
  @.rcode/references/verifier-playbook.md
12
12
 
@@ -10,7 +10,7 @@ description: |
10
10
  Do NOT use for: strategy / "should we build" (Sadiq), backend impl (Yousef),
11
11
  scope / PRD (Hussain-PM), test strategy (Fatima), market / GTM (Mariam),
12
12
  org-level multi-team coordination (Ahmed-Hassani-Director).
13
- tools: Read, Grep, Glob, Bash, WebFetch, WebSearch
13
+ tools: Read, Grep, Glob, Bash, WebFetch, WebSearch, Write, Edit
14
14
  color: green
15
15
  ---
16
16
 
@@ -11,7 +11,7 @@ description: |
11
11
  (use Haitham), test methodology (use Fatima), strategic priority (use Sadiq),
12
12
  scope / PRD (use Hussain-PM), implementation across the full stack
13
13
  (use Hanzla / Omar), deployment / CI (use Khalid).
14
- tools: Read, Grep, Glob, Bash, WebFetch
14
+ tools: Read, Grep, Glob, Bash, WebFetch, Write, Edit
15
15
  color: blue
16
16
  ---
17
17
 
@@ -1,10 +1,12 @@
1
1
  # rcode Planner: Task Templates by Type
2
2
 
3
+ <!-- id= and <title> are REQUIRED across every template below — this is what server/lib/scanner.js's buildPhaseTree actually parses. Do not use a `name` child tag or a bare title attribute; both are legacy/unsupported paths. -->
4
+
3
5
  ## Standard Task Template
4
6
 
5
7
  ```xml
6
- <task type="auto">
7
- <name>Task: [Action-oriented name]</name>
8
+ <task id="{sprint-id}.{NN}" type="auto">
9
+ <title>Task: [Action-oriented name]</title>
8
10
  <files>path/to/file.ext, path/to/another.ts</files>
9
11
  <action>[Specific implementation with what to avoid and WHY]</action>
10
12
  <verify>
@@ -21,8 +23,8 @@
21
23
  Use when you can write `expect(fn(input)).toBe(output)` before implementing.
22
24
 
23
25
  ```xml
24
- <task type="auto" tdd="true">
25
- <name>Task: [name]</name>
26
+ <task id="{sprint-id}.{NN}" type="auto" tdd="true">
27
+ <title>Task: [name]</title>
26
28
  <files>src/feature.ts, src/feature.test.ts</files>
27
29
  <behavior>
28
30
  - Test 1: [expected behavior]
@@ -44,8 +46,8 @@ Use when you can write `expect(fn(input)).toBe(output)` before implementing.
44
46
  Use for visual/functional verification (90% of checkpoints).
45
47
 
46
48
  ```xml
47
- <task type="checkpoint:human-verify" gate="blocking">
48
- <name>Verify: [Feature name works]</name>
49
+ <task id="{sprint-id}.{NN}" type="checkpoint:human-verify" gate="blocking">
50
+ <title>Verify: [Feature name works]</title>
49
51
  <what-built>[What the agent automated]</what-built>
50
52
  <how-to-verify>
51
53
  1. Navigate to http://localhost:3000/login
@@ -65,8 +67,8 @@ Use for visual/functional verification (90% of checkpoints).
65
67
  Use for implementation choices (9% of checkpoints).
66
68
 
67
69
  ```xml
68
- <task type="checkpoint:decision" gate="blocking">
69
- <name>Decide: Database schema for messages</name>
70
+ <task id="{sprint-id}.{NN}" type="checkpoint:decision" gate="blocking">
71
+ <title>Decide: Database schema for messages</title>
70
72
  <what-youre-deciding>
71
73
  Where to store user-to-message relationships:
72
74
 
@@ -94,8 +96,8 @@ Use for implementation choices (9% of checkpoints).
94
96
  Use for unavoidable manual steps (1% of checkpoints).
95
97
 
96
98
  ```xml
97
- <task type="checkpoint:human-action" gate="blocking">
98
- <name>Set up Stripe API key</name>
99
+ <task id="{sprint-id}.{NN}" type="checkpoint:human-action" gate="blocking">
100
+ <title>Set up Stripe API key</title>
99
101
  <what-needed>
100
102
  The next task implements Stripe payment processing, which requires your API key.
101
103
  </what-needed>
@@ -115,8 +117,8 @@ Use for unavoidable manual steps (1% of checkpoints).
115
117
  ## Database Migration Task Template
116
118
 
117
119
  ```xml
118
- <task type="auto">
119
- <name>Task: Add role field to users table</name>
120
+ <task id="{sprint-id}.{NN}" type="auto">
121
+ <title>Task: Add role field to users table</title>
120
122
  <files>prisma/schema.prisma, prisma/migrations/[timestamp]_add_role.sql</files>
121
123
  <action>
122
124
  1. Update User model in schema: add `role String @default("user")`
@@ -136,8 +138,8 @@ Use for unavoidable manual steps (1% of checkpoints).
136
138
  ## API Endpoint Task Template
137
139
 
138
140
  ```xml
139
- <task type="auto">
140
- <name>Task: Implement POST /api/users endpoint</name>
141
+ <task id="{sprint-id}.{NN}" type="auto">
142
+ <title>Task: Implement POST /api/users endpoint</title>
141
143
  <files>src/app/api/users/route.ts, src/types/user.ts</files>
142
144
  <action>
143
145
  Create POST endpoint accepting { email, password, name }:
@@ -167,8 +169,8 @@ Use for unavoidable manual steps (1% of checkpoints).
167
169
  ## UI Component Task Template
168
170
 
169
171
  ```xml
170
- <task type="auto">
171
- <name>Task: Create LoginForm component</name>
172
+ <task id="{sprint-id}.{NN}" type="auto">
173
+ <title>Task: Create LoginForm component</title>
172
174
  <files>src/components/LoginForm.tsx, src/components/LoginForm.test.tsx</files>
173
175
  <action>
174
176
  Create form component with:
@@ -195,8 +197,8 @@ Use for unavoidable manual steps (1% of checkpoints).
195
197
  ## Configuration Task Template
196
198
 
197
199
  ```xml
198
- <task type="auto">
199
- <name>Task: Configure environment variables</name>
200
+ <task id="{sprint-id}.{NN}" type="auto">
201
+ <title>Task: Configure environment variables</title>
200
202
  <files>.env.example, .env.local (local only)</files>
201
203
  <action>
202
204
  1. Create .env.example with template (no real values):
@@ -219,8 +221,8 @@ Use for unavoidable manual steps (1% of checkpoints).
219
221
  ## Documentation Task Template
220
222
 
221
223
  ```xml
222
- <task type="auto">
223
- <name>Task: Document authentication flow</name>
224
+ <task id="{sprint-id}.{NN}" type="auto">
225
+ <title>Task: Document authentication flow</title>
224
226
  <files>docs/AUTHENTICATION.md</files>
225
227
  <action>
226
228
  Document in docs/AUTHENTICATION.md:
@@ -242,8 +244,8 @@ Use for unavoidable manual steps (1% of checkpoints).
242
244
  ## Refactoring Task Template
243
245
 
244
246
  ```xml
245
- <task type="auto">
246
- <name>Task: Extract auth validation to utility</name>
247
+ <task id="{sprint-id}.{NN}" type="auto">
248
+ <title>Task: Extract auth validation to utility</title>
247
249
  <files>src/lib/auth-validation.ts, src/api/auth/login/route.ts</files>
248
250
  <action>
249
251
  1. Create src/lib/auth-validation.ts with functions:
@@ -4,7 +4,7 @@
4
4
 
5
5
  **ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation.
6
6
 
7
- Create `.rcode/phases/{phase_dir}/{phase_num}-VERIFICATION.md`:
7
+ Create `.planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md`:
8
8
 
9
9
  ```markdown
10
10
  ---
@@ -108,7 +108,7 @@ Return with:
108
108
 
109
109
  **Status:** {passed | gaps_found | human_needed}
110
110
  **Score:** {N}/{M} must-haves verified
111
- **Report:** .rcode/phases/{phase_dir}/{phase_num}-VERIFICATION.md
111
+ **Report:** .planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md
112
112
 
113
113
  {If passed:}
114
114
  All must-haves verified. Phase goal achieved. Ready to proceed.
@@ -326,7 +326,12 @@ function cmdUpdatePlanProgress(projectRoot, phaseNum, planId, status) {
326
326
  if (!phaseNum) {
327
327
  throw new Error('Usage: roadmap update-plan-progress <phase> [<plan-id> <status>]');
328
328
  }
329
- // 1-arg form: auto-detect progress from disk
329
+ // 1-arg form: auto-detect progress from disk and persist it into the
330
+ // phase's **Status:**/**Plans:** prose block in ROADMAP.md.
331
+ // Fix #1013: this branch used to only *compute* status from disk and
332
+ // return it — it never read/wrote ROADMAP.md at all, so it always
333
+ // reported `updated: false` even when the computed status genuinely
334
+ // differed from the stale prose already in the file.
330
335
  if (!planId && !status) {
331
336
  const planningDir = path.join(projectRoot, '.planning', 'phases');
332
337
  let phaseDir = null;
@@ -344,12 +349,83 @@ function cmdUpdatePlanProgress(projectRoot, phaseNum, planId, status) {
344
349
  const planFiles = files.filter(f => /(?:^|-)(SPRINT|PLAN)\.md$/i.test(f));
345
350
  const summaryFiles = files.filter(f => /(?:^|-)SUMMARY\.md$/i.test(f));
346
351
  const allDone = planFiles.length > 0 && summaryFiles.length >= planFiles.length;
352
+ const computedStatus = allDone ? 'complete' : 'in_progress';
353
+ const note = `phase ${phaseNum}: ${planFiles.length} plan(s), ${summaryFiles.length} summary(s) — ${allDone ? 'complete' : 'in progress'}`;
354
+
355
+ const { path: rp } = resolveRoadmapPath(projectRoot);
356
+ if (!fs.existsSync(rp)) {
357
+ return { updated: false, note, plan_count: planFiles.length, summary_count: summaryFiles.length, status: computedStatus, error: 'ROADMAP.md not found' };
358
+ }
359
+ const content = fs.readFileSync(rp, 'utf8');
360
+ const phases = extractPhases(content);
361
+ const phase = phases.find((p) => normalizePhaseNum(p.number) === normalizePhaseNum(phaseNum));
362
+ if (!phase) {
363
+ return { updated: false, note, plan_count: planFiles.length, summary_count: summaryFiles.length, status: computedStatus, error: `phase ${phaseNum} not found in ROADMAP.md` };
364
+ }
365
+
366
+ const planIdRe = /^(\d+[A-Z]?(?:\.\d+)?-\d+)-(?:SPRINT|PLAN)\.md$/i;
367
+ const summaryIdRe = /^(\d+[A-Z]?(?:\.\d+)?-\d+)-SUMMARY\.md$/i;
368
+ const summarizedIds = new Set(
369
+ summaryFiles.map((f) => { const m = f.match(summaryIdRe); return m ? m[1] : null; }).filter(Boolean)
370
+ );
371
+ const planIds = [...new Set(
372
+ planFiles.map((f) => { const m = f.match(planIdRe); return m ? m[1] : null; }).filter(Boolean)
373
+ )].sort();
374
+
375
+ const section = content.slice(phase.headerIndex, phase.sectionEnd);
376
+ let newSection = section;
377
+ let changed = false;
378
+
379
+ // Update **Status:** — only when the computed status actually differs
380
+ // from what the prose says, so re-running this after completion is a
381
+ // no-op instead of rewriting the completion date every time.
382
+ const statusLineRe = /(\*\*Status(?::\*\*|\*\*:)\s*)([^\n]+)/i;
383
+ const statusMatch = section.match(statusLineRe);
384
+ if (statusMatch) {
385
+ const currentStatusNorm = statusMatch[2].trim().toLowerCase();
386
+ if (computedStatus === 'complete' && !currentStatusNorm.startsWith('complete')) {
387
+ const today = new Date().toISOString().slice(0, 10);
388
+ newSection = newSection.replace(statusLineRe, `$1Complete (${today})`);
389
+ changed = true;
390
+ } else if (computedStatus === 'in_progress' && currentStatusNorm.startsWith('planned')) {
391
+ newSection = newSection.replace(statusLineRe, `$1In Progress`);
392
+ changed = true;
393
+ }
394
+ }
395
+
396
+ // Replace a placeholder **Plans:** list (`- _TBD_` or empty) with the
397
+ // plan IDs actually found on disk. Leave already-populated lists alone.
398
+ if (planIds.length > 0) {
399
+ const plansBlockRe = /(\*\*Plans(?::\*\*|\*\*:)\s*\n)((?:\s*-\s*[^\n]+\n?)+)/i;
400
+ const plansMatch = newSection.match(plansBlockRe);
401
+ if (plansMatch) {
402
+ const isPlaceholder = /^\s*-\s*_TBD_\s*$/im.test(plansMatch[2]) || plansMatch[2].trim() === '';
403
+ if (isPlaceholder) {
404
+ const plansList = planIds
405
+ .map((id) => `- ${id} — ${summarizedIds.has(id) ? 'SUMMARY shipped' : 'in progress'}`)
406
+ .join('\n') + '\n';
407
+ newSection = newSection.replace(plansBlockRe, `$1${plansList}`);
408
+ changed = true;
409
+ }
410
+ }
411
+ }
412
+
413
+ if (!changed) {
414
+ return { updated: false, note: `${note} (ROADMAP.md already reflects this)`, plan_count: planFiles.length, summary_count: summaryFiles.length, status: computedStatus };
415
+ }
416
+
417
+ const newContent = content.slice(0, phase.headerIndex) + newSection + content.slice(phase.sectionEnd);
418
+ const tmp = rp + '.tmp';
419
+ fs.writeFileSync(tmp, newContent, 'utf8');
420
+ fs.renameSync(tmp, rp);
421
+
347
422
  return {
348
- updated: false,
349
- note: `phase ${phaseNum}: ${planFiles.length} plan(s), ${summaryFiles.length} summary(s) — ${allDone ? 'complete' : 'in progress'}`,
423
+ updated: true,
424
+ phase: phaseNum,
425
+ status: computedStatus,
350
426
  plan_count: planFiles.length,
351
427
  summary_count: summaryFiles.length,
352
- status: allDone ? 'complete' : 'in_progress',
428
+ note: `ROADMAP.md phase ${phaseNum} block updated to ${computedStatus}`,
353
429
  };
354
430
  }
355
431
 
@@ -381,6 +381,7 @@ function cmdInit(workflowName, rawArgs) {
381
381
 
382
382
  let panel = [];
383
383
  let scores = {};
384
+ let domain = null;
384
385
 
385
386
  let agent_id = null;
386
387
 
@@ -395,8 +396,12 @@ function cmdInit(workflowName, rawArgs) {
395
396
  // Don't pad when user explicitly specified the agent list — their
396
397
  // choice is the final word.
397
398
  panel = filterPanelToInstalled(ideal, councilAgents, { pad: flags.agents.length === 0 });
399
+ // #1010 — domain must populate on every council session, not just
400
+ // --explain, because council.md's <output_format> banner prints
401
+ // Domain: unconditionally.
402
+ const explained = scorer.explainSelection(question, opts);
403
+ domain = explained.domain || null;
398
404
  if (flags.explain) {
399
- const explained = scorer.explainSelection(question, opts);
400
405
  scores = explained.scores || {};
401
406
  }
402
407
  }
@@ -426,9 +431,14 @@ function cmdInit(workflowName, rawArgs) {
426
431
  flags,
427
432
  panel,
428
433
  scores,
434
+ domain,
429
435
  question_type: questionClassification.type,
430
436
  question_signals: questionClassification.signals,
431
437
  mode: config.mode || null,
438
+ // #1010 — response_language was only ever populated for phase-op/
439
+ // sprint-plan below; council.md documents it as a top-level init field
440
+ // and gates language pass-through to subagents on it being set.
441
+ response_language: config.response_language || config.language || null,
432
442
  config,
433
443
  installed_agents: installedAgents,
434
444
  paths: {
@@ -1519,6 +1529,10 @@ function cmdState(subArgs) {
1519
1529
  // =====================================================================
1520
1530
 
1521
1531
  // --- sprint add --phase NN --goal "Sprint goal" ---
1532
+ // NOTE: this populates entry.sprints[] (an array). A separate code path,
1533
+ // 'planned-phase' below (~line 3152), populates entry.plans (a plain count).
1534
+ // These two fields are never reconciled with each other — see AUDIT-redundant-work.md
1535
+ // finding 1 cross-check. Do not assume one implies the other is populated.
1522
1536
  if (sub === 'sprint' && subArgs[1] === 'add') {
1523
1537
  const flags = parseFlags(2);
1524
1538
  const state = readState() || defaultState();
@@ -3149,6 +3163,9 @@ function cmdState(subArgs) {
3149
3163
  // Execution-lifecycle phase state
3150
3164
  // =====================================================================
3151
3165
 
3166
+ // NOTE: entry.plans (a count) is disjoint from entry.sprints[] (an array,
3167
+ // set by 'sprint add' above) — see the comment there. Known schema divergence,
3168
+ // not yet unified; do not read one as evidence the other is in sync.
3152
3169
  if (sub === 'planned-phase') {
3153
3170
  const flags = parseFlags(1);
3154
3171
  if (!flags.phase) throw new Error('planned-phase requires --phase <N>');
@@ -5385,6 +5402,109 @@ function cmdPlanCheckWaveOverlaps(rawArgs) {
5385
5402
  return { phase: phaseArg, phase_dir: path.relative(PROJECT_ROOT, phaseDir), plans_checked: plans.length, conflicts };
5386
5403
  }
5387
5404
 
5405
+ /**
5406
+ * Deterministic frontend/backend glob check for classify-plan (issue #1021).
5407
+ * Mirrors the FRONTEND_GLOBS / BACKEND_GLOBS rules formerly hand-applied by
5408
+ * the orchestrating LLM in execute-waves.md — kept here as the single source
5409
+ * of truth so both the CLI and the workflow doc describe the same behavior.
5410
+ */
5411
+ function matchesFrontendGlob(file) {
5412
+ const f = String(file || '').toLowerCase();
5413
+ if (/\.(tsx|jsx|css)$/.test(f)) return true;
5414
+ return f.includes('client') || f.includes('ui');
5415
+ }
5416
+ function matchesBackendGlob(file) {
5417
+ const f = String(file || '').toLowerCase();
5418
+ return f.includes('api') || f.includes('server') || f.includes('db') || f.includes('service');
5419
+ }
5420
+
5421
+ const CLASSIFY_PLAN_ROUTE = { frontend: 'rcode-haitham', backend: 'rcode-yousef', 'full-stack': 'rcode-hanzla', other: 'rcode-executor' };
5422
+
5423
+ function classifyPlanFiles(files, objective) {
5424
+ const touchesFrontend = files.some(matchesFrontendGlob);
5425
+ const touchesBackend = files.some(matchesBackendGlob);
5426
+ let classification;
5427
+ if (touchesFrontend && touchesBackend) classification = 'full-stack';
5428
+ else if (touchesFrontend) classification = 'frontend';
5429
+ else if (touchesBackend) classification = 'backend';
5430
+ else classification = 'other';
5431
+
5432
+ if (classification === 'other') {
5433
+ const obj = String(objective || '').toLowerCase();
5434
+ const frontendKeywords = ['react', 'component', 'ui', 'css', 'tailwind', 'frontend', 'client-side', 'accessibility', 'a11y'];
5435
+ const backendKeywords = ['api', 'endpoint', 'database', 'schema', 'service', 'queue', 'backend', 'server-side'];
5436
+ if (frontendKeywords.some((k) => obj.includes(k))) classification = 'frontend';
5437
+ else if (backendKeywords.some((k) => obj.includes(k))) classification = 'backend';
5438
+ }
5439
+ return classification;
5440
+ }
5441
+
5442
+ /**
5443
+ * classify-plan — deterministic replacement for execute-waves.md's
5444
+ * hand-computed FRONTEND_GLOBS/BACKEND_GLOBS classification (issue #1021).
5445
+ * A live execution run showed the orchestrating LLM never actually carried
5446
+ * out the prose pseudocode, so a plan with a "db"-containing path still fell
5447
+ * back to rcode-executor instead of rcode-yousef.
5448
+ *
5449
+ * Two call shapes:
5450
+ * classify-plan <phase> <plan-id> — reads files_modified/objective from the plan's SPRINT.md
5451
+ * classify-plan --files=a,b,c --objective="..." — classify an already-parsed list directly
5452
+ */
5453
+ function cmdClassifyPlan(args) {
5454
+ const flags = {};
5455
+ const positional = [];
5456
+ for (const t of args) {
5457
+ if (t.startsWith('--files=')) flags.files = t.slice('--files='.length);
5458
+ else if (t.startsWith('--objective=')) flags.objective = t.slice('--objective='.length);
5459
+ else positional.push(t);
5460
+ }
5461
+
5462
+ let files = [];
5463
+ let objective = '';
5464
+
5465
+ if (flags.files !== undefined || flags.objective !== undefined) {
5466
+ files = flags.files ? flags.files.split(',').map((s) => s.trim()).filter(Boolean) : [];
5467
+ objective = flags.objective || '';
5468
+ } else {
5469
+ const [phaseArg, planArg] = positional;
5470
+ if (!phaseArg || !planArg) {
5471
+ throw new Error('Usage: classify-plan <phase> <plan-id> OR classify-plan --files=a,b,c --objective="text"');
5472
+ }
5473
+ const phasesDir = path.join(PLANNING_DIR, 'phases');
5474
+ const norm = phaseArg.replace(/^0+/, '') || '0';
5475
+ let phaseDir = null;
5476
+ if (fs.existsSync(phasesDir)) {
5477
+ for (const d of fs.readdirSync(phasesDir)) {
5478
+ const m = d.match(/^(\d+)(?:[-.])/);
5479
+ if (m && (m[1].replace(/^0+/, '') || '0') === norm) { phaseDir = path.join(phasesDir, d); break; }
5480
+ }
5481
+ }
5482
+ if (!phaseDir) throw new Error(`Phase not found: ${phaseArg}`);
5483
+ let planFile = null;
5484
+ for (const file of fs.readdirSync(phaseDir).filter((f) => /-SPRINT\.md$/i.test(f)).sort()) {
5485
+ const stem = file.replace(/-SPRINT\.md$/i, '');
5486
+ if (stem === planArg || stem.endsWith(`-${planArg}`)) { planFile = file; break; }
5487
+ const text = fs.readFileSync(path.join(phaseDir, file), 'utf8');
5488
+ const { frontmatter } = parseFrontmatter(text);
5489
+ if ((frontmatter.sprint || frontmatter.plan) === planArg) { planFile = file; break; }
5490
+ }
5491
+ if (!planFile) throw new Error(`Plan not found: ${planArg} in phase ${phaseArg}`);
5492
+ const text = fs.readFileSync(path.join(phaseDir, planFile), 'utf8');
5493
+ const { frontmatter, body } = parseFrontmatter(text);
5494
+ let block = '';
5495
+ if (text.startsWith('---\n')) {
5496
+ const end = text.indexOf('\n---\n', 4);
5497
+ if (end !== -1) block = text.slice(4, end);
5498
+ }
5499
+ files = fmListField(block, 'files_modified');
5500
+ const objMatch = body.match(/^##\s+(?:Objective|Goal)\s*\n+([^\n]+)/mi);
5501
+ objective = objMatch ? objMatch[1].trim() : (frontmatter.goal || '').replace(/^["']|["']$/g, '');
5502
+ }
5503
+
5504
+ const classification = classifyPlanFiles(files, objective);
5505
+ return { classification, subagent_type: CLASSIFY_PLAN_ROUTE[classification], files_checked: files.length };
5506
+ }
5507
+
5388
5508
  /** phases list — directory inventory under .planning/phases with optional --type filter and --pick path. */
5389
5509
  function cmdPhasesList(args) {
5390
5510
  const argv = Array.isArray(args) ? args : String(args || '').trim().split(/\s+/).filter(Boolean);
@@ -6641,6 +6761,9 @@ async function main() {
6641
6761
  case 'phase-plan-index':
6642
6762
  result = cmdPhasePlanIndex(args.join(' '));
6643
6763
  break;
6764
+ case 'classify-plan':
6765
+ result = cmdClassifyPlan(args);
6766
+ break;
6644
6767
  case 'phases':
6645
6768
  if (args[0] === 'list') { result = cmdPhasesList(args.slice(1)); if (result === undefined) return; }
6646
6769
  else { console.error('Unknown phases subcommand. Valid: list'); process.exit(1); }
@@ -8,7 +8,7 @@ Immediately after appending content to any of:
8
8
 
9
9
  - `.planning/ROADMAP.md` — milestones and phases
10
10
  - `.planning/epics.md` — epics and stories
11
- - `.rcode/phases/{phase}/sprint-{N}.md` — sprint commitments
11
+ - `.planning/phases/{phase-dir}/{phase}-{plan}-SPRINT.md` — sprint commitments
12
12
 
13
13
  Call the state-sync helper:
14
14
 
@@ -32,7 +32,7 @@ Agreements without new information are silence. If an agent genuinely has nothin
32
32
 
33
33
  ## Panel selection (deterministic scoring)
34
34
 
35
- rcode's council uses a pure-function keyword scorer (`cli/lib/council-panel.cjs`) instead of LLM judgment. This is intentional:
35
+ rcode's council uses a pure-function keyword scorer (`rcode/bin/lib/council-panel.cjs` source, installed at `.rcode/bin/lib/council-panel.cjs`) instead of LLM judgment. This is intentional:
36
36
 
37
37
  - **Deterministic:** same question produces same panel every time
38
38
  - **Testable:** the scoring function has unit tests
@@ -47,13 +47,18 @@ The scoring table is versioned with the package. Users who want different weight
47
47
  - Scope/feature questions (containing "scope", "feature", "roadmap", "prd") always include Hussain-PM.
48
48
  - If fewer than the minimum panel size (default 3) score non-zero, the panel is padded with the STRATEGIC_PADDING_ORDER list: sadiq, hussain-pm, waleed, fatima, nasser.
49
49
 
50
- **v2 prototype note:** only Sadiq, Waleed, and Fatima are installed as first-class subagents. The scorer may select agent ids that don't yet have subagent files — the orchestrator must filter to installed agents and fall back to the 3-agent panel.
50
+ **Installed roster:** the full agent roster (currently 45 agents under `rcode/agents/`) is installed as first-class subagents. The scorer may still select an agent id that isn't present in a given project's installed set — the orchestrator filters the scored panel down to `installed_agents` from `init council` and pads from the fallback order if needed.
51
51
 
52
52
  ## Response presentation
53
53
 
54
- Subagent responses are presented **verbatim and in panel order**. The orchestrator never summarizes, paraphrases, or condenses agent output. The user came to the council to hear the agents speak in their own voicessummary defeats the point.
54
+ Two presentation modes control how panel responses appear inline. The session artifact (see below) always stores the full verbatim text regardless of modeonly the inline presentation differs:
55
55
 
56
- After all verbatim responses, the orchestrator may add a single **Orchestrator Note** (max 3 sentences) that flags a disagreement worth following up on or recommends a second round. The Orchestrator Note is clearly labeled so it's not confused with agent speech.
56
+ - **Default (compact summary)** scannable in ~20 seconds: a one-sentence paraphrased position per agent, a convergence/divergence table, and Round 2 deltas. No verbatim transcripts inline.
57
+ - **Verbose** (`--verbose` flag or `output.verbose: true` in config) — full verbatim responses printed in panel order, no summarization.
58
+
59
+ The user came to the council to hear the agents' actual positions, not filler agreement — so even in compact mode, paraphrase the substance, not "I agree with X."
60
+
61
+ After the panel output, the orchestrator may add a single **Orchestrator Note** (max 2-3 sentences) that flags a disagreement worth following up on or recommends a second round. The Orchestrator Note is clearly labeled so it's not confused with agent speech.
57
62
 
58
63
  ## Session artifact format
59
64
 
@@ -0,0 +1,15 @@
1
+ # execute.md — auto_copy_learnings (global learnings store)
2
+
3
+ Extracted from `execute.md`'s `auto_copy_learnings` step. Only loaded when `features.global_learnings` is enabled (disabled by default) — see the conditional include at that point in `execute.md`.
4
+
5
+ **Auto-copy phase learnings to global store (when enabled).**
6
+
7
+ This step runs AFTER phase completion and SUMMARY.md is written. It copies any LEARNINGS.md
8
+ entries from the completed phase to the global learnings store at `.rcode/knowledge/`.
9
+
10
+ 1. Check if LEARNINGS.md exists in the phase directory (use the `phase_dir` value from init context)
11
+ 2. If found, copy to global store:
12
+ ```bash
13
+ node ".rcode/bin/rcode-tools.cjs" learnings copy 2>/dev/null || echo "⚠ Learnings copy failed — continuing"
14
+ ```
15
+ Copy failure must NOT block phase completion.