@duyquangnvx/agent-cli 0.27.0 → 0.28.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 (124) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/agent-cli.js +9 -2
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/built-ins/agents-md.d.ts +3 -2
  5. package/dist/built-ins/agents-md.js +8 -9
  6. package/dist/built-ins/agents-md.js.map +1 -1
  7. package/dist/built-ins/commands.d.ts +2 -1
  8. package/dist/built-ins/commands.js.map +1 -1
  9. package/dist/built-ins/doctor-rows.js +1 -1
  10. package/dist/built-ins/doctor-rows.js.map +1 -1
  11. package/dist/built-ins/doctor.d.ts +0 -1
  12. package/dist/built-ins/doctor.js +21 -8
  13. package/dist/built-ins/doctor.js.map +1 -1
  14. package/dist/built-ins/install/dependency.js +23 -23
  15. package/dist/built-ins/install/dependency.js.map +1 -1
  16. package/dist/built-ins/install/global-npm.d.ts +6 -3
  17. package/dist/built-ins/install/global-npm.js +19 -14
  18. package/dist/built-ins/install/global-npm.js.map +1 -1
  19. package/dist/built-ins/install/global.js +6 -7
  20. package/dist/built-ins/install/global.js.map +1 -1
  21. package/dist/built-ins/install/install.d.ts +0 -3
  22. package/dist/built-ins/install/install.js +0 -1
  23. package/dist/built-ins/install/install.js.map +1 -1
  24. package/dist/built-ins/install/release-check.js +10 -17
  25. package/dist/built-ins/install/release-check.js.map +1 -1
  26. package/dist/built-ins/npm.d.ts +8 -7
  27. package/dist/built-ins/npm.js +20 -12
  28. package/dist/built-ins/npm.js.map +1 -1
  29. package/dist/built-ins/project-root.d.ts +4 -4
  30. package/dist/built-ins/project-root.js +12 -22
  31. package/dist/built-ins/project-root.js.map +1 -1
  32. package/dist/built-ins/read.d.ts +5 -0
  33. package/dist/built-ins/read.js +29 -0
  34. package/dist/built-ins/read.js.map +1 -0
  35. package/dist/built-ins/setup.d.ts +1 -2
  36. package/dist/built-ins/setup.js +32 -26
  37. package/dist/built-ins/setup.js.map +1 -1
  38. package/dist/built-ins/skill/home.d.ts +2 -1
  39. package/dist/built-ins/skill/home.js +19 -17
  40. package/dist/built-ins/skill/home.js.map +1 -1
  41. package/dist/built-ins/skill/install-command.d.ts +2 -1
  42. package/dist/built-ins/skill/install-command.js +13 -11
  43. package/dist/built-ins/skill/install-command.js.map +1 -1
  44. package/dist/built-ins/skill/project.js +6 -7
  45. package/dist/built-ins/skill/project.js.map +1 -1
  46. package/dist/built-ins/skill/sync.d.ts +4 -3
  47. package/dist/built-ins/skill/sync.js +24 -17
  48. package/dist/built-ins/skill/sync.js.map +1 -1
  49. package/dist/built-ins/update.d.ts +1 -1
  50. package/dist/built-ins/update.js +9 -7
  51. package/dist/built-ins/update.js.map +1 -1
  52. package/dist/built-ins/upgrades.d.ts +2 -1
  53. package/dist/built-ins/upgrades.js +5 -12
  54. package/dist/built-ins/upgrades.js.map +1 -1
  55. package/dist/check-examples.js +5 -23
  56. package/dist/check-examples.js.map +1 -1
  57. package/dist/check-next-steps.d.ts +3 -9
  58. package/dist/check-next-steps.js +20 -32
  59. package/dist/check-next-steps.js.map +1 -1
  60. package/dist/command-line/line-problem.d.ts +14 -0
  61. package/dist/command-line/line-problem.js +41 -0
  62. package/dist/command-line/line-problem.js.map +1 -0
  63. package/dist/context/files.d.ts +3 -0
  64. package/dist/context/files.js +17 -2
  65. package/dist/context/files.js.map +1 -1
  66. package/dist/context/outputs.d.ts +13 -24
  67. package/dist/context/outputs.js +37 -37
  68. package/dist/context/outputs.js.map +1 -1
  69. package/dist/context/spawn.js +3 -1
  70. package/dist/context/spawn.js.map +1 -1
  71. package/dist/context/tools.d.ts +9 -4
  72. package/dist/context/tools.js +12 -12
  73. package/dist/context/tools.js.map +1 -1
  74. package/dist/context.d.ts +3 -1
  75. package/dist/define-command.d.ts +19 -12
  76. package/dist/define-command.js +5 -3
  77. package/dist/define-command.js.map +1 -1
  78. package/dist/define.d.ts +10 -3
  79. package/dist/define.js.map +1 -1
  80. package/dist/errors.d.ts +3 -6
  81. package/dist/errors.js +17 -28
  82. package/dist/errors.js.map +1 -1
  83. package/dist/execute.d.ts +7 -16
  84. package/dist/execute.js +11 -14
  85. package/dist/execute.js.map +1 -1
  86. package/dist/exit-classes.d.ts +7 -10
  87. package/dist/exit-classes.js +20 -81
  88. package/dist/exit-classes.js.map +1 -1
  89. package/dist/failure.d.ts +36 -0
  90. package/dist/failure.js +65 -0
  91. package/dist/failure.js.map +1 -0
  92. package/dist/help-json.js +5 -3
  93. package/dist/help-json.js.map +1 -1
  94. package/dist/help.js +3 -3
  95. package/dist/help.js.map +1 -1
  96. package/dist/output.d.ts +3 -10
  97. package/dist/output.js +14 -24
  98. package/dist/output.js.map +1 -1
  99. package/dist/run-cli.js +7 -5
  100. package/dist/run-cli.js.map +1 -1
  101. package/dist/run-command.d.ts +6 -1
  102. package/dist/run-command.js +31 -158
  103. package/dist/run-command.js.map +1 -1
  104. package/dist/run-options.d.ts +5 -2
  105. package/dist/run-scope.d.ts +35 -0
  106. package/dist/run-scope.js +138 -0
  107. package/dist/run-scope.js.map +1 -0
  108. package/dist/runtime-codes.d.ts +140 -1
  109. package/dist/runtime-codes.js +60 -4
  110. package/dist/runtime-codes.js.map +1 -1
  111. package/docs/example/cli.test.ts +9 -2
  112. package/docs/example/cli.ts +1 -0
  113. package/docs/example/folder.ts +1 -0
  114. package/docs/guides/commands.md +1 -1
  115. package/docs/guides/doctor.md +2 -1
  116. package/docs/guides/errors.md +1 -1
  117. package/docs/guides/testing.md +2 -1
  118. package/package.json +1 -1
  119. package/dist/command-errors.d.ts +0 -8
  120. package/dist/command-errors.js +0 -25
  121. package/dist/command-errors.js.map +0 -1
  122. package/dist/command-line/route-line.d.ts +0 -15
  123. package/dist/command-line/route-line.js +0 -18
  124. package/dist/command-line/route-line.js.map +0 -1
