@phnx-labs/agents-cli 1.20.72 → 1.20.74

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 (234) hide show
  1. package/CHANGELOG.md +395 -0
  2. package/README.md +40 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +12 -0
  5. package/dist/commands/activity.js +68 -0
  6. package/dist/commands/apply.d.ts +13 -0
  7. package/dist/commands/apply.js +28 -3
  8. package/dist/commands/browser.js +1 -1
  9. package/dist/commands/cloud.js +21 -11
  10. package/dist/commands/doctor.js +21 -5
  11. package/dist/commands/events.d.ts +11 -10
  12. package/dist/commands/events.js +30 -21
  13. package/dist/commands/exec.d.ts +34 -0
  14. package/dist/commands/exec.js +337 -39
  15. package/dist/commands/export.d.ts +11 -0
  16. package/dist/commands/export.js +215 -0
  17. package/dist/commands/feed.d.ts +13 -0
  18. package/dist/commands/feed.js +130 -29
  19. package/dist/commands/hq.d.ts +2 -0
  20. package/dist/commands/hq.js +58 -0
  21. package/dist/commands/import.js +51 -3
  22. package/dist/commands/lease.d.ts +19 -0
  23. package/dist/commands/lease.js +138 -11
  24. package/dist/commands/login.d.ts +2 -0
  25. package/dist/commands/login.js +123 -0
  26. package/dist/commands/logs.js +1 -1
  27. package/dist/commands/mine.d.ts +27 -0
  28. package/dist/commands/mine.js +207 -0
  29. package/dist/commands/monitors.js +79 -49
  30. package/dist/commands/plugins.js +1 -0
  31. package/dist/commands/profiles.js +158 -0
  32. package/dist/commands/repo.js +2 -60
  33. package/dist/commands/resources.d.ts +5 -0
  34. package/dist/commands/resources.js +95 -0
  35. package/dist/commands/routines.d.ts +2 -0
  36. package/dist/commands/routines.js +139 -37
  37. package/dist/commands/secrets.js +234 -47
  38. package/dist/commands/send.d.ts +15 -0
  39. package/dist/commands/send.js +62 -0
  40. package/dist/commands/sessions-tail.d.ts +1 -0
  41. package/dist/commands/sessions-tail.js +20 -9
  42. package/dist/commands/sessions.js +18 -3
  43. package/dist/commands/setup-mine.d.ts +18 -0
  44. package/dist/commands/setup-mine.js +106 -0
  45. package/dist/commands/setup-share.js +2 -2
  46. package/dist/commands/setup.js +9 -2
  47. package/dist/commands/share.d.ts +8 -1
  48. package/dist/commands/share.js +122 -37
  49. package/dist/commands/ssh.d.ts +9 -0
  50. package/dist/commands/ssh.js +196 -26
  51. package/dist/commands/sync.js +1 -1
  52. package/dist/commands/teams.js +11 -2
  53. package/dist/commands/versions.js +36 -5
  54. package/dist/commands/view.js +75 -27
  55. package/dist/index.js +80 -10
  56. package/dist/lib/activity.d.ts +124 -0
  57. package/dist/lib/activity.js +542 -0
  58. package/dist/lib/agents.d.ts +35 -0
  59. package/dist/lib/agents.js +79 -6
  60. package/dist/lib/artifact-actions.d.ts +1 -1
  61. package/dist/lib/artifact-actions.js +1 -1
  62. package/dist/lib/ask-classifier.d.ts +2 -1
  63. package/dist/lib/ask-classifier.js +3 -3
  64. package/dist/lib/auth-health.d.ts +32 -4
  65. package/dist/lib/auth-health.js +40 -1
  66. package/dist/lib/auto-pull.js +8 -1
  67. package/dist/lib/brand.d.ts +40 -0
  68. package/dist/lib/brand.js +122 -0
  69. package/dist/lib/browser/drivers/ssh.js +4 -7
  70. package/dist/lib/browser/service.js +6 -8
  71. package/dist/lib/channels/providers/index.d.ts +2 -0
  72. package/dist/lib/channels/providers/index.js +20 -0
  73. package/dist/lib/channels/providers/mailbox.d.ts +2 -0
  74. package/dist/lib/channels/providers/mailbox.js +26 -0
  75. package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
  76. package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
  77. package/dist/lib/channels/providers/rush.d.ts +6 -0
  78. package/dist/lib/channels/providers/rush.js +55 -0
  79. package/dist/lib/channels/registry.d.ts +42 -0
  80. package/dist/lib/channels/registry.js +20 -0
  81. package/dist/lib/channels/resolve.d.ts +11 -0
  82. package/dist/lib/channels/resolve.js +12 -0
  83. package/dist/lib/cli-resources.d.ts +18 -0
  84. package/dist/lib/cli-resources.js +53 -4
  85. package/dist/lib/cloud/codex.js +6 -3
  86. package/dist/lib/cloud/factory.d.ts +1 -0
  87. package/dist/lib/cloud/factory.js +10 -4
  88. package/dist/lib/cloud/rush.d.ts +3 -1
  89. package/dist/lib/cloud/rush.js +6 -1
  90. package/dist/lib/cloud/types.d.ts +2 -0
  91. package/dist/lib/codex-home.d.ts +35 -0
  92. package/dist/lib/codex-home.js +136 -0
  93. package/dist/lib/config-transfer.d.ts +16 -0
  94. package/dist/lib/config-transfer.js +56 -0
  95. package/dist/lib/crabbox/cli.d.ts +44 -0
  96. package/dist/lib/crabbox/cli.js +120 -20
  97. package/dist/lib/crabbox/lease.d.ts +52 -1
  98. package/dist/lib/crabbox/lease.js +117 -16
  99. package/dist/lib/crabbox/progress.d.ts +31 -0
  100. package/dist/lib/crabbox/progress.js +76 -0
  101. package/dist/lib/crabbox/runtimes.d.ts +4 -0
  102. package/dist/lib/crabbox/runtimes.js +33 -8
  103. package/dist/lib/crabbox/setup-copy.d.ts +87 -0
  104. package/dist/lib/crabbox/setup-copy.js +127 -0
  105. package/dist/lib/daemon.d.ts +5 -5
  106. package/dist/lib/daemon.js +9 -9
  107. package/dist/lib/drive-sync.js +6 -3
  108. package/dist/lib/event-stream.d.ts +36 -0
  109. package/dist/lib/event-stream.js +68 -0
  110. package/dist/lib/events.d.ts +1 -1
  111. package/dist/lib/exec.d.ts +11 -0
  112. package/dist/lib/exec.js +23 -2
  113. package/dist/lib/export.d.ts +72 -0
  114. package/dist/lib/export.js +269 -0
  115. package/dist/lib/feed-policy.d.ts +1 -1
  116. package/dist/lib/feed-policy.js +14 -9
  117. package/dist/lib/feed-ranking.d.ts +32 -0
  118. package/dist/lib/feed-ranking.js +224 -0
  119. package/dist/lib/feed.d.ts +35 -2
  120. package/dist/lib/feed.js +66 -3
  121. package/dist/lib/fleet/auth-sync.d.ts +53 -0
  122. package/dist/lib/fleet/auth-sync.js +92 -0
  123. package/dist/lib/fleet/remote-login.d.ts +187 -0
  124. package/dist/lib/fleet/remote-login.js +556 -0
  125. package/dist/lib/format.d.ts +30 -3
  126. package/dist/lib/format.js +34 -5
  127. package/dist/lib/fs-atomic.d.ts +7 -2
  128. package/dist/lib/fs-atomic.js +8 -12
  129. package/dist/lib/git.d.ts +16 -0
  130. package/dist/lib/git.js +79 -2
  131. package/dist/lib/heal.js +11 -3
  132. package/dist/lib/hosts/progress.d.ts +28 -10
  133. package/dist/lib/hosts/progress.js +79 -22
  134. package/dist/lib/hosts/remote-cmd.js +4 -0
  135. package/dist/lib/hq/floor.d.ts +87 -0
  136. package/dist/lib/hq/floor.js +226 -0
  137. package/dist/lib/import.d.ts +23 -0
  138. package/dist/lib/import.js +74 -2
  139. package/dist/lib/isolation-boundary-report.d.ts +10 -0
  140. package/dist/lib/isolation-boundary-report.js +35 -0
  141. package/dist/lib/menubar/install-menubar.js +14 -20
  142. package/dist/lib/notify.d.ts +1 -0
  143. package/dist/lib/notify.js +3 -3
  144. package/dist/lib/open-url.d.ts +2 -0
  145. package/dist/lib/open-url.js +19 -0
  146. package/dist/lib/openclaw-keychain.d.ts +56 -0
  147. package/dist/lib/openclaw-keychain.js +236 -0
  148. package/dist/lib/overdue.js +10 -0
  149. package/dist/lib/permissions.d.ts +44 -1
  150. package/dist/lib/permissions.js +284 -7
  151. package/dist/lib/project-launch.d.ts +6 -12
  152. package/dist/lib/project-launch.js +13 -228
  153. package/dist/lib/project-resources.d.ts +7 -0
  154. package/dist/lib/project-resources.js +306 -0
  155. package/dist/lib/pty-client.d.ts +27 -0
  156. package/dist/lib/pty-client.js +136 -10
  157. package/dist/lib/refresh.js +14 -10
  158. package/dist/lib/resource-profiles.d.ts +26 -0
  159. package/dist/lib/resource-profiles.js +157 -0
  160. package/dist/lib/resources/permissions.js +7 -1
  161. package/dist/lib/resources/types.d.ts +1 -1
  162. package/dist/lib/resources.d.ts +1 -1
  163. package/dist/lib/resources.js +32 -3
  164. package/dist/lib/routines.d.ts +11 -0
  165. package/dist/lib/routines.js +56 -15
  166. package/dist/lib/runner.d.ts +1 -0
  167. package/dist/lib/runner.js +66 -5
  168. package/dist/lib/secrets/bundles.d.ts +16 -3
  169. package/dist/lib/secrets/bundles.js +206 -37
  170. package/dist/lib/secrets/icloud-import.d.ts +2 -2
  171. package/dist/lib/secrets/icloud-import.js +9 -6
  172. package/dist/lib/secrets/mcp.js +1 -1
  173. package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
  174. package/dist/lib/secrets/vault-age-helper.js +34 -0
  175. package/dist/lib/secrets/vault.d.ts +49 -0
  176. package/dist/lib/secrets/vault.js +397 -0
  177. package/dist/lib/self-heal/checks/path.js +3 -1
  178. package/dist/lib/self-heal/checks/shadowing.js +10 -2
  179. package/dist/lib/self-heal/checks/shims.js +19 -11
  180. package/dist/lib/session/cloud.d.ts +2 -2
  181. package/dist/lib/session/cloud.js +2 -2
  182. package/dist/lib/session/db.d.ts +4 -0
  183. package/dist/lib/session/db.js +44 -7
  184. package/dist/lib/session/discover.d.ts +2 -0
  185. package/dist/lib/session/discover.js +114 -3
  186. package/dist/lib/session/stream-render.d.ts +3 -0
  187. package/dist/lib/session/stream-render.js +130 -0
  188. package/dist/lib/session/types.d.ts +6 -0
  189. package/dist/lib/share/analytics.d.ts +13 -0
  190. package/dist/lib/share/analytics.js +45 -0
  191. package/dist/lib/share/config.d.ts +19 -5
  192. package/dist/lib/share/config.js +44 -8
  193. package/dist/lib/share/provision.d.ts +58 -6
  194. package/dist/lib/share/provision.js +97 -22
  195. package/dist/lib/share/publish.d.ts +36 -13
  196. package/dist/lib/share/publish.js +55 -8
  197. package/dist/lib/share/worker-template.js +83 -17
  198. package/dist/lib/shims.d.ts +35 -0
  199. package/dist/lib/shims.js +159 -13
  200. package/dist/lib/ssh-exec.d.ts +14 -0
  201. package/dist/lib/ssh-exec.js +57 -0
  202. package/dist/lib/staleness/detectors/hooks.js +25 -1
  203. package/dist/lib/staleness/detectors/permissions.js +66 -0
  204. package/dist/lib/staleness/detectors/workflows.js +20 -0
  205. package/dist/lib/staleness/writers/hooks.js +58 -4
  206. package/dist/lib/staleness/writers/permissions.js +2 -2
  207. package/dist/lib/staleness/writers/skills.js +1 -1
  208. package/dist/lib/staleness/writers/sources.d.ts +10 -1
  209. package/dist/lib/staleness/writers/sources.js +68 -1
  210. package/dist/lib/star-nudge.d.ts +45 -0
  211. package/dist/lib/star-nudge.js +91 -0
  212. package/dist/lib/startup/command-registry.d.ts +7 -1
  213. package/dist/lib/startup/command-registry.js +19 -3
  214. package/dist/lib/state.d.ts +2 -0
  215. package/dist/lib/state.js +17 -4
  216. package/dist/lib/subagents-registry.d.ts +2 -0
  217. package/dist/lib/subagents-registry.js +3 -0
  218. package/dist/lib/teams/parsers.js +214 -6
  219. package/dist/lib/teams/supervisor.d.ts +7 -0
  220. package/dist/lib/teams/supervisor.js +2 -1
  221. package/dist/lib/template.d.ts +1 -1
  222. package/dist/lib/template.js +1 -1
  223. package/dist/lib/triggers/webhook.js +36 -3
  224. package/dist/lib/types.d.ts +84 -0
  225. package/dist/lib/uninstall.js +1 -45
  226. package/dist/lib/version.d.ts +40 -0
  227. package/dist/lib/version.js +94 -16
  228. package/dist/lib/versions.d.ts +24 -0
  229. package/dist/lib/versions.js +269 -78
  230. package/dist/lib/workflows.d.ts +2 -0
  231. package/dist/lib/workflows.js +88 -0
  232. package/package.json +2 -1
  233. package/dist/commands/daemon.d.ts +0 -10
  234. package/dist/commands/daemon.js +0 -121
