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 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 items to `~/.claude/skills.disabled/`. **Nothing is deleted. Ever.**
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** | Nothing is ever deleted. Disabled items move to `~/.claude/skills.disabled/` |
164
- | **Reversible** | `/claude-slim restore` brings anything back, any time |
165
- | **User-controlled** | Always asks before making changes. `--dry-run` to preview. |
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/` and even those are moved to `skills.disabled/`, not deleted (except `temp_local_*` failed-install caches, which are removed outright).
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')
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-slim",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Analyze and reduce Claude Code token overhead",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.** The CLI moves items to `~/.claude/skills.disabled/`.
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.