codex-session-history/skills/codex-session-history/SKILL.md
Query local Codex session history with jq. Use when users explicitly ask to search Codex sessions, inspect prior Codex conversations, find previous user messages, locate tool calls, summarize past work by working directory, or query Codex rollout JSONL files.
npx skillsauth add grailautomation/claude-plugins codex-session-historyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when the user explicitly asks to inspect local Codex session history. Codex session files can contain private prompts, screenshots, tool outputs, and credentials accidentally pasted by a user, so extract only the fields needed to answer the request.
Codex stores active session JSONL files under date-partitioned directories:
~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
Older sessions may be archived flat under:
~/.codex/archived_sessions/rollout-*.jsonl
Memory summaries under ~/.codex/memories/rollout_summaries/ are derived summaries, not raw session logs. Use them only when the user asks for memory-derived history or when raw session search is too broad.
Each line is a JSON object. Common top-level type values:
| Type | Useful fields | Notes |
| --- | --- | --- |
| session_meta | .payload.id, .payload.cwd, .payload.originator, .payload.cli_version, .payload.source, .payload.model_provider | Also may contain large instructions; do not print the full payload by default. |
| turn_context | .payload.turn_id, .payload.cwd, .payload.current_date, .payload.model, .payload.summary | Marks turn boundaries and compaction context. |
| event_msg | .payload.type, plus event-specific fields | Includes user_message, agent_message, task_started, task_complete, token_count, and patch events. |
| response_item | .payload.type, .payload.role, .payload.content, .payload.name, .payload.arguments | Stores model messages, reasoning, function calls, and tool outputs. |
Common response_item.payload.type values include message, reasoning, function_call, function_call_output, custom_tool_call, and custom_tool_call_output.
jq; avoid raw cat, head, or grep over full files.session_meta.payload.base_instructions, large image payloads, and full tool outputs unless the user explicitly needs them.--arg for user-provided search text instead of interpolating it into jq programs.Recent active sessions:
find "$HOME/.codex/sessions" -name '*.jsonl' -type f -print0 |
xargs -0 ls -t |
head -20
Recent active and archived sessions together:
find "$HOME/.codex/sessions" "$HOME/.codex/archived_sessions" \
-name '*.jsonl' -type f -print0 2>/dev/null |
xargs -0 ls -t |
head -30
Sessions for a working directory:
TARGET_CWD="$PWD"
find "$HOME/.codex/sessions" "$HOME/.codex/archived_sessions" \
-name '*.jsonl' -type f -print0 2>/dev/null |
while IFS= read -r -d '' f; do
jq -e --arg cwd "$TARGET_CWD" '
select(.type == "session_meta" and .payload.cwd == $cwd)
' "$f" >/dev/null 2>&1 && printf '%s\n' "$f"
done |
xargs ls -t 2>/dev/null |
head -20
Count top-level line types:
jq -r '.type // "<missing>"' "$SESSION_FILE" | sort | uniq -c
Count response item types:
jq -r '
select(.type == "response_item") |
.payload.type // "<missing>"
' "$SESSION_FILE" | sort | uniq -c
Show safe metadata:
jq -c '
select(.type == "session_meta") |
{
timestamp,
id: .payload.id,
cwd: .payload.cwd,
originator: .payload.originator,
cli_version: .payload.cli_version,
source: .payload.source,
model_provider: .payload.model_provider
}
' "$SESSION_FILE"
User messages from the event stream:
jq -r '
select(.type == "event_msg" and .payload.type == "user_message") |
.timestamp + " | " + (.payload.message // "" | gsub("\n"; " ") | .[0:300])
' "$SESSION_FILE"
Assistant final/intermediate messages from the event stream:
jq -r '
select(.type == "event_msg" and .payload.type == "agent_message") |
.timestamp + " | " + ((.payload.phase // "agent") + " | " + (.payload.message // "" | gsub("\n"; " ") | .[0:300]))
' "$SESSION_FILE"
Messages from response items:
jq -r '
def content_text:
if (.payload.content | type) == "array" then
[.payload.content[]? | .text // .input_text // empty] | join(" ")
elif (.payload.content | type) == "string" then
.payload.content
else
""
end;
select(.type == "response_item" and .payload.type == "message") |
.timestamp + " | " + (.payload.role // "unknown") + " | " + (content_text | gsub("\n"; " ") | .[0:300])
' "$SESSION_FILE"
Search user and assistant event messages across active and archived sessions:
QUERY="keyword"
find "$HOME/.codex/sessions" "$HOME/.codex/archived_sessions" \
-name '*.jsonl' -type f -print0 2>/dev/null |
while IFS= read -r -d '' f; do
jq -r --arg q "$QUERY" --arg file "$f" '
select(
.type == "event_msg" and
(.payload.type == "user_message" or .payload.type == "agent_message") and
((.payload.message // "") | test($q; "i"))
) |
$file + "\t" + .timestamp + "\t" + .payload.type + "\t" +
((.payload.message // "") | gsub("\n"; " ") | .[0:240])
' "$f" 2>/dev/null
done
Search response item text while avoiding session metadata:
QUERY="keyword"
jq -r --arg q "$QUERY" '
def content_text:
if (.payload.content | type) == "array" then
[.payload.content[]? | .text // .input_text // empty] | join(" ")
elif (.payload.content | type) == "string" then
.payload.content
else
""
end;
select(.type == "response_item" and .payload.type == "message") |
content_text as $text |
select($text | test($q; "i")) |
.timestamp + " | " + (.payload.role // "unknown") + " | " + ($text | gsub("\n"; " ") | .[0:240])
' "$SESSION_FILE"
List function calls without full arguments:
jq -r '
select(.type == "response_item" and .payload.type == "function_call") |
.timestamp + " | " + (.payload.name // "tool") + " | " + (.payload.call_id // "")
' "$SESSION_FILE"
Find calls to a specific tool:
TOOL_NAME="exec_command"
jq -r --arg tool "$TOOL_NAME" '
select(
.type == "response_item" and
.payload.type == "function_call" and
.payload.name == $tool
) |
.timestamp + " | " + (.payload.call_id // "") + " | " + ((.payload.arguments // "") | tostring | gsub("\n"; " ") | .[0:300])
' "$SESSION_FILE"
List tool outputs by call id without printing full output:
jq -r '
select(.type == "response_item" and .payload.type == "function_call_output") |
.timestamp + " | " + (.payload.call_id // "") + " | " + ((.payload.output // "") | tostring | gsub("\n"; " ") | .[0:220])
' "$SESSION_FILE"
When the user asks to find prior Codex work:
session_meta.payload.cwd.event_msg user and assistant messages first; use response_item for deeper reconstruction.development
Parse and analyze Workato recipe JSON exports. Use when the user asks about a Workato recipe's logic, data flow, field mappings, error handling, or control flow. Also use when the user references a .recipe.json file or asks to debug a Workato integration.
databases
Review implementation plans as a staff software engineer. Use when the user asks for staff engineer feedback, senior engineering review, plan review, architecture review, implementation-plan critique, approval before proceeding, risk review, or feedback on a proposed technical plan.
tools
Use when the user invokes $spec-kit:tasks or /spec-kit:tasks, or asks to generate dependency-ordered tasks from the plan and repo context.
testing
Use when the user invokes $spec-kit:specify or /spec-kit:specify, or asks to create a brownfield-aware feature specification from a natural language description.