shippingszn 0.8.4 → 0.8.5

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.
Files changed (3) hide show
  1. package/README.md +30 -58
  2. package/dist/index.js +62 -98
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,13 +12,14 @@ pnpm dlx shippingszn
12
12
  ```
13
13
 
14
14
  The CLI **never writes, modifies, or deletes** any files - it only reads.
15
- Everything stays on your machine unless you explicitly opt in with `--proof`
16
- or `--publish`.
15
+ By default it posts one anonymous Wall of Launches entry with score, severity
16
+ counts, files scanned, scanner version, and safe stack tags. It never uploads
17
+ source code, paths, filenames, repo URLs, project names, secrets, handles, or
18
+ emails. Use `--no-publish` for a private local run.
17
19
 
18
20
  Human output starts with a Launch Readiness Score, scope covered, the top next
19
21
  step to fix, and severity counts. Run with `--json` to get machine-readable
20
- findings, copy/paste fix prompts for your AI builder, and proof/report handoff
21
- fields:
22
+ findings plus the Wall/report handoff fields:
22
23
 
23
24
  ```json
24
25
  {
@@ -32,24 +33,15 @@ fields:
32
33
  "coveragePenalty": 0,
33
34
  "coverageSummary": "CLI scan covered 4 readiness areas with 4 areas requiring owner verification.",
34
35
  "topNextStep": "Fix Lock up your API keys and passwords: Possible OpenAI API key hardcoded in source.",
35
- "proofCreatePath": "https://shippingszn.com/scan",
36
- "proofUploadHint": "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON in the web scan proof flow to create a shareable launch proof result.",
37
- "proofUrl": "https://shippingszn.com/proof/00000000-0000-4000-8000-000000000123",
38
- "proofResultId": "00000000-0000-4000-8000-000000000123",
39
- "badgeMarkdown": "[![shippingszn scan proof: 70%](https://shippingszn.com/api/badge.svg?...)](https://shippingszn.com/proof/00000000-0000-4000-8000-000000000123)",
40
36
  "wallUrl": "https://shippingszn.com/wall",
41
- "reportUrl": "https://shippingszn.com/report?scanResultId=00000000-0000-4000-8000-000000000123"
37
+ "reportUrl": "https://shippingszn.com/report"
42
38
  },
43
39
  "findings": [
44
40
  {
45
41
  "severity": "critical",
46
42
  "message": "Possible OpenAI API key hardcoded in source.",
47
- "remediation": {
48
- "fixPrompt": "You are fixing a shippingszn launch-readiness finding...",
49
- "verify": "Re-run npx shippingszn --json and confirm this exact finding is gone...",
50
- "escalation": "Escalate before launch if this touches secrets, auth, payments...",
51
- "proofNextStep": "After the fix verifies clean, run npx shippingszn --json..."
52
- }
43
+ "whatFailed": "A secret-like token appears in a scanned file.",
44
+ "whyItBlocksLaunch": "A leaked key can spend money or expose customer data before you notice."
53
45
  }
54
46
  ]
55
47
  }
@@ -59,26 +51,20 @@ When Critical or High findings make paid confidence relevant, the summary also
59
51
  includes a `/report` link. Clean scans and lower-risk findings do not push the
60
52
  paid report CTA.
61
53
 
62
- ## Direct proof publishing
54
+ ## Wall publishing
63
55
 
64
- Use `--proof` when you want the current scan to become shareable launch proof:
56
+ Plain `npx shippingszn` feeds the Wall automatically. The public row is
57
+ anonymous and intentionally small: score, launch label, files scanned, finding
58
+ counts, detected stack tags, scanner version, and timestamp.
59
+
60
+ Use this when you want a completely private local run:
65
61
 
66
62
  ```bash
67
- npx shippingszn --json --proof > shippingszn-scan.json
63
+ npx shippingszn --no-publish
68
64
  ```
