claude-slim 2.11.0 → 2.12.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/README.md +5 -8
- package/dist/cli.js +104 -2
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +16 -0
- package/dist/scanner/index.d.ts +9 -0
- package/dist/scanner/index.js +1 -1
- package/dist/update-run.d.ts +40 -0
- package/dist/update-run.js +101 -0
- package/package.json +1 -1
- package/skills/claude-slim/SKILL.md +8 -8
package/README.md
CHANGED
|
@@ -165,6 +165,7 @@ npx claude-slim scan --no-codex # Skip the ~/.codex scan
|
|
|
165
165
|
npx claude-slim doctor # Diagnose Node/Claude/session-log readiness
|
|
166
166
|
npx claude-slim doctor --offline # Same, without the version check (no network)
|
|
167
167
|
npx claude-slim check-update # Report-only version check
|
|
168
|
+
npx claude-slim update # Run the upgrade for this install method
|
|
168
169
|
npx claude-slim restore # Undo
|
|
169
170
|
npx claude-slim report # Show savings from last clean
|
|
170
171
|
```
|
|
@@ -247,16 +248,12 @@ Token counts come from [js-tiktoken](https://github.com/nicolo-ribaudo/js-tiktok
|
|
|
247
248
|
|
|
248
249
|
---
|
|
249
250
|
|
|
250
|
-
## v2.
|
|
251
|
+
## v2.12.1 — What's new
|
|
251
252
|
|
|
252
|
-
|
|
253
|
+
- **Fixed: the `/claude-slim` skill reported project memory as zero.** It invoked the CLI with `cd "${CLAUDE_PLUGIN_ROOT}"`, making `cwd` the plugin cache directory; the project slug resolved there, matched nothing, and every project-memory token silently left the startup total — **108,570** on the machine where this surfaced. `SKILL.md` no longer `cd`s. This hit the tool's primary entry point, and quietly: a zero reads as a clean result.
|
|
254
|
+
- **`--project-dir <path>`** for callers that cannot run from the project directory, plus a warning when the CLI notices it is running from its own install without one.
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
- **`~/.codex/skills.disabled/`** — moves are reversible through the same `restore`, and manifest entries record which agent they came from.
|
|
256
|
-
- **The path guard is now per-agent.** Not "inside any known root" — a Codex issue must not resolve into `~/.claude/` and vice versa, so a tampered manifest cannot cross between agents.
|
|
257
|
-
- `unused_skill` and `oversized_memory` remain unavailable for Codex: no invocation record, and no `~/.codex/projects/*/memory/`.
|
|
258
|
-
|
|
259
|
-
Tests: 347 → 374 (+27), the guard suite being the point — cross-agent isolation both directions, traversal escapes, and a `~/.claude-backup` sibling a naive `startsWith` would have allowed.
|
|
256
|
+
Tests: 392 → 401 (+9).
|
|
260
257
|
|
|
261
258
|
For older release notes, see [CHANGELOG.md](CHANGELOG.md).
|
|
262
259
|
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,9 @@ import { cleanIssues, restoreItem } from './cleaner.js';
|
|
|
10
10
|
import { readManifest } from './manifest.js';
|
|
11
11
|
import { formatScanSummary, formatReportBox, calculateReport, } from './report.js';
|
|
12
12
|
import { collectDoctorReport, formatDoctorReport } from './doctor.js';
|
|
13
|
+
import { looksLikeToolInstallDir } from './paths.js';
|
|
13
14
|
import { checkForUpdate, formatUpdateNotice } from './update-check.js';
|
|
15
|
+
import { confirmDecision, planUpdate, renderStep, runUpdate } from './update-run.js';
|
|
14
16
|
import { scanCodex } from './codex/index.js';
|
|
15
17
|
import { formatCodexSummary } from './codex/report.js';
|
|
16
18
|
import { classifyCodexIssues } from './codex/detectors.js';
|
|
@@ -39,10 +41,15 @@ program
|
|
|
39
41
|
.description('Scan environment and report issues')
|
|
40
42
|
.option('--json', 'Output raw JSON')
|
|
41
43
|
.option('--lookback-days <n>', 'Days of session history for skill-usage analysis', '60')
|
|
44
|
+
.option('--project-dir <path>', 'Directory whose project memory counts toward the total (default: cwd)')
|
|
42
45
|
.option('--no-codex', 'Skip the ~/.codex scan even if Codex is installed')
|
|
43
46
|
.action(async (opts) => {
|
|
44
47
|
await initTokenizer();
|
|
45
|
-
const
|
|
48
|
+
const projectDir = resolveProjectDir(opts.projectDir);
|
|
49
|
+
const result = await scan({
|
|
50
|
+
lookbackDays: parseNonNegativeInt(opts.lookbackDays, 60),
|
|
51
|
+
projectDir,
|
|
52
|
+
});
|
|
46
53
|
// Codex is scanned when present. Reported only — it is never modified, and
|
|
47
54
|
// unused-skill detection is suppressed there for lack of a usage signal.
|
|
48
55
|
// commander maps `--no-codex` to `opts.codex === false`, not `opts.noCodex`.
|
|
@@ -101,6 +108,81 @@ program
|
|
|
101
108
|
console.log(`\n \x1b[32m✓\x1b[0m claude-slim ${result.installed} is up to date.\n`);
|
|
102
109
|
}
|
|
103
110
|
});
|
|
111
|
+
// --- update ---
|
|
112
|
+
// Detection lives in `check-update`; this runs the command that detection
|
|
113
|
+
// identified. claude-slim still never writes into a package manager's
|
|
114
|
+
// directories itself — it invokes the manager, the same way cleanup already
|
|
115
|
+
// invokes `claude plugin disable`.
|
|
116
|
+
program
|
|
117
|
+
.command('update')
|
|
118
|
+
.description('Run the upgrade command for however this copy was installed')
|
|
119
|
+
.option('--yes', 'Skip the confirmation prompt')
|
|
120
|
+
.option('--dry-run', 'Show the commands without running them')
|
|
121
|
+
.action(async (opts) => {
|
|
122
|
+
const result = await checkForUpdate({ force: true });
|
|
123
|
+
const plan = planUpdate(result.installMethod);
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(` Installed: ${result.installed} Latest: ${result.latest ?? 'unknown'}`);
|
|
126
|
+
console.log(` Install method: ${result.installMethod}`);
|
|
127
|
+
console.log('');
|
|
128
|
+
if (result.latest === null) {
|
|
129
|
+
console.log(' Could not reach the npm registry, so there is nothing to compare against.');
|
|
130
|
+
console.log('');
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!result.outdated) {
|
|
135
|
+
console.log(' \x1b[32m✓\x1b[0m Already up to date.\n');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!plan.runnable) {
|
|
139
|
+
console.log(` ${plan.guidance}\n`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
console.log(' Will run:');
|
|
143
|
+
for (const step of plan.steps)
|
|
144
|
+
console.log(` ${renderStep(step)}`);
|
|
145
|
+
console.log('');
|
|
146
|
+
if (opts.dryRun) {
|
|
147
|
+
console.log(' \x1b[90mDry run — nothing was executed.\x1b[0m\n');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const gate = confirmDecision(opts, Boolean(process.stdin.isTTY));
|
|
151
|
+
if (gate === 'refuse') {
|
|
152
|
+
console.error(' Refusing to run unattended. Re-run with --yes to confirm.\n');
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (gate === 'prompt') {
|
|
157
|
+
const answer = await askUser(' Proceed? [y/N] ');
|
|
158
|
+
if (!/^y(es)?$/i.test(answer)) {
|
|
159
|
+
console.log(' Cancelled.\n');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
const results = await runUpdate(plan);
|
|
165
|
+
for (const r of results) {
|
|
166
|
+
const mark = r.ok ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
167
|
+
console.log(` ${mark} ${renderStep(r.step)}`);
|
|
168
|
+
if (r.output)
|
|
169
|
+
console.log(r.output.split('\n').map((l) => ` ${l}`).join('\n'));
|
|
170
|
+
}
|
|
171
|
+
const failed = results.find((r) => !r.ok);
|
|
172
|
+
console.log('');
|
|
173
|
+
if (failed) {
|
|
174
|
+
console.log(' Update did not complete. Run the commands above manually to see the full error.\n');
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
}
|
|
177
|
+
else if (result.installMethod === 'plugin') {
|
|
178
|
+
// The new version is on disk but the running session still holds the old
|
|
179
|
+
// one — without saying so, users update, re-run, and see identical output.
|
|
180
|
+
console.log(' \x1b[33m!\x1b[0m Restart Claude Code for the update to take effect.\n');
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log(' \x1b[32m✓\x1b[0m Updated.\n');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
104
186
|
// --- clean ---
|
|
105
187
|
program
|
|
106
188
|
.command('clean')
|
|
@@ -109,6 +191,7 @@ program
|
|
|
109
191
|
.option('--auto', 'Non-interactive: auto-select Tier 1 items only')
|
|
110
192
|
.option('--sessions-per-day <n>', 'Sessions per day for savings estimate', '2')
|
|
111
193
|
.option('--lookback-days <n>', 'Days of session history for skill-usage analysis', '60')
|
|
194
|
+
.option('--project-dir <path>', 'Directory whose project memory counts toward the total (default: cwd)')
|
|
112
195
|
.option('--no-codex', 'Skip ~/.codex entirely')
|
|
113
196
|
.action(async (opts) => {
|
|
114
197
|
await runCleanPipeline({
|
|
@@ -117,6 +200,7 @@ program
|
|
|
117
200
|
sessionsPerDay: parseNonNegativeInt(opts.sessionsPerDay, 2),
|
|
118
201
|
lookbackDays: parseNonNegativeInt(opts.lookbackDays, 60),
|
|
119
202
|
codex: opts.codex !== false,
|
|
203
|
+
projectDir: opts.projectDir,
|
|
120
204
|
});
|
|
121
205
|
});
|
|
122
206
|
// --- restore ---
|
|
@@ -274,10 +358,28 @@ program.action(async () => {
|
|
|
274
358
|
}
|
|
275
359
|
await runCleanPipeline({ dryRun: false, auto: false, sessionsPerDay: 2, lookbackDays: 60, codex: true });
|
|
276
360
|
});
|
|
361
|
+
/**
|
|
362
|
+
* Resolve which directory's project memory counts toward the startup estimate.
|
|
363
|
+
*
|
|
364
|
+
* Warns when the CLI is running from its own install directory and no explicit
|
|
365
|
+
* directory was given: the slug would resolve to the plugin cache, match no
|
|
366
|
+
* project, and silently zero out every project-memory token. Warning beats
|
|
367
|
+
* guessing — we cannot know which project the user meant.
|
|
368
|
+
*/
|
|
369
|
+
function resolveProjectDir(explicit) {
|
|
370
|
+
if (explicit)
|
|
371
|
+
return explicit;
|
|
372
|
+
if (looksLikeToolInstallDir()) {
|
|
373
|
+
console.error(' \x1b[33m!\x1b[0m Running from claude-slim\'s own install directory, so project memory\n' +
|
|
374
|
+
' cannot be attributed and is reported as 0. Pass --project-dir <path>\n' +
|
|
375
|
+
' (or run from your project) for an accurate startup total.\n');
|
|
376
|
+
}
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
277
379
|
// --- shared clean pipeline ---
|
|
278
380
|
async function runCleanPipeline(opts) {
|
|
279
381
|
await initTokenizer();
|
|
280
|
-
const result = await scan({ lookbackDays: opts.lookbackDays });
|
|
382
|
+
const result = await scan({ lookbackDays: opts.lookbackDays, projectDir: resolveProjectDir(opts.projectDir) });
|
|
281
383
|
// Codex issues join the same tiered list. They carry `agent: 'codex'`, which
|
|
282
384
|
// is what keeps the cleaner's path guard pointed at ~/.codex/.
|
|
283
385
|
const codexContents = new Map();
|
package/dist/paths.d.ts
CHANGED
|
@@ -12,6 +12,17 @@ export declare function getDisabledDir(): string;
|
|
|
12
12
|
* the startup estimate must not sum memory across every project on disk.
|
|
13
13
|
*/
|
|
14
14
|
export declare function getCurrentProjectSlug(cwd?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* True when `cwd` sits inside claude-slim's own install rather than a project.
|
|
17
|
+
*
|
|
18
|
+
* The `/claude-slim` skill invokes the CLI with `cd "${CLAUDE_PLUGIN_ROOT}"`,
|
|
19
|
+
* which makes `process.cwd()` the plugin cache directory. The project slug then
|
|
20
|
+
* resolves to that path, no memory matches it, and the startup estimate silently
|
|
21
|
+
* drops every project-memory token — 108,570 of them on the machine where this
|
|
22
|
+
* was found. Detecting it lets the caller fail loudly or be told to pass
|
|
23
|
+
* `--project-dir` instead of quietly reporting zero.
|
|
24
|
+
*/
|
|
25
|
+
export declare function looksLikeToolInstallDir(cwd?: string): boolean;
|
|
15
26
|
export declare function getManifestPath(): string;
|
|
16
27
|
export declare function getLegacyManifestPath(): string;
|
|
17
28
|
/** The agents claude-slim is allowed to touch. Adding one widens what every
|
package/dist/paths.js
CHANGED
|
@@ -26,6 +26,22 @@ export function getDisabledDir() {
|
|
|
26
26
|
export function getCurrentProjectSlug(cwd = process.cwd()) {
|
|
27
27
|
return resolve(cwd).replace(/\//g, '-');
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* True when `cwd` sits inside claude-slim's own install rather than a project.
|
|
31
|
+
*
|
|
32
|
+
* The `/claude-slim` skill invokes the CLI with `cd "${CLAUDE_PLUGIN_ROOT}"`,
|
|
33
|
+
* which makes `process.cwd()` the plugin cache directory. The project slug then
|
|
34
|
+
* resolves to that path, no memory matches it, and the startup estimate silently
|
|
35
|
+
* drops every project-memory token — 108,570 of them on the machine where this
|
|
36
|
+
* was found. Detecting it lets the caller fail loudly or be told to pass
|
|
37
|
+
* `--project-dir` instead of quietly reporting zero.
|
|
38
|
+
*/
|
|
39
|
+
export function looksLikeToolInstallDir(cwd = process.cwd()) {
|
|
40
|
+
const p = resolve(cwd).replace(/\\/g, '/');
|
|
41
|
+
return (p.includes('/.claude/plugins/') ||
|
|
42
|
+
p.includes('/_npx/') ||
|
|
43
|
+
/\/node_modules\/claude-slim(\/|$)/.test(p));
|
|
44
|
+
}
|
|
29
45
|
export function getManifestPath() {
|
|
30
46
|
return join(getDisabledDir(), 'manifest.json');
|
|
31
47
|
}
|
package/dist/scanner/index.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ScanResult } from '../types.js';
|
|
2
2
|
export interface ScanOptions {
|
|
3
3
|
lookbackDays?: number;
|
|
4
|
+
/**
|
|
5
|
+
* Directory whose project memory counts toward the startup estimate.
|
|
6
|
+
*
|
|
7
|
+
* Defaults to `process.cwd()`, which is wrong whenever the CLI is launched
|
|
8
|
+
* from its own install directory — the `/claude-slim` skill does exactly that
|
|
9
|
+
* via `cd "${CLAUDE_PLUGIN_ROOT}"`, and every project-memory token silently
|
|
10
|
+
* dropped out of the total as a result.
|
|
11
|
+
*/
|
|
12
|
+
projectDir?: string;
|
|
4
13
|
}
|
|
5
14
|
export declare function scan(opts?: ScanOptions): Promise<ScanResult>;
|
package/dist/scanner/index.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function scan(opts = {}) {
|
|
|
97
97
|
// disk. Summing all of them (pre-2.8 behaviour) inflated the startup estimate
|
|
98
98
|
// by a factor of however many projects the user had — 100k+ tokens on a busy
|
|
99
99
|
// machine, for a number labelled "tokens at session start".
|
|
100
|
-
const currentProjectSlug = getCurrentProjectSlug();
|
|
100
|
+
const currentProjectSlug = getCurrentProjectSlug(opts.projectDir);
|
|
101
101
|
const currentProjectMemoryTokens = memoryFiles
|
|
102
102
|
.filter((m) => m.project === currentProjectSlug)
|
|
103
103
|
.reduce((sum, m) => sum + m.tokens, 0);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { InstallMethod } from './update-check.js';
|
|
2
|
+
export interface UpdateStep {
|
|
3
|
+
file: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface UpdatePlan {
|
|
7
|
+
/** false when there is nothing to execute — see `guidance`. */
|
|
8
|
+
runnable: boolean;
|
|
9
|
+
steps: UpdateStep[];
|
|
10
|
+
/** Why nothing runs, for the methods where that is the correct answer. */
|
|
11
|
+
guidance?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function planUpdate(method: InstallMethod): UpdatePlan;
|
|
14
|
+
/** Human-readable rendering of a step, matching what the user would type. */
|
|
15
|
+
export declare function renderStep(step: UpdateStep): string;
|
|
16
|
+
export interface StepResult {
|
|
17
|
+
step: UpdateStep;
|
|
18
|
+
ok: boolean;
|
|
19
|
+
output: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Run the plan's steps in order, stopping at the first failure.
|
|
23
|
+
*
|
|
24
|
+
* Sequential rather than parallel, and halting on error, because step 2 depends
|
|
25
|
+
* on step 1 having refreshed the marketplace — running it against a stale
|
|
26
|
+
* manifest would report success while changing nothing.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runUpdate(plan: UpdatePlan): Promise<StepResult[]>;
|
|
29
|
+
export type ConfirmDecision = 'run' | 'prompt' | 'refuse';
|
|
30
|
+
/**
|
|
31
|
+
* Decide how to gate an update that will modify the user's install.
|
|
32
|
+
*
|
|
33
|
+
* Refusing when there is no TTY is the important case: piped into a script or a
|
|
34
|
+
* CI job, there is nobody to answer the prompt, and silently proceeding would
|
|
35
|
+
* mean a tool that changes an installation without anyone agreeing to it.
|
|
36
|
+
* `--yes` is how you say so deliberately.
|
|
37
|
+
*/
|
|
38
|
+
export declare function confirmDecision(opts: {
|
|
39
|
+
yes?: boolean;
|
|
40
|
+
}, isTTY: boolean): ConfirmDecision;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
// Running the upgrade.
|
|
3
|
+
//
|
|
4
|
+
// v2.9.0 shipped detection only, on the reasoning that updating is the package
|
|
5
|
+
// manager's job. Half of that was right and half was not. Writing into the
|
|
6
|
+
// plugin directory ourselves would indeed corrupt an install — but *invoking*
|
|
7
|
+
// the package manager is something this tool already does elsewhere
|
|
8
|
+
// (`claude plugin disable` during unused-plugin cleanup). Refusing to invoke it
|
|
9
|
+
// here was inconsistent, not principled.
|
|
10
|
+
//
|
|
11
|
+
// So: still never write to those directories directly. Just run the command the
|
|
12
|
+
// user would have typed, after showing it to them.
|
|
13
|
+
//
|
|
14
|
+
// Every argv below is a fixed literal. Nothing from the environment, the
|
|
15
|
+
// manifest, or user input is interpolated, and execFile never routes through a
|
|
16
|
+
// shell — so there is no argument-injection surface to guard.
|
|
17
|
+
const STEP_TIMEOUT_MS = 180_000;
|
|
18
|
+
export function planUpdate(method) {
|
|
19
|
+
switch (method) {
|
|
20
|
+
case 'plugin':
|
|
21
|
+
return {
|
|
22
|
+
runnable: true,
|
|
23
|
+
steps: [
|
|
24
|
+
// Refresh the marketplace first; otherwise `plugin update` compares
|
|
25
|
+
// against a stale manifest and reports "already up to date".
|
|
26
|
+
{ file: 'claude', args: ['plugin', 'marketplace', 'update', 'claude-slim'] },
|
|
27
|
+
// The qualified id is required: the bare name fails with
|
|
28
|
+
// `Plugin "claude-slim" not found` when a marketplace shares its name.
|
|
29
|
+
{ file: 'claude', args: ['plugin', 'update', 'claude-slim@claude-slim'] },
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
case 'global':
|
|
33
|
+
return {
|
|
34
|
+
runnable: true,
|
|
35
|
+
steps: [{ file: 'npm', args: ['install', '-g', 'claude-slim@latest'] }],
|
|
36
|
+
};
|
|
37
|
+
case 'npx':
|
|
38
|
+
return {
|
|
39
|
+
runnable: false,
|
|
40
|
+
steps: [],
|
|
41
|
+
guidance: 'npx resolves the latest version on every invocation, so there is nothing to update. ' +
|
|
42
|
+
'If a stale copy is cached, run `npx claude-slim@latest` once to refresh it.',
|
|
43
|
+
};
|
|
44
|
+
case 'source':
|
|
45
|
+
return {
|
|
46
|
+
runnable: false,
|
|
47
|
+
steps: [],
|
|
48
|
+
guidance: 'This is a source checkout. Updating it means pulling your own repository, ' +
|
|
49
|
+
'which claude-slim will not do on your behalf — run `git pull && npm install` yourself.',
|
|
50
|
+
};
|
|
51
|
+
default:
|
|
52
|
+
return {
|
|
53
|
+
runnable: false,
|
|
54
|
+
steps: [],
|
|
55
|
+
guidance: 'Could not tell how this copy was installed, so no upgrade command can be chosen safely. ' +
|
|
56
|
+
'Update it the same way you installed it.',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Human-readable rendering of a step, matching what the user would type. */
|
|
61
|
+
export function renderStep(step) {
|
|
62
|
+
return [step.file, ...step.args].join(' ');
|
|
63
|
+
}
|
|
64
|
+
function runStep(step) {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
execFile(step.file, step.args, { timeout: STEP_TIMEOUT_MS }, (err, stdout, stderr) => {
|
|
67
|
+
const output = [stdout, stderr].map((s) => s?.trim()).filter(Boolean).join('\n');
|
|
68
|
+
resolve({ step, ok: !err, output: err ? `${err.message}${output ? `\n${output}` : ''}` : output });
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run the plan's steps in order, stopping at the first failure.
|
|
74
|
+
*
|
|
75
|
+
* Sequential rather than parallel, and halting on error, because step 2 depends
|
|
76
|
+
* on step 1 having refreshed the marketplace — running it against a stale
|
|
77
|
+
* manifest would report success while changing nothing.
|
|
78
|
+
*/
|
|
79
|
+
export async function runUpdate(plan) {
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const step of plan.steps) {
|
|
82
|
+
const result = await runStep(step);
|
|
83
|
+
results.push(result);
|
|
84
|
+
if (!result.ok)
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Decide how to gate an update that will modify the user's install.
|
|
91
|
+
*
|
|
92
|
+
* Refusing when there is no TTY is the important case: piped into a script or a
|
|
93
|
+
* CI job, there is nobody to answer the prompt, and silently proceeding would
|
|
94
|
+
* mean a tool that changes an installation without anyone agreeing to it.
|
|
95
|
+
* `--yes` is how you say so deliberately.
|
|
96
|
+
*/
|
|
97
|
+
export function confirmDecision(opts, isTTY) {
|
|
98
|
+
if (opts.yes)
|
|
99
|
+
return 'run';
|
|
100
|
+
return isTTY ? 'prompt' : 'refuse';
|
|
101
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-slim",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"description": "Audit and shrink your Claude Code startup context. Measures what every skill, plugin, agent, command, and memory file costs in the system prompt, then reversibly disables the dead weight. Non-destructive scan, tiered proposals, one-command restore — no proxy, no compression.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,7 @@ Analyze the user's Claude Code environment for token waste and perform non-destr
|
|
|
23
23
|
An outdated claude-slim does not merely lack features — it reports **wrong numbers**. Versions before 2.8.0 summed memory across every project on disk and inflated the startup estimate roughly 8×. Presenting those figures as fact is worse than not running at all, so check first:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
26
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" check-update --json
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
The check is cached for 24h and fails open — if it errors, times out, or returns `"latest": null`, **proceed silently**. Never block the user because a version lookup failed.
|
|
@@ -50,13 +50,13 @@ If `"outdated": false`, say nothing and continue to Phase 1.
|
|
|
50
50
|
Run the CLI to collect environment data:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
|
|
53
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" scan --json
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
If `CLAUDE_PLUGIN_ROOT` is not set:
|
|
57
57
|
```bash
|
|
58
58
|
PLUGIN_DIR=$(find ~/.claude/plugins -path "*/claude-slim/dist/cli.js" -type f 2>/dev/null | head -1 | xargs dirname | xargs dirname)
|
|
59
|
-
|
|
59
|
+
node "$PLUGIN_DIR/dist/cli.js" scan --json
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
If `node` is not available, fall back to the legacy bash scanner:
|
|
@@ -134,18 +134,18 @@ If subcommand is `scan`, stop here. Ask a localized equivalent of "Proceed with
|
|
|
134
134
|
Run the interactive clean command:
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
|
|
137
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" clean
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
Or with dry-run:
|
|
141
141
|
```bash
|
|
142
|
-
|
|
142
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" clean --dry-run
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
After cleanup, re-run scan to get updated numbers, then show the savings report:
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
|
-
|
|
148
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" report
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
Present the report box AND the before/after breakdown table to the user.
|
|
@@ -157,7 +157,7 @@ Present the report box AND the before/after breakdown table to the user.
|
|
|
157
157
|
When `/claude-slim restore` is invoked:
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
|
-
|
|
160
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" restore
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
## Doctor
|
|
@@ -165,7 +165,7 @@ cd "${CLAUDE_PLUGIN_ROOT}" && node dist/cli.js restore
|
|
|
165
165
|
When `/claude-slim doctor` is invoked:
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
|
-
|
|
168
|
+
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.js" doctor
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
Explain warnings in the user's language. Pay special attention to session-log warnings because they explain why unused-skill detection may be suppressed.
|