ship-or-theater 1.0.0 → 1.0.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.1] - 2026-11-07
9
+
10
+ ### Changed
11
+ - Updated `package.json`, CLI `--help`, and MCP `evaluate_claim` descriptions to begin with the plain-English summary: *"Paste in an AI claim you saw online and get a straight answer, with evidence: real (SHIPS), half-true (MIXED) or hype (THEATER)."*
12
+ - Landing page, README and social card now say who it's for and why; phone menu links to Principles.
13
+ - Upgraded `@anthropic-ai/sdk` from `^0.39.0` to `^1.6.0`, removing the transitive `formdata-node` / `node-domexception@1.0.0` install deprecation warning.
14
+ - `VERSION` is now read from `package.json` at runtime so CLI and MCP version metadata cannot drift from the package manifest.
15
+
16
+ ### Fixed
17
+ - `npx ship-or-theater "<claim>" > file` from Git Bash on Windows could exit 0 with an empty file. The CLI now waits for `stdout` to finish writing (and drain) before it exits; verified with a real run through `npx` in Git Bash. `--out <file>` remains the most robust way to save a scorecard.
18
+ - Output write errors (for example a closed pipe) are reported on `stderr` with exit code `1` instead of crashing.
19
+
8
20
  ## [1.0.0] - 2026-11-07
9
21
 
10
22
  ### Added
package/README.md CHANGED
@@ -1,3 +1,13 @@
1
+ <!-- sot:readme-pitch:start -->
2
+ # Saw an AI claim that sounds too good to be true?
3
+
4
+ Paste in an AI claim you saw online and get a straight answer, with evidence: real (SHIPS), half-true (MIXED) or hype (THEATER).
5
+
6
+ - Reading a launch post? Find out what actually shipped.
7
+ - Vetting a vendor demo? See what the evidence supports.
8
+ - Checking a headline before you share it? Get the reasons first.
9
+ <!-- sot:readme-pitch:end -->
10
+
1
11
  <div align="center">
2
12
 
3
13
  <img src="assets/logo.svg" alt="ship or theater" width="360">
package/dist/cli.d.ts CHANGED
@@ -1,11 +1,13 @@
1
- import { L as LLM } from './llm-gKw2aSfQ.js';
1
+ import { L as LLM } from './llm-BDKu9GhU.js';
2
2
  import '@anthropic-ai/sdk';
3
3
  import 'zod';
4
4
 
5
5
  interface CliStream {
6
6
  isTTY?: boolean;
7
7
  columns?: number;
8
- write: (chunk: string) => boolean;
8
+ write: (chunk: string, callback?: (error?: Error | null) => void) => boolean;
9
+ once?: (event: string, listener: (...args: unknown[]) => void) => unknown;
10
+ off?: (event: string, listener: (...args: unknown[]) => void) => unknown;
9
11
  }
10
12
  interface CliIO {
11
13
  stdout?: CliStream;
@@ -13,6 +15,11 @@ interface CliIO {
13
15
  env?: NodeJS.ProcessEnv;
14
16
  llmOverride?: LLM;
15
17
  }
18
+ /**
19
+ * Write a chunk to stdout and wait for both the write callback and stream
20
+ * drain when backpressure occurs on a pipe or redirected file descriptor.
21
+ */
22
+ declare function writeStdout(stream: CliStream, chunk: string): Promise<void>;
16
23
  declare function runCli(argv: string[], io?: CliIO): Promise<number>;
17
24
 
18
- export { type CliIO, type CliStream, runCli };
25
+ export { type CliIO, type CliStream, runCli, writeStdout };
package/dist/cli.js CHANGED
@@ -5,9 +5,21 @@ import { writeFile } from "fs/promises";
5
5
  import { Command } from "commander";
6
6
 
7
7
  // src/llm.ts
8
+ import { readFileSync } from "fs";
8
9
  import Anthropic from "@anthropic-ai/sdk";
9
10
  import "zod";
10
- var VERSION = "1.0.0";
11
+ function readPackageVersion(pkgUrl = new URL("../package.json", import.meta.url)) {
12
+ try {
13
+ const raw = readFileSync(pkgUrl, "utf8");
14
+ const parsed = JSON.parse(raw);
15
+ if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
16
+ return parsed.version.trim();
17
+ }
18
+ } catch {
19
+ }
20
+ return "1.0.1";
21
+ }
22
+ var VERSION = readPackageVersion();
11
23
  var DEFAULT_MODEL = "claude-sonnet-5-5";
