AI-powered UI feedback system. pnpm monorepo (Turborepo): Chrome Extension + MCP Server + shared types.
packages/
shared/ → TypeScript shared types (ESM, zero runtime deps)
mcp-server/ → MCP server + WebSocket server (ESM, Node.js)
extension/ → Chrome Extension (vanilla JS, CommonJS, no build step)
external/
opencode/ → Git submodule (OpenCode fork) — do NOT modify
Extension ↔ MCP Server via WebSocket (port 19989). MCP Server → OpenCode/LLM via MCP SDK sampling (server.createMessage()).
pnpm install # install all
pnpm build # build all (shared → mcp-server)
pnpm --filter @agentation/shared build # build single package
pnpm --filter @agentation/mcp-server build # build single package
pnpm typecheck # type check all
pnpm --filter @agentation/mcp-server typecheck # type check single
pnpm dev # watch mode
pnpm format # prettier (all files)
pnpm clean # clean dist/
pnpm --filter @agentation/mcp-server start # start MCP serverBuild order: shared → mcp-server (via dependsOn: ["^build"]). Always build shared first when changing shared types.
Tests: No test framework configured. pnpm test exists in turbo.json but no packages define test runners.
Both shared and mcp-server use identical tsconfig: target ES2022, module/moduleResolution NodeNext, strict: true, declaration + sourceMap enabled. All packages use "type": "module" (ESM). Extension uses "type": "commonjs".
Import rules — .js extension required on all relative imports (NodeNext resolution):
import { AgentationMCPServer } from "./mcp-server.js"; // CORRECT
import { AgentationMCPServer } from "./mcp-server"; // WRONG
import { DEFAULT_MCP_SERVER_PORT } from "@agentation/shared"; // package imports: no extension
import type { Annotation } from "@agentation/shared"; // type-only: use `import type`Import order: Node built-ins → external packages → @agentation/* → relative imports.
No .prettierrc — uses Prettier v3 defaults: double quotes, semicolons, trailing commas, 2-space indent. Run: pnpm format.
| Kind | Convention | Example |
|---|---|---|
| Variables/funcs | camelCase | pendingFeedback, buildFeedbackPrompt |
| Classes | PascalCase | AgentationMCPServer |
| Interfaces/Types | PascalCase | ExtensionClient, StatusPayload |
| Constants | UPPER_SNAKE | DEFAULT_MCP_SERVER_PORT, ERROR_CODES |
| Files | kebab-case | mcp-server.ts, websocket-server.ts |
| CSS classes | agentation-* prefix |
agentation-toolbar |
interfacefor object shapes;typefor unions/computed types- All shared types in
packages/shared/src/index.ts - Discriminated unions with
typefield for message protocols - Zod schemas for runtime validation at MCP tool input boundaries
as constfor constant objects serving as enums
console.errorwith bracketed prefix tags:[MCP],[WS],[CLI]- Check
error instanceof Errorbefore accessing.message/.stack - Propagate errors up; never swallow silently
- Structured error payloads:
{ code, message }for WebSocket errors
} catch (error) {
console.error("[MCP] Sampling request failed:");
console.error("[MCP] Error message:", error instanceof Error ? error.message : String(error));
throw error;
}[MCP] — MCP server operations
[WS] — WebSocket server operations
[CLI] — CLI entry point
[Agentation] — Extension client-side
[WS Client] — Extension WebSocket client
- Class-based architecture (
AgentationMCPServer,AgentationWebSocketServer) privatekeyword for private members (not#)- JSDoc
/** */on public API methods - Constructor takes config params with sensible defaults
- Content scripts wrapped in IIFE:
(function() { "use strict"; ... })(); - No build step — loaded directly by Chrome (Manifest V3)
chrome.runtime.sendMessage/chrome.storage.localfor state- Cross-script communication via
window.*(e.g.,window.agentationWS) - i18n via
window.agentationI18n.t(key)helper - All CSS classes prefixed
agentation-to avoid page conflicts
| Package | Key Dependencies |
|---|---|
shared |
none (types only) |
mcp-server |
@modelcontextprotocol/sdk, ws, zod, @agentation/shared |
extension |
playwright (devDep only — screenshot capture) |
- WebSocket messages:
{ type: WebSocketMessageType, id?, payload?, timestamp }— discriminated union ontype - MCP sampling:
server.createMessage()from@modelcontextprotocol/sdk - Internal deps:
"workspace:*"references in package.json
- Modify anything under
external/opencode/(git submodule) - Use
as any,@ts-ignore, or@ts-expect-error - Add ESLint — project intentionally uses Prettier only
- Change module resolution from NodeNext
- Omit
.jsextensions on relative TypeScript imports - Break the
shared → mcp-serverbuild dependency order