69
65
 
70
- That opt-in posts the scan summary and remediation fields to
71
- `/api/scan-results`, then adds `launchReadiness.proofUrl`,
72
- `launchReadiness.proofResultId`, `launchReadiness.badgeMarkdown`,
73
- `launchReadiness.wallUrl`, and `launchReadiness.reportUrl` to the JSON. It
74
- also posts a proof-linked severity summary to the Wall of Launches. If proof
75
- upload fails, findings still print and JSON includes
76
- `launchReadiness.proofUploadError`; if the Wall publish fails, JSON includes
77
- `launchReadiness.wallPublishError`.
78
-
79
- For a manual handoff, you can still run `npx shippingszn --json >
80
- shippingszn-scan.json` and paste or upload the file at
81
- `https://shippingszn.com/scan`.
66
+ For a paid-report handoff, run `npx shippingszn --json > shippingszn-scan.json`
67
+ and paste or upload the file at `https://shippingszn.com/scan`.
82
68
 
83
69
  ## What gets checked
84
70
 
@@ -156,12 +142,8 @@ shippingszn [path] [options]
156
142
 
157
143
  Options:
158
144
  --json Output a machine-readable JSON report.
159
- --publish Opt in to posting an anonymous summary to the public
160
- Wall of Launches. Never uploads source code, paths,
161
- filenames, or project names.
162
- --proof Opt in to posting this scan to /api/scan-results and
163
- printing a proof URL, Wall URL, report URL, and badge.
164
- Can also be enabled with SHIPPINGSZN_PROOF=1.
145
+ --publish Legacy alias. Anonymous Wall stats publish by default.
146
+ --no-publish Disable the anonymous Wall post for this run.
165
147
  --base-url <url> Base URL used to build links back to checklist items.
166
148
  --cwd <path> Directory to scan. Default: current working directory.
167
149
  --no-color Disable ANSI colors in the human-readable report.
@@ -179,32 +161,22 @@ This makes the CLI suitable for CI:
179
161
 
180
162
  ```yaml
181
163
  # .github/workflows/launch-check.yml
182
- - run: npx shippingszn --json --proof > launch-check.json
164
+ - run: npx shippingszn --json > launch-check.json
183
165
  ```
184
166
 
185
- For PR scan proof, have GitHub Actions run the scanner with `--proof` and post
186
- the JSON summary as a comment: score, severity counts, top next step, proof URL,
187
- badge markdown, Wall URL, report URL, and worst Critical/High findings. Keep
188
- `--publish` out of the workflow unless you explicitly want to publish an
189
- anonymous Wall summary when proof upload fails.
167
+ For PR scan signal, have GitHub Actions run the scanner and post the JSON
168
+ summary as a comment: score, severity counts, top next step, Wall URL, report
169
+ URL, and worst Critical/High findings.
190
170
 
191
171
  ## Privacy
192
172
 
193
- `shippingszn` reads files on your machine. It never uploads source code,
194
- makes outbound network calls, or phones home by default. No accounts.
195
- Inspect the source or audit `npm pack --dry-run` to confirm.
196
-
197
- If you want public launch-readiness proof, pass `--proof`. That opt-in posts
198
- the current scan result to shippingszn so it can create a proof page, report
199
- handoff, README badge, and proof-linked Wall entry. It uploads finding titles,
200
- messages, remediation prompts, verification text, checklist permalinks,
201
- severity counts, score, file count, target folder name, and scanner version. It
202
- does not upload source code or secret values.
203
-
204
- If you only want an anonymous Wall of Launches summary, pass `--publish`.
205
- That opt-in posts files scanned count, finding counts by severity, detected
206
- stack tags, and scanner version. It never uploads source code, file paths,
207
- filenames, project names, secrets, or report contents.
173
+ `shippingszn` reads files on your machine. It never uploads source code. By
174
+ default it makes one best-effort outbound request to post anonymous Wall stats:
175
+ score, launch label, files scanned count, finding counts by severity, detected
176
+ stack tags, scanner version, and timestamp. It never uploads source code, file
177
+ paths, filenames, project names, repo URLs, secrets, emails, handles, or report
178
+ contents. Use `--no-publish` or `SHIPPINGSZN_DISABLE_PUBLISH=1` to disable that
179
+ request.
208
180
 
