@phnx-labs/agents-cli 1.20.21 → 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.
- package/CHANGELOG.md +14 -0
- package/dist/commands/cloud.js +142 -13
- package/dist/commands/exec.js +13 -1
- package/dist/commands/menubar.d.ts +10 -0
- package/dist/commands/menubar.js +83 -0
- package/dist/commands/routines.js +34 -1
- package/dist/commands/secrets.d.ts +1 -1
- package/dist/commands/secrets.js +95 -38
- package/dist/index.js +292 -225
- package/dist/lib/agents.js +8 -0
- package/dist/lib/cloud/antigravity.d.ts +70 -0
- package/dist/lib/cloud/antigravity.js +196 -0
- package/dist/lib/cloud/codex.d.ts +1 -0
- package/dist/lib/cloud/codex.js +8 -2
- package/dist/lib/cloud/factory.d.ts +79 -18
- package/dist/lib/cloud/factory.js +324 -26
- package/dist/lib/cloud/registry.d.ts +18 -2
- package/dist/lib/cloud/registry.js +28 -4
- package/dist/lib/cloud/types.d.ts +73 -2
- package/dist/lib/cloud/types.js +17 -0
- package/dist/lib/exec.d.ts +2 -0
- package/dist/lib/exec.js +5 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +20 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +115 -0
- package/dist/lib/menubar/install-menubar.d.ts +57 -0
- package/dist/lib/menubar/install-menubar.js +291 -0
- package/dist/lib/secrets/agent.d.ts +9 -1
- package/dist/lib/secrets/agent.js +91 -10
- package/dist/lib/secrets/bundles.d.ts +19 -12
- package/dist/lib/secrets/bundles.js +22 -14
- package/dist/lib/self-update.d.ts +34 -0
- package/dist/lib/self-update.js +63 -2
- package/dist/lib/startup/command-registry.d.ts +99 -0
- package/dist/lib/startup/command-registry.js +136 -0
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/version.d.ts +11 -0
- package/dist/lib/version.js +20 -0
- package/package.json +5 -3
- package/scripts/postinstall.js +35 -0
package/dist/index.js
CHANGED
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
-
import ora from 'ora';
|
|
11
10
|
import * as fs from 'fs';
|
|
12
11
|
import * as os from 'os';
|
|
13
12
|
import * as path from 'path';
|
|
14
13
|
import { fileURLToPath } from 'url';
|
|
15
|
-
|
|
14
|
+
// `ora`, `@inquirer/prompts`, `./commands/utils.js`, and the agents/versions/shims
|
|
15
|
+
// modules are imported dynamically at their use sites: they are needed only on
|
|
16
|
+
// interactive / update / shim-repair paths, never for fast commands like
|
|
17
|
+
// `--version`, `--help`, or `view`. Keeping them off the module-eval path is
|
|
18
|
+
// what gets cold starts under the target.
|
|
16
19
|
// Force exit on Ctrl+C when no interactive prompt is handling it.
|
|
17
20
|
process.on('SIGINT', () => process.exit(130));
|
|
18
21
|
// Ignore SIGPIPE — prevents exit code 13 crashes in piped environments
|
|
@@ -23,7 +26,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
23
26
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
24
27
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
25
28
|
const VERSION = packageJson.version;
|
|
26
|
-
import { NPM_PACKAGE_NAME, deriveGlobalPrefix, installPackageIntoPrefix, verifyInstalledVersion, refreshAliasShims, } from './lib/self-update.js';
|
|
29
|
+
import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, } from './lib/self-update.js';
|
|
27
30
|
// Detect dev/working-tree builds and default the noisy startup steps off.
|
|
28
31
|
// Three cases trip this:
|
|
29
32
|
// 1. Dev install (scripts/install.sh) — package.json version stamped 0.0.0-dev.<sha>
|
|
@@ -54,58 +57,12 @@ if (IS_DEV_BUILD) {
|
|
|
54
57
|
if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE === undefined)
|
|
55
58
|
process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE = '1';
|
|
56
59
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
import {
|
|
62
|
-
import { registerFeedbackCommand } from './commands/feedback.js';
|
|
63
|
-
import { registerViewCommand } from './commands/view.js';
|
|
64
|
-
import { registerInspectCommand } from './commands/inspect.js';
|
|
65
|
-
import { registerCommandsCommands } from './commands/commands.js';
|
|
66
|
-
import { registerHooksCommands } from './commands/hooks.js';
|
|
67
|
-
import { registerSkillsCommands } from './commands/skills.js';
|
|
68
|
-
import { registerRulesCommands } from './commands/rules.js';
|
|
69
|
-
import { registerPermissionsCommands } from './commands/permissions.js';
|
|
70
|
-
import { registerMcpCommands } from './commands/mcp.js';
|
|
71
|
-
import { registerCliCommands } from './commands/cli.js';
|
|
72
|
-
import { registerVersionsCommands } from './commands/versions.js';
|
|
73
|
-
import { registerImportCommand } from './commands/import.js';
|
|
74
|
-
import { registerPackagesCommands } from './commands/packages.js';
|
|
75
|
-
import { registerDaemonCommands } from './commands/daemon.js';
|
|
76
|
-
import { registerRoutinesCommands } from './commands/routines.js';
|
|
77
|
-
import { registerRunCommand } from './commands/exec.js';
|
|
78
|
-
import { registerModelsCommand } from './commands/models.js';
|
|
79
|
-
import { registerDefaultsCommands } from './commands/defaults.js';
|
|
80
|
-
import { registerPruneCommand } from './commands/prune.js';
|
|
81
|
-
import { registerTrashCommands, registerRestoreCommand } from './commands/trash.js';
|
|
82
|
-
import { registerDoctorCommand } from './commands/doctor.js';
|
|
83
|
-
import { registerSubagentsCommands } from './commands/subagents.js';
|
|
84
|
-
import { registerPluginsCommands } from './commands/plugins.js';
|
|
85
|
-
import { registerWorkflowsCommands } from './commands/workflows.js';
|
|
86
|
-
import { registerWorktreeCommands } from './commands/worktree.js';
|
|
87
|
-
import { registerSyncCommand } from './commands/sync.js';
|
|
88
|
-
import { registerRefreshRulesCommand } from './commands/refresh-rules.js';
|
|
89
|
-
import { registerDriveCommands } from './commands/drive.js';
|
|
90
|
-
import { registerPtyCommands } from './commands/pty.js';
|
|
91
|
-
import { registerTmuxCommands } from './commands/tmux.js';
|
|
92
|
-
import { registerBrowserCommand } from './commands/browser.js';
|
|
93
|
-
import { registerComputerCommand } from './commands/computer.js';
|
|
94
|
-
import { registerProfilesCommands } from './commands/profiles.js';
|
|
95
|
-
import { registerSecretsCommands } from './commands/secrets.js';
|
|
96
|
-
import { registerWalletCommands } from './commands/wallet.js';
|
|
97
|
-
import { registerHelperCommand } from './commands/helper.js';
|
|
98
|
-
import { registerFactoryCommands } from './commands/factory.js';
|
|
99
|
-
import { registerUsageCommand } from './commands/usage.js';
|
|
100
|
-
import { registerCostCommand } from './commands/cost.js';
|
|
101
|
-
import { registerBudgetCommand } from './commands/budget.js';
|
|
102
|
-
import { registerAliasCommand } from './commands/alias.js';
|
|
103
|
-
import { registerBetaCommands } from './commands/beta.js';
|
|
60
|
+
// Command registration is lazy: instead of statically importing every command
|
|
61
|
+
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
62
|
+
// first byte of output), the registry maps a command name to a thunk that
|
|
63
|
+
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
64
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages, loadDaemon, loadRoutines, loadRun, loadDefaults, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadProfiles, loadSecrets, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadRefreshRules, loadDrive, loadFactory, loadUsage, loadCost, loadBudget, loadAlias, loadPty, loadTmux, loadBrowser, loadComputer, loadPull, loadPush, loadRepo, loadSetup, } from './lib/startup/command-registry.js';
|
|
104
65
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
105
|
-
import { isInteractiveTerminal, isPromptCancelled } from './commands/utils.js';
|
|
106
|
-
import { AGENTS } from './lib/agents.js';
|
|
107
|
-
import { getGlobalDefault, listInstalledVersions } from './lib/versions.js';
|
|
108
|
-
import { addShimsToPath, ensureShimCurrent, ensureVersionedAliasCurrent, getPathShadowingExecutable, getPathSetupInstructions, getShimsDir, isShimsInPath, listAgentsWithInstalledVersions, removeLegacyUserShim, } from './lib/shims.js';
|
|
109
66
|
import { IS_WINDOWS } from './lib/platform/index.js';
|
|
110
67
|
// Transparent shim delegate: the generated Windows `.cmd` shims invoke
|
|
111
68
|
// `agents __shim <agent>[@version] <raw args>`. Intercept here, before commander
|
|
@@ -347,8 +304,17 @@ function printResolvedPackage(metadata) {
|
|
|
347
304
|
}
|
|
348
305
|
async function installResolvedPackage(metadata) {
|
|
349
306
|
const packageRoot = path.resolve(__dirname, '..');
|
|
350
|
-
const
|
|
351
|
-
|
|
307
|
+
const spec = `${NPM_PACKAGE_NAME}@${metadata.version}`;
|
|
308
|
+
// Upgrade with the package manager that owns this install. A bun global
|
|
309
|
+
// install lives at <bunGlobalDir>/node_modules/... (no `lib` segment), so an
|
|
310
|
+
// `npm install --prefix` would write to <bunGlobalDir>/lib/node_modules and
|
|
311
|
+
// never touch the running copy — npm exits 0, the verify below fails.
|
|
312
|
+
if (detectPackageManager(packageRoot) === 'bun') {
|
|
313
|
+
await installPackageWithBun(spec);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
await installPackageIntoPrefix(spec, deriveGlobalPrefix(packageRoot));
|
|
317
|
+
}
|
|
352
318
|
verifyInstalledVersion(packageRoot, metadata.version);
|
|
353
319
|
refreshAliasShims(packageRoot);
|
|
354
320
|
// The npm install above runs with --ignore-scripts, so the postinstall that
|
|
@@ -372,6 +338,9 @@ async function installResolvedPackage(metadata) {
|
|
|
372
338
|
}
|
|
373
339
|
/** Present an interactive upgrade prompt (TTY) or a one-line hint (non-TTY). */
|
|
374
340
|
async function promptUpgrade(latestVersion) {
|
|
341
|
+
const { default: ora } = await import('ora');
|
|
342
|
+
const { confirm, select } = await import('@inquirer/prompts');
|
|
343
|
+
const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
|
|
375
344
|
if (!isInteractiveTerminal()) {
|
|
376
345
|
console.error(chalk.yellow(`Update available: ${VERSION} -> ${latestVersion}. Run: agents upgrade --yes`));
|
|
377
346
|
return;
|
|
@@ -470,6 +439,7 @@ async function checkForUpdates() {
|
|
|
470
439
|
await promptUpgrade(cache.latestVersion);
|
|
471
440
|
}
|
|
472
441
|
catch (err) {
|
|
442
|
+
const { isPromptCancelled } = await import('./commands/utils.js');
|
|
473
443
|
if (isPromptCancelled(err))
|
|
474
444
|
return;
|
|
475
445
|
/* prompt error, ignore */
|
|
@@ -490,6 +460,13 @@ async function maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequ
|
|
|
490
460
|
if (requestedCommand === 'sync' || requestedCommand === 'refresh-rules') {
|
|
491
461
|
return;
|
|
492
462
|
}
|
|
463
|
+
// Past the documentation/non-TTY guards: only now load the shim + agent
|
|
464
|
+
// tables this interactive repair flow needs, so fast commands never pay for
|
|
465
|
+
// them at module-eval time.
|
|
466
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
467
|
+
const { AGENTS } = await import('./lib/agents.js');
|
|
468
|
+
const { getGlobalDefault, listInstalledVersions } = await import('./lib/versions.js');
|
|
469
|
+
const { addShimsToPath, ensureShimCurrent, ensureVersionedAliasCurrent, getPathShadowingExecutable, getPathSetupInstructions, getShimsDir, isShimsInPath, listAgentsWithInstalledVersions, removeLegacyUserShim, } = await import('./lib/shims.js');
|
|
493
470
|
const installedAgents = listAgentsWithInstalledVersions();
|
|
494
471
|
if (installedAgents.length === 0) {
|
|
495
472
|
return;
|
|
@@ -611,87 +588,50 @@ async function maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequ
|
|
|
611
588
|
}
|
|
612
589
|
catch { /* best-effort */ }
|
|
613
590
|
}
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
registerModelsCommand(program);
|
|
659
|
-
registerPruneCommand(program);
|
|
660
|
-
registerTrashCommands(program);
|
|
661
|
-
registerRestoreCommand(program);
|
|
662
|
-
registerDoctorCommand(program);
|
|
663
|
-
// Deprecated 'exec' alias for 'run'
|
|
664
|
-
program
|
|
665
|
-
.command('exec', { hidden: true })
|
|
666
|
-
.allowUnknownOption()
|
|
667
|
-
.allowExcessArguments()
|
|
668
|
-
.action(async () => {
|
|
669
|
-
console.log(chalk.yellow('Deprecated: Use "agents run" instead of "agents exec"\n'));
|
|
670
|
-
const args = process.argv.slice(2);
|
|
671
|
-
args[0] = 'run';
|
|
672
|
-
await program.parseAsync(['node', 'agents', ...args]);
|
|
673
|
-
});
|
|
674
|
-
registerProfilesCommands(program);
|
|
675
|
-
registerSecretsCommands(program);
|
|
676
|
-
registerWalletCommands(program);
|
|
677
|
-
registerHelperCommand(program);
|
|
678
|
-
registerBetaCommands(program);
|
|
679
|
-
registerSyncCommand(program);
|
|
680
|
-
registerRefreshRulesCommand(program);
|
|
681
|
-
registerDriveCommands(program);
|
|
682
|
-
registerFactoryCommands(program);
|
|
683
|
-
registerUsageCommand(program);
|
|
684
|
-
registerCostCommand(program);
|
|
685
|
-
registerBudgetCommand(program);
|
|
686
|
-
registerAliasCommand(program);
|
|
687
|
-
registerPtyCommands(program);
|
|
688
|
-
registerTmuxCommands(program);
|
|
689
|
-
registerBrowserCommand(program);
|
|
690
|
-
registerComputerCommand(program);
|
|
691
|
-
// Deprecated 'jobs' and 'cron' aliases for 'routines'
|
|
692
|
-
for (const alias of ['jobs', 'cron']) {
|
|
693
|
-
program
|
|
694
|
-
.command(alias, { hidden: true })
|
|
591
|
+
// --- Inline command registrars ----------------------------------------------
|
|
592
|
+
// These commands are defined here rather than in a command module because they
|
|
593
|
+
// close over entry-point-local state (program re-parsing, VERSION, the npm
|
|
594
|
+
// upgrade helpers). The lazy registrar and the all-commands fallback below both
|
|
595
|
+
// call them, so the behavior is identical to the old eager registration.
|
|
596
|
+
/** Deprecated `memory` command — hard error pointing users at `rules`. */
|
|
597
|
+
function registerMemoryCommand(p) {
|
|
598
|
+
p.command('memory', { hidden: true })
|
|
599
|
+
.allowUnknownOption()
|
|
600
|
+
.allowExcessArguments()
|
|
601
|
+
.action(() => {
|
|
602
|
+
console.error(chalk.red('"agents memory" has been renamed to "agents rules".'));
|
|
603
|
+
console.error(chalk.gray('Run "agents rules --help" for usage.\n'));
|
|
604
|
+
process.exit(1);
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
/** Deprecated `perms` alias — re-parses as `permissions`. */
|
|
608
|
+
function registerPermsAliasCommand(p) {
|
|
609
|
+
p.command('perms', { hidden: true })
|
|
610
|
+
.allowUnknownOption()
|
|
611
|
+
.allowExcessArguments()
|
|
612
|
+
.action(async () => {
|
|
613
|
+
console.log(chalk.yellow('Deprecated: Use "agents permissions" instead of "agents perms"\n'));
|
|
614
|
+
// Re-parse with 'permissions' command
|
|
615
|
+
const args = process.argv.slice(2);
|
|
616
|
+
args[0] = 'permissions';
|
|
617
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
/** Deprecated `exec` alias — re-parses as `run`. */
|
|
621
|
+
function registerExecAliasCommand(p) {
|
|
622
|
+
p.command('exec', { hidden: true })
|
|
623
|
+
.allowUnknownOption()
|
|
624
|
+
.allowExcessArguments()
|
|
625
|
+
.action(async () => {
|
|
626
|
+
console.log(chalk.yellow('Deprecated: Use "agents run" instead of "agents exec"\n'));
|
|
627
|
+
const args = process.argv.slice(2);
|
|
628
|
+
args[0] = 'run';
|
|
629
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
/** Deprecated `jobs` / `cron` aliases — re-parse as `routines`. */
|
|
633
|
+
function registerJobsCronAliasCommand(p, alias) {
|
|
634
|
+
p.command(alias, { hidden: true })
|
|
695
635
|
.allowUnknownOption()
|
|
696
636
|
.allowExcessArguments()
|
|
697
637
|
.action(async () => {
|
|
@@ -701,60 +641,166 @@ for (const alias of ['jobs', 'cron']) {
|
|
|
701
641
|
await program.parseAsync(['node', 'agents', ...args]);
|
|
702
642
|
});
|
|
703
643
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
.
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
if (!version && compareVersions(resolvedVersion, VERSION) <= 0) {
|
|
722
|
-
spinner.succeed(`Already ahead of latest (${VERSION} >= ${resolvedVersion})`);
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
const direction = compareVersions(resolvedVersion, VERSION) < 0 ? 'Downgrade' : 'Upgrade';
|
|
726
|
-
spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${resolvedVersion}`);
|
|
727
|
-
printResolvedPackage(metadata);
|
|
728
|
-
if (isInteractiveTerminal() && !options.yes) {
|
|
729
|
-
const approved = await confirm({
|
|
730
|
-
message: `Install ${NPM_PACKAGE_NAME}@${resolvedVersion}?`,
|
|
731
|
-
default: false,
|
|
732
|
-
});
|
|
733
|
-
if (!approved) {
|
|
734
|
-
console.log(chalk.gray('Upgrade cancelled'));
|
|
644
|
+
/** Self-upgrade command (`agents upgrade [version]`). */
|
|
645
|
+
function registerUpgradeCommand(p) {
|
|
646
|
+
p.command('upgrade')
|
|
647
|
+
.description('Upgrade agents-cli to the latest version (or a specific [version])')
|
|
648
|
+
.argument('[version]', 'Target version or dist-tag to install (default: latest)')
|
|
649
|
+
.option('-y, --yes', 'Install without an interactive confirmation prompt')
|
|
650
|
+
.action(async (version, options) => {
|
|
651
|
+
const { default: ora } = await import('ora');
|
|
652
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
653
|
+
const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
|
|
654
|
+
const target = version ?? 'latest';
|
|
655
|
+
let spinner = ora(version ? `Resolving ${NPM_PACKAGE_NAME}@${target}...` : 'Checking for updates...').start();
|
|
656
|
+
try {
|
|
657
|
+
const metadata = await fetchNpmPackageMetadata(target);
|
|
658
|
+
const resolvedVersion = metadata.version;
|
|
659
|
+
if (resolvedVersion === VERSION) {
|
|
660
|
+
spinner.succeed(`Already on ${VERSION}`);
|
|
735
661
|
return;
|
|
736
662
|
}
|
|
663
|
+
// For `latest` (no explicit version) skip when already ahead. When a
|
|
664
|
+
// version is named explicitly, honor it even if it's a downgrade.
|
|
665
|
+
if (!version && compareVersions(resolvedVersion, VERSION) <= 0) {
|
|
666
|
+
spinner.succeed(`Already ahead of latest (${VERSION} >= ${resolvedVersion})`);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const direction = compareVersions(resolvedVersion, VERSION) < 0 ? 'Downgrade' : 'Upgrade';
|
|
670
|
+
spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${resolvedVersion}`);
|
|
671
|
+
printResolvedPackage(metadata);
|
|
672
|
+
if (isInteractiveTerminal() && !options.yes) {
|
|
673
|
+
const approved = await confirm({
|
|
674
|
+
message: `Install ${NPM_PACKAGE_NAME}@${resolvedVersion}?`,
|
|
675
|
+
default: false,
|
|
676
|
+
});
|
|
677
|
+
if (!approved) {
|
|
678
|
+
console.log(chalk.gray('Upgrade cancelled'));
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
spinner = ora(`${direction === 'Downgrade' ? 'Downgrading' : 'Upgrading'} ${VERSION} -> ${resolvedVersion}...`).start();
|
|
683
|
+
await installResolvedPackage(metadata);
|
|
684
|
+
spinner.succeed(`${direction}d to ${resolvedVersion}`);
|
|
685
|
+
// Only show the changelog for a genuine upgrade range.
|
|
686
|
+
if (compareVersions(resolvedVersion, VERSION) > 0) {
|
|
687
|
+
await showWhatsNew(VERSION, resolvedVersion);
|
|
688
|
+
}
|
|
737
689
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
await showWhatsNew(VERSION, resolvedVersion);
|
|
690
|
+
catch (err) {
|
|
691
|
+
if (isPromptCancelled(err))
|
|
692
|
+
return;
|
|
693
|
+
spinner.fail(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
694
|
+
console.log(chalk.gray(`Run manually: agents upgrade ${version ? version + ' ' : ''}--yes`));
|
|
744
695
|
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
// --- Lazy registration orchestration -----------------------------------------
|
|
699
|
+
/** Import a command module via its loader and register it on the program. */
|
|
700
|
+
async function reg(loader) {
|
|
701
|
+
(await loader())(program);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Register exactly the command(s) the requested top-level name needs.
|
|
705
|
+
* Returns false when the name maps to no known command (typo / unknown) so the
|
|
706
|
+
* caller can fall back to registering everything for spellcheck.
|
|
707
|
+
*
|
|
708
|
+
* Lazy commands (sessions/teams/cloud) are intentionally NOT handled here — they
|
|
709
|
+
* must register after applyGlobalHelpConventions to match main's ordering.
|
|
710
|
+
*/
|
|
711
|
+
async function registerEagerForRequest(name) {
|
|
712
|
+
switch (name) {
|
|
713
|
+
case 'memory':
|
|
714
|
+
registerMemoryCommand(program);
|
|
715
|
+
return true;
|
|
716
|
+
case 'perms':
|
|
717
|
+
// The action re-parses as `permissions`, so that target must exist too.
|
|
718
|
+
registerPermsAliasCommand(program);
|
|
719
|
+
await reg(loadPermissions);
|
|
720
|
+
return true;
|
|
721
|
+
case 'exec':
|
|
722
|
+
registerExecAliasCommand(program);
|
|
723
|
+
await reg(loadRun);
|
|
724
|
+
return true;
|
|
725
|
+
case 'jobs':
|
|
726
|
+
case 'cron':
|
|
727
|
+
registerJobsCronAliasCommand(program, name);
|
|
728
|
+
await reg(loadRoutines);
|
|
729
|
+
return true;
|
|
730
|
+
case 'upgrade':
|
|
731
|
+
registerUpgradeCommand(program);
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
const loaders = COMMAND_LOADERS[name];
|
|
735
|
+
if (!loaders)
|
|
736
|
+
return false;
|
|
737
|
+
for (const loader of loaders)
|
|
738
|
+
await reg(loader);
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Register every command in the EXACT order main does (old src/index.ts lines
|
|
743
|
+
* 691-844), including the inline deprecated aliases. Used only on the slow paths
|
|
744
|
+
* (unknown command spellcheck, "did you mean" auto-correct) where the full set
|
|
745
|
+
* of names — and their registration order, which breaks ties in the suggestion
|
|
746
|
+
* picker — must match main byte-for-byte.
|
|
747
|
+
*/
|
|
748
|
+
async function registerAllEagerCommands() {
|
|
749
|
+
await reg(loadView);
|
|
750
|
+
await reg(loadInspect);
|
|
751
|
+
await reg(loadFeedback);
|
|
752
|
+
await reg(loadCommands);
|
|
753
|
+
await reg(loadHooks);
|
|
754
|
+
await reg(loadSkills);
|
|
755
|
+
await reg(loadRules);
|
|
756
|
+
registerMemoryCommand(program);
|
|
757
|
+
await reg(loadPermissions);
|
|
758
|
+
registerPermsAliasCommand(program);
|
|
759
|
+
await reg(loadMcp);
|
|
760
|
+
await reg(loadCli);
|
|
761
|
+
await reg(loadSubagents);
|
|
762
|
+
await reg(loadPlugins);
|
|
763
|
+
await reg(loadWorkflows);
|
|
764
|
+
await reg(loadWorktree);
|
|
765
|
+
await reg(loadVersions);
|
|
766
|
+
await reg(loadImport);
|
|
767
|
+
await reg(loadPackages);
|
|
768
|
+
await reg(loadDaemon);
|
|
769
|
+
await reg(loadRoutines);
|
|
770
|
+
await reg(loadRun);
|
|
771
|
+
await reg(loadDefaults);
|
|
772
|
+
await reg(loadModels);
|
|
773
|
+
await reg(loadPrune);
|
|
774
|
+
await reg(loadTrash);
|
|
775
|
+
await reg(loadRestore);
|
|
776
|
+
await reg(loadDoctor);
|
|
777
|
+
registerExecAliasCommand(program);
|
|
778
|
+
await reg(loadProfiles);
|
|
779
|
+
await reg(loadSecrets);
|
|
780
|
+
await reg(loadWallet);
|
|
781
|
+
await reg(loadHelper);
|
|
782
|
+
await reg(loadMenubar);
|
|
783
|
+
await reg(loadBeta);
|
|
784
|
+
await reg(loadSync);
|
|
785
|
+
await reg(loadRefreshRules);
|
|
786
|
+
await reg(loadDrive);
|
|
787
|
+
await reg(loadFactory);
|
|
788
|
+
await reg(loadUsage);
|
|
789
|
+
await reg(loadCost);
|
|
790
|
+
await reg(loadBudget);
|
|
791
|
+
await reg(loadAlias);
|
|
792
|
+
await reg(loadPty);
|
|
793
|
+
await reg(loadTmux);
|
|
794
|
+
await reg(loadBrowser);
|
|
795
|
+
await reg(loadComputer);
|
|
796
|
+
registerJobsCronAliasCommand(program, 'jobs');
|
|
797
|
+
registerJobsCronAliasCommand(program, 'cron');
|
|
798
|
+
registerUpgradeCommand(program);
|
|
799
|
+
await reg(loadPull);
|
|
800
|
+
await reg(loadPush);
|
|
801
|
+
await reg(loadRepo);
|
|
802
|
+
await reg(loadSetup);
|
|
803
|
+
}
|
|
758
804
|
/** Calculate the Levenshtein edit distance between two strings. */
|
|
759
805
|
function levenshtein(a, b) {
|
|
760
806
|
const m = a.length;
|
|
@@ -801,45 +847,54 @@ program.on('command:*', (operands) => {
|
|
|
801
847
|
}
|
|
802
848
|
process.exit(1);
|
|
803
849
|
});
|
|
804
|
-
//
|
|
805
|
-
|
|
806
|
-
//
|
|
807
|
-
// fire-and-forget the next background sync. System repo gets a real fast-forward
|
|
808
|
-
// pull (read-only locally, safe). User repo and extras get fetch-only + a
|
|
809
|
-
// status marker that we'll print on the *next* invocation.
|
|
810
|
-
const { spawnDetachedSync } = await import('./lib/auto-pull.js');
|
|
811
|
-
spawnDetachedSync();
|
|
812
|
-
// First-run experience: no args + no config yet + TTY -> launch interactive setup.
|
|
813
|
-
// Skipped when stdin/stdout isn't a terminal (CI, pipes) or when user passes any args.
|
|
850
|
+
// Parse the invocation shape up front: the first non-flag token is the command,
|
|
851
|
+
// and the doc flags (--version/--help/-h) drive both the registration strategy
|
|
852
|
+
// and whether the update check + background sync run at all.
|
|
814
853
|
const passedArgs = process.argv.slice(2);
|
|
815
854
|
const requestedCommand = passedArgs.find((arg) => !arg.startsWith('-'));
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const { registerTeamsCommands } = await import('./commands/teams.js');
|
|
830
|
-
registerTeamsCommands(program);
|
|
831
|
-
break;
|
|
832
|
-
}
|
|
833
|
-
case 'cloud': {
|
|
834
|
-
const { registerCloudCommands } = await import('./commands/cloud.js');
|
|
835
|
-
registerCloudCommands(program);
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
default:
|
|
839
|
-
break;
|
|
855
|
+
// Help and version output are pure documentation — they must never gate on
|
|
856
|
+
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
857
|
+
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
858
|
+
// Register only the command(s) this invocation actually uses. Lazy commands
|
|
859
|
+
// (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
|
|
860
|
+
const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
|
|
861
|
+
if (requestedCommand !== undefined && !isLazyRequest) {
|
|
862
|
+
const known = await registerEagerForRequest(requestedCommand);
|
|
863
|
+
if (!known) {
|
|
864
|
+
// Unknown top-level command: register the full tree so the "did you mean"
|
|
865
|
+
// spellcheck and edit-distance-1 auto-correct (the command:* handler above)
|
|
866
|
+
// see the same candidate set — and ordering — as main.
|
|
867
|
+
await registerAllEagerCommands();
|
|
840
868
|
}
|
|
841
869
|
}
|
|
842
|
-
|
|
870
|
+
// When requestedCommand is undefined (bare invocation, --version, --help, -h) no
|
|
871
|
+
// command modules are needed: --version is built in and the root help text is a
|
|
872
|
+
// static string.
|
|
873
|
+
// Mirror main: help conventions are applied after the eager command tree and
|
|
874
|
+
// before the lazy commands, so the latter inherit the root's custom help
|
|
875
|
+
// formatter instead of getting the per-command recursive pass.
|
|
876
|
+
applyGlobalHelpConventions(program);
|
|
877
|
+
// Lazy commands pull in the SQLite-backed session/cloud stack; register them
|
|
878
|
+
// only when explicitly requested, keeping lightweight commands off that path.
|
|
879
|
+
if (isLazyRequest) {
|
|
880
|
+
for (const loader of COMMAND_LOADERS[requestedCommand])
|
|
881
|
+
await reg(loader);
|
|
882
|
+
}
|
|
883
|
+
// Pure documentation paths (--version / --help / -h) return immediately: skip
|
|
884
|
+
// the update check (PATH scan + cache read) and the detached background sync
|
|
885
|
+
// (spawns a child process) that every other invocation runs.
|
|
886
|
+
if (!helpOrVersionRequested) {
|
|
887
|
+
// Run update check before parsing so the upgrade notice/prompt precedes output.
|
|
888
|
+
await checkForUpdates();
|
|
889
|
+
// Surface any "behind upstream" notices from the previous detached sync, then
|
|
890
|
+
// fire-and-forget the next background sync. System repo gets a real fast-forward
|
|
891
|
+
// pull (read-only locally, safe). User repo and extras get fetch-only + a
|
|
892
|
+
// status marker that we'll print on the *next* invocation.
|
|
893
|
+
const { spawnDetachedSync } = await import('./lib/auto-pull.js');
|
|
894
|
+
spawnDetachedSync();
|
|
895
|
+
}
|
|
896
|
+
// First-run experience: no args + no config yet + TTY -> launch interactive setup.
|
|
897
|
+
// Skipped when stdin/stdout isn't a terminal (CI, pipes) or when user passes any args.
|
|
843
898
|
const metaFilePath = path.join(getUserAgentsDir(), 'agents.yaml');
|
|
844
899
|
const firstRun = passedArgs.length === 0 &&
|
|
845
900
|
!fs.existsSync(metaFilePath) &&
|
|
@@ -847,6 +902,7 @@ const firstRun = passedArgs.length === 0 &&
|
|
|
847
902
|
process.stdout.isTTY;
|
|
848
903
|
if (firstRun) {
|
|
849
904
|
try {
|
|
905
|
+
const { runSetup } = await import('./commands/setup.js');
|
|
850
906
|
await runSetup(program);
|
|
851
907
|
}
|
|
852
908
|
catch (err) {
|
|
@@ -859,9 +915,6 @@ if (firstRun) {
|
|
|
859
915
|
// Every command requires the system repo to be cloned first. `setup` is the
|
|
860
916
|
// only exemption — it's the command that does the cloning.
|
|
861
917
|
const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help']);
|
|
862
|
-
// Help and version output are pure documentation — they must never gate on
|
|
863
|
-
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
864
|
-
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
865
918
|
// Fold legacy ~/.agents-system/ into ~/.agents/.system/ BEFORE ensureInitialized
|
|
866
919
|
// runs. ensureInitialized checks for .git inside the new path; if the user is
|
|
867
920
|
// upgrading from a layout where .git lives under the legacy path, the check
|
|
@@ -916,6 +969,20 @@ if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
|
|
|
916
969
|
}
|
|
917
970
|
catch { /* migration must never block CLI startup */ }
|
|
918
971
|
}
|
|
972
|
+
// Auto-enable the macOS menu-bar helper once, for every user. Best-effort and
|
|
973
|
+
// idempotent: installMenubarLaunchAgentOnUpgrade() no-ops when not on darwin,
|
|
974
|
+
// when the user ran `agents menubar disable` (sticky opt-out), when the service
|
|
975
|
+
// is already installed, or when no helper bundle ships with this build. This is
|
|
976
|
+
// a lightweight startup self-heal (two existsSync checks then return) rather
|
|
977
|
+
// than a migration-sentinel bump, so it covers fresh installs AND upgrades
|
|
978
|
+
// without re-running the full migration for the whole user base (issue #20).
|
|
979
|
+
if (process.platform === 'darwin' && process.env.AGENTS_SKIP_MIGRATION !== '1') {
|
|
980
|
+
try {
|
|
981
|
+
const { installMenubarLaunchAgentOnUpgrade } = await import('./lib/menubar/install-menubar.js');
|
|
982
|
+
installMenubarLaunchAgentOnUpgrade();
|
|
983
|
+
}
|
|
984
|
+
catch { /* never block CLI startup on the menu bar */ }
|
|
985
|
+
}
|
|
919
986
|
try {
|
|
920
987
|
await maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested);
|
|
921
988
|
await program.parseAsync();
|