Architecture
This page covers Kangentic’s internal architecture for contributors and advanced users who want to understand how the system works under the hood.
High-Level Overview
Section titled “High-Level Overview”Kangentic is an Electron application with a React frontend and a Node.js backend. The architecture follows Electron’s process model:
┌─────────────────────────────────┐│ Main Process ││ ┌───────────┐ ┌─────────────┐ ││ │ Session │ │ Worktree │ ││ │ Manager │ │ Manager │ ││ ├───────────┤ ├─────────────┤ ││ │ Transition│ │ Database │ ││ │ Engine │ │ (SQLite) │ ││ └───────────┘ └─────────────┘ │└──────────┬──────────────────────┘ │ IPC (contextBridge)┌──────────┴──────────────────────┐│ Renderer Process ││ ┌───────────┐ ┌─────────────┐ ││ │ Kanban │ │ Terminal │ ││ │ Board │ │ (xterm.js) │ ││ └───────────┘ └─────────────┘ │└─────────────────────────────────┘Process Model
Section titled “Process Model”Main Process
Section titled “Main Process”The main process handles all system-level operations:
| Component | Role |
|---|---|
| Session Manager | Tracks agent lifecycle (spawn, suspend, resume, kill). Manages PTY instances via node-pty. Maintains session state and scrollback buffers. |
| Transition Engine | Executes actions when tasks move between columns. Resolves permission modes. Handles spawn, kill, command injection, and worktree actions. |
| Worktree Manager | Creates and removes git worktrees. Manages branch naming (kanban/{slug}-{id}) and cleanup. |
| Database | SQLite with WAL mode for concurrent reads and crash-safe writes. Two databases: global (projects, config) and per-project (swimlanes, tasks, sessions). |
| Config Manager | Loads, saves, and merges configuration across global, project, and per-swimlane levels. |
| Agent Adapters | Per-agent subfolders under src/main/agent/adapters/ (claude/, codex/, gemini/, aider/, cursor/, copilot/, warp/). Each adapter handles its CLI’s detection, command building, hooks, session resume, and activity strategy behind the shared AgentAdapter interface. |
| Hook Manager | Injects agent-specific hooks (e.g., Claude Code’s PreToolUse/PostToolUse/Stop, Gemini’s BeforeAgent/AfterAgent, Copilot’s inline hooks) for activity detection. Hook files are reference-counted so concurrent sessions don’t clobber each other. |
Renderer Process
Section titled “Renderer Process”The renderer is a React application that provides the UI:
| Component | Role |
|---|---|
| Kanban Board | Drag-and-drop board with swimlane columns. Built with @dnd-kit for smooth interactions. |
| Terminal | xterm.js instances for each active session. Receives data from the main process via IPC. |
IPC Communication
Section titled “IPC Communication”The main and renderer processes communicate via Electron’s contextBridge API. IPC channels are organized by domain:
| Domain | Channels | Description |
|---|---|---|
| Projects | project:list, project:create, project:delete, project:open, project:getCurrent, project:openByPath, project:searchEntries, project:reorder, project:setGroup, project:rename, project:setDefaultAgent, project:autoOpened | Multi-project management |
| Project Groups | projectGroup:list, projectGroup:create, projectGroup:update, projectGroup:delete, projectGroup:reorder, projectGroup:setCollapsed | Sidebar group management |
| Tasks | task:list, task:create, task:update, task:delete, task:move, task:list-archived, task:unarchive, task:bulk-delete, task:bulk-unarchive, task:switchBranch, task:autoMoved, task:createdByAgent, task:updatedByAgent, task:deletedByAgent, task:spawnProgress | Task CRUD and movement |
| Attachments | attachment:list, attachment:add, attachment:remove, attachment:getDataUrl, attachment:open | Task image attachments |
| Swimlanes | swimlane:list, swimlane:create, swimlane:update, swimlane:delete, swimlane:reorder, swimlane:updatedByAgent | Column management |
| Actions/Transitions | action:list, action:create, action:update, action:delete, transition:list, transition:set, transition:getFor | Workflow automation |
| Sessions | session:spawn, session:kill, session:write, session:resize, session:list, session:getScrollback, session:suspend, session:resume, session:reset, session:idleTimeout, session:getSummary, session:listSummaries, session:spawnTransient, session:killTransient, session:getPeriodStats, session:setFocused | Agent lifecycle |
| Session Events | session:data, session:firstOutput, session:exit, session:usage, session:getUsage, session:activity, session:getActivity, session:event, session:getEvents, session:getEventsCache, session:status | Real-time streaming |
| Config | config:get, config:getGlobal, config:set, config:getProject, config:setProject, config:getProjectByPath, config:setProjectByPath, config:syncDefaultToProjects | Settings management |
| Agent | agent:detect, agent:listCommands, agent:list | Agent detection and listing |
| Handoffs | handoff:list | Cross-agent handoff history |
| Shell | shell:getAvailable, shell:getDefault, shell:openPath, shell:openExternal, shell:exec | Shell detection and utilities |
| Git | git:detect, git:listBranches, git:diffFiles, git:fileContent, git:diffSubscribe, git:diffUnsubscribe, git:diffChanged, git:checkPendingChanges | Git operations and diff streaming |
| Dialog | dialog:selectFolder | Native file dialogs |
| Window | window:minimize, window:maximize, window:close, window:flashFrame, window:isFocused | Window controls |
| Analytics | analytics:trackRendererError | Error telemetry |
| App | app:getVersion | App metadata |
| Notifications | notification:show, notification:clicked | Desktop notifications |
| Board Config | boardConfig:exists, boardConfig:export, boardConfig:apply, boardConfig:changed, boardConfig:getShortcuts, boardConfig:setShortcuts, boardConfig:shortcutsChanged, boardConfig:setDefaultBaseBranch | kangentic.json import/export |
| Backlog | backlog:list, backlog:create, backlog:update, backlog:delete, backlog:reorder, backlog:bulk-delete, backlog:promote, backlog:demote, backlog:renameLabel, backlog:deleteLabel, backlog:remapPriorities, backlog:changedByAgent, backlog:labelColorsChanged | Backlog CRUD and labels |
| Backlog Import | backlog:importCheckCli, backlog:importFetch, backlog:importExecute, backlog:importSourcesList, backlog:importSourcesAdd, backlog:importSourcesRemove | External issue import |
| Backlog Attachments | backlogAttachment:list, backlogAttachment:add, backlogAttachment:remove, backlogAttachment:getDataUrl, backlogAttachment:open | Backlog item attachments |
| Clipboard | clipboard:saveImage | Clipboard image capture |
| Updater | updater:check, updater:install, updater:downloaded | Auto-update lifecycle |
All handle calls are async. Real-time updates (terminal data, session status, activity state) use event-based callbacks via ipcMain.on / webContents.send.
Database Schema
Section titled “Database Schema”Kangentic uses two SQLite databases:
Global Database
Section titled “Global Database”-- Projects across the workspaceCREATE TABLE projects ( id TEXT PRIMARY KEY, name TEXT NOT NULL, path TEXT NOT NULL, github_url TEXT, default_agent TEXT NOT NULL DEFAULT 'claude', position INTEGER NOT NULL DEFAULT 0, last_opened TEXT NOT NULL, created_at TEXT NOT NULL);
CREATE TABLE global_config ( key TEXT PRIMARY KEY, value TEXT NOT NULL);Per-Project Database
Section titled “Per-Project Database”-- Board columns (swimlanes)CREATE TABLE swimlanes ( id TEXT PRIMARY KEY, name TEXT NOT NULL, role TEXT, -- 'backlog', 'done', or NULL position INTEGER NOT NULL, color TEXT NOT NULL DEFAULT '#3b82f6', icon TEXT DEFAULT NULL, -- Lucide icon name is_archived INTEGER NOT NULL DEFAULT 0, is_ghost INTEGER NOT NULL DEFAULT 0, permission_mode TEXT DEFAULT NULL, auto_spawn INTEGER NOT NULL DEFAULT 1, auto_command TEXT DEFAULT NULL, plan_exit_target_id TEXT DEFAULT NULL, created_at TEXT NOT NULL);
-- Kanban tasksCREATE TABLE tasks ( id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', swimlane_id TEXT NOT NULL REFERENCES swimlanes(id), position INTEGER NOT NULL, agent TEXT, session_id TEXT, worktree_path TEXT, branch_name TEXT, base_branch TEXT DEFAULT NULL, use_worktree INTEGER DEFAULT NULL, pr_number INTEGER, pr_url TEXT, archived_at TEXT DEFAULT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
-- Workflow actions (spawn_agent, kill_session, etc.)CREATE TABLE actions ( id TEXT PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL, config_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL);
-- Column transition rules (which actions fire on move)CREATE TABLE swimlane_transitions ( id TEXT PRIMARY KEY, from_swimlane_id TEXT NOT NULL, -- swimlane ID or '*' for any source to_swimlane_id TEXT NOT NULL REFERENCES swimlanes(id), action_id TEXT NOT NULL REFERENCES actions(id), execution_order INTEGER NOT NULL DEFAULT 0);
-- Agent sessionsCREATE TABLE sessions ( id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id), session_type TEXT NOT NULL, agent_session_id TEXT, -- native session ID from the agent CLI (Claude/Codex/Gemini) command TEXT NOT NULL, cwd TEXT NOT NULL, permission_mode TEXT, prompt TEXT, status TEXT NOT NULL DEFAULT 'running', -- running, suspended, exited, orphaned exit_code INTEGER, suspended_by TEXT DEFAULT NULL, -- 'user' or 'system' started_at TEXT NOT NULL, suspended_at TEXT, exited_at TEXT);
-- Task image/file attachmentsCREATE TABLE task_attachments ( id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, filename TEXT NOT NULL, file_path TEXT NOT NULL, media_type TEXT NOT NULL, size_bytes INTEGER NOT NULL, created_at TEXT NOT NULL);Both databases use WAL (Write-Ahead Logging) mode for:
- Concurrent read access from both main and renderer processes
- Crash safety — committed data survives unexpected termination
- Better write performance for high-frequency updates
Data Flow
Section titled “Data Flow”Starting an Agent
Section titled “Starting an Agent”User drags card → Renderer sends task:move → Transition Engine looks up actions for * → target swimlane → Actions execute in order (e.g., kill_session → spawn_agent) → Worktree Manager creates worktree + branch (if enabled) → agent-resolver picks the effective agent (column override or project default) → Session Manager asks the agent adapter to build a spawn command → Session Manager spawns the CLI via node-pty → Agent adapter writes any required hook files (reference-counted) → Session record created in DB (status: running) → Terminal data flows: agent stdout → IPC session:data → xterm.jsSuspending an Agent
Section titled “Suspending an Agent”User drags card to Done → Renderer sends task:move → Transition Engine fires kill_session action → Session Manager sends the agent's configured exit sequence → Session Manager updates status to "suspended" → Worktree is preserved on disk → Main sends session:status update to RendererResuming an Agent
Section titled “Resuming an Agent”User drags card from Done → Renderer sends task:move → Transition Engine fires spawn_agent action → Agent adapter builds a resume command with the stored native session ID → Session Manager launches the new process → Session Manager updates status to "running" → Terminal reconnects to new process → Agent continues with full context (where resume is supported)Technology Stack
Section titled “Technology Stack”| Component | Technology | Purpose |
|---|---|---|
| Shell | Electron | Cross-platform desktop container |
| UI Framework | React | Component-based UI |
| State Management | React Context + hooks | UI state |
| Board | @dnd-kit | Kanban drag-and-drop interactions |
| Terminal | xterm.js + node-pty | Terminal emulation and PTY management |
| Database | better-sqlite3 | Persistent storage (WAL mode) |
| Agent Runtime | Claude Code, Codex, Gemini, Aider, Cursor, GitHub Copilot, Warp (Oz) CLIs | Pluggable coding-agent adapters, one per CLI |
| Version Control | git worktree | Isolated environments |
| Packaging | electron-builder | Platform installers (NSIS, DMG, deb/rpm) |
| Language | TypeScript | Type safety throughout |