@alexkroman1/aai-cli 5.9.0 → 5.10.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.
Files changed (43) hide show
  1. package/bin.mjs +32 -0
  2. package/dist/{_agent-BPEXSdWX.mjs → _agent-DS2PUJcl.mjs} +1 -1
  3. package/dist/{_bundler-Cjaxa2wi.mjs → _bundler-DC17suWN.mjs} +2 -2
  4. package/dist/{_config-BJm2o795.mjs → _config-DMyolIk9.mjs} +92 -10
  5. package/dist/_config.d.ts +12 -0
  6. package/dist/_dev-env.d.ts +38 -0
  7. package/dist/{_dev-server-Cnf4jNzE.mjs → _dev-server-B3ivqyEd.mjs} +72 -54
  8. package/dist/_dev-server.d.ts +2 -2
  9. package/dist/{_init-DVNeyGSl.mjs → _init-CYLvYU-B.mjs} +3 -3
  10. package/dist/{_server-common-CnaP_Urf.mjs → _server-common-DX8Bfrf5.mjs} +1 -1
  11. package/dist/{_slug-api-CQw1YGR5.mjs → _slug-api-fRNNR8tz.mjs} +1 -1
  12. package/dist/{_templates-DDx08LIG.mjs → _templates-BWJOiWOO.mjs} +2 -2
  13. package/dist/{_typecheck-gate-BT4iPz7A.mjs → _typecheck-gate-DB-PY0A3.mjs} +1 -1
  14. package/dist/{_ui-RZmPgrF6.mjs → _ui-DfwfDbT-.mjs} +26 -1
  15. package/dist/_ui.d.ts +19 -0
  16. package/dist/{_utils-Ch0J4s6a.mjs → _utils-8KKw-bzi.mjs} +9 -2
  17. package/dist/_utils.d.ts +9 -2
  18. package/dist/{build-WdGtFsSx.mjs → build-CACFbdQ4.mjs} +4 -4
  19. package/dist/cli.mjs +20 -20
  20. package/dist/{client-bundler-yiWoXrgb.mjs → client-bundler-DONm-khu.mjs} +1 -1
  21. package/dist/client-bundler.mjs +1 -1
  22. package/dist/{delete-Dr1Jn65f.mjs → delete-DEZ7u3u4.mjs} +5 -4
  23. package/dist/delete.d.ts +5 -0
  24. package/dist/{deploy-Bij1jES6.mjs → deploy-Ch0d_jje.mjs} +7 -7
  25. package/dist/{dev-HBCgChsA.mjs → dev-CdeRcYiQ.mjs} +6 -6
  26. package/dist/{init-BYGp3Usr.mjs → init-GQsNWkLJ.mjs} +7 -7
  27. package/dist/{login-CXazkkcR.mjs → login-BeFUiU6M.mjs} +6 -7
  28. package/dist/scaffold/CLAUDE.md +35 -12
  29. package/dist/scaffold/package.json +3 -3
  30. package/dist/{secret-vhGe-r2a.mjs → secret-4_dYyrpA.mjs} +2 -2
  31. package/dist/{storage-CMPsAmch.mjs → storage-BTfErOOW.mjs} +2 -2
  32. package/dist/{studio-B6zDNPPr.mjs → studio-LNvXtWak.mjs} +6 -6
  33. package/dist/templates/dispatch-center/agent.ts +4 -6
  34. package/dist/templates/retail/agent.ts +4 -6
  35. package/dist/templates/retail/registry.test.ts +5 -3
  36. package/dist/templates/retail/resolve.test.ts +6 -4
  37. package/dist/templates/retail/seed.test.ts +21 -13
  38. package/dist/templates/retail/shared.test.ts +9 -5
  39. package/dist/templates/solo-rpg/agent.test.ts +7 -5
  40. package/dist/templates/solo-rpg/shared.ts +2 -2
  41. package/dist/{test-CkXvfcpq.mjs → test-C-V98oC-.mjs} +3 -4
  42. package/dist/typecheck.mjs +65 -14
  43. package/package.json +5 -4
@@ -90,18 +90,25 @@ describe("seed shape", () => {
90
90
  expect(Object.keys(seed.orders)).toHaveLength(22);
91
91
  });
92
92
 
