Kōbō (工房) — Japanese for workshop. A multi-workspace agent manager for Claude Code.
Note
🚧 Active development — breaking changes may still land on develop. The database layer ships with forward-only migrations and a timestamped pre-migration backup of kobo.db before any schema change, so upgrades preserve your data even across invasive refactors.
Kōbō lets you delegate multiple coding missions to Claude Code agents in parallel. Each workspace lives in its own isolated git worktree with its own branch, its own Claude session, optionally its own dev server, and a custom MCP tools server the agent uses to track progress. A Vue 3 dashboard shows live agent output, tasks, acceptance criteria, and git state across every workspace.
Think of it as an apprentice's hall: you hand out missions, each apprentice sets up their own workbench, and you watch them work from a single control surface.
- Isolated git worktrees — every workspace runs on its own branch in its own directory, so concurrent Claude sessions never step on each other
- Pluggable agent engine — Kōbō talks to agents through an
AgentEnginecontract with a normalisedAgentEventstream (src/server/services/agent/engines/). Theclaude-codeengine runs on the official@anthropic-ai/claude-agent-sdk; adding a second runtime (e.g. Codex) only requires a new adapter, not a rewrite of the UI or orchestration layer - Interactive
AskUserQuestion— when the agent invokesAskUserQuestion, Kōbō pauses the session via the SDK'sdeferpattern, surfaces a question panel in the UI, and resumes the agent once the user answers. The session does not occupy any resources while it waits - Rich chat feed — live streaming text, thinking blocks, inline tool calls with expandable diffs for Edit/Write, per-turn session cards, markdown rendering, jump-to-previous-user-message button, and infinite scroll-up over persisted history
- Task & acceptance criteria tracking — the agent reports progress through a dedicated MCP server (
kobo-tasks) that reads and updates tasks directly from the SQLite database - Documents panel — tree view in the right drawer that surfaces every AI-generated markdown file under
docs/plans/,docs/superpowers/, and.ai/thoughts/. Paths mentioned in chat messages are auto-detected against the catalogue and become one-click deep-links into the panel - Git panel with inline diff viewer — Monaco-powered side-by-side / inline diff of the working branch against its source, with file tree (same q-tree as Documents), inline rebase/merge conflict resolution, and a clean action bar:
Syncsplit-button (pull / rebase / merge),Push,Diff,Create PR - Notion integration — pull workspace missions straight from Notion pages, extract markdown, and use it as the source of truth for acceptance criteria. Right-click a Notion-sourced workspace to jump back to its source page in one click
- Sentry integration — paste a Sentry issue URL to spin up a dedicated "fix workspace" with the stacktrace, tags, and offending spans written to
.ai/thoughts/SENTRY-<id>.md; the agent is primed with a TDD fix workflow and has access to the Sentry MCP tools for deeper digging. Right-click reopens the Sentry issue in Sentry's UI - Per-workspace dev servers — start/stop Docker or Node dev servers scoped to each branch, with log streaming
- Conventional-commit enforcement — project-level git conventions are written to
.ai/.git-conventions.mdinside every workspace so Claude follows them during commits - Pull request automation — one-click
push,pull,open-pr, and "change PR base" endpoints integrate with the GitHub CLI, using a configurable prompt template - Multi-session support — create multiple Claude agent sessions per workspace, each with its own chat history; resume completed sessions via
--resume; sessions are named and persisted in localStorage - Prompt templates — personal library of reusable prompts with variable substitution (
{working_branch},{commit_count}, etc.), insertable from the chat input via/autocomplete; editable in Settings > Templates - Favorites and tags — pin workspaces to the top via right-click favourite, organise with per-workspace tags filterable from the sidebar; a global tag catalogue keeps colours consistent across workspaces
- Health panel + config export/import — inspect backend health (agent sessions, migration state, dev servers, DB size) and roundtrip your Kōbō config (settings, templates, skills) between machines via JSON
- Account-level quota panel — a colored mini-bar badge in the chat footer shows the current Claude Code 5-hour and 7-day usage, fed by a backend service that polls Anthropic's OAuth usage endpoint every 60 seconds. Click to open a popover with full bars, reset times, a "Refresh now" button, and a one-click jump to the Stats tab. Pluggable per-provider (Codex-ready), persisted in SQLite so the badge is populated on cold start, and account-level so it's the same across workspaces sharing the same engine
- Resizable right drawer — drag-to-resize horizontally and vertically, with tab state and split ratio persisted to localStorage
- Soft interrupt — pause an agent mid-execution (SIGINT, like pressing Escape in Claude Code) without killing the process; the agent stops the current tool and waits for the next message
- Archive instead of delete — soft-remove workspaces without losing the worktree, branches, or history; unarchive restores the exact pre-archive state
- Auto-loop mode — opt-in, per-workspace: when enabled, Kōbō spawns a fresh Claude session for the next pending task after every
session:ended, walking through the task list until all aredone. Stops automatically on error, on stall (3 consecutive sessions with no task completed), or when the user clicks Stop. A grooming step (/kobo-prep-autoloop) ensures tasks are atomic before the loop runs; Notion-imported workspaces with both todos and acceptance criteria are auto-unlocked. E2E grooming — when a project declares an E2E framework in Settings (Cypress, Playwright, Vitest, etc.), the grooming phase injects an[E2E]test sub-task between every parent task; each iteration then runs the matching E2E suite as part of its acceptance check - Attach existing worktrees — Kōbō detects orphan worktrees under
.worktrees/(created outside Kōbō, or left over from an earlier install) and lets you attach them to a new workspace from the creation form, picking up the existing branch and folder instead of cloning a new one - Quota-aware retry backoff — when a Claude rate limit is hit mid-session, Kōbō schedules the retry at the actual reset time reported by the API (via
rate_limit.info.buckets[].resetsAt), falling back to a 15 → 30 → 60 → 180 → 300 min ladder only when the reset info is missing or implausible - Scheduled wakeups — the
ScheduleWakeuptool is honoured server-side: Kōbō persists the wakeup in SQLite, rehydrates on restart, and respawns the agent with--resumeat the target time
- Backend — Node.js ≥ 20, Hono, better-sqlite3, ws,
@modelcontextprotocol/sdk - Frontend — Vue 3, Quasar 2, Pinia,
vue-router, Monaco Editor (git diff viewer),marked+dompurify(markdown rendering) - Tooling — TypeScript, Vitest, Biome (lint + format),
tsxfor dev - Storage — single SQLite file (
~/.config/kobo/kobo.dbby default, overridable viaKOBO_HOME) with WAL mode and forward-only migrations
- Node.js ≥ 20
- Claude Code authenticated via
claude /loginonce. TheclaudeCLI is no longer required at runtime — Kōbō embeds the official@anthropic-ai/claude-agent-sdk, which reuses the same login. - Git
- Optional: Docker (if you configure per-workspace dev servers)
- Optional:
ghCLI (if you use the PR automation) - Optional: a Notion integration token (only if you want to import workspace missions from Notion pages — see Notion integration)
- Optional: a Sentry auth token (only if you want to create fix workspaces from Sentry issue URLs — see Sentry integration)
SERVER_PORT=9998 PORT=9999 npx @loicngr/kobo@latestThat's it. npm downloads the package, installs dependencies, starts the Kōbō server on the port you specified, and serves the web UI at http://localhost:9999. Data is persisted to ~/.config/kobo/ (overridable via KOBO_HOME).
On first launch Kōbō creates ~/.config/kobo/ if it doesn't exist. If you have not yet logged in to Claude Code (claude /login), the SDK will prompt for an ANTHROPIC_API_KEY instead — log in once to share the same authentication across Claude Code, the embedded SDK, and the quota poller.
git clone https://github.com/loicngr/kobo.git
cd kobo
npm install
(cd src/client && npm install)npm run dev:allThis starts the Hono backend on port 3300 (via tsx watch, with KOBO_HOME=./data so dev uses the repo-local data directory and never touches your real ~/.config/kobo/) and the Quasar dev server on port 8080 concurrently. Open http://localhost:8080 in your browser.
You can run a production-installed Kōbō (npx @loicngr/kobo) alongside a dev server without any conflict — they use different data directories by design.
To run them separately:
npm run dev # backend only (KOBO_HOME=./data automatically)
npm run dev:client # frontend onlynpm run build # builds client + server
npm start # runs the compiled servernpm test # backend vitest suite (950+ tests)
npm run test:client # client vitest suite (Pinia stores + pure utils, 85+ tests)
npm run test:all # backend + client suites
npm run lint # biome check (lint + format verification)
npm run lint:fix # biome check with safe auto-fixes
npm run format # biome format --write
npx tsc --noEmit # server type checkKōbō can pull the content of a Notion page (title, body, checklists) and turn it into tasks and acceptance criteria when you create a workspace. This feature is opt-in and requires you to configure your own Notion credentials — Kōbō does not ship an API key.
Under the hood, Kōbō spawns the official @notionhq/notion-mcp-server as a child process and talks to it over stdio using the Model Context Protocol. The package is fetched via npx -y @notionhq/notion-mcp-server the first time you trigger an import, so there is nothing to install manually — only a token to provide.
- Go to https://www.notion.so/profile/integrations and create a new internal integration
- Give it a name (e.g.
kobo) and the capabilities you need (at minimum: Read content) - Copy the internal integration secret (format
ntn_...orsecret_...) - Open the Notion page you want to import, click … → Connections → Add connection → select your integration. Kōbō can only read pages that are explicitly shared with the integration.
Kōbō reads the token from the first source available, in this order:
NOTION_API_TOKENenvironment variableNOTION_TOKENenvironment variable~/.claude.json— if you already have the Notion MCP configured for Claude Code, Kōbō reads the token frommcpServers.notion.env.NOTION_TOKEN(orNOTION_API_TOKEN). This is the recommended setup — one token configured once, shared by both Claude Code and Kōbō.
Example: configure Notion MCP in Claude Code (one-time setup that also unlocks Kōbō's Notion import):
claude mcp add notion -s user -e NOTION_TOKEN=ntn_your_token_here -- npx -y @notionhq/notion-mcp-serverOr launch Kōbō with the token inline:
NOTION_API_TOKEN=ntn_your_token_here PORT=9999 npx @loicngr/kobo@latestIf you need to pin a specific version of the Notion MCP server, use a fork, or avoid npx, set these env vars before launching Kōbō:
NOTION_MCP_COMMAND— the binary to run (default:npx)NOTION_MCP_ARGS— space-separated arguments (default:-y @notionhq/notion-mcp-server)
Without a valid token configured, the Notion import field in the workspace creation form will return an error when you click Refresh or submit a Notion URL — the rest of Kōbō (workspaces, agents, tasks, Git integration) keeps working independently.
Kōbō can turn a Sentry issue into a dedicated "fix workspace" — you paste the issue URL at workspace creation and Kōbō extracts the stacktrace, culprit, tags, offending spans and extra context, writes them as a local markdown file inside the worktree (.ai/thoughts/SENTRY-<id>.md), and primes the Claude agent with a TDD fix workflow that points at that file. The agent also keeps access to the Sentry MCP tools (search_issue_events, get_issue_tag_values, get_sentry_resource) so it can dig deeper on its own. This feature is opt-in and reuses the Sentry MCP configuration you already have for Claude Code — Kōbō does not manage a Sentry token separately.
Under the hood, Kōbō spawns the official @sentry/mcp-server as a child process using the exact command, args, and env from your ~/.claude.json, then calls get_sentry_resource over stdio. No token handling inside Kōbō — if you change the token or the host in your Claude Code config, Kōbō follows automatically.
- In Sentry, go to Settings → Developer Settings → Custom Integrations (or User Auth Tokens for personal use)
- Create a token with at least these scopes:
project:read,event:read,org:read - Copy the token (format
sntryu_...for user tokens)
The recommended setup is to register the Sentry MCP once in Claude Code — Kōbō picks it up automatically:
claude mcp add sentry -s user \
-e SENTRY_ACCESS_TOKEN=sntryu_your_token_here \
-e SENTRY_HOST=your-org.sentry.io \
-- npx -y @sentry/mcp-server@latestFor self-hosted Sentry, set SENTRY_HOST to your Sentry hostname (e.g. sentry.mycompany.com).
Kōbō reads ~/.claude.json and uses the first entry under mcpServers whose key contains sentry (case-insensitive) and is not disabled. This means:
- A single
sentryentry → used as-is - Multiple entries whose key contains
sentry→ the first matching non-disabled key wins - Toggle
"disabled": trueon an entry to make Kōbō skip it
- In the workspace creation form, click Import Sentry
- Paste the issue URL (e.g.
https://your-org.sentry.io/issues/112081699) - Submit — Kōbō extracts the issue, writes
.ai/thoughts/SENTRY-<numericId>.md, creates aFix: <title>task, and boots the agent with the fix workflow
The Sentry issue Short-ID (e.g. ACME-API-3 — the canonical identifier Sentry assigns to each issue) is used as the ticket prefix for the working branch (e.g. fix/ACME-API-3--slow-db-query or bugfix/ACME-API-3--slow-db-query, depending on the branch prefix you chose at creation). The Short-ID is also what Sentry recognises in commit messages like Fixes ACME-API-3 to auto-close the issue on merge. The local copy of the issue is written to .ai/thoughts/SENTRY-<shortId>.md (e.g. SENTRY-ACME-API-3.md). When Sentry is active, the description field becomes optional — the extracted context is enough to start work.
If the MCP server is slow to initialize (e.g. cold npx fetch, self-hosted host validation), bump the handshake timeout with KOBO_MCP_INIT_TIMEOUT_MS (default: 30000).
Without a valid Sentry MCP configured in ~/.claude.json, the Sentry import field returns a clear error when you submit — the rest of Kōbō keeps working.
For the best experience, we recommend installing the superpowers plugin in Claude Code. Kōbō is designed to work well with it out of the box:
- Brainstorming → spec → plan → execute workflow — superpowers produces design specs in
docs/superpowers/specs/and implementation plans indocs/superpowers/plans/; Kōbō's Plan browser (right-side drawer) lists both so you can review them without leaving the UI - Subagent-driven development — executes plans task-by-task via parallel subagents; Kōbō surfaces sub-agent activity in the chat feed and the Agent busy banner so you always know what's running
- Test-driven development, systematic debugging, code review — all integrated with Kōbō's task tracking and git workflow
Install inside Claude Code:
/plugin marketplace add obra/superpowers-marketplace
/plugin install superpowers@superpowers-marketplaceThen start a new workspace in Kōbō — the agent will pick up the skills automatically.
src/
├── server/ # Hono backend
│ ├── index.ts # app bootstrap + WS upgrade
│ ├── db/ # SQLite schema, migrations, singleton
│ ├── services/
│ │ ├── agent/ # agent engine abstraction (replaces agent-manager.ts)
│ │ │ ├── orchestrator.ts # per-workspace engine map, retry/quota, watchdog, public API
│ │ │ ├── session-controller.ts # lifecycle wrapper around one AgentEngine instance
│ │ │ ├── event-router.ts # maps engine AgentEvent stream to WS emit + DB side-effects
│ │ │ └── engines/claude-code/ # spawn + NDJSON stream-parser + args-builder + mcp-config + capabilities
│ │ ├── content-migration-service.ts # legacy ws_events → normalised AgentEvent rows, with DB backup
│ │ ├── usage/ # pluggable quota provider, 60s poller, persistence, WS broadcast
│ │ └── … # workspace, dev-server, ws, notion, sentry, settings, pr-template
│ ├── routes/ # Hono handlers (workspaces, engines, migration, templates, usage, …)
│ └── utils/ # git-ops, process-tracker, paths
├── shared/ # modules shared by backend and frontend (e.g. model catalogue)
├── client/ # Vue 3 + Quasar SPA
│ └── src/
│ ├── stores/ # Pinia: workspace, websocket, agent-stream, migration, settings, …
│ ├── components/ # ActivityFeed, TurnCard, WorkspaceList, ChatInput, GitPanel, …
│ ├── services/ # agent-event-view (foldEvents), conversation-turns (groupIntoTurns), inline-diff
│ ├── pages/ # WorkspacePage, CreatePage, SettingsPage
│ └── router/
├── mcp-server/ # standalone MCP server spawned per workspace
│ ├── kobo-tasks-server.ts # entry point, registers list_tasks & mark_task_done
│ └── kobo-tasks-handlers.ts # pure handlers over SQLite
└── __tests__/ # Vitest suite (engines, orchestrator, migration, routes, …)
See AGENTS.md for a deeper dive into conventions, data model, WebSocket protocol, and contribution guidelines.
| Table | Purpose |
|---|---|
workspaces |
the unit of work — branch, status, model, engine, archived_at, favorited_at, tags, Notion link, … |
tasks |
workspace sub-items — tasks and acceptance criteria |
agent_sessions |
agent runs — pid, engine_session_id, lifecycle |
ws_events |
persisted WebSocket events (chat history, agent:event stream, user messages) for replay on reconnect |
usage_snapshots |
latest quota snapshot per provider (one row per provider_id) — populated by the 60s polling loop, used for cold-start hydration of the chat-footer quota badge |
Each workspace spawns its own kobo-tasks MCP server as a child process of the Claude Code agent. It exposes two tools:
list_tasks()— returns all tasks & acceptance criteria for the current workspace with their IDs and statusmark_task_done(task_id)— marks a task as done and notifies the backend over HTTP so the UI updates live
The MCP server reads and writes the same SQLite database as the main backend. Isolation between workspaces is enforced via the KOBO_WORKSPACE_ID environment variable passed at spawn time and validated on every query.
Kōbō reads settings from ~/.config/kobo/settings.json (or falls back to defaults). Global settings cascade into per-project overrides:
defaultModel— Claude model to use (e.g.claude-opus-4-6)prPromptTemplate— template rendered when opening a PR via the/open-prendpoint; supports{{pr_number}},{{pr_url}},{{branch_name}},{{diff_stats}},{{commits}}, etc.gitConventions— markdown-formatted git conventions written to.ai/.git-conventions.mdin every workspace so the agent follows them when committingdevServer— per-projectstartCommand/stopCommandfor launching workspace-scoped dev serverse2e— per-project E2E test framework (cypress,playwright,jest,vitest,other, or none) plus an optional skill name and prompt; consumed by the auto-loop grooming step to inject[E2E]test sub-tasks alongside parent tasksfinalization— per-project free-form prompt that runs as the very last auto-loop iteration. The grooming step injects a[FINAL]-prefixed task at the end of the list whose iteration block is replaced by this prompt. Default content asks the agent to run linters, type-checkers, and tests. Empty string disables the feature.
This is a personal tool, but PRs and issues are welcome. Before submitting:
- Read
AGENTS.md— it covers the commit rules, branching model, and code conventions - Run
npm run lint,npx tsc --noEmit, andnpm testlocally - Base your branch on
develop(notmain); PRs targetdevelop
CI runs lint + type check + tests on every PR to develop.
GNU General Public License v3.0 or later. See LICENSE for the full text.
Kōbō links against better-sqlite3, @modelcontextprotocol/sdk, Vue, Quasar, and other open-source libraries — see package.json for the full list.