ce-sessions

$npx mdskill add EveryInc/compound-engineering-plugin/ce-sessions

Search session history across Claude Code, Codex, and Cursor and synthesize findings about what was worked on, tried, decided, or learned in prior sessions.

SKILL.md
.github/skills/ce-sessionsView on GitHub ↗
---
name: ce-sessions
description: "Search and ask questions about coding agent session history across Claude Code, Codex, and Cursor. Use when asking what was worked on, what was tried before, how a problem was investigated across sessions, what happened recently, or any question about past agent sessions. Also use when the user references prior sessions, previous attempts, or past investigations — even without saying 'sessions' explicitly."
---

# /ce-sessions

Search session history across Claude Code, Codex, and Cursor and synthesize findings about what was worked on, tried, decided, or learned in prior sessions.

## Usage

```
/ce-sessions [question or topic]
/ce-sessions
```

## Pre-resolved context

**Git branch (pre-resolved):** !`git rev-parse --abbrev-ref HEAD 2>/dev/null || true`

If the line above resolved to a plain branch name (like `feat/my-branch`), use it for branch filtering and pass it to the synthesis subagent. If it still contains a backtick command string or is empty, derive the branch at runtime instead.

**Repo root (pre-resolved):** !`git rev-parse --show-toplevel 2>/dev/null || true`

If the line above resolved to a path, take its last path component as the repo folder name and use that for session discovery. If it is empty or still contains a backtick command string, derive the repo name at runtime instead.

## Note: 2026

The current year is 2026. Use this when interpreting session timestamps.

## Guardrails

These rules apply at all times during orchestration and synthesis.

- **Never read entire session files into context.** Session files can be 1-7MB. Always use the extraction scripts to filter first, then reason over the filtered output.
- **Never extract or reproduce tool call inputs/outputs verbatim.** Summarize what was attempted and what happened.
- **Never include thinking or reasoning block content.** Claude Code thinking blocks are internal reasoning; Codex reasoning blocks are encrypted. Neither is actionable.
- **Never analyze the current session.** Its conversation history is already available to the caller.
- **Surface technical content, not personal content.** Sessions contain everything — credentials, frustration, half-formed opinions. Use judgment about what belongs in a technical summary and what doesn't.
- **Fail fast on access errors.** If session discovery fails on permissions, report the issue immediately. Do not retry the same operation with different tools or approaches — repeated retries waste tokens without changing the outcome.

## Execution

If no question argument is provided, ask what the user wants to know about their session history. Use the platform's blocking question tool: `AskUserQuestion` in Claude Code (call `ToolSearch` with `select:AskUserQuestion` first if its schema isn't loaded), `request_user_input` in Codex, `ask_user` in Gemini, `ask_user` in Pi (requires the `pi-ask-user` extension). Fall back to asking in plain text only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.

### Step 1 — Determine scan window

Infer a time range from the user's question. Start narrow; widen only if a narrow scan finds nothing relevant.

| Signal | Initial scan window |
|--------|---------------------|
| "today", "this morning" | 1 day |
| "recently", "last few days", "this week", or no time signal | 7 days |
| "last few weeks", "this month" | 30 days |
| "last few months", broad feature history | 90 days |

Claude Code retains session history for ~30 days by default. Wider windows may find nothing on Claude Code unless the user has extended retention.

### Step 2 — Discover sessions and extract metadata

Run the discovery + metadata pipeline (preserving the null-delimited xargs hardening that lets `extract-metadata.py` run in batch mode):

```bash
bash scripts/discover-sessions.sh <repo> <days> | tr '\n' '\0' | xargs -0 python3 scripts/extract-metadata.py --cwd-filter <repo>
```

Each output line is a JSON object describing a session (platform, file, size, ts, session, plus platform-specific fields). The final `_meta` line carries `files_processed` and `parse_errors`.

If the inventory's `_meta` line shows `files_processed: 0`, return "no relevant prior sessions" and stop.

