@jean/noir
v0.2.0-alpha.0
Published
A local-first investigation tool for coding-agent sessions.
Maintainers
Readme
Agent Noir
A private eye for your coding agents.
Agent Noir is a local-first investigation tool for coding-agent sessions. It turns Claude Code and Codex histories into searchable cases with prompts, files, commands, errors, and handoff exports.
No cloud. No account. No alibi.
Status
v0.2.0-alpha — packaged for private alpha. Real Claude and Codex logs produce useful evidence (file activity, shell commands, errors, project paths) — not just raw events. Ships an experimental local log UI in addition to the CLI.
Try Agent Noir
Agent Noir runs with Bun.
bunx @jean/noir@alphaThe first run scans your local Claude Code and Codex histories, builds a local SQLite index, and prints next commands.
No cloud. No account. No alibi.
Requirements
- Bun >= 1.3
- macOS or Linux tested first
- Claude Code and/or Codex CLI histories on your machine for real data
Common commands
bunx @jean/noir@alpha # first-run scan + suggestions
bunx @jean/noir@alpha scan # scan default sources
bunx @jean/noir@alpha list # list indexed cases
bunx @jean/noir@alpha search "auth" # search indexed text
bunx @jean/noir@alpha show <case-id> # show timeline
bunx @jean/noir@alpha export <case-id> --format handoff
bunx @jean/noir@alpha stats # index counts
bunx @jean/noir@alpha doctor --json --redact # safe diagnostics
bunx @jean/noir@alpha serve # open the local case desk on :6647
bunx @jean/noir@alpha serve --scan --open # scan, serve, and open browserLocal Log UI (experimental)
Agent Noir ships an embedded local log inspector — a three-column case desk (sessions / trace / actions) for browsing the same SQLite index the CLI uses.
noir serve # start the UI on http://127.0.0.1:6647
noir serve --port 6647 # pick a custom port
noir serve --open # open the browser after starting
noir serve --scan # scan default sources before serving- The UI is experimental — feedback wanted.
- The server binds to
127.0.0.1by default and is not exposed to the network. - It reads from the local SQLite index (
~/.agent-noir/noir.sqliteorAGENT_NOIR_DB_PATH). It does not upload data and makes no external network calls. - If no index exists yet, the UI shows an empty state pointing you at
noir scan. First-run combo:noir serve --scan --open.
Privacy
Agent Noir is local-first.
- Source logs are read from your machine.
- The index is stored locally under
~/.agent-noir/noir.sqliteunless overridden viaAGENT_NOIR_DB_PATH. - No data is uploaded.
- No account is required.
Override the index location:
AGENT_NOIR_DB_PATH=.agent-noir/noir.sqlite bunx @jean/noir@alphaAlpha limitations
- Parser coverage is best-effort.
- Redaction is best-effort.
- Bun is required for now (
npxsupport is not in this slice). - The local log UI is experimental — layout, copy actions, and filters may change.
- MCP support is not included yet.
- Codex handoff quality may vary depending on session shape.
Development setup
git clone https://github.com/jeanlucaslima/noir
cd noir
bun install
bun run dev --helpFixture mode (no real logs needed)
bun run scan --path fixtures
bun run list
bun run search "auth"
bun run export <case-id> --format handoff
bun run export <case-id> --format md
bun run export <case-id> --format jsonReal-log mode
Run scan with no --path and Agent Noir reads from default locations:
~/.claude/projects/**/*.jsonl
~/.codex/sessions/**/rollout-*.jsonl
~/.codex/history.jsonlbun run scan # reads default sources
bun run list
bun run stats
bun run show <case-id>The SQLite index lives at ~/.agent-noir/noir.sqlite by default. Override with:
AGENT_NOIR_DB_PATH=.agent-noir/noir.sqlite bun run scan --path fixturesCommands
scan [--path <dir>]— index JSONL logs. Without--path, reads from default Claude/Codex locations.list— show recent cases.search <query>— search indexed text (FTS5 when available,LIKEfallback otherwise).show <case-id>— print the case timeline in the terminal.export <case-id> --format <handoff|md|json> [--out <path>] [--redact]— write a case file. Default is stdout.stats [--json]— counts by source, totals, raw events, parser warnings, DB path.doctor [--json] [--redact]— diagnostics for bug reports: OS, Bun version, DB path, detected source paths, file counts, indexed counts, parser warning summary.serve [--port <n>] [--host <h>] [--open] [--scan]— start the local log UI on127.0.0.1:6647(default). Reads from the local SQLite index; no external network calls.
Vocabulary
| Concept | Agent Noir term | | --------------------- | --------------- | | Session | Case | | Timeline | Trail | | Files/commands/errors | Evidence | | Export | Case file | | Continuation context | Handoff |
Privacy
Everything runs locally. The DB is a single SQLite file under ~/.agent-noir/. No telemetry, no accounts, no network calls.
The redactor is best-effort — review exports before sharing.
Bug reports
Run:
bun run doctor --json --redactPaste the output. It includes OS, Bun version, DB path, source-path probes, and parser warning counts. The --redact flag masks obvious secrets and replaces your home directory with <HOME> so the report doesn't leak your username or local paths. JSON form is preferred — it stays valid after redaction.
Do not paste raw Claude/Codex logs, private JSONL histories, local SQLite databases, or unreviewed exports into public issues. Share redacted doctor output first, and only attach small sanitized excerpts when needed.
Alpha parser limitations
Real Claude and Codex log shapes evolve. v0.1.3 normalizes the common semantic events into evidence, classifies known-but-not-yet-normalized events, and preserves anything else as raw events. Real-log parsing is best-effort.
Claude
Claude Code writes nested events: type: "user" | "assistant" rows carry a message.content[] array of text / thinking / tool_use / tool_result items.
- Normalized into evidence:
tool_useitems with namesRead/Glob/Grep→file_readtool_useitems with namesEdit/Write/MultiEdit/NotebookEdit→file_edittool_useitems with nameBash→shell_commandtool_resultitems withis_error: true→error(plustool_result)textitems in user/assistant content →user_message/assistant_message- Other tool names (
TaskCreate,Agent,mcp__*,Grep, etc.) →tool_call cwdfield captured intocase.projectPath
- Known-ignored top-level events:
custom-title,ai-title,agent-name,permission-mode,file-history-snapshot,attachment,system,progress,last-prompt,queue-operation.custom-title(withai-titlefallback) setscase.title. - Known-ignored content items:
thinkingblocks.
Codex
Codex CLI writes wrapped events: {type, payload, timestamp} where top-level type is session_meta / turn_context / event_msg / response_item.
- Normalized into evidence:
event_msg.exec_command_end→shell_command(+errorif non-zero exit or stderr)event_msg.patch_apply_end→file_editper changed path (+errorifsuccess: false)event_msg.user_message/event_msg.agent_message→user_message/assistant_messageevent_msg.error/event_msg.web_search_end→error/tool_resultresponse_item.message→user_message/assistant_messageby roleresponse_item.function_call→tool_call(intent only; the real shell/edit evidence comes from the matching*_endevent so each command/edit is counted once)response_item.function_call_output→tool_resultsession_meta.cwd/turn_context.cwd→case.projectPath~/.codex/history.jsonlflat entries ({session_id, text, ts}) →user_message
- Known-ignored event_msg subtypes:
agent_reasoning,agent_reasoning_section_break,token_count,task_started,task_complete,turn_aborted,stream_error. - Known-ignored response_item subtypes:
reasoning,custom_tool_call,custom_tool_call_output,web_search_call,ghost_snapshot.
Catch-all
Anything else is preserved as a raw event and counted under unknown_event. Malformed JSONL lines are skipped and recorded as malformed_json.
Parser diagnostics kinds
| Kind | Meaning |
| --- | --- |
| malformed_json | Line is not valid JSON. Skipped. |
| read_error | The file itself could not be read. |
| unknown_event | Event shape not recognized by the parser. Preserved as raw. Worth investigating. |
| known_ignored | Event shape recognized but intentionally not normalized yet. Preserved as raw. Lower-priority for parser work. |
Use bun run stats to see how many raw events and parser warnings the index holds, broken down by kind. bun run doctor shows the same breakdown alongside source-path probes.
Known limitations
- Real-log parsing is best-effort; event coverage will expand as shapes are observed.
- Redaction is best-effort.
- The local log UI is experimental.
- No MCP server yet.
- No AI summaries yet.
- No project metadata extraction (
project_name/project_pathare not yet inferred from real logs). - Cursor / Aider / Goose are not yet supported.
Repo layout
src/
cli/index.ts
core/
types.ts
parser/ jsonl + normalization helpers
sources/ discover, defaults, claude, codex
db/ schema + bun:sqlite wrapper
indexer/ scan
search/ search
export/ handoff, markdown, json
redact/ redact
server/ Bun-served local log UI + JSON API
fixtures/
claude/ codex/ broken/