@alexkroman1/aai-cli 13.3.0 → 14.0.0

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 (134) hide show
  1. package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
  2. package/dist/_agent.d.ts +23 -0
  3. package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DolUCMxu.mjs} +8 -7
  4. package/dist/_cli-common.d.ts +45 -6
  5. package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
  6. package/dist/_dev-env.d.ts +1 -1
  7. package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-CSMqF8PN.mjs} +20 -22
  8. package/dist/_dev-vite-config.d.ts +3 -3
  9. package/dist/{_init-Ci4QGjp8.mjs → _init-CQ8idAwo.mjs} +9 -23
  10. package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
  11. package/dist/_output.d.ts +10 -9
  12. package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
  13. package/dist/_resource-commands.d.ts +9 -9
  14. package/dist/{_server-common-BV4_CrP3.mjs → _server-common-vILJp3it.mjs} +1 -1
  15. package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
  16. package/dist/_slug-api.d.ts +19 -0
  17. package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
  18. package/dist/_studio-commands.d.ts +13 -13
  19. package/dist/{_templates-V4l5sZJy.mjs → _templates-CK4oKoeX.mjs} +3 -3
  20. package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
  21. package/dist/_typecheck-gate.d.ts +3 -1
  22. package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
  23. package/dist/_utils.d.ts +35 -0
  24. package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
  25. package/dist/_workflow-determinism.d.ts +3 -3
  26. package/dist/{build-CWyFGe-W.mjs → build-Mxk8gWvX.mjs} +46 -7
  27. package/dist/build.d.ts +5 -0
  28. package/dist/cli.d.ts +1 -2
  29. package/dist/cli.mjs +117 -135
  30. package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-BJgREAh6.mjs} +1 -1
  31. package/dist/client-bundler.mjs +1 -1
  32. package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
  33. package/dist/{deploy-B7f3CK7v.mjs → deploy-uAJ4NukN.mjs} +11 -11
  34. package/dist/{dev-DamXieW_.mjs → dev-DApPSaE_.mjs} +3 -3
  35. package/dist/{eval-h3DTVfRw.mjs → eval-BK47A_K5.mjs} +3 -3
  36. package/dist/{init-D-vFrV8h.mjs → init-DukDxECd.mjs} +11 -19
  37. package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
  38. package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
  39. package/dist/project-config.mjs +1 -1
  40. package/dist/scaffold/CLAUDE.md +84 -56
  41. package/dist/scaffold/package.json +4 -4
  42. package/dist/scaffold/server.mjs +129 -25
  43. package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
  44. package/dist/secret.d.ts +8 -6
  45. package/dist/{studio-BpMCfUNx.mjs → studio-CpHlNHUZ.mjs} +13 -15
  46. package/dist/templates/call-audit/agent.test.ts +11 -6
  47. package/dist/templates/call-audit/agent.ts +2 -2
  48. package/dist/templates/call-audit/client.tsx +27 -42
  49. package/dist/templates/call-audit/workflows/audit.ts +7 -5
  50. package/dist/templates/call-audit/workflows/ingest.ts +7 -7
  51. package/dist/templates/call-audit/workflows/media.ts +12 -37
  52. package/dist/templates/call-audit/workflows/summarize.ts +8 -8
  53. package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
  54. package/dist/templates/dispatch-center/agent.test.ts +7 -7
  55. package/dist/templates/dispatch-center/client.tsx +53 -61
  56. package/dist/templates/dispatch-center/shared.ts +0 -8
  57. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
  58. package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
  59. package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
  60. package/dist/templates/infocom-adventure/client.tsx +40 -36
  61. package/dist/templates/link-digest/agent.test.ts +3 -3
  62. package/dist/templates/link-digest/agent.ts +2 -2
  63. package/dist/templates/link-digest/client.tsx +27 -17
  64. package/dist/templates/link-digest/workflows/digest.ts +13 -16
  65. package/dist/templates/night-owl/agent.eval.test.ts +4 -2
  66. package/dist/templates/night-owl/client.tsx +8 -2
  67. package/dist/templates/pipeline-simple/agent.test.ts +2 -2
  68. package/dist/templates/pizza-ordering/client.tsx +5 -4
  69. package/dist/templates/pizza-ordering/shared.ts +3 -8
  70. package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
  71. package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
  72. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
  73. package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
  74. package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
  75. package/dist/templates/plan-and-execute/agent.test.ts +10 -5
  76. package/dist/templates/plan-and-execute/client.tsx +2 -2
  77. package/dist/templates/podcast-digest/agent.test.ts +20 -11
  78. package/dist/templates/podcast-digest/client.tsx +16 -22
  79. package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
  80. package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
  81. package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
  82. package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
  83. package/dist/templates/recap-workflow/agent.test.ts +27 -23
  84. package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
  85. package/dist/templates/redline/agent.eval.test.ts +5 -8
  86. package/dist/templates/redline/agent.test.ts +1 -1
  87. package/dist/templates/redline/client.tsx +36 -34
  88. package/dist/templates/redline/workflows/redline.ts +17 -17
  89. package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
  90. package/dist/templates/research-workflow/agent.test.ts +10 -7
  91. package/dist/templates/research-workflow/workflows/research.ts +28 -30
  92. package/dist/templates/retail/agent.test.ts +17 -15
  93. package/dist/templates/retail/cancel.ts +7 -6
  94. package/dist/templates/retail/client.tsx +39 -58
  95. package/dist/templates/retail/payment.ts +4 -3
  96. package/dist/templates/retail/pending.test.ts +19 -19
  97. package/dist/templates/retail/store.test.ts +5 -5
  98. package/dist/templates/retail/swap.ts +8 -11
  99. package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
  100. package/dist/templates/solo-rpg/agent.test.ts +12 -12
  101. package/dist/templates/solo-rpg/client.tsx +3 -3
  102. package/dist/templates/spoken-summary/agent.test.ts +9 -9
  103. package/dist/templates/spoken-summary/agent.ts +3 -3
  104. package/dist/templates/spoken-summary/client.tsx +23 -30
  105. package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
  106. package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
  107. package/dist/templates/support-line/client.tsx +4 -3
  108. package/dist/templates/support-line/prompts.ts +0 -2
  109. package/dist/templates/transcription-workflow/agent.test.ts +20 -12
  110. package/dist/templates/transcription-workflow/agent.ts +2 -2
  111. package/dist/templates/transcription-workflow/client.tsx +15 -14
  112. package/dist/templates/transcription-workflow/recover.ts +1 -1
  113. package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
  114. package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
  115. package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
  116. package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
  117. package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
  118. package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
  119. package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
  120. package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
  121. package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
  122. package/dist/templates/travel-concierge/agent.test.ts +15 -7
  123. package/dist/templates/travel-concierge/client.tsx +2 -2
  124. package/dist/templates/travel-concierge/shared.ts +10 -14
  125. package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
  126. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
  127. package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
  128. package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
  129. package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
  130. package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
  131. package/dist/typecheck.mjs +1 -1
  132. package/dist/worker-bundler.mjs +1 -1
  133. package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
  134. package/package.json +4 -4
