@phnx-labs/agents-cli 1.20.0 → 1.20.3
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 +73 -0
- package/README.md +4 -4
- package/dist/commands/cli.js +3 -3
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +24 -7
- package/dist/commands/exec.js +36 -16
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +86 -7
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +117 -4
- package/dist/commands/pull.js +4 -4
- package/dist/commands/routines.js +6 -6
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +74 -39
- package/dist/commands/skills.js +22 -5
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +48 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.js +4 -4
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +164 -8
- package/dist/commands/workflows.js +29 -6
- package/dist/index.js +4 -0
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +18 -14
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/chrome.js +4 -0
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/profiles.d.ts +3 -3
- package/dist/lib/browser/profiles.js +3 -3
- package/dist/lib/browser/service.js +19 -0
- package/dist/lib/browser/types.d.ts +4 -4
- package/dist/lib/cli-resources.d.ts +36 -8
- package/dist/lib/cli-resources.js +268 -46
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +39 -11
- package/dist/lib/exec.js +90 -31
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +68 -15
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +40 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +51 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +187 -8
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/routines-format.d.ts +17 -5
- package/dist/lib/routines-format.js +37 -16
- package/dist/lib/routines.d.ts +1 -1
- package/dist/lib/routines.js +2 -2
- package/dist/lib/runner.js +64 -10
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +18 -22
- package/dist/lib/secrets/bundles.js +75 -99
- package/dist/lib/secrets/index.d.ts +51 -27
- package/dist/lib/secrets/index.js +147 -156
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/shims.d.ts +4 -1
- package/dist/lib/shims.js +5 -35
- package/dist/lib/state.d.ts +14 -1
- package/dist/lib/state.js +49 -5
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +47 -21
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/types.d.ts +57 -1
- package/dist/lib/types.js +2 -0
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +35 -1
- package/dist/lib/versions.js +267 -64
- package/package.json +9 -8
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
package/dist/commands/utils.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Small helpers used across multiple commands: prompt cancellation detection,
|
|
5
5
|
* table formatting, spinner management, and platform-specific workarounds.
|
|
6
6
|
*/
|
|
7
|
+
import type { AgentId } from '../lib/types.js';
|
|
8
|
+
import { type InstalledAgentTargetResult, type VersionSelectionResult } from '../lib/versions.js';
|
|
7
9
|
/**
|
|
8
10
|
* Check if an error is from user cancelling a prompt (Ctrl+C)
|
|
9
11
|
*/
|
|
@@ -58,3 +60,34 @@ export declare function promptRemovalTargets(resourceName: string, targets: Remo
|
|
|
58
60
|
* Format a path for display, using ~ for home directory
|
|
59
61
|
*/
|
|
60
62
|
export declare function formatPath(fullPath: string, cwd?: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Make sure every specific `agent@x.y.z` the user typed is installed before
|
|
65
|
+
* the caller resolves targets. Returns true if the caller should continue,
|
|
66
|
+
* false if the user declined the prompt. Exported so non-standard caller
|
|
67
|
+
* shapes (e.g. mcp.ts's manifest-shaped parser) can run the pre-flight
|
|
68
|
+
* without going through resolveAgentVersionTargets first.
|
|
69
|
+
*/
|
|
70
|
+
export declare function ensureAgentVersionsInstalled(value: string, availableAgents: readonly AgentId[], options?: {
|
|
71
|
+
yes?: boolean;
|
|
72
|
+
}): Promise<boolean>;
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a `--agents` selector and, if any requested `agent@version` isn't
|
|
75
|
+
* installed yet, prompt to install it (or auto-install with --yes) before
|
|
76
|
+
* delegating to resolveAgentVersionTargets. Returns null when the user
|
|
77
|
+
* declines the install prompt — callers should treat that as a clean cancel.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveAgentTargetsAutoInstalling(value: string, availableAgents: readonly AgentId[], options?: {
|
|
80
|
+
yes?: boolean;
|
|
81
|
+
allVersions?: boolean;
|
|
82
|
+
}): Promise<VersionSelectionResult | null>;
|
|
83
|
+
/**
|
|
84
|
+
* Same as resolveAgentTargetsAutoInstalling but returns the broader
|
|
85
|
+
* InstalledAgentTargetResult that includes `directAgents` (for paths like
|
|
86
|
+
* `agents install` and `mcp register` that fall through to unmanaged homes
|
|
87
|
+
* when no managed version is installed).
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveInstalledAgentTargetsAutoInstalling(value: string, availableAgents: readonly AgentId[], options?: {
|
|
90
|
+
yes?: boolean;
|
|
91
|
+
allVersions?: boolean;
|
|
92
|
+
}): Promise<InstalledAgentTargetResult | null>;
|
|
93
|
+
export { VersionNotInstalledError } from '../lib/versions.js';
|
package/dist/commands/utils.js
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
import { spawnSync } from 'child_process';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
import { confirm } from '@inquirer/prompts';
|
|
12
|
+
import { agentLabel, resolveAgentName } from '../lib/agents.js';
|
|
13
|
+
import { installVersion, listInstalledVersions, resolveAgentVersionTargets, resolveInstalledAgentTargets, } from '../lib/versions.js';
|
|
10
14
|
/**
|
|
11
15
|
* Check if an error is from user cancelling a prompt (Ctrl+C)
|
|
12
16
|
*/
|
|
@@ -135,3 +139,138 @@ export function formatPath(fullPath, cwd) {
|
|
|
135
139
|
}
|
|
136
140
|
return fullPath;
|
|
137
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Parse a --agents selector and collect every (agentId, specificVersion) pair
|
|
144
|
+
* the user requested where the version is a concrete x.y.z (not `default`,
|
|
145
|
+
* not `all`, not `latest`) and is NOT currently installed.
|
|
146
|
+
*
|
|
147
|
+
* This is the lookahead the auto-install wrappers use to decide whether to
|
|
148
|
+
* prompt + install before delegating to resolveAgentVersionTargets.
|
|
149
|
+
*/
|
|
150
|
+
function collectMissingVersions(value, availableAgents) {
|
|
151
|
+
const missing = [];
|
|
152
|
+
const seen = new Set();
|
|
153
|
+
for (const raw of value.split(',').map((s) => s.trim()).filter(Boolean)) {
|
|
154
|
+
// Literal `all` / `all@all` expand to per-agent — never missing.
|
|
155
|
+
if (raw === 'all' || raw === 'all@all')
|
|
156
|
+
continue;
|
|
157
|
+
const atIndex = raw.indexOf('@');
|
|
158
|
+
if (atIndex === -1)
|
|
159
|
+
continue; // bare agent → resolves to default; never missing in this sense
|
|
160
|
+
const agentToken = raw.slice(0, atIndex).trim();
|
|
161
|
+
const versionToken = raw.slice(atIndex + 1).trim();
|
|
162
|
+
// Non-specific selectors handled by the underlying resolver.
|
|
163
|
+
if (!versionToken || versionToken === 'default' || versionToken === 'all')
|
|
164
|
+
continue;
|
|
165
|
+
const agentId = resolveAgentName(agentToken);
|
|
166
|
+
if (!agentId || !availableAgents.includes(agentId))
|
|
167
|
+
continue;
|
|
168
|
+
const installed = listInstalledVersions(agentId);
|
|
169
|
+
if (installed.includes(versionToken))
|
|
170
|
+
continue;
|
|
171
|
+
const key = `${agentId}@${versionToken}`;
|
|
172
|
+
if (seen.has(key))
|
|
173
|
+
continue;
|
|
174
|
+
seen.add(key);
|
|
175
|
+
missing.push({ agentId, version: versionToken });
|
|
176
|
+
}
|
|
177
|
+
return missing;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Sequentially install every requested missing version with a per-version
|
|
181
|
+
* spinner. Aborts via process.exit(1) on the first failure — the user
|
|
182
|
+
* already approved the install so a partial-install outcome is worse than
|
|
183
|
+
* a hard stop.
|
|
184
|
+
*/
|
|
185
|
+
async function installMissingVersions(missing) {
|
|
186
|
+
for (const { agentId, version } of missing) {
|
|
187
|
+
const label = `${agentLabel(agentId)}@${version}`;
|
|
188
|
+
const spinner = ora(`Installing ${label}...`).start();
|
|
189
|
+
try {
|
|
190
|
+
const result = await installVersion(agentId, version, (msg) => {
|
|
191
|
+
spinner.text = msg;
|
|
192
|
+
});
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
spinner.fail(`Failed to install ${label}: ${result.error ?? 'unknown error'}`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
spinner.succeed(`Installed ${label}`);
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
spinner.fail(`Failed to install ${label}: ${err.message}`);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Make sure every specific `agent@x.y.z` the user typed is installed before
|
|
207
|
+
* the caller resolves targets. Returns true if the caller should continue,
|
|
208
|
+
* false if the user declined the prompt. Exported so non-standard caller
|
|
209
|
+
* shapes (e.g. mcp.ts's manifest-shaped parser) can run the pre-flight
|
|
210
|
+
* without going through resolveAgentVersionTargets first.
|
|
211
|
+
*/
|
|
212
|
+
export async function ensureAgentVersionsInstalled(value, availableAgents, options = {}) {
|
|
213
|
+
const missing = collectMissingVersions(value, availableAgents);
|
|
214
|
+
if (missing.length === 0)
|
|
215
|
+
return true;
|
|
216
|
+
const summary = missing.map((m) => `${agentLabel(m.agentId)}@${m.version}`).join(', ');
|
|
217
|
+
if (!options.yes) {
|
|
218
|
+
if (!isInteractiveTerminal()) {
|
|
219
|
+
console.error(chalk.red(`Missing agent version(s): ${summary}`));
|
|
220
|
+
console.error(chalk.gray('In a scripted shell, opt in to auto-install:'));
|
|
221
|
+
console.error(chalk.cyan(` rerun with --yes`));
|
|
222
|
+
console.error(chalk.gray('Or pre-install:'));
|
|
223
|
+
for (const m of missing) {
|
|
224
|
+
console.error(chalk.cyan(` agents add ${m.agentId}@${m.version}`));
|
|
225
|
+
}
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
console.log(chalk.yellow(`\nThe following agent version(s) are not installed:`));
|
|
229
|
+
for (const m of missing) {
|
|
230
|
+
console.log(` ${chalk.cyan(`${agentLabel(m.agentId)}@${m.version}`)}`);
|
|
231
|
+
}
|
|
232
|
+
let proceed;
|
|
233
|
+
try {
|
|
234
|
+
proceed = await confirm({
|
|
235
|
+
message: `Install ${missing.length} missing version${missing.length === 1 ? '' : 's'}?`,
|
|
236
|
+
default: true,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
if (isPromptCancelled(err))
|
|
241
|
+
return false;
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
if (!proceed)
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
await installMissingVersions(missing);
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Resolve a `--agents` selector and, if any requested `agent@version` isn't
|
|
252
|
+
* installed yet, prompt to install it (or auto-install with --yes) before
|
|
253
|
+
* delegating to resolveAgentVersionTargets. Returns null when the user
|
|
254
|
+
* declines the install prompt — callers should treat that as a clean cancel.
|
|
255
|
+
*/
|
|
256
|
+
export async function resolveAgentTargetsAutoInstalling(value, availableAgents, options = {}) {
|
|
257
|
+
const ok = await ensureAgentVersionsInstalled(value, availableAgents, options);
|
|
258
|
+
if (!ok)
|
|
259
|
+
return null;
|
|
260
|
+
return resolveAgentVersionTargets(value, availableAgents, { allVersions: options.allVersions });
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Same as resolveAgentTargetsAutoInstalling but returns the broader
|
|
264
|
+
* InstalledAgentTargetResult that includes `directAgents` (for paths like
|
|
265
|
+
* `agents install` and `mcp register` that fall through to unmanaged homes
|
|
266
|
+
* when no managed version is installed).
|
|
267
|
+
*/
|
|
268
|
+
export async function resolveInstalledAgentTargetsAutoInstalling(value, availableAgents, options = {}) {
|
|
269
|
+
const ok = await ensureAgentVersionsInstalled(value, availableAgents, options);
|
|
270
|
+
if (!ok)
|
|
271
|
+
return null;
|
|
272
|
+
return resolveInstalledAgentTargets(value, availableAgents, { allVersions: options.allVersions });
|
|
273
|
+
}
|
|
274
|
+
// Re-export so callers can `catch (err) { if (err instanceof VersionNotInstalledError) … }`
|
|
275
|
+
// without reaching into ../lib/versions directly.
|
|
276
|
+
export { VersionNotInstalledError } from '../lib/versions.js';
|
|
@@ -7,7 +7,7 @@ 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, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, printTrashFooter, } from '../lib/versions.js';
|
|
10
|
+
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, 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';
|
|
@@ -255,7 +255,7 @@ export function registerVersionsCommands(program) {
|
|
|
255
255
|
}
|
|
256
256
|
const { agent, version } = parsed;
|
|
257
257
|
const agentConfig = AGENTS[agent];
|
|
258
|
-
if (!agentConfig.npmPackage) {
|
|
258
|
+
if (!agentConfig.npmPackage && !agentConfig.installScript) {
|
|
259
259
|
console.log(chalk.yellow(`${agentLabel(agentConfig.id)} has no npm package. Install manually.`));
|
|
260
260
|
continue;
|
|
261
261
|
}
|
|
@@ -293,7 +293,7 @@ export function registerVersionsCommands(program) {
|
|
|
293
293
|
// Smart resource detection: compare available vs ACTUALLY synced (source of truth: files)
|
|
294
294
|
const available = getAvailableResources();
|
|
295
295
|
const actuallySynced = getActuallySyncedResources(agent, installedVersion);
|
|
296
|
-
const newResources = getNewResources(available, actuallySynced);
|
|
296
|
+
const newResources = getNewResources(available, actuallySynced, getProjectOnlyResources());
|
|
297
297
|
const hasAnySynced = actuallySynced.commands.length > 0 ||
|
|
298
298
|
actuallySynced.skills.length > 0 ||
|
|
299
299
|
actuallySynced.hooks.length > 0 ||
|
|
@@ -577,7 +577,7 @@ export function registerVersionsCommands(program) {
|
|
|
577
577
|
// Smart resource detection: compare available vs ACTUALLY synced (source of truth: files, not tracking)
|
|
578
578
|
const available = getAvailableResources();
|
|
579
579
|
const actuallySynced = getActuallySyncedResources(agentId, finalVersion);
|
|
580
|
-
const newResources = getNewResources(available, actuallySynced);
|
|
580
|
+
const newResources = getNewResources(available, actuallySynced, getProjectOnlyResources());
|
|
581
581
|
// Check if anything is actually synced (source of truth: actual files)
|
|
582
582
|
const hasAnySynced = actuallySynced.commands.length > 0 ||
|
|
583
583
|
actuallySynced.skills.length > 0 ||
|
package/dist/commands/view.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Command } from 'commander';
|
|
10
10
|
import type { AgentId } from '../lib/types.js';
|
|
11
|
+
import { type ProfileSummary } from '../lib/profiles.js';
|
|
11
12
|
/** Machine-readable entry for a single installed version. */
|
|
12
13
|
export interface ViewJsonVersion {
|
|
13
14
|
version: string;
|
|
@@ -16,6 +17,10 @@ export interface ViewJsonVersion {
|
|
|
16
17
|
email: string | null;
|
|
17
18
|
plan: string | null;
|
|
18
19
|
usageStatus: 'available' | 'rate_limited' | 'out_of_credits' | null;
|
|
20
|
+
overageCredits?: {
|
|
21
|
+
amount: number;
|
|
22
|
+
currency: string;
|
|
23
|
+
} | null;
|
|
19
24
|
windows: Array<{
|
|
20
25
|
key: 'session' | 'week' | 'sonnet_week';
|
|
21
26
|
usedPercent: number;
|
|
@@ -28,6 +33,7 @@ export interface ViewJsonVersion {
|
|
|
28
33
|
export interface ViewJsonAgent {
|
|
29
34
|
agent: AgentId;
|
|
30
35
|
versions: ViewJsonVersion[];
|
|
36
|
+
profiles: ProfileSummary[];
|
|
31
37
|
}
|
|
32
38
|
/**
|
|
33
39
|
* Prune older installed versions that share an email with a newer installed
|
package/dist/commands/view.js
CHANGED
|
@@ -3,9 +3,9 @@ import ora from 'ora';
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
|
|
6
|
-
import { formatUsageSection, formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
6
|
+
import { formatUsageSection, formatUsageSummary, formatUsageStatusBadge, 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, printTrashFooter, } from '../lib/versions.js';
|
|
8
|
+
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, 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';
|
|
@@ -16,8 +16,33 @@ import { isGitRepo, getGitSyncStatus } from '../lib/git.js';
|
|
|
16
16
|
import { getCentralRulesFileName } from '../lib/rules/rules.js';
|
|
17
17
|
import { composeRulesFromState } from '../lib/rules/compose.js';
|
|
18
18
|
import { getConfiguredRunStrategy } from '../lib/rotate.js';
|
|
19
|
+
import { listProfiles, profileSummary } from '../lib/profiles.js';
|
|
19
20
|
import { confirm } from '@inquirer/prompts';
|
|
20
21
|
import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
22
|
+
/**
|
|
23
|
+
* Group profile summaries by their host harness, optionally filtered to a
|
|
24
|
+
* single agent. Profile YAMLs that fail validation are silently skipped by
|
|
25
|
+
* `listProfiles` so this never throws on a malformed file.
|
|
26
|
+
*/
|
|
27
|
+
function getProfilesByAgent(filterAgentId) {
|
|
28
|
+
const byAgent = new Map();
|
|
29
|
+
for (const profile of listProfiles()) {
|
|
30
|
+
if (filterAgentId && profile.host.agent !== filterAgentId)
|
|
31
|
+
continue;
|
|
32
|
+
const summary = profileSummary(profile);
|
|
33
|
+
const existing = byAgent.get(profile.host.agent);
|
|
34
|
+
if (existing)
|
|
35
|
+
existing.push(summary);
|
|
36
|
+
else
|
|
37
|
+
byAgent.set(profile.host.agent, [summary]);
|
|
38
|
+
}
|
|
39
|
+
return byAgent;
|
|
40
|
+
}
|
|
41
|
+
/** Build the usage-column equivalent for a profile row: "profile <model>". */
|
|
42
|
+
function profileKindAndModel(model, planWidth) {
|
|
43
|
+
const kind = 'profile'.padEnd(Math.max(planWidth, 'profile'.length));
|
|
44
|
+
return `${kind} ${model}`;
|
|
45
|
+
}
|
|
21
46
|
function termLink(text, filePath) {
|
|
22
47
|
const url = `file://${filePath}`;
|
|
23
48
|
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
@@ -88,6 +113,32 @@ function getProjectVersionFromCwd(agent) {
|
|
|
88
113
|
return null;
|
|
89
114
|
}
|
|
90
115
|
}
|
|
116
|
+
function getProfileSummaries(filterAgentId) {
|
|
117
|
+
return listProfiles()
|
|
118
|
+
.filter((profile) => !filterAgentId || profile.host.agent === filterAgentId)
|
|
119
|
+
.map(profileSummary);
|
|
120
|
+
}
|
|
121
|
+
function renderProfilesSection(profiles) {
|
|
122
|
+
if (profiles.length === 0)
|
|
123
|
+
return;
|
|
124
|
+
const nameWidth = Math.max(4, ...profiles.map((p) => p.name.length));
|
|
125
|
+
const hostWidth = Math.max(4, ...profiles.map((p) => p.host.length));
|
|
126
|
+
const providerWidth = Math.max(8, ...profiles.map((p) => p.provider.length));
|
|
127
|
+
console.log(chalk.bold('Profiles\n'));
|
|
128
|
+
console.log(` ${chalk.gray('NAME'.padEnd(nameWidth))} ` +
|
|
129
|
+
`${chalk.gray('HOST'.padEnd(hostWidth))} ` +
|
|
130
|
+
`${chalk.gray('PROVIDER'.padEnd(providerWidth))} ` +
|
|
131
|
+
chalk.gray('MODEL'));
|
|
132
|
+
for (const profile of profiles) {
|
|
133
|
+
console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
|
|
134
|
+
`${profile.host.padEnd(hostWidth)} ` +
|
|
135
|
+
`${profile.provider.padEnd(providerWidth)} ` +
|
|
136
|
+
chalk.gray(profile.model));
|
|
137
|
+
}
|
|
138
|
+
console.log(chalk.gray('\n Run: agents run <profile> [prompt]'));
|
|
139
|
+
console.log(chalk.gray(' agents profiles view <profile>'));
|
|
140
|
+
console.log();
|
|
141
|
+
}
|
|
91
142
|
/**
|
|
92
143
|
* Show installed versions for one or all agents.
|
|
93
144
|
* Called when: `agents view` or `agents view claude`
|
|
@@ -101,6 +152,8 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
101
152
|
spinner.stop();
|
|
102
153
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
103
154
|
const showPaths = !!filterAgentId;
|
|
155
|
+
const profilesByAgent = getProfilesByAgent(filterAgentId);
|
|
156
|
+
const profileSummaries = [...profilesByAgent.values()].flat();
|
|
104
157
|
// Auto-heal stale versioned aliases. Pre-v2 aliases (e.g. pre-CLAUDE_CONFIG_DIR
|
|
105
158
|
// claude shims) silently route login through the default version's symlinked
|
|
106
159
|
// home, so `agents view` would never reflect the right account. Regenerate on
|
|
@@ -190,15 +243,20 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
190
243
|
// Separate version-managed from globally-installed agents
|
|
191
244
|
const versionManaged = [];
|
|
192
245
|
const globallyInstalled = [];
|
|
246
|
+
const profileOnly = [];
|
|
193
247
|
for (const agentId of agentsToShow) {
|
|
194
248
|
const versions = listInstalledVersions(agentId);
|
|
195
249
|
const cliState = cliStates[agentId];
|
|
250
|
+
const hasProfiles = (profilesByAgent.get(agentId)?.length ?? 0) > 0;
|
|
196
251
|
if (versions.length > 0) {
|
|
197
252
|
versionManaged.push(agentId);
|
|
198
253
|
}
|
|
199
254
|
else if (cliState?.installed) {
|
|
200
255
|
globallyInstalled.push(agentId);
|
|
201
256
|
}
|
|
257
|
+
else if (hasProfiles) {
|
|
258
|
+
profileOnly.push(agentId);
|
|
259
|
+
}
|
|
202
260
|
}
|
|
203
261
|
// Show version-managed agents
|
|
204
262
|
if (versionManaged.length > 0) {
|
|
@@ -207,6 +265,7 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
207
265
|
let maxEmail = 0;
|
|
208
266
|
let maxPlanWidth = 3;
|
|
209
267
|
let maxUsageWidth = 0;
|
|
268
|
+
let maxStatusWidth = 0;
|
|
210
269
|
for (const agentId of versionManaged) {
|
|
211
270
|
const versions = listInstalledVersions(agentId);
|
|
212
271
|
const globalDefault = getGlobalDefault(agentId);
|
|
@@ -220,8 +279,14 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
220
279
|
if (info?.plan)
|
|
221
280
|
maxPlanWidth = Math.max(maxPlanWidth, info.plan.length);
|
|
222
281
|
}
|
|
282
|
+
// Profile rows share these columns with version rows so they line up.
|
|
283
|
+
for (const profile of profilesByAgent.get(agentId) ?? []) {
|
|
284
|
+
maxVerLabel = Math.max(maxVerLabel, profile.name.length);
|
|
285
|
+
maxEmail = Math.max(maxEmail, profile.auth.length);
|
|
286
|
+
maxPlanWidth = Math.max(maxPlanWidth, 'profile'.length);
|
|
287
|
+
}
|
|
223
288
|
}
|
|
224
|
-
// Second pass: compute max visible usage
|
|
289
|
+
// Second pass: compute max visible usage + status widths (now that maxPlanWidth is settled)
|
|
225
290
|
for (const agentId of versionManaged) {
|
|
226
291
|
const versions = listInstalledVersions(agentId);
|
|
227
292
|
for (const v of versions) {
|
|
@@ -231,6 +296,12 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
231
296
|
const usageInfo = usageKey ? usageByKey.get(usageKey) : undefined;
|
|
232
297
|
const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth);
|
|
233
298
|
maxUsageWidth = Math.max(maxUsageWidth, visibleWidth(usageStr));
|
|
299
|
+
const statusStr = formatUsageStatusBadge(info?.usageStatus);
|
|
300
|
+
maxStatusWidth = Math.max(maxStatusWidth, visibleWidth(statusStr));
|
|
301
|
+
}
|
|
302
|
+
for (const profile of profilesByAgent.get(agentId) ?? []) {
|
|
303
|
+
const usageEquivalent = profileKindAndModel(profile.model, maxPlanWidth);
|
|
304
|
+
maxUsageWidth = Math.max(maxUsageWidth, visibleWidth(usageEquivalent));
|
|
234
305
|
}
|
|
235
306
|
}
|
|
236
307
|
for (const agentId of versionManaged) {
|
|
@@ -280,6 +351,11 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
280
351
|
const usagePad = ' '.repeat(Math.max(0, maxUsageWidth - visibleWidth(usageStr)));
|
|
281
352
|
parts.push(usageStr + usagePad);
|
|
282
353
|
}
|
|
354
|
+
const statusStr = formatUsageStatusBadge(vInfo?.usageStatus);
|
|
355
|
+
if (maxStatusWidth > 0) {
|
|
356
|
+
const statusPad = ' '.repeat(Math.max(0, maxStatusWidth - visibleWidth(statusStr)));
|
|
357
|
+
parts.push(statusStr + statusPad);
|
|
358
|
+
}
|
|
283
359
|
if (hasActive)
|
|
284
360
|
parts.push(activeStr);
|
|
285
361
|
}
|
|
@@ -289,6 +365,18 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
289
365
|
console.log(chalk.gray(` ${versionDir}`));
|
|
290
366
|
}
|
|
291
367
|
}
|
|
368
|
+
// Profile rows share the same columns as versions: name | auth | "profile"+model.
|
|
369
|
+
// No status badge, no last-active — profiles don't accumulate usage state.
|
|
370
|
+
for (const profile of profilesByAgent.get(agentId) ?? []) {
|
|
371
|
+
const nameCol = chalk.cyan(profile.name.padEnd(maxVerLabel));
|
|
372
|
+
const authCol = chalk.gray(profile.auth.padEnd(maxEmail));
|
|
373
|
+
const usageEquivalent = profileKindAndModel(profile.model, maxPlanWidth);
|
|
374
|
+
const usagePad = ' '.repeat(Math.max(0, maxUsageWidth - visibleWidth(usageEquivalent)));
|
|
375
|
+
console.log(` ${nameCol} ${authCol} ${chalk.gray(usageEquivalent + usagePad)}`);
|
|
376
|
+
if (showPaths) {
|
|
377
|
+
console.log(chalk.gray(` ${profile.path}`));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
292
380
|
// Check for project override
|
|
293
381
|
const projectVersion = getProjectVersionFromCwd(agentId);
|
|
294
382
|
if (projectVersion && projectVersion !== globalDefault) {
|
|
@@ -305,6 +393,19 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
305
393
|
const cliState = cliStates[agentId];
|
|
306
394
|
return `${cliState?.version || 'installed'} (global)`.length;
|
|
307
395
|
}));
|
|
396
|
+
// Pre-pass: max badge width so rows with `lastActive` line up whether or
|
|
397
|
+
// not THIS row carries a throttle badge. Without this, the row that DOES
|
|
398
|
+
// have "out of credits" shifts every other row's `lastActive` left by
|
|
399
|
+
// ~16 chars, exactly what the version-managed block at maxStatusWidth
|
|
400
|
+
// already solves above.
|
|
401
|
+
let gMaxStatusWidth = 0;
|
|
402
|
+
for (const agentId of globallyInstalled) {
|
|
403
|
+
const gInfoRaw = globalInfoMap.get(agentId);
|
|
404
|
+
const gInfo = gInfoRaw ? mergeCanonical(gInfoRaw) : undefined;
|
|
405
|
+
const w = visibleWidth(formatUsageStatusBadge(gInfo?.usageStatus));
|
|
406
|
+
if (w > gMaxStatusWidth)
|
|
407
|
+
gMaxStatusWidth = w;
|
|
408
|
+
}
|
|
308
409
|
for (const agentId of globallyInstalled) {
|
|
309
410
|
const agent = AGENTS[agentId];
|
|
310
411
|
const cliState = cliStates[agentId];
|
|
@@ -323,25 +424,77 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
323
424
|
parts.push(gInfo?.email ? chalk.cyan(gInfo.email) : '');
|
|
324
425
|
if (gUsageStr || gActiveStr)
|
|
325
426
|
parts.push(gUsageStr);
|
|
427
|
+
const gStatusStr = formatUsageStatusBadge(gInfo?.usageStatus);
|
|
428
|
+
if (gMaxStatusWidth > 0) {
|
|
429
|
+
const statusPad = ' '.repeat(Math.max(0, gMaxStatusWidth - visibleWidth(gStatusStr)));
|
|
430
|
+
parts.push(gStatusStr + statusPad);
|
|
431
|
+
}
|
|
326
432
|
if (gActiveStr)
|
|
327
433
|
parts.push(gActiveStr);
|
|
328
434
|
console.log(parts.join(' '));
|
|
329
435
|
if (showPaths && cliState?.path) {
|
|
330
436
|
console.log(chalk.gray(` ${cliState.path}`));
|
|
331
437
|
}
|
|
438
|
+
// Profile rows under a globally-installed harness. Use a simpler
|
|
439
|
+
// alignment here since this section doesn't share column state with
|
|
440
|
+
// the version-managed block.
|
|
441
|
+
const profilesHere = profilesByAgent.get(agentId) ?? [];
|
|
442
|
+
if (profilesHere.length > 0) {
|
|
443
|
+
const nameWidth = Math.max(globalMaxVerLabel, ...profilesHere.map((p) => p.name.length));
|
|
444
|
+
const authWidth = Math.max(...profilesHere.map((p) => p.auth.length));
|
|
445
|
+
for (const profile of profilesHere) {
|
|
446
|
+
console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
|
|
447
|
+
`${chalk.gray(profile.auth.padEnd(authWidth))} ` +
|
|
448
|
+
`${chalk.gray('profile')} ` +
|
|
449
|
+
chalk.gray(profile.model));
|
|
450
|
+
if (showPaths) {
|
|
451
|
+
console.log(chalk.gray(` ${profile.path}`));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
332
455
|
if (agent.npmPackage && cliState?.version) {
|
|
333
456
|
console.log(chalk.gray(` Manage: agents add ${agentId}@${cliState.version} -y`));
|
|
334
457
|
}
|
|
335
458
|
console.log();
|
|
336
459
|
}
|
|
337
460
|
}
|
|
461
|
+
// Agents with no install but with profiles defined — render under the same
|
|
462
|
+
// harness header so users find them where they look.
|
|
463
|
+
if (profileOnly.length > 0) {
|
|
464
|
+
if (versionManaged.length === 0 && globallyInstalled.length === 0) {
|
|
465
|
+
console.log(chalk.bold('Profile-only Agents\n'));
|
|
466
|
+
}
|
|
467
|
+
for (const agentId of profileOnly) {
|
|
468
|
+
const profilesHere = profilesByAgent.get(agentId) ?? [];
|
|
469
|
+
console.log(` ${chalk.bold(agentLabel(agentId))}${chalk.yellow(' (profile only)')}`);
|
|
470
|
+
const nameWidth = Math.max(...profilesHere.map((p) => p.name.length));
|
|
471
|
+
const authWidth = Math.max(...profilesHere.map((p) => p.auth.length));
|
|
472
|
+
for (const profile of profilesHere) {
|
|
473
|
+
console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
|
|
474
|
+
`${chalk.gray(profile.auth.padEnd(authWidth))} ` +
|
|
475
|
+
`${chalk.gray('profile')} ` +
|
|
476
|
+
chalk.gray(profile.model));
|
|
477
|
+
if (showPaths) {
|
|
478
|
+
console.log(chalk.gray(` ${profile.path}`));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
console.log();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
338
484
|
// If filtering to a specific agent and not found
|
|
339
|
-
if (filterAgentId &&
|
|
485
|
+
if (filterAgentId &&
|
|
486
|
+
versionManaged.length === 0 &&
|
|
487
|
+
globallyInstalled.length === 0 &&
|
|
488
|
+
profileOnly.length === 0) {
|
|
340
489
|
console.log(` ${chalk.bold(agentLabel(filterAgentId))}: ${chalk.gray('not installed')}`);
|
|
341
490
|
console.log();
|
|
342
491
|
}
|
|
343
492
|
// No agents installed at all
|
|
344
|
-
if (versionManaged.length === 0 &&
|
|
493
|
+
if (versionManaged.length === 0 &&
|
|
494
|
+
globallyInstalled.length === 0 &&
|
|
495
|
+
profileOnly.length === 0 &&
|
|
496
|
+
profileSummaries.length === 0 &&
|
|
497
|
+
!filterAgentId) {
|
|
345
498
|
console.log(chalk.gray(' No agent CLIs installed.'));
|
|
346
499
|
console.log(chalk.gray(' Run: agents add claude@latest'));
|
|
347
500
|
console.log();
|
|
@@ -363,7 +516,8 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
363
516
|
if (defaultVersion) {
|
|
364
517
|
const available = getAvailableResources();
|
|
365
518
|
const synced = getActuallySyncedResources(filterAgentId, defaultVersion);
|
|
366
|
-
const
|
|
519
|
+
const projectOnly = getProjectOnlyResources();
|
|
520
|
+
const newResources = getNewResources(available, synced, projectOnly);
|
|
367
521
|
if (hasNewResources(newResources, filterAgentId, defaultVersion)) {
|
|
368
522
|
try {
|
|
369
523
|
const selection = await promptNewResourceSelection(filterAgentId, newResources, defaultVersion);
|
|
@@ -742,6 +896,7 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
742
896
|
email: info.email,
|
|
743
897
|
plan: info.plan,
|
|
744
898
|
usageStatus: info.usageStatus,
|
|
899
|
+
overageCredits: info.overageCredits,
|
|
745
900
|
windows: snapshot
|
|
746
901
|
? snapshot.windows.map((w) => ({
|
|
747
902
|
key: w.key,
|
|
@@ -758,6 +913,7 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
758
913
|
else
|
|
759
914
|
byAgent.set(agentId, [entry]);
|
|
760
915
|
}
|
|
916
|
+
const profilesByAgent = getProfilesByAgent(filterAgentId);
|
|
761
917
|
const out = [];
|
|
762
918
|
for (const agentId of agentsToShow) {
|
|
763
919
|
const versions = byAgent.get(agentId) ?? [];
|
|
@@ -766,7 +922,7 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
766
922
|
return a.isDefault ? -1 : 1;
|
|
767
923
|
return compareVersions(b.version, a.version);
|
|
768
924
|
});
|
|
769
|
-
out.push({ agent: agentId, versions });
|
|
925
|
+
out.push({ agent: agentId, versions, profiles: profilesByAgent.get(agentId) ?? [] });
|
|
770
926
|
}
|
|
771
927
|
return out;
|
|
772
928
|
}
|
|
@@ -1009,7 +1165,7 @@ export async function viewAction(agentArg, options) {
|
|
|
1009
1165
|
// --json ignores the @version suffix (detailed resource view is not yet
|
|
1010
1166
|
// exposed as structured data). Emit the version list for the agent.
|
|
1011
1167
|
const data = await collectAgentsJson(agentId);
|
|
1012
|
-
console.log(JSON.stringify(data[0] ?? { agent: agentId, versions: [] }, null, 2));
|
|
1168
|
+
console.log(JSON.stringify(data[0] ?? { agent: agentId, versions: [], profiles: [] }, null, 2));
|
|
1013
1169
|
return;
|
|
1014
1170
|
}
|
|
1015
1171
|
if (requestedVersion) {
|
|
@@ -7,9 +7,9 @@ import { select, checkbox } from '@inquirer/prompts';
|
|
|
7
7
|
import { resolveAgentName, agentLabel } from '../lib/agents.js';
|
|
8
8
|
import { cloneRepo } from '../lib/git.js';
|
|
9
9
|
import { WORKFLOW_CAPABLE_AGENTS, discoverWorkflowsFromRepo, installWorkflowCentrally, removeWorkflow, listInstalledWorkflows, listWorkflowsForAgent, removeWorkflowFromVersion, iterWorkflowsCapableVersions, } from '../lib/workflows.js';
|
|
10
|
-
import { getVersionHomePath, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection,
|
|
10
|
+
import { getVersionHomePath, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, } from '../lib/versions.js';
|
|
11
11
|
import { recordVersionResources, getUserWorkflowsDir } from '../lib/state.js';
|
|
12
|
-
import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, printWithPager, promptRemovalTargets, } from './utils.js';
|
|
12
|
+
import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, printWithPager, promptRemovalTargets, parseCommaSeparatedList, resolveAgentTargetsAutoInstalling, } from './utils.js';
|
|
13
13
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
14
14
|
/** Register the `agents workflows` command tree (list, view, add, remove). */
|
|
15
15
|
export function registerWorkflowsCommands(program) {
|
|
@@ -92,18 +92,25 @@ Examples:
|
|
|
92
92
|
workflowsCmd
|
|
93
93
|
.command('add [source]')
|
|
94
94
|
.description('Install workflows from a source (GitHub, local) or pick from central storage')
|
|
95
|
-
.option('-a, --agents <list>', 'Targets: claude, claude@2.1.138')
|
|
95
|
+
.option('-a, --agents <list>', 'Targets: claude, claude@2.1.138, claude@all, all')
|
|
96
|
+
.option('--names <list>', 'Workflow names from the source (comma-separated)')
|
|
96
97
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
97
98
|
.addHelpText('after', `
|
|
98
99
|
Examples:
|
|
99
100
|
# Install from GitHub
|
|
100
101
|
agents workflows add gh:user/workflows
|
|
101
102
|
|
|
103
|
+
# Pluck specific workflows from a multi-workflow repo
|
|
104
|
+
agents workflows add gh:user/workflows --names rdev,plan
|
|
105
|
+
|
|
102
106
|
# Install a local workflow directory (must contain WORKFLOW.md)
|
|
103
107
|
agents workflows add ./rdev
|
|
104
108
|
|
|
105
109
|
# Install and sync to a specific version
|
|
106
110
|
agents workflows add gh:user/workflows --agents claude@2.1.138
|
|
111
|
+
|
|
112
|
+
# Install across every installed Claude version
|
|
113
|
+
agents workflows add gh:user/workflows --agents claude@all
|
|
107
114
|
`)
|
|
108
115
|
.action(async (source, options) => {
|
|
109
116
|
try {
|
|
@@ -156,11 +163,23 @@ Examples:
|
|
|
156
163
|
}
|
|
157
164
|
spinner.succeed('Using local path');
|
|
158
165
|
}
|
|
159
|
-
|
|
166
|
+
let discovered = discoverWorkflowsFromRepo(localPath);
|
|
160
167
|
if (discovered.length === 0) {
|
|
161
168
|
console.log(chalk.yellow('No workflows found (looking for WORKFLOW.md files)'));
|
|
162
169
|
return;
|
|
163
170
|
}
|
|
171
|
+
// --names filter: pluck specific workflows from a multi-workflow source.
|
|
172
|
+
const requestedNames = parseCommaSeparatedList(options.names);
|
|
173
|
+
if (requestedNames.length > 0) {
|
|
174
|
+
const discoveredNames = new Set(discovered.map((w) => w.name));
|
|
175
|
+
const missing = requestedNames.filter((n) => !discoveredNames.has(n));
|
|
176
|
+
if (missing.length > 0) {
|
|
177
|
+
console.log(chalk.red(`\nWorkflow(s) not found in source: ${missing.join(', ')}`));
|
|
178
|
+
console.log(chalk.gray(`Available: ${[...discoveredNames].join(', ')}`));
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
discovered = discovered.filter((w) => requestedNames.includes(w.name));
|
|
182
|
+
}
|
|
164
183
|
console.log(chalk.bold(`\nFound ${discovered.length} workflow(s):`));
|
|
165
184
|
for (const w of discovered) {
|
|
166
185
|
console.log(`\n ${chalk.cyan(w.name)}: ${w.frontmatter.description || 'no description'}`);
|
|
@@ -191,13 +210,17 @@ Examples:
|
|
|
191
210
|
let selectedAgents;
|
|
192
211
|
let versionSelections;
|
|
193
212
|
if (options.agents) {
|
|
194
|
-
const result =
|
|
213
|
+
const result = await resolveAgentTargetsAutoInstalling(options.agents, WORKFLOW_CAPABLE_AGENTS, { yes: options.yes });
|
|
214
|
+
if (!result) {
|
|
215
|
+
console.log(chalk.gray('Cancelled.'));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
195
218
|
selectedAgents = result.selectedAgents;
|
|
196
219
|
versionSelections = result.versionSelections;
|
|
197
220
|
}
|
|
198
221
|
else {
|
|
199
222
|
const result = await promptAgentVersionSelection(WORKFLOW_CAPABLE_AGENTS, {
|
|
200
|
-
skipPrompts: options.yes
|
|
223
|
+
skipPrompts: options.yes,
|
|
201
224
|
});
|
|
202
225
|
selectedAgents = result.selectedAgents;
|
|
203
226
|
versionSelections = result.versionSelections;
|