Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

claude-spt is the Spacetime (spt) adapter for Claude Code. You install one Claude Code plugin — sptc — and get three things at once:

  • Agent messaging and live agents inside Claude Code: send messages between agents, run a reachable “perch,” and drive long-lived live-agent sessions, all delegated to the spt binary.
  • An invisible spt-core installer. The first time the plugin loads, it installs spt-core for you if it is missing. No separate setup step.
  • A casual on-ramp to spt-core. The plugin is the friendly front door to the wider Spacetime agent ecosystem — subnets, terminal hosting, seamless self-update — without leaving your editor.

Mental model

Claude Code provides the harness (hooks, skills, your prompt). The spt binary provides the core (messaging, lifecycle, networking). claude-spt is the thin adapter between them: it maps Claude Code’s hook events to the spt binary’s harness-contract entry points and surfaces delivered messages back into your session.

   Claude Code  ──hook events──▶   sptc adapter   ──spt api──▶   spt-core
   (the harness)                  (this project)                (messaging,
        ▲                          thin glue                     live agents,
        └──────  additionalContext / skills  ◀── renders ◀────    networking)

Two ideas follow from that picture, and they shape everything else in these docs:

  1. The adapter is thin by design. Logic and skill instructions live in the spt binary and its adapter manifest (conducted by spt-core), not in the plugin. The plugin ships skeletons; the operative content is delivered at run time. See Harness contract.
  2. It is built against spt-core’s public surface only — the published spt-releases binary, install scripts, and docs. That constraint is the point: it proves the adapter contract is buildable by anyone, from the published surface alone.

Status

This is an early, skeleton-honest build. Skills surface under the /sptc:* namespace; surfaces that are not yet operative say so in place. Start with the Quickstart.

Quickstart

Goal: install the sptc plugin, watch it bring up spt-core for you, and confirm your session is reachable — in under ten minutes. You need Claude Code and a shell (bash on macOS/Linux, Git Bash on Windows).

1. Add the marketplace and install the plugin

From Claude Code:

/plugin marketplace add SaberMage/cplugs
/plugin install sptc@cplugs

Then restart the Claude Code session so the plugin’s SessionStart hook runs and populates the session environment.

2. Let it install spt-core for you

On that first start, the plugin’s bootstrap installs spt-core if it is not already present — this is the invisible-installer step, nothing for you to run. Confirm it landed:

spt --version

If spt is not yet on your PATH in this shell, it was installed to ~/.local/bin/spt (macOS / Linux) or %LOCALAPPDATA%\spt-core\bin\spt.exe (Windows); open a fresh shell and try again.

3. Confirm your session is reachable

Each Claude Code session resolves its own agent identity. Check it:

spt whoami

That prints this session’s perch id (resolved from $OWL_SESSION_ID / $SPT_AGENT_ID). A printed id means the adapter wired your session into Spacetime’s messaging fabric.

What works today, and what is coming

This is an early build. The plumbing above — install, invisible spt-core bootstrap, identity — is operative now. The /sptc:* skills (send, ready, live, commune, and the rest) ship as skeletons: their operative instructions are delivered by the adapter at invocation time rather than baked into the plugin, and some are still being wired. Each skill says in place whether it is operative yet.

Next, read Harness contract to see exactly how Claude Code hook events map onto the spt binary.

Harness contract

claude-spt is glue: it maps Claude Code hook events to the spt binary’s harness-contract inbound surface (spt api --adapter claude-spt <verb>). The binary is harness-agnostic; this adapter is the Claude-Code-shaped edge of it.

The authoritative contract lives on spt-core’s published surface — the harness-contract + CLI reference. This page documents the adapter’s wiring: which Claude Code hook drives which spt api verb.

Hook → spt api mapping

The plugin ships a static hooks.json that routes every Claude Code hook event through one thin wrapper, hooks/dispatch.sh <EventName>, which resolves the claude-spt program (from the adapter’s [strings].hook_cmd = "{adapter_dir}/claude-spt hook", looked up once per session) and runs claude-spt hook <EventName> with the Claude Code hook payload on stdin. The hook logic lives in the program — so it updates with spt adapter update, and the plugin’s hook wiring stays fixed.

Claude Code hookhandlerspt api verb (representative)Purpose
SessionStartclaude-spt hook SessionStartseed / bind / boundaryBootstrap spt-core (via dispatch), register the perch (bind spt-hosted · seed harness-hosted · boundary on clear/compact), then relay an agent-facing brief (see below); non-blocking — never listen.
UserPromptSubmitclaude-spt hook UserPromptSubmitstate busy + pollMark the turn busy, drain delivered messages (incl. deferred) to the prompt as additionalContext, and inject a /sptc:… skill body when present.
PreToolUseclaude-spt hook PreToolUsestate busy + pollMid-turn delivery: drain messages deferred while busy so a live agent receives them while working.
Stopclaude-spt hook Stopstate (idle)Mark the agent idle when a turn ends.
SessionEndclaude-spt hook SessionEndsession-endTear down session state cleanly.
SubagentStartclaude-spt hook SubagentStartworker-*Track a spawned subagent.
SubagentStopclaude-spt hook SubagentStopworker-*Track subagent completion.
PostToolUse (Write)claude-spt hook PostToolUsestate idle + self-sendDetect a !!checkpoint!! commune Write and self-send the agent-driven checkpoint signal (spt-hosted live agents).

