@hanzlaa/rcode 2.4.1 → 2.5.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 +107 -2
- package/cli/uninstall.js +62 -2
- package/dist/rcode.js +112 -5
- package/package.json +1 -1
package/cli/install.js
CHANGED
|
@@ -116,6 +116,7 @@ function parseArgs(argv) {
|
|
|
116
116
|
language: 'English',
|
|
117
117
|
mode: 'guided',
|
|
118
118
|
ide: 'claude', // claude, cursor, gemini (copilot = TODO)
|
|
119
|
+
ideProvided: false, // true when --ide is passed explicitly — skip interactive prompt
|
|
119
120
|
help: false,
|
|
120
121
|
modules: [], // --module core --module execution or empty = all
|
|
121
122
|
// #189 — planning commit policy. null = ask interactively (or default true under --yes).
|
|
@@ -143,7 +144,7 @@ function parseArgs(argv) {
|
|
|
143
144
|
else if (arg === '--project') opts.projectName = argv[++i];
|
|
144
145
|
else if (arg === '--language') opts.language = argv[++i];
|
|
145
146
|
else if (arg === '--mode') opts.mode = argv[++i];
|
|
146
|
-
else if (arg === '--ide') opts.ide = argv[++i];
|
|
147
|
+
else if (arg === '--ide') { opts.ide = argv[++i]; opts.ideProvided = true; }
|
|
147
148
|
else if (arg === '--module') opts.modules.push(argv[++i]);
|
|
148
149
|
else if (arg === '--commit-planning') opts.commitPlanning = true;
|
|
149
150
|
else if (arg === '--no-commit-planning' || arg === '--ignore-planning') opts.commitPlanning = false;
|
|
@@ -163,6 +164,102 @@ function parseArgs(argv) {
|
|
|
163
164
|
return opts;
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Print the Rihal Memory Bank installer header. Box-drawn banner shown once
|
|
169
|
+
* at the top of every interactive install run.
|
|
170
|
+
*/
|
|
171
|
+
function printInstallHeader(targetVersion) {
|
|
172
|
+
const v = targetVersion || readPackageVersion();
|
|
173
|
+
const lines = [
|
|
174
|
+
'',
|
|
175
|
+
pc.cyan('╭───────────────────────────────────────────────────────────╮'),
|
|
176
|
+
pc.cyan('│') + ' ' + pc.cyan('│'),
|
|
177
|
+
pc.cyan('│') + ' ' + pc.bold(pc.yellow('🕌 Rihal Memory Bank')) + ' ' + dim('— installer') + ' ' + pc.cyan('│'),
|
|
178
|
+
pc.cyan('│') + ' ' + dim('A persistent context-brain for your editor') + ' ' + pc.cyan('│'),
|
|
179
|
+
pc.cyan('│') + ' ' + pc.cyan('│'),
|
|
180
|
+
pc.cyan('│') + ' ' + dim('version ') + pc.green('v' + v) + ' ' + pc.cyan('│'),
|
|
181
|
+
pc.cyan('│') + ' ' + dim('docs ') + 'github.com/hanzla-habib/rihal-code ' + pc.cyan('│'),
|
|
182
|
+
pc.cyan('│') + ' ' + pc.cyan('│'),
|
|
183
|
+
pc.cyan('╰───────────────────────────────────────────────────────────╯'),
|
|
184
|
+
'',
|
|
185
|
+
];
|
|
186
|
+
console.log(lines.join('\n'));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Detect which IDEs the user likely uses. Soft signals only — never rejects,
|
|
191
|
+
* just biases the default selection in the interactive prompt.
|
|
192
|
+
* Returns a set like { claude: true, cursor: false, gemini: false }.
|
|
193
|
+
*/
|
|
194
|
+
function detectIdeSignals(target) {
|
|
195
|
+
const signals = { claude: false, cursor: false, gemini: false };
|
|
196
|
+
// 1. Project-local install dirs (strongest signal — they already use one)
|
|
197
|
+
if (fs.existsSync(path.join(target, '.claude'))) signals.claude = true;
|
|
198
|
+
if (fs.existsSync(path.join(target, '.cursor'))) signals.cursor = true;
|
|
199
|
+
if (fs.existsSync(path.join(target, '.gemini'))) signals.gemini = true;
|
|
200
|
+
// 2. User-level config dirs
|
|
201
|
+
const home = os.homedir();
|
|
202
|
+
if (fs.existsSync(path.join(home, '.claude'))) signals.claude = true;
|
|
203
|
+
if (fs.existsSync(path.join(home, '.cursor'))) signals.cursor = true;
|
|
204
|
+
if (fs.existsSync(path.join(home, '.config', 'Cursor'))) signals.cursor = true;
|
|
205
|
+
if (fs.existsSync(path.join(home, '.gemini'))) signals.gemini = true;
|
|
206
|
+
// 3. Env vars commonly set by editor terminals
|
|
207
|
+
if (process.env.CURSOR_TRACE_ID || /cursor/i.test(process.env.TERM_PROGRAM || '')) signals.cursor = true;
|
|
208
|
+
if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_ENTRYPOINT) signals.claude = true;
|
|
209
|
+
return signals;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Resolve target IDE — explicit --ide flag wins, then interactive prompt
|
|
214
|
+
* (when TTY + not --yes + not --ideProvided), else default to 'claude'.
|
|
215
|
+
*
|
|
216
|
+
* Closes the gap where users got auto-installed to claude even when they
|
|
217
|
+
* actually wanted cursor or gemini.
|
|
218
|
+
*/
|
|
219
|
+
async function resolveIde(opts) {
|
|
220
|
+
if (opts.ideProvided) return opts.ide; // user passed --ide, respect it
|
|
221
|
+
if (opts.yes || !process.stdin.isTTY) return opts.ide || 'claude';
|
|
222
|
+
|
|
223
|
+
const signals = detectIdeSignals(opts.target);
|
|
224
|
+
const detected = ['claude', 'cursor', 'gemini'].filter(k => signals[k]);
|
|
225
|
+
|
|
226
|
+
// Build the menu — detected IDEs marked with a hint
|
|
227
|
+
const choices = [
|
|
228
|
+
{ key: '1', value: 'claude', label: 'Claude Code', hint: signals.claude ? dim('(detected)') : '' },
|
|
229
|
+
{ key: '2', value: 'cursor', label: 'Cursor', hint: signals.cursor ? dim('(detected)') : '' },
|
|
230
|
+
{ key: '3', value: 'gemini', label: 'Gemini CLI', hint: signals.gemini ? dim('(detected)') : dim('(beta — limited)') },
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
// Pick a default: prefer the single detected IDE; otherwise claude
|
|
234
|
+
let defaultValue = 'claude';
|
|
235
|
+
if (detected.length === 1) defaultValue = detected[0];
|
|
236
|
+
|
|
237
|
+
const readline = require('readline');
|
|
238
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
239
|
+
const prompt = (q) => new Promise(r => rl.question(q, a => r(a)));
|
|
240
|
+
|
|
241
|
+
console.log(pc.bold('🎯 Which editor will you use Rihal with?'));
|
|
242
|
+
console.log('');
|
|
243
|
+
for (const c of choices) {
|
|
244
|
+
const marker = c.value === defaultValue ? pc.green('●') : dim('○');
|
|
245
|
+
const label = c.value === defaultValue ? pc.bold(c.label) : c.label;
|
|
246
|
+
console.log(` ${marker} ${pc.cyan('[' + c.key + ']')} ${label} ${c.hint}`);
|
|
247
|
+
}
|
|
248
|
+
console.log('');
|
|
249
|
+
const defaultKey = choices.find(c => c.value === defaultValue).key;
|
|
250
|
+
const answer = (await prompt(` Pick an editor [${defaultKey}]: `)).trim().toLowerCase();
|
|
251
|
+
rl.close();
|
|
252
|
+
|
|
253
|
+
if (!answer) return defaultValue;
|
|
254
|
+
// Accept either the number key or the name
|
|
255
|
+
const byKey = choices.find(c => c.key === answer);
|
|
256
|
+
if (byKey) return byKey.value;
|
|
257
|
+
const byName = choices.find(c => c.value === answer || c.label.toLowerCase().startsWith(answer));
|
|
258
|
+
if (byName) return byName.value;
|
|
259
|
+
console.log(dim(` Unrecognised choice "${answer}" — falling back to ${defaultValue}.`));
|
|
260
|
+
return defaultValue;
|
|
261
|
+
}
|
|
262
|
+
|
|
166
263
|
/**
|
|
167
264
|
* Resolve commit-planning preference — CLI flag wins, then interactive
|
|
168
265
|
* prompt (when TTY + not --yes), else GSD-style default: true.
|
|
@@ -1018,10 +1115,18 @@ function convertToCursorMdc(sourceText) {
|
|
|
1018
1115
|
async function install(opts) {
|
|
1019
1116
|
if (opts.help) { printHelp(); return 0; }
|
|
1020
1117
|
|
|
1118
|
+
const pkgVersion = readPackageVersion();
|
|
1119
|
+
|
|
1120
|
+
// Header banner — only shown for interactive runs to keep CI/non-TTY logs terse.
|
|
1121
|
+
const isInteractive = process.stdin.isTTY && !opts.yes;
|
|
1122
|
+
if (isInteractive) printInstallHeader(pkgVersion);
|
|
1123
|
+
|
|
1124
|
+
// Resolve target IDE (interactive prompt unless --ide flag, --yes, or non-TTY).
|
|
1125
|
+
opts.ide = await resolveIde(opts);
|
|
1126
|
+
|
|
1021
1127
|
// Resolve commit-planning preference (interactive prompt or flag) — #189.
|
|
1022
1128
|
opts.commitPlanning = await resolveCommitPlanning(opts);
|
|
1023
1129
|
|
|
1024
|
-
const pkgVersion = readPackageVersion();
|
|
1025
1130
|
console.log(`\n🕌 ${bold('Rihal Code')} ${pc.cyan('v' + pkgVersion)} ${dim('→')} ${opts.target}`);
|
|
1026
1131
|
|
|
1027
1132
|
// Detect an existing install and surface it (#195).
|
package/cli/uninstall.js
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* --editor=claude|cursor|windsurf|antigravity|all Limit scope
|
|
18
18
|
* --keep-state Never touch .rihal/
|
|
19
19
|
* --delete-state Also delete .rihal/ (skip prompt)
|
|
20
|
+
* --purge / --all Wipe everything — editor files,
|
|
21
|
+
* .rihal/, .planning/, gitignore block.
|
|
22
|
+
* Use when you want /rihal:init to
|
|
23
|
+
* report "fresh" on next install.
|
|
20
24
|
* --yes / -y Skip the main confirmation
|
|
21
25
|
*/
|
|
22
26
|
|
|
@@ -32,6 +36,7 @@ function parseArgs(args) {
|
|
|
32
36
|
keepState: false, // if true, never delete .rihal/
|
|
33
37
|
deleteState: false, // if true, delete .rihal/ without prompting
|
|
34
38
|
yes: false, // skip the main confirmation
|
|
39
|
+
purge: false, // wipe everything: editor files + .rihal/ + .planning/ + gitignore block
|
|
35
40
|
};
|
|
36
41
|
for (const arg of args) {
|
|
37
42
|
if (arg.startsWith('--editor=')) {
|
|
@@ -42,6 +47,11 @@ function parseArgs(args) {
|
|
|
42
47
|
opts.deleteState = true;
|
|
43
48
|
} else if (arg === '--yes' || arg === '-y') {
|
|
44
49
|
opts.yes = true;
|
|
50
|
+
} else if (arg === '--purge' || arg === '--all') {
|
|
51
|
+
// --purge implies --delete-state and removes .planning/ + gitignore block.
|
|
52
|
+
// Use this when you want a clean slate so /rihal:init reports "fresh" next time.
|
|
53
|
+
opts.purge = true;
|
|
54
|
+
opts.deleteState = true;
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
return opts;
|
|
@@ -365,9 +375,14 @@ async function runUninstall(args) {
|
|
|
365
375
|
console.log();
|
|
366
376
|
|
|
367
377
|
// Fast path: is Rihal Code installed here at all? Check our own marker
|
|
368
|
-
// (.rihal/config.
|
|
378
|
+
// (.rihal/config.yaml) + any editor install trace. If nothing, exit cleanly
|
|
369
379
|
// with a clear message so users don't wonder "did it work?"
|
|
370
|
-
|
|
380
|
+
// (Was checking config.json — a long-standing typo since the installer
|
|
381
|
+
// writes config.yaml. The check still worked thanks to the editor-files
|
|
382
|
+
// fallback, but a project with .rihal/ and no editor files would falsely
|
|
383
|
+
// report "not installed".)
|
|
384
|
+
const hasConfig = fs.existsSync(path.join(cwd, '.rihal/config.yaml'))
|
|
385
|
+
|| fs.existsSync(path.join(cwd, '.rihal/config.json'));
|
|
371
386
|
const hasAnyEditorFiles =
|
|
372
387
|
fs.existsSync(path.join(cwd, '.claude/skills')) ||
|
|
373
388
|
fs.existsSync(path.join(cwd, '.cursor/rules')) ||
|
|
@@ -565,9 +580,14 @@ async function runUninstall(args) {
|
|
|
565
580
|
if (!opts.deleteState && !opts.keepState && !opts.yes) {
|
|
566
581
|
console.log();
|
|
567
582
|
console.log(`⚠️ The .rihal/ state directory contains your project data:`);
|
|
583
|
+
console.log(` - config.yaml, state.json, RIHLA.md`);
|
|
568
584
|
console.log(` - phases, decisions, progress, artifacts, context`);
|
|
569
585
|
console.log(` - ${plan.stateDir.files} files total`);
|
|
570
586
|
console.log();
|
|
587
|
+
console.log(` If you keep it: /rihal:init will report "already configured"`);
|
|
588
|
+
console.log(` and reuse your existing config + history on next install.`);
|
|
589
|
+
console.log(` If you delete it: next install starts fresh — no carry-over.`);
|
|
590
|
+
console.log();
|
|
571
591
|
shouldDeleteState = await askConfirm(
|
|
572
592
|
`Also delete .rihal/ state? This is destructive and cannot be undone. [y/N] `,
|
|
573
593
|
{ default: 'n' },
|
|
@@ -582,11 +602,51 @@ async function runUninstall(args) {
|
|
|
582
602
|
}
|
|
583
603
|
}
|
|
584
604
|
|
|
605
|
+
// --purge: also wipe .planning/ artifacts and the rcode .gitignore block.
|
|
606
|
+
// Without this, "uninstall + reinstall" carries forward stale phases /
|
|
607
|
+
// sprints / SUMMARY files even after .rihal/ is gone.
|
|
608
|
+
if (opts.purge) {
|
|
609
|
+
const planningDir = path.join(cwd, '.planning');
|
|
610
|
+
if (fs.existsSync(planningDir)) {
|
|
611
|
+
fs.rmSync(planningDir, { recursive: true, force: true });
|
|
612
|
+
console.log(` ✓ removed .planning/ (--purge)`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Strip the rcode-managed block from .gitignore. The installer writes
|
|
616
|
+
// a fenced block; we remove it cleanly without touching user lines.
|
|
617
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
618
|
+
if (fs.existsSync(gitignorePath)) {
|
|
619
|
+
try {
|
|
620
|
+
const before = fs.readFileSync(gitignorePath, 'utf8');
|
|
621
|
+
// Match either fenced markers or the legacy "# rcode" header through to
|
|
622
|
+
// the next blank line — both shapes the installer has used historically.
|
|
623
|
+
const stripped = before
|
|
624
|
+
.replace(/\n?# >>> rihal-code >>>[\s\S]*?# <<< rihal-code <<<\n?/g, '\n')
|
|
625
|
+
.replace(/\n?# rcode[\s\S]*?(?=\n\n|\n$|$)/g, '\n')
|
|
626
|
+
.replace(/\n{3,}/g, '\n\n');
|
|
627
|
+
if (stripped !== before) {
|
|
628
|
+
fs.writeFileSync(gitignorePath, stripped);
|
|
629
|
+
console.log(` ✓ stripped rcode block from .gitignore (--purge)`);
|
|
630
|
+
}
|
|
631
|
+
} catch (err) {
|
|
632
|
+
console.log(` ⚠ could not strip .gitignore block: ${err.message}`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
585
637
|
console.log(`\n✅ Uninstall complete. Removed ${removed} files.`);
|
|
586
638
|
if (backup.ok) {
|
|
587
639
|
console.log(` Backup: ${backup.path} (restore with: tar -xzf ${backup.path})`);
|
|
588
640
|
}
|
|
589
641
|
|
|
642
|
+
// Hint about the purge flag if the user kept state — closes the user's
|
|
643
|
+
// most common confusion: "I uninstalled but /rihal:init still says configured."
|
|
644
|
+
if (plan.stateDir && fs.existsSync(path.join(cwd, '.rihal'))) {
|
|
645
|
+
console.log();
|
|
646
|
+
console.log(`ℹ .rihal/ state was preserved. /rihal:init will detect this on reinstall.`);
|
|
647
|
+
console.log(` For a fully clean slate next time, use: rcode uninstall --purge`);
|
|
648
|
+
}
|
|
649
|
+
|
|
590
650
|
// Hint about reinstalling
|
|
591
651
|
console.log(`\nTo reinstall later:`);
|
|
592
652
|
console.log(` rcode install`);
|
package/dist/rcode.js
CHANGED
|
@@ -15830,6 +15830,8 @@ var require_install = __commonJS({
|
|
|
15830
15830
|
mode: "guided",
|
|
15831
15831
|
ide: "claude",
|
|
15832
15832
|
// claude, cursor, gemini (copilot = TODO)
|
|
15833
|
+
ideProvided: false,
|
|
15834
|
+
// true when --ide is passed explicitly — skip interactive prompt
|
|
15833
15835
|
help: false,
|
|
15834
15836
|
modules: [],
|
|
15835
15837
|
// --module core --module execution or empty = all
|
|
@@ -15858,8 +15860,10 @@ var require_install = __commonJS({
|
|
|
15858
15860
|
else if (arg === "--project") opts.projectName = argv[++i];
|
|
15859
15861
|
else if (arg === "--language") opts.language = argv[++i];
|
|
15860
15862
|
else if (arg === "--mode") opts.mode = argv[++i];
|
|
15861
|
-
else if (arg === "--ide")
|
|
15862
|
-
|
|
15863
|
+
else if (arg === "--ide") {
|
|
15864
|
+
opts.ide = argv[++i];
|
|
15865
|
+
opts.ideProvided = true;
|
|
15866
|
+
} else if (arg === "--module") opts.modules.push(argv[++i]);
|
|
15863
15867
|
else if (arg === "--commit-planning") opts.commitPlanning = true;
|
|
15864
15868
|
else if (arg === "--no-commit-planning" || arg === "--ignore-planning") opts.commitPlanning = false;
|
|
15865
15869
|
else if (arg === "--non-destructive") opts.nonDestructive = true;
|
|
@@ -15877,6 +15881,71 @@ var require_install = __commonJS({
|
|
|
15877
15881
|
if (!opts.projectName) opts.projectName = path2.basename(opts.target);
|
|
15878
15882
|
return opts;
|
|
15879
15883
|
}
|
|
15884
|
+
function printInstallHeader(targetVersion) {
|
|
15885
|
+
const v = targetVersion || readPackageVersion();
|
|
15886
|
+
const lines = [
|
|
15887
|
+
"",
|
|
15888
|
+
pc.cyan("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"),
|
|
15889
|
+
pc.cyan("\u2502") + " " + pc.cyan("\u2502"),
|
|
15890
|
+
pc.cyan("\u2502") + " " + pc.bold(pc.yellow("\u{1F54C} Rihal Memory Bank")) + " " + dim("\u2014 installer") + " " + pc.cyan("\u2502"),
|
|
15891
|
+
pc.cyan("\u2502") + " " + dim("A persistent context-brain for your editor") + " " + pc.cyan("\u2502"),
|
|
15892
|
+
pc.cyan("\u2502") + " " + pc.cyan("\u2502"),
|
|
15893
|
+
pc.cyan("\u2502") + " " + dim("version ") + pc.green("v" + v) + " " + pc.cyan("\u2502"),
|
|
15894
|
+
pc.cyan("\u2502") + " " + dim("docs ") + "github.com/hanzla-habib/rihal-code " + pc.cyan("\u2502"),
|
|
15895
|
+
pc.cyan("\u2502") + " " + pc.cyan("\u2502"),
|
|
15896
|
+
pc.cyan("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"),
|
|
15897
|
+
""
|
|
15898
|
+
];
|
|
15899
|
+
console.log(lines.join("\n"));
|
|
15900
|
+
}
|
|
15901
|
+
function detectIdeSignals(target) {
|
|
15902
|
+
const signals = { claude: false, cursor: false, gemini: false };
|
|
15903
|
+
if (fs2.existsSync(path2.join(target, ".claude"))) signals.claude = true;
|
|
15904
|
+
if (fs2.existsSync(path2.join(target, ".cursor"))) signals.cursor = true;
|
|
15905
|
+
if (fs2.existsSync(path2.join(target, ".gemini"))) signals.gemini = true;
|
|
15906
|
+
const home = os.homedir();
|
|
15907
|
+
if (fs2.existsSync(path2.join(home, ".claude"))) signals.claude = true;
|
|
15908
|
+
if (fs2.existsSync(path2.join(home, ".cursor"))) signals.cursor = true;
|
|
15909
|
+
if (fs2.existsSync(path2.join(home, ".config", "Cursor"))) signals.cursor = true;
|
|
15910
|
+
if (fs2.existsSync(path2.join(home, ".gemini"))) signals.gemini = true;
|
|
15911
|
+
if (process.env.CURSOR_TRACE_ID || /cursor/i.test(process.env.TERM_PROGRAM || "")) signals.cursor = true;
|
|
15912
|
+
if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT) signals.claude = true;
|
|
15913
|
+
return signals;
|
|
15914
|
+
}
|
|
15915
|
+
async function resolveIde(opts) {
|
|
15916
|
+
if (opts.ideProvided) return opts.ide;
|
|
15917
|
+
if (opts.yes || !process.stdin.isTTY) return opts.ide || "claude";
|
|
15918
|
+
const signals = detectIdeSignals(opts.target);
|
|
15919
|
+
const detected = ["claude", "cursor", "gemini"].filter((k) => signals[k]);
|
|
15920
|
+
const choices = [
|
|
15921
|
+
{ key: "1", value: "claude", label: "Claude Code", hint: signals.claude ? dim("(detected)") : "" },
|
|
15922
|
+
{ key: "2", value: "cursor", label: "Cursor", hint: signals.cursor ? dim("(detected)") : "" },
|
|
15923
|
+
{ key: "3", value: "gemini", label: "Gemini CLI", hint: signals.gemini ? dim("(detected)") : dim("(beta \u2014 limited)") }
|
|
15924
|
+
];
|
|
15925
|
+
let defaultValue = "claude";
|
|
15926
|
+
if (detected.length === 1) defaultValue = detected[0];
|
|
15927
|
+
const readline = require("readline");
|
|
15928
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
15929
|
+
const prompt = (q) => new Promise((r) => rl.question(q, (a) => r(a)));
|
|
15930
|
+
console.log(pc.bold("\u{1F3AF} Which editor will you use Rihal with?"));
|
|
15931
|
+
console.log("");
|
|
15932
|
+
for (const c of choices) {
|
|
15933
|
+
const marker = c.value === defaultValue ? pc.green("\u25CF") : dim("\u25CB");
|
|
15934
|
+
const label = c.value === defaultValue ? pc.bold(c.label) : c.label;
|
|
15935
|
+
console.log(` ${marker} ${pc.cyan("[" + c.key + "]")} ${label} ${c.hint}`);
|
|
15936
|
+
}
|
|
15937
|
+
console.log("");
|
|
15938
|
+
const defaultKey = choices.find((c) => c.value === defaultValue).key;
|
|
15939
|
+
const answer = (await prompt(` Pick an editor [${defaultKey}]: `)).trim().toLowerCase();
|
|
15940
|
+
rl.close();
|
|
15941
|
+
if (!answer) return defaultValue;
|
|
15942
|
+
const byKey = choices.find((c) => c.key === answer);
|
|
15943
|
+
if (byKey) return byKey.value;
|
|
15944
|
+
const byName = choices.find((c) => c.value === answer || c.label.toLowerCase().startsWith(answer));
|
|
15945
|
+
if (byName) return byName.value;
|
|
15946
|
+
console.log(dim(` Unrecognised choice "${answer}" \u2014 falling back to ${defaultValue}.`));
|
|
15947
|
+
return defaultValue;
|
|
15948
|
+
}
|
|
15880
15949
|
async function resolveCommitPlanning(opts) {
|
|
15881
15950
|
if (opts.commitPlanning !== null) return opts.commitPlanning;
|
|
15882
15951
|
if (opts.yes || !process.stdin.isTTY) return true;
|
|
@@ -16555,8 +16624,11 @@ Say "plan a sprint" or run \`/rihal:sprint-planning\` to break Phase 01 into sto
|
|
|
16555
16624
|
printHelp2();
|
|
16556
16625
|
return 0;
|
|
16557
16626
|
}
|
|
16558
|
-
opts.commitPlanning = await resolveCommitPlanning(opts);
|
|
16559
16627
|
const pkgVersion = readPackageVersion();
|
|
16628
|
+
const isInteractive = process.stdin.isTTY && !opts.yes;
|
|
16629
|
+
if (isInteractive) printInstallHeader(pkgVersion);
|
|
16630
|
+
opts.ide = await resolveIde(opts);
|
|
16631
|
+
opts.commitPlanning = await resolveCommitPlanning(opts);
|
|
16560
16632
|
console.log(`
|
|
16561
16633
|
\u{1F54C} ${bold("Rihal Code")} ${pc.cyan("v" + pkgVersion)} ${dim("\u2192")} ${opts.target}`);
|
|
16562
16634
|
const existingManifestPath = path2.join(opts.target, ".rihal", "_config", "manifest.yaml");
|
|
@@ -17793,8 +17865,10 @@ var require_uninstall = __commonJS({
|
|
|
17793
17865
|
// if true, never delete .rihal/
|
|
17794
17866
|
deleteState: false,
|
|
17795
17867
|
// if true, delete .rihal/ without prompting
|
|
17796
|
-
yes: false
|
|
17868
|
+
yes: false,
|
|
17797
17869
|
// skip the main confirmation
|
|
17870
|
+
purge: false
|
|
17871
|
+
// wipe everything: editor files + .rihal/ + .planning/ + gitignore block
|
|
17798
17872
|
};
|
|
17799
17873
|
for (const arg of args) {
|
|
17800
17874
|
if (arg.startsWith("--editor=")) {
|
|
@@ -17805,6 +17879,9 @@ var require_uninstall = __commonJS({
|
|
|
17805
17879
|
opts.deleteState = true;
|
|
17806
17880
|
} else if (arg === "--yes" || arg === "-y") {
|
|
17807
17881
|
opts.yes = true;
|
|
17882
|
+
} else if (arg === "--purge" || arg === "--all") {
|
|
17883
|
+
opts.purge = true;
|
|
17884
|
+
opts.deleteState = true;
|
|
17808
17885
|
}
|
|
17809
17886
|
}
|
|
17810
17887
|
return opts;
|
|
@@ -18037,7 +18114,7 @@ var require_uninstall = __commonJS({
|
|
|
18037
18114
|
console.log(` Project: ${cwd}`);
|
|
18038
18115
|
console.log(` Scope: ${editors.join(", ")}`);
|
|
18039
18116
|
console.log();
|
|
18040
|
-
const hasConfig = fs2.existsSync(path2.join(cwd, ".rihal/config.json"));
|
|
18117
|
+
const hasConfig = fs2.existsSync(path2.join(cwd, ".rihal/config.yaml")) || fs2.existsSync(path2.join(cwd, ".rihal/config.json"));
|
|
18041
18118
|
const hasAnyEditorFiles = fs2.existsSync(path2.join(cwd, ".claude/skills")) || fs2.existsSync(path2.join(cwd, ".cursor/rules")) || fs2.existsSync(path2.join(cwd, ".windsurf/rules")) || fs2.existsSync(path2.join(cwd, ".antigravity/agents"));
|
|
18042
18119
|
if (!hasConfig && !hasAnyEditorFiles) {
|
|
18043
18120
|
console.log(`
|
|
@@ -18202,9 +18279,14 @@ var require_uninstall = __commonJS({
|
|
|
18202
18279
|
if (!opts.deleteState && !opts.keepState && !opts.yes) {
|
|
18203
18280
|
console.log();
|
|
18204
18281
|
console.log(`\u26A0\uFE0F The .rihal/ state directory contains your project data:`);
|
|
18282
|
+
console.log(` - config.yaml, state.json, RIHLA.md`);
|
|
18205
18283
|
console.log(` - phases, decisions, progress, artifacts, context`);
|
|
18206
18284
|
console.log(` - ${plan.stateDir.files} files total`);
|
|
18207
18285
|
console.log();
|
|
18286
|
+
console.log(` If you keep it: /rihal:init will report "already configured"`);
|
|
18287
|
+
console.log(` and reuse your existing config + history on next install.`);
|
|
18288
|
+
console.log(` If you delete it: next install starts fresh \u2014 no carry-over.`);
|
|
18289
|
+
console.log();
|
|
18208
18290
|
shouldDeleteState = await askConfirm(
|
|
18209
18291
|
`Also delete .rihal/ state? This is destructive and cannot be undone. [y/N] `,
|
|
18210
18292
|
{ default: "n" }
|
|
@@ -18217,11 +18299,36 @@ var require_uninstall = __commonJS({
|
|
|
18217
18299
|
console.log(` \u2139 kept .rihal/ state directory (your project data is preserved)`);
|
|
18218
18300
|
}
|
|
18219
18301
|
}
|
|
18302
|
+
if (opts.purge) {
|
|
18303
|
+
const planningDir = path2.join(cwd, ".planning");
|
|
18304
|
+
if (fs2.existsSync(planningDir)) {
|
|
18305
|
+
fs2.rmSync(planningDir, { recursive: true, force: true });
|
|
18306
|
+
console.log(` \u2713 removed .planning/ (--purge)`);
|
|
18307
|
+
}
|
|
18308
|
+
const gitignorePath = path2.join(cwd, ".gitignore");
|
|
18309
|
+
if (fs2.existsSync(gitignorePath)) {
|
|
18310
|
+
try {
|
|
18311
|
+
const before = fs2.readFileSync(gitignorePath, "utf8");
|
|
18312
|
+
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");
|
|
18313
|
+
if (stripped !== before) {
|
|
18314
|
+
fs2.writeFileSync(gitignorePath, stripped);
|
|
18315
|
+
console.log(` \u2713 stripped rcode block from .gitignore (--purge)`);
|
|
18316
|
+
}
|
|
18317
|
+
} catch (err) {
|
|
18318
|
+
console.log(` \u26A0 could not strip .gitignore block: ${err.message}`);
|
|
18319
|
+
}
|
|
18320
|
+
}
|
|
18321
|
+
}
|
|
18220
18322
|
console.log(`
|
|
18221
18323
|
\u2705 Uninstall complete. Removed ${removed} files.`);
|
|
18222
18324
|
if (backup.ok) {
|
|
18223
18325
|
console.log(` Backup: ${backup.path} (restore with: tar -xzf ${backup.path})`);
|
|
18224
18326
|
}
|
|
18327
|
+
if (plan.stateDir && fs2.existsSync(path2.join(cwd, ".rihal"))) {
|
|
18328
|
+
console.log();
|
|
18329
|
+
console.log(`\u2139 .rihal/ state was preserved. /rihal:init will detect this on reinstall.`);
|
|
18330
|
+
console.log(` For a fully clean slate next time, use: rcode uninstall --purge`);
|
|
18331
|
+
}
|
|
18225
18332
|
console.log(`
|
|
18226
18333
|
To reinstall later:`);
|
|
18227
18334
|
console.log(` rcode install`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.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": {
|