@phnx-labs/agents-cli 1.20.91 → 1.20.93

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 (105) hide show
  1. package/CHANGELOG.md +276 -0
  2. package/README.md +1 -1
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +72 -6
  5. package/dist/commands/activity.js +198 -49
  6. package/dist/commands/beta.js +1 -0
  7. package/dist/commands/doctor.js +4 -2
  8. package/dist/commands/events.js +91 -1
  9. package/dist/commands/exec.d.ts +14 -0
  10. package/dist/commands/exec.js +144 -14
  11. package/dist/commands/projects.d.ts +22 -0
  12. package/dist/commands/projects.js +539 -0
  13. package/dist/commands/secrets.d.ts +17 -0
  14. package/dist/commands/secrets.js +198 -7
  15. package/dist/commands/send.d.ts +14 -12
  16. package/dist/commands/send.js +105 -35
  17. package/dist/commands/sessions-picker.d.ts +15 -0
  18. package/dist/commands/sessions-picker.js +37 -12
  19. package/dist/commands/sessions-resume.d.ts +2 -0
  20. package/dist/commands/sessions-resume.js +9 -1
  21. package/dist/commands/sessions.d.ts +10 -5
  22. package/dist/commands/sessions.js +65 -27
  23. package/dist/commands/sync.js +9 -3
  24. package/dist/commands/view.js +4 -0
  25. package/dist/index.js +18 -1
  26. package/dist/lib/activity.d.ts +77 -12
  27. package/dist/lib/activity.js +424 -74
  28. package/dist/lib/beta.d.ts +1 -1
  29. package/dist/lib/beta.js +1 -1
  30. package/dist/lib/channels/send.d.ts +83 -0
  31. package/dist/lib/channels/send.js +112 -0
  32. package/dist/lib/devices/registry.d.ts +14 -0
  33. package/dist/lib/devices/registry.js +37 -0
  34. package/dist/lib/events-ingest.d.ts +46 -0
  35. package/dist/lib/events-ingest.js +182 -0
  36. package/dist/lib/events.d.ts +15 -3
  37. package/dist/lib/events.js +55 -3
  38. package/dist/lib/feed-post.js +8 -2
  39. package/dist/lib/hosts/remote-cmd.js +4 -0
  40. package/dist/lib/linear-project-counts.d.ts +62 -0
  41. package/dist/lib/linear-project-counts.js +122 -0
  42. package/dist/lib/linear-projects.d.ts +50 -0
  43. package/dist/lib/linear-projects.js +114 -0
  44. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  45. package/dist/lib/menubar/install-menubar.d.ts +14 -4
  46. package/dist/lib/menubar/install-menubar.js +20 -6
  47. package/dist/lib/menubar/notify-desktop.d.ts +17 -2
  48. package/dist/lib/menubar/notify-desktop.js +8 -2
  49. package/dist/lib/project-key.d.ts +44 -0
  50. package/dist/lib/project-key.js +79 -0
  51. package/dist/lib/project-probe.d.ts +75 -0
  52. package/dist/lib/project-probe.js +160 -0
  53. package/dist/lib/project-resources.d.ts +8 -0
  54. package/dist/lib/project-resources.js +31 -3
  55. package/dist/lib/project-root.js +16 -0
  56. package/dist/lib/project-status.d.ts +100 -0
  57. package/dist/lib/project-status.js +182 -0
  58. package/dist/lib/projects.d.ts +144 -0
  59. package/dist/lib/projects.js +313 -0
  60. package/dist/lib/remote-agents-json.d.ts +9 -0
  61. package/dist/lib/remote-agents-json.js +11 -5
  62. package/dist/lib/routine-notify.d.ts +11 -0
  63. package/dist/lib/routine-notify.js +22 -0
  64. package/dist/lib/run-notify.js +3 -0
  65. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  66. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  67. package/dist/lib/secrets/audit.d.ts +1 -1
  68. package/dist/lib/secrets/audit.js +53 -10
  69. package/dist/lib/secrets/list-filter.d.ts +20 -5
  70. package/dist/lib/secrets/list-filter.js +22 -6
  71. package/dist/lib/secrets/usage-db.d.ts +106 -0
  72. package/dist/lib/secrets/usage-db.js +236 -0
  73. package/dist/lib/session/bash-command.d.ts +53 -0
  74. package/dist/lib/session/bash-command.js +364 -0
  75. package/dist/lib/session/digest.d.ts +6 -0
  76. package/dist/lib/session/digest.js +19 -0
  77. package/dist/lib/session/relative-time.d.ts +23 -0
  78. package/dist/lib/session/relative-time.js +60 -8
  79. package/dist/lib/session/remote-active.d.ts +5 -1
  80. package/dist/lib/session/remote-active.js +4 -1
  81. package/dist/lib/session/remote-list.js +5 -2
  82. package/dist/lib/session/render.d.ts +2 -9
  83. package/dist/lib/session/render.js +25 -56
  84. package/dist/lib/sqlite.js +28 -1
  85. package/dist/lib/ssh-exec.d.ts +6 -0
  86. package/dist/lib/ssh-exec.js +10 -1
  87. package/dist/lib/startup/command-registry.d.ts +1 -0
  88. package/dist/lib/startup/command-registry.js +2 -0
  89. package/dist/lib/state.d.ts +14 -0
  90. package/dist/lib/state.js +19 -0
  91. package/dist/lib/terminal/backends/index.d.ts +10 -2
  92. package/dist/lib/terminal/backends/index.js +14 -2
  93. package/dist/lib/terminal/backends/terminal-app.d.ts +13 -0
  94. package/dist/lib/terminal/backends/terminal-app.js +73 -0
  95. package/dist/lib/terminal/index.d.ts +2 -1
  96. package/dist/lib/terminal/index.js +2 -1
  97. package/dist/lib/terminal/preferred.d.ts +89 -0
  98. package/dist/lib/terminal/preferred.js +87 -0
  99. package/dist/lib/terminal/run-surface.d.ts +82 -0
  100. package/dist/lib/terminal/run-surface.js +146 -0
  101. package/dist/lib/terminal/types.d.ts +1 -1
  102. package/dist/lib/types.d.ts +6 -5
  103. package/dist/lib/versions.d.ts +6 -0
  104. package/dist/lib/versions.js +6 -4
  105. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,270 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.20.93
