tandem-editor 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -8
- package/README.md +46 -52
- package/dist/channel/index.js +201 -95
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +210 -96
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/{CoworkSettings-CXODT6KV.js → CoworkSettings-BlGNryD3.js} +1 -1
- package/dist/client/assets/index-Bnc4LNBi.css +1 -0
- package/dist/client/assets/index-D0gSEOxm.js +228 -0
- package/dist/client/index.html +2 -2
- package/dist/monitor/index.js +4 -4
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +644 -306
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -2
- package/sample/table-test.md +8 -0
- package/skills/tandem/SKILL.md +11 -11
- package/dist/client/assets/index-C6rbXHNq.css +0 -1
- package/dist/client/assets/index-q_8NVSM3.js +0 -228
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## \[Unreleased]
|
|
9
9
|
|
|
10
|
+
## \[0.9.1] - 2026-05-01
|
|
11
|
+
|
|
12
|
+
Hotfix patch bundling ADR-027 surface cleanup and file-I/O correctness fixes before the v0.10.0 Svelte conversion. All changes are patch-class; no MCP API changes.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Imported Word reviewer comments now surface to Claude by default (#482)** — `.docx` reviewer comments are imported as `author: "import"`, `type: "comment"` (was `type: "note"` in the unreleased PR #474 plan). Reverts the `tandem_getAnnotations` `includeImports` opt-in introduced in PR #474 — Claude can read imported comments alongside its own without an explicit flag, which matches the .docx review workflow. The opt-in plumbing (`includeImports` parameter, `importsExcluded` response field) is removed. Existing on-disk records with `author: "import", type: "note"` migrate transparently on read via `sanitizeAnnotation`; on next import the durable record is rewritten in place. Safe because PR #474 was never tagged in a release.
|
|
17
|
+
- **Markdown tables preserved across Tiptap round-trip (#379)** — bidirectional MDAST↔Y.Doc table conversion added to `mdast-ydoc.ts`. Tables with mixed column alignment, inline marks in cells, and empty cells all survive load/save cycles. Flat-offset alignment preserved so annotations anchored after a table resolve correctly.
|
|
18
|
+
- **HTML blocks and insertion-order fixed in mdast-ydoc (#496)** — raw HTML blocks (`<div>`, `<details>`, etc.) now round-trip as `html` nodes instead of being dropped. Insertion-order bug in `blockToYxml` fixed — the two-pass Y.XmlText attach-before-populate pattern now applied uniformly to all block types.
|
|
19
|
+
- **Channel shim per-request timeouts (#364)** — event bridge and `run.ts` now use bounded request-response fetches with split SSE handshake/body watchdogs and a 1 MB SSE frame buffer cap. `tandem_reply` returns a structured timeout error instead of hanging indefinitely.
|
|
20
|
+
- **Sanitize coercions routed to migration-log (#483)** — lossy ADR-027 type coercions in `sanitize.ts` (e.g. `flag` → `note`) now emit a `migration-log.ts` entry (once per doc/kind) instead of silently rewriting records, restoring the forensic trail for ADR-027 transitions.
|
|
21
|
+
- **Doc hash required for collection logs (#495)** — annotation collection log entries now require a `docHash` field, preventing cross-document log pollution from unkeyed writes.
|
|
22
|
+
- **Standalone monitor gated on backend readiness (#491)** — `dev:standalone` waits for the backend health endpoint before starting the monitor, eliminating the startup race that caused spurious connection errors.
|
|
23
|
+
|
|
24
|
+
### Tests
|
|
25
|
+
|
|
26
|
+
- **E2E toolbar regression guard (#484)** — Playwright coverage for the redesigned toolbar (ADR-027 note/comment/highlight flow), including a regression guard for the note button empty-annotation bug (#480).
|
|
27
|
+
|
|
28
|
+
## \[0.9.0] - 2026-04-28
|
|
29
|
+
|
|
30
|
+
### Breaking Changes (MCP)
|
|
31
|
+
|
|
32
|
+
This is the last breaking-change window before semver lock. MCP tool count: 31 → 28.
|
|
33
|
+
|
|
34
|
+
- **`tandem_suggest` deprecated (#259)** — returns a structured error stub pointing to `tandem_comment` with `suggestedText`. Hard-remove in v0.10.0.
|
|
35
|
+
- **`tandem_getContent` removed (#259)** — superseded by `tandem_getTextContent`.
|
|
36
|
+
- **`tandem_getSelections` removed (#259)** — superseded by `tandem_checkInbox`.
|
|
37
|
+
- **`tandem_setStatus` merged into `tandem_status` (#259)** — `tandem_status` now accepts optional write params (`text`, `focusParagraph`, `focusOffset`). When params are present it writes to awareness; when absent it reads.
|
|
38
|
+
- **`tandem_flag` deprecated (ADR-027, #473)** — returns a `DEPRECATED` error stub. Use `tandem_comment` instead. Hard-remove in v0.10.0.
|
|
39
|
+
- **`tandem_highlight` deprecated (ADR-027, #473)** — returns a `DEPRECATED` error stub. Highlights are user-only. Hard-remove in v0.10.0.
|
|
40
|
+
- **Annotation `directedAt` field removed (ADR-027, #473)** — silently ignored on input; stripped from on-disk records via `sanitizeAnnotation` and the `normalizeAnnotation` fast path on read.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- **`/api/info` endpoint (#441)** — returns app version, MCP SDK version, tool count, data directory, and platform. Serves the Settings panel About footer.
|
|
45
|
+
- **Tabbed-left layout variant (#445)** — new `"tabbed-left"` layout mode places the side panel on the left and editor on the right. Three layout modes total: `tabbed`, `tabbed-left`, `three-panel`.
|
|
46
|
+
- **App version in Settings (#435)** — `useAppInfo` hook fetches `/api/info` and displays version + MCP SDK version in the Settings popover footer.
|
|
47
|
+
- **View Changelog button (#437)** — Settings panel button opens `CHANGELOG.md` as a read-only document tab via `POST /api/open` with `readOnly: true`.
|
|
48
|
+
- **Authorship `data-tandem-author` attributes (#443)** — authorship decorations switched from CSS classes (`.tandem-authorship--user`) to data attributes (`[data-tandem-author="user"]`), per ADR-026. Enables future attribute-based styling without class proliferation.
|
|
49
|
+
- **Schema foundations (#440, #442, #444, #450)** — `heldInSolo` field on `AnnotationBase`; 7 new `TandemSettings` fields (`accentHue`, `editorFont`, `density`, `defaultMode`, `highContrast`, `annotationPatterns`, `selectionToolbar`); `showAuthorship` default flipped to `true`; editor width minimum lowered from 50% to 40%.
|
|
50
|
+
- **Highlight palette migration (#450)** — palette switched from 5 colors to 4 (yellow/green/blue/pink). `LEGACY_COLOR_MAP` migrates `red` → `yellow`, `purple` → `blue` on annotation load.
|
|
51
|
+
- **CI stdio smoke test (#341)** — GitHub Actions step validates the Cowork stdio bridge (`scripts/ci/stdio-smoke.mjs`) on every push.
|
|
52
|
+
- **`__MCP_SDK_VERSION__` build-time injection** — tsup reads the real SDK version from the package root (not the CJS type marker) and injects it at build time.
|
|
53
|
+
- **`tandem_getAnnotations` `includeImports` opt-in (ADR-027, #473)** — accepts `includeImports: true` to surface `author: "import"` reviewer comments imported from `.docx` files. Default still excludes them so the user triages first. When imports are filtered out, the response includes `importsExcluded: N` so Claude can prompt the user to opt in.
|
|
54
|
+
- **Deprecated-tool user notifications (ADR-027, #473)** — `tandem_highlight`, `tandem_flag`, and `tandem_suggest` stubs now `pushNotification` a warning toast in addition to returning the `DEPRECATED` mcpError, so the user sees what Claude tried.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **Cross-element edit merging (#456)** — canonical Y.js length + delta-walking merge for edits spanning multiple inline elements.
|
|
59
|
+
- **Annotation decoration initial-sync race** — Y.Map observers firing before y-prosemirror sync no longer produce empty decoration sets.
|
|
60
|
+
- **Channel checkpoint timing** — checkpoint advances after MCP notification delivery, not before, preventing event loss on reconnect.
|
|
61
|
+
- **Auto-save spurious tab switches** — auto-save no longer triggers `document:switched` events that confuse tab state.
|
|
62
|
+
- **Annotation recovery guard hardening** — narrowed guard scope for edge cases in session restore.
|
|
63
|
+
- **Event-bridge error handling** — uncaught errors in SSE delivery no longer crash the event loop.
|
|
64
|
+
- **MCP SDK version resolution** — `require("@modelcontextprotocol/sdk/package.json")` resolves to `dist/cjs/package.json` (a CJS type marker without `version`); build now walks back past `dist/` to find the real version.
|
|
65
|
+
- **Silent-migration logging (ADR-027, #473)** — `parseAnnotationDoc`, `migrateToV1`, and the `directedAt` strip fast path now log via the new `migration-log.ts` module (once per `${docHash}:${kind}`) instead of silently rewriting v0 records. Restores forensic trail for the v0→v1 transition.
|
|
66
|
+
- **`normalizeReply` validation (#473)** — replies are now Zod-validated before being merged; malformed entries are dropped + logged instead of poisoning the envelope.
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- **Redesign gap audit resolved (#439)** — 7 product decisions documented in ADR-026. Design response prompt at `docs/claude-design-response-prompt.md`.
|
|
71
|
+
- **Distribution items deferred** — #316 (macOS/Linux Cowork auto-setup), #317 (cross-platform firewall scoping), #322 (network-type detection) moved to v0.13.0. Requires macOS/Linux validation hardware.
|
|
72
|
+
- **Annotation type model unified to audience-based (ADR-027, #473)** — `flag` → `note`. Three types now: `highlight` (visual marker), `note` (private), `comment` (sent to Claude). Channel observer filters notes from SSE — they never reach Claude. `checkInbox` returns only `comment` annotations.
|
|
73
|
+
- **Note toolbar UX (#480)** — Note button now opens an inline input mirroring the Comment flow.
|
|
74
|
+
- **Note card visual distinction (#481)** — amber border + warning-bg tint distinguishes notes from comments in the side panel.
|
|
75
|
+
|
|
76
|
+
### Internal
|
|
77
|
+
|
|
78
|
+
- Annotation schema Zod validation with `LEGACY_COLOR_MAP` migration path.
|
|
79
|
+
- `ResizeHandle` and `TabbedPanelContainer` shared components extracted for layout code reuse.
|
|
80
|
+
- `useAppInfo` hook with exponential backoff retry for `/api/info` fetch.
|
|
81
|
+
- `file-opener` read-only mode support for changelog viewing.
|
|
82
|
+
- 900+ lines of new test coverage: authorship decorations, annotation decorations, panel layout, app info hook, settings fields, schema migration, info route, document edit edge cases.
|
|
83
|
+
|
|
10
84
|
## \[0.8.0] - 2026-04-26
|
|
11
85
|
|
|
12
86
|
### Added
|
|
13
87
|
|
|
88
|
+
- **NSIS pre-install sidecar kill (#434)** — the NSIS installer now kills the running `node-sidecar.exe` process before file replacement, preventing "Error opening file for writing" failures during upgrade installs. Uses `nsis_tauri_utils::KillProcessCurrentUser` for user-scoped process termination. Tauri's built-in `CheckIfAppIsRunning` already handles the main binary.
|
|
89
|
+
|
|
14
90
|
- **Semantic token lint enforcement (#356)** — `npm run check:tokens` scans `src/client/` for raw hex and non-neutral `rgba()` violations. Runs on pre-commit via lint-staged, blocking merges that introduce unsanctioned color literals.
|
|
15
91
|
- **`--tandem-suggestion-*` token family (#340)** — violet semantic tokens for replacement/suggestion annotations (`--tandem-suggestion`, `-fg-strong`, `-bg`, `-border`), visually distinct from the indigo accent family.
|
|
16
92
|
- **Annotation drop count surfacing (#351)** — `normalizeAnnotation` now returns drop counts in snapshot metadata so callers can detect lossy session migrations.
|
|
@@ -84,14 +160,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
84
160
|
|
|
85
161
|
- **Annotation GC race on startup (#334)** — `cleanupOrphanedAnnotationFiles` previously ran as a `.then()` chain during boot, racing the boot-path doc opens. On upgrade paths where `sample/welcome.md` or `CHANGELOG.md` hadn't been opened in 30+ days, the GC could unlink the annotation file between read intent and the actual read, silently returning an empty doc. Now `await`-ed before all boot-path opens.
|
|
86
162
|
- **Settings Popover extends out of view (#306)** — centered the popover in the viewport with `transform: translate(-50%, -50%)` and added `maxHeight: calc(100vh - 32px)` + `overflowY: auto` so it is always fully visible and internally scrollable on short screens.
|
|
87
|
-
- **Dark-mode
|
|
163
|
+
- **Dark-mode `*-bg` tokens inconsistent (#307)** — `--tandem-success-bg` and `--tandem-warning-bg` in dark mode were hand-coded hex while `--tandem-error-bg` used `color-mix`. All three now use `color-mix(in srgb, var(--tandem-<semantic>) 15%, var(--tandem-surface))` for consistency with light-mode behavior.
|
|
88
164
|
- **stdio bridge silent-failure paths (#336 partial)** — three paths in `src/cli/mcp-stdio.ts` (preflight exit, `http.onclose`, `http.start()` TOCTOU) previously closed stdio without writing a JSON-RPC error, producing "tools never appear in Cowork" with no diagnostics. All three now synthesize `-32000` for any in-flight request ID before exit. Remaining #336 items (channel-shim tests, Windows npx smoke, nits) carry to v0.7.0.
|
|
89
165
|
|
|
90
166
|
### Changed
|
|
91
167
|
|
|
92
168
|
- **Annotation module internals** — extracted `mergeMap<T>` helper (#324), promoted `UPLOAD_PREFIX` to shared constants (#327), centralized app-data dir resolution in `platform.ts` (#328), extracted `ReplyAuthorSchema`, trimmed module headers, and dropped unused `docContexts` map (#332). No user-facing behavior changes.
|
|
93
|
-
- **Annotation serialization upgrades legacy
|
|
94
|
-
-
|
|
169
|
+
- **Annotation serialization upgrades legacy `type` values on write (#329)** — records with non-canonical `type` (`"suggestion"` / `"question"` / anything outside `highlight` / `comment` / `flag`) are now routed through `sanitizeAnnotation` during snapshot serialization, which rewrites them to `"comment"`. **One-way lossy migration:** users with legacy-type annotations will see `type` flip to `"comment"` on the next durable write for that document — the original distinction between `suggestion` and `question` is not recoverable.
|
|
170
|
+
- **`migrateToV1` reports drop counts (#330)** — `migrateToV1(raw)` now returns `{ doc, droppedAnnotations, droppedReplies }` so future production callers can surface lossy upgrades to users rather than silently discarding malformed records. No production caller exists yet; a follow-up will wire drop counts to `npm run doctor` or a toast when the first caller lands.
|
|
95
171
|
|
|
96
172
|
### Internal
|
|
97
173
|
|
|
@@ -136,9 +212,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
136
212
|
|
|
137
213
|
### Fixed
|
|
138
214
|
|
|
139
|
-
- **Monitor preserves last-known
|
|
215
|
+
- **Monitor preserves last-known `documentId`** — doc-less events (e.g. `chat:message`) no longer blank out the tracked document, so the shutdown awareness clear always targets a valid document.
|
|
140
216
|
- **Monitor exits 1 on shutdown awareness failure** — if the final `clearAwareness` POST fails during SIGINT/SIGTERM, the monitor exits with a non-zero status rather than silently succeeding.
|
|
141
|
-
-
|
|
217
|
+
- **`/api/setup` returns accurate status codes** — 207 on partial failure (some targets configured, some failed) and 500 on total failure, instead of always returning 200.
|
|
142
218
|
- **Checkpoint after stdout write** — `lastEventId` is only advanced after `process.stdout.write` returns, so EPIPE on a closed pipe no longer silently skips an event on reconnect.
|
|
143
219
|
- **Async EPIPE surfaces as exit(1)** — `process.stdout.on('error')` listener now catches asynchronous EPIPE (plugin host closes pipe mid-stream); monitor exits 1 instead of silently advancing `lastEventId` past lost events.
|
|
144
220
|
- **Defensive exit on monitor fallthrough** — the retry loop exits 1 if it ever terminates without hitting the explicit exhaustion path.
|
|
@@ -148,7 +224,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
148
224
|
### Added
|
|
149
225
|
|
|
150
226
|
- **Claude Code plugin support** — monitor-based event push (`src/monitor/index.ts`) gives real-time notifications without polling or the channel shim. Install via `claude plugin marketplace add bloknayrb/tandem`.
|
|
151
|
-
-
|
|
227
|
+
- **`--with-channel-shim` opt-in** — `tandem setup --with-channel-shim` writes the legacy `tandem-channel` MCP entry for setups that can't install the plugin.
|
|
152
228
|
|
|
153
229
|
### Changed
|
|
154
230
|
|
|
@@ -162,7 +238,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
162
238
|
- **Exponential backoff on reconnect** — monitor reconnects use 2s/4s/8s/16s/30s backoff instead of a fixed 2s delay.
|
|
163
239
|
- **SIGINT/SIGTERM clears awareness** — monitor posts a final `clearAwareness` before exit so the "Claude is active" indicator doesn't hang in the browser.
|
|
164
240
|
- **Per-route fetch timeouts** — `AbortSignal.timeout` enforces budgets per route (connect 10s, mode 2s, awareness 5s, error report 3s) to prevent hung SSE connects or mode lookups from stalling the monitor.
|
|
165
|
-
- **SSE parse errors don't advance
|
|
241
|
+
- **SSE parse errors don't advance `lastEventId`** — JSON parse failures and schema validation errors are logged with event ID + frame tail but do not advance `lastEventId`, so bad events are re-delivered on reconnect rather than silently dropped.
|
|
166
242
|
- **SKILL.md corrected** — `question` annotation guidance now uses `type === 'comment' && directedAt === 'claude' && author === 'user'`; all 5 highlight colors listed (yellow, red, green, blue, purple).
|
|
167
243
|
|
|
168
244
|
## \[0.5.0] - 2026-04-13
|
|
@@ -229,7 +305,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
229
305
|
- **Annotation type unification:** Three semantically identical types (`comment`, `suggestion`, `question`) collapsed into a single `comment` type with optional `suggestedText` and `directedAt` fields. `AnnotationTypeSchema` reduced from 5 values to 3: `highlight`, `comment`, `flag`. (#193, #245, #255)
|
|
230
306
|
- **Toolbar:** Three annotation buttons (Comment, Suggest, Ask Claude) replaced with a single Comment button with "Replace" and "@Claude" toggles (#193)
|
|
231
307
|
- **Side panel filters:** "Suggestions" → "With replacement", "Questions" → "For Claude" (#193)
|
|
232
|
-
-
|
|
308
|
+
- **`sanitizeAnnotation()` moved to `src/shared/sanitize.ts`** — now available to both server and client code. Client-side Y.Map reads are sanitized to handle legacy session data. (#255)
|
|
233
309
|
|
|
234
310
|
### Added
|
|
235
311
|
|
package/README.md
CHANGED
|
@@ -6,8 +6,6 @@ Have you ever been working on a piece of writing with an LLM and caught yourself
|
|
|
6
6
|
|
|
7
7
|
And because Tandem hooks into Claude as an MCP server, you're not stuck in some stripped-down document-editing silo. It's the full Claude — with all its knowledge, your conversation context, and every tool it has access to — just now it can also see and edit your document.
|
|
8
8
|
|
|
9
|
-

|
|
10
|
-
|
|
11
9
|
## Why Tandem?
|
|
12
10
|
|
|
13
11
|
- **No more copy-paste ping-pong.** Select text in the editor, and Claude reads your selection directly. Ask "what do you think of this?" or "make this more concise" — Claude knows exactly which text you mean.
|
|
@@ -101,7 +99,7 @@ claude
|
|
|
101
99
|
Then pick one of two ways to keep the conversation flowing:
|
|
102
100
|
|
|
103
101
|
1. **Just chat in the terminal (simplest).** Every time you send Claude a message, it has a chance to call `tandem_checkInbox` and pick up your latest selection, any annotations you accepted or dismissed, and any chat messages from the Tandem sidebar. Zero setup — this is how it works out of the box. With Tandem and the terminal snapped side by side, the loop feels surprisingly natural; Claude just reacts when you nudge it rather than spontaneously.
|
|
104
|
-
2. **Background polling with
|
|
102
|
+
2. **Background polling with ****/loop**** (hands-off).** Ask Claude to check in on its own using the `/loop` skill:
|
|
105
103
|
```
|
|
106
104
|
/loop 30s check tandem inbox and respond to any new messages
|
|
107
105
|
```
|
|
@@ -131,10 +129,10 @@ curl http://localhost:3479/health
|
|
|
131
129
|
git clone https://github.com/bloknayrb/tandem.git
|
|
132
130
|
cd tandem
|
|
133
131
|
npm install
|
|
134
|
-
npm run dev:standalone # starts
|
|
132
|
+
npm run dev:standalone # starts backend (:3478/:3479), editor client (:5173), and monitor
|
|
135
133
|
```
|
|
136
134
|
|
|
137
|
-
Open http://localhost:5173 — you'll see `sample/welcome.md` loaded automatically on first run. The `.mcp.json` in the repo configures Claude Code automatically when run from this directory.
|
|
135
|
+
Open <http://localhost:5173> — you'll see `sample/welcome.md` loaded automatically on first run. The `.mcp.json` in the repo configures Claude Code automatically when run from this directory.
|
|
138
136
|
|
|
139
137
|
</details>
|
|
140
138
|
|
|
@@ -154,20 +152,14 @@ Everything in Tandem is built around one idea: you work in the document, Claude
|
|
|
154
152
|
|
|
155
153
|
### Chat
|
|
156
154
|
|
|
157
|
-

|
|
158
|
-
|
|
159
155
|
Send messages to Claude alongside your document. Select text before sending to attach it as context — Claude sees exactly what you mean. Clicking an anchored selection later scrolls back to that passage.
|
|
160
156
|
|
|
161
157
|
### Annotations
|
|
162
158
|
|
|
163
|
-

|
|
164
|
-
|
|
165
159
|
This is how Claude's feedback shows up in the document. Claude adds highlights, comments, suggestions, and flags directly on the text. Suggestion cards show a visual diff — original text in red strikethrough, replacement in green. The side panel lists all annotations with filtering by type, author, and status. Accept, dismiss, or edit each one individually — or use bulk actions to process them in batches.
|
|
166
160
|
|
|
167
161
|
### Review Mode
|
|
168
162
|
|
|
169
|
-

|
|
170
|
-
|
|
171
163
|
Press **Ctrl+Shift+R** to enter keyboard review mode. Navigate with **Tab**, accept with **Y**, dismiss with **N**, examine with **E**. A 10-second undo window with a visual countdown lets you reverse accidental accepts. Shortcut hints appear below the Review button.
|
|
172
164
|
|
|
173
165
|
### More
|
|
@@ -193,7 +185,7 @@ Press **Ctrl+Shift+R** to enter keyboard review mode. Navigate with **Tab**, acc
|
|
|
193
185
|
|
|
194
186
|
## Where Tandem is headed
|
|
195
187
|
|
|
196
|
-
Since the v0.4.0 desktop app launch, Tandem has added auth tokens for LAN exposure (v0.7.0), a Claude Code plugin bridge for Cowork and Claude Desktop (v0.6.0+), durable annotation persistence, settings with Light/Dark/System theming,
|
|
188
|
+
Since the v0.4.0 desktop app launch, Tandem has added auth tokens for LAN exposure (v0.7.0), a Claude Code plugin bridge for Cowork and Claude Desktop (v0.6.0+), durable annotation persistence, settings with Light/Dark/System theming, authorship text coloring, and coordinate system correctness fixes with semantic token enforcement (v0.8.0). A few directions on the radar for later releases:
|
|
197
189
|
|
|
198
190
|
- **High-fidelity .docx round-trip** — current `.docx` support is review-only; production export is planned so you can stay in Tandem through the final draft.
|
|
199
191
|
- **Exportable annotated documents** — PDF (and eventually `.docx`) with annotations baked in, so you can share reviewed drafts outside Tandem.
|
|
@@ -205,30 +197,30 @@ See the full [Roadmap](docs/roadmap.md) and [Known Limitations](docs/roadmap.md#
|
|
|
205
197
|
## Documentation
|
|
206
198
|
|
|
207
199
|
- **[User Guide](docs/user-guide.md)** — How to use Tandem: editor UI, annotations, chat, review mode, keyboard shortcuts
|
|
208
|
-
- [MCP Tool Reference](docs/mcp-tools.md) —
|
|
200
|
+
- [MCP Tool Reference](docs/mcp-tools.md) — 28 MCP tools (25 active, 3 deprecated stubs) + channel API endpoints
|
|
209
201
|
- [Architecture](docs/architecture.md) — System design, data flows, coordinate systems, channel push
|
|
210
202
|
- [Workflows](docs/workflows.md) — Claude Code usage patterns: text iteration, cross-referencing, multi-model
|
|
211
203
|
- [Roadmap](docs/roadmap.md) — Phase 2+ roadmap, known issues, future extensions
|
|
212
204
|
- [Design Decisions](docs/decisions.md) — ADR-001 through ADR-024
|
|
213
|
-
- [Lessons Learned](docs/lessons-learned.md) —
|
|
205
|
+
- [Lessons Learned](docs/lessons-learned.md) — 48 implementation lessons
|
|
214
206
|
|
|
215
207
|
## CLI Commands
|
|
216
208
|
|
|
217
|
-
| Command
|
|
218
|
-
|
|
219
|
-
| `tandem`
|
|
220
|
-
| `tandem setup`
|
|
221
|
-
| `tandem setup --force`
|
|
222
|
-
| `tandem --version`
|
|
223
|
-
| `tandem --help`
|
|
224
|
-
| `tandem setup --with-channel-shim` | Also register the stdio channel shim
|
|
225
|
-
| `tandem rotate-token`
|
|
226
|
-
| `tandem mcp-stdio`
|
|
227
|
-
| `tandem channel`
|
|
209
|
+
| Command | What it does |
|
|
210
|
+
| ---------------------------------- | ------------------------------------------------------------------ |
|
|
211
|
+
| `tandem` | Start server and open editor (global install) |
|
|
212
|
+
| `tandem setup` | Register MCP tools with Claude Code / Claude Desktop |
|
|
213
|
+
| `tandem setup --force` | Register to default paths regardless of auto-detection |
|
|
214
|
+
| `tandem --version` | Show installed version |
|
|
215
|
+
| `tandem --help` | Show usage |
|
|
216
|
+
| `tandem setup --with-channel-shim` | Also register the stdio channel shim |
|
|
217
|
+
| `tandem rotate-token` | Rotate auth token (60-second grace window) |
|
|
218
|
+
| `tandem mcp-stdio` | Run as stdio MCP server (proxy to local HTTP, for plugin bridge) |
|
|
219
|
+
| `tandem channel` | Run the channel shim (stdio MCP for plugin's tandem-channel entry) |
|
|
228
220
|
|
|
229
221
|
## MCP Configuration
|
|
230
222
|
|
|
231
|
-
Tandem registers two MCP connections: **HTTP** for document tools (
|
|
223
|
+
Tandem registers two MCP connections: **HTTP** for document tools (28 tools including annotation editing — always on), and a **channel shim** for real-time push notifications. The channel shim is what enables the live-collaborator experience described in [Connect Claude Code](#connect-claude-code) and is recommended; it activates when you start Claude Code with `--dangerously-load-development-channels server:tandem-channel`. If you'd rather not pass that experimental flag, the entry sits idle and everything still works through polling on the HTTP connection — you just lose spontaneous reactions.
|
|
232
224
|
|
|
233
225
|
**Global install** (`tandem setup`): Automatically writes both entries to `~/.claude/mcp_settings.json` (Claude Code) and/or `claude_desktop_config.json` (Claude Desktop) with absolute paths. No manual configuration needed.
|
|
234
226
|
|
|
@@ -256,21 +248,21 @@ Both entries are cross-platform — no platform-specific configuration needed.
|
|
|
256
248
|
|
|
257
249
|
All optional — defaults work out of the box.
|
|
258
250
|
|
|
259
|
-
| Variable
|
|
260
|
-
|
|
261
|
-
| `TANDEM_PORT`
|
|
262
|
-
| `TANDEM_MCP_PORT`
|
|
263
|
-
| `TANDEM_URL`
|
|
264
|
-
| `TANDEM_TRANSPORT`
|
|
265
|
-
| `TANDEM_NO_SAMPLE`
|
|
266
|
-
| `TANDEM_CLAUDE_CMD`
|
|
267
|
-
| `TANDEM_BIND_HOST`
|
|
268
|
-
| `TANDEM_AUTH_TOKEN`
|
|
269
|
-
| `TANDEM_ALLOW_UNAUTHENTICATED_LAN` | unset
|
|
270
|
-
| `TANDEM_LAN_IP`
|
|
271
|
-
| `TANDEM_REQUEST_TIMEOUT_MS`
|
|
272
|
-
| `TANDEM_APP_DATA_DIR`
|
|
273
|
-
| `TANDEM_ANNOTATION_STORE`
|
|
251
|
+
| Variable | Default | Description |
|
|
252
|
+
| ---------------------------------- | ----------------------- | --------------------------------------------------------------- |
|
|
253
|
+
| `TANDEM_PORT` | `3478` | Hocuspocus WebSocket port |
|
|
254
|
+
| `TANDEM_MCP_PORT` | `3479` | MCP HTTP + REST API port |
|
|
255
|
+
| `TANDEM_URL` | `http://localhost:3479` | Channel shim server URL |
|
|
256
|
+
| `TANDEM_TRANSPORT` | `http` | Transport mode (`http` or `stdio`) |
|
|
257
|
+
| `TANDEM_NO_SAMPLE` | unset | Set to `1` to skip auto-opening `sample/welcome.md` |
|
|
258
|
+
| `TANDEM_CLAUDE_CMD` | `claude` | Claude Code executable name (for `tandem setup` auto-detection) |
|
|
259
|
+
| `TANDEM_BIND_HOST` | `127.0.0.1` | Bind address for MCP HTTP (`0.0.0.0` for LAN) |
|
|
260
|
+
| `TANDEM_AUTH_TOKEN` | auto-generated | Override auth token (set by Tauri; manual use rare) |
|
|
261
|
+
| `TANDEM_ALLOW_UNAUTHENTICATED_LAN` | unset | Set to `1` to skip token requirement on LAN bind |
|
|
262
|
+
| `TANDEM_LAN_IP` | auto-detected | Explicit LAN IP for multi-homed machines |
|
|
263
|
+
| `TANDEM_REQUEST_TIMEOUT_MS` | `30000` | Per-request timeout in stdio bridge (ms) |
|
|
264
|
+
| `TANDEM_APP_DATA_DIR` | platform default | Override app-data root (sessions, auth-token, annotations) |
|
|
265
|
+
| `TANDEM_ANNOTATION_STORE` | unset | Set to `off` to disable durable annotation persistence |
|
|
274
266
|
|
|
275
267
|
See `.env.example` for a copy-paste template.
|
|
276
268
|
|
|
@@ -287,6 +279,8 @@ Tandem kills stale processes on :3478/:3479 at startup. If another app uses thos
|
|
|
287
279
|
**Channel shim fails to start**
|
|
288
280
|
The `tandem-channel` entry spawns a subprocess. For global installs, `tandem setup` writes absolute paths to the bundled `dist/channel/index.js` — re-run `tandem setup` after upgrading. For dev setup, if you see `MODULE_NOT_FOUND` with a production config (`node dist/channel/index.js`), run `npm run build`. The default dev config uses `npx tsx` and doesn't require a build step.
|
|
289
281
|
|
|
282
|
+
If the shim starts but logs `/api/events timed out after 10000ms`, `SSE inactivity timeout`, or `/api/channel-reply timed out after 5000ms`, the Tandem server accepted a connection but stopped responding on that path. Restart Tandem; the shim reports the timeout instead of hanging silently.
|
|
283
|
+
|
|
290
284
|
**Editor shows "Cannot reach the Tandem server"**
|
|
291
285
|
The editor connects to the server via WebSocket. For global installs, run `tandem` to start the server. For dev setup, use `npm run dev:standalone` (or `npm run dev:server`). The message appears after 3 seconds of failed connection.
|
|
292
286
|
|
|
@@ -295,17 +289,17 @@ On first run, `sample/welcome.md` auto-opens. If you've cleared sessions or dele
|
|
|
295
289
|
|
|
296
290
|
## Development
|
|
297
291
|
|
|
298
|
-
| Command
|
|
299
|
-
|
|
300
|
-
| `npm run dev:standalone` | **Recommended** —
|
|
301
|
-
| `npm run dev:server`
|
|
302
|
-
| `npm run dev:client`
|
|
303
|
-
| `npm run build`
|
|
304
|
-
| `npm test`
|
|
305
|
-
| `npm run test:e2e`
|
|
306
|
-
| `npm run test:e2e:ui`
|
|
307
|
-
| `cargo tauri dev`
|
|
308
|
-
| `cargo tauri build`
|
|
292
|
+
| Command | What it does |
|
|
293
|
+
| ------------------------ | ---------------------------------------------------------------------------------- |
|
|
294
|
+
| `npm run dev:standalone` | **Recommended** — frontend + backend + monitor |
|
|
295
|
+
| `npm run dev:server` | Backend only: Hocuspocus (:3478) + MCP HTTP (:3479) |
|
|
296
|
+
| `npm run dev:client` | Frontend only: Vite dev server (:5173) |
|
|
297
|
+
| `npm run build` | Production build (`dist/server/` + `dist/channel/` + `dist/cli/` + `dist/client/`) |
|
|
298
|
+
| `npm test` | Run vitest (unit tests) |
|
|
299
|
+
| `npm run test:e2e` | Run Playwright E2E tests |
|
|
300
|
+
| `npm run test:e2e:ui` | Playwright UI mode |
|
|
301
|
+
| `cargo tauri dev` | Tauri desktop app (dev mode with hot-reload) |
|
|
302
|
+
| `cargo tauri build` | Tauri production build (installer output) |
|
|
309
303
|
|
|
310
304
|
**Tauri development** requires the [Rust toolchain](https://www.rust-lang.org/tools/install) and [Tauri CLI](https://v2.tauri.app/start/prerequisites/). Web-only development (`npm run dev:standalone`) does not require Rust.
|
|
311
305
|
|