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