@phnx-labs/agents-cli 1.14.2 → 1.14.4

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 (121) hide show
  1. package/README.md +17 -7
  2. package/dist/browser.d.ts +2 -0
  3. package/dist/browser.js +7 -0
  4. package/dist/commands/browser.d.ts +3 -0
  5. package/dist/commands/browser.js +392 -0
  6. package/dist/commands/daemon.js +1 -1
  7. package/dist/commands/doctor.d.ts +16 -9
  8. package/dist/commands/doctor.js +248 -12
  9. package/dist/commands/prune.js +9 -3
  10. package/dist/commands/refresh-rules.d.ts +15 -0
  11. package/dist/commands/{refresh-memory.js → refresh-rules.js} +14 -14
  12. package/dist/commands/routines.js +1 -1
  13. package/dist/commands/rules.js +100 -4
  14. package/dist/commands/secrets.js +198 -11
  15. package/dist/commands/sync.js +19 -0
  16. package/dist/commands/teams.js +184 -22
  17. package/dist/commands/trash.d.ts +10 -0
  18. package/dist/commands/trash.js +187 -0
  19. package/dist/commands/view.js +47 -14
  20. package/dist/index.js +62 -4
  21. package/dist/lib/agents.js +2 -2
  22. package/dist/lib/browser/cdp.d.ts +24 -0
  23. package/dist/lib/browser/cdp.js +94 -0
  24. package/dist/lib/browser/chrome.d.ts +16 -0
  25. package/dist/lib/browser/chrome.js +157 -0
  26. package/dist/lib/browser/drivers/local.d.ts +8 -0
  27. package/dist/lib/browser/drivers/local.js +22 -0
  28. package/dist/lib/browser/drivers/ssh.d.ts +9 -0
  29. package/dist/lib/browser/drivers/ssh.js +129 -0
  30. package/dist/lib/browser/index.d.ts +5 -0
  31. package/dist/lib/browser/index.js +5 -0
  32. package/dist/lib/browser/input.d.ts +6 -0
  33. package/dist/lib/browser/input.js +52 -0
  34. package/dist/lib/browser/ipc.d.ts +12 -0
  35. package/dist/lib/browser/ipc.js +223 -0
  36. package/dist/lib/browser/profiles.d.ts +11 -0
  37. package/dist/lib/browser/profiles.js +61 -0
  38. package/dist/lib/browser/refs.d.ts +21 -0
  39. package/dist/lib/browser/refs.js +88 -0
  40. package/dist/lib/browser/service.d.ts +45 -0
  41. package/dist/lib/browser/service.js +404 -0
  42. package/dist/lib/browser/types.d.ts +73 -0
  43. package/dist/lib/browser/types.js +7 -0
  44. package/dist/lib/cloud/codex.js +1 -1
  45. package/dist/lib/cloud/registry.js +2 -2
  46. package/dist/lib/cloud/rush.js +2 -2
  47. package/dist/lib/cloud/store.js +2 -2
  48. package/dist/lib/daemon.d.ts +1 -1
  49. package/dist/lib/daemon.js +47 -11
  50. package/dist/lib/diff-text.d.ts +25 -0
  51. package/dist/lib/diff-text.js +47 -0
  52. package/dist/lib/doctor-diff.d.ts +64 -0
  53. package/dist/lib/doctor-diff.js +497 -0
  54. package/dist/lib/git.js +3 -3
  55. package/dist/lib/hooks.d.ts +6 -0
  56. package/dist/lib/hooks.js +6 -1
  57. package/dist/lib/migrate.js +123 -0
  58. package/dist/lib/pty-client.js +3 -3
  59. package/dist/lib/pty-server.js +36 -7
  60. package/dist/lib/resources/commands.d.ts +46 -0
  61. package/dist/lib/resources/commands.js +208 -0
  62. package/dist/lib/resources/hooks.d.ts +12 -0
  63. package/dist/lib/resources/hooks.js +136 -0
  64. package/dist/lib/resources/index.d.ts +36 -0
  65. package/dist/lib/resources/index.js +69 -0
  66. package/dist/lib/resources/mcp.d.ts +34 -0
  67. package/dist/lib/resources/mcp.js +483 -0
  68. package/dist/lib/resources/permissions.d.ts +13 -0
  69. package/dist/lib/resources/permissions.js +184 -0
  70. package/dist/lib/resources/rules.d.ts +43 -0
  71. package/dist/lib/resources/rules.js +146 -0
  72. package/dist/lib/resources/skills.d.ts +37 -0
  73. package/dist/lib/resources/skills.js +238 -0
  74. package/dist/lib/resources/subagents.d.ts +46 -0
  75. package/dist/lib/resources/subagents.js +198 -0
  76. package/dist/lib/resources/types.d.ts +82 -0
  77. package/dist/lib/resources/types.js +8 -0
  78. package/dist/lib/resources.js +1 -1
  79. package/dist/lib/rotate.d.ts +8 -1
  80. package/dist/lib/rotate.js +17 -4
  81. package/dist/lib/rules/compile.d.ts +104 -0
  82. package/dist/lib/{memory-compile.js → rules/compile.js} +160 -21
  83. package/dist/lib/rules/compose.d.ts +78 -0
  84. package/dist/lib/rules/compose.js +170 -0
  85. package/dist/lib/{memory.d.ts → rules/rules.d.ts} +5 -5
  86. package/dist/lib/{memory.js → rules/rules.js} +10 -10
  87. package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
  88. package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
  89. package/dist/lib/secrets/bundles.d.ts +61 -4
  90. package/dist/lib/secrets/bundles.js +222 -54
  91. package/dist/lib/secrets/index.d.ts +24 -5
  92. package/dist/lib/secrets/index.js +70 -41
  93. package/dist/lib/session/active.js +5 -5
  94. package/dist/lib/session/db.js +4 -4
  95. package/dist/lib/session/discover.js +2 -2
  96. package/dist/lib/session/render.js +21 -7
  97. package/dist/lib/shims.d.ts +28 -4
  98. package/dist/lib/shims.js +72 -14
  99. package/dist/lib/state.d.ts +22 -28
  100. package/dist/lib/state.js +83 -78
  101. package/dist/lib/sync-manifest.d.ts +2 -2
  102. package/dist/lib/sync-manifest.js +5 -5
  103. package/dist/lib/teams/agents.d.ts +4 -2
  104. package/dist/lib/teams/agents.js +11 -4
  105. package/dist/lib/teams/api.d.ts +1 -1
  106. package/dist/lib/teams/api.js +2 -2
  107. package/dist/lib/teams/index.d.ts +1 -0
  108. package/dist/lib/teams/index.js +1 -0
  109. package/dist/lib/teams/persistence.js +3 -3
  110. package/dist/lib/teams/registry.d.ts +12 -1
  111. package/dist/lib/teams/registry.js +12 -2
  112. package/dist/lib/teams/worktree.d.ts +30 -0
  113. package/dist/lib/teams/worktree.js +96 -0
  114. package/dist/lib/types.d.ts +12 -6
  115. package/dist/lib/types.js +3 -3
  116. package/dist/lib/versions.d.ts +32 -3
  117. package/dist/lib/versions.js +147 -119
  118. package/package.json +3 -2
  119. package/scripts/postinstall.js +29 -0
  120. package/dist/commands/refresh-memory.d.ts +0 -15
  121. package/dist/lib/memory-compile.d.ts +0 -66
