kanbot 0.2.0 → 0.4.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.
Files changed (62) hide show
  1. package/README.md +15 -9
  2. package/package.json +1 -1
  3. package/schema/config.schema.json +92 -8
  4. package/src/agents/load.ts +11 -0
  5. package/src/agents/sync.ts +8 -8
  6. package/src/capabilities.ts +157 -0
  7. package/src/commands/app.ts +169 -0
  8. package/src/commands/board.ts +14 -7
  9. package/src/commands/doctor.ts +115 -6
  10. package/src/commands/init.ts +15 -3
  11. package/src/commands/skills.ts +1 -1
  12. package/src/commands/sync.ts +1 -1
  13. package/src/commands/upgrade.ts +20 -1
  14. package/src/config.ts +72 -8
  15. package/src/daemon/derive.ts +79 -1
  16. package/src/daemon/loop.ts +10 -1
  17. package/src/daemon/snapshot.ts +1 -0
  18. package/src/daemon/types.ts +17 -0
  19. package/src/github/api.ts +47 -2
  20. package/src/github/app.ts +153 -0
  21. package/src/github/gh.ts +29 -3
  22. package/src/jobs/prompts.ts +30 -6
  23. package/src/jobs/run.ts +135 -56
  24. package/src/known-hashes.json +47 -57
  25. package/src/main.ts +1 -0
  26. package/src/providers/claude.ts +40 -30
  27. package/src/providers/codex.ts +60 -30
  28. package/src/providers/mcp.ts +71 -0
  29. package/src/schemas/results.ts +2 -5
  30. package/src/templates.ts +1 -0
  31. package/templates/.agents/skills/approach-designer/SKILL.md +1 -1
  32. package/templates/.agents/skills/kanbot-admin/SKILL.md +39 -7
  33. package/templates/.agents/skills/kanbot-guide/SKILL.md +110 -0
  34. package/templates/.agents/skills/kanbot-idea/SKILL.md +1 -1
  35. package/templates/.agents/skills/plan-template/SKILL.md +14 -6
  36. package/templates/.agents/skills/pr-description/SKILL.md +7 -3
  37. package/templates/.agents/skills/project-conventions/SKILL.md +23 -35
  38. package/templates/.agents/skills/review-findings/SKILL.md +47 -0
  39. package/templates/.agents/skills/spec-review/SKILL.md +10 -27
  40. package/templates/.agents/skills/user-stories/SKILL.md +1 -1
  41. package/templates/.github/pull_request_template.md +6 -0
  42. package/templates/.kanbot/agents/acceptance-reviewer.md +43 -0
  43. package/templates/.kanbot/agents/fixer.md +7 -5
  44. package/templates/.kanbot/agents/implementer.md +49 -0
  45. package/templates/.kanbot/agents/plan-critic.md +3 -3
  46. package/templates/.kanbot/agents/planner.md +10 -9
  47. package/templates/.kanbot/agents/reviewer.md +42 -0
  48. package/templates/AGENTS.md +3 -4
  49. package/templates/.agents/skills/accessibility/SKILL.md +0 -76
  50. package/templates/.agents/skills/code-review/SKILL.md +0 -83
  51. package/templates/.agents/skills/frontend-design/LICENSE.txt +0 -177
  52. package/templates/.agents/skills/frontend-design/SKILL.md +0 -76
  53. package/templates/.agents/skills/tdd/SKILL.md +0 -51
  54. package/templates/.agents/skills/verify-ui/SKILL.md +0 -104
  55. package/templates/.kanbot/agents/implementer-backend.md +0 -57
  56. package/templates/.kanbot/agents/implementer-docs.md +0 -55
  57. package/templates/.kanbot/agents/implementer-frontend.md +0 -54
  58. package/templates/.kanbot/agents/implementer-technical.md +0 -53
  59. package/templates/.kanbot/agents/reviewer-code.md +0 -46
  60. package/templates/.kanbot/agents/reviewer-spec.md +0 -49
  61. package/templates/.kanbot/agents/test-writer.md +0 -35
  62. package/templates/.kanbot/agents/ui-verifier.md +0 -35
@@ -8,6 +8,7 @@ import {
8
8
  CLAUDE_DENY,
9
9
  claudeTools,
10
10
  } from "../agents/sync.ts";
