This file is read by Claude Code, Copilot, Codex, and other coding agents working on this repo.
./start-dev.sh # No OAuth, mock dev-user, backend :8080, frontend :5174
./startup-oauth.sh # With GitHub OAuth (requires .env with GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET)Build and lint before creating PRs:
cd web && npm run build && npm run lintcmd/console/ Server entry point
cmd/kc-agent/ Local agent (bridges browser to kubeconfig + MCP)
pkg/agent/ AI providers (Claude, OpenAI, Gemini)
pkg/api/ HTTP/WS server + handlers
pkg/mcp/ MCP bridge to Kubernetes
pkg/store/ SQLite database layer
web/src/ React + TypeScript frontend
components/cards/ Dashboard card components
hooks/ Data fetching hooks (useCached*)
lib/ Utilities, card registry, demo data
deploy/helm/ Helm chart
ALL UI and API work MUST be tested before marking complete. Do not just write code and assume it works. Use one or more of these tools:
- Playwright (preferred for comprehensive E2E tests)
cd web && npx playwright test --grep "your-test-pattern"
- Chrome DevTools MCP (for interactive testing)
mcp__chrome-devtools__navigate_page- Load pagesmcp__chrome-devtools__take_snapshot- Verify DOM elementsmcp__chrome-devtools__click/mcp__chrome-devtools__fill- Interactmcp__chrome-devtools__take_screenshot- Capture visual state
- curl - Test REST API endpoints
curl -s http://localhost:8080/api/health | jq - websocat - Test WebSocket connections
websocat ws://localhost:8585/ws
- New UI components render correctly
- User interactions work as expected
- No console errors
- API endpoints return expected data
- WebSocket connections establish properly
- Backend: Must always run on port 8080
- Frontend: Must always start on port 5174 (use
npm run dev -- --port 5174)
Use ./startup-oauth.sh to start the full development environment:
./startup-oauth.shThis script automatically:
- Kills existing processes on ports 8080, 5174, 8585
- Loads
.envcredentials (GitHub OAuth) - Starts kc-agent, backend (OAuth mode), and frontend
- Handles Ctrl+C cleanup
Requirements: Create a .env file with GitHub OAuth credentials:
GITHUB_CLIENT_ID=<your-client-id>
GITHUB_CLIENT_SECRET=<your-client-secret>
If you need to start components individually:
npm run dev -- --port 5174 # FrontendThe backend (KC API server) runs on port 8080. The KC agent WebSocket runs on port 8585.
Every dashboard card component MUST follow these patterns for loading, caching, and demo data to work correctly.
Every card using a useCached* hook MUST destructure isDemoData (or isDemoFallback) and isRefreshing, then pass both to useCardLoadingState() or useReportCardDataState():
const { data, isLoading, isRefreshing, isDemoData, isFailed, consecutiveFailures } = useCachedXxx()
useCardLoadingState({
isLoading,
isRefreshing, // ← Required for refresh icon animation
isDemoData, // ← Required for Demo badge + yellow outline
hasAnyData: data.length > 0,
isFailed,
consecutiveFailures,
})Without isDemoData: cards show demo data without the Demo badge/yellow outline.
Without isRefreshing: no refresh icon animation when data is being updated in background.
The hook's isDemoFallback must be false while isLoading is true. This ensures CardWrapper shows a loading skeleton instead of immediately rendering demo data.
Correct pattern in hooks:
const effectiveIsDemoFallback = cacheResult.isDemoFallback && !cacheResult.isLoadingWrong pattern:
const effectiveIsDemoFallback = cacheResult.isDemoFallback // BUG: true during loading| Scenario | Behavior |
|---|---|
| First visit, API keys present | Loading skeleton → live data |
| Revisit, API keys present | Cached data instantly → refresh icon spins → updated data |
| No API keys / demo mode | Demo data immediately (with Demo badge + yellow outline) |
| API keys present, fetch fails | Loading skeleton → demo data fallback after timeout |
All data fetching in cards MUST go through the cache layer (useCache or a useCached* hook from hooks/useCachedData.ts). This provides:
- Persistent cache (IndexedDB/SQLite) for instant data on revisit
- SWR (stale-while-revalidate) pattern
- Automatic demo fallback
- Loading/refreshing state management
useCardLoadingState / useReportCardDataState must be called AFTER the hooks that provide isDemoData. React hooks run in order — if the loading state hook runs before data hooks, it won't have the correct values.