Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "past",
"source": "./plugins/past",
"description": "Your Claude Code sessions become memory, and every session starts briefed.",
"version": "0.1.1",
"version": "0.2.0",
"author": {
"name": "past.dev",
"url": "https://past.dev"
Expand Down
41 changes: 31 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ whoever changes it.

```
Claude Code
hooks/hooks.json
hooks/hooks.json, .mcp.json
commands/*.md, skills/past
|
bin/past-hook.mjs <mode>
|
session start brief recall into context; sends idle sessions left behind
every prompt prompt recall into context
every prompt prompt recall into context: ten memories, the block naming the tool
on demand mcp past_recall, the model's own recall, on a local MCP server
every turn stop stamps the activity file, keeps one waiter on the session
compaction precompact sends the session so far
session end end hands the send to a detached process (flush)
Expand All @@ -34,6 +35,7 @@ Claude Code
plugins/past/
.claude-plugin/plugin.json the manifest, including the settings form (userConfig)
hooks/hooks.json SessionStart, UserPromptSubmit, Stop, PreCompact, SessionEnd
.mcp.json past_recall: the script's mcp mode, started by Claude Code
bin/past-hook.mjs the whole connector, one file, no dependencies
commands/ /past:connect, :status, :cut, :ingest, :backfill
skills/past/SKILL.md teaches Claude when to search history
Expand Down Expand Up @@ -162,6 +164,15 @@ plugins/past/
the one it was written for (`SessionStart`, `UserPromptSubmit`, `Stop`, `PreCompact`,
`SessionEnd`) and otherwise stays silent: no recall is spent on that agent's prompts, and none
of its conversations is booked as a session. What Claude Code's own sessions send is unchanged.
15. **The recall tool speaks JSON-RPC alone on stdout.** `mcp` mode is a stdio MCP server, started by
Claude Code from `.mcp.json` and alive as long as the session. Nothing else may print there:
like every mode it swallows its own failures, and a failed recall is a tool result that says so
(`isError`), never a crash. A call is one recall, redacted as every query is (rule 5), and reads
the same `~/.past/config.json`. Its page (up to a hundred memories within 2,000 tokens, each with
its moment, a "replaced" mark and the source excerpts) and the prompt hook's ten memories were
set by measurement: change them with a measurement. The block before a prompt names the tool in
one line, which is how the model learns to call it; the line rides inside a block the script
already drops from what it sends (rule 13), so it changes nothing that travels.

## Beside other past.dev plugins

Expand All @@ -172,14 +183,15 @@ one more reason `state.json` changes only through `updateState` (rule 8). In `ac
plugin's files are at the top; a folder in it belongs to another plugin, and the waiters only look
at `.json` files.

## Why the API and not MCP
## Hooks first, and a tool beside them

Hooks are shell commands and cannot speak MCP. Recall through a hook is injected on every prompt;
recall through an MCP tool happens only if the model chooses to call it. The guarantee is the
product, so the hooks use `/api/v1/recall` and `/api/v1/ingest/batch`.

MCP stays the right surface for Claude web and desktop, and for conversational lookups. Adding a
`.mcp.json` here later is additive and needs the project's MCP server enabled first.
Hooks are shell commands and cannot speak MCP, so the write path is the API. Recall comes two ways.
The hooks inject it on every prompt, whether or not the model would have looked: that is the
guarantee, and it is the product. Beside them the plugin serves `past_recall` on a local MCP server,
so the model can ask again, with its own question, for what the block does not hold. The tool calls
the same `/api/v1/recall` with the project key in `~/.past/config.json`; it needs no MCP server on
the project's side. MCP alone stays the right surface for Claude web and desktop, where past.dev
cannot read the conversation and can only be recalled from.

## Contracts this depends on

Expand All @@ -189,14 +201,18 @@ MCP stays the right surface for Claude web and desktop, and for conversational l
items and 16 MB of content.
- `POST /api/v1/recall` — **`identity` is required**. Recall stays off until `/past:connect`
sets one. The response is one result per document in rank order; the hooks read each result's
`content` and `occurredAt`.
`content` and `occurredAt`, and the tool also its sources' `excerpts` and `metadata.localTime`
and its artifact's `supersededAt`. The prompt hook sends `limit` 10; the tool sends `limit` 100
and `maxTokens` 2,000.
- `audience` on ingest — a slug; absent means the whole project. `connect --audience` checks it
with `GET /api/v1/audiences/{slug}` (404 when unknown); a later `audience-unknown` refusal is
kept in `state.lastError` for `/past:status`, like every failed send.
- `UserPromptSubmit` gives the prompt as `prompt` on stdin.
- Claude Code gives a hook `session_id`, `transcript_path`, `cwd` and `hook_event_name`, and
takes `hookSpecificOutput.additionalContext` at session start and on a prompt.
- A project API key (`past_sk_…`) names its own project. The plugin never sends a project id.
- Claude Code starts a plugin's MCP server from the `mcpServers` file its manifest names, expands
`${CLAUDE_PLUGIN_ROOT}` in its arguments, and names its tool `mcp__plugin_past_past__past_recall`.

If any of these contracts changes, this file changes with it.

Expand All @@ -223,6 +239,11 @@ logging what arrives, and the mode under test fed its stdin JSON by hand (`echo
'{"hook_event_name":"Stop","session_id":…,"transcript_path":…,"cwd":…}' | node plugins/past/bin/past-hook.mjs stop`). A
small `idleMinutes` in that config makes the idle timer testable in seconds.

For the tool, pipe JSON-RPC lines (`initialize`, `tools/list`, `tools/call`) into `node
plugins/past/bin/past-hook.mjs mcp` in the same throwaway home, with a stand-in answering `POST
/api/v1/recall`, and check what the request carried; then install the plugin from the folder into a
throwaway `CLAUDE_CONFIG_DIR` and see `claude mcp list` report `plugin:past:past` connected.

**A change to the script is checked against what it sent before.** Run the old script and the new
one over the same transcripts, each in a throwaway home with its own stand-in, and compare what each
mode prints, what each request carries and what `state.json` keeps, byte for byte (rule 10).
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ reasons behind them, what was already tried and dropped.
```

That block is what Claude reads before your prompt. It arrives on its own: Claude does not have
to decide to look anything up.
to decide to look anything up. When a request needs more than the block holds, Claude asks for it
itself, with the `past_recall` tool the plugin serves.

## Get started

Expand All @@ -29,17 +30,14 @@ You need Claude Code and Node 16 or later.

2. **Install the plugin.** In Claude Code:

```
/plugin install past --marketplace pastdotdev/claude-code-plugin
```

Claude Code older than v2.1.275 adds the marketplace first:

```
/plugin marketplace add pastdotdev/claude-code-plugin
/plugin install past@past
```

The first line adds this repository as a marketplace. The second line installs the plugin
from it.

In the Claude desktop app, or on claude.ai: open **Customize › Plugins**, select **Add › Add
marketplace** and enter `pastdotdev/claude-code-plugin`, then open **past.dev** and select
**Add**. The plugin is saved to your account and loads in Claude Code, in the app's Code tab and
Expand All @@ -66,12 +64,14 @@ instead of step 3: a form that masks the key and keeps it in your system's crede
```text
session opens recall what past.dev knows about this project ──► into context
every prompt recall what past.dev knows about this prompt ──► into context
any time Claude asks past.dev itself, with past_recall ──► into context
every reply nothing is sent; the session is marked active
compaction the session so far is sent, before its detail is summarised away
session over the session is sent: closed, its window quit, or quiet for 12 hours
```

Five hooks do this, and one script runs them all. A recall that has not answered within 4 seconds
Five hooks and one tool do this, and one script runs them all. The block before a prompt holds
up to ten memories; the tool returns up to a hundred, within 2,000 tokens. A recall that has not answered within 4 seconds
(8 at session start) is dropped, so it never holds a prompt up, and a prompt shorter than 12
characters ("yes", "go on") recalls nothing.

Expand Down
3 changes: 2 additions & 1 deletion plugins/past/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"name": "past",
"displayName": "past.dev",
"version": "0.1.1",
"version": "0.2.0",
"description": "Your Claude Code sessions become memory, and every session starts briefed.",
"author": { "name": "past.dev", "url": "https://past.dev" },
"homepage": "https://github.com/pastdotdev/claude-code-plugin",
"repository": "https://github.com/pastdotdev/claude-code-plugin",
"license": "MIT",
"keywords": ["memory", "recall", "context"],
"hooks": "./hooks/hooks.json",
"mcpServers": "./.mcp.json",
"commands": ["./commands/connect.md", "./commands/status.md", "./commands/cut.md", "./commands/ingest.md", "./commands/backfill.md"],
"skills": "./skills/",
"userConfig": {
Expand Down
8 changes: 8 additions & 0 deletions plugins/past/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"past": {
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/bin/past-hook.mjs", "mcp"]
}
}
}
21 changes: 12 additions & 9 deletions plugins/past/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ Your Claude Code sessions become memory, and every session opens already briefed

## Install

```
/plugin install past --marketplace pastdotdev/claude-code-plugin
```

On Claude Code older than v2.1.275, `--marketplace` does not exist, so it is two steps:

```
/plugin marketplace add pastdotdev/claude-code-plugin
/plugin install past@past
```

The first line adds this repository as a marketplace. The second line installs the plugin from it.

In the Claude desktop app, or on claude.ai: open **Customize › Plugins**, select **Add › Add
marketplace** and enter `pastdotdev/claude-code-plugin`, then open **past.dev** and select **Add**.
The plugin is saved to your account and loads in Claude Code, in the app's Code tab and in the
Expand All @@ -38,7 +34,7 @@ prompt or session.
| Hook | When | What |
|---|---|---|
| `SessionStart` | a session opens | recalls this project's history and puts it in context |
| `UserPromptSubmit` | every prompt | recalls against the prompt and puts it in context |
| `UserPromptSubmit` | every prompt | recalls the ten memories closest to the prompt and puts them in context |
| `Stop` | after each reply | starts or resets the idle timer; sends nothing itself |
| `PreCompact` | the context is about to be compacted | sends the session so far, before the detail is summarised away |
| `SessionEnd` | a session closes | sends the session's prose to past.dev |
Expand Down Expand Up @@ -93,8 +89,15 @@ to the credit that is roughly 90 credits for a session.
Without an audience everyone in the project sees what you send. `/past:connect --audience <slug>`
narrows it to an audience from the console; `--audience project` widens it back.

The bundled skill searches past.dev on demand: ask Claude what was decided, or whether something was
tried before, and it looks.
**And a recall tool.** The plugin also serves `past_recall`, on a small MCP server of its own
(`.mcp.json`, run by the same script). The block before a prompt holds what was recalled for it;
when the request depends on something that block does not hold, Claude calls `past_recall` with a
question of its own and reads up to a hundred dated memories, within 2,000 tokens. Each call is one
recall. Claude Code asks you before the first call: allow it, for the session or for good.
`claude mcp list` shows the server as `plugin:past:past`.

The bundled skill teaches Claude when to look history up: ask what was decided, or whether
something was tried before, and it looks.

## Configuration

Expand Down
Loading
Loading