Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@
],
"repository": "https://github.com/Unsupervisedcom/deepwork",
"license": "MIT"
},
{
"name": "full-convo-memory",
"description": "Search the current Claude Code session's jsonl transcript using jq filters",
"version": "0.1.0",
"source": "./plugins/full-convo-memory",
"author": {
"name": "DeepWork"
},
"category": "productivity",
"keywords": [
"memory",
"search",
"transcript",
"conversation",
"jq"
],
"repository": "https://github.com/Unsupervisedcom/deepwork",
"license": "BSL-1.1"
}
]
}
7 changes: 5 additions & 2 deletions .deepreview
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,11 @@ test_file_quality:
blank lines or other comments).

3. **Valid REQ ID**: The requirement ID in parentheses must follow the
pattern `{PREFIX}-REQ-NNN.M` where PREFIX is one of: DW-REQ,
JOBS-REQ, REVIEW-REQ, LA-REQ, PLUG-REQ.
pattern `{PREFIX}-REQ-NNN.M` or `{PREFIX}-REQ-NNN.M.L` where PREFIX
is one of: DW-REQ, JOBS-REQ, REVIEW-REQ, LA-REQ, PLUG-REQ. The
optional third `.L` level is used when a test maps to a specific
sub-clause of a requirement section (e.g. `PLUG-REQ-004.1.2`
referring to clause 2 of section PLUG-REQ-004.1).

4. **Consistency**: If a test references a REQ ID only in a docstring
but is missing the formal two-line comment block, flag it.
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- New `full-convo-memory` Claude Code plugin (registered in `.claude-plugin/marketplace.json`) providing a `search_conversation` skill and `scripts/search_conversation.sh`. The script runs `jq` against the current session's jsonl transcript, auto-detecting the log file for both top-level sessions (via `CLAUDE_CODE_SESSION_ID`) and sub-agents (via `CLAUDE_CODE_SESSION_ID` + `CLAUDE_CODE_AGENT_ID`), filtering out compaction-summary messages, and appending a pointer line naming the log file for fallback semantic search via an Explore agent

### Changed

### Fixed
Expand Down
6 changes: 6 additions & 0 deletions doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ deepwork/ # DeepWork tool repository
│ │ │ └── review/SKILL.md
│ │ ├── hooks/ # hooks.json, post_commit_reminder.sh, post_compact.sh, startup_context.sh, deepschema_write.sh
│ │ └── .mcp.json # MCP server config
│ ├── full-convo-memory/ # Transcript-search plugin
│ │ ├── .claude-plugin/plugin.json
│ │ ├── scripts/search_conversation.sh
│ │ └── skills/search_conversation/
│ │ ├── SKILL.md
│ │ └── .deepschema.SKILL.md.yml
│ └── gemini/ # Gemini CLI extension
│ └── skills/deepwork/SKILL.md
├── library/jobs/ # Reusable example jobs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# PLUG-REQ-004: Full Convo Memory Plugin

## Overview

The `full-convo-memory` plugin is a small Claude Code plugin shipped via the DeepWork marketplace. It provides a `search_conversation` skill and a companion `search_conversation.sh` script that run `jq` against the current Claude Code session's jsonl transcript so the agent can recall earlier turns without rereading the whole conversation. The script auto-detects the log file for both top-level sessions and sub-agents, filters out synthetic compaction-summary messages, and ends its output with a pointer line naming the exact log file path (so a caller can fall back to a semantic search via an Explore sub-agent when `jq` matching is insufficient).

## Requirements

### PLUG-REQ-004.1: Plugin Manifest

1. The plugin MUST provide a manifest at `plugins/full-convo-memory/.claude-plugin/plugin.json`.
2. The manifest `name` field MUST be `"full-convo-memory"`.
3. The manifest MUST include non-empty `description`, `version`, `author.name`, and `repository` fields.

### PLUG-REQ-004.2: Marketplace Registration

1. The plugin MUST be registered under the `plugins` array of `.claude-plugin/marketplace.json`.
2. The marketplace entry's `name` field MUST be `"full-convo-memory"`.
3. The marketplace entry's `source` field MUST be `"./plugins/full-convo-memory"`.
4. The marketplace entry MUST include `description`, `version`, `author`, `category`, `keywords`, `repository`, and `license` fields.

### PLUG-REQ-004.3: Plugin Root Directory Layout

