@hanzlaa/rcode 3.4.32 → 3.5.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/agent.js +3 -2
- 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 +318 -24
- package/package.json +8 -4
- package/rihal/agents/rihal-cross-platform-auditor.md +15 -0
- package/rihal/agents/rihal-dep-auditor.md +15 -0
- package/rihal/agents/rihal-docs-auditor.md +3 -145
- package/rihal/agents/rihal-i18n-auditor.md +16 -0
- package/rihal/agents/rihal-nyquist-auditor.md +4 -156
- package/rihal/agents/rihal-observability-auditor.md +16 -0
- package/rihal/agents/rihal-phase-researcher.md +1 -1
- package/rihal/agents/rihal-planner.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 +7 -3
- 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/skills/agents/majlis-council/SKILL.md +1 -1
- package/rihal/team.yaml +32 -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 +53 -6
- package/server/lib/api.js +7 -0
- package/server/lib/html/client.js +725 -13
- package/server/lib/html/css.js +2046 -466
- package/server/lib/html/shell.js +227 -134
- package/server/lib/scanner.js +33 -0
- package/server/orchestrator.js +438 -0
package/dist/rcode.js
CHANGED
|
@@ -15217,7 +15217,7 @@ var require_install = __commonJS({
|
|
|
15217
15217
|
var bold = (s) => pc.bold(s);
|
|
15218
15218
|
var PACKAGE_ROOT2 = path2.resolve(__dirname, "..");
|
|
15219
15219
|
var SOURCE_ROOT = path2.join(PACKAGE_ROOT2, "rihal");
|
|
15220
|
-
var SUPPORTED_IDES = Object.freeze(["claude", "cursor", "gemini", "vscode", "antigravity"]);
|
|
15220
|
+
var SUPPORTED_IDES = Object.freeze(["claude", "cursor", "gemini", "vscode", "antigravity", "windsurf"]);
|
|
15221
15221
|
var ConfigSchema = z.object({
|
|
15222
15222
|
user_name: z.string().min(1),
|
|
15223
15223
|
project_name: z.string().min(1),
|
|
@@ -15394,12 +15394,13 @@ var require_install = __commonJS({
|
|
|
15394
15394
|
console.log(lines.join("\n"));
|
|
15395
15395
|
}
|
|
15396
15396
|
function detectIdeSignals(target) {
|
|
15397
|
-
const signals = { claude: false, cursor: false, gemini: false, vscode: false, antigravity: false };
|
|
15397
|
+
const signals = { claude: false, cursor: false, gemini: false, vscode: false, antigravity: false, windsurf: false };
|
|
15398
15398
|
if (fs2.existsSync(path2.join(target, ".claude"))) signals.claude = true;
|
|
15399
15399
|
if (fs2.existsSync(path2.join(target, ".cursor"))) signals.cursor = true;
|
|
15400
15400
|
if (fs2.existsSync(path2.join(target, ".gemini"))) signals.gemini = true;
|
|
15401
15401
|
if (fs2.existsSync(path2.join(target, ".vscode"))) signals.vscode = true;
|
|
15402
15402
|
if (fs2.existsSync(path2.join(target, ".antigravity"))) signals.antigravity = true;
|
|
15403
|
+
if (fs2.existsSync(path2.join(target, ".windsurf"))) signals.windsurf = true;
|
|
15403
15404
|
const home = os.homedir();
|
|
15404
15405
|
if (fs2.existsSync(path2.join(home, ".claude"))) signals.claude = true;
|
|
15405
15406
|
if (fs2.existsSync(path2.join(home, ".cursor"))) signals.cursor = true;
|
|
@@ -15408,9 +15409,12 @@ var require_install = __commonJS({
|
|
|
15408
15409
|
if (fs2.existsSync(path2.join(home, ".vscode"))) signals.vscode = true;
|
|
15409
15410
|
if (fs2.existsSync(path2.join(home, ".config", "Code"))) signals.vscode = true;
|
|
15410
15411
|
if (fs2.existsSync(path2.join(home, ".antigravity"))) signals.antigravity = true;
|
|
15412
|
+
if (fs2.existsSync(path2.join(home, ".windsurf"))) signals.windsurf = true;
|
|
15413
|
+
if (fs2.existsSync(path2.join(home, ".codeium", "windsurf"))) signals.windsurf = true;
|
|
15411
15414
|
if (process.env.CURSOR_TRACE_ID || /cursor/i.test(process.env.TERM_PROGRAM || "")) signals.cursor = true;
|
|
15412
15415
|
if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT) signals.claude = true;
|
|
15413
15416
|
if (process.env.VSCODE_PID || /vscode/i.test(process.env.TERM_PROGRAM || "")) signals.vscode = true;
|
|
15417
|
+
if (/windsurf/i.test(process.env.TERM_PROGRAM || "")) signals.windsurf = true;
|
|
15414
15418
|
return signals;
|
|
15415
15419
|
}
|
|
15416
15420
|
async function resolveIde(opts) {
|
|
@@ -15530,7 +15534,7 @@ Installs (IDE-specific):
|
|
|
15530
15534
|
case "vscode":
|
|
15531
15535
|
return {
|
|
15532
15536
|
agentsDir: path2.join(target, ".claude", "agents"),
|
|
15533
|
-
commandsDir: path2.join(target, ".claude", "commands"
|
|
15537
|
+
commandsDir: path2.join(target, ".claude", "commands"),
|
|
15534
15538
|
workflowsDir: path2.join(target, ".rihal", "workflows"),
|
|
15535
15539
|
referencesDir: path2.join(target, ".rihal", "references"),
|
|
15536
15540
|
binDir: path2.join(target, ".rihal", "bin"),
|
|
@@ -15544,8 +15548,16 @@ Installs (IDE-specific):
|
|
|
15544
15548
|
referencesDir: path2.join(target, ".rihal", "references"),
|
|
15545
15549
|
binDir: path2.join(target, ".rihal", "bin")
|
|
15546
15550
|
};
|
|
15551
|
+
case "windsurf":
|
|
15552
|
+
return {
|
|
15553
|
+
agentsDir: path2.join(target, ".windsurf", "rules", "rihal", "agents"),
|
|
15554
|
+
commandsDir: path2.join(target, ".windsurf", "rules", "rihal", "commands"),
|
|
15555
|
+
workflowsDir: path2.join(target, ".rihal", "workflows"),
|
|
15556
|
+
referencesDir: path2.join(target, ".rihal", "references"),
|
|
15557
|
+
binDir: path2.join(target, ".rihal", "bin")
|
|
15558
|
+
};
|
|
15547
15559
|
default:
|
|
15548
|
-
throw new Error(`Unknown IDE: ${ide}. Supported:
|
|
15560
|
+
throw new Error(`Unknown IDE: ${ide}. Supported: ${SUPPORTED_IDES.join(", ")}`);
|
|
15549
15561
|
}
|
|
15550
15562
|
}
|
|
15551
15563
|
function walkFiles(dir, extraIgnore = []) {
|
|
@@ -15944,6 +15956,34 @@ ${BLOCK}`, { mode: 493 });
|
|
|
15944
15956
|
}
|
|
15945
15957
|
return { frontmatter: fm, body };
|
|
15946
15958
|
}
|
|
15959
|
+
function migrateVscodeCommandsLayout(target) {
|
|
15960
|
+
const legacyDir = path2.join(target, ".claude", "commands", "rihal");
|
|
15961
|
+
const newRoot = path2.join(target, ".claude", "commands");
|
|
15962
|
+
if (!fs2.existsSync(legacyDir) || !fs2.statSync(legacyDir).isDirectory()) {
|
|
15963
|
+
return { moved: 0, removed_dir: false };
|
|
15964
|
+
}
|
|
15965
|
+
let moved = 0;
|
|
15966
|
+
for (const entry of fs2.readdirSync(legacyDir)) {
|
|
15967
|
+
const src = path2.join(legacyDir, entry);
|
|
15968
|
+
if (!fs2.statSync(src).isFile() || !entry.endsWith(".md")) continue;
|
|
15969
|
+
const baseName = path2.basename(entry, ".md");
|
|
15970
|
+
const targetName = baseName.startsWith("rihal-") ? entry : `rihal-${entry}`;
|
|
15971
|
+
const dst = path2.join(newRoot, targetName);
|
|
15972
|
+
if (fs2.existsSync(dst)) {
|
|
15973
|
+
fs2.unlinkSync(src);
|
|
15974
|
+
continue;
|
|
15975
|
+
}
|
|
15976
|
+
fs2.renameSync(src, dst);
|
|
15977
|
+
moved++;
|
|
15978
|
+
}
|
|
15979
|
+
let removedDir = false;
|
|
15980
|
+
try {
|
|
15981
|
+
fs2.rmdirSync(legacyDir);
|
|
15982
|
+
removedDir = true;
|
|
15983
|
+
} catch (_) {
|
|
15984
|
+
}
|
|
15985
|
+
return { moved, removed_dir: removedDir };
|
|
15986
|
+
}
|
|
15947
15987
|
function buildInstallPlan(ide = "claude", target = process.cwd()) {
|
|
15948
15988
|
if (Array.isArray(ide)) {
|
|
15949
15989
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -15957,19 +15997,6 @@ ${BLOCK}`, { mode: 493 });
|
|
|
15957
15997
|
}
|
|
15958
15998
|
}
|
|
15959
15999
|
}
|
|
15960
|
-
if (ide.includes("claude") && ide.includes("vscode")) {
|
|
15961
|
-
const claudeCommandRels = new Set(
|
|
15962
|
-
merged.filter((e) => e.ide === "claude" && e.rel.split(path2.sep).join("/").startsWith(".claude/commands/")).map((e) => path2.basename(e.rel, ".md").replace(/^rihal-/, ""))
|
|
15963
|
-
);
|
|
15964
|
-
return merged.filter((e) => {
|
|
15965
|
-
const rel = e.rel.split(path2.sep).join("/");
|
|
15966
|
-
if (e.ide === "vscode" && rel.startsWith(".claude/commands/rihal/")) {
|
|
15967
|
-
const baseName = path2.basename(e.rel, path2.extname(e.rel));
|
|
15968
|
-
return !claudeCommandRels.has(baseName);
|
|
15969
|
-
}
|
|
15970
|
-
return true;
|
|
15971
|
-
});
|
|
15972
|
-
}
|
|
15973
16000
|
return merged;
|
|
15974
16001
|
}
|
|
15975
16002
|
const plan = [];
|
|
@@ -16007,7 +16034,7 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16007
16034
|
const rel = path2.relative(path2.join(SOURCE_ROOT, "commands"), f);
|
|
16008
16035
|
const ext = ide === "cursor" ? ".mdc" : ".md";
|
|
16009
16036
|
const baseName = path2.basename(f, ".md");
|
|
16010
|
-
const outName = ide === "claude" ? `rihal-${baseName}${ext}` : baseName + ext;
|
|
16037
|
+
const outName = ide === "claude" || ide === "vscode" ? `rihal-${baseName}${ext}` : baseName + ext;
|
|
16011
16038
|
plan.push({ src: f, rel: path2.join(relCommands, path2.dirname(rel), outName), ide, cursor: ide === "cursor" });
|
|
16012
16039
|
}
|
|
16013
16040
|
const agentRulesDir = path2.join(target, ".rihal", "agents-rules");
|
|
@@ -16476,6 +16503,12 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16476
16503
|
return 1;
|
|
16477
16504
|
}
|
|
16478
16505
|
}
|
|
16506
|
+
if (Array.isArray(opts.ides) && opts.ides.includes("vscode") || opts.ide === "vscode") {
|
|
16507
|
+
const migrated = migrateVscodeCommandsLayout(opts.target);
|
|
16508
|
+
if (migrated.moved > 0) {
|
|
16509
|
+
console.log(` \u21BB Migrated ${migrated.moved} legacy vscode-layout command(s) to .claude/commands/rihal-{name}.md`);
|
|
16510
|
+
}
|
|
16511
|
+
}
|
|
16479
16512
|
const fullPlan = buildInstallPlan(opts.ides, opts.target);
|
|
16480
16513
|
const plan = filterPlanByModules(fullPlan, opts.modules);
|
|
16481
16514
|
if (plan.length === 0) {
|
|
@@ -16521,6 +16554,7 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16521
16554
|
let copied = 0;
|
|
16522
16555
|
let skipped = 0;
|
|
16523
16556
|
let preserved = 0;
|
|
16557
|
+
let updated = 0;
|
|
16524
16558
|
const preservedFiles = [];
|
|
16525
16559
|
const preservedDiffs = [];
|
|
16526
16560
|
const conflictedFiles = [];
|
|
@@ -16618,6 +16652,7 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16618
16652
|
fs2.writeFileSync(c.destPath, c.sourceContent, "utf8");
|
|
16619
16653
|
applied++;
|
|
16620
16654
|
}
|
|
16655
|
+
updated += applied;
|
|
16621
16656
|
console.log(" " + ok(`Applied v${readPackageVersion()} to ${applied} file${applied === 1 ? "" : "s"}.`));
|
|
16622
16657
|
} else if (action === "review") {
|
|
16623
16658
|
let applied = 0, kept = 0;
|
|
@@ -16664,6 +16699,7 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16664
16699
|
kept++;
|
|
16665
16700
|
}
|
|
16666
16701
|
}
|
|
16702
|
+
updated += applied;
|
|
16667
16703
|
console.log(" " + ok(`Review complete: ${applied} applied, ${kept} kept local.`));
|
|
16668
16704
|
} else {
|
|
16669
16705
|
console.log(" " + dim(`${conflictedFiles.length} file${conflictedFiles.length === 1 ? "" : "s"} kept local. Re-run with --force-overwrite or 'rcode update' anytime.`));
|
|
@@ -16673,6 +16709,10 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16673
16709
|
}
|
|
16674
16710
|
console.log("");
|
|
16675
16711
|
}
|
|
16712
|
+
if (updated > 0) {
|
|
16713
|
+
console.log(" " + ok(`Total this run: ${copied} installed \xB7 ${updated} updated \xB7 ${preserved + skipped} unchanged.`));
|
|
16714
|
+
console.log("");
|
|
16715
|
+
}
|
|
16676
16716
|
if (opts.global) {
|
|
16677
16717
|
const configDir2 = path2.join(opts.target, ".rihal", "_config");
|
|
16678
16718
|
ensureDir(configDir2);
|
|
@@ -16766,8 +16806,8 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16766
16806
|
const match = before.match(re);
|
|
16767
16807
|
const currentInFile = match ? match[1] === "true" : null;
|
|
16768
16808
|
if (match && currentInFile !== desired) {
|
|
16769
|
-
const
|
|
16770
|
-
writeFileAtomic(configPath,
|
|
16809
|
+
const updated2 = before.replace(re, `commit_planning: ${desired}`);
|
|
16810
|
+
writeFileAtomic(configPath, updated2);
|
|
16771
16811
|
console.log(" " + dim(`Updated commit_planning in config.yaml (${currentInFile} \u2192 ${desired}) \u2014 closes #685.`));
|
|
16772
16812
|
} else if (!match) {
|
|
16773
16813
|
const appended = before.replace(/\n*$/, "") + `
|
|
@@ -16951,14 +16991,14 @@ commit_planning: ${desired}
|
|
|
16951
16991
|
const homeCommands = path2.join(os.homedir(), ".claude/commands");
|
|
16952
16992
|
const homeSkills = path2.join(os.homedir(), ".claude/skills");
|
|
16953
16993
|
if (agentCount === 0 && fs2.existsSync(homeAgents)) {
|
|
16954
|
-
const n = fs2.readdirSync(homeAgents).filter((f) => f.startsWith("rihal-") && f.endsWith(".md")).length;
|
|
16994
|
+
const n = fs2.readdirSync(homeAgents).filter((f) => (f.startsWith("rihal-") || f.startsWith("rcode-")) && f.endsWith(".md")).length;
|
|
16955
16995
|
if (n > 0) {
|
|
16956
16996
|
agentCount = n;
|
|
16957
16997
|
agentsFromGlobal = true;
|
|
16958
16998
|
}
|
|
16959
16999
|
}
|
|
16960
17000
|
if (commandCount === 0 && fs2.existsSync(homeCommands)) {
|
|
16961
|
-
const n = fs2.readdirSync(homeCommands).filter((f) => f.startsWith("rihal-") && f.endsWith(".md")).length;
|
|
17001
|
+
const n = fs2.readdirSync(homeCommands).filter((f) => (f.startsWith("rihal-") || f.startsWith("rcode-")) && f.endsWith(".md")).length;
|
|
16962
17002
|
if (n > 0) {
|
|
16963
17003
|
commandCount = n;
|
|
16964
17004
|
commandsFromGlobal = true;
|
|
@@ -16999,6 +17039,12 @@ commit_planning: ${desired}
|
|
|
16999
17039
|
console.log(" /rihal-do # interactive command picker");
|
|
17000
17040
|
console.log(" /rihal-council <q> # multi-agent strategic answer");
|
|
17001
17041
|
console.log("");
|
|
17042
|
+
if (opts.global || opts.noPrompt) {
|
|
17043
|
+
console.log(` ${dim("Configure interactively (one-time, per project):")}`);
|
|
17044
|
+
console.log(` ${dim("rcode install # pick IDE + planning policy for THIS project")}`);
|
|
17045
|
+
console.log(` ${dim("rcode config # adjust defaults later")}`);
|
|
17046
|
+
console.log("");
|
|
17047
|
+
}
|
|
17002
17048
|
console.log(dim(" Refresh anytime:"));
|
|
17003
17049
|
console.log(dim(" npx @hanzlaa/rcode@latest install # pull the latest rcode + brain"));
|
|
17004
17050
|
console.log(dim(` /rihal-update v${version} # pin rcode to a specific version`));
|
|
@@ -17252,6 +17298,8 @@ commit_planning: ${desired}
|
|
|
17252
17298
|
module2.exports.buildInstallPlan = buildInstallPlan;
|
|
17253
17299
|
module2.exports.install = install;
|
|
17254
17300
|
module2.exports.SUPPORTED_IDES = SUPPORTED_IDES;
|
|
17301
|
+
module2.exports.migrateVscodeCommandsLayout = migrateVscodeCommandsLayout;
|
|
17302
|
+
module2.exports.getPathsForIde = getPathsForIde;
|
|
17255
17303
|
}
|
|
17256
17304
|
});
|
|
17257
17305
|
|
|
@@ -18904,7 +18952,8 @@ var require_agent = __commonJS({
|
|
|
18904
18952
|
console.error(`Available: ${available}`);
|
|
18905
18953
|
process.exit(1);
|
|
18906
18954
|
}
|
|
18907
|
-
const
|
|
18955
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
18956
|
+
const claudeCheck = spawnSync(whichCmd, ["claude"], { encoding: "utf8" });
|
|
18908
18957
|
if (claudeCheck.status !== 0) {
|
|
18909
18958
|
console.error("Error: claude binary not found. Install Claude Code: https://claude.ai/code");
|
|
18910
18959
|
process.exit(1);
|
|
@@ -19143,6 +19192,193 @@ var require_memory_bank = __commonJS({
|
|
|
19143
19192
|
}
|
|
19144
19193
|
});
|
|
19145
19194
|
|
|
19195
|
+
// cli/lib/schemas.cjs
|
|
19196
|
+
var require_schemas = __commonJS({
|
|
19197
|
+
"cli/lib/schemas.cjs"(exports2, module2) {
|
|
19198
|
+
var { z } = require_zod();
|
|
19199
|
+
function parseFrontmatter(text) {
|
|
19200
|
+
if (typeof text !== "string" || !text.startsWith("---\n")) {
|
|
19201
|
+
return { frontmatter: {}, body: text || "" };
|
|
19202
|
+
}
|
|
19203
|
+
const end = text.indexOf("\n---\n", 4);
|
|
19204
|
+
if (end === -1) return { frontmatter: {}, body: text };
|
|
19205
|
+
const block = text.slice(4, end);
|
|
19206
|
+
const body = text.slice(end + 5);
|
|
19207
|
+
const fm = {};
|
|
19208
|
+
const lines = block.split("\n");
|
|
19209
|
+
let i = 0;
|
|
19210
|
+
while (i < lines.length) {
|
|
19211
|
+
const raw = lines[i];
|
|
19212
|
+
if (!raw.trim() || raw.trim().startsWith("#")) {
|
|
19213
|
+
i++;
|
|
19214
|
+
continue;
|
|
19215
|
+
}
|
|
19216
|
+
const m = raw.match(/^([A-Za-z0-9_-]+):(.*)$/);
|
|
19217
|
+
if (!m) {
|
|
19218
|
+
i++;
|
|
19219
|
+
continue;
|
|
19220
|
+
}
|
|
19221
|
+
const key = m[1].trim();
|
|
19222
|
+
let inline = m[2].trim();
|
|
19223
|
+
inline = inline.replace(/\s+#.*$/, "").trim();
|
|
19224
|
+
if (inline === ">" || inline === "|" || inline === ">-" || inline === "|-") {
|
|
19225
|
+
const collected = [];
|
|
19226
|
+
i++;
|
|
19227
|
+
while (i < lines.length) {
|
|
19228
|
+
const cont = lines[i];
|
|
19229
|
+
if (cont.trim() === "") {
|
|
19230
|
+
collected.push("");
|
|
19231
|
+
i++;
|
|
19232
|
+
continue;
|
|
19233
|
+
}
|
|
19234
|
+
if (/^\s/.test(cont)) {
|
|
19235
|
+
collected.push(cont.trim());
|
|
19236
|
+
i++;
|
|
19237
|
+
continue;
|
|
19238
|
+
}
|
|
19239
|
+
break;
|
|
19240
|
+
}
|
|
19241
|
+
fm[key] = collected.join(" ").replace(/\s+/g, " ").trim();
|
|
19242
|
+
continue;
|
|
19243
|
+
}
|
|
19244
|
+
if (inline === "") {
|
|
19245
|
+
const items = [];
|
|
19246
|
+
let j = i + 1;
|
|
19247
|
+
while (j < lines.length) {
|
|
19248
|
+
const cont = lines[j];
|
|
19249
|
+
if (cont.trim() === "" || cont.trim().startsWith("#")) {
|
|
19250
|
+
j++;
|
|
19251
|
+
continue;
|
|
19252
|
+
}
|
|
19253
|
+
const li = cont.match(/^\s+-\s+(.*)$/);
|
|
19254
|
+
if (!li) break;
|
|
19255
|
+
let v = li[1].trim();
|
|
19256
|
+
if (v.startsWith('"') && v.endsWith('"')) v = v.slice(1, -1);
|
|
19257
|
+
if (v.startsWith("'") && v.endsWith("'")) v = v.slice(1, -1);
|
|
19258
|
+
items.push(v);
|
|
19259
|
+
j++;
|
|
19260
|
+
}
|
|
19261
|
+
if (items.length) {
|
|
19262
|
+
fm[key] = items;
|
|
19263
|
+
i = j;
|
|
19264
|
+
continue;
|
|
19265
|
+
}
|
|
19266
|
+
fm[key] = "";
|
|
19267
|
+
i++;
|
|
19268
|
+
continue;
|
|
19269
|
+
}
|
|
19270
|
+
if (inline.startsWith('"') && inline.endsWith('"')) inline = inline.slice(1, -1);
|
|
19271
|
+
if (inline.startsWith("'") && inline.endsWith("'")) inline = inline.slice(1, -1);
|
|
19272
|
+
fm[key] = inline;
|
|
19273
|
+
i++;
|
|
19274
|
+
}
|
|
19275
|
+
return { frontmatter: fm, body };
|
|
19276
|
+
}
|
|
19277
|
+
function countTriggerPhrases(fm) {
|
|
19278
|
+
const desc = typeof fm.description === "string" ? fm.description : "";
|
|
19279
|
+
const quoted = desc.match(/"[^"]+"/g) || [];
|
|
19280
|
+
if (quoted.length > 0) return quoted.length;
|
|
19281
|
+
if (Array.isArray(fm.triggers)) {
|
|
19282
|
+
return fm.triggers.filter((t) => typeof t === "string" && t.trim()).length;
|
|
19283
|
+
}
|
|
19284
|
+
if (typeof fm.triggers === "string" && fm.triggers.trim()) {
|
|
19285
|
+
return fm.triggers.split(",").map((s) => s.trim()).filter(Boolean).length;
|
|
19286
|
+
}
|
|
19287
|
+
return 0;
|
|
19288
|
+
}
|
|
19289
|
+
var skillFrontmatterSchema = z.object({
|
|
19290
|
+
name: z.string().min(1, "name is required"),
|
|
19291
|
+
description: z.string().min(1, "description is required")
|
|
19292
|
+
});
|
|
19293
|
+
var NEGATIVE_BOUNDARY_RE = /not for|do not|does not|don't|never\b|audit-only|negative/i;
|
|
19294
|
+
function validateSkillFrontmatter(obj, body = "") {
|
|
19295
|
+
const errors = [];
|
|
19296
|
+
const warnings = [];
|
|
19297
|
+
const parsed = skillFrontmatterSchema.safeParse(obj || {});
|
|
19298
|
+
if (!parsed.success) {
|
|
19299
|
+
for (const issue of parsed.error.issues) {
|
|
19300
|
+
const field = issue.path.length ? issue.path.join(".") : "(root)";
|
|
19301
|
+
errors.push(`${field}: ${issue.message}`);
|
|
19302
|
+
}
|
|
19303
|
+
}
|
|
19304
|
+
if (obj && typeof obj === "object") {
|
|
19305
|
+
const count = countTriggerPhrases(obj);
|
|
19306
|
+
if (count < 5) {
|
|
19307
|
+
errors.push(`too few trigger phrases: found ${count}, need at least 5`);
|
|
19308
|
+
} else if (count > 12) {
|
|
19309
|
+
warnings.push(`many trigger phrases: found ${count}, recommended max is 12`);
|
|
19310
|
+
}
|
|
19311
|
+
const desc = typeof obj.description === "string" ? obj.description : "";
|
|
19312
|
+
const bodyText = typeof body === "string" ? body : "";
|
|
19313
|
+
const hasBoundary = NEGATIVE_BOUNDARY_RE.test(desc) || /##[^\n]*\bnot\b/i.test(bodyText) || /\bdo not (use|include)\b/i.test(bodyText) || /\bdon't touch\b/i.test(bodyText);
|
|
19314
|
+
if (!hasBoundary) {
|
|
19315
|
+
errors.push(
|
|
19316
|
+
'missing negative-boundary clause (e.g. "Do NOT use for: ..." in the description or a "## Do NOT use" section)'
|
|
19317
|
+
);
|
|
19318
|
+
}
|
|
19319
|
+
}
|
|
19320
|
+
return { ok: errors.length === 0, errors, warnings };
|
|
19321
|
+
}
|
|
19322
|
+
var agentFrontmatterSchema = z.object({
|
|
19323
|
+
name: z.string().min(1, "name is required").refine((v) => v.startsWith("rihal-"), 'name must start with the "rihal-" prefix'),
|
|
19324
|
+
description: z.string().min(1, "description is required"),
|
|
19325
|
+
color: z.string().min(1, "color is required")
|
|
19326
|
+
});
|
|
19327
|
+
function validateAgentFrontmatter(obj) {
|
|
19328
|
+
const errors = [];
|
|
19329
|
+
const parsed = agentFrontmatterSchema.safeParse(obj || {});
|
|
19330
|
+
if (!parsed.success) {
|
|
19331
|
+
for (const issue of parsed.error.issues) {
|
|
19332
|
+
const field = issue.path.length ? issue.path.join(".") : "(root)";
|
|
19333
|
+
errors.push(`${field}: ${issue.message}`);
|
|
19334
|
+
}
|
|
19335
|
+
}
|
|
19336
|
+
if (obj && typeof obj === "object") {
|
|
19337
|
+
const tools = obj.tools;
|
|
19338
|
+
const hasTools = Array.isArray(tools) ? tools.length > 0 : typeof tools === "string" && tools.trim().length > 0;
|
|
19339
|
+
if (!hasTools) {
|
|
19340
|
+
errors.push("tools is required (comma-separated string or array)");
|
|
19341
|
+
}
|
|
19342
|
+
} else {
|
|
19343
|
+
errors.push("tools is required (comma-separated string or array)");
|
|
19344
|
+
}
|
|
19345
|
+
return { ok: errors.length === 0, errors };
|
|
19346
|
+
}
|
|
19347
|
+
var stateSchema = z.object({
|
|
19348
|
+
version: z.union([z.string(), z.number()]),
|
|
19349
|
+
project: z.string().min(1),
|
|
19350
|
+
phases: z.array(z.any()),
|
|
19351
|
+
schema_version: z.number(),
|
|
19352
|
+
current_phase: z.union([z.string(), z.number()]).optional(),
|
|
19353
|
+
current_plan: z.union([z.string(), z.number()]).optional(),
|
|
19354
|
+
current_sprint: z.union([z.string(), z.number()]).nullable().optional(),
|
|
19355
|
+
velocity_history: z.array(z.any()).optional(),
|
|
19356
|
+
milestones: z.array(z.any()).optional()
|
|
19357
|
+
}).passthrough();
|
|
19358
|
+
function validateState(obj) {
|
|
19359
|
+
const errors = [];
|
|
19360
|
+
const parsed = stateSchema.safeParse(obj || {});
|
|
19361
|
+
if (!parsed.success) {
|
|
19362
|
+
for (const issue of parsed.error.issues) {
|
|
19363
|
+
const where = issue.path.length ? issue.path.join(".") : "(root)";
|
|
19364
|
+
errors.push(`${where}: ${issue.message}`);
|
|
19365
|
+
}
|
|
19366
|
+
}
|
|
19367
|
+
return { ok: errors.length === 0, errors };
|
|
19368
|
+
}
|
|
19369
|
+
module2.exports = {
|
|
19370
|
+
parseFrontmatter,
|
|
19371
|
+
countTriggerPhrases,
|
|
19372
|
+
skillFrontmatterSchema,
|
|
19373
|
+
agentFrontmatterSchema,
|
|
19374
|
+
stateSchema,
|
|
19375
|
+
validateSkillFrontmatter,
|
|
19376
|
+
validateAgentFrontmatter,
|
|
19377
|
+
validateState
|
|
19378
|
+
};
|
|
19379
|
+
}
|
|
19380
|
+
});
|
|
19381
|
+
|
|
19146
19382
|
// cli/doctor.js
|
|
19147
19383
|
var require_doctor = __commonJS({
|
|
19148
19384
|
"cli/doctor.js"(exports2, module2) {
|
|
@@ -19151,6 +19387,11 @@ var require_doctor = __commonJS({
|
|
|
19151
19387
|
var { spawnSync } = require("child_process");
|
|
19152
19388
|
var { verifyInstall, formatReport } = require_manifest();
|
|
19153
19389
|
var { checkStaleness } = require_memory_bank();
|
|
19390
|
+
var {
|
|
19391
|
+
parseFrontmatter,
|
|
19392
|
+
validateSkillFrontmatter,
|
|
19393
|
+
validateAgentFrontmatter
|
|
19394
|
+
} = require_schemas();
|
|
19154
19395
|
function findSkillFiles(dir) {
|
|
19155
19396
|
const results = [];
|
|
19156
19397
|
if (!fs2.existsSync(dir)) return results;
|
|
@@ -19164,6 +19405,10 @@ var require_doctor = __commonJS({
|
|
|
19164
19405
|
}
|
|
19165
19406
|
return results;
|
|
19166
19407
|
}
|
|
19408
|
+
function findAgentFiles(dir) {
|
|
19409
|
+
if (!fs2.existsSync(dir)) return [];
|
|
19410
|
+
return fs2.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path2.join(dir, e.name));
|
|
19411
|
+
}
|
|
19167
19412
|
function checkCompliance(filePath) {
|
|
19168
19413
|
const content = fs2.readFileSync(filePath, "utf8");
|
|
19169
19414
|
const missing = [];
|
|
@@ -19346,6 +19591,52 @@ var require_doctor = __commonJS({
|
|
|
19346
19591
|
}
|
|
19347
19592
|
return failing;
|
|
19348
19593
|
}
|
|
19594
|
+
function runSchemaValidation(packageRoot) {
|
|
19595
|
+
const skillDirs = [
|
|
19596
|
+
path2.join(packageRoot, "rihal/skills/agents"),
|
|
19597
|
+
path2.join(packageRoot, "rihal/skills/actions")
|
|
19598
|
+
];
|
|
19599
|
+
let totalSkills = 0;
|
|
19600
|
+
let totalAgents = 0;
|
|
19601
|
+
let failing = 0;
|
|
19602
|
+
let warned = 0;
|
|
19603
|
+
for (const dir of skillDirs) {
|
|
19604
|
+
for (const file of findSkillFiles(dir)) {
|
|
19605
|
+
totalSkills++;
|
|
19606
|
+
const { frontmatter, body } = parseFrontmatter(fs2.readFileSync(file, "utf8"));
|
|
19607
|
+
const result = validateSkillFrontmatter(frontmatter, body);
|
|
19608
|
+
const rel = path2.relative(packageRoot, file);
|
|
19609
|
+
if (!result.ok) {
|
|
19610
|
+
failing++;
|
|
19611
|
+
console.log(` \u2717 ${rel}`);
|
|
19612
|
+
for (const err of result.errors) console.log(` ${err}`);
|
|
19613
|
+
}
|
|
19614
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
19615
|
+
warned++;
|
|
19616
|
+
for (const w of result.warnings) console.log(` \u26A0 ${rel}: ${w}`);
|
|
19617
|
+
}
|
|
19618
|
+
}
|
|
19619
|
+
}
|
|
19620
|
+
for (const file of findAgentFiles(path2.join(packageRoot, "rihal/agents"))) {
|
|
19621
|
+
totalAgents++;
|
|
19622
|
+
const { frontmatter } = parseFrontmatter(fs2.readFileSync(file, "utf8"));
|
|
19623
|
+
const result = validateAgentFrontmatter(frontmatter);
|
|
19624
|
+
if (!result.ok) {
|
|
19625
|
+
failing++;
|
|
19626
|
+
const rel = path2.relative(packageRoot, file);
|
|
19627
|
+
console.log(` \u2717 ${rel}`);
|
|
19628
|
+
for (const err of result.errors) console.log(` ${err}`);
|
|
19629
|
+
}
|
|
19630
|
+
}
|
|
19631
|
+
if (failing === 0) {
|
|
19632
|
+
console.log(
|
|
19633
|
+
` \u2713 ${totalSkills} skill + ${totalAgents} agent frontmatter blocks pass schema validation` + (warned > 0 ? ` (${warned} with advisory warnings)` : "")
|
|
19634
|
+
);
|
|
19635
|
+
} else {
|
|
19636
|
+
console.log(` \u2717 ${failing} artifact(s) failed schema validation`);
|
|
19637
|
+
}
|
|
19638
|
+
return failing;
|
|
19639
|
+
}
|
|
19349
19640
|
function checkDuplicateInstalls() {
|
|
19350
19641
|
const os = require("os");
|
|
19351
19642
|
const home = os.homedir();
|
|
@@ -19420,7 +19711,10 @@ Duplicate installations:`);
|
|
|
19420
19711
|
console.log(`
|
|
19421
19712
|
Package compliance:`);
|
|
19422
19713
|
const complianceFailures = runCompliance(packageRoot);
|
|
19423
|
-
|
|
19714
|
+
console.log(`
|
|
19715
|
+
Artifact schema validation:`);
|
|
19716
|
+
const schemaFailures = runSchemaValidation(packageRoot);
|
|
19717
|
+
const totalFailures = preflightFailures + complianceFailures + duplicateFailures + schemaFailures;
|
|
19424
19718
|
console.log();
|
|
19425
19719
|
if (totalFailures === 0) {
|
|
19426
19720
|
console.log(`\u2705 All checks passed.`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "rcode — the
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"description": "rcode — the AI team that never forgets. Persistent memory, specialist agents, and slash commands for AI IDEs. Works in Claude Code, Cursor, Gemini, VS Code, and Antigravity.",
|
|
5
5
|
"main": "cli/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"rcode": "dist/rcode.js",
|
|
@@ -40,13 +40,17 @@
|
|
|
40
40
|
"arabic",
|
|
41
41
|
"ai-methodology",
|
|
42
42
|
"team-simulation",
|
|
43
|
-
"orchestration"
|
|
43
|
+
"orchestration",
|
|
44
|
+
"memory-bank",
|
|
45
|
+
"cursor",
|
|
46
|
+
"planning",
|
|
47
|
+
"subagents"
|
|
44
48
|
],
|
|
45
49
|
"author": {
|
|
46
50
|
"name": "Hanzla Habib",
|
|
47
51
|
"url": "https://github.com/hanzlahabib"
|
|
48
52
|
},
|
|
49
|
-
"license": "
|
|
53
|
+
"license": "MIT",
|
|
50
54
|
"repository": {
|
|
51
55
|
"type": "git",
|
|
52
56
|
"url": "https://github.com/hanzlahabib/rihal-code.git"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rihal-cross-platform-auditor
|
|
3
|
+
description: |
|
|
4
|
+
Cross-platform portability auditor. Detects bash-isms, macOS-only flags
|
|
5
|
+
(BSD sed/awk), hardcoded absolute Unix paths in Node code, Windows path
|
|
6
|
+
separators, and CRLF line endings. Audit-only — never modifies scripts.
|
|
7
|
+
Activates: "cross-platform audit", "bash-isms", "macOS only",
|
|
8
|
+
"Windows compatibility", "portability check".
|
|
9
|
+
Do NOT use for: fixing scripts, frontend RTL, or translation.
|
|
10
|
+
tools: Read, Bash, Grep, Glob
|
|
11
|
+
color: yellow
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
@.rihal/references/response-style.md
|
|
15
|
+
@rihal/skills/agents/rihal-cross-platform-auditor/SKILL.md
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rihal-dep-auditor
|
|
3
|
+
description: |
|
|
4
|
+
Dependency health auditor — scans for outdated packages, CVEs, unused
|
|
5
|
+
dependencies, loose version pins, and missing lock files. Audit-only:
|
|
6
|
+
never modifies package.json or runs installs.
|
|
7
|
+
Activates: "audit dependencies", "dep health", "CVE scan", "check packages",
|
|
8
|
+
"outdated deps", "loose pins", "lock file".
|
|
9
|
+
Do NOT use for: installing packages, updating deps, or security penetration testing.
|
|
10
|
+
tools: Read, Bash, Grep, Glob
|
|
11
|
+
color: yellow
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
@.rihal/references/response-style.md
|
|
15
|
+
@rihal/skills/agents/rihal-dep-auditor/SKILL.md
|