chekk 0.2.5 → 0.4.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 CHANGED
@@ -1,4 +1,5 @@
1
1
  import chalk from 'chalk';
2
+ import { BENCHMARKS, DIM_RANGES } from './insights.js';
2
3
 
3
4
  // ── Color palette ──
4
5
  const gold = chalk.hex('#FFD700');
@@ -6,6 +7,9 @@ const purple = chalk.hex('#A855F7');
6
7
  const blue = chalk.hex('#3B82F6');
7
8
  const green = chalk.hex('#22C55E');
8
9
  const cyan = chalk.hex('#06B6D4');
10
+ const orange = chalk.hex('#F97316');
11
+ const red = chalk.hex('#EF4444');
12
+ const yellow = chalk.hex('#EAB308');
9
13
  const dim = chalk.dim;
10
14
  const bold = chalk.bold;
11
15
  const white = chalk.white;
@@ -21,9 +25,9 @@ function tierColor(tier) {
21
25
  function scoreColor(score) {
22
26
  if (score >= 85) return green;
23
27
  if (score >= 70) return cyan;
24
- if (score >= 55) return chalk.hex('#EAB308');
25
- if (score >= 40) return chalk.hex('#F97316');
26
- return chalk.hex('#EF4444');
28
+ if (score >= 55) return yellow;
29
+ if (score >= 40) return orange;
30
+ return red;
27
31
  }
28
32
 
29
33
  function progressBar(score, width = 18) {
@@ -42,9 +46,27 @@ function pad(str, len) {
42
46
  return str + ' '.repeat(Math.max(0, len - visible.length));
43
47
  }
44
48
 
45
- function snippet(prompt, maxLen = 70) {
49
+ // ── Qualitative tier labels for dimensions ──
50
+ function dimTierLabel(score) {
51
+ if (score >= 80) return 'EXCEPTIONAL';
52
+ if (score >= 65) return 'STRONG';
53
+ if (score >= 50) return 'SOLID';
54
+ if (score >= 35) return 'DEVELOPING';
55
+ return 'NEEDS WORK';
56
+ }
57
+
58
+ function dimTierColor(score) {
59
+ if (score >= 80) return green;
60
+ if (score >= 65) return cyan;
61
+ if (score >= 50) return yellow;
62
+ if (score >= 35) return orange;
63
+ return red;
64
+ }
65
+
66
+ // ── Snippet helpers ──
67
+
68
+ function cleanPrompt(prompt, maxLen = 120) {
46
69
  if (!prompt) return null;
47
- // Clean up whitespace / newlines
48
70
  let clean = prompt.replace(/\s+/g, ' ').trim();
49
71
  if (clean.length > maxLen) {
50
72
  clean = clean.slice(0, maxLen - 1) + '\u2026';
@@ -52,10 +74,10 @@ function snippet(prompt, maxLen = 70) {
52
74
  return clean;
53
75
  }
54
76
 
55
- function displaySnippet(prompt, maxLen = 70) {
56
- const s = snippet(prompt, maxLen);
77
+ function displayLabeledSnippet(label, prompt, maxLen = 120) {
78
+ const s = cleanPrompt(prompt, maxLen);
57
79
  if (!s) return;
58
- console.log(` ${dim('\u201C')}${dim.italic(s)}${dim('\u201D')}`);
80
+ console.log(` ${dim('\u21B3')} ${dim(label + ':')} ${dim.italic('\u201C' + s + '\u201D')}`);
59
81
  }
60
82
 
61
83
  function pickExample(examples, type) {
@@ -66,7 +88,7 @@ function pickExample(examples, type) {
66
88
 
67
89
  // ── Box drawing ──
68
90
 
69
- function box(lines, width = 43) {
91
+ function box(lines, width = 47) {
70
92
  const out = [];
71
93
  out.push(dim(' \u250C' + '\u2500'.repeat(width) + '\u2510'));
72
94
  for (const line of lines) {
@@ -78,33 +100,44 @@ function box(lines, width = 43) {
78
100
  return out;
79
101
  }
80
102
 
81
- function doubleRule(width = 47) {
103
+ function doubleRule(width = 53) {
82
104
  return dim(' ' + '\u2550'.repeat(width));
83
105
  }
84
106
 
107
+ function wrapText(text, maxWidth = 49) {
108
+ const words = text.split(' ');
109
+ const lines = [];
110
+ let current = '';
111
+ for (const word of words) {
112
+ if (current.length + word.length + 1 > maxWidth) {
113
+ lines.push(current);
114
+ current = word;
115
+ } else {
116
+ current = current ? current + ' ' + word : word;
117
+ }
118
+ }
119
+ if (current) lines.push(current);
120
+ return lines;
121
+ }
122
+
85
123
  // ══════════════════════════════════════════════
86
- // HEADER
124
+ // HEADER — Scanning + progress
87
125
  // ══════════════════════════════════════════════
88
126
 
89
127
  export function displayHeader() {
90
128
  console.log();
91
129
  const lines = [
92
130
  '',
93
- ` ${bold.white('chekk')}${dim(' v0.2.5')}`,
94
- ` ${dim('the engineering capability score')}`,
131
+ ` ${bold.white('chekk')}${dim(' v0.4.0')}`,
132
+ ` ${dim('engineering capability profile')}`,
95
133
  '',
96
134
  ];
97
135
  for (const l of box(lines, 45)) console.log(l);
98
136
  console.log();
99
137
  }
100
138
 
101
- // ══════════════════════════════════════════════
102
- // SCAN RESULTS
103
- // ══════════════════════════════════════════════
104
-
105
139
  export function displayScan(tools) {
106
140
  console.log(dim(' Scanning local AI tools...\n'));
107
-
108
141
  for (const tool of tools) {
109
142
  const sessions = numberFormat(tool.sessions);
110
143
  const projects = tool.projects.length;
@@ -116,10 +149,6 @@ export function displayScan(tools) {
116
149
  console.log();
117
150
  }
118
151
 
119
- // ══════════════════════════════════════════════
120
- // ANALYSIS PROGRESS BAR
121
- // ══════════════════════════════════════════════
122
-
123
152
  export function displayAnalysisStart(dateRange) {
124
153
  console.log(` ${dim('Analyzing')} ${white(dateRange)}${dim('...\n')}`);
125
154
  }
@@ -128,7 +157,6 @@ export async function displayProgressBar(durationMs = 2000) {
128
157
  const width = 40;
129
158
  const steps = 40;
130
159
  const stepTime = durationMs / steps;
131
-
132
160
  for (let i = 0; i <= steps; i++) {
133
161
  const filled = i;
134
162
  const empty = width - filled;
@@ -139,87 +167,217 @@ export async function displayProgressBar(durationMs = 2000) {
139
167
  }
140
168
  console.log();
141
169
  console.log();
142
- console.log(` ${green('\u2713')} ${dim('Score generated.')}`);
170
+ console.log(` ${green('\u2713')} ${dim('Profile generated.')}`);
143
171
  console.log();
144
172
  }
145
173
 
146
174
  // ══════════════════════════════════════════════
147
- // MAIN SCORE DISPLAY
175
+ // PROFILE HEADER — Official document feel
148
176
  // ══════════════════════════════════════════════
149
177
 
150
- export function displayScore(result, prose) {
151
- const { overall, scores, archetype, tier, tierBadge, tierPercentile } = result;
152
- const tc = tierColor(tier);
153
- const sc = scoreColor(overall);
178
+ function displayProfileHeader(result, extra = {}) {
179
+ const { sessionStats } = extra;
180
+ const now = new Date();
181
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
182
+ const dateStr = `${months[now.getMonth()]} ${now.getDate()}, ${now.getFullYear()}`;
154
183
 
155
- // Big score block
156
184
  console.log(doubleRule());
157
185
  console.log();
158
- console.log(dim(' YOUR CHEKK SCORE'));
186
+ console.log(` ${bold.white('ENGINEERING CAPABILITY PROFILE')}`);
159
187
  console.log();
160
- console.log(` ${sc.bold(String(overall))}`);
161
- const badgeStr = tierBadge ? ` ${tierBadge}` : '';
162
- const pctStr = tierPercentile ? ` ${dim(tierPercentile)}` : '';
163
- console.log(` ${tc('\u2500\u2500 ' + tier + ' \u2500\u2500')}${badgeStr}${pctStr}`);
188
+ if (sessionStats) {
189
+ console.log(` ${dim(`Generated ${dateStr} | chekk v0.4.0`)}`);
190
+ console.log(` ${dim(`Analysis: ${sessionStats.totalSessions} sessions \u00B7 ${sessionStats.tools.length} tool${sessionStats.tools.length > 1 ? 's' : ''} \u00B7 ${numberFormat(sessionStats.totalExchanges)} exchanges`)}`);
191
+ if (sessionStats.dateRangeShort) {
192
+ console.log(` ${dim(`Period: ${sessionStats.dateRangeShort}`)}`);
193
+ }
194
+ }
164
195
  console.log();
165
- console.log(` ${dim('Archetype:')} ${bold.white(archetype.name)}`);
196
+ console.log(doubleRule());
197
+ }
166
198
 
167
- if (prose && prose.tagline) {
168
- console.log(` ${dim('"' + prose.tagline + '"')}`);
199
+ // ══════════════════════════════════════════════
200
+ // SUMMARY BLOCK
201
+ // ══════════════════════════════════════════════
202
+
203
+ function displaySummary(result, extra = {}) {
204
+ const { overall, scores, archetype, tier, tierBadge, tierPercentile } = result;
205
+ const { scoreDelta, perToolScores, insights, sessionStats } = extra;
206
+ const tc = tierColor(tier);
207
+
208
+ // Find strongest and weakest
209
+ const dims = [
210
+ { label: 'Thinking', score: scores.decomposition },
211
+ { label: 'Debugging', score: scores.debugCycles },
212
+ { label: 'AI Leverage', score: scores.aiLeverage },
213
+ { label: 'Workflow', score: scores.sessionStructure },
214
+ ];
215
+ dims.sort((a, b) => b.score - a.score);
216
+ const strongest = dims[0];
217
+ const weakest = dims[dims.length - 1];
218
+
219
+ console.log();
220
+ console.log(dim(' SUMMARY'));
221
+
222
+ const badge = tierBadge || '';
223
+ const toolsList = sessionStats ? sessionStats.tools.join(' \u00B7 ') : '';
224
+ const sessionCount = sessionStats ? `${sessionStats.totalSessions} across ${sessionStats.projectCount} projects` : '';
225
+ const period = sessionStats?.dateRangeShort || '';
226
+
227
+ // Delta string
228
+ let deltaStr = '';
229
+ if (scoreDelta !== null && scoreDelta !== undefined) {
230
+ if (scoreDelta > 0) deltaStr = ` ${green('\u2191 +' + scoreDelta)}`;
231
+ else if (scoreDelta < 0) deltaStr = ` ${orange('\u2193 ' + scoreDelta)}`;
169
232
  }
233
+
234
+ const summaryLines = [
235
+ '',
236
+ ` ${dim('Score')} ${scoreColor(overall).bold(String(overall))}${deltaStr}`,
237
+ ` ${dim('Tier')} ${tc(tier)} ${badge} ${dim('\u2014')} ${dim(tierPercentile)}`,
238
+ ` ${dim('Archetype')} ${bold.white(archetype.name)}`,
239
+ ];
240
+ if (toolsList) summaryLines.push(` ${dim('Tools')} ${dim(toolsList)}`);
241
+ if (sessionCount) summaryLines.push(` ${dim('Sessions')} ${dim(sessionCount)}`);
242
+ if (period) summaryLines.push(` ${dim('Period')} ${dim(period)}`);
243
+ summaryLines.push(` ${dim('Strongest')} ${dim(strongest.label + ' (' + dimTierLabel(strongest.score) + ')')}`);
244
+ summaryLines.push(` ${dim('Growth Area')} ${dim(weakest.label + ' (' + dimTierLabel(weakest.score) + ')')}`);
245
+ summaryLines.push('');
246
+
247
+ for (const l of box(summaryLines, 53)) console.log(l);
170
248
  console.log();
171
- console.log(doubleRule());
249
+ }
250
+
251
+ // ══════════════════════════════════════════════
252
+ // ARCHETYPE DEFINITION
253
+ // ══════════════════════════════════════════════
254
+
255
+ function displayArchetype(result) {
256
+ const { archetype } = result;
257
+ console.log(` ${bold.white(archetype.name)}`);
172
258
  console.log();
259
+ if (archetype.description) {
260
+ const lines = wrapText(archetype.description, 51);
261
+ for (const line of lines) {
262
+ console.log(` ${dim(line)}`);
263
+ }
264
+ }
265
+ if (archetype.distribution) {
266
+ console.log();
267
+ console.log(` ${dim('Distribution: ' + archetype.distribution + ' of scored engineers')}`);
268
+ }
269
+ console.log();
270
+ }
173
271
 
174
- // Dimensions box
175
- console.log(dim(' DIMENSIONS\n'));
272
+ // ══════════════════════════════════════════════
273
+ // DIMENSIONS — with ranges and qualitative labels
274
+ // ══════════════════════════════════════════════
176
275
 
177
- // Build dimension lines with fixed-width layout
178
- // Format: " Label ██████████████░░░░ XX "
179
- // Visible: 2 + 15 + 18 + 2 + 3 + 1 = ~41 chars
180
- function dimLine(label, score) {
276
+ function displayDimensions(result) {
277
+ const { scores } = result;
278
+ console.log(dim(' DIMENSIONS'));
279
+ console.log();
280
+
281
+ // Score weight transparency
282
+ console.log(` ${dim('Weights: Thinking 25% | Debugging 25% | AI Leverage 30% | Workflow 20%')}`);
283
+ console.log();
284
+
285
+ function dimLine(label, key, score) {
181
286
  const labelStr = bold(label);
182
287
  const labelVisible = label.length;
183
288
  const labelPad = ' '.repeat(Math.max(0, 15 - labelVisible));
184
- const scoreStr = String(score);
185
- const scorePad = score < 10 ? ' ' : score < 100 ? ' ' : '';
186
- return ` ${labelStr}${labelPad}${progressBar(score)} ${scorePad}${bold(scoreStr)} `;
289
+ const tierLabel = dimTierLabel(score);
290
+ const tierStr = dimTierColor(score)(pad(tierLabel, 11));
291
+ const range = DIM_RANGES[key];
292
+ const rangeStr = dim(`range: ${range.min}-${range.max} | you: ${score}`);
293
+ return [
294
+ ` ${labelStr}${labelPad}${progressBar(score)} ${tierStr}`,
295
+ ` ${' '.repeat(15)}${rangeStr}`,
296
+ ];
187
297
  }
188
298
 
189
- const dimLines = [
190
- '',
191
- dimLine('Thinking', scores.decomposition),
192
- dimLine('Debugging', scores.debugCycles),
193
- dimLine('AI Leverage', scores.aiLeverage),
194
- dimLine('Workflow', scores.sessionStructure),
195
- '',
196
- ];
197
- for (const l of box(dimLines, 45)) console.log(l);
299
+ const allLines = [''];
300
+ allLines.push(...dimLine('Thinking', 'decomposition', scores.decomposition));
301
+ allLines.push('');
302
+ allLines.push(...dimLine('Debugging', 'debugCycles', scores.debugCycles));
303
+ allLines.push('');
304
+ allLines.push(...dimLine('AI Leverage', 'aiLeverage', scores.aiLeverage));
305
+ allLines.push('');
306
+ allLines.push(...dimLine('Workflow', 'sessionStructure', scores.sessionStructure));
307
+ allLines.push('');
308
+
309
+ for (const l of box(allLines, 53)) console.log(l);
310
+ console.log();
311
+ }
312
+
313
+ // ══════════════════════════════════════════════
314
+ // CROSS-PLATFORM
315
+ // ══════════════════════════════════════════════
316
+
317
+ function displayCrossPlatform(perToolScores) {
318
+ if (!perToolScores || Object.keys(perToolScores).length <= 1) return;
319
+
320
+ console.log(dim(' CROSS-PLATFORM\n'));
321
+ const sorted = Object.entries(perToolScores).sort((a, b) => {
322
+ if (a[1].score === null) return 1;
323
+ if (b[1].score === null) return -1;
324
+ return b[1].score - a[1].score;
325
+ });
326
+ const maxSessions = Math.max(...sorted.map(([, v]) => v.sessions));
327
+
328
+ for (const [tool, data] of sorted) {
329
+ if (data.score !== null) {
330
+ const isPrimary = data.sessions === maxSessions;
331
+ const label = isPrimary ? 'primary tool' : data.sessions < 5 ? 'limited usage' : 'active';
332
+ console.log(` ${pad(bold(tool), 18)} ${bold(String(data.score))} ${progressBar(data.score)} ${dim(label)}`);
333
+ } else {
334
+ console.log(` ${pad(bold(tool), 18)} ${dim('--')} ${dim('\u2591'.repeat(18))} ${dim('limited data')}`);
335
+ }
336
+ }
198
337
  console.log();
199
338
  }
200
339
 
201
340
  // ══════════════════════════════════════════════
202
- // DIMENSION NARRATIVES (data-driven, punchy)
341
+ // PROJECT ANALYSIS
342
+ // ══════════════════════════════════════════════
343
+
344
+ function displayProjects(insights) {
345
+ const projects = insights?.projectComplexity;
346
+ if (!projects || projects.length === 0) return;
347
+
348
+ console.log(dim(' PROJECT ANALYSIS\n'));
349
+ for (const p of projects) {
350
+ const compColor = p.complexity === 'HIGH' ? green : p.complexity === 'MEDIUM' ? yellow : dim;
351
+ const signals = p.signals.length > 0 ? p.signals.join(' \u00B7 ') : '';
352
+ console.log(` ${pad(bold(p.name), 32)} Complexity: ${compColor(p.complexity)}`);
353
+ if (signals) {
354
+ console.log(` ${dim(signals)}`);
355
+ }
356
+ console.log(` ${dim(`${p.sessions} sessions \u00B7 ${numberFormat(p.exchanges)} exchanges \u00B7 ${p.daysActive} days active`)}`);
357
+ console.log();
358
+ }
359
+ }
360
+
361
+ // ══════════════════════════════════════════════
362
+ // DIMENSION NARRATIVES — Clinical, benchmarked
203
363
  // ══════════════════════════════════════════════
204
364
 
205
365
  export function displayNarratives(metrics, prose) {
206
- // Track shown snippets globally to avoid duplicates across sections
207
366
  const shownSnippets = new Set();
208
- function showUniqueSnippet(prompt) {
367
+ function showLabeledSnippet(label, prompt) {
209
368
  if (!prompt) return;
210
- const s = snippet(prompt, 70);
369
+ const s = cleanPrompt(prompt, 120);
211
370
  if (shownSnippets.has(s)) return;
212
371
  shownSnippets.add(s);
213
- displaySnippet(prompt);
372
+ displayLabeledSnippet(label, prompt);
214
373
  }
215
374
 
216
375
  if (prose && prose.sections) {
217
- // Use DeepSeek-generated prose with inline prompt snippets per section
218
376
  const sectionSnippetMap = {
219
- 'thinking': pickExample(metrics.decomposition.examples, 'decomposition'),
220
- 'debugging': pickExample(metrics.debugCycles.examples, 'specific_report') || pickExample(metrics.debugCycles.examples, 'quick_fix'),
221
- 'ai leverage': pickExample(metrics.aiLeverage.examples, 'architectural') || pickExample(metrics.aiLeverage.examples, 'planning'),
222
- 'workflow': pickExample(metrics.sessionStructure.examples, 'context_setting') || pickExample(metrics.sessionStructure.examples, 'refinement'),
377
+ 'thinking': { label: 'Evidence', prompt: pickExample(metrics.decomposition.examples, 'decomposition') },
378
+ 'debugging': { label: 'Evidence', prompt: pickExample(metrics.debugCycles.examples, 'specific_report') || pickExample(metrics.debugCycles.examples, 'quick_fix') },
379
+ 'ai leverage': { label: 'Evidence', prompt: pickExample(metrics.aiLeverage.examples, 'architectural') || pickExample(metrics.aiLeverage.examples, 'planning') },
380
+ 'workflow': { label: 'Evidence', prompt: pickExample(metrics.sessionStructure.examples, 'context_setting') || pickExample(metrics.sessionStructure.examples, 'refinement') },
223
381
  };
224
382
 
225
383
  for (const section of prose.sections) {
@@ -228,115 +386,204 @@ export function displayNarratives(metrics, prose) {
228
386
  for (const line of lines) {
229
387
  console.log(` ${dim(line.trim())}`);
230
388
  }
231
- // Show relevant prompt snippet under this section
232
389
  const titleLower = section.title.toLowerCase();
233
- const matchedSnippet = sectionSnippetMap[titleLower];
234
- if (matchedSnippet) showUniqueSnippet(matchedSnippet);
390
+ const matched = sectionSnippetMap[titleLower];
391
+ if (matched && matched.prompt) showLabeledSnippet(matched.label, matched.prompt);
235
392
  console.log();
236
393
  }
237
394
  } else {
238
- // Fallback: data-driven bullet points with inline snippets
239
- displayDataNarrativesWithTracker(metrics, shownSnippets);
395
+ displayDataNarratives(metrics, shownSnippets);
240
396
  }
241
397
  }
242
398
 
243
- function displayDataNarrativesWithTracker(metrics, shownSnippets) {
399
+ function displayDataNarratives(metrics, shownSnippets) {
244
400
  const d = metrics.decomposition.details;
245
401
  const db = metrics.debugCycles.details;
246
402
  const ai = metrics.aiLeverage.details;
247
403
  const ss = metrics.sessionStructure.details;
248
- const dEx = metrics.decomposition.examples || [];
249
- const dbEx = metrics.debugCycles.examples || [];
250
- const aiEx = metrics.aiLeverage.examples || [];
251
- const ssEx = metrics.sessionStructure.examples || [];
252
404
 
253
- function showUniqueSnippet(prompt) {
405
+ function showSnippet(label, prompt) {
254
406
  if (!prompt) return;
255
- const s = snippet(prompt, 70);
407
+ const s = cleanPrompt(prompt, 120);
256
408
  if (shownSnippets.has(s)) return;
257
409
  shownSnippets.add(s);
258
- displaySnippet(prompt);
410
+ displayLabeledSnippet(label, prompt);
259
411
  }
260
412
 
261
- // Thinking
413
+ // Thinking — clinical
262
414
  console.log(` ${bold('\uD83E\uDDE0 THINKING')}`);
263
- const exchPerSession = d.avgExchangesPerSession;
264
- console.log(` ${dim(exchPerSession > 20 ? `${exchPerSession} avg exchanges/session \u2014 marathon builder` : exchPerSession > 8 ? `${exchPerSession} avg exchanges/session \u2014 iterative` : `${exchPerSession} avg exchanges/session \u2014 concise`)}`);
265
- console.log(` ${dim(d.avgPromptLength > 500 ? `${numberFormat(d.avgPromptLength)} char avg prompt \u2014 thinks out loud` : `${d.avgPromptLength} char avg prompt \u2014 concise communicator`)}`);
266
- console.log(` ${dim(d.multiStepSessions > d.singleShotSessions * 2 ? 'Multi-step decomposition over single-shot' : 'Mix of multi-step and single-shot sessions')}`);
267
- showUniqueSnippet(pickExample(dEx, 'decomposition'));
415
+ console.log(` ${dim(`Avg session depth: ${d.avgExchangesPerSession} exchanges (benchmark: ${BENCHMARKS.avgExchangesPerSession})`)}`);
416
+ console.log(` ${dim(`Avg prompt length: ${numberFormat(d.avgPromptLength)} chars (benchmark: ${BENCHMARKS.avgPromptLength})`)}`);
417
+ console.log(` ${dim(`Multi-step ratio: ${d.multiStepSessions}/${d.totalSessions} sessions (${Math.round(d.multiStepSessions / Math.max(1, d.totalSessions) * 100)}%)`)}`);
418
+ showSnippet('Evidence', pickExample(metrics.decomposition.examples, 'decomposition'));
268
419
  console.log();
269
420
 
270
- // Debugging
421
+ // Debugging — clinical
271
422
  console.log(` ${bold('\u26A1 DEBUGGING')}`);
272
- const turns = db.avgTurnsToResolve;
273
- console.log(` ${dim(turns <= 2 ? `${turns} turns to resolve \u2014 surgical` : turns <= 4 ? `${turns} turns to resolve \u2014 efficient` : `${turns} turns to resolve \u2014 iterative`)}`);
274
- console.log(` ${dim(`${db.specificReportRatio}% specific error reports`)}`);
275
- console.log(` ${dim(db.longLoops === 0 ? 'Zero extended debug loops detected' : `${db.longLoops} extended debug loops`)}`);
276
- showUniqueSnippet(pickExample(dbEx, 'specific_report') || pickExample(dbEx, 'quick_fix'));
423
+ console.log(` ${dim(`Avg resolution: ${db.avgTurnsToResolve} turns (benchmark: ${BENCHMARKS.avgTurnsToResolve})`)}`);
424
+ console.log(` ${dim(`Specific reports: ${db.specificReportRatio}% (benchmark: ${BENCHMARKS.specificReportRatio}%)`)}`);
425
+ console.log(` ${dim(`Extended loops: ${db.longLoops} | Quick fixes: ${db.quickFixes}/${db.totalDebugSequences}`)}`);
426
+ showSnippet('Evidence', pickExample(metrics.debugCycles.examples, 'specific_report') || pickExample(metrics.debugCycles.examples, 'quick_fix'));
277
427
  console.log();
278
428
 
279
- // AI Leverage
429
+ // AI Leverage — clinical
280
430
  console.log(` ${bold('\uD83D\uDD27 AI LEVERAGE')}`);
281
- console.log(` ${dim(`${ai.highLevelRatio}% architecture prompts \u2014 ${ai.highLevelRatio > 25 ? 'high-level partner usage' : 'room to leverage AI more strategically'}`)}`);
282
- const codingRatio = ai.toolDiversity.coding > ai.toolDiversity.research ? 'Coding-heavy' : 'Research-heavy';
283
- console.log(` ${dim(`${codingRatio} over ${ai.toolDiversity.coding > ai.toolDiversity.research ? 'research' : 'coding'}-heavy`)}`);
284
- console.log(` ${dim(`${ai.architecturalPrompts} architectural, ${ai.planningPrompts} planning, ${ai.exploratoryPrompts} exploratory`)}`);
285
- showUniqueSnippet(pickExample(aiEx, 'architectural') || pickExample(aiEx, 'planning'));
431
+ console.log(` ${dim(`High-level ratio: ${ai.highLevelRatio}% (benchmark: ${BENCHMARKS.highLevelRatio}%)`)}`);
432
+ console.log(` ${dim(`Breakdown: ${ai.architecturalPrompts} architectural \u00B7 ${ai.planningPrompts} planning \u00B7 ${ai.exploratoryPrompts} exploratory`)}`);
433
+ console.log(` ${dim(`Boilerplate: ${ai.boilerplatePrompts} (${Math.round(ai.boilerplatePrompts / Math.max(1, ai.totalPrompts) * 100)}%) \u00B7 Testing: ${ai.testingPrompts}`)}`);
434
+ showSnippet('Evidence', pickExample(metrics.aiLeverage.examples, 'architectural') || pickExample(metrics.aiLeverage.examples, 'planning'));
286
435
  console.log();
287
436
 
288
- // Workflow
437
+ // Workflow — clinical
289
438
  console.log(` ${bold('\uD83D\uDCD0 WORKFLOW')}`);
290
- console.log(` ${dim(`${ss.contextSetRatio}% context-setting rate \u2014 ${ss.contextSetRatio > 50 ? 'deliberate' : ss.contextSetRatio > 25 ? 'moderate' : 'low'}`)}`);
291
- console.log(` ${dim(`${ss.reviewEndRatio}% sessions end with review`)}`);
292
- console.log(` ${dim(`${ss.refinementRatio}% refinement rate \u2014 ${ss.refinementRatio > 20 ? 'critical eye' : 'accepts readily'}`)}`);
293
- showUniqueSnippet(pickExample(ssEx, 'context_setting') || pickExample(ssEx, 'refinement'));
439
+ console.log(` ${dim(`Context-setting: ${ss.contextSetRatio}% (benchmark: ${BENCHMARKS.contextSetRatio}%)`)}`);
440
+ console.log(` ${dim(`Review at end: ${ss.reviewEndRatio}% (benchmark: ${BENCHMARKS.reviewEndRatio}%)`)}`);
441
+ console.log(` ${dim(`Refinement rate: ${ss.refinementRatio}% (benchmark: ${BENCHMARKS.refinementRatio}%)`)}`);
442
+ showSnippet('Evidence', pickExample(metrics.sessionStructure.examples, 'context_setting') || pickExample(metrics.sessionStructure.examples, 'refinement'));
294
443
  console.log();
295
444
  }
296
445
 
297
-
298
446
  // ══════════════════════════════════════════════
299
- // EASTER EGGS
447
+ // SIGNATURES — What makes you distinctive
300
448
  // ══════════════════════════════════════════════
301
449
 
302
- export function displayEasterEggs(result, metrics) {
303
- const { overall } = result;
304
- const db = metrics.debugCycles.details;
450
+ function displaySignatures(insights) {
451
+ const sigs = insights?.signatures;
452
+ if (!sigs || sigs.length === 0) return;
453
+
454
+ console.log(dim(' YOUR SIGNATURES\n'));
455
+ for (const sig of sigs) {
456
+ console.log(` ${cyan('\u2726')} ${bold(sig.name)}`);
457
+ const lines = wrapText(sig.detail, 49);
458
+ for (const line of lines) {
459
+ console.log(` ${dim(line)}`);
460
+ }
461
+ console.log();
462
+ }
463
+ }
464
+
465
+ // ══════════════════════════════════════════════
466
+ // WATCH POINTS — Areas to improve
467
+ // ══════════════════════════════════════════════
305
468
 
306
- const eggs = [];
469
+ function displayWatchPoints(insights) {
470
+ const wps = insights?.watchPoints;
471
+ if (!wps || wps.length === 0) return;
307
472
 
308
- if (overall >= 90) {
309
- eggs.push('Top-tier. Companies should be applying to you.');
473
+ console.log(dim(' WATCH POINTS\n'));
474
+ for (const wp of wps) {
475
+ console.log(` ${orange('\u26A0')} ${bold(wp.name)}`);
476
+ const lines = wrapText(wp.detail, 49);
477
+ for (const line of lines) {
478
+ console.log(` ${dim(line)}`);
479
+ }
480
+ console.log();
310
481
  }
482
+ }
311
483
 
312
- if (db.longLoops === 0 && db.totalDebugSequences > 10) {
313
- eggs.push('Zero debug spirals. Either you\'re exceptional or your AI is.');
484
+ // ══════════════════════════════════════════════
485
+ // TRAJECTORY Weekly evolution
486
+ // ══════════════════════════════════════════════
487
+
488
+ function displayTrajectory(insights) {
489
+ const traj = insights?.trajectory;
490
+ if (!traj) return;
491
+
492
+ console.log(dim(' YOUR TRAJECTORY\n'));
493
+ for (let i = 0; i < traj.weeks.length; i++) {
494
+ const w = traj.weeks[i];
495
+ const prev = i > 0 ? traj.weeks[i - 1] : null;
496
+ const arrow = prev ? (w.score > prev.score ? green(' \u2191') : w.score < prev.score ? orange(' \u2193') : dim(' \u2192')) : '';
497
+ const tierLabel = dimTierLabel(w.score);
498
+ console.log(` ${dim(pad(w.label, 16))} Score: ${scoreColor(w.score).bold(String(w.score))} ${dimTierColor(w.score)(tierLabel)}${arrow}`);
314
499
  }
500
+ console.log();
501
+ const velColor = traj.velocityLabel === 'FAST' ? green : traj.velocityLabel === 'STEADY' ? cyan : traj.velocityLabel === 'STABLE' ? yellow : orange;
502
+ console.log(` ${dim('Learning velocity:')} ${velColor(traj.velocityLabel)} ${dim('\u2014 ' + traj.velocityDetail)}`);
503
+ console.log();
504
+ }
505
+
506
+ // ══════════════════════════════════════════════
507
+ // ASSESSMENT — Narrative paragraph
508
+ // ══════════════════════════════════════════════
315
509
 
316
- if (db.vagueReports === 0 && db.totalDebugSequences > 20) {
317
- eggs.push('Never once said "it\'s broken, fix it." Respect.');
510
+ function displayAssessment(insights) {
511
+ const assessment = insights?.assessment;
512
+ if (!assessment) return;
513
+
514
+ console.log(dim(' ASSESSMENT\n'));
515
+ const lines = wrapText(assessment, 51);
516
+ for (const line of lines) {
517
+ console.log(` ${dim(line)}`);
318
518
  }
519
+ console.log();
520
+ }
319
521
 
320
- if (eggs.length > 0) {
321
- for (const egg of eggs) {
322
- console.log(` ${dim('\u2727')} ${dim.italic(egg)}`);
323
- }
522
+ // ══════════════════════════════════════════════
523
+ // CONFIDENCE INDICATOR
524
+ // ══════════════════════════════════════════════
525
+
526
+ function displayConfidence(insights, sessionStats) {
527
+ const conf = insights?.confidence;
528
+ if (!conf) return;
529
+
530
+ const barWidth = 18;
531
+ const filled = Math.round((conf.score / 100) * barWidth);
532
+ const empty = barWidth - filled;
533
+ const confColor = conf.level === 'HIGH' ? green : conf.level === 'MODERATE' ? yellow : orange;
534
+ const bar = confColor('\u2588'.repeat(filled)) + dim('\u2591'.repeat(empty));
535
+
536
+ console.log(` ${dim('Profile confidence:')} ${bar} ${confColor(conf.level)}`);
537
+ if (sessionStats) {
538
+ console.log(` ${dim(`Based on ${sessionStats.totalSessions} sessions across ${sessionStats.tools.length} tool${sessionStats.tools.length > 1 ? 's' : ''}.`)}`);
539
+ }
540
+ console.log();
541
+ if (conf.level !== 'HIGH') {
542
+ console.log(` ${dim('Confidence increases with more sessions and tool')}`);
543
+ console.log(` ${dim('diversity. Run again in 30 days for updated assessment.')}`);
324
544
  console.log();
325
545
  }
326
546
  }
327
547
 
328
548
  // ══════════════════════════════════════════════
329
- // VIRAL ENDING
549
+ // METHODOLOGY FOOTER
330
550
  // ══════════════════════════════════════════════
331
551
 
332
- export function displayEnding(result) {
552
+ function displayMethodology() {
553
+ console.log(doubleRule());
554
+ console.log();
555
+ console.log(` ${dim('METHODOLOGY')}`);
556
+ console.log(` ${dim('Scores derived from structural analysis of prompt')}`);
557
+ console.log(` ${dim('patterns, debug sequences, and session architecture')}`);
558
+ console.log(` ${dim('across detected AI coding tools. Tier placement based')}`);
559
+ console.log(` ${dim('on distribution of scored engineers. No prompt content')}`);
560
+ console.log(` ${dim('is stored or transmitted. Learn more: chekk.dev/methodology')}`);
561
+ console.log();
562
+ }
563
+
564
+ // ══════════════════════════════════════════════
565
+ // NEXT STEPS — Credential issuance
566
+ // ══════════════════════════════════════════════
567
+
568
+ function displayNextSteps(result) {
333
569
  const { overall, archetype, tier, tierBadge } = result;
334
570
  const tc = tierColor(tier);
335
571
 
336
572
  console.log(doubleRule());
337
573
  console.log();
338
- console.log(` ${dim('\u2192 Claim your profile:')} ${cyan.underline('chekk.dev/claim')}`);
339
- console.log(` ${dim('\u2192 Leaderboard:')} ${cyan.underline('chekk.dev/leaderboard')}`);
574
+ console.log(` ${bold.white('NEXT STEPS')}`);
575
+ console.log();
576
+ console.log(` ${dim('\u2192 Claim your verified profile')}`);
577
+ console.log(` ${dim('Link your GitHub and LinkedIn to create your')}`);
578
+ console.log(` ${dim('portable engineering credential.')}`);
579
+ console.log(` ${cyan.underline('chekk.dev/claim')}`);
580
+ console.log();
581
+ console.log(` ${dim('\u2192 View industry benchmarks')}`);
582
+ console.log(` ${dim('See how you compare across scored engineers.')}`);
583
+ console.log(` ${cyan.underline('chekk.dev/benchmarks')}`);
584
+ console.log();
585
+ console.log(` ${dim('\u2192 Export this report')}`);
586
+ console.log(` ${dim('npx chekk --json')}`);
340
587
  console.log();
341
588
 
342
589
  // Copy-paste share line
@@ -346,7 +593,7 @@ export function displayEnding(result) {
346
593
  console.log(` ${dim('\u2191 Copy this to share')}`);
347
594
  console.log();
348
595
  console.log(doubleRule());
349
- console.log(` ${dim('chekk.dev \u2014 the engineering capability score')}`);
596
+ console.log(` ${dim('chekk.dev \u2014 engineering capability profiles')}`);
350
597
  console.log();
351
598
  }
352
599
 
@@ -375,65 +622,98 @@ export function displayVerbose(metrics, sessions) {
375
622
  }
376
623
  console.log();
377
624
 
378
- // Full metric details
379
- console.log(bold(' DECOMPOSITION DETAILS'));
380
- for (const [k, v] of Object.entries(metrics.decomposition.details)) {
381
- console.log(` ${dim(pad(k, 30))} ${dim(String(v) + (typeof v === 'number' && k.includes('Ratio') ? '%' : ''))}`);
382
- }
625
+ // Decomposition
626
+ const d = metrics.decomposition.details;
627
+ console.log(bold(' DECOMPOSITION'));
628
+ console.log(` ${dim(pad('Avg session depth', 30))} ${dim(String(d.avgExchangesPerSession))} ${dim(`(benchmark: ${BENCHMARKS.avgExchangesPerSession})`)}`);
629
+ console.log(` ${dim(pad('Avg prompt length', 30))} ${dim(d.avgPromptLength + ' chars')} ${dim(`(benchmark: ${BENCHMARKS.avgPromptLength})`)}`);
630
+ console.log(` ${dim(pad('Multi-step sessions', 30))} ${dim(String(d.multiStepSessions) + '/' + d.totalSessions)} ${dim(d.multiStepSessions > d.totalSessions * 0.5 ? '\u2014 strong' : '\u2014 room to grow')}`);
631
+ console.log(` ${dim(pad('Single-shot sessions', 30))} ${dim(String(d.singleShotSessions))}`);
632
+ console.log(` ${dim(pad('Contextual followups', 30))} ${dim(d.contextualFollowupRatio + '%')} ${dim(d.contextualFollowupRatio > 20 ? '\u2014 builds on context well' : '\u2014 could reference prior work more')}`);
383
633
  console.log();
384
634
 
385
- console.log(bold(' DEBUG CYCLE DETAILS'));
386
- for (const [k, v] of Object.entries(metrics.debugCycles.details)) {
387
- console.log(` ${dim(pad(k, 30))} ${dim(String(v))}`);
388
- }
635
+ // Debug
636
+ const db = metrics.debugCycles.details;
637
+ console.log(bold(' DEBUG CYCLES'));
638
+ console.log(` ${dim(pad('Total sequences', 30))} ${dim(String(db.totalDebugSequences))}`);
639
+ console.log(` ${dim(pad('Avg turns to resolve', 30))} ${dim(String(db.avgTurnsToResolve))} ${dim(`(benchmark: ${BENCHMARKS.avgTurnsToResolve})`)}`);
640
+ console.log(` ${dim(pad('Quick fixes (\u22642 turns)', 30))} ${dim(String(db.quickFixes))}`);
641
+ console.log(` ${dim(pad('Extended loops (>5 turns)', 30))} ${dim(String(db.longLoops))} ${dim(db.longLoops === 0 ? '\u2014 zero spirals' : '')}`);
642
+ console.log(` ${dim(pad('Specific report ratio', 30))} ${dim(db.specificReportRatio + '%')} ${dim(`(benchmark: ${BENCHMARKS.specificReportRatio}%)`)}`);
643
+ console.log(` ${dim(pad('Vague reports', 30))} ${dim(String(db.vagueReports))} ${dim(db.vagueReports === 0 ? '\u2014 never vague' : '')}`);
389
644
  console.log();
390
645
 
391
- console.log(bold(' AI LEVERAGE DETAILS'));
392
- for (const [k, v] of Object.entries(metrics.aiLeverage.details)) {
393
- if (typeof v === 'object') {
394
- for (const [k2, v2] of Object.entries(v)) {
395
- console.log(` ${dim(pad(` ${k}.${k2}`, 30))} ${dim(String(v2))}`);
396
- }
397
- } else {
398
- console.log(` ${dim(pad(k, 30))} ${dim(String(v))}`);
399
- }
400
- }
646
+ // AI Leverage
647
+ const ai = metrics.aiLeverage.details;
648
+ console.log(bold(' AI LEVERAGE'));
649
+ console.log(` ${dim(pad('Total prompts', 30))} ${dim(numberFormat(ai.totalPrompts))}`);
650
+ console.log(` ${dim(pad('Architectural', 30))} ${dim(String(ai.architecturalPrompts))} ${dim(`(${Math.round(ai.architecturalPrompts / Math.max(1, ai.totalPrompts) * 100)}%)`)}`);
651
+ console.log(` ${dim(pad('Planning', 30))} ${dim(String(ai.planningPrompts))} ${dim(`(${Math.round(ai.planningPrompts / Math.max(1, ai.totalPrompts) * 100)}%)`)}`);
652
+ console.log(` ${dim(pad('Exploratory', 30))} ${dim(String(ai.exploratoryPrompts))} ${dim(`(${Math.round(ai.exploratoryPrompts / Math.max(1, ai.totalPrompts) * 100)}%)`)}`);
653
+ console.log(` ${dim(pad('Boilerplate', 30))} ${dim(String(ai.boilerplatePrompts))} ${dim(`(${Math.round(ai.boilerplatePrompts / Math.max(1, ai.totalPrompts) * 100)}%)`)} ${dim(ai.boilerplatePrompts < ai.totalPrompts * 0.05 ? '\u2014 minimal' : '')}`);
654
+ console.log(` ${dim(pad('Testing', 30))} ${dim(String(ai.testingPrompts))}`);
655
+ console.log(` ${dim(pad('High-level ratio', 30))} ${dim(ai.highLevelRatio + '%')} ${dim(`(benchmark: ${BENCHMARKS.highLevelRatio}%)`)}`);
401
656
  console.log();
402
657
 
403
- console.log(bold(' SESSION STRUCTURE DETAILS'));
404
- for (const [k, v] of Object.entries(metrics.sessionStructure.details)) {
405
- if (typeof v === 'object') {
406
- for (const [k2, v2] of Object.entries(v)) {
407
- console.log(` ${dim(pad(` ${k}.${k2}`, 30))} ${dim(String(v2))}`);
408
- }
409
- } else {
410
- console.log(` ${dim(pad(k, 30))} ${dim(String(v) + (typeof v === 'number' && k.includes('Ratio') ? '%' : ''))}`);
411
- }
658
+ // Session structure
659
+ const ss = metrics.sessionStructure.details;
660
+ console.log(bold(' SESSION STRUCTURE'));
661
+ console.log(` ${dim(pad('Context-setting', 30))} ${dim(ss.contextSetRatio + '%')} ${dim(`(benchmark: ${BENCHMARKS.contextSetRatio}%)`)}`);
662
+ console.log(` ${dim(pad('Plan before code', 30))} ${dim(ss.planBeforeCodeRatio + '%')}`);
663
+ console.log(` ${dim(pad('Review at end', 30))} ${dim(ss.reviewEndRatio + '%')} ${dim(`(benchmark: ${BENCHMARKS.reviewEndRatio}%)`)}`);
664
+ console.log(` ${dim(pad('Refinement rate', 30))} ${dim(ss.refinementRatio + '%')} ${dim(`(benchmark: ${BENCHMARKS.refinementRatio}%)`)}`);
665
+ console.log(` ${dim(pad('Avg first prompt', 30))} ${dim(ss.avgFirstPromptLength + ' chars')}`);
666
+ if (ss.durationDistribution) {
667
+ const dur = ss.durationDistribution;
668
+ console.log(` ${dim(pad('Focused sessions (10-45m)', 30))} ${dim(String(dur.focused))}`);
669
+ console.log(` ${dim(pad('Short (<5m)', 30))} ${dim(String(dur.short))}`);
670
+ console.log(` ${dim(pad('Medium (5-60m)', 30))} ${dim(String(dur.medium))}`);
671
+ console.log(` ${dim(pad('Long (>60m)', 30))} ${dim(String(dur.long))}`);
412
672
  }
413
673
  console.log();
414
674
  }
415
675
 
416
676
  // ══════════════════════════════════════════════
417
- // OFFLINE DISPLAY (no API)
677
+ // OFFLINE DISPLAY
418
678
  // ══════════════════════════════════════════════
419
679
 
420
- export function displayOffline(result, metrics) {
421
- displayScore(result, null);
422
- displayDataNarrativesWithTracker(metrics, new Set());
423
- displayEasterEggs(result, metrics);
424
- console.log(dim(' Run without --offline for personalized AI-generated insights\n'));
425
- displayEnding(result);
680
+ export function displayOffline(result, metrics, extra = {}) {
681
+ displayProfileHeader(result, extra);
682
+ displaySummary(result, extra);
683
+ displayArchetype(result);
684
+ displayDimensions(result);
685
+ displayCrossPlatform(extra.perToolScores);
686
+ displayDataNarratives(metrics, new Set());
687
+ displayProjects(extra.insights);
688
+ displaySignatures(extra.insights);
689
+ displayWatchPoints(extra.insights);
690
+ displayTrajectory(extra.insights);
691
+ displayAssessment(extra.insights);
692
+ displayConfidence(extra.insights, extra.sessionStats);
693
+ displayMethodology();
694
+ console.log(dim(' Run without --offline for AI-generated narrative insights\n'));
695
+ displayNextSteps(result);
426
696
  }
427
697
 
428
698
  // ══════════════════════════════════════════════
429
699
  // FULL ONLINE DISPLAY
430
700
  // ══════════════════════════════════════════════
431
701
 
432
- export function displayFull(result, metrics, prose) {
433
- displayScore(result, prose);
702
+ export function displayFull(result, metrics, prose, extra = {}) {
703
+ displayProfileHeader(result, extra);
704
+ displaySummary(result, extra);
705
+ displayArchetype(result);
706
+ displayDimensions(result);
707
+ displayCrossPlatform(extra.perToolScores);
434
708
  displayNarratives(metrics, prose);
435
- displayEasterEggs(result, metrics);
436
- displayEnding(result);
709
+ displayProjects(extra.insights);
710
+ displaySignatures(extra.insights);
711
+ displayWatchPoints(extra.insights);
712
+ displayTrajectory(extra.insights);
713
+ displayAssessment(extra.insights);
714
+ displayConfidence(extra.insights, extra.sessionStats);
715
+ displayMethodology();
716
+ displayNextSteps(result);
437
717
  }
438
718
 
439
719
  // ── Utility ──