@@ -1,7 +1,3 @@
1
- import { DOCTOR_PROBLEMS } from "./built-ins/doctor.js";
2
- import { NOT_GLOBAL_INSTALL } from "./built-ins/install/global-npm.js";
3
- import { INSTALL_FAILED, NO_LATEST, NPM_FAILED, REGISTRY_AUTH, REGISTRY_UNREACHABLE, } from "./built-ins/npm.js";
4
- import { NOT_ON_PATH } from "./built-ins/setup.js";
5
1
  import { INVALID_INPUT, UNKNOWN_COMMAND, UNKNOWN_OPTION } from "./command-line/usage.js";
6
2
  import { CONFIRMATION_MISMATCH, CONFIRMATION_REQUIRED } from "./context/confirm.js";
7
3
  import { NOT_CONFIRMED } from "./context/cost.js";
@@ -16,6 +12,55 @@ import { STDIN_STALLED } from "./context/stdin.js";
16
12
  import { TOOL_MISSING } from "./context/tools.js";
17
13
  import { INTERNAL } from "./errors.js";
18
14
  import { UNKNOWN_FIELD } from "./list.js";
15
+ export const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
16
+ export const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
17
+ export const NOT_ON_PATH = "NOT_ON_PATH";
18
+ export const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
19
+ export const REGISTRY_AUTH = "REGISTRY_AUTH";
20
+ export const INSTALL_FAILED = "INSTALL_FAILED";
21
+ export const NPM_FAILED = "NPM_FAILED";
22
+ export const NO_LATEST = "NO_LATEST";
23
+ /** The Runtime codes any Command can meet, which no Command declares. */
24
+ const anyCommandErrors = {
25
+ [INVALID_INPUT]: { class: "caller" },
26
+ [UNKNOWN_OPTION]: { class: "caller" },
27
+ [UNKNOWN_COMMAND]: { class: "caller" },
28
+ [UNKNOWN_FIELD]: { class: "caller" },
29
+ [CONFIRMATION_REQUIRED]: { class: "caller" },
30
+ [CONFIRMATION_MISMATCH]: { class: "caller" },
31
+ [NOT_CONFIRMED]: { class: "operator" },
32
+ [NEEDS_OPERATOR]: { class: "operator" },
33
+ [OUTPUT_EXISTS]: { class: "caller" },
34
+ [STDIN_STALLED]: { class: "caller" },
35
+ [INTERRUPTED]: { class: "interrupted" },
36
+ [DEADLINE_EXCEEDED]: { class: "wait" },
37
+ [INTERNAL]: { class: "bug" },
38
+ [PERMISSION_DENIED]: { class: "operator" },
39
+ [DISK_FULL]: { class: "operator" },
40
+ [NOT_FOUND]: { class: "caller" },
41
+ [ALREADY_EXISTS]: { class: "caller" },
42
+ [NOT_A_FILE]: { class: "caller" },
43
+ [TOOL_FAILED]: { class: "operator" },
44
+ [TOOL_MISSING]: { class: "operator" },
45
+ [SECRET_MISSING]: { class: "operator" },
46
+ [PROJECT_INVALID]: { class: "operator" },
47
+ };
48
+ /**
49
+ * The Runtime codes only some Commands reach: each Command that throws one, Built-in or the CLI's
50
+ * own, declares it in `errors`, as it does a code of the CLI's Error table.
51
+ */
52
+ export const declaredRuntimeErrors = {
53
+ [DOCTOR_PROBLEMS]: { class: "operator" },
54
+ [NOT_GLOBAL_INSTALL]: { class: "operator" },
55
+ [NOT_ON_PATH]: { class: "operator" },
56
+ [REGISTRY_UNREACHABLE]: { class: "operator" },
57
+ [REGISTRY_AUTH]: { class: "operator" },
58
+ [INSTALL_FAILED]: { class: "operator" },
59
+ [NPM_FAILED]: { class: "operator" },
60
+ [NO_LATEST]: { class: "source" },
61
+ };
62
+ /** The Runtime's own Error table: every code it fails a run with, and the class that must act. */
63
+ export const runtimeErrors = { ...anyCommandErrors, ...declaredRuntimeErrors };
19
64
  /** Every code the Runtime fails a run with, keyed by itself, to catch or assert one. */
