@hanzlaa/rcode 4.4.3 → 4.4.4
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/README.md +1 -1
- package/package.json +1 -1
- package/rcode/bin/rcode-tools.cjs +80 -15
- package/rcode/workflows/execute.md +10 -1
- package/rcode/workflows/insert-phase.md +8 -0
- package/rcode/workflows/plan.md +17 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ pnpm dlx @hanzlaa/rcode install
|
|
|
23
23
|
[](https://github.com/hanzlahabib/rcode/actions/workflows/test.yml)
|
|
24
24
|
[](LICENSE)
|
|
25
25
|
|
|
26
|
-
Status: `@hanzlaa/rcode` v4.4.
|
|
26
|
+
Status: `@hanzlaa/rcode` v4.4.4 on npm. 45 agents · 117 commands · 129 workflows · **1 runtime dependency**. Test status tracked by CI badge above. Actively dogfooded on real projects every week.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.4",
|
|
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": {
|
|
@@ -2278,12 +2278,16 @@ function cmdState(subArgs) {
|
|
|
2278
2278
|
}
|
|
2279
2279
|
|
|
2280
2280
|
writeState(state);
|
|
2281
|
+
// #942 — surface the milestone close nudge for inserted phases too.
|
|
2282
|
+
const insHealth = milestoneCloseNudge();
|
|
2281
2283
|
return {
|
|
2282
2284
|
ok: true,
|
|
2283
2285
|
phase_number: phaseNumber,
|
|
2284
2286
|
name: phaseName,
|
|
2285
2287
|
slug: slug,
|
|
2286
2288
|
directory: path.join(PLANNING_DIR, 'phases', `${phaseNumber}-${slug}`),
|
|
2289
|
+
milestone_health: insHealth.milestone_health,
|
|
2290
|
+
...(insHealth.nudge ? { nudge: insHealth.nudge } : {}),
|
|
2287
2291
|
};
|
|
2288
2292
|
}
|
|
2289
2293
|
|
|
@@ -3734,22 +3738,30 @@ function cmdPhase(subArgs) {
|
|
|
3734
3738
|
// value at the scales we operate. Applies to phases, sprints, epics, stories,
|
|
3735
3739
|
// tasks, decisions across all artifacts (dirs, ROADMAP, state.json, banners).
|
|
3736
3740
|
|
|
3737
|
-
// #583 sanity guard: prevent phantom phase numbers caused by stale
|
|
3738
|
-
// entries in ROADMAP.md or phases/ (e.g. a prior phantom
|
|
3739
|
-
// in ROADMAP triggers the next add to produce 1010).
|
|
3740
|
-
//
|
|
3741
|
-
//
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3741
|
+
// #583 / #944 sanity guard: prevent phantom phase numbers caused by stale
|
|
3742
|
+
// high-number entries in ROADMAP.md or phases/ (e.g. a prior phantom
|
|
3743
|
+
// "## Phase 1009" left in ROADMAP triggers the next add to produce 1010).
|
|
3744
|
+
//
|
|
3745
|
+
// The guard must NOT misfire on an INTENTIONAL high-base numbering scheme
|
|
3746
|
+
// (e.g. a milestone that deliberately numbers phases 1031, 1032, …). The
|
|
3747
|
+
// discriminant: is the high number an actual TRACKED phase in state.json,
|
|
3748
|
+
// or only a ROADMAP/dir entry that state has never seen?
|
|
3749
|
+
// - next === maxTracked + 1 → contiguous with real tracked phases →
|
|
3750
|
+
// intentional, allow regardless of absolute magnitude.
|
|
3751
|
+
// - maxNum (overall) sits far ABOVE maxTracked → a non-tracked phantom
|
|
3752
|
+
// is driving the number → suspect, abort.
|
|
3753
|
+
const trackedNums = state.phases
|
|
3754
|
+
.map(p => parseInt(String(p.number || ''), 10))
|
|
3755
|
+
.filter(n => !Number.isNaN(n) && n > 0);
|
|
3756
|
+
const trackedCount = trackedNums.length;
|
|
3757
|
+
const maxTracked = trackedNums.length ? Math.max(...trackedNums) : 0;
|
|
3758
|
+
if (maxNum > maxTracked && (maxNum - maxTracked) > 50) {
|
|
3747
3759
|
throw new Error(
|
|
3748
|
-
`Computed phase number ${next} is
|
|
3749
|
-
`(
|
|
3750
|
-
`ROADMAP.md or the phases/ directory
|
|
3760
|
+
`Computed phase number ${next} is driven by a non-tracked entry ` +
|
|
3761
|
+
`(highest in ROADMAP/phases = ${maxNum}, highest in state.json = ${maxTracked}). ` +
|
|
3762
|
+
`ROADMAP.md or the phases/ directory likely contains a stale high-number entry. ` +
|
|
3751
3763
|
`Inspect with: node rcode-tools.cjs phases list\n` +
|
|
3752
|
-
`Then retry with an explicit number: rcode-tools.cjs phase add "${phaseName}" --number ${
|
|
3764
|
+
`Then retry with an explicit number: rcode-tools.cjs phase add "${phaseName}" --number ${maxTracked + 1}`
|
|
3753
3765
|
);
|
|
3754
3766
|
}
|
|
3755
3767
|
|
|
@@ -3822,12 +3834,17 @@ function cmdPhase(subArgs) {
|
|
|
3822
3834
|
}
|
|
3823
3835
|
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n');
|
|
3824
3836
|
|
|
3837
|
+
// #942 — surface the milestone close nudge from the CLI itself so it can't
|
|
3838
|
+
// be bypassed by adding phases outside the add-phase workflow.
|
|
3839
|
+
const { milestone_health, nudge } = milestoneCloseNudge();
|
|
3825
3840
|
return {
|
|
3826
3841
|
ok: true,
|
|
3827
3842
|
phase_number: number,
|
|
3828
3843
|
name: phaseName,
|
|
3829
3844
|
slug,
|
|
3830
3845
|
directory: path.relative(PROJECT_ROOT, directory),
|
|
3846
|
+
milestone_health,
|
|
3847
|
+
...(nudge ? { nudge } : {}),
|
|
3831
3848
|
};
|
|
3832
3849
|
}
|
|
3833
3850
|
|
|
@@ -3866,6 +3883,19 @@ function cmdPhase(subArgs) {
|
|
|
3866
3883
|
.sort((a, b) => parseInt(String(a.number), 10) - parseInt(String(b.number), 10))[0] || null;
|
|
3867
3884
|
|
|
3868
3885
|
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n');
|
|
3886
|
+
|
|
3887
|
+
// #943 — when no open phases remain, the milestone is effectively finished.
|
|
3888
|
+
// Surface the close/next guidance from this chokepoint so finishing the
|
|
3889
|
+
// last phase via execute/verify/dev-story doesn't strand the user (the
|
|
3890
|
+
// guidance previously only appeared in /rcode-status or progress insights).
|
|
3891
|
+
const doneStatuses = new Set(['complete', 'completed', 'verified', 'shipped']);
|
|
3892
|
+
const openRemaining = state.phases.filter(p => !doneStatuses.has(p.status)).length;
|
|
3893
|
+
let nudge = null;
|
|
3894
|
+
if (openRemaining === 0 && state.phases.length > 0) {
|
|
3895
|
+
nudge = 'All phases are complete — this milestone is finished. ' +
|
|
3896
|
+
'Run /rcode-complete-milestone to archive it, then /rcode-new-milestone to start the next.';
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3869
3899
|
return {
|
|
3870
3900
|
ok: true,
|
|
3871
3901
|
phase: phaseRef,
|
|
@@ -3874,6 +3904,8 @@ function cmdPhase(subArgs) {
|
|
|
3874
3904
|
next_phase: next ? next.number : null,
|
|
3875
3905
|
next_phase_name: next ? (next.name || null) : null,
|
|
3876
3906
|
is_last_phase: !next,
|
|
3907
|
+
open_phases_remaining: openRemaining,
|
|
3908
|
+
...(nudge ? { nudge } : {}),
|
|
3877
3909
|
warnings: [],
|
|
3878
3910
|
has_warnings: false,
|
|
3879
3911
|
};
|
|
@@ -4175,7 +4207,13 @@ function cmdPhase(subArgs) {
|
|
|
4175
4207
|
if (!fs.existsSync(stateDir)) fs.mkdirSync(stateDir, { recursive: true });
|
|
4176
4208
|
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n');
|
|
4177
4209
|
|
|
4178
|
-
|
|
4210
|
+
// #942 — same milestone close nudge for the bulk-draft path.
|
|
4211
|
+
const bulkHealth = milestoneCloseNudge();
|
|
4212
|
+
return {
|
|
4213
|
+
ok: true, count: created.length, phases: created, roadmap_skipped: roadmapSkipped,
|
|
4214
|
+
milestone_health: bulkHealth.milestone_health,
|
|
4215
|
+
...(bulkHealth.nudge ? { nudge: bulkHealth.nudge } : {}),
|
|
4216
|
+
};
|
|
4179
4217
|
}
|
|
4180
4218
|
|
|
4181
4219
|
// =====================================================================
|
|
@@ -7226,6 +7264,33 @@ function cmdMilestoneHealth() {
|
|
|
7226
7264
|
};
|
|
7227
7265
|
}
|
|
7228
7266
|
|
|
7267
|
+
// #942 — build a milestone-health summary + human-readable nudge for any
|
|
7268
|
+
// phase-adding code path (single add, bulk draft, plan, insert) so the
|
|
7269
|
+
// "milestone has too many open phases" guidance can't be bypassed by adding
|
|
7270
|
+
// phases outside the add-phase workflow. Returns { milestone_health, nudge }.
|
|
7271
|
+
function milestoneCloseNudge() {
|
|
7272
|
+
let h;
|
|
7273
|
+
try { h = cmdMilestoneHealth(); } catch { return { milestone_health: null, nudge: null }; }
|
|
7274
|
+
if (!h || !h.ok) return { milestone_health: null, nudge: null };
|
|
7275
|
+
const summary = {
|
|
7276
|
+
open_phases: h.open_phases,
|
|
7277
|
+
recommendation: h.recommendation,
|
|
7278
|
+
threshold_should: h.threshold_should,
|
|
7279
|
+
threshold_consider: h.threshold_consider,
|
|
7280
|
+
};
|
|
7281
|
+
let nudge = null;
|
|
7282
|
+
if (h.recommendation === 'should-close') {
|
|
7283
|
+
nudge = `Milestone "${h.milestone || 'current'}" has ${h.open_phases} open phases ` +
|
|
7284
|
+
`(≥${h.threshold_should}). Consider /rcode-complete-milestone to archive done ` +
|
|
7285
|
+
`phases, then /rcode-new-milestone for ongoing work — before adding more.`;
|
|
7286
|
+
} else if (h.recommendation === 'consider-closing') {
|
|
7287
|
+
nudge = `Milestone "${h.milestone || 'current'}" has ${h.open_phases} open phases ` +
|
|
7288
|
+
`(≥${h.threshold_consider}). Getting large — /rcode-complete-milestone + ` +
|
|
7289
|
+
`/rcode-new-milestone will keep the roadmap navigable.`;
|
|
7290
|
+
}
|
|
7291
|
+
return { milestone_health: summary, nudge };
|
|
7292
|
+
}
|
|
7293
|
+
|
|
7229
7294
|
function cmdStateSnapshot() {
|
|
7230
7295
|
const statePath = path.join(RCODE_DIR, 'state.json');
|
|
7231
7296
|
if (!fs.existsSync(statePath)) return { ok: true, state: null };
|
|
@@ -871,7 +871,16 @@ The CLI handles:
|
|
|
871
871
|
- Updating REQUIREMENTS.md traceability
|
|
872
872
|
- Scanning for verification debt (returns `warnings` array)
|
|
873
873
|
|
|
874
|
-
Extract from result: `next_phase`, `next_phase_name`, `is_last_phase`, `warnings`, `has_warnings`.
|
|
874
|
+
Extract from result: `next_phase`, `next_phase_name`, `is_last_phase`, `warnings`, `has_warnings`, `open_phases_remaining`, `nudge`.
|
|
875
|
+
|
|
876
|
+
**If `nudge` is present (#943 — no open phases remain, milestone finished):**
|
|
877
|
+
Surface it verbatim so the user is guided forward instead of stranded:
|
|
878
|
+
```
|
|
879
|
+
✓ Milestone complete — all phases done.
|
|
880
|
+
{nudge}
|
|
881
|
+
```
|
|
882
|
+
Do not auto-advance past a finished milestone; let the user choose
|
|
883
|
+
`/rcode-complete-milestone` or `/rcode-new-milestone`.
|
|
875
884
|
|
|
876
885
|
**If has_warnings is true:**
|
|
877
886
|
```
|
|
@@ -83,6 +83,14 @@ Next steps:
|
|
|
83
83
|
Or continue with current work and return to this phase later.
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
## Step 3.5 — Surface milestone-health nudge (#942)
|
|
87
|
+
|
|
88
|
+
`state insert-phase` returns a `nudge` field when the milestone has too many
|
|
89
|
+
open phases (≥8 = consider, ≥12 = should-close). If `RESULT.nudge` is present,
|
|
90
|
+
print it verbatim so the user is guided toward `/rcode-complete-milestone` +
|
|
91
|
+
`/rcode-new-milestone` instead of silently accumulating phases. If absent, say
|
|
92
|
+
nothing.
|
|
93
|
+
|
|
86
94
|
## Anti-patterns
|
|
87
95
|
|
|
88
96
|
- Don't insert before Phase 1 (decimal 0.1 makes no sense)
|
package/rcode/workflows/plan.md
CHANGED
|
@@ -906,6 +906,23 @@ node ".rcode/bin/rcode-tools.cjs" state planned-phase --phase "${PHASE_NUMBER}"
|
|
|
906
906
|
|
|
907
907
|
This updates STATUS to "Ready to execute", sets the correct plan count, and timestamps Last Activity.
|
|
908
908
|
|
|
909
|
+
## 13c. Milestone-health nudge (#942)
|
|
910
|
+
|
|
911
|
+
After recording completion, check whether the milestone has accumulated too many
|
|
912
|
+
open phases — so planning the Nth phase of a sprawling milestone guides the user
|
|
913
|
+
toward closing it instead of silently growing the roadmap:
|
|
914
|
+
|
|
915
|
+
```bash
|
|
916
|
+
HEALTH=$(node ".rcode/bin/rcode-tools.cjs" milestone-health 2>/dev/null)
|
|
917
|
+
REC=$(echo "$HEALTH" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{console.log(JSON.parse(s).recommendation||'')}catch{console.log('')}})")
|
|
918
|
+
OPEN=$(echo "$HEALTH" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{console.log(JSON.parse(s).open_phases||0)}catch{console.log(0)}})")
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
- If `REC` is `should-close` (≥12 open): surface a hard nudge recommending
|
|
922
|
+
`/rcode-complete-milestone` then `/rcode-new-milestone`.
|
|
923
|
+
- If `REC` is `consider-closing` (8–11 open): softer nudge.
|
|
924
|
+
- If `healthy`: say nothing.
|
|
925
|
+
|
|
909
926
|
## 14. Present Final Status
|
|
910
927
|
|
|
911
928
|
Route to `<offer_next>` OR `auto_advance` depending on flags/config.
|