Workfile
About
Repository-native protocol and local MCP server for Work, Docs, History and durable project Memory.
Details
- Author
- illodev
- Categories
- Productivity, Knowledge Base, Other
Jump to
Setup
Install Workfile in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/illodev/workfile
Follow the installation instructions in the repository README, then restart your MCP client.
@illodev/workfileis a repository-native protocol for coordinatingWork, Docs, History and durable project Memorybetween humans and software agents.
Markdown files in the repository are canonical. The CLI, HTTP API and local UI use the same core services, collection registry, index and validation rules. No exclusive state is kept in the browser or in a database.
Work, Docs, History and Memory share the commonProjectRecordindex. The core, CLI, HTTP server and MCP runtime are authored in TypeScript and distributed as compiled ESM with public declarations. The local UI is precompiled and included in the package, and semantic search runs on-device through the optional@illodev/workfile-search-localworkspace package.
Try the live demo— it replays this repository's own workspace: the real cards, releases, incidents and learnings of Workfile's development. Mutations work per browser session and reset on reload.
https://github.com/user-attachments/assets/c9cd3035-6729-4cda-9172-984829ab5dbc
Workfile records work. It does not configure agents.
The two get confused because both live next to the same repository. Ecosystem configurators —gentle-aiis a good example — install a persona, curated skills, model routing, MCP servers and review gates into the agents you already use, across many agents at once. Their question ishow your agent works. Workfile's question iswhat was done, who holds it and on what evidence, and its answer is Markdown files that outlive the agent, the session and this package.
They compose. A well-configured agent still needs somewhere durable to write down what it did.
What is here, and is not a configurator's job:
- The repository is canonical.A card is a file in the pull request: reviewed in the diff, reported byworkfile doctorwhen malformed. No exclusive state in a browser, a database or~/.config. Remove the package and the records stay readable.
- Claims are enforced, not agreed.Ownership is checked at the mutation, so a card another actor holds refuses your transition withCARD_CLAIM_OWNER_MISMATCHinstead of quietly accepting it — a guarantee no sentence in a prompt can make.
- reviewis notdone.donerequires evidence from somewhere the code actually ran. A merge is not evidence.
- Humans read the same records.The UI, the rendered changelog and the releases are derived from exactly what the agent writes; there is no machine view and human view to keep in sync.
What is deliberately absent: Workfile does not install or update agents, ship a persona, route models or curate a skill catalogue. It syncs its own protocol into the instruction files an agent already reads (workfile agents sync) and exposes every operation over MCP — vendor neutral, but a server, not an ecosystem.
- Node.js 22 or newer.
- npm, pnpm, yarn or Bun may invoke the package.
Everyworkfile …command in this README requires the package to be installed —pnpm dlx/npxone-offs run a command and discard the binary afterwards:
pnpm add -D @illodev/workfile # per repository (recommended) pnpm workfile doctor # dependency bins run through pnpm / npx pnpm add -g @illodev/workfile # or globally: workfile lands on your PATH workfile doctor wf doctor # wf is the same binary, for typing by hand
wfis an alias, not a rename: both names reach the same entry point, and the help and error hints answer in whichever one you typed. Keep the long form in anything generated or shared.wfonly resolves once the package is installed, and an unrelatedwfexists on the registry — sonpx wfwould fetch someone else's tool wherenpx workfilefails outright.
pnpm dlx @illodev/workfile initis fine for one-shot initialization, but keep the package as a devDependency afterwards: that is what makes theprojectscripts thatinitadds to package.json resolve. That prefix is an npm script namespace —pnpm projectopens the UI,pnpm project:doctorruns the checks — and has nothing to do with the old binary name.
The published surface exposes JavaScript and declarations through conditional package exports. TypeScript consumers receive typed configuration, workspace, record, search and integration contracts from the root package and every documented subpath:
import { defineProject, type CardStatus, type ProjectConfig, type ProjectRecord } from "@illodev/workfile"; import { createSemanticSearchProvider } from "@illodev/workfile/search"; const config: ProjectConfig = defineProject({ schemaVersion: 2, name: "Billing", cards: { areas: ["api", "web"] } }); const status: CardStatus = "doing";
The CLI and UI do not require TypeScript in consuming projects. React, Primer, Vite and the UI type packages are build-only dependencies; the installed package serves bundled browser assets fromdist/ui.
A project is discovered throughproject.config.mjsand normally stores protocol-owned files under.project/:
project.config.mjs .project/ ├── VERSION ├── cards/ │ └── archive/ ├── assets/ ├── docs/ ├── changelog/ │ ├── unreleased/ │ └── releases/ ├── memory/ │ ├── learnings/ │ ├── decisions/ │ ├── incidents/ │ ├── conventions/ │ └── context/ ├── agents/ └── .cache/
Minimal configuration — a plain object, notdefineProject(...). The loader appliesdefineProjectitself, and an import here is a bare specifier the file can only resolve withnode_modulespresent, which breaks the two consumers that run without one: apnpm dlx-initialized workspace before the package is installed, and the generated CI job'snpxrun on a clean clone. The JSDoc annotation keeps editor typing without a runtime import:
/ @type {import("@illodev/workfile").ProjectConfigInput} / export default { schemaVersion: 2, name: "My project", cards: { areas: ["api", "web", "infra", "docs"] }, docs: { sources: [ "README.md", "docs//.md", "apps//README.md", ".project/specs//.md" ] }, changelog: { releaseStrategy: "semver", defaultVisibility: "public" }, memory: { collections: [ "learnings", "decisions", "incidents", "conventions", "context" ] }, agents: { targets: ["agents-md", "cursor"] }, ci: { targets: ["github"] }, mcp: { allowMutations: true }, search: { semanticWeight: 0.35, maxProviderRecords: 500 } };
Project-specific areas, paths and vocabularies are resolved at runtime and exposed through the effective schema. The eight Work statuses and the schema-v2 memory collection semantics remain protocol contracts.
Cards are managed Markdown records under.project/cards/. The Work module provides hierarchy, dependencies, claims, scope, status transitions, archives, assets and conflict-aware writes.
workfile card list --json workfile card show T-0042 --json workfile card create --title "Implement runtime schema" --area infra workfile card create --json-input card.json # body, parent, source and tags in one call workfile card claim T-0042 --scope apps/api,packages/sdk # actor resolves itself workfile card transition T-0042 review workfile card patch T-0042 --json-input changes.json --expected-revision sha256:... workfile card archive T-0042 workfile card reopen T-0042 --status backlog
Docs combines two sources without copying existing documentation:
- Indexed documentsdiscovered from configured globs. They receive deterministicPATH-IDs and remain read-only through the protocol.
- Managed documentsstored in.project/docs/with stableDOC-NNNNIDs, typed frontmatter and revision-aware mutations.
Managed documents are read recursively, so they can be grouped in folders — including folders you create by hand. IDs stay global and sequential: a folder is organization, not identity. New documents followdocs.layout(kind, the default, groups them by document kind;flatwrites them to the managed root) and--folderoverrides it.
workfile doc list --query billing workfile doc show DOC-0012 --json workfile doc create --title "Deployment runbook" --kind runbook --status current workfile doc create --title "Rate limiting" --folder architecture/billing workfile doc move DOC-0012 --folder architecture workfile doc patch DOC-0012 --json-input changes.json --expected-revision sha256:...
The doctor detects broken local links, unresolved related or superseded records, missing scope paths and stale review/source relationships.
History uses atomic change fragments rather than asking multiple branches or agents to edit one sharedCHANGELOG.md.
--- id: CHG-0042 title: Add portable history workspace type: added area: infra visibility: public cards: [T-0042] created: 2026-07-28 updated: 2026-07-28 ---
A release consumes selected fragments, moves them beneath the release directory and creates a canonicalREL-NNNNrecord. Public or internal changelogs are derived output.
workfile changelog list --unreleased workfile changelog add --title "Add portable history" --type added --area infra workfile changelog preview workfile changelog release 0.4.0 --title "History and Memory" workfile changelog render --visibility public workfile changelog render --visibility public --write workfile changelog verify
Release versions can usesemver,calendarorfreeformvalidation according to configuration. Fragments and releases participate in the same workfile search and backlink graph as cards, docs and memory.
Memory is a set of typed, atomic and lifecycle-aware records rather than a single growing conversation transcript:
workfile memory list --collection learnings --status active workfile memory add learning --title "Atomic fragments avoid merge conflicts" \ --confidence high workfile memory add decision --title "Keep Markdown canonical" --status accepted workfile memory add incident --title "Release pipeline stalled" --severity high workfile memory graduate LRN-0004 --to CONV-0002,DOC-0012 workfile memory supersede ADR-0003 --by ADR-0009 workfile memory patch CTX-0002 --json-input changes.json --expected-revision sha256:... workfile memory verify
The doctor checks invalid lifecycle states, missing graduation/supersession targets, expired context and incomplete incident resolution metadata.
Every module normalizes its files asProjectRecordentries through a common collection registry. The derived process-local index provides:
- weighted full-project text search;
- lookup by stable record ID;
- outgoing references and incoming backlinks across all four domains;
- cardsource:links and local Markdown links;
- module-specific health, lifecycle and freshness signals;
- module and collection counts.
Canonical state always remains on disk. The server cache is short-lived, invalidatable and fully rebuildable.
workfile search "billing architecture" workfile search release --kind change,release,memory --limit 25 --json
The initializer can run interactively or deterministically in automation. It detects the package manager, monorepo folders, likely card areas, documentation sources, existing agent environments and CI providers. A dry run exposes the exact filesystem plan.
pnpm dlx @illodev/workfile init pnpm dlx @illodev/workfile init --yes \ --agents agents-md,claude,cursor,copilot --ci github workfile init --dry-run --json
The generatedproject.config.mjsexports a plain object, so a workspace initialized viapnpm dlxremains loadable before the package is installed locally. Existing files are not overwritten unless--forceis explicit..project/.cache/is added to.gitignore; all canonical protocol files remain tracked.
The UI ships with a demo mode for static hosting (Vercel, GitHub Pages, any file server).npm run build:demobuilds the UI with an in-memory API that replays a snapshot of a seeded workspace: every view works and mutations behave normally for the session, then reset on reload. The repository includes avercel.json, so importing it into Vercel deploys the demo with zero configuration.
pnpm run demo:data # reseed and resnapshot packages/workfile/ui/src/demo-data.json pnpm run build:demo # static demo build into packages/workfile/dist/demo
Regular builds tree-shake the demo layer and snapshot out of the bundle.
There is no prerelease channel: every published version is onenpm installaway, and av-rc.tag fails the release rather than publishing. That is a consequence of trusted publishing rather than a preference — OIDC authorizesnpm publishand no other registry write, so CI cannot move a dist-tag off a release candidate once it has been set.
Canonical instructions and workflows live under.project/agents/. Compact managed blocks are synchronized into supported environments without replacing unrelated user content:
AGENTS.md CLAUDE.md .cursor/rules/workfile.mdc .github/copilot-instructions.md
workfile agents sync workfile agents sync --targets agents-md,claude,cursor,copilot workfile agents check workfile agents context --card T-0042
Managed blocks carry the package version and a SHA-256 digest.agents checkandworkfile doctorreport missing, unmanaged or stale generated instructions. Agent context is bounded and prioritizes the selected card, direct relationships, active conventions, unresolved incidents and non-expired context instead of loading all workfile memory.
Workfile includes a local, dependency-free MCP server using UTF-8, newline-delimited JSON-RPC over stdio. It delegates every operation to the same core services used by the CLI and HTTP API, speaks both the modern (2026-07-28) and legacy (2025-11-25) protocol revisions, and exposes 30 tools, four resources and three prompts. Mutation tools disappear entirely in--read-onlymode.
Point a client at it without installing anything. This is the invocation theofficial registrypublishes forio.github.illodev/workfile, and what most clients will build for you from that listing:
{ "mcpServers": { "workfile": { "command": "npx", "args": ["-y", "@illodev/workfile", "mcp"] } } }
mcpthere is a subcommand, not a binary:npxresolves the bin whose name matches the package and hands it everything that follows. Append--root PATHwhen the client starts somewhere other than the workspace — it searches upwards for.project/otherwise — and--read-onlyto serve the read tools alone, with every mutation refused.
With the package installed, the same server is a subcommand away:
workfile mcp workfile mcp inspect --json workfile mcp config --read-only --json
For Claude Code the same surface ships as a plugin — the MCP server plus/claim,/context,/nextand/donecommands, a skill, and hooks that turn card claims into an executable guard rail — with no generated files committed to the repository:
/plugin marketplace add illodev/workfile /plugin install workfile@illodev
The full contract — tool inventory, resources, prompts, process hygiene and the plugin's surface — is documented indocs/mcp.md.
The server is listed onGlama, which builds it in a container and inspects the capabilities it reports:
Lexical search remains deterministic and local. Hosts may inject an optional semantic provider programmatically; Workfile never selects a vendor or sends repository content over the network by itself.
@illodev/workfile-search-localruns embeddings on-device (onnxruntime-web, ONNX on CPU,Xenova/multilingual-e5-smallquantized) — repository content never leaves the machine. Declare it inproject.config.mjswith aguarded import, because the config must also load where the package cannot resolve (the generated CI job runsnpxon a clean clone):
export const integrations = await (async () => { try { const { localSearchIntegration } = await import( "@illodev/workfile-search-local" ); return [localSearchIntegration()]; } catch { return []; // package absent: search stays lexical } })(); export default { // … search: { provider: "local-embeddings" } };
Know the cost model before wiring it: thefirst*hybrid search embeds every uncached candidate record — minutes of sustained CPU on a few-thousand-record workspace, triggered by whichever surface searches first (CLI, board UI, or the MCP server an agent loads). The provider caps ONNX at half the cores by default, persists per batch so an interrupted pass resumes instead of restarting, and reports progress on stderr; sizingsearch.maxProviderRecordsto your corpus makes every record eligible. Details and options inpackages/search-local/README.md.
import { createSemanticSearchProvider, searchProjectRecordsHybrid } from "@illodev/workfile/search"; const provider = createSemanticSearchProvider({ id: "company-embeddings", async search({ query, records }) { // Return [{ id, score }] with scores between 0 and 1. return rankWithYourApprovedProvider(query, records); } }); const result = await searchProjectRecordsHybrid(index.records, query, { provider, semanticWeight: 0.35 });
The adapter boundary makes external data disclosure an explicit host decision and keeps the canonical Markdown/index implementation provider-independent.
Programmatic hosts can group approved semantic search and health adapters in a small, vendor-neutral registry:
import { createIntegrationRegistry, defineProjectIntegration } from "@illodev/workfile/integrations"; const integrations = createIntegrationRegistry([ defineProjectIntegration({ id: "company.platform", semanticSearchProvider: provider, async healthCheck({ workspace, index }) { return []; } }) ]);
The registry is accepted by the MCP server and doctor APIs. It is intentionally limited in the current RC: vendor-specific issue trackers, deployment systems and credentials are not part of the canonical package. The boundary can mature from real integrations without committing the schema to GitHub, GitLab, Jira or a deployment provider.
CI files use the same managed-file contract and can be generated for GitHub Actions, GitLab CI or a generic shell runner:
workfile ci sync --targets github,gitlab,generic workfile ci check
Templates run both the workfile doctor and agent synchronization check against the pinned Workfile version.
The v1.planningsystem can be planned and applied with deterministic collision checks:
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





