feat(cli): add initial CLI implementation with argument parsing and prompt handling
- Created package.json and package-lock.json for CLI package. - Implemented argument parsing in args.ts to handle various flags and commands. - Developed main CLI logic in cli.ts to execute commands and handle errors. - Added configuration loading from a JSON file in config.ts, with environment variable support. - Implemented prompt resolution and provider interaction in prompt.ts. - Added usage documentation for the CLI. - Configured TypeScript settings in tsconfig.json for the CLI package. - Updated README in vscode package to reflect the new CLI functionality. - Refactored root tsconfig.json to streamline project structure.
This commit is contained in:
71
CLAUDE.md
71
CLAUDE.md
@@ -12,15 +12,15 @@
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Project | LexAI — Grammarly-like Chrome extension (Manifest V3), BYO-LLM-key |
|
||||
| Outcome | Select text on any page → AI action (fix/rephrase/shorten/expand/explain/prompt) → Replace or Copy, with no backend and no subscription |
|
||||
| Project | LexAI — BYO-LLM Chrome MV3 + VS Code + CLI Prompt Builder |
|
||||
| Outcome | Select text/code → AI help (writing actions / Code Assist / prompt engineer) with no LexAI backend or subscription |
|
||||
| Non-goals | no backend/account/subscription; no telemetry; no transmission of text/key except to the user's chosen provider; not a full editor |
|
||||
| Primary user | people who hold an LLM API key and want inline writing help without a SaaS subscription |
|
||||
| Acceptance tests | `npm run typecheck` + `npm test -- --run` pass; `npm run build` yields a loadable `.output/chrome-mv3/`; Replace works on textarea/input and contenteditable; key uses the encrypted path and is never logged/exfiltrated |
|
||||
| Constraints | WXT ^0.20 + React 18 + TS; Node 22; Tailwind inactive (inline styles); `<all_urls>` today; Gitea CI + Chrome Web Store |
|
||||
| Source of truth | this file + `docs/`; issue tracking in Plane (LEXAI); task list `docs/TASKS.md` (derived from `RECOMMENDATIONS.md`) |
|
||||
| Reference bar | not yet supplied — decision-ready proposals in `docs/REFERENCE_BAR.md` (candidate: Grammarly's selection-toolbar/card UX captured as screenshots into `docs/reference/`); a gauntlet does not start until the bar is concrete |
|
||||
| Commands | `install: npm install` · `test: npm test -- --run` · `lint: npm run typecheck` · `build: npm run build` · `zip: npm run zip` · e2e: `npm run test:e2e` (after build) |
|
||||
| Primary user | people who hold an LLM API key and want inline writing / prompt help without a SaaS subscription |
|
||||
| Acceptance tests | `npm run chrome:typecheck` + `chrome:test`; Chrome build → `packages/chrome/.output/chrome-mv3/`; `vscode:typecheck` + `vscode:build`; `cli:typecheck` + `cli:build`; key never logged/exfiltrated |
|
||||
| Constraints | Monorepo `packages/{chrome,vscode,cli}` + `src/lib`; WXT ^0.20 + React 18; Node 22; Gitea CI + Chrome Web Store |
|
||||
| Source of truth | this file + `docs/` + root `README.md`; Plane (LEXAI); `docs/TASKS.md` |
|
||||
| Reference bar | decision-ready proposals in `docs/REFERENCE_BAR.md` |
|
||||
| Commands | `install:all` · `chrome:dev|build|test|typecheck|zip` · `vscode:*` · `cli:*` |
|
||||
|
||||
### Definition of done
|
||||
|
||||
@@ -32,57 +32,36 @@ Work is done only when the requested outcome is implemented, relevant checks pas
|
||||
|
||||
A Grammarly-like **Chrome Extension (Manifest V3)** providing AI writing assistance (grammar fix, rephrase, shorten, expand, explain) on any webpage. Users bring **their own LLM API key** — there is no LexAI backend. The service worker calls the user's chosen provider directly.
|
||||
|
||||
- **Providers:** OpenAI, Anthropic, Groq, OpenRouter (configured in `entrypoints/background.ts`).
|
||||
- **No subscription, no server.** The API key lives encrypted in `chrome.storage.local`.
|
||||
- **Providers:** OpenAI, Anthropic, Groq, OpenRouter (`packages/chrome/entrypoints/background.ts`).
|
||||
- **Also:** VS Code twin (`packages/vscode`), CLI Prompt Builder (`packages/cli`), shared `src/lib`.
|
||||
- **No subscription, no server.** Chrome key encrypted in `chrome.storage.local`.
|
||||
|
||||
#### Tech stack
|
||||
|
||||
- **WXT** `^0.20` — extension framework (wraps Vite). Entrypoints in `entrypoints/`.
|
||||
- **WXT** `^0.20` — Chrome package under `packages/chrome/` (entrypoints there).
|
||||
- **React 18** + TypeScript — Options and Popup pages only.
|
||||
- **Zustand** — a dependency, but state is currently local; not yet wired into a store.
|
||||
- **tweetnacl** / **tweetnacl-util** — `secretbox` symmetric encryption for the API key.
|
||||
- **Vitest** (jsdom) unit tests, **Playwright** e2e.
|
||||
- **Tailwind** is in devDependencies but **not active** — all UI uses inline style objects (WXT PostCSS was never wired up). Do not assume Tailwind classes work.
|
||||
- **tweetnacl** — `secretbox` for the Chrome API key.
|
||||
- **Vitest** / **Playwright** — configured in `packages/chrome`.
|
||||
- **Tailwind** inactive — inline styles only.
|
||||
|
||||
#### Commands
|
||||
|
||||
```bash
|
||||
npm install # first-time setup (node_modules gitignored; not present by default)
|
||||
npm run dev # WXT dev server with hot reload
|
||||
npm run build # production build → .output/chrome-mv3/
|
||||
npm run zip # package for Chrome Web Store
|
||||
npm test # vitest (add `-- --run` for one-shot, non-watch)
|
||||
npm run test:e2e # Playwright (requires a prior `npm run build`)
|
||||
npm run typecheck # tsc --noEmit
|
||||
npm run install:all
|
||||
npm run chrome:dev
|
||||
npm run chrome:build # → packages/chrome/.output/chrome-mv3/
|
||||
npm run chrome:zip
|
||||
npm run chrome:test
|
||||
npm run chrome:typecheck
|
||||
npm run vscode:build
|
||||
npm run cli:build
|
||||
```
|
||||
|
||||
**Prerequisite:** Node. CI pins **Node 22** (`node:22-bookworm`). Run `npm install` before any `npm run *` — the binaries (`tsc`, `vitest`) come from `node_modules/.bin`. Install triggers `postinstall: wxt prepare`, which generates `.wxt/types/` — **typecheck fails without it** (`import.meta.env` is typed there). If a fresh clone errors with `Property 'env' does not exist on type 'ImportMeta'`, run `npx wxt prepare`.
|
||||
|
||||
**Load unpacked in Chrome:** `npm run build` → `chrome://extensions` → Developer Mode → Load unpacked → select `.output/chrome-mv3`.
|
||||
**Prerequisite:** Node **22**. Chrome `postinstall` runs `wxt prepare`. Load unpacked from `packages/chrome/.output/chrome-mv3`.
|
||||
|
||||
#### Architecture
|
||||
|
||||
Three cooperating contexts, message-passed over `chrome.runtime`:
|
||||
|
||||
```
|
||||
entrypoints/content.ts (content script, injected into <all_urls>)
|
||||
• Detects text selection: textarea/input (selectionStart/End) vs contenteditable/DOM (Range API)
|
||||
• Renders the floating toolbar + result modal + toasts (inline-styled, appended to document.body)
|
||||
• Snapshots selection state BEFORE any async call, then Replace uses the snapshot
|
||||
• Sends { type: 'ANALYZE_TEXT', payload: {text, action, style} } to the background
|
||||
|
||||
entrypoints/background.ts (service worker — the LLM proxy)
|
||||
• onMessage: ANALYZE_TEXT and COPY_AS
|
||||
• Reads provider/apiKey/apiKeyEnc/encKey/model from chrome.storage.local
|
||||
• Decrypts the key (tweetnacl secretbox), routes to the correct provider's fetch call
|
||||
• Registers right-click context menus (action × style) on install
|
||||
|
||||
entrypoints/options/Options.tsx (settings page, React)
|
||||
• Provider + model + API key form; encrypts the key and writes apiKeyEnc/encKey to storage
|
||||
|
||||
entrypoints/popup/Popup.tsx (toolbar popup, React)
|
||||
• Standalone text box → same ANALYZE_TEXT flow; shows config status; links to Options
|
||||
```
|
||||
Chrome: three contexts over `chrome.runtime` under `packages/chrome/entrypoints/` (`content`, `background`, `options`, `popup`). VS Code and CLI reuse `@lib` from `src/lib`.
|
||||
|
||||
Content script and popup **must not** call provider APIs directly — CORS and key handling belong in the background service worker. Route everything through `ANALYZE_TEXT`/`COPY_AS`. See `docs/ARCHITECTURE.md` for the component table.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user