Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c07393f
feat(core): add harness envelope and redaction floor for bm hook fron…
phernandez Jul 15, 2026
3e3b85f
feat(core): add hook inbox WAL and harness stdin adapters
phernandez Jul 15, 2026
2d7c1c1
feat(core): add deterministic hook projector
phernandez Jul 15, 2026
af38809
feat(cli): add bm hook command group
phernandez Jul 15, 2026
6d34fe5
refactor(plugins): reduce claude and codex hooks to bm hook shims
phernandez Jul 15, 2026
a3ab49c
feat(cli): add bm hook install and remove with ownership-tagged merging
phernandez Jul 15, 2026
956a693
chore(plugins): bump the shim uvx floor from the release version updater
phernandez Jul 15, 2026
f494950
docs(plugins): document uv prerequisite, uvx floor, and captureEvents…
phernandez Jul 15, 2026
63bc6c8
fix(core): address review findings in bm hook front door
phernandez Jul 16, 2026
7fd1d03
fix(core): address PR #1070 review — hook probe, path redaction, Wind…
phernandez Jul 16, 2026
aacb9ab
fix(core): address PR #1070 round-2 review — tilde paths, shim fail-open
phernandez Jul 16, 2026
709a622
fix(core): redact envelope and checkpoint cwd (PR #1070 round-3 review)
phernandez Jul 16, 2026
e908d94
fix(core): fix projector replay-retire race and bound unmapped inbox …
phernandez Jul 16, 2026
c04e0c8
fix(core): dedup processed replay history on projector rebuild (PR #1…
phernandez Jul 16, 2026
0a39f5f
fix(core): route on project hint carried by an in-group replay (PR #1…
phernandez Jul 16, 2026
3d6e78a
fix(core): resolvable install command + flush tolerance for vanished …
phernandez Jul 16, 2026
daf3b4e
fix(core): probe PATH launchers before baking them into installed hoo…
phernandez Jul 16, 2026
14e6d3e
fix(core): recall generic session notes in the Codex brief (PR #1070 …
phernandez Jul 16, 2026
481b308
fix(core): redact Codex working-tree rows in checkpoints (PR #1070 ro…
phernandez Jul 16, 2026
a5f8798
fix(core): projector note_type, exact-dir redaction, envelope types, …
phernandez Jul 16, 2026
0f1f903
fix(core): skip CLI init for hook commands, redact roots before punct…
phernandez Jul 16, 2026
85cc94c
fix(plugins): restore uv tool run fallback for hook launchers (PR #10…
phernandez Jul 16, 2026
0cb528e
fix(core): don't prune write-failed envelopes; fail-open hook config …
phernandez Jul 16, 2026
a08ff88
fix(core): scope hook fail-open so operator verbs aren't silenced (PR…
phernandez Jul 16, 2026
a71b154
fix(core): install uvloop on the hook path before async work (PR #107…
phernandez Jul 16, 2026
d5b65a2
fix(core): catch SystemExit on the hook fail-open paths (PR #1070 rou…
phernandez Jul 16, 2026
510525e
fix(core): guard hook logging setup against config SystemExit (PR #10…
phernandez Jul 16, 2026
d9b2c20
fix(core): make pending retention session-aware (PR #1070 round-19)
phernandez Jul 16, 2026
6da90df
fix(core): create the hook inbox with owner-only permissions (PR #107…
phernandez Jul 16, 2026
a636e89
fix(core): expand user redactPaths; serialize checkpoint frontmatter …
phernandez Jul 16, 2026
56b76fd
fix(core): preserve the brief's closing fence under truncation (PR #1…
phernandez Jul 16, 2026
d883a05
fix(plugins): strip copied quotes from a multi-token BM_BIN (PR #1070…
phernandez Jul 16, 2026
93245ed
fix(core): bound the brief fence; route on full processed history (PR…
phernandez Jul 16, 2026
ac9702e
fix(core): reject negative retention; case-insensitive deny paths on …
phernandez Jul 16, 2026
5723092
fix(core): redact whole-value denied paths containing spaces (PR #107…
phernandez Jul 16, 2026
9931d5f
fix(core): redact embedded denied paths with spaced directories (PR #…
phernandez Jul 16, 2026
8ff92a7
fix(core): fix Windows deny-path redaction failures (PR #1070)
phernandez Jul 16, 2026
d6f60d8
refactor(core): value objects + Pydantic for the hooks module (PR #1070)
phernandez Jul 16, 2026
64b7f2e
fix(core): serialize projector artifact frontmatter via metadata= (PR…
phernandez Jul 17, 2026
14b07c7
fix(core): serialize concurrent projector flushes with an inbox lock …
phernandez Jul 17, 2026
d651ba2
fix(core): skip checkpoint working-tree section for denied workspaces…
phernandez Jul 17, 2026
5d61444
fix(cli): rewrite harness configs atomically in bm hook install/remov…
phernandez Jul 20, 2026
6cd43b7
fix(plugins): pin LF line endings for shell shims via .gitattributes …
phernandez Jul 20, 2026
c0bc0ab
fix(cli): preserve harness config file modes across atomic rewrites (…
phernandez Jul 20, 2026
3db833e
fix(cli): recreate stale config tmp files at the target's mode (PR #1…
phernandez Jul 20, 2026
123a44f
fix(core): collapse control characters in provenance observation line…
phernandez Jul 20, 2026
b82454f
refactor(plugins): replace bash hook shims with PEP 723 uv scripts
phernandez Jul 20, 2026
f7dd197
fix(core): single-line every dynamic line in projected note bodies (P…
phernandez Jul 20, 2026
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
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Shell scripts must reach every checkout with LF endings. Marketplace plugin
# installs are git clones, and Git for Windows defaults to core.autocrlf=true;
# a CRLF-converted hook shim makes bash exit 2 with syntax errors on every
# session start — breaking the hooks' fail-open contract exactly where the
# uvx fallback is meant to protect users without a local install.
*.sh text eol=lf
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# CHANGELOG

## Unreleased

### Features

- **#997**: Added the `bm hook` harness front door (SPEC-55). Lifecycle verbs
(`session-start`, `pre-compact`) move the plugin hook logic into the package
behind per-harness stdin adapters, with the session brief fenced as reference
data. Opt-in envelope capture (`captureEvents: true`, fail-closed) records
redacted lifecycle events into a local inbox WAL, projected deterministically
by `bm hook flush`; `bm hook status` shows the surface. `bm hook install` /
`bm hook remove` wire the hooks into user-level harness config for standalone
users with ownership-tagged, surgical merging. The Claude Code and Codex
plugin hooks are now zero-logic PEP 723 uv scripts (`uv run --script`) that
invoke `basic-memory hook` in-process; their dependency floor is bumped by
release tooling, and `BM_BIN` overrides the uv-managed environment for
development.

## v0.22.1 (2026-06-12)

Follow-up patch to v0.22.0. Fixes project and default-project resolution on
Expand Down
14 changes: 12 additions & 2 deletions plugins/claude-code/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ Memory's durable graph**, rather than a memory layer of its own. See
plugin-namespaced commands (`/basic-memory:<name>`).


- **SessionStart hook** (`hooks/session-start.sh`) — briefs Claude at session
- **SessionStart hook** (`hooks/session_start.py`) — briefs Claude at session
start with active tasks from the graph (one structured `type: task` query) plus
an always-on recall prompt. Works against the default project with zero config;
pin a project via `basicMemory.primaryProject`. Plain-stdout output, capped well
under the 10k limit, and silent if Basic Memory isn't installed.
- **PreCompact hook** (`hooks/pre-compact.sh`) — writes a `type: session`
- **PreCompact hook** (`hooks/pre_compact.py`) — writes a `type: session`
checkpoint to the graph before context compaction (extractive in this phase;
LLM-summarized capture is the next step). Only writes when a `primaryProject` is
configured, so it never touches a graph the user hasn't opted in.
Expand All @@ -62,6 +62,16 @@ Memory's durable graph**, rather than a memory layer of its own. See

### Changed

- **Hooks are now zero-logic uv scripts** (SPEC-55, #997). `session_start.py`
and `pre_compact.py` are self-contained PEP 723 scripts run via
`uv run --quiet --script`: uv resolves `basic-memory>=<floor>` (floor bumped
by release tooling) and the script invokes
`basic-memory hook <event> --harness claude` in-process with the hook JSON
on stdin. `BM_BIN` overrides the uv-managed environment for development.
The brief/checkpoint logic lives in the released package; opt-in
`captureEvents: true` additionally records redacted event envelopes to a
local inbox. uv is the required prerequisite; the first run fetches from
PyPI, later runs use uv's cache.
- **SessionStart hook now nudges toward `/basic-memory:bm-setup` on first run** — when
no `basicMemory` config block is present in either settings file. The nudge
survives a failed/empty task query (so a brand-new user with no project yet still
Expand Down
8 changes: 4 additions & 4 deletions plugins/claude-code/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ plugins/claude-code/
│ ├── plugin.json
│ └── marketplace.json
├── hooks/
│ ├── session-start.sh # ambient: brief Claude on what's relevant
│ └── pre-compact.sh # ambient: checkpoint before amnesia
│ ├── session_start.py # ambient: brief Claude on what's relevant
│ └── pre_compact.py # ambient: checkpoint before amnesia
├── output-styles/
│ └── basic-memory.md # reflexes: search first, capture decisions
│ # NOTE: rules/ deferred — path-scoped rules don't load yet (Q5)
Expand Down Expand Up @@ -476,10 +476,10 @@ eventual default is `"summarized"` — the LLM pass is the first enrich step.
(`status?(enum, desc): [a,b]`), not after the `[...]` value — the latter is invalid
YAML. (Same latent bug exists in the canonical `memory-tasks` schema → flagged as a
separate task.)
- [x] Write `hooks/session-start.sh` — plain-stdout brief, single `type: task` /
- [x] Write `hooks/session_start.py` — plain-stdout brief, single `type: task` /
`status: active` query, recall prompt; works against the default project (pin via
`primaryProject`); silent if BM absent. Tested end-to-end.
- [x] Write `hooks/pre-compact.sh` — **extractive** checkpoint conforming to the
- [x] Write `hooks/pre_compact.py` — **extractive** checkpoint conforming to the
`session` schema; only writes when `primaryProject` is set; silent/no-op otherwise.
Tested end-to-end (note written + queryable by `--type session`).
- [x] Write `output-styles/basic-memory.md` — reflexes; `keep-coding-instructions: true`
Expand Down
35 changes: 30 additions & 5 deletions plugins/claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,37 @@ Plugin skills are namespaced under the plugin name:

## Requirements

- [Basic Memory](https://github.com/basicmachines-co/basic-memory) `>= 0.19.0`
connected as an MCP server. `uv tool install basic-memory` is recommended (it puts
a `basic-memory` binary on PATH, which the hooks call directly). A `uvx
basic-memory mcp`-only setup also works — the hooks fall back to `uvx`/`uv` when no
binary is on PATH.
- **[uv](https://docs.astral.sh/uv/)** — required: the hooks are PEP 723
scripts executed via `uv run --script`. Install per platform:
- macOS: `brew install uv` (or the curl installer below)
- Linux/macOS: `curl -LsSf https://astral.sh/uv/install.sh | sh`
- Windows: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
- [Basic Memory](https://github.com/basicmachines-co/basic-memory) connected as an
MCP server. The hooks resolve their own `basic-memory` through uv, so no
PATH install is required for them.
- Claude Code.

### What the hooks execute

The hook scripts are zero-logic PEP 723 uv scripts: `uv run --quiet --script`
resolves `basic-memory>=<floor>` (pinned in each script's inline metadata) and
the script invokes `basic-memory hook <event>` in-process with the hook JSON
on stdin. All behavior lives in the released Python package — versioned,
typed, and tested. Setting `BM_BIN` (a binary path or a quoted launcher
string) overrides the uv-managed environment, e.g. for development against a
local checkout. Two disclosures:

- **Network fetch on first run.** uv downloads `basic-memory` from PyPI at a
pinned minimum version (bumped by release tooling); later runs use uv's
cache.
- **Event capture is opt-in and off by default.** Setting `captureEvents: true`
(the JSON boolean — strings never enable it) records redacted lifecycle-event
envelopes to a local inbox under your Basic Memory home. Inspect with
`basic-memory hook status`, project with `basic-memory hook flush`.

Every failure path exits 0 — the hooks stay invisible rather than disrupt a
session.

## Installation

```bash
Expand Down Expand Up @@ -101,6 +125,7 @@ settings (or select it via `/config`).
| `recallTimeframe` | `3d` | Recency window for the session brief |
| `recallPrompt` | _(built-in)_ | The instruction appended to the brief |
| `preCompactCapture` | `extractive` | How checkpoints are produced |
| `captureEvents` | `false` | Opt-in: record redacted lifecycle-event envelopes to the local inbox (see `basic-memory hook status` / `flush`). Only the JSON boolean `true` enables it. |

See [DESIGN.md](./DESIGN.md) for the complete configuration schema, the
Claude-Code-project ↔ Basic-Memory-project mapping, and team-workspace behavior.
Expand Down
6 changes: 3 additions & 3 deletions plugins/claude-code/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ of Claude *before the first prompt*, so the session starts oriented instead of c
```mermaid
sequenceDiagram
participant CC as Claude Code
participant H as session-start.sh
participant H as session_start.py
participant BM as Basic Memory (bm CLI)
participant C as Claude

Expand Down Expand Up @@ -95,7 +95,7 @@ would be lost), the hook writes a durable checkpoint so the next session can res
```mermaid
sequenceDiagram
participant CC as Claude Code
participant H as pre-compact.sh
participant H as pre_compact.py
participant BM as Basic Memory

CC->>H: PreCompact (transcript_path, cwd)
Expand Down Expand Up @@ -157,7 +157,7 @@ project names collide across workspaces. Reads route over the user's OAuth sessi

| Path | Role |
|------|------|
| `hooks/session-start.sh`, `hooks/pre-compact.sh` | the ambient bridge (read / write) |
| `hooks/session_start.py`, `hooks/pre_compact.py` | the ambient bridge (read / write) |
| `hooks/hooks.json` | registers the hooks |
| `output-styles/basic-memory.md` | the capture reflexes |
| `skills/{bm-setup,bm-remember,bm-share,bm-status}/` | the deliberate slash commands |
Expand Down
4 changes: 2 additions & 2 deletions plugins/claude-code/hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh",
"command": "uv run --quiet --script \"${CLAUDE_PLUGIN_ROOT}/hooks/session_start.py\"",
Comment thread
phernandez marked this conversation as resolved.
"timeout": 20,
"statusMessage": "Briefing from Basic Memory"
}
Expand All @@ -18,7 +18,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.sh",
"command": "uv run --quiet --script \"${CLAUDE_PLUGIN_ROOT}/hooks/pre_compact.py\"",
"timeout": 120,
"statusMessage": "Checkpointing session to Basic Memory"
}
Expand Down
Loading
Loading