@almyty/cli 1.2.0 → 1.3.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.
package/README.md CHANGED
@@ -1,40 +1,102 @@
1
1
  # @almyty/cli
2
2
 
3
- Umbrella CLI for almyty. One install, every command.
3
+ Umbrella CLI for almyty. One install, one login, every command.
4
4
 
5
5
  ## Quick start
6
6
 
7
7
  ```bash
8
8
  $ npm install -g @almyty/cli
9
+ $ almyty # a short tour
9
10
  $ almyty login
10
11
  $ almyty agents list
11
12
  ```
12
13
 
14
+ `almyty` on its own prints a five-line tour. `almyty help` prints the
15
+ full reference, and `almyty <command> --help` prints that command's own
16
+ options — the umbrella forwards `--help` to the package it delegates to.
17
+
13
18
  ## Commands
14
19
 
15
20
  | Command | Delegates to | Description |
16
21
  |---------|-------------|-------------|
17
- | `almyty login` | `@almyty/auth` | Browser-based login |
22
+ | `almyty login` | `@almyty/auth` | Browser-based login (writes `~/.almyty/credentials.json`) |
18
23
  | `almyty logout` | `@almyty/auth` | Remove stored credentials |
19
- | `almyty whoami` | `@almyty/auth` | Show current identity |
20
- | `almyty agents <cmd>` | `@almyty/agents` | List, run, inspect agents |
21
- | `almyty chat [org/slug]` | `@almyty/chat` | Interactive chat REPL |
22
- | `almyty skills <cmd>` | `@almyty/skills` | Install and manage skills |
23
- | `almyty mcp <args>` | `@almyty/mcp-server` | Start MCP server |
24
+ | `almyty whoami` | `@almyty/auth` | Show the current identity and when it expires |
25
+ | `almyty auth <cmd>` | `@almyty/auth` | The same three, spelled out |
26
+ | `almyty agents <cmd>` | `@almyty/agents` | List, run, inspect and trace agents |
27
+ | `almyty chat [ref]` | `@almyty/chat` | Interactive chat REPL |
28
+ | `almyty skills <cmd>` | `@almyty/skills` | Install API skills into AI coding agents |
29
+ | `almyty models <cmd>` | `@almyty/models` | Model catalog: cards, validation, deployments |
30
+ | `almyty connections <cmd>` | `@almyty/connections` | Connect third-party accounts, validate, grant |
31
+ | `almyty runner <cmd>` | `@almyty/runner` | Run agents on this machine as a daemon |
32
+ | `almyty mcp <args>` | `@almyty/mcp-server` | Serve your agents and tools over MCP |
24
33
  | `almyty acp <args>` | `@almyty/acp-server` | Serve an agent over the Agent Client Protocol |
25
- | `almyty runner <cmd>` | `@almyty/runner` | Run a machine daemon (`runner start`, `runner status`) |
34
+
35
+ Handled by the umbrella itself:
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `almyty help`, `--help` | The full command reference |
40
+ | `almyty version`, `--version` | The installed version |
41
+ | `almyty completion <shell>` | A completion script for `bash`, `zsh` or `fish` |
42
+
43
+ An unknown command exits `2` and suggests the nearest real one
44
+ (`almyty agent list` → "Did you mean `almyty agents`?").
45
+
46
+ ## Shell completion
47
+
48
+ ```bash
49
+ # bash — add to ~/.bashrc
50
+ eval "$(almyty completion bash)"
51
+
52
+ # zsh — add to ~/.zshrc
53
+ eval "$(almyty completion zsh)"
54
+
55
+ # fish
56
+ almyty completion fish > ~/.config/fish/completions/almyty.fish
57
+ ```
58
+
59
+ ## Exit codes
60
+
61
+ The same table in every almyty CLI, so `almyty <anything>` can be
62
+ scripted the same way:
63
+
64
+ | Code | Meaning |
65
+ |------|---------|
66
+ | `0` | success |
67
+ | `1` | unexpected error |
68
+ | `2` | usage error (bad flags, unknown command) |
69
+ | `3` | not authenticated — run `almyty login` |
70
+ | `4` | not found (agent, gateway, skill, or run) |
71
+ | `5` | the operation ran and failed |
72
+
73
+ ```bash
74
+ almyty agents run deploy-check --watch || case $? in
75
+ 3) almyty login ;;
76
+ 5) echo "the check failed"; exit 1 ;;
77
+ esac
78
+ ```
26
79
 
