@phnx-labs/agents-cli 1.22.24 → 1.22.26
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 +350 -1
- package/README.md +31 -6
- package/dist/bin/agents +0 -0
- package/dist/browser.js +14 -4
- package/dist/commands/apply.js +52 -8
- package/dist/commands/browser.js +35 -0
- package/dist/commands/cloud.js +9 -5
- package/dist/commands/doctor.d.ts +24 -0
- package/dist/commands/doctor.js +108 -9
- package/dist/commands/exec.js +19 -16
- package/dist/commands/feed.d.ts +5 -0
- package/dist/commands/feed.js +21 -1
- package/dist/commands/focus.js +2 -2
- package/dist/commands/insights.d.ts +25 -19
- package/dist/commands/insights.js +107 -33
- package/dist/commands/menubar.js +8 -0
- package/dist/commands/reconnect.d.ts +46 -0
- package/dist/commands/reconnect.js +109 -0
- package/dist/commands/routines.d.ts +3 -0
- package/dist/commands/routines.js +72 -49
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets.d.ts +2 -8
- package/dist/commands/secrets.js +29 -105
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +27 -7
- package/dist/commands/sessions-resume.js +3 -2
- package/dist/commands/sessions.d.ts +13 -1
- package/dist/commands/sessions.js +28 -2
- package/dist/commands/setup-secrets.d.ts +1 -0
- package/dist/commands/setup-secrets.js +1 -1
- package/dist/commands/setup-watchdog.js +5 -10
- package/dist/commands/setup.d.ts +26 -3
- package/dist/commands/setup.js +106 -47
- package/dist/commands/teams.d.ts +6 -0
- package/dist/commands/teams.js +46 -2
- package/dist/commands/trends.d.ts +8 -0
- package/dist/commands/trends.js +10 -156
- package/dist/commands/watchdog.d.ts +3 -4
- package/dist/commands/watchdog.js +26 -66
- package/dist/index.js +37 -2
- package/dist/lib/agents.d.ts +11 -0
- package/dist/lib/agents.js +155 -23
- package/dist/lib/analytics/dashboard.d.ts +10 -6
- package/dist/lib/analytics/dashboard.js +6 -4
- package/dist/lib/analytics/mix-commands.d.ts +53 -0
- package/dist/lib/analytics/mix-commands.js +229 -0
- package/dist/lib/analytics/recipes.d.ts +19 -14
- package/dist/lib/analytics/recipes.js +4 -2
- package/dist/lib/browser/ipc.d.ts +26 -0
- package/dist/lib/browser/ipc.js +139 -24
- package/dist/lib/browser/profiles.d.ts +11 -0
- package/dist/lib/browser/profiles.js +1 -1
- package/dist/lib/browser/stream.d.ts +14 -0
- package/dist/lib/browser/stream.js +71 -0
- package/dist/lib/channels/owner-sink.d.ts +27 -0
- package/dist/lib/channels/owner-sink.js +93 -0
- package/dist/lib/cloud/cursor.d.ts +79 -0
- package/dist/lib/cloud/cursor.js +228 -0
- package/dist/lib/cloud/registry.js +2 -0
- package/dist/lib/cloud/types.d.ts +7 -2
- package/dist/lib/cloud/types.js +14 -0
- package/dist/lib/crabbox/cli.d.ts +2 -2
- package/dist/lib/crabbox/config.d.ts +7 -8
- package/dist/lib/crabbox/config.js +14 -14
- package/dist/lib/crabbox/lease.d.ts +11 -4
- package/dist/lib/crabbox/lease.js +40 -8
- package/dist/lib/crabbox/setup-copy.d.ts +5 -0
- package/dist/lib/crabbox/setup-copy.js +17 -1
- package/dist/lib/daemon.js +27 -1
- package/dist/lib/device-config.js +7 -0
- package/dist/lib/devices/doctor-findings.d.ts +13 -1
- package/dist/lib/devices/doctor-findings.js +73 -2
- package/dist/lib/events.d.ts +9 -0
- package/dist/lib/events.js +58 -0
- package/dist/lib/exec.d.ts +3 -3
- package/dist/lib/exec.js +24 -10
- package/dist/lib/feed-outcome.d.ts +3 -0
- package/dist/lib/feed-outcome.js +18 -10
- package/dist/lib/feed.d.ts +4 -0
- package/dist/lib/fleet/apply.d.ts +59 -3
- package/dist/lib/fleet/apply.js +183 -6
- package/dist/lib/fleet/types.d.ts +21 -2
- package/dist/lib/hooks/cache.js +15 -0
- package/dist/lib/hosts/passthrough.d.ts +44 -0
- package/dist/lib/hosts/passthrough.js +84 -12
- package/dist/lib/hosts/ready.d.ts +2 -0
- package/dist/lib/hosts/ready.js +10 -1
- package/dist/lib/hosts/reconnect.d.ts +14 -12
- package/dist/lib/hosts/reconnect.js +41 -40
- package/dist/lib/mcp.js +5 -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/menubar/snapshot.d.ts +15 -0
- package/dist/lib/menubar/snapshot.js +40 -0
- package/dist/lib/plugins.js +13 -1
- package/dist/lib/resources/mcp.js +3 -0
- package/dist/lib/routine-process-cleanup.d.ts +9 -0
- package/dist/lib/routine-process-cleanup.js +73 -0
- package/dist/lib/routines.js +14 -2
- package/dist/lib/runner.d.ts +0 -3
- package/dist/lib/runner.js +6 -19
- 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 +1 -20
- package/dist/lib/secrets/filestore.d.ts +2 -0
- package/dist/lib/secrets/filestore.js +13 -0
- package/dist/lib/secrets/push.d.ts +94 -0
- package/dist/lib/secrets/push.js +145 -0
- package/dist/lib/secrets/rc-hygiene.d.ts +14 -0
- package/dist/lib/secrets/rc-hygiene.js +14 -1
- package/dist/lib/secrets/reaper.d.ts +15 -1
- package/dist/lib/secrets/reaper.js +30 -3
- package/dist/lib/session/active.d.ts +4 -0
- package/dist/lib/session/db.d.ts +21 -3
- package/dist/lib/session/db.js +229 -15
- package/dist/lib/session/discover.d.ts +1 -0
- package/dist/lib/session/discover.js +115 -19
- package/dist/lib/session/insights.d.ts +18 -0
- package/dist/lib/session/insights.js +143 -1
- package/dist/lib/session/remote-list.d.ts +2 -0
- package/dist/lib/session/remote-list.js +1 -0
- package/dist/lib/session/session-cache.d.ts +4 -4
- package/dist/lib/session/session-cache.js +4 -4
- package/dist/lib/session/tool-index.js +133 -22
- package/dist/lib/session/tool-store.d.ts +26 -2
- package/dist/lib/session/tool-store.js +36 -17
- package/dist/lib/shims.js +21 -1
- package/dist/lib/signin-badge.js +2 -0
- package/dist/lib/ssh-exec.js +8 -2
- package/dist/lib/startup/command-registry.d.ts +16 -0
- package/dist/lib/startup/command-registry.js +46 -0
- package/dist/lib/teams/agents.d.ts +13 -0
- package/dist/lib/teams/agents.js +76 -8
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/placement-probe.d.ts +21 -0
- package/dist/lib/teams/placement-probe.js +135 -0
- package/dist/lib/teams/scheduler.d.ts +74 -1
- package/dist/lib/teams/scheduler.js +187 -10
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/versions.js +16 -1
- package/dist/lib/watchdog/service.d.ts +17 -0
- package/dist/lib/watchdog/service.js +39 -0
- package/package.json +1 -1
package/dist/commands/focus.js
CHANGED
|
@@ -16,7 +16,7 @@ import fs from 'node:fs';
|
|
|
16
16
|
import chalk from 'chalk';
|
|
17
17
|
import { confirm } from '@inquirer/prompts';
|
|
18
18
|
import { gatherLiveTargets, pickLiveTarget, pickLiveTargets, jumpTo, probeAttachRail, refuseFallback } from './go.js';
|
|
19
|
-
import { buildSessionRecoveryCommand, filterSessionsByQuery, formatLiveStatusHeadline, formatPickerLabel, pickerColumnsFor, resumeSessionInPlace, resolveSessionMetadataValue, resolveSessionAgentName, requestedLiveStatuses, } from './sessions.js';
|
|
19
|
+
import { buildSessionRecoveryCommand, filterSessionsByQuery, formatLiveStatusHeadline, formatPickerLabel, isRunningLiveSession, pickerColumnsFor, resumeSessionInPlace, resolveSessionMetadataValue, resolveSessionAgentName, requestedLiveStatuses, } from './sessions.js';
|
|
20
20
|
import { resolveBackend, CONFIRM_THRESHOLD } from './sessions-resume.js';
|
|
21
21
|
import { runOnPeer } from '../lib/session/remote-list.js';
|
|
22
22
|
import { discoverSessions } from '../lib/session/discover.js';
|
|
@@ -276,7 +276,7 @@ export async function focusAction(id, opts) {
|
|
|
276
276
|
}
|
|
277
277
|
/** A retained pane is not attachable merely because tmux can still display it. */
|
|
278
278
|
export function isAttachableLiveSession(session) {
|
|
279
|
-
return session
|
|
279
|
+
return isRunningLiveSession(session);
|
|
280
280
|
}
|
|
281
281
|
const FOCUS_AGENT_SHORTHANDS = ['claude', 'codex', 'kimi', 'antigravity', 'grok', 'opencode'];
|
|
282
282
|
function focusOptionAgent(opts) {
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Insights command —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* agents
|
|
9
|
-
*
|
|
10
|
-
* agents
|
|
11
|
-
* agents
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
2
|
+
* Insights command — one observe verb for "how work looks".
|
|
3
|
+
*
|
|
4
|
+
* Two data paths under one name (do not re-split into peer top-level commands):
|
|
5
|
+
*
|
|
6
|
+
* agents insights HOW you work (transcript content: tools, friction,
|
|
7
|
+
* rhythm, edits) — split by Claude account by default
|
|
8
|
+
* agents insights mix COUNTERS (sessions index + usage.db recipes:
|
|
9
|
+
* harness/model mix, token ratios, secrets, browser)
|
|
10
|
+
* agents insights <recipe> One baked mix recipe (harness-mix, tools-per-session, …)
|
|
11
|
+
* agents insights query Raw usage.db rows
|
|
12
|
+
*
|
|
13
|
+
* Sibling observe verbs (stay separate — different questions):
|
|
14
|
+
*
|
|
15
|
+
* agents cost what you spent ($ and duration)
|
|
16
|
+
* agents output what shipped (burn vs PRs and commits)
|
|
17
|
+
* agents usage live quota headroom
|
|
18
|
+
* agents perf latency (hooks, CLI commands, agent.run) — not popularity
|
|
19
|
+
* agents sessions stats which skills/slash-commands were explicitly invoked
|
|
20
|
+
*
|
|
21
|
+
* Why mix lives here (not a second top-level `trends`): two abstract "analytics"
|
|
22
|
+
* nouns taught agents and humans to guess. One verb, two engines — cheap SQL mix
|
|
23
|
+
* vs transcript facets. Latency stays on `perf` so it is never confused with mix.
|
|
21
24
|
*
|
|
22
25
|
* Modelled on Claude Code's `/insights`, with the difference that motivated it: that
|
|
23
26
|
* command reads one account's directory, while `balanced` rotation sprays sessions
|
|
@@ -27,6 +30,9 @@
|
|
|
27
30
|
* The deterministic report makes zero network calls. `--narrative` is opt-in and adds
|
|
28
31
|
* the coaching prose by piping the AGGREGATE (never raw transcripts) through a headless
|
|
29
32
|
* `claude -p`.
|
|
33
|
+
*
|
|
34
|
+
* `agents trends` is a thin deprecated alias of the mix tree only (see commands/trends.ts).
|
|
30
35
|
*/
|
|
31
36
|
import type { Command } from 'commander';
|
|
32
37
|
export declare function registerInsightsCommand(program: Command): void;
|
|
38
|
+
export declare function registerSessionsInsightsCommand(sessions: Command): void;
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Insights command —
|
|
2
|
+
* Insights command — one observe verb for "how work looks".
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* any of them:
|
|
4
|
+
* Two data paths under one name (do not re-split into peer top-level commands):
|
|
6
5
|
*
|
|
7
|
-
* agents
|
|
8
|
-
*
|
|
9
|
-
* agents
|
|
10
|
-
*
|
|
11
|
-
* agents
|
|
12
|
-
* agents insights
|
|
6
|
+
* agents insights HOW you work (transcript content: tools, friction,
|
|
7
|
+
* rhythm, edits) — split by Claude account by default
|
|
8
|
+
* agents insights mix COUNTERS (sessions index + usage.db recipes:
|
|
9
|
+
* harness/model mix, token ratios, secrets, browser)
|
|
10
|
+
* agents insights <recipe> One baked mix recipe (harness-mix, tools-per-session, …)
|
|
11
|
+
* agents insights query Raw usage.db rows
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
13
|
+
* Sibling observe verbs (stay separate — different questions):
|
|
14
|
+
*
|
|
15
|
+
* agents cost what you spent ($ and duration)
|
|
16
|
+
* agents output what shipped (burn vs PRs and commits)
|
|
17
|
+
* agents usage live quota headroom
|
|
18
|
+
* agents perf latency (hooks, CLI commands, agent.run) — not popularity
|
|
19
|
+
* agents sessions stats which skills/slash-commands were explicitly invoked
|
|
20
|
+
*
|
|
21
|
+
* Why mix lives here (not a second top-level `trends`): two abstract "analytics"
|
|
22
|
+
* nouns taught agents and humans to guess. One verb, two engines — cheap SQL mix
|
|
23
|
+
* vs transcript facets. Latency stays on `perf` so it is never confused with mix.
|
|
21
24
|
*
|
|
22
25
|
* Modelled on Claude Code's `/insights`, with the difference that motivated it: that
|
|
23
26
|
* command reads one account's directory, while `balanced` rotation sprays sessions
|
|
@@ -27,6 +30,8 @@
|
|
|
27
30
|
* The deterministic report makes zero network calls. `--narrative` is opt-in and adds
|
|
28
31
|
* the coaching prose by piping the AGGREGATE (never raw transcripts) through a headless
|
|
29
32
|
* `claude -p`.
|
|
33
|
+
*
|
|
34
|
+
* `agents trends` is a thin deprecated alias of the mix tree only (see commands/trends.ts).
|
|
30
35
|
*/
|
|
31
36
|
import * as fs from 'fs';
|
|
32
37
|
import chalk from 'chalk';
|
|
@@ -37,11 +42,25 @@ import { setHelpSections } from '../lib/help.js';
|
|
|
37
42
|
import { discoverSessions, parseTimeFilter } from '../lib/session/discover.js';
|
|
38
43
|
import { querySessions, readSessionInsights, writeSessionInsights, clearSessionInsights, } from '../lib/session/db.js';
|
|
39
44
|
import { parseSession } from '../lib/session/parse.js';
|
|
40
|
-
import { computeInsightFacets, mergeFacets, newFacetAccumulator, detectOverlap, percentile, bucketGaps, topEntries, } from '../lib/session/insights.js';
|
|
45
|
+
import { computeInsightFacets, mergeFacets, newFacetAccumulator, detectOverlap, percentile, bucketGaps, topEntries, buildInsightActions, } from '../lib/session/insights.js';
|
|
41
46
|
import { formatUsd } from '../lib/pricing/index.js';
|
|
42
47
|
import { formatDuration } from '../lib/session/render.js';
|
|
43
48
|
import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/session/width.js';
|
|
49
|
+
import { registerMixCommands } from '../lib/analytics/mix-commands.js';
|
|
44
50
|
const execFileAsync = promisify(execFile);
|
|
51
|
+
function collectAgent(value, previous) {
|
|
52
|
+
return [...previous, value];
|
|
53
|
+
}
|
|
54
|
+
function agentsFromArgv(argv) {
|
|
55
|
+
const values = [];
|
|
56
|
+
for (let i = 0; i < argv.length; i++) {
|
|
57
|
+
if (argv[i] === '--agent' && argv[i + 1])
|
|
58
|
+
values.push(argv[++i]);
|
|
59
|
+
else if (argv[i].startsWith('--agent='))
|
|
60
|
+
values.push(argv[i].slice('--agent='.length));
|
|
61
|
+
}
|
|
62
|
+
return values;
|
|
63
|
+
}
|
|
45
64
|
function resolveGroup(by) {
|
|
46
65
|
if (by === undefined)
|
|
47
66
|
return 'account';
|
|
@@ -187,7 +206,7 @@ function renderHours(hours, out) {
|
|
|
187
206
|
out.push(` ${chalk.cyan(spark)}`);
|
|
188
207
|
out.push(` ${chalk.gray('0h'.padEnd(6))}${chalk.gray('6h'.padEnd(6))}${chalk.gray('12h'.padEnd(6))}${chalk.gray('18h'.padEnd(5))}${chalk.gray('23h')}`);
|
|
189
208
|
}
|
|
190
|
-
function renderReport(groups, dim, meta) {
|
|
209
|
+
function renderReport(groups, dim, meta, actions, harnesses) {
|
|
191
210
|
const out = [];
|
|
192
211
|
const scope = meta.since ? `last ${meta.since}` : 'all time';
|
|
193
212
|
out.push(chalk.bold('Insights') + chalk.gray(` ${scope} · ${meta.analyzed} of ${meta.scanned} sessions`));
|
|
@@ -232,6 +251,22 @@ function renderReport(groups, dim, meta) {
|
|
|
232
251
|
for (const e of errs)
|
|
233
252
|
out.push(` ${chalk.gray('·')} ${padToWidth(e.name, 16)} ${chalk.gray(String(e.count))}`);
|
|
234
253
|
}
|
|
254
|
+
renderCounts('Friction / thrash', topEntries(all.frictionSignals, 10), out);
|
|
255
|
+
renderCounts('Dissatisfaction / corrections', topEntries(all.correctionSignals, 10), out);
|
|
256
|
+
renderCounts('Automatable repeats', topEntries(all.automationSignals, 10), out);
|
|
257
|
+
renderCounts('Harness split', harnesses, out);
|
|
258
|
+
out.push('');
|
|
259
|
+
out.push(chalk.bold('Actions'));
|
|
260
|
+
if (actions.length === 0) {
|
|
261
|
+
out.push(chalk.gray(' No repeated action pattern met the evidence threshold in this window.'));
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
out.push(chalk.gray(' pri category evidence sample sessions action'));
|
|
265
|
+
for (const action of actions.slice(0, 12)) {
|
|
266
|
+
out.push(` ${padToWidth(action.priority, 7)} ${padToWidth(action.category, 11)} ` +
|
|
267
|
+
`${String(action.evidenceCount).padStart(8)} ${padToWidth(action.sampleSessionIds.join(', '), 25)} ${action.action}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
235
270
|
// Output
|
|
236
271
|
out.push('');
|
|
237
272
|
out.push(chalk.bold('What you changed'));
|
|
@@ -351,11 +386,11 @@ async function insightsAction(options) {
|
|
|
351
386
|
if (options.refresh)
|
|
352
387
|
clearSessionInsights();
|
|
353
388
|
const filter = { sinceMs };
|
|
354
|
-
if (options.agent)
|
|
355
|
-
filter.agent = options.agent;
|
|
356
389
|
const scanned = querySessions(filter);
|
|
357
390
|
const wanted = options.account?.toLowerCase();
|
|
358
391
|
const inScope = scanned.filter((m) => {
|
|
392
|
+
if (options.agent?.length && !options.agent.includes(m.agent))
|
|
393
|
+
return false;
|
|
359
394
|
if (!wanted)
|
|
360
395
|
return true;
|
|
361
396
|
return [m.accountKey, m.account, m.accountOrg]
|
|
@@ -381,6 +416,15 @@ async function insightsAction(options) {
|
|
|
381
416
|
});
|
|
382
417
|
const overlap = detectOverlap(spans);
|
|
383
418
|
const groups = buildGroups(substantive, facetsById, dim);
|
|
419
|
+
const evidence = substantive.flatMap((m) => {
|
|
420
|
+
const facets = facetsById.get(m.id);
|
|
421
|
+
return facets ? [{ id: m.id, facets }] : [];
|
|
422
|
+
});
|
|
423
|
+
const actions = buildInsightActions(evidence);
|
|
424
|
+
const harnesses = topEntries(substantive.reduce((counts, row) => {
|
|
425
|
+
counts[row.agent] = (counts[row.agent] ?? 0) + 1;
|
|
426
|
+
return counts;
|
|
427
|
+
}, {}), 20);
|
|
384
428
|
if (options.json) {
|
|
385
429
|
const payload = {
|
|
386
430
|
generatedAt: new Date().toISOString(),
|
|
@@ -392,6 +436,8 @@ async function insightsAction(options) {
|
|
|
392
436
|
minMessages,
|
|
393
437
|
by: dim,
|
|
394
438
|
overlap,
|
|
439
|
+
actions,
|
|
440
|
+
harnesses,
|
|
395
441
|
groups: groups.map((g) => ({
|
|
396
442
|
key: g.key,
|
|
397
443
|
label: g.label,
|
|
@@ -420,7 +466,7 @@ async function insightsAction(options) {
|
|
|
420
466
|
unreadable,
|
|
421
467
|
minMessages,
|
|
422
468
|
overlap,
|
|
423
|
-
});
|
|
469
|
+
}, actions, harnesses);
|
|
424
470
|
if (options.narrative) {
|
|
425
471
|
await renderNarrative(groups.map((g) => ({
|
|
426
472
|
account: g.label, sessions: g.sessions, costUsd: g.costUsd,
|
|
@@ -434,49 +480,77 @@ async function insightsAction(options) {
|
|
|
434
480
|
})));
|
|
435
481
|
}
|
|
436
482
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
.description('How
|
|
483
|
+
function configureInsightsCommand(cmd) {
|
|
484
|
+
addHostOption(cmd)
|
|
485
|
+
.description('How work looks — behavioural report (default) or counter mix (`mix`, recipes)')
|
|
440
486
|
.option('--json', 'Output the full report as JSON')
|
|
441
487
|
.option('--since <time>', 'Window: 7d, 4w, 3mo, an ISO date, or "all" (default 30d)')
|
|
442
488
|
.option('--all', 'Every session ever indexed. Alias for --since all')
|
|
443
489
|
.option('--by <dimension>', 'Group by: account (default), agent, project, or day')
|
|
444
490
|
.option('--account <match>', 'Only sessions whose account key, email, or org contains this')
|
|
445
|
-
.option('--agent <id>', 'Only
|
|
491
|
+
.option('--agent <id>', 'Only these harnesses; repeat for more than one', collectAgent, [])
|
|
446
492
|
.option('--min-messages <n>', 'Skip sessions under this many messages, both roles counted (default 2)')
|
|
447
493
|
.option('--refresh', 'Discard cached facets and re-read every transcript')
|
|
448
494
|
.option('--narrative', 'Add a written read on the numbers via a headless `claude -p`')
|
|
449
495
|
.action(async (options) => {
|
|
450
|
-
|
|
496
|
+
const inherited = cmd.parent?.name() === 'sessions'
|
|
497
|
+
? cmd.parent.opts()
|
|
498
|
+
: {};
|
|
499
|
+
const inheritedAgent = typeof inherited.agent === 'string' ? [inherited.agent] : [];
|
|
500
|
+
const rawAgents = agentsFromArgv(process.argv.slice(2));
|
|
501
|
+
await insightsAction({
|
|
502
|
+
...inherited,
|
|
503
|
+
...options,
|
|
504
|
+
agent: rawAgents.length > 0 ? rawAgents : [...inheritedAgent, ...(options.agent ?? [])],
|
|
505
|
+
json: options.json ?? inherited.json,
|
|
506
|
+
since: options.since ?? inherited.since,
|
|
507
|
+
});
|
|
451
508
|
});
|
|
509
|
+
// Cheap counter recipes (former top-level `agents trends`) — same parent, no peer verb.
|
|
510
|
+
registerMixCommands(cmd);
|
|
452
511
|
setHelpSections(cmd, {
|
|
453
512
|
examples: `
|
|
454
|
-
#
|
|
513
|
+
# Behavioural report — last 30 days, split by Claude account (default)
|
|
455
514
|
agents insights
|
|
456
515
|
|
|
457
516
|
# Which repo is eating the time
|
|
458
517
|
agents insights --by project --since 90d
|
|
459
518
|
|
|
519
|
+
# Counter mix board (harness/model/token/secrets recipes) — former agents trends
|
|
520
|
+
agents insights mix
|
|
521
|
+
agents insights mix --days 30
|
|
522
|
+
agents insights harness-mix --json
|
|
523
|
+
agents insights query --kind secret --days 7
|
|
524
|
+
|
|
460
525
|
# One account only, all of its history
|
|
461
526
|
agents insights --account "Turing Labs" --all
|
|
462
527
|
|
|
463
528
|
# Machine-readable, for a dashboard or a slash command
|
|
464
|
-
agents insights --json
|
|
529
|
+
agents sessions insights --agent claude --agent codex --json
|
|
465
530
|
|
|
466
531
|
# Add a written read on what to change
|
|
467
532
|
agents insights --narrative
|
|
468
533
|
`,
|
|
469
534
|
notes: `
|
|
470
|
-
|
|
471
|
-
|
|
535
|
+
Two paths under one verb:
|
|
536
|
+
bare \`agents insights\` — transcript behaviour (tools, friction, rhythm, by account)
|
|
537
|
+
\`agents insights mix\` — cheap counters from sessions.db + usage.db
|
|
538
|
+
Latency is \`agents perf\` (not mix). Quota is \`agents usage\`. Skill/slash popularity
|
|
539
|
+
is \`agents sessions stats\`. \`agents trends\` is a deprecated alias of the mix tree.
|
|
472
540
|
|
|
473
|
-
The
|
|
541
|
+
The behavioural report parses in-scope transcripts once and caches facets; later runs
|
|
474
542
|
re-read only files that changed. \`--refresh\` forces a full re-read.
|
|
475
543
|
|
|
476
|
-
|
|
477
|
-
|
|
544
|
+
\`agents insights\` is the top-level alias of \`agents sessions insights\`.
|
|
545
|
+
Repeat \`--agent\` to compare several harnesses in one report.
|
|
478
546
|
|
|
479
547
|
Everything except \`--narrative\` is local and makes no network calls.
|
|
480
548
|
`,
|
|
481
549
|
});
|
|
482
550
|
}
|
|
551
|
+
export function registerInsightsCommand(program) {
|
|
552
|
+
configureInsightsCommand(program.command('insights'));
|
|
553
|
+
}
|
|
554
|
+
export function registerSessionsInsightsCommand(sessions) {
|
|
555
|
+
configureInsightsCommand(sessions.command('insights'));
|
|
556
|
+
}
|
package/dist/commands/menubar.js
CHANGED
|
@@ -137,6 +137,14 @@ export function registerMenubarCommands(program) {
|
|
|
137
137
|
again, run \`agents menubar disable\`.
|
|
138
138
|
`,
|
|
139
139
|
});
|
|
140
|
+
menubar
|
|
141
|
+
.command('snapshot', { hidden: true })
|
|
142
|
+
.description('Emit the consolidated AGI Menu polling snapshot.')
|
|
143
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
const { computeMenubarSnapshot } = await import('../lib/menubar/snapshot.js');
|
|
146
|
+
process.stdout.write(`${JSON.stringify(await computeMenubarSnapshot())}\n`);
|
|
147
|
+
});
|
|
140
148
|
menubar
|
|
141
149
|
.command('enable')
|
|
142
150
|
.description('Install and start AGI Menu (launches at login)')
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents reconnect [session-id]` — get me back into an agent terminal whose ssh
|
|
3
|
+
* link dropped.
|
|
4
|
+
*
|
|
5
|
+
* The auto-reconnect in lib/hosts/reconnect.ts already re-attaches a live remote
|
|
6
|
+
* pane over its own bounded backoff on a transient blink. This is the MANUAL
|
|
7
|
+
* recovery for after it gave up — a sustained outage, or a VS Code terminal tab
|
|
8
|
+
* that closed with the dead ssh client. One verb that always tries hardest to put
|
|
9
|
+
* you back into that agent: attach the live pane if it still exists, else resume
|
|
10
|
+
* the session; live pane > resumed copy > a clear message about what was lost.
|
|
11
|
+
*
|
|
12
|
+
* It delegates to `focus`, which already does exactly attach-else-resume for a
|
|
13
|
+
* resolved id ({@link focusAction} → `focusResolvedSession`), so there is no second
|
|
14
|
+
* recovery path to keep in sync. What `reconnect` adds is the recovery framing and,
|
|
15
|
+
* with no id, targeting the MOST RECENT session in this shell's directory — the one
|
|
16
|
+
* that most likely just dropped — instead of opening the full fleet picker.
|
|
17
|
+
*/
|
|
18
|
+
import type { Command } from 'commander';
|
|
19
|
+
import type { SessionMeta } from '../lib/session/types.js';
|
|
20
|
+
/**
|
|
21
|
+
* The recency signal a no-id reconnect targets: last activity if the scan
|
|
22
|
+
* computed one, else the creation time. A row with neither parseable sorts last.
|
|
23
|
+
* Pure so target selection is unit-tested without the scanner.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sessionRecency(s: Pick<SessionMeta, 'lastActivity' | 'timestamp'>): number;
|
|
26
|
+
/**
|
|
27
|
+
* Pick the session to reconnect when the user gave no id: the most recently
|
|
28
|
+
* active one in scope — the terminal that most likely just dropped. Pure; returns
|
|
29
|
+
* undefined for an empty scope so the caller can print guidance.
|
|
30
|
+
*/
|
|
31
|
+
export declare function pickMostRecentSession(sessions: SessionMeta[]): SessionMeta | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the no-id target: prefer the most recent session started from THIS
|
|
34
|
+
* directory (the shell the user is reconnecting from), falling back to the most
|
|
35
|
+
* recent session anywhere when the current directory has none. Best-effort — a
|
|
36
|
+
* discovery failure yields undefined, never throws. Injectable `discover` keeps
|
|
37
|
+
* the two-step scope preference testable without touching the real scanner.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveRecentTarget(cwd: string, discover?: (opts: {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
all?: boolean;
|
|
42
|
+
since?: string;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}) => Promise<SessionMeta[]>): Promise<SessionMeta | undefined>;
|
|
45
|
+
export declare function reconnectAction(id: string | undefined): Promise<void>;
|
|
46
|
+
export declare function registerReconnectCommand(program: Command): void;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents reconnect [session-id]` — get me back into an agent terminal whose ssh
|
|
3
|
+
* link dropped.
|
|
4
|
+
*
|
|
5
|
+
* The auto-reconnect in lib/hosts/reconnect.ts already re-attaches a live remote
|
|
6
|
+
* pane over its own bounded backoff on a transient blink. This is the MANUAL
|
|
7
|
+
* recovery for after it gave up — a sustained outage, or a VS Code terminal tab
|
|
8
|
+
* that closed with the dead ssh client. One verb that always tries hardest to put
|
|
9
|
+
* you back into that agent: attach the live pane if it still exists, else resume
|
|
10
|
+
* the session; live pane > resumed copy > a clear message about what was lost.
|
|
11
|
+
*
|
|
12
|
+
* It delegates to `focus`, which already does exactly attach-else-resume for a
|
|
13
|
+
* resolved id ({@link focusAction} → `focusResolvedSession`), so there is no second
|
|
14
|
+
* recovery path to keep in sync. What `reconnect` adds is the recovery framing and,
|
|
15
|
+
* with no id, targeting the MOST RECENT session in this shell's directory — the one
|
|
16
|
+
* that most likely just dropped — instead of opening the full fleet picker.
|
|
17
|
+
*/
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import { focusAction } from './focus.js';
|
|
20
|
+
import { discoverSessions } from '../lib/session/discover.js';
|
|
21
|
+
import { setHelpSections } from '../lib/help.js';
|
|
22
|
+
/**
|
|
23
|
+
* The recency signal a no-id reconnect targets: last activity if the scan
|
|
24
|
+
* computed one, else the creation time. A row with neither parseable sorts last.
|
|
25
|
+
* Pure so target selection is unit-tested without the scanner.
|
|
26
|
+
*/
|
|
27
|
+
export function sessionRecency(s) {
|
|
28
|
+
const t = Date.parse(s.lastActivity ?? s.timestamp ?? '');
|
|
29
|
+
return Number.isFinite(t) ? t : Number.NEGATIVE_INFINITY;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pick the session to reconnect when the user gave no id: the most recently
|
|
33
|
+
* active one in scope — the terminal that most likely just dropped. Pure; returns
|
|
34
|
+
* undefined for an empty scope so the caller can print guidance.
|
|
35
|
+
*/
|
|
36
|
+
export function pickMostRecentSession(sessions) {
|
|
37
|
+
let best;
|
|
38
|
+
for (const s of sessions) {
|
|
39
|
+
if (!best || sessionRecency(s) > sessionRecency(best))
|
|
40
|
+
best = s;
|
|
41
|
+
}
|
|
42
|
+
return best;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the no-id target: prefer the most recent session started from THIS
|
|
46
|
+
* directory (the shell the user is reconnecting from), falling back to the most
|
|
47
|
+
* recent session anywhere when the current directory has none. Best-effort — a
|
|
48
|
+
* discovery failure yields undefined, never throws. Injectable `discover` keeps
|
|
49
|
+
* the two-step scope preference testable without touching the real scanner.
|
|
50
|
+
*/
|
|
51
|
+
export async function resolveRecentTarget(cwd, discover = discoverSessions) {
|
|
52
|
+
try {
|
|
53
|
+
const here = await discover({ cwd, since: '7d', limit: 200 });
|
|
54
|
+
const local = pickMostRecentSession(here);
|
|
55
|
+
if (local)
|
|
56
|
+
return local;
|
|
57
|
+
const any = await discover({ all: true, since: '7d', limit: 200 });
|
|
58
|
+
return pickMostRecentSession(any);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function reconnectAction(id) {
|
|
65
|
+
if (id) {
|
|
66
|
+
// focus already attaches a live pane or recovers a dropped one for a resolved
|
|
67
|
+
// id — on the origin device for a remote session. Reconnect is that path with
|
|
68
|
+
// a recovery-first name; no reason to duplicate the resolution/attach logic.
|
|
69
|
+
await focusAction(id, {});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const recent = await resolveRecentTarget(process.cwd());
|
|
73
|
+
if (!recent) {
|
|
74
|
+
console.log(chalk.gray('No recent session to reconnect from here.'));
|
|
75
|
+
console.log(chalk.gray(' reconnect a specific one: agents reconnect <session-id>'));
|
|
76
|
+
console.log(chalk.gray(' or pick from the fleet: agents sessions'));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const where = recent.machine ? ` on ${recent.machine}` : '';
|
|
80
|
+
console.log(chalk.gray(`Reconnecting ${recent.shortId}${where} (most recent here)…`));
|
|
81
|
+
await focusAction(recent.id, {});
|
|
82
|
+
}
|
|
83
|
+
export function registerReconnectCommand(program) {
|
|
84
|
+
const cmd = program
|
|
85
|
+
.command('reconnect')
|
|
86
|
+
.argument('[session-id]', 'Session id/prefix to reconnect (default: the most recent session started here)')
|
|
87
|
+
.description('Re-enter a dropped agent terminal: attach the live pane if it survived, else resume the session')
|
|
88
|
+
.action(async (id) => {
|
|
89
|
+
await reconnectAction(id);
|
|
90
|
+
});
|
|
91
|
+
setHelpSections(cmd, {
|
|
92
|
+
examples: `
|
|
93
|
+
# Reconnect the session that just dropped in this shell (most recent here)
|
|
94
|
+
agents reconnect
|
|
95
|
+
|
|
96
|
+
# Reconnect one specific agent terminal by id/prefix
|
|
97
|
+
agents reconnect 74b13b0b
|
|
98
|
+
|
|
99
|
+
# Same, spelled under the sessions group
|
|
100
|
+
agents sessions reconnect 74b13b0b
|
|
101
|
+
`,
|
|
102
|
+
notes: `
|
|
103
|
+
- Best-effort recovery: a living remote tmux pane is JOINED (a second client, no fork); a dropped one is RESUMED on its origin device (a copy if it was mid-run, a /continue if idle).
|
|
104
|
+
- With no id, targets the most recent session started from this directory (the terminal that most likely just dropped), not the full fleet picker. Falls back to the most recent session anywhere when this directory has none.
|
|
105
|
+
- This is the manual companion to the automatic reconnect that runs during a live 'agents run --device <box>' when the network blinks; use it after that gave up or the terminal tab closed.
|
|
106
|
+
- Related: 'agents sessions focus' (attach/recover with a picker) and 'agents sessions resume' (multi-select history -> tabs).
|
|
107
|
+
`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -46,5 +46,8 @@ export declare function groupRoutineJobsByDevice(jobs: JobConfig[], registry: De
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function groupRoutineJobsByProject(jobs: JobConfig[], knownProjectNames: Set<string>): RoutineListGroup[];
|
|
48
48
|
export declare function buildRunsJson(runs: RunMeta[]): Record<string, unknown>[];
|
|
49
|
+
/** Build the exact structured routine rows shared by `routines list --json`
|
|
50
|
+
* and the one-process AGI Menu snapshot. */
|
|
51
|
+
export declare function buildRoutineListJson(): Record<string, unknown>[];
|
|
49
52
|
/** Register the `agents routines` command tree. */
|
|
50
53
|
export declare function registerRoutinesCommands(program: Command): void;
|