@hanzlaa/rcode 3.4.9 → 3.4.11
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/cli/digest.js +6 -1
- package/cli/index.js +1 -1
- package/cli/lib/fsutil.cjs +1 -1
- package/cli/lib/github.cjs +10 -3
- package/package.json +11 -11
- package/rihal/bin/rihal-tools.cjs +2 -2
- package/rihal/workflows/do.md +1 -0
- package/rihal/workflows/feature-drift.md +2 -2
- package/rihal/workflows/phase.md +37 -2
- package/rihal/workflows/status.md +4 -1
package/cli/digest.js
CHANGED
|
@@ -12,7 +12,12 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
|
|
14
14
|
function normalize(name) {
|
|
15
|
-
|
|
15
|
+
const stripped = name.replace(/^rihal-/, '');
|
|
16
|
+
// Reject path traversal attempts — names must be simple identifiers
|
|
17
|
+
if (stripped.includes('..') || stripped.includes('/') || stripped.includes('\\')) {
|
|
18
|
+
throw new Error(`Invalid agent name: '${name}'`);
|
|
19
|
+
}
|
|
20
|
+
return stripped;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
function listAvailable(digestDir, agentsDir) {
|
package/cli/index.js
CHANGED
|
@@ -51,7 +51,7 @@ Usage:
|
|
|
51
51
|
|
|
52
52
|
📦 PROJECT
|
|
53
53
|
install Install Rihal Code into the current project
|
|
54
|
-
(sets up .rihal/, .claude/skills/, .claude/commands
|
|
54
|
+
(sets up .rihal/, .claude/skills/, .claude/commands/,
|
|
55
55
|
.cursor/rules/, .windsurf/rules/, .antigravity/agents/, AGENTS.md)
|
|
56
56
|
init Alias for install
|
|
57
57
|
update Refresh skill files (backs up .rihal/ state first)
|
package/cli/lib/fsutil.cjs
CHANGED
|
@@ -43,7 +43,7 @@ function writeFileAtomic(filePath, content, opts = {}) {
|
|
|
43
43
|
|
|
44
44
|
let fd;
|
|
45
45
|
try {
|
|
46
|
-
fd = fs.openSync(tmpPath, '
|
|
46
|
+
fd = fs.openSync(tmpPath, 'wx', mode ?? 0o644);
|
|
47
47
|
fs.writeSync(fd, content, 0, encoding);
|
|
48
48
|
// fsync the data to disk before rename — otherwise a crash between
|
|
49
49
|
// write() and rename() could leave the target renamed but with zero
|
package/cli/lib/github.cjs
CHANGED
|
@@ -18,6 +18,14 @@ const { execSync, spawnSync } = require('child_process');
|
|
|
18
18
|
|
|
19
19
|
// ---------- Utility: run gh commands safely ----------
|
|
20
20
|
|
|
21
|
+
function sanitizeGhOutput(text) {
|
|
22
|
+
if (!text) return '';
|
|
23
|
+
// Strip anything that looks like a token (ghp_*, ghs_*, github_pat_*)
|
|
24
|
+
return text
|
|
25
|
+
.replace(/\b(ghp_|ghs_|github_pat_)[A-Za-z0-9_]{10,}\b/g, '[REDACTED]')
|
|
26
|
+
.slice(0, 2000);
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
function runGh(args, { input = null, allowFailure = false } = {}) {
|
|
22
30
|
const result = spawnSync('gh', args, {
|
|
23
31
|
encoding: 'utf8',
|
|
@@ -26,9 +34,8 @@ function runGh(args, { input = null, allowFailure = false } = {}) {
|
|
|
26
34
|
});
|
|
27
35
|
|
|
28
36
|
if (result.status !== 0 && !allowFailure) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
37
|
+
const detail = sanitizeGhOutput(result.stderr || result.stdout || '(no output)');
|
|
38
|
+
throw new Error(`gh ${args.join(' ')} failed:\n${detail}`);
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.11",
|
|
4
4
|
"description": "rcode — the memory bank for AI-driven SaaS teams. Persistent project context, distinctive engineering personas, and phase-based workflows. Built by Rihal. Works in Claude Code, Cursor, Gemini, VS Code, and Antigravity.",
|
|
5
5
|
"main": "cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,15 +8,6 @@
|
|
|
8
8
|
"rihal": "dist/rcode.js",
|
|
9
9
|
"rihal-code": "dist/rcode.js"
|
|
10
10
|
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"dashboard": "node server/dashboard.js",
|
|
13
|
-
"test": "node --test",
|
|
14
|
-
"test:ci": "node --test --test-reporter=spec",
|
|
15
|
-
"postinstall": "node cli/postinstall.js",
|
|
16
|
-
"build:cli": "node scripts/build.cjs",
|
|
17
|
-
"build": "node scripts/build.cjs",
|
|
18
|
-
"dogfood": "bash scripts/dogfood-check.sh"
|
|
19
|
-
},
|
|
20
11
|
"files": [
|
|
21
12
|
"cli/",
|
|
22
13
|
"rihal/",
|
|
@@ -69,5 +60,14 @@
|
|
|
69
60
|
},
|
|
70
61
|
"publishConfig": {
|
|
71
62
|
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"dashboard": "node server/dashboard.js",
|
|
66
|
+
"test": "node --test",
|
|
67
|
+
"test:ci": "node --test --test-reporter=spec",
|
|
68
|
+
"postinstall": "node cli/postinstall.js",
|
|
69
|
+
"build:cli": "node scripts/build.cjs",
|
|
70
|
+
"build": "node scripts/build.cjs",
|
|
71
|
+
"dogfood": "bash scripts/dogfood-check.sh"
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}
|
|
@@ -4688,11 +4688,11 @@ function cmdProgress(args) {
|
|
|
4688
4688
|
byNum[num] = {
|
|
4689
4689
|
path: full,
|
|
4690
4690
|
dirName: entry,
|
|
4691
|
-
plan_count: files.filter(f =>
|
|
4691
|
+
plan_count: files.filter(f => /-SPRINT\.md$/i.test(f)).length,
|
|
4692
4692
|
summary_count: files.filter(f => /SUMMARY\.md$|-SUMMARY\.md$/.test(f)).length,
|
|
4693
4693
|
has_research: files.includes('RESEARCH.md'),
|
|
4694
4694
|
has_context: files.includes('CONTEXT.md'),
|
|
4695
|
-
has_verification: files.
|
|
4695
|
+
has_verification: files.some(f => /VERIFICATION\.md$/i.test(f)),
|
|
4696
4696
|
};
|
|
4697
4697
|
}
|
|
4698
4698
|
return byNum;
|
package/rihal/workflows/do.md
CHANGED
|
@@ -296,6 +296,7 @@ Evaluate `$QUESTION` against these routing rules. Apply the **first matching** r
|
|
|
296
296
|
| Implement a story, "work on story", "dev story", "build story" | `/rihal-dev-story` | Story-level implementation |
|
|
297
297
|
| Find gaps in milestone plans, "gaps in plans", "missing plan", "unplanned phases" | `/rihal-plan-milestone-gaps` | Identify and fill planning gaps |
|
|
298
298
|
| Executing a phase, "build phase N", "run phase N", "implement phase" | `/rihal-execute` | Direct phase execution request |
|
|
299
|
+
| `/rihal-phase <number>` where number matches an existing phase dir | `/rihal-execute <N>` | User mistyped phase instead of execute — detect bare integer + existing dir, route to execute |
|
|
299
300
|
| Running all remaining phases automatically | `/rihal-autonomous` | Full autonomous execution |
|
|
300
301
|
| A review or quality concern about existing work | `/rihal-verify-work` | Needs verification |
|
|
301
302
|
| "Council", "discuss strategy", "should we" | `/rihal-council` | Multi-agent strategic discussion |
|
|
@@ -103,8 +103,8 @@ Auditor returns structured JSON:
|
|
|
103
103
|
**If `MODE=phase-status` (Phase 8 / #461):**
|
|
104
104
|
|
|
105
105
|
Spawn `rihal-docs-auditor` with `--mode=phase-status`. Pass:
|
|
106
|
-
- `roadmap_phases[]` — output of `node rihal/bin/rihal-tools.cjs roadmap list-phases` (post-#464 fix)
|
|
107
|
-
- `phase_dirs[]` — output of `node rihal/bin/rihal-tools.cjs init phase-op N` for each phase number, OR a direct walk of `.planning/phases/*` that captures: dir name, presence of `*-SUMMARY.md`, `*-SPRINT.md`, `*-PLAN.md`, `*-CONTEXT.md`, `*-RESEARCH.md`, `*-VERIFICATION.md`
|
|
106
|
+
- `roadmap_phases[]` — output of `node .rihal/bin/rihal-tools.cjs roadmap list-phases` (post-#464 fix)
|
|
107
|
+
- `phase_dirs[]` — output of `node .rihal/bin/rihal-tools.cjs init phase-op N` for each phase number, OR a direct walk of `.planning/phases/*` that captures: dir name, presence of `*-SUMMARY.md`, `*-SPRINT.md`, `*-PLAN.md`, `*-CONTEXT.md`, `*-RESEARCH.md`, `*-VERIFICATION.md`
|
|
108
108
|
- For each phase, the most recent commit hash that touches files in `${phase_dir}/` (used as a freshness signal)
|
|
109
109
|
|
|
110
110
|
Auditor returns structured JSON:
|
package/rihal/workflows/phase.md
CHANGED
|
@@ -14,7 +14,42 @@ If `$ARGUMENTS` is empty AND no flag is set:
|
|
|
14
14
|
|
|
15
15
|
STOP — do not proceed.
|
|
16
16
|
|
|
17
|
-
## Step 1 —
|
|
17
|
+
## Step 1 — Disambiguate numeric arguments BEFORE routing
|
|
18
|
+
|
|
19
|
+
If `$ARGUMENTS` is a bare integer (e.g. `116`, `20`, `7`) with no other words or flags:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Check if a phase directory matching this number already exists
|
|
23
|
+
PHASE_NUM="$ARGUMENTS"
|
|
24
|
+
EXISTING=$(find .planning/phases -maxdepth 1 -type d -name "${PHASE_NUM}-*" 2>/dev/null | head -1)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If `$EXISTING` is non-empty, the user typed a phase number that already exists — they almost certainly meant to operate on it, not create a new one. Stop and ask:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Phase {N} already exists: {directory name}
|
|
31
|
+
|
|
32
|
+
What did you mean to do?
|
|
33
|
+
|
|
34
|
+
/rihal-execute {N} — execute the sprint plan for this phase
|
|
35
|
+
/rihal-plan {N} — re-plan or view the plan for this phase
|
|
36
|
+
/rihal-status — see overall project status
|
|
37
|
+
|
|
38
|
+
/rihal-phase "{description}" — add a NEW phase (put the description in quotes)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Do NOT proceed to add/insert/remove. Wait for the user to clarify.
|
|
42
|
+
|
|
43
|
+
If `$ARGUMENTS` is a bare integer and `$EXISTING` is empty, it's an ambiguous but plausible new-phase name. Proceed to Step 2 but warn:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Note: "{N}" looks like a number. If you meant to execute/plan phase {N}, use /rihal-execute {N} or /rihal-plan {N}.
|
|
47
|
+
Adding a new phase named "{N}" — press Ctrl+C to cancel, or continue.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then proceed to Step 2.
|
|
51
|
+
|
|
52
|
+
## Step 1b — Parse mode flags
|
|
18
53
|
|
|
19
54
|
Inspect `$ARGUMENTS`:
|
|
20
55
|
|
|
@@ -24,7 +59,7 @@ Inspect `$ARGUMENTS`:
|
|
|
24
59
|
|
|
25
60
|
Strip the mode flag and pass remaining args to the underlying workflow.
|
|
26
61
|
|
|
27
|
-
## Step 2 — Dispatch to underlying workflow
|
|
62
|
+
## Step 2 — Dispatch to underlying workflow (after disambiguation)
|
|
28
63
|
|
|
29
64
|
Each mode is implemented by an existing workflow:
|
|
30
65
|
|
|
@@ -58,12 +58,15 @@ the weighted bar as the primary progress indicator to avoid a misleading `0/N (0
|
|
|
58
58
|
For each entry in `SNAPSHOT.phases[]`:
|
|
59
59
|
|
|
60
60
|
- `▶` if `phase.number === SNAPSHOT.current_phase`
|
|
61
|
-
- `✓` if `phase.disk.summary_count > 0` AND
|
|
61
|
+
- `✓` if `phase.disk.summary_count > 0` AND `phase.disk.summary_count >= phase.disk.plan_count` AND `phase.disk.has_verification` (complete + verified; if VERIFICATION.md absent, use `◎` and label "complete-unverified")
|
|
62
|
+
- `◎` if `phase.disk.summary_count > 0` AND `phase.disk.summary_count >= phase.disk.plan_count` AND NOT `phase.disk.has_verification` (work done, awaiting verification)
|
|
62
63
|
- `◆` if `phase.disk.plan_count > phase.disk.summary_count` (executing — has plans, not all summarized)
|
|
63
64
|
- `◇` if `phase.disk.has_context && !phase.disk.plan_count` (discussing — CONTEXT.md exists but no plan yet)
|
|
64
65
|
- `◈` if `phase.disk.has_research && !phase.disk.plan_count` (researched — RESEARCH.md but no plan)
|
|
65
66
|
- `○` otherwise (planned — no artifacts on disk)
|
|
66
67
|
|
|
68
|
+
**Edge case — summary without sprint:** If `summary_count > 0` AND `plan_count === 0`, treat as `◎ complete-unverified` (sprint was archived or the phase used an older single-file workflow; summary is evidence of completed work).
|
|
69
|
+
|
|
67
70
|
```
|
|
68
71
|
Phases:
|
|
69
72
|
▶ [04] Component compaction — executing (1/3 plans)
|