Skip to content

Architecture

This page covers Kangentic’s internal architecture for contributors and advanced users who want to understand how the system works under the hood.

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) │ │
│ └───────────┘ └─────────────┘ │
└─────────────────────────────────┘

The main process handles all system-level operations:

ComponentRole
Session ManagerTracks agent lifecycle (spawn, suspend, resume, kill). Manages PTY instances via node-pty. Maintains session state and scrollback buffers.
Transition EngineExecutes actions when tasks move between columns. Resolves permission modes. Handles spawn, kill, command injection, and worktree actions.
Worktree ManagerCreates and removes git worktrees. Manages branch naming (kanban/{slug}-{id}) and cleanup.
DatabaseSQLite with WAL mode for concurrent reads and crash-safe writes. Two databases: global (projects, config) and per-project (swimlanes, tasks, sessions).
Config ManagerLoads, saves, and merges configuration across global, project, and per-swimlane levels.
Agent AdaptersPer-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 ManagerInjects 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.

The renderer is a React application that provides the UI:

ComponentRole
Kanban BoardDrag-and-drop board with swimlane columns. Built with @dnd-kit for smooth interactions.
Terminalxterm.js instances for each active session. Receives data from the main process via IPC.

The main and renderer processes communicate via Electron’s contextBridge API. IPC channels are organized by domain:

DomainChannelsDescription
Projectsproject:list, project:create, project:delete, project:open, project:getCurrent, project:openByPath, project:searchEntries, project:reorder, project:setGroup, project:rename, project:setDefaultAgent, project:autoOpenedMulti-project management
Project GroupsprojectGroup:list, projectGroup:create, projectGroup:update, projectGroup:delete, projectGroup:reorder, projectGroup:setCollapsedSidebar group management
Taskstask: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:spawnProgressTask CRUD and movement
Attachmentsattachment:list, attachment:add, attachment:remove, attachment:getDataUrl, attachment:openTask image attachments
Swimlanesswimlane:list, swimlane:create, swimlane:update, swimlane:delete, swimlane:reorder, swimlane:updatedByAgentColumn management
Actions/Transitionsaction:list, action:create, action:update, action:delete, transition:list, transition:set, transition:getForWorkflow automation
Sessionssession: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:setFocusedAgent lifecycle
Session Eventssession:data, session:firstOutput, session:exit, session:usage, session:getUsage, session:activity, session:getActivity, session:event, session:getEvents, session:getEventsCache, session:statusReal-time streaming
Configconfig:get, config:getGlobal, config:set, config:getProject, config:setProject, config:getProjectByPath, config:setProjectByPath, config:syncDefaultToProjectsSettings management
Agentagent:detect, agent:listCommands, agent:listAgent detection and listing
Handoffshandoff:listCross-agent handoff history
Shellshell:getAvailable, shell:getDefault, shell:openPath, shell:openExternal, shell:execShell detection and utilities
Gitgit:detect, git:listBranches, git:diffFiles, git:fileContent, git:diffSubscribe, git:diffUnsubscribe, git:diffChanged, git:checkPendingChangesGit operations and diff streaming
Dialogdialog:selectFolderNative file dialogs
Windowwindow:minimize, window:maximize, window:close, window:flashFrame, window:isFocusedWindow controls
Analyticsanalytics:trackRendererErrorError telemetry
Appapp:getVersionApp metadata
Notificationsnotification:show, notification:clickedDesktop notifications
Board ConfigboardConfig:exists, boardConfig:export, boardConfig:apply, boardConfig:changed, boardConfig:getShortcuts, boardConfig:setShortcuts, boardConfig:shortcutsChanged, boardConfig:setDefaultBaseBranchkangentic.json import/export
Backlogbacklog: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:labelColorsChangedBacklog CRUD and labels
Backlog Importbacklog:importCheckCli, backlog:importFetch, backlog:importExecute, backlog:importSourcesList, backlog:importSourcesAdd, backlog:importSourcesRemoveExternal issue import
Backlog AttachmentsbacklogAttachment:list, backlogAttachment:add, backlogAttachment:remove, backlogAttachment:getDataUrl, backlogAttachment:openBacklog item attachments
Clipboardclipboard:saveImageClipboard image capture
Updaterupdater:check, updater:install, updater:downloadedAuto-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.

Kangentic uses two SQLite databases:

-- Projects across the workspace
CREATE 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
);
-- 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 tasks
CREATE 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 sessions
CREATE 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 attachments
CREATE 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
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.js
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 Renderer
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)
ComponentTechnologyPurpose
ShellElectronCross-platform desktop container
UI FrameworkReactComponent-based UI
State ManagementReact Context + hooksUI state
Board@dnd-kitKanban drag-and-drop interactions
Terminalxterm.js + node-ptyTerminal emulation and PTY management
Databasebetter-sqlite3Persistent storage (WAL mode)
Agent RuntimeClaude Code, Codex, Gemini, Aider, Cursor, GitHub Copilot, Warp (Oz) CLIsPluggable coding-agent adapters, one per CLI
Version Controlgit worktreeIsolated environments
Packagingelectron-builderPlatform installers (NSIS, DMG, deb/rpm)
LanguageTypeScriptType safety throughout