/session-log/session-log writes the prompt and response text exposed by the current harness to a local Markdown transcript. It supports exactly five host identities: Claude Code, Codex, Cursor, OpenCode, and OMP.
The same five commands control every harness, but their reports are deliberately not identical. Each adapter uses only its native lifecycle and usage records: Cursor exposes per-turn token counts in hook payloads but not in transcripts, while Codex exposes token totals through native rollout records.
TOTAL. Cursor reports per-turn lines and a TOTAL in Claude Code's format from the token counts its stop hook recorded, for the parent agent only; each sub-agent shows only its final context size from Cursor's local state, never added to TOTAL.claude, codex, cursor, opencode, or omp; it never infers identity from paths, processes, or environment variables.600 permissions and nothing is uploaded by this package.Run /session-log with one of these. With no argument it defaults to status.
| Command | What it does | Typical output |
|---|---|---|
on |
Installs the current harness adapter and turns logging on. | <label>: on or on — restart required; Codex uses on — trust review/restart required |
off |
Turns logging off for the current harness. | <label>: off |
status default |
Repairs an enabled adapter if needed, then reports whether it is active. | on / off / on — adapter missing / on — restart required; Codex uses on — trust review/restart required |
usage |
Runs the current harness's native usage report. | <label>: usage and a native report |
repair |
Removes duplicate and stale session-log hook registrations for the current harness and restores the missing ones of a partial set: Claude Code settings.json hooks, Codex and Cursor hooks.json entries, and dangling OpenCode and OMP adapter links that point into the claude-goodies plugin cache. It registers no hooks where none exist; on runs the same repair first. |
<label>: removed … duplicate or stale hook registration(s) from <path>, <label>: restored … missing hook registration(s) in <path>, <label>: removed dangling adapter link <path>, <label>: repair skipped: <reason>, or <label>: repair found no duplicate or stale registrations |
The exact labels are Claude Code, Codex, Cursor, OpenCode, and OMP. Without a target, usage reports the session you are working in: among the current project's top-level sessions it picks the one with the newest user prompt, so a parallel session that is still writing output is never chosen; OMP's /session-log command passes its own session directly. Pass a session ID, or a transcript path where that adapter accepts one, to report another session. OpenCode accepts only an ID or --latest. Claude Code alone accepts --check to compare with ccusage when installed.
bash install-universal-session-log.sh. It seeds or updates a complete stable package in every default skill root and installs OpenCode's command entrypoint.bash skills/session-log/install.sh --harness cursor --arguments "status" first seeds or updates Cursor's package, then dispatches status. Use --install --harness <identity> to seed one package without running a command.on, off, status, and repair touch only the explicitly selected identity. Restart after on — restart required. For Codex, review the commands added to ~/.codex/hooks.json, trust them, then restart.enabledPlugins in ~/.claude/settings.json sets claude-goodies@<marketplace> to true (what claude plugin install writes by default). Run from the plugin, on, off, repair, and (while logging is on) status then remove every session-log hook from ~/.claude/settings.json, and a new session does the same silently. A session repeats that cleanup only when settings.json, the session-log package (its location or VERSION), the plugin version that last started a session, or the presence of a hook script that settings.json registers changed since that session-log copy's last successful cleanup. A session-log hook is any command that starts with SESSION_LOG_OWNER=universal-session-log, a copy of the plugin's own hooks/hooks.json command, or any bash …/skills/session-log/adapters/claude/claude_hook.sh <event> command; a native Windows C:\ path is recognized too, but removing it takes WSL. This clears dead paths that older releases wrote into the versioned plugin cache. Once the plugin's hooks have started a session, a standalone copy under ~/.claude/skills defers to them and registers no settings.json hooks of its own, unless Claude Code has orphaned that plugin version after an update or uninstall. With the plugin enabled only at project, local, or managed scope, the plugin removes only dead session-log registrations (those whose script no longer exists or lies in the versioned plugin cache) and the standalone copy keeps its settings.json hooks, so that project may log twice. With the plugin enabled at user scope but disabled at project or local scope, that project does not log, because the standalone copy still defers to the plugin. After uninstalling or disabling the plugin, run /session-log on once to register the standalone copy's hooks again. Other hooks stay untouched.status and usage also reinstall a missing or outdated native adapter before continuing.stop hook recorded while logging was on with the current adapter (subagentStop carries no tokens), lists each sub-agent with only its final context size from Cursor's local state (not added to TOTAL), and fails explicitly when a session has no records.bash, Python 3, and shasum or sha256sum; runtime-state checks and Claude Code need jq. OpenCode usage additionally needs sqlite3; OMP usage needs Bun. ccusage is optional for Claude Code's --check. On native Windows only the Claude Code plugin's hooks run (under Git Bash, without jq while logging is off); on, off, repair, and the session-start cleanup need WSL. While settings.json holds session-log hooks, each native session start attempts that cleanup, which fails silently and is retried at the next session start (a small startup delay), because only a successful cleanup is recorded.~/.claude, ~/.codex, ~/.cursor, ~/.config/opencode (with native data in ~/.local/share/opencode), and ~/.omp/agent. Relocated roots fail explicitly.Scenario: one full turn — you submit a prompt, a background sub-agent finishes, the assistant answers, and you had switched model and effort since the previous turn. This is what lands in the session file (Claude Code, ~/.claude/prompt-logs/<project>/session_….md):
## 14:02:11
Implement the next uncompleted task in plans/FEAT-001.md
---
14:03:05 sub-agent finished: general-purpose (agent-9f3c2a1b), jsonl: /Users/…/<sid>/subagents/agent-9f3c2a1b.jsonl
### 14:05:53 response
<the assistant's final message, verbatim>
working time: 00:03:42
est. used token: input: 2000, output: 1000, cache_create: 150000, cache_read: 0, total_tokens: 153000, price: $2.32, model: claude-opus-5, effort: max
switched: model claude-sonnet-5 → claude-opus-5, effort high → max
---
Reading top to bottom: the prompt, then the sub-agent that ran, then the reply, then the measurements. total_tokens is input + output + cache-write + cache-read. The switched: line here explains the large cache_create: changing models forced a full cache write on this turn.
usage report looks likeCommand: /session-log usage in Claude Code (totals the current session — the project's session with the newest user prompt). Other harnesses keep their native report shapes described above.
Claude Code: usage
1. 14:02:11 Implement the next uncompleted task… est. used token: … price: $2.32
2. 14:12:40 Fix the failing test in auth_test… est. used token: … price: $0.41
sub-agent general-purpose (agent-9f3c2a1b) est. used token: … price: $0.77
TOTAL (2 requests, 1 sub-agent) input: … output: … total_tokens: … price: $3.50
One numbered line per turn, one line per sub-agent transcript found under the session, then a merged TOTAL. Every transcript is counted once, no matter how deeply sub-agents nest.