@hanzlaa/rcode 3.4.33 → 3.6.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.
Files changed (106) hide show
  1. package/AGENTS.md +6 -6
  2. package/CONTRIBUTING.md +2 -0
  3. package/LICENSE +21 -0
  4. package/README.md +66 -403
  5. package/cli/doctor.js +87 -1
  6. package/cli/install.js +122 -31
  7. package/cli/lib/schemas.cjs +318 -0
  8. package/cli/postinstall.js +19 -3
  9. package/dist/rcode.js +316 -23
  10. package/package.json +14 -4
  11. package/rihal/agents/rihal-cross-platform-auditor.md +1 -1
  12. package/rihal/agents/rihal-dep-auditor.md +1 -1
  13. package/rihal/agents/rihal-docs-auditor.md +3 -145
  14. package/rihal/agents/rihal-i18n-auditor.md +1 -1
  15. package/rihal/agents/rihal-nyquist-auditor.md +4 -156
  16. package/rihal/agents/rihal-observability-auditor.md +1 -1
  17. package/rihal/bin/rihal-hooks.cjs +394 -4
  18. package/rihal/bin/rihal-tools.cjs +891 -24
  19. package/rihal/commands/create-prd.md +18 -0
  20. package/rihal/commands/execute-milestone.md +18 -0
  21. package/rihal/commands/plan-milestone.md +18 -0
  22. package/rihal/commands/scaffold-milestone.md +18 -0
  23. package/rihal/commands/scaffold-skill.md +18 -0
  24. package/rihal/references/REFERENCES_INDEX.md +49 -7
  25. package/rihal/references/agent-contracts.md +10 -0
  26. package/rihal/references/design-tokens.md +98 -0
  27. package/rihal/references/docs-auditor-playbook.md +148 -0
  28. package/rihal/references/git-preflight.md +117 -0
  29. package/rihal/references/iterative-retrieval.md +85 -0
  30. package/rihal/references/nyquist-auditor-playbook.md +157 -0
  31. package/rihal/references/workstream-flag.md +2 -2
  32. package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +9 -0
  33. package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +9 -0
  34. package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +4 -0
  35. package/rihal/skills/actions/4-implementation/rihal-code-review/steps/step-02-review.md +2 -2
  36. package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +4 -0
  37. package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +4 -0
  38. package/rihal/skills/agents/haitham-frontend/SKILL.md +2 -0
  39. package/rihal/templates/settings-hooks.json +39 -0
  40. package/rihal/workflows/check-todos.md +4 -0
  41. package/rihal/workflows/code-review-fix.md +4 -3
  42. package/rihal/workflows/code-review.md +1 -1
  43. package/rihal/workflows/debug.md +1 -1
  44. package/rihal/workflows/dev-story.md +4 -0
  45. package/rihal/workflows/diff.md +2 -2
  46. package/rihal/workflows/do.md +16 -8
  47. package/rihal/workflows/docs-update.md +2 -2
  48. package/rihal/workflows/enable-hooks.md +6 -1
  49. package/rihal/workflows/execute-milestone.md +139 -0
  50. package/rihal/workflows/execute-regression-gates.md +1 -1
  51. package/rihal/workflows/execute-sprint.md +54 -2
  52. package/rihal/workflows/execute-verify-phase-goal.md +31 -4
  53. package/rihal/workflows/execute-waves.md +33 -5
  54. package/rihal/workflows/execute.md +40 -6
  55. package/rihal/workflows/help.md +1 -1
  56. package/rihal/workflows/import.md +1 -1
  57. package/rihal/workflows/lens-audit.md +39 -23
  58. package/rihal/workflows/list-workspaces.md +1 -1
  59. package/rihal/workflows/map-codebase.md +4 -4
  60. package/rihal/workflows/new-milestone.md +18 -1
  61. package/rihal/workflows/new-project-research.md +53 -1
  62. package/rihal/workflows/new-workspace.md +1 -1
  63. package/rihal/workflows/plan-milestone.md +105 -0
  64. package/rihal/workflows/plan-research-validation.md +1 -1
  65. package/rihal/workflows/plan-spawn-planner.md +1 -1
  66. package/rihal/workflows/plan.md +31 -3
  67. package/rihal/workflows/plant-seed.md +6 -0
  68. package/rihal/workflows/quick.md +11 -5
  69. package/rihal/workflows/research-phase.md +24 -0
  70. package/rihal/workflows/scaffold-milestone.md +60 -0
  71. package/rihal/workflows/scaffold-skill.md +137 -0
  72. package/rihal/workflows/scan.md +1 -1
  73. package/rihal/workflows/session-report.md +43 -3
  74. package/rihal/workflows/verify-work.md +3 -3
  75. package/server/dashboard.js +154 -5
  76. package/server/lib/html/client/agents-data.js +27 -0
  77. package/server/lib/html/client/app.js +15 -0
  78. package/server/lib/html/client/components/App.js +211 -0
  79. package/server/lib/html/client/components/OrchPanel.js +293 -0
  80. package/server/lib/html/client/components/Sidebar.js +73 -0
  81. package/server/lib/html/client/components/Topbar.js +53 -0
  82. package/server/lib/html/client/components/XtermPanel.js +220 -0
  83. package/server/lib/html/client/components/shared.js +330 -0
  84. package/server/lib/html/client/icons-client.js +85 -0
  85. package/server/lib/html/client/orchestrator.js +279 -0
  86. package/server/lib/html/client/preact.js +34 -0
  87. package/server/lib/html/client/store.js +91 -0
  88. package/server/lib/html/client/util.js +186 -0
  89. package/server/lib/html/client/views/AgentsView.js +83 -0
  90. package/server/lib/html/client/views/DecisionsView.js +102 -0
  91. package/server/lib/html/client/views/FilesView.js +223 -0
  92. package/server/lib/html/client/views/KanbanView.js +236 -0
  93. package/server/lib/html/client/views/MemoryView.js +157 -0
  94. package/server/lib/html/client/views/MilestonesView.js +136 -0
  95. package/server/lib/html/client/views/OrchestrationView.js +167 -0
  96. package/server/lib/html/client/views/OverviewView.js +221 -0
  97. package/server/lib/html/client/views/PhasesView.js +184 -0
  98. package/server/lib/html/client/views/RoadmapView.js +238 -0
  99. package/server/lib/html/client/views/SprintsView.js +178 -0
  100. package/server/lib/html/client/views/TasksView.js +148 -0
  101. package/server/lib/html/client.js +42 -1064
  102. package/server/lib/html/css.js +2266 -466
  103. package/server/lib/html/icons.js +68 -0
  104. package/server/lib/html/shell.js +16 -210
  105. package/server/lib/scanner.js +109 -0
  106. package/server/orchestrator.js +362 -0
