@loicngr/kobo 0.1.1

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.
Files changed (59) hide show
  1. package/AGENTS.md +227 -0
  2. package/LICENSE +674 -0
  3. package/README.md +199 -0
  4. package/dist/mcp-server/kobo-tasks-handlers.js +27 -0
  5. package/dist/mcp-server/kobo-tasks-server.js +116 -0
  6. package/dist/server/db/index.js +22 -0
  7. package/dist/server/db/migrations.js +20 -0
  8. package/dist/server/db/schema.js +49 -0
  9. package/dist/server/index.js +178 -0
  10. package/dist/server/routes/dev-server.js +74 -0
  11. package/dist/server/routes/git.js +20 -0
  12. package/dist/server/routes/notion.js +24 -0
  13. package/dist/server/routes/settings.js +92 -0
  14. package/dist/server/routes/workspaces.js +730 -0
  15. package/dist/server/services/agent-manager.js +435 -0
  16. package/dist/server/services/dev-server-service.js +298 -0
  17. package/dist/server/services/notion-service.js +369 -0
  18. package/dist/server/services/pr-template-service.js +38 -0
  19. package/dist/server/services/settings-service.js +205 -0
  20. package/dist/server/services/websocket-service.js +212 -0
  21. package/dist/server/services/workspace-service.js +208 -0
  22. package/dist/server/services/worktree-service.js +117 -0
  23. package/dist/server/utils/git-ops.js +117 -0
  24. package/dist/server/utils/paths.js +95 -0
  25. package/dist/server/utils/process-tracker.js +46 -0
  26. package/package.json +84 -0
  27. package/src/client/dist/spa/assets/ActivityFeed-BveJRagX.js +60 -0
  28. package/src/client/dist/spa/assets/ActivityFeed-DBNn62g_.css +1 -0
  29. package/src/client/dist/spa/assets/CreatePage-BlgXsrJO.css +1 -0
  30. package/src/client/dist/spa/assets/CreatePage-wbOkBwYU.js +2 -0
  31. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-BepdiOnY.woff +0 -0
  32. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-4ZhHFPot.woff +0 -0
  33. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-CNa4tw4G.woff +0 -0
  34. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-CHKg1YId.woff +0 -0
  35. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-yBxCyPWP.woff +0 -0
  36. package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-3fZ6d7DD.woff +0 -0
  37. package/src/client/dist/spa/assets/MainLayout-6hzaLlYO.js +1 -0
  38. package/src/client/dist/spa/assets/MainLayout-D0OU6djX.css +1 -0
  39. package/src/client/dist/spa/assets/QBadge-Cb92Ia8-.js +1 -0
  40. package/src/client/dist/spa/assets/QDialog-B5H6ayTp.js +1 -0
  41. package/src/client/dist/spa/assets/QExpansionItem-DJgnAZg_.js +1 -0
  42. package/src/client/dist/spa/assets/QPage-CLk9i9z8.js +1 -0
  43. package/src/client/dist/spa/assets/QSpinnerDots-DcaNq8uL.js +1 -0
  44. package/src/client/dist/spa/assets/QTabPanels-DlG5TZhP.js +1 -0
  45. package/src/client/dist/spa/assets/QTooltip-637ruGFc.js +1 -0
  46. package/src/client/dist/spa/assets/SettingsPage-B9VYIQs-.css +1 -0
  47. package/src/client/dist/spa/assets/SettingsPage-KEqbLZUA.js +1 -0
  48. package/src/client/dist/spa/assets/WorkspacePage-BFuHLjou.css +1 -0
  49. package/src/client/dist/spa/assets/WorkspacePage-D0Hm21LY.js +2 -0
  50. package/src/client/dist/spa/assets/_plugin-vue_export-helper-CHpmshS7.js +1 -0
  51. package/src/client/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  52. package/src/client/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  53. package/src/client/dist/spa/assets/index-BThMCiY7.css +1 -0
  54. package/src/client/dist/spa/assets/index-CMvo3OTb.js +5 -0
  55. package/src/client/dist/spa/assets/nodes-DeIen-kp.js +1 -0
  56. package/src/client/dist/spa/assets/use-quasar-Dq-Vjx_2.js +1 -0
  57. package/src/client/dist/spa/index.html +4 -0
  58. package/src/mcp-server/kobo-tasks-handlers.ts +54 -0
  59. package/src/mcp-server/kobo-tasks-server.ts +128 -0
