@hanzlaa/rcode 3.4.22 → 3.4.23

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 (2) hide show
  1. package/dist/rcode.js +123 -27
  2. package/package.json +2 -1
package/dist/rcode.js CHANGED
@@ -15188,10 +15188,23 @@ var require_install = __commonJS({
15188
15188
  async function resolveCommitPlanning(opts) {
15189
15189
  if (opts.commitPlanning !== null) return opts.commitPlanning;
15190
15190
  if (opts.noPrompt || opts.global) return false;
15191
- if (opts.yes || !process.stdin.isTTY) return true;
15191
+ let existingValue = null;
15192
+ try {
15193
+ const cfgPath = path2.join(opts.target, ".rihal", "config.yaml");
15194
+ if (fs2.existsSync(cfgPath)) {
15195
+ const cfg = fs2.readFileSync(cfgPath, "utf8");
15196
+ const m = cfg.match(/^commit_planning:\s*(true|false)\s*$/m);
15197
+ if (m) existingValue = m[1] === "true";
15198
+ }
15199
+ } catch {
15200
+ }
15201
+ if (opts.yes || !process.stdin.isTTY) {
15202
+ return existingValue !== null ? existingValue : true;
15203
+ }
15204
+ const initialValue = existingValue === false ? "gitignore" : "commit";
15192
15205
  const choice = await clack.select({
15193
- message: "\u{1F4CB} .planning/ holds PRDs, roadmaps, sprints, SUMMARY files. How should they be tracked?",
15194
- initialValue: "commit",
15206
+ message: existingValue !== null ? "\u{1F4CB} .planning/ tracking \u2014 current setting preserved unless you change it." : "\u{1F4CB} .planning/ holds PRDs, roadmaps, sprints, SUMMARY files. How should they be tracked?",
15207
+ initialValue,
15195
15208
  options: [
15196
15209
  { value: "commit", label: "Commit", hint: "collaborators see the same plans (recommended)" },
15197
15210
  { value: "gitignore", label: "Gitignore", hint: "planning stays local (good for sensitive PRDs)" }
@@ -15312,9 +15325,13 @@ Installs (IDE-specific):
15312
15325
  fs2.mkdirSync(planningDir, { recursive: true });
15313
15326
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15314
15327
  const name = projectName || path2.basename(target);
15328
+ const STUB_BANNER = `<!-- INSTALL STUB \u2014 overwritten by /rihal-new-project. Delete this file or run
15329
+ /rihal-new-project before committing. See https://github.com/hanzlahabib/rihal-code/issues/670 -->
15330
+
15331
+ `;
15315
15332
  fs2.writeFileSync(
15316
15333
  projectPath,
15317
- `# ${name}
15334
+ STUB_BANNER + `# ${name}
15318
15335
 
15319
15336
  **One-line:** Describe what this project is in one sentence.
15320
15337
 
@@ -15331,7 +15348,7 @@ What this project delivers and who it serves.
15331
15348
  );
15332
15349
  fs2.writeFileSync(
15333
15350
  roadmapPath,
15334
- `# ${name} \u2014 Roadmap
15351
+ STUB_BANNER + `# ${name} \u2014 Roadmap
15335
15352
 
15336
15353
  **Milestone: M1 \u2014 Initial Delivery** (v1.0)
15337
15354
  Started: ${today} \xB7 Current
@@ -15355,7 +15372,7 @@ Ideas and future phases go here.
15355
15372
  );
15356
15373
  fs2.writeFileSync(
15357
15374
  statePath,
15358
- `# ${name} \u2014 State
15375
+ STUB_BANNER + `# ${name} \u2014 State
15359
15376
 
15360
15377
  **Last updated:** ${today}
15361
15378
  **Milestone:** M1 \u2014 Initial Delivery
@@ -15374,7 +15391,7 @@ _None._
15374
15391
 
15375
15392
  ## Next Action
15376
15393
 
15377
- Say "plan a sprint" or run \`/rihal-sprint-planning\` to break Phase 01 into stories.
15394
+ Run \`/rihal-new-project <description>\` to bootstrap, or \`/rihal-sprint-planning\` once a real phase exists.
15378
15395
  `
15379
15396
  );
15380
15397
  const rihalStateJson = path2.join(target, ".rihal", "state.json");
@@ -15382,16 +15399,15 @@ Say "plan a sprint" or run \`/rihal-sprint-planning\` to break Phase 01 into sto
15382
15399
  const now = (/* @__PURE__ */ new Date()).toISOString();
15383
15400
  const state = {
15384
15401
  version: "1",
15385
- project: name,
15402
+ project: null,
15403
+ _seeded_stub: true,
15386
15404
  created: now,
15387
15405
  updated: now,
15388
- current_phase: "01",
15406
+ current_phase: null,
15389
15407
  current_plan: 0,
15390
15408
  current_sprint: null,
15391
- milestone: "M1 \u2014 Initial Delivery",
15392
- phases: [
15393
- { id: "01", name: "Setup & Scaffolding", status: "planned" }
15394
- ],
15409
+ milestone: null,
15410
+ phases: [],
15395
15411
  executions: [],
15396
15412
  decisions: [],
15397
15413
  blockers: [],
@@ -15584,20 +15600,31 @@ ${BLOCK}`);
15584
15600
  }
15585
15601
  return copied;
15586
15602
  }
15587
- function installSkills(packageRoot, target) {
15603
+ function installSkills(packageRoot, target, options = {}) {
15588
15604
  const skillsSource = path2.join(packageRoot, "rihal/skills");
15589
15605
  const skillsDest = path2.join(target, ".claude/skills");
15590
15606
  const internalDest = path2.join(target, ".rihal/skills");
15591
- if (!fs2.existsSync(skillsSource)) return 0;
15607
+ if (!fs2.existsSync(skillsSource)) return { count: 0, skippedGlobal: 0 };
15592
15608
  fs2.mkdirSync(skillsDest, { recursive: true });
15593
15609
  fs2.mkdirSync(internalDest, { recursive: true });
15610
+ const globalSkillsDir = path2.join(os.homedir(), ".claude", "skills");
15611
+ const globalRihalSkills = options.skipGlobalDuplicates && fs2.existsSync(globalSkillsDir) ? new Set(fs2.readdirSync(globalSkillsDir).filter((n) => n.startsWith("rihal-"))) : /* @__PURE__ */ new Set();
15594
15612
  let count = 0;
15613
+ let skippedGlobal = 0;
15595
15614
  function isInternalSkill(skillDir) {
15596
15615
  const skillMd = path2.join(skillDir, "SKILL.md");
15597
15616
  if (!fs2.existsSync(skillMd)) return false;
15598
15617
  const text = fs2.readFileSync(skillMd, "utf8");
15599
15618
  return /^internal:\s*true\s*$/m.test(text);
15600
15619
  }
15620
+ function hasLocalOverride(destDir) {
15621
+ if (!fs2.existsSync(destDir)) return false;
15622
+ try {
15623
+ return fs2.readdirSync(destDir).some((f) => f.endsWith(".local.md"));
15624
+ } catch {
15625
+ return false;
15626
+ }
15627
+ }
15601
15628
  function walkForSkills(dir) {
15602
15629
  if (!fs2.existsSync(dir)) return;
15603
15630
  for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
@@ -15606,7 +15633,18 @@ ${BLOCK}`);
15606
15633
  const hasSkillMd = fs2.existsSync(path2.join(src, "SKILL.md"));
15607
15634
  if (hasSkillMd) {
15608
15635
  const destName = entry.name.startsWith("rihal-") ? entry.name : `rihal-${entry.name}`;
15609
- const dest = isInternalSkill(src) ? path2.join(internalDest, destName) : path2.join(skillsDest, destName);
15636
+ const internal = isInternalSkill(src);
15637
+ const dest = internal ? path2.join(internalDest, destName) : path2.join(skillsDest, destName);
15638
+ if (!internal && globalRihalSkills.has(destName) && !hasLocalOverride(dest)) {
15639
+ if (fs2.existsSync(dest)) {
15640
+ try {
15641
+ fs2.rmSync(dest, { recursive: true, force: true });
15642
+ } catch {
15643
+ }
15644
+ }
15645
+ skippedGlobal++;
15646
+ continue;
15647
+ }
15610
15648
  copyDirRecursive(src, dest);
15611
15649
  count++;
15612
15650
  } else {
@@ -15617,7 +15655,7 @@ ${BLOCK}`);
15617
15655
  for (const bucket of ["agents", "actions", "core"]) {
15618
15656
  walkForSkills(path2.join(skillsSource, bucket));
15619
15657
  }
15620
- return count;
15658
+ return { count, skippedGlobal };
15621
15659
  }
15622
15660
  function parseFrontmatter(text) {
15623
15661
  if (!text.startsWith("---\n")) return { frontmatter: {}, body: text };
@@ -16001,6 +16039,15 @@ ${BLOCK}`);
16001
16039
  printHelp2();
16002
16040
  return 0;
16003
16041
  }
16042
+ if (opts.reset && !opts.force) {
16043
+ console.log("");
16044
+ console.log(" " + warn("--reset has no effect without --force."));
16045
+ console.log(" " + dim(" --reset wipes config.yaml and state.json. To prevent accidental data loss,"));
16046
+ console.log(" " + dim(" it must be paired with --force. Re-run as:"));
16047
+ console.log(" " + dim(" rcode install --reset --force"));
16048
+ console.log("");
16049
+ return 2;
16050
+ }
16004
16051
  const pkgVersion = readPackageVersion();
16005
16052
  const isInteractive = process.stdin.isTTY && !opts.yes;
16006
16053
  if (isInteractive) printInstallHeader(pkgVersion);
@@ -16273,7 +16320,8 @@ ${BLOCK}`);
16273
16320
  const configDir2 = path2.join(opts.target, ".rihal", "_config");
16274
16321
  ensureDir(configDir2);
16275
16322
  fs2.writeFileSync(path2.join(configDir2, "manifest.yaml"), generateInstallManifest(opts));
16276
- let skillsInstalled2 = installSkills(PACKAGE_ROOT2, opts.target);
16323
+ const skillsResult2 = installSkills(PACKAGE_ROOT2, opts.target);
16324
+ let skillsInstalled2 = skillsResult2.count;
16277
16325
  try {
16278
16326
  const { main: generateCommandSkills } = require(path2.join(PACKAGE_ROOT2, "cli", "generate-command-skills.cjs"));
16279
16327
  const stubsDir = path2.join(opts.target, ".claude", "skills");
@@ -16353,6 +16401,25 @@ ${BLOCK}`);
16353
16401
  }
16354
16402
  if (!fs2.existsSync(configPath)) {
16355
16403
  fs2.writeFileSync(configPath, generateConfigYaml(opts));
16404
+ } else {
16405
+ try {
16406
+ const before = fs2.readFileSync(configPath, "utf8");
16407
+ const desired = opts.commitPlanning !== false;
16408
+ const re = /^commit_planning:\s*(true|false)\s*$/m;
16409
+ const match = before.match(re);
16410
+ const currentInFile = match ? match[1] === "true" : null;
16411
+ if (match && currentInFile !== desired) {
16412
+ const updated = before.replace(re, `commit_planning: ${desired}`);
16413
+ fs2.writeFileSync(configPath, updated);
16414
+ console.log(" " + dim(`Updated commit_planning in config.yaml (${currentInFile} \u2192 ${desired}) \u2014 closes #685.`));
16415
+ } else if (!match) {
16416
+ const appended = before.replace(/\n*$/, "") + `
16417
+ commit_planning: ${desired}
16418
+ `;
16419
+ fs2.writeFileSync(configPath, appended);
16420
+ }
16421
+ } catch {
16422
+ }
16356
16423
  }
16357
16424
  try {
16358
16425
  const configText = fs2.readFileSync(configPath, "utf8");
@@ -16392,15 +16459,26 @@ ${BLOCK}`);
16392
16459
  path2.join(configDir, "files-manifest.csv"),
16393
16460
  generateFilesManifest(plan, opts.target, { mergeExistingManifest: !opts.force })
16394
16461
  );
16395
- let skillsInstalled = installSkills(PACKAGE_ROOT2, opts.target);
16462
+ const skillsResult = installSkills(PACKAGE_ROOT2, opts.target, {
16463
+ skipGlobalDuplicates: isProjectInstall
16464
+ });
16465
+ let skillsInstalled = skillsResult.count;
16466
+ if (skillsResult.skippedGlobal > 0) {
16467
+ console.log(" " + dim(`Skipped ${skillsResult.skippedGlobal} project-level rihal skills (global ones in ~/.claude/skills/ take precedence) \u2014 closes #679.`));
16468
+ }
16396
16469
  try {
16397
16470
  const { main: generateCommandSkills } = require(path2.join(PACKAGE_ROOT2, "cli", "generate-command-skills.cjs"));
16398
16471
  const stubsDir = path2.join(opts.target, ".claude", "skills");
16399
- const result = generateCommandSkills(PACKAGE_ROOT2, stubsDir, readPackageVersion());
16472
+ const result = generateCommandSkills(PACKAGE_ROOT2, stubsDir, readPackageVersion(), {
16473
+ skipGlobalDuplicates: isProjectInstall
16474
+ });
16400
16475
  if (result.generated > 0) {
16401
16476
  console.log(" " + dim(`${result.generated} sidebar skill stub${result.generated === 1 ? "" : "s"} generated for command discoverability`));
16402
16477
  skillsInstalled += result.generated;
16403
16478
  }
16479
+ if (result.skippedGlobal > 0) {
16480
+ console.log(" " + dim(`Skipped ${result.skippedGlobal} sidebar stub${result.skippedGlobal === 1 ? "" : "s"} that duplicate global ~/.claude/skills/ \u2014 closes #679.`));
16481
+ }
16404
16482
  } catch (err) {
16405
16483
  console.log(" " + dim(`(sidebar stub generation skipped: ${err.message})`));
16406
16484
  }
@@ -17666,7 +17744,7 @@ var require_uninstall = __commonJS({
17666
17744
  function isKnownSkillName(name) {
17667
17745
  return KNOWN_ACTION_SKILLS.includes(name);
17668
17746
  }
17669
- function planToPathList(plan, cwd) {
17747
+ function planToPathList(plan, cwd, options = {}) {
17670
17748
  const paths = [];
17671
17749
  for (const name of plan.claude.skills) {
17672
17750
  paths.push(path2.join(".claude/skills", name));
@@ -17689,10 +17767,25 @@ var require_uninstall = __commonJS({
17689
17767
  if (plan.agentsMd && fs2.existsSync(path2.join(cwd, "AGENTS.md"))) {
17690
17768
  paths.push("AGENTS.md");
17691
17769
  }
17770
+ if (options.purge) {
17771
+ const rihalDir = path2.join(cwd, ".rihal");
17772
+ if (fs2.existsSync(rihalDir)) {
17773
+ try {
17774
+ for (const entry of fs2.readdirSync(rihalDir)) {
17775
+ if (entry === "backups") continue;
17776
+ paths.push(path2.join(".rihal", entry));
17777
+ }
17778
+ } catch {
17779
+ }
17780
+ }
17781
+ if (fs2.existsSync(path2.join(cwd, ".planning"))) {
17782
+ paths.push(".planning");
17783
+ }
17784
+ }
17692
17785
  return paths;
17693
17786
  }
17694
- function createBackup(cwd, plan) {
17695
- const paths = planToPathList(plan, cwd);
17787
+ function createBackup(cwd, plan, options = {}) {
17788
+ const paths = planToPathList(plan, cwd, { purge: options.purge === true });
17696
17789
  if (paths.length === 0) {
17697
17790
  return { ok: false, warning: "nothing to back up" };
17698
17791
  }
@@ -17700,11 +17793,11 @@ var require_uninstall = __commonJS({
17700
17793
  if (tarCheck.status !== 0) {
17701
17794
  return { ok: false, warning: "tar not available on this system" };
17702
17795
  }
17703
- const backupsDir = path2.join(cwd, ".rihal/backups");
17796
+ const backupsDir = options.purge ? path2.join(cwd, ".rihal-backups") : path2.join(cwd, ".rihal/backups");
17704
17797
  try {
17705
17798
  fs2.mkdirSync(backupsDir, { recursive: true });
17706
17799
  } catch (err) {
17707
- return { ok: false, warning: `could not create .rihal/backups/: ${err.message}` };
17800
+ return { ok: false, warning: `could not create ${path2.relative(cwd, backupsDir)}/: ${err.message}` };
17708
17801
  }
17709
17802
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17710
17803
  const backupFile = path2.join(backupsDir, `uninstall-${ts}.tgz`);
@@ -17845,9 +17938,12 @@ var require_uninstall = __commonJS({
17845
17938
  }
17846
17939
  }
17847
17940
  console.log();
17848
- const backup = createBackup(cwd, plan);
17941
+ const backup = createBackup(cwd, plan, { purge: opts.purge === true });
17849
17942
  if (backup.ok) {
17850
17943
  console.log(` \u{1F4BE} backup created: ${backup.path}`);
17944
+ if (opts.purge) {
17945
+ console.log(" includes .rihal/ and .planning/ (state, decisions, planning artifacts)");
17946
+ }
17851
17947
  } else {
17852
17948
  console.log(` \u26A0 no backup created (${backup.warning}) \u2014 continuing anyway`);
17853
17949
  }
@@ -17977,7 +18073,7 @@ var require_uninstall = __commonJS({
17977
18073
  if (fs2.existsSync(gitignorePath)) {
17978
18074
  try {
17979
18075
  const before = fs2.readFileSync(gitignorePath, "utf8");
17980
- const stripped = before.replace(/\n?# >>> rihal-code >>>[\s\S]*?# <<< rihal-code <<<\n?/g, "\n").replace(/\n?# rcode[\s\S]*?(?=\n\n|\n$|$)/g, "\n").replace(/\n{3,}/g, "\n\n");
18076
+ const stripped = before.replace(/\n?# ===== rcode-managed gitignore block[\s\S]*?# ===== end rcode-managed gitignore block =====\n?/g, "\n").replace(/\n?# >>> rihal-code >>>[\s\S]*?# <<< rihal-code <<<\n?/g, "\n").replace(/\n{3,}/g, "\n\n");
17981
18077
  if (stripped !== before) {
17982
18078
  fs2.writeFileSync(gitignorePath, stripped);
17983
18079
  console.log(` \u2713 stripped rcode block from .gitignore (--purge)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzlaa/rcode",
3
- "version": "3.4.22",
3
+ "version": "3.4.23",
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": {
@@ -15,6 +15,7 @@
15
15
  "postinstall": "node cli/postinstall.js",
16
16
  "build:cli": "node scripts/build.cjs",
17
17
  "build": "node scripts/build.cjs",
18
+ "prepack": "node scripts/build.cjs",
18
19
  "dogfood": "bash scripts/dogfood-check.sh"
19
20
  },
20
21
  "files": [