Three conflicts, all in the files this PR refactored. The code from #1955 and #2161 (core/react's useSyncExternalStore conversion) merged cleanly. - `.github/copilot-instructions.md` — modify/delete. This PR deletes it, and the four lines v2/main added there are a distillation of AGENTS.md rules that arrive in AGENTS.md anyway. Kept deleted. - `AGENTS.md` — the incoming change expanded the `core/react/` entry in the project tree by eleven lines; this PR compacted that tree to one line per entry on purpose. Took the compact tree and carried the substance onto its one line. The other incoming hunk — the store-subscription rules under React instructions — merged cleanly, which is the split working: it is a RULE, so it lands in the section that stayed. - `README.md` — same shape, one tree line. This README no longer enumerates `core/`'s subdirectories at all, so the substance went where that content now lives: `docs/architecture.md` and the `project-structure` skill's core table. Nothing from v2/main was dropped; every incoming statement is now in exactly one place rather than the three it used to occupy. Signed-off-by: cliffhall <cliff@futurescale.com>
2.8 KiB
Architecture
The @inspector/core shared package
core/ holds the logic shared by all three clients so that web, CLI, and TUI behave identically. Its entry point is the InspectorClient class (core/mcp/), which owns the connection to an MCP server, the request/response lifecycle, and a set of state stores; core/react/ exposes React hooks over those stores that both the web and TUI (Ink) React trees consume — each reading its snapshot during render via useSyncExternalStore, so a store swap lands in the same frame and no event dispatched between render and subscribe is lost (#1955). OAuth (core/auth/) is factored into isomorphic logic plus browser/node/remote backends so the same flows work in the browser, in Node, and against a remote backend.
core/ intentionally has no package.json — it is not published on its own. Each client bundles it in via a @inspector/core alias:
- CLI / TUI:
esbuildOptions.aliasin theirtsup.config.tsmaps@inspector/core→ the repocore/directory, andnoExternal: [/^@inspector\/core/]inlines it into the bundle. - Web: two configs, because the client is two builds —
clients/web/vite.config.tsfor the browser app, andclients/web/tsup.runner.config.tsfor the Node backend runner, which defines the alias itself.
Publishing core/ as its own package (e.g. for third parties to build on) is deliberately deferred — see issue #1636.
Web client: "dumb components" + Storybook
The v2 web client is built from presentational ("dumb") components — they accept data and callbacks as props and contain only display logic, with no direct data fetching or client state. State comes from the @inspector/core hooks, wired in near the top of the tree. This keeps components isolated, testable, and documentable.
That approach is what makes Storybook first-class here: every screen and element component has a *.stories.tsx file (96+ stories) that renders it against fixture props. Storybook play functions double as interaction tests, run headless in GitHub CI (test:storybook, Chromium via Playwright) and in the local gate (npm run local:storybook, which installs the browser first).
Styling follows a strict Mantine-first convention (theme variants and component props over CSS classes, --inspector-* CSS custom properties over raw color literals). The full rules live in AGENTS.md under React instructions — read them before touching web UI. Element components live in clients/web/src/components/elements/; theme variants in clients/web/src/theme/.