11
+ import { mcpConfigPath } from "./mcp.ts";
11
12
  import {
12
13
  looksRateLimited,
13
14
  type Provider,
@@ -25,45 +26,54 @@ type ResultEvent = {
25
26
  api_error_status?: number | null;
26
27
  };
27
28
 
29
+ /** CLI args for a headless run. */
30
+ export function claudeArgs(input: RunInput, agentsFile: string): string[] {
31
+ const { agent } = input;
32
+ const cmd = [
33
+ "claude",
34
+ "-p",
35
+ "--agents",
36
+ agentsFile,
37
+ "--agent",
38
+ agent.name,
39
+ "--output-format",
40
+ "stream-json",
41
+ "--verbose",
42
+ "--json-schema",
43
+ JSON.stringify(input.schema),
44
+ "--permission-mode",
45
+ "acceptEdits",
46
+ "--allowedTools",
47
+ ...headlessTools(agent),
48
+ "--disallowedTools",
49
+ ...CLAUDE_DENY,
50
+ "--no-session-persistence",
51
+ // Isolate from the user's own setup: no user-level plugins, skills or
52
+ // MCP servers, so workers only see the repo's agents and skills.
53
+ "--setting-sources",
54
+ "project,local",
55
+ "--strict-mcp-config",
56
+ "--settings",
57
+ JSON.stringify(CLAUDE_ATTRIBUTION),
58
+ ];
59
+ // The repo's committed MCP servers only.
60
+ const mcp = mcpConfigPath(input.cwd);
61
+ if (mcp) cmd.push("--mcp-config", mcp);
62
+ if (agent.models.claude) cmd.push("--model", agent.models.claude);
63
+ if (agent.effort) cmd.push("--effort", agent.effort);
64
+ return cmd;
65
+ }
66
+
28
67
  /** Claude Code headless (`claude -p`), using the logged-in subscription. */
29
68
  export const claude: Provider = {
30
69
  name: "claude",
31
70
  async run(input: RunInput): Promise<RunResult> {
32
- const { agent } = input;
33
71
  // Pass agent definitions explicitly so runs don't depend on what is
34
72
  // committed in the worktree's .claude/agents.
35
73
  const tmp = await mkdtemp(join(tmpdir(), "kanbot-claude-"));
36
74
  const agentsFile = join(tmp, "agents.json");
37
75
  await Bun.write(agentsFile, JSON.stringify(claudeAgentsJson(input.agents)));
38
- const cmd = [
39
- "claude",
40
- "-p",
41
- "--agents",
42
- agentsFile,
43
- "--agent",
44
- agent.name,
45
- "--output-format",
46
- "stream-json",
47
- "--verbose",
48
- "--json-schema",
49
- JSON.stringify(input.schema),
50
- "--permission-mode",
51
- "acceptEdits",
52
- "--allowedTools",
53
- ...headlessTools(agent),
54
- "--disallowedTools",
55
- ...CLAUDE_DENY,
56
- "--no-session-persistence",
57
- // Isolate from the user's own setup: no user-level plugins, skills or
58
- // MCP servers, so workers only see the repo's agents and skills.
59
- "--setting-sources",
60
- "project,local",
61
- "--strict-mcp-config",
62
- "--settings",
63
- JSON.stringify(CLAUDE_ATTRIBUTION),
64
- ];
65
- if (agent.models.claude) cmd.push("--model", agent.models.claude);
66
- if (agent.effort) cmd.push("--effort", agent.effort);
76
+ const cmd = claudeArgs(input, agentsFile);
67
77
 
68
78
  const res = await spawnWorker(cmd, {
69
79
  cwd: input.cwd,
@@ -1,8 +1,10 @@
1
1
  import { mkdtemp, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
+ import type { AgentDef } from "../agents/load.ts";
4
5
  import { agentInstructions } from "../agents/sync.ts";
5
6
  import { git } from "../daemon/git.ts";
7
+ import { codexMcpArgs, type McpServer, readMcpServers } from "./mcp.ts";
6
8
  import {
7
9
  looksRateLimited,
8
10
  type Provider,
@@ -11,6 +13,58 @@ import {
11
13
  spawnWorker,
12
14
  } from "./types.ts";
13
15
 
16
+ /** CLI args for a headless run. */
17
+ export function codexArgs(o: {
18
+ agent: AgentDef;
19
+ cwd: string;
20
+ gitDir: string;
21
+ schemaFile: string;
22
+ lastFile: string;
23
+ networkAccess: boolean;
24
+ mcpServers: Record<string, McpServer>;
25
+ }): string[] {
26
+ const { agent } = o;
27
+ const sandbox =
28
+ agent.tools.includes("edit") || agent.tools.includes("bash")
29
+ ? "workspace-write"
30
+ : "read-only";
31
+ const cmd = [
32
+ "codex",
33
+ "exec",
34
+ "--json",
35
+ "--ephemeral",
36
+ // Isolate from the user's own setup: no user-level plugins or MCP
37
+ // servers. Auth still comes from CODEX_HOME.
38
+ "--ignore-user-config",
39
+ "--sandbox",
40
+ sandbox,
41
+ "--cd",
42
+ o.cwd,
43
+ "--add-dir",
44
+ o.gitDir,
45
+ "--output-schema",
46
+ o.schemaFile,
47
+ "--output-last-message",
48
+ o.lastFile,
49
+ "-c",
50
+ `developer_instructions=${JSON.stringify(agentInstructions(agent))}`,
51
+ // The repo's committed MCP servers only.
52
+ ...codexMcpArgs(o.mcpServers),
53
+ ];
54
+ if (o.networkAccess)
55
+ cmd.push("-c", "sandbox_workspace_write.network_access=true");
56
+ if (agent.models.codex && agent.models.codex !== "default")
57
+ cmd.push("--model", agent.models.codex);
58
+ if (agent.effort) {
59
+ cmd.push(
60
+ "-c",
61
+ `model_reasoning_effort="${agent.effort === "max" ? "xhigh" : agent.effort}"`,
62
+ );
63
+ }
64
+ cmd.push("-");
65
+ return cmd;
66
+ }
67
+
14
68
  /** Codex CLI headless (`codex exec`), using the logged-in ChatGPT plan. */
15
69
  export function codex({ networkAccess }: { networkAccess: boolean }): Provider {
16
70
  return {
@@ -29,39 +83,15 @@ export function codex({ networkAccess }: { networkAccess: boolean }): Provider {
29
83
  "--path-format=absolute",
30
84
  "--git-common-dir",
31
85
  );
32
- const sandbox =
33
- agent.tools.includes("edit") || agent.tools.includes("bash")
34
- ? "workspace-write"
35
- : "read-only";
36
- const cmd = [
37
- "codex",
38
- "exec",
39
- "--json",
40
- "--ephemeral",
41
- "--sandbox",
42
- sandbox,
43
- "--cd",
44
- input.cwd,
45
- "--add-dir",
86
+ const cmd = codexArgs({
87
+ agent,
88
+ cwd: input.cwd,
46
89
  gitDir,
47
- "--output-schema",
48
90
  schemaFile,
49
- "--output-last-message",
50
91
  lastFile,
51
- "-c",
52
- `developer_instructions=${JSON.stringify(agentInstructions(agent))}`,
53
- ];
54
- if (networkAccess)
55
- cmd.push("-c", "sandbox_workspace_write.network_access=true");
56
- if (agent.models.codex && agent.models.codex !== "default")
57
- cmd.push("--model", agent.models.codex);
58
- if (agent.effort) {
59
- cmd.push(
60
- "-c",
61
- `model_reasoning_effort="${agent.effort === "max" ? "xhigh" : agent.effort}"`,
62
- );
63
- }
64
- cmd.push("-");
92
+ networkAccess,
93
+ mcpServers: await readMcpServers(input.cwd),
94
+ });
65
95
 
66
96
  const res = await spawnWorker(cmd, {
67
97
  cwd: input.cwd,
@@ -0,0 +1,71 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ /*
5
+ * Workers get the repo's committed MCP servers, never the user's own.
6
+ * `.mcp.json` (Claude's format) is the one source for both providers.
7
+ */
8
+
9
+ export type McpServer = {
10
+ type?: string;
11
+ command?: string;
12
+ args?: string[];
13
+ env?: Record<string, string>;
14
+ url?: string;
15
+ headers?: Record<string, string>;
16
+ };
17
+
18
+ export function mcpConfigPath(dir: string): string | null {
19
+ const path = join(dir, ".mcp.json");
20
+ return existsSync(path) ? path : null;
21
+ }
22
+
23
+ /** Servers in `<dir>/.mcp.json`. Empty when missing or unreadable. */
24
+ export async function readMcpServers(
25
+ dir: string,
26
+ ): Promise<Record<string, McpServer>> {
27
+ const path = mcpConfigPath(dir);
28
+ if (!path) return {};
29
+ try {
30
+ const json = (await Bun.file(path).json()) as {
31
+ mcpServers?: Record<string, McpServer>;
32
+ };
33
+ return json.mcpServers ?? {};
34
+ } catch {
35
+ return {};
36
+ }
37
+ }
38
+
39
+ /** Codex runs stdio and streamable HTTP servers. Others (e.g. sse) are left out. */
40
+ export function codexSupports(s: McpServer): boolean {
41
+ if (s.type === "sse") return false;
42
+ return Boolean(s.command || s.url);
43
+ }
44
+
45
+ const str = (s: string) => JSON.stringify(s);
46
+ const table = (o: Record<string, string>) =>
47
+ `{${Object.entries(o)
48
+ .map(([k, v]) => `${str(k)}=${str(v)}`)
49
+ .join(",")}}`;
50
+
51
+ /** `-c mcp_servers=…` for codex, as an inline TOML table. Empty when none. */
52
+ export function codexMcpArgs(servers: Record<string, McpServer>): string[] {
53
+ const entries = Object.entries(servers)
54
+ .filter(([, s]) => codexSupports(s))
55
+ .map(([name, s]) => {
56
+ const fields = s.command
57
+ ? [
58
+ `command=${str(s.command)}`,
59
+ s.args?.length ? `args=[${s.args.map(str).join(",")}]` : "",
60
+ s.env && Object.keys(s.env).length ? `env=${table(s.env)}` : "",
61
+ ]
62
+ : [
63
+ `url=${str(s.url as string)}`,
64
+ s.headers && Object.keys(s.headers).length
65
+ ? `http_headers=${table(s.headers)}`
66
+ : "",
67
+ ];
68
+ return `${str(name)}={${fields.filter(Boolean).join(",")}}`;
69
+ });
70
+ return entries.length ? ["-c", `mcp_servers={${entries.join(",")}}`] : [];
71
+ }
@@ -35,7 +35,7 @@ export const PlanResult = z.strictObject({
35
35
  .describe("Issues to create, e.g. when the human asks to split work."),
36
36
  labels: z
37
37
  .array(z.string())
38
- .describe("Labels the issue should have, e.g. type:frontend or technical."),
38
+ .describe("Labels the issue should have, e.g. technical."),
39
39
  questions: z
40
40
  .array(z.string())
41
41
  .describe(
@@ -55,9 +55,6 @@ export const ImplementResult = z.strictObject({
55
55
  testsRun: z
56
56
  .array(z.string())
57
57
  .describe("One line each: command and result, e.g. `bun test`: 5 pass."),
58
- screenshots: z
59
- .array(z.string())
60
- .describe("Repo-relative paths of screenshots committed for review."),
61
58
  risks: z
62
59
  .array(z.string())
63
60
  .describe("Real risks only, one line each. Empty when none."),
@@ -98,7 +95,7 @@ export const FixResult = z.strictObject({
98
95
  .string()
99
96
  .nullable()
100
97
  .describe(
101
- "Full updated PR description when your changes made the current one wrong (behaviour, copy, screenshots); null otherwise.",
98
+ "Full updated PR description when your changes made the current one wrong (behaviour, copy, validation); null otherwise.",
102
99
  ),
103
100
  });
104
101
  export type FixResult = z.infer<typeof FixResult>;
package/src/templates.ts CHANGED
@@ -24,6 +24,7 @@ export type FileClass = "kanbot" | "user" | "shared";
24
24
  const CLASSES: [prefix: string, FileClass][] = [
25
25
  [".github/workflows/kanbot-", "kanbot"],
26
26
  [".agents/skills/kanbot-admin/", "kanbot"],
27
+ [".agents/skills/kanbot-guide/", "kanbot"],
27
28
  [".agents/skills/kanbot-idea/", "kanbot"],
28
29
  ["AGENTS.md", "user"],
29
30
  ["CLAUDE.md", "user"],
@@ -17,7 +17,7 @@ Pick the axis where the decision matters most for this problem, then spread appr
17
17
  | Interaction model | Search box · faceted filters · timeline browsing |
18
18
  | Where logic lives | Client-side filtering of loaded data · server query endpoint · search index |
19
19
  | Data model | Denormalised read model · query on existing tables · materialised view |
20
- | Build vs. reuse | Custom component · existing design-system table · third-party library |
20
+ | Build vs. reuse | Build it · reuse what the project has · third-party library |
21
21
  | Timing | Synchronous · background job with notification · precomputed nightly |
22
22
  | Scope of change | Minimal tweak to the current screen · new dedicated screen |
23
23
 
@@ -1,12 +1,12 @@
1
1
  ---
2
2
 
3
3
  name: kanbot-admin
4
- description: Maintain this repo's kanbot setup with a human. Use to add or edit kanbot agents, skills or project memory, change `.kanbot/config.yml`, run `kanbot upgrade` or merge `.kanbot-new` files. Not for board jobs.
4
+ description: Change this repo's kanbot setup with a human. Use to add or edit kanbot agents, skills or project memory, map capabilities, give workers MCP servers, change `.kanbot/config.yml`, switch identity mode, turn on merge on approval, run `kanbot upgrade` or merge `.kanbot-new` files. Not for board jobs. For setup walkthroughs and explaining options, use kanbot-guide.
5
5
  ---
6
6
 
7
7
  # kanbot admin
8
8
 
9
- You are helping a human maintain kanbot, the board-driven agent team in this repo. Board agents (planner, implementers, reviewers, fixer) never use this skill.
9
+ You are helping a human maintain kanbot, the board-driven agent team in this repo. Board agents (planner, implementer, reviewers, fixer) never use this skill. To explain options or set kanbot up from scratch, use `kanbot-guide`.
10
10
 
11
11
  ## Map
12
12
 
@@ -14,27 +14,58 @@ You are helping a human maintain kanbot, the board-driven agent team in this rep
14
14
  | -------------------------- | -------------------------------------- | ------------------------------- |
15
15
  | `.kanbot/agents/<name>.md` | Agent definitions, the source of truth | Yes, then `kanbot sync` |
16
16
  | `.agents/skills/<name>/` | Skills shared by all agents | Yes |
17
- | `.kanbot/config.yml` | Daemon config; schema next to it | Yes |
17
+ | `.kanbot/config.yml` | Daemon config; schema next to it | Yes, then restart the daemon |
18
+ | `.mcp.json` | MCP servers workers get | Yes, then commit |
18
19
  | `.kanbot/manifest.json` | Template hashes from the last upgrade | Never by hand |
19
20
  | `.claude/`, `.codex/` | Generated by `kanbot sync` | Never; edit the source and sync |
21
+ | `~/.kanbot/apps/<id>.pem` | Private key of the kanbot GitHub App | Never commit |
20
22
 
21
- `kanbot <command> --help` lists every flag. Agent frontmatter fields and output schemas: read an existing agent in `.kanbot/agents/` and copy its shape.
23
+ `kanbot <command> --help` lists every flag. `.kanbot/config.schema.json` describes every config field. Docs: https://kanbot.mib.im.
22
24
 
23
25
  ## Recipes
24
26
 
25
- Finish every recipe with `kanbot sync` when agents changed, then `kanbot doctor`, then show the human the diff. The human commits and pushes; worktrees are made from the pushed base branch, so agents see nothing until then.
27
+ Finish every recipe with `kanbot sync` when agents changed, then `kanbot doctor`, then show the human the diff. The human commits and pushes; worktrees are made from the pushed base branch, so agents see nothing until then. Restart the daemon after config changes.
28
+
29
+ ### Switch to app mode
30
+
31
+ 1. `kanbot app create`. The human clicks **Create GitHub App** on GitHub, then installs it on the repo.
32
+ 2. Already have an app from another repo? `kanbot app link --id <id> --slug <slug>`, add the repo to its installation, and copy `~/.kanbot/apps/<id>.pem` if on another machine.
33
+ 3. `kanbot doctor` checks the app, its key and the install.
34
+
35
+ Back to user mode: set `identity.mode: user`.
36
+
37
+ ### Turn on merge on approval
38
+
39
+ 1. Set `merge.onApproval: true` and pick `merge.method` (`squash`, `merge` or `rebase`).
40
+ 2. Recommend app mode, so they can approve their own kanbot PRs.
41
+ 3. Warn: never enable the branch rule "Require approval of the most recent reviewable push". It blocks their own approval.
42
+
43
+ ### Map a capability
44
+
45
+ kanbot's agents run the process; the repo's own skills and agents do the work on code. Capabilities tell them which to use.
46
+
47
+ 1. Make sure the skill is in `.agents/skills/<name>/` or the agent in `.kanbot/agents/`. A `.claude/agents` or `.codex/agents` agent works on one provider only.
48
+ 2. Add it under `capabilities` in config: `review: code-review`, or `{ use, when, required }` when it only applies sometimes or a miss should block acceptance review.
49
+ 3. Reserved names: `verify` (prove acceptance criteria), `review` (technical review, pass 1), `preview` (something a human can try). Any other name is the human's.
50
+ 4. `kanbot doctor` lists what's mapped, flags a missing `use`, and names reserved needs not yet mapped.
51
+
52
+ Don't add implementers or `type:*` labels. `implementers` in config is deprecated: replace it with capabilities and remove it.
53
+
54
+ ### Give workers an MCP server
55
+
56
+ Add it to `.mcp.json` (Claude's format) and commit. Workers get only these servers, never the human's own. Codex can't run `sse` servers; `kanbot doctor` lists what workers get.
26
57
 
27
58
  ### Add or change an agent
28
59
 
29
60
  1. Copy the closest existing agent. `name` must match the filename.
30
61
  2. Column agents (`tier: column`) need an `output` schema; subagents (`tier: sub`) are reached through a column agent's `subagents`.
31
- 3. A new implementer also needs an entry under `implementers` in config and a matching `type:<x>` label on GitHub. Tell the human to create the label.
62
+ 3. To have board agents use it, map it as a capability (above).
32
63
  4. The body says _how_ to work. The daemon's prompt carries the task.
33
64
 
34
65
  ### Add a skill
35
66
 
36
67
  1. Create `.agents/skills/<name>/SKILL.md` with `name` (matches the folder) and a `description` that says what it covers and when it applies.
37
- 2. Add the name to `skills` in each agent that should load it.
68
+ 2. For work on code, map it as a capability. For kanbot's own process, add the name to `skills` in each agent that should load it. `kanbot doctor` fails on skills that don't exist.
38
69
 
39
70
  ### Add a memory
40
71
 
@@ -45,6 +76,7 @@ When the human says "remember that …" or corrects something agents keep gettin
45
76
  1. Run `bunx kanbot@latest upgrade --dry-run` and summarise it for the human.
46
77
  2. Run `bunx kanbot@latest upgrade`.
47
78
  3. Merge every `.kanbot-new` file (below).
79
+ 4. Files kanbot no longer ships stay in place. If the repo had kanbot's old craft skills (`verify-ui`, `code-review`, `tdd`…) and no `capabilities`, upgrade maps them. Show the human the new `capabilities` block. Old `implementer-*` and `reviewer-*` agents are unused once the new ones exist; the human may delete them.
48
80
 
49
81
  ### Merge `.kanbot-new` files
50
82
 
@@ -0,0 +1,110 @@
1
+ ---
2
+
3
+ name: kanbot-guide
4
+ description: Explain kanbot and guide a human through it. Use when someone asks what kanbot is, how to set it up in a repo, which config options to pick (provider, bot or user identity, merge on approval, Copilot, notifications), how the board flow works, where something is documented, or why something isn't working. Works before kanbot is installed in the repo. For changing agents, skills or upgrading, use kanbot-admin.
5
+ ---
6
+
7
+ # kanbot guide
8
+
9
+ You are helping a human understand and set up kanbot: a daemon that runs a team of Claude Code or Codex agents from a GitHub Project board. They write and prioritize issues; agents plan, build, review and fix; they approve plans and merge.
10
+
11
+ Explain in plain words. Ask before choosing for them; give a recommendation with one line of why. Docs live at https://kanbot.mib.im/<slug>. Link the page instead of repeating it.
12
+
13
+ ## Docs
14
+
15
+ | Question | Slug |
16
+ | --------------------------------------------- | ------------------- |
17
+ | How do I install and set up a repo? | `getting-started` |
18
+ | How does a card move across the board? | `workflow` |
19
+ | How do I file ideas by chatting? | `capturing-ideas` |
20
+ | My own PRs, local sessions, direct commits? | `outside-the-board` |
21
+ | Which agents are there, how do I change them? | `agents` |
22
+ | Skills and project memory? | `skills` |
23
+ | Claude or Codex? | `providers` |
24
+ | Every config field, labels, notifications | `configuration` |
25
+ | Using our own skills and agents for the work | `capabilities` |
26
+ | Copilot as an extra reviewer | `copilot-review` |
27
+ | Bot or user identity, merge by approving | `identity` |
28
+ | Upgrading and `.kanbot-new` files | `upgrading` |
29
+ | Every command and flag | `usage` |
30
+
31
+ ## Board flow
32
+
33
+ Ideas (you write) → Planning (agent plans, you comment) → Ready (you approve by moving the card) → In Progress (agent builds, opens a PR) → Review (technical, then acceptance review, with fixes; you comment) → Done (you merge, or approve and kanbot merges).
34
+
35
+ kanbot runs the process; the repo's own skills and agents do the work on code, through capabilities. Plans list assumptions about user need; open questions keep a card from starting in Ready.
36
+
37
+ Agents never get a GitHub token. The daemon does every comment, push and PR. Only collaborators with write access can steer agents. Workers see only what's committed, including MCP servers in `.mcp.json`; never the user's own plugins or servers.
38
+
39
+ ## Guided setup
40
+
41
+ Go step by step. Run each command, check its output, then go on. If `.kanbot/config.yml` exists, run `kanbot doctor` first and skip what passes.
42
+
43
+ 1. **Requirements.** Bun 1.3+, `gh` logged in, `claude` or `codex` logged in with a subscription (not an API key). Run `gh auth refresh -s project`.
44
+ 2. **Provider.** Ask which subscription they have, then `bunx kanbot init --provider claude|codex` in the repo root.
45
+ 3. **Conventions.** Fill in `.agents/skills/project-conventions/SKILL.md` with them: stack, commands to test, lint and typecheck. Adjust `.github/workflows/ci.yml` to run the same. Every agent reads this first.
46
+ 4. **Capabilities.** kanbot runs the process; the repo's own skills and agents do the work. Ask which skills they have for proving acceptance criteria (`verify`), technical review (`review`) and something a human can try (`preview`), and map them under `capabilities` in `.kanbot/config.yml`. See [Capabilities](#capabilities).
47
+ 5. **Board.** `kanbot board create`, or `kanbot board link --owner <login> --number <n>` for an existing project. Have them enable the four built-in project workflows it prints; they can't be set through the API.
48
+ 6. **Options.** Go through [Choose options](#choose-options). For app mode, run `kanbot app create` and have them install the app on the repo.
49
+ 7. **Commit and push** the kanbot files. Agents only see what's on the pushed base branch.
50
+ 8. **Check.** `kanbot doctor` until it passes, then `kanbot run --once --dry-run`, then `kanbot run`.
51
+ 9. **First card.** Have them write an issue, put it in Planning and read the plan. Point to `workflow`.
52
+
53
+ ## Capabilities
54
+
55
+ ```yaml
56
+ capabilities:
57
+ verify: { use: e2e-tests, when: Changes users see }
58
+ preview: { use: deploy-preview, required: true }
59
+ review: code-review # bare name: no when, not required
60
+ ```
61
+
62
+ - `use`: a skill in `.agents/skills` or an agent in `.kanbot/agents`. `when`: when it applies. `required: true`: a miss blocks acceptance review.
63
+ - Any other name is theirs (`benchmark`, `security`…). Unmapped needs are left to the agent.
64
+ - Suggest, don't install: no `verify` → a skill that runs the app or end-to-end tests; no `preview` → a preview deploy or test build; no `review` → a review skill with their checklist. `kanbot doctor` names what's not mapped.
65
+
66
+ ## Choose options
67
+
68
+ Ask only about what matters to them. Defaults are fine for the rest. Options live in `.kanbot/config.yml`; restart the daemon after changes.
69
+
70
+ | Option | Ask | Recommend |
71
+ | ---------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
72
+ | `identity.mode` (`user` \| `app`) | Should kanbot talk as you, or as its own bot? | `app`: clear who said what, GitHub notifies you, and you can approve its PRs. `user` for zero setup. Commits are yours in both. |
73
+ | `merge.onApproval`, `merge.method` | Merge by approving the PR, or press merge yourself? | `true` with `app` mode. In `user` mode only another collaborator can approve. |
74
+ | `provider` | Claude or ChatGPT subscription? | Whichever they pay for. |
75
+ | `concurrency` | How many agents at once? | Default 3. Lower on a small machine or near usage limits. |
76
+ | `review.copilot` | Want GitHub Copilot as an extra reviewer? (costs credits) | Off unless they have a paid Copilot plan. |
77
+ | `notify.desktop`, `notify.webhook` | Where do you want to hear that a PR is ready? | Desktop. Add an ntfy.sh webhook for phone alerts. |
78
+ | `codex.networkAccess` | Do agents need to install packages? (Codex only) | Off unless installs fail. |
79
+ | `explore:N` label | Want several approaches built for some issues? | Per issue, not config. |
80
+
81
+ ### Who does what
82
+
83
+ | Action | `user` mode | `app` mode |
84
+ | ----------------------------------------- | ---------------------- | ------------ |
85
+ | Comments, PRs, labels, issue and PR edits | User, `🤖 kanbot` line | `<app>[bot]` |
86
+ | Commits, pushes, merges, board moves | User | User |
87
+
88
+ Board moves stay on the user's account: GitHub Apps can't use boards owned by a personal account.
89
+
90
+ ### App mode
91
+
92
+ - `kanbot app create` opens GitHub; they click **Create GitHub App**, then install it on the repo. The key goes in `~/.kanbot/apps/<id>.pem`; never commit it.
93
+ - Same app in another repo: `kanbot app link --id <id> --slug <slug>`, then add the repo to the app's installation.
94
+ - Back to user mode: `identity.mode: user`.
95
+
96
+ ### Merge on approval
97
+
98
+ kanbot merges, as the user, only `kanbot/*` PRs that are agent-reviewed, approved on the latest commit by a collaborator, with no open change request, passing checks, no conflicts and no `needs-human`. A push after approval needs a new approval.
99
+
100
+ Warn: never enable the branch rule "Require approval of the most recent reviewable push". The user's account pushes kanbot's commits, so it blocks their own approval.
101
+
102
+ ## Troubleshoot
103
+
104
+ 1. `kanbot doctor`: prints a fix for each failed check.
105
+ 2. `kanbot status`: board by column, daemon, failures, rate-limit pause.
106
+ 3. `.kanbot/logs/*.jsonl`: full agent transcripts.
107
+ 4. `needs-human` label: the issue has a comment saying why. Fix the cause, then remove the label to retry.
108
+ 5. Nothing happens: is the daemon running, are kanbot files pushed to the base branch, is the card in an active column?
109
+ 6. Card stays in Ready: the plan has open questions. kanbot commented on the issue. Answer them in Planning, or remove them from the plan.
110
+ 7. Agent didn't use a skill: is it mapped under `capabilities`, does its `when` fit, and is it committed?
@@ -45,7 +45,7 @@ Match the repo's issue template, so the issue reads like one filed through GitHu
45
45
  <notes, or _None._>
46
46
  ```
47
47
 
48
- Title: the outcome in under 70 characters. Technical issues get the `technical` label. Leave `type:*` labels to the planner.
48
+ Title: the outcome in under 70 characters. Technical issues get the `technical` label.
49
49
 
50
50
  Show the title and body. File it only after the human says yes.
51
51
 
@@ -20,13 +20,18 @@ Need: <one or two sentences: what hurts today and how we'll know it worked.>
20
20
  - [ ] Given <context>, when <action>, then <observable result>.
21
21
  <!-- 2-6 items. Cover the happy path plus the edge cases that matter. -->
22
22
 
23
+ ## Assumptions
24
+
25
+ - <What the plan assumes about the user's need or the business value.> Confirm: <ask, data, or a user test>.
26
+ <!-- 1-4 items. Risky, unconfirmed ones also go in `questions`. -->
27
+
23
28
  ## Out of scope
24
29
 
25
30
  - <Only what a reader would otherwise expect. Omit the section if nothing.>
26
31
 
27
32
  ## Design notes
28
33
 
29
- <UI: placement, key copy, states that differ from the obvious. Non-UI: API or data shape. 1-4 lines. Omit if nothing beyond the criteria.>
34
+ <Decisions the criteria don't settle: what users see or call, key copy, data shape. 1-4 lines. Omit if nothing beyond the criteria.>
30
35
 
31
36
  ## Implementation steps
32
37
 
@@ -39,12 +44,14 @@ Need: <one or two sentences: what hurts today and how we'll know it worked.>
39
44
 
40
45
  ## Open questions
41
46
 
42
- - <Only what blocks the human's approval, with the default you assume. Omit if none.>
47
+ <!-- Don't write this section. kanbot fills it from your `questions` output. Open questions keep the issue out of Ready. -->
43
48
  ```
44
49
 
45
50
  ## Rules
46
51
 
47
52
  - Required: User story (or Technical goal), Acceptance criteria, Implementation steps. Omit other sections when empty; don't write "None."
53
+ - Assumptions are about user need and business value, not code. Technical unknowns go in `explore:N` or a spike.
54
+ - Ask a question (`questions` output) only if building on a wrong guess would waste the work. kanbot won't start while any are open.
48
55
  - One line per bullet. No prose paragraphs, no repeating the issue text.
49
56
  - Acceptance criteria are shared by all approaches. Approach-specific steps go inside each `### Approach k`.
50
57
  - File paths come from reading the code, not guesses. Mark new files "(new)".
@@ -65,6 +72,11 @@ Need: entries without a project silently drop off invoices; users find out when
65
72
  - [ ] Given every entry has a project, when I open the month view, then the list is hidden.
66
73
  - [ ] Given loading fails, when I open the month view, then the list shows "Couldn't load entries" and "Try again".
67
74
 
75
+ ## Assumptions
76
+
77
+ - Users fix missing projects before invoicing, not after. Confirm: ask two users how they invoice.
78
+ - Most months have under 20 such entries. Confirm: query last quarter's data.
79
+
68
80
  ## Out of scope
69
81
 
70
82
  - Bulk-assigning projects (new issue).
@@ -78,8 +90,4 @@ List sits above the calendar, collapsed to 5 rows with "Show all (n)". Reuse `Pr
78
90
  1. Add `missingProject` filter to `GET /entries`, with tests. Files: `api/entries/routes.ts`
79
91
  2. Add `MissingProjectSection` with its states and tests. Files: `web/entries/MissingProjectSection.tsx` (new)
80
92
  3. Mount it in the month view and refresh after assignment. Files: `web/month/MonthView.tsx`
81
-
82
- ## Open questions
83
-
84
- - Include earlier months? Default: current month only.
85
93
  ```
@@ -27,8 +27,12 @@ Conventional commit, imperative, under 72 characters, naming the user-visible ef
27
27
 
28
28
  ## Screenshots
29
29
 
30
- ![<state, width>](.kanbot/screenshots/<branch>/<name>.png)
31
- <!-- UI changes only; 1-3 images that show the change. -->
30
+ ![<state>](repo-relative path to a committed image)
31
+ <!-- Optional. Only when the change is visible; 1-3 images. -->
32
+
33
+ ## Validation
34
+
35
+ - <What you used to prove it works (tests, skills, preview) and what it showed. 1-3 bullets.>
32
36
 
33
37
  ## Notes for reviewer
34
38
 
@@ -40,5 +44,5 @@ Conventional commit, imperative, under 72 characters, naming the user-visible ef
40
44
  - One line per bullet. No "Why" section; the issue has it.
41
45
  - Describe what the code does now, not how you got there.
42
46
  - Anything you could not verify goes in `risks`, not the body.
43
- - Screenshot paths must match the `screenshots` output field.
47
+ - Omit Screenshots when there is nothing to show.
44
48
  - Aim for under 20 lines.