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
/plugin marketplace add latebit-io/demarkus
/plugin install demarkus-memory@demarkus
- Postinstall downloads platform-specific binaries to
${CLAUDE_PLUGIN_ROOT}/bin/
- On the next session, a SessionStart hook ensures the server is up and the token exists
- The agent has the full MCP tool set pointed at
mark://localhost:<port>
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:
- Try
FETCH /health against mark://127.0.0.1:6310
- 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.
- 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
- 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:
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:
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:
# 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:<port>
commands/
soul.md # /soul → display /index.md
soul-journal.md # /soul-journal <entry> → 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:
- Detects platform via
uname -sm (darwin/arm64, darwin/amd64, linux/amd64, linux/arm64)
- Reads the plugin version from
plugin.json
- Downloads the three binaries from
https://github.com/latebit-io/demarkus/releases/download/v<version>/...
- Verifies SHA256 checksums against a bundled checksums file
- 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 <entry> — 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) ✓
- ✓ Write
postinstall.sh (detect platform, fetch binaries from GitHub releases, verify checksums)
- ✓ Write
session-start.sh (probe 6310, spawn server if absent, generate token on first run, export DEMARKUS_AUTH, seed index.md)
- ✓ Author
.mcp.json, plugin.json, seed index.md
- ✓ Publish
.claude-plugin/marketplace.json at the monorepo root
- ✓ Manual end-to-end test on darwin/arm64 and linux/amd64
Phase 2: Agent surface ✓
- ✓ Slash commands (
/soul, /soul-journal, plus /soul-status, /soul-init, /soul-context, /soul-memory)
- ✓ Memory skill
- ⚠ Documentation for migrating to a networked server (deferred — no user demand yet)
Phase 3: Polish (deferred)
- Optional user-level service installer
- Binary update flow on SessionStart
- 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/