botanary 0.1.0 → 0.2.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 (73) hide show
  1. package/README.md +186 -9
  2. package/dist/bin/botanary.js +13 -3
  3. package/dist/bin/botanary.js.map +1 -1
  4. package/dist/package.json +2 -2
  5. package/dist/src/app-url.js +32 -0
  6. package/dist/src/app-url.js.map +1 -0
  7. package/dist/src/cli.js +113 -23
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/commands/agent.js +190 -49
  10. package/dist/src/commands/agent.js.map +1 -1
  11. package/dist/src/commands/mcp.js +136 -5
  12. package/dist/src/commands/mcp.js.map +1 -1
  13. package/dist/src/commands/system.js +109 -12
  14. package/dist/src/commands/system.js.map +1 -1
  15. package/dist/src/commands/utilities.js +44 -0
  16. package/dist/src/commands/utilities.js.map +1 -0
  17. package/dist/src/commands/wallet.js +332 -94
  18. package/dist/src/commands/wallet.js.map +1 -1
  19. package/dist/src/completion.js +69 -0
  20. package/dist/src/completion.js.map +1 -0
  21. package/dist/src/config-store.js +52 -0
  22. package/dist/src/config-store.js.map +1 -0
  23. package/dist/src/context.js +14 -4
  24. package/dist/src/context.js.map +1 -1
  25. package/dist/src/errors.js +116 -0
  26. package/dist/src/errors.js.map +1 -0
  27. package/dist/src/help/examples.js +357 -0
  28. package/dist/src/help/examples.js.map +1 -0
  29. package/dist/src/help/format.js +144 -0
  30. package/dist/src/help/format.js.map +1 -0
  31. package/dist/src/help/groups.js +39 -0
  32. package/dist/src/help/groups.js.map +1 -0
  33. package/dist/src/mcp-snippets.js +6 -0
  34. package/dist/src/mcp-snippets.js.map +1 -1
  35. package/dist/src/outcome.js +63 -0
  36. package/dist/src/outcome.js.map +1 -0
  37. package/dist/src/parser.js +18 -39
  38. package/dist/src/parser.js.map +1 -1
  39. package/dist/src/progress.js +37 -0
  40. package/dist/src/progress.js.map +1 -0
  41. package/dist/src/prompt-doc.js +92 -0
  42. package/dist/src/prompt-doc.js.map +1 -0
  43. package/dist/src/render/accounts.js +21 -0
  44. package/dist/src/render/accounts.js.map +1 -0
  45. package/dist/src/render/activity.js +34 -0
  46. package/dist/src/render/activity.js.map +1 -0
  47. package/dist/src/render/agents.js +42 -0
  48. package/dist/src/render/agents.js.map +1 -0
  49. package/dist/src/render/balance.js +68 -0
  50. package/dist/src/render/balance.js.map +1 -0
  51. package/dist/src/render/chains.js +52 -0
  52. package/dist/src/render/chains.js.map +1 -0
  53. package/dist/src/render/gas.js +34 -0
  54. package/dist/src/render/gas.js.map +1 -0
  55. package/dist/src/render/grant.js +34 -0
  56. package/dist/src/render/grant.js.map +1 -0
  57. package/dist/src/render/kv.js +60 -0
  58. package/dist/src/render/kv.js.map +1 -0
  59. package/dist/src/render/mandates.js +111 -0
  60. package/dist/src/render/mandates.js.map +1 -0
  61. package/dist/src/render/print.js +18 -0
  62. package/dist/src/render/print.js.map +1 -0
  63. package/dist/src/repl.js +114 -10
  64. package/dist/src/repl.js.map +1 -1
  65. package/dist/src/review.js +52 -0
  66. package/dist/src/review.js.map +1 -0
  67. package/dist/src/settings.js +88 -0
  68. package/dist/src/settings.js.map +1 -0
  69. package/dist/src/update-check.js +85 -0
  70. package/dist/src/update-check.js.map +1 -0
  71. package/dist/src/validate.js +92 -0
  72. package/dist/src/validate.js.map +1 -0
  73. package/package.json +2 -2