27
80
  ## Standalone packages
28
81
 
29
- Every subcommand is also available as a standalone package via `npx`:
82
+ Every subcommand is also a standalone package, so the umbrella is a
83
+ convenience and not a requirement:
30
84
 
31
85
  ```bash
86
+ $ npx @almyty/auth login
32
87
  $ npx @almyty/agents list
33
88
  $ npx @almyty/chat myorg/my-bot
34
- $ npx @almyty/skills install @org/gateway
89
+ $ npx @almyty/skills install org/gateway
35
90
  ```
36
91
 
37
- All packages share the same credentials file (`~/.almyty/credentials.json`), so logging in once works everywhere.
92
+ They all read the same credentials file (`~/.almyty/credentials.json`),
93
+ so logging in once works everywhere.
94
+
95
+ ## Piping
96
+
97
+ Every read command takes `--json` and prints nothing but JSON on stdout.
98
+ No almyty CLI emits ANSI colour of its own, so `NO_COLOR` is honoured
99
+ and there is nothing to strip when you pipe.
38
100
 
39
101
  ## About almyty
40
102
 
@@ -42,8 +104,10 @@ almyty is the full-stack platform for AI agents, agnostic by design: any LLM, an
42
104
  API turned into tools, served over MCP, A2A, UTCP, and Agent Skills. Open source,
43
105
  no lock-in.
44
106
 
45
- - Website — https://almyty.com
46
- - Docs — https://docs.almyty.com
47
- - Source — https://github.com/almyty-inc/almyty
107
+ - Website: https://almyty.com
108
+ - Docs: https://docs.almyty.com
109
+ - Source: https://github.com/almyty-inc/almyty
110
+
111
+ This CLI is part of the `@almyty/*` suite (versioned together at 1.x) and works with the almyty platform 0.1 and later.
48
112
 