1. The plugin root `plugins/full-convo-memory/` MUST contain `.claude-plugin/plugin.json`, `scripts/search_conversation.sh`, and `skills/search_conversation/SKILL.md`.

### PLUG-REQ-004.4: Search Script Existence and Shebang

1. The script MUST exist at `plugins/full-convo-memory/scripts/search_conversation.sh`.
2. The script MUST have the executable bit set for the owner.
3. The script's first line MUST be `#!/usr/bin/env bash`.

### PLUG-REQ-004.5: Zero-Argument Guard

1. When invoked with no positional arguments (and no `--log-file` option consumed), the script MUST print a usage message to stderr and exit with status `2` rather than invoking `jq` with no filter.

### PLUG-REQ-004.6: jq Dependency Check

1. If `jq` is not present on `PATH`, the script MUST write an error message to stderr and exit with status `127`.

### PLUG-REQ-004.7: Explicit Log-File Override

1. When invoked with `--log-file <path>` as the first argument, the script MUST use `<path>` as the log file and MUST remove both tokens from the argument list before forwarding the remainder to `jq`.
2. If `--log-file` is supplied without a path argument, the script MUST exit non-zero with an error message to stderr.

### PLUG-REQ-004.8: Sub-Agent Log-File Resolution

1. When `--log-file` is not provided and both `CLAUDE_CODE_SESSION_ID` and `CLAUDE_CODE_AGENT_ID` environment variables are set to non-empty values, the script MUST first try to resolve the log file to `$HOME/.claude/projects/<encoded-cwd>/$CLAUDE_CODE_SESSION_ID/subagents/agent-$CLAUDE_CODE_AGENT_ID.jsonl`, where `<encoded-cwd>` is `$PWD` with every `/` replaced by `-` (leading `-` preserved).
2. If the sub-agent path does not exist, resolution MUST fall through to PLUG-REQ-004.9.

### PLUG-REQ-004.9: Top-Level Session Log-File Resolution

1. When `--log-file` is not provided and the sub-agent path (PLUG-REQ-004.8) did not resolve but `CLAUDE_CODE_SESSION_ID` is set to a non-empty value, the script MUST try to resolve the log file to `$HOME/.claude/projects/<encoded-cwd>/$CLAUDE_CODE_SESSION_ID.jsonl`.
2. If the top-level path does not exist, resolution MUST fall through to PLUG-REQ-004.10.

### PLUG-REQ-004.10: Fallback Log-File Resolution

1. When neither `--log-file` nor the env-var paths yield an existing file, the script MUST select the most-recently-modified `*.jsonl` file located directly (non-recursively) inside `$HOME/.claude/projects/<encoded-cwd>/` as the log file.

### PLUG-REQ-004.11: Unresolvable Log File

1. If none of the strategies in PLUG-REQ-004.7 through PLUG-REQ-004.10 yield an existing file, the script MUST print a diagnostic to stderr that includes the values inspected for `CLAUDE_CODE_SESSION_ID`, `CLAUDE_CODE_AGENT_ID`, and the computed project directory, and MUST exit with status `1`.

### PLUG-REQ-004.12: Compaction-Summary Filter

1. Before running the caller's `jq` expression, the script MUST pre-filter transcript lines with the equivalent of `jq 'select(.isCompactSummary != true)'` so that entries with `isCompactSummary: true` are dropped and never reach the caller's filter.

### PLUG-REQ-004.13: jq Pass-Through

1. After the compaction pre-filter, the script MUST forward all remaining positional arguments to `jq` verbatim, supporting any `jq` flag or filter the caller provides.

### PLUG-REQ-004.14: Exit Code Propagation

1. The script's exit code MUST be the exit code of the caller's `jq` invocation (the right-hand side of the filter pipeline).
2. The pre-filter `jq` exit code MUST NOT propagate to the script's exit code.

### PLUG-REQ-004.15: Trailing Pointer Line

1. After running the `jq` pipeline, the script MUST print a final line to stdout of the form `If you want a more semantic search of the history, start an Explore agent and tell it what to look for in <LOG_FILE>`, where `<LOG_FILE>` is the absolute path of the resolved log file.
2. The trailing pointer line MUST be printed regardless of whether the caller's `jq` produced any matches.

### PLUG-REQ-004.16: Skill Location and Frontmatter

