commitshow 0.1.2 → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  import { resolveTarget, TargetError } from '../lib/target.js';
2
2
  import { findProjectByGithubUrl, fetchLatestSnapshot, fetchStanding, runPreviewAudit, waitForPreviewSnapshot, } from '../lib/api.js';
3
- import { renderAudit, renderMarkdown, renderJson, renderUpsell, renderQuotaFooter, renderRateLimitDeny, writeAuditMarkdown, writeAuditJson, } from '../lib/render.js';
3
+ import { renderAudit, renderMarkdown, renderJson, renderUpsell, renderQuotaFooter, renderRateLimitDeny, renderAuditError, writeAuditMarkdown, writeAuditJson, } from '../lib/render.js';
4
4
  import { c } from '../lib/colors.js';
5
5
  export async function audit(args) {
6
6
  const asJson = args.includes('--json');
@@ -27,6 +27,30 @@ export async function audit(args) {
27
27
  fetchStanding(project.id),
28
28
  ]);
29
29
  const view = { project, snapshot, standing };
30
+ // The snapshot may carry an audit-engine error (Claude quota exceeded,
31
+ // rate limit, etc.). Render the friendly explanation panel and exit
32
+ // 2 so CI scripts can detect "engine unavailable" without conflating
33
+ // it with a genuine low score.
34
+ const auditErr = snapshot?.rich_analysis?.error;
35
+ if (auditErr) {
36
+ if (asJson) {
37
+ process.stdout.write(JSON.stringify({
38
+ error: 'audit_engine_error',
39
+ reason: auditErr.type,
40
+ message: auditErr.message ?? null,
41
+ retry_after: auditErr.retry_after_seconds ?? null,
42
+ project: { id: project.id, name: project.project_name, github_url: project.github_url },
43
+ }) + '\n');
44
+ }
45
+ else {
46
+ console.error('');
47
+ console.error(renderAuditError({ type: auditErr.type, message: auditErr.message ?? undefined,
48
+ retry_after_seconds: auditErr.retry_after_seconds ?? null,
49
+ http_status: auditErr.http_status }, project.project_name, `https://commit.show/projects/${project.id}`));
50
+ console.error('');
51
+ }
52
+ return 2;
53
+ }
30
54
  if (asJson) {
31
55
  process.stdout.write(renderJson(view) + '\n');
32
56
  }
@@ -106,6 +130,30 @@ export async function audit(args) {
106
130
  envelope = result;
107
131
  }
108
132
  const view = { project: envelope.project, snapshot: envelope.snapshot, standing: null };