package/README.md CHANGED
@@ -250,19 +250,19 @@ agents secrets create prod-stripe
250
250
  agents secrets add prod-stripe STRIPE_SECRET_KEY # Prompts, stores in Keychain
251
251
  agents secrets add prod-stripe TEST_CARD --value "4242..."
252
252
 
253
- # Injected at run time. The YAML on disk has only refs.
253
+ # Injected at run time. Bundle definitions live in the Keychain, not on disk.
254
254
  agents run claude "charge a test card" --secrets prod-stripe
255
255
  ```
256
256
 
257
257
  <p align="center">
258
- <img src="assets/secrets.svg" alt="How agents-cli secrets work: stripe.yml holds a pointer, the macOS Keychain holds the value, agents-cli resolves at runtime and injects the env into the child process" width="100%" />
258
+ <img src="assets/secrets.svg" alt="How agents-cli secrets work: bundle definitions live in the macOS Keychain alongside their values, agents-cli resolves at runtime and injects the env into the child process" width="100%" />
259
259
  </p>
260
260
 
261
261
  Merge order: profile env < `--secrets` < `--env K=V`. A missing keychain item aborts before the child starts.
262
262
 
263
263
  ### Cross-machine sync via iCloud Keychain
264
264
 
265
- Pass `--icloud-sync` when creating a bundle and the values are written to the iCloud-synced keychain. Sign into the same iCloud account on another Mac (with iCloud Keychain enabled) and the values appear there within seconds — no copy-paste, no `.env` files emailed to yourself, no shared secret stores.
265
+ Pass `--icloud-sync` when creating a bundle and both the bundle definition and its values are written to the iCloud-synced keychain. Sign into the same iCloud account on another Mac (with iCloud Keychain enabled) and the bundle appears there within seconds — no copy-paste, no `.env` files emailed to yourself, no shared secret stores.
266
266
 
267
267
  ```bash
