@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.
- package/AGENTS.md +6 -6
- package/CONTRIBUTING.md +2 -0
- package/LICENSE +21 -0
- package/README.md +66 -403
- package/cli/doctor.js +87 -1
- package/cli/install.js +122 -31
- package/cli/lib/schemas.cjs +318 -0
- package/cli/postinstall.js +19 -3
- package/dist/rcode.js +316 -23
- package/package.json +14 -4
- package/rihal/agents/rihal-cross-platform-auditor.md +1 -1
- package/rihal/agents/rihal-dep-auditor.md +1 -1
- package/rihal/agents/rihal-docs-auditor.md +3 -145
- package/rihal/agents/rihal-i18n-auditor.md +1 -1
- package/rihal/agents/rihal-nyquist-auditor.md +4 -156
- package/rihal/agents/rihal-observability-auditor.md +1 -1
- package/rihal/bin/rihal-hooks.cjs +394 -4
- package/rihal/bin/rihal-tools.cjs +891 -24
- package/rihal/commands/create-prd.md +18 -0
- package/rihal/commands/execute-milestone.md +18 -0
- package/rihal/commands/plan-milestone.md +18 -0
- package/rihal/commands/scaffold-milestone.md +18 -0
- package/rihal/commands/scaffold-skill.md +18 -0
- package/rihal/references/REFERENCES_INDEX.md +49 -7
- package/rihal/references/agent-contracts.md +10 -0
- package/rihal/references/design-tokens.md +98 -0
- package/rihal/references/docs-auditor-playbook.md +148 -0
- package/rihal/references/git-preflight.md +117 -0
- package/rihal/references/iterative-retrieval.md +85 -0
- package/rihal/references/nyquist-auditor-playbook.md +157 -0
- package/rihal/references/workstream-flag.md +2 -2
- package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +9 -0
- package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +9 -0
- package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +4 -0
- package/rihal/skills/actions/4-implementation/rihal-code-review/steps/step-02-review.md +2 -2
- package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +4 -0
- package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +4 -0
- package/rihal/skills/agents/haitham-frontend/SKILL.md +2 -0
- package/rihal/templates/settings-hooks.json +39 -0
- package/rihal/workflows/check-todos.md +4 -0
- package/rihal/workflows/code-review-fix.md +4 -3
- package/rihal/workflows/code-review.md +1 -1
- package/rihal/workflows/debug.md +1 -1
- package/rihal/workflows/dev-story.md +4 -0
- package/rihal/workflows/diff.md +2 -2
- package/rihal/workflows/do.md +16 -8
- package/rihal/workflows/docs-update.md +2 -2
- package/rihal/workflows/enable-hooks.md +6 -1
- package/rihal/workflows/execute-milestone.md +139 -0
- package/rihal/workflows/execute-regression-gates.md +1 -1
- package/rihal/workflows/execute-sprint.md +54 -2
- package/rihal/workflows/execute-verify-phase-goal.md +31 -4
- package/rihal/workflows/execute-waves.md +33 -5
- package/rihal/workflows/execute.md +40 -6
- package/rihal/workflows/help.md +1 -1
- package/rihal/workflows/import.md +1 -1
- package/rihal/workflows/lens-audit.md +39 -23
- package/rihal/workflows/list-workspaces.md +1 -1
- package/rihal/workflows/map-codebase.md +4 -4
- package/rihal/workflows/new-milestone.md +18 -1
- package/rihal/workflows/new-project-research.md +53 -1
- package/rihal/workflows/new-workspace.md +1 -1
- package/rihal/workflows/plan-milestone.md +105 -0
- package/rihal/workflows/plan-research-validation.md +1 -1
- package/rihal/workflows/plan-spawn-planner.md +1 -1
- package/rihal/workflows/plan.md +31 -3
- package/rihal/workflows/plant-seed.md +6 -0
- package/rihal/workflows/quick.md +11 -5
- package/rihal/workflows/research-phase.md +24 -0
- package/rihal/workflows/scaffold-milestone.md +60 -0
- package/rihal/workflows/scaffold-skill.md +137 -0
- package/rihal/workflows/scan.md +1 -1
- package/rihal/workflows/session-report.md +43 -3
- package/rihal/workflows/verify-work.md +3 -3
- package/server/dashboard.js +154 -5
- package/server/lib/html/client/agents-data.js +27 -0
- package/server/lib/html/client/app.js +15 -0
- package/server/lib/html/client/components/App.js +211 -0
- package/server/lib/html/client/components/OrchPanel.js +293 -0
- package/server/lib/html/client/components/Sidebar.js +73 -0
- package/server/lib/html/client/components/Topbar.js +53 -0
- package/server/lib/html/client/components/XtermPanel.js +220 -0
- package/server/lib/html/client/components/shared.js +330 -0
- package/server/lib/html/client/icons-client.js +85 -0
- package/server/lib/html/client/orchestrator.js +279 -0
- package/server/lib/html/client/preact.js +34 -0
- package/server/lib/html/client/store.js +91 -0
- package/server/lib/html/client/util.js +186 -0
- package/server/lib/html/client/views/AgentsView.js +83 -0
- package/server/lib/html/client/views/DecisionsView.js +102 -0
- package/server/lib/html/client/views/FilesView.js +223 -0
- package/server/lib/html/client/views/KanbanView.js +236 -0
- package/server/lib/html/client/views/MemoryView.js +157 -0
- package/server/lib/html/client/views/MilestonesView.js +136 -0
- package/server/lib/html/client/views/OrchestrationView.js +167 -0
- package/server/lib/html/client/views/OverviewView.js +221 -0
- package/server/lib/html/client/views/PhasesView.js +184 -0
- package/server/lib/html/client/views/RoadmapView.js +238 -0
- package/server/lib/html/client/views/SprintsView.js +178 -0
- package/server/lib/html/client/views/TasksView.js +148 -0
- package/server/lib/html/client.js +42 -1064
- package/server/lib/html/css.js +2266 -466
- package/server/lib/html/icons.js +68 -0
- package/server/lib/html/shell.js +16 -210
- package/server/lib/scanner.js +109 -0
- 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
|
-
|
|
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
|
-
//
|
|
500
|
-
//
|
|
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'
|
|
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:
|
|
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
|
-
//
|
|
1062
|
-
//
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1065
|
-
//
|
|
1066
|
-
|
|
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 it — both
|
|
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 (
|
|
1130
|
-
//
|
|
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
|
-
|
|
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)
|
|
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;
|