133
+ // Same audit-engine error check as the cached path. The polled snapshot
134
+ // can carry a Claude failure even though the audit-preview Edge Function
135
+ // returned 202 (the failure happened in the background analyze-project).
136
+ const polledErr = envelope.snapshot?.rich_analysis?.error;
137
+ if (polledErr) {
138
+ if (asJson) {
139
+ process.stdout.write(JSON.stringify({
140
+ error: 'audit_engine_error',
141
+ reason: polledErr.type,
142
+ message: polledErr.message ?? null,
143
+ retry_after: polledErr.retry_after_seconds ?? null,
144
+ project: { id: envelope.project.id, name: envelope.project.project_name, github_url: envelope.project.github_url },
145
+ quota: envelope.quota,
146
+ }) + '\n');
147
+ }
148
+ else {
149
+ console.error('');
150
+ console.error(renderAuditError({ type: polledErr.type, message: polledErr.message ?? undefined,
151
+ retry_after_seconds: polledErr.retry_after_seconds ?? null,
152
+ http_status: polledErr.http_status }, envelope.project.project_name, `https://commit.show/projects/${envelope.project.id}`));
153
+ console.error('');
154
+ }
155
+ return 2;
156
+ }
109
157
  if (asJson) {
110
158
  // Inject quota into the v1 schema as an additive field — schema_version
111
159
  // unchanged because additive-only fields don't bump it.
@@ -301,6 +301,69 @@ export function toAgentShape(view) {
301
301
  export function renderJson(view) {
302
302
  return JSON.stringify(toAgentShape(view), null, 2);
303
303
  }
304
+ // ── Audit-engine error panel ────────────────────────────────────────
305
+ // Rendered when the snapshot exists but rich_analysis.error is set —
306
+ // i.e., the Claude call itself failed (quota, rate limit, network). The
307
+ // project's auto-50 signals may still be valid; we explain that and tell
308
+ // the user when fresh audits will resume.
309
+ const AUDIT_ERROR_LABEL = {
310
+ anthropic_quota_exceeded: 'Daily audit budget reached',
311
+ anthropic_rate_limited: 'Audit engine rate-limited',
312
+ anthropic_overloaded: 'Audit engine overloaded',
313
+ anthropic_auth_error: 'Audit engine auth issue',
314
+ anthropic_other: 'Audit engine error',
315
+ claude_returned_no_data: 'Audit engine returned no data',
316
+ network_error: 'Audit engine network error',
317
+ };
318
+ const AUDIT_ERROR_DETAIL = {
319
+ anthropic_quota_exceeded: "commit.show paused fresh audits until the daily budget refills. " +
320
+ "Cached audits (any repo audited in the last 7 days) still work normally.",
321
+ anthropic_rate_limited: "Too many fresh audits in a short window. Wait a minute and retry. " +
322
+ "Cached results stay available.",
323
+ anthropic_overloaded: "The audit engine is briefly overloaded. Retry in a minute or two.",
324
+ anthropic_auth_error: "commit.show's API key needs attention. Cached results still work.",
325
+ anthropic_other: "Something on the audit engine side blocked this run. Try again later.",
326
+ claude_returned_no_data: "The audit engine ran but returned an empty response. Try again.",
327
+ network_error: "The audit engine couldn't be reached. Check your connection or retry.",
328
+ };
329
+ function untilHuman(seconds) {
330
+ if (seconds < 60)
331
+ return `${seconds}s`;
332
+ if (seconds < 3600)
333
+ return `${Math.round(seconds / 60)}m`;
334
+ if (seconds < 86400)
335
+ return `${Math.round(seconds / 3600)}h`;
336
+ return `${Math.round(seconds / 86400)}d`;
337
+ }
338
+ export function renderAuditError(err, projectName, projectUrl) {
339
+ const label = AUDIT_ERROR_LABEL[err.type] ?? AUDIT_ERROR_LABEL.anthropic_other;
340
+ const detail = AUDIT_ERROR_DETAIL[err.type] ?? AUDIT_ERROR_DETAIL.anthropic_other;
341
+ const lines = [];
342
+ const horiz = '─'.repeat(58);
343
+ lines.push(' ' + c.muted('┌' + horiz + '┐'));
344
+ lines.push(' ' + c.muted('│ ') + c.bold(c.gold('commit.show')) + c.muted(' · ') + c.scarlet(label) + ' '.repeat(Math.max(0, 58 - 14 - label.length)) + c.muted('│'));
345
+ lines.push(' ' + c.muted('│' + ' '.repeat(58) + '│'));
346
+ if (projectName) {
347
+ lines.push(' ' + c.muted('│ ') + c.cream(`Repo: ${projectName}`) + ' '.repeat(Math.max(0, 56 - 6 - projectName.length)) + c.muted('│'));
348
+ lines.push(' ' + c.muted('│' + ' '.repeat(58) + '│'));
349
+ }
350
+ for (const w of wrapText(detail, 54)) {
351
+ lines.push(' ' + c.muted('│ ') + c.cream(w) + ' '.repeat(Math.max(0, 56 - w.length)) + c.muted('│'));
352
+ }
353
+ if (err.retry_after_seconds && err.retry_after_seconds > 0) {
354
+ lines.push(' ' + c.muted('│' + ' '.repeat(58) + '│'));
355
+ const t = `Retry after ~${untilHuman(err.retry_after_seconds)}`;
356
+ lines.push(' ' + c.muted('│ ') + c.dim(t) + ' '.repeat(Math.max(0, 56 - t.length)) + c.muted('│'));
357
+ }
358
+ lines.push(' ' + c.muted('│' + ' '.repeat(58) + '│'));
359
+ const statusLine = `Status check: commitshow status ${projectName ?? '<repo>'}`;
360
+ lines.push(' ' + c.muted('│ ') + c.dim(statusLine) + ' '.repeat(Math.max(0, 56 - statusLine.length)) + c.muted('│'));
361
+ if (projectUrl) {
362
+ lines.push(' ' + c.muted('│ ') + c.dim('Web view: ') + c.cream(projectUrl.length > 44 ? projectUrl.slice(0, 41) + '…' : projectUrl) + ' '.repeat(Math.max(0, 46 - Math.min(44, projectUrl.length))) + c.muted('│'));
363
+ }
364
+ lines.push(' ' + c.muted('└' + horiz + '┘'));
365
+ return lines.join('\n');
366
+ }
304
367
  function timeUntil(isoTarget) {
305
368
  const ms = Math.max(0, new Date(isoTarget).getTime() - Date.now());
306
369
  const h = Math.floor(ms / 3_600_000);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commitshow",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "commit.show CLI — audit any vibe-coded project from your terminal.",
5
5
  "type": "module",
6
6
  "bin": {