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/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
- }