feat(cli): add initial CLI implementation with argument parsing and prompt handling
Some checks failed
CI — Test & Build / Test & Build (pull_request) Has been cancelled
Preview — PR Build Check / PR Preview Build (pull_request) Has been cancelled

- 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:
john kevin asprec
2026-08-13 19:19:42 +08:00
parent d63d698b57
commit 2c00d2e431
37 changed files with 9303 additions and 166 deletions

View File

@@ -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.