@phnx-labs/agents-cli 1.20.5 → 1.20.6
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/README.md +1 -1
- package/dist/commands/browser.js +31 -4
- package/dist/commands/computer.js +10 -2
- package/dist/commands/defaults.d.ts +7 -0
- package/dist/commands/defaults.js +89 -0
- package/dist/commands/exec.js +24 -6
- package/dist/commands/rules.js +3 -3
- package/dist/commands/secrets.js +46 -9
- package/dist/commands/setup.js +2 -2
- package/dist/commands/teams.js +108 -11
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +121 -38
- package/dist/index.js +38 -21
- package/dist/lib/agents.d.ts +10 -6
- package/dist/lib/agents.js +23 -14
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +84 -3
- package/dist/lib/exec.js +24 -4
- package/dist/lib/migrate.js +6 -4
- package/dist/lib/permissions.d.ts +23 -0
- package/dist/lib/permissions.js +89 -7
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/project-launch.d.ts +5 -0
- package/dist/lib/project-launch.js +37 -0
- package/dist/lib/pty-server.js +7 -4
- package/dist/lib/resources/rules.js +1 -1
- package/dist/lib/resources/skills.js +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.js +2 -1
- package/dist/lib/rotate.js +6 -18
- package/dist/lib/run-config.d.ts +9 -0
- package/dist/lib/run-config.js +35 -0
- package/dist/lib/run-defaults.d.ts +42 -0
- package/dist/lib/run-defaults.js +180 -0
- 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/install-helper.d.ts +11 -3
- package/dist/lib/secrets/install-helper.js +48 -6
- package/dist/lib/secrets/linux.d.ts +12 -0
- package/dist/lib/secrets/linux.js +30 -16
- package/dist/lib/shims.d.ts +9 -1
- package/dist/lib/shims.js +35 -3
- package/dist/lib/staleness/detectors/hooks.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/teams/api.d.ts +67 -0
- package/dist/lib/teams/api.js +78 -0
- package/dist/lib/types.d.ts +15 -6
- package/dist/lib/versions.js +4 -4
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -243,7 +243,7 @@ agents teams status auth-feature # Who's working, what they changed, what the
|
|
|
243
243
|
|
|
244
244
|
Teammates run detached -- close your terminal, they keep working. Check in with `teams status`, read full output with `teams logs <name>`, clean up with `teams disband`.
|
|
245
245
|
|
|
246
|
-
Team state is observable via `agents teams list --json` / `agents teams status --json
|
|
246
|
+
Team state is observable via `agents teams list --json` / `agents teams status --json` (compact by default; add `--verbose` for the full per-teammate shape). External tools join it with `sessions --json` (teammates get `isTeamOrigin: true`) and `cloud list --json` (for `--cloud` teammates) to build a unified fleet view. See [docs/06-observability.md](docs/06-observability.md).
|
|
247
247
|
|
|
248
248
|
---
|
|
249
249
|
|
package/dist/commands/browser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { listProfiles, getProfile, createProfile, deleteProfile, ensureDefaultBrowserProfile, getProfileRuntimeDir, extractConfiguredPort, findFreeProfilePort, getEndpointPresets, } from '../lib/browser/profiles.js';
|
|
4
|
-
import { findBrowserPath, getPortOccupant } from '../lib/browser/chrome.js';
|
|
4
|
+
import { findBrowserPath, getPortOccupant, isLauncherScript } from '../lib/browser/chrome.js';
|
|
5
5
|
import { listProfileCacheDirs, removeProfileCache, listAllProfileSnapshots, } from '../lib/browser/runtime-state.js';
|
|
6
6
|
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
7
7
|
import { discoverBrowserWsUrl, verifyBrowserIdentity } from '../lib/browser/cdp.js';
|
|
@@ -311,10 +311,28 @@ function registerProfilesCommands(browser) {
|
|
|
311
311
|
process.exit(1);
|
|
312
312
|
}
|
|
313
313
|
const checks = [];
|
|
314
|
-
// 1. Binary exists for declared browser type
|
|
314
|
+
// 1. Binary exists for declared browser type, and is a real executable we
|
|
315
|
+
// can drive — not a distro launcher script. findBrowserPath already
|
|
316
|
+
// unwraps the known Chromium wrappers to their ELF; if it still hands
|
|
317
|
+
// back a shebang script we couldn't resolve, `start` would fail with
|
|
318
|
+
// `CDP connection closed` (the wrapper re-execs the browser as a child,
|
|
319
|
+
// breaking the --remote-debugging-pipe transport — issue #229). Flag it
|
|
320
|
+
// here instead of letting launch fail opaquely.
|
|
315
321
|
try {
|
|
316
322
|
const binPath = findBrowserPath(profile.browser, profile.binary);
|
|
317
|
-
|
|
323
|
+
if (isLauncherScript(binPath)) {
|
|
324
|
+
checks.push({
|
|
325
|
+
label: 'binary',
|
|
326
|
+
ok: false,
|
|
327
|
+
detail: `${binPath} is a launcher script, not the browser executable — ` +
|
|
328
|
+
`agents browser drives the browser over --remote-debugging-pipe and ` +
|
|
329
|
+
`can't attach to a wrapper that re-execs it. Point the profile at the ` +
|
|
330
|
+
`real binary (\`--binary /path/to/browser\`) or reinstall the standard package.`,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
checks.push({ label: 'binary', ok: true, detail: binPath });
|
|
335
|
+
}
|
|
318
336
|
}
|
|
319
337
|
catch (err) {
|
|
320
338
|
checks.push({
|
|
@@ -351,7 +369,16 @@ function registerProfilesCommands(browser) {
|
|
|
351
369
|
else {
|
|
352
370
|
const occupant = getPortOccupant(port);
|
|
353
371
|
if (!occupant) {
|
|
354
|
-
|
|
372
|
+
// A free port doesn't mean "ready to launch here": for a local
|
|
373
|
+
// profile we self-launch over an internal --remote-debugging-pipe and
|
|
374
|
+
// never bind this port. The port is consulted only to attach to a
|
|
375
|
+
// browser someone already started on it. Say so, so a green doctor
|
|
376
|
+
// can't be read as "the port is what launch depends on" (#229).
|
|
377
|
+
checks.push({
|
|
378
|
+
label: 'port',
|
|
379
|
+
ok: true,
|
|
380
|
+
detail: `${port} free — will self-launch over an internal pipe (port used only to attach to an already-running browser)`,
|
|
381
|
+
});
|
|
355
382
|
}
|
|
356
383
|
else {
|
|
357
384
|
try {
|
|
@@ -12,8 +12,16 @@ const COMPUTER_HELP_GROUPS = [
|
|
|
12
12
|
];
|
|
13
13
|
export function registerComputerCommand(program) {
|
|
14
14
|
const computer = program
|
|
15
|
-
.command('
|
|
16
|
-
.description('Drive macOS apps via Accessibility — list, screenshot, click, type')
|
|
15
|
+
.command('computers')
|
|
16
|
+
.description('Drive macOS apps via Accessibility — list, screenshot, click, type (macOS only)')
|
|
17
|
+
// The whole subsystem is macOS Accessibility / TCC. Fail fast with a clear
|
|
18
|
+
// message on other platforms instead of a downstream ENOENT / launchctl error.
|
|
19
|
+
.hook('preAction', () => {
|
|
20
|
+
if (process.platform !== 'darwin') {
|
|
21
|
+
console.error('agents computers: macOS only — it drives apps via the macOS Accessibility API.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
17
25
|
registerComputerSubcommands(computer);
|
|
18
26
|
registerCommandGroups(computer, COMPUTER_HELP_GROUPS);
|
|
19
27
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defaults command tree.
|
|
3
|
+
*
|
|
4
|
+
* `agents defaults run ...` manages selector-based defaults for `agents run`.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { setHelpSections } from '../lib/help.js';
|
|
8
|
+
import { listRunDefaults, setRunDefault, unsetRunDefault, } from '../lib/run-defaults.js';
|
|
9
|
+
function formatRunDefault(entry) {
|
|
10
|
+
const parts = [];
|
|
11
|
+
if (entry.defaults.mode)
|
|
12
|
+
parts.push(`mode ${chalk.white(entry.defaults.mode)}`);
|
|
13
|
+
if (entry.defaults.model)
|
|
14
|
+
parts.push(`model ${chalk.white(entry.defaults.model)}`);
|
|
15
|
+
return `${chalk.cyan(entry.selector.padEnd(22))} ${parts.join(' ')}`;
|
|
16
|
+
}
|
|
17
|
+
export function registerDefaultsCommands(program) {
|
|
18
|
+
const defaults = program
|
|
19
|
+
.command('defaults')
|
|
20
|
+
.description('Manage default options for agents-cli commands');
|
|
21
|
+
const run = defaults
|
|
22
|
+
.command('run')
|
|
23
|
+
.description('Manage selector-based defaults for `agents run`');
|
|
24
|
+
setHelpSections(run, {
|
|
25
|
+
examples: `
|
|
26
|
+
agents defaults run list
|
|
27
|
+
agents defaults run set 'claude:*' --mode auto --model opus
|
|
28
|
+
agents defaults run set claude@2.1.45 --mode plan --model sonnet
|
|
29
|
+
agents defaults run unset 'claude:*'
|
|
30
|
+
`,
|
|
31
|
+
notes: `
|
|
32
|
+
Selectors use <agent>:<version>. Use * for all versions of an agent.
|
|
33
|
+
Exact selectors override wildcard selectors field by field.
|
|
34
|
+
Explicit flags on agents run always win over configured defaults.
|
|
35
|
+
`,
|
|
36
|
+
});
|
|
37
|
+
run
|
|
38
|
+
.command('list')
|
|
39
|
+
.description('List configured run defaults')
|
|
40
|
+
.action(() => {
|
|
41
|
+
const entries = listRunDefaults();
|
|
42
|
+
if (entries.length === 0) {
|
|
43
|
+
console.log(chalk.gray('No run defaults configured.'));
|
|
44
|
+
console.log(chalk.gray("Set one with: agents defaults run set 'claude:*' --mode auto --model opus"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(chalk.bold('Run Defaults\n'));
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
console.log(` ${formatRunDefault(entry)}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
run
|
|
53
|
+
.command('set <selector>')
|
|
54
|
+
.description('Set defaults for an agent/version selector')
|
|
55
|
+
.option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
|
|
56
|
+
.option('--model <model>', 'Default model or model alias, forwarded via --model')
|
|
57
|
+
.action((selector, options) => {
|
|
58
|
+
try {
|
|
59
|
+
const entry = setRunDefault(selector, {
|
|
60
|
+
...(options.mode !== undefined ? { mode: options.mode } : {}),
|
|
61
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
62
|
+
});
|
|
63
|
+
console.log(chalk.green('Set run default:'));
|
|
64
|
+
console.log(` ${formatRunDefault(entry)}`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.error(chalk.red(err.message));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
run
|
|
72
|
+
.command('unset <selector>')
|
|
73
|
+
.description('Remove defaults for an agent/version selector')
|
|
74
|
+
.action((selector) => {
|
|
75
|
+
try {
|
|
76
|
+
const removed = unsetRunDefault(selector);
|
|
77
|
+
if (removed) {
|
|
78
|
+
console.log(chalk.green(`Removed run default ${selector}`));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(chalk.gray(`No run default matched ${selector}`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error(chalk.red(err.message));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -85,7 +85,7 @@ export function registerRunCommand(program) {
|
|
|
85
85
|
`,
|
|
86
86
|
});
|
|
87
87
|
runCmd.action(async (agentSpec, prompt, options) => {
|
|
88
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, defaultModeFor }, { ALL_AGENT_IDS }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersionAlias }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef },] = await Promise.all([
|
|
88
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, defaultModeFor }, { ALL_AGENT_IDS }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef }, { resolveRunDefaults },] = await Promise.all([
|
|
89
89
|
import('../lib/exec.js'),
|
|
90
90
|
import('../lib/agents.js'),
|
|
91
91
|
import('../lib/profiles.js'),
|
|
@@ -94,6 +94,7 @@ export function registerRunCommand(program) {
|
|
|
94
94
|
import('../lib/versions.js'),
|
|
95
95
|
import('../lib/plugins.js'),
|
|
96
96
|
import('../lib/workflows.js'),
|
|
97
|
+
import('../lib/run-defaults.js'),
|
|
97
98
|
]);
|
|
98
99
|
const isValidAgent = (agent) => ALL_AGENT_IDS.includes(agent);
|
|
99
100
|
// Parse agent@version
|
|
@@ -102,6 +103,7 @@ export function registerRunCommand(program) {
|
|
|
102
103
|
let version = rawVersion || undefined;
|
|
103
104
|
let profileEnv;
|
|
104
105
|
let fromProfile = false;
|
|
106
|
+
let workflowModel;
|
|
105
107
|
const cwd = options.cwd ?? process.cwd();
|
|
106
108
|
if (isValidAgent(rawAgent)) {
|
|
107
109
|
agent = rawAgent;
|
|
@@ -133,6 +135,10 @@ export function registerRunCommand(program) {
|
|
|
133
135
|
// subagents/*.md ← flat .md files copied to ~/.claude/agents/ for Agent tool discovery
|
|
134
136
|
const workflowDir = resolveWorkflowRef(rawAgent, cwd);
|
|
135
137
|
agent = 'claude';
|
|
138
|
+
const workflowFrontmatter = parseWorkflowFrontmatter(workflowDir);
|
|
139
|
+
if (typeof workflowFrontmatter?.model === 'string' && workflowFrontmatter.model.trim() !== '') {
|
|
140
|
+
workflowModel = workflowFrontmatter.model.trim();
|
|
141
|
+
}
|
|
136
142
|
const resolvedVersion = resolveVersionAlias('claude', version);
|
|
137
143
|
const versionHome = getVersionHomePath('claude', resolvedVersion ?? getGlobalDefault('claude') ?? '');
|
|
138
144
|
const claudeAgentsDir = path.join(versionHome, '.claude', 'agents');
|
|
@@ -179,8 +185,7 @@ export function registerRunCommand(program) {
|
|
|
179
185
|
// Auto-inject secrets bundles declared in the workflow's frontmatter `secrets:` field.
|
|
180
186
|
// Union with any --secrets flags the user passed; dedupe. Skip when --no-auto-secrets is set.
|
|
181
187
|
if (!options.noAutoSecrets) {
|
|
182
|
-
const
|
|
183
|
-
const declared = fm?.secrets ?? [];
|
|
188
|
+
const declared = workflowFrontmatter?.secrets ?? [];
|
|
184
189
|
if (declared.length > 0) {
|
|
185
190
|
const existing = new Set(options.secrets);
|
|
186
191
|
const added = [];
|
|
@@ -262,9 +267,18 @@ export function registerRunCommand(program) {
|
|
|
262
267
|
}
|
|
263
268
|
}
|
|
264
269
|
}
|
|
270
|
+
const defaultVersion = version ?? resolveVersion(agent, cwd);
|
|
271
|
+
const runDefaults = fromProfile
|
|
272
|
+
? { sources: {} }
|
|
273
|
+
: resolveRunDefaults(agent, defaultVersion, cwd);
|
|
265
274
|
// Accept the four canonical modes plus 'full' as a permanent silent
|
|
266
275
|
// alias for 'skip' (rewritten downstream by normalizeMode in exec.ts).
|
|
267
276
|
let mode = options.mode;
|
|
277
|
+
const modeSource = runCmd.getOptionValueSource('mode');
|
|
278
|
+
const modeFromRunDefault = modeSource === 'default' && !!runDefaults.mode;
|
|
279
|
+
if (modeFromRunDefault) {
|
|
280
|
+
mode = runDefaults.mode;
|
|
281
|
+
}
|
|
268
282
|
if (!['plan', 'edit', 'auto', 'skip', 'full'].includes(mode)) {
|
|
269
283
|
console.error(chalk.red(`Invalid mode: ${mode}. Use plan, edit, auto, or skip ('full' accepted as alias for skip).`));
|
|
270
284
|
process.exit(1);
|
|
@@ -276,13 +290,12 @@ export function registerRunCommand(program) {
|
|
|
276
290
|
// user did not ask for read-only, they asked for "just run it." An
|
|
277
291
|
// explicit --mode plan still throws (see resolveMode), because silently
|
|
278
292
|
// elevating an explicit read-only request to edit is unsafe.
|
|
279
|
-
const modeSource = runCmd.getOptionValueSource('mode');
|
|
280
293
|
const modeIsDefault = modeSource === 'default';
|
|
281
294
|
try {
|
|
282
295
|
resolveMode(agent, normalizeMode(mode));
|
|
283
296
|
}
|
|
284
297
|
catch (err) {
|
|
285
|
-
if (modeIsDefault) {
|
|
298
|
+
if (modeIsDefault && !modeFromRunDefault) {
|
|
286
299
|
mode = defaultModeFor(agent);
|
|
287
300
|
if (!options.quiet) {
|
|
288
301
|
process.stderr.write(chalk.gray(`[agents] ${agent} has no '${options.mode}' mode; using '${mode}'\n`));
|
|
@@ -334,6 +347,11 @@ export function registerRunCommand(program) {
|
|
|
334
347
|
const env = hasOverrides
|
|
335
348
|
? { ...(profileEnv ?? {}), ...secretsEnv, ...(userEnv ?? {}) }
|
|
336
349
|
: undefined;
|
|
350
|
+
const modelSource = runCmd.getOptionValueSource('model');
|
|
351
|
+
const model = options.model
|
|
352
|
+
?? (!fromProfile && modelSource === undefined
|
|
353
|
+
? (workflowModel ?? (options.fallback ? undefined : runDefaults.model))
|
|
354
|
+
: undefined);
|
|
337
355
|
const execOptions = {
|
|
338
356
|
agent,
|
|
339
357
|
version,
|
|
@@ -342,7 +360,7 @@ export function registerRunCommand(program) {
|
|
|
342
360
|
mode,
|
|
343
361
|
effort,
|
|
344
362
|
cwd: options.cwd,
|
|
345
|
-
model
|
|
363
|
+
model,
|
|
346
364
|
addDirs: options.addDir,
|
|
347
365
|
json: options.json,
|
|
348
366
|
headless: options.headless ?? true,
|
package/dist/commands/rules.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { select, checkbox } from '@inquirer/prompts';
|
|
7
|
-
import { AGENTS, ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
7
|
+
import { AGENTS, agentConfigDirName, ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
8
|
import { cloneRepo } from '../lib/git.js';
|
|
9
9
|
import { discoverInstructionsFromRepo, discoverRuleFilesFromRepo, installInstructionsCentrally, uninstallInstructions, listInstalledInstructionsWithScope, instructionsExists, getInstructionsContent, listCentralRules, } from '../lib/rules/rules.js';
|
|
10
10
|
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
@@ -426,7 +426,7 @@ Examples:
|
|
|
426
426
|
return;
|
|
427
427
|
}
|
|
428
428
|
const home = getVersionHomePath(agentId, requestedVersion);
|
|
429
|
-
const filePath = path.join(home,
|
|
429
|
+
const filePath = path.join(home, agentConfigDirName(agentId), AGENTS[agentId].instructionsFile);
|
|
430
430
|
if (!fs.existsSync(filePath)) {
|
|
431
431
|
console.log(chalk.yellow(`No user rules found for ${agentLabel(agentId)}@${requestedVersion}`));
|
|
432
432
|
return;
|
|
@@ -486,7 +486,7 @@ Examples:
|
|
|
486
486
|
}
|
|
487
487
|
const home = getVersionHomePath(agentId, requestedVersion);
|
|
488
488
|
const agent = AGENTS[agentId];
|
|
489
|
-
const filePath = path.join(home,
|
|
489
|
+
const filePath = path.join(home, agentConfigDirName(agentId), agent.instructionsFile);
|
|
490
490
|
if (fs.existsSync(filePath)) {
|
|
491
491
|
fs.unlinkSync(filePath);
|
|
492
492
|
console.log(chalk.green(`Removed ${agent.instructionsFile} from ${agentLabel(agent.id)}@${requestedVersion}`));
|
package/dist/commands/secrets.js
CHANGED
|
@@ -1052,15 +1052,15 @@ Examples:
|
|
|
1052
1052
|
password += charClass[charIndex];
|
|
1053
1053
|
}
|
|
1054
1054
|
if (opts.copy) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1055
|
+
try {
|
|
1056
|
+
await copyToClipboard(password);
|
|
1057
|
+
console.log(chalk.green(`Password copied to clipboard (${length} chars)`));
|
|
1058
|
+
}
|
|
1059
|
+
catch (err) {
|
|
1060
|
+
console.error(chalk.red(`Clipboard copy failed: ${err.message}`));
|
|
1061
|
+
console.error(chalk.gray('Re-run without --copy to print the password instead.'));
|
|
1062
|
+
process.exitCode = 1;
|
|
1063
|
+
}
|
|
1064
1064
|
}
|
|
1065
1065
|
else {
|
|
1066
1066
|
console.log(password);
|
|
@@ -1069,3 +1069,40 @@ Examples:
|
|
|
1069
1069
|
registerSecretsSyncCommands(cmd);
|
|
1070
1070
|
registerSecretsMigrateAclCommand(cmd);
|
|
1071
1071
|
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Copy text to the system clipboard, cross-platform.
|
|
1074
|
+
* macOS: `pbcopy`. Windows: `clip`. Linux: tries `wl-copy` (Wayland), then
|
|
1075
|
+
* `xclip`, then `xsel` (X11). Throws with an install hint if none are present.
|
|
1076
|
+
*/
|
|
1077
|
+
async function copyToClipboard(text) {
|
|
1078
|
+
const { spawn } = await import('child_process');
|
|
1079
|
+
const candidates = process.platform === 'darwin'
|
|
1080
|
+
? [['pbcopy', []]]
|
|
1081
|
+
: process.platform === 'win32'
|
|
1082
|
+
? [['clip', []]]
|
|
1083
|
+
: [
|
|
1084
|
+
['wl-copy', []],
|
|
1085
|
+
['xclip', ['-selection', 'clipboard']],
|
|
1086
|
+
['xsel', ['--clipboard', '--input']],
|
|
1087
|
+
];
|
|
1088
|
+
let lastErr = null;
|
|
1089
|
+
for (const [cmd, args] of candidates) {
|
|
1090
|
+
try {
|
|
1091
|
+
await new Promise((resolve, reject) => {
|
|
1092
|
+
const proc = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore'] });
|
|
1093
|
+
proc.on('error', reject);
|
|
1094
|
+
proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`${cmd} exited ${code}`))));
|
|
1095
|
+
proc.stdin.write(text);
|
|
1096
|
+
proc.stdin.end();
|
|
1097
|
+
});
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
catch (err) {
|
|
1101
|
+
lastErr = err;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
const hint = process.platform === 'linux'
|
|
1105
|
+
? ' Install one: wl-clipboard (Wayland) or xclip / xsel (X11).'
|
|
1106
|
+
: '';
|
|
1107
|
+
throw new Error(`no clipboard tool available (${lastErr?.message ?? 'none found'}).${hint}`);
|
|
1108
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -14,7 +14,7 @@ import { DEFAULT_SYSTEM_REPO, systemRepoSlug } from '../lib/types.js';
|
|
|
14
14
|
import { getAgentsDir, getVersionsDir, ensureAgentsDir } from '../lib/state.js';
|
|
15
15
|
import { isGitRepo, cloneIntoExisting, pullRepo } from '../lib/git.js';
|
|
16
16
|
import { isPromptCancelled, isInteractiveTerminal } from './utils.js';
|
|
17
|
-
import { AGENTS, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
|
|
17
|
+
import { AGENTS, agentConfigDirName, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
|
|
18
18
|
import { setGlobalDefault } from '../lib/versions.js';
|
|
19
19
|
import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPath, getPathSetupInstructions } from '../lib/shims.js';
|
|
20
20
|
import { setHelpSections } from '../lib/help.js';
|
|
@@ -28,7 +28,7 @@ async function importAgent(agentId, version) {
|
|
|
28
28
|
const configDir = agent.configDir;
|
|
29
29
|
const versionsDir = getVersionsDir();
|
|
30
30
|
const versionHome = path.join(versionsDir, agentId, version, 'home');
|
|
31
|
-
const versionConfigDir = path.join(versionHome,
|
|
31
|
+
const versionConfigDir = path.join(versionHome, agentConfigDirName(agentId));
|
|
32
32
|
// Skip if version dir already exists (collision)
|
|
33
33
|
if (fs.existsSync(versionConfigDir)) {
|
|
34
34
|
return { success: false, skipped: true, error: `${version} already installed` };
|
package/dist/commands/teams.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { AgentManager, checkAllClis, getAgentsDir, VALID_TASK_TYPES, } from '../lib/teams/agents.js';
|
|
5
5
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
6
6
|
import { runSupervisor } from '../lib/teams/supervisor.js';
|
|
7
|
-
import { handleSpawn, handleStatus, handleStop, handleTasks, } from '../lib/teams/api.js';
|
|
7
|
+
import { handleSpawn, handleStatus, handleStop, handleTasks, toTaskStatusSummary, } from '../lib/teams/api.js';
|
|
8
8
|
import { createTeam, ensureTeam, getTeam, loadTeams, removeTeam, teamExists, } from '../lib/teams/registry.js';
|
|
9
9
|
import { setHelpSections } from '../lib/help.js';
|
|
10
10
|
import { createWorktree, isGitRepo, hasUncommittedChanges, removeWorktree, } from '../lib/teams/worktree.js';
|
|
@@ -382,6 +382,55 @@ async function resolveTeammateSessions(agents) {
|
|
|
382
382
|
}
|
|
383
383
|
return map;
|
|
384
384
|
}
|
|
385
|
+
// Default compact renderer — one block per teammate, optimized for the
|
|
386
|
+
// orchestrator scanning "what state, what did you touch, what did you say
|
|
387
|
+
// last." Caller passes the projected AgentStatusSummary; for the full
|
|
388
|
+
// verbose layout use printAgentDetail above.
|
|
389
|
+
function printAgentSummary(s) {
|
|
390
|
+
const label = statusColor(s.status)(s.status.toUpperCase());
|
|
391
|
+
const handle = s.name ?? shortId(s.agent_id);
|
|
392
|
+
const ident = s.name ? chalk.gray(`(${shortId(s.agent_id)})`) : '';
|
|
393
|
+
const duration = s.duration ? `${chalk.gray(' · ')}${chalk.white(s.duration)}` : '';
|
|
394
|
+
const errBadge = s.has_errors ? chalk.red(' !') : '';
|
|
395
|
+
const tools = chalk.gray(` · ${s.tool_count} tools`);
|
|
396
|
+
console.log(` ${chalk.cyan(handle.padEnd(14))} ${ident.padEnd(11)} ${label}${duration}${tools}${errBadge}`);
|
|
397
|
+
// Files: counts + basenames. Read is count only.
|
|
398
|
+
const fileLines = [];
|
|
399
|
+
const renderCat = (label, cat) => {
|
|
400
|
+
if (cat.count === 0)
|
|
401
|
+
return;
|
|
402
|
+
const more = cat.count > cat.names.length ? ` +${cat.count - cat.names.length}` : '';
|
|
403
|
+
const names = cat.names.length ? ` ${cat.names.join(', ')}${more}` : '';
|
|
404
|
+
fileLines.push(`${label} ${cat.count}${names}`);
|
|
405
|
+
};
|
|
406
|
+
renderCat('modified', s.files.modified);
|
|
407
|
+
renderCat('created', s.files.created);
|
|
408
|
+
renderCat('deleted', s.files.deleted);
|
|
409
|
+
if (s.files.read.count > 0)
|
|
410
|
+
fileLines.push(`read ${s.files.read.count}`);
|
|
411
|
+
if (fileLines.length) {
|
|
412
|
+
console.log(` ${chalk.gray('files ')} ${fileLines.join(chalk.gray(' · '))}`);
|
|
413
|
+
}
|
|
414
|
+
// Last 3 bash commands.
|
|
415
|
+
const recentBash = s.bash_commands.slice(-3);
|
|
416
|
+
if (recentBash.length) {
|
|
417
|
+
console.log(` ${chalk.gray('bash ')}`);
|
|
418
|
+
for (const cmd of recentBash) {
|
|
419
|
+
console.log(` ${chalk.gray('$')} ${truncate(cmd, 96)}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// Last messages — first non-empty line of each, truncated.
|
|
423
|
+
if (s.last_messages.length) {
|
|
424
|
+
console.log(` ${chalk.gray('messages')}`);
|
|
425
|
+
for (const msg of s.last_messages) {
|
|
426
|
+
const firstLine = msg.split(/\r?\n/).find((l) => l.trim()) || '';
|
|
427
|
+
if (firstLine)
|
|
428
|
+
console.log(` ${chalk.gray('>')} ${truncate(firstLine, 96)}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (s.pr_url)
|
|
432
|
+
console.log(` ${chalk.gray('PR ')} ${chalk.cyan(s.pr_url)}`);
|
|
433
|
+
}
|
|
385
434
|
// Render a team's status in the same format the `status` subcommand uses, so
|
|
386
435
|
// the interactive picker's Enter action drops the user into a familiar view.
|
|
387
436
|
async function printTeamStatus(team, result) {
|
|
@@ -409,6 +458,36 @@ async function printTeamStatus(team, result) {
|
|
|
409
458
|
console.log();
|
|
410
459
|
console.log(chalk.gray(`cursor: ${result.cursor}`));
|
|
411
460
|
}
|
|
461
|
+
// Compact default renderer — no session-file dive, no per-teammate
|
|
462
|
+
// 15-line preview. One block per teammate, suitable for the orchestrator
|
|
463
|
+
// scanning what each agent did. Use `printTeamStatus` (above) for the
|
|
464
|
+
// verbose/legacy layout.
|
|
465
|
+
function printTeamSummary(team, result) {
|
|
466
|
+
const { summary, agents } = result;
|
|
467
|
+
console.log(chalk.bold(`Team ${chalk.cyan(team)} `) +
|
|
468
|
+
chalk.gray(summary.pending > 0
|
|
469
|
+
? `(${summary.pending} pending, ${summary.running} working, ${summary.completed} done, ${summary.failed} failed, ${summary.stopped} stopped)`
|
|
470
|
+
: `(${summary.running} working, ${summary.completed} done, ${summary.failed} failed, ${summary.stopped} stopped)`));
|
|
471
|
+
if (agents.length === 0) {
|
|
472
|
+
console.log(chalk.gray(' (no teammates yet — add one with `agents teams add`)'));
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
const width = Math.min(process.stdout.columns || 80, 80);
|
|
476
|
+
const divider = chalk.gray('┈'.repeat(width));
|
|
477
|
+
for (let i = 0; i < agents.length; i++) {
|
|
478
|
+
console.log();
|
|
479
|
+
if (i > 0) {
|
|
480
|
+
console.log(divider);
|
|
481
|
+
console.log();
|
|
482
|
+
}
|
|
483
|
+
printAgentSummary(agents[i]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
console.log();
|
|
487
|
+
console.log(chalk.gray(`cursor: ${result.cursor}`));
|
|
488
|
+
console.log(chalk.gray('Full detail: agents teams status ' + team + ' --verbose'));
|
|
489
|
+
console.log(chalk.gray('Raw log: agents teams logs --team ' + team + ' --teammate <name>'));
|
|
490
|
+
}
|
|
412
491
|
// Classify a team into a single bucket for --status filtering.
|
|
413
492
|
// - empty: no teammates (created but nobody added yet)
|
|
414
493
|
// - waiting: only staged teammates — call `teams start` to kick them off
|
|
@@ -964,10 +1043,11 @@ export function registerTeamsCommands(program) {
|
|
|
964
1043
|
teams
|
|
965
1044
|
.command('status [team]')
|
|
966
1045
|
.aliases(['s', 'st', 'check'])
|
|
967
|
-
.description("Check in on a team:
|
|
1046
|
+
.description("Check in on a team: status, files touched, recent commands, last messages. Pass --verbose for the full per-teammate dump; --since for delta polling.")
|
|
968
1047
|
.option('-f, --filter <state>', 'Show only teammates in this state: running, completed, failed, stopped, or all (default: all)', 'all')
|
|
969
1048
|
.option('-s, --since <iso>', 'Cursor from a previous status call; only show updates after this timestamp (enables efficient polling)')
|
|
970
1049
|
.option('--agent-id <id>', 'Show only this one teammate (by UUID or UUID prefix)')
|
|
1050
|
+
.option('-v, --verbose', 'Emit the full per-teammate detail (prompt, all file paths, all messages). Default is a compact summary.')
|
|
971
1051
|
.option('--json', 'Output machine-readable JSON')
|
|
972
1052
|
.action(async (team, opts) => {
|
|
973
1053
|
const filter = opts.filter;
|
|
@@ -984,8 +1064,15 @@ export function registerTeamsCommands(program) {
|
|
|
984
1064
|
const agents = opts.agentId
|
|
985
1065
|
? result.agents.filter((a) => a.agent_id.startsWith(opts.agentId))
|
|
986
1066
|
: result.agents;
|
|
1067
|
+
const filtered = { ...result, agents };
|
|
987
1068
|
if (isJsonMode(opts)) {
|
|
988
|
-
|
|
1069
|
+
// JSON output also respects --verbose. Default = compact summary
|
|
1070
|
+
// shape; --verbose = full AgentStatusDetail. Same toggle covers
|
|
1071
|
+
// both text and JSON so MCP-style consumers can opt into detail.
|
|
1072
|
+
const payload = opts.verbose
|
|
1073
|
+
? filtered
|
|
1074
|
+
: toTaskStatusSummary(filtered);
|
|
1075
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
989
1076
|
return;
|
|
990
1077
|
}
|
|
991
1078
|
const exists = await teamExists(team);
|
|
@@ -993,7 +1080,12 @@ export function registerTeamsCommands(program) {
|
|
|
993
1080
|
console.log(chalk.yellow(`No team called '${team}'. Create it with: agents teams create ${team}`));
|
|
994
1081
|
return;
|
|
995
1082
|
}
|
|
996
|
-
|
|
1083
|
+
if (opts.verbose) {
|
|
1084
|
+
await printTeamStatus(team, filtered);
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
printTeamSummary(team, toTaskStatusSummary(filtered));
|
|
1088
|
+
}
|
|
997
1089
|
}
|
|
998
1090
|
catch (err) {
|
|
999
1091
|
die(`Could not check on team ${team}: ${err.message}`);
|
|
@@ -1308,14 +1400,19 @@ export function registerTeamsCommands(program) {
|
|
|
1308
1400
|
teams
|
|
1309
1401
|
.command('logs [teammate]')
|
|
1310
1402
|
.alias('log')
|
|
1311
|
-
.description("Read a teammate's raw log output. Accepts name, UUID, or UUID prefix.")
|
|
1403
|
+
.description("Read a teammate's raw log output. Accepts positional name, --teammate <name>, UUID, or UUID prefix.")
|
|
1312
1404
|
.option('-n, --tail <n>', 'Show only the last N lines instead of the full log')
|
|
1313
1405
|
.option('--team <team>', 'Disambiguate when the same name appears in multiple teams')
|
|
1406
|
+
.option('--teammate <name>', 'Teammate name (alias for the positional arg; useful for scripts)')
|
|
1314
1407
|
.action(async (ref, opts) => {
|
|
1315
1408
|
const base = await getAgentsDir();
|
|
1316
|
-
//
|
|
1409
|
+
// Resolve teammate identity. Precedence:
|
|
1410
|
+
// 1. positional `[teammate]` arg (back-compat, most common)
|
|
1411
|
+
// 2. --teammate <name> flag (script-friendly alias)
|
|
1412
|
+
// 3. interactive picker (TTY only)
|
|
1413
|
+
const teammateRef = ref ?? opts.teammate;
|
|
1317
1414
|
let agentId;
|
|
1318
|
-
if (!
|
|
1415
|
+
if (!teammateRef) {
|
|
1319
1416
|
const mgr = mkManager();
|
|
1320
1417
|
const picked = await pickTeammateOr(mgr, 'agents teams logs');
|
|
1321
1418
|
if (!picked)
|
|
@@ -1323,13 +1420,13 @@ export function registerTeamsCommands(program) {
|
|
|
1323
1420
|
agentId = picked.agentId;
|
|
1324
1421
|
}
|
|
1325
1422
|
else {
|
|
1326
|
-
const resolved = await resolveTeammateAcrossTeams(base,
|
|
1423
|
+
const resolved = await resolveTeammateAcrossTeams(base, teammateRef, opts.team);
|
|
1327
1424
|
if (resolved.kind === 'none') {
|
|
1328
|
-
die(`No notes on record for teammate '${
|
|
1425
|
+
die(`No notes on record for teammate '${teammateRef}'`, 2);
|
|
1329
1426
|
}
|
|
1330
1427
|
if (resolved.kind === 'ambiguous') {
|
|
1331
1428
|
const hints = resolved.candidates.map((c) => `${c.team}/${c.display}`).join(', ');
|
|
1332
|
-
die(`'${
|
|
1429
|
+
die(`'${teammateRef}' matches multiple teammates: ${hints}.\n` +
|
|
1333
1430
|
` Narrow it with --team <team>, or pass a UUID prefix.`, 2);
|
|
1334
1431
|
}
|
|
1335
1432
|
agentId = resolved.agentId;
|
|
@@ -1346,7 +1443,7 @@ export function registerTeamsCommands(program) {
|
|
|
1346
1443
|
process.stdout.write(lines.slice(-n).join('\n'));
|
|
1347
1444
|
}
|
|
1348
1445
|
catch {
|
|
1349
|
-
die(`No notes on record for teammate '${
|
|
1446
|
+
die(`No notes on record for teammate '${teammateRef ?? agentId}' (looked in ${logPath})`, 2);
|
|
1350
1447
|
}
|
|
1351
1448
|
});
|
|
1352
1449
|
// doctor
|
package/dist/commands/view.d.ts
CHANGED
|
@@ -9,6 +9,17 @@
|
|
|
9
9
|
import type { Command } from 'commander';
|
|
10
10
|
import type { AgentId } from '../lib/types.js';
|
|
11
11
|
import { type ProfileSummary } from '../lib/profiles.js';
|
|
12
|
+
/** Per-section filter flags. When any are true, only those sections render. */
|
|
13
|
+
export interface ViewSectionFilter {
|
|
14
|
+
commands?: boolean;
|
|
15
|
+
skills?: boolean;
|
|
16
|
+
mcp?: boolean;
|
|
17
|
+
workflows?: boolean;
|
|
18
|
+
plugins?: boolean;
|
|
19
|
+
rules?: boolean;
|
|
20
|
+
hooks?: boolean;
|
|
21
|
+
promptcuts?: boolean;
|
|
22
|
+
}
|
|
12
23
|
/** Machine-readable entry for a single installed version. */
|
|
13
24
|
export interface ViewJsonVersion {
|
|
14
25
|
version: string;
|
|
@@ -54,6 +65,6 @@ export declare function viewAction(agentArg?: string, options?: {
|
|
|
54
65
|
prune?: boolean;
|
|
55
66
|
yes?: boolean;
|
|
56
67
|
dryRun?: boolean;
|
|
57
|
-
}): Promise<void>;
|
|
68
|
+
} & ViewSectionFilter): Promise<void>;
|
|
58
69
|
/** Register the `agents view` command. */
|
|
59
70
|
export declare function registerViewCommand(program: Command): void;
|