feat(tools): Trajectory — query the session transcript as structured data - #1867
Open
0bsolescence wants to merge 3 commits into
Open
feat(tools): Trajectory — query the session transcript as structured data#18670bsolescence wants to merge 3 commits into
0bsolescence wants to merge 3 commits into
Conversation
added 3 commits
August 15, 2026 11:43
Claude Code already writes the full trajectory of every session to disk as JSONL under ~/.claude/projects, but nothing in LifeOS reads it as a record. Answering "when did we last touch this file", "which tools keep failing" or "where did that decision get made" meant hand-rolled jq against a schema nobody had written down. The evidence was there; the query surface was not. Trajectory.ts adds four read-only commands over that record: - sessions: uuid, first/last timestamp, cwd(s), git branch, message and tool counts, plus Claude Code's own ai-title line, which turns out to be the single most useful column for finding a session again. - grep: a regex over user text, assistant text, thinking blocks, tool inputs and tool results, filterable by --role, --session and --since. Tool hits are attributed back to the tool name by resolving tool_use_id, so a match inside a result names the call that produced it. - tools: call frequency with a failure count. Failures live on the user-side tool_result as is_error, which only identifies the call by id, so the id map is built first and errors are attributed through it. - file: every read/write/edit of a path, with session and timestamp. Design notes, both places where being wrong would have put fiction in an evidence tool: - Bash is deliberately excluded from 'file'. A shell command that touches a file is not reliably distinguishable from one that merely mentions a path, and guessing would manufacture history. 'grep' covers shell archaeology. - --since reads a bare 2026-08-13 as LOCAL midnight. new Date() on a bare date yields UTC midnight, which on this UTC-7 host silently pulled in the previous evening's sessions — caught against real transcripts, where a session ending 19:33 local appeared under --since for the following day. It also accepts 48h / 7d, matching WorkSweep's existing --since. Every unknown line type is skipped rather than fatal: the format carries at least fifteen type values across versions, most of them bookkeeping, and a malformed or truncated line is a skip, never a crash. Empty results exit 0; only usage and read errors exit 2. --json emits [] rather than prose so it stays machine-readable when nothing matched. Reads nothing but *.jsonl one level under the transcript root, makes no network calls, and writes nothing. 63 tests, all against fixtures synthesised in a temp dir from the observed line shapes — no real transcript is read by the suite, so it carries no personal data and runs on a machine that has never held a session.
Cross-vendor audit (codex review) flagged that statSync follows symlinks, so a link beneath the configured root could point the walk at an arbitrary tree and defeat the confinement this tool advertises. Switch to lstatSync for both the project dir and the .jsonl entry; a symlink resolving outside <root>/<project> is now rejected. Two regression tests cover the file and directory cases.
…dates Two audit-flagged quality nits: - excerpt() re-ran the regex on flattened text, which missed when the pattern matched whitespace, defaulting to index 0 and showing a window without the match. Window on the original text around the true match, flatten for display. - parseSince() accepted out-of-range days (2026-02-31 → March 3) because the Date constructor normalizes rather than returning NaN. Round-trip the y/m/d components and reject on drift. Both covered by regression tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
LIFEOS/TOOLS/Trajectory.ts: a read-only CLI that treats Claude Code session transcripts (JSONL) as queryable data — filter by session, time window, tool, or text; emit structured summaries instead of grepping raw JSONL. Useful for post-session review, hook debugging, and building on-disk observability without a server.Standalone: one new tool file plus its test, no changes to existing files.
Testing
bun test test/Trajectory.test.ts— 63 pass, 0 fail, 153 assertions, run against currentmain.Developed with AI assistance (Claude); reviewed and tested by me.