@@ -27,6 +27,25 @@ export declare function secretRequest<T = unknown>(cwd: string, resourcePath: st
27
27
  data: T;
28
28
  target: string;
29
29
  }>;
30
+ /** An already-resolved deployed target — what {@link slugRequestOn} needs. */
31
+ export type SlugTarget = {
32
+ serverUrl: string;
33
+ slug: string;
34
+ apiKey: string;
35
+ };
36
+ /**
37
+ * A slug-scoped request against an ALREADY-resolved target.
38
+ *
39
+ * {@link slugRequest} resolves per call, which is right for a command that
40
+ * issues one request and wrong for one that POLLS. `aai logs --follow` re-entered
41
+ * `getServerInfo` on every tick — a read and zod parse of `.aai/project.json`, a
42
+ * read of the global config, a `new URL` trust check, a slug-shape test, and with
43
+ * `--server` an `approveServer` that takes the CROSS-PROCESS config lock — once a
44
+ * second for the life of the follow, all of it re-deriving an immutable answer.
45
+ * `delete.ts` documents the same cost and answers it the same way, by hand; this
46
+ * is the general version, so the next polling command inherits it.
47
+ */
48
+ export declare function slugRequestOn<T = unknown>(target: SlugTarget, resourcePath: string, init: SlugRequestInit): Promise<T>;
30
49
  export declare function slugRequest<T = unknown>(cwd: string, resourcePath: string, init: SlugRequestInit, server?: string): Promise<{
31
50
  data: T;
32
51
  slug: string;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as CliError } from "./_output-DbJt9gsl.mjs";
