Skip to main content

witan — agent memory, planning, and collaboration graph (work-coordination layer + umbrella CLI)

Project description

witan

Pronounced WIT-ən (/ˈwɪtən/) — rhymes with "written" minus the r. From Old English witan, the king's council of advisors.

A team-wide shared knowledge graph for coding agents, backed by Omnigraph. Stores coding patterns, project/repo facts, lessons, and agent context. Exposed over MCP so every agent platform (pi, Claude Desktop, GitHub Copilot) can read and write without platform-specific code.

Documentation

See the User Guide for a task-oriented walkthrough (install, setup, day-to-day loop, operating modes, troubleshooting) and the CLI Reference for the full witan command surface.

Quick Start

witan setup installs the omnigraph binary, copies hooks/skills, and wires the MCP server entry into your agent config in one step. Run it once; re-run after upgrades to refresh installed files.

The omnigraph binary step always downloads the release pinned by _OMNIGRAPH_VERSION in witan/setup.py straight from GitHub releases into ~/.local/bin/omnigraph — there is no build-time bundling, so re-running witan setup is also how you pick up an omnigraph version bump (a Renovate PR bumps the pin; see renovate.json's omnigraph-version customManager). When witan-code is also installed (importable in this environment — e.g. via the --with in the uv tool install/MCP server's uvx invocation below), witan setup folds its skill and hooks into the same install pass — registered as witan code …, so the hooks only need witan on PATH, not a separate witan-code binary — and skips a separate MCP entry, since witan serve already mounts witan-code's tools in-process. One witan setup then covers both packages. Installed standalone (without witan), witan-code has its own equivalent witan-code setup (or the mounted witan code setup) — see its README.

With persistent CLI — required for Claude Code and Pi (and --agent all), whose hooks/extensions call the witan command directly, so it must stay on your PATH. Install witan-code alongside it with --with so its witan code … hooks work too (omit --with for a witan-only install):

# Install the latest release from PyPI …
uv tool install --with witan-code witan-council
# … or, to track pre-release/unreleased code, from the git repo directly:
uv tool install --with git+https://github.com/mitodl/agent-kit#subdirectory=mcp/servers/witan-code \
    git+https://github.com/mitodl/agent-kit#subdirectory=mcp/servers/witan
# … or editable from a local checkout, at the repo root:
uv tool install --editable --with ./mcp/servers/witan-code mcp/servers/witan

# then run setup:
witan setup --agent claude   # or: pi | all

Without persistent CLI — enough for the MCP-only agents (Copilot, OpenCode, Kilo), whose server runs via uvx, so no install is needed:

# From PyPI …
uvx --from witan-council witan setup --agent copilot   # or: opencode | kilo
# … or, to track pre-release/unreleased code, from the git repo directly:
uvx --from git+https://github.com/mitodl/agent-kit#subdirectory=mcp/servers/witan \
    witan setup --agent copilot   # or: opencode | kilo
# … or from a local checkout, at the repo root:
uvx --from mcp/servers/witan witan setup --agent copilot
# Optional — set your author name (defaults to git config user.name or $USER):
export WITAN_AUTHOR="Your Name"

Manual wiring (if you prefer to configure agents by hand): run ./install.sh to initialise the local graph, then copy the appropriate config snippet from config/ into your agent's MCP config file.

Local development setup (both servers, hooks, and skills wired from your checkout): see Local Development Setup.

Environment Variables

Variable Required Default Description
WITAN_MEMORY_URI No ~/.local/share/witan/graph.omni Graph URI — local path, s3://, or http://
WITAN_MEMORY_TOKEN Only for http:// Bearer token for remote server auth
WITAN_AUTHOR No $USER Attribution on every insert
WITAN_REPO No Repo slug override (bypasses git detection)
WITAN_SCAN_ENABLED No true Write-path secret/PII scanning; set to false to opt out — see Write-path content scanning
WITAN_OPTIMIZE_INTERVAL No 86400 Throttle window (seconds) for the Stop hook's opportunistic background store compaction; 0 disables it

