@hanzlaa/rcode 2.7.0 → 2.7.1

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 CHANGED
@@ -1025,12 +1025,18 @@ function sweepStaleInstalledFiles(target, newPlan) {
1025
1025
  const newRelsSet = new Set(newPlan.map(e => e.rel.split(path.sep).join('/')));
1026
1026
  // Safety — never sweep these, even if they somehow landed in the manifest.
1027
1027
  const neverSweep = /^(\.rihal\/config\.yaml|\.rihal\/state\.json|\.rihal\/state\.json\.lock|\.planning\/|\.rihal\/brain\/sources\.yaml)/;
1028
+ // #382 — local overrides: files matching <name>.local.md are user-managed.
1029
+ // The installer never touches them: not in copy, not in sweep, not even on
1030
+ // --force-overwrite. This gives users a stable path to customize agent
1031
+ // voice / examples / project-specific rules without losing them on update.
1032
+ const isLocalOverride = (rel) => /\.local\.(md|mdc|json|yaml|yml|toml|js|ts)$/.test(rel);
1028
1033
 
1029
1034
  let removed = 0;
1030
1035
  const emptyCandidateDirs = new Set();
1031
1036
  for (const rel of oldRels) {
1032
1037
  if (newRelsSet.has(rel)) continue;
1033
1038
  if (neverSweep.test(rel)) continue;
1039
+ if (isLocalOverride(rel)) continue; // #382 — never sweep user-owned overrides
1034
1040
  const full = path.join(target, rel);
1035
1041
  try {
1036
1042
  if (fs.existsSync(full)) {
@@ -1603,6 +1609,10 @@ async function install(opts) {
1603
1609
  console.log(dim(' npx @hanzlaa/rcode@latest install # pull the latest rcode + brain'));
1604
1610
  console.log(dim(` /rihal:update v${version} # pin rcode to a specific version`));
1605
1611
  console.log('');
1612
+ console.log(dim(' Customize without losing changes on update:'));
1613
+ console.log(dim(' Create <name>.local.md siblings (e.g. .claude/agents/rihal-waleed.local.md)'));
1614
+ console.log(dim(' *.local.md files are NEVER touched by install / --force-overwrite / uninstall.'));
1615
+ console.log('');
1606
1616
  console.log(' ' + warn('If your IDE is already open, reload the window to refresh skills/commands.'));
1607
1617
  console.log(dim(' Claude Code / VS Code / Cursor: Cmd+Shift+P → Reload Window'));
1608
1618
  console.log('');
package/cli/uninstall.js CHANGED
@@ -57,14 +57,26 @@ function parseArgs(args) {
57
57
  return opts;
58
58
  }
59
59
 
60
+ /**
61
+ * #382 — Local overrides: files matching <name>.local.md (or .local.mdc /
62
+ * .local.json / etc.) are user-managed. The uninstaller never removes them
63
+ * — they survive both regular uninstall AND --purge. Users can customize
64
+ * an agent voice / skill / command by creating a .local.md sibling, knowing
65
+ * it'll persist across updates and uninstalls.
66
+ */
67
+ function isLocalOverride(name) {
68
+ return /\.local\.(md|mdc|json|yaml|yml|toml|js|ts)$/.test(name);
69
+ }
70
+
60
71
  /**
61
72
  * Walk a directory and remove all files/subdirs whose name matches a predicate.
62
- * Returns the number of entries removed.
73
+ * Returns the number of entries removed. Always skips local overrides (#382).
63
74
  */
64
75
  function removeMatching(dir, predicate) {
65
76
  if (!fs.existsSync(dir)) return 0;
66
77
  let count = 0;
67
78
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
79
+ if (isLocalOverride(entry.name)) continue; // #382 — never remove user overrides
68
80
  if (!predicate(entry.name)) continue;
69
81
  const full = path.join(dir, entry.name);
70
82
  fs.rmSync(full, { recursive: true, force: true });
package/dist/rcode.js CHANGED
@@ -16530,11 +16530,13 @@ Say "plan a sprint" or run \`/rihal:sprint-planning\` to break Phase 01 into sto
16530
16530
  }
16531
16531
  const newRelsSet = new Set(newPlan.map((e) => e.rel.split(path2.sep).join("/")));
16532
16532
  const neverSweep = /^(\.rihal\/config\.yaml|\.rihal\/state\.json|\.rihal\/state\.json\.lock|\.planning\/|\.rihal\/brain\/sources\.yaml)/;
16533
+ const isLocalOverride = (rel) => /\.local\.(md|mdc|json|yaml|yml|toml|js|ts)$/.test(rel);
16533
16534
  let removed = 0;
16534
16535
  const emptyCandidateDirs = /* @__PURE__ */ new Set();
16535
16536
  for (const rel of oldRels) {
16536
16537
  if (newRelsSet.has(rel)) continue;
16537
16538
  if (neverSweep.test(rel)) continue;
16539
+ if (isLocalOverride(rel)) continue;
16538
16540
  const full = path2.join(target, rel);
16539
16541
  try {
16540
16542
  if (fs2.existsSync(full)) {
@@ -17005,6 +17007,10 @@ Say "plan a sprint" or run \`/rihal:sprint-planning\` to break Phase 01 into sto
17005
17007
  console.log(dim(" npx @hanzlaa/rcode@latest install # pull the latest rcode + brain"));
17006
17008
  console.log(dim(` /rihal:update v${version} # pin rcode to a specific version`));
17007
17009
  console.log("");
17010
+ console.log(dim(" Customize without losing changes on update:"));
17011
+ console.log(dim(" Create <name>.local.md siblings (e.g. .claude/agents/rihal-waleed.local.md)"));
17012
+ console.log(dim(" *.local.md files are NEVER touched by install / --force-overwrite / uninstall."));
17013
+ console.log("");
17008
17014
  console.log(" " + warn("If your IDE is already open, reload the window to refresh skills/commands."));
17009
17015
  console.log(dim(" Claude Code / VS Code / Cursor: Cmd+Shift+P \u2192 Reload Window"));
17010
17016
  console.log("");
@@ -17953,10 +17959,14 @@ var require_uninstall = __commonJS({
17953
17959
  }
17954
17960
  return opts;
17955
17961
  }
17962
+ function isLocalOverride(name) {
17963
+ return /\.local\.(md|mdc|json|yaml|yml|toml|js|ts)$/.test(name);
17964
+ }
17956
17965
  function removeMatching(dir, predicate) {
17957
17966
  if (!fs2.existsSync(dir)) return 0;
17958
17967
  let count = 0;
17959
17968
  for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
17969
+ if (isLocalOverride(entry.name)) continue;
17960
17970
  if (!predicate(entry.name)) continue;
17961
17971
  const full = path2.join(dir, entry.name);
17962
17972
  fs2.rmSync(full, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzlaa/rcode",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Rihal Code (rcode) — installable context-brain for Rihalians. 43 agents, 99 slash commands, 56 skills, pullable Rihal standards. Unified install for Claude Code, Cursor, and Gemini.",
5
5
  "main": "cli/index.js",
6
6
  "bin": {