No description
  • TypeScript 97.7%
  • Shell 2.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Morten Olsen 60801a7705
Share the scratchpad with Claude Code
The extension was omp-only. Add a Claude Code half over the same
~/scratchpad directory so a day planned in one agent is picked up by the
other, without either drifting into writing a day the other would not
have written.

Everything that decides what a day is now lives in shared code:
daily-scratchpad.tools.ts holds both tool descriptions and both tool
bodies, and buildSessionBlock does the day + carry-over lookup. The omp
extension gets thinner as a result — its registrations are adapters over
the same functions Claude calls, so names, date parsing, append
semantics and the budget verdict are one implementation.

The Claude half is two adapters: an MCP stdio server for the tools, and
a SessionStart hook for the injected block (with --print backing
/scratchpad). A resumed session is skipped since its transcript carries
the block; a compacted one is re-injected, since compaction drops the
block but leaves it in the transcript.

Tool names in the block take a prefix, because Claude addresses an MCP
server through a namespaced name and a block pointing at an uncallable
tool is worse than one pointing at nothing.

First runtime dependency (the MCP SDK), so install is no longer types-only.
2026-09-23 10:07:27 +02:00
.claude-plugin Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
commands Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
hooks Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
scripts Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
src Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
.gitignore init 2026-09-17 14:31:54 +02:00
bun.lock Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
mise.toml init 2026-09-17 14:31:54 +02:00
package.json Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
README.md Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
Taskfile.yml Share the scratchpad with Claude Code 2026-09-23 10:07:27 +02:00
tsconfig.json init 2026-09-17 14:31:54 +02:00

pi-daily-scratchpad

One markdown file per day that an agent can author. Every new session gets today's scratchpad injected; the agent can update it during the day and read or edit any other day.

Outline your day in the morning, let the agent keep it honest as you go, and tomorrow ask what you did yesterday.

Not a memory system: flat files, two tools, no retrieval logic, no index.

Two agents, one scratchpad. It ships as an omp extension and a Claude Code plugin over the same directory. Plan the day in one, wrap it up in the other — they read and write the same files, through the same two tools, under the same rules, so neither can drift into writing a day the other would not have written. The host-specific parts are an adapter each; everything that decides what a day looks like is shared code.

The flow it is built for

Nothing here is automatic. The agent writes when you ask it to, or when the conversation clearly produced something worth keeping — the injected block tells it the file exists and invites an update, it does not run a capture loop behind your back. If you never ask, the day stays empty.

Morning — outline the day. Plain prose is enough; the agent turns it into today's file.

Today: finish the scratchpad plugin, review the homelab CI PR, call the dentist.

If today is still empty, the block already showed the agent the last written day as carry-over, so this works too:

What's still open from last time? Start today's list with the parts that matter.

During the day — keep it honest. Append as things land or change. append is the default mode, so ordinary requests never clobber the morning plan.

Note that the CI review is blocked on Renovate, and add "write the README".

End of day — capture what actually happened. This is the entry that makes tomorrow useful, and the one most worth asking for explicitly.

Wrap up the day on the scratchpad: what we actually did, what's left.

Next morning — recall. The new session has today injected; yesterday is one tool call away.

What did I do yesterday, and where did we leave the plugin?

Ahead and behind. Future days are planning, past days are corrections.

Put "dentist 09:00" on tomorrow's scratchpad.

I did finish the CI review on Monday — fix that day's entry.

Across days. Search is the filesystem, not a tool: the agent has the directory path and greps it.

When did I last touch the homelab network config? Grep the scratchpads.

Mid-session, a write lands on disk but not in this conversation. The agent knows what it wrote; if you edited a file by hand, use /scratchpad to re-inject it.

What belongs on a day

An index, not a workspace. One terse line per item, anchored to something that already exists — a Linear issue, a GitHub PR, a calendar event, a spec path — carrying the reference rather than a restatement of it. The work happens when the task starts, from the source; the scratchpad only says it is on the list.

## Plan

- Review ZAP-462 (Linear)
- Review PR #486 in zap-core
- 09:30 ZAP Core daily
- Decided: e2e stays out of CI, local `make e2e` only

Content that has no home elsewhere — a decision, a loose thought, something said out loud — belongs here too, as in the last bullet above. That is the exception, not the rule: prose standing in for an available reference is what turns a day into a wall of text.