209
181
  ## License
210
182
 
package/dist/index.js CHANGED
@@ -1506,10 +1506,6 @@ import { promises as fs2 } from "node:fs";
1506
1506
  import * as path10 from "node:path";
1507
1507
  var DEFAULT_BASE_URL = "https://shippingszn.com";
1508
1508
  var PUBLISH_TIMEOUT_MS = 3e3;
1509
- function shouldPublish() {
1510
- const v = process.env["SHIPPINGSZN_PUBLISH"] ?? "";
1511
- return v === "1" || v.toLowerCase() === "true" || v === "yes";
1512
- }
1513
1509
  async function detectStack(cwd2) {
1514
1510
  const tags = /* @__PURE__ */ new Set();
1515
1511
  try {
@@ -1555,8 +1551,11 @@ async function detectStack(cwd2) {
1555
1551
  }
1556
1552
  return [...tags].slice(0, 12);
1557
1553
  }
1558
- function buildPayload(totals, filesScanned, stack, scannerVersion) {
1554
+ function buildPayload(totals, filesScanned, stack, scannerVersion, score, label) {
1559
1555
  const out = {
1556
+ source: "cli",
1557
+ score,
1558
+ label,
1560
1559
  filesScanned,
1561
1560
  findingsCritical: totals.critical ?? 0,
1562
1561
  findingsHigh: totals.high ?? 0,
@@ -1568,14 +1567,15 @@ function buildPayload(totals, filesScanned, stack, scannerVersion) {
1568
1567
  return out;
1569
1568
  }
1570
1569
  async function publishScan(totals, filesScanned, opts) {
1571
- if (!shouldPublish()) return "skipped";
1572
1570
  const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
1573
1571
  const stack = await detectStack(opts.cwd);
1574
1572
  const payload = buildPayload(
1575
1573
  totals,
1576
1574
  filesScanned,
1577
1575
  stack,
1578
- opts.scannerVersion
1576
+ opts.scannerVersion,
1577
+ opts.score,
1578
+ opts.label
1579
1579
  );
1580
1580
  const controller = new AbortController();
1581
1581
  const timer = setTimeout(() => controller.abort(), PUBLISH_TIMEOUT_MS);
@@ -1644,13 +1644,8 @@ function buildProofPayload(report, scannerVersion) {
1644
1644
  body: clampText(finding2.message, 3e3),
1645
1645
  whatFailed: clampText(finding2.whatFailed, 3e3),
1646
1646
  whyItBlocksLaunch: clampText(finding2.whyItBlocksLaunch, 3e3),
1647
- fixInstructions: clampText(finding2.fixInstructions, 4e3),
1648
- aiBuilderPrompt: clampText(finding2.aiBuilderPrompt, 4e3),
1649
1647
  evidence: clampText(finding2.evidence, 3e3),
1650
1648
  confidence: finding2.confidence,
1651
- fixPrompt: clampText(finding2.aiBuilderPrompt, 4e3),
1652
- verify: clampText(finding2.verificationStep, 3e3),
1653
- verificationStep: clampText(finding2.verificationStep, 3e3),
1654
1649
  ...locationFromFinding(finding2) ? { location: clampText(locationFromFinding(finding2), 500) } : {},
1655
1650
  permalink: finding2.permalink,
1656
1651
  itemTitle: clampText(finding2.itemTitle, 160)
@@ -1761,52 +1756,6 @@ async function uploadProof(report, opts) {
1761
1756
  }
1762
1757
  }
1763
1758
 
1764
- // src/remediation.ts
1765
- var SEVERITY_INTENT = {
1766
- critical: "Treat this as launch-blocking until fixed.",
1767
- high: "Fix this before public traffic or paid acquisition.",
1768
- medium: "Fix this before announcing or indexing the launch.",
1769
- lower: "Fix this when polishing the launch artifact."
1770
- };
1771
- var ESCALATION = {
1772
- critical: "Escalate before launch if this touches secrets, auth, payments, customer data, production infrastructure, or anything already exposed publicly.",
1773
- high: "Escalate if the fix affects auth, payments, deploy config, browser security, or a user-visible production path.",
1774
- medium: "Escalate only if the fix changes routing, indexing, analytics, or public launch messaging.",
1775
- lower: "Escalation is usually unnecessary unless this blocks installability, branding, or a promised launch surface."
1776
- };
1777
- function locationForPrompt(finding2) {
1778
- if (!finding2.file) return "No specific file was attached to the finding.";
1779
- const line = finding2.line ? `:${finding2.line}` : "";
1780
- return `Likely location: ${finding2.file}${line}.`;
1781
- }
1782
- function proofStepForSeverity(severity, proofCreatePath, reportUrl) {
1783
- const proofStep = `After the fix verifies clean, run npx shippingszn --json and paste or upload the JSON at ${proofCreatePath} to create launch proof.`;
1784
- if (severity === "critical" || severity === "high") {
1785
- return `${proofStep} If this app has users, revenue, client trust, or paid API exposure, attach the clean scan to a paid launch-readiness report at ${reportUrl}.`;
1786
- }
1787
- return proofStep;
1788
- }
1789
- function buildRemediationPrompt({
1790
- finding: finding2,
1791
- item,
1792
- proofCreatePath,
1793
- reportUrl
1794
- }) {
1795
- const itemTitle = item?.title ?? finding2.itemId;
1796
- const location = locationForPrompt(finding2);
1797
- const intent = SEVERITY_INTENT[finding2.severity];
1798
- return {
1799
- fixPrompt: `You are fixing a shippingszn launch-readiness finding. Severity: ${finding2.severity}. Checklist area: ${itemTitle}. Finding: ${finding2.message} ${location} ${intent} Make the smallest production-safe code or config change that removes the underlying risk, preserve existing behavior, and list the files changed. Do not suppress the scanner unless you can prove the finding is a false positive.`,
1800
- verify: "Re-run npx shippingszn --json and confirm this exact finding is gone. Also run the app's normal typecheck/test/build command when the fix changes code, config, routing, security behavior, or public assets.",
1801
- escalation: ESCALATION[finding2.severity],
1802
- proofNextStep: proofStepForSeverity(
1803
- finding2.severity,
1804
- proofCreatePath,
1805
- reportUrl
1806
- )
1807
- };
1808
- }
1809
-
1810
1759
  // ../../lib/launch-readiness/src/index.ts
1811
1760
  var SEVERITY_WEIGHTS = {
1812
1761
  critical: 35,
@@ -1843,7 +1792,7 @@ var SCORE_BANDS = [
1843
1792
  id: "launchable",
1844
1793
  minScore: 90,
1845
1794
  maxScore: 100,
1846
- label: "Launchable with proof",
1795
+ label: "Launchable",
1847
1796
  decision: "go",
1848
1797
  goNoGoLabel: "Go"
1849
1798
  }
@@ -2224,12 +2173,12 @@ function decisionText(band, counts, source) {
2224
2173
  return `Fix the high-risk launch gaps before a real campaign, customer demo, or paid traffic.`;
2225
2174
  }
2226
2175
  if (counts.medium > 0 || counts.lower > 0) {
2227
- return `No critical or high blocker was detected, but the remaining readiness gaps should be fixed and verified before treating the ${sourceLabel(source)} as clean proof.`;
2176
+ return `No critical or high blocker was detected, but the remaining readiness gaps should be fixed and verified before treating the ${sourceLabel(source)} as launchable.`;
2228
2177
  }
2229
2178
  if (source === "url") {
2230
2179
  return "No public-page blockers were found. Run the CLI or GitHub Action before calling the app launch-ready.";
2231
2180
  }
2232
- return `${band.goNoGoLabel}: no scan-detected blockers were found. Keep the proof attached to the launch record.`;
2181
+ return `${band.goNoGoLabel}: no scan-detected blockers were found. Monitor first production traffic.`;
2233
2182
  }
2234
2183
  function topNextStep(blockers, counts, source) {
2235
2184
  const top = blockers[0];
@@ -2240,9 +2189,9 @@ function topNextStep(blockers, counts, source) {
2240
2189
  return "Run the repo scan next so secrets, auth, API routes, and generated-code risks are checked before launch.";
2241
2190
  }
2242
2191
  if (counts.medium > 0 || counts.lower > 0) {
2243
- return "Fix the remaining readiness gaps and rescan before publishing proof.";
2192
+ return "Fix the remaining readiness gaps and rescan before launch.";
2244
2193
  }
2245
- return "Attach this clean scan proof to the launch record and keep monitoring the first production traffic.";
2194
+ return "No obvious launch blockers caught. Monitor first production traffic and keep the paid report for higher-stakes launches.";
2246
2195
  }
2247
2196
  function aggregateConfidence(source, findings) {
2248
2197
  if (findings.some((finding2) => finding2.confidence === "low")) return "low";
@@ -2327,7 +2276,7 @@ function parseArgs(argv2) {
2327
2276
  help: false,
2328
2277
  version: false,
2329
2278
  noColor: !!process2.env.NO_COLOR,
2330
- publish: false,
2279
+ publish: !publishDisabledByEnv(),
2331
2280
  proof: shouldUploadProof()
2332
2281
  };
2333
2282
  for (let i = 0; i < argv2.length; i++) {
@@ -2336,6 +2285,7 @@ function parseArgs(argv2) {
2336
2285
  else if (a === "--version" || a === "-v") opts.version = true;
2337
2286
  else if (a === "--json") opts.json = true;
2338
2287
  else if (a === "--publish") opts.publish = true;
2288
+ else if (a === "--no-publish") opts.publish = false;
2339
2289
  else if (a === "--proof") opts.proof = true;
2340
2290
  else if (a === "--no-color") opts.noColor = true;
2341
2291
  else if (a === "--base-url") opts.baseUrl = argv2[++i] ?? opts.baseUrl;
@@ -2344,6 +2294,21 @@ function parseArgs(argv2) {
2344
2294
  }
2345
2295
  return opts;
2346
2296
  }
2297
+ function envTruthy(value) {
2298
+ if (!value) return false;
2299
+ const normalized = value.trim().toLowerCase();
2300
+ return normalized === "1" || normalized === "true" || normalized === "yes";
2301
+ }
2302
+ function envFalsey(value) {
2303
+ if (!value) return false;
2304
+ const normalized = value.trim().toLowerCase();
2305
+ return normalized === "0" || normalized === "false" || normalized === "no";
2306
+ }
2307
+ function publishDisabledByEnv() {
2308
+ if (envTruthy(process2.env.SHIPPINGSZN_DISABLE_PUBLISH)) return true;
2309
+ if (envFalsey(process2.env.SHIPPINGSZN_PUBLISH)) return true;
2310
+ return false;
2311
+ }
2347
2312
  function color(enabled) {
2348
2313
  const wrap = (codes) => (s) => enabled ? `\x1B[${codes}m${s}\x1B[0m` : s;
2349
2314
  return {
@@ -2377,12 +2342,8 @@ Usage:
2377
2342
 
2378
2343
  Options:
2379
2344
  --json Output a machine-readable JSON report.
2380
- --publish Opt in to posting an anonymous summary to the public
2381
- Wall of Launches. Never uploads source code, paths,
2382
- filenames, or project names.
2383
- --proof Opt in to posting this scan to /api/scan-results and
2384
- printing a proof URL, Wall URL, report URL, and badge.
2385
- Can also be enabled with SHIPPINGSZN_PROOF=1.
2345
+ --publish Legacy alias. Anonymous Wall stats publish by default.
2346
+ --no-publish Disable the anonymous Wall post for this run.
2386
2347
  --base-url <url> Base URL used to build links back to checklist items.
2387
2348
  (default: ${DEFAULT_BASE_URL2})
2388
2349
  --cwd <path> Directory to scan. Default: current working directory.
@@ -2390,8 +2351,9 @@ Options:
2390
2351
  -h, --help Show this help.
2391
2352
  -v, --version Print version.
2392
2353
 
2393
- The scanner only reads files. It never writes, modifies, deletes, or makes
2394
- network requests unless you explicitly opt in with --publish or --proof.
2354
+ The scanner only reads files. It never writes, modifies, or deletes. It posts
2355
+ anonymous aggregate Wall stats by default: no source code, paths, filenames,
2356
+ project names, secrets, handles, emails, or repo URLs.
2395
2357
  Exit code is non-zero if any Critical findings are detected.
2396
2358
  `
2397
2359
  );
@@ -2432,18 +2394,13 @@ async function run() {
2432
2394
  const trimmedBaseUrl = opts.baseUrl.replace(/\/$/, "");
2433
2395
  const reportUrl = `${trimmedBaseUrl}/report`;
2434
2396
  const proofCreatePath = `${trimmedBaseUrl}/scan`;
2435
- const proofUploadHint = "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON in the web scan proof flow to create a shareable launch proof result.";
2397
+ const proofUploadHint = "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON on shippingszn.com/scan if you want to attach the local scan to a paid report handoff.";
2436
2398
  const tracked_aware = applyTrackingAwareSeverity(all, tracked);
2437
2399
  const source = scanSource();
2438
2400
  const enriched = tracked_aware.map((f) => {
2439
2401
  const item = CHECKLIST_ITEMS[f.itemId];
2440
- const remediation = buildRemediationPrompt({
2441
- finding: f,
2442
- item,
2443
- proofCreatePath,
2444
- reportUrl
2445
- });
2446
2402
  const itemTitle = item?.title ?? f.itemId;
2403
+ const permalink = permalinkFor(f.itemId, opts.baseUrl);
2447
2404
  const normalized = normalizeLaunchFinding(
2448
2405
  {
2449
2406
  severity: f.severity,
@@ -2453,22 +2410,26 @@ async function run() {
2453
2410
  evidence: f.evidence,
2454
2411
  file: f.file,
2455
2412
  line: f.line,
2456
- permalink: permalinkFor(f.itemId, opts.baseUrl),
2457
- itemTitle,
2458
- fixInstructions: remediation.fixPrompt,
2459
- aiBuilderPrompt: remediation.fixPrompt,
2460
- verificationStep: remediation.verify,
2461
- fixPrompt: remediation.fixPrompt,
2462
- verify: remediation.verify
2413
+ permalink,
2414
+ itemTitle
2463
2415
  },
2464
2416
  source
2465
2417
  );
2466
2418
  return {
2467
2419
  ...f,
2468
- ...normalized,
2420
+ severity: normalized.severity,
2421
+ title: normalized.title,
2422
+ body: normalized.body,
2423
+ message: normalized.message,
2424
+ whatFailed: normalized.whatFailed,
2425
+ whyItBlocksLaunch: normalized.whyItBlocksLaunch,
2426
+ evidence: normalized.evidence,
2427
+ confidence: normalized.confidence,
2428
+ ...normalized.location ? { location: normalized.location } : {},
2429
+ ...normalized.file ? { file: normalized.file } : {},
2430
+ ...normalized.line ? { line: normalized.line } : {},
2469
2431
  itemTitle,
2470
- permalink: permalinkFor(f.itemId, opts.baseUrl),
2471
- remediation
2432
+ permalink
2472
2433
  };
2473
2434
  });
2474
2435
  enriched.sort((a, b) => {
@@ -2535,16 +2496,18 @@ async function run() {
2535
2496
  }
2536
2497
  let publishResult = "skipped";
2537
2498
  try {
2538
- if (opts.publish) {
2539
- process2.env["SHIPPINGSZN_PUBLISH"] = "1";
2540
- }
2541
2499
  const proofAlreadyPublishedWall = proofResult.status === "uploaded" && !!proofResult.wallUrl;
2542
- if (!proofAlreadyPublishedWall) {
2500
+ if (opts.publish && !proofAlreadyPublishedWall) {
2543
2501
  publishResult = await publishScan(totals, files.length, {
2544
2502
  cwd: opts.cwd,
2545
2503
  baseUrl: opts.baseUrl,
2546
- scannerVersion: PKG_VERSION
2504
+ scannerVersion: PKG_VERSION,
2505
+ score: launchReadiness.score,
2506
+ label: launchReadiness.label
2547
2507
  });
2508
+ if (publishResult === "published") {
2509
+ report.launchReadiness.wallUrl = `${opts.baseUrl.replace(/\/$/, "")}/wall`;
2510
+ }
2548
2511
  }
2549
2512
  } catch {
2550
2513
  }
@@ -2582,12 +2545,12 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
2582
2545
  `
2583
2546
  );
2584
2547
  process2.stdout.write(
2585
- `${c.bold("Fix prompts:")} ${c.dim("Run with --json for copy/paste AI-builder remediation prompts.")}
2548
+ `${c.bold("Action plan:")} ${c.dim("Exact AI-builder prompts and verification steps are in the paid report.")}
2586
2549
  `
2587
2550
  );
2588
2551
  if (launchReadiness.reportUrl) {
2589
2552
  process2.stdout.write(
2590
- `${c.bold("Need paid confidence?")} ${c.dim(`Get a launch-readiness report: ${launchReadiness.reportUrl}`)}
2553
+ `${c.bold("Need a written launch decision?")} ${c.dim(`Get the $49 report: ${launchReadiness.reportUrl}`)}
2591
2554
  `
2592
2555
  );
2593
2556
  }
@@ -2622,8 +2585,9 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
2622
2585
  `
2623
2586
  );
2624
2587
  } else {
2588
+ const wallLine = publishResult === "published" ? `Posted anonymous scan stats to ${opts.baseUrl.replace(/\/$/, "")}/wall.` : opts.publish ? "Anonymous Wall stats post by default. If the network is blocked, the local scan still finishes." : "Private local run: Wall publish disabled.";
2625
2589
  process2.stdout.write(
2626
- `${c.bold("Share proof:")} ${c.dim("Run with --proof to publish this scan to a public proof URL.")}
2590
+ `${c.bold("Wall signal:")} ${c.dim(wallLine)}
2627
2591
  `
2628
2592
  );
2629
2593
  }
@@ -2631,7 +2595,7 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
2631
2595
  if (enriched.length === 0) {
2632
2596
  process2.stdout.write(
2633
2597
  c.green(
2634
- "No findings. Attach the clean scan proof to the launch record before shipping.\n\n"
2598
+ "No findings. The scanner did not catch obvious launch blockers in this pass.\n\n"
2635
2599
  )
2636
2600
  );
2637
2601
  if (publishResult === "published") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shippingszn",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Read-only CLI scanner that checks a project for common pre-launch issues from the shippingszn.com launch checklist.",
5
5
  "license": "MIT",
6
6
  "type": "module",