This file provides guidance to AI Code Agent when working with code in this repository.
This repository is a Go backend plus a Vite/TanStack Router frontend workspace.
- Go entrypoints live at the repository root:
main.gois the thin CLI entrypointfrontend_dev.goprovides the development-mode frontend handler wrapperfrontend_embed.goprovides the embedded production frontend handler wrapper for-tags embed
- Core Go packages live under
pkg/:pkg/app/for CLI setup and startup configpkg/server/for server startup and HTTP route compositionpkg/configservice/for ConnectRPC handlerspkg/config/for config loading and validationpkg/frontend/for shared frontend handler implementations
- Protobuf and RPC sources live under:
proto/for.protocontracts managed by Bufpkg/rpc/for generated Go protobuf and Connect stubs
- Frontend source lives in
frontend/:- Vite entry:
frontend/src/main.tsx - Router setup:
frontend/src/router.tsx - Route modules:
frontend/src/routes - Shared UI composites:
frontend/src/components - Connect/Web generated client types:
frontend/src/rpc - Shared Connect transport/helpers:
frontend/src/lib/connect - Design primitives/themes:
frontend/src/ui - Global styling:
frontend/src/styles.css - Static assets:
frontend/public
- Vite entry:
- Frontend production assets are emitted to
frontend/dist/ - The production Go binary is emitted to
dist/git-plus
Colocate new frontend feature assets with the component or route that consumes them to keep dependencies obvious.
- UI: Mantine v8. Use the context7 MCP tool with the library id
/mantine/mantineto load docs. - Routing: TanStack Router. Use context7 with
/websites/tanstack_routerfor Router docs. - Generated files like
routeTree.gen.tsare auto-created by@tanstack/router-plugin; do not edit. - Generated files under
pkg/rpc/andfrontend/src/rpc/are auto-created by Buf plugins; do not edit them by hand. Updateproto/files and re-run code generation instead.
Use pnpm for workspace tasks and Go tooling for backend compilation/tests.
pnpm devstarts both the frontend dev server and the Go server. The Go server proxies every non-/apirequest to the frontend dev server.pnpm buf:generateregenerates Go and TypeScript RPC code fromproto/intopkg/rpc/andfrontend/src/rpc/.pnpm buf:lintvalidates.protofiles with Buf lint rules.pnpm buildfirst builds the frontend intofrontend/dist/, then buildsdist/git-pluswithgo build -tags embed, embedding the frontend assets into the binary.pnpm db:generate:drizzleregenerates Drizzle SQL migrations fromdb/src/schema.ts.pnpm db:generate:schema-sqlregeneratesdb/schema.sqlfromdb/src/schema.ts.pnpm db:generate:sqlcregenerates Gosqlcquery code fromdb/schema.sqlanddb/queries/*.sql.pnpm db:generateruns all database codegen steps in sequence.pnpm testrunsgo test ./...and then frontend Vitest.pnpm check:typesrunsfrontendtype-checking viatsc --noEmit.pnpm lintapplies the TanStack + React ESLint rules.pnpm formatruns Prettier, then ESLint autofix, then ESLint verification.
If you need frontend-only commands during local debugging, use the package-level scripts in frontend/package.json directly.
Database schema workflow:
db/src/schema.tsis the source-of-truth for the entire SQLite schema.- Do not handwrite or manually edit
db/migrations/*/migration.sql. - Do not handwrite or manually edit Drizzle snapshot files such as
db/migrations/*/snapshot.json. - Do not handwrite or manually edit
db/schema.sql. - When the database schema changes, update
db/src/schema.tsfirst, then regenerate artifacts in order:pnpm db:generate:drizzle,pnpm db:generate:schema-sql,pnpm db:generate:sqlc. - Always use
pnpm db:generate:schema-sqlto updatedb/schema.sql; do not edit it manually. - Do not run
db/schema.sqlgeneration andsqlcgeneration in parallel.sqlcmust read the freshly generateddb/schema.sql. db/schema.sqlis a generated downstream schema artifact for SQL tooling; do not treat it as the primary schema definition.- Raw SQL execution is allowed only in the migration runner. All non-migration database reads and writes in application code must go through
sqlc-generated queries defined indb/queries/*.sql. - After changing database queries or any schema used by application code, regenerate database artifacts with
pnpm db:generateunless you intentionally need only one sub-step.
Dependency upgrade policy:
- Keep
@types/nodeandeslintat their current versions. - Do not upgrade them unless explicitly requested.
Frontend testing uses Vitest:
- Vitest is configured via
vitest.config.tsusingtest.projects(separateunit+browserprojects). - Root
pnpm testruns Go tests first, then frontend Vitest. - Frontend-only
pnpm --filter ./frontend testruns all Vitest projects. - Frontend-only
pnpm --filter ./frontend test:unitruns theunitproject. - Frontend-only
pnpm --filter ./frontend test:browserruns thebrowserproject. - The default test environment is
node. For React component tests, prefer Browser Mode (pnpm test:browser) and name files*.browser.test.tsx/*.browser.spec.tsx.
Browser Mode notes:
- Test file pattern:
src/**/*.browser.{test,spec}.{ts,tsx} - Setup file:
vitest.browser.setup.ts - If Chromium is missing locally, install it with
pnpm exec playwright install chromium
Before declaring any Agent task complete, re-run the commands affected by your changes. For full-stack changes, prefer validating with pnpm test, pnpm build, and pnpm check:types.
If you changed any .proto definitions or RPC wiring, also run pnpm buf:generate and pnpm buf:lint.
ESLint 9 flat config is used (eslint.config.js). The configuration extends @tanstack/eslint-config and adds React-specific rules via eslint-plugin-react.
Key React rules enforced (errors):
react/button-has-type- buttons must have an explicittypeattributereact/self-closing-comp- components without children must self-closereact/jsx-curly-brace-presence- no unnecessary curly braces in JSXreact/jsx-boolean-value- omit={true}for boolean propsreact/jsx-fragments- use<>syntax for fragmentsreact/no-children-prop- don't pass children as a prop
Run pnpm lint to check for errors. ESLint errors are blocking and must be fixed before committing.
Prettier config lives in prettier.config.js:
semi: true- always use semicolonssingleQuote: true- use single quotes for stringstrailingComma: 'all'- trailing commas everywhere
Run pnpm format to format and fix lint errors in one step (runs Prettier then ESLint --fix).
Write TypeScript React function components. Prettier enforces semicolons, single quotes, and trailing commas—never hand-format around it.
Use PascalCase for components (DashboardCard.tsx), camelCase for hooks/utilities. Mantine styles should live beside their component, leveraging Mantine's theming utilities before reaching for raw CSS.
Commits should mirror the existing short, imperative pattern (use mantine components for index page). Keep changes scoped and meaningful. PRs must include: a concise summary, linked issue/ticket, before/after screenshots for UI work, and confirmation that pnpm lint and pnpm check:types passed. Mention any config or env changes up front so reviewers can reproduce.
Secrets belong at the repository root in local env files such as .env / .env.local (gitignored); frontend-only examples live alongside the workspace, but the active runtime setup is root-oriented. Run lefthook install once so git hooks catch lint regressions pre-push. Document any third-party scripts or analytics additions in the PR, including CSP or token requirements.
- Prefer Mantine components and styles; avoid unnecessary custom CSS.
- Extract custom frontend components into
frontend/src/components/; use the existing frontend structure and tooling rather than introducing a second pattern. - Always use English when writing code, comments, and documentation.
The backend API under /api is implemented with ConnectRPC and generated from protobuf definitions.
- Define API contracts in
proto/. - Generate Go server/client stubs into
pkg/rpc/and TypeScript definitions intofrontend/src/rpc/. - Use Buf for schema linting and code generation via
pnpm buf:generateandpnpm buf:lint. - Mount Connect handlers under the
/apibase path. Frontend transports should usecreateConnectTransport({ baseUrl: '/api' }). - Prefer changing schemas and regenerating code over editing generated RPC files directly.
The project uses Mantine v8 for UI components. Use the context7 MCP tool with the library id /mantine/mantine to load (or search) docs.
The project uses TanStack Router with the Vite plugin for file-based routing. Use the context7 MCP tool with the library id /websites/tanstack_router to load (or search) docs.
Use @mantine/hooks for common React hooks. Prefer these over writing custom hooks when available.
Note: Do NOT use useClipboard from @mantine/hooks. Use copy-to-clipboard instead (see below).
Use copy-to-clipboard for clipboard operations:
import copy from 'copy-to-clipboard';
copy('Text to copy');
copy(text, { format: 'text/plain' });Use @mantine/modals for declarative modal management. The ModalsProvider is already configured in __root.tsx.
Use TanStack Query for server state management and data fetching. Use the context7 MCP tool with the library id /tanstack/query to load (or search) docs.
Prefer useQuery / useMutation / useSuspenseQuery / useSuspenseInfiniteQuery for data fetching. Do NOT use useEffect + useState to fetch data. TanStack Query provides built-in caching, background refetching, error handling, and loading states.
TanStack Query can be combined with TanStack Router's loader / beforeLoad for optimal data fetching:
- Define query options using
queryOptions()for reuse between loader and component - Preload in loader using
queryClient.ensureQueryData(options)(awaits data) orqueryClient.prefetchQuery(options)(non-blocking) - Consume in component using
useSuspenseQuery(options)to automatically use cached data
Recommended patterns:
- Use
ensureQueryDatain loader for critical data that must be available before render - Use
prefetchQuery(without await) for slower/non-critical data, then wrap component in<Suspense>to handle loading - Access
queryClientfrom route context:loader: ({ context: { queryClient } }) => ... - Always reuse the same
queryOptionsin both loader and component to ensure cache hits
Use dayjs for date manipulation. It's a lightweight alternative to moment.js.
Use Zod v4 for schema validation. Prefer Zod for form validation and API response parsing.
The project uses Sonner for toast notifications instead of @mantine/notifications. Import toast from sonner to show notifications:
import { toast } from 'sonner';
toast.success('Success message');
toast.error('Error message');
toast.info('Info message');
toast.warning('Warning message');
toast.loading('Loading...');
toast.promise(promise, {
loading: 'Loading...',
success: 'Done!',
error: 'Error!',
});If you need to get some other documentations that you don't know exactly, you can use the context7 mcp tool.