93
+ // These four sweep the whole corpus, so they assert SOFTLY: one hand-edited
94
+ // order usually breaks several records at once, and a hard failure reports
95
+ // the first and hides how far the damage runs.
93
96
  test("every order belongs to a seeded user and is listed on that user", () => {
94
97
  for (const order of Object.values(seed.orders)) {
95
98
  const user = seed.users[order.user_id];
96
- expect(user, `order ${order.order_id} has no seeded user`).toBeDefined();
97
- expect(user?.orders).toContain(order.order_id);
99
+ expect.soft(user, `order ${order.order_id} has no seeded user`).toBeDefined();
100
+ expect
101
+ .soft(user?.orders ?? [], `${order.order_id} is not listed on its user`)
102
+ .toContain(order.order_id);
98
103
  }
99
104
  });
100
105
 
101
106
  test("every user's listed orders are all seeded", () => {
102
107
  for (const user of Object.values(seed.users)) {
103
108
  for (const orderId of user.orders) {
104
- expect(seed.orders[orderId], `${user.user_id} lists unseeded ${orderId}`).toBeDefined();
109
+ expect
110
+ .soft(seed.orders[orderId], `${user.user_id} lists unseeded ${orderId}`)
111
+ .toBeDefined();
105
112
  }
106
113
  }
107
114
  });
@@ -110,11 +117,10 @@ describe("seed shape", () => {
110
117
  for (const order of Object.values(seed.orders)) {
111
118
  for (const item of order.items) {
112
119
  const variant = seed.products[item.product_id]?.variants[item.item_id];
113
- expect(
114
- variant,
115
- `${order.order_id}: ${item.item_id} missing from its product`,
116
- ).toBeDefined();
117
- expect(variant?.price).toBe(item.price);
120
+ expect
121
+ .soft(variant, `${order.order_id}: ${item.item_id} missing from its product`)
122
+ .toBeDefined();
123
+ expect.soft(variant?.price, `${order.order_id}: ${item.item_id} price`).toBe(item.price);
118
124
  }
119
125
  }
120
126
  });
@@ -123,10 +129,12 @@ describe("seed shape", () => {
123
129
  for (const order of Object.values(seed.orders)) {
124
130
  const user = seed.users[order.user_id];
125
131
  for (const payment of order.payment_history) {
126
- expect(
127
- user?.payment_methods[payment.payment_method_id],
128
- `${order.order_id}: unknown method ${payment.payment_method_id}`,
129
- ).toBeDefined();
132
+ expect
133
+ .soft(
134
+ user?.payment_methods[payment.payment_method_id],
135
+ `${order.order_id}: unknown method ${payment.payment_method_id}`,
136
+ )
137
+ .toBeDefined();
130
138
  }
131
139
  }
132
140
  });
@@ -148,7 +156,7 @@ describe("seed coverage — each datum backs a specific test in agent.test.ts",
148
156
  test("every status the tools branch on is present", () => {
149
157
  const statuses = new Set(Object.values(seed.orders).map((o) => o.status));
150
158
  for (const s of ["pending", "processed", "delivered", "cancelled"]) {
151
- expect(statuses, `no ${s} order seeded`).toContain(s);
159
+ expect.soft(statuses, `no ${s} order seeded`).toContain(s);
152
160
  }
153
161
  });
154
162
 
@@ -57,7 +57,9 @@ describe("storeView", () => {
57
57
  "#W4316152",
58
58
  "#W1840144",
59
59
  ]) {
60
- expect(json, `projection leaked ${leaked}`).not.toContain(leaked);
60
+ // Soft: a widened projection leaks several fields at once, and the full
61
+ // list is what says how much of the other customers' records got out.
62
+ expect.soft(json, `projection leaked ${leaked}`).not.toContain(leaked);
61
63
  }
62
64
  });
63
65
 
@@ -84,15 +86,17 @@ describe("DEMO_PERSONAS", () => {
84
86
  const users = Object.values((structuredClone(seedJson) as unknown as Store).users);
85
87
  for (const persona of DEMO_PERSONAS) {
86
88
  const user = users.find((u) => u.email === persona.email);
87
- expect(user, `no seeded customer has email ${persona.email}`).toBeDefined();
88
- expect(`${user?.name.first_name} ${user?.name.last_name}`).toBe(persona.name);
89
- expect(user?.address.zip).toBe(persona.zip);
89
+ expect.soft(user, `no seeded customer has email ${persona.email}`).toBeDefined();
90
+ expect
91
+ .soft(`${user?.name.first_name} ${user?.name.last_name}`, persona.email)
92
+ .toBe(persona.name);
93
+ expect.soft(user?.address.zip, persona.email).toBe(persona.zip);
90
94
  }
91
95
  });
92
96
 
