--- status: shipped owner: claude-code --- # Plan: Claude Code Plugin Make demarkus usable in Claude Code with a single marketplace install. No manual server setup, no token generation, no `.mcp.json` hand editing. **The plugin reuses the existing demarkus server/client/protocol surface — no core code changes are required to ship it.** ## Status **Shipped.** `demarkus-memory` plugin v0.1.1 is live on the marketplace; `plugins/claude-code/` ships hooks (postinstall.sh, session-start.sh), slash commands (`/soul`, `/soul-journal`, `/soul-status`, `/soul-init`, `/soul-context`, `/soul-memory`), the memory skill, the seed `index.md`, and the binary-download pipeline. `~/.demarkus/plugin-memory.conf` records the operator's chosen `MODE` / `SOUL_DIR` / `PORT`. The plugin's lazy-spawned `demarkus-server` is the memory backend for every demarkus development session. ## Goal After `/plugin marketplace add latebit-io/demarkus` and `/plugin install demarkus-memory`, the user has a working memory agent. `mark_fetch`, `mark_publish`, `mark_append`, and the rest of the MCP tool set are available. A local `demarkus-server` backs them. Memory persists across sessions. The TUI and browser can hit the same server on the same machine. ## Non-Goals - Cross-machine sync of the local soul. Users who want that run a networked server and reconfigure the plugin. - Windows native. WSL only, matching the rest of demarkus. - Embedded (in-process) MCP mode. We keep server and client as separate processes; the plugin just automates the spin-up. - Remote marketplace hosting. The plugin lives in the monorepo and is published via a marketplace.json at the repo root. - Core code changes. The plugin is built entirely on existing primitives; any need for a new flag or endpoint must be discussed and justified before landing. ## User Flow 1. `/plugin marketplace add latebit-io/demarkus` 2. `/plugin install demarkus-memory@demarkus` 3. Postinstall downloads platform-specific binaries to `${CLAUDE_PLUGIN_ROOT}/bin/` 4. On the next session, a SessionStart hook ensures the server is up and the token exists 5. The agent has the full MCP tool set pointed at `mark://localhost:` No config files to touch, no prompts, no decisions forced on the user. ## Architecture ### Process Lifecycle: Lazy Spawn The SessionStart hook: - Checks for an existing server with `pgrep -f "demarkus-server.*\.demarkus/soul"`; if running, noop - Otherwise spawns it detached: `${CLAUDE_PLUGIN_ROOT}/bin/demarkus-server -root ~/.demarkus/soul -port $PORT -log-file ~/.demarkus/soul/.log &` followed by `disown` - Server persists across sessions; the next session reuses it No service manager (launchd, systemd user unit) in v1. If the process model proves fragile later, promote to a user service. ### Content Root `~/.demarkus/soul/` is the default content directory. Created on first run. All versioning, graph data, tokens, and logs live under it. ### Port Handling Default port: 6310. On every SessionStart, use primitives the server already exposes: 1. Try `FETCH /health` against `mark://127.0.0.1:6310` 2. If it succeeds (QUIC handshake completes with ALPN `"mark"` and `/health` returns OK), treat the port as "a demarkus server we can use" and reuse it. Any other service would fail the ALPN negotiation, so there is no meaningful risk of mistaking a non-demarkus process for ours. 3. If the handshake fails or the port is unreachable, pick the next free port in a small range, spawn our server there, and write the chosen port to `~/.demarkus/soul/.port` 4. The MCP client reads the port from `.port` on startup (falls back to 6310 if absent) No server-side changes needed — `/health` and ALPN `"mark"` are already in the protocol. ### Auth On first run the hook generates a token via `demarkus-token generate`, scoped `/*` with ops `publish,archive`. The existing CLI writes the raw token to stdout and context messages to stderr, so the hook captures it with shell redirection and restricts permissions: ```bash demarkus-token generate -label claude-code-plugin -paths "/*" -ops "publish,archive" \ -tokens ~/.demarkus/soul/tokens.toml \ 2>/dev/null > ~/.demarkus/soul/.token chmod 600 ~/.demarkus/soul/.token ``` The MCP client picks up the token via the existing `DEMARKUS_AUTH` environment variable, which `tokens.Resolve` already honors. The `session-start.sh` hook exports it before Claude Code launches the MCP process: ```bash export DEMARKUS_AUTH="$(cat ~/.demarkus/soul/.token)" ``` No server-side or client-side changes needed — `tokens.Resolve`, `demarkus-token generate`, and the `DEMARKUS_AUTH` env var are all already shipped. ### First-Run Seeding The plugin ships a `seed/index.md` template. The SessionStart hook copies it to `~/.demarkus/soul/index.md` only when the content root is empty. The agent expands from there. Minimal seed: ```md # My Soul Personal memory for Claude Code agents. ## Sections - [Journal](/journal.md) — session notes - [Notes](/notes.md) — anything worth remembering ``` ## Plugin Layout ``` plugins/claude-code/ .claude-plugin/plugin.json hooks/ postinstall.sh # download + verify binaries session-start.sh # ensure server, ensure token, export DEMARKUS_AUTH, seed index .mcp.json # demarkus-mcp -host mark://localhost: commands/ soul.md # /soul → display /index.md soul-journal.md # /soul-journal → mark_append /journal.md skills/ memory/SKILL.md # triggers on remember/save/note/recall seed/ index.md ``` At the monorepo root: `.claude-plugin/marketplace.json` listing the plugin and pointing at `plugins/claude-code/`. ## Binary Distribution `postinstall.sh`: 1. Detects platform via `uname -sm` (darwin/arm64, darwin/amd64, linux/amd64, linux/arm64) 2. Reads the plugin version from `plugin.json` 3. Downloads the three binaries from `https://github.com/latebit-io/demarkus/releases/download/v/...` 4. Verifies SHA256 checksums against a bundled checksums file 5. Places binaries in `${CLAUDE_PLUGIN_ROOT}/bin/` and `chmod +x` them Binaries required: `demarkus-server`, `demarkus-mcp`, `demarkus-token`. GoReleaser already produces these per platform; the only possible pipeline tweak is ensuring the release assets include a consolidated SHA256SUMS file. Confirm the GoReleaser config before hardcoding download URLs. ## Core Code Changes **None.** Every piece of plumbing the plugin needs already exists: - Token injection: `DEMARKUS_AUTH` env var (honored by `tokens.Resolve`) - Server identity / port probe: `/health` endpoint + ALPN `"mark"` negotiation - Writing a generated token to a file: shell redirection of `demarkus-token generate` stdout + `chmod 600` If any future iteration of the plugin surfaces a genuine need for a new core primitive, discuss and justify it before landing. Plugin-driven polish is not a reason to touch core. ## Commands and Skills ### Slash Commands - `/soul` — `mark_fetch /index.md` and render - `/soul-journal ` — `mark_append /journal.md` with the entry, auto-resolving `expected_version` ### Skill: Memory Triggers on phrases like "remember this", "save to memory", "what do I know about X", "note that", "recall". - Read intents → `mark_fetch`, then `mark_backlinks` or `mark_graph` for exploration - Write intents → `mark_append /notes.md` by default, or `mark_publish` to a topic page when the agent decides the note deserves its own document ## Release Pipeline - Plugin version tags follow the monorepo convention: `claude-code-plugin/v0.1.0` - Releases are cut from main; the marketplace.json is updated in the same commit - Users who already added the marketplace get the update via `/plugin marketplace update` ## Open Questions and Deferred Work - **Service installer**: launchd plist / systemd --user unit. Deferred. Lazy spawn is enough for v1. - **Binary auto-update**: v1 downloads on postinstall only. A later version can verify against latest on SessionStart and prompt. - **Cross-machine access**: document the path for users who want to run a networked server and point the plugin at it, but don't automate the migration. - **Port collision UX**: if the probe can't find a free port in the scanned range, the hook should surface a clear error rather than silently fail. ## Phasing ### Phase 1: Core plugin (ships usable v1) ✓ 1. ✓ Write `postinstall.sh` (detect platform, fetch binaries from GitHub releases, verify checksums) 2. ✓ Write `session-start.sh` (probe 6310, spawn server if absent, generate token on first run, export `DEMARKUS_AUTH`, seed `index.md`) 3. ✓ Author `.mcp.json`, `plugin.json`, seed `index.md` 4. ✓ Publish `.claude-plugin/marketplace.json` at the monorepo root 5. ✓ Manual end-to-end test on darwin/arm64 and linux/amd64 ### Phase 2: Agent surface ✓ 6. ✓ Slash commands (`/soul`, `/soul-journal`, plus `/soul-status`, `/soul-init`, `/soul-context`, `/soul-memory`) 7. ✓ Memory skill 8. ⚠ Documentation for migrating to a networked server (deferred — no user demand yet) ### Phase 3: Polish (deferred) 9. Optional user-level service installer 10. Binary update flow on SessionStart 11. Better port-collision error messaging ## Success Criteria - ✓ Fresh Claude Code install + marketplace add + plugin install → a working `mark_fetch /index.md` on first session with zero prompts - ✓ Existing demarkus users can disable the plugin-managed server and point MCP at their own server via a documented override (`MODE=reuse` in `~/.demarkus/plugin-memory.conf`) - ✓ Plugin works offline on subsequent sessions (no network needed once binaries are cached) - ✓ Total install time under 10 seconds on typical broadband - ✓ Zero changes to `protocol/`, `server/`, or `client/` modules outside `plugins/claude-code/`