@phnx-labs/agents-cli 1.19.1 → 1.20.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/CHANGELOG.md +67 -0
- package/README.md +70 -10
- package/dist/commands/browser.js +88 -16
- package/dist/commands/cli.d.ts +14 -0
- package/dist/commands/cli.js +244 -0
- package/dist/commands/commands.js +3 -3
- package/dist/commands/computer.js +18 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/exec.js +3 -3
- package/dist/commands/factory.d.ts +3 -14
- package/dist/commands/factory.js +3 -3
- package/dist/commands/hooks.js +3 -3
- package/dist/commands/mcp.js +29 -0
- package/dist/commands/plugins.js +11 -4
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/prune.js +39 -160
- package/dist/commands/pull.js +56 -3
- package/dist/commands/routines.js +106 -13
- package/dist/commands/secrets.js +6 -8
- package/dist/commands/sessions.d.ts +36 -7
- package/dist/commands/sessions.js +130 -53
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +37 -28
- package/dist/commands/skills.js +3 -3
- package/dist/commands/teams.js +13 -0
- package/dist/commands/versions.d.ts +4 -3
- package/dist/commands/versions.js +147 -124
- package/dist/commands/view.js +12 -12
- package/dist/index.js +34 -6
- package/dist/lib/acp/harnesses.js +8 -0
- package/dist/lib/agents.js +162 -9
- package/dist/lib/browser/cdp.d.ts +8 -1
- package/dist/lib/browser/cdp.js +40 -3
- package/dist/lib/browser/chrome.d.ts +13 -0
- package/dist/lib/browser/chrome.js +42 -3
- package/dist/lib/browser/domain-skills.d.ts +51 -0
- package/dist/lib/browser/domain-skills.js +157 -0
- package/dist/lib/browser/drivers/local.js +45 -4
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/ipc.d.ts +8 -1
- package/dist/lib/browser/ipc.js +37 -28
- package/dist/lib/browser/profiles.d.ts +13 -0
- package/dist/lib/browser/profiles.js +41 -1
- package/dist/lib/browser/service.d.ts +3 -0
- package/dist/lib/browser/service.js +21 -5
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/cli-resources.d.ts +109 -0
- package/dist/lib/cli-resources.js +255 -0
- package/dist/lib/cloud/rush.js +5 -5
- package/dist/lib/command-skills.js +0 -2
- package/dist/lib/computer-rpc.d.ts +3 -0
- package/dist/lib/computer-rpc.js +53 -0
- package/dist/lib/daemon.js +20 -0
- package/dist/lib/exec.d.ts +3 -2
- package/dist/lib/exec.js +62 -6
- package/dist/lib/hooks.js +182 -0
- package/dist/lib/mcp.js +6 -0
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/overdue.d.ts +26 -0
- package/dist/lib/overdue.js +101 -0
- package/dist/lib/permissions.js +5 -1
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/profiles-presets.js +37 -0
- package/dist/lib/registry.d.ts +18 -0
- package/dist/lib/registry.js +44 -0
- package/dist/lib/resources/mcp.js +43 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/rotate.js +10 -4
- package/dist/lib/routines-format.d.ts +35 -0
- package/dist/lib/routines-format.js +173 -0
- package/dist/lib/routines.d.ts +7 -1
- package/dist/lib/routines.js +32 -12
- package/dist/lib/runner.js +19 -5
- package/dist/lib/scheduler.js +8 -1
- package/dist/lib/secrets/{AgentsKeychain.app → Agents CLI.app}/Contents/CodeResources +0 -0
- package/dist/lib/secrets/{AgentsKeychain.app/Contents/Info.plist → Agents CLI.app/Contents/Info.plist } +4 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/bundles.d.ts +33 -2
- package/dist/lib/secrets/bundles.js +249 -26
- package/dist/lib/secrets/index.d.ts +10 -1
- package/dist/lib/secrets/index.js +143 -48
- package/dist/lib/session/active.d.ts +8 -0
- package/dist/lib/session/active.js +3 -2
- package/dist/lib/session/db.d.ts +10 -4
- package/dist/lib/session/db.js +16 -16
- package/dist/lib/session/parse.d.ts +1 -0
- package/dist/lib/session/parse.js +44 -0
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +6 -2
- package/dist/lib/shims.js +88 -10
- package/dist/lib/state.d.ts +0 -1
- package/dist/lib/state.js +2 -15
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +153 -3
- package/dist/lib/teams/summarizer.js +18 -2
- package/dist/lib/teams/worktree.js +14 -3
- package/dist/lib/types.d.ts +7 -4
- package/dist/lib/types.js +6 -3
- package/dist/lib/versions.d.ts +10 -2
- package/dist/lib/versions.js +227 -35
- package/package.json +9 -9
- package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
- package/npm-shrinkwrap.json +0 -3162
- /package/dist/lib/secrets/{AgentsKeychain.app → Agents CLI.app}/Contents/_CodeSignature/CodeResources +0 -0
- /package/dist/lib/secrets/{AgentsKeychain.app → Agents CLI.app}/Contents/embedded.provisionprofile +0 -0
package/dist/commands/setup.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { Command } from 'commander';
|
|
|
9
9
|
export declare function runSetup(program: Command, options?: {
|
|
10
10
|
force?: boolean;
|
|
11
11
|
suppressFooter?: boolean;
|
|
12
|
+
systemRepo?: boolean;
|
|
12
13
|
}): Promise<void>;
|
|
13
14
|
/**
|
|
14
15
|
* Ensure the system repo exists before running a command that needs it.
|
package/dist/commands/setup.js
CHANGED
|
@@ -68,38 +68,46 @@ export async function runSetup(program, options = {}) {
|
|
|
68
68
|
for (const install of unmanaged) {
|
|
69
69
|
sessionCounts[install.agentId] = countSessionFiles(install.agentId);
|
|
70
70
|
}
|
|
71
|
+
const systemRepo = process.env.AGENTS_SYSTEM_REPO || DEFAULT_SYSTEM_REPO;
|
|
71
72
|
console.log(chalk.bold('\nWelcome to agents-cli.'));
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// --force on an existing repo: pull instead of re-clone
|
|
77
|
-
const result = await pullRepo(agentsDir);
|
|
78
|
-
if (!result.success) {
|
|
79
|
-
spinner.fail(`Pull failed: ${result.error}`);
|
|
80
|
-
console.log(chalk.gray('Fix the issue and re-run: agents setup --force'));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
spinner.succeed(`Updated to ${result.commit}`);
|
|
73
|
+
if (options.systemRepo === false) {
|
|
74
|
+
ensureAgentsDir();
|
|
75
|
+
console.log(chalk.gray('Skipping system repo clone (--no-system-repo).'));
|
|
76
|
+
console.log(chalk.gray(`Populate ~/.agents-system/ yourself before running other commands that depend on it.\n`));
|
|
84
77
|
}
|
|
85
78
|
else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
79
|
+
console.log(chalk.gray(`Cloning the system repo from ${systemRepoSlug(systemRepo)} into ~/.agents-system/.\n`));
|
|
80
|
+
ensureAgentsDir();
|
|
81
|
+
const spinner = ora('Cloning system repo...').start();
|
|
82
|
+
if (isGitRepo(agentsDir)) {
|
|
83
|
+
// --force on an existing repo: pull instead of re-clone
|
|
84
|
+
const result = await pullRepo(agentsDir);
|
|
85
|
+
if (!result.success) {
|
|
86
|
+
spinner.fail(`Pull failed: ${result.error}`);
|
|
87
|
+
console.log(chalk.gray('Fix the issue and re-run: agents setup --force'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
spinner.succeed(`Updated to ${result.commit}`);
|
|
95
91
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
else {
|
|
93
|
+
// Check git is available
|
|
94
|
+
try {
|
|
95
|
+
const { execSync } = await import('child_process');
|
|
96
|
+
execSync('which git', { stdio: 'ignore' });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
spinner.fail('git is not installed');
|
|
100
|
+
console.log(chalk.gray('Install git first: https://git-scm.com/downloads'));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const result = await cloneIntoExisting(systemRepo, agentsDir);
|
|
104
|
+
if (!result.success) {
|
|
105
|
+
spinner.fail(`Clone failed: ${result.error}`);
|
|
106
|
+
console.log(chalk.gray('Fix the issue and re-run: agents setup --force'));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
spinner.succeed(`Cloned ${systemRepoSlug(systemRepo)} (${result.commit})`);
|
|
101
110
|
}
|
|
102
|
-
spinner.succeed(`Cloned ${systemRepoSlug(DEFAULT_SYSTEM_REPO)} (${result.commit})`);
|
|
103
111
|
}
|
|
104
112
|
// Offer to import existing unmanaged installations
|
|
105
113
|
if (unmanaged.length > 0 && isInteractiveTerminal()) {
|
|
@@ -195,7 +203,8 @@ export function registerSetupCommand(program) {
|
|
|
195
203
|
const setupCmd = program
|
|
196
204
|
.command('setup')
|
|
197
205
|
.description('First-time setup. Clones a config repo and installs agent CLIs.')
|
|
198
|
-
.option('-f, --force', 'Re-run setup even if ~/.agents-system/ already exists (use with caution)')
|
|
206
|
+
.option('-f, --force', 'Re-run setup even if ~/.agents-system/ already exists (use with caution)')
|
|
207
|
+
.option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents-system/ yourself)');
|
|
199
208
|
setHelpSections(setupCmd, {
|
|
200
209
|
examples: `
|
|
201
210
|
# First-time setup (clones the system repo into ~/.agents-system/)
|
package/dist/commands/skills.js
CHANGED
|
@@ -394,17 +394,17 @@ Examples:
|
|
|
394
394
|
.action(() => {
|
|
395
395
|
console.error(chalk.red('"agents skills sync" is gone.'));
|
|
396
396
|
console.error(chalk.gray('Sync runs automatically when you launch the agent.'));
|
|
397
|
-
console.error(chalk.gray('To remove orphans, use: agents prune skills'));
|
|
397
|
+
console.error(chalk.gray('To remove orphans, use: agents prune cleanup skills'));
|
|
398
398
|
process.exit(1);
|
|
399
399
|
});
|
|
400
|
-
// `skills prune` moved to the top-level `agents prune` command.
|
|
400
|
+
// `skills prune` moved to the top-level `agents prune cleanup` command.
|
|
401
401
|
skillsCmd
|
|
402
402
|
.command('prune', { hidden: true })
|
|
403
403
|
.allowUnknownOption()
|
|
404
404
|
.allowExcessArguments()
|
|
405
405
|
.action(() => {
|
|
406
406
|
console.error(chalk.red('"agents skills prune" moved.'));
|
|
407
|
-
console.error(chalk.gray('Use: agents prune skills (or `agents prune` for everything)'));
|
|
407
|
+
console.error(chalk.gray('Use: agents prune cleanup skills (or `agents prune cleanup` for everything)'));
|
|
408
408
|
process.exit(1);
|
|
409
409
|
});
|
|
410
410
|
skillsCmd
|
package/dist/commands/teams.js
CHANGED
|
@@ -21,6 +21,8 @@ const AGENT_NAMES = {
|
|
|
21
21
|
gemini: 'Gemini',
|
|
22
22
|
cursor: 'Cursor',
|
|
23
23
|
opencode: 'OpenCode',
|
|
24
|
+
grok: 'Grok',
|
|
25
|
+
antigravity: 'Antigravity',
|
|
24
26
|
};
|
|
25
27
|
const VALID_AGENTS = Object.keys(AGENT_NAMES);
|
|
26
28
|
const VALID_MODES = ['plan', 'edit', 'full'];
|
|
@@ -756,6 +758,11 @@ export function registerTeamsCommands(program) {
|
|
|
756
758
|
die(`Invalid teammate name '${opts.name}'. Use letters, numbers, '-', or '_'.`);
|
|
757
759
|
}
|
|
758
760
|
}
|
|
761
|
+
if (opts.worktree !== undefined) {
|
|
762
|
+
if (!opts.worktree || !/^[A-Za-z0-9_-]+$/.test(opts.worktree)) {
|
|
763
|
+
die(`Invalid worktree name '${opts.worktree}'. Use letters, numbers, '-', or '_'.`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
759
766
|
const after = opts.after
|
|
760
767
|
? opts.after.split(',').map((s) => s.trim()).filter(Boolean)
|
|
761
768
|
: [];
|
|
@@ -899,6 +906,12 @@ export function registerTeamsCommands(program) {
|
|
|
899
906
|
console.log();
|
|
900
907
|
if (staged) {
|
|
901
908
|
console.log(chalk.gray(`Start the ready teammates: agents teams start ${team}`));
|
|
909
|
+
if (after.length > 0) {
|
|
910
|
+
process.stderr.write(chalk.yellow(`\nWarning: this teammate has --after dependencies and will NEVER start on its own.\n` +
|
|
911
|
+
` A supervisor watch process is required to launch it when its deps complete.\n` +
|
|
912
|
+
` Run this in another terminal:\n` +
|
|
913
|
+
` agents teams start ${team} --watch\n`));
|
|
914
|
+
}
|
|
902
915
|
}
|
|
903
916
|
else {
|
|
904
917
|
console.log(chalk.gray(`Check in later: agents teams status ${team}`));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version management commands for installing, switching, and removing agent CLIs.
|
|
3
3
|
*
|
|
4
|
-
* Implements `agents add`, `agents
|
|
5
|
-
* `agents list`. Handles npm-based installation,
|
|
4
|
+
* Implements `agents add`, `agents prune`, `agents remove` (alias),
|
|
5
|
+
* `agents use`, and the deprecated `agents list`. Handles npm-based installation,
|
|
6
|
+
* shim creation, config symlink
|
|
6
7
|
* switching, resource sync prompts, and project-level version pinning.
|
|
7
8
|
*/
|
|
8
9
|
import type { Command } from 'commander';
|
|
9
|
-
/** Register `agents add`, `agents remove`, `agents use`, and `agents list` (deprecated). */
|
|
10
|
+
/** Register `agents add`, `agents prune`, `agents remove`, `agents use`, and `agents list` (deprecated). */
|
|
10
11
|
export declare function registerVersionsCommands(program: Command): void;
|
|
@@ -7,12 +7,27 @@ import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, } f
|
|
|
7
7
|
import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
8
8
|
import { viewAction } from './view.js';
|
|
9
9
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
10
|
-
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, } from '../lib/versions.js';
|
|
10
|
+
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, printTrashFooter, } from '../lib/versions.js';
|
|
11
11
|
import { createShim, createVersionedAlias, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
|
|
12
12
|
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
13
13
|
import { tryAutoPull } from '../lib/git.js';
|
|
14
|
-
import { getAgentsDir } from '../lib/state.js';
|
|
14
|
+
import { getAgentsDir, getTrashVersionsDir } from '../lib/state.js';
|
|
15
15
|
import { setHelpSections } from '../lib/help.js';
|
|
16
|
+
import { updateSessionFilePaths } from '../lib/session/db.js';
|
|
17
|
+
/**
|
|
18
|
+
* After removeVersion soft-deletes a version dir to trash, rewrite session
|
|
19
|
+
* file_path entries in the DB so reads still work from the new trash location.
|
|
20
|
+
*/
|
|
21
|
+
function fixSessionFilePaths(agent, version, oldVersionDir) {
|
|
22
|
+
const trashAgentDir = path.join(getTrashVersionsDir(), agent, version);
|
|
23
|
+
if (!fs.existsSync(trashAgentDir))
|
|
24
|
+
return;
|
|
25
|
+
const stamps = fs.readdirSync(trashAgentDir).sort().reverse();
|
|
26
|
+
if (stamps.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
const trashPath = path.join(trashAgentDir, stamps[0]);
|
|
29
|
+
updateSessionFilePaths(oldVersionDir, trashPath);
|
|
30
|
+
}
|
|
16
31
|
/**
|
|
17
32
|
* Helper to get actual installed version for an agent.
|
|
18
33
|
* Returns the latest installed version, or throws if none installed.
|
|
@@ -78,7 +93,130 @@ function warnIfShimShadowed(agent) {
|
|
|
78
93
|
console.log(chalk.gray(` Managed shim: ${getShimPath(agent)}`));
|
|
79
94
|
console.log(chalk.gray(` ${getPathSetupInstructions().split('\n').join('\n ')}`));
|
|
80
95
|
}
|
|
81
|
-
|
|
96
|
+
async function versionPruneAction(specs, options, commandName) {
|
|
97
|
+
const isProject = options.project;
|
|
98
|
+
const moved = [];
|
|
99
|
+
for (const spec of specs) {
|
|
100
|
+
const parsed = parseAgentSpec(spec);
|
|
101
|
+
if (!parsed) {
|
|
102
|
+
console.log(chalk.red(`Invalid agent: ${spec}`));
|
|
103
|
+
console.log(chalk.gray(`Format: <agent>[@version]. Available: ${ALL_AGENT_IDS.join(', ')}`));
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const { agent, version } = parsed;
|
|
107
|
+
const agentConfig = AGENTS[agent];
|
|
108
|
+
if (version === 'latest' || !spec.includes('@')) {
|
|
109
|
+
const versions = listInstalledVersions(agent);
|
|
110
|
+
if (versions.length === 0) {
|
|
111
|
+
console.log(chalk.gray(`No versions of ${agentLabel(agentConfig.id)} installed`));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!isInteractiveTerminal()) {
|
|
115
|
+
requireInteractiveSelection(`Selecting ${agentLabel(agentConfig.id)} versions to ${commandName}`, [
|
|
116
|
+
`agents ${commandName} ${agent}@${versions[0]}`,
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
const globalDefault = getGlobalDefault(agent);
|
|
120
|
+
const sortedVersions = [...versions].sort((a, b) => {
|
|
121
|
+
if (a === globalDefault)
|
|
122
|
+
return -1;
|
|
123
|
+
if (b === globalDefault)
|
|
124
|
+
return 1;
|
|
125
|
+
return 0;
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
const toRemove = await checkbox({
|
|
129
|
+
message: `Select ${agentLabel(agentConfig.id)} versions to ${commandName}:`,
|
|
130
|
+
choices: sortedVersions.map((v) => ({
|
|
131
|
+
name: v === globalDefault ? `${v} ${chalk.green('(default)')}` : v,
|
|
132
|
+
value: v,
|
|
133
|
+
checked: false,
|
|
134
|
+
})),
|
|
135
|
+
});
|
|
136
|
+
if (toRemove.length === 0) {
|
|
137
|
+
console.log(chalk.gray('No versions selected'));
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
for (const v of toRemove) {
|
|
141
|
+
const versionDir = getVersionDir(agent, v);
|
|
142
|
+
removeVersion(agent, v);
|
|
143
|
+
fixSessionFilePaths(agent, v, versionDir);
|
|
144
|
+
console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${v} to trash`));
|
|
145
|
+
moved.push({ agent, version: v });
|
|
146
|
+
}
|
|
147
|
+
if (globalDefault && toRemove.includes(globalDefault)) {
|
|
148
|
+
setGlobalDefault(agent, undefined);
|
|
149
|
+
console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
|
|
150
|
+
}
|
|
151
|
+
const remaining = listInstalledVersions(agent);
|
|
152
|
+
if (remaining.length === 0) {
|
|
153
|
+
removeShim(agent);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
if (isPromptCancelled(err)) {
|
|
158
|
+
console.log(chalk.gray('Cancelled'));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (!isVersionInstalled(agent, version)) {
|
|
165
|
+
console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${version} not installed`));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const versionDir = getVersionDir(agent, version);
|
|
169
|
+
removeVersion(agent, version);
|
|
170
|
+
fixSessionFilePaths(agent, version, versionDir);
|
|
171
|
+
console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${version} to trash`));
|
|
172
|
+
moved.push({ agent, version });
|
|
173
|
+
const remaining = listInstalledVersions(agent);
|
|
174
|
+
if (remaining.length === 0) {
|
|
175
|
+
removeShim(agent);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (isProject) {
|
|
179
|
+
const projectManifestPath = path.join(process.cwd(), '.agents', 'agents.yaml');
|
|
180
|
+
if (fs.existsSync(projectManifestPath)) {
|
|
181
|
+
const manifest = readManifest(process.cwd());
|
|
182
|
+
if (manifest?.agents?.[agent]) {
|
|
183
|
+
delete manifest.agents[agent];
|
|
184
|
+
writeManifest(process.cwd(), manifest);
|
|
185
|
+
console.log(chalk.gray(` Removed from .agents/agents.yaml`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
printTrashFooter(moved);
|
|
191
|
+
}
|
|
192
|
+
function configureVersionPruneCommand(cmd, commandName) {
|
|
193
|
+
const isAlias = commandName === 'remove';
|
|
194
|
+
cmd
|
|
195
|
+
.description(isAlias
|
|
196
|
+
? 'Alias for agents prune. Uninstalls agent CLI versions.'
|
|
197
|
+
: 'Uninstall agent CLI versions. Moves version data to trash for recovery.')
|
|
198
|
+
.option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project');
|
|
199
|
+
setHelpSections(cmd, {
|
|
200
|
+
examples: `
|
|
201
|
+
# Prune a specific version
|
|
202
|
+
agents ${commandName} claude@2.0.50
|
|
203
|
+
|
|
204
|
+
# Pick interactively if you omit the version
|
|
205
|
+
agents ${commandName} claude
|
|
206
|
+
|
|
207
|
+
# Prune and also clear the project pin
|
|
208
|
+
agents ${commandName} claude@2.0.50 --project
|
|
209
|
+
`,
|
|
210
|
+
notes: `
|
|
211
|
+
- Pruned version directories move to trash with their home/ data intact.
|
|
212
|
+
- Session file paths are rewritten so session history remains readable.
|
|
213
|
+
- Removing the default version unsets the default; run 'agents use' to pick a new one.
|
|
214
|
+
- Reinstall any time with 'agents add'.
|
|
215
|
+
`,
|
|
216
|
+
});
|
|
217
|
+
cmd.action((specs, options) => versionPruneAction(specs, options, commandName));
|
|
218
|
+
}
|
|
219
|
+
/** Register `agents add`, `agents prune`, `agents remove`, `agents use`, and `agents list` (deprecated). */
|
|
82
220
|
export function registerVersionsCommands(program) {
|
|
83
221
|
const addCmd = program
|
|
84
222
|
.command('add <specs...>')
|
|
@@ -180,9 +318,9 @@ export function registerVersionsCommands(program) {
|
|
|
180
318
|
selection = userSelection;
|
|
181
319
|
}
|
|
182
320
|
}
|
|
183
|
-
else if (hasNewResources(newResources, agent)) {
|
|
321
|
+
else if (hasNewResources(newResources, agent, installedVersion)) {
|
|
184
322
|
// Some synced, but NEW resources available - prompt for new only
|
|
185
|
-
const userSelection = await promptNewResourceSelection(agent, newResources);
|
|
323
|
+
const userSelection = await promptNewResourceSelection(agent, newResources, installedVersion);
|
|
186
324
|
if (userSelection) {
|
|
187
325
|
selection = userSelection;
|
|
188
326
|
}
|
|
@@ -296,123 +434,8 @@ export function registerVersionsCommands(program) {
|
|
|
296
434
|
}
|
|
297
435
|
}
|
|
298
436
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
.description('Uninstall agent CLI versions. Frees disk space and removes the version\'s auth token.')
|
|
302
|
-
.option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project');
|
|
303
|
-
setHelpSections(removeCmd, {
|
|
304
|
-
examples: `
|
|
305
|
-
# Remove a specific version
|
|
306
|
-
agents remove claude@2.0.50
|
|
307
|
-
|
|
308
|
-
# Pick interactively if you omit the version
|
|
309
|
-
agents remove claude
|
|
310
|
-
|
|
311
|
-
# Remove and also clear the project pin
|
|
312
|
-
agents remove claude@2.0.50 --project
|
|
313
|
-
`,
|
|
314
|
-
notes: `
|
|
315
|
-
- Removing the default version unsets the default; run 'agents use' to pick a new one.
|
|
316
|
-
- Reinstall any time with 'agents add'.
|
|
317
|
-
`,
|
|
318
|
-
});
|
|
319
|
-
removeCmd.action(async (specs, options) => {
|
|
320
|
-
const isProject = options.project;
|
|
321
|
-
for (const spec of specs) {
|
|
322
|
-
const parsed = parseAgentSpec(spec);
|
|
323
|
-
if (!parsed) {
|
|
324
|
-
console.log(chalk.red(`Invalid agent: ${spec}`));
|
|
325
|
-
console.log(chalk.gray(`Format: <agent>[@version]. Available: ${ALL_AGENT_IDS.join(', ')}`));
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
const { agent, version } = parsed;
|
|
329
|
-
const agentConfig = AGENTS[agent];
|
|
330
|
-
if (version === 'latest' || !spec.includes('@')) {
|
|
331
|
-
// Show picker for which versions to remove
|
|
332
|
-
const versions = listInstalledVersions(agent);
|
|
333
|
-
if (versions.length === 0) {
|
|
334
|
-
console.log(chalk.gray(`No versions of ${agentLabel(agentConfig.id)} installed`));
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (!isInteractiveTerminal()) {
|
|
338
|
-
requireInteractiveSelection(`Selecting ${agentLabel(agentConfig.id)} versions to remove`, [
|
|
339
|
-
`agents remove ${agent}@${versions[0]}`,
|
|
340
|
-
]);
|
|
341
|
-
}
|
|
342
|
-
const globalDefault = getGlobalDefault(agent);
|
|
343
|
-
// Sort versions with default first
|
|
344
|
-
const sortedVersions = [...versions].sort((a, b) => {
|
|
345
|
-
if (a === globalDefault)
|
|
346
|
-
return -1;
|
|
347
|
-
if (b === globalDefault)
|
|
348
|
-
return 1;
|
|
349
|
-
return 0;
|
|
350
|
-
});
|
|
351
|
-
try {
|
|
352
|
-
const toRemove = await checkbox({
|
|
353
|
-
message: `Select ${agentLabel(agentConfig.id)} versions to remove:`,
|
|
354
|
-
choices: sortedVersions.map((v) => ({
|
|
355
|
-
name: v === globalDefault ? `${v} ${chalk.green('(default)')}` : v,
|
|
356
|
-
value: v,
|
|
357
|
-
checked: false, // All unchecked by default
|
|
358
|
-
})),
|
|
359
|
-
});
|
|
360
|
-
if (toRemove.length === 0) {
|
|
361
|
-
console.log(chalk.gray('No versions selected'));
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
for (const v of toRemove) {
|
|
365
|
-
removeVersion(agent, v);
|
|
366
|
-
console.log(chalk.green(`Removed ${agentLabel(agentConfig.id)}@${v}`));
|
|
367
|
-
}
|
|
368
|
-
// Check if default was removed
|
|
369
|
-
if (globalDefault && toRemove.includes(globalDefault)) {
|
|
370
|
-
setGlobalDefault(agent, undefined);
|
|
371
|
-
console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
|
|
372
|
-
}
|
|
373
|
-
// Remove shim if no versions left
|
|
374
|
-
const remaining = listInstalledVersions(agent);
|
|
375
|
-
if (remaining.length === 0) {
|
|
376
|
-
removeShim(agent);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
catch (err) {
|
|
380
|
-
if (isPromptCancelled(err)) {
|
|
381
|
-
console.log(chalk.gray('Cancelled'));
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
throw err;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
// Remove specific version
|
|
389
|
-
if (!isVersionInstalled(agent, version)) {
|
|
390
|
-
console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${version} not installed`));
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
removeVersion(agent, version);
|
|
394
|
-
console.log(chalk.green(`Removed ${agentLabel(agentConfig.id)}@${version}`));
|
|
395
|
-
// Remove shim if no versions left
|
|
396
|
-
const remaining = listInstalledVersions(agent);
|
|
397
|
-
if (remaining.length === 0) {
|
|
398
|
-
removeShim(agent);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// Update project manifest if -p flag
|
|
403
|
-
if (isProject) {
|
|
404
|
-
const projectManifestPath = path.join(process.cwd(), '.agents', 'agents.yaml');
|
|
405
|
-
if (fs.existsSync(projectManifestPath)) {
|
|
406
|
-
const manifest = readManifest(process.cwd());
|
|
407
|
-
if (manifest?.agents?.[agent]) {
|
|
408
|
-
delete manifest.agents[agent];
|
|
409
|
-
writeManifest(process.cwd(), manifest);
|
|
410
|
-
console.log(chalk.gray(` Removed from .agents/agents.yaml`));
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
});
|
|
437
|
+
configureVersionPruneCommand(program.command('prune <specs...>'), 'prune');
|
|
438
|
+
configureVersionPruneCommand(program.command('remove <specs...>', { hidden: true }), 'remove');
|
|
416
439
|
const useCmd = program
|
|
417
440
|
.command('use <agent> [version]')
|
|
418
441
|
.description('Switch the active version for an agent. This is the only command that sets the default.')
|
|
@@ -619,9 +642,9 @@ export function registerVersionsCommands(program) {
|
|
|
619
642
|
}
|
|
620
643
|
}
|
|
621
644
|
}
|
|
622
|
-
else if (hasNewResources(newResources, agentId)) {
|
|
645
|
+
else if (hasNewResources(newResources, agentId, finalVersion)) {
|
|
623
646
|
// Has synced before, but NEW items available
|
|
624
|
-
const userSelection = await promptNewResourceSelection(agentId, newResources);
|
|
647
|
+
const userSelection = await promptNewResourceSelection(agentId, newResources, finalVersion);
|
|
625
648
|
if (userSelection && Object.keys(userSelection).length > 0) {
|
|
626
649
|
const syncResult = syncResourcesToVersion(agentId, finalVersion, userSelection);
|
|
627
650
|
const syncedTypes = [];
|
package/dist/commands/view.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as path from 'path';
|
|
|
5
5
|
import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
|
|
6
6
|
import { formatUsageSection, formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
7
7
|
import { readManifest } from '../lib/manifest.js';
|
|
8
|
-
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, } from '../lib/versions.js';
|
|
8
|
+
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, } from '../lib/versions.js';
|
|
9
9
|
import { getShimsDir, isShimsInPath, ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
|
|
10
10
|
import { getAgentResources } from '../lib/resources.js';
|
|
11
11
|
import { WORKFLOW_CAPABLE_AGENTS } from '../lib/workflows.js';
|
|
@@ -366,7 +366,7 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
366
366
|
const newResources = getNewResources(available, synced);
|
|
367
367
|
if (hasNewResources(newResources, filterAgentId, defaultVersion)) {
|
|
368
368
|
try {
|
|
369
|
-
const selection = await promptNewResourceSelection(filterAgentId, newResources);
|
|
369
|
+
const selection = await promptNewResourceSelection(filterAgentId, newResources, defaultVersion);
|
|
370
370
|
if (selection && Object.keys(selection).length > 0) {
|
|
371
371
|
const result = syncResourcesToVersion(filterAgentId, defaultVersion, selection);
|
|
372
372
|
const synced = [];
|
|
@@ -833,13 +833,12 @@ async function buildAgentPrunePlan(agentId) {
|
|
|
833
833
|
return { agentId, toPrune, skippedDefaults };
|
|
834
834
|
}
|
|
835
835
|
async function executePrunePlan(plan) {
|
|
836
|
-
|
|
836
|
+
const moved = [];
|
|
837
837
|
for (const p of plan.toPrune) {
|
|
838
|
-
console.log(chalk.gray(`Removing ${agentLabel(p.agentId)}@${p.version}...`));
|
|
839
838
|
const ok = removeVersion(p.agentId, p.version);
|
|
840
839
|
if (ok) {
|
|
841
|
-
console.log(chalk.green(`
|
|
842
|
-
|
|
840
|
+
console.log(chalk.green(`Moved ${agentLabel(p.agentId)}@${p.version} to trash`));
|
|
841
|
+
moved.push({ agent: p.agentId, version: p.version });
|
|
843
842
|
}
|
|
844
843
|
else {
|
|
845
844
|
console.log(chalk.yellow(`Already gone: ${agentLabel(p.agentId)}@${p.version}`));
|
|
@@ -848,7 +847,7 @@ async function executePrunePlan(plan) {
|
|
|
848
847
|
if (listInstalledVersions(plan.agentId).length === 0) {
|
|
849
848
|
removeShim(plan.agentId);
|
|
850
849
|
}
|
|
851
|
-
return
|
|
850
|
+
return moved;
|
|
852
851
|
}
|
|
853
852
|
function printPrunePlan(plan, isFirst) {
|
|
854
853
|
if (plan.skippedDefaults.length > 0) {
|
|
@@ -897,7 +896,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
|
|
|
897
896
|
return;
|
|
898
897
|
}
|
|
899
898
|
const totalCandidates = actionable.reduce((n, plan) => n + plan.toPrune.length, 0);
|
|
900
|
-
|
|
899
|
+
const allMoved = [];
|
|
901
900
|
let isFirst = true;
|
|
902
901
|
let processedAny = false;
|
|
903
902
|
for (const plan of actionable) {
|
|
@@ -916,10 +915,10 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
|
|
|
916
915
|
if (!isInteractiveTerminal()) {
|
|
917
916
|
console.log(chalk.red('Refusing to prune in a non-interactive shell without --yes.'));
|
|
918
917
|
if (filterAgentId) {
|
|
919
|
-
console.log(chalk.gray(`Re-run with: agents prune ${filterAgentId} --dry-run`));
|
|
918
|
+
console.log(chalk.gray(`Re-run with: agents prune cleanup ${filterAgentId} --dry-run`));
|
|
920
919
|
}
|
|
921
920
|
else {
|
|
922
|
-
console.log(chalk.gray('Re-run with: agents prune --dry-run'));
|
|
921
|
+
console.log(chalk.gray('Re-run with: agents prune cleanup --dry-run'));
|
|
923
922
|
}
|
|
924
923
|
process.exit(1);
|
|
925
924
|
}
|
|
@@ -943,7 +942,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
|
|
|
943
942
|
break;
|
|
944
943
|
}
|
|
945
944
|
}
|
|
946
|
-
|
|
945
|
+
allMoved.push(...(await executePrunePlan(plan)));
|
|
947
946
|
processedAny = true;
|
|
948
947
|
isFirst = false;
|
|
949
948
|
console.log();
|
|
@@ -953,7 +952,8 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
|
|
|
953
952
|
return;
|
|
954
953
|
}
|
|
955
954
|
if (processedAny) {
|
|
956
|
-
console.log(chalk.bold(`Pruned ${
|
|
955
|
+
console.log(chalk.bold(`Pruned ${allMoved.length} version${allMoved.length === 1 ? '' : 's'}.`));
|
|
956
|
+
printTrashFooter(allMoved);
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
959
|
/**
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,7 @@ import { registerSkillsCommands } from './commands/skills.js';
|
|
|
66
66
|
import { registerRulesCommands } from './commands/rules.js';
|
|
67
67
|
import { registerPermissionsCommands } from './commands/permissions.js';
|
|
68
68
|
import { registerMcpCommands } from './commands/mcp.js';
|
|
69
|
+
import { registerCliCommands } from './commands/cli.js';
|
|
69
70
|
import { registerVersionsCommands } from './commands/versions.js';
|
|
70
71
|
import { registerImportCommand } from './commands/import.js';
|
|
71
72
|
import { registerPackagesCommands } from './commands/packages.js';
|
|
@@ -120,11 +121,12 @@ Quick start:
|
|
|
120
121
|
agents sessions Browse past sessions across all agents
|
|
121
122
|
|
|
122
123
|
Agent versions:
|
|
123
|
-
add <agent>[@version] Install an agent CLI (e.g. agents add codex)
|
|
124
|
+
add <agent>[@version] Install an agent CLI (e.g. agents add grok or agents add codex)
|
|
124
125
|
import <agent> Adopt an existing global install (npm/homebrew) into agents-cli
|
|
125
|
-
|
|
126
|
+
prune <agent>[@version] Uninstall a version
|
|
127
|
+
remove <agent>[@version] Alias for prune
|
|
126
128
|
use <agent>@<version> Set the default version
|
|
127
|
-
prune [target]
|
|
129
|
+
prune cleanup [target] Remove orphan resources and older duplicate version installs
|
|
128
130
|
trash Inspect and restore soft-deleted version directories
|
|
129
131
|
view [agent[@version]] List versions, or inspect one in detail
|
|
130
132
|
|
|
@@ -171,7 +173,7 @@ Automation tips:
|
|
|
171
173
|
Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
|
|
172
174
|
Use --yes for defaults Auto-accept sync/default prompts on add/use/pull
|
|
173
175
|
Use --names for central items e.g. agents commands add --names review-pr,debug
|
|
174
|
-
Use agent@version targets e.g. --agents claude@2.1.79,codex@default
|
|
176
|
+
Use agent@version targets e.g. --agents grok@0.1.218,claude@2.1.79,codex@default
|
|
175
177
|
Non-TTY shells apply defaults Omitted required selections fail with a plain hint
|
|
176
178
|
|
|
177
179
|
Options:
|
|
@@ -361,7 +363,13 @@ async function promptUpgrade(latestVersion) {
|
|
|
361
363
|
console.log();
|
|
362
364
|
}
|
|
363
365
|
}
|
|
364
|
-
/**
|
|
366
|
+
/**
|
|
367
|
+
* Background update check — fires once per 24h cache window.
|
|
368
|
+
* Network: GET registry.npmjs.org/@phnx-labs/agents-cli/latest.
|
|
369
|
+
* Disable: set AGENTS_CLI_DISABLE_AUTO_UPDATE=1 in shell rc.
|
|
370
|
+
*
|
|
371
|
+
* Fire-and-forget; never blocks the CLI's foreground operation.
|
|
372
|
+
*/
|
|
365
373
|
function refreshUpdateCacheInBackground() {
|
|
366
374
|
fetch('https://registry.npmjs.org/@phnx-labs/agents-cli/latest', {
|
|
367
375
|
signal: AbortSignal.timeout(2000),
|
|
@@ -529,6 +537,7 @@ program
|
|
|
529
537
|
await program.parseAsync(['node', 'agents', ...args]);
|
|
530
538
|
});
|
|
531
539
|
registerMcpCommands(program);
|
|
540
|
+
registerCliCommands(program);
|
|
532
541
|
registerSubagentsCommands(program);
|
|
533
542
|
registerPluginsCommands(program);
|
|
534
543
|
registerWorkflowsCommands(program);
|
|
@@ -726,7 +735,13 @@ if (firstRun) {
|
|
|
726
735
|
// Every command requires the system repo to be cloned first. `setup` is the
|
|
727
736
|
// only exemption — it's the command that does the cloning.
|
|
728
737
|
const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help']);
|
|
729
|
-
|
|
738
|
+
// Help and version output are pure documentation — they must never gate on
|
|
739
|
+
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
740
|
+
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
741
|
+
if (!firstRun &&
|
|
742
|
+
requestedCommand &&
|
|
743
|
+
!SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
|
|
744
|
+
!helpOrVersionRequested) {
|
|
730
745
|
const { ensureInitialized } = await import('./commands/setup.js');
|
|
731
746
|
await ensureInitialized(program);
|
|
732
747
|
}
|
|
@@ -771,5 +786,18 @@ catch (err) {
|
|
|
771
786
|
if (err instanceof Error && err.name === 'ExitPromptError') {
|
|
772
787
|
process.exit(130);
|
|
773
788
|
}
|
|
789
|
+
// Browser-daemon-not-running and CDP-not-reachable surface as typed errors
|
|
790
|
+
// from src/lib/browser/. Don't dump a Node stacktrace for these — they are
|
|
791
|
+
// user-actionable, not engineering bugs. See issues #41 and #43.
|
|
792
|
+
if (err instanceof Error) {
|
|
793
|
+
const isBrowserDaemonNotRunning = err.name === 'BrowserDaemonNotRunningError';
|
|
794
|
+
const isBrowserCdpUnreachable = err.name === 'BrowserCdpConnectionError';
|
|
795
|
+
const isBrowserIpcDown = err.message.startsWith('IPC error:') &&
|
|
796
|
+
(err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT'));
|
|
797
|
+
if (isBrowserDaemonNotRunning || isBrowserCdpUnreachable || isBrowserIpcDown) {
|
|
798
|
+
console.error(err.message);
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
774
802
|
throw err;
|
|
775
803
|
}
|