@hanzlaa/rcode 3.4.10 → 3.4.12
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 +5 -0
- package/cli/digest.js +6 -1
- package/cli/index.js +1 -1
- package/cli/install.js +4 -0
- package/cli/lib/fsutil.cjs +1 -1
- package/cli/lib/github.cjs +10 -3
- package/dist/rcode.js +18 -7
- package/package.json +11 -11
- package/rihal/bin/rihal-tools.cjs +2 -2
- package/rihal/workflows/feature-drift.md +2 -2
- package/rihal/workflows/status.md +4 -1
package/README.md
CHANGED
|
@@ -100,6 +100,11 @@ Restart Claude Code (or your IDE), type `/`, and every `rihal-*` command appears
|
|
|
100
100
|
|
|
101
101
|
Update anytime with `npx @hanzlaa/rcode update` (or `/rihal-update` inside a Claude session).
|
|
102
102
|
|
|
103
|
+
> **Want `rcode` on your PATH?** `npx @hanzlaa/rcode install` sets up the project files but doesn't put `rcode` in your shell. For the `rcode` CLI command (e.g. `rcode version`, `rcode update`), install globally once:
|
|
104
|
+
> ```bash
|
|
105
|
+
> npm install -g @hanzlaa/rcode
|
|
106
|
+
> ```
|
|
107
|
+
|
|
103
108
|
### Then begin the rihla
|
|
104
109
|
|
|
105
110
|
```
|
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/install.js
CHANGED
|
@@ -1972,6 +1972,10 @@ async function install(opts) {
|
|
|
1972
1972
|
console.log(dim(' npx @hanzlaa/rcode@latest install # pull the latest rcode + brain'));
|
|
1973
1973
|
console.log(dim(` /rihal-update v${version} # pin rcode to a specific version`));
|
|
1974
1974
|
console.log('');
|
|
1975
|
+
console.log(dim(' Want the rcode CLI on your PATH? (optional — needed for rcode version / rcode update):'));
|
|
1976
|
+
console.log(dim(' npm install -g @hanzlaa/rcode # installs rcode, rihal, rihal-code commands'));
|
|
1977
|
+
console.log(dim(' rcode version # verify'));
|
|
1978
|
+
console.log('');
|
|
1975
1979
|
console.log(dim(' Customize without losing changes on update:'));
|
|
1976
1980
|
console.log(dim(' Create <name>.local.md siblings (e.g. .claude/agents/rihal-waleed.local.md)'));
|
|
1977
1981
|
console.log(dim(' *.local.md files are NEVER touched by install / --force-overwrite / uninstall.'));
|
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/dist/rcode.js
CHANGED
|
@@ -16462,6 +16462,10 @@ ${BLOCK}`);
|
|
|
16462
16462
|
console.log(dim(" npx @hanzlaa/rcode@latest install # pull the latest rcode + brain"));
|
|
16463
16463
|
console.log(dim(` /rihal-update v${version} # pin rcode to a specific version`));
|
|
16464
16464
|
console.log("");
|
|
16465
|
+
console.log(dim(" Want the rcode CLI on your PATH? (optional \u2014 needed for rcode version / rcode update):"));
|
|
16466
|
+
console.log(dim(" npm install -g @hanzlaa/rcode # installs rcode, rihal, rihal-code commands"));
|
|
16467
|
+
console.log(dim(" rcode version # verify"));
|
|
16468
|
+
console.log("");
|
|
16465
16469
|
console.log(dim(" Customize without losing changes on update:"));
|
|
16466
16470
|
console.log(dim(" Create <name>.local.md siblings (e.g. .claude/agents/rihal-waleed.local.md)"));
|
|
16467
16471
|
console.log(dim(" *.local.md files are NEVER touched by install / --force-overwrite / uninstall."));
|
|
@@ -16909,7 +16913,7 @@ var require_fsutil = __commonJS({
|
|
|
16909
16913
|
);
|
|
16910
16914
|
let fd;
|
|
16911
16915
|
try {
|
|
16912
|
-
fd = fs2.openSync(tmpPath, "
|
|
16916
|
+
fd = fs2.openSync(tmpPath, "wx", mode ?? 420);
|
|
16913
16917
|
fs2.writeSync(fd, content, 0, encoding);
|
|
16914
16918
|
fs2.fsyncSync(fd);
|
|
16915
16919
|
fs2.closeSync(fd);
|
|
@@ -17923,7 +17927,11 @@ var require_digest = __commonJS({
|
|
|
17923
17927
|
var fs2 = require("fs");
|
|
17924
17928
|
var path2 = require("path");
|
|
17925
17929
|
function normalize(name) {
|
|
17926
|
-
|
|
17930
|
+
const stripped = name.replace(/^rihal-/, "");
|
|
17931
|
+
if (stripped.includes("..") || stripped.includes("/") || stripped.includes("\\")) {
|
|
17932
|
+
throw new Error(`Invalid agent name: '${name}'`);
|
|
17933
|
+
}
|
|
17934
|
+
return stripped;
|
|
17927
17935
|
}
|
|
17928
17936
|
function listAvailable(digestDir, agentsDir) {
|
|
17929
17937
|
const digestNames = fs2.existsSync(digestDir) ? fs2.readdirSync(digestDir).filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => f.replace(".md", "")) : [];
|
|
@@ -19285,6 +19293,10 @@ var require_show_model = __commonJS({
|
|
|
19285
19293
|
var require_github = __commonJS({
|
|
19286
19294
|
"cli/lib/github.cjs"(exports2, module2) {
|
|
19287
19295
|
var { execSync, spawnSync } = require("child_process");
|
|
19296
|
+
function sanitizeGhOutput(text) {
|
|
19297
|
+
if (!text) return "";
|
|
19298
|
+
return text.replace(/\b(ghp_|ghs_|github_pat_)[A-Za-z0-9_]{10,}\b/g, "[REDACTED]").slice(0, 2e3);
|
|
19299
|
+
}
|
|
19288
19300
|
function runGh(args, { input = null, allowFailure = false } = {}) {
|
|
19289
19301
|
const result = spawnSync("gh", args, {
|
|
19290
19302
|
encoding: "utf8",
|
|
@@ -19292,10 +19304,9 @@ var require_github = __commonJS({
|
|
|
19292
19304
|
stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
19293
19305
|
});
|
|
19294
19306
|
if (result.status !== 0 && !allowFailure) {
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
${
|
|
19298
|
-
);
|
|
19307
|
+
const detail = sanitizeGhOutput(result.stderr || result.stdout || "(no output)");
|
|
19308
|
+
throw new Error(`gh ${args.join(" ")} failed:
|
|
19309
|
+
${detail}`);
|
|
19299
19310
|
}
|
|
19300
19311
|
return {
|
|
19301
19312
|
status: result.status,
|
|
@@ -20408,7 +20419,7 @@ Usage:
|
|
|
20408
20419
|
|
|
20409
20420
|
\u{1F4E6} PROJECT
|
|
20410
20421
|
install Install Rihal Code into the current project
|
|
20411
|
-
(sets up .rihal/, .claude/skills/, .claude/commands
|
|
20422
|
+
(sets up .rihal/, .claude/skills/, .claude/commands/,
|
|
20412
20423
|
.cursor/rules/, .windsurf/rules/, .antigravity/agents/, AGENTS.md)
|
|
20413
20424
|
init Alias for install
|
|
20414
20425
|
update Refresh skill files (backs up .rihal/ state first)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.12",
|
|
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;
|
|
@@ -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:
|
|
@@ -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)
|