4
+
5
+ - **`agents send` is a real delivery envelope; `notify` is just `--to owner` (RUSH-2123).** Flag-first form: `--to`, `--text`, `--channel`, `--attach`, `--url`. `--to owner` expands from `notify.owner` in agents.yaml. Positional text still works. Help names the three planes (deliver / record / control) so send is not confused with `feed post`, `activity`, or `message`/`sessions inject`. Source: `apps/cli/src/commands/send.ts`, `apps/cli/src/lib/channels/send.ts`.
6
+
7
+ - **`agents events emit` — record events produced outside the CLI.** In-process code
8
+ calls `emit()` directly, but the producers that most need to record events are not
9
+ agents-cli processes: the Factory VS Code extension host, shell guards, external
10
+ tools. They now pipe JSONL on stdin —
11
+ `… | agents events emit --source factory`. `--source` is stamped as `module`, so
12
+ `agents events --module factory` filters to one producer. Routing is forced by the
13
+ stores rather than chosen: a milestone kind requires a `sessionId` and lands in that
14
+ session's activity log, everything else lands in the operational log. A milestone
15
+ with no `sessionId` is rejected, not quietly written elsewhere. Rejection is per
16
+ line, so one bad line never discards a batch, and the exit code is 1 if any line was
17
+ rejected. `--dry-run` validates without writing.
18
+ Source: `apps/cli/src/lib/events-ingest.ts`, `apps/cli/src/commands/events.ts`,
19
+ `apps/cli/docs/06-observability.md`.
20
+
21
+ - **Four `factory.*` event kinds.** `factory.command`, `factory.action`, `factory.uri`
22
+ and `factory.launch` describe what a user did in the Factory VS Code extension.
23
+ `factory.launch` is a milestone — it carries the `sessionId` and `terminalId` that
24
+ later events join through — and `factory.uri` is audit-level, since an external
25
+ process driving the user's editor is a "who reached in from outside" fact.
26
+ Source: `apps/cli/src/lib/events.ts`, `apps/cli/src/lib/activity.ts`.
27
+
28
+ - **`emit()` accepts a caller-supplied timestamp.** A batched producer records when
29
+ each event *happened* and flushes later; without this, every event in a flush was
30
+ stamped at flush time, collapsing their order and corrupting `--since` boundaries.
31
+ `ts` stays reserved against payload injection — only the explicit override can set
32
+ it. Source: `apps/cli/src/lib/events.ts`.
33
+
34
+ - **Fixed: `agents _internal friction` recorded its own invocation.** The command
35
+ exists precisely because shell guards run before any `agents` process exists and so
36
+ cannot emit in-process, but it still fired the `command.start` / `command.end` audit
37
+ hooks, writing two records on top of every friction record. Recorder commands are now
38
+ exempt. Source: `apps/cli/src/index.ts`.
39
+
40
+ - **Desktop notifications now show the agent on the right, not a second copy of
41
+ the app icon.** macOS draws two images on a banner: the sending app's icon on
42
+ the left and `contentImage` on the right (a YouTube notification uses the slots
43
+ for "YouTube" plus the channel avatar). agents-cli was putting its own lime mark
44
+ in the right slot, so both slots said the same thing. The right slot now carries
45
+ the harness the notification is *about* — a brand-colored tile with a two-letter
46
+ mark (`CL` claude, `CX` codex, `GK` grok, …), two letters because four harnesses
47
+ start with `c` and two with `g`. `agents run --notify` and agent/workflow
48
+ routines pass their harness through; a daemon heal, an overdue sweep, a command
49
+ routine, or a fan-out across several agents has no single agent and leaves the
50
+ right slot empty. Source:
51
+ `apps/cli/menubar/Sources/MenubarHelper/AgentAvatar.swift`,
52
+ `apps/cli/src/lib/menubar/notify-desktop.ts`, `apps/cli/src/lib/run-notify.ts`,
53
+ `apps/cli/src/lib/routine-notify.ts`, `apps/cli/docs/menubar.md`.
54
+
55
+ - **`MenubarHelper --notify` gains `--agent <id>`.** The one-shot notifier accepts
56
+ the harness id that drives the right-hand avatar; omitting it is how a caller
57
+ says "no single agent owns this event".
58
+
59
+ - **`agents projects status --fleet` — per-device workspace drift (beta).**
60
+ Projects are natively multi-device; `--fleet` adds a `fleet` line to the status
61
+ card showing, for each project, whether its workspace repos are present on every
62
+ fleet device, on which branch, ahead/behind their upstream (`↑`/`↓`), and how
63
+ many uncommitted changes they carry — plus a hidden `agents projects probe
64
+ --json <path...>` subcommand that is the peer half of the fan-out. One parallel
65
+ SSH call per device (12s timeout), **no `git fetch`** — drift is measured
66
+ against each peer's last-fetched upstream, and a repo with no upstream reports
67
+ no drift rather than zero. Peers that are unreachable or run an older CLI are
68
+ named once in a trailing note; `probe` itself is not beta-gated so peers answer
69
+ whenever their binary carries it. The schema gains `repos[].path` (home-relative
70
+ local checkout) to opt additional repos into probing beyond the primary `root`,
71
+ and `--json` gains per-project `workspaces[]` with the host-tagged probe rows.
72
+ The card's `live` line also counts agents on every box under `--fleet` via the
73
+ existing sessions fan-out. Source: `apps/cli/src/lib/project-probe.ts`,
74
+ `apps/cli/src/commands/projects.ts`, `apps/cli/src/lib/projects.ts`.
75
+
76
+ - **`agents projects` outcomes on the card — agent×project members, releases,
77
+ Linear counts, and `projects link --linear` (beta).** The status card gains an
78
+ `agents` line under `live` naming WHICH harness is on each project
79
+ (`claude · running · RUSH-2107 @zion`, sorted running-first, capped at 6 with a
80
+ `+N more` tail; under `--fleet` remote agents carry their peer's hostname), a
81
+ latest-release tag on the `ships` line (primary repo only, best-effort
82
+ `gh release list`), and a `linear` line counting the bound Linear project's
83
+ issues by state type (`12/30 done · 5 in progress`) — best-effort with an 8s
84
+ budget, skipped by `--no-remote`, and omitted when the def has no
85
+ `linear.projectId`. The new `agents projects link <name> --linear [query]`
86
+ writes that binding: no query auto-suggests from the def name + repo slug via
87
+ the normalized-key matcher (ported from Factory's `linearProjects.ts`),
88
+ ambiguous/none prints the candidate list and exits 1. `--json` gains
89
+ `members[]`, `latestRelease`, and `linear`. Source:
90
+ `apps/cli/src/lib/project-status.ts`, `apps/cli/src/lib/linear-projects.ts`,
91
+ `apps/cli/src/lib/linear-project-counts.ts`, `apps/cli/src/commands/projects.ts`.
92
+
93
+ - **Project sync no longer spams a warning per file it left alone.** Syncing a
94
+ project whose `.claude/commands/` you wrote yourself printed one wrapped
95
+ `Skipping project resource target …: already exists and is user-owned` line
96
+ per file — six hand-authored commands meant twelve lines of terminal noise in
97
+ the middle of `agents view claude`. Those files are the normal steady state,
98
+ not a warning, so the sync now reports them once, grouped, in plain words:
99
+ `Kept 6 of your own files in .claude/commands: debug.md, doc-gaps.md,
100
+ image-nbp.md, +3 more`. The list also rides out on `SyncResult.projectSkipped`
101
+ for callers that want it. Source: `apps/cli/src/lib/project-resources.ts`.
102
+
103
+ - **`agents secrets` now tracks per-bundle usage and surfaces it.** Every secret
104
+ lifecycle/access event — create, import, export, view, access (a value read for
105
+ injection), unlock — funnels through the one `emitSecretAudit` chokepoint, which
106
+ writes to BOTH the append-only `~/.agents/events.jsonl` audit log AND a derived,
107
+ value-free read-model at `~/.agents/secrets/secrets.db` (never a secret value —
108
+ bundle name, event kind, key count, resolving agent/host, status only).
109
+ `agents secrets view <bundle>` now shows whether the bundle is currently
110
+ **unlocked** (held by the secrets-agent, so reads are prompt-free), a **usage**
111
+ summary ("accessed 42× (last 2h ago) · exported 3× (last 1d ago)"), and
112
+ **per-agent** attribution, and nudges when a bundle has no description (also at
113
+ `create` time). `agents secrets list` gains **`--sort uses`** (most frequently
114
+ accessed) alongside the existing `--sort used`, and the `--json` payloads carry
115
+ `uses`, `usage`, and `heldExpiresAt`. A new **`agents secrets activity [bundle]`**
116
+ prints the recent value-free event timeline (bounded to 90 days). Naming guidance
117
+ is taught in the help and skill: name a website bundle after its domain
118
+ (`stripe.com`, `openai.ai`), a desktop-app bundle after its binary suffix
119
+ (`slack.app`, `photoshop.exe`). Recording is best-effort and
120
+ `AGENTS_NO_USAGE_TRACK=1` disables it. Source:
121
+ `apps/cli/src/lib/secrets/usage-db.ts`, `apps/cli/src/lib/secrets/audit.ts`,
122
+ `apps/cli/src/commands/secrets.ts`, `apps/cli/src/lib/secrets/list-filter.ts`.
123
+
124
+ ## 1.20.92
125
+
126
+ - **`agents sessions` rows show creation time as well as last activity (RUSH-2107).**
127
+ The trailing time cell used to carry one unlabeled "X ago" — last activity — so a
128
+ row could not say when the session began or how long it had been alive. It now
129
+ reads `3d → 1 hour ago`: the compact creation age, then the last-activity label the
130
+ listing sorts by. Both the interactive picker and the flat/tree listings render it.
131
+ A session that ran for under a minute keeps a single field (the two halves would
132
+ name the same moment), and a terminal too narrow for both drops the creation age
133
+ rather than squeezing the topic below its floor, so rows never wrap. The picker's
134
+ detail pane spells the same facts out as `created X ago · last active Y ago ·
135
+ lasted Z`, and now derives them from the indexed session metadata when no local
136
+ transcript exists — so **remote** and not-yet-indexed sessions report their timing
137
+ instead of showing none. Source: `apps/cli/src/lib/session/relative-time.ts`,
138
+ `apps/cli/src/commands/sessions.ts`, `apps/cli/src/commands/sessions-picker.ts`.
139
+
140
+ - **Stop fleet health probes from orphaning remote processes on timeout (RUSH-2114).** `sshExecAsync` now uses a direct ssh connection whenever a `timeoutMs` is set, because a control-master outlives the local client and keeps the remote command running after we kill it. `agents doctor` also normalizes host names before excluding the local machine, so `zion.local` can no longer be self-SSH'd. Source: `apps/cli/src/lib/ssh-exec.ts`, `apps/cli/src/commands/doctor.ts`.
141
+ - **Harden menubar install against Gatekeeper rejection.** `ensureValidSignature` now checks `spctl --assess`; a Developer-ID-signed but un-notarized bundle is stripped of quarantine and re-signed ad-hoc so the launchd service does not crash-loop with "app is damaged". The release build script gained optional notarization via `MENUBAR_HELPER_NOTARIZE` and `MENUBAR_HELPER_NOTARIZE_KEYCHAIN_PROFILE`. Source: `apps/cli/src/lib/menubar/install-menubar.ts`, `apps/cli/menubar/scripts/build.sh`.
142
+
143
+ - **`agents activity` now shows the whole fleet, grouped by project.** The
144
+ question the command answers is "what are my agents doing", and agents run on
145
+ every box — but it read only the local logs unless you remembered
146
+ `--devices-all`, and printed one flat newest-first stream. Both defaults are
147
+ inverted: every run fans `activity --json` out to each reachable device and
148
+ merges the peers' streams host-tagged, then buckets them by project, one level,
149
+ no sub-grouping. `--local` scopes back to this machine, `-H/--host` to named
150
+ boxes, and `--flat` (or `--group-by none`) restores the single stream;
151
+ `--devices-all`/`--hosts-all` remain accepted so existing scripts keep working.
152
+ A peer answering the fan-out still carries the recursion guard, so it never
153
+ re-fans the fleet.
154
+
155
+ - **Each project header names the machines its work ran on.** A bucket reads
156
+ `▸ agents-cli 12 events · 4 milestones · zion, yosemite-s0` — up to three
157
+ machines by name plus a `+N` tail, so a project touched by a dozen boxes stays
158
+ one scannable line; individual rows keep their own `[host]` tag. Peers that
159
+ never answered are reported once at the end (`· 2 devices unreachable: …`)
160
+ rather than a line each above the timeline, so a missing machine is visible but
161
+ not noisy.
162
+
163
+ - **A project is now the repository, not whatever directory the agent sat in.**
164
+ A cwd resolves to the git repository containing it, so `<repo>/apps/cli` files
165
+ under `<repo>` instead of `cli`, and a worktree under
166
+ `<repo>/.agents/worktrees/<slug>` folds back into the repo it branched from.
167
+ A directory in no repo groups as itself, and a dotfiles repo at `$HOME` is not
168
+ treated as a project. The `agents sessions` overview and `agents feed post` now
169
+ share this one resolver (`lib/project-key.ts`), so a project reads identically
170
+ everywhere instead of each view folding cwds its own way.
171
+
172
+ - **`--limit` is spent on milestones, not on collapsed churn.** The default view
173
+ rolls routine `file.edited` work up to a count, so a plain slice let one busy
174
+ machine's 40 file edits hide every other device's PRs behind a single
175
+ `file edited ×40` line. The cap now bounds the milestones shown, with the
176
+ routine events inside that window riding along for the counts. `--all` still
177
+ shows routine work inline and caps every event.
178
+
179
+ - **The activity header no longer carries other subsystems' hook warnings.**
180
+ Registering the activity-log hooks surfaced every unresolved entry in the hook
181
+ manifest — a missing `inject-session-id` script, someone else's half-installed
182
+ plugin — printing five wrapped yellow lines above the timeline on every run.
183
+ Those are `agents doctor`'s job; only a failure that would leave the activity
184
+ log unwritten is reported here.
185
+
186
+ - **The menu bar's New Session opens in the terminal you actually work in.** It
187
+ hardcoded AppleScript at Terminal.app, so a Ghostty or iTerm user got a
188
+ Terminal.app window every time. It now shells `agents run <agent> --terminal`,
189
+ and the CLI resolves the terminal from the user's own live sessions — the host
190
+ app `agents sessions --active` already attributes every session to
191
+ (`ActiveSession.host`). Order: the terminal the caller is in, then the host of
192
+ the most recent live session, then the first available backend. Hosts map to
193
+ backends only where the engine can really drive them, so an undrivable host
194
+ (Warp, kitty, Cursor) falls through instead of opening the wrong app. A
195
+ tmux-hosted session (every interactive `agents run`) resolves to the app its
196
+ attached tmux client is in, via the same resolver behind
197
+ `agents sessions`' "viewing in Ghostty tab 2" — without that it would name the
198
+ multiplexer and no terminal at all. Source:
199
+ `apps/cli/src/lib/terminal/preferred.ts`,
200
+ `apps/cli/src/lib/terminal/backends/terminal-app.ts`,
201
+ `apps/cli/menubar/Sources/MenubarHelper/AgentsCLI.swift`.
202
+
203
+ - **`agents run <agent> --terminal` opens a run in a real terminal tab.** For a
204
+ caller that cannot host a TUI (the menu bar, a script). Without a value the
205
+ terminal is detected as above; `--terminal <backend>` forces one
206
+ (`iterm | ghostty | terminal | tmux | vscodium-agent`) and errors on an unknown
207
+ id rather than silently auto-detecting. The tab re-invokes the same argv with
208
+ the flag stripped, so `--mode`, `--cwd`, and a `--` passthrough ride along.
209
+ Cannot combine with `--host`. Source: `apps/cli/src/lib/terminal/run-surface.ts`,
210
+ `apps/cli/src/commands/exec.ts`.
211
+
212
+ - **Terminal.app is a real launch backend now (`terminal`).** Registered last, so
213
+ it is the every-Mac floor without outranking a terminal the user chose to
214
+ install, and reported unavailable over SSH where `osascript` cannot reach the
215
+ GUI login. It has no scriptable split, so a split request opens a tab, and
216
+ `agents sessions resume --splits` now says so instead of quietly producing
217
+ tabs. `detectCurrentBackend` also recognizes `TERM_PROGRAM=Apple_Terminal`.
218
+ Source: `apps/cli/src/lib/terminal/backends/terminal-app.ts`.
219
+
220
+ - **`agents sessions resume` / `sessions focus` reach Terminal.app too.** Adding
221
+ it to the backend registry changes both: on a Mac with neither iTerm, Ghostty,
222
+ nor VSCodium installed they used to fall back to resuming in the current
223
+ process, and now open a Terminal.app tab; `resume`'s interactive picker gains a
224
+ Terminal row, and `--terminal-app` forces it (named apart from
225
+ `run --terminal`, which means something different). Source:
226
+ `apps/cli/src/commands/sessions-resume.ts`, `apps/cli/src/commands/focus.ts`.
227
+
228
+ - **New Task… in the menu bar.** A row above New Session that opens the
229
+ quick-dispatch bar — the same panel as `Cmd-Shift-O`, now reachable without the
230
+ chord (and without the Accessibility grant the chord needs). The status item
231
+ owns the one panel instance, so an interrupted capture is restored whichever
232
+ entry point you return through. Source:
233
+ `apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`.
234
+
235
+ - **Activity, feed posts, and the sessions overview now speak defined project
236
+ names.** One resolver (`resolveProjectNameForCwd`, `lib/projects.ts`) backs all
237
+ three: a cwd inside a defined project (`~/.agents/projects/<name>.yaml`) reads
238
+ as the project's name — a multi-repo project is a single bucket in `agents
239
+ activity`, not one per repo — and anything else falls back to the
240
+ repository-level key, so nothing changes without definitions. Each peer
241
+ resolves its own cwds against its synced definitions before events cross the
242
+ wire. Source: `apps/cli/src/lib/projects.ts`, `apps/cli/src/commands/activity.ts`.
243
+
244
+ - **`agents activity --project <name>`** narrows the fleet stream to one project,
245
+ exact-matched on the resolved label — one project's PRs, plans, and worktrees
246
+ across every box without the rest of the fleet's noise. Source:
247
+ `apps/cli/src/lib/activity.ts` (`filterActivityByProject`).
248
+
249
+ - **`agents projects` — named multi-repo projects with a project progress rollup (beta).**
250
+ Define a project once in `~/.agents/projects/<name>.yaml` (name, home-relative
251
+ root/defaultPath, multiple repos with monorepo subpaths, described `contexts[]`
252
+ starting points, external `integrations[]`, Linear link) and `agents run --project
253
+ <name>` resolves the definition before the old `<root>/<slug>` convention — undefined
254
+ slugs behave exactly as before. The headline is `agents projects status`: instead of a
255
+ per-agent activity line, it renders one card per project — live agents by state, plan
256
+ completion, open **and** recently-merged PRs, tickets in flight, and the artifacts
257
+ agents produced — by rolling up signals already on disk (live agents matched to a project
258
+ by this machine's session cwd; the merged-PR count is repo-global via `gh`). `--window
259
+ <days>` and `--no-remote`
260
+ tune the PR/artifact lookup. Also `list` / `add` (infers root + origin slug) / `show` /
261
+ `edit` / `import --from-factory` (absorbs the Factory `projects.json` registry) / `rm`.
262
+ Enable with `agents beta enable projects`. Source: `apps/cli/src/lib/projects.ts`,
263
+ `apps/cli/src/lib/project-status.ts`, `apps/cli/src/commands/projects.ts`,
264
+ `apps/cli/src/lib/project-root.ts`.
265
+
266
+ - **The cross-fleet session sweep no longer hides sessions on manually-registered devices.** `agents sessions` fan-out (and therefore `--resolve`, cross-machine resume, and `--active`) picked peers with a strict `tailscale.online === true` test. A device registered with `address.via: "manual"` never gets a Tailscale peer entry at all, so its `online` stayed `undefined` and the sweep skipped it **permanently** — every session on that box was invisible and could not be resolved or resumed from any other machine. Peer selection is now `isDialableDevice`, a union of both liveness signals: a device with no Tailscale block is unknown-not-offline (the rule `ssh.ts` `renderDeviceTable` and Factory's `isDeviceOnline` already used, so the picker and the sweep finally agree on who exists), and a positive live SSH probe (`DeviceProfile.reachability`, RUSH-1965) additionally rescues a device whose snapshot says offline. A **failed** probe deliberately does not remove a peer — the probe runs on a short SSH budget and returns false negatives on a congested tailnet (observed calling the local machine unreachable), and letting that shrink the sweep would hide sessions on healthy boxes. Applied to both sweeps that share this shape. Source: `apps/cli/src/lib/devices/registry.ts` (`isDialableDevice`), `apps/cli/src/lib/session/remote-list.ts`, `apps/cli/src/lib/remote-agents-json.ts`.
267
+
3
268
  ## 1.20.91
4
269
 
5
270
  - **An agent can now say it is stuck: `agents feed post --blocked` (RUSH-2110).** The
@@ -123,6 +388,17 @@
123
388
 
124
389
  ## 1.20.90
125
390
 
391
+ - **Bash commands are now parsed and classified for richer activity summaries.** The
392
+ `11-activity-log.py` hook tokenizes every Bash tool call and emits a structured
393
+ `bash.executed` activity record with `category`, `bashTool`, and `bashAction`.
394
+ High-signal commands also raise milestones: `video.rendered`/`video.converted`
395
+ for `ffmpeg`, `image.upscaled` for `realesrgan`/`waifu2x`/`swin2sr`, and
396
+ `metadata.edited` for `exiftool`/`id3v2`/`metaflac`/`vorbiscomment`. The session
397
+ renderer and digest use the shared `lib/session/bash-command.ts` classifier.
398
+ Source: `apps/cli/src/lib/session/bash-command.ts`,
399
+ `apps/cli/src/lib/activity.ts`, `apps/cli/src/lib/session/digest.ts`,
400
+ `apps/cli/src/lib/session/render.ts`.
401
+
126
402
  - **`agents sessions --active` now shows one row per agent, not one per directory.**
127
403
  A live tmux agent pane whose durable identity records were missing (the common case
128
404
  once meta/pid-registry entries age out) was dropped, then re-surfaced by the ps-scan
package/README.md CHANGED
@@ -288,7 +288,7 @@ Filters **stack** (they AND together), the active set shows in the header, and t
288
288
 
289
289
  Each live session resolves to `working`, `waiting_input` (with why -- a question, a plan review, or a permission prompt), or `idle`, alongside badges for the PR it opened, the worktree it sits in, and the ticket it's working. `agents sessions focus [id]` attaches the live pane in place -- the tmux split locally or over SSH, or its Ghostty tab -- and falls back to a fresh tab + resume when the terminal is gone.
290
290
 
291
- Landing on a session cold? `agents sessions <id>` prints a catch-up digest: an inferred title, files changed grouped by directory (created / modified / deleted), a histogram of which tools did the work, and the last test verdict -- the signals to reload a task in seconds.
291
+ Landing on a session cold? `agents sessions <id>` prints a catch-up digest: an inferred title, files changed grouped by directory (created / modified / deleted), a histogram of which tools did the work (including parsed Bash commands -- `git`, `npm`, `ffmpeg`, `ssh`, and so on), and the last test verdict -- the signals to reload a task in seconds.
292
292
 
293
293
  ### Resume anywhere — and stay resumed
294
294
 
package/dist/bin/agents CHANGED
Binary file
@@ -8,14 +8,80 @@
8
8
  * it tails the logs and collapses routine work to counts, surfacing milestones
9
9
  * individually and newest-first.
10
10
  *
11
- * Fleet-wide by opt-in: `--devices-all` (or `--host <box>`) fans the same
12
- * `activity --json` payload out across the fleet the way `agents feed` does,
13
- * merges every peer's stream host-tagged, and joins each item to live sessions
14
- * (project / ticket / execution host) so `--group-by project` shows, per
15
- * project, what each agent did, where, and for which ticket -- progress at a
16
- * glance. Local-only stays the default.
11
+ * Fleet-wide and project-grouped BY DEFAULT: the question this command answers
12
+ * is "what are my agents doing", and agents run on every box, so a local-only
13
+ * flat stream answered a narrower question than anyone asked. Each run fans the
14
+ * same `activity --json` payload out across the fleet the way `agents feed`
15
+ * does, merges every peer's stream host-tagged, joins each item to live sessions
16
+ * (project / ticket / execution host), and buckets it by project -- one level,
17
+ * no sub-grouping, with the machines named in each project's header.
18
+ *
19
+ * `--local` scopes back to this machine, `--host <box>` to named peers, and
20
+ * `--flat` (or `--group-by none`) restores the single newest-first stream.
17
21
  */
18
22
  import type { Command } from 'commander';
23
+ import { type ActivityGroupBy } from '../lib/activity.js';
19
24
  /** Recursion guard: a peer answering a fan-out never re-fans-out itself. */
20
25
  export declare const ACTIVITY_NO_FANOUT_ENV = "AGENTS_ACTIVITY_LOCAL";
26
+ interface ActivityOpts {
27
+ json?: boolean;
28
+ all?: boolean;
29
+ milestones?: boolean;
30
+ limit: number;
31
+ since?: number;
32
+ local?: boolean;
33
+ host?: string[];
34
+ device?: string[];
35
+ devicesAll?: boolean;
36
+ hostsAll?: boolean;
37
+ groupBy?: string;
38
+ flat?: boolean;
39
+ project?: string;
40
+ filter?: string;
41
+ }
42
+ /**
43
+ * Does this hook-registration error concern one of the activity-log hooks?
44
+ * `registerHooksToSettings` prefixes each error with the manifest hook name
45
+ * (`<name>: script not found`), so the entry names are the discriminator.
46
+ */
47
+ export declare function isActivityHookError(error: string): boolean;
48
+ /**
49
+ * Is this error attributable to a named manifest hook at all? Errors that name
50
+ * no hook — `Failed to parse settings.json`, `Failed to write
51
+ * agents-cli-hooks.ts: …` — are agent-level aborts, not per-hook noise, and
52
+ * must never be filtered out.
53
+ */
54
+ export declare function isManifestHookError(error: string, manifestNames: string[]): boolean;
55
+ /**
56
+ * One compact trailing note for peers that didn't answer the fan-out. Now that
57
+ * every run dials the fleet, an offline box is routine -- but never silent: the
58
+ * reader has to know the timeline is missing a machine, not that the machine
59
+ * was idle. Empty string when everything answered.
60
+ */
61
+ export declare function formatUnreachableNote(skipped: string[]): string;
62
+ /**
63
+ * Resolve `--group-by` / `--flat` into the grouping dimension, or `undefined`
64
+ * for a flat stream. Throws on an unknown value so the caller can exit 1 --
65
+ * a mistyped dimension must not silently fall back to the default.
66
+ */
67
+ export declare function resolveActivityGrouping(opts: Pick<ActivityOpts, 'groupBy' | 'flat'>): ActivityGroupBy | undefined;
68
+ /** Which machines an invocation reads from. Pure -- no SSH, no device registry. */
69
+ export interface ActivityScope {
70
+ /** Read this box's own activity logs. */
71
+ includeLocal: boolean;
72
+ /** Dial peers over SSH. */
73
+ wantRemote: boolean;
74
+ /** Peers to dial; `undefined` means "every reachable device". */
75
+ remoteHosts?: string[];
76
+ }
77
+ /**
78
+ * Decide the read scope. Fleet-wide is the DEFAULT -- agents run on every box,
79
+ * so "what are my agents doing" is a fleet question -- and three things narrow
80
+ * it: `--local`, an explicit `--host`/`--device` list, and the recursion guard
81
+ * a peer answering a fan-out carries (else every peer would re-fan the fleet).
82
+ * `--devices-all`/`--hosts-all` are kept as explicit no-ops for scripts that
83
+ * already pass them.
84
+ */
85
+ export declare function resolveActivityScope(opts: Pick<ActivityOpts, 'local' | 'host'>, self: string, noFanoutEnv?: boolean): ActivityScope;
21
86
  export declare function registerActivityCommand(program: Command): void;
87
+ export {};