@alexkroman1/aai-cli 8.2.0 → 9.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 (95) hide show
  1. package/dist/_dev-env.d.ts +15 -2
  2. package/dist/{_dev-server-Dt3TCjhj.mjs → _dev-server-BAR8Ya43.mjs} +182 -55
  3. package/dist/_dev-server.d.ts +5 -30
  4. package/dist/_dev-typecheck.d.ts +38 -0
  5. package/dist/_dev-vite-config.d.ts +74 -0
  6. package/dist/_e2e-test-utils.d.ts +33 -1
  7. package/dist/_init-BNotRgV1.mjs +163 -0
  8. package/dist/_resource-commands.d.ts +0 -1
  9. package/dist/{_slug-api-z-USxPtL.mjs → _slug-api--ZBmIGH-.mjs} +1 -1
  10. package/dist/{_studio-DR5yzZ1T.mjs → _studio-CKrsixd0.mjs} +2 -1
  11. package/dist/_studio.d.ts +3 -1
  12. package/dist/{build-BBxf8vom.mjs → build-DvDdSwAS.mjs} +1 -1
  13. package/dist/cli.mjs +26 -97
  14. package/dist/{delete-Dl_LSfvI.mjs → delete-8pjH8n8n.mjs} +3 -2
  15. package/dist/{deploy-DRC8jWr8.mjs → deploy-CjU3e-3P.mjs} +4 -4
  16. package/dist/{dev-QuuOOUtv.mjs → dev-dFzdvwa7.mjs} +3 -2
  17. package/dist/dev.d.ts +2 -0
  18. package/dist/{eval-DfQ8cm5p.mjs → eval-DwNwdqmS.mjs} +1 -1
  19. package/dist/{init-CUo87gcI.mjs → init-DK7GpIF9.mjs} +7 -6
  20. package/dist/{logs-CQ2T46dr.mjs → logs-D2Yhqe2w.mjs} +2 -2
  21. package/dist/scaffold/.env.example +7 -4
  22. package/dist/scaffold/CLAUDE.md +152 -97
  23. package/dist/scaffold/global.d.ts +12 -0
  24. package/dist/scaffold/package.json +4 -4
  25. package/dist/scaffold/server.mjs +49 -2
  26. package/dist/scaffold/tsconfig.json +1 -0
  27. package/dist/scaffold/vitest.config.ts +24 -0
  28. package/dist/{secret-Ct34kdgU.mjs → secret-CwgDLpSy.mjs} +2 -2
  29. package/dist/{studio-CzTzPqe1.mjs → studio-D3-HWIfk.mjs} +7 -5
  30. package/dist/templates/briefing-desk/agent.test.ts +3 -15
  31. package/dist/templates/briefing-desk/shared.ts +3 -3
  32. package/dist/templates/call-audit/agent.eval.test.ts +8 -1
  33. package/dist/templates/call-audit/agent.test.ts +0 -1
  34. package/dist/templates/call-audit/agent.ts +4 -3
  35. package/dist/templates/call-audit/client.tsx +4 -7
  36. package/dist/templates/call-audit/workflows/audit.ts +10 -1
  37. package/dist/templates/code-interpreter/agent.eval.test.ts +14 -43
  38. package/dist/templates/dispatch-center/agent.eval.test.ts +16 -26
  39. package/dist/templates/embedded-assets/agent.eval.test.ts +6 -16
  40. package/dist/templates/embedded-assets/agent.test.ts +3 -16
  41. package/dist/templates/health-assistant/agent.eval.test.ts +5 -15
  42. package/dist/templates/health-assistant/agent.test.ts +3 -14
  43. package/dist/templates/infocom-adventure/agent.eval.test.ts +6 -16
  44. package/dist/templates/infocom-adventure/agent.test.ts +3 -17
  45. package/dist/templates/link-digest/agent.test.ts +0 -14
  46. package/dist/templates/link-digest/agent.ts +9 -5
  47. package/dist/templates/link-digest/client.tsx +8 -6
  48. package/dist/templates/math-buddy/agent.eval.test.ts +11 -40
  49. package/dist/templates/night-owl/agent.eval.test.ts +15 -44
  50. package/dist/templates/night-owl/agent.test.ts +2 -15
  51. package/dist/templates/personal-finance/agent.eval.test.ts +10 -39
  52. package/dist/templates/pipeline-simple/agent.test.ts +73 -0
  53. package/dist/templates/pizza-ordering/agent.eval.test.ts +2 -17
  54. package/dist/templates/pizza-ordering/agent.test.ts +2 -17
  55. package/dist/templates/plan-and-execute/agent.eval.test.ts +2 -12
  56. package/dist/templates/plan-and-execute/agent.test.ts +3 -23
  57. package/dist/templates/plan-and-execute/shared.ts +1 -1
  58. package/dist/templates/podcast-digest/agent.test.ts +0 -1
  59. package/dist/templates/podcast-digest/client.tsx +3 -6
  60. package/dist/templates/podcast-digest/workflows/digest.ts +16 -8
  61. package/dist/templates/recap-workflow/agent.eval.test.ts +7 -14
  62. package/dist/templates/recap-workflow/agent.test.ts +3 -18
  63. package/dist/templates/recap-workflow/agent.ts +7 -5
  64. package/dist/templates/redline/agent.test.ts +0 -13
  65. package/dist/templates/redline/agent.ts +3 -2
  66. package/dist/templates/redline/client.tsx +9 -4
  67. package/dist/templates/research-workflow/agent.eval.test.ts +7 -14
  68. package/dist/templates/research-workflow/agent.test.ts +3 -15
  69. package/dist/templates/research-workflow/agent.ts +7 -5
  70. package/dist/templates/research-workflow/workflows/research.ts +8 -2
  71. package/dist/templates/retail/agent.eval.test.ts +10 -25
  72. package/dist/templates/retail/registry.test.ts +16 -13
  73. package/dist/templates/solo-rpg/agent.eval.test.ts +5 -15
  74. package/dist/templates/solo-rpg/agent.test.ts +17 -124
  75. package/dist/templates/solo-rpg/shared.ts +13 -61
  76. package/dist/templates/spoken-summary/agent.test.ts +0 -1
  77. package/dist/templates/spoken-summary/agent.ts +5 -4
  78. package/dist/templates/spoken-summary/client.tsx +4 -7
  79. package/dist/templates/support-line/agent.eval.test.ts +2 -12
  80. package/dist/templates/support-line/agent.test.ts +3 -22
  81. package/dist/templates/transcription-workflow/agent.ts +4 -4
  82. package/dist/templates/transcription-workflow/client.tsx +5 -5
  83. package/dist/templates/transcription-workflow/workflows/transcribe.ts +10 -1
  84. package/dist/templates/travel-concierge/agent.eval.test.ts +5 -15
  85. package/dist/templates/travel-concierge/agent.test.ts +3 -23
  86. package/dist/templates/web-researcher/agent.eval.test.ts +2 -5
  87. package/dist/test-CvwgeVSQ.mjs +181 -0
  88. package/dist/test.d.ts +33 -2
  89. package/package.json +4 -4
  90. package/dist/_init-BhEfQ6Yi.mjs +0 -93
  91. package/dist/storage-i_79ZwAR.mjs +0 -66
  92. package/dist/storage.d.ts +0 -22
  93. package/dist/templates/solo-rpg/tools/load_game.ts +0 -37
  94. package/dist/templates/solo-rpg/tools/save_game.ts +0 -32
  95. package/dist/test-BvyT6sAc.mjs +0 -104
