Skip to content

Contributing

Contributions are welcome. The canonical guide is CONTRIBUTING.md in the repo; this mirrors it.

Runs on Python 3.10+, tested on Linux · macOS · Windows.

Terminal window
git clone https://github.com/vikast908/WritingAgent.git
cd WritingAgent
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\Activate.ps1 # Windows (PowerShell)
pip install -e ".[dev]" # editable install + pytest + ruff

You do not need an API key to develop or run the tests - see fake mode below. Add one (cp .env.example .env, set OPENROUTER_API_KEY) only for real runs.

Optional - the engine imports them lazily and degrades gracefully without them.

  • Deep-research fetch backend - pip install -e ".[deep]" (Scrapo + Playwright, Python 3.11+), then python -m playwright install chromium for the browser binary.
  • Firecrawl search - set FIRECRAWL_API_KEY in .env and search_provider: firecrawl to swap the default DuckDuckGo backend (a missing key falls back to DuckDuckGo).
Terminal window
pytest

The suite runs fully offline. Pipeline tests use fake mode (WRITINGAGENT_FAKE=1), where every LLM node returns deterministic placeholder output - no network, no key. Brain and index isolation is autouse, so tests never touch your real telemetry or projects.

Terminal window
ruff check . # lint
ruff format . # format

A .pre-commit-config.yaml is provided - run pre-commit install to lint on commit.

The browser UI lives in src/writingagent/webui/ - a pure-stdlib ThreadingHTTPServer + SSE backend (server.py) and a single-page app under static/. There is no build step and no bundler; just run:

Terminal window
writing-agent web # http://127.0.0.1:8787, auto-opens the browser
writing-agent web --port 9000 --no-browser

It binds 127.0.0.1 only with no auth and runs one job at a time - see the HTTP API for the endpoints. webui/server.py calls the same engine facade the TUI does, so a change to the pipeline shows up in both without extra wiring.

Both the web dashboard and the TUI follow the editorial design system spec’d in docs/design.md (ink on warm paper, one accent - manuscript red #a3341f - the Fraunces display serif, the pilcrow mark, WCAG-AA verified). Values are tokens: port them 1:1 to CSS vars for the web app or to a ui.THEMES palette for the TUI. Named themes recolor but never restructure - match design.md rather than hand-picking colors.

  • docs/plan.md is the architecture/spec source of truth; docs/dev/resume.md is the running dev journal (newest entry on top). Durable decisions go in docs/plan.md, not docs/dev/resume.md. The journal is maintainer-local (untracked) - it stays on the maintainer’s machine and isn’t published, matching the root CONTRIBUTING.md.
  • Keep nodes deterministic LLM calls (see docs/plan.md §4) - don’t add free-roaming agentic behavior; the orchestrator decides the sequence.
  • Network/IO is best-effort: degrade gracefully, never crash the pipeline on a fetch error.
  • All numeric thresholds are tunable config (config/settings.yaml), not hard-coded.
  • Cross-platform: use pathlib, avoid shelling out, don’t assume a POSIX or Windows path layout. CI runs the suite on all three OSes across Python 3.10–3.13 - keep it green.
  1. Branch off master.
  2. Make the change + add/adjust tests.
  3. ruff check . && pytest locally.
  4. Open a PR describing the change and linking any issue. CI must pass.

A 60-second map (full detail in Architecture and docs/plan.md):

  • orchestrator/ - durable on-disk state machine (the brain is the checkpoint); a package (common · book · article · export · manage · review) behind a stable orchestrator/__init__.py facade.
  • nodes.py / prompts.py / schemas.py - the LLM nodes, their prompts (incl. the wrap_untrusted injection fence), and structured outputs.
  • llm.py - OpenRouter wrapper (retry/backoff, timeout, repair, token budget, cost tallies).
  • telemetry.py - per-call JSONL records + the /dashboard aggregation (per-node / per-unit cost attribution behind the Telemetry / Cost views).
  • brain.py / store.py - markdown filesystem layout + SQLite/FTS canon & graph.
  • search.py / deep_research.py / images.py / cache.py - the optional research stack (search_provider: DuckDuckGo default, Firecrawl opt-in).
  • seo.py / promote.py - the local promotion layer: on-page SEO audit + keyword pack, and platform variants / headline variants / restyle. Both write local artifacts only.
  • webui/ - the local web dashboard (server.py + static/); see above.
  • shell/ / cli/ / ui.py - Rich TUI (a package: branding · help · commands · dashboard · chat · dispatch · slash · session · repl, behind shell/__init__.py), one-shot CLI, and the theme registry (incl. the colourblind-safe highcontrast; default editorial “ink & brass”).
  • export.py - pdf · epub · html · docx · txt · md renderers.

See also SECURITY.md and the Changelog.