@@ -0,0 +1,69 @@
1
+ import { GET_ROUTES } from 'botanary-mcp';
2
+ import { invalidArgs } from './errors.js';
3
+ export const SHELLS = ['bash', 'zsh', 'fish', 'powershell'];
4
+ /**
5
+ * Every word worth completing: command names at any depth, every long flag, and the documented GET routes
6
+ * (so `botanary api /y<TAB>` works). All of it is static at generation time, which is the point - a
7
+ * completion script that had to make a network call to answer would hang a shell.
8
+ */
9
+ export function completionWords(program) {
10
+ const words = new Set();
11
+ const walk = (cmd) => {
12
+ for (const option of cmd.options) {
13
+ if (option.long)
14
+ words.add(option.long);
15
+ }
16
+ for (const sub of cmd.commands) {
17
+ words.add(sub.name());
18
+ for (const alias of sub.aliases())
19
+ words.add(alias);
20
+ walk(sub);
21
+ }
22
+ };
23
+ walk(program);
24
+ for (const route of GET_ROUTES)
25
+ words.add(route);
26
+ return [...words].sort();
27
+ }
28
+ export function completionScript(shell, program) {
29
+ const words = completionWords(program).join(' ');
30
+ switch (shell) {
31
+ case 'bash':
32
+ return [
33
+ '# botanary completion for bash',
34
+ '_botanary_complete() {',
35
+ ' local cur="${COMP_WORDS[COMP_CWORD]}"',
36
+ ` COMPREPLY=( $(compgen -W "${words}" -- "$cur") )`,
37
+ '}',
38
+ 'complete -F _botanary_complete botanary',
39
+ '',
40
+ ].join('\n');
41
+ case 'zsh':
42
+ return [
43
+ '#compdef botanary',
44
+ '# botanary completion for zsh',
45
+ '_botanary_complete() {',
46
+ ` local -a words; words=(${words})`,
47
+ ' compadd -- $words',
48
+ '}',
49
+ 'compdef _botanary_complete botanary',
50
+ '',
51
+ ].join('\n');
52
+ case 'fish':
53
+ return ['# botanary completion for fish', `complete -c botanary -f -a "${words}"`, ''].join('\n');
54
+ case 'powershell':
55
+ return [
56
+ '# botanary completion for PowerShell',
57
+ 'Register-ArgumentCompleter -Native -CommandName botanary -ScriptBlock {',
58
+ ' param($wordToComplete, $commandAst, $cursorPosition)',
59
+ ` @(${completionWords(program).map((w) => `'${w}'`).join(',')}) |`,
60
+ ' Where-Object { $_ -like "$wordToComplete*" } |',
61
+ " ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }",
62
+ '}',
63
+ '',
64
+ ].join('\n');
65
+ default:
66
+ throw invalidArgs(`Unsupported shell "${shell}". Supported: ${SHELLS.join(', ')}.`, `Run \`botanary completion <shell>\` with one of the supported shells.`);
67
+ }
68
+ }
69
+ //# sourceMappingURL=completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/completion.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAU,CAAC;AAErE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,IAAI,GAAG,CAAC,GAAY,EAAQ,EAAE;QAClC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACjC,IAAI,MAAM,CAAC,IAAI;gBAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC/B,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACtB,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE;gBAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpD,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,UAAU;QAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,OAAgB;IAC9D,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO;gBACL,gCAAgC;gBAChC,wBAAwB;gBACxB,yCAAyC;gBACzC,+BAA+B,KAAK,gBAAgB;gBACpD,GAAG;gBACH,yCAAyC;gBACzC,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,KAAK,KAAK;YACR,OAAO;gBACL,mBAAmB;gBACnB,+BAA+B;gBAC/B,wBAAwB;gBACxB,4BAA4B,KAAK,GAAG;gBACpC,qBAAqB;gBACrB,GAAG;gBACH,qCAAqC;gBACrC,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,KAAK,MAAM;YACT,OAAO,CAAC,gCAAgC,EAAE,+BAA+B,KAAK,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpG,KAAK,YAAY;YACf,OAAO;gBACL,sCAAsC;gBACtC,yEAAyE;gBACzE,wDAAwD;gBACxD,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK;gBACnE,oDAAoD;gBACpD,2GAA2G;gBAC3G,GAAG;gBACH,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf;YACE,MAAM,WAAW,CACf,sBAAsB,KAAK,iBAAiB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAChE,uEAAuE,CACxE,CAAC;IACN,CAAC;AACH,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { botanaryMcpHome } from 'botanary-mcp';
4
+ import { invalidArgs } from './errors.js';
5
+ /** Every key `botanary config set` accepts. Anything else is refused, never silently stored. */
6
+ export const CONFIG_KEYS = ['chainId', 'accountId', 'apiUrl', 'output', 'color', 'confirm'];
7
+ /** Keys whose value is a fixed set. A key absent here takes any non-empty string. */
8
+ const ENUMS = {
9
+ output: ['auto', 'json', 'raw'],
10
+ color: ['auto', 'never'],
11
+ confirm: ['always', 'never'],
12
+ };
13
+ /** Beside identity and session, so one BOTANARY_MCP_HOME moves all three together. */
14
+ export function configPath() {
15
+ return join(botanaryMcpHome(), 'cli-config.json');
16
+ }
17
+ /** A missing or unreadable file is an empty config, not an error: defaults are the whole point of it. */
18
+ export async function readConfig() {
19
+ try {
20
+ const parsed = JSON.parse(await readFile(configPath(), 'utf8'));
21
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
22
+ return {};
23
+ return parsed;
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ export async function writeConfig(next) {
30
+ await mkdir(botanaryMcpHome(), { recursive: true });
31
+ await writeFile(configPath(), `${JSON.stringify(next, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
32
+ }
33
+ /** Assert that a key is known, and return it narrowed. Throws a CliError with exit 2 if unknown. */
34
+ export function assertKnownKey(key) {
35
+ if (!CONFIG_KEYS.includes(key)) {
36
+ throw invalidArgs(`Unknown config key "${key}". Valid keys: ${CONFIG_KEYS.join(', ')}.`, undefined);
37
+ }
38
+ return key;
39
+ }
40
+ /** Validate one `config set` pair, returning it narrowed. Throws a CliError with exit 2 on anything else. */
41
+ export function validateEntry(key, value) {
42
+ const narrowed = assertKnownKey(key);
43
+ const allowed = ENUMS[narrowed];
44
+ if (allowed && !allowed.includes(value)) {
45
+ throw invalidArgs(`Invalid value "${value}" for ${narrowed}. Valid values: ${allowed.join(', ')}.`, undefined);
46
+ }
47
+ if (!value) {
48
+ throw invalidArgs(`${narrowed} cannot be empty.`, `Use \`botanary config unset ${narrowed}\` to remove it.`);
49
+ }
50
+ return { key: narrowed, value };
51
+ }
52
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,gGAAgG;AAChG,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAarG,qFAAqF;AACrF,MAAM,KAAK,GAAkD;IAC3D,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;IAC/B,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,sFAAsF;AACtF,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,CAAC,CAAC;AACpD,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9E,OAAO,MAAuB,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAmB;IACnD,MAAM,KAAK,CAAC,eAAe,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,UAAU,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACzG,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAE,WAAiC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,MAAM,WAAW,CACf,uBAAuB,GAAG,kBAAkB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EACrE,SAAS,CACV,CAAC;IACJ,CAAC;IACD,OAAO,GAAgB,CAAC;AAC1B,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,KAAa;IACtD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,MAAM,WAAW,CACf,kBAAkB,KAAK,SAAS,QAAQ,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAChF,SAAS,CACV,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,WAAW,CAAC,GAAG,QAAQ,mBAAmB,EAAE,+BAA+B,QAAQ,kBAAkB,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC"}
@@ -1,9 +1,19 @@
1
- export function createContext(runtime, globalOpts) {
1
+ export function createContext(runtime, settings) {
2
2
  return {
3
3
  runtime,
4
- json: !!globalOpts.json,
5
- noInteractive: globalOpts.interactive === false,
6
- reveal: !!globalOpts.reveal,
4
+ settings,
5
+ json: settings.json,
6
+ raw: settings.raw,
7
+ quiet: settings.quiet,
8
+ verbose: settings.verbose,
9
+ reveal: settings.reveal,
10
+ interactive: settings.interactive,
11
+ noInteractive: !settings.interactive,
12
+ yes: settings.yes,
13
+ dryRun: settings.dryRun,
14
+ chain: settings.chain,
15
+ account: settings.account,
16
+ outcome: { exit: 0 },
7
17
  };
8
18
  }
9
19
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAmBA,MAAM,UAAU,aAAa,CAAC,OAAqB,EAAE,UAAsB;IACzE,OAAO;QACL,OAAO;QACP,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI;QACvB,aAAa,EAAE,UAAU,CAAC,WAAW,KAAK,KAAK;QAC/C,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM;KAC5B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AA2BA,MAAM,UAAU,aAAa,CAAC,OAAqB,EAAE,QAA0B;IAC7E,OAAO;QACL,OAAO;QACP,QAAQ;QACR,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,aAAa,EAAE,CAAC,QAAQ,CAAC,WAAW;QACpC,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;KACrB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { BotanaryApiError } from 'botanary-mcp';
2
+ import { colors, glyphs } from './render/colors.js';
3
+ /**
4
+ * The exit-code contract. Deliberately typed rather than the 0/1 the v1 design chose: a CI step branches
5
+ * on an exit code before it ever pipes through jq, and Botanary has one outcome nobody else's table has
6
+ * (`pending` - a relayed op whose receipt has not arrived, which is neither success nor failure).
7
+ */
8
+ export const EXIT = {
9
+ ok: 0,
10
+ error: 1,
11
+ invalidArgs: 2,
12
+ notAuthenticated: 3,
13
+ declined: 4,
14
+ pending: 5,
15
+ upstream: 6,
16
+ interrupted: 130,
17
+ };
18
+ /** The same table, in help-print order. Root help renders this verbatim (see src/help/format.ts). */
19
+ export const EXIT_CODES = [
20
+ { code: EXIT.ok, meaning: 'Success' },
21
+ { code: EXIT.error, meaning: 'Error' },
22
+ { code: EXIT.invalidArgs, meaning: 'Invalid arguments' },
23
+ { code: EXIT.notAuthenticated, meaning: 'Not logged in, or not paired' },
24
+ { code: EXIT.declined, meaning: 'Declined (policy, bounds, or chain tier)' },
25
+ { code: EXIT.pending, meaning: 'Pending: relayed, settlement unknown' },
26
+ { code: EXIT.upstream, meaning: 'Upstream unavailable (network, RPC, bundler)' },
27
+ { code: EXIT.interrupted, meaning: 'Interrupted (SIGINT)' },
28
+ ];
29
+ /**
30
+ * Every failure this CLI reports. `message` is ALWAYS the backend's own words when a backend produced it -
31
+ * re-authoring a decline is how a CLI ends up overstating enforcement that is not live (see the workspace
32
+ * CLAUDE.md's hookless-accounts caveat). `code` is the stable string a script branches on; `hint` is one
33
+ * actionable sentence, never a paragraph.
34
+ */
35
+ export class CliError extends Error {
36
+ code;
37
+ exit;
38
+ hint;
39
+ constructor(message, code, exit, hint) {
40
+ super(message);
41
+ this.code = code;
42
+ this.exit = exit;
43
+ this.hint = hint;
44
+ this.name = 'CliError';
45
+ }
46
+ }
47
+ export function invalidArgs(message, hint) {
48
+ return new CliError(message, 'invalid_args', EXIT.invalidArgs, hint);
49
+ }
50
+ export function notLoggedIn() {
51
+ return new CliError('Not logged in as the wallet owner.', 'not_logged_in', EXIT.notAuthenticated, 'Run `botanary login` first.');
52
+ }
53
+ export function notPaired() {
54
+ return new CliError('This machine has no agent identity paired with Botanary.', 'not_paired', EXIT.notAuthenticated, 'Run `botanary agent pair` and enter the code in Botanary.');
55
+ }
56
+ /** True for the transport-level failures fetch reports as a plain Error, which carry no HTTP status. */
57
+ function isNetworkError(e) {
58
+ if (!(e instanceof Error))
59
+ return false;
60
+ const code = e.code;
61
+ if (code === 'ENOTFOUND' || code === 'ECONNREFUSED' || code === 'ECONNRESET' || code === 'ETIMEDOUT' || code === 'EAI_AGAIN') {
62
+ return true;
63
+ }
64
+ return e.name === 'AbortError' || /fetch failed|network|socket hang up/i.test(e.message);
65
+ }
66
+ function fromApiError(e) {
67
+ // The backend's own typed refusal, when it sent one. Keyed off the guaranteed field rather than the
68
+ // prose, exactly as BotanaryApiError.details' own doc comment asks.
69
+ const declineReason = typeof e.details?.declineReason === 'string' ? e.details.declineReason : null;
70
+ if (e.status === 401 || e.status === 403) {
71
+ return new CliError(e.message, 'not_authenticated', EXIT.notAuthenticated, 'Run `botanary login` again.');
72
+ }
73
+ if (declineReason || e.status === 402) {
74
+ return new CliError(e.message, declineReason ?? 'declined', EXIT.declined, 'Run `botanary diagnose` to see what is blocking this.');
75
+ }
76
+ if (e.status === 404) {
77
+ return new CliError(e.message, 'not_found', EXIT.error);
78
+ }
79
+ if (e.status === 400 || e.status === 422) {
80
+ return new CliError(e.message, 'invalid_args', EXIT.invalidArgs);
81
+ }
82
+ if (e.status === 429) {
83
+ return new CliError(e.message, 'rate_limited', EXIT.upstream, e.retryAfterSeconds != null ? `Retry in ${e.retryAfterSeconds}s.` : 'Try again shortly.');
84
+ }
85
+ if (e.status >= 500) {
86
+ return new CliError(e.message, 'upstream_unavailable', EXIT.upstream, 'The backend or an upstream RPC is unavailable. Try again shortly.');
87
+ }
88
+ return new CliError(e.message, 'error', EXIT.error);
89
+ }
90
+ /** Normalize anything a command threw into the one type cli.ts knows how to report. */
91
+ export function toCliError(e) {
92
+ if (e instanceof CliError)
93
+ return e;
94
+ if (e instanceof BotanaryApiError)
95
+ return fromApiError(e);
96
+ if (isNetworkError(e)) {
97
+ return new CliError(e instanceof Error ? e.message : String(e), 'upstream_unavailable', EXIT.upstream, 'Check your connection, or `botanary config` for the API URL in use.');
98
+ }
99
+ return new CliError(e instanceof Error ? e.message : String(e), 'error', EXIT.error);
100
+ }
101
+ /** The `--json` failure contract. Successful output stays the bare payload: a zero exit already says ok. */
102
+ export function errorEnvelope(err) {
103
+ return {
104
+ ok: false,
105
+ error: { code: err.code, message: err.message, ...(err.hint ? { hint: err.hint } : {}) },
106
+ };
107
+ }
108
+ /** The human failure shape the v1 design spec §7 promised: the reason, the machine code, the next step. */
109
+ export function errorLines(err) {
110
+ const label = err.exit === EXIT.declined ? 'declined' : 'error';
111
+ const lines = [glyphs.danger(`${label}: ${err.message}`), colors.dim(` code: ${err.code}`)];
112
+ if (err.hint)
113
+ lines.push(` ${err.hint}`);
114
+ return lines;
115
+ }
116
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,357 @@
1
+ /** Keyed by the command's full path with the program name stripped: 'send', 'agent spend', 'mcp config'. */
2
+ export const COMMAND_HELP = {
3
+ login: {
4
+ body: 'Log in as the account owner. Botanary prints a one-time code and opens your browser; you approve\n' +
5
+ 'there. The CLI never sees your key and never signs for you - it stores only a session token, in the\n' +
6
+ 'same place botanary-mcp stores its own, so a coding agent on this machine sees the same login.',
7
+ examples: ['botanary login', 'botanary login --json'],
8
+ notes: 'If a browser cannot be opened (SSH, headless, WSL), the printed URL is the one to visit yourself.',
9
+ seeAlso: 'botanary status, botanary logout - wallet_login (MCP)',
10
+ },
11
+ logout: {
12
+ body: 'Revoke the stored owner session on the server and delete it from this machine.',
13
+ examples: ['botanary logout'],
14
+ seeAlso: 'botanary login - wallet_logout (MCP)',
15
+ },
16
+ status: {
17
+ body: 'The owner session and this machine\'s agent binding, side by side, with no network call beyond\nreading each one.',
18
+ examples: ['botanary status', 'botanary status --json'],
19
+ seeAlso: 'botanary whoami, botanary diagnose - wallet_status (MCP)',
20
+ },
21
+ whoami: {
22
+ body: 'Who Botanary says you are, in both lanes: the owner session this machine holds, and whether the\n' +
23
+ 'agent identity on this machine is a live, claimed connected agent.',
24
+ examples: ['botanary whoami', 'botanary whoami --json'],
25
+ seeAlso: 'botanary status, botanary agent whoami - wallet_status + whoami (MCP)',
26
+ },
27
+ balance: {
28
+ body: 'What this account holds, across every chain Botanary serves, with a fiat total and the freshness of\n' +
29
+ 'the numbers. Balances come from the backend store, which can lag the chain; the footer says so\n' +
30
+ 'whenever it does, rather than presenting a stale number as settled.',
31
+ examples: ['botanary balance', 'botanary balance --json | jq .balance.totalFiat', 'botanary balance --raw'],
32
+ seeAlso: 'botanary activity, botanary chains - wallet_portfolio (MCP)',
33
+ },
34
+ send: {
35
+ body: 'Send tokens from an account you own. Botanary never holds your key: the CLI builds an unsigned\n' +
36
+ 'operation, then either a mandate signs it locally or you approve it in your browser.\n' +
37
+ '\n' +
38
+ 'Which lane signs is decided for you:\n' +
39
+ ' mandate a live mandate covers this exact amount, token and chain\n' +
40
+ ' browser anything else, which opens Botanary for you to approve',
41
+ examples: [
42
+ 'botanary send # guided',
43
+ 'botanary send --amount 25 --token USDC --recipient 0xAb..3F --chain-id 8453',
44
+ 'botanary send --amount 25 --token USDC --recipient 0xAb..3F --chain-id 8453 --dry-run',
45
+ 'botanary send --amount 25 --token USDC --recipient 0xAb..3F --chain-id 8453 --yes --json | jq -r .txHash',
46
+ ],
47
+ notes: 'Mandates are testnet-only today, so on mainnet every send is browser-signed.\n' +
48
+ 'Exit 5 means the op was relayed but no receipt arrived yet - poll `botanary api /userops/<opId> --json` to check its status.',
49
+ seeAlso: 'botanary chains, botanary gas, botanary mandates - wallet_send (MCP)',
50
+ },
51
+ activity: {
52
+ body: 'Recent sends, swaps and yield transactions for this account, newest first.',
53
+ examples: ['botanary activity', 'botanary activity --json'],
54
+ seeAlso: 'botanary balance - wallet_activity (MCP)',
55
+ },
56
+ markets: {
57
+ body: 'Public market data: the token catalog Botanary prices, with symbols and current prices.',
58
+ examples: ['botanary markets', 'botanary markets --json'],
59
+ seeAlso: 'botanary yield - wallet_markets (MCP)',
60
+ },
61
+ yield: {
62
+ body: 'Yield pools, a shortlist ranked off your own holdings, and this account\'s farm positions. The\n' +
63
+ 'shortlist is ranked server-side and is not scoped by `accounts use`.',
64
+ examples: ['botanary yield', 'botanary yield --json'],
65
+ seeAlso: 'botanary markets - wallet_yield (MCP)',
66
+ },
67
+ mandates: {
68
+ body: 'What the agent lane may spend unattended: every delegation on this account, its bounds, and how much\n' +
69
+ 'of each budget is already spent.',
70
+ examples: ['botanary mandates', 'botanary mandates suggest', 'botanary mandates --json'],
71
+ notes: 'Mandates are testnet-only and pre-mainnet. Nothing here is enforced on mainnet yet.',
72
+ seeAlso: 'botanary agents, botanary agent grant - wallet_mandates (MCP)',
73
+ },
74
+ 'mandates suggest': {
75
+ body: 'Suggest a bounded mandate for each registered agent that does not have one yet, with a Grant URL for\n' +
76
+ 'each. A suggestion only: it never widens an existing grant, and granting happens in Botanary.',
77
+ examples: ['botanary mandates suggest', 'botanary mandates suggest --json'],
78
+ seeAlso: 'botanary mandates, botanary agents - wallet_suggest_mandate (MCP)',
79
+ },
80
+ agents: {
81
+ body: 'Connected agents on this account, and any approval requests waiting on your verdict.',
82
+ examples: ['botanary agents', 'botanary agents --json'],
83
+ seeAlso: 'botanary mandates, botanary agent requests - wallet_agents (MCP)',
84
+ },
85
+ accounts: {
86
+ body: 'Which of your accounts subsequent commands use.',
87
+ examples: ['botanary accounts list', 'botanary accounts use acct_2'],
88
+ seeAlso: 'botanary status - wallet_use_account (MCP)',
89
+ },
90
+ 'accounts list': {
91
+ body: 'Every account this login can see, with the active one marked.',
92
+ examples: ['botanary accounts list', 'botanary accounts list --json'],
93
+ seeAlso: 'botanary accounts use',
94
+ },
95
+ 'accounts use': {
96
+ body: 'Switch the active account. The id is validated against the accounts this login actually has, so a\n' +
97
+ 'typo fails here rather than silently targeting the wrong account later.',
98
+ examples: ['botanary accounts use acct_2'],
99
+ seeAlso: 'botanary accounts list - wallet_use_account (MCP)',
100
+ },
101
+ chains: {
102
+ body: 'Every chain this backend serves, and what each one can actually do:\n' +
103
+ ' watch read-only. No bundler, no authority\n' +
104
+ ' basic a bundler exists but no AgentGuard: send, swap and manage work; authority declines\n' +
105
+ ' botanary full on-chain authority, including mandates\n' +
106
+ '\n' +
107
+ 'This is the answer to "what do I pass to --chain-id".',
108
+ examples: ['botanary chains', 'botanary chains --json | jq -r ".[] | select(.tier==\\"botanary\\") | .key"'],
109
+ seeAlso: 'botanary gas, botanary send - list_chains (MCP)',
110
+ },
111
+ gas: {
112
+ body: 'The gas methods actually available for this account on a chain. The backend filters out anything\n' +
113
+ 'unavailable, so every row printed is usable. An empty list is the usual cause of a relay failing to\n' +
114
+ 'pay for itself.',
115
+ examples: ['botanary gas --chain-id 8453', 'botanary gas --chain-id 8453 --json'],
116
+ seeAlso: 'botanary chains, botanary send - get_gas_methods (MCP)',
117
+ },
118
+ api: {
119
+ body: 'Read any GET endpoint the frozen OpenAPI contract documents, authenticated as the owner. Paths are\n' +
120
+ 'validated against the contract before anything reaches the network, and there is no write\n' +
121
+ 'equivalent: every write goes through a typed intent you sign, or an on-chain-bound mandate.',
122
+ examples: [
123
+ 'botanary api /chains',
124
+ 'botanary api "/balance?focusChainRef=eip155:8453"',
125
+ 'botanary api --list',
126
+ 'botanary api --list yield',
127
+ ],
128
+ seeAlso: 'botanary balance, botanary activity - wallet_api_get (MCP)',
129
+ },
130
+ open: {
131
+ body: 'Open Botanary in your browser, optionally straight to one surface.',
132
+ examples: ['botanary open', 'botanary open mandates', 'botanary open agents'],
133
+ seeAlso: 'botanary docs',
134
+ },
135
+ agent: {
136
+ body: 'This machine\'s own agent identity: what it is, what its owner granted it, and what it may spend\n' +
137
+ 'unattended. This is never your owner session - the two lanes are deliberately separate.',
138
+ examples: ['botanary agent whoami', 'botanary agent grant', 'botanary agent pair'],
139
+ seeAlso: 'botanary whoami, botanary mandates',
140
+ },
141
+ 'agent whoami': {
142
+ body: 'What the Botanary backend says about this agent: whether it was actually claimed, which account it is\n' +
143
+ 'bound to, and its live grant if it has one. Fails honestly when this agent was never claimed rather\n' +
144
+ 'than reporting a fabricated "connected".',
145
+ examples: ['botanary agent whoami', 'botanary agent whoami --json'],
146
+ seeAlso: 'botanary agent identity, botanary agent grant, botanary diagnose - whoami (MCP)',
147
+ },
148
+ 'agent identity': {
149
+ body: 'This machine\'s local agent key: address, public key, short fingerprint, and which backend holds the\n' +
150
+ 'private key (an OS keychain, or a 0600 file when none is available). Never contacts the backend and\n' +
151
+ 'never prints the private key. Having an identity grants nothing by itself.',
152
+ examples: ['botanary agent identity', 'botanary agent identity --json'],
153
+ seeAlso: 'botanary agent whoami, botanary agent pair - get_identity (MCP)',
154
+ },
155
+ 'agent pair': {
156
+ body: 'Show the pairing code for this machine and register it with Botanary. In Botanary, open Connected\n' +
157
+ 'agents, enter the code, and name the agent. The same code comes back until it expires; --regenerate\n' +
158
+ 'forces a fresh one.',
159
+ examples: ['botanary agent pair', 'botanary agent pair --regenerate'],
160
+ notes: 'The code only identifies this agent. Completing a pairing also needs a signature from the private key, which never leaves this machine.',
161
+ seeAlso: 'botanary agent whoami - get_pairing_code (MCP)',
162
+ },
163
+ 'agent grant': {
164
+ body: 'What this agent may do right now, in plain terms, without hitting any bound to find out.',
165
+ examples: ['botanary agent grant', 'botanary agent grant --json'],
166
+ seeAlso: 'botanary agent whoami, botanary mandates - what_may_i_do (MCP)',
167
+ },
168
+ 'agent account': {
169
+ body: 'The one account this agent is bound to: its address and deployment status, optionally on one chain.',
170
+ examples: ['botanary agent account', 'botanary agent account --chain-id 84532'],
171
+ seeAlso: 'botanary agent whoami - get_account (MCP)',
172
+ },
173
+ 'agent balance': {
174
+ body: 'The bound account\'s balance, read with this agent\'s own session rather than the owner\'s.',
175
+ examples: ['botanary agent balance', 'botanary agent balance --json'],
176
+ seeAlso: 'botanary balance - get_balance (MCP)',
177
+ },
178
+ 'agent requests': {
179
+ body: 'Every approval request this agent has filed, and the owner\'s verdict on each.',
180
+ examples: ['botanary agent requests', 'botanary agent requests --json'],
181
+ seeAlso: 'botanary agent request - list_requests (MCP)',
182
+ },
183
+ 'agent request': {
184
+ body: 'Ask the owner to approve something this agent could not do under its own grant. Names the exact calls\n' +
185
+ 'and why, and returns which bound was crossed and the deadline, as the backend computed them. The\n' +
186
+ 'owner approves it as their own action, in Botanary.',
187
+ examples: [
188
+ 'botanary agent request --reason "Pay the September invoice" --calls-file ./calls.json',
189
+ 'botanary agent request --reason "Top up gas" --calls-file ./calls.json --json',
190
+ ],
191
+ notes: 'The calls file is a JSON array of { to, data, value, chainId } - the same shape a build response already carries.',
192
+ seeAlso: 'botanary agent requests, botanary agent spend - request_approval (MCP)',
193
+ },
194
+ 'agent spend': {
195
+ body: 'Spend under this agent\'s own grant, unattended. Reads the grant first: with no grant, an inactive\n' +
196
+ 'grant, the wrong chain, or an amount past its remaining budget or per-action cap, this refuses with\n' +
197
+ 'the backend\'s own numbers instead of attempting anything.',
198
+ examples: [
199
+ 'botanary agent spend --amount 5 --token USDC --recipient 0xAb..3F --chain-id 84532',
200
+ 'botanary agent spend --amount 5 --token USDC --recipient 0xAb..3F --chain-id 84532 --dry-run',
201
+ ],
202
+ notes: 'When it refuses, `botanary agent request` is the lane that asks the owner instead.',
203
+ seeAlso: 'botanary agent grant, botanary agent status - propose_payment (MCP)',
204
+ },
205
+ 'agent swap': {
206
+ body: 'Swap under this agent\'s own grant, unattended, through the one venue the grant pins.',
207
+ examples: [
208
+ 'botanary agent swap --token-in USDC --token-out ETH --amount-in 10 --chain-id 84532',
209
+ 'botanary agent swap --token-in USDC --token-out ETH --amount-in 10 --chain-id 84532 --max-slippage-bps 100',
210
+ ],
211
+ seeAlso: 'botanary agent grant - propose_swap (MCP)',
212
+ },
213
+ 'agent status': {
214
+ body: 'The current status of an action this agent relayed, by the op id spend returned: pending, included or\n' +
215
+ 'failed, with a transaction hash once one exists. Never resend a payment to find out what happened.',
216
+ examples: ['botanary agent status op_123', 'botanary agent status op_123 --json'],
217
+ seeAlso: 'botanary agent spend - get_action_status (MCP)',
218
+ },
219
+ 'agent apis': {
220
+ body: 'Paid (x402) third-party API endpoints this agent may call, with live price and remaining budget.',
221
+ examples: ['botanary agent apis --chain-id 84532', 'botanary agent apis --chain-id 84532 --json'],
222
+ seeAlso: 'botanary agent pay, botanary agent budget - list_apis (MCP)',
223
+ },
224
+ 'agent pay': {
225
+ body: 'Call a paid API endpoint and pay for it inside the budget the owner committed on chain.',
226
+ examples: ['botanary agent pay ep_1 --chain-id 84532 --provider-id prov_1 --url https://api.example/v1/thing'],
227
+ seeAlso: 'botanary agent apis, botanary agent budget - call_api (MCP)',
228
+ },
229
+ 'agent budget': {
230
+ body: 'Remaining authorizations, per-call maximum, expiry and epoch for this agent on a chain.',
231
+ examples: ['botanary agent budget --chain-id 84532', 'botanary agent budget --chain-id 84532 --json'],
232
+ seeAlso: 'botanary agent apis - get_api_budget (MCP)',
233
+ },
234
+ 'agent forget': {
235
+ body: 'Step 1 of 2. Preview what forgetting this machine\'s agent identity would destroy, and mint a\n' +
236
+ 'confirmation token valid for five minutes. Nothing is deleted by this command.',
237
+ examples: ['botanary agent forget'],
238
+ seeAlso: 'botanary agent confirm-forget - forget (MCP)',
239
+ },
240
+ 'agent confirm-forget': {
241
+ body: 'Step 2 of 2. Actually delete the agent key from this machine, given the token forget printed. The key\n' +
242
+ 'cannot be recovered, and any grant issued to it stays orphaned until the owner revokes it.',
243
+ examples: ['botanary agent confirm-forget 9f2c..'],
244
+ seeAlso: 'botanary agent forget - confirm_forget (MCP)',
245
+ },
246
+ mcp: {
247
+ body: 'Connect a coding agent (Claude Code, Codex, Cursor, Claude Desktop) to Botanary via botanary-mcp.',
248
+ examples: ['botanary mcp config claude', 'botanary mcp install claude', 'botanary mcp serve'],
249
+ seeAlso: 'botanary agent pair',
250
+ },
251
+ 'mcp config': {
252
+ body: 'Print the install snippet for one client, ready to copy.',
253
+ examples: ['botanary mcp config claude', 'botanary mcp config cursor'],
254
+ seeAlso: 'botanary mcp install',
255
+ },
256
+ 'mcp install': {
257
+ body: 'Install botanary-mcp into a client that has its own CLI for it (claude, codex), by running that\n' +
258
+ 'client\'s own command. The exact command is printed first and needs your confirmation.\n' +
259
+ '\n' +
260
+ 'For cursor and claude-desktop this prints the snippet and the config path instead: Botanary does not\n' +
261
+ 'edit another tool\'s config file on your behalf.',
262
+ examples: ['botanary mcp install claude', 'botanary mcp install codex --yes'],
263
+ seeAlso: 'botanary mcp config',
264
+ },
265
+ 'mcp serve': {
266
+ body: 'Run the botanary-mcp server on stdio from this binary, for an MCP client configured to launch\n' +
267
+ '`botanary mcp serve`. Identical to running botanary-mcp directly.',
268
+ examples: ['botanary mcp serve'],
269
+ seeAlso: 'botanary mcp config',
270
+ },
271
+ diagnose: {
272
+ body: 'Start here when anything is unclear or an action failed. One call answers who this agent is bound to,\n' +
273
+ 'whether that account is funded on the grant\'s own chain, whether that chain can do what the grant\n' +
274
+ 'claims, and what is blocking the next action, ranked, each with a concrete next step.',
275
+ examples: ['botanary diagnose', 'botanary diagnose --json'],
276
+ seeAlso: 'botanary doctor, botanary agent whoami - diagnose (MCP)',
277
+ },
278
+ doctor: {
279
+ body: 'The same readiness check as `botanary diagnose`, under the name most CLIs use for it.',
280
+ examples: ['botanary doctor'],
281
+ seeAlso: 'botanary diagnose',
282
+ },
283
+ config: {
284
+ body: 'Where identity and session live, which environment variables are active, and the CLI defaults stored\n' +
285
+ 'on this machine. Unauthenticated and safe to run any time.',
286
+ examples: ['botanary config', 'botanary config list', 'botanary config set chainId 8453'],
287
+ seeAlso: 'botanary diagnose',
288
+ },
289
+ 'config list': {
290
+ body: 'Every setting, its resolved value, and where that value came from: a flag, an environment variable,\n' +
291
+ 'the config file, or the built-in default. The provenance column is the point - a stale file value\n' +
292
+ 'that overrides nothing looks identical to one silently retargeting every command otherwise.',
293
+ examples: ['botanary config list', 'botanary config list --json'],
294
+ seeAlso: 'botanary config set',
295
+ },
296
+ 'config get': {
297
+ body: 'Print one stored config value, or nothing when it is unset.',
298
+ examples: ['botanary config get chainId'],
299
+ seeAlso: 'botanary config list',
300
+ },
301
+ 'config set': {
302
+ body: 'Store a default for one setting, so you stop retyping it. Unknown keys and values are refused.\n' +
303
+ 'Warning: `config set confirm never` auto-confirms all mutations. Do not set this in an untrusted terminal.',
304
+ examples: ['botanary config set chainId 8453', 'botanary config set output json'],
305
+ seeAlso: 'botanary config list, botanary config unset',
306
+ },
307
+ 'config unset': {
308
+ body: 'Remove one stored default, falling back to the environment or the built-in default.',
309
+ examples: ['botanary config unset chainId'],
310
+ seeAlso: 'botanary config list',
311
+ },
312
+ version: {
313
+ body: 'The installed botanary CLI version.',
314
+ examples: ['botanary version', 'botanary --version'],
315
+ seeAlso: 'botanary update-check',
316
+ },
317
+ 'update-check': {
318
+ body: 'Ask npm whether a newer botanary is published. Cached for a day, silent when offline, and never\n' +
319
+ 'self-updating - it prints the upgrade command and leaves the decision to you.',
320
+ examples: ['botanary update-check', 'botanary update-check --json'],
321
+ notes: 'Set BOTANARY_NO_UPDATE_NOTIFIER=1 to suppress the one-line nag other commands print.',
322
+ seeAlso: 'botanary version',
323
+ },
324
+ docs: {
325
+ body: 'Open the Botanary documentation in your browser, optionally at one topic.',
326
+ examples: ['botanary docs', 'botanary docs mandates'],
327
+ seeAlso: 'botanary open',
328
+ },
329
+ completion: {
330
+ body: 'Print a shell completion script. Add it to your shell profile to complete command names, flags,\n' +
331
+ 'chain ids and documented API paths.',
332
+ examples: [
333
+ 'botanary completion zsh > ~/.botanary-completion.zsh',
334
+ 'botanary completion bash >> ~/.bashrc',
335
+ ],
336
+ seeAlso: 'botanary help',
337
+ },
338
+ prompt: {
339
+ body: 'Print the complete operating manual for a non-human driver: every command, flags-only invocation,\n' +
340
+ 'the exit-code table, the JSON envelope, the two lanes, and which steps still need a human. Generated\n' +
341
+ 'from the same metadata as --help, so it cannot drift from it.',
342
+ examples: ['botanary prompt', 'botanary prompt > BOTANARY.md'],
343
+ seeAlso: 'botanary completion, botanary mcp config',
344
+ },
345
+ };
346
+ /** The command's full path with the program name stripped: '', 'send', 'agent spend'. */
347
+ export function commandPath(cmd) {
348
+ const parts = [];
349
+ for (let c = cmd; c && c.parent; c = c.parent) {
350
+ parts.unshift(c.name());
351
+ }
352
+ return parts.join(' ');
353
+ }
354
+ export function helpFor(cmd) {
355
+ return COMMAND_HELP[commandPath(cmd)];
356
+ }
357
+ //# sourceMappingURL=examples.js.map