@phnx-labs/agents-cli 1.20.78 → 1.20.82
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 +487 -0
- package/README.md +25 -25
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +9 -0
- package/dist/commands/activity.js +153 -24
- package/dist/commands/apply.js +11 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/commands.d.ts +1 -1
- package/dist/commands/commands.js +7 -6
- package/dist/commands/defaults.js +3 -11
- package/dist/commands/doctor.d.ts +65 -0
- package/dist/commands/doctor.js +575 -31
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +24 -5
- package/dist/commands/feed.d.ts +4 -0
- package/dist/commands/feed.js +87 -11
- package/dist/commands/harness.js +2 -2
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +8 -4
- package/dist/commands/logs.js +6 -0
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -6
- package/dist/commands/permissions.js +1 -1
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/pull.d.ts +1 -1
- package/dist/commands/pull.js +4 -4
- package/dist/commands/repo.d.ts +78 -0
- package/dist/commands/repo.js +187 -11
- package/dist/commands/routines.js +12 -2
- package/dist/commands/rules.js +13 -12
- package/dist/commands/secrets.d.ts +3 -1
- package/dist/commands/secrets.js +100 -32
- package/dist/commands/sessions-inject.d.ts +7 -4
- package/dist/commands/sessions-inject.js +18 -18
- package/dist/commands/sessions-picker.js +32 -3
- package/dist/commands/sessions-resume.d.ts +16 -0
- package/dist/commands/sessions-resume.js +14 -13
- package/dist/commands/sessions.d.ts +31 -0
- package/dist/commands/sessions.js +141 -16
- package/dist/commands/set.d.ts +15 -0
- package/dist/commands/set.js +79 -0
- package/dist/commands/setup-fleet.d.ts +21 -0
- package/dist/commands/setup-fleet.js +201 -0
- package/dist/commands/setup-secrets.d.ts +20 -0
- package/dist/commands/setup-secrets.js +223 -0
- package/dist/commands/setup.js +17 -3
- package/dist/commands/skills.js +1 -1
- package/dist/commands/sync.js +7 -2
- package/dist/commands/teams-picker.js +0 -1
- package/dist/commands/teams.d.ts +33 -1
- package/dist/commands/teams.js +208 -44
- package/dist/commands/versions.js +10 -5
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +11 -0
- package/dist/index.js +5 -8
- package/dist/lib/acp/harnesses.js +0 -7
- package/dist/lib/activity.d.ts +156 -0
- package/dist/lib/activity.js +282 -0
- package/dist/lib/agents.d.ts +9 -2
- package/dist/lib/agents.js +72 -63
- package/dist/lib/browser/service.js +3 -0
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/capabilities.js +6 -2
- package/dist/lib/crabbox/lease.js +2 -2
- package/dist/lib/crabbox/setup-copy.d.ts +4 -4
- package/dist/lib/crabbox/setup-copy.js +4 -4
- package/dist/lib/daemon.d.ts +18 -0
- package/dist/lib/daemon.js +47 -6
- package/dist/lib/devices/fleet.d.ts +1 -1
- package/dist/lib/devices/fleet.js +1 -1
- package/dist/lib/doctor-diff.d.ts +20 -0
- package/dist/lib/doctor-diff.js +6 -1
- package/dist/lib/drift.d.ts +31 -11
- package/dist/lib/drift.js +58 -7
- package/dist/lib/events.d.ts +8 -1
- package/dist/lib/events.js +10 -1
- package/dist/lib/exec.js +35 -8
- package/dist/lib/feed-post.d.ts +28 -1
- package/dist/lib/feed-post.js +110 -2
- package/dist/lib/fleet/apply.js +8 -1
- package/dist/lib/fleet/auth-sync.d.ts +18 -1
- package/dist/lib/fleet/auth-sync.js +25 -11
- package/dist/lib/fleet/remote-login.js +5 -0
- package/dist/lib/git.d.ts +14 -14
- package/dist/lib/git.js +41 -44
- package/dist/lib/hooks.d.ts +42 -0
- package/dist/lib/hooks.js +137 -93
- package/dist/lib/hosts/dispatch.d.ts +28 -1
- package/dist/lib/hosts/dispatch.js +33 -5
- package/dist/lib/hosts/option.js +2 -2
- package/dist/lib/hosts/passthrough.d.ts +22 -1
- package/dist/lib/hosts/passthrough.js +238 -13
- package/dist/lib/hosts/run-target.d.ts +2 -0
- package/dist/lib/hosts/run-target.js +1 -0
- package/dist/lib/hq/floor.d.ts +1 -1
- package/dist/lib/hq/floor.js +6 -1
- package/dist/lib/mcp.js +3 -71
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/notify-desktop.d.ts +15 -0
- package/dist/lib/menubar/notify-desktop.js +41 -5
- package/dist/lib/merged-resources.d.ts +11 -0
- package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
- package/dist/lib/permissions.d.ts +0 -32
- package/dist/lib/permissions.js +4 -157
- package/dist/lib/picker.js +1 -1
- package/dist/lib/platform/process.d.ts +16 -5
- package/dist/lib/platform/process.js +54 -0
- package/dist/lib/plugins.d.ts +0 -8
- package/dist/lib/plugins.js +4 -110
- package/dist/lib/project-resources.js +4 -1
- package/dist/lib/refresh.d.ts +5 -3
- package/dist/lib/refresh.js +7 -5
- package/dist/lib/resources/commands.js +4 -1
- package/dist/lib/resources/mcp.js +0 -4
- package/dist/lib/resources/permissions.d.ts +1 -1
- package/dist/lib/resources/permissions.js +1 -5
- package/dist/lib/resources/rules.js +4 -1
- package/dist/lib/resources/skills.js +4 -1
- package/dist/lib/resources/subagents.js +4 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/routines.d.ts +20 -0
- package/dist/lib/routines.js +6 -0
- package/dist/lib/run-defaults.d.ts +1 -0
- package/dist/lib/run-defaults.js +9 -0
- package/dist/lib/runner.d.ts +0 -12
- package/dist/lib/runner.js +49 -10
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.d.ts +16 -0
- package/dist/lib/secrets/agent.js +105 -3
- package/dist/lib/secrets/audit.d.ts +46 -0
- package/dist/lib/secrets/audit.js +56 -0
- package/dist/lib/secrets/bundles.d.ts +11 -8
- package/dist/lib/secrets/bundles.js +31 -23
- package/dist/lib/secrets/index.d.ts +2 -1
- package/dist/lib/secrets/index.js +8 -2
- package/dist/lib/secrets/remote.d.ts +75 -2
- package/dist/lib/secrets/remote.js +139 -5
- package/dist/lib/secrets/sync.js +3 -3
- package/dist/lib/session/active.d.ts +110 -26
- package/dist/lib/session/active.js +161 -50
- package/dist/lib/session/actor-sidecar.d.ts +23 -0
- package/dist/lib/session/actor-sidecar.js +101 -0
- package/dist/lib/session/db.d.ts +4 -1
- package/dist/lib/session/db.js +65 -8
- package/dist/lib/session/discover.d.ts +4 -0
- package/dist/lib/session/discover.js +13 -0
- package/dist/lib/session/hook-sessions.d.ts +9 -0
- package/dist/lib/session/hook-sessions.js +57 -8
- package/dist/lib/session/pid-registry.d.ts +13 -0
- package/dist/lib/session/render.d.ts +2 -0
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/types.d.ts +16 -0
- package/dist/lib/staleness/detectors/commands.js +2 -2
- package/dist/lib/staleness/detectors/permissions.js +1 -45
- package/dist/lib/staleness/writers/commands.js +4 -5
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -2
- package/dist/lib/startup/command-registry.js +2 -4
- package/dist/lib/subagents-registry.js +5 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -12
- package/dist/lib/teams/agents.d.ts +0 -2
- package/dist/lib/teams/agents.js +10 -27
- package/dist/lib/teams/index.d.ts +1 -1
- package/dist/lib/teams/index.js +1 -1
- package/dist/lib/types.d.ts +17 -9
- package/dist/lib/usage.d.ts +28 -5
- package/dist/lib/usage.js +271 -8
- package/dist/lib/versions.js +7 -1
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +25 -13
- package/package.json +1 -2
- package/dist/commands/check.d.ts +0 -15
- package/dist/commands/check.js +0 -180
- package/dist/commands/resources.d.ts +0 -5
|
@@ -40,12 +40,23 @@ export declare function liveGlyphAndPreview(a: ActiveSession | undefined): {
|
|
|
40
40
|
glyph: string;
|
|
41
41
|
preview: string;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* The one-word live status for a listing row — `working` / `waiting` / `idle`,
|
|
45
|
+
* the same three states the `--active` column shows, so the default list is no
|
|
46
|
+
* longer just a glyph. `waiting` is the actionable "needs you" case (a question /
|
|
47
|
+
* permission / plan-review), kept distinct from `idle` (stopped) and `working`.
|
|
48
|
+
* Empty for a not-live row, and for the rare no-signal `unknown`. Pure +
|
|
49
|
+
* exported for the row tests.
|
|
50
|
+
*/
|
|
51
|
+
export declare function liveStatusWord(a: ActiveSession | undefined): string;
|
|
43
52
|
/**
|
|
44
53
|
* The tracker/PR ref for a session's dedicated column: the ticket id when known,
|
|
45
54
|
* else `PR#<n>`, else empty. Pulled out of the trailing badge blob so refs align
|
|
46
55
|
* into a scannable column instead of jamming against a truncated topic.
|
|
47
56
|
*/
|
|
48
57
|
export declare function ticketLabel(s: Pick<SessionMeta, 'ticketId' | 'prNumber'>): string;
|
|
58
|
+
export declare function linkTicketCell(s: Pick<SessionMeta, 'ticketId' | 'prNumber' | 'prUrl'>, label: string): string;
|
|
59
|
+
export declare function linkCwdCell(s: Pick<SessionMeta, 'cwd' | '_remote'>, label: string): string;
|
|
49
60
|
/**
|
|
50
61
|
* The row shape `agents sessions --active --json` emits. RUSH-1981: a watcher
|
|
51
62
|
* joins active sessions on ticketId + project, but the raw ActiveSession nests
|
|
@@ -58,7 +69,16 @@ export declare function ticketLabel(s: Pick<SessionMeta, 'ticketId' | 'prNumber'
|
|
|
58
69
|
export declare function serializeActiveSessionsForJson(sessions: ActiveSession[]): Array<ActiveSession & {
|
|
59
70
|
ticketId: string | null;
|
|
60
71
|
project: string | null;
|
|
72
|
+
prLink: string | null;
|
|
61
73
|
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Compact owner display for the `--active` owner column: the local-part of a
|
|
76
|
+
* resolved actor email/login (`muqsit@getrush.ai` -> `muqsit`), the id as-is
|
|
77
|
+
* when it has no `@`, and `-` when the actor is unresolved (`UNRESOLVED@<host>`)
|
|
78
|
+
* or absent (a launch predating actor stamping). Honest by design — an
|
|
79
|
+
* unresolved local run shows no owner rather than inventing one.
|
|
80
|
+
*/
|
|
81
|
+
export declare function ownerLabel(s: ActiveSession): string;
|
|
62
82
|
/** Grouped + sorted view of active sessions for the --active renderer. */
|
|
63
83
|
export interface ActiveSessionsLayout {
|
|
64
84
|
workspaces: Array<{
|
|
@@ -170,6 +190,13 @@ export declare function gatherActiveSessions(opts?: {
|
|
|
170
190
|
sessions: ActiveSession[];
|
|
171
191
|
remoteDeviceCount: number;
|
|
172
192
|
}>;
|
|
193
|
+
/** One flat table row:
|
|
194
|
+
* shortId · agent · version · model · project · [glyph] label·doing · [ticket] · [wt] · time
|
|
195
|
+
* `doing` is the live preview when running, else the topic. The `ticket` column
|
|
196
|
+
* (tracker/PR ref, pulled out of the badge blob so refs align) is only rendered
|
|
197
|
+
* when `showTicket` — otherwise a listing with no refs would waste a column of
|
|
198
|
+
* dashes and needlessly truncate the topic. Worktree stays a trailing badge. */
|
|
199
|
+
export declare function flatSessionRow(session: SessionMeta, live?: ActiveSession, showTicket?: boolean, cols?: PickerColumns): string;
|
|
173
200
|
/**
|
|
174
201
|
* Group key for the overview: prefer the indexed project name; else fold the cwd
|
|
175
202
|
* to its repo — a worktree (`.../<repo>/.agents/worktrees/<slug>`) folds to the
|
|
@@ -221,6 +248,10 @@ export interface PickerColumns {
|
|
|
221
248
|
/** Total width of the machine column, sized to the widest compacted hostname
|
|
222
249
|
* in the pool (capped). Falls back to PICKER_MACHINE_W when absent. */
|
|
223
250
|
machineWidth?: number;
|
|
251
|
+
/** Render the model column only when at least one row carries a model. */
|
|
252
|
+
showModel?: boolean;
|
|
253
|
+
/** Pool-sized model column width, including one trailing separator cell. */
|
|
254
|
+
modelWidth?: number;
|
|
224
255
|
/** Render the ticket/PR column (only when at least one row carries a ref). */
|
|
225
256
|
showTicket?: boolean;
|
|
226
257
|
/**
|
|
@@ -32,7 +32,7 @@ import { filterTeamSessions } from '../lib/session/team-filter.js';
|
|
|
32
32
|
import { parseSession } from '../lib/session/parse.js';
|
|
33
33
|
import { runRemoteSessions, buildForwardedArgs, ensureWholeIndex } from '../lib/session/remote.js';
|
|
34
34
|
import { formatRelativeTime } from '../lib/session/relative-time.js';
|
|
35
|
-
import { renderConversationMarkdown, renderSummary, renderSummaryHeader, computeSummaryStats, renderJson, filterEvents, parseRoleList, linkUrl } from '../lib/session/render.js';
|
|
35
|
+
import { renderConversationMarkdown, renderSummary, renderSummaryHeader, computeSummaryStats, renderJson, filterEvents, parseRoleList, linkPath, linkUrl, shortenModel } from '../lib/session/render.js';
|
|
36
36
|
import { linearIssueUrl } from '../lib/session/linear.js';
|
|
37
37
|
import { renderMarkdown } from '../lib/markdown.js';
|
|
38
38
|
import { AGENTS, colorAgent, resolveAgentName } from '../lib/agents.js';
|
|
@@ -182,6 +182,11 @@ function statusColor(status) {
|
|
|
182
182
|
case 'idle': return chalk.gray;
|
|
183
183
|
case 'queued': return chalk.blue;
|
|
184
184
|
case 'input_required': return chalk.yellow;
|
|
185
|
+
// Dead process: dimmed so it recedes from the live rows without being mistaken
|
|
186
|
+
// for the gray `idle` (which reads as "done, waiting for you" — a live state).
|
|
187
|
+
case 'closed': return chalk.dim;
|
|
188
|
+
// Days-stale / dangling: red so a session nobody is driving stands out.
|
|
189
|
+
case 'abandoned': return chalk.red;
|
|
185
190
|
// Alive but un-introspectable (a harness whose transcript we can't parse).
|
|
186
191
|
// Magenta so it never reads as the gray "idle" it used to be faked as.
|
|
187
192
|
case 'unknown': return chalk.magenta;
|
|
@@ -303,6 +308,10 @@ export function formatActiveRowDescription(s) {
|
|
|
303
308
|
}
|
|
304
309
|
/** Short human word for a session's activity (falls back to the coarse status). */
|
|
305
310
|
function activityLabel(s) {
|
|
311
|
+
// Lifecycle status wins over any residual parsed activity — a dead/dangling
|
|
312
|
+
// session must read `closed`/`abandoned`, not the `idle` its stale tail infers.
|
|
313
|
+
if (s.status === 'closed' || s.status === 'abandoned')
|
|
314
|
+
return s.status;
|
|
306
315
|
if (s.activity === 'waiting_input')
|
|
307
316
|
return 'waiting';
|
|
308
317
|
if (s.activity === 'working')
|
|
@@ -335,14 +344,54 @@ export function indexActiveBySessionId(active) {
|
|
|
335
344
|
export function liveGlyphAndPreview(a) {
|
|
336
345
|
if (!a)
|
|
337
346
|
return { glyph: '', preview: '' };
|
|
347
|
+
// `◌` (dotted) = alive but un-introspectable — visually distinct from `○` idle
|
|
348
|
+
// so an opaque harness is never mistaken for a finished one. `⊘` = abandoned /
|
|
349
|
+
// dangling; `×` = closed (dead pid) — both distinct from the `○` idle a live,
|
|
350
|
+
// stopped session shows, so a gone session never masquerades as a resting one.
|
|
351
|
+
if (a.status === 'abandoned')
|
|
352
|
+
return { glyph: statusColor(a.status)('⊘'), preview: buildSessionDescription(a) };
|
|
353
|
+
if (a.status === 'closed')
|
|
354
|
+
return { glyph: statusColor(a.status)('×'), preview: buildSessionDescription(a) };
|
|
338
355
|
const waiting = a.status === 'input_required' || a.activity === 'waiting_input';
|
|
339
356
|
const running = a.status === 'running' || a.activity === 'working';
|
|
340
|
-
// `◌` (dotted) = alive but un-introspectable — visually distinct from `○` idle
|
|
341
|
-
// so an opaque harness is never mistaken for a finished one.
|
|
342
357
|
const unknown = a.status === 'unknown';
|
|
343
|
-
const shape = waiting ? '◐'
|
|
358
|
+
const shape = waiting ? '◐'
|
|
359
|
+
: running ? '●'
|
|
360
|
+
: unknown ? '◌'
|
|
361
|
+
: '○';
|
|
344
362
|
return { glyph: statusColor(a.status)(shape), preview: buildSessionDescription(a) };
|
|
345
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* The one-word live status for a listing row — `working` / `waiting` / `idle`,
|
|
366
|
+
* the same three states the `--active` column shows, so the default list is no
|
|
367
|
+
* longer just a glyph. `waiting` is the actionable "needs you" case (a question /
|
|
368
|
+
* permission / plan-review), kept distinct from `idle` (stopped) and `working`.
|
|
369
|
+
* Empty for a not-live row, and for the rare no-signal `unknown`. Pure +
|
|
370
|
+
* exported for the row tests.
|
|
371
|
+
*/
|
|
372
|
+
export function liveStatusWord(a) {
|
|
373
|
+
if (!a)
|
|
374
|
+
return '';
|
|
375
|
+
// Lifecycle status is definitive — surface it ahead of any parsed activity.
|
|
376
|
+
if (a.status === 'closed' || a.status === 'abandoned')
|
|
377
|
+
return a.status;
|
|
378
|
+
if (a.status === 'input_required' || a.activity === 'waiting_input')
|
|
379
|
+
return 'waiting';
|
|
380
|
+
if (a.status === 'running' || a.activity === 'working')
|
|
381
|
+
return 'working';
|
|
382
|
+
if (a.status === 'idle' || a.activity === 'idle')
|
|
383
|
+
return 'idle';
|
|
384
|
+
if (a.status === 'queued')
|
|
385
|
+
return 'queued';
|
|
386
|
+
return '';
|
|
387
|
+
}
|
|
388
|
+
/** The colored, space-padded status cell for a listing row (empty when not live). */
|
|
389
|
+
function liveStatusCell(live) {
|
|
390
|
+
const word = liveStatusWord(live);
|
|
391
|
+
if (!word || !live)
|
|
392
|
+
return { cell: '', width: 0 };
|
|
393
|
+
return { cell: statusColor(live.status)(padToWidth(word, 8)), width: 8 };
|
|
394
|
+
}
|
|
346
395
|
/**
|
|
347
396
|
* The tracker/PR ref for a session's dedicated column: the ticket id when known,
|
|
348
397
|
* else `PR#<n>`, else empty. Pulled out of the trailing badge blob so refs align
|
|
@@ -351,6 +400,21 @@ export function liveGlyphAndPreview(a) {
|
|
|
351
400
|
export function ticketLabel(s) {
|
|
352
401
|
return s.ticketId ?? (s.prNumber ? `PR#${s.prNumber}` : '');
|
|
353
402
|
}
|
|
403
|
+
function ticketUrl(s) {
|
|
404
|
+
if (s.ticketId)
|
|
405
|
+
return linearIssueUrl(s.ticketId);
|
|
406
|
+
return s.prNumber ? s.prUrl : undefined;
|
|
407
|
+
}
|
|
408
|
+
export function linkTicketCell(s, label) {
|
|
409
|
+
const url = ticketUrl(s);
|
|
410
|
+
return url && label.trim() !== '-' ? linkUrl(url, label) : label;
|
|
411
|
+
}
|
|
412
|
+
export function linkCwdCell(s, label) {
|
|
413
|
+
return s.cwd && !s._remote ? linkPath(s.cwd, label) : label;
|
|
414
|
+
}
|
|
415
|
+
function modelLabel(model) {
|
|
416
|
+
return model ? shortenModel(model) : '-';
|
|
417
|
+
}
|
|
354
418
|
/**
|
|
355
419
|
* The row shape `agents sessions --active --json` emits. RUSH-1981: a watcher
|
|
356
420
|
* joins active sessions on ticketId + project, but the raw ActiveSession nests
|
|
@@ -365,6 +429,7 @@ export function serializeActiveSessionsForJson(sessions) {
|
|
|
365
429
|
...s,
|
|
366
430
|
ticketId: s.ticket?.id ?? null,
|
|
367
431
|
project: s.cwd ? path.basename(s.cwd) : null,
|
|
432
|
+
prLink: s.pr?.url ?? null,
|
|
368
433
|
}));
|
|
369
434
|
}
|
|
370
435
|
/**
|
|
@@ -435,11 +500,12 @@ function locatorBadge(s) {
|
|
|
435
500
|
* terminal width so the row never wraps.
|
|
436
501
|
*/
|
|
437
502
|
function printActiveRow(s, indent) {
|
|
438
|
-
// shortId (8-char) · agent · host · status · badges · identity+todos+snippet
|
|
503
|
+
// shortId (8-char) · agent · host · status · owner · badges · identity+todos+snippet
|
|
439
504
|
const idCol = chalk.dim(padToWidth((s.sessionId?.slice(0, 8)) ?? '-', 9));
|
|
440
505
|
const kindCol = colorAgent(s.kind)(padToWidth(truncateToWidth(s.kind, 8), 9));
|
|
441
506
|
const hostCol = chalk.gray(padToWidth(truncateToWidth(s.host ?? '-', 8), 9));
|
|
442
507
|
const statusCol = statusColor(s.status)(padToWidth(truncateToWidth(activityLabel(s), 8), 9));
|
|
508
|
+
const ownerCol = chalk.cyan(padToWidth(truncateToWidth(ownerLabel(s), 8), 9));
|
|
443
509
|
const fork = s.pidCount && s.pidCount > 1 ? chalk.dim(`×${s.pidCount} `) : '';
|
|
444
510
|
const badges = (fork ? fork : '') + [signalBadges(s), locatorBadge(s)].filter(Boolean).join(' ');
|
|
445
511
|
// Identity (label/project/ticket clickable) + checklist + live snippet.
|
|
@@ -447,10 +513,24 @@ function printActiveRow(s, indent) {
|
|
|
447
513
|
// via the SSH fan-out already populated (including todos when the peer has them).
|
|
448
514
|
const desc = formatActiveRowDescription(s) || '-';
|
|
449
515
|
// Fill the remaining width with the preview so nothing wraps under tmux/SSH.
|
|
450
|
-
const fixed = stringWidth(indent) + 9 + 9 + 9 + 9 + (badges ? stringWidth(badges) + 1 : 0);
|
|
516
|
+
const fixed = stringWidth(indent) + 9 + 9 + 9 + 9 + 9 + (badges ? stringWidth(badges) + 1 : 0);
|
|
451
517
|
const room = Math.max(12, terminalWidth() - fixed - 1);
|
|
452
518
|
const descCol = chalk.white(truncateToWidth(desc, room));
|
|
453
|
-
console.log(indent + idCol + kindCol + hostCol + statusCol + (badges ? badges + ' ' : '') + descCol);
|
|
519
|
+
console.log(indent + idCol + kindCol + hostCol + statusCol + ownerCol + (badges ? badges + ' ' : '') + descCol);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Compact owner display for the `--active` owner column: the local-part of a
|
|
523
|
+
* resolved actor email/login (`muqsit@getrush.ai` -> `muqsit`), the id as-is
|
|
524
|
+
* when it has no `@`, and `-` when the actor is unresolved (`UNRESOLVED@<host>`)
|
|
525
|
+
* or absent (a launch predating actor stamping). Honest by design — an
|
|
526
|
+
* unresolved local run shows no owner rather than inventing one.
|
|
527
|
+
*/
|
|
528
|
+
export function ownerLabel(s) {
|
|
529
|
+
const owner = s.owner;
|
|
530
|
+
if (!owner || owner.startsWith('UNRESOLVED@'))
|
|
531
|
+
return '-';
|
|
532
|
+
const at = owner.indexOf('@');
|
|
533
|
+
return at > 0 ? owner.slice(0, at) : owner;
|
|
454
534
|
}
|
|
455
535
|
/**
|
|
456
536
|
* Short label for an IDE window. The slice key in live-terminals.json is
|
|
@@ -666,8 +746,8 @@ async function runRemoteSessionsJson(hosts) {
|
|
|
666
746
|
process.stdout.write(serializeSessionsJson(sessions));
|
|
667
747
|
}
|
|
668
748
|
/**
|
|
669
|
-
* `running N · idle N · waiting N · queued N ·
|
|
670
|
-
* sessions (zero buckets omitted). Same bucketing as the
|
|
749
|
+
* `running N · idle N · waiting N · queued N · closed N · abandoned N · unknown N`
|
|
750
|
+
* for a bucket of sessions (zero buckets omitted). Same bucketing as the summary so
|
|
671
751
|
* per-group counts reconcile with the `(total)` beside the header — the `unknown`
|
|
672
752
|
* bucket is what keeps an alive-but-opaque row from silently vanishing from the
|
|
673
753
|
* tally. Empty when nothing.
|
|
@@ -677,6 +757,8 @@ function groupTally(sessions) {
|
|
|
677
757
|
const idle = sessions.filter(s => s.status === 'idle').length;
|
|
678
758
|
const waiting = sessions.filter(s => s.status === 'input_required').length;
|
|
679
759
|
const queued = sessions.filter(s => s.status === 'queued').length;
|
|
760
|
+
const closed = sessions.filter(s => s.status === 'closed').length;
|
|
761
|
+
const abandoned = sessions.filter(s => s.status === 'abandoned').length;
|
|
680
762
|
const unknown = sessions.filter(s => s.status === 'unknown').length;
|
|
681
763
|
const parts = [];
|
|
682
764
|
if (running)
|
|
@@ -687,6 +769,10 @@ function groupTally(sessions) {
|
|
|
687
769
|
parts.push(`${waiting} waiting`);
|
|
688
770
|
if (queued)
|
|
689
771
|
parts.push(`${queued} queued`);
|
|
772
|
+
if (closed)
|
|
773
|
+
parts.push(`${closed} closed`);
|
|
774
|
+
if (abandoned)
|
|
775
|
+
parts.push(`${abandoned} abandoned`);
|
|
690
776
|
if (unknown)
|
|
691
777
|
parts.push(`${unknown} unknown`);
|
|
692
778
|
return parts.join(' · ');
|
|
@@ -958,6 +1044,21 @@ async function renderSessionPreview(query, scope) {
|
|
|
958
1044
|
console.log(chalk.gray(`No session matches "${query}".`));
|
|
959
1045
|
return;
|
|
960
1046
|
}
|
|
1047
|
+
// Lead with the live status when the session is still running, so the preview
|
|
1048
|
+
// says working / waiting / idle up front — not just the historical transcript.
|
|
1049
|
+
let live;
|
|
1050
|
+
try {
|
|
1051
|
+
live = indexActiveBySessionId(await getActiveSessions()).get(session.id);
|
|
1052
|
+
}
|
|
1053
|
+
catch { /* plain preview on any probe failure */ }
|
|
1054
|
+
if (live) {
|
|
1055
|
+
const { glyph } = liveGlyphAndPreview(live);
|
|
1056
|
+
const word = liveStatusWord(live) || live.status;
|
|
1057
|
+
const needsYou = live.status === 'input_required' || live.activity === 'waiting_input';
|
|
1058
|
+
const reason = live.awaitingReason ? ` (${live.awaitingReason.replace('_', ' ')})` : '';
|
|
1059
|
+
const suffix = needsYou ? chalk.yellow(` ← needs you${reason}`) : '';
|
|
1060
|
+
console.log(`${glyph} ${statusColor(live.status)(word)}${suffix}`);
|
|
1061
|
+
}
|
|
961
1062
|
console.log(buildPreview(session));
|
|
962
1063
|
}
|
|
963
1064
|
/** Main action handler for `agents sessions`. Routes to picker, table, or single-session render. */
|
|
@@ -1322,12 +1423,12 @@ function metaSignals(s) {
|
|
|
1322
1423
|
};
|
|
1323
1424
|
}
|
|
1324
1425
|
/** One flat table row:
|
|
1325
|
-
* shortId · agent · version · project · [glyph] label·doing · [ticket] · [wt] · time
|
|
1426
|
+
* shortId · agent · version · model · project · [glyph] label·doing · [ticket] · [wt] · time
|
|
1326
1427
|
* `doing` is the live preview when running, else the topic. The `ticket` column
|
|
1327
1428
|
* (tracker/PR ref, pulled out of the badge blob so refs align) is only rendered
|
|
1328
1429
|
* when `showTicket` — otherwise a listing with no refs would waste a column of
|
|
1329
1430
|
* dashes and needlessly truncate the topic. Worktree stays a trailing badge. */
|
|
1330
|
-
function flatSessionRow(session, live, showTicket = false, cols = {}) {
|
|
1431
|
+
export function flatSessionRow(session, live, showTicket = false, cols = {}) {
|
|
1331
1432
|
const agentColor = colorAgent(session.agent);
|
|
1332
1433
|
const when = formatRelativeTime(session.lastActivity ?? session.timestamp);
|
|
1333
1434
|
const project = session.project || '-';
|
|
@@ -1350,19 +1451,31 @@ function flatSessionRow(session, live, showTicket = false, cols = {}) {
|
|
|
1350
1451
|
: '';
|
|
1351
1452
|
const TICKET_W = 10;
|
|
1352
1453
|
const ticketCell = showTicket
|
|
1353
|
-
? chalk.blue(padToWidth(truncateToWidth(ticketLabel(session) || '-', TICKET_W), TICKET_W + 1))
|
|
1454
|
+
? chalk.blue(linkTicketCell(session, padToWidth(truncateToWidth(ticketLabel(session) || '-', TICKET_W), TICKET_W + 1)))
|
|
1354
1455
|
: '';
|
|
1456
|
+
// Live status word (working / waiting / idle) next to the glyph — the default
|
|
1457
|
+
// list is no longer a bare glyph. Empty (zero width) for resting rows.
|
|
1458
|
+
const { cell: statusCell, width: statusW } = liveStatusCell(live);
|
|
1355
1459
|
const glyphW = glyph ? 2 : 0;
|
|
1356
1460
|
const machineW = cols.showMachine ? machineColW : 0;
|
|
1357
1461
|
const ticketW = showTicket ? TICKET_W + 1 : 0;
|
|
1358
1462
|
const wtW = wt ? stringWidth(wt) + 1 : 0;
|
|
1359
|
-
const
|
|
1463
|
+
const width = terminalWidth();
|
|
1464
|
+
const requestedModelW = cols.showModel ? (cols.modelWidth ?? PICKER_MODEL_MAX) : 0;
|
|
1465
|
+
const fixedW = (10 + 9 + 8 + 16) + glyphW + statusW + machineW + ticketW + wtW + stringWidth(when) + 1;
|
|
1466
|
+
const modelSlack = width - fixedW - 16;
|
|
1467
|
+
const modelW = requestedModelW <= modelSlack
|
|
1468
|
+
? requestedModelW
|
|
1469
|
+
: modelSlack >= PICKER_MODEL_MIN ? modelSlack : 0;
|
|
1470
|
+
const topicW = Math.max(16, width - fixedW - modelW);
|
|
1360
1471
|
return (chalk.white(padToWidth(truncateToWidth(session.shortId, 9), 10)) +
|
|
1361
1472
|
agentColor(padToWidth(truncateToWidth(session.agent, 8), 9)) +
|
|
1362
1473
|
chalk.yellow(padToWidth(truncateToWidth(session.version || '-', 7), 8)) +
|
|
1474
|
+
(modelW ? chalk.yellow(padToWidth(truncateToWidth(modelLabel(session.model), modelW - 1), modelW)) : '') +
|
|
1363
1475
|
machineCell +
|
|
1364
|
-
chalk.cyan(padToWidth(truncateToWidth(project, 14), 16)) +
|
|
1476
|
+
chalk.cyan(linkCwdCell(session, padToWidth(truncateToWidth(project, 14), 16))) +
|
|
1365
1477
|
(glyph ? glyph + ' ' : '') +
|
|
1478
|
+
statusCell +
|
|
1366
1479
|
renderTopicCell(label, doing, '', topicW, topicW) +
|
|
1367
1480
|
ticketCell +
|
|
1368
1481
|
(wt ? wt + ' ' : '') +
|
|
@@ -1383,13 +1496,15 @@ function treeSessionRow(session, live) {
|
|
|
1383
1496
|
const badges = signalBadges(metaSignals(session));
|
|
1384
1497
|
const badgeW = badges ? stringWidth(badges) + 1 : 0;
|
|
1385
1498
|
const head = label ? `${label} · ${topic}` : topic;
|
|
1499
|
+
const { cell: statusCell, width: statusW } = liveStatusCell(live);
|
|
1386
1500
|
const glyphW = glyph ? 2 : 0;
|
|
1387
|
-
const topicW = Math.max(12, terminalWidth() - (2 + 9 + 8) - glyphW - badgeW - stringWidth(when) - 1);
|
|
1501
|
+
const topicW = Math.max(12, terminalWidth() - (2 + 9 + 8) - glyphW - statusW - badgeW - stringWidth(when) - 1);
|
|
1388
1502
|
return (' ' +
|
|
1389
1503
|
chalk.dim(padToWidth(session.shortId, 9)) +
|
|
1390
1504
|
agentColor(padToWidth(truncateToWidth(session.agent, 7), 8)) +
|
|
1391
1505
|
(badges ? badges + ' ' : '') +
|
|
1392
1506
|
(glyph ? glyph + ' ' : '') +
|
|
1507
|
+
statusCell +
|
|
1393
1508
|
padToWidth(chalk.white(truncateToWidth(head, topicW)), topicW) +
|
|
1394
1509
|
' ' + chalk.gray(when));
|
|
1395
1510
|
}
|
|
@@ -1506,7 +1621,9 @@ function printSessionTable(sessions, hiddenCount = 0, tree = false, liveIndex) {
|
|
|
1506
1621
|
console.log();
|
|
1507
1622
|
first = false;
|
|
1508
1623
|
const group = byDir.get(key);
|
|
1509
|
-
|
|
1624
|
+
const cwd = group.find((s) => s.cwd && !s._remote)?.cwd;
|
|
1625
|
+
const header = cwd ? linkPath(cwd, shortCwd(key)) : shortCwd(key);
|
|
1626
|
+
console.log(`${chalk.cyan.bold(header)} ${chalk.gray(`(${group.length})`)}`);
|
|
1510
1627
|
for (const s of group)
|
|
1511
1628
|
console.log(treeSessionRow(s, liveIndex?.get(s.id)));
|
|
1512
1629
|
}
|
|
@@ -1702,12 +1819,18 @@ function renderTopicCell(label, topic, query, visibleWidth, paddedWidth) {
|
|
|
1702
1819
|
const PICKER_MACHINE_W = 11;
|
|
1703
1820
|
const PICKER_MACHINE_MIN = 8;
|
|
1704
1821
|
const PICKER_MACHINE_MAX = 18;
|
|
1822
|
+
const PICKER_MODEL_MIN = 6;
|
|
1823
|
+
const PICKER_MODEL_MAX = 13;
|
|
1705
1824
|
/** Column width that shows every compacted hostname in `machines` whole (one
|
|
1706
1825
|
* trailing space for separation), bounded by MIN/MAX. */
|
|
1707
1826
|
function machineColumnWidth(machines, label) {
|
|
1708
1827
|
const widest = machines.reduce((w, m) => Math.max(w, stringWidth(label(m))), 0);
|
|
1709
1828
|
return Math.min(PICKER_MACHINE_MAX, Math.max(PICKER_MACHINE_MIN, widest + 1));
|
|
1710
1829
|
}
|
|
1830
|
+
function modelColumnWidth(sessions) {
|
|
1831
|
+
const widest = sessions.reduce((width, session) => (Math.max(width, session.model ? stringWidth(modelLabel(session.model)) : 0)), 0);
|
|
1832
|
+
return Math.min(PICKER_MODEL_MAX, Math.max(PICKER_MODEL_MIN, widest + 1));
|
|
1833
|
+
}
|
|
1711
1834
|
/**
|
|
1712
1835
|
* Compact display form for machine ids: strip the longest shared dash-delimited
|
|
1713
1836
|
* prefix so "yosemite-s0"/"yosemite-s1" read as "s0"/"s1" while unrelated ids
|
|
@@ -1743,6 +1866,8 @@ export function pickerColumnsFor(sessions) {
|
|
|
1743
1866
|
showMachine: distinct.length > 1,
|
|
1744
1867
|
machineLabel,
|
|
1745
1868
|
machineWidth: machineColumnWidth(distinct, machineLabel),
|
|
1869
|
+
showModel: sessions.some((s) => !!s.model),
|
|
1870
|
+
modelWidth: modelColumnWidth(sessions),
|
|
1746
1871
|
showTicket: sessions.some((s) => ticketLabel(s) !== ''),
|
|
1747
1872
|
};
|
|
1748
1873
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents set` — ergonomic top-level setter for per-agent/version run defaults.
|
|
3
|
+
*
|
|
4
|
+
* `agents set claude@2.1.220 --model opus-5` pins the default model (and/or
|
|
5
|
+
* mode) that `agents run` uses for that agent version. It reads and writes the
|
|
6
|
+
* same store as `agents defaults run set` (agents.yaml -> run.defaults), so the
|
|
7
|
+
* two stay consistent — `set` is just the short front door.
|
|
8
|
+
*
|
|
9
|
+
* agents set # list every configured default
|
|
10
|
+
* agents set claude@2.1.220 # show the default for one version
|
|
11
|
+
* agents set claude@2.1.220 --model opus-5
|
|
12
|
+
* agents set 'claude:*' --mode auto --model opus
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from 'commander';
|
|
15
|
+
export declare function registerSetCommand(program: Command): void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents set` — ergonomic top-level setter for per-agent/version run defaults.
|
|
3
|
+
*
|
|
4
|
+
* `agents set claude@2.1.220 --model opus-5` pins the default model (and/or
|
|
5
|
+
* mode) that `agents run` uses for that agent version. It reads and writes the
|
|
6
|
+
* same store as `agents defaults run set` (agents.yaml -> run.defaults), so the
|
|
7
|
+
* two stay consistent — `set` is just the short front door.
|
|
8
|
+
*
|
|
9
|
+
* agents set # list every configured default
|
|
10
|
+
* agents set claude@2.1.220 # show the default for one version
|
|
11
|
+
* agents set claude@2.1.220 --model opus-5
|
|
12
|
+
* agents set 'claude:*' --mode auto --model opus
|
|
13
|
+
*/
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { setHelpSections } from '../lib/help.js';
|
|
16
|
+
import { formatRunDefaultEntry, listRunDefaults, parseRunDefaultSelector, setRunDefault, } from '../lib/run-defaults.js';
|
|
17
|
+
export function registerSetCommand(program) {
|
|
18
|
+
const set = program
|
|
19
|
+
.command('set [selector]')
|
|
20
|
+
.description('Set the default model/mode an agent version uses for `agents run`')
|
|
21
|
+
.option('--model <model>', 'Default model or model alias, forwarded via --model')
|
|
22
|
+
.option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
|
|
23
|
+
.action((selector, options) => {
|
|
24
|
+
try {
|
|
25
|
+
const hasFlags = options.model !== undefined || options.mode !== undefined;
|
|
26
|
+
if (!selector) {
|
|
27
|
+
if (hasFlags) {
|
|
28
|
+
throw new Error('Selector is required when passing --model/--mode. Example: agents set claude@2.1.220 --model opus-5');
|
|
29
|
+
}
|
|
30
|
+
const entries = listRunDefaults();
|
|
31
|
+
if (entries.length === 0) {
|
|
32
|
+
console.log(chalk.gray('No agent defaults configured.'));
|
|
33
|
+
console.log(chalk.gray('Set one with: agents set claude@2.1.220 --model opus-5'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk.bold('Agent Defaults\n'));
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!hasFlags) {
|
|
43
|
+
const parsed = parseRunDefaultSelector(selector);
|
|
44
|
+
const entry = listRunDefaults().find((e) => e.selector === parsed.selector);
|
|
45
|
+
if (!entry || (!entry.defaults.mode && !entry.defaults.model)) {
|
|
46
|
+
console.log(chalk.gray(`No default set for ${parsed.selector}.`));
|
|
47
|
+
console.log(chalk.gray(`Set one with: agents set ${selector} --model <model>`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const entry = setRunDefault(selector, {
|
|
54
|
+
...(options.mode !== undefined ? { mode: options.mode } : {}),
|
|
55
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
56
|
+
});
|
|
57
|
+
console.log(chalk.green('Set default:'));
|
|
58
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(chalk.red(err.message));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
setHelpSections(set, {
|
|
66
|
+
examples: `
|
|
67
|
+
agents set claude@2.1.220 --model opus-5
|
|
68
|
+
agents set 'claude:*' --mode auto --model opus
|
|
69
|
+
agents set claude@2.1.220
|
|
70
|
+
agents set
|
|
71
|
+
`,
|
|
72
|
+
notes: `
|
|
73
|
+
Selectors use <agent>@<version> or <agent>:<version>; * matches all versions.
|
|
74
|
+
Exact selectors override wildcard selectors field by field.
|
|
75
|
+
Writes the same store as 'agents defaults run set'. Explicit flags on
|
|
76
|
+
'agents run' always win over configured defaults.
|
|
77
|
+
`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup fleet` — guided Tailscale device onboarding.
|
|
3
|
+
*
|
|
4
|
+
* This wizard is a front door over the existing fleet/device commands: sync
|
|
5
|
+
* Tailscale into the registry, choose auth, render SSH config, test devices, and
|
|
6
|
+
* optionally run the fleet updater without reimplementing those subcommands.
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from 'commander';
|
|
9
|
+
interface SetupFleetOptions {
|
|
10
|
+
auth?: string;
|
|
11
|
+
bundle?: string;
|
|
12
|
+
bundleKey?: string;
|
|
13
|
+
yes?: boolean;
|
|
14
|
+
writeSshConfig?: boolean;
|
|
15
|
+
testConnectivity?: boolean;
|
|
16
|
+
fleetUpdate?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function runFleetSetupWizard(opts?: SetupFleetOptions): Promise<boolean>;
|
|
19
|
+
/** Register `agents setup fleet` under the parent `setup` command. */
|
|
20
|
+
export declare function registerSetupFleetCommand(setupCmd: Command): void;
|
|
21
|
+
export {};
|