@phnx-labs/agents-cli 1.20.34 → 1.20.36
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 +113 -0
- package/LICENSE +185 -21
- package/README.md +13 -5
- package/dist/commands/audit.d.ts +14 -0
- package/dist/commands/audit.js +68 -0
- package/dist/commands/browser.js +82 -8
- package/dist/commands/check.d.ts +15 -0
- package/dist/commands/check.js +84 -0
- package/dist/commands/cloud.js +143 -3
- package/dist/commands/computer-actions.d.ts +15 -0
- package/dist/commands/computer-actions.js +120 -22
- package/dist/commands/computer.js +81 -0
- package/dist/commands/cost.js +17 -12
- package/dist/commands/daemon.js +4 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +54 -97
- package/dist/commands/events.d.ts +16 -0
- package/dist/commands/events.js +173 -0
- package/dist/commands/exec.d.ts +19 -0
- package/dist/commands/exec.js +283 -12
- package/dist/commands/hosts.js +37 -29
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +62 -16
- package/dist/commands/lock.d.ts +12 -0
- package/dist/commands/lock.js +70 -0
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +139 -0
- package/dist/commands/message.d.ts +15 -0
- package/dist/commands/message.js +56 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.js +27 -8
- package/dist/commands/permissions.js +9 -2
- package/dist/commands/repo.d.ts +34 -0
- package/dist/commands/repo.js +243 -65
- package/dist/commands/resource-view.d.ts +20 -0
- package/dist/commands/resource-view.js +90 -28
- package/dist/commands/routines.js +101 -5
- package/dist/commands/secrets-migrate.js +106 -57
- package/dist/commands/secrets.d.ts +31 -18
- package/dist/commands/secrets.js +196 -91
- package/dist/commands/serve.d.ts +10 -0
- package/dist/commands/serve.js +37 -0
- package/dist/commands/sessions-inject.d.ts +14 -0
- package/dist/commands/sessions-inject.js +111 -0
- package/dist/commands/sessions-picker.d.ts +2 -0
- package/dist/commands/sessions-picker.js +24 -3
- package/dist/commands/sessions-resume.d.ts +2 -0
- package/dist/commands/sessions-resume.js +221 -0
- package/dist/commands/sessions-tail.d.ts +10 -0
- package/dist/commands/sessions-tail.js +11 -0
- package/dist/commands/sessions.d.ts +143 -1
- package/dist/commands/sessions.js +628 -80
- package/dist/commands/setup.js +4 -2
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.js +156 -4
- package/dist/commands/teams.js +217 -0
- package/dist/commands/versions.js +2 -4
- package/dist/commands/view.d.ts +3 -0
- package/dist/commands/view.js +19 -8
- package/dist/commands/wallet.d.ts +6 -0
- package/dist/commands/wallet.js +22 -5
- package/dist/commands/watchdog.d.ts +18 -0
- package/dist/commands/watchdog.js +238 -0
- package/dist/index.js +82 -32
- package/dist/lib/agents.js +30 -9
- package/dist/lib/audit/log.d.ts +92 -0
- package/dist/lib/audit/log.js +177 -0
- package/dist/lib/auto-pull.js +2 -1
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +18 -7
- package/dist/lib/browser/drivers/ssh.d.ts +8 -0
- package/dist/lib/browser/drivers/ssh.js +77 -16
- package/dist/lib/browser/har.d.ts +84 -0
- package/dist/lib/browser/har.js +77 -0
- package/dist/lib/browser/ipc.d.ts +3 -0
- package/dist/lib/browser/ipc.js +37 -12
- package/dist/lib/browser/profiles.d.ts +1 -1
- package/dist/lib/browser/profiles.js +8 -10
- package/dist/lib/browser/refs.d.ts +65 -0
- package/dist/lib/browser/refs.js +73 -1
- package/dist/lib/browser/runtime-state.js +1 -0
- package/dist/lib/browser/service.d.ts +38 -2
- package/dist/lib/browser/service.js +183 -23
- package/dist/lib/browser/types.d.ts +14 -1
- package/dist/lib/budget/live-cloud.d.ts +42 -0
- package/dist/lib/budget/live-cloud.js +79 -0
- package/dist/lib/budget/live-team.d.ts +31 -0
- package/dist/lib/budget/live-team.js +115 -0
- package/dist/lib/cloud/codex.js +4 -0
- package/dist/lib/cloud/rush.d.ts +12 -1
- package/dist/lib/cloud/rush.js +13 -3
- package/dist/lib/cloud/types.d.ts +9 -0
- package/dist/lib/computer/dispatch.d.ts +8 -0
- package/dist/lib/computer/dispatch.js +125 -0
- package/dist/lib/computer/loop.d.ts +62 -0
- package/dist/lib/computer/loop.js +98 -0
- package/dist/lib/computer/model.d.ts +44 -0
- package/dist/lib/computer/model.js +157 -0
- package/dist/lib/concurrency.d.ts +19 -0
- package/dist/lib/concurrency.js +33 -0
- package/dist/lib/daemon.d.ts +92 -0
- package/dist/lib/daemon.js +222 -18
- package/dist/lib/devices/connect.d.ts +3 -2
- package/dist/lib/devices/connect.js +5 -3
- package/dist/lib/devices/registry.d.ts +7 -0
- package/dist/lib/devices/registry.js +24 -0
- package/dist/lib/devices/tailscale.js +1 -1
- package/dist/lib/drift.d.ts +52 -0
- package/dist/lib/drift.js +112 -0
- package/dist/lib/events.d.ts +10 -3
- package/dist/lib/events.js +101 -24
- package/dist/lib/exec.d.ts +17 -0
- package/dist/lib/exec.js +100 -12
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +56 -1
- package/dist/lib/hooks/cache.d.ts +6 -0
- package/dist/lib/hooks/cache.js +54 -12
- package/dist/lib/hooks.d.ts +27 -0
- package/dist/lib/hooks.js +135 -10
- package/dist/lib/hosts/dispatch.d.ts +15 -0
- package/dist/lib/hosts/dispatch.js +45 -13
- package/dist/lib/hosts/logs.d.ts +16 -0
- package/dist/lib/hosts/logs.js +74 -0
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.js +3 -1
- package/dist/lib/hosts/progress.d.ts +66 -0
- package/dist/lib/hosts/progress.js +125 -17
- package/dist/lib/hosts/ready.d.ts +51 -7
- package/dist/lib/hosts/ready.js +98 -25
- package/dist/lib/hosts/reconcile.d.ts +53 -0
- package/dist/lib/hosts/reconcile.js +81 -0
- package/dist/lib/hosts/registry.d.ts +19 -2
- package/dist/lib/hosts/registry.js +58 -2
- package/dist/lib/hosts/remote-cmd.d.ts +62 -1
- package/dist/lib/hosts/remote-cmd.js +70 -1
- package/dist/lib/hosts/remote-os.d.ts +17 -0
- package/dist/lib/hosts/remote-os.js +30 -0
- package/dist/lib/hosts/session-index.d.ts +34 -0
- package/dist/lib/hosts/session-index.js +56 -0
- package/dist/lib/hosts/tasks.d.ts +22 -0
- package/dist/lib/hosts/tasks.js +29 -0
- package/dist/lib/lock.d.ts +93 -0
- package/dist/lib/lock.js +207 -0
- package/dist/lib/loop.js +16 -1
- package/dist/lib/machine-id.d.ts +21 -0
- package/dist/lib/machine-id.js +26 -0
- package/dist/lib/mailbox-target.d.ts +36 -0
- package/dist/lib/mailbox-target.js +45 -0
- package/dist/lib/mailbox.d.ts +47 -0
- package/dist/lib/mailbox.js +194 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +24 -8
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.js +134 -26
- package/dist/lib/overdue.js +3 -0
- package/dist/lib/permissions.d.ts +4 -0
- package/dist/lib/permissions.js +35 -0
- package/dist/lib/picker.d.ts +26 -0
- package/dist/lib/picker.js +127 -0
- package/dist/lib/platform/exec.d.ts +46 -0
- package/dist/lib/platform/exec.js +74 -0
- package/dist/lib/platform/process.d.ts +31 -0
- package/dist/lib/platform/process.js +34 -1
- package/dist/lib/platform/winpath.js +2 -0
- package/dist/lib/plugin-marketplace.d.ts +30 -0
- package/dist/lib/plugin-marketplace.js +215 -2
- package/dist/lib/plugins.d.ts +5 -0
- package/dist/lib/plugins.js +61 -10
- package/dist/lib/profiles.d.ts +25 -0
- package/dist/lib/profiles.js +22 -6
- package/dist/lib/pty-client.js +2 -1
- package/dist/lib/rotate.d.ts +61 -0
- package/dist/lib/rotate.js +52 -0
- package/dist/lib/routines.d.ts +40 -2
- package/dist/lib/routines.js +66 -8
- package/dist/lib/runner.d.ts +11 -2
- package/dist/lib/runner.js +49 -7
- package/dist/lib/scheduler.js +6 -1
- package/dist/lib/secrets/bundles.d.ts +60 -4
- package/dist/lib/secrets/bundles.js +131 -12
- package/dist/lib/secrets/filestore.d.ts +3 -0
- package/dist/lib/secrets/filestore.js +42 -16
- package/dist/lib/secrets/index.d.ts +43 -2
- package/dist/lib/secrets/index.js +102 -3
- package/dist/lib/secrets/mcp.d.ts +93 -0
- package/dist/lib/secrets/mcp.js +205 -0
- package/dist/lib/secrets/remote.js +12 -5
- package/dist/lib/secrets/sync.js +83 -4
- package/dist/lib/secrets/windows.js +14 -3
- package/dist/lib/serve/data.d.ts +81 -0
- package/dist/lib/serve/data.js +91 -0
- package/dist/lib/serve/page.d.ts +7 -0
- package/dist/lib/serve/page.js +140 -0
- package/dist/lib/serve/server.d.ts +46 -0
- package/dist/lib/serve/server.js +115 -0
- package/dist/lib/session/active.d.ts +61 -0
- package/dist/lib/session/active.js +196 -17
- package/dist/lib/session/discover.d.ts +37 -0
- package/dist/lib/session/discover.js +204 -27
- package/dist/lib/session/inject.d.ts +18 -0
- package/dist/lib/session/inject.js +21 -0
- package/dist/lib/session/parse.d.ts +6 -0
- package/dist/lib/session/parse.js +286 -2
- package/dist/lib/session/pid-registry.d.ts +22 -0
- package/dist/lib/session/pid-registry.js +104 -0
- package/dist/lib/session/provenance.d.ts +14 -2
- package/dist/lib/session/provenance.js +39 -8
- package/dist/lib/session/remote-active.d.ts +26 -0
- package/dist/lib/session/remote-active.js +153 -0
- package/dist/lib/session/remote-list.d.ts +51 -0
- package/dist/lib/session/remote-list.js +213 -0
- package/dist/lib/session/remote.d.ts +7 -1
- package/dist/lib/session/remote.js +18 -8
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/sync/config.d.ts +1 -7
- package/dist/lib/session/sync/config.js +4 -11
- package/dist/lib/session/types.d.ts +18 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +44 -6
- package/dist/lib/shims.js +143 -57
- package/dist/lib/ssh-exec.d.ts +26 -3
- package/dist/lib/ssh-exec.js +47 -3
- package/dist/lib/ssh-tunnel.d.ts +24 -5
- package/dist/lib/ssh-tunnel.js +62 -63
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +8 -0
- package/dist/lib/startup/command-registry.js +17 -1
- package/dist/lib/state.d.ts +13 -0
- package/dist/lib/state.js +103 -9
- package/dist/lib/sync-umbrella.d.ts +14 -7
- package/dist/lib/sync-umbrella.js +17 -9
- package/dist/lib/teams/forEach.d.ts +110 -0
- package/dist/lib/teams/forEach.js +186 -0
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/pr-watch.d.ts +226 -0
- package/dist/lib/teams/pr-watch.js +371 -0
- package/dist/lib/teams/registry.js +25 -9
- package/dist/lib/teams/supervisor.d.ts +14 -1
- package/dist/lib/teams/supervisor.js +19 -0
- package/dist/lib/teams/worktree.d.ts +9 -0
- package/dist/lib/teams/worktree.js +32 -0
- package/dist/lib/terminal/backends/ghostty.d.ts +6 -0
- package/dist/lib/terminal/backends/ghostty.js +69 -0
- package/dist/lib/terminal/backends/index.d.ts +19 -0
- package/dist/lib/terminal/backends/index.js +31 -0
- package/dist/lib/terminal/backends/iterm.d.ts +6 -0
- package/dist/lib/terminal/backends/iterm.js +62 -0
- package/dist/lib/terminal/backends/tmux.d.ts +14 -0
- package/dist/lib/terminal/backends/tmux.js +23 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +31 -0
- package/dist/lib/terminal/backends/vscodium-agent.js +72 -0
- package/dist/lib/terminal/engine.d.ts +39 -0
- package/dist/lib/terminal/engine.js +54 -0
- package/dist/lib/terminal/index.d.ts +17 -0
- package/dist/lib/terminal/index.js +8 -0
- package/dist/lib/terminal/inject.d.ts +204 -0
- package/dist/lib/terminal/inject.js +247 -0
- package/dist/lib/terminal/policy.d.ts +11 -0
- package/dist/lib/terminal/policy.js +11 -0
- package/dist/lib/terminal/quote.d.ts +11 -0
- package/dist/lib/terminal/quote.js +13 -0
- package/dist/lib/terminal/resolve.d.ts +64 -0
- package/dist/lib/terminal/resolve.js +90 -0
- package/dist/lib/terminal/shell.d.ts +6 -0
- package/dist/lib/terminal/shell.js +23 -0
- package/dist/lib/terminal/transport.d.ts +15 -0
- package/dist/lib/terminal/transport.js +40 -0
- package/dist/lib/terminal/types.d.ts +59 -0
- package/dist/lib/terminal/types.js +13 -0
- package/dist/lib/triggers/webhook.d.ts +85 -0
- package/dist/lib/triggers/webhook.js +141 -0
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/versions.d.ts +30 -3
- package/dist/lib/versions.js +129 -18
- package/dist/lib/watchdog/index.d.ts +3 -0
- package/dist/lib/watchdog/index.js +5 -0
- package/dist/lib/watchdog/read.d.ts +35 -0
- package/dist/lib/watchdog/read.js +149 -0
- package/dist/lib/watchdog/runner.d.ts +127 -0
- package/dist/lib/watchdog/runner.js +322 -0
- package/dist/lib/watchdog/watchdog.d.ts +40 -0
- package/dist/lib/watchdog/watchdog.js +166 -0
- package/dist/lib/watchdog/watchdogTail.d.ts +5 -0
- package/dist/lib/watchdog/watchdogTail.js +154 -0
- package/dist/lib/whats-new.d.ts +9 -0
- package/dist/lib/whats-new.js +35 -0
- package/dist/lib/workflows.d.ts +166 -0
- package/dist/lib/workflows.js +193 -0
- package/package.json +5 -4
|
@@ -18,6 +18,8 @@ import { type PluginResourceGroup } from '../lib/plugins.js';
|
|
|
18
18
|
/** Resource kinds the inspect command can drill into. */
|
|
19
19
|
declare const DRILLABLE_KINDS: readonly ["commands", "skills", "hooks", "mcp", "rules", "plugins", "workflows", "subagents"];
|
|
20
20
|
type DrillableKind = typeof DRILLABLE_KINDS[number];
|
|
21
|
+
/** Wrap a separator-joined value list under `prefix`, preserving a hanging indent. */
|
|
22
|
+
export declare function wrapJoined(prefix: string, items: string[], sep: string, width: number): string[];
|
|
21
23
|
interface ResourceItem {
|
|
22
24
|
name: string;
|
|
23
25
|
source: string;
|
package/dist/commands/inspect.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* names render as OSC-8 hyperlinks to the marker file (SKILL.md / WORKFLOW.md /
|
|
12
12
|
* AGENT.md / the file itself) so users can click straight to the source.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { execFileSync } from 'child_process';
|
|
15
15
|
import { addHostOption } from '../lib/hosts/option.js';
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import * as os from 'os';
|
|
@@ -31,6 +31,7 @@ import { discoverPlugins, discoverPluginsInDir, pluginResourceGroups } from '../
|
|
|
31
31
|
import { PLUGIN_GROUP_COLORS } from './plugins.js';
|
|
32
32
|
import { countSessionsInScope } from '../lib/session/discover.js';
|
|
33
33
|
import { damerauLevenshtein } from '../lib/fuzzy.js';
|
|
34
|
+
import { terminalWidth, truncateToWidth, stringWidth, stripAnsi } from '../lib/session/width.js';
|
|
34
35
|
/** Resource kinds the inspect command can drill into. */
|
|
35
36
|
const DRILLABLE_KINDS = [
|
|
36
37
|
'commands',
|
|
@@ -68,6 +69,44 @@ const SINGULAR_DRILL_ALIASES = {
|
|
|
68
69
|
const CAPABILITY_NAMES = [
|
|
69
70
|
'hooks', 'mcp', 'skills', 'commands', 'subagents', 'plugins', 'workflows', 'rules', 'allowlist',
|
|
70
71
|
];
|
|
72
|
+
/** Wrap a separator-joined value list under `prefix`, preserving a hanging indent. */
|
|
73
|
+
export function wrapJoined(prefix, items, sep, width) {
|
|
74
|
+
if (items.length === 0)
|
|
75
|
+
return [];
|
|
76
|
+
const continuation = ' '.repeat(stringWidth(prefix));
|
|
77
|
+
const fitItem = (item, linePrefix) => {
|
|
78
|
+
const room = Math.max(1, width - stringWidth(linePrefix));
|
|
79
|
+
return stringWidth(item) > room ? truncateToWidth(item, room) : item;
|
|
80
|
+
};
|
|
81
|
+
const lines = [];
|
|
82
|
+
let linePrefix = prefix;
|
|
83
|
+
let line = prefix;
|
|
84
|
+
let hasItem = false;
|
|
85
|
+
for (const raw of items) {
|
|
86
|
+
const item = fitItem(raw, linePrefix);
|
|
87
|
+
const candidate = hasItem ? `${line}${sep}${item}` : `${line}${item}`;
|
|
88
|
+
if (hasItem && stringWidth(candidate) > width) {
|
|
89
|
+
lines.push(line);
|
|
90
|
+
linePrefix = continuation;
|
|
91
|
+
line = continuation + fitItem(raw, linePrefix);
|
|
92
|
+
hasItem = true;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
line = candidate;
|
|
96
|
+
hasItem = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (hasItem)
|
|
100
|
+
lines.push(line);
|
|
101
|
+
return lines;
|
|
102
|
+
}
|
|
103
|
+
function printWrappedJoined(prefix, items, sep) {
|
|
104
|
+
for (const line of wrapJoined(prefix, items, sep, terminalWidth()))
|
|
105
|
+
console.log(line);
|
|
106
|
+
}
|
|
107
|
+
function truncateValueForPrefix(prefix, value) {
|
|
108
|
+
return truncateToWidth(stripAnsi(value), Math.max(1, terminalWidth() - stringWidth(prefix)));
|
|
109
|
+
}
|
|
71
110
|
// ─── Command registration ────────────────────────────────────────────────────
|
|
72
111
|
export function registerInspectCommand(program) {
|
|
73
112
|
const cmd = addHostOption(program.command('inspect <target>'))
|
|
@@ -425,7 +464,10 @@ function renderRepoSummary(repo, options) {
|
|
|
425
464
|
console.log(` ${'git'.padEnd(10)} ${git.branch}${dirty}${url}`);
|
|
426
465
|
if (git.lastCommit) {
|
|
427
466
|
const rel = git.lastCommit.relative ? ` ${chalk.gray(`(${git.lastCommit.relative})`)}` : '';
|
|
428
|
-
|
|
467
|
+
const prefix = ` ${''.padEnd(10)} ${chalk.gray('last'.padEnd(8))} `;
|
|
468
|
+
const sha = chalk.cyan(git.lastCommit.sha);
|
|
469
|
+
const subjectWidth = Math.max(8, terminalWidth() - stringWidth(prefix) - stringWidth(sha) - 2 - stringWidth(rel));
|
|
470
|
+
console.log(`${prefix}${sha} ${truncateToWidth(git.lastCommit.subject, subjectWidth)}${rel}`);
|
|
429
471
|
}
|
|
430
472
|
if (git.ahead !== null && git.behind !== null && (git.ahead > 0 || git.behind > 0)) {
|
|
431
473
|
sub('sync', `ahead ${git.ahead} ${chalk.gray('·')} behind ${git.behind}`);
|
|
@@ -440,10 +482,12 @@ function renderRepoSummary(repo, options) {
|
|
|
440
482
|
console.log(` ${'manifests'.padEnd(10)} ${manifests.join(', ')}`);
|
|
441
483
|
if (manifest) {
|
|
442
484
|
if (manifest.versions.length > 0) {
|
|
443
|
-
|
|
485
|
+
const prefix = ` ${''.padEnd(10)} ${chalk.gray('versions'.padEnd(8))} `;
|
|
486
|
+
printWrappedJoined(prefix, manifest.versions.map(v => `${v.agent} ${chalk.cyan(v.version)}`), chalk.gray(' · '));
|
|
444
487
|
}
|
|
445
488
|
if (manifest.strategies.length > 0) {
|
|
446
|
-
|
|
489
|
+
const prefix = ` ${''.padEnd(10)} ${chalk.gray('run'.padEnd(8))} `;
|
|
490
|
+
printWrappedJoined(prefix, manifest.strategies.map(s => `${s.agent}:${s.strategy}`), chalk.gray(' · '));
|
|
447
491
|
}
|
|
448
492
|
}
|
|
449
493
|
}
|
|
@@ -468,35 +512,34 @@ function renderRepoSummary(repo, options) {
|
|
|
468
512
|
export function repoGitInfo(root) {
|
|
469
513
|
const git = (args) => {
|
|
470
514
|
try {
|
|
471
|
-
return
|
|
472
|
-
.toString().trim();
|
|
515
|
+
return execFileSync('git', ['-C', root, ...args], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
|
|
473
516
|
}
|
|
474
517
|
catch {
|
|
475
518
|
return null;
|
|
476
519
|
}
|
|
477
520
|
};
|
|
478
|
-
const branch = git('rev-parse --abbrev-ref HEAD');
|
|
521
|
+
const branch = git(['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
479
522
|
if (branch === null)
|
|
480
523
|
return null;
|
|
481
|
-
// Read status WITHOUT trimming —
|
|
524
|
+
// Read status WITHOUT trimming — trimming would strip the leading
|
|
482
525
|
// space of the first porcelain line (`XY path`), corrupting the path slice.
|
|
483
526
|
let statusRaw;
|
|
484
527
|
try {
|
|
485
|
-
statusRaw =
|
|
528
|
+
statusRaw = execFileSync('git', ['-C', root, 'status', '--porcelain'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' });
|
|
486
529
|
}
|
|
487
530
|
catch {
|
|
488
531
|
statusRaw = null;
|
|
489
532
|
}
|
|
490
533
|
const dirtyFiles = statusRaw ? statusRaw.split('\n').filter(Boolean).map(l => l.slice(3)) : [];
|
|
491
534
|
let lastCommit = null;
|
|
492
|
-
const log = git('log -1 --format=%h%x1f%s%x1f%cr');
|
|
535
|
+
const log = git(['log', '-1', '--format=%h%x1f%s%x1f%cr']);
|
|
493
536
|
if (log) {
|
|
494
537
|
const [sha, subject, relative] = log.split('\x1f');
|
|
495
538
|
if (sha)
|
|
496
539
|
lastCommit = { sha, subject: subject ?? '', relative: relative ?? '' };
|
|
497
540
|
}
|
|
498
541
|
let ahead = null, behind = null;
|
|
499
|
-
const counts = git(
|
|
542
|
+
const counts = git(['rev-list', '--left-right', '--count', '@{upstream}...HEAD']);
|
|
500
543
|
if (counts) {
|
|
501
544
|
const [b, a] = counts.split(/\s+/).map(n => parseInt(n, 10));
|
|
502
545
|
if (Number.isFinite(b) && Number.isFinite(a)) {
|
|
@@ -504,7 +547,7 @@ export function repoGitInfo(root) {
|
|
|
504
547
|
ahead = a;
|
|
505
548
|
}
|
|
506
549
|
}
|
|
507
|
-
return { branch, dirty: dirtyFiles.length, dirtyFiles, url: git('remote get-url origin'), lastCommit, ahead, behind };
|
|
550
|
+
return { branch, dirty: dirtyFiles.length, dirtyFiles, url: git(['remote', 'get-url', 'origin']), lastCommit, ahead, behind };
|
|
508
551
|
}
|
|
509
552
|
// ─── Summary mode ────────────────────────────────────────────────────────────
|
|
510
553
|
async function renderSummary(agent, version, versionHome, options) {
|
|
@@ -551,8 +594,10 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
551
594
|
['alias', aliasPath],
|
|
552
595
|
['strategy', strategy],
|
|
553
596
|
];
|
|
554
|
-
for (const [k, v] of rows)
|
|
555
|
-
|
|
597
|
+
for (const [k, v] of rows) {
|
|
598
|
+
const prefix = ` ${k.padEnd(10)} `;
|
|
599
|
+
console.log(prefix + truncateValueForPrefix(prefix, v));
|
|
600
|
+
}
|
|
556
601
|
console.log('\n' + chalk.bold('Capabilities'));
|
|
557
602
|
for (const cap of CAPABILITY_NAMES) {
|
|
558
603
|
const res = capabilities[cap];
|
|
@@ -603,7 +648,8 @@ function renderItemList(header, jsonHead, kind, items, options) {
|
|
|
603
648
|
const tag = chalk.gray(`[${item.source}]`.padEnd(10));
|
|
604
649
|
console.log(` ${tag} ${termLink(chalk.cyan(item.name), item.linkTarget)}`);
|
|
605
650
|
if (item.description) {
|
|
606
|
-
|
|
651
|
+
const prefix = ' ';
|
|
652
|
+
console.log(prefix + chalk.gray(truncateToWidth(item.description, Math.max(1, terminalWidth() - stringWidth(prefix)))));
|
|
607
653
|
}
|
|
608
654
|
if (item.groups)
|
|
609
655
|
printGroupRows(item.groups);
|
|
@@ -1171,7 +1217,7 @@ function readFirstProseLine(p) {
|
|
|
1171
1217
|
}
|
|
1172
1218
|
// ─── OSC-8 + path helpers ────────────────────────────────────────────────────
|
|
1173
1219
|
function termLink(text, filePath) {
|
|
1174
|
-
if (!filePath)
|
|
1220
|
+
if (!filePath || !process.stdout.isTTY)
|
|
1175
1221
|
return text;
|
|
1176
1222
|
const url = `file://${filePath}`;
|
|
1177
1223
|
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents lock` — write a deterministic `agents.lock` for the resolved resource
|
|
3
|
+
* set at the project root; `agents lock --frozen` verifies the live resources
|
|
4
|
+
* match that lock EXACTLY and fails closed (non-zero exit + a clear diff) on any
|
|
5
|
+
* drift. The reproducible-CI slice of governance #337.
|
|
6
|
+
*
|
|
7
|
+
* This is OFF the default path: normal `agents add` / `agents sync` never touch
|
|
8
|
+
* the lock. Generation and verification only run when this command is invoked.
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
/** Register the `agents lock` command. */
|
|
12
|
+
export declare function registerLockCommand(program: Command): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents lock` — write a deterministic `agents.lock` for the resolved resource
|
|
3
|
+
* set at the project root; `agents lock --frozen` verifies the live resources
|
|
4
|
+
* match that lock EXACTLY and fails closed (non-zero exit + a clear diff) on any
|
|
5
|
+
* drift. The reproducible-CI slice of governance #337.
|
|
6
|
+
*
|
|
7
|
+
* This is OFF the default path: normal `agents add` / `agents sync` never touch
|
|
8
|
+
* the lock. Generation and verification only run when this command is invoked.
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { enumerateLockSources, buildLock, writeLock, readLock, verifyLock, lockDiffIsClean, resolveProjectRoot, LOCK_FILENAME, } from '../lib/lock.js';
|
|
12
|
+
/** Register the `agents lock` command. */
|
|
13
|
+
export function registerLockCommand(program) {
|
|
14
|
+
program
|
|
15
|
+
.command('lock')
|
|
16
|
+
.summary('Write or verify agents.lock — a SHA-256 manifest of resolved resources')
|
|
17
|
+
.description('Capture a deterministic SHA-256 per resolved resource file (commands, skills, hooks, rules, mcp, permissions, subagents — project > user > system > extras) into an agents.lock at the project root.\n\n' +
|
|
18
|
+
'Run bare to (re)generate the lock. Pass --frozen to VERIFY the live resolved resources match an existing agents.lock exactly — it exits non-zero with an added/removed/changed diff on any mismatch, and errors if no lock exists yet. That is the reproducible-CI gate; normal installs and syncs are unaffected.')
|
|
19
|
+
.option('--cwd <path>', 'Working directory to resolve resources and the project root from')
|
|
20
|
+
.option('--frozen', 'Verify resolved resources match agents.lock exactly; fail (non-zero) on any drift', false)
|
|
21
|
+
.action((opts) => {
|
|
22
|
+
runLock(opts);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function runLock(opts) {
|
|
26
|
+
const cwd = opts.cwd || process.cwd();
|
|
27
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
28
|
+
const sources = enumerateLockSources(cwd);
|
|
29
|
+
if (opts.frozen) {
|
|
30
|
+
verifyFrozen(projectRoot, sources);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const lock = buildLock(sources);
|
|
34
|
+
const written = writeLock(projectRoot, lock);
|
|
35
|
+
const count = Object.keys(lock.resources).length;
|
|
36
|
+
console.log(chalk.green(`Wrote ${LOCK_FILENAME}`) +
|
|
37
|
+
chalk.gray(` — ${count} resource file(s) → ${written}`));
|
|
38
|
+
}
|
|
39
|
+
function verifyFrozen(projectRoot, sources) {
|
|
40
|
+
let existing;
|
|
41
|
+
try {
|
|
42
|
+
existing = readLock(projectRoot);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
console.error(chalk.red(`agents lock --frozen: ${e.message}`));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!existing) {
|
|
50
|
+
console.error(chalk.red(`No ${LOCK_FILENAME} at ${projectRoot}.`));
|
|
51
|
+
console.error(chalk.gray('Generate one first: agents lock'));
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const diff = verifyLock(existing, sources);
|
|
56
|
+
if (lockDiffIsClean(diff)) {
|
|
57
|
+
const count = Object.keys(existing.resources).length;
|
|
58
|
+
console.log(chalk.green(`✓ ${count} resource file(s) match ${LOCK_FILENAME}`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.error(chalk.red(`✗ resources drifted from ${LOCK_FILENAME}:`));
|
|
62
|
+
for (const key of diff.changed)
|
|
63
|
+
console.error(chalk.yellow(` changed ${key}`));
|
|
64
|
+
for (const key of diff.added)
|
|
65
|
+
console.error(chalk.yellow(` added ${key}`));
|
|
66
|
+
for (const key of diff.removed)
|
|
67
|
+
console.error(chalk.yellow(` removed ${key}`));
|
|
68
|
+
console.error(chalk.gray('Re-lock once the change is intended: agents lock'));
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents logs` — unified, discoverable run-log viewer.
|
|
3
|
+
*
|
|
4
|
+
* Resolves a run across two substrates and shows (or `-f` follows) its log:
|
|
5
|
+
* - host-dispatch tasks (`agents run --host`) → combined-stdout log, offset-tailed
|
|
6
|
+
* - sessions (the local index) → transcript, tailed via the sessions tailer
|
|
7
|
+
*
|
|
8
|
+
* `[id]`/`--session` load directly (host task tried first, then session). With no
|
|
9
|
+
* id, `--host`/`--agent`/`--version` filter a merged candidate list; one match is
|
|
10
|
+
* shown, several open the fuzzy picker (or, non-TTY, print the list).
|
|
11
|
+
*
|
|
12
|
+
* Additive: `agents hosts logs` and `agents sessions tail` are unchanged and share
|
|
13
|
+
* the same underlying helpers (showHostTaskLog / streamSessionTail).
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from 'commander';
|
|
16
|
+
/** Register the top-level `agents logs` command. */
|
|
17
|
+
export declare function registerLogsCommand(program: Command): void;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents logs` — unified, discoverable run-log viewer.
|
|
3
|
+
*
|
|
4
|
+
* Resolves a run across two substrates and shows (or `-f` follows) its log:
|
|
5
|
+
* - host-dispatch tasks (`agents run --host`) → combined-stdout log, offset-tailed
|
|
6
|
+
* - sessions (the local index) → transcript, tailed via the sessions tailer
|
|
7
|
+
*
|
|
8
|
+
* `[id]`/`--session` load directly (host task tried first, then session). With no
|
|
9
|
+
* id, `--host`/`--agent`/`--version` filter a merged candidate list; one match is
|
|
10
|
+
* shown, several open the fuzzy picker (or, non-TTY, print the list).
|
|
11
|
+
*
|
|
12
|
+
* Additive: `agents hosts logs` and `agents sessions tail` are unchanged and share
|
|
13
|
+
* the same underlying helpers (showHostTaskLog / streamSessionTail).
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { discoverSessions, resolveSessionById } from '../lib/session/discover.js';
|
|
17
|
+
import { parseAgentFilter, renderSessionLog } from './sessions.js';
|
|
18
|
+
import { streamSessionTail, isTailable } from './sessions-tail.js';
|
|
19
|
+
import { showHostTaskLog } from '../lib/hosts/logs.js';
|
|
20
|
+
import { listTasks } from '../lib/hosts/tasks.js';
|
|
21
|
+
import { itemPicker } from '../lib/picker.js';
|
|
22
|
+
/** Compact one-line label used by both the picker and the non-TTY list. */
|
|
23
|
+
function candidateLabel(c) {
|
|
24
|
+
if (c.kind === 'task') {
|
|
25
|
+
const t = c.task;
|
|
26
|
+
const status = t.status === 'completed' ? chalk.green(t.status)
|
|
27
|
+
: t.status === 'failed' ? chalk.red(t.status)
|
|
28
|
+
: chalk.yellow(t.status);
|
|
29
|
+
return `${chalk.gray('task')} ${t.id.slice(0, 8).padEnd(9)} ${t.host.padEnd(14)} ${t.agent.padEnd(8)} ${status} ${t.prompt.slice(0, 40)}`;
|
|
30
|
+
}
|
|
31
|
+
const s = c.session;
|
|
32
|
+
const ver = s.version ? chalk.gray(`@${s.version}`) : '';
|
|
33
|
+
const title = s.label || s.topic || '';
|
|
34
|
+
return `${chalk.gray('sess')} ${s.shortId.padEnd(9)} ${(s.agent + ver).padEnd(14)} ${chalk.gray(s.timestamp.slice(0, 16))} ${title.slice(0, 40)}`;
|
|
35
|
+
}
|
|
36
|
+
/** Show a resolved session — follow (tail) or render its transcript. */
|
|
37
|
+
async function showSession(session, follow) {
|
|
38
|
+
if (follow) {
|
|
39
|
+
if (!isTailable(session.agent)) {
|
|
40
|
+
console.error(chalk.red(`Tailing is supported for claude and codex sessions only (got ${session.agent}).`));
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
await streamSessionTail(session, {});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await renderSessionLog(session);
|
|
47
|
+
}
|
|
48
|
+
async function showCandidate(c, follow) {
|
|
49
|
+
if (c.kind === 'task') {
|
|
50
|
+
const res = await showHostTaskLog(c.task.id, follow);
|
|
51
|
+
if (res.exitCode !== undefined)
|
|
52
|
+
process.exitCode = res.exitCode;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await showSession(c.session, follow);
|
|
56
|
+
}
|
|
57
|
+
/** Resolve an explicit id/--session: host task first, then a session. */
|
|
58
|
+
async function showById(id, follow) {
|
|
59
|
+
const hostRes = await showHostTaskLog(id, follow);
|
|
60
|
+
if (hostRes.found) {
|
|
61
|
+
if (hostRes.exitCode !== undefined)
|
|
62
|
+
process.exitCode = hostRes.exitCode;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const sessions = await discoverSessions({ all: true, limit: 5000 });
|
|
66
|
+
const matches = resolveSessionById(sessions, id);
|
|
67
|
+
if (matches.length === 0) {
|
|
68
|
+
console.error(chalk.red(`No run or session found matching "${id}".`));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
await showSession(matches[0], follow);
|
|
72
|
+
}
|
|
73
|
+
async function runLogs(id, opts) {
|
|
74
|
+
const follow = !!opts.follow;
|
|
75
|
+
const directId = opts.session ?? id;
|
|
76
|
+
if (directId) {
|
|
77
|
+
await showById(directId, follow);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const { agent, version } = parseAgentFilter(opts.agent);
|
|
81
|
+
const wantVersion = opts.version ?? version;
|
|
82
|
+
// Host tasks carry no session-index metadata; sessions carry no host tag.
|
|
83
|
+
// So --host scopes to dispatched tasks, and --version to sessions.
|
|
84
|
+
const candidates = [];
|
|
85
|
+
let tasks = listTasks();
|
|
86
|
+
if (opts.host)
|
|
87
|
+
tasks = tasks.filter((t) => t.host === opts.host);
|
|
88
|
+
if (agent)
|
|
89
|
+
tasks = tasks.filter((t) => t.agent === agent);
|
|
90
|
+
for (const t of tasks)
|
|
91
|
+
candidates.push({ kind: 'task', task: t });
|
|
92
|
+
if (!opts.host) {
|
|
93
|
+
const sessions = await discoverSessions({ agent, version: wantVersion, limit: 50 });
|
|
94
|
+
for (const s of sessions)
|
|
95
|
+
candidates.push({ kind: 'session', session: s });
|
|
96
|
+
}
|
|
97
|
+
if (candidates.length === 0) {
|
|
98
|
+
console.error(chalk.yellow('No matching runs. Dispatch one: agents run <agent> "<task>" [--host <name>]'));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (candidates.length === 1) {
|
|
102
|
+
await showCandidate(candidates[0], follow);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Multiple sessions matched → picker if interactive, else a list to pick from.
|
|
106
|
+
if (!process.stdin.isTTY) {
|
|
107
|
+
console.error(chalk.yellow(`${candidates.length} runs match. Pass an id or --session <id>:`));
|
|
108
|
+
for (const c of candidates.slice(0, 30))
|
|
109
|
+
console.error(' ' + candidateLabel(c));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const picked = await itemPicker({
|
|
113
|
+
message: 'Select a run to view its log:',
|
|
114
|
+
items: candidates,
|
|
115
|
+
filter: (query) => {
|
|
116
|
+
const q = query.trim().toLowerCase();
|
|
117
|
+
if (!q)
|
|
118
|
+
return candidates;
|
|
119
|
+
return candidates.filter((c) => candidateLabel(c).toLowerCase().includes(q));
|
|
120
|
+
},
|
|
121
|
+
labelFor: (c) => candidateLabel(c),
|
|
122
|
+
shortIdFor: (c) => (c.kind === 'task' ? c.task.id : c.session.shortId),
|
|
123
|
+
});
|
|
124
|
+
if (!picked)
|
|
125
|
+
return;
|
|
126
|
+
await showCandidate(picked.item, follow);
|
|
127
|
+
}
|
|
128
|
+
/** Register the top-level `agents logs` command. */
|
|
129
|
+
export function registerLogsCommand(program) {
|
|
130
|
+
program
|
|
131
|
+
.command('logs [id]')
|
|
132
|
+
.description('Show a run’s log — a host-dispatch task or a session. -f to follow a live one.')
|
|
133
|
+
.option('--host <name>', 'Scope to runs dispatched to a host')
|
|
134
|
+
.option('-a, --agent <agent>', 'Filter by agent (e.g. claude, codex@0.116.0)')
|
|
135
|
+
.option('--version <version>', 'Filter by agent version')
|
|
136
|
+
.option('--session <id>', 'Select a session/run by id (same as the positional id)')
|
|
137
|
+
.option('-f, --follow', 'Follow live output')
|
|
138
|
+
.action((id, opts) => runLogs(id, opts));
|
|
139
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents message <target> <text>` — send a message to a running agent.
|
|
3
|
+
*
|
|
4
|
+
* Unifies two delivery paths behind one verb:
|
|
5
|
+
* - a live LOCAL/teams/loop agent → enqueue into its file-spool mailbox; the
|
|
6
|
+
* PreToolUse hook injects it at the agent's next tool call.
|
|
7
|
+
* - a CLOUD task id → the existing provider follow-up path (was
|
|
8
|
+
* `agents cloud message`).
|
|
9
|
+
*
|
|
10
|
+
* Cross-host is handled one layer up: `--host <h>` routes the whole command over
|
|
11
|
+
* ssh via `REMOTE_PASSTHROUGH` (see src/lib/hosts/passthrough.ts), so the box is
|
|
12
|
+
* written on the host that actually owns the agent.
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from 'commander';
|
|
15
|
+
export declare function registerMessageCommand(program: Command): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getActiveSessions } from '../lib/session/active.js';
|
|
3
|
+
import { getTaskById, updateTaskStatus } from '../lib/cloud/store.js';
|
|
4
|
+
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
5
|
+
import { mailboxDir, enqueue } from '../lib/mailbox.js';
|
|
6
|
+
import { resolveMessageTarget } from '../lib/mailbox-target.js';
|
|
7
|
+
function die(msg, code = 1) {
|
|
8
|
+
console.error(chalk.red(msg));
|
|
9
|
+
process.exit(code);
|
|
10
|
+
}
|
|
11
|
+
export function registerMessageCommand(program) {
|
|
12
|
+
program
|
|
13
|
+
.command('message <target> <text>')
|
|
14
|
+
.description('Send a message to a running agent (delivered at its next tool call) or a cloud task.')
|
|
15
|
+
.option('--from <who>', 'Label recorded as the sender of this message')
|
|
16
|
+
.action(async (target, text, opts) => {
|
|
17
|
+
if (!target.trim()) {
|
|
18
|
+
die('Target must be a session/agent id or cloud task id. Run `agents sessions --active` to list running agents.');
|
|
19
|
+
}
|
|
20
|
+
const sessions = await getActiveSessions();
|
|
21
|
+
const res = resolveMessageTarget(target, sessions, (id) => getTaskById(id) != null);
|
|
22
|
+
switch (res.kind) {
|
|
23
|
+
case 'cloud': {
|
|
24
|
+
const task = getTaskById(res.id);
|
|
25
|
+
const provider = resolveProvider(task.provider);
|
|
26
|
+
try {
|
|
27
|
+
await provider.message(res.id, text);
|
|
28
|
+
updateTaskStatus(res.id, 'running');
|
|
29
|
+
console.log(chalk.green(`Message sent to cloud task ${res.id}. Agent is continuing.`));
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
die(err.message);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
case 'local': {
|
|
37
|
+
try {
|
|
38
|
+
const msgId = enqueue(mailboxDir(res.id), { to: res.id, text, from: opts.from });
|
|
39
|
+
console.log(chalk.green(`Queued message ${msgId} for ${res.id}. `) +
|
|
40
|
+
chalk.dim('The agent will see it at its next tool call.'));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
die(err.message);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
case 'ambiguous': {
|
|
48
|
+
const lines = res.candidates.map((c) => ` ${c.id} ${chalk.dim(c.label)}`).join('\n');
|
|
49
|
+
die(`"${target}" matches ${res.candidates.length} running agents:\n${lines}\nRe-run with a full id.`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
case 'none':
|
|
53
|
+
die(`No running agent or cloud task matches "${target}". List targets with \`agents sessions --active\`.`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -8,3 +8,6 @@
|
|
|
8
8
|
import type { Command } from 'commander';
|
|
9
9
|
/** Register the hidden `agents models` command. */
|
|
10
10
|
export declare function registerModelsCommand(program: Command): void;
|
|
11
|
+
export declare function formatModelSourceLine(kind: string, sourcePath: string, width?: number): string;
|
|
12
|
+
export declare function formatModelAliasLines(parts: string[], width?: number): string[];
|
|
13
|
+
export declare function formatModelSummaryLine(star: string, id: string, displayName?: string, alias?: string, width?: number): string;
|
package/dist/commands/models.js
CHANGED
|
@@ -11,6 +11,8 @@ import { homeDir } from '../lib/platform/index.js';
|
|
|
11
11
|
import { resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
12
12
|
import { listInstalledVersions, getGlobalDefault, resolveVersion, resolveVersionAlias } from '../lib/versions.js';
|
|
13
13
|
import { getModelCatalog, locateModelSource } from '../lib/models.js';
|
|
14
|
+
import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
|
|
15
|
+
import { wrapJoined } from './inspect.js';
|
|
14
16
|
const MODEL_CAPABLE_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'cursor', 'openclaw', 'antigravity', 'kimi'];
|
|
15
17
|
/**
|
|
16
18
|
* Agents that don't necessarily install under ~/.agents/versions (cursor ships
|
|
@@ -131,21 +133,19 @@ function printCatalog(agent, version, isDefault, options) {
|
|
|
131
133
|
console.log(chalk.yellow(` No models extracted from ${src.kind} at ${src.path}.`));
|
|
132
134
|
return;
|
|
133
135
|
}
|
|
134
|
-
console.log(chalk.gray(
|
|
136
|
+
console.log(chalk.gray(formatModelSourceLine(src.kind, shortPath(src.path))));
|
|
135
137
|
if (Object.keys(catalog.aliases).length > 0) {
|
|
136
138
|
const parts = Object.entries(catalog.aliases).map(([alias, id]) => `${chalk.cyan(alias)}=${id}`);
|
|
137
|
-
|
|
139
|
+
for (const line of formatModelAliasLines(parts))
|
|
140
|
+
console.log(line);
|
|
138
141
|
}
|
|
139
142
|
console.log();
|
|
140
143
|
for (const model of catalog.models) {
|
|
141
144
|
const star = model.isDefault ? chalk.green('*') : ' ';
|
|
142
|
-
|
|
143
|
-
? chalk.gray(` (${model.displayName})`)
|
|
144
|
-
: '';
|
|
145
|
-
const aliasTag = model.alias ? chalk.cyan(` [${model.alias}]`) : '';
|
|
146
|
-
console.log(` ${star} ${chalk.bold(model.id)}${display}${aliasTag}`);
|
|
145
|
+
console.log(formatModelSummaryLine(star, model.id, model.displayName, model.alias));
|
|
147
146
|
if (model.description) {
|
|
148
|
-
|
|
147
|
+
const descPrefix = ' ';
|
|
148
|
+
console.log(chalk.gray(descPrefix + truncateToWidth(model.description, Math.max(1, terminalWidth() - stringWidth(descPrefix)))));
|
|
149
149
|
}
|
|
150
150
|
if (options.cloud && model.perCloud) {
|
|
151
151
|
const c = model.perCloud;
|
|
@@ -175,3 +175,22 @@ function printCatalog(agent, version, isDefault, options) {
|
|
|
175
175
|
function shortPath(p) {
|
|
176
176
|
return p.replace(homeDir(), '~');
|
|
177
177
|
}
|
|
178
|
+
export function formatModelSourceLine(kind, sourcePath, width = terminalWidth()) {
|
|
179
|
+
const prefix = ` source: ${kind} (`;
|
|
180
|
+
const suffix = ')';
|
|
181
|
+
const room = Math.max(1, width - stringWidth(prefix) - stringWidth(suffix));
|
|
182
|
+
return prefix + truncateToWidth(sourcePath, room) + suffix;
|
|
183
|
+
}
|
|
184
|
+
export function formatModelAliasLines(parts, width = terminalWidth()) {
|
|
185
|
+
return wrapJoined(chalk.gray(' aliases: '), parts, ', ', width);
|
|
186
|
+
}
|
|
187
|
+
export function formatModelSummaryLine(star, id, displayName, alias, width = terminalWidth()) {
|
|
188
|
+
const display = displayName && displayName !== id ? ` (${displayName})` : '';
|
|
189
|
+
const aliasTag = alias ? ` [${alias}]` : '';
|
|
190
|
+
const prefix = ` ${star} `;
|
|
191
|
+
const plain = `${id}${display}${aliasTag}`;
|
|
192
|
+
if (stringWidth(plain) > Math.max(1, width - stringWidth(prefix))) {
|
|
193
|
+
return prefix + truncateToWidth(plain, Math.max(1, width - stringWidth(prefix)));
|
|
194
|
+
}
|
|
195
|
+
return prefix + chalk.bold(id) + chalk.gray(display) + chalk.cyan(aliasTag);
|
|
196
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
+
import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
|
|
3
4
|
import * as fs from 'fs';
|
|
4
5
|
import * as os from 'os';
|
|
5
6
|
import * as path from 'path';
|
|
@@ -191,9 +192,15 @@ When to use:
|
|
|
191
192
|
return;
|
|
192
193
|
}
|
|
193
194
|
console.log(chalk.bold('Installed Permission Sets:\n'));
|
|
195
|
+
const permCols = terminalWidth();
|
|
194
196
|
for (const perm of sets) {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
+
const prefix = ` ${chalk.cyan(perm.name)}`;
|
|
198
|
+
// Cap the description to the line so a prose-paragraph set (400+ chars) can't smear.
|
|
199
|
+
const budget = permCols - stringWidth(prefix) - 3;
|
|
200
|
+
const desc = perm.set.description && budget > 3
|
|
201
|
+
? ` - ${chalk.gray(truncateToWidth(perm.set.description, budget))}`
|
|
202
|
+
: '';
|
|
203
|
+
console.log(`${prefix}${desc}`);
|
|
197
204
|
console.log(` ${chalk.gray(`${perm.set.allow.length} allow, ${perm.set.deny?.length || 0} deny rules`)}`);
|
|
198
205
|
}
|
|
199
206
|
}
|
package/dist/commands/repo.d.ts
CHANGED
|
@@ -28,6 +28,36 @@ export declare function resourceUnit(file: string): {
|
|
|
28
28
|
kind: RepoResourceKind;
|
|
29
29
|
unit: string;
|
|
30
30
|
};
|
|
31
|
+
/** One resource kind + action, collapsed to a unit count (e.g. 2 new skills). */
|
|
32
|
+
export interface CountedResource {
|
|
33
|
+
action: ChangeAction;
|
|
34
|
+
kind: RepoResourceKind;
|
|
35
|
+
count: number;
|
|
36
|
+
}
|
|
37
|
+
/** Resource-level summary of a changed-file set. */
|
|
38
|
+
export interface ResourceDelta {
|
|
39
|
+
/** Counts in display order (grouped new -> changed -> removed, then RESOURCE_ORDER). */
|
|
40
|
+
counts: CountedResource[];
|
|
41
|
+
/** Total distinct resource units changed. */
|
|
42
|
+
total: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Collapse a changed-file set to distinct resource units, then count them by
|
|
46
|
+
* (action, kind). A skill whose three files all changed counts as one changed
|
|
47
|
+
* skill; a unit with mixed add+modify reads as a single change. This is the
|
|
48
|
+
* shared structured core behind every SYNC / CHANGES rendering.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resourceDelta(entries: {
|
|
51
|
+
action: ChangeAction;
|
|
52
|
+
file: string;
|
|
53
|
+
}[]): ResourceDelta;
|
|
54
|
+
/**
|
|
55
|
+
* Kind-only brief for the compact (wide-terminal) layout: `(24 skills, 9 commands, +20)`
|
|
56
|
+
* — the top `maxKinds` kinds by display order, with a `+N` remainder. Drops the
|
|
57
|
+
* new/changed verb (the arrow already carries direction) to stay short. Returns
|
|
58
|
+
* '' when there is nothing to summarize.
|
|
59
|
+
*/
|
|
60
|
+
export declare function deltaBrief(delta: ResourceDelta, maxKinds?: number): string;
|
|
31
61
|
/**
|
|
32
62
|
* Render a set of changed files as a resource-level summary, e.g.
|
|
33
63
|
* `2 new skills, 1 changed hook`. Counts distinct resource units (a skill whose
|
|
@@ -39,6 +69,10 @@ export declare function formatResourceDelta(entries: {
|
|
|
39
69
|
action: ChangeAction;
|
|
40
70
|
file: string;
|
|
41
71
|
}[], maxParts?: number): string;
|
|
72
|
+
/** `owner/repo` from a git URL, for the compact card header. Falls back to the raw URL. */
|
|
73
|
+
export declare function repoSlug(url: string): string;
|
|
74
|
+
/** Pack colored phrases into `, `-joined lines no wider than `width`. */
|
|
75
|
+
export declare function wrapPhrases(parts: string[], width: number): string[];
|
|
42
76
|
/** Register the `agents repo` command tree. */
|
|
43
77
|
export declare function registerRepoCommands(program: Command): void;
|
|
44
78
|
export {};
|