@loicngr/kobo 1.11.45 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +17 -2
- package/CHANGELOG.md +8 -0
- package/CODE_OF_CONDUCT.md +16 -0
- package/CONFIGURATION.md +1968 -0
- package/CONTRIBUTING.md +83 -0
- package/DESIGN.md +296 -0
- package/README.md +35 -7
- package/SECURITY.md +54 -0
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/dist/server/db/migrations.js +23 -0
- package/dist/server/db/schema.js +2 -0
- package/dist/server/index.js +6 -0
- package/dist/server/routes/environment.js +18 -0
- package/dist/server/routes/health.js +17 -2
- package/dist/server/routes/integrations.js +29 -0
- package/dist/server/routes/settings.js +5 -0
- package/dist/server/routes/sounds.js +60 -0
- package/dist/server/routes/workspaces.js +46 -12
- package/dist/server/services/agent/engines/codex/options-builder.js +1 -0
- package/dist/server/services/agent/engines/codex/spawn.js +2 -2
- package/dist/server/services/agent/integration-mcp.js +49 -0
- package/dist/server/services/agent/orchestrator.js +6 -2
- package/dist/server/services/auto-loop-service.js +45 -25
- package/dist/server/services/custom-sound-service.js +0 -0
- package/dist/server/services/environment-check-service.js +125 -0
- package/dist/server/services/initial-prompt-template-service.js +8 -21
- package/dist/server/services/integration-config-service.js +65 -0
- package/dist/server/services/legacy-public-prompts.js +112 -0
- package/dist/server/services/notion-service.js +25 -61
- package/dist/server/services/sentry-service.js +17 -12
- package/dist/server/services/settings-defaults.js +5 -0
- package/dist/server/services/settings-service.js +112 -23
- package/dist/server/services/skill-suite-prompts.js +29 -22
- package/dist/server/services/templates-service.js +31 -8
- package/dist/server/services/terminal-service.js +16 -0
- package/dist/server/services/wakeup-service.js +7 -5
- package/dist/server/services/workspace-service.js +25 -4
- package/dist/server/services/worktree-service.js +3 -1
- package/dist/server/utils/git-repo-lock.js +7 -3
- package/dist/server/utils/mcp-client.js +26 -12
- package/dist/server/utils/mcp-process.js +25 -0
- package/dist/server/utils/node-pty-spawn-helper.js +31 -0
- package/dist/server/utils/paths.js +7 -0
- package/dist/shared/auto-loop-prompts.js +6 -2
- package/dist/shared/automatic-admission.js +1 -0
- package/dist/shared/environment.js +1 -0
- package/dist/shared/notification-assets.js +73 -0
- package/dist/shared/skill-suite-prompts.js +6 -3
- package/dist/shared/workflow-policy.js +25 -0
- package/docs/getting-started.md +47 -0
- package/docs/troubleshooting.md +49 -0
- package/package.json +19 -7
- package/src/client/dist/pwa/assets/ActivityFeed-DRgetl1M.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-CnXTnFhe.js → ChangelogPage-BJdICFvz.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-CKylN45q.js → ComparisonPanel-2GLd8KIy.js} +2 -2
- package/src/client/dist/pwa/assets/CreatePage-B9AdNJZj.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-B9C9J9Pw.css +1 -0
- package/src/client/dist/pwa/assets/DashboardPage-CzVOvjSP.js +1 -0
- package/src/client/dist/pwa/assets/DiffViewer-X25vuf83.js +8 -0
- package/src/client/dist/pwa/assets/{DocumentsPanel-BeCttB-x.js → DocumentsPanel-BastjrEm.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-BJBBYeOc.js → DrawerToggleButton-DbipGmBL.js} +1 -1
- package/src/client/dist/pwa/assets/{GitPanel-BFCnyhE2.css → GitPanel-C4wOcZV2.css} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-CnW4iVao.js +3 -0
- package/src/client/dist/pwa/assets/{HealthPage-l88TEk-k.js → HealthPage-aieNQly-.js} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-Cc4XzCAy.css +1 -0
- package/src/client/dist/pwa/assets/MainLayout-DqBbE8o4.js +2 -0
- package/src/client/dist/pwa/assets/{QBtnDropdown-44Mj-y_1.js → QBtnDropdown-BWy-rh45.js} +1 -1
- package/src/client/dist/pwa/assets/{QExpansionItem-Bok1m8Wi.js → QExpansionItem-DfmdBGuW.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-BfqaWakg.js +1 -0
- package/src/client/dist/pwa/assets/{QTooltip-BDihn2aY.js → QTooltip-Cy0T0vk2.js} +1 -1
- package/src/client/dist/pwa/assets/SchedulePanel-BYKSFoFj.css +1 -0
- package/src/client/dist/pwa/assets/SchedulePanel-YQPObRR3.js +1 -0
- package/src/client/dist/pwa/assets/{SearchPage-BwZ2TX1G.js → SearchPage-BoBjmItc.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-0GtDcXRC.js +9 -0
- package/src/client/dist/pwa/assets/{SettingsPage-W6N01tGz.css → SettingsPage-BYdLXJEJ.css} +1 -1
- package/src/client/dist/pwa/assets/SplitWorkspacePage-4wFh65Lh.js +1 -0
- package/src/client/dist/pwa/assets/{TerminalPanel-Cq-FR7A7.js → TerminalPanel-b70x_jqH.js} +4 -4
- package/src/client/dist/pwa/assets/{TouchPan-BQ1cvzRC.js → TouchPan-CcTVcxwd.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-Dz1yEz_z.js → TourReplayButton-CpEMnQS8.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-C5lTHTp2.js +1 -0
- package/src/client/dist/pwa/assets/{WorkspacePage-BbUvR1Vf.css → WorkspacePage--5HsnMQ1.css} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-D_V9gNON.js +4 -0
- package/src/client/dist/pwa/assets/{browser-BmWgBx9M.js → browser-LBbxVwWf.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-6EjBmYYY.js → build-path-tree-DyvtWxb0.js} +1 -1
- package/src/client/dist/pwa/assets/custom-sounds-BpeLZUDW.js +1 -0
- package/src/client/dist/pwa/assets/de-v6i2HlE2.js +17 -0
- package/src/client/dist/pwa/assets/{documents-BZ8bwcjr.js → documents-Bjx8lz-h.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-CK7--ckY.js → editor.api-BC3PbJkx.js} +1 -1
- package/src/client/dist/pwa/assets/efforts-BDHAnNUs.js +9 -0
- package/src/client/dist/pwa/assets/engineFeatures-hlEiwqt-.js +1 -0
- package/src/client/dist/pwa/assets/es-Bc1KgkAT.js +17 -0
- package/src/client/dist/pwa/assets/{expand-template-C8VyjASo.js → expand-template-DLDl1qPx.js} +1 -1
- package/src/client/dist/pwa/assets/{format-LtTLLitJ.js → format-Q94deO3I.js} +7 -7
- package/src/client/dist/pwa/assets/{formatters-Bl9zA7Ai.js → formatters-CWxRHuUu.js} +1 -1
- package/src/client/dist/pwa/assets/fr-ByYPK5vr.js +17 -0
- package/src/client/dist/pwa/assets/{freemarker2-DxezMaje.js → freemarker2-MPhk0eTh.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-DK_Bj6Rx.js → handlebars-C60ru5gR.js} +1 -1
- package/src/client/dist/pwa/assets/{html-DFfsfStD.js → html-ClzchR9j.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-Sy42tm-V.js +1 -0
- package/src/client/dist/pwa/assets/index-CqZ7sDcM.js +20 -0
- package/src/client/dist/pwa/assets/it-BYF_88z6.js +17 -0
- package/src/client/dist/pwa/assets/{javascript-DuNbcaL7.js → javascript-C43UJmMx.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-CM3JKyhv.js → liquid-CYTrvTKF.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-BG3zYwNF.js → mdx-CxsXOvkQ.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-DLscZGH-.js → monaco-contributions-Cl4-iVpK.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-DnhZHwXT.js → monaco.contribution-DkjLUmyA.js} +2 -2
- package/src/client/dist/pwa/assets/network-auth-CXQn2wO7.js +1 -0
- package/src/client/dist/pwa/assets/notify-theme-BrueBr2j.js +1 -0
- package/src/client/dist/pwa/assets/{pending-diff-open-dYIFdS4r.js → pending-diff-open-B4POItbR.js} +1 -1
- package/src/client/dist/pwa/assets/permissionModes-B-ciuUbz.js +36 -0
- package/src/client/dist/pwa/assets/{project-color-R_1je4vW.js → project-color-CM3hflLH.js} +1 -1
- package/src/client/dist/pwa/assets/{python-COs6pHk2.js → python-s2IMpsoV.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-Jl1VqMsk.js → razor-DVNXiVBh.js} +1 -1
- package/src/client/dist/pwa/assets/render-chat-markdown-TU6CDfD8.js +69 -0
- package/src/client/dist/pwa/assets/{typescript-BU_TUuAM.js → typescript-BYT_8ABE.js} +1 -1
- package/src/client/dist/pwa/assets/use-tours-CNe8qb1q.js +2 -0
- package/src/client/dist/pwa/assets/workspace-templates-tBxXE52A.js +1 -0
- package/src/client/dist/pwa/assets/{xml-gFFsobxE.js → xml-BIG-fmxk.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-DaXaqaGD.js → yaml-D7gu2CQh.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sounds/neutral.wav +0 -0
- package/src/client/dist/pwa/sounds/ready.wav +0 -0
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-CLXjdqS8.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-SoCcvxuD.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-TY9g3hZb.css +0 -1
- package/src/client/dist/pwa/assets/DashboardPage-CZQLfoV1.js +0 -1
- package/src/client/dist/pwa/assets/DiffViewer-Csf3WmLc.js +0 -8
- package/src/client/dist/pwa/assets/GitPanel-B-ZHgghQ.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-BAAQLq95.css +0 -1
- package/src/client/dist/pwa/assets/MainLayout-Cf4Q9x1u.js +0 -2
- package/src/client/dist/pwa/assets/QDrawer-B4qHw8wt.js +0 -1
- package/src/client/dist/pwa/assets/QSelect-BbtD5Iks.js +0 -1
- package/src/client/dist/pwa/assets/SchedulePanel-BAA4Aajn.css +0 -1
- package/src/client/dist/pwa/assets/SchedulePanel-DiWCDNZ8.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-BUh0WfNf.js +0 -9
- package/src/client/dist/pwa/assets/SplitWorkspacePage-Bg6nKBcA.js +0 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-BCVOP8vp.js +0 -1
- package/src/client/dist/pwa/assets/WorkspacePage-Cl8gap4b.js +0 -4
- package/src/client/dist/pwa/assets/de-COObokpu.js +0 -17
- package/src/client/dist/pwa/assets/efforts-Bw0o2Bud.js +0 -9
- package/src/client/dist/pwa/assets/engineFeatures-DQO9HHXI.js +0 -1
- package/src/client/dist/pwa/assets/es-By2Q9Tca.js +0 -17
- package/src/client/dist/pwa/assets/fouet-ahh-DdxrUXRj.mp3 +0 -0
- package/src/client/dist/pwa/assets/fr-D-sd0ddn.js +0 -17
- package/src/client/dist/pwa/assets/i18n-aDWy4KHt.js +0 -1
- package/src/client/dist/pwa/assets/index-BLd8DAzg.js +0 -20
- package/src/client/dist/pwa/assets/it-CeK5lyIA.js +0 -17
- package/src/client/dist/pwa/assets/network-auth-BJiPbO5l.js +0 -1
- package/src/client/dist/pwa/assets/notify-theme-D4p3qPkp.js +0 -1
- package/src/client/dist/pwa/assets/permissionModes-B7fuUegU.js +0 -36
- package/src/client/dist/pwa/assets/render-chat-markdown-AaOo0Tgq.js +0 -67
- package/src/client/dist/pwa/assets/use-tours-CVn874kD.js +0 -2
- package/src/client/dist/pwa/assets/workspace-templates-BCKR8TXr.js +0 -1
- package/src/client/dist/pwa/sounds/7eme-compagnie-03.mp3 +0 -0
- package/src/client/dist/pwa/sounds/aller-ftg.mp3 +0 -0
- package/src/client/dist/pwa/sounds/arrete-de-mentir.mp3 +0 -0
- package/src/client/dist/pwa/sounds/arretez-les-messages.mp3 +0 -0
- package/src/client/dist/pwa/sounds/bah-alors-on-est-nul.mp3 +0 -0
- package/src/client/dist/pwa/sounds/basic-notification.mp3 +0 -0
- package/src/client/dist/pwa/sounds/ca_va_peter.mp3 +0 -0
- package/src/client/dist/pwa/sounds/dry-fart.mp3 +0 -0
- package/src/client/dist/pwa/sounds/faaah.mp3 +0 -0
- package/src/client/dist/pwa/sounds/for-shure.mp3 +0 -0
- package/src/client/dist/pwa/sounds/gta-v-death.mp3 +0 -0
- package/src/client/dist/pwa/sounds/hey.mp3 +0 -0
- package/src/client/dist/pwa/sounds/mais-laisse-moi-dormir-zebi.mp3 +0 -0
- package/src/client/dist/pwa/sounds/nan-tu-degages.mp3 +0 -0
- package/src/client/dist/pwa/sounds/nan-wallah-pardon.mp3 +0 -0
- package/src/client/dist/pwa/sounds/ouais-cest-greg.mp3 +0 -0
- package/src/client/dist/pwa/sounds/pas-ca-zinedine.mp3 +0 -0
- package/src/client/dist/pwa/sounds/ta-gueule.mp3 +0 -0
- package/src/client/dist/pwa/sounds/travail_termine.mp3 +0 -0
- package/src/client/dist/pwa/sounds/tu-vas-la-fermer.mp3 +0 -0
- package/src/client/dist/pwa/sounds/warcraft-3-humain-travail.mp3 +0 -0
- package/src/client/dist/pwa/sounds/yaaa.mp3 +0 -0
package/CONFIGURATION.md
ADDED
|
@@ -0,0 +1,1968 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Reference for Kōbō configuration and external integrations. The tables cover the main settings; feature sections document additional keys and behavior. For a quick start see the [README](./README.md); for architecture, conventions, and contribution rules see [`AGENTS.md`](./AGENTS.md).
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Storage layout](#storage-layout)
|
|
8
|
+
- [Environment variables](#environment-variables)
|
|
9
|
+
- [Settings UI](#settings-ui)
|
|
10
|
+
- [Settings navigation](#settings-navigation)
|
|
11
|
+
- [Fresh defaults and Git preferences](#first-run-defaults-and-git-workflow-preferences)
|
|
12
|
+
- [Managed MCPs in agent sessions](#managed-integration-mcps-in-agent-sessions)
|
|
13
|
+
- [Progressive Web App](#progressive-web-app)
|
|
14
|
+
- [Custom change-source-branch script](#custom-change-source-branch-script)
|
|
15
|
+
- [Lifecycle hooks](#lifecycle-hooks)
|
|
16
|
+
- [Unanswered question reminder](#unanswered-question-reminder)
|
|
17
|
+
- [Imported notification sounds](#imported-notification-sounds)
|
|
18
|
+
- [Comparing two engines on one task](#comparing-two-engines-on-one-task)
|
|
19
|
+
- [Workspace templates and duplication](#workspace-templates-and-duplication)
|
|
20
|
+
- [Auto-purge worktree on PR merged](#auto-purge-worktree-on-pr-merged)
|
|
21
|
+
- [Conversation history retention](#conversation-history-retention-opt-in)
|
|
22
|
+
- [Concurrent agents](#concurrent-agents)
|
|
23
|
+
- [Network access](#network-access)
|
|
24
|
+
- [Reachable addresses](#reachable-addresses)
|
|
25
|
+
- [External LLM access through MCP](#external-llm-access-through-mcp)
|
|
26
|
+
- [Kōbō update checks](#kōbō-update-checks)
|
|
27
|
+
- [Docker deployment](#docker-deployment)
|
|
28
|
+
- [Choosing a compose file](#choosing-a-compose-file)
|
|
29
|
+
- [The image](#the-image)
|
|
30
|
+
- [Environment variables (Docker)](#environment-variables-docker)
|
|
31
|
+
- [Volumes](#volumes)
|
|
32
|
+
- [Ports](#ports)
|
|
33
|
+
- [Token / login flow](#token--login-flow)
|
|
34
|
+
- [Quick local test — docker-compose.local.yml](#quick-local-test--docker-composelocalyml)
|
|
35
|
+
- [Rehearsing the reverse-proxy stack — docker-compose.local-traefikyml](#rehearsing-the-reverse-proxy-stack--docker-composelocal-traefikyml)
|
|
36
|
+
- [Real deployment — docker-compose.exampleyml](#real-deployment--docker-composeexampleyml)
|
|
37
|
+
- [Troubleshooting](#troubleshooting)
|
|
38
|
+
- [Agent runtimes](#agent-runtimes)
|
|
39
|
+
- [Claude Code](#claude-code)
|
|
40
|
+
- [OpenAI Codex](#openai-codex)
|
|
41
|
+
- [Permission modes](#permission-modes)
|
|
42
|
+
- [Dev server](#dev-server)
|
|
43
|
+
- [Forge integration](#forge-integration)
|
|
44
|
+
- [Notion integration](#notion-integration)
|
|
45
|
+
- [Sentry integration](#sentry-integration)
|
|
46
|
+
- [Voice transcription](#voice-transcription)
|
|
47
|
+
- [Skill suites](#skill-suites)
|
|
48
|
+
- [superpowers](#superpowers)
|
|
49
|
+
- [gstack](#gstack)
|
|
50
|
+
- [superpowers + gstack](#superpowers--gstack)
|
|
51
|
+
- [custom](#custom)
|
|
52
|
+
- [gbrain (companion MCP)](#gbrain-companion-mcp)
|
|
53
|
+
- [Workspace sorting, split view and absence digest](#workspace-sorting-split-view-and-absence-digest)
|
|
54
|
+
- [Conversation search index](#conversation-search-index)
|
|
55
|
+
- [Audit compatibility notes](#audit-compatibility-notes)
|
|
56
|
+
|
|
57
|
+
## Storage layout
|
|
58
|
+
|
|
59
|
+
Kōbō persists all state under a single home directory, resolved in this order:
|
|
60
|
+
|
|
61
|
+
1. `KOBO_HOME` env var (relative paths resolve against the working directory)
|
|
62
|
+
2. `$XDG_CONFIG_HOME/kobo/`
|
|
63
|
+
3. `~/.config/kobo/`
|
|
64
|
+
|
|
65
|
+
Contents:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
$KOBO_HOME/
|
|
69
|
+
├── kobo.db # SQLite (WAL mode). Forward-only migrations.
|
|
70
|
+
├── kobo.db.backup-<ISO>-<seq> # Daily WAL-safe backup (seven most recent kept).
|
|
71
|
+
├── kobo.db.premigration-* # Separate snapshot before schema changes (five kept).
|
|
72
|
+
├── settings.json # Global and per-project settings.
|
|
73
|
+
├── integrations.json # Private MCP configuration (0600); excluded from settings exports.
|
|
74
|
+
├── sounds/ # Imported notification sounds plus their sounds.json manifest.
|
|
75
|
+
├── templates.json # Prompt templates.
|
|
76
|
+
├── workspace-templates.json # Saved presets for the create form.
|
|
77
|
+
├── skills.json # Cached Claude slash-command catalogue; suite settings live in settings.json.
|
|
78
|
+
└── voice/
|
|
79
|
+
└── models/whisper/ # Downloaded ggml-*.bin models.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A production-installed Kōbō (`npx @loicngr/kobo`) and a dev server can run side by side: `npm run dev` forces `KOBO_HOME=./data` so dev never touches your real config.
|
|
83
|
+
|
|
84
|
+
## Environment variables
|
|
85
|
+
|
|
86
|
+
| Variable | Default | Purpose |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `PORT` | `3000` | HTTP / WebSocket server port. Overridden by `SERVER_PORT` when both are set. |
|
|
89
|
+
| `SERVER_PORT` | — | Takes precedence over `PORT`. Useful for stacking Kōbō next to other tools that also honour `PORT`. |
|
|
90
|
+
| `KOBO_VERBOSE` | — | Set to `1` to show the data directory and successful database backup/rotation details in the terminal. Errors always remain visible. |
|
|
91
|
+
| `NO_COLOR` | — | When present, disables startup banner colors. Redirected output is always plain text. |
|
|
92
|
+
| `KOBO_HOME` | `~/.config/kobo` | Override the storage directory. |
|
|
93
|
+
| `KOBO_ENFORCE_LOCAL_HOME` | — | When set, refuses any `KOBO_HOME` that resolves outside the current directory. Used by `npm run dev` to guarantee dev data lives in `./data`. |
|
|
94
|
+
| `KOBO_MCP_INIT_TIMEOUT_MS` | `30000` | Handshake timeout for the Notion and Sentry MCP servers. Bump it if cold `npx` fetches are slow on your network. |
|
|
95
|
+
| `XDG_CONFIG_HOME` | — | Standard XDG override consulted before `~/.config`. |
|
|
96
|
+
| `NOTION_API_TOKEN` | — | Notion integration token (preferred over `NOTION_TOKEN`). |
|
|
97
|
+
| `NOTION_TOKEN` | — | Fallback Notion token. |
|
|
98
|
+
| `NOTION_MCP_COMMAND` | `npx` | Binary used to launch the Notion MCP server. |
|
|
99
|
+
| `NOTION_MCP_ARGS` | `-y @notionhq/notion-mcp-server` | Space-separated arguments passed to `NOTION_MCP_COMMAND`. |
|
|
100
|
+
| `OPENAI_API_KEY` | — | Codex engine credential. Alternative to `codex login`. |
|
|
101
|
+
| `ANTHROPIC_API_KEY` | — | Claude credential. Alternative to `claude /login`. |
|
|
102
|
+
| `CLAUDE_CONFIG_DIR` | `~/.claude` | Where the Claude SDK reads its auth and MCP config. |
|
|
103
|
+
| `WHISPER_CPP_COMMAND` | `whisper-cli` | Override the whisper.cpp binary if it's not in `PATH` and you don't want to set it via Settings. |
|
|
104
|
+
| `DEBUG_MCP_STDERR` | — | When set, pipe spawned MCP servers' stderr to the Kōbō log. Useful for debugging Notion/Sentry MCP issues. |
|
|
105
|
+
| `KOBO_NETWORK_ACCESS_ENABLED` | — | Set `true` to enable [network access](#network-access) at boot, without going through Settings. Mainly useful for headless/Docker deployments. |
|
|
106
|
+
| `KOBO_NETWORK_ACCESS_BEHIND_PROXY` | — | Set `true` to enable [behind a reverse proxy](#behind-a-reverse-proxy) mode at boot (disables the loopback bypass entirely). Requires `KOBO_NETWORK_ACCESS_ENABLED=true`. |
|
|
107
|
+
| `KOBO_NETWORK_ACCESS_PROXY_HOST` | — | Domain your proxy serves Kōbō under (`kobo.example.com`). Makes the [host check](#reachable-addresses) enforce that name in behind-a-reverse-proxy mode instead of accepting any. A scheme and port are ignored. |
|
|
108
|
+
| `KOBO_DEV_CLIENT_ORIGIN` | — | Origin the Quasar dev server is served from (`http://localhost:8080`), so its page counts as Kōbō's own UI despite the proxy rewriting the `Host`. Set by the `dev` npm script; never set in a published build. |
|
|
109
|
+
| `GH_TOKEN` | — | Non-interactive auth for the `gh` CLI (used by the [forge integration](#forge-integration)). Needed in headless containers where `gh auth login`'s browser flow isn't practical. |
|
|
110
|
+
| `GITLAB_TOKEN` | — | Non-interactive auth for the `glab` CLI, same rationale as `GH_TOKEN`. |
|
|
111
|
+
| `KOBO_WORKTREE_CLEANUP_IMAGE` | `alpine` | Docker image used to reclaim ownership of root-owned files when removing a worktree fails on a permission error. Override with a locally cached image to avoid a pull. |
|
|
112
|
+
|
|
113
|
+
## Settings UI
|
|
114
|
+
|
|
115
|
+
Settings are managed live from the **Settings** page in the UI and persisted to `$KOBO_HOME/settings.json`. The file is structured as:
|
|
116
|
+
|
|
117
|
+
```jsonc
|
|
118
|
+
{
|
|
119
|
+
"global": { /* defaults applied to every project */ },
|
|
120
|
+
"projects": [
|
|
121
|
+
{ "path": "/abs/path/to/repo", /* per-project overrides */ }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The current [navigation map](#settings-navigation) explains where each group lives.
|
|
127
|
+
Workflow and integration drafts survive tab changes; leaving with unsaved changes
|
|
128
|
+
asks for confirmation. Ordinary settings use the main Save action. Direct connection
|
|
129
|
+
credentials use their separate **Replace connection** action and are excluded from
|
|
130
|
+
settings exports.
|
|
131
|
+
|
|
132
|
+
### Global settings
|
|
133
|
+
|
|
134
|
+
| Key | Type | Purpose |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `defaultModel` | `string` | Fallback model when no engine-specific default is set. |
|
|
137
|
+
| `defaultModelByEngine` | `Record<engine, string>` | Per-engine default model (`claude-code` / `codex`). Fresh installs use `auto` for both engines: the runtime chooses its default model. Historical migrations and existing user choices are preserved. The actual model is reported by the engine when the session starts; an Auto configuration label is not a model name. |
|
|
138
|
+
| `workflowPolicy` | `{ commit, push, publish }` | Independent `manual` / `automatic` preferences, snapshotted at workspace creation. Fresh defaults are manual. See [Git preferences](#first-run-defaults-and-git-workflow-preferences). |
|
|
139
|
+
| `skillSuite` | `string` | Standard, an external suite/combination, or custom instructions. Fresh default `standard`; see [Skill suites](#skill-suites). |
|
|
140
|
+
| `defaultPermissionModeByEngine` | `Record<engine, mode>` | Default permission mode per engine. See [Permission modes](#permission-modes). |
|
|
141
|
+
| `dangerouslySkipPermissions` | `boolean` | Disable all approval prompts. **Use with care.** |
|
|
142
|
+
| `prPromptTemplate` | `string` | Template rendered by the `/open-pr` endpoint. Supports `{{pr_number}}`, `{{pr_url}}`, `{{branch_name}}`, `{{diff_stats}}`, `{{commits}}`, etc. |
|
|
143
|
+
| `reviewPromptTemplate` | `string` | Template for the review prompt action. |
|
|
144
|
+
| `notionInitialPromptTemplate` | `string` | Template injected as the first user message when a workspace is created from a Notion page. |
|
|
145
|
+
| `sentryInitialPromptTemplate` | `string` | Template injected as the first user message when a workspace is created from a Sentry issue. |
|
|
146
|
+
| `gitConventions` | `string` | Markdown block written to `.ai/.git-conventions.md` inside every workspace. |
|
|
147
|
+
| `setupScript` | `string` | Shell script run in a worktree after it is created, before the agent starts. Empty disables the setup script. |
|
|
148
|
+
| `cleanupScript` | `string` | Shell script run after a session ends. Empty disables. See `cleanupScriptMode`. |
|
|
149
|
+
| `cleanupScriptMode` | `'idle' \| 'no-tasks'` | When the cleanup script fires: after every session (`idle`) or only when no Kōbō task remains (`no-tasks`). In auto-loop it runs only once every task is done. |
|
|
150
|
+
| `cleanupScriptOnlyOnChanges` | `boolean` | Run the cleanup script only when the worktree has uncommitted changes (modified / added / deleted / untracked files). |
|
|
151
|
+
| `archiveScript` | `string` | Shell script run server-side when a workspace is archived. Empty disables the archive script. |
|
|
152
|
+
| `changeSourceBranchScript` | `string` | Shell script that **replaces** the built-in change-source-branch logic. The global default is Kōbō's bundled script; clearing the effective script hides the UI action. The backend retains a built-in fallback. See [Custom change-source-branch script](#custom-change-source-branch-script). |
|
|
153
|
+
| `editorCommand` | `string` | Command used by the "Open in editor" action (e.g. `code`, `phpstorm`, `cursor`). The worktree path is appended as the last argument. |
|
|
154
|
+
| `browserNotifications` | `boolean` | Trigger Web Notifications when an agent finishes a turn. |
|
|
155
|
+
| `audioNotifications` | `boolean` | Play a sound when an agent finishes a turn. |
|
|
156
|
+
| `whipEnabled` | `boolean` | Enable the interactive whip. Default `false`. |
|
|
157
|
+
| `whipShortcut` | `string` | Portable whip shortcut. Default `mod+shift+x`. |
|
|
158
|
+
| `whipVolume` | `number` | Independent whip volume from `0` to `1`. Default `1`; master `audioNotifications` still mutes playback. |
|
|
159
|
+
| `audioNotificationSound` | `string` | Identifier of the chosen sound. |
|
|
160
|
+
| `audioQuestionSound` | `string` | Sound used when an agent asks a question. |
|
|
161
|
+
| `audioPrCiFailedSound` | `string` | Sound used when CI enters `FAILURE`. |
|
|
162
|
+
| `audioPrCiFailedEnabled` | `boolean` | Enable audio when CI enters `FAILURE`. |
|
|
163
|
+
| `audioPrCiFailedVolume` | `number` | CI-failure sound volume from `0` to `1`. |
|
|
164
|
+
| `audioPrCiRecoveredSound` | `string` | Sound used when CI moves from `FAILURE` to `SUCCESS`. |
|
|
165
|
+
| `audioPrCiRecoveredEnabled` | `boolean` | Enable audio when CI recovers. |
|
|
166
|
+
| `audioPrCiRecoveredVolume` | `number` | CI-recovery sound volume from `0` to `1`. |
|
|
167
|
+
| `audioPrChangesRequestedSound` | `string` | Sound used when a review requests changes. |
|
|
168
|
+
| `audioPrChangesRequestedEnabled` | `boolean` | Enable audio when changes are requested. |
|
|
169
|
+
| `audioPrChangesRequestedVolume` | `number` | Changes-requested sound volume from `0` to `1`. |
|
|
170
|
+
| `audioPrApprovedSound` | `string` | Sound used when a PR becomes approved. |
|
|
171
|
+
| `audioPrApprovedEnabled` | `boolean` | Enable audio when a PR becomes approved. |
|
|
172
|
+
| `audioPrApprovedVolume` | `number` | PR-approved sound volume from `0` to `1`. |
|
|
173
|
+
| `audioPrMergeConflictSound` | `string` | Sound used when a merge conflict appears. |
|
|
174
|
+
| `audioPrMergeConflictEnabled` | `boolean` | Enable audio when a merge conflict appears. |
|
|
175
|
+
| `audioPrMergeConflictVolume` | `number` | Merge-conflict sound volume from `0` to `1`. |
|
|
176
|
+
| `audioPrReadyToMergeSound` | `string` | Sound used when an idle workspace's PR becomes ready to merge. |
|
|
177
|
+
| `audioPrReadyToMergeEnabled` | `boolean` | Enable audio when a PR becomes ready to merge. |
|
|
178
|
+
| `audioPrReadyToMergeVolume` | `number` | Ready-to-merge sound volume from `0` to `1`. |
|
|
179
|
+
| `audioPrMergedSound` | `string` | Sound used when an open PR becomes merged. |
|
|
180
|
+
| `audioPrMergedEnabled` | `boolean` | Enable audio when a PR becomes merged. |
|
|
181
|
+
| `audioPrMergedVolume` | `number` | PR-merged sound volume from `0` to `1`. |
|
|
182
|
+
| `audioNotificationVolume` | `number` | General notification sound volume from `0` to `1`. |
|
|
183
|
+
| `notionMcpKey` | `string` | Override the `~/.claude.json` MCP key used for Notion (defaults to the enabled entry named exactly `notion`). |
|
|
184
|
+
| `sentryMcpKey` | `string` | Override the Sentry MCP key (defaults to the first enabled entry whose key contains `sentry`, case-insensitive). |
|
|
185
|
+
| `notionEnabled` | `boolean` | Enable Notion imports and automations. Defaults to `true`; disabling preserves its configuration and existing workspace links. |
|
|
186
|
+
| `sentryEnabled` | `boolean` | Enable Sentry imports and automations. Defaults to `true`; disabling preserves its configuration and existing workspace links. |
|
|
187
|
+
| `showThinkingBlocks` | `boolean` | Display the latest agent reasoning panel immediately above the chat input. Defaults to `true`. |
|
|
188
|
+
| `notionStatusProperty` | `string` | Notion DB property updated to `notionInProgressStatus` when a workspace starts work on a Notion-backed mission. Empty disables the feature. |
|
|
189
|
+
| `notionInProgressStatus` | `string` | Value written to `notionStatusProperty`. |
|
|
190
|
+
| `notionAssigneeProperty` | `string` | Notion People property to auto-assign to the authenticated user. |
|
|
191
|
+
| `notionUserId` | `string` | Notion user UUID assigned via `notionAssigneeProperty`. |
|
|
192
|
+
| `tags` | `string[]` | Global tag catalogue exposed in the sidebar filters and the workspace-tag picker. |
|
|
193
|
+
| `branchPrefixes` | `string[]` | Git branch prefixes offered on the workspace creation page (stored without the trailing `/`). The first entry is the default selection. |
|
|
194
|
+
| `worktreesPath` | `string` | Root directory where new worktrees are created. Defaults to `.worktrees` (resolved relative to the project). Absolute, `$HOME`, `~`, and `%USERPROFILE%` are accepted; `..` and drive-relative Windows paths (`C:foo`) are rejected. |
|
|
195
|
+
| `worktreesPrefixByProject` | `boolean` | Nest each worktree under a project-named subfolder, preventing collisions when multiple projects share the same `worktreesPath`. |
|
|
196
|
+
| `autoPurgeOnPrMerged` | `boolean` | When a PR transitions to MERGED, also remove the worktree from disk (in addition to archiving). Chat history and PR metadata are preserved. Default `false`. See [Auto-purge worktree on PR merged](#auto-purge-worktree-on-pr-merged). |
|
|
197
|
+
| `voiceEnabled` | `boolean` | Enable local push-to-talk transcription. |
|
|
198
|
+
| `voicePttKey` | `'alt' \| 'ctrl+space'` | Hotkey held to record. |
|
|
199
|
+
| `voiceLanguage` | `string` | `auto` or a 2-letter language code. |
|
|
200
|
+
| `voiceModel` | `string \| null` | Active model name (e.g. `base`). |
|
|
201
|
+
| `voiceCommandPath` | `string` | Override `whisper-cli` path. Empty falls back to `WHISPER_CPP_COMMAND` then `PATH`. |
|
|
202
|
+
| `voiceFfmpegPath` | `string` | Override `ffmpeg` path. Empty falls back to `PATH`. |
|
|
203
|
+
| `voiceTemperature` | `number` | Decoding temperature, `0`–`1`. |
|
|
204
|
+
| `voicePrompt` | `string` | Initial prompt to bias transcription (custom vocabulary, names). |
|
|
205
|
+
|
|
206
|
+
PR/MR event sound settings accept `inherit` (the default, uses
|
|
207
|
+
`audioNotificationSound`) or a bundled sound filename from **Settings →
|
|
208
|
+
Notifications**. Each event has an independent `Enabled` toggle and `Volume`.
|
|
209
|
+
Migration v45 converts a previous `none` selection into a disabled card and
|
|
210
|
+
preserves the effective general volume. Review-decision sounds currently
|
|
211
|
+
require GitHub because the GitLab provider does not expose approval decisions.
|
|
212
|
+
When multiple states change in one poll, every enabled sound is queued and
|
|
213
|
+
played in order.
|
|
214
|
+
|
|
215
|
+
### Per-project settings
|
|
216
|
+
|
|
217
|
+
Projects override a subset of global settings. Anything you set here takes precedence for workspaces created under that project path.
|
|
218
|
+
|
|
219
|
+
| Key | Type | Purpose |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `path` | `string` | Absolute project path (primary key). |
|
|
222
|
+
| `displayName` | `string` | Override the auto-derived name. |
|
|
223
|
+
| `color` | `ProjectColor \| null` | Sidebar accent. |
|
|
224
|
+
| `defaultSourceBranch` | `string` | Source branch new workspaces branch from (e.g. `develop`). |
|
|
225
|
+
| `defaultModel` | `string` | Project-scoped model default. |
|
|
226
|
+
| `dangerouslySkipPermissions` | `boolean` | Project-scoped override. |
|
|
227
|
+
| `prPromptTemplate`, `reviewPromptTemplate`, `notionInitialPromptTemplate`, `sentryInitialPromptTemplate`, `gitConventions` | `string` | Per-project versions of the global templates. Empty inherits the global value. |
|
|
228
|
+
| `setupScript`, `cleanupScript`, `archiveScript` | `string` | Per-project versions of the global lifecycle scripts. Empty inherits the global value. |
|
|
229
|
+
| `changeSourceBranchScript` | `string` | Per-project version of the custom change-source-branch script. Empty inherits the global value. See [Custom change-source-branch script](#custom-change-source-branch-script). |
|
|
230
|
+
| `cleanupScriptMode` | `'' \| 'idle' \| 'no-tasks'` | Per-project override of the cleanup trigger mode. Empty inherits the global mode. |
|
|
231
|
+
| `taskPromptTemplate` | `string` | Prompt used to prefill the mission description when this project is selected. Empty leaves the description without a project-provided prompt. |
|
|
232
|
+
| `forge` | `'auto' \| 'github' \| 'gitlab' \| 'bitbucket-community' \| 'none'` | Which forge provides PR/MR features. `auto` detects from the git remote URL. See [Forge integration](#forge-integration). |
|
|
233
|
+
| `devServer.startCommand` / `stopCommand` | `string` | Per-workspace dev server commands. Docker, npm, or any shell-startable process. See [Dev server](#dev-server) for the status/URL contract. |
|
|
234
|
+
| `e2e.framework` | `'cypress' \| 'playwright' \| 'jest' \| 'vitest' \| 'other' \| ''` | E2E framework auto-loop grooming should target. |
|
|
235
|
+
| `e2e.skill` | `string` | Optional skill name injected into the E2E grooming prompt. |
|
|
236
|
+
| `e2e.prompt` | `string` | Free-form prompt appended to every `[E2E] ` sub-task. |
|
|
237
|
+
| `finalization.prompt` | `string` | Runs as the very last auto-loop iteration (`[FINAL]`-prefixed task). Empty uses the built-in mandatory final verification. |
|
|
238
|
+
|
|
239
|
+
## Progressive Web App
|
|
240
|
+
|
|
241
|
+
Production builds (`npm run build`, `npm start`, or `npx @loicngr/kobo`) serve
|
|
242
|
+
the Quasar PWA build. Open Kōbō in a browser and use **Install app** or **Add
|
|
243
|
+
to Home Screen** to install it as a desktop or mobile application.
|
|
244
|
+
|
|
245
|
+
The PWA caches the application shell for fast reopening, but agent sessions,
|
|
246
|
+
chat history, HTTP requests, and WebSocket updates still require a reachable
|
|
247
|
+
Kōbō server. Kōbō shows an offline notice and offers an explicit reload when a
|
|
248
|
+
new service worker is ready; it never reloads an active page automatically.
|
|
249
|
+
The development client (`npm run dev:client`) intentionally uses Quasar's
|
|
250
|
+
normal dev server and is not installable.
|
|
251
|
+
|
|
252
|
+
When working from source, `npm run install-all` installs the root, client, and
|
|
253
|
+
`src/client/src-pwa` dependency trees. The third tree contains Quasar's
|
|
254
|
+
build-only Workbox dependencies and is required by `npm run build`, but not by
|
|
255
|
+
the normal development client.
|
|
256
|
+
|
|
257
|
+
## Custom change-source-branch script
|
|
258
|
+
|
|
259
|
+
The **Change source branch** action uses the effective `changeSourceBranchScript`:
|
|
260
|
+
a non-empty project value overrides the global script. New installations and the
|
|
261
|
+
settings migration seed Kōbō's bundled script as the global default; **Reset to Kōbō
|
|
262
|
+
default** restores it. Clearing the effective script hides the UI action. The
|
|
263
|
+
backend still has a built-in cherry-pick fallback for requests made without a
|
|
264
|
+
custom script. A configured script **replaces** that built-in flow.
|
|
265
|
+
|
|
266
|
+
### Contract
|
|
267
|
+
|
|
268
|
+
When the script is set, Kōbō:
|
|
269
|
+
|
|
270
|
+
- still refuses if the agent is running (corruption risk, non-negotiable);
|
|
271
|
+
- spawns `bash -c "<your script>"` from the worktree as cwd;
|
|
272
|
+
- waits up to 5 minutes;
|
|
273
|
+
- on exit `0` → updates the workspace's `source_branch` metadata to the new
|
|
274
|
+
base the user typed, and refreshes the UI;
|
|
275
|
+
- on non-zero exit (or timeout) → surfaces the script's stderr as the toast.
|
|
276
|
+
|
|
277
|
+
The script owns everything else: the git reconstruction (cherry-pick, rebase,
|
|
278
|
+
or reset, your choice), the conflict resolution, the PR base change (`gh pr edit`
|
|
279
|
+
/ `glab mr update`), and the force-push (`git push --force-with-lease`). The
|
|
280
|
+
built-in features that only apply to the cherry-pick path stay **inactive** on
|
|
281
|
+
the custom path: the backup branch, the `cancel-source-change` recovery, the
|
|
282
|
+
force-push confirmation prompt, and the agent-driven conflict resolution.
|
|
283
|
+
|
|
284
|
+
### Environment variables
|
|
285
|
+
|
|
286
|
+
| Variable | Value |
|
|
287
|
+
|---|---|
|
|
288
|
+
| `KOBO_NEW_BASE` | the new source branch the user typed in the dialog |
|
|
289
|
+
| `KOBO_OLD_BASE` | the workspace's previous `sourceBranch` |
|
|
290
|
+
| `KOBO_WORKING_BRANCH` | the workspace's working branch |
|
|
291
|
+
| `KOBO_WORKTREE_PATH` | absolute path of the worktree (also the script's cwd) |
|
|
292
|
+
| `KOBO_PROJECT_PATH` | absolute path of the main project repo |
|
|
293
|
+
| `KOBO_PROJECT_NAME` | project directory name (basename of `KOBO_PROJECT_PATH`); handy for log lines or notifications |
|
|
294
|
+
| `KOBO_WORKSPACE_ID` | Kōbō workspace id (stable across renames); useful for backup branch naming, idempotency keys, etc. |
|
|
295
|
+
| `KOBO_WORKSPACE_NAME` | workspace display name as shown in the Kōbō UI |
|
|
296
|
+
| `KOBO_FORGE` | resolved forge id for this project (`github`, `gitlab`, `bitbucket-community` or `none`); use it to pick the appropriate CLI cleanly instead of probing them all |
|
|
297
|
+
| `KOBO_PR_NUMBER` | number of the PR / MR open on the resolved forge for the working branch. Empty when none is open, when the forge is `none`, or when its CLI cannot resolve it. |
|
|
298
|
+
|
|
299
|
+
The standard process env (`PATH`, `HOME`, etc.) is forwarded unchanged.
|
|
300
|
+
|
|
301
|
+
### Example — sekur-style cherry-pick + PR re-target + force-push
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
#!/usr/bin/env bash
|
|
305
|
+
set -euo pipefail
|
|
306
|
+
|
|
307
|
+
git fetch origin
|
|
308
|
+
COMMITS=$(git log --reverse --format=%H "$KOBO_WORKING_BRANCH" \
|
|
309
|
+
--not "origin/$KOBO_NEW_BASE" "origin/$KOBO_OLD_BASE")
|
|
310
|
+
COUNT=$(printf '%s\n' "$COMMITS" | grep -c '^[0-9a-f]' || true)
|
|
311
|
+
|
|
312
|
+
if [ "$COUNT" -eq 0 ]; then
|
|
313
|
+
git reset --hard "origin/$KOBO_NEW_BASE"
|
|
314
|
+
elif [ "$COUNT" -gt 50 ]; then
|
|
315
|
+
echo "Too many proper commits ($COUNT) — rebase manually" >&2
|
|
316
|
+
exit 1
|
|
317
|
+
else
|
|
318
|
+
git branch "kobo-backup/${KOBO_WORKING_BRANCH}-$(date +%s)" "$KOBO_WORKING_BRANCH"
|
|
319
|
+
git reset --hard "origin/$KOBO_NEW_BASE"
|
|
320
|
+
printf '%s\n' "$COMMITS" | xargs git cherry-pick
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
# Re-target the PR / MR — use the forge Kōbō resolved for this project.
|
|
324
|
+
# `command -v` makes the script degrade gracefully if `gh` / `glab` is missing.
|
|
325
|
+
case "${KOBO_FORGE:-none}" in
|
|
326
|
+
github)
|
|
327
|
+
if command -v gh >/dev/null 2>&1; then
|
|
328
|
+
gh pr edit --base "$KOBO_NEW_BASE" 2>/dev/null || true
|
|
329
|
+
else
|
|
330
|
+
echo "warn: 'gh' CLI not installed — skipping PR base update" >&2
|
|
331
|
+
fi
|
|
332
|
+
;;
|
|
333
|
+
gitlab)
|
|
334
|
+
if command -v glab >/dev/null 2>&1; then
|
|
335
|
+
glab mr update --target-branch "$KOBO_NEW_BASE" 2>/dev/null || true
|
|
336
|
+
else
|
|
337
|
+
echo "warn: 'glab' CLI not installed — skipping MR base update" >&2
|
|
338
|
+
fi
|
|
339
|
+
;;
|
|
340
|
+
*) : ;; # no forge configured — skip
|
|
341
|
+
esac
|
|
342
|
+
|
|
343
|
+
# Force-push if the branch is tracked upstream.
|
|
344
|
+
if git rev-parse --abbrev-ref "@{upstream}" >/dev/null 2>&1; then
|
|
345
|
+
git push --force-with-lease origin "$KOBO_WORKING_BRANCH"
|
|
346
|
+
fi
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Trust model
|
|
350
|
+
|
|
351
|
+
The script can do anything: `git reset --hard`, force-push, delete files. The
|
|
352
|
+
trust model is identical to `setupScript` / `cleanupScript` / `archiveScript`.
|
|
353
|
+
Kōbō is a local single-user dev tool and the script is your own code.
|
|
354
|
+
|
|
355
|
+
## Lifecycle hooks
|
|
356
|
+
|
|
357
|
+
Three moments in a workspace's life can trigger a shell script of your own:
|
|
358
|
+
a session ending, a PR being merged, auto-loop turning itself off. They join
|
|
359
|
+
the scripts Kōbō already ran on setup, cleanup, archive and
|
|
360
|
+
change-source-branch, and work exactly the same way.
|
|
361
|
+
|
|
362
|
+
Each hook is **empty by default, which means disabled** — a hook runs arbitrary
|
|
363
|
+
shell on your machine, so it only ever exists because you wrote it. Configure
|
|
364
|
+
them in **Settings → Scripts**, globally or per project; an empty per-project
|
|
365
|
+
value inherits the global one, like every other script.
|
|
366
|
+
|
|
367
|
+
| Hook | Fires when | Settings key |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| Session ended | An agent session ends, for any reason: clean finish, error, a session that failed to start, the watchdog finding the engine dead, or you stopping it | `sessionEndedScript` |
|
|
370
|
+
| PR merged | The PR watcher sees a PR go to MERGED | `prMergedScript` |
|
|
371
|
+
| Auto-loop stopped | Auto-loop is disabled, by Kōbō **or by you** | `autoLoopDisabledScript` |
|
|
372
|
+
|
|
373
|
+
Two ends never run the `Session ended` hook: a session replaced by a newer one
|
|
374
|
+
on the same workspace (it did not end, it was superseded), and a session
|
|
375
|
+
stopped by a **delete** or a **purge**, since the worktree the script would run
|
|
376
|
+
in is being removed at that very moment.
|
|
377
|
+
|
|
378
|
+
### Hook contract
|
|
379
|
+
|
|
380
|
+
Each hook is spawned with `bash`, cwd = the workspace worktree, a **5 minute**
|
|
381
|
+
timeout, and its output streamed live into the workspace feed under
|
|
382
|
+
`hook:<event>:*`. The exit code is recorded but **never blocks the lifecycle
|
|
383
|
+
transition** that triggered it: a failing hook is reported, not fatal. If the
|
|
384
|
+
worktree is no longer on disk (purged, deleted by hand), the hook is skipped
|
|
385
|
+
silently.
|
|
386
|
+
|
|
387
|
+
### Hook environment variables
|
|
388
|
+
|
|
389
|
+
Every hook gets the standard set: `WORKSPACE_ID`, `WORKSPACE_NAME`,
|
|
390
|
+
`BRANCH_NAME`, `SOURCE_BRANCH`, `PROJECT_PATH`, plus `KOBO_HOOK_EVENT` naming
|
|
391
|
+
the event. On top of that:
|
|
392
|
+
|
|
393
|
+
| Hook | Extra variables |
|
|
394
|
+
|---|---|
|
|
395
|
+
| Session ended | `KOBO_SESSION_ID`, `KOBO_SESSION_END_REASON` (`completed` / `error` / `killed` / `watchdog`), `KOBO_SESSION_EXIT_CODE` (empty when the engine reports none), `KOBO_SESSION_STOP_CAUSE` (`user`, `replacement`, `setup`, `archive`, or `shutdown`; empty for a natural end; superseded sessions and delete/purge stops skip the hook), `KOBO_AUTOLOOP_ACTIVE` (`1` when auto-loop is enabled, so a test run may overlap with the next agent's edits) |
|
|
396
|
+
| PR merged | `KOBO_PR_NUMBER`, `KOBO_PR_URL` |
|
|
397
|
+
| Auto-loop stopped | `KOBO_AUTOLOOP_REASON` (`completed` / `user-action`; blocked missions retain their intent and do not fire this hook), `KOBO_TASKS_PENDING` |
|
|
398
|
+
|
|
399
|
+
Only `KOBO_`-prefixed variables are passed through from the event; a hook
|
|
400
|
+
payload can never replace `PATH`, `HOME` or the identity variables above.
|
|
401
|
+
|
|
402
|
+
### Hook examples
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Session ended — run the suite, but only after a clean finish, and not
|
|
406
|
+
# while auto-loop is already starting the next iteration in this worktree.
|
|
407
|
+
[ "$KOBO_SESSION_END_REASON" = "completed" ] || exit 0
|
|
408
|
+
[ "$KOBO_AUTOLOOP_ACTIVE" = "1" ] && exit 0
|
|
409
|
+
npm test
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# PR merged — notify a chat channel.
|
|
414
|
+
curl -fsS -X POST "$MY_WEBHOOK" \
|
|
415
|
+
-d "text=PR #${KOBO_PR_NUMBER} merged on ${WORKSPACE_NAME}: ${KOBO_PR_URL}"
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
# Auto-loop stopped — notify when all work has been verified.
|
|
420
|
+
[ "$KOBO_AUTOLOOP_REASON" = "completed" ] || exit 0
|
|
421
|
+
notify-send "Kobo" "$WORKSPACE_NAME completed"
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Ordering with archive and auto-purge
|
|
425
|
+
|
|
426
|
+
The `PR merged` hook **starts while the worktree is still on disk**, before the
|
|
427
|
+
workspace is archived. Archiving does not wait for it (archiving keeps the
|
|
428
|
+
worktree, so there is nothing to protect); [auto-purge](#auto-purge-worktree-on-pr-merged)
|
|
429
|
+
does wait, so a hook that deploys or tags from the worktree will not have the
|
|
430
|
+
ground removed under it — at the cost of delaying the purge by however long the
|
|
431
|
+
hook takes (capped by the 5 minute timeout), and of holding one of the PR
|
|
432
|
+
watcher's four concurrent slots for that long.
|
|
433
|
+
|
|
434
|
+
The hook also runs when an agent is still active in that workspace (the PR
|
|
435
|
+
merged while it was working). A hook that deploys from the worktree may then
|
|
436
|
+
pick up the agent's uncommitted changes; check `git status` first if that
|
|
437
|
+
matters to you.
|
|
438
|
+
|
|
439
|
+
### Hook trust model
|
|
440
|
+
|
|
441
|
+
Identical to the other scripts: the hook can do anything your user can. Kōbō is
|
|
442
|
+
a local single-user dev tool and the script is your own code.
|
|
443
|
+
|
|
444
|
+
## Imported notification sounds
|
|
445
|
+
|
|
446
|
+
Kōbō ships two original tones, `neutral.wav` and `ready.wav`. **Settings →
|
|
447
|
+
Notifications → Imported sounds** adds your own: pick a file, and it becomes
|
|
448
|
+
selectable in every sound selector on that page, including the seven PR events.
|
|
449
|
+
|
|
450
|
+
- Formats: `.wav`, `.mp3`, `.ogg`, `.webm` — what every targeted browser decodes.
|
|
451
|
+
The extension decides, because browsers label audio MIME types inconsistently.
|
|
452
|
+
- Limits: 2 MiB per file, 20 files. A notification is short; the bundled sounds
|
|
453
|
+
removed in the public-readiness pass ranged from 1.7 KB to 207 KB.
|
|
454
|
+
- Storage: `<KOBO_HOME>/sounds/`. Each file is saved as `<id><extension>` with a
|
|
455
|
+
server-generated id; the uploaded filename is kept only as a display label and
|
|
456
|
+
never becomes a path. The `sounds.json` manifest holds
|
|
457
|
+
`{ id, name, extension, size, createdAt }` per entry.
|
|
458
|
+
- Settings store the reference `custom:<id>`. Deleting a sound that a setting
|
|
459
|
+
still points at never fails: playback falls back to `neutral.wav`, exactly as
|
|
460
|
+
it does for any unknown identifier.
|
|
461
|
+
- The catalogue is loaded when the app starts, so a sound plays for a notification
|
|
462
|
+
that arrives before you ever open Settings. Playback goes through a local blob
|
|
463
|
+
URL, which is what makes an imported sound audible over network access and
|
|
464
|
+
behind a reverse proxy.
|
|
465
|
+
- The files live in the Kōbō home, not in the published package, so they survive
|
|
466
|
+
an upgrade and are reachable from a phone over network access. They are not
|
|
467
|
+
part of a settings export — back up `<KOBO_HOME>/sounds/` with the rest of the
|
|
468
|
+
data directory.
|
|
469
|
+
|
|
470
|
+
HTTP: `GET /api/sounds` lists the catalogue, `POST /api/sounds` takes one
|
|
471
|
+
multipart `sound` field, `DELETE /api/sounds/:id` removes one, and
|
|
472
|
+
`GET /api/sounds/:id/file` serves the audio. All four sit behind the same
|
|
473
|
+
Host/Origin and network-token gates as the rest of the API.
|
|
474
|
+
|
|
475
|
+
## Unanswered question reminder
|
|
476
|
+
|
|
477
|
+
A workspace that asks you something sits in `awaiting-user` until you answer.
|
|
478
|
+
The badge in the drawer says so, but only if you are looking at Kōbō. This
|
|
479
|
+
reminds you.
|
|
480
|
+
|
|
481
|
+
**Settings → Notifications → Unanswered question reminder**, in minutes.
|
|
482
|
+
**`0` is the default and means off** — it is your attention being spent.
|
|
483
|
+
|
|
484
|
+
With a value of `10`, a workspace that has been waiting ten minutes triggers a
|
|
485
|
+
reminder, then another ten minutes after each reminder until you answer.
|
|
486
|
+
Changing the value applies from the next reminder. Answering (or the workspace
|
|
487
|
+
leaving `awaiting-user` for any other reason) resets the clock. The loop looks
|
|
488
|
+
once a minute, so a reminder lands within a minute of being due.
|
|
489
|
+
|
|
490
|
+
The reminder is a browser notification plus the question sound, the same pair
|
|
491
|
+
used when the question is first asked, and it is **broadcast to every open
|
|
492
|
+
Kōbō client** rather than only the tab showing that workspace — the point is to
|
|
493
|
+
reach you while you are looking at something else. As with any browser
|
|
494
|
+
notification, it needs the tab to be open (though not focused) and the
|
|
495
|
+
permission granted.
|
|
496
|
+
|
|
497
|
+
Two consequences worth knowing:
|
|
498
|
+
|
|
499
|
+
- The waiting clock lives in server memory, so restarting the server restarts
|
|
500
|
+
it. You get a later reminder, never a lost one.
|
|
501
|
+
- However far behind the loop falls (a laptop waking from sleep, say), it sends
|
|
502
|
+
**one** reminder per pass, not one per missed interval.
|
|
503
|
+
|
|
504
|
+
## Attachments in the creation description
|
|
505
|
+
|
|
506
|
+
On the create page, paste or drop files into the mission description, or use
|
|
507
|
+
**Attach files** to choose them. Images show a preview; documents show their
|
|
508
|
+
filename and an icon. Remove any attachment before creating the workspace.
|
|
509
|
+
|
|
510
|
+
Supported formats:
|
|
511
|
+
|
|
512
|
+
- Images: PNG, JPEG, GIF and WebP.
|
|
513
|
+
- Documents: PDF, Markdown (`.md`, `.markdown`), plain text (`.txt`, `.text`),
|
|
514
|
+
CSV, TSV, JSON, JSONL, YAML, YML, XML, LOG, RST and TOML.
|
|
515
|
+
|
|
516
|
+
The shared limit is 10 files, 50 MiB per file and 50 MiB total, counting images
|
|
517
|
+
and documents together. An attachment can provide the mission context without
|
|
518
|
+
description text; the workspace still needs a name.
|
|
519
|
+
|
|
520
|
+
Files stay in the browser until you press **Create**. Kōbō then copies images
|
|
521
|
+
into `.ai/images/` and documents into `.ai/attachments/` in the new or reused
|
|
522
|
+
worktree before starting the agent. The initial prompt includes the paths and
|
|
523
|
+
document names so the agent can read them. PDFs are copied as supplied; reading
|
|
524
|
+
or extracting their content uses the selected agent's available tools. Kōbō does
|
|
525
|
+
not perform PDF extraction or OCR during creation.
|
|
526
|
+
|
|
527
|
+
Comparing two engines copies the same attachments into each worktree
|
|
528
|
+
independently. If creation fails without creating a workspace, the form keeps
|
|
529
|
+
the attachments for retry. If only the setup script fails, the workspace keeps
|
|
530
|
+
the files and initial prompt for resuming later.
|
|
531
|
+
|
|
532
|
+
Selected files are not saved in workspace templates or copied by **Duplicate**.
|
|
533
|
+
Reloading the create page discards files that have not been submitted.
|
|
534
|
+
|
|
535
|
+
## Attachments in workspace chat
|
|
536
|
+
|
|
537
|
+
Existing workspaces accept the same images and documents as the creation form,
|
|
538
|
+
with the same limits: 50 MiB per file, 50 MiB of newly uploaded files per draft,
|
|
539
|
+
and 10 files maximum. Use **Attach files**, drag files into the chat input, or
|
|
540
|
+
paste files from the clipboard. Ordinary pasted text keeps its usual behavior.
|
|
541
|
+
|
|
542
|
+
Uploads show a pending badge and then the original filename. Sending is blocked
|
|
543
|
+
until every upload succeeds; remove a failed attachment and select it again to
|
|
544
|
+
retry. Removing a draft attachment also removes its uploaded file. Images keep
|
|
545
|
+
their inline conversation previews; document references include their original
|
|
546
|
+
name and worktree path for the agent to read. PDF extraction/OCR is delegated to
|
|
547
|
+
the agent's available tools.
|
|
548
|
+
|
|
549
|
+
Files are kept once the message is sent or queued. A failed send restores the
|
|
550
|
+
draft and its attachment badges while that conversation is still selected.
|
|
551
|
+
Switching workspaces or sessions discards unsent attachments; an upload that
|
|
552
|
+
finishes after the switch is cleaned up in its original workspace. Queued and
|
|
553
|
+
sent attachments are preserved. Documents stored in `.ai/attachments/` are added
|
|
554
|
+
to the worktree's `.gitignore`, including workspaces created before this feature.
|
|
555
|
+
|
|
556
|
+
## Comparing two engines on one task
|
|
557
|
+
|
|
558
|
+
Kōbō drives two agent runtimes. Which one does better on *your* code is an
|
|
559
|
+
empirical question, and this answers it.
|
|
560
|
+
|
|
561
|
+
On the create page, turn on **Compare two engines** and pick the engine to
|
|
562
|
+
compare against. Creating then produces **two workspaces instead of one**, from
|
|
563
|
+
the same task description, source branch and Notion/Sentry context:
|
|
564
|
+
|
|
565
|
+
- names are suffixed with the engine (`Fix the parser (Claude Code)` and
|
|
566
|
+
`Fix the parser (Codex)`), both halves, so neither reads as the default one;
|
|
567
|
+
- branches are suffixed with the engine id (`feature/fix-the-parser-claude-code`,
|
|
568
|
+
`feature/fix-the-parser-codex`), so each gets its own worktree;
|
|
569
|
+
- each engine runs with the model, reasoning effort and permission mode set in
|
|
570
|
+
its own block (engine B's are pre-filled with that engine's defaults), since
|
|
571
|
+
a Codex model id means nothing to Claude Code and vice versa;
|
|
572
|
+
- with auto-loop on, the brainstorm model and effort pickers are disabled: each
|
|
573
|
+
engine brainstorms on the model and effort of its own block, so both halves
|
|
574
|
+
start from the same footing.
|
|
575
|
+
|
|
576
|
+
The two workspaces are created **one after the other**, not in parallel: each
|
|
577
|
+
one fetches, branches and builds a worktree in the same repository, and two of
|
|
578
|
+
those at once contend on the git index lock. If the second creation fails you
|
|
579
|
+
are told so explicitly — the first workspace exists and is usable on its own.
|
|
580
|
+
|
|
581
|
+
Once both workspaces are created, Kōbō opens them automatically in the side-by-side
|
|
582
|
+
view, with the first engine on the left and the comparison engine on the right.
|
|
583
|
+
If only the first workspace was created, it opens in the usual single-workspace view.
|
|
584
|
+
|
|
585
|
+
### Reading the result
|
|
586
|
+
|
|
587
|
+
Both workspaces show an **Engine comparison** table at the top of their Git tab:
|
|
588
|
+
engine, model, status, tasks done over tasks planned, commits, files changed,
|
|
589
|
+
diff size, then what it took — agent messages over your messages, questions the
|
|
590
|
+
agent stopped to ask, tool calls, errors, input over output tokens, sessions and
|
|
591
|
+
total time — for each side, with a download button that exports the whole
|
|
592
|
+
table as CSV (one row per engine) and
|
|
593
|
+
a button to jump to the other one. The numbers come from the same git-stats
|
|
594
|
+
cache the rest of the UI uses; the table reads it when it opens and on its
|
|
595
|
+
refresh button, so a dash means "not measured yet", which is not the same claim
|
|
596
|
+
as zero. When one half was never created (or was deleted), the table says so
|
|
597
|
+
instead of showing a single row as if that were the whole comparison.
|
|
598
|
+
|
|
599
|
+
From there, each side's actual diff is one click away in the Git tab it sits
|
|
600
|
+
above.
|
|
601
|
+
|
|
602
|
+
## Choosing an LLM for code review
|
|
603
|
+
|
|
604
|
+
The workspace's **Start review** dialog offers engine (Claude Code or Codex),
|
|
605
|
+
model, reasoning effort and permission mode using the same catalogues as workspace
|
|
606
|
+
creation. The current workspace settings are selected initially. Changing any of
|
|
607
|
+
these settings automatically enables and locks **New session**; with unchanged
|
|
608
|
+
settings you can continue the current session or start a fresh one explicitly.
|
|
609
|
+
|
|
610
|
+
Enable **Return to the current session after the review** to resume the original
|
|
611
|
+
conversation with its original LLM settings and the reviewer's final summary.
|
|
612
|
+
This option requires an existing resumable session and is off by default. The
|
|
613
|
+
receiving agent summarizes the findings and waits for your instructions before
|
|
614
|
+
applying fixes. The complete review remains in its own session history.
|
|
615
|
+
|
|
616
|
+
With the option off, subsequent conversation continues with the review LLM.
|
|
617
|
+
When automatic return is enabled, stopping the review cancels it. If the review
|
|
618
|
+
fails or Kobo restarts during it, the original settings are restored without launching the
|
|
619
|
+
original agent automatically; review output already recorded remains available.
|
|
620
|
+
|
|
621
|
+
## Workspace templates and duplication
|
|
622
|
+
|
|
623
|
+
A template is a named preset of the create-workspace form: project (optional),
|
|
624
|
+
source branch, branch type, engine, model, reasoning effort, permission mode,
|
|
625
|
+
auto-loop settings, description, tasks and acceptance criteria. The workspace
|
|
626
|
+
name and working branch are never part of it - they are derived at each
|
|
627
|
+
creation - and neither are Notion / Sentry / PR URLs, which are one-off
|
|
628
|
+
context.
|
|
629
|
+
|
|
630
|
+
On the create page, **From a template** prefills the form with a saved preset:
|
|
631
|
+
only the fields the template carries change, the rest keeps its value. Once
|
|
632
|
+
applied, the select returns to **None**, so picking the same template again
|
|
633
|
+
re-applies it. A template that pins a project is listed only when that project
|
|
634
|
+
is selected.
|
|
635
|
+
**Save as template** captures the current form under a name; saving under an
|
|
636
|
+
existing name asks before overwriting. Templates are managed (rename, delete,
|
|
637
|
+
inspect) in **Settings → Workspace templates**; to edit one, load it on the
|
|
638
|
+
create page, adjust, and save it again under the same name.
|
|
639
|
+
|
|
640
|
+
**Duplicate**, in a workspace's context menu, opens the create page prefilled
|
|
641
|
+
from that workspace - its settings, description, tasks and criteria (all reset
|
|
642
|
+
to "to do") - with the name suffixed "(copy)". Nothing is created until you
|
|
643
|
+
press Create, so the branch and name can still be adjusted. Duplication carries
|
|
644
|
+
the workspace's settings, description, tasks and criteria, but not the
|
|
645
|
+
brainstorm effort nor "skip setup script", which are creation-time choices.
|
|
646
|
+
Archived workspaces can be duplicated too.
|
|
647
|
+
|
|
648
|
+
Templates live in `<KOBO_HOME>/workspace-templates.json` (100 at most). A
|
|
649
|
+
corrupt file is read as empty and reported in the server log, and is only
|
|
650
|
+
rewritten by the next save.
|
|
651
|
+
|
|
652
|
+
## Auto-purge worktree on PR merged
|
|
653
|
+
|
|
654
|
+
Purging a worktree frees its disk space (often hundreds of MB or GB worth of
|
|
655
|
+
`node_modules`, `vendor`, build artefacts, etc.) while keeping the workspace
|
|
656
|
+
discoverable: chat history and PR metadata are retained in the database, the
|
|
657
|
+
workspace is archived, and the worktree folder is removed from disk.
|
|
658
|
+
|
|
659
|
+
**Manual trigger**: workspace context menu → **Free disk space (delete worktree)**.
|
|
660
|
+
A confirmation dialog warns you before any destructive action.
|
|
661
|
+
|
|
662
|
+
**Automatic trigger**: Settings → Worktrees → **Auto-purge worktree on PR
|
|
663
|
+
merged**. When enabled, the pr-watcher fires `purgeWorktree(id)` on the
|
|
664
|
+
OPEN → MERGED transition, in addition to the existing auto-archive. Disabled by
|
|
665
|
+
default; opt in once you trust the restore flow on your machine.
|
|
666
|
+
|
|
667
|
+
### Restoring a purged workspace
|
|
668
|
+
|
|
669
|
+
Click **Restore worktree** in the workspace context menu or in the purged-worktree
|
|
670
|
+
banner. Kōbō recreates the checkout at its recorded path, then unarchives the same
|
|
671
|
+
workspace. Its conversation, tasks, sessions and lifecycle status are retained.
|
|
672
|
+
Restoration does not start an agent or dev server, run setup scripts, reinstall
|
|
673
|
+
dependencies, or re-enable auto-loop or cancelled schedules.
|
|
674
|
+
|
|
675
|
+
Recovery uses the first available source:
|
|
676
|
+
|
|
677
|
+
1. The existing local working branch (including commits made since purge).
|
|
678
|
+
2. The `headCommitSha` captured at purge time, if the branch was deleted and the
|
|
679
|
+
commit object is still available locally.
|
|
680
|
+
3. The exact working branch fetched from `origin`.
|
|
681
|
+
|
|
682
|
+
The local path works offline. Older purge records without a saved SHA remain
|
|
683
|
+
supported. Kōbō never substitutes the latest source branch or a merge commit for
|
|
684
|
+
missing work, resets an existing branch, or overwrites an occupied directory.
|
|
685
|
+
If no recovery source remains, the workspace stays purged and the UI explains
|
|
686
|
+
how to proceed. If Git still registers the missing worktree, repair that stale
|
|
687
|
+
registration manually before retrying.
|
|
688
|
+
|
|
689
|
+
**This is not a filesystem backup:** discarded uncommitted changes, ignored files,
|
|
690
|
+
local secrets and installed dependencies are not recovered. Git objects are not
|
|
691
|
+
pinned against garbage collection; restoration cannot recover deleted history
|
|
692
|
+
that no longer exists locally or remotely. `mergeCommitSha` is historical metadata
|
|
693
|
+
and is not currently populated; `headCommitSha` is optional and best-effort.
|
|
694
|
+
|
|
695
|
+
Manual recovery remains available when the working branch was deleted remotely.
|
|
696
|
+
For example, GitHub retains PR head refs:
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
# Run from the project's repository, substituting the saved values.
|
|
700
|
+
git fetch origin "pull/<pr-number>/head:<branch-name>"
|
|
701
|
+
git worktree add "<original-worktree-path>" "<branch-name>"
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
Use the appropriate provider's recovery flow for a GitLab MR. Once the expected
|
|
705
|
+
worktree is recreated, the PR watcher detects it on its next 30 s tick. It verifies
|
|
706
|
+
the exact root, repository and working branch before clearing both purge fields
|
|
707
|
+
and `archived_at`, and emits `workspace:worktree-restored` to refresh the client.
|
|
708
|
+
Unrelated directories and checkouts are never automatically adopted.
|
|
709
|
+
|
|
710
|
+
### Permission errors during purge
|
|
711
|
+
|
|
712
|
+
Docker often leaves root-owned files in `node_modules` / `vendor` inside the
|
|
713
|
+
worktree (containers run as `root` by default). When Kōbō tries to remove them
|
|
714
|
+
it hits `EACCES` / `EPERM`. Kōbō now auto-recovers by spawning a throwaway
|
|
715
|
+
Docker container that runs `chown` to reclaim ownership of the worktree, then
|
|
716
|
+
retries the removal; the manual `sudo rm -rf` fallback is only shown when
|
|
717
|
+
Docker is unavailable or the retry still fails. See Settings → Worktrees →
|
|
718
|
+
**How purge works** expansion in the UI for the full guide (manual recovery
|
|
719
|
+
commands for already-broken worktrees, including `setfacl` and `chown -R`
|
|
720
|
+
alternatives).
|
|
721
|
+
|
|
722
|
+
**Prevention summary**:
|
|
723
|
+
|
|
724
|
+
- **Best**: configure your container to run as the host user (`USER`
|
|
725
|
+
directive in `Dockerfile`, or `user: "${UID}:${GID}"` in `docker-compose.yml`
|
|
726
|
+
with `UID`/`GID` exported).
|
|
727
|
+
- **Fallback**: pose a default ACL on the worktrees root so future files
|
|
728
|
+
inherit access for your user: `setfacl -d -m u:$(whoami):rwX <worktrees-root>`.
|
|
729
|
+
Works on ext4 / btrfs / xfs with standard Docker bind mounts; does NOT work
|
|
730
|
+
on named Docker volumes, NTFS / exFAT / tmpfs, strict SELinux with `:Z`, or
|
|
731
|
+
`userns-remap`.
|
|
732
|
+
|
|
733
|
+
## Conversation history retention (opt-in)
|
|
734
|
+
|
|
735
|
+
Every agent message, tool call and diff Kōbō displays is persisted in the
|
|
736
|
+
`ws_events` table so the activity feed survives a reload. At roughly 25 rows per
|
|
737
|
+
second per running session, that table is the one that grows without bound —
|
|
738
|
+
and SQLite never gives the space back on its own.
|
|
739
|
+
|
|
740
|
+
Kōbō can prune it, but **the feature is off by default and always has to be
|
|
741
|
+
turned on deliberately**: it permanently deletes conversation history, and a
|
|
742
|
+
silent loss of history is worse than a large file. Leave it off and nothing is
|
|
743
|
+
ever deleted, exactly as before.
|
|
744
|
+
|
|
745
|
+
**Settings → Worktrees → Conversation history retention** exposes two values,
|
|
746
|
+
both global (the database is single):
|
|
747
|
+
|
|
748
|
+
| Setting | Default | Meaning |
|
|
749
|
+
|---|---|---|
|
|
750
|
+
| Delete events older than (days) | `0` | Age above which an agent event is deleted. **`0` means retention is disabled — nothing is ever deleted.** |
|
|
751
|
+
| Events always kept per workspace | `0` | The most recent events of each workspace survive whatever their age. Set it (5000 is a good starting point) at the same time you enable retention, so an old but still-open workspace keeps a readable history. |
|
|
752
|
+
|
|
753
|
+
The prune runs **at server startup and every 24 hours** while the backend stays
|
|
754
|
+
running. Each pass reads the current settings and deletes in batches of 5,000
|
|
755
|
+
rows. When enough pages have been freed, a `VACUUM` returns the space to the
|
|
756
|
+
filesystem.
|
|
757
|
+
|
|
758
|
+
**What it deletes:** eligible rows of `ws_events` — the recorded agent output.
|
|
759
|
+
Affected session metrics and the derived search index are reconciled with the
|
|
760
|
+
retained history. External MCP message receipts are kept independently.
|
|
761
|
+
**What it never touches:** workspaces, tasks, sessions, branches, worktrees, or
|
|
762
|
+
any file in your repositories.
|
|
763
|
+
|
|
764
|
+
Enabling retention — or shortening an existing window — asks for confirmation
|
|
765
|
+
first, and the dialog states how many of your recorded events the new window
|
|
766
|
+
would delete. Note that the first run is the largest one: switching a year-old
|
|
767
|
+
database to a 30-day window deletes eleven months in one go. Deleted events
|
|
768
|
+
cannot be recovered, and the server logs how many each prune removed.
|
|
769
|
+
|
|
770
|
+
## Concurrent agents
|
|
771
|
+
|
|
772
|
+
`maxConcurrentAgents` (global, fresh-install default `2`; `0` = no limit) controls admission of unattended agent sessions. Existing installations retain their configured value.
|
|
773
|
+
|
|
774
|
+
The limit applies to auto-loop iterations, cron, one-shot wakeups and quota retries. Every owned agent controller counts, including starting, manual and closing sessions. Manual starts and chat resumes are exempt from admission; this is not a hard global process limit.
|
|
775
|
+
|
|
776
|
+
An auto-loop workspace that finds no free slot stays enabled and waits. Confirmed
|
|
777
|
+
capacity release and lifecycle-guard release reconsider waiting workspaces.
|
|
778
|
+
It emits `autoloop:waiting-for-slot` so the
|
|
779
|
+
interface can say so rather than looking stalled.
|
|
780
|
+
|
|
781
|
+
The setting lives in `settings.json` under `global` and has no UI control yet:
|
|
782
|
+
|
|
783
|
+
```json
|
|
784
|
+
{ "global": { "maxConcurrentAgents": 3 } }
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## External LLM access through MCP
|
|
788
|
+
|
|
789
|
+
An external MCP client can connect to `http://127.0.0.1:3000/api/mcp` using Streamable HTTP (use your backend's configured port). It can discover workspaces, read their conversations, send messages to their agents and answer pending questions. The existing global stdio MCP server exposes these conversation tools too.
|
|
790
|
+
|
|
791
|
+
Remote HTTP clients follow the [network access](#network-access) settings and supply the Kōbō token as `Authorization: Bearer <token>` or `X-Kobo-Token`. The endpoint uses the same Host/Origin and reverse-proxy policy as the rest of the backend. No network setting is changed automatically.
|
|
792
|
+
|
|
793
|
+
See [Connect an external LLM](src/mcp-server/README.md#connect-an-external-llm) for connection examples, tool arguments, cursor polling and authentication details.
|
|
794
|
+
|
|
795
|
+
The **External LLM access (MCP)** panel within global Network access settings provides copyable URLs and HTTP/stdio configurations for this running installation. Previews use token placeholders; copying a configuration with the real token is a separate action. Set a client name to identify its messages and question answers in workspace history.
|
|
796
|
+
|
|
797
|
+
External senders can supply `idempotency_key` once per intended message and reuse it on retries. The durable receipt survives backend restarts and event retention. An uncertain delivery is explicitly reported and never automatically sent again; see the MCP guide for response codes and limitations.
|
|
798
|
+
|
|
799
|
+
## Kōbō update checks
|
|
800
|
+
|
|
801
|
+
The backend checks the package's npm `latest` release at startup and then every **20 minutes**, even with no browser open. All connected interfaces receive the result through the existing update banner, without reloading the page. Reconnecting clients retrieve the current snapshot.
|
|
802
|
+
|
|
803
|
+
Checks share one request across browser tabs, have a five-second deadline, and do not interrupt running agents when the registry is unreachable. A transient error preserves the last known valid version until the next check. Dismissing the banner hides that version; a newer release appears again. The post-install What's New dialog remains separate.
|
|
804
|
+
|
|
805
|
+
These checks report availability only: they do not install packages or restart Kōbō automatically.
|
|
806
|
+
|
|
807
|
+
## Network access
|
|
808
|
+
|
|
809
|
+
By default, Kōbō binds its HTTP and WebSocket server to `127.0.0.1` (localhost
|
|
810
|
+
only). Network access is an **opt-in** feature that re-binds to all interfaces
|
|
811
|
+
(`0.0.0.0`) so you can control Kōbō from another device on the same LAN: a
|
|
812
|
+
phone, a tablet, a second computer.
|
|
813
|
+
|
|
814
|
+
**Default posture: disabled.** Localhost API/WS connections bypass the token gate
|
|
815
|
+
unless **Behind a reverse proxy** is enabled. `/api/health` remains exempt in
|
|
816
|
+
either mode; Host/Origin checks apply separately.
|
|
817
|
+
|
|
818
|
+
### Production vs. development mode (important)
|
|
819
|
+
|
|
820
|
+
The setting controls the backend, not the separate development server:
|
|
821
|
+
|
|
822
|
+
- **Production** (`npm start`, or `npx @loicngr/kobo`): a single Kōbō process
|
|
823
|
+
serves both the UI and the API on one port, so the bind address follows this
|
|
824
|
+
setting directly: disabled → `127.0.0.1` (invisible on the LAN, connections
|
|
825
|
+
refused), enabled → all interfaces + token gate. Behind-a-reverse-proxy mode
|
|
826
|
+
also removes the loopback exemption.
|
|
827
|
+
- **Development** (`npm run dev:all`): the Quasar/Vite dev server runs as a
|
|
828
|
+
**separate process** (port 8080) and proxies `/api` + `/ws` to the backend over
|
|
829
|
+
`localhost`. The backend toggle does not set Vite's listening address.
|
|
830
|
+
Proxied requests use the loopback token exemption unless proxy mode is enabled;
|
|
831
|
+
Host/Origin checks still apply. To keep development local, explicitly set
|
|
832
|
+
`devServer: { host: '127.0.0.1' }` in `src/client/quasar.config.ts`. Use a
|
|
833
|
+
production build for remote access through Kōbō's normal network settings.
|
|
834
|
+
|
|
835
|
+
### Enabling network access
|
|
836
|
+
|
|
837
|
+
1. Open **Settings → General → Network access**.
|
|
838
|
+
2. Toggle **Enable network access** on.
|
|
839
|
+
3. A banner appears: **"Kōbō must be restarted to apply this change."** Restart
|
|
840
|
+
Kōbō; the server cannot re-bind a live port without restarting. The banner
|
|
841
|
+
stays visible until the restart happens so you don't miss it.
|
|
842
|
+
4. After restart, Kōbō listens on all interfaces. Your LAN IP addresses (one
|
|
843
|
+
per network interface) are listed in the same Settings panel.
|
|
844
|
+
|
|
845
|
+
To disable network access, toggle it off and restart again.
|
|
846
|
+
|
|
847
|
+
### The network access token
|
|
848
|
+
|
|
849
|
+
When network access is enabled, every non-loopback HTTP request and every
|
|
850
|
+
non-loopback WebSocket connection must supply a shared token.
|
|
851
|
+
|
|
852
|
+
- **Auto-generated**: Kōbō generates a random token the first time you enable
|
|
853
|
+
the feature. You never have to type one in.
|
|
854
|
+
- **Where to find it**: Settings → General → Network access → **Token** field
|
|
855
|
+
(always masked; use the copy button).
|
|
856
|
+
- **Regenerate**: the **Regenerate** button creates a new random token and
|
|
857
|
+
invalidates any device already using the old one. Useful when a device is
|
|
858
|
+
lost or you rotate credentials.
|
|
859
|
+
- **Not exported**: the token is excluded from any config export / backup
|
|
860
|
+
bundle, because it is a per-machine secret tied to this installation.
|
|
861
|
+
|
|
862
|
+
### Connecting a remote device
|
|
863
|
+
|
|
864
|
+
**Option A: QR code (recommended for phones)**
|
|
865
|
+
|
|
866
|
+
The Settings panel shows a QR code encoding
|
|
867
|
+
`http://<lan-ip>:<port>/?token=<token>`. Scan it with your phone's camera
|
|
868
|
+
while on the same Wi-Fi network; the browser opens directly to a pre-authenticated
|
|
869
|
+
session. The token is stored in the browser's `localStorage` under
|
|
870
|
+
`kobo:network-token` so subsequent visits don't require re-scanning.
|
|
871
|
+
|
|
872
|
+
**Option B: manual URL**
|
|
873
|
+
|
|
874
|
+
1. Copy one of the LAN URLs shown in the Settings panel (format
|
|
875
|
+
`http://<lan-ip>:<port>/`).
|
|
876
|
+
2. Open it on the remote device.
|
|
877
|
+
3. A login dialog appears: paste the token from the Settings panel and confirm.
|
|
878
|
+
4. The token is stored in `localStorage`; subsequent visits auto-authenticate.
|
|
879
|
+
|
|
880
|
+
**Host machine (localhost)**: connect to `http://localhost:<port>` as usual.
|
|
881
|
+
No token is needed unless **Behind a reverse proxy** is enabled.
|
|
882
|
+
|
|
883
|
+
### Token transport
|
|
884
|
+
|
|
885
|
+
| Connection type | How to supply the token |
|
|
886
|
+
|---|---|
|
|
887
|
+
| HTTP requests | `X-Kobo-Token: <token>` header |
|
|
888
|
+
| MCP HTTP requests (`/api/mcp`) | `X-Kobo-Token` or `Authorization: Bearer <token>` |
|
|
889
|
+
| WebSocket connections | `?token=<token>` query parameter on the upgrade URL |
|
|
890
|
+
|
|
891
|
+
The Kōbō browser client handles both automatically once the token is stored in
|
|
892
|
+
`localStorage`. Third-party scripts or API consumers must supply the header or
|
|
893
|
+
query param manually.
|
|
894
|
+
|
|
895
|
+
The IP address used for the loopback exemption is taken **from the OS socket**,
|
|
896
|
+
never from forwarded headers (`X-Forwarded-For` etc.), so a remote client cannot
|
|
897
|
+
spoof a loopback address to bypass authentication.
|
|
898
|
+
|
|
899
|
+
### Reachable addresses
|
|
900
|
+
|
|
901
|
+
Kōbō only answers to the names it is legitimately reachable at: `localhost`,
|
|
902
|
+
`127.0.0.1`, `::1`, `0.0.0.0` and — once network access is enabled — its own
|
|
903
|
+
non-internal addresses, IPv4 and IPv6 alike. A request carrying any other
|
|
904
|
+
`Host` gets a **403 `forbidden host`**. Note that Settings lists only the IPv4
|
|
905
|
+
URLs, since those are the ones meant to be read off a screen or scanned; the
|
|
906
|
+
IPv6 addresses of the same machine are accepted too.
|
|
907
|
+
|
|
908
|
+
This is what stops DNS rebinding: a page you visit can make its own domain
|
|
909
|
+
re-resolve to `127.0.0.1`, after which the browser treats Kōbō's responses as
|
|
910
|
+
same-origin and can read them — your settings, your workspaces — and can open
|
|
911
|
+
`/ws/terminal/<id>`, which is a real shell on your machine. The connection
|
|
912
|
+
genuinely comes from loopback, so the token gate lets it through; only the
|
|
913
|
+
`Host` and `Origin` headers tell it apart from your own tab.
|
|
914
|
+
|
|
915
|
+
The `Origin` check closes cross-site writes and the WebSocket terminal. A page
|
|
916
|
+
on another site can reach `http://localhost:3000` with a legitimate `Host`, and
|
|
917
|
+
while it cannot read the reply, the write would otherwise land. The rule is that
|
|
918
|
+
the page must have been served by us: its origin has to match the `Host` of the
|
|
919
|
+
same request, otherwise it gets **403 `forbidden origin`**. That holds however
|
|
920
|
+
you reach Kōbō, including through a remapped port such as `docker -p 3001:3000`
|
|
921
|
+
or an `ssh -L` tunnel. A request carrying no `Origin` at all is allowed:
|
|
922
|
+
browsers always send one on a write or a WebSocket handshake, so its absence
|
|
923
|
+
means a command-line caller.
|
|
924
|
+
|
|
925
|
+
Practical consequences:
|
|
926
|
+
|
|
927
|
+
- **Reaching Kōbō by a custom hostname does not work** (`http://my-nas.local:3000`,
|
|
928
|
+
a `/etc/hosts` alias, a container name). Use the LAN IP shown in Settings, or
|
|
929
|
+
turn on [behind a reverse proxy](#behind-a-reverse-proxy) mode.
|
|
930
|
+
- **In Docker, the LAN IP shown in Settings is the container's**, not the host's,
|
|
931
|
+
because Kōbō reads its own interfaces. Reaching a containerised Kōbō from
|
|
932
|
+
another device on the LAN therefore requires behind-a-reverse-proxy mode,
|
|
933
|
+
which `docker-compose.example.yml` and `docker-compose.local-traefik.yml`
|
|
934
|
+
already set. `docker-compose.local.yml` does not need it: it is opened at
|
|
935
|
+
`http://localhost:3000` from the host, which is allowed as-is.
|
|
936
|
+
- **`/api/health` is exempt**, so a Docker or Compose healthcheck reaching it
|
|
937
|
+
through the service name keeps working. `/api/health/logs` and
|
|
938
|
+
`/api/health/report` are not exempt.
|
|
939
|
+
- **A page served from another loopback port is not trusted.** Being on
|
|
940
|
+
loopback is not enough: Kōbō starts a dev server per workspace, on a loopback
|
|
941
|
+
port, serving whatever code an agent just wrote, and such a page must not
|
|
942
|
+
count as the real interface. In development the Quasar dev server is the one
|
|
943
|
+
exception, since it proxies to the backend and so never matches the `Host`;
|
|
944
|
+
the `dev` npm script declares it through `KOBO_DEV_CLIENT_ORIGIN`.
|
|
945
|
+
- **Behind-a-reverse-proxy mode accepts any host by default**, because only the
|
|
946
|
+
operator knows the expected domain. Declare it with
|
|
947
|
+
`KOBO_NETWORK_ACCESS_PROXY_HOST` (see below) to close that; the bundled
|
|
948
|
+
`docker-compose.example.yml` and `docker-compose.local-traefik.yml` do. That
|
|
949
|
+
mode also disables the loopback exemption, so every `/api/*` request has to
|
|
950
|
+
carry the token — but the SPA itself is served outside `/api/*` and behind no
|
|
951
|
+
token, so without a declared hostname a rebinding page can be served the real
|
|
952
|
+
interface and ask the user to paste their token into it.
|
|
953
|
+
|
|
954
|
+
#### `KOBO_NETWORK_ACCESS_PROXY_HOST`
|
|
955
|
+
|
|
956
|
+
Set it to the domain your proxy serves Kōbō under, and the host check is
|
|
957
|
+
enforced in behind-a-reverse-proxy mode instead of being skipped:
|
|
958
|
+
|
|
959
|
+
```yaml
|
|
960
|
+
environment:
|
|
961
|
+
- KOBO_NETWORK_ACCESS_ENABLED=true
|
|
962
|
+
- KOBO_NETWORK_ACCESS_BEHIND_PROXY=true
|
|
963
|
+
- KOBO_NETWORK_ACCESS_PROXY_HOST=kobo.example.com
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
A scheme and a port are accepted and ignored (`https://kobo.example.com:8443`
|
|
967
|
+
reads as `kobo.example.com`). Loopback stays reachable so a local healthcheck
|
|
968
|
+
keeps working. Leaving it unset preserves the previous behaviour.
|
|
969
|
+
|
|
970
|
+
### Security caveats
|
|
971
|
+
|
|
972
|
+
> **Network access is designed for trusted local networks only.**
|
|
973
|
+
|
|
974
|
+
- **No HTTPS / no TLS.** Kōbō's built-in server serves plain HTTP. The token
|
|
975
|
+
travels in cleartext on the network. Anyone who can observe your LAN traffic
|
|
976
|
+
(e.g. another device on an open Wi-Fi network) can capture it.
|
|
977
|
+
- **Not suitable for internet exposure.** Do not open the Kōbō port on your
|
|
978
|
+
router or firewall. If you need access over the internet, place a terminating
|
|
979
|
+
HTTPS reverse proxy (nginx, Caddy…) or a zero-config VPN (Tailscale, WireGuard)
|
|
980
|
+
in front of Kōbō yourself. Kōbō is not responsible for that layer.
|
|
981
|
+
- **A loopback reverse proxy requires the proxy setting.** Enable
|
|
982
|
+
[Behind a reverse proxy](#behind-a-reverse-proxy) to disable the loopback
|
|
983
|
+
exemption, configure the expected proxy hostname, and terminate TLS at the
|
|
984
|
+
proxy. Without that setting, requests forwarded from loopback bypass the token.
|
|
985
|
+
- **The Vite development server has its own network exposure.** The backend
|
|
986
|
+
toggle does not control its listener or secure its development assets. See
|
|
987
|
+
[Production vs. development mode](#production-vs-development-mode-important)
|
|
988
|
+
above; use production (`npm start`) for the normal remote-access setup.
|
|
989
|
+
- **Single shared token.** All remote devices share the same token. Regenerating
|
|
990
|
+
it disconnects every device at once; there is no per-device revocation.
|
|
991
|
+
|
|
992
|
+
### Behind a reverse proxy
|
|
993
|
+
|
|
994
|
+
The loopback bypass described above — any request from `127.0.0.1`/`::1` is always allowed,
|
|
995
|
+
token or not — becomes unsafe the moment a reverse proxy (Traefik, nginx, Caddy…) sits in front
|
|
996
|
+
of Kōbō. In a typical Docker Compose deployment, Traefik and Kōbō run as separate containers on a
|
|
997
|
+
shared network; every request Kōbō sees originates from the proxy, and depending on the network
|
|
998
|
+
path it can be indistinguishable from a genuine loopback request. Without this setting, anyone who
|
|
999
|
+
can reach the proxy publicly gets unauthenticated access to Kōbō, silently.
|
|
1000
|
+
|
|
1001
|
+
**Enable it**: Settings → General → Network access → **Behind a reverse proxy** (only visible once
|
|
1002
|
+
Network access itself is enabled). No restart needed — unlike the main Network access toggle, this
|
|
1003
|
+
one only changes the per-request auth decision, not the listening address.
|
|
1004
|
+
|
|
1005
|
+
**What changes**: the loopback bypass is disabled for API requests and WebSocket
|
|
1006
|
+
upgrades, including those from the host itself. For example, requests to
|
|
1007
|
+
`http://localhost:3000/api/workspaces` now need `X-Kobo-Token: <token>`.
|
|
1008
|
+
The application shell remains public so it can display the login dialog, and
|
|
1009
|
+
`/api/health` remains exempt for healthchecks.
|
|
1010
|
+
|
|
1011
|
+
For the concrete Docker setup (which compose file, which env vars, which volumes), see
|
|
1012
|
+
[Docker deployment](#docker-deployment) below.
|
|
1013
|
+
|
|
1014
|
+
## Docker deployment
|
|
1015
|
+
|
|
1016
|
+
Kōbō ships an official `Dockerfile` and three ready-to-use Compose files, one per use case. All
|
|
1017
|
+
three build from the same image; only the topology and a handful of env vars differ.
|
|
1018
|
+
|
|
1019
|
+
### Choosing a compose file
|
|
1020
|
+
|
|
1021
|
+
| File | Topology | Use it for |
|
|
1022
|
+
|---|---|---|
|
|
1023
|
+
| [`docker-compose.local.yml`](./docker-compose.local.yml) | Kōbō only, port `3000` published directly. No Traefik, no TLS, no SSH server, no Docker socket. | Trying Kōbō out or poking at a change locally — the fastest path to a running instance. |
|
|
1024
|
+
| [`docker-compose.local-traefik.yml`](./docker-compose.local-traefik.yml) | Traefik in front, Kōbō never on a published port, `kobo.localhost` (auto-resolves to `127.0.0.1`, no `/etc/hosts` editing). | Rehearsing the exact reverse-proxy topology/auth/mount setup locally, before touching a real VPS. |
|
|
1025
|
+
| [`docker-compose.example.yml`](./docker-compose.example.yml) | Traefik + Let's Encrypt (real domain, real TLS), SSH access, optional Docker-socket passthrough. | The reference for an actual production deployment (VPS behind a reverse proxy). |
|
|
1026
|
+
|
|
1027
|
+
All three share the same underlying image (`docker build -t kobo:local .` from the repo root,
|
|
1028
|
+
once) and the same core volume set; they differ in network topology and which optional pieces
|
|
1029
|
+
(SSH server, Docker socket, TLS) are wired in.
|
|
1030
|
+
|
|
1031
|
+
### The image
|
|
1032
|
+
|
|
1033
|
+
Multi-stage `Dockerfile`, `node:24.19.0-slim` base:
|
|
1034
|
+
|
|
1035
|
+
- **Build stage**: installs root, client, and PWA deps, then runs `npm run build` (client via Quasar, server via `tsc`).
|
|
1036
|
+
- **Runtime stage**: production deps only, plus OS tooling the agents and forge CLIs need — `git`, `openssh-client` + `openssh-server`, `ripgrep`, `fd-find`, `jq`, `curl`, Python + build tools, the Docker CLI (talks to a mounted host socket, see [Volumes](#volumes)), `gh`, `glab`, `tmux`, `vim`/`nano`, and more for interactive shell use.
|
|
1037
|
+
- **Global CLIs**: `@anthropic-ai/claude-code` and `@openai/codex` are installed globally for interactive login (`claude /login`, `codex login` over SSH) — separate from Kōbō's own bundled SDK/subprocess usage, which needs no global install.
|
|
1038
|
+
- **`git config --system --add safe.directory '*'`**: the container runs as root, but bind-mounted project repos keep the *host* user's UID on disk. Git's ownership check (CVE-2022-24765 mitigation) refuses to touch a repo it doesn't "own" unless told otherwise. This line pre-trusts every mounted path — fine for a single-user dev container, not something you'd do on a multi-tenant host.
|
|
1039
|
+
- **`HEALTHCHECK`**: `curl -f http://localhost:$SERVER_PORT/api/health` every 30s. `/api/health` is exempt from the network-access token gate unconditionally (even with `networkAccessBehindProxy` on) — Docker's `HEALTHCHECK` process has no way to supply the runtime-generated token, and without the exemption a "behind a reverse proxy" container never reports healthy, which makes Traefik's Docker provider filter it out of its router table entirely (Traefik drops unhealthy/starting containers from routing).
|
|
1040
|
+
- **`EXPOSE 3000`**, `ENV SERVER_PORT=3000`, entrypoint `docker-entrypoint.sh` → `npm start`.
|
|
1041
|
+
|
|
1042
|
+
Build once: `docker build -t kobo:local .`
|
|
1043
|
+
|
|
1044
|
+
### Environment variables (Docker)
|
|
1045
|
+
|
|
1046
|
+
Beyond the [general environment variables](#environment-variables), these matter specifically for a container deployment:
|
|
1047
|
+
|
|
1048
|
+
| Variable | Where it's set | Purpose |
|
|
1049
|
+
|---|---|---|
|
|
1050
|
+
| `KOBO_HOME` | all three compose files | Points at the named volume mount (`/root/.config/kobo`) so state survives `docker compose down`. |
|
|
1051
|
+
| `KOBO_NETWORK_ACCESS_ENABLED` | all three | Binds Kōbō to `0.0.0.0` inside the container at boot — required, since a Docker-forwarded or Traefik-proxied connection never looks like true loopback from Kōbō's point of view, even locally. Without it the container's `127.0.0.1`-only default blocks every external route. |
|
|
1052
|
+
| `KOBO_NETWORK_ACCESS_BEHIND_PROXY` | `local-traefik.yml`, `example.yml` | Disables the loopback-trust bypass entirely — needed whenever a reverse proxy sits in front, since every request now looks like it comes from the proxy. Not set in `local.yml`, which publishes Kōbō's port directly with no proxy in between. |
|
|
1053
|
+
| `GH_TOKEN` / `GITLAB_TOKEN` | `example.yml` (bring your own via `.env`) | Non-interactive auth for `gh`/`glab` inside the container — there's no browser to complete an interactive `gh auth login` on a headless VPS. |
|
|
1054
|
+
| `KOBO_PROJECTS_DIR` | host-side, read by all three via `${KOBO_PROJECTS_DIR:-./kobo-projects}` | Not a container env var — a *host* shell variable that picks the bind-mount source for `/projects`. Defaults to `./kobo-projects` next to the compose file. |
|
|
1055
|
+
|
|
1056
|
+
Both `KOBO_NETWORK_ACCESS_ENABLED` and `KOBO_NETWORK_ACCESS_BEHIND_PROXY` are read once at boot —
|
|
1057
|
+
this is what lets the container come up fully configured with no manual Settings-UI step (there's
|
|
1058
|
+
no browser to click through on a fresh headless deploy). The auto-generated token still needs to be
|
|
1059
|
+
retrieved once — see [Token / login flow](#token--login-flow).
|
|
1060
|
+
|
|
1061
|
+
### Volumes
|
|
1062
|
+
|
|
1063
|
+
| Mount | RO/RW | Purpose |
|
|
1064
|
+
|---|---|---|
|
|
1065
|
+
| `<name>-data:/root/.config/kobo` (named volume) | RW | Kōbō's own state — SQLite DB, `settings.json`, etc. Named (not bind-mounted) so it survives `docker compose down` without `-v`, and so it can be deliberately **shared** across compose files by reusing the same volume name (see the two local files, both `kobo-test-data`). |
|
|
1066
|
+
| `${KOBO_PROJECTS_DIR:-./kobo-projects}:/projects` | RW | The repos you create workspaces against, and their worktrees — all on one filesystem, so a worktree created next to a project lands on the same volume as the project itself. |
|
|
1067
|
+
| `${HOME}/.ssh:/root/.ssh` | RO | Git SSH auth — needed to push/pull over SSH remotes. |
|
|
1068
|
+
| `${HOME}/.gitconfig:/root/.gitconfig` | RO | Commit author identity. Without it, agent-authored commits fail with "please tell me who you are". |
|
|
1069
|
+
| `${HOME}/.claude.json:/root/.claude.json` | RW | Claude Code auth + MCP server registrations. **Must already exist as a file on the host** (run `claude /login` locally first if it doesn't) — Docker silently creates an empty *directory* instead of erroring on a missing bind-mount source, which breaks auth in a confusing way. |
|
|
1070
|
+
| `${HOME}/.claude:/root/.claude` | RW | Claude Code's full state directory (skills/plugins cache, session transcripts, memory) — Claude Code writes into this tree during normal operation. |
|
|
1071
|
+
| `${HOME}/.codex:/root/.codex` | RW | Codex's full state directory (`auth.json`, `config.toml`). Harmless to mount even if you only use Claude Code. |
|
|
1072
|
+
| `/var/run/docker.sock:/var/run/docker.sock` | RW, `example.yml` only | Lets the dev-server panel start/stop/tail logs for your projects' own `docker compose` stacks. **DANGER**: grants the container root-equivalent control over the host — anyone who can run a command inside `kobo` can spawn a privileged container that mounts the host filesystem. Remove this line if you don't need the dev-server panel. |
|
|
1073
|
+
|
|
1074
|
+
### Ports
|
|
1075
|
+
|
|
1076
|
+
| Port | File(s) | Notes |
|
|
1077
|
+
|---|---|---|
|
|
1078
|
+
| `3000` (published) | `local.yml` only | Kōbō's HTTP/WS port, published directly — no proxy in front. |
|
|
1079
|
+
| `80` | `local-traefik.yml` | Traefik's plain-HTTP entrypoint, routes `kobo.localhost` / `traefik.localhost`. |
|
|
1080
|
+
| `443` | `example.yml` | Traefik's TLS entrypoint (Let's Encrypt via the `le` cert resolver), routes your real domain. |
|
|
1081
|
+
| `2222 → 22` (published) | `example.yml` only | SSH into the container (`ssh -p 2222 root@<host>`), key-based auth only via the same mounted `~/.ssh`, no password login. The one exception to "no published ports" — Traefik proxies HTTP/WS, not raw TCP, so SSH needs its own path. Rebuilding the image regenerates the SSH host keys, so expect a one-time "host key changed" warning from your SSH client after a rebuild. |
|
|
1082
|
+
|
|
1083
|
+
Kōbō itself is **never** published directly in the two Traefik-fronted files — only Traefik is
|
|
1084
|
+
reachable, matching the real production posture.
|
|
1085
|
+
|
|
1086
|
+
### Token / login flow
|
|
1087
|
+
|
|
1088
|
+
All three files pre-enable network access via env vars, so there's no interactive Settings-UI step.
|
|
1089
|
+
Retrieve the auto-generated token from the container's boot log, then bookmark the token-bearing URL:
|
|
1090
|
+
|
|
1091
|
+
```bash
|
|
1092
|
+
docker compose -f <compose-file> logs kobo | grep Token
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
Open `http://<host>/?token=<token>` (exact host depends on the file — see the walkthroughs below).
|
|
1096
|
+
The token is stored in the browser's `localStorage`, so you only paste it once per browser.
|
|
1097
|
+
|
|
1098
|
+
### Quick local test — `docker-compose.local.yml`
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
docker build -t kobo:local . # once
|
|
1102
|
+
docker compose -f docker-compose.local.yml up -d
|
|
1103
|
+
docker compose -f docker-compose.local.yml logs kobo | grep Token
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
Open `http://localhost:3000/?token=<token from above>`.
|
|
1107
|
+
|
|
1108
|
+
By default the file bind-mounts `./kobo-projects` (next to it) at `/projects` — put a real git repo
|
|
1109
|
+
in there (or set `KOBO_PROJECTS_DIR` to point elsewhere) before creating a workspace. When you get
|
|
1110
|
+
to the project-path picker in the Create-workspace UI, remember it's browsing the **container's**
|
|
1111
|
+
filesystem, not your host's — your repo shows up under `/projects/<name>`, not wherever it lives on
|
|
1112
|
+
your machine. Kōbō attempts to fetch the source branch from `origin`, then falls
|
|
1113
|
+
back to an existing local source branch if fetch fails. A local-only repository
|
|
1114
|
+
therefore works once it has a commit and the selected source branch; remote
|
|
1115
|
+
push and PR actions still require a configured remote.
|
|
1116
|
+
|
|
1117
|
+
Stop with `docker compose -f docker-compose.local.yml down` (add `-v` to also drop the named volume
|
|
1118
|
+
and lose all Kōbō state).
|
|
1119
|
+
|
|
1120
|
+
### Rehearsing the reverse-proxy stack — `docker-compose.local-traefik.yml`
|
|
1121
|
+
|
|
1122
|
+
```bash
|
|
1123
|
+
docker build -t kobo:local . # once, if not already built
|
|
1124
|
+
docker compose -f docker-compose.local-traefik.yml up -d
|
|
1125
|
+
docker compose -f docker-compose.local-traefik.yml logs kobo | grep Token
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
Open `http://kobo.localhost/?token=<token from above>` — `.localhost` resolves to `127.0.0.1` on
|
|
1129
|
+
most OSes without touching `/etc/hosts`. This exercises the exact routing/auth/mount setup used by
|
|
1130
|
+
`docker-compose.example.yml`, minus a real domain and TLS certificate.
|
|
1131
|
+
|
|
1132
|
+
**Traefik dashboard**: `http://traefik.localhost`, protected by HTTP basic auth (`admin` /
|
|
1133
|
+
`kobo-local` — a local-only throwaway credential). Useful for debugging router/service discovery
|
|
1134
|
+
(`/api/http/routers` shows exactly what Traefik sees). To change the password, generate a new hash
|
|
1135
|
+
and swap it into the `dashboard-auth` middleware label:
|
|
1136
|
+
|
|
1137
|
+
```bash
|
|
1138
|
+
openssl passwd -apr1 '<new-password>'
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
The hash contains literal `$` characters, which Compose's own variable interpolation would try to
|
|
1142
|
+
expand — every `$` in the label value must be doubled to `$$` (already done in the shipped file).
|
|
1143
|
+
|
|
1144
|
+
This file intentionally shares its named volume (`kobo-test-data`) with `docker-compose.local.yml`,
|
|
1145
|
+
so a workspace created under one is visible under the other. If you want isolated state instead,
|
|
1146
|
+
rename the volume in one of the two files.
|
|
1147
|
+
|
|
1148
|
+
Stop with `docker compose -f docker-compose.local-traefik.yml down`.
|
|
1149
|
+
|
|
1150
|
+
### Real deployment — `docker-compose.example.yml`
|
|
1151
|
+
|
|
1152
|
+
Before using it:
|
|
1153
|
+
|
|
1154
|
+
1. Replace `image: kobo:local` with a real registry tag once one is published, or `docker build -t kobo:local .` from this repo on the target machine.
|
|
1155
|
+
2. Replace `kobo.example.com` with your real domain, and `you@example.com` with a real email (Let's Encrypt account).
|
|
1156
|
+
3. Populate a `.env` file next to the compose file with `GH_TOKEN` / `GITLAB_TOKEN` if you use the forge integration.
|
|
1157
|
+
4. Point every bind mount (`~/.ssh`, `~/.gitconfig`, `~/.claude.json`, `~/.claude`, `~/.codex`) at real paths on the host — see [Volumes](#volumes) for what each one is for, in particular the "`~/.claude.json` must already exist as a file" gotcha.
|
|
1158
|
+
5. Decide whether you need the Docker-socket passthrough (dev-server panel). If not, delete that volume line — see the DANGER note in [Volumes](#volumes).
|
|
1159
|
+
6. Network access + behind-a-reverse-proxy are already pre-enabled via the `KOBO_NETWORK_ACCESS_*` env vars in the file — no manual bootstrap step.
|
|
1160
|
+
|
|
1161
|
+
Bring it up, grab the token, bookmark the URL:
|
|
1162
|
+
|
|
1163
|
+
```bash
|
|
1164
|
+
docker compose -f docker-compose.example.yml up -d
|
|
1165
|
+
docker compose -f docker-compose.example.yml logs kobo | grep Token
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
Open `https://kobo.example.com/?token=<token>`. SSH access: `ssh -p 2222 root@<host>` (key-based
|
|
1169
|
+
only). See [Ports](#ports) and [Volumes](#volumes) above for the full rationale on the Docker socket
|
|
1170
|
+
and SSH tradeoffs before deploying this to a real machine.
|
|
1171
|
+
|
|
1172
|
+
### Troubleshooting
|
|
1173
|
+
|
|
1174
|
+
- **`fatal: detected dubious ownership in repository at '/projects/<name>'`** — only happens if you
|
|
1175
|
+
build your own image without the `git config --system --add safe.directory '*'` line described in
|
|
1176
|
+
[The image](#the-image) above; the shipped image already has it.
|
|
1177
|
+
- **A workspace's `git fetch origin` fails with "'origin' does not appear to be a git repository"**
|
|
1178
|
+
— the project under `/projects/<name>` has no `origin` remote (e.g. a plain `git init` test repo).
|
|
1179
|
+
Kōbō falls back to the selected local source branch when it exists. If neither
|
|
1180
|
+
source is available, choose an existing branch or configure a reachable remote.
|
|
1181
|
+
For throwaway testing, a local bare repo works fine:
|
|
1182
|
+
```bash
|
|
1183
|
+
git init -q --bare /projects/my-repo-remote.git
|
|
1184
|
+
git -C /projects/my-repo push /projects/my-repo-remote.git HEAD
|
|
1185
|
+
git -C /projects/my-repo remote add origin /projects/my-repo-remote.git
|
|
1186
|
+
```
|
|
1187
|
+
- **The project-path picker in the Create-workspace UI shows unfamiliar folders** — it browses the
|
|
1188
|
+
**container's** filesystem, not your host's. Your repos show up under `/projects/<name>`, not
|
|
1189
|
+
wherever they live on your machine.
|
|
1190
|
+
- **Container stays `unhealthy` / Traefik never routes to it with `KOBO_NETWORK_ACCESS_BEHIND_PROXY=true`**
|
|
1191
|
+
— check `docker inspect <container> --format '{{json .State.Health}}'`. This should not happen on
|
|
1192
|
+
a current image (`/api/health` is exempt from the token gate, see [The image](#the-image)); if you
|
|
1193
|
+
see a 401 in the health log on a custom-built image, you're missing that exemption.
|
|
1194
|
+
- **`docker compose logs kobo | grep Token` returns nothing** — network access wasn't enabled at
|
|
1195
|
+
boot. Confirm `KOBO_NETWORK_ACCESS_ENABLED=true` is actually set in the `environment:` block for
|
|
1196
|
+
the `kobo` service.
|
|
1197
|
+
|
|
1198
|
+
## Agent runtimes
|
|
1199
|
+
|
|
1200
|
+
Engines are selected per workspace at creation time. Both share the same UI surface (chat feed, task panel, permission modes, sub-agent panel, quota footer, auto-loop).
|
|
1201
|
+
|
|
1202
|
+
### Claude Code
|
|
1203
|
+
|
|
1204
|
+
Authenticate once:
|
|
1205
|
+
|
|
1206
|
+
```bash
|
|
1207
|
+
claude /login
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
Kōbō talks to the embedded [`@anthropic-ai/claude-agent-sdk`](https://github.com/anthropics/claude-agent-sdk-typescript), which reuses the same login. The `claude` CLI is **not** required at runtime; you only need it for `/login`, `mcp add`, and other one-off setup commands. As a fallback you can export `ANTHROPIC_API_KEY` instead.
|
|
1211
|
+
|
|
1212
|
+
The model selector is populated from [`src/shared/models.ts`](./src/shared/models.ts), also used by backend validation. This checkout includes Fable 5.1, Mythos 5, Opus 5, Opus 4.8, Sonnet 5 and older variants. Pick the default in **Settings → Agents**. Catalogue entries describe what Kōbō offers; provider access still depends on your account. `auto` leaves model selection to the runtime.
|
|
1213
|
+
|
|
1214
|
+
### OpenAI Codex
|
|
1215
|
+
|
|
1216
|
+
> Experimental. The Claude Code engine remains the primary, battle-tested path.
|
|
1217
|
+
|
|
1218
|
+
Authenticate either way:
|
|
1219
|
+
|
|
1220
|
+
```bash
|
|
1221
|
+
codex login # writes ~/.codex/auth.json
|
|
1222
|
+
# OR
|
|
1223
|
+
OPENAI_API_KEY=sk-… npx @loicngr/kobo
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
Kōbō spawns a long-lived `codex app-server` subprocess per workspace and bridges its JSON-RPC stream to the same UI. The binary is supplied by the direct `@openai/codex` dependency, so no separate runtime install is required.
|
|
1227
|
+
|
|
1228
|
+
When a Codex session is already working, **Send now** sends a queued chat message through the app-server's `turn/steer` endpoint. The message stays queued until Codex acknowledges the steer request; a failed request is restored to the queue instead of being lost.
|
|
1229
|
+
|
|
1230
|
+
The selector reads [`src/shared/codex-models.ts`](./src/shared/codex-models.ts). This checkout lists GPT-6 Astra, GPT-5.6 Sol/Terra/Luna, GPT-5.5 and older variants; availability depends on your account and authentication. Kōbō exposes effort values `auto`, `none`, `low`, `medium`, `high`, `xhigh`, and `max`, as declared in the engine's capabilities. The runtime must support the selected model/effort combination. Both selectors switch when you change the engine.
|
|
1231
|
+
|
|
1232
|
+
### Permission modes
|
|
1233
|
+
|
|
1234
|
+
Claude Code uses SDK permission modes; Codex uses sandbox and approval flags. These controls differ by engine. A Git worktree itself is not a security boundary.
|
|
1235
|
+
|
|
1236
|
+
**Claude Code** (controlled via SDK options):
|
|
1237
|
+
|
|
1238
|
+
| Kōbō mode | Effect |
|
|
1239
|
+
|---|---|
|
|
1240
|
+
| `plan` | SDK `plan` permission mode for planning without edits; not an OS sandbox. |
|
|
1241
|
+
| `bypass` | SDK `bypassPermissions`: approvals bypassed, without an OS boundary at the worktree. |
|
|
1242
|
+
| `strict` | SDK `acceptEdits`: file edits accepted; other tools follow approval rules. |
|
|
1243
|
+
| `interactive` | SDK `default`: unapproved operations can request user permission. |
|
|
1244
|
+
|
|
1245
|
+
**OpenAI Codex** (controlled via `sandbox` + `approvalPolicy` + `collaborationMode`):
|
|
1246
|
+
|
|
1247
|
+
| Kōbō mode | Codex sandbox | Codex approval | `collaborationMode` |
|
|
1248
|
+
|---|---|---|---|
|
|
1249
|
+
| `plan` | `read-only` | `never` | `plan` (enables `request_user_input`) |
|
|
1250
|
+
| `bypass` | `danger-full-access` | `never` | `default` |
|
|
1251
|
+
| `strict` | `workspace-write` | `on-request` | `default` |
|
|
1252
|
+
| `interactive` | `workspace-write` | `unless-trusted` | `default` |
|
|
1253
|
+
|
|
1254
|
+
Interactive Q&A (`request_user_input`) is only available in `plan` for Codex, a constraint of Codex itself. In `bypass`, `strict`, and `interactive`, Codex can still ask questions as normal chat text and you can respond in the chat, but it cannot open Kōbō's structured question panel.
|
|
1255
|
+
Linked-worktree Git operations may need access to the repository's shared `.git` directory outside the worktree. Depending on the engine and sandbox, this can require approval or a permission change explicitly chosen by the user. Kōbō does not automatically promote Plan to Bypass.
|
|
1256
|
+
|
|
1257
|
+
When an approval panel appears, **Allow this turn** permits the same tool for
|
|
1258
|
+
the rest of the current session. **Allow this operation** stores a rule for the
|
|
1259
|
+
same workspace, engine, tool, and exact operation payload; **Allow this tool**
|
|
1260
|
+
stores a rule for every invocation of that tool in the workspace. Persistent
|
|
1261
|
+
rules survive restarts and can be removed from the workspace's permission UI.
|
|
1262
|
+
|
|
1263
|
+
## Dev server
|
|
1264
|
+
|
|
1265
|
+
Each workspace can run its own dev server. The **Tools** panel shows its status, a clickable URL, the live container count, and a logs button. That works only when your project follows a small contract. This section documents the contract so the panel lights up for *your* project, not just Docker-instance projects.
|
|
1266
|
+
|
|
1267
|
+
### What the panel actually does
|
|
1268
|
+
|
|
1269
|
+
Kōbō treats the dev server in two independent layers:
|
|
1270
|
+
|
|
1271
|
+
| Layer | Driven by | Works for any project? |
|
|
1272
|
+
|---|---|---|
|
|
1273
|
+
| **Start / Stop** | The shell commands you configure | ✅ Yes, any shell-startable process |
|
|
1274
|
+
| **Status badge, URL, container count, logs** | The `.container/instances/*.env` convention + Docker | ❌ No, requires the convention below |
|
|
1275
|
+
|
|
1276
|
+
Configured processes can be started and stopped. Docker status, a URL and logs
|
|
1277
|
+
require a matching instance file and running containers. Without an instance,
|
|
1278
|
+
runtime detection returns `unknown`; the HTTP route may show the saved process
|
|
1279
|
+
status instead, but has no URL or container logs. Without a start command, the
|
|
1280
|
+
route returns `not_configured`.
|
|
1281
|
+
|
|
1282
|
+
> **Known limitation: Docker-coupled, for now.** Status detection, the URL, the container count, and the logs viewer are currently hard-wired to Docker and to the `.container/instances/*.env` convention described below. This is a deliberate, temporary state: it works today for Docker-instance projects, and the convention is documented here so you can adopt it. An **evolution is planned** to abstract this layer, making the URL/port configurable and the running-detection engine-agnostic (e.g. a TCP probe) so the panel works for any project, Docker or not, without imposing this file layout. Until that lands, follow the contract below.
|
|
1283
|
+
|
|
1284
|
+
### 1. Configure start / stop commands
|
|
1285
|
+
|
|
1286
|
+
In **Settings → (project) → Dev server**, set:
|
|
1287
|
+
|
|
1288
|
+
| Field | Purpose |
|
|
1289
|
+
|---|---|
|
|
1290
|
+
| `devServer.startCommand` | Shell command (or multi-line script) run to start the server. **Required.** |
|
|
1291
|
+
| `devServer.stopCommand` | Shell command run to stop it. Optional. |
|
|
1292
|
+
|
|
1293
|
+
How Kōbō runs them:
|
|
1294
|
+
|
|
1295
|
+
- `startCommand` runs as `bash -c "<command>"`, detached, with the working directory set to the workspace **worktree** (falling back to the project root).
|
|
1296
|
+
- It runs with two extra environment variables injected:
|
|
1297
|
+
- `INSTANCE`: the workspace branch name, sanitized: lowercased, with `/` and `_` replaced by `-`. Example: branch `feature/My_Thing` → `INSTANCE=feature-my-thing`.
|
|
1298
|
+
- `DEV_DOCKER_NO_FOLLOW=1`: a hint that a Docker setup script should exit after starting its containers instead of following logs. A tracked start process reports `starting` when an instance file exists but no matching container is running yet; once containers appear, Docker status takes precedence.
|
|
1299
|
+
- `stopCommand` runs with `INSTANCE` and `PROJECT_NAME` in the environment. After it, Kōbō also unconditionally runs `docker compose -p <PROJECT_NAME> down` if a `PROJECT_NAME` was resolved, so a Docker project does not strictly need a `stopCommand` at all.
|
|
1300
|
+
|
|
1301
|
+
### 2. Make the status panel light up — the `.container/instances` contract
|
|
1302
|
+
|
|
1303
|
+
For the **Running** badge, the URL, the container count, and the logs button to appear, your **start command must create an instance file**:
|
|
1304
|
+
|
|
1305
|
+
```
|
|
1306
|
+
<project-root>/.container/instances/<any-name>.env
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
Notes:
|
|
1310
|
+
|
|
1311
|
+
- The path is relative to the **project root** Kōbō knows (the main repo path), not the per-workspace worktree.
|
|
1312
|
+
- The file name is free; Kōbō scans *every* `*.env` file in that directory.
|
|
1313
|
+
- The file must contain these keys (`KEY=value`, `#` comments and surrounding quotes are tolerated):
|
|
1314
|
+
|
|
1315
|
+
| Key | Used for |
|
|
1316
|
+
|---|---|
|
|
1317
|
+
| `INSTANCE_NAME` | **Matching.** Must equal the sanitized branch name, i.e. the value of the `INSTANCE` env var Kōbō passed you. This is how Kōbō finds *this workspace's* instance among all the `.env` files. |
|
|
1318
|
+
| `HTTP_PORT` | The URL. Kōbō shows `http://localhost:<HTTP_PORT>`. |
|
|
1319
|
+
| `PROJECT_NAME` | **Running detection.** Must equal the Docker Compose project label `com.docker.compose.project`. One or more running containers with that exact label → `running`; custom container names are supported. |
|
|
1320
|
+
|
|
1321
|
+
The simplest correct start script therefore: read `$INSTANCE`, start your containers, then write the `.env` file with `INSTANCE_NAME=$INSTANCE`.
|
|
1322
|
+
|
|
1323
|
+
### 3. Running detection and logs
|
|
1324
|
+
|
|
1325
|
+
- **Status**: `running` if at least one container has the exact Compose project label; `starting` if an instance file exists and the start process remains tracked but no container is running; `stopped` otherwise; `unknown` if no instance file matched.
|
|
1326
|
+
- **Logs**: the logs button runs `docker logs` on those same matched containers.
|
|
1327
|
+
|
|
1328
|
+
Both are **Docker-only**. A plain Node process (`npm run dev`) does not produce a
|
|
1329
|
+
running Docker status or container logs. Writing an instance file alone is not
|
|
1330
|
+
enough to expose a URL: status responses only include the URL when matching
|
|
1331
|
+
containers are running. Direct processes can still be started and stopped.
|
|
1332
|
+
|
|
1333
|
+
### Minimal setups
|
|
1334
|
+
|
|
1335
|
+
**Docker project**: full panel support. Follow the contract: the start command writes the `.env` under the main project root and starts Compose with `-p "$PROJECT_NAME"` so its containers carry the matching label.
|
|
1336
|
+
|
|
1337
|
+
**Non-Docker project (e.g. Vite/Node)**: configure a foreground command such as
|
|
1338
|
+
`npm run dev`. Kōbō tracks its process group for shutdown, including children that
|
|
1339
|
+
outlive the shell. Open the project's URL yourself; the Docker status, URL and
|
|
1340
|
+
logs contract does not provide general process health monitoring.
|
|
1341
|
+
|
|
1342
|
+
**No instance file at all**: Start/Stop run your commands; the panel uses the
|
|
1343
|
+
saved status when available, otherwise `unknown`, with no URL or Docker logs.
|
|
1344
|
+
|
|
1345
|
+
### Example: the full convention end-to-end
|
|
1346
|
+
|
|
1347
|
+
The reference implementation is the `sekur` project. Its Kōbō `startCommand` is effectively `make dev-docker`, where the `Makefile` calls `./sh/dev-docker.sh $(INSTANCE)` and `$(INSTANCE)` reads the `INSTANCE` env var Kōbō injects. The script:
|
|
1348
|
+
|
|
1349
|
+
1. Picks free ports starting at `8700`.
|
|
1350
|
+
2. Names the Docker Compose project `sekur-<instance>` and runs `docker compose -p sekur-<instance> up -d`.
|
|
1351
|
+
3. Writes `<project-root>/.container/instances/sekur-<instance>.env`:
|
|
1352
|
+
|
|
1353
|
+
```env
|
|
1354
|
+
# Instance: feature-my-thing
|
|
1355
|
+
PROJECT_NAME=sekur-feature-my-thing
|
|
1356
|
+
INSTANCE_NAME=feature-my-thing
|
|
1357
|
+
HTTP_PORT=8700
|
|
1358
|
+
HTTPS_PORT=8701
|
|
1359
|
+
DB_PORT=8702
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
4. Honors `DEV_DOCKER_NO_FOLLOW=1` by exiting instead of tailing logs.
|
|
1363
|
+
|
|
1364
|
+
Kōbō then resolves the workspace (branch `feature/my-thing` → sanitized `feature-my-thing` → matches `INSTANCE_NAME`), shows `http://localhost:8700`, and detects the `sekur-feature-my-thing-*` containers as `running`.
|
|
1365
|
+
|
|
1366
|
+
### A minimal, copy-pasteable start script
|
|
1367
|
+
|
|
1368
|
+
Drop this in your project as `sh/dev-docker.sh`, make it executable (`chmod +x`), and set the Kōbō `startCommand` to `bash sh/dev-docker.sh`. It is a trimmed-down version of the `sekur` script, keeping only what the convention requires.
|
|
1369
|
+
|
|
1370
|
+
```bash
|
|
1371
|
+
#!/usr/bin/env bash
|
|
1372
|
+
# Starts an isolated Docker instance for the current Kōbō workspace and
|
|
1373
|
+
# writes the .container/instances/<project>.env file Kōbō reads for status.
|
|
1374
|
+
set -e
|
|
1375
|
+
|
|
1376
|
+
# Kōbō injects INSTANCE (sanitized branch name). Fall back to the git branch
|
|
1377
|
+
# so the script also works when run by hand.
|
|
1378
|
+
INSTANCE_NAME="${INSTANCE:-$(git branch --show-current | tr '/_' '-' | tr '[:upper:]' '[:lower:]')}"
|
|
1379
|
+
|
|
1380
|
+
# Docker Compose project name — Compose writes it to each container's
|
|
1381
|
+
# com.docker.compose.project label, which Kōbō matches exactly.
|
|
1382
|
+
APP_NAME="${APP_NAME:-myapp}"
|
|
1383
|
+
PROJECT_NAME="${APP_NAME}-${INSTANCE_NAME}"
|
|
1384
|
+
|
|
1385
|
+
# Pick the first free HTTP port starting at 8700 (10-port stride per instance).
|
|
1386
|
+
HTTP_PORT=8700
|
|
1387
|
+
while nc -zw1 localhost "$HTTP_PORT" 2>/dev/null; do
|
|
1388
|
+
HTTP_PORT=$((HTTP_PORT + 10))
|
|
1389
|
+
done
|
|
1390
|
+
|
|
1391
|
+
# Resolve the main project root even when this script runs in a linked worktree.
|
|
1392
|
+
COMMON_GIT_DIR="$(git rev-parse --path-format=absolute --git-common-dir)"
|
|
1393
|
+
PROJECT_ROOT="$(dirname "$COMMON_GIT_DIR")"
|
|
1394
|
+
INSTANCE_DIR="${PROJECT_ROOT}/.container/instances"
|
|
1395
|
+
mkdir -p "$INSTANCE_DIR"
|
|
1396
|
+
cat > "${INSTANCE_DIR}/${PROJECT_NAME}.env" <<EOF
|
|
1397
|
+
# Instance: ${INSTANCE_NAME}
|
|
1398
|
+
PROJECT_NAME=${PROJECT_NAME}
|
|
1399
|
+
INSTANCE_NAME=${INSTANCE_NAME}
|
|
1400
|
+
HTTP_PORT=${HTTP_PORT}
|
|
1401
|
+
EOF
|
|
1402
|
+
|
|
1403
|
+
# Start the containers. `-p "$PROJECT_NAME"` sets the matching Compose label;
|
|
1404
|
+
# export HTTP_PORT so compose.yaml can bind it.
|
|
1405
|
+
HTTP_PORT="$HTTP_PORT" docker compose -p "$PROJECT_NAME" up -d
|
|
1406
|
+
|
|
1407
|
+
echo "Started ${PROJECT_NAME} on http://localhost:${HTTP_PORT}"
|
|
1408
|
+
|
|
1409
|
+
# Kōbō runs this detached and sets DEV_DOCKER_NO_FOLLOW=1 — exit instead of
|
|
1410
|
+
# leaving an unnecessary log-following process running.
|
|
1411
|
+
if [ "${DEV_DOCKER_NO_FOLLOW:-0}" = "1" ]; then
|
|
1412
|
+
exit 0
|
|
1413
|
+
fi
|
|
1414
|
+
docker compose -p "$PROJECT_NAME" logs -f
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
Matching `stopCommand` (optional; Kōbō also runs `docker compose -p <PROJECT_NAME> down` itself):
|
|
1418
|
+
|
|
1419
|
+
```bash
|
|
1420
|
+
docker compose -p "${APP_NAME:-myapp}-${INSTANCE}" down
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
Adapt the port stride, the `APP_NAME`, and any extra `*_PORT` keys to your stack. The three keys Kōbō reads stay fixed: `INSTANCE_NAME`, `PROJECT_NAME`, `HTTP_PORT`.
|
|
1424
|
+
|
|
1425
|
+
### Troubleshooting
|
|
1426
|
+
|
|
1427
|
+
| Symptom | Likely cause |
|
|
1428
|
+
|---|---|
|
|
1429
|
+
| Status stuck on `starting` | The start process is still tracked but no matching Docker container is running. Check build/start errors and the Compose project label. |
|
|
1430
|
+
| Status `unknown`, no URL | No `.env` in `.container/instances/`, or its `INSTANCE_NAME` doesn't match the sanitized branch. Check `INSTANCE_NAME` equals `$INSTANCE`. |
|
|
1431
|
+
| Badge says `stopped`, no URL | No running Docker container with the exact Compose project label (normal for non-Docker projects). |
|
|
1432
|
+
| Logs button empty | Same cause: `docker logs` has no matching container. |
|
|
1433
|
+
| Wrong port in URL | `HTTP_PORT` in the `.env` doesn't match the port the server actually bound. |
|
|
1434
|
+
|
|
1435
|
+
## Forge integration
|
|
1436
|
+
|
|
1437
|
+
Kōbō can open pull requests (GitHub or Bitbucket) or merge requests (GitLab) directly from the Git panel. The **forge** setting controls which service is used for a given project.
|
|
1438
|
+
|
|
1439
|
+
### Modes
|
|
1440
|
+
|
|
1441
|
+
| Value | Behaviour |
|
|
1442
|
+
|---|---|
|
|
1443
|
+
| `auto` (default) | Kōbō reads the `origin` remote URL: host contains `github.com` → GitHub; host contains `gitlab` → GitLab; host contains `bitbucket` → Bitbucket Community; anything else → `none`. |
|
|
1444
|
+
| `github` | Always use GitHub, regardless of the remote URL. |
|
|
1445
|
+
| `gitlab` | Always use GitLab, regardless of the remote URL. |
|
|
1446
|
+
| `bitbucket-community` | Always use Bitbucket Community (`bkt`), regardless of the remote URL. |
|
|
1447
|
+
| `none` | PR/MR features are disabled. The PR block is hidden in the Git panel. |
|
|
1448
|
+
|
|
1449
|
+
Set the per-project value in **Settings → (project) → Forge**.
|
|
1450
|
+
|
|
1451
|
+
### Auto-detection
|
|
1452
|
+
|
|
1453
|
+
`auto` is designed to work without any configuration for the common cases:
|
|
1454
|
+
|
|
1455
|
+
- GitHub.com repositories: the remote URL contains `github.com` → GitHub detected automatically.
|
|
1456
|
+
- GitLab.com repositories: the remote URL contains `gitlab` → GitLab detected automatically.
|
|
1457
|
+
- Bitbucket Cloud repositories: the remote URL contains `bitbucket.org` → Bitbucket Community detected automatically.
|
|
1458
|
+
- Self-hosted GitLab on a custom hostname (e.g. `git.mycompany.com`): the hostname does not contain `gitlab`, so `auto` falls back to `none`. Set `forge: gitlab` explicitly for these projects.
|
|
1459
|
+
|
|
1460
|
+
### CLI prerequisites
|
|
1461
|
+
|
|
1462
|
+
Kōbō delegates PR/MR operations to the forge CLI and ships no credentials of its own. You must install and authenticate the relevant CLI before the PR actions become available:
|
|
1463
|
+
|
|
1464
|
+
**GitHub:**
|
|
1465
|
+
|
|
1466
|
+
```bash
|
|
1467
|
+
# Install: https://cli.github.com
|
|
1468
|
+
gh auth login
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
**GitLab (GitLab.com):**
|
|
1472
|
+
|
|
1473
|
+
```bash
|
|
1474
|
+
# Install: https://gitlab.com/gitlab-org/cli
|
|
1475
|
+
glab auth login
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
**GitLab (self-hosted):**
|
|
1479
|
+
|
|
1480
|
+
```bash
|
|
1481
|
+
glab auth login --hostname git.mycompany.com
|
|
1482
|
+
```
|
|
1483
|
+
|
|
1484
|
+
**Bitbucket Cloud:**
|
|
1485
|
+
|
|
1486
|
+
Install [`bkt`](https://github.com/avivsinai/bitbucket-cli), then open **Settings → Forge** and enter your Atlassian email and a Bitbucket-scoped API token. Kōbō keeps this token locally, excludes it from settings exports, and passes it to `bkt` for Bitbucket workspaces and their agents. No `bkt auth login` context or interactive keyring prompt is required.
|
|
1487
|
+
|
|
1488
|
+
Kōbō only invokes the CLI binary. If the CLI is missing from `PATH` or not authenticated, PR/MR actions are disabled with an explanatory tooltip in the Git panel. Kōbō keeps working; only the PR/MR features are affected.
|
|
1489
|
+
|
|
1490
|
+
### UI behaviour
|
|
1491
|
+
|
|
1492
|
+
- The Git panel label adapts per forge: **Create PR** on GitHub and Bitbucket, **Create MR** on GitLab.
|
|
1493
|
+
- When `forge` is `none` (or auto-resolves to `none`), the PR/MR block is hidden entirely: no errors, no placeholders.
|
|
1494
|
+
- When the CLI is absent or unauthenticated, the button is disabled with a tooltip explaining the issue instead of surfacing a raw error.
|
|
1495
|
+
|
|
1496
|
+
### Creating a workspace from a PR/MR
|
|
1497
|
+
|
|
1498
|
+
The **Create workspace** page's **Import from a PR** button (Project Git section) opens a picker listing open pull/merge requests on the resolved forge, with pagination and quick filters (all / mine / review-requested) plus a text search.
|
|
1499
|
+
|
|
1500
|
+
- Requires a forge that supports listing PRs/MRs (GitHub, GitLab, or Bitbucket Community — see [CLI prerequisites](#cli-prerequisites) above); the button is disabled with a tooltip when the project's resolved forge is `none` or the CLI is unauthenticated. Forks are listed but not selectable.
|
|
1501
|
+
- Selecting a PR/MR opens a guided stepper that diagnoses the local state (fetches the branch first) and walks through every conflict it finds one step at a time: an existing Kōbō workspace already tracking the branch (open it, or continue past an archived one), a worktree that already exists at the target path (attach to it or pick another path), local branch divergence from the remote (fast-forward, rebase your local commits on top, discard them, or keep as-is), uncommitted changes, and an in-progress rebase/merge/cherry-pick to resolve or cancel first. A rebase conflict leaves the worktree in that state for you (or the agent, via **Resolve with agent** in the Git panel) to finish.
|
|
1502
|
+
- Once resolved, the worktree-mode toggle, project, and branch fields lock to the imported PR/MR (an **Unlock** control reverts to the normal "new worktree" flow). The PR/MR URL is stored on the workspace and its title/description can seed the mission context. Your own **Description** text, if you've written one, is never overwritten.
|
|
1503
|
+
- **Advanced options → Skip setup script** is enabled by default for a PR/MR import. Turn it off to run the configured setup script before starting the agent. Ordinary attachment of an existing worktree outside PR/MR import still skips setup automatically.
|
|
1504
|
+
|
|
1505
|
+
## Notion integration
|
|
1506
|
+
|
|
1507
|
+
Pulls the body, title, and checklists of a Notion page to seed a workspace's tasks and acceptance criteria.
|
|
1508
|
+
|
|
1509
|
+
Enable the integration in **Settings → Notion**. Disabling it preserves its
|
|
1510
|
+
credentials and existing workspace links but hides Notion import and automation
|
|
1511
|
+
actions until it is enabled again.
|
|
1512
|
+
|
|
1513
|
+
Use **Test connection** in the same Settings tab to start the configured MCP
|
|
1514
|
+
server, initialise it, and make a lightweight authenticated API request. The
|
|
1515
|
+
result shows the elapsed time or an actionable configuration error.
|
|
1516
|
+
|
|
1517
|
+
### Setup
|
|
1518
|
+
|
|
1519
|
+
1. Visit <https://www.notion.so/profile/integrations> and create an internal integration.
|
|
1520
|
+
2. Grant it at least *Read content*.
|
|
1521
|
+
3. Copy the token (format `ntn_…` or `secret_…`).
|
|
1522
|
+
4. Open the target Notion page → **…** → **Connections** → **Add connection** → select the integration. Kōbō can only read pages explicitly shared with the integration.
|
|
1523
|
+
|
|
1524
|
+
### Token sources
|
|
1525
|
+
|
|
1526
|
+
Resolved in this order:
|
|
1527
|
+
|
|
1528
|
+
1. `NOTION_API_TOKEN` env var
|
|
1529
|
+
2. `NOTION_TOKEN` env var
|
|
1530
|
+
3. The direct Notion connection saved in Settings (`NOTION_TOKEN` or `NOTION_API_TOKEN`)
|
|
1531
|
+
4. If no direct connection exists, `~/.claude.json` → `mcpServers.notion.env.NOTION_TOKEN` (or `NOTION_API_TOKEN`)
|
|
1532
|
+
|
|
1533
|
+
The default config entry is named exactly `notion` and must be enabled.
|
|
1534
|
+
`notionMcpKey` selects another exact key. An existing `OPENAPI_MCP_HEADERS` value
|
|
1535
|
+
from the environment or selected entry is preserved; otherwise Kōbō constructs
|
|
1536
|
+
the authorization headers from the resolved token.
|
|
1537
|
+
|
|
1538
|
+
For a direct connection, enter command `npx`, arguments `["-y", "@notionhq/notion-mcp-server"]` and an environment object containing your `NOTION_TOKEN`. No Claude installation is required.
|
|
1539
|
+
|
|
1540
|
+
The legacy shared-Claude configuration remains supported:
|
|
1541
|
+
|
|
1542
|
+
```bash
|
|
1543
|
+
claude mcp add notion -s user -e NOTION_TOKEN=ntn_your_token -- npx -y @notionhq/notion-mcp-server
|
|
1544
|
+
```
|
|
1545
|
+
|
|
1546
|
+
### Overriding the MCP command
|
|
1547
|
+
|
|
1548
|
+
The command and arguments come from `NOTION_MCP_COMMAND` / `NOTION_MCP_ARGS`,
|
|
1549
|
+
then the direct saved connection or selected Claude MCP entry, then the bundled `npx -y
|
|
1550
|
+
@notionhq/notion-mcp-server` defaults. Pin a version or use a fork:
|
|
1551
|
+
|
|
1552
|
+
```bash
|
|
1553
|
+
NOTION_MCP_COMMAND=node \
|
|
1554
|
+
NOTION_MCP_ARGS="./my-fork/dist/server.js" \
|
|
1555
|
+
npx @loicngr/kobo
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
### Auto-assign on workspace creation
|
|
1559
|
+
|
|
1560
|
+
When `notionAssigneeProperty` is set, Kōbō updates the named People property on the imported page to `notionUserId`, but only if the property has no assignee yet. Both fields are global settings; leave blank to disable.
|
|
1561
|
+
|
|
1562
|
+
### Status field updates
|
|
1563
|
+
|
|
1564
|
+
When `notionStatusProperty` is set, Kōbō flips it to `notionInProgressStatus` as soon as the workspace starts work. Useful for keeping a Notion kanban in sync.
|
|
1565
|
+
|
|
1566
|
+
## Sentry integration
|
|
1567
|
+
|
|
1568
|
+
Turns a Sentry issue URL into a "fix workspace": Kōbō extracts the stacktrace, tags, and offending spans, writes them to `.ai/thoughts/SENTRY-<id>.md`, and primes the agent with a TDD fix workflow plus live access to the Sentry MCP tools.
|
|
1569
|
+
|
|
1570
|
+
Enable the integration in **Settings → Sentry**. Disabling it preserves its
|
|
1571
|
+
configuration and existing workspace links but hides Sentry import and
|
|
1572
|
+
automation actions until it is enabled again.
|
|
1573
|
+
|
|
1574
|
+
Use **Test connection** in the same Settings tab to initialise the configured
|
|
1575
|
+
MCP server and run its identity check. The result shows the elapsed time or an
|
|
1576
|
+
actionable configuration error.
|
|
1577
|
+
|
|
1578
|
+
### Setup
|
|
1579
|
+
|
|
1580
|
+
1. Generate a Sentry auth token with at least `project:read`, `event:read`, `org:read`. User tokens (format `sntryu_…`) are simplest for personal use.
|
|
1581
|
+
2. Register the Sentry MCP in Claude Code:
|
|
1582
|
+
|
|
1583
|
+
```bash
|
|
1584
|
+
claude mcp add sentry -s user \
|
|
1585
|
+
-e SENTRY_ACCESS_TOKEN=sntryu_your_token \
|
|
1586
|
+
-e SENTRY_HOST=your-org.sentry.io \
|
|
1587
|
+
-- npx -y @sentry/mcp-server@latest
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
For self-hosted Sentry, set `SENTRY_HOST` to your hostname (e.g. `sentry.mycompany.com`).
|
|
1591
|
+
|
|
1592
|
+
Alternatively, save command `npx`, arguments `["-y", "@sentry/mcp-server"]` and the appropriate environment variables in **Settings → Sentry → Direct connection**. This works without a Claude installation. Direct credentials live in the private `KOBO_HOME/integrations.json` file (mode `0600`) and are excluded from settings exports. Saving replaces the complete connection; only configured/unconfigured status is returned to the browser. Clearing the direct connection restores legacy lookup.
|
|
1593
|
+
|
|
1594
|
+
### How Kōbō picks the entry
|
|
1595
|
+
|
|
1596
|
+
Uses the direct saved connection when present. Otherwise reads `~/.claude.json` and uses the first entry under `mcpServers` whose key contains `sentry` (case-insensitive) **and is not disabled**. To force a specific entry, set the global `sentryMcpKey` setting to its exact key.
|
|
1597
|
+
|
|
1598
|
+
### Usage
|
|
1599
|
+
|
|
1600
|
+
1. Paste a Sentry issue URL in the workspace creation form (**Import Sentry**).
|
|
1601
|
+
2. Submit. Kōbō extracts the issue, writes `.ai/thoughts/SENTRY-<shortId>.md`, creates a `Fix: <title>` task, and boots the agent with the fix workflow.
|
|
1602
|
+
3. The Sentry Short-ID (e.g. `ACME-API-3`) becomes the branch prefix (`fix/ACME-API-3--slug`), which means commit messages like `Fixes ACME-API-3` will auto-close the issue on merge.
|
|
1603
|
+
|
|
1604
|
+
When Sentry is active, the workspace description field becomes optional; the extracted context is enough to start work.
|
|
1605
|
+
|
|
1606
|
+
## Voice transcription
|
|
1607
|
+
|
|
1608
|
+
Local push-to-talk transcription using [`whisper.cpp`](https://github.com/ggml-org/whisper.cpp). Available in the workspace chat input and the workspace creation form.
|
|
1609
|
+
|
|
1610
|
+
### Requirements
|
|
1611
|
+
|
|
1612
|
+
- `whisper-cli` from whisper.cpp
|
|
1613
|
+
- `ffmpeg`
|
|
1614
|
+
- `cmake` (only to build whisper.cpp from source)
|
|
1615
|
+
- At least one Whisper model downloaded via **Settings → Voice**
|
|
1616
|
+
|
|
1617
|
+
### Install whisper.cpp (Linux / macOS)
|
|
1618
|
+
|
|
1619
|
+
```bash
|
|
1620
|
+
git clone https://github.com/ggml-org/whisper.cpp.git
|
|
1621
|
+
cd whisper.cpp
|
|
1622
|
+
cmake -B build
|
|
1623
|
+
cmake --build build -j
|
|
1624
|
+
# Binary lands at build/bin/whisper-cli
|
|
1625
|
+
```
|
|
1626
|
+
|
|
1627
|
+
Alternatively, grab a prebuilt archive from the [whisper.cpp releases](https://github.com/ggml-org/whisper.cpp/releases) and point Kōbō at the extracted binary.
|
|
1628
|
+
|
|
1629
|
+
### Install whisper.cpp (Windows)
|
|
1630
|
+
|
|
1631
|
+
1. Install CMake and Visual Studio Build Tools (C/C++).
|
|
1632
|
+
2. Build whisper.cpp as above, or use a prebuilt archive.
|
|
1633
|
+
3. Verify in PowerShell:
|
|
1634
|
+
|
|
1635
|
+
```powershell
|
|
1636
|
+
where whisper-cli
|
|
1637
|
+
whisper-cli -h
|
|
1638
|
+
```
|
|
1639
|
+
|
|
1640
|
+
### Install ffmpeg
|
|
1641
|
+
|
|
1642
|
+
**Ubuntu / Debian:**
|
|
1643
|
+
|
|
1644
|
+
```bash
|
|
1645
|
+
sudo apt update
|
|
1646
|
+
sudo apt install -y ffmpeg
|
|
1647
|
+
```
|
|
1648
|
+
|
|
1649
|
+
**macOS:**
|
|
1650
|
+
|
|
1651
|
+
```bash
|
|
1652
|
+
brew install ffmpeg
|
|
1653
|
+
```
|
|
1654
|
+
|
|
1655
|
+
**Windows:**
|
|
1656
|
+
|
|
1657
|
+
```powershell
|
|
1658
|
+
choco install ffmpeg # or: scoop install ffmpeg
|
|
1659
|
+
where ffmpeg
|
|
1660
|
+
```
|
|
1661
|
+
|
|
1662
|
+
### Configure in Kōbō
|
|
1663
|
+
|
|
1664
|
+
**Settings → Voice**:
|
|
1665
|
+
|
|
1666
|
+
- Enable voice transcription.
|
|
1667
|
+
- Optionally set the binary paths. Empty falls back to `WHISPER_CPP_COMMAND` env var, then `PATH`, for whisper-cli; `ffmpeg` is looked up in `PATH`.
|
|
1668
|
+
- Download a model from the **Whisper models** section (sizes range from ~75 MB for `tiny` to ~3 GB for `large-v3`). The storage directory is displayed at the top of the section. Downloads are chunked and stream to disk with a live progress bar, and can be cancelled mid-flight.
|
|
1669
|
+
- Pick the active model.
|
|
1670
|
+
|
|
1671
|
+
The Voice panel shows the runtime status for whisper-cli and ffmpeg so misconfigurations are visible immediately.
|
|
1672
|
+
|
|
1673
|
+
### Model sizes (approximate)
|
|
1674
|
+
|
|
1675
|
+
| Model | Disk | Recommended temperature |
|
|
1676
|
+
|---|---|---|
|
|
1677
|
+
| `tiny` | ~75 MB | `0.1` |
|
|
1678
|
+
| `base` | ~142 MB | `0.1` |
|
|
1679
|
+
| `small` | ~466 MB | `0.2` |
|
|
1680
|
+
| `medium` | ~1.5 GB | `0.2` |
|
|
1681
|
+
| `large-v3` | ~3.1 GB | `0.2` |
|
|
1682
|
+
|
|
1683
|
+
### Advanced parameters
|
|
1684
|
+
|
|
1685
|
+
- **Temperature** (`0`–`1`): decoding stability vs flexibility.
|
|
1686
|
+
- **Initial prompt**: biases recognition for custom vocabulary, names, or jargon.
|
|
1687
|
+
- **Translate to English**: translate non-English speech rather than transcribing it.
|
|
1688
|
+
- **Suppress non-speech tokens**: reduce non-speech artefacts in the output.
|
|
1689
|
+
|
|
1690
|
+
## Skill suites
|
|
1691
|
+
|
|
1692
|
+
Kōbō's generated instructions cover reviews, auto-loop grooming, QA and brainstorming. Standard uses neutral instructions; external suites reference their skills by name. The **Settings → Skills → Skill suite** selector picks which ecosystem those prompts target. Seven options are supported:
|
|
1693
|
+
|
|
1694
|
+
| Suite | Auto-prompts cite | Best for |
|
|
1695
|
+
|---|---|---|
|
|
1696
|
+
| `standard` (fresh-install default) | Neutral, engine-independent instructions without named external skills | First use and repositories with their own conventions |
|
|
1697
|
+
| `superpowers` | `superpowers:*` skills (brainstorming, writing-plans, executing-plans, TDD, debugging, requesting-code-review, …) | Plugin-driven workflow inside Claude Code |
|
|
1698
|
+
| `gstack` | gstack slash commands (`/review`, `/ship`, `/qa`, `/browse`, `/design-review`, `/investigate`, `/codex`, …) | Concrete CLI-driven workflows, browser-based QA, opinionated ship/deploy loop |
|
|
1699
|
+
| `superpowers+gstack` | Both, specialised by intent (e.g. `/review` for tactical bug-hunting, `superpowers:requesting-code-review` for principles-level critique) | Users who install both suites and want each used for what it does best |
|
|
1700
|
+
| `ecc` | ECC review, testing, debugging and verification skills referenced by the bundled prompts | Users with ECC skills available to their engine |
|
|
1701
|
+
| `superpowers+gstack+ecc` | Combined instructions for the three installed suites | Users who already maintain all three ecosystems |
|
|
1702
|
+
| `custom` | Whatever you write in the `custom*` fields (Settings → Skills → Custom prompts) | Air-gapped, suite-agnostic, or non-standard setups |
|
|
1703
|
+
|
|
1704
|
+
Switching the selector only changes the prompt text Kōbō emits; it does **not** install or remove anything. Pick whichever matches the skills you have available in your Claude Code / Codex environment.
|
|
1705
|
+
|
|
1706
|
+
### standard
|
|
1707
|
+
|
|
1708
|
+
The fresh-install default. No external skill package is required. Agents receive
|
|
1709
|
+
neutral instructions and can still follow repository conventions and explicit user
|
|
1710
|
+
requests. Standard does not remove skills already available in the agent runtime.
|
|
1711
|
+
Use the dedicated Skills step in the Settings guided tour for an in-app explanation.
|
|
1712
|
+
|
|
1713
|
+
### superpowers
|
|
1714
|
+
|
|
1715
|
+
An optional open-source Claude Code plugin covering brainstorming, writing-plans, executing-plans, TDD, systematic-debugging, requesting / receiving-code-review, dispatching-parallel-agents, etc. Kōbō also surfaces specs from `docs/superpowers/specs/` and plans from `docs/superpowers/plans/` in the right-drawer Documents browser.
|
|
1716
|
+
|
|
1717
|
+
Install inside Claude Code:
|
|
1718
|
+
|
|
1719
|
+
```bash
|
|
1720
|
+
/plugin marketplace add obra/superpowers-marketplace
|
|
1721
|
+
/plugin install superpowers@superpowers-marketplace
|
|
1722
|
+
```
|
|
1723
|
+
|
|
1724
|
+
Repo: <https://github.com/obra/superpowers>.
|
|
1725
|
+
|
|
1726
|
+
### gstack
|
|
1727
|
+
|
|
1728
|
+
CLI-driven skill suite with a different philosophy: concrete slash commands for navigation (`/browse`), QA (`/qa`, `/qa-only`), design (`/design-review`, `/plan-design-review`, `/design-shotgun`, `/design-html`), strategy (`/office-hours`, `/plan-ceo-review`, `/plan-eng-review`), debugging (`/investigate`, `/codex`), and the full ship loop (`/ship`, `/land-and-deploy`, `/canary`, `/document-release`). Voice triggers exist for many of the commands.
|
|
1729
|
+
|
|
1730
|
+
When this suite is active, Kōbō's review template tells the agent to invoke `/review`, the auto-loop review gate uses gstack's reviewer, and the QA prompt routes to `/browse` for real-browser smoke tests.
|
|
1731
|
+
|
|
1732
|
+
Install by following the instructions in the [gstack repo](https://github.com/garrytan/gstack), then switch the suite to `gstack` in **Settings → Skills**.
|
|
1733
|
+
|
|
1734
|
+
### superpowers + gstack
|
|
1735
|
+
|
|
1736
|
+
When both suites are installed, set the selector to `superpowers+gstack`. Kōbō's prompts then cite each suite for what it does best: superpowers for the discipline (TDD loop, plan execution, parallel subagents), gstack for the tactical surface (real-browser QA, design audits, ship pipeline, codex second-opinion).
|
|
1737
|
+
|
|
1738
|
+
### ECC and combined suites
|
|
1739
|
+
|
|
1740
|
+
Choose `ecc` when the ECC skills referenced by Kōbō are already available to the
|
|
1741
|
+
selected engine. Choose `superpowers+gstack+ecc` only when all three suites are
|
|
1742
|
+
available. Kōbō adapts its instructions but does not install, update or verify the
|
|
1743
|
+
availability of external suites. Suite selection never overrides workflow
|
|
1744
|
+
preferences, engine permissions or narrower instructions from the user.
|
|
1745
|
+
|
|
1746
|
+
### custom
|
|
1747
|
+
|
|
1748
|
+
Lets you bypass the bundled prompts entirely and write your own. The five `custom*` fields under **Settings → Skills → Custom prompts** are seeded with neutral, suite-free defaults (the *AGNOSTIC* prompt strings in `src/shared/skill-suite-prompts.ts`) so you have a starting point. Useful for air-gapped setups, internal forks, or compliance environments where a third-party plugin is off-limits.
|
|
1749
|
+
|
|
1750
|
+
## gbrain (companion MCP)
|
|
1751
|
+
|
|
1752
|
+
[`gbrain`](https://github.com/garrytan/gbrain) is a separate companion tool: a per-project knowledge graph and semantic search index, with an MCP server interface. It's **not** a Kōbō skill suite (it doesn't change the auto-generated prompts) but it plugs into Kōbō workspaces transparently via the standard Claude Code MCP config: Claude Code sessions can inherit MCP servers registered in `~/.claude.json`. Codex uses its own native MCP configuration; Kōbō does not copy arbitrary Claude MCP entries into Codex. The managed Notion/Sentry connections described above are supplied explicitly to both engines.
|
|
1753
|
+
|
|
1754
|
+
When gbrain is configured, the agent gets access to tools like:
|
|
1755
|
+
|
|
1756
|
+
- `search`, `query`, `resolve_slugs`: semantic + graph search over the project's indexed content
|
|
1757
|
+
- `get_page`, `put_page`, `delete_page`, `get_chunks`, `get_versions`, `revert_version`: page-level knowledge-graph CRUD
|
|
1758
|
+
- `add_link`, `remove_link`, `get_links`, `get_backlinks`, `traverse_graph`, `find_orphans`: link operations
|
|
1759
|
+
- `add_tag`, `remove_tag`, `get_tags`: tagging
|
|
1760
|
+
- `submit_job`, `list_jobs`, `get_job`, `get_job_progress`, `pause_job`, `resume_job`, `retry_job`, `replay_job`, `cancel_job`: async ingestion jobs
|
|
1761
|
+
- `add_timeline_entry`, `get_timeline`, `log_ingest`, `get_ingest_log`: per-project activity log
|
|
1762
|
+
- `file_list`, `file_upload`, `file_url`: file attachments
|
|
1763
|
+
- `sync_brain`, `get_health`, `get_stats`: administration
|
|
1764
|
+
|
|
1765
|
+
Install the CLI by following the instructions in the [gbrain repo](https://github.com/garrytan/gbrain), then initialise and register it with Claude Code:
|
|
1766
|
+
|
|
1767
|
+
```bash
|
|
1768
|
+
# 1. Initialise a brain for this project (or use a shared remote brain)
|
|
1769
|
+
cd /path/to/your/project
|
|
1770
|
+
gbrain init
|
|
1771
|
+
|
|
1772
|
+
# 2. Register the MCP server in Claude Code (Kōbō reads the same ~/.claude.json)
|
|
1773
|
+
claude mcp add gbrain -s user -- gbrain mcp
|
|
1774
|
+
```
|
|
1775
|
+
|
|
1776
|
+
Once registered for the chosen engine, Kōbō workspaces opened on this project can see the `gbrain__*` tools automatically. The `/sync-gbrain` gstack skill keeps the index fresh after large refactors. Repo: <https://github.com/garrytan/gbrain>.
|
|
1777
|
+
|
|
1778
|
+
Pairing notes:
|
|
1779
|
+
|
|
1780
|
+
- gbrain pairs especially well with the `gstack` skill suite (which calls `gbrain search` in several of its skills) and with auto-loop mode (the agent can query past work without re-reading the diff every iteration).
|
|
1781
|
+
- If you don't use gbrain, the agent falls back to `grep` / `Read` for code exploration; no setup is required.
|
|
1782
|
+
|
|
1783
|
+
## Workspace sorting, split view and absence digest
|
|
1784
|
+
|
|
1785
|
+
The workspace drawer's sort menu offers last activity, creation date, name and
|
|
1786
|
+
attention priority, in either direction. The preference is saved in the browser.
|
|
1787
|
+
Sorting applies within the existing status/project groups, including archives;
|
|
1788
|
+
search relevance remains primary while searching. Last activity uses workspace
|
|
1789
|
+
updates and agent liveness events, never the read marker from opening a workspace.
|
|
1790
|
+
|
|
1791
|
+
**Split view:** choose “Open side by side” in the workspace header’s overflow menu, or “Open beside”
|
|
1792
|
+
in a workspace context menu. Each pane has its own workspace/session selection,
|
|
1793
|
+
conversation, Git/documents panels and terminal. Select workspaces with the two
|
|
1794
|
+
selectors and drag the divider (stacked vertically on small screens). The active
|
|
1795
|
+
pane is marked at the top and the recipient is named explicitly. The URL preserves
|
|
1796
|
+
both workspace choices on reload; the divider position is browser-local. Closing
|
|
1797
|
+
or navigating away checks unsaved files and unsent pane messages. Each pane runs
|
|
1798
|
+
an isolated same-origin client, so split mode uses two additional client instances
|
|
1799
|
+
and WebSocket connections. Browser/audio notifications stay with the host client.
|
|
1800
|
+
|
|
1801
|
+
**Since your last visit:** the entry at the bottom of the workspace drawer lists questions, approval requests,
|
|
1802
|
+
session endings, agent errors and PR changes since the browser's previous visit.
|
|
1803
|
+
The global **Settings → Notifications → Since your last visit** toggle
|
|
1804
|
+
(`activityDigestEnabled`, enabled by default) hides this entry and stops browser
|
|
1805
|
+
visit tracking and new server-side journal entries when switched off and saved.
|
|
1806
|
+
Existing history and browser checkpoints are preserved. Re-enabling resumes
|
|
1807
|
+
tracking; events occurring while disabled are not collected retroactively.
|
|
1808
|
+
Click an entry to open its workspace and, where available, its session or Git panel.
|
|
1809
|
+
“Mark displayed events as read” acknowledges only the loaded entries; older pages
|
|
1810
|
+
can be loaded before acknowledging. No automatic agent or workspace action runs.
|
|
1811
|
+
|
|
1812
|
+
Significant event metadata is retained server-side for 30 days, starting when this
|
|
1813
|
+
version is installed; conversation contents are not copied into the journal.
|
|
1814
|
+
Tracking the last visit is browser-local and only advances while the application
|
|
1815
|
+
is visible and no digest entries remain unread. A first visit establishes a
|
|
1816
|
+
checkpoint rather than presenting historical events. Deleted workspaces and
|
|
1817
|
+
expired activity do not appear. SQLite migration v41 creates the journal without
|
|
1818
|
+
changing existing workspace or conversation records.
|
|
1819
|
+
|
|
1820
|
+
**Unavailable actions:** Tools, Git and development-server controls explain common
|
|
1821
|
+
blocking conditions (archived/purged workspace, running agent or operation, missing
|
|
1822
|
+
configuration). Explanations remain visible and keyboard-accessible even when the
|
|
1823
|
+
button is disabled; setup configuration links directly to the Scripts settings.
|
|
1824
|
+
|
|
1825
|
+
The compact workspace header opens sessions from its title. **Configure** groups
|
|
1826
|
+
model, effort, permissions and auto-loop; the overflow menu holds split view,
|
|
1827
|
+
command palette, panel visibility and the guided tour. Stop remains visible while
|
|
1828
|
+
an agent runs. The active model badge describes a running session only.
|
|
1829
|
+
|
|
1830
|
+
Dismissed agent error banners stay dismissed after a reload in the same browser;
|
|
1831
|
+
the conversation history is preserved and new errors still appear. The Help menu
|
|
1832
|
+
can mark all current tour steps as seen at once, without hiding future additions.
|
|
1833
|
+
Queued messages are shared with split panes through the host client, so they
|
|
1834
|
+
remain visible and cancellable across view changes. Unsent drafts are guarded.
|
|
1835
|
+
|
|
1836
|
+
While the agent compacts its context, the workspace shows **Compacting** in its
|
|
1837
|
+
status indicators. Sending a chat message is temporarily disabled, including
|
|
1838
|
+
keyboard shortcuts; the draft remains editable and is preserved. Sending becomes
|
|
1839
|
+
available again when compaction finishes. This status survives page reloads, and
|
|
1840
|
+
**Stop** remains available if you need to stop the agent.
|
|
1841
|
+
|
|
1842
|
+
|
|
1843
|
+
### Checkout and process recovery
|
|
1844
|
+
|
|
1845
|
+
PR checkout matches an existing workspace by project and branch. A branch already checked out in a worktree can be attached; creating another worktree on that same branch is disabled. Keeping uncommitted changes cannot be combined with a hard reset: choose stash or commit first, or keep the branch. Checkout diagnosis includes staged, unstaged and untracked contents, including nested Git repositories; changing them invalidates the pending decision. Ignored files are excluded. This check detects changes between diagnosis and resolution; it cannot lock out edits from an unrelated terminal at the instant Git mutates a file.
|
|
1846
|
+
|
|
1847
|
+
Source-branch changes and their rollback require the worktree to be on the workspace's expected branch. If an agent or development server cannot confirm its shutdown, destructive workspace operations stop with an error and leave the worktree in place. Retry after shutdown completes. Starts are refused while an incompatible workspace operation is in progress.
|
|
1848
|
+
|
|
1849
|
+
Stopping an agent also cancels a queued replacement and disables auto-loop for that workspace, including while waiting for a free agent slot. Enable auto-loop again to resume automatic iterations. Internal restarts and application shutdown preserve the auto-loop setting.
|
|
1850
|
+
|
|
1851
|
+
Development-server shutdown waits for the entire process group, including children that outlive their shell. Process-group verification requires the standard POSIX `ps` command. When a custom stop script succeeds but the generic Docker shutdown fails, Kōbō confirms shutdown only if an independent Docker query reports no running containers for the project.
|
|
1852
|
+
|
|
1853
|
+
Docker status and logs identify containers by their exact `com.docker.compose.project` label. Custom container names work; unlabeled containers are not attributed by a name prefix.
|
|
1854
|
+
|
|
1855
|
+
### Automatic database backups
|
|
1856
|
+
|
|
1857
|
+
Kōbō checks for a daily backup at startup and every hour while running. A new WAL-safe snapshot is created when the latest backup is at least 24 hours old, keeping the seven most recent daily snapshots. Slow backups never overlap. Failures are logged and retried at the next check; graceful shutdown waits for the active backup before closing SQLite. Pre-migration snapshots have their own independent retention.
|
|
1858
|
+
|
|
1859
|
+
## Conversation search index
|
|
1860
|
+
|
|
1861
|
+
Global search and workspace history search use the same index of complete messages.
|
|
1862
|
+
Streamed fragments are assembled before indexing, so a word can match across token
|
|
1863
|
+
boundaries. Queries are literal substrings, with Unicode normalization and
|
|
1864
|
+
case-insensitive matching; quotes and wildcard characters are treated literally.
|
|
1865
|
+
|
|
1866
|
+
On the first upgrade to database migration 42, Kōbō builds the index in the
|
|
1867
|
+
background. Search remains available with partial results and an indexing progress
|
|
1868
|
+
indicator. The original conversation events are preserved, and indexing resumes
|
|
1869
|
+
from its saved progress after a restart. Existing workspaces and agents remain
|
|
1870
|
+
usable during this operation. Indexing and search run in a dedicated worker;
|
|
1871
|
+
no additional credentials or configuration are required.
|
|
1872
|
+
|
|
1873
|
+
An unavailable index affects search only and is reported explicitly. Restarting
|
|
1874
|
+
Kōbō restarts the worker and resumes pending indexing. The derived index lives in
|
|
1875
|
+
the existing SQLite database and is covered by its normal backups.
|
|
1876
|
+
Temporary SQLite writer contention is retried automatically. Search results link
|
|
1877
|
+
to the fragment containing the match, including within long streamed responses
|
|
1878
|
+
and across Unicode normalization boundaries.
|
|
1879
|
+
|
|
1880
|
+
## Audit compatibility notes
|
|
1881
|
+
|
|
1882
|
+
- Previously remembered Codex **exact-operation** file-edit approvals are inactive
|
|
1883
|
+
when their old fingerprints did not identify the actual changes. Approve the
|
|
1884
|
+
operation again to create a precise rule. Deliberately remembered whole-tool
|
|
1885
|
+
approvals and Claude approvals retain their existing scope.
|
|
1886
|
+
- Cron schedules accept five fields or `@hourly`, `@daily`, `@weekly`, `@monthly`,
|
|
1887
|
+
and `@yearly`. Older invalid expressions, including six-field schedules with
|
|
1888
|
+
seconds, remain visible as inactive entries; replace them with a valid schedule.
|
|
1889
|
+
- Submitting a review keeps its draft until the server confirms delivery and
|
|
1890
|
+
persistence. Rejections, disconnects, and a 30-second confirmation timeout
|
|
1891
|
+
preserve the draft. If confirmation is missing, inspect the conversation before
|
|
1892
|
+
manually retrying: the agent may already have received the message.
|
|
1893
|
+
- A slow WebSocket connection is disconnected and replays missed events on
|
|
1894
|
+
reconnection. Current questions, agent state, and previously viewed development
|
|
1895
|
+
servers are refreshed after replay.
|
|
1896
|
+
- MCP thought logs use unique filenames. Repeated notes with the same title and
|
|
1897
|
+
tag no longer replace earlier files.
|
|
1898
|
+
|
|
1899
|
+
## Auto-loop completion and recovery
|
|
1900
|
+
|
|
1901
|
+
Auto-loop remains enabled while work is unfinished. Its panel distinguishes grooming, execution and final verification from waiting, blocked, completed and stopped states. All todos and acceptance criteria must be complete, followed by a successful final verification. The final check runs again after task changes or new instructions. Empty custom finalization settings use the built-in final check.
|
|
1902
|
+
|
|
1903
|
+
Completing an auto-loop task through MCP or HTTP requires structured verification: a non-empty `method`, `summary`, and `checks` array with named checks whose `status` is `passed`. A required test that could not run stays open (`not_run`); writing test source alone is insufficient. These are results reported by the agent, not independently executed by Kōbō.
|
|
1904
|
+
|
|
1905
|
+
After three iterations without a new task milestone, Kōbō runs one diagnostic session and allows two further attempts. Turns dedicated to integrating new instructions preserve these counters, including in continuous-session mode. Persistent failure pauses the mission with its reason and a **Resume** action. The loop's intent is retained. Explicitly stopping, archiving or deleting a workspace disables it. Independently configured cron schedules retain their existing behavior; cancel them separately when no scheduled session should run.
|
|
1906
|
+
|
|
1907
|
+
Quota waits survive restarts and respect known reset dates, even several days away. Unknown resets use the fallback schedule, capped at five hours between attempts. `autoLoopMaxRetries` limits temporary engine/transport failures, not genuine quota waits. Outside auto-loop, an explicitly scheduled wakeup or cron can resume after the quota wait expires; expiry alone never launches a manual workspace. No automatic start may overlap an engine whose shutdown remains unconfirmed.
|
|
1908
|
+
|
|
1909
|
+
Messages sent while auto-loop is enabled are stored for the next iteration, including during grooming. That iteration integrates the instructions into the task list before implementation continues. The panel lists pending messages and lets you cancel them before dispatch. If a crash or interrupted session makes delivery uncertain, the mission pauses: inspect its history, then explicitly acknowledge the instruction as handled or request a retry. A retry can repeat work; it never happens automatically for an uncertain delivery.
|
|
1910
|
+
|
|
1911
|
+
HTTP clients can use `POST /api/workspaces/:id/auto-loop/messages` with `{content, clientMessageId}`, `GET` on the same path to inspect the queue, and `PATCH .../messages/:messageId` with `{action: "cancel" | "acknowledge" | "retry"}`. Keep the same `clientMessageId` when retrying an uncertain HTTP request. Existing immediate message delivery retains its explicit manual semantics.
|
|
1912
|
+
|
|
1913
|
+
## First-run defaults and Git workflow preferences
|
|
1914
|
+
|
|
1915
|
+
New installations select Standard, Plan, automatic provider model selection, two unattended slots, silent audio and disabled Notion/Sentry. Existing installations skip first-run setup and preserve their own choices. Known shipped prompt text is updated only on exact match; customized text and deleted templates remain unchanged. Retired sound identifiers map to the original neutral tone while preserving volume and enablement.
|
|
1916
|
+
|
|
1917
|
+
`workflowPolicy` contains independent `commit`, `push` and `publish` values (`manual` or `automatic`). Global settings provide defaults; project settings may override individual values; workspace creation snapshots the resolved result into the workspace. Later global/project changes affect future workspaces, not existing snapshots. A workspace policy can be updated through its validated PATCH API. The workspace toolbar's **Configure** menu exposes the three preferences beside the
|
|
1918
|
+
model, effort and permission selectors; they are disabled while an agent is running,
|
|
1919
|
+
for the same reason the API refuses the change. A change returns HTTP 409 while an agent controller is still present (including shutdown) or a workspace lifecycle operation is in progress: stop the agent, wait for the operation to finish, then update the policy. The next session start or resume receives the updated preferences. A PATCH that leaves the effective policy unchanged remains accepted, subject to the existing lifecycle restrictions.
|
|
1920
|
+
|
|
1921
|
+
Fresh defaults are manual. Existing settings/workspaces migrate to the previous automatic behavior for compatibility. Manual requires an explicit user request for that action; automatic supplies the workflow preference for the mission. Narrower user instructions always win. Publish means PR/MR creation, description updates and comments; merging needs separate authorization. Explicit Git-panel actions remain available. These preferences are agent instructions, not a shell-command filter or an OS sandbox.
|
|
1922
|
+
|
|
1923
|
+
Plan is never promoted to Bypass by auto-loop preparation, an agent tool event, or loop enablement. Preparation can run in Plan; execution/finalization blocks with an actionable message until an execution mode is explicitly selected.
|
|
1924
|
+
|
|
1925
|
+
Notion token environment overrides are applied consistently to both token variables passed to the child process. Explicit `OPENAPI_MCP_HEADERS` remain authoritative for compatibility and are not replaced by the resolved token. Direct integration configuration and legacy fallback are only read when an integration is used; opening Settings does not start a Notion users lookup. Connection tests and users refresh are explicit actions.
|
|
1926
|
+
|
|
1927
|
+
|
|
1928
|
+
### Managed integration MCPs in agent sessions
|
|
1929
|
+
|
|
1930
|
+
**Test connection** uses the saved direct configuration when present, otherwise the
|
|
1931
|
+
selected Claude entry, with the documented provider environment overrides. It does
|
|
1932
|
+
not test unsaved form values. Notion checks authentication with a users lookup;
|
|
1933
|
+
it does not write a page. A successful connection test is separate from an agent
|
|
1934
|
+
actually calling a tool.
|
|
1935
|
+
|
|
1936
|
+
Enabled, configured Notion and Sentry connections are supplied to both Claude Code
|
|
1937
|
+
and Codex at every agent launch or resume. Resolution is shared with connection
|
|
1938
|
+
tests and imports: direct configuration takes priority over selected Claude MCP
|
|
1939
|
+
entries; the documented Notion environment overrides still apply. Optional,
|
|
1940
|
+
unconfigured integrations are omitted and do not prevent ordinary sessions.
|
|
1941
|
+
|
|
1942
|
+
Managed server names begin with `kobo-notion-` or `kobo-sentry-` and carry a fresh
|
|
1943
|
+
launch suffix. The session prompt identifies the current namespaces and asks the
|
|
1944
|
+
agent to use them for the corresponding integration. This prevents Codex's native
|
|
1945
|
+
configuration merge from retaining stale credentials or transport fields under a
|
|
1946
|
+
reused server name. Only connection-specific environment overrides are supplied;
|
|
1947
|
+
the complete backend environment is not serialized into the agent configuration.
|
|
1948
|
+
|
|
1949
|
+
Saving settings does not replace connections in an already-running engine. Stop
|
|
1950
|
+
that engine and resume, or start a fresh session, to apply changes. Disabling an
|
|
1951
|
+
integration removes its Kōbō-managed connection from subsequent launches; it does
|
|
1952
|
+
not remove independent MCP servers configured in Claude or Codex. Integration
|
|
1953
|
+
credentials do not authorize the agent to modify remote data.
|
|
1954
|
+
|
|
1955
|
+
|
|
1956
|
+
### Settings navigation
|
|
1957
|
+
|
|
1958
|
+
**Git** contains commit/push/publication preferences, Git conventions, branch
|
|
1959
|
+
prefixes. **Prompts** contains instructions sent after opening a PR/MR, as well
|
|
1960
|
+
as review, CI-fix and finalization instructions. **Prompt templates** contains reusable chat text;
|
|
1961
|
+
it is distinct from model selection under **Agents** and workspace presets under
|
|
1962
|
+
**Workspace templates**. Per-project Git overrides remain under **Projects → Git**.
|
|
1963
|
+
|
|
1964
|
+
Automatic retry limits are under **Agents**; awaiting-user reminders are under
|
|
1965
|
+
**Notifications**. **Worktrees** contains paths, purge and retention. **Forge**
|
|
1966
|
+
contains Bitbucket credentials; GitHub/GitLab reuse authenticated local CLIs and
|
|
1967
|
+
the provider is selected per project. These moves do not change stored settings
|
|
1968
|
+
or inheritance. The main save action and unsaved-edit protection still apply.
|