@@ -1,93 +0,0 @@
1
- #!/usr/bin/env node
2
- import { c as isEexist, l as readJson, p as writeJson } from "./_utils-B8QmtFhK.mjs";
3
- import { r as isDevMode, t as getMonorepoRoot } from "./_agent-BTwZJu4P.mjs";
4
- import { REPO_URL, downloadAndMergeTemplate } from "./_templates-4WcKOjS5.mjs";
5
- import path from "node:path";
6
- import fs from "node:fs/promises";
7
- //#region _init.ts
8
- function readmeContent(slug) {
9
- return `# ${slug}
10
-
11
- A voice agent built with [aai](${REPO_URL}).
12
-
13
- ## Getting started
14
-
15
- \`\`\`sh
16
- npm install # Install dependencies
17
- aai dev # Run locally (opens browser)
18
- aai publish # Publish to production (and sync to the studio)
19
- \`\`\`
20
-
21
- ## Secrets
22
-
23
- Access secrets in your agent via \`ctx.env.MY_KEY\`.
24
-
25
- **Local development** — add secrets to \`.env\` (auto-loaded by \`aai dev\`):
26
-
27
- \`\`\`sh
28
- ALPHA_VANTAGE_KEY=sk-abc123
29
- MY_API_KEY=secret-value
30
- \`\`\`
31
-
32
- **Production** — set secrets on the server:
33
-
34
- \`\`\`sh
35
- aai secret put MY_KEY # Set a secret (prompts for value)
36
- aai secret list # List secret names
37
- aai secret delete MY_KEY # Remove a secret
38
- \`\`\`
39
-
40
- `;
41
- }
42
- /**
43
- * Map from npm package name to directory name under packages/.
44
- * Used to rewrite published version ranges to link: paths in dev mode.
45
- */
46
- const WORKSPACE_PKG_DIRS = {
47
- "@alexkroman1/aai": "aai",
48
- "@alexkroman1/aai-cli": "aai-cli",
49
- "@alexkroman1/aai-runtime": "aai-runtime",
50
- "@alexkroman1/aai-ui": "aai-ui"
51
- };
52
- /** Rewrite workspace deps to link: paths so pnpm links to local source. */
53
- async function patchPackageJsonForWorkspace(targetDir) {
54
- const pkgPath = path.join(targetDir, "package.json");
55
- const pkgJson = await readJson(pkgPath);
56
- if (!pkgJson) return;
57
- pkgJson.name = path.basename(targetDir);
58
- delete pkgJson.packageManager;
59
- const root = getMonorepoRoot();
60
- if (!root) return;
61
- const packagesDir = path.join(root, "packages");
62
- for (const field of ["dependencies", "devDependencies"]) {
63
- const deps = pkgJson[field];
64
- if (!deps) continue;
65
- for (const key of Object.keys(deps)) {
66
- const dir = WORKSPACE_PKG_DIRS[key];
67
- if (dir) deps[key] = `link:${path.relative(targetDir, path.join(packagesDir, dir))}`;
68
- }
69
- }
70
- await writeJson(pkgPath, pkgJson);
71
- }
72
- async function runInit(opts) {
73
- const { targetDir, template } = opts;
74
- await downloadAndMergeTemplate(template, targetDir);
75
- if (isDevMode()) {
76
- await patchPackageJsonForWorkspace(targetDir);
77
- try {
78
- await fs.unlink(path.join(targetDir, ".npmrc"));
79
- } catch {}
80
- }
81
- try {
82
- await fs.copyFile(path.join(targetDir, ".env.example"), path.join(targetDir, ".env"), fs.constants.COPYFILE_EXCL);
83
- } catch {}
84
- const readmePath = path.join(targetDir, "README.md");
85
- const slug = path.basename(path.resolve(targetDir));
86
- try {
87
- await fs.writeFile(readmePath, readmeContent(slug), { flag: "wx" });
88
- } catch (err) {
89
- if (!isEexist(err)) throw err;
90
- }
91
- }
92
- //#endregion
93
- export { runInit };
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
- import { n as log } from "./_ui-u7T4YooX.mjs";
4
- import { f as checkedResponse } from "./_studio-DR5yzZ1T.mjs";
5
- import { n as slugRequest } from "./_slug-api-z-USxPtL.mjs";
6
- import { isRecord } from "@alexkroman1/aai/utils";
7
- import * as p from "@clack/prompts";
8
- //#region storage.ts
9
- /**
10
- * The storage route's answer, CHECKED — every caller here reads `enabled` and
11
- * a body without it reported "Storage is disabled for <slug>" (and returned
12
- * `enabled: undefined` to a script) for a server that never said so. See
13
- * `checkedResponse`.
14
- */
15
- async function storageRequest(cwd, init, server) {
16
- const { data, slug } = await slugRequest(cwd, "/storage", {
17
- ...init,
18
- action: "storage"
19
- }, server);
20
- return {
21
- enabled: checkedResponse(data, (value) => isRecord(value) && typeof value.enabled === "boolean", `the storage route for ${slug}`).enabled,
22
- slug
23
- };
24
- }
25
- async function executeStorageStatus(cwd, server) {
26
- const { enabled, slug } = await storageRequest(cwd, void 0, server);
27
- if (enabled) log.info(`Storage is enabled for ${slug}`);
28
- else log.info(`Storage is disabled for ${slug}. Use \`aai storage enable\` to turn it on.`);
29
- return ok({
30
- slug,
31
- enabled
32
- });
33
- }
34
- async function executeStorageEnable(cwd, server) {
35
- const { enabled, slug } = await storageRequest(cwd, { method: "POST" }, server);
36
- log.success(`Storage enabled for ${slug}`);
37
- log.info("Tool code can now use ctx.db.query(sql, params).");
38
- return ok({
39
- slug,
40
- enabled
41
- });
42
- }
43
- /**
44
- * Disable storage — destructive: the server DROPS the app's database schema
45
- * and all its data. On a TTY this requires interactive confirmation; without
46
- * a TTY it refuses unless `--force` is passed, so a script can never drop
47
- * data by accident.
48
- */
49
- async function executeStorageDisable(cwd, opts = {}) {
50
- if (!opts.force) {
51
- if (!(opts.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) return fail("confirmation_required", "Disabling storage drops the app's database schema and ALL its data.", "Re-run with --force to disable storage without confirmation.");
52
- const confirmed = await p.confirm({ message: "Disable storage? This DROPS the app's database schema and all its data." });
53
- if (p.isCancel(confirmed) || confirmed !== true) {
54
- log.info("Cancelled. Storage was left unchanged.");
55
- return fail("cancelled", "Disable cancelled");
56
- }
57
- }
58
- const { enabled, slug } = await storageRequest(cwd, { method: "DELETE" }, opts.server);
59
- log.success(`Storage disabled for ${slug} — database schema and data dropped`);
60
- return ok({
61
- slug,
62
- enabled
63
- });
64
- }
65
- //#endregion
66
- export { executeStorageDisable, executeStorageEnable, executeStorageStatus };
package/dist/storage.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { type CommandResult } from "./_output.ts";
2
- type StorageStatusData = {
3
- slug: string;
4
- enabled: boolean;
5
- };
6
- export declare function executeStorageStatus(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
7
- export declare function executeStorageEnable(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
8
- export type StorageDisableOpts = {
9
- server?: string | undefined;
10
- /** Skip the confirmation prompt (required in non-interactive runs). */
11
- force?: boolean | undefined;
12
- /** TTY override for testing. Defaults to real stdin+stdout TTY state. */
13
- isTTY?: boolean | undefined;
14
- };
15
- /**
16
- * Disable storage — destructive: the server DROPS the app's database schema
17
- * and all its data. On a TTY this requires interactive confirmation; without
18
- * a TTY it refuses unless `--force` is passed, so a script can never drop
19
- * data by accident.
20
- */
21
- export declare function executeStorageDisable(cwd: string, opts?: StorageDisableOpts): Promise<CommandResult<StorageStatusData>>;
22
- export {};
@@ -1,37 +0,0 @@
1
- import { tool } from "@alexkroman1/aai";
2
- import { z } from "zod";
3
- import type { GameState } from "../shared.ts";
4
- import { gameSlot, loadState, saveSlotKey, saveSlotParam, storyFlow } from "../shared.ts";
5
-
6
- // Requires storage — `aai storage enable` (or DATABASE_URL in .env under
7
- // `aai dev`); the rest of the game works without it.
8
- export default tool({
9
- description: "Load a previously saved game.",
10
- inputSchema: z.object({ slot: saveSlotParam }),
11
- async execute(args, ctx) {
12
- const saved = await loadState<GameState>(ctx, saveSlotKey(args.slot));
13
- if (!saved) return { error: "No save found." };
14
- gameSlot.set(ctx, saved);
15
-
16
- // The flow has to be restored alongside the campaign, or a loaded game
17
- // would still be sitting in `awaitingSetup` with every roll tool refusing.
18
- // A save is written from a settled scene, so it resumes at `awaitingRoll`
19
- // rather than with a standing roll — `lastRoll` may be set, but a burn
20
- // window that survived a save and a reload is not one a player is still in.
21
- storyFlow.reset(ctx);
22
- let at = storyFlow.send(ctx, { type: "SETUP" });
23
- if (saved.gameOver) at = storyFlow.send(ctx, { type: "DOWNED" });
24
-
25
- return {
26
- loaded: true,
27
- // The position spread verbatim — the same keys every gated tool reports.
28
- ...at,
29
- playerName: saved.playerName,
30
- characterConcept: saved.characterConcept,
31
- settingGenre: saved.settingGenre,
32
- sceneCount: saved.sceneCount,
33
- currentLocation: saved.currentLocation,
34
- initialized: saved.initialized,
35
- };
36
- },
37
- });
@@ -1,32 +0,0 @@
1
- import { z } from "zod";
2
- import { gameSlot, saveSlotKey, saveSlotParam, saveState, storyFlow } from "../shared.ts";
3
-
4
- // Requires storage — `aai storage enable` (or DATABASE_URL in .env under
5
- // `aai dev`); the rest of the game works without it.
6
- //
7
- /**
8
- * Gated on `playing` or `gameOver` — i.e. anything but `awaitingSetup`.
9
- *
10
- * Saving before a character exists writes an empty campaign under a slot name,
11
- * and `load_game` would then cheerfully restore it over a real game. That is a
12
- * position, so it is a `when`.
13
- *
14
- * The body AWAITS, which a flow tool allows: only `slot.updateTool` must be
15
- * synchronous, because its draft is stored when it returns. What a save needs is
16
- * the frozen value, and `gameSlot.get` is what hands it over.
17
- */
18
- export default storyFlow.tool({
19
- description: "Save current game to persistent storage.",
20
- inputSchema: z.object({ slot: saveSlotParam }),
21
- when: ["playing", "gameOver"],
22
- async execute(args, ctx) {
23
- const game = gameSlot.get(ctx);
24
- await saveState(ctx, saveSlotKey(args.slot), game);
25
- return {
26
- saved: true,
27
- slot: args.slot ?? "autosave",
28
- name: game.playerName,
29
- scene: game.sceneCount,
30
- };
31
- },
32
- });
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
3
- import { n as log } from "./_ui-u7T4YooX.mjs";
4
- import { i as errorCode, o as errorMessage$1, r as binFromPackageJson } from "./_utils-B8QmtFhK.mjs";
5
- import { createRequire } from "node:module";
6
- import { existsSync } from "node:fs";
7
- import path from "node:path";
8
- import { omitUndefined } from "@alexkroman1/aai/utils";
9
- import { execaSync } from "execa";
10
- //#region test.ts
11
- /**
12
- * `aai test` — run agent tests via vitest.
13
- */
14
- /**
15
- * Resolve the agent project's own vitest binary so tests run without the
16
- * npx resolution overhead (and its potential network fetch of vitest).
17
- *
18
- * Resolves `vitest/package.json` from the agent directory, derives the bin
19
- * script, and runs it with the current Node executable. Falls back to
20
- * `npx vitest` only when no local install is resolvable.
21
- */
22
- function resolveVitestCommand(cwd, resolve = createRequire(path.join(cwd, "package.json")).resolve) {
23
- try {
24
- const bin = binFromPackageJson(resolve("vitest/package.json"), "vitest");
25
- if (bin) return {
26
- cmd: process.execPath,
27
- args: [bin]
28
- };
29
- } catch {}
30
- return {
31
- cmd: "npx",
32
- args: ["vitest"]
33
- };
34
- }
35
- /** The files `aai test` runs, in preference order. */
36
- const TEST_FILES = ["agent.test.ts", "agent.test.js"];
37
- /**
38
- * Run vitest over one of `candidates` in the given project directory.
39
- *
40
- * Returns `true` if it ran, `false` if none of the candidate files exists.
41
- * Throws on failure.
42
- *
43
- * A vitest FILTER, not an include glob: the argument is matched as a substring
44
- * against the paths vitest's own include globs already found, which is why the
45
- * candidates are named `*.test.ts` — `agent.test.ts` cannot match
46
- * `agent.eval.test.ts` and vice versa, so the two commands stay disjoint
47
- * without either one having to exclude the other's file.
48
- */
49
- function runVitest(cwd, opts = { candidates: TEST_FILES }) {
50
- const testFile = opts.candidates.find((name) => existsSync(path.join(cwd, name)));
51
- if (!testFile) return false;
52
- const { cmd, args } = resolveVitestCommand(cwd);
53
- execaSync(cmd, [
54
- ...args,
55
- "run",
56
- "--root",
57
- ".",
58
- ...opts.extraArgs ?? [],
59
- testFile
60
- ], {
61
- cwd,
62
- stdio: "inherit",
63
- ...omitUndefined({ env: opts.env ? {
64
- ...process.env,
65
- ...opts.env
66
- } : void 0 })
67
- });
68
- return true;
69
- }
70
- /**
71
- * Classify a {@link runVitest} failure. execaSync throws an ENOENT-coded
72
- * error when the binary itself couldn't be spawned (infrastructure problem)
73
- * and an exit-code error when vitest ran and the tests failed.
74
- */
75
- function classifyVitestError(err, label = "Tests") {
76
- if (errorCode(err) === "ENOENT") return {
77
- code: "spawn_failed",
78
- message: `Could not launch the test runner: ${errorMessage$1(err)} — is the binary on your PATH?`
79
- };
80
- return {
81
- code: "test_failed",
82
- message: `${label} failed: ${errorMessage$1(err)}`
83
- };
84
- }
85
- /** Execute agent tests and return structured result. */
86
- async function executeTest(cwd) {
87
- log.step("Running agent tests");
88
- try {
89
- if (!runVitest(cwd)) {
90
- log.info("No test file found. Create agent.test.ts to add tests.");
91
- return ok({
92
- passed: true,
93
- skipped: true
94
- });
95
- }
96
- log.success("Tests passed");
97
- return ok({ passed: true });
98
- } catch (err) {
99
- const { code, message } = classifyVitestError(err);
100
- return fail(code, message);
101
- }
102
- }
103
- //#endregion
104
- export { classifyVitestError, executeTest, runVitest };