package/AGENTS.md ADDED
@@ -0,0 +1,227 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents (Claude Code, Cursor, etc.) working on this repository.
4
+
5
+ ## What this project is
6
+
7
+ **Kōbō** (工房 — Japanese for "workshop") orchestrates multiple Claude Code agents across isolated git worktrees. Each "workspace" is a self-contained mission with its own worktree, branch, Claude session, optional dev server, optional Notion source-of-truth, and a dedicated MCP tools server. A Vue 3 UI lets the human track progress, read live agent output, and manage the lifecycle.
8
+
9
+ Single-user, single-machine dev tool. No auth, no multi-tenant concerns.
10
+
11
+ ## Tech stack
12
+
13
+ **Backend** — Node.js ≥ 20, Hono (HTTP), `ws` (WebSocket), better-sqlite3 (WAL mode), nanoid, `@modelcontextprotocol/sdk`. TypeScript throughout, `tsx` for dev, `tsc` for production build.
14
+
15
+ **Frontend** — Vue 3, Quasar 2, Pinia, vue-router, marked + dompurify for markdown rendering. Vite via `@quasar/app-vite`.
16
+
17
+ **Database** — Single SQLite file under the **Kōbō home directory** (`~/.config/kobo/kobo.db` by default, overridable via `KOBO_HOME`). Fresh-install schema lives in `src/server/db/schema.ts` (`initSchema`); incremental migrations live in `src/server/db/migrations.ts`. **The project is in production** — every schema change MUST ship as a migration that preserves data, never as a breaking change to `initSchema` alone. See [Database migrations](#database-migrations) below.
18
+
19
+ **Kōbō home directory** — `KOBO_HOME` env var overrides everything. Otherwise `$XDG_CONFIG_HOME/kobo/`, else `~/.config/kobo/`. Contains `kobo.db`, `settings.json`, `skills.json`. **Development uses `./data/`** via the `KOBO_HOME=./data` prefix in the `dev` npm script, so local dev never touches your real `~/.config/kobo/` and can run in parallel with a production-installed Kōbō (`npx @loicngr/kobo`). See `src/server/utils/paths.ts`.
20
+
21
+ **Tests** — vitest (18 test files, 355+ tests at time of writing). No frontend test infrastructure.
22
+
23
+ ## Commands
24
+
25
+ ```bash
26
+ # Install
27
+ npm install # root
28
+ (cd src/client && npm install) # client — separate tree
29
+
30
+ # Develop
31
+ npm run dev # backend only (tsx watch on src/server/index.ts)
32
+ npm run dev:client # frontend only (quasar dev)
33
+ npm run dev:all # both concurrently
34
+
35
+ # Check & test
36
+ npx tsc --noEmit # type check (the project's primary quality gate)
37
+ npm test # run full vitest suite
38
+ npm run test:watch # vitest in watch mode
39
+
40
+ # Build & run
41
+ npm run build # builds client + server
42
+ npm start # production server (requires prior build)
43
+ ```
44
+
45
+ **Run individual test files** with `npx vitest run src/__tests__/<file>.test.ts`. Filter by test name with `-t "<pattern>"`.
46
+
47
+ ## Architecture
48
+
49
+ ```
50
+ src/
51
+ ├── server/
52
+ │ ├── index.ts # Hono app bootstrap + WS upgrade
53
+ │ ├── db/
54
+ │ │ ├── index.ts # singleton getDb / closeDb
55
+ │ │ ├── schema.ts # initSchema — CREATE TABLE IF NOT EXISTS …
56
+ │ │ └── migrations.ts # incremental migrations, bumped per feature
57
+ │ ├── services/ # business logic — pure functions over db + external processes
58
+ │ │ ├── workspace-service.ts # workspaces + tasks + agent_sessions CRUD
59
+ │ │ ├── agent-manager.ts # spawns Claude Code CLI, streams stdout, tracks sessions
60
+ │ │ ├── dev-server-service.ts # per-workspace dev server lifecycle (docker or npm process)
61
+ │ │ ├── websocket-service.ts # emit / emitEphemeral to subscribed clients
62
+ │ │ ├── worktree-service.ts # git worktree create/remove
63
+ │ │ ├── notion-service.ts # extract Notion page via @notionhq/notion-mcp-server (user-provided token)
64
+ │ │ ├── settings-service.ts # global + per-project settings cascade
65
+ │ │ └── pr-template-service.ts # pure template variable substitution
66
+ │ ├── routes/ # Hono handlers, thin layer over services
67
+ │ │ ├── workspaces.ts # /api/workspaces/* — the main surface
68
+ │ │ ├── dev-server.ts, git.ts, notion.ts, settings.ts
69
+ │ ├── utils/
70
+ │ │ ├── git-ops.ts # pushBranch, getCommitsBetween, delete{Local,Remote}Branch…
71
+ │ │ └── process-tracker.ts # per-workspace spawned-process map
72
+ ├── client/ # Vue 3 + Quasar SPA
73
+ │ └── src/
74
+ │ ├── stores/ # pinia: workspace, websocket, settings, dev-server
75
+ │ ├── components/ # WorkspaceList, NotionPanel, AcceptancePanel, ChatInput, GitPanel…
76
+ │ ├── pages/ # WorkspacePage, CreatePage, SettingsPage
77
+ │ └── router/
78
+ ├── mcp-server/ # standalone MCP server spawned per workspace
79
+ │ ├── kobo-tasks-server.ts # entrypoint, registers tools
80
+ │ └── kobo-tasks-handlers.ts # pure handlers (list_tasks, mark_task_done)
81
+ └── __tests__/ # vitest, one file per service/route
82
+ ```
83
+
84
+ ## Data model (SQLite)
85
+
86
+ | Table | Purpose |
87
+ |---|---|
88
+ | `workspaces` | the unit of work — id, name, project_path, source_branch, working_branch, status, notion_url, model, dev_server_status, `archived_at`, timestamps |
89
+ | `tasks` | workspace sub-items — title, status, `is_acceptance_criterion`, sort_order; CASCADE DELETE on workspace |
90
+ | `agent_sessions` | Claude Code CLI invocations — pid, `claude_session_id`, status, started_at, ended_at |
91
+ | `ws_events` | persisted WebSocket events for replay on reconnect — type, payload, session_id, created_at |
92
+
93
+ `status` enum: `created | extracting | brainstorming | executing | completed | idle | error | quota`. Transitions are validated in `updateWorkspaceStatus` against `VALID_TRANSITIONS`.
94
+
95
+ `archived_at` is **orthogonal** to `status` — archiving is a visibility flag, not a lifecycle state. Unarchive restores the exact pre-archive `status`.
96
+
97
+ ## Database migrations
98
+
99
+ **The project is in production**. Every schema change MUST ship as an incremental migration that preserves existing data. Never drop-and-recreate, never rely on `initSchema` alone to patch running databases.
100
+
101
+ ### The two files and their roles
102
+
103
+ - **`src/server/db/schema.ts`** — `initSchema(db)` is the source of truth for **fresh installs only**. It creates every table at its current shape. New installations (empty `data/` directory) run `initSchema` once and land at the latest `SCHEMA_VERSION`.
104
+ - **`src/server/db/migrations.ts`** — `runMigrations(db)` reads the current `version` from the `schema_version` table and sequentially applies every pending migration block up to `SCHEMA_VERSION`. Existing databases upgrade through this path.
105
+
106
+ Both files must be kept in sync: after adding a migration, update `initSchema` so fresh installs get the same final shape without replaying migrations.
107
+
108
+ ### Adding a migration for a new feature
109
+
110
+ Every feature that touches the schema:
111
+
112
+ 1. Bump `SCHEMA_VERSION` in `migrations.ts` (e.g. `1` → `2`)
113
+ 2. Add a new guarded block at the bottom of `runMigrations` that applies only if `currentVersion < newVersion`, using `db.exec` (better-sqlite3) to run raw SQL like `ALTER TABLE workspaces ADD COLUMN new_field TEXT`
114
+ 3. Update `initSchema` in `schema.ts` so the fresh-install shape matches (e.g. add the new column to the `CREATE TABLE` statement)
115
+ 4. At the end of `runMigrations`, bump the row in `schema_version` to the new `SCHEMA_VERSION` (the existing code already does this)
116
+ 5. Add a test in `src/__tests__/migrations.test.ts` that verifies:
117
+ - A database at the previous version can be upgraded without data loss
118
+ - The new version matches `SCHEMA_VERSION`
119
+ - Fresh installs and upgraded installs converge to the same schema
120
+ 6. Never edit or reorder migration blocks that have already shipped — they are historical. If you need to fix a mistake, add a new migration.
121
+
122
+ ### Rules
123
+
124
+ - **Migrations are append-only.** Shipped migration blocks are frozen. Fixes go in new migrations.
125
+ - **Always idempotent where possible.** Use `IF NOT EXISTS`, check for column existence before altering, etc. Prefer migrations that can be safely re-run.
126
+ - **`ALTER TABLE ADD COLUMN` is safe in SQLite** (even on large tables). For more invasive changes (rename, drop, change type), use the [12-step SQLite pattern](https://sqlite.org/lang_altertable.html#otheralter) within a transaction.
127
+ - **Run migrations on every backend start.** `runMigrations(db)` is called from `getDb()` via `src/server/db/index.ts`.
128
+ - **Test upgrades, not just fresh installs.** The `migrations.test.ts` suite must exercise "old DB → new DB" paths.
129
+
130
+ ## WebSocket protocol
131
+
132
+ Clients subscribe to individual workspace ids. The server sends `WsEvent` objects:
133
+
134
+ ```ts
135
+ { id, workspaceId, type, payload, sessionId?, createdAt }
136
+ ```
137
+
138
+ Common types: `agent:output`, `agent:status`, `agent:error`, `user:message`, `task:updated`, `devserver:status`, `workspace:archived`, `workspace:unarchived`, `sync:response`.
139
+
140
+ Two emit flavors in `websocket-service.ts`:
141
+ - `emit(workspaceId, type, payload)` — persists to `ws_events` for later replay via `sync:request` on reconnect
142
+ - `emitEphemeral(workspaceId, type, payload)` — delivered once, never persisted. Use for lifecycle events (archive, status changes) that shouldn't replay.
143
+
144
+ ## External integrations
145
+
146
+ ### Notion (opt-in, user-provided credentials)
147
+
148
+ `notion-service.ts` spawns the official [`@notionhq/notion-mcp-server`](https://github.com/makenotion/notion-mcp-server) as a child process (`npx -y @notionhq/notion-mcp-server`) and talks to it over stdio using JSON-RPC / MCP. **Kōbō ships no Notion credentials** — the feature only works if the user has configured their own integration token. The token is resolved in this order:
149
+
150
+ 1. `NOTION_API_TOKEN` env var
151
+ 2. `NOTION_TOKEN` env var
152
+ 3. `~/.claude.json` → `mcpServers.notion.env.NOTION_TOKEN` / `NOTION_API_TOKEN` (Claude Code's MCP config — the recommended path, same token shared with Claude Code)
153
+
154
+ The MCP command and args can be overridden via `NOTION_MCP_COMMAND` (default `npx`) and `NOTION_MCP_ARGS` (default `-y @notionhq/notion-mcp-server`) for pinning a specific version or using a fork.
155
+
156
+ When adding features touching `notion-service.ts`, remember: **no token = no feature**. The rest of Kōbō must keep working if the Notion token is absent — only the explicit Notion import endpoints should fail with a clear error. Do not throw at server startup.
157
+
158
+ See the "Notion integration" section of the README for the end-user setup guide.
159
+
160
+ ## Code conventions
161
+
162
+ **Service layer** throws descriptive errors; the route layer catches and maps to HTTP status codes. Error messages follow the pattern `` `Workspace '${id}' not found` `` / `` `... is already archived` ``.
163
+
164
+ **Route layer** is thin — always wrap the handler body in `try / catch` and return `c.json({ error: message }, status)`. Match the existing shape in `src/server/routes/workspaces.ts`.
165
+
166
+ **Swallowed failures** are acceptable (and required) for best-effort side effects like `agentManager.stopAgent` and `devServerService.stopDevServer` during delete/archive. Log with `console.error` and continue. Never let these break the happy path.
167
+
168
+ **Route ordering matters** in Hono. Static paths (`GET /archived`) MUST be declared **before** dynamic segments (`GET /:id`) or the dynamic segment captures them. There's a regression test locking this invariant in `src/__tests__/routes-workspaces.test.ts`.
169
+
170
+ **File size** — prefer focused files. `WorkspaceList.vue` and `workspaces.ts` (routes) are the largest files; don't grow them further without a clear reason. If a file approaches unwieldy, surface it as a concern, don't silently split.
171
+
172
+ **Dependencies** — root `package.json` covers backend + tests. `src/client/package.json` is a separate npm tree. Install both.
173
+
174
+ ## Testing discipline
175
+
176
+ - **TDD for backend** — write the failing test, confirm it fails for the right reason, implement minimally, confirm it passes, commit. One commit per logical unit. See existing tests in `src/__tests__/workspace-service.test.ts` for the setup pattern (fresh in-memory DB per test via `resetDb()`).
177
+ - **Route tests** use `vi.mock()` on service modules before imports (see `src/__tests__/routes-workspaces.test.ts`). Keep mocks complete — missing exports cause obscure failures.
178
+ - **No frontend test infra.** Type-check via `npx tsc --noEmit` is the frontend gate. Manual smoke testing covers UI behavior.
179
+ - **`beforeEach(() => vi.clearAllMocks())`** is the convention for all route test files.
180
+
181
+ ## Git workflow
182
+
183
+ - Feature branches live under `.worktrees/<name>` (git worktrees, not checkout switching). The directory is gitignored.
184
+ - Branches named `feature/<slug>` target `develop`. `develop` merges to `main` for releases.
185
+ - This project uses the **superpowers** skills workflow: brainstorming → writing-plans → subagent-driven-development → finishing-a-development-branch. Specs land in `docs/superpowers/specs/`, plans in `docs/superpowers/plans/`. Both directories are gitignored for new files going forward.
186
+ - NEVER `git push --force` without explicit user consent. Always prefer `--force-with-lease` over `--force` when rewriting pushed branches.
187
+
188
+ ### Commit rules (mirrors `DEFAULT_GIT_CONVENTIONS` in `src/server/services/settings-service.ts`)
189
+
190
+ These rules are the source of truth and are also written to `.ai/git-conventions.md` inside every workspace that the agent creates. Follow them when committing on this repository too.
191
+
192
+ **Commits**
193
+ - Use Conventional Commits: `type(scope): subject`
194
+ - Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`, `build`, `ci`
195
+ - Subject: imperative mood, lowercase, no trailing period, max 72 chars
196
+ - Body: wrap at 72 chars, explain *why* not *what*
197
+ - Reference issues with `Refs #123` or `Closes #123`
198
+ - **NEVER add a `Co-Authored-By:` trailer**, regardless of whether the commit was assisted by an AI agent. Commits on this repository must have a single human author.
199
+
200
+ **Branches**
201
+ - Feature: `feature/<short-kebab-case>`
202
+ - Fix: `fix/<short-kebab-case>`
203
+ - Never commit directly to `main`/`master`/`develop`
204
+
205
+ **Workflow**
206
+ - Rebase on the source branch before opening a PR — do not merge it in
207
+ - Keep commits atomic and self-contained (each compiles and passes tests)
208
+ - Squash fixup commits before pushing
209
+ - Never force-push to shared branches
210
+
211
+ **Safety**
212
+ - Never run destructive commands (`reset --hard`, `push --force`, `clean -fd`) without explicit user confirmation
213
+ - Never skip hooks (`--no-verify`) unless the user explicitly asks
214
+ - Always inspect `git status` and `git diff` before staging
215
+
216
+ ## Human language
217
+
218
+ The human user of this repository prefers French for conversational exchanges. Code, tests, commit messages, and documentation (including this file) remain in English for toolchain compatibility, but chat responses should be in French unless the user switches.
219
+
220
+ ## What NOT to do
221
+
222
+ - Don't drop-and-recreate the database to apply schema changes. The project is in production — every schema change ships as a migration that preserves data (see [Database migrations](#database-migrations)).
223
+ - Don't edit or reorder migration blocks that have already shipped. Migrations are append-only; fixes go in new migrations.
224
+ - Don't add confirmation dialogs for reversible actions (archive, unarchive). Only destructive actions (delete) get a dialog.
225
+ - Don't introduce ORMs, query builders, or schema validation libraries — the project is small enough for raw prepared statements and hand-written mappers.
226
+ - Don't break the single-source-of-truth of `CLAUDE.md` → `AGENTS.md` symlink. Edit `AGENTS.md`; `CLAUDE.md` follows automatically.
227
+ - Don't skip `try/catch` swallowing on best-effort cleanup (agent stop, dev-server stop, worktree removal). These must never break the primary operation.