@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 CHANGED
@@ -12,7 +12,12 @@ const fs = require('fs');
12
12
  const path = require('path');
13
13
 
14
14
  function normalize(name) {
15
- return name.replace(/^rihal-/, '');
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/rihal/,
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)
@@ -43,7 +43,7 @@ function writeFileAtomic(filePath, content, opts = {}) {
43
43
 
44
44
  let fd;
45
45
  try {
46
- fd = fs.openSync(tmpPath, 'w', mode ?? 0o644);
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
@@ -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
- throw new Error(
30
- `gh ${args.join(' ')} failed:\n${result.stderr || result.stdout || '(no output)'}`
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.9",
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 => /PLAN\.md$|-PLAN\.md$|SPRINT\.md$/.test(f)).length,
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.includes('VERIFICATION.md'),
4695
+ has_verification: files.some(f => /VERIFICATION\.md$/i.test(f)),
4696
4696
  };
4697
4697
  }
4698
4698
  return byNum;
@@ -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:
@@ -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 — Parse mode flags
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 matches `phase.disk.plan_count` AND `phase.disk.has_verification` (complete + verified; if VERIFICATION.md absent, use `◎` and label "complete-unverified")
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)