@phnx-labs/agents-cli 1.20.33 → 1.20.35
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 +29 -0
- package/README.md +32 -2
- package/dist/commands/computer-actions.d.ts +15 -0
- package/dist/commands/computer-actions.js +120 -22
- package/dist/commands/computer.d.ts +23 -0
- package/dist/commands/computer.js +45 -3
- package/dist/commands/cost.js +17 -12
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +108 -14
- package/dist/commands/events.d.ts +16 -0
- package/dist/commands/events.js +173 -0
- package/dist/commands/exec.js +6 -1
- package/dist/commands/hosts.js +28 -24
- package/dist/commands/import.js +1 -1
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +54 -7
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +139 -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/rules.js +1 -1
- package/dist/commands/secrets-migrate.js +23 -11
- package/dist/commands/secrets.d.ts +20 -0
- package/dist/commands/secrets.js +93 -17
- package/dist/commands/sessions-resume.d.ts +2 -0
- package/dist/commands/sessions-resume.js +209 -0
- package/dist/commands/sessions-tail.d.ts +10 -0
- package/dist/commands/sessions-tail.js +11 -0
- package/dist/commands/sessions.d.ts +73 -1
- package/dist/commands/sessions.js +273 -53
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/teams.js +70 -6
- package/dist/commands/versions.js +2 -1
- package/dist/commands/view.d.ts +42 -0
- package/dist/commands/view.js +213 -83
- package/dist/commands/wallet.d.ts +6 -0
- package/dist/commands/wallet.js +22 -5
- package/dist/index.js +61 -32
- package/dist/lib/acp/harnesses.d.ts +1 -1
- package/dist/lib/acp/harnesses.js +2 -2
- package/dist/lib/agents.d.ts +12 -0
- package/dist/lib/agents.js +145 -41
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.js +20 -0
- package/dist/lib/browser/drivers/ssh.d.ts +27 -0
- package/dist/lib/browser/drivers/ssh.js +94 -19
- package/dist/lib/browser/ipc.d.ts +3 -0
- package/dist/lib/browser/ipc.js +13 -9
- package/dist/lib/browser/service.js +71 -15
- package/dist/lib/daemon.d.ts +35 -0
- package/dist/lib/daemon.js +33 -5
- package/dist/lib/devices/connect.d.ts +3 -2
- package/dist/lib/devices/connect.js +5 -3
- package/dist/lib/doctor-diff.js +29 -2
- package/dist/lib/drift-sync.d.ts +43 -0
- package/dist/lib/drift-sync.js +179 -0
- package/dist/lib/events.d.ts +9 -2
- package/dist/lib/events.js +70 -11
- package/dist/lib/exec.d.ts +15 -0
- package/dist/lib/exec.js +43 -11
- package/dist/lib/hooks.js +8 -2
- package/dist/lib/hosts/dispatch.js +6 -7
- package/dist/lib/hosts/logs.d.ts +16 -0
- package/dist/lib/hosts/logs.js +45 -0
- package/dist/lib/hosts/progress.d.ts +66 -0
- package/dist/lib/hosts/progress.js +125 -17
- package/dist/lib/hosts/ready.d.ts +23 -2
- package/dist/lib/hosts/ready.js +35 -13
- package/dist/lib/hosts/reconcile.d.ts +53 -0
- package/dist/lib/hosts/reconcile.js +81 -0
- package/dist/lib/hosts/tasks.d.ts +8 -0
- package/dist/lib/hosts/tasks.js +14 -0
- package/dist/lib/permissions.d.ts +4 -0
- package/dist/lib/permissions.js +35 -0
- package/dist/lib/picker.d.ts +24 -0
- package/dist/lib/picker.js +124 -0
- package/dist/lib/platform/winpath.d.ts +31 -2
- package/dist/lib/platform/winpath.js +133 -24
- 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 +45 -4
- package/dist/lib/pwsh.d.ts +11 -0
- package/dist/lib/pwsh.js +13 -0
- 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 +42 -1
- package/dist/lib/secrets/agent.js +89 -11
- package/dist/lib/secrets/bundles.js +40 -9
- package/dist/lib/secrets/filestore.js +31 -1
- package/dist/lib/secrets/index.d.ts +33 -1
- package/dist/lib/secrets/index.js +90 -9
- package/dist/lib/secrets/windows.d.ts +74 -0
- package/dist/lib/secrets/windows.js +440 -0
- package/dist/lib/session/active.d.ts +7 -0
- package/dist/lib/session/active.js +11 -3
- package/dist/lib/session/discover.js +94 -0
- package/dist/lib/session/parse.d.ts +6 -0
- package/dist/lib/session/parse.js +283 -2
- package/dist/lib/session/pid-registry.d.ts +21 -0
- package/dist/lib/session/pid-registry.js +80 -0
- package/dist/lib/session/remote-active.d.ts +26 -0
- package/dist/lib/session/remote-active.js +141 -0
- package/dist/lib/session/remote.js +2 -6
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/sync/config.d.ts +8 -0
- package/dist/lib/session/sync/config.js +11 -2
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +30 -2
- package/dist/lib/shims.js +107 -50
- package/dist/lib/ssh-exec.d.ts +26 -3
- package/dist/lib/ssh-exec.js +45 -3
- package/dist/lib/ssh-tunnel.d.ts +24 -5
- package/dist/lib/ssh-tunnel.js +60 -62
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +3 -0
- package/dist/lib/startup/command-registry.js +6 -0
- package/dist/lib/sync-status.d.ts +102 -0
- package/dist/lib/sync-status.js +135 -0
- package/dist/lib/teams/agents.d.ts +24 -0
- package/dist/lib/teams/agents.js +30 -1
- package/dist/lib/teams/registry.js +25 -9
- 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 +18 -0
- package/dist/lib/terminal/backends/index.js +29 -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/engine.d.ts +39 -0
- package/dist/lib/terminal/engine.js +54 -0
- package/dist/lib/terminal/index.d.ts +14 -0
- package/dist/lib/terminal/index.js +5 -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/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/types.d.ts +27 -1
- package/dist/lib/usage.d.ts +30 -0
- package/dist/lib/usage.js +159 -2
- package/dist/lib/versions.d.ts +7 -3
- package/dist/lib/versions.js +10 -5
- package/dist/lib/whats-new.d.ts +9 -0
- package/dist/lib/whats-new.js +35 -0
- package/package.json +1 -1
package/dist/commands/view.js
CHANGED
|
@@ -23,9 +23,26 @@ import { listProfiles, profileSummary } from '../lib/profiles.js';
|
|
|
23
23
|
import { loadManifest, isStale } from '../lib/staleness/index.js';
|
|
24
24
|
import { confirm } from '@inquirer/prompts';
|
|
25
25
|
import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
26
|
+
import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
|
|
26
27
|
// Shown in the email column for agents that are signed in but expose no email
|
|
27
|
-
// address locally (Antigravity
|
|
28
|
+
// address locally (Antigravity stores an opaque OAuth grant with no identity).
|
|
28
29
|
const SIGNED_IN_LABEL = 'signed in';
|
|
30
|
+
/**
|
|
31
|
+
* Text for the account (email) column. Prefers the real email; otherwise, for a
|
|
32
|
+
* signed-in agent whose credential carries no email but does carry an opaque
|
|
33
|
+
* account id (Kimi's user_id), show `id:<user_id>` so distinct accounts read
|
|
34
|
+
* distinctly instead of a generic "signed in". Falls back to "signed in" when we
|
|
35
|
+
* have neither (Antigravity), and empty when signed out.
|
|
36
|
+
*/
|
|
37
|
+
function accountColumnLabel(info) {
|
|
38
|
+
if (!info)
|
|
39
|
+
return '';
|
|
40
|
+
if (info.email)
|
|
41
|
+
return info.email;
|
|
42
|
+
if (info.signedIn)
|
|
43
|
+
return info.accountId ? `id:${info.accountId}` : SIGNED_IN_LABEL;
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
29
46
|
/**
|
|
30
47
|
* Group profile summaries by their host harness, optionally filtered to a
|
|
31
48
|
* single agent. Profile YAMLs that fail validation are silently skipped by
|
|
@@ -51,6 +68,8 @@ function profileKindAndModel(model, planWidth) {
|
|
|
51
68
|
return `${kind} ${model}`;
|
|
52
69
|
}
|
|
53
70
|
function termLink(text, filePath) {
|
|
71
|
+
if (!process.stdout.isTTY)
|
|
72
|
+
return text;
|
|
54
73
|
const url = `file://${filePath}`;
|
|
55
74
|
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
56
75
|
}
|
|
@@ -135,13 +154,18 @@ function shouldRenderSection(key, filter) {
|
|
|
135
154
|
return filter[key] === true;
|
|
136
155
|
}
|
|
137
156
|
/** Trim a description to a column-friendly snippet. Strips newlines, collapses whitespace. */
|
|
138
|
-
function summarizeDescription(desc, maxLen = 80) {
|
|
157
|
+
export function summarizeDescription(desc, maxLen = 80) {
|
|
139
158
|
if (!desc)
|
|
140
159
|
return '';
|
|
141
160
|
const cleaned = desc.replace(/\s+/g, ' ').trim();
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
return truncateToWidth(cleaned, maxLen);
|
|
162
|
+
}
|
|
163
|
+
export function descriptionForPrefix(desc, prefix) {
|
|
164
|
+
if (!desc)
|
|
165
|
+
return '';
|
|
166
|
+
const visiblePrefix = prefix.replace(/\x1b]8;;[^\x1b]*(?:\x1b\\|\x07)/g, '');
|
|
167
|
+
const budget = Math.max(1, terminalWidth() - stringWidth(visiblePrefix));
|
|
168
|
+
return summarizeDescription(desc, budget);
|
|
145
169
|
}
|
|
146
170
|
function getProfileSummaries(filterAgentId) {
|
|
147
171
|
return listProfiles()
|
|
@@ -207,8 +231,10 @@ function renderHostClisSection(cwd) {
|
|
|
207
231
|
const status = installed ? chalk.green('installed') : chalk.red('missing ');
|
|
208
232
|
const linkedName = termLink(manifest.name.padEnd(nameWidth), linkTarget(manifest.path));
|
|
209
233
|
const tag = hostCliSourceTag(manifest.source);
|
|
210
|
-
const
|
|
211
|
-
|
|
234
|
+
const prefix = ` ${status} ${chalk.cyan(linkedName)} ${tag}`;
|
|
235
|
+
const descSnippet = descriptionForPrefix(manifest.description, `${prefix} `);
|
|
236
|
+
const desc = descSnippet ? chalk.gray(` ${descSnippet}`) : '';
|
|
237
|
+
console.log(prefix + desc);
|
|
212
238
|
}
|
|
213
239
|
if (anyMissing) {
|
|
214
240
|
console.log(chalk.gray(' Install missing with `agents cli install`'));
|
|
@@ -310,7 +336,10 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
310
336
|
return info;
|
|
311
337
|
return {
|
|
312
338
|
...info,
|
|
313
|
-
plan
|
|
339
|
+
// Prefer a plan the live usage fetch surfaced (Kimi reports membership
|
|
340
|
+
// tier in /usages; its local auth file has none) and fall back to the
|
|
341
|
+
// account-derived plan (Claude's billingType).
|
|
342
|
+
plan: usageByKey.get(key)?.snapshot?.plan ?? canon.plan,
|
|
314
343
|
// Throttle state comes from the live usage windows, not the pay-as-you-go
|
|
315
344
|
// overage flag that AccountInfo.usageStatus used to carry. A maxed window
|
|
316
345
|
// means rate-limited; no snapshot means no badge. See
|
|
@@ -353,10 +382,9 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
353
382
|
maxVerLabel = Math.max(maxVerLabel, label.length);
|
|
354
383
|
const rawInfo = infoMap.get(`${agentId}:${v}`);
|
|
355
384
|
const info = rawInfo ? mergeCanonical(rawInfo) : undefined;
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
maxEmail = Math.max(maxEmail, SIGNED_IN_LABEL.length);
|
|
385
|
+
const accountLabel = accountColumnLabel(info);
|
|
386
|
+
if (accountLabel)
|
|
387
|
+
maxEmail = Math.max(maxEmail, accountLabel.length);
|
|
360
388
|
if (info?.plan)
|
|
361
389
|
maxPlanWidth = Math.max(maxPlanWidth, info.plan.length);
|
|
362
390
|
}
|
|
@@ -432,9 +460,10 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
432
460
|
}
|
|
433
461
|
else {
|
|
434
462
|
if (hasEmail || hasUsage || hasActive || signedIn) {
|
|
435
|
-
// Signed-in agents without a local email
|
|
436
|
-
// "signed in" placeholder so they
|
|
437
|
-
|
|
463
|
+
// Signed-in agents without a local email show their account id
|
|
464
|
+
// (Kimi's user_id) or a "signed in" placeholder (Antigravity) so they
|
|
465
|
+
// read as logged in, not blank.
|
|
466
|
+
const display = accountColumnLabel(vInfo);
|
|
438
467
|
const emailCol = display.padEnd(maxEmail);
|
|
439
468
|
parts.push(display ? chalk.cyan(emailCol) : ' '.repeat(maxEmail));
|
|
440
469
|
}
|
|
@@ -515,7 +544,7 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
515
544
|
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null);
|
|
516
545
|
const gActiveStr = gInfo ? formatLastActive(gInfo.lastActive) : '';
|
|
517
546
|
if (gInfo?.email || gUsageStr || gActiveStr || gInfo?.signedIn) {
|
|
518
|
-
const gDisplay =
|
|
547
|
+
const gDisplay = accountColumnLabel(gInfo);
|
|
519
548
|
parts.push(gDisplay ? chalk.cyan(gDisplay) : '');
|
|
520
549
|
}
|
|
521
550
|
if (gUsageStr || gActiveStr)
|
|
@@ -686,53 +715,9 @@ async function showAgentResources(agentId, requestedVersion, filter) {
|
|
|
686
715
|
}
|
|
687
716
|
}
|
|
688
717
|
const home = getVersionHomePath(agentId, version);
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
const hasGitRepo =
|
|
692
|
-
const commandsSync = hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'commands') : null;
|
|
693
|
-
const skillsSync = hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'skills') : null;
|
|
694
|
-
const hooksSync = hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'hooks') : null;
|
|
695
|
-
const memorySync = hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'rules') : null;
|
|
696
|
-
// Helper to determine sync state for a resource
|
|
697
|
-
const getSyncState = (resourceName, resourceType, syncStatus) => {
|
|
698
|
-
if (!syncStatus)
|
|
699
|
-
return undefined;
|
|
700
|
-
let relativePath;
|
|
701
|
-
if (resourceType === 'commands') {
|
|
702
|
-
relativePath = `commands/${resourceName}.md`;
|
|
703
|
-
}
|
|
704
|
-
else if (resourceType === 'skills') {
|
|
705
|
-
relativePath = `skills/${resourceName}`;
|
|
706
|
-
}
|
|
707
|
-
else if (resourceType === 'hooks') {
|
|
708
|
-
relativePath = `hooks/${resourceName}`;
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
// Rules files: map agent-specific name (CLAUDE.md) back to canonical (AGENTS.md)
|
|
712
|
-
const centralName = getCentralRulesFileName(agentId);
|
|
713
|
-
relativePath = `rules/${centralName}`;
|
|
714
|
-
}
|
|
715
|
-
const matchesPath = (f) => f === relativePath || f.startsWith(relativePath + '/');
|
|
716
|
-
const isNew = syncStatus.new.some(matchesPath);
|
|
717
|
-
const isStaged = syncStatus.staged.some(matchesPath);
|
|
718
|
-
const isModified = syncStatus.modified.some(matchesPath);
|
|
719
|
-
const isDeleted = syncStatus.deleted.some(matchesPath);
|
|
720
|
-
const isSynced = syncStatus.synced.some(matchesPath);
|
|
721
|
-
if (isNew || isStaged) {
|
|
722
|
-
return 'new';
|
|
723
|
-
}
|
|
724
|
-
if (isModified) {
|
|
725
|
-
return 'modified';
|
|
726
|
-
}
|
|
727
|
-
if (isDeleted) {
|
|
728
|
-
return 'deleted';
|
|
729
|
-
}
|
|
730
|
-
if (isSynced) {
|
|
731
|
-
return 'synced';
|
|
732
|
-
}
|
|
733
|
-
// Not in any array = local-only (untracked with no files)
|
|
734
|
-
return 'new';
|
|
735
|
-
};
|
|
718
|
+
// Git sync status if ~/.agents/ is a git repo (shared loader — see
|
|
719
|
+
// loadResourceSyncData / resolveResourceSyncState, reused by --json --resources).
|
|
720
|
+
const { hasGitRepo, commands: commandsSync, skills: skillsSync, hooks: hooksSync, memory: memorySync } = await loadResourceSyncData();
|
|
736
721
|
const resources = getAgentResources(agentId, {
|
|
737
722
|
cwd,
|
|
738
723
|
scope: 'all',
|
|
@@ -745,23 +730,23 @@ async function showAgentResources(agentId, requestedVersion, filter) {
|
|
|
745
730
|
version,
|
|
746
731
|
commands: resources.commands.map(r => ({
|
|
747
732
|
...r,
|
|
748
|
-
syncState: r.scope === 'project' ? undefined :
|
|
733
|
+
syncState: r.scope === 'project' ? undefined : resolveResourceSyncState(agentId, r.name, 'commands', commandsSync),
|
|
749
734
|
})),
|
|
750
735
|
skills: resources.skills.map(r => ({
|
|
751
736
|
...r,
|
|
752
737
|
// ruleCount of 0 is noise — every skill has 0 unless it ships subrules, which is rare.
|
|
753
738
|
ruleCount: r.ruleCount && r.ruleCount > 0 ? r.ruleCount : undefined,
|
|
754
|
-
syncState: r.scope === 'project' ? undefined :
|
|
739
|
+
syncState: r.scope === 'project' ? undefined : resolveResourceSyncState(agentId, r.name, 'skills', skillsSync),
|
|
755
740
|
})),
|
|
756
741
|
skillErrors: resources.skillErrors,
|
|
757
742
|
mcp: resources.mcp.map(r => ({ name: r.name, scope: r.scope, syncState: r.scope === 'project' ? undefined : 'synced' })),
|
|
758
743
|
memory: resources.memory.map(r => ({
|
|
759
744
|
...r,
|
|
760
|
-
syncState: r.scope === 'project' ? undefined :
|
|
745
|
+
syncState: r.scope === 'project' ? undefined : resolveResourceSyncState(agentId, r.name, 'memory', memorySync),
|
|
761
746
|
})),
|
|
762
747
|
hooks: resources.hooks.map(r => ({
|
|
763
748
|
...r,
|
|
764
|
-
syncState: r.scope === 'project' ? undefined :
|
|
749
|
+
syncState: r.scope === 'project' ? undefined : resolveResourceSyncState(agentId, r.name, 'hooks', hooksSync),
|
|
765
750
|
})),
|
|
766
751
|
workflows: resources.workflows.map(r => ({ name: r.name, path: r.path, scope: r.scope })),
|
|
767
752
|
};
|
|
@@ -796,9 +781,10 @@ async function showAgentResources(agentId, requestedVersion, filter) {
|
|
|
796
781
|
: chalk.gray('[system]');
|
|
797
782
|
display += ` ${sourceTag}`;
|
|
798
783
|
const syncStr = r.syncState ? chalk.gray(` [${r.syncState}]`) : '';
|
|
799
|
-
const
|
|
784
|
+
const prefix = ` ${display}${syncStr}`;
|
|
785
|
+
const descSnippet = descriptionForPrefix(r.description, `${prefix} `);
|
|
800
786
|
const descStr = descSnippet ? chalk.gray(` ${descSnippet}`) : '';
|
|
801
|
-
console.log(
|
|
787
|
+
console.log(prefix + descStr);
|
|
802
788
|
}
|
|
803
789
|
}
|
|
804
790
|
// Render promptcuts (cross-agent, not per-version). Shortcuts are layered
|
|
@@ -827,13 +813,10 @@ async function showAgentResources(agentId, requestedVersion, filter) {
|
|
|
827
813
|
cliVersion: version,
|
|
828
814
|
info: accountInfo,
|
|
829
815
|
});
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
: accountInfo.signedIn
|
|
833
|
-
? chalk.cyan(` ${SIGNED_IN_LABEL}`)
|
|
834
|
-
: '';
|
|
816
|
+
const accountLabel = accountColumnLabel(accountInfo);
|
|
817
|
+
const emailStr = accountLabel ? chalk.cyan(` ${accountLabel}`) : '';
|
|
835
818
|
const status = chalk.green(version);
|
|
836
|
-
const usageStr = formatUsageSummary(accountInfo.plan, null);
|
|
819
|
+
const usageStr = formatUsageSummary(usageInfo.snapshot?.plan ?? accountInfo.plan, null);
|
|
837
820
|
const usagePart = usageStr ? ` ${usageStr}` : '';
|
|
838
821
|
console.log(` ${colorAgent(agentId)(AGENTS[agentId].name.padEnd(14))} ${status}${emailStr}${usagePart}`);
|
|
839
822
|
const usageLines = formatUsageSection(usageInfo);
|
|
@@ -973,14 +956,144 @@ async function showAgentResources(agentId, requestedVersion, filter) {
|
|
|
973
956
|
console.log(chalk.gray('Legend:'), chalk.green('Tracked'), chalk.blue('Local-only'), chalk.yellow('Modified'), chalk.red('Deleted'));
|
|
974
957
|
}
|
|
975
958
|
}
|
|
959
|
+
const ALL_RESOURCE_SECTIONS = ['commands', 'skills', 'mcp', 'memory', 'hooks', 'workflows', 'plugins'];
|
|
960
|
+
/** Load git sync-state for the tracked resource kinds. Shared by the human
|
|
961
|
+
* detail view (showAgentResources) and the `--json --resources` path. */
|
|
962
|
+
async function loadResourceSyncData() {
|
|
963
|
+
const userAgentsDir = getUserAgentsDir();
|
|
964
|
+
const hasGitRepo = isGitRepo(userAgentsDir);
|
|
965
|
+
return {
|
|
966
|
+
hasGitRepo,
|
|
967
|
+
commands: hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'commands') : null,
|
|
968
|
+
skills: hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'skills') : null,
|
|
969
|
+
hooks: hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'hooks') : null,
|
|
970
|
+
memory: hasGitRepo ? await getGitSyncStatus(userAgentsDir, 'rules') : null,
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
/** Resolve one resource's git sync-state. Extracted from showAgentResources so
|
|
974
|
+
* the human view and the JSON path derive drift identically. */
|
|
975
|
+
function resolveResourceSyncState(agentId, resourceName, resourceType, syncStatus) {
|
|
976
|
+
if (!syncStatus)
|
|
977
|
+
return undefined;
|
|
978
|
+
let relativePath;
|
|
979
|
+
if (resourceType === 'commands') {
|
|
980
|
+
relativePath = `commands/${resourceName}.md`;
|
|
981
|
+
}
|
|
982
|
+
else if (resourceType === 'skills') {
|
|
983
|
+
relativePath = `skills/${resourceName}`;
|
|
984
|
+
}
|
|
985
|
+
else if (resourceType === 'hooks') {
|
|
986
|
+
relativePath = `hooks/${resourceName}`;
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
// Rules files: map agent-specific name (CLAUDE.md) back to canonical (AGENTS.md)
|
|
990
|
+
const centralName = getCentralRulesFileName(agentId);
|
|
991
|
+
relativePath = `rules/${centralName}`;
|
|
992
|
+
}
|
|
993
|
+
const matchesPath = (f) => f === relativePath || f.startsWith(relativePath + '/');
|
|
994
|
+
if (syncStatus.new.some(matchesPath) || syncStatus.staged.some(matchesPath))
|
|
995
|
+
return 'new';
|
|
996
|
+
if (syncStatus.modified.some(matchesPath))
|
|
997
|
+
return 'modified';
|
|
998
|
+
if (syncStatus.deleted.some(matchesPath))
|
|
999
|
+
return 'deleted';
|
|
1000
|
+
if (syncStatus.synced.some(matchesPath))
|
|
1001
|
+
return 'synced';
|
|
1002
|
+
// Not in any array = local-only (untracked with no files)
|
|
1003
|
+
return 'new';
|
|
1004
|
+
}
|
|
1005
|
+
/** Collect one version's resources for `--json`, limited to `sections`. Scans
|
|
1006
|
+
* the version's `home`, so per-version differences are reported accurately. */
|
|
1007
|
+
function collectVersionResources(agentId, home, cwd, cliInstalled, sections, sync) {
|
|
1008
|
+
const res = getAgentResources(agentId, { cwd, scope: 'all', cliInstalled, home });
|
|
1009
|
+
const out = {};
|
|
1010
|
+
const withSync = (r, type, syncStatus) => {
|
|
1011
|
+
const item = { name: r.name, scope: r.scope };
|
|
1012
|
+
if (r.scope !== 'project') {
|
|
1013
|
+
const s = resolveResourceSyncState(agentId, r.name, type, syncStatus);
|
|
1014
|
+
if (s)
|
|
1015
|
+
item.syncState = s;
|
|
1016
|
+
}
|
|
1017
|
+
if (r.description)
|
|
1018
|
+
item.description = r.description;
|
|
1019
|
+
return item;
|
|
1020
|
+
};
|
|
1021
|
+
if (sections.has('commands'))
|
|
1022
|
+
out.commands = res.commands.map((r) => withSync(r, 'commands', sync.commands));
|
|
1023
|
+
if (sections.has('skills')) {
|
|
1024
|
+
out.skills = res.skills.map((r) => {
|
|
1025
|
+
const item = withSync(r, 'skills', sync.skills);
|
|
1026
|
+
// ruleCount of 0 is noise — every skill has 0 unless it ships subrules.
|
|
1027
|
+
if (r.ruleCount && r.ruleCount > 0)
|
|
1028
|
+
item.ruleCount = r.ruleCount;
|
|
1029
|
+
return item;
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
if (sections.has('mcp')) {
|
|
1033
|
+
out.mcp = res.mcp.map((r) => {
|
|
1034
|
+
const item = { name: r.name, scope: r.scope };
|
|
1035
|
+
if (r.scope !== 'project')
|
|
1036
|
+
item.syncState = 'synced';
|
|
1037
|
+
return item;
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
if (sections.has('memory'))
|
|
1041
|
+
out.memory = res.memory.map((r) => withSync(r, 'memory', sync.memory));
|
|
1042
|
+
if (sections.has('hooks'))
|
|
1043
|
+
out.hooks = res.hooks.map((r) => withSync(r, 'hooks', sync.hooks));
|
|
1044
|
+
if (sections.has('workflows'))
|
|
1045
|
+
out.workflows = res.workflows.map((r) => ({ name: r.name, scope: r.scope }));
|
|
1046
|
+
if (sections.has('plugins')) {
|
|
1047
|
+
out.plugins = discoverPlugins()
|
|
1048
|
+
.filter((p) => pluginSupportsAgent(p, agentId))
|
|
1049
|
+
.map((p) => ({ name: p.name }));
|
|
1050
|
+
}
|
|
1051
|
+
return out;
|
|
1052
|
+
}
|
|
1053
|
+
/** Build the set of resource sections to include in `--json` from the
|
|
1054
|
+
* `--resources` / `--detailed` flags, plus (in --json mode) the per-section
|
|
1055
|
+
* boolean filters (`--skills` etc.) that plain `--json` historically ignored. */
|
|
1056
|
+
export function parseResourceSections(options, jsonMode) {
|
|
1057
|
+
const set = new Set();
|
|
1058
|
+
const addAll = () => ALL_RESOURCE_SECTIONS.forEach((s) => set.add(s));
|
|
1059
|
+
if (options.detailed)
|
|
1060
|
+
addAll();
|
|
1061
|
+
const rv = options.resources;
|
|
1062
|
+
if (rv !== undefined) {
|
|
1063
|
+
if (rv === true || String(rv).trim() === '' || String(rv).toLowerCase() === 'all') {
|
|
1064
|
+
addAll();
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
for (const raw of String(rv).split(',').map((s) => s.trim().toLowerCase()).filter(Boolean)) {
|
|
1068
|
+
const section = raw === 'rules' ? 'memory' : raw; // --rules is the memory file
|
|
1069
|
+
if (ALL_RESOURCE_SECTIONS.includes(section))
|
|
1070
|
+
set.add(section);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (jsonMode) {
|
|
1075
|
+
const flagToSection = [
|
|
1076
|
+
['commands', 'commands'], ['skills', 'skills'], ['mcp', 'mcp'],
|
|
1077
|
+
['workflows', 'workflows'], ['plugins', 'plugins'], ['hooks', 'hooks'], ['rules', 'memory'],
|
|
1078
|
+
];
|
|
1079
|
+
for (const [flag, section] of flagToSection)
|
|
1080
|
+
if (options[flag])
|
|
1081
|
+
set.add(section);
|
|
1082
|
+
}
|
|
1083
|
+
return set;
|
|
1084
|
+
}
|
|
976
1085
|
/**
|
|
977
1086
|
* Collect structured info for one or more agents without rendering to the
|
|
978
1087
|
* terminal. Used by `--json` output and any programmatic consumer (e.g. the
|
|
979
1088
|
* agents-cli extension's "resume current session in best available version"
|
|
980
1089
|
* command).
|
|
981
1090
|
*/
|
|
982
|
-
async function collectAgentsJson(filterAgentId) {
|
|
1091
|
+
async function collectAgentsJson(filterAgentId, resourceSections) {
|
|
983
1092
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
1093
|
+
const wantResources = !!resourceSections && resourceSections.size > 0;
|
|
1094
|
+
// Only pay for the git-status + resource scans when resources were requested.
|
|
1095
|
+
const resourceSync = wantResources ? await loadResourceSyncData() : null;
|
|
1096
|
+
const cliStates = wantResources ? await getAllCliStates() : null;
|
|
984
1097
|
const infoFetches = [];
|
|
985
1098
|
for (const agentId of agentsToShow) {
|
|
986
1099
|
for (const ver of listInstalledVersions(agentId)) {
|
|
@@ -1004,7 +1117,10 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
1004
1117
|
return info;
|
|
1005
1118
|
return {
|
|
1006
1119
|
...info,
|
|
1007
|
-
plan
|
|
1120
|
+
// Prefer a plan the live usage fetch surfaced (Kimi reports membership
|
|
1121
|
+
// tier in /usages; its local auth file has none) and fall back to the
|
|
1122
|
+
// account-derived plan (Claude's billingType).
|
|
1123
|
+
plan: usageByKey.get(key)?.snapshot?.plan ?? canon.plan,
|
|
1008
1124
|
// Throttle state comes from the live usage windows, not the pay-as-you-go
|
|
1009
1125
|
// overage flag that AccountInfo.usageStatus used to carry. A maxed window
|
|
1010
1126
|
// means rate-limited; no snapshot means no badge. See
|
|
@@ -1014,7 +1130,7 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
1014
1130
|
};
|
|
1015
1131
|
};
|
|
1016
1132
|
const byAgent = new Map();
|
|
1017
|
-
for (const { agentId, version, info: rawInfo } of infoResults) {
|
|
1133
|
+
for (const { agentId, version, home, info: rawInfo } of infoResults) {
|
|
1018
1134
|
const info = mergeCanonical(rawInfo);
|
|
1019
1135
|
const globalDefault = getGlobalDefault(agentId);
|
|
1020
1136
|
const usageKey = getUsageLookupKey(info);
|
|
@@ -1025,6 +1141,7 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
1025
1141
|
isDefault: version === globalDefault,
|
|
1026
1142
|
signedIn: info.signedIn,
|
|
1027
1143
|
email: info.email,
|
|
1144
|
+
accountId: info.accountId,
|
|
1028
1145
|
plan: info.plan,
|
|
1029
1146
|
usageStatus: info.usageStatus,
|
|
1030
1147
|
overageCredits: info.overageCredits,
|
|
@@ -1038,6 +1155,9 @@ async function collectAgentsJson(filterAgentId) {
|
|
|
1038
1155
|
lastActive: info.lastActive ? info.lastActive.toISOString() : null,
|
|
1039
1156
|
path: getVersionDir(agentId, version),
|
|
1040
1157
|
};
|
|
1158
|
+
if (wantResources && resourceSync) {
|
|
1159
|
+
entry.resources = collectVersionResources(agentId, home, process.cwd(), cliStates?.[agentId]?.installed ?? false, resourceSections, resourceSync);
|
|
1160
|
+
}
|
|
1041
1161
|
const existing = byAgent.get(agentId);
|
|
1042
1162
|
if (existing)
|
|
1043
1163
|
existing.push(entry);
|
|
@@ -1248,7 +1368,10 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
|
|
|
1248
1368
|
* Exported for use by deprecated aliases.
|
|
1249
1369
|
*/
|
|
1250
1370
|
export async function viewAction(agentArg, options) {
|
|
1251
|
-
|
|
1371
|
+
// --resources / --detailed imply --json (they only shape structured output).
|
|
1372
|
+
const explicitResources = options?.detailed === true || options?.resources !== undefined;
|
|
1373
|
+
const json = options?.json === true || explicitResources;
|
|
1374
|
+
const resourceSections = parseResourceSections(options ?? {}, json);
|
|
1252
1375
|
const prune = options?.prune === true;
|
|
1253
1376
|
const yes = options?.yes === true;
|
|
1254
1377
|
const dryRun = options?.dryRun === true;
|
|
@@ -1280,7 +1403,7 @@ export async function viewAction(agentArg, options) {
|
|
|
1280
1403
|
return;
|
|
1281
1404
|
}
|
|
1282
1405
|
if (json) {
|
|
1283
|
-
const data = await collectAgentsJson();
|
|
1406
|
+
const data = await collectAgentsJson(undefined, resourceSections);
|
|
1284
1407
|
console.log(JSON.stringify(data, null, 2));
|
|
1285
1408
|
return;
|
|
1286
1409
|
}
|
|
@@ -1329,9 +1452,9 @@ export async function viewAction(agentArg, options) {
|
|
|
1329
1452
|
return;
|
|
1330
1453
|
}
|
|
1331
1454
|
if (json) {
|
|
1332
|
-
// --json ignores the @version suffix
|
|
1333
|
-
//
|
|
1334
|
-
const data = await collectAgentsJson(agentId);
|
|
1455
|
+
// --json ignores the @version suffix, but --resources/--detailed (or a
|
|
1456
|
+
// section flag) now attach each version's resource inventory + sync-state.
|
|
1457
|
+
const data = await collectAgentsJson(agentId, resourceSections);
|
|
1335
1458
|
console.log(JSON.stringify(data[0] ?? { agent: agentId, versions: [], profiles: [] }, null, 2));
|
|
1336
1459
|
return;
|
|
1337
1460
|
}
|
|
@@ -1354,6 +1477,8 @@ export function registerViewCommand(program) {
|
|
|
1354
1477
|
addHostOption(program.command('view [agent]'))
|
|
1355
1478
|
.description('Show what agent CLIs are installed and which versions you have. Inspect resources when you pass agent@version.')
|
|
1356
1479
|
.option('--json', 'Emit machine-readable JSON (version list, usage, signed-in status).')
|
|
1480
|
+
.option('--resources [sections]', 'In --json mode, include each version\'s resources: "all" (default) or a comma list (skills,plugins,mcp,commands,workflows,memory,hooks). Implies --json.')
|
|
1481
|
+
.option('--detailed', 'Include all resources in --json output (alias for --resources all). Implies --json.')
|
|
1357
1482
|
.option('--prune', 'Remove older installed versions that share an account with a newer installed version. Skips the global default.')
|
|
1358
1483
|
.option('--dry-run', 'With --prune, show duplicate versions without deleting')
|
|
1359
1484
|
.option('-y, --yes', 'Skip the prune confirmation prompt.')
|
|
@@ -1381,6 +1506,11 @@ Examples:
|
|
|
1381
1506
|
# Machine-readable output (used by tools that pick a version programmatically)
|
|
1382
1507
|
agents view claude --json
|
|
1383
1508
|
|
|
1509
|
+
# One call: full inventory + what's synced on a host (installed agents,
|
|
1510
|
+
# versions, accounts, usage, and per-version resource sync-state)
|
|
1511
|
+
agents view claude --host yosemite-s0 --json --resources all
|
|
1512
|
+
agents view claude --json --resources skills,plugins # just those sections
|
|
1513
|
+
|
|
1384
1514
|
# Prune older versions that duplicate an account already used by a newer version
|
|
1385
1515
|
agents view --prune --dry-run
|
|
1386
1516
|
agents view claude --prune
|
|
@@ -11,4 +11,10 @@
|
|
|
11
11
|
* agents-cli.secrets.wallet.<id> JSON {pan, cvc, cardholder}
|
|
12
12
|
*/
|
|
13
13
|
import type { Command } from 'commander';
|
|
14
|
+
import { type CardMetadata } from '../lib/wallet/index.js';
|
|
15
|
+
/** Width of the id column (incl. its 2-space gap): full UUID >=104, short id >=80, none below. */
|
|
16
|
+
export declare function walletIdWidth(cols: number): number;
|
|
17
|
+
/** Nickname column width. Shrinks so the whole row fits even at the 60-col floor. */
|
|
18
|
+
export declare function walletNickWidth(cols: number): number;
|
|
19
|
+
export declare function renderRow(c: CardMetadata, cols?: number): string;
|
|
14
20
|
export declare function registerWalletCommands(program: Command): void;
|
package/dist/commands/wallet.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* agents-cli.secrets.wallet.<id> JSON {pan, cvc, cardholder}
|
|
12
12
|
*/
|
|
13
13
|
import chalk from 'chalk';
|
|
14
|
+
import { terminalWidth, truncateToWidth } from '../lib/session/width.js';
|
|
14
15
|
import { addCard, detectBrand, isValidLuhn, listCards, removeCard, renameCard, showCard, } from '../lib/wallet/index.js';
|
|
15
16
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
16
17
|
import { setHelpSections } from '../lib/help.js';
|
|
@@ -29,12 +30,25 @@ function brandLabel(b) {
|
|
|
29
30
|
function formatExp(month, year) {
|
|
30
31
|
return `${month}/${year.slice(-2)}`;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
/** Width of the id column (incl. its 2-space gap): full UUID >=104, short id >=80, none below. */
|
|
34
|
+
export function walletIdWidth(cols) {
|
|
35
|
+
return cols >= 104 ? 2 + 36 : cols >= 80 ? 2 + 8 : 0;
|
|
36
|
+
}
|
|
37
|
+
/** Nickname column width. Shrinks so the whole row fits even at the 60-col floor. */
|
|
38
|
+
export function walletNickWidth(cols) {
|
|
39
|
+
const rest = 2 + 1 + 18 + 1 + 10 + 1 + 9 + walletIdWidth(cols); // everything but the nickname
|
|
40
|
+
return Math.max(8, Math.min(24, cols - rest));
|
|
41
|
+
}
|
|
42
|
+
export function renderRow(c, cols = terminalWidth()) {
|
|
43
|
+
const nickW = walletNickWidth(cols);
|
|
44
|
+
const nick = truncateToWidth(c.nickname, nickW).padEnd(nickW);
|
|
34
45
|
const brand = brandLabel(c.brand).padEnd(18);
|
|
35
46
|
const last4 = `•••• ${c.last4}`.padEnd(10);
|
|
36
47
|
const exp = formatExp(c.exp_month, c.exp_year);
|
|
37
|
-
|
|
48
|
+
// Full 36-char UUID pushed rows to ~106 chars; size the id to the terminal.
|
|
49
|
+
const id = cols >= 104 ? c.id : cols >= 80 ? c.id.slice(0, 8) : '';
|
|
50
|
+
const idCol = id ? ` ${chalk.gray(id)}` : '';
|
|
51
|
+
return ` ${chalk.cyan(nick)} ${brand} ${last4} ${chalk.gray('exp ' + exp)}${idCol}`;
|
|
38
52
|
}
|
|
39
53
|
async function promptString(message, validate) {
|
|
40
54
|
const { input } = await import('@inquirer/prompts');
|
|
@@ -131,9 +145,12 @@ export function registerWalletCommands(program) {
|
|
|
131
145
|
console.log(chalk.gray('No cards in wallet. Try: agents wallet add'));
|
|
132
146
|
return;
|
|
133
147
|
}
|
|
134
|
-
|
|
148
|
+
const walletCols = terminalWidth();
|
|
149
|
+
const nickW = walletNickWidth(walletCols);
|
|
150
|
+
const idHeader = walletIdWidth(walletCols) ? ' ID' : '';
|
|
151
|
+
console.log(chalk.bold(` ${'NICKNAME'.padEnd(nickW)} ${'BRAND'.padEnd(18)} ${'LAST4'.padEnd(10)} EXP${idHeader}`));
|
|
135
152
|
for (const c of cards)
|
|
136
|
-
console.log(renderRow(c));
|
|
153
|
+
console.log(renderRow(c, walletCols));
|
|
137
154
|
}
|
|
138
155
|
catch (err) {
|
|
139
156
|
console.error(chalk.red(err.message));
|