@duyquangnvx/agent-cli 0.4.0 → 0.5.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 (115) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/agent-cli.js +1 -1
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/agent.d.ts +5 -3
  5. package/dist/agent.js +31 -10
  6. package/dist/agent.js.map +1 -1
  7. package/dist/built-ins.d.ts +6 -1
  8. package/dist/built-ins.js +3 -3
  9. package/dist/built-ins.js.map +1 -1
  10. package/dist/command-spec.d.ts +5 -0
  11. package/dist/command-spec.js +7 -0
  12. package/dist/command-spec.js.map +1 -1
  13. package/dist/confirm.js +6 -2
  14. package/dist/confirm.js.map +1 -1
  15. package/dist/context.d.ts +102 -0
  16. package/dist/context.js +2 -0
  17. package/dist/context.js.map +1 -0
  18. package/dist/cost.js +2 -2
  19. package/dist/cost.js.map +1 -1
  20. package/dist/define.d.ts +35 -80
  21. package/dist/define.js +3 -1
  22. package/dist/define.js.map +1 -1
  23. package/dist/doctor-rows.d.ts +2 -1
  24. package/dist/doctor-rows.js +15 -10
  25. package/dist/doctor-rows.js.map +1 -1
  26. package/dist/doctor.d.ts +2 -1
  27. package/dist/doctor.js +24 -17
  28. package/dist/doctor.js.map +1 -1
  29. package/dist/errno.d.ts +8 -4
  30. package/dist/errno.js +21 -4
  31. package/dist/errno.js.map +1 -1
  32. package/dist/errors.d.ts +2 -1
  33. package/dist/errors.js +4 -0
  34. package/dist/errors.js.map +1 -1
  35. package/dist/execute.d.ts +9 -2
  36. package/dist/execute.js +10 -3
  37. package/dist/execute.js.map +1 -1
  38. package/dist/exit-classes.js +4 -1
  39. package/dist/exit-classes.js.map +1 -1
  40. package/dist/files.d.ts +23 -6
  41. package/dist/files.js +100 -14
  42. package/dist/files.js.map +1 -1
  43. package/dist/help-json.d.ts +1 -1
  44. package/dist/help-json.js +4 -1
  45. package/dist/help-json.js.map +1 -1
  46. package/dist/help.d.ts +1 -1
  47. package/dist/help.js +11 -7
  48. package/dist/help.js.map +1 -1
  49. package/dist/index.d.ts +8 -3
  50. package/dist/index.js +4 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/input.d.ts +7 -1
  53. package/dist/input.js +33 -4
  54. package/dist/input.js.map +1 -1
  55. package/dist/interrupts.d.ts +3 -1
  56. package/dist/interrupts.js +4 -3
  57. package/dist/interrupts.js.map +1 -1
  58. package/dist/invocation.d.ts +10 -5
  59. package/dist/invocation.js +24 -11
  60. package/dist/invocation.js.map +1 -1
  61. package/dist/list.js +5 -2
  62. package/dist/list.js.map +1 -1
  63. package/dist/npm.d.ts +5 -2
  64. package/dist/npm.js +23 -31
  65. package/dist/npm.js.map +1 -1
  66. package/dist/operator.d.ts +5 -0
  67. package/dist/operator.js +15 -0
  68. package/dist/operator.js.map +1 -0
  69. package/dist/output.d.ts +4 -1
  70. package/dist/output.js +4 -0
  71. package/dist/output.js.map +1 -1
  72. package/dist/outputs.d.ts +24 -1
  73. package/dist/outputs.js +58 -2
  74. package/dist/outputs.js.map +1 -1
  75. package/dist/parse-input.d.ts +11 -2
  76. package/dist/parse-input.js +68 -26
  77. package/dist/parse-input.js.map +1 -1
  78. package/dist/project.d.ts +1 -1
  79. package/dist/route.d.ts +1 -1
  80. package/dist/route.js +2 -1
  81. package/dist/route.js.map +1 -1
  82. package/dist/run-cli.d.ts +16 -2
  83. package/dist/run-cli.js +89 -34
  84. package/dist/run-cli.js.map +1 -1
  85. package/dist/run-process.js +3 -2
  86. package/dist/run-process.js.map +1 -1
  87. package/dist/secrets.d.ts +5 -4
  88. package/dist/secrets.js +4 -5
  89. package/dist/secrets.js.map +1 -1
  90. package/dist/skill-source.d.ts +4 -0
  91. package/dist/skill-source.js +42 -0
  92. package/dist/skill-source.js.map +1 -0
  93. package/dist/skills.d.ts +2 -1
  94. package/dist/skills.js +4 -0
  95. package/dist/skills.js.map +1 -1
  96. package/dist/spawn.d.ts +22 -5
  97. package/dist/spawn.js +126 -29
  98. package/dist/spawn.js.map +1 -1
  99. package/dist/spill.d.ts +4 -3
  100. package/dist/spill.js +1 -7
  101. package/dist/spill.js.map +1 -1
  102. package/dist/stdin.d.ts +13 -0
  103. package/dist/stdin.js +55 -0
  104. package/dist/stdin.js.map +1 -0
  105. package/dist/style.d.ts +1 -1
  106. package/dist/tokenize.d.ts +3 -0
  107. package/dist/tokenize.js +5 -4
  108. package/dist/tokenize.js.map +1 -1
  109. package/dist/tools.d.ts +6 -3
  110. package/dist/tools.js +18 -11
  111. package/dist/tools.js.map +1 -1
  112. package/dist/update.d.ts +2 -1
  113. package/dist/update.js +3 -3
  114. package/dist/update.js.map +1 -1
  115. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,52 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.5.0 (2026-10-19)
