@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.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +3 -0
- package/README.md +7 -5
- package/cli/github-sync.js +17 -201
- package/cli/install.js +16 -13
- package/cli/lib/config.cjs +1 -1
- package/cli/lib/github-sync-discover.cjs +304 -0
- package/cli/lib/namespace-migrate.cjs +16 -1
- package/cli/uninstall.js +7 -2
- package/dist/rcode.js +197 -197
- package/package.json +1 -1
- package/rcode/agents/rcode-code-reviewer.md +1 -1
- package/rcode/agents/rcode-docs-auditor.md +1 -1
- package/rcode/agents/rcode-edge-case-hunter.md +1 -1
- package/rcode/agents/rcode-haitham.md +1 -1
- package/rcode/agents/rcode-hanzla.md +1 -1
- package/rcode/agents/rcode-hussain-pm.md +1 -1
- package/rcode/agents/rcode-nyquist-auditor.md +1 -1
- package/rcode/agents/rcode-omar.md +1 -1
- package/rcode/agents/rcode-remediation-planner.md +1 -1
- package/rcode/agents/rcode-security-adversary.md +1 -1
- package/rcode/agents/rcode-security-auditor.md +1 -1
- package/rcode/agents/rcode-sprint-checker.md +1 -1
- package/rcode/agents/rcode-verifier.md +1 -1
- package/rcode/agents/rcode-waleed.md +1 -1
- package/rcode/agents/rcode-yousef.md +1 -1
- package/rcode/agents/rules/planner/task-templates.md +24 -22
- package/rcode/agents/rules/verifier/verification-report.md +2 -2
- package/rcode/bin/lib/roadmap.cjs +80 -4
- package/rcode/bin/rcode-tools.cjs +124 -1
- package/rcode/brain/best-practices/state-sync-rule.md +1 -1
- package/rcode/references/council-protocol.md +9 -4
- package/rcode/references/execute-auto-copy-learnings.md +15 -0
- package/rcode/references/execute-close-parent-artifacts.md +49 -0
- package/rcode/references/execute-interactive-mode.md +44 -0
- package/rcode/references/execute-notify-webhooks.md +22 -0
- package/rcode/references/execution-protocol.md +11 -11
- package/rcode/references/git-preflight.md +7 -4
- package/rcode/references/output-format.md +5 -5
- package/rcode/references/plan-gaps-mode.md +89 -0
- package/rcode/references/plan-thinking-partner.md +20 -0
- package/rcode/references/plan-windows-troubleshooting.md +25 -0
- package/rcode/references/planner-playbook.md +23 -29
- package/rcode/skills/_shared/state-sync-rule.md +1 -1
- package/rcode/skills/actions/1-analysis/rcode-document-project/SKILL.md +2 -0
- package/rcode/skills/actions/1-analysis/rcode-prfaq/SKILL.md +2 -0
- package/rcode/skills/actions/2-plan/rcode-create-epics-and-stories/SKILL.md +11 -0
- package/rcode/skills/actions/2-plan/rcode-create-prd/SKILL.md +2 -0
- package/rcode/skills/actions/2-plan/rcode-create-story/SKILL.md +11 -0
- package/rcode/skills/actions/2-plan/rcode-edit-prd/SKILL.md +2 -0
- package/rcode/skills/actions/2-plan/rcode-validate-prd/SKILL.md +2 -0
- package/rcode/skills/actions/3-solutioning/rcode-check-implementation-readiness/SKILL.md +2 -0
- package/rcode/skills/actions/3-solutioning/rcode-create-architecture/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-checkpoint-preview/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-code-review/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-correct-course/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-debug/SKILL.md +1 -1
- package/rcode/skills/actions/4-implementation/rcode-dev-story/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-retrospective/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-scaffold-project/SKILL.md +2 -0
- package/rcode/skills/actions/4-implementation/rcode-sprint-planning/SKILL.md +3 -2
- package/rcode/skills/actions/4-implementation/rcode-sprint-status/SKILL.md +2 -0
- package/rcode/skills/agents/haitham-frontend/SKILL.md +6 -0
- package/rcode/skills/agents/hanzla-engineer/SKILL.md +6 -0
- package/rcode/skills/agents/hussain-pm/SKILL.md +1 -1
- package/rcode/skills/agents/hussain-sm/SKILL.md +3 -3
- package/rcode/skills/agents/majlis-council/references.md +17 -7
- package/rcode/skills/agents/raees-orchestrator/SKILL.md +13 -11
- package/rcode/skills/agents/yousef-backend/SKILL.md +6 -0
- package/rcode/templates/github/epic-template.md +1 -1
- package/rcode/templates/github/feature-template.md +1 -1
- package/rcode/templates/github/task-template.md +1 -1
- package/rcode/templates/sprint.md +14 -3
- package/rcode/templates/verification-report.md +7 -4
- package/rcode/workflows/add-phase.md +33 -14
- package/rcode/workflows/code-review-fix.md +5 -5
- package/rcode/workflows/code-review.md +1 -1
- package/rcode/workflows/council.md +1 -0
- package/rcode/workflows/create-epics-and-stories.md +12 -6
- package/rcode/workflows/dev-story.md +12 -14
- package/rcode/workflows/do.md +12 -9
- package/rcode/workflows/execute-sprint.md +7 -4
- package/rcode/workflows/execute-waves.md +38 -2
- package/rcode/workflows/execute.md +42 -138
- package/rcode/workflows/init.md +10 -2
- package/rcode/workflows/lens-audit.md +7 -7
- package/rcode/workflows/plan-research-validation.md +10 -5
- package/rcode/workflows/plan-spawn-planner.md +9 -14
- package/rcode/workflows/plan.md +27 -138
- package/rcode/workflows/quick.md +1 -1
- package/rcode/workflows/scaffold-skill.md +19 -1
- package/rcode/workflows/secure-phase.md +7 -1
- package/rcode/workflows/sprint-planning.md +14 -37
- 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.
|
|
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: yellow
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
@.rcode/references/response-style.md
|
|
9
|
-
@.rcode/references/karpathy-guidelines
|
|
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
|
|
@@ -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
|
|
|
@@ -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
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
5
|
color: orange
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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 `.
|
|
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:** .
|
|
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:
|
|
349
|
-
|
|
423
|
+
updated: true,
|
|
424
|
+
phase: phaseNum,
|
|
425
|
+
status: computedStatus,
|
|
350
426
|
plan_count: planFiles.length,
|
|
351
427
|
summary_count: summaryFiles.length,
|
|
352
|
-
|
|
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
|
-
- `.
|
|
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 (`
|
|
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
|
-
**
|
|
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
|
-
|
|
54
|
+
Two presentation modes control how panel responses appear inline. The session artifact (see below) always stores the full verbatim text regardless of mode — only the inline presentation differs:
|
|
55
55
|
|
|
56
|
-
|
|
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.
|