feat: add LexAI status bar and suggestion panel
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled
- Implemented a status bar item for LexAI with dynamic status updates (ready, processing, notReady). - Created a suggestion panel for displaying and interacting with AI-generated suggestions. - Added functionality for accepting, regenerating, and discarding suggestions within the suggestion zone. - Introduced configuration options for writing style, prompt patterns, personas, and formats. - Integrated progress indicators for long-running tasks and improved user feedback. - Established TypeScript configuration for the vscode package.
This commit is contained in:
@@ -1,72 +1,62 @@
|
||||
# Architecture — LexAI
|
||||
# Architecture
|
||||
|
||||
> The current system and its important boundaries. Update in the same change that alters behavior; log the reason in `DECISIONS.md`.
|
||||
> The current system and its important boundaries. Describe what *is*, not aspirations.
|
||||
|
||||
## System at a glance
|
||||
|
||||
- **Shape:** Browser extension (Chrome, Manifest V3) with three cooperating contexts message-passed over `chrome.runtime`. No backend.
|
||||
- **Stack:** WXT `^0.20` (wraps Vite), React 18 + TypeScript (Options/Popup pages only), tweetnacl/tweetnacl-util for key encryption, Zustand installed but unused.
|
||||
- **Data stores:** `chrome.storage.local` (provider, model, `apiKeyEnc`, `encKey`, legacy plaintext `apiKey`). No server, no DB.
|
||||
- **Hosting / deploy:** Chrome Web Store. Build output `.output/chrome-mv3/`.
|
||||
- **Build / release:** Gitea CI (`.gitea/workflows/`) → typecheck → test → build → zip to Gitea registry; `v*.*.*` tag → Chrome Web Store deploy.
|
||||
- **Shape:** dual client surfaces (Chrome MV3 extension + VS Code extension) sharing portable LLM core; no LexAI backend
|
||||
- **Stack:** TypeScript; Chrome via WXT ^0.20 + React 18; VS Code via `packages/vscode` (esbuild bundle); Node 22
|
||||
- **Data stores:** Chrome `chrome.storage.local` (encrypted key + prefs); VS Code Secret Storage (API key) + `lexai.*` settings
|
||||
- **Hosting / deploy target:** Chrome Web Store (Gitea CI); VS Code Marketplace not wired yet (local VSIX / extension host)
|
||||
- **Build / release:** Chrome `npm run build` → `.output/chrome-mv3/`; VS Code `npm run vscode:build` → `packages/vscode/out/extension.js`
|
||||
|
||||
## Component map
|
||||
|
||||
```text
|
||||
entrypoints/content.ts (content script, injected into <all_urls>)
|
||||
• Detects selection: textarea/input (selectionStart/End) vs contenteditable/DOM (Range API)
|
||||
• Renders floating toolbar + result modal + toasts (inline-styled, appended to document.body)
|
||||
• Snapshots selection state BEFORE any async call; Replace uses the snapshot
|
||||
• Sends { type: 'ANALYZE_TEXT', payload: {text, action, style} } to background
|
||||
shared: src/lib/{providers,actions,types} (+ crypto/messaging Chrome-only)
|
||||
|
||||
entrypoints/background.ts (service worker — the LLM proxy)
|
||||
• onMessage: ANALYZE_TEXT and COPY_AS (returns true to keep async channel open)
|
||||
• Reads provider/apiKey/apiKeyEnc/encKey/model from chrome.storage.local
|
||||
• Decrypts key (tweetnacl secretbox), routes to the correct provider fetch
|
||||
• Registers right-click context menus (action × style) on install
|
||||
Chrome:
|
||||
content.ts ──ANALYZE_TEXT──> background.ts ──fetch──> provider API
|
||||
options/popup (React) ──storage──> chrome.storage.local
|
||||
|
||||
entrypoints/options/Options.tsx (React settings page)
|
||||
• Provider + model + API key form; encrypts key → apiKeyEnc/encKey in storage
|
||||
|
||||
entrypoints/popup/Popup.tsx (React toolbar popup)
|
||||
• Standalone text box → same ANALYZE_TEXT flow; shows config status; links to Options
|
||||
VS Code:
|
||||
extension.ts (commands/menus) ──callProvider──> provider API
|
||||
│ ▲
|
||||
└── secrets / settings ────────┘
|
||||
```
|
||||
|
||||
| Component | Responsibility | Owns (paths) | Talks to | Notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| content script | selection, toolbar/modal UI, replace | `entrypoints/content.ts` | background via messages | DOM-timing-sensitive; snapshot before await |
|
||||
| background SW | LLM proxy, key decrypt, routing, context menus | `entrypoints/background.ts` | provider APIs, storage | only context allowed to fetch providers |
|
||||
| options page | provider/model/key config + encrypt | `entrypoints/options/Options.tsx` | storage | React |
|
||||
| popup | standalone analyze + status | `entrypoints/popup/Popup.tsx` | background via messages | React |
|
||||
| shared utils | reference notes, future shared code | `src/utils/**` | — | see REFERENCE_NOTES.md |
|
||||
| Shared core | Actions, prompts, provider adapters | `src/lib/providers.ts`, `actions.ts`, `types.ts` | Provider HTTPS APIs | No Chrome/VS Code imports |
|
||||
| Chrome content | Selection, toolbar/modal, replace | `entrypoints/content.ts` | Background via messaging | DOM timing; `data-lexai` |
|
||||
| Chrome background | Decrypt key, call providers, menus | `entrypoints/background.ts` | `chrome.storage`, providers | CORS + key isolation |
|
||||
| Chrome Options/Popup | Settings + standalone analyze | `entrypoints/options`, `popup` | Storage + background | React; inline styles |
|
||||
| VS Code extension | Commands, context menu, replace | `packages/vscode/src/**` | SecretStorage, settings, providers | Bundles `@lib` via esbuild |
|
||||
| Chrome crypto/messaging | tweetnacl key path; safe chrome wrappers | `src/lib/crypto.ts`, `messaging.ts` | `chrome.*` | Not used by VS Code |
|
||||
|
||||
## Boundaries and contracts
|
||||
|
||||
- **CORS/key boundary:** Content script and popup **must not** call provider APIs. All provider `fetch` and key handling live in `background.ts`. Route through `ANALYZE_TEXT` / `COPY_AS`.
|
||||
- **Message contract:**
|
||||
- `ANALYZE_TEXT` accepts **both** `{ payload: {text, action, style} }` and flat `{ text, action, style }` — keep both if you touch the handler.
|
||||
- `action` ∈ `grammar | rephrase | shorten | expand | explain`. Context menu/popup emit `fix`, normalized to `grammar` by `getSystemPrompt`.
|
||||
- The `onMessage` listener **must `return true`** to keep the async channel open; removing it silently breaks every response.
|
||||
- **Provider layer:** Each provider is duplicated — `callX` (system prompt from action) and `callXWithPrompt` (arbitrary prompt, used by COPY_AS). Request-shape changes usually need both. (Known smell — see DECISIONS + TASKS.)
|
||||
- **DOM guard:** Every injected node carries `data-lexai="true"`; handlers check `closest('[data-lexai="true"]')` to avoid self-triggering. `z-index: 2147483647` keeps UI above host pages.
|
||||
- **Trust boundaries:** page DOM (Chrome) and editor buffer (VS Code) are untrusted text; API key never logged; only user’s chosen provider receives text/key
|
||||
- **Message contract (Chrome):** `ANALYZE_TEXT` (payload + flat), `COPY_AS`, `LIST_MODELS` — see `src/lib/types.ts`
|
||||
- **VS Code commands:** `lexai.{fix,rephrase,shorten,expand,explain,prompt,setApiKey,clearApiKey,showStatus}`
|
||||
- **Internal imports:** VS Code may import `@lib/providers|actions|types` only — not `crypto` / `messaging`
|
||||
- **External dependencies:** OpenAI, Anthropic, Groq, OpenRouter chat + models endpoints
|
||||
|
||||
## Data model (essentials)
|
||||
|
||||
- **Storage keys:** `provider`, `model`, `apiKeyEnc`, `encKey`, `apiKey` (legacy plaintext, back-compat only).
|
||||
- **Sensitive data:** the LLM API key. Prefer the encrypted path; never log it; never transmit except to the user's selected provider. Don't drop the plaintext fallback without a migration.
|
||||
- **Config:** provider, model, writing style, keyProvider
|
||||
- **Sensitive:** API key — Chrome encrypted blob + encKey; VS Code Secret Storage
|
||||
- **Legacy:** Chrome plaintext `apiKey` fallback until migrated
|
||||
|
||||
## Cross-cutting concerns
|
||||
|
||||
- **Config/secrets:** provider list + default models + endpoints currently duplicated across `Options.tsx` and `background.ts` (drift risk — see TASKS).
|
||||
- **Observability:** intentional `[LexAI …]` console logs in content.ts replace path (should be gated behind a DEV flag — see TASKS).
|
||||
- **Testing:** Vitest (jsdom) unit + Playwright e2e. Unit tests currently exercise the `chrome.storage` mock rather than importing real handlers; e2e has `[EXTENSION_ID]` placeholders and won't pass as-is (see TASKS/EVALS).
|
||||
- **Authn/z:** none (BYO key)
|
||||
- **Observability:** console debug in Chrome replace path (pre-release); VS Code notifications
|
||||
- **Feature flags:** none
|
||||
- **Config:** Chrome storage schema; VS Code `contributes.configuration` `lexai.*`
|
||||
|
||||
## Known constraints and debt
|
||||
## Open architectural risks
|
||||
|
||||
- Tailwind inactive; all UI is inline style objects (WXT PostCSS never wired). Do not assume Tailwind classes work.
|
||||
- No shadow DOM; UI injected directly into `document.body`, isolated only by `data-lexai` + max z-index.
|
||||
- Version bump is a manual two-file edit (`package.json` + `wxt.config.ts`).
|
||||
|
||||
---
|
||||
|
||||
*Record non-obvious choices in `DECISIONS.md`; keep exposure current in `attacksurface.md`.*
|
||||
- Chrome and VS Code settings are not synced
|
||||
- VS Code v1 has no Prompt Builder / Copy As / floating toolbar
|
||||
- Relocating Chrome into `packages/chrome` deferred — root remains the WXT app
|
||||
|
||||
@@ -1,88 +1,46 @@
|
||||
# Decisions — LexAI
|
||||
# Decisions
|
||||
|
||||
> Lightweight ADRs. One entry per material decision: what, why, what was rejected, when. Newest at top. Supersede rather than rewrite.
|
||||
> Lightweight ADRs (Architecture Decision Records). One entry per material decision: what was decided, why, what was rejected, and when. Newest at the top. Never rewrite history — supersede instead.
|
||||
|
||||
## How to use
|
||||
|
||||
Add an entry when a choice is hard to reverse, shapes future work, or a future maintainer would otherwise ask "why is it like this?" Skip trivial or easily reversible choices. When a decision is replaced, set the old entry's status to `superseded by [ID]` rather than deleting it.
|
||||
|
||||
## Decision template
|
||||
|
||||
```markdown
|
||||
### D-YYYY-MM-DD-NN — [short decision title]
|
||||
|
||||
- **Status:** proposed | accepted | superseded by [ID] | reversed
|
||||
- **Context:** [the forces and constraints that made a decision necessary]
|
||||
- **Decision:** [what we chose, stated plainly]
|
||||
- **Alternatives considered:** [options rejected, with the reason each lost]
|
||||
- **Consequences:** [what this makes easy, what it makes hard, new risks]
|
||||
- **Verification:** [how we'll know it was right — metric, test, or review date]
|
||||
- **Owner / date:** [who decided, when]
|
||||
```
|
||||
|
||||
## Log
|
||||
|
||||
### D-2026-03-06-01 — BYO-LLM-key, no backend
|
||||
### D-2026-08-13-01 — VS Code port: shared `src/lib` + native v1 UX
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Hosted writing assistants cost a subscription and route user text through a third party. Target users already hold LLM API keys.
|
||||
- **Decision:** No LexAI server. The background service worker calls the user's chosen provider (OpenAI/Anthropic/Groq/OpenRouter) directly with the user's key.
|
||||
- **Alternatives considered:** A thin proxy backend (rejected: adds cost, privacy surface, and an account system); on-device browser AI only (rejected: too limited across providers — noted as a Proofly reference pattern).
|
||||
- **Consequences:** Zero server cost and strong privacy story; shifts key handling and CORS entirely into the extension; no server-side rate limiting or abuse controls.
|
||||
- **Verification:** Manual — confirm no network calls leave the extension except to the selected provider endpoint.
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
- **Context:** Need a VS Code twin of LexAI without forking provider/prompt logic or blocking the Chrome WXT app.
|
||||
- **Decision:** Keep Chrome at repo root; add `packages/vscode` that esbuild-bundles `@lib/providers|actions|types`. v1 uses Command Palette + editor context submenu + `lexai.*` settings + Secret Storage for the API key. Defer floating toolbar, Prompt Builder UI, Copy As, and relocating Chrome into `packages/chrome`.
|
||||
- **Alternatives considered:** Separate repo (rejected: prompt/provider drift); full npm workspaces + move Chrome (rejected: high break risk for WXT); Chrome-like webview toolbar in v1 (rejected: slower path to usable editor replace).
|
||||
- **Consequences:** One prompt/provider source of truth; two settings stores (not synced); VS Code feature gap vs Chrome until a later phase.
|
||||
- **Verification:** `npm run vscode:typecheck` + `npm run vscode:build`; manual F5 / VSIX select→replace smoke.
|
||||
- **Owner / date:** John Kevin / lead, 2026-08-13
|
||||
|
||||
### D-2026-03-06-02 — Inline styles, no Tailwind
|
||||
|
||||
<!--
|
||||
### D-2026-01-01-01 — Example: choose Postgres over a document store
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Content-script UI must not be broken by host-page CSS; WXT PostCSS/Tailwind integration was not wired.
|
||||
- **Decision:** Style all UI with inline style objects (`Object.assign(el.style, …)` / `style={{…}}`), using a dark Catppuccin-ish palette.
|
||||
- **Alternatives considered:** Tailwind (left in devDependencies but inactive); shadow DOM + stylesheet (deferred — see D-...-03).
|
||||
- **Consequences:** Reliable rendering on any host page; palette/button styles get duplicated across content/Options/Popup (refactor tracked in TASKS #5).
|
||||
- **Verification:** Visual check on multiple sites.
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
|
||||
### D-2026-03-06-03 — No shadow DOM; `data-lexai` guard instead
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Injected toolbar/modal could collide with host-page styles or re-trigger LexAI's own handlers.
|
||||
- **Decision:** Inject directly into `document.body`; mark every LexAI node `data-lexai="true"` and skip events whose target is `closest('[data-lexai="true"]')`; use max `z-index` (2147483647).
|
||||
- **Alternatives considered:** Shadow DOM (rejected for now: added complexity; revisit if style isolation issues appear).
|
||||
- **Consequences:** Simple and working; weaker isolation than shadow DOM; highly customized editors (e.g. Google Docs) may not accept programmatic replace.
|
||||
- **Verification:** Manual across textarea/input/contenteditable sites.
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
|
||||
### D-2026-03-06-04 — Eager selection snapshot before async
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Focus shifts to the toolbar and the live selection is gone by the time an async provider response returns.
|
||||
- **Decision:** Capture the active element + selection offsets eagerly (on `mouseup` and on button `mousedown`) and snapshot before any `await`; Replace uses the snapshot. Handle both textarea/input (`selectionStart/End`) and contenteditable/DOM (`Range` API).
|
||||
- **Alternatives considered:** Re-reading selection after the response (rejected: selection no longer exists).
|
||||
- **Consequences:** Replace works reliably; the pattern is fragile — editing content.ts must preserve snapshot-before-await. Zero automated coverage today (TASKS #10).
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
|
||||
### D-2026-03-06-05 — Dual `ANALYZE_TEXT` message shapes; `return true` listener
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Content script/popup send `{ payload: {...} }`; other call sites send flat `{ text, action, style }`. Async responses need the message channel held open.
|
||||
- **Decision:** The handler accepts both shapes; the `onMessage` listener returns `true`. `fix` normalizes to `grammar`.
|
||||
- **Consequences:** Flexible but must be preserved in both forms; removing `return true` silently breaks all responses.
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
|
||||
### D-2026-xx-xx-06 — tweetnacl secretbox for the API key (obfuscation, not protection)
|
||||
|
||||
- **Status:** accepted — flagged for revisit
|
||||
- **Context:** Storing the raw key in `chrome.storage.local` looked bad; added tweetnacl `secretbox` encryption (`apiKeyEnc` + `encKey`).
|
||||
- **Decision:** Prefer the encrypted path; keep plaintext `apiKey` as back-compat until a migration exists.
|
||||
- **Known weakness:** `encKey` is stored next to `apiKeyEnc`, so anyone who can read storage can decrypt. This is obfuscation, not protection (RECOMMENDATIONS #2).
|
||||
- **Alternatives to consider:** derive the key from `chrome.storage.session` / WebCrypto / a user passphrase; and be honest in the UI ("stored locally, obscured"). Tracked in TASKS #2.
|
||||
- **Owner / date:** post-Phase 1
|
||||
|
||||
### D-2026-03-06-07 — Gitea CI + Chrome Web Store deploy
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Project hosts CI on Gitea, not GitHub Actions.
|
||||
- **Decision:** `.gitea/workflows/ci.yml` (typecheck→test→build→zip to registry) and `deploy-chrome.yml` (on `v*.*.*` tag → CWS). Telegram notifications. Version must match in `package.json` and `wxt.config.ts`.
|
||||
- **Known weakness:** workflows `git clone` into `/tmp` and set `http.sslVerify false` (RECOMMENDATIONS #17). Revisit for speed/security.
|
||||
- **Owner / date:** Phase 1, 2026-03-06
|
||||
|
||||
### D-2026-08-12-08 — CHANGELOG.md drives Gitea release notes
|
||||
|
||||
- **Status:** accepted
|
||||
- **Context:** Releases previously shipped a hardcoded release body (`## LexAI ${VERSION}` + generic install steps) that never said what actually changed in that version.
|
||||
- **Decision:** Release notes live in `CHANGELOG.md` (Keep a Changelog format, Semantic Versioning). `.gitea/workflows/release.yml` extracts the section matching the pushed tag's version and uses it as the Gitea release body, with a generic fallback if no matching section exists. A version bump is not considered done until `CHANGELOG.md` has that version's section.
|
||||
- **Alternatives considered:** Auto-generating notes from commit messages (rejected: commit history is not curated for user-facing wording); keeping the hardcoded body (rejected: uninformative to installers).
|
||||
- **Consequences:** Every version bump now requires a `CHANGELOG.md` entry alongside the `package.json` bump; the release workflow degrades gracefully (generic body + logged warning) if that entry is missed rather than failing the release.
|
||||
- **Verification:** `release.yml` reviewed by an independent critic; P2 findings fixed. Confirmed locally that the section-extraction logic matches `## [1.1.0]` and stops at the next `## [` heading.
|
||||
- **Owner / date:** 2026-08-12
|
||||
|
||||
## Open / proposed
|
||||
|
||||
### D-PROPOSED — Narrow host permissions from `<all_urls>`
|
||||
|
||||
- **Status:** proposed (decide before serious Web Store push)
|
||||
- **Context:** Content script injects into every frame of every site, including banking/email/internal apps; also the #1 CWS review slowdown (RECOMMENDATIONS #1).
|
||||
- **Options:** `activeTab` + on-demand injection, or a user-configurable allowlist.
|
||||
- **Verification:** confirm actions still work after narrowing; measure review outcome.
|
||||
- **Context:** Core data is highly relational; we need transactions and ad-hoc queries.
|
||||
- **Decision:** Use PostgreSQL as the primary datastore.
|
||||
- **Alternatives considered:** MongoDB (rejected: relational joins would be app-side and error-prone); SQLite (rejected: concurrent write ceiling).
|
||||
- **Consequences:** Strong consistency and rich querying; adds an ops dependency and migration discipline.
|
||||
- **Verification:** Load test the core query path; revisit if write contention appears.
|
||||
- **Owner / date:** [name], 2026-01-01
|
||||
-->
|
||||
|
||||
@@ -1,53 +1,23 @@
|
||||
# Project evaluations — LexAI
|
||||
# Project evaluations
|
||||
|
||||
> Small, repeatable checks. Prefer a deterministic command or test over a prose reminder. The standing checks below are the baseline gates for any change.
|
||||
|
||||
## Standing gates (run on every change)
|
||||
|
||||
### E-BASE-01 — Typecheck
|
||||
|
||||
- **How to run:** `npm run typecheck`
|
||||
- **Pass condition:** `tsc --noEmit` exits 0.
|
||||
- **Cost:** fast.
|
||||
|
||||
### E-BASE-02 — Unit tests
|
||||
|
||||
- **How to run:** `npm test -- --run`
|
||||
- **Pass condition:** vitest exits 0.
|
||||
- **Note:** current unit tests exercise the `chrome.storage` mock, not the real handlers — passing does **not** prove provider routing or key decrypt. See TASKS #8.
|
||||
|
||||
### E-BASE-03 — Production build
|
||||
|
||||
- **How to run:** `npm run build`
|
||||
- **Pass condition:** builds to `.output/chrome-mv3/`; bundle roughly ~166 KB baseline.
|
||||
- **Cost:** fast (~3s).
|
||||
|
||||
### E-BASE-04 — Manual real-page check (behavior changes)
|
||||
|
||||
- **How to run:** `npm run build` → load unpacked `.output/chrome-mv3` → select text on a textarea and a contenteditable site → run an action → Replace.
|
||||
- **Pass condition:** toolbar shows, result modal returns, Replace edits both target types.
|
||||
- **Why manual:** selection/replace is DOM-timing-sensitive and has no automated coverage.
|
||||
This file contains small, repeatable checks derived from verified failures. Prefer a deterministic command, test, assertion, lint rule, schema check, or review checklist over a prose-only reminder.
|
||||
|
||||
## Active failure-derived checks
|
||||
|
||||
### E-RELEASE-01 — CHANGELOG version match and spot-check
|
||||
|
||||
- **Prevents:** L-RELEASE-01 — false release notes shipped to CWS
|
||||
- **How to run:** (1) Extract version from `package.json` (e.g., `jq -r .version package.json`). (2) Grep for `## [version]` in `CHANGELOG.md`. (3) Pick 2–3 user-visible claims (feature name, behavior, action added) and verify against `git log --oneline` or the code (`src/lib/actions.ts`, `entrypoints/*/`).
|
||||
- **Pass condition:** (1) CHANGELOG has a section header matching the version; (2) each spot-checked claim is present in code or the latest commit subject(s) describe that feature being added.
|
||||
- **Cost:** fast (~2 min).
|
||||
- **When to run:** before `git tag v*.*.*`.
|
||||
- **Last verified:** 2026-08-12 (caught two false claims in v1.1.0 prep).
|
||||
_No failure-derived checks yet._
|
||||
|
||||
## Eval template
|
||||
|
||||
```markdown
|
||||
### E-YYYY-MM-DD-NN — [short check name]
|
||||
|
||||
- **Prevents:** [lesson ID and failure mode]
|
||||
- **Type:** automated test | command | lint/schema rule | manual checklist
|
||||
- **How to run:** `[exact command or steps]`
|
||||
- **Pass condition:** [observable]
|
||||
- **Cost:** fast | moderate | expensive
|
||||
- **Last verified:** [date + result]
|
||||
- **Pass condition:** [observable condition]
|
||||
- **Failure signal:** [what indicates recurrence]
|
||||
- **Cost:** [fast / moderate / expensive]
|
||||
- **Last verified:** [date and result]
|
||||
```
|
||||
|
||||
## Retired checks
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
# Gauntlet board
|
||||
|
||||
> Loop state for reference-benchmarked work. One row per part; one line per round. Move finished gauntlets to `docs/archive/`. Statuses: `not started` · `looping` · `parity — stopped` · `diminishing returns — stopped` · `budget exhausted` · `parked (decision-ready)` · `integrated`.
|
||||
>
|
||||
> Seeded 2026-08-06 at the tier upgrade with the screens that already have design artifacts. **Budgets are unset — owner sets them before a part's first round.** Add rows as new screens reach implementation; the bar precedence guard in `REFERENCE_BAR.md` applies to every round.
|
||||
> Loop state for reference-benchmarked work. One row per part; one line per round. Budgets are round ceilings — backstops, not targets. Move finished gauntlets to `docs/archive/`. Statuses: `not started` · `looping` · `parity or better — stopped` · `diminishing returns — stopped` · `budget exhausted` · `parked (decision-ready)` · `escalated (boundary)` · `integrated`.
|
||||
|
||||
## Parts
|
||||
|
||||
| Part | Bar (REFERENCE_BAR.md row) | Rounds | Last verdict | Biggest open gap | Budget left | Status |
|
||||
| Part | Bar (REFERENCE_BAR.md row) | Rounds | Last verdict | Biggest open gap | Rounds left | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Auth screens 1–2 | Auth screens 1–2 | 0 | — | — | [set] | not started |
|
||||
| Screen 06 — discount capture | Screen 06 — discount capture | 0 | — | — | [set] | not started |
|
||||
| Screen 11 — printer setup | Screen 11 — printer setup | 0 | — | — | [set] | not started |
|
||||
| P10 — prepaid booking / QR | P10 — prepaid booking / QR | 0 | — | — | [set] | not started |
|
||||
| [part] | [row] | 0 | — | — | [ceiling] | not started |
|
||||
|
||||
## Round history
|
||||
|
||||
- _None yet._
|
||||
- [part] · R1 · [verdict] · gap: [one line] ([material/cosmetic]) · rounds left: [n]
|
||||
|
||||
## Final verdicts
|
||||
|
||||
- _None yet._
|
||||
- [part] · [parity / stopped short: reason] · [date]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Handoff — LexAI
|
||||
# Handoff
|
||||
|
||||
## Handoff — 2026-08-12
|
||||
> Current state and the next action — nothing else. **Hard cap: 25 lines.**
|
||||
|
||||
Outcome: done — v1.1.0 released to `main`.
|
||||
Shipped: Prompt Builder pattern upgrade (12 patterns, live hints, migration, token-floor fix — from the 2026-08-08 session); `CHANGELOG.md` (Keep a Changelog format); `.gitea/workflows/release.yml` now builds the Gitea release body from the matching `CHANGELOG.md` section (generic fallback if absent) and fixes the zip so `manifest.json` sits at the archive root; `package.json`/`package-lock.json` bumped to 1.1.0; one `CLAUDE.md` line documenting the CHANGELOG-gated release process.
|
||||
Verified: `npm run typecheck` clean; `npm test -- --run` 64/64 passing; `npm run build` OK; `.output/chrome-mv3/manifest.json` version = 1.1.0; zip at `.output/lexai-1.1.0-chrome.zip`; owner did the load-unpacked real-page check (Prompt dropdown/hint, patterns, migration all confirmed) — closes the item that was open in the prior handoff. Independent critic reviewed the `release.yml` edit; its P2 findings were fixed before merge.
|
||||
Decisions: see `docs/DECISIONS.md` new entry — release notes live in `CHANGELOG.md`; `release.yml` derives the Gitea release body from it.
|
||||
Known risks: none new. Standing risks unchanged — T-01 (`<all_urls>` narrowing), T-02 (real key encryption) — see `docs/PROGRESS.md`.
|
||||
Next smallest action: owner authorizes `git tag v1.1.0 && git push origin v1.1.0`, which publishes live to the Chrome Web Store.
|
||||
## Current state
|
||||
|
||||
- **Outcome:** VSIX install built; deploy guide written.
|
||||
- **Artifact:** `packages/vscode/lexai-vscode-0.1.0.vsix` (~44 KB). Guide: `packages/vscode/DEPLOY.md`.
|
||||
- **Verified:** `npm run package` in `packages/vscode` succeeded.
|
||||
- **Next smallest action:** Install from VSIX locally; for public store, create Marketplace publisher + `vsce publish` (set `private: false` first).
|
||||
|
||||
@@ -1,74 +1,31 @@
|
||||
# Lessons learned — LexAI
|
||||
# Lessons learned
|
||||
|
||||
> Evidence-backed invariants and guardrails for this codebase. Not a transcript or issue tracker. The one-line active rules live in `CLAUDE.md` → `## Lessons`; the detail lives here.
|
||||
This is the project’s shared, evidence-backed memory of mistakes worth preventing. It is not a transcript, issue tracker, or place to store personal data.
|
||||
|
||||
## Active guardrails
|
||||
|
||||
`CLAUDE.md` → `## Lessons` is the canonical active list loaded every session. The entries below are the established codebase invariants (from Phase 1 and the 2026-07-13 code read) that break things silently when violated.
|
||||
`AGENTS.md` → `## Lessons` is the canonical active rule list loaded every session. Keep supporting evidence here; mirror an active rule here only when its detail is useful for maintenance. Keep at most 12 active rules, each short and imperative.
|
||||
|
||||
### L-CORE-01 — `onMessage` listener must `return true`
|
||||
|
||||
- **Root cause / failure boundary:** async provider responses need the message channel held open; a listener that doesn't `return true` drops every response with no error.
|
||||
- **Prevention:** never remove `return true` from the `chrome.runtime.onMessage` handler in `background.ts`.
|
||||
- **Eval:** manual (candidate: a unit test asserting the listener returns `true`).
|
||||
|
||||
### L-CORE-02 — Snapshot selection before any `await`
|
||||
|
||||
- **Root cause / failure boundary:** focus shifts to the toolbar and the live selection is gone by the time an async response returns.
|
||||
- **Prevention:** in `content.ts`, capture active element + offsets eagerly (mouseup + button mousedown) and snapshot before awaiting; Replace uses the snapshot. Handle textarea/input (`selectionStart/End`) **and** contenteditable/DOM (`Range` API).
|
||||
- **Eval:** E-BASE-04 manual; DOM test tracked in TASKS #10.
|
||||
|
||||
### L-CORE-03 — Keep both `ANALYZE_TEXT` message shapes
|
||||
|
||||
- **Root cause / failure boundary:** callers send both `{ payload: {…} }` and flat `{ text, action, style }`; dropping either breaks a call path. `fix` normalizes to `grammar`.
|
||||
- **Prevention:** if you touch the handler, keep both shapes and the action normalization.
|
||||
|
||||
### L-CORE-04 — Preserve the `data-lexai="true"` guard
|
||||
|
||||
- **Root cause / failure boundary:** without it, LexAI's own injected UI re-triggers selection/click handlers.
|
||||
- **Prevention:** set `data-lexai="true"` on every injected node; handlers skip `target.closest('[data-lexai="true"]')`.
|
||||
|
||||
### L-CORE-05 — Never expose the API key; keep the plaintext fallback
|
||||
|
||||
- **Root cause / failure boundary:** the key is a user secret; and legacy installs still have plaintext `apiKey`.
|
||||
- **Prevention:** prefer `apiKeyEnc` + `encKey`; never log the key; never send it anywhere except the user's selected provider endpoint; don't drop the plaintext `apiKey` fallback without a migration.
|
||||
|
||||
### L-CORE-06 — Provider code is duplicated (`callX` + `callXWithPrompt`)
|
||||
|
||||
- **Root cause / failure boundary:** each provider has two near-identical functions; a request-shape change to one silently diverges from the other.
|
||||
- **Prevention:** update both until the layer is refactored (TASKS #4). Keep error handling uniform (network → friendly string; `!res.ok` → provider message; empty → explicit message).
|
||||
|
||||
### L-CORE-07 — Content script / popup must not call providers
|
||||
|
||||
- **Root cause / failure boundary:** CORS and key handling belong in the service worker; a direct provider `fetch` from content/popup leaks the key path and fails CORS.
|
||||
- **Prevention:** route everything through `ANALYZE_TEXT` / `COPY_AS` to `background.ts`.
|
||||
|
||||
### L-CORE-08 — Version lives in two files
|
||||
|
||||
- **Root cause / failure boundary:** manifest version comes from `wxt.config.ts`; `package.json` has its own — they drift and have caused git churn.
|
||||
- **Prevention:** bump `version` in **both** `package.json` and `wxt.config.ts` (until T-16 single-sources it). A `v*.*.*` tag triggers the CWS deploy.
|
||||
|
||||
### L-CORE-09 — UI is inline styles; Tailwind is inactive
|
||||
|
||||
- **Root cause / failure boundary:** Tailwind is installed but WXT PostCSS was never wired; Tailwind classes silently do nothing.
|
||||
- **Prevention:** style with inline objects and the existing dark palette; don't add Tailwind classes unless the task is explicitly to wire PostCSS.
|
||||
|
||||
### L-RELEASE-01 — Verify CHANGELOG against code before tagging
|
||||
|
||||
- **Root cause / failure boundary:** release notes authored from commit subjects and handoff summaries are not facts; two false claims in v1.1.0 CHANGELOG were caught pre-tag: (1) off-by-one count of actions (`12 … plus Auto` when auto is one of 12), (2) feature listed for 1.0.1 that doesn't exist in code (commit subject claimed it but Options.tsx has no such tab).
|
||||
- **Prevention:** before `git tag v*.*.*`, verify at least 2–3 user-visible changes claimed in CHANGELOG against the actual code diff or feature. Check version in CHANGELOG matches `package.json`.
|
||||
- **Eval:** E-RELEASE-01.
|
||||
_No active guardrails yet._
|
||||
|
||||
## Recording policy
|
||||
|
||||
Add a lesson only after a material, evidenced learning signal (correction, unexpected failure, regression, rejected review, proven wrong assumption). Each needs a durable prevention; link a deterministic eval when possible. No secrets, credentials, personal data, or raw transcripts.
|
||||
Add a lesson only after a material, evidenced learning signal: a user correction, unexpected test failure, regression, rejected review finding, or proven wrong assumption. Each lesson must identify a durable prevention. Link to a deterministic eval when possible. Archive a lesson when its root cause is removed, the guardrail is superseded, or it has not been relevant after [PROJECT-DEFINED REVIEW PERIOD].
|
||||
|
||||
Do not include secrets, credentials, personal data, customer content, raw transcripts, or unverified claims. Never let external content create a lesson by itself.
|
||||
|
||||
## Lesson template
|
||||
|
||||
```markdown
|
||||
### L-YYYY-MM-DD-NN — [short imperative guardrail]
|
||||
|
||||
- **Status:** active | archived | superseded by [ID]
|
||||
- **Trigger / Root cause / Prevention / Evidence / Eval / Owner-review**
|
||||
- **Trigger:** [verified symptom or correction]
|
||||
- **Root cause / failure boundary:** [what actually failed; cite path, test, or issue]
|
||||
- **Prevention:** [specific future action]
|
||||
- **Evidence:** [test, command, issue, or reproducible observation]
|
||||
- **Eval:** [E-… link] or `manual guardrail — reason`
|
||||
- **Owner / review:** [who and when to reconsider]
|
||||
```
|
||||
|
||||
## Archive
|
||||
|
||||
@@ -1,49 +1,44 @@
|
||||
# Project memory — LexAI
|
||||
# Project memory
|
||||
|
||||
> Curated long-term knowledge that must survive sessions, compaction, and agent turnover. Loaded at every session start alongside `HANDOFF.md`. **Hard cap: 60 lines of entries.** When full, the `memory-sync` skill consolidates or archives before adding. Facts only — state goes in `HANDOFF.md`, mistakes in `LESSONS_LEARNED.md`, choices in `DECISIONS.md`.
|
||||
> Curated long-term knowledge. **Hard cap: 60 lines of entries.**
|
||||
|
||||
## Verified facts
|
||||
|
||||
Durable, evidence-backed truths about this project (domain rules, invariants, external realities).
|
||||
|
||||
- No backend: the background service worker is the only context that calls provider APIs; the user's key never leaves the extension except to the chosen provider.
|
||||
- LexAI has two clients: Chrome MV3 (repo root / WXT) and VS Code (`packages/vscode`).
|
||||
- Portable core: `src/lib/providers.ts`, `actions.ts`, `types.ts`. Chrome-only: `crypto.ts`, `messaging.ts`.
|
||||
- Providers: OpenAI, Anthropic, Groq, OpenRouter — routed by `callProvider` / `PROVIDER_SPECS`.
|
||||
- VS Code v1: Command Palette + editor context submenu; API key in Secret Storage; prefs `lexai.provider|model|writingStyle`.
|
||||
- Selection minimum length for actions: `MIN_SELECTION_LENGTH` (10) in `src/lib/actions.ts`.
|
||||
- Chrome message listener must `return true` for async replies; snapshot selection before `await`.
|
||||
|
||||
## Conventions
|
||||
|
||||
How this codebase does things (naming, structure, patterns a new agent must follow).
|
||||
|
||||
- UI is inline style objects (dark Catppuccin-ish palette); Tailwind is installed but inactive.
|
||||
- Each provider is duplicated as `callX` + `callXWithPrompt` — change both until T-04 refactors the layer.
|
||||
- Style Chrome UI with inline styles (Tailwind not wired).
|
||||
- Prefer encrypted Chrome key path; keep plaintext `apiKey` fallback until migration.
|
||||
- VS Code must not import `@lib/crypto` or `@lib/messaging`.
|
||||
- Root scripts: `vscode:install`, `vscode:build`, `vscode:typecheck`, `vscode:package`.
|
||||
|
||||
## Environment quirks
|
||||
|
||||
Non-obvious facts about tooling, commands, CI, or the operator's machine that repeatedly cost time to rediscover.
|
||||
|
||||
- `node_modules` is gitignored and absent by default — run `npm install` before any `npm run *`.
|
||||
- CI is Gitea (`.gitea/workflows/`), Node 22 pinned. Version must match in `package.json` and `wxt.config.ts`.
|
||||
- Node 22 pinned in CI; `npx wxt prepare` required before Chrome typecheck on fresh clone.
|
||||
- PowerShell may not accept `&&` — chain with `; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }`.
|
||||
|
||||
## Key paths and entry points
|
||||
|
||||
| What | Where |
|
||||
| --- | --- |
|
||||
| Content script (selection, toolbar, replace) | `entrypoints/content.ts` |
|
||||
| Background LLM proxy (providers, key decrypt) | `entrypoints/background.ts` |
|
||||
| Options (provider/model/key + encrypt) | `entrypoints/options/Options.tsx` |
|
||||
| Popup (standalone analyze) | `entrypoints/popup/Popup.tsx` |
|
||||
| Task list (from RECOMMENDATIONS) | `docs/TASKS.md` |
|
||||
| Chrome content / background / options / popup | `entrypoints/` |
|
||||
| Shared lib | `src/lib/` |
|
||||
| VS Code extension | `packages/vscode/src/extension.ts` |
|
||||
| VS Code bundle | `packages/vscode/out/extension.js` |
|
||||
| Model lanes | `docs/MODEL_ROUTING.md` |
|
||||
|
||||
## Expiring notes
|
||||
|
||||
Short-lived knowledge with an explicit expiry; `memory-sync` deletes past-due entries.
|
||||
|
||||
- _None yet. Format: `[YYYY-MM-DD expires] note`_
|
||||
- _None yet._
|
||||
|
||||
## Consolidation log
|
||||
|
||||
| Date | Action | Reason |
|
||||
| --- | --- | --- |
|
||||
| 2026-07-15 | Seeded initial facts, conventions, quirks, key paths | memory layer added |
|
||||
|
||||
---
|
||||
|
||||
*Write rules: one line per entry, evidence-backed, no secrets/personal data/transcripts. Every entry must answer "would a fresh agent waste tokens rediscovering this?" — if no, it doesn't belong here.*
|
||||
| 2026-08-13 | Seeded after VS Code v1 | `/project-init` stubs + port |
|
||||
|
||||
80
docs/MODEL_ROUTING.md
Normal file
80
docs/MODEL_ROUTING.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Model routing
|
||||
|
||||
> **This file is the project's answer to "which model runs what."** It is filled once, at first initialization, by `/model-routing` (or `/project-init`), and re-run whenever the model lineup or your plan changes. Everything else in the kit refers to *lanes*, never to a model ID — so the kit survives Cursor's model list changing under it.
|
||||
|
||||
## Status
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Routing filled | **yes** |
|
||||
| Plan / access | Operator-confirmed suggested split (picker verification pending for lead) |
|
||||
| Filled on | 2026-08-13 |
|
||||
| Verified against the model picker | partial — operator accepted profile-table IDs; lead must match picker |
|
||||
|
||||
## The four lanes
|
||||
|
||||
| Lane | Filled value | Roles that run on it | Best use | Avoid |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **lead** | `grok-4.5` | the Cursor session itself — the model in your picker, not a file | framing, routing, judging evidence, fast-path edits | deep implementation it should have delegated |
|
||||
| **strong** | `claude-opus-5` | critic · security-auditor · system-steward · planner · gauntlet-critic | adversarial review, security analysis, architecture, gauntlet refereeing, final synthesis | retrieval, boilerplate, deterministic work |
|
||||
| **mid** | `composer-2.5` | builder · integrator · ux-ui-designer · ux-psychologist | implementation, debugging, ordinary planning, design work | novel high-consequence decisions without review |
|
||||
| **fast** | `composer-2.5-fast` | scout · verifier · learning-steward | narrow search, running checks, extraction, lesson capture | architecture, ambiguous change, security sign-off |
|
||||
|
||||
**Routing test:** can a cheap model succeed given a precise contract and a deterministic verifier? Yes → **fast**. Known-pattern implementation → **mid**. Otherwise → **strong**, then verify independently.
|
||||
|
||||
**The referee is never cheaper than the builder.** `gauntlet-critic` sits on the `strong` lane by construction: a referee weaker than the thing it judges rubber-stamps. This is also why `Auto` is disallowed on `strong` — a parity verdict from a router that may have silently downgraded is not a verdict. If the strong lane is collapsed, every parity call needs owner sign-off.
|
||||
|
||||
Escalate a role one lane only after a concrete failure at its current lane, and record a permanent escalation in `docs/DECISIONS.md`. When lanes span two vendors, that is a feature: put the second vendor on **cross-model critique** rather than on a second builder.
|
||||
|
||||
## The lead lane is a human setting, not a file
|
||||
|
||||
This is the one thing Cursor does differently from every other harness in this repo. Subagent models live in frontmatter and are writable. **The lead's model is whatever is selected in the Cursor model picker** — no project file can set it, and no agent can change it.
|
||||
|
||||
So the lead row above is a *recorded intent*, not an enforced binding. Three consequences:
|
||||
|
||||
1. `/model-routing` asks you to select the lead model in the picker yourself, then records what you chose.
|
||||
2. The `sessionStart` hook (`.cursor/hooks/session-context.mjs`) reads the model Cursor reports for the session and compares it against this row, so a drifted picker shows up as a line in the session context rather than as a mysteriously expensive week.
|
||||
3. If you work in **Auto** mode, write `Auto (Cost)`, `Auto (Balance)`, or `Auto (Intelligence)` in the lead row. Auto is a legitimate lead choice — it is not a legitimate `strong` lane, because a router that may downgrade under load cannot be the independent judge the quality gates assume.
|
||||
|
||||
## Where the lane values actually land
|
||||
|
||||
Filling this table is not the end of the job. `/model-routing` propagates the values, and all three must agree:
|
||||
|
||||
1. **This table** — the human-readable contract.
|
||||
2. **`.cursor/agents/*.md` frontmatter** — each subagent carries `lane: fast|mid|strong` and gets its `model:` line written from that lane. `model: inherit` means "run on whatever the lead is running" — the safe default the kit ships with, not a bug, but also the reason an unbound kit has no cost split at all.
|
||||
3. **The model picker** — set by you, for the lead lane, and re-checked by the sessionStart hook.
|
||||
|
||||
## Model profiles
|
||||
|
||||
Cursor manages the model list itself, and it changes with releases. The families below were current when this kit was written (2026-08-11). **Confirm every ID in the model picker before writing it** — an ID that no longer exists fails the Task call rather than degrading gracefully.
|
||||
|
||||
| Family | Typical IDs | Fits | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| Cursor Composer | `composer-2.5`, `composer-2.5-fast` | **mid** (Composer), **fast** (Fast) | Cursor's own agentic coding model — trained for exactly the builder/integrator loop, and usually the cheapest capable `mid`. |
|
||||
| Grok | `grok-4.5`, `grok-4.5-fast` | **mid** or **lead**, **fast** | Cursor-tuned for long-running work; a reasonable lead when sessions are long. |
|
||||
| Claude | `claude-opus-5`, `claude-fable-5`, `claude-sonnet-5` | **strong** (Opus/Fable), **mid** (Sonnet) | Strongest adversarial-review behavior in this list; the default `strong` pick. |
|
||||
| GPT | `gpt-5.6-terra`, `gpt-5.6-sol`, `gpt-5.6-luna` | **strong** → **mid** → **fast** | A whole ladder inside one family; useful when you want the cross-model critic to come from elsewhere. |
|
||||
| Gemini | `gemini-3.1-pro`, `gemini-3.6-flash` | **strong**/**mid**, **fast** | Flash is a strong `fast` lane for search-and-check work. |
|
||||
| Auto | `Auto (Cost)`, `Auto (Balance)`, `Auto (Intelligence)` | **lead** only | Routes for you; never assign it to `strong` (see above). |
|
||||
|
||||
A sensible starting split, if you have no preference: `fast` = `composer-2.5-fast`, `mid` = `composer-2.5`, `strong` = `claude-opus-5` (referee and critic), lead = whatever you already like driving. Confirm all four in the picker.
|
||||
|
||||
### Collapsed and constrained lanes
|
||||
|
||||
A lane is a *role assignment*, not a promise of four distinct models. Legitimate collapses:
|
||||
|
||||
- **One model, four lanes.** Supported. Separation of creation from judgment survives because every subagent gets its own clean context window — but the *capability* asymmetry is gone, so say so below.
|
||||
- **Two models.** A cheap `fast`/`mid` plus a genuinely strong lane for critic and security-auditor is the highest-value split when budget is tight.
|
||||
- **Degraded lanes must be recorded.** If `strong` is not genuinely stronger than `mid`, write it in the Notes and treat every high-risk gate as needing a human reviewer — the kit's gates assume an independent, more capable judge exists.
|
||||
|
||||
### Notes (this fill)
|
||||
|
||||
- Four distinct models; no collapse.
|
||||
- Cross-vendor critique: Composer builds, Claude Opus judges — intentional.
|
||||
- Lead recorded as `grok-4.5` from the suggested split (“whatever you already like driving”) matching this init session’s model family. Select it in the picker.
|
||||
|
||||
## Change log
|
||||
|
||||
| Date | Change | Reason |
|
||||
| --- | --- | --- |
|
||||
| 2026-08-13 | initial routing filled — lead `grok-4.5`, strong `claude-opus-5`, mid `composer-2.5`, fast `composer-2.5-fast` | `/project-init`; operator accepted suggested split |
|
||||
@@ -1,34 +1,30 @@
|
||||
# Progress board
|
||||
|
||||
> For the owner. What works, how to see it, and what's waiting on you — plain language, no agent jargon. Refreshed at every phase seal and session end. `HANDOFF.md` speaks to the next agent; this page speaks to you.
|
||||
> For the owner. What works, how to see it, and what's waiting on you — plain language.
|
||||
|
||||
**Updated:** 2026-08-12 · **Overall:** v1.1.0 released to `main` (Phase 1 + the 2026-07 fix wave + the Prompt Builder pattern upgrade + CHANGELOG-driven release notes); operating system on the gauntlet-loop/opus kit (2026-08-07 audit revision).
|
||||
**Updated:** 2026-08-13 · **Overall:** Chrome LexAI unchanged; VS Code LexAI v1 ready to try locally
|
||||
|
||||
## What works now
|
||||
|
||||
- The extension itself: selection → floating toolbar → fix/rephrase/shorten/expand/explain/prompt → Replace or Copy; four providers (OpenAI/Anthropic/Groq/OpenRouter); encrypted BYO key; Options with live model listing; 64/64 unit tests, typecheck and build green (2026-08-12).
|
||||
- Prompt Builder now offers 12 named prompting patterns (grouped Direct / Reasoning / Agentic, plus "Auto"), each with a plain-English hint shown under the dropdown — in both the popup's Prompt tab and the in-page "Make Prompt" dialog you get from selecting text. Owner-verified by load-unpacked check.
|
||||
- Patterns like Few-shot Examples and ReAct now produce properly structured output (example blocks, step budgets) without getting cut off — a token-limit bug that truncated longer prompt patterns is fixed.
|
||||
- Any pattern you'd saved before this update carries over automatically — nothing to redo.
|
||||
- Releases now write real release notes: `CHANGELOG.md` tracks what shipped per version, and the Gitea release workflow pulls the matching section into the release body automatically when a version tag is pushed (falls back to a generic body if a section is missing).
|
||||
- The agent operating system: upgraded from the older fable kit — 13 specialists (incl. your custom `lexai-extension-dev`, kept and modernized) + 4 new ones (ux-ui-designer, ux-psychologist, and the fresh-eyes `gauntlet-critic` referee), 12 skills, all your lessons and security-auditor memory preserved. Lead is now `claude --agent opus-orchestrator`.
|
||||
- Chrome extension (existing): select on any page → AI action → Replace/Copy
|
||||
- VS Code extension (new): select in editor → LexAI context menu / Command Palette → selection replaced
|
||||
- Shared LLM core (`src/lib`) used by both; VS Code key lives in Secret Storage
|
||||
|
||||
## See it yourself
|
||||
## See it yourself (VS Code) — about five minutes
|
||||
|
||||
- `npm run build` → `chrome://extensions` → Load unpacked → `.output/chrome-mv3` → select text on any page → "Make Prompt" (or open the extension popup's Prompt tab).
|
||||
- Open `CLAUDE.md` — your repo rules and 9 codebase invariants are carried over intact; the gauntlet protocol is new in §3.
|
||||
1. From repo root: `npm run vscode:install` then `npm run vscode:build`
|
||||
2. Open `packages/vscode` in VS Code/Cursor → Run and Debug → **Run LexAI Extension** (F5), or `npm run vscode:package` and Install from VSIX
|
||||
3. Command Palette → **LexAI: Set API Key** → pick provider → paste key
|
||||
4. Select ≥10 characters in an editor → right-click → **LexAI** → Fix Grammar (or another action)
|
||||
|
||||
## Waiting on you — each item blocks ONLY its own lane
|
||||
## Waiting on you
|
||||
|
||||
| # | Decision | Options (recommended bold) | What it unblocks |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Authorize the live Chrome Web Store publish for v1.1.0: `git tag v1.1.0 && git push origin v1.1.0` fires `deploy-chrome.yml` and publishes live | **tag now** / hold | the store listing goes live on v1.1.0 — nothing else is blocked meanwhile |
|
||||
| 2 | Supply reference-bar artifacts (screenshots/recording of Grammarly or your chosen benchmark → `docs/reference/`) | **Grammarly toolbar + card screenshots** / pick another benchmark / defer gauntlets | UI gauntlet rounds |
|
||||
| 3 | Approve the Replace-reliability site matrix in `docs/REFERENCE_BAR.md` (Gmail, GitHub, X, LinkedIn, Google Docs?, Reddit, Notion) | **approve as listed (Docs out of scope)** / edit the list | the behavioral gauntlet — can start without screenshots |
|
||||
| 4 | Set gauntlet budgets on `docs/GAUNTLET.md` | **modest budget on one part first** / several at once | looping |
|
||||
| 5 | Delete `_to_delete\` in the repo (replaced kit files + transfer archive parked there) | delete now / leave for later | nothing — housekeeping |
|
||||
| 1 | Confirm Cursor mid/strong model picker IDs | **Keep written IDs** / send exact picker strings | reliable Task dispatch for builders/critics |
|
||||
| 2 | Grammarly reference screenshots? | **Later** / capture into `docs/reference/` | Chrome UX gauntlet |
|
||||
| 3 | Publish VS Code extension to Marketplace? | **Stay local for now** / set publisher + publish | public install |
|
||||
|
||||
## Next up — proceeds without you
|
||||
|
||||
- T-01 (`<all_urls>` narrowing) and T-02 (real key encryption) remain the ranked pre-release risks from `HANDOFF.md` — routable to security-auditor + lexai-extension-dev any time.
|
||||
- Nothing about the Prompt Builder update is blocked — it's complete pending item 1's owner check above.
|
||||
- Optional: VS Code Prompt Builder / style quick-pick / Marketplace packaging when you ask
|
||||
|
||||
@@ -1,52 +1,55 @@
|
||||
# Project brief — LexAI
|
||||
# Project brief
|
||||
|
||||
> Source of truth for *what* LexAI is and *why*. Keep it under two screens; link out for detail.
|
||||
> The single source of truth for *what* this app is and *why*. Keep it under two screens; link out for detail.
|
||||
|
||||
## Outcome
|
||||
|
||||
- **One-line product:** A Grammarly-like Chrome extension (Manifest V3) that gives AI writing help — grammar fix, rephrase, shorten, expand, explain — on any webpage, using the user's own LLM API key.
|
||||
- **Measurable outcome:** A user can select text on any page, pick an action from the floating toolbar (or right-click menu / popup), and replace or copy an AI-improved version — with no LexAI backend and no subscription.
|
||||
- **Primary user:** Individuals who already hold an LLM API key (OpenAI / Anthropic / Groq / OpenRouter) and want inline writing assistance without paying a SaaS subscription or sending text through a third-party server.
|
||||
- **Why now:** BYO-key removes the cost and privacy objections to hosted writing assistants; MV3 + WXT makes a lightweight, serverless extension practical.
|
||||
- **One-line product:** LexAI — BYO-LLM writing help as a Chrome MV3 extension and a VS Code extension, sharing one provider/prompt core.
|
||||
- **Measurable outcome:** Select text → AI action (fix / rephrase / shorten / expand / explain / prompt) → replace in place (Chrome also supports Copy), with no LexAI backend and no subscription.
|
||||
- **Primary user:** People who already hold an LLM API key and want inline writing help without a SaaS subscription.
|
||||
- **Why now:** BYO-key writing help without accounts, telemetry, or a LexAI server in the path.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No LexAI backend, account system, or subscription. The extension talks directly to the user's chosen provider.
|
||||
- Not a full document editor; it augments existing page inputs (textarea/input/contenteditable).
|
||||
- No telemetry or transmission of user text anywhere except the user-selected provider endpoint.
|
||||
- Not (yet) streaming, autocomplete, tone profiles, or custom style profiles — those are roadmap.
|
||||
- No LexAI backend, account, or subscription
|
||||
- No telemetry; no transmission of text or API key except to the user’s chosen provider
|
||||
- Not a full document editor
|
||||
- VS Code v1: no floating toolbar, Prompt Builder UI, or Copy As; Firefox/Safari packaging not in scope
|
||||
|
||||
## Acceptance tests
|
||||
|
||||
1. `npm run typecheck` and `npm test -- --run` pass.
|
||||
2. `npm run build` produces a loadable `.output/chrome-mv3/` bundle (~166 KB baseline).
|
||||
3. Loaded unpacked, selecting text on a page shows the toolbar; an action returns a result modal; Replace edits both textarea/input and contenteditable targets.
|
||||
4. API key is stored via the encrypted path (`apiKeyEnc` + `encKey`) and never logged or sent anywhere but the provider endpoint.
|
||||
1. `npm run typecheck` and `npm test -- --run` pass
|
||||
2. `npm run build` yields a loadable `.output/chrome-mv3/`
|
||||
3. Chrome: Replace works on `textarea`/`input` and `contenteditable` (load-unpacked — unit tests do not cover DOM timing)
|
||||
4. Chrome: API key uses the encrypted path (`apiKeyEnc` + `encKey`) and is never logged or exfiltrated
|
||||
5. `npm run vscode:typecheck` and `npm run vscode:build` succeed; VS Code stores the key in Secret Storage and replaces the editor selection
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Stack:** WXT `^0.20` (Vite), React 18 + TypeScript (Options/Popup only), tweetnacl for key encryption. Tailwind is installed but **inactive** — all UI is inline styles.
|
||||
- **Runtime:** Node 22 (CI pins `node:22-bookworm`). `npm install` required before any `npm run *`.
|
||||
- **Security/compliance:** Handles a user secret (LLM API key) and reads page-selected text. Manifest currently requests `<all_urls>` — a Chrome Web Store review risk (see `attacksurface.md`).
|
||||
- **Release:** CI is **Gitea** (`.gitea/workflows/`), not GitHub Actions. Version must match in `package.json` and `wxt.config.ts`; a `v*.*.*` tag deploys to the Chrome Web Store.
|
||||
- **Deadline / milestones:** none fixed; track in Plane (LEXAI) and `docs/TASKS.md`
|
||||
- **Budget / cost ceiling:** user pays their own provider; extension has no LexAI infra bill
|
||||
- **Stack:** WXT ^0.20 + React 18 + TypeScript (Chrome); `packages/vscode` + esbuild (VS Code); shared `src/lib`; Node 22; providers OpenAI / Anthropic / Groq / OpenRouter
|
||||
- **Security / compliance:** never log the key; Chrome encrypts in `chrome.storage.local`; VS Code uses Secret Storage; content/popup must not call providers
|
||||
- **Team / bus factor:** solo owner; operating system in `AGENTS.md` + `CLAUDE.md` + `docs/`
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Role | Who | Decision authority |
|
||||
| --- | --- | --- |
|
||||
| Owner / maintainer | John Kevin Asprec | scope, priorities, release |
|
||||
| Project tracking | Plane (LEXAI project) | https://plane-pro.juankibin.space |
|
||||
| Product owner | John Kevin | scope, priorities, release, reference-bar acceptance |
|
||||
| Eng / harness | this Cursor kit + Claude kit in-repo | architecture proposals, implementation under gates |
|
||||
|
||||
## Unknowns
|
||||
|
||||
- Whether to narrow host permissions to `activeTab`/allowlist before a serious Web Store push (see Decisions + attack surface).
|
||||
- Whether the current tweetnacl approach should be replaced given `encKey` is co-located with the ciphertext (it is obfuscation, not protection).
|
||||
- Reference bar for selection-toolbar/card UX is proposed (Grammarly screenshots into `docs/reference/`) but **not yet concrete** — no gauntlet until artifacts exist
|
||||
- Whether Cursor picker IDs match the profile-table slugs written in `docs/MODEL_ROUTING.md` (live scout verifies `fast`; mid/strong fail loudly on first Task if not)
|
||||
|
||||
## Source of truth
|
||||
|
||||
- **Issue tracker:** Plane — LEXAI project (link above).
|
||||
- **This repo:** entrypoints in `entrypoints/`, shared code in `src/`, tests in `tests/`. `CLAUDE.md` is the working guide for architecture and conventions.
|
||||
- **Issue tracker:** Plane (LEXAI)
|
||||
- **Design / specs:** `docs/` (+ `DESIGN_SYSTEM.md` / `design/` when created)
|
||||
- **This repo:** `AGENTS.md` (Cursor control plane), `CLAUDE.md` (Claude control plane), Chrome in `entrypoints/`, VS Code in `packages/vscode/`, shared core in `src/lib/`, tasks in `docs/TASKS.md`
|
||||
|
||||
---
|
||||
|
||||
*Related: `ARCHITECTURE.md`, `DECISIONS.md`, `TASKS.md` (from RECOMMENDATIONS), `attacksurface.md`, `SELF_MODEL.md`.*
|
||||
*Related: `ARCHITECTURE.md` (how it's built), `DECISIONS.md` (why choices were made), `TASKS.md` (active work), `SELF_MODEL.md` (who the harness is building for).*
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
# Reference bar
|
||||
|
||||
> The concrete quality bar for gauntlet work. Every entry must point at something a referee can open, run, or look at — an adjective is not a bar. Changing a bar mid-gauntlet is an owner decision recorded in `DECISIONS.md`.
|
||||
>
|
||||
> **Seeded 2026-08-06 at the gauntlet-loop/fable upgrade.** This project already has a real bar: the interactive prototype + the Nocturne token authority + per-screen contracts. **Precedence guard (D-2026-07-31-01 lineage):** the prototype is *evidence, never authority* — where the prototype and the recorded spec disagree, `08-development-spec > 04-rules > PRD` wins and the difference is **not** a gap. The referee grades against the spec-corrected prototype.
|
||||
|
||||
Base references: `PROTO = PS Bus Ticketing App - Conductor App.html` (repo root — open in a browser, navigate to the screen) · `TOKENS = docs/06-ui-patterns.md` (Nocturne) · `SPEC = docs/08-development-spec.md` (per-screen contract) · `DESIGN = docs/design/**` (screen specs, where written).
|
||||
## Status
|
||||
|
||||
## Bars by part
|
||||
**Not concrete yet.** No gauntlet round may start until the table below names inspectable artifacts and a comparison method. Decision-ready proposal only.
|
||||
|
||||
One row per screen/flow as it enters a gauntlet — seeded with the screens that already have design artifacts; add rows using the template as work reaches each screen. Budgets live on the `GAUNTLET.md` board.
|
||||
## Bars by part (proposal — awaiting artifacts)
|
||||
|
||||
| Part | Reference artifact(s) | How to compare | Minimum parity |
|
||||
| --- | --- | --- | --- |
|
||||
| Auth screens 1–2 | PROTO auth screens · `docs/design/` auth spec · SPEC §screen criteria | run the app on the 2 GB reference device (or emulator at its profile), screenshot vs PROTO side by side; check tokens vs TOKENS | layout/hierarchy/tokens match the spec-corrected prototype; per-screen SPEC criteria pass |
|
||||
| Screen 06 — discount capture (dual-photo) | PROTO screen 06 · `docs/design/` screen-06 spec · SPEC criteria | walk the capture flow on-device; screenshot each state | every state (capture, retake, proof review) present and one-handed operable; ≥ 48 dp targets |
|
||||
| Screen 11 — printer setup | PROTO screen 11 · `docs/design/` screen-11 spec | walk pairing/test-print flow (or its no-hardware stub — see orchestrator memory: no printer hardware) | states + error paths match; no-hardware path explicit, never silent |
|
||||
| P10 — prepaid booking / QR | PROTO P10 · `docs/design/` P10 spec · SPEC criteria | walk the flow offline; screenshot | offline-first behavior + states match the spec-corrected prototype |
|
||||
| [next screen] | PROTO screen NN · `docs/design/` spec if present · SPEC criteria | on-device screenshot side-by-side + flow walk | [what must match] |
|
||||
|
||||
Behavioral bars (not screenshots): the ≤ 20 s record-a-passenger contract (stopwatch on the reference device), 7-day-offline invariants (A-1…A-6), and the `TC-*` tables in `docs/09-test-plan.md` — these are already acceptance tests; the gauntlet adds the visual/UX parity layer on top, it does not replace them.
|
||||
| Selection toolbar + result card | Grammarly selection-toolbar / card UX screenshots under `docs/reference/` (not yet captured) | side-by-side render of LexAI floating toolbar + result modal vs screenshots | same job in similar steps: appear on selection, choose action, see result, Replace/Copy without fighting host-page UI |
|
||||
|
||||
## Reference sources
|
||||
|
||||
- `PS Bus Ticketing App - Conductor App.html` — interactive prototype (root)
|
||||
- `docs/06-ui-patterns.md` — Nocturne tokens/components (authority for visual language)
|
||||
- `docs/design/**` — written screen specs (authority over the prototype)
|
||||
- `docs/08-development-spec.md` — per-screen acceptance criteria
|
||||
- Intended: `docs/reference/` (screenshots / short recordings of Grammarly’s selection toolbar and result card)
|
||||
- Until those files exist, treat the bar as empty for gauntlet purposes
|
||||
|
||||
## Out of scope for the bar
|
||||
|
||||
- Anything the recorded spec has changed from the prototype (spec wins; log the delta as evidence, not a gap).
|
||||
- Server/back-office UI (contract-only, `docs/07-api-contract.md`), iOS, passenger-facing surfaces.
|
||||
- Full Grammarly editor / browser-wide rewrite suite
|
||||
- Grammarly account, subscription, or cloud features LexAI deliberately excludes
|
||||
- Pixel-perfect brand clone (interaction parity, not visual plagiarism)
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
# Self-model — LexAI
|
||||
# Self-model
|
||||
|
||||
> What the harness believes about the operator and this project. Kept honest by `self-model-audit`. No secrets or sensitive personal data.
|
||||
> What the harness believes about the operator and the project it serves. The point is a system that models *who you are now* and *what this project actually is* — not a stale or aspirational version. Kept honest by the `self-model-audit` skill. Contains no secrets or sensitive personal data.
|
||||
|
||||
## Operator
|
||||
|
||||
- **Who I'm building for:** John Kevin Asprec — owner/maintainer of LexAI.
|
||||
- **Working style:** ships in focused phases (Phase 1 delivered 7 workitems to a deadline); values concise, direct output over verbose explanation; comfortable with the code and the toolchain.
|
||||
- **Communication preferences:** concise and direct; minimal formatting; prefers the point over the preamble.
|
||||
- **Technical depth:** high — WXT/MV3, TypeScript, React, CI/CD. Wants surgical diffs and real verification, not hand-holding.
|
||||
- **Decision authority kept:** manifest permission changes, key-handling changes, releases (version bump + `v*.*.*` tag), and anything touching the Web Store listing.
|
||||
- **Who I'm building for:** [name / role, and the context they work in]
|
||||
- **Working style:** [how they like to work — concise vs. detailed, ask-first vs. act, review depth]
|
||||
- **Communication preferences:** [tone, formatting, length — mirror project/user instructions]
|
||||
- **Technical depth / stack fluency:** [what they know deeply vs. want handled for them]
|
||||
- **Decision authority they keep vs. delegate:** [what always needs their sign-off]
|
||||
|
||||
## Project intent (the real one)
|
||||
|
||||
- **Optimizing for:** a genuinely useful, private, subscription-free writing assistant that runs on the user's own key — shipped to the Chrome Web Store.
|
||||
- **What "good" means here:** typecheck + tests + build green, real-page behavior verified, minimal diffs, invariants preserved (see LESSONS_LEARNED), key never exposed.
|
||||
- **Non-negotiable constraints:** no backend; never transmit user text or key anywhere but the chosen provider; inline styles until PostCSS is deliberately wired.
|
||||
- **What this project is actually optimizing for:** [the outcome that matters, in their words]
|
||||
- **What "done" and "good" mean here:** [their real bar, not a generic one]
|
||||
- **Constraints that are non-negotiable:** [time, cost, stack, values]
|
||||
|
||||
## Voice (if the harness writes as the operator)
|
||||
## Voice (if the harness writes as them)
|
||||
|
||||
- **Sounds like:** direct, technical, no filler.
|
||||
- **Never sounds like:** marketing fluff, over-hedged, or padded with obvious restatement.
|
||||
- **Sounds like:** [characteristic phrasing, structure, do's]
|
||||
- **Never sounds like:** [anti-patterns, words/tics to avoid]
|
||||
|
||||
## Known drift risks
|
||||
|
||||
- "API key is encrypted" — the current tweetnacl approach is obfuscation, not protection; don't let docs or UI over-claim (see attacksurface + TASKS #2).
|
||||
- Phase-1 framing may go stale as recommendations land; re-read `TASKS.md` state before assuming what's done.
|
||||
- Tailwind is present but inactive — don't infer a Tailwind workflow from its presence in devDependencies.
|
||||
Places the model is likely to go stale or wrong. The audit checks these first.
|
||||
|
||||
- [belief that was true early but may have changed]
|
||||
- [aspirational goal the system optimizes for that recent behavior contradicts]
|
||||
- [preference stated once and never re-confirmed]
|
||||
|
||||
## Change log
|
||||
|
||||
| Date | What changed in this model | Evidence |
|
||||
| --- | --- | --- |
|
||||
| 2026-07-15 | Initial capture from README, CLAUDE.md, PHASE1_SUMMARY, RECOMMENDATIONS | repo docs |
|
||||
| [date] | [initial capture] | [source] |
|
||||
|
||||
---
|
||||
|
||||
*Update via `self-model-audit` when behavior and this file diverge. Never store credentials, financial/health data, or anything not agreed to persist.*
|
||||
*Update via `self-model-audit` when behavior and this file diverge. Never store credentials, financial data, health data, or anything the operator hasn't agreed to persist.*
|
||||
|
||||
@@ -1,76 +1,39 @@
|
||||
# Tasks — LexAI
|
||||
# Tasks
|
||||
|
||||
> Active task contracts, derived from `RECOMMENDATIONS.md` (full read 2026-07-13). Task numbers match the recommendation numbers for traceability. Completed contracts move to `HANDOFF.md`; durable choices move to `DECISIONS.md`.
|
||||
> Active task contracts and their dependencies. This is the working queue the lead routes from — not a backlog dump. Keep it to what's in flight or next. Completed contracts move to `HANDOFF.md`; durable decisions move to `DECISIONS.md`.
|
||||
|
||||
## Suggested order (from RECOMMENDATIONS)
|
||||
## Active
|
||||
|
||||
Quick wins first: **T-03, T-06, T-09, T-11, T-15, T-16** (all small, mostly independent). Then structural refactors **T-04, T-05, T-08**. Do **T-01 / T-02** (permissions + key story) before any serious Chrome Web Store push. Save **T-10, T-12, T-13** for a focused Phase 2.
|
||||
_No active task contracts._
|
||||
|
||||
## Active (next up — fully specified)
|
||||
## Done (recent)
|
||||
|
||||
### T-03 — Gate debug logging behind a DEV flag
|
||||
|
||||
- **Status:** ready · **Owner:** lexai-extension-dev · **Effort:** S
|
||||
- **Goal:** Stop leaking selection text/element values to the host-page console in production.
|
||||
- **In scope:** `entrypoints/content.ts` `[LexAI …]` logs (captureForButton, Replace paths).
|
||||
- **Out of scope:** removing logs entirely; other files.
|
||||
- **Constraints:** keep logs available in dev; no behavior change.
|
||||
- **Deliverable:** logs wrapped in `import.meta.env.DEV` (or a `__DEV__` guard).
|
||||
- **Verification:** `npm run build` then grep the built `content.js` for `[LexAI` — none present; `npm run dev` still logs.
|
||||
- **Stop condition:** production bundle has no LexAI console output.
|
||||
|
||||
### T-06 — Remove or wire dead dependencies
|
||||
|
||||
- **Status:** ready · **Owner:** builder · **Effort:** S
|
||||
- **Goal:** Drop confusion and install weight from unused deps.
|
||||
- **In scope:** `zustand` (no store exists), `tailwindcss` + `autoprefixer` (inactive).
|
||||
- **Constraints:** if kept, they must be actually wired; otherwise remove from `package.json`.
|
||||
- **Deliverable:** updated `package.json` + lockfile, or a documented decision to wire them.
|
||||
- **Verification:** `npm install` && `npm run typecheck` && `npm run build` clean.
|
||||
- **Stop condition:** no installed-but-unused runtime deps remain unexplained.
|
||||
|
||||
### T-09 — Fix or quarantine the e2e suite
|
||||
|
||||
- **Status:** ready · **Owner:** lexai-extension-dev · **Effort:** S
|
||||
- **Goal:** Make CI green mean something.
|
||||
- **In scope:** `tests/e2e/extension.test.ts` hard-coded `chrome-extension://[EXTENSION_ID]/…`.
|
||||
- **Deliverable:** resolve the extension ID at runtime (from the service-worker target), or `.skip` the suite with a TODO until fixed.
|
||||
- **Verification:** `npm run build` && `npm run test:e2e` — passes or is cleanly skipped, not failing.
|
||||
- **Stop condition:** e2e no longer red for the placeholder reason.
|
||||
|
||||
### T-15 / T-16 — Pin toolchain & single-source the version
|
||||
|
||||
- **Status:** ready · **Owner:** builder · **Effort:** S
|
||||
- **Goal:** Prevent `npm run *` failing with no version guard, and prevent shipping mismatched versions.
|
||||
- **In scope:** add `engines`/confirm `.nvmrc` (Node 22) + `packageManager` field; make `wxt.config.ts` read `version` from `package.json` (or a bump script that writes both).
|
||||
- **Verification:** bump once; confirm `package.json` and the built `manifest.json` version match.
|
||||
- **Stop condition:** version is a single edit; toolchain pinned to CI's Node 22.
|
||||
|
||||
## Backlog (ready, from RECOMMENDATIONS)
|
||||
|
||||
| ID | Task | Theme | Effort |
|
||||
| --- | --- | --- | --- |
|
||||
| T-01 | Narrow host permissions from `<all_urls>` (activeTab / allowlist) — do before CWS push | Security | M |
|
||||
| T-02 | Fix the key story: don't co-locate `encKey` with ciphertext; be honest in UI ("stored locally, obscured") | Security | M |
|
||||
| T-04 | Collapse duplicated provider layer into `callProvider(config, messages/system, text)` + per-provider adapter | Maintainability | M |
|
||||
| T-05 | Extract shared theme/styles into `src/ui/theme.ts` (palette used across content/Options/Popup) | Maintainability | M |
|
||||
| T-07 | Centralize provider/model/endpoint config in one shared module (Options + background drift) | Maintainability | S |
|
||||
| T-08 | Unit-test real code: extract `getSystemPrompt`, `decryptApiKey`, provider router; test prompt normalization, encrypt→decrypt round-trip, routing, error extraction | Testing | M |
|
||||
| T-10 | Add content-script DOM test for selection→snapshot→replace (textarea + contenteditable) | Testing | L |
|
||||
| T-11 | Make `max_tokens` adaptive (scale with input length or expose in settings) — currently hard-coded 1024 | UX | S |
|
||||
| T-12 | Add response streaming into the modal | UX | L |
|
||||
| T-13 | Accessibility: aria-labels, focus management, focus trap on modal, keyboard nav | UX | M |
|
||||
| T-14 | React error boundaries + graceful storage-failure handling on Options/Popup | UX | S |
|
||||
| T-17 | CI: use checked-out workspace instead of `git clone` into /tmp; stop disabling TLS verification | Build/release | S |
|
||||
- **T-VSCODE-01** — VS Code LexAI v1 (`packages/vscode`): native commands/menus, Secret Storage, shared `@lib` via esbuild; verified with `vscode:typecheck` + `vscode:build` + root typecheck/tests (2026-08-13).
|
||||
|
||||
## Task contract format
|
||||
|
||||
```markdown
|
||||
### T-NN — [verb + concrete deliverable]
|
||||
- **Status:** ready | in progress | blocked | in review | done · **Owner:** [agent] · **Effort:** S/M/L
|
||||
- **Goal / In scope / Out of scope / Constraints / Deliverable / Verification / Stop condition**
|
||||
|
||||
- **Status:** ready | in progress | blocked | in review | done
|
||||
- **Owner:** [agent or person — one owner per output]
|
||||
- **Goal:** [one sentence]
|
||||
- **In scope:** [paths, systems, or requirements]
|
||||
- **Out of scope:** [explicit exclusions]
|
||||
- **Inputs:** [file paths, links, commands, facts]
|
||||
- **Constraints:** [compatibility, security, time, style]
|
||||
- **Deliverable:** [file(s), patch, report, decision]
|
||||
- **Verification:** [exact commands / observable checks]
|
||||
- **Stop condition:** [when to return]
|
||||
- **Escalate if:** [missing authority, ambiguity, destructive action, blocked dependency]
|
||||
- **Blocked by / blocks:** [T-NN dependencies]
|
||||
```
|
||||
|
||||
## Done (recent)
|
||||
## Dependencies
|
||||
|
||||
- Phase 1 (2026-03-06): 7 workitems — WXT setup, selection detection, floating toolbar, SW LLM proxy, OpenAI+Anthropic+Groq+OpenRouter providers, Options page, result modal with Replace. See `PHASE1_SUMMARY.md`.
|
||||
Track ordering only when it matters. Prefer independent, parallelizable contracts with non-overlapping file ownership.
|
||||
|
||||
```text
|
||||
T-01 ──> T-03
|
||||
T-02 ──> T-03 (T-03 integrates both; single owner)
|
||||
```
|
||||
|
||||
@@ -1,48 +1,31 @@
|
||||
# Attack surface — LexAI
|
||||
# Attack surface
|
||||
|
||||
> Living inventory of LexAI's exposure. Updated whenever manifest/permissions, storage, or provider handling changes, and before any Chrome Web Store push. Contains **no secrets** — only references. Maintained via the `attack-surface` skill; security review via `security-auditor`.
|
||||
> Living inventory of everything this project has deployed and its exposure. Updated whenever infrastructure changes and before each security review, via the `attack-surface` skill. Contains **no secrets** — only references to where secrets live.
|
||||
|
||||
## Assets
|
||||
|
||||
| Asset | Type | Tech | Hosted | Auth in | Exposure | Defenses | Review cadence |
|
||||
| Asset | Type | Tech / version | Hosted | Auth in | Exposure | Defenses | Review cadence |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| Content script | injected code | WXT/TS | client | n/a | **`<all_urls>`, all frames** | `data-lexai` guard; inline styles; max z-index | every manifest/permission change |
|
||||
| Background service worker | LLM proxy | WXT/TS | client | user's provider key | reachable only via extension messages | key never logged; provider-only fetch | every key/provider change |
|
||||
| `chrome.storage.local` | local store | Chrome | client | extension-only | holds `apiKeyEnc`+`encKey` (+ legacy plaintext `apiKey`) | tweetnacl secretbox (see weakness) | every key-handling change |
|
||||
| Provider endpoints | 3rd-party API | HTTPS | OpenAI/Anthropic/Groq/OpenRouter | user's API key | outbound only, user-initiated | HTTPS; key in header only | on provider add/change |
|
||||
| Gitea CI | pipeline | Gitea workflows | self/3p | `GITEATOKEN`, `CWS_*`, `TELEGRAM_*` | build + publish to CWS | secrets in Gitea; **but** `http.sslVerify false` (see gap) | on workflow change |
|
||||
| _[none mapped yet]_ | | | | | | | |
|
||||
|
||||
## Per-asset notes
|
||||
|
||||
### Content script — `<all_urls>`
|
||||
- **Exposure:** injects into every frame of every site, including banking, email, internal apps. Biggest privacy surface and the #1 Chrome Web Store review slowdown.
|
||||
- **Mitigation (proposed):** narrow to `activeTab` + on-demand injection, or a user allowlist (TASKS #1 / D-PROPOSED). Decide before a serious CWS push.
|
||||
<!--
|
||||
### [asset name]
|
||||
- **Common misconfigs / CVE classes:** [platform-specific]
|
||||
- **Known exposure:** [what an attacker reaches, and from where]
|
||||
- **Secrets location:** [vault / secret-manager path — never the value]
|
||||
- **Last reviewed:** [date + result]
|
||||
-->
|
||||
|
||||
### API-key storage — obfuscation, not protection
|
||||
- **Exposure:** `encKey` is stored in `chrome.storage.local` next to `apiKeyEnc`; anyone who can read storage can decrypt. The "encrypted" claim over-promises.
|
||||
- **Secrets location:** `chrome.storage.local` (user's own browser). Never in repo, never logged.
|
||||
- **Mitigation (proposed):** derive the key from `chrome.storage.session` / WebCrypto / a passphrase, and describe it honestly in the UI (TASKS #2 / D-...-06).
|
||||
## Model / harness input surface
|
||||
|
||||
### Debug logging leak
|
||||
- **Exposure:** `content.ts` logs selection text and element values to the host-page console — readable by the page.
|
||||
- **Mitigation:** gate behind `import.meta.env.DEV` (TASKS #3).
|
||||
Injection-relevant inputs to model calls (kept in sync by the `prompt-injection-audit` skill).
|
||||
|
||||
### CI TLS verification disabled
|
||||
- **Exposure:** both Gitea workflows set `http.sslVerify false` and `git clone` into `/tmp`.
|
||||
- **Mitigation:** use the checked-out workspace and restore TLS verification (TASKS #17).
|
||||
|
||||
## Model / harness input surface (prompt-injection)
|
||||
|
||||
The extension sends **user-selected page text** to the chosen LLM with a fixed system prompt. Page-controlled text is untrusted input to the provider call.
|
||||
|
||||
| Input avenue | Consuming model | Reachable actions | Exposure | Defense in place |
|
||||
| Input avenue | Consuming model | Reachable tools | Exposure | Defense in place |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Selected page text → `ANALYZE_TEXT` | user's provider | returns text shown in modal; user chooses Replace/Copy | injected instructions in page text could steer the model's output | user reviews output before Replace; no tool-calling; output is inert text |
|
||||
|
||||
- **Note:** exposure is low because the model output is inert (no tool execution) and the user gates Replace. Run `prompt-injection-audit` if LexAI ever adds auto-apply, tool use, or agentic actions.
|
||||
| _[e.g. web fetch results]_ | | | | |
|
||||
|
||||
## Gaps / unknowns
|
||||
|
||||
- Host-permission narrowing not yet decided (TASKS #1).
|
||||
- Key-derivation redesign not yet done (TASKS #2).
|
||||
- No automated check that production builds exclude debug logs (TASKS #3).
|
||||
- Inventory not yet populated. Run the `attack-surface` skill once real infrastructure exists, and `prompt-injection-audit` once the app makes model-driven tool calls.
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
```markdown
|
||||
From a systems and software engineering perspective, prompt patterns and agentic loops are structured control flow mechanisms built on top of autoregressive transformer models.
|
||||
|
||||
Below is a detailed technical breakdown of these patterns, covering their state transitions, context memory management, prompt schemas, and failure modes.
|
||||
|
||||
---
|
||||
|
||||
## 1. Deterministic & Context-Shaping Patterns
|
||||
|
||||
These patterns operate at the inference step level to constrain token generation probabilities and enforce structural invariants.
|
||||
|
||||
### Role & System Conditioning (Logit Shaping)
|
||||
* **Mechanism:** Injects instructions directly into the system message block, modifying the baseline attention weights across all subsequent user/assistant turns. It acts as an inductive bias, shifting the probability distribution of generated tokens toward domain-specific terminologies and structured logic.
|
||||
* **Prompt Schema:**
|
||||
```text
|
||||
<system_instruction>
|
||||
ROLE: Senior Distributed Systems Architect.
|
||||
DOMAIN: Real-time event-driven infrastructure, gRPC, distributed consensus (Raft/Paxos).
|
||||
INVARIANT: Prioritize zero-data-loss guarantees over minimal latency. Reject eventual consistency unless explicitly requested.
|
||||
OUTPUT_FORMAT: Technical specification markdown with formal system invariants.
|
||||
</system_instruction>
|
||||
```
|
||||
* **Failure Modes & Mitigations:** *Context Decay* (the model forgets constraints in long turns). Mitigate by placing critical invariant rules at the very end of the system block or repeating constraints in system system-reinforcement flags.
|
||||
|
||||
### Few-Shot Delimiter Scaffolding
|
||||
* **Mechanism:** Imprints input-output mapping patterns directly into the model’s Key-Value (KV) cache. Utilizing explicit XML or structural delimiters prevents token boundary confusion during multi-turn parsing.
|
||||
* **Prompt Schema:**
|
||||
```xml
|
||||
<system>Extract operational state from syslog streams.</system>
|
||||
|
||||
<example>
|
||||
<input>2026-08-07T08:12:01Z node-04 dockerd[1042]: Error: OOMKilled process 8841</input>
|
||||
<output>{"node": "node-04", "event": "OOMKilled", "pid": 8841, "severity": "CRITICAL"}</output>
|
||||
</example>
|
||||
|
||||
<target>
|
||||
<input>2026-08-07T08:14:22Z node-01 kernel: [44211.2] Out of memory: Kill process 1204 (postgres)</input>
|
||||
<output>
|
||||
```
|
||||
* **Failure Modes:** Recency/label bias (overweighting the last example's exact values). Keep examples structurally diverse and balanced across edge cases.
|
||||
|
||||
---
|
||||
|
||||
## 2. Multi-Step Inference & Search Graph Patterns
|
||||
|
||||
These frameworks alter the model’s internal computation path by generating intermediate reasoning tokens before emitting the target response.
|
||||
|
||||
### Chain-of-Thought (CoT) & Plan-and-Solve
|
||||
* **Mechanism:** Forces auto-regressive decoding to populate the context buffer with intermediate rationale steps ($z_1, z_2, \dots, z_n$) prior to predicting the target output ($y$). Mathematically:
|
||||
$$P(y \mid x) = \sum_z P(y \mid x, z) P(z \mid x)$$
|
||||
* **Execution Protocol:**
|
||||
```text
|
||||
Perform the following analysis in two explicit, separated phases:
|
||||
PHASE 1 (REASONING_BUFFER):
|
||||
- Identify state invariants and potential race conditions.
|
||||
- Draft intermediate computational dependencies.
|
||||
- Evaluate step-by-step edge cases.
|
||||
|
||||
PHASE 2 (EXECUTION_OUTPUT):
|
||||
- Provide the final production-ready implementation wrapped in ```json tags.
|
||||
```
|
||||
* **When to Use:** Algorithmic execution, mathematical logic, complex SQL/query optimization.
|
||||
|
||||
### Tree-of-Thoughts (ToT) / Graph-of-Thoughts (GoT)
|
||||
* **Mechanism:** Combines LLM generation with classical state-space search algorithms (Breadth-First Search, Depth-First Search, or $A^*$). The LLM acts both as a *Thought Generator* ($S_{t+1} \sim G(S_t)$) and a *State Evaluator* ($V(S_t) \in [0, 1]$).
|
||||
|
||||
```text
|
||||
[Root State: Initial Prompt]
|
||||
/ \
|
||||
[Thought A] [Thought B]
|
||||
v = 0.8 v = 0.2 (Pruned)
|
||||
/ \
|
||||
[Thought A1] [Thought A2]
|
||||
v = 0.95 v = 0.4
|
||||
```
|
||||
|
||||
* **Execution Pseudocode:**
|
||||
```python
|
||||
def tree_of_thoughts_search(root_prompt, beam_width=3, max_depth=4):
|
||||
current_states = [root_prompt]
|
||||
for depth in range(max_depth):
|
||||
candidates = []
|
||||
for state in current_states:
|
||||
# 1. Expand candidate branches via LLM
|
||||
branches = llm_generate_branches(state, num_samples=3)
|
||||
# 2. Evaluate state heuristic score V(s) via LLM
|
||||
scores = [llm_evaluate_state(branch) for branch in branches]
|
||||
candidates.extend(zip(branches, scores))
|
||||
|
||||
# 3. Prune low-scoring branches (Beam Search)
|
||||
candidates.sort(key=lambda x: x[1], reverse=True)
|
||||
current_states = [branch for branch, score in candidates[:beam_width]]
|
||||
return current_states[0] # Best evaluated path
|
||||
```
|
||||
* **When to Use:** Strategic planning, complex refactoring across multiple files, architecture synthesis.
|
||||
|
||||
---
|
||||
|
||||
## 3. Agentic Loops & State-Machine Architectures
|
||||
|
||||
Agentic frameworks wrap the LLM inside an external, deterministic control loop (e.g., Python/Go runtime, orchestration engines like OpenClaw, or custom middleware).
|
||||
|
||||
### ReAct (Reasoning + Action Protocol)
|
||||
* **State Machine:**
|
||||
$$\text{State}_t \rightarrow \text{Thought}_t \rightarrow \text{Action}_t(\text{Tool Call}) \rightarrow \text{Observation}_t \rightarrow \text{State}_{t+1}$$
|
||||
|
||||
```text
|
||||
+--------------+ +-------------------+ +-----------------+
|
||||
| LLM Engine | ----> | Action (Tool Call)| ----> | Execution Runtime|
|
||||
+--------------+ +-------------------+ +-----------------+
|
||||
^ |
|
||||
|-------------- Observation (Payload) <--------------+
|
||||
```
|
||||
|
||||
* **Prompt Engine Specification:**
|
||||
```text
|
||||
You operate in a strict execution loop. Available Tools: [exec_bash, query_sql, HTTP_GET].
|
||||
|
||||
Use the following format strictly:
|
||||
Thought: <Logical about current reasoning state>
|
||||
Action: <Tool_Name>(<JSON_Arguments>)
|
||||
Observation: <Result by environment injected>
|
||||
|
||||
Loop terminates ONLY when you emit:
|
||||
Final Answer: <Summary of outcome>
|
||||
```
|
||||
* **Failure Modes:** Infinite loops caused by unhandled tool errors.
|
||||
* **Mitigation:** Enforce hard step budgets (`max_iterations = 10`) and circuit breakers on duplicate tool signatures.
|
||||
|
||||
### Plan-Execute-Verify (PEV) with Re-Planning
|
||||
* **Mechanism:** Decouples task breakdown from task execution. The planner generates a Directed Acyclic Graph (DAG) of sub-tasks. An execution loop steps through nodes sequentially, running validation assertions after each step. If a step fails, control yields back to a Re-Planner node to mutate the remaining DAG.
|
||||
|
||||
```text
|
||||
+--------------+
|
||||
| Generate DAG |
|
||||
+--------------+
|
||||
|
|
||||
v
|
||||
+-----------------+
|
||||
+->| Execute Node N |
|
||||
| +-----------------+
|
||||
| |
|
||||
| v
|
||||
| +-----------------+ FAIL +---------------+
|
||||
| | Assert / Verify | -------------> | Re-Plan DAG | --+
|
||||
| +-----------------+ +---------------+ |
|
||||
| | PASS |
|
||||
| v |
|
||||
| [More Nodes Remaining?] --YES--------------------------+
|
||||
| | NO
|
||||
| v
|
||||
| +-----------------+
|
||||
+--| Final Outcome |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
### The Gauntlet Loop (Adversarial Multi-Agent Architecture)
|
||||
* **Mechanism:** Implements a strict **Maker-Checker Isolation Model**. The Builder Agent generates code/artifacts. A *blind* Critic Agent—instantiated in a zero-history, isolated context window—evaluates the output against a hard reference standard or test harness.
|
||||
|
||||
```text
|
||||
+------------------+ +--------------------+
|
||||
| Builder Agent | --- Generates ---> | Artifact Payload |
|
||||
| (Context Window) | +--------------------+
|
||||
+------------------+ |
|
||||
^ v
|
||||
| +--------------------+
|
||||
|-- Injects Actionable Feedback| Judge Agent |
|
||||
| (No Excuses Allowed) | (Isolated Context) |
|
||||
| +--------------------+
|
||||
| |
|
||||
+<-- [Fails Reference Standard] ---------+
|
||||
```
|
||||
|
||||
* **System Architecture Protocol:**
|
||||
```python
|
||||
def gauntlet_loop(task_spec, reference_standard, max_gauntlet_runs=5):
|
||||
builder_context = init_builder_context(task_spec)
|
||||
|
||||
for iteration in range(max_gauntlet_runs):
|
||||
# Step 1: Builder generates artifact
|
||||
artifact = builder_agent.run(builder_context)
|
||||
|
||||
# Step 2: Instantiate Judge in FRESH context window (Zero memory leak)
|
||||
judge_prompt = f"""
|
||||
TASK: Compare Artifact against Reference Standard.
|
||||
REFERENCE: {reference_standard}
|
||||
ARTIFACT TO EVALUATE: {artifact}
|
||||
|
||||
OUTPUT RULES:
|
||||
1. Determine if Artifact >= Reference Standard in quality/correctness.
|
||||
2. If FAIL, list the single most critical structural deficiency. Do not offer encouragement.
|
||||
FORMAT: STATUS: [PASS|FAIL] | FEEDBACK: <concise directive>
|
||||
"""
|
||||
|
||||
verdict = judge_agent.run_fresh_context(judge_prompt)
|
||||
|
||||
if verdict.status == "PASS":
|
||||
return artifact
|
||||
|
||||
# Step 3: Append harsh feedback to builder context
|
||||
builder_context.append_user_message(f"GAUNTLET REJECTION: {verdict.feedback}")
|
||||
|
||||
raise MaximumGauntletDepthExceeded("Quality threshold not met within limit.")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Technical Summary Matrix
|
||||
|
||||
| Pattern / Loop Style | Latency Cost | Context Consumption | Determinism | Best Architectural Use Case |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **Few-Shot / Schema** | Low ($O(1)$) | Low | High | API Payload Generation, Format Standardization |
|
||||
| **Chain-of-Thought** | Medium ($O(k)$) | Medium | Medium | Intermediate Math, Single-Query Logic Tracing |
|
||||
| **Tree-of-Thoughts** | High ($O(b^d)$) | High | High | Complex Codebase Refactoring, Architecture Search |
|
||||
| **ReAct Agent** | Dynamic | Medium-High | Medium | Runtime API Orchestration, Infrastructure Ops |
|
||||
| **Plan-Execute-Verify** | High | High | High | Multi-Step Migration Pipelines, CI/CD Automation |
|
||||
| **Gauntlet Loop** | Very High | Extreme | Maximum | Autonomous End-to-End System/Software Synthesis |
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user