@phnx-labs/agents-cli 1.22.25 → 1.22.27

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.
Files changed (178) hide show
  1. package/CHANGELOG.md +427 -0
  2. package/README.md +35 -3
  3. package/dist/bin/agents +0 -0
  4. package/dist/browser.js +14 -4
  5. package/dist/commands/apply.js +52 -8
  6. package/dist/commands/bench.d.ts +2 -0
  7. package/dist/commands/bench.js +101 -0
  8. package/dist/commands/browser.js +35 -0
  9. package/dist/commands/doctor.js +32 -10
  10. package/dist/commands/exec.js +17 -16
  11. package/dist/commands/insights.d.ts +25 -19
  12. package/dist/commands/insights.js +146 -38
  13. package/dist/commands/output.js +100 -25
  14. package/dist/commands/reconnect.d.ts +46 -0
  15. package/dist/commands/reconnect.js +109 -0
  16. package/dist/commands/resume.d.ts +11 -0
  17. package/dist/commands/resume.js +51 -0
  18. package/dist/commands/routines.js +2 -2
  19. package/dist/commands/secrets.d.ts +2 -8
  20. package/dist/commands/secrets.js +29 -105
  21. package/dist/commands/sessions-picker.js +12 -7
  22. package/dist/commands/sessions.d.ts +47 -1
  23. package/dist/commands/sessions.js +349 -37
  24. package/dist/commands/setup-secrets.d.ts +1 -0
  25. package/dist/commands/setup-secrets.js +1 -1
  26. package/dist/commands/setup.d.ts +26 -3
  27. package/dist/commands/setup.js +105 -46
  28. package/dist/commands/ssh.js +184 -11
  29. package/dist/commands/teams.d.ts +6 -0
  30. package/dist/commands/teams.js +46 -3
  31. package/dist/commands/trends.d.ts +8 -0
  32. package/dist/commands/trends.js +10 -156
  33. package/dist/index.js +17 -21
  34. package/dist/lib/agents.d.ts +11 -0
  35. package/dist/lib/agents.js +29 -2
  36. package/dist/lib/analytics/dashboard.d.ts +10 -6
  37. package/dist/lib/analytics/dashboard.js +6 -4
  38. package/dist/lib/analytics/mix-commands.d.ts +53 -0
  39. package/dist/lib/analytics/mix-commands.js +229 -0
  40. package/dist/lib/analytics/recipes.d.ts +19 -14
  41. package/dist/lib/analytics/recipes.js +4 -2
  42. package/dist/lib/auth-health.d.ts +47 -3
  43. package/dist/lib/auth-health.js +74 -15
  44. package/dist/lib/bench/index.d.ts +4 -0
  45. package/dist/lib/bench/index.js +4 -0
  46. package/dist/lib/bench/runner.d.ts +16 -0
  47. package/dist/lib/bench/runner.js +111 -0
  48. package/dist/lib/bench/schema.d.ts +5 -0
  49. package/dist/lib/bench/schema.js +91 -0
  50. package/dist/lib/bench/storage.d.ts +5 -0
  51. package/dist/lib/bench/storage.js +32 -0
  52. package/dist/lib/bench/types.d.ts +40 -0
  53. package/dist/lib/bench/types.js +1 -0
  54. package/dist/lib/browser/ipc.d.ts +26 -0
  55. package/dist/lib/browser/ipc.js +139 -24
  56. package/dist/lib/browser/profiles.d.ts +11 -0
  57. package/dist/lib/browser/profiles.js +1 -1
  58. package/dist/lib/browser/stream.d.ts +14 -0
  59. package/dist/lib/browser/stream.js +71 -0
  60. package/dist/lib/channels/owner-sink.d.ts +27 -0
  61. package/dist/lib/channels/owner-sink.js +93 -0
  62. package/dist/lib/claude-account-token.d.ts +2 -0
  63. package/dist/lib/claude-account-token.js +27 -1
  64. package/dist/lib/crabbox/cli.d.ts +2 -0
  65. package/dist/lib/crabbox/cli.js +2 -0
  66. package/dist/lib/crabbox/lease.js +7 -1
  67. package/dist/lib/daemon.js +20 -0
  68. package/dist/lib/devices/connect.d.ts +2 -0
  69. package/dist/lib/devices/connect.js +7 -0
  70. package/dist/lib/devices/doctor-findings.d.ts +10 -1
  71. package/dist/lib/devices/doctor-findings.js +47 -1
  72. package/dist/lib/devices/harness-inventory.d.ts +97 -0
  73. package/dist/lib/devices/harness-inventory.js +0 -0
  74. package/dist/lib/devices/registry.d.ts +2 -0
  75. package/dist/lib/devices/ssh-config.js +3 -0
  76. package/dist/lib/devices/windows-ssh-enrollment.d.ts +20 -0
  77. package/dist/lib/devices/windows-ssh-enrollment.js +98 -0
  78. package/dist/lib/exec.bench.d.ts +1 -0
  79. package/dist/lib/exec.bench.js +186 -0
  80. package/dist/lib/exec.js +18 -8
  81. package/dist/lib/fleet/apply.d.ts +59 -3
  82. package/dist/lib/fleet/apply.js +191 -12
  83. package/dist/lib/fleet/remote-login.d.ts +4 -3
  84. package/dist/lib/fleet/remote-login.js +11 -9
  85. package/dist/lib/fleet/types.d.ts +21 -2
  86. package/dist/lib/gemini-settings.d.ts +0 -1
  87. package/dist/lib/gemini-settings.js +12 -7
  88. package/dist/lib/hooks/cache.js +15 -0
  89. package/dist/lib/hooks.d.ts +8 -5
  90. package/dist/lib/hooks.js +10 -6
  91. package/dist/lib/hosts/dispatch.js +7 -3
  92. package/dist/lib/hosts/passthrough.d.ts +45 -0
  93. package/dist/lib/hosts/passthrough.js +59 -8
  94. package/dist/lib/hosts/progress.d.ts +2 -1
  95. package/dist/lib/hosts/progress.js +4 -3
  96. package/dist/lib/hosts/providers/devices.js +1 -0
  97. package/dist/lib/hosts/ready.d.ts +6 -2
  98. package/dist/lib/hosts/ready.js +33 -8
  99. package/dist/lib/hosts/reconcile.d.ts +1 -1
  100. package/dist/lib/hosts/reconcile.js +16 -7
  101. package/dist/lib/hosts/reconnect.d.ts +56 -21
  102. package/dist/lib/hosts/reconnect.js +128 -71
  103. package/dist/lib/hosts/registry.d.ts +2 -2
  104. package/dist/lib/hosts/registry.js +3 -5
  105. package/dist/lib/hosts/remote-cmd.d.ts +17 -0
  106. package/dist/lib/hosts/remote-cmd.js +29 -0
  107. package/dist/lib/hosts/tasks.d.ts +2 -0
  108. package/dist/lib/hosts/types.d.ts +1 -0
  109. package/dist/lib/hosts/types.js +3 -0
  110. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  111. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  112. package/dist/lib/menubar/install-menubar.d.ts +9 -6
  113. package/dist/lib/menubar/install-menubar.js +20 -9
  114. package/dist/lib/models.d.ts +30 -1
  115. package/dist/lib/models.js +42 -144
  116. package/dist/lib/pricing/cost.d.ts +9 -0
  117. package/dist/lib/pricing/cost.js +24 -0
  118. package/dist/lib/pricing/index.d.ts +1 -1
  119. package/dist/lib/pricing/index.js +1 -1
  120. package/dist/lib/redact.js +8 -3
  121. package/dist/lib/remote-agents-json.d.ts +32 -0
  122. package/dist/lib/remote-agents-json.js +47 -16
  123. package/dist/lib/resource-profiles.js +1 -2
  124. package/dist/lib/routine-notify-owner.d.ts +102 -0
  125. package/dist/lib/routine-notify-owner.js +232 -0
  126. package/dist/lib/routines.d.ts +10 -0
  127. package/dist/lib/routines.js +14 -2
  128. package/dist/lib/runner.d.ts +9 -4
  129. package/dist/lib/runner.js +182 -30
  130. package/dist/lib/sandbox.d.ts +0 -2
  131. package/dist/lib/sandbox.js +2 -19
  132. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  133. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  134. package/dist/lib/secrets/agent.js +3 -2
  135. package/dist/lib/secrets/lease.d.ts +25 -0
  136. package/dist/lib/secrets/lease.js +44 -0
  137. package/dist/lib/secrets/push.d.ts +94 -0
  138. package/dist/lib/secrets/push.js +145 -0
  139. package/dist/lib/secrets/reaper.d.ts +15 -1
  140. package/dist/lib/secrets/reaper.js +30 -3
  141. package/dist/lib/self-update.d.ts +20 -5
  142. package/dist/lib/self-update.js +93 -16
  143. package/dist/lib/session/db.d.ts +38 -3
  144. package/dist/lib/session/db.js +273 -16
  145. package/dist/lib/session/discover.d.ts +22 -1
  146. package/dist/lib/session/discover.js +239 -51
  147. package/dist/lib/session/insights.d.ts +37 -0
  148. package/dist/lib/session/insights.js +219 -9
  149. package/dist/lib/session/recovery.d.ts +17 -1
  150. package/dist/lib/session/recovery.js +111 -4
  151. package/dist/lib/session/remote-list.d.ts +10 -9
  152. package/dist/lib/session/remote-list.js +25 -23
  153. package/dist/lib/session/resume-owner.d.ts +55 -0
  154. package/dist/lib/session/resume-owner.js +69 -0
  155. package/dist/lib/session/team-filter.d.ts +65 -0
  156. package/dist/lib/session/team-filter.js +98 -3
  157. package/dist/lib/session/tool-index.js +133 -22
  158. package/dist/lib/session/tool-store.d.ts +26 -2
  159. package/dist/lib/session/tool-store.js +36 -17
  160. package/dist/lib/session/types.d.ts +33 -0
  161. package/dist/lib/smart-launch.d.ts +31 -5
  162. package/dist/lib/smart-launch.js +43 -7
  163. package/dist/lib/ssh-exec.d.ts +27 -0
  164. package/dist/lib/ssh-exec.js +42 -3
  165. package/dist/lib/ssh-tunnel.d.ts +3 -2
  166. package/dist/lib/ssh-tunnel.js +25 -16
  167. package/dist/lib/startup/command-registry.d.ts +2 -0
  168. package/dist/lib/startup/command-registry.js +6 -0
  169. package/dist/lib/teams/agents.d.ts +13 -0
  170. package/dist/lib/teams/agents.js +75 -7
  171. package/dist/lib/teams/placement-probe.d.ts +21 -0
  172. package/dist/lib/teams/placement-probe.js +135 -0
  173. package/dist/lib/teams/scheduler.d.ts +74 -1
  174. package/dist/lib/teams/scheduler.js +187 -10
  175. package/dist/lib/tmux/session.d.ts +8 -0
  176. package/dist/lib/tmux/session.js +22 -0
  177. package/dist/lib/types.d.ts +2 -0
  178. package/package.json +1 -1
