https://github.com/mgacy/cc-session-tool
A CLI to allow agents to query Claude Code session transcripts.
https://github.com/mgacy/cc-session-tool
ai-tools claude-code cli
Last synced: 6 days ago
JSON representation
A CLI to allow agents to query Claude Code session transcripts.
- Host: GitHub
- URL: https://github.com/mgacy/cc-session-tool
- Owner: mgacy
- Created: 2026-03-24T19:13:49.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-25T04:55:47.000Z (2 months ago)
- Last Synced: 2026-04-25T05:27:10.859Z (2 months ago)
- Topics: ai-tools, claude-code, cli
- Language: TypeScript
- Homepage:
- Size: 108 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# cc-session-tool
A CLI tool for querying Claude Code session transcripts. Reads JSONL session files from `~/.claude/projects/` and compresses raw transcripts (50-120K tokens each) into focused JSON responses — designed for use by coding agents and scripts.
## Installation
```bash
bun install
```
## Usage
```bash
bun run dev [session] [options]
```
Or compile to a standalone binary:
```bash
bun run build
./cc-session-tool [session] [options]
```
### Session Identifiers
Session-scoped commands require a `` argument; `list` and `projects` do not. Three forms are accepted:
| Form | Example | Resolution |
| ----------- | -------------------------------------- | ------------------------------------------------------ |
| Full UUID | `DA2738E3-0ADE-40E7-B6AC-450F9CCE1B43` | Exact filename match |
| UUID prefix | `DA2738E3` | Prefix match (must be unambiguous) |
| Slug | `snuggly-floating-barto` | Search across all `.jsonl` files (must be unambiguous) |
All session commands accept `--project ` to specify the project directory (defaults to CWD). They also accept `--claude-project ` when you need to target a raw Claude project directory basename returned by `search`, `list`, or `projects`. `--project` and `--claude-project` are mutually exclusive.
Use `--claude-project` for stable follow-up from cross-project or worktree search results:
```bash
cc-session-tool shape DA2738E3 --claude-project -workspace-app
```
**Subagent targeting:** Append `:` to a session identifier in any session-scoped command (except `subagents`) to target a subagent session (e.g., `DA2738E3:a8361bc` or `snuggly-floating-barto:a8361bc`). The parent session is resolved normally, then the subagent file is located at `/subagents/agent-.jsonl`. Use the `subagents` command to discover available agent IDs.
**Input validation:** Session IDs accept `[a-zA-Z0-9-]` characters; agent IDs also allow underscores `[a-zA-Z0-9_-]`. All other inputs are rejected with exit code 2.
### Turn Numbering
All commands use consistent turn numbering:
- Each JSONL entry of type `user` or `assistant` gets the next sequential integer, starting at 1.
- Other entry types (`system`, `progress`, etc.) are excluded from numbering.
- When an assistant entry contains multiple content blocks (e.g., thinking + tool_use), they share the same turn number. The `block_index` field (0-indexed) disambiguates blocks within a turn.
- Turn numbers are consistent across all commands: turn 5 in `shape` is the same entry as turn 5 in `tools`, `tokens`, `messages`, `slice`, and `files`.
## Commands
### `list`
Index sessions by reading metadata from the first few lines of each file. A scoped `list --project ` includes the repo's associated Claude-managed worktree transcript directories by default, matching `search`; use `--main-only` to inspect only the main project transcript directory.
```bash
cc-session-tool list [--project ] [--main-only] [--all-projects] [--project-glob ] [--intent-match |--intent-regex ] [--branch ] [--after ] [--before ] [--since ] [--last ] [--min-lines ] [--include-subagents]
```
| Option | Default | Description |
| ------------- | ------- | -------------------------------------------------------------- |
| `--branch` | all | Filter by git branch name |
| `--all-projects` | false | List sessions from every top-level Claude project directory under `~/.claude/projects`. |
| `--main-only` | false | With scoped `--project`, skip associated Claude worktree transcript directories. Cannot be combined with `--all-projects`. |
| `--project-glob` | — | With `--all-projects`, filter by raw Claude project basename or display-only `project_path_guess`. Supports `*` and `?`. |
| `--intent-match` | — | Filter session intent sources (`slug`, first prompt token, first user message) by case-insensitive substring. |
| `--intent-regex` | — | Filter session intent sources by regex. Mutually exclusive with `--intent-match`. |
| `--after` | — | Sessions after ISO 8601 date (mutually exclusive with `--since`) |
| `--before` | — | Sessions before ISO 8601 date |
| `--since` | — | Sessions from the last duration: `30m`, `2h`, `1d`, `1w` (mutually exclusive with `--after`) |
| `--last` | all | Return only the N most recent sessions |
| `--min-lines` | 0 | Sessions with at least N lines |
| `--include-subagents` | false | Include `subagent_count` per session |
**Output:** Sessions sorted by timestamp (newest first). When `--include-subagents` is set, each session includes a `subagent_count` field; otherwise the field is omitted entirely. Fields may be `null` for sessions with missing metadata. When `--last` is used, `_meta.total` reflects the pre-limit count and `_meta.hasMore` is `true` if results were truncated. When multiple project contexts are included, rows include `project`, `project_path_guess`, `project_role`, and `session_ref`; `_meta.included_projects` records the selected Claude project contexts. `project_path_guess` is display-only; use raw `project` or `session_ref.project` with `--claude-project` for follow-up commands.
```json
{
"ok": true,
"data": [
{
"session_id": "DA2738E3-0ADE-40E7-B6AC-450F9CCE1B43",
"branch": "feature/auth",
"timestamp": "2026-03-07T22:31:26.359Z",
"version": "2.1.71",
"model": "claude-sonnet-4-5",
"lines": 342,
"slug": "snuggly-floating-barto"
}
],
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
---
### `projects`
Summarize Claude project transcript directories and expose stable raw project handles.
```bash
cc-session-tool projects [--glob ] [--project ]
```
Without `--project`, `projects` scans all top-level Claude project directories under `~/.claude/projects`. With `--project`, it returns the main project plus associated Claude worktree transcript directories. Rows are sorted by raw `project` basename.
| Field | Description |
| ----- | ----------- |
| `project` | Raw Claude project directory basename; pass this to session commands as `--claude-project`. |
| `project_path_guess` | Best-effort display path only. Do not use it as a stable handle. |
| `project_role` | `main`, `worktree`, or `global`. |
| `session_count` | Count of parent `.jsonl` sessions in that Claude project directory. |
| `total_lines` | Total non-empty transcript lines across counted sessions. |
| `first_session_at` / `last_session_at` | Earliest/latest metadata timestamp, or `null`. |
---
### `shape `
Turn-by-turn skeleton with summary stats. Compresses a full session into a table of contents.
```bash
cc-session-tool shape [--project | --claude-project ]
```
**Output:**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"turns": [
{ "n": 1, "role": "user", "type": "user", "block_index": 0 },
{ "n": 2, "role": "assistant", "type": "tool_use", "block_index": 0, "tools": ["Grep"] },
{ "n": 3, "role": "user", "type": "tool_result", "block_index": 0 },
{ "n": 4, "role": "assistant", "type": "thinking", "block_index": 0 },
{ "n": 4, "role": "assistant", "type": "text", "block_index": 1 }
],
"summary": {
"total_turns": 42,
"user_messages": 8,
"tool_calls": { "Grep": 12, "Read": 8, "Edit": 5, "Bash": 4 },
"first_edit_turn": 15,
"duration_minutes": 38.2
}
},
"_meta": { "total": 5, "returned": 5, "hasMore": false }
}
```
**Turn types:**
| Role | Type | Description |
| ----------- | ------------- | ---------------------------------------- |
| `user` | `user` | User text message |
| `user` | `tool_result` | Tool result returned to assistant |
| `user` | `image` | Image attachment |
| `assistant` | `text` | Assistant text response |
| `assistant` | `thinking` | Thinking/reasoning block |
| `assistant` | `tool_use` | Tool invocation (includes `tools` array) |
**`block_index`:** 0-indexed position within the turn's content blocks. When an assistant turn has multiple non-tool blocks (e.g., thinking + text), each gets a separate row with incrementing `block_index`. Assistant turns with `tool_use` blocks are collapsed into a single row with `block_index: 0`.
**Summary fields:**
| Field | Description |
| ------------------ | ------------------------------------------------------------------- |
| `total_turns` | Count of user + assistant entries |
| `user_messages` | Count of user entries |
| `tool_calls` | Map of tool name to call count |
| `first_edit_turn` | Turn of first `Edit` or `Write` call (`null` if none) |
| `duration_minutes` | Minutes between first and last entry (`null` if missing timestamps) |
---
### `summary `
One-call triage summary for a session.
```bash
cc-session-tool summary [--project | --claude-project ]
```
The result combines the common first-pass fields agents previously gathered from `shape`, `tools`, `files`, and `messages`: model, timing, first prompt snippet, last assistant text snippet, tool counts, file access counts, and subagent metadata. When following a `search` or `list` row, use `session_ref.project` as `--claude-project`; do not use `project_path_guess` as a handle.
---
### `tools `
Tool call log with condensed input summaries and outcome detection.
```bash
cc-session-tool tools [--project | --claude-project ] [--name |--name-match ] [--input-match |--input-regex ] [--failed] [--turn ]
```
`--input-match` and `--input-regex` search the raw structured tool input, including values omitted or truncated in `input_summary`. `--name` and `--name-match` are mutually exclusive, as are the two input filters.
**Output:**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"tool_calls": [
{
"turn": 3,
"tool": "Grep",
"input_summary": "pattern='AuthReducer' path='Sources/'",
"outcome": "empty",
"duration_ms": 450
},
{
"turn": 7,
"tool": "Read",
"input_summary": "file='AuthReducer.swift'",
"outcome": "success (245 lines)",
"duration_ms": 120
}
]
},
"_meta": { "total": 2, "returned": 2, "hasMore": false }
}
```
**Input summary formats:**
| Tool | Summary Format |
| -------------- | ----------------------------------------------- |
| `Grep` | `pattern='...' path='...'` |
| `Read` | `file='' offset=N limit=N` |
| `Edit` | `file='' old=(N chars) new=(N chars)` |
| `Write` | `file='' (N chars)` |
| `Bash` | First 80 chars of command |
| `Glob` | `pattern='...' path='...'` |
| `Agent`/`Task` | `prompt='...'` (first 80 chars) |
| `WebFetch` | `url='...'` |
| `WebSearch` | `query='...'` |
| Other | First 80 chars of JSON-encoded input |
**Outcome values:**
| Outcome | Meaning |
| ------------------- | ----------------------------------------------------- |
| `success` | Non-empty content |
| `success (N lines)` | N lines of content |
| `empty` | Empty content |
| `error: ` | Error (first 60 chars) |
| `no_result` | No matching `tool_result` found (interrupted session) |
**`duration_ms`:** Milliseconds between assistant entry and corresponding tool_result. `null` if timestamps are missing.
---
### `files `
Files touched in a session, grouped by file path or chronologically by turn.
```bash
cc-session-tool files [--project | --claude-project ] [--group-by ] [--turn ] [--operation ]
```
| Option | Default | Description |
| ------------- | ------- | -------------------------------------------------- |
| `--group-by` | `file` | `file` (one entry per unique path) or `turn` (chronological) |
| `--turn` | all | Turn number `N` or range `N-M` |
| `--operation` | all | Filter by operation: `read`, `edit`, `write`, `grep`, `glob` |
**Output (group-by=file, default):**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"group_by": "file",
"files": [
{
"path": "/workspace/project/src/auth.ts",
"operations": ["read", "edit"],
"turns": [6, 8, 12, 18],
"errored": false
},
{
"path": "/workspace/project/src/auth.test.ts",
"operations": ["read", "write"],
"turns": [10, 22],
"errored": false
}
]
},
"_meta": { "total": 2, "returned": 2, "hasMore": false }
}
```
**Output (group-by=turn):**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"group_by": "turn",
"accesses": [
{ "path": "/workspace/project/src/auth.ts", "operation": "read", "turn": 6, "errored": false },
{ "path": "/workspace/project/src/auth.ts", "operation": "edit", "turn": 8, "errored": true }
]
},
"_meta": { "total": 2, "returned": 2, "hasMore": false }
}
```
**Notes:**
- Extracts full file paths from tool inputs (`Read`, `Edit`, `Write` use `file_path`; `Grep`, `Glob` use `path`).
- `Grep`/`Glob` calls without a `path` argument are excluded (they search CWD implicitly).
- `Bash` calls are excluded — file references in shell commands are not parsed.
- Files are sorted by first turn accessed. `errored` is `true` if any operation on that file errored.
---
### `tokens `
Per-turn token usage from assistant entries.
```bash
cc-session-tool tokens [--project | --claude-project ] [--cumulative]
```
**Output:**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"turns": [
{
"n": 2,
"input": 20170,
"output": 11,
"cache_read": 0,
"cache_create": 20170
},
{
"n": 4,
"input": 5995,
"output": 380,
"cache_read": 20170,
"cache_create": 5995
}
],
"totals": {
"input": 142000,
"output": 38000,
"cache_read": 890000,
"cache_create": 45000
}
},
"_meta": { "total": 2, "returned": 2, "hasMore": false }
}
```
**Notes:**
- Only assistant entries appear in `turns` (they carry `message.usage`). User turns are counted but not listed.
- `totals` is always non-cumulative, even with `--cumulative`.
- With `--cumulative`, each field in `turns` is a running total.
**Token fields:**
| Field | Source |
| -------------- | ------------------------------------------- |
| `input` | `message.usage.input_tokens` |
| `output` | `message.usage.output_tokens` |
| `cache_read` | `message.usage.cache_read_input_tokens` |
| `cache_create` | `message.usage.cache_creation_input_tokens` |
---
### `messages `
Filtered, truncated message content for drilling into specific parts of a session.
```bash
cc-session-tool messages [--project | --claude-project ] [--role ] [--type ] [--turn ] [--max-content ]
```
| Option | Default | Description |
| --------------- | ------- | ------------------------------------------------------------------- |
| `--role` | all | Filter by `user` or `assistant` |
| `--type` | all | Filter by block type: `text`, `thinking`, `tool_use`, `tool_result` |
| `--turn` | all | Turn number `N` or range `N-M` |
| `--max-content` | 200 | Truncate text/thinking content to N chars |
**Output:**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"agent_id": null,
"messages": [
{
"n": 1,
"role": "user",
"content": [{ "type": "text", "text": "Implement the feature..." }]
},
{
"n": 2,
"role": "assistant",
"content": [
{ "type": "thinking", "text": "I need to...[truncated, 4832 chars]" },
{ "type": "tool_use", "name": "Grep", "id": "toolu_abc123" }
]
}
]
},
"_meta": { "total": 2, "returned": 2, "hasMore": false }
}
```
**Notes:**
- Content is truncated with `...[truncated, N chars]` suffix.
- `tool_use` blocks preserve `name` and `id` (not truncated).
- `tool_result` blocks preserve `tool_use_id` and `is_error`, with truncated content.
---
### `slice `
Raw entries for a turn range. The escape hatch for getting full content of specific turns.
```bash
cc-session-tool slice --turn [--project | --claude-project ] [--max-content ]
```
| Option | Description |
| --------------- | ----------------------------------------------------- |
| `--turn` | Turn range (**required**) |
| `--max-content` | Truncate text/thinking/tool_result content to N chars |
**Notes:**
- Without `--max-content`, entries are output as-is (full content preserved).
- Output is wrapped in the standard `{ ok, data, _meta }` envelope where `data` is a structured object with `session_id`, `agent_id`, and `entries` (array of raw session entries). When targeting a subagent via colon notation, `agent_id` contains the agent ID; otherwise it is `null`.
---
### `search`
Find sessions matching structured filters. By default, search is scoped to the logical project derived from `--project ` or the current working directory, and it also includes associated Claude-managed worktree transcript directories. This agent-first default means a query from the main checkout can find work performed from a Claude worktree without `--all-projects`.
```bash
cc-session-tool search [--project ] [--all-projects] [--project-glob ] [--tool ] [--input-match |--input-regex ] [--file ] [--text |--text-regex ] [--intent-match |--intent-regex ] [--bash |--bash-regex ] [--no-subagents] [--operation ] [--origin] [--sort ] [--aggregate count-per-session|counters] [--counter NAME=text] [--counter-regex NAME=regex] [--bucket day|week] [--per-subagent] [--branch ] [--after ] [--before ] [--since ] [--last ]
```
| Option | Default | Description |
| ---------------- | ------- | ----------- |
| `--project` | CWD | Logical project path for scoped search. Scoped searches include associated Claude worktrees by default. With `--all-projects`, this becomes a file-identity query anchor, not a scan limit. |
| `--all-projects` | false | Search every top-level Claude project directory under `~/.claude/projects` for broad audits. Without an explicit `--project`, absolute file queries do not infer a logical project anchor from CWD or `project_path_guess`. |
| `--project-glob` | — | With `--all-projects`, filter by raw Claude project basename or display-only `project_path_guess`. Supports `*` and `?`. |
| `--tool` | — | Match tool names by case-insensitive substring. |
| `--input-match` | — | Match raw structured tool input by case-insensitive substring, including fields omitted or truncated by `input_summary`. |
| `--input-regex` | — | Match raw structured tool input by regex. Mutually exclusive with `--input-match`. |
| `--file` | — | Match touched file paths by substring. |
| `--operation` | all | With `--file`, filter by matching file operation: `read`, `edit`, `write`, `grep`, `glob`. |
| `--origin` | false | With `--file`, return earliest matching transcript write evidence. Implies `--operation write` and defaults to one result. |
| `--sort` | `session-newest` | Sort matches by `session-newest`, `match-earliest`, `match-newest`, or `project`. |
| `--aggregate` | `none` | `count-per-session` returns per-session tool-input counts; `counters` returns named raw-input counters. |
| `--counter` / `--counter-regex` | — | Named raw-input counter as `NAME=pattern`; requires `--aggregate counters`. |
| `--bucket` | `none` | With `--aggregate counters`, bucket by `day` or `week`. Missing timestamps use `unknown`. |
| `--per-subagent` | false | With counter aggregation, emit transcript-level rows instead of parent session rollups. |
| `--text` | — | Match user/assistant text and thinking by case-insensitive substring. |
| `--text-regex` | — | Match user/assistant text and thinking by regex. |
| `--intent-match` | — | Match session intent sources (`slug`, first prompt token, first user message) by substring. |
| `--intent-regex` | — | Match session intent sources by regex. |
| `--bash` | — | Match Bash command inputs by case-insensitive substring. |
| `--bash-regex` | — | Match Bash command inputs by regex. |
| `--no-subagents` | false | Disable default one-level subagent transcript inclusion. |
| `--branch` | all | Filter by git branch name. |
| `--after` | — | Sessions after ISO 8601 date (mutually exclusive with `--since`). |
| `--before` | — | Sessions before ISO 8601 date. |
| `--since` | — | Sessions from the last duration: `30m`, `2h`, `1d`, `1w` (mutually exclusive with `--after`). |
| `--last` | all | Return only the N matches after sorting. With `--origin`, defaults to 1 unless set. |
At least one selector is required: `--tool`, raw-input, file, text, intent, bash, or counter filter. You may provide more than one; multiple filters use AND semantics. `--operation` is valid only with `--file`, and it is tied to the same matching file access, not any other file access in the session.
For absolute `--file` queries inside the logical project, search compares exact canonical path candidates first, then exact project-relative logical identity. For example, `/workspace/app/src/auth.ts` in the main checkout matches `/workspace/app/.claude/worktrees/feature-a/src/auth.ts` from a related Claude worktree as the same logical file `src/auth.ts`. If a worktree path reaches the same file through a symlinked directory, the realpath candidate can also match even when the lexical absolute paths differ. If both sides normalize to logical paths and they differ, search does not fall back to substring matching.
**Examples:**
```bash
# Search the current project for sessions that touched auth.ts
cc-session-tool search --file auth.ts
# From the main checkout, find worktree sessions that wrote the same logical file
cc-session-tool search --file /workspace/app/src/auth.ts --operation write
# Find the earliest transcript write for a file
cc-session-tool search --file /workspace/app/src/auth.ts --origin
# Search all selected Claude project identities for recent Bash usage
cc-session-tool search --all-projects --project-glob "*app*" --bash "bun test" --since 1d --last 5
# Broad audit with the main checkout used only as a file-identity anchor
cc-session-tool search --all-projects --project /workspace/app --file /workspace/app/src/auth.ts --operation write
# Audit raw Bash inputs, including long commands beyond the condensed summary
cc-session-tool search --tool Bash --input-match "bun test" --aggregate count-per-session
```
**Output:**
```json
{
"ok": true,
"data": [
{
"session_id": "DA2738E3-...",
"branch": "feature/auth",
"timestamp": "2026-03-07T22:31:26.359Z",
"slug": "snuggly-floating-barto",
"matches": {
"tools": ["Read", "Edit"],
"files": ["/workspace/app/src/auth.ts"],
"normalized_files": ["src/auth.ts"],
"evidence": [
{
"kind": "file",
"rawPath": "/workspace/app/.claude/worktrees/feature-a/src/auth.ts",
"logicalPath": "src/auth.ts",
"operation": "edit",
"turn": 8,
"block_index": 0,
"timestamp": "2026-03-07T22:34:10.000Z"
}
],
"operations": ["read", "edit"],
"turns": [6, 8]
}
}
],
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
With `--aggregate count-per-session`, `data` is a per-session summary instead of full match rows:
```json
{
"ok": true,
"data": [
{
"session_id": "DA2738E3-...",
"branch": "feature/auth",
"timestamp": "2026-03-07T22:31:26.359Z",
"slug": "snuggly-floating-barto",
"counts": { "tool_inputs": 3 },
"sample_matches": [
{ "tool": "Bash", "input_summary": "bun test index.test.ts", "turn": 9 }
]
}
],
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
Scoped responses include `_meta.included_projects`, showing the main project and associated worktree Claude project contexts that were scanned. All-project responses include every scanned Claude project unless narrowed by `--project-glob`; an explicit `--project` in all-project mode is only used to normalize absolute file queries against that logical checkout. Worktree or all-project result rows can include:
| Field | Description |
| ----- | ----------- |
| `project` | Raw Claude project directory basename from `~/.claude/projects`. |
| `project_path_guess` | Best-effort unmangled path guess, or `null` when the directory name is not in Claude's path-mangled form. This is display-only and lossy when path segments contain literal hyphens. |
| `project_role` | `main`, `worktree`, or `global`. |
| `session_ref` | Stable follow-up identity: `{ "session_id": "...", "project": "" }`. |
Use `session_ref.project` with `--claude-project` rather than relying on `project_path_guess`:
```bash
cc-session-tool tools DA2738E3 --claude-project -workspace-app--claude-worktrees-feature-a
```
All-project responses also include `_meta.projects_scanned`. `--project-glob` matches Claude project identity fields, not filesystem globs over project files. Do not derive follow-up commands or roots from `project_path_guess`; it is for display and rough filtering only.
`--origin` reports the earliest matching transcript write evidence that satisfies the filters. It is not VCS creation history.
```json
{
"ok": true,
"data": [
{
"session_id": "DA2738E3-...",
"session_ref": {
"session_id": "DA2738E3-...",
"project": "-workspace-app--claude-worktrees-feature-a"
},
"matches": {
"files": ["/workspace/app/.claude/worktrees/feature-a/src/auth.ts"],
"file_evidence": [
{
"rawPath": "/workspace/app/.claude/worktrees/feature-a/src/auth.ts",
"logicalPath": "src/auth.ts",
"operation": "write",
"turn": 6,
"timestamp": "2026-03-07T22:33:00.000Z"
}
],
"turns": [6]
}
}
],
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
---
### `subagents `
List subagents spawned during a session. Reads metadata from companion `.meta.json` files and counts JSONL lines for each subagent.
```bash
cc-session-tool subagents [--project | --claude-project ]
```
**Output:**
```json
{
"ok": true,
"data": {
"session_id": "DA2738E3-...",
"subagents": [
{
"agent_id": "a8361bc",
"agent_type": "task",
"description": "Implement auth middleware",
"lines": 84,
"timestamp": "2026-03-07T22:35:00.000Z"
}
]
},
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
**Notes:**
- Subagents are sorted by timestamp (newest first). Null timestamps sort to end.
- `agent_type` and `description` come from `agent-.meta.json`; both are `null` if the meta file is missing.
- Use the returned `agent_id` with colon notation to target a subagent with any session-scoped command (e.g., `shape DA2738E3:a8361bc`).
- Colon notation is rejected by this command (subagents of subagents do not exist).
## Composition Examples
### Quick access to recent sessions
```bash
# Last 5 sessions
cc-session-tool list --last 5
# Sessions from the last day
cc-session-tool list --since 1d
# Last 3 sessions on a specific branch
cc-session-tool list --branch feature/auth --last 3
# Recent sessions across selected Claude project identities
cc-session-tool list --all-projects --project-glob "*app*" --last 5
```
### Navigation analysis before first edit
```bash
# Find the session
cc-session-tool list --branch feature/auth
# See the structure -- note first_edit_turn in summary
cc-session-tool shape DA2738E3
# Extract all tool calls before the first edit (say turn 15)
cc-session-tool tools DA2738E3 --turn 1-14
```
### Failed Grep patterns
```bash
cc-session-tool tools --name Grep --failed
```
### Drill into a specific turn
```bash
# Full content of turns around the first edit
cc-session-tool slice DA2738E3 --turn 14-16
# Just the assistant's reasoning
cc-session-tool messages DA2738E3 --role assistant --type thinking --turn 14-16
```
### What files did a session touch?
```bash
# All files, grouped by path
cc-session-tool files DA2738E3
# Just edits
cc-session-tool files DA2738E3 --operation edit
# Chronological log of all file accesses
cc-session-tool files DA2738E3 --group-by turn
```
### Find which session wrote a file
```bash
# Current logical project, including associated Claude worktrees
cc-session-tool search --file /workspace/app/src/auth.ts --operation write
# Earliest matching transcript write with file evidence
cc-session-tool search --file /workspace/app/src/auth.ts --origin
# Broad audit across selected Claude project identities
cc-session-tool search --all-projects --project-glob "*app*" --file src/auth.ts --operation write
```
### Token consumption comparison
```bash
cc-session-tool tokens
cc-session-tool tokens
# Compare input/output ratios and cache_read/cache_create patterns
```
### Inspect subagent activity
```bash
# Discover subagents spawned during a session
cc-session-tool subagents DA2738E3
# See what a specific subagent did
cc-session-tool shape DA2738E3:a8361bc
# Check tool calls in a subagent session
cc-session-tool tools DA2738E3:a8361bc
# List sessions with subagent counts
cc-session-tool list --include-subagents
```
## Output Format
All commands return JSON with a consistent envelope:
```json
{
"ok": true,
"data": { ... },
"_meta": { "total": 1, "returned": 1, "hasMore": false }
}
```
Errors:
```json
{
"ok": false,
"error": { "code": "NOT_FOUND", "message": "..." }
}
```
### Exit Codes
| Code | Meaning |
| ---- | ------------------------------ |
| 0 | Success |
| 1 | Format error / terminated |
| 2 | Invalid arguments or ID format |
| 3 | Not found |
## Testing
```bash
bun test
```
## Releasing
```bash
bun run release
```
The release script (`scripts/release.sh`) handles the full local workflow:
1. Asserts clean working tree on `main`, up-to-date with remote
2. Runs tests
3. Bumps `VERSION` in `index.ts`, commits, tags `vX.Y.Z`
4. Pushes commit + tag to origin
The tag push triggers the `release.yml` GitHub Actions workflow, which builds universal macOS binaries and creates a GitHub release with checksums.