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/display.js DELETED
@@ -1,1153 +0,0 @@
1
- import chalk from 'chalk';
2
- import { BENCHMARKS, DIM_RANGES } from './insights.js';
3
-
4
- // ── Color palette ──
5
- const gold = chalk.hex('#FFD700');
6
- const purple = chalk.hex('#A855F7');
7
- const blue = chalk.hex('#3B82F6');
8
- const green = chalk.hex('#22C55E');
9
- const cyan = chalk.hex('#06B6D4');
10
- const orange = chalk.hex('#F97316');
11
- const red = chalk.hex('#EF4444');
12
- const yellow = chalk.hex('#EAB308');
13
- const dim = chalk.dim;
14
- const bold = chalk.bold;
15
- const white = chalk.white;
16
-
17
- function tierColor(tier) {
18
- if (tier === 'LEGENDARY') return gold;
19
- if (tier === 'ULTRA RARE') return purple;
20
- if (tier === 'RARE') return cyan;
21
- if (tier === 'UNCOMMON') return blue;
22
- return dim;
23
- }
24
-
25
- function scoreColor(score) {
26
- if (score >= 85) return green;
27
- if (score >= 70) return cyan;
28
- if (score >= 55) return yellow;
29
- if (score >= 40) return orange;
30
- return red;
31
- }
32
-
33
- function progressBar(score, width = 18) {
34
- const filled = Math.round((score / 100) * width);
35
- const empty = width - filled;
36
- return scoreColor(score)('\u2588'.repeat(filled)) + dim('\u2591'.repeat(empty));
37
- }
38
-
39
- function numberFormat(n) {
40
- if (n >= 1_000_000_000) return (n / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
41
- if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
42
- if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
43
- return String(n);
44
- }
45
-
46
- // Measure visible display width accounting for wide characters (emoji, CJK)
47
- function visibleWidth(str) {
48
- const stripped = str.replace(/\u001b\[[0-9;]*m/g, '');
49
- let width = 0;
50
- for (const ch of stripped) {
51
- const code = ch.codePointAt(0);
52
- // Emoji and symbols that take 2 terminal columns
53
- if (code > 0x1F000 || // emoji block
54
- (code >= 0x2600 && code <= 0x27BF) || // misc symbols
55
- (code >= 0x2B50 && code <= 0x2B55) || // stars
56
- (code >= 0xFE00 && code <= 0xFE0F) || // variation selectors
57
- (code >= 0x1F300 && code <= 0x1FAFF)) { // extended emoji
58
- width += 2;
59
- } else {
60
- width += 1;
61
- }
62
- }
63
- return width;
64
- }
65
-
66
- function pad(str, len) {
67
- return str + ' '.repeat(Math.max(0, len - visibleWidth(str)));
68
- }
69
-
70
- // ── Qualitative tier labels for dimensions ──
71
- function dimTierLabel(score) {
72
- if (score >= 80) return 'EXCEPTIONAL';
73
- if (score >= 65) return 'STRONG';
74
- if (score >= 50) return 'SOLID';
75
- if (score >= 35) return 'DEVELOPING';
76
- return 'NEEDS WORK';
77
- }
78
-
79
- function dimTierColor(score) {
80
- if (score >= 80) return green;
81
- if (score >= 65) return cyan;
82
- if (score >= 50) return yellow;
83
- if (score >= 35) return orange;
84
- return red;
85
- }
86
-
87
- // ── Snippet helpers ──
88
-
89
- function cleanPrompt(prompt) {
90
- if (!prompt) return null;
91
- return prompt.replace(/\s+/g, ' ').trim();
92
- }
93
-
94
- function displayLabeledSnippet(label, prompt) {
95
- const s = cleanPrompt(prompt);
96
- if (!s) return;
97
- // Wrap the full prompt across multiple lines instead of truncating
98
- const prefix = `${dim('\u21B3')} ${dim(label + ':')} `;
99
- const quoted = `\u201C${s}\u201D`;
100
- const lines = wrapText(quoted, 53);
101
- console.log(` ${prefix}${dim.italic(lines[0])}`);
102
- for (let i = 1; i < lines.length; i++) {
103
- console.log(` ${dim.italic(lines[i])}`);
104
- }
105
- }
106
-
107
- // Cross-dimension filters: reject prompts that clearly belong to another dimension
108
- const architecturalRe = /\b(architect|design|refactor|redesign|restructure|system design|data model|schema|api design|infrastructure|migration|strategy)\b/i;
109
- const debugRe = /\b(error|bug|broken|crash|fail|exception|traceback|stack trace|doesn'?t work|not working|TypeError|SyntaxError|ImportError|ReferenceError|500|502|503|404|CORS)\b/i;
110
- const planningRe = /\b(plan|breakdown|break down|think through|help me think|pros and cons|how should|code review|audit)\b/i;
111
-
112
- // For each dimension, prompts matching these patterns are *excluded* as evidence
113
- const dimensionExclusions = {
114
- 'specific_report': [architecturalRe, planningRe],
115
- 'quick_fix': [architecturalRe, planningRe],
116
- 'architectural': [debugRe],
117
- 'planning': [debugRe],
118
- 'exploratory': [debugRe],
119
- 'decomposition': [],
120
- 'followup': [],
121
- 'context_setting': [],
122
- 'refinement': [],
123
- };
124
-
125
- function pickExample(examples, type) {
126
- if (!examples || !examples.length) return null;
127
- const exclusions = dimensionExclusions[type] || [];
128
- // Prefer a match that doesn't trigger exclusion patterns
129
- const candidates = examples.filter(e => e.type === type);
130
- if (candidates.length === 0) return null;
131
- const clean = candidates.find(e => !exclusions.some(re => re.test(e.prompt)));
132
- return (clean || candidates[0]).prompt;
133
- }
134
-
135
- // ── Box drawing ──
136
-
137
- function box(lines, width = 47) {
138
- const out = [];
139
- out.push(dim(' \u250C' + '\u2500'.repeat(width) + '\u2510'));
140
- for (const line of lines) {
141
- const padding = Math.max(0, width - visibleWidth(line));
142
- out.push(dim(' \u2502') + line + ' '.repeat(padding) + dim('\u2502'));
143
- }
144
- out.push(dim(' \u2514' + '\u2500'.repeat(width) + '\u2518'));
145
- return out;
146
- }
147
-
148
- function doubleRule(width = 53) {
149
- return dim(' ' + '\u2550'.repeat(width));
150
- }
151
-
152
- function wrapText(text, maxWidth = 49) {
153
- const words = text.split(' ');
154
- const lines = [];
155
- let current = '';
156
- for (const word of words) {
157
- if (current.length + word.length + 1 > maxWidth) {
158
- lines.push(current);
159
- current = word;
160
- } else {
161
- current = current ? current + ' ' + word : word;
162
- }
163
- }
164
- if (current) lines.push(current);
165
- return lines;
166
- }
167
-
168
- // ══════════════════════════════════════════════
169
- // HEADER — Scanning + progress
170
- // ══════════════════════════════════════════════
171
-
172
- export function displayHeader() {
173
- console.log();
174
- const lines = [
175
- '',
176
- ` ${bold.white('chekk')}${dim(' v0.5.5')}`,
177
- ` ${dim('prompt engineering capability profile')}`,
178
- '',
179
- ];
180
- for (const l of box(lines, 45)) console.log(l);
181
- console.log();
182
- }
183
-
184
- export function displayScan(tools) {
185
- console.log(dim(' Scanning local AI tools...\n'));
186
- for (const tool of tools) {
187
- const sessions = numberFormat(tool.sessions);
188
- const projects = tool.projects.length;
189
- console.log(
190
- ` ${cyan('\u2726')} ${bold.white(tool.tool)} ` +
191
- dim(`${sessions} sessions \u00B7 ${projects} projects`)
192
- );
193
- }
194
- console.log();
195
- }
196
-
197
- export function displayAnalysisStart(dateRange) {
198
- console.log(` ${dim('Analyzing')} ${white(dateRange)}${dim('...\n')}`);
199
- }
200
-
201
- export async function displayProgressBar(durationMs = 2000) {
202
- const width = 40;
203
- const steps = 40;
204
- const stepTime = durationMs / steps;
205
- for (let i = 0; i <= steps; i++) {
206
- const filled = i;
207
- const empty = width - filled;
208
- const pct = Math.round((i / steps) * 100);
209
- const bar = green('\u2588'.repeat(filled)) + dim('\u2591'.repeat(empty));
210
- process.stdout.write(`\r ${bar} ${dim(pct + '%')}`);
211
- if (i < steps) await sleep(stepTime);
212
- }
213
- console.log();
214
- console.log();
215
- console.log(` ${green('\u2713')} ${dim('Profile generated.')}`);
216
- await sleep(600);
217
- }
218
-
219
- // ══════════════════════════════════════════════
220
- // PROFILE HEADER — Official document feel
221
- // ══════════════════════════════════════════════
222
-
223
- function displayProfileHeader(result, extra = {}) {
224
- const { sessionStats } = extra;
225
- const now = new Date();
226
- const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
227
- const dateStr = `${months[now.getMonth()]} ${now.getDate()}, ${now.getFullYear()}`;
228
-
229
- console.log(doubleRule());
230
- console.log();
231
- console.log(` ${bold.white('PROMPT ENGINEERING CAPABILITY PROFILE')}`);
232
- console.log();
233
- if (sessionStats) {
234
- console.log(` ${dim(`Generated ${dateStr} | chekk v0.5.5`)}`);
235
- console.log(` ${dim(`Analysis: ${sessionStats.totalSessions} sessions \u00B7 ${sessionStats.tools.length} tool${sessionStats.tools.length > 1 ? 's' : ''} \u00B7 ${numberFormat(sessionStats.totalExchanges)} exchanges`)}`);
236
- if (sessionStats.dateRangeShort) {
237
- console.log(` ${dim(`Period: ${sessionStats.dateRangeShort}`)}`);
238
- }
239
- }
240
- console.log();
241
- console.log(doubleRule());
242
- }
243
-
244
- // ══════════════════════════════════════════════
245
- // SUMMARY BLOCK
246
- // ══════════════════════════════════════════════
247
-
248
- function displaySummary(result, extra = {}) {
249
- const { overall, scores, archetype, tier, tierBadge, tierPercentile } = result;
250
- const { scoreDelta, perToolScores, insights, sessionStats } = extra;
251
- const tc = tierColor(tier);
252
-
253
- // Find strongest and weakest
254
- const dims = [
255
- { label: 'Thinking', score: scores.decomposition },
256
- { label: 'Debugging', score: scores.debugCycles },
257
- { label: 'AI Leverage', score: scores.aiLeverage },
258
- { label: 'Workflow', score: scores.sessionStructure },
259
- ];
260
- dims.sort((a, b) => b.score - a.score);
261
- const strongest = dims[0];
262
- const weakest = dims[dims.length - 1];
263
-
264
- console.log();
265
- console.log(dim(' SUMMARY'));
266
-
267
- const badge = tierBadge || '';
268
- const toolsList = sessionStats ? sessionStats.tools.join(' \u00B7 ') : '';
269
- const sessionCount = sessionStats ? `${sessionStats.totalSessions} across ${sessionStats.projectCount} projects` : '';
270
- const period = sessionStats?.dateRangeShort || '';
271
-
272
- // Delta string
273
- let deltaStr = '';
274
- if (scoreDelta !== null && scoreDelta !== undefined) {
275
- if (scoreDelta > 0) deltaStr = ` ${green('\u2191 +' + scoreDelta)}`;
276
- else if (scoreDelta < 0) deltaStr = ` ${orange('\u2193 ' + scoreDelta)}`;
277
- }
278
-
279
- const summaryLines = [
280
- '',
281
- ` ${dim('Score')} ${scoreColor(overall).bold(String(overall))}${deltaStr}`,
282
- ` ${dim('Tier')} ${tc(tier)} ${badge} ${dim('\u2014')} ${dim(tierPercentile)}`,
283
- ` ${dim('Archetype')} ${bold.white(archetype.name)}`,
284
- ];
285
- if (toolsList) summaryLines.push(` ${dim('Tools')} ${dim(toolsList)}`);
286
- if (sessionCount) summaryLines.push(` ${dim('Sessions')} ${dim(sessionCount)}`);
287
- if (period) summaryLines.push(` ${dim('Period')} ${dim(period)}`);
288
- summaryLines.push(` ${dim('Strongest')} ${dim(strongest.label + ' (' + dimTierLabel(strongest.score) + ')')}`);
289
- summaryLines.push(` ${dim('Growth Area')} ${dim(weakest.label + ' (' + dimTierLabel(weakest.score) + ')')}`);
290
- summaryLines.push('');
291
-
292
- for (const l of box(summaryLines, 53)) console.log(l);
293
- console.log();
294
- }
295
-
296
- // ══════════════════════════════════════════════
297
- // ARCHETYPE DEFINITION
298
- // ══════════════════════════════════════════════
299
-
300
- function displayArchetype(result) {
301
- const { archetype } = result;
302
- console.log(` ${bold.white(archetype.name)}`);
303
- console.log();
304
- if (archetype.description) {
305
- const lines = wrapText(archetype.description, 51);
306
- for (const line of lines) {
307
- console.log(` ${dim(line)}`);
308
- }
309
- }
310
- if (archetype.distribution) {
311
- console.log();
312
- console.log(` ${dim('Distribution: ' + archetype.distribution + ' of scored engineers')}`);
313
- }
314
- console.log();
315
- }
316
-
317
- // ══════════════════════════════════════════════
318
- // DIMENSIONS — with ranges and qualitative labels
319
- // ══════════════════════════════════════════════
320
-
321
- function displayDimensions(result) {
322
- const { scores } = result;
323
- console.log(dim(' DIMENSIONS'));
324
- console.log();
325
-
326
- // Score weight transparency
327
- console.log(` ${dim('Weights: Thinking 25% | Debugging 25% | AI Leverage 30% | Workflow 20%')}`);
328
- console.log();
329
-
330
- function dimLine(label, key, score) {
331
- const labelStr = bold(label);
332
- const labelVisible = label.length;
333
- const labelPad = ' '.repeat(Math.max(0, 15 - labelVisible));
334
- const tierLabel = dimTierLabel(score);
335
- const tierStr = dimTierColor(score)(pad(tierLabel, 11));
336
- const range = DIM_RANGES[key];
337
- const rangeStr = dim(`range: ${range.min}-${range.max} | you: ${score}`);
338
- return [
339
- ` ${labelStr}${labelPad}${progressBar(score)} ${tierStr}`,
340
- ` ${' '.repeat(15)}${rangeStr}`,
341
- ];
342
- }
343
-
344
- const allLines = [''];
345
- allLines.push(...dimLine('Thinking', 'decomposition', scores.decomposition));
346
- allLines.push('');
347
- allLines.push(...dimLine('Debugging', 'debugCycles', scores.debugCycles));
348
- allLines.push('');
349
- allLines.push(...dimLine('AI Leverage', 'aiLeverage', scores.aiLeverage));
350
- allLines.push('');
351
- allLines.push(...dimLine('Workflow', 'sessionStructure', scores.sessionStructure));
352
- allLines.push('');
353
-
354
- for (const l of box(allLines, 53)) console.log(l);
355
- console.log();
356
- }
357
-
358
- // ══════════════════════════════════════════════
359
- // TOKEN EFFICIENCY — Spend overview panel
360
- // ══════════════════════════════════════════════
361
-
362
- export function displayTokenEfficiency(tokenEfficiency, metrics) {
363
- if (!tokenEfficiency || !tokenEfficiency.hasData) return;
364
-
365
- const te = tokenEfficiency;
366
- console.log(dim(' TOKEN EFFICIENCY'));
367
- console.log();
368
-
369
- // ── Overview stats ──
370
- const overviewLines = [
371
- '',
372
- ` ${dim('Total tokens')} ${bold(numberFormat(te.grandTotal))}`,
373
- ` ${dim('Est. cost')} ${bold('$' + te.estimatedCostTotal.toFixed(2))}`,
374
- ` ${dim('Sessions')} ${dim(String(te.sessionsAnalyzed))}`,
375
- ` ${dim('Avg/exchange')} ${dim(numberFormat(te.avgTokensPerExchange) + ' tokens')}`,
376
- '',
377
- ];
378
-
379
- // Token composition bar — ensure every non-zero category gets at least 1 block
380
- const barWidth = 40;
381
- const categories = [
382
- { pct: te.composition.cacheReadPct, color: orange, label: 'context re-read' },
383
- { pct: te.composition.cacheCreationPct, color: yellow, label: 'cache create' },
384
- { pct: te.composition.inputPct, color: blue, label: 'new input' },
385
- { pct: te.composition.outputPct, color: green, label: 'output (code)' },
386
- ];
387
-
388
- // Allocate bar widths: give at least 1 block to any non-zero category
389
- let remaining = barWidth;
390
- const widths = categories.map(c => {
391
- if (c.pct > 0 && c.pct < (100 / barWidth)) { remaining--; return 1; }
392
- return 0;
393
- });
394
- for (let i = 0; i < categories.length; i++) {
395
- if (widths[i] === 0 && categories[i].pct > 0) {
396
- widths[i] = Math.max(1, Math.round(categories[i].pct / 100 * barWidth));
397
- }
398
- }
399
- // Adjust largest to fill remaining
400
- const total = widths.reduce((a, b) => a + b, 0);
401
- if (total !== barWidth) {
402
- const largest = widths.indexOf(Math.max(...widths));
403
- widths[largest] += barWidth - total;
404
- }
405
-
406
- let barStr = '';
407
- for (let i = 0; i < categories.length; i++) {
408
- barStr += categories[i].color('\u2588'.repeat(Math.max(0, widths[i])));
409
- }
410
-
411
- overviewLines.push(` ${barStr}`);
412
-
413
- // Format percentages with appropriate precision
414
- function fmtPct(pct) {
415
- if (pct >= 99.5) return pct.toFixed(1) + '%';
416
- if (pct >= 10) return Math.round(pct) + '%';
417
- if (pct >= 1) return pct.toFixed(1) + '%';
418
- if (pct > 0) return pct.toFixed(2) + '%';
419
- return '0%';
420
- }
421
-
422
- overviewLines.push(` ${orange('\u2588')} ${dim('context re-read ' + fmtPct(te.composition.cacheReadPct))} ` +
423
- `${yellow('\u2588')} ${dim('cache create ' + fmtPct(te.composition.cacheCreationPct))}`);
424
- overviewLines.push(` ${blue('\u2588')} ${dim('new input ' + fmtPct(te.composition.inputPct))} ` +
425
- `${green('\u2588')} ${dim('output ' + fmtPct(te.composition.outputPct))}`);
426
- overviewLines.push('');
427
-
428
- // The key insight — use composition percentages for accuracy
429
- const outputPct = te.composition.outputPct;
430
- const nonOutputPct = 100 - outputPct;
431
- if (outputPct < 50) {
432
- overviewLines.push(` ${dim('Only')} ${bold(fmtPct(outputPct))} ${dim('of tokens are Claude writing code.')}`);
433
- overviewLines.push(` ${dim('The other')} ${bold(fmtPct(nonOutputPct))} ${dim('is context re-reading.')}`);
434
- overviewLines.push('');
435
- }
436
-
437
- for (const l of box(overviewLines, 53)) console.log(l);
438
- console.log();
439
-
440
- // ── Per-project breakdown ──
441
- if (te.perProject.length > 1) {
442
- console.log(` ${dim('SPEND BY PROJECT')}`);
443
- console.log(` ${dim('\u2500'.repeat(53))}`);
444
- for (const p of te.perProject.slice(0, 5)) {
445
- const pctOfTotal = te.grandTotal > 0 ? Math.round(p.totalTokens / te.grandTotal * 100) : 0;
446
- const costStr = '$' + p.estimatedCost.toFixed(2);
447
- const shortName = p.name.length > 24 ? '...' + p.name.slice(-21) : p.name;
448
- console.log(
449
- ` ${pad(white(shortName), 26)} ${pad(dim(numberFormat(p.totalTokens) + ' tokens'), 16)} ` +
450
- `${pad(dim(costStr), 8)} ${dim(pctOfTotal + '%')}`
451
- );
452
- }
453
- console.log();
454
- }
455
-
456
- // ── Costliest sessions ──
457
- if (te.costliestSessions.length > 0) {
458
- console.log(` ${dim('COSTLIEST SESSIONS')}`);
459
- console.log(` ${dim('\u2500'.repeat(53))}`);
460
- for (const s of te.costliestSessions.slice(0, 3)) {
461
- const costStr = '$' + s.estimatedCost.toFixed(2);
462
- const truncPrompt = s.firstPrompt.length > 40 ? s.firstPrompt.slice(0, 37) + '...' : s.firstPrompt;
463
- console.log(
464
- ` ${dim(numberFormat(s.totalTokens) + ' tokens')} ${dim(costStr)} ${dim(s.exchanges + ' exchanges')}`
465
- );
466
- if (truncPrompt) {
467
- console.log(` ${dim('\u21B3')} ${dim.italic('\u201C' + truncPrompt + '\u201D')}`);
468
- }
469
- console.log();
470
- }
471
- }
472
-
473
- // ── Token cost evidence from metrics ──
474
- displayTokenEvidence(metrics, tokenEfficiency);
475
- }
476
-
477
- // ── Per-dimension cost annotation ──
478
- // Shows a single cost insight line under each scored dimension
479
-
480
- function dollarFormat(amount) {
481
- if (amount >= 1000) return '$' + (amount / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
482
- if (amount >= 100) return '$' + Math.round(amount);
483
- if (amount >= 1) return '$' + amount.toFixed(2);
484
- return '$' + amount.toFixed(2);
485
- }
486
-
487
- function displayDimensionCostLine(dimensionName, metrics, tokenEfficiency) {
488
- if (!tokenEfficiency || !tokenEfficiency.hasData) return;
489
-
490
- const pricing = tokenEfficiency.estimatedCostTotal / Math.max(1, tokenEfficiency.grandTotal);
491
-
492
- if (dimensionName === 'thinking') {
493
- const de = metrics.decomposition.details.tokenEvidence;
494
- if (de && de.avgTokensPerExchangeSingleShot && de.avgTokensPerExchangeMultiStep) {
495
- const ssCost = de.avgTokensPerExchangeSingleShot * pricing;
496
- const msCost = de.avgTokensPerExchangeMultiStep * pricing;
497
- if (ssCost > msCost * 1.1) {
498
- console.log(` ${dim('\u21B3 Cost:')} ${dim('single-shot')} ${orange(dollarFormat(ssCost) + '/exchange')} ${dim('vs multi-step')} ${green(dollarFormat(msCost) + '/exchange')}`);
499
- }
500
- }
501
- }
502
-
503
- if (dimensionName === 'debugging') {
504
- const dbe = metrics.debugCycles.details.tokenEvidence;
505
- if (dbe && dbe.avgTokensQuickFix && dbe.avgTokensLongLoop) {
506
- const qfCost = dbe.avgTokensQuickFix * pricing;
507
- const llCost = dbe.avgTokensLongLoop * pricing;
508
- console.log(` ${dim('\u21B3 Cost:')} ${dim('quick fix')} ${green(dollarFormat(qfCost))} ${dim('vs debug spiral')} ${orange(dollarFormat(llCost))}`);
509
- } else if (dbe && dbe.avgTokensSpecificDebug) {
510
- const spCost = dbe.avgTokensSpecificDebug * pricing;
511
- console.log(` ${dim('\u21B3 Cost:')} ${dim('avg debug cycle')} ${dim(dollarFormat(spCost))}`);
512
- }
513
- }
514
-
515
- if (dimensionName === 'ai leverage') {
516
- const aie = metrics.aiLeverage.details.tokenEvidence;
517
- if (aie && aie.avgTokensArchitectural && aie.avgTokensBoilerplate) {
518
- const archCost = aie.avgTokensArchitectural * pricing;
519
- const boilCost = aie.avgTokensBoilerplate * pricing;
520
- console.log(` ${dim('\u21B3 Cost:')} ${dim('architectural prompt')} ${dim(dollarFormat(archCost))} ${dim('vs boilerplate')} ${dim(dollarFormat(boilCost))}`);
521
- } else if (aie && aie.avgTokensTrivialPrompt && aie.avgTokensComplexPrompt) {
522
- const trivCost = aie.avgTokensTrivialPrompt * pricing;
523
- const compCost = aie.avgTokensComplexPrompt * pricing;
524
- console.log(` ${dim('\u21B3 Cost:')} ${dim('trivial prompt')} ${dim(dollarFormat(trivCost))} ${dim('vs detailed')} ${dim(dollarFormat(compCost))}`);
525
- }
526
- }
527
-
528
- if (dimensionName === 'workflow') {
529
- const sse = metrics.sessionStructure.details.tokenEvidence;
530
- if (sse && sse.avgTokensPerExchangeMarathon && sse.avgTokensPerExchangeFocused) {
531
- const marCost = sse.avgTokensPerExchangeMarathon * pricing;
532
- const focCost = sse.avgTokensPerExchangeFocused * pricing;
533
- if (marCost > focCost * 1.1) {
534
- console.log(` ${dim('\u21B3 Cost:')} ${dim('marathon')} ${orange(dollarFormat(marCost) + '/exchange')} ${dim('vs focused')} ${green(dollarFormat(focCost) + '/exchange')}`);
535
- }
536
- }
537
- }
538
- }
539
-
540
- function displayTokenEvidence(metrics, tokenEfficiency) {
541
- const evidenceLines = [];
542
- const pricing = tokenEfficiency && tokenEfficiency.hasData
543
- ? tokenEfficiency.estimatedCostTotal / Math.max(1, tokenEfficiency.grandTotal) : 0;
544
-
545
- // Decomposition: single-shot vs multi-step cost
546
- const de = metrics.decomposition.details.tokenEvidence;
547
- if (de && de.avgTokensPerExchangeSingleShot && de.avgTokensPerExchangeMultiStep) {
548
- const ratio = (de.avgTokensPerExchangeSingleShot / de.avgTokensPerExchangeMultiStep).toFixed(1);
549
- if (parseFloat(ratio) > 1.2) {
550
- if (pricing > 0) {
551
- const ssCost = dollarFormat(de.avgTokensPerExchangeSingleShot * pricing);
552
- const msCost = dollarFormat(de.avgTokensPerExchangeMultiStep * pricing);
553
- evidenceLines.push(
554
- ` ${dim('\u2022 Single-shot prompts cost')} ${orange(ssCost + '/exchange')} ${dim('vs')} ${green(msCost)} ${dim('in multi-step sessions')} ${dim('(' + ratio + 'x)')}`
555
- );
556
- } else {
557
- evidenceLines.push(
558
- ` ${dim('\u2022 Single-shot prompts cost')} ${orange(ratio + 'x')} ${dim('more tokens per exchange than multi-step sessions')}`
559
- );
560
- }
561
- }
562
- }
563
-
564
- // Debug cycles: vague vs specific cost
565
- const dbe = metrics.debugCycles.details.tokenEvidence;
566
- if (dbe && dbe.avgTokensVagueDebug && dbe.avgTokensSpecificDebug) {
567
- const ratio = (dbe.avgTokensVagueDebug / dbe.avgTokensSpecificDebug).toFixed(1);
568
- if (parseFloat(ratio) > 1.2) {
569
- if (pricing > 0) {
570
- const vCost = dollarFormat(dbe.avgTokensVagueDebug * pricing);
571
- const sCost = dollarFormat(dbe.avgTokensSpecificDebug * pricing);
572
- evidenceLines.push(
573
- ` ${dim('\u2022 Vague debug prompts cost')} ${orange(vCost)} ${dim('vs')} ${green(sCost)} ${dim('for specific reports')} ${dim('(' + ratio + 'x)')}`
574
- );
575
- } else {
576
- evidenceLines.push(
577
- ` ${dim('\u2022 Vague debug prompts cost')} ${orange(ratio + 'x')} ${dim('more than specific error reports')}`
578
- );
579
- }
580
- }
581
- }
582
-
583
- // AI Leverage: trivial prompts vs detailed ones
584
- const aie = metrics.aiLeverage.details.tokenEvidence;
585
- if (aie && aie.avgTokensTrivialPrompt && aie.avgTokensComplexPrompt) {
586
- const savingsPct = Math.round((1 - aie.avgTokensTrivialPrompt / aie.avgTokensComplexPrompt) * 100);
587
- if (pricing > 0) {
588
- const trivCost = dollarFormat(aie.avgTokensTrivialPrompt * pricing);
589
- const compCost = dollarFormat(aie.avgTokensComplexPrompt * pricing);
590
- evidenceLines.push(
591
- ` ${dim('\u2022 Short vague prompts (<50 chars) cost')} ${dim(trivCost)} ${dim('— only ' + savingsPct + '% less than detailed ones at')} ${dim(compCost)}`
592
- );
593
- } else if (savingsPct < 40) {
594
- evidenceLines.push(
595
- ` ${dim('\u2022 Short vague prompts (<50 chars) cost')} ${dim(numberFormat(aie.avgTokensTrivialPrompt) + ' tokens')} ${dim('— only ' + savingsPct + '% less than detailed ones')}`
596
- );
597
- }
598
- }
599
-
600
- // AI Leverage: architectural vs boilerplate cost
601
- if (aie && aie.avgTokensArchitectural && aie.avgTokensBoilerplate && pricing > 0) {
602
- const archCost = dollarFormat(aie.avgTokensArchitectural * pricing);
603
- const boilCost = dollarFormat(aie.avgTokensBoilerplate * pricing);
604
- evidenceLines.push(
605
- ` ${dim('\u2022 Architectural prompts cost')} ${dim(archCost + '/exchange')} ${dim('vs boilerplate at')} ${dim(boilCost + '/exchange')}`
606
- );
607
- }
608
-
609
- // Session structure: marathon vs focused cost
610
- const sse = metrics.sessionStructure.details.tokenEvidence;
611
- if (sse && sse.avgTokensPerExchangeMarathon && sse.avgTokensPerExchangeFocused) {
612
- const ratio = (sse.avgTokensPerExchangeMarathon / sse.avgTokensPerExchangeFocused).toFixed(1);
613
- if (parseFloat(ratio) > 1.1) {
614
- if (pricing > 0) {
615
- const marCost = dollarFormat(sse.avgTokensPerExchangeMarathon * pricing);
616
- const focCost = dollarFormat(sse.avgTokensPerExchangeFocused * pricing);
617
- evidenceLines.push(
618
- ` ${dim('\u2022 Marathon sessions (>60m) cost')} ${orange(marCost + '/exchange')} ${dim('vs focused at')} ${green(focCost)} ${dim('(' + ratio + 'x)')}`
619
- );
620
- } else {
621
- evidenceLines.push(
622
- ` ${dim('\u2022 Marathon sessions (>60m) cost')} ${orange(ratio + 'x')} ${dim('more per exchange than focused ones (10-45m)')}`
623
- );
624
- }
625
- }
626
- }
627
-
628
- // Context-setting vs no context
629
- if (sse && sse.avgTokensPerExchangeNoContext && sse.avgTokensPerExchangeWithContext) {
630
- const ratio = (sse.avgTokensPerExchangeNoContext / sse.avgTokensPerExchangeWithContext).toFixed(1);
631
- if (parseFloat(ratio) > 1.1) {
632
- if (pricing > 0) {
633
- const noCost = dollarFormat(sse.avgTokensPerExchangeNoContext * pricing);
634
- const wCost = dollarFormat(sse.avgTokensPerExchangeWithContext * pricing);
635
- evidenceLines.push(
636
- ` ${dim('\u2022 Sessions without upfront context cost')} ${orange(noCost + '/exchange')} ${dim('vs')} ${green(wCost)} ${dim('with context')} ${dim('(' + ratio + 'x)')}`
637
- );
638
- } else {
639
- evidenceLines.push(
640
- ` ${dim('\u2022 Sessions without upfront context cost')} ${orange(ratio + 'x')} ${dim('more per exchange')}`
641
- );
642
- }
643
- }
644
- }
645
-
646
- // Session length cost curve from token efficiency
647
- if (tokenEfficiency && tokenEfficiency.hasData && tokenEfficiency.sessionLengthAnalysis) {
648
- const sla = tokenEfficiency.sessionLengthAnalysis;
649
- if (sla.length >= 2 && pricing > 0) {
650
- const sorted = [...sla].sort((a, b) => a.avgTokensPerExchange - b.avgTokensPerExchange);
651
- const cheapest = sorted[0];
652
- const costliest = sorted[sorted.length - 1];
653
- if (costliest.avgTokensPerExchange > cheapest.avgTokensPerExchange * 1.5) {
654
- evidenceLines.push(
655
- ` ${dim('\u2022 Cost curve:')} ${dim(cheapest.label)} ${green(dollarFormat(cheapest.avgTokensPerExchange * pricing) + '/exchange')} ${dim('\u2192')} ${dim(costliest.label)} ${orange(dollarFormat(costliest.avgTokensPerExchange * pricing) + '/exchange')}`
656
- );
657
- }
658
- }
659
- }
660
-
661
- if (evidenceLines.length > 0) {
662
- console.log(` ${dim('WHAT YOUR HABITS ACTUALLY COST')}`);
663
- console.log(` ${dim('\u2500'.repeat(53))}`);
664
- for (const line of evidenceLines) {
665
- console.log(line);
666
- }
667
- console.log();
668
- }
669
- }
670
-
671
- // ══════════════════════════════════════════════
672
- // CROSS-PLATFORM
673
- // ══════════════════════════════════════════════
674
-
675
- function displayCrossPlatform(perToolScores) {
676
- if (!perToolScores || Object.keys(perToolScores).length <= 1) return;
677
-
678
- console.log(dim(' CROSS-PLATFORM\n'));
679
- const sorted = Object.entries(perToolScores).sort((a, b) => {
680
- if (a[1].score === null) return 1;
681
- if (b[1].score === null) return -1;
682
- return b[1].score - a[1].score;
683
- });
684
- const maxSessions = Math.max(...sorted.map(([, v]) => v.sessions));
685
-
686
- for (const [tool, data] of sorted) {
687
- if (data.score !== null) {
688
- const isPrimary = data.sessions === maxSessions;
689
- const label = isPrimary ? 'primary tool' : data.sessions < 5 ? 'limited usage' : 'active';
690
- console.log(` ${pad(bold(tool), 18)} ${bold(String(data.score))} ${progressBar(data.score)} ${dim(label)}`);
691
- } else {
692
- console.log(` ${pad(bold(tool), 18)} ${dim('--')} ${dim('\u2591'.repeat(18))} ${dim('limited data')}`);
693
- }
694
- }
695
- console.log();
696
- }
697
-
698
- // ══════════════════════════════════════════════
699
- // PROJECT ANALYSIS
700
- // ══════════════════════════════════════════════
701
-
702
- function displayProjects(insights, tokenEfficiency = null) {
703
- const projects = insights?.projectComplexity;
704
- if (!projects || projects.length === 0) return;
705
-
706
- // Build a lookup from token efficiency per-project data
707
- const projectCostMap = {};
708
- if (tokenEfficiency && tokenEfficiency.hasData && tokenEfficiency.perProject) {
709
- for (const pp of tokenEfficiency.perProject) {
710
- projectCostMap[pp.fullName] = pp;
711
- }
712
- }
713
-
714
- console.log(dim(' PROJECT ANALYSIS\n'));
715
- for (const p of projects) {
716
- const compColor = p.complexity === 'HIGH' ? green : p.complexity === 'MEDIUM' ? yellow : dim;
717
- const signals = p.signals.length > 0 ? p.signals.join(' \u00B7 ') : '';
718
- console.log(` ${pad(bold(p.name), 32)} Complexity: ${compColor(p.complexity)}`);
719
- if (signals) {
720
- console.log(` ${dim(signals)}`);
721
- }
722
- // Look up cost data — match by full project name or short name
723
- const costData = Object.values(projectCostMap).find(pp =>
724
- p.name === pp.name || pp.fullName?.endsWith(p.name.replace('...', ''))
725
- );
726
- if (costData && costData.exchanges > 0) {
727
- const costPerExchange = costData.estimatedCost / costData.exchanges;
728
- const totalCost = costData.estimatedCost;
729
- console.log(` ${dim(`${p.sessions} sessions \u00B7 ${numberFormat(p.exchanges)} exchanges \u00B7 ${p.daysActive} days active`)} ${dim('|')} ${dim('$' + totalCost.toFixed(0) + ' total \u00B7 $' + costPerExchange.toFixed(2) + '/exchange')}`);
730
- } else {
731
- console.log(` ${dim(`${p.sessions} sessions \u00B7 ${numberFormat(p.exchanges)} exchanges \u00B7 ${p.daysActive} days active`)}`);
732
- }
733
- console.log();
734
- }
735
- }
736
-
737
- // ══════════════════════════════════════════════
738
- // DIMENSION NARRATIVES — Clinical, benchmarked
739
- // ══════════════════════════════════════════════
740
-
741
- export function displayNarratives(metrics, prose, tokenEfficiency = null) {
742
- const shownSnippets = new Set();
743
- function showLabeledSnippet(label, prompt) {
744
- if (!prompt) return;
745
- const s = cleanPrompt(prompt);
746
- if (shownSnippets.has(s)) return;
747
- shownSnippets.add(s);
748
- displayLabeledSnippet(label, prompt);
749
- }
750
-
751
- if (prose && prose.sections) {
752
- const sectionSnippetMap = {
753
- 'thinking': { label: 'Evidence', prompt: pickExample(metrics.decomposition.examples, 'decomposition') },
754
- 'debugging': { label: 'Evidence', prompt: pickExample(metrics.debugCycles.examples, 'specific_report') || pickExample(metrics.debugCycles.examples, 'quick_fix') },
755
- 'ai leverage': { label: 'Evidence', prompt: pickExample(metrics.aiLeverage.examples, 'architectural') || pickExample(metrics.aiLeverage.examples, 'planning') },
756
- 'workflow': { label: 'Evidence', prompt: pickExample(metrics.sessionStructure.examples, 'context_setting') || pickExample(metrics.sessionStructure.examples, 'refinement') },
757
- };
758
-
759
- for (const section of prose.sections) {
760
- console.log(` ${section.emoji} ${bold(section.title.toUpperCase())}`);
761
- const lines = section.description.split('\n').filter(l => l.trim());
762
- for (const line of lines) {
763
- console.log(` ${dim(line.trim())}`);
764
- }
765
- // Add token cost line for this dimension
766
- const titleLower = section.title.toLowerCase();
767
- displayDimensionCostLine(titleLower, metrics, tokenEfficiency);
768
- const matched = sectionSnippetMap[titleLower];
769
- if (matched && matched.prompt) showLabeledSnippet(matched.label, matched.prompt);
770
- console.log();
771
- }
772
- } else {
773
- displayDataNarratives(metrics, shownSnippets, tokenEfficiency);
774
- }
775
- }
776
-
777
- function displayDataNarratives(metrics, shownSnippets, tokenEfficiency = null) {
778
- const d = metrics.decomposition.details;
779
- const db = metrics.debugCycles.details;
780
- const ai = metrics.aiLeverage.details;
781
- const ss = metrics.sessionStructure.details;
782
-
783
- function showSnippet(label, prompt) {
784
- if (!prompt) return;
785
- const s = cleanPrompt(prompt);
786
- if (shownSnippets.has(s)) return;
787
- shownSnippets.add(s);
788
- displayLabeledSnippet(label, prompt);
789
- }
790
-
791
- // Thinking — clinical
792
- console.log(` ${bold('\uD83E\uDDE0 THINKING')}`);
793
- console.log(` ${dim(`Avg session depth: ${d.avgExchangesPerSession} exchanges (benchmark: ${BENCHMARKS.avgExchangesPerSession})`)}`);
794
- console.log(` ${dim(`Avg prompt length: ${numberFormat(d.avgPromptLength)} chars (benchmark: ${BENCHMARKS.avgPromptLength})`)}`);
795
- console.log(` ${dim(`Multi-step ratio: ${d.multiStepSessions}/${d.totalSessions} sessions (${Math.round(d.multiStepSessions / Math.max(1, d.totalSessions) * 100)}%)`)}`);
796
- displayDimensionCostLine('thinking', metrics, tokenEfficiency);
797
- showSnippet('Evidence', pickExample(metrics.decomposition.examples, 'decomposition'));
798
- console.log();
799
-
800
- // Debugging — clinical
801
- console.log(` ${bold('\u26A1 DEBUGGING')}`);
802
- console.log(` ${dim(`Avg resolution: ${db.avgTurnsToResolve} turns (benchmark: ${BENCHMARKS.avgTurnsToResolve})`)}`);
803
- console.log(` ${dim(`Specific reports: ${db.specificReportRatio}% (benchmark: ${BENCHMARKS.specificReportRatio}%)`)}`);
804
- console.log(` ${dim(`Extended loops: ${db.longLoops} | Quick fixes: ${db.quickFixes}/${db.totalDebugSequences}`)}`);
805
- displayDimensionCostLine('debugging', metrics, tokenEfficiency);
806
- showSnippet('Evidence', pickExample(metrics.debugCycles.examples, 'specific_report') || pickExample(metrics.debugCycles.examples, 'quick_fix'));
807
- console.log();
808
-
809
- // AI Leverage — clinical
810
- console.log(` ${bold('\uD83D\uDD27 AI LEVERAGE')}`);
811
- console.log(` ${dim(`High-level ratio: ${ai.highLevelRatio}% (benchmark: ${BENCHMARKS.highLevelRatio}%)`)}`);
812
- console.log(` ${dim(`Breakdown: ${ai.architecturalPrompts} architectural \u00B7 ${ai.planningPrompts} planning \u00B7 ${ai.exploratoryPrompts} exploratory`)}`);
813
- console.log(` ${dim(`Boilerplate: ${ai.boilerplatePrompts} (${Math.round(ai.boilerplatePrompts / Math.max(1, ai.totalPrompts) * 100)}%) \u00B7 Testing: ${ai.testingPrompts}`)}`);
814
- displayDimensionCostLine('ai leverage', metrics, tokenEfficiency);
815
- showSnippet('Evidence', pickExample(metrics.aiLeverage.examples, 'architectural') || pickExample(metrics.aiLeverage.examples, 'planning'));
816
- console.log();
817
-
818
- // Workflow — clinical
819
- console.log(` ${bold('\uD83D\uDCD0 WORKFLOW')}`);
820
- console.log(` ${dim(`Context-setting: ${ss.contextSetRatio}% (benchmark: ${BENCHMARKS.contextSetRatio}%)`)}`);
821
- console.log(` ${dim(`Review at end: ${ss.reviewEndRatio}% (benchmark: ${BENCHMARKS.reviewEndRatio}%)`)}`);
822
- console.log(` ${dim(`Refinement rate: ${ss.refinementRatio}% (benchmark: ${BENCHMARKS.refinementRatio}%)`)}`);
823
- displayDimensionCostLine('workflow', metrics, tokenEfficiency);
824
- showSnippet('Evidence', pickExample(metrics.sessionStructure.examples, 'context_setting') || pickExample(metrics.sessionStructure.examples, 'refinement'));
825
- console.log();
826
- }
827
-
828
- // ══════════════════════════════════════════════
829
- // SIGNATURES — What makes you distinctive
830
- // ══════════════════════════════════════════════
831
-
832
- function displaySignatures(insights) {
833
- const sigs = insights?.signatures;
834
- if (!sigs || sigs.length === 0) return;
835
-
836
- console.log(dim(' YOUR SIGNATURES\n'));
837
- for (const sig of sigs) {
838
- console.log(` ${cyan('\u2726')} ${bold(sig.name)}`);
839
- const lines = wrapText(sig.detail, 49);
840
- for (const line of lines) {
841
- console.log(` ${dim(line)}`);
842
- }
843
- if (sig.evidence) {
844
- displayLabeledSnippet('Proof', sig.evidence);
845
- }
846
- console.log();
847
- }
848
- }
849
-
850
- // ══════════════════════════════════════════════
851
- // WATCH POINTS — Areas to improve
852
- // ══════════════════════════════════════════════
853
-
854
- function displayWatchPoints(insights) {
855
- const wps = insights?.watchPoints;
856
- if (!wps || wps.length === 0) return;
857
-
858
- console.log(dim(' WATCH POINTS\n'));
859
- for (const wp of wps) {
860
- console.log(` ${orange('\u26A0')} ${bold(wp.name)}`);
861
- const lines = wrapText(wp.detail, 49);
862
- for (const line of lines) {
863
- console.log(` ${dim(line)}`);
864
- }
865
- if (wp.evidence) {
866
- displayLabeledSnippet('Example', wp.evidence);
867
- }
868
- console.log();
869
- }
870
- }
871
-
872
- // ══════════════════════════════════════════════
873
- // TRAJECTORY — Weekly evolution
874
- // ══════════════════════════════════════════════
875
-
876
- function displayTrajectory(insights) {
877
- const traj = insights?.trajectory;
878
- if (!traj) return;
879
-
880
- console.log(dim(' YOUR TRAJECTORY\n'));
881
- for (let i = 0; i < traj.weeks.length; i++) {
882
- const w = traj.weeks[i];
883
- const prev = i > 0 ? traj.weeks[i - 1] : null;
884
- const arrow = prev ? (w.score > prev.score ? green(' \u2191') : w.score < prev.score ? orange(' \u2193') : dim(' \u2192')) : '';
885
- const tierLabel = dimTierLabel(w.score);
886
- console.log(` ${dim(pad(w.label, 16))} Score: ${scoreColor(w.score).bold(String(w.score))} ${dimTierColor(w.score)(tierLabel)}${arrow}`);
887
- }
888
- console.log();
889
- const velColor = traj.velocityLabel === 'FAST' ? green : traj.velocityLabel === 'STEADY' ? cyan : traj.velocityLabel === 'STABLE' ? yellow : orange;
890
- console.log(` ${dim('Learning velocity:')} ${velColor(traj.velocityLabel)} ${dim('\u2014 ' + traj.velocityDetail)}`);
891
- console.log();
892
- }
893
-
894
- // ══════════════════════════════════════════════
895
- // ASSESSMENT — Narrative paragraph
896
- // ══════════════════════════════════════════════
897
-
898
- function displayAssessment(insights) {
899
- const assessment = insights?.assessment;
900
- if (!assessment) return;
901
-
902
- console.log(dim(' ASSESSMENT\n'));
903
- const lines = wrapText(assessment, 51);
904
- for (const line of lines) {
905
- console.log(` ${dim(line)}`);
906
- }
907
- console.log();
908
- }
909
-
910
- // ══════════════════════════════════════════════
911
- // CONFIDENCE INDICATOR
912
- // ══════════════════════════════════════════════
913
-
914
- function displayConfidence(insights, sessionStats) {
915
- const conf = insights?.confidence;
916
- if (!conf) return;
917
-
918
- const barWidth = 18;
919
- const filled = Math.round((conf.score / 100) * barWidth);
920
- const empty = barWidth - filled;
921
- const confColor = conf.level === 'HIGH' ? green : conf.level === 'MODERATE' ? yellow : orange;
922
- const bar = confColor('\u2588'.repeat(filled)) + dim('\u2591'.repeat(empty));
923
-
924
- console.log(` ${dim('Profile confidence:')} ${bar} ${confColor(conf.level)}`);
925
- if (sessionStats) {
926
- console.log(` ${dim(`Based on ${sessionStats.totalSessions} sessions across ${sessionStats.tools.length} tool${sessionStats.tools.length > 1 ? 's' : ''}.`)}`);
927
- }
928
- console.log();
929
- if (conf.level !== 'HIGH') {
930
- console.log(` ${dim('Confidence increases with more sessions and tool')}`);
931
- console.log(` ${dim('diversity. Run again in 30 days for updated assessment.')}`);
932
- console.log();
933
- }
934
- }
935
-
936
- // ══════════════════════════════════════════════
937
- // METHODOLOGY FOOTER
938
- // ══════════════════════════════════════════════
939
-
940
- function displayMethodology() {
941
- console.log(doubleRule());
942
- console.log();
943
- console.log(` ${dim('METHODOLOGY')}`);
944
- console.log(` ${dim('Scores derived from structural analysis of prompt')}`);
945
- console.log(` ${dim('patterns, debug sequences, and session architecture')}`);
946
- console.log(` ${dim('across detected AI coding tools. Tier placement based')}`);
947
- console.log(` ${dim('on distribution of scored engineers. No prompt content')}`);
948
- console.log(` ${dim('is stored or transmitted. Learn more: chekk.dev/methodology')}`);
949
- console.log();
950
- }
951
-
952
- // ══════════════════════════════════════════════
953
- // NEXT STEPS — Credential issuance
954
- // ══════════════════════════════════════════════
955
-
956
- function displayNextSteps(result) {
957
- const { overall, archetype, tier, tierBadge } = result;
958
- const tc = tierColor(tier);
959
-
960
- console.log(doubleRule());
961
- console.log();
962
- console.log(` ${bold.white('NEXT STEPS')}`);
963
- console.log();
964
- console.log(` ${dim('\u2192 Claim your verified profile')}`);
965
- console.log(` ${dim('Link your GitHub and LinkedIn to create your')}`);
966
- console.log(` ${dim('portable engineering credential.')}`);
967
- console.log(` ${cyan.underline('chekk.dev/claim')}`);
968
- console.log();
969
- console.log(` ${dim('\u2192 View industry benchmarks')}`);
970
- console.log(` ${dim('See how you compare across scored engineers.')}`);
971
- console.log(` ${cyan.underline('chekk.dev/benchmarks')}`);
972
- console.log();
973
- console.log(` ${dim('\u2192 Export this report')}`);
974
- console.log(` ${dim('npx chekk --json')}`);
975
- console.log();
976
-
977
- // Copy-paste share line
978
- const badge = tierBadge ? ` ${tierBadge}` : '';
979
- const shareLine = `${overall} \u2014 ${tier}${badge} \u2014 ${archetype.name}`;
980
- console.log(` ${dim('"')}${tc(shareLine)}${dim('"')}`);
981
- console.log(` ${dim('\u2191 Copy this to share')}`);
982
- console.log();
983
- console.log(doubleRule());
984
- console.log(` ${dim('chekk.dev \u2014 prompt engineering capability profiles')}`);
985
- console.log();
986
- }
987
-
988
- // ══════════════════════════════════════════════
989
- // VERBOSE: DETAILED BREAKDOWN
990
- // ══════════════════════════════════════════════
991
-
992
- export function displayVerbose(metrics, sessions, tokenEfficiency = null) {
993
- console.log(doubleRule());
994
- console.log(dim('\n DETAILED BREAKDOWN\n'));
995
-
996
- // Helper: show a metric row with value, benchmark comparison, and verdict
997
- // lowerIsBetter: true for metrics where lower = better (e.g. turns to resolve)
998
- function metricRow(label, value, benchmark, unit = '', lowerIsBetter = false) {
999
- const valStr = typeof value === 'number' ? String(value) : value;
1000
- let verdict = '';
1001
- if (benchmark !== null && benchmark !== undefined && typeof value === 'number') {
1002
- const ratio = value / benchmark;
1003
- if (lowerIsBetter) {
1004
- if (ratio <= 0.5) verdict = green(' ++ faster than benchmark');
1005
- else if (ratio <= 0.8) verdict = cyan(' + faster than benchmark');
1006
- else if (ratio <= 1.1) verdict = dim(' ~ at benchmark');
1007
- else if (ratio <= 1.5) verdict = orange(' - slower than benchmark');
1008
- else verdict = red(' -- well above benchmark');
1009
- } else {
1010
- if (ratio >= 1.5) verdict = green(' ++ above benchmark');
1011
- else if (ratio >= 1.1) verdict = cyan(' + above benchmark');
1012
- else if (ratio >= 0.9) verdict = dim(' ~ at benchmark');
1013
- else if (ratio >= 0.6) verdict = orange(' - below benchmark');
1014
- else verdict = red(' -- well below benchmark');
1015
- }
1016
- }
1017
- const benchStr = benchmark !== null && benchmark !== undefined ? dim(` (benchmark: ${benchmark}${unit})`) : '';
1018
- console.log(` ${dim(pad(label, 28))} ${white(valStr + unit)}${benchStr}${verdict}`);
1019
- }
1020
-
1021
- // ── Projects ──
1022
- const projects = {};
1023
- for (const s of sessions) {
1024
- const p = s.project || 'unknown';
1025
- if (!projects[p]) projects[p] = { sessions: 0, exchanges: 0, minutes: 0 };
1026
- projects[p].sessions++;
1027
- projects[p].exchanges += s.exchangeCount;
1028
- projects[p].minutes += s.durationMinutes || 0;
1029
- }
1030
-
1031
- console.log(` ${bold('PROJECTS')}`);
1032
- console.log(` ${dim('\u2500'.repeat(50))}`);
1033
- for (const [name, data] of Object.entries(projects).sort((a, b) => b[1].exchanges - a[1].exchanges)) {
1034
- const shortName = name.length > 28 ? '...' + name.slice(-25) : name;
1035
- console.log(` ${pad(white(shortName), 30)} ${dim(data.sessions + ' sessions')} ${dim(numberFormat(data.exchanges) + ' exchanges')}`);
1036
- }
1037
- console.log();
1038
-
1039
- // ── Thinking / Decomposition ──
1040
- const d = metrics.decomposition.details;
1041
- console.log(` ${bold('\uD83E\uDDE0 THINKING')} ${dim('(weight: 25%)')}`);
1042
- console.log(` ${dim('\u2500'.repeat(50))}`);
1043
- metricRow('Session depth', d.avgExchangesPerSession, BENCHMARKS.avgExchangesPerSession, ' exchanges');
1044
- metricRow('Prompt length', d.avgPromptLength, BENCHMARKS.avgPromptLength, ' chars');
1045
- console.log(` ${dim(pad('Multi-step sessions', 28))} ${white(d.multiStepSessions + '/' + d.totalSessions)} ${dim('(' + Math.round(d.multiStepSessions / Math.max(1, d.totalSessions) * 100) + '%)')}`);
1046
- console.log(` ${dim(pad('Single-shot sessions', 28))} ${white(String(d.singleShotSessions))}`);
1047
- console.log(` ${dim(pad('Contextual followups', 28))} ${white(d.contextualFollowupRatio + '%')}${d.contextualFollowupRatio > 20 ? cyan(' builds on context well') : orange(' could reference prior work more')}`);
1048
- displayDimensionCostLine('thinking', metrics, tokenEfficiency);
1049
- console.log();
1050
-
1051
- // ── Debugging ──
1052
- const db = metrics.debugCycles.details;
1053
- console.log(` ${bold('\u26A1 DEBUGGING')} ${dim('(weight: 25%)')}`);
1054
- console.log(` ${dim('\u2500'.repeat(50))}`);
1055
- console.log(` ${dim(pad('Debug sequences', 28))} ${white(String(db.totalDebugSequences))}`);
1056
- metricRow('Turns to resolve', db.avgTurnsToResolve, BENCHMARKS.avgTurnsToResolve, ' avg', true);
1057
- console.log(` ${dim(pad('Quick fixes (1-2 turns)', 28))} ${white(String(db.quickFixes))} ${dim('of ' + db.totalDebugSequences)}`);
1058
- console.log(` ${dim(pad('Extended loops (>5 turns)', 28))} ${db.longLoops === 0 ? green('0 -- zero spirals') : orange(String(db.longLoops))}`);
1059
- metricRow('Specific report ratio', db.specificReportRatio, BENCHMARKS.specificReportRatio, '%');
1060
- console.log(` ${dim(pad('Vague reports', 28))} ${db.vagueReports === 0 ? green('0 -- never vague') : orange(String(db.vagueReports))}`);
1061
- displayDimensionCostLine('debugging', metrics, tokenEfficiency);
1062
- console.log();
1063
-
1064
- // ── AI Leverage ──
1065
- const ai = metrics.aiLeverage.details;
1066
- const total = Math.max(1, ai.totalPrompts);
1067
- console.log(` ${bold('\uD83D\uDD27 AI LEVERAGE')} ${dim('(weight: 30%)')}`);
1068
- console.log(` ${dim('\u2500'.repeat(50))}`);
1069
- console.log(` ${dim(pad('Total prompts analyzed', 28))} ${white(numberFormat(ai.totalPrompts))}`);
1070
- console.log();
1071
- console.log(` ${dim(' Prompt type breakdown:')}`);
1072
- console.log(` ${dim(pad(' Architectural', 28))} ${white(String(ai.architecturalPrompts))} ${dim('(' + Math.round(ai.architecturalPrompts / total * 100) + '%) design, schema, strategy')}`);
1073
- console.log(` ${dim(pad(' Planning', 28))} ${white(String(ai.planningPrompts))} ${dim('(' + Math.round(ai.planningPrompts / total * 100) + '%) how-should-I, trade-offs')}`);
1074
- console.log(` ${dim(pad(' Exploratory', 28))} ${white(String(ai.exploratoryPrompts))} ${dim('(' + Math.round(ai.exploratoryPrompts / total * 100) + '%) explain, investigate')}`);
1075
- console.log(` ${dim(pad(' Boilerplate', 28))} ${white(String(ai.boilerplatePrompts))} ${dim('(' + Math.round(ai.boilerplatePrompts / total * 100) + '%) CRUD, templates')}${ai.boilerplatePrompts < total * 0.05 ? green(' minimal') : ''}`);
1076
- console.log(` ${dim(pad(' Testing', 28))} ${white(String(ai.testingPrompts))} ${dim('(' + Math.round(ai.testingPrompts / total * 100) + '%)')}`);
1077
- console.log();
1078
- metricRow('High-level ratio', ai.highLevelRatio, BENCHMARKS.highLevelRatio, '%');
1079
- displayDimensionCostLine('ai leverage', metrics, tokenEfficiency);
1080
- console.log();
1081
-
1082
- // ── Session Structure ──
1083
- const ss = metrics.sessionStructure.details;
1084
- console.log(` ${bold('\uD83D\uDCD0 WORKFLOW')} ${dim('(weight: 20%)')}`);
1085
- console.log(` ${dim('\u2500'.repeat(50))}`);
1086
- metricRow('Context-setting', ss.contextSetRatio, BENCHMARKS.contextSetRatio, '%');
1087
- console.log(` ${dim(pad('Plan before code', 28))} ${white(ss.planBeforeCodeRatio + '%')}`);
1088
- metricRow('Review at end', ss.reviewEndRatio, BENCHMARKS.reviewEndRatio, '%');
1089
- metricRow('Refinement rate', ss.refinementRatio, BENCHMARKS.refinementRatio, '%');
1090
- console.log(` ${dim(pad('Avg first prompt length', 28))} ${white(ss.avgFirstPromptLength + ' chars')}`);
1091
- if (ss.durationDistribution) {
1092
- const dur = ss.durationDistribution;
1093
- console.log();
1094
- console.log(` ${dim(' Session duration:')}`);
1095
- console.log(` ${dim(pad(' Focused (10-45m)', 28))} ${white(String(dur.focused))} ${dim('-- ideal range')}`);
1096
- console.log(` ${dim(pad(' Short (<5m)', 28))} ${white(String(dur.short))}`);
1097
- console.log(` ${dim(pad(' Medium (5-60m)', 28))} ${white(String(dur.medium))}`);
1098
- console.log(` ${dim(pad(' Long (>60m)', 28))} ${white(String(dur.long))}`);
1099
- }
1100
- displayDimensionCostLine('workflow', metrics, tokenEfficiency);
1101
- console.log();
1102
- }
1103
-
1104
- // ══════════════════════════════════════════════
1105
- // OFFLINE DISPLAY
1106
- // ══════════════════════════════════════════════
1107
-
1108
- export function displayOffline(result, metrics, extra = {}) {
1109
- displayProfileHeader(result, extra);
1110
- displaySummary(result, extra);
1111
- displayArchetype(result);
1112
- displayDimensions(result);
1113
- displayTokenEfficiency(extra.tokenEfficiency, metrics);
1114
- displayCrossPlatform(extra.perToolScores);
1115
- displayDataNarratives(metrics, new Set(), extra.tokenEfficiency);
1116
- displayProjects(extra.insights, extra.tokenEfficiency);
1117
- displaySignatures(extra.insights);
1118
- displayWatchPoints(extra.insights);
1119
- displayTrajectory(extra.insights);
1120
- displayAssessment(extra.insights);
1121
- displayConfidence(extra.insights, extra.sessionStats);
1122
- displayMethodology();
1123
- console.log(dim(' Run without --offline for AI-generated narrative insights\n'));
1124
- displayNextSteps(result);
1125
- }
1126
-
1127
- // ══════════════════════════════════════════════
1128
- // FULL ONLINE DISPLAY
1129
- // ══════════════════════════════════════════════
1130
-
1131
- export function displayFull(result, metrics, prose, extra = {}) {
1132
- displayProfileHeader(result, extra);
1133
- displaySummary(result, extra);
1134
- displayArchetype(result);
1135
- displayDimensions(result);
1136
- displayTokenEfficiency(extra.tokenEfficiency, metrics);
1137
- displayCrossPlatform(extra.perToolScores);
1138
- displayNarratives(metrics, prose, extra.tokenEfficiency);
1139
- displayProjects(extra.insights, extra.tokenEfficiency);
1140
- displaySignatures(extra.insights);
1141
- displayWatchPoints(extra.insights);
1142
- displayTrajectory(extra.insights);
1143
- displayAssessment(extra.insights);
1144
- displayConfidence(extra.insights, extra.sessionStats);
1145
- displayMethodology();
1146
- displayNextSteps(result);
1147
- }
1148
-
1149
- // ── Utility ──
1150
-
1151
- function sleep(ms) {
1152
- return new Promise(resolve => setTimeout(resolve, ms));
1153
- }