@phnx-labs/agents-cli 1.20.92 → 1.21.0

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 (146) hide show
  1. package/CHANGELOG.md +385 -0
  2. package/README.md +3 -0
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/beta.js +3 -5
  5. package/dist/commands/doctor.js +23 -0
  6. package/dist/commands/events.js +91 -1
  7. package/dist/commands/exec.js +13 -0
  8. package/dist/commands/mailboxes.js +39 -1
  9. package/dist/commands/message.js +12 -1
  10. package/dist/commands/packages.js +14 -5
  11. package/dist/commands/projects.d.ts +38 -0
  12. package/dist/commands/projects.js +355 -48
  13. package/dist/commands/routines.js +27 -1
  14. package/dist/commands/rules.js +6 -0
  15. package/dist/commands/secrets.d.ts +17 -0
  16. package/dist/commands/secrets.js +198 -7
  17. package/dist/commands/send.d.ts +14 -12
  18. package/dist/commands/send.js +105 -35
  19. package/dist/commands/sessions.d.ts +17 -0
  20. package/dist/commands/sessions.js +16 -8
  21. package/dist/commands/setup-browser.js +5 -1
  22. package/dist/commands/setup-fleet.js +5 -0
  23. package/dist/commands/setup-preferences.d.ts +53 -0
  24. package/dist/commands/setup-preferences.js +142 -0
  25. package/dist/commands/setup.js +8 -9
  26. package/dist/commands/ssh.js +220 -10
  27. package/dist/commands/sync.d.ts +2 -2
  28. package/dist/commands/sync.js +13 -12
  29. package/dist/commands/view.js +4 -0
  30. package/dist/commands/watchdog.js +26 -3
  31. package/dist/index.js +38 -6
  32. package/dist/lib/activity.d.ts +15 -0
  33. package/dist/lib/activity.js +26 -2
  34. package/dist/lib/auto-dispatch.d.ts +6 -1
  35. package/dist/lib/auto-dispatch.js +7 -2
  36. package/dist/lib/beta.d.ts +1 -1
  37. package/dist/lib/beta.js +1 -1
  38. package/dist/lib/channels/send.d.ts +83 -0
  39. package/dist/lib/channels/send.js +112 -0
  40. package/dist/lib/daemon.d.ts +13 -0
  41. package/dist/lib/daemon.js +80 -60
  42. package/dist/lib/device-config.d.ts +82 -0
  43. package/dist/lib/device-config.js +296 -0
  44. package/dist/lib/event-provenance.d.ts +19 -0
  45. package/dist/lib/event-provenance.js +48 -0
  46. package/dist/lib/events-ingest.d.ts +46 -0
  47. package/dist/lib/events-ingest.js +182 -0
  48. package/dist/lib/events.d.ts +17 -5
  49. package/dist/lib/events.js +59 -56
  50. package/dist/lib/feed.d.ts +1 -1
  51. package/dist/lib/feed.js +2 -0
  52. package/dist/lib/git.d.ts +13 -2
  53. package/dist/lib/git.js +38 -6
  54. package/dist/lib/linear-project-counts.d.ts +129 -0
  55. package/dist/lib/linear-project-counts.js +206 -0
  56. package/dist/lib/linear-projects.d.ts +62 -0
  57. package/dist/lib/linear-projects.js +144 -0
  58. package/dist/lib/mailbox-gc.js +30 -7
  59. package/dist/lib/mailbox.d.ts +14 -1
  60. package/dist/lib/mailbox.js +35 -3
  61. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  62. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  63. package/dist/lib/menubar/install-menubar.d.ts +5 -21
  64. package/dist/lib/menubar/install-menubar.js +35 -44
  65. package/dist/lib/menubar/notify-desktop.d.ts +17 -2
  66. package/dist/lib/menubar/notify-desktop.js +8 -2
  67. package/dist/lib/migrate.js +1 -1
  68. package/dist/lib/models.js +21 -11
  69. package/dist/lib/overdue.d.ts +1 -1
  70. package/dist/lib/overdue.js +54 -19
  71. package/dist/lib/project-import.d.ts +96 -0
  72. package/dist/lib/project-import.js +171 -0
  73. package/dist/lib/project-probe.d.ts +75 -0
  74. package/dist/lib/project-probe.js +160 -0
  75. package/dist/lib/project-resources.d.ts +8 -0
  76. package/dist/lib/project-resources.js +31 -3
  77. package/dist/lib/project-status.d.ts +32 -1
  78. package/dist/lib/project-status.js +82 -1
  79. package/dist/lib/projects.d.ts +6 -0
  80. package/dist/lib/projects.js +12 -0
  81. package/dist/lib/remote-agents-json.d.ts +14 -1
  82. package/dist/lib/remote-agents-json.js +21 -3
  83. package/dist/lib/routine-notify.d.ts +11 -0
  84. package/dist/lib/routine-notify.js +22 -0
  85. package/dist/lib/routines.d.ts +53 -0
  86. package/dist/lib/routines.js +105 -4
  87. package/dist/lib/rules/run-sync.d.ts +18 -0
  88. package/dist/lib/rules/run-sync.js +92 -0
  89. package/dist/lib/run-notify.js +3 -0
  90. package/dist/lib/scheduler.js +12 -2
  91. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  92. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  93. package/dist/lib/secrets/audit.d.ts +1 -1
  94. package/dist/lib/secrets/audit.js +53 -10
  95. package/dist/lib/secrets/index.d.ts +4 -5
  96. package/dist/lib/secrets/index.js +4 -5
  97. package/dist/lib/secrets/list-filter.d.ts +20 -5
  98. package/dist/lib/secrets/list-filter.js +22 -6
  99. package/dist/lib/secrets/usage-db.d.ts +106 -0
  100. package/dist/lib/secrets/usage-db.js +236 -0
  101. package/dist/lib/session/active.d.ts +16 -2
  102. package/dist/lib/session/active.js +8 -4
  103. package/dist/lib/session/bundle.js +5 -1
  104. package/dist/lib/session/remote-active.d.ts +5 -1
  105. package/dist/lib/session/remote-active.js +4 -1
  106. package/dist/lib/session/remote-list.d.ts +1 -15
  107. package/dist/lib/session/remote-list.js +22 -111
  108. package/dist/lib/session/sync/config.d.ts +13 -5
  109. package/dist/lib/session/sync/config.js +21 -13
  110. package/dist/lib/sqlite.js +28 -1
  111. package/dist/lib/startup/command-registry.d.ts +0 -2
  112. package/dist/lib/startup/command-registry.js +1 -5
  113. package/dist/lib/state.d.ts +19 -2
  114. package/dist/lib/state.js +99 -10
  115. package/dist/lib/sync-umbrella.d.ts +11 -23
  116. package/dist/lib/sync-umbrella.js +14 -45
  117. package/dist/lib/teams/agents.d.ts +35 -3
  118. package/dist/lib/teams/agents.js +52 -8
  119. package/dist/lib/teams/scheduler.d.ts +29 -2
  120. package/dist/lib/teams/scheduler.js +60 -16
  121. package/dist/lib/types.d.ts +22 -5
  122. package/dist/lib/versions.d.ts +6 -0
  123. package/dist/lib/versions.js +6 -4
  124. package/package.json +1 -2
  125. package/dist/commands/drive.d.ts +0 -10
  126. package/dist/commands/drive.js +0 -183
  127. package/dist/commands/hq.d.ts +0 -2
  128. package/dist/commands/hq.js +0 -58
  129. package/dist/commands/sessions-sync.d.ts +0 -17
  130. package/dist/commands/sessions-sync.js +0 -135
  131. package/dist/commands/sync-provision.d.ts +0 -23
  132. package/dist/commands/sync-provision.js +0 -107
  133. package/dist/lib/drive-sync.d.ts +0 -45
  134. package/dist/lib/drive-sync.js +0 -238
  135. package/dist/lib/hq/floor.d.ts +0 -87
  136. package/dist/lib/hq/floor.js +0 -243
  137. package/dist/lib/session/sync/crdt.d.ts +0 -44
  138. package/dist/lib/session/sync/crdt.js +0 -119
  139. package/dist/lib/session/sync/manifest.d.ts +0 -62
  140. package/dist/lib/session/sync/manifest.js +0 -100
  141. package/dist/lib/session/sync/provision.d.ts +0 -49
  142. package/dist/lib/session/sync/provision.js +0 -91
  143. package/dist/lib/session/sync/r2.d.ts +0 -32
  144. package/dist/lib/session/sync/r2.js +0 -121
  145. package/dist/lib/session/sync/sync.d.ts +0 -106
  146. package/dist/lib/session/sync/sync.js +0 -374