package/dist/index.js CHANGED
@@ -26,6 +26,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
26
  const packageJsonPath = path.join(__dirname, '..', 'package.json');
27
27
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
28
28
  const VERSION = packageJson.version;
29
+ if (process.argv[2] === '__vault-age-helper') {
30
+ const { runVaultAgeHelperCli } = await import('./lib/secrets/vault-age-helper.js');
31
+ await runVaultAgeHelperCli();
32
+ process.exit(process.exitCode ?? 0);
33
+ }
29
34
  import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, downloadVerifiedTarball, } from './lib/self-update.js';
30
35
  // Detect dev/working-tree builds and default the noisy startup steps off.
31
36
  // Three cases trip this:
@@ -50,7 +55,7 @@ if (IS_DEV_BUILD) {
50
55
  // module on each invocation (which loaded the whole ~50-module tree before the
51
56
  // first byte of output), the registry maps a command name to a thunk that
52
57
  // imports only what that command needs. See src/lib/startup/command-registry.ts.
53
- import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages, loadDaemon, loadRoutines, loadMonitors, loadRun, loadFork, loadDefaults, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadCheck, loadStatus, loadProfiles, loadSecrets, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadDrive, loadFactory, loadUsage, loadCost, loadOutput, loadBudget, loadAlias, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
58
+ import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadResources, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadRun, loadFork, loadDefaults, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadCheck, loadStatus, loadProfiles, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadDrive, loadFactory, loadUsage, loadCost, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadHq, loadFeed, loadActivity, loadMailboxes, } from './lib/startup/command-registry.js';
54
59
  import { applyGlobalHelpConventions } from './lib/help.js';
