@hanzlaa/rcode 3.4.24 → 3.4.26
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 +92 -2
- package/cli/uninstall.js +30 -30
- package/dist/rcode.js +348 -284
- package/package.json +1 -1
package/cli/install.js
CHANGED
|
@@ -57,7 +57,7 @@ const os = require('os');
|
|
|
57
57
|
|
|
58
58
|
// Atomic write helper (#687) + symlink-safe rmSync (#688) — protect against
|
|
59
59
|
// Ctrl+C mid-write and malicious symlink-traversal during dedup/cleanup.
|
|
60
|
-
const { writeFileAtomic, safeRmSync } = require(
|
|
60
|
+
const { writeFileAtomic, safeRmSync } = require('./lib/fsutil.cjs');
|
|
61
61
|
|
|
62
62
|
// Bundled packages — devDeps inlined by esbuild, loaded from node_modules in dev.
|
|
63
63
|
const pc = require('picocolors');
|
|
@@ -324,6 +324,11 @@ function detectIdeSignals(target) {
|
|
|
324
324
|
* actually wanted cursor or gemini.
|
|
325
325
|
*/
|
|
326
326
|
async function resolveIde(opts) {
|
|
327
|
+
// Issue #692: when the wizard has already collected opts.ides (interactive
|
|
328
|
+
// run from main()), resolveIde was re-prompting because it only checked
|
|
329
|
+
// opts.ideProvided (set by --ide flag, not by the wizard). Honor any
|
|
330
|
+
// pre-existing array result so we don't double-prompt.
|
|
331
|
+
if (Array.isArray(opts.ides) && opts.ides.length > 0) return opts.ides;
|
|
327
332
|
if (opts.ideProvided) return [opts.ide]; // user passed --ide, respect it
|
|
328
333
|
if (opts.noPrompt || opts.global) return ['claude']; // auto-install: always claude
|
|
329
334
|
if (opts.yes || !process.stdin.isTTY) {
|
|
@@ -1491,6 +1496,86 @@ async function install(opts) {
|
|
|
1491
1496
|
return 2;
|
|
1492
1497
|
}
|
|
1493
1498
|
|
|
1499
|
+
// Issue #691: file lock prevents concurrent installs from racing on the
|
|
1500
|
+
// same .rihal/_config/manifest.yaml + files-manifest.csv. Without it, two
|
|
1501
|
+
// parallel runs (two terminals, postinstall + manual install, etc.) can
|
|
1502
|
+
// each write a manifest the OTHER doesn't see → orphan sweep on the next
|
|
1503
|
+
// install deletes files the other run considered legit.
|
|
1504
|
+
let releaseLock = () => {};
|
|
1505
|
+
if (!opts.global) {
|
|
1506
|
+
const lockResult = acquireInstallLock(opts.target);
|
|
1507
|
+
if (!lockResult.ok) {
|
|
1508
|
+
console.log('');
|
|
1509
|
+
console.log(' ' + warn(`Another install is already running here (PID ${lockResult.pid}).`));
|
|
1510
|
+
console.log(' ' + dim(` Lock: ${lockResult.lockPath}`));
|
|
1511
|
+
console.log(' ' + dim(' If the other process crashed, delete the lock file and retry:'));
|
|
1512
|
+
console.log(' ' + dim(` rm ${lockResult.lockPath}`));
|
|
1513
|
+
console.log('');
|
|
1514
|
+
return 3;
|
|
1515
|
+
}
|
|
1516
|
+
releaseLock = lockResult.release;
|
|
1517
|
+
// Make sure the lock is released even if install throws unexpectedly.
|
|
1518
|
+
process.on('exit', releaseLock);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
try {
|
|
1522
|
+
return await installInner(opts);
|
|
1523
|
+
} finally {
|
|
1524
|
+
releaseLock();
|
|
1525
|
+
process.removeListener('exit', releaseLock);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Acquire an exclusive install lock at .rihal/.install.lock (issue #691).
|
|
1531
|
+
*
|
|
1532
|
+
* Returns:
|
|
1533
|
+
* { ok: true, release: () => void } lock acquired
|
|
1534
|
+
* { ok: false, pid: number, lockPath: string } another process holds it
|
|
1535
|
+
*
|
|
1536
|
+
* Stale-lock detection: if the recorded PID is not alive, the lock is
|
|
1537
|
+
* reclaimed automatically.
|
|
1538
|
+
*/
|
|
1539
|
+
function acquireInstallLock(target) {
|
|
1540
|
+
const lockDir = path.join(target, '.rihal');
|
|
1541
|
+
const lockPath = path.join(lockDir, '.install.lock');
|
|
1542
|
+
try {
|
|
1543
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
1544
|
+
} catch { /* fall through; openSync will fail with a clearer error */ }
|
|
1545
|
+
|
|
1546
|
+
function isAlive(pid) {
|
|
1547
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1551
|
+
try {
|
|
1552
|
+
const fd = fs.openSync(lockPath, 'wx'); // exclusive create
|
|
1553
|
+
fs.writeSync(fd, String(process.pid));
|
|
1554
|
+
fs.closeSync(fd);
|
|
1555
|
+
return {
|
|
1556
|
+
ok: true,
|
|
1557
|
+
release: () => {
|
|
1558
|
+
try { fs.unlinkSync(lockPath); } catch {}
|
|
1559
|
+
},
|
|
1560
|
+
};
|
|
1561
|
+
} catch (err) {
|
|
1562
|
+
if (err.code !== 'EEXIST') throw err;
|
|
1563
|
+
// Lock exists — check if holder is alive.
|
|
1564
|
+
let pid = 0;
|
|
1565
|
+
try { pid = parseInt(fs.readFileSync(lockPath, 'utf8'), 10); } catch {}
|
|
1566
|
+
if (pid && !isAlive(pid)) {
|
|
1567
|
+
// Stale lock — remove and retry once.
|
|
1568
|
+
try { fs.unlinkSync(lockPath); } catch {}
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
return { ok: false, pid, lockPath };
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
// Should be unreachable, but degrade gracefully.
|
|
1575
|
+
return { ok: false, pid: 0, lockPath };
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
async function installInner(opts) {
|
|
1494
1579
|
const pkgVersion = readPackageVersion();
|
|
1495
1580
|
|
|
1496
1581
|
// Header banner — only shown for interactive runs to keep CI/non-TTY logs terse.
|
|
@@ -2274,7 +2359,7 @@ function runInstallHealthCheck(target, counts) {
|
|
|
2274
2359
|
// in cli/lib/manifest.cjs already does this; we mirror its result here.
|
|
2275
2360
|
let expected = { agents: 20, skills: 20, commands: 20 };
|
|
2276
2361
|
try {
|
|
2277
|
-
const { readPackageManifest } = require(
|
|
2362
|
+
const { readPackageManifest } = require('./lib/manifest.cjs');
|
|
2278
2363
|
const pkgManifest = readPackageManifest(PACKAGE_ROOT);
|
|
2279
2364
|
if (pkgManifest && pkgManifest.agents instanceof Set && pkgManifest.actions instanceof Set) {
|
|
2280
2365
|
// Tolerate ~10% loss vs source — global precedence, .local.md
|
|
@@ -2423,6 +2508,11 @@ async function runInstallWizard(opts) {
|
|
|
2423
2508
|
});
|
|
2424
2509
|
if (isCancel(editorChoices)) { cancel('Installation cancelled.'); process.exit(0); }
|
|
2425
2510
|
opts.ides = editorChoices;
|
|
2511
|
+
// Issue #692: keep opts.ide and opts.ides consistent so downstream callers
|
|
2512
|
+
// that historically read either field see the same answer. Mark provided
|
|
2513
|
+
// so any later resolveIde call exits early.
|
|
2514
|
+
opts.ide = editorChoices[0];
|
|
2515
|
+
opts.ideProvided = true;
|
|
2426
2516
|
|
|
2427
2517
|
// ── 3. Communication language ─────────────────────────────────────────
|
|
2428
2518
|
const langChoice = await select({
|
package/cli/uninstall.js
CHANGED
|
@@ -220,35 +220,29 @@ function buildPlan(cwd, editors) {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
223
|
+
* Names of action-skill directories the installer places under .claude/skills/.
|
|
224
|
+
*
|
|
225
|
+
* Issue #693: this used to be a hardcoded array of 23 names that drifted from
|
|
226
|
+
* the source the moment anyone added or removed a skill in `rihal/skills/`.
|
|
227
|
+
* We now derive it from the package's own manifest (cli/lib/manifest.cjs)
|
|
228
|
+
* with a static fallback for the rare case where the manifest module isn't
|
|
229
|
+
* resolvable from the uninstall context.
|
|
226
230
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
'
|
|
237
|
-
'rihal-
|
|
238
|
-
'rihal
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
'rihal-market-research',
|
|
243
|
-
'rihal-product-brief',
|
|
244
|
-
'rihal-qa-generate-e2e-tests',
|
|
245
|
-
'rihal-retrospective',
|
|
246
|
-
'rihal-sprint-planning',
|
|
247
|
-
'rihal-sprint-status',
|
|
248
|
-
'rihal-technical-research',
|
|
249
|
-
'rihal-validate-prd',
|
|
250
|
-
'rihal-clone-website',
|
|
251
|
-
];
|
|
231
|
+
function discoverKnownActionSkills() {
|
|
232
|
+
try {
|
|
233
|
+
const { readPackageManifest } = require('./lib/manifest.cjs');
|
|
234
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
235
|
+
const pkg = readPackageManifest(packageRoot);
|
|
236
|
+
if (pkg && pkg.actions instanceof Set && pkg.actions.size > 0) {
|
|
237
|
+
return Array.from(pkg.actions);
|
|
238
|
+
}
|
|
239
|
+
} catch { /* fall through to static list */ }
|
|
240
|
+
// Static fallback — kept minimal, only the names that don't start with
|
|
241
|
+
// 'rihal-' would actually need this list since we already match
|
|
242
|
+
// 'rihal-*' via prefix. This is defensive only.
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
const KNOWN_ACTION_SKILLS = discoverKnownActionSkills();
|
|
252
246
|
|
|
253
247
|
function isKnownSkillName(name) {
|
|
254
248
|
return KNOWN_ACTION_SKILLS.includes(name);
|
|
@@ -425,9 +419,15 @@ async function runUninstall(args) {
|
|
|
425
419
|
const opts = parseArgs(args);
|
|
426
420
|
const cwd = process.cwd();
|
|
427
421
|
|
|
422
|
+
// Issue #693: keep the IDE list in sync with the installer. The installer
|
|
423
|
+
// ships claude/cursor/gemini/vscode/antigravity. The previous uninstaller
|
|
424
|
+
// list (claude/cursor/windsurf/antigravity) was missing gemini + vscode
|
|
425
|
+
// and included windsurf (which the installer never writes). Result: a
|
|
426
|
+
// user with vscode-style commands could never `rcode uninstall`.
|
|
427
|
+
const SUPPORTED_EDITORS = ['claude', 'cursor', 'gemini', 'vscode', 'antigravity'];
|
|
428
428
|
const editors = opts.editor
|
|
429
|
-
? (opts.editor === 'all' ?
|
|
430
|
-
:
|
|
429
|
+
? (opts.editor === 'all' ? SUPPORTED_EDITORS : [opts.editor])
|
|
430
|
+
: SUPPORTED_EDITORS;
|
|
431
431
|
|
|
432
432
|
console.log(`\n🕌 Rihal Code — Uninstall\n`);
|
|
433
433
|
console.log(` Project: ${cwd}`);
|
package/dist/rcode.js
CHANGED
|
@@ -5,6 +5,88 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
5
5
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
// cli/lib/fsutil.cjs
|
|
9
|
+
var require_fsutil = __commonJS({
|
|
10
|
+
"cli/lib/fsutil.cjs"(exports2, module2) {
|
|
11
|
+
var crypto = require("crypto");
|
|
12
|
+
var fs2 = require("fs");
|
|
13
|
+
var path2 = require("path");
|
|
14
|
+
function writeFileAtomic(filePath, content, opts = {}) {
|
|
15
|
+
const { encoding = "utf8", mode } = opts;
|
|
16
|
+
const dir = path2.dirname(filePath);
|
|
17
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
18
|
+
const tmpPath = path2.join(
|
|
19
|
+
dir,
|
|
20
|
+
`.${path2.basename(filePath)}.tmp-${process.pid}-${crypto.randomBytes(8).toString("hex")}`
|
|
21
|
+
);
|
|
22
|
+
let fd;
|
|
23
|
+
try {
|
|
24
|
+
fd = fs2.openSync(tmpPath, "wx", mode ?? 420);
|
|
25
|
+
fs2.writeSync(fd, content, 0, encoding);
|
|
26
|
+
fs2.fsyncSync(fd);
|
|
27
|
+
fs2.closeSync(fd);
|
|
28
|
+
fd = null;
|
|
29
|
+
fs2.renameSync(tmpPath, filePath);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (fd !== null && fd !== void 0) {
|
|
32
|
+
try {
|
|
33
|
+
fs2.closeSync(fd);
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
fs2.unlinkSync(tmpPath);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function writeJsonAtomic(filePath, obj, opts = {}) {
|
|
45
|
+
const content = JSON.stringify(obj, null, 2) + "\n";
|
|
46
|
+
writeFileAtomic(filePath, content, opts);
|
|
47
|
+
}
|
|
48
|
+
function safeRmSync(targetPath, projectRoot) {
|
|
49
|
+
let stats;
|
|
50
|
+
try {
|
|
51
|
+
stats = fs2.lstatSync(targetPath);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (err.code === "ENOENT") return { ok: true, reason: "missing" };
|
|
54
|
+
return { ok: false, reason: `lstat: ${err.message}` };
|
|
55
|
+
}
|
|
56
|
+
if (stats.isSymbolicLink()) {
|
|
57
|
+
try {
|
|
58
|
+
fs2.unlinkSync(targetPath);
|
|
59
|
+
return { ok: true, reason: "symlink-unlinked" };
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return { ok: false, reason: `unlink: ${err.message}` };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const root = path2.resolve(projectRoot);
|
|
65
|
+
let resolved;
|
|
66
|
+
try {
|
|
67
|
+
resolved = fs2.realpathSync(targetPath);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return { ok: false, reason: `realpath: ${err.message}` };
|
|
70
|
+
}
|
|
71
|
+
const relative = path2.relative(root, resolved);
|
|
72
|
+
if (relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
73
|
+
return { ok: false, reason: "outside-root" };
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
fs2.rmSync(resolved, { recursive: true, force: true });
|
|
77
|
+
return { ok: true };
|
|
78
|
+
} catch (err) {
|
|
79
|
+
return { ok: false, reason: `rmSync: ${err.message}` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
module2.exports = {
|
|
83
|
+
writeFileAtomic,
|
|
84
|
+
writeJsonAtomic,
|
|
85
|
+
safeRmSync
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
8
90
|
// node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
9
91
|
var require_picocolors = __commonJS({
|
|
10
92
|
"node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
@@ -14939,6 +15021,180 @@ ${e__default.gray(d)} ${t}
|
|
|
14939
15021
|
}
|
|
14940
15022
|
});
|
|
14941
15023
|
|
|
15024
|
+
// cli/lib/manifest.cjs
|
|
15025
|
+
var require_manifest = __commonJS({
|
|
15026
|
+
"cli/lib/manifest.cjs"(exports2, module2) {
|
|
15027
|
+
var fs2 = require("fs");
|
|
15028
|
+
var path2 = require("path");
|
|
15029
|
+
function readPackageManifest(packageRoot) {
|
|
15030
|
+
const skillsRoot = path2.join(packageRoot, "rihal/skills");
|
|
15031
|
+
const manifest = { agents: /* @__PURE__ */ new Set(), actions: /* @__PURE__ */ new Set() };
|
|
15032
|
+
const agentsDir = path2.join(skillsRoot, "agents");
|
|
15033
|
+
if (fs2.existsSync(agentsDir)) {
|
|
15034
|
+
for (const entry of fs2.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
15035
|
+
if (entry.isDirectory()) manifest.agents.add(entry.name);
|
|
15036
|
+
}
|
|
15037
|
+
}
|
|
15038
|
+
function walkActions(dir) {
|
|
15039
|
+
if (!fs2.existsSync(dir)) return;
|
|
15040
|
+
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
15041
|
+
if (!entry.isDirectory()) continue;
|
|
15042
|
+
const full = path2.join(dir, entry.name);
|
|
15043
|
+
if (fs2.existsSync(path2.join(full, "SKILL.md"))) {
|
|
15044
|
+
const installedName = entry.name.startsWith("rihal-") ? entry.name : `rihal-${entry.name}`;
|
|
15045
|
+
manifest.actions.add(installedName);
|
|
15046
|
+
} else {
|
|
15047
|
+
walkActions(full);
|
|
15048
|
+
}
|
|
15049
|
+
}
|
|
15050
|
+
}
|
|
15051
|
+
walkActions(path2.join(skillsRoot, "actions"));
|
|
15052
|
+
return manifest;
|
|
15053
|
+
}
|
|
15054
|
+
function readInstalledDirs(dir, prefix = null) {
|
|
15055
|
+
if (!fs2.existsSync(dir)) return /* @__PURE__ */ new Set();
|
|
15056
|
+
const names = /* @__PURE__ */ new Set();
|
|
15057
|
+
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
15058
|
+
if (!entry.isDirectory()) continue;
|
|
15059
|
+
if (prefix && !entry.name.startsWith(prefix)) continue;
|
|
15060
|
+
names.add(prefix ? entry.name.slice(prefix.length) : entry.name);
|
|
15061
|
+
}
|
|
15062
|
+
return names;
|
|
15063
|
+
}
|
|
15064
|
+
function diffSet(editor, kind, expected, installed) {
|
|
15065
|
+
const missing = [...expected].filter((x) => !installed.has(x)).sort();
|
|
15066
|
+
const extra = [...installed].filter((x) => !expected.has(x)).sort();
|
|
15067
|
+
return {
|
|
15068
|
+
editor,
|
|
15069
|
+
kind,
|
|
15070
|
+
expectedCount: expected.size,
|
|
15071
|
+
installedCount: installed.size,
|
|
15072
|
+
missing,
|
|
15073
|
+
extra
|
|
15074
|
+
};
|
|
15075
|
+
}
|
|
15076
|
+
function verifyClaudeInstall(cwd, packageRoot) {
|
|
15077
|
+
const pkg = readPackageManifest(packageRoot);
|
|
15078
|
+
const agentsDir = path2.join(cwd, ".claude/agents");
|
|
15079
|
+
const skillsDir = path2.join(cwd, ".claude/skills");
|
|
15080
|
+
const installedAgents = /* @__PURE__ */ new Set();
|
|
15081
|
+
if (fs2.existsSync(agentsDir)) {
|
|
15082
|
+
for (const f of fs2.readdirSync(agentsDir)) {
|
|
15083
|
+
if (f.startsWith("rihal-") && f.endsWith(".md")) {
|
|
15084
|
+
installedAgents.add(f.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
15085
|
+
}
|
|
15086
|
+
}
|
|
15087
|
+
}
|
|
15088
|
+
if (installedAgents.size === 0) {
|
|
15089
|
+
try {
|
|
15090
|
+
const os = require("os");
|
|
15091
|
+
const globalAgentsDir = path2.join(os.homedir(), ".claude/agents");
|
|
15092
|
+
if (fs2.existsSync(globalAgentsDir)) {
|
|
15093
|
+
for (const f of fs2.readdirSync(globalAgentsDir)) {
|
|
15094
|
+
if (f.startsWith("rihal-") && f.endsWith(".md")) {
|
|
15095
|
+
installedAgents.add(f.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
15096
|
+
}
|
|
15097
|
+
}
|
|
15098
|
+
}
|
|
15099
|
+
} catch {
|
|
15100
|
+
}
|
|
15101
|
+
}
|
|
15102
|
+
const allInstalled = readInstalledDirs(skillsDir);
|
|
15103
|
+
const actionsInstalled = new Set(
|
|
15104
|
+
[...allInstalled].filter((n) => !n.startsWith("rihal-"))
|
|
15105
|
+
);
|
|
15106
|
+
return [
|
|
15107
|
+
diffSet("claude", "agents", pkg.agents, installedAgents),
|
|
15108
|
+
diffSet("claude", "actions", pkg.actions, actionsInstalled)
|
|
15109
|
+
];
|
|
15110
|
+
}
|
|
15111
|
+
function verifyRulesInstall(editor, cwd, packageRoot) {
|
|
15112
|
+
const pkg = readPackageManifest(packageRoot);
|
|
15113
|
+
const rulesDir = path2.join(
|
|
15114
|
+
cwd,
|
|
15115
|
+
editor === "cursor" ? ".cursor/rules" : ".windsurf/rules"
|
|
15116
|
+
);
|
|
15117
|
+
const installed = /* @__PURE__ */ new Set();
|
|
15118
|
+
if (fs2.existsSync(rulesDir)) {
|
|
15119
|
+
for (const file of fs2.readdirSync(rulesDir)) {
|
|
15120
|
+
if (!file.startsWith("rihal-") || !file.endsWith(".mdc")) continue;
|
|
15121
|
+
if (file === "rihal-code.mdc") continue;
|
|
15122
|
+
installed.add(file.replace(/^rihal-/, "").replace(/\.mdc$/, ""));
|
|
15123
|
+
}
|
|
15124
|
+
}
|
|
15125
|
+
const digestsDir = path2.join(packageRoot, "rihal/digests");
|
|
15126
|
+
const expected = /* @__PURE__ */ new Set();
|
|
15127
|
+
if (fs2.existsSync(digestsDir)) {
|
|
15128
|
+
for (const file of fs2.readdirSync(digestsDir)) {
|
|
15129
|
+
if (!file.endsWith(".md") || file === "README.md") continue;
|
|
15130
|
+
expected.add(file.replace(/\.md$/, ""));
|
|
15131
|
+
}
|
|
15132
|
+
}
|
|
15133
|
+
return [diffSet(editor, "rules", expected, installed)];
|
|
15134
|
+
}
|
|
15135
|
+
function verifyAntigravityInstall(cwd, packageRoot) {
|
|
15136
|
+
const agentsDir = path2.join(cwd, ".antigravity/agents");
|
|
15137
|
+
const installed = /* @__PURE__ */ new Set();
|
|
15138
|
+
if (fs2.existsSync(agentsDir)) {
|
|
15139
|
+
for (const file of fs2.readdirSync(agentsDir)) {
|
|
15140
|
+
if (!file.startsWith("rihal-") || !file.endsWith(".md")) continue;
|
|
15141
|
+
installed.add(file.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
15142
|
+
}
|
|
15143
|
+
}
|
|
15144
|
+
const digestsDir = path2.join(packageRoot, "rihal/digests");
|
|
15145
|
+
const expected = /* @__PURE__ */ new Set();
|
|
15146
|
+
if (fs2.existsSync(digestsDir)) {
|
|
15147
|
+
for (const file of fs2.readdirSync(digestsDir)) {
|
|
15148
|
+
if (!file.endsWith(".md") || file === "README.md") continue;
|
|
15149
|
+
expected.add(file.replace(/\.md$/, ""));
|
|
15150
|
+
}
|
|
15151
|
+
}
|
|
15152
|
+
return [diffSet("antigravity", "agents", expected, installed)];
|
|
15153
|
+
}
|
|
15154
|
+
function verifyInstall(cwd, packageRoot, editors) {
|
|
15155
|
+
const reports = [];
|
|
15156
|
+
if (editors.includes("claude")) {
|
|
15157
|
+
reports.push(...verifyClaudeInstall(cwd, packageRoot));
|
|
15158
|
+
}
|
|
15159
|
+
if (editors.includes("cursor")) {
|
|
15160
|
+
reports.push(...verifyRulesInstall("cursor", cwd, packageRoot));
|
|
15161
|
+
}
|
|
15162
|
+
if (editors.includes("windsurf")) {
|
|
15163
|
+
reports.push(...verifyRulesInstall("windsurf", cwd, packageRoot));
|
|
15164
|
+
}
|
|
15165
|
+
if (editors.includes("antigravity")) {
|
|
15166
|
+
reports.push(...verifyAntigravityInstall(cwd, packageRoot));
|
|
15167
|
+
}
|
|
15168
|
+
const hasDrift = reports.some((r) => r.missing.length > 0 || r.extra.length > 0);
|
|
15169
|
+
return { reports, hasDrift };
|
|
15170
|
+
}
|
|
15171
|
+
function formatReport(reports) {
|
|
15172
|
+
const lines = [];
|
|
15173
|
+
for (const r of reports) {
|
|
15174
|
+
const symbol = r.missing.length === 0 && r.extra.length === 0 ? "\u2713" : "\u26A0";
|
|
15175
|
+
lines.push(
|
|
15176
|
+
` ${symbol} ${r.editor.padEnd(12)} ${r.kind.padEnd(8)} ${r.installedCount}/${r.expectedCount}`
|
|
15177
|
+
);
|
|
15178
|
+
if (r.missing.length > 0) {
|
|
15179
|
+
lines.push(` missing: ${r.missing.join(", ")}`);
|
|
15180
|
+
}
|
|
15181
|
+
if (r.extra.length > 0) {
|
|
15182
|
+
lines.push(` extra: ${r.extra.join(", ")}`);
|
|
15183
|
+
}
|
|
15184
|
+
}
|
|
15185
|
+
return lines.join("\n");
|
|
15186
|
+
}
|
|
15187
|
+
module2.exports = {
|
|
15188
|
+
readPackageManifest,
|
|
15189
|
+
verifyInstall,
|
|
15190
|
+
verifyClaudeInstall,
|
|
15191
|
+
verifyRulesInstall,
|
|
15192
|
+
verifyAntigravityInstall,
|
|
15193
|
+
formatReport
|
|
15194
|
+
};
|
|
15195
|
+
}
|
|
15196
|
+
});
|
|
15197
|
+
|
|
14942
15198
|
// cli/install.js
|
|
14943
15199
|
var require_install = __commonJS({
|
|
14944
15200
|
"cli/install.js"(exports2, module2) {
|
|
@@ -14946,7 +15202,7 @@ var require_install = __commonJS({
|
|
|
14946
15202
|
var path2 = require("path");
|
|
14947
15203
|
var crypto = require("crypto");
|
|
14948
15204
|
var os = require("os");
|
|
14949
|
-
var { writeFileAtomic, safeRmSync } =
|
|
15205
|
+
var { writeFileAtomic, safeRmSync } = require_fsutil();
|
|
14950
15206
|
var pc = require_picocolors();
|
|
14951
15207
|
var { createSpinner } = require_dist();
|
|
14952
15208
|
var fg = require_out4();
|
|
@@ -15158,6 +15414,7 @@ var require_install = __commonJS({
|
|
|
15158
15414
|
return signals;
|
|
15159
15415
|
}
|
|
15160
15416
|
async function resolveIde(opts) {
|
|
15417
|
+
if (Array.isArray(opts.ides) && opts.ides.length > 0) return opts.ides;
|
|
15161
15418
|
if (opts.ideProvided) return [opts.ide];
|
|
15162
15419
|
if (opts.noPrompt || opts.global) return ["claude"];
|
|
15163
15420
|
if (opts.yes || !process.stdin.isTTY) {
|
|
@@ -16046,6 +16303,78 @@ ${BLOCK}`, { mode: 493 });
|
|
|
16046
16303
|
console.log("");
|
|
16047
16304
|
return 2;
|
|
16048
16305
|
}
|
|
16306
|
+
let releaseLock = () => {
|
|
16307
|
+
};
|
|
16308
|
+
if (!opts.global) {
|
|
16309
|
+
const lockResult = acquireInstallLock(opts.target);
|
|
16310
|
+
if (!lockResult.ok) {
|
|
16311
|
+
console.log("");
|
|
16312
|
+
console.log(" " + warn(`Another install is already running here (PID ${lockResult.pid}).`));
|
|
16313
|
+
console.log(" " + dim(` Lock: ${lockResult.lockPath}`));
|
|
16314
|
+
console.log(" " + dim(" If the other process crashed, delete the lock file and retry:"));
|
|
16315
|
+
console.log(" " + dim(` rm ${lockResult.lockPath}`));
|
|
16316
|
+
console.log("");
|
|
16317
|
+
return 3;
|
|
16318
|
+
}
|
|
16319
|
+
releaseLock = lockResult.release;
|
|
16320
|
+
process.on("exit", releaseLock);
|
|
16321
|
+
}
|
|
16322
|
+
try {
|
|
16323
|
+
return await installInner(opts);
|
|
16324
|
+
} finally {
|
|
16325
|
+
releaseLock();
|
|
16326
|
+
process.removeListener("exit", releaseLock);
|
|
16327
|
+
}
|
|
16328
|
+
}
|
|
16329
|
+
function acquireInstallLock(target) {
|
|
16330
|
+
const lockDir = path2.join(target, ".rihal");
|
|
16331
|
+
const lockPath = path2.join(lockDir, ".install.lock");
|
|
16332
|
+
try {
|
|
16333
|
+
fs2.mkdirSync(lockDir, { recursive: true });
|
|
16334
|
+
} catch {
|
|
16335
|
+
}
|
|
16336
|
+
function isAlive(pid) {
|
|
16337
|
+
try {
|
|
16338
|
+
process.kill(pid, 0);
|
|
16339
|
+
return true;
|
|
16340
|
+
} catch {
|
|
16341
|
+
return false;
|
|
16342
|
+
}
|
|
16343
|
+
}
|
|
16344
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
16345
|
+
try {
|
|
16346
|
+
const fd = fs2.openSync(lockPath, "wx");
|
|
16347
|
+
fs2.writeSync(fd, String(process.pid));
|
|
16348
|
+
fs2.closeSync(fd);
|
|
16349
|
+
return {
|
|
16350
|
+
ok: true,
|
|
16351
|
+
release: () => {
|
|
16352
|
+
try {
|
|
16353
|
+
fs2.unlinkSync(lockPath);
|
|
16354
|
+
} catch {
|
|
16355
|
+
}
|
|
16356
|
+
}
|
|
16357
|
+
};
|
|
16358
|
+
} catch (err) {
|
|
16359
|
+
if (err.code !== "EEXIST") throw err;
|
|
16360
|
+
let pid = 0;
|
|
16361
|
+
try {
|
|
16362
|
+
pid = parseInt(fs2.readFileSync(lockPath, "utf8"), 10);
|
|
16363
|
+
} catch {
|
|
16364
|
+
}
|
|
16365
|
+
if (pid && !isAlive(pid)) {
|
|
16366
|
+
try {
|
|
16367
|
+
fs2.unlinkSync(lockPath);
|
|
16368
|
+
} catch {
|
|
16369
|
+
}
|
|
16370
|
+
continue;
|
|
16371
|
+
}
|
|
16372
|
+
return { ok: false, pid, lockPath };
|
|
16373
|
+
}
|
|
16374
|
+
}
|
|
16375
|
+
return { ok: false, pid: 0, lockPath };
|
|
16376
|
+
}
|
|
16377
|
+
async function installInner(opts) {
|
|
16049
16378
|
const pkgVersion = readPackageVersion();
|
|
16050
16379
|
const isInteractive = process.stdin.isTTY && !opts.yes;
|
|
16051
16380
|
if (isInteractive) printInstallHeader(pkgVersion);
|
|
@@ -16665,7 +16994,7 @@ commit_planning: ${desired}
|
|
|
16665
16994
|
let fails = 0;
|
|
16666
16995
|
let expected = { agents: 20, skills: 20, commands: 20 };
|
|
16667
16996
|
try {
|
|
16668
|
-
const { readPackageManifest } =
|
|
16997
|
+
const { readPackageManifest } = require_manifest();
|
|
16669
16998
|
const pkgManifest = readPackageManifest(PACKAGE_ROOT2);
|
|
16670
16999
|
if (pkgManifest && pkgManifest.agents instanceof Set && pkgManifest.actions instanceof Set) {
|
|
16671
17000
|
const tolerate = (n) => Math.max(1, Math.floor(n * 0.9));
|
|
@@ -16796,6 +17125,8 @@ commit_planning: ${desired}
|
|
|
16796
17125
|
process.exit(0);
|
|
16797
17126
|
}
|
|
16798
17127
|
opts.ides = editorChoices;
|
|
17128
|
+
opts.ide = editorChoices[0];
|
|
17129
|
+
opts.ideProvided = true;
|
|
16799
17130
|
const langChoice = await select({
|
|
16800
17131
|
message: "Communication language?",
|
|
16801
17132
|
options: [
|
|
@@ -17080,262 +17411,6 @@ var require_prompts = __commonJS({
|
|
|
17080
17411
|
}
|
|
17081
17412
|
});
|
|
17082
17413
|
|
|
17083
|
-
// cli/lib/fsutil.cjs
|
|
17084
|
-
var require_fsutil = __commonJS({
|
|
17085
|
-
"cli/lib/fsutil.cjs"(exports2, module2) {
|
|
17086
|
-
var crypto = require("crypto");
|
|
17087
|
-
var fs2 = require("fs");
|
|
17088
|
-
var path2 = require("path");
|
|
17089
|
-
function writeFileAtomic(filePath, content, opts = {}) {
|
|
17090
|
-
const { encoding = "utf8", mode } = opts;
|
|
17091
|
-
const dir = path2.dirname(filePath);
|
|
17092
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
17093
|
-
const tmpPath = path2.join(
|
|
17094
|
-
dir,
|
|
17095
|
-
`.${path2.basename(filePath)}.tmp-${process.pid}-${crypto.randomBytes(8).toString("hex")}`
|
|
17096
|
-
);
|
|
17097
|
-
let fd;
|
|
17098
|
-
try {
|
|
17099
|
-
fd = fs2.openSync(tmpPath, "wx", mode ?? 420);
|
|
17100
|
-
fs2.writeSync(fd, content, 0, encoding);
|
|
17101
|
-
fs2.fsyncSync(fd);
|
|
17102
|
-
fs2.closeSync(fd);
|
|
17103
|
-
fd = null;
|
|
17104
|
-
fs2.renameSync(tmpPath, filePath);
|
|
17105
|
-
} catch (err) {
|
|
17106
|
-
if (fd !== null && fd !== void 0) {
|
|
17107
|
-
try {
|
|
17108
|
-
fs2.closeSync(fd);
|
|
17109
|
-
} catch {
|
|
17110
|
-
}
|
|
17111
|
-
}
|
|
17112
|
-
try {
|
|
17113
|
-
fs2.unlinkSync(tmpPath);
|
|
17114
|
-
} catch {
|
|
17115
|
-
}
|
|
17116
|
-
throw err;
|
|
17117
|
-
}
|
|
17118
|
-
}
|
|
17119
|
-
function writeJsonAtomic(filePath, obj, opts = {}) {
|
|
17120
|
-
const content = JSON.stringify(obj, null, 2) + "\n";
|
|
17121
|
-
writeFileAtomic(filePath, content, opts);
|
|
17122
|
-
}
|
|
17123
|
-
function safeRmSync(targetPath, projectRoot) {
|
|
17124
|
-
let stats;
|
|
17125
|
-
try {
|
|
17126
|
-
stats = fs2.lstatSync(targetPath);
|
|
17127
|
-
} catch (err) {
|
|
17128
|
-
if (err.code === "ENOENT") return { ok: true, reason: "missing" };
|
|
17129
|
-
return { ok: false, reason: `lstat: ${err.message}` };
|
|
17130
|
-
}
|
|
17131
|
-
if (stats.isSymbolicLink()) {
|
|
17132
|
-
try {
|
|
17133
|
-
fs2.unlinkSync(targetPath);
|
|
17134
|
-
return { ok: true, reason: "symlink-unlinked" };
|
|
17135
|
-
} catch (err) {
|
|
17136
|
-
return { ok: false, reason: `unlink: ${err.message}` };
|
|
17137
|
-
}
|
|
17138
|
-
}
|
|
17139
|
-
const root = path2.resolve(projectRoot);
|
|
17140
|
-
let resolved;
|
|
17141
|
-
try {
|
|
17142
|
-
resolved = fs2.realpathSync(targetPath);
|
|
17143
|
-
} catch (err) {
|
|
17144
|
-
return { ok: false, reason: `realpath: ${err.message}` };
|
|
17145
|
-
}
|
|
17146
|
-
const relative = path2.relative(root, resolved);
|
|
17147
|
-
if (relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
17148
|
-
return { ok: false, reason: "outside-root" };
|
|
17149
|
-
}
|
|
17150
|
-
try {
|
|
17151
|
-
fs2.rmSync(resolved, { recursive: true, force: true });
|
|
17152
|
-
return { ok: true };
|
|
17153
|
-
} catch (err) {
|
|
17154
|
-
return { ok: false, reason: `rmSync: ${err.message}` };
|
|
17155
|
-
}
|
|
17156
|
-
}
|
|
17157
|
-
module2.exports = {
|
|
17158
|
-
writeFileAtomic,
|
|
17159
|
-
writeJsonAtomic,
|
|
17160
|
-
safeRmSync
|
|
17161
|
-
};
|
|
17162
|
-
}
|
|
17163
|
-
});
|
|
17164
|
-
|
|
17165
|
-
// cli/lib/manifest.cjs
|
|
17166
|
-
var require_manifest = __commonJS({
|
|
17167
|
-
"cli/lib/manifest.cjs"(exports2, module2) {
|
|
17168
|
-
var fs2 = require("fs");
|
|
17169
|
-
var path2 = require("path");
|
|
17170
|
-
function readPackageManifest(packageRoot) {
|
|
17171
|
-
const skillsRoot = path2.join(packageRoot, "rihal/skills");
|
|
17172
|
-
const manifest = { agents: /* @__PURE__ */ new Set(), actions: /* @__PURE__ */ new Set() };
|
|
17173
|
-
const agentsDir = path2.join(skillsRoot, "agents");
|
|
17174
|
-
if (fs2.existsSync(agentsDir)) {
|
|
17175
|
-
for (const entry of fs2.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
17176
|
-
if (entry.isDirectory()) manifest.agents.add(entry.name);
|
|
17177
|
-
}
|
|
17178
|
-
}
|
|
17179
|
-
function walkActions(dir) {
|
|
17180
|
-
if (!fs2.existsSync(dir)) return;
|
|
17181
|
-
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
17182
|
-
if (!entry.isDirectory()) continue;
|
|
17183
|
-
const full = path2.join(dir, entry.name);
|
|
17184
|
-
if (fs2.existsSync(path2.join(full, "SKILL.md"))) {
|
|
17185
|
-
const installedName = entry.name.startsWith("rihal-") ? entry.name : `rihal-${entry.name}`;
|
|
17186
|
-
manifest.actions.add(installedName);
|
|
17187
|
-
} else {
|
|
17188
|
-
walkActions(full);
|
|
17189
|
-
}
|
|
17190
|
-
}
|
|
17191
|
-
}
|
|
17192
|
-
walkActions(path2.join(skillsRoot, "actions"));
|
|
17193
|
-
return manifest;
|
|
17194
|
-
}
|
|
17195
|
-
function readInstalledDirs(dir, prefix = null) {
|
|
17196
|
-
if (!fs2.existsSync(dir)) return /* @__PURE__ */ new Set();
|
|
17197
|
-
const names = /* @__PURE__ */ new Set();
|
|
17198
|
-
for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
|
|
17199
|
-
if (!entry.isDirectory()) continue;
|
|
17200
|
-
if (prefix && !entry.name.startsWith(prefix)) continue;
|
|
17201
|
-
names.add(prefix ? entry.name.slice(prefix.length) : entry.name);
|
|
17202
|
-
}
|
|
17203
|
-
return names;
|
|
17204
|
-
}
|
|
17205
|
-
function diffSet(editor, kind, expected, installed) {
|
|
17206
|
-
const missing = [...expected].filter((x) => !installed.has(x)).sort();
|
|
17207
|
-
const extra = [...installed].filter((x) => !expected.has(x)).sort();
|
|
17208
|
-
return {
|
|
17209
|
-
editor,
|
|
17210
|
-
kind,
|
|
17211
|
-
expectedCount: expected.size,
|
|
17212
|
-
installedCount: installed.size,
|
|
17213
|
-
missing,
|
|
17214
|
-
extra
|
|
17215
|
-
};
|
|
17216
|
-
}
|
|
17217
|
-
function verifyClaudeInstall(cwd, packageRoot) {
|
|
17218
|
-
const pkg = readPackageManifest(packageRoot);
|
|
17219
|
-
const agentsDir = path2.join(cwd, ".claude/agents");
|
|
17220
|
-
const skillsDir = path2.join(cwd, ".claude/skills");
|
|
17221
|
-
const installedAgents = /* @__PURE__ */ new Set();
|
|
17222
|
-
if (fs2.existsSync(agentsDir)) {
|
|
17223
|
-
for (const f of fs2.readdirSync(agentsDir)) {
|
|
17224
|
-
if (f.startsWith("rihal-") && f.endsWith(".md")) {
|
|
17225
|
-
installedAgents.add(f.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
17226
|
-
}
|
|
17227
|
-
}
|
|
17228
|
-
}
|
|
17229
|
-
if (installedAgents.size === 0) {
|
|
17230
|
-
try {
|
|
17231
|
-
const os = require("os");
|
|
17232
|
-
const globalAgentsDir = path2.join(os.homedir(), ".claude/agents");
|
|
17233
|
-
if (fs2.existsSync(globalAgentsDir)) {
|
|
17234
|
-
for (const f of fs2.readdirSync(globalAgentsDir)) {
|
|
17235
|
-
if (f.startsWith("rihal-") && f.endsWith(".md")) {
|
|
17236
|
-
installedAgents.add(f.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
17237
|
-
}
|
|
17238
|
-
}
|
|
17239
|
-
}
|
|
17240
|
-
} catch {
|
|
17241
|
-
}
|
|
17242
|
-
}
|
|
17243
|
-
const allInstalled = readInstalledDirs(skillsDir);
|
|
17244
|
-
const actionsInstalled = new Set(
|
|
17245
|
-
[...allInstalled].filter((n) => !n.startsWith("rihal-"))
|
|
17246
|
-
);
|
|
17247
|
-
return [
|
|
17248
|
-
diffSet("claude", "agents", pkg.agents, installedAgents),
|
|
17249
|
-
diffSet("claude", "actions", pkg.actions, actionsInstalled)
|
|
17250
|
-
];
|
|
17251
|
-
}
|
|
17252
|
-
function verifyRulesInstall(editor, cwd, packageRoot) {
|
|
17253
|
-
const pkg = readPackageManifest(packageRoot);
|
|
17254
|
-
const rulesDir = path2.join(
|
|
17255
|
-
cwd,
|
|
17256
|
-
editor === "cursor" ? ".cursor/rules" : ".windsurf/rules"
|
|
17257
|
-
);
|
|
17258
|
-
const installed = /* @__PURE__ */ new Set();
|
|
17259
|
-
if (fs2.existsSync(rulesDir)) {
|
|
17260
|
-
for (const file of fs2.readdirSync(rulesDir)) {
|
|
17261
|
-
if (!file.startsWith("rihal-") || !file.endsWith(".mdc")) continue;
|
|
17262
|
-
if (file === "rihal-code.mdc") continue;
|
|
17263
|
-
installed.add(file.replace(/^rihal-/, "").replace(/\.mdc$/, ""));
|
|
17264
|
-
}
|
|
17265
|
-
}
|
|
17266
|
-
const digestsDir = path2.join(packageRoot, "rihal/digests");
|
|
17267
|
-
const expected = /* @__PURE__ */ new Set();
|
|
17268
|
-
if (fs2.existsSync(digestsDir)) {
|
|
17269
|
-
for (const file of fs2.readdirSync(digestsDir)) {
|
|
17270
|
-
if (!file.endsWith(".md") || file === "README.md") continue;
|
|
17271
|
-
expected.add(file.replace(/\.md$/, ""));
|
|
17272
|
-
}
|
|
17273
|
-
}
|
|
17274
|
-
return [diffSet(editor, "rules", expected, installed)];
|
|
17275
|
-
}
|
|
17276
|
-
function verifyAntigravityInstall(cwd, packageRoot) {
|
|
17277
|
-
const agentsDir = path2.join(cwd, ".antigravity/agents");
|
|
17278
|
-
const installed = /* @__PURE__ */ new Set();
|
|
17279
|
-
if (fs2.existsSync(agentsDir)) {
|
|
17280
|
-
for (const file of fs2.readdirSync(agentsDir)) {
|
|
17281
|
-
if (!file.startsWith("rihal-") || !file.endsWith(".md")) continue;
|
|
17282
|
-
installed.add(file.replace(/^rihal-/, "").replace(/\.md$/, ""));
|
|
17283
|
-
}
|
|
17284
|
-
}
|
|
17285
|
-
const digestsDir = path2.join(packageRoot, "rihal/digests");
|
|
17286
|
-
const expected = /* @__PURE__ */ new Set();
|
|
17287
|
-
if (fs2.existsSync(digestsDir)) {
|
|
17288
|
-
for (const file of fs2.readdirSync(digestsDir)) {
|
|
17289
|
-
if (!file.endsWith(".md") || file === "README.md") continue;
|
|
17290
|
-
expected.add(file.replace(/\.md$/, ""));
|
|
17291
|
-
}
|
|
17292
|
-
}
|
|
17293
|
-
return [diffSet("antigravity", "agents", expected, installed)];
|
|
17294
|
-
}
|
|
17295
|
-
function verifyInstall(cwd, packageRoot, editors) {
|
|
17296
|
-
const reports = [];
|
|
17297
|
-
if (editors.includes("claude")) {
|
|
17298
|
-
reports.push(...verifyClaudeInstall(cwd, packageRoot));
|
|
17299
|
-
}
|
|
17300
|
-
if (editors.includes("cursor")) {
|
|
17301
|
-
reports.push(...verifyRulesInstall("cursor", cwd, packageRoot));
|
|
17302
|
-
}
|
|
17303
|
-
if (editors.includes("windsurf")) {
|
|
17304
|
-
reports.push(...verifyRulesInstall("windsurf", cwd, packageRoot));
|
|
17305
|
-
}
|
|
17306
|
-
if (editors.includes("antigravity")) {
|
|
17307
|
-
reports.push(...verifyAntigravityInstall(cwd, packageRoot));
|
|
17308
|
-
}
|
|
17309
|
-
const hasDrift = reports.some((r) => r.missing.length > 0 || r.extra.length > 0);
|
|
17310
|
-
return { reports, hasDrift };
|
|
17311
|
-
}
|
|
17312
|
-
function formatReport(reports) {
|
|
17313
|
-
const lines = [];
|
|
17314
|
-
for (const r of reports) {
|
|
17315
|
-
const symbol = r.missing.length === 0 && r.extra.length === 0 ? "\u2713" : "\u26A0";
|
|
17316
|
-
lines.push(
|
|
17317
|
-
` ${symbol} ${r.editor.padEnd(12)} ${r.kind.padEnd(8)} ${r.installedCount}/${r.expectedCount}`
|
|
17318
|
-
);
|
|
17319
|
-
if (r.missing.length > 0) {
|
|
17320
|
-
lines.push(` missing: ${r.missing.join(", ")}`);
|
|
17321
|
-
}
|
|
17322
|
-
if (r.extra.length > 0) {
|
|
17323
|
-
lines.push(` extra: ${r.extra.join(", ")}`);
|
|
17324
|
-
}
|
|
17325
|
-
}
|
|
17326
|
-
return lines.join("\n");
|
|
17327
|
-
}
|
|
17328
|
-
module2.exports = {
|
|
17329
|
-
readPackageManifest,
|
|
17330
|
-
verifyInstall,
|
|
17331
|
-
verifyClaudeInstall,
|
|
17332
|
-
verifyRulesInstall,
|
|
17333
|
-
verifyAntigravityInstall,
|
|
17334
|
-
formatReport
|
|
17335
|
-
};
|
|
17336
|
-
}
|
|
17337
|
-
});
|
|
17338
|
-
|
|
17339
17414
|
// cli/update.js
|
|
17340
17415
|
var require_update = __commonJS({
|
|
17341
17416
|
"cli/update.js"(exports2, module2) {
|
|
@@ -17779,31 +17854,19 @@ var require_uninstall = __commonJS({
|
|
|
17779
17854
|
}
|
|
17780
17855
|
return plan;
|
|
17781
17856
|
}
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
"rihal-frontend-design",
|
|
17796
|
-
"rihal-generate-project-context",
|
|
17797
|
-
"rihal-market-research",
|
|
17798
|
-
"rihal-product-brief",
|
|
17799
|
-
"rihal-qa-generate-e2e-tests",
|
|
17800
|
-
"rihal-retrospective",
|
|
17801
|
-
"rihal-sprint-planning",
|
|
17802
|
-
"rihal-sprint-status",
|
|
17803
|
-
"rihal-technical-research",
|
|
17804
|
-
"rihal-validate-prd",
|
|
17805
|
-
"rihal-clone-website"
|
|
17806
|
-
];
|
|
17857
|
+
function discoverKnownActionSkills() {
|
|
17858
|
+
try {
|
|
17859
|
+
const { readPackageManifest } = require_manifest();
|
|
17860
|
+
const packageRoot = path2.resolve(__dirname, "..");
|
|
17861
|
+
const pkg = readPackageManifest(packageRoot);
|
|
17862
|
+
if (pkg && pkg.actions instanceof Set && pkg.actions.size > 0) {
|
|
17863
|
+
return Array.from(pkg.actions);
|
|
17864
|
+
}
|
|
17865
|
+
} catch {
|
|
17866
|
+
}
|
|
17867
|
+
return [];
|
|
17868
|
+
}
|
|
17869
|
+
var KNOWN_ACTION_SKILLS = discoverKnownActionSkills();
|
|
17807
17870
|
function isKnownSkillName(name) {
|
|
17808
17871
|
return KNOWN_ACTION_SKILLS.includes(name);
|
|
17809
17872
|
}
|
|
@@ -17918,7 +17981,8 @@ var require_uninstall = __commonJS({
|
|
|
17918
17981
|
async function runUninstall(args) {
|
|
17919
17982
|
const opts = parseArgs(args);
|
|
17920
17983
|
const cwd = process.cwd();
|
|
17921
|
-
const
|
|
17984
|
+
const SUPPORTED_EDITORS = ["claude", "cursor", "gemini", "vscode", "antigravity"];
|
|
17985
|
+
const editors = opts.editor ? opts.editor === "all" ? SUPPORTED_EDITORS : [opts.editor] : SUPPORTED_EDITORS;
|
|
17922
17986
|
console.log(`
|
|
17923
17987
|
\u{1F54C} Rihal Code \u2014 Uninstall
|
|
17924
17988
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.26",
|
|
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": {
|