chekk 0.2.2 → 0.2.3

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/bin/chekk.js CHANGED
@@ -8,7 +8,7 @@ const program = new Command();
8
8
  program
9
9
  .name('chekk')
10
10
  .description('The engineering capability score. See how you prompt.')
11
- .version('0.2.2')
11
+ .version('0.2.3')
12
12
  .option('--offline', 'Skip AI prose generation, show data-driven output')
13
13
  .option('--verbose', 'Show detailed per-project and per-metric breakdowns')
14
14
  .option('--json', 'Output raw metrics as JSON')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chekk",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "See how you prompt. Chekk analyzes your AI coding workflow and tells you what kind of engineer you are.",
5
5
  "bin": {
6
6
  "chekk": "./bin/chekk.js"
package/src/display.js CHANGED
@@ -42,6 +42,28 @@ function pad(str, len) {
42
42
  return str + ' '.repeat(Math.max(0, len - visible.length));
43
43
  }
44
44
 
45
+ function snippet(prompt, maxLen = 70) {
46
+ if (!prompt) return null;
47
+ // Clean up whitespace / newlines
48
+ let clean = prompt.replace(/\s+/g, ' ').trim();
49
+ if (clean.length > maxLen) {
50
+ clean = clean.slice(0, maxLen - 1) + '\u2026';
51
+ }
52
+ return clean;
53
+ }
54
+
55
+ function displaySnippet(prompt, maxLen = 70) {
56
+ const s = snippet(prompt, maxLen);
57
+ if (!s) return;
58
+ console.log(` ${dim('\u201C')}${dim.italic(s)}${dim('\u201D')}`);
59
+ }
60
+
61
+ function pickExample(examples, type) {
62
+ if (!examples || !examples.length) return null;
63
+ const match = examples.find(e => e.type === type);
64
+ return match ? match.prompt : null;
65
+ }
66
+
45
67
  // ── Box drawing ──
46
68
 
47
69
  function box(lines, width = 43) {
@@ -68,7 +90,7 @@ export function displayHeader() {
68
90
  console.log();
69
91
  const lines = [
70
92
  '',
71
- ` ${bold.white('chekk')}${dim(' v0.2.2')}`,
93
+ ` ${bold.white('chekk')}${dim(' v0.2.3')}`,
72
94
  ` ${dim('the engineering capability score')}`,
73
95
  '',
74
96
  ];
@@ -196,8 +218,10 @@ export function displayNarratives(metrics, prose) {
196
218
  }
197
219
  console.log();
198
220
  }
221
+ // Show prompt evidence after AI prose
222
+ displayPromptEvidence(metrics);
199
223
  } else {
200
- // Fallback: data-driven bullet points
224
+ // Fallback: data-driven bullet points with inline snippets
201
225
  displayDataNarratives(metrics);
202
226
  }
203
227
  }