55
60
  import { renderWhatsNew } from './lib/whats-new.js';
56
61
  import { emit, redactArgs } from './lib/events.js';
@@ -68,9 +73,18 @@ if (process.argv[2] === '__shim') {
68
73
  const code = await execShimPassthrough(agent, rawArgs, process.cwd(), pinned || undefined);
69
74
  process.exit(code);
70
75
  }
76
+ if (process.argv[2] === '__daemon-run') {
77
+ const { runDaemon } = await import('./lib/daemon.js');
78
+ await runDaemon();
79
+ process.exit(process.exitCode ?? 0);
80
+ }
81
+ // White-label: the shim for a brand (e.g. `jack`) exports AGENTS_BRAND, so the
82
+ // CLI presents its own name/help/errors as the brand. Unbranded (AGENTS_BRAND
83
+ // unset) resolves to 'agents' and everything below is byte-identical to before.
84
+ const BRAND = resolveBrandName();
71
85
  const program = new Command();
72
86
  program
73
- .name('agents')
87
+ .name(BRAND)
74
88
  .description('Environment manager for AI agents')
75
89
  .version(VERSION)
76
90
  .option('--verbose', 'Show startup self-heal details on stderr')
@@ -86,7 +100,7 @@ program
86
100
  function auditCommandPath(cmd) {
87
101
  const parts = [];
88
102
  let c = cmd;
89
- while (c && c.name() && c.name() !== 'agents') {
103
+ while (c && c.name() && c.name() !== BRAND) {
90
104
  parts.unshift(c.name());
91
105
  c = c.parent;
92
106
  }
@@ -129,11 +143,36 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
129
143
  // Best-effort completion record; the start line is the durable audit fact.
130
144
  }
131
145
  });