1. The skill MUST exist at `plugins/full-convo-memory/skills/search_conversation/SKILL.md`.
2. The skill's YAML frontmatter `name` field MUST be `"search_conversation"` (matching its directory name).
3. The skill's YAML frontmatter MUST include a non-empty `description` field.

### PLUG-REQ-004.17: Skill Documentation Content

1. The skill body MUST document the script path using the `${CLAUDE_PLUGIN_ROOT}` variable.
2. The skill body MUST state that any `jq` arguments are passed through verbatim.
3. The skill body MUST describe the log-file auto-detection order, explicitly covering the sub-agent case so a sub-agent invoking the skill searches its own transcript.
4. The skill body MUST state that compaction-summary messages are excluded automatically.
5. The skill body MUST include at least one worked `jq` example.
6. The skill body MUST direct the agent to use the printed log-file path with an Explore sub-agent when `jq` matching is insufficient.
3 changes: 0 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@
uv sync --extra dev --quiet 2>/dev/null || true
export PATH="$REPO_ROOT/.venv/bin:$PATH"

# Also register as a uv tool so `uvx deepwork serve` uses local source
uv tool install -e "$REPO_ROOT" --quiet 2>/dev/null || true

# Create claude wrapper script so direnv (which can't export functions) works
_claude_real=$(PATH="$(echo "$PATH" | sed "s|$REPO_ROOT/.venv/bin:||g")" command -v claude)
if [ -n "$_claude_real" ]; then
Expand Down
9 changes: 9 additions & 0 deletions plugins/full-convo-memory/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "full-convo-memory",
"description": "Search the current Claude Code session's jsonl transcript using jq filters",
"version": "0.1.0",
"author": {
"name": "DeepWork"
},
"repository": "https://github.com/Unsupervisedcom/deepwork"
}
139 changes: 139 additions & 0 deletions plugins/full-convo-memory/scripts/search_conversation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env bash
# search_conversation.sh — search the current Claude Code session's jsonl
# transcript using jq. Compaction-summary messages are dropped automatically;
# everything else is passed through to jq verbatim.
#
# Usage:
# search_conversation.sh [--log-file <path>] <jq-args...>
#
# Log-file resolution (first match wins):
# 1. --log-file <path> (explicit override)
# 2. sub-agent : ~/.claude/projects/<encoded-cwd>/$CLAUDE_CODE_SESSION_ID/subagents/agent-$CLAUDE_CODE_AGENT_ID.jsonl
# 3. top-level : ~/.claude/projects/<encoded-cwd>/$CLAUDE_CODE_SESSION_ID.jsonl
# 4. fallback : most-recently-modified *.jsonl directly in ~/.claude/projects/<encoded-cwd>/
#
# <encoded-cwd> is $PWD with every '/' replaced by '-' (leading '-' preserved).

set -euo pipefail

usage() {
cat >&2 <<'EOF'
Usage: search_conversation.sh [--log-file <path>] <jq-args...>

Pre-filters the current Claude Code session's jsonl transcript to drop
compaction-summary entries, then runs `jq <jq-args...>` on the result.

Examples:
search_conversation.sh 'select(.type == "user")'
search_conversation.sh -r 'select((.message.content | tostring) | test("plan mode"; "i")) | .timestamp'
search_conversation.sh --log-file /tmp/session.jsonl 'select(.type == "assistant")'

Any flags and filters accepted by jq are passed through verbatim.
EOF
}

encode_cwd() {
printf '%s' "$1" | sed 's|/|-|g'
}

# ============================================================================
# PARSE OPTIONAL --log-file OVERRIDE
# ============================================================================

