@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.
- package/dist/rcode.js +123 -27
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
15402
|
+
project: null,
|
|
15403
|
+
_seeded_stub: true,
|
|
15386
15404
|
created: now,
|
|
15387
15405
|
updated: now,
|
|
15388
|
-
current_phase:
|
|
15406
|
+
current_phase: null,
|
|
15389
15407
|
current_plan: 0,
|
|
15390
15408
|
current_sprint: null,
|
|
15391
|
-
milestone:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 .
|
|
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?#
|
|
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.
|
|
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": [
|