@@ -2,7 +2,9 @@ import * as os from 'os';
2
2
  import * as path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { die, humanDuration, relTime, truncate, visibleWidth } from '../lib/format.js';
5
+ import { setHelpSections } from '../lib/help.js';
5
6
  import { listBoxes, readBox, mailboxDir, isValidMailboxId, watchMessages, } from '../lib/mailbox.js';
7
+ import { gcMailbox } from '../lib/mailbox-gc.js';
6
8
  import { GLYPH, masthead, sparkline, aggregate, hourlyCounts, graphEdges, } from '../lib/comms-render.js';
7
9
  import { getMailboxRootDir } from '../lib/state.js';
8
10
  import { getActiveSessions } from '../lib/session/active.js';
@@ -262,6 +264,21 @@ function renderBetween(boxes, a, b, json) {
262
264
  console.log(` ${truncate(m.text.replace(/\s+/g, ' ').trim(), 100)}`);
263
265
  }
264
266
  }
267
+ async function runGcCommand(opts) {
268
+ const sessions = await getActiveSessions();
269
+ const activeBoxIds = new Set(sessions.map(mailboxIdForActiveSession).filter((id) => !!id));
270
+ const result = gcMailbox(activeBoxIds);
271
+ if (opts.json) {
272
+ console.log(JSON.stringify(result, null, 2));
273
+ return;
274
+ }
275
+ if (result.messagesDroppedDead === 0 && result.messagesDroppedExpired === 0) {
276
+ console.log(chalk.dim(`gc: ${result.boxesScanned} boxes scanned, no pending messages dropped.`));
277
+ return;
278
+ }
279
+ console.log(chalk.yellow(`gc: ${result.messagesDroppedDead} dead-box messages, ${result.messagesDroppedExpired} expired messages dropped ` +
280
+ `(${result.boxesScanned} boxes scanned, ${result.deadBoxes} dead boxes).`));
281
+ }
265
282
  function renderGraph(boxes, filters, json) {
266
283
  const nonEmpty = boxes.filter((b) => b.messages.length > 0);
267
284
  const msgs = aggregate(nonEmpty).filter((m) => matchesFilters(m, m.toLabel, m.box, filters));
@@ -313,7 +330,7 @@ function newestMessage(box) {
313
330
  return box.messages.reduce((a, b) => (a.ts >= b.ts ? a : b));
314
331
  }
315
332
  export function registerMailboxesCommand(program) {
316
- program
333
+ const cmd = program
317
334
  .command('mailboxes')
318
335
  .alias('mailbox')
319
336
  .argument('[id]', 'A mailbox id (session UUID / teams agentId) to inspect in full')
@@ -387,4 +404,25 @@ export function registerMailboxesCommand(program) {
387
404
  }
388
405
  renderOverview(boxes, limit, filters);
389
406
  });
