con un clic
crush-session-extract
Extract crush CLI sessions from per-project SQLite DBs.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Extract crush CLI sessions from per-project SQLite DBs.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
| name | crush-session-extract |
| description | Extract crush CLI sessions from per-project SQLite DBs. |
| version | 0.1.0 |
| author | Hermes |
| metadata | {"hermes":{"tags":["Crush","Extract","Sqlite","Agent-History","Per-Project"],"related_skills":["agent-history-importer","importing-agent-prompts","hermes-agent"]}} |
Pull sessions out of the crush CLI's per-project SQLite store and render them
as Markdown or JSON transcripts. The store is not a flat file like most
agent tools — every project keeps its own <project>/.crush/crush.db, and the
schema comment about millisecond timestamps is a lie (it stores seconds).
This skill finds the right DB, validates the time unit, hot-backups the file,
and emits a clean transcript. It does not modify or delete sessions.
The workhorse script is scripts/crush_extract.py (stdlib only).
created_at is milliseconds but my dates are 1970"Skip this skill if the user wants a one-shot peek — crush run resume is
already on the CLI (crush --session {id}). Use this when they want to
inspect or move sessions outside the live CLI.
sqlite3.Connection.backup, pathlib.Path.rglob).crush CLI itself does not need to be running — extraction is
read-only and works on a cold DB.All work is done via the terminal tool invoking scripts/crush_extract.py.
The script has five subcommands: find, list, show, dump, verify.
# locate every per-project crush DB
python3 scripts/crush_extract.py find --root ~
# hot-backup + cross-check message_count column vs actual row count
python3 scripts/crush_extract.py verify --db <project>/.crush/crush.db
# list sessions in a date window (auto-detects seconds vs ms)
python3 scripts/crush_extract.py list --db <db> --since YYYY-MM-DD --until YYYY-MM-DD
# show metadata + actual vs column message count for one session
python3 scripts/crush_extract.py show --db <db> --session <id>
# render one session as Markdown transcript (or --format json)
python3 scripts/crush_extract.py dump --db <db> --session <id> --format md --out transcript.md
Crush does not store sessions in ~/.crush/. It writes
<project>/.crush/crush.db per project, alongside .gitignore. The global
file at ~/.config/crush/.crush/crush.db is only a goose_db_version
migration header and has no session rows.
python3 scripts/crush_extract.py find --root ~
Output is one row per DB: size, mtime, session count, message count,
absolute path. The script skips Trash/ and .bak files automatically.
If the user's target project isn't in the output, walk a wider root
(--root /home/<user>), or pass the path explicitly to --db.
Always run verify first on the source DB. It does two things in one shot:
Connection.backup() (WAL-safe). Output is
<db>.pre-verify-<ts>.bak next to the source. Use trash <bak> to remove
after a successful import — never rm.sessions.message_count column against the actual
COUNT(*) FROM messages WHERE session_id = s.id per session. If any
mismatch, the script exits 1 and lists the offenders.python3 scripts/crush_extract.py verify --db <project>/.crush/crush.db
If the schema looks broken (count drift, orphan rows), stop and read
references/schema-cheatsheet.md before proceeding.
List with a date window. The script detects the time unit (see step 4) and prints ISO timestamps; no math required on the agent's side.
python3 scripts/crush_extract.py list \
--db <project>/.crush/crush.db \
--since 2026-07-05 --until 2026-07-05
Copy the full UUID from the output (the column shows only a 16-char prefix;
the script needs the full id for dump/show).
This is the single biggest pitfall. The schema says:
created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
The comment is wrong. created_at for sessions created today is
~1783258809 (seconds since epoch), not ~1783258809000. Naively doing
datetime(value, 'unixepoch') works; doing datetime(value/1000, 'unixepoch') lands you in 1970.
verify auto-detects this and prints # unit detected: seconds. The
heuristic: if MIN(created_at) falls in [2024-01-01 UTC, now+1d], treat
as seconds; if the same range in ms, treat as milliseconds. The script
applies the same conversion everywhere internally — but if you hand-write
SQL, don't divide by 1000.
python3 scripts/crush_extract.py dump \
--db <project>/.crush/crush.db \
--session <full-uuid> \
--format md \
--out transcript.md
The Markdown rendering:
## [N] <role> (<ISO timestamp>) header.text parts inline as paragraphs.reasoning parts wrapped in > **reasoning:** blockquotes (preserves
Crush's chain-of-thought separately from user-visible text).tool_call parts render **→ tool_call <name> followed by the JSON input
in a fenced block. tool_result renders the content in a fenced block,
truncated to 4000 chars with a marker (Crush tool outputs are frequently
huge).finish parts render as a one-liner with reason + time.part.type values are dumped inline as an HTML comment so the
Markdown still validates.JSON mode dumps the raw rows + parsed parts arrays with no rendering —
use it for re-importing into another store.
For hermes specifically, the existing agent-history-importer skill's
Section 12 (zcode-schema.md) is the cheat sheet for writing hermes
SessionDB rows. Use crush_extract.py dump --format json as the input
source — messages.parts is already a parsed list, not a JSON string.
For Org / Markdown archive, --format md is the canonical form; pipe it
into ~/Documents/Org/conversations/crush-<date>-<id>.org using
terminal (hermes desktop file browser picks it up automatically).
~/.config/crush/.crush/crush.db is empty. It only holds the
goose_db_version migration row. All sessions are in <project>/.crush/.messages.id is not globally unique. Subagent tool invocations reuse
the parent session id with a $$<tool_call_id> suffix, so
SELECT DISTINCT id lies. Use id, session_id as a composite key.parts is a JSON TEXT array. Use json_each(messages.parts) to
filter by type. json_extract(parts, '$[0].type') only sees the first
element — Crush often emits [reasoning, text, tool_call, ...].read_files table is per-session, not per-message. It tracks which
files the agent opened in the editor; useful for "what did crush touch"
forensics but not part of the transcript.files table is a content-addressed cache, not user uploads. Don't
confuse with attachments.session.message_count is maintained by a trigger, but the trigger
only fires on INSERT/DELETE — a manual UPDATE to messages bypasses it.
verify catches drift.list output shows
only 16 chars; copy the full id from show --session if unsure..crush/...) — they're not nested in the parent
messages.parts. To follow a subagent, run find --root <project> and
look for a sibling DB.After running the full pipeline, the script should pass these three checks:
verify exits 0 and prints # message_count column matches actual count for all sessions ✓.list --since <today> --until <today> shows every session the user
remembers having today.dump --format md --out /tmp/x.md produces a non-empty file whose first
## [1] user heading matches the user's opening prompt.If any of these fail, the time unit is probably wrong, or the DB path points
at the global empty one. Re-run find --root ~ to confirm.
scripts/crush_extract.py — the only executable. Five subcommands, stdlib only.references/schema-cheatsheet.md — full DDL + every observed part.type sample JSON + field mapping table for importer authors.Diagnose and fix the dual-channel memory problem in Hermes Agent — when `memory.provider: holographic` (or any external provider) is configured but the agent keeps writing only to `memories/MEMORY.md`, bypassing it. Covers why agents default to the markdown file, the four root causes of routing failure, and three remediation options (SOUL.md rules / disable MEMORY.md / mirror via code). Triggers on 'memory 不生效', 'holographic 没在用', 'agent 都写到 MEMORY.md', '双 memory 系统', 'memory tool vs fact_store', 'on_memory_write', 'memory drift', 'Mirrors 不到', or any 'why is my agent ignoring the configured memory provider' question.
Use when the user works inside ~/Projects/Config/Guix-configs and mentions '改 dotfiles', 'blue home', 'guix system reconfigure', 'shepherd service', 'stow 死链', 'blue stow', 'AGENTS.md 翻新', 'fcitx', 'IME 没输入法', 'Electron 没输入法', 'wireplumber', '二轨 dotfiles', '恢复被删除的 dotfiles', or related. Ten sub-protocols — dotfiles deploy verification, worker delegation, multi-line edit safety, Guix service debugging, GNU Stow mutable config, restoring deleted dotfiles, ISO 移植, 需求澄清, 模块归属陷阱.
跨 agent KB 健康度维护。**触发信号**:每周/长会话后例行维护 / 卡片 >50 张 / 检索质量明显下降 / 用户触发 `/agenote-curate` / 发现重复卡片或矛盾结论 / 新增了对话抽取源。**当上述任一信号出现时立即调用本 skill** 做健康检查+去重+归档+权重重分配+reconcile 多源 memory。基础用法见 `agenote-base`;会话中单次经验记录见 `agenote-review`。
Maintain the personal Guix channel at ~/Projects/Config/jeans (Just Enough AI-geNerated Slops). Use when the user asks to "fix the CI build failure issue", "升级 X 包", "add a package", "check upstream updates", "跑 maak upgrade", "修复 auto-update 流水线的 issue
How to author a Hermes Agent skill the right way. Covers the two non-negotiable structural principles every skill must follow — **self-contained** (all runnable artifacts ship inside the skill directory so backup = usable) and **progressive disclosure** (SKILL.md is a thin router; details live under `references/`, `templates/`, `scripts/`) — the directory layout, file-type rules, decision trees for what goes in SKILL.md vs. a support file, **which of the 12 existing categories a new skill belongs to (never top-level `<skill-name>/`)**, and a checklist before declaring a skill done. Trigger when: writing a new skill, refactoring an existing skill's structure, wondering 'should this go in SKILL.md or references()' / 'which category directory should this skill live into', preparing a skill for backup/share, noticing a self-contained violation, or auditing existing skills. Also trigger when the user complains something is 'too verbose' or 'in the wrong place' — those are progressive-disclosure violations.
When the user says '校对文档 / 检查文档是否还有效 / sync doc with code / 扩写 README / 改写文档 / 把这个 PLAN/plan/任务书做成文档 / 文档并入主文档', or when a `refs/*.md` document may be drifting from the tool/CLI/源码 it describes, or when a PLAN file needs to become a user-facing reference doc. Covers three entry points — check, rewrite, plan-to-doc — backed by a `scripts/doc-check.py` checker that catches silent drift between a doc and its source-of-truth (CLI flags, MCP tool names, file paths). Trigger when the user names a doc path and asks whether it's still accurate, when adding a new CLI/MCP/API surface that old docs don't mention, or when a PLAN/任务书/thread needs to graduate into a stable reference.