LOG_FILE=""
if [ "${1:-}" = "--log-file" ]; then
if [ $# -lt 2 ]; then
echo "error: --log-file requires a path argument" >&2
exit 2
fi
LOG_FILE="$2"
shift 2
fi

# ============================================================================
# GUARD: AT LEAST ONE JQ ARG REQUIRED
# ============================================================================

if [ $# -eq 0 ]; then
usage
exit 2
fi

# ============================================================================
# GUARD: jq MUST BE ON PATH
# ============================================================================

if ! command -v jq >/dev/null 2>&1; then
echo "error: jq is required but not found on PATH" >&2
exit 127
fi

# ============================================================================
# RESOLVE THE LOG FILE
# ============================================================================

ENCODED_CWD=$(encode_cwd "$PWD")
PROJECT_DIR="$HOME/.claude/projects/$ENCODED_CWD"

if [ -z "$LOG_FILE" ]; then
if [ -n "${CLAUDE_CODE_AGENT_ID:-}" ] && [ -n "${CLAUDE_CODE_SESSION_ID:-}" ]; then
CANDIDATE="$PROJECT_DIR/$CLAUDE_CODE_SESSION_ID/subagents/agent-$CLAUDE_CODE_AGENT_ID.jsonl"
if [ -f "$CANDIDATE" ]; then
LOG_FILE="$CANDIDATE"
fi
fi
fi

if [ -z "$LOG_FILE" ] && [ -n "${CLAUDE_CODE_SESSION_ID:-}" ]; then
CANDIDATE="$PROJECT_DIR/$CLAUDE_CODE_SESSION_ID.jsonl"
if [ -f "$CANDIDATE" ]; then
LOG_FILE="$CANDIDATE"
fi
fi

if [ -z "$LOG_FILE" ] && [ -d "$PROJECT_DIR" ]; then
# Most-recently-modified top-level *.jsonl in the project dir (no recursion).
# `|| true` swallows SIGPIPE (141) from `ls` when `head` closes early, and
# also handles the "no matching files" case cleanly under `set -e`.
CANDIDATE=$(find "$PROJECT_DIR" -maxdepth 1 -type f -name '*.jsonl' -print0 2>/dev/null \
| { xargs -0 ls -t 2>/dev/null || true; } \
| head -n 1 \
|| true)
if [ -n "$CANDIDATE" ] && [ -f "$CANDIDATE" ]; then
LOG_FILE="$CANDIDATE"
fi
fi

if [ -z "$LOG_FILE" ] || [ ! -f "$LOG_FILE" ]; then
cat >&2 <<EOF
error: could not resolve session transcript file.
checked --log-file : ${LOG_FILE:-<none>}
CLAUDE_CODE_SESSION_ID : ${CLAUDE_CODE_SESSION_ID:-<unset>}
CLAUDE_CODE_AGENT_ID : ${CLAUDE_CODE_AGENT_ID:-<unset>}
project dir : $PROJECT_DIR

Pass --log-file <path> to override.
EOF
exit 1
fi

# ============================================================================
# RUN THE PIPELINE
# ============================================================================
# Drop compaction-summary messages first, then apply the caller's jq args.
# The `|| true` on the pre-filter stops a single malformed line from flipping
# the pipeline's exit code under `pipefail`; we only care about the user's jq
# exit code, captured via $? after the pipeline.

set +e
{ jq -c 'select(.isCompactSummary != true)' "$LOG_FILE" || true; } | jq "$@"
USER_JQ_EXIT=$?
set -e

# ============================================================================
# TRAILING POINTER LINE (always printed so the caller sees the log path)
# ============================================================================

printf '\nIf you want a more semantic search of the history, start an Explore agent and tell it what to look for in %s\n' "$LOG_FILE"

exit "$USER_JQ_EXIT"
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
requirements:
# PLUG-REQ-004.17.1
plugin-root-variable: >
The skill body MUST document the script path using the
`${CLAUDE_PLUGIN_ROOT}` variable so it resolves correctly regardless of
where the plugin is installed.

# PLUG-REQ-004.17.2
jq-passthrough-doc: >
The skill body MUST state that any `jq` arguments (flags and filters)
are passed through to jq verbatim.

# PLUG-REQ-004.17.3
log-file-resolution-doc: >
The skill body MUST describe the log-file auto-detection order and MUST
explicitly cover the sub-agent case (via `CLAUDE_CODE_SESSION_ID` plus
`CLAUDE_CODE_AGENT_ID`), so that an agent calling the skill from a
sub-agent context understands it will search its own transcript rather
than the parent's.

# PLUG-REQ-004.17.4
compaction-filter-doc: >
The skill body MUST state that compaction-summary messages
(`isCompactSummary: true`) are excluded from search results
automatically.

# PLUG-REQ-004.17.5
worked-example: >
The skill body MUST include at least one worked `jq` example showing how
to call the script with a concrete filter.

# PLUG-REQ-004.17.6
explore-fallback-guidance: >
The skill body MUST direct the agent to use the printed log-file path
with an Explore sub-agent when `jq` matching is insufficient for a
fuzzy/semantic question.
Loading
Loading