If `parse_errors > 0`, note that some sessions could not be parsed and proceed with what was returned.

To narrow the platform set, add `--platform claude`, `--platform codex`, or `--platform cursor` to the `discover-sessions.sh` invocation. Default to all three.

### Step 3 — Filter and rank

Apply these filters in order to pick the sessions worth deep-diving:

1. **Branch filter (Claude Code only).** Keep sessions where `branch == dispatch_branch` exactly, or where the branch name contains a keyword from the question's topic (e.g., a question about "auth middleware" matches branches `feat/auth-fix`, `chore/auth-refactor`). Codex sessions don't carry `gitBranch` — skip this filter for them.

2. **If the branch filter returned zero sessions, or you're processing Codex sessions:**
   - Derive 2-4 keywords from the question's topic. For "a recent crash in the auth middleware where session-validation rejects valid tokens", derive `auth,middleware,session,token` (or similar).
   - Re-invoke the discovery pipeline with `--keyword K1,K2,...` appended to the `extract-metadata.py` invocation. The script returns sessions with non-zero `match_count` plus per-keyword counts.
   - **If `files_matched: 0`, return "no relevant prior sessions" and stop.** Do not extract anything.
   - If `files_matched > 0`, treat those sessions as candidates. Rank by `match_count`, break ties by per-keyword counts.

3. **Drop sessions outside the scan window.** Use `last_ts` when available, fall back to `ts`. Discard sessions where both fall before the window start.

4. **Exclude the current session** — its conversation history is already available to the caller.

5. **Apply the deep-dive cap.** Take at most **5 sessions total across all platforms**. Narrow by branch-match → `match_count` → file size > 30KB → recency.

6. **Proceed only if at least one session remains after filtering.** Otherwise return "no relevant prior sessions" and stop.

**Note: `gitBranch` is captured at the first user message only.** A session that began on `main` and did substantive work on a feature branch via mid-session `git checkout` records `branch: "main"`. Branch-match returning nothing is not conclusive evidence — that's why the keyword-filter fallback in step 2 is required.

### Step 4 — Set up scratch space

Create a per-run throwaway scratch directory:

```bash
SCRATCH=$(mktemp -d -t ce-sessions-XXXXXX)
```

Capture the absolute path; thread it into Step 5 and Step 6. The OS handles cleanup on session end; an explicit `rm -rf "$SCRATCH"` at the end of Step 7 is harmless and makes intent explicit.

### Step 5 — Extract per-session content (file-mediated)

For each selected session, run the skeleton extractor with `--output` so content writes directly to the scratch file — extraction bytes never round-trip through the orchestrator's tool results:

```bash
python3 scripts/extract-skeleton.py --output "$SCRATCH/<session-id>.skeleton.txt" < <session-file>
```

Stdout receives only a one-line JSON status (`{"_meta": true, "wrote": "...", "bytes": N, ...}`). Capture `bytes` and `parse_errors` from each status line.

**Conditional tail-extract** — if a skeleton terminates mid-investigation (last visible turn is a tool call with no resolution, or the assistant is mid-debugging without a conclusion), re-extract with a `tail` shape:

```bash
python3 scripts/extract-skeleton.py --output "$SCRATCH/<session-id>.skeleton.tail.txt" < <session-file>
```

(The skeleton script does not accept a `tail:N` cap directly; if a tail-only view is needed, post-process the scratch file in shell with `tail -n 50` after extraction. Use this only when the head output suggests the session was truncated mid-investigation.)

**Conditional errors-mode** — for sessions where investigation dead-ends are likely valuable:

```bash
python3 scripts/extract-errors.py --output "$SCRATCH/<session-id>.errors.txt" < <session-file>
```

Use selectively — only when understanding what went wrong adds value. Cursor agent transcripts don't log tool results, so errors-mode produces nothing for Cursor sessions.

### Step 6 — Dispatch synthesis subagent

