context-attachment-manager
Codex and Claude Code plugin (works in Claude Desktop): choose which past images go to the model with your next message. Unchecked images become placeholders, the conversation goes on, and requests st
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Context snapshots for Claude Code — session continuity across compactions and terminals
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Context snapshots for Claude Code.
You've been deep in a coding session. Claude knows your architecture, the decisions you've made, the bugs you've fixed, the open items left. Then the context window compacts — or you close your terminal and come back tomorrow.
Claude forgets everything.
Bookmark fixes this. It captures snapshots of your session context automatically and restores them when you return. No manual steps. No copy-pasting context. You pick up exactly where you left off.
Claude Code sessions are ephemeral. Context is lost when:
This creates a painful pattern: every new session starts with you re-explaining what you were working on, what you decided, what's still left to do.
Bookmark runs as a small external process. Four hooks keep a durable handoff current:
| Hook | When | What |
|---|---|---|
| PreCompact | Before context compaction | Captures a mechanical file/tool checkpoint |
| SessionStart | New session begins | Restores prior context so Claude knows what you were doing |
| UserPromptSubmit | Every user message | Checks time and token thresholds; alerts at 75% used |
| Stop | End of every assistant turn | Captures files; asks once per session for a fresh handoff only when context usage is at or above the token threshold (75%) |
When you open a new terminal and run claude, Bookmark restores your prior context. Claude greets you knowing what you were working on, what decisions were made, and what's left to do.
Bookmark separates reliable mechanical evidence from semantic judgment:
bookmark.context.md — Claude writes the current task, status, remaining work,
decisions, risks, open questions, sources of truth, and next steps.LATEST.md — Bookmark extracts file changes, tool counts,
capture reason, model, and measured context usage from the transcript.The per-prompt threshold check makes no API call and reads only the transcript tail. Full transcript parsing runs only when an interval or threshold capture is due. The semantic handoff is capped at 800 tokens and points to source files instead of copying them.
As of v0.3.3, file paths in .bookmark/snapshots/SNAP_*.json are stored
relative to project_path (e.g. src/foo.ts instead of
/Users/me/dev/git-folder/myapp/src/foo.ts). Older snapshots may carry
pre-move absolute paths that went stale when a project was relocated
(e.g. ~/Desktop/git-folder/... → ~/dev/git-folder/...); those are not
auto-rewritten — they're left intact so the historical record stays
honest. Going forward, a project move only invalidates project_path
itself, not the per-file entries inside each snapshot.
Via Claude Code plugin marketplace (recommended):
/plugin marketplace add tyroneross/bookmark
/plugin install bookmark@bookmark
Via npm (in a project):
npm install @tyroneross/bookmark
Hooks are configured automatically. Start a Claude Code session and you're covered.
Via npm (globally):
npm install -g @tyroneross/bookmark
Then activate in any project by running bookmark setup in your project directory.
Use these inside Claude Code:
| Command | What it does |
|---|---|
/bookmark | Show current session context and bookmark status; forwards any arguments to the bookmark CLI |
/bookmark:snapshot | Take a manual snapshot right now |
/bookmark:restore | Restore from latest or a specific snapshot |
/bookmark:status | Show snapshot count, compaction cycles, last snapshot time |
/bookmark:list | List available snapshots with details |
bookmark status # Show stats
bookmark snapshot # Take a manual snapshot now
bookmark list # List snapshots
bookmark show --latest # Show latest snapshot content
bookmark show SNAP_ID # Show specific snapshot
bookmark config # Show current configuration
bookmark config --interval 15 # Change snapshot interval to 15 minutes
bookmark config --token-threshold 75 # Capture and alert at 75% used
bookmark config --context-limit 500000 # Override model context limit
bookmark setup # Interactive configuration
bookmark install --host codex --dry-run # Print the hook config Bookmark would merge for a host
bookmark install --host gemini # Merge Bookmark's hooks into that host's config (backs up first)
bookmark catalog refresh # Cache the models.dev context-window catalog
Bookmark's core (thresholds, state, handoffs, restore) is host-free. A small adapter per host
translates hook stdin and stdout. Host facts live in one place: hosts/<id>.json holds the event
map, config path and output dialect, and bookmark install generates each host's config from it.
Host (--host) | Config written by install | Restore | Context check | Precompact | Stop | Usage source |
|---|---|---|---|---|---|---|
claude | ~/.claude/settings.json | SessionStart | UserPromptSubmit | PreCompact | Stop | transcript usage |
codex | ~/.codex/hooks.json | SessionStart | UserPromptSubmit | PreCompact | Stop | rollout token_count (reports its window) |
gemini | ~/.gemini/settings.json | SessionStart | BeforeAgent (measures; asks for the handoff at AfterAgent) | PreCompress | AfterAgent (deny) | session tokens |
copilot | ~/.copilot/hooks/bookmark.json | sessionStart | — (measured at agentStop) | preCompact | agentStop | turn-count estimate, labelled "estimated" |
--host <id>. Without it, Bookmark detects the host from the
hook's stdin: Gemini-only event names (BeforeAgent, AfterAgent, PreCompress) or a
~/.gemini/tmp/ transcript → Gemini; turn_id or a ~/.codex/sessions/ transcript → Codex;
camelCase sessionId or a ~/.copilot/ transcript → Copilot; otherwise Claude Code.install merges into the existing file. It touches only Bookmark's own handlers (commands that
name @tyroneross/bookmark, or that have the exact generated <bin> <role> --host <id> shape).
Other hooks, sibling handlers in the same group, and other keys stay. Duplicate Bookmark handlers
collapse to one, and a second run changes nothing. Before any write, the previous file is copied to
~/.bookmark/backups/<host>/<file>.<timestamp>. The backup file is 0600 in a 0700 directory, so it
stays out of a dotfiles repo that may hold the real file. A symlinked config is written through to
its target, and a symlink to a missing file is refused. The file keeps its mode, and a new file is
created 0600. A config file that is not valid JSON is refused and left untouched; bookmark setup
prints one line saying the hooks were skipped. --dry-run prints and writes nothing.--bin <cmd> default:
npm link (the package root has .git, or is not inside
node_modules): node <checkout>/dist/cli/index.js, so hooks run that build. install says so.npx -y @tyroneross/bookmark@<this version>. install asks npm
whether that version is published and warns if it is not. Offline, the check is skipped. Hooks
never make this check.--bin accepts plain words only (letters, digits, @ / . _ - and spaces) and is written verbatim.timeoutSec (15 / 10). Restore gets
15 s so that the first npx download on a cold cache can finish before the host gives up.install prints each host's unverified details (for example, whether Gemini needs hooks enabled,
and that Copilot hook stdin has no model).install checks the event names with rosslabs-lsp compat --section agent-hooks.
If that tool is not installed, it warns and continues; if it lists an event as unknown for the
host, refuses to write.Upgrade notes for this change:
.bookmark/ in a folder that has only a package.json. Storage needs a
git repo or an existing .bookmark/ ancestor.SessionStart fallback is now || true (it was || echo '{}'), so a failed restore
no longer injects the text {}.bookmark setup writes --host claude, the pinned npx -y command, and second-based timeouts.
It backs up ~/.claude/settings.json before changing it.docs/research/2026-10-28-multi-host-support.md).Bookmark reads the latest model-reported usage from the Claude Code transcript. It counts input,
cache-read, cache-write, output, and the pending prompt against the active model's context limit.
At 75% used, Bookmark captures a token_threshold snapshot, shows a warning, asks Claude to
refresh bookmark.context.md, and recommends a new session. It alerts once until a new session
or lower post-compaction usage re-arms the threshold.
Codex sessions are measured from the rollout file: the latest token_count event gives the tokens
used and the context window Codex reports for the active model, so no lookup table is needed.
Bookmark finds a model's context window in this order: BOOKMARK_CONTEXT_LIMIT (or
bookmark config --context-limit); the window the host reports (Codex); the cached
models.dev catalog, filled only by bookmark catalog refresh (hooks never
use the network); the built-in table below; otherwise unknown, and Bookmark asks for the limit.
For Claude Code transcripts, Bookmark resolves context limits only for model IDs documented by Anthropic. If the transcript reports an unknown model, Bookmark pauses token-threshold capture, asks the user for the verified limit, and keeps manual and periodic snapshots active. It never guesses a context limit. The mapping follows Anthropic's context-window documentation. Override either value when needed:
bookmark config --token-threshold 75
bookmark config --context-limit 500000
Bookmark installs four hooks because each protects a separate lifecycle boundary:
| Hook | Needed for |
|---|---|
SessionStart | Restore the latest durable handoff into a new session |
UserPromptSubmit | Run periodic capture and model-aware token-threshold checks |
PreCompact | Save a final mechanical checkpoint before compaction |
Stop | Save files at each turn end; request a handoff once, only near the context limit |
UserPromptSubmit is the only hook required for periodic and token-threshold capture. The other
three complete the restore, pre-compaction, and exit continuity path.
Claude Code fires Stop at the end of every assistant turn, not only at exit. Bookmark therefore
approves ordinary turn ends silently. Every hook (restore, context check, precompact, stop) resolves
storage the same way: the git repo root of cwd, else the nearest ancestor that already has
.bookmark/, else nothing is written. Restore, context check and precompact create .bookmark/ at
that root when it is missing; stop never creates it. No hook creates .bookmark/ in a non-repo
folder, a subfolder, or $HOME (restore can still follow an existing scope: home pointer), and
Stop never asks the agent to overwrite a scope: home pointer.
Manual capture does not require another hook: run /bookmark:snapshot or bookmark snapshot.
Default: every 5 minutes of active session time. Configurable:
bookmark config --interval 10 # Every 10 minutes
bookmark config --interval 30 # Every 30 minutes
Or set via environment: BOOKMARK_INTERVAL=15
All data lives in your project at .bookmark/:
.bookmark/
├── LATEST.md # Hot context — what gets restored on SessionStart
├── index.json # Snapshot index with stats
├── state.json # Plugin state (compaction count, thresholds, timing)
├── config.json # Your preferences
├── snapshots/ # Full snapshot files
└── archive/ # Old snapshots (>30 days)
Automatically added to .gitignore — snapshot data never gets committed.
Every bookmark.context.md should start with an BOOKMARK_IDENTITY HTML comment that
declares which project and git state the summary belongs to. This makes cross-session
restoration unambiguous — a bookmark can't be mistaken for a different repo's context.
# Session Context — Travel Planner
<!-- BOOKMARK_IDENTITY
scope: repo
project: travel-planner
repo_path: /Users/me/dev/git-folder/Travel Planner
branch: feature/summer-camps
head: 4988383
written: 2026-04-11
-->
## Current Task
...
Supported fields: scope (repo or home), project, repo_path, repo_name, branch,
head, base, written, written_by. Unknown keys are preserved for forward compatibility.
At restore time, bookmark compares the identity's repo_path to the CWD it was invoked in.
Mismatch → the restored content is prefixed with an "identity mismatch" warning, so the new
session can verify the bookmark belongs to the project before acting on it.
A scope: home bookmark at ~/.bookmark/bookmark.context.md is a pointer, not a
session context. It contains points_to_canonical naming the real repo-scoped file:
<!-- BOOKMARK_IDENTITY
scope: home
project: POINTER_ONLY
points_to_project: travel-planner
points_to_canonical: /Users/me/dev/git-folder/Travel Planner/.bookmark/bookmark.context.md
-->
SessionStart automatically follows the pointer — a session launched from ~/ now gets
routed to the canonical project bookmark without manual cd. Previously, the home
bookmark would be served as if it were the active context (with potentially stale or
wrong-project content).
Stale auto-restore is worse than no auto-restore: a 14-day-old bookmark prefixed with a
soft warning still creates "confident wrong starts" because the warning reads as noise.
Bookmark now hard-blocks auto-restore at 72 hours — past that threshold, the
restored content is replaced with a message telling you to pick a specific snapshot via
/bookmark:list or read the file manually if you actually want it. Soft warnings still
apply between 24h and 72h.
Bookmark performs mechanical capture outside the model context. It injects only the compact handoff on SessionStart and one short instruction when a token threshold is crossed.
| Variable | Default | Description |
|---|---|---|
BOOKMARK_INTERVAL | 5 | Snapshot interval in minutes |
BOOKMARK_TOKEN_THRESHOLD | 0.75 | Context-used fraction that captures and alerts |
BOOKMARK_CONTEXT_LIMIT | model-aware | Explicit context-window override in tokens |
BOOKMARK_STOP_MODE | threshold | threshold: Stop blocks only when usage is at or above the lowest token threshold. every-turn: block once on any turn end without a fresh handoff (old behaviour). Config key: handoff.stopMode |
BOOKMARK_STORAGE_PATH | .bookmark | Storage directory |
BOOKMARK_VERBOSE | false | Enable verbose logging |
BOOKMARK_SKIP_SETUP | false | Skip postinstall auto-setup |
BOOKMARK_MODEL_CATALOG | $XDG_CACHE_HOME/bookmark/models-dev.json (else ~/.cache/bookmark/models-dev.json) | Cached models.dev catalog used for context windows |
BOOKMARK_TOKENS_PER_TURN | 4000 | Tokens per user turn for hosts measured by turn count (Copilot); results are labelled "estimated" |
BOOKMARK_COMPAT_BIN | rosslabs-lsp | Binary bookmark install calls to validate hook event names |
BOOKMARK_SKIP_PUBLISH_CHECK | unset | 1 skips the npm check bookmark install runs before using the pinned npx command |
Hook commands also accept --host <id> (default: detect from stdin). bookmark install takes
--host <id> (required), --dry-run (default off) and --bin <cmd> (default: the local build
when run from a checkout, else npx -y @tyroneross/bookmark@<this version>). bookmark catalog refresh times out after 15 s and
caps the download at 10 MB.
Apache-2.0
This package now ships an additive Codex plugin surface alongside the existing Claude Code package. The Claude package remains authoritative for Claude behavior; the Codex package adds a parallel .codex-plugin/plugin.json install surface without changing the Claude runtime.
Package root for Codex installs:
.)Primary Codex surface:
./skills when present./.mcp.json when presentInstall the package from this package root using your current Codex plugin install flow. The Codex package is additive only: Claude-specific hooks, slash commands, and agent wiring remain unchanged for Claude Code.
installhooks/hooks.json is generated from hosts/claude.json by
node scripts/gen-plugin-hooks.mjs (after npm run build). It has no --host because both the
Claude Code and Codex plugin surfaces load it. It has no version pin because a pin would go stale
on every release-please version bump. Its Stop fallback prints {}, which both hosts accept as a
no-op; {"decision":"approve"} is Claude-only.bundle/ (one self-contained file each,
built by npm run build), so a plugin installed from GitHub runs that commit's code with no npm
download and no node_modules. Hooks fall back to npx only when CLAUDE_PLUGIN_ROOT is unset
or the bundle is missing. CI fails if bundle/ or hooks/hooks.json differ from the source.agentStop. Without a configured limit,
the first stop asks the agent once to tell you to set BOOKMARK_CONTEXT_LIMIT. Within a session,
the turn-count estimate never decreases..bookmark/ can shape them.