MCP Tools

witan memory vs. your agent's built-in/session memory. witan is the shared, synced, team-wide store; a coding agent's built-in memory (e.g. Claude Code's memory/ files) is private to one machine and user. Durable, shareable engineering knowledge — project facts, patterns, lessons, decisions — belongs in witan so other agents and future sessions can find it; keep only private/ephemeral notes in built-in memory. The witan-memory skill and the MCP server instructions steer the agent accordingly.

Memory Tools

Tool Description
memory_search Full-text BM25 search across all memories, with optional repo/kind filters
memory_list List memories (no search), optionally filtered by kind and/or repo; ordered most-recent first
memory_store Insert a new memory (pattern, project_fact, lesson, or agent_context)
memory_get Fetch a single memory by slug
memory_get_project_facts Return all project facts for the current (or specified) repo
memory_list_patterns List coding patterns, optionally scoped to a repo and/or language

Workflow Tracking Tools

Track engineering projects end-to-end across multiple Claude Code sessions. See mcp/servers/witan/witan/skills/witan-project-tracker/SKILL.md for usage.

Tool Description
workflow_project_create Create a new project (wp- slug) to track an engineering objective
workflow_project_get Fetch a single project by slug
workflow_project_list List projects filtered by repo, status, or phase; defaults to active
workflow_project_advance Advance a project to the next phase (discovery → spec → implementation → delivery)
workflow_project_complete Mark project done; assembles a WorkflowTrace corpus record from all sessions
workflow_project_link_memory Attach a memory (pattern/lesson/project_fact) to a project via an Informed edge
workflow_project_block Declare that one project must complete before another can begin
workflow_project_unblock Remove a project dependency declared with workflow_project_block
workflow_project_get_blockers Return all projects currently blocking the given project
workflow_session_start Link the current agent session to a project; writes a state file for the Stop hook
workflow_session_end Close the session with a summary, tools used, and files changed
workflow_trace_get Fetch a completed project's corpus WorkflowTrace by its wt-/wp- slug

Task Tracking Tools

A dependency-aware, hierarchical task tracker (beads-like) in the same graph, so tasks hard-link to projects, sessions, and memories. See mcp/servers/witan/witan/skills/witan-task/SKILL.md (the /witan-task skill) for usage.

Tool Description
task_create Create a task (tk- slug). Supports parent (epic → sub-issue), blocked_by, project_slug, external_uri (e.g. a GitHub issue), and symbol_refs
task_get Fetch a single task by slug
task_list List tasks filtered by repo, status, project, parent (epic children), or assignee
task_update Update mutable fields — reassign, re-prioritise, re-parent, attach a URI (to claim for work, prefer task_claim)
task_claim Advisory claim for parallel/multi-user agents: set in_progress under an assignee with a lease; refuses if actively held (use force to steal). Read-check-write, not an atomic lock — see claimed_at lease + the CAS follow-up
task_release Drop a claim: clear assignee/lease, return the task to open
task_close Close a task with an optional resolution; unblocks its dependents
task_ready Ready work: open/unblocked tasks (plus in_progress tasks whose lease has lapsed) ordered by priority — the core coordination primitive
task_link Link tasks: blocks / parent / discovered_from, or addresses a Memory node
context_for_symbol Reverse lookup: given a code-graph symbol id (repo#path::Name), return the memories and tasks whose symbolRefs include it

Tasks are hierarchical (an epic decomposes into sub-issues via parent, with parentSlug denormalized for fast child lookup) and dependency-aware (blocked_by maintains a denormalized blockedBy list that drives the task_ready query without graph traversal). external_uri links a task to a GitHub issue/PR or any reference.

Code Branch Tracking

Links a git branch (repo + raw branch name, e.g. feature/new-api — never witan-code's sanitized omnigraph branch name) to the task/project it's carrying, so "which branch carries task X" and "which tasks are in flight on branch B" are one-hop graph queries. Coordination state that lives in witan (shared, durable), not witan-code's per-repo/bridge omnigraph stores (local, re-derivable caches branches --prune may destroy at any time).

Wired in automatically, best-effort — no dedicated tool call needed:

  • workflow_session_start upserts a CodeBranch for the current checkout's repo+branch (when detected) and links it ForProject to the session's project.
  • task_claim upserts a CodeBranch on success and links it WorksOn the claimed task; re-calling task_claim (lease renewal) does not duplicate the edge.
  • The UserPromptSubmit context-injection hook surfaces an In-Flight Branch section when the current checkout's branch already carries an open task — a signal that this session should likely continue that work rather than pick up something new.

Both call sites no-op silently with no repo/branch context (detached HEAD, outside a git repo, or a store that predates this feature and hasn't run witan migrate schema yet) — this is metadata riding alongside a task/ workflow tool call, never a hard requirement for the tool it's attached to.

Write-path content scanning

Pluggable secret/PII scanning on every write (memories, tasks, projects, sessions, traces), with block/redact/warn enforcement and a witan.scanners entry-point mechanism so other organizations can add their own detection rules without forking. Enabled by default (opt-out) — see docs/write-path-scanning.md for the full config surface, the witan scan test/witan scan rules CLI, the audit trail, and how to write a plugin, and ADR 0001 for the design rationale.

Migrating or merging stores

Moving a store to a new machine, merging two machines' local stores, or moving a local store onto the shared multi-tenant server — same command in all three cases:

witan migrate merge <source> [--target <target>] [--dry-run]

Reconciles slug collisions newest-record-wins (by timestamp) rather than omnigraph load --mode merge's raw last-loaded-wins overwrite, and is repeatable — re-running against an already-merged target loads nothing new. Never mv/copy a store directly (Lance embeds absolute paths). See docs/migration-runbook.md for the full procedure and what was verified about --mode merge's actual collision behavior.

Tests

Integration tests spin up throwaway omnigraph graphs and exercise the real query files end-to-end (they skip automatically if the omnigraph binary is absent):

uv run --group test pytest

Exploring the graph (witan)

A cyclopts CLI for manual inspection of the work-coordination graph and the indexed code graphs. Installed alongside the server (uv run witan …, or uv tool install the package to get it on PATH):

Command Description
setup [--agent claude|pi|…|all] Install witan for one or all supported coding agents; also writes a starter ~/.config/witan/config.toml if one doesn't exist yet
tasks [--ready] [--status …] [--project wp-…] [--all-repos] Tasks for the current repo (closed elided by default — pass --status closed to see them); --ready = open with no open blockers
task <tk-slug> One task's details, blockers, and sub-tasks
task create <title> Create a task from the CLI
task close|claim|release|update|link <tk-slug> … Drive task state transitions from the CLI
run <tk-slug> [--agent claude|pi] [--dry-run] Claim a task and launch an agent to execute it
projects [--status …] [--all-repos] Workflow projects (default: active in this repo)
project <wp-slug> A project with its sessions, tasks, and corpus trace
project status <wp-slug> [--json] "What next" resume view — phase, ready tasks, last session, blockers
project tasks <wp-slug> [--status …] [--detail] A project's tasks; --detail expands each task's blockers and dependents
project create <title> Create a workflow project from the CLI
project advance|complete|block|unblock <wp-slug> … Drive project phase/dependency transitions from the CLI
session start|end|list … Start/close a workflow session, or list a project's sessions
memory [QUERY] [--kind …] BM25 memory search, or (with no query) list memories
code repos Repositories with a code graph indexed (requires witan-code)
scan test <text> Dry-run active detectors against an ad-hoc string; prints findings (never the matched text)
scan rules List active write-path scan detectors, their category, source, and enforcement mode
inject-context [--debug] The UserPromptSubmit hook body; --debug prints detection/read diagnostics to stderr (repo, branch, graph reads, counts, swallowed-failure reasons) to explain a blank block
optimize [--store URI] Compact the store's Lance fragments (non-destructive) so query latency doesn't bloat; safe to run on a cron/systemd-timer
cleanup [--keep N] [--older-than 7d] --yes Reclaim disk by GC-ing old Lance versions (destructive; requires --yes)
serve Start the MCP server (memory + code tools when witan-code is installed)

run claims the task (in_progress + your author), then hands the terminal to the agent seeded with the task description and a reminder to task_close when done — invoke it from the task's repo checkout. Use --dry-run to print the prompt without launching.

Operating Modes

Local Disk (default)

No extra infrastructure. Memories persist at ~/.local/share/witan/graph.omni.

# No env vars required.
export WITAN_AUTHOR="Alice Smith"

Local RustFS (S3-compatible, for testing team mode)

RUSTFS=1 ./install.sh
# Follow the printed export instructions.

Remote Team Server

export WITAN_MEMORY_URI=http://witan.internal:8080
export WITAN_MEMORY_TOKEN=<bearer-token>
export WITAN_AUTHOR="Alice Smith"

See docs/agent-memory.md for full deployment instructions, the graph schema, and the v2 roadmap.

Project Structure

witan/
├── README.md                  # This file
├── install.sh                 # Initialise local graph (manual alternative to `witan setup`)
├── pyproject.toml             # Python package metadata
├── schema/
│   └── schema.pg              # Omnigraph graph schema
├── queries/
│   ├── read.gq                # Read queries
│   └── mutations.gq           # Insert/update queries
├── tests/                     # Integration tests
├── witan/          # Python package
│   ├── __init__.py
│   ├── __main__.py            # Entry point
│   ├── server.py              # FastMCP app + tool definitions
│   ├── cli.py                 # `witan` umbrella CLI (cyclopts)
│   ├── config.py              # Config loaded from env vars
│   ├── context.py             # inject-context / session-checkpoint helpers
│   ├── repo.py                # Git remote → canonical repo slug
│   ├── graph.py               # OmnigraphClient (CLI subprocess wrapper)
│   ├── setup.py               # `witan setup` agent-installer + omnigraph fetch logic
│   ├── extensions/            # Agent extension configs (bundled by setup)
│   ├── hooks/                 # Shell hooks (bundled by setup)
│   └── skills/                # Bundled agent skills
└── config/
    ├── pi.json                # Snippet for ~/.pi/agent/mcp.json
    ├── claude.json            # Snippet for claude_desktop_config.json
    └── copilot.json           # Snippet for .vscode/mcp.json

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

witan_council-0.2.0.tar.gz (308.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

witan_council-0.2.0-py3-none-any.whl (156.8 kB view details)

Uploaded Python 3

File details

Details for the file witan_council-0.2.0.tar.gz.

File metadata

  • Download URL: witan_council-0.2.0.tar.gz
  • Upload date:
  • Size: 308.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for witan_council-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8900e25ee47b7f996b976ae826e3a52b3f2eecafd332357fc2404b4fd219c6b6
MD5 59e5cbb9c80bd2b90506023ea0c51e1a
BLAKE2b-256 d54fa1cdcfdbb052984cd0f2a5146c6134bf7f782a4717296bc216a6ec4fbe85

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan_council-0.2.0.tar.gz:

Publisher: publish-witan.yml on mitodl/agent-kit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan_council-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: witan_council-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 156.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for witan_council-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 21299b2a3676f7a040de5687d594bf9e44a4efbceffbfc55591cfefe00af69f5
MD5 5b3157edf8c34b77091ffe9d2307ccc1
BLAKE2b-256 384e1740fc8e7ff42799fafd78e6b76d7b0155187c81b899ae48425ab2db01dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan_council-0.2.0-py3-none-any.whl:

Publisher: publish-witan.yml on mitodl/agent-kit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page