407
+ const gcCmd = cmd
408
+ .command('gc')
409
+ .description('Run a liveness sweep: archive pending messages in dead boxes and prune stale consumed mail.')
410
+ .option('--json', 'Emit the GC result as JSON')
411
+ .action(async (_opts, command) => runGcCommand({ json: command.optsWithGlobals().json === true }));
412
+ setHelpSections(gcCmd, {
413
+ examples: `
414
+ # One-shot sweep using the live session set as the liveness source
415
+ agents mailboxes gc
416
+
417
+ # Machine-readable summary (for scripts / monitors)
418
+ agents mailboxes gc --json
419
+ `,
420
+ notes: `
421
+ A box is considered dead when no live session (the same source
422
+ \`agents sessions --active\` uses) owns it. Pending messages in dead boxes
423
+ are archived as \`dropped: dead\`; expired messages in live boxes are
424
+ archived as \`dropped: expired\'. Both surfaces a failure receipt back to
425
+ the feed store when the message carried a blockId.
426
+ `,
427
+ });
390
428
  }
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { spawn } from 'child_process';
3
3
  import { die } from '../lib/format.js';
4
+ import { parseDuration } from '../lib/hooks/cache.js';
4
5
  import { getActiveSessions } from '../lib/session/active.js';
5
6
  import { getTaskById, updateTaskStatus } from '../lib/cloud/store.js';
6
7
  import { resolveProvider } from '../lib/cloud/registry.js';
@@ -53,6 +54,7 @@ async function deliverViaMailbox(mailboxId, text, block, opts) {
53
54
  text,
54
55
  from: opts.from,
55
56
  blockId: block?.blockId,
57
+ ttlSeconds: opts.ttlSeconds,
56
58
  });
