claude-slim 2.5.1 → 2.6.0
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 +7 -5
- package/dist/cli.js +18 -0
- package/dist/doctor.d.ts +15 -0
- package/dist/doctor.js +119 -0
- package/package.json +1 -1
- package/skills/claude-slim/SKILL.md +12 -1
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ That's slower responses. Hitting your usage cap faster. Paying for context you'r
|
|
|
80
80
|
| **Recommended** | Suggested | Duplicates, stale memory, disabled plugins, stale projects |
|
|
81
81
|
| **Optional** | Your call | Oversized skills you might still use |
|
|
82
82
|
|
|
83
|
-
**Clean** — Moves selected
|
|
83
|
+
**Clean** — Moves selected skills and project memory to `~/.claude/skills.disabled/`. Failed-install temp caches and dead symlink files are the only permanent cleanups, and they are labeled before selection.
|
|
84
84
|
|
|
85
85
|
**Report** — Shows exactly what changed:
|
|
86
86
|
|
|
@@ -139,6 +139,7 @@ Then just type `/claude-slim` in any session.
|
|
|
139
139
|
/claude-slim scan # Report only, no changes
|
|
140
140
|
/claude-slim scan --json # Machine-readable JSON output
|
|
141
141
|
/claude-slim scan --lookback-days 30 # Treat skills idle for 30+ days as unused
|
|
142
|
+
/claude-slim doctor # Check scanner prerequisites and data fidelity
|
|
142
143
|
/claude-slim restore # Bring back anything you disabled
|
|
143
144
|
```
|
|
144
145
|
|
|
@@ -150,6 +151,7 @@ npx claude-slim clean --dry-run # See what would happen (no changes)
|
|
|
150
151
|
npx claude-slim clean --auto # Non-interactive, Tier 1 only (CI/scripts)
|
|
151
152
|
npx claude-slim clean --lookback-days N # Tune the unused-skill detection window
|
|
152
153
|
npx claude-slim scan # Report only
|
|
154
|
+
npx claude-slim doctor # Diagnose Node/Claude/session-log readiness
|
|
153
155
|
npx claude-slim restore # Undo
|
|
154
156
|
npx claude-slim report # Show savings from last clean
|
|
155
157
|
```
|
|
@@ -160,9 +162,9 @@ npx claude-slim report # Show savings from last clean
|
|
|
160
162
|
|
|
161
163
|
| | |
|
|
162
164
|
|---|---|
|
|
163
|
-
| **Non-destructive** |
|
|
164
|
-
| **Reversible** | `/claude-slim restore` brings
|
|
165
|
-
| **User-controlled** |
|
|
165
|
+
| **Non-destructive for user data** | Skills and project memory move to `~/.claude/skills.disabled/` |
|
|
166
|
+
| **Reversible where state exists** | `/claude-slim restore` brings moved skills and project memory back |
|
|
167
|
+
| **User-controlled** | Interactive runs ask before changes. `--dry-run` previews; `--auto` selects Tier 1 only. |
|
|
166
168
|
| **Hands off** | Never touches CLAUDE.md, settings.json, or plugin configs |
|
|
167
169
|
| **Scoped** | All operations are refused if the target path escapes `~/.claude/` |
|
|
168
170
|
|
|
@@ -174,7 +176,7 @@ npx claude-slim report # Show savings from last clean
|
|
|
174
176
|
- **Git / project sources** — claude-slim only looks inside `~/.claude/`, never at your code.
|
|
175
177
|
- **Anything outside `~/.claude/`** — a path-containment guard refuses destructive ops anywhere else, even if a tampered manifest asked it to.
|
|
176
178
|
|
|
177
|
-
Only touched: entries under `~/.claude/skills/`, `~/.claude/plugins/cache/temp_local_*`, and `~/.claude/projects/*/memory
|
|
179
|
+
Only touched: entries under `~/.claude/skills/`, `~/.claude/plugins/cache/temp_local_*`, and `~/.claude/projects/*/memory/`. Skill and memory entries are moved to `skills.disabled/`; broken symlink files are unlinked and `temp_local_*` failed-install caches are removed outright.
|
|
178
180
|
|
|
179
181
|
---
|
|
180
182
|
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { scan, SKILL_PROMPT_OVERHEAD_TOKENS } from './scanner.js';
|
|
|
9
9
|
import { cleanIssues, restoreItem } from './cleaner.js';
|
|
10
10
|
import { readManifest } from './manifest.js';
|
|
11
11
|
import { formatScanSummary, formatReportBox, calculateReport, } from './report.js';
|
|
12
|
+
import { collectDoctorReport, formatDoctorReport } from './doctor.js';
|
|
12
13
|
import { resolveSelection, resolveRestoreSelection } from './selection.js';
|
|
13
14
|
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
14
15
|
const { version: PKG_VERSION } = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
@@ -34,6 +35,23 @@ program
|
|
|
34
35
|
console.log(formatScanSummary(result));
|
|
35
36
|
}
|
|
36
37
|
});
|
|
38
|
+
// --- doctor ---
|
|
39
|
+
program
|
|
40
|
+
.command('doctor')
|
|
41
|
+
.description('Check local Claude Code environment and scanner fidelity')
|
|
42
|
+
.option('--json', 'Output raw JSON')
|
|
43
|
+
.option('--lookback-days <n>', 'Days of session history for skill-usage analysis', '60')
|
|
44
|
+
.action(async (opts) => {
|
|
45
|
+
const report = await collectDoctorReport({
|
|
46
|
+
lookbackDays: parseInt(opts.lookbackDays, 10) || 60,
|
|
47
|
+
});
|
|
48
|
+
if (opts.json) {
|
|
49
|
+
console.log(JSON.stringify(report, null, 2));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(formatDoctorReport(report));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
37
55
|
// --- clean ---
|
|
38
56
|
program
|
|
39
57
|
.command('clean')
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type DoctorStatus = 'ok' | 'warn' | 'fail';
|
|
2
|
+
export interface DoctorCheck {
|
|
3
|
+
label: string;
|
|
4
|
+
status: DoctorStatus;
|
|
5
|
+
detail: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DoctorReport {
|
|
9
|
+
checks: DoctorCheck[];
|
|
10
|
+
}
|
|
11
|
+
export declare function isSupportedRuntimeNode(version: string): boolean;
|
|
12
|
+
export declare function collectDoctorReport(opts?: {
|
|
13
|
+
lookbackDays?: number;
|
|
14
|
+
}): Promise<DoctorReport>;
|
|
15
|
+
export declare function formatDoctorReport(report: DoctorReport): string;
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { access, readdir } from 'node:fs/promises';
|
|
2
|
+
import { getClaudeDir, getPluginsDir, getProjectsDir, getSkillsDir } from './paths.js';
|
|
3
|
+
import { runCommand } from './scanner/fs-walk.js';
|
|
4
|
+
import { scanSessionUsage } from './scanner/sessions.js';
|
|
5
|
+
const MIN_RUNTIME_NODE_MAJOR = 20;
|
|
6
|
+
export function isSupportedRuntimeNode(version) {
|
|
7
|
+
const normalized = version.trim().replace(/^v/, '');
|
|
8
|
+
const major = Number.parseInt(normalized.split('.')[0] || '', 10);
|
|
9
|
+
return Number.isFinite(major) && major >= MIN_RUNTIME_NODE_MAJOR;
|
|
10
|
+
}
|
|
11
|
+
async function pathReadable(path) {
|
|
12
|
+
try {
|
|
13
|
+
await access(path);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function countEntries(path) {
|
|
21
|
+
try {
|
|
22
|
+
return (await readdir(path)).length;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function collectDoctorReport(opts = {}) {
|
|
29
|
+
const lookbackDays = opts.lookbackDays ?? 60;
|
|
30
|
+
const checks = [];
|
|
31
|
+
checks.push({
|
|
32
|
+
label: 'Node.js',
|
|
33
|
+
status: isSupportedRuntimeNode(process.version) ? 'ok' : 'fail',
|
|
34
|
+
detail: `${process.version} (requires >=${MIN_RUNTIME_NODE_MAJOR})`,
|
|
35
|
+
hint: isSupportedRuntimeNode(process.version)
|
|
36
|
+
? undefined
|
|
37
|
+
: 'Install Node.js 20 or newer.',
|
|
38
|
+
});
|
|
39
|
+
const claudeDir = getClaudeDir();
|
|
40
|
+
const claudeDirReadable = await pathReadable(claudeDir);
|
|
41
|
+
checks.push({
|
|
42
|
+
label: 'Claude directory',
|
|
43
|
+
status: claudeDirReadable ? 'ok' : 'fail',
|
|
44
|
+
detail: claudeDir,
|
|
45
|
+
hint: claudeDirReadable ? undefined : 'Run Claude Code once so ~/.claude is created.',
|
|
46
|
+
});
|
|
47
|
+
const skillCount = await countEntries(getSkillsDir());
|
|
48
|
+
checks.push({
|
|
49
|
+
label: 'Local skills directory',
|
|
50
|
+
status: skillCount === null ? 'warn' : 'ok',
|
|
51
|
+
detail: skillCount === null ? `${getSkillsDir()} not readable` : `${skillCount} entries`,
|
|
52
|
+
hint: skillCount === null ? 'No local skills were found or the directory is not readable.' : undefined,
|
|
53
|
+
});
|
|
54
|
+
const pluginCount = await countEntries(getPluginsDir());
|
|
55
|
+
checks.push({
|
|
56
|
+
label: 'Plugin cache directory',
|
|
57
|
+
status: pluginCount === null ? 'warn' : 'ok',
|
|
58
|
+
detail: pluginCount === null ? `${getPluginsDir()} not readable` : `${pluginCount} entries`,
|
|
59
|
+
hint: pluginCount === null ? 'Install a Claude Code plugin if you expect plugin-skill scanning.' : undefined,
|
|
60
|
+
});
|
|
61
|
+
const pluginListOutput = await runCommand('claude', ['plugin', 'list']);
|
|
62
|
+
checks.push({
|
|
63
|
+
label: 'Claude plugin CLI',
|
|
64
|
+
status: pluginListOutput.trim() ? 'ok' : 'warn',
|
|
65
|
+
detail: pluginListOutput.trim() ? 'claude plugin list returned output' : 'no output from claude plugin list',
|
|
66
|
+
hint: pluginListOutput.trim()
|
|
67
|
+
? undefined
|
|
68
|
+
: 'Install or sign in to Claude Code if disabled-plugin detection looks incomplete.',
|
|
69
|
+
});
|
|
70
|
+
const projectsReadable = await pathReadable(getProjectsDir());
|
|
71
|
+
if (!projectsReadable) {
|
|
72
|
+
checks.push({
|
|
73
|
+
label: 'Session transcripts',
|
|
74
|
+
status: 'warn',
|
|
75
|
+
detail: `${getProjectsDir()} not readable`,
|
|
76
|
+
hint: 'Unused-skill detection needs recent ~/.claude/projects/*.jsonl session logs.',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const usage = await scanSessionUsage(lookbackDays);
|
|
81
|
+
checks.push({
|
|
82
|
+
label: 'Session transcripts',
|
|
83
|
+
status: usage.dataAvailable ? 'ok' : 'warn',
|
|
84
|
+
detail: `${usage.sessionsInWindow} sessions in last ${lookbackDays}d, ${usage.invokedSkills.size} invoked skills`,
|
|
85
|
+
hint: usage.dataAvailable
|
|
86
|
+
? undefined
|
|
87
|
+
: 'Unused-skill detection will be suppressed until enough reliable session data exists.',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return { checks };
|
|
91
|
+
}
|
|
92
|
+
export function formatDoctorReport(report) {
|
|
93
|
+
const lines = ['', '\x1b[1m=== claude-slim doctor ===\x1b[0m', ''];
|
|
94
|
+
const symbols = {
|
|
95
|
+
ok: '\x1b[32m✓\x1b[0m',
|
|
96
|
+
warn: '\x1b[33m!\x1b[0m',
|
|
97
|
+
fail: '\x1b[31m✗\x1b[0m',
|
|
98
|
+
};
|
|
99
|
+
for (const check of report.checks) {
|
|
100
|
+
lines.push(` ${symbols[check.status]} ${check.label}: ${check.detail}`);
|
|
101
|
+
if (check.hint && check.status !== 'ok') {
|
|
102
|
+
lines.push(` ${check.hint}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const failed = report.checks.filter((c) => c.status === 'fail').length;
|
|
106
|
+
const warned = report.checks.filter((c) => c.status === 'warn').length;
|
|
107
|
+
lines.push('');
|
|
108
|
+
if (failed > 0) {
|
|
109
|
+
lines.push(` ${failed} failing check(s), ${warned} warning(s).`);
|
|
110
|
+
}
|
|
111
|
+
else if (warned > 0) {
|
|
112
|
+
lines.push(` No failing checks. ${warned} warning(s) may reduce scan fidelity.`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
lines.push(' All checks passed.');
|
|
116
|
+
}
|
|
117
|
+
lines.push('');
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ Analyze the user's Claude Code environment for token waste and perform non-destr
|
|
|
12
12
|
- `/claude-slim` or `/claude-slim run` → full pipeline (scan → propose → execute → report)
|
|
13
13
|
- `/claude-slim scan` → report only, no changes
|
|
14
14
|
- `/claude-slim scan --json` → raw JSON output
|
|
15
|
+
- `/claude-slim doctor` → check scanner prerequisites and session-log signal quality
|
|
15
16
|
- `/claude-slim restore` → restore previously disabled items
|
|
16
17
|
|
|
17
18
|
---
|
|
@@ -131,6 +132,16 @@ When `/claude-slim restore` is invoked:
|
|
|
131
132
|
cd "${CLAUDE_PLUGIN_ROOT}" && node dist/cli.js restore
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
## Doctor
|
|
136
|
+
|
|
137
|
+
When `/claude-slim doctor` is invoked:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
cd "${CLAUDE_PLUGIN_ROOT}" && node dist/cli.js doctor
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Explain warnings in the user's language. Pay special attention to session-log warnings because they explain why unused-skill detection may be suppressed.
|
|
144
|
+
|
|
134
145
|
---
|
|
135
146
|
|
|
136
147
|
## Language
|
|
@@ -139,7 +150,7 @@ Detect the user's language from their most recent message. Present all reports,
|
|
|
139
150
|
|
|
140
151
|
## Rules
|
|
141
152
|
|
|
142
|
-
1. **Never delete.**
|
|
153
|
+
1. **Never delete user data.** Skill directories and project memory are moved to `~/.claude/skills.disabled/`; dead symlink files and failed-install `temp_local_*` caches are permanent cleanups and should be described that way.
|
|
143
154
|
2. **Never modify CLAUDE.md or settings.json.**
|
|
144
155
|
3. **Never disable plugin-managed skills.** Report only.
|
|
145
156
|
4. **Always confirm before executing.** Use `--dry-run` to preview changes.
|