20
65
  export const runtimeCodes = {
21
66
  INVALID_INPUT,
@@ -49,4 +94,15 @@ export const runtimeCodes = {
49
94
  NPM_FAILED,
50
95
  NO_LATEST,
51
96
  };
97
+ export const anyCommandClasses = Object.values(anyCommandErrors).map((declaration) => declaration.class);
98
+ export function isRuntimeCode(code) {
99
+ return Object.hasOwn(runtimeErrors, code);
100
+ }
101
+ export function isDeclaredRuntimeCode(code) {
102
+ return Object.hasOwn(declaredRuntimeErrors, code);
103
+ }
104
+ /** Whether a Command or `doctor` row throwing `code` must declare it: a code of `table`, or a Runtime one only some reach. */
105
+ export function mustDeclare(code, table) {
106
+ return Object.hasOwn(table, code) || isDeclaredRuntimeCode(code);
107
+ }
52
108
  //# sourceMappingURL=runtime-codes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-codes.js","sourceRoot":"","sources":["../src/runtime-codes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,cAAc,EACd,SAAS,EACT,UAAU,EACV,aAAa,EACb,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACL,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,wFAAwF;AACxF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,aAAa;IACb,cAAc;IACd,eAAe;IACf,aAAa;IACb,qBAAqB;IACrB,qBAAqB;IACrB,aAAa;IACb,cAAc;IACd,aAAa;IACb,aAAa;IACb,WAAW;IACX,iBAAiB;IACjB,QAAQ;IACR,iBAAiB;IACjB,SAAS;IACT,SAAS;IACT,cAAc;IACd,UAAU;IACV,WAAW;IACX,YAAY;IACZ,cAAc;IACd,eAAe;IACf,eAAe;IACf,kBAAkB;IAClB,WAAW;IACX,oBAAoB;IACpB,aAAa;IACb,cAAc;IACd,UAAU;IACV,SAAS;CACD,CAAC"}
1
+ {"version":3,"file":"runtime-codes.js","sourceRoot":"","sources":["../src/runtime-codes.ts"],"names":[],"mappings":"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"}
@@ -241,8 +241,8 @@ describe("links check", () => {
241
241
  });
242
242
 
243
243
  describe("links doctor", () => {
244
- async function doctor(env: Record<string, string>, config?: unknown) {
245
- const cwd = await folderWith(LINKS);
244
+ async function doctor(env: Record<string, string>, config?: unknown, links: unknown = LINKS) {
245
+ const cwd = await folderWith(links);
246
246
  if (config !== undefined) {
247
247
  await writeFile(join(cwd, "links.config.json"), JSON.stringify(config));
248
248
  }
@@ -268,6 +268,13 @@ describe("links doctor", () => {
268
268
  });
269
269
  });
270
270
 
271
+ test("a corrupt links.json fails doctor in the store row as CORRUPT_DATA, exit 1", async () => {
272
+ const { result, row } = await doctor({ PATH: await pathWith("git") }, undefined, "not links");
273
+
274
+ expect(row("store")).toMatchObject({ state: "problem", code: "CORRUPT_DATA" });
275
+ expect(result.exitCode).toBe(1);
276
+ });
277
+
271
278
  test("the tag row reads the Project file's tag", async () => {
272
279
  const { row } = await doctor({ PATH: await pathWith("git") }, { tag: "work" });
273
280
 
@@ -148,6 +148,7 @@ const check = defineCommand({
148
148
 
149
149
  const store: DoctorCheck<Ports, Settings> = {
150
150
  name: "store",
151
+ errors: ["CORRUPT_DATA"],
151
152
  run: async (ctx) => {
152
153
  const links = await readLinks(ctx);
153
154
  return { state: "ok", message: `${linksPath(ctx.cwd)} holds ${links.length} links` };
@@ -39,6 +39,7 @@ export function fakeFiles(overrides: Partial<Files>): Files {
39
39
  remove: async () => {},
40
40
  mkdir: async () => {},
41
41
  stat: async () => undefined,
42
+ realPath: async () => undefined,
42
43
  ...overrides,
43
44
  };
44
45
  }
@@ -26,7 +26,7 @@ Data over 30,000 characters spills to a file, so make a list that may grow a Lis
26
26
 
27
27
  Reach the outside through `ctx`, so tests control it:
28
28
 
29
- - the working directory and environment as `ctx.cwd` and `ctx.env`;
29
+ - the working directory, environment and platform as `ctx.cwd`, `ctx.env` and `ctx.platform`, which `runCli`'s `platform` replaces in a test;
30
30
  - the file system through `ctx.files`, whose failures name the path; a `node:fs` failure is a bug (exit 70). Pass `mode` to its `write` or `mkdir` for a file or folder holding tokens. Catch a file code from `runtimeCodes` to say what to do instead, as [`example/bundle.ts`](../example/bundle.ts) turns `ALREADY_EXISTS` into its own `BUNDLE_EXISTS`;
31
31
  - a key or token through `ctx.secret(name)`, for a name in the Command's `secrets`. The Runtime reads each declared Secret from the environment, then the CLI's `.env` at `ctx.envFile` (`~/.config/<name>/.env`, or under `$XDG_CONFIG_HOME`), an empty value counting as unset, and fails the run with `SECRET_MISSING` before any work, `--dry-run` included, when one is unset; so `ctx.secret` returns a `Secret`, never `undefined`. The exception is a key that only replaces something the CLI already has, such as a token that overrides a saved login: declared `optional: true`, its absence fails nothing and `ctx.secret` may give `undefined`, while help still names it, so an agent learns the key exists. Messages name its `origin` and keep its `value` out of the agent's context. `doctor` reports every declared Secret it needs, and each optional one that is set; a key it should check that no Command requires goes in `defineCli({ secrets })`;
32
32
  - a program through `ctx.spawn`, listed in the Command's `tools`, so a missing one fails before any work and `doctor` checks it. Its `stdin` option feeds the program, from an async iterable for input too large to hold, and its `onLine` option receives each line the program writes as it comes;
@@ -5,6 +5,7 @@ Every multi-Command CLI has a built-in `doctor` that reports one row per check:
5
5
  ```ts
6
6
  const store: DoctorCheck<Ports, Settings> = {
7
7
  name: "store",
8
+ errors: ["CORRUPT_DATA"],
8
9
  run: async (ctx) => {
9
10
  const links = await readLinks(ctx);
10
11
  return { state: "ok", message: `${linksPath(ctx.cwd)} holds ${links.length} links` };
@@ -36,7 +37,7 @@ A row returns one of:
36
37
  - `{ state: "problem", message, fix?, code? }`, where `fix` is the command that fixes the problem, which `doctor` gives as a Next step before its own rerun, and `code` is one of the Error table's or the Runtime's;
37
38
  - `{ state: "skipped", message }` for a row not checked, such as "not checked: the bridge is down", so one cause shows up as one problem. It never fails `doctor`.
38
39
 
39
- When any row is a problem, `doctor` fails with one error per problem row, its `code` or `DOCTOR_PROBLEMS` (exit 2) without one, so it exits by the class that must act first as any Command does: an expired session 3, a network failure 75. Give a problem the code of its real cause. A row that throws becomes its own problem, with a `CliError`'s message and hint as the message, its first Next step other than `ctx.rerun()` as `fix`, and its code as `code`; the failure ends with `doctor` as typed, which a rerun in the row's hint means. A row whose `ctx.spawn` finds no declared tool on `PATH` is `skipped`, since that tool's row reports it. Each row runs under its own deadline, 10 s unless it sets `deadlineMs`, and `ctx.signal` aborts at that deadline, so pass it on. A row with `advisory: true` shows its problem and `fix` but never fails `doctor`; when it has no `fix`, `next` ends with `doctor` as typed.
40
+ When any row is a problem, `doctor` fails with one error per problem row, its `code` or `DOCTOR_PROBLEMS` (exit 2) without one, so it exits by the class that must act first as any Command does: an expired session 3, a network failure 75. Give a problem the code of its real cause, and list in the row's `errors` each code of the Error table, and each Runtime code only some Commands reach, that it fails with, thrown or returned, as a Command does: the `store` row above declares `CORRUPT_DATA`, which `readLinks` throws. A row failing with a code it does not declare fails `doctor` as a bug (exit 70), even when another row declares it. `doctor`'s own `errors`, which `--help --json` lists, are `DOCTOR_PROBLEMS` and the codes of every row that is not advisory. A row that throws becomes its own problem, with a `CliError`'s message and hint as the message, its first Next step other than `ctx.rerun()` as `fix`, and its code as `code`; the failure ends with `doctor` as typed, which a rerun in the row's hint means. A row whose `ctx.spawn` finds no declared tool on `PATH` is `skipped`, since that tool's row reports it. Each row runs under its own deadline, 10 s unless it sets `deadlineMs`, and `ctx.signal` aborts at that deadline, so pass it on. A row with `advisory: true` shows its problem and `fix` but never fails `doctor`; when it has no `fix`, `next` ends with `doctor` as typed.
40
41
 
41
42
  The Runtime's `secrets` row checks every Secret the CLI and its Commands declare, naming where each came from and never its value, so no row of yours reads a key. An optional Secret appears only when set, unless some Command or the CLI needs the same name.
42
43
 
@@ -21,7 +21,7 @@ export const errors = defineErrors({
21
21
 
22
22
  When a `details` field is worth reading in text mode too, such as a stack, name it in the code's `print` rather than repeating it in the message, which `--json` would then carry twice.
23
23
 
24
- Each Command lists in `errors` the table codes it throws, its helpers' and ports' included, so a shared store's `CORRUPT_DATA` goes on every Command that reads the store. A run ending in a table code its Command does not declare, directly or as an `AggregateError` leaf, fails as a bug (exit 70) naming the code and the Command, so the Command's tests catch a missing one; `defineCli` throws on a code the table lacks. Runtime codes are never listed.
24
+ Each Command lists in `errors` the table codes it throws, its helpers' and ports' included, so a shared store's `CORRUPT_DATA` goes on every Command that reads the store. A run ending in a table code its Command does not declare, directly or as an `AggregateError` leaf, fails as a bug (exit 70) naming the code and the Command, so the Command's tests catch a missing one; `defineCli` throws on a code the table lacks. A Runtime code any Command can meet, such as `NOT_FOUND` or `TOOL_MISSING`, is never listed; one only some Commands reach (`NOT_GLOBAL_INSTALL`, `NOT_ON_PATH`, `REGISTRY_UNREACHABLE`, `REGISTRY_AUTH`, `INSTALL_FAILED`, `NPM_FAILED`, `NO_LATEST`, `DOCTOR_PROBLEMS`) is declared like a table code by each Command that throws it, the Built-in Commands included. A `doctor` row declares its codes in its own `errors` ([`doctor` rows](doctor.md)). `--help` lists only the exit codes some Command can end in, so exit 3 shows only when a code of class `source` is declared.
25
25
 
26
26
  Inside a Command, throw `ctx.fail(code, init, options?)`, typed to the Command's `errors` and each code's `details`, so an undeclared code is a type error. A helper outside a Command, such as [`example/store.ts`](../example/store.ts), throws through the `cliError` that `agentCli` returns, typed by the whole table.
27
27
 
@@ -17,7 +17,7 @@ test("--dry-run prints the Plan and saves nothing", async () => {
17
17
 
18
18
  The scaffolded `test/global-setup.ts` points `TMPDIR` at one root that it removes after the run, so these folders leave nothing behind; a CLI without it should add one.
19
19
 
20
- One test checks every example, the Built-in Commands' included: `checkExamples(cli)` from `@duyquangnvx/agent-cli/testing` throws one error listing each example that does not tokenize, route to a Command or pass its schemas. The scaffolded test calls it already.
20
+ One test checks every example, the Built-in Commands' included: `checkExamples(cli)` from `@duyquangnvx/agent-cli/testing` throws one error listing each example that does not tokenize, route to a Command or pass its schemas. The scaffolded test calls it already. Under `runCli`, a failure's Next steps face the same check: a Next step that would not run as given, such as one missing a required option or holding a placeholder like `<id>` for a number, fails the run as a bug (exit 70) naming it and why.
21
21
 
22
22
  Under `--json`, stderr is JSON Lines: one `{"progress":"..."}` per progress line, then `{ errors: [{ code, message, hint, next?, details?, retryAt? }] }` as the last line, even for one error. Assert a failure's code through `runtimeCodes`, so a misspelt code fails type checking:
23
23
 
@@ -63,6 +63,7 @@ A Command that awaits native threads, such as sharp's, runs work the clock canno
63
63
  - `spawnDetached`: replace it to answer for a detached child, such as a Daemon: return `{ pid }`. A test that fakes `spawn` but not `spawnDetached` fails as a bug when the Command starts one, rather than start a real child.
64
64
  - `tools`: put a folder holding an empty executable of that name on the test's `PATH`.
65
65
  - `stdin`: pass the text an `orStdin` arg reads as `runCli`'s `stdin` option.
66
+ - `platform`: pass `"win32"` to run the Runtime's Windows branches on any OS, where it would read `process.platform`: hint wording, a declared tool found on `PATH` by its `PATHEXT` extension, such as an executable `ffmpeg.CMD`, and the npm file (`npm.cmd`), bin folder and global `node_modules` folder the Built-in Commands use. Starting a real child and retrying a real file rename still follow the host.
66
67
 
67
68
  ## What the Runtime already tests
68
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/agent-cli",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "Runtime for agent-first command-line tools: owns parsing, output, errors and exit codes.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,8 +0,0 @@
1
- import { type CommandSpec } from "./command-spec.js";
2
- import type { Cli } from "./define.js";
3
- /**
4
- * `error`, or a bug naming each Error table code in it that the Command does not declare in its
5
- * `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
6
- * included, belong to no declaration, so both pass.
7
- */
8
- export declare function checkedCodes(error: unknown, { command, path, builtIn }: CommandSpec, cli: Pick<Cli, "name" | "errors">): unknown;
@@ -1,25 +0,0 @@
1
- import { commandLabel } from "./command-spec.js";
2
- import { errorTableOf } from "./error-table.js";
3
- import { CliError, errorsOf } from "./errors.js";
4
- /**
5
- * `error`, or a bug naming each Error table code in it that the Command does not declare in its
6
- * `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
7
- * included, belong to no declaration, so both pass.
8
- */
9
- export function checkedCodes(error, { command, path, builtIn }, cli) {
10
- if (builtIn)
11
- return error;
12
- const table = errorTableOf(cli);
13
- const declared = new Set(command.errors ?? []);
14
- const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && Object.hasOwn(table, leaf.code) && !declared.has(leaf.code)
15
- ? [leaf.code]
16
- : []);
17
- if (undeclared.length === 0)
18
- return error;
19
- const label = commandLabel(path, cli.name).join(" ");
20
- const codes = [...new Set(undeclared)].join(", ");
21
- return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, {
22
- cause: error,
23
- });
24
- }
25
- //# sourceMappingURL=command-errors.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"command-errors.js","sourceRoot":"","sources":["../src/command-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAc,EACd,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAe,EACvC,GAAiC;IAEjC,IAAI,OAAO;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAClD,IAAI,YAAY,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QACrF,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACb,CAAC,CAAC,EAAE,CACP,CAAC;IACF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,YAAY,KAAK,mCAAmC,EAAE;QAC/E,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC"}
@@ -1,15 +0,0 @@
1
- import type { SpecNode } from "../command-spec.js";
2
- import type { Invocation } from "./invocation.js";
3
- import { type CommandRoute } from "./route.js";
4
- import type { ShellWords } from "./shell-words.js";
5
- /** A line a test checks: the Command it routes to, or why it routes to none. */
6
- export type RoutedLine = {
7
- readonly ok: true;
8
- readonly routed: CommandRoute;
9
- readonly invocation: Invocation;
10
- } | {
11
- readonly ok: false;
12
- readonly reason: string;
13
- };
14
- /** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
15
- export declare function routeLine(root: SpecNode, runAs: string, argv: ShellWords): RoutedLine;
@@ -1,18 +0,0 @@
1
- import { route } from "./route.js";
2
- /** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
3
- export function routeLine(root, runAs, argv) {
4
- if (!argv.ok)
5
- return { ok: false, reason: `it does not tokenize: ${argv.reason}` };
6
- const invocation = { runAs, argv: argv.words, flags: [] };
7
- const routed = route(root, invocation);
8
- switch (routed.kind) {
9
- case "usage error":
10
- return { ok: false, reason: routed.error.message };
11
- case "help":
12
- case "version":
13
- return { ok: false, reason: "it names no Command" };
14
- case "command":
15
- return { ok: true, routed, invocation };
16
- }
17
- }
18
- //# sourceMappingURL=route-line.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"route-line.js","sourceRoot":"","sources":["../../src/command-line/route-line.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,EAAE,MAAM,YAAY,CAAC;AAQtD,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,IAAc,EAAE,KAAa,EAAE,IAAgB;IACvE,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IACnF,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACvC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,aAAa;YAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACrD,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS;YACZ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QACtD,KAAK,SAAS;YACZ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC5C,CAAC;AACH,CAAC"}