@@ -1,158 +1,12 @@
1
- import chalk from 'chalk';
2
- import { setHelpSections } from '../lib/help.js';
3
- import { buildTrendsDashboard, trendsWindow, runRecipe, RECIPE_IDS } from '../lib/analytics/dashboard.js';
4
- import { listRecipes } from '../lib/analytics/recipes.js';
5
- import { queryUsage, usageDbPath, USAGE_KINDS } from '../lib/analytics/usage-db.js';
6
- function parseDays(raw) {
7
- const n = parseInt(raw ?? '7', 10);
8
- return Number.isFinite(n) && n > 0 ? n : 7;
9
- }
10
- function parseLimit(raw, fallback) {
11
- const n = parseInt(raw ?? String(fallback), 10);
12
- return Number.isFinite(n) && n > 0 ? n : fallback;
13
- }
14
- function printSection(section) {
15
- console.log(chalk.bold(section.title));
16
- if (section.rows.length === 0) {
17
- console.log(chalk.gray(' (no data)'));
18
- console.log();
19
- return;
20
- }
21
- const keys = Object.keys(section.rows[0]);
22
- const widths = keys.map((k) => Math.max(k.length, ...section.rows.map((r) => String(r[k] ?? '').length), 4));
23
- const pad = (s, w) => (s.length >= w ? s.slice(0, w) : s + ' '.repeat(w - s.length));
24
- console.log(chalk.gray(keys.map((k, i) => pad(k.toUpperCase(), widths[i])).join(' ')));
25
- for (const row of section.rows) {
26
- console.log(keys.map((k, i) => pad(String(row[k] ?? ''), widths[i])).join(' '));
27
- }
28
- console.log();
29
- }
30
- function renderDashboard(days, asJson) {
31
- const dash = buildTrendsDashboard({ days });
32
- if (asJson) {
33
- console.log(JSON.stringify(dash, null, 2));
34
- return;
35
- }
36
- console.log(chalk.bold(`agents trends — last ${dash.window.days} days`));
37
- console.log(chalk.gray(`compute ${dash.durationMs}ms · usage ${usageDbPath()}`));
38
- console.log();
39
- if (dash.sections.length === 0) {
40
- console.log(chalk.gray('No session or usage data in this window yet.'));
41
- return;
42
- }
43
- for (const section of dash.sections)
44
- printSection(section);
45
- }
1
+ /**
2
+ * Deprecated top-level spelling of the counter-mix tree.
3
+ *
4
+ * Implementation lives under `agents insights mix` (see
5
+ * `lib/analytics/mix-commands.ts`). This file only registers the thin alias so
6
+ * old scripts and agent briefs keep working while printing one deprecation line
7
+ * no second recipe implementation.
8
+ */
9
+ import { registerDeprecatedTrendsAlias } from '../lib/analytics/mix-commands.js';
46
10
  export function registerTrendsCommand(program) {
47
- const trends = program
48
- .command('trends')
49
- .description('Usage analytics — harness/model mix, token ratios, resource frequency')
50
- .option('--days <n>', 'Days of history to include', '7')
51
- .option('--json', 'Emit JSON instead of tables')
52
- .action(function summary() {
53
- const opts = this.opts();
54
- renderDashboard(parseDays(opts.days), Boolean(opts.json));
55
- });
56
- setHelpSections(trends, {
57
- examples: `
58
- # Auto recipe dashboard (7d)
59
- agents trends
60
-
61
- # Last 30 days
62
- agents trends --days 30
63
-
64
- # One recipe as JSON
65
- agents trends harness-mix --json
66
-
67
- # Raw usage events
68
- agents trends query --kind secret --days 7
69
-
70
- # List baked recipe ids
71
- agents trends recipes
72
- `,
73
- notes: `
74
- Session recipes read sessions.db; resource recipes read ~/.agents/.history/analytics/usage.db.
75
- Empty recipes are skipped on the default dashboard.
76
- Quota / rate-limits remain on \`agents usage\`; latency on \`agents perf\`.
77
- `,
78
- });
79
- trends.command('recipes')
80
- .description('List baked recipe ids')
81
- .option('--json', 'Emit JSON')
82
- .action((opts) => {
83
- const list = listRecipes();
84
- if (opts.json) {
85
- console.log(JSON.stringify(list, null, 2));
86
- return;
87
- }
88
- for (const r of list) {
89
- console.log(`${r.id.padEnd(22)} ${r.store.padEnd(10)} ${r.title}`);
90
- }
91
- });
92
- trends.command('query')
93
- .description('Raw usage-event query')
94
- .option('--kind <kind>', `One of: ${USAGE_KINDS.join(', ')}`)
95
- .option('--name <name>', 'Resource name filter')
96
- .option('--event <event>', 'Event name filter')
97
- .option('--days <n>', 'Days of history', '7')
98
- .option('--limit <n>', 'Max rows', '40')
99
- .option('--json', 'Emit JSON')
100
- .action((opts) => {
101
- const win = trendsWindow(parseDays(opts.days));
102
- const kind = opts.kind && USAGE_KINDS.includes(opts.kind)
103
- ? opts.kind
104
- : undefined;
105
- if (opts.kind && !kind) {
106
- console.error(`Unknown kind '${opts.kind}'. Expected: ${USAGE_KINDS.join(', ')}`);
107
- process.exitCode = 1;
108
- return;
109
- }
110
- const rows = queryUsage({
111
- kind,
112
- name: opts.name,
113
- event: opts.event,
114
- sinceIso: win.sinceIso,
115
- limit: parseLimit(opts.limit, 40),
116
- });
117
- if (opts.json) {
118
- console.log(JSON.stringify({ window: win, rows }, null, 2));
119
- return;
120
- }
121
- if (rows.length === 0) {
122
- console.log(chalk.gray('No usage events.'));
123
- return;
124
- }
125
- printSection({
126
- id: 'query',
127
- title: 'Usage events',
128
- rows: rows.map((r) => ({
129
- ts: r.ts,
130
- kind: r.kind,
131
- name: r.name,
132
- event: r.event,
133
- agent: r.agent,
134
- })),
135
- });
136
- });
137
- for (const id of RECIPE_IDS) {
138
- trends.command(id)
139
- .description(`Recipe: ${id}`)
140
- .option('--days <n>', 'Days of history', '7')
141
- .option('--json', 'Emit JSON')
142
- .action(function recipeAction() {
143
- const parent = this.parent?.opts?.();
144
- const opts = { ...parent, ...this.opts() };
145
- const win = trendsWindow(parseDays(opts.days));
146
- const section = runRecipe(id, win);
147
- if (opts.json) {
148
- console.log(JSON.stringify({ window: win, section }, null, 2));
149
- return;
150
- }
151
- if (section.empty) {
152
- console.log(chalk.gray(`No data for recipe '${id}' in the last ${win.days} days.`));
153
- return;
154
- }
155
- printSection(section);
156
- });
157
- }
11
+ registerDeprecatedTrendsAlias(program);
158
12
  }
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ if (IS_DEV_BUILD) {
94
94
  // module on each invocation (which loaded the whole ~50-module tree before the
95
95
  // first byte of output), the registry maps a command name to a thunk that
96
96
  // imports only what that command needs. See src/lib/startup/command-registry.ts.
97
- import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadModes, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadInsights, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadHumans, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
97
+ import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadModes, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadInsights, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadHumans, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadBench, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
98
98
  import { applyGlobalHelpConventions } from './lib/help.js';
99
99
  import { renderWhatsNew } from './lib/whats-new.js';
100
100
  import { getCliLaunch } from './lib/cli-entry.js';
@@ -327,7 +327,7 @@ Credentials and profiles:
327
327
  Diagnostics:
328
328
  doctor [agent[@version]] Diagnose CLI availability, sync status, and resource divergence; --check for the CI drift gate
329
329
  usage [agent] Show rate-limit and quota usage per agent
330
- insights How you work — tools, friction, rhythm, split by Claude account
330
+ insights How work looks behaviour (default) or counter mix (insights mix)
331
331
  perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
332
332
 
333
333
  Config sync:
@@ -392,15 +392,15 @@ async function showWhatsNew(fromVersion, toVersion) {
392
392
  const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
393
393
  import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
394
394
  import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
395
- import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, findAgentsCliInstalls, resolveRunningPackageRoot, } from './lib/self-update.js';
395
+ import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, buildMultiInstallInventory, findAgentsCliInstalls, resolveRunningPackageRoot, } from './lib/self-update.js';
396
396
  const UPDATE_CHECK_FILE = getUpdateCheckPath();