93
97
  test("every persona carries a hint, so a user can pick a path deliberately", () => {
94
98
  for (const persona of DEMO_PERSONAS) {
95
- expect(persona.hint.length, persona.name).toBeGreaterThan(20);
99
+ expect.soft(persona.hint.length, persona.name).toBeGreaterThan(20);
96
100
  }
97
101
  });
98
102
  });
@@ -167,11 +167,13 @@ describe("applyConsequences MISS matrix", () => {
167
167
  ] as const) {
168
168
  const state = playingState();
169
169
  const { deltas, clockEvents } = applyConsequences(state, miss, position, "standard", null);
170
- expect(state.health).toBe(5 - dmg);
171
- expect(deltas.health).toBe(-dmg);
172
- expect(state.clocks[0]?.filled).toBe(ticks);
173
- expect(deltas.clockTicks).toBe(ticks);
174
- expect(clockEvents).toHaveLength(0); // 4-segment clock not full yet
170
+ // Each row starts from a fresh state, so they are independent — assert
171
+ // softly and a rebalanced table reports the whole matrix in one run.
172
+ expect.soft(state.health, position).toBe(5 - dmg);
173
+ expect.soft(deltas.health, position).toBe(-dmg);
174
+ expect.soft(state.clocks[0]?.filled, position).toBe(ticks);
175
+ expect.soft(deltas.clockTicks, position).toBe(ticks);
176
+ expect.soft(clockEvents, position).toHaveLength(0); // 4-segment clock not full yet
175
177
  }
176
178
  });
177
179
 
@@ -341,8 +341,8 @@ export function saveGameState(ctx: ToolContext, state: GameState): void {
341
341
 
342
342
  // ── Persistent save slots (ctx.db) ───────────────────────────────────────────
343
343
  // save_game / load_game are genuine cross-session persistence, so they use
344
- // the app's SQL database. Requires storage: `aai storage enable` (or the
345
- // Storage toggle in the studio); under `aai dev`, set DATABASE_URL in .env.
344
+ // the app's SQL database. Requires storage: `aai storage enable` (or
345
+ // Settings Database in the studio); under `aai dev`, set DATABASE_URL in .env.
346
346
  //
347
347
  // Slots are keyed by name alone — the whole point of a save is loading it in
348
348
  // a LATER session, whose sessionId differs, so the key can't embed one. The
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as ok, n as fail } from "./_output-CC300DzW.mjs";
3
- import { n as log } from "./_ui-RZmPgrF6.mjs";
4
- import { a as errorMessage, n as binFromPackageJson, r as errorCode } from "./_utils-Ch0J4s6a.mjs";
3
+ import { n as log } from "./_ui-DfwfDbT-.mjs";
4
+ import { a as errorMessage, n as binFromPackageJson, r as errorCode } from "./_utils-8KKw-bzi.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { existsSync } from "node:fs";
7
7
  import path from "node:path";
@@ -51,8 +51,7 @@ function runVitest(cwd) {
51
51
  testFile
52
52
  ], {
53
53
  cwd,
54
- stdio: "inherit",
55
- env: { NODE_OPTIONS: "--experimental-strip-types" }
54
+ stdio: "inherit"
56
55
  });
57
56
  return true;
58
57
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, n as binFromPackageJson } from "./_utils-Ch0J4s6a.mjs";
3
- import { existsSync } from "node:fs";
2
+ import { a as errorMessage, n as binFromPackageJson } from "./_utils-8KKw-bzi.mjs";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { spawn } from "node:child_process";
6
6
  //#region typecheck.ts
@@ -56,13 +56,69 @@ function findTypescriptPackage(cwd) {
56
56
  dir = parent;
57
57
  }
58
58
  }