Dispatch the `ce-session-historian` subagent via the platform's subagent primitive (`Agent` in Claude Code, `spawn_agent` in Codex, `subagent` in Pi via the `pi-subagents` extension). Omit the `mode` parameter so the user's configured permission settings apply. Run on the mid-tier model (e.g., `model: "sonnet"` in Claude Code) — the synthesizer doesn't need frontier reasoning.

The dispatch prompt is the agent's input contract. Pass these fields:

- `problem_topic` — one sentence naming the concrete question. Lift from the user's argument or, if missing, from the answer to the no-arg prompt.
- `scratch_dir` — absolute path to `$SCRATCH`.
- `sessions` — an array of objects, one per extracted session, each with:
  - `path` — absolute path to the skeleton file (and optionally `errors_path` for the errors file when extracted)
  - `platform` — `claude`, `codex`, or `cursor`
  - `branch` — git branch when present (Claude Code only)
  - `cwd` — working directory when present (Codex only)
  - `ts` and `last_ts` — session timestamps
  - `match_count` and `keyword_matches` — when keyword filtering was used
- `output_schema` — the structure the agent's response should follow. Default schema:
  ```
  Structure your response with these sections (omit any with no findings):
  - What was tried before
  - What didn't work
  - Key decisions
  - Related context
  ```
  When the caller (e.g., `ce-compound`) supplies a schema in the skill argument, pass it through verbatim.

Example dispatch shape:

```
Synthesize findings from these prior sessions:

Problem topic: <one-line topic>

Sessions to read (paths in $SCRATCH):
1. /tmp/ce-sessions-XXXX/abc123.skeleton.txt
   platform=claude branch=feat/auth-fix ts=2026-05-01
2. /tmp/ce-sessions-XXXX/def456.skeleton.txt  errors=/tmp/ce-sessions-XXXX/def456.errors.txt
   platform=codex cwd=/Users/.../my-project ts=2026-05-03
...

Output schema:
- What was tried before
- What didn't work
- Key decisions
- Related context

Filter rule: only surface findings directly relevant to this specific problem.
Ignore unrelated work from the same sessions or branches.
```

The agent reads each path via the platform's native file-read tool and returns prose findings. Bulk extraction content lives only in the agent's subagent context — the orchestrator's working state stays at file paths plus small inventory metadata.

### Step 7 — Return findings

Return the synthesizer's output text to the caller verbatim. If discovery or keyword filtering returned zero sessions (Step 2 or Step 3), return the literal string `no relevant prior sessions` instead.

Optionally clean up scratch:

```bash
rm -rf "$SCRATCH"
```

The OS handles cleanup eventually regardless; the explicit cleanup is for readers who expect it.

## Output

When the caller (typically a user typing `/ce-sessions`, or another skill invoking ce-sessions via the platform's skill-invocation primitive) does not specify an output format, include a brief header noting what was searched:

```
**Sessions searched**: [count] ([N] Claude Code, [N] Codex, [N] Cursor) | [date range]
```

Then the synthesizer's prose findings. When the caller supplies a schema, honor it verbatim and omit the default header.

## Time budget

Stop as soon as a complete answer is available. A confident "no relevant prior sessions" within seconds is a complete answer; do not extend the search to fill time. The structural caps in Step 3 (max 5 sessions deep-dived) and Step 5 (conditional tail/errors extraction) bound runtime by construction.

## Error handling

If the discovery pipeline fails (e.g., unreadable home directory, permission failure), surface the error to the caller. Do not substitute git log, file listings, or other sources — this skill's contract is session metadata and synthesis.

If extraction `--output` write fails (disk full, permission), surface a clear error and do not dispatch the synthesizer with partial paths.

If `_meta` reports `parse_errors > 0` from any script, note partial extraction in the dispatch prompt and proceed; the synthesizer flags partial in findings.
More from EveryInc/compound-engineering-plugin