Metadata-Version: 2.4
Name: witan-council
Version: 0.2.0
Summary: witan — agent memory, planning, and collaboration graph (work-coordination layer + umbrella CLI)
Project-URL: Homepage, https://github.com/mitodl/agent-kit/tree/main/mcp/servers/witan
Project-URL: Repository, https://github.com/mitodl/agent-kit
Project-URL: Documentation, https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan/README.md
Project-URL: Issues, https://github.com/mitodl/agent-kit/issues
License-Expression: BSD-3-Clause
Keywords: agent,coding-agent,knowledge-graph,mcp,memory,task-tracking
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.11
Requires-Dist: agent-config-kit<1,>=0.1
Requires-Dist: cyclopts<5,>=4
Requires-Dist: fastmcp<4,>=3.4.2
Requires-Dist: pydantic<3,>=2
Requires-Dist: rich>=13
Description-Content-Type: text/markdown

# 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](https://github.com/ModernRelay/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](https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan/docs/USER_GUIDE.md)
for a task-oriented walkthrough (install, setup, day-to-day loop, operating
modes, troubleshooting) and the [CLI Reference](https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan/docs/CLI_REFERENCE.md)
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`](./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](../witan-code/README.md#install).

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

```bash
# 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:

```bash
# 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
```

```bash
# 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](../../../docs/agent-memory.md#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](docs/write-path-scanning.md) |
| `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`](./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`](./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`](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](docs/adr/0001-write-path-content-scanning.md) 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:

```bash
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`](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):

```bash
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`.

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

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

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

### Remote Team Server

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

See [`docs/agent-memory.md`](../../../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
```