package/cli/doctor.js CHANGED
@@ -17,6 +17,11 @@ const path = require('path');
17
17
  const { spawnSync } = require('child_process');
18
18
  const { verifyInstall, formatReport } = require('./lib/manifest.cjs');
19
19
  const { checkStaleness } = require('./lib/memory-bank.cjs');
20
+ const {
21
+ parseFrontmatter,
22
+ validateSkillFrontmatter,
23
+ validateAgentFrontmatter,
24
+ } = require('./lib/schemas.cjs');
20
25
 
21
26
  // ---------- Shared helpers ----------
22
27
 
@@ -34,6 +39,19 @@ function findSkillFiles(dir) {
34
39
  return results;
35
40
  }
36
41
 
42
+ /**
43
+ * One-level scan of `rihal/agents/` for `.md` files. There is no existing
44
+ * agent-file iterator (findSkillFiles matches only files named SKILL.md),
45
+ * so the schema-validation pass needs its own shallow glob.
46
+ */
47
+ function findAgentFiles(dir) {
48
+ if (!fs.existsSync(dir)) return [];
49
+ return fs
50
+ .readdirSync(dir, { withFileTypes: true })
51
+ .filter((e) => e.isFile() && e.name.endsWith('.md'))
52
+ .map((e) => path.join(dir, e.name));
53
+ }
54
+
37
55
  function checkCompliance(filePath) {
38
56
  const content = fs.readFileSync(filePath, 'utf8');
39
57
  const missing = [];
@@ -259,6 +277,70 @@ function runCompliance(packageRoot) {
259
277
  return failing;
260
278
  }
261
279
 
280
+ // ---------- Schema validation ----------
281
+
282
+ /**
283
+ * Validate SKILL.md and agent frontmatter against the zod schemas in
284
+ * cli/lib/schemas.cjs (issue #747). Hard failures (missing name, too few
285
+ * trigger phrases, missing negative boundary, missing tools) count toward
286
+ * the non-zero exit path; advisory warnings (e.g. >12 trigger phrases) just
287
+ * print a ⚠.
288
+ *
289
+ * @returns {number} count of artifacts with hard failures
290
+ */
291
+ function runSchemaValidation(packageRoot) {
292
+ const skillDirs = [
293
+ path.join(packageRoot, 'rihal/skills/agents'),
294
+ path.join(packageRoot, 'rihal/skills/actions'),
295
+ ];
296
+
297
+ let totalSkills = 0;
298
+ let totalAgents = 0;
299
+ let failing = 0;
300
+ let warned = 0;
301
+
302
+ for (const dir of skillDirs) {
303
+ for (const file of findSkillFiles(dir)) {
304
+ totalSkills++;
305
+ const { frontmatter, body } = parseFrontmatter(fs.readFileSync(file, 'utf8'));
306
+ const result = validateSkillFrontmatter(frontmatter, body);
307
+ const rel = path.relative(packageRoot, file);
308
+ if (!result.ok) {
309
+ failing++;
310
+ console.log(` ✗ ${rel}`);
311
+ for (const err of result.errors) console.log(` ${err}`);
312
+ }
313
+ if (result.warnings && result.warnings.length > 0) {
314
+ warned++;
315
+ for (const w of result.warnings) console.log(` ⚠ ${rel}: ${w}`);
316
+ }
317
+ }
318
+ }
319
+
320
+ for (const file of findAgentFiles(path.join(packageRoot, 'rihal/agents'))) {
321
+ totalAgents++;
322
+ const { frontmatter } = parseFrontmatter(fs.readFileSync(file, 'utf8'));
323
+ const result = validateAgentFrontmatter(frontmatter);
324
+ if (!result.ok) {
325
+ failing++;
326
+ const rel = path.relative(packageRoot, file);
327
+ console.log(` ✗ ${rel}`);
328
+ for (const err of result.errors) console.log(` ${err}`);
329
+ }
330
+ }
331
+
332
+ if (failing === 0) {
333
+ console.log(
334
+ ` ✓ ${totalSkills} skill + ${totalAgents} agent frontmatter blocks pass schema validation` +
335
+ (warned > 0 ? ` (${warned} with advisory warnings)` : ''),
336
+ );
337
+ } else {
338
+ console.log(` ✗ ${failing} artifact(s) failed schema validation`);
339
+ }
340
+
341
+ return failing;
342
+ }
343
+
262
344
  // ---------- Duplicate-installation check ----------
263
345
 
264
346
  /**
@@ -348,7 +430,11 @@ module.exports = function doctor(args, { packageRoot }) {
348
430
  console.log(`\nPackage compliance:`);
349
431
  const complianceFailures = runCompliance(packageRoot);
350
432
 
351
- const totalFailures = preflightFailures + complianceFailures + duplicateFailures;
433
+ console.log(`\nArtifact schema validation:`);
434
+ const schemaFailures = runSchemaValidation(packageRoot);
435
+
436
+ const totalFailures =
437
+ preflightFailures + complianceFailures + duplicateFailures + schemaFailures;
352
438
  console.log();
353
439
  if (totalFailures === 0) {
354
440
  console.log(`✅ All checks passed.`);
package/cli/install.js CHANGED
@@ -89,7 +89,7 @@ const SOURCE_ROOT = path.join(PACKAGE_ROOT, 'rihal');
89
89
  * detectIdeSignals, plus a row to runInstallWizard's multiselect — three
90
90
  * sites instead of ten.
91
91
  */
92
- const SUPPORTED_IDES = Object.freeze(['claude', 'cursor', 'gemini', 'vscode', 'antigravity']);
92
+ const SUPPORTED_IDES = Object.freeze(['claude', 'cursor', 'gemini', 'vscode', 'antigravity', 'windsurf']);
93
93
 
94
94
  // Zod schema for .rihal/config.yaml validation (#250).
95
95
  const ConfigSchema = z.object({
@@ -305,13 +305,14 @@ function printInstallHeader(targetVersion) {
305
305
  * Returns a set like { claude: true, cursor: false, gemini: false }.
306
306
  */
307
307
  function detectIdeSignals(target) {
308
- const signals = { claude: false, cursor: false, gemini: false, vscode: false, antigravity: false };
308
+ const signals = { claude: false, cursor: false, gemini: false, vscode: false, antigravity: false, windsurf: false };
309
309
  // 1. Project-local install dirs (strongest signal — they already use one)
310
310
  if (fs.existsSync(path.join(target, '.claude'))) signals.claude = true;
311
311
  if (fs.existsSync(path.join(target, '.cursor'))) signals.cursor = true;
312
312
  if (fs.existsSync(path.join(target, '.gemini'))) signals.gemini = true;
313
313
  if (fs.existsSync(path.join(target, '.vscode'))) signals.vscode = true;
314
314
  if (fs.existsSync(path.join(target, '.antigravity'))) signals.antigravity = true;
315
+ if (fs.existsSync(path.join(target, '.windsurf'))) signals.windsurf = true;
315
316
  // 2. User-level config dirs
316
317
  const home = os.homedir();
317
318
  if (fs.existsSync(path.join(home, '.claude'))) signals.claude = true;
@@ -321,10 +322,13 @@ function detectIdeSignals(target) {
321
322
  if (fs.existsSync(path.join(home, '.vscode'))) signals.vscode = true;
322
323
  if (fs.existsSync(path.join(home, '.config', 'Code'))) signals.vscode = true;
323
324
  if (fs.existsSync(path.join(home, '.antigravity'))) signals.antigravity = true;
325
+ if (fs.existsSync(path.join(home, '.windsurf'))) signals.windsurf = true;
326
+ if (fs.existsSync(path.join(home, '.codeium', 'windsurf'))) signals.windsurf = true;
324
327
  // 3. Env vars commonly set by editor terminals
325
328
  if (process.env.CURSOR_TRACE_ID || /cursor/i.test(process.env.TERM_PROGRAM || '')) signals.cursor = true;
326
329
  if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_ENTRYPOINT) signals.claude = true;
327
330
  if (process.env.VSCODE_PID || /vscode/i.test(process.env.TERM_PROGRAM || '')) signals.vscode = true;
331
+ if (/windsurf/i.test(process.env.TERM_PROGRAM || '')) signals.windsurf = true;
328
332
  return signals;
329
333
  }
330
334
 
@@ -496,11 +500,12 @@ function getPathsForIde(ide, target) {
496
500
  case 'vscode':
497
501
  // VS Code's Claude Code / Continue / Copilot extensions all read from
498
502
  // .claude/ (Claude Code's canonical paths). We install there directly
499
- // and additionally write a .vscode/rihal/ marker so VS Code workspace
500
- // settings can pin behaviour.
503
+ // using the SAME layout as the claude case (prefixed-root form) so
504
+ // multi-IDE installs don't double up — see #723 / #635-#643 / #646.
505
+ // The .vscode/rihal/ marker is preserved for workspace settings.
501
506
  return {
502
507
  agentsDir: path.join(target, '.claude', 'agents'),
503
- commandsDir: path.join(target, '.claude', 'commands', 'rihal'),
508
+ commandsDir: path.join(target, '.claude', 'commands'),
504
509
  workflowsDir: path.join(target, '.rihal', 'workflows'),
505
510
  referencesDir: path.join(target, '.rihal', 'references'),
506
511
  binDir: path.join(target, '.rihal', 'bin'),
@@ -518,8 +523,19 @@ function getPathsForIde(ide, target) {
518
523
  referencesDir: path.join(target, '.rihal', 'references'),
519
524
  binDir: path.join(target, '.rihal', 'bin'),
520
525
  };
526
+ case 'windsurf':
527
+ // Windsurf (Codeium's agentic IDE) — uses .windsurf/rules/ for .mdc rule
528
+ // files, parallel to cursor's .cursor/rules/. cli/lib/manifest.cjs already
529
+ // handles the rules-install verify path (#723 closes the install-side gap).
530
+ return {
531
+ agentsDir: path.join(target, '.windsurf', 'rules', 'rihal', 'agents'),
532
+ commandsDir: path.join(target, '.windsurf', 'rules', 'rihal', 'commands'),
533
+ workflowsDir: path.join(target, '.rihal', 'workflows'),
534
+ referencesDir: path.join(target, '.rihal', 'references'),
535
+ binDir: path.join(target, '.rihal', 'bin'),
536
+ };
521
537
  default:
522
- throw new Error(`Unknown IDE: ${ide}. Supported: claude, cursor, gemini, vscode, antigravity`);
538
+ throw new Error(`Unknown IDE: ${ide}. Supported: ${SUPPORTED_IDES.join(', ')}`);
523
539
  }
524
540
  }
525
541
 
@@ -1044,6 +1060,51 @@ function parseFrontmatter(text) {
1044
1060
  *
1045
1061
  * For cursor IDE, converts command files from .md to .mdc format.
1046
1062
  */
1063
+ /**
1064
+ * Migrate legacy vscode-layout commands (.claude/commands/rihal/{name}.md)
1065
+ * to the unified prefixed-root form (.claude/commands/rihal-{name}.md).
1066
+ *
1067
+ * Idempotent. Safe to run on every install/update — no-op when no legacy
1068
+ * dir exists. After move, removes the now-empty rihal/ subdir.
1069
+ *
1070
+ * Returns { moved, removed_dir } so callers can log the migration count.
1071
+ * Designed by Waleed for #723; closes the dual-layout cause of #635, #637,
1072
+ * #638, #639, #640, #641, #642, #643, #646.
1073
+ */
1074
+ function migrateVscodeCommandsLayout(target) {
1075
+ const legacyDir = path.join(target, '.claude', 'commands', 'rihal');
1076
+ const newRoot = path.join(target, '.claude', 'commands');
1077
+ if (!fs.existsSync(legacyDir) || !fs.statSync(legacyDir).isDirectory()) {
1078
+ return { moved: 0, removed_dir: false };
1079
+ }
1080
+ let moved = 0;
1081
+ for (const entry of fs.readdirSync(legacyDir)) {
1082
+ const src = path.join(legacyDir, entry);
1083
+ if (!fs.statSync(src).isFile() || !entry.endsWith('.md')) continue;
1084
+ const baseName = path.basename(entry, '.md');
1085
+ // Don't double-prefix if someone already had rihal-foo.md inside rihal/.
1086
+ const targetName = baseName.startsWith('rihal-') ? entry : `rihal-${entry}`;
1087
+ const dst = path.join(newRoot, targetName);
1088
+ if (fs.existsSync(dst)) {
1089
+ // Already migrated by an earlier pass — remove the duplicate at source.
1090
+ fs.unlinkSync(src);
1091
+ continue;
1092
+ }
1093
+ fs.renameSync(src, dst);
1094
+ moved++;
1095
+ }
1096
+ // Remove the now-empty legacy dir. fs.rmdir fails if non-empty — that's
1097
+ // a signal worth surfacing (manual user files in the dir we shouldn't touch).
1098
+ let removedDir = false;
1099
+ try {
1100
+ fs.rmdirSync(legacyDir);
1101
+ removedDir = true;
1102
+ } catch (_) {
1103
+ // Non-empty (user files we don't manage) — leave it alone.
1104
+ }
1105
+ return { moved, removed_dir: removedDir };
1106
+ }
1107
+
1047
1108
  function buildInstallPlan(ide = 'claude', target = process.cwd()) {
1048
1109
  // Support array of IDEs — merge plans with deduplication (#449/#450 multi-IDE).
1049
1110
  if (Array.isArray(ide)) {
@@ -1058,26 +1119,12 @@ function buildInstallPlan(ide = 'claude', target = process.cwd()) {
1058
1119
  }
1059
1120
  }
1060
1121
  }
1061
- // When both claude and vscode are in the IDE list, vscode writes commands to
1062
- // .claude/commands/rihal/{name}.md (subdirectory) while claude writes them to
1063
- // .claude/commands/rihal-{name}.md (root). Claude Code reads the full tree
1064
- // recursively, so both sets appear as slash commandsduplicates in the UI.
1065
- // Drop the vscode-style subdir entries when claude entries already cover them.
1066
- if (ide.includes('claude') && ide.includes('vscode')) {
1067
- const claudeCommandRels = new Set(
1068
- merged
1069
- .filter(e => e.ide === 'claude' && e.rel.split(path.sep).join('/').startsWith('.claude/commands/'))
1070
- .map(e => path.basename(e.rel, '.md').replace(/^rihal-/, ''))
1071
- );
1072
- return merged.filter(e => {
1073
- const rel = e.rel.split(path.sep).join('/');
1074
- if (e.ide === 'vscode' && rel.startsWith('.claude/commands/rihal/')) {
1075
- const baseName = path.basename(e.rel, path.extname(e.rel));
1076
- return !claudeCommandRels.has(baseName);
1077
- }
1078
- return true;
1079
- });
1080
- }
1122
+ // Note: pre-#723 we had a dual-layout workaround here that filtered
1123
+ // vscode subdir entries when claude+vscode were both selected. After
1124
+ // Waleed's unification (vscode now writes the same rihal-{name}.md root
1125
+ // form as claude), the seen-by-rel dedup above already covers itboth
1126
+ // IDEs emit identical `rel` values and only one wins. Layout drift will
1127
+ // resurface this filter; it's intentionally deleted, not commented out.
1081
1128
  return merged;
1082
1129
  }
1083
1130
 
@@ -1126,13 +1173,14 @@ function buildInstallPlan(ide = 'claude', target = process.cwd()) {
1126
1173
  }
1127
1174
 
1128
1175
  // Commands — IDE-specific
1129
- // Claude: output as .claude/commands/rihal-{name}.md (hyphen namespace → /rihal-name)
1130
- // Cursor/Gemini: keep original flat name inside their rihal/ subdirectory
1176
+ // Claude AND VSCode: output as .claude/commands/rihal-{name}.md (prefixed root).
1177
+ // Both target the same commands dir (#723 / Waleed unification) so multi-IDE
1178
+ // installs never duplicate. Cursor/Gemini keep the bare-name-in-rihal/-subdir form.
1131
1179
  for (const f of walkFiles(path.join(SOURCE_ROOT, 'commands'))) {
1132
1180
  const rel = path.relative(path.join(SOURCE_ROOT, 'commands'), f);
1133
1181
  const ext = ide === 'cursor' ? '.mdc' : '.md';
1134
1182
  const baseName = path.basename(f, '.md');
1135
- const outName = ide === 'claude'
1183
+ const outName = (ide === 'claude' || ide === 'vscode')
1136
1184
  ? `rihal-${baseName}${ext}`
1137
1185
  : baseName + ext;
1138
1186
  plan.push({ src: f, rel: path.join(relCommands, path.dirname(rel), outName), ide, cursor: ide === 'cursor' });
@@ -1730,6 +1778,15 @@ async function installInner(opts) {
1730
1778
  }
1731
1779
  }
1732
1780
 
1781
+ // #723 Waleed — migrate legacy vscode subdir layout BEFORE building the plan
1782
+ // so the plan never has to reason about both forms. Idempotent + safe.
1783
+ if (Array.isArray(opts.ides) && opts.ides.includes('vscode') || (opts.ide === 'vscode')) {
1784
+ const migrated = migrateVscodeCommandsLayout(opts.target);
1785
+ if (migrated.moved > 0) {
1786
+ console.log(` ↻ Migrated ${migrated.moved} legacy vscode-layout command(s) to .claude/commands/rihal-{name}.md`);
1787
+ }
1788
+ }
1789
+
1733
1790
  const fullPlan = buildInstallPlan(opts.ides, opts.target);
1734
1791
  const plan = filterPlanByModules(fullPlan, opts.modules);
1735
1792
  if (plan.length === 0) {
@@ -1795,6 +1852,10 @@ async function installInner(opts) {
1795
1852
  let copied = 0;
1796
1853
  let skipped = 0;
1797
1854
  let preserved = 0;
1855
+ // #667 — track files the user explicitly chose to update via the conflict
1856
+ // resolver. Without this, accepting "Take vN" for 10 files still printed
1857
+ // "0 files installed" because `copied` only counts pre-conflict writes.
1858
+ let updated = 0;
1798
1859
  const preservedFiles = [];
1799
1860
  const preservedDiffs = []; // { rel, insertions, deletions, patch } for #251
1800
1861
  const conflictedFiles = []; // { rel, src, destPath, existingContent, sourceContent } for #451 / #453
@@ -1867,6 +1928,10 @@ async function installInner(opts) {
1867
1928
  }
1868
1929
 
1869
1930
  spinner.success({ text: ok(`${copied} files installed`) });
1931
+ // #667 — placeholder; the real count is logged AFTER the conflict resolver
1932
+ // runs below. We re-emit a corrected summary line if the user updated files
1933
+ // via the resolver so they don't walk away thinking "0 files installed"
1934
+ // when they just accepted 10 vN updates.
1870
1935
 
1871
1936
  // Categorised conflict summary (#451) + interactive resolution offer (#453).
1872
1937
  // Replaces the per-file 'differs from package version' warning spam.
@@ -1906,6 +1971,7 @@ async function installInner(opts) {
1906
1971
  fs.writeFileSync(c.destPath, c.sourceContent, 'utf8');
1907
1972
  applied++;
1908
1973
  }
1974
+ updated += applied; // #667 — surface in final summary
1909
1975
  console.log(' ' + ok(`Applied v${readPackageVersion()} to ${applied} file${applied === 1 ? '' : 's'}.`));
1910
1976
  } else if (action === 'review') {
1911
1977
  let applied = 0, kept = 0;
@@ -1952,6 +2018,7 @@ async function installInner(opts) {
1952
2018
  kept++;
1953
2019
  }
1954
2020
  }
2021
+ updated += applied; // #667 — surface in final summary
1955
2022
  console.log(' ' + ok(`Review complete: ${applied} applied, ${kept} kept local.`));
1956
2023
  } else {
1957
2024
  console.log(' ' + dim(`${conflictedFiles.length} file${conflictedFiles.length === 1 ? '' : 's'} kept local. Re-run with --force-overwrite or 'rcode update' anytime.`));
@@ -1962,6 +2029,13 @@ async function installInner(opts) {
1962
2029
  console.log('');
1963
2030
  }
1964
2031
 
2032
+ // #667 — corrected post-resolver summary. Only re-emit when the conflict
2033
+ // resolver actually updated files; preserves the original line otherwise.
2034
+ if (updated > 0) {
2035
+ console.log(' ' + ok(`Total this run: ${copied} installed · ${updated} updated · ${preserved + skipped} unchanged.`));
2036
+ console.log('');
2037
+ }
2038
+
1965
2039
  // In global install mode (~/.claude/), skip per-project artifacts — those are
1966
2040
  // created by `rcode install` inside each project directory at project-init time.
1967
2041
  // Global install only ships the read-only tooling: commands, skills, workflows, bin.
@@ -2355,11 +2429,17 @@ async function installInner(opts) {
2355
2429
  const homeCommands = path.join(os.homedir(), '.claude/commands');
2356
2430
  const homeSkills = path.join(os.homedir(), '.claude/skills');
2357
2431
  if (agentCount === 0 && fs.existsSync(homeAgents)) {
2358
- const n = fs.readdirSync(homeAgents).filter(f => f.startsWith('rihal-') && f.endsWith('.md')).length;
2432
+ // #669 count both rihal-* and rcode-* prefixes; missing rcode-
2433
+ // branch produced "Agents: 0" alongside "Skills: 120".
2434
+ const n = fs.readdirSync(homeAgents)
2435
+ .filter(f => (f.startsWith('rihal-') || f.startsWith('rcode-')) && f.endsWith('.md'))
2436
+ .length;
2359
2437
  if (n > 0) { agentCount = n; agentsFromGlobal = true; }
2360
2438
  }
2361
2439
  if (commandCount === 0 && fs.existsSync(homeCommands)) {
2362
- const n = fs.readdirSync(homeCommands).filter(f => f.startsWith('rihal-') && f.endsWith('.md')).length;
2440
+ const n = fs.readdirSync(homeCommands)
2441
+ .filter(f => (f.startsWith('rihal-') || f.startsWith('rcode-')) && f.endsWith('.md'))
2442
+ .length;
2363
2443
  if (n > 0) { commandCount = n; commandsFromGlobal = true; }
2364
2444
  }
2365
2445
  if (skillsInstalled < 20 && fs.existsSync(homeSkills)) {
@@ -2398,6 +2478,15 @@ async function installInner(opts) {
2398
2478
  console.log(' /rihal-do # interactive command picker');
2399
2479
  console.log(' /rihal-council <q> # multi-agent strategic answer');
2400
2480
  console.log('');
2481
+ // #665 — when the install came in via npm -g (--global --no-prompt), the
2482
+ // interactive IDE/planning prompts were skipped. Tell the user how to
2483
+ // configure them per-project so they aren't stranded with defaults.
2484
+ if (opts.global || opts.noPrompt) {
2485
+ console.log(` ${dim('Configure interactively (one-time, per project):')}`);
2486
+ console.log(` ${dim('rcode install # pick IDE + planning policy for THIS project')}`);
2487
+ console.log(` ${dim('rcode config # adjust defaults later')}`);
2488
+ console.log('');
2489
+ }
2401
2490
  console.log(dim(' Refresh anytime:'));
2402
2491
  console.log(dim(' npx @hanzlaa/rcode@latest install # pull the latest rcode + brain'));
2403
2492
  console.log(dim(` /rihal-update v${version} # pin rcode to a specific version`));
@@ -2699,3 +2788,5 @@ module.exports.parseArgs = parseArgs;
2699
2788
  module.exports.buildInstallPlan = buildInstallPlan;
2700
2789
  module.exports.install = install;
2701
2790
  module.exports.SUPPORTED_IDES = SUPPORTED_IDES;
2791
+ module.exports.migrateVscodeCommandsLayout = migrateVscodeCommandsLayout;
2792
+ module.exports.getPathsForIde = getPathsForIde;