Local dashboard for attune-rag / attune-help / attune-author. Server-rendered Jinja2 UI — ships clean via PyPI with no npm step.
Project description
attune-gui
Local dashboard for the attune-* documentation family
(attune-rag, attune-help, attune-author). Server-rendered Jinja2
UI ("Cowork dashboard") backed by a FastAPI sidecar — ships clean via
PyPI with no npm step required to run.
What it does
Sidebar nav with seven pages, each consuming the existing JSON API:
| Page | What it shows |
|---|---|
| Health | Cross-layer health (rag/help/author/gui versions) + corpus snapshot |
| Templates | Markdown templates with mtime staleness, tags, and a manual-pin toggle |
| Specs | Feature specs in specs/ with phase + status badges. + New spec bootstraps from TEMPLATE.md; + Design / + Tasks inline; status dropdown in Preview |
| Summaries | Inline-editable summaries.json with overwrite warning |
| Living Docs | Workspace editor, scan trigger, document health, review queue, RAG quality bars |
| Commands | Run any registered command from a card grid (RAG queries, regen, maintain, …) |
| Jobs | Job history with per-feature progress, last-output column, Cancel button, auto-refresh |
Click any spec or template to open the Preview / Edit panel — server-side
Markdown rendering plus a raw <textarea> for editing.
Template editor (/editor)
A first-class CodeMirror 6 editor for attune-help-style markdown templates.
Triggered by attune-author edit <path> or by navigating to
/editor?corpus=<id>&path=<rel>. Reads from / writes back to any registered
corpus on disk, with all retrieval/lint/refactor smarts coming from
attune-rag's editor toolkit.
Developer features:
| Feature | What it does |
|---|---|
| Schema-driven frontmatter form | Reads attune_rag.editor.load_schema() at request time and renders typed inputs (select / chip-input / textarea / text). Unknown frontmatter keys are preserved verbatim. Raw-YAML toggle round-trips byte-for-byte. |
| CodeMirror 6 + Lezer extension | Composes the standard @codemirror/lang-markdown with a custom Lezer extension for YAML frontmatter delimiters, ## Depth N markers, and [[alias]] refs (excluding fenced code, supporting \[[ escape). |
| Server-side lint | 300 ms debounced POST to /api/corpus/<id>/lint. Diagnostics paint as squiggles in the editor + a clickable strip at the bottom. Local fast-path skips the round-trip for YAML parse errors. |
| Tag + alias autocomplete | Context-aware completions inside tags: / aliases: frontmatter fields and [[…]] body refs. Per-prefix LRU cache invalidates on WS file-change events. |
| Per-hunk save modal | /api/corpus/<id>/template/diff returns stable hunk ids; the modal shows each hunk with a checkbox and runs a projected-state lint so a partial save can't write known-broken frontmatter. Atomic save via /template/save (409 on base_hash mismatch → conflict mode). |
| 3-way merge conflict mode | A WebSocket at /ws/corpus/<id>?path=<rel> pushes file_changed events from watchfiles. On conflict (or 409), a banner offers Reload / Keep / Resolve. Resolve uses node-diff3 for per-region accept-disk / accept-editor / keep-both. |
| Cross-corpus rename refactor | Right-click any tag/alias chip → "Rename …". /api/corpus/<id>/refactor/rename/preview returns a multi-file diff; apply is atomic across files (per-file tempfile + sequential rename + drift-detection rollback). 409 with owning_path on alias collisions. |
| Corpus switcher | Top-bar dropdown lists registered corpora (~/.attune/corpora.json). Search input materializes above 10 corpora. "+ Add corpus…" registers a new root via /api/corpus/register. Switching with unsaved edits prompts Save / Discard / Cancel. |
| Generated-corpus advisory | Persistent, non-dismissible banner when the active corpus has kind: "generated" — flags that edits will be overwritten on the next attune-author maintain. |
| Read-only on duplicate session | A second tab opening the same (corpus, path) receives a duplicate_session WS message and goes read-only with a banner — first tab keeps full control. |
| Keyboard shortcuts | ⌘/Ctrl-S opens the save modal. beforeunload warns on unsaved edits. |
| Pre-bundled, no Node at install | editor-frontend/ is Vite + TypeScript; make build-editor produces a hashed-filename bundle into sidecar/attune_gui/static/editor/ that's checked into the repo. PyPI consumers don't need npm. |
Editor frontend dev loop
# In one shell — sidecar with auto-reload:
uv run attune-gui --port 8765 --reload
# In another — vitest watch (92 unit tests, ~2s):
cd editor-frontend && npm run test --watch
# Rebuild the bundle (deterministic; output committed):
make build-editor
# Run the Playwright e2e suite against a fresh sidecar
# (auto-spawned by playwright.config.ts):
cd editor-frontend && npm run e2e
The editor bundle is ~210 KB gzipped (budget: 600 KB). Schema and Lezer
grammar parse fixtures live in editor-frontend/src/grammar/; merge
correctness in three-way-merge.test.ts. The Playwright suite under
editor-frontend/e2e/ exercises the end-to-end flows (open→edit→save,
conflict resolve via WS-pushed file_changed, rename refactor
preview+apply, corpus switcher with unsaved-edits guard, save-modal
controls, keyboard shortcuts + advisories) — it spins up a real
sidecar with an isolated ATTUNE_CORPORA_REGISTRY so your dev
registry isn't touched.
Endpoint summary
| Method | Path | Purpose |
|---|---|---|
| GET | /editor?corpus=<id>&path=<rel> |
Editor shell (Jinja) |
| GET | /api/editor/template-schema |
Frontmatter JSON Schema |
| GET | /api/corpus |
List registered corpora + active id |
| POST | /api/corpus/active |
Switch active corpus |
| POST | /api/corpus/register |
Add a new corpus root |
| POST | /api/corpus/resolve |
Map an absolute path → (corpus_id, rel_path) |
| GET | /api/corpus/<id>/template?path=<rel> |
Read template + base hash + mtime |
| POST | /api/corpus/<id>/template/diff |
Compute unified-diff hunks vs disk |
| POST | /api/corpus/<id>/template/save |
Atomic save (base_hash 409-guarded) |
| POST | /api/corpus/<id>/lint |
Lint a template body |
| GET | /api/corpus/<id>/autocomplete?kind=tag|alias&prefix=… |
Autocomplete |
| POST | /api/corpus/<id>/refactor/rename/preview |
Multi-file rename plan |
| POST | /api/corpus/<id>/refactor/rename/apply |
Atomic rename across files |
| WS | /ws/corpus/<id>?path=<rel> |
file_changed + duplicate_session push |
Looking for AI dev workflows (code review, security audits, refactor planning, multi-agent orchestration)? Those live in
attune-ai— a separate product. attune-gui is deliberately scoped to the documentation lifecycle.
Quickstart
pip install attune-gui
attune-gui
# Or pick a specific port:
attune-gui --port 8765
The sidecar binds to 127.0.0.1, prints SIDECAR_URL=…, and serves the
new dashboard at /. Use --open to auto-open your browser.
Configuration
.env auto-loading
The sidecar loads KEY=value lines from the first .env it finds, in this order:
./.env(current working directory)<repo-root>/.env(the attune-gui checkout root)~/.attune-gui/.env~/.attune/.env
Existing real env values are preserved; empty/whitespace-only values are treated as unset and overwritten. Common keys:
ANTHROPIC_API_KEY=sk-ant-… # required for author.regen / author.maintain
ATTUNE_SPECS_ROOT=/path/to/your/repo/specs
ATTUNE_WORKSPACE=/path/to/your/project
~/.attune-gui/config.json
A single typed config file holds the keys below. Each key has a matching environment variable that overrides the file at runtime — useful for CI or one-off runs. Precedence: env > file > default.
| Key | Env var | Purpose |
|---|---|---|
workspace |
ATTUNE_WORKSPACE |
Project the sidecar watches (Living Docs, templates) |
corpora_registry |
ATTUNE_CORPORA_REGISTRY |
Path to corpora registry (default: ~/.attune/corpora.json) |
specs_root |
ATTUNE_SPECS_ROOT |
Where the Specs page reads from (default: <workspace>/specs/, then walks up from cwd) |
Manage the file from the CLI — no hand-editing JSON:
attune-gui config list # show resolved values + source
attune-gui config get workspace # print one value
attune-gui config set workspace /path/to/project # persist a value
attune-gui config unset specs_root # remove a key
config set workspace validates that the path is a real directory; the
others trust you. Workspace can also be set via Living Docs →
Workspace in the UI — both surfaces write to the same file.
Development
git clone https://github.com/Smart-AI-Memory/attune-gui
cd attune-gui
uv sync
uv run attune-gui --port 8765 --reload
Templates auto-reload — edit anything under sidecar/attune_gui/templates/
and refresh the browser. Python code changes reload automatically with
--reload.
Tests
uv run pytest # 124 tests, ~2s
uv run ruff check . # lint
Architecture
┌──────────────────────────────────────┐
│ Cowork dashboard (Jinja2) / │
└──────────────────┬───────────────────┘
│ /api/*
┌──────────────────▼───────────────────┐
│ FastAPI sidecar — 127.0.0.1 │
│ ├─ routes/system, rag, help, … │
│ ├─ routes/cowork_health │
│ ├─ routes/cowork_specs │
│ ├─ routes/cowork_templates │
│ ├─ routes/cowork_files │
│ └─ routes/cowork_pages (HTML) │
└──────────────────┬───────────────────┘
│
┌──────────────────▼───────────────────┐
│ attune-rag · attune-help │
│ attune-author[ai] │
└──────────────────────────────────────┘
Frontend boundary
Two rendering surfaces, one rule:
- Template editor at
/editoris a Vite + TypeScript SPA (CodeMirror 6, WebSocket conflict mode, per-hunk diff). Source ineditor-frontend/, pre-bundled intosidecar/attune_gui/static/editor/and committed so PyPI consumers do not need Node. - Everything else (Health, Templates, Specs, Summaries, Living Docs, Commands, Jobs) is server-rendered Jinja2 with light vanilla-JS for inline actions and polling.
New UI defaults to Jinja. Reach for the SPA only when the surface needs editor-grade interactivity — rich text editing, real-time conflict resolution, multi-file refactor previews. Inline actions, polling, and form-driven dashboards stay server-rendered.
Security notes
This is a single-user, local-only app. Not designed for multi-user deployment, not hardened against a motivated attacker on the same machine.
- Binds only to
127.0.0.1— not reachable from other machines - An
Originheader guard rejects browser requests from non-localhost origins - Mutating endpoints require the
X-Attune-Clientheader to match a per-process token (served from/api/session/token) - File API enforces a path-traversal guard against three named roots
(
templates,specs,summaries); writes outside those roots return 400
Related packages
attune-rag— RAG pipelineattune-help— help runtimeattune-author— doc authoringattune-gui-plugin— Claude Code plugin that launches the dashboard inside Cowork's preview paneattune-ai— separate AI dev workflow product (not used by attune-gui)
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file attune_gui-0.7.0.tar.gz.
File metadata
- Download URL: attune_gui-0.7.0.tar.gz
- Upload date:
- Size: 407.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5642f3db3d0b2389114ebc85996085001041a3c122fda1e2a7d55baece8f7585
|
|
| MD5 |
c32c8212bb0766ee518306c4a543bfc1
|
|
| BLAKE2b-256 |
3f0e262e32656ee5be70f68f365e6e4849a1560f69c8028da4f9470a1ed4437f
|
Provenance
The following attestation bundles were made for attune_gui-0.7.0.tar.gz:
Publisher:
publish.yml on Smart-AI-Memory/attune-gui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attune_gui-0.7.0.tar.gz -
Subject digest:
5642f3db3d0b2389114ebc85996085001041a3c122fda1e2a7d55baece8f7585 - Sigstore transparency entry: 1469509820
- Sigstore integration time:
-
Permalink:
Smart-AI-Memory/attune-gui@196bc7d42714669109e0879053ac2685e746ea81 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Smart-AI-Memory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@196bc7d42714669109e0879053ac2685e746ea81 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file attune_gui-0.7.0-py3-none-any.whl.
File metadata
- Download URL: attune_gui-0.7.0-py3-none-any.whl
- Upload date:
- Size: 331.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
344228b611d815b0d51ba61ae206f778af9750702938e6b3733514c8bd4ca895
|
|
| MD5 |
2555f2d50472092ff5cd007843cd3f37
|
|
| BLAKE2b-256 |
5ce388f11073dcfb0b27ab08051472ec844bd9566154d6e722e0dec1c7bf0a20
|
Provenance
The following attestation bundles were made for attune_gui-0.7.0-py3-none-any.whl:
Publisher:
publish.yml on Smart-AI-Memory/attune-gui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attune_gui-0.7.0-py3-none-any.whl -
Subject digest:
344228b611d815b0d51ba61ae206f778af9750702938e6b3733514c8bd4ca895 - Sigstore transparency entry: 1469510064
- Sigstore integration time:
-
Permalink:
Smart-AI-Memory/attune-gui@196bc7d42714669109e0879053ac2685e746ea81 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Smart-AI-Memory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@196bc7d42714669109e0879053ac2685e746ea81 -
Trigger Event:
workflow_dispatch
-
Statement type: