@phnx-labs/agents-cli 1.22.22 → 1.22.24
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 +402 -0
- package/README.md +13 -6
- package/dist/bin/agents +0 -0
- package/dist/commands/attach.d.ts +2 -0
- package/dist/commands/attach.js +32 -7
- package/dist/commands/defaults.js +2 -0
- package/dist/commands/doctor.js +20 -7
- package/dist/commands/exec.js +100 -34
- package/dist/commands/feed.d.ts +18 -0
- package/dist/commands/feed.js +44 -1
- package/dist/commands/focus.d.ts +108 -5
- package/dist/commands/focus.js +455 -32
- package/dist/commands/fork.d.ts +20 -2
- package/dist/commands/fork.js +91 -64
- package/dist/commands/go.d.ts +39 -1
- package/dist/commands/go.js +112 -7
- package/dist/commands/harness-wizard.d.ts +206 -0
- package/dist/commands/harness-wizard.js +403 -0
- package/dist/commands/harness.d.ts +12 -0
- package/dist/commands/harness.js +97 -101
- package/dist/commands/insights.js +6 -2
- package/dist/commands/inspect.js +39 -5
- package/dist/commands/menubar.js +6 -1
- package/dist/commands/models.js +1 -0
- package/dist/commands/modes.d.ts +12 -0
- package/dist/commands/modes.js +147 -0
- package/dist/commands/resume.js +10 -3
- package/dist/commands/secrets-sync.js +11 -13
- package/dist/commands/secrets.d.ts +2 -0
- package/dist/commands/secrets.js +79 -47
- package/dist/commands/sessions-browser.d.ts +35 -0
- package/dist/commands/sessions-browser.js +140 -14
- package/dist/commands/sessions-resume.d.ts +23 -3
- package/dist/commands/sessions-resume.js +73 -19
- package/dist/commands/sessions.d.ts +91 -5
- package/dist/commands/sessions.js +359 -125
- package/dist/commands/setup-secrets.js +1 -1
- package/dist/commands/sync.js +246 -42
- package/dist/commands/view.js +2 -0
- package/dist/commands/watchdog.js +13 -2
- package/dist/index.js +2 -1
- package/dist/lib/agent-modes.d.ts +49 -0
- package/dist/lib/agent-modes.js +70 -0
- package/dist/lib/agents.d.ts +1 -1
- package/dist/lib/agents.js +132 -0
- package/dist/lib/codex-policy.d.ts +17 -0
- package/dist/lib/codex-policy.js +48 -0
- package/dist/lib/crabbox/lease.d.ts +25 -0
- package/dist/lib/crabbox/lease.js +62 -0
- package/dist/lib/daemon.js +70 -0
- package/dist/lib/doctor-diff.d.ts +3 -0
- package/dist/lib/doctor-diff.js +15 -13
- package/dist/lib/event-stream.d.ts +3 -1
- package/dist/lib/event-stream.js +14 -1
- package/dist/lib/exec.d.ts +16 -0
- package/dist/lib/exec.js +175 -62
- package/dist/lib/feed-broadcast.d.ts +1 -20
- package/dist/lib/feed-broadcast.js +31 -1
- package/dist/lib/hooks/cache.js +36 -3
- package/dist/lib/hooks.d.ts +19 -7
- package/dist/lib/hooks.js +100 -40
- package/dist/lib/hosts/session-index.d.ts +4 -0
- package/dist/lib/hosts/session-index.js +7 -0
- package/dist/lib/manifest.d.ts +12 -2
- package/dist/lib/manifest.js +60 -5
- package/dist/lib/mcp.js +44 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +52 -2
- package/dist/lib/menubar/install-menubar.js +128 -6
- package/dist/lib/models.d.ts +0 -5
- package/dist/lib/models.js +48 -0
- package/dist/lib/plugin-marketplace.js +9 -0
- package/dist/lib/pricing/prices.json +119 -92
- package/dist/lib/pricing/table.js +13 -0
- package/dist/lib/refresh.d.ts +5 -0
- package/dist/lib/refresh.js +37 -33
- package/dist/lib/remote-agents-json.d.ts +29 -1
- package/dist/lib/remote-agents-json.js +47 -10
- package/dist/lib/resource-inventory.d.ts +79 -0
- package/dist/lib/resource-inventory.js +122 -0
- package/dist/lib/resources/mcp.js +2 -0
- package/dist/lib/resources/permissions.js +3 -0
- package/dist/lib/resources/types.d.ts +2 -1
- package/dist/lib/resources.js +8 -5
- package/dist/lib/run-defaults.d.ts +2 -0
- package/dist/lib/run-defaults.js +23 -2
- package/dist/lib/runner.js +50 -36
- 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/bundles.js +157 -65
- package/dist/lib/secrets/filestore.d.ts +5 -3
- package/dist/lib/secrets/filestore.js +12 -8
- package/dist/lib/secrets/index.d.ts +15 -1
- package/dist/lib/secrets/index.js +148 -53
- package/dist/lib/secrets/reaper.d.ts +97 -0
- package/dist/lib/secrets/reaper.js +219 -0
- package/dist/lib/secrets/remote.d.ts +29 -0
- package/dist/lib/secrets/remote.js +37 -1
- package/dist/lib/secrets/sync-passphrase.d.ts +27 -0
- package/dist/lib/secrets/sync-passphrase.js +78 -0
- package/dist/lib/session/active.d.ts +36 -1
- package/dist/lib/session/active.js +60 -19
- package/dist/lib/session/actor-sidecar.d.ts +14 -0
- package/dist/lib/session/actor-sidecar.js +67 -4
- package/dist/lib/session/db.d.ts +1 -1
- package/dist/lib/session/db.js +32 -1
- package/dist/lib/session/discover.js +168 -0
- package/dist/lib/session/parse.d.ts +10 -0
- package/dist/lib/session/parse.js +98 -0
- package/dist/lib/session/recovery.d.ts +37 -0
- package/dist/lib/session/recovery.js +95 -0
- package/dist/lib/session/remote-list.d.ts +10 -1
- package/dist/lib/session/remote-list.js +2 -8
- package/dist/lib/session/remote.d.ts +57 -3
- package/dist/lib/session/remote.js +90 -26
- package/dist/lib/session/resume-command.d.ts +6 -0
- package/dist/lib/session/resume-command.js +8 -0
- package/dist/lib/session/session-cache.d.ts +173 -0
- package/dist/lib/session/session-cache.js +399 -0
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/session/width.d.ts +1 -1
- package/dist/lib/session/width.js +12 -2
- package/dist/lib/shims.d.ts +2 -2
- package/dist/lib/shims.js +62 -8
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/sync-umbrella.d.ts +5 -0
- package/dist/lib/sync-umbrella.js +5 -4
- package/dist/lib/tmux/session.d.ts +4 -2
- package/dist/lib/tmux/session.js +5 -5
- package/dist/lib/types.d.ts +3 -1
- package/dist/lib/usage.d.ts +13 -0
- package/dist/lib/usage.js +215 -0
- package/dist/lib/versions.js +13 -2
- package/package.json +1 -1
package/dist/commands/harness.js
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { addProfile, ensureProviderToken, applyFromSecrets } from './profiles.js';
|
|
15
15
|
import { isInteractiveTerminal } from './utils.js';
|
|
16
|
-
import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists, profileHostLabel, profileProviderLabel, profileModelLabel, profileAuthLabel, profileLabel, forkProfile, editProfile, renameProfile, profileFromHostModel, authEnvKeyForHost, getProfilePath, validateProfileName, } from '../lib/profiles.js';
|
|
16
|
+
import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists, profileHostLabel, profileProviderLabel, profileModelLabel, profileAuthLabel, profileLabel, forkProfile, editProfile, renameProfile, profileFromHostModel, authEnvKeyForHost, modelEnvKeyForHost, baseUrlEnvKeyForHost, getProfilePath, validateProfileName, } from '../lib/profiles.js';
|
|
17
17
|
import { listPresets, getPreset } from '../lib/profiles-presets.js';
|
|
18
|
-
import { listBundles } from '../lib/secrets/bundles.js';
|
|
19
18
|
import { AGENTS, ALL_AGENT_IDS, resolveAgentName } from '../lib/agents.js';
|
|
19
|
+
import { runWizardSteps, createSteps, editSteps, defaultWizardIO, } from './harness-wizard.js';
|
|
20
20
|
/** Short capability summary for a native harness — its supported run modes. */
|
|
21
21
|
function nativeModes(id) {
|
|
22
22
|
const modes = AGENTS[id]?.capabilities?.modes ?? [];
|
|
@@ -193,108 +193,93 @@ async function runForkFlow(source, name, opts) {
|
|
|
193
193
|
console.log(chalk.green(`Harness '${name}' forked from ${source}.`));
|
|
194
194
|
console.log(chalk.gray(`Try: agents run ${name} "hello"`));
|
|
195
195
|
}
|
|
196
|
-
/** The first `_MODEL`-suffixed env var in a preset's static env block, if any. */
|
|
197
|
-
function presetModel(preset) {
|
|
198
|
-
return Object.entries(preset.env).find(([k]) => k.endsWith('_MODEL'))?.[1];
|
|
199
|
-
}
|
|
200
196
|
/**
|
|
201
197
|
* Interactive `agents harness add`/`fork` wizard — runs when required info is
|
|
202
|
-
* missing and stdout
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* build an
|
|
198
|
+
* missing and stdin+stdout are a TTY (see {@link forkNeedsWizard}, {@link addNeedsWizard}).
|
|
199
|
+
* Drives the shared step engine ({@link createSteps}) and maps its finished draft
|
|
200
|
+
* back to the same `(source, name, opts)` shape {@link buildFork} accepts via
|
|
201
|
+
* {@link runForkFlow}, so a wizard run and a hand-written fork call build an
|
|
202
|
+
* identical profile.
|
|
206
203
|
*/
|
|
207
|
-
async function
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
baseUrl =
|
|
204
|
+
async function runCreateWizard() {
|
|
205
|
+
const io = await defaultWizardIO();
|
|
206
|
+
const draft = await runWizardSteps(createSteps(), { mode: 'create' }, io);
|
|
207
|
+
return {
|
|
208
|
+
source: draft.source,
|
|
209
|
+
name: draft.name,
|
|
210
|
+
opts: {
|
|
211
|
+
model: draft.model,
|
|
212
|
+
baseUrl: draft.baseUrl,
|
|
213
|
+
authProvider: draft.authProvider,
|
|
214
|
+
fromSecrets: draft.fromSecrets,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Map a finished edit-wizard draft onto {@link EditOptions}, keeping only the
|
|
220
|
+
* fields the user actually changed from the profile's current values. Unchanged
|
|
221
|
+
* accepts (the wizard pre-fills each prompt with the current value) drop out, so
|
|
222
|
+
* the resulting {@link buildEdit} touches nothing the user left alone — and the
|
|
223
|
+
* "no changes" case is detectable via {@link hasEditFlags}. Base-URL clearing is
|
|
224
|
+
* intentionally not expressed here: the flag path can't clear it either (an empty
|
|
225
|
+
* `--base-url` is a no-op in `forkProfile`), so the wizard matches that until a
|
|
226
|
+
* later subtask adds explicit clearing.
|
|
227
|
+
*/
|
|
228
|
+
export function draftToEditOptions(draft, original) {
|
|
229
|
+
const host = original.host.agent;
|
|
230
|
+
const curModel = original.env[modelEnvKeyForHost(host)];
|
|
231
|
+
const baseKey = baseUrlEnvKeyForHost(host);
|
|
232
|
+
const curBaseUrl = baseKey ? original.env[baseKey] : undefined;
|
|
233
|
+
const curVersion = original.host.version ?? '';
|
|
234
|
+
const curFallback = original.fallback_model ?? '';
|
|
235
|
+
const curDescription = original.description ?? '';
|
|
236
|
+
const opts = {};
|
|
237
|
+
if (draft.model !== undefined && draft.model !== curModel)
|
|
238
|
+
opts.model = draft.model;
|
|
239
|
+
if (draft.baseUrl && draft.baseUrl !== curBaseUrl)
|
|
240
|
+
opts.baseUrl = draft.baseUrl;
|
|
241
|
+
if (draft.authProvider !== undefined)
|
|
242
|
+
opts.authProvider = draft.authProvider;
|
|
243
|
+
if (draft.fromSecrets !== undefined)
|
|
244
|
+
opts.fromSecrets = draft.fromSecrets;
|
|
245
|
+
if (draft.version !== undefined && draft.version !== curVersion)
|
|
246
|
+
opts.version = draft.version;
|
|
247
|
+
if (draft.fallbackModel !== undefined && draft.fallbackModel !== curFallback)
|
|
248
|
+
opts.fallbackModel = draft.fallbackModel;
|
|
249
|
+
if (draft.description !== undefined && draft.description !== curDescription)
|
|
250
|
+
opts.description = draft.description;
|
|
251
|
+
return opts;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Interactive `agents harness edit <name>` wizard — runs when no edit flags were
|
|
255
|
+
* given and stdin+stdout are a TTY. Loads the profile, drives the shared step
|
|
256
|
+
* engine ({@link editSteps}) pre-filled with current values, then persists via the
|
|
257
|
+
* same build+write path as the flag-driven edit. `--key-stdin` is honored for the
|
|
258
|
+
* auth step's key entry. When the user changes nothing, it says so and writes
|
|
259
|
+
* nothing.
|
|
260
|
+
*/
|
|
261
|
+
async function runEditWizard(name, cliOpts) {
|
|
262
|
+
if (!profileExists(name)) {
|
|
263
|
+
throw new Error(`Harness '${name}' not found. Create it first: agents harness add ${name} ...`);
|
|
244
264
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
defaultName = preset.name;
|
|
265
|
+
const original = readProfile(name);
|
|
266
|
+
const io = await defaultWizardIO();
|
|
267
|
+
const draft = await runWizardSteps(editSteps(original), { mode: 'edit', original, host: original.host.agent, name }, io);
|
|
268
|
+
const opts = { ...draftToEditOptions(draft, original), keyStdin: cliOpts.keyStdin };
|
|
269
|
+
if (!hasEditFlags(opts)) {
|
|
270
|
+
console.log(chalk.gray(`No changes made to '${name}'.`));
|
|
271
|
+
return;
|
|
253
272
|
}
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
catch (err) {
|
|
263
|
-
return err.message;
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
const opts = { model, baseUrl, authProvider };
|
|
268
|
-
if (authProvider) {
|
|
269
|
-
const bundles = listBundles();
|
|
270
|
-
const TYPE_NOW = 'type';
|
|
271
|
-
const FROM_SECRETS = 'secrets';
|
|
272
|
-
const keySource = bundles.length > 0
|
|
273
|
-
? await select({
|
|
274
|
-
message: `How should '${authProvider}' get its key?`,
|
|
275
|
-
choices: [
|
|
276
|
-
{ name: 'Type a key now', value: TYPE_NOW },
|
|
277
|
-
{ name: 'Use an existing agents secrets bundle', value: FROM_SECRETS },
|
|
278
|
-
],
|
|
279
|
-
})
|
|
280
|
-
: TYPE_NOW;
|
|
281
|
-
if (keySource === FROM_SECRETS) {
|
|
282
|
-
const bundleName = await select({
|
|
283
|
-
message: 'Bundle',
|
|
284
|
-
choices: bundles.map((b) => ({
|
|
285
|
-
name: b.description ? `${b.name} ${chalk.gray(b.description)}` : b.name,
|
|
286
|
-
value: b.name,
|
|
287
|
-
})),
|
|
288
|
-
});
|
|
289
|
-
const bundle = bundles.find((b) => b.name === bundleName);
|
|
290
|
-
const keys = Object.keys(bundle.vars);
|
|
291
|
-
const key = keys.length === 1
|
|
292
|
-
? keys[0]
|
|
293
|
-
: await select({ message: 'Key', choices: keys.map((k) => ({ name: k, value: k })) });
|
|
294
|
-
opts.fromSecrets = `${bundleName}:${key}`;
|
|
295
|
-
}
|
|
273
|
+
const edited = buildEdit(name, opts);
|
|
274
|
+
if (opts.fromSecrets) {
|
|
275
|
+
await applyFromSecrets(edited, opts.fromSecrets, opts.authProvider);
|
|
276
|
+
}
|
|
277
|
+
else if (opts.authProvider) {
|
|
278
|
+
await ensureProviderToken(opts.authProvider, undefined, opts.keyStdin);
|
|
296
279
|
}
|
|
297
|
-
|
|
280
|
+
writeProfile(edited);
|
|
281
|
+
console.log(chalk.green(`Harness '${name}' updated.`));
|
|
282
|
+
console.log(chalk.gray(`Model: ${profileModelLabel(edited)}`));
|
|
298
283
|
}
|
|
299
284
|
export function registerHarnessCommands(program) {
|
|
300
285
|
const cmd = program
|
|
@@ -354,7 +339,7 @@ Examples:
|
|
|
354
339
|
if (!isInteractiveTerminal()) {
|
|
355
340
|
throw new Error("'agents harness add' needs --preset or --host + --model (or a name and an interactive terminal for the wizard).");
|
|
356
341
|
}
|
|
357
|
-
const wiz = await
|
|
342
|
+
const wiz = await runCreateWizard();
|
|
358
343
|
await runForkFlow(wiz.source, wiz.name, { ...wiz.opts, force: opts.force, keyStdin: opts.keyStdin });
|
|
359
344
|
return;
|
|
360
345
|
}
|
|
@@ -399,7 +384,7 @@ Examples:
|
|
|
399
384
|
if (!isInteractiveTerminal()) {
|
|
400
385
|
throw new Error("'agents harness fork' needs <source> and <name> (or an interactive terminal for the wizard).");
|
|
401
386
|
}
|
|
402
|
-
const wiz = await
|
|
387
|
+
const wiz = await runCreateWizard();
|
|
403
388
|
await runForkFlow(wiz.source, wiz.name, { ...wiz.opts, force: opts.force, keyStdin: opts.keyStdin });
|
|
404
389
|
return;
|
|
405
390
|
}
|
|
@@ -412,7 +397,7 @@ Examples:
|
|
|
412
397
|
});
|
|
413
398
|
cmd
|
|
414
399
|
.command('edit <name>')
|
|
415
|
-
.description('Edit an existing custom harness in place — model, endpoint, auth, version, description, fallback.')
|
|
400
|
+
.description('Edit an existing custom harness in place — model, endpoint, auth, version, description, fallback. Omit flags in a terminal for the interactive wizard.')
|
|
416
401
|
.option('--model <id>', 'Swap the pinned model')
|
|
417
402
|
.option('--base-url <url>', 'Swap the custom endpoint base URL')
|
|
418
403
|
.option('--auth-provider <provider>', 'Repoint auth at a different provider (keychain-backed)')
|
|
@@ -437,9 +422,20 @@ Examples:
|
|
|
437
422
|
|
|
438
423
|
# Copy a key out of an existing secrets bundle instead of typing it
|
|
439
424
|
agents harness edit corp --from-secrets prod:OPENROUTER_KEY
|
|
425
|
+
|
|
426
|
+
# No flags, in an interactive terminal: a wizard walks each field pre-filled
|
|
427
|
+
agents harness edit deepseek
|
|
440
428
|
`)
|
|
441
429
|
.action(async (name, opts) => {
|
|
442
430
|
try {
|
|
431
|
+
// No edit flags + a real terminal → the interactive wizard, pre-filled
|
|
432
|
+
// with current values. Any flag (or a non-interactive caller) takes the
|
|
433
|
+
// flag path unchanged; a flagless non-interactive call still errors via
|
|
434
|
+
// buildEdit's EDIT_FLAGS_HELP.
|
|
435
|
+
if (!hasEditFlags(opts) && isInteractiveTerminal()) {
|
|
436
|
+
await runEditWizard(name, opts);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
443
439
|
const edited = buildEdit(name, opts);
|
|
444
440
|
if (opts.fromSecrets) {
|
|
445
441
|
await applyFromSecrets(edited, opts.fromSecrets, opts.authProvider);
|
|
@@ -340,7 +340,10 @@ async function insightsAction(options) {
|
|
|
340
340
|
console.error(chalk.red('error: --min-messages must be a non-negative integer'));
|
|
341
341
|
process.exit(1);
|
|
342
342
|
}
|
|
343
|
-
|
|
343
|
+
// `--all` is the spelling people reach for; `--since all` is what the window parser
|
|
344
|
+
// speaks. Accept both rather than failing on an unknown option, and let an explicit
|
|
345
|
+
// --since win so `--all --since 7d` is not silently contradictory.
|
|
346
|
+
const since = options.since ?? (options.all ? 'all' : '30d');
|
|
344
347
|
const sinceMs = since === 'all' ? undefined : parseTimeFilter(since);
|
|
345
348
|
// Refresh the index first, exactly as `agents cost` does, so a report never silently
|
|
346
349
|
// describes a stale picture of disk.
|
|
@@ -436,6 +439,7 @@ export function registerInsightsCommand(program) {
|
|
|
436
439
|
.description('How you work — tools, friction, and rhythm, split by the account that did the work')
|
|
437
440
|
.option('--json', 'Output the full report as JSON')
|
|
438
441
|
.option('--since <time>', 'Window: 7d, 4w, 3mo, an ISO date, or "all" (default 30d)')
|
|
442
|
+
.option('--all', 'Every session ever indexed. Alias for --since all')
|
|
439
443
|
.option('--by <dimension>', 'Group by: account (default), agent, project, or day')
|
|
440
444
|
.option('--account <match>', 'Only sessions whose account key, email, or org contains this')
|
|
441
445
|
.option('--agent <id>', 'Only one harness (claude, codex, droid, …)')
|
|
@@ -454,7 +458,7 @@ export function registerInsightsCommand(program) {
|
|
|
454
458
|
agents insights --by project --since 90d
|
|
455
459
|
|
|
456
460
|
# One account only, all of its history
|
|
457
|
-
agents insights --account "Turing Labs" --
|
|
461
|
+
agents insights --account "Turing Labs" --all
|
|
458
462
|
|
|
459
463
|
# Machine-readable, for a dashboard or a slash command
|
|
460
464
|
agents insights --json
|
package/dist/commands/inspect.js
CHANGED
|
@@ -22,12 +22,14 @@ import * as yaml from 'yaml';
|
|
|
22
22
|
import { AGENTS, getCliState, resolveAgentName } from '../lib/agents.js';
|
|
23
23
|
import { supports } from '../lib/capabilities.js';
|
|
24
24
|
import { resolveConfiguredModel } from '../lib/models.js';
|
|
25
|
+
import { getAgentModesCatalog } from '../lib/agent-modes.js';
|
|
25
26
|
import { resolveSingleAgentTarget, AgentSpecError } from '../lib/agent-spec/index.js';
|
|
26
27
|
import { readMeta, getUserAgentsDir, getSystemAgentsDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../lib/state.js';
|
|
27
28
|
import { getVersionHomePath, isVersionIsolated, getIsolatedDefault, } from '../lib/versions.js';
|
|
28
29
|
import { getShimsDir, getVersionedAliasPath } from '../lib/shims.js';
|
|
29
30
|
import { getAgentResources, listResources, } from '../lib/resources.js';
|
|
30
31
|
import { listHookEntriesFromDir } from '../lib/hooks.js';
|
|
32
|
+
import { getResourceInventory } from '../lib/resource-inventory.js';
|
|
31
33
|
import { listMcpServerConfigs, discoverMcpConfigsFromRepo } from '../lib/mcp.js';
|
|
32
34
|
import { discoverPlugins, discoverPluginsInDir, pluginResourceGroups } from '../lib/plugins.js';
|
|
33
35
|
import { PLUGIN_GROUP_COLORS } from './plugins.js';
|
|
@@ -113,7 +115,7 @@ function truncateValueForPrefix(prefix, value) {
|
|
|
113
115
|
// ─── Command registration ────────────────────────────────────────────────────
|
|
114
116
|
export function registerInspectCommand(program) {
|
|
115
117
|
const cmd = addHostOption(program.command('inspect <target>'))
|
|
116
|
-
.description('Inspect one installed agent at one version, or a DotAgents repo
|
|
118
|
+
.description('Inspect one installed agent harness at one version (not a model), or a DotAgents repo — paths, capabilities, resources, and hook capable/on-disk/wired state.')
|
|
117
119
|
.option('--brief', 'header + capabilities only; skip resources/sessions')
|
|
118
120
|
.option('--json', 'machine-readable JSON output');
|
|
119
121
|
for (const kind of DRILLABLE_KINDS) {
|
|
@@ -575,12 +577,14 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
575
577
|
const isIsolatedDefault = isolated && getIsolatedDefault(agent) === version;
|
|
576
578
|
const capabilities = collectCapabilities(agent, version);
|
|
577
579
|
const itemsByKind = options.brief ? null : collectItemsByKind(agent, versionHome);
|
|
580
|
+
const hookInventory = options.brief ? null : getResourceInventory(agent, version, 'hooks');
|
|
578
581
|
const hookByScript = options.brief ? null : hookManifestByScript(loadCentralHookManifest());
|
|
579
582
|
const mcpConfigs = options.brief ? null : new Map(listMcpServerConfigs().map(s => [s.name, s.config]));
|
|
580
583
|
const sessions = options.brief ? null : {
|
|
581
584
|
total: safeCountSessions(agent),
|
|
582
585
|
};
|
|
583
586
|
if (options.json) {
|
|
587
|
+
const modeCat = getAgentModesCatalog(agent, version);
|
|
584
588
|
const json = {
|
|
585
589
|
agent,
|
|
586
590
|
version,
|
|
@@ -594,7 +598,14 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
594
598
|
strategy,
|
|
595
599
|
installedShim: cliState?.installed === true ? cliState.path : null,
|
|
596
600
|
capabilities,
|
|
597
|
-
|
|
601
|
+
modes: {
|
|
602
|
+
supported: modeCat.modes.map((m) => m.mode),
|
|
603
|
+
defaultMode: modeCat.defaultMode,
|
|
604
|
+
configuredMode: modeCat.configuredMode,
|
|
605
|
+
headlessPlan: modeCat.headlessPlan,
|
|
606
|
+
unsupported: modeCat.unsupported,
|
|
607
|
+
},
|
|
608
|
+
resources: itemsByKind ? summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs, hookInventory) : null,
|
|
598
609
|
sessions,
|
|
599
610
|
};
|
|
600
611
|
console.log(JSON.stringify(json, null, 2));
|
|
@@ -623,12 +634,23 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
623
634
|
const reason = res.ok ? '' : chalk.gray(`(${res.reason}${res.need ? ' ' + res.need : ''})`);
|
|
624
635
|
console.log(` ${cap.padEnd(10)} ${mark} ${reason}`);
|
|
625
636
|
}
|
|
637
|
+
// Permission modes are a separate axis from the capability booleans above —
|
|
638
|
+
// same data as `agents modes <agent>`, kept next to the rest of the inspect
|
|
639
|
+
// surface so a human/agent does not have to leave this view.
|
|
640
|
+
{
|
|
641
|
+
const modeCat = getAgentModesCatalog(agent, version);
|
|
642
|
+
const modeList = modeCat.modes.map((m) => (m.isDefault ? `${m.mode}*` : m.mode)).join('/');
|
|
643
|
+
const cfg = modeCat.configuredMode ? chalk.gray(` run default: ${modeCat.configuredMode}`) : '';
|
|
644
|
+
console.log(` ${'modes'.padEnd(10)} ${chalk.cyan(modeList)}${cfg}`);
|
|
645
|
+
console.log(chalk.gray(` agents modes ${agent} · agents models ${agent}@${version}`));
|
|
646
|
+
}
|
|
626
647
|
if (itemsByKind) {
|
|
627
648
|
console.log('\n' + chalk.bold('Resources'));
|
|
628
649
|
for (const kind of SIMPLE_KINDS) {
|
|
629
650
|
printSimpleResourceRow(kind, itemsByKind[kind]);
|
|
630
651
|
}
|
|
631
|
-
|
|
652
|
+
console.log(` ${chalk.bold('Hooks')} ${chalk.gray(formatHookInventoryCounts(hookInventory))}`);
|
|
653
|
+
printExpandedSection('Hook files', hookRows(itemsByKind.hooks, hookByScript));
|
|
632
654
|
printExpandedSection('Plugins', pluginRows(itemsByKind.plugins));
|
|
633
655
|
printExpandedSection('MCP', mcpRows(itemsByKind.mcp, mcpConfigs));
|
|
634
656
|
}
|
|
@@ -769,13 +791,20 @@ function printSimpleResourceRow(kind, items) {
|
|
|
769
791
|
* simple kinds add `names`, and the rich kinds add structured `items` (hook
|
|
770
792
|
* events/predicates, mcp transport/url/command, plugin version + group counts).
|
|
771
793
|
*/
|
|
772
|
-
function summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs) {
|
|
794
|
+
function summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs, hookInventory) {
|
|
773
795
|
const out = {};
|
|
774
796
|
for (const kind of DRILLABLE_KINDS) {
|
|
775
797
|
const items = itemsByKind[kind];
|
|
776
798
|
const base = { total: items.length, bySource: countBySource(items.map(i => i.source)) };
|
|
777
799
|
if (kind === 'hooks') {
|
|
778
|
-
out[kind] = { ...base,
|
|
800
|
+
out[kind] = { ...base,
|
|
801
|
+
capable: hookInventory.capable,
|
|
802
|
+
declared: hookInventory.declared,
|
|
803
|
+
onDisk: hookInventory.onDisk,
|
|
804
|
+
wired: hookInventory.wired,
|
|
805
|
+
unmanaged: hookInventory.unmanaged,
|
|
806
|
+
wiringSupported: hookInventory.wiringSupported,
|
|
807
|
+
items: items.map(i => {
|
|
779
808
|
const h = hookByScript.get(i.name);
|
|
780
809
|
return { name: i.name, source: i.source, events: h?.events ?? [], matcher: h?.matcher, matches: h?.matches, cache: h?.cache };
|
|
781
810
|
}) };
|
|
@@ -800,6 +829,11 @@ function summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs) {
|
|
|
800
829
|
}
|
|
801
830
|
return out;
|
|
802
831
|
}
|
|
832
|
+
function formatHookInventoryCounts(inventory) {
|
|
833
|
+
const wired = inventory.wiringSupported ? String(inventory.wired.length) : 'unknown';
|
|
834
|
+
const unmanaged = inventory.unmanaged.length > 0 ? ` · unmanaged ${inventory.unmanaged.length}` : '';
|
|
835
|
+
return `capable ${inventory.capable ? 'yes' : 'no'} · on-disk ${inventory.onDisk.length} · wired ${wired}${unmanaged}`;
|
|
836
|
+
}
|
|
803
837
|
function collectKind(agent, versionHome, kind) {
|
|
804
838
|
switch (kind) {
|
|
805
839
|
case 'commands':
|
package/dist/commands/menubar.js
CHANGED
|
@@ -51,7 +51,12 @@ function printStatus(s, opts = {}) {
|
|
|
51
51
|
console.log(chalk.gray(' End them with `agents menubar setup`.'));
|
|
52
52
|
}
|
|
53
53
|
if (s.stale) {
|
|
54
|
-
|
|
54
|
+
// Not "runs on next startup": the self-heal only reinstalls from the install
|
|
55
|
+
// that owns the helper, or from another one once the takeover cooldown has
|
|
56
|
+
// passed (mayInstallMenubarHelper) — so on a box with several agents-cli
|
|
57
|
+
// copies this can persist for a while. `setup` bypasses the gate and is the
|
|
58
|
+
// immediate fix.
|
|
59
|
+
console.log(chalk.yellow('\n Installed AGI Menu is stale — `agents menubar setup` updates it now.'));
|
|
55
60
|
}
|
|
56
61
|
else if (!s.serviceInstalled && !s.disabledByUser) {
|
|
57
62
|
console.log(chalk.gray('\n Set it up with `agents menubar setup`.'));
|
package/dist/commands/models.js
CHANGED
|
@@ -72,6 +72,7 @@ export function registerModelsCommand(program) {
|
|
|
72
72
|
if (!agentSpec && !all) {
|
|
73
73
|
console.log(chalk.gray('\n `agents models <agent>` for one harness · `--all` for the full model list'));
|
|
74
74
|
console.log(chalk.gray(' `agents models tier set <agent> <tier> <model>` to override a tier'));
|
|
75
|
+
console.log(chalk.gray(' Permission modes (--mode plan|edit|auto|skip): `agents modes <agent>`'));
|
|
75
76
|
}
|
|
76
77
|
});
|
|
77
78
|
// Override subcommands. These WRITE agents.yaml so the user never hand-edits it;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents modes` — list the permission modes a harness accepts for
|
|
3
|
+
* `agents run` / `agents teams add`.
|
|
4
|
+
*
|
|
5
|
+
* Mirror of `agents models`: humans and orchestrating agents read this before
|
|
6
|
+
* picking `--mode plan|edit|auto|skip`. Modes are per-agent today (not version-
|
|
7
|
+
* gated); `agent@version` is accepted so the configured run.defaults mode for
|
|
8
|
+
* that version can be shown beside the catalog.
|
|
9
|
+
*/
|
|
10
|
+
import type { Command } from 'commander';
|
|
11
|
+
/** Register `agents modes [agent[@version]]`. */
|
|
12
|
+
export declare function registerModesCommand(program: Command): void;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents modes` — list the permission modes a harness accepts for
|
|
3
|
+
* `agents run` / `agents teams add`.
|
|
4
|
+
*
|
|
5
|
+
* Mirror of `agents models`: humans and orchestrating agents read this before
|
|
6
|
+
* picking `--mode plan|edit|auto|skip`. Modes are per-agent today (not version-
|
|
7
|
+
* gated); `agent@version` is accepted so the configured run.defaults mode for
|
|
8
|
+
* that version can be shown beside the catalog.
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { AGENTS, ALL_AGENT_IDS, agentLabel, formatAgentError, resolveAgentName, } from '../lib/agents.js';
|
|
12
|
+
import { formatModeFlags, getAgentModesCatalog, } from '../lib/agent-modes.js';
|
|
13
|
+
import { setHelpSections } from '../lib/help.js';
|
|
14
|
+
import { getGlobalDefault, listInstalledVersions, resolveVersion, resolveVersionAlias } from '../lib/versions.js';
|
|
15
|
+
/** Agents that show in the no-arg overview (skip hard-deprecated). */
|
|
16
|
+
const MODE_AGENTS = ALL_AGENT_IDS.filter((id) => !AGENTS[id].deprecated?.hard);
|
|
17
|
+
/** Register `agents modes [agent[@version]]`. */
|
|
18
|
+
export function registerModesCommand(program) {
|
|
19
|
+
const modes = program
|
|
20
|
+
.command('modes [agentSpec]')
|
|
21
|
+
.description('Show which permission modes (--mode plan|edit|auto|skip) a harness supports for agents run / teams add')
|
|
22
|
+
.option('--json', 'Output machine-readable JSON')
|
|
23
|
+
.action((agentSpec, options) => {
|
|
24
|
+
const targets = resolveTargets(agentSpec);
|
|
25
|
+
if (targets.length === 0)
|
|
26
|
+
process.exit(1);
|
|
27
|
+
if (options.json) {
|
|
28
|
+
const out = targets.map(({ agent, version }) => {
|
|
29
|
+
const catalog = getAgentModesCatalog(agent, version);
|
|
30
|
+
return {
|
|
31
|
+
agent,
|
|
32
|
+
version,
|
|
33
|
+
defaultMode: catalog.defaultMode,
|
|
34
|
+
configuredMode: catalog.configuredMode,
|
|
35
|
+
configuredModeSource: catalog.configuredModeSource,
|
|
36
|
+
headlessPlan: catalog.headlessPlan,
|
|
37
|
+
modes: catalog.modes.map((m) => ({
|
|
38
|
+
mode: m.mode,
|
|
39
|
+
flags: m.flags,
|
|
40
|
+
description: m.description,
|
|
41
|
+
isDefault: m.isDefault,
|
|
42
|
+
})),
|
|
43
|
+
unsupported: catalog.unsupported,
|
|
44
|
+
notes: catalog.notes,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
console.log(JSON.stringify(out, null, 2));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
let printed = 0;
|
|
51
|
+
for (const { agent, version, isDefault } of targets) {
|
|
52
|
+
if (printed > 0)
|
|
53
|
+
console.log();
|
|
54
|
+
printCatalog(getAgentModesCatalog(agent, version), version, isDefault);
|
|
55
|
+
printed++;
|
|
56
|
+
}
|
|
57
|
+
if (!agentSpec) {
|
|
58
|
+
console.log(chalk.gray('\n `agents modes <agent>` for one harness · `agents modes claude@2.1.219` for a version\'s run default'));
|
|
59
|
+
console.log(chalk.gray(' Pass --mode on `agents run` / `agents teams add`. Models: `agents models <agent>`.'));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
setHelpSections(modes, {
|
|
63
|
+
examples: `
|
|
64
|
+
agents modes
|
|
65
|
+
agents modes claude
|
|
66
|
+
agents modes claude@2.1.219
|
|
67
|
+
agents modes cursor --json
|
|
68
|
+
`,
|
|
69
|
+
notes: `
|
|
70
|
+
Permission modes control how much the agent can do (not which LLM model
|
|
71
|
+
runs — that is --model / agents models). Supported modes come from each
|
|
72
|
+
harness's capabilities table; unsupported requests degrade (auto→edit,
|
|
73
|
+
plan→safest native) or error (skip when missing).
|
|
74
|
+
'full' is a silent alias for skip.
|
|
75
|
+
agent@version is accepted so any configured run.defaults mode for that
|
|
76
|
+
version is shown; the mode list itself is per-agent today.
|
|
77
|
+
`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function resolveTargets(agentSpec) {
|
|
81
|
+
if (!agentSpec) {
|
|
82
|
+
const targets = [];
|
|
83
|
+
for (const agent of MODE_AGENTS) {
|
|
84
|
+
const version = getGlobalDefault(agent) || listInstalledVersions(agent)[0] || null;
|
|
85
|
+
targets.push({ agent, version, isDefault: true });
|
|
86
|
+
}
|
|
87
|
+
return targets;
|
|
88
|
+
}
|
|
89
|
+
const [agentName, versionSpec] = agentSpec.split('@');
|
|
90
|
+
const agent = resolveAgentName(agentName);
|
|
91
|
+
if (!agent) {
|
|
92
|
+
console.error(chalk.red(formatAgentError(agentName, MODE_AGENTS)));
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
if (AGENTS[agent].deprecated?.hard) {
|
|
96
|
+
console.error(chalk.yellow(`${agent} is deprecated and no longer supports run modes.`));
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
if (versionSpec === 'all') {
|
|
100
|
+
const installed = listInstalledVersions(agent);
|
|
101
|
+
if (installed.length === 0) {
|
|
102
|
+
return [{ agent, version: null, isDefault: true }];
|
|
103
|
+
}
|
|
104
|
+
return installed.map((v) => ({
|
|
105
|
+
agent,
|
|
106
|
+
version: v,
|
|
107
|
+
isDefault: v === getGlobalDefault(agent),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
if (versionSpec) {
|
|
111
|
+
// resolveVersionAlias exits process if the concrete version is not installed.
|
|
112
|
+
const version = resolveVersionAlias(agent, versionSpec) ?? null;
|
|
113
|
+
return [{ agent, version, isDefault: version === getGlobalDefault(agent) }];
|
|
114
|
+
}
|
|
115
|
+
const version = resolveVersion(agent, process.cwd()) || getGlobalDefault(agent) || null;
|
|
116
|
+
return [{ agent, version, isDefault: true }];
|
|
117
|
+
}
|
|
118
|
+
function printCatalog(catalog, version, isDefault) {
|
|
119
|
+
const tag = version
|
|
120
|
+
? ` ${chalk.bold(version)}${isDefault ? chalk.gray(' (default)') : ''}`
|
|
121
|
+
: chalk.gray(' (not installed)');
|
|
122
|
+
console.log(`${agentLabel(catalog.agent)}${tag}`);
|
|
123
|
+
console.log(chalk.gray(' modes:'));
|
|
124
|
+
for (const entry of catalog.modes) {
|
|
125
|
+
const star = entry.isDefault ? chalk.cyan('*') : ' ';
|
|
126
|
+
const flags = chalk.gray(formatModeFlags(entry.flags));
|
|
127
|
+
console.log(` ${star} ${chalk.cyan(entry.mode.padEnd(6))} ${chalk.bold(entry.description)} ${flags}`);
|
|
128
|
+
}
|
|
129
|
+
if (catalog.unsupported.length > 0) {
|
|
130
|
+
console.log(chalk.gray(` unsupported: ${catalog.unsupported.join(', ')}`));
|
|
131
|
+
}
|
|
132
|
+
const defaultBits = [`native default ${chalk.white(catalog.defaultMode)}`];
|
|
133
|
+
if (catalog.configuredMode) {
|
|
134
|
+
const src = catalog.configuredModeSource ? chalk.gray(` (${catalog.configuredModeSource})`) : '';
|
|
135
|
+
defaultBits.push(`run default ${chalk.white(catalog.configuredMode)}${src}`);
|
|
136
|
+
}
|
|
137
|
+
console.log(chalk.gray(` ${defaultBits.join(' · ')}`));
|
|
138
|
+
if (!catalog.headlessPlan && catalog.modes.some((m) => m.mode === 'plan')) {
|
|
139
|
+
console.log(chalk.yellow(' headless plan: not supported (interactive plan still works)'));
|
|
140
|
+
}
|
|
141
|
+
for (const note of catalog.notes) {
|
|
142
|
+
// Keep the full-alias note quiet; surface degrades more visibly.
|
|
143
|
+
if (note.startsWith("'full'"))
|
|
144
|
+
continue;
|
|
145
|
+
console.log(chalk.gray(` note: ${note}`));
|
|
146
|
+
}
|
|
147
|
+
}
|
package/dist/commands/resume.js
CHANGED
|
@@ -22,8 +22,8 @@ export function buildResumeRunArgs(session, prompt, options) {
|
|
|
22
22
|
}
|
|
23
23
|
export function registerResumeCommand(program) {
|
|
24
24
|
const cmd = program
|
|
25
|
-
.command('resume <session
|
|
26
|
-
.description('Resume a session with its original harness, version, device, account, cwd, and mode')
|
|
25
|
+
.command('resume <session> [prompt]')
|
|
26
|
+
.description('Resume a session by id, tmux alias, or exact label with its original harness, version, device, account, cwd, and mode. Searches the fleet automatically; a local full-id hit resumes with zero SSH.')
|
|
27
27
|
.option('-m, --mode <mode>', 'Override the recorded launch mode')
|
|
28
28
|
.option('-i, --interactive', 'Resume interactively even when a prompt is provided')
|
|
29
29
|
.option('--headless', 'Resume headlessly (a prompt is required)')
|
|
@@ -71,10 +71,17 @@ export function registerResumeCommand(program) {
|
|
|
71
71
|
# Resume and continue headlessly
|
|
72
72
|
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 "finish the tests"
|
|
73
73
|
|
|
74
|
+
# Resume by exact label (auto-resumes the one match)
|
|
75
|
+
agents resume "fix the flaky ssh test"
|
|
76
|
+
|
|
77
|
+
# Resume by durable tmux alias or its unique suffix
|
|
78
|
+
agents resume ag-codex-c1f3d813
|
|
79
|
+
agents resume c1f3d813
|
|
80
|
+
|
|
74
81
|
# Deliberately change permissions
|
|
75
82
|
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 --mode edit`,
|
|
76
83
|
notes: `
|
|
77
|
-
|
|
84
|
+
A full ID resolves from the local session database first (zero SSH) and, on a local miss, fans out with the first peer holding it cancelling the rest. An exact label always consults the fleet (labels are not globally unique) and auto-resumes the one match; a cross-machine label collision surfaces as an ambiguity.
|
|
78
85
|
Use agents run auto --resume <id> when the original account is unavailable and another harness may continue.`,
|
|
79
86
|
});
|
|
80
87
|
}
|