268
268
  # On laptop:
@@ -270,13 +270,23 @@ agents secrets create npm-tokens --icloud-sync
270
270
  agents secrets add npm-tokens NPM_TOKEN # value lives in iCloud Keychain
271
271
 
272
272
  # On another Mac (same iCloud account):
273
- agents secrets add npm-tokens NPM_TOKEN # the value is already there;
274
- # you only need the bundle YAML locally
273
+ agents secrets list # npm-tokens is already there;
274
+ agents run claude "..." --secrets npm-tokens # injects NPM_TOKEN automatically
275
275
  ```
276
276
 
277
- Under the hood, `--icloud-sync` routes writes through a notarized helper app (`AgentsKeychain.app`) that holds the entitlement macOS requires for `kSecAttrSynchronizable`. Bundles without `--icloud-sync` use `/usr/bin/security` and stay device-local.
277
+ Under the hood, `--icloud-sync` routes writes through a notarized helper app (`AgentsKeychain.app`) that holds the entitlement macOS requires for `kSecAttrSynchronizable`. Bundles without `--icloud-sync` stay device-local.
278
278
 
279
- Bundle YAML files (`~/.agents/secrets/*.yml`) are not syncedonly the secret values. Push the YAMLs across machines via `agents repo push` if you want full bundle definitions to follow.
279
+ Bundle definitions sync via iCloud Keychain toono `agents repo push` needed for secrets, no recreate step on each Mac. Nothing about secrets ever lives in plaintext on disk.
280
+
281
+ ### Per-secret metadata and rotation
282
+
283
+ Tag each secret with `--type`, `--expires`, and `--note` so the bundle is self-documenting. `--expires` is always future-dated (`YYYY-MM-DD`); past or same-day values are rejected. Use `agents secrets rotate <bundle> <key>` to refresh a credential — `add` only creates new keys, `rotate` replaces the value and preserves metadata unless overridden.
284
+
285
+ ```bash
286
+ agents secrets add prod STRIPE_API_KEY --type api-key --expires 2027-01-15 --note "Live key, owner: payments-team"
287
+ agents secrets rotate prod STRIPE_API_KEY --note "rotated after suspected leak"
288
+ agents secrets list # EXPIRING column flags secrets due in the next 30 days
289
+ ```
280
290
 
281
291
  ---
282
292
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { registerBrowserSubcommands } from './commands/browser.js';
4
+ const program = new Command();
5
+ program.name('browser').description('Browser automation via CDP');
6
+ registerBrowserSubcommands(program);
7
+ program.parse();
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerBrowserCommand(program: Command): void;
3
+ export declare function registerBrowserSubcommands(program: Command): void;
@@ -0,0 +1,392 @@
1
+ import { listProfiles, getProfile, createProfile, deleteProfile, } from '../lib/browser/profiles.js';
2
+ import { sendIPCRequest } from '../lib/browser/ipc.js';
3
+ import { isValidTaskId } from '../lib/browser/types.js';
4
+ export function registerBrowserCommand(program) {
5
+ const browser = program
6
+ .command('browser')
7
+ .description('Browser automation via CDP');
8
+ registerProfilesCommands(browser);
9
+ registerTaskCommands(browser);
10
+ }
11
+ export function registerBrowserSubcommands(program) {
12
+ registerProfilesCommands(program);
13
+ registerTaskCommands(program);
14
+ }
15
+ function registerProfilesCommands(browser) {
16
+ const profiles = browser
17
+ .command('profiles')
18
+ .description('Manage browser profiles');
19
+ profiles
20
+ .command('list')
21
+ .alias('ls')
22
+ .description('List all browser profiles')
23
+ .action(async () => {
24
+ const allProfiles = await listProfiles();
25
+ if (allProfiles.length === 0) {
26
+ console.log('No browser profiles configured.');
27
+ console.log('Create one with: agents browser profiles create <name> --endpoint <url>');
28
+ return;
29
+ }
30
+ console.log('NAME'.padEnd(20) + 'BROWSER'.padEnd(12) + 'ENDPOINTS');
31
+ console.log('-'.repeat(72));
32
+ for (const p of allProfiles) {
33
+ const endpoints = p.endpoints.join(', ');
34
+ console.log(p.name.padEnd(20) + (p.browser || '-').padEnd(12) + endpoints);
35
+ }
36
+ });
37
+ const VALID_BROWSERS = ['chrome', 'comet', 'chromium', 'brave', 'edge'];
38
+ profiles
39
+ .command('create <name>')
40
+ .description('Create a new browser profile')
41
+ .requiredOption('-b, --browser <type>', `Browser type: ${VALID_BROWSERS.join(', ')}`)
42
+ .requiredOption('-e, --endpoint <url>', 'CDP endpoint URL (repeatable)', collect, [])
43
+ .option('-s, --secrets <bundle>', 'Secrets bundle to inject')
44
+ .option('-d, --description <text>', 'Profile description')
45
+ .option('--headless', 'Run in headless mode')
46
+ .action(async (name, opts) => {
47
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
48
+ console.error('Profile name must be lowercase alphanumeric with hyphens');
49
+ process.exit(1);
50
+ }
51
+ if (!VALID_BROWSERS.includes(opts.browser)) {
52
+ console.error(`Invalid browser type. Must be one of: ${VALID_BROWSERS.join(', ')}`);
53
+ process.exit(1);
54
+ }
55
+ const profile = {
56
+ name,
57
+ description: opts.description,
58
+ browser: opts.browser,
59
+ endpoints: opts.endpoint,
60
+ secrets: opts.secrets,
61
+ chrome: opts.headless ? { headless: true } : undefined,
62
+ };
63
+ await createProfile(profile);
64
+ console.log(`Created profile: ${name}`);
65
+ });
66
+ profiles
67
+ .command('show <name>')
68
+ .description('Show profile details')
69
+ .action(async (name) => {
70
+ const profile = await getProfile(name);
71
+ if (!profile) {
72
+ console.error(`Profile "${name}" not found`);
73
+ process.exit(1);
74
+ }
75
+ console.log(`Name: ${profile.name}`);
76
+ console.log(`Browser: ${profile.browser}`);
77
+ if (profile.description)
78
+ console.log(`Description: ${profile.description}`);
79
+ console.log(`Endpoints:`);
80
+ for (const e of profile.endpoints) {
81
+ console.log(` - ${e}`);
82
+ }
83
+ if (profile.secrets)
84
+ console.log(`Secrets: ${profile.secrets}`);
85
+ if (profile.chrome?.headless)
86
+ console.log(`Headless: true`);
87
+ });
88
+ profiles
89
+ .command('delete <name>')
90
+ .description('Delete a browser profile')
91
+ .action(async (name) => {
92
+ await deleteProfile(name);
93
+ console.log(`Deleted profile: ${name}`);
94
+ });
95
+ }
96
+ function registerTaskCommands(browser) {
97
+ browser
98
+ .command('start [task]')
99
+ .description('Start a browser task')
100
+ .requiredOption('-p, --profile <name>', 'Browser profile to use')
101
+ .action(async (task, opts) => {
102
+ if (task && !isValidTaskId(task)) {
103
+ console.error('Task ID must be lowercase alphanumeric with hyphens');
104
+ process.exit(1);
105
+ }
106
+ const response = await sendIPCRequest({
107
+ action: 'start',
108
+ profile: opts.profile,
109
+ task,
110
+ });
111
+ if (!response.ok) {
112
+ console.error(response.error);
113
+ process.exit(1);
114
+ }
115
+ console.log(response.task);
116
+ });
117
+ browser
118
+ .command('stop <task>')
119
+ .description('Stop a browser task and close its tabs')
120
+ .action(async (task) => {
121
+ const response = await sendIPCRequest({
122
+ action: 'stop',
123
+ task,
124
+ });
125
+ if (!response.ok) {
126
+ console.error(response.error);
127
+ process.exit(1);
128
+ }
129
+ console.log(`Stopped task: ${task}`);
130
+ });
131
+ browser
132
+ .command('navigate <task> <url>')
133
+ .description('Open a URL in the task window')
134
+ .option('-p, --profile <name>', 'Browser profile (optional if task is unique)')
135
+ .action(async (task, url, opts) => {
136
+ const response = await sendIPCRequest({
137
+ action: 'navigate',
138
+ task,
139
+ url,
140
+ profile: opts.profile,
141
+ });
142
+ if (!response.ok) {
143
+ console.error(response.error);
144
+ process.exit(1);
145
+ }
146
+ console.log(`Opened tab ${response.tabId}: ${url}`);
147
+ });
148
+ browser
149
+ .command('tabs [task]')
150
+ .description('List open tabs')
151
+ .option('-p, --profile <name>', 'Filter by profile')
152
+ .action(async (task, opts) => {
153
+ const response = await sendIPCRequest({
154
+ action: 'tabs',
155
+ task,
156
+ profile: opts.profile,
157
+ });
158
+ if (!response.ok) {
159
+ console.error(response.error);
160
+ process.exit(1);
161
+ }
162
+ if (!response.tabs || response.tabs.length === 0) {
163
+ console.log('No tabs open');
164
+ return;
165
+ }
166
+ console.log('TASK'.padEnd(15) + 'TAB'.padEnd(12) + 'URL');
167
+ console.log('-'.repeat(80));
168
+ for (const tab of response.tabs) {
169
+ const shortId = tab.id.slice(0, 8);
170
+ console.log(tab.task.padEnd(15) +
171
+ shortId.padEnd(12) +
172
+ tab.url.slice(0, 55));
173
+ }
174
+ });
175
+ browser
176
+ .command('close <task> [tabId]')
177
+ .description('Close tabs for a task')
178
+ .action(async (task, tabId) => {
179
+ const response = await sendIPCRequest({
180
+ action: 'close',
181
+ task,
182
+ tabId,
183
+ });
184
+ if (!response.ok) {
185
+ console.error(response.error);
186
+ process.exit(1);
187
+ }
188
+ console.log(tabId ? `Closed tab ${tabId}` : `Closed all tabs for task ${task}`);
189
+ });
190
+ browser
191
+ .command('screenshot <task> [tabId]')
192
+ .description('Take a screenshot')
193
+ .option('-o, --output <path>', 'Output path')
194
+ .action(async (task, tabId, opts) => {
195
+ const response = await sendIPCRequest({
196
+ action: 'screenshot',
197
+ task,
198
+ tabId,
199
+ path: opts.output,
200
+ });
201
+ if (!response.ok) {
202
+ console.error(response.error);
203
+ process.exit(1);
204
+ }
205
+ console.log(response.path);
206
+ });
207
+ browser
208
+ .command('evaluate <task> <tabId> <expression>')
209
+ .description('Evaluate JavaScript in a tab')
210
+ .action(async (task, tabId, expression) => {
211
+ const response = await sendIPCRequest({
212
+ action: 'evaluate',
213
+ task,
214
+ tabId,
215
+ expr: expression,
216
+ });
217
+ if (!response.ok) {
218
+ console.error(response.error);
219
+ process.exit(1);
220
+ }
221
+ console.log(JSON.stringify(response.result, null, 2));
222
+ });
223
+ browser
224
+ .command('status')
225
+ .description('Show running browser tasks')
226
+ .option('-p, --profile <name>', 'Filter by profile')
227
+ .action(async (opts) => {
228
+ const response = await sendIPCRequest({
229
+ action: 'status',
230
+ profile: opts.profile,
231
+ });
232
+ if (!response.ok) {
233
+ console.error(response.error);
234
+ process.exit(1);
235
+ }
236
+ if (!response.profiles || response.profiles.length === 0) {
237
+ console.log('No browser profiles running');
238
+ return;
239
+ }
240
+ for (const profile of response.profiles) {
241
+ console.log(`\n${profile.name} (port ${profile.port}, pid ${profile.pid})`);
242
+ if (profile.tasks.length === 0) {
243
+ console.log(' No active tasks');
244
+ }
245
+ else {
246
+ console.log(' TASK'.padEnd(17) + 'TABS'.padEnd(8) + 'CREATED');
247
+ for (const task of profile.tasks) {
248
+ const age = formatAge(task.createdAt);
249
+ console.log(' ' +
250
+ task.id.padEnd(15) +
251
+ String(task.tabCount).padEnd(8) +
252
+ age);
253
+ }
254
+ }
255
+ }
256
+ });
257
+ browser
258
+ .command('tasks')
259
+ .description('List all browser tasks')
260
+ .option('-p, --profile <name>', 'Filter by profile')
261
+ .action(async (opts) => {
262
+ const response = await sendIPCRequest({
263
+ action: 'status',
264
+ profile: opts.profile,
265
+ });
266
+ if (!response.ok) {
267
+ console.error(response.error);
268
+ process.exit(1);
269
+ }
270
+ const allTasks = [];
271
+ for (const profile of response.profiles || []) {
272
+ for (const task of profile.tasks) {
273
+ allTasks.push({
274
+ profile: profile.name,
275
+ id: task.id,
276
+ tabs: task.tabCount,
277
+ created: task.createdAt,
278
+ });
279
+ }
280
+ }
281
+ if (allTasks.length === 0) {
282
+ console.log('No active tasks');
283
+ return;
284
+ }
285
+ console.log('PROFILE'.padEnd(18) + 'TASK'.padEnd(15) + 'TABS'.padEnd(8) + 'CREATED');
286
+ console.log('-'.repeat(55));
287
+ for (const t of allTasks) {
288
+ console.log(t.profile.padEnd(18) +
289
+ t.id.padEnd(15) +
290
+ String(t.tabs).padEnd(8) +
291
+ formatAge(t.created));
292
+ }
293
+ });
294
+ browser
295
+ .command('refs <task> [tabId]')
296
+ .description('Get DOM refs for interactive elements')
297
+ .option('--all', 'Include non-interactive elements')
298
+ .option('-l, --limit <n>', 'Max elements (default 500)', '500')
299
+ .action(async (task, tabId, opts) => {
300
+ const response = await sendIPCRequest({
301
+ action: 'refs',
302
+ task,
303
+ tabId,
304
+ interactive: !opts.all,
305
+ limit: parseInt(opts.limit, 10),
306
+ });
307
+ if (!response.ok) {
308
+ console.error(response.error);
309
+ process.exit(1);
310
+ }
311
+ console.log(response.refs);
312
+ });
313
+ browser
314
+ .command('click <task> <tabId> <ref>')
315
+ .description('Click an element by ref')
316
+ .action(async (task, tabId, ref) => {
317
+ const response = await sendIPCRequest({
318
+ action: 'click',
319
+ task,
320
+ tabId,
321
+ ref: parseInt(ref, 10),
322
+ });
323
+ if (!response.ok) {
324
+ console.error(response.error);
325
+ process.exit(1);
326
+ }
327
+ console.log('Clicked');
328
+ });
329
+ browser
330
+ .command('type <task> <tabId> <ref> <text>')
331
+ .description('Type text into an element by ref')
332
+ .action(async (task, tabId, ref, text) => {
333
+ const response = await sendIPCRequest({
334
+ action: 'type',
335
+ task,
336
+ tabId,
337
+ ref: parseInt(ref, 10),
338
+ text,
339
+ });
340
+ if (!response.ok) {
341
+ console.error(response.error);
342
+ process.exit(1);
343
+ }
344
+ console.log('Typed');
345
+ });
346
+ browser
347
+ .command('press <task> <tabId> <key>')
348
+ .description('Press a key (Enter, Tab, Escape, etc)')
349
+ .action(async (task, tabId, key) => {
350
+ const response = await sendIPCRequest({
351
+ action: 'press',
352
+ task,
353
+ tabId,
354
+ key,
355
+ });
356
+ if (!response.ok) {
357
+ console.error(response.error);
358
+ process.exit(1);
359
+ }
360
+ console.log('Pressed');
361
+ });
362
+ browser
363
+ .command('hover <task> <tabId> <ref>')
364
+ .description('Hover over an element by ref')
365
+ .action(async (task, tabId, ref) => {
366
+ const response = await sendIPCRequest({
367
+ action: 'hover',
368
+ task,
369
+ tabId,
370
+ ref: parseInt(ref, 10),
371
+ });
372
+ if (!response.ok) {
373
+ console.error(response.error);
374
+ process.exit(1);
375
+ }
376
+ console.log('Hovered');
377
+ });
378
+ }
379
+ function collect(val, memo) {
380
+ memo.push(val);
381
+ return memo;
382
+ }
383
+ function formatAge(timestamp) {
384
+ const seconds = Math.floor((Date.now() - timestamp) / 1000);
385
+ if (seconds < 60)
386
+ return `${seconds}s ago`;
387
+ const minutes = Math.floor(seconds / 60);
388
+ if (minutes < 60)
389
+ return `${minutes}m ago`;
390
+ const hours = Math.floor(minutes / 60);
391
+ return `${hours}h ago`;
392
+ }
@@ -92,7 +92,7 @@ you never need to start it manually.
92
92
  warnDeprecated('logs', 'agents routines scheduler-logs');
93
93
  if (options.follow) {
94
94
  const { getAgentsDir } = await import('../lib/state.js');
95
- const logPath = path.join(getAgentsDir(), 'daemon.log');
95
+ const logPath = path.join(getAgentsDir(), 'helpers/daemon/logs.jsonl');
96
96
  const child = spawn('tail', ['-f', logPath], { stdio: ['ignore', 'pipe', 'pipe'] });
97
97
  child.stdout.pipe(process.stdout);
98
98
  child.stderr.pipe(process.stderr);
@@ -1,15 +1,22 @@
1
1
  /**
2
2
  * `agents doctor` — diagnostic readout across the install.
3
3
  *
4
- * Three sections:
5
- * 1. CLI availability — which agent binaries can be invoked.
6
- * 2. Sync status — per (agent, default-version), is the version-home sync
7
- * manifest fresh? (sync runs at launch; this surfaces what would happen.)
8
- * 3. Orphans per resource type per default version, count of files that
9
- * would be removed by `agents prune`.
10
- *
11
- * Read-only: doctor never mutates state. Run `agents prune` to act on the
12
- * orphan readout, or just launch the agent to apply pending sync.
4
+ * Two modes:
5
+ *
6
+ * 1. Overview (no target): three sections
7
+ * - CLI availability (which agent binaries can be invoked).
8
+ * - Sync status per default version (fresh / stale / never-synced).
9
+ * - Orphans per default version per resource type.
10
+ *
11
+ * 2. Target mode: `agents doctor <agent>[@version]` full per-resource
12
+ * diff for a single (agent, version) against the current cwd's resolved
13
+ * sources. Reports ok / DIFF / MISS / EXTRA per resource with the source
14
+ * layer (project, user, system, extra repo). With `--diff`, renders a
15
+ * unified diff body for each divergent file. Mirrors the resolution that
16
+ * the shim drives at runtime: project > user > system > extras.
17
+ *
18
+ * Read-only: doctor never mutates state. Run `agents prune` to act on orphan
19
+ * readouts, or just launch the agent to apply pending sync.
13
20
  */
14
21
  import type { Command } from 'commander';
15
22
  export declare function registerDoctorCommand(program: Command): void;