@phnx-labs/agents-cli 1.20.22 → 1.20.23

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.
@@ -19,11 +19,19 @@
19
19
  * the droid binary + a configured computer so the provider fails with a clear
20
20
  * message rather than misfiring when unconfigured.
21
21
  */
22
- import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, DispatchOptions, ProviderCapabilities, DroidAutonomy } from './types.js';
22
+ import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, CloudTarget, DispatchOptions, ProviderCapabilities, DroidAutonomy } from './types.js';
23
23
  /** Locate the droid binary, checking agents-cli shims first then PATH. */
24
24
  export declare function findDroidBinary(): string | null;
25
25
  /** Normalize an autonomy value, falling back to the safe cloud default (`high`). */
26
26
  export declare function resolveAutonomy(value: unknown, fallback?: DroidAutonomy): DroidAutonomy;
27
+ /**
28
+ * Parse `droid computer list` text into targets. Defensive: the exact column
29
+ * layout isn't documented, so we take the first whitespace token of each data
30
+ * row as the computer name and keep the remainder as a label, skipping headers,
31
+ * separators, and status messages. The interactive picker degrades to free-text
32
+ * entry if this yields nothing, so an unexpected layout never blocks a dispatch.
33
+ */
34
+ export declare function parseComputerList(text: string): CloudTarget[];
27
35
  /**
28
36
  * Build the remote `droid exec` argv. Headless, stream-json output, given
29
37
  * autonomy. `sessionId` (when resuming) maps to `-s`.
@@ -61,6 +69,7 @@ export declare function mapDroidEvent(obj: Record<string, unknown>): CloudEvent;
61
69
  export declare class FactoryCloudProvider implements CloudProvider {
62
70
  id: "factory";
63
71
  name: string;
72
+ targetKind: "computer";
64
73
  private defaultComputer?;
65
74
  private defaultAutonomy;
66
75
  /** session_id → buffered run, populated by dispatch, drained by stream. */
@@ -70,6 +79,8 @@ export declare class FactoryCloudProvider implements CloudProvider {
70
79
  autonomy?: DroidAutonomy;
71
80
  });
72
81
  capabilities(): ProviderCapabilities;
82
+ /** Enumerate Droid Computers via `droid computer list`. Throws if not signed in. */
83
+ listTargets(): Promise<CloudTarget[]>;
73
84
  dispatch(options: DispatchOptions): Promise<CloudTask>;
74
85
  /** Run the remote droid exec to completion, collecting events + final result. */
75
86
  private runRemote;
@@ -22,6 +22,7 @@
22
22
  import { spawn, execFileSync } from 'child_process';
23
23
  import * as fs from 'fs';
24
24
  import * as path from 'path';
25
+ import { MissingTargetError } from './types.js';
25
26
  import { getShimsDir } from '../state.js';
26
27
  const SHIMS_DIR = getShimsDir();
27
28
  const DEFAULT_AUTONOMY = 'high';
@@ -44,6 +45,45 @@ export function resolveAutonomy(value, fallback = DEFAULT_AUTONOMY) {
44
45
  ? value
45
46
  : fallback;
46
47
  }
