ba32a44cb4
* refactor(backend): drop legacy orchestrator WS handlers superseded by session.launch The orchestrator exposed six WS handlers (orchestrator.start, orchestrator.trigger, orchestrator.prompt, orchestrator.complete, task.session.resume, task.session.prepare) that all delegated to the unified LaunchSession or are no longer reachable from any client. Drop the handlers, their DTOs, and the dead action constants (orchestrator.pause, orchestrator.resume) that were never registered. Migrate the remaining integration tests to the unified session.launch action. * refactor(backend): drop orphan DTO request types and fix stale doc reference Address PR review feedback: the DTO types TaskSessionStatusRequest, StopTaskRequest, and PermissionRespondRequest had no consumers (each was shadowed by an inline private ws*Request type in the handler). AGENTS.md still referenced the removed task.start action; switch it to session.launch. --------- Co-authored-by: Kandev Agent <agent@kandev.dev>
392 lines
23 KiB
Markdown
392 lines
23 KiB
Markdown
# Kandev Engineering Guide
|
|
|
|
> **Purpose**: Architecture notes, key patterns, and conventions for LLM agents working on Kandev.
|
|
|
|
## Repo Layout
|
|
|
|
```
|
|
apps/
|
|
├── backend/ # Go backend (orchestrator, lifecycle, agentctl, WS gateway)
|
|
├── web/ # Next.js frontend (SSR + WS + Zustand)
|
|
├── cli/ # CLI tool (TypeScript)
|
|
├── landing/ # Landing page
|
|
└── packages/ # Shared packages/types
|
|
```
|
|
|
|
## Tooling
|
|
|
|
- **Package manager**: `pnpm` workspace (run from `apps/`, not repo root)
|
|
- **Backend**: Go with Make (`make -C apps/backend test|lint|build`)
|
|
- **Frontend**: Next.js (`cd apps && pnpm --filter @kandev/web dev|lint|typecheck`)
|
|
- **UI**: Shadcn components via `@kandev/ui`
|
|
- **GitHub repo**: `https://github.com/kdlbs/kandev`
|
|
- **Container image**: `ghcr.io/kdlbs/kandev` (GitHub Container Registry)
|
|
|
|
---
|
|
|
|
## Backend Architecture
|
|
|
|
### Package Structure
|
|
|
|
```
|
|
apps/backend/
|
|
├── cmd/
|
|
│ ├── kandev/ # Main backend binary entry point
|
|
│ ├── agentctl/ # Agentctl binary (runs inside containers or standalone)
|
|
│ └── mock-agent/ # Mock agent for testing
|
|
├── internal/
|
|
│ ├── agent/
|
|
│ │ ├── lifecycle/ # Agent instance management (see below)
|
|
│ │ ├── agents/ # Agent type implementations
|
|
│ │ ├── controller/ # Agent control operations
|
|
│ │ ├── credentials/ # Agent credential management
|
|
│ │ ├── discovery/ # Agent discovery
|
|
│ │ ├── docker/ # Docker-specific agent logic
|
|
│ │ ├── dto/ # Agent data transfer objects
|
|
│ │ ├── executor/ # Executor types, checks, and service
|
|
│ │ ├── handlers/ # Agent event handlers
|
|
│ │ ├── registry/ # Agent type registry and defaults
|
|
│ │ ├── settings/ # Agent settings
|
|
│ │ ├── mcpconfig/ # MCP server configuration
|
|
│ │ └── remoteauth/ # Remote auth catalog and method IDs for remote executors/UI
|
|
│ ├── agentctl/
|
|
│ │ ├── client/ # HTTP client for talking to agentctl
|
|
│ │ └── server/ # agentctl HTTP server
|
|
│ │ ├── acp/ # ACP protocol implementation
|
|
│ │ ├── adapter/ # Protocol adapters + transport/ (ACP, Codex, OpenCode, Copilot, Amp)
|
|
│ │ ├── api/ # HTTP endpoints
|
|
│ │ ├── config/ # agentctl configuration
|
|
│ │ ├── instance/ # Multi-instance management
|
|
│ │ ├── mcp/ # MCP server integration
|
|
│ │ ├── process/ # Agent subprocess management
|
|
│ │ ├── shell/ # Shell session management
|
|
│ │ └── utility/ # agentctl utilities
|
|
│ ├── orchestrator/ # Task execution coordination
|
|
│ │ ├── dto/ # Orchestrator data transfer objects
|
|
│ │ ├── executor/ # Launches agents via lifecycle manager
|
|
│ │ ├── handlers/ # Orchestrator event handlers
|
|
│ │ ├── messagequeue/ # Message queue for agent prompts
|
|
│ │ ├── queue/ # Task queue
|
|
│ │ ├── scheduler/ # Task scheduling
|
|
│ │ └── watcher/ # Event handlers
|
|
│ ├── task/
|
|
│ │ ├── controller/ # Task HTTP/WS controllers
|
|
│ │ ├── dto/ # Task data transfer objects
|
|
│ │ ├── events/ # Task event types
|
|
│ │ ├── handlers/ # Task event handlers
|
|
│ │ ├── models/ # Task, Session, Executor, Message models
|
|
│ │ ├── repository/ # Database access (SQLite)
|
|
│ │ └── service/ # Task business logic
|
|
│ ├── analytics/ # Usage analytics
|
|
│ ├── clarification/ # Agent clarification handling
|
|
│ ├── common/ # Shared utilities
|
|
│ ├── db/ # Database initialization
|
|
│ ├── debug/ # Debug tooling
|
|
│ ├── editors/ # Editor integration
|
|
│ ├── events/ # Event bus for internal pub/sub
|
|
│ ├── gateway/ # WebSocket gateway
|
|
│ ├── github/ # GitHub API integration (PRs, reviews, webhooks)
|
|
│ ├── integration/ # External integrations
|
|
│ ├── integrations/ # Shared shapes for third-party integrations
|
|
│ │ ├── healthpoll/ # Reusable 90s auth-health Poller (used by jira, linear)
|
|
│ │ └── secretadapter/ # Upsert-style adapter over secrets.SecretStore
|
|
│ ├── jira/ # Jira/Atlassian Cloud integration (config, REST client, poller)
|
|
│ ├── linear/ # Linear integration (config, GraphQL client, poller)
|
|
│ ├── lsp/ # LSP server
|
|
│ ├── mcp/ # MCP protocol support
|
|
│ ├── health/ # Health check endpoints
|
|
│ ├── notifications/ # Notification system
|
|
│ ├── persistence/ # Persistence layer
|
|
│ ├── prompts/ # Prompt management
|
|
│ ├── repoclone/ # Repository cloning for remote executors
|
|
│ ├── scriptengine/ # Script placeholder resolution and interpolation
|
|
│ ├── secrets/ # Secret management
|
|
│ ├── sprites/ # Sprites AI integration
|
|
│ ├── sysprompt/ # System prompt injection
|
|
│ ├── task/
|
|
│ │ └── ...
|
|
│ ├── tools/ # Tool integrations
|
|
│ ├── user/ # User management
|
|
│ ├── utility/ # Shared utility functions
|
|
│ ├── workflow/ # Workflow engine
|
|
│ │ ├── engine/ # Typed state-machine engine (trigger evaluation, action callbacks, transition store)
|
|
│ │ ├── models/ # Workflow step, template, and history models
|
|
│ │ ├── repository/ # Workflow persistence (SQLite)
|
|
│ │ └── service/ # Workflow CRUD and step resolution
|
|
│ └── worktree/ # Git worktree management for workspace isolation
|
|
```
|
|
|
|
### Key Concepts
|
|
|
|
**Orchestrator** coordinates task execution:
|
|
- Receives task start/stop/resume requests via WebSocket
|
|
- Delegates to lifecycle manager for agent operations
|
|
- Handles event-driven state transitions via workflow engine
|
|
- Located in `internal/orchestrator/`
|
|
|
|
**Workflow Engine** (`internal/workflow/engine/`) provides typed state-machine evaluation:
|
|
- `Engine.HandleTrigger()` evaluates step actions for triggers (on_enter, on_turn_start, on_turn_complete, on_exit)
|
|
- `TransitionStore` interface abstracts persistence (implemented by `orchestrator.workflowStore`)
|
|
- `CallbackRegistry` maps action kinds to callbacks (plan mode, auto-start, context reset)
|
|
- First-transition-wins: multiple transition actions in one trigger, first eligible wins
|
|
- `EvaluateOnly` mode: engine evaluates without persisting, caller orchestrates on_exit → DB → on_enter
|
|
- `RequiresApproval` on actions: transitions requiring review gating are skipped
|
|
- Idempotent by `OperationID`; session-scoped data bag via `MachineState.Data`
|
|
|
|
**Lifecycle Manager** (`internal/agent/lifecycle/`) manages agent instances:
|
|
- `Manager` (`manager.go`, `manager_*.go`) - central coordinator for agent lifecycle
|
|
- `ExecutorBackend` interface (`executor_backend.go`) - abstracts execution environment (Docker, Standalone, Sprites, Remote Docker)
|
|
- `ExecutionStore` (`execution_store.go`) - thread-safe in-memory execution tracking
|
|
- `session.go` - ACP session initialization and resume
|
|
- `streams.go` - WebSocket stream connections to agentctl
|
|
- `process_runner.go` - agent process launch and management
|
|
- `profile_resolver.go` - resolves agent profiles/settings
|
|
|
|
**agentctl** is an HTTP server that:
|
|
- Runs inside Docker containers or as standalone process
|
|
- Manages agent subprocess via stdin/stdout (ACP protocol)
|
|
- Exposes workspace operations (shell, git, files)
|
|
- Supports multiple concurrent instances on different ports
|
|
|
|
**Executor Types** (database model):
|
|
- `local_pc` - Standalone process on host ✅
|
|
- `local_docker` - Docker container on host ✅
|
|
- `sprites` - Sprites cloud environment ✅
|
|
- `remote_docker`, `remote_vps`, `k8s` - Planned
|
|
|
|
### Execution Flow
|
|
|
|
```
|
|
Client (WS) → Orchestrator → Lifecycle Manager → ExecutorBackend (container/process) → agentctl
|
|
↓
|
|
Client (WS) ← Orchestrator ← Lifecycle Manager ←──── stream updates (WS) ──────── agent subprocess
|
|
```
|
|
|
|
1. Orchestrator receives `session.launch` via WS
|
|
2. Lifecycle Manager creates executor instance (container or process)
|
|
3. agentctl starts inside the instance, agent subprocess is configured and started
|
|
4. Agent events stream back via WS through the chain
|
|
|
|
**Session Resume:** `TaskSession.ACPSessionID` stored for resume; `ExecutorRunning` tracks active state; on restart `RecoverInstances()` reconnects.
|
|
|
|
**Provider Pattern:** Packages expose `Provide(cfg, log) (*impl, cleanup, error)` for DI. Returns implementation, cleanup function, and error. Cleanup called during graceful shutdown.
|
|
|
|
**Worktrees:** `internal/worktree/Manager` provides workspace isolation. Each session can have its own worktree (branch) to prevent conflicts between concurrent agents.
|
|
|
|
**Executor default scripts:** Default prepare scripts are in `internal/agent/lifecycle/default_scripts.go`; `internal/scriptengine/` handles placeholder resolution.
|
|
|
|
---
|
|
|
|
## agentctl Server
|
|
|
|
### API Groups
|
|
|
|
agentctl exposes these route groups (see `internal/agentctl/server/api/`):
|
|
- `/health`, `/info`, `/status` - Health and status
|
|
- `/instances/*` - Multi-instance management
|
|
- `/processes/*` - Agent subprocess management (start/stop)
|
|
- `/agent/configure`, `/agent/stream` - Agent configuration and event streaming
|
|
- `/git/*` - Git operations (status, commit, push, pull, rebase, stage, etc.)
|
|
- `/shell/*` - Shell session management
|
|
- `/workspace/*` - File operations, search, tree
|
|
- `/vscode/*` - VS Code integration proxy
|
|
|
|
### Adapter Model
|
|
|
|
Protocol adapters in `adapter/transport/` normalize different agent CLIs:
|
|
- `AgentAdapter` interface defines `Start()`, `Stop()`, `Prompt()`, `Cancel()`
|
|
- Transports: `acp` (Claude Code), `codex` (OpenAI Codex), `opencode`, `shared`, `streamjson`
|
|
- Top-level adapters: `CopilotAdapter` (GitHub Copilot SDK), `AmpAdapter` (Sourcegraph Amp)
|
|
- `process.Manager` owns subprocess, wires stdio to adapter
|
|
- Factory pattern in `adapter/factory.go` selects adapter by agent type
|
|
|
|
### ACP Protocol
|
|
|
|
JSON-RPC 2.0 over stdin/stdout between agentctl and agent process. Requests: `initialize`, `session/new`, `session/load`, `session/prompt`, `session/cancel`. Notifications: `session/update` with types `message_chunk`, `tool_call`, `tool_update`, `complete`, `error`, `permission_request`, `context_window`.
|
|
|
|
---
|
|
|
|
## Frontend Architecture
|
|
|
|
### UI Components
|
|
|
|
**Shadcn Components:** Import from `@kandev/ui` package:
|
|
```typescript
|
|
import { Badge } from '@kandev/ui/badge';
|
|
import { Button } from '@kandev/ui/button';
|
|
import { Dialog } from '@kandev/ui/dialog';
|
|
// etc...
|
|
```
|
|
|
|
**Do NOT** import from `@/components/ui/*` - always use `@kandev/ui` package.
|
|
|
|
### Data Flow Pattern (Critical)
|
|
|
|
```
|
|
SSR Fetch -> Hydrate Store -> Components Read Store -> Hooks Subscribe
|
|
```
|
|
|
|
**Never fetch data directly in components.**
|
|
|
|
### Store Structure (Domain Slices)
|
|
|
|
```
|
|
lib/state/
|
|
├── store.ts # Root composition
|
|
├── default-state.ts # Default state + initial state merge
|
|
├── slices/ # Domain slices
|
|
│ ├── kanban/ # boards, tasks, columns
|
|
│ ├── session/ # sessions, messages, turns, worktrees
|
|
│ ├── session-runtime/ # shell, processes, git, context
|
|
│ ├── workspace/ # workspaces, repos, branches
|
|
│ ├── settings/ # executors, agents, editors, prompts (incl. userSettings)
|
|
│ ├── comments/ # code review diff comments
|
|
│ ├── github/ # GitHub PRs, reviews
|
|
│ └── ui/ # preview, connection, active state, sidebar views
|
|
├── hydration/ # SSR merge strategies
|
|
|
|
hooks/domains/{kanban,session,workspace,settings,comments,github}/ # Domain-organized hooks
|
|
lib/api/domains/ # API clients
|
|
├── kanban-api, session-api, workspace-api, settings-api, process-api
|
|
├── plan-api, queue-api, workflow-api, stats-api, github-api
|
|
├── user-shell-api, debug-api, secrets-api, sprites-api, vscode-api
|
|
├── health-api, utility-api
|
|
```
|
|
|
|
**Key State Paths:**
|
|
- `messages.bySession[sessionId]`, `shell.outputs[sessionId]`, `gitStatus.bySessionId[sessionId]`
|
|
- `tasks.activeTaskId`, `tasks.activeSessionId`, `workspaces.activeId`
|
|
- `repositories.byWorkspace`, `repositoryBranches.byRepository`
|
|
|
|
**Hydration:** `lib/state/hydration/merge-strategies.ts` has `deepMerge()`, `mergeSessionMap()`, `mergeLoadingState()` to avoid overwriting live client state. Pass `activeSessionId` to protect active sessions.
|
|
|
|
**Hooks Pattern:** Hooks in `hooks/domains/` encapsulate WS subscription + store selection. WS client deduplicates subscriptions automatically.
|
|
|
|
### WS
|
|
|
|
**Format:** `{id, type, action, payload, timestamp}`.
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### Commit Conventions (enforced by CI)
|
|
|
|
Commits to `main` **must** follow [Conventional Commits](https://www.conventionalcommits.org/) (`type: description`). PRs are squash-merged — the PR title becomes the commit, validated by CI. Changelog is auto-generated from these via git-cliff (`cliff.toml`). See `.agents/skills/commit/SKILL.md` for allowed types and examples.
|
|
|
|
### Release & Versioning Convention
|
|
|
|
Kandev uses a **single SemVer** `X.Y.Z` shared across all distribution channels:
|
|
|
|
- `apps/cli/package.json` version → `X.Y.Z`
|
|
- npm main package: `kandev@X.Y.Z`
|
|
- npm runtime packages: `@kdlbs/runtime-{platform}@X.Y.Z` (5 platforms; declared as `optionalDependencies` in main package)
|
|
- Git tag: `vX.Y.Z` (three-part; legacy `vM.m` tags normalize to `M.m.0`)
|
|
- Homebrew formula: `kdlbs/homebrew-kandev` `Formula/kandev.rb` `version "X.Y.Z"`
|
|
- GitHub release: `vX.Y.Z` with platform tarballs `kandev-{platform}.tar.gz` + `.sha256`
|
|
|
|
**npm and Homebrew are sibling channels**, not chained. Both consume the same GitHub release artifacts; neither depends on the other.
|
|
|
|
**Release flow** (entirely in CI via `.github/workflows/release.yml`, triggered by maintainer from GitHub Actions UI):
|
|
|
|
1. Maintainer clicks "Run workflow" → picks `bump` (patch/minor/major) → optional `dry_run`.
|
|
2. `prepare` job bumps version + regenerates CHANGELOG, opens release PR, squash-merges, tags `vX.Y.Z`.
|
|
3. `build-web` + `build-cli` + `build-bundles` (5 platforms) build the release artifacts.
|
|
4. `publish-release` creates the GitHub release with platform tarballs + sha256 + auto-generated notes.
|
|
5. `publish-npm` publishes 5 `@kdlbs/runtime-*` packages + main `kandev` package to npmjs.
|
|
6. `update-homebrew-tap` pushes updated `Formula/kandev.rb` to `kdlbs/homebrew-kandev` via SSH deploy key.
|
|
|
|
There is no local release script — the entire flow runs in GHA.
|
|
|
|
**Runtime resolution** (in `apps/cli/src/runtime.ts`):
|
|
|
|
1. `KANDEV_BUNDLE_DIR` env var (set by Homebrew wrapper, used by tests).
|
|
2. Installed `@kdlbs/runtime-{platform}` npm package via `require.resolve()`.
|
|
3. `--runtime-version <tag>` cache fallback (debug only — downloads from GitHub).
|
|
|
|
### Code Quality (enforced by linters)
|
|
|
|
Static analysis runs in CI and pre-commit. New code **must** stay within these limits:
|
|
|
|
**Go** (`apps/backend/.golangci.yml` - errors on new code only):
|
|
- Functions: **≤80 lines**, **≤50 statements**
|
|
- Cyclomatic complexity: **≤15** · Cognitive complexity: **≤30**
|
|
- Nesting depth: **≤5** · Naked returns only in functions **≤30 lines**
|
|
- No duplicated blocks (**≥150 tokens**) · Repeated strings → constants (**≥3 occurrences**)
|
|
|
|
**TypeScript** (`apps/web/eslint.config.mjs` - warnings, will become errors):
|
|
- Files: **≤600 lines** · Functions: **≤100 lines**
|
|
- Cyclomatic complexity: **≤15** · Cognitive complexity: **≤20**
|
|
- Nesting depth: **≤4** · Parameters: **≤5**
|
|
- No duplicated strings (**≥4 occurrences**) · No identical functions · No unused imports
|
|
- No nested ternaries
|
|
|
|
**When you hit a limit:** extract a helper function, custom hook, or sub-component. Prefer composition over growing a single function.
|
|
|
|
### Testing
|
|
|
|
Every code change must include tests for new or changed logic. Backend: `*_test.go` files alongside the source. Frontend: `*.test.ts` files for utility functions, hooks, API clients, and store slices. Exceptions: config files, generated code, React component markup. Use `/tdd` for test-driven development.
|
|
|
|
### Knowledge
|
|
- **Specs:** Feature specs live in `docs/specs/<slug>/spec.md` — the "what & why" of a feature, written before coding. Optional siblings: `plan.md` (how), `notes.md` (post-ship). Use `/spec` to write or update a spec. See `docs/specs/INDEX.md`.
|
|
- **Decisions:** Architecture decisions are recorded in `docs/decisions/`. Read `docs/decisions/INDEX.md` for an overview. When making significant architectural choices, create a new ADR via `/record decision`.
|
|
- **Plans:** Retrospective implementation plans live in `docs/plans/` (created via `/record plan`). A forward-looking plan tied to a spec goes at `docs/specs/<slug>/plan.md`.
|
|
|
|
### Backend
|
|
- Provider pattern for DI; stderr for logs, stdout for ACP only
|
|
- Pass context through chains; event bus for cross-component comm
|
|
- **Execution access:** Workspace-oriented handlers (files, shell, inference, ports, vscode, LSP) MUST use `GetOrEnsureExecution(ctx, sessionID)` — it recovers from backend restarts by creating executions on-demand. Only use `GetExecutionBySessionID` for operations that require a running agent process (prompt, cancel, mode).
|
|
- **Testing:** Prefer `testing/synctest` (Go 1.24+) over `time.Sleep` for time-dependent tests. Use `synctest.Test` to wrap tests with tickers or timeouts — it advances fake time instantly when all goroutines are idle. When `synctest` is not feasible (e.g., tests spawning external processes like `git`), use channel-based synchronization (`<-started`, non-blocking `select`) instead of sleep-based waits. Reserve `time.Sleep` only for integration tests that need real subprocess execution time.
|
|
|
|
### Frontend
|
|
- **Data:** SSR fetch → hydrate → read store. Never fetch in components
|
|
- **UI Components:**
|
|
- Import shadcn components from `@kandev/ui`, NOT `@/components/ui/*`
|
|
- **Always prefer native shadcn components** over custom implementations
|
|
- Check `apps/packages/ui/src/` for available components (pagination, table, dialog, etc.)
|
|
- For data tables, use `@kandev/ui/table` with TanStack Table; use shadcn Pagination components
|
|
- Only create custom components when shadcn doesn't provide what's needed
|
|
- **Components:** <200 lines, extract to domain components, composition over props
|
|
- **Hooks:** Domain-organized in `hooks/domains/`, encapsulate subscription + selection
|
|
- **WS:** Use subscription hooks only; client auto-deduplicates
|
|
- **Interactivity:** All buttons and links with actions must have `cursor-pointer` class
|
|
|
|
### Plan Implementation
|
|
- After implementing a plan, run `make fmt` first to format code, then run `make typecheck test lint` to verify the changes. Formatting must come first because formatters may split lines, which can trigger complexity linter warnings.
|
|
|
|
### GitHub Operations
|
|
Skills use `gh` CLI by default. If a `gh` command fails (not installed, not authenticated, etc.), use whatever GitHub tools are available in the environment (MCP GitHub tools, API tools, etc.) to accomplish the same operation. The goal is the same — the tool may differ.
|
|
|
|
### Adding a new third-party integration
|
|
|
|
Jira and Linear are the model: per-workspace credentials, a 90s auth-health poller, a settings page with status banner + reconnect CTA, link/import buttons that gate on availability. New integrations should reuse the shared shapes rather than copying either.
|
|
|
|
**Backend** (`apps/backend/internal/<name>/`):
|
|
- Mirror the package layout: `service.go`, `store.go`, `client.go`, `provider.go`, `handlers.go`, `models.go`, `poller.go`. Expose `Provide(writer, reader *sqlx.DB, secrets SecretStore, eventBus bus.EventBus, log *logger.Logger) (*Service, func() error, error)`. Pass `nil` for `eventBus` when the integration doesn't publish events; both Jira and Linear take and use it for issue-watch publishing.
|
|
- Use `internal/integrations/secretadapter` instead of writing your own upsert wrapper around `secrets.SecretStore`. The adapter satisfies any per-integration `SecretStore` interface shaped as `{Reveal, Set, Delete, Exists}`.
|
|
- Use `internal/integrations/healthpoll` for the auth-health loop. Implement the `Prober` interface (`ListConfiguredWorkspaces` + `RecordAuthHealth`) on a small adapter and let `healthpoll.New("name", prober, log)` own Start/Stop/ticker. Keep integration-specific loops (JQL polling, webhook reconciliation, etc.) separate, like jira's issue-watch loop.
|
|
- Wire the service via a per-domain `init<Name>Service(...)` helper in `cmd/kandev/services.go`, not inline in `provideServices`.
|
|
- Ship a `mock_client.go` + `mock_controller.go` next to the real client. `Provide` branches on `KANDEV_MOCK_<NAME>=true` and returns the in-memory client; `RegisterMockRoutes(router, svc, log)` mounts `/api/v1/<name>/mock/*` only when the service was built with the mock. The e2e backend fixture sets the env var so Playwright tests drive the mock via `apiClient.mock<Name>*()` helpers — see jira/linear for the layout.
|
|
|
|
**Frontend**:
|
|
- Hooks live under `hooks/domains/<name>/`, **not** `components/<name>/`.
|
|
- Use `hooks/domains/integrations/use-integration-availability.ts` and `use-integration-enabled.ts` — each integration's `useXAvailable` / `useXEnabled` should be a one-line wrapper passing the storage key + sync event + config-fetch function.
|
|
- Settings page reuses `<IntegrationAuthStatusBanner>` (`components/integrations/auth-status-banner.tsx`).
|
|
- "Auth required / reconnect" UI reuses `<IntegrationAuthErrorMessage>` (`components/integrations/auth-error-message.tsx`) — supply the integration's display name, regex check, and reconnect href.
|
|
- Link / import popovers reuse `<ValidatedPopover>` (`components/integrations/validated-popover.tsx`) — supply the icon, label, key regex, fetch function, and success callback.
|
|
|
|
**Where Jira and Linear deliberately diverge:**
|
|
- Issue model: Jira uses transitions + JQL; Linear uses state IDs + structured filters. Don't merge these schemas — the upstream APIs are genuinely different.
|
|
- Watch filter persistence: Jira stores the JQL string verbatim; Linear stores the structured `SearchFilter` as JSON in `filter_json` (Linear has no JQL equivalent). The orchestrator emits `NewJiraIssueEvent` / `NewLinearIssueEvent` respectively and dedups by issue key (Jira) vs identifier (Linear).
|
|
- Health column extras: Linear's `linear_configs` row carries an `org_slug` captured from successful probes; Jira's row does not.
|
|
|
|
---
|
|
|
|
## Maintaining This File
|
|
|
|
This file is read by AI coding agents (Claude Code via `CLAUDE.md` symlink, Codex via `AGENTS.md`). If your changes make any section of this file outdated or inaccurate - e.g., you add/remove/rename packages, change architectural patterns, add new adapters, modify store slices, or change conventions - **update the relevant sections of this file as part of the same PR**. Keep descriptions concise and factual. Do not add speculative or aspirational content.
|
|
|
|
---
|
|
|
|
**Last Updated**: 2026-05-03
|