agenthud 0.12.4 → 0.13.1
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.
- package/README.md +32 -252
- package/dist/index.js +1 -1
- package/dist/{main-PNDDQ5WC.js → main-GMNV6O4E.js} +129 -34
- package/dist/templates/summary-prompt.md +33 -14
- package/dist/templates/summary-range-prompt.md +18 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,9 +10,11 @@ An observability layer for [Claude Code](https://github.com/anthropics/claude-co
|
|
|
10
10
|
|
|
11
11
|
AgentHUD reads Claude Code's session files from `~/.claude/projects/` and gives you three things:
|
|
12
12
|
|
|
13
|
-
- **Live monitor** (
|
|
14
|
-
- **Structured export** (
|
|
15
|
-
- **LLM digest** (
|
|
13
|
+
- **Live monitor** (`agenthud`) — a split-view TUI showing every project, session, sub-agent, and activity as it happens
|
|
14
|
+
- **Structured export** (`agenthud report`) — Markdown or JSON for piping to scripts, dashboards, or other LLMs
|
|
15
|
+
- **LLM digest** (`agenthud summary`) — a day or a date range synthesized into an engineering summary via the `claude` CLI
|
|
16
|
+
|
|
17
|
+
→ **See [FEATURES.md](./FEATURES.md) for the full surface** — every flag, keybinding, config key, file path, and env var.
|
|
16
18
|
|
|
17
19
|
## Install
|
|
18
20
|
|
|
@@ -26,17 +28,28 @@ Run this in a separate terminal while using Claude Code. Press `?` inside the TU
|
|
|
26
28
|
|
|
27
29
|
> **Platform support.** Primary development is on macOS and Linux; the full test suite runs on all three platforms in CI (including Windows). Windows runtime behavior is exercised by a manual smoke job but isn't daily-driven — issues there are valued bug reports.
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
## Quickstart
|
|
30
32
|
|
|
31
33
|
```bash
|
|
32
|
-
|
|
33
|
-
agenthud
|
|
34
|
-
agenthud --
|
|
34
|
+
# Live monitor
|
|
35
|
+
agenthud # all Claude projects
|
|
36
|
+
agenthud --cwd # scope to the project containing $PWD
|
|
37
|
+
agenthud --once # snapshot mode, no alt-screen
|
|
38
|
+
|
|
39
|
+
# Activity report
|
|
40
|
+
agenthud report --date today # today's activity as markdown
|
|
41
|
+
agenthud report --format json # script-readable
|
|
42
|
+
agenthud report --with-git # merge git commits into the timeline
|
|
43
|
+
|
|
44
|
+
# LLM summary
|
|
45
|
+
agenthud summary --date today # daily summary via `claude -p`
|
|
46
|
+
agenthud summary --last 7d # cross-day synthesis of last 7 days
|
|
47
|
+
agenthud summary -oI # open the summary + the summaries index
|
|
35
48
|
```
|
|
36
49
|
|
|
37
|
-
##
|
|
50
|
+
## What you see
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
The TUI splits into a project tree (top) and an activity viewer (bottom):
|
|
40
53
|
|
|
41
54
|
```
|
|
42
55
|
┌─ Projects ─────────────────────────────────────────────────┐
|
|
@@ -57,257 +70,24 @@ AgentHUD's TUI splits the screen into a project tree and an activity viewer:
|
|
|
57
70
|
└────────────────────────────────────────────────────────────┘
|
|
58
71
|
```
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
- Sessions grouped under their project (project name + path at the top).
|
|
62
|
-
- Session rows show short ID + first user prompt (the session's "topic"). Long titles truncate with a `…` suffix.
|
|
63
|
-
- Right edge of each row shows how long ago it was last touched: `42m`, `17h`, `3d`, `2w`, `1mo`, `1y`. Project rows use the most recent session's mtime.
|
|
64
|
-
- Non-interactive sessions (from `claude -p`, SDK, `agenthud summary`) appear in parens and dimmed.
|
|
65
|
-
- Sub-agents nest one level deeper under their parent session.
|
|
66
|
-
- Cold projects collapse under `... N cold projects` at the bottom (press Enter on the line to expand).
|
|
67
|
-
- Press `h` to hide a project, session, or sub-agent (saved to `~/.agenthud/state.yaml`).
|
|
68
|
-
|
|
69
|
-
**Activity viewer (bottom pane)**
|
|
70
|
-
- Real-time feed for the selected session: file reads, edits, bash, responses, thinking, git commits. Newest at the bottom, like `tail -f`.
|
|
71
|
-
- The **newest visible row is rendered "alive"** while in LIVE mode: its icon is replaced with a spinning glyph and the text turns bold. When a new activity arrives, the spinner moves to the new bottom row. Hidden when paused or empty.
|
|
72
|
-
- Press `f` to cycle through filter presets (configurable).
|
|
73
|
-
- Press `↵` on any row to open a scrollable detail view; on a commit row this shows `git show --stat --patch`.
|
|
74
|
-
|
|
75
|
-
### Session status
|
|
76
|
-
|
|
77
|
-
Each session row carries a colored badge derived from when its JSONL file was last touched:
|
|
78
|
-
|
|
79
|
-
| Badge | Color | Meaning |
|
|
80
|
-
|-------|-------|---------|
|
|
81
|
-
| `[hot]` | green | Updated in the last 30 minutes — actively running |
|
|
82
|
-
| `[warm]` | yellow | Updated in the last hour |
|
|
83
|
-
| `[cool]` | cyan | Updated earlier today |
|
|
84
|
-
| `[cold]` | gray | Last updated yesterday or earlier — collapsed under `... N cold projects` at the bottom |
|
|
85
|
-
|
|
86
|
-
Sub-agents use the same scheme. Projects inherit the hottest status of their sessions; a project is treated as "cold" only when all its sessions are cold.
|
|
87
|
-
|
|
88
|
-
### Activity types
|
|
89
|
-
|
|
90
|
-
| Icon | Type | Description |
|
|
91
|
-
|------|------|-------------|
|
|
92
|
-
| `○` | Read | File being read |
|
|
93
|
-
| `~` | Edit / Write | File being modified |
|
|
94
|
-
| `$` | Bash | Shell command |
|
|
95
|
-
| `*` | Glob / Grep | File search |
|
|
96
|
-
| `@` | WebFetch / WebSearch | Web request |
|
|
97
|
-
| `»` | Task | Sub-agent task |
|
|
98
|
-
| `<` | Response | Claude's text response |
|
|
99
|
-
| `>` | User | Your message |
|
|
100
|
-
| `…` | Thinking | Claude's thinking (requires `showThinkingSummaries: true`) |
|
|
101
|
-
| `◆` | Commit | Git commit in the project (when `--with-git` or in viewer) |
|
|
102
|
-
|
|
103
|
-
### Keyboard shortcuts
|
|
104
|
-
|
|
105
|
-
Full reference is also available inside the app — press `?`.
|
|
106
|
-
|
|
107
|
-
#### Project tree focus
|
|
108
|
-
|
|
109
|
-
| Key | Action |
|
|
110
|
-
|-----|--------|
|
|
111
|
-
| `↑` / `k` | Move up |
|
|
112
|
-
| `↓` / `j` | Move down |
|
|
113
|
-
| `←` | Jump to parent (sub-agent → session, session → project) |
|
|
114
|
-
| `↵` | Expand/collapse project, session, or sub-agent summary |
|
|
115
|
-
| `h` | Hide selected (project / session / sub-agent) |
|
|
116
|
-
| `Tab` | Switch focus to activity viewer |
|
|
117
|
-
| `PgUp` / `Ctrl+B` | Page up |
|
|
118
|
-
| `PgDn` / `Ctrl+F` | Page down |
|
|
119
|
-
| `t` | Track — auto-follow the newest live sub-agent (any nav key turns it off) |
|
|
120
|
-
| `r` | Refresh now |
|
|
121
|
-
| `?` | Help |
|
|
122
|
-
| `q` | Quit |
|
|
123
|
-
|
|
124
|
-
When tracking is on, the tree panel's title shows `[LIVE ⠧]` and the status bar replaces `t: track` with `TRK ●`. Any explicit selection-changing key (`↑/k`, `↓/j`, `PgUp/PgDn`, `↵`, `h`, or `t` again) turns tracking off.
|
|
125
|
-
|
|
126
|
-
#### Activity viewer focus
|
|
127
|
-
|
|
128
|
-
| Key | Action |
|
|
129
|
-
|-----|--------|
|
|
130
|
-
| `↑` / `k` | Scroll one line up |
|
|
131
|
-
| `↓` / `j` | Scroll one line down |
|
|
132
|
-
| `PgUp` / `Ctrl+B` | Page up |
|
|
133
|
-
| `PgDn` / `Ctrl+F` | Page down |
|
|
134
|
-
| `Ctrl+U` / `Ctrl+D` | Half page up / down |
|
|
135
|
-
| `g` | Jump to top (oldest) |
|
|
136
|
-
| `G` | Jump to live (newest, bottom) |
|
|
137
|
-
| `↵` | Open detail view |
|
|
138
|
-
| `f` | Cycle filter preset |
|
|
139
|
-
| `r` | Refresh now |
|
|
140
|
-
| `Tab` | Switch focus to project tree |
|
|
141
|
-
| `?` | Help |
|
|
142
|
-
| `q` | Quit |
|
|
143
|
-
|
|
144
|
-
#### Detail view
|
|
145
|
-
|
|
146
|
-
| Key | Action |
|
|
147
|
-
|-----|--------|
|
|
148
|
-
| `↑` / `k` / `↓` / `j` | Scroll one line |
|
|
149
|
-
| `Ctrl+U` / `Ctrl+D` | Half page up / down |
|
|
150
|
-
| `↵` / `Esc` / `q` | Close |
|
|
151
|
-
|
|
152
|
-
Detail view colors the content based on activity type:
|
|
153
|
-
|
|
154
|
-
- **Git commit detail** (`git show --stat --patch`): added lines green (`+`), removed lines red (`-`), hunk headers cyan (`@@ ... @@`), `commit/Author/Date/diff` metadata dimmed.
|
|
155
|
-
- **Response / thinking / prompt**: text inside triple-backtick code fences renders in cyan so the boundary between prose and code is obvious. No language-specific syntax highlighting — just code-vs-prose separation.
|
|
156
|
-
|
|
157
|
-
### Behavior
|
|
158
|
-
|
|
159
|
-
- **Alternate screen buffer.** Watch mode uses the alt-screen (like `vim`, `htop`, `btop`), so quitting (`q`) restores the pre-launch shell completely. No TUI residue, no "is it still running?" confusion.
|
|
160
|
-
- **Minimum terminal size.** 80 cols × 20 rows. Smaller terminals show a one-line hint and redraw automatically when you resize.
|
|
161
|
-
- **Help overlay scrolls.** Press `?` for an in-app reference. The overlay scrolls (`j/k`, `PgUp/PgDn`, `Ctrl+B/F`, `Space`, `g/G`) so the full content is reachable on shorter terminals.
|
|
162
|
-
|
|
163
|
-
## Report
|
|
164
|
-
|
|
165
|
-
Print activity for a date in Markdown or JSON — suitable for piping to scripts or LLMs:
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
agenthud report # today (Markdown)
|
|
169
|
-
agenthud report --date 2026-05-14 # specific date
|
|
170
|
-
agenthud report --date today --include all # all activity types
|
|
171
|
-
agenthud report --format json # JSON output
|
|
172
|
-
agenthud report --detail-limit 0 # no truncation (full text)
|
|
173
|
-
agenthud report --with-git # merge git commits into timeline
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Output:
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
# AgentHUD Report: 2026-05-14
|
|
180
|
-
|
|
181
|
-
## myproject (10:23 – 14:45)
|
|
182
|
-
|
|
183
|
-
[10:23] $ npm test
|
|
184
|
-
[10:35] ~ src/ui/App.tsx
|
|
185
|
-
[11:15] < Added spinner hook to make the UI feel alive.
|
|
186
|
-
[14:30] ◆ abc1234 feat: fix auth callback
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Options:**
|
|
190
|
-
|
|
191
|
-
| Flag | Default | Description |
|
|
192
|
-
|------|---------|-------------|
|
|
193
|
-
| `--date` | today | `YYYY-MM-DD`, `today`, `yesterday`, or `-Nd` (N days ago, local date) |
|
|
194
|
-
| `--include` | `user,response,bash,edit,thinking` | Comma-separated types or `all` |
|
|
195
|
-
| `--format` | `markdown` | `markdown` or `json` |
|
|
196
|
-
| `--detail-limit` | `120` | Max chars per detail field; `0` = unlimited |
|
|
197
|
-
| `--with-git` | off | Merge git commits from each session's project into the timeline |
|
|
198
|
-
|
|
199
|
-
`--include` types: `response`, `bash`, `edit`, `thinking`, `read`, `glob`, `user`
|
|
200
|
-
|
|
201
|
-
## Summary
|
|
202
|
-
|
|
203
|
-
Generate an LLM-based summary of one day or a date range using the `claude` CLI:
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
# Single day
|
|
207
|
-
agenthud summary # today (always regenerated)
|
|
208
|
-
agenthud summary --date yesterday # natural-language date
|
|
209
|
-
agenthud summary --date 2026-05-14 # past date (cached on second run)
|
|
210
|
-
agenthud summary --date 2026-05-14 --force # ignore cache
|
|
211
|
-
agenthud summary --prompt "Only commits" # override prompt
|
|
212
|
-
|
|
213
|
-
# Date range — daily summaries are re-summarized into a meta-summary
|
|
214
|
-
agenthud summary --last 7d # last 7 days, ending today
|
|
215
|
-
agenthud summary --from 2026-05-10 --to 2026-05-16 # explicit range
|
|
216
|
-
agenthud summary --last 7d -y # skip per-day confirmations
|
|
217
|
-
|
|
218
|
-
# Tune what gets sent to the LLM — same option shape as `report`
|
|
219
|
-
agenthud summary --include all # include every activity type
|
|
220
|
-
agenthud summary --detail-limit 200 # truncate long tool outputs
|
|
221
|
-
agenthud summary --with-git # include git commits (default in config)
|
|
222
|
-
# Defaults are CLI flag → `summary:` config → `report:` config → built-in.
|
|
223
|
-
|
|
224
|
-
# Cheaper model — summarization doesn't need Opus-tier reasoning
|
|
225
|
-
agenthud summary --date today --model sonnet # ~40% cheaper than Opus
|
|
226
|
-
agenthud summary --last 7d --model haiku # ~80% cheaper, 200K context
|
|
227
|
-
|
|
228
|
-
# Open the resulting summary in your OS default app (browser, VS Code, etc.)
|
|
229
|
-
agenthud summary --last 7d --open # -o is the short form
|
|
230
|
-
|
|
231
|
-
# Open the index (~/.agenthud/summaries/index.md) instead — a hub
|
|
232
|
-
# listing every daily and range summary, grouped by year/month.
|
|
233
|
-
agenthud summary --open-index # -I is the short form
|
|
234
|
-
agenthud summary -oI # open today + the index
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
The index is auto-regenerated whenever a summary writes, and each summary file gets a one-line backlink footer at the top (`← all summaries · ← prev · next →`) so any markdown viewer is enough to navigate the whole corpus without leaving the file.
|
|
238
|
-
|
|
239
|
-
**Daily summaries** are saved to `~/.agenthud/summaries/YYYY-MM-DD.md`. Past dates are cached and returned instantly; today is always regenerated (activity still growing).
|
|
73
|
+
Sessions get colored badges — `[hot]` / `[warm]` / `[cool]` / `[cold]` — based on how recently their JSONL file was touched. Cold projects collapse under a `... N cold projects` sentinel. Press `↵` on any activity to open a scrollable detail view.
|
|
240
74
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Each missing daily prompts for confirmation just before generation, so you see concrete context (session/activity/commit counts and report size) before deciding. Pass `-y` / `--yes` to skip all prompts. Press Enter to accept the default (`[Y/n]`).
|
|
244
|
-
|
|
245
|
-
**Prompt customization:** The daily template lives at `~/.agenthud/summary-prompt.md` and the range template at `~/.agenthud/summary-range-prompt.md`. Both are auto-created from built-in templates on first run. Edit them freely.
|
|
246
|
-
|
|
247
|
-
**`--date` formats:** `YYYY-MM-DD`, `today`, `yesterday`, or `-Nd` (N days ago).
|
|
248
|
-
|
|
249
|
-
**Model selection:** Summarization is a low-reasoning task (structured input → structured markdown) — Sonnet or Haiku usually beats Opus on cost-per-summary with no quality loss. Pass `--model sonnet`, `--model haiku`, or a full model id (`--model claude-sonnet-4-6`). With no flag, `claude` uses its default model.
|
|
250
|
-
|
|
251
|
-
**Cost warning:** If the day's activity log is large (~300K tokens or more), AgentHUD prints a warning before sending and asks for one more confirmation in interactive mode. `-y` skips the prompt but still prints the warning.
|
|
252
|
-
|
|
253
|
-
**Requires:** [`@anthropic-ai/claude-code`](https://www.npmjs.com/package/@anthropic-ai/claude-code) installed and authenticated.
|
|
75
|
+
Full keybinding and badge reference: [FEATURES.md](./FEATURES.md#keybindings).
|
|
254
76
|
|
|
255
77
|
## Configuration
|
|
256
78
|
|
|
257
|
-
`~/.agenthud/config.yaml` is auto-created on first run with sensible defaults.
|
|
258
|
-
|
|
259
|
-
```yaml
|
|
260
|
-
# How often to poll for activity updates (Linux fallback when fs.watch isn't used)
|
|
261
|
-
refreshInterval: 2s
|
|
262
|
-
|
|
263
|
-
# Activity filter presets (cycle with 'f' key in viewer)
|
|
264
|
-
# Each list is one preset. Use "all" (or "*") to show everything.
|
|
265
|
-
# Types: response, user, bash, edit, thinking, read, glob, commit
|
|
266
|
-
filterPresets:
|
|
267
|
-
- ["all"]
|
|
268
|
-
- ["response", "user"]
|
|
269
|
-
- ["commit"]
|
|
270
|
-
|
|
271
|
-
# Defaults for `agenthud report` (CLI flags still win per-invocation).
|
|
272
|
-
report:
|
|
273
|
-
include: [user, response, bash, edit, thinking]
|
|
274
|
-
detailLimit: 120
|
|
275
|
-
withGit: false
|
|
276
|
-
format: markdown
|
|
277
|
-
|
|
278
|
-
# Defaults for `agenthud summary`. Any field omitted here is inherited
|
|
279
|
-
# from `report` above. `model` is summary-specific.
|
|
280
|
-
summary:
|
|
281
|
-
withGit: true
|
|
282
|
-
detailLimit: 0
|
|
283
|
-
# model: sonnet
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
`report` / `summary` resolve each option as **CLI flag → `summary:` key → `report:` key → built-in default**. The effective set is printed to stderr at the start of each run (e.g. `agenthud: report → include=[user,response,bash,edit,thinking] detail-limit=120 with-git=off format=markdown`), so the actual values are always visible.
|
|
287
|
-
|
|
288
|
-
App-managed state (hidden items) lives separately in `~/.agenthud/state.yaml` so your config file stays clean. You shouldn't need to edit it manually — use `h` in the TUI to hide things.
|
|
289
|
-
|
|
290
|
-
## Files
|
|
291
|
-
|
|
292
|
-
| Path | Purpose |
|
|
293
|
-
|------|---------|
|
|
294
|
-
| `~/.agenthud/config.yaml` | User settings (edit freely) |
|
|
295
|
-
| `~/.agenthud/state.yaml` | Hidden projects/sessions/sub-agents (app-managed) |
|
|
296
|
-
| `~/.agenthud/summary-prompt.md` | LLM prompt template for daily `summary` |
|
|
297
|
-
| `~/.agenthud/summary-range-prompt.md` | LLM prompt template for range `summary --last/--from/--to` |
|
|
298
|
-
| `~/.agenthud/summaries/YYYY-MM-DD.md` | Cached daily summaries |
|
|
299
|
-
| `~/.agenthud/summaries/range-FROM_TO.md` | Cached range summaries |
|
|
300
|
-
| `~/.agenthud/summaries/index.md` | Auto-regenerated hub linking every summary; each summary also gets a backlink footer at the top |
|
|
79
|
+
`~/.agenthud/config.yaml` is auto-created on first run with sensible defaults. CLI flags override config values per-invocation. Resolution order is `CLI flag → summary.<key> → report.<key> → built-in default`, and the effective values print to stderr at the start of every `report` / `summary` run.
|
|
301
80
|
|
|
302
|
-
|
|
81
|
+
App-managed UI state (hidden projects/sessions/sub-agents toggled by `h`) lives separately in `~/.agenthud/state.yaml`.
|
|
303
82
|
|
|
304
|
-
|
|
305
|
-
|----------|---------|-------------|
|
|
306
|
-
| `CLAUDE_PROJECTS_DIR` | `~/.claude/projects` | Path to Claude Code projects directory. Useful for backups or mounted volumes. |
|
|
83
|
+
Full schema, file paths, and env vars: [FEATURES.md → Config](./FEATURES.md#config).
|
|
307
84
|
|
|
308
|
-
##
|
|
85
|
+
## More
|
|
309
86
|
|
|
310
|
-
|
|
87
|
+
- **Reference:** [FEATURES.md](./FEATURES.md) — every flag, keybinding, config key, file path, env var
|
|
88
|
+
- **Release history:** [CHANGELOG.md](./CHANGELOG.md)
|
|
89
|
+
- **Deferred items:** [BACKLOG.md](./BACKLOG.md)
|
|
90
|
+
- **Issues / PRs:** [GitHub](https://github.com/neochoon/agenthud)
|
|
311
91
|
|
|
312
92
|
## License
|
|
313
93
|
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,8 @@ var DEFAULT_INCLUDE_TYPES = [
|
|
|
23
23
|
"response",
|
|
24
24
|
"bash",
|
|
25
25
|
"edit",
|
|
26
|
-
"thinking"
|
|
26
|
+
"thinking",
|
|
27
|
+
"task"
|
|
27
28
|
];
|
|
28
29
|
var ALLOWED_INCLUDE_TYPES = /* @__PURE__ */ new Set([
|
|
29
30
|
"user",
|
|
@@ -33,7 +34,8 @@ var ALLOWED_INCLUDE_TYPES = /* @__PURE__ */ new Set([
|
|
|
33
34
|
"thinking",
|
|
34
35
|
"read",
|
|
35
36
|
"glob",
|
|
36
|
-
"commit"
|
|
37
|
+
"commit",
|
|
38
|
+
"task"
|
|
37
39
|
]);
|
|
38
40
|
var DEFAULT_GLOBAL_CONFIG = {
|
|
39
41
|
refreshIntervalMs: 2e3,
|
|
@@ -944,11 +946,34 @@ function numberLines(content, start) {
|
|
|
944
946
|
const width = String(start + lines.length - 1).length;
|
|
945
947
|
return lines.map((line, i) => `${String(start + i).padStart(width)}: ${line}`).join("\n");
|
|
946
948
|
}
|
|
949
|
+
function formatBashBody(stdout, stderr, interrupted) {
|
|
950
|
+
const out = stdout?.replace(/\n+$/, "");
|
|
951
|
+
const err = stderr?.replace(/\n+$/, "");
|
|
952
|
+
const sections = [];
|
|
953
|
+
if (out) sections.push(out);
|
|
954
|
+
if (err) sections.push(out ? `--- stderr ---
|
|
955
|
+
${err}` : err);
|
|
956
|
+
if (interrupted) sections.push("[interrupted]");
|
|
957
|
+
if (sections.length === 0) return null;
|
|
958
|
+
return sections.join("\n\n");
|
|
959
|
+
}
|
|
947
960
|
function buildToolDetailBody(name, input, result) {
|
|
948
961
|
if (name === "Write") {
|
|
949
962
|
const content = result?.content ?? input?.content;
|
|
950
963
|
if (content) return { text: content, kind: "code" };
|
|
951
964
|
}
|
|
965
|
+
if (name === "Task") {
|
|
966
|
+
const content = result?.content;
|
|
967
|
+
if (content) return { text: content, kind: "code" };
|
|
968
|
+
}
|
|
969
|
+
if (name === "Bash") {
|
|
970
|
+
const text = formatBashBody(
|
|
971
|
+
result?.stdout,
|
|
972
|
+
result?.stderr,
|
|
973
|
+
result?.interrupted
|
|
974
|
+
);
|
|
975
|
+
if (text) return { text, kind: "code" };
|
|
976
|
+
}
|
|
952
977
|
if (name === "Read") {
|
|
953
978
|
const content = result?.file?.content;
|
|
954
979
|
if (content) {
|
|
@@ -1123,6 +1148,7 @@ function activityMatchesInclude(activity, include) {
|
|
|
1123
1148
|
return true;
|
|
1124
1149
|
if (include.includes("glob") && (label === "glob" || label === "grep"))
|
|
1125
1150
|
return true;
|
|
1151
|
+
if (include.includes("task") && label === "task") return true;
|
|
1126
1152
|
return false;
|
|
1127
1153
|
}
|
|
1128
1154
|
function isSameLocalDay(a, b) {
|
|
@@ -1137,6 +1163,14 @@ function formatActivity(activity, limit) {
|
|
|
1137
1163
|
const suffix = detail ? `: ${detail}` : "";
|
|
1138
1164
|
return `[${time}] ${activity.icon} ${activity.label}${suffix}`;
|
|
1139
1165
|
}
|
|
1166
|
+
function formatTaskBody(activity, limit) {
|
|
1167
|
+
if (activity.label !== "Task") return null;
|
|
1168
|
+
if (!activity.detailBody) return null;
|
|
1169
|
+
const body = truncateRaw(activity.detailBody, limit);
|
|
1170
|
+
return `<task-result>
|
|
1171
|
+
${body}
|
|
1172
|
+
</task-result>`;
|
|
1173
|
+
}
|
|
1140
1174
|
function formatDateString2(date) {
|
|
1141
1175
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
1142
1176
|
}
|
|
@@ -1239,6 +1273,8 @@ function generateReport(sessions, options2) {
|
|
|
1239
1273
|
lines.push("");
|
|
1240
1274
|
for (const activity of activities) {
|
|
1241
1275
|
lines.push(formatActivity(activity, detailLimit));
|
|
1276
|
+
const taskBody = formatTaskBody(activity, detailLimit);
|
|
1277
|
+
if (taskBody) lines.push(taskBody);
|
|
1242
1278
|
}
|
|
1243
1279
|
lines.push("");
|
|
1244
1280
|
}
|
|
@@ -2131,6 +2167,16 @@ function resolvePrompt(kind, override) {
|
|
|
2131
2167
|
return "Summarize the input below.";
|
|
2132
2168
|
}
|
|
2133
2169
|
}
|
|
2170
|
+
function buildRangeMetaInput(dailyMarkdowns) {
|
|
2171
|
+
if (dailyMarkdowns.length === 0) return "";
|
|
2172
|
+
return dailyMarkdowns.map(
|
|
2173
|
+
({ date, markdown }) => `<day date="${dateKey(date)}">
|
|
2174
|
+
|
|
2175
|
+
${markdown}
|
|
2176
|
+
|
|
2177
|
+
</day>`
|
|
2178
|
+
).join("\n\n");
|
|
2179
|
+
}
|
|
2134
2180
|
function shouldUseRangeCache(force, dates, today, cacheExists) {
|
|
2135
2181
|
if (force) return false;
|
|
2136
2182
|
if (!cacheExists) return false;
|
|
@@ -2217,7 +2263,12 @@ function spawnClaude(opts) {
|
|
|
2217
2263
|
resolve2({ code: 1, text: "", usage: null });
|
|
2218
2264
|
}
|
|
2219
2265
|
});
|
|
2266
|
+
let firstChunkFired = false;
|
|
2220
2267
|
const writeText = (text) => {
|
|
2268
|
+
if (!firstChunkFired) {
|
|
2269
|
+
firstChunkFired = true;
|
|
2270
|
+
opts.onFirstChunk?.();
|
|
2271
|
+
}
|
|
2221
2272
|
assembledText += text;
|
|
2222
2273
|
if (opts.streamToStdout) process.stdout.write(text);
|
|
2223
2274
|
cacheStream?.write(text);
|
|
@@ -2410,22 +2461,17 @@ async function generateDailySummary(opts) {
|
|
|
2410
2461
|
}
|
|
2411
2462
|
}
|
|
2412
2463
|
}
|
|
2413
|
-
const
|
|
2414
|
-
if (opts.announce && !showTicker) {
|
|
2415
|
-
process.stderr.write(
|
|
2416
|
-
`sending to claude (this may take a minute)...
|
|
2417
|
-
|
|
2418
|
-
`
|
|
2419
|
-
);
|
|
2420
|
-
}
|
|
2421
|
-
const stopTicker = showTicker ? startStderrTicker("sending to claude") : null;
|
|
2464
|
+
const stopTicker = opts.announce ? startStderrTicker("sending to claude") : null;
|
|
2422
2465
|
const prompt = resolvePrompt("daily", opts.promptOverride);
|
|
2423
2466
|
const result = await spawnClaude({
|
|
2424
2467
|
prompt,
|
|
2425
2468
|
stdin: reportMarkdown,
|
|
2426
2469
|
cachePath: cached2,
|
|
2427
2470
|
streamToStdout: opts.streamToStdout,
|
|
2428
|
-
model: opts.model
|
|
2471
|
+
model: opts.model,
|
|
2472
|
+
onFirstChunk: () => {
|
|
2473
|
+
if (stopTicker) stopTicker();
|
|
2474
|
+
}
|
|
2429
2475
|
});
|
|
2430
2476
|
if (stopTicker) stopTicker();
|
|
2431
2477
|
if (opts.announce && result.code === 0) {
|
|
@@ -2583,33 +2629,24 @@ async function runRangeSummary(options2) {
|
|
|
2583
2629
|
}
|
|
2584
2630
|
return 0;
|
|
2585
2631
|
}
|
|
2586
|
-
const metaInput = dailyMarkdowns
|
|
2587
|
-
|
|
2588
|
-
${markdown}`).join("\n\n---\n\n");
|
|
2632
|
+
const metaInput = buildRangeMetaInput(dailyMarkdowns);
|
|
2589
2633
|
process.stderr.write(
|
|
2590
2634
|
`
|
|
2591
2635
|
combining ${dailyMarkdowns.length} daily summaries into range summary...
|
|
2592
2636
|
`
|
|
2593
2637
|
);
|
|
2594
2638
|
const metaStreams = !options2.open;
|
|
2595
|
-
|
|
2596
|
-
} else {
|
|
2597
|
-
process.stderr.write(
|
|
2598
|
-
`sending to claude (this may take a minute)...
|
|
2599
|
-
|
|
2600
|
-
`
|
|
2601
|
-
);
|
|
2602
|
-
}
|
|
2603
|
-
const stopMetaTicker = metaStreams ? null : startStderrTicker("sending to claude");
|
|
2639
|
+
const stopMetaTicker = startStderrTicker("sending to claude");
|
|
2604
2640
|
const metaPrompt = resolvePrompt("range");
|
|
2605
2641
|
const metaResult = await spawnClaude({
|
|
2606
2642
|
prompt: metaPrompt,
|
|
2607
2643
|
stdin: metaInput,
|
|
2608
2644
|
cachePath: rangeCache,
|
|
2609
2645
|
streamToStdout: metaStreams,
|
|
2610
|
-
model: options2.model
|
|
2646
|
+
model: options2.model,
|
|
2647
|
+
onFirstChunk: () => stopMetaTicker()
|
|
2611
2648
|
});
|
|
2612
|
-
|
|
2649
|
+
stopMetaTicker();
|
|
2613
2650
|
if (metaResult.code !== 0) {
|
|
2614
2651
|
return metaResult.code;
|
|
2615
2652
|
}
|
|
@@ -3829,6 +3866,41 @@ function SessionTreePanel({
|
|
|
3829
3866
|
] });
|
|
3830
3867
|
}
|
|
3831
3868
|
|
|
3869
|
+
// src/ui/viewerCursor.ts
|
|
3870
|
+
function adjustViewerCursorOnNewActivities(args) {
|
|
3871
|
+
const {
|
|
3872
|
+
prevCursorLine,
|
|
3873
|
+
prevActivityCount,
|
|
3874
|
+
newActivityCount,
|
|
3875
|
+
isLive,
|
|
3876
|
+
viewerRows
|
|
3877
|
+
} = args;
|
|
3878
|
+
const noOp = {
|
|
3879
|
+
cursorLine: prevCursorLine,
|
|
3880
|
+
autoPause: false,
|
|
3881
|
+
scrollDelta: 0
|
|
3882
|
+
};
|
|
3883
|
+
if (!isLive) return noOp;
|
|
3884
|
+
if (prevCursorLine === 0)
|
|
3885
|
+
return { cursorLine: 0, autoPause: false, scrollDelta: 0 };
|
|
3886
|
+
if (newActivityCount <= prevActivityCount) return noOp;
|
|
3887
|
+
const delta = newActivityCount - prevActivityCount;
|
|
3888
|
+
const maxCursor = Math.max(0, viewerRows - 1);
|
|
3889
|
+
const upwardRoom = maxCursor - prevCursorLine;
|
|
3890
|
+
if (delta <= upwardRoom) {
|
|
3891
|
+
return {
|
|
3892
|
+
cursorLine: prevCursorLine + delta,
|
|
3893
|
+
autoPause: false,
|
|
3894
|
+
scrollDelta: 0
|
|
3895
|
+
};
|
|
3896
|
+
}
|
|
3897
|
+
return {
|
|
3898
|
+
cursorLine: maxCursor,
|
|
3899
|
+
autoPause: true,
|
|
3900
|
+
scrollDelta: delta - upwardRoom
|
|
3901
|
+
};
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3832
3904
|
// src/ui/App.tsx
|
|
3833
3905
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3834
3906
|
var VIEWER_HEIGHT_FRACTION = 0.55;
|
|
@@ -4063,12 +4135,12 @@ function App({
|
|
|
4063
4135
|
useEffect3(() => {
|
|
4064
4136
|
allFlatRef.current = allFlat;
|
|
4065
4137
|
}, [allFlat]);
|
|
4066
|
-
const activitiesLengthRef = useRef(0);
|
|
4067
4138
|
const activitiesRef = useRef(activities);
|
|
4068
4139
|
useEffect3(() => {
|
|
4069
|
-
activitiesLengthRef.current = activities.length;
|
|
4070
4140
|
activitiesRef.current = activities;
|
|
4071
4141
|
}, [activities]);
|
|
4142
|
+
const prevMergedCountRef = useRef(0);
|
|
4143
|
+
const prevPausedCountRef = useRef(0);
|
|
4072
4144
|
const lastLoadedFileRef = useRef(null);
|
|
4073
4145
|
useEffect3(() => {
|
|
4074
4146
|
let node = allFlatRef.current.find((s) => s.id === selectedId);
|
|
@@ -4163,13 +4235,8 @@ function App({
|
|
|
4163
4235
|
setSessionTree(tree);
|
|
4164
4236
|
if (!node || !node.filePath) return;
|
|
4165
4237
|
const newActivities = parseSessionHistory(node.filePath);
|
|
4166
|
-
const delta = newActivities.length - activitiesLengthRef.current;
|
|
4167
4238
|
setActivities(newActivities);
|
|
4168
|
-
|
|
4169
|
-
setScrollOffset((o) => o + delta);
|
|
4170
|
-
setNewCount((n) => n + delta);
|
|
4171
|
-
}
|
|
4172
|
-
}, [selectedId, isLive, expandedIds, tracking, discoverOptions]);
|
|
4239
|
+
}, [selectedId, expandedIds, tracking, discoverOptions]);
|
|
4173
4240
|
const refreshRef = useRef(refresh);
|
|
4174
4241
|
useEffect3(() => {
|
|
4175
4242
|
refreshRef.current = refresh;
|
|
@@ -4234,6 +4301,34 @@ function App({
|
|
|
4234
4301
|
const naturalTreeRows = allFlat.length;
|
|
4235
4302
|
const treeRows = Math.max(1, Math.min(naturalTreeRows, maxTreeRows));
|
|
4236
4303
|
const viewerRows = Math.max(5, height - 7 - treeRows);
|
|
4304
|
+
useEffect3(() => {
|
|
4305
|
+
const prev = prevMergedCountRef.current;
|
|
4306
|
+
prevMergedCountRef.current = mergedActivities.length;
|
|
4307
|
+
const result = adjustViewerCursorOnNewActivities({
|
|
4308
|
+
prevCursorLine: viewerCursorLine,
|
|
4309
|
+
prevActivityCount: prev,
|
|
4310
|
+
newActivityCount: mergedActivities.length,
|
|
4311
|
+
isLive,
|
|
4312
|
+
viewerRows
|
|
4313
|
+
});
|
|
4314
|
+
if (result.cursorLine !== viewerCursorLine) {
|
|
4315
|
+
setViewerCursorLine(result.cursorLine);
|
|
4316
|
+
}
|
|
4317
|
+
if (result.autoPause) {
|
|
4318
|
+
setIsLive(false);
|
|
4319
|
+
setScrollOffset((o) => o + result.scrollDelta);
|
|
4320
|
+
setNewCount((n) => n + result.scrollDelta);
|
|
4321
|
+
}
|
|
4322
|
+
}, [mergedActivities.length, isLive, viewerRows, viewerCursorLine]);
|
|
4323
|
+
useEffect3(() => {
|
|
4324
|
+
const prev = prevPausedCountRef.current;
|
|
4325
|
+
prevPausedCountRef.current = mergedActivities.length;
|
|
4326
|
+
if (!isLive && mergedActivities.length > prev) {
|
|
4327
|
+
const delta = mergedActivities.length - prev;
|
|
4328
|
+
setScrollOffset((o) => o + delta);
|
|
4329
|
+
setNewCount((n) => n + delta);
|
|
4330
|
+
}
|
|
4331
|
+
}, [mergedActivities.length, isLive]);
|
|
4237
4332
|
const spinner = useSpinner(isWatchMode, 150);
|
|
4238
4333
|
const tickActive = isWatchMode && isLive && !helpMode && !detailMode && activities.length > 0;
|
|
4239
4334
|
const liveTick = useTick(tickActive, 150);
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
The following is an activity log from a single day of Claude Code sessions
|
|
2
|
-
|
|
1
|
+
The following is an activity log from a single day of Claude Code sessions,
|
|
2
|
+
possibly spanning multiple projects.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Each entry: `[HH:MM] <icon> <label>: <detail>`. Icons:
|
|
5
|
+
○ Read ~ Edit/Write $ Bash
|
|
6
|
+
* Glob/Grep @ WebFetch » Sub-agent task
|
|
7
|
+
< Response > User message … Thinking
|
|
8
|
+
◆ Git commit
|
|
9
|
+
|
|
10
|
+
Write a concise, high-signal engineering retro in first-person voice
|
|
11
|
+
("I implemented X", "I discovered Y") — treat the developer as the author.
|
|
12
|
+
|
|
13
|
+
Target ~300-500 words total; individual sections may be 50-100 words.
|
|
14
|
+
Favor synthesis over completeness.
|
|
6
15
|
|
|
7
16
|
Focus on:
|
|
8
17
|
- meaningful accomplishments
|
|
@@ -10,28 +19,38 @@ Focus on:
|
|
|
10
19
|
- architectural decisions
|
|
11
20
|
- implementation progress
|
|
12
21
|
- important tradeoffs or realizations
|
|
13
|
-
- unfinished work and next steps
|
|
22
|
+
- unfinished work and explicit next steps
|
|
23
|
+
|
|
24
|
+
Do NOT:
|
|
25
|
+
- mechanically summarize every edit, response, or commit
|
|
26
|
+
- list every modified file (cite paths only for important changes)
|
|
27
|
+
- invent follow-ups not present in the log (no "consider refactoring X")
|
|
28
|
+
- include placeholders like "None" or "N/A" — omit empty sections entirely
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Omit any section that has no substantive content — do not emit placeholders like "None" or "N/A".
|
|
18
|
-
Prefer high-level synthesis grouped by theme or workstream.
|
|
30
|
+
If activity spans multiple projects, group findings by project where clarity
|
|
31
|
+
improves. If they share a theme, synthesize.
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
If the day was light (few entries, no meaningful work), output a single short
|
|
34
|
+
paragraph instead of the full template. Do not pad.
|
|
35
|
+
|
|
36
|
+
Format (omit any section without substance):
|
|
21
37
|
|
|
22
38
|
## Context
|
|
23
|
-
|
|
39
|
+
The primary goal(s) of the day. If multi-project, briefly note each.
|
|
24
40
|
|
|
25
41
|
## Key Accomplishments
|
|
26
42
|
Meaningful progress, completed implementations, resolved issues.
|
|
27
43
|
|
|
28
44
|
## Technical Insights
|
|
29
|
-
Important debugging findings, architectural decisions, tradeoffs,
|
|
45
|
+
Important debugging findings, architectural decisions, tradeoffs, discoveries.
|
|
30
46
|
|
|
31
47
|
## Major Code Changes
|
|
32
|
-
Significant codebase
|
|
48
|
+
Significant codebase changes grouped by theme. Reference commits inline
|
|
49
|
+
(e.g., `abc1234`).
|
|
33
50
|
|
|
34
51
|
## Open Questions / Next Steps
|
|
35
|
-
Only
|
|
52
|
+
Only items explicitly stated in the log or directly tied to unfinished workflows.
|
|
53
|
+
|
|
54
|
+
After reading the log below, write the summary.
|
|
36
55
|
|
|
37
56
|
Activity log:
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
The
|
|
2
|
-
|
|
1
|
+
The input contains daily engineering summaries from a date range.
|
|
2
|
+
|
|
3
|
+
Each day is wrapped in an XML tag with its date as an attribute:
|
|
4
|
+
|
|
5
|
+
<day date="YYYY-MM-DD">
|
|
6
|
+
(that day's summary — markdown using sections like
|
|
7
|
+
Context / Key Accomplishments / Technical Insights /
|
|
8
|
+
Major Code Changes / Open Questions)
|
|
9
|
+
</day>
|
|
10
|
+
|
|
11
|
+
Multiple `<day>` blocks follow in chronological order. Use the
|
|
12
|
+
`date` attribute as the authoritative date for that block — do not
|
|
13
|
+
infer dates from headings or text inside the block. Any `# YYYY-MM-DD`
|
|
14
|
+
heading or `---` line you see inside a `<day>` block is part of the
|
|
15
|
+
day's content, not a structural divider.
|
|
3
16
|
|
|
4
17
|
Write a range-level synthesis in English.
|
|
5
18
|
Aim for roughly 400-700 words total — the value of a range summary is cross-day pattern extraction, not re-summarization.
|
|
@@ -15,6 +28,8 @@ The point of a range summary is to surface what only becomes visible by looking
|
|
|
15
28
|
- outcomes that matter at the range level, not at any single day
|
|
16
29
|
- work that started in one day and continued, completed, or was abandoned later
|
|
17
30
|
|
|
31
|
+
When you reference a specific day, use its `date` attribute value verbatim (`2026-06-07`), not a relative term like "Monday" or "yesterday".
|
|
32
|
+
|
|
18
33
|
Use the following format:
|
|
19
34
|
|
|
20
35
|
## Range Overview
|
|
@@ -32,4 +47,4 @@ Themes that surfaced on more than one day: repeated debugging classes, recurring
|
|
|
32
47
|
## Carried-Over / Unfinished Work
|
|
33
48
|
Work still in progress at the end of the range, or open questions that persisted across multiple days.
|
|
34
49
|
|
|
35
|
-
Daily summaries:
|
|
50
|
+
Daily summaries follow:
|
package/package.json
CHANGED