49
113
  Apache-2.0 © Almyty Inc.
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The umbrella's routing table, help text, and shell completion.
3
+ *
4
+ * Kept out of index.ts so it can be unit-tested without spawning a
5
+ * child process: every entry here must be listed in the help text and
6
+ * must name a package the umbrella actually depends on, and there are
7
+ * tests that assert both.
8
+ */
9
+ export interface Subcommand {
10
+ /** Package name to delegate to. */
11
+ pkg: string;
12
+ /**
13
+ * Optional argv prefix injected before the user-supplied args.
14
+ * Used for top-level shortcuts like `almyty login` -> `@almyty/auth login`.
15
+ */
16
+ prefixArgs?: string[];
17
+ /** Short help line shown by `almyty help`. */
18
+ help: string;
19
+ /** Section heading this command is grouped under in the help output. */
20
+ group: string;
21
+ /** Subcommand names, for shell completion. Empty when it takes free-form args. */
22
+ subcommands?: string[];
23
+ }
24
+ /**
25
+ * Top-level subcommand routing table. Order = display order in help.
26
+ *
27
+ * Every `pkg` here must be a dependency of @almyty/cli. `models` and
28
+ * `connections` were listed in the help text while the umbrella did
29
+ * not depend on them, so `almyty models list` answered "package
30
+ * @almyty/models is not installed" for a command its own --help
31
+ * advertised.
32
+ */
33
+ export declare const SUBCOMMANDS: Record<string, Subcommand>;
34
+ /** Commands the umbrella answers itself, rather than delegating. */
35
+ export declare const BUILTIN_COMMANDS: readonly ["help", "version", "completion"];
36
+ /** Every name `almyty <x>` accepts, for completion and suggestions. */
37
+ export declare function allCommandNames(): string[];
38
+ /**
39
+ * A short tour, printed when `almyty` is run with no arguments.
40
+ *
41
+ * A bare `almyty` used to print the entire command reference, which is
42
+ * the least useful thing to show someone who has just installed it and
43
+ * does not yet know what the product does.
44
+ */
45
+ export declare function tourText(version: string): string;
46
+ /** The full command reference, printed by `almyty help` / `--help`. */
47
+ export declare function helpText(version: string): string;
48
+ /**
49
+ * The closest command name to what the user typed, or null.
50
+ * `almyty agent list` (singular) should not just say "unknown command".
51
+ */
52
+ export declare function suggestCommand(input: string): string | null;
53
+ export declare const COMPLETION_SHELLS: readonly ["bash", "zsh", "fish"];
54
+ export type CompletionShell = (typeof COMPLETION_SHELLS)[number];
55
+ export declare function isCompletionShell(value: string): value is CompletionShell;
56
+ /**
57
+ * A completion script for one shell. Static — it lists the top-level
58
+ * commands and each one's own subcommands from the table above, so it
59
+ * never has to shell out to the sub-CLIs to build a candidate list.
60
+ */
61
+ export declare function completionScript(shell: CompletionShell): string;
@@ -0,0 +1,204 @@
1
+ /**
2
+ * The umbrella's routing table, help text, and shell completion.
3
+ *
4
+ * Kept out of index.ts so it can be unit-tested without spawning a
5
+ * child process: every entry here must be listed in the help text and
6
+ * must name a package the umbrella actually depends on, and there are
7
+ * tests that assert both.
8
+ */
9
+ /**
10
+ * Top-level subcommand routing table. Order = display order in help.
11
+ *
12
+ * Every `pkg` here must be a dependency of @almyty/cli. `models` and
13
+ * `connections` were listed in the help text while the umbrella did
14
+ * not depend on them, so `almyty models list` answered "package
15
+ * @almyty/models is not installed" for a command its own --help
16
+ * advertised.
17
+ */
18
+ export const SUBCOMMANDS = {
19
+ // Auth shortcuts at top level (gh-style: `almyty login` not `almyty auth login`)
20
+ login: { pkg: '@almyty/auth', prefixArgs: ['login'], group: 'Auth', help: 'Browser-based login (writes ~/.almyty/credentials.json)' },
21
+ logout: { pkg: '@almyty/auth', prefixArgs: ['logout'], group: 'Auth', help: 'Remove stored credentials' },
22
+ whoami: { pkg: '@almyty/auth', prefixArgs: ['whoami'], group: 'Auth', help: 'Show the current identity' },
23
+ auth: { pkg: '@almyty/auth', group: 'Auth', help: 'Auth subcommands', subcommands: ['login', 'logout', 'whoami'] },
24
+ // Domain CLIs
25
+ agents: { pkg: '@almyty/agents', group: 'Agents', help: 'List, run, and inspect agents', subcommands: ['list', 'get', 'run', 'runs', 'inspect', 'executions', 'trace', 'cancel'] },
26
+ chat: { pkg: '@almyty/chat', group: 'Agents', help: 'Interactive chat REPL with an agent' },
27
+ skills: { pkg: '@almyty/skills', group: 'Skills', help: 'Install API skills into AI coding agents', subcommands: ['install', 'list', 'search', 'run', 'installed', 'remove', 'gateways', 'daemon', 'watch'] },
28
+ models: { pkg: '@almyty/models', group: 'Platform', help: 'Model catalog: cards, validation, deployments' },
29
+ connections: { pkg: '@almyty/connections', group: 'Platform', help: 'Connect third-party accounts: connectors, validate, grants' },
30
+ runner: { pkg: '@almyty/runner', group: 'Serving', help: 'Run agents on this machine as a daemon' },
31
+ mcp: { pkg: '@almyty/mcp-server', group: 'Serving', help: 'Serve your agents and tools over MCP' },
32
+ acp: { pkg: '@almyty/acp-server', group: 'Serving', help: 'Serve an agent over the Agent Client Protocol' },
33
+ };
34
+ /** Commands the umbrella answers itself, rather than delegating. */
35
+ export const BUILTIN_COMMANDS = ['help', 'version', 'completion'];
36
+ /** Every name `almyty <x>` accepts, for completion and suggestions. */
37
+ export function allCommandNames() {
38
+ return [...Object.keys(SUBCOMMANDS), ...BUILTIN_COMMANDS];
39
+ }
40
+ /**
41
+ * A short tour, printed when `almyty` is run with no arguments.
42
+ *
43
+ * A bare `almyty` used to print the entire command reference, which is
44
+ * the least useful thing to show someone who has just installed it and
45
+ * does not yet know what the product does.
46
+ */
47
+ export function tourText(version) {
48
+ return `almyty v${version} — build, run, and serve AI agents.
49
+
50
+ Start here:
51
+ almyty login Authenticate this machine
52
+ almyty agents list See the agents in your organization
53
+ almyty agents run <agent> Run one and print its output
54
+ almyty chat <agent> Talk to one in your terminal
55
+ almyty skills install <ref> Teach your coding agent an API
56
+
57
+ almyty help Every command, grouped
58
+ almyty <command> --help Detail for one command
59
+
60
+ Docs: https://docs.almyty.com`;
61
+ }
62
+ /** The full command reference, printed by `almyty help` / `--help`. */
63
+ export function helpText(version) {
64
+ const groups = new Map();
65
+ const width = Math.max(...Object.keys(SUBCOMMANDS).map((n) => n.length), 'completion <shell>'.length);
66
+ for (const [name, sub] of Object.entries(SUBCOMMANDS)) {
67
+ const lines = groups.get(sub.group) ?? [];
68
+ lines.push(` ${name.padEnd(width)} ${sub.help}`);
69
+ groups.set(sub.group, lines);
70
+ }
71
+ const sections = [...groups.entries()]
72
+ .map(([group, lines]) => `${group}:\n${lines.join('\n')}`)
73
+ .join('\n\n');
74
+ return `almyty CLI v${version}
75
+
76
+ Usage:
77
+ almyty <command> [args]
78
+ almyty <command> --help Full options for that command
79
+
80
+ ${sections}
81
+
82
+ Other:
83
+ ${'help'.padEnd(width)} Show this help
84
+ ${'version'.padEnd(width)} Print the version
85
+ ${'completion <shell>'.padEnd(width)} Print a completion script (bash, zsh, fish)
86
+
87
+ Exit codes (the same in every almyty CLI):
88
+ 0 success
89
+ 1 unexpected error
90
+ 2 usage error (bad flags, unknown command)
91
+ 3 not authenticated — run \`almyty login\`
92
+ 4 not found (agent, gateway, skill, or run)
93
+ 5 the operation ran and failed
94
+
95
+ Every subcommand is also a standalone package, so nothing here is
96
+ required in order to use one of them:
97
+
98
+ npx @almyty/auth login
99
+ npx @almyty/agents list
100
+ npx @almyty/chat my-research-bot
101
+ npx @almyty/skills install org/gateway
102
+
103
+ They all read the same credentials file (~/.almyty/credentials.json),
104
+ so \`almyty login\` logs all of them in at once.`;
105
+ }
106
+ /** Levenshtein distance, capped — only used to suggest a command name. */
107
+ function distance(a, b) {
108
+ const rows = a.length + 1;
109
+ const cols = b.length + 1;
110
+ let prev = Array.from({ length: cols }, (_, j) => j);
111
+ for (let i = 1; i < rows; i++) {
112
+ const curr = [i];
113
+ for (let j = 1; j < cols; j++) {
114
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
115
+ }
116
+ prev = curr;
117
+ }
118
+ return prev[cols - 1];
119
+ }
120
+ /**
121
+ * The closest command name to what the user typed, or null.
122
+ * `almyty agent list` (singular) should not just say "unknown command".
123
+ */
124
+ export function suggestCommand(input) {
125
+ const typed = input.toLowerCase();
126
+ let best = null;
127
+ let bestScore = Infinity;
128
+ for (const name of allCommandNames()) {
129
+ const d = distance(typed, name);
130
+ if (d < bestScore) {
131
+ bestScore = d;
132
+ best = name;
133
+ }
134
+ }
135
+ const tolerance = typed.length <= 4 ? 1 : 2;
136
+ return best !== null && bestScore <= tolerance ? best : null;
137
+ }
138
+ export const COMPLETION_SHELLS = ['bash', 'zsh', 'fish'];
139
+ export function isCompletionShell(value) {
140
+ return COMPLETION_SHELLS.includes(value);
141
+ }
142
+ /**
143
+ * A completion script for one shell. Static — it lists the top-level
144
+ * commands and each one's own subcommands from the table above, so it
145
+ * never has to shell out to the sub-CLIs to build a candidate list.
146
+ */
147
+ export function completionScript(shell) {
148
+ const top = allCommandNames().join(' ');
149
+ if (shell === 'bash') {
150
+ const cases = Object.entries(SUBCOMMANDS)
151
+ .filter(([, s]) => s.subcommands?.length)
152
+ .map(([name, s]) => ` ${name}) subs="${s.subcommands.join(' ')}" ;;`)
153
+ .join('\n');
154
+ return `# almyty bash completion — add to ~/.bashrc:
155
+ # eval "$(almyty completion bash)"
156
+ _almyty_complete() {
157
+ local cur prev subs
158
+ cur="\${COMP_WORDS[COMP_CWORD]}"
159
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
160
+ if [ "$COMP_CWORD" -eq 1 ]; then
161
+ COMPREPLY=( $(compgen -W "${top}" -- "$cur") )
162
+ return
163
+ fi
164
+ subs=""
165
+ case "\${COMP_WORDS[1]}" in
166
+ ${cases}
167
+ completion) subs="bash zsh fish" ;;
168
+ esac
169
+ if [ -n "$subs" ] && [ "$COMP_CWORD" -eq 2 ]; then
170
+ COMPREPLY=( $(compgen -W "$subs" -- "$cur") )
171
+ fi
172
+ }
173
+ complete -F _almyty_complete almyty`;
174
+ }
175
+ if (shell === 'zsh') {
176
+ const descs = Object.entries(SUBCOMMANDS)
177
+ .map(([name, s]) => ` '${name}:${s.help.replace(/'/g, '')}'`)
178
+ .join('\n');
179
+ return `# almyty zsh completion — add to ~/.zshrc:
180
+ # eval "$(almyty completion zsh)"
181
+ _almyty() {
182
+ local -a commands
183
+ commands=(
184
+ ${descs}
185
+ 'help:Show help'
186
+ 'version:Print the version'
187
+ 'completion:Print a shell completion script'
188
+ )
189
+ if (( CURRENT == 2 )); then
190
+ _describe -t commands 'almyty command' commands
191
+ fi
192
+ }
193
+ compdef _almyty almyty`;
194
+ }
195
+ const fishLines = Object.entries(SUBCOMMANDS)
196
+ .map(([name, s]) => `complete -c almyty -n __fish_use_subcommand -a ${name} -d '${s.help.replace(/'/g, '')}'`)
197
+ .join('\n');
198
+ return `# almyty fish completion — save as ~/.config/fish/completions/almyty.fish:
199
+ # almyty completion fish > ~/.config/fish/completions/almyty.fish
200
+ ${fishLines}
201
+ complete -c almyty -n __fish_use_subcommand -a help -d 'Show help'
202
+ complete -c almyty -n __fish_use_subcommand -a version -d 'Print the version'
203
+ complete -c almyty -n __fish_use_subcommand -a completion -d 'Print a shell completion script'`;
204
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Exit codes shared by every almyty CLI.
3
+ *
4
+ * Scripts need to tell "you are not logged in" apart from "that agent
5
+ * does not exist" apart from "the run you asked for failed" without
6
+ * grepping stderr. Every almyty CLI uses this same table, so
7
+ * `almyty agents run x || case $? in 3) almyty login;; esac` behaves
8
+ * the same whichever binary produced the code.
9
+ */
10
+ export declare const EXIT: {
11
+ /** Success. */
12
+ readonly OK: 0;
13
+ /** Unexpected failure (a thrown error with no better classification). */
14
+ readonly ERROR: 1;
15
+ /** Bad or missing arguments, or an unknown command. */
16
+ readonly USAGE: 2;
17
+ /** No stored credential, or the API rejected the one we had. */
18
+ readonly AUTH: 3;
19
+ /** The named agent / gateway / skill / run does not exist. */
20
+ readonly NOT_FOUND: 4;
21
+ /** The command ran; the operation it asked for failed. */
22
+ readonly FAILED: 5;
23
+ };
24
+ export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
25
+ /** One line per code, for `--help` output and READMEs. */
26
+ export declare const EXIT_CODE_HELP: string;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Exit codes shared by every almyty CLI.
3
+ *
4
+ * Scripts need to tell "you are not logged in" apart from "that agent
5
+ * does not exist" apart from "the run you asked for failed" without
6
+ * grepping stderr. Every almyty CLI uses this same table, so
7
+ * `almyty agents run x || case $? in 3) almyty login;; esac` behaves
8
+ * the same whichever binary produced the code.
9
+ */
10
+ export const EXIT = {
11
+ /** Success. */
12
+ OK: 0,
13
+ /** Unexpected failure (a thrown error with no better classification). */
14
+ ERROR: 1,
15
+ /** Bad or missing arguments, or an unknown command. */
16
+ USAGE: 2,
17
+ /** No stored credential, or the API rejected the one we had. */
18
+ AUTH: 3,
19
+ /** The named agent / gateway / skill / run does not exist. */
20
+ NOT_FOUND: 4,
21
+ /** The command ran; the operation it asked for failed. */
22
+ FAILED: 5,
23
+ };
24
+ /** One line per code, for `--help` output and READMEs. */
25
+ export const EXIT_CODE_HELP = [
26
+ ' 0 success',
27
+ ' 1 unexpected error',
28
+ ' 2 usage error (bad flags, unknown command)',
29
+ ' 3 not authenticated — run `almyty login`',
30
+ ' 4 not found (agent, gateway, skill, or run)',
31
+ ' 5 the operation ran and failed',
32
+ ].join('\n');
package/dist/index.d.ts CHANGED
@@ -6,8 +6,7 @@
6
6
  * almyty login
7
7
  * almyty agents list
8
8
  * almyty chat my-research-bot
9
- * almyty skills install @org/gateway
10
- * almyty mcp <args>
9
+ * almyty skills install org/gateway
11
10
  *
12
11
  * The umbrella delegates each subcommand to a standalone @almyty/<thing>
13
12
  * package by spawning its bin. Each package can ALSO be invoked directly:
@@ -15,7 +14,6 @@
15
14
  * npx @almyty/auth login
16
15
  * npx @almyty/agents list
17
16
  * npx @almyty/chat my-research-bot
18
- * …
19
17
  *
20
18
  * The standalone packages and the umbrella stay in sync because they
21
19
  * share the same on-disk credentials store at ~/.almyty/credentials.json.
package/dist/index.js CHANGED
@@ -6,8 +6,7 @@
6
6
  * almyty login
7
7
  * almyty agents list
8
8
  * almyty chat my-research-bot
9
- * almyty skills install @org/gateway
10
- * almyty mcp <args>
9
+ * almyty skills install org/gateway
11
10
  *
12
11
  * The umbrella delegates each subcommand to a standalone @almyty/<thing>
13
12
  * package by spawning its bin. Each package can ALSO be invoked directly:
@@ -15,84 +14,18 @@
15
14
  * npx @almyty/auth login
16
15
  * npx @almyty/agents list
17
16
  * npx @almyty/chat my-research-bot
18
- * …
19
17
  *
20
18
  * The standalone packages and the umbrella stay in sync because they
21
19
  * share the same on-disk credentials store at ~/.almyty/credentials.json.
22
20
  */
23
- import { spawn } from 'child_process';
24
- import { createRequire } from 'module';
25
- import { dirname, join } from 'path';
26
- import { existsSync } from 'fs';
21
+ import { spawn } from 'node:child_process';
22
+ import { createRequire } from 'node:module';
23
+ import { dirname, join } from 'node:path';
24
+ import { existsSync } from 'node:fs';
25
+ import { VERSION } from './version.js';
26
+ import { EXIT } from './exit-codes.js';
27
+ import { SUBCOMMANDS, completionScript, helpText, isCompletionShell, suggestCommand, tourText, COMPLETION_SHELLS, } from './commands.js';
27
28
  const require = createRequire(import.meta.url);
28
- const VERSION = '0.1.0';
29
- /**
30
- * Top-level subcommand routing table. Order = display order in help.
31
- */
32
- const SUBCOMMANDS = {
33
- // Auth shortcuts at top level (gh-style: `almyty login` not `almyty auth login`)
34
- login: { pkg: '@almyty/auth', prefixArgs: ['login'], help: 'Browser-based login' },
35
- logout: { pkg: '@almyty/auth', prefixArgs: ['logout'], help: 'Remove stored credentials' },
36
- whoami: { pkg: '@almyty/auth', prefixArgs: ['whoami'], help: 'Show the current identity' },
37
- auth: { pkg: '@almyty/auth', help: 'Auth subcommands (login/logout/whoami)' },
38
- // Domain CLIs
39
- agents: { pkg: '@almyty/agents', help: 'List, run, and inspect agents' },
40
- chat: { pkg: '@almyty/chat', help: 'Interactive chat REPL with an agent' },
41
- skills: { pkg: '@almyty/skills', help: 'Install API skills into AI coding agents' },
42
- mcp: { pkg: '@almyty/mcp-server', help: 'Run the MCP server proxy' },
43
- acp: { pkg: '@almyty/acp-server', help: 'Run the ACP agent server' },
44
- runner: { pkg: '@almyty/runner', help: 'Long-running daemon to run agents on this machine' },
45
- };
46
- function printHelp() {
47
- console.log(`
48
- almyty CLI v${VERSION}
49
-
50
- Usage:
51
- almyty <command> [args]
52
-
53
- Auth:
54
- login Browser-based login (writes ~/.almyty/credentials.json)
55
- logout Remove stored credentials
56
- whoami Show the currently logged-in identity
57
- auth <subcommand> Pass-through to @almyty/auth
58
-
59
- Agents:
60
- agents list List agents in your organization
61
- agents get <ref> Show details for one agent
62
- agents run <ref> [--input ...] Invoke / start a run
63
- agents runs <ref> List recent runs
64
- agents cancel <ref> <runId> Cancel an in-flight run
65
-
66
- Chat:
67
- chat Interactive REPL — pick an agent from a menu
68
- chat <ref> Start chatting with that agent
69
-
70
- Skills:
71
- skills install <ref> Install skills into local AI coding agents
72
- skills list [ref] List available skills
73
- skills daemon Sync skills on a schedule
74
- skills run <ref> Execute a skill once
75
-
76
- MCP:
77
- mcp <args> Run the MCP server proxy
78
-
79
- Runner:
80
- runner start [--name n] Start a runner daemon, register with the SaaS
81
- runner status Show local runner status
82
-
83
- Other:
84
- help, --help Show this help
85
- version, --version Show version
86
-
87
- Each subcommand maps to a standalone npm package. You can invoke any
88
- of them directly with npx:
89
-
90
- npx @almyty/auth login
91
- npx @almyty/agents list
92
- npx @almyty/chat my-research-bot
93
- npx @almyty/skills install @org/gateway
94
- `);
95
- }
96
29
  function resolveBinPath(packageName) {
97
30
  // Resolve the package's package.json to find its installation root,
98
31
  // then read the "bin" entry to find the actual script path.
@@ -133,8 +66,8 @@ function delegate(packageName, args) {
133
66
  if (!binPath) {
134
67
  console.error(`Error: package ${packageName} is not installed.`);
135
68
  console.error(` Install it with: npm install -g ${packageName}`);
136
- console.error(` Or use: npx ${packageName} ${args.join(' ')}`);
137
- return Promise.resolve(1);
69
+ console.error(` Or use: npx ${packageName} ${args.join(' ')}`);
70
+ return Promise.resolve(EXIT.ERROR);
138
71
  }
139
72
  return new Promise((resolve) => {
140
73
  const child = spawn(process.execPath, [binPath, ...args], {
@@ -162,30 +95,49 @@ function delegate(packageName, args) {
162
95
  process.kill(process.pid, signal);
163
96
  return;
164
97
  }
165
- resolve(code ?? 0);
98
+ resolve(code ?? EXIT.OK);
166
99
  });
167
100
  child.on('error', (err) => {
168
101
  console.error(`Failed to launch ${packageName}: ${err.message}`);
169
- resolve(1);
102
+ resolve(EXIT.ERROR);
170
103
  });
171
104
  });
172
105
  }
173
106
  async function main() {
174
107
  const argv = process.argv.slice(2);
175
- if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h' || argv[0] === 'help') {
176
- printHelp();
108
+ // Bare `almyty` shows a short tour, not the full reference.
109
+ if (argv.length === 0) {
110
+ console.log(tourText(VERSION));
111
+ return;
112
+ }
113
+ if (argv[0] === '--help' || argv[0] === '-h' || argv[0] === 'help') {
114
+ console.log(helpText(VERSION));
177
115
  return;
178
116
  }
179
117
  if (argv[0] === '--version' || argv[0] === '-v' || argv[0] === 'version') {
180
118
  console.log(VERSION);
181
119
  return;
182
120
  }
121
+ if (argv[0] === 'completion') {
122
+ const shell = argv[1];
123
+ if (!shell || !isCompletionShell(shell)) {
124
+ console.error(`Usage: almyty completion <${COMPLETION_SHELLS.join('|')}>`);
125
+ process.exit(EXIT.USAGE);
126
+ }
127
+ console.log(completionScript(shell));
128
+ return;
129
+ }
183
130
  const cmd = argv[0];
184
131
  const sub = SUBCOMMANDS[cmd];
185
132
  if (!sub) {
186
133
  console.error(`Unknown command: ${cmd}`);
187
- console.error(`Run \`almyty help\` for the list of commands.`);
188
- process.exit(1);
134
+ const suggestion = suggestCommand(cmd);
135
+ if (suggestion)
136
+ console.error(`Did you mean \`almyty ${suggestion}\`?`);
137
+ console.error('Run `almyty help` for the list of commands.');
138
+ // Usage error, not a generic failure: a script can tell a typo
139
+ // apart from a command that ran and failed.
140
+ process.exit(EXIT.USAGE);
189
141
  }
190
142
  const passthroughArgs = [...(sub.prefixArgs ?? []), ...argv.slice(1)];
191
143
  const code = await delegate(sub.pkg, passthroughArgs);
@@ -193,5 +145,5 @@ async function main() {
193
145
  }
194
146
  main().catch((err) => {
195
147
  console.error(`Error: ${err.message}`);
196
- process.exit(1);
148
+ process.exit(EXIT.ERROR);
197
149
  });
@@ -0,0 +1,2 @@
1
+ export declare function readVersion(fallback?: string): string;
2
+ export declare const VERSION: string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The CLI's own version, read from its package.json at startup.
3
+ *
4
+ * It used to be a hardcoded string, which drifted: `--version`
5
+ * answered 0.1.0 while the published package was 1.2.0, so a bug
6
+ * report never identified the build it came from. Both `dist/index.js`
7
+ * and `src/index.ts` sit one directory below the package root, so the
8
+ * same relative path resolves for the built bin and for `tsx src/index.ts`.
9
+ */
10
+ import { readFileSync } from 'node:fs';
11
+ export function readVersion(fallback = '0.0.0') {
12
+ try {
13
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
14
+ return typeof pkg.version === 'string' && pkg.version.length > 0
15
+ ? pkg.version
16
+ : fallback;
17
+ }
18
+ catch {
19
+ return fallback;
20
+ }
21
+ }
22
+ export const VERSION = readVersion();
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@almyty/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "description": "The almyty CLI \u2014 one login for agents, chat, skills, an MCP server, and a machine runner. Part of almyty, the full-stack platform for AI agents.",
7
+ "description": "The almyty CLI — one login for agents, chat, skills, an MCP server, and a machine runner. Part of almyty, the full-stack platform for AI agents.",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",
10
10
  "bin": {
@@ -35,6 +35,8 @@
35
35
  "@almyty/agents": "^1.2.0",
36
36
  "@almyty/chat": "^1.2.0",
37
37
  "@almyty/skills": "^1.2.0",
38
+ "@almyty/models": "^1.2.0",
39
+ "@almyty/connections": "^1.2.0",
38
40
  "@almyty/mcp-server": "^1.2.0",
39
41
  "@almyty/acp-server": "^1.2.0",
40
42
  "@almyty/runner": "^1.2.0"
@@ -45,6 +47,12 @@
45
47
  "typescript": "^5.3.0",
46
48
  "vitest": "^4.1.0"
47
49
  },
50
+ "overrides": {
51
+ "@hono/node-server": "^2.0.5",
52
+ "fast-uri": "^3.1.5",
53
+ "ip-address": "^10.4.0",
54
+ "postcss": "^8.5.23"
55
+ },
48
56
  "homepage": "https://almyty.com",
49
57
  "repository": {
50
58
  "type": "git",