chekk 0.5.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -0
- package/dist/index.js +448 -0
- package/package.json +18 -34
- package/bin/chekk.js +0 -62
- package/src/detect.js +0 -146
- package/src/display.js +0 -1153
- package/src/index.js +0 -281
- package/src/insights.js +0 -661
- package/src/metrics/ai-leverage.js +0 -186
- package/src/metrics/debug-cycles.js +0 -204
- package/src/metrics/decomposition.js +0 -158
- package/src/metrics/session-structure.js +0 -199
- package/src/metrics/token-efficiency.js +0 -258
- package/src/parsers/claude-code.js +0 -231
- package/src/parsers/codex.js +0 -188
- package/src/parsers/cursor.js +0 -281
- package/src/scorer.js +0 -228
- package/src/upload.js +0 -140
package/src/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.4')}`,
|
|
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.4`)}`);
|
|
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
|
-
}
|