59
- /** Resolve the project's TypeScript compiler entry (its own `tsc` bin). */
60
- function resolveTscEntry(cwd) {
59
+ /**
60
+ * Resolve the project's TypeScript compiler entry (its own `tsc` bin), plus
61
+ * its major version — see {@link tscArgs} for what the version decides.
62
+ */
63
+ function resolveTsc(cwd) {
61
64
  const dir = findTypescriptPackage(cwd);
62
65
  if (dir === void 0) throw new Error("no typescript package in the project's node_modules");
63
- const bin = binFromPackageJson(path.join(dir, "package.json"), "tsc");
66
+ const manifest = path.join(dir, "package.json");
67
+ const bin = binFromPackageJson(manifest, "tsc");
64
68
  if (!bin) throw new Error("installed typescript package declares no tsc bin");
65
- return bin;
69
+ return {
70
+ entry: bin,
71
+ major: readMajor(manifest)
72
+ };
73
+ }
74
+ /**
75
+ * The resolved compiler's major version, or 0 when it can't be read.
76
+ *
77
+ * 0 is the safe answer: {@link tscArgs} only ADDS flags above a version
78
+ * floor, so an unreadable manifest degrades to the flag set every TypeScript
79
+ * accepts rather than failing a build over a cosmetic read.
80
+ */
81
+ function readMajor(manifest) {
82
+ try {
83
+ const { version } = JSON.parse(readFileSync(manifest, "utf-8"));
84
+ return typeof version === "string" ? Number.parseInt(version, 10) || 0 : 0;
85
+ } catch {
86
+ return 0;
87
+ }
88
+ }
89
+ /**
90
+ * The `tsc` argv for one project check.
91
+ *
92
+ * `--singleThreaded` is the interesting one, and it is a SPEEDUP here rather
93
+ * than a throttle. TypeScript 7 parallelizes parse/check/emit by default,
94
+ * which pays off on a repo-sized program and costs on a single agent project —
95
+ * and this function only ever checks one of those. Measured on the templates
96
+ * project (14 agents, the closest in-repo analogue of a studio workspace):
97
+ *
98
+ * - pinned to 1 core: 2.4–2.9s parallel vs 1.2–1.4s single — ~2x faster
99
+ * - on 4 cores: 1.21–1.24s parallel vs 1.01–1.04s single
100
+ *
101
+ * The 1-core number is the one that matters: a guest sandbox RESERVES one CPU
102
+ * (`SANDBOX_CPU`) and this same check runs after every settled write burst in
103
+ * the studio (`aai-guest/studio-write-diagnostics.ts`), where the whole design
104
+ * rests on it finishing in well under a second. Parallelism inside a one-core
105
+ * reservation is oversubscription: the threads exist, contend, and the wall
106
+ * clock doubles.
107
+ *
108
+ * Gated on major >= 7 because the flag is TS 7's, and an unknown compiler
109
+ * option is a HARD error (TS5023) — a project pinning an older TypeScript
110
+ * would fail its build on a flag it never asked for. `engines`/scaffold pin
111
+ * `^7`, so in practice this floor is always met; it exists so a project that
112
+ * pins otherwise degrades instead of breaking.
113
+ */
114
+ function tscArgs(entry, major) {
115
+ return [
116
+ entry,
117
+ "--noEmit",
118
+ "--pretty",
119
+ "false",
120
+ ...major >= 7 ? ["--singleThreaded"] : []
121
+ ];
66
122
  }
67
123
  /**
68
124
  * Typecheck the project at `cwd` with its own tsconfig + compiler.
@@ -76,9 +132,9 @@ async function typecheckProject(cwd) {
76
132
  ok: true,
77
133
  skipped: true
78
134
  };
79
- let tscEntry;
135
+ let tsc;
80
136
  try {
81
- tscEntry = resolveTscEntry(cwd);
137
+ tsc = resolveTsc(cwd);
82
138
  } catch (err) {
83
139
  return {
84
140
  ok: false,
@@ -86,12 +142,7 @@ async function typecheckProject(cwd) {
86
142
  };
87
143
  }
88
144
  const result = await new Promise((resolve, reject) => {
89
- const child = spawn(process.execPath, [
90
- tscEntry,
91
- "--noEmit",
92
- "--pretty",
93
- "false"
94
- ], {
145
+ const child = spawn(process.execPath, tscArgs(tsc.entry, tsc.major), {
95
146
  cwd,
96
147
  timeout: TYPECHECK_TIMEOUT_MS,
97
148
  stdio: [
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-cli",
3
- "version": "5.9.0",
3
+ "version": "5.10.1",
4
4
  "type": "module",
5
5
  "bin": {
6
- "aai": "dist/cli.mjs"
6
+ "aai": "bin.mjs"
7
7
  },
8
8
  "exports": {
9
9
  "./client-bundler": {
@@ -23,6 +23,7 @@
23
23
  }
24
24
  },
25
25
  "files": [
26
+ "bin.mjs",
26
27
  "dist"
27
28
  ],
28
29
  "dependencies": {
@@ -37,8 +38,8 @@
37
38
  "p-timeout": "^7.0.1",
38
39
  "vite": "^8.1.5",
39
40
  "zod": "^4.4.3",
40
- "@alexkroman1/aai": "5.9.0",
41
- "@alexkroman1/aai-ui": "5.9.0"
41
+ "@alexkroman1/aai": "5.10.1",
42
+ "@alexkroman1/aai-ui": "5.10.1"
42
43
  },
43
44
  "devDependencies": {
44
45
  "playwright": "^1.61.1",