2
+ import { t as CliError } from "./_output-DBP9Op_d.mjs";
3
3
  import path from "node:path";
4
4
  import { isRecord, omitUndefined } from "@alexkroman1/aai/utils";
5
5
  import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE } from "@alexkroman1/aai/internal";
@@ -221,7 +221,11 @@ function fetchStudioProject(serverUrl, apiKey, project) {
221
221
  apiKey,
222
222
  action: "pull",
223
223
  allow404: true
224
- });
224
+ }).then((res) => res === null ? null : checkedResponse(res, isStudioProject, `the studio project ${project} at ${serverUrl}`));
225
+ }
226
+ /** A pulled workspace, as the wire must actually carry it. */
227
+ function isStudioProject(value) {
228
+ return isRecord(value) && isRecord(value.files) && Object.values(value.files).every((v) => typeof v === "string") && typeof value.sourceHash === "string";
225
229
  }
226
230
  /** `PUT /studio/projects/:project/source` — the atomic whole-tree push. */
227
231
  function pushStudioSource(serverUrl, apiKey, project, body) {
@@ -231,7 +235,7 @@ function pushStudioSource(serverUrl, apiKey, project, body) {
231
235
  method: "PUT",
232
236
  body,
233
237
  hints: { 409: "The studio has newer changes. Run `aai pull` to fetch them, or `aai push --force` to overwrite." }
234
- });
238
+ }).then((res) => checkedResponse(res, (value) => isRecord(value) && typeof value.sourceHash === "string" && typeof value.created === "boolean", `the studio push route at ${serverUrl}`));
235
239
  }
236
240
  /** `POST /studio/projects/:project/deploy` — Publish, in the project's sandbox. */
237
241
  function publishStudioProject(serverUrl, apiKey, project, opts = {}) {
@@ -10,6 +10,15 @@ export declare const list: import("citty").CommandDef<{
10
10
  };
11
11
  }>;