@@ -207,6 +231,20 @@ function displayDataNarratives(metrics) {
207
231
  const db = metrics.debugCycles.details;
208
232
  const ai = metrics.aiLeverage.details;
209
233
  const ss = metrics.sessionStructure.details;
234
+ const dEx = metrics.decomposition.examples || [];
235
+ const dbEx = metrics.debugCycles.examples || [];
236
+ const aiEx = metrics.aiLeverage.examples || [];
237
+ const ssEx = metrics.sessionStructure.examples || [];
238
+
239
+ // Track shown snippets to avoid duplicates
240
+ const shownSnippets = new Set();
241
+ function showUniqueSnippet(prompt) {
242
+ if (!prompt) return;
243
+ const s = snippet(prompt, 70);
244
+ if (shownSnippets.has(s)) return;
245
+ shownSnippets.add(s);
246
+ displaySnippet(prompt);
247
+ }
210
248
 
211
249
  // Thinking
212
250
  console.log(` ${bold('\uD83E\uDDE0 THINKING')}`);
@@ -214,6 +252,7 @@ function displayDataNarratives(metrics) {
214
252
  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`)}`);
215
253
  console.log(` ${dim(d.avgPromptLength > 500 ? `${numberFormat(d.avgPromptLength)} char avg prompt \u2014 thinks out loud` : `${d.avgPromptLength} char avg prompt \u2014 concise communicator`)}`);
216
254
  console.log(` ${dim(d.multiStepSessions > d.singleShotSessions * 2 ? 'Multi-step decomposition over single-shot' : 'Mix of multi-step and single-shot sessions')}`);
255
+ showUniqueSnippet(pickExample(dEx, 'decomposition'));
217
256
  console.log();
218
257
 
219
258
  // Debugging
@@ -222,6 +261,7 @@ function displayDataNarratives(metrics) {
222
261
  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`)}`);
223
262
  console.log(` ${dim(`${db.specificReportRatio}% specific error reports`)}`);
224
263
  console.log(` ${dim(db.longLoops === 0 ? 'Zero extended debug loops detected' : `${db.longLoops} extended debug loops`)}`);
264
+ showUniqueSnippet(pickExample(dbEx, 'specific_report') || pickExample(dbEx, 'quick_fix'));
225
265
  console.log();
226
266
 
227
267
  // AI Leverage
@@ -230,6 +270,7 @@ function displayDataNarratives(metrics) {
230
270
  const codingRatio = ai.toolDiversity.coding > ai.toolDiversity.research ? 'Coding-heavy' : 'Research-heavy';
231
271
  console.log(` ${dim(`${codingRatio} over ${ai.toolDiversity.coding > ai.toolDiversity.research ? 'research' : 'coding'}-heavy`)}`);
232
272
  console.log(` ${dim(`${ai.architecturalPrompts} architectural, ${ai.planningPrompts} planning, ${ai.exploratoryPrompts} exploratory`)}`);
273
+ showUniqueSnippet(pickExample(aiEx, 'architectural') || pickExample(aiEx, 'planning'));
233
274
  console.log();
234
275
 
235
276
  // Workflow
@@ -237,6 +278,51 @@ function displayDataNarratives(metrics) {
237
278
  console.log(` ${dim(`${ss.contextSetRatio}% context-setting rate \u2014 ${ss.contextSetRatio > 50 ? 'deliberate' : ss.contextSetRatio > 25 ? 'moderate' : 'low'}`)}`);
238
279
  console.log(` ${dim(`${ss.reviewEndRatio}% sessions end with review`)}`);
239
280
  console.log(` ${dim(`${ss.refinementRatio}% refinement rate \u2014 ${ss.refinementRatio > 20 ? 'critical eye' : 'accepts readily'}`)}`);
281
+ showUniqueSnippet(pickExample(ssEx, 'context_setting') || pickExample(ssEx, 'refinement'));
282
+ console.log();
283
+ }
284
+
285
+ // ── Prompt evidence block (shown after AI prose) ──
286
+
287
+ function displayPromptEvidence(metrics) {
288
+ const allExamples = [
289
+ ...(metrics.decomposition.examples || []),
290
+ ...(metrics.debugCycles.examples || []),
291
+ ...(metrics.aiLeverage.examples || []),
292
+ ...(metrics.sessionStructure.examples || []),
293
+ ];
294
+
295
+ if (allExamples.length === 0) return;
296
+
297
+ // Pick up to 3 best examples to show as evidence, deduplicated
298
+ const candidates = [];
299
+ const arch = pickExample(metrics.aiLeverage.examples, 'architectural');
300
+ const decomp = pickExample(metrics.decomposition.examples, 'decomposition');
301
+ const debug = pickExample(metrics.debugCycles.examples, 'specific_report');
302
+ const ctx = pickExample(metrics.sessionStructure.examples, 'context_setting');
303
+
304
+ if (arch) candidates.push({ label: 'Architecture', prompt: arch });
305
+ if (decomp) candidates.push({ label: 'Thinking', prompt: decomp });
306
+ if (debug) candidates.push({ label: 'Debugging', prompt: debug });
307
+ if (ctx) candidates.push({ label: 'Context', prompt: ctx });
308
+
309
+ // Deduplicate by snippet text
310
+ const picks = [];
311
+ const seen = new Set();
312
+ for (const c of candidates) {
313
+ const s = snippet(c.prompt, 65);
314
+ if (!seen.has(s)) {
315
+ seen.add(s);
316
+ picks.push(c);
317
+ }
318
+ }
319
+
320
+ if (picks.length === 0) return;
321
+
322
+ console.log(dim(' YOUR PROMPTS\n'));
323
+ for (const pick of picks.slice(0, 3)) {
324
+ console.log(` ${dim(pick.label + ':')} ${dim('\u201C')}${dim.italic(snippet(pick.prompt, 65))}${dim('\u201D')}`);
325
+ }
240
326
  console.log();
241
327
  }
242
328
 
@@ -37,15 +37,32 @@ export function computeAILeverage(sessions) {
37
37
  let complexPrompts = 0; // > 200 chars with multiple sentences
38
38
  let trivialPrompts = 0; // < 50 chars, simple commands
39
39
 
40
+ // Capture representative examples
41
+ let bestArchPrompt = null; // best architectural prompt
42
+ let bestPlanPrompt = null; // best planning prompt
43
+ let bestExplorePrompt = null; // best exploratory prompt
44
+ let bestArchLen = 0;
45
+ let bestPlanLen = 0;
46
+ let bestExploreLen = 0;
47
+
40
48
  for (const session of sessions) {
41
49
  for (const exchange of session.exchanges) {
42
50
  const prompt = exchange.userPrompt || '';
43
51
  totalPrompts++;
44
52
 
45
53
  // Categorize prompt type
46
- if (architecturalPatterns.test(prompt)) architecturalPrompts++;
47
- if (planningPatterns.test(prompt)) planningPrompts++;
48
- if (exploratoryPatterns.test(prompt)) exploratoryPrompts++;
54
+ if (architecturalPatterns.test(prompt)) {
55
+ architecturalPrompts++;
56
+ if (prompt.length > bestArchLen) { bestArchLen = prompt.length; bestArchPrompt = prompt; }
57
+ }
58
+ if (planningPatterns.test(prompt)) {
59
+ planningPrompts++;
60
+ if (prompt.length > bestPlanLen) { bestPlanLen = prompt.length; bestPlanPrompt = prompt; }
61
+ }
62
+ if (exploratoryPatterns.test(prompt)) {
63
+ exploratoryPrompts++;
64
+ if (prompt.length > bestExploreLen) { bestExploreLen = prompt.length; bestExplorePrompt = prompt; }
65
+ }
49
66
  if (boilerplatePatterns.test(prompt)) boilerplatePrompts++;
50
67
  if (testingPatterns.test(prompt)) testingPrompts++;
51
68
 
@@ -103,6 +120,12 @@ export function computeAILeverage(sessions) {
103
120
  50 * 0 // baseline filler
104
121
  );
105
122
 
123
+ // Build examples — pick the best one available
124
+ const examples = [];
125
+ if (bestArchPrompt) examples.push({ type: 'architectural', prompt: bestArchPrompt });
126
+ if (bestPlanPrompt) examples.push({ type: 'planning', prompt: bestPlanPrompt });
127
+ if (bestExplorePrompt) examples.push({ type: 'exploratory', prompt: bestExplorePrompt });
128
+
106
129
  return {
107
130
  score: Math.max(0, Math.min(100, score)),
108
131
  details: {
@@ -120,5 +143,6 @@ export function computeAILeverage(sessions) {
120
143
  coding: codingToolUses,
121
144
  },
122
145
  },
146
+ examples,
123
147
  };
124
148
  }
@@ -26,10 +26,16 @@ export function computeDebugCycles(sessions) {
26
26
  let quickFixes = 0; // resolved in 1-2 turns
27
27
  let longLoops = 0; // > 5 turns to resolve
28
28
 
29
+ // Capture representative examples
30
+ let bestSpecificReport = null; // best specific error report
31
+ let bestQuickFix = null; // prompt that led to quick resolution
32
+ let bestSpecificLen = 0;
33
+
29
34
  for (const session of sessions) {
30
35
  const { exchanges } = session;
31
36
  let inDebugMode = false;
32
37
  let debugTurnCount = 0;
38
+ let debugStartPrompt = null;
33
39
 
34
40
  for (let i = 0; i < exchanges.length; i++) {
35
41
  const prompt = exchanges[i].userPrompt || '';
@@ -39,6 +45,7 @@ export function computeDebugCycles(sessions) {
39
45
  // Starting a new debug sequence
40
46
  inDebugMode = true;
41
47
  debugTurnCount = 1;
48
+ debugStartPrompt = prompt;
42
49
  totalDebugSequences++;
43
50
  } else {
44
51
  debugTurnCount++;
@@ -50,21 +57,31 @@ export function computeDebugCycles(sessions) {
50
57
  }
51
58
  if (specificDebugPatterns.test(prompt) || prompt.length > 200) {
52
59
  specificReports++;
60
+ // Track best specific report
61
+ if (prompt.length > bestSpecificLen) {
62
+ bestSpecificLen = prompt.length;
63
+ bestSpecificReport = prompt;
64
+ }
53
65
  }
54
66
  } else if (inDebugMode) {
55
67
  // Check if this exchange resolves the debug
56
68
  if (resolutionPatterns.test(prompt)) {
57
69
  totalTurnsToResolve += debugTurnCount;
58
- if (debugTurnCount <= 2) quickFixes++;
70
+ if (debugTurnCount <= 2) {
71
+ quickFixes++;
72
+ if (!bestQuickFix) bestQuickFix = debugStartPrompt;
73
+ }
59
74
  if (debugTurnCount > 5) longLoops++;
60
75
  inDebugMode = false;
61
76
  debugTurnCount = 0;
77
+ debugStartPrompt = null;
62
78
  } else {
63
79
  // Moved on without explicit resolution
64
80
  totalTurnsToResolve += debugTurnCount;
65
81
  unresolvedSequences++;
66
82
  inDebugMode = false;
67
83
  debugTurnCount = 0;
84
+ debugStartPrompt = null;
68
85
  }
69
86
  }
70
87
  }
@@ -109,6 +126,11 @@ export function computeDebugCycles(sessions) {
109
126
  (100 - longLoopPenalty) * 0.15
110
127
  );
111
128
 
129
+ // Build examples array
130
+ const examples = [];
131
+ if (bestSpecificReport) examples.push({ type: 'specific_report', prompt: bestSpecificReport });
132
+ if (bestQuickFix) examples.push({ type: 'quick_fix', prompt: bestQuickFix });
133
+
112
134
  return {
113
135
  score: Math.max(0, Math.min(100, score)),
114
136
  details: {
@@ -120,5 +142,6 @@ export function computeDebugCycles(sessions) {
120
142
  vagueReports,
121
143
  specificReports,
122
144
  },
145
+ examples,
123
146
  };
124
147
  }
@@ -26,6 +26,11 @@ export function computeDecomposition(sessions) {
26
26
  const followupPatterns = /^(now |next |then |also |and |ok |okay |great |good |perfect |after that|building on|following up|continuing)/i;
27
27
  const refinementPatterns = /^(actually |wait |hmm |instead |change |modify |update |tweak |adjust |fix |but )/i;
28
28
 
29
+ // Capture representative prompt examples
30
+ // Keep top 3 candidates and pick the 2nd-longest to avoid overlap with other metrics
31
+ const decompCandidates = [];
32
+ let bestFollowupPrompt = null;
33
+
29
34
  for (const session of sessions) {
30
35
  const { exchanges } = session;
31
36
  totalExchanges += exchanges.length;
@@ -45,10 +50,19 @@ export function computeDecomposition(sessions) {
45
50
  if (len > 500) longPromptCount++;
46
51
  if (len < 100) shortPromptCount++;
47
52
 
53
+ // Track decomposition examples (multi-sentence prompts showing task breakdown)
54
+ if (len > 150 && len < 2000) {
55
+ decompCandidates.push(prompt);
56
+ }
57
+
48
58
  // Check for contextual followups (not the first prompt in a session)
49
59
  if (i > 0) {
50
60
  if (followupPatterns.test(prompt) || refinementPatterns.test(prompt)) {
51
61
  contextualFollowups++;
62
+ // Capture best followup example
63
+ if (!bestFollowupPrompt || prompt.length > bestFollowupPrompt.length) {
64
+ bestFollowupPrompt = prompt;
65
+ }
52
66
  }
53
67
  }
54
68
  }
@@ -83,6 +97,16 @@ export function computeDecomposition(sessions) {
83
97
  depthScore * 0.2
84
98
  );
85
99
 
100
+ // Build examples array — pick a mid-length prompt to avoid overlap with other metrics
101
+ const examples = [];
102
+ if (decompCandidates.length > 0) {
103
+ decompCandidates.sort((a, b) => b.length - a.length);
104
+ // Pick ~median length to avoid the longest (which will also match ai-leverage)
105
+ const pickIdx = Math.min(Math.floor(decompCandidates.length / 3), decompCandidates.length - 1);
106
+ examples.push({ type: 'decomposition', prompt: decompCandidates[pickIdx] });
107
+ }
108
+ if (bestFollowupPrompt) examples.push({ type: 'followup', prompt: bestFollowupPrompt });
109
+
86
110
  return {
87
111
  score: Math.max(0, Math.min(100, score)),
88
112
  details: {
@@ -94,5 +118,6 @@ export function computeDecomposition(sessions) {
94
118
  longPromptRatio: promptCount > 0 ? Math.round(longPromptCount / promptCount * 100) : 0,
95
119
  contextualFollowupRatio: promptCount > 0 ? Math.round(followupRatio * 100) : 0,
96
120
  },
121
+ examples,
97
122
  };
98
123
  }
@@ -34,6 +34,11 @@ export function computeSessionStructure(sessions) {
34
34
  // First prompt length distribution (longer first prompts = more context setting)
35
35
  let firstPromptTotalLength = 0;
36
36
 
37
+ // Capture representative examples
38
+ let bestContextPrompt = null; // best context-setting opener
39
+ let bestRefinementPrompt = null; // best refinement/critical feedback
40
+ let bestContextLen = 0;
41
+
37
42
  for (const session of sessions) {
38
43
  const { exchanges, durationMinutes } = session;
39
44
  if (exchanges.length === 0) continue;
@@ -46,6 +51,11 @@ export function computeSessionStructure(sessions) {
46
51
 
47
52
  if (contextSettingPatterns.test(firstPrompt) || firstPrompt.length > 200) {
48
53
  contextSetSessions++;
54
+ // Track best context-setting prompt
55
+ if (firstPrompt.length > bestContextLen) {
56
+ bestContextLen = firstPrompt.length;
57
+ bestContextPrompt = firstPrompt;
58
+ }
49
59
  }
50
60
 
51
61
  if (planningStartPatterns.test(firstPrompt)) {
@@ -66,6 +76,10 @@ export function computeSessionStructure(sessions) {
66
76
  const prompt = exchanges[i].userPrompt || '';
67
77
  if (refinementPatterns.test(prompt)) {
68
78
  refinementCount++;
79
+ // Track best refinement example
80
+ if (!bestRefinementPrompt || prompt.length > bestRefinementPrompt.length) {
81
+ bestRefinementPrompt = prompt;
82
+ }
69
83
  }
70
84
  }
71
85
 
@@ -115,6 +129,11 @@ export function computeSessionStructure(sessions) {
115
129
  firstPromptScore * 0.15
116
130
  );
117
131
 
132
+ // Build examples array
133
+ const examples = [];
134
+ if (bestContextPrompt) examples.push({ type: 'context_setting', prompt: bestContextPrompt });
135
+ if (bestRefinementPrompt) examples.push({ type: 'refinement', prompt: bestRefinementPrompt });
136
+
118
137
  return {
119
138
  score: Math.max(0, Math.min(100, score)),
120
139
  details: {
@@ -130,5 +149,6 @@ export function computeSessionStructure(sessions) {
130
149
  focused: focusedSessions,
131
150
  },
132
151
  },
152
+ examples,
133
153
  };
134
154
  }
package/src/upload.js CHANGED
@@ -5,24 +5,38 @@ const API_BASE = 'https://chekk-production.up.railway.app/api/v1';
5
5
  /**
6
6
  * Call the Chekk API to generate personalized prose from metrics.
7
7
  */
8
+ function truncateExamples(examples, maxLen = 120) {
9
+ if (!examples || !examples.length) return [];
10
+ return examples.map(e => ({
11
+ type: e.type,
12
+ prompt: e.prompt && e.prompt.length > maxLen
13
+ ? e.prompt.replace(/\s+/g, ' ').trim().slice(0, maxLen) + '...'
14
+ : (e.prompt || '').replace(/\s+/g, ' ').trim(),
15
+ }));
16
+ }
17
+
8
18
  export async function generateProse(metrics, result, sessionStats) {
9
19
  const payload = {
10
20
  metrics: {
11
21
  decomposition: {
12
22
  score: metrics.decomposition.score,
13
23
  details: metrics.decomposition.details,
24
+ examples: truncateExamples(metrics.decomposition.examples),
14
25
  },
15
26
  debugCycles: {
16
27
  score: metrics.debugCycles.score,
17
28
  details: metrics.debugCycles.details,
29
+ examples: truncateExamples(metrics.debugCycles.examples),
18
30
  },
19
31
  aiLeverage: {
20
32
  score: metrics.aiLeverage.score,
21
33
  details: metrics.aiLeverage.details,
34
+ examples: truncateExamples(metrics.aiLeverage.examples),
22
35
  },
23
36
  sessionStructure: {
24
37
  score: metrics.sessionStructure.score,
25
38
  details: metrics.sessionStructure.details,
39
+ examples: truncateExamples(metrics.sessionStructure.examples),
26
40
  },
27
41
  },
28
42
  result: {