Maintains a narrative log of exploratory work with file archives and git state tracking. Capabilities: (1) Log entries with propose-confirm flow, (2) Archive files linked to entries, (3) Capture git state (commit hash), (4) Create git commits with entry as message, (5) Restore archived files, (6) Query past work by time or topic, (7) Link related entries for thread tracking. Activates when user addresses "scribe" directly (e.g., "hey scribe, log this", "scribe, save this notebook", "scribe, what did we try yesterday?") or uses `/scribe` commands.
Resources
2Install
npx skillscat add robdmc/claude-tools/scribe Install via the SkillsCat registry.
Scribe
The scribe maintains a narrative log of your exploratory work, can archive important files, and tracks git state.
Address naturally: "hey scribe, log this" / "scribe, save this notebook" / "scribe, what did we try yesterday?"
Quick Reference
| Mode | Trigger | Action |
|---|---|---|
| Log | "scribe, log this" | Propose → confirm → prepare → Edit → finalize |
| Git entry | "scribe, git entry" | Same flow with --git-entry flag (creates commit) |
| Archive | "scribe, save notebook.ipynb" | Same flow with --archive flag |
| Restore | "scribe, restore the ETL script" | Copy from assets |
| Query | "scribe, what did we try?" | Search and summarize |
Important: Git entries ONLY happen when user explicitly says "git". Never auto-promote.
CRITICAL: Always Propose Before Finalizing
NEVER run entry.py prepare or entry.py finalize without first:
- Showing the user the proposed entry text (title + body) in a markdown block
- Getting explicit user approval ("ok", "yes", "looks good", etc.)
This applies to ALL entry types (log, git-entry, archive).
Example Propose Step
Here's the entry I'll create:
Title: Migrate signup forecast to Parquet format
Body: Removed all DuckDB dependencies, switching to a simpler Parquet-based workflow. The notebook now loads from
subscriptions.parquetand saves tosignup_forecast.parquet...Files touched:
compute_signup_forecast.nb.pyDoes this look good?
Directory Structure
.scribe/
├── 2026-01-23.md # Daily log files (tracked in git)
├── assets/ # Archived files (gitignored)
└── diffs/ # Legacy diffs (gitignored, not created for new entries)Entry Flow
- Assess — Check context, recent logs,
git status - Propose — Draft the full entry text (title and body) and show it to the user in a markdown block. Ask "Does this look good?" or "Should I proceed?"
- Confirm — STOP and WAIT for explicit user approval ("ok", "yes", "looks good", etc.). Do NOT proceed until user confirms.
- Prepare —
entry.py prepare [options]→ outputs staging file path - Edit title — Replace
__TITLE__in staging file - Edit body — Replace
__BODY__in staging file - Finalize —
entry.py finalize→ validates, appends to log, archives files
Prepare Command
uv run --project {SKILL_DIR}/scripts python {SKILL_DIR}/scripts/entry.py prepare [options]| Option | Description |
|---|---|
--git-entry |
Create git commit when finalizing |
--touched "file.py:desc" |
Add to Files touched: section (repeatable) |
--archive "file.py:desc" |
Archive file when finalizing (repeatable) |
--related ID |
Add to Related: section with auto title lookup (repeatable) |
Outputs the staging file path (e.g., .scribe/__2026-01-23-14-35__.md).
Staging File
The staging file contains placeholders to fill:
---
id: 2026-01-23-14-35
timestamp: "14:35"
title: __TITLE__
git: abc123
_pending:
git_entry: false
---
## 14:35 — __TITLE__
__BODY__
**Files touched:**
- `etl.py` — Added coalesce logic
---Use the Edit tool to replace __TITLE__ and __BODY__ with actual content.
Finalize Command
uv run --project {SKILL_DIR}/scripts python {SKILL_DIR}/scripts/entry.py finalizeThis validates placeholders are filled, strips _pending, archives files, appends to daily log, and (if git-entry) creates commit.
Scripts
Scripts in {SKILL_DIR}/scripts/. Run with: uv run --project {SKILL_DIR}/scripts python {SKILL_DIR}/scripts/<script.py>
| Script | Purpose |
|---|---|
entry.py prepare |
Create staging file with placeholders |
entry.py finalize |
Complete entry (archive, append, commit) |
entry.py abort |
Delete staging file (recover from interruption) |
entry.py status |
Show pending entry state |
entry.py last --with-title |
Get last entry ID (for --related) |
assets.py save <id> <file> |
Archive a file |
assets.py list [filter] |
List archived files |
assets.py get <asset> --dest <dir> |
Restore a file |
validate.py |
Check for errors |
Entry Structure
Daily logs (.scribe/YYYY-MM-DD.md) contain entries:
---
id: 2026-01-23-14-35
timestamp: "14:35"
title: Entry title here
git: abc1234
---
## 14:35 — Entry title here
Narrative...
**Files touched:**
- `file.py` — Description
---Searchable fields: id:, title:, git:, mode: git-entry, **Related:**, **Archived:**
Querying
- Time-based: Read
.scribe/YYYY-MM-DD.mddirectly - Topic-based:
Grepin.scribe/, then Read matches - Assets:
assets.py list [filter]
Error Recovery
| Command | Use |
|---|---|
entry.py abort |
Discard pending entry |
entry.py status |
Check pending state |
entry.py edit-latest show |
View last finalized entry |
entry.py edit-latest delete |
Remove last entry + assets |
entry.py edit-latest replace --file |
Replace last entry content |
Principles
- Narrator, not stenographer — Write prose, not dumps
- Capture the why — Not just what, but why it was tried
- Stay concise — Entries should be scannable
- Preserve dead ends — Failed approaches prevent repeats
- Git state anchors context — Commit hash precisely identifies code state
Reference Files
For detailed examples: references/entries.md | references/querying.md | references/recovery.md