397
397
  /**
398
- * Warn once when PATH resolves `agents` to a different agents-cli install
399
- * than the copy that is currently running (or to several). Divergent installs
398
+ * Warn once when this machine contains a different agents-cli install than the
399
+ * copy that is currently running (or several). Divergent installs
400
400
  * are how self-updates "succeed" without changing the command the user types.
401
- * The warning re-fires only when the set of install roots changes; dev builds
402
- * (0.0.0-dev) are ignored because side-by-side dev installs are a supported
403
- * workflow.
401
+ * The warning re-fires only when the set of install roots or their helper-copy
402
+ * safety changes. Dev builds are included because old dev copies can still
403
+ * overwrite the shared macOS helper bundle non-atomically.
404
404
  */
405
405
  function maybeWarnMultiInstall() {
406
406
  const sentinel = path.join(getRuntimeStateDir(), 'multi-install-warned');
@@ -414,31 +414,26 @@ function maybeWarnMultiInstall() {
414
414
  // "/$bunfs" install. This warning is advisory — stay silent instead.
415
415
  return;
416
416
  }
417
- const byRoot = new Map();
418
- byRoot.set(runningRoot, { version: VERSION, note: 'running' });
419
- for (const install of findAgentsCliInstalls(process.env.PATH || '')) {
420
- if (install.version.startsWith('0.0.0-dev'))
421
- continue;
422
- if (!byRoot.has(install.packageRoot)) {
423
- byRoot.set(install.packageRoot, { version: install.version, note: `agents on PATH: ${install.binPath}` });
424
- }
425
- }
426
- if (byRoot.size < 2) {
417
+ const inventory = buildMultiInstallInventory(runningRoot, VERSION, findAgentsCliInstalls(process.env.PATH || ''));
418
+ if (inventory.length < 2) {
427
419
  try {
428
420
  fs.unlinkSync(sentinel);
429
421
  }
430
422
  catch { /* nothing recorded */ }
431
423
  return;
432
424
  }
433
- const key = [...byRoot.keys()].sort().join('\n');
425
+ const key = inventory
426
+ .map((info) => `${info.packageRoot}\t${info.version}\t${info.note}`)
427
+ .sort()
428
+ .join('\n');
434
429
  try {
435
430
  if (fs.readFileSync(sentinel, 'utf-8') === key)
436
431
  return;
437
432
  }
438
433
  catch { /* not warned for this set yet */ }
439
434
  console.error(chalk.yellow('Multiple agents-cli installs detected:'));
440
- for (const [root, info] of byRoot) {
441
- console.error(chalk.gray(` ${root} ${info.version} (${info.note})`));
435
+ for (const info of inventory) {
436
+ console.error(chalk.gray(` ${info.packageRoot} ${info.version} (${info.note})`));
442
437
  }
443
438
  console.error(chalk.gray('Upgrades apply to the running copy. Remove a stale copy with: npm uninstall -g --prefix <prefix> @phnx-labs/agents-cli'));
444
439
  try {
@@ -967,6 +962,7 @@ async function registerAllEagerCommands() {
967
962
  await reg(loadCost);
968
963
  await reg(loadInsights);
969
964
  await reg(loadPerf);
965
+ await reg(loadBench);
970
966
  await reg(loadTrends);
971
967
  await reg(loadOutput);
972
968
  await reg(loadBudget);
@@ -34,6 +34,17 @@ export declare function findInPath(command: string): string | null;
34
34
  export declare const AGENTS: Record<AgentId, AgentConfig>;
35
35
  /** All current and legacy agent IDs derived from the AGENTS registry. */
36
36
  export declare const ALL_AGENT_IDS: AgentId[];
37
+ /**
38
+ * CLI command templates per agent for daemon-fired routine jobs, with
39
+ * {prompt} as a placeholder. Lives here (not runner.ts) so routines.ts can
40
+ * import ROUTINE_AGENT_IDS for schedule-time validation without a circular
41
+ * import (runner.ts already imports from routines.ts).
42
+ */
43
+ export declare const ROUTINE_AGENT_COMMANDS: Record<string, string[]>;
44
+ /** Agents the routine daemon can actually run when firing locally, derived
45
+ * from the command table above so the `--agent` help and validateJob's
46
+ * schedule-time check can never drift from it. */
47
+ export declare const ROUTINE_AGENT_IDS: readonly string[];
37
48
  /** Agents retained only for legacy reads, not install/import/sync targets. */
38
49
  export declare const HARD_DEPRECATED_AGENT_IDS: AgentId[];
39
50
  /** Agents that can receive managed installs, imports, and resource sync writes. */
@@ -384,14 +384,22 @@ export const AGENTS = {
384
384
  instructionsFile: 'workspace/AGENTS.md', // Primary memory file (also has SOUL.md, IDENTITY.md, etc.)
385
385
  format: 'markdown',
386
386
  variableSyntax: '{{ARGUMENTS}}',
387
- supportsHooks: true,
387
+ // hooks: NOT supported. OpenClaw only exposes a fixed set of internal,
388
+ // named hooks (e.g. `boot-md`, which runs BOOT.md on gateway restart) —
389
+ // there is no general event->shell-command registration surface (no
390
+ // PreToolUse/PostToolUse/UserPromptSubmit equivalent an agents-cli
391
+ // hooks.yaml manifest could target). registerHooksToSettings has no
392
+ // `openclaw` case and silently no-ops (RUSH-2122: capability claimed
393
+ // hooks:true with zero hooks ever installed). Flip back to `true` only
394
+ // alongside a real registerHooksForOpenClaw implementation.
395
+ supportsHooks: false,
388
396
  // allowlist: maps blanket (whole-tool) rules to ~/.openclaw/openclaw.json
389
397
  // tools.alsoAllow (allow) / tools.deny (deny). OpenClaw gates at tool
390
398
  // granularity only, so sub-command/path/domain patterns are skipped.
391
399
  // OpenClaw is self-updating (no pinned since), so `true` is correct.
392
400
  // Workflows sync as Lobster `.lobster` files under `.openclaw/workflows/`;
393
401
  // the Lobster tool runs them by receiving the file path as `pipeline`.
394
- capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
402
+ capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
395
403
  },
396
404
  copilot: {
397
405
  id: 'copilot',
@@ -884,6 +892,25 @@ export const AGENTS = {
884
892
  };
885
893
  /** All current and legacy agent IDs derived from the AGENTS registry. */
886
894
  export const ALL_AGENT_IDS = Object.keys(AGENTS);
895
+ /**
896
+ * CLI command templates per agent for daemon-fired routine jobs, with
897
+ * {prompt} as a placeholder. Lives here (not runner.ts) so routines.ts can
898
+ * import ROUTINE_AGENT_IDS for schedule-time validation without a circular
899
+ * import (runner.ts already imports from routines.ts).
900
+ */
901
+ export const ROUTINE_AGENT_COMMANDS = {
902
+ claude: ['claude', '-p', '--verbose', '{prompt}', '--output-format', 'stream-json', '--permission-mode', 'plan'],
903
+ codex: ['codex', 'exec', '{prompt}', '--json'],
904
+ gemini: ['gemini', '{prompt}', '--output-format', 'stream-json'],
905
+ cursor: ['cursor-agent', '-p', '{prompt}', '--output-format', 'stream-json'],
906
+ kimi: ['kimi', '--prompt', '{prompt}', '--output-format', 'stream-json'],
907
+ droid: ['droid', 'exec', '{prompt}', '-o', 'stream-json'],
908
+ muse: ['muse', 'exec', '{prompt}', '--json'],
909
+ };
910
+ /** Agents the routine daemon can actually run when firing locally, derived
911
+ * from the command table above so the `--agent` help and validateJob's
912
+ * schedule-time check can never drift from it. */
913
+ export const ROUTINE_AGENT_IDS = Object.freeze(Object.keys(ROUTINE_AGENT_COMMANDS));
887
914
  /** Agents retained only for legacy reads, not install/import/sync targets. */
888
915
  export const HARD_DEPRECATED_AGENT_IDS = ALL_AGENT_IDS.filter((id) => AGENTS[id].deprecated?.hard);
889
916
  /** Agents that can receive managed installs, imports, and resource sync writes. */
@@ -1,11 +1,15 @@
1
- import { RECIPE_IDS, runRecipe, trendsWindow, type RecipeId, type RecipeSection, type TrendsWindow } from './recipes.js';
2
- export interface TrendsDashboard {
3
- window: TrendsWindow;
1
+ import { RECIPE_IDS, runRecipe, analyticsWindow, type RecipeId, type RecipeSection, type AnalyticsWindow } from './recipes.js';
2
+ export interface MixDashboard {
3
+ window: AnalyticsWindow;
4
4
  durationMs: number;
5
5
  sections: RecipeSection[];
6
6
  }
7
- export declare function buildTrendsDashboard(opts?: {
7
+ /** @deprecated Use MixDashboard. */
8
+ export type TrendsDashboard = MixDashboard;
9
+ export declare function buildMixDashboard(opts?: {
8
10
  days?: number;
9
11
  ids?: RecipeId[];
10
- }): TrendsDashboard;
11
- export { trendsWindow, runRecipe, RECIPE_IDS, type RecipeId, type RecipeSection };
12
+ }): MixDashboard;
13
+ /** @deprecated Use buildMixDashboard. */
14
+ export declare const buildTrendsDashboard: typeof buildMixDashboard;
15
+ export { analyticsWindow, analyticsWindow as trendsWindow, runRecipe, RECIPE_IDS, type RecipeId, type RecipeSection, type AnalyticsWindow, };
@@ -1,4 +1,4 @@
1
- import { RECIPE_IDS, runRecipe, trendsWindow, } from './recipes.js';
1
+ import { RECIPE_IDS, runRecipe, analyticsWindow, } from './recipes.js';
2
2
  const DEFAULT_ORDER = [
3
3
  'harness-mix',
4
4
  'model-mix',
@@ -9,9 +9,9 @@ const DEFAULT_ORDER = [
9
9
  'browser-activity',
10
10
  'resource-mix',
11
11
  ];
12
- export function buildTrendsDashboard(opts = {}) {
12
+ export function buildMixDashboard(opts = {}) {
13
13
  const t0 = Date.now();
14
- const win = trendsWindow(opts.days ?? 7);
14
+ const win = analyticsWindow(opts.days ?? 7);
15
15
  const ids = opts.ids ?? DEFAULT_ORDER;
16
16
  const sections = [];
17
17
  for (const id of ids) {
@@ -28,4 +28,6 @@ export function buildTrendsDashboard(opts = {}) {
28
28
  sections,
29
29
  };
30
30
  }
31
- export { trendsWindow, runRecipe, RECIPE_IDS };
31
+ /** @deprecated Use buildMixDashboard. */
32
+ export const buildTrendsDashboard = buildMixDashboard;
33
+ export { analyticsWindow, analyticsWindow as trendsWindow, runRecipe, RECIPE_IDS, };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Counter / warehouse mix recipes under `agents insights`.
3
+ *
4
+ * These used to live as the top-level `agents trends` tree. That name was a
5
+ * peer of `agents insights` with overlapping "analytics" meaning, so agents and
6
+ * humans kept picking the wrong verb. The cheap counter path (sessions index +
7
+ * usage.db) still exists — it is now `agents insights mix` and the recipe
8
+ * subcommands below. Latency stays on `agents perf`; quota on `agents usage`.
9
+ *
10
+ * `agents trends` remains a thin deprecated alias registered from
11
+ * `commands/trends.ts` that shares this same registration — no second copy of
12
+ * the recipe tree.
13
+ */
14
+ import type { Command } from 'commander';
15
+ export declare function parseMixDays(raw: string | undefined): number;
16
+ export declare function printMixSection(section: {
17
+ id: string;
18
+ title: string;
19
+ rows: Array<Record<string, string | number | null>>;
20
+ }): void;
21
+ export declare function renderMixDashboard(days: number, asJson: boolean, bannerLabel?: string): void;
22
+ /** One-line deprecation for the retired top-level `agents trends` spelling. */
23
+ export declare function printTrendsDeprecation(): void;
24
+ export interface RegisterMixCommandsOptions {
25
+ /**
26
+ * When true, every action prints the trends deprecation line first. Used only
27
+ * by the thin top-level `agents trends` alias — the real tree under insights
28
+ * never sets this.
29
+ */
30
+ deprecated?: boolean;
31
+ /**
32
+ * Banner label for the multi-recipe dashboard (default: agents insights mix).
33
+ * The deprecated alias passes `agents insights mix` still, so callers learn
34
+ * the new name even when they typed trends.
35
+ */
36
+ dashboardBanner?: string;
37
+ }
38
+ /**
39
+ * Attach counter-mix subcommands to a parent (typically `insights` or the
40
+ * deprecated `trends` alias).
41
+ *
42
+ * Layout:
43
+ * <parent> mix multi-recipe board (default for trends alias)
44
+ * <parent> recipes list recipe ids
45
+ * <parent> query raw usage.db rows
46
+ * <parent> harness-mix|… one baked recipe
47
+ */
48
+ export declare function registerMixCommands(parent: Command, opts?: RegisterMixCommandsOptions): void;
49
+ /**
50
+ * Thin top-level `agents trends` alias: same mix tree, deprecation on every path,
51
+ * default action = mix board (old `agents trends` with no subcommand).
52
+ */
53
+ export declare function registerDeprecatedTrendsAlias(program: Command): void;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Counter / warehouse mix recipes under `agents insights`.
3
+ *
4
+ * These used to live as the top-level `agents trends` tree. That name was a
5
+ * peer of `agents insights` with overlapping "analytics" meaning, so agents and
6
+ * humans kept picking the wrong verb. The cheap counter path (sessions index +
7
+ * usage.db) still exists — it is now `agents insights mix` and the recipe
8
+ * subcommands below. Latency stays on `agents perf`; quota on `agents usage`.
9
+ *
10
+ * `agents trends` remains a thin deprecated alias registered from
11
+ * `commands/trends.ts` that shares this same registration — no second copy of
12
+ * the recipe tree.
13
+ */
14
+ import chalk from 'chalk';
15
+ import { setHelpSections } from '../help.js';
16
+ import { buildMixDashboard, analyticsWindow, runRecipe, RECIPE_IDS, } from './dashboard.js';
17
+ import { listRecipes } from './recipes.js';
18
+ import { queryUsage, usageDbPath, USAGE_KINDS } from './usage-db.js';
19
+ export function parseMixDays(raw) {
20
+ const n = parseInt(raw ?? '7', 10);
21
+ return Number.isFinite(n) && n > 0 ? n : 7;
22
+ }
23
+ function parseLimit(raw, fallback) {
24
+ const n = parseInt(raw ?? String(fallback), 10);
25
+ return Number.isFinite(n) && n > 0 ? n : fallback;
26
+ }
27
+ export function printMixSection(section) {
28
+ console.log(chalk.bold(section.title));
29
+ if (section.rows.length === 0) {
30
+ console.log(chalk.gray(' (no data)'));
31
+ console.log();
32
+ return;
33
+ }
34
+ const keys = Object.keys(section.rows[0]);
35
+ const widths = keys.map((k) => Math.max(k.length, ...section.rows.map((r) => String(r[k] ?? '').length), 4));
36
+ const pad = (s, w) => (s.length >= w ? s.slice(0, w) : s + ' '.repeat(w - s.length));
37
+ console.log(chalk.gray(keys.map((k, i) => pad(k.toUpperCase(), widths[i])).join(' ')));
38
+ for (const row of section.rows) {
39
+ console.log(keys.map((k, i) => pad(String(row[k] ?? ''), widths[i])).join(' '));
40
+ }
41
+ console.log();
42
+ }
43
+ export function renderMixDashboard(days, asJson, bannerLabel = 'agents insights mix') {
44
+ const dash = buildMixDashboard({ days });
45
+ if (asJson) {
46
+ console.log(JSON.stringify(dash, null, 2));
47
+ return;
48
+ }
49
+ console.log(chalk.bold(`${bannerLabel} — last ${dash.window.days} days`));
50
+ console.log(chalk.gray(`compute ${dash.durationMs}ms · usage ${usageDbPath()}`));
51
+ console.log();
52
+ if (dash.sections.length === 0) {
53
+ console.log(chalk.gray('No session or usage data in this window yet.'));
54
+ return;
55
+ }
56
+ for (const section of dash.sections)
57
+ printMixSection(section);
58
+ }
59
+ /** One-line deprecation for the retired top-level `agents trends` spelling. */
60
+ export function printTrendsDeprecation() {
61
+ console.error(chalk.yellow('agents trends is deprecated — use `agents insights mix` (or `agents insights <recipe>`).'));
62
+ }
63
+ /**
64
+ * Attach counter-mix subcommands to a parent (typically `insights` or the
65
+ * deprecated `trends` alias).
66
+ *
67
+ * Layout:
68
+ * <parent> mix multi-recipe board (default for trends alias)
69
+ * <parent> recipes list recipe ids
70
+ * <parent> query raw usage.db rows
71
+ * <parent> harness-mix|… one baked recipe
72
+ */
73
+ export function registerMixCommands(parent, opts = {}) {
74
+ const before = () => {
75
+ if (opts.deprecated)
76
+ printTrendsDeprecation();
77
+ };
78
+ const banner = opts.dashboardBanner ?? 'agents insights mix';
79
+ const mix = parent
80
+ .command('mix')
81
+ .description('Counter recipes — harness/model mix, token ratios, resource frequency (sessions index + usage.db)')
82
+ .option('--days <n>', 'Days of history to include', '7')
83
+ .option('--json', 'Emit JSON instead of tables')
84
+ .action(function summary() {
85
+ before();
86
+ const o = this.opts();
87
+ renderMixDashboard(parseMixDays(o.days), Boolean(o.json), banner);
88
+ });
89
+ setHelpSections(mix, {
90
+ examples: `
91
+ # Auto recipe board (7d)
92
+ agents insights mix
93
+
94
+ # Last 30 days
95
+ agents insights mix --days 30
96
+
97
+ # One recipe as JSON
98
+ agents insights harness-mix --json
99
+
100
+ # Raw usage events
101
+ agents insights query --kind secret --days 7
102
+
103
+ # List baked recipe ids
104
+ agents insights recipes
105
+ `,
106
+ notes: `
107
+ Session recipes read sessions.db; resource recipes read ~/.agents/.history/analytics/usage.db.
108
+ Empty recipes are skipped on the default mix board.
109
+ This is the cheap counter path. Behavioural report (transcript content, account split)
110
+ is bare \`agents insights\`. Latency is \`agents perf\`; quota is \`agents usage\`.
111
+ Skill/slash-command popularity is \`agents sessions stats\`.
112
+ `,
113
+ });
114
+ parent.command('recipes')
115
+ .description('List baked mix-recipe ids')
116
+ .option('--json', 'Emit JSON')
117
+ .action((o) => {
118
+ before();
119
+ const list = listRecipes();
120
+ if (o.json) {
121
+ console.log(JSON.stringify(list, null, 2));
122
+ return;
123
+ }
124
+ for (const r of list) {
125
+ console.log(`${r.id.padEnd(22)} ${r.store.padEnd(10)} ${r.title}`);
126
+ }
127
+ });
128
+ parent.command('query')
129
+ .description('Raw usage-event query (usage.db)')
130
+ .option('--kind <kind>', `One of: ${USAGE_KINDS.join(', ')}`)
131
+ .option('--name <name>', 'Resource name filter')
132
+ .option('--event <event>', 'Event name filter')
133
+ .option('--days <n>', 'Days of history', '7')
134
+ .option('--limit <n>', 'Max rows', '40')
135
+ .option('--json', 'Emit JSON')
136
+ .action((o) => {
137
+ before();
138
+ const win = analyticsWindow(parseMixDays(o.days));
139
+ const kind = o.kind && USAGE_KINDS.includes(o.kind)
140
+ ? o.kind
141
+ : undefined;
142
+ if (o.kind && !kind) {
143
+ console.error(`Unknown kind '${o.kind}'. Expected: ${USAGE_KINDS.join(', ')}`);
144
+ process.exitCode = 1;
145
+ return;
146
+ }
147
+ const rows = queryUsage({
148
+ kind,
149
+ name: o.name,
150
+ event: o.event,
151
+ sinceIso: win.sinceIso,
152
+ limit: parseLimit(o.limit, 40),
153
+ });
154
+ if (o.json) {
155
+ console.log(JSON.stringify({ window: win, rows }, null, 2));
156
+ return;
157
+ }
158
+ if (rows.length === 0) {
159
+ console.log(chalk.gray('No usage events.'));
160
+ return;
161
+ }
162
+ printMixSection({
163
+ id: 'query',
164
+ title: 'Usage events',
165
+ rows: rows.map((r) => ({
166
+ ts: r.ts,
167
+ kind: r.kind,
168
+ name: r.name,
169
+ event: r.event,
170
+ agent: r.agent,
171
+ })),
172
+ });
173
+ });
174
+ for (const id of RECIPE_IDS) {
175
+ parent.command(id)
176
+ .description(`Mix recipe: ${id}`)
177
+ .option('--days <n>', 'Days of history', '7')
178
+ .option('--json', 'Emit JSON')
179
+ .action(function recipeAction() {
180
+ before();
181
+ const parentOpts = this.parent?.opts?.();
182
+ const o = { ...parentOpts, ...this.opts() };
183
+ const win = analyticsWindow(parseMixDays(o.days));
184
+ const section = runRecipe(id, win);
185
+ if (o.json) {
186
+ console.log(JSON.stringify({ window: win, section }, null, 2));
187
+ return;
188
+ }
189
+ if (section.empty) {
190
+ console.log(chalk.gray(`No data for recipe '${id}' in the last ${win.days} days.`));
191
+ return;
192
+ }
193
+ printMixSection(section);
194
+ });
195
+ }
196
+ }
197
+ /**
198
+ * Thin top-level `agents trends` alias: same mix tree, deprecation on every path,
199
+ * default action = mix board (old `agents trends` with no subcommand).
200
+ */
201
+ export function registerDeprecatedTrendsAlias(program) {
202
+ const trends = program
203
+ .command('trends')
204
+ .description('Deprecated alias of `agents insights mix` — counter recipes over sessions + usage.db')
205
+ .option('--days <n>', 'Days of history to include', '7')
206
+ .option('--json', 'Emit JSON instead of tables')
207
+ .action(function summary() {
208
+ printTrendsDeprecation();
209
+ const o = this.opts();
210
+ renderMixDashboard(parseMixDays(o.days), Boolean(o.json), 'agents insights mix');
211
+ });
212
+ setHelpSections(trends, {
213
+ examples: `
214
+ # Prefer the canonical spelling
215
+ agents insights mix
216
+
217
+ # Still works (prints a deprecation line)
218
+ agents trends
219
+ agents trends harness-mix --json
220
+ `,
221
+ notes: `
222
+ \`agents trends\` is a compatibility alias. New code and agent briefs should use
223
+ \`agents insights mix\` / \`agents insights <recipe>\`. Behavioural report remains
224
+ bare \`agents insights\`. Latency remains \`agents perf\`.
225
+ `,
226
+ });
227
+ // Same subcommands as insights, with deprecation on every fire.
228
+ registerMixCommands(trends, { deprecated: true, dashboardBanner: 'agents insights mix' });
229
+ }