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.
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.
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 bydisown - 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:
- Try to connect to
127.0.0.1:6310 - If something responds, fetch
/.well-known/demarkus-instance.mdand compare its instance ID against~/.demarkus/soul/.instance-id - If it is our server, reuse the port
- If the port is taken by something else, pick the next free port in a small range, write it to
~/.demarkus/soul/.port, and spawn the server there - The MCP client reads the chosen port from
.port(falls back to 6310 if absent)
Auth
On first run the hook generates a token via demarkus-token, scoped /* with ops publish,archive. The raw token is written to ~/.demarkus/soul/.token with mode 0600. The hashed form lives in the server's tokens.toml under the content root.
demarkus-mcp gets a new -token-file <path> flag that reads the raw token from disk. This keeps the secret off argv and out of ps.
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, seed index
.mcp.json # demarkus-mcp -host mark://localhost:<port> -token-file ...
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/andchmod +xthem
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.
Required Code Changes
Small and surgical:
demarkus-mcp -token-file <path>— read the raw token from a file. Error when the file is missing, empty, or has permissions looser than 0600./.well-known/demarkus-instance.md— auto-served on the server, body contains the instance ID generated once on first boot and stored at<root>/.instance-id. Lets port probes distinguish our server from a colliding process.demarkus-token -out <path>(optional, nice to have) — atomically writes the raw token to a file with 0600 perms, avoiding shell piping.
Everything else (server, store, MCP client, protocol) is unchanged.
Commands and Skills
Slash Commands
/soul—mark_fetch /index.mdand render/soul-journal <entry>—mark_append /journal.mdwith the entry, auto-resolvingexpected_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, thenmark_backlinksormark_graphfor exploration - Write intents →
mark_append /notes.mdby default, ormark_publishto 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)
- Add
-token-fileflag todemarkus-mcp - Add
/.well-known/demarkus-instance.mdhandler to the server - (Optional) Add
-outflag todemarkus-token - Write
postinstall.shandsession-start.sh - Author
.mcp.json,plugin.json, seedindex.md - Publish
.claude-plugin/marketplace.jsonat the monorepo root - Manual end-to-end test on darwin/arm64 and linux/amd64
Phase 2: Agent surface
- Slash commands (
/soul,/soul-journal) - Memory skill
- Documentation for migrating to a networked server
Phase 3: Polish
- 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.mdon 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
- Plugin works offline on subsequent sessions (no network needed once binaries are cached)
- Total install time under 10 seconds on typical broadband