This guide covers the tools, setup, and conventions needed to work on osapi-ui.
Install tools using mise:
mise install- Node.js — Required runtime.
- Bun — Package manager and script runner.
- just — Task runner used for building, testing, formatting, and other
development workflows. Install with
brew install just.
If you use Claude Code for development, install these plugins from the default marketplace:
/plugin install commit-commands@claude-plugins-official
/plugin install superpowers@claude-plugins-official
- commit-commands — provides
/commitand/commit-push-prslash commands that follow the project's commit conventions automatically. - superpowers — provides structured workflows for planning, TDD, debugging, code review, and git worktree isolation.
Fetch shared justfiles and install all dependencies:
just fetch
just depsjust devOpens at http://localhost:5173. Hot-reloads on file changes.
Create a .env.local file (gitignored):
OSAPI_API_URL=http://localhost:8080
OSAPI_BEARER_TOKEN=<your-jwt-here>All env vars use the OSAPI_ prefix (configured in vite.config.ts).
| Variable | Default | Description |
|---|---|---|
OSAPI_API_URL |
http://localhost:8080 |
OSAPI API base URL |
OSAPI_BEARER_TOKEN |
(empty) | JWT token for auto-login |
OSAPI_FEATURE_STACKS |
false |
Enable saved stacks UI |
Generate a bearer token with the OSAPI CLI:
osapi token generateIf OSAPI_BEARER_TOKEN is set, the app auto-authenticates and skips the
sign-in page. If not set, users paste their token on the sign-in page.
TypeScript and CSS should be formatted by Prettier and linted using ESLint. This style is enforced by CI.
just react::fmt # Auto-fix formatting
just react::lint # Run ESLint- One component per file.
- Use
cvafrom class-variance-authority for component variants. - Use the
cn()helper for conditional Tailwind classes. - Icons from lucide-react only.
- No inline styles — Tailwind classes only.
- Use shared UI primitives (see Architecture docs) instead of repeating Tailwind patterns inline.
- Always use
Textfor styled text — never writetext-xs text-text-mutedinline. Use<Text variant="muted">instead. - Always use the custom
Dropdowncomponent. Never use native<select>. - Use Tailwind scale only (
text-xs,text-sm, etc.). Never use arbitrary pixel values liketext-[10px].
- Components:
kebab-case.tsx(e.g.,agent-card.tsx) - Hooks:
use-kebab-case.ts(e.g.,use-health.ts) - Utilities:
kebab-case.ts(e.g.,cn.ts)
When the OSAPI Go API changes, copy the combined OpenAPI spec from the osapi repo and regenerate the TypeScript SDK:
# In the osapi repo, run `just generate` first to produce the combined spec.
# Then copy it into this repo:
cp <osapi-repo>/internal/controller/api/gen/api.yaml src/sdk/gen/api.yaml
# Regenerate typed fetch functions from the spec:
just react::generateSee the Architecture docs for the full generation flow.
Run just ready before committing to ensure SDK generation, formatting, lint,
and build are all up to date:
just ready # generate + fmt + lint + buildAll changes should be developed on feature branches. Create a branch from
main using the naming convention type/short-description, where type
matches the Conventional Commits type:
feat/add-dns-blockfix/agent-card-overflowdocs/update-architecturerefactor/extract-componentchore/update-dependencies
When using Claude Code's /commit command, a branch will be created
automatically if you are on main.
Follow Conventional Commits with the 50/72 rule:
- Subject line: max 50 characters, imperative mood, capitalized, no period
- Body: wrap at 72 characters, separated from subject by a blank line
- Format:
type(scope): description - Types:
feat,fix,docs,style,refactor,perf,test,chore - Summarize the "what" and "why", not the "how"
Try to write meaningful commit messages and avoid having too many commits on a PR. Most PRs should likely have a single commit (although for bigger PRs it may be reasonable to split it in a few). Git squash and rebase is your friend!