- Use Bun from the repository root. The tracked lockfile is
bun.lock, thespaceworkspace dependency usesworkspace:*, and install/build hooks invoke Bun even when started through npm. bun run setupis the interactive Cloudflare/resource bootstrap. Local development expects the generated.dev.vars; never commit.dev.vars*or.prod.vars.bun run devstarts the React frontend and Worker together through@cloudflare/vite-pluginathttp://localhost:5173. There is no separate Worker dev command.bun run dev:browseris an optional local Chromium sidecar for the think agent's browser-console tool; absence only produces a warning.
- Root checks:
bun run typecheck,bun run lint,bun run test,bun run build. bun run buildbuildsspaceand the Vite/Worker bundle; it does not typecheck. Runbun run typecheckseparately.- Focus a root test with
bunx vitest run path/to/file.test.ts; test execution uses the Workers pool andwrangler.test.jsonc. - The root Vitest suite excludes all
sdk/test/**andcontainer/monitor-cli.test.ts. SDK tests use Bun:bun run --cwd sdk test. - SDK integration tests require a running root dev server and
VIBESDK_INTEGRATION_API_KEY; runbun run --cwd sdk test:integration. They can take 5-10 minutes;VIBESDK_INTEGRATION_RUN_PREVIEW=1enables the slower preview case. - Root typecheck/lint do not validate
spaceorsdk. For touched packages runbun run --cwd space typecheck/bun run --cwd space buildandbun run --cwd sdk packageas appropriate. - ESLint checks only
src/**andworker/**and deliberately ignores tests; do not treatbun run lintas repository-wide validation. - Pre-commit typechecks staged TypeScript and runs related Vitest tests.
RUN_ALL_TESTS=1selects its broader suite;SKIP_TESTS=1bypasses the hook.
- Tailwind CSS v4 via CSS-first setup in
src/index.css(@import 'tailwindcss',@theme, Kumo tokens); notailwind.config.*. - Prefer
@cloudflare/kumofor new UI. List components withbun kumo ls; component docs viabun kumo doc Button(swap name as needed). Legacy shadcn/Radix undersrc/components/ui/still exists—do not add new primitives there when Kumo covers the case. - Icons:
@phosphor-icons/react. Dark mode isdata-mode="dark"on the root (not aclassstrategy). - Path aliases:
@/*→src/*,shared/*,worker/*(seetsconfig.app.json).
- Use TanStack Query for frontend server state and network-call caching.
QueryClientProvideris wired at the React root; configure shared defaults insrc/lib/query-client.ts. - Keep TanStack query keys centralized in
src/lib/query-keys.ts. Use hierarchical keys so broad invalidation works, for examplequeryKeys.apps.allshould invalidate app list/favorite variants. - Frontend HTTP still goes through
src/lib/api-client.ts; query functions should wrap existingapiClientmethods rather than callingfetchdirectly from components. - Include user/account identity in query keys when cached data is user-specific, or explicitly clear/remove those queries on logout/user switch.
enabled: !!userprevents fetching but does not clear old cached data. - Mutations that change cached server state must update cache with
queryClient.setQueryDataor invalidate the relevantqueryKeyson success. Do not rely on a localrefetch()in one component if sidebar or other shared UI consumes the same data. - Prefer query hooks (
useQuery,useMutation) over ad-hoc loading/error state in React contexts. Context remains appropriate for client-only UI state or providers required by libraries.
src/is the React app (src/main.tsx, routes insrc/routes.tsx). API contracts live insrc/api-types.ts; frontend HTTP calls belong insrc/lib/api-client.ts.worker/index.tsis the Worker entrypoint and Durable Object export surface. Hono middleware/routes are wired byworker/app.tsandworker/api/routes/index.ts.space/is the only declared workspace package. It provides theSpaceDOworkspace and file layer used by the think agent, with durable git history stored through Cloudflare Artifacts, and is bundled before the root app; edit implementation inspace/src, never generatedspace/dist, and keep the hand-maintainedspace/types/index.d.tsaligned with public exports.sdk/is an independent Bun package with its own lockfile, scripts, and tests. It imports the platform WebSocket protocol fromworker/api/websocketTypes.ts, so protocol changes must remain SDK-compatible.- Shared frontend/backend types belong in
shared/; Worker-only types stay underworker/. - Architecture overview (ThinkAgent, SpaceDO, Artifacts, Dynamic Worker previews):
docs/llm.md. Production deploy:bun run deploy(needs.prod.vars).
- API endpoint: update
src/api-types.ts->src/lib/api-client.ts->worker/database/services/(when persistence is needed) ->worker/api/controllers/->worker/api/routes/, then register the route inworker/api/routes/index.ts. - WebSocket message: update
worker/api/websocketTypes.ts, backend handling inworker/agents/core/websocket.ts, and frontend handling insrc/routes/chat/utils/handle-websocket-message.ts; verify SDK tests because its protocol re-exports these types. - LLM tool: add it under
worker/agents/tools/toolkit/and register it inworker/agents/tools/customTools.ts(buildToolsorbuildDebugTools). The think behavior has a separate tool path and bypassesbuildTools. - Think tool: create it under
worker/agents/think/, add SpaceDO RPC typing if needed, register it inThinkAgent.getTools(), and update the relevant prompt or skill. - D1 schema source is
worker/database/schema.ts; generate migrations intomigrations/withbun run db:generate, then apply locally withbun run db:migrate:local. - After changing Wrangler bindings, run
bun run cf-typegen;worker-configuration.d.tsis consumed by setup and TypeScript configs.
- Do not introduce new
anytypes even though ESLint currently permits existing ones; find or define a concrete type. Frontend API types should import from@/api-types. - Worker code reads bindings from
env; do not use Vite environment variables there. - All
/api/*routes are owner-only by default inworker/app.ts; public routes must explicitly follow the existing auth override pattern. - User secrets RPC methods return
null/booleanon failure rather than throwing; preserve that contract when editingworker/services/secrets/. - For usage-limit UI behavior and its cross-component invariants, read
docs/usage-limits-ui.mdbefore editing the badge, credits banner, or limit popups.