@hanzlaa/rcode 3.4.13 → 3.4.15
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/install.js +65 -10
- package/dist/rcode.js +48 -6
- package/package.json +1 -1
package/cli/install.js
CHANGED
|
@@ -1724,6 +1724,61 @@ async function install(opts) {
|
|
|
1724
1724
|
return 0;
|
|
1725
1725
|
}
|
|
1726
1726
|
|
|
1727
|
+
// Duplicate-prevention: if rihal commands already exist globally in ~/.claude/commands/,
|
|
1728
|
+
// skip writing agents/commands to the project's .claude/ directory. Without this,
|
|
1729
|
+
// running `npx rcode install` in the home dir AND then in a project creates two sets
|
|
1730
|
+
// of identical files — Claude Code shows both as duplicate slash commands.
|
|
1731
|
+
const globalClaudeCommands = path.join(os.homedir(), '.claude', 'commands');
|
|
1732
|
+
const projectClaudeCommands = path.join(opts.target, '.claude', 'commands');
|
|
1733
|
+
const isProjectInstall = opts.target !== os.homedir();
|
|
1734
|
+
if (isProjectInstall && !opts.force && !opts.forceOverwrite) {
|
|
1735
|
+
try {
|
|
1736
|
+
const globalHasRihal = fs.existsSync(globalClaudeCommands) &&
|
|
1737
|
+
fs.readdirSync(globalClaudeCommands).some(f => f.startsWith('rihal-') && f.endsWith('.md'));
|
|
1738
|
+
const projectHasRihal = fs.existsSync(projectClaudeCommands) &&
|
|
1739
|
+
fs.readdirSync(projectClaudeCommands).some(f => f.startsWith('rihal-') && f.endsWith('.md'));
|
|
1740
|
+
if (globalHasRihal && !projectHasRihal) {
|
|
1741
|
+
// Global commands exist, project has none yet — filter them out of the plan
|
|
1742
|
+
// so we don't create duplicates. Project gets .rihal/ state only.
|
|
1743
|
+
const before = plan.length;
|
|
1744
|
+
const filtered = plan.filter(e => {
|
|
1745
|
+
const rel = e.rel.split(path.sep).join('/');
|
|
1746
|
+
return !rel.startsWith('.claude/commands/') && !rel.startsWith('.claude/agents/');
|
|
1747
|
+
});
|
|
1748
|
+
if (filtered.length < before) {
|
|
1749
|
+
plan.length = 0;
|
|
1750
|
+
filtered.forEach(e => plan.push(e));
|
|
1751
|
+
console.log(' ' + dim('Global rihal commands detected in ~/.claude/ — skipping project-level agent/command install to avoid duplicates.'));
|
|
1752
|
+
console.log(' ' + dim('Use --force-overwrite to install locally anyway.'));
|
|
1753
|
+
}
|
|
1754
|
+
} else if (globalHasRihal && projectHasRihal) {
|
|
1755
|
+
// Both exist — project commands are duplicates. Remove project-level ones.
|
|
1756
|
+
try {
|
|
1757
|
+
const projectCommandFiles = fs.readdirSync(projectClaudeCommands)
|
|
1758
|
+
.filter(f => f.startsWith('rihal-') && f.endsWith('.md'));
|
|
1759
|
+
for (const f of projectCommandFiles) {
|
|
1760
|
+
fs.unlinkSync(path.join(projectClaudeCommands, f));
|
|
1761
|
+
}
|
|
1762
|
+
const projectAgentsDir = path.join(opts.target, '.claude', 'agents');
|
|
1763
|
+
if (fs.existsSync(projectAgentsDir)) {
|
|
1764
|
+
const agentFiles = fs.readdirSync(projectAgentsDir)
|
|
1765
|
+
.filter(f => f.startsWith('rihal-') && f.endsWith('.md'));
|
|
1766
|
+
for (const f of agentFiles) {
|
|
1767
|
+
fs.unlinkSync(path.join(projectAgentsDir, f));
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
console.log(' ' + dim('Removed duplicate project-level rihal commands (global ones in ~/.claude/ take precedence).'));
|
|
1771
|
+
} catch { /* non-fatal */ }
|
|
1772
|
+
const filtered = plan.filter(e => {
|
|
1773
|
+
const rel = e.rel.split(path.sep).join('/');
|
|
1774
|
+
return !rel.startsWith('.claude/commands/') && !rel.startsWith('.claude/agents/');
|
|
1775
|
+
});
|
|
1776
|
+
plan.length = 0;
|
|
1777
|
+
filtered.forEach(e => plan.push(e));
|
|
1778
|
+
}
|
|
1779
|
+
} catch { /* non-fatal — skip detection on permission errors */ }
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1727
1782
|
// Write .rihal/_config/manifest.yaml + agent-manifest.csv + files-manifest.csv
|
|
1728
1783
|
const configDir = path.join(opts.target, '.rihal', '_config');
|
|
1729
1784
|
ensureDir(configDir);
|
|
@@ -1920,10 +1975,11 @@ async function install(opts) {
|
|
|
1920
1975
|
console.log('');
|
|
1921
1976
|
}
|
|
1922
1977
|
|
|
1923
|
-
// Count installed agents + commands dynamically (#190).
|
|
1924
|
-
// IDE
|
|
1925
|
-
//
|
|
1926
|
-
|
|
1978
|
+
// Count installed agents + commands dynamically (#190).
|
|
1979
|
+
// Prefer the 'claude' IDE paths for counting when claude is in the selected list —
|
|
1980
|
+
// that's what actually matters for Claude Code slash command availability.
|
|
1981
|
+
// Fall back to the first selected IDE only when claude isn't included.
|
|
1982
|
+
const primaryIde = opts.ides.includes('claude') ? 'claude' : opts.ides[0];
|
|
1927
1983
|
const idePaths = getPathsForIde(primaryIde, opts.target);
|
|
1928
1984
|
const agentsDir = idePaths.agentsDir;
|
|
1929
1985
|
const commandsDir = idePaths.commandsDir;
|
|
@@ -1933,12 +1989,11 @@ async function install(opts) {
|
|
|
1933
1989
|
agentCount = fs.readdirSync(agentsDir).filter(f => (f.startsWith('rihal-') || f.startsWith('rcode-')) && (f.endsWith('.md') || f.endsWith('.mdc'))).length;
|
|
1934
1990
|
}
|
|
1935
1991
|
if (fs.existsSync(commandsDir)) {
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
fs.rmSync(legacyColonDir, { recursive: true, force: true });
|
|
1992
|
+
// claude IDE names commands rihal-*.md; other IDEs use plain {name}.md inside a rihal/ subdir
|
|
1993
|
+
const commandFilter = primaryIde === 'claude'
|
|
1994
|
+
? f => f.startsWith('rihal-') && (f.endsWith('.md') || f.endsWith('.mdc'))
|
|
1995
|
+
: f => f.endsWith('.md') || f.endsWith('.mdc');
|
|
1996
|
+
commandCount = fs.readdirSync(commandsDir).filter(commandFilter).length;
|
|
1942
1997
|
}
|
|
1943
1998
|
} catch {}
|
|
1944
1999
|
|
package/dist/rcode.js
CHANGED
|
@@ -16269,6 +16269,51 @@ ${BLOCK}`);
|
|
|
16269
16269
|
console.log(` ${dim(`${skillsInstalled2} skills installed globally`)}`);
|
|
16270
16270
|
return 0;
|
|
16271
16271
|
}
|
|
16272
|
+
const globalClaudeCommands = path2.join(os.homedir(), ".claude", "commands");
|
|
16273
|
+
const projectClaudeCommands = path2.join(opts.target, ".claude", "commands");
|
|
16274
|
+
const isProjectInstall = opts.target !== os.homedir();
|
|
16275
|
+
if (isProjectInstall && !opts.force && !opts.forceOverwrite) {
|
|
16276
|
+
try {
|
|
16277
|
+
const globalHasRihal = fs2.existsSync(globalClaudeCommands) && fs2.readdirSync(globalClaudeCommands).some((f) => f.startsWith("rihal-") && f.endsWith(".md"));
|
|
16278
|
+
const projectHasRihal = fs2.existsSync(projectClaudeCommands) && fs2.readdirSync(projectClaudeCommands).some((f) => f.startsWith("rihal-") && f.endsWith(".md"));
|
|
16279
|
+
if (globalHasRihal && !projectHasRihal) {
|
|
16280
|
+
const before = plan.length;
|
|
16281
|
+
const filtered = plan.filter((e) => {
|
|
16282
|
+
const rel = e.rel.split(path2.sep).join("/");
|
|
16283
|
+
return !rel.startsWith(".claude/commands/") && !rel.startsWith(".claude/agents/");
|
|
16284
|
+
});
|
|
16285
|
+
if (filtered.length < before) {
|
|
16286
|
+
plan.length = 0;
|
|
16287
|
+
filtered.forEach((e) => plan.push(e));
|
|
16288
|
+
console.log(" " + dim("Global rihal commands detected in ~/.claude/ \u2014 skipping project-level agent/command install to avoid duplicates."));
|
|
16289
|
+
console.log(" " + dim("Use --force-overwrite to install locally anyway."));
|
|
16290
|
+
}
|
|
16291
|
+
} else if (globalHasRihal && projectHasRihal) {
|
|
16292
|
+
try {
|
|
16293
|
+
const projectCommandFiles = fs2.readdirSync(projectClaudeCommands).filter((f) => f.startsWith("rihal-") && f.endsWith(".md"));
|
|
16294
|
+
for (const f of projectCommandFiles) {
|
|
16295
|
+
fs2.unlinkSync(path2.join(projectClaudeCommands, f));
|
|
16296
|
+
}
|
|
16297
|
+
const projectAgentsDir = path2.join(opts.target, ".claude", "agents");
|
|
16298
|
+
if (fs2.existsSync(projectAgentsDir)) {
|
|
16299
|
+
const agentFiles = fs2.readdirSync(projectAgentsDir).filter((f) => f.startsWith("rihal-") && f.endsWith(".md"));
|
|
16300
|
+
for (const f of agentFiles) {
|
|
16301
|
+
fs2.unlinkSync(path2.join(projectAgentsDir, f));
|
|
16302
|
+
}
|
|
16303
|
+
}
|
|
16304
|
+
console.log(" " + dim("Removed duplicate project-level rihal commands (global ones in ~/.claude/ take precedence)."));
|
|
16305
|
+
} catch {
|
|
16306
|
+
}
|
|
16307
|
+
const filtered = plan.filter((e) => {
|
|
16308
|
+
const rel = e.rel.split(path2.sep).join("/");
|
|
16309
|
+
return !rel.startsWith(".claude/commands/") && !rel.startsWith(".claude/agents/");
|
|
16310
|
+
});
|
|
16311
|
+
plan.length = 0;
|
|
16312
|
+
filtered.forEach((e) => plan.push(e));
|
|
16313
|
+
}
|
|
16314
|
+
} catch {
|
|
16315
|
+
}
|
|
16316
|
+
}
|
|
16272
16317
|
const configDir = path2.join(opts.target, ".rihal", "_config");
|
|
16273
16318
|
ensureDir(configDir);
|
|
16274
16319
|
fs2.writeFileSync(path2.join(configDir, "manifest.yaml"), generateInstallManifest(opts));
|
|
@@ -16415,7 +16460,7 @@ ${BLOCK}`);
|
|
|
16415
16460
|
console.log(dim(" To overwrite: re-run with --force-overwrite | To see full diffs: --show-diff"));
|
|
16416
16461
|
console.log("");
|
|
16417
16462
|
}
|
|
16418
|
-
const primaryIde = opts.ides[0];
|
|
16463
|
+
const primaryIde = opts.ides.includes("claude") ? "claude" : opts.ides[0];
|
|
16419
16464
|
const idePaths = getPathsForIde(primaryIde, opts.target);
|
|
16420
16465
|
const agentsDir = idePaths.agentsDir;
|
|
16421
16466
|
const commandsDir = idePaths.commandsDir;
|
|
@@ -16425,11 +16470,8 @@ ${BLOCK}`);
|
|
|
16425
16470
|
agentCount = fs2.readdirSync(agentsDir).filter((f) => (f.startsWith("rihal-") || f.startsWith("rcode-")) && (f.endsWith(".md") || f.endsWith(".mdc"))).length;
|
|
16426
16471
|
}
|
|
16427
16472
|
if (fs2.existsSync(commandsDir)) {
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
const legacyColonDir = path2.join(opts.target, ".claude", "commands", "rihal");
|
|
16431
|
-
if (primaryIde === "claude" && fs2.existsSync(legacyColonDir)) {
|
|
16432
|
-
fs2.rmSync(legacyColonDir, { recursive: true, force: true });
|
|
16473
|
+
const commandFilter = primaryIde === "claude" ? (f) => f.startsWith("rihal-") && (f.endsWith(".md") || f.endsWith(".mdc")) : (f) => f.endsWith(".md") || f.endsWith(".mdc");
|
|
16474
|
+
commandCount = fs2.readdirSync(commandsDir).filter(commandFilter).length;
|
|
16433
16475
|
}
|
|
16434
16476
|
} catch {
|
|
16435
16477
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.15",
|
|
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": {
|