@rryando/darcs
v4.6.0
Published
Darcs — DAG-based task orchestration for AI agents. Persistent workflow continuity via graph-structured context.
Maintainers
Readme
Darcs
Durable Agent Routing & Context System
Keep project context between agent sessions.
Darcs is a local-first CLI for durable, dependency-aware agent work. It is a history-preserving fork of ARCS: projects retain their task queue, plans, and reusable knowledge as a local DAG so the next session can begin with evidence rather than rediscovery.
4.4.0 release highlights
- The new
/collaborateworkspace creates and edits workspace-visible or project-global DOCX files through a local TipTap, Mammoth, anddocxeditor with immutable versions, optimistic conflict handling, and restore controls. - Claude Code collaboration runs through a temporary, strict five-tool MCP bridge: document edits remain pinned to one DOCX, cited knowledge and vault reads are turn-scoped, and cancellation, stale anchors, and version races fail without overwriting newer work.
- Diagram changes use cited proposal-and-confirm flows, while reviewed DOCX content reaches curated knowledge only through a separate preview, explicit confirmation, and idempotent publication receipt.
- The package remains MIT and local-first. SuperDoc is excluded; required permissive editor notices ship with both the npm package and compiled web client, and current semantic DOCX fidelity limits are documented below.
Quick Start
Prerequisite: Node.js 20 or later.
npm install -g @rryando/darcs
darcs initThen register a project and use the core loop:
darcs project init
darcs brief # Current focus and next action
darcs next # Next dependency-safe task
darcs done <task-id> # Complete a task and unblock dependents
darcs remember "..." # Capture a durable lessonAll commands support --json; use darcs --commands --json for discovery.
darcs brief returns an operating brief with the current focus and next action.
Local Data and Boundaries
Darcs stores its local state in ~/.darcs by default. Set DARCS_DATA_DIR to use another writable directory.
Project state lives under ~/.darcs/projects/<slug>/: plan records as plans/*.md with .meta.json and .diagram.mmd siblings, knowledge entries as knowledge/*.md with .meta.json siblings, and the project-global partition of the graph as graph/nodes.jsonl, graph/edges.jsonl, and graph/manifest.json. Tasks are stored only as nodes in that partition; tasks.md remains the rendered human view.
Each registered checkout has an immutable workspace ID and a workspace partition at ~/.darcs/projects/<slug>/workspaces/<workspace-id>/ (or below DARCS_DATA_DIR). That partition holds document artifacts and ledgers, the workspace graph, collections, chat history, workflows, hook credentials, and managed converter state. Original source files remain in the checkout. New registrations use global authority immediately and do not create durable <workspace>/.darcs state.
Resolve a workspace by current directory, project slug plus a single linked workspace, canonical path, or immutable workspace UUID. Use darcs workspace list --project=<slug> to discover IDs and authority before an explicit migration. Removing a project path unlinks it without deleting its ID, global partition, or local backup; adding the same canonical path later relinks the same identity.
darcs web runs one loopback-only server over the global registry and can switch among projects and workspaces without restart. Detached workspaces keep stored metadata, artifacts, and history readable, but source reads can be unavailable and all mutations are refused until the source path returns.
Existing legacy workspaces remain authoritative at <workspace>/.darcs until darcs workspace migrate --project=<slug> --workspace=<uuid> performs an explicit, versioned, copy-only migration. Activation happens only after validation; the local directory is retained untouched as a backup. darcs workspace rollback is safe only before any global-authority write. After a global write, Darcs does not automatically reverse-migrate, export, replace, merge, or delete either copy. See Workspace storage migration and recovery.
Reads never write. A stale or damaged partition is changed only by an explicit darcs graph repair <slug>.
Earlier versions kept separate tasks/index.json, plans/index.json, and knowledge/index.json files plus a workspace documents/graph-mapping.json. Replay them into the graph once with:
darcs migrate graph # every tracked project and linked workspace
darcs migrate graph --slug=<slug> # one project
darcs migrate graph --workspace=<path> # one workspaceThe replay is one-shot: it writes the partitions and then removes the legacy files it read. An aborted replay exits non-zero and leaves those legacy files in place.
Darcs does not automatically read, migrate, delete, or mutate ~/.arcs. Existing ARCS state and assets remain separate and unmanaged.
The CLI is local-first. It requires no external API key, LLM, cloud service, CDN, runtime network connection, or external model provider. Agent hosts and their configured providers remain their own concern.
Three Surfaces
| Surface | Storage | Purpose |
|---|---|---|
| Queue | graph/nodes.jsonl (rendered to tasks.md) | Dependency-aware work items |
| Plan | plans/*.md and .diagram.mmd | Durable multi-step change records |
| Memory | knowledge/*.md | Reusable lessons, gotchas, patterns, architecture, and decisions |
darcs next selects the first unblocked task. darcs diagram ready reports the unblocked nodes in a plan's Mermaid execution map. The DAG preserves dependencies across the queue, plans, and knowledge, and the graph partitions record how those surfaces connect to each other and to synced documents.
Commands
| Command | Purpose |
|---|---|
| darcs brief <slug> | Operating brief: focus, next action, and knowledge health |
| darcs next <slug> | Next dependency-safe task and related knowledge |
| darcs task create <slug> <title> --dependsOn=id1,id2 | Add a task with dependency edges |
| darcs plan create <slug> <title> | Create a durable plan record |
| darcs knowledge upsert <slug> <title> --kind=<kind> | Create or update reusable knowledge; --derives-from, --relates-to, and --supersedes record its edges |
| darcs graph search <slug> <query> | Search the graph, citing the documents each knowledge hit derives from |
| darcs graph neighbors <slug> <node> | List the nodes reachable from one node |
| darcs graph audit <slug> | Report orphans, dangling references, and stale derivations without changing anything |
| darcs graph repair <slug> | Fix referential integrity problems in a project's stored graph |
| darcs project init | Register the current directory as a project |
| darcs validate <slug> --checks=<check> | Check project health |
Discover the current, machine-readable registry command set with:
darcs --commands --jsonRegistry discovery lists only commands registered through defineCommand, including the graph family (graph inspect, graph neighbors, graph paths, graph subgraph, graph search, graph repair, graph audit) and darcs migrate graph. Public special routes are dispatched outside the registry: darcs init, darcs config, darcs opencode, darcs claudecode, darcs import, and darcs graph view.
Local Documents and Graphs
Darcs deterministically imports a local document into a Darcs-managed Markdown artifact with provenance:
darcs import path/to/document.pdfThe import result reports the artifact and provenance paths. Importing does not open a browser. Add --view only when you want to inspect the imported Markdown graph locally:
darcs import path/to/document.pdf --view
darcs graph view path/to/document.mddarcs graph view <markdown-file> starts a dependency-free local viewer for one UTF-8 Markdown document. It binds only on loopback and serves the same canonical graph response used by the CLI.
Workspace-scoped MarkItDown
Interactive darcs init and darcs config can offer to set up document conversion. You can also inspect and confirm it directly:
darcs documents setup markitdown
darcs documents setup markitdown --jsonThe JSON/non-interactive form is read-only: it reports whether setup or repair is required and the fixed command plan. In an interactive terminal, Darcs asks for fresh confirmation before either setup or repair. Approval creates tools/markitdown-venv inside the selected workspace's global Darcs partition and installs the managed package there; it never performs a system install, changes PATH, or uses sudo. The approved pip step may access its configured package index.
Setup and repair install into a sibling candidate environment and verify Python, MarkItDown, and PDF conversion before promotion. A failed install or verification leaves an existing active environment and state in place. After successful promotion, removal of the previous backup is best effort; a stale sibling backup may remain without making the promoted environment degraded.
Optional Docling structured enrichment
MarkItDown remains the mandatory local converter. Docling is a fully optional, additive add-on: when present, OCR-eligible images (.png, .jpg, .jpeg, .tiff, .bmp, .webp) synced through a document collection are routed to Docling instead of MarkItDown, and table extraction and picture classification are captured automatically into a structured.json sidecar next to the usual Markdown artifact — none of this ever gates a successful import. PDFs and every other format stay on MarkItDown. Darcs never installs the docling executable itself; you install it yourself (python -m pip install docling), and Darcs only manages the shared, machine-wide model-weights cache, with your explicit confirmation:
darcs documents setup docling
darcs documents setup docling --jsonLike MarkItDown's setup flow, the JSON/non-interactive form is read-only. In an interactive terminal, Darcs asks for fresh confirmation before downloading Docling's model weights into a cache shared by every workspace on the machine — never per-workspace, never a global install, PATH change, or sudo. A broken (but present) Docling install is reported, not auto-repaired: Darcs doesn't own that executable.
Chart extraction is the one Docling capability that runs a local generative image model, so it's opt-in and double-gated: it requires both this explicit flag and a machine-capability check (≥16GB RAM or CUDA) before it will run anything, and it stays entirely local — no network access.
darcs documents sync --enable-chart-extractionAgent-authored documents
Every document create or edit request starts with the document-artifact skill. It is the mandatory source-first entry point: the agent resolves the canonical source, keeps the write inside a collection already approved by darcs documents add, and invokes the appropriate specialist procedure. Darcs itself neither transcribes nor generates content. The agent writes the source file, and an explicit darcs documents sync --json converts, indexes, and links it.
The web Library separates source records into Library Artifacts and Diagrams. Artifacts are immutable evidence of synchronized source content, never edit targets. The Library can request source-specific reconversion and reports the returned source, artifact, and relationship states; it does not turn derived Markdown into an editable substitute for a non-Markdown source.
document-artifactroutes every create or edit request, checks the approved collection boundary, resolves the real source rather than a derived artifact, asks before overwriting an existing source, and reports source-write, sync or reconversion, artifact, and relationship statuses separately.document-annotatetranscribes one local image, using the host agent's own vision, into<image file name>.annotation.mdbeside it. Sync derives alinkedge from that exact filename suffix and synthesizes the image's own graph node when the image has no converted artifact. Darcs runs no OCR and no model to read the image.document-diagramauthors or reconstructs exactly one canonical local diagram source: Mermaid TD.mmdbeginning withflowchart TD, PlantUML.puml, or Draw.io.drawio. Its<canonical-source>.annotation.mdcompanion carries the load-bearing suffix that produces the graph link. An optional same-stem.svgis accepted only when the user explicitly supplies it; it is a static snapshot, is never canonical, and is not regenerated automatically.document-templatederives a<slug>.template.mdfrom two or more samples read throughdarcs documents inspect. The template declares{{snake_case}}fields, a fenced structure skeleton, and a## Derived fromlist whose relative links become one graph edge per sample.document-draftwrites a new<slug>.mdfrom a saved template, carrying a real relative markdown link back to that template.
Which mechanism produces the edge depends on whether the artifact converts. A file Darcs converts to Markdown — a template, a draft — is linked by a relative markdown link written inside it. A file it does not convert is linked by the .annotation.md companion written beside it. Either way the new file must land under a folder already approved with darcs documents add: files outside an approved root are never scanned, so they never reach the graph.
Diagram authoring: No hosted renderer, CDN, Java runtime, embedded editor, background regeneration, or generic document editor is required or provided. It stays local text or XML, and the optional SVG remains a user-supplied snapshot rather than a rendered preview.
DOCX Collaboration
The web dashboard's /collaborate page opens and edits DOCX files in either the current workspace scope or the machine-wide global scope. Collaboration is local-first: the document assistant uses the locally installed Claude Code bridge, and Darcs adds no hosted service, cloud document store, API key, or CDN dependency. Each successful edit creates an immutable document version; stale-version or stale-hash writes are rejected as conflicts rather than silently overwriting newer work. Knowledge publication is a separate, explicit user action rather than a side effect of editing or chat.
DOCX fidelity is intentionally limited. Import and export round-trip semantic text and bold/italic emphasis. Headings, list numbering, advanced page layout, images, and tables may flatten or be lost, and chat commit serialization is text-oriented rather than a lossless Word document transform. Current residual risks also include an orphan-sidecar crash window between document and sidecar persistence and conversation writes that are not atomic with every related file operation; immutable versions and conflict checks limit overwrite risk but do not remove those crash-consistency gaps.
Agent-Host Bundles
Darcs provides optional bundles for supported agent hosts.
Both bundles carry the same ten skills: document-artifact, document-collection, document-sync, document-search, document-annotate, document-diagram, document-template, document-draft, knowledge-curate, and install-darcs-claude-code-hook. Claude Code deploys them under a darcs- prefix, so document-artifact installs as darcs-document-artifact. The OpenCode bundle also registers exactly three slash commands: darcs-document-sync, darcs-document-search, and darcs-knowledge-curate.
- OpenCode: Darcs-owned bundle files live under
opencode/darcs. Deployment does not impose a default cloud-model override; existing provider and model choices remain user-controlled. Deployment does not migrate ARCS assets, create MCP configuration, or wire external codegraph or RTK integrations. - Claude Code: the Claude deployer owns only the agent and
darcs-*skill files listed by its exact Darcs ownership marker. On later deployments it removes only obsolete marked agent/skill paths. ARCS assets, user or unmarked files, foreign markers, and settings remain untouched. Dry-run does not mutate the target.
Use the explicit platform routes: darcs opencode for the OpenCode bundle and darcs claudecode for the Claude Code bundle. These routes validate Python 3.10+ and MarkItDown before host deployment. The separately confirmed workspace lifecycle above may create its local environment and install its pinned capability; it does not install Python or modify a host installation.
Preview Claude bundle deployment without writes with:
darcs deploy-claudecode-superpowers --dry-runDocument and Graph Boundaries
darcs graph view covers a single local Markdown document, its headings, containment relationships, and eligible resolved relative links. A document sync publishes those same node and edge shapes, for every synced document, into the workspace graph partition. The canonical response is shared unchanged by CLI and browser adapters. It does not use Graphify, a graph library, a CDN, or a cloud service.
Darcs does not run a document watcher, scheduler, or daemon. Document work happens only when a user or agent invokes a command. Darcs ships local codegraph integration, but it does not deploy a public codegraph service or MCP server, create public MCP wiring, or publish documents to a cloud service.
Development
git clone https://github.com/rryando/darcs.git
cd darcs
npm install
npm run build| Command | Description |
|---|---|
| npm run build | Compile TypeScript to dist/ |
| npm run test | Run the Vitest suite |
| npm run typecheck | Type-check without emitting files |
| npm run lint | Run Biome checks for src/ and test/ |
| npm run build:opencode-bundle | Build the optional OpenCode bundle |
Tests that read or write Darcs data must use an isolated temporary DARCS_DATA_DIR; see CLAUDE.md.
During development, run checks against only the files being changed, for example:
npx vitest run test/readme.test.ts
npx biome check test/readme.test.tsBefore release, build and lint the bundle, then inspect the npm package contents without publishing:
npm run build:opencode-bundle
darcs lint-bundle
npm pack --offline --dry-run --jsonPackage inspection is verification only. Publishing the npm package and deploying a host bundle are separate, explicit operations.
Web dashboard
The optional web dashboard is a React SPA you can build, run, and hot-reload from the repo root without cd web. Root web:* scripts delegate into the web/ npm workspace.
Build from root
npm run build # TypeScript compile, then the web build
npm run build:web # Web workspace build; promotes web/dist → dist/web-clientnpm run build runs tsc followed by npm run build:web. npm run build:web builds the web workspace and promotes its output from web/dist to dist/web-client via scripts/copy-web-client.mjs.
Run from root
npm run web:serve # node scripts/darcs-cli.mjs webnpm run web:serve is the same as darcs web. It serves the SPA from dist/web-client/ on port 4173 by default, injects one process-scoped, ephemeral window.__DARCS_DASHBOARD_TOKEN__ into the shell, and serves /api/*. That dashboard token authorizes browser API requests only; it is not a persisted workspace hook credential.
Hot reload
Use two terminals:
# Terminal 1 — API half on port 4173
node scripts/darcs-cli.mjs web --no-open
# Terminal 2 — Vite dev server on port 5173
npm run web:devTerminal 2 runs Vite on port 5173, proxying /api to 127.0.0.1:4173. On each shell transform, its serve-only plugin fetches the backend shell from the configured loopback backend origin (default http://127.0.0.1:4173), extracts the ephemeral window.__DARCS_DASHBOARD_TOKEN__ assignment, and safely relays it into Vite's shell. The plugin does not read or expose hook credentials, does not persist the dashboard token, and leaves the shell unchanged if the backend or token assignment is unavailable. Start the backend first; reloading after a backend restart relays its new process-scoped token.
For a different backend port, set the same loopback origin in both terminals; Vite uses it for the API proxy and token relay:
# Terminal 1
DARCS_WEB_BACKEND_ORIGIN=http://127.0.0.1:4188 node scripts/darcs-cli.mjs web --port=4188 --no-open
# Terminal 2
DARCS_WEB_BACKEND_ORIGIN=http://127.0.0.1:4188 npm run web:devWorkspace note
Never run npx from inside web/ — node_modules is hoisted to the repo root. Use the root delegation scripts (npm run web:dev, npm run web:test, npm run web:typecheck) instead.
Claude Code integration
The dashboard can observe this workspace's Claude Code sessions. A session hook posts the session lifecycle to darcs web, which renders it in the Sessions view. Install it once per workspace:
darcs hooks install-claude-code
darcs hooks install-claude-code --port 8080
darcs hooks install-claude-code --url http://127.0.0.1:8080--port and --url both name the dashboard origin the hook posts to and are mutually exclusive; the default is http://127.0.0.1:4173. The command writes only the workspace-local .claude/settings.local.json — never ~/.claude/settings.json, because the credential it installs belongs to this workspace alone. It merges its entry by the absolute hook-script path, so a re-run replaces that entry in place and unrelated hooks in the file are left alone; settings it cannot parse abort the install before a byte is written. Claude Code snapshots hooks at session start, so start a new session afterwards.
The hook registers four session events — SessionStart, SessionEnd, UserPromptSubmit, and Stop — and each one posts {hookEventName, sessionId, cwd, at}. That is the whole payload: the prompt text on UserPromptSubmit and the transcript path Claude Code supplies are dropped by the hook rather than sent, so neither the text you typed nor a path into your transcripts ever leaves the local process boundary.
The request carries a per-workspace bearer token kept under the selected workspace's authoritative state partition at hooks/claude-code-token.json; before migration, legacy local authority uses .darcs/hooks/claude-code-token.json. It is owner-readable and provisioned once. This credential authorizes hook ingest only and is separate from the ephemeral dashboard token, so starting the dashboard never rotates it or exposes it. The token is embedded in the installed hook command and is never echoed: no envelope, terminal line, or log prints it.
To uninstall, delete from .claude/settings.local.json the hook entries whose command names the darcs hook script path — that settings file remains in the source workspace, and the script path is the same key the installer merges by — and leave the rest of the file alone. Deleting hooks/claude-code-token.json from the selected authoritative global workspace partition (or legacy .darcs/hooks/claude-code-token.json before migration) rotates the credential, which retires any hook entry still carrying the old one.
With the hook installed and the claude CLI on PATH, the reader closes the loop: select text in an .md document, ask Claude about it, and the turn runs as a confined child process allowed to edit that one file and nothing else. Each turn is snapshotted before and after under chat/ in the selected authoritative global workspace partition (or legacy .darcs/chat/ before migration), so an edit you dislike is one undo away and redo puts it back.
License
MIT
