@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
@@ -23,6 +23,12 @@ import { machineId, normalizeHost } from '../lib/machine-id.js';
23
23
  import { loadDevices } from '../lib/devices/registry.js';
24
24
  import { setHelpSections } from '../lib/help.js';
25
25
  import { isInteractiveTerminal, requireInteractiveSelection } from './utils.js';
26
+ function stdoutJson(payload) {
27
+ process.stdout.write(JSON.stringify(payload) + '\n');
28
+ }
29
+ function stderrLine(message) {
30
+ process.stderr.write(message + '\n');
31
+ }
26
32
  /** A one-line human label for what a monitor watches. */
27
33
  function sourceLabel(source) {
28
34
  switch (source.type) {
@@ -70,7 +76,7 @@ function ownerLabel(monitor) {
70
76
  function ensureDaemonRunning() {
71
77
  if (isDaemonRunning()) {
72
78
  signalDaemonReload();
73
- console.log(chalk.gray('Daemon reloaded'));
79
+ stderrLine(chalk.gray('Daemon reloaded'));
74
80
  return;
75
81
  }
76
82
  const result = startDaemon();
@@ -79,22 +85,22 @@ function ensureDaemonRunning() {
79
85
  console.log(chalk.gray('Stop anytime with: agents routines stop'));
80
86
  }
81
87
  else {
82
- console.log(chalk.yellow('Could not start the daemon. Start it manually with: agents routines start'));
88
+ stderrLine(chalk.yellow('Could not start the daemon. Start it manually with: agents routines start'));
83
89
  }
84
90
  }
85
91
  /** Validate a single device name against the registered fleet; exit on miss. */
86
92
  async function validateDevice(name) {
87
93
  const normalized = normalizeHost(name.trim());
88
94
  if (!normalized) {
89
- console.log(chalk.red('device name must be non-empty'));
95
+ stderrLine(chalk.red('device name must be non-empty'));
90
96
  process.exit(1);
91
97
  }
92
98
  const registry = await loadDevices();
93
99
  const registered = new Set(Object.keys(registry).map((k) => normalizeHost(k)));
94
100
  if (!registered.has(normalized)) {
95
- console.log(chalk.red(`Unknown device: ${normalized}`));
96
- console.log(chalk.gray(`Registered: ${[...registered].sort().join(', ') || '(none)'}`));
97
- console.log(chalk.gray('Enroll devices with: agents devices sync'));
101
+ stderrLine(chalk.red(`Unknown device: ${normalized}`));
102
+ stderrLine(chalk.gray(`Registered: ${[...registered].sort().join(', ') || '(none)'}`));
103
+ stderrLine(chalk.gray('Enroll devices with: agents devices sync'));
98
104
  process.exit(1);
99
105
  }
100
106
  return normalized;
@@ -126,7 +132,7 @@ function buildSource(options) {
126
132
  const raw = String(options.on);
127
133
  const [src, event] = raw.includes(':') ? raw.split(':', 2) : ['github', raw];
128
134
  if (src !== 'github' && src !== 'linear') {
129
- console.log(chalk.red('--on source must be github or linear'));
135
+ stderrLine(chalk.red('--on source must be github or linear'));
130
136
  process.exit(1);
131
137
  }
132
138
  const webhook = { source: src, event };
@@ -143,11 +149,11 @@ function buildSource(options) {
143
149
  chosen.push({ type: 'webhook', source: { type: 'webhook', webhook } });
144
150
  }
145
151
  if (chosen.length === 0) {
146
- console.log(chalk.red('A source is required: --watch, --poll, --poll-http, --ws, --watch-file, --watch-device, or --on'));
152
+ stderrLine(chalk.red('A source is required: --watch, --poll, --poll-http, --ws, --watch-file, --watch-device, or --on'));
147
153
  process.exit(1);
148
154
  }
149
155
  if (chosen.length > 1) {
150
- console.log(chalk.red(`Exactly one source is allowed; got ${chosen.map((c) => c.type).join(', ')}`));
156
+ stderrLine(chalk.red(`Exactly one source is allowed; got ${chosen.map((c) => c.type).join(', ')}`));
151
157
  process.exit(1);
152
158
  }
153
159
  return chosen[0].source;
@@ -162,7 +168,7 @@ function buildCondition(options) {
162
168
  if (options.every)
163
169
  modes.push('every');
164
170
  if (modes.length > 1) {
165
- console.log(chalk.red('--on-change, --match, and --every are mutually exclusive'));
171
+ stderrLine(chalk.red('--on-change, --match, and --every are mutually exclusive'));
166
172
  process.exit(1);
167
173
  }
168
174
  const mode = modes[0] ?? (options.match ? 'match' : 'on-change');
@@ -196,11 +202,11 @@ function buildAction(options) {
196
202
  if (options.webhookOut)
197
203
  chosen.push({ type: 'webhook-out', url: options.webhookOut });
198
204
  if (chosen.length === 0) {
199
- console.log(chalk.red('An action is required: --run <agent> --prompt, --routine, --notify, or --webhook-out'));
205
+ stderrLine(chalk.red('An action is required: --run <agent> --prompt, --routine, --notify, or --webhook-out'));
200
206
  process.exit(1);
201
207
  }
202
208
  if (chosen.length > 1) {
203
- console.log(chalk.red(`Exactly one action is allowed; got ${chosen.map((c) => c.type).join(', ')}`));
209
+ stderrLine(chalk.red(`Exactly one action is allowed; got ${chosen.map((c) => c.type).join(', ')}`));
204
210
  process.exit(1);
205
211
  }
206
212
  return chosen[0];
@@ -209,7 +215,7 @@ function buildAction(options) {
209
215
  async function pickMonitor(message, alternatives = []) {
210
216
  const monitors = listMonitors();
211
217
  if (monitors.length === 0) {
212
- console.log(chalk.yellow('No monitors configured'));
218
+ stderrLine(chalk.yellow('No monitors configured'));
213
219
  return null;
214
220
  }
215
221
  if (!isInteractiveTerminal()) {
@@ -227,7 +233,7 @@ async function pickMonitor(message, alternatives = []) {
227
233
  }
228
234
  catch (err) {
229
235
  if (err instanceof Error && (err.name === 'ExitPromptError' || err.message.includes('User force closed'))) {
230
- console.log(chalk.gray('Cancelled'));
236
+ stderrLine(chalk.gray('Cancelled'));
231
237
  return null;
232
238
  }
233
239
  throw err;
@@ -322,16 +328,16 @@ export function registerMonitorsCommands(program) {
322
328
  parsed = yaml.parse(fs.readFileSync(resolved, 'utf-8'));
323
329
  }
324
330
  catch (err) {
325
- console.log(chalk.red(`Invalid YAML: ${err.message}`));
331
+ stderrLine(chalk.red(`Invalid YAML: ${err.message}`));
326
332
  process.exit(1);
327
333
  }
328
334
  const name = parsed?.name || path.basename(resolved).replace(/\.ya?ml$/, '');
329
335
  const config = { enabled: true, ...parsed, name };
330
336
  const errors = validateMonitor(config);
331
337
  if (errors.length > 0) {
332
- console.log(chalk.red('Validation errors:'));
338
+ stderrLine(chalk.red('Validation errors:'));
333
339
  for (const err of errors)
334
- console.log(chalk.red(` - ${err}`));
340
+ stderrLine(chalk.red(` - ${err}`));
335
341
  process.exit(1);
336
342
  }
337
343
  writeMonitor(config);
@@ -340,8 +346,8 @@ export function registerMonitorsCommands(program) {
340
346
  return;
341
347
  }
342
348
  if (!nameOrPath) {
343
- console.log(chalk.red('Monitor name is required'));
344
- console.log(chalk.gray('Usage: agents monitors add <name> --poll "<cmd>" 30s --match fail --run claude --prompt "..."'));
349
+ stderrLine(chalk.red('Monitor name is required'));
350
+ stderrLine(chalk.gray('Usage: agents monitors add <name> --poll "<cmd>" 30s --match fail --run claude --prompt "..."'));
345
351
  process.exit(1);
346
352
  }
347
353
  const source = buildSource(options);
@@ -357,7 +363,7 @@ export function registerMonitorsCommands(program) {
357
363
  let device;
358
364
  let devices;
359
365
  if (options.device && options.devices) {
360
- console.log(chalk.red('--device (single owner) and --devices (allowlist) are mutually exclusive'));
366
+ stderrLine(chalk.red('--device (single owner) and --devices (allowlist) are mutually exclusive'));
361
367
  process.exit(1);
362
368
  }
363
369
  if (options.device)
@@ -371,13 +377,13 @@ export function registerMonitorsCommands(program) {
371
377
  // --run-on with no owner pin would fire from every daemon → duplicate actions.
372
378
  if (options.runOn && !device && !devices) {
373
379
  device = machineId();
374
- console.log(chalk.gray(`--run-on set with no --device/--devices: pinned owner to this machine (${device}).`));
380
+ stderrLine(chalk.gray(`--run-on set with no --device/--devices: pinned owner to this machine (${device}).`));
375
381
  }
376
382
  let rateLimit;
377
383
  if (options.rateLimit) {
378
384
  const m = String(options.rateLimit).match(/^(\d+)\/(.+)$/);
379
385
  if (!m) {
380
- console.log(chalk.red('--rate-limit must be N/<interval>, e.g. 5/1m'));
386
+ stderrLine(chalk.red('--rate-limit must be N/<interval>, e.g. 5/1m'));
381
387
  process.exit(1);
382
388
  }
383
389
  rateLimit = { max: parseInt(m[1], 10), per: m[2] };
@@ -395,15 +401,15 @@ export function registerMonitorsCommands(program) {
395
401
  };
396
402
  const errors = validateMonitor(config);
397
403
  if (errors.length > 0) {
398
- console.log(chalk.red('Validation errors:'));
404
+ stderrLine(chalk.red('Validation errors:'));
399
405
  for (const err of errors)
400
- console.log(chalk.red(` - ${err}`));
406
+ stderrLine(chalk.red(` - ${err}`));
401
407
  process.exit(1);
402
408
  }
403
409
  // Coverage lint (Anthropic's "silence is not success"): warn when a --match
404
410
  // names only a success-shaped token with no failure branch.
405
411
  if (condition.mode === 'match' && condition.match && /^(issued|success|ok|pass(ed)?|done|ready)$/i.test(condition.match)) {
406
- console.log(chalk.yellow(` Note: --match '${condition.match}' only fires on success — it stays silent if the source breaks or never matches.`));
412
+ stderrLine(chalk.yellow(` Note: --match '${condition.match}' only fires on success — it stays silent if the source breaks or never matches.`));
407
413
  }
408
414
  writeMonitor(config);
409
415
  console.log(chalk.green(`Monitor '${nameOrPath}' added`));
@@ -432,7 +438,7 @@ export function registerMonitorsCommands(program) {
432
438
  lastFiredAt: state?.lastFiredAt ?? null,
433
439
  };
434
440
  });
435
- process.stdout.write(JSON.stringify(payload) + '\n');
441
+ stdoutJson(payload);
436
442
  return;
437
443
  }
438
444
  if (monitors.length === 0) {
@@ -456,7 +462,8 @@ export function registerMonitorsCommands(program) {
456
462
  monitorsCmd
457
463
  .command('view [name]')
458
464
  .description('Show a monitor’s full YAML config plus its current watched-state and recent fires.')
459
- .action(async (name) => {
465
+ .option('--json', 'Emit machine-readable JSON')
466
+ .action(async (name, options) => {
460
467
  if (!name) {
461
468
  name = (await pickMonitor('Select monitor to view', ['agents monitors view <name>'])) ?? undefined;
462
469
  if (!name)
@@ -464,12 +471,24 @@ export function registerMonitorsCommands(program) {
464
471
  }
465
472
  const monitor = readMonitor(name);
466
473
  if (!monitor) {
467
- console.log(chalk.red(`Monitor '${name}' not found`));
474
+ stderrLine(chalk.red(`Monitor '${name}' not found`));
468
475
  process.exit(1);
469
476
  }
477
+ const state = readState(name);
478
+ const recentFires = listFires(name).slice(-5);
479
+ if (options.json) {
480
+ stdoutJson({
481
+ name,
482
+ monitor,
483
+ owner: ownerLabel(monitor),
484
+ runsHere: monitorRunsOnThisDevice(monitor),
485
+ state,
486
+ recentFires,
487
+ });
488
+ return;
489
+ }
470
490
  console.log(chalk.bold(`Monitor: ${name}\n`));
471
491
  console.log(yaml.stringify(monitor));
472
- const state = readState(name);
473
492
  if (state) {
474
493
  console.log(chalk.bold('Watched state'));
475
494
  console.log(chalk.gray(` last seen: ${state.lastSeenAt}`));
@@ -477,10 +496,9 @@ export function registerMonitorsCommands(program) {
477
496
  console.log(chalk.gray(` last fired: ${state.lastFiredAt}`));
478
497
  console.log(chalk.gray(` last value: ${state.lastValue.replace(/\s+/g, ' ').slice(0, 120)}`));
479
498
  }
480
- const fires = listFires(name).slice(-5);
481
- if (fires.length > 0) {
499
+ if (recentFires.length > 0) {
482
500
  console.log(chalk.bold('\nRecent fires'));
483
- for (const f of fires) {
501
+ for (const f of recentFires) {
484
502
  console.log(` ${chalk.gray(f.firedAt)} ${f.action ?? '?'} ${f.ok === false ? chalk.red('failed') : chalk.green('ok')}`);
485
503
  }
486
504
  }
@@ -489,7 +507,8 @@ export function registerMonitorsCommands(program) {
489
507
  monitorsCmd
490
508
  .command('test [name]')
491
509
  .description('DRY-RUN: evaluate the source once and print the emitted event + whether it would fire. No action is taken.')
492
- .action(async (name) => {
510
+ .option('--json', 'Emit machine-readable JSON')
511
+ .action(async (name, options) => {
493
512
  if (!name) {
494
513
  name = (await pickMonitor('Select monitor to test', ['agents monitors test <name>'])) ?? undefined;
495
514
  if (!name)
@@ -497,12 +516,24 @@ export function registerMonitorsCommands(program) {
497
516
  }
498
517
  const monitor = readMonitor(name);
499
518
  if (!monitor) {
500
- console.log(chalk.red(`Monitor '${name}' not found`));
519
+ stderrLine(chalk.red(`Monitor '${name}' not found`));
501
520
  process.exit(1);
502
521
  }
522
+ const { observation, decision } = await evaluateMonitorOnce(monitor);
523
+ const wouldFire = Boolean(decision?.fire);
524
+ if (options.json) {
525
+ stdoutJson({
526
+ name,
527
+ dryRun: true,
528
+ monitor,
529
+ observation,
530
+ decision,
531
+ wouldFire,
532
+ });
533
+ return;
534
+ }
503
535
  console.log(chalk.bold(`Dry-run: ${name}\n`));
504
536
  console.log(chalk.gray(` ${sourceLabel(monitor.source)} · [${monitor.condition.mode}] · ${actionLabel(monitor.action)}\n`));
505
- const { observation, decision } = await evaluateMonitorOnce(monitor);
506
537
  if (!observation) {
507
538
  console.log(chalk.yellow('No observation — this source is push-only (ws/webhook) or produced nothing this tick.'));
508
539
  return;
@@ -511,7 +542,6 @@ export function registerMonitorsCommands(program) {
511
542
  console.log(observation.raw.split('\n').slice(0, 20).map((l) => ` ${l}`).join('\n'));
512
543
  if (observation.meta)
513
544
  console.log(chalk.gray(` meta: ${JSON.stringify(observation.meta)}`));
514
- const wouldFire = Boolean(decision?.fire);
515
545
  console.log('');
516
546
  console.log(`Would fire: ${wouldFire ? chalk.green('yes') : chalk.gray('no')}`);
517
547
  if (decision?.event) {
@@ -560,15 +590,15 @@ export function registerMonitorsCommands(program) {
560
590
  return;
561
591
  const errors = validateMonitor(monitor);
562
592
  if (errors.length > 0) {
563
- console.log(chalk.yellow('\nWarning: monitor has validation errors:'));
593
+ stderrLine(chalk.yellow('\nWarning: monitor has validation errors:'));
564
594
  for (const err of errors)
565
- console.log(chalk.yellow(` - ${err}`));
595
+ stderrLine(chalk.yellow(` - ${err}`));
566
596
  }
567
597
  else {
568
598
  console.log(chalk.green(`\nMonitor '${name}' saved`));
569
599
  if (isDaemonRunning()) {
570
600
  signalDaemonReload();
571
- console.log(chalk.gray('Daemon reloaded'));
601
+ stderrLine(chalk.gray('Daemon reloaded'));
572
602
  }
573
603
  }
574
604
  });
@@ -587,14 +617,14 @@ export function registerMonitorsCommands(program) {
587
617
  }
588
618
  const run = options.run ? listRuns(name).find((r) => r.runId === options.run) : getLatestRun(name);
589
619
  if (!run) {
590
- console.log(chalk.yellow(`No action runs found for monitor '${name}'`));
591
- console.log(chalk.gray(' (notify / webhook-out actions have no run log — see: agents monitors runs)'));
620
+ stderrLine(chalk.yellow(`No action runs found for monitor '${name}'`));
621
+ stderrLine(chalk.gray(' (notify / webhook-out actions have no run log — see: agents monitors runs)'));
592
622
  return;
593
623
  }
594
624
  const logPath = path.join(getRunDir(name, run.runId), 'stdout.log');
595
625
  if (options.full) {
596
626
  if (!fs.existsSync(logPath)) {
597
- console.log(chalk.yellow(`Log not found: ${logPath}`));
627
+ stderrLine(chalk.yellow(`Log not found: ${logPath}`));
598
628
  return;
599
629
  }
600
630
  console.log(chalk.gray(`Run: ${run.runId}\n`));
@@ -628,7 +658,7 @@ export function registerMonitorsCommands(program) {
628
658
  }
629
659
  const fires = listFires(name);
630
660
  if (fires.length === 0) {
631
- console.log(chalk.yellow(`No fires recorded for monitor '${name}'`));
661
+ stderrLine(chalk.yellow(`No fires recorded for monitor '${name}'`));
632
662
  return;
633
663
  }
634
664
  console.log(chalk.bold(`Fire history: ${name}\n`));
@@ -656,7 +686,7 @@ export function registerMonitorsCommands(program) {
656
686
  signalDaemonReload();
657
687
  }
658
688
  catch (err) {
659
- console.log(chalk.red(err.message));
689
+ stderrLine(chalk.red(err.message));
660
690
  process.exit(1);
661
691
  }
662
692
  });
@@ -676,7 +706,7 @@ export function registerMonitorsCommands(program) {
676
706
  signalDaemonReload();
677
707
  }
678
708
  catch (err) {
679
- console.log(chalk.red(err.message));
709
+ stderrLine(chalk.red(err.message));
680
710
  process.exit(1);
681
711
  }
682
712
  });
@@ -688,7 +718,7 @@ export function registerMonitorsCommands(program) {
688
718
  .option('--clear', 'Remove the owner pin so the monitor runs on every device')
689
719
  .action(async (name, options) => {
690
720
  if (options.set !== undefined && options.clear) {
691
- console.log(chalk.red('--set and --clear are mutually exclusive'));
721
+ stderrLine(chalk.red('--set and --clear are mutually exclusive'));
692
722
  process.exit(1);
693
723
  }
694
724
  if (!name) {
@@ -698,7 +728,7 @@ export function registerMonitorsCommands(program) {
698
728
  }
699
729
  const monitor = readMonitor(name);
700
730
  if (!monitor) {
701
- console.log(chalk.red(`Monitor '${name}' not found`));
731
+ stderrLine(chalk.red(`Monitor '${name}' not found`));
702
732
  process.exit(1);
703
733
  }
704
734
  if (options.clear) {
@@ -737,11 +767,11 @@ export function registerMonitorsCommands(program) {
737
767
  console.log(chalk.green(`Monitor '${name}' removed`));
738
768
  if (isDaemonRunning()) {
739
769
  signalDaemonReload();
740
- console.log(chalk.gray('Daemon reloaded'));
770
+ stderrLine(chalk.gray('Daemon reloaded'));
741
771
  }
742
772
  }
743
773
  else {
744
- console.log(chalk.red(`Monitor '${name}' not found`));
774
+ stderrLine(chalk.red(`Monitor '${name}' not found`));
745
775
  process.exit(1);
746
776
  }
747
777
  });
@@ -493,6 +493,7 @@ Examples:
493
493
  // agents plugins install <spec>
494
494
  pluginsCmd
495
495
  .command('install <spec>')
496
+ .alias('add')
496
497
  .description('Install a plugin from a git URL or local path (format: name@source or source)')
497
498
  .option('--allow-exec-surfaces', 'Allow installing plugins that ship executable surfaces')
498
499
  .addHelpText('after', `
@@ -14,6 +14,9 @@ import { getPreset, listPresets, expandPreset } from '../lib/profiles-presets.js
14
14
  import { hasKeychainToken, keychainItemName, setKeychainToken, deleteKeychainToken, } from '../lib/secrets/profiles.js';
15
15
  import { isInteractiveTerminal } from './utils.js';
16
16
  import { getAgentsInvocation } from '../lib/daemon.js';
17
+ import { getActiveResourceProfileName, getResourceProfilePreset, listResourceProfileNames, setActiveResourceProfile, upsertResourceProfilePreset, validateResourceProfileName, } from '../lib/resource-profiles.js';
18
+ import { AGENTS } from '../lib/agents.js';
19
+ import { listInstalledVersions, syncResourcesToVersion } from '../lib/versions.js';
17
20
  /**
18
21
  * Pure helper: builds a Profile from collected wizard inputs. Extracted so the
19
22
  * shape of preset->profile mapping for the `create` wizard is unit-testable
@@ -69,8 +72,141 @@ function renderProfileRow(p) {
69
72
  const provider = p.provider || (p.auth?.keychainItem?.split('.')[1]) || '-';
70
73
  return `${chalk.cyan(p.name.padEnd(16))} ${host.padEnd(14)} ${provider.padEnd(12)} ${chalk.gray(model)}`;
71
74
  }
75
+ function splitList(value) {
76
+ if (value === undefined)
77
+ return undefined;
78
+ return value.split(',').map((part) => part.trim()).filter(Boolean);
79
+ }
80
+ function buildResourceProfileFromOptions(existing, opts) {
81
+ const next = { ...(existing ?? {}) };
82
+ if (opts.description !== undefined)
83
+ next.description = opts.description || undefined;
84
+ const keys = ['commands', 'skills', 'hooks', 'subagents', 'plugins', 'workflows', 'permissions', 'mcp'];
85
+ for (const key of keys) {
86
+ const parsed = splitList(opts[key]);
87
+ if (parsed !== undefined)
88
+ next[key] = parsed;
89
+ }
90
+ const secrets = splitList(opts.secrets);
91
+ if (secrets !== undefined)
92
+ next.secrets = secrets;
93
+ if (opts.rules !== undefined)
94
+ next.rules = opts.rules || undefined;
95
+ return next;
96
+ }
97
+ function printResourceProfile(name, preset, activeName) {
98
+ const activeMark = activeName === name ? '*' : ' ';
99
+ const description = preset.description ? ` ${chalk.gray(preset.description)}` : '';
100
+ console.log(`${activeMark} ${chalk.cyan(name)}${description}`);
101
+ }
102
+ function syncInstalledVersionsForActiveProfile(cwd) {
103
+ let synced = 0;
104
+ for (const agent of Object.keys(AGENTS)) {
105
+ for (const version of listInstalledVersions(agent)) {
106
+ syncResourcesToVersion(agent, version, undefined, { cwd, force: true });
107
+ synced++;
108
+ }
109
+ }
110
+ return synced;
111
+ }
112
+ function registerResourceProfileCommands(program) {
113
+ const cmd = program
114
+ .command('profile')
115
+ .description('Activate top-level resource profiles across commands, skills, hooks, rules, MCP, permissions, and secrets.');
116
+ cmd
117
+ .command('list')
118
+ .alias('ls')
119
+ .description('List top-level resource profiles')
120
+ .action(() => {
121
+ const names = listResourceProfileNames();
122
+ if (names.length === 0) {
123
+ console.log(chalk.gray('No resource profiles configured.'));
124
+ console.log(chalk.gray('Try: agents profile set work --skills user:code-review --secrets work'));
125
+ return;
126
+ }
127
+ const active = getActiveResourceProfileName();
128
+ for (const name of names) {
129
+ printResourceProfile(name, getResourceProfilePreset(name), active);
130
+ }
131
+ });
132
+ cmd
133
+ .command('status')
134
+ .description('Show the active top-level resource profile')
135
+ .action(() => {
136
+ const active = getActiveResourceProfileName();
137
+ if (!active) {
138
+ console.log(chalk.gray('No resource profile active.'));
139
+ return;
140
+ }
141
+ const preset = getResourceProfilePreset(active);
142
+ console.log(chalk.bold(active));
143
+ if (!preset) {
144
+ console.log(chalk.red('Configured active profile is missing from profiles.presets.'));
145
+ return;
146
+ }
147
+ if (preset.description)
148
+ console.log(chalk.gray(preset.description));
149
+ for (const [key, value] of Object.entries(preset)) {
150
+ if (key === 'description')
151
+ continue;
152
+ console.log(`${key}: ${Array.isArray(value) ? value.join(', ') : value}`);
153
+ }
154
+ });
155
+ cmd
156
+ .command('set <name>')
157
+ .alias('define')
158
+ .description('Create or update a top-level resource profile')
159
+ .option('--description <text>', 'Free-form description')
160
+ .option('--commands <patterns>', 'Comma-separated command selectors, e.g. system:commit,user:*')
161
+ .option('--skills <patterns>', 'Comma-separated skill selectors')
162
+ .option('--hooks <patterns>', 'Comma-separated hook selectors')
163
+ .option('--subagents <patterns>', 'Comma-separated subagent selectors')
164
+ .option('--plugins <patterns>', 'Comma-separated plugin selectors')
165
+ .option('--workflows <patterns>', 'Comma-separated workflow selectors')
166
+ .option('--permissions <patterns>', 'Comma-separated permission group selectors')
167
+ .option('--mcp <patterns>', 'Comma-separated MCP server selectors')
168
+ .option('--rules <preset>', 'Rules preset to compose while active')
169
+ .option('--secrets <names>', 'Comma-separated secrets bundles, or "*"')
170
+ .action((name, opts) => {
171
+ try {
172
+ validateResourceProfileName(name);
173
+ const preset = buildResourceProfileFromOptions(getResourceProfilePreset(name), opts);
174
+ upsertResourceProfilePreset(name, preset);
175
+ console.log(chalk.green(`Profile '${name}' saved.`));
176
+ }
177
+ catch (err) {
178
+ console.error(chalk.red(err.message));
179
+ process.exit(1);
180
+ }
181
+ });
182
+ cmd
183
+ .command('use <name>')
184
+ .description('Activate a top-level resource profile and reconcile installed versions')
185
+ .action((name) => {
186
+ try {
187
+ setActiveResourceProfile(name);
188
+ const synced = syncInstalledVersionsForActiveProfile(process.cwd());
189
+ console.log(chalk.green(`Profile '${name}' active.`));
190
+ console.log(chalk.gray(`Reconciled ${synced} installed version${synced === 1 ? '' : 's'}.`));
191
+ }
192
+ catch (err) {
193
+ console.error(chalk.red(err.message));
194
+ process.exit(1);
195
+ }
196
+ });
197
+ cmd
198
+ .command('clear')
199
+ .description('Clear the active top-level resource profile and reconcile installed versions')
200
+ .action(() => {
201
+ setActiveResourceProfile(null);
202
+ const synced = syncInstalledVersionsForActiveProfile(process.cwd());
203
+ console.log(chalk.green('Resource profile cleared.'));
204
+ console.log(chalk.gray(`Reconciled ${synced} installed version${synced === 1 ? '' : 's'}.`));
205
+ });
206
+ }
72
207
  /** Register the `agents profiles` command tree. */
73
208
  export function registerProfilesCommands(program) {
209
+ registerResourceProfileCommands(program);
74
210
  const cmd = program
75
211
  .command('profiles')
76
212
  .description('Named bundles of (host CLI, endpoint, model, auth) — run Kimi/DeepSeek/Qwen/etc through Claude Code without a proxy.')
@@ -142,6 +278,28 @@ Examples:
142
278
  # Fully remove the OpenRouter key from Keychain
143
279
  agents profiles logout openrouter
144
280
  `);
281
+ cmd
282
+ .command('use <name>')
283
+ .description('Alias for `agents profile use <name>`')
284
+ .action((name) => {
285
+ try {
286
+ setActiveResourceProfile(name);
287
+ const synced = syncInstalledVersionsForActiveProfile(process.cwd());
288
+ console.log(chalk.green(`Profile '${name}' active.`));
289
+ console.log(chalk.gray(`Reconciled ${synced} installed version${synced === 1 ? '' : 's'}.`));
290
+ }
291
+ catch (err) {
292
+ console.error(chalk.red(err.message));
293
+ process.exit(1);
294
+ }
295
+ });
296
+ cmd
297
+ .command('status')
298
+ .description('Alias for `agents profile status`')
299
+ .action(() => {
300
+ const active = getActiveResourceProfileName();
301
+ console.log(active ? active : chalk.gray('No resource profile active.'));
302
+ });
145
303
  cmd
146
304
  .command('list')
147
305
  .alias('ls')
@@ -6,7 +6,7 @@ import * as fs from 'fs';
6
6
  import * as path from 'path';
7
7
  import * as os from 'os';
8
8
  import simpleGit from 'simple-git';
9
- import { confirm, input } from '@inquirer/prompts';
9
+ import { input } from '@inquirer/prompts';
10
10
  import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
11
11
  import { setHelpSections } from '../lib/help.js';
12
12
  import { itemPicker } from '../lib/picker.js';
@@ -843,66 +843,8 @@ export function registerRepoCommands(program) {
843
843
  continue;
844
844
  }
845
845
  }
846
- if (t.alias === 'system') {
846
+ if (t.alias === 'system' && alias !== 'system') {
847
847
  // Skip system repo unless explicitly requested
848
- if (alias !== 'system')
849
- continue;
850
- // User explicitly asked for system repo — show status and offer to pull
851
- try {
852
- const git = simpleGit(t.dir);
853
- await git.fetch();
854
- const status = await git.status();
855
- const behind = status.behind ?? 0;
856
- if (behind === 0) {
857
- console.log(chalk.green('Up to date'));
858
- }
859
- else {
860
- // Count changed resources by type
861
- const diff = await git.diff(['--name-only', 'HEAD..@{upstream}']);
862
- const files = diff.split('\n').filter(Boolean);
863
- const counts = {};
864
- for (const f of files) {
865
- if (f.startsWith('skills/'))
866
- counts['skills'] = (counts['skills'] || 0) + 1;
867
- else if (f.startsWith('commands/'))
868
- counts['commands'] = (counts['commands'] || 0) + 1;
869
- else if (f.startsWith('hooks/'))
870
- counts['hooks'] = (counts['hooks'] || 0) + 1;
871
- else if (f.startsWith('rules/'))
872
- counts['rules'] = (counts['rules'] || 0) + 1;
873
- else
874
- counts['other'] = (counts['other'] || 0) + 1;
875
- }
876
- const parts = [];
877
- if (counts['skills'])
878
- parts.push(`${counts['skills']} skill${counts['skills'] > 1 ? 's' : ''}`);
879
- if (counts['commands'])
880
- parts.push(`${counts['commands']} command${counts['commands'] > 1 ? 's' : ''}`);
881
- if (counts['hooks'])
882
- parts.push(`${counts['hooks']} hook${counts['hooks'] > 1 ? 's' : ''}`);
883
- if (counts['rules'])
884
- parts.push(`${counts['rules']} rule${counts['rules'] > 1 ? 's' : ''}`);
885
- if (counts['other'])
886
- parts.push(`${counts['other']} other`);
887
- const summary = parts.length > 0 ? parts.join(', ') : `${behind} update${behind > 1 ? 's' : ''}`;
888
- console.log(chalk.yellow(`${summary} available`));
889
- if (isInteractiveTerminal()) {
890
- const doPull = await confirm({ message: 'Pull now?', default: true });
891
- if (doPull) {
892
- const result = await pullRepo(t.dir);
893
- if (result.success) {
894
- console.log(chalk.green('Updated'));
895
- }
896
- else {
897
- console.log(chalk.red(result.error || 'Pull failed'));
898
- }
899
- }
900
- }
901
- }
902
- }
903
- catch (err) {
904
- console.log(chalk.red(err.message));
905
- }
906
848
  continue;
907
849
  }
908
850
  const spinner = ora(`Pulling ${formatRepoTarget(t.alias, t.dir)}...`).start();
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `agents resources` — show the merged DotAgents resource surface.
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerResourcesCommand(program: Command): void;