@phnx-labs/agents-cli 1.22.6 → 1.22.8
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 +131 -1
- package/README.md +7 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/browser.js +61 -0
- package/dist/commands/exec.js +83 -16
- package/dist/commands/feed.d.ts +2 -1
- package/dist/commands/feed.js +47 -20
- package/dist/commands/focus.js +22 -1
- package/dist/commands/harness.d.ts +0 -1
- package/dist/commands/harness.js +60 -4
- package/dist/commands/models.js +2 -2
- package/dist/commands/monitors.js +2 -2
- package/dist/commands/projects.js +122 -107
- package/dist/commands/routines.js +2 -2
- package/dist/commands/run-account-picker.js +2 -0
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/sessions-backfill.d.ts +33 -0
- package/dist/commands/sessions-backfill.js +83 -1
- package/dist/commands/sessions-stats.d.ts +36 -0
- package/dist/commands/sessions-stats.js +263 -0
- package/dist/commands/sessions.d.ts +1 -1
- package/dist/commands/sessions.js +21 -1
- package/dist/commands/snapshot.d.ts +11 -0
- package/dist/commands/snapshot.js +107 -0
- package/dist/commands/teams.js +2 -1
- package/dist/commands/view.d.ts +9 -1
- package/dist/commands/view.js +63 -12
- package/dist/index.js +4 -2
- package/dist/lib/activity.js +2 -2
- package/dist/lib/agents.js +68 -11
- package/dist/lib/analytics/recipes.js +11 -5
- package/dist/lib/browser/ipc.js +2 -0
- package/dist/lib/browser/profiles.d.ts +15 -7
- package/dist/lib/browser/profiles.js +53 -12
- package/dist/lib/browser/remote-control.d.ts +35 -0
- package/dist/lib/browser/remote-control.js +48 -0
- package/dist/lib/browser/service.d.ts +19 -0
- package/dist/lib/browser/service.js +19 -1
- package/dist/lib/browser/types.d.ts +14 -2
- package/dist/lib/byok-usage.d.ts +38 -0
- package/dist/lib/byok-usage.js +117 -0
- package/dist/lib/capabilities.js +1 -1
- package/dist/lib/device-config.js +8 -0
- package/dist/lib/exec.d.ts +20 -0
- package/dist/lib/exec.js +73 -8
- package/dist/lib/feed-outcome.d.ts +1 -0
- package/dist/lib/feed-outcome.js +2 -0
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -0
- package/dist/lib/feed-ranking.js +4 -0
- package/dist/lib/feed.d.ts +4 -1
- package/dist/lib/feed.js +25 -0
- package/dist/lib/hosts/passthrough.d.ts +10 -1
- package/dist/lib/hosts/passthrough.js +23 -3
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/mcp.js +6 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/model-tiers.js +4 -1
- package/dist/lib/models.js +63 -0
- package/dist/lib/placement.d.ts +82 -0
- package/dist/lib/placement.js +188 -0
- package/dist/lib/profiles.d.ts +73 -10
- package/dist/lib/profiles.js +133 -15
- package/dist/lib/project-focus.d.ts +9 -0
- package/dist/lib/project-focus.js +23 -0
- package/dist/lib/project-key.d.ts +1 -1
- package/dist/lib/project-key.js +1 -1
- package/dist/lib/project-probe.d.ts +18 -0
- package/dist/lib/project-probe.js +46 -0
- package/dist/lib/project-status.d.ts +56 -0
- package/dist/lib/project-status.js +125 -18
- package/dist/lib/resources/mcp.js +3 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/rotate.d.ts +19 -4
- package/dist/lib/rotate.js +24 -1
- package/dist/lib/routines.d.ts +2 -0
- package/dist/lib/runner.d.ts +3 -0
- package/dist/lib/runner.js +90 -7
- 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/audit.js +1 -1
- package/dist/lib/secrets/index.d.ts +1 -0
- package/dist/lib/secrets/index.js +29 -15
- package/dist/lib/secrets/remote.d.ts +1 -1
- package/dist/lib/secrets/remote.js +1 -1
- package/dist/lib/session/active.d.ts +2 -0
- package/dist/lib/session/bash-command.d.ts +2 -3
- package/dist/lib/session/db.d.ts +92 -1
- package/dist/lib/session/db.js +230 -1
- package/dist/lib/session/digest.d.ts +1 -1
- package/dist/lib/session/digest.js +1 -1
- package/dist/lib/share/publish.js +24 -0
- package/dist/lib/snapshot.d.ts +103 -0
- package/dist/lib/snapshot.js +99 -0
- package/dist/lib/startup/command-registry.d.ts +1 -1
- package/dist/lib/startup/command-registry.js +2 -2
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/types.d.ts +11 -2
- package/dist/lib/usage.d.ts +5 -0
- package/dist/lib/usage.js +3 -3
- package/package.json +1 -1
- package/dist/commands/activity.d.ts +0 -87
- package/dist/commands/activity.js +0 -346
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { discoverSessions, parseTimeFilter } from '../lib/session/discover.js';
|
|
3
|
+
import { queryResourceUsageStats, resourceUsageCoverage, } from '../lib/session/db.js';
|
|
4
|
+
import { listResources } from '../lib/resources.js';
|
|
5
|
+
import { discoverPlugins } from '../lib/plugins.js';
|
|
6
|
+
import { setHelpSections } from '../lib/help.js';
|
|
7
|
+
import { terminalWidth, truncateToWidth, padToWidth, stringWidth } from '../lib/session/width.js';
|
|
8
|
+
const DEFAULT_TOP = 20;
|
|
9
|
+
/**
|
|
10
|
+
* The both-ends "dead weight" set: installed resources whose identity
|
|
11
|
+
* (kind + name — name already embeds `plugin:short`, so it matches the stored
|
|
12
|
+
* invoked name) never appears in the invoked rows. Sorted kind then name.
|
|
13
|
+
* Pure set-difference so it is unit-testable without touching the filesystem.
|
|
14
|
+
*/
|
|
15
|
+
export function diffZeroInvoked(installed, invoked) {
|
|
16
|
+
const invokedKeys = new Set(invoked.map(r => `${r.kind}:${r.name}`));
|
|
17
|
+
return installed
|
|
18
|
+
.filter(r => !invokedKeys.has(`${r.kind}:${r.name}`))
|
|
19
|
+
.sort((a, b) => (a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind.localeCompare(b.kind)));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Enumerate the installed resource set the invoked rows are measured against:
|
|
23
|
+
* flat skills/commands from the DotAgents layers (`listResources`) plus each
|
|
24
|
+
* plugin's own skills/commands as `plugin:short` (invisible to the flat scan) —
|
|
25
|
+
* exactly the union `resolveResourceProvenance` reconciles against at write time.
|
|
26
|
+
*/
|
|
27
|
+
function installedResources(kindFilter, pluginFilter, cwd) {
|
|
28
|
+
const kinds = kindFilter ? [kindFilter] : ['skill', 'command'];
|
|
29
|
+
const byKey = new Map();
|
|
30
|
+
const plugins = discoverPlugins({ cwd });
|
|
31
|
+
for (const kind of kinds) {
|
|
32
|
+
const listKind = kind === 'skill' ? 'skills' : 'commands';
|
|
33
|
+
// Flat (non-namespaced) resources are plugin-less; skip them when the caller
|
|
34
|
+
// narrows to one plugin.
|
|
35
|
+
if (!pluginFilter) {
|
|
36
|
+
for (const r of listResources(listKind, cwd)) {
|
|
37
|
+
byKey.set(`${kind}:${r.name}`, { kind, name: r.name, plugin: null, source: r.source });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const p of plugins) {
|
|
41
|
+
if (pluginFilter && p.name !== pluginFilter)
|
|
42
|
+
continue;
|
|
43
|
+
const names = kind === 'skill' ? p.skills : p.commands;
|
|
44
|
+
for (const short of names) {
|
|
45
|
+
const name = `${p.name}:${short}`;
|
|
46
|
+
byKey.set(`${kind}:${name}`, { kind, name, plugin: p.name, source: p.marketplace ?? null });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [...byKey.values()];
|
|
51
|
+
}
|
|
52
|
+
export function registerSessionsStatsCommand(sessionsCmd) {
|
|
53
|
+
const stats = sessionsCmd
|
|
54
|
+
.command('stats')
|
|
55
|
+
.description('Which skills/commands you actually invoke, and which installed ones are dead weight.')
|
|
56
|
+
.option('--kind <kind>', 'Limit to one kind: skill or command')
|
|
57
|
+
.option('--plugin <name>', "Only this plugin's resources (r.plugin), not every resource a plugin-touching session used")
|
|
58
|
+
.option('-a, --agent <agent>', 'Only sessions from this agent (e.g. claude)')
|
|
59
|
+
.option('-p, --project <name>', 'Only sessions in this project')
|
|
60
|
+
.option('--machine <name>', 'Only sessions recorded on this machine (host)')
|
|
61
|
+
.option('--since <time>', 'Only sessions newer than this (e.g. 7d, 30d, or ISO date)')
|
|
62
|
+
.option('--top <n>', `How many ranked rows to show (default ${DEFAULT_TOP})`)
|
|
63
|
+
.option('--bottom', 'Rank the least-invoked resources first instead of the most-invoked')
|
|
64
|
+
.option('--zero', 'Show only installed-but-never-invoked resources (the dead weight)')
|
|
65
|
+
.option('--json', 'Output the stats as JSON')
|
|
66
|
+
.action(async (_opts, cmd) => {
|
|
67
|
+
await statsAction(cmd);
|
|
68
|
+
});
|
|
69
|
+
setHelpSections(stats, {
|
|
70
|
+
examples: `
|
|
71
|
+
# Most-invoked skills and commands, plus the installed ones you never invoke
|
|
72
|
+
agents sessions stats
|
|
73
|
+
|
|
74
|
+
# Just the dead weight — installed but never explicitly invoked
|
|
75
|
+
agents sessions stats --zero
|
|
76
|
+
|
|
77
|
+
# Skills only, last 30 days, machine-readable
|
|
78
|
+
agents sessions stats --kind skill --since 30d --json
|
|
79
|
+
|
|
80
|
+
# One plugin's resources, least-used first
|
|
81
|
+
agents sessions stats --plugin rush --bottom
|
|
82
|
+
|
|
83
|
+
# Backfill historical sessions first if coverage is low
|
|
84
|
+
agents sessions backfill resources
|
|
85
|
+
`,
|
|
86
|
+
notes: `
|
|
87
|
+
- The signal captures EXPLICIT invocations only: slash commands and \`Skill\` tool calls. An auto-triggered skill (loaded by description match) emits no event, so it reads as 0 — that means "never explicitly invoked", not "never loaded".
|
|
88
|
+
- Skill invocations are recorded for Claude and Kimi; slash-commands for Claude only. Other harnesses contribute nothing to these counts.
|
|
89
|
+
- Counts come from the SQLite index. New/changed sessions are recorded on their normal scan; run \`agents sessions backfill resources\` once to fold in historical sessions (a low coverage line means it hasn't run).
|
|
90
|
+
- --plugin filters the resource rows (this plugin's skills/commands), distinct from the top-level \`agents sessions --plugin\` which filters SESSIONS.
|
|
91
|
+
`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function statsAction(cmd) {
|
|
95
|
+
// The parent `sessions` command owns --agent/--project/--plugin/--since/--json
|
|
96
|
+
// and keeps parsing them past the subcommand name (it has a positional
|
|
97
|
+
// [query]), binding them to the PARENT — so read the merged view, not the
|
|
98
|
+
// action's own options. Same reason sessions-favorite.ts uses optsWithGlobals.
|
|
99
|
+
const opts = cmd.optsWithGlobals();
|
|
100
|
+
const kind = normalizeKind(opts.kind);
|
|
101
|
+
const sinceMs = opts.since ? parseTimeFilter(opts.since) : undefined;
|
|
102
|
+
const top = resolveTop(opts.top);
|
|
103
|
+
// Warm the incremental index (like `agents cost`) so recent sessions' usage is
|
|
104
|
+
// current before we read. Historical gaps still need the explicit backfill.
|
|
105
|
+
await discoverSessions({ all: true, since: opts.since, limit: 1 });
|
|
106
|
+
const filter = {};
|
|
107
|
+
if (opts.agent)
|
|
108
|
+
filter.agent = opts.agent;
|
|
109
|
+
if (opts.project)
|
|
110
|
+
filter.project = opts.project;
|
|
111
|
+
if (opts.machine)
|
|
112
|
+
filter.machine = opts.machine;
|
|
113
|
+
if (typeof sinceMs === 'number')
|
|
114
|
+
filter.sinceMs = sinceMs;
|
|
115
|
+
const statsOpts = {
|
|
116
|
+
...filter,
|
|
117
|
+
kind,
|
|
118
|
+
pluginFilter: opts.plugin,
|
|
119
|
+
order: opts.bottom ? 'bottom' : 'top',
|
|
120
|
+
};
|
|
121
|
+
// Full invoked set (no limit) drives both the zero-invoked diff and the totals;
|
|
122
|
+
// the ranked view is a slice of it.
|
|
123
|
+
const allInvoked = queryResourceUsageStats(statsOpts);
|
|
124
|
+
const ranked = top > 0 ? allInvoked.slice(0, top) : allInvoked;
|
|
125
|
+
const installed = installedResources(kind, opts.plugin, process.cwd());
|
|
126
|
+
const zeroInvoked = diffZeroInvoked(installed, allInvoked);
|
|
127
|
+
const coverage = resourceUsageCoverage();
|
|
128
|
+
const totalInvocations = allInvoked.reduce((s, r) => s + r.invocations, 0);
|
|
129
|
+
if (opts.json) {
|
|
130
|
+
process.stdout.write(JSON.stringify({
|
|
131
|
+
schemaVersion: 1,
|
|
132
|
+
kind: 'sessions-stats',
|
|
133
|
+
generatedAt: new Date().toISOString(),
|
|
134
|
+
filters: {
|
|
135
|
+
kind: kind ?? null,
|
|
136
|
+
plugin: opts.plugin ?? null,
|
|
137
|
+
agent: opts.agent ?? null,
|
|
138
|
+
project: opts.project ?? null,
|
|
139
|
+
machine: opts.machine ?? null,
|
|
140
|
+
since: opts.since ?? null,
|
|
141
|
+
},
|
|
142
|
+
signal: {
|
|
143
|
+
explicitOnly: true,
|
|
144
|
+
note: 'Explicit invocations only (slash commands + Skill tool calls). Auto-triggered skills emit no event and read as 0. Skill invocations are recorded for Claude and Kimi; slash-commands for Claude only.',
|
|
145
|
+
},
|
|
146
|
+
coverage: {
|
|
147
|
+
sessionsWithUsage: coverage.covered,
|
|
148
|
+
sessionsIndexed: coverage.total,
|
|
149
|
+
},
|
|
150
|
+
totals: {
|
|
151
|
+
invokedResources: allInvoked.length,
|
|
152
|
+
invocations: totalInvocations,
|
|
153
|
+
zeroInvoked: zeroInvoked.length,
|
|
154
|
+
},
|
|
155
|
+
order: opts.bottom ? 'bottom' : 'top',
|
|
156
|
+
ranked: ranked.map(serializeRow),
|
|
157
|
+
zeroInvoked: zeroInvoked.map(r => ({
|
|
158
|
+
kind: r.kind,
|
|
159
|
+
name: r.name,
|
|
160
|
+
plugin: r.plugin,
|
|
161
|
+
source: r.source,
|
|
162
|
+
})),
|
|
163
|
+
}, null, 2) + '\n');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
renderHuman({ opts, kind, ranked, zeroInvoked, coverage, totalInvocations, allInvokedCount: allInvoked.length, top });
|
|
167
|
+
}
|
|
168
|
+
function serializeRow(r) {
|
|
169
|
+
return {
|
|
170
|
+
kind: r.kind,
|
|
171
|
+
name: r.name,
|
|
172
|
+
plugin: r.plugin,
|
|
173
|
+
source: r.source,
|
|
174
|
+
sessions: r.sessions,
|
|
175
|
+
invocations: r.invocations,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function normalizeKind(raw) {
|
|
179
|
+
if (raw === undefined)
|
|
180
|
+
return undefined;
|
|
181
|
+
const k = raw.toLowerCase();
|
|
182
|
+
if (k === 'skill' || k === 'skills')
|
|
183
|
+
return 'skill';
|
|
184
|
+
if (k === 'command' || k === 'commands')
|
|
185
|
+
return 'command';
|
|
186
|
+
console.error(chalk.red('error: --kind must be one of: skill, command'));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
function resolveTop(raw) {
|
|
190
|
+
if (raw === undefined)
|
|
191
|
+
return DEFAULT_TOP;
|
|
192
|
+
const n = Number.parseInt(raw, 10);
|
|
193
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
194
|
+
console.error(chalk.red('error: --top must be a non-negative integer'));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
return n;
|
|
198
|
+
}
|
|
199
|
+
function renderHuman(args) {
|
|
200
|
+
const { opts, kind, ranked, zeroInvoked, coverage, totalInvocations, allInvokedCount } = args;
|
|
201
|
+
const out = [];
|
|
202
|
+
const scopeBits = [
|
|
203
|
+
kind ? kind : null,
|
|
204
|
+
opts.plugin ? `plugin ${opts.plugin}` : null,
|
|
205
|
+
opts.agent ? opts.agent : null,
|
|
206
|
+
opts.project ? `project ${opts.project}` : null,
|
|
207
|
+
opts.machine ? opts.machine : null,
|
|
208
|
+
opts.since ? `since ${opts.since}` : null,
|
|
209
|
+
].filter(Boolean);
|
|
210
|
+
out.push(chalk.bold('Resource usage') +
|
|
211
|
+
chalk.gray(` · ${allInvokedCount} invoked · ${totalInvocations} invocation${totalInvocations !== 1 ? 's' : ''}` +
|
|
212
|
+
(scopeBits.length ? ` · ${scopeBits.join(' · ')}` : '')));
|
|
213
|
+
out.push(chalk.gray(` coverage: ${coverage.covered}/${coverage.total} sessions carry the signal`) +
|
|
214
|
+
(coverage.total > 0 && coverage.covered / coverage.total < 0.5
|
|
215
|
+
? chalk.yellow(' — run `agents sessions backfill resources` to fold in history')
|
|
216
|
+
: ''));
|
|
217
|
+
out.push(chalk.gray(' signal: explicit invocations only (slash commands + Skill tool); auto-triggered skills read as 0; skills from Claude+Kimi, slash-commands Claude-only'));
|
|
218
|
+
out.push('');
|
|
219
|
+
// Ranked section (skipped by --zero).
|
|
220
|
+
if (!opts.zero) {
|
|
221
|
+
const heading = opts.bottom ? 'Least invoked' : 'Most invoked';
|
|
222
|
+
out.push(chalk.bold(heading));
|
|
223
|
+
if (ranked.length === 0) {
|
|
224
|
+
out.push(chalk.gray(' (no explicit invocations recorded — run the backfill, or nothing has been invoked yet)'));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
out.push(...renderRankedTable(ranked));
|
|
228
|
+
}
|
|
229
|
+
out.push('');
|
|
230
|
+
}
|
|
231
|
+
// Zero-invoked section (the dead weight).
|
|
232
|
+
out.push(chalk.bold('Installed but never invoked') + chalk.gray(` (${zeroInvoked.length})`));
|
|
233
|
+
if (zeroInvoked.length === 0) {
|
|
234
|
+
out.push(chalk.gray(' (every installed resource has at least one explicit invocation in this window)'));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const cols = terminalWidth();
|
|
238
|
+
for (const r of zeroInvoked) {
|
|
239
|
+
const tag = chalk.gray(`[${r.kind}]`);
|
|
240
|
+
const owner = r.plugin ? chalk.gray(` ${r.plugin}`) : r.source ? chalk.gray(` ${r.source}`) : '';
|
|
241
|
+
const nameW = Math.max(12, cols - 12 - stringWidth(owner));
|
|
242
|
+
out.push(` ${tag} ${truncateToWidth(r.name, nameW)}${owner}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
console.log(out.join('\n'));
|
|
246
|
+
}
|
|
247
|
+
function renderRankedTable(rows) {
|
|
248
|
+
const cols = terminalWidth();
|
|
249
|
+
const invW = Math.max(...rows.map(r => String(r.invocations).length), 3);
|
|
250
|
+
const sessW = Math.max(...rows.map(r => `${r.sessions} sess`.length), 6);
|
|
251
|
+
const lines = [];
|
|
252
|
+
for (const r of rows) {
|
|
253
|
+
const inv = String(r.invocations).padStart(invW);
|
|
254
|
+
const sess = padToWidth(`${r.sessions} sess`, sessW);
|
|
255
|
+
const tag = chalk.gray(`[${r.kind}]`);
|
|
256
|
+
const owner = r.plugin ? chalk.gray(` ${r.plugin}`) : r.source ? chalk.gray(` ${r.source}`) : '';
|
|
257
|
+
const prefix = ` ${chalk.green(inv)} ${chalk.gray(sess)} ${tag} `;
|
|
258
|
+
const suffix = owner;
|
|
259
|
+
const nameW = Math.max(12, cols - stringWidth(prefix) - stringWidth(suffix));
|
|
260
|
+
lines.push(prefix + truncateToWidth(r.name, nameW) + suffix);
|
|
261
|
+
}
|
|
262
|
+
return lines;
|
|
263
|
+
}
|
|
@@ -389,7 +389,7 @@ export declare function flatSessionRow(session: SessionMeta, live?: ActiveSessio
|
|
|
389
389
|
export declare function maybeLiveIndex(options: SessionsOptions): Promise<Map<string, ActiveSession> | undefined>;
|
|
390
390
|
/**
|
|
391
391
|
* Group key for the overview: resolve the cwd through the same canonical
|
|
392
|
-
* resolver the `agents
|
|
392
|
+
* resolver the `agents feed` timeline groups by — a defined project's name
|
|
393
393
|
* when `defs` contains the cwd (multi-repo projects read as one group), else
|
|
394
394
|
* the repo-level key, so a monorepo subdir (`<repo>/apps/cli`) reads as
|
|
395
395
|
* `<repo>` in both views. Falls back to the indexed project name (stamped at
|
|
@@ -59,6 +59,7 @@ import { registerSessionsExportCommand } from './sessions-export.js';
|
|
|
59
59
|
import { registerSessionsImportCommand } from './sessions-import.js';
|
|
60
60
|
import { registerSessionsMigrateCommand, registerSessionsMigrationsCommand } from './sessions-migrate.js';
|
|
61
61
|
import { registerSessionsBackfillCommand } from './sessions-backfill.js';
|
|
62
|
+
import { registerSessionsStatsCommand } from './sessions-stats.js';
|
|
62
63
|
import { runBrowserSessions } from '../lib/browser/sessions-list.js';
|
|
63
64
|
import { countToolProgramOccurrences, parseToolProgramCountClause, readToolIndexCoverage, searchToolCalls, TOOL_QUERY_MAX_CLAUSE_BYTES, TOOL_QUERY_MAX_CLAUSES, TOOL_QUERY_MAX_RESULT_SESSIONS, serializeToolSearchEnvelope, toolSearchRemoteReceiveBudget, } from '../lib/session/tool-index.js';
|
|
64
65
|
const SESSION_AGENT_FILTER_HELP = `Filter by agent, e.g. claude, codex, claude@2.0.65`;
|
|
@@ -2210,7 +2211,7 @@ export async function maybeLiveIndex(options) {
|
|
|
2210
2211
|
}
|
|
2211
2212
|
/**
|
|
2212
2213
|
* Group key for the overview: resolve the cwd through the same canonical
|
|
2213
|
-
* resolver the `agents
|
|
2214
|
+
* resolver the `agents feed` timeline groups by — a defined project's name
|
|
2214
2215
|
* when `defs` contains the cwd (multi-repo projects read as one group), else
|
|
2215
2216
|
* the repo-level key, so a monorepo subdir (`<repo>/apps/cli`) reads as
|
|
2216
2217
|
* `<repo>` in both views. Falls back to the indexed project name (stamped at
|
|
@@ -3643,6 +3644,18 @@ export function registerSessionsCommands(program) {
|
|
|
3643
3644
|
# Show only what's running right now (terminals, teams, cloud, headless)
|
|
3644
3645
|
agents sessions --active
|
|
3645
3646
|
|
|
3647
|
+
# --- Session lifecycle (one verb per intent) ---
|
|
3648
|
+
# Jump to a live session (attach its terminal, or open a tab + resume)
|
|
3649
|
+
agents sessions focus a1b2c3d4
|
|
3650
|
+
# Attach only — never fork a copy (old: sessions go)
|
|
3651
|
+
agents sessions focus a1b2c3d4 --attach-only
|
|
3652
|
+
# Interactive → headless (keep working unattended)
|
|
3653
|
+
agents sessions detach a1b2c3d4
|
|
3654
|
+
# Headless → interactive in this terminal
|
|
3655
|
+
agents sessions attach a1b2c3d4
|
|
3656
|
+
# Multi-select history and open each in a tab
|
|
3657
|
+
agents sessions resume
|
|
3658
|
+
|
|
3646
3659
|
# The interactive list folds in other online machines automatically,
|
|
3647
3660
|
# labelled by host with this machine first. Stay local with --local:
|
|
3648
3661
|
agents sessions --local
|
|
@@ -3683,6 +3696,12 @@ export function registerSessionsCommands(program) {
|
|
|
3683
3696
|
agents sessions --all "deploy script" --host box-a --host box-b
|
|
3684
3697
|
`,
|
|
3685
3698
|
notes: `
|
|
3699
|
+
Session lifecycle (pick one verb — they are not synonyms):
|
|
3700
|
+
focus [id] jump to a live session (attach, or open tab + resume)
|
|
3701
|
+
focus [id] --attach-only attach only; never fork (replaces sessions go)
|
|
3702
|
+
detach <id> interactive → headless continuation
|
|
3703
|
+
attach <id> headless → interactive in this terminal
|
|
3704
|
+
resume [query] multi-select history → open tabs (or run --resume <id>)
|
|
3686
3705
|
- The interactive listing folds in your other online machines automatically (live over SSH, no sync) — each row is labelled by host, this machine first. Use --local to skip the fan-out; --json and single-id lookups stay local.
|
|
3687
3706
|
- --host runs the query on the remote's own index over SSH (host alias or user@host); repeat or pass several to fan out. SSH access is the only auth.
|
|
3688
3707
|
- --in-team matches both ends of the lineage: the session that ran 'agents teams create/add', and (with --teams) that team's teammates. In the interactive list, 't' cycles the same filter over the teams in view.
|
|
@@ -3720,6 +3739,7 @@ export function registerSessionsCommands(program) {
|
|
|
3720
3739
|
registerSessionsMigrateCommand(sessionsCmd);
|
|
3721
3740
|
registerSessionsMigrationsCommand(sessionsCmd);
|
|
3722
3741
|
registerSessionsBackfillCommand(sessionsCmd);
|
|
3742
|
+
registerSessionsStatsCommand(sessionsCmd);
|
|
3723
3743
|
}
|
|
3724
3744
|
function formatNoSessionsMessage(showAll, project) {
|
|
3725
3745
|
const projectQuery = project?.trim();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents snapshot` — one-process consumer snapshot for pollers.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the N× `view --json` + `sessions --active --json` (+ optional feed)
|
|
5
|
+
* fork storm with a single command. Does NOT replace `agents status`, which
|
|
6
|
+
* remains the UnifiedSyncStatus sync contract for menubar / Agency drift.
|
|
7
|
+
*
|
|
8
|
+
* JSON shape: {@link FleetSnapshot} in `lib/snapshot.ts` (version: 1).
|
|
9
|
+
*/
|
|
10
|
+
import type { Command } from 'commander';
|
|
11
|
+
export declare function registerSnapshotCommand(program: Command): void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents snapshot` — one-process consumer snapshot for pollers.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the N× `view --json` + `sessions --active --json` (+ optional feed)
|
|
5
|
+
* fork storm with a single command. Does NOT replace `agents status`, which
|
|
6
|
+
* remains the UnifiedSyncStatus sync contract for menubar / Agency drift.
|
|
7
|
+
*
|
|
8
|
+
* JSON shape: {@link FleetSnapshot} in `lib/snapshot.ts` (version: 1).
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
12
|
+
import { setHelpSections } from '../lib/help.js';
|
|
13
|
+
import { resolveAgentName, AGENTS } from '../lib/agents.js';
|
|
14
|
+
import { computeSnapshot } from '../lib/snapshot.js';
|
|
15
|
+
import { resolveSurface } from './utils.js';
|
|
16
|
+
function resolveAgentFilter(raw) {
|
|
17
|
+
if (!raw)
|
|
18
|
+
return undefined;
|
|
19
|
+
const name = resolveAgentName(raw.split('@')[0]);
|
|
20
|
+
if (!name) {
|
|
21
|
+
console.error(chalk.red(`Unknown agent "${raw}".`));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
/** Compact human summary — the JSON path is the real contract. */
|
|
27
|
+
function renderHuman(snap) {
|
|
28
|
+
console.log(chalk.bold('Fleet snapshot') + chalk.gray(` · ${snap.host} · ${snap.capturedAt}`));
|
|
29
|
+
const invParts = [];
|
|
30
|
+
for (const a of snap.inventory) {
|
|
31
|
+
if (a.versions.length === 0)
|
|
32
|
+
continue;
|
|
33
|
+
const def = a.versions.find((v) => v.isDefault) ?? a.versions[0];
|
|
34
|
+
const label = AGENTS[a.agent]?.name ?? a.agent;
|
|
35
|
+
const signed = def.signedIn ? chalk.green('in') : chalk.gray('out');
|
|
36
|
+
invParts.push(`${label}@${def.version} (${signed})`);
|
|
37
|
+
}
|
|
38
|
+
console.log(` ${'Inventory'.padEnd(12)} ${invParts.length ? invParts.join(chalk.gray(' · ')) : chalk.gray('(none installed)')}`);
|
|
39
|
+
const { running, live, byContext, byAgent } = snap.agents;
|
|
40
|
+
const ctx = Object.entries(byContext)
|
|
41
|
+
.map(([k, n]) => `${k}:${n}`)
|
|
42
|
+
.join(' ');
|
|
43
|
+
const ag = Object.entries(byAgent)
|
|
44
|
+
.map(([k, n]) => `${k}:${n}`)
|
|
45
|
+
.join(' ');
|
|
46
|
+
console.log(` ${'Sessions'.padEnd(12)} ${live} live · ${running} running` +
|
|
47
|
+
(snap.remoteDeviceCount > 0 ? chalk.gray(` · ${snap.remoteDeviceCount} remote device(s)`) : '') +
|
|
48
|
+
(ctx ? chalk.gray(` · ${ctx}`) : '') +
|
|
49
|
+
(ag ? chalk.gray(` · ${ag}`) : ''));
|
|
50
|
+
if (snap.feed) {
|
|
51
|
+
console.log(` ${'Feed'.padEnd(12)} ${snap.feed.openBlocks} open block${snap.feed.openBlocks === 1 ? '' : 's'}`);
|
|
52
|
+
}
|
|
53
|
+
if (snap.sync) {
|
|
54
|
+
const need = snap.sync.agents.filter((a) => a.needsSync).length;
|
|
55
|
+
const sys = snap.sync.system.unknown
|
|
56
|
+
? 'system unknown'
|
|
57
|
+
: snap.sync.system.behind > 0
|
|
58
|
+
? `system ${snap.sync.system.behind} behind`
|
|
59
|
+
: 'system ok';
|
|
60
|
+
console.log(` ${'Sync'.padEnd(12)} ${sys} · ${need} version${need === 1 ? '' : 's'} need sync`);
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.gray('\n Machine-readable: agents snapshot --json\n' +
|
|
63
|
+
' Sync-only (unchanged): agents status --json'));
|
|
64
|
+
}
|
|
65
|
+
export function registerSnapshotCommand(program) {
|
|
66
|
+
const cmd = addHostOption(program
|
|
67
|
+
.command('snapshot')
|
|
68
|
+
.description('One-process poll snapshot: install inventory + active sessions (optional feed/sync). Not the sync-status command — use `agents status` for drift.'))
|
|
69
|
+
.option('--json', 'Emit the machine-readable FleetSnapshot contract (version 1)')
|
|
70
|
+
.option('--all-hosts', 'Include remote devices in the sessions gather (same fan-out as `sessions --active`; default is this machine only)')
|
|
71
|
+
.option('--with-feed', 'Include open feed-block summary (needs-you count + compact rows)')
|
|
72
|
+
.option('--with-sync', 'Include UnifiedSyncStatus (same engine as `agents status`; opt-in)')
|
|
73
|
+
.option('--agent <name>', 'Restrict inventory to one agent (e.g. claude)');
|
|
74
|
+
setHelpSections(cmd, {
|
|
75
|
+
examples: `
|
|
76
|
+
# One-shot inventory + local active sessions (JSON)
|
|
77
|
+
agents snapshot --json
|
|
78
|
+
|
|
79
|
+
# Same, plus open feed blocks for needs-you polls
|
|
80
|
+
agents snapshot --json --with-feed
|
|
81
|
+
|
|
82
|
+
# Fleet-wide active sessions (matches sessions --active scope)
|
|
83
|
+
agents snapshot --json --all-hosts
|
|
84
|
+
|
|
85
|
+
# Inventory for one harness only (Factory-style usage poll)
|
|
86
|
+
agents snapshot --json --agent claude
|
|
87
|
+
|
|
88
|
+
# Run the whole snapshot on another device
|
|
89
|
+
agents snapshot --json --host yosemite-s0
|
|
90
|
+
`,
|
|
91
|
+
});
|
|
92
|
+
cmd.action(async (opts, command) => {
|
|
93
|
+
const surface = resolveSurface(command);
|
|
94
|
+
const agent = resolveAgentFilter(opts.agent);
|
|
95
|
+
const snap = await computeSnapshot({
|
|
96
|
+
agent,
|
|
97
|
+
local: opts.allHosts !== true,
|
|
98
|
+
withFeed: opts.withFeed === true,
|
|
99
|
+
withSync: opts.withSync === true,
|
|
100
|
+
});
|
|
101
|
+
if (surface.json || opts.json) {
|
|
102
|
+
console.log(JSON.stringify(snap, null, 2));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
renderHuman(snap);
|
|
106
|
+
});
|
|
107
|
+
}
|
package/dist/commands/teams.js
CHANGED
|
@@ -275,7 +275,8 @@ function throttleWarningLine(agent, version, r) {
|
|
|
275
275
|
const acct = r.email ? ` (${r.email})` : '';
|
|
276
276
|
const reason = r.reason === 'out_of_credits' ? 'is out of credits'
|
|
277
277
|
: r.reason === 'signed_out' ? 'is not signed in'
|
|
278
|
-
: '
|
|
278
|
+
: r.reason === 'revoked' ? 'needs re-login (its token was revoked)'
|
|
279
|
+
: 'is rate-limited right now';
|
|
279
280
|
return (chalk.yellow(`⚠ ${who}${acct} ${reason}.`) +
|
|
280
281
|
chalk.gray(`\n A pinned version skips account rotation, so it will launch on this account and may immediately hit its limit.` +
|
|
281
282
|
`\n Use a bare \`${agent}\` teammate to let the team pick a healthy account, or pass --force to silence this.`));
|
package/dist/commands/view.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { AccountInfo } from '../lib/agents.js';
|
|
|
12
12
|
import type { AgentId } from '../lib/types.js';
|
|
13
13
|
import { type ConfiguredModelSource } from '../lib/models.js';
|
|
14
14
|
import { type ProfileSummary } from '../lib/profiles.js';
|
|
15
|
+
import { type ByokUsageResult } from '../lib/byok-usage.js';
|
|
15
16
|
/** Shared account identity formatter, re-exported for the view-specific tests. */
|
|
16
17
|
export declare const accountColumnLabel: typeof accountDisplayLabel;
|
|
17
18
|
/**
|
|
@@ -46,7 +47,7 @@ export declare function descriptionForPrefix(desc: string | undefined, prefix: s
|
|
|
46
47
|
* `installedHosts` is the set of agent ids with a usable install; a harness whose
|
|
47
48
|
* host is missing is flagged rather than silently listed as runnable.
|
|
48
49
|
*/
|
|
49
|
-
export declare function renderHarnessBlocks(harnesses: ProfileSummary[], installedHosts: Set<AgentId>, showPaths: boolean): void;
|
|
50
|
+
export declare function renderHarnessBlocks(harnesses: ProfileSummary[], installedHosts: Set<AgentId>, showPaths: boolean, byokMap?: Map<string, ByokUsageResult>): void;
|
|
50
51
|
/** Machine-readable entry for a single installed version. */
|
|
51
52
|
export interface ViewJsonVersion {
|
|
52
53
|
version: string;
|
|
@@ -128,6 +129,13 @@ export declare function parseResourceSections(options: {
|
|
|
128
129
|
resources?: string | boolean;
|
|
129
130
|
detailed?: boolean;
|
|
130
131
|
} & ViewSectionFilter, jsonMode: boolean): Set<ResourceSection>;
|
|
132
|
+
/**
|
|
133
|
+
* Collect structured info for one or more agents without rendering to the
|
|
134
|
+
* terminal. Used by `--json` output and any programmatic consumer (e.g. the
|
|
135
|
+
* agents-cli extension's "resume current session in best available version"
|
|
136
|
+
* command).
|
|
137
|
+
*/
|
|
138
|
+
export declare function collectAgentsJson(filterAgentId?: AgentId, resourceSections?: Set<ResourceSection>): Promise<ViewJsonAgent[]>;
|
|
131
139
|
/**
|
|
132
140
|
* Identity key for duplicate-install detection. Prefers accountKey — which
|
|
133
141
|
* encodes account AND org — over the bare email: two installs can share an
|
package/dist/commands/view.js
CHANGED
|
@@ -27,6 +27,7 @@ import { getConfiguredRunStrategy } from '../lib/rotate.js';
|
|
|
27
27
|
import { resolveRunDefaults } from '../lib/run-defaults.js';
|
|
28
28
|
import { resolveConfiguredModel } from '../lib/models.js';
|
|
29
29
|
import { listProfiles, profileExists, profileSummary, readProfile } from '../lib/profiles.js';
|
|
30
|
+
import { getByokUsageForHarness, hasByokProvider, renderByokBar } from '../lib/byok-usage.js';
|
|
30
31
|
import { renderHarnessDetail } from './harness.js';
|
|
31
32
|
import { loadManifest, isStale } from '../lib/staleness/index.js';
|
|
32
33
|
import { confirm } from '@inquirer/prompts';
|
|
@@ -67,9 +68,28 @@ function getHarnesses(filterAgentId) {
|
|
|
67
68
|
.filter((profile) => !filterAgentId || profile.host.agent === filterAgentId)
|
|
68
69
|
.map(profileSummary);
|
|
69
70
|
}
|
|
70
|
-
/**
|
|
71
|
-
function
|
|
72
|
-
|
|
71
|
+
/** Version-first label: "<version> (forked from <host>[, tracks default])" */
|
|
72
|
+
function harnessVersionLabel(harness, globalDefault) {
|
|
73
|
+
const version = harness.hostVersion ?? globalDefault;
|
|
74
|
+
if (version) {
|
|
75
|
+
const trailer = harness.hostVersion
|
|
76
|
+
? chalk.gray(` (forked from ${harness.agent})`)
|
|
77
|
+
: chalk.gray(` (forked from ${harness.agent}, `) +
|
|
78
|
+
chalk.green(`tracks default`) +
|
|
79
|
+
chalk.gray(`)`);
|
|
80
|
+
return `${version}${trailer}`;
|
|
81
|
+
}
|
|
82
|
+
return chalk.gray(`(forked from ${harness.agent})`);
|
|
83
|
+
}
|
|
84
|
+
/** One-hop or two-hop fork origin label for the harness block header. */
|
|
85
|
+
function buildHarnessOrigin(harness, allHarnesses) {
|
|
86
|
+
if (!harness.forkedFrom || harness.forkedFrom === harness.agent)
|
|
87
|
+
return 'custom';
|
|
88
|
+
const parent = allHarnesses.find((h) => h.name === harness.forkedFrom);
|
|
89
|
+
if (parent?.forkedFrom) {
|
|
90
|
+
return `custom · forked from ${harness.forkedFrom} -> ${parent.forkedFrom}`;
|
|
91
|
+
}
|
|
92
|
+
return `custom · forked from ${harness.forkedFrom}`;
|
|
73
93
|
}
|
|
74
94
|
/**
|
|
75
95
|
* Resolve a resource path to something the IDE can open inline. When `p` is a
|
|
@@ -172,24 +192,23 @@ export function descriptionForPrefix(desc, prefix) {
|
|
|
172
192
|
* `installedHosts` is the set of agent ids with a usable install; a harness whose
|
|
173
193
|
* host is missing is flagged rather than silently listed as runnable.
|
|
174
194
|
*/
|
|
175
|
-
export function renderHarnessBlocks(harnesses, installedHosts, showPaths) {
|
|
195
|
+
export function renderHarnessBlocks(harnesses, installedHosts, showPaths, byokMap) {
|
|
176
196
|
if (harnesses.length === 0)
|
|
177
197
|
return;
|
|
178
198
|
const modelWidth = Math.max(...harnesses.map((h) => h.model.length));
|
|
179
199
|
const authWidth = Math.max(...harnesses.map((h) => h.auth.length));
|
|
180
200
|
for (const harness of harnesses) {
|
|
181
|
-
|
|
182
|
-
// only a fork of another custom harness adds lineage worth printing.
|
|
183
|
-
const origin = harness.forkedFrom && harness.forkedFrom !== harness.agent
|
|
184
|
-
? `custom · forked from ${harness.forkedFrom}`
|
|
185
|
-
: 'custom';
|
|
201
|
+
const origin = buildHarnessOrigin(harness, harnesses);
|
|
186
202
|
const missingHost = installedHosts.has(harness.agent)
|
|
187
203
|
? ''
|
|
188
204
|
: chalk.yellow(` (host ${harness.agent} not installed)`);
|
|
189
205
|
console.log(` ${chalk.bold(harness.label)}${chalk.gray(` (${origin})`)}${missingHost}`);
|
|
206
|
+
const versionTag = harnessVersionLabel(harness, getGlobalDefault(harness.agent));
|
|
207
|
+
const byokEntry = byokMap?.get(harness.name);
|
|
208
|
+
const byokBar = byokEntry ? ` ${renderByokBar(byokEntry)}` : '';
|
|
190
209
|
console.log(` ${chalk.yellow(harness.model.padEnd(modelWidth))} ` +
|
|
191
210
|
`${chalk.cyan(harness.auth.padEnd(authWidth))} ` +
|
|
192
|
-
|
|
211
|
+
versionTag + byokBar);
|
|
193
212
|
if (showPaths)
|
|
194
213
|
console.log(chalk.gray(` ${harness.path}`));
|
|
195
214
|
console.log();
|
|
@@ -616,7 +635,39 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
616
635
|
// Custom harnesses sit in the same list as the native ones — they are run the
|
|
617
636
|
// same way (`agents run <name>`), so they read as their own agent type rather
|
|
618
637
|
// than as an indented row under whichever host CLI executes them.
|
|
619
|
-
|
|
638
|
+
const byokMap = new Map();
|
|
639
|
+
const byKeychainItem = new Map();
|
|
640
|
+
for (const h of harnesses) {
|
|
641
|
+
if (!h.provider || !hasByokProvider(h.provider))
|
|
642
|
+
continue;
|
|
643
|
+
let prof = null;
|
|
644
|
+
try {
|
|
645
|
+
prof = readProfile(h.name);
|
|
646
|
+
}
|
|
647
|
+
catch {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!prof.auth)
|
|
651
|
+
continue;
|
|
652
|
+
const item = prof.auth.keychainItem;
|
|
653
|
+
const existing = byKeychainItem.get(item);
|
|
654
|
+
if (existing) {
|
|
655
|
+
existing.names.push(h.name);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
byKeychainItem.set(item, { profile: prof, names: [h.name] });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (byKeychainItem.size > 0) {
|
|
662
|
+
const fetched = await Promise.all([...byKeychainItem.values()].map(({ profile, names }) => getByokUsageForHarness(profile, { forceRefresh: viewOpts?.forceRefresh }).then((result) => ({ result, names }))));
|
|
663
|
+
for (const { result, names } of fetched) {
|
|
664
|
+
if (!result?.budget)
|
|
665
|
+
continue;
|
|
666
|
+
for (const name of names)
|
|
667
|
+
byokMap.set(name, result);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
renderHarnessBlocks(harnesses, installedHosts, showPaths, byokMap.size > 0 ? byokMap : undefined);
|
|
620
671
|
// Show globally installed (not managed) agents
|
|
621
672
|
if (globallyInstalled.length > 0) {
|
|
622
673
|
console.log(chalk.bold('Not Managed by Agents CLI\n'));
|
|
@@ -1191,7 +1242,7 @@ export function parseResourceSections(options, jsonMode) {
|
|
|
1191
1242
|
* agents-cli extension's "resume current session in best available version"
|
|
1192
1243
|
* command).
|
|
1193
1244
|
*/
|
|
1194
|
-
async function collectAgentsJson(filterAgentId, resourceSections) {
|
|
1245
|
+
export async function collectAgentsJson(filterAgentId, resourceSections) {
|
|
1195
1246
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
1196
1247
|
const wantResources = !!resourceSections && resourceSections.size > 0;
|
|
1197
1248
|
// Only pay for the git-status + resource scans when resources were requested.
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ if (IS_DEV_BUILD) {
|
|
|
94
94
|
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
95
95
|
// first byte of output), the registry maps a command name to a thunk that
|
|
96
96
|
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
97
|
-
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed,
|
|
97
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
98
98
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
99
99
|
import { renderWhatsNew } from './lib/whats-new.js';
|
|
100
100
|
import { getCliLaunch } from './lib/cli-entry.js';
|
|
@@ -317,6 +317,8 @@ Credentials and profiles:
|
|
|
317
317
|
|
|
318
318
|
Diagnostics:
|
|
319
319
|
doctor [agent[@version]] Diagnose CLI availability, sync status, and resource divergence; --check for the CI drift gate
|
|
320
|
+
status Unified sync status (drift/missing) — not the live fleet snapshot
|
|
321
|
+
snapshot One-process poll: inventory + active sessions (+ optional feed/sync)
|
|
320
322
|
usage [agent] Show rate-limit and quota usage per agent
|
|
321
323
|
perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
|
|
322
324
|
|
|
@@ -938,6 +940,7 @@ async function registerAllEagerCommands() {
|
|
|
938
940
|
registerCheckTombstoneCommand(program);
|
|
939
941
|
await reg(loadApply);
|
|
940
942
|
await reg(loadStatus);
|
|
943
|
+
await reg(loadSnapshot);
|
|
941
944
|
registerExecAliasCommand(program);
|
|
942
945
|
await reg(loadProfiles);
|
|
943
946
|
await reg(loadHarness);
|
|
@@ -972,7 +975,6 @@ async function registerAllEagerCommands() {
|
|
|
972
975
|
await reg(loadFunnel);
|
|
973
976
|
registerHqTombstoneCommand(program);
|
|
974
977
|
await reg(loadFeed);
|
|
975
|
-
await reg(loadActivity);
|
|
976
978
|
await reg(loadMailboxes);
|
|
977
979
|
await reg(loadSsh);
|
|
978
980
|
registerJobsCronAliasCommand(program, 'jobs');
|