12
24
  function resolveModel() {
13
25
  const fromEnv = process.env.SOT_MODEL?.trim();
@@ -1226,6 +1238,68 @@ function createProgressReporter(stream = process.stderr, options = {}) {
1226
1238
  }
1227
1239
 
1228
1240
  // src/cli.ts
1241
+ async function writeStdout(stream, chunk) {
1242
+ await new Promise((resolve, reject) => {
1243
+ let settled = false;
1244
+ let writeReturned = false;
1245
+ let writeDone = false;
1246
+ let drainDone = true;
1247
+ const cleanup = (keepErrorListener = false) => {
1248
+ if (typeof stream.off === "function") {
1249
+ stream.off("drain", onDrain);
1250
+ if (!keepErrorListener) stream.off("error", onError);
1251
+ }
1252
+ };
1253
+ const finish = (err) => {
1254
+ if (settled) return;
1255
+ if (err) {
1256
+ settled = true;
1257
+ cleanup(true);
1258
+ reject(err instanceof Error ? err : new Error(String(err)));
1259
+ return;
1260
+ }
1261
+ if (writeReturned && writeDone && drainDone) {
1262
+ settled = true;
1263
+ cleanup();
1264
+ resolve();
1265
+ }
1266
+ };
1267
+ const onDrain = () => {
1268
+ drainDone = true;
1269
+ finish();
1270
+ };
1271
+ const onError = (err) => {
1272
+ finish(err ?? new Error("stdout stream error"));
1273
+ };
1274
+ if (typeof stream.once === "function") {
1275
+ stream.once("error", onError);
1276
+ }
1277
+ try {
1278
+ const expectsCallback = stream.write.length >= 2;
1279
+ const flushed = expectsCallback ? stream.write(chunk, (err) => {
1280
+ if (err) {
1281
+ finish(err);
1282
+ return;
1283
+ }
1284
+ writeDone = true;
1285
+ finish();
1286
+ }) : stream.write(chunk);
1287
+ if (!expectsCallback) {
1288
+ writeDone = true;
1289
+ }
1290
+ if (!flushed && typeof stream.once === "function") {
1291
+ drainDone = false;
1292
+ }
1293
+ writeReturned = true;
1294
+ if (!drainDone && typeof stream.once === "function") {
1295
+ stream.once("drain", onDrain);
1296
+ }
1297
+ finish();
1298
+ } catch (err) {
1299
+ finish(err);
1300
+ }
1301
+ });
1302
+ }
1229
1303
  async function runCli(argv, io = {}) {
1230
1304
  const stdout = io.stdout ?? process.stdout;
1231
1305
  const stderr = io.stderr ?? process.stderr;
@@ -1233,7 +1307,7 @@ async function runCli(argv, io = {}) {
1233
1307
  let exitCode = 0;
1234
1308
  const program = new Command();
1235
1309
  program.name("ship-or-theater").version(VERSION, "-V, --version", "print the package version").description(
1236
- "Evaluate an AI claim against a fixed reality rubric and render a scorecard."
1310
+ "Paste in an AI claim you saw online and get a straight answer, with evidence: real (SHIPS), half-true (MIXED) or hype (THEATER)."
1237
1311
  ).argument(
1238
1312
  "[claim]",
1239
1313
  "the AI claim, announcement, or demo description to evaluate (or 'rubric' to print the fixed rubric)"
@@ -1280,12 +1354,19 @@ async function runCli(argv, io = {}) {
1280
1354
  color: useColor,
1281
1355
  width
1282
1356
  });
1283
- if (options.out) {
1284
- await writeFile(options.out, rubricText, "utf8");
1285
- stderr.write(`Rubric written to ${options.out}
1357
+ try {
1358
+ if (options.out) {
1359
+ await writeFile(options.out, rubricText, "utf8");
1360
+ stderr.write(`Rubric written to ${options.out}
1286
1361
  `);
1287
- } else {
1288
- stdout.write(rubricText + "\n");
1362
+ } else {
1363
+ await writeStdout(stdout, rubricText + "\n");
1364
+ }
1365
+ } catch (err) {
1366
+ const message = err instanceof Error ? err.message : String(err);
1367
+ stderr.write(`Failed to write output: ${message}
1368
+ `);
1369
+ exitCode = 1;
1289
1370
  }
1290
1371
  return;
1291
1372
  }
@@ -1334,12 +1415,19 @@ async function runCli(argv, io = {}) {
1334
1415
  progress.clear();
1335
1416
  }
1336
1417
  const output = format === "json" ? toJSON(scorecard) : format === "pretty" ? renderTerminal(scorecard, { color: useColor, width }) : renderMarkdown(scorecard);
1337
- if (options.out) {
1338
- await writeFile(options.out, output, "utf8");
1339
- stderr.write(`Scorecard written to ${options.out}
1418
+ try {
1419
+ if (options.out) {
1420
+ await writeFile(options.out, output, "utf8");
1421
+ stderr.write(`Scorecard written to ${options.out}
1340
1422
  `);
1341
- } else {
1342
- stdout.write(output + "\n");
1423
+ } else {
1424
+ await writeStdout(stdout, output + "\n");
1425
+ }
1426
+ } catch (err) {
1427
+ const message = err instanceof Error ? err.message : String(err);
1428
+ stderr.write(`Failed to write output: ${message}
1429
+ `);
1430
+ exitCode = 1;
1343
1431
  }
1344
1432
  });
1345
1433
  try {
@@ -1367,6 +1455,7 @@ if (!process.env.VITEST) {
1367
1455
  });
1368
1456
  }
1369
1457
  export {
1370
- runCli
1458
+ runCli,
1459
+ writeStdout
1371
1460
  };
1372
1461
  //# sourceMappingURL=cli.js.map