commitshow 0.3.0 → 0.3.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/README.md +116 -25
- package/dist/commands/audit.js +18 -1
- package/dist/lib/render.js +53 -4
- package/dist/lib/target.js +32 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,39 +1,87 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">commit.show CLI</h1>
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Audit any vibe-coded project from your terminal.</strong><br>
|
|
5
|
+
Score · 3-axis breakdown · 3 strengths + 2 concerns · rank · delta — in one command.
|
|
6
|
+
</p>
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
When a walk-on is ready to enter the season for real — Scout forecasts,
|
|
13
|
-
season ranking, Backstage prompt-extraction, Hall of Fame — they audition
|
|
14
|
-
at <https://commit.show/submit>.
|
|
15
|
-
|
|
16
|
-
The npm package + command is `commitshow` (no dot — npm doesn't allow it in
|
|
17
|
-
package names). Everything else uses the brand `commit.show`.
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/commitshow"><img src="https://img.shields.io/npm/v/commitshow?color=F0C040&label=npm&style=flat-square" alt="npm version"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/commitshow"><img src="https://img.shields.io/npm/dw/commitshow?color=0F2040&style=flat-square" alt="weekly downloads"></a>
|
|
11
|
+
<img src="https://img.shields.io/node/v/commitshow?color=0F2040&style=flat-square" alt="node">
|
|
12
|
+
<img src="https://img.shields.io/npm/l/commitshow?color=0F2040&style=flat-square" alt="MIT license">
|
|
13
|
+
</p>
|
|
18
14
|
|
|
19
15
|
```bash
|
|
20
|
-
npx commitshow@latest audit
|
|
21
|
-
# or audit any public project by URL — no cd required
|
|
22
16
|
npx commitshow@latest audit github.com/owner/repo
|
|
23
17
|
```
|
|
24
18
|
|
|
19
|
+
```
|
|
20
|
+
┌──────────────────────────────────────────────────────────┐
|
|
21
|
+
│ commit.show · Audit report │
|
|
22
|
+
└──────────────────────────────────────────────────────────┘
|
|
23
|
+
|
|
24
|
+
maa-website austinpw-cloud/maa-website
|
|
25
|
+
|
|
26
|
+
╔══════════════╗
|
|
27
|
+
║ 82 / 100 ║
|
|
28
|
+
╚══════════════╝
|
|
29
|
+
|
|
30
|
+
Audit 42/50 ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▱▱
|
|
31
|
+
Scout 26/30 ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱
|
|
32
|
+
Comm. 14/20 ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱▱▱
|
|
33
|
+
|
|
34
|
+
┌───────────────────────────────────────────────────────┐
|
|
35
|
+
│ ↑ 80+ edge functions · LCP 1.4s · 50 RLS policies │
|
|
36
|
+
│ ↑ Brief integrity 9/10 · all 6 sections answered │
|
|
37
|
+
│ ↑ Tech layers 6 · full-stack evidence │
|
|
38
|
+
│ ↓ Accessibility 72 · buttons missing aria-labels │
|
|
39
|
+
│ ↓ No API rate limiting on /auth endpoint │
|
|
40
|
+
└───────────────────────────────────────────────────────┘
|
|
41
|
+
|
|
42
|
+
Ranked #3 of 47 Season Zero
|
|
43
|
+
Tier Honors (top 5%)
|
|
44
|
+
Δ +12 since yesterday's audit
|
|
45
|
+
|
|
46
|
+
→ commit.show/projects/bfe11d75-dc67-…
|
|
47
|
+
commit.show
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> [⭐ Star us on GitHub](https://github.com/commitshow/cli) if `commitshow audit` saved you a `// TODO`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Why
|
|
55
|
+
|
|
56
|
+
Vibe-coded projects ship fast and break differently. The CLI gives you a
|
|
57
|
+
**zero-config**, **walk-on** lane to commit.show's audit engine — the same
|
|
58
|
+
Claude-grade analysis used in our 3-week leagues, minus the signup, fee,
|
|
59
|
+
and league commitment. You get a snapshot of where the build sits, what's
|
|
60
|
+
strong, what's wobbling, and how it ranks against everyone else this week.
|
|
61
|
+
|
|
62
|
+
Local runs also drop `.commitshow/audit.md` and `.commitshow/audit.json`
|
|
63
|
+
into your repo, so your AI coding agent can read the report on the next
|
|
64
|
+
turn without a prompt-engineering ritual.
|
|
65
|
+
|
|
66
|
+
When a project is ready for the full season — Scout forecasts, season
|
|
67
|
+
ranking, Backstage prompt extraction, Hall of Fame — it auditions at
|
|
68
|
+
[commit.show/submit](https://commit.show/submit).
|
|
69
|
+
|
|
70
|
+
> The npm package + command is `commitshow` (no dot — npm doesn't allow
|
|
71
|
+
> it in package names). Everywhere else uses the brand **commit.show**.
|
|
72
|
+
|
|
25
73
|
## Install
|
|
26
74
|
|
|
27
75
|
```bash
|
|
28
|
-
# one-shot
|
|
76
|
+
# one-shot (recommended for trying it on someone else's repo)
|
|
29
77
|
npx commitshow@latest audit <target>
|
|
30
78
|
|
|
31
|
-
# or global
|
|
79
|
+
# or global, if you'll run it daily
|
|
32
80
|
npm i -g commitshow
|
|
33
81
|
commitshow audit <target>
|
|
34
82
|
```
|
|
35
83
|
|
|
36
|
-
Node 20
|
|
84
|
+
Requires **Node 20+**.
|
|
37
85
|
|
|
38
86
|
## Usage
|
|
39
87
|
|
|
@@ -61,6 +109,17 @@ Node 20+.
|
|
|
61
109
|
Remote-URL mode works from any directory, which makes one-line X posts
|
|
62
110
|
(`npx commitshow@latest audit <their-url>`) trivial.
|
|
63
111
|
|
|
112
|
+
> **AI agents — read this before invoking.** The CLI does **not** look up
|
|
113
|
+
> projects by name. `commitshow audit warp` will error: there is no
|
|
114
|
+
> agreed-upon mapping from the word "warp" to a single GitHub repo.
|
|
115
|
+
> If a user asks you to audit something by name, **first resolve the
|
|
116
|
+
> canonical `github.com/<owner>/<repo>` URL** (ask the user, or search
|
|
117
|
+
> github.com), then invoke `commitshow audit <that-url>`. The CLI does
|
|
118
|
+
> a HEAD pre-flight against `github.com/<owner>/<repo>` before any audit
|
|
119
|
+
> spend, so a hallucinated slug is rejected with a `not_found` error
|
|
120
|
+
> before we burn Claude credits — but you should still get the URL right
|
|
121
|
+
> on the first try by asking, not guessing.
|
|
122
|
+
|
|
64
123
|
## The AI-coding loop
|
|
65
124
|
|
|
66
125
|
`commitshow audit` in local mode writes to `.commitshow/audit.md` **and**
|
|
@@ -99,6 +158,29 @@ if [ "$score" -lt 80 ]; then
|
|
|
99
158
|
fi
|
|
100
159
|
```
|
|
101
160
|
|
|
161
|
+
### Auditing someone else's project (agent recipe)
|
|
162
|
+
|
|
163
|
+
When a user says "audit warp" or "what does commit.show say about Postiz",
|
|
164
|
+
**don't guess the slug.** Walk this loop:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
1. Ask the user (or search github.com) for the canonical
|
|
168
|
+
`github.com/<owner>/<repo>` URL. Don't invent one — repo names are
|
|
169
|
+
ambiguous and a single wrong guess wastes an audit call.
|
|
170
|
+
2. Run:
|
|
171
|
+
commitshow audit <that-url> --json
|
|
172
|
+
3. If stdout starts with `{"error":"not_found"`, the URL was wrong.
|
|
173
|
+
Re-confirm with the user before retrying.
|
|
174
|
+
4. Otherwise parse `score.total`, `score.band`, `concerns[]` and
|
|
175
|
+
answer the user with those exact numbers — don't paraphrase the
|
|
176
|
+
score.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The CLI HEAD-checks `github.com/<owner>/<repo>` before any audit spend,
|
|
180
|
+
so a hallucinated slug fails fast with a clear `not_found` envelope.
|
|
181
|
+
The friendly error in the message body explicitly tells you to ask the
|
|
182
|
+
user instead of guessing again.
|
|
183
|
+
|
|
102
184
|
### JSON shape (v1 schema)
|
|
103
185
|
|
|
104
186
|
Stable by contract — additive fields don't bump `schema_version`; breaking
|
|
@@ -126,12 +208,21 @@ changes do. Known keys: `project`, `score`, `standing`, `strengths`, `concerns`,
|
|
|
126
208
|
- `0.1` — ✓ read-only audit · status · `--json` · target auto-detect · sidecar files
|
|
127
209
|
- `0.2` — device-flow login · `commitshow submit` · `--watch` mode · CI exit-code gate
|
|
128
210
|
- `0.3` — `commitshow install <pack>` with {{VARIABLE}} substitution
|
|
129
|
-
- `0.4` — MCP server variant (Cursor / Claude Desktop can call commit.show tools directly
|
|
211
|
+
- `0.4` — MCP server variant (Cursor / Claude Desktop can call commit.show tools directly)
|
|
130
212
|
|
|
131
213
|
## Links
|
|
132
214
|
|
|
133
|
-
- Home
|
|
134
|
-
- Source
|
|
135
|
-
- Issues
|
|
215
|
+
- Home — <https://commit.show>
|
|
216
|
+
- Source — <https://github.com/commitshow/cli>
|
|
217
|
+
- Issues — <https://github.com/commitshow/cli/issues>
|
|
218
|
+
- The platform repo — <https://github.com/commitshow/commitshow>
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
<p align="center">
|
|
223
|
+
<strong>Built one repo at a time. <a href="https://commit.show">commit.show</a></strong>
|
|
224
|
+
</p>
|
|
136
225
|
|
|
137
|
-
|
|
226
|
+
<p align="center">
|
|
227
|
+
MIT © 2026 commit.show
|
|
228
|
+
</p>
|
package/dist/commands/audit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveTarget, TargetError } from '../lib/target.js';
|
|
1
|
+
import { resolveTarget, verifyRemoteExists, TargetError } from '../lib/target.js';
|
|
2
2
|
import { findProjectByGithubUrl, fetchLatestSnapshot, fetchStanding, runPreviewAudit, waitForPreviewSnapshot, } from '../lib/api.js';
|
|
3
3
|
import { renderAudit, renderMarkdown, renderJson, renderUpsell, renderQuotaFooter, renderRateLimitDeny, renderAuditError, writeAuditMarkdown, writeAuditJson, } from '../lib/render.js';
|
|
4
4
|
import { c } from '../lib/colors.js';
|
|
@@ -18,6 +18,23 @@ export async function audit(args) {
|
|
|
18
18
|
}
|
|
19
19
|
throw err;
|
|
20
20
|
}
|
|
21
|
+
// Pre-flight: when the user pointed at a remote URL (or owner/repo
|
|
22
|
+
// shorthand), confirm it actually resolves on github.com before we
|
|
23
|
+
// spend any audit budget. Catches the 'agent invented a slug' case
|
|
24
|
+
// ('warp' → 'warpdotdev/warp' that doesn't exist) cleanly.
|
|
25
|
+
if (target.kind === 'remote-url') {
|
|
26
|
+
const check = await verifyRemoteExists(target.github_url);
|
|
27
|
+
if (!check.exists) {
|
|
28
|
+
emitError(asJson, 'not_found', `${target.slug} doesn't resolve on github.com (HTTP ${check.status ?? 'n/a'}).\n` +
|
|
29
|
+
` Common causes:\n` +
|
|
30
|
+
` · wrong owner spelling (try the canonical org slug)\n` +
|
|
31
|
+
` · repo is private — commitshow only audits public ones\n` +
|
|
32
|
+
` · repo was renamed or deleted\n` +
|
|
33
|
+
` If you're an AI agent: ask the user for the canonical github.com URL,\n` +
|
|
34
|
+
` don't guess from the project name.`, target.github_url);
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
21
38
|
if (!asJson) {
|
|
22
39
|
if (force)
|
|
23
40
|
console.log(c.dim(`Refreshing audit for ${target.slug}…`));
|
package/dist/lib/render.js
CHANGED
|
@@ -220,18 +220,18 @@ export function renderAudit(view) {
|
|
|
220
220
|
}
|
|
221
221
|
lines.push('');
|
|
222
222
|
// (concerns/strengths block moved above the score · errors-first 2026-04-30)
|
|
223
|
-
// ───
|
|
223
|
+
// ─── AI Coder 7 Frames · signature framework ───
|
|
224
224
|
// Render only the categories that produced an actionable status (fail /
|
|
225
225
|
// warn / pass when meaningful). N/A categories are dropped to keep the
|
|
226
|
-
// terminal output compact.
|
|
227
|
-
//
|
|
226
|
+
// terminal output compact. Surfaces the seven AI-specific failure
|
|
227
|
+
// modes generic linters miss.
|
|
228
228
|
const vc = snapshot?.github_signals?.vibe_concerns;
|
|
229
229
|
if (vc) {
|
|
230
230
|
const items = vibeChecklistLines(vc);
|
|
231
231
|
const actionable = items.filter(i => i.status !== 'na');
|
|
232
232
|
if (actionable.length > 0) {
|
|
233
233
|
lines.push(' ' + boxTop());
|
|
234
|
-
lines.push(' ' + boxRow('
|
|
234
|
+
lines.push(' ' + boxRow('AI Coder 7 Frames · what AI ships without'.length, c.bold(c.gold('AI Coder 7 Frames')) + c.muted(' · what AI ships without')));
|
|
235
235
|
lines.push(' ' + boxBlank());
|
|
236
236
|
for (const it of actionable.slice(0, 7)) {
|
|
237
237
|
const tone = it.status === 'fail' ? c.scarlet : it.status === 'warn' ? c.gold : c.teal;
|
|
@@ -374,6 +374,55 @@ function vibeChecklistLines(vc) {
|
|
|
374
374
|
else
|
|
375
375
|
out.push({ key: 'prompt_injection', status: 'pass', label: 'Prompt injection risk', detail: 'AI SDK in use · no obvious raw-input patterns' });
|
|
376
376
|
}
|
|
377
|
+
// 8. Hardcoded URLs
|
|
378
|
+
{
|
|
379
|
+
const h = vc?.hardcoded_urls;
|
|
380
|
+
if (h && h.total > 0) {
|
|
381
|
+
const ev = h.samples?.[0] ? `${h.samples[0].file} · ${h.samples[0].pattern}` : undefined;
|
|
382
|
+
out.push({ key: 'hardcoded_urls', status: 'warn', label: 'Hardcoded URLs', detail: `${h.total} file${h.total > 1 ? 's' : ''} · localhost / 127.0.0.1 baked in`, evidence: ev });
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
out.push({ key: 'hardcoded_urls', status: 'pass', label: 'Hardcoded URLs', detail: 'no localhost / dev URLs in scanned files' });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// 9. Mock data in production
|
|
389
|
+
{
|
|
390
|
+
const m = vc?.mock_data;
|
|
391
|
+
if (m && m.total > 0) {
|
|
392
|
+
const ev = m.samples?.[0] ? `${m.samples[0].file} · const ${m.samples[0].collection} = […]` : undefined;
|
|
393
|
+
out.push({ key: 'mock_data', status: 'warn', label: 'Mock data in prod', detail: `${m.total} file${m.total > 1 ? 's' : ''} with inline seed arrays`, evidence: ev });
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
out.push({ key: 'mock_data', status: 'pass', label: 'Mock data in prod', detail: 'no inline mock arrays in app paths' });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// 10. Webhook signature
|
|
400
|
+
{
|
|
401
|
+
const w = vc?.webhook_signature;
|
|
402
|
+
if (!w || w.handlers_seen === 0) {
|
|
403
|
+
out.push({ key: 'webhook_signature', status: 'na', label: 'Webhook signature', detail: 'no webhook handler files detected' });
|
|
404
|
+
}
|
|
405
|
+
else if (w.gap) {
|
|
406
|
+
out.push({ key: 'webhook_signature', status: 'fail', label: 'Webhook signature', detail: `${w.handlers_seen} handler${w.handlers_seen > 1 ? 's' : ''} · 0 HMAC verification` });
|
|
407
|
+
}
|
|
408
|
+
else if (w.verified_seen >= w.handlers_seen) {
|
|
409
|
+
out.push({ key: 'webhook_signature', status: 'pass', label: 'Webhook signature', detail: `${w.verified_seen}/${w.handlers_seen} handlers verify signature` });
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
out.push({ key: 'webhook_signature', status: 'warn', label: 'Webhook signature', detail: `${w.verified_seen}/${w.handlers_seen} handlers verify signature · partial` });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// 11. CORS permissive
|
|
416
|
+
{
|
|
417
|
+
const c = vc?.cors_permissive;
|
|
418
|
+
if (c && c.total > 0) {
|
|
419
|
+
const ev = c.samples?.[0] ? `${c.samples[0].file} · ${c.samples[0].pattern}` : undefined;
|
|
420
|
+
out.push({ key: 'cors_permissive', status: 'warn', label: 'CORS too permissive', detail: `${c.total} file${c.total > 1 ? 's' : ''} · origin: '*' or origin: true`, evidence: ev });
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
out.push({ key: 'cors_permissive', status: 'pass', label: 'CORS too permissive', detail: "no 'origin: *' patterns detected" });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
377
426
|
// Sort fail → warn → pass → na
|
|
378
427
|
const order = { fail: 0, warn: 1, pass: 2, na: 3 };
|
|
379
428
|
return out.sort((a, b) => order[a.status] - order[b.status]);
|
package/dist/lib/target.js
CHANGED
|
@@ -13,6 +13,38 @@ import { existsSync, statSync } from 'node:fs';
|
|
|
13
13
|
import { resolve } from 'node:path';
|
|
14
14
|
export class TargetError extends Error {
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Cheap HEAD request against github.com/<owner>/<repo> so an agent that
|
|
18
|
+
* confidently invented a repo URL ('warp' → 'warpdotdev/warp' that doesn't
|
|
19
|
+
* exist) gets a clean 'no such repo' error before we burn an audit-preview
|
|
20
|
+
* call + Claude credits. Returns:
|
|
21
|
+
* · { exists: true } — 2xx/3xx response (or rate-limited; we
|
|
22
|
+
* allow through rather than false-flag)
|
|
23
|
+
* · { exists: false, status: 404 } — repo missing, private, or renamed
|
|
24
|
+
* · { exists: true, ambiguous: true } — network/CORS/transient failure;
|
|
25
|
+
* don't block the audit, let the
|
|
26
|
+
* server-side path produce its own error
|
|
27
|
+
*/
|
|
28
|
+
export async function verifyRemoteExists(githubUrl) {
|
|
29
|
+
if (!/^https?:\/\/github\.com\//i.test(githubUrl))
|
|
30
|
+
return { exists: true, ambiguous: true };
|
|
31
|
+
try {
|
|
32
|
+
const ctrl = new AbortController();
|
|
33
|
+
const t = setTimeout(() => ctrl.abort(), 5000);
|
|
34
|
+
const r = await fetch(githubUrl, {
|
|
35
|
+
method: 'HEAD',
|
|
36
|
+
redirect: 'follow',
|
|
37
|
+
signal: ctrl.signal,
|
|
38
|
+
});
|
|
39
|
+
clearTimeout(t);
|
|
40
|
+
if (r.status === 404)
|
|
41
|
+
return { exists: false, status: 404 };
|
|
42
|
+
return { exists: true, status: r.status };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { exists: true, ambiguous: true };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
16
48
|
const GITHUB_URL_RE = /^https?:\/\/github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i;
|
|
17
49
|
const GITHUB_HOST_RE = /^github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i;
|
|
18
50
|
const GITHUB_SSH_RE = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commitshow",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "commit.show CLI — audit any vibe-coded project from your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,15 +23,21 @@
|
|
|
23
23
|
"commit.show",
|
|
24
24
|
"audit",
|
|
25
25
|
"vibe-coding",
|
|
26
|
-
"cli"
|
|
26
|
+
"cli",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"cursor",
|
|
29
|
+
"code-quality",
|
|
30
|
+
"developer-tools"
|
|
27
31
|
],
|
|
28
32
|
"author": "commit.show",
|
|
29
33
|
"license": "MIT",
|
|
30
34
|
"homepage": "https://commit.show",
|
|
31
35
|
"repository": {
|
|
32
36
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/
|
|
34
|
-
|
|
37
|
+
"url": "https://github.com/commitshow/cli"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/commitshow/cli/issues"
|
|
35
41
|
},
|
|
36
42
|
"dependencies": {
|
|
37
43
|
"kleur": "^4.1.5"
|