@phnx-labs/agents-cli 1.20.22 → 1.20.24

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>
@@ -25,6 +25,17 @@
25
25
  import type { SecretsBundle } from './bundles.js';
26
26
  /** Default lifetime of an unlocked bundle when `--ttl` is not given. */
27
27
  export declare const DEFAULT_TTL_MS: number;
28
+ /**
29
+ * Decide whether a persistent broker should self-heal onto freshly-installed
30
+ * code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
31
+ * with bundles still unlocked wipes them from memory, so the next reader falls
32
+ * back to a direct keychain read and re-prompts for Touch ID. Deferring the
33
+ * restart until the cache is idle (TTL-expired / screen-locked) means an
34
+ * in-place `npm i -g` never wipes a hot cache — the new code is adopted at the
35
+ * next quiet moment instead. See #435: rapid repeated upgrades wiped a hot
36
+ * cache on every bump and produced a recurring Touch ID storm.
37
+ */
38
+ export declare function shouldSelfHealForUpgrade(persistent: boolean, storeSize: number, runningVersion: string, onDiskVersion: string): boolean;
28
39
  export interface StoredBundle {
29
40
  bundle: SecretsBundle;
30
41
  env: Record<string, string>;
@@ -41,6 +41,25 @@ export const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24h
41
41
  const IDLE_EXIT_MS = 5 * 60 * 1000; // 5m
42
42
  /** How often the broker sweeps expired entries. */
43
43
  const SWEEP_INTERVAL_MS = 30 * 1000;
44
+ /**
45
+ * Decide whether a persistent broker should self-heal onto freshly-installed
46
+ * code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
47
+ * with bundles still unlocked wipes them from memory, so the next reader falls
48
+ * back to a direct keychain read and re-prompts for Touch ID. Deferring the
49
+ * restart until the cache is idle (TTL-expired / screen-locked) means an
50
+ * in-place `npm i -g` never wipes a hot cache — the new code is adopted at the
51
+ * next quiet moment instead. See #435: rapid repeated upgrades wiped a hot
52
+ * cache on every bump and produced a recurring Touch ID storm.
53
+ */
54
+ export function shouldSelfHealForUpgrade(persistent, storeSize, runningVersion, onDiskVersion) {
55
+ if (!persistent)
56
+ return false;
57
+ if (storeSize > 0)
58
+ return false; // hot cache — defer rather than wipe unlocks
59
+ if (runningVersion === 'unknown' || onDiskVersion === 'unknown')
60
+ return false;
61
+ return onDiskVersion !== runningVersion;
62
+ }
44
63
  function onDarwin() {
45
64
  return process.platform === 'darwin';
46
65
  }
@@ -307,15 +326,15 @@ export async function runSecretsAgent(opts = {}) {
307
326
  for (const [name, e] of store)
308
327
  if (now >= e.expiresAt)
309
328
  store.delete(name);
310
- // Self-heal: a newer version was installed in place exit so launchd
311
- // relaunches us on the new code. Only meaningful when launchd will restart
312
- // us (persistent); a one-off broker just keeps serving until idle.
313
- if (persistent) {
314
- const onDisk = getCliVersionFresh();
315
- if (onDisk !== 'unknown' && runningVersion !== 'unknown' && onDisk !== runningVersion) {
316
- shutdown(0); // KeepAlive relaunches on the new code
317
- return;
318
- }
329
+ // Self-heal onto a newer in-place install but ONLY while the store is
330
+ // empty, so we never wipe live unlocks and force a re-prompt (#435). The
331
+ // `store.size === 0` short-circuit also keeps getCliVersionFresh (a disk
332
+ // read) off the hot path. A pending upgrade is adopted at the next idle
333
+ // sweep instead of immediately.
334
+ if (store.size === 0 &&
335
+ shouldSelfHealForUpgrade(persistent, store.size, runningVersion, getCliVersionFresh())) {
336
+ shutdown(0); // KeepAlive relaunches on the new code
337
+ return;
319
338
  }
320
339
  if (store.size === 0) {
321
340
  if (!persistent && now - emptySince >= IDLE_EXIT_MS)
@@ -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[]>;