48
+ /** Run the droid CLI and capture output (used for `computer list`). */
49
+ function runDroid(bin, args) {
50
+ return new Promise((resolve) => {
51
+ const proc = spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
52
+ let stdout = '';
53
+ let stderr = '';
54
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
55
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
56
+ proc.on('error', (e) => resolve({ stdout, stderr: stderr + String(e), code: 127 }));
57
+ proc.on('close', (code) => resolve({ stdout, stderr, code: code ?? 1 }));
58
+ });
59
+ }
60
+ /**
61
+ * Parse `droid computer list` text into targets. Defensive: the exact column
62
+ * layout isn't documented, so we take the first whitespace token of each data
63
+ * row as the computer name and keep the remainder as a label, skipping headers,
64
+ * separators, and status messages. The interactive picker degrades to free-text
65
+ * entry if this yields nothing, so an unexpected layout never blocks a dispatch.
66
+ */
67
+ export function parseComputerList(text) {
68
+ const out = [];
69
+ for (const raw of text.split('\n')) {
70
+ const line = raw.trim();
71
+ if (!line)
72
+ continue;
73
+ if (/^(name|computer|status|id)\b/i.test(line))
74
+ continue; // header row
75
+ if (/^[-=_\s|]+$/.test(line))
76
+ continue; // separator rule
77
+ if (/^(no |failed|error|warning)\b/i.test(line))
78
+ continue; // status message
79
+ const name = line.split(/\s+/)[0];
80
+ if (!name)
81
+ continue;
82
+ const label = line.slice(name.length).trim() || undefined;
83
+ out.push({ id: name, label, kind: 'computer' });
84
+ }
85
+ return out;
86
+ }
47
87
  /**
48
88
  * Build the remote `droid exec` argv. Headless, stream-json output, given
49
89
  * autonomy. `sessionId` (when resuming) maps to `-s`.
@@ -152,6 +192,7 @@ function extractText(obj) {
152
192
  export class FactoryCloudProvider {
153
193
  id = 'factory';
154
194
  name = 'Factory (Droid)';
195
+ targetKind = 'computer';
155
196
  defaultComputer;
156
197
  defaultAutonomy;
157
198
  /** session_id → buffered run, populated by dispatch, drained by stream. */
@@ -178,6 +219,20 @@ export class FactoryCloudProvider {
178
219
  images: false,
179
220
  };
180
221
  }
222
+ /** Enumerate Droid Computers via `droid computer list`. Throws if not signed in. */
223
+ async listTargets() {
224
+ const droidBin = findDroidBinary();
225
+ if (!droidBin) {
226
+ throw new Error('droid CLI not found. Install it: curl -fsSL https://app.factory.ai/cli | sh');
227
+ }
228
+ const { stdout, stderr, code } = await runDroid(droidBin, ['computer', 'list']);
229
+ if (code !== 0) {
230
+ // Surface droid's own message verbatim — e.g. "No authenticated user with
231
+ // organization available" when the user hasn't logged in.
232
+ throw new Error((stderr.trim() || stdout.trim() || `droid computer list exited ${code}`));
233
+ }
234
+ return parseComputerList(stdout);
235
+ }
181
236
  async dispatch(options) {
182
237
  const droidBin = findDroidBinary();
183
238
  if (!droidBin) {
@@ -185,9 +240,9 @@ export class FactoryCloudProvider {
185
240
  }
186
241
  const computer = options.providerOptions?.computer ?? this.defaultComputer;
187
242
  if (!computer) {
188
- throw new Error('Factory cloud requires a Droid Computer. Pass --computer <name>, or set ' +
189
- 'cloud.providers.factory.computer in ~/.agents/agents.yaml. Create one in ' +
190
- 'Factory (Settings Droid Computers), or register a machine with `droid computer register`.');
243
+ throw new MissingTargetError('computer', 'Factory cloud requires a Droid Computer.', 'Pass --computer <name>, or set cloud.providers.factory.computer in ~/.agents/agents.yaml. ' +
244
+ 'Create one in Factory (Settings Droid Computers), or register a machine with `droid computer register`. ' +
245
+ 'List yours with `agents cloud envs --provider factory`.');
191
246
  }
192
247
  const autonomy = resolveAutonomy(options.providerOptions?.autonomy ?? options.providerOptions?.mode, this.defaultAutonomy);
193
248
  const user = options.providerOptions?.user ?? 'droid';
@@ -175,6 +175,33 @@ export interface ProviderCapabilities {
175
175
  skills: boolean;
176
176
  images: boolean;
177
177
  }
178
+ /**
179
+ * A pre-provisioned dispatch target a provider runs *inside* — a Codex
180
+ * environment (`env_…`) or a Factory Droid Computer (a name). Surfaced by
181
+ * `agents cloud envs` and the missing-target picker so users don't have to
182
+ * copy opaque IDs out of a web UI.
183
+ */
184
+ export interface CloudTarget {
185
+ /** The value passed to dispatch (env id / computer name). */
186
+ id: string;
187
+ /** Human label — repo, description, or status. */
188
+ label?: string;
189
+ kind: TargetKind;
190
+ }
191
+ /** Which dispatch option a provider's pre-provisioned target maps to. */
192
+ export type TargetKind = 'env' | 'computer';
193
+ /**
194
+ * Thrown by a provider's `dispatch()` when it needs a pre-provisioned target
195
+ * (Codex env / Factory computer) and none was supplied. The CLI catches this
196
+ * to offer a picker (`listTargets`) or actionable guidance, instead of a raw
197
+ * error. `kind` names the missing flag; `guidance` is shown when the target
198
+ * can't be enumerated.
199
+ */
200
+ export declare class MissingTargetError extends Error {
201
+ kind: TargetKind;
202
+ guidance?: string | undefined;
203
+ constructor(kind: TargetKind, message: string, guidance?: string | undefined);
204
+ }
178
205
  /**
179
206
  * Contract that every cloud backend must implement.
180
207
  *
@@ -198,6 +225,20 @@ export interface CloudProvider {
198
225
  cancel(taskId: string): Promise<void>;
199
226
  /** Send a follow-up message to a finished/idle/needs_review task. */
200
227
  message(taskId: string, content: string): Promise<void>;
228
+ /**
229
+ * The pre-provisioned target this provider runs inside, if any. Set for
230
+ * Codex (`env`) and Factory (`computer`); undefined for Rush (per-repo) and
231
+ * Antigravity (on-demand sandbox).
232
+ */
233
+ targetKind?: TargetKind;
234
+ /**
235
+ * Enumerate selectable targets for `agents cloud envs` and the picker.
236
+ * Present only when the backend can list non-interactively (Factory via
237
+ * `droid computer list`). Codex has no such CLI — it omits this, and callers
238
+ * fall back to `MissingTargetError.guidance`. May throw (e.g. not signed in);
239
+ * callers surface that verbatim.
240
+ */
241
+ listTargets?(): Promise<CloudTarget[]>;
201
242
  }
202
243
  /** Autonomy level passed to `droid exec --auto` for Factory cloud dispatches. */
203
244
  export type DroidAutonomy = 'low' | 'medium' | 'high';
@@ -32,3 +32,20 @@ export function resolveDispatchRepos(options) {
32
32
  }
33
33
  return out;
34
34
  }
35
+ /**
36
+ * Thrown by a provider's `dispatch()` when it needs a pre-provisioned target
37
+ * (Codex env / Factory computer) and none was supplied. The CLI catches this
38
+ * to offer a picker (`listTargets`) or actionable guidance, instead of a raw
39
+ * error. `kind` names the missing flag; `guidance` is shown when the target
40
+ * can't be enumerated.
41
+ */
42
+ export class MissingTargetError extends Error {
43
+ kind;
44
+ guidance;
45
+ constructor(kind, message, guidance) {
46
+ super(message);
47
+ this.kind = kind;
48
+ this.guidance = guidance;
49
+ this.name = 'MissingTargetError';
50
+ }
51
+ }
@@ -99,6 +99,8 @@ export interface ExecOptions {
99
99
  * flag alone merely ADDS to the existing server set).
100
100
  */
101
101
  mcpConfigPath?: string;
102
+ /** Raw args captured after `--` on the command line, forwarded verbatim to the underlying agent CLI. */
103
+ passthroughArgs?: string[];
102
104
  }
103
105
  /**
104
106
  * Resolve interactive vs headless. Explicit flags are definitive and win over
package/dist/lib/exec.js CHANGED
@@ -596,6 +596,11 @@ export function buildExecCommand(options) {
596
596
  cmd.push('--strict-mcp-config');
597
597
  }
598
598
  }
599
+ // Forward arbitrary native flags supplied after `--` verbatim. Appended last
600
+ // so they cannot be misinterpreted as values for earlier flags or as the prompt.
601
+ if (options.passthroughArgs && options.passthroughArgs.length > 0) {
602
+ cmd.push(...options.passthroughArgs);
603
+ }
599
604
  return cmd;
600
605
  }
601
606
  /** Spawn an agent and return its exit code. Convenience wrapper over spawnAgent. */
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleExecutable</key>
6
+ <string>MenubarHelper</string>
7
+ <key>CFBundleIdentifier</key>
8
+ <string>com.phnx-labs.agents-menubar</string>
9
+ <key>CFBundleName</key>
10
+ <string>Agents Menu Bar</string>
11
+ <key>CFBundlePackageType</key>
12
+ <string>APPL</string>
13
+ <key>CFBundleShortVersionString</key>
14
+ <string>0.1.0</string>
15
+ <key>CFBundleVersion</key>
16
+ <string>1</string>
17
+ <key>LSUIElement</key>
18
+ <true/>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,115 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict/>
7
+ <key>files2</key>
8
+ <dict/>
9
+ <key>rules</key>
10
+ <dict>
11
+ <key>^Resources/</key>
12
+ <true/>
13
+ <key>^Resources/.*\.lproj/</key>
14
+ <dict>
15
+ <key>optional</key>
16
+ <true/>
17
+ <key>weight</key>
18
+ <real>1000</real>
19
+ </dict>
20
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
21
+ <dict>
22
+ <key>omit</key>
23
+ <true/>
24
+ <key>weight</key>
25
+ <real>1100</real>
26
+ </dict>
27
+ <key>^Resources/Base\.lproj/</key>
28
+ <dict>
29
+ <key>weight</key>
30
+ <real>1010</real>
31
+ </dict>
32
+ <key>^version.plist$</key>
33
+ <true/>
34
+ </dict>
35
+ <key>rules2</key>
36
+ <dict>
37
+ <key>.*\.dSYM($|/)</key>
38
+ <dict>
39
+ <key>weight</key>
40
+ <real>11</real>
41
+ </dict>
42
+ <key>^(.*/)?\.DS_Store$</key>
43
+ <dict>
44
+ <key>omit</key>
45
+ <true/>
46
+ <key>weight</key>
47
+ <real>2000</real>
48
+ </dict>
49
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
50
+ <dict>
51
+ <key>nested</key>
52
+ <true/>
53
+ <key>weight</key>
54
+ <real>10</real>
55
+ </dict>
56
+ <key>^.*</key>
57
+ <true/>
58
+ <key>^Info\.plist$</key>
59
+ <dict>
60
+ <key>omit</key>
61
+ <true/>
62
+ <key>weight</key>
63
+ <real>20</real>
64
+ </dict>
65
+ <key>^PkgInfo$</key>
66
+ <dict>
67
+ <key>omit</key>
68
+ <true/>
69
+ <key>weight</key>
70
+ <real>20</real>
71
+ </dict>
72
+ <key>^Resources/</key>
73
+ <dict>
74
+ <key>weight</key>
75
+ <real>20</real>
76
+ </dict>
77
+ <key>^Resources/.*\.lproj/</key>
78
+ <dict>
79
+ <key>optional</key>
80
+ <true/>
81
+ <key>weight</key>
82
+ <real>1000</real>
83
+ </dict>
84
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
85
+ <dict>
86
+ <key>omit</key>
87
+ <true/>
88
+ <key>weight</key>
89
+ <real>1100</real>
90
+ </dict>
91
+ <key>^Resources/Base\.lproj/</key>
92
+ <dict>
93
+ <key>weight</key>
94
+ <real>1010</real>
95
+ </dict>
96
+ <key>^[^/]+$</key>
97
+ <dict>
98
+ <key>nested</key>
99
+ <true/>
100
+ <key>weight</key>
101
+ <real>10</real>
102
+ </dict>
103
+ <key>^embedded\.provisionprofile$</key>
104
+ <dict>
105
+ <key>weight</key>
106
+ <real>20</real>
107
+ </dict>
108
+ <key>^version\.plist$</key>
109
+ <dict>
110
+ <key>weight</key>
111
+ <real>20</real>
112
+ </dict>
113
+ </dict>
114
+ </dict>
115
+ </plist>
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Lazy command registry.
3
+ *
4
+ * The CLI entry point (src/index.ts) used to statically import every command
5
+ * module and call its `registerXCommand(program)` on every invocation. That
6
+ * loaded the entire command tree (~50 modules) before the first line of output,
7
+ * dominating cold-start latency.
8
+ *
9
+ * This module maps each user-typed top-level command name to a thunk that
10
+ * dynamically imports ONLY the module(s) that command needs. Fast commands
11
+ * (`--version`, `view`, ...) now pay for just the one module they use; the full
12
+ * tree is loaded only on the rare slow paths (unknown-command spellcheck, bare
13
+ * help) via `registerAllEagerCommands` in src/index.ts.
14
+ *
15
+ * Parity is non-negotiable: the name -> loader map below mirrors exactly which
16
+ * module registers which top-level command on `main`. Multi-command modules
17
+ * (versions, packages) map several names to the same loader; `prune` needs BOTH
18
+ * versions (which creates `prune <specs...>`) and prune.js (which attaches the
19
+ * `cleanup` subcommand to it), in that order — see commands/prune.ts.
20
+ */
21
+ import type { Command } from 'commander';
22
+ /** A function that registers one or more commands onto the root program. */
23
+ export type Registrar = (program: Command) => void;
24
+ /** A thunk that dynamically imports a command module and returns its registrar. */
25
+ export type ModuleLoader = () => Promise<Registrar>;
26
+ export declare const loadView: ModuleLoader;
27
+ export declare const loadInspect: ModuleLoader;
28
+ export declare const loadFeedback: ModuleLoader;
29
+ export declare const loadCommands: ModuleLoader;
30
+ export declare const loadHooks: ModuleLoader;
31
+ export declare const loadSkills: ModuleLoader;
32
+ export declare const loadRules: ModuleLoader;
33
+ export declare const loadPermissions: ModuleLoader;
34
+ export declare const loadMcp: ModuleLoader;
35
+ export declare const loadCli: ModuleLoader;
36
+ export declare const loadSubagents: ModuleLoader;
37
+ export declare const loadPlugins: ModuleLoader;
38
+ export declare const loadWorkflows: ModuleLoader;
39
+ export declare const loadWorktree: ModuleLoader;
40
+ export declare const loadVersions: ModuleLoader;
41
+ export declare const loadImport: ModuleLoader;
42
+ export declare const loadPackages: ModuleLoader;
43
+ export declare const loadDaemon: ModuleLoader;
44
+ export declare const loadRoutines: ModuleLoader;
45
+ export declare const loadRun: ModuleLoader;
46
+ export declare const loadDefaults: ModuleLoader;
47
+ export declare const loadModels: ModuleLoader;
48
+ export declare const loadPrune: ModuleLoader;
49
+ export declare const loadTrash: ModuleLoader;
50
+ export declare const loadRestore: ModuleLoader;
51
+ export declare const loadDoctor: ModuleLoader;
52
+ export declare const loadProfiles: ModuleLoader;
53
+ export declare const loadSecrets: ModuleLoader;
54
+ export declare const loadWallet: ModuleLoader;
55
+ export declare const loadHelper: ModuleLoader;
56
+ export declare const loadMenubar: ModuleLoader;
57
+ export declare const loadBeta: ModuleLoader;
58
+ export declare const loadSync: ModuleLoader;
59
+ export declare const loadRefreshRules: ModuleLoader;
60
+ export declare const loadDrive: ModuleLoader;
61
+ export declare const loadFactory: ModuleLoader;
62
+ export declare const loadUsage: ModuleLoader;
63
+ export declare const loadCost: ModuleLoader;
64
+ export declare const loadBudget: ModuleLoader;
65
+ export declare const loadAlias: ModuleLoader;
66
+ export declare const loadPty: ModuleLoader;
67
+ export declare const loadTmux: ModuleLoader;
68
+ export declare const loadBrowser: ModuleLoader;
69
+ export declare const loadComputer: ModuleLoader;
70
+ export declare const loadPull: ModuleLoader;
71
+ export declare const loadPush: ModuleLoader;
72
+ export declare const loadRepo: ModuleLoader;
73
+ export declare const loadSetup: ModuleLoader;
74
+ export declare const loadSessions: ModuleLoader;
75
+ export declare const loadTeams: ModuleLoader;
76
+ export declare const loadCloud: ModuleLoader;
77
+ /**
78
+ * Commands whose modules pull in the SQLite-backed session/cloud stack. They are
79
+ * registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
80
+ * conventions at module top-level, lazy registration just before parse), so they
81
+ * inherit the root's custom help formatter rather than getting the per-command
82
+ * recursive pass. Keeping that ordering preserves their `--help` output exactly.
83
+ */
84
+ export declare const LAZY_COMMAND_NAMES: ReadonlySet<string>;
85
+ /**
86
+ * User-typed top-level command name -> ordered list of module loaders to run.
87
+ *
88
+ * Most names map to a single loader. The exceptions encode real coupling on main:
89
+ * - `add`/`use`/`list`/`remove`/`rm`/`purge` all come from the versions module.
90
+ * - `registry`/`search`/`install` all come from the packages module.
91
+ * - `trash` and `restore` are separate registrars in the trash module.
92
+ * - `prune` needs versions FIRST (it creates `prune <specs...>`) then prune.js
93
+ * (which finds that command and attaches the `cleanup` subcommand).
94
+ *
95
+ * Inline deprecated aliases (memory/perms/exec/jobs/cron) and the inline
96
+ * `upgrade` command are NOT here — they are closures over entry-point state and
97
+ * are handled directly in src/index.ts.
98
+ */
99
+ export declare const COMMAND_LOADERS: Record<string, ModuleLoader[]>;
@@ -0,0 +1,136 @@
1
+ // One loader per command module. Each dynamically imports the module and hands
2
+ // back its register function. Kept as named consts so src/index.ts can compose
3
+ // them into the exact main-branch registration order for the slow path.
4
+ export const loadView = async () => (await import('../../commands/view.js')).registerViewCommand;
5
+ export const loadInspect = async () => (await import('../../commands/inspect.js')).registerInspectCommand;
6
+ export const loadFeedback = async () => (await import('../../commands/feedback.js')).registerFeedbackCommand;
7
+ export const loadCommands = async () => (await import('../../commands/commands.js')).registerCommandsCommands;
8
+ export const loadHooks = async () => (await import('../../commands/hooks.js')).registerHooksCommands;
9
+ export const loadSkills = async () => (await import('../../commands/skills.js')).registerSkillsCommands;
10
+ export const loadRules = async () => (await import('../../commands/rules.js')).registerRulesCommands;
11
+ export const loadPermissions = async () => (await import('../../commands/permissions.js')).registerPermissionsCommands;
12
+ export const loadMcp = async () => (await import('../../commands/mcp.js')).registerMcpCommands;
13
+ export const loadCli = async () => (await import('../../commands/cli.js')).registerCliCommands;
14
+ export const loadSubagents = async () => (await import('../../commands/subagents.js')).registerSubagentsCommands;
15
+ export const loadPlugins = async () => (await import('../../commands/plugins.js')).registerPluginsCommands;
16
+ export const loadWorkflows = async () => (await import('../../commands/workflows.js')).registerWorkflowsCommands;
17
+ export const loadWorktree = async () => (await import('../../commands/worktree.js')).registerWorktreeCommands;
18
+ export const loadVersions = async () => (await import('../../commands/versions.js')).registerVersionsCommands;
19
+ export const loadImport = async () => (await import('../../commands/import.js')).registerImportCommand;
20
+ export const loadPackages = async () => (await import('../../commands/packages.js')).registerPackagesCommands;
21
+ export const loadDaemon = async () => (await import('../../commands/daemon.js')).registerDaemonCommands;
22
+ export const loadRoutines = async () => (await import('../../commands/routines.js')).registerRoutinesCommands;
23
+ export const loadRun = async () => (await import('../../commands/exec.js')).registerRunCommand;
24
+ export const loadDefaults = async () => (await import('../../commands/defaults.js')).registerDefaultsCommands;
25
+ export const loadModels = async () => (await import('../../commands/models.js')).registerModelsCommand;
26
+ export const loadPrune = async () => (await import('../../commands/prune.js')).registerPruneCommand;
27
+ export const loadTrash = async () => (await import('../../commands/trash.js')).registerTrashCommands;
28
+ export const loadRestore = async () => (await import('../../commands/trash.js')).registerRestoreCommand;
29
+ export const loadDoctor = async () => (await import('../../commands/doctor.js')).registerDoctorCommand;
30
+ export const loadProfiles = async () => (await import('../../commands/profiles.js')).registerProfilesCommands;
31
+ export const loadSecrets = async () => (await import('../../commands/secrets.js')).registerSecretsCommands;
32
+ export const loadWallet = async () => (await import('../../commands/wallet.js')).registerWalletCommands;
33
+ export const loadHelper = async () => (await import('../../commands/helper.js')).registerHelperCommand;
34
+ export const loadMenubar = async () => (await import('../../commands/menubar.js')).registerMenubarCommands;
35
+ export const loadBeta = async () => (await import('../../commands/beta.js')).registerBetaCommands;
36
+ export const loadSync = async () => (await import('../../commands/sync.js')).registerSyncCommand;
37
+ export const loadRefreshRules = async () => (await import('../../commands/refresh-rules.js')).registerRefreshRulesCommand;
38
+ export const loadDrive = async () => (await import('../../commands/drive.js')).registerDriveCommands;
39
+ export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
40
+ export const loadUsage = async () => (await import('../../commands/usage.js')).registerUsageCommand;
41
+ export const loadCost = async () => (await import('../../commands/cost.js')).registerCostCommand;
42
+ export const loadBudget = async () => (await import('../../commands/budget.js')).registerBudgetCommand;
43
+ export const loadAlias = async () => (await import('../../commands/alias.js')).registerAliasCommand;
44
+ export const loadPty = async () => (await import('../../commands/pty.js')).registerPtyCommands;
45
+ export const loadTmux = async () => (await import('../../commands/tmux.js')).registerTmuxCommands;
46
+ export const loadBrowser = async () => (await import('../../commands/browser.js')).registerBrowserCommand;
47
+ export const loadComputer = async () => (await import('../../commands/computer.js')).registerComputerCommand;
48
+ export const loadPull = async () => (await import('../../commands/pull.js')).registerPullCommand;
49
+ export const loadPush = async () => (await import('../../commands/push.js')).registerPushCommand;
50
+ export const loadRepo = async () => (await import('../../commands/repo.js')).registerRepoCommands;
51
+ export const loadSetup = async () => (await import('../../commands/setup.js')).registerSetupCommand;
52
+ export const loadSessions = async () => (await import('../../commands/sessions.js')).registerSessionsCommands;
53
+ export const loadTeams = async () => (await import('../../commands/teams.js')).registerTeamsCommands;
54
+ export const loadCloud = async () => (await import('../../commands/cloud.js')).registerCloudCommands;
55
+ /**
56
+ * Commands whose modules pull in the SQLite-backed session/cloud stack. They are
57
+ * registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
58
+ * conventions at module top-level, lazy registration just before parse), so they
59
+ * inherit the root's custom help formatter rather than getting the per-command
60
+ * recursive pass. Keeping that ordering preserves their `--help` output exactly.
61
+ */
62
+ export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud']);
63
+ /**
64
+ * User-typed top-level command name -> ordered list of module loaders to run.
65
+ *
66
+ * Most names map to a single loader. The exceptions encode real coupling on main:
67
+ * - `add`/`use`/`list`/`remove`/`rm`/`purge` all come from the versions module.
68
+ * - `registry`/`search`/`install` all come from the packages module.
69
+ * - `trash` and `restore` are separate registrars in the trash module.
70
+ * - `prune` needs versions FIRST (it creates `prune <specs...>`) then prune.js
71
+ * (which finds that command and attaches the `cleanup` subcommand).
72
+ *
73
+ * Inline deprecated aliases (memory/perms/exec/jobs/cron) and the inline
74
+ * `upgrade` command are NOT here — they are closures over entry-point state and
75
+ * are handled directly in src/index.ts.
76
+ */
77
+ export const COMMAND_LOADERS = {
78
+ view: [loadView],
79
+ inspect: [loadInspect],
80
+ feedback: [loadFeedback],
81
+ commands: [loadCommands],
82
+ hooks: [loadHooks],
83
+ skills: [loadSkills],
84
+ rules: [loadRules],
85
+ permissions: [loadPermissions],
86
+ mcp: [loadMcp],
87
+ cli: [loadCli],
88
+ subagents: [loadSubagents],
89
+ plugins: [loadPlugins],
90
+ workflows: [loadWorkflows],
91
+ worktree: [loadWorktree],
92
+ add: [loadVersions],
93
+ use: [loadVersions],
94
+ list: [loadVersions],
95
+ remove: [loadVersions],
96
+ rm: [loadVersions],
97
+ purge: [loadVersions],
98
+ prune: [loadVersions, loadPrune],
99
+ import: [loadImport],
100
+ registry: [loadPackages],
101
+ search: [loadPackages],
102
+ install: [loadPackages],
103
+ daemon: [loadDaemon],
104
+ routines: [loadRoutines],
105
+ run: [loadRun],
106
+ defaults: [loadDefaults],
107
+ models: [loadModels],
108
+ trash: [loadTrash],
109
+ restore: [loadRestore],
110
+ doctor: [loadDoctor],
111
+ profiles: [loadProfiles],
112
+ secrets: [loadSecrets],
113
+ wallet: [loadWallet],
114
+ helper: [loadHelper],
115
+ menubar: [loadMenubar],
116
+ beta: [loadBeta],
117
+ sync: [loadSync],
118
+ 'refresh-rules': [loadRefreshRules],
119
+ drive: [loadDrive],
120
+ factory: [loadFactory],
121
+ usage: [loadUsage],
122
+ cost: [loadCost],
123
+ budget: [loadBudget],
124
+ alias: [loadAlias],
125
+ pty: [loadPty],
126
+ tmux: [loadTmux],
127
+ browser: [loadBrowser],
128
+ computer: [loadComputer],
129
+ pull: [loadPull],
130
+ push: [loadPush],
131
+ repo: [loadRepo],
132
+ setup: [loadSetup],
133
+ sessions: [loadSessions],
134
+ teams: [loadTeams],
135
+ cloud: [loadCloud],
136
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.20.22",
3
+ "version": "1.20.23",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,7 +45,8 @@
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsc && rm -rf 'dist/lib/secrets/AgentsKeychain.app' 'dist/lib/secrets/Agents CLI.app' 'dist/lib/menubar/MenubarHelper.app' && ([ \"$(uname)\" = \"Darwin\" ] && cp -R 'bin/Agents CLI.app' 'dist/lib/secrets/Agents CLI.app' || true) && ([ \"$(uname)\" = \"Darwin\" ] && [ -d 'bin/MenubarHelper.app' ] && mkdir -p 'dist/lib/menubar' && cp -R 'bin/MenubarHelper.app' 'dist/lib/menubar/MenubarHelper.app' || true)",
48
- "prepack": "scripts/verify-keychain-helper.sh",
48
+ "build:bin": "scripts/build-bin.sh",
49
+ "prepack": "scripts/verify-keychain-helper.sh && scripts/verify-menubar-helper.sh",
49
50
  "postinstall": "node scripts/postinstall.js",
50
51
  "dev": "tsx src/index.ts",
51
52
  "start": "node dist/index.js",