@phnx-labs/agents-cli 1.20.0 → 1.20.4
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 +81 -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/import.js +90 -37
- 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 +169 -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 +41 -17
- 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/import.d.ts +21 -0
- package/dist/lib/import.js +55 -2
- 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/plugin-marketplace.d.ts +10 -0
- package/dist/lib/plugin-marketplace.js +47 -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/pty-server.js +27 -3
- 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 +288 -64
- package/package.json +13 -12
- 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/secrets.js
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import { bundleExists, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, migrateLegacyBundles, parseDotenv, readBundle, renameBundle, rotateBundleSecret, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, } from '../lib/secrets/bundles.js';
|
|
11
|
-
import { deleteKeychainToken,
|
|
11
|
+
import { deleteKeychainToken, getKeychainTokens, hasKeychainToken, secretsKeychainItem, setKeychainToken, } from '../lib/secrets/index.js';
|
|
12
12
|
import { assertOpAvailable, createPasswordItem, deleteItemByTitle, extractSecrets, itemExistsByTitle, listItems, listVaults, } from '../lib/onepassword.js';
|
|
13
13
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
14
14
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
15
|
+
import { registerSecretsSyncCommands } from './secrets-sync.js';
|
|
16
|
+
import { registerSecretsMigrateAclCommand } from './secrets-migrate.js';
|
|
15
17
|
/** Prompt the user for a secret value with masked input. Requires an interactive TTY. */
|
|
16
18
|
async function promptForSecret(message) {
|
|
17
19
|
if (!isInteractiveTerminal()) {
|
|
@@ -178,7 +180,6 @@ function humanAge(iso) {
|
|
|
178
180
|
function renderBundleRow(b) {
|
|
179
181
|
const entries = describeBundle(b);
|
|
180
182
|
const keys = entries.length;
|
|
181
|
-
const sync = b.icloud_sync ? chalk.cyan('icloud') : '';
|
|
182
183
|
const expiringCount = countExpiringSoon(b.meta);
|
|
183
184
|
const expiring = expiringCount > 0 ? chalk.yellow(String(expiringCount)) : chalk.gray('-');
|
|
184
185
|
// Timestamp distinction:
|
|
@@ -194,12 +195,11 @@ function renderBundleRow(b) {
|
|
|
194
195
|
: (b.created_at ? chalk.gray('never') : chalk.gray('?'));
|
|
195
196
|
const head = `${chalk.cyan(b.name.padEnd(20))} ` +
|
|
196
197
|
`${String(keys).padEnd(5)} ` +
|
|
197
|
-
`${padVisible(sync, 6)} ` +
|
|
198
198
|
`${padVisible(expiring, 9)} ` +
|
|
199
199
|
`${padVisible(created, 9)} ` +
|
|
200
200
|
`${padVisible(updated, 9)} ` +
|
|
201
201
|
`${padVisible(used, 7)}`;
|
|
202
|
-
return b.description ? `${head} ${chalk.gray(b.description)}` : head.trimEnd();
|
|
202
|
+
return b.description ? `${head} ${chalk.gray(safePrint(b.description))}` : head.trimEnd();
|
|
203
203
|
}
|
|
204
204
|
/** Colorize a variable source kind (literal, keychain, env, file, exec). */
|
|
205
205
|
function kindLabel(kind) {
|
|
@@ -220,6 +220,17 @@ function redact(value, reveal) {
|
|
|
220
220
|
return '';
|
|
221
221
|
return '*'.repeat(Math.min(value.length, 8));
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Strip ASCII / C1 control bytes from a string before printing it to the
|
|
225
|
+
* terminal. Bundle descriptions, notes, and remote-supplied names can carry
|
|
226
|
+
* arbitrary text and a malicious value containing ANSI escape sequences (e.g.
|
|
227
|
+
* OSC 52 clipboard set, screen-clear, cursor moves) would otherwise be
|
|
228
|
+
* interpreted by the user's terminal. Allow tab and newline so multi-line
|
|
229
|
+
* notes still render; strip everything else in the C0/C1 ranges plus DEL.
|
|
230
|
+
*/
|
|
231
|
+
function safePrint(s) {
|
|
232
|
+
return s.replace(/[\x00-\x08\x0B-\x1F\x7F-\x9F]/g, '');
|
|
233
|
+
}
|
|
223
234
|
/**
|
|
224
235
|
* Build a VarMeta patch from CLI flags. Validates each provided field. Returns
|
|
225
236
|
* undefined if no meta flag was passed (so callers know to skip meta updates).
|
|
@@ -286,7 +297,7 @@ function renderMetaLine(meta, reveal) {
|
|
|
286
297
|
parts.push(colored);
|
|
287
298
|
}
|
|
288
299
|
if (meta.note) {
|
|
289
|
-
let note = meta.note;
|
|
300
|
+
let note = safePrint(meta.note);
|
|
290
301
|
if (!reveal && note.length > 80) {
|
|
291
302
|
note = note.slice(0, 79) + '\u2026';
|
|
292
303
|
}
|
|
@@ -314,7 +325,7 @@ function countExpiringSoon(meta) {
|
|
|
314
325
|
export function registerSecretsCommands(program) {
|
|
315
326
|
const cmd = program
|
|
316
327
|
.command('secrets')
|
|
317
|
-
.description('Named bundles of env variables backed by macOS Keychain (
|
|
328
|
+
.description('Named bundles of env variables backed by macOS Keychain (device-local, biometry-gated). Inject into agents via `agents run --secrets <name>`.');
|
|
318
329
|
setHelpSections(cmd, {
|
|
319
330
|
examples: `
|
|
320
331
|
# Create a bundle
|
|
@@ -340,23 +351,22 @@ export function registerSecretsCommands(program) {
|
|
|
340
351
|
`,
|
|
341
352
|
notes: `
|
|
342
353
|
Bundles are containers; secrets are the variables inside them. Keychain values
|
|
343
|
-
never touch disk in plaintext.
|
|
344
|
-
|
|
345
|
-
iCloud sync: new bundles use the iCloud-synced keychain by default so they
|
|
346
|
-
appear on other Macs (same iCloud account, iCloud Keychain enabled). Pass
|
|
347
|
-
--no-icloud-sync at create time to keep values device-local instead.
|
|
354
|
+
never touch disk in plaintext. Every item is device-local and gated by Touch ID
|
|
355
|
+
or device passcode; cross-machine sync is handled by 'agents secrets push/pull'.
|
|
348
356
|
|
|
349
357
|
See also:
|
|
350
358
|
agents secrets rotate <bundle> <key> rotate value, preserve metadata
|
|
351
359
|
agents secrets import <bundle> --from .env bulk import from .env
|
|
352
360
|
agents secrets import <bundle> --from-1password --vault <name>
|
|
353
361
|
agents secrets generate [length] generate a random password / PIN / hex
|
|
362
|
+
agents secrets migrate-acl upgrade legacy items to the biometry ACL
|
|
354
363
|
`,
|
|
355
364
|
});
|
|
356
365
|
registerCommandGroups(cmd, [
|
|
357
366
|
{ title: 'Bundle commands', names: ['list', 'view', 'create', 'rename', 'describe', 'delete'] },
|
|
358
367
|
{ title: 'Secret commands', names: ['add', 'rotate', 'remove', 'import', 'export'] },
|
|
359
|
-
{ title: '
|
|
368
|
+
{ title: 'Sync commands', names: ['push', 'pull', 'remote-list'] },
|
|
369
|
+
{ title: 'Utilities', names: ['exec', 'generate', 'migrate-acl'] },
|
|
360
370
|
]);
|
|
361
371
|
cmd
|
|
362
372
|
.command('list')
|
|
@@ -369,7 +379,7 @@ export function registerSecretsCommands(program) {
|
|
|
369
379
|
console.log(chalk.gray('Try: agents secrets create <name>'));
|
|
370
380
|
return;
|
|
371
381
|
}
|
|
372
|
-
console.log(chalk.bold(`${'NAME'.padEnd(20)} ${'KEYS'.padEnd(5)} ${'
|
|
382
|
+
console.log(chalk.bold(`${'NAME'.padEnd(20)} ${'KEYS'.padEnd(5)} ${'EXPIRING'.padEnd(9)} ${'CREATED'.padEnd(9)} ${'UPDATED'.padEnd(9)} ${'USED'.padEnd(7)} DESCRIPTION`));
|
|
373
383
|
for (const b of bundles) {
|
|
374
384
|
console.log(renderBundleRow(b));
|
|
375
385
|
}
|
|
@@ -387,11 +397,9 @@ export function registerSecretsCommands(program) {
|
|
|
387
397
|
const entries = describeBundle(bundle);
|
|
388
398
|
console.log(chalk.bold(bundle.name));
|
|
389
399
|
if (bundle.description)
|
|
390
|
-
console.log(chalk.gray(bundle.description));
|
|
400
|
+
console.log(chalk.gray(safePrint(bundle.description)));
|
|
391
401
|
if (bundle.allow_exec)
|
|
392
402
|
console.log(chalk.yellow('allow_exec: true'));
|
|
393
|
-
if (bundle.icloud_sync)
|
|
394
|
-
console.log(chalk.cyan('icloud_sync: true'));
|
|
395
403
|
if (bundle.created_at)
|
|
396
404
|
console.log(chalk.gray(`created_at: ${bundle.created_at} (${humanAge(bundle.created_at)})`));
|
|
397
405
|
if (bundle.updated_at)
|
|
@@ -408,19 +416,30 @@ export function registerSecretsCommands(program) {
|
|
|
408
416
|
console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
|
|
409
417
|
process.exit(1);
|
|
410
418
|
}
|
|
419
|
+
// Batch every keychain read into one helper call so --reveal pops
|
|
420
|
+
// Touch ID once for the whole bundle instead of once per key.
|
|
421
|
+
const revealedValues = new Map();
|
|
422
|
+
if (reveal) {
|
|
423
|
+
const items = entries
|
|
424
|
+
.filter((e) => e.kind === 'keychain')
|
|
425
|
+
.map((e) => secretsKeychainItem(bundle.name, e.detail));
|
|
426
|
+
try {
|
|
427
|
+
const fetched = getKeychainTokens(items);
|
|
428
|
+
for (const [item, value] of fetched)
|
|
429
|
+
revealedValues.set(item, value);
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
// Fall through to masked output on cancellation / batch failure.
|
|
433
|
+
}
|
|
434
|
+
}
|
|
411
435
|
for (const e of entries) {
|
|
412
436
|
if (e.kind === 'keychain') {
|
|
413
437
|
const item = secretsKeychainItem(bundle.name, e.detail);
|
|
414
|
-
const stored = hasKeychainToken(item
|
|
438
|
+
const stored = hasKeychainToken(item);
|
|
415
439
|
const marker = stored ? chalk.green('stored') : chalk.red('missing');
|
|
416
440
|
let valueCol = `[keychain:${e.detail}] ${marker}`;
|
|
417
|
-
if (reveal &&
|
|
418
|
-
|
|
419
|
-
valueCol = redact(getKeychainToken(item, bundle.icloud_sync), true);
|
|
420
|
-
}
|
|
421
|
-
catch {
|
|
422
|
-
// fall through to masked
|
|
423
|
-
}
|
|
441
|
+
if (reveal && revealedValues.has(item)) {
|
|
442
|
+
valueCol = redact(revealedValues.get(item), true);
|
|
424
443
|
}
|
|
425
444
|
console.log(` ${chalk.cyan(e.key.padEnd(28))} ${kindLabel(e.kind).padEnd(18)} ${valueCol}`);
|
|
426
445
|
}
|
|
@@ -429,7 +448,7 @@ export function registerSecretsCommands(program) {
|
|
|
429
448
|
const literalValue = typeof raw === 'string'
|
|
430
449
|
? raw
|
|
431
450
|
: (raw && typeof raw === 'object' && 'value' in raw ? raw.value : '');
|
|
432
|
-
console.log(` ${chalk.cyan(e.key.padEnd(28))} ${kindLabel(e.kind).padEnd(18)} ${literalValue}`);
|
|
451
|
+
console.log(` ${chalk.cyan(e.key.padEnd(28))} ${kindLabel(e.kind).padEnd(18)} ${redact(literalValue, reveal)}`);
|
|
433
452
|
}
|
|
434
453
|
else {
|
|
435
454
|
console.log(` ${chalk.cyan(e.key.padEnd(28))} ${kindLabel(e.kind).padEnd(18)} ${e.detail}`);
|
|
@@ -451,7 +470,6 @@ export function registerSecretsCommands(program) {
|
|
|
451
470
|
.description('Create an empty bundle')
|
|
452
471
|
.option('--description <text>', 'Free-form description')
|
|
453
472
|
.option('--allow-exec', 'Allow exec: refs in this bundle (off by default)')
|
|
454
|
-
.option('--no-icloud-sync', 'Store keychain values device-local instead of syncing them via iCloud Keychain')
|
|
455
473
|
.option('--force', 'Overwrite an existing bundle')
|
|
456
474
|
.action(async (name, opts) => {
|
|
457
475
|
try {
|
|
@@ -465,7 +483,6 @@ export function registerSecretsCommands(program) {
|
|
|
465
483
|
name: resolvedName,
|
|
466
484
|
description: opts.description,
|
|
467
485
|
allow_exec: opts.allowExec,
|
|
468
|
-
icloud_sync: opts.icloudSync !== false,
|
|
469
486
|
vars: {},
|
|
470
487
|
};
|
|
471
488
|
writeBundle(bundle);
|
|
@@ -600,12 +617,11 @@ export function registerSecretsCommands(program) {
|
|
|
600
617
|
secretValue = await promptForSecret(`Enter value for ${resolvedBundleName}.${resolvedKey}`);
|
|
601
618
|
}
|
|
602
619
|
const item = secretsKeychainItem(resolvedBundleName, resolvedKey);
|
|
603
|
-
setKeychainToken(item, secretValue
|
|
620
|
+
setKeychainToken(item, secretValue);
|
|
604
621
|
bundle.vars[resolvedKey] = keychainRef(resolvedKey);
|
|
605
622
|
applyMeta();
|
|
606
623
|
writeBundle(bundle);
|
|
607
|
-
|
|
608
|
-
console.log(chalk.green(`${resolvedBundleName}.${resolvedKey} stored in ${where} (${item}).`));
|
|
624
|
+
console.log(chalk.green(`${resolvedBundleName}.${resolvedKey} stored in keychain (${item}).`));
|
|
609
625
|
}
|
|
610
626
|
catch (err) {
|
|
611
627
|
if (isPromptCancelled(err))
|
|
@@ -665,8 +681,7 @@ Examples:
|
|
|
665
681
|
clearMeta: opts.clearMeta,
|
|
666
682
|
meta: metaPatch,
|
|
667
683
|
});
|
|
668
|
-
|
|
669
|
-
console.log(chalk.green(`${resolvedBundleName}.${resolvedKey} rotated in ${where}.`));
|
|
684
|
+
console.log(chalk.green(`${resolvedBundleName}.${resolvedKey} rotated in keychain.`));
|
|
670
685
|
}
|
|
671
686
|
catch (err) {
|
|
672
687
|
if (isPromptCancelled(err))
|
|
@@ -679,6 +694,7 @@ Examples:
|
|
|
679
694
|
.command('remove [bundle] [key]')
|
|
680
695
|
.description('Remove a key from the bundle. Purges the keychain item if the ref was keychain:. Use --keep-secret to retain it.')
|
|
681
696
|
.option('--keep-secret', 'Leave the keychain item in place after removing the ref from the bundle')
|
|
697
|
+
.option('-y, --yes', 'Skip the confirmation prompt when purging a keychain item')
|
|
682
698
|
.action(async (bundleName, key, opts) => {
|
|
683
699
|
try {
|
|
684
700
|
const resolvedBundleName = bundleName ?? (await pickBundleName('remove from'));
|
|
@@ -689,11 +705,28 @@ Examples:
|
|
|
689
705
|
process.exit(1);
|
|
690
706
|
}
|
|
691
707
|
const raw = bundle.vars[resolvedKey];
|
|
708
|
+
const willPurge = !opts.keepSecret && typeof raw === 'string' && raw.startsWith('keychain:');
|
|
709
|
+
if (willPurge && !opts.yes) {
|
|
710
|
+
if (!isInteractiveTerminal()) {
|
|
711
|
+
console.error(chalk.red(`Refusing to purge keychain item for ${resolvedBundleName}.${resolvedKey} non-interactively. ` +
|
|
712
|
+
`Pass --yes to confirm or --keep-secret to retain the keychain entry.`));
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
716
|
+
const ok = await confirm({
|
|
717
|
+
message: `Purge keychain item for ${resolvedBundleName}.${resolvedKey}? (use --keep-secret to retain)`,
|
|
718
|
+
default: false,
|
|
719
|
+
});
|
|
720
|
+
if (!ok) {
|
|
721
|
+
console.log(chalk.gray('Aborted. Bundle metadata unchanged.'));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
692
725
|
delete bundle.vars[resolvedKey];
|
|
693
726
|
writeBundle(bundle);
|
|
694
|
-
if (
|
|
727
|
+
if (willPurge) {
|
|
695
728
|
const item = secretsKeychainItem(resolvedBundleName, raw.slice('keychain:'.length));
|
|
696
|
-
const removed = deleteKeychainToken(item
|
|
729
|
+
const removed = deleteKeychainToken(item);
|
|
697
730
|
if (removed) {
|
|
698
731
|
console.log(chalk.green(`Removed ${resolvedBundleName}.${resolvedKey} and purged keychain item.`));
|
|
699
732
|
return;
|
|
@@ -738,7 +771,7 @@ Examples:
|
|
|
738
771
|
}
|
|
739
772
|
if (!opts.keepSecrets) {
|
|
740
773
|
for (const { item } of keychainItemsForBundle(bundle)) {
|
|
741
|
-
deleteKeychainToken(item
|
|
774
|
+
deleteKeychainToken(item);
|
|
742
775
|
}
|
|
743
776
|
}
|
|
744
777
|
const existed = deleteBundle(resolvedName);
|
|
@@ -824,7 +857,7 @@ Examples:
|
|
|
824
857
|
}
|
|
825
858
|
else {
|
|
826
859
|
const item = secretsKeychainItem(resolvedBundleName, envKey);
|
|
827
|
-
setKeychainToken(item, value
|
|
860
|
+
setKeychainToken(item, value);
|
|
828
861
|
bundle.vars[envKey] = keychainRef(envKey);
|
|
829
862
|
}
|
|
830
863
|
added++;
|
|
@@ -848,7 +881,7 @@ Examples:
|
|
|
848
881
|
}
|
|
849
882
|
else {
|
|
850
883
|
const item = secretsKeychainItem(resolvedBundleName, key);
|
|
851
|
-
setKeychainToken(item, value
|
|
884
|
+
setKeychainToken(item, value);
|
|
852
885
|
bundle.vars[key] = keychainRef(key);
|
|
853
886
|
}
|
|
854
887
|
added++;
|
|
@@ -908,8 +941,8 @@ Examples:
|
|
|
908
941
|
console.log(chalk.green(`Exported to 1Password vault '${vault}': ${parts.join(', ')}.`));
|
|
909
942
|
return;
|
|
910
943
|
}
|
|
911
|
-
if (
|
|
912
|
-
console.error(chalk.red('export
|
|
944
|
+
if (!opts.plaintext) {
|
|
945
|
+
console.error(chalk.red('export prints secrets in the clear and requires --plaintext (works for TTY and pipes alike).'));
|
|
913
946
|
process.exit(1);
|
|
914
947
|
}
|
|
915
948
|
const { env } = readAndResolveBundleEnv(resolvedBundleName, { caller: `export to shell` });
|
|
@@ -1033,4 +1066,6 @@ Examples:
|
|
|
1033
1066
|
console.log(password);
|
|
1034
1067
|
}
|
|
1035
1068
|
});
|
|
1069
|
+
registerSecretsSyncCommands(cmd);
|
|
1070
|
+
registerSecretsMigrateAclCommand(cmd);
|
|
1036
1071
|
}
|
package/dist/commands/skills.js
CHANGED
|
@@ -7,9 +7,9 @@ import { select, checkbox } from '@inquirer/prompts';
|
|
|
7
7
|
import { AGENTS, SKILLS_CAPABLE_AGENTS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
8
|
import { cloneRepo } from '../lib/git.js';
|
|
9
9
|
import { discoverSkillsFromRepo, installSkillCentrally, listInstalledSkills, listInstalledSkillsWithScope, getSkillInfo, getSkillRules, getSkillsDir, countSkillFiles, tryParseSkillMetadata, diffVersionSkills, iterSkillsCapableVersions, removeSkillFromVersion, } from '../lib/skills.js';
|
|
10
|
-
import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath,
|
|
10
|
+
import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
11
11
|
import { recordVersionResources } from '../lib/state.js';
|
|
12
|
-
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, promptRemovalTargets, } from './utils.js';
|
|
12
|
+
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, resolveAgentTargetsAutoInstalling, promptRemovalTargets, } from './utils.js';
|
|
13
13
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
14
14
|
/** Register the `agents skills` command tree (list, add, remove, sync, prune, view). */
|
|
15
15
|
export function registerSkillsCommands(program) {
|
|
@@ -202,11 +202,24 @@ Examples:
|
|
|
202
202
|
spinner.succeed('Using local path');
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
console.log(chalk.bold(`\nFound ${discoveredSkills.length} skill(s):`));
|
|
206
205
|
if (discoveredSkills.length === 0) {
|
|
207
206
|
console.log(chalk.yellow('No skills found (looking for SKILL.md files)'));
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
209
|
+
// Filter by --names if provided. Mirrors the no-source path's behavior
|
|
210
|
+
// so users can pluck specific skills from a multi-skill repo.
|
|
211
|
+
const requestedNames = parseCommaSeparatedList(options.names);
|
|
212
|
+
if (requestedNames.length > 0) {
|
|
213
|
+
const discoveredNames = new Set(discoveredSkills.map((s) => s.name));
|
|
214
|
+
const missing = requestedNames.filter((n) => !discoveredNames.has(n));
|
|
215
|
+
if (missing.length > 0) {
|
|
216
|
+
console.log(chalk.red(`\nSkill(s) not found in source: ${missing.join(', ')}`));
|
|
217
|
+
console.log(chalk.gray(`Available: ${[...discoveredNames].join(', ')}`));
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
discoveredSkills = discoveredSkills.filter((s) => requestedNames.includes(s.name));
|
|
221
|
+
}
|
|
222
|
+
console.log(chalk.bold(`\nFound ${discoveredSkills.length} skill(s):`));
|
|
210
223
|
for (const skill of discoveredSkills) {
|
|
211
224
|
const nameColor = skill.parseError ? chalk.yellow : chalk.cyan;
|
|
212
225
|
console.log(`\n ${nameColor(skill.name)}: ${skill.metadata.description || 'no description'}`);
|
|
@@ -238,13 +251,17 @@ Examples:
|
|
|
238
251
|
let selectedAgents;
|
|
239
252
|
let versionSelections;
|
|
240
253
|
if (options.agents) {
|
|
241
|
-
const result =
|
|
254
|
+
const result = await resolveAgentTargetsAutoInstalling(options.agents, SKILLS_CAPABLE_AGENTS, { yes: options.yes });
|
|
255
|
+
if (!result) {
|
|
256
|
+
console.log(chalk.gray('Cancelled.'));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
242
259
|
selectedAgents = result.selectedAgents;
|
|
243
260
|
versionSelections = result.versionSelections;
|
|
244
261
|
}
|
|
245
262
|
else {
|
|
246
263
|
const result = await promptAgentVersionSelection(SKILLS_CAPABLE_AGENTS, {
|
|
247
|
-
skipPrompts: options.yes
|
|
264
|
+
skipPrompts: options.yes,
|
|
248
265
|
});
|
|
249
266
|
selectedAgents = result.selectedAgents;
|
|
250
267
|
versionSelections = result.versionSelections;
|
|
@@ -9,13 +9,12 @@ import chalk from 'chalk';
|
|
|
9
9
|
import ora from 'ora';
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
|
-
import {
|
|
13
|
-
import { AGENTS, agentLabel } from '../lib/agents.js';
|
|
12
|
+
import { agentLabel } from '../lib/agents.js';
|
|
14
13
|
import { cloneRepo } from '../lib/git.js';
|
|
15
14
|
import { discoverSubagentsFromRepo, installSubagentCentrally, listInstalledSubagents, getInstalledSubagent, listSubagentsForAgent, SUBAGENT_CAPABLE_AGENTS, iterSubagentsCapableVersions, removeSubagentFromVersion, } from '../lib/subagents.js';
|
|
16
|
-
import { listInstalledVersions, syncResourcesToVersion, getGlobalDefault, getVersionHomePath, } from '../lib/versions.js';
|
|
17
|
-
import { getSubagentsDir } from '../lib/state.js';
|
|
18
|
-
import {
|
|
15
|
+
import { listInstalledVersions, syncResourcesToVersion, getGlobalDefault, getVersionHomePath, promptAgentVersionSelection, } from '../lib/versions.js';
|
|
16
|
+
import { getSubagentsDir, recordVersionResources } from '../lib/state.js';
|
|
17
|
+
import { requireDestructiveArg, promptRemovalTargets, parseCommaSeparatedList, resolveAgentTargetsAutoInstalling, } from './utils.js';
|
|
19
18
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
20
19
|
/** Replace the home directory prefix with ~ for display. */
|
|
21
20
|
function formatPath(p) {
|
|
@@ -102,13 +101,20 @@ Examples:
|
|
|
102
101
|
subagentsCmd
|
|
103
102
|
.command('add <source>')
|
|
104
103
|
.description('Install subagents from a source (GitHub, local path) and sync to agent versions')
|
|
105
|
-
.option('-a, --agents <
|
|
104
|
+
.option('-a, --agents <list>', 'Targets: claude, openclaw, claude@2.1.141, claude@all, all')
|
|
105
|
+
.option('--names <list>', 'Subagent names from the source (comma-separated)')
|
|
106
106
|
.option('-y, --yes', 'Skip all prompts and confirmations')
|
|
107
107
|
.addHelpText('after', `
|
|
108
108
|
Examples:
|
|
109
109
|
# Install from GitHub
|
|
110
110
|
agents subagents add gh:team/subagents --agents claude,openclaw
|
|
111
111
|
|
|
112
|
+
# Pluck specific subagents from a multi-subagent repo
|
|
113
|
+
agents subagents add gh:team/subagents --names code-reviewer,planner
|
|
114
|
+
|
|
115
|
+
# Install across every installed Claude version
|
|
116
|
+
agents subagents add gh:team/subagents --agents claude@all
|
|
117
|
+
|
|
112
118
|
# Install from local directory (must contain subagents/*/AGENT.md)
|
|
113
119
|
agents subagents add ~/my-subagent --agents claude
|
|
114
120
|
|
|
@@ -117,9 +123,13 @@ Examples:
|
|
|
117
123
|
`)
|
|
118
124
|
.action(async (source, options) => {
|
|
119
125
|
const spinner = ora({ text: 'Fetching source...', isSilent: !process.stdout.isTTY }).start();
|
|
120
|
-
// Clone or use local source
|
|
126
|
+
// Clone or use local source. Accept any git-like scheme to match the
|
|
127
|
+
// other <resource> add commands (skills, workflows, commands, hooks).
|
|
121
128
|
let sourcePath;
|
|
122
|
-
|
|
129
|
+
const isGitRepo = source.startsWith('gh:') || source.startsWith('git:') ||
|
|
130
|
+
source.startsWith('ssh:') || source.startsWith('https://') ||
|
|
131
|
+
source.startsWith('http://');
|
|
132
|
+
if (isGitRepo) {
|
|
123
133
|
try {
|
|
124
134
|
const cloneResult = await cloneRepo(source);
|
|
125
135
|
sourcePath = cloneResult.localPath;
|
|
@@ -138,12 +148,24 @@ Examples:
|
|
|
138
148
|
}
|
|
139
149
|
// Discover subagents
|
|
140
150
|
spinner.text = 'Discovering subagents...';
|
|
141
|
-
|
|
151
|
+
let discovered = discoverSubagentsFromRepo(sourcePath);
|
|
142
152
|
if (discovered.length === 0) {
|
|
143
153
|
spinner.fail('No subagents found in source');
|
|
144
154
|
console.log(chalk.gray(`Expected: subagents/*/AGENT.md`));
|
|
145
155
|
process.exit(1);
|
|
146
156
|
}
|
|
157
|
+
// --names filter: pluck specific subagents from a multi-subagent source.
|
|
158
|
+
const requestedNames = parseCommaSeparatedList(options.names);
|
|
159
|
+
if (requestedNames.length > 0) {
|
|
160
|
+
const discoveredNames = new Set(discovered.map((s) => s.name));
|
|
161
|
+
const missing = requestedNames.filter((n) => !discoveredNames.has(n));
|
|
162
|
+
if (missing.length > 0) {
|
|
163
|
+
spinner.fail(`Subagent(s) not found in source: ${missing.join(', ')}`);
|
|
164
|
+
console.log(chalk.gray(`Available: ${[...discoveredNames].join(', ')}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
discovered = discovered.filter((s) => requestedNames.includes(s.name));
|
|
168
|
+
}
|
|
147
169
|
spinner.succeed(`Found ${discovered.length} subagent(s)`);
|
|
148
170
|
// Show what we found
|
|
149
171
|
console.log();
|
|
@@ -151,42 +173,29 @@ Examples:
|
|
|
151
173
|
console.log(` ${chalk.cyan(sub.name)}: ${chalk.gray(sub.frontmatter.description)}`);
|
|
152
174
|
}
|
|
153
175
|
console.log();
|
|
154
|
-
// Determine target
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
else {
|
|
168
|
-
if (!isInteractiveTerminal()) {
|
|
169
|
-
requireInteractiveSelection('Selecting target agents for subagents', [
|
|
170
|
-
'agents subagents add <source> --agents claude openclaw',
|
|
171
|
-
'agents subagents add <source> --yes',
|
|
172
|
-
]);
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
targetAgents = await checkbox({
|
|
176
|
-
message: 'Install to which agents?',
|
|
177
|
-
choices: installedAgents.map(id => ({
|
|
178
|
-
name: AGENTS[id].name,
|
|
179
|
-
value: id,
|
|
180
|
-
checked: true,
|
|
181
|
-
})),
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
catch (err) {
|
|
185
|
-
if (isPromptCancelled(err))
|
|
186
|
-
return;
|
|
187
|
-
throw err;
|
|
188
|
-
}
|
|
176
|
+
// Determine target agent versions, using the same path skills/workflows use.
|
|
177
|
+
// Back-compat: commander's old `--agents <agents...>` shape arrives as an array;
|
|
178
|
+
// join it with commas so resolveAgentVersionTargets can parse it.
|
|
179
|
+
const agentsArg = Array.isArray(options.agents)
|
|
180
|
+
? options.agents.join(',')
|
|
181
|
+
: options.agents;
|
|
182
|
+
let selectedAgents;
|
|
183
|
+
let versionSelections;
|
|
184
|
+
if (agentsArg) {
|
|
185
|
+
const result = await resolveAgentTargetsAutoInstalling(agentsArg, SUBAGENT_CAPABLE_AGENTS, { yes: options.yes });
|
|
186
|
+
if (!result) {
|
|
187
|
+
console.log(chalk.gray('Cancelled.'));
|
|
188
|
+
return;
|
|
189
189
|
}
|
|
190
|
+
selectedAgents = result.selectedAgents;
|
|
191
|
+
versionSelections = result.versionSelections;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const result = await promptAgentVersionSelection(SUBAGENT_CAPABLE_AGENTS, {
|
|
195
|
+
skipPrompts: options.yes,
|
|
196
|
+
});
|
|
197
|
+
selectedAgents = result.selectedAgents;
|
|
198
|
+
versionSelections = result.versionSelections;
|
|
190
199
|
}
|
|
191
200
|
// Install centrally
|
|
192
201
|
const installSpinner = ora({ text: 'Installing subagents...', isSilent: !process.stdout.isTTY }).start();
|
|
@@ -198,16 +207,27 @@ Examples:
|
|
|
198
207
|
}
|
|
199
208
|
}
|
|
200
209
|
installSpinner.succeed(`Installed ${discovered.length} subagent(s) to ${formatPath(getSubagentsDir())}`);
|
|
201
|
-
// Sync to
|
|
202
|
-
if (
|
|
210
|
+
// Sync to selected versions
|
|
211
|
+
if (versionSelections.size > 0) {
|
|
203
212
|
const syncSpinner = ora({ text: 'Syncing to agents...', isSilent: !process.stdout.isTTY }).start();
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
const subagentNames = discovered.map((s) => s.name);
|
|
214
|
+
let synced = 0;
|
|
215
|
+
for (const [agentId, versions] of versionSelections) {
|
|
206
216
|
for (const version of versions) {
|
|
207
217
|
syncResourcesToVersion(agentId, version);
|
|
218
|
+
recordVersionResources(agentId, version, 'subagents', subagentNames);
|
|
219
|
+
synced++;
|
|
208
220
|
}
|
|
209
221
|
}
|
|
210
|
-
|
|
222
|
+
if (synced > 0) {
|
|
223
|
+
syncSpinner.succeed(`Synced to ${synced} agent version(s) across ${selectedAgents.map((id) => agentLabel(id)).join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
syncSpinner.info('No version-managed agents to sync');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(chalk.gray('Stored centrally; no agent versions selected for sync.'));
|
|
211
231
|
}
|
|
212
232
|
console.log();
|
|
213
233
|
});
|
package/dist/commands/teams.js
CHANGED
|
@@ -14,6 +14,7 @@ import { buildPreview as buildSessionPreview } from './sessions-picker.js';
|
|
|
14
14
|
import { parseExecEnv } from '../lib/exec.js';
|
|
15
15
|
import { teamPicker, printTeamTable } from './teams-picker.js';
|
|
16
16
|
import { itemPicker } from '../lib/picker.js';
|
|
17
|
+
import { profileExists, readProfile } from '../lib/profiles.js';
|
|
17
18
|
import { isPromptCancelled, isInteractiveTerminal, requireDestructiveArg, requireInteractiveSelection, } from './utils.js';
|
|
18
19
|
const AGENT_NAMES = {
|
|
19
20
|
claude: 'Claude',
|
|
@@ -25,7 +26,8 @@ const AGENT_NAMES = {
|
|
|
25
26
|
antigravity: 'Antigravity',
|
|
26
27
|
};
|
|
27
28
|
const VALID_AGENTS = Object.keys(AGENT_NAMES);
|
|
28
|
-
|
|
29
|
+
// 'full' kept as historical alias for 'skip'; normalized to 'skip' downstream.
|
|
30
|
+
const VALID_MODES = ['plan', 'edit', 'auto', 'skip', 'full'];
|
|
29
31
|
const VALID_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'auto'];
|
|
30
32
|
const VALID_CLOUD_PROVIDERS = ['rush', 'codex', 'factory'];
|
|
31
33
|
// Auto-enable JSON mode when piped / not a TTY so AI agent consumers get
|
|
@@ -87,14 +89,48 @@ function fullName(type, version) {
|
|
|
87
89
|
const name = AGENT_NAMES[type];
|
|
88
90
|
return version ? `${name} ${version}` : name;
|
|
89
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a teammate spec to its execution target.
|
|
94
|
+
*
|
|
95
|
+
* Accepts:
|
|
96
|
+
* - `claude` — default version of an installed agent
|
|
97
|
+
* - `claude@2.1.112` — pinned version of an installed agent
|
|
98
|
+
* - `<profile-name>` — runs through `agents run <profile>`, with the
|
|
99
|
+
* profile's host agent used as the underlying
|
|
100
|
+
* AgentType for event parsing and CLI checks.
|
|
101
|
+
*
|
|
102
|
+
* `agent` is always the underlying harness so event parsers, CLI-availability
|
|
103
|
+
* checks, and version pins keep working. `profileName` is set only when the
|
|
104
|
+
* spec resolved through a profile.
|
|
105
|
+
*/
|
|
90
106
|
function parseTeammate(spec) {
|
|
91
107
|
const [name, version] = spec.split('@');
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
if (VALID_AGENTS.includes(name)) {
|
|
109
|
+
const agent = name;
|
|
110
|
+
return {
|
|
111
|
+
agent,
|
|
112
|
+
version: resolveVersionAlias(agent, version) ?? null,
|
|
113
|
+
profileName: null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Not a built-in agent id — try resolving as a profile name. A profile
|
|
117
|
+
// pinning a version is allowed; `profile@<override>` is not (would conflict
|
|
118
|
+
// with the profile's own host.version).
|
|
119
|
+
if (!version && profileExists(name)) {
|
|
120
|
+
try {
|
|
121
|
+
const profile = readProfile(name);
|
|
122
|
+
return {
|
|
123
|
+
agent: profile.host.agent,
|
|
124
|
+
version: profile.host.version ?? null,
|
|
125
|
+
profileName: profile.name,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
die(`Profile '${name}' is malformed: ${err.message}`);
|
|
130
|
+
}
|
|
95
131
|
}
|
|
96
|
-
|
|
97
|
-
|
|
132
|
+
die(`Unknown teammate '${spec}'. Available agents: ${VALID_AGENTS.join(', ')}.\n` +
|
|
133
|
+
` Use 'claude', 'claude@2.1.112', or the name of a profile from 'agents view'.`);
|
|
98
134
|
}
|
|
99
135
|
function shortId(id) {
|
|
100
136
|
return id.slice(0, 8);
|
|
@@ -547,6 +583,8 @@ export function registerTeamsCommands(program) {
|
|
|
547
583
|
Teammate syntax:
|
|
548
584
|
'claude' the default Claude version on this machine
|
|
549
585
|
'claude@2.1.112' a specific installed version (see 'agents view')
|
|
586
|
+
'<profile>' a profile from 'agents view' — runs through 'agents
|
|
587
|
+
run <profile>' with the profile's host harness
|
|
550
588
|
|
|
551
589
|
Short aliases:
|
|
552
590
|
teams c = create teams a = add teams s = status
|
|
@@ -711,7 +749,7 @@ export function registerTeamsCommands(program) {
|
|
|
711
749
|
.alias('a')
|
|
712
750
|
.description("Add a teammate to work on a task. Runs in background; returns immediately. Use 'status' to check in.")
|
|
713
751
|
.option('-n, --name <name>', 'Friendly name for this teammate (e.g. alice). Required if using --after. Unique within team.')
|
|
714
|
-
.option('-m, --mode <mode>', `Permissions: plan (read-only) | edit (can write files) |
|
|
752
|
+
.option('-m, --mode <mode>', `Permissions: plan (read-only) | edit (can write files) | auto (smart classifier auto-approves safe ops) | skip (bypass all permission prompts). 'full' accepted as alias for skip.`, 'edit')
|
|
715
753
|
.option('-e, --effort <effort>', `Reasoning intensity: ${VALID_EFFORTS.join('|')}`, 'medium')
|
|
716
754
|
.option('--model <model>', 'Override the effort tier and use this specific model (e.g. claude-opus-4-6)')
|
|
717
755
|
.option('--env <key=value>', 'Set an environment variable for this teammate (repeatable for multiple vars)', (val, prev) => [...prev, val], [])
|
|
@@ -747,7 +785,7 @@ export function registerTeamsCommands(program) {
|
|
|
747
785
|
die(`--cloud rush requires --repo <owner/repo>`);
|
|
748
786
|
}
|
|
749
787
|
}
|
|
750
|
-
const { agent, version } = parseTeammate(teammate);
|
|
788
|
+
const { agent, version, profileName } = parseTeammate(teammate);
|
|
751
789
|
if (version && !isVersionInstalled(agent, version)) {
|
|
752
790
|
die(`${AGENT_NAMES[agent]} ${version} isn't installed.\n` +
|
|
753
791
|
` Install it: agents add ${agent}@${version}\n` +
|
|
@@ -872,12 +910,12 @@ export function registerTeamsCommands(program) {
|
|
|
872
910
|
}
|
|
873
911
|
}
|
|
874
912
|
try {
|
|
875
|
-
const result = await handleSpawn(mgr, team, agent, effectiveTask, cwd, opts.mode, opts.effort, null, cwd, version, opts.name ?? null, after, opts.model ?? null, envOverrides ?? null, taskType, cloudProviderId, cloudSessionId, opts.repo ?? null, opts.branch ?? null, worktreeName, worktreePath);
|
|
913
|
+
const result = await handleSpawn(mgr, team, agent, effectiveTask, cwd, opts.mode, opts.effort, null, cwd, version, opts.name ?? null, after, opts.model ?? null, envOverrides ?? null, taskType, cloudProviderId, cloudSessionId, opts.repo ?? null, opts.branch ?? null, worktreeName, worktreePath, profileName);
|
|
876
914
|
if (isJsonMode(opts)) {
|
|
877
915
|
console.log(JSON.stringify(result, null, 2));
|
|
878
916
|
return;
|
|
879
917
|
}
|
|
880
|
-
const who = fullName(agent, version);
|
|
918
|
+
const who = profileName ? `${profileName} (via ${fullName(agent, version)})` : fullName(agent, version);
|
|
881
919
|
const staged = result.status === 'pending';
|
|
882
920
|
const verb = staged ? 'Staged' : 'Welcomed';
|
|
883
921
|
const greeting = result.name
|