8
+
9
+ ### Added
10
+
11
+ - A command-tree CLI may declare `defineCli({ tools })`, the tools `doctor` checks besides its Commands' own. `doctor` reports one row per tool from the CLI's `tools` and every Command's, once per name, a missing one as a problem with its `install` as the fix. A Command still requires only its own `tools`, so the CLI's list never makes a Command refuse with `TOOL_MISSING`.
12
+ - A Command may declare `hidden: true`. It routes and runs when named, and its own `--help` works, but it is left out of text help, `--help --json`, the root examples and "did you mean" suggestions for a misspelt Command.
13
+ - A Command may declare `operator: true`, an Operator Command only a person at a terminal runs. When a Coding agent is detected, the run fails before its input is read, stdin included, with the new Runtime code `NEEDS_OPERATOR` (exit 2, exported as a constant), whose hint is `ask the operator to type: ! <command line as typed>`; `--json` alone does not refuse. Text help marks it `(operator only)` in a listing and with an `Operator Command:` line in its own help, and `--help --json` carries `operator` on every Command, `true` on an Operator Command and `false` on the rest.
14
+ - A Command may declare `help`, a paragraph printed as written after the options in text help. `--help --json` carries it as `help`, which is `null` on a Command without one.
15
+ - `ctx.spawn(command, args, { progress: true })` passes each line of the child's stdout and stderr through `ctx.progress` as it arrives, and still resolves to the whole output, which `check: true` uses for `TOOL_FAILED`. A `Spawn` passed as `runCli`'s `spawn` option receives an `onLine` callback when the caller asked for progress, to emit the child's lines.
16
+ - On Windows, `ctx.spawn` runs a `.cmd` or `.bat` tool, such as one npm installed, found on `PATH` through `PATHEXT` or given by its path. Each argument reaches the tool as given: it is quoted, then escaped for `cmd.exe`, which otherwise expands `%VAR%` and acts on `&`, `|`, `<`, `>`, `^`, `(`, `)` and `!` even inside quotes, twice for a `.cmd` shim that hands its arguments to a second `cmd.exe` parse, as cross-spawn does.
17
+ - A field of a Command's `options` may be a `z.array(...)`, optionally wrapped in optional or default, which collects every occurrence of its flag in order: `--ref a --ref=b --ref c` gives `ref: ["a", "b", "c"]`. Each value is coerced like a scalar option of the element's type, and an issue on one names it, as in `--row "x": ...`. None gives an empty array for the schema to judge, or leaves an optional field out so its default applies. Text help shows the flag as `--ref <value>...`, and `--help --json` gives it the array schema. `defineCommand` throws for an array of booleans. A repeated scalar flag still keeps its last value.
18
+ - `ctx.commandLine({ add?, remove? })` returns the command line as the caller typed it, starting with the CLI's name and shell-quoted, with each flag in `remove` dropped along with its value (`--from 3`, `--from=3`, every time it was given) and the words in `add` placed before any `--`, for a hint naming the exact command to run next. It is the helper the Runtime's own hints (`--confirm`, `--yes`, `--force`, a page's `next`) are built with.
19
+ - A run deadline's `resume(input, commandLine)` receives `ctx.commandLine` as its second argument. A `resume` taking only `input` still type-checks.
20
+ - `ctx.poll(check, { intervalMs, signal })` also stops when `signal` aborts, ending with its reason, so `signal: ctx.deadline(ms)` stops polling at a `--wait` deadline rather than one interval after it.
21
+ - `INTERRUPTED` and `DEADLINE_EXCEEDED` are exported as constants, so a Command tells `ctx.signal.reason` apart and throws its own `INTERRUPTED` with a hint it computes, which wins over the Runtime's. The skill teaches it.
22
+ - A `doctor` row may return `{ state: "skipped", message }` for a check it did not run, such as "not checked: the bridge is down". It shows in text and `--json` and never fails `doctor`.
23
+ - On Ctrl-C or SIGTERM, `doctor` prints the rows finished so far, in text or `--json`, before the run ends with 130 or 143. Rows cut short are left out.
24
+ - With no copy of the skill installed, `doctor`'s `skill` row gives the fix `npx -y skills add <source> --skill <cli name> -g`, which installs only this CLI's skill from a repo holding several, and stays ok. The source is `defineCli({ skill: { source } })` as given, else the package.json `repository` URL with `git+` dropped, `.git` added if missing and `#release` appended, for an `https`, `http`, `ssh` or `git` URL or an scp-style `git@host:group/repo`; a GitHub `owner/repo` shorthand is kept as it is. With no `repository`, one that is neither, or a package.json that cannot be read or parsed, the row gives no fix.
25
+ - `orStdin(schema)` marks one string field of a Command's `args` as also reading stdin: when its operand is exactly `-`, the Runtime reads stdin to its end as UTF-8, before `plan`, `run` or `start`, and the field's schema judges the text, so an empty or invalid text fails with `INVALID_INPUT` like typed text, the issue naming the field `from stdin` instead of quoting the text. Ctrl-C and the run deadline stop a read that never ends; a deadline hit there hints to pipe the text in. When stdin is a terminal, a progress line first says what is read and how to end it (Ctrl-D, or Ctrl-Z then Enter on Windows). Text help shows the field as `<name|->` (`[name|-]` when optional), and `--help --json` marks it `"stdin": true`. `defineCommand` throws for two such args, or for the marker on an option. `runCli` takes `stdin`, a string or a readable stream, read by a `-`; absent, a `-` reads an empty text. The type `StdinSource` is exported.
26
+ - The ports factory's `PortsBase` adds `progress`, `fileError`, `version`, `build` and `self`, which `Context` carries too: a bridge or store writes progress lines (JSON Lines under `--json`) and classifies a failure of its own file as a Command does. `version` and `build` are the CLI's version and bundler stamp as `--version` prints them, `build` absent when unbundled. `self` is the command line that reruns the CLI, `[process.execPath, ...process.execArgv, bin]` from `runCli`'s `bin`, or `[name]`, found on `PATH`, without one, for spawning a hidden Command. The type `BuildStamp` is exported.
27
+ - `ctx.fileError(error, { path, access })` turns an errno failure of a Command's own file access, such as a SQLite store or a log it opens itself, into the Runtime's classified error, with the same codes and hints as `ctx.files`; any other error comes back unchanged. The type `FileAccess` is exported.
28
+ - The Runtime code `NOT_A_FILE` (exit 1), raised for EISDIR, such as `ctx.files.read` of a folder, naming the path. It is exported as a constant.
29
+ - `runCli` takes a `platform` option, which replaces `process.platform` in picking the wording of hints, the `PERMISSION_DENIED` hint and how to end stdin, so a test checks the Windows wording on any OS.
30
+ - `ctx.files.write(path, data, { exclusive, mode })`: `exclusive: true` fails with `ALREADY_EXISTS` when anything is at the path, never leaving half a file there, and `mode` sets the file's permission bits whatever mode an older file had. The type `WriteOptions` is exported.
31
+ - `ctx.files.stat(path)` returns `{ size, id }`, where `id` is the same for every link to one file (device and inode), or `undefined` when nothing is at the path or a part of it is a file. The type `FileStat` is exported.
32
+ - The agent-cli skill's example `links` has `import <text|->`, which saves every link found in a text, read from stdin when given `-`, tags each with every repeated `--tag`, and explains its stdin form in a `help` paragraph. The scaffolded template's `card` takes an optional message to write under the greeting, read from stdin when given `-`, signs the card from every repeated `--from`, and states its cost in a `help` paragraph; its tests cover each.
33
+
34
+ ### Changed
35
+
36
+ - **Breaking:** `DoctorFinding` is a union over `state`: `ok` (`message`, `fix?`), `problem` (`message`, `fix?`, `code?`) and `skipped` (`message`). A row that throws a `CliError` keeps its code, and `doctor --json` rows carry `code`.
37
+ - **Breaking:** `doctor` fails with one error per non-advisory problem row, with the row's code or `DOCTOR_PROBLEMS` without one, so it exits by the first of 1, 2, 3 and 75 that any of them has, not always 2: an expired session exits 3, a network failure 75. Each error's message is `<cli> doctor found a problem in <row>: <message>`. The report still prints on stdout.
38
+ - **Breaking:** the `Files` interface gains `stat`, and its `write` receives the write options, so every fake passed as `runCli`'s `files` option must implement `stat`. The scaffolded template's fake and the skill's example implement it.
39
+ - **Breaking:** more errno are classified, naming the path: EROFS and EBUSY fail with `PERMISSION_DENIED` (exit 2, with the sandbox hint under a Coding agent), EDQUOT with `DISK_FULL` (exit 2), and ENOTDIR and ENAMETOOLONG with `NOT_FOUND` (exit 1). Each was exit 70 before, so a write Claude Code's Linux sandbox denies with EROFS no longer reads as a bug.
40
+ - **Breaking:** for a Command with `outputs`, after every guard passes and before `apply`, the Runtime creates each output's folder and a temp file in it, so a denied folder fails with `PERMISSION_DENIED` naming the folder before `apply` runs; `--dry-run` and a refused guard create nothing. Without `--force`, the first write to each output during `apply`, through `ctx.files` or a port's `files`, is exclusive: it fails with `ALREADY_EXISTS`, whose hint names the command line with `--force`, if the file appeared meanwhile. A later write to an output the run already wrote replaces it; with `--force` every write replaces.
41
+ - **Breaking:** agent detection follows gh's marker list: `AI_AGENT` (an agent name), `AGENT=amp`, `CODEX_SANDBOX`, `CODEX_CI`, `CODEX_THREAD_ID`, `GEMINI_CLI`, `COPILOT_CLI`, `OPENCODE`, `ANTIGRAVITY_AGENT`, `AUGMENT_AGENT`, `REPL_ID`, `CLAUDE_CODE_IS_COWORK`, `CLAUDECODE`, `CLAUDE_CODE`, `CURSOR_TRACE_ID`, `CURSOR_AGENT`, `CURSOR_EXTENSION_HOST_ROLE=agent-exec`, `TERM_PROGRAM=kiro`, a `.pi/agent` folder on `PATH`, or `GOOSE_PROVIDER`. A marker set to an empty string no longer counts, and `CI` alone no longer counts as an agent, so a usage error in a CI job prints the error and hint without the full help, and a CI job on a TTY gets colour.
42
+ - **Breaking:** under `--json`, stderr is JSON Lines (ADR 0006): each `ctx.progress(message)` writes `{"progress":"<message>"}` as it happens, the Runtime's own progress lines (`finishing <name>`, the `stopping… (Ctrl-C again to quit now)` notice, the stdin announcement) and a spawned child's lines under `progress: true` are written the same way, and a failure's `{"errors":[…]}` stays the last line, so every stderr line parses as JSON. Without `--json`, progress stays plain text. The agent-cli skill and the scaffolded skill say so.
43
+ - On Windows, the `PERMISSION_DENIED` hint says a program may hold the path open, and to close it, then run the same command again.
44
+ - `ctx.files.write` through a symlink replaces the link's target and keeps the link.
45
+ - On Windows, a replacing `ctx.files.write` retries a transient EPERM or EBUSY, such as an antivirus scan holding the file, for about half a second before failing with `PERMISSION_DENIED`.
46
+ - `doctor` and `update` run the npm beside the Node running the CLI (`npm`, or `npm.cmd` on Windows), with that Node first on its `PATH`, so under nvm they install into that Node; with no npm beside it, they run the npm on `PATH` as before. `runCli` gains the option `node`, the path `runProcess` passes as `process.execPath`; absent, npm comes from `PATH`.
47
+
48
+ ### Fixed
49
+
50
+ - On Windows, an interrupt kills the child `ctx.spawn` started, or the npm `doctor` and `update` run, together with every process it started (`taskkill /T /F`), so Ctrl-C leaves no orphaned processes.
51
+ - `ctx.files.exists` answers false for a path under a file instead of failing.
52
+
7
53
  ## v0.4.0 (2026-10-19)
8
54
 
9
55
  ### Added
package/dist/agent-cli.js CHANGED
@@ -12,7 +12,7 @@ function defineCli(cli) {
12
12
  if (cli.commands === undefined)
13
13
  return cli;
14
14
  const { commands } = cli;
15
- const taken = Object.keys(builtInCommands(cli, undefined)).filter((name) => commands[name] !== undefined);
15
+ const taken = Object.keys(builtInCommands(cli, { bin: undefined, node: undefined })).filter((name) => commands[name] !== undefined);
16
16
  if (taken.length > 0) {
17
17
  throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-cli.js","sourceRoot":"","sources":["../src/agent-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAIL,gBAAgB,GAGjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAgBnD,SAAS,aAAa,CAAkB,GAAyB;IAC/D,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACpD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,IAAI,aAAa,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,8CAA8C,CAC5F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAkB,GAAyB;IAC3D,aAAa,CAAC,GAAG,CAAC,CAAC;IACnB,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAC/D,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,SAAS,CACvC,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,IAAI,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAmB,EAAE,SAAS,EAAE,CAAC;AAC3E,CAAC"}
1
+ {"version":3,"file":"agent-cli.js","sourceRoot":"","sources":["../src/agent-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAIL,gBAAgB,GAGjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAgBnD,SAAS,aAAa,CAAkB,GAAyB;IAC/D,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACpD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,IAAI,aAAa,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,8CAA8C,CAC5F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAkB,GAAyB;IAC3D,aAAa,CAAC,GAAG,CAAC,CAAC;IACnB,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,MAAM,CACzF,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,SAAS,CACvC,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,IAAI,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAmB,EAAE,SAAS,EAAE,CAAC;AAC3E,CAAC"}
package/dist/agent.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { Env } from "./define.js";
2
- export declare const AGENT_VARIABLES: readonly ["CLAUDECODE", "CODEX_CI", "CODEX_SANDBOX", "GEMINI_CLI", "CURSOR_AGENT", "CI"];
3
- /** Whether a Coding agent, not a person, is running the Agent CLI. */
1
+ import type { Env } from "./context.js";
2
+ /**
3
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
4
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
5
+ */
4
6
  export declare function isAgent(env: Env): boolean;
package/dist/agent.js CHANGED
@@ -1,14 +1,35 @@
1
- // Some Coding agents run commands in a PTY, so a TTY alone doesn't mean a person is watching.
2
- export const AGENT_VARIABLES = [
3
- "CLAUDECODE",
4
- "CODEX_CI",
5
- "CODEX_SANDBOX",
6
- "GEMINI_CLI",
7
- "CURSOR_AGENT",
8
- "CI",
1
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
2
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
3
+ const equals = (name, value) => (env) => env[name] === value;
4
+ // Copied in order from gh, which keeps it current as agents appear:
5
+ // https://github.com/cli/cli/blob/17142e08db2e300b37e6da1ddcfb651eb6d9c587/internal/agents/detect.go
6
+ const MARKERS = [
7
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
8
+ equals("AGENT", "amp"),
9
+ isSet("CODEX_SANDBOX"),
10
+ isSet("CODEX_CI"),
11
+ isSet("CODEX_THREAD_ID"),
12
+ isSet("GEMINI_CLI"),
13
+ isSet("COPILOT_CLI"),
14
+ isSet("OPENCODE"),
15
+ isSet("ANTIGRAVITY_AGENT"),
16
+ isSet("AUGMENT_AGENT"),
17
+ isSet("REPL_ID"),
18
+ isSet("CLAUDE_CODE_IS_COWORK"),
19
+ isSet("CLAUDECODE"),
20
+ isSet("CLAUDE_CODE"),
21
+ isSet("CURSOR_TRACE_ID"),
22
+ isSet("CURSOR_AGENT"),
23
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
24
+ equals("TERM_PROGRAM", "kiro"),
25
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
26
+ isSet("GOOSE_PROVIDER"),
9
27
  ];
10
- /** Whether a Coding agent, not a person, is running the Agent CLI. */
28
+ /**
29
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
30
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
31
+ */
11
32
  export function isAgent(env) {
12
- return AGENT_VARIABLES.some((name) => env[name] !== undefined);
33
+ return MARKERS.some((marker) => marker(env));
13
34
  }
14
35
  //# sourceMappingURL=agent.js.map
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,YAAY;IACZ,UAAU;IACV,eAAe;IACf,YAAY;IACZ,cAAc;IACd,IAAI;CACI,CAAC;AAEX,sEAAsE;AACtE,MAAM,UAAU,OAAO,CAAC,GAAQ;IAC9B,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC;AACjE,CAAC"}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,mBAAmB,CAAC;AAEvC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;AACvE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC;AAElF,oEAAoE;AACpE,qGAAqG;AACrG,MAAM,OAAO,GAAuC;IAClD,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC5C,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;IACtB,KAAK,CAAC,eAAe,CAAC;IACtB,KAAK,CAAC,UAAU,CAAC;IACjB,KAAK,CAAC,iBAAiB,CAAC;IACxB,KAAK,CAAC,YAAY,CAAC;IACnB,KAAK,CAAC,aAAa,CAAC;IACpB,KAAK,CAAC,UAAU,CAAC;IACjB,KAAK,CAAC,mBAAmB,CAAC;IAC1B,KAAK,CAAC,eAAe,CAAC;IACtB,KAAK,CAAC,SAAS,CAAC;IAChB,KAAK,CAAC,uBAAuB,CAAC;IAC9B,KAAK,CAAC,YAAY,CAAC;IACnB,KAAK,CAAC,aAAa,CAAC;IACpB,KAAK,CAAC,iBAAiB,CAAC;IACxB,KAAK,CAAC,cAAc,CAAC;IACrB,MAAM,CAAC,4BAA4B,EAAE,YAAY,CAAC;IAClD,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC;IAC9B,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrF,KAAK,CAAC,gBAAgB,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAQ;IAC9B,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -1,3 +1,8 @@
1
1
  import type { Commands, CommandTreeCli } from "./define.js";
2
+ /** How the CLI was started: the script, and the Node running it. */
3
+ export interface Launch {
4
+ readonly bin: string | undefined;
5
+ readonly node: string | undefined;
6
+ }
2
7
  /** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
3
- export declare function builtInCommands(cli: CommandTreeCli, bin: string | undefined): Commands;
8
+ export declare function builtInCommands(cli: CommandTreeCli, launch: Launch): Commands;
package/dist/built-ins.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { doctorCommand } from "./doctor.js";
2
2
  import { updateCommand } from "./update.js";
3
3
  /** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
4
- export function builtInCommands(cli, bin) {
4
+ export function builtInCommands(cli, launch) {
5
5
  return {
6
- doctor: doctorCommand(cli, bin),
7
- ...(cli.install === undefined ? {} : { update: updateCommand(cli, cli.install, bin) }),
6
+ doctor: doctorCommand(cli, launch),
7
+ ...(cli.install === undefined ? {} : { update: updateCommand(cli, cli.install, launch) }),
8
8
  };
9
9
  }
10
10
  //# sourceMappingURL=built-ins.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"built-ins.js","sourceRoot":"","sources":["../src/built-ins.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,GAAmB,EAAE,GAAuB;IAC1E,OAAO;QACL,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;QAC/B,GAAG,CAAC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;KACvF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"built-ins.js","sourceRoot":"","sources":["../src/built-ins.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ5C,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,GAAmB,EAAE,MAAc;IACjE,OAAO;QACL,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC;QAClC,GAAG,CAAC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;KAC1F,CAAC;AACJ,CAAC"}
@@ -9,6 +9,9 @@ export interface CommandSpec {
9
9
  readonly commandLine: string;
10
10
  readonly command: Command;
11
11
  readonly description: string;
12
+ readonly help: string | undefined;
13
+ readonly hidden: boolean;
14
+ readonly operator: boolean;
12
15
  readonly effect: Command["effect"];
13
16
  readonly examples: readonly Example[];
14
17
  readonly args: readonly InputField[];
@@ -24,5 +27,7 @@ export interface GroupSpec {
24
27
  readonly commands: Readonly<Record<string, SpecNode>>;
25
28
  }
26
29
  export type SpecNode = CommandSpec | GroupSpec;
30
+ /** The group's Commands and groups that help and suggestions may name: hidden Commands left out. */
31
+ export declare function visibleCommands({ commands }: GroupSpec): Readonly<Record<string, SpecNode>>;
27
32
  export declare function commandSpec(commandLine: string, command: Command): CommandSpec;
28
33
  export declare function groupSpec(commandLine: string, commands: Commands): GroupSpec;
@@ -3,6 +3,10 @@ import { standardFlagsFor } from "./flags.js";
3
3
  import { argFields, optionFields } from "./input.js";
4
4
  import { listSpec } from "./list.js";
5
5
  import { hasPlan } from "./planned.js";
6
+ /** The group's Commands and groups that help and suggestions may name: hidden Commands left out. */
7
+ export function visibleCommands({ commands }) {
8
+ return Object.fromEntries(Object.entries(commands).filter(([, node]) => node.kind === "group" || !node.hidden));
9
+ }
6
10
  function declaredOf(command) {
7
11
  if (!hasPlan(command)) {
8
12
  return { outputs: false, force: undefined, cost: false };
@@ -20,6 +24,9 @@ export function commandSpec(commandLine, command) {
20
24
  commandLine,
21
25
  command,
22
26
  description: command.description,
27
+ help: command.help,
28
+ hidden: command.hidden === true,
29
+ operator: command.operator === true,
23
30
  effect: command.effect,
24
31
  examples: command.examples,
25
32
  args: argFields(command.args),
@@ -1 +1 @@
1
- {"version":3,"file":"command-spec.js","sourceRoot":"","sources":["../src/command-spec.ts"],"names":[],"mappings":"AACA,OAAO,EAA6C,SAAS,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAoC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,SAAS,EAAmB,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAiB,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA0BvC,SAAS,UAAU,CAAC,OAAgB;IAClC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,SAAS;QACtC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,OAAgB;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW;QACX,OAAO;QACP,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QAC7B,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC;QACtC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC;QAC1E,IAAI;QACJ,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,OAAO,CAAC,MAAM;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB,EAAE,QAAkB;IAC/D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,WAAW;QACX,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAC5F;KACF,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,WAAmB,EAAE,IAAwB;IAC3D,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"command-spec.js","sourceRoot":"","sources":["../src/command-spec.ts"],"names":[],"mappings":"AACA,OAAO,EAA6C,SAAS,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAoC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,SAAS,EAAmB,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAiB,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA6BvC,oGAAoG;AACpG,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAa;IACrD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CACrF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB;IAClC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,SAAS;QACtC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,OAAgB;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW;QACX,OAAO;QACP,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI;QAC/B,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QAC7B,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC;QACtC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC;QAC1E,IAAI;QACJ,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,OAAO,CAAC,MAAM;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB,EAAE,QAAkB;IAC/D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,WAAW;QACX,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAC5F;KACF,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,WAAmB,EAAE,IAAwB;IAC3D,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACzF,CAAC"}
package/dist/confirm.js CHANGED
@@ -1,13 +1,17 @@
1
1
  import { UsageError } from "./errors.js";
2
2
  import { CONFIRM_FLAG } from "./flags.js";
3
- import { commandLineWith } from "./invocation.js";
3
+ import { commandLineOf } from "./invocation.js";
4
4
  export const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
5
5
  export const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
6
6
  export function checkConfirmation({ invocation, commandLine, given }, expected) {
7
7
  if (given === expected) {
8
8
  return;
9
9
  }
10
- const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
10
+ const confirmed = commandLineOf(invocation, {
11
+ remove: [CONFIRM_FLAG.flag],
12
+ add: [CONFIRM_FLAG.flag, expected],
13
+ });
14
+ const hint = `to confirm, run \`${confirmed}\``;
11
15
  if (given === undefined) {
12
16
  throw new UsageError({
13
17
  code: CONFIRMATION_REQUIRED,
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../src/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAmB,MAAM,iBAAiB,CAAC;AAEnE,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAC7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAQ7D,MAAM,UAAU,iBAAiB,CAC/B,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAgB,EAChD,QAAgB;IAEhB,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,qBAAqB,eAAe,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;IAC/F,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,KAAK,WAAW,+BAA+B,YAAY,CAAC,IAAI,EAAE;YAC3E,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,UAAU,CAAC;QACnB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,mCAAmC,WAAW,IAAI;QACxG,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../src/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AAEjE,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAC7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAQ7D,MAAM,UAAU,iBAAiB,CAC/B,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAgB,EAChD,QAAgB;IAEhB,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,EAAE;QAC1C,MAAM,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC;QAC3B,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC;KACnC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,qBAAqB,SAAS,IAAI,CAAC;IAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,KAAK,WAAW,+BAA+B,YAAY,CAAC,IAAI,EAAE;YAC3E,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,UAAU,CAAC;QACnB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,mCAAmC,WAAW,IAAI;QACxG,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,102 @@
1
+ import type * as z from "zod";
2
+ import type { Clock } from "./clock.js";
3
+ import type { FileAccess } from "./errno.js";
4
+ import type { Files } from "./files.js";
5
+ import type { CommandLineChange } from "./invocation.js";
6
+ import type { Secret } from "./secrets.js";
7
+ import type { ContextSpawn } from "./spawn.js";
8
+ export type Env = Readonly<Record<string, string | undefined>>;
9
+ export interface PollOptions {
10
+ readonly intervalMs: number;
11
+ /** Stops the polling when it aborts, as `ctx.signal` does; pass `ctx.deadline(ms)` for a wait. */
12
+ readonly signal?: AbortSignal;
13
+ }
14
+ /** The commit a build was made from and when, stamped by the bundler. */
15
+ export interface BuildStamp {
16
+ readonly sha: string;
17
+ readonly dirty: boolean;
18
+ readonly builtAt: string;
19
+ }
20
+ export interface Context<Ports = unknown, Settings = unknown> {
21
+ readonly cwd: string;
22
+ readonly env: Env;
23
+ /** The CLI's version, as `--version` prints it. */
24
+ readonly version: string;
25
+ /** The bundler's stamp, as `--version` prints it; absent when the CLI runs unbundled. */
26
+ readonly build: BuildStamp | undefined;
27
+ /**
28
+ * The command line that reruns this CLI, to spawn one of its hidden Commands: Node, its flags and
29
+ * the bin, or the CLI's name, found on PATH, when run without a bin.
30
+ */
31
+ readonly self: readonly string[];
32
+ /** The folder holding this CLI's `package.json`, for reading files shipped in the package. */
33
+ readonly packageRoot: string;
34
+ /** Writes a plain line to stderr, for long work; it never reaches stdout. */
35
+ progress(message: string): void;
36
+ /**
37
+ * Aborts when the run is interrupted (Ctrl-C) or hits the Command's run deadline; pass it to
38
+ * `fetch` and child processes.
39
+ */
40
+ readonly signal: AbortSignal;
41
+ /** Returns a signal that also aborts, with `DEADLINE_EXCEEDED`, once `ms` have passed. */
42
+ deadline(ms: number): AbortSignal;
43
+ /**
44
+ * Runs a step that must not stop halfway: the run waits for it even when stopped, and then
45
+ * prints `finishing <name>` on stderr.
46
+ */
47
+ runToEnd<T>(name: string, work: () => Promise<T>): Promise<T>;
48
+ /**
49
+ * Records the data done so far, in the shape `render` takes; the last call wins. A run that then
50
+ * fails, hits a deadline or is interrupted prints it; a returned value replaces it.
51
+ */
52
+ deliver(data: unknown): void;
53
+ /** The outside systems the CLI declared with `agentCli<Ports>()`. */
54
+ readonly ports: Ports;
55
+ /**
56
+ * Reads, lists, removes and atomically writes files and folders, paths resolved against `cwd`; a
57
+ * failure fails the run with `PERMISSION_DENIED`, `DISK_FULL`, `NOT_FOUND`, `NOT_A_FILE` or
58
+ * `ALREADY_EXISTS`.
59
+ */
60
+ readonly files: Files;
61
+ /**
62
+ * Runs a child process, killed when the run is interrupted; `check: true` fails the run with
63
+ * `TOOL_FAILED` and the tail of its stderr when it exits non-zero.
64
+ */
65
+ readonly spawn: ContextSpawn;
66
+ /** Deadlines, `poll` and the grace period after an interrupt run on it. */
67
+ readonly clock: Clock;
68
+ /**
69
+ * Calls `check` until it returns a value other than `undefined`, sleeping `intervalMs` between
70
+ * calls; it stops when `ctx.signal` or the given `signal` aborts.
71
+ */
72
+ poll<T>(check: () => T | undefined | Promise<T | undefined>, options: PollOptions): Promise<T>;
73
+ /** The Project file found by walking up from `cwd`, parsed by the CLI's schema. */
74
+ readonly project: Project<Settings> | undefined;
75
+ /** Reads `name` from the env, then from `$XDG_CONFIG_HOME/<cli>/.env`, with where it came from. */
76
+ secret(name: string): Secret | undefined;
77
+ /**
78
+ * Classifies an errno failure of the CLI's own file access, such as a store it opens itself, as
79
+ * `ctx.files` does; any other error comes back unchanged.
80
+ */
81
+ fileError(error: unknown, at: FileAccess): unknown;
82
+ /** Whether the caller passed `--force`; always false for a Command without it. */
83
+ readonly force: boolean;
84
+ /**
85
+ * The command line as the caller typed it, starting with the CLI's name and shell-quoted, with
86
+ * each flag in `remove` dropped along with its value and the words in `add` placed before any
87
+ * `--`; for the hint that names the command to run next.
88
+ */
89
+ commandLine(change?: CommandLineChange): string;
90
+ }
91
+ /** The slice of `Context` the ports factory receives, which exists before the ports do. */
92
+ export type PortsBase = Pick<Context, "env" | "cwd" | "packageRoot" | "secret" | "spawn" | "files" | "clock" | "signal" | "progress" | "version" | "build" | "self" | "fileError">;
93
+ export interface Project<Settings = unknown> {
94
+ /** The folder holding the Project file. */
95
+ readonly root: string;
96
+ readonly settings: Settings;
97
+ }
98
+ /** Names the Project file and the schema its JSON must match. */
99
+ export interface ProjectFile<Settings = unknown> {
100
+ readonly file: string;
101
+ readonly schema: z.ZodType<Settings>;
102
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":""}
package/dist/cost.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CliError } from "./errors.js";
2
2
  import { YES_FLAG } from "./flags.js";
3
- import { commandLineWith } from "./invocation.js";
3
+ import { commandLineOf } from "./invocation.js";
4
4
  export const NOT_CONFIRMED = "NOT_CONFIRMED";
5
5
  /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
6
6
  export function checkCost({ invocation, commandLine, yes }, cost) {
@@ -10,7 +10,7 @@ export function checkCost({ invocation, commandLine, yes }, cost) {
10
10
  throw new CliError({
11
11
  code: NOT_CONFIRMED,
12
12
  message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
13
- hint: `to approve the cost, run \`${commandLineWith(invocation, YES_FLAG.flag)}\``,
13
+ hint: `to approve the cost, run \`${commandLineOf(invocation, { add: [YES_FLAG.flag] })}\``,
14
14
  });
15
15
  }
16
16
  //# sourceMappingURL=cost.js.map
package/dist/cost.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAmB,MAAM,iBAAiB,CAAC;AAEnE,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAQ7C,uFAAuF;AACvF,MAAM,UAAU,SAAS,CACvB,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,EAAY,EAC1C,IAAwB;IAExB,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC;QACjB,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,KAAK,WAAW,YAAY,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE;QAC7D,IAAI,EAAE,8BAA8B,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI;KACnF,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AAEjE,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAQ7C,uFAAuF;AACvF,MAAM,UAAU,SAAS,CACvB,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,EAAY,EAC1C,IAAwB;IAExB,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC;QACjB,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,KAAK,WAAW,YAAY,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE;QAC7D,IAAI,EAAE,8BAA8B,aAAa,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI;KAC5F,CAAC,CAAC;AACL,CAAC"}
package/dist/define.d.ts CHANGED
@@ -1,82 +1,13 @@
1
1
  import type * as z from "zod";
2
- import type { Clock } from "./clock.js";
2
+ import type { BuildStamp, Context, PortsBase, ProjectFile } from "./context.js";
3
3
  import type { ErrorTable } from "./error-table.js";
4
- import type { Files } from "./files.js";
5
4
  import { type StandardFlagName } from "./flags.js";
6
5
  import type { Page, PageInput, RowSchema } from "./list.js";
7
- import type { Secret } from "./secrets.js";
8
- import type { ContextSpawn } from "./spawn.js";
9
6
  import type { Style } from "./style.js";
10
- export type Env = Readonly<Record<string, string | undefined>>;
11
7
  export interface Example {
12
8
  readonly command: string;
13
9
  readonly note: string;
14
10
  }
15
- export interface PollOptions {
16
- readonly intervalMs: number;
17
- }
18
- export interface Context<Ports = unknown, Settings = unknown> {
19
- readonly cwd: string;
20
- readonly env: Env;
21
- /** The folder holding this CLI's `package.json`, for reading files shipped in the package. */
22
- readonly packageRoot: string;
23
- /** Writes a plain line to stderr, for long work; it never reaches stdout. */
24
- progress(message: string): void;
25
- /**
26
- * Aborts when the run is interrupted (Ctrl-C) or hits the Command's run deadline; pass it to
27
- * `fetch` and child processes.
28
- */
29
- readonly signal: AbortSignal;
30
- /** Returns a signal that also aborts, with `DEADLINE_EXCEEDED`, once `ms` have passed. */
31
- deadline(ms: number): AbortSignal;
32
- /**
33
- * Runs a step that must not stop halfway: the run waits for it even when stopped, and then
34
- * prints `finishing <name>` on stderr.
35
- */
36
- runToEnd<T>(name: string, work: () => Promise<T>): Promise<T>;
37
- /**
38
- * Records the data done so far, in the shape `render` takes; the last call wins. A run that then
39
- * fails, hits a deadline or is interrupted prints it; a returned value replaces it.
40
- */
41
- deliver(data: unknown): void;
42
- /** The outside systems the CLI declared with `agentCli<Ports>()`. */
43
- readonly ports: Ports;
44
- /**
45
- * Reads, lists, removes and atomically writes files and folders, paths resolved against `cwd`; a
46
- * failure fails the run with `PERMISSION_DENIED`, `DISK_FULL`, `NOT_FOUND` or `ALREADY_EXISTS`.
47
- */
48
- readonly files: Files;
49
- /**
50
- * Runs a child process, killed when the run is interrupted; `check: true` fails the run with
51
- * `TOOL_FAILED` and the tail of its stderr when it exits non-zero.
52
- */
53
- readonly spawn: ContextSpawn;
54
- /** Deadlines, `poll` and the grace period after an interrupt run on it. */
55
- readonly clock: Clock;
56
- /**
57
- * Calls `check` until it returns a value other than `undefined`, sleeping `intervalMs` between
58
- * calls; it stops when `ctx.signal` aborts.
59
- */
60
- poll<T>(check: () => T | undefined | Promise<T | undefined>, options: PollOptions): Promise<T>;
61
- /** The Project file found by walking up from `cwd`, parsed by the CLI's schema. */
62
- readonly project: Project<Settings> | undefined;
63
- /** Reads `name` from the env, then from `$XDG_CONFIG_HOME/<cli>/.env`, with where it came from. */
64
- secret(name: string): Secret | undefined;
65
- /** Whether the caller passed `--force`; always false for a Command without it. */
66
- readonly force: boolean;
67
- }
68
- /** The slice of `Context` the ports factory receives, which exists before the ports do. */
69
- export type PortsBase = Pick<Context, "env" | "cwd" | "packageRoot" | "secret" | "spawn" | "files" | "clock" | "signal">;
70
- export interface Project<Settings = unknown> {
71
- /** The folder holding the Project file. */
72
- readonly root: string;
73
- readonly settings: Settings;
74
- }
75
- /** Names the Project file and the schema its JSON must match. */
76
- export interface ProjectFile<Settings = unknown> {
77
- readonly file: string;
78
- readonly schema: z.ZodType<Settings>;
79
- }
80
11
  export type InputSchema = z.ZodObject;
81
12
  export type NoPorts = Record<never, never>;
82
13
  export type NoInput = z.ZodObject<Record<never, never>>;
@@ -94,13 +25,25 @@ export interface Tool {
94
25
  /** Bounds a whole run, so a long run stops before the caller's own timeout and can be continued. */
95
26
  export interface RunDeadline<In> {
96
27
  readonly ms: number;
97
- /** The command line that continues the work, given as the hint of the run's `DEADLINE_EXCEEDED`. */
98
- resume(input: In): string;
28
+ /**
29
+ * The command line that continues the work, given as the hint of the run's `DEADLINE_EXCEEDED`;
30
+ * `commandLine` is `ctx.commandLine`.
31
+ */
32
+ resume(input: In, commandLine: Context["commandLine"]): string;
99
33
  }
100
34
  interface CommandBase<Args extends InputSchema, Options extends InputSchema> {
101
35
  readonly name: string;
102
36
  readonly description: string;
37
+ /** A paragraph printed after the options in text help, and carried as `help` in help JSON. */
38
+ readonly help?: string;
103
39
  readonly examples: readonly Example[];
40
+ /** Left out of help, help JSON, the root examples and suggestions, but still routed and run. */
41
+ readonly hidden?: boolean;
42
+ /**
43
+ * Only a person at a terminal runs it: under a Coding agent the run fails with `NEEDS_OPERATOR`
44
+ * before `plan`, `run` or `start`, naming the line the operator types.
45
+ */
46
+ readonly operator?: true;
104
47
  /** Missing ones fail the run with `TOOL_MISSING` before `plan`, `run` or `start`. */
105
48
  readonly tools?: readonly Tool[];
106
49
  readonly args?: Args;
@@ -171,16 +114,16 @@ interface CliBase<Ports, Settings> {
171
114
  readonly name: string;
172
115
  readonly version: string;
173
116
  /** The commit the build was made from and when, stamped by the bundler; `--version` prints it. */
174
- readonly build?: {
175
- readonly sha: string;
176
- readonly dirty: boolean;
177
- readonly builtAt: string;
178
- };
117
+ readonly build?: BuildStamp;
179
118
  /** Every code the CLI throws; a code missing here exits 70, as a bug. */
180
119
  readonly errors?: ErrorTable;
181
120
  /** Builds the real ports when a run first reads `ctx.ports`; `runCli`'s `ports` replaces them in a test. */
182
121
  readonly ports?: (base: PortsBase) => Ports;
183
122
  readonly project?: ProjectFile<Settings>;
123
+ /** Where `npx skills add` installs the CLI's skill from; read from package.json `repository` when absent. */
124
+ readonly skill?: {
125
+ readonly source: string;
126
+ };
184
127
  }
185
128
  /** Where an Agent CLI is published and how it is installed; `doctor` and `update` read it. */
186
129
  export interface GlobalInstall {
@@ -188,12 +131,22 @@ export interface GlobalInstall {
188
131
  readonly package: string;
189
132
  readonly registry: string;
190
133
  }
191
- export interface DoctorFinding {
192
- readonly state: "ok" | "problem";
134
+ /** What a `doctor` row found. A skipped row was not checked, and never fails `doctor`. */
135
+ export type DoctorFinding = {
136
+ readonly state: "ok";
137
+ readonly message: string;
138
+ readonly fix?: string;
139
+ } | {
140
+ readonly state: "problem";
193
141
  readonly message: string;
194
142
  /** The command that fixes the problem. */
195
143
  readonly fix?: string;
196
- }
144
+ /** A code of the Error table or the Runtime's that `doctor` fails with; `DOCTOR_PROBLEMS` when absent. */
145
+ readonly code?: string;
146
+ } | {
147
+ readonly state: "skipped";
148
+ readonly message: string;
149
+ };
197
150
  /** A row the CLI adds to `doctor`, for a system it depends on. */
198
151
  export interface DoctorCheck<Ports = unknown, Settings = unknown> {
199
152
  readonly name: string;
@@ -215,6 +168,8 @@ export interface CommandTreeCli<Ports = unknown, Settings = unknown> extends Cli
215
168
  readonly node?: string;
216
169
  };
217
170
  readonly doctor?: readonly DoctorCheck<Ports, Settings>[];
171
+ /** Tools `doctor` checks besides the Commands' own; no Command requires them. */
172
+ readonly tools?: readonly Tool[];
218
173
  }
219
174
  /** An Agent CLI that is one Command, run with no Command name, as in `tool <args>`. */
220
175
  export interface SingleCommandCli<Ports = unknown, Settings = unknown> extends CliBase<Ports, Settings> {
package/dist/define.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { checkForce, checkNoStandardFlags } from "./flags.js";
2
- import { checkArrayArgLast } from "./input.js";
2
+ import { checkArrayArgLast, checkNoBooleanArrayOption, checkStdinFields } from "./input.js";
3
3
  function checkedCommand(command) {
4
4
  checkNoStandardFlags(command);
5
5
  checkForce(command);
6
6
  checkArrayArgLast(command.name, command.args);
7
+ checkStdinFields(command.name, command.args, command.options);
8
+ checkNoBooleanArrayOption(command.name, command.options);
7
9
  return command;
8
10
  }
9
11
  export function defineCommandFor() {
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAyB,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA+V/C,SAAS,cAAc,CAA2B,OAAiB;IACjE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAA2B,gBAAgB,EAAE,CAAC;AAExE,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,IAAwB;IAChD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAyB,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAwS5F,SAAS,cAAc,CAA2B,OAAiB;IACjE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,yBAAyB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAA2B,gBAAgB,EAAE,CAAC;AAExE,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,IAAwB;IAChD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;AACzC,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { Launch } from "./built-ins.js";
1
2
  import type { CommandTreeCli, DoctorCheck } from "./define.js";
2
3
  /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
3
- export declare function doctorChecks(cli: CommandTreeCli, bin: string | undefined): DoctorCheck[];
4
+ export declare function doctorChecks(cli: CommandTreeCli, launch: Launch): DoctorCheck[];