Two budgets keep it honest, since this file rides along in every session whatever the topic:

  • Over 1500 characters, the injected block and the write result both say so and ask for the day to be condensed with mode: replace.
  • Over 6000 characters, only the head is injected, with a pointer to the file — the agent can still read it in full, but an oversized day stops being billed to unrelated conversations.

Both live in daily-scratchpad.context.ts (SOFT_BUDGET, HARD_CAP).

Install

task link      # both agents

Then restart omp and Claude Code. task unlink removes both again; each half also has its own task link-omp / task link-claude if you only want one.

There is no build step on either side — both ship as TypeScript and run through bun. omp loads src/daily-scratchpad.ts directly via the omp.extensions manifest field; Claude Code starts src/daily-scratchpad.mcp.ts as a stdio MCP server and runs src/daily-scratchpad.inject.ts from a SessionStart hook. task check type-checks everything, task validate-claude checks the plugin manifests.

Claude Code installs from this directory as a local marketplace, which is why uninstalling takes the marketplace with it. Without Task, that is:

bun install                                          # the MCP SDK is a runtime dep
omp plugin link .
claude plugin marketplace add .
claude plugin install daily-scratchpad@daily-scratchpad

Hooks and MCP servers start from a non-interactive shell that may not have the mise-managed bun on PATH, so both go through scripts/bun.sh, which looks in the usual install locations before giving up.

Storage

One flat directory, one file per calendar day:

~/scratchpad/2026-09-17.md
~/scratchpad/2026-09-16.md

Set DAILY_SCRATCHPAD_DIR to move it (~ is expanded). The directory is a plain directory under $HOME on purpose: it survives reinstalling either agent, it is what makes one scratchpad readable by both, and it can be synced or version-controlled.

The directory path is injected along with the content, so cross-day search is just grep/read/glob over the directory — there is no search tool here.

What the agent gets

Injected at session start: today's content, today's date, the file path, the scratchpad directory, and which earlier days exist on disk. In omp that is a hidden custom message attached to your first message; in Claude Code it is a SessionStart hook returning additionalContext. Same block either way.

If today has nothing written yet, the most recent day that does is included instead, labelled as carry-over and explicitly not today's plan — so unfinished work and open threads get picked up rather than rediscovered. Empty days in between are skipped. Once today has content, only today is injected.

The injected snapshot is frozen for the session. A write changes the file immediately but not this conversation — the agent knows what it wrote, and the next session reads the updated file. That is the whole design: no live context mutation, no reconciliation. It is also what makes sharing the file safe: a day written in one agent is picked up by the other's next session, not raced for mid-conversation.

A resumed session is not re-injected — its transcript already carries the block — but a compacted one is, since compaction drops the block while leaving it in the transcript.

scratchpad_write — content, optional date, optional mode: append | replace. append (default) adds after what is already there, separated by a blank line; replace overwrites the day.

scratchpad_read — optional date. For another day, or to re-read today after writing.

date accepts YYYY-MM-DD, today, yesterday, tomorrow, or a signed day offset (-3, +1 — the latter is how you plan ahead). Anything else is rejected rather than guessed, so a typo never writes into the wrong day.

/scratchpad [date] — re-inject a day into the current conversation, for example after editing a file by hand, after the other agent wrote to it, or to pull up yesterday mid-session.

In Claude Code the two tools arrive through an MCP server, so their full names are mcp__daily-scratchpad__scratchpad_read / …_write — which is what the injected block points at. If that namespace ever changes, set DAILY_SCRATCHPAD_TOOL_PREFIX on the hook rather than editing the block.

Layout

Shared — everything that decides what a day is and what goes in it:

File Role
daily-scratchpad.store.ts disk layer: paths, read/write, which days exist
daily-scratchpad.dates.ts YYYY-MM-DD resolution and day arithmetic
daily-scratchpad.context.ts the injected block, and the budgets
daily-scratchpad.tools.ts the two tools: descriptions and bodies

One adapter per host, each doing nothing but translation:

File Role
daily-scratchpad.ts omp extension: injection, tools, /scratchpad
daily-scratchpad.mcp.ts Claude Code: the two tools as a stdio MCP server
daily-scratchpad.inject.ts Claude Code: SessionStart hook and /scratchpad

Manifests: package.json (omp.extensions) for omp; .claude-plugin/, hooks/ and commands/ for Claude Code. Both describe the same directory, so one task link installs into both.