skalpel 3.0.9 → 3.0.10
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/INSTALL.md +18 -3
- package/npm-bin/skalpel.js +92 -7
- package/package.json +6 -7
package/INSTALL.md
CHANGED
|
@@ -71,11 +71,26 @@ A user who has rolled back to a previous version (by running `npm install -g ska
|
|
|
71
71
|
|
|
72
72
|
## Uninstall
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
```
|
|
75
|
+
npx skalpel uninstall
|
|
76
|
+
```
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
is the canonical one-liner for a clean, full removal on every supported OS. It works whether or not skalpel was previously installed globally:
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
1. Unregisters the per-OS service entry (launchd on macOS, systemd user unit on Linux, Task Scheduler entry on Windows).
|
|
81
|
+
2. Removes the managed shell-rc block injected on install.
|
|
82
|
+
3. Deletes user state under the per-OS configuration directory: `auth.json`, `config.toml`, `skalpeld.lock`, `logs/`, and `cache/`.
|
|
83
|
+
4. Auto-detects a global npm install (via `npm prefix -g`) and removes it via `npm uninstall -g skalpel`, so the `skalpel` binary leaves your `PATH` in the same command.
|
|
84
|
+
|
|
85
|
+
Flags: `--keep-data` preserves user-data files (engine toggles, cached auth) for a future reinstall; `--keep-package` preserves the global npm package (only the state cleanup runs); `--dry-run` previews every action without writing anything.
|
|
86
|
+
|
|
87
|
+
`skalpel uninstall` from a global install behaves identically — same flags, same auto-removal of the npm package. Pick whichever is convenient: `npx skalpel uninstall` requires no prior install; `skalpel uninstall` is faster on a machine that already has the binary on `PATH`.
|
|
88
|
+
|
|
89
|
+
### Why `npm uninstall -g skalpel` is not the documented path
|
|
90
|
+
|
|
91
|
+
npm 7+ removed the `preuninstall`/`uninstall`/`postuninstall` lifecycle scripts; the `npm uninstall` command runs Arborist's `reify` which simply deletes files from the global `node_modules` and returns. There is no hook left for skalpel to clean up the rc-file managed block, the service entry, or user data on `npm uninstall -g skalpel` alone. Running `npm uninstall -g skalpel` without first running `skalpel uninstall` (or `npx skalpel uninstall`) leaves orphaned shell-rc env vars pointing at a dead proxy port and an orphaned launchd/systemd/Task Scheduler entry trying to start a binary that no longer exists. The `npx skalpel uninstall` one-liner above does both the state cleanup and the package removal in a single command — that's why it's the documented path.
|
|
92
|
+
|
|
93
|
+
The configuration directory itself (auth.json + config.toml + cache + logs) is deliberately wiped by the default uninstall so a reinstalled skalpel starts from a known state. Users who want to reinstall and keep their auth + engine toggles should pass `--keep-data`.
|
|
79
94
|
|
|
80
95
|
## Version coupling
|
|
81
96
|
|
package/npm-bin/skalpel.js
CHANGED
|
@@ -189,6 +189,7 @@ function resolveBinary(name, argv) {
|
|
|
189
189
|
// only place this command can live.
|
|
190
190
|
function runUninstall(rest) {
|
|
191
191
|
let cleanupData = true;
|
|
192
|
+
let removePackage = true;
|
|
192
193
|
let dryRun = false;
|
|
193
194
|
let showHelp = false;
|
|
194
195
|
for (const a of rest) {
|
|
@@ -196,6 +197,9 @@ function runUninstall(rest) {
|
|
|
196
197
|
case '--keep-data':
|
|
197
198
|
cleanupData = false;
|
|
198
199
|
break;
|
|
200
|
+
case '--keep-package':
|
|
201
|
+
removePackage = false;
|
|
202
|
+
break;
|
|
199
203
|
case '--dry-run':
|
|
200
204
|
dryRun = true;
|
|
201
205
|
break;
|
|
@@ -212,14 +216,16 @@ function runUninstall(rest) {
|
|
|
212
216
|
const head = colored ? theme.bold(theme.lilac('skalpel uninstall')) : 'skalpel uninstall';
|
|
213
217
|
const dim = colored ? theme.dim : (s) => s;
|
|
214
218
|
process.stdout.write(
|
|
215
|
-
`${head} —
|
|
216
|
-
`usage: skalpel uninstall [--keep-data] [--dry-run]\n\n` +
|
|
219
|
+
`${head} — fully remove skalpel from this machine\n\n` +
|
|
220
|
+
`usage: skalpel uninstall [--keep-data] [--keep-package] [--dry-run]\n\n` +
|
|
217
221
|
`By default, removes:\n` +
|
|
218
222
|
` • per-OS service entry (launchd / systemd user / Task Scheduler)\n` +
|
|
219
223
|
` • managed shell-rc block injected on install\n` +
|
|
220
|
-
` • auth.json, config.toml, skalpeld.lock, and
|
|
221
|
-
|
|
222
|
-
|
|
224
|
+
` • auth.json, config.toml, skalpeld.lock, logs/ and cache/ (use --keep-data to preserve)\n` +
|
|
225
|
+
` • the global npm package itself (use --keep-package to preserve the binary)\n\n` +
|
|
226
|
+
`${dim('Equivalent invocations (both do the same thing):')}\n` +
|
|
227
|
+
` skalpel uninstall ${dim('(from a global install)')}\n` +
|
|
228
|
+
` npx skalpel uninstall ${dim('(no global install needed)')}\n`
|
|
223
229
|
);
|
|
224
230
|
return 0;
|
|
225
231
|
}
|
|
@@ -316,11 +322,90 @@ function runUninstall(rest) {
|
|
|
316
322
|
);
|
|
317
323
|
}
|
|
318
324
|
|
|
319
|
-
|
|
320
|
-
|
|
325
|
+
// True-uninstall: auto-detect a global npm install of `skalpel` and
|
|
326
|
+
// remove it via `npm uninstall -g skalpel`. This makes
|
|
327
|
+
// `npx skalpel uninstall` (or `skalpel uninstall` from a global
|
|
328
|
+
// install) a single one-stop true uninstall on every OS.
|
|
329
|
+
//
|
|
330
|
+
// Safety notes:
|
|
331
|
+
// - When invoked from a global install (not via npx), we're about
|
|
332
|
+
// to delete the files Node loaded our shim from. Node read the
|
|
333
|
+
// script and closed the handle, so deletion is safe cross-OS.
|
|
334
|
+
// - When invoked from npx, the global install (if any) is at a
|
|
335
|
+
// different prefix than the npx cache, so no self-deletion.
|
|
336
|
+
// - We skip when --keep-package is passed or when the postinstall
|
|
337
|
+
// summary indicates nothing was removed and no global install
|
|
338
|
+
// exists (i.e. user has nothing to uninstall).
|
|
339
|
+
let globalRemoved = false;
|
|
340
|
+
if (removePackage && !dryRun) {
|
|
341
|
+
const detected = detectGlobalInstall();
|
|
342
|
+
if (detected) {
|
|
343
|
+
const r = removeGlobalInstall();
|
|
344
|
+
if (r.removed) {
|
|
345
|
+
globalRemoved = true;
|
|
346
|
+
process.stdout.write(`${ok} Removed global npm package skalpel (${detected.pkgRoot}).\n`);
|
|
347
|
+
} else {
|
|
348
|
+
process.stdout.write(
|
|
349
|
+
`\n${dim('Could not auto-remove the global skalpel package ')}` +
|
|
350
|
+
`${dim('(' + r.reason + ').')}\n` +
|
|
351
|
+
`Run manually:\n npm uninstall -g skalpel\n` +
|
|
352
|
+
dim(' (sudo may be required on some setups)\n')
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} else if (removePackage && dryRun) {
|
|
357
|
+
const detected = detectGlobalInstall();
|
|
358
|
+
if (detected) {
|
|
359
|
+
process.stdout.write(`${dim('[dry-run] would run: npm uninstall -g skalpel')}\n`);
|
|
360
|
+
process.stdout.write(`${dim(' (' + detected.pkgRoot + ')')}\n`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (globalRemoved || (summary && (summary.rcBlocksRemoved || summary.serviceFileRemoved || summary.userDataFilesRemoved))) {
|
|
365
|
+
process.stdout.write(`\n${ok} skalpel fully removed from this machine.\n`);
|
|
366
|
+
}
|
|
321
367
|
return 0;
|
|
322
368
|
}
|
|
323
369
|
|
|
370
|
+
// detectGlobalInstall returns { prefix, pkgRoot } if a global npm
|
|
371
|
+
// install of `skalpel` exists, or null. Uses `npm prefix -g` which is
|
|
372
|
+
// the cross-OS canonical way to find the global root. We don't trust
|
|
373
|
+
// `which skalpel` because PATH ordering can hide a global install
|
|
374
|
+
// behind another shim.
|
|
375
|
+
function detectGlobalInstall() {
|
|
376
|
+
const r = spawnSync('npm', ['prefix', '-g'], {
|
|
377
|
+
encoding: 'utf8',
|
|
378
|
+
shell: process.platform === 'win32',
|
|
379
|
+
});
|
|
380
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
381
|
+
const prefix = r.stdout.trim();
|
|
382
|
+
if (!prefix) return null;
|
|
383
|
+
// On Unix, `npm prefix -g` returns e.g. /usr/local; the package is
|
|
384
|
+
// at lib/node_modules/skalpel. On Windows, prefix is the AppData
|
|
385
|
+
// dir itself; package is at node_modules/skalpel.
|
|
386
|
+
const candidate = process.platform === 'win32'
|
|
387
|
+
? path.join(prefix, 'node_modules', 'skalpel')
|
|
388
|
+
: path.join(prefix, 'lib', 'node_modules', 'skalpel');
|
|
389
|
+
// Avoid false positive when we ARE the global install we just
|
|
390
|
+
// checked (npx cache lives elsewhere, so this is fine; only matters
|
|
391
|
+
// if some non-npx, non-global path resolves to the same dir).
|
|
392
|
+
if (!fs.existsSync(candidate)) return null;
|
|
393
|
+
return { prefix, pkgRoot: candidate };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// removeGlobalInstall shells out to `npm uninstall -g skalpel`. Uses
|
|
397
|
+
// shell: true on Windows so the .cmd shim resolves; harmless on
|
|
398
|
+
// Unix. Returns { removed: bool, reason: string }.
|
|
399
|
+
function removeGlobalInstall() {
|
|
400
|
+
const r = spawnSync('npm', ['uninstall', '-g', 'skalpel'], {
|
|
401
|
+
stdio: 'inherit',
|
|
402
|
+
shell: process.platform === 'win32',
|
|
403
|
+
});
|
|
404
|
+
if (r.error) return { removed: false, reason: r.error.message };
|
|
405
|
+
if (r.status !== 0) return { removed: false, reason: `npm exited ${r.status}` };
|
|
406
|
+
return { removed: true };
|
|
407
|
+
}
|
|
408
|
+
|
|
324
409
|
if (require.main === module) {
|
|
325
410
|
const argv = process.argv.slice(2);
|
|
326
411
|
if (argv[0] === 'uninstall') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skalpel",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.10",
|
|
4
4
|
"description": "Skalpel — local proxy and TUI for coding agents (skalpel + skalpeld bundle).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://skalpel.ai",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
31
|
"postinstall": "node postinstall/index.js",
|
|
32
|
-
"preuninstall": "node postinstall/index.js --uninstall",
|
|
33
32
|
"test": "echo 'no top-level tests; run make test or npm run test:rc-edit' && exit 0",
|
|
34
33
|
"test:rc-edit": "node postinstall/lib/rc-edit.test.js"
|
|
35
34
|
},
|
|
@@ -54,10 +53,10 @@
|
|
|
54
53
|
"x64"
|
|
55
54
|
],
|
|
56
55
|
"optionalDependencies": {
|
|
57
|
-
"@skalpelai/skalpel-darwin-arm64": "3.0.
|
|
58
|
-
"@skalpelai/skalpel-darwin-x64": "3.0.
|
|
59
|
-
"@skalpelai/skalpel-linux-arm64": "3.0.
|
|
60
|
-
"@skalpelai/skalpel-linux-x64": "3.0.
|
|
61
|
-
"@skalpelai/skalpel-win32-x64": "3.0.
|
|
56
|
+
"@skalpelai/skalpel-darwin-arm64": "3.0.10",
|
|
57
|
+
"@skalpelai/skalpel-darwin-x64": "3.0.10",
|
|
58
|
+
"@skalpelai/skalpel-linux-arm64": "3.0.10",
|
|
59
|
+
"@skalpelai/skalpel-linux-x64": "3.0.10",
|
|
60
|
+
"@skalpelai/skalpel-win32-x64": "3.0.10"
|
|
62
61
|
}
|
|
63
62
|
}
|