Two invariants the handler holds

  • Payload comes from stdin, never from a /-leading argument. On Windows under Git Bash / MSYS, any argument beginning with / is silently rewritten to a Windows path. The dispatch wrapper passes the payload straight through on stdin (its only argument is the event name) and the program reads the Claude Code hook payload as JSON from stdin, so a /sptc:… token is never corrupted.
  • Messages are self-delimiting <EVENT> envelopes. poll output is rendered by splitting on the canonical <EVENT type="msg" from="…">body</EVENT> envelope, so a multi-message drain parses cleanly and each message keeps its sender for reply-correlation.

Identity

Every session resolves its own perch id via spt whoami, off $OWL_SESSION_ID / $SPT_AGENT_ID. A session with no perch (never made reachable) simply delivers nothing — the per-prompt drain no-ops rather than erroring.

SessionStart briefs

SessionStart also relays an agent-facing brief as additionalContext, composed from the adapter’s [strings.briefs] (same file-backed/inline machinery as [strings.skills]). The hook only selects + composes + {id}-substitutes — it never authors the prose.

session statetriggerbrief
has a perchbind ($SPT_ENDPOINT_ID) or boundary (clear/compact)identity brief — who it is ({id}), that its perch is already live (don’t re-arm → COLLISION), and how to message (spt send + reply + the spt endpoint list roster).
no perch, node has subnet peersseed / fresh startupring brief — how to reach other agents without a perch (spt ring <target> --timeout 60) + the roster.
no perch and no peers · subagent (agent_type set)nothing.

The brief is liveness-agnostic (no live-vs-ready distinction) pending a published machine-readable liveness query on the spt surface. The peer gate is a line-count presence check on spt subnet status — it never parses the human-formatted column values.

The operative skill instructions are not in this plugin. They are delivered by the adapter manifest (conducted by spt-core) at invocation time; the /sptc:* SKILL.md files are deliberately thin skeletons. This split keeps the marketplace artifact low-churn while logic and instructions update through spt-core’s signed adapter-update channel.

Alt accounts

Run a session under a different Anthropic account without logging out of the first — so when one account runs out of tokens, an endpoint can keep going on another.

Claude Code holds exactly one account per config root: /login replaces the credentials in the active root, and claude auth offers only login/logout/status. So a second account means a second config root, selected by CLAUDE_CONFIG_DIR. claude-spt owns those roots and calls them account roots; an alt profile is an adapter profile whose only job is to point a session at one.

The layout

~/.claude-spt/
  shared/                     one node-wide continuity tree
    projects/ todos/ file-history/ shell-snapshots/ session-env/
  accounts/<account>/         = CLAUDE_CONFIG_DIR for one alt profile
    .credentials.json         real file — one login, shared by every endpoint on this account
    .claude.json              real file — identity + trust
    projects/ …               junctions into ../../shared/…
    skills/ agents/ commands/ plugins/
                              junctions into your primary root's
    settings.json             a copy of your primary root's, taken at init

Two properties are load-bearing:

  • Continuity is shared. projects/ and friends are directory junctions into one node-wide tree, so a session started on one account can be resumed on another — spt’s resume passes --resume <session_id>, and Claude Code reads that transcript out of whichever root is active.
  • Nothing account-bound is shared. Credentials and .claude.json are real files in each root. .claude.json is rewritten with a new file identity every 15-25 seconds while a session is live, so a link there would stop tracking on the first rewrite and the two stores would diverge silently — a refreshed token landing in one while the other rots.

history.jsonl is a file, so it cannot join the shared tree: prompt history does not follow an account switch. That is the one accepted loss.

Your plugins ride along too: the plugin store — marketplaces, installs, caches — is junctioned from your primary root, so a plugin installed once is present under every account. Which plugins are enabled stays per-account, because settings.json is a copy. Without the shared store, the copied settings would enable plugins the account root does not hold, and the session would come up with its hooks and skills silently missing.

One account, many endpoints

An account root holds a login, not a session — every endpoint on that account shares the one login. Per-endpoint memory and identity are spt’s job at the mind layer, so endpoints do not need a root apiece (and a root apiece would cost a login apiece, which defeats the failover).

An endpoint keeps its id across profiles. The profile selects which account pays, never who is speaking.

Setup

There is none. The first launch of an endpoint on an alt profile notices the account root does not exist yet and builds it right there: it creates the root and the shared tree, junctions the continuity set and the authoring surface, copies settings.json, and seeds the trust map from your current root (trust is per-config-root, and an unseeded root does not fail — it hangs on the trust dialog). Every launch after the first finds the root in place and pays nothing: per-launch maintenance is exactly the cost this design removes.

So the whole flow is: launch an endpoint on the profile and log in at Claude Code’s own prompt. A launch against a root with no credentials proceeds deliberately — Claude Code does the login, and the credentials land in that root for every later endpoint on the account.

The same one-shot setup also exists as a command, for building a root ahead of its first launch or repairing one (it is idempotent — re-running it fixes the missing piece and touches nothing else):

claude-spt alt init [account]    # account defaults to "alt", the shipped profile's

Using it

The adapter ships one example profile, claude-spt:alt, bound to an account literally named alt:

spt endpoint run --adapter claude-spt:alt --id <id>

Every spawned role follows the profile — the session, its resume, the Psyche, and the end-of-session summarizer — because a failover that leaves the Psyche drawing on the exhausted account is not a failover.

Your own accounts

Add your own without waiting for an adapter release, using spt-core’s own profile machinery:

spt adapter create-profile claude-spt work     # then point its commands at --account work

The first launch on the new profile builds its account root, exactly as with the shipped one. A created profile is node-local and survives re-registration and adapter updates. Profiles are static: spt has no runtime profile parameterization and no {account} fill key, so each profile names its account literally — one profile per account.

What this is not

It does not swap providers or backends. The claude-spt:ccs profile (model/billing multiplexing through ccs) is untouched and keeps its own roles.