146
+ /**
147
+ * Skin the static root help for a brand: rewrite the visible `agents` command
148
+ * examples to the brand name and drop lines for commands this brand disabled.
149
+ * A no-op for the unbranded `agents` CLI with nothing disabled.
150
+ */
151
+ function brandRootHelp(raw) {
152
+ let text = raw;
153
+ if (BRAND !== 'agents') {
154
+ text = text
155
+ .replace(/Usage: agents /g, `Usage: ${BRAND} `)
156
+ .replace(/^ {2}agents /gm, ` ${BRAND} `)
157
+ .replace(/Run 'agents /g, `Run '${BRAND} `);
158
+ }
159
+ const disabled = disabledCommandsForActiveBrand();
160
+ if (disabled.size > 0) {
161
+ text = text
162
+ .split('\n')
163
+ .filter((line) => {
164
+ const m = line.match(/^ {2}([a-z][\w-]*)/);
165
+ return !(m && disabled.has(m[1]));
166
+ })
167
+ .join('\n');
168
+ }
169
+ return text;
170
+ }
132
171
  // Custom help for the main program only
133
172
  const originalHelpInformation = program.helpInformation.bind(program);
134
173
  program.helpInformation = function () {
135
- if (this.name() === 'agents' && !this.parent) {
136
- return `Usage: agents [command] [options]
174
+ if (this.name() === BRAND && !this.parent) {
175
+ return brandRootHelp(`Usage: agents [command] [options]
137
176
 
138
177
  Install, configure, run, and dispatch AI coding agents from one place.
139
178
  Works with Claude, Codex, Gemini, Cursor, OpenCode, OpenClaw, and Droid.
@@ -154,6 +193,7 @@ Agent versions:
154
193
  trash Inspect and restore soft-deleted version directories
155
194
  view [agent[@version]] List versions, or inspect one in detail
156
195
  inspect <target> Deep details for one agent+version, or a DotAgents repo (user|system|project|alias|path)
196
+ resources Show merged DotAgents resources with their winning layer
157
197
 
158
198
  Agent configuration (synced across versions):
159
199
  rules Instructions given to agents (CLAUDE.md, etc.)
@@ -173,6 +213,7 @@ Run and dispatch:
173
213
  run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
174
214
  defaults Configure run defaults by agent/version selector
175
215
  teams Coordinate multiple agents on shared work
216
+ hq JSON bridge for the interactive Agents HQ floor
176
217
  routines Run agents on a cron schedule (scheduler auto-starts)
177
218
  webhook Receive signed GitHub/Linear webhooks for trigger routines
178
219
  funnel Expose a webhook receiver through Tailscale Funnel
@@ -182,6 +223,7 @@ Run and dispatch:
182
223
  pty Drive interactive terminal programs (REPLs, TUIs) via a persistent PTY session
183
224
 
184
225
  Credentials and profiles:
226
+ profile Activate resource profiles across skills, MCP, permissions, and secrets
185
227
  profiles Bundles of (host CLI, endpoint, model, auth)
186
228
  secrets Keychain-backed env bundles; use 'secrets exec <bundle> -- <cmd>' to inject into a subprocess
187
229
 
@@ -213,7 +255,7 @@ Options:
213
255
  --verbose Show startup self-heal details on stderr
214
256
 
215
257
  System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
216
- `;
258
+ `);
217
259
  }
218
260
  return originalHelpInformation();
219
261
  };
@@ -251,6 +293,7 @@ async function showWhatsNew(fromVersion, toVersion) {
251
293
  }
252
294
  const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
253
295
  import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
296
+ import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
254
297
  import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, findAgentsCliInstalls, } from './lib/self-update.js';
255
298
  const UPDATE_CHECK_FILE = getUpdateCheckPath();
256
299
  /**
@@ -668,7 +711,9 @@ async function registerEagerForRequest(name) {
668
711
  async function registerAllEagerCommands() {
669
712
  await reg(loadView);
670
713
  await reg(loadShare);
714
+ await reg(loadSend);
671
715
  await reg(loadInspect);
716
+ await reg(loadResources);
672
717
  await reg(loadFeedback);
673
718
  await reg(loadCommands);
674
719
  await reg(loadHooks);
@@ -685,8 +730,8 @@ async function registerAllEagerCommands() {
685
730
  await reg(loadWorktree);
686
731
  await reg(loadVersions);
687
732
  await reg(loadImport);
733
+ await reg(loadExport);
688
734
  await reg(loadPackages);
689
- await reg(loadDaemon);
690
735
  await reg(loadRoutines);
691
736
  await reg(loadMonitors);
692
737
  await reg(loadRun);
@@ -703,6 +748,7 @@ async function registerAllEagerCommands() {
703
748
  registerExecAliasCommand(program);
704
749
  await reg(loadProfiles);
705
750
  await reg(loadSecrets);
751
+ await reg(loadLogin);
706
752
  await reg(loadWallet);
707
753
  await reg(loadHelper);
708
754
  await reg(loadMenubar);
@@ -717,6 +763,7 @@ async function registerAllEagerCommands() {
717
763
  await reg(loadOutput);
718
764
  await reg(loadBudget);
719
765
  await reg(loadAlias);
766
+ await reg(loadMine);
720
767
  await reg(loadPty);
721
768
  await reg(loadTmux);
722
769
  await reg(loadWatchdog);
@@ -728,7 +775,9 @@ async function registerAllEagerCommands() {
728
775
  await reg(loadAudit);
729
776
  await reg(loadWebhook);
730
777
  await reg(loadFunnel);
778
+ await reg(loadHq);
731
779
  await reg(loadFeed);
780
+ await reg(loadActivity);
732
781
  await reg(loadMailboxes);
733
782
  await reg(loadSsh);
734
783
  registerJobsCronAliasCommand(program, 'jobs');
@@ -795,13 +844,19 @@ const verboseStartup = passedArgs.includes('--verbose');
795
844
  // Help and version output are pure documentation — they must never gate on
796
845
  // setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
797
846
  const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
847
+ // White-label: a brand can hide built-in top-level commands. A hidden command
848
+ // must behave as if it doesn't exist under this brand (unknown-command +
849
+ // spellcheck), while `agents` itself is unaffected. `brandDisabled` is empty for
850
+ // the unbranded CLI, so all of this is a no-op there.
851
+ const brandDisabled = disabledCommandsForActiveBrand();
852
+ const requestedIsDisabled = requestedCommand !== undefined && brandDisabled.has(requestedCommand);
798
853
  // `--host` passthrough: run this invocation on a remote machine over SSH instead
799
854
  // of locally. Handled before any local command registration / update check /
800
855
  // background sync — a remote run needs none of that. Only the allowlisted
801
856
  // read-only + config + teams commands route here; `run`/`sessions` are absent
802
857
  // from the table and fall through to their own richer `--host` handling below.
803
858
  // `--help`/`--version` stay local (docs must work without a reachable host).
804
- if (requestedCommand !== undefined && !helpOrVersionRequested) {
859
+ if (requestedCommand !== undefined && !helpOrVersionRequested && !requestedIsDisabled) {
805
860
  const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
806
861
  if (await maybeRunOnHost(requestedCommand, passedArgs)) {
807
862
  process.exit(process.exitCode ?? 0);
@@ -810,7 +865,13 @@ if (requestedCommand !== undefined && !helpOrVersionRequested) {
810
865
  // Register only the command(s) this invocation actually uses. Lazy commands
811
866
  // (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
812
867
  const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
813
- if (requestedCommand !== undefined && !isLazyRequest) {
868
+ if (requestedIsDisabled) {
869
+ // The brand turned this command off: register the full tree so the "did you
870
+ // mean" picker still works, then strip the disabled commands below so the
871
+ // request resolves as unknown.
872
+ await registerAllEagerCommands();
873
+ }
874
+ else if (requestedCommand !== undefined && !isLazyRequest) {
814
875
  const known = await registerEagerForRequest(requestedCommand);
815
876
  if (!known) {
816
877
  // Unknown top-level command: register the full tree so the "did you mean"
@@ -828,10 +889,19 @@ if (requestedCommand !== undefined && !isLazyRequest) {
828
889
  applyGlobalHelpConventions(program);
829
890
  // Lazy commands pull in the SQLite-backed session/cloud stack; register them
830
891
  // only when explicitly requested, keeping lightweight commands off that path.
831
- if (isLazyRequest) {
892
+ if (isLazyRequest && !requestedIsDisabled) {
832
893
  for (const loader of COMMAND_LOADERS[requestedCommand])
833
894
  await reg(loader);
834
895
  }
896
+ // White-label: remove any commands this brand disabled so they resolve as
897
+ // unknown (the command:* handler then reports "unknown command"). Unbranded or
898
+ // nothing-disabled → no-op. Done after all registration paths above.
899
+ if (brandDisabled.size > 0) {
900
+ const kept = program.commands.filter((c) => !brandDisabled.has(c.name()));
901
+ if (kept.length !== program.commands.length) {
902
+ program.commands = kept;
903
+ }
904
+ }
835
905
  // Pure documentation paths (--version / --help / -h) return immediately: skip
836
906
  // the update check (PATH scan + cache read) and the detached background sync
837
907
  // (spawns a child process) that every other invocation runs.
@@ -0,0 +1,124 @@
1
+ import type { EventRecord } from './events.js';
2
+ /** Recognizable milestone events, ordered first in any activity lane. */
3
+ export type MilestoneEvent = 'plan.created' | 'pr.opened' | 'pr.merged' | 'worktree.created' | 'worktree.removed' | 'commit.created' | 'pushed' | 'subagent.spawned' | 'artifact.created';
4
+ /** Routine activity events, collapsed to counts by readers. */
5
+ export type ActivityKind = 'file.edited';
6
+ export type ActivityEventKind = MilestoneEvent | ActivityKind;
7
+ export type ActivityTier = 'milestone' | 'activity';
8
+ /** The set of milestone events, for tier classification and ordering. */
9
+ export declare const MILESTONE_EVENTS: readonly MilestoneEvent[];
10
+ export declare function tierForEvent(event: string): ActivityTier;
11
+ export interface ActivityEvent {
12
+ /** Schema version, for forward-compatible readers. */
13
+ v: number;
14
+ /** ISO-8601 timestamp of the event. */
15
+ ts: string;
16
+ /** Event kind (see {@link ActivityEventKind}). */
17
+ event: ActivityEventKind | string;
18
+ /** Coarse importance tier -- stamped by the writer, recomputed if absent. */
19
+ tier: ActivityTier;
20
+ /** Owning session id. */
21
+ sessionId: string;
22
+ /** Mailbox id for routing/joins (falls back to sessionId). */
23
+ mailboxId: string;
24
+ /** Short, normalized hostname the event fired on. */
25
+ host: string;
26
+ /** Runtime label (headless, tmux, cloud, ...). */
27
+ runtime: string;
28
+ /** Working directory at event time -- the join key to a project/git repo. */
29
+ cwd?: string;
30
+ /** Agent that produced the event (claude, codex, ...). */
31
+ agent?: string;
32
+ /** Tool that triggered the event (Bash, Task, ExitPlanMode, ...). */
33
+ tool?: string;
34
+ /** One-line human summary (plan title, PR command, sub-agent role). */
35
+ detail?: string;
36
+ /** Extracted URL when the event has one (e.g. the opened PR). */
37
+ url?: string;
38
+ }
39
+ /**
40
+ * Append one event to a session's activity log. Primarily the Python hook
41
+ * writes these at runtime; this TS writer exists for tests and any in-process
42
+ * emitter. Uses O_APPEND so concurrent appends never interleave a line.
43
+ */
44
+ export declare function appendActivityEvent(event: Omit<ActivityEvent, 'v' | 'tier'> & {
45
+ v?: number;
46
+ tier?: ActivityTier;
47
+ }, root?: string): void;
48
+ /** Read all events for one session (bounded tail). */
49
+ export declare function readSessionActivity(sessionId: string, root?: string, maxBytes?: number): ActivityEvent[];
50
+ /** List session ids that have an activity log. */
51
+ export declare function listActivitySessions(root?: string): string[];
52
+ export interface RecentActivityOptions {
53
+ /** Only include events at or after this epoch-ms. */
54
+ sinceMs?: number;
55
+ /** Cap the number of returned events (most recent first). */
56
+ limit?: number;
57
+ /** Override the activity dir (tests). */
58
+ root?: string;
59
+ /** Per-session tail budget in bytes. */
60
+ maxBytesPerSession?: number;
61
+ }
62
+ /**
63
+ * Merge recent events across every session's log, newest first. Reads only the
64
+ * tail of each file, so cost scales with active sessions, not transcript size.
65
+ */
66
+ export declare function readRecentActivity(opts?: RecentActivityOptions): ActivityEvent[];
67
+ export interface CollapsedActivity {
68
+ /** Milestone events, individually preserved, newest first. */
69
+ milestones: ActivityEvent[];
70
+ /** Routine events rolled up to counts, e.g. { 'file.edited': 12 }. */
71
+ counts: Record<string, number>;
72
+ /** Number of sub-agents spawned across the collapsed set. */
73
+ subagentCount: number;
74
+ }
75
+ /**
76
+ * Split a chronological event list into individual milestones plus a
77
+ * count map for the routine (tier-2) events, so a reader shows recognizable
78
+ * deliverables in full and collapses the noise.
79
+ */
80
+ export declare function collapseActivity(events: ActivityEvent[]): CollapsedActivity;
81
+ /**
82
+ * Normalize one activity event into the shared {@link EventRecord} shape so the
83
+ * agent-semantic stream reads through the same reader as operational events.
84
+ * `module` is stamped `activity` so `--module` filters partition the two cleanly.
85
+ */
86
+ export declare function activityEventToRecord(ev: ActivityEvent): EventRecord;
87
+ /** Read recent activity across sessions as unified {@link EventRecord}s. */
88
+ export declare function readActivityAsEventRecords(opts?: RecentActivityOptions): EventRecord[];
89
+ /** Glyph + color + human label per event, so the lane reads at a glance. */
90
+ export declare const EVENT_STYLE: Record<string, {
91
+ glyph: string;
92
+ color: (s: string) => string;
93
+ label: string;
94
+ }>;
95
+ export declare function styleForEvent(event: string): {
96
+ glyph: string;
97
+ color: (s: string) => string;
98
+ label: string;
99
+ };
100
+ /** One rendered activity line: ` <rel> [host] <glyph label> detail url`. */
101
+ export declare function formatActivityLine(ev: ActivityEvent, opts?: {
102
+ showHost?: boolean;
103
+ }): string;
104
+ /**
105
+ * The activity-log hook (Python), sibling to 10-feed-publish.py. Classifies
106
+ * PreToolUse/PostToolUse payloads into activity events and appends one JSONL
107
+ * line per event to ~/.agents/.history/activity/<sessionId>.jsonl.
108
+ *
109
+ * Matcher-gated to mutating/milestone tools (Bash|Task|ExitPlanMode|Write|
110
+ * Edit|MultiEdit) so read-only tools never pay the hook cost. Fail-open: any
111
+ * error is swallowed so a logging hiccup never blocks a tool call.
112
+ */
113
+ export declare const ACTIVITY_LOG_HOOK_SCRIPT = "#!/usr/bin/env python3\n\"\"\"Append agent-activity events for `agents feed` / `agents activity`.\n\nBound to PreToolUse (ExitPlanMode, Task) and PostToolUse (Bash, Write, Edit,\nMultiEdit). One append-only file per session; read-only tools never trigger it\nbecause the manifest matcher excludes them.\n\nSub-agent gate: when the payload carries `agent_type`, this is a Task/Agent\nsub-agent -- skip so only the top-level agent logs its own activity.\n\nFail-open: ANY error is swallowed so a logging hiccup never blocks a tool call.\n\"\"\"\nimport os\nimport re\nimport sys\nimport json\nimport shlex\nimport socket\nfrom datetime import datetime, timezone\n\nMAX_LOG_BYTES = 5 * 1024 * 1024 # cap a pathological session's log\nMILESTONE_EVENTS = {\n \"plan.created\", \"pr.opened\", \"pr.merged\", \"worktree.created\",\n \"worktree.removed\", \"commit.created\", \"pushed\", \"subagent.spawned\",\n \"artifact.created\",\n}\n\n# Deliverable file types + locations -- a Write here is a recognizable artifact\n# (an HTML plan, a PDF report, a rendered image), not a routine code edit.\nARTIFACT_EXTS = {\n \".html\", \".htm\", \".pdf\", \".png\", \".jpg\", \".jpeg\", \".gif\", \".svg\",\n \".webp\", \".mp4\", \".mov\", \".webm\", \".csv\", \".xlsx\", \".pptx\", \".docx\",\n}\nARTIFACT_DIR_HINTS = (\"/tmp/\", \"/downloads/\", \"/.agents/artifacts/\")\n\n\ndef is_artifact(file_path):\n low = (file_path or \"\").lower()\n if os.path.splitext(low)[1] in ARTIFACT_EXTS:\n return True\n return any(hint in low for hint in ARTIFACT_DIR_HINTS)\n\n\ndef first_line(text, limit=140):\n for raw in (text or \"\").splitlines():\n s = raw.strip().lstrip(\"#\").strip()\n if s:\n return s[:limit]\n return \"\"\n\n\ndef _subcommand(tokens, tool):\n \"\"\"First non-flag token after `tool`, i.e. its subcommand (skips -C <path>,\n -c <cfg>, and other leading flags). None if `tool` isn't the invoked command.\"\"\"\n try:\n i = tokens.index(tool) + 1\n except ValueError:\n return None, []\n while i < len(tokens):\n t = tokens[i]\n if t in (\"-C\", \"-c\", \"--git-dir\", \"--work-tree\"):\n i += 2 # flag that consumes the next token\n continue\n if t.startswith(\"-\"):\n i += 1\n continue\n return t, tokens[i + 1:]\n return None, []\n\n\ndef classify_bash(command):\n \"\"\"Return the milestone event for a git/gh command, else None. Tokenizes so a\n path like `git diff -- src/commit.ts` is not mistaken for a commit.\"\"\"\n try:\n tokens = shlex.split(command or \"\")\n except Exception:\n tokens = (command or \"\").split()\n\n verb, rest = _subcommand(tokens, \"git\")\n if verb == \"worktree\":\n sub = rest[0] if rest else \"\"\n if sub == \"add\":\n return \"worktree.created\"\n if sub == \"remove\":\n return \"worktree.removed\"\n elif verb == \"commit\":\n return \"commit.created\"\n elif verb == \"push\":\n return \"pushed\"\n\n verb, rest = _subcommand(tokens, \"gh\")\n if verb == \"pr\" and rest:\n if rest[0] == \"create\":\n return \"pr.opened\"\n if rest[0] == \"merge\":\n return \"pr.merged\"\n return None\n\n\ndef extract_url(tool_response):\n \"\"\"Pull the first https URL out of a Bash tool response (stdout).\"\"\"\n text = \"\"\n if isinstance(tool_response, dict):\n text = str(tool_response.get(\"stdout\") or tool_response.get(\"output\") or \"\")\n elif isinstance(tool_response, str):\n text = tool_response\n m = re.search(r\"https?://\\S+\", text)\n return m.group(0).rstrip(\").,\") if m else None\n\n\ndef build_event(payload, hook_event):\n tool_name = payload.get(\"tool_name\", \"\")\n tool_input = payload.get(\"tool_input\", {}) or {}\n tool_response = payload.get(\"tool_response\", {})\n\n event = None\n detail = None\n url = None\n\n if hook_event == \"PreToolUse\":\n if tool_name == \"ExitPlanMode\":\n event = \"plan.created\"\n detail = first_line(tool_input.get(\"plan\", \"\")) or \"plan presented\"\n elif tool_name == \"Task\":\n event = \"subagent.spawned\"\n role = tool_input.get(\"subagent_type\") or \"agent\"\n desc = tool_input.get(\"description\") or tool_input.get(\"prompt\") or \"\"\n detail = (role + \": \" + first_line(desc)).strip(\": \").strip()\n elif hook_event == \"PostToolUse\":\n if tool_name == \"Bash\":\n event = classify_bash(tool_input.get(\"command\", \"\"))\n if event:\n detail = first_line(tool_input.get(\"command\", \"\"))\n url = extract_url(tool_response)\n elif tool_name in (\"Write\", \"Edit\", \"MultiEdit\"):\n fp = tool_input.get(\"file_path\") or tool_input.get(\"path\") or \"\"\n # A freshly-written deliverable is a milestone; edits and code\n # writes stay routine and collapse to a count.\n if tool_name == \"Write\" and is_artifact(fp):\n event = \"artifact.created\"\n else:\n event = \"file.edited\"\n detail = os.path.basename(fp) if fp else tool_name\n\n if not event:\n return None, tool_name\n\n tier = \"milestone\" if event in MILESTONE_EVENTS else \"activity\"\n record = {\n \"v\": 1,\n \"ts\": datetime.now(timezone.utc).isoformat(),\n \"event\": event,\n \"tier\": tier,\n }\n if detail:\n record[\"detail\"] = detail\n if url:\n record[\"url\"] = url\n record[\"tool\"] = tool_name\n return record, tool_name\n\n\ndef main():\n raw = sys.stdin.read()\n try:\n payload = json.loads(raw) if raw.strip() else {}\n except Exception:\n return\n\n # Sub-agent gate -- only the top-level agent logs.\n if payload.get(\"agent_type\"):\n return\n\n session_id = payload.get(\"session_id\", \"\")\n if not session_id:\n return\n\n hook_event = payload.get(\"hook_event_name\", \"\")\n record, tool_name = build_event(payload, hook_event)\n if not record:\n return\n\n safe_session = re.sub(r\"[^A-Za-z0-9._-]\", \"-\", session_id) or \"unknown\"\n home = os.environ.get(\"HOME\") or os.path.expanduser(\"~\")\n activity_dir = os.path.join(home, \".agents\", \".history\", \"activity\")\n target = os.path.join(activity_dir, safe_session + \".jsonl\")\n\n # Identity (mirrors 10-feed-publish.py).\n mailbox_id = os.path.basename(\n os.environ.get(\"AGENTS_MAILBOX_DIR\", \"\").rstrip(\"/\")\n ) or session_id\n hostname = os.environ.get(\"AGENTS_SYNC_MACHINE_ID\") or socket.gethostname()\n host = re.sub(r\"[^a-z0-9_-]\", \"-\", hostname.split(\".\")[0].strip().lower()) or \"unknown\"\n\n record[\"sessionId\"] = session_id\n record[\"mailboxId\"] = mailbox_id\n record[\"host\"] = host\n record[\"runtime\"] = os.environ.get(\"AGENTS_RUNTIME\", \"headless\")\n cwd = payload.get(\"cwd\") or os.environ.get(\"AGENTS_CWD\")\n if cwd:\n record[\"cwd\"] = cwd\n agent = os.environ.get(\"AGENTS_AGENT_NAME\") or \"claude\"\n record[\"agent\"] = agent\n\n try:\n os.makedirs(activity_dir, exist_ok=True)\n # Cap growth: once over the size limit, keep only milestones.\n if record[\"tier\"] != \"milestone\":\n try:\n if os.path.getsize(target) > MAX_LOG_BYTES:\n return\n except OSError:\n pass\n with open(target, \"a\") as f:\n f.write(json.dumps(record) + \"\\n\")\n except Exception:\n pass # fail open\n\n\nif __name__ == \"__main__\":\n try:\n main()\n except Exception:\n pass # fail open\n";
114
+ /** Hook manifest entries (agents.yaml shape) for the activity-log hook. */
115
+ export declare const ACTIVITY_HOOK_DEFINITIONS: Record<string, Record<string, unknown>>;
116
+ /**
117
+ * Install the activity-log hook script into the user hooks dir and add its
118
+ * manifest entries to the user agents.yaml. Mirrors {@link ensureFeedPublishHook}
119
+ * in feed.ts -- idempotent, and never writes the read-only system repo.
120
+ */
121
+ export declare function ensureActivityLogHook(userAgentsDir?: string): {
122
+ installed: boolean;
123
+ error?: string;
124
+ };