chekk 0.2.1 → 0.2.2
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 +1 -1
- package/package.json +1 -1
- package/src/display.js +11 -9
- package/src/index.js +31 -14
- package/src/scorer.js +138 -48
- package/src/upload.js +17 -0
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.
|
|
11
|
+
.version('0.2.2')
|
|
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
package/src/display.js
CHANGED
|
@@ -12,7 +12,8 @@ const white = chalk.white;
|
|
|
12
12
|
|
|
13
13
|
function tierColor(tier) {
|
|
14
14
|
if (tier === 'LEGENDARY') return gold;
|
|
15
|
-
if (tier === 'RARE') return purple;
|
|
15
|
+
if (tier === 'ULTRA RARE') return purple;
|
|
16
|
+
if (tier === 'RARE') return cyan;
|
|
16
17
|
if (tier === 'UNCOMMON') return blue;
|
|
17
18
|
return dim;
|
|
18
19
|
}
|
|
@@ -67,7 +68,7 @@ export function displayHeader() {
|
|
|
67
68
|
console.log();
|
|
68
69
|
const lines = [
|
|
69
70
|
'',
|
|
70
|
-
` ${bold.white('chekk')}${dim(' v0.2.
|
|
71
|
+
` ${bold.white('chekk')}${dim(' v0.2.2')}`,
|
|
71
72
|
` ${dim('the engineering capability score')}`,
|
|
72
73
|
'',
|
|
73
74
|
];
|
|
@@ -130,7 +131,7 @@ export async function displayProgressBar(durationMs = 2000) {
|
|
|
130
131
|
// ══════════════════════════════════════════════
|
|
131
132
|
|
|
132
133
|
export function displayScore(result, prose) {
|
|
133
|
-
const { overall, scores, archetype, tier } = result;
|
|
134
|
+
const { overall, scores, archetype, tier, tierBadge, tierPercentile } = result;
|
|
134
135
|
const tc = tierColor(tier);
|
|
135
136
|
const sc = scoreColor(overall);
|
|
136
137
|
|
|
@@ -140,7 +141,9 @@ export function displayScore(result, prose) {
|
|
|
140
141
|
console.log(dim(' YOUR CHEKK SCORE'));
|
|
141
142
|
console.log();
|
|
142
143
|
console.log(` ${sc.bold(String(overall))}`);
|
|
143
|
-
|
|
144
|
+
const badgeStr = tierBadge ? ` ${tierBadge}` : '';
|
|
145
|
+
const pctStr = tierPercentile ? ` ${dim(tierPercentile)}` : '';
|
|
146
|
+
console.log(` ${tc('\u2500\u2500 ' + tier + ' \u2500\u2500')}${badgeStr}${pctStr}`);
|
|
144
147
|
console.log();
|
|
145
148
|
console.log(` ${dim('Archetype:')} ${bold.white(archetype.name)}`);
|
|
146
149
|
|
|
@@ -272,7 +275,7 @@ export function displayEasterEggs(result, metrics) {
|
|
|
272
275
|
// ══════════════════════════════════════════════
|
|
273
276
|
|
|
274
277
|
export function displayEnding(result) {
|
|
275
|
-
const { overall, archetype, tier } = result;
|
|
278
|
+
const { overall, archetype, tier, tierBadge } = result;
|
|
276
279
|
const tc = tierColor(tier);
|
|
277
280
|
|
|
278
281
|
console.log(doubleRule());
|
|
@@ -282,7 +285,8 @@ export function displayEnding(result) {
|
|
|
282
285
|
console.log();
|
|
283
286
|
|
|
284
287
|
// Copy-paste share line
|
|
285
|
-
const
|
|
288
|
+
const badge = tierBadge ? ` ${tierBadge}` : '';
|
|
289
|
+
const shareLine = `${overall} \u2014 ${tier}${badge} \u2014 ${archetype.name}`;
|
|
286
290
|
console.log(` ${dim('"')}${tc(shareLine)}${dim('"')}`);
|
|
287
291
|
console.log(` ${dim('\u2191 Copy this to share')}`);
|
|
288
292
|
console.log();
|
|
@@ -362,8 +366,7 @@ export function displayOffline(result, metrics) {
|
|
|
362
366
|
displayScore(result, null);
|
|
363
367
|
displayDataNarratives(metrics);
|
|
364
368
|
displayEasterEggs(result, metrics);
|
|
365
|
-
console.log(dim(' Run without --offline for personalized AI-generated insights'));
|
|
366
|
-
console.log(dim(' Run with --verbose for full per-project and metric breakdown\n'));
|
|
369
|
+
console.log(dim(' Run without --offline for personalized AI-generated insights\n'));
|
|
367
370
|
displayEnding(result);
|
|
368
371
|
}
|
|
369
372
|
|
|
@@ -375,7 +378,6 @@ export function displayFull(result, metrics, prose) {
|
|
|
375
378
|
displayScore(result, prose);
|
|
376
379
|
displayNarratives(metrics, prose);
|
|
377
380
|
displayEasterEggs(result, metrics);
|
|
378
|
-
console.log(dim(' Run with --verbose for full per-project and metric breakdown\n'));
|
|
379
381
|
displayEnding(result);
|
|
380
382
|
}
|
|
381
383
|
|
package/src/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
displayOffline,
|
|
16
16
|
displayVerbose,
|
|
17
17
|
} from './display.js';
|
|
18
|
-
import { generateProse, askClaim, uploadAndClaim } from './upload.js';
|
|
18
|
+
import { generateProse, askVerbose, askClaim, uploadAndClaim } from './upload.js';
|
|
19
19
|
|
|
20
20
|
export async function run(options = {}) {
|
|
21
21
|
// ── Header ──
|
|
@@ -93,35 +93,52 @@ export async function run(options = {}) {
|
|
|
93
93
|
tools: supported.map(t => t.tool),
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
// Progress bar animation
|
|
97
|
-
await displayProgressBar(1500);
|
|
98
|
-
|
|
99
96
|
// ── JSON output ──
|
|
100
97
|
if (options.json) {
|
|
101
98
|
console.log(JSON.stringify({ metrics, result, sessionStats }, null, 2));
|
|
102
99
|
return;
|
|
103
100
|
}
|
|
104
101
|
|
|
105
|
-
// ── Step 4:
|
|
102
|
+
// ── Step 4: Progress bar + API call in parallel ──
|
|
103
|
+
// Run prose generation alongside the progress bar so score appears
|
|
104
|
+
// immediately when the bar hits 100% — no lag.
|
|
105
|
+
let prose = null;
|
|
106
|
+
if (!options.offline) {
|
|
107
|
+
const [, proseResult] = await Promise.all([
|
|
108
|
+
displayProgressBar(1500),
|
|
109
|
+
generateProse(metrics, result, sessionStats).catch(() => null),
|
|
110
|
+
]);
|
|
111
|
+
prose = proseResult;
|
|
112
|
+
} else {
|
|
113
|
+
await displayProgressBar(1500);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Step 5: Display results ──
|
|
106
117
|
if (options.offline) {
|
|
107
118
|
displayOffline(result, metrics);
|
|
108
119
|
} else {
|
|
109
|
-
// Generate prose from API
|
|
110
|
-
let prose = null;
|
|
111
|
-
try {
|
|
112
|
-
prose = await generateProse(metrics, result, sessionStats);
|
|
113
|
-
} catch {
|
|
114
|
-
// Silently fall back to data-driven display
|
|
115
|
-
}
|
|
116
120
|
displayFull(result, metrics, prose);
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
// ── Verbose
|
|
123
|
+
// ── Step 6: Verbose prompt (interactive) ──
|
|
124
|
+
// If --verbose flag was passed, show immediately. Otherwise prompt.
|
|
120
125
|
if (options.verbose) {
|
|
121
126
|
displayVerbose(metrics, allSessions);
|
|
127
|
+
} else {
|
|
128
|
+
try {
|
|
129
|
+
const wantsVerbose = await askVerbose();
|
|
130
|
+
if (wantsVerbose) {
|
|
131
|
+
console.log();
|
|
132
|
+
displayVerbose(metrics, allSessions);
|
|
133
|
+
} else {
|
|
134
|
+
console.log();
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
console.log();
|
|
138
|
+
}
|
|
122
139
|
}
|
|
123
140
|
|
|
124
|
-
// ── Step
|
|
141
|
+
// ── Step 7: Claim prompt ──
|
|
125
142
|
if (options.upload !== false) {
|
|
126
143
|
try {
|
|
127
144
|
const wantsClaim = await askClaim();
|
package/src/scorer.js
CHANGED
|
@@ -1,79 +1,162 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Combine individual metric scores into composite scores and assign archetype.
|
|
3
|
+
*
|
|
4
|
+
* Archetypes and tiers are consistent with the GitHub analysis system.
|
|
5
|
+
* 15 archetypes across 5 tiers, mapped from repo-based signals to
|
|
6
|
+
* prompt-behavior signals measured by the CLI.
|
|
3
7
|
*/
|
|
4
8
|
|
|
9
|
+
// ── Tier thresholds (match GitHub analysis) ──
|
|
10
|
+
|
|
11
|
+
const TIERS = [
|
|
12
|
+
{ name: 'LEGENDARY', min: 85, badge: '🌟🌟🌟', percentile: 'Top 1%' },
|
|
13
|
+
{ name: 'ULTRA RARE', min: 73, badge: '🌟🌟', percentile: 'Top 5%' },
|
|
14
|
+
{ name: 'RARE', min: 60, badge: '⭐', percentile: 'Top 15%' },
|
|
15
|
+
{ name: 'UNCOMMON', min: 47, badge: '◆', percentile: 'Top 30%' },
|
|
16
|
+
{ name: 'COMMON', min: 0, badge: '●', percentile: 'Top 50%' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// ── Archetype definitions ──
|
|
20
|
+
// Each archetype has a match function based on the 4 CLI dimension scores:
|
|
21
|
+
// decomposition (thinking), debugCycles (debugging),
|
|
22
|
+
// aiLeverage (AI leverage), sessionStructure (workflow)
|
|
23
|
+
//
|
|
24
|
+
// Tier-restricted: archetypes only match within their allowed tier range.
|
|
25
|
+
|
|
5
26
|
const ARCHETYPES = [
|
|
27
|
+
// ── LEGENDARY (85+) ──
|
|
6
28
|
{
|
|
7
|
-
id: '
|
|
8
|
-
name: 'THE
|
|
9
|
-
|
|
10
|
-
match: (s) =>
|
|
29
|
+
id: 'THE_10X_ENGINEER',
|
|
30
|
+
name: 'THE 10X ENGINEER',
|
|
31
|
+
tierRange: ['LEGENDARY'],
|
|
32
|
+
match: (s) => true, // If you hit LEGENDARY, you're a 10x
|
|
11
33
|
priority: 1,
|
|
12
34
|
},
|
|
35
|
+
|
|
36
|
+
// ── ULTRA RARE (73-84) ──
|
|
13
37
|
{
|
|
14
|
-
id: '
|
|
15
|
-
name: 'THE
|
|
16
|
-
|
|
17
|
-
|
|
38
|
+
id: 'THE_ARCHITECT',
|
|
39
|
+
name: 'THE ARCHITECT',
|
|
40
|
+
tierRange: ['ULTRA RARE'],
|
|
41
|
+
// High decomposition + structured workflow + strong leverage
|
|
42
|
+
match: (s) => s.decomposition >= 70 && s.sessionStructure >= 65,
|
|
18
43
|
priority: 2,
|
|
19
44
|
},
|
|
45
|
+
{
|
|
46
|
+
id: 'THE_PROFESSOR',
|
|
47
|
+
name: 'THE PROFESSOR',
|
|
48
|
+
tierRange: ['ULTRA RARE'],
|
|
49
|
+
// Exceptional at explaining: high decomposition, detailed prompts, strong context-setting
|
|
50
|
+
match: (s) => s.decomposition >= 65 && s.sessionStructure >= 70,
|
|
51
|
+
priority: 3,
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// ── RARE (60-72) ──
|
|
55
|
+
{
|
|
56
|
+
id: 'THE_SPECIALIST',
|
|
57
|
+
name: 'THE SPECIALIST',
|
|
58
|
+
tierRange: ['RARE'],
|
|
59
|
+
// Deep focused work: high debug efficiency + high leverage in specific domain
|
|
60
|
+
match: (s) => s.debugCycles >= 70 && s.aiLeverage >= 65,
|
|
61
|
+
priority: 4,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'THE_SYSTEMS_THINKER',
|
|
65
|
+
name: 'THE SYSTEMS THINKER',
|
|
66
|
+
tierRange: ['RARE'],
|
|
67
|
+
// Strong decomposition + structured approach to complex problems
|
|
68
|
+
match: (s) => s.decomposition >= 65 && s.sessionStructure >= 60,
|
|
69
|
+
priority: 5,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'THE_MAINTAINER',
|
|
73
|
+
name: 'THE MAINTAINER',
|
|
74
|
+
tierRange: ['RARE'],
|
|
75
|
+
// Reliable workflow + solid debugging: keeps things running
|
|
76
|
+
match: (s) => s.sessionStructure >= 60 && s.debugCycles >= 60,
|
|
77
|
+
priority: 6,
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// ── UNCOMMON (47-59) ──
|
|
81
|
+
{
|
|
82
|
+
id: 'THE_CRAFTSPERSON',
|
|
83
|
+
name: 'THE CRAFTSPERSON',
|
|
84
|
+
tierRange: ['UNCOMMON'],
|
|
85
|
+
// Quality obsession: high debug precision + structured sessions
|
|
86
|
+
match: (s) => s.debugCycles >= 60 && s.sessionStructure >= 55,
|
|
87
|
+
priority: 7,
|
|
88
|
+
},
|
|
20
89
|
{
|
|
21
90
|
id: 'THE_BUILDER',
|
|
22
91
|
name: 'THE BUILDER',
|
|
23
|
-
|
|
92
|
+
tierRange: ['UNCOMMON'],
|
|
93
|
+
// Well-rounded: decent across all dimensions
|
|
24
94
|
match: (s) => {
|
|
25
|
-
const
|
|
26
|
-
return
|
|
95
|
+
const min = Math.min(s.decomposition, s.debugCycles, s.aiLeverage, s.sessionStructure);
|
|
96
|
+
return min >= 45;
|
|
27
97
|
},
|
|
28
|
-
priority:
|
|
98
|
+
priority: 8,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'THE_CONTRIBUTOR',
|
|
102
|
+
name: 'THE CONTRIBUTOR',
|
|
103
|
+
tierRange: ['UNCOMMON'],
|
|
104
|
+
// Strong AI leverage + decent decomposition: collaborates well with AI
|
|
105
|
+
match: (s) => s.aiLeverage >= 55 && s.decomposition >= 50,
|
|
106
|
+
priority: 9,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'THE_HIDDEN_GEM',
|
|
110
|
+
name: 'THE HIDDEN GEM',
|
|
111
|
+
tierRange: ['UNCOMMON'],
|
|
112
|
+
// Strong skills but unstructured: high debug or leverage but low structure
|
|
113
|
+
match: (s) => (s.debugCycles >= 55 || s.aiLeverage >= 55) && s.sessionStructure < 50,
|
|
114
|
+
priority: 10,
|
|
29
115
|
},
|
|
30
116
|
{
|
|
31
117
|
id: 'THE_EXPLORER',
|
|
32
118
|
name: 'THE EXPLORER',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
119
|
+
tierRange: ['UNCOMMON'],
|
|
120
|
+
// High leverage + curiosity-driven: research-heavy prompting
|
|
121
|
+
match: (s) => s.aiLeverage >= 50,
|
|
122
|
+
priority: 11,
|
|
36
123
|
},
|
|
124
|
+
|
|
125
|
+
// ── COMMON (0-46) ──
|
|
37
126
|
{
|
|
38
|
-
id: '
|
|
39
|
-
name: 'THE
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
127
|
+
id: 'THE_TINKERER',
|
|
128
|
+
name: 'THE TINKERER',
|
|
129
|
+
tierRange: ['COMMON'],
|
|
130
|
+
// Practical problem-solving: decent debug + some leverage
|
|
131
|
+
match: (s) => s.debugCycles >= 40 && s.aiLeverage >= 35,
|
|
132
|
+
priority: 12,
|
|
43
133
|
},
|
|
44
134
|
{
|
|
45
|
-
id: '
|
|
46
|
-
name: 'THE
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
priority: 6,
|
|
135
|
+
id: 'THE_GRINDER',
|
|
136
|
+
name: 'THE GRINDER',
|
|
137
|
+
tierRange: ['COMMON'],
|
|
138
|
+
// High volume, iterative: lots of exchanges, keeps going
|
|
139
|
+
match: (s) => s.decomposition >= 35,
|
|
140
|
+
priority: 13,
|
|
53
141
|
},
|
|
54
142
|
{
|
|
55
|
-
id: '
|
|
56
|
-
name: 'THE
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
143
|
+
id: 'THE_HOBBYIST',
|
|
144
|
+
name: 'THE HOBBYIST',
|
|
145
|
+
tierRange: ['COMMON'],
|
|
146
|
+
// Active but early: some leverage or debugging skill
|
|
147
|
+
match: (s) => s.aiLeverage >= 30 || s.debugCycles >= 30,
|
|
148
|
+
priority: 14,
|
|
60
149
|
},
|
|
61
150
|
{
|
|
62
|
-
id: '
|
|
63
|
-
name: 'THE
|
|
64
|
-
|
|
151
|
+
id: 'THE_APPRENTICE',
|
|
152
|
+
name: 'THE APPRENTICE',
|
|
153
|
+
tierRange: ['COMMON'],
|
|
154
|
+
// Default fallback for COMMON tier
|
|
65
155
|
match: () => true,
|
|
66
156
|
priority: 99,
|
|
67
157
|
},
|
|
68
158
|
];
|
|
69
159
|
|
|
70
|
-
const TIERS = [
|
|
71
|
-
{ name: 'LEGENDARY', min: 90 },
|
|
72
|
-
{ name: 'RARE', min: 75 },
|
|
73
|
-
{ name: 'UNCOMMON', min: 55 },
|
|
74
|
-
{ name: 'COMMON', min: 0 },
|
|
75
|
-
];
|
|
76
|
-
|
|
77
160
|
export function computeOverallScore(metrics) {
|
|
78
161
|
const scores = {
|
|
79
162
|
decomposition: metrics.decomposition.score,
|
|
@@ -90,13 +173,18 @@ export function computeOverallScore(metrics) {
|
|
|
90
173
|
scores.sessionStructure * 0.2
|
|
91
174
|
);
|
|
92
175
|
|
|
93
|
-
// Assign
|
|
94
|
-
const sortedArchetypes = [...ARCHETYPES].sort((a, b) => a.priority - b.priority);
|
|
95
|
-
const archetype = sortedArchetypes.find(a => a.match(scores)) || ARCHETYPES[ARCHETYPES.length - 1];
|
|
96
|
-
|
|
97
|
-
// Assign tier
|
|
176
|
+
// Assign tier first
|
|
98
177
|
const tier = TIERS.find(t => overall >= t.min) || TIERS[TIERS.length - 1];
|
|
99
178
|
|
|
179
|
+
// Assign archetype within tier
|
|
180
|
+
const tierArchetypes = ARCHETYPES
|
|
181
|
+
.filter(a => a.tierRange.includes(tier.name))
|
|
182
|
+
.sort((a, b) => a.priority - b.priority);
|
|
183
|
+
|
|
184
|
+
const archetype = tierArchetypes.find(a => a.match(scores))
|
|
185
|
+
|| tierArchetypes[tierArchetypes.length - 1]
|
|
186
|
+
|| { id: 'THE_APPRENTICE', name: 'THE APPRENTICE' };
|
|
187
|
+
|
|
100
188
|
return {
|
|
101
189
|
overall,
|
|
102
190
|
scores,
|
|
@@ -105,5 +193,7 @@ export function computeOverallScore(metrics) {
|
|
|
105
193
|
name: archetype.name,
|
|
106
194
|
},
|
|
107
195
|
tier: tier.name,
|
|
196
|
+
tierBadge: tier.badge,
|
|
197
|
+
tierPercentile: tier.percentile,
|
|
108
198
|
};
|
|
109
199
|
}
|
package/src/upload.js
CHANGED
|
@@ -47,6 +47,23 @@ export async function generateProse(metrics, result, sessionStats) {
|
|
|
47
47
|
return response.json();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Ask user if they want to see detailed breakdown.
|
|
52
|
+
*/
|
|
53
|
+
export function askVerbose() {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const rl = createInterface({
|
|
56
|
+
input: process.stdin,
|
|
57
|
+
output: process.stdout,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
rl.question(' See detailed breakdown? (y/n) ', (answer) => {
|
|
61
|
+
rl.close();
|
|
62
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
/**
|
|
51
68
|
* Ask user if they want to claim their profile.
|
|
52
69
|
*/
|