57
59
  if (block) {
58
60
  recordMessageReceipt(block.blockId, {
@@ -116,10 +118,19 @@ export function registerMessageCommand(program) {
116
118
  .option('--from <who>', 'Label recorded as the sender of this message')
117
119
  .option('--as <operator>', 'Verified operator id answering a high-consequence block')
118
120
  .option('--surface <surface>', 'Surface that is sending this answer (feed, terminal, etc.)', 'cli')
121
+ .option('--ttl <dur>', 'Delivery TTL if the message is not consumed (e.g. 30m, 1h, 24h); 0 disables expiry')
119
122
  .action(async (target, text, opts) => {
120
123
  if (!target.trim()) {
121
124
  die('Target must be a session/agent id or cloud task id. Run `agents sessions --active` to list running agents.');
122
125
  }
126
+ let ttlSeconds;
127
+ if (opts.ttl !== undefined) {
128
+ const parsed = parseDuration(opts.ttl);
129
+ if (parsed == null) {
130
+ die(`Invalid --ttl ${JSON.stringify(opts.ttl)}: expected a duration like 30m, 1h, 24h, or 0.`);
131
+ }
132
+ ttlSeconds = parsed;
133
+ }
123
134
  const sessions = await getActiveSessions();
124
135
  const res = resolveMessageTarget(target, sessions, (id) => getTaskById(id) != null);
125
136
  switch (res.kind) {
@@ -152,7 +163,7 @@ export function registerMessageCommand(program) {
152
163
  // First-answer-wins for any path that closes an open block.
153
164
  claimBlockAnswer(block, opts);
154
165
  if (route.kind === 'mailbox') {
155
- await deliverViaMailbox(res.id, text, block, opts);
166
+ await deliverViaMailbox(res.id, text, block, { from: opts.from, ttlSeconds });
156
167
  return;
157
168
  }
158
169
  if (route.kind === 'tmux' || route.kind === 'iterm' || route.kind === 'pty') {
@@ -14,7 +14,7 @@ import { AGENTS, getAllCliStates, agentLabel, } from '../lib/agents.js';
14
14
  import { capableAgents } from '../lib/capabilities.js';
15
15
  import { DEFAULT_REGISTRIES } from '../lib/types.js';
16
16
  import { getRegistries, setRegistry, removeRegistry, search as searchRegistries, resolvePackage, validatedNpmSpec, validatedPyPISpec, buildSkillIndex, verifySkillIntegrity, parseOwnerRepoFromRemote, } from '../lib/registry.js';
17
- import { cloneRepo, commitAndPush, getRemoteUrl, isGitRepo } from '../lib/git.js';
17
+ import { cloneRepo, commitAndPush, getCurrentBranch, getRemoteUrl, isGitRepo } from '../lib/git.js';
18
18
  import { discoverCommands, resolveCommandSource, installCommand, installCommandCentrally, } from '../lib/commands.js';
19
19
  import { discoverSkillsFromRepo, installSkill, installSkillCentrally, } from '../lib/skills.js';
20
20
  import { discoverHooksFromRepo, installHooks, installHooksCentrally, } from '../lib/hooks.js';
@@ -335,7 +335,7 @@ When to use:
335
335
  .description('Generate a skills-index.json for a git repo and push it, making its skills discoverable via agents search/install')
336
336
  .option('--repo <alias>', 'Publish an extra repo added via `agents repo add` (default: your ~/.agents repo)')
337
337
  .option('--name <name>', 'Registry name to suggest in the output (default: the repo name)')
338
- .option('--branch <branch>', 'Branch the raw URL should reference', 'main')
338
+ .option('--branch <branch>', 'Branch to push the index to and reference in the raw URL (default: the repo\'s current branch)')
339
339
  .option('--dry-run', 'Write the index and print the URL without committing or pushing')
340
340
  .addHelpText('after', `
341
341
  Publish walks a repo's skills/ directory, records a sha256 of every SKILL.md,
@@ -398,20 +398,29 @@ the sha256 in the index and abort on mismatch.
398
398
  for (const s of index.skills) {
399
399
  console.log(` ${chalk.cyan(s.name)} ${chalk.gray(`sha256:${s.sha256?.slice(0, 12)}…`)}`);
400
400
  }
401
+ // The branch the URL references must be the branch the index actually
402
+ // lands on. In a real push that's whatever commitAndPush reports back
403
+ // (the requested --branch, else the checked-out branch); in a dry run we
404
+ // resolve it the same way without pushing.
405
+ let urlBranch;
401
406
  if (options.dryRun) {
407
+ urlBranch = options.branch || (await getCurrentBranch(repoDir));
402
408
  console.log(chalk.gray(`\nDry run — wrote ${indexPath} but did not commit or push.`));
403
409
  }
404
410
  else {
405
411
  const pushSpinner = ora('Committing and pushing skills-index.json...').start();
406
- const result = await commitAndPush(repoDir, 'chore: update skills-index.json (agents publish)');
412
+ const result = await commitAndPush(repoDir, 'chore: update skills-index.json (agents publish)', options.branch);
407
413
  if (!result.success) {
408
414
  pushSpinner.fail(`Push failed: ${result.error}`);
409
415
  console.log(chalk.gray('The index was written locally — commit and push it manually to publish.'));
410
416
  process.exit(1);
411
417
  }
412
- pushSpinner.succeed('Pushed skills-index.json');
418
+ // commitAndPush always reports the pushed branch on success; the `??`
419
+ // only satisfies the optional return type, it is not a behavior path.
420
+ urlBranch = result.branch ?? (await getCurrentBranch(repoDir));
421
+ pushSpinner.succeed(`Pushed skills-index.json to ${urlBranch}`);
413
422
  }
414
- const rawUrl = `https://raw.githubusercontent.com/${repoSlug}/${options.branch}/skills-index.json`;
423
+ const rawUrl = `https://raw.githubusercontent.com/${repoSlug}/${urlBranch}/skills-index.json`;
415
424
  const registryName = options.name || repoSlug.split('/')[1] || 'my-skills';
416
425
  console.log(chalk.bold('\nPublished. Share these with anyone who wants your skills:\n'));
417
426
  console.log(chalk.gray(' Index URL:'));
@@ -9,4 +9,42 @@
9
9
  * lifecycle state, plan completion, open PRs, and tickets in flight.
10
10
  */
11
11
  import type { Command } from 'commander';
12
+ import { type LinearMilestone } from '../lib/linear-project-counts.js';
13
+ /** Recursion guard: a peer answering a probe fan-out never re-fans-out itself. */
14
+ export declare const PROJECTS_NO_FANOUT_ENV = "AGENTS_PROJECTS_LOCAL";
15
+ /**
16
+ * One compact trailing note for peers that didn't answer the `--fleet`
17
+ * fan-out — unreachable, running an agents-cli too old to carry `projects
18
+ * probe`, or too slow to finish inside the 12s SSH budget. Mirrors
19
+ * `formatUnreachableNote` (activity) with the probe-specific reasons; empty
20
+ * string when everything answered.
21
+ */
22
+ export declare function formatFleetSkippedNote(skipped: string[]): string;
23
+ /** One `projects list` row, pre-render. */
24
+ export interface ProjectListRow {
25
+ name: string;
26
+ path: string;
27
+ repo: string;
28
+ }
29
+ /**
30
+ * Column widths for `projects list`, sized to the rows actually being printed.
31
+ * Fixed padding was the bug: home-relative roots run past 50 characters, so a
32
+ * hardcoded 32 pushed the repo column off its gridline on every long path.
33
+ * Paths longer than {@link LIST_PATH_MAX} truncate rather than widen the table.
34
+ */
35
+ export declare function computeProjectListWidths(rows: ProjectListRow[]): {
36
+ name: number;
37
+ path: number;
38
+ repo: number;
39
+ };
40
+ /**
41
+ * A milestone's target date as a person would say it — "due tomorrow",
42
+ * "overdue by 3 days", "due Aug 21" — never a raw `2026-08-21` or a duration in
43
+ * hours. Linear stores a calendar date with no timezone, so both sides are
44
+ * compared at LOCAL midnight; parsing `YYYY-MM-DD` with `new Date(str)` would
45
+ * read it as UTC and shift the answer by a day for anyone west of Greenwich.
46
+ */
47
+ export declare function formatMilestoneDue(targetDate: string, nowMs: number): string | undefined;
48
+ /** The `next` card line: what this project is due to hit, and how far along it is. */
49
+ export declare function formatNextMilestone(ms: LinearMilestone, nowMs: number): string;
12
50
  export declare function registerProjectsCommands(program: Command): void;