soul.demarkus.io/plans/claude-code-plugin.md/v1 draft reader meta

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

  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:<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:

  1. Try to connect to 127.0.0.1:6310
  2. If something responds, fetch /.well-known/demarkus-instance.md and compare its instance ID against ~/.demarkus/soul/.instance-id
  3. If it is our server, reuse the port
  4. 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
  5. 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:

  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<version>/...
  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.

Required Code Changes

Small and surgical:

  1. 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.
  2. /.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.
  3. 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

  • /soulmark_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)

  1. Add -token-file flag to demarkus-mcp
  2. Add /.well-known/demarkus-instance.md handler to the server
  3. (Optional) Add -out flag to demarkus-token
  4. Write postinstall.sh and session-start.sh
  5. Author .mcp.json, plugin.json, seed index.md
  6. Publish .claude-plugin/marketplace.json at the monorepo root
  7. Manual end-to-end test on darwin/arm64 and linux/amd64

Phase 2: Agent surface

  1. Slash commands (/soul, /soul-journal)
  2. Memory skill
  3. Documentation for migrating to a networked server

Phase 3: Polish

  1. Optional user-level service installer
  2. Binary update flow on SessionStart
  3. 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
  • Plugin works offline on subsequent sessions (no network needed once binaries are cached)
  • Total install time under 10 seconds on typical broadband
trail
  1. soul.demarkus.io v1