chekk 0.5.5 → 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/dist/index.d.ts +17 -0
- package/dist/index.js +448 -0
- package/package.json +18 -34
- package/bin/chekk.js +0 -62
- package/src/detect.js +0 -146
- package/src/display.js +0 -1153
- package/src/index.js +0 -301
- package/src/insights.js +0 -661
- package/src/metrics/ai-leverage.js +0 -186
- package/src/metrics/debug-cycles.js +0 -204
- package/src/metrics/decomposition.js +0 -158
- package/src/metrics/session-structure.js +0 -199
- package/src/metrics/token-efficiency.js +0 -258
- package/src/parsers/claude-code.js +0 -231
- package/src/parsers/codex.js +0 -188
- package/src/parsers/cursor.js +0 -281
- package/src/scorer.js +0 -228
- package/src/upload.js +0 -140
package/src/index.js
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { spawn as spawnProcess } from 'child_process';
|
|
6
|
-
import { detectTools } from './detect.js';
|
|
7
|
-
import { parseAllProjects } from './parsers/claude-code.js';
|
|
8
|
-
import { parseAllWorkspaces } from './parsers/cursor.js';
|
|
9
|
-
import { parseAllSessions as parseCodexSessions } from './parsers/codex.js';
|
|
10
|
-
import { computeDecomposition } from './metrics/decomposition.js';
|
|
11
|
-
import { computeDebugCycles } from './metrics/debug-cycles.js';
|
|
12
|
-
import { computeAILeverage } from './metrics/ai-leverage.js';
|
|
13
|
-
import { computeSessionStructure } from './metrics/session-structure.js';
|
|
14
|
-
import { computeOverallScore } from './scorer.js';
|
|
15
|
-
import { computeTokenEfficiency } from './metrics/token-efficiency.js';
|
|
16
|
-
import {
|
|
17
|
-
computeSignatures,
|
|
18
|
-
computeWatchPoints,
|
|
19
|
-
computeTrajectory,
|
|
20
|
-
computeProjectComplexity,
|
|
21
|
-
generateAssessment,
|
|
22
|
-
computeConfidence,
|
|
23
|
-
} from './insights.js';
|
|
24
|
-
import {
|
|
25
|
-
displayHeader,
|
|
26
|
-
displayScan,
|
|
27
|
-
displayAnalysisStart,
|
|
28
|
-
displayProgressBar,
|
|
29
|
-
displayFull,
|
|
30
|
-
displayOffline,
|
|
31
|
-
displayVerbose,
|
|
32
|
-
} from './display.js';
|
|
33
|
-
import { generateProse, askVerbose, askClaim, uploadAndClaim } from './upload.js';
|
|
34
|
-
|
|
35
|
-
// ── Score history ──
|
|
36
|
-
const HISTORY_DIR = join(homedir(), '.chekk');
|
|
37
|
-
const HISTORY_FILE = join(HISTORY_DIR, 'history.json');
|
|
38
|
-
|
|
39
|
-
function loadHistory() {
|
|
40
|
-
try {
|
|
41
|
-
if (existsSync(HISTORY_FILE)) {
|
|
42
|
-
return JSON.parse(readFileSync(HISTORY_FILE, 'utf-8'));
|
|
43
|
-
}
|
|
44
|
-
} catch {}
|
|
45
|
-
return { scans: [] };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function saveHistory(history) {
|
|
49
|
-
try {
|
|
50
|
-
if (!existsSync(HISTORY_DIR)) mkdirSync(HISTORY_DIR, { recursive: true });
|
|
51
|
-
writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
|
|
52
|
-
} catch {}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function computePerToolScores(allSessions) {
|
|
56
|
-
const toolSessions = {};
|
|
57
|
-
for (const s of allSessions) {
|
|
58
|
-
const t = s.sourceTool || 'Unknown';
|
|
59
|
-
if (!toolSessions[t]) toolSessions[t] = [];
|
|
60
|
-
toolSessions[t].push(s);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const perTool = {};
|
|
64
|
-
for (const [tool, sessions] of Object.entries(toolSessions)) {
|
|
65
|
-
if (sessions.length < 2) {
|
|
66
|
-
perTool[tool] = { sessions: sessions.length, score: null, label: 'limited data' };
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const m = {
|
|
70
|
-
decomposition: computeDecomposition(sessions),
|
|
71
|
-
debugCycles: computeDebugCycles(sessions),
|
|
72
|
-
aiLeverage: computeAILeverage(sessions),
|
|
73
|
-
sessionStructure: computeSessionStructure(sessions),
|
|
74
|
-
};
|
|
75
|
-
const r = computeOverallScore(m);
|
|
76
|
-
perTool[tool] = { sessions: sessions.length, score: r.overall, label: null };
|
|
77
|
-
}
|
|
78
|
-
return perTool;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function run(options = {}) {
|
|
82
|
-
// ── Header ──
|
|
83
|
-
displayHeader();
|
|
84
|
-
|
|
85
|
-
// ── Step 1: Detect tools ──
|
|
86
|
-
const tools = detectTools();
|
|
87
|
-
|
|
88
|
-
if (tools.length === 0) {
|
|
89
|
-
console.log(chalk.dim(' No AI coding tools detected.'));
|
|
90
|
-
console.log(chalk.dim(' Chekk supports Claude Code, Cursor, and Codex.\n'));
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
displayScan(tools);
|
|
95
|
-
|
|
96
|
-
// ── Step 2: Parse sessions (tag each with sourceTool) ──
|
|
97
|
-
let allSessions = [];
|
|
98
|
-
for (const tool of tools) {
|
|
99
|
-
let parsed = [];
|
|
100
|
-
if (tool.tool === 'Claude Code') {
|
|
101
|
-
parsed = parseAllProjects(tool.basePath);
|
|
102
|
-
} else if (tool.tool === 'Cursor') {
|
|
103
|
-
parsed = parseAllWorkspaces(tool.basePath);
|
|
104
|
-
} else if (tool.tool === 'Codex') {
|
|
105
|
-
parsed = parseCodexSessions(tool.basePath);
|
|
106
|
-
}
|
|
107
|
-
for (const s of parsed) {
|
|
108
|
-
s.sourceTool = tool.tool;
|
|
109
|
-
}
|
|
110
|
-
allSessions.push(...parsed);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (allSessions.length === 0) {
|
|
114
|
-
console.log(chalk.dim(' No sessions found to analyze.\n'));
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Stats
|
|
119
|
-
const totalExchanges = allSessions.reduce((sum, s) => sum + s.exchangeCount, 0);
|
|
120
|
-
const projects = [...new Set(allSessions.map(s => s.project))];
|
|
121
|
-
const allTimestamps = allSessions
|
|
122
|
-
.map(s => s.startTime)
|
|
123
|
-
.filter(Boolean)
|
|
124
|
-
.map(t => new Date(t))
|
|
125
|
-
.sort((a, b) => a - b);
|
|
126
|
-
|
|
127
|
-
const dateRangeShort = allTimestamps.length >= 2
|
|
128
|
-
? formatDateRange(allTimestamps[0], allTimestamps[allTimestamps.length - 1])
|
|
129
|
-
: 'recent history';
|
|
130
|
-
|
|
131
|
-
const dateRangeFull = allTimestamps.length >= 2
|
|
132
|
-
? `${allTimestamps[0].toLocaleDateString()} to ${allTimestamps[allTimestamps.length - 1].toLocaleDateString()}`
|
|
133
|
-
: 'unknown';
|
|
134
|
-
|
|
135
|
-
displayAnalysisStart(dateRangeShort);
|
|
136
|
-
|
|
137
|
-
// ── Step 3: Compute metrics ──
|
|
138
|
-
const metrics = {
|
|
139
|
-
decomposition: computeDecomposition(allSessions),
|
|
140
|
-
debugCycles: computeDebugCycles(allSessions),
|
|
141
|
-
aiLeverage: computeAILeverage(allSessions),
|
|
142
|
-
sessionStructure: computeSessionStructure(allSessions),
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const result = computeOverallScore(metrics);
|
|
146
|
-
|
|
147
|
-
// ── Step 3a: Compute token efficiency analytics ──
|
|
148
|
-
const tokenEfficiency = computeTokenEfficiency(allSessions);
|
|
149
|
-
|
|
150
|
-
// ── Cross-platform scores ──
|
|
151
|
-
const perToolScores = tools.length > 1 ? computePerToolScores(allSessions) : null;
|
|
152
|
-
|
|
153
|
-
// ── Score delta (compare to last scan) ──
|
|
154
|
-
const history = loadHistory();
|
|
155
|
-
const lastScan = history.scans.length > 0 ? history.scans[history.scans.length - 1] : null;
|
|
156
|
-
const scoreDelta = lastScan ? result.overall - lastScan.overall : null;
|
|
157
|
-
|
|
158
|
-
// Save current scan
|
|
159
|
-
history.scans.push({
|
|
160
|
-
overall: result.overall,
|
|
161
|
-
tier: result.tier,
|
|
162
|
-
archetype: result.archetype.name,
|
|
163
|
-
scores: result.scores,
|
|
164
|
-
date: new Date().toISOString(),
|
|
165
|
-
});
|
|
166
|
-
// Keep last 20 scans
|
|
167
|
-
if (history.scans.length > 20) history.scans = history.scans.slice(-20);
|
|
168
|
-
saveHistory(history);
|
|
169
|
-
|
|
170
|
-
const sessionStats = {
|
|
171
|
-
totalSessions: allSessions.length,
|
|
172
|
-
totalExchanges,
|
|
173
|
-
projectCount: projects.length,
|
|
174
|
-
dateRange: dateRangeFull,
|
|
175
|
-
dateRangeShort,
|
|
176
|
-
tools: tools.map(t => t.tool),
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// ── Step 3b: Compute insights ──
|
|
180
|
-
const signatures = computeSignatures(allSessions, metrics, tokenEfficiency);
|
|
181
|
-
const watchPoints = computeWatchPoints(allSessions, metrics, tokenEfficiency);
|
|
182
|
-
const trajectory = computeTrajectory(allSessions);
|
|
183
|
-
const projectComplexity = computeProjectComplexity(allSessions);
|
|
184
|
-
const assessment = generateAssessment(result, metrics, signatures, watchPoints);
|
|
185
|
-
const confidence = computeConfidence(sessionStats);
|
|
186
|
-
|
|
187
|
-
const insights = { signatures, watchPoints, trajectory, projectComplexity, assessment, confidence };
|
|
188
|
-
|
|
189
|
-
// ── JSON output ──
|
|
190
|
-
if (options.json) {
|
|
191
|
-
console.log(JSON.stringify({ metrics, result, sessionStats, perToolScores, scoreDelta, insights, tokenEfficiency }, null, 2));
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ── Step 4: Progress bar + API call in parallel ──
|
|
196
|
-
let prose = null;
|
|
197
|
-
if (!options.offline) {
|
|
198
|
-
const [, proseResult] = await Promise.all([
|
|
199
|
-
displayProgressBar(1500),
|
|
200
|
-
generateProse(metrics, result, sessionStats, tokenEfficiency).catch(() => null),
|
|
201
|
-
]);
|
|
202
|
-
prose = proseResult;
|
|
203
|
-
} else {
|
|
204
|
-
await displayProgressBar(1500);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ── Step 5: Display results ──
|
|
208
|
-
// Capture output to a buffer so we can page it from the top
|
|
209
|
-
const outputChunks = [];
|
|
210
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
211
|
-
const origLog = console.log.bind(console);
|
|
212
|
-
const capture = (chunk) => { outputChunks.push(typeof chunk === 'string' ? chunk : String(chunk)); };
|
|
213
|
-
process.stdout.write = function(chunk, ...args) { capture(chunk); return true; };
|
|
214
|
-
console.log = function(...args) { capture(args.join(' ') + '\n'); };
|
|
215
|
-
|
|
216
|
-
const extra = { scoreDelta, perToolScores, insights, sessionStats, tokenEfficiency };
|
|
217
|
-
if (options.offline) {
|
|
218
|
-
displayOffline(result, metrics, extra);
|
|
219
|
-
} else {
|
|
220
|
-
displayFull(result, metrics, prose, extra);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Include verbose in the paged output
|
|
224
|
-
if (options.verbose) {
|
|
225
|
-
displayVerbose(metrics, allSessions, tokenEfficiency);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Restore stdout
|
|
229
|
-
process.stdout.write = origWrite;
|
|
230
|
-
console.log = origLog;
|
|
231
|
-
|
|
232
|
-
const fullOutput = outputChunks.join('');
|
|
233
|
-
|
|
234
|
-
// ── Page output through less (or fallback to direct print) ──
|
|
235
|
-
if (process.stdout.isTTY && !options.json) {
|
|
236
|
-
await new Promise((resolve) => {
|
|
237
|
-
const pager = spawnProcess('less', ['-R', '--quit-if-one-screen'], {
|
|
238
|
-
stdio: ['pipe', process.stdout, process.stderr],
|
|
239
|
-
});
|
|
240
|
-
pager.stdin.write(fullOutput);
|
|
241
|
-
pager.stdin.end();
|
|
242
|
-
pager.on('error', () => {
|
|
243
|
-
// less not available — just print directly
|
|
244
|
-
process.stdout.write(fullOutput);
|
|
245
|
-
resolve();
|
|
246
|
-
});
|
|
247
|
-
pager.on('exit', () => resolve());
|
|
248
|
-
});
|
|
249
|
-
} else {
|
|
250
|
-
process.stdout.write(fullOutput);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ── Step 6: Verbose prompt (interactive, if not already shown) ──
|
|
254
|
-
if (!options.verbose) {
|
|
255
|
-
try {
|
|
256
|
-
const wantsVerbose = await askVerbose();
|
|
257
|
-
if (wantsVerbose) {
|
|
258
|
-
console.log();
|
|
259
|
-
displayVerbose(metrics, allSessions, tokenEfficiency);
|
|
260
|
-
} else {
|
|
261
|
-
console.log();
|
|
262
|
-
}
|
|
263
|
-
} catch {
|
|
264
|
-
console.log();
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// ── Step 7: Claim prompt ──
|
|
269
|
-
if (options.upload !== false) {
|
|
270
|
-
try {
|
|
271
|
-
const wantsClaim = await askClaim();
|
|
272
|
-
if (wantsClaim) {
|
|
273
|
-
try {
|
|
274
|
-
const claimResult = await uploadAndClaim(metrics, result, sessionStats);
|
|
275
|
-
console.log();
|
|
276
|
-
console.log(` ${chalk.green('\u2713')} ${chalk.dim('Profile created:')} ${chalk.cyan.underline(claimResult.claimUrl || 'https://chekk.dev/claim')}`);
|
|
277
|
-
console.log();
|
|
278
|
-
} catch (err) {
|
|
279
|
-
console.log();
|
|
280
|
-
console.log(chalk.dim(` Could not create profile \u2014 try again later`));
|
|
281
|
-
console.log();
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
console.log();
|
|
285
|
-
console.log(chalk.dim(' Run `npx chekk` again anytime.\n'));
|
|
286
|
-
}
|
|
287
|
-
} catch {
|
|
288
|
-
console.log();
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function formatDateRange(start, end) {
|
|
294
|
-
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
295
|
-
const s = `${months[start.getMonth()]} ${start.getDate()}`;
|
|
296
|
-
const e = `${months[end.getMonth()]} ${end.getDate()}, ${end.getFullYear()}`;
|
|
297
|
-
if (start.getFullYear() === end.getFullYear()) {
|
|
298
|
-
return `${s} \u2013 ${e}`;
|
|
299
|
-
}
|
|
300
|
-
return `${s}, ${start.getFullYear()} \u2013 ${e}`;
|
|
301
|
-
}
|