12
12
  export declare const pull: import("citty").CommandDef<{
13
+ readonly server: {
14
+ readonly type: "string";
15
+ readonly alias: "s";
16
+ readonly description: "Platform server URL";
17
+ };
18
+ readonly json: {
19
+ readonly type: "boolean";
20
+ readonly description: "Output JSON (auto-detected in non-TTY)";
21
+ };
13
22
  readonly project: {
14
23
  readonly type: "positional";
15
24
  readonly description: "Studio project name (see `aai list`)";
@@ -25,6 +34,8 @@ export declare const pull: import("citty").CommandDef<{
25
34
  readonly alias: "f";
26
35
  readonly description: "Overwrite files in a non-empty directory";
27
36
  };
37
+ }>;
38
+ export declare const push: import("citty").CommandDef<{
28
39
  readonly server: {
29
40
  readonly type: "string";
30
41
  readonly alias: "s";
@@ -34,13 +45,13 @@ export declare const pull: import("citty").CommandDef<{
34
45
  readonly type: "boolean";
35
46
  readonly description: "Output JSON (auto-detected in non-TTY)";
36
47
  };
37
- }>;
38
- export declare const push: import("citty").CommandDef<{
39
48
  readonly force: {
40
49
  readonly type: "boolean";
41
50
  readonly alias: "f";
42
51
  readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
43
52
  };
53
+ }>;
54
+ export declare const publish: import("citty").CommandDef<{
44
55
  readonly server: {
45
56
  readonly type: "string";
46
57
  readonly alias: "s";
@@ -50,22 +61,11 @@ export declare const push: import("citty").CommandDef<{
50
61
  readonly type: "boolean";
51
62
  readonly description: "Output JSON (auto-detected in non-TTY)";
52
63
  };
53
- }>;
54
- export declare const publish: import("citty").CommandDef<{
55
64
  readonly force: {
56
65
  readonly type: "boolean";
57
66
  readonly alias: "f";
58
67
  readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
59
68
  };
60
- readonly server: {
61
- readonly type: "string";
62
- readonly alias: "s";
63
- readonly description: "Platform server URL";
64
- };
65
- readonly json: {
66
- readonly type: "boolean";
67
- readonly description: "Output JSON (auto-detected in non-TTY)";
68
- };
69
69
  readonly skipTypecheck: {
70
70
  readonly type: "boolean";
71
71
  readonly description: "Skip type checking before publishing";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { o as errorMessage$1 } from "./_utils-Dhd058D9.mjs";
3
- import { t as getMonorepoRoot } from "./_agent-Clbq3a4h.mjs";
2
+ import { g as writeJson, s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
3
+ import { t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
4
4
  import { existsSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import { isRecord } from "@alexkroman1/aai/utils";
@@ -106,7 +106,7 @@ async function layerScaffoldManifest(scaffoldDir, targetDir) {
106
106
  const [mine, theirs] = await Promise.all([readJsonFile(target), readJsonFile(path.join(scaffoldDir, "package.json"))]);
107
107
  if (!(mine && theirs)) return;
108
108
  const merged = mergeScaffoldManifest(mine, theirs);
109
- if (merged) await fs.writeFile(target, `${JSON.stringify(merged, null, 2)}\n`, "utf-8");
109
+ if (merged) await writeJson(target, merged);
110
110
  }
111
111
  /** Parse a JSON file, or null when it is missing or unparseable. */
112
112
  async function readJsonFile(file) {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as CliError } from "./_output-DbJt9gsl.mjs";
2
+ import { t as CliError } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log } from "./_ui-DCt4qZrk.mjs";
4
4
  import { typecheckProject } from "./typecheck.mjs";
5
5
  //#region src/_typecheck-gate.ts
@@ -9,7 +9,8 @@ import { typecheckProject } from "./typecheck.mjs";
9
9
  * bundlers strip types unchecked, so without this a type-broken agent
10
10
  * ships and misbehaves at runtime instead of failing here.
11
11
  */
12
- async function assertTypechecks(cwd) {
12
+ async function assertTypechecks(cwd, opts = {}) {
13
+ if (opts.skip) return;
13
14
  log.step("Type checking…");
14
15
  const result = await typecheckProject(cwd);
15
16
  if (!result.ok) throw new CliError("typecheck_failed", result.output, "Fix the type errors, or pass --skipTypecheck to build anyway");
@@ -4,4 +4,6 @@
4
4
  * bundlers strip types unchecked, so without this a type-broken agent
5
5
  * ships and misbehaves at runtime instead of failing here.
6
6
  */
7
- export declare function assertTypechecks(cwd: string): Promise<void>;
7
+ export declare function assertTypechecks(cwd: string, opts?: {
8
+ skip?: boolean | undefined;
9
+ }): Promise<void>;
@@ -115,5 +115,47 @@ async function writeJson(filePath, data, opts = {}) {
115
115
  throw err;
116
116
  }
117
117
  }
118
+ /**
119
+ * Code-unit ordering, never `localeCompare`.
120
+ *
121
+ * With no explicit locale `localeCompare` answers to the runtime's ICU default,
122
+ * so the same project would produce a differently-ordered artifact on a
123
+ * different machine — which is why the repo's own generated files sort this way.
124
+ * The reproducibility this protects is real in both callers: the scaffold's
125
+ * `pnpm-workspace.yaml` overrides, and the unrun-spec report.
126
+ */
127
+ function compareCodeUnits(a, b) {
128
+ if (a === b) return 0;
129
+ return a < b ? -1 : 1;
130
+ }
131
+ /**
132
+ * Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
133
+ *
134
+ * Here rather than beside its first caller because BOTH callers need it and
135
+ * `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
136
+ * importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
137
+ * took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
138
+ * only input either was written for.
139
+ */
140
+ function stripTrailingSlash(url) {
141
+ return url.replace(/\/+$/, "");
142
+ }
143
+ /**
144
+ * A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
145
+ *
146
+ * One definition because a user reads this wording from two different commands:
147
+ * the unrun-spec report (`aai test`) and the "no such project" hint
148
+ * (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
149
+ * `, and N more`, so the cap could drift between two messages one CLI prints.
150
+ *
151
+ * Here rather than in `_ui.ts` because this is a pure formatter and that module
152
+ * is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
153
+ * factory, which drops every export the factory does not list.
154
+ */
155
+ function formatCappedList(items, max = 10) {
156
+ const named = items.slice(0, max).join(", ");
157
+ const rest = items.length - max;
158
+ return rest > 0 ? `${named}, and ${rest} more` : named;
159
+ }
118
160
  //#endregion
119
- export { errorDetail as a, isEexist as c, resolveCwd as d, validateAgentExport as f, errorCode as i, readJson as l, binFromManifest as n, errorMessage$1 as o, writeJson as p, binFromPackageJson as r, fileExists as s, AGENT_ENTRY as t, readPackageJson as u };
161
+ export { errorCode as a, fileExists as c, readJson as d, readPackageJson as f, writeJson as g, validateAgentExport as h, compareCodeUnits as i, formatCappedList as l, stripTrailingSlash as m, binFromManifest as n, errorDetail as o, resolveCwd as p, binFromPackageJson as r, errorMessage$1 as s, AGENT_ENTRY as t, isEexist as u };
package/dist/_utils.d.ts CHANGED
@@ -69,3 +69,38 @@ export declare function readJson(filePath: string): Promise<unknown>;
69
69
  export declare function writeJson(filePath: string, data: unknown, opts?: {
70
70
  mode?: number;
71
71
  }): Promise<void>;
72
+ /**
73
+ * Code-unit ordering, never `localeCompare`.
74
+ *
75
+ * With no explicit locale `localeCompare` answers to the runtime's ICU default,
76
+ * so the same project would produce a differently-ordered artifact on a
77
+ * different machine — which is why the repo's own generated files sort this way.
78
+ * The reproducibility this protects is real in both callers: the scaffold's
79
+ * `pnpm-workspace.yaml` overrides, and the unrun-spec report.
80
+ */
81
+ export declare function compareCodeUnits(a: string, b: string): number;
82
+ /**
83
+ * Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
84
+ *
85
+ * Here rather than beside its first caller because BOTH callers need it and
86
+ * `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
87
+ * importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
88
+ * took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
89
+ * only input either was written for.
90
+ */
91
+ export declare function stripTrailingSlash(url: string): string;
92
+ /** How many names a capped list prints before it starts counting. */
93
+ export declare const MAX_NAMED_ITEMS = 10;
94
+ /**
95
+ * A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
96
+ *
97
+ * One definition because a user reads this wording from two different commands:
98
+ * the unrun-spec report (`aai test`) and the "no such project" hint
99
+ * (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
100
+ * `, and N more`, so the cap could drift between two messages one CLI prints.
101
+ *
102
+ * Here rather than in `_ui.ts` because this is a pure formatter and that module
103
+ * is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
104
+ * factory, which drops every export the factory does not list.
105
+ */
106
+ export declare function formatCappedList(items: readonly string[], max?: number): string;
@@ -24,12 +24,12 @@ import { readFile, readdir } from "node:fs/promises";
24
24
  * compiling every fence in this repo's shipped docs:
25
25
  *
26
26
  * ```ts
27
- * import type { WorkflowCtx } from "@alexkroman1/aai";
27
+ * import type { WorkflowContext } from "@alexkroman1/aai";
28
28
  *
29
29
  * declare const charge: () => Promise<string>;
30
30
  *
31
- * export async function body(ctx: WorkflowCtx, coin: string): Promise<void> {
32
- * // No error. The banned shape, against the real `WorkflowCtx`.
31
+ * export async function body(ctx: WorkflowContext, coin: string): Promise<void> {
32
+ * // No error. The banned shape, against the real `WorkflowContext`.
33
33
  * await ctx.step(`charge-${coin}`, charge);
34
34
  * }
35
35
  * ```
@@ -81,19 +81,6 @@ import { readFile, readdir } from "node:fs/promises";
81
81
  * @module
82
82
  */
83
83
  /**
84
- * The three `ctx` methods whose first argument is a journal identity.
85
- *
86
- * All three key a journal row by it — `name#occurrence` for a step,
87
- * `sleep!<label>#<n>` and `hook!<token>#<n>` for the waits — so a computed one
88
- * mints a row no earlier walk reached in exactly the same way. Kept as a list
89
- * so the message can name the method that was called.
90
- */
91
- const IDENTITY_METHODS = [
92
- "step",
93
- "sleep",
94
- "waitFor"
95
- ];
96
- /**
97
84
  * A `ctx.<method>(` immediately followed by a BACKTICK.
98
85
  *
99
86
  * The whole check, and its narrowness is the point. A quoted literal is
@@ -104,8 +91,17 @@ const IDENTITY_METHODS = [
104
91
  * The receiver is not pinned to `ctx` — a body may destructure or rename it —
105
92
  * and a `.step(` on anything else taking a template-literal first argument is
106
93
  * not a shape worth excluding.
94
+ *
95
+ * Shared rather than built per call, which is safe ONLY because the single
96
+ * reader is `matchAll`: that method species-constructs its own clone and never
97
+ * advances this object's `lastIndex`. A `.exec` or `.test` added against a `/g`
98
+ * regex would carry state between lines — build a fresh one there.
107
99
  */
108
- const identityPattern = () => new RegExp(`\\.(${IDENTITY_METHODS.join("|")})\\(\``, "g");
100
+ const IDENTITY_PATTERN = new RegExp(`\\.(${[
101
+ "step",
102
+ "sleep",
103
+ "waitFor"
104
+ ].join("|")})\\(\``, "g");
109
105
  /** Whether a template literal actually INTERPOLATES, or is merely quoted oddly. */
110
106
  const INTERPOLATES = /\$\{/;
111
107
  /**
@@ -132,7 +128,7 @@ function findComputedIdentities(source, file) {
132
128
  const findings = [];
133
129
  for (const [n, line] of source.split("\n").entries()) {
134
130
  if (isCommentOnly(line)) continue;
135
- for (const match of line.matchAll(identityPattern())) {
131
+ for (const match of line.matchAll(IDENTITY_PATTERN)) {
136
132
  const method = match[1];
137
133
  if (method === void 0) continue;
138
134
  if (!INTERPOLATES.test(line.slice((match.index ?? 0) + match[0].length))) continue;
@@ -169,17 +165,13 @@ async function workflowFiles(cwd) {
169
165
  * warning pass beside a typecheck that has already run on the same tree.
170
166
  */
171
167
  async function scanWorkflowDeterminism(cwd) {
172
- const findings = [];
173
- for (const rel of await workflowFiles(cwd)) {
174
- let source;
168
+ return (await Promise.all((await workflowFiles(cwd)).map(async (rel) => {
175
169
  try {
176
- source = await readFile(path.join(cwd, rel), "utf-8");
170
+ return findComputedIdentities(await readFile(path.join(cwd, rel), "utf-8"), rel);
177
171
  } catch {
178
- continue;
172
+ return [];
179
173
  }
180
- findings.push(...findComputedIdentities(source, rel));
181
- }
182
- return findings;
174
+ }))).flat();
183
175
  }
184
176
  /**
185
177
  * The remedy, once, however many findings there are.
@@ -20,12 +20,12 @@
20
20
  * compiling every fence in this repo's shipped docs:
21
21
  *
22
22
  * ```ts
23
- * import type { WorkflowCtx } from "@alexkroman1/aai";
23
+ * import type { WorkflowContext } from "@alexkroman1/aai";
24
24
  *
25
25
  * declare const charge: () => Promise<string>;
26
26
  *
27
- * export async function body(ctx: WorkflowCtx, coin: string): Promise<void> {
28
- * // No error. The banned shape, against the real `WorkflowCtx`.
27
+ * export async function body(ctx: WorkflowContext, coin: string): Promise<void> {
28
+ * // No error. The banned shape, against the real `WorkflowContext`.
29
29
  * await ctx.step(`charge-${coin}`, charge);
30
30
  * }
31
31
  * ```
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { a as ok, t as CliError } from "./_output-DbJt9gsl.mjs";
2
+ import { a as ok, t as CliError } from "./_output-DBP9Op_d.mjs";
3
3
  import { n as log, r as notify } from "./_ui-DCt4qZrk.mjs";
4
- import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-DEkjO1d8.mjs";
5
- import { assertTypechecks } from "./_typecheck-gate-sV4N5F93.mjs";
6
- import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-C9l8uC2R.mjs";
7
- import { TEST_FILES, classifyVitestError, runVitest } from "./test-guenJUxi.mjs";
4
+ import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-DolUCMxu.mjs";
5
+ import { assertTypechecks } from "./_typecheck-gate-U3jcil8L.mjs";
6
+ import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-CvpqMpdh.mjs";
7
+ import { TEST_FILES, classifyVitestError, runVitest } from "./test-D032V2cA.mjs";
8
8
  import path from "node:path";
9
9
  import fs from "node:fs/promises";
10
10
  import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
11
+ import { DEFAULT_SYSTEM_PROMPT } from "@alexkroman1/aai";
11
12
  //#region src/build.ts
12
13
  /**
13
14
  * `aai build` — bundle the agent without deploying, behind the same gates
@@ -32,6 +33,41 @@ import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
32
33
  */
33
34
  const WORKER_ARTIFACT_REL = path.join(".aai", "worker.mjs");
34
35
  /**
36
+ * The prose slot, named by convention. Mirrors `worker-bundler.ts`'s constant of
37
+ * the same name — the two cannot share one without an import cycle
38
+ * (`build.ts` → `_bundler.ts` → `worker-bundler.ts`), so `_build.test.ts` pins
39
+ * the pair end to end instead: a build whose prompt really came from the file
40
+ * has to report the file.
41
+ */
42
+ const SYSTEM_PROMPT_FILE = "system-prompt.md";
43
+ /** What {@link systemPromptSource} reports when nothing declared a prompt. */
44
+ const FRAMEWORK_DEFAULT_PROMPT_SOURCE = "the framework default (DEFAULT_SYSTEM_PROMPT)";
45
+ /**
46
+ * Which of the three possible authors of this agent's system prompt actually
47
+ * wrote the one in the bundle.
48
+ *
49
+ * `aai build` reported `name` and `workerBytes` and nothing about the prompt,
50
+ * and the prompt is the one field that can change COMPLETELY without any
51
+ * config changing: deleting `system-prompt.md` swaps in
52
+ * `DEFAULT_SYSTEM_PROMPT` — a total personality change — with exit 0 and no
53
+ * line anywhere. `withSystemPrompt` refuses the case where a file exists and
54
+ * nothing reads it; it cannot refuse a file that is simply GONE, because that
55
+ * is what an agent with no file legitimately looks like. So this is a report
56
+ * rather than a check.
57
+ *
58
+ * Decided by comparing VALUES against the built agent's resolved prompt, which
59
+ * is the same method `withSystemPrompt` uses and for the same reason — the
60
+ * alternative asks the bundler's module graph a question, and the author may
61
+ * have imported the file and composed it, which is neither "the file" nor
62
+ * "agent.ts" alone.
63
+ */
64
+ async function systemPromptSource(cwd, resolved) {
65
+ if (resolved === DEFAULT_SYSTEM_PROMPT) return FRAMEWORK_DEFAULT_PROMPT_SOURCE;
66
+ const trimmed = (await fs.readFile(path.join(cwd, SYSTEM_PROMPT_FILE), "utf-8").catch(() => void 0))?.trim();
67
+ if (trimmed === void 0 || trimmed === "" || !resolved.includes(trimmed)) return "agent.ts";
68
+ return resolved.trim() === trimmed ? SYSTEM_PROMPT_FILE : `agent.ts (composing ${SYSTEM_PROMPT_FILE})`;
69
+ }
70
+ /**
35
71
  * Map a {@link runVitest} failure to a CliError — distinguishing a real test
36
72
  * failure (test_failed) from the runner not spawning (spawn_failed) instead
37
73
  * of a generic command_failed.
@@ -50,7 +86,7 @@ async function executeBuild(opts) {
50
86
  } catch (err) {
51
87
  throw testGateError(err);
52
88
  }
53
- if (!opts.skipTypecheck) await assertTypechecks(cwd);
89
+ await assertTypechecks(cwd, { skip: opts.skipTypecheck });
54
90
  const bundle = await buildAgentBundle(cwd, { minify: true });
55
91
  const agentDef = await evalWorkerBundle(bundle.worker);
56
92
  for (const warning of agentConfigWarnings(agentDef)) notify("warn", warning);
@@ -58,11 +94,14 @@ async function executeBuild(opts) {
58
94
  const worker = path.join(cwd, WORKER_ARTIFACT_REL);
59
95
  await fs.mkdir(path.dirname(worker), { recursive: true });
60
96
  await fs.writeFile(worker, bundle.worker, "utf-8");
97
+ const systemPrompt = await systemPromptSource(cwd, agentDef.systemPrompt);
98
+ log.info(`System prompt: ${systemPrompt}`);
61
99
  log.success("Build complete");
62
100
  return ok({
63
101
  name: agentDef.name,
64
102
  workerBytes: bundle.worker.length,
65
- worker
103
+ worker,
104
+ systemPrompt
66
105
  });
67
106
  }
68
107
  //#endregion
package/dist/build.d.ts CHANGED
@@ -26,6 +26,11 @@ type BuildData = {
26
26
  workerBytes: number;
27
27
  /** Absolute path of the worker written — what `npm start` boots. */
28
28
  worker: string;
29
+ /**
30
+ * WHERE the prompt this bundle carries came from — see
31
+ * {@link systemPromptSource}.
32
+ */
33
+ systemPrompt: string;
29
34
  };
30
35
  export declare function executeBuild(opts: {
31
36
  cwd: string;
package/dist/cli.d.ts CHANGED
@@ -1,2 +1 @@
1
- import { type ArgsDef, type CommandDef } from "citty";
2
- export declare const mainCommand: CommandDef<ArgsDef>;
1
+ export declare const mainCommand: import("citty").CommandDef<import("citty").ArgsDef>;