compress-claude 1.0.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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +73 -0
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.js +191 -0
- package/dist/generator.d.ts +35 -0
- package/dist/generator.js +3025 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +101 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +42 -0
- package/dist/learning.d.ts +78 -0
- package/dist/learning.js +488 -0
- package/dist/license.d.ts +7 -0
- package/dist/license.js +75 -0
- package/dist/proxy/compressors.d.ts +5 -0
- package/dist/proxy/compressors.js +281 -0
- package/dist/proxy/middleware.d.ts +20 -0
- package/dist/proxy/middleware.js +77 -0
- package/dist/proxy/server.d.ts +8 -0
- package/dist/proxy/server.js +624 -0
- package/dist/proxy/transcript.d.ts +37 -0
- package/dist/proxy/transcript.js +164 -0
- package/dist/scanner.d.ts +21 -0
- package/dist/scanner.js +311 -0
- package/dist/utils/colors.d.ts +15 -0
- package/dist/utils/colors.js +34 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/tokens.d.ts +8 -0
- package/dist/utils/tokens.js +21 -0
- package/package.json +48 -0
package/dist/learning.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.findTranscripts = findTranscripts;
|
|
37
|
+
exports.parseTranscript = parseTranscript;
|
|
38
|
+
exports.extractToolPairs = extractToolPairs;
|
|
39
|
+
exports.getProjectDir = getProjectDir;
|
|
40
|
+
exports.getAllProjectDirs = getAllProjectDirs;
|
|
41
|
+
exports.findPatterns = findPatterns;
|
|
42
|
+
exports.generateLearnings = generateLearnings;
|
|
43
|
+
exports.applyLearnings = applyLearnings;
|
|
44
|
+
exports.runLearn = runLearn;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const logger_1 = require("./utils/logger");
|
|
49
|
+
// --- Helpers ---
|
|
50
|
+
function isErrorContent(text) {
|
|
51
|
+
const errorPatterns = [
|
|
52
|
+
/error/i, /exception/i, /traceback/i, /failed/i,
|
|
53
|
+
/ENOENT/, /EACCES/, /SyntaxError/, /TypeError/,
|
|
54
|
+
/ReferenceError/, /command not found/,
|
|
55
|
+
];
|
|
56
|
+
return errorPatterns.some((p) => p.test(text));
|
|
57
|
+
}
|
|
58
|
+
function findTranscripts(projectDir) {
|
|
59
|
+
const sessionsDir = path.join(projectDir, 'sessions');
|
|
60
|
+
if (!fs.existsSync(sessionsDir))
|
|
61
|
+
return [];
|
|
62
|
+
const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
63
|
+
const files = [];
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
const transcript = path.join(sessionsDir, entry.name, 'transcript.jsonl');
|
|
67
|
+
if (fs.existsSync(transcript))
|
|
68
|
+
files.push(transcript);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
function parseTranscript(filePath) {
|
|
74
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
75
|
+
return raw
|
|
76
|
+
.split('\n')
|
|
77
|
+
.filter((line) => line.trim().length > 0)
|
|
78
|
+
.map((line) => JSON.parse(line));
|
|
79
|
+
}
|
|
80
|
+
function extractToolPairs(messages) {
|
|
81
|
+
const pairs = [];
|
|
82
|
+
for (const msg of messages) {
|
|
83
|
+
if (typeof msg.content !== 'object' || !Array.isArray(msg.content))
|
|
84
|
+
continue;
|
|
85
|
+
for (const block of msg.content) {
|
|
86
|
+
if (block.type === 'tool_result' || block.type === 'tool_use') {
|
|
87
|
+
// Simplified extraction
|
|
88
|
+
const toolName = block.name || '';
|
|
89
|
+
const input = block.input || {};
|
|
90
|
+
const resultText = typeof block.content === 'string'
|
|
91
|
+
? block.content
|
|
92
|
+
: JSON.stringify(block.content || '');
|
|
93
|
+
const isError = !!(block.is_error);
|
|
94
|
+
pairs.push({
|
|
95
|
+
tool: { name: toolName, input },
|
|
96
|
+
resultText,
|
|
97
|
+
contentSize: resultText.length,
|
|
98
|
+
isError,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return pairs;
|
|
104
|
+
}
|
|
105
|
+
function getProjectDir(cwd) {
|
|
106
|
+
const claudeDir = path.join(os.homedir(), '.claude', 'projects');
|
|
107
|
+
if (!fs.existsSync(claudeDir))
|
|
108
|
+
return null;
|
|
109
|
+
// Hash-based project directory lookup
|
|
110
|
+
const safeName = cwd.replace(/\//g, '-').replace(/^-/, '');
|
|
111
|
+
const candidates = fs.readdirSync(claudeDir).filter((d) => d.includes(safeName) || d === safeName);
|
|
112
|
+
if (candidates.length > 0)
|
|
113
|
+
return path.join(claudeDir, candidates[0]);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function getAllProjectDirs() {
|
|
117
|
+
const claudeDir = path.join(os.homedir(), '.claude', 'projects');
|
|
118
|
+
if (!fs.existsSync(claudeDir))
|
|
119
|
+
return [];
|
|
120
|
+
return fs.readdirSync(claudeDir, { withFileTypes: true })
|
|
121
|
+
.filter((d) => d.isDirectory())
|
|
122
|
+
.map((d) => path.join(claudeDir, d.name));
|
|
123
|
+
}
|
|
124
|
+
// --- Pattern Detection ---
|
|
125
|
+
function findPatterns(pairs) {
|
|
126
|
+
const patterns = {
|
|
127
|
+
errorFixes: [],
|
|
128
|
+
readStorms: {},
|
|
129
|
+
retrySpirals: [],
|
|
130
|
+
largeResults: [],
|
|
131
|
+
failedSearches: [],
|
|
132
|
+
totalToolCalls: pairs.length,
|
|
133
|
+
totalResultTokens: 0,
|
|
134
|
+
toolBreakdown: {},
|
|
135
|
+
};
|
|
136
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
137
|
+
const p = pairs[i];
|
|
138
|
+
const estTokens = Math.round(p.contentSize / 4);
|
|
139
|
+
patterns.totalResultTokens += estTokens;
|
|
140
|
+
// Tool breakdown
|
|
141
|
+
const tn = p.tool.name;
|
|
142
|
+
if (!patterns.toolBreakdown[tn])
|
|
143
|
+
patterns.toolBreakdown[tn] = { calls: 0, tokens: 0 };
|
|
144
|
+
patterns.toolBreakdown[tn].calls++;
|
|
145
|
+
patterns.toolBreakdown[tn].tokens += estTokens;
|
|
146
|
+
// Error -> Fix patterns
|
|
147
|
+
if (p.isError || isErrorContent(p.resultText)) {
|
|
148
|
+
for (let j = i + 1; j < Math.min(i + 10, pairs.length); j++) {
|
|
149
|
+
if (pairs[j].tool.name === p.tool.name && !pairs[j].isError && !isErrorContent(pairs[j].resultText)) {
|
|
150
|
+
const failedInput = p.tool.name === 'Bash'
|
|
151
|
+
? (p.tool.input?.command || '')
|
|
152
|
+
: JSON.stringify(p.tool.input || {}).substring(0, 200);
|
|
153
|
+
const fixedInput = pairs[j].tool.name === 'Bash'
|
|
154
|
+
? (pairs[j].tool.input?.command || '')
|
|
155
|
+
: JSON.stringify(pairs[j].tool.input || {}).substring(0, 200);
|
|
156
|
+
// Extract clean error lines
|
|
157
|
+
const errorMsg = p.resultText.split('\n')
|
|
158
|
+
.map((l) => l.replace(/^\s*\d+[|\u2192\u2502]\s*/, '').trim())
|
|
159
|
+
.filter((l) => l.length > 5 && l.length < 200 && isErrorContent(l))
|
|
160
|
+
.slice(0, 2).join(' | ');
|
|
161
|
+
if (errorMsg.length > 10) {
|
|
162
|
+
patterns.errorFixes.push({
|
|
163
|
+
tool: p.tool.name,
|
|
164
|
+
failed: failedInput.substring(0, 300),
|
|
165
|
+
fixed: fixedInput.substring(0, 300),
|
|
166
|
+
error: errorMsg.substring(0, 200),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Read storms (same file read 3+ times)
|
|
174
|
+
if (p.tool.name === 'Read' && p.tool.input?.file_path) {
|
|
175
|
+
const fp = p.tool.input.file_path;
|
|
176
|
+
patterns.readStorms[fp] = (patterns.readStorms[fp] || 0) + 1;
|
|
177
|
+
}
|
|
178
|
+
// Large results
|
|
179
|
+
if (p.contentSize > 5000) {
|
|
180
|
+
patterns.largeResults.push({
|
|
181
|
+
tool: p.tool.name,
|
|
182
|
+
input: p.tool.name === 'Bash'
|
|
183
|
+
? (p.tool.input?.command || '').substring(0, 100)
|
|
184
|
+
: JSON.stringify(p.tool.input || {}).substring(0, 100),
|
|
185
|
+
size: p.contentSize,
|
|
186
|
+
tokens: estTokens,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// Failed searches (grep/glob with empty results)
|
|
190
|
+
if ((p.tool.name === 'Grep' || p.tool.name === 'Glob') && p.resultText.trim().length < 10) {
|
|
191
|
+
patterns.failedSearches.push({
|
|
192
|
+
tool: p.tool.name,
|
|
193
|
+
input: p.tool.input,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Filter read storms to 3+
|
|
198
|
+
const stormEntries = Object.entries(patterns.readStorms).filter((e) => e[1] >= 3);
|
|
199
|
+
patterns.readStorms = {};
|
|
200
|
+
for (const [file, count] of stormEntries) {
|
|
201
|
+
patterns.readStorms[file] = count;
|
|
202
|
+
}
|
|
203
|
+
// Find retry spirals (same bash command 3+ times)
|
|
204
|
+
const bashCmds = pairs
|
|
205
|
+
.filter((pp) => pp.tool.name === 'Bash')
|
|
206
|
+
.map((pp) => (pp.tool.input?.command || '').trim().split('\n')[0]);
|
|
207
|
+
const cmdCounts = {};
|
|
208
|
+
for (const cmd of bashCmds) {
|
|
209
|
+
if (cmd)
|
|
210
|
+
cmdCounts[cmd] = (cmdCounts[cmd] || 0) + 1;
|
|
211
|
+
}
|
|
212
|
+
patterns.retrySpirals = Object.entries(cmdCounts)
|
|
213
|
+
.filter((e) => e[1] >= 3)
|
|
214
|
+
.map((e) => ({ command: e[0], count: e[1] }));
|
|
215
|
+
return patterns;
|
|
216
|
+
}
|
|
217
|
+
// --- Learning Generation ---
|
|
218
|
+
function generateLearnings(allPatterns) {
|
|
219
|
+
const learnings = [];
|
|
220
|
+
const seen = {};
|
|
221
|
+
for (const patterns of allPatterns) {
|
|
222
|
+
// Error->Fix learnings
|
|
223
|
+
for (const ef of patterns.errorFixes) {
|
|
224
|
+
const key = `${ef.tool}:${ef.error.substring(0, 50)}`;
|
|
225
|
+
if (seen[key])
|
|
226
|
+
continue;
|
|
227
|
+
seen[key] = true;
|
|
228
|
+
learnings.push({
|
|
229
|
+
type: 'mistake',
|
|
230
|
+
severity: 'high',
|
|
231
|
+
text: `When using ${ef.tool}: "${ef.error.substring(0, 120)}" -- Fix: use the corrected approach instead of retrying the same thing.`,
|
|
232
|
+
failed: ef.failed.substring(0, 150),
|
|
233
|
+
fixed: ef.fixed.substring(0, 150),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// Read storm learnings
|
|
237
|
+
const stormFiles = Object.entries(patterns.readStorms);
|
|
238
|
+
for (const [file, count] of stormFiles) {
|
|
239
|
+
const key2 = `readstorm:${file}`;
|
|
240
|
+
if (seen[key2])
|
|
241
|
+
continue;
|
|
242
|
+
seen[key2] = true;
|
|
243
|
+
learnings.push({
|
|
244
|
+
type: 'optimization',
|
|
245
|
+
severity: 'medium',
|
|
246
|
+
text: `File "${file}" was read ${count} times in one session. Read once and reference from context.`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// Retry spirals
|
|
250
|
+
for (const rs of patterns.retrySpirals) {
|
|
251
|
+
const key3 = `retry:${rs.command.substring(0, 50)}`;
|
|
252
|
+
if (seen[key3])
|
|
253
|
+
continue;
|
|
254
|
+
seen[key3] = true;
|
|
255
|
+
learnings.push({
|
|
256
|
+
type: 'mistake',
|
|
257
|
+
severity: 'medium',
|
|
258
|
+
text: `Command retried ${rs.count} times: "${rs.command.substring(0, 100)}". Diagnose the root cause instead of retrying.`,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Deduplicate by similar text
|
|
263
|
+
const unique = [];
|
|
264
|
+
const seenTexts = {};
|
|
265
|
+
for (const learning of learnings) {
|
|
266
|
+
const normalized = learning.text.substring(0, 80).toLowerCase();
|
|
267
|
+
if (!seenTexts[normalized]) {
|
|
268
|
+
seenTexts[normalized] = true;
|
|
269
|
+
unique.push(learning);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return unique;
|
|
273
|
+
}
|
|
274
|
+
// --- Apply Learnings to Files ---
|
|
275
|
+
function applyLearnings(cwd, learnings) {
|
|
276
|
+
if (learnings.length === 0)
|
|
277
|
+
return { applied: 0, files: [] };
|
|
278
|
+
const files = [];
|
|
279
|
+
const mistakesPath = path.join(cwd, 'ai-docs', 'MISTAKES.md');
|
|
280
|
+
const claudeMdPath = path.join(cwd, 'CLAUDE.md');
|
|
281
|
+
const globalClaudeMd = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
282
|
+
const mistakeEntries = learnings.filter((l) => l.type === 'mistake');
|
|
283
|
+
const highSeverity = learnings.filter((l) => l.severity === 'high');
|
|
284
|
+
// Append to MISTAKES.md
|
|
285
|
+
if (mistakeEntries.length > 0 && fs.existsSync(path.join(cwd, 'ai-docs'))) {
|
|
286
|
+
let existing = '';
|
|
287
|
+
try {
|
|
288
|
+
existing = fs.readFileSync(mistakesPath, 'utf8');
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
existing = '# MISTAKES.md -- Learned from Sessions\n\n';
|
|
292
|
+
}
|
|
293
|
+
let newSection = `\n## Auto-learned (${new Date().toISOString().split('T')[0]})\n\n`;
|
|
294
|
+
for (const m of mistakeEntries) {
|
|
295
|
+
if (existing.includes(m.text.substring(0, 60)))
|
|
296
|
+
continue;
|
|
297
|
+
newSection += `- ${m.text}\n`;
|
|
298
|
+
if (m.failed)
|
|
299
|
+
newSection += ` - Failed: \`${m.failed.substring(0, 120)}\`\n`;
|
|
300
|
+
if (m.fixed)
|
|
301
|
+
newSection += ` - Fixed: \`${m.fixed.substring(0, 120)}\`\n`;
|
|
302
|
+
}
|
|
303
|
+
if (newSection.split('\n').length > 3) {
|
|
304
|
+
const dir = path.dirname(mistakesPath);
|
|
305
|
+
if (!fs.existsSync(dir))
|
|
306
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
307
|
+
fs.writeFileSync(mistakesPath, existing + newSection, 'utf8');
|
|
308
|
+
files.push('ai-docs/MISTAKES.md');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Append critical learnings to CLAUDE.md
|
|
312
|
+
if (highSeverity.length > 0 && fs.existsSync(claudeMdPath)) {
|
|
313
|
+
let claudeContent = fs.readFileSync(claudeMdPath, 'utf8');
|
|
314
|
+
const learnedHeader = '\n## Learned from Sessions\n';
|
|
315
|
+
if (!claudeContent.includes('## Learned from Sessions')) {
|
|
316
|
+
claudeContent += learnedHeader;
|
|
317
|
+
}
|
|
318
|
+
let newItems = '';
|
|
319
|
+
for (const hs of highSeverity) {
|
|
320
|
+
const shortText = hs.text.substring(0, 150);
|
|
321
|
+
if (claudeContent.includes(shortText.substring(0, 60)))
|
|
322
|
+
continue;
|
|
323
|
+
newItems += `- ${shortText}\n`;
|
|
324
|
+
}
|
|
325
|
+
if (newItems) {
|
|
326
|
+
claudeContent = claudeContent.replace('## Learned from Sessions\n', `## Learned from Sessions\n${newItems}`);
|
|
327
|
+
fs.writeFileSync(claudeMdPath, claudeContent, 'utf8');
|
|
328
|
+
files.push('CLAUDE.md');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Write to memory dir if it exists
|
|
332
|
+
let memoryDir = null;
|
|
333
|
+
const projectDir = getProjectDir(cwd);
|
|
334
|
+
if (projectDir) {
|
|
335
|
+
memoryDir = path.join(projectDir, 'memory');
|
|
336
|
+
if (!fs.existsSync(memoryDir)) {
|
|
337
|
+
try {
|
|
338
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
memoryDir = null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (memoryDir) {
|
|
346
|
+
const memoryFile = path.join(memoryDir, 'session-learnings.md');
|
|
347
|
+
let memContent = '';
|
|
348
|
+
try {
|
|
349
|
+
memContent = fs.readFileSync(memoryFile, 'utf8');
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
memContent = '# Session Learnings\n\nAuto-generated by compress-claude --learn. Claude reads this every session.\n\n';
|
|
353
|
+
}
|
|
354
|
+
let newMemory = '';
|
|
355
|
+
for (const learning of learnings) {
|
|
356
|
+
if (memContent.includes(learning.text.substring(0, 60)))
|
|
357
|
+
continue;
|
|
358
|
+
newMemory += `- ${learning.text}\n`;
|
|
359
|
+
}
|
|
360
|
+
if (newMemory) {
|
|
361
|
+
fs.writeFileSync(memoryFile, memContent + newMemory, 'utf8');
|
|
362
|
+
files.push('~/.claude/projects/.../memory/session-learnings.md');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Write critical patterns to global CLAUDE.md
|
|
366
|
+
if (highSeverity.length > 0) {
|
|
367
|
+
let globalContent = '';
|
|
368
|
+
try {
|
|
369
|
+
globalContent = fs.readFileSync(globalClaudeMd, 'utf8');
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
globalContent = '';
|
|
373
|
+
}
|
|
374
|
+
if (!globalContent.includes('## Session Learnings (global)')) {
|
|
375
|
+
globalContent += '\n## Session Learnings (global)\n';
|
|
376
|
+
}
|
|
377
|
+
let globalNew = '';
|
|
378
|
+
for (const hs of highSeverity) {
|
|
379
|
+
const gText = hs.text.substring(0, 150);
|
|
380
|
+
if (globalContent.includes(gText.substring(0, 60)))
|
|
381
|
+
continue;
|
|
382
|
+
globalNew += `- ${gText}\n`;
|
|
383
|
+
}
|
|
384
|
+
if (globalNew) {
|
|
385
|
+
globalContent = globalContent.replace('## Session Learnings (global)\n', `## Session Learnings (global)\n${globalNew}`);
|
|
386
|
+
fs.writeFileSync(globalClaudeMd, globalContent, 'utf8');
|
|
387
|
+
files.push('~/.claude/CLAUDE.md');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return { applied: learnings.length, files };
|
|
391
|
+
}
|
|
392
|
+
async function runLearn(cwd, opts) {
|
|
393
|
+
const options = opts || {};
|
|
394
|
+
const allProjects = options.all || false;
|
|
395
|
+
(0, logger_1.log)('LEARN', 'start', 'Starting learn analysis');
|
|
396
|
+
let dirs = [];
|
|
397
|
+
if (allProjects) {
|
|
398
|
+
dirs = getAllProjectDirs();
|
|
399
|
+
(0, logger_1.log)('LEARN', 'scan', `Scanning all projects (${dirs.length} found)`);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
const projDir = getProjectDir(cwd);
|
|
403
|
+
if (!projDir) {
|
|
404
|
+
(0, logger_1.logWarn)('LEARN', 'scan', 'No Claude Code sessions found for this project');
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
dirs = [projDir];
|
|
408
|
+
}
|
|
409
|
+
const allPatterns = [];
|
|
410
|
+
let totalTranscripts = 0;
|
|
411
|
+
let totalPairs = 0;
|
|
412
|
+
for (const dir of dirs) {
|
|
413
|
+
const transcripts = findTranscripts(dir);
|
|
414
|
+
for (const transcript of transcripts) {
|
|
415
|
+
totalTranscripts++;
|
|
416
|
+
try {
|
|
417
|
+
const messages = parseTranscript(transcript);
|
|
418
|
+
const pairs = extractToolPairs(messages);
|
|
419
|
+
totalPairs += pairs.length;
|
|
420
|
+
if (pairs.length > 0) {
|
|
421
|
+
const patterns = findPatterns(pairs);
|
|
422
|
+
allPatterns.push(patterns);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
// Skip corrupt transcripts
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
(0, logger_1.log)('LEARN', 'analyzed', `Sessions: ${totalTranscripts}, Tool calls: ${totalPairs}`);
|
|
431
|
+
if (allPatterns.length === 0) {
|
|
432
|
+
(0, logger_1.logWarn)('LEARN', 'result', 'No patterns found yet. Use Claude Code more, then run --learn again.');
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
// Aggregate stats
|
|
436
|
+
let totalTokensInResults = 0;
|
|
437
|
+
let totalErrorFixes = 0;
|
|
438
|
+
let totalReadStorms = 0;
|
|
439
|
+
let totalRetries = 0;
|
|
440
|
+
let totalLargeResults = 0;
|
|
441
|
+
const toolBreakdown = {};
|
|
442
|
+
for (const p of allPatterns) {
|
|
443
|
+
totalTokensInResults += p.totalResultTokens;
|
|
444
|
+
totalErrorFixes += p.errorFixes.length;
|
|
445
|
+
totalReadStorms += Object.keys(p.readStorms).length;
|
|
446
|
+
totalRetries += p.retrySpirals.length;
|
|
447
|
+
totalLargeResults += p.largeResults.length;
|
|
448
|
+
for (const [key, stats] of Object.entries(p.toolBreakdown)) {
|
|
449
|
+
if (!toolBreakdown[key])
|
|
450
|
+
toolBreakdown[key] = { calls: 0, tokens: 0 };
|
|
451
|
+
toolBreakdown[key].calls += stats.calls;
|
|
452
|
+
toolBreakdown[key].tokens += stats.tokens;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
(0, logger_1.log)('LEARN', 'tokens', `Result tokens: ~${totalTokensInResults.toLocaleString()}`);
|
|
456
|
+
// Show tool breakdown
|
|
457
|
+
(0, logger_1.log)('LEARN', 'breakdown', 'Token usage by tool:');
|
|
458
|
+
const sorted = Object.entries(toolBreakdown).sort((a, b) => b[1].tokens - a[1].tokens);
|
|
459
|
+
for (const [name, stats] of sorted) {
|
|
460
|
+
const pct = Math.round((stats.tokens / totalTokensInResults) * 100) || 0;
|
|
461
|
+
(0, logger_1.log)('LEARN', 'tool', `${name.padEnd(14)} ${stats.tokens.toLocaleString()} tokens ${stats.calls} calls ${pct}%`);
|
|
462
|
+
}
|
|
463
|
+
(0, logger_1.log)('LEARN', 'patterns', `Error->Fix: ${totalErrorFixes}, Read storms: ${totalReadStorms}, Retry spirals: ${totalRetries}, Oversized: ${totalLargeResults}`);
|
|
464
|
+
// Generate and apply learnings
|
|
465
|
+
const learnings = generateLearnings(allPatterns);
|
|
466
|
+
if (learnings.length === 0) {
|
|
467
|
+
(0, logger_1.log)('LEARN', 'result', 'No new learnings to apply -- sessions look clean.');
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
(0, logger_1.log)('LEARN', 'generated', `Generated ${learnings.length} learnings`);
|
|
471
|
+
for (let i = 0; i < Math.min(learnings.length, 10); i++) {
|
|
472
|
+
const severity = learnings[i].severity === 'high' ? '!!' : ' !';
|
|
473
|
+
(0, logger_1.log)('LEARN', 'learning', `${severity} ${learnings[i].text.substring(0, 120)}`);
|
|
474
|
+
}
|
|
475
|
+
if (learnings.length > 10) {
|
|
476
|
+
(0, logger_1.log)('LEARN', 'learning', `... and ${learnings.length - 10} more`);
|
|
477
|
+
}
|
|
478
|
+
// Apply
|
|
479
|
+
const result = applyLearnings(cwd, learnings);
|
|
480
|
+
if (result.files.length > 0) {
|
|
481
|
+
(0, logger_1.log)('LEARN', 'applied', `Applied to: ${result.files.join(', ')}`);
|
|
482
|
+
(0, logger_1.log)('LEARN', 'info', 'Claude will see these learnings in every future session. Run --learn weekly.');
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
(0, logger_1.log)('LEARN', 'result', 'All learnings already applied. Nothing new to add.');
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=learning.js.map
|
package/dist/license.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getLicenseKey = getLicenseKey;
|
|
37
|
+
exports.isLicensed = isLicensed;
|
|
38
|
+
exports.saveLicenseKey = saveLicenseKey;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const CONFIG_DIR = path.join(os.homedir(), '.compress-claude');
|
|
43
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
44
|
+
function getLicenseKey() {
|
|
45
|
+
// Check env var COMPRESS_CLAUDE_KEY first, then ~/.compress-claude/config.json
|
|
46
|
+
return process.env.COMPRESS_CLAUDE_KEY || readConfigKey();
|
|
47
|
+
}
|
|
48
|
+
function isLicensed() {
|
|
49
|
+
return !!getLicenseKey();
|
|
50
|
+
}
|
|
51
|
+
function readConfigKey() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
54
|
+
const config = JSON.parse(raw);
|
|
55
|
+
return config.key;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function saveLicenseKey(key) {
|
|
62
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
63
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
let config = {};
|
|
66
|
+
try {
|
|
67
|
+
config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
config = {};
|
|
71
|
+
}
|
|
72
|
+
config.key = key;
|
|
73
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=license.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function compressContent(content: string): string;
|
|
2
|
+
export declare function isErrorContent(text: string): boolean;
|
|
3
|
+
export declare function compressToolResult(content: string, toolName?: string): string;
|
|
4
|
+
export declare function formatBytes(bytes: number): string;
|
|
5
|
+
//# sourceMappingURL=compressors.d.ts.map
|