commitshow 0.3.17 → 0.3.19
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/lib/render.js +104 -74
- package/package.json +1 -1
package/dist/lib/render.js
CHANGED
|
@@ -239,20 +239,43 @@ export function renderAudit(view) {
|
|
|
239
239
|
// ── 3 strengths + 2 concerns box · errors-first reorder (2026-04-30) ──
|
|
240
240
|
// CONCERNS render before STRENGTHS · the value prop is "what your AI
|
|
241
241
|
// missed", so they lead. Score follows as the receipt below.
|
|
242
|
+
//
|
|
243
|
+
// 3-tier severity (2026-05-04): the engine ranks weaknesses[] by impact,
|
|
244
|
+
// so we treat concerns[0] as CRITICAL (✕ scarlet) and the remainder as
|
|
245
|
+
// WARNING (⚠ gold). Strengths stay as ↑ teal. A one-line counter above
|
|
246
|
+
// the box gives a scannable tone summary without needing finding IDs.
|
|
242
247
|
const strengths = asStringArray(snapshot?.rich_analysis?.scout_brief?.strengths, 3);
|
|
243
248
|
const concerns = asStringArray(snapshot?.rich_analysis?.scout_brief?.weaknesses, 2);
|
|
244
249
|
if (strengths.length > 0 || concerns.length > 0) {
|
|
245
250
|
const bulletWidth = CONTENT_W - 2;
|
|
251
|
+
// Findings counter line · sits ABOVE the box so a quick scan picks up
|
|
252
|
+
// the tone (any criticals?) before reading the bullets.
|
|
253
|
+
const critCount = Math.min(concerns.length, 1);
|
|
254
|
+
const warnCount = Math.max(0, concerns.length - 1);
|
|
255
|
+
const counterParts = [];
|
|
256
|
+
if (critCount > 0)
|
|
257
|
+
counterParts.push(c.scarlet(`${critCount} critical`));
|
|
258
|
+
if (warnCount > 0)
|
|
259
|
+
counterParts.push(c.gold(`${warnCount} warning`));
|
|
260
|
+
if (strengths.length > 0)
|
|
261
|
+
counterParts.push(c.teal(`${strengths.length} strength${strengths.length === 1 ? '' : 's'}`));
|
|
262
|
+
if (counterParts.length > 0) {
|
|
263
|
+
lines.push(' ' + c.muted('Findings · ') + counterParts.join(c.muted(' · ')));
|
|
264
|
+
lines.push('');
|
|
265
|
+
}
|
|
246
266
|
lines.push(' ' + boxTop());
|
|
247
267
|
// Heading row inside the box · "What this build missed" lead.
|
|
248
268
|
if (concerns.length > 0) {
|
|
249
269
|
const heading = 'What this build missed';
|
|
250
270
|
lines.push(' ' + boxRow(heading.length, c.bold(c.scarlet(heading))));
|
|
251
271
|
}
|
|
252
|
-
|
|
272
|
+
concerns.forEach((s, i) => {
|
|
253
273
|
const txt = truncate(s, bulletWidth);
|
|
254
|
-
|
|
255
|
-
|
|
274
|
+
const isCrit = i === 0;
|
|
275
|
+
const icon = isCrit ? '✕ ' : '⚠ ';
|
|
276
|
+
const tone = isCrit ? c.scarlet : c.gold;
|
|
277
|
+
lines.push(' ' + boxRow(2 + txt.length, tone(icon) + c.cream(txt)));
|
|
278
|
+
});
|
|
256
279
|
if (strengths.length > 0) {
|
|
257
280
|
if (concerns.length > 0)
|
|
258
281
|
lines.push(' ' + boxBlank());
|
|
@@ -266,77 +289,10 @@ export function renderAudit(view) {
|
|
|
266
289
|
lines.push(' ' + boxBottom());
|
|
267
290
|
lines.push('');
|
|
268
291
|
}
|
|
269
|
-
// Hero score · trophy plate
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
// rows are stacked. Outer 58-col layout still centers the box.
|
|
274
|
-
const band = total >= 75 ? 'strong' : total >= 50 ? 'mid' : 'weak';
|
|
275
|
-
const bandTone = scoreTone(total);
|
|
276
|
-
const bigRows = bigText(String(total));
|
|
277
|
-
const bigWidth = bigRows[0].length;
|
|
278
|
-
// Inner content width = the longer of (digit width, caption width) + a
|
|
279
|
-
// small breathing margin on each side. Box outer = inner + 2 (frame).
|
|
280
|
-
const captionVisible = isWalkOn
|
|
281
|
-
? `/ 100 · walk-on · ${band}`
|
|
282
|
-
: `/ 100 · ${band}`;
|
|
283
|
-
const SCORE_PAD = 4; // 2 cells of breathing room on each side of widest line
|
|
284
|
-
const scoreInsideW = Math.max(bigWidth, captionVisible.length) + SCORE_PAD;
|
|
285
|
-
const scoreOuterW = scoreInsideW + 2;
|
|
286
|
-
// Center the trophy box inside the 58-col layout
|
|
287
|
-
const trophyLeftPad = Math.max(0, Math.floor((58 - scoreOuterW) / 2));
|
|
288
|
-
const trophyIndent = ' ' + ' '.repeat(trophyLeftPad);
|
|
289
|
-
const trophyTop = c.muted('╔' + '═'.repeat(scoreInsideW) + '╗');
|
|
290
|
-
const trophyBottom = c.muted('╚' + '═'.repeat(scoreInsideW) + '╝');
|
|
291
|
-
const trophyBlank = c.muted('║' + ' '.repeat(scoreInsideW) + '║');
|
|
292
|
-
// Center a colored line inside the box. visibleLen is the *visible*
|
|
293
|
-
// (ANSI-stripped) cell count of the colored content.
|
|
294
|
-
const trophyRow = (visibleLen, colored) => {
|
|
295
|
-
const pad = Math.max(0, scoreInsideW - visibleLen);
|
|
296
|
-
const lp = Math.floor(pad / 2);
|
|
297
|
-
const rp = pad - lp;
|
|
298
|
-
return c.muted('║') + ' '.repeat(lp) + colored + ' '.repeat(rp) + c.muted('║');
|
|
299
|
-
};
|
|
300
|
-
lines.push(trophyIndent + trophyTop);
|
|
301
|
-
lines.push(trophyIndent + trophyBlank);
|
|
302
|
-
for (const row of bigRows) {
|
|
303
|
-
lines.push(trophyIndent + trophyRow(bigWidth, c.pixelInk(row)));
|
|
304
|
-
}
|
|
305
|
-
lines.push(trophyIndent + trophyBlank);
|
|
306
|
-
// Caption row · band-tinted so the signal lives on the band word.
|
|
307
|
-
const captionColored = isWalkOn
|
|
308
|
-
? c.muted('/ 100 · ') + c.gold('walk-on') + c.muted(' · ') + bandTone(band)
|
|
309
|
-
: c.muted('/ 100 · ') + bandTone(band);
|
|
310
|
-
lines.push(trophyIndent + trophyRow(captionVisible.length, captionColored));
|
|
311
|
-
lines.push(trophyIndent + trophyBottom);
|
|
312
|
-
// Walk-on sub-caption stays OUTSIDE the trophy — it's an explanation
|
|
313
|
-
// of the cap, not part of the headline. Keeps the box clean and tight
|
|
314
|
-
// for screenshot crops.
|
|
315
|
-
if (isWalkOn) {
|
|
316
|
-
const subVisible = 'audition unlocks final 5 · max walk-on score 95';
|
|
317
|
-
const subPad = Math.floor((58 - subVisible.length) / 2);
|
|
318
|
-
lines.push(' ' + ' '.repeat(subPad) + c.muted(subVisible));
|
|
319
|
-
}
|
|
320
|
-
lines.push('');
|
|
321
|
-
// Axis bars · league shows all three; walk-on shows Audit only and
|
|
322
|
-
// surfaces Scout + Community as locked-with-unlock-hint rows.
|
|
323
|
-
// Walk-on Audit denominator is 45 (Brief slot excluded) so the math is
|
|
324
|
-
// visibly consistent with the big-digit normalization above.
|
|
325
|
-
const lockedBar = '─ audition unlocks ─'; // exactly 20 cells · matches scoreBar width
|
|
326
|
-
const auditDen = isWalkOn ? WALK_ON_AUDIT_MAX : 50;
|
|
327
|
-
const auditScoreClamp = Math.min(p.score_auto ?? 0, auditDen);
|
|
328
|
-
const auditLine = ` Audit ${pad(`${auditScoreClamp}/${auditDen}`, 7)} ${scoreBar(auditScoreClamp, auditDen)}`;
|
|
329
|
-
lines.push(' ' + auditLine);
|
|
330
|
-
if (isWalkOn) {
|
|
331
|
-
lines.push(' ' + ` Scout ${pad('—/30', 7)} ` + c.muted(lockedBar));
|
|
332
|
-
lines.push(' ' + ` Comm. ${pad('—/20', 7)} ` + c.muted(lockedBar));
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
lines.push(' ' + ` Scout ${pad(`${p.score_forecast}/30`, 7)} ${scoreBar(p.score_forecast, 30)}`);
|
|
336
|
-
lines.push(' ' + ` Comm. ${pad(`${p.score_community}/20`, 7)} ${scoreBar(p.score_community, 20)}`);
|
|
337
|
-
}
|
|
338
|
-
lines.push('');
|
|
339
|
-
// (concerns/strengths block moved above the score · errors-first 2026-04-30)
|
|
292
|
+
// (Hero score · trophy plate + axis bars moved BELOW all info sections
|
|
293
|
+
// on 2026-05-04 · "info top, capture-worthy hero at the bottom" so a
|
|
294
|
+
// crop of the last block is a self-contained share asset. See the
|
|
295
|
+
// HERO BLOCK construction near the end of this function.)
|
|
340
296
|
// ─── AI Coder 7 Frames · signature framework ───
|
|
341
297
|
// Render only the categories that produced an actionable status (fail /
|
|
342
298
|
// warn / pass when meaningful). N/A categories are dropped to keep the
|
|
@@ -396,6 +352,80 @@ export function renderAudit(view) {
|
|
|
396
352
|
const url = `https://commit.show/projects/${p.id}`;
|
|
397
353
|
lines.push(' ' + c.muted('→ ') + c.cream(url));
|
|
398
354
|
lines.push('');
|
|
355
|
+
// Agent-loop hint · nudges users into the `--json` workflow that makes
|
|
356
|
+
// this CLI useful inside Claude Code · Cursor · etc. Kept short so it
|
|
357
|
+
// fits inside the 58-cell layout (longer "github.com/foo/bar --json |
|
|
358
|
+
// jq .concerns" forms blew past the wordmark right edge). The repo
|
|
359
|
+
// form drops to `.` because anyone running an agent loop is in cwd;
|
|
360
|
+
// the URL case is already covered by the walk-on upsell box below.
|
|
361
|
+
if (concerns.length > 0) {
|
|
362
|
+
const cmd = 'commitshow audit . --json';
|
|
363
|
+
lines.push(' ' + c.muted('next · feed your AI loop → ') + c.cream(cmd));
|
|
364
|
+
lines.push('');
|
|
365
|
+
}
|
|
366
|
+
// ─── HERO BLOCK · trophy + axis bars · 2026-05-04 ───
|
|
367
|
+
// All info sits ABOVE this block so a screenshot cropped to the bottom
|
|
368
|
+
// (trophy + axis bars + wordmark) is a self-contained share asset
|
|
369
|
+
// showing project identity, score, and brand mark in one frame.
|
|
370
|
+
const band = total >= 75 ? 'strong' : total >= 50 ? 'mid' : 'weak';
|
|
371
|
+
const bandTone = scoreTone(total);
|
|
372
|
+
const bigRows = bigText(String(total));
|
|
373
|
+
const bigWidth = bigRows[0].length;
|
|
374
|
+
// Trophy: name strip + big digits + caption inside one ╔═╗ frame so a
|
|
375
|
+
// crop of just the trophy tells the whole story (project · score · band).
|
|
376
|
+
const slugVisible = (p.github_url?.replace(/^https?:\/\/github\.com\//, '') ?? '').slice(0, 40);
|
|
377
|
+
const captionVisible = isWalkOn
|
|
378
|
+
? `/ 100 · walk-on · ${band}`
|
|
379
|
+
: `/ 100 · ${band}`;
|
|
380
|
+
const SCORE_PAD = 4; // 2 cells of breathing room on each side of widest line
|
|
381
|
+
const scoreInsideW = Math.max(bigWidth, captionVisible.length, slugVisible.length) + SCORE_PAD;
|
|
382
|
+
const scoreOuterW = scoreInsideW + 2;
|
|
383
|
+
const trophyLeftPad = Math.max(0, Math.floor((58 - scoreOuterW) / 2));
|
|
384
|
+
const trophyIndent = ' ' + ' '.repeat(trophyLeftPad);
|
|
385
|
+
const trophyTop = c.muted('╔' + '═'.repeat(scoreInsideW) + '╗');
|
|
386
|
+
const trophyBottom = c.muted('╚' + '═'.repeat(scoreInsideW) + '╝');
|
|
387
|
+
const trophyBlank = c.muted('║' + ' '.repeat(scoreInsideW) + '║');
|
|
388
|
+
const trophyRow = (visibleLen, colored) => {
|
|
389
|
+
const pad = Math.max(0, scoreInsideW - visibleLen);
|
|
390
|
+
const lp = Math.floor(pad / 2);
|
|
391
|
+
const rp = pad - lp;
|
|
392
|
+
return c.muted('║') + ' '.repeat(lp) + colored + ' '.repeat(rp) + c.muted('║');
|
|
393
|
+
};
|
|
394
|
+
lines.push(trophyIndent + trophyTop);
|
|
395
|
+
if (slugVisible) {
|
|
396
|
+
lines.push(trophyIndent + trophyRow(slugVisible.length, c.muted(slugVisible)));
|
|
397
|
+
}
|
|
398
|
+
lines.push(trophyIndent + trophyBlank);
|
|
399
|
+
for (const row of bigRows) {
|
|
400
|
+
lines.push(trophyIndent + trophyRow(bigWidth, c.pixelInk(row)));
|
|
401
|
+
}
|
|
402
|
+
lines.push(trophyIndent + trophyBlank);
|
|
403
|
+
const captionColored = isWalkOn
|
|
404
|
+
? c.muted('/ 100 · ') + c.gold('walk-on') + c.muted(' · ') + bandTone(band)
|
|
405
|
+
: c.muted('/ 100 · ') + bandTone(band);
|
|
406
|
+
lines.push(trophyIndent + trophyRow(captionVisible.length, captionColored));
|
|
407
|
+
lines.push(trophyIndent + trophyBottom);
|
|
408
|
+
if (isWalkOn) {
|
|
409
|
+
const subVisible = 'audition unlocks final 5 · max walk-on score 95';
|
|
410
|
+
const subPad = Math.floor((58 - subVisible.length) / 2);
|
|
411
|
+
lines.push(' ' + ' '.repeat(subPad) + c.muted(subVisible));
|
|
412
|
+
}
|
|
413
|
+
lines.push('');
|
|
414
|
+
// Axis bars sit directly under the trophy as the per-pillar breakdown
|
|
415
|
+
// — same visual frame for share screenshots.
|
|
416
|
+
const lockedBar = '─ audition unlocks ─';
|
|
417
|
+
const auditDen = isWalkOn ? WALK_ON_AUDIT_MAX : 50;
|
|
418
|
+
const auditScoreClamp = Math.min(p.score_auto ?? 0, auditDen);
|
|
419
|
+
lines.push(' ' + ` Audit ${pad(`${auditScoreClamp}/${auditDen}`, 7)} ${scoreBar(auditScoreClamp, auditDen)}`);
|
|
420
|
+
if (isWalkOn) {
|
|
421
|
+
lines.push(' ' + ` Scout ${pad('—/30', 7)} ` + c.muted(lockedBar));
|
|
422
|
+
lines.push(' ' + ` Comm. ${pad('—/20', 7)} ` + c.muted(lockedBar));
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
lines.push(' ' + ` Scout ${pad(`${p.score_forecast}/30`, 7)} ${scoreBar(p.score_forecast, 30)}`);
|
|
426
|
+
lines.push(' ' + ` Comm. ${pad(`${p.score_community}/20`, 7)} ${scoreBar(p.score_community, 20)}`);
|
|
427
|
+
}
|
|
428
|
+
lines.push('');
|
|
399
429
|
const wordmark = 'commit.show';
|
|
400
430
|
const footerPad = Math.max(0, BOX_W - wordmark.length);
|
|
401
431
|
lines.push(' '.repeat(footerPad) + c.gold(wordmark));
|