litura-app 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vadim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Litura
2
+
3
+ Litura is a local, AI-assisted text editor for making prose sharper and less generic without taking control away from its author. The name comes from Latin: a correction, erasure, or visible revision in a manuscript.
4
+
5
+ ![Litura editor highlighting generic writing patterns](docs/screenshot.png)
6
+
7
+ ## What it does
8
+
9
+ - Keeps focus on working document in a distraction-free editor.
10
+ - Shows an AI score for texts; click it to see which words and phrases raised it.
11
+ - Reviews sentences as you finish them — switchable off in settings — and reviews the full document on demand.
12
+ - Highlights named, checkable prose and structure problems, including vague attribution, filler, buried points, broken paragraph promises, and abrupt topic shifts.
13
+ - Offers three replace-in-place alternatives when you ask for them — `Offer rewrites` on a finding card, or a message with a passage attached. Nothing changes until you choose an option. `Try again` asks for three more.
14
+ - Lets you dismiss a finding you disagree with, and jump between findings by clicking the counter in the header.
15
+ - Provides a compact chat for discussing the whole draft.
16
+ - Suggests short continuations below the current line; press `Tab` to accept or `Escape` to dismiss.
17
+
18
+ Right-click a selection to attach it to the composer, or press `Cmd/Ctrl+K` to attach the current selection or focus the chat. `Cmd/Ctrl+S` downloads the draft; dropping a text file on the editor opens it.
19
+
20
+ ## Data and model access
21
+
22
+ The draft, findings, conversation, and selected model are stored in browser local storage and survive a reload.
23
+
24
+ The draft is also mirrored to `draft.md` in the folder Litura was started from, so it survives cleared browser storage and opens in any editor. The browser copy stays authoritative while the app runs; if the file changes underneath it, Litura says so and offers to load it rather than merging or overwriting silently. Nothing leaves the machine except what a model request sends to the selected provider.
25
+
26
+
27
+ ## Run
28
+
29
+ Litura requires Node.js 20 or newer.
30
+
31
+ ```bash
32
+ npx litura-app
33
+ ```
34
+
35
+ Run it in the folder that holds your writing: Litura edits `draft.md` there and reads a `style.md` next to it if one exists. The browser opens on [http://127.0.0.1:3456](http://127.0.0.1:3456), or the next free port if that one is taken.
36
+
37
+ The npm package is `litura-app` — `litura` belongs to an unrelated project. The command it installs is `litura`.
38
+
39
+ To work on Litura itself:
40
+
41
+ ```bash
42
+ git clone https://github.com/vadimchirkov/litura.git
43
+ cd litura
44
+ npm install
45
+ npm start
46
+ ```
47
+
48
+ ## Updating
49
+
50
+ Running `npx litura-app` is the update: for a bare package name npm re-resolves the registry on every run, so each start picks up the latest release. The exceptions are a global install, which shadows that check and needs `npm i -g litura-app`, and a clone, which needs `git pull`.
51
+
52
+ ```bash
53
+ npx litura-app --check-update
54
+ ```
55
+
56
+ prints the published version next to the running one. Litura opens no connection on its own — the check happens only when you run it, and nothing else phones home.
57
+
58
+ An update leaves your draft, your `style.md`, and your model settings alone. Stored findings and chat history are dropped only when a release changes their format.
59
+
60
+ ## Configuring
61
+
62
+ Use the gear button to choose a provider, model, and reasoning level or to add an API key. Litura also discovers credentials already stored by Pi and supported provider environment variables.
63
+
64
+ Optional environment defaults:
65
+
66
+ ```bash
67
+ PI_PROVIDER=anthropic
68
+ PI_MODEL=claude-sonnet-4-6
69
+ PI_THINKING_LEVEL=medium
70
+ PORT=3456
71
+ STYLE_FILE=./style.md
72
+ DRAFT_FILE=./draft.md
73
+ ```
74
+
75
+ Edit `style.md` to describe the voice, facts, and constraints Litura should preserve. The file is read again for each model request, so changes apply without restarting the server.
76
+
77
+ ## Development
78
+
79
+ ```bash
80
+ npm run build
81
+ npm run check
82
+ ```
83
+
84
+ The server uses Node's native HTTP module. The browser UI is vanilla JavaScript with CodeMirror 6 and is bundled with esbuild. Source changes belong in `src/app.js`; `public/app.js` is generated.
85
+
86
+ See [SPEC.md](SPEC.md) for product behavior, API contracts, and implementation boundaries.
87
+
88
+ ## License
89
+
90
+ [MIT](LICENSE)
package/SPEC.md ADDED
@@ -0,0 +1,376 @@
1
+ # Litura - Product and Technical Specification
2
+
3
+ ## 1. Product
4
+
5
+ Litura is a standalone local writing editor. It helps an author find generic prose, discuss a draft, and try alternative wording while keeping every document change under explicit user control.
6
+
7
+ The product does not claim to determine whether text was written by AI. Its local score and model review identify named writing patterns only.
8
+
9
+ ### Product principles
10
+
11
+ - The document stays central; assistant UI floats above it instead of replacing it.
12
+ - Detection must name a concrete, checkable problem.
13
+ - The model may suggest text but never edits the document autonomously.
14
+ - Specific facts, unusual details, uncertainty, humor, and the author's voice should survive a rewrite.
15
+ - Empty output is better than a low-quality suggestion.
16
+ - Local analysis should avoid model calls when it can confidently do so.
17
+
18
+ ## 2. Interface
19
+
20
+ ```text
21
+ +---------------------------------------------------------------+
22
+ | Litura local score review model settings |
23
+ +---------------------------------------------------------------+
24
+ | |
25
+ | WORKING DOCUMENT |
26
+ | |
27
+ | CodeMirror editor |
28
+ | |
29
+ | highlighted review findings |
30
+ | |
31
+ | chat messages / finding / rewrite cards |
32
+ | +-----------------------------------------+ |
33
+ | | attached-passage chip | |
34
+ | | Ask anything... | |
35
+ | +-----------------------------------------+ |
36
+ +---------------------------------------------------------------+
37
+ ```
38
+
39
+ The current product has one working-document pane. There is no separate context pane, file browser, or document list.
40
+
41
+ ### Header
42
+
43
+ - `Litura` wordmark.
44
+ - Local style score when the document is predominantly Latin script.
45
+ - `Review` action. After a review it shows the number of active findings; clicking it then moves the selection to the next finding and scrolls it into view, cycling from the end back to the first. Shift-click runs a new full review.
46
+ - Selected model name.
47
+ - Settings button.
48
+
49
+ ### Editor
50
+
51
+ - Plain-text CodeMirror 6 editor with line wrapping and history.
52
+ - iA Writer Duo for document text; system UI font for controls.
53
+ - Warm paper canvas and header; chat blocks are white cards on it, without shadows.
54
+ - System light/dark theme.
55
+ - Draft saved to browser local storage after every edit.
56
+ - The editor reserves the measured height of the floating panel, so the caret,
57
+ the line being typed, and any continuation panel scroll above it instead of
58
+ disappearing behind the cards.
59
+
60
+ ### Composer
61
+
62
+ - Fixed near the bottom center of the window.
63
+ - Grows upward as messages, findings, and alternatives appear.
64
+ - A backdrop fades the draft out behind the panel so cards read as floating above the document rather than as part of it.
65
+ - A send button sits at the right of the field and is disabled while the field is empty.
66
+ - An attached passage is named by a small chip, not quoted: the passage itself stays highlighted in the draft.
67
+ - `Enter` sends; `Shift+Enter` inserts a newline.
68
+ - `Escape` cancels an active request or detaches the current selection.
69
+ - `Clear` removes the in-memory conversation and visible cards.
70
+
71
+ ## 3. Writing workflows
72
+
73
+ ### 3.1 Local style score
74
+
75
+ The browser computes a score from 0 to 100 using:
76
+
77
+ - known English AI-tell words and phrases;
78
+ - sentence-length variation;
79
+ - moving lexical diversity;
80
+ - repeated three-word sequences.
81
+
82
+ For passages shorter than 40 words or three sentences, only the lexical component is used. The score is hidden for predominantly non-Latin text because the current word lists are English-specific.
83
+
84
+ The score is a heuristic used for feedback and request filtering. It is never sent to the model and is not an authorship probability.
85
+
86
+ Clicking the score adds a card naming the matched tell words and phrases as they appear in the draft, plus a line for each structural axis that reads badly. A number the writer cannot trace back to their own text is only something to argue with.
87
+
88
+ ### 3.2 Automatic review
89
+
90
+ After 1.5 seconds of inactivity, the client considers completed sentences that:
91
+
92
+ - are at least 25 characters long;
93
+ - are not currently being edited;
94
+ - have not already been checked in the current session.
95
+
96
+ For Latin-script text, a sentence is sent only when its local score is at least 20. Non-Latin sentences bypass the English-only prefilter and are reviewed by the model.
97
+
98
+ The full document is included as context, but the server instructs the model to return findings only for the submitted target sentences. Automatic review runs one request at a time, and the `Review` control shows that a background check is in flight.
99
+
100
+ Automatic review can be switched off in settings, leaving the `Review` button as the only action that spends a model call. The setting is remembered.
101
+
102
+ ### 3.3 Full-document review
103
+
104
+ The `Review` button clears current findings and audits the entire non-empty document. It runs a global pass for levels 1–4 and a local pass for levels 5–7 plus generic prose in parallel. It merges contained duplicate quotes of the same code and suppresses a level-6 finding whose quote contains or is contained by a level-3 finding's quote, then returns at most eight findings with:
105
+
106
+ - `code`: internal diagnostic level (`level-1` through `level-7`, or `generic-prose`); not shown in the interface;
107
+ - `quote`: exact contiguous text from the draft;
108
+ - `pattern`: name of the writing problem;
109
+ - `reason`: why the quote is a strong example;
110
+ - `fix`: a short editing direction, not a rewrite.
111
+
112
+ The prompt caps these at four words for `pattern` and twelve each for `reason` and `fix`: the card is read at a glance beside the draft, and a paragraph of explanation there is not read at all.
113
+
114
+ Each returned quote is anchored to a non-overlapping occurrence in the current document. Findings appear as wavy underlines and move with edits outside their ranges. Editing inside a range removes its underline.
115
+
116
+ A finding card carries a dismiss control. Dismissing removes the underline, the card, any alternatives requested for it, and cancels an in-flight request, so the counter only reports findings the author has not rejected.
117
+
118
+ Structural findings use the same underlines and finding cards as prose findings. They cover
119
+ problems such as a broken opening promise, a buried point, an abrupt
120
+ old-to-new transition, or a dropped key term. The review treats constant-topic,
121
+ linking, super-theme, and preview-and-develop progressions as alternatives rather than a
122
+ single mandatory paragraph template.
123
+
124
+ ### 3.4 Finding and selection rewrites
125
+
126
+ A passage can be attached to the composer in three ways:
127
+
128
+ - click a review underline;
129
+ - select text and open the context menu;
130
+ - press `Cmd/Ctrl+K` with a selection.
131
+
132
+ An attached passage is marked in the draft with a tinted highlight that survives the editor losing focus, and moves with edits like a review underline. The composer shows only the finding's pattern name, or `Selected text` for an ordinary selection.
133
+
134
+ Clicking an underline places the caret where it was clicked and leaves the keyboard in the editor, so an underlined sentence stays as editable as any other text. `Cmd/Ctrl+K` and the context menu move focus to the composer instead, because both are explicit requests to instruct.
135
+
136
+ Editing inside the attached passage ends the attachment and cancels an in-flight rewrite: the author has taken the sentence over, and alternatives generated for the old wording no longer apply.
137
+
138
+ Clicking a generic prose finding attaches its quote and shows its card. It makes no model request: the card carries an `Offer rewrites` control, and reading the remark and fixing the sentence by hand is a complete outcome. Clicking the same underline again returns to the card already in the stream rather than repeating the remark.
139
+
140
+ A structural finding names a problem with a paragraph, not with the sentence it quotes, so clicking one attaches the whole blank-line-delimited paragraph around the quote and its `Offer rewrites` control asks for alternatives that rewrite that paragraph — sentences may be reordered and rejoined, facts and voice may not change. Typing in the composer with a structural finding attached still opens a discussion instead, which is the path for a change that has to move material between paragraphs.
141
+
142
+ For an ordinary selection, the next composer message becomes the rewrite instruction.
143
+
144
+ A group of alternatives carries a `Try again` control that discards the three and re-requests them with the same instruction and the same attached passage. It disappears once one alternative has been applied.
145
+
146
+ The server returns exactly three strings. The client optionally ranks them by the resulting whole-document local score and displays that score delta for Latin-script drafts. Clicking a card replaces only the attached range. The remaining cards are then disabled.
147
+
148
+ ### 3.5 Draft chat
149
+
150
+ When no passage is attached, composer messages open a conversation about the whole draft. The server includes the current document in the system context and retains the last 20 valid user/assistant messages supplied by the client.
151
+
152
+ Replies stream through Server-Sent Events and are rendered with a small, HTML-escaped Markdown subset: paragraphs, headings, emphasis, links, lists, inline code, and fenced code blocks.
153
+
154
+ Chat replies cannot directly modify the document.
155
+
156
+ The conversation survives a reload. A message sent with a finding attached carries that finding's context to the model but is shown to the writer as what they typed.
157
+
158
+ ### 3.6 Continuation suggestions
159
+
160
+ After 900 milliseconds without typing, a suggestion may be requested when:
161
+
162
+ - the document contains at least 15 non-whitespace characters;
163
+ - the selection is collapsed;
164
+ - the caret is at the end of a paragraph or before a blank line;
165
+ - the current line is not an `/idea` command.
166
+
167
+ The model returns a 5-15 word continuation. Known local tell words veto the result. A valid continuation appears in a block below the current line without entering the document.
168
+
169
+ - `Tab`: insert the suggestion at its original cursor position.
170
+ - `Escape`: dismiss it.
171
+ - Typing or moving the cursor: dismiss it and cancel pending work.
172
+
173
+ ### 3.7 `/idea` expansion
174
+
175
+ Typing `/idea <instruction>` on a line and pressing `Enter`:
176
+
177
+ 1. removes the command text;
178
+ 2. temporarily makes the editor read-only;
179
+ 3. streams generated text into the command's position;
180
+ 4. restores editing and saves the document.
181
+
182
+ If the request fails, the original command is restored.
183
+
184
+ ### 3.8 Model and access settings
185
+
186
+ The settings dialog:
187
+
188
+ - discovers Pi credentials and supported provider environment variables;
189
+ - lists authenticated providers and their available models;
190
+ - exposes only thinking levels supported by the selected model;
191
+ - saves the active provider/model/thinking selection to local storage;
192
+ - adds and removes Pi API-key credentials;
193
+ - does not offer removal for credentials supplied by environment variables;
194
+ - switches automatic review on or off.
195
+
196
+ When no model is selected, the review, rewrite, and chat actions re-check Pi status once, then report the missing setup in the composer and open this dialog instead of failing silently. Automatic review and continuation suggestions stay silent and make no request.
197
+
198
+ ## 4. State and privacy
199
+
200
+ | State | Location | Lifetime |
201
+ |---|---|---|
202
+ | Working document | `localStorage["wa-working"]` | Until browser storage is cleared |
203
+ | Working document mirror | `DRAFT_FILE` (default `<cwd>/draft.md`) | Until the file is deleted |
204
+ | Agent selection | `localStorage["wa-agent"]` | Until browser storage is cleared |
205
+ | Chat history | `localStorage["wa-chat"]`, last 20 turns | Until `Clear` or browser storage is cleared |
206
+ | Findings | `localStorage["wa-findings"]` | Until a new review, `Clear`, or browser storage is cleared |
207
+ | Automatic review setting | `localStorage["wa-autoreview"]` | Until browser storage is cleared |
208
+ | Checked sentences | Browser memory | Until reload or a full review reset |
209
+ | API credentials | Pi credential storage or environment | Managed by Pi |
210
+
211
+ The document is written to a local Markdown file 800 milliseconds after the last edit. The browser copy is authoritative: the file is read back into the editor only when `localStorage["wa-working"]` is empty, or when the writer accepts the prompt described below.
212
+
213
+ Findings are stored as their quotes and are re-anchored against the document at load, so a finding whose text has since changed is dropped rather than misplaced. The conversation is restored as messages only; finding and alternative cards are not.
214
+
215
+ If the file and the browser copy differ — at startup, or when the window regains focus — Litura says so and offers to load the file. Until the writer accepts, the browser copy stands and the next save overwrites the file. Nothing is merged.
216
+
217
+ `Cmd/Ctrl+S` downloads the draft as `draft.md`. Dropping a text file on the editor replaces the draft, after a confirmation when the current draft is not empty.
218
+
219
+ Every model-backed action sends the current full document to the selected provider. Rewrite and review requests additionally send the relevant selection or target passages. The local score does not make a network request.
220
+
221
+ ## 5. Technical architecture
222
+
223
+ - **Runtime:** Node.js 20 or newer.
224
+ - **Server:** native `node:http`, bound to `127.0.0.1`.
225
+ - **Frontend:** vanilla JavaScript and CodeMirror 6.
226
+ - **Bundling:** esbuild produces an IIFE bundle.
227
+ - **Model runtime:** `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent`.
228
+ - **Streaming:** Server-Sent Events for `/idea` and `/chat`.
229
+ - **Non-streaming:** JSON for status, credentials, review, rewrite, and suggestions.
230
+
231
+ `npm start` bundles the frontend before starting the server. If that build fails, the server logs the error and serves the existing bundle. `public/app.js` is generated and should not be edited directly.
232
+
233
+ ### File structure
234
+
235
+ ```text
236
+ litura/
237
+ |- README.md product overview and setup
238
+ |- SPEC.md product and technical behavior
239
+ |- index.js build step, HTTP server, routes, and prompts
240
+ |- pi.js Pi discovery, credentials, model resolution, requests
241
+ |- review-prompt.js shared review taxonomy and production prompt
242
+ |- review-model.js model request, response parsing, and bounded retries
243
+ |- review.js local metrics and review-response helpers
244
+ |- markdown.js safe minimal Markdown renderer for chat
245
+ |- selfcheck.js assertion-based checks
246
+ |- deepcheck.js model-backed essay and paragraph checks
247
+ |- style.md writing constraints injected into model prompts
248
+ |- plugin.json webview plugin manifest (/write, port 3456)
249
+ |- src/
250
+ | `- app.js frontend source
251
+ `- public/
252
+ |- index.html application shell
253
+ |- style.css application styles
254
+ |- app.js generated bundle
255
+ `- fonts/ local iA Writer Duo files
256
+ ```
257
+
258
+ ## 6. HTTP API
259
+
260
+ All bodies and non-streaming responses are JSON unless noted.
261
+
262
+ | Method | Route | Behavior |
263
+ |---|---|---|
264
+ | `GET` | `/` | Application HTML |
265
+ | `GET` | `/style.css`, `/app.js`, `/fonts/*` | Static assets |
266
+ | `GET` | `/draft` | Current contents of the draft file as `{ text, path }` |
267
+ | `PUT` | `/draft` | Overwrite the draft file with `{ text }` |
268
+ | `GET` | `/api/agent/status` | Providers, models, auth status, and default selection |
269
+ | `POST` | `/api/agent/credentials` | Save a provider API key through Pi |
270
+ | `DELETE` | `/api/agent/credentials` | Remove a stored provider credential |
271
+ | `POST` | `/review` | Return up to eight writing-pattern findings |
272
+ | `POST` | `/rewrite` | Return three replacement variants |
273
+ | `POST` | `/suggest` | Return a short continuation |
274
+ | `POST` | `/idea` | Stream an idea expansion over SSE |
275
+ | `POST` | `/chat` | Stream a draft conversation over SSE |
276
+
277
+ ### Agent selection
278
+
279
+ Model-backed routes accept:
280
+
281
+ ```json
282
+ {
283
+ "agent": {
284
+ "provider": "provider-id",
285
+ "model": "model-id",
286
+ "thinkingLevel": "medium"
287
+ }
288
+ }
289
+ ```
290
+
291
+ If omitted, the server tries the `PI_PROVIDER` and `PI_MODEL` environment values, then preferred fallbacks, then the first available authenticated model. The fallback list prefers named models; a routing pseudo-model such as `openrouter/auto` is last, because a different model per request breaks the output contracts these routes depend on. Unsupported thinking levels are clamped to a level supported by the model.
292
+
293
+ ### Route-specific request fields
294
+
295
+ | Route | Fields |
296
+ |---|---|
297
+ | `/review` | `document`, optional `target`, optional `context`, `agent` |
298
+ | `/rewrite` | `document`, `selected`, optional `instruction`, optional `context`, `agent` |
299
+ | `/suggest` | `document`, optional `cursor`, optional `context`, `agent` |
300
+ | `/idea` | `document`, `idea`, optional `context`, `agent` |
301
+ | `/chat` | `document`, `messages`, optional `selection`, `agent` |
302
+
303
+ The server still accepts optional `context` fields for API callers, although the current browser interface has no separate context editor.
304
+
305
+ SSE routes emit JSON text deltas followed by a final marker:
306
+
307
+ ```text
308
+ data: {"text":"..."}
309
+
310
+ data: [DONE]
311
+ ```
312
+
313
+ An SSE error is emitted as `{"error":"..."}` when headers have already been sent.
314
+
315
+ ## 7. Prompt and style handling
316
+
317
+ `style.md` is read for every request and prepended to the task prompt. It includes shared reader-orientation rules for opening promises, point placement, old-to-new flow, key-term continuity, and problem resolution. Its prose diagnostics also cover vague attribution, mind-like agency assigned to abstractions, stacked hedging, and manufactured emphasis. A shared `NO_SLOP` instruction is added to prose-generating routes so the assistant does not intentionally produce patterns that review would immediately flag.
318
+
319
+ If `style.md` is missing, the server logs one warning and continues without it.
320
+
321
+ Review detects problems but does not rewrite. Rewrite, suggestion, idea, and chat prompts have separate output contracts and token limits.
322
+
323
+ The rewrite prompt leads with the selection, then the instruction, then the containing sentence with the selection replaced by a `___` slot, and only then the full document with the selection marked. A variant longer than three times the selection is treated as a whole-document rewrite: the request is repeated once with the scope restated. Unparseable answers are retried up to three attempts in total.
324
+
325
+ Each review pass validates diagnostic codes against its assigned levels and checks exact quotes against the audited source. Invalid responses are retried, up to three attempts with a 90-second timeout per attempt. Invalid finding objects are errors, not empty reviews.
326
+
327
+ ## 8. Environment
328
+
329
+ | Variable | Purpose | Default |
330
+ |---|---|---|
331
+ | `PI_PROVIDER` | Preferred Pi provider ID | Selected from authenticated models |
332
+ | `PI_MODEL` | Preferred Pi model ID | Selected from authenticated models |
333
+ | `PI_THINKING_LEVEL` | Preferred reasoning level | `medium` |
334
+ | `PORT` | Local HTTP port; the next free port up to +10 is used if taken | `3456` |
335
+ | `STYLE_FILE` | Writing style guide path | `<cwd>/style.md`, else the bundled `style.md` |
336
+ | `DRAFT_FILE` | Draft mirror path | `<cwd>/draft.md` |
337
+ | `LITURA_NO_OPEN` | Set to skip opening the browser at startup | unset |
338
+
339
+ ## 9. Distribution and updates
340
+
341
+ Litura is published to npm and run as `npx litura` from the folder holding the draft. For a bare package name npm re-resolves the registry manifest on every run, so starting Litura is the update; a global install (`npm i -g`) shadows that check and has to be updated by hand.
342
+
343
+ `litura --version` prints the running version. `litura --check-update` prints the published version next to it. Neither the server nor the browser contacts the registry on its own: the check runs only when the command asks for it.
344
+
345
+ | Survives an update | Mechanism |
346
+ |---|---|
347
+ | Draft | `wa-working` and `DRAFT_FILE` are never cleared by the app |
348
+ | Style guide | `STYLE_FILE` is only ever read; the bundled `style.md` is a fallback, not a template that gets written |
349
+ | Model selection and API keys | `wa-agent`; credentials stay in Pi's own store |
350
+ | Findings and chat | Dropped when `STORAGE_SCHEMA` in `src/app.js` is bumped, which happens only when their stored shape changes |
351
+
352
+ `index.html`, `style.css`, and `app.js` are served `no-cache` so an open tab picks up the new bundle on reload instead of running a stale one against a new API. Fonts are immutable.
353
+
354
+ ## 10. Checks
355
+
356
+ ```bash
357
+ npm run build
358
+ npm run check
359
+ npm run check:deep
360
+ ```
361
+
362
+ `npm run check` validates server syntax, rebuilds the browser bundle, exercises Markdown escaping and rendering, checks style metrics and review anchoring, and verifies that Pi status has a consistent shape.
363
+
364
+ `npm run check:deep` sends synthetic failures and clean controls through the configured Pi model and the exact production review prompts. It covers the seven reader-structure levels, exact quote anchoring, targeted-review scope, all four valid paragraph progressions, and paired positive/control cases for selected generic prose diagnostics. Use `DEEP_CASE=name` to run matching cases and `DEEP_RUNS=3` to measure repeatability. The command makes model requests and is therefore kept out of the fast check.
365
+
366
+ The model checks are regression tests, not an independent accuracy benchmark. Set `DEEP_REPORT=results.json` to save individual findings and failures. See [review-evaluation.md](docs/review-evaluation.md) for source provenance, development results, limitations, and the manual browser smoke check. There is no automated browser end-to-end suite.
367
+
368
+ ## 11. Current boundaries
369
+
370
+ Litura currently supports one browser-local plain-text document. It does not include:
371
+
372
+ - a separate reference/context editor;
373
+ - a document picker or multiple documents — the mirror file is one fixed path, and import and export are a drop and a download;
374
+ - document history or versioning beyond CodeMirror's current-session undo stack;
375
+ - accounts, collaboration, or cloud sync;
376
+ - a language-specific local score outside English/Latin-script heuristics.