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.
- package/dist/commands/audit.js +49 -1
- package/dist/lib/render.js +63 -0
- package/package.json +1 -1
package/dist/commands/audit.js
CHANGED
|
@@ -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.
|
package/dist/lib/render.js
CHANGED
|
@@ -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);
|