kanbo-cli 0.2.0 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-BwpC-4BO.mjs","names":["requireCard","requireCard","requireCard","requireCard","requireRun","requireCard","requireCard","requireCard","requireCard","assertPostgresBoardSchema","describePlan"],"sources":["../../src/cli/output.ts","../../src/cli/actor.ts","../../src/domain/numbering.ts","../../src/domain/issue-view.ts","../../src/domain/issue-activity.ts","../../src/domain/time.ts","../../src/ops/types.ts","../../src/ops/activity.ts","../../src/ops/approval.ts","../../src/domain/field-changes.ts","../../src/domain/status-name.ts","../../src/ops/change-seq.ts","../../src/domain/entry-rules.ts","../../src/ops/columns.ts","../../src/domain/pull-request-fact-keys.ts","../../src/ops/entry-rules.ts","../../src/domain/external-session-ref.ts","../../src/ops/runs.ts","../../src/ops/cards.ts","../../src/ops/card-batch.ts","../../src/ops/context-refs.ts","../../src/ops/migrate.ts","../../src/ops/agent-rules.ts","../../src/ops/prime.ts","../../src/domain/pull-request-ref.ts","../../src/ops/pull-requests.ts","../../src/ops/ready.ts","../../src/ops/relations.ts","../../src/ops/sprints.ts","../../src/ops/index.ts","../../src/domain/database-url.ts","../../src/cli/binding.ts","../../src/cli/db-target.ts","../../src/cli/postgres-board.ts","../../src/cli/schema-guard.ts","../../src/cli/workspace.ts","../../src/cli/command.ts","../../src/domain/card-display-title.ts","../../src/cli/view.ts","../../src/cli/commands/approve.ts","../../src/capabilities/markdown.ts","../../src/cli/commands/capabilities.ts","../../src/cli/commands/card.ts","../../src/cli/commands/columns.ts","../../src/package-version.ts","../../src/cli/failure.ts","../../src/cli/setup/instructions.ts","../../src/cli/setup/paths.ts","../../src/cli/setup/mcp-config.ts","../../src/cli/doctor.ts","../../src/cli/commands/doctor.ts","../../src/default-board-file-path.ts","../../src/cli/setup/confirm.ts","../../src/cli/setup/file-change.ts","../../src/cli/commands/init-global.ts","../../src/cli/commands/init.ts","../../src/mcp/transport.ts","../../src/mcp/db-transport.ts","../../src/mcp/resources.ts","../../src/mcp/instructions.ts","../../src/mcp/tools/tool.ts","../../src/mcp/tools/card-comment.ts","../../src/mcp/tools/card-create.ts","../../src/mcp/tools/card-get.ts","../../src/mcp/tools/card-link-pr.ts","../../src/mcp/tools/card-list.ts","../../src/mcp/tools/card-move.ts","../../src/mcp/tools/card-pull-requests.ts","../../src/mcp/tools/card-update.ts","../../src/mcp/tools/columns.ts","../../src/mcp/tools/prime.ts","../../src/mcp/tools/ready.ts","../../src/mcp/tools/run-finish.ts","../../src/mcp/tools/run-start.ts","../../src/mcp/tools/sprints.ts","../../src/mcp/tools/status-line.ts","../../src/mcp/tools/wait-approval.ts","../../src/mcp/tools/index.ts","../../src/mcp/server.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/migrate.ts","../../src/cli/commands/prime.ts","../../src/cli/commands/ready.ts","../../src/cli/commands/return.ts","../../src/cli/commands/roles.ts","../../src/cli/commands/run.ts","../../src/serve/actors.ts","../../src/serve/board-page.ts","../../src/serve/board-error-responses.ts","../../src/serve/errors.ts","../../src/serve/router.ts","../../src/serve/schemas.ts","../../src/serve/views.ts","../../src/serve/routes.ts","../../src/serve/server.ts","../../src/serve/access.ts","../../src/cli/commands/serve.ts","../../src/cli/commands/sprint.ts","../../src/cli/commands/uninstall.ts","../../src/cli/commands/index.ts","../../src/mcp/tool-names.ts","../../src/capabilities/manifest.ts"],"sourcesContent":["/**\n * Everything the board's command line writes, and the code it leaves with.\n *\n * The contract is deliberately small: a command hands back a value and — when\n * there is a good way to say it in one breath — the text a person should read.\n * `--format` chooses between the two machine shapes, and `--json <fields>` is\n * the agent's shortcut: the same value with only the named fields kept, so a\n * script can ask for `title,column` and get exactly that.\n *\n * The exit codes live here too, because a failure is output as much as a\n * result is: the board tool promises `2` for \"I could not find what to work\n * on\", `3` for \"this database is older than I am\" and `4` for \"a person has to\n * do this\", and a caller may branch on them.\n */\n\n/** What a command produces: the value, and how to say it to a person. */\nexport interface CliResult {\n /** The machine-readable result, printed as JSON unless `text` takes over. */\n value: unknown\n /** What a person reads when no format was asked for. */\n text?: string\n /**\n * The fields `--json` may name, declared up front rather than read off a row:\n * a result that comes back as an empty list still tells a caller what it\n * could have asked for instead of guessing from nothing.\n */\n fields?: readonly string[]\n}\n\n/** The two machine shapes `--format` offers. */\nexport type CliFormat = 'json' | 'pretty'\n\n/** The output flags every board command carries. */\nexport interface CliOutputOptions {\n /** Comma-separated field names; printing keeps only these. */\n json?: string\n format?: string\n}\n\n/** Nothing to work on, or nothing told us what to work on. */\nexport const EXIT_NOT_RESOLVED = 2\n/** The database is older than the board schema this build speaks. */\nexport const EXIT_SCHEMA_OUTDATED = 3\n/** The command is a person's to run, and this shell is not a person. */\nexport const EXIT_HUMAN_ONLY = 4\n\n/**\n * Every exit code this tool promises, and what it means — the capabilities\n * manifest's `exitCodes` reads this rather than naming the codes again. `0` and\n * `1` are not declared above: they need no name of their own, success and \"I\n * could not do that\" being every command line's baseline.\n */\nexport const CLI_EXIT_CODES = [\n { code: 0, meaning: 'Success.' },\n { code: 1, meaning: 'The command did not do what it was asked. See the message.' },\n { code: EXIT_NOT_RESOLVED, meaning: 'Nothing to work on, or nothing told the command what to work on.' },\n { code: EXIT_SCHEMA_OUTDATED, meaning: 'The database is older than the board schema this build speaks.' },\n { code: EXIT_HUMAN_ONLY, meaning: 'The command is a person\\'s to run, and this shell is not a person.' },\n] as const satisfies readonly { code: number, meaning: string }[]\n\n/**\n * A failure with a promised exit code. Anything else that goes wrong leaves\n * with `1`, which is the command line saying only that it did not work.\n */\nexport class CliError extends Error {\n readonly exitCode: number\n\n constructor(exitCode: number, message: string) {\n super(message)\n this.name = 'CliError'\n this.exitCode = exitCode\n }\n}\n\n/** The format a caller asked for, or the error that says there is no such format. */\nexport function readFormat(options: CliOutputOptions): CliFormat | null {\n if (options.format === undefined) {\n return null\n }\n if (options.format !== 'json' && options.format !== 'pretty') {\n throw new CliError(1, `Unknown format \"${options.format}\". Use json or pretty.`)\n }\n return options.format\n}\n\n/**\n * Write the result out.\n *\n * Without a format and without `--json`, a command that offered text gets its\n * text: `kanbo prime` prints the board's rules the way an agent should read\n * them, not a JSON string with the newlines spelled out. Everything else falls\n * back to indented JSON, which is the honest answer when the value has no\n * one-breath form.\n */\nexport function printResult(result: CliResult, options: CliOutputOptions): void {\n const fields = readFields(options.json)\n const format = readFormat(options)\n\n if (fields) {\n warnUnknownFields(fields, result)\n console.log(JSON.stringify(selectFields(result.value, fields), null, 2))\n return\n }\n if (format === 'json') {\n console.log(JSON.stringify(result.value))\n return\n }\n if (format === 'pretty' || result.text === undefined) {\n console.log(JSON.stringify(result.value, null, 2))\n return\n }\n console.log(result.text)\n}\n\nfunction readFields(json: string | undefined): string[] | null {\n if (json === undefined) {\n return null\n }\n const fields = json.split(',').map(field => field.trim()).filter(field => field.length > 0)\n if (fields.length === 0) {\n throw new CliError(1, 'Pass --json a comma-separated list of fields, for example --json title,column.')\n }\n return fields\n}\n\n/**\n * Say so, on stderr, about every field a caller asked for that this result\n * does not carry — one line per field, so a typo is caught instead of quietly\n * printing an object with the field missing. Never the exit code: the fields\n * that do exist still print, and an agent that asked for nine good fields and\n * one bad one should get the nine.\n */\nfunction warnUnknownFields(fields: string[], result: CliResult): void {\n const available = declaredFields(result)\n for (const field of fields) {\n if (!available.includes(field)) {\n console.error(`kanbo: unknown field \"${field}\"; available: ${available.join(', ')}`)\n }\n }\n}\n\n/**\n * What a caller could have asked `--json` for. A result that names its fields\n * up front is trusted, because a list of them can be empty and still have\n * something to say; otherwise the first row of a list, or the record itself,\n * says what it carries.\n */\nfunction declaredFields(result: CliResult): string[] {\n if (result.fields) {\n return [...result.fields]\n }\n const sample = Array.isArray(result.value) ? result.value[0] : result.value\n return isRecord(sample) ? Object.keys(sample) : []\n}\n\n/**\n * The same value with only the named fields kept. A list keeps them on every\n * item; a single record keeps them on itself. A field the value does not carry\n * is simply absent, rather than printed as null — a caller asking for a field\n * that is not there should see that it is not there.\n */\nfunction selectFields(value: unknown, fields: string[]): unknown {\n if (Array.isArray(value)) {\n return value.map(item => selectFields(item, fields))\n }\n if (!isRecord(value)) {\n return value\n }\n return Object.fromEntries(fields\n .filter(field => Object.hasOwn(value, field))\n .map(field => [field, value[field]]))\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { hostname, userInfo } from 'node:os'\n\nimport type { BoardActor } from '../ops/types'\nimport { CliError, EXIT_HUMAN_ONLY } from './output'\n\n/**\n * Who the board is being written by, as far as a shell can tell.\n *\n * The HTTP route knows its actor from the session it was called with. A\n * terminal has no session, so the tool says the honest thing: this is something\n * outside any app, run by whoever owns this shell, on this machine. It is never\n * an argument — a caller that could name its own kind could name `user`, and\n * the one rule the board has about people would be worth nothing.\n */\n\n/** What a person is told when an agent's shell tries to answer for them. */\nexport const APPROVAL_IS_HUMAN_MESSAGE\n = 'Approval is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to approve it on the board or run \"kanbo approve\" in their own terminal.'\n\n/** The same, for sending a card back. */\nexport const RETURN_IS_HUMAN_MESSAGE\n = 'Return is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to return it on the board or run \"kanbo return\" in their own terminal.'\n\n/** The same, for closing a sprint (ruling 5x-4). */\nexport const SPRINT_CLOSE_IS_HUMAN_MESSAGE\n = 'Closing a sprint is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to close the sprint on the board or run \"kanbo sprint close\" in their own terminal.'\n\n/**\n * The same, for setting what a column asks of a card (ruling 6-1): an agent that\n * could clear a column's rules could walk any card past them.\n */\nexport const COLUMN_RULES_IS_HUMAN_MESSAGE\n = 'Column entry rules are for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to set them on the board or run \"kanbo columns rules\" in their own terminal.'\n\n/**\n * The same, for taking back the log a run names: an agent that could clear or\n * swap it could point a card's history at any log it liked.\n */\nexport const RUN_SESSION_REF_IS_HUMAN_MESSAGE\n = 'Changing or clearing the log a run names is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to do it on the board or run \"kanbo run clear-session\" or \"kanbo run attach-session --replace\" in their own terminal.'\n\n/** The commands only a person may run, as typed after `kanbo` — the capabilities manifest reads this rather than naming them again. */\nexport const HUMAN_ONLY_ACTIONS = ['approve', 'return', 'sprint close', 'columns rules', 'run clear-session'] as const\n\n/** What only a person may do: the commands above, and one option of a command anyone may run. */\ntype HumanOnlyAction = typeof HUMAN_ONLY_ACTIONS[number] | 'run attach-session --replace'\n\nconst HUMAN_ONLY_MESSAGES: Record<HumanOnlyAction, string> = {\n 'approve': APPROVAL_IS_HUMAN_MESSAGE,\n 'return': RETURN_IS_HUMAN_MESSAGE,\n 'sprint close': SPRINT_CLOSE_IS_HUMAN_MESSAGE,\n 'columns rules': COLUMN_RULES_IS_HUMAN_MESSAGE,\n 'run clear-session': RUN_SESSION_REF_IS_HUMAN_MESSAGE,\n 'run attach-session --replace': RUN_SESSION_REF_IS_HUMAN_MESSAGE,\n}\n\n/** The actor every board write from this tool is filed under. */\nexport function createCliActor(): BoardActor {\n return {\n kind: 'external',\n id: process.env.KANBO_ACTOR_ID?.trim() || userInfo().username,\n name: hostname(),\n }\n}\n\n/**\n * The person on the other end of this shell — or the refusal that says there\n * is none.\n *\n * Approving, returning, closing a sprint, setting a column's entry rules and\n * taking back the log a run names are the things only a person may do, and a shell\n * that says it belongs to an agent is not one. There is no flag to get past this: an\n * agent that can approve its own work makes every card waiting for a person a\n * lie, and an escape hatch would be used by the first agent that read about it.\n */\nexport function requireHumanActor(action: HumanOnlyAction): BoardActor {\n if (isAgentShell()) {\n throw new CliError(EXIT_HUMAN_ONLY, HUMAN_ONLY_MESSAGES[action])\n }\n return createPersonActor()\n}\n\n/**\n * The actor a card placement — `card create`, `card update`, `card move` —\n * runs as. It is filed `external` like every other write from this tool\n * (ruling 6-6). In a person's own terminal it also carries `placesForPerson`,\n * so a column's entry rules warn that person instead of holding the card back\n * (ruling 6-5), exactly as they do on the board page. In an agent's shell it\n * does not, and the column refuses the card.\n */\nexport function createPlacementActor(): BoardActor {\n return isAgentShell() ? createCliActor() : { ...createCliActor(), placesForPerson: true }\n}\n\n/**\n * The person who owns this shell, as the board files a person. Only for a\n * caller that has already asked `isAgentShell` — `requireHumanActor` for a\n * command, `kanbo serve` once when it starts.\n */\nexport function createPersonActor(): BoardActor {\n return { kind: 'user', id: userInfo().username }\n}\n\n/**\n * Does this shell say it belongs to an agent?\n *\n * `KANBO_ACTOR_KIND=agent` is the one way to say so. An app that starts agents\n * sets it in every shell and process it starts for one; a person can set it\n * for an agent they run themselves. Two things ask. `approve`, `return`, `sprint close` and `columns rules` refuse such a shell outright, and the\n * board resolution hands it the agent role's own connection string when the\n * project has one — the same question, answered once here.\n */\nexport function isAgentShell(): boolean {\n return process.env.KANBO_ACTOR_KIND?.trim() === 'agent'\n}\n","import type { BoardStore } from '../board-store'\n\n/**\n * What numbering needs to know about a workspace: enough to build the key stem,\n * and nothing else. A host app's workspace row satisfies it; so does a workspace\n * a command-line tool resolved from a path, without the board reading anybody's\n * tables to find it.\n *\n * Written out rather than `Pick`ed from a row type, because the board has no row\n * to pick from: a workspace belongs to whoever tracks it, and this package declares only what it\n * needs of one.\n */\nexport interface BoardWorkspaceIdentity {\n id: string\n identifier: string\n name: string\n}\n\n/** The three-letter stem every issue key in the workspace starts with. */\nexport function readIssuePrefix(workspace: BoardWorkspaceIdentity): string {\n const raw = workspace.identifier || workspace.name || workspace.id\n const normalized = raw.toUpperCase().replace(/[^A-Z0-9]/g, '')\n return (normalized.slice(0, 3) || 'ISS').padEnd(3, 'X')\n}\n\nexport function formatIssueId(prefix: string, number: number): string {\n return `${prefix}-${number.toString().padStart(3, '0')}`\n}\n\n/**\n * The key and number the next issue of this workspace gets. The loop skips keys\n * an earlier prefix already took, so a renamed workspace never collides with its\n * own history.\n */\nexport async function nextIssueIdentity(workspace: BoardWorkspaceIdentity, store: BoardStore): Promise<{ id: string, number: number }> {\n const prefix = readIssuePrefix(workspace)\n let number = await store.issues.maxNumber(workspace.id) + 1\n\n while (true) {\n const id = formatIssueId(prefix, number)\n const existing = await store.issues.findById(id)\n if (!existing) {\n return { id, number }\n }\n number += 1\n }\n}\n\n// `tx` is the store the caller is working through: inside `store.transaction`\n// that is the transactional view, so the read sees what the transaction has\n// already written.\nexport async function nextIssueNumber(workspaceId: string, tx: BoardStore): Promise<number> {\n return await tx.issues.maxNumber(workspaceId) + 1\n}\n\nexport async function nextIssueOrder(workspaceId: string, tx: BoardStore): Promise<number> {\n return await tx.issues.maxOrder(workspaceId) + 1024\n}\n","import { z } from 'zod'\n\nimport type { Issue, IssueComment, IssueFieldChange } from '../sqlite/schema'\n\n/** An issue row with its `labels` column decoded from JSON. */\nexport type IssueView = Omit<Issue, 'labels'> & { labels: string[] }\n\n/**\n * Who a board change is attributed to. The `issue_field_changes.actor_kind`\n * column owns the union; comment authors carry the same kinds plus the\n * `system.*` markers that `normalizeCommentAuthorKind` folds away.\n */\nexport type IssueActorKind = IssueFieldChange['actorKind']\n\nconst IssueLabelsJsonSchema = z.string()\n .transform(raw => JSON.parse(raw))\n .pipe(z.array(z.string()))\n\nexport function toIssueView(issue: Issue): IssueView {\n return {\n ...issue,\n labels: IssueLabelsJsonSchema.parse(issue.labels),\n }\n}\n\n/**\n * Fold a comment's author kind onto the actor kinds a reader renders: only a\n * person, an agent and a provider target speak for themselves, so every\n * `system.*` marker — whatever it marks — is the board speaking as `system`.\n */\nexport function normalizeCommentAuthorKind(authorKind: IssueComment['authorKind']): IssueActorKind {\n if (authorKind === 'user' || authorKind === 'agent' || authorKind === 'provider-target') {\n return authorKind\n }\n return 'system'\n}\n\n/** Who a row is filed under, as the row stores it — what a host resolves into a name to show. */\nexport interface IssueActorReference {\n kind: IssueActorKind\n id: string | null\n sourceChatSessionId: string | null\n}\n\n/** What a stored row says about who wrote it: the name its writer gave, and the chat session it came from. */\nexport interface IssueWriterProvenance {\n authorId: IssueComment['authorId']\n sourceChatSessionId: IssueComment['sourceChatSessionId']\n}\n\n/**\n * Was this row written by an outside writer?\n *\n * An outside writer is stored as `system`, the same kind the board writes\n * under, so the kind alone cannot say. What a row carries can: a writer from\n * outside — the `kanbo` command line, an agent someone ran themselves — gives\n * a name and speaks from no chat session, while the board writing on its\n * own behalf gives no name (`BOARD_ACTOR`) and an app's agent runtime filed under\n * `system` always carries the session it was called in. The caller establishes\n * the `system` kind first; this answers the rest of the question, and says in\n * its type that such a writer has a name to show.\n */\nexport function isOutsideWriter(provenance: IssueWriterProvenance): provenance is IssueWriterProvenance & { authorId: string } {\n return provenance.authorId !== null && provenance.sourceChatSessionId === null\n}\n","import type { IssueComment } from '../sqlite/schema'\nimport type {\n IssueActivityCommentView,\n IssueActivityItemView,\n IssueActivityLookup,\n IssueActivityValueToken,\n IssueActivityValueView,\n} from './activity-types'\nimport type { IssueActorReference, IssueWriterProvenance } from './issue-view'\nimport { isOutsideWriter, normalizeCommentAuthorKind } from './issue-view'\n\nexport function activityKindOrder(kind: IssueActivityItemView['kind']): number {\n if (kind === 'created') {\n return 0\n }\n if (kind === 'field-change') {\n return 1\n }\n return 2\n}\n\n/**\n * The badge a system comment renders under. Delegation owns two of its own; the\n * rest of the `system.*` markers read as plain system comments until they earn\n * a badge.\n *\n * A plain `system` row is where provenance decides: a writer from outside —\n * the command line, an agent someone ran themselves — is stored as `system` too, and what it wrote is an ordinary comment\n * rather than a line about the board — `isOutsideWriter` is that question.\n */\nexport function readSystemCommentKind(\n authorKind: IssueComment['authorKind'],\n provenance: IssueWriterProvenance,\n): IssueActivityCommentView['systemKind'] {\n if (authorKind === 'system.delegated') {\n return 'delegated'\n }\n if (authorKind === 'system.undelegated') {\n return 'undelegated'\n }\n if (authorKind === 'user' || authorKind === 'agent' || authorKind === 'provider-target') {\n return null\n }\n if (authorKind === 'system' && isOutsideWriter(provenance)) {\n return null\n }\n return 'system'\n}\n\n/**\n * Who a comment is shown under. A `system.*` marker is the board's own line\n * about what happened — a run launched, a person's decision — so the id it\n * carries names whoever caused the line, never its author. Only a plain\n * `system` row can name a writer.\n */\nexport function commentAuthorReference(comment: IssueComment): IssueActorReference {\n const kind = normalizeCommentAuthorKind(comment.authorKind)\n const isBoardMarker = kind === 'system' && comment.authorKind !== 'system'\n return {\n kind,\n id: isBoardMarker ? null : comment.authorId,\n sourceChatSessionId: comment.sourceChatSessionId,\n }\n}\n\nexport function formatActivityIssueReference(value: string | null, lookup: IssueActivityLookup): IssueActivityValueView {\n if (!value) {\n return activityToken('no-parent')\n }\n const issue = lookup.issueById.get(value)\n if (!issue) {\n return activityToken('unknown-issue')\n }\n return { kind: 'text', text: `${issue.id} ${issue.title}` }\n}\n\nexport function formatPlainActivityValue(value: string | null): IssueActivityValueView {\n if (isEmptyActivityValue(value)) {\n return activityToken('empty')\n }\n\n const trimmed = value.trim()\n if (trimmed.length > 96) {\n return activityToken('changed')\n }\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) {\n return activityToken('changed')\n }\n if (/^(external_provider_target|provider_target|agent_session)_/.test(trimmed)) {\n return activityToken('changed')\n }\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return activityToken('changed')\n }\n return { kind: 'text', text: trimmed }\n}\n\nexport function parseActivityStringArray(value: string | null): string[] | null {\n if (value == null || value === '') {\n return []\n }\n\n try {\n const parsed = JSON.parse(value) as unknown\n if (Array.isArray(parsed) && parsed.every(item => typeof item === 'string')) {\n return parsed\n }\n }\n catch {\n return null\n }\n\n return null\n}\n\nexport function isEmptyActivityValue(value: string | null): value is null | '' | '[]' {\n return value == null || value === '' || value === '[]'\n}\n\nexport function activityText(text: string | null): IssueActivityValueView | null {\n const trimmed = text?.trim()\n if (!trimmed) {\n return null\n }\n return { kind: 'text', text: trimmed }\n}\n\nexport function activityToken(token: IssueActivityValueToken): IssueActivityValueView {\n return { kind: 'token', token }\n}\n","/**\n * Unix seconds — the unit every board timestamp column stores.\n *\n * The package carries its own clock, so it depends on nothing of its host's.\n */\nexport function currentUnixSeconds(): number {\n return Math.floor(Date.now() / 1000)\n}\n\nconst DAY_SECONDS = 86_400\n\n/**\n * A board date is a calendar day (ruling 5x-6): a start is kept as the first\n * second of its UTC day, a due date as the last, so a sprint runs through its\n * due day wherever the person reading it is.\n */\nexport function utcDayStart(seconds: number): number {\n return Math.floor(seconds / DAY_SECONDS) * DAY_SECONDS\n}\n\n/** The last second of the UTC day `seconds` falls on — see `utcDayStart`. */\nexport function utcDayEnd(seconds: number): number {\n return utcDayStart(seconds) + DAY_SECONDS - 1\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueComment, IssueFieldChange, IssueRun } from '../sqlite/schema'\n\n/**\n * Who is acting on the board.\n *\n * Four kinds reach it: a person, an agent an app launched and knows, a provider\n * target standing in for one, and anything else — a command-line tool, a\n * script, an agent someone ran themselves. `id` is whatever the host calls that\n * actor, or `null` when it has no name to give; `chatSessionId` is the app's\n * chat session the action came from, and means nothing to a board written from\n * outside.\n */\nexport interface BoardActor {\n kind: 'user' | 'agent' | 'provider-target' | 'external'\n id: string | null\n name?: string\n chatSessionId?: string | null\n /**\n * A person's own terminal placing a card (ruling 6-6): the write is still\n * filed under `kind`/`id`, but a column's entry rules warn instead of\n * refusing, as they do for a person on the board page. Only the entry-rule check reads\n * it; it is never stored.\n */\n placesForPerson?: boolean\n}\n\n/**\n * The board writing on its own behalf: the lines and comments the board produces\n * from a run's lifecycle, which no person and no agent typed.\n *\n * This matters beyond provenance. Whether a failed run ends with \"no report\"\n * depends on the agent having written a status line since it started, so a line\n * the board wrote itself must never be filed under the agent that was running.\n */\nexport const BOARD_ACTOR: BoardActor = { kind: 'external', id: null }\n\n/**\n * How an actor is stored on a comment or a field change. The board's four kinds\n * do not all exist in those columns: an outside writer is the system speaking,\n * while a provider target keeps its own kind, because an app's comment view\n * resolves it to the provider that wrote the comment.\n */\nexport function toStoredActorKind(actor: BoardActor): IssueFieldChange['actorKind'] {\n return actor.kind === 'external' ? 'system' : actor.kind\n}\n\n/**\n * How an actor is stored on a run. A run is launched by a person, by an agent,\n * or from outside; a provider target launches as the agent it stands in\n * for, because the run registry has no kind of its own for it.\n */\nexport function toRunLaunchedByKind(actor: BoardActor): IssueRun['launchedByKind'] {\n switch (actor.kind) {\n case 'user':\n return 'user'\n case 'agent':\n case 'provider-target':\n return 'agent'\n case 'external':\n return 'external'\n }\n}\n\n/** The card, or the error that says the board has no such card. */\nexport async function requireCard(store: BoardStore, issueId: string): Promise<Issue> {\n const card = await store.issues.findById(issueId)\n if (!card) {\n throw new BoardError('issue_not_found', { issueId })\n }\n return card\n}\n\n/**\n * Write what the card is doing right now, and record who said so.\n *\n * The column carries the current line; the field change carries its history,\n * and is the only place the actor and the time behind a line are kept — which is\n * what lets a reader tell a line the agent wrote from one the board wrote for\n * it. A line that says exactly what the card already says is not a change, and\n * writes nothing at all.\n */\nexport async function writeStatusLine(\n store: BoardStore,\n card: Issue,\n statusLine: string | null,\n actor: BoardActor,\n): Promise<Issue> {\n if (card.statusLine === statusLine) {\n return card\n }\n\n const now = currentUnixSeconds()\n await store.issues.update(card.id, { statusLine, updatedAt: now })\n await store.fieldChanges.create({\n id: randomUUID(),\n issueId: card.id,\n field: 'statusLine',\n fromValue: card.statusLine,\n toValue: statusLine,\n actorKind: toStoredActorKind(actor),\n actorId: actor.id ?? null,\n sourceChatSessionId: actor.chatSessionId ?? null,\n createdAt: now,\n })\n return { ...card, statusLine, updatedAt: now }\n}\n\n/** A comment as the board writes it, before the row exists. */\nexport interface BoardCommentInput {\n issueId: string\n content: string\n authorKind: IssueComment['authorKind']\n authorId?: string | null\n sourceChatSessionId?: string | null\n agentActivityId?: string | null\n /**\n * The key that makes the comment a fact rather than a message: writing it\n * again with the same key leaves the first one standing.\n */\n dedupeKey?: string | null\n}\n\nexport async function insertComment(store: BoardStore, input: BoardCommentInput): Promise<IssueComment> {\n const values = {\n id: randomUUID(),\n issueId: input.issueId,\n content: input.content,\n authorKind: input.authorKind,\n authorId: input.authorId ?? null,\n sourceChatSessionId: input.sourceChatSessionId ?? null,\n agentActivityId: input.agentActivityId ?? null,\n createdAt: currentUnixSeconds(),\n }\n\n if (input.dedupeKey == null) {\n return await store.comments.create(values)\n }\n return await store.comments.createOnce({ ...values, dedupeKey: input.dedupeKey })\n}\n","import type { BoardStore } from '../board-store'\nimport type {\n IssueActivityField,\n IssueActivityFieldChangeView,\n IssueActivityItemView,\n IssueActivityLookup,\n IssueActivityValueToken,\n IssueActivityValueView,\n IssueCommentAuthorView,\n} from '../domain/activity-types'\nimport {\n activityKindOrder,\n activityText,\n activityToken,\n commentAuthorReference,\n formatActivityIssueReference,\n formatPlainActivityValue,\n isEmptyActivityValue,\n parseActivityStringArray,\n readSystemCommentKind,\n} from '../domain/issue-activity'\nimport type { IssueActorReference } from '../domain/issue-view'\nimport type { IssueComment, IssueFieldChange } from '../sqlite/schema'\nimport { requireCard } from './types'\n\n/**\n * What the host knows and the board does not: the names of its workspaces, who\n * an actor is, and what an agent is called. A board holds ids for all three and\n * nothing more, so an Activity feed is the board's rows read through these.\n */\nexport interface IssueActivityResolver {\n workspaceById: IssueActivityLookup['workspaceById']\n /** Who a reader sees beside a row. */\n resolveActor: (actor: IssueActorReference) => Promise<IssueCommentAuthorView>\n /** The agent's name, or `null` when the host has no such agent. */\n resolveAgentName: (agentId: string) => Promise<string | null>\n}\n\n/**\n * Field changes the feed keeps out of sight. `executionMode` is recorded and\n * readable through the field-change history — where a card runs is a fact about\n * the work — but the card itself already says it, so the feed does not repeat\n * it. `waitingFor` is listed for the same reason and is not recorded at all:\n * whose turn it is shows in the status line the card is handed over with, and in\n * the approval and return comments around it.\n */\nconst HIDDEN_ACTIVITY_FIELDS = new Set([\n 'assigneeKind',\n 'contextRefs',\n 'delegateAgentId',\n 'delegateProviderTargetId',\n 'executionMode',\n 'order',\n 'waitingFor',\n])\n\nconst ACTIVITY_FIELD_BY_RAW_FIELD: Record<string, IssueActivityField> = {\n assigneeId: 'assignee',\n description: 'description',\n dueDate: 'due-date',\n labels: 'labels',\n milestoneId: 'milestone',\n parentIssueId: 'parent',\n priority: 'priority',\n statusId: 'status',\n statusLine: 'status-line',\n title: 'title',\n workspaceId: 'workspace',\n}\n\nconst PRIORITY_ACTIVITY_VALUE_TOKEN: Record<string, IssueActivityValueToken> = {\n none: 'priority-none',\n low: 'priority-low',\n medium: 'priority-medium',\n high: 'priority-high',\n urgent: 'priority-urgent',\n}\n\n/** The card's field history, oldest first — every tracked change, hidden ones included. */\nexport async function listFieldChanges(store: BoardStore, issueId: string): Promise<IssueFieldChange[]> {\n await requireCard(store, issueId)\n return await store.fieldChanges.listByIssue(issueId)\n}\n\n/**\n * The card's Activity feed: its creation, its visible field changes and its\n * comments, oldest first, with a creation before a change and a change before a\n * comment written in the same second.\n */\nexport async function listActivity(\n store: BoardStore,\n issueId: string,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityItemView[]> {\n const issue = await requireCard(store, issueId)\n const lookup: IssueActivityLookup = {\n issue,\n issueById: new Map((await store.issues.listAll()).map(row => [row.id, row])),\n milestoneById: new Map((await store.milestones.listAll()).map(row => [row.id, row])),\n statusById: new Map((await store.statuses.listAll()).map(row => [row.id, row])),\n workspaceById: resolver.workspaceById,\n }\n const commentItems = await Promise.all(\n (await store.comments.listByIssue(issueId)).map(async comment => await toCommentActivityItem(comment, resolver)),\n )\n const fieldChangeItems = (await Promise.all(\n (await store.fieldChanges.listByIssue(issueId)).map(async change => await toFieldChangeActivityItem(change, lookup, resolver)),\n ))\n .filter((item): item is IssueActivityItemView => item !== null)\n const createdItem: IssueActivityItemView = {\n id: `${issue.id}:created`,\n issueId: issue.id,\n kind: 'created',\n actor: await resolver.resolveActor({\n kind: issue.createdByKind,\n id: issue.createdById,\n sourceChatSessionId: issue.sourceChatSessionId,\n }),\n comment: null,\n fieldChange: null,\n sourceChatSessionId: issue.sourceChatSessionId,\n createdAt: issue.createdAt,\n }\n\n return [\n createdItem,\n ...fieldChangeItems,\n ...commentItems,\n ].toSorted((left, right) => left.createdAt - right.createdAt || activityKindOrder(left.kind) - activityKindOrder(right.kind))\n}\n\nasync function toCommentActivityItem(comment: IssueComment, resolver: IssueActivityResolver): Promise<IssueActivityItemView> {\n return {\n id: comment.id,\n issueId: comment.issueId,\n kind: 'comment',\n actor: await resolver.resolveActor(commentAuthorReference(comment)),\n comment: {\n content: comment.content,\n systemKind: readSystemCommentKind(comment.authorKind, comment),\n },\n fieldChange: null,\n sourceChatSessionId: comment.sourceChatSessionId,\n createdAt: comment.createdAt,\n }\n}\n\nasync function toFieldChangeActivityItem(\n change: IssueFieldChange,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityItemView | null> {\n if (HIDDEN_ACTIVITY_FIELDS.has(change.field)) {\n return null\n }\n\n const field = ACTIVITY_FIELD_BY_RAW_FIELD[change.field] ?? 'metadata'\n return {\n id: change.id,\n issueId: change.issueId,\n kind: 'field-change',\n actor: await resolver.resolveActor({\n kind: change.actorKind,\n id: change.actorId,\n sourceChatSessionId: change.sourceChatSessionId,\n }),\n comment: null,\n fieldChange: await formatActivityFieldChange(change, field, lookup, resolver),\n sourceChatSessionId: change.sourceChatSessionId,\n createdAt: change.createdAt,\n }\n}\n\nasync function formatActivityFieldChange(\n change: IssueFieldChange,\n field: IssueActivityField,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityFieldChangeView> {\n if (field === 'description') {\n if (isEmptyActivityValue(change.fromValue) && !isEmptyActivityValue(change.toValue)) {\n return { action: 'added-description', field, fromValue: null, toValue: null }\n }\n if (!isEmptyActivityValue(change.fromValue) && isEmptyActivityValue(change.toValue)) {\n return { action: 'cleared-description', field, fromValue: null, toValue: null }\n }\n return { action: 'updated-description', field, fromValue: null, toValue: null }\n }\n\n return {\n action: field === 'title' ? 'renamed-issue' : 'changed-field',\n field,\n fromValue: await formatActivityFieldValue(field, change.fromValue, lookup, resolver),\n toValue: await formatActivityFieldValue(field, change.toValue, lookup, resolver),\n }\n}\n\nasync function formatActivityFieldValue(\n field: IssueActivityField,\n value: string | null,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityValueView> {\n switch (field) {\n case 'assignee':\n if (isEmptyActivityValue(value)) {\n return activityToken('unassigned')\n }\n if (value === '__self__') {\n return activityToken('current-user')\n }\n return activityText(await resolver.resolveAgentName(value)) ?? activityToken('unknown-user')\n\n case 'due-date': {\n if (isEmptyActivityValue(value)) {\n return activityToken('no-due-date')\n }\n const timestamp = Number(value)\n if (!Number.isFinite(timestamp)) {\n return activityToken('changed')\n }\n return { kind: 'date', timestamp }\n }\n\n case 'labels': {\n const labels = parseActivityStringArray(value)\n if (!labels || labels.length === 0) {\n return activityToken('no-labels')\n }\n return { kind: 'text', text: labels.join(', ') }\n }\n\n case 'milestone':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-milestone')\n }\n return activityText(lookup.milestoneById.get(value)?.title ?? null) ?? activityToken('unknown-milestone')\n\n case 'parent':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-parent')\n }\n return formatActivityIssueReference(value, lookup)\n\n case 'priority':\n return activityToken(PRIORITY_ACTIVITY_VALUE_TOKEN[value ?? ''] ?? 'priority-none')\n\n case 'status':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-status')\n }\n return activityText(lookup.statusById.get(value)?.name ?? null) ?? activityToken('unknown-status')\n\n case 'title':\n return formatPlainActivityValue(value)\n\n case 'workspace':\n if (isEmptyActivityValue(value)) {\n return activityToken('empty')\n }\n return activityText(lookup.workspaceById.get(value)?.name ?? null) ?? formatPlainActivityValue(value)\n\n default:\n return formatPlainActivityValue(value)\n }\n}\n","import type { BoardStore } from '../board-store'\nimport type { IssueComment } from '../sqlite/schema'\nimport { requireCard } from './types'\n\n/** Where a card stands with the person who has to approve it. */\nexport interface BoardApproval {\n state: 'none' | 'pending' | 'approved' | 'returned'\n /** When the person decided, in unix seconds; `null` while nobody has. */\n decidedAt: number | null\n /** What the person wrote alongside the decision, if anything. */\n comment: string | null\n}\n\n/** The first line of the comment an approval writes; anything after it is the person's own. */\nexport const APPROVED_COMMENT = 'Approved'\n\n/**\n * Where the card stands with the person who has to approve it: waiting for one,\n * or carrying the last decision a person made — which is what a waiting session\n * compares against its own `since` to learn that the answer has arrived.\n */\nexport async function readApproval(store: BoardStore, issueId: string): Promise<BoardApproval> {\n const card = await requireCard(store, issueId)\n if (card.waitingFor === 'human') {\n return { state: 'pending', decidedAt: null, comment: null }\n }\n\n const decision = (await store.comments.listByIssue(issueId))\n .filter(comment => comment.authorKind === 'system.approved' || comment.authorKind === 'system.returned')\n .at(-1)\n if (!decision) {\n return { state: 'none', decidedAt: null, comment: null }\n }\n\n return {\n state: decision.authorKind === 'system.approved' ? 'approved' : 'returned',\n decidedAt: decision.createdAt,\n comment: humanComment(decision),\n }\n}\n\n/** What the person wrote beside a decision, with the board's own wording taken off. */\nfunction humanComment(decision: IssueComment): string | null {\n if (decision.authorKind !== 'system.approved') {\n return decision.content.trim() || null\n }\n const [first, ...rest] = decision.content.split('\\n')\n const written = (first === APPROVED_COMMENT ? rest.join('\\n') : decision.content).trim()\n return written || null\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { Issue, issueFieldChanges, issues } from '../sqlite/schema'\nimport type { IssueActorKind } from './issue-view'\n\n/**\n * The issue columns whose edits are written to the audit trail.\n *\n * `statusLine` is absent on purpose: `writeStatusLine` records every line it\n * writes itself, and no patch ever carries the column. `waitingFor` is absent\n * too — whose turn it is shows in the card's own line and in the\n * `system.approved` / `system.returned` comments around it, so a row saying the\n * same thing a third time would only crowd the history.\n */\nexport const TRACKED_FIELDS = [\n 'workspaceId',\n 'title',\n 'description',\n 'priority',\n 'labels',\n 'milestoneId',\n 'parentIssueId',\n 'statusId',\n 'assigneeKind',\n 'assigneeId',\n 'dueDate',\n 'delegateAgentId',\n 'delegateProviderTargetId',\n 'contextRefs',\n 'executionMode',\n 'order',\n] as const\n\n/** Who an issue edit is attributed to, with the chat session it came from. */\nexport interface IssueMutationActor {\n kind: IssueActorKind\n id: string | null\n sourceChatSessionId?: string | null\n}\n\n/**\n * The audit rows one patch produces: a row per tracked field whose stringified\n * value actually changed. Nothing is written here — the caller inserts what\n * comes back, so the diff stays a pure function of the row, the patch, the\n * actor and the clock.\n */\nexport function diffFieldChanges(\n before: Issue,\n updates: Partial<typeof issues.$inferInsert>,\n actor: IssueMutationActor,\n now: number,\n): Array<typeof issueFieldChanges.$inferInsert> {\n const rows: Array<typeof issueFieldChanges.$inferInsert> = []\n for (const field of TRACKED_FIELDS) {\n if (!(field in updates)) {\n continue\n }\n const dbField = field === 'labels' ? 'labels' : field\n const rawBefore = before[dbField as keyof Issue]\n const rawAfter = updates[field]\n const fromValue = rawBefore == null ? null : String(rawBefore)\n const toValue = rawAfter == null ? null : String(rawAfter)\n if (fromValue === toValue) {\n continue\n }\n rows.push({\n id: randomUUID(),\n issueId: before.id,\n field,\n fromValue,\n toValue,\n actorKind: actor.kind,\n actorId: actor.id ?? null,\n sourceChatSessionId: actor.sourceChatSessionId ?? null,\n createdAt: now,\n })\n }\n return rows\n}\n","/**\n * The columns a workspace gets the first time someone opens its board, in board\n * order. A workspace that already holds statuses is never re-seeded.\n */\nexport const DEFAULT_STATUSES = [\n { name: 'Backlog', description: 'Idea captured, not ready to work on', color: '#6b7280', category: 'backlog' as const },\n { name: 'To Do', description: 'Spelled out, ready to launch', color: '#9ca3af', category: 'unstarted' as const },\n { name: 'In Progress', description: 'The orchestrator and its agents are working', color: '#f59e0b', category: 'started' as const },\n { name: 'In Review', description: 'Work done; being checked or waiting for you', color: '#8b5cf6', category: 'started' as const },\n { name: 'Done', description: 'Accepted', color: '#22c55e', category: 'completed' as const },\n { name: 'Canceled', description: 'Canceled', color: '#6b7280', category: 'canceled' as const },\n] as const\n\n/**\n * The slug two status names have in common when they mean the same column:\n * case and the run of spaces or dashes between words are ignored, so `In\n * Progress`, `in-progress` and `IN_PROGRESS` all resolve to one status.\n */\nexport function normalizeStatusName(value: string): string {\n return value.trim().toLowerCase().replace(/[\\s-]+/g, '_')\n}\n","import type { BoardStore } from '../board-store'\nimport { currentUnixSeconds } from '../domain/time'\n\n/** The `kanban_meta` row every board write bumps. */\nconst BOARD_CHANGE_KEY = 'board'\n\n/** The row groups of the store — everything it offers except the transaction itself. */\ntype BoardStoreGroup = Exclude<keyof BoardStore, 'transaction'>\n\n/**\n * Every store method that changes a row, per group.\n *\n * The list is written out rather than derived from a naming rule so that a\n * method added to a group has to be classified by hand: a read that were treated\n * as a write would only bump the counter too often, but a write mistaken for a\n * read would leave readers on a board that silently changed. `satisfies` ties\n * each name to its group, so renaming a store method breaks the build here.\n */\nconst MUTATING_METHODS = {\n statuses: ['create', 'update', 'delete'],\n milestones: ['create', 'update', 'delete'],\n issues: ['create', 'update', 'updateMany', 'clearMilestoneReferences', 'clearParentReferences', 'clearDelegateReferences', 'delete'],\n comments: ['create', 'createOnce', 'delete'],\n runs: ['create', 'update'],\n relations: ['create', 'delete', 'deleteByIssue'],\n contextRefs: ['replace'],\n fieldChanges: ['create'],\n pullRequests: ['link', 'unlink'],\n meta: ['bumpRevision'],\n} satisfies { [TGroup in BoardStoreGroup]: Array<keyof BoardStore[TGroup]> }\n\n/**\n * One board write in progress: the store to work through, and — behind it — the\n * transaction and the change flag the whole write shares.\n *\n * An operation that takes a scope joins the write it belongs to instead of\n * opening one of its own, which is how a card operation that seeds columns, a\n * status line and a comment still counts as a single change.\n */\nexport interface BoardWriteScope<TStore extends BoardStore = BoardStore> {\n /** The transactional store, wrapped so every write through it is noticed. */\n readonly tx: TStore\n}\n\ninterface ChangeTracker {\n changed: boolean\n}\n\n/**\n * The same store, with every mutating method wrapped so that calling it marks\n * the write as having changed something.\n *\n * Groups the board does not own — the `lookups` a host composes beside it — are\n * carried over untouched, which is why the wrapper is built from the store's own\n * keys rather than from a fixed list. The cast restores the host's store type:\n * every property is either the original or a wrapper with the same signature.\n */\nfunction trackChanges<TStore extends BoardStore>(store: TStore, tracker: ChangeTracker): TStore {\n const wrapped = { ...store } as Record<string, unknown>\n\n for (const [group, methods] of Object.entries(MUTATING_METHODS)) {\n const source = store[group as BoardStoreGroup] as unknown as Record<string, (...args: unknown[]) => unknown>\n const replacement: Record<string, unknown> = { ...source }\n for (const method of methods) {\n replacement[method] = (...args: unknown[]) => {\n tracker.changed = true\n return source[method](...args)\n }\n }\n wrapped[group] = replacement\n }\n\n // A transaction opened through the wrapper keeps the callback inside it, so\n // writes made there are noticed too.\n wrapped.transaction = async (fn: (tx: TStore) => Promise<unknown>, options?: { mode?: 'deferred' | 'immediate' }) =>\n await store.transaction(async () => await fn(wrapped as TStore), options)\n\n return wrapped as TStore\n}\n\n/**\n * Run `fn` as one board write: everything it writes commits together, and the\n * board's change counter goes up by exactly one if it wrote anything at all.\n *\n * The counter is what tells a reader — the board UI polling, a CLI, another\n * agent — that the board is not the board it last read. It is bumped from the\n * outermost scope only, after `fn` returns and only when a mutating store method\n * was actually called, so a read-only pass and a transaction that rolls back\n * both leave it untouched. Pass `scope` to join a write that is already open\n * instead of starting a second one.\n *\n * The transaction takes its write lock up front (`immediate`), so two writers\n * racing for the same board file fail fast rather than half-way through. A write\n * that starts while the host already holds a transaction of its own becomes a\n * savepoint inside it — the board counter then commits, or rolls back, with\n * whatever the host is doing.\n *\n * The caller rule of the underlying transaction applies in full: `fn` must be\n * the only database-touching async chain in flight, and inside it only\n * operations that resolve without I/O may be awaited.\n */\nexport async function runBoardWrite<TStore extends BoardStore, TResult>(\n store: TStore,\n fn: (scope: BoardWriteScope<TStore>) => Promise<TResult>,\n scope?: BoardWriteScope<TStore>,\n): Promise<TResult> {\n if (scope) {\n return await fn(scope)\n }\n\n const tracker: ChangeTracker = { changed: false }\n return await store.transaction(async (tx) => {\n // A host that composes its own groups beside the board — an app's server\n // and its `lookups` — hands its own store to the callback; only the type of\n // `transaction` on the constraint says `BoardStore`.\n const result = await fn({ tx: trackChanges(tx as TStore, tracker) })\n if (tracker.changed) {\n await tx.meta.bumpRevision(BOARD_CHANGE_KEY, currentUnixSeconds())\n }\n return result\n }, { mode: 'immediate' })\n}\n\n/**\n * How many times the board has changed. A reader that holds a number and reads\n * the same one back is looking at the board it already has.\n */\nexport async function readChangeSeq(store: BoardStore): Promise<number> {\n return (await store.meta.read(BOARD_CHANGE_KEY))?.revision ?? 0\n}\n","import { z } from 'zod'\n\nimport type { IssueStatus } from '../sqlite/schema'\nimport { BoardError } from './errors'\n\n/**\n * What a column may ask of a card before an agent puts the card in it (ruling\n * 6-1). The set is fixed: each rule is one fact the board can check on its own.\n *\n * - `checklist_complete` — the description holds at least one markdown task\n * item, and every one of them is checked.\n * - `pull_request_linked` — the card names at least one pull request.\n * - `ci_green` — the newest CI verdict across the card's linked pull requests\n * is green.\n * - `approved` — a person's last decision on the card was to approve it.\n */\nexport const ENTRY_RULES = ['checklist_complete', 'pull_request_linked', 'ci_green', 'approved'] as const\n\nexport type EntryRule = typeof ENTRY_RULES[number]\n\n/** Why a rule is unmet, as a key a translated surface can name in its own words. */\nexport const UNMET_ENTRY_RULE_REASONS = [\n 'no_checklist',\n 'unchecked',\n 'no_pull_request',\n 'no_ci',\n 'ci_red',\n 'approval_pending',\n 'returned',\n 'not_approved',\n] as const\n\ntype UnmetEntryRuleReason = typeof UNMET_ENTRY_RULE_REASONS[number]\n\n/**\n * One rule a card does not meet yet: `detail` says what is missing in English\n * words the command line and MCP print; `reason` (with `checked` and `total`\n * for `unchecked`) says the same for a surface that translates it.\n */\nexport interface UnmetEntryRule {\n rule: EntryRule\n reason: UnmetEntryRuleReason\n detail: string\n checked?: number\n total?: number\n}\n\n/** What `evaluateEntryRules` needs to know beyond the card's own fields. */\nexport interface EntryRuleFacts {\n /** How many pull requests the card names. */\n pullRequestCount: number\n /** The newest CI verdict across those pull requests, or `null` while none has arrived. */\n latestCi: 'green' | 'red' | null\n /** Where the card stands with the person who approves it (`BoardApproval['state']`). */\n approval: 'none' | 'pending' | 'approved' | 'returned'\n}\n\nconst entryRulesSchema = z.array(z.enum(ENTRY_RULES))\n\n/**\n * The rules a caller asked for, as the column stores them: known rules only,\n * each once, in `ENTRY_RULES` order, and `null` for none — an empty list and no\n * list are the same column. An unknown rule is `board_entry_rule_invalid`,\n * before anything is written.\n */\nexport function serializeEntryRules(rules: readonly string[] | null): string | null {\n const parsed = entryRulesSchema.safeParse(rules ?? [])\n if (!parsed.success) {\n const invalid = (rules ?? []).filter(rule => !(ENTRY_RULES as readonly string[]).includes(rule))\n throw new BoardError('board_entry_rule_invalid', { rules: invalid, allowed: [...ENTRY_RULES] })\n }\n const chosen = ENTRY_RULES.filter(rule => parsed.data.includes(rule))\n return chosen.length > 0 ? JSON.stringify(chosen) : null\n}\n\n/**\n * The rules a column asks for. A rule this build does not know — written by a\n * newer one — is left out rather than failing the read: the column still\n * enforces everything this build can check.\n */\nexport function readEntryRules(column: Pick<IssueStatus, 'entryRules'>): EntryRule[] {\n if (!column.entryRules) {\n return []\n }\n const stored: string[] = JSON.parse(column.entryRules)\n return ENTRY_RULES.filter(rule => stored.includes(rule))\n}\n\n/** How many markdown task items a description holds, and how many are checked. */\nexport interface ChecklistProgress {\n total: number\n checked: number\n}\n\n/** A list item that is a task: `- [ ]`, `* [x]`, `+ [X]`, `1. [ ]`, at any depth. */\nconst TASK_ITEM = /^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+\\[([ x])\\](?:\\s|$)/i\n/** The line that opens or closes a fenced code block: three or more backticks or tildes. */\nconst FENCE = /^\\s*(`{3,}|~{3,})/\n\n/**\n * Count the task items of a markdown description.\n *\n * Every list item that starts with `[ ]` or `[x]` (either case) counts,\n * nested ones included — a sub-task is still a task. Lines inside a fenced code\n * block (```` ``` ```` or `~~~`) do not: a `- [ ]` there is an example someone\n * quoted, not a box anyone can tick. An indented code block cannot be told\n * apart from a nested list without a full markdown parser, so it counts; fence\n * the example to keep it out.\n */\nexport function readChecklist(description: string | null): ChecklistProgress {\n let total = 0\n let checked = 0\n let fence: string | null = null\n for (const line of (description ?? '').split(/\\r?\\n/)) {\n const marker = FENCE.exec(line)?.[1]\n if (fence) {\n if (marker && marker[0] === fence[0] && marker.length >= fence.length) {\n fence = null\n }\n continue\n }\n if (marker) {\n fence = marker\n continue\n }\n const item = TASK_ITEM.exec(line)\n if (item) {\n total++\n if (item[1] !== ' ') {\n checked++\n }\n }\n }\n return { total, checked }\n}\n\n/**\n * The rules a card does not meet, in the order the column lists them (ruling\n * 6-2). Pure: everything beyond the card's own description arrives in `facts`.\n */\nexport function evaluateEntryRules(\n card: { description: string | null },\n rules: readonly EntryRule[],\n facts: EntryRuleFacts,\n): { unmet: UnmetEntryRule[] } {\n const unmet: UnmetEntryRule[] = []\n for (const rule of rules) {\n const missing = unmetReason(rule, card, facts)\n if (missing) {\n unmet.push({ rule, ...missing })\n }\n }\n return { unmet }\n}\n\n/** Why the card fails `rule`, or `null` when it meets it. */\nfunction unmetReason(\n rule: EntryRule,\n card: { description: string | null },\n facts: EntryRuleFacts,\n): Omit<UnmetEntryRule, 'rule'> | null {\n switch (rule) {\n case 'checklist_complete': {\n const { total, checked } = readChecklist(card.description)\n if (total === 0) {\n return { reason: 'no_checklist', detail: 'no checklist in the description' }\n }\n return checked < total ? { reason: 'unchecked', detail: `${total - checked} of ${total} unchecked`, checked, total } : null\n }\n case 'pull_request_linked':\n return facts.pullRequestCount > 0 ? null : { reason: 'no_pull_request', detail: 'no pull request linked' }\n case 'ci_green':\n if (facts.latestCi === 'green') {\n return null\n }\n return facts.latestCi === 'red' ? { reason: 'ci_red', detail: 'CI is red' } : { reason: 'no_ci', detail: 'no CI result yet' }\n case 'approved':\n switch (facts.approval) {\n case 'approved':\n return null\n case 'pending':\n return { reason: 'approval_pending', detail: 'waiting for a person to approve' }\n case 'returned':\n return { reason: 'returned', detail: 'returned by a person, not approved' }\n case 'none':\n return { reason: 'not_approved', detail: 'not approved by a person' }\n }\n }\n}\n\n/** What `ENTRY_RULES` each mean, in the words the prime text and the manifest use. */\nexport const ENTRY_RULE_DESCRIPTIONS: Record<EntryRule, string> = {\n checklist_complete: 'every checkbox (`- [ ]`) in the card description is checked, and there is at least one',\n pull_request_linked: 'at least one pull request is linked to the card',\n ci_green: 'the newest CI result across the card\\'s linked pull requests is green',\n approved: 'a person has approved the card',\n}\n\n/** What `board_column_rules_unmet` carries in its details. */\nexport interface ColumnRefusalDetails {\n /** The card that was held back; `null` for one that was being created. */\n issueId: string | null\n column: string\n statusId: string\n unmet: UnmetEntryRule[]\n}\n\n/**\n * A refusal at a column's door as a person or an agent reads it: the error code\n * first, so a caller matching on it still can, then one line per unmet rule.\n * The command line and the MCP tools say it in exactly these words.\n */\nexport function describeColumnRefusal(details: ColumnRefusalDetails): string {\n const card = details.issueId ? `${details.issueId} cannot enter` : 'A new card cannot be created in'\n return [\n `board_column_rules_unmet: ${card} \"${details.column}\" yet:`,\n ...details.unmet.map(item => `- ${item.rule}: ${item.detail}`),\n ].join('\\n')\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { serializeEntryRules } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { DEFAULT_STATUSES, normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { IssueStatus, issueStatuses } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR } from './types'\n\n/** What a column is made of, beyond the workspace it belongs to and its position. */\nexport interface BoardColumnInput {\n workspaceId: string\n name: string\n description?: string | null\n color?: string | null\n category?: IssueStatus['category']\n /** What a card must satisfy before an agent may put it here — `ENTRY_RULES`, none by default. */\n entryRules?: readonly string[] | null\n}\n\n/** What `updateColumn` may change about a column. */\nexport interface BoardColumnPatch {\n name?: string\n description?: string | null\n color?: string | null\n /** `ENTRY_RULES`; `null` or an empty list asks for nothing. */\n entryRules?: readonly string[] | null\n}\n\n/** The columns of one workspace, in board order. */\nexport async function listColumns(store: BoardStore, workspaceId: string): Promise<IssueStatus[]> {\n return await store.statuses.listByWorkspace(workspaceId)\n}\n\n/**\n * The column the caller named, by id or by any spelling of its name — `In\n * Progress`, `in-progress` and `IN_PROGRESS` all reach the same one. `null` when\n * the workspace holds no such column.\n */\nexport async function findColumn(store: BoardStore, workspaceId: string, nameOrId: string): Promise<IssueStatus | null> {\n const columns = await listColumns(store, workspaceId)\n const byId = columns.find(column => column.id === nameOrId)\n if (byId) {\n return byId\n }\n const slug = normalizeStatusName(nameOrId)\n if (!slug) {\n throw new BoardError('issue_status_name_empty', { workspaceId })\n }\n return columns.find(column => normalizeStatusName(column.name) === slug) ?? null\n}\n\n/** The column the caller named, or the error that says the board has no such column. */\nexport async function requireColumn(store: BoardStore, workspaceId: string, nameOrId: string): Promise<IssueStatus> {\n const column = await findColumn(store, workspaceId, nameOrId)\n if (!column) {\n throw new BoardError('issue_status_not_found', {\n workspaceId,\n statusName: nameOrId,\n normalizedStatusName: normalizeStatusName(nameOrId),\n })\n }\n return column\n}\n\n/**\n * The columns of the workspace, seeding the standard six the first time someone\n * looks. A board that already holds a column of its own is never seeded: those\n * columns are the user's data, and a workspace that was pruned down to one\n * column meant it.\n */\nexport async function ensureDefaultColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus[]> {\n if (await store.statuses.countByWorkspace(workspaceId) > 0) {\n return await listColumns(store, workspaceId)\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n // Another writer may have seeded the same workspace between the count above\n // and this transaction; the unique index on `(workspace_id, name)` would\n // then refuse the insert, so the count is taken again under the write lock.\n if (await tx.statuses.countByWorkspace(workspaceId) > 0) {\n return await listColumns(tx, workspaceId)\n }\n for (const [order, status] of DEFAULT_STATUSES.entries()) {\n await insertColumn(tx, { workspaceId, ...status }, order)\n }\n return await listColumns(tx, workspaceId)\n }, scope)\n}\n\n/**\n * Only a person sets, changes or clears what a column asks of a card: an agent\n * that could clear a column's rules could walk any card past them.\n */\nfunction assertPersonSetsEntryRules(actor: BoardActor, details: Record<string, unknown>): void {\n if (actor.kind !== 'user') {\n throw new BoardError('board_column_rules_requires_user', { ...details, actorKind: actor.kind })\n }\n}\n\nexport async function createColumn<TStore extends BoardStore>(\n store: TStore,\n input: BoardColumnInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n if (serializeEntryRules(input.entryRules ?? null) !== null) {\n assertPersonSetsEntryRules(actor, { workspaceId: input.workspaceId, name: input.name })\n }\n return await runBoardWrite(store, async ({ tx }) => {\n // New columns land at the end of the board.\n const order = await tx.statuses.countByWorkspace(input.workspaceId)\n return await insertColumn(tx, input, order)\n }, scope)\n}\n\nexport async function updateColumn<TStore extends BoardStore>(\n store: TStore,\n statusId: string,\n patch: BoardColumnPatch,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n // Checked before the write starts: an unknown rule, or an actor who is not a\n // person, changes nothing.\n const entryRules = 'entryRules' in patch ? serializeEntryRules(patch.entryRules ?? null) : undefined\n if (entryRules !== undefined) {\n assertPersonSetsEntryRules(actor, { statusId })\n }\n return await runBoardWrite(store, async ({ tx }) => {\n const updates: Partial<typeof issueStatuses.$inferInsert> = {}\n if (patch.name !== undefined) {\n updates.name = patch.name\n }\n if ('description' in patch) {\n updates.description = patch.description ?? null\n }\n if ('color' in patch) {\n updates.color = patch.color ?? null\n }\n if (entryRules !== undefined) {\n updates.entryRules = entryRules\n }\n if (Object.keys(updates).length > 0) {\n await tx.statuses.update(statusId, updates)\n }\n const column = await tx.statuses.findById(statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n return column\n }, scope)\n}\n\n/**\n * Give a column the one line that tells an agent when a card belongs in it. The\n * column is named the way a person would name it — by id, or by any spelling of\n * its name.\n */\nexport async function describeColumn<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n nameOrId: string,\n description: string | null,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n return await runBoardWrite(store, async ({ tx }) => {\n const column = await requireColumn(tx, workspaceId, nameOrId)\n return await updateColumn(tx, column.id, { description }, BOARD_ACTOR, { tx })\n }, scope)\n}\n\n/**\n * Set what a card must satisfy before an agent may put it in the column\n * (ruling 6-1): `ENTRY_RULES`, or `null` / an empty list for nothing. The column\n * is named the way `describeColumn` names it.\n */\nexport async function setColumnEntryRules<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n nameOrId: string,\n entryRules: readonly string[] | null,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n serializeEntryRules(entryRules)\n assertPersonSetsEntryRules(actor, { workspaceId, statusName: nameOrId })\n return await runBoardWrite(store, async ({ tx }) => {\n const column = await requireColumn(tx, workspaceId, nameOrId)\n return await updateColumn(tx, column.id, { entryRules }, actor, { tx })\n }, scope)\n}\n\nexport async function deleteColumn<TStore extends BoardStore>(\n store: TStore,\n statusId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n if (!await tx.statuses.findById(statusId)) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n // Cards pointing at the column keep their place on the board and lose their\n // column, the way the schema's `on delete set null` says they should.\n await tx.statuses.delete(statusId)\n }, scope)\n}\n\n/** Put the workspace's columns in the given order; unnamed columns keep their place. */\nexport async function reorderColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n orderedIds: string[],\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n for (const [order, statusId] of orderedIds.entries()) {\n await tx.statuses.update(statusId, { order })\n }\n }, scope)\n}\n\n/**\n * Bring a board that predates the standard set up to it, without touching the\n * columns its owner made.\n *\n * Each missing standard column goes directly after the nearest standard column\n * before it that the board already has — so `To Do` lands behind `Backlog` even\n * when the owner put three columns of their own in between — and at the front\n * when none of them is there. A board that already holds all six is left\n * untouched, down to the board version: running this twice must not look like a\n * change to anyone polling.\n */\nexport async function addStandardColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ statuses: IssueStatus[], added: string[] }> {\n const present = await listColumns(store, workspaceId)\n const presentSlugs = new Set(present.map(column => normalizeStatusName(column.name)))\n const missing = DEFAULT_STATUSES.filter(status => !presentSlugs.has(normalizeStatusName(status.name)))\n if (missing.length === 0) {\n return { statuses: present, added: [] }\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n // The board as it will look, standard columns included, before anything is\n // written: inserting into this list is what decides the final order.\n const planned: Array<{ column: IssueStatus } | { standard: typeof DEFAULT_STATUSES[number] }> = present\n .map(column => ({ column }))\n\n for (const standard of missing) {\n planned.splice(placeAfterPrecedingStandard(planned, standard), 0, { standard })\n }\n\n const added: string[] = []\n for (const [order, entry] of planned.entries()) {\n if ('standard' in entry) {\n await insertColumn(tx, { workspaceId, ...entry.standard }, order)\n added.push(entry.standard.name)\n continue\n }\n if (entry.column.order !== order) {\n await tx.statuses.update(entry.column.id, { order })\n }\n }\n\n return { statuses: await listColumns(tx, workspaceId), added }\n }, scope)\n}\n\n/** Where a missing standard column goes: right after the last standard column before it. */\nfunction placeAfterPrecedingStandard(\n planned: Array<{ column: IssueStatus } | { standard: typeof DEFAULT_STATUSES[number] }>,\n standard: typeof DEFAULT_STATUSES[number],\n): number {\n const precedingSlugs = DEFAULT_STATUSES\n .slice(0, DEFAULT_STATUSES.indexOf(standard))\n .map(preceding => normalizeStatusName(preceding.name))\n\n for (const slug of precedingSlugs.toReversed()) {\n const index = planned.findIndex(entry => normalizeStatusName(\n 'standard' in entry ? entry.standard.name : entry.column.name,\n ) === slug)\n if (index >= 0) {\n return index + 1\n }\n }\n return 0\n}\n\nasync function insertColumn(\n store: BoardStore,\n input: BoardColumnInput,\n order: number,\n): Promise<IssueStatus> {\n return await store.statuses.create({\n id: randomUUID(),\n workspaceId: input.workspaceId,\n name: input.name,\n description: input.description ?? null,\n color: input.color ?? null,\n category: input.category ?? 'unstarted',\n entryRules: serializeEntryRules(input.entryRules ?? null),\n order,\n createdAt: currentUnixSeconds(),\n })\n}\n","/**\n * The dedupe keys the facts about one pull request are written under on a card.\n *\n * Whatever watches a pull request writes them — opened, merged, closed and a CI\n * verdict per head — and whatever reads a card's pull requests, this package's\n * `ci_green` entry rule included, reads them back (rulings 4-7, 6-2), so the spelling lives here,\n * with the board, rather than being copied into each. Owner and repo are lower-cased\n * because GitHub names are case-insensitive (ruling 4-6): `Acme/Repo#7` and\n * `acme/repo#7` key to the same facts.\n */\nexport interface PullRequestFactKeys {\n opened: string\n merged: string\n closed: string\n /** GitHub answered 404/422 for this link on enough consecutive ticks that the watcher dropped it (`pr-watcher.ts`). */\n gone: string\n /** What every CI verdict key of this pull request starts with; the head sha and `:green` or `:red` follow. */\n ciPrefix: string\n}\n\nexport function pullRequestFactKeys({ owner, repo, number }: { owner: string, repo: string, number: number }): PullRequestFactKeys {\n const key = `${owner.toLowerCase()}/${repo.toLowerCase()}#${number}`\n return {\n opened: `pr-opened:${key}`,\n merged: `pr-merged:${key}`,\n closed: `pr-closed:${key}`,\n gone: `pr-gone:${key}`,\n ciPrefix: `ci:${key}@`,\n }\n}\n","import type { BoardStore } from '../board-store'\nimport type { EntryRule, EntryRuleFacts, UnmetEntryRule } from '../domain/entry-rules'\nimport { evaluateEntryRules, readEntryRules } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { pullRequestFactKeys } from '../domain/pull-request-fact-keys'\nimport type { IssueStatus } from '../sqlite/schema'\nimport { readApproval } from './approval'\nimport type { BoardActor } from './types'\n\n/**\n * A card about to enter a column: its id — `null` while it is still being\n * created — and its description as it will be once the write lands.\n */\nexport interface EnteringCard {\n id: string | null\n description: string | null\n}\n\n/**\n * The column's entry rules the card does not meet (ruling 6-2). A column with\n * no rules answers at once, without reading anything; otherwise only the facts\n * the column's rules need are read.\n */\nasync function checkColumnEntry(\n store: BoardStore,\n card: EnteringCard,\n column: IssueStatus,\n): Promise<UnmetEntryRule[]> {\n const rules = readEntryRules(column)\n if (rules.length === 0) {\n return []\n }\n return evaluateEntryRules(card, rules, await readEntryRuleFacts(store, card.id, rules)).unmet\n}\n\n/**\n * Hold a card at the door of a column that asks for more than it has (ruling\n * 6-3). Anyone but a person — or a person's own terminal, `placesForPerson`\n * (ruling 6-6) — is refused with `board_column_rules_unmet`, which\n * names the column and every unmet rule; a person is let through, and the\n * unmet rules come back so the caller can warn them.\n */\nexport async function enforceColumnEntry(\n store: BoardStore,\n card: EnteringCard,\n column: IssueStatus,\n actor: BoardActor,\n): Promise<UnmetEntryRule[]> {\n const unmet = await checkColumnEntry(store, card, column)\n if (unmet.length > 0 && actor.kind !== 'user' && !actor.placesForPerson) {\n throw new BoardError('board_column_rules_unmet', {\n issueId: card.id,\n column: column.name,\n statusId: column.id,\n unmet,\n })\n }\n return unmet\n}\n\n/**\n * The same door for several cards entering one column at once — a bulk status\n * change. All or nothing: the first card an agent may not move refuses the\n * whole change. A person gets back the cards that entered with rules unmet.\n */\nexport async function enforceColumnEntryForCards(\n store: BoardStore,\n issueIds: string[],\n statusId: string,\n actor: BoardActor,\n): Promise<Array<{ issueId: string, unmet: UnmetEntryRule[] }>> {\n const column = await store.statuses.findById(statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n const entered: Array<{ issueId: string, unmet: UnmetEntryRule[] }> = []\n for (const card of await store.issues.listByIds(issueIds)) {\n if (card.statusId === column.id) {\n continue\n }\n const unmet = await enforceColumnEntry(store, card, column, actor)\n if (unmet.length > 0) {\n entered.push({ issueId: card.id, unmet })\n }\n }\n return entered\n}\n\n/**\n * What the rules need to know beyond the card's description. A card still\n * being created has no pull request, no CI verdict and no decision yet.\n */\nasync function readEntryRuleFacts(store: BoardStore, issueId: string | null, rules: EntryRule[]): Promise<EntryRuleFacts> {\n if (issueId === null) {\n return { pullRequestCount: 0, latestCi: null, approval: 'none' }\n }\n const links = rules.includes('pull_request_linked') || rules.includes('ci_green')\n ? await store.pullRequests.listByIssue(issueId)\n : []\n return {\n pullRequestCount: links.length,\n latestCi: rules.includes('ci_green') ? await readLatestCi(store, issueId, links) : null,\n approval: rules.includes('approved') ? (await readApproval(store, issueId)).state : 'none',\n }\n}\n\n/**\n * The newest CI verdict across the card's linked pull requests, read off the\n * facts' dedupe keys: `ci:<owner>/<repo>#<number>@<sha>:green|red`, the\n * spelling a pull request watcher writes. A verdict about a commit that is not one\n * of the card's linked pull requests does not count.\n */\nasync function readLatestCi(\n store: BoardStore,\n issueId: string,\n links: Array<{ owner: string, repo: string, number: number }>,\n): Promise<'green' | 'red' | null> {\n if (links.length === 0) {\n return null\n }\n const prefixes = links.map(link => pullRequestFactKeys(link).ciPrefix)\n const newest = (await store.comments.listByDedupeKeyPrefix(issueId, 'ci:'))\n .filter(comment => prefixes.some(prefix => comment.dedupeKey?.startsWith(prefix)))\n .at(-1)\n ?.dedupeKey\n if (newest?.endsWith(':green')) {\n return 'green'\n }\n return newest?.endsWith(':red') ? 'red' : null\n}\n","import { BoardError } from './errors'\n\n/**\n * Where an external agent's own log of a run lives, once it has said (ruling\n * 7-1): which tool wrote it, and the id that tool knows the session by.\n */\nexport interface ExternalSessionRef {\n provider: 'claude' | 'codex'\n /**\n * The tool's own id for the session — a Claude Code session id, or a Codex\n * session id (the one in the rollout's `session_meta`, which Codex prints).\n */\n id: string\n}\n\n/** `claude:<id>` or `codex:<id>`, the id made of anything a filename may hold. */\nconst EXTERNAL_SESSION_REF = /^(claude|codex):([\\w.-]+)$/\n\n/**\n * Read an external session reference out of what an agent passed to\n * `kanbo run start --session` or `kanbo run attach-session`: `claude:<id>` —\n * a Claude Code session id — or `codex:<id>` — a Codex session id. Anything\n * else is refused with `board_run_session_ref_invalid` rather than stored\n * unread, because a ref this build cannot parse back is a transcript nobody\n * can ever open.\n *\n * Doubles as the validator: a caller that only needs to know whether a string\n * is well formed calls this and catches the throw, the same as\n * `parsePullRequestRef`.\n */\nexport function parseExternalSessionRef(input: string): ExternalSessionRef {\n const match = EXTERNAL_SESSION_REF.exec(input.trim())\n if (!match) {\n throw new BoardError('board_run_session_ref_invalid', { value: input })\n }\n return { provider: match[1] as ExternalSessionRef['provider'], id: match[2]! }\n}\n\n/** The stored and printed form: `claude:<id>` or `codex:<id>`. */\nexport function formatExternalSessionRef(ref: ExternalSessionRef): string {\n return `${ref.provider}:${ref.id}`\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardRunProjection, BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { formatExternalSessionRef, parseExternalSessionRef } from '../domain/external-session-ref'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueRun } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR, insertComment, requireCard, toRunLaunchedByKind, writeStatusLine } from './types'\n\n/** What is known about a run when it starts. */\nexport interface StartRunInput {\n agentName: string\n agentId?: string | null\n host?: string | null\n /** Defaults to the mode the card carries. */\n executionMode?: Issue['executionMode']\n /** Defaults to the actor's own kind and id. */\n launchedByKind?: IssueRun['launchedByKind']\n launchedById?: string | null\n chatSessionId?: string | null\n branch?: string | null\n worktreePath?: string | null\n /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n externalSessionRef?: string | null\n}\n\n/** What a run turned out to be running in, once the host knows. */\nexport interface AttachRunExecutionInput {\n chatSessionId?: string | null\n branch?: string | null\n worktreePath?: string | null\n /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n externalSessionRef?: string | null\n}\n\n/**\n * Read and normalize an `externalSessionRef` a caller passed in — `undefined`\n * when the input named nothing, `null` when it named nothing on purpose, and\n * the canonical `claude:<id>` / `codex:<id>` form otherwise. Checked before the\n * write starts, the same as an entry rule: a ref this build cannot parse back\n * changes nothing on the board.\n */\nfunction readExternalSessionRef(value: string | null | undefined): string | null | undefined {\n if (value === undefined || value === null) {\n return value\n }\n return formatExternalSessionRef(parseExternalSessionRef(value))\n}\n\n/** How a run ended, and — for a failure or a stop — what to say about it. */\nexport interface FinishRunInput {\n state: 'finished' | 'failed' | 'stopped'\n errorText?: string | null\n /**\n * Why a `stopped` run ended, when the answer is neither a person pressing\n * stop nor the app that launched it tidying up quietly: the comment then reads\n * `Run stopped: <reason>` instead of `Run stopped`.\n */\n reason?: string\n /**\n * What the card says it is doing once that stop is recorded, whoever the\n * actor is. A stop with no line of its own leaves the rule as it was: the\n * person who pressed stop is named, and anything else says nothing.\n */\n statusLine?: string\n}\n\n/** A run with the attempt number it is, counting from the card's first launch. */\nexport type BoardRunView = IssueRun & { attempt: number }\n\n/** The status line written when a run fails with nothing to show for it. */\nexport const NO_REPORT_STATUS_LINE = 'agent exited with an error, no report'\n\n/** What a launch writes as the card's status line, ahead of the agent's own name. */\nexport const LAUNCHED_STATUS_LINE_PREFIX = 'launched '\n\n/** What a launch writes as the system comment, ahead of the agent, attempt and mode. */\nexport const RUN_STARTED_COMMENT_PREFIX = 'Run started: '\n\n/** The system comment a run that finished cleanly writes. */\nexport const RUN_FINISHED_COMMENT = 'Run finished'\n\n/** The system comment a failed run writes when it names no error text. */\nexport const RUN_FAILED_COMMENT = 'Run failed'\n\n/** What a failed run writes as the system comment, ahead of its own error text. */\nexport const RUN_FAILED_COMMENT_PREFIX = 'Run failed: '\n\n/** The system comment a stop writes when a person pressed stop. */\nexport const RUN_STOPPED_BY_YOU_COMMENT = 'Run stopped by you'\n\n/** The system comment a stop writes when nobody in particular pressed it. */\nexport const RUN_STOPPED_COMMENT = 'Run stopped'\n\n/** What a stop with a reason writes as the system comment, ahead of that reason. */\nexport const RUN_STOPPED_REASON_PREFIX = 'Run stopped: '\n\n/** The status line a stop writes when a person pressed stop and named no line of their own. */\nexport const STOPPED_BY_YOU_STATUS_LINE = 'stopped by you'\n\n/**\n * Record that the card is being worked on.\n *\n * The card gets a line saying who is on it and a comment naming the attempt, so\n * the board reads the same whether the agent was launched by an app or from\n * somewhere else entirely. The attempt number is simply how many runs the card\n * has had — a card whose runs were all launched elsewhere still counts them.\n */\nexport async function startRun<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: StartRunInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts: a session ref this build cannot parse\n // back changes nothing.\n const externalSessionRef = readExternalSessionRef(input.externalSessionRef) ?? null\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const attempt = await tx.runs.countByIssue(issueId) + 1\n const executionMode = input.executionMode ?? card.executionMode\n\n const run = await tx.runs.create({\n id: randomUUID(),\n issueId,\n launchedByKind: input.launchedByKind ?? toRunLaunchedByKind(actor),\n launchedById: input.launchedById ?? actor.id ?? null,\n agentId: input.agentId ?? null,\n agentName: input.agentName,\n host: input.host ?? null,\n executionMode,\n branch: input.branch ?? null,\n worktreePath: input.worktreePath ?? null,\n state: 'running',\n chatSessionId: input.chatSessionId ?? null,\n externalSessionRef,\n startedAt: currentUnixSeconds(),\n endedAt: null,\n })\n\n await writeStatusLine(tx, card, `${LAUNCHED_STATUS_LINE_PREFIX}${input.agentName}`, BOARD_ACTOR)\n await insertComment(tx, {\n issueId,\n authorKind: 'system.run',\n content: `${RUN_STARTED_COMMENT_PREFIX}${input.agentName} · attempt ${attempt} · ${executionMode}`,\n })\n\n return run\n }, scope)\n}\n\n/**\n * Fill in what the host only learns after the run is already going — the session\n * driving it, the branch and the worktree it works in. A run that has already\n * ended is history, and history is not edited. The agent's own log is only ever\n * filled, never replaced or cleared, the same as in `recordRunSessionRef`.\n */\nexport async function attachRunExecution<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n input: AttachRunExecutionInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = readExternalSessionRef(input.externalSessionRef)\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.state !== 'running') {\n throw new BoardError('board_run_already_finished', { runId, state: run.state })\n }\n\n const patch: Partial<IssueRun> = {}\n if (input.chatSessionId !== undefined) {\n patch.chatSessionId = input.chatSessionId\n }\n if (input.branch !== undefined) {\n patch.branch = input.branch\n }\n if (input.worktreePath !== undefined) {\n patch.worktreePath = input.worktreePath\n }\n if (externalSessionRef !== undefined && fillRunSessionRef(run, externalSessionRef)) {\n patch.externalSessionRef = externalSessionRef\n }\n if (Object.keys(patch).length > 0) {\n await tx.runs.update(runId, patch)\n }\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Name the log a run wrote, after the fact — the agent saying so with\n * `kanbo run attach-session`, or a person picking it out of the logs on this\n * machine once the run is over (ruling 7-2).\n *\n * Unlike `attachRunExecution`, this is allowed on a run that has ended: which\n * log the run wrote is part of its history, found later, not a change to it.\n * It only fills a run that named no log of its own. The same ref again changes\n * nothing; a different one is refused, because the first word on where the log\n * is may already have had a chat imported from it.\n */\nexport async function recordRunSessionRef<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n ref: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = formatExternalSessionRef(parseExternalSessionRef(ref))\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (!fillRunSessionRef(run, externalSessionRef)) {\n return run\n }\n await tx.runs.update(runId, { externalSessionRef })\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Take back the log a run names — a person saying the one recorded was wrong.\n *\n * The one way past the fill-only rule, and so a person's alone: an agent that\n * could clear or swap a ref could point a card's history at any log it liked.\n * Without `replaceWith` the run names no log afterwards; with it, the run names\n * that one instead. Allowed on a run that has ended, like `recordRunSessionRef`.\n * Whatever chat was imported from the old ref is the host's to forget.\n */\nexport async function clearRunSessionRef<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n actor: BoardActor,\n options: { replaceWith?: string } = {},\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_run_session_ref_requires_user', { runId, actorKind: actor.kind })\n }\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = readExternalSessionRef(options.replaceWith) ?? null\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.externalSessionRef === externalSessionRef) {\n return run\n }\n await tx.runs.update(runId, { externalSessionRef })\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Whether naming `ref` as the run's log writes anything — the one rule every\n * path that touches `externalSessionRef` follows (ruling 7-2): the ref is only\n * ever filled. Naming the one the run already carries, or naming none on a run\n * that carries none, changes nothing; replacing or clearing one it carries is\n * refused, because a chat may already have been imported from it.\n */\nfunction fillRunSessionRef(run: IssueRun, ref: string | null): boolean {\n if (run.externalSessionRef === ref) {\n return false\n }\n if (run.externalSessionRef !== null) {\n throw new BoardError('board_run_session_ref_conflict', { runId: run.id, externalSessionRef: run.externalSessionRef })\n }\n return ref !== null\n}\n\n/**\n * End a run, and say on the card what that meant.\n *\n * Finishing is idempotent: a run that already ended comes back untouched, with\n * nothing written. An app that launches agents reaches this from several directions at once — the\n * session settling, a person pressing stop, a card being returned — and the\n * second of them must not double the comments or move the status line again.\n *\n * A completed run leaves the status line alone: whatever the agent last said it\n * was doing is a better final word than anything the app could write. A failure\n * only writes a line when nothing but the board wrote one since the launch,\n * because a card that says nothing after a crash is the one case where the\n * person looking at it learns nothing at all.\n */\nexport async function finishRun<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n input: FinishRunInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.state !== 'running') {\n return run\n }\n\n await tx.runs.update(runId, { state: input.state, endedAt: currentUnixSeconds() })\n const card = await requireCard(tx, run.issueId)\n await insertComment(tx, {\n issueId: run.issueId,\n authorKind: 'system.run',\n content: runComment(input, actor),\n })\n\n if (input.state === 'failed' && !await reportedSince(tx, run)) {\n await writeStatusLine(tx, card, NO_REPORT_STATUS_LINE, BOARD_ACTOR)\n }\n if (input.state === 'stopped') {\n const line = stoppedStatusLine(input, actor)\n if (line) {\n await writeStatusLine(tx, card, line, BOARD_ACTOR)\n }\n }\n\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/** End every run of the card that is still going. Returns the runs that were stopped. */\nexport async function stopRunningRuns<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<string[]> {\n return await runBoardWrite(store, async ({ tx }) => {\n const stopped: string[] = []\n for (const run of await tx.runs.listRunning(issueId)) {\n await finishRun(tx, run.id, { state: 'stopped' }, actor, { tx })\n stopped.push(run.id)\n }\n return stopped\n }, scope)\n}\n\n/** Every run of the card, oldest first, each carrying its attempt number. */\nexport async function listRuns(store: BoardStore, issueId: string): Promise<BoardRunView[]> {\n const runs = await store.runs.listByIssue(issueId)\n return runs.map((run, index) => ({ ...run, attempt: index + 1 }))\n}\n\n/** The run driving that chat session, for the facts an app learns session-first. */\nexport async function findRunByChatSessionId(store: BoardStore, chatSessionId: string): Promise<IssueRun | null> {\n return await store.runs.findByChatSessionId(chatSessionId)\n}\n\n/**\n * The run facts of a whole workspace in one statement: cards with no runs are\n * absent, because a sweep of the board only needs to hear about the ones that\n * have been launched.\n */\nexport async function readBoardProjection(store: BoardStore, workspaceId: string): Promise<BoardRunProjection[]> {\n return await store.runs.project({ workspaceId })\n}\n\n/** The run facts of the named cards, one entry per card asked for, in that order. */\nexport async function readBoardProjectionForIssues(store: BoardStore, issueIds: string[]): Promise<BoardRunProjection[]> {\n const found = new Map((await store.runs.project({ issueIds })).map(projection => [projection.issueId, projection]))\n return issueIds.map(issueId => found.get(issueId) ?? emptyProjection(issueId))\n}\n\n/** The run facts of one card, whether or not it has ever been launched. */\nexport async function readBoardProjectionForIssue(store: BoardStore, issueId: string): Promise<BoardRunProjection> {\n return (await readBoardProjectionForIssues(store, [issueId]))[0]\n}\n\nfunction emptyProjection(issueId: string): BoardRunProjection {\n return { issueId, attemptCount: 0, activeRun: null }\n}\n\nasync function requireRun(store: BoardStore, runId: string): Promise<IssueRun> {\n const run = await store.runs.findById(runId)\n if (!run) {\n throw new BoardError('board_run_not_found', { runId })\n }\n return run\n}\n\nfunction runComment(input: FinishRunInput, actor: BoardActor): string {\n if (input.state === 'finished') {\n return RUN_FINISHED_COMMENT\n }\n if (input.state === 'failed') {\n return input.errorText ? `${RUN_FAILED_COMMENT_PREFIX}${input.errorText}` : RUN_FAILED_COMMENT\n }\n if (input.reason) {\n return `${RUN_STOPPED_REASON_PREFIX}${input.reason}`\n }\n return actor.kind === 'user' ? RUN_STOPPED_BY_YOU_COMMENT : RUN_STOPPED_COMMENT\n}\n\n/**\n * What the card says about itself once a stop is recorded, or nothing at all.\n *\n * A stop that names its own line writes it whoever the actor is: the card was\n * showing work going on, and what ended it — the launching app restarting, say — is a fact\n * about the card, not about the person who happened to be holding the button.\n * Without one the old rule stands: only a person pressing stop is named, and\n * the app tidying up leaves the card's last word alone.\n */\nfunction stoppedStatusLine(input: FinishRunInput, actor: BoardActor): string | null {\n if (input.statusLine) {\n return input.statusLine\n }\n return actor.kind === 'user' ? STOPPED_BY_YOU_STATUS_LINE : null\n}\n\n/**\n * Did anything working this card say a word of its own since the run began?\n *\n * Not only the `agent` kind. An agent an app launched writes as `agent`, one a\n * provider target stands in for keeps that kind, and an agent launched from a\n * terminal or through `kanbo mcp` is stored as the system with a name — its\n * line is still the run's own report, and the last thing worth reading on a\n * card that then crashed. The board itself is the one writer that does not\n * count, and `BOARD_ACTOR` is the only one stored as the system with no name at\n * all, so that single pair is what this rules out.\n */\nasync function reportedSince(store: BoardStore, run: IssueRun): Promise<boolean> {\n const lines = await store.fieldChanges.listByIssueField(run.issueId, 'statusLine')\n return lines.some(line => line.createdAt >= run.startedAt\n && !(line.actorKind === 'system' && line.actorId === null))\n}\n","import type { BoardStore } from '../board-store'\nimport type { UnmetEntryRule } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { diffFieldChanges } from '../domain/field-changes'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport { nextIssueIdentity, nextIssueNumber, nextIssueOrder } from '../domain/numbering'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueComment, issues, IssueStatus } from '../sqlite/schema'\nimport { APPROVED_COMMENT } from './approval'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { ensureDefaultColumns, listColumns, requireColumn } from './columns'\nimport { enforceColumnEntry } from './entry-rules'\nimport { readBoardProjectionForIssue, stopRunningRuns } from './runs'\nimport type { BoardActor, BoardCommentInput } from './types'\nimport { BOARD_ACTOR, insertComment, requireCard, toStoredActorKind, writeStatusLine } from './types'\n\n/** Which column a caller means, when it may name either. */\ninterface ColumnReference {\n statusId?: string | null\n statusName?: string | null\n}\n\n/** A new card. The workspace comes in resolved: the board never reads anybody else's tables to find it. */\nexport interface CreateCardInput extends ColumnReference {\n workspace: BoardWorkspaceIdentity\n /** Optional: a card written as a description alone is titled with its own key. */\n title?: string | null\n description?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n parentIssueId?: string | null\n dueDate?: number | null\n assigneeKind?: string | null\n assigneeId?: string | null\n executionMode?: Issue['executionMode']\n}\n\n/** Everything a card's fields can be changed to. */\nexport interface UpdateCardInput extends ColumnReference {\n workspaceId?: string\n title?: string\n description?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n parentIssueId?: string | null\n assigneeKind?: string | null\n assigneeId?: string | null\n dueDate?: number | null\n order?: number\n executionMode?: Issue['executionMode']\n}\n\n/** What a return writes as the card's status line, ahead of the person's own reason. */\nexport const RETURNED_STATUS_LINE_PREFIX = 'returned by you: '\n\n/**\n * A card as a write that may have put it in a column hands it back. `unmetRules`\n * is there only when a person put the card in a column whose entry rules it does\n * not meet (ruling 6-3) — the write went through, and this is what to warn them\n * about. An agent never sees it: it is refused instead.\n */\nexport type BoardCardWrite = Issue & { unmetRules?: UnmetEntryRule[] }\n\n/** A card and the rules it entered its column without — the field only when there are some. */\nfunction withUnmetRules(card: Issue, unmet: UnmetEntryRule[]): BoardCardWrite {\n return unmet.length > 0 ? { ...card, unmetRules: unmet } : card\n}\n\n/** How many times a card creation retries a number another writer took first. */\nconst NUMBERING_ATTEMPTS = 3\n\n/**\n * Put a new card on the board.\n *\n * The key and the number are taken inside the write, because they are read from\n * the board itself: two writers picking `WOR-004` at the same moment is the one\n * collision the unique index cannot be argued out of, and the loser simply takes\n * the next free number instead. A failure that is not about numbering — a\n * milestone that no longer exists, a parent in another workspace — is not\n * retried; it would fail the same way three times.\n *\n * A card needs no title: one created without it (or with a blank one) is titled\n * with its key, so a person or an agent can write only what the task is.\n */\nexport async function createCard<TStore extends BoardStore>(\n store: TStore,\n input: CreateCardInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n const workspaceId = input.workspace.id\n\n return await runBoardWrite(store, async ({ tx }) => {\n const statusId = await resolveColumnReference(tx, workspaceId, input, { useDefaultWhenMissing: true })\n // The column's entry rules hold for a new card as for a moved one: a card\n // being created has no pull request and no decision yet, only the\n // checklist it is written with.\n const column = statusId === null ? null : await tx.statuses.findById(statusId)\n const unmet = column\n ? await enforceColumnEntry(tx, { id: null, description: input.description ?? null }, column, actor)\n : []\n const milestoneId = await requireMilestoneInWorkspace(tx, workspaceId, input.milestoneId ?? null)\n // The parent is checked before the key is generated: a key the board has not\n // handed out yet cannot be its own parent, so only the workspace matters.\n const parentIssueId = await requireParentInWorkspace(tx, workspaceId, input.parentIssueId ?? null)\n const order = await nextIssueOrder(workspaceId, tx)\n const now = currentUnixSeconds()\n\n for (let attempt = 1; ; attempt++) {\n const identity = await nextIssueIdentity(input.workspace, tx)\n try {\n return withUnmetRules(await tx.issues.create({\n id: identity.id,\n workspaceId,\n title: input.title?.trim() || identity.id,\n description: input.description ?? null,\n priority: input.priority ?? 'none',\n labels: JSON.stringify(input.labels ?? []),\n milestoneId,\n parentIssueId,\n statusId,\n number: identity.number,\n assigneeKind: input.assigneeKind ?? null,\n assigneeId: input.assigneeId ?? null,\n dueDate: input.dueDate ?? null,\n createdByKind: toStoredActorKind(actor),\n createdById: actor.id ?? '__self__',\n sourceChatSessionId: actor.chatSessionId ?? null,\n delegateAgentId: null,\n delegateProviderTargetId: null,\n contextRefs: '[]',\n executionMode: input.executionMode ?? 'worktree',\n order,\n createdAt: now,\n updatedAt: now,\n }), unmet)\n }\n catch (error) {\n // The board, not the driver, says whether this was a numbering\n // collision: the key or the number is now taken by a row that is not\n // this one. Anything else is a real failure and belongs to the caller.\n const numberTaken = await tx.issues.findByNumber(workspaceId, identity.number)\n const keyTaken = await tx.issues.findById(identity.id)\n if (attempt >= NUMBERING_ATTEMPTS || (!numberTaken && !keyTaken)) {\n throw error\n }\n }\n }\n }, scope)\n}\n\n/** Put a new card under an existing one, in the parent's workspace. */\nexport async function createSubCard<TStore extends BoardStore>(\n store: TStore,\n parentIssueId: string,\n input: Omit<CreateCardInput, 'parentIssueId'>,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const parent = await requireCard(tx, parentIssueId)\n return await createCard(tx, { ...input, parentIssueId: parent.id }, actor, { tx })\n }, scope)\n}\n\n/**\n * Change a card's fields, recording every tracked one in its history.\n *\n * Moving a card to another workspace is part of this: the card takes that\n * board's columns, drops a milestone or a parent that does not live there, goes\n * to the end of the board, and takes a new number if its own is already in use.\n * The execution mode is the one field that stops being editable — once a card\n * has been launched, where it runs is a fact about work that already happened.\n *\n * Naming a column the card is not in yet is putting it there, and the column's\n * entry rules are checked against the card as this write leaves it — a\n * description ticked off in the same update counts (ruling 6-3). So is the\n * first column a card lands in because it changed workspace (ruling 6-7): the\n * caller asked for the move, so it is not the board's own doing.\n */\nexport async function updateCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: UpdateCardInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const workspaceId = input.workspaceId ?? card.workspaceId\n const workspaceChanged = workspaceId !== card.workspaceId\n const updates: Partial<typeof issues.$inferInsert> = { updatedAt: currentUnixSeconds() }\n\n if ('workspaceId' in input) {\n updates.workspaceId = workspaceId\n }\n if (workspaceChanged) {\n if (!('statusId' in input) && !('statusName' in input)) {\n updates.statusId = await resolveColumnReference(tx, workspaceId, {}, { useDefaultWhenMissing: true })\n }\n if (!('milestoneId' in input)) {\n updates.milestoneId = card.milestoneId && await tx.milestones.findInWorkspace(workspaceId, card.milestoneId)\n ? card.milestoneId\n : null\n }\n if (!('parentIssueId' in input)) {\n updates.parentIssueId = card.parentIssueId && await tx.issues.findInWorkspace(workspaceId, card.parentIssueId)\n ? card.parentIssueId\n : null\n }\n if (input.order === undefined) {\n updates.order = await nextIssueOrder(workspaceId, tx)\n }\n if (await numberTakenByAnother(tx, workspaceId, card)) {\n updates.number = await nextIssueNumber(workspaceId, tx)\n }\n }\n if (input.title !== undefined) {\n updates.title = input.title\n }\n if ('description' in input) {\n updates.description = input.description ?? null\n }\n if (input.priority !== undefined) {\n updates.priority = input.priority\n }\n if (input.labels !== undefined) {\n updates.labels = JSON.stringify(input.labels)\n }\n if ('milestoneId' in input) {\n updates.milestoneId = await requireMilestoneInWorkspace(tx, workspaceId, input.milestoneId ?? null)\n }\n if ('parentIssueId' in input) {\n updates.parentIssueId = await requireParentInWorkspace(tx, workspaceId, input.parentIssueId ?? null, issueId)\n }\n if ('statusId' in input || 'statusName' in input) {\n updates.statusId = await resolveColumnReference(tx, workspaceId, input, { useDefaultWhenMissing: false })\n }\n // Named, or the first column of the board the card moved to (ruling 6-7):\n // either way the card is entering it.\n let unmet: UnmetEntryRule[] = []\n const column = updates.statusId && updates.statusId !== card.statusId\n ? await tx.statuses.findById(updates.statusId)\n : null\n if (column) {\n const description = 'description' in updates ? updates.description ?? null : card.description\n unmet = await enforceColumnEntry(tx, { id: card.id, description }, column, actor)\n }\n if ('assigneeKind' in input) {\n updates.assigneeKind = input.assigneeKind ?? null\n }\n if ('assigneeId' in input) {\n updates.assigneeId = input.assigneeId ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n if (input.order !== undefined) {\n updates.order = input.order\n }\n if (input.executionMode !== undefined && input.executionMode !== card.executionMode) {\n if ((await readBoardProjectionForIssue(tx, issueId)).attemptCount > 0) {\n throw new BoardError('board_execution_mode_locked', { issueId, executionMode: card.executionMode })\n }\n updates.executionMode = input.executionMode\n }\n\n await recordFieldChanges(tx, card, updates, actor)\n await tx.issues.update(issueId, updates)\n return withUnmetRules(await requireCard(tx, issueId), unmet)\n }, scope)\n}\n\n/**\n * Move a card to the column named by id, by slug or by name — through\n * `updateCard`, so the column's entry rules hold here too.\n */\nexport async function moveCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n nameOrId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await ensureDefaultColumns(tx, card.workspaceId, { tx })\n const column = await requireColumn(tx, card.workspaceId, nameOrId)\n return await updateCard(tx, issueId, { statusId: column.id }, actor, { tx })\n }, scope)\n}\n\n/** Say what the card is doing right now. */\nexport async function setStatusLine<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n statusLine: string | null,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await writeStatusLine(tx, card, statusLine, actor)\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Hand the card to a person and stop working on it. Only a person takes it back\n * — through `approve` or `returnCard` — which is the whole point of the flag.\n */\nexport async function waitApproval<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { statusLine?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await tx.issues.update(issueId, { waitingFor: 'human', updatedAt: currentUnixSeconds() })\n if (input.statusLine !== undefined) {\n await writeStatusLine(tx, { ...card, waitingFor: 'human' }, input.statusLine, actor)\n }\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Accept the work: the card stops waiting, and the decision is written down.\n *\n * Nothing but a person may do this. On a local board file that is an agreement\n * rather than a lock — the same file is writable by anything that can open it —\n * but the agreement is worth having: an agent that could approve its own work\n * would make the whole waiting state meaningless.\n */\nexport async function approve<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { comment?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_approval_requires_user', { issueId, actorKind: actor.kind })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n await tx.issues.update(issueId, { waitingFor: null, updatedAt: currentUnixSeconds() })\n const comment = input.comment?.trim()\n await insertComment(tx, {\n issueId,\n authorKind: 'system.approved',\n content: comment ? `${APPROVED_COMMENT}\\n${comment}` : APPROVED_COMMENT,\n authorId: actor.id,\n sourceChatSessionId: actor.chatSessionId,\n })\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Send the card back a column with a reason.\n *\n * The reason is the point: it goes on the card as the person's own comment and\n * as the status line, so the next agent to pick the card up reads why it came\n * back before anything else. Whatever was running on the card is stopped in the\n * same write — a card sitting a column earlier while an agent still works on it\n * is the state the board must never be left in.\n *\n * This is a person's, exactly as `approve` is. They are the two answers to a\n * card that is waiting for a human, and an agent that could give either of them\n * could undo the one state the board promises a person owns — the line it\n * leaves even says \"returned by you\".\n */\nexport async function returnCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { comment: string, toStatusName?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ card: Issue, stoppedRunIds: string[] }> {\n return await runBoardWrite(store, async ({ tx }) => {\n await ensureDefaultColumns(tx, (await requireCard(tx, issueId)).workspaceId, { tx })\n const target = await assertReturnable(tx, issueId, input, actor)\n const card = await requireCard(tx, issueId)\n\n const updates = { statusId: target.id, waitingFor: null, updatedAt: currentUnixSeconds() } as const\n await recordFieldChanges(tx, card, updates, actor)\n await tx.issues.update(issueId, updates)\n await insertComment(tx, {\n issueId,\n authorKind: 'system.returned',\n content: input.comment,\n authorId: actor.id,\n sourceChatSessionId: actor.chatSessionId,\n })\n\n // The board stops the runs on its own behalf: the person is returning the\n // card, not pressing stop, and the line below is the one they should read.\n const stoppedRunIds = await stopRunningRuns(tx, issueId, BOARD_ACTOR, { tx })\n await writeStatusLine(tx, await requireCard(tx, issueId), `${RETURNED_STATUS_LINE_PREFIX}${input.comment}`, actor)\n\n return { card: await requireCard(tx, issueId), stoppedRunIds }\n }, scope)\n}\n\n/**\n * Every reason `returnCard` would refuse, asked on reads alone, and the column\n * the card would go back to when none applies.\n *\n * `returnCard` asks exactly this inside its write. A caller that has to undo\n * something of its own before the return — stop listening for the approval the\n * card is waiting on, say — asks it first, so a return the board refuses leaves\n * that untouched. The refusals are the same `BoardError` codes: an actor who is\n * not a person (`board_return_requires_user`), a card that is not there\n * (`issue_not_found`), a named column the workspace does not hold\n * (`issue_status_not_found`), and a card already in the first column with no\n * column named (`board_return_no_previous_column`).\n */\nexport async function assertReturnable(\n store: BoardStore,\n issueId: string,\n input: { toStatusName?: string | null },\n actor: BoardActor,\n): Promise<IssueStatus> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_return_requires_user', { issueId, actorKind: actor.kind })\n }\n\n const card = await requireCard(store, issueId)\n return input.toStatusName\n ? await requireColumn(store, card.workspaceId, input.toStatusName)\n : previousColumn(await listColumns(store, card.workspaceId), card)\n}\n\n/** A comment a person or an agent writes; the author defaults to the actor. */\nexport interface AddCommentInput extends Omit<BoardCommentInput, 'authorKind' | 'dedupeKey'> {\n authorKind?: BoardCommentInput['authorKind']\n}\n\n/** A fact the board records once, however many times it is told. */\nexport interface CreateCommentOnceInput {\n issueId: string\n authorKind: BoardCommentInput['authorKind']\n content: string\n dedupeKey: string\n actor: BoardActor\n}\n\n/** Write a comment on the card, as the actor unless the caller names another author. */\nexport async function addComment<TStore extends BoardStore>(\n store: TStore,\n input: AddCommentInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueComment> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.issueId)\n const authorKind = input.authorKind ?? toStoredActorKind(actor)\n return await insertComment(tx, {\n ...input,\n authorKind,\n authorId: input.authorId !== undefined ? input.authorId : actor.id,\n sourceChatSessionId: input.sourceChatSessionId !== undefined ? input.sourceChatSessionId : actor.chatSessionId,\n })\n }, scope)\n}\n\n/**\n * Write a fact on the card, once. A fact — a pull request opened, a check that\n * went green — is reported by whatever noticed it, as often as it notices;\n * `dedupeKey` is what keeps the card from saying it twice.\n */\nexport async function createCommentOnce<TStore extends BoardStore>(\n store: TStore,\n input: CreateCommentOnceInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueComment> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.issueId)\n return await insertComment(tx, {\n issueId: input.issueId,\n authorKind: input.authorKind,\n content: input.content,\n dedupeKey: input.dedupeKey,\n authorId: input.actor.id,\n sourceChatSessionId: input.actor.chatSessionId,\n })\n }, scope)\n}\n\n/** The card's comments, oldest first. */\nexport async function listComments(store: BoardStore, issueId: string): Promise<IssueComment[]> {\n await requireCard(store, issueId)\n return await store.comments.listByIssue(issueId)\n}\n\n/**\n * Delete a comment. A comment the board wrote — a decision (`system.*`) or a\n * fact it keeps once (`dedupeKey`, CI among them) — is a person's to delete:\n * the board reads the newest of them, so an agent deleting one could revive an\n * older approval or an older green CI run.\n */\nexport async function deleteComment<TStore extends BoardStore>(\n store: TStore,\n commentId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n const comment = await tx.comments.findById(commentId)\n if (!comment) {\n throw new BoardError('issue_comment_not_found', { commentId })\n }\n if (actor.kind !== 'user' && (comment.authorKind.startsWith('system.') || comment.dedupeKey !== null)) {\n throw new BoardError('issue_comment_delete_requires_user', { commentId, actorKind: toStoredActorKind(actor) })\n }\n await tx.comments.delete(commentId)\n }, scope)\n}\n\n/**\n * Take a card off the board, and hand back the ids of the runs that went with\n * it — a host that keeps rows of its own naming those runs lets go of them.\n *\n * Its children stay on the board without a parent, and every edge touching it\n * goes in both directions: an edge is not a child, and a counterpart left\n * holding half of one would render a relation to nothing.\n */\nexport async function deleteCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ runIds: string[] }> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const runIds = (await tx.runs.listByIssue(issueId)).map(run => run.id)\n await tx.issues.clearParentReferences(issueId, currentUnixSeconds())\n await tx.relations.deleteByIssue(issueId)\n await tx.issues.delete(issueId)\n return { runIds }\n }, scope)\n}\n\n/**\n * Cards whose key, number, title or description carry the text, ignoring case,\n * newest first and at most `limit` of them.\n *\n * Newest first is the order a host merging several boards' answers relies on:\n * each board's answer is already sorted on `createdAt`, so a stable merge on it\n * and a second cut to `limit` give the answer one board holding every card would.\n */\nexport async function searchCards(store: BoardStore, query: string, limit: number): Promise<Issue[]> {\n const needle = query.toLowerCase()\n return (await store.issues.listNewestFirst())\n .filter(card => [card.id, String(card.number), card.title, card.description ?? ''].join(' ').toLowerCase().includes(needle))\n .slice(0, limit)\n}\n\n/** The column before the card's own, or the error that says there is none. */\nfunction previousColumn(columns: IssueStatus[], card: Issue) {\n const index = columns.findIndex(column => column.id === card.statusId)\n const previous = index > 0 ? columns[index - 1] : undefined\n if (!previous) {\n throw new BoardError('board_return_no_previous_column', { issueId: card.id, statusId: card.statusId })\n }\n return previous\n}\n\n/** Append the history rows a patch earns, one per tracked field it actually changes. */\nexport async function recordFieldChanges(\n store: BoardStore,\n card: Issue,\n updates: Partial<typeof issues.$inferInsert>,\n actor: BoardActor,\n): Promise<void> {\n const rows = diffFieldChanges(card, updates, {\n kind: toStoredActorKind(actor),\n id: actor.id,\n sourceChatSessionId: actor.chatSessionId ?? null,\n }, currentUnixSeconds())\n for (const row of rows) {\n await store.fieldChanges.create(row)\n }\n}\n\n/**\n * The column a caller named, seeding the standard columns first so a board that\n * has never been opened still has somewhere to put the card. Naming both an id\n * and a name is a caller mistake, not a preference to resolve.\n */\nasync function resolveColumnReference(\n store: BoardStore,\n workspaceId: string,\n reference: ColumnReference,\n options: { useDefaultWhenMissing: boolean },\n): Promise<string | null> {\n if (reference.statusId != null && reference.statusName != null) {\n throw new BoardError('issue_status_reference_conflict', {\n statusId: reference.statusId,\n statusName: reference.statusName,\n })\n }\n if (reference.statusId == null && reference.statusName == null && !options.useDefaultWhenMissing) {\n return null\n }\n\n const columns = await ensureDefaultColumns(store, workspaceId, { tx: store })\n\n // A caller that named an id is answered about that id, and one that named a\n // name about that name: the failure has to hand back what was actually sent,\n // or the client is told it asked something it did not ask.\n if (reference.statusId != null) {\n const column = columns.find(candidate => candidate.id === reference.statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { workspaceId, statusId: reference.statusId })\n }\n return column.id\n }\n\n if (reference.statusName != null) {\n const slug = normalizeStatusName(reference.statusName)\n if (!slug) {\n throw new BoardError('issue_status_name_empty', { workspaceId })\n }\n const column = columns.find(candidate => normalizeStatusName(candidate.name) === slug)\n if (!column) {\n throw new BoardError('issue_status_not_found', {\n workspaceId,\n statusName: reference.statusName,\n normalizedStatusName: slug,\n })\n }\n return column.id\n }\n\n return columns[0]?.id ?? null\n}\n\n/** The milestone, when it belongs to the workspace, or the error that says it does not. */\nexport async function requireMilestoneInWorkspace(\n store: BoardStore,\n workspaceId: string,\n milestoneId: string | null,\n): Promise<string | null> {\n if (milestoneId === null) {\n return null\n }\n const milestone = await store.milestones.findInWorkspace(workspaceId, milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { workspaceId, milestoneId })\n }\n return milestone.id\n}\n\nasync function requireParentInWorkspace(\n store: BoardStore,\n workspaceId: string,\n parentIssueId: string | null,\n issueId?: string,\n): Promise<string | null> {\n if (parentIssueId === null) {\n return null\n }\n if (parentIssueId === issueId) {\n throw new BoardError('issue_parent_self_reference', { issueId, parentIssueId })\n }\n if (!await store.issues.findInWorkspace(workspaceId, parentIssueId)) {\n throw new BoardError('issue_parent_not_found', { workspaceId, parentIssueId })\n }\n return parentIssueId\n}\n\n/** Is the card's own number already taken by another card in the target workspace? */\nexport async function numberTakenByAnother(store: BoardStore, workspaceId: string, card: Issue): Promise<boolean> {\n const holder = await store.issues.findByNumber(workspaceId, card.number)\n return holder !== null && holder.id !== card.id\n}\n","import type { BoardStore } from '../board-store'\nimport type { UnmetEntryRule } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, issues } from '../sqlite/schema'\nimport { recordFieldChanges, requireMilestoneInWorkspace } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { enforceColumnEntryForCards } from './entry-rules'\nimport type { BoardActor } from './types'\n\n/** The fields one change can set on many cards at once. */\nexport interface BulkUpdateCardsInput {\n statusId?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n assigneeKind?: string | null\n assigneeId?: string | null\n dueDate?: number | null\n}\n\n/** What a bulk change did: how many cards it wrote, and — for a person — which of them entered a column short of its rules. */\nexport interface BulkUpdateCardsResult {\n updated: number\n unmetRules?: Array<{ issueId: string, unmet: UnmetEntryRule[] }>\n}\n\n/** How far apart a reorder, or a move onto another board, spaces the cards it places. */\nexport const CARD_ORDER_GAP = 1024\n\n/**\n * Apply one change to many cards, in one write, each card's history recording\n * what changed on it.\n *\n * Every card is checked before anything is written: a column or a milestone\n * must belong to each card's own workspace, and the column's door holds for\n * every card at once — an agent moving one card that does not qualify moves\n * none (ruling 6-3). An id the board does not hold is skipped, not refused.\n */\nexport async function bulkUpdateCards<TStore extends BoardStore>(\n store: TStore,\n issueIds: string[],\n input: BulkUpdateCardsInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BulkUpdateCardsResult> {\n if (issueIds.length === 0) {\n return { updated: 0 }\n }\n const uniqueIssueIds = [...new Set(issueIds)]\n const updates: Partial<typeof issues.$inferInsert> = { updatedAt: currentUnixSeconds() }\n if ('statusId' in input) {\n updates.statusId = input.statusId ?? null\n }\n if (input.priority !== undefined) {\n updates.priority = input.priority\n }\n if (input.labels !== undefined) {\n updates.labels = JSON.stringify(input.labels)\n }\n if ('milestoneId' in input) {\n updates.milestoneId = input.milestoneId ?? null\n }\n if ('assigneeKind' in input) {\n updates.assigneeKind = input.assigneeKind ?? null\n }\n if ('assigneeId' in input) {\n updates.assigneeId = input.assigneeId ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n const cards = await tx.issues.listByIds(uniqueIssueIds)\n for (const card of cards) {\n if (input.statusId != null && !await tx.statuses.findInWorkspace(card.workspaceId, input.statusId)) {\n throw new BoardError('issue_status_not_found', { workspaceId: card.workspaceId, statusId: input.statusId })\n }\n if ('milestoneId' in input) {\n await requireMilestoneInWorkspace(tx, card.workspaceId, input.milestoneId ?? null)\n }\n }\n const unmetRules = input.statusId\n ? await enforceColumnEntryForCards(tx, uniqueIssueIds, input.statusId, actor)\n : []\n for (const card of cards) {\n await recordFieldChanges(tx, card, updates, actor)\n }\n\n const updated = await tx.issues.updateMany(uniqueIssueIds, updates)\n return unmetRules.length > 0 ? { updated, unmetRules } : { updated }\n }, scope)\n}\n\n/**\n * Give the named cards of one workspace their positions in the order named.\n *\n * Positions are spaced by a gap rather than packed, so a card dropped between\n * two others later can take a midpoint without moving its neighbours. The whole\n * order is one write — a drag that half-applied would leave a board no client\n * could reconcile — and a card that is gone or belongs to another workspace is\n * skipped rather than failing the rest of the drop.\n */\nexport async function reorderCards<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n orderedIds: string[],\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n for (const [index, issueId] of orderedIds.entries()) {\n const card = await tx.issues.findById(issueId)\n if (!card || card.workspaceId !== workspaceId) {\n continue\n }\n await tx.issues.update(issueId, { order: (index + 1) * CARD_ORDER_GAP, updatedAt: currentUnixSeconds() })\n }\n }, scope)\n}\n","import { z } from 'zod'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue } from '../sqlite/schema'\nimport { recordFieldChanges } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { requireCard } from './types'\n\nconst ContextRefsJsonSchema = z.string()\n .transform(raw => JSON.parse(raw))\n .pipe(z.array(z.string()))\n\n/** Add a context ref at the end of the card's list, recording the change in its history. */\nexport async function addContextRef<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n ref: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const refs = ContextRefsJsonSchema.parse(card.contextRefs)\n refs.push(ref)\n return await replaceContextRefs(tx, card, refs, actor)\n }, scope)\n}\n\n/** Remove the card's context ref at `index`, or say there is none there. */\nexport async function removeContextRef<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n index: number,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const refs = ContextRefsJsonSchema.parse(card.contextRefs)\n if (index < 0 || index >= refs.length) {\n throw new BoardError('issue_context_ref_invalid_index', { issueId, index })\n }\n refs.splice(index, 1)\n return await replaceContextRefs(tx, card, refs, actor)\n }, scope)\n}\n\nasync function replaceContextRefs(store: BoardStore, card: Issue, refs: string[], actor: BoardActor): Promise<Issue> {\n const updates = { contextRefs: JSON.stringify(refs), updatedAt: currentUnixSeconds() }\n await recordFieldChanges(store, card, updates, actor)\n await store.contextRefs.replace(card.id, updates.contextRefs, updates.updatedAt)\n return await requireCard(store, card.id)\n}\n","import type { BoardStore } from '../board-store'\nimport { nextIssueNumber, nextIssueOrder } from '../domain/numbering'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { issues, IssueStatus } from '../sqlite/schema'\nimport { CARD_ORDER_GAP } from './card-batch'\nimport { numberTakenByAnother, recordFieldChanges } from './cards'\nimport { runBoardWrite } from './change-seq'\nimport { BOARD_ACTOR } from './types'\n\n/** Which workspace's cards move where, and how their columns and milestones are matched up. */\nexport interface MigrateCardsInput {\n sourceId: string\n targetId: string\n /**\n * Both workspaces' columns, in board order. The host reads them from each\n * workspace's own board, which is not always the board the cards are moved on:\n * a preview of a move across two boards reads the target's columns from the\n * target while the cards are still on the source.\n */\n sourceStatuses: IssueStatus[]\n targetStatuses: IssueStatus[]\n /** A source column name → the target column name it becomes, when the names differ. */\n statusMappings?: Record<string, string>\n /** A source milestone title → the target milestone title it becomes, when the titles differ. */\n milestoneMappings?: Record<string, string>\n /** Count what would happen, and write nothing. */\n dryRun?: boolean\n}\n\n/** What a move of every card from one workspace to another did — or, for a dry run, would do. */\nexport interface MigrateCardsResult {\n processed: number\n updated: number\n numbersReassigned: number\n statusesMapped: { from: string, to: string }[]\n milestonesMapped: { from: string, to: string | null }[]\n parentIssuesCleared: number\n}\n\n/**\n * Move every card of one workspace to another on this board, in one write.\n *\n * Each source column maps to the target column of the same name (or the name\n * `statusMappings` gives it), and to the target's first column when there is\n * none; each milestone maps by title the same way, and to no milestone when\n * there is none. A card whose number the target already uses takes the next\n * free one, the moved cards go to the end of the target board in their own\n * order, and a parent that is in neither workspace is let go of. Every changed\n * field is recorded in the card's history under the board's own actor.\n *\n * One write, because a half-moved board leaves cards split across both\n * workspaces with renumbering already spent on some of them.\n */\nexport async function migrateCards(store: BoardStore, input: MigrateCardsInput): Promise<MigrateCardsResult> {\n const { sourceId, targetId } = input\n const targetStatusByName = new Map(input.targetStatuses.map(status => [normalizeStatusName(status.name), status]))\n const defaultTargetStatusId = input.targetStatuses[0]?.id ?? null\n\n const statusIdMap = new Map<string, string | null>()\n const statusesMapped: MigrateCardsResult['statusesMapped'] = []\n for (const source of input.sourceStatuses) {\n const target = targetStatusByName.get(normalizeStatusName(input.statusMappings?.[source.name] ?? source.name))\n statusIdMap.set(source.id, target ? target.id : defaultTargetStatusId)\n statusesMapped.push({ from: source.name, to: target ? target.name : input.targetStatuses[0]?.name ?? '(none)' })\n }\n\n const targetMilestoneByTitle = new Map((await store.milestones.listByWorkspace(targetId))\n .map(milestone => [milestone.title.trim().toLowerCase(), milestone]))\n const milestoneIdMap = new Map<string, string | null>()\n const milestonesMapped: MigrateCardsResult['milestonesMapped'] = []\n for (const source of await store.milestones.listByWorkspace(sourceId)) {\n const target = targetMilestoneByTitle.get((input.milestoneMappings?.[source.title] ?? source.title).trim().toLowerCase())\n milestoneIdMap.set(source.id, target ? target.id : null)\n milestonesMapped.push({ from: source.title, to: target ? target.title : null })\n }\n\n const cards = await store.issues.listByWorkspace(sourceId)\n let numbersReassigned = 0\n let parentIssuesCleared = 0\n\n if (input.dryRun) {\n for (const card of cards) {\n if (await numberTakenByAnother(store, targetId, card)) {\n numbersReassigned++\n }\n if (card.parentIssueId) {\n const parent = await store.issues.findById(card.parentIssueId)\n if (parent && parent.workspaceId !== sourceId && parent.workspaceId !== targetId) {\n parentIssuesCleared++\n }\n }\n }\n return { processed: cards.length, updated: 0, numbersReassigned, statusesMapped, milestonesMapped, parentIssuesCleared }\n }\n\n await runBoardWrite(store, async ({ tx }) => {\n let order = await nextIssueOrder(targetId, tx)\n for (const card of cards) {\n const updates: Partial<typeof issues.$inferInsert> = {\n workspaceId: targetId,\n updatedAt: currentUnixSeconds(),\n statusId: card.statusId ? (statusIdMap.get(card.statusId) ?? defaultTargetStatusId) : defaultTargetStatusId,\n }\n if (card.milestoneId) {\n updates.milestoneId = milestoneIdMap.get(card.milestoneId) ?? null\n }\n if (await numberTakenByAnother(tx, targetId, card)) {\n updates.number = await nextIssueNumber(targetId, tx)\n numbersReassigned++\n }\n updates.order = order\n order += CARD_ORDER_GAP\n // A parent moving with the card, or already in the target, stays its parent.\n if (card.parentIssueId) {\n const parent = await tx.issues.findById(card.parentIssueId)\n if (parent && parent.workspaceId !== sourceId && parent.workspaceId !== targetId) {\n updates.parentIssueId = null\n parentIssuesCleared++\n }\n }\n\n await recordFieldChanges(tx, card, updates, BOARD_ACTOR)\n await tx.issues.update(card.id, updates)\n }\n })\n\n return { processed: cards.length, updated: cards.length, numbersReassigned, statusesMapped, milestonesMapped, parentIssuesCleared }\n}\n","/**\n * The sentences that tell an agent how to work this board, written once.\n *\n * Four surfaces say them: the text `kanbo prime` returns (`BOARD_RULES`), the\n * block `kanbo init` writes into a project's `CLAUDE.md` / `AGENTS.md`, the\n * `instructions` the `kanbo mcp` server sends when a client connects, and the\n * `kanbo_card_create` tool's description. Each surface adds its own way of\n * doing the thing — a command, a tool name — but the rule itself is read from\n * here, so the four cannot come to say different things.\n *\n * No sentence names a tool or a command: the same text reaches an agent holding\n * MCP tools, one with a command line, and one talking to a server over HTTP.\n */\n\nexport const MOVE_CARD_RULE = 'You move the card between columns yourself, at the moment its real state changes. The board never moves a card for you.'\n\nexport const STATUS_LINE_RULE = 'Write a status line at every step, including before and after anything long-running. One sentence, present tense, about what is happening right now.'\n\nexport const WAIT_FOR_PERSON_RULE = 'When you need a person, mark the card as waiting for approval and end your turn. You will be told when the answer comes.'\n\nexport const PERSON_ONLY_RULE = 'Only a person approves a card, and only a person takes one out of waiting. Never do either yourself.'\n\nexport const EXECUTION_MODE_RULE = 'Each card says where it runs: `worktree` means a separate copy of the project, `main` means the project folder itself. Work in the mode the card names.'\n\n/** When to split a card at all — said on its own where the surface names the parent its own way. */\nexport const SUBTASK_WHEN_SENTENCE = 'Create subtasks only when the person asks for them, or when a task has parts that can be done and checked separately (different stages, owners or pull requests); do not split small work you will finish in one go.'\n\n/** How to split one, with the way this surface names the parent card. */\nexport function subtaskSplitSentence(parentReference: string): string {\n return `When you do split a card, make the parts subtasks of it (${parentReference}), not separate top-level cards.`\n}\n\nexport const SUBTASK_RULE = `${SUBTASK_WHEN_SENTENCE} ${subtaskSplitSentence('the parent card\\'s id')}`\n\nexport const PULL_REQUEST_RULE = 'Opened a pull request? Link it to the card. Its state and CI checks then reach the card as comments on their own — nothing else tells the board a pull request exists.'\n\nexport const OWN_SESSION_RULE = 'Started the work yourself, not launched by an app that tracks the run for you? Say so when you start the run — `claude:<session id>` from Claude Code, `codex:<session id>` from Codex (the session id Codex prints) — so your own log of it can be found later.'\n\n/** Said by the `kanbo init` block and the MCP instructions, which name the comment command or tool beside it. */\nexport const COMMENT_RULE = 'Findings, results, decisions and questions go on the card as comments.'\n\nexport const BOARD_TOOLS_RULE = 'Read the column descriptions above before moving a card, and do all of this through the board tools or commands you have — a statement in your reply changes nothing.'\n\n/**\n * The rules of this board, as `kanbo prime` says them under its column list.\n *\n * They are about the board and nothing else. The board does not hand out a role, a\n * method or a pipeline — that belongs to the instructions the person wrote for\n * their own agent — so the list stops at what the board itself guarantees and\n * expects.\n *\n * Also the source `buildCapabilitiesManifest` reads its `rules` from — one list,\n * read by the prompt an agent sees and by the manifest an orchestrator reads\n * about the board.\n */\nexport const BOARD_RULES = [\n MOVE_CARD_RULE,\n STATUS_LINE_RULE,\n WAIT_FOR_PERSON_RULE,\n PERSON_ONLY_RULE,\n EXECUTION_MODE_RULE,\n SUBTASK_RULE,\n PULL_REQUEST_RULE,\n OWN_SESSION_RULE,\n BOARD_TOOLS_RULE,\n]\n","import type { BoardStore } from '../board-store'\nimport { ENTRY_RULE_DESCRIPTIONS, readEntryRules } from '../domain/entry-rules'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { IssueStatus } from '../sqlite/schema'\nimport { BOARD_RULES } from './agent-rules'\nimport { listColumns } from './columns'\n\n/**\n * What the board tells an agent about itself: the columns it has, what each one\n * means, and how a card is expected to travel across them.\n *\n * The text is the same whether it reaches the agent in a prompt an app builds\n * or through a tool the agent called on its own.\n */\nexport async function buildPrimeText(store: BoardStore, workspaceId: string): Promise<string> {\n const columns = await listColumns(store, workspaceId)\n const ruled = columns.some(column => readEntryRules(column).length > 0)\n const lines = [\n 'Columns of this board, in board order:',\n '',\n ...(columns.length > 0 ? columns.map(describeColumnLine) : ['- (this board has no columns yet)']),\n ...(ruled ? ['', ENTRY_RULES_NOTE] : []),\n '',\n 'How this board works:',\n '',\n ...BOARD_RULES.map(rule => `- ${rule}`),\n ]\n return lines.join('\\n')\n}\n\n/**\n * Said under the columns only when one of them has entry rules, so a board\n * without any reads exactly as it did before they existed.\n */\nconst ENTRY_RULES_NOTE = 'A column that lists requirements refuses a card that does not meet them yet, with the list of what is missing: do that work first, then move the card. A person may still move it.'\n\nfunction describeColumnLine(column: IssueStatus): string {\n const slug = normalizeStatusName(column.name)\n const rules = readEntryRules(column)\n const requires = rules.length > 0\n ? ` Requires: ${rules.map(rule => `${rule} (${ENTRY_RULE_DESCRIPTIONS[rule]})`).join('; ')}.`\n : ''\n return column.description\n ? `- ${column.name} (\\`${slug}\\`) — ${column.description}${requires}`\n : `- ${column.name} (\\`${slug}\\`)${requires ? ` —${requires}` : ''}`\n}\n","import { BoardError } from './errors'\n\n/** A GitHub pull request, as a card names it. */\nexport interface PullRequestRef {\n owner: string\n repo: string\n number: number\n /** The canonical `https://github.com/<owner>/<repo>/pull/<number>`, whatever form it was given in. */\n url: string\n}\n\n/**\n * The largest pull-request number a board can hold — `number` is a 32-bit\n * `integer` in the Postgres dialect.\n */\nconst MAX_PULL_REQUEST_NUMBER = 2_147_483_647\n\n/**\n * A GitHub owner (user or organisation) and a repository name, as GitHub allows\n * them; both patterns are read case-insensitively. An owner may carry `_`: an\n * Enterprise Managed User is `name_shortcode`.\n */\nconst OWNER = '[a-z0-9](?:[\\\\w-]*[a-z0-9])?'\nconst REPO = '[\\\\w.-]+'\n\n/** `https://github.com/<owner>/<repo>/pull/<n>`, with anything GitHub hangs off it — `/files`, a query, an anchor. */\nconst PULL_REQUEST_URL = new RegExp(`^https://(?:www\\\\.)?github\\\\.com/(${OWNER})/(${REPO})/pull/(\\\\d+)(?:[/?#].*)?$`, 'i')\n\n/** `<owner>/<repo>#<n>`, the short form GitHub itself writes. */\nconst PULL_REQUEST_SHORT_REF = new RegExp(`^(${OWNER})/(${REPO})#(\\\\d+)$`, 'i')\n\n/**\n * Read a pull request out of what a person or an agent typed: a GitHub pull\n * request URL or `owner/repo#number`. Anything else — an issue URL, a commit, a\n * host that is not GitHub, a bare number with no repository — is refused with\n * `board_pull_request_invalid` rather than guessed at, because a link the\n * watcher cannot resolve is a card that never hears about its pull request.\n *\n * GitHub owner and repository names are case-insensitive, so both are\n * lower-cased before they become a stored link or a dedupe key (ruling 4-6) —\n * `Acme/Repo#7` and `acme/repo#7` are the same pull request and must never\n * become two links or two \"opened\" lines.\n */\nexport function parsePullRequestRef(input: string): PullRequestRef {\n const value = input.trim()\n const match = PULL_REQUEST_URL.exec(value) ?? PULL_REQUEST_SHORT_REF.exec(value)\n const number = match ? Number(match[3]) : Number.NaN\n if (!match || !Number.isSafeInteger(number) || number < 1 || number > MAX_PULL_REQUEST_NUMBER || match[2] === '.' || match[2] === '..') {\n throw new BoardError('board_pull_request_invalid', { value: input })\n }\n const owner = match[1].toLowerCase()\n const repo = match[2].toLowerCase()\n return { owner, repo, number, url: `https://github.com/${owner}/${repo}/pull/${number}` }\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { pullRequestFactKeys } from '../domain/pull-request-fact-keys'\nimport { parsePullRequestRef } from '../domain/pull-request-ref'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { IssuePullRequest } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR, requireCard, toStoredActorKind } from './types'\n\n/**\n * Name a pull request on a card (ruling 4-1).\n *\n * `urlOrRef` is a GitHub pull request URL or `owner/repo#number`, and anything\n * else is refused before the board is touched. Naming one the card already\n * names hands back the standing link and writes nothing — so the board version\n * moves only when a link was really made. No comment is written: what the pull\n * request *is* — opened, merged, green — is a fact the watcher reads off GitHub\n * and writes itself.\n */\nexport async function linkPullRequest<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n urlOrRef: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssuePullRequest> {\n const ref = parsePullRequestRef(urlOrRef)\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const standing = (await tx.pullRequests.listByIssue(issueId))\n .find(link => link.owner === ref.owner && link.repo === ref.repo && link.number === ref.number)\n if (standing) {\n return standing\n }\n return await tx.pullRequests.link({\n id: randomUUID(),\n issueId,\n owner: ref.owner,\n repo: ref.repo,\n number: ref.number,\n url: ref.url,\n createdByKind: toStoredActorKind(actor),\n createdById: actor.id ?? null,\n createdAt: currentUnixSeconds(),\n })\n }, scope)\n}\n\n/**\n * Drop one pull request link off a card. A link the card does not name is\n * `board_pull_request_not_found`.\n *\n * Who may drop it depends on who linked it. A person, and the board writing\n * for itself (`BOARD_ACTOR` — the watcher's own \"PR is gone\" unlink), may drop\n * any link. Every other actor — an agent, a provider target, or anything\n * outside with an identity of its own — may only drop a link its own\n * stored kind and id created; naming another actor's link is\n * `board_pull_request_not_yours`, the same 403 an agent gets for a card it may\n * not approve.\n */\nexport async function unlinkPullRequest<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n linkId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssuePullRequest> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const named = (await tx.pullRequests.listByIssue(issueId)).find(link => link.id === linkId)\n if (!named) {\n throw new BoardError('board_pull_request_not_found', { issueId, linkId })\n }\n if (!canUnlinkPullRequest(actor, named)) {\n throw new BoardError('board_pull_request_not_yours', { issueId, linkId })\n }\n const removed = await tx.pullRequests.unlink(issueId, linkId)\n if (!removed) {\n throw new BoardError('board_pull_request_not_found', { issueId, linkId })\n }\n return removed\n }, scope)\n}\n\n/** Whether `actor` may drop `link` — see `unlinkPullRequest`. */\nfunction canUnlinkPullRequest(actor: BoardActor, link: IssuePullRequest): boolean {\n if (actor.kind === 'user') {\n return true\n }\n if (actor.kind === BOARD_ACTOR.kind && actor.id === BOARD_ACTOR.id) {\n return true\n }\n return link.createdByKind === toStoredActorKind(actor) && link.createdById === actor.id\n}\n\n/** The pull requests a card names, in the order they were linked. */\nexport async function listPullRequests(store: BoardStore, issueId: string): Promise<IssuePullRequest[]> {\n await requireCard(store, issueId)\n return await store.pullRequests.listByIssue(issueId)\n}\n\n/**\n * A pull request a card names, with where it stands as the card's own facts\n * say (ruling 4-7): `merged` or `closed` once the watcher has written that,\n * `open` once it has written «opened», `unknown` before it has read GitHub at\n * all; `ci` is the newest CI verdict written for it, whatever head it was for.\n */\nexport type BoardPullRequestStanding = IssuePullRequest & {\n state: 'open' | 'merged' | 'closed' | 'unknown'\n ci: 'green' | 'red' | null\n}\n\n/**\n * The pull requests a card names, in the order they were linked, each with\n * where it stands — read off the dedupe keys of the facts the watcher wrote on\n * the card, not the text of its comments, and nothing asked of GitHub.\n */\nexport async function listPullRequestStandings(store: BoardStore, issueId: string): Promise<BoardPullRequestStanding[]> {\n const listed: BoardPullRequestStanding[] = []\n for (const link of await listPullRequests(store, issueId)) {\n listed.push({ ...link, ...await readPullRequestStanding(store, link) })\n }\n return listed\n}\n\n/** Three indexed reads for the state and one prefix listing for the newest CI verdict, whose key ends in `:green` or `:red`. */\nasync function readPullRequestStanding(\n store: BoardStore,\n link: IssuePullRequest,\n): Promise<Pick<BoardPullRequestStanding, 'state' | 'ci'>> {\n const keys = pullRequestFactKeys(link)\n const carries = async (key: string): Promise<boolean> => (await store.comments.findByDedupeKey(link.issueId, key)) !== null\n const state = await carries(keys.merged)\n ? 'merged'\n : await carries(keys.closed) ? 'closed' : await carries(keys.opened) ? 'open' : 'unknown'\n const newest = (await store.comments.listByDedupeKeyPrefix(link.issueId, keys.ciPrefix)).at(-1)?.dedupeKey\n const ci = newest?.endsWith(':green') ? 'green' : newest?.endsWith(':red') ? 'red' : null\n return { state, ci }\n}\n","import type { BoardStore } from '../board-store'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { Issue } from '../sqlite/schema'\nimport { listColumns } from './columns'\nimport { readBoardProjectionForIssues } from './runs'\n\n/** The column an agent takes its next card from. */\nconst READY_COLUMN_SLUG = 'to_do'\n\n/**\n * The cards an agent may pick up right now: spelled out, nobody working on them,\n * nobody's turn but the agent's.\n *\n * \"Ready\" is deliberately narrow. A card in Backlog is an idea, a card with a\n * run is taken, and a card waiting for a person belongs to that person until\n * they answer. What is left is the queue — in board order, so the card someone\n * dragged to the top is the one an agent asking for work gets.\n */\nexport async function listReady(\n store: BoardStore,\n input: { workspaceId: string, limit?: number },\n): Promise<Issue[]> {\n const column = (await listColumns(store, input.workspaceId))\n .find(candidate => normalizeStatusName(candidate.name) === READY_COLUMN_SLUG)\n if (!column) {\n return []\n }\n\n const candidates = (await store.issues.listInBoardOrder(input.workspaceId))\n .filter(card => card.statusId === column.id && card.waitingFor === null)\n const running = new Set((await readBoardProjectionForIssues(store, candidates.map(card => card.id)))\n .filter(projection => projection.activeRun !== null)\n .map(projection => projection.issueId))\n\n const ready = candidates.filter(card => !running.has(card.id))\n return input.limit === undefined ? ready : ready.slice(0, input.limit)\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueRelation } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { requireCard } from './types'\n\n/** The card on the other end of an edge, as much of it as a relation row shows. */\nexport type IssueRelationCounterpart = Pick<Issue, 'id' | 'workspaceId' | 'number' | 'title' | 'statusId' | 'priority'>\n\n/** An edge as seen from one of the two cards it touches. */\nexport type IssueRelationView = IssueRelation & {\n /** Orientation of the edge relative to the issue the relations were listed for. */\n direction: 'outgoing' | 'incoming'\n /** The issue on the other end of the edge, resolved across workspaces. */\n counterpart: IssueRelationCounterpart | null\n}\n\n/** A new edge between two cards. */\nexport interface CreateRelationInput {\n sourceIssueId: string\n targetIssueId: string\n type: IssueRelation['type']\n}\n\n/**\n * Every edge touching the card, in either direction, each with the card on its\n * other end. A counterpart the board no longer holds comes back as `null`\n * rather than dropping the edge.\n */\nexport async function listRelations(store: BoardStore, issueId: string): Promise<IssueRelationView[]> {\n await requireCard(store, issueId)\n const rows = await store.relations.listByIssue(issueId)\n const counterpartIds = [...new Set(rows.map(row => row.sourceIssueId === issueId ? row.targetIssueId : row.sourceIssueId))]\n const counterpartRows = counterpartIds.length > 0 ? await store.issues.listByIds(counterpartIds) : []\n const counterpartsById = new Map<string, IssueRelationCounterpart>(counterpartRows.map(row => [row.id, {\n id: row.id,\n workspaceId: row.workspaceId,\n number: row.number,\n title: row.title,\n statusId: row.statusId,\n priority: row.priority,\n }]))\n\n return rows.map((row) => {\n const outgoing = row.sourceIssueId === issueId\n return {\n ...row,\n direction: outgoing ? 'outgoing' : 'incoming',\n counterpart: counterpartsById.get(outgoing ? row.targetIssueId : row.sourceIssueId) ?? null,\n }\n })\n}\n\n/**\n * Draw an edge between two cards of this board.\n *\n * A card is never related to itself, and both cards must be on the board. The\n * same edge drawn twice is one edge: the standing row comes back and nothing is\n * written — `relates_to` is symmetric, so the reversed pair is the same edge,\n * while a directed type keeps its orientation.\n */\nexport async function createRelation<TStore extends BoardStore>(\n store: TStore,\n input: CreateRelationInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRelation> {\n if (input.sourceIssueId === input.targetIssueId) {\n throw new BoardError('issue_relation_self_reference', { issueId: input.sourceIssueId })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.sourceIssueId)\n await requireCard(tx, input.targetIssueId)\n const duplicate = await tx.relations.findMatching(input)\n if (duplicate) {\n return duplicate\n }\n return await tx.relations.create({\n id: randomUUID(),\n sourceIssueId: input.sourceIssueId,\n targetIssueId: input.targetIssueId,\n type: input.type,\n createdAt: currentUnixSeconds(),\n })\n }, scope)\n}\n\n/** Remove one edge, or say the board holds no such edge. */\nexport async function deleteRelation<TStore extends BoardStore>(\n store: TStore,\n relationId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n if (!await tx.relations.findById(relationId)) {\n throw new BoardError('issue_relation_not_found', { relationId })\n }\n await tx.relations.delete(relationId)\n }, scope)\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds, utcDayEnd, utcDayStart } from '../domain/time'\nimport type { IssueMilestone, IssueStatus } from '../sqlite/schema'\nimport { updateCard } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\n\n/**\n * A milestone read as a sprint (ruling 5x-3): the row itself, how many of its\n * cards are still open and how many are done, and whether it is the sprint\n * running right now.\n *\n * `done` counts the cards in a `completed` or `canceled` column — the ones\n * closing the sprint leaves where they are; `open` counts every other card,\n * a card in no column at all included — the ones closing it carries on.\n */\nexport interface BoardSprint extends IssueMilestone {\n cards: { open: number, done: number }\n current: boolean\n}\n\n/**\n * What a milestone is created with; `startDate` and `dueDate` are unix seconds,\n * kept as the first and the last second of their UTC days.\n */\nexport interface CreateMilestoneInput {\n workspaceId: string\n title: string\n description?: string | null\n startDate?: number | null\n dueDate?: number | null\n status?: IssueMilestone['status']\n}\n\n/** What of a milestone can change. */\nexport type UpdateMilestoneInput = Partial<Omit<CreateMilestoneInput, 'workspaceId'>>\n\n/** What closing a sprint did: how many unfinished cards it moved on, and that the sprint is closed. */\nexport interface CloseSprintResult {\n carried: number\n closed: true\n}\n\nconst FINISHED_CATEGORIES = new Set<IssueStatus['category']>(['completed', 'canceled'])\n\n/**\n * A milestone's dates as the calendar days they fall on (ruling 5x-6): the\n * start at the first second of its UTC day, the due date at the last. Only the\n * dates the input names are touched.\n */\nfunction toCalendarDays<T extends { startDate?: number | null, dueDate?: number | null }>(input: T): T {\n const days = { ...input }\n if (days.startDate != null) {\n days.startDate = utcDayStart(days.startDate)\n }\n if (days.dueDate != null) {\n days.dueDate = utcDayEnd(days.dueDate)\n }\n return days\n}\n\n/** A sprint that starts after it ends is not a sprint. */\nfunction assertDatesInOrder(startDate: number | null | undefined, dueDate: number | null | undefined): void {\n if (startDate != null && dueDate != null && startDate > dueDate) {\n throw new BoardError('board_sprint_dates_invalid', { startDate, dueDate })\n }\n}\n\n/** Whether a milestone is the sprint running at `now`: open, started, not yet due. */\nfunction isRunningAt(milestone: IssueMilestone, now: number): boolean {\n return milestone.status === 'open'\n && milestone.startDate !== null\n && milestone.dueDate !== null\n && milestone.startDate <= now\n && now <= milestone.dueDate\n}\n\n/**\n * Create a milestone — a sprint when it is given a start and a due date.\n * Milestones carry no history, so there is no actor to record.\n */\nexport async function createMilestone<TStore extends BoardStore>(\n store: TStore,\n rawInput: CreateMilestoneInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueMilestone> {\n const input = toCalendarDays(rawInput)\n assertDatesInOrder(input.startDate, input.dueDate)\n const now = currentUnixSeconds()\n return await runBoardWrite(store, async ({ tx }) => await tx.milestones.create({\n id: randomUUID(),\n workspaceId: input.workspaceId,\n title: input.title,\n description: input.description ?? null,\n startDate: input.startDate ?? null,\n dueDate: input.dueDate ?? null,\n status: input.status ?? 'open',\n createdAt: now,\n updatedAt: now,\n }), scope)\n}\n\n/** Change a milestone's title, description, dates or status. */\nexport async function updateMilestone<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n rawInput: UpdateMilestoneInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueMilestone> {\n const input = toCalendarDays(rawInput)\n return await runBoardWrite(store, async ({ tx }) => {\n const milestone = await requireMilestone(tx, milestoneId)\n assertDatesInOrder(\n 'startDate' in input ? input.startDate : milestone.startDate,\n 'dueDate' in input ? input.dueDate : milestone.dueDate,\n )\n const updates: Partial<IssueMilestone> = { updatedAt: currentUnixSeconds() }\n if (input.title !== undefined) {\n updates.title = input.title\n }\n if ('description' in input) {\n updates.description = input.description ?? null\n }\n if ('startDate' in input) {\n updates.startDate = input.startDate ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n if (input.status !== undefined) {\n updates.status = input.status\n }\n await tx.milestones.update(milestoneId, updates)\n return await requireMilestone(tx, milestoneId)\n }, scope)\n}\n\n/** Milestones, newest first — one workspace's, or with `null` every workspace's on the board. */\nexport async function listMilestones(store: BoardStore, workspaceId: string | null): Promise<IssueMilestone[]> {\n return await store.milestones.listNewestFirst(workspaceId)\n}\n\n/**\n * Delete a milestone. Its cards stay where they are and simply belong to no\n * milestone any more — the reference is cleared in the same write, so no card\n * is ever left naming a milestone that is gone.\n */\nexport async function deleteMilestone<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n await requireMilestone(tx, milestoneId)\n await tx.issues.clearMilestoneReferences(milestoneId, currentUnixSeconds())\n await tx.milestones.delete(milestoneId)\n }, scope)\n}\n\n/**\n * Every milestone of a workspace as a sprint, the one running at `now` marked\n * `current`. Ordered by start date, the milestones without one last, then by\n * due date and creation.\n */\nexport async function listSprints(\n store: BoardStore,\n workspaceId: string,\n now: number = currentUnixSeconds(),\n): Promise<BoardSprint[]> {\n const [milestones, cards, columns] = await Promise.all([\n store.milestones.listByWorkspace(workspaceId),\n store.issues.listByWorkspace(workspaceId),\n store.statuses.listByWorkspace(workspaceId),\n ])\n const finishedColumns = new Set(columns\n .filter(column => FINISHED_CATEGORIES.has(column.category))\n .map(column => column.id))\n const current = pickCurrent(milestones, now)\n\n return milestones\n .toSorted((a, b) =>\n (a.startDate ?? Number.MAX_SAFE_INTEGER) - (b.startDate ?? Number.MAX_SAFE_INTEGER)\n || (a.dueDate ?? Number.MAX_SAFE_INTEGER) - (b.dueDate ?? Number.MAX_SAFE_INTEGER)\n || a.createdAt - b.createdAt)\n .map((milestone) => {\n const own = cards.filter(card => card.milestoneId === milestone.id)\n const done = own.filter(card => card.statusId !== null && finishedColumns.has(card.statusId)).length\n return { ...milestone, cards: { open: own.length - done, done }, current: milestone.id === current?.id }\n })\n}\n\n/**\n * The sprint running at `now`: an open milestone with `start_date <= now <=\n * due_date`. When several overlap, the one that started last; when none does,\n * `null`.\n */\nexport async function currentSprint(\n store: BoardStore,\n workspaceId: string,\n now: number = currentUnixSeconds(),\n): Promise<BoardSprint | null> {\n return (await listSprints(store, workspaceId, now)).find(sprint => sprint.current) ?? null\n}\n\nfunction pickCurrent(milestones: IssueMilestone[], now: number): IssueMilestone | null {\n return milestones\n .filter(milestone => isRunningAt(milestone, now))\n .reduce<IssueMilestone | null>((latest, milestone) =>\n latest === null || (milestone.startDate ?? 0) > (latest.startDate ?? 0) ? milestone : latest, null)\n}\n\n/**\n * Close a sprint, carrying its unfinished cards on (rulings 5x-3, 5x-4).\n *\n * Only a person closes a sprint: deciding what did not get done is a review of\n * the work, and an agent is one of the parties being reviewed. Every card of the\n * sprint whose column is not `completed` or `canceled` moves to `carryTo` — an\n * open milestone of the same workspace — through `updateCard`, so each one gets\n * the same `milestoneId` history entry any other edit would. Without `carryTo`\n * those cards leave the milestone instead. The finished cards stay where they\n * are, and the sprint is closed, all in one write.\n *\n * Closing a sprint that is already closed does nothing and says so with\n * `carried: 0` — the board version does not move.\n */\nexport async function closeSprint<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n input: { carryTo?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<CloseSprintResult> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_sprint_close_requires_user', { milestoneId, actorKind: actor.kind })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n const sprint = await requireMilestone(tx, milestoneId)\n if (sprint.status === 'closed') {\n return { carried: 0, closed: true }\n }\n const carryTo = input.carryTo ?? null\n if (carryTo !== null) {\n const target = await tx.milestones.findInWorkspace(sprint.workspaceId, carryTo)\n if (!target || target.status !== 'open' || target.id === sprint.id) {\n throw new BoardError('board_sprint_carry_invalid', { milestoneId, carryTo })\n }\n }\n\n const finishedColumns = new Set((await tx.statuses.listByWorkspace(sprint.workspaceId))\n .filter(column => FINISHED_CATEGORIES.has(column.category))\n .map(column => column.id))\n const unfinished = (await tx.issues.listByWorkspace(sprint.workspaceId))\n .filter(card => card.milestoneId === sprint.id && !(card.statusId !== null && finishedColumns.has(card.statusId)))\n for (const card of unfinished) {\n await updateCard(tx, card.id, { milestoneId: carryTo }, actor, { tx })\n }\n await tx.milestones.update(sprint.id, { status: 'closed', updatedAt: currentUnixSeconds() })\n return { carried: unfinished.length, closed: true }\n }, scope)\n}\n\nasync function requireMilestone(store: BoardStore, milestoneId: string): Promise<IssueMilestone> {\n const milestone = await store.milestones.findById(milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { milestoneId })\n }\n return milestone\n}\n","import type { BoardStore } from '../board-store'\nimport type { IssueActivityResolver } from './activity'\nimport { listActivity, listFieldChanges } from './activity'\nimport { readApproval } from './approval'\nimport type { BulkUpdateCardsInput } from './card-batch'\nimport { bulkUpdateCards, reorderCards } from './card-batch'\nimport type { AddCommentInput, CreateCardInput, CreateCommentOnceInput, UpdateCardInput } from './cards'\nimport {\n addComment,\n approve,\n assertReturnable,\n createCard,\n createCommentOnce,\n createSubCard,\n deleteCard,\n deleteComment,\n listComments,\n moveCard,\n returnCard,\n searchCards,\n setStatusLine,\n updateCard,\n waitApproval,\n} from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport type { BoardColumnInput, BoardColumnPatch } from './columns'\nimport {\n addStandardColumns,\n createColumn,\n deleteColumn,\n describeColumn,\n ensureDefaultColumns,\n findColumn,\n listColumns,\n reorderColumns,\n requireColumn,\n setColumnEntryRules,\n updateColumn,\n} from './columns'\nimport { addContextRef, removeContextRef } from './context-refs'\nimport { enforceColumnEntryForCards } from './entry-rules'\nimport type { MigrateCardsInput } from './migrate'\nimport { migrateCards } from './migrate'\nimport { buildPrimeText } from './prime'\nimport { linkPullRequest, listPullRequests, listPullRequestStandings, unlinkPullRequest } from './pull-requests'\nimport { listReady } from './ready'\nimport type { CreateRelationInput } from './relations'\nimport { createRelation, deleteRelation, listRelations } from './relations'\nimport type { AttachRunExecutionInput, FinishRunInput, StartRunInput } from './runs'\nimport {\n attachRunExecution,\n clearRunSessionRef,\n findRunByChatSessionId,\n finishRun,\n listRuns,\n readBoardProjection,\n readBoardProjectionForIssue,\n readBoardProjectionForIssues,\n recordRunSessionRef,\n startRun,\n stopRunningRuns,\n} from './runs'\nimport type { CreateMilestoneInput, UpdateMilestoneInput } from './sprints'\nimport { closeSprint, createMilestone, currentSprint, deleteMilestone, listMilestones, listSprints, updateMilestone } from './sprints'\nimport type { BoardActor } from './types'\n\n/**\n * Everything the board can be asked to do, bound to one store.\n *\n * This is the layer the semantics live in — numbering, columns, the status line,\n * approval, runs — and there is exactly one of it. `kanbo serve`, an app's server, the\n * command-line tool and the MCP server all call these operations rather than\n * writing rows of their own, which is why a card launched from a terminal and a\n * card launched from the app end up telling the same story.\n *\n * Every write takes an actor, and every write may take a `scope`: passing the\n * scope of a write already in progress joins it, so a caller composing several\n * operations gets one transaction and one board version bump.\n */\nexport function createBoardOps<TStore extends BoardStore>(store: TStore) {\n type Scope = BoardWriteScope<TStore>\n\n return {\n // Cards\n createCard: async (input: CreateCardInput, actor: BoardActor, scope?: Scope) =>\n await createCard(store, input, actor, scope),\n createSubCard: async (parentIssueId: string, input: Omit<CreateCardInput, 'parentIssueId'>, actor: BoardActor, scope?: Scope) =>\n await createSubCard(store, parentIssueId, input, actor, scope),\n updateCard: async (issueId: string, input: UpdateCardInput, actor: BoardActor, scope?: Scope) =>\n await updateCard(store, issueId, input, actor, scope),\n moveCard: async (issueId: string, nameOrId: string, actor: BoardActor, scope?: Scope) =>\n await moveCard(store, issueId, nameOrId, actor, scope),\n addComment: async (input: AddCommentInput, actor: BoardActor, scope?: Scope) =>\n await addComment(store, input, actor, scope),\n createCommentOnce: async (input: CreateCommentOnceInput, scope?: Scope) =>\n await createCommentOnce(store, input, scope),\n listComments: async (issueId: string) => await listComments(store, issueId),\n deleteComment: async (commentId: string, actor: BoardActor, scope?: Scope) =>\n await deleteComment(store, commentId, actor, scope),\n deleteCard: async (issueId: string, scope?: Scope) => await deleteCard(store, issueId, scope),\n searchCards: async (query: string, limit: number) => await searchCards(store, query, limit),\n\n // Many cards at once\n bulkUpdateCards: async (issueIds: string[], input: BulkUpdateCardsInput, actor: BoardActor, scope?: Scope) =>\n await bulkUpdateCards(store, issueIds, input, actor, scope),\n reorderCards: async (workspaceId: string, orderedIds: string[], scope?: Scope) =>\n await reorderCards(store, workspaceId, orderedIds, scope),\n migrateCards: async (input: MigrateCardsInput) => await migrateCards(store, input),\n\n // What a card points at, and what it has been through\n addContextRef: async (issueId: string, ref: string, actor: BoardActor, scope?: Scope) =>\n await addContextRef(store, issueId, ref, actor, scope),\n removeContextRef: async (issueId: string, index: number, actor: BoardActor, scope?: Scope) =>\n await removeContextRef(store, issueId, index, actor, scope),\n listRelations: async (issueId: string) => await listRelations(store, issueId),\n createRelation: async (input: CreateRelationInput, scope?: Scope) => await createRelation(store, input, scope),\n deleteRelation: async (relationId: string, scope?: Scope) => await deleteRelation(store, relationId, scope),\n listFieldChanges: async (issueId: string) => await listFieldChanges(store, issueId),\n listActivity: async (issueId: string, resolver: IssueActivityResolver) => await listActivity(store, issueId, resolver),\n\n // The card's own state: what it is doing, and whose turn it is\n setStatusLine: async (issueId: string, statusLine: string | null, actor: BoardActor, scope?: Scope) =>\n await setStatusLine(store, issueId, statusLine, actor, scope),\n waitApproval: async (issueId: string, input: { statusLine?: string | null }, actor: BoardActor, scope?: Scope) =>\n await waitApproval(store, issueId, input, actor, scope),\n approve: async (issueId: string, input: { comment?: string | null }, actor: BoardActor, scope?: Scope) =>\n await approve(store, issueId, input, actor, scope),\n returnCard: async (issueId: string, input: { comment: string, toStatusName?: string | null }, actor: BoardActor, scope?: Scope) =>\n await returnCard(store, issueId, input, actor, scope),\n assertReturnable: async (issueId: string, input: { toStatusName?: string | null }, actor: BoardActor) =>\n await assertReturnable(store, issueId, input, actor),\n readApproval: async (issueId: string) => await readApproval(store, issueId),\n\n // The pull requests a card names\n linkPullRequest: async (issueId: string, urlOrRef: string, actor: BoardActor, scope?: Scope) =>\n await linkPullRequest(store, issueId, urlOrRef, actor, scope),\n unlinkPullRequest: async (issueId: string, linkId: string, actor: BoardActor, scope?: Scope) =>\n await unlinkPullRequest(store, issueId, linkId, actor, scope),\n listPullRequests: async (issueId: string) => await listPullRequests(store, issueId),\n listPullRequestStandings: async (issueId: string) => await listPullRequestStandings(store, issueId),\n\n // Milestones, and milestones read as sprints\n listMilestones: async (workspaceId: string | null) => await listMilestones(store, workspaceId),\n deleteMilestone: async (milestoneId: string, scope?: Scope) => await deleteMilestone(store, milestoneId, scope),\n createMilestone: async (input: CreateMilestoneInput, scope?: Scope) => await createMilestone(store, input, scope),\n updateMilestone: async (milestoneId: string, input: UpdateMilestoneInput, scope?: Scope) =>\n await updateMilestone(store, milestoneId, input, scope),\n listSprints: async (workspaceId: string, now?: number) => await listSprints(store, workspaceId, now),\n currentSprint: async (workspaceId: string, now?: number) => await currentSprint(store, workspaceId, now),\n closeSprint: async (milestoneId: string, input: { carryTo?: string | null }, actor: BoardActor, scope?: Scope) =>\n await closeSprint(store, milestoneId, input, actor, scope),\n\n // Columns\n listColumns: async (workspaceId: string) => await listColumns(store, workspaceId),\n findColumn: async (workspaceId: string, nameOrId: string) => await findColumn(store, workspaceId, nameOrId),\n requireColumn: async (workspaceId: string, nameOrId: string) => await requireColumn(store, workspaceId, nameOrId),\n ensureDefaultColumns: async (workspaceId: string, scope?: Scope) => await ensureDefaultColumns(store, workspaceId, scope),\n createColumn: async (input: BoardColumnInput, actor: BoardActor, scope?: Scope) => await createColumn(store, input, actor, scope),\n updateColumn: async (statusId: string, patch: BoardColumnPatch, actor: BoardActor, scope?: Scope) =>\n await updateColumn(store, statusId, patch, actor, scope),\n describeColumn: async (workspaceId: string, nameOrId: string, description: string | null, scope?: Scope) =>\n await describeColumn(store, workspaceId, nameOrId, description, scope),\n setColumnEntryRules: async (workspaceId: string, nameOrId: string, entryRules: readonly string[] | null, actor: BoardActor, scope?: Scope) =>\n await setColumnEntryRules(store, workspaceId, nameOrId, entryRules, actor, scope),\n enforceColumnEntryForCards: async (issueIds: string[], statusId: string, actor: BoardActor) =>\n await enforceColumnEntryForCards(store, issueIds, statusId, actor),\n deleteColumn: async (statusId: string, scope?: Scope) => await deleteColumn(store, statusId, scope),\n reorderColumns: async (workspaceId: string, orderedIds: string[], scope?: Scope) =>\n await reorderColumns(store, workspaceId, orderedIds, scope),\n addStandardColumns: async (workspaceId: string, scope?: Scope) => await addStandardColumns(store, workspaceId, scope),\n\n // Runs\n startRun: async (issueId: string, input: StartRunInput, actor: BoardActor, scope?: Scope) =>\n await startRun(store, issueId, input, actor, scope),\n attachRunExecution: async (runId: string, input: AttachRunExecutionInput, scope?: Scope) =>\n await attachRunExecution(store, runId, input, scope),\n recordRunSessionRef: async (runId: string, ref: string, scope?: Scope) =>\n await recordRunSessionRef(store, runId, ref, scope),\n clearRunSessionRef: async (runId: string, actor: BoardActor, options?: { replaceWith?: string }, scope?: Scope) =>\n await clearRunSessionRef(store, runId, actor, options, scope),\n finishRun: async (runId: string, input: FinishRunInput, actor: BoardActor, scope?: Scope) =>\n await finishRun(store, runId, input, actor, scope),\n stopRunningRuns: async (issueId: string, actor: BoardActor, scope?: Scope) =>\n await stopRunningRuns(store, issueId, actor, scope),\n listRuns: async (issueId: string) => await listRuns(store, issueId),\n findRunByChatSessionId: async (chatSessionId: string) => await findRunByChatSessionId(store, chatSessionId),\n readBoardProjection: async (workspaceId: string) => await readBoardProjection(store, workspaceId),\n readBoardProjectionForIssues: async (issueIds: string[]) => await readBoardProjectionForIssues(store, issueIds),\n readBoardProjectionForIssue: async (issueId: string) => await readBoardProjectionForIssue(store, issueId),\n\n // The board as a whole\n listReady: async (input: { workspaceId: string, limit?: number }) => await listReady(store, input),\n buildPrimeText: async (workspaceId: string) => await buildPrimeText(store, workspaceId),\n }\n}\n\n/** The board's operations, as a type callers can name. */\nexport type BoardOps = ReturnType<typeof createBoardOps<BoardStore>>\n","/**\n * A board's connection string, as it may be said out loud.\n *\n * Everything a person needs to recognise which board they are talking to is\n * the host and the database name. The password is the one part that must never\n * reach a terminal, a log, a `--json` result or a tool's answer — and it is not\n * the only part: a connection string carries options in its query, and a\n * `?password=…` there is as much a secret as the one in front of the host.\n *\n * It is a fact about the value rather than about the command line, which is why\n * it lives here: the MCP tools hand back messages they did not write either,\n * and they must not be made to carry the board tool's own runtime to say them\n * safely.\n */\n\n/** What a connection string is called when there is nothing safe to print of it. */\nconst MASKED_UNREADABLE_URL = 'postgres://***'\n\n/** What is printed in place of a password, and of a query that may hide one. */\nconst MASK = '***'\n\n/**\n * A connection string inside a longer line. Anything but whitespace and the\n * quotes a message may wrap it in belongs to the URL — a password is allowed\n * every character a URL is, so the match has to be greedy to be safe.\n */\nconst DATABASE_URL_PATTERN = /\\bpostgres(?:ql)?:\\/\\/[^\\s'\"`]+/gi\n\n/**\n * The connection string with everything secret taken out of it.\n *\n * A string this cannot take apart is not examined to find out why — it is\n * replaced entirely, because whatever is wrong with it, the secret is still\n * somewhere inside. A missing scheme counts as that: `user:secret@host/db`\n * parses, as a `user:` URL whose whole password is in the path, and printing it\n * back would say the secret out loud.\n */\nexport function maskDatabaseUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (!parsed.host) {\n return MASKED_UNREADABLE_URL\n }\n if (parsed.password) {\n parsed.password = MASK\n }\n // Not read for what it holds: an option this build has never heard of may\n // still be a credential, and the query is not what tells a person which\n // board this is.\n if (parsed.search) {\n parsed.search = MASK\n }\n return parsed.toString()\n }\n catch {\n return MASKED_UNREADABLE_URL\n }\n}\n\n/**\n * Every connection string inside a line of text, masked.\n *\n * A message this package did not write — a driver's, a migrator's — may carry\n * the whole connection string in the middle of a sentence, and it reaches the\n * same terminal, or the same agent, as everything else.\n */\nexport function maskDatabaseUrls(text: string): string {\n return text.replace(DATABASE_URL_PATTERN, url => maskDatabaseUrl(url))\n}\n","import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\n\nimport { z } from 'zod'\n\n/**\n * What ties a project folder to a board.\n *\n * A person running `kanbo` in their project should not have to say which\n * workspace they mean, and the answer cannot live in any database: the folder\n * knows which project it is, a database does not know which folder the shell\n * is in. `kanbo init` writes the answer next to the code, in a directory kanbo\n * owns — never in the user's own `.gitignore`, which is theirs.\n */\n\n/** The directory kanbo keeps inside a project: the binding, and a board file of the project's own. */\nconst PROJECT_DIRECTORY = '.kanbo'\n\n/** The binding's own file name, which is also the line that keeps it out of git. */\nconst BINDING_FILE_NAME = 'binding.json'\n\n/** The binding file itself, relative to the project root. */\nexport const BINDING_FILE_PATH = join(PROJECT_DIRECTORY, BINDING_FILE_NAME)\n\n/** SQLite's two companions of a board file in WAL mode, which hold committed data of their own. */\nconst SQLITE_SIDE_FILES = ['-wal', '-shm'] as const\n\n/** Readable and writable by its owner and by nobody else: it holds connection strings. */\nconst BINDING_FILE_MODE = 0o600\n\n/**\n * The spellings of a `.gitignore` line that already cover the binding.\n *\n * Git's pattern language is not reimplemented here: the cost of failing to\n * recognise an exotic pattern is one redundant line in a file, and the cost of\n * guessing that some pattern covers the binding when it does not is a\n * workspace id committed to someone else's checkout.\n */\nconst BINDING_IGNORED_BY = new Set([BINDING_FILE_NAME, `/${BINDING_FILE_NAME}`, `./${BINDING_FILE_NAME}`, `**/${BINDING_FILE_NAME}`])\n\nconst BindingSchema = z.object({\n schemaVersion: z.literal(1),\n workspaceId: z.string().trim().min(1),\n boardId: z.string().trim().min(1).nullish(),\n /**\n * The board file `kanbo init --file` created for this project, relative to\n * the project root — `.kanbo/board.db` by default. Resolution honours it\n * between the connection-string checks and the `KANBO_DB_PATH` fallback: a\n * project bound to a file of its own falls back to *that* file rather than\n * a host database that may not exist on this machine. `null` on a project\n * that has never run `kanbo init --file`.\n */\n dbPath: z.string().nullish(),\n /**\n * The external board this project works on, when it has one. Unlike\n * `dbPath` this *is* read: a project bound to a database has no board file\n * to fall back to. It is a secret, and the binding is the one file here that\n * git never sees — which is why it may be written down at all.\n */\n databaseUrl: z.string().trim().min(1).nullish(),\n /**\n * The same board, as the agent role logs into it.\n *\n * A shell started for an agent — and the MCP server, whose whole\n * client is an agent — is given this one when it is there, so the rules the\n * database keeps about `kanban_agent` are the rules it actually meets. It is\n * a different login, not a hidden one; a project with only `databaseUrl`\n * hands that one to everybody.\n */\n agentDatabaseUrl: z.string().trim().min(1).nullish(),\n /**\n * What this workspace's card keys start with (`MAN` in `MAN-012`).\n *\n * An external board holds only the board: there is no `workspaces` row to\n * read a key off, so `kanbo init` writes the answer down here. On a board\n * file the row is still the truth and this is not read.\n */\n identifier: z.string().trim().min(1).nullish(),\n})\n\n/** The project's binding, as it is written and read back. */\nexport type KanboBinding = z.infer<typeof BindingSchema>\n\n/** A binding that was found, the file it was found in, and the project it belongs to. */\nexport interface FoundBinding {\n path: string\n /** The project root the binding is for — where `.kanbo` sits, and what a relative `dbPath` is relative to. */\n projectDir: string\n binding: KanboBinding\n}\n\n/**\n * The nearest binding at or above `startDir`.\n *\n * Walking up is what makes the tool usable from a subdirectory: an agent that\n * `cd`s into `packages/web` is still working on the same project. A file that\n * does not parse is not a binding — the walk continues past it, and the caller\n * ends up with the same error as a project that was never bound.\n */\nexport function findBinding(startDir: string): FoundBinding | null {\n let directory = resolve(startDir)\n\n while (true) {\n const path = join(directory, BINDING_FILE_PATH)\n const binding = readBinding(path)\n if (binding) {\n return { path, projectDir: directory, binding }\n }\n const parent = dirname(directory)\n if (parent === directory) {\n return null\n }\n directory = parent\n }\n}\n\n/**\n * The binding this project folder has of its own — not a parent's.\n *\n * `kanbo init` merges into it rather than replacing it: a person re-running\n * the command to refresh an instruction block never said anything about the\n * board, and a second `init` that quietly dropped the connection string would\n * leave a project bound to nothing.\n */\nexport function readProjectBinding(projectDir: string): KanboBinding | null {\n return readBinding(join(projectDir, BINDING_FILE_PATH))\n}\n\n/** The binding in that exact file, or `null` when there is none to read. */\nexport function readBinding(path: string): KanboBinding | null {\n if (!existsSync(path)) {\n return null\n }\n try {\n return BindingSchema.parse(JSON.parse(readFileSync(path, 'utf8')))\n }\n catch {\n return null\n }\n}\n\n/**\n * Bind the project folder to a board.\n *\n * The binding itself is kept out of git, because the workspace id in it means\n * nothing in anyone else's checkout. The ignore rule names the files kanbo\n * writes rather than `*`, so a person who does want something in `.kanbo`\n * committed can still add it. The user's own `.gitignore` is not touched at\n * all; it is not kanbo's file to edit.\n *\n * It is written `0600`, because it may hold one or two connection strings and\n * keeping a file out of a repository is not keeping it away from the other\n * accounts on the machine. `mode` only applies to a file this call creates, so\n * an existing one — a second `init`, or a file written before this rule — is\n * narrowed explicitly. Windows has no such bits and `chmodSync` does nothing\n * there, which is the right nothing: no permission to narrow, and no failure\n * to handle.\n */\nexport function writeBinding(projectDir: string, binding: KanboBinding): string {\n const directory = join(projectDir, PROJECT_DIRECTORY)\n mkdirSync(directory, { recursive: true })\n ignoreBindingFile(directory)\n\n // Written beside itself and renamed into place, so a crash half-way leaves\n // the old binding or the new one and never half of either.\n const path = join(projectDir, BINDING_FILE_PATH)\n const temporaryPath = `${path}.${process.pid}.tmp`\n try {\n writeFileSync(temporaryPath, `${JSON.stringify(BindingSchema.parse(binding), null, 2)}\\n`, { mode: BINDING_FILE_MODE })\n chmodSync(temporaryPath, BINDING_FILE_MODE)\n renameSync(temporaryPath, path)\n }\n catch (error) {\n rmSync(temporaryPath, { force: true })\n throw error\n }\n return path\n}\n\n/**\n * Make sure `.kanbo/.gitignore` covers the binding, and change nothing else\n * about it. Lines that are already there belong to whoever wrote them — this\n * appends one line, or none at all.\n */\nfunction ignoreBindingFile(directory: string): void {\n const path = join(directory, '.gitignore')\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : null\n if (existing !== null && existing.split('\\n').some(line => BINDING_IGNORED_BY.has(line.trim()))) {\n return\n }\n\n const kept = existing?.replace(/\\s*$/, '') ?? ''\n writeFileSync(path, kept ? `${kept}\\n${BINDING_FILE_NAME}\\n` : `${BINDING_FILE_NAME}\\n`)\n}\n\n/**\n * Make sure `.kanbo/.gitignore` also covers a board file of this project's\n * own — `kanbo init --file` calls this once, right after creating it — and\n * the two files SQLite leaves beside it in WAL mode, `-wal` and `-shm`, which\n * hold committed data of their own and are exactly as much a secret as the\n * database itself.\n *\n * Only when the file is inside `.kanbo`, which is where `--file` puts it by\n * default: the ignore rule only ever grows kanbo's own directory, and a path\n * chosen elsewhere in the project gets no line here, the same way the user's\n * own `.gitignore` is never touched at all.\n */\nexport function ignoreBoardFile(projectDir: string, absoluteDbPath: string): void {\n const directory = join(projectDir, PROJECT_DIRECTORY)\n const relativeToDirectory = relative(directory, absoluteDbPath)\n if (relativeToDirectory.startsWith('..') || isAbsolute(relativeToDirectory)) {\n return\n }\n\n mkdirSync(directory, { recursive: true })\n const wanted = [relativeToDirectory, ...SQLITE_SIDE_FILES.map(suffix => `${relativeToDirectory}${suffix}`)]\n const path = join(directory, '.gitignore')\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : null\n const already = new Set((existing ?? '').split('\\n').map(line => line.trim()))\n const missing = wanted.filter(line => !already.has(line))\n if (missing.length === 0) {\n return\n }\n\n const kept = existing?.replace(/\\s*$/, '') ?? ''\n writeFileSync(path, kept ? `${kept}\\n${missing.join('\\n')}\\n` : `${missing.join('\\n')}\\n`)\n}\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { maskDatabaseUrl } from '../domain/database-url'\nimport type { BoardFileOwner } from '../sqlite/open-database'\nimport { readBoardFileContents } from '../sqlite/open-database'\nimport { isAgentShell } from './actor'\nimport { findBinding } from './binding'\nimport { CliError, EXIT_NOT_RESOLVED } from './output'\n\n/**\n * Which board the command works on.\n *\n * A board is a SQLite file — one of a project's own (`kanbo init --file`), or\n * a host app's database the board lives inside — or a Postgres database of its\n * own, and the command line reaches all three. What the caller typed comes\n * first, and only one of `--db` and `--database-url` may be typed: together\n * they name two boards, which is a mistake to say out loud rather than a\n * precedence to resolve. Failing a flag, a connection string is looked for —\n * the environment, then the project's binding — because a project bound to a\n * database has no board file to fall back to, and a fallback that quietly wrote\n * to some local file instead is the one mistake worth making impossible.\n *\n * Failing a connection string, a board file is looked for: the project's own\n * binding first — `kanbo init --file` records the file it created — and only\n * then `KANBO_DB_PATH`, which is how an app that embeds the board tells the\n * shells it starts where its database is. Nothing here is created and nothing\n * is migrated — a file that is not there means the person has not run\n * `kanbo init --file` yet, and saying so is more useful than opening an empty\n * database they will never see. Whichever file is chosen, `readFileOwnerAt`\n * says whose file it is: the marker `kanbo init --file` leaves is what every\n * later command reads to decide whether it may look up a `workspaces` row in\n * it at all (ruling 5-4).\n */\n\nexport const DATABASE_NOT_FOUND_MESSAGE = 'No board found. Run \"kanbo init --file\" in the project, '\n + 'pass --db or --database-url, or set KANBO_DB_PATH or KANBO_DATABASE_URL.'\n\n/** What a caller is told when they name both kinds of board at once. */\nexport const TWO_BOARDS_MESSAGE\n = 'One board at a time: pass --db for a board file or --database-url for an external board, not both.'\n\n/** Where the board this command works on lives. */\nexport type BoardTarget\n = | { kind: 'sqlite', path: string, owner: BoardFileOwner }\n | { kind: 'postgres', url: string }\n\n/** What a caller typed, and where they typed it. */\nexport interface BoardTargetInput {\n /** What `--db` was given: a board file. */\n explicitPath?: string | null\n /** What `--database-url` was given: an external board's connection string. */\n explicitUrl?: string | null\n /** The directory the command was typed in; the binding is looked for at or above it. */\n cwd?: string\n /**\n * Reach the board as an agent rather than as whoever owns this shell. Never\n * typed: `kanbo mcp` sets it, because the client it serves is the agent, and\n * every other command asks the shell itself.\n */\n asAgent?: boolean\n}\n\n/**\n * The board to open, in the order a caller would expect to be obeyed: what\n * they typed, what they exported for this tool, what the project folder was\n * bound to, and only then the board file the environment names.\n *\n * A typed flag beats the environment, whichever kind of board it names: a\n * person who passes `--db` is looking at that file, and an exported connection\n * string left over from another project is not a reason to open something else.\n */\nexport function resolveDbTarget(input: BoardTargetInput = {}): BoardTarget {\n const explicitPath = input.explicitPath?.trim()\n const explicitUrl = input.explicitUrl?.trim()\n if (explicitPath && explicitUrl) {\n throw new CliError(1, TWO_BOARDS_MESSAGE)\n }\n if (explicitUrl) {\n return { kind: 'postgres', url: explicitUrl }\n }\n if (explicitPath) {\n return toSqliteTarget(resolveDbPath({ explicitPath }))\n }\n\n const url = resolveDatabaseUrl(input)\n return url ? { kind: 'postgres', url } : toSqliteTarget(resolveDbPath(input))\n}\n\n/** The sqlite half of a target: the path, and who wrote the file it names. */\nfunction toSqliteTarget(path: string): BoardTarget {\n return { kind: 'sqlite', path, owner: readFileOwnerAt(path) }\n}\n\n/**\n * Who wrote this file's board, without leaving a second connection open onto\n * it: opened, read, closed, before the caller opens the file for real.\n *\n * A file with no board in it yet still reads as a host database's — `assertBoardSchema`\n * is what says a board is missing or too old, once the caller actually tries to\n * use it. A path that is not a SQLite database at all is a different mistake\n * and gets its own answer: `kanbo card list --db ./README.md` used to leave\n * the driver's own sentence and its stack, where every other way of naming the\n * wrong board leaves one line and exit `2`.\n */\nfunction readFileOwnerAt(path: string): BoardFileOwner {\n try {\n return readBoardFileContents(path).owner\n }\n catch {\n throw new CliError(EXIT_NOT_RESOLVED, `${path} is not a board database this tool can open.`)\n }\n}\n\n/**\n * The external board named for this command, or `null` when none is.\n *\n * Apart from `resolveDbTarget` itself, this is for the two commands that do\n * not take a board the ordinary way: `kanbo init`, where `--db` names the\n * host database rather than the board being bound, and the administrative ones,\n * which never take the agent's login.\n */\nexport function resolveDatabaseUrl(input: BoardTargetInput = {}): string | null {\n return input.explicitUrl?.trim() || process.env.KANBO_DATABASE_URL?.trim() || readBoundDatabaseUrl(input)\n}\n\n/**\n * The external board this project is bound to, as this shell should reach it.\n *\n * A binding may carry two connection strings: the one a person uses, and the\n * one the agent role logs in with. An agent's shell — and the MCP server, whose\n * whole client is an agent — is given the second when it is there, so the rules\n * the database keeps about `kanban_agent` are the rules it actually meets.\n * Nothing about this is a secret from the agent: it is a different login, not a\n * hidden one, and a project with only one string hands that one to everybody.\n */\nfunction readBoundDatabaseUrl(input: BoardTargetInput): string | null {\n const bound = findBinding(input.cwd ?? process.cwd())?.binding\n if (!bound) {\n return null\n }\n const asAgent = input.asAgent ?? isAgentShell()\n return (asAgent ? bound.agentDatabaseUrl?.trim() : null) || bound.databaseUrl?.trim() || null\n}\n\n/**\n * The board file to open, in the order a caller would expect to be obeyed:\n * what they typed, the file this project was bound to by `kanbo init --file`,\n * and what they exported for this tool.\n *\n * The chosen path has to exist. A `--db` that is not there is a typo, not an\n * invitation to fall back to the real board and write to it by mistake.\n */\nfunction resolveDbPath(input: BoardTargetInput = {}): string {\n return requireExisting(input.explicitPath?.trim() || readBoundDbPath(input) || readEnvironmentDbPath())\n}\n\n/**\n * A host app's database on this machine, never through this project's own\n * `kanbo init --file` binding: what `--db` was given, or `KANBO_DB_PATH`.\n *\n * `kanbo init` calls this — not `resolveDbPath` — to find the database it\n * reads a workspace's id and card-key off before binding this project to a\n * database or a board file of its own. Whether *this* project already has a\n * board file of its own is not that question: a project bound to one by an\n * earlier `kanbo init --file` still has cards to read a workspace off if a\n * host database also knows this folder, and that file has no `workspaces`\n * table to be misread as one either way.\n */\nexport function resolveHostDbPath(explicitPath?: string | null): string {\n return requireExisting(explicitPath?.trim() || readEnvironmentDbPath())\n}\n\nfunction requireExisting(path: string | null): string {\n if (!path || !existsSync(path)) {\n throw new CliError(EXIT_NOT_RESOLVED, DATABASE_NOT_FOUND_MESSAGE)\n }\n return path\n}\n\n/**\n * The board file this project was bound to by `kanbo init --file`, resolved\n * against the project root the binding was found in — `dbPath` is written\n * relative to it, the same way a project's own `node_modules/.bin` is relative\n * to the project rather than to whichever subdirectory a shell happens to be\n * standing in.\n */\nfunction readBoundDbPath(input: BoardTargetInput): string | null {\n const found = findBinding(input.cwd ?? process.cwd())\n if (!found) {\n return null\n }\n const dbPath = found.binding.dbPath?.trim()\n return dbPath ? resolve(found.projectDir, dbPath) : null\n}\n\n/** The board file the environment names — the one an app that embeds the board exports for the shells it starts. */\nfunction readEnvironmentDbPath(): string | null {\n return process.env.KANBO_DB_PATH?.trim() || null\n}\n\n/** The board a command worked on, in the one line a person reads or a result carries. */\nexport function describeTarget(target: BoardTarget): string {\n return target.kind === 'postgres' ? maskDatabaseUrl(target.url) : target.path\n}\n","import { drizzle } from 'drizzle-orm/postgres-js'\nimport { migrate } from 'drizzle-orm/postgres-js/migrator'\nimport postgres from 'postgres'\n\nimport { getBoardMigrationsPath } from '../postgres/migrations-path'\nimport type { BoardPostgresDatabase } from '../postgres/open-database'\n\n/**\n * The one place the command line opens an external board.\n *\n * Everything a command does with a connection lives behind this: the board\n * itself for the store and the schema guard, the migration, and the role\n * script. A command never holds the driver, which is what lets the tests hand\n * the whole thing an embedded Postgres instead — a board in memory answers all\n * three the same way, and a real server is not something a unit test may need.\n */\n\n/** An open external board, and everything a command does with one. */\nexport interface OpenPostgresBoard {\n /** The board, for the store and for the guard that says whether it is a board at all. */\n database: BoardPostgresDatabase\n /** Bring it up to the schema this build speaks. Running it again does nothing. */\n migrate: () => Promise<void>\n /** Run a script of several statements as it is written — the role SQL is one. */\n runScript: (script: string) => Promise<void>\n close: () => Promise<void>\n}\n\n/** How a board is opened from a connection string. */\nexport type PostgresBoardOpener = (url: string) => Promise<OpenPostgresBoard>\n\n/**\n * How long a connection is given to say goodbye. The command is about to exit\n * either way; the timeout is there so a pooler that has already gone does not\n * hold the process open behind it.\n */\nconst CLOSE_TIMEOUT_SECONDS = 5\n\nlet openerForTests: PostgresBoardOpener | null = null\n\n/**\n * Open the board this connection string names.\n *\n * `prepare: false` because both of Supabase's poolers sit in front of the\n * database and neither carries a prepared statement across a connection, and\n * `max: 1` because a `kanbo` command is one command: a pool of one is the\n * whole of the concurrency there is, and it is also what makes `set role` and\n * a transaction mean what they say.\n */\nexport async function openPostgresBoard(url: string): Promise<OpenPostgresBoard> {\n return await (openerForTests ?? openWithPostgresJs)(url)\n}\n\nasync function openWithPostgresJs(url: string): Promise<OpenPostgresBoard> {\n const client = postgres(url, { prepare: false, max: 1 })\n const database = drizzle(client)\n return {\n database,\n migrate: async () => {\n await migrate(database, { migrationsFolder: getBoardMigrationsPath() })\n },\n // The role script is many statements and a `do $$ … $$` block, which is\n // the simple query protocol's job rather than a parameterised statement's.\n runScript: async (script: string) => {\n await client.unsafe(script).simple()\n },\n close: async () => {\n await client.end({ timeout: CLOSE_TIMEOUT_SECONDS })\n },\n }\n}\n\n/**\n * Hand the commands an already-open board instead of a connection.\n *\n * A `kanbo` test runs the commands in this process against a database it owns,\n * and an embedded Postgres listens on no socket, so there is no connection\n * string that could reach one. This is the seam, and it is deliberately a\n * function rather than an environment variable: a production path that can be\n * switched from outside is a production path that will be.\n */\nexport function overridePostgresOpenerForTests(opener: PostgresBoardOpener | null): void {\n openerForTests = opener\n}\n","import { BoardError } from '../domain/errors'\nimport type { BoardPostgresDatabase } from '../postgres/open-database'\nimport { assertBoardSchema as assertPostgresBoardSchema } from '../postgres/open-database'\nimport type { BoardFileOwner } from '../sqlite/open-database'\nimport { assertBoardSchema } from '../sqlite/open-database'\nimport type { SqliteDatabase } from '../sqlite/transaction'\nimport { CliError, EXIT_SCHEMA_OUTDATED } from './output'\n\nexport const SCHEMA_OUTDATED_MESSAGE\n = 'This database is older than the board schema. Let the app it belongs to apply its migrations first.'\n\n/** The same, for a board file this package created and migrates itself. */\nexport const FILE_SCHEMA_OUTDATED_MESSAGE\n = 'Board file is older than this build. Run kanbo migrate.'\n\nexport const SCHEMA_NOT_INSTALLED_MESSAGE\n = 'This database holds no board this build can speak for — it has none, or one an '\n + 'older kanbo created. Run kanbo migrate.'\n\n/**\n * Refuse to write to a board file this build cannot speak for.\n *\n * Which remedy a caller is told about depends on who migrates the file: a host\n * app migrates its own database, so an outdated one is read from at the\n * caller's own risk but never written to — a write shaped by this build's idea\n * of the schema is how a person's board gets a column full of nothing. A file\n * of this package's own is this package's to migrate, so the same refusal\n * points at `kanbo migrate` instead of at the app. Reads stay open either way,\n * because a person who cannot migrate right now should still be able to look at\n * their cards.\n */\nexport function assertWritableBoard(database: SqliteDatabase, owner: BoardFileOwner): void {\n try {\n assertBoardSchema(database)\n }\n catch (error) {\n if (error instanceof BoardError && error.code === 'board_schema_outdated') {\n throw new CliError(EXIT_SCHEMA_OUTDATED, owner === 'kanbo' ? FILE_SCHEMA_OUTDATED_MESSAGE : SCHEMA_OUTDATED_MESSAGE)\n }\n throw error\n }\n}\n\n/**\n * Refuse an external board this build cannot speak for — before reading it as\n * well as before writing to it.\n *\n * The asymmetry with the file above is the ownership. Nobody but this package\n * ever creates these tables, so a database with no board in it is not an older\n * board to be read carefully: it is a connection string pointing at something\n * else entirely, and reading eight tables that are not there says less than\n * naming the one command that would put them there. The same sentence answers\n * a board of another generation, which `kanbo migrate` also settles.\n */\nexport async function assertInstalledBoard(database: BoardPostgresDatabase): Promise<void> {\n try {\n await assertPostgresBoardSchema(database)\n }\n catch (error) {\n if (error instanceof BoardError && error.code === 'board_schema_outdated') {\n throw new CliError(EXIT_SCHEMA_OUTDATED, SCHEMA_NOT_INSTALLED_MESSAGE)\n }\n throw error\n }\n}\n","import { realpathSync } from 'node:fs'\nimport { resolve, sep } from 'node:path'\n\nimport { sql } from 'drizzle-orm'\nimport { z } from 'zod'\n\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport type { SqliteDatabase } from '../sqlite/transaction'\nimport { findBinding } from './binding'\nimport { CliError, EXIT_NOT_RESOLVED } from './output'\n\n/**\n * Which workspace the command is about.\n *\n * Cards are numbered per workspace and columns belong to one, so a command\n * typed in a project folder has to arrive at a workspace — its id, and the\n * identifier its card keys are built from — somehow. Where the answer comes\n * from is the one thing the two kinds of board do not share.\n *\n * In a host database — an app's own SQLite file the board lives inside — this\n * is the one place the tool reads a table it does not own: the record of which\n * folder is which workspace is the app's `workspaces` row (`id`, `name`,\n * `identifier`, `locator_json`). The module reads that row and nothing else —\n * the name, the identifier the issue key is built from, and the path inside the\n * locator — and never writes there. It reads it as four columns of raw SQL\n * rather than through a schema, because this package declares the board and\n * not the app: a table belonging to somebody else is read by name, or the library would carry\n * a copy of a schema it does not own (ruling 5-1).\n *\n * An external board, like a board file of a project's own, holds only the\n * board. There is no `workspaces` table to\n * read, so nothing can be looked up and everything has to have been said:\n * `--workspace`, the environment, or the binding `kanbo init` wrote.\n */\n\n/**\n * The part of a workspace locator this tool understands: which machine the\n * project is on, and where it is there. The app that writes the row owns the\n * full shape; a row that does not carry these two is not a locator this tool\n * can act on, and is skipped.\n */\nconst LocatorSchema = z.object({\n nodeId: z.string().trim().min(1),\n path: z.string().trim().min(1),\n})\n\n/** The node id a locator gives this machine. */\nconst LOCAL_NODE_ID = 'local'\n\n/**\n * The one message that has to work for `kanbo init` too. A command that told a\n * person to run `init` while they were running `init` would be answering a\n * question they did not ask, so what to do comes first and the binding second.\n */\nconst WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. Pass --workspace <name|id>, '\n + 'or run \"kanbo init\" in the project folder.'\n\n/** The same, for a board that cannot look a workspace up at all. */\nexport const EXTERNAL_WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. An external board holds no workspaces table, so pass '\n + '--workspace <id>, set KANBO_WORKSPACE_ID, or run \"kanbo init --database-url <url>\" in the project folder.'\n\n/** And the one that is missing only the key a card is numbered with. */\nexport const EXTERNAL_IDENTIFIER_NOT_RESOLVED_MESSAGE\n = 'Could not tell what this workspace\\'s card keys start with. An external board holds no workspaces table, '\n + 'so the key comes from this project\\'s binding: run '\n + '\"kanbo init --database-url <url> --workspace <id> --identifier <KEY>\" in the project folder.'\n\n/** The same two, for a board file of this project's own — which holds no `workspaces` table either. */\nexport const FILE_WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. A board file of this project\\'s own holds no workspaces table, so '\n + 'pass --workspace <id>, set KANBO_WORKSPACE_ID, or run \"kanbo init --file\" in the project folder.'\n\nexport const FILE_IDENTIFIER_NOT_RESOLVED_MESSAGE\n = 'Could not tell what this workspace\\'s card keys start with. A board file of this project\\'s own holds no '\n + 'workspaces table, so the key comes from this project\\'s binding: run \"kanbo init --file --workspace <id> '\n + '--identifier <KEY>\" in the project folder.'\n\n/**\n * A host database's workspace, as much of one as this tool reads: the four\n * columns the statement below selects. The app's own row may have more, and\n * none of it is this package's business.\n */\ninterface HostWorkspaceRow {\n id: string\n name: string\n identifier: string\n locatorJson: string\n}\n\n/** Where a command's workspace is looked up — or the fact that it cannot be. */\nexport type BoardWorkspaceSource\n = | { kind: 'sqlite', database: SqliteDatabase }\n | { kind: 'postgres' }\n | { kind: 'file' }\n\n/** How a command names the workspace it means, and where it was typed. */\nexport interface WorkspaceResolutionInput {\n /** What `--workspace` was given: a workspace name or its id — an id only, on an external board. */\n explicit?: string | null\n /** The directory the command was typed in. */\n cwd: string\n}\n\n/**\n * The workspace to work on, and everything card numbering needs to know about\n * it. The board never reads anybody else's tables to build an issue key, so the\n * identity is resolved here, once, and handed over.\n */\nexport function resolveWorkspace(\n source: BoardWorkspaceSource,\n input: WorkspaceResolutionInput,\n): BoardWorkspaceIdentity {\n if (source.kind === 'sqlite') {\n return toIdentity(findLocalWorkspace(source.database, input))\n }\n return resolveBindingOnlyWorkspace(input, source.kind === 'file' ? FILE_MESSAGES : EXTERNAL_MESSAGES)\n}\n\n/**\n * The workspace row to work on, in the order a caller would expect to be\n * obeyed: what they typed, what they exported, what the project folder was\n * bound to, and finally the workspace whose own path contains the folder they\n * are standing in.\n *\n * Every path ends at a row that exists. A binding pointing at a workspace\n * someone deleted is a broken binding, and saying so beats numbering a card\n * into a workspace that is not there.\n */\nfunction findLocalWorkspace(database: SqliteDatabase, input: WorkspaceResolutionInput): HostWorkspaceRow {\n const rows = listWorkspaces(database)\n\n const explicit = input.explicit?.trim()\n if (explicit) {\n return findByNameOrId(rows, explicit)\n }\n\n const fromEnvironment = process.env.KANBO_WORKSPACE_ID?.trim()\n if (fromEnvironment) {\n return findByNameOrId(rows, fromEnvironment)\n }\n\n const bound = findBinding(input.cwd)\n if (bound) {\n return findByNameOrId(rows, bound.binding.workspaceId)\n }\n\n const enclosing = findEnclosingWorkspace(rows, input.cwd)\n if (enclosing) {\n return enclosing\n }\n\n throw new CliError(EXIT_NOT_RESOLVED, WORKSPACE_NOT_RESOLVED_MESSAGE)\n}\n\n/** The two messages a board with no `workspaces` table refuses with — one pair per kind of such a board. */\ninterface BindingOnlyMessages {\n workspace: string\n identifier: string\n}\n\nconst EXTERNAL_MESSAGES: BindingOnlyMessages = {\n workspace: EXTERNAL_WORKSPACE_NOT_RESOLVED_MESSAGE,\n identifier: EXTERNAL_IDENTIFIER_NOT_RESOLVED_MESSAGE,\n}\n\nconst FILE_MESSAGES: BindingOnlyMessages = {\n workspace: FILE_WORKSPACE_NOT_RESOLVED_MESSAGE,\n identifier: FILE_IDENTIFIER_NOT_RESOLVED_MESSAGE,\n}\n\n/**\n * The same question asked of a board that cannot answer it — an external\n * database or a board file of the project's own, which is the one difference\n * between the two that matters here: neither holds a `workspaces` table.\n *\n * Nothing is looked up: an id is what the caller said, and the key its cards\n * are numbered with is what `kanbo init` wrote into the binding. The binding's\n * key is only trusted for the workspace the binding is about — a `--workspace`\n * naming another one would otherwise number its cards with this project's\n * prefix, and two workspaces would print the same key.\n */\nfunction resolveBindingOnlyWorkspace(input: WorkspaceResolutionInput, messages: BindingOnlyMessages): BoardWorkspaceIdentity {\n const bound = findBinding(input.cwd)?.binding\n const id = input.explicit?.trim() || process.env.KANBO_WORKSPACE_ID?.trim() || bound?.workspaceId\n if (!id) {\n throw new CliError(EXIT_NOT_RESOLVED, messages.workspace)\n }\n\n const identifier = bound?.workspaceId === id ? bound?.identifier?.trim() : null\n if (!identifier) {\n throw new CliError(EXIT_NOT_RESOLVED, messages.identifier)\n }\n // Neither board has a name for a workspace either, and the key is the one\n // thing a person recognises it by.\n return { id, identifier, name: identifier }\n}\n\nfunction toIdentity(workspace: HostWorkspaceRow): BoardWorkspaceIdentity {\n return { id: workspace.id, identifier: workspace.identifier, name: workspace.name }\n}\n\nfunction listWorkspaces(database: SqliteDatabase): HostWorkspaceRow[] {\n return database\n .all<{ id: string, name: string, identifier: string, locator_json: string }>(\n sql`select id, name, identifier, locator_json from workspaces`,\n )\n .map(row => ({ id: row.id, name: row.name, identifier: row.identifier, locatorJson: row.locator_json }))\n}\n\n/**\n * The workspace a caller named, by id first and by name second.\n *\n * Names are not unique — two checkouts of the same repository are two\n * workspaces with one name — so a name that matches more than one is refused\n * rather than resolved to whichever row came back first. Guessing here would\n * put a card on the wrong board and say nothing.\n */\nfunction findByNameOrId(rows: HostWorkspaceRow[], nameOrId: string): HostWorkspaceRow {\n const byId = rows.find(row => row.id === nameOrId)\n if (byId) {\n return byId\n }\n\n const byName = rows.filter(row => row.name.toLowerCase() === nameOrId.toLowerCase())\n if (byName.length > 1) {\n throw new CliError(EXIT_NOT_RESOLVED, `More than one workspace is called \"${nameOrId}\": `\n + `${byName.map(row => row.id).join(', ')}. Name the one you mean by its id.`)\n }\n if (byName.length === 0) {\n throw new CliError(EXIT_NOT_RESOLVED, `No workspace in this database matches \"${nameOrId}\".`)\n }\n return byName[0]\n}\n\n/**\n * The workspace whose folder contains `cwd`. The deepest match wins: a worktree\n * registered inside a project is a workspace of its own, and standing in it\n * means working on it.\n *\n * Only workspaces on this machine are considered. A locator names a node as\n * well as a path, and a path on another machine that happens to read like this\n * one is a different folder entirely, so node ids are compared before paths. Both sides are compared as real paths, so a\n * project reached through a symlink still matches the workspace it is.\n */\nfunction findEnclosingWorkspace(rows: HostWorkspaceRow[], cwd: string): HostWorkspaceRow | null {\n const directory = canonicalPath(cwd)\n const candidates = rows\n .flatMap((row) => {\n const locator = readLocator(row)\n if (!locator || locator.nodeId !== LOCAL_NODE_ID) {\n return []\n }\n const path = canonicalPath(locator.path)\n return directory === path || directory.startsWith(path + sep) ? [{ row, path }] : []\n })\n .sort((left, right) => right.path.length - left.path.length)\n\n return candidates[0]?.row ?? null\n}\n\nfunction readLocator(row: HostWorkspaceRow): z.infer<typeof LocatorSchema> | null {\n try {\n return LocatorSchema.parse(JSON.parse(row.locatorJson))\n }\n catch {\n return null\n }\n}\n\n/** The path itself, with symlinks resolved — or the absolute path, when it is not there to resolve. */\nfunction canonicalPath(path: string): string {\n const absolute = resolve(path)\n try {\n return realpathSync(absolute)\n }\n catch {\n return absolute\n }\n}\n","import type { Command } from 'commander'\n\nimport type { BoardStore } from '../board-store'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport { readIssuePrefix } from '../domain/numbering'\nimport type { BoardOps } from '../ops'\nimport { createBoardOps } from '../ops'\nimport { createPostgresBoardStore } from '../postgres/board-store.postgres'\nimport { createSqliteBoardStore } from '../sqlite/board-store.sqlite'\nimport { openBoardDatabase } from '../sqlite/open-database'\nimport type { Issue, IssueRun } from '../sqlite/schema'\nimport type { BoardTarget } from './db-target'\nimport { resolveDbTarget } from './db-target'\nimport type { CliOutputOptions, CliResult } from './output'\nimport { CliError, EXIT_NOT_RESOLVED, printResult, readFormat } from './output'\nimport { openPostgresBoard } from './postgres-board'\nimport { assertInstalledBoard, assertWritableBoard } from './schema-guard'\nimport { resolveWorkspace } from './workspace'\n\n/**\n * What every board command needs before it can do its one thing: the board\n * file, the workspace, the operations bound to them — and, when it is over, the\n * way its result and its failures reach the caller.\n *\n * Commands are written against this rather than opening databases themselves,\n * so the rules that must hold for all of them hold in one place: a write never\n * touches a database older than this build, the connection is always closed,\n * and a failure always leaves with the exit code the tool promises for it.\n */\n\n/** The options that say which board a command works on, and how it answers. */\nexport interface BoardTargetCommandOptions extends CliOutputOptions {\n db?: string\n databaseUrl?: string\n /**\n * Reach the board as an agent rather than as whoever owns this shell. Not a\n * flag anyone types — `kanbo mcp` sets it, because the client it serves is\n * the agent.\n */\n asAgent?: boolean\n}\n\n/** The same, plus the workspace — which every command but `migrate` and `roles` is about. */\nexport interface BoardCommandOptions extends BoardTargetCommandOptions {\n workspace?: string\n}\n\n/** One open board, and everything a command does its work through. */\nexport interface BoardSession {\n ops: BoardOps\n store: BoardStore\n /** The workspace the command is about, and the key its cards are numbered with. */\n workspace: BoardWorkspaceIdentity\n /**\n * Refuse a write to a board this build cannot speak for. A command declares\n * a write when it opens the session; a server that holds one open for many\n * calls asks again before each of them.\n */\n assertWritable: () => Promise<void>\n}\n\n/** The same, held by a caller that outlives one command and closes it itself. */\nexport interface OpenBoardSession extends BoardSession {\n close: () => Promise<void>\n}\n\n/** Whether the command is going to write to the board. Writes are guarded; reads are not. */\nexport type BoardCommandMode = 'read' | 'write'\n\n/** The two modes a card can be worked in, as `issues.execution_mode` spells them. */\nconst EXECUTION_MODES = ['worktree', 'main'] as const\n\n/** Add the options that name a board and shape the answer, wherever the command sits in the tree. */\nexport function withTargetOptions(command: Command): Command {\n return command\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to work on instead of a board file')\n .option('--json <fields>', 'print only these comma-separated fields, as JSON')\n .option('--format <format>', 'output format: json or pretty')\n}\n\n/** The same, plus the workspace: the options every command about cards carries. */\nexport function withBoardOptions(command: Command): Command {\n return withTargetOptions(command)\n .option('--workspace <nameOrId>', 'workspace the command is about')\n}\n\n/**\n * Open the board the caller named and resolve the workspace it is about.\n *\n * The board stays open until the caller closes it: one command does its one\n * thing and closes, while `kanbo mcp` holds the same session open for as long\n * as a client is talking to it. A failure on the way out closes the board first\n * — a caller that never received a session has nothing to close.\n */\nexport async function openBoardSession(\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n): Promise<OpenBoardSession> {\n const cwd = process.cwd()\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: options.asAgent,\n cwd,\n })\n return target.kind === 'postgres'\n ? await openExternalSession(target, options, cwd)\n : await openFileSession(target, options, mode, cwd)\n}\n\nasync function openFileSession(\n target: Extract<BoardTarget, { kind: 'sqlite' }>,\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n cwd: string,\n): Promise<OpenBoardSession> {\n const board = await openBoardDatabase(target.path)\n try {\n const assertWritable = async (): Promise<void> => {\n assertWritableBoard(board.database, target.owner)\n }\n if (mode === 'write') {\n assertWritableBoard(board.database, target.owner)\n }\n // A file of this package's own holds no `workspaces` table to look up —\n // the same reason an external board resolves from the binding alone.\n const workspace = resolveWorkspace(\n target.owner === 'kanbo' ? { kind: 'file' } : { kind: 'sqlite', database: board.database },\n { explicit: options.workspace, cwd },\n )\n const store = createSqliteBoardStore({ database: () => board.database })\n return { ops: createBoardOps(store), store, workspace, assertWritable, close: async () => board.close() }\n }\n catch (error) {\n board.close()\n throw error\n }\n}\n\n/**\n * The same, on a board that is a database of its own.\n *\n * The guard runs whatever the command came to do. A board file that this build\n * cannot write to is still a board worth reading; a connection string pointing\n * at a database with no board in it is nothing to read at all, and every\n * command would otherwise fail one statement later with the driver's own words.\n */\nasync function openExternalSession(\n target: Extract<BoardTarget, { kind: 'postgres' }>,\n options: BoardCommandOptions,\n cwd: string,\n): Promise<OpenBoardSession> {\n const board = await openPostgresBoard(target.url)\n try {\n const assertWritable = async (): Promise<void> => {\n await assertInstalledBoard(board.database)\n }\n await assertWritable()\n const workspace = resolveWorkspace({ kind: 'postgres' }, { explicit: options.workspace, cwd })\n const store = createPostgresBoardStore({ database: board.database })\n return { ops: createBoardOps(store), store, workspace, assertWritable, close: board.close }\n }\n catch (error) {\n await board.close()\n throw error\n }\n}\n\n/**\n * Open the board, do the one thing, print the result, close the board.\n *\n * The output options are checked before anything is opened: a caller who\n * mistyped `--format` should hear about it instead of writing to the board and\n * then failing to print what they wrote.\n */\nexport async function runBoardCommand(\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n body: (session: BoardSession) => Promise<CliResult>,\n): Promise<void> {\n readFormat(options)\n\n const session = await openBoardSession(options, mode)\n try {\n printResult(await body(session), options)\n }\n finally {\n await session.close()\n }\n}\n\n/** Where a card's work happens. A caller naming anything else is refused, not corrected. */\nexport function parseExecutionMode(value: string): Issue['executionMode'] {\n const mode = EXECUTION_MODES.find(candidate => candidate === value)\n if (!mode) {\n throw new CliError(1, `Unknown execution mode \"${value}\". Use ${EXECUTION_MODES.join(' or ')}.`)\n }\n return mode\n}\n\n/** A count a caller typed, refused rather than quietly rounded when it is not one. */\nexport function parseCount(value: string): number {\n const count = Number(value)\n if (!Number.isInteger(count) || count <= 0) {\n throw new CliError(1, `Expected a positive whole number, got \"${value}\".`)\n }\n return count\n}\n\n/**\n * The card a caller named, in the workspace the command is about.\n *\n * People and agents refer to a card by whatever they have in front of them: the\n * key printed on the board (`MAN-012`), the same key typed without the padding\n * (`MAN-12`), or just the number. All three mean one card in one workspace, and\n * a tool that only accepted the exact key would be wrong about that.\n *\n * Every lookup is scoped to the workspace, keys included. One database holds\n * every project's board, so an unscoped `findById` would let a key pasted from\n * another project — or from an agent that resolved the wrong workspace — move a\n * card on a board the caller is not even looking at.\n */\nexport async function requireCard(session: BoardSession, reference: string): Promise<Issue> {\n const trimmed = reference.trim()\n const byId = await session.store.issues.findInWorkspace(session.workspace.id, trimmed)\n if (byId) {\n return byId\n }\n\n const number = readCardNumber(session, trimmed)\n const byNumber = number === null ? null : await session.store.issues.findByNumber(session.workspace.id, number)\n if (!byNumber) {\n throw new CliError(EXIT_NOT_RESOLVED, `No card \"${reference}\" on this board.`)\n }\n return byNumber\n}\n\n/**\n * The run a caller named, in the workspace the command is about.\n *\n * A run id is opaque — there is no key to read a workspace off — so the card it\n * belongs to answers instead. One database holds every project's board, and a\n * run id pasted from another project would otherwise end a run on a board this\n * caller is not even looking at. The refusal is the one every other command\n * gives for something that is not on this board.\n */\nexport async function requireRun(session: BoardSession, runId: string): Promise<IssueRun> {\n const run = await session.store.runs.findById(runId.trim())\n const card = run ? await session.store.issues.findInWorkspace(session.workspace.id, run.issueId) : null\n if (!run || !card) {\n throw new CliError(EXIT_NOT_RESOLVED, `No run \"${runId}\" on this board.`)\n }\n return run\n}\n\n/**\n * The card number a reference names — but only when it is a reference to this\n * board's own cards.\n *\n * A bare number is one. A key is one only when its stem is this workspace's own\n * prefix: `MAN-12` and `MAN-012` are the same card, while `OTH-001` is another\n * board's key that happens to end in a number this board also uses, and reading\n * the digits off it would quietly hand back the wrong card.\n */\nfunction readCardNumber(session: BoardSession, reference: string): number | null {\n if (/^\\d+$/.test(reference)) {\n return Number(reference)\n }\n\n const separator = reference.lastIndexOf('-')\n const digits = reference.slice(separator + 1)\n if (separator < 0 || !/^\\d+$/.test(digits)) {\n return null\n }\n const prefix = readIssuePrefix(session.workspace)\n return reference.slice(0, separator).toUpperCase() === prefix ? Number(digits) : null\n}\n","/**\n * What a card is called wherever a person or an agent reads it by name.\n *\n * A card written as a description alone is titled with its own key (every\n * writer does that). Named by its key, such a card says nothing — so its name\n * is the first line of its description instead. A browser bundle that does not\n * load this package keeps the same rule on its own, tested with the same\n * examples.\n */\n\n/** How long a description line may run before it is cut with an ellipsis. */\nconst DESCRIPTION_LINE_LIMIT = 120\n\nexport function cardDisplayTitle(card: { id: string, title: string, description?: string | null }): string {\n if (card.title !== card.id) {\n return card.title\n }\n return descriptionFirstLine(card.description) ?? card.title\n}\n\nfunction descriptionFirstLine(description: string | null | undefined): string | null {\n for (const rawLine of (description ?? '').split('\\n')) {\n const line = stripMarkdown(rawLine)\n if (line) {\n return line.length > DESCRIPTION_LINE_LIMIT ? `${line.slice(0, DESCRIPTION_LINE_LIMIT - 1).trimEnd()}…` : line\n }\n }\n return null\n}\n\nfunction stripMarkdown(line: string): string {\n return line\n .replace(/^\\s*(?:>\\s*)+/, '')\n .replace(/^\\s*#{1,6}\\s+/, '')\n .replace(/^\\s*(?:[-*+]|\\d+[.)])\\s+(?:\\[[ x]\\]\\s+)?/i, '')\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/<[^>]+>/g, '')\n .replace(/[*_~`]/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n","import type { BoardActiveRun, BoardRunProjection } from '../board-store'\nimport { cardDisplayTitle } from '../domain/card-display-title'\nimport type { EntryRule } from '../domain/entry-rules'\nimport { readEntryRules } from '../domain/entry-rules'\nimport { toIssueView } from '../domain/issue-view'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { BoardRunView } from '../ops/runs'\nimport type { BoardSprint } from '../ops/sprints'\nimport type { Issue, IssueRun, IssueStatus } from '../sqlite/schema'\n\n/**\n * The board's rows as a command line prints them.\n *\n * A card row carries columns a terminal has no use for — the delegation\n * markers, the context refs, the chat session it came from — and hides the one\n * thing every caller wants, because the column is a foreign key rather than a\n * name. These projections are what `--json` selects fields from, so they are\n * also the shape a script is entitled to rely on.\n */\n\n/** A card, as the command line prints it. */\nexport interface CardView {\n /** The issue key, which is also the row id: `MAN-012`. */\n id: string\n number: number\n title: string\n description: string | null\n /** The column's name, `null` when the card sits in no column. */\n column: string | null\n /** The same column as the slug `kanbo card move` takes. */\n columnSlug: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n /** Every run the card has ever had; the next launch is attempt `attemptCount + 1`. */\n attemptCount: number\n /** The run going on right now, `null` when nobody is working on the card. */\n activeRun: CardActiveRunView | null\n updatedAt: number\n}\n\n/** Every field `kanbo card list|get --json` may ask for, in listing order. */\nexport const CARD_VIEW_FIELDS: readonly (keyof CardView)[] = [\n 'id',\n 'number',\n 'title',\n 'description',\n 'column',\n 'columnSlug',\n 'statusLine',\n 'waitingFor',\n 'priority',\n 'labels',\n 'executionMode',\n 'parentIssueId',\n 'attemptCount',\n 'activeRun',\n 'updatedAt',\n]\n\n/** The run going on right now, as much as a line in a card list needs. */\ninterface CardActiveRunView {\n id: string\n agentName: string\n state: IssueRun['state']\n startedAt: number\n}\n\n/** A column of the board, as the command line prints it. */\nexport interface ColumnView {\n id: string\n name: string\n /** What `kanbo card move` and the prime text call this column. */\n slug: string\n description: string | null\n category: IssueStatus['category']\n order: number\n /** What a card must satisfy before an agent may move it here; empty for nothing. */\n entryRules: EntryRule[]\n}\n\n/** A milestone read as a sprint, as the command line prints it. Dates are unix seconds. */\nexport interface SprintView {\n id: string\n title: string\n status: BoardSprint['status']\n startDate: number | null\n dueDate: number | null\n /** The open sprint running today — at most one per board. */\n current: boolean\n /** Cards not yet in a completed or canceled column: what closing the sprint would carry on. */\n openCards: number\n doneCards: number\n}\n\n/** Every field `kanbo sprint list --json` may ask for, in listing order. */\nexport const SPRINT_VIEW_FIELDS: readonly (keyof SprintView)[] = [\n 'id',\n 'title',\n 'status',\n 'startDate',\n 'dueDate',\n 'current',\n 'openCards',\n 'doneCards',\n]\n\n/** One launch of a card, as the command line prints it. */\nexport interface RunView {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n worktreePath: string | null\n startedAt: number\n endedAt: number | null\n /** Which launch of the card this is, counting from its first — `listRuns` counts them. */\n attempt: number\n chatSessionId: string | null\n /** Where the agent's own log of this run lives, once it said (ruling 7-1): `claude:<id>` or `codex:<id>`, else `null`. */\n externalSessionRef: string | null\n launchedByKind: IssueRun['launchedByKind']\n}\n\n/** Every field `kanbo run start|finish|attach-session --json` may ask for, in listing order. */\nexport const RUN_VIEW_FIELDS: readonly (keyof RunView)[] = [\n 'id',\n 'issueId',\n 'agentName',\n 'state',\n 'executionMode',\n 'branch',\n 'worktreePath',\n 'startedAt',\n 'endedAt',\n 'attempt',\n 'chatSessionId',\n 'externalSessionRef',\n 'launchedByKind',\n]\n\nexport function projectCard(card: Issue, columns: IssueStatus[], runs: BoardRunProjection): CardView {\n const view = toIssueView(card)\n const column = columns.find(candidate => candidate.id === card.statusId) ?? null\n\n return {\n id: view.id,\n number: view.number,\n title: view.title,\n description: view.description,\n column: column?.name ?? null,\n columnSlug: column ? normalizeStatusName(column.name) : null,\n statusLine: view.statusLine,\n waitingFor: view.waitingFor,\n priority: view.priority,\n labels: view.labels,\n executionMode: view.executionMode,\n parentIssueId: view.parentIssueId,\n attemptCount: runs.attemptCount,\n activeRun: projectCardActiveRun(runs.activeRun),\n updatedAt: view.updatedAt,\n }\n}\n\n/** The run going on right now, trimmed to what a card line needs — not the agent id, the branch or the session behind it. */\nfunction projectCardActiveRun(run: BoardActiveRun | null): CardActiveRunView | null {\n return run === null\n ? null\n : { id: run.id, agentName: run.agentName, state: run.state, startedAt: run.startedAt }\n}\n\nexport function projectColumn(column: IssueStatus): ColumnView {\n return {\n id: column.id,\n name: column.name,\n slug: normalizeStatusName(column.name),\n description: column.description,\n category: column.category,\n order: column.order,\n entryRules: readEntryRules(column),\n }\n}\n\nexport function projectSprint(sprint: BoardSprint): SprintView {\n return {\n id: sprint.id,\n title: sprint.title,\n status: sprint.status,\n startDate: sprint.startDate,\n dueDate: sprint.dueDate,\n current: sprint.current,\n openCards: sprint.cards.open,\n doneCards: sprint.cards.done,\n }\n}\n\nexport function projectRun(run: BoardRunView): RunView {\n return {\n id: run.id,\n issueId: run.issueId,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n worktreePath: run.worktreePath,\n startedAt: run.startedAt,\n endedAt: run.endedAt,\n attempt: run.attempt,\n chatSessionId: run.chatSessionId,\n externalSessionRef: run.externalSessionRef,\n launchedByKind: run.launchedByKind,\n }\n}\n\n/** One card per line: the key, the column it is in, and what it is called (`cardDisplayTitle`). */\nexport function describeCards(cards: CardView[]): string {\n if (cards.length === 0) {\n return 'No cards'\n }\n\n const keyWidth = Math.max(...cards.map(card => card.id.length))\n const columnWidth = Math.max(...cards.map(card => (card.column ?? '—').length))\n return cards\n .map(card => `${card.id.padEnd(keyWidth)} ${(card.column ?? '—').padEnd(columnWidth)} ${cardDisplayTitle(card)}`)\n .join('\\n')\n}\n\n/** The whole card, for a person who asked about exactly one. */\nexport function describeCard(card: CardView): string {\n const lines = [\n `${card.id} ${card.column ?? '—'} ${card.executionMode}`,\n cardDisplayTitle(card),\n ]\n if (card.description) {\n lines.push('', card.description)\n }\n const facts = [\n card.statusLine ? `status line: ${card.statusLine}` : null,\n card.waitingFor === 'human' ? 'waiting for a person' : null,\n card.labels.length > 0 ? `labels: ${card.labels.join(', ')}` : null,\n card.priority === 'none' ? null : `priority: ${card.priority}`,\n card.parentIssueId ? `parent: ${card.parentIssueId}` : null,\n ].filter(fact => fact !== null)\n if (facts.length > 0) {\n lines.push('', ...facts)\n }\n return lines.join('\\n')\n}\n\n/** One column per line, in board order, with the slug a card is moved by. */\nexport function describeColumns(columns: ColumnView[]): string {\n if (columns.length === 0) {\n return 'This board has no columns yet'\n }\n\n const slugWidth = Math.max(...columns.map(column => column.slug.length))\n return columns\n .map(column => [\n `${column.slug.padEnd(slugWidth)} ${column.name}`,\n column.description ? ` — ${column.description}` : '',\n column.entryRules.length > 0 ? ` [requires: ${column.entryRules.join(', ')}]` : '',\n ].join(''))\n .join('\\n')\n}\n\n/** A unix-seconds date as the UTC day it falls on, `—` for none. */\nfunction formatDay(seconds: number | null): string {\n return seconds === null ? '—' : new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n\n/** One sprint per line: `*` on the current one, its dates, open and done cards, title and id. */\nexport function describeSprints(sprints: SprintView[]): string {\n if (sprints.length === 0) {\n return 'This board has no milestones yet'\n }\n\n return sprints\n .map(sprint => [\n sprint.current ? '*' : ' ',\n `${formatDay(sprint.startDate)} → ${formatDay(sprint.dueDate)}`,\n sprint.status.padEnd(6),\n `${sprint.openCards} open / ${sprint.doneCards} done`,\n `${sprint.title} (${sprint.id})`,\n ].join(' '))\n .join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { CARD_VIEW_FIELDS, describeCard, projectCard } from '../view'\n\ninterface ApproveOptions extends BoardCommandOptions {\n comment?: string\n}\n\n/**\n * `kanbo approve` — a person accepting the work on a card.\n *\n * The shell is checked before the board is even opened: a refusal has to leave\n * the database exactly as it found it, and the check costs nothing. The board\n * refuses a non-person actor of its own accord too — this is the same rule said\n * earlier, with a message a person can act on.\n */\nexport function registerApproveCommand(program: Command): void {\n withBoardOptions(program\n .command('approve')\n .description('accept the work on a card that is waiting for you')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--comment <text>', 'what to say alongside the decision'))\n .action(async (reference: string, options: ApproveOptions) => {\n const actor = requireHumanActor('approve')\n\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const approved = await session.ops.approve(existing.id, { comment: options.comment }, actor)\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(approved.id),\n ])\n const view = projectCard(approved, columns, runs)\n return { value: view, text: `Approved ${view.id}\\n\\n${describeCard(view)}`, fields: CARD_VIEW_FIELDS }\n })\n })\n}\n","import type { KanboCapabilities, KanboCapabilityCommand, KanboCapabilityTool } from './manifest'\n\n/**\n * The same manifest `buildCapabilitiesManifest` returns, as Markdown a person\n * can read and an agent can paste into its own `CLAUDE.md` or `AGENTS.md`.\n *\n * Nothing here decides what the board can do — it only lays the manifest's own\n * fields out under headings. `kanbo capabilities` prints this by default, and\n * `kanbo mcp` serves it as `kanbo://capabilities.md` beside the JSON.\n */\n\n/**\n * A table cell, safe from a pipe inside the text breaking the row it sits in —\n * the same escape the package README's own tables use for a command that\n * spells `\\|` between two states.\n */\nfunction cell(text: string): string {\n return text.replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ').trim()\n}\n\nfunction renderStorages(manifest: KanboCapabilities): string[] {\n const rows = manifest.storages.map(storage =>\n `| \\`${storage.kind}\\` | ${cell(storage.label)} | ${cell(storage.description)} | ${storage.schemaEpoch} |`)\n return [\n '## Storages',\n '',\n '| Kind | Label | Description | Schema epoch |',\n '| --- | --- | --- | --- |',\n ...rows,\n ]\n}\n\nfunction renderTool(tool: KanboCapabilityTool): string {\n return `| \\`${tool.name}\\` | \\`${cell(tool.cli.kanbo)}\\` | ${cell(tool.description)} |`\n}\n\nfunction renderTools(manifest: KanboCapabilities): string[] {\n return [\n '## Tools',\n '',\n 'Full JSON Schema for each tool\\'s input is in the JSON manifest (`kanbo capabilities --json` or '\n + '`kanbo://capabilities`) — this table is the tools themselves and what to type instead of calling one.',\n '',\n '| Tool | `kanbo` | Does |',\n '| --- | --- | --- |',\n ...manifest.tools.map(renderTool),\n ]\n}\n\nfunction renderCommand(command: KanboCapabilityCommand): string {\n const options = command.options.map(option => `\\`${option.flags}\\`${option.mandatory ? ' (required)' : ''}`).join(', ')\n const humanOnly = command.humanOnly ? ' **Person only.**' : ''\n return `| \\`kanbo ${command.path}\\` | ${cell(command.description)}${humanOnly} | ${options ? cell(options) : '—'} |`\n}\n\nfunction renderCommands(manifest: KanboCapabilities): string[] {\n return [\n '## Commands',\n '',\n '| Command | Does | Options |',\n '| --- | --- | --- |',\n ...manifest.commands.map(renderCommand),\n ]\n}\n\nfunction renderRules(manifest: KanboCapabilities): string[] {\n return ['## Rules', '', ...manifest.rules.map(rule => `- ${rule}`)]\n}\n\nfunction renderEntryRules(manifest: KanboCapabilities): string[] {\n return [\n '## Column entry rules',\n '',\n 'What a column may ask of a card before an agent moves it in. Which columns ask for which is per board: '\n + '`kanbo columns list` or `kanbo_columns`.',\n '',\n ...manifest.entryRules.map(entry => `- \\`${entry.rule}\\` — ${entry.description}`),\n ]\n}\n\nfunction renderCanonicalStrings(manifest: KanboCapabilities): string[] {\n const rows = Object.entries(manifest.canonicalStrings)\n .map(([name, value]) => `| ${cell(name)} | \\`${cell(value)}\\` |`)\n return [\n '## Canonical strings',\n '',\n 'What the board writes for you, on a status line or in a system comment. A value ending in a space or `: ` '\n + 'is a prefix — the rest of the line is whatever the write was about.',\n '',\n '| Name | Value |',\n '| --- | --- |',\n ...rows,\n ]\n}\n\nfunction renderRoles(manifest: KanboCapabilities): string[] {\n return [\n '## Roles',\n '',\n `- Person: \\`${manifest.roles.person}\\``,\n `- Agent: \\`${manifest.roles.agent}\\``,\n '',\n 'What the database itself refuses the agent role, on an external board, whichever client sends the write:',\n '',\n ...manifest.roles.refusals.map(refusal => `- ${refusal}`),\n ]\n}\n\nfunction renderLimits(manifest: KanboCapabilities): string[] {\n return ['## Limits', '', ...manifest.limits.map(limit => `- ${limit}`)]\n}\n\n/** The manifest, laid out under one heading per field — stable and diffable, never a snapshot to update by hand. */\nexport function renderCapabilitiesMarkdown(manifest: KanboCapabilities): string {\n return [\n '# Kanbo capabilities',\n '',\n `\\`${manifest.package.name}\\` ${manifest.package.version} — capabilities manifest version ${manifest.version}.`,\n '',\n ...renderStorages(manifest),\n '',\n ...renderTools(manifest),\n '',\n ...renderCommands(manifest),\n '',\n ...renderRules(manifest),\n '',\n ...renderEntryRules(manifest),\n '',\n ...renderCanonicalStrings(manifest),\n '',\n ...renderRoles(manifest),\n '',\n ...renderLimits(manifest),\n '',\n ].join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport { buildCapabilitiesManifest } from '../../capabilities/manifest'\nimport { renderCapabilitiesMarkdown } from '../../capabilities/markdown'\nimport { CliError } from '../output'\n\n/**\n * `kanbo capabilities` — the board's tools, commands, rules and limits, for\n * whoever is deciding how to work it rather than working a card right now.\n *\n * No board is opened. Everything this command prints is board-independent —\n * a board's columns are its own and `kanbo prime` is where those live — so\n * there is nothing here to fail on a missing `--db` or a stale schema, and\n * none of the ordinary board flags apply. `kanbo mcp` serves the same two\n * shapes as resources, `kanbo://capabilities` and `kanbo://capabilities.md`,\n * from the same `buildCapabilitiesManifest`.\n */\ninterface CapabilitiesOptions {\n json?: boolean\n markdown?: boolean\n}\n\nexport function registerCapabilitiesCommand(program: Command): void {\n program\n .command('capabilities')\n .description('the board\\'s tools, commands, rules and limits — machine-readable, no board needed')\n .option('--json', 'print the manifest as JSON')\n .option('--markdown', 'print the manifest as Markdown (the default)')\n .action((options: CapabilitiesOptions) => {\n if (options.json && options.markdown) {\n throw new CliError(1, 'Pass --json or --markdown, not both.')\n }\n\n const manifest = buildCapabilitiesManifest()\n console.log(options.json ? JSON.stringify(manifest, null, 2) : renderCapabilitiesMarkdown(manifest))\n })\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCardWrite, UpdateCardInput } from '../../ops/cards'\nimport type { Issue, IssuePullRequest } from '../../sqlite/schema'\nimport { createCliActor, createPlacementActor } from '../actor'\nimport type { BoardCommandOptions, BoardSession } from '../command'\nimport { parseCount, parseExecutionMode, requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport type { CardView } from '../view'\nimport { CARD_VIEW_FIELDS, describeCard, describeCards, projectCard } from '../view'\n\n/**\n * `kanbo card …` — the cards themselves.\n *\n * Every write here is filed under the shell that made it and goes through the\n * board's own operations, so a card an agent moved from a terminal reads on the\n * board exactly like one moved in the app: the same field history, the same\n * status line, the same board version bump for whoever is watching.\n */\n\nconst PRIORITIES = ['none', 'low', 'medium', 'high', 'urgent'] as const\n\ninterface ListOptions extends BoardCommandOptions {\n column?: string\n limit?: number\n}\n\ninterface CreateOptions extends BoardCommandOptions {\n title?: string\n description?: string\n column?: string\n parent?: string\n executionMode?: Issue['executionMode']\n}\n\ninterface UpdateOptions extends BoardCommandOptions {\n title?: string\n description?: string\n priority?: Issue['priority']\n labels?: string[]\n executionMode?: Issue['executionMode']\n}\n\ninterface TextOptions extends BoardCommandOptions {\n text: string\n}\n\ninterface ContentOptions extends BoardCommandOptions {\n content: string\n}\n\ninterface WaitApprovalOptions extends BoardCommandOptions {\n text?: string\n}\n\nexport function registerCardCommands(program: Command): void {\n const card = program\n .command('card')\n .description('the cards on this board')\n\n withBoardOptions(card\n .command('list')\n .description('cards in board order')\n .option('--column <column>', 'only cards in this column, by slug, name or id')\n .option('--limit <count>', 'how many cards to print', parseCount))\n .action(async (options: ListOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const columns = await session.ops.listColumns(session.workspace.id)\n const wanted = options.column\n ? await session.ops.findColumn(session.workspace.id, options.column)\n : null\n if (options.column && !wanted) {\n throw new CliError(1, `This board has no column \"${options.column}\".`)\n }\n\n const rows = (await session.store.issues.listInBoardOrder(session.workspace.id))\n .filter(row => !wanted || row.statusId === wanted.id)\n .slice(0, options.limit ?? Number.POSITIVE_INFINITY)\n const runs = await session.ops.readBoardProjectionForIssues(rows.map(row => row.id))\n const cards = rows.map((row, index) => projectCard(row, columns, runs[index]))\n return { value: cards, text: describeCards(cards), fields: CARD_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(card\n .command('get')\n .description('one card, by key or number')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12'))\n .action(async (reference: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n return await presentCard(session, await requireCard(session, reference))\n })\n })\n\n withBoardOptions(card\n .command('create')\n .description('put a new card on the board')\n .option('--title <title>', 'what the card is called; the card\\'s own key when absent')\n .option('--description <text>', 'what the card is about')\n .option('--column <column>', 'the column to put it in, by slug, name or id')\n .option('--parent <card>', 'the card this one belongs under')\n .option('--execution-mode <mode>', 'worktree or main', parseExecutionMode))\n .action(async (options: CreateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const parent = options.parent ? await requireCard(session, options.parent) : null\n const created = await session.ops.createCard({\n workspace: session.workspace,\n title: options.title,\n description: options.description,\n statusName: options.column,\n parentIssueId: parent?.id,\n executionMode: options.executionMode,\n }, createPlacementActor())\n return await presentCard(session, created)\n })\n })\n\n withBoardOptions(card\n .command('update')\n .description('change a card\\'s fields')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--title <title>', 'what the card is called')\n .option('--description <text>', 'what the card is about')\n .option('--priority <priority>', `one of ${PRIORITIES.join(', ')}`, parsePriority)\n .option('--labels <labels>', 'comma-separated labels, replacing the ones the card has', parseLabels)\n .option('--execution-mode <mode>', 'worktree or main', parseExecutionMode))\n .action(async (reference: string, options: UpdateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const input = readUpdateInput(options)\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.updateCard(existing.id, input, createPlacementActor()))\n })\n })\n\n withBoardOptions(card\n .command('move')\n .description('move a card to another column')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<column>', 'the column, by slug, name or id'))\n .action(async (reference: string, column: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.moveCard(existing.id, column, createPlacementActor()))\n })\n })\n\n withBoardOptions(card\n .command('status-line')\n .description('say what the card is doing right now')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--text <text>', 'one sentence, present tense'))\n .action(async (reference: string, options: TextOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.setStatusLine(existing.id, options.text, createCliActor()))\n })\n })\n\n withBoardOptions(card\n .command('comment')\n .description('write a finding, a decision or a question on the card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--content <text>', 'what to say'))\n .action(async (reference: string, options: ContentOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const comment = await session.ops.addComment({ issueId: existing.id, content: options.content }, createCliActor())\n return {\n value: { id: comment.id, issueId: comment.issueId, content: comment.content, createdAt: comment.createdAt },\n text: `Commented on ${existing.id}`,\n }\n })\n })\n\n withBoardOptions(card\n .command('wait-approval')\n .description('hand the card to a person and end your turn')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--text <text>', 'the status line to leave, saying what you need'))\n .action(async (reference: string, options: WaitApprovalOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const waiting = await session.ops.waitApproval(existing.id, { statusLine: options.text }, createCliActor())\n return await presentCard(session, waiting)\n })\n })\n\n registerPullRequestCommands(card)\n}\n\n/** The fields a pull-request link prints, and the ones `--json` may name. */\nconst PULL_REQUEST_FIELDS = ['id', 'issueId', 'owner', 'repo', 'number', 'url', 'createdAt'] as const\n\ntype PullRequestView = Pick<IssuePullRequest, typeof PULL_REQUEST_FIELDS[number]>\n\nfunction projectPullRequest(link: IssuePullRequest): PullRequestView {\n return {\n id: link.id,\n issueId: link.issueId,\n owner: link.owner,\n repo: link.repo,\n number: link.number,\n url: link.url,\n createdAt: link.createdAt,\n }\n}\n\nfunction describePullRequest(link: PullRequestView): string {\n return `${link.id} ${link.owner}/${link.repo}#${link.number} ${link.url}`\n}\n\n/**\n * `kanbo card pr …` — the pull requests a card names (ruling 4-1).\n *\n * Linking is all an agent does: what the pull request is — open, merged, green\n * — reaches the card as comments from whoever watches GitHub, so nothing here\n * writes one.\n */\nfunction registerPullRequestCommands(card: Command): void {\n const pr = card\n .command('pr')\n .description('the pull requests a card names')\n\n withBoardOptions(pr\n .command('add')\n .description('link a pull request to the card; linking it again changes nothing')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<url>', 'the pull request, as https://github.com/<owner>/<repo>/pull/<n> or <owner>/<repo>#<n>'))\n .action(async (reference: string, url: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const link = projectPullRequest(await session.ops.linkPullRequest(existing.id, url, createCliActor()))\n return {\n value: link,\n text: `Linked ${link.owner}/${link.repo}#${link.number} to ${existing.id} (${link.id})`,\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n\n withBoardOptions(pr\n .command('list')\n .description('the pull requests linked to the card, in the order they were linked')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12'))\n .action(async (reference: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const existing = await requireCard(session, reference)\n const links = (await session.ops.listPullRequests(existing.id)).map(projectPullRequest)\n return {\n value: links,\n text: links.length === 0 ? `${existing.id} names no pull requests.` : links.map(describePullRequest).join('\\n'),\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n\n withBoardOptions(pr\n .command('remove')\n .description('unlink a pull request from the card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<linkId>', 'the link, by the id `kanbo card pr list` prints'))\n .action(async (reference: string, linkId: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const link = projectPullRequest(await session.ops.unlinkPullRequest(existing.id, linkId, createCliActor()))\n return {\n value: link,\n text: `Unlinked ${link.owner}/${link.repo}#${link.number} from ${existing.id}`,\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n}\n\n/**\n * The card as both shapes the output contract offers. A card a person placed\n * in a column whose entry rules it does not meet (ruling 6-5) also leaves one\n * warning line per unmet rule on stderr, so the card prints as usual.\n */\nasync function presentCard(session: BoardSession, card: BoardCardWrite): Promise<{ value: CardView, text: string, fields: readonly string[] }> {\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(card.id),\n ])\n const view = projectCard(card, columns, runs)\n for (const item of card.unmetRules ?? []) {\n console.error(`kanbo: warning: ${view.id} entered \"${view.column ?? '—'}\" without ${item.rule}: ${item.detail}`)\n }\n return { value: view, text: describeCard(view), fields: CARD_VIEW_FIELDS }\n}\n\n/**\n * The fields `card update` was asked to change. A command that names none of\n * them is refused rather than obeyed: an empty update still bumps the board\n * version, and every reader would be told the board changed when it did not.\n */\nfunction readUpdateInput(options: UpdateOptions): UpdateCardInput {\n const input: UpdateCardInput = {}\n if (options.title !== undefined) {\n input.title = options.title\n }\n if (options.description !== undefined) {\n input.description = options.description\n }\n if (options.priority !== undefined) {\n input.priority = options.priority\n }\n if (options.labels !== undefined) {\n input.labels = options.labels\n }\n if (options.executionMode !== undefined) {\n input.executionMode = options.executionMode\n }\n\n if (Object.keys(input).length === 0) {\n throw new CliError(1, 'Nothing to update. Pass --title, --description, --priority, --labels or --execution-mode.')\n }\n return input\n}\n\nfunction parsePriority(value: string): Issue['priority'] {\n const priority = PRIORITIES.find(candidate => candidate === value)\n if (!priority) {\n throw new CliError(1, `Unknown priority \"${value}\". Use one of ${PRIORITIES.join(', ')}.`)\n }\n return priority\n}\n\nfunction parseLabels(value: string): string[] {\n return value.split(',').map(label => label.trim()).filter(label => label.length > 0)\n}\n","import type { Command } from 'commander'\n\nimport { ENTRY_RULES } from '../../domain/entry-rules'\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { describeColumns, projectColumn } from '../view'\n\ninterface DescribeColumnOptions extends BoardCommandOptions {\n text: string\n}\n\ninterface ColumnRulesOptions extends BoardCommandOptions {\n require?: string\n clear?: boolean\n}\n\n/**\n * `kanbo columns …` — the board's columns.\n *\n * Listing them is how a person finds the slug a card is moved by; describing\n * one is how a person tells every agent that will ever read this board when a\n * card belongs in it, in one line they only have to write once. Setting its\n * rules is how a person keeps an agent from moving a card in before it is\n * ready — a person's command, refused in an agent's shell. Adding the\n * standard set brings a board made before them up to date without touching the\n * columns its owner made.\n */\nexport function registerColumnsCommands(program: Command): void {\n const columns = program\n .command('columns')\n .description('the columns of this board')\n\n withBoardOptions(columns\n .command('list')\n .description('every column in board order, with the slug a card is moved by'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const views = (await session.ops.listColumns(session.workspace.id)).map(projectColumn)\n return { value: views, text: describeColumns(views) }\n })\n })\n\n withBoardOptions(columns\n .command('describe')\n .description('say in one line when a card belongs in a column')\n .argument('<column>', 'the column, by slug, name or id')\n .requiredOption('--text <text>', 'what the column means'))\n .action(async (column: string, options: DescribeColumnOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const updated = await session.ops.describeColumn(session.workspace.id, column, options.text)\n const view = projectColumn(updated)\n return { value: view, text: describeColumns([view]) }\n })\n })\n\n withBoardOptions(columns\n .command('rules')\n .description('set what a card must satisfy before an agent may move it into a column')\n .argument('<column>', 'the column, by slug, name or id')\n .option('--require <rules>', `comma-separated: ${ENTRY_RULES.join(', ')}`)\n .option('--clear', 'ask nothing of a card entering the column'))\n .action(async (column: string, options: ColumnRulesOptions) => {\n // Checked before the board is opened, like `approve`: a refusal leaves\n // the board exactly as it found it.\n const actor = requireHumanActor('columns rules')\n const rules = readRequestedRules(options)\n\n await runBoardCommand(options, 'write', async (session) => {\n const updated = await session.ops.setColumnEntryRules(session.workspace.id, column, rules, actor)\n const view = projectColumn(updated)\n return { value: view, text: describeColumns([view]) }\n })\n })\n\n withBoardOptions(columns\n .command('add-standard')\n .description('add any of the standard columns this board is missing'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const { statuses, added } = await session.ops.addStandardColumns(session.workspace.id)\n const views = statuses.map(projectColumn)\n return {\n value: { added, columns: views },\n text: added.length === 0\n ? 'This board already has every standard column'\n : `Added ${added.join(', ')}\\n\\n${describeColumns(views)}`,\n }\n })\n })\n}\n\n/** The rules `--require` names, or `null` for `--clear` — exactly one of the two. */\nfunction readRequestedRules(options: ColumnRulesOptions): string[] | null {\n if (options.clear && options.require !== undefined) {\n throw new CliError(1, 'Pass --require or --clear, not both.')\n }\n if (options.clear) {\n return null\n }\n if (options.require === undefined) {\n throw new CliError(1, `Pass --require ${ENTRY_RULES.join(',')} (any of them) or --clear.`)\n }\n return options.require.split(',').map(rule => rule.trim()).filter(Boolean)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst here = dirname(fileURLToPath(import.meta.url))\n\n/**\n * The package root's `package.json`, found relative to the file asking for\n * it — the same reason `migrations-path.ts` resolves a candidate rather than\n * a fixed `../`: this file's own compiled output does not always land at the\n * same depth. One level up is the package root from `src/package-version.ts`\n * in a checkout and from `dist/cli.cjs`'s single bundle; two levels up is the\n * same root from a `dist/lib/*` chunk, because the library build nests every\n * entry — and the shared chunk this file becomes part of — one directory\n * deeper than the CLI's one bundle.\n */\nfunction resolvePackageJsonPath(): string {\n const oneUp = join(here, '..', 'package.json')\n return existsSync(oneUp) ? oneUp : join(here, '..', '..', 'package.json')\n}\n\n/**\n * The version a build wrote in. `tsdown` replaces this identifier with the\n * literal from `package.json` in both builds (`tsdown.cli.config.ts`,\n * `tsdown.lib.config.ts`), because a built copy may live where no\n * `package.json` exists at all — an app that embeds the command may ship `dist/` alone.\n */\ndeclare const __KANBO_PACKAGE_VERSION__: string | undefined\n\n/**\n * This build's own version, one source for the CLI's `--version`, the MCP\n * server's reported version and the capabilities manifest's\n * `package.version`. A build carries it as a literal; running from source\n * (tests, `tsx`) reads the package's own `package.json` instead.\n */\nexport const KANBO_PACKAGE_VERSION: string\n = typeof __KANBO_PACKAGE_VERSION__ === 'string'\n ? __KANBO_PACKAGE_VERSION__\n : (JSON.parse(readFileSync(resolvePackageJsonPath(), 'utf-8')) as { version: string }).version\n","import { maskDatabaseUrls } from '../domain/database-url'\nimport type { ColumnRefusalDetails } from '../domain/entry-rules'\nimport { describeColumnRefusal } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { CliError } from './output'\n\n/**\n * What a failed command says, and what it leaves with.\n *\n * One place, because a failure is output as much as a result is: the exit code\n * a caller may branch on, the sentence a person reads, and the chain underneath\n * it that says what actually went wrong.\n */\n\n/** How far down a chain of causes is still worth reading. */\nconst CAUSE_DEPTH_LIMIT = 5\n\n/**\n * What to say about a failure, and what to leave with.\n *\n * A `CliError` already carries both. A `BoardError` is a rule of the board the\n * caller broke: its code is the honest name of what happened, and its details\n * say which value did it, so both are printed rather than translated into a\n * sentence that could drift away from the rule. The one exception is a card\n * held at a column's door: its details are a list, and a person reads it as\n * one line per rule the card does not meet yet (`describeColumnRefusal`).\n *\n * Whatever it is, its causes are printed under it. A driver's failure arrives\n * wrapped — `Failed query: create schema …` with the connection refused\n * underneath — and the wrapper alone tells a person nothing they can act on.\n * Every line goes through the masking on its way out, because a message this\n * tool did not write is free to carry the whole connection string in it.\n */\nexport function describeFailure(error: unknown): { message: string, exitCode: number } {\n const { message, exitCode } = readFailure(error)\n const causes = describeCauses(error, 1)\n return { message: maskDatabaseUrls([message, ...causes].join('\\n')), exitCode }\n}\n\nfunction readFailure(error: unknown): { message: string, exitCode: number } {\n if (error instanceof CliError) {\n return { message: error.message, exitCode: error.exitCode }\n }\n if (error instanceof BoardError && error.code === 'board_column_rules_unmet') {\n return { message: describeColumnRefusal(error.details as unknown as ColumnRefusalDetails), exitCode: 1 }\n }\n if (error instanceof BoardError) {\n const details = error.details ? ` ${JSON.stringify(error.details)}` : ''\n return { message: `${error.code}${details}`, exitCode: 1 }\n }\n return { message: error instanceof Error ? error.message : String(error), exitCode: 1 }\n}\n\n/**\n * The chain under a failure, one indented line per link.\n *\n * Bounded, because `cause` is an ordinary property and nothing stops it\n * pointing back at the error it came from; a chain longer than this has stopped\n * explaining anything anyway.\n */\nfunction describeCauses(error: unknown, depth: number): string[] {\n const cause = error instanceof Error ? (error as { cause?: unknown }).cause : undefined\n if (cause === undefined || cause === null || depth > CAUSE_DEPTH_LIMIT) {\n return []\n }\n const message = cause instanceof Error ? cause.message : String(cause)\n return [`${' '.repeat(depth)}Caused by: ${message}`, ...describeCauses(cause, depth + 1)]\n}\n","import { existsSync, readFileSync } from 'node:fs'\n\nimport {\n COMMENT_RULE,\n MOVE_CARD_RULE,\n OWN_SESSION_RULE,\n PERSON_ONLY_RULE,\n STATUS_LINE_RULE,\n SUBTASK_RULE,\n WAIT_FOR_PERSON_RULE,\n} from '../../ops/agent-rules'\nimport type { FileChange } from './file-change'\n\n/**\n * The instruction block kanbo writes into an agent's standing instructions,\n * and the three things done to it: written, found again, taken out.\n *\n * Two versions of the block exist. The project block says \"this project is on\n * a board\"; the global one, for a person's user-level file, says \"a project\n * with `.kanbo/` is on a board\" and binds nothing — one user file serves every\n * project, and most of them have no board.\n */\n\n/** The markers that make the block something this tool can find again. */\nexport const INSTRUCTION_START = '<!-- KANBO_START -->'\nexport const INSTRUCTION_END = '<!-- KANBO_END -->'\n\n/**\n * What to run and when, and nothing about how to do the work: the method is\n * the person's own instructions to their agent. The rules are the sentences\n * `ops/agent-rules.ts` holds for every surface, with the command beside each.\n */\nconst RULE_LINES = [\n '- Take a card from `kanbo ready`.',\n `- ${MOVE_CARD_RULE} (\\`kanbo card move <id> <column>\\`)`,\n `- ${STATUS_LINE_RULE} (\\`kanbo card status-line <id> --text \"...\"\\`)`,\n `- ${SUBTASK_RULE} (\\`kanbo card create --description \"...\" --parent <id>\\`)`,\n `- ${COMMENT_RULE} (\\`kanbo card comment <id> --content \"...\"\\`)`,\n `- ${WAIT_FOR_PERSON_RULE} (\\`kanbo card wait-approval <id>\\`)`,\n `- ${PERSON_ONLY_RULE}`,\n `- ${OWN_SESSION_RULE} (\\`kanbo run start <id> --agent <name> --session <ref>\\`)`,\n '- Run `kanbo capabilities` for the full list of tools, commands and rules.',\n]\n\n/** The block `kanbo init --instructions …` writes into a project's own file. */\nexport const INSTRUCTION_BLOCK = [\n INSTRUCTION_START,\n '## Kanbo board',\n '',\n 'Work on this project is tracked on a kanbo board. Before starting a task run `kanbo prime` —'\n + ' it prints the board\\'s columns and what each one means.',\n '',\n ...RULE_LINES,\n INSTRUCTION_END,\n].join('\\n')\n\n/** The block `kanbo init --global` writes into a person's user-level file. */\nexport const GLOBAL_INSTRUCTION_BLOCK = [\n INSTRUCTION_START,\n '## Kanbo boards',\n '',\n 'A project with a `.kanbo/` directory tracks its work on a kanbo board. In such a project, before starting a task'\n + ' run `kanbo prime` (or call the `kanbo_prime` tool) — it prints the board\\'s columns and what each one means.'\n + ' A project without `.kanbo/` has no board: ignore this section there, and do not create one — a person sets a'\n + ' board up with `kanbo init`.',\n '',\n ...RULE_LINES,\n INSTRUCTION_END,\n].join('\\n')\n\n/**\n * The block in that file, once.\n *\n * A file that already carries the markers has its block replaced rather than\n * appended to, so a current block changes nothing at all and an outdated one is\n * brought forward. Everything outside the markers is the person's own writing\n * and is never touched.\n */\nexport function planInstructionBlock(path: string, block: string): FileChange {\n const existing = readText(path)\n const next = withBlock(existing, block)\n return { path, next: existing === next ? null : next }\n}\n\n/**\n * The file without the block, or no change when it carries none. The file\n * itself stays even when nothing is left in it: kanbo does not record which\n * files it created, and a person's file is not this tool's to delete.\n */\nexport function planInstructionBlockRemoval(path: string): FileChange {\n const existing = readText(path)\n const bounds = existing === null ? null : findBlock(existing)\n if (existing === null || bounds === null) {\n return { path, next: null }\n }\n const before = existing.slice(0, bounds.start).replace(/\\s*$/, '')\n const after = existing.slice(bounds.end).trim()\n const joined = [before, after].filter(part => part.length > 0).join('\\n\\n')\n return { path, next: joined ? `${joined}\\n` : '' }\n}\n\n/** The block a file carries, markers included, or `null` when it carries none. */\nexport function readInstructionBlock(path: string): string | null {\n const existing = readText(path)\n const bounds = existing === null ? null : findBlock(existing)\n return existing === null || bounds === null ? null : existing.slice(bounds.start, bounds.end)\n}\n\nfunction withBlock(existing: string | null, block: string): string {\n if (existing === null || !existing.trim()) {\n return `${block}\\n`\n }\n const bounds = findBlock(existing)\n if (bounds) {\n return existing.slice(0, bounds.start) + block + existing.slice(bounds.end)\n }\n return `${existing.replace(/\\s*$/, '')}\\n\\n${block}\\n`\n}\n\nfunction findBlock(text: string): { start: number, end: number } | null {\n const start = text.indexOf(INSTRUCTION_START)\n const end = text.indexOf(INSTRUCTION_END)\n return start >= 0 && end > start ? { start, end: end + INSTRUCTION_END.length } : null\n}\n\nfunction readText(path: string): string | null {\n return existsSync(path) ? readFileSync(path, 'utf8') : null\n}\n","import { accessSync, constants, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, isAbsolute, join } from 'node:path'\n\n/**\n * Where each agent tool keeps the files kanbo writes into, for one project and\n * for the person's whole machine.\n *\n * Resolved on every call rather than at import, so `HOME` and `CODEX_HOME` set\n * by a test (or a person) are the ones obeyed. Each path was checked against\n * the tool's own documentation; `docs/agents.md` lists the pages.\n */\n\n/** The tools whose user-level instruction file `kanbo init --global` can write. */\nexport const GLOBAL_INSTRUCTION_CLIENTS = ['claude', 'codex', 'gemini'] as const\nexport type GlobalInstructionClient = typeof GLOBAL_INSTRUCTION_CLIENTS[number]\n\n/** The files a project's agents read their standing instructions from. */\nexport const PROJECT_INSTRUCTION_FILES = { claude: 'CLAUDE.md', agents: 'AGENTS.md' } as const\nexport type ProjectInstructionTarget = keyof typeof PROJECT_INSTRUCTION_FILES\n\n/** The tools that can be told about the board's MCP server. */\nexport const MCP_CLIENTS = ['claude', 'codex', 'cursor'] as const\nexport type McpClient = typeof MCP_CLIENTS[number]\n\n/** Where Codex keeps its global files: `$CODEX_HOME`, else `~/.codex`. */\nexport function codexHome(): string {\n return process.env.CODEX_HOME?.trim() || join(homedir(), '.codex')\n}\n\n/** The user-level instruction file of each tool. */\nexport function globalInstructionPath(client: GlobalInstructionClient): string {\n switch (client) {\n case 'claude': return join(homedir(), '.claude', 'CLAUDE.md')\n case 'codex': return join(codexHome(), 'AGENTS.md')\n case 'gemini': return join(homedir(), '.gemini', 'GEMINI.md')\n }\n}\n\n/**\n * The user-level MCP configuration of each tool. Claude Code's is\n * `~/.claude.json`, its own state file: kanbo reads it but never writes it —\n * `claude mcp add` and `claude mcp remove` do.\n */\nexport function globalMcpConfigPath(client: McpClient): string {\n switch (client) {\n case 'claude': return join(homedir(), '.claude.json')\n case 'codex': return join(codexHome(), 'config.toml')\n case 'cursor': return join(homedir(), '.cursor', 'mcp.json')\n }\n}\n\n/** The project-level MCP configuration of each tool. */\nexport function projectMcpConfigPath(projectDir: string, client: McpClient): string {\n switch (client) {\n case 'claude': return join(projectDir, '.mcp.json')\n case 'codex': return join(projectDir, '.codex', 'config.toml')\n case 'cursor': return join(projectDir, '.cursor', 'mcp.json')\n }\n}\n\n/**\n * Every executable of that name on `PATH`, in the order a shell would find\n * them. A path with a separator in it is looked at directly, as a shell does.\n */\nexport function findAllOnPath(command: string): string[] {\n if (isAbsolute(command) || command.includes('/')) {\n return isExecutableFile(command) ? [command] : []\n }\n const extensions = process.platform === 'win32'\n ? ['', ...(process.env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';')]\n : ['']\n const found: string[] = []\n for (const directory of (process.env.PATH ?? '').split(delimiter).filter(Boolean)) {\n for (const extension of extensions) {\n const candidate = join(directory, command + extension)\n if (isExecutableFile(candidate) && !found.includes(candidate)) {\n found.push(candidate)\n }\n }\n }\n return found\n}\n\n/** The executable a shell would start for that command, or `null` when there is none. */\nexport function findOnPath(command: string): string | null {\n return findAllOnPath(command)[0] ?? null\n}\n\nfunction isExecutableFile(path: string): boolean {\n try {\n if (!statSync(path).isFile()) {\n return false\n }\n accessSync(path, constants.X_OK)\n return true\n }\n catch {\n return false\n }\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { CliError } from '../output'\nimport type { FileChange } from './file-change'\nimport { findOnPath, globalMcpConfigPath } from './paths'\n\n/**\n * The board's MCP server entry, in the two formats the tools keep it in: the\n * `mcpServers` JSON map Claude Code (`.mcp.json`) and Cursor (`mcp.json`) read,\n * and the `[mcp_servers.<name>]` TOML table Codex reads (`config.toml`). Each\n * is planned before anything is written, found again by the doctor, and taken\n * out by `kanbo uninstall` without touching any other server.\n */\n\n/** The name the board's MCP server is registered under, in every tool. */\nexport const MCP_SERVER_NAME = 'kanbo'\n\n/** How every registration kanbo writes starts the server. */\nexport const MCP_COMMAND = 'kanbo'\nexport const MCP_ARGS = ['mcp'] as const\n\n/** The line that registers the server with Claude Code for every project of this user. */\nexport const CLAUDE_USER_ADD = ['claude', 'mcp', 'add', '--scope', 'user', MCP_SERVER_NAME, '--', MCP_COMMAND, ...MCP_ARGS]\n/** The line that takes that registration out again. */\nexport const CLAUDE_USER_REMOVE = ['claude', 'mcp', 'remove', MCP_SERVER_NAME, '--scope', 'user']\n\n/**\n * A Codex table header naming this server or one of its subtables\n * (`[mcp_servers.kanbo.env]`), in every spelling TOML allows for the key: bare,\n * double-quoted and single-quoted all name the same server, and a check that\n * only knew the bare one would write a second table beside a registration the\n * person already has.\n */\nconst CODEX_HEADER_PATTERN = new RegExp(\n String.raw`^\\s*\\[\\s*mcp_servers\\s*\\.\\s*(?:${MCP_SERVER_NAME}|\"${MCP_SERVER_NAME}\"|'${MCP_SERVER_NAME}')\\s*(\\.[^\\]]*)?\\]`,\n)\n\n/** Any table header, which is where a table's keys end. */\nconst TOML_HEADER_PATTERN = /^\\s*\\[/\n\n/** A `command = \"...\"` line, either string spelling. */\nconst TOML_COMMAND_PATTERN = /^\\s*command\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/m\n\n/**\n * The `mcpServers` map, merged rather than replaced: the file may already\n * describe servers that have nothing to do with this board. A `kanbo` entry\n * that is already there belongs to the person — it may point at a different\n * binary or carry arguments of its own — and is left as it is.\n */\nexport function planJsonMcpServer(path: string): FileChange {\n const existing = readJsonConfig(path)\n if (existing.mcpServers && MCP_SERVER_NAME in existing.mcpServers) {\n return { path, next: null }\n }\n const next = {\n ...existing,\n mcpServers: { ...existing.mcpServers, [MCP_SERVER_NAME]: { type: 'stdio', command: MCP_COMMAND, args: [...MCP_ARGS] } },\n }\n return { path, next: `${JSON.stringify(next, null, 2)}\\n` }\n}\n\n/**\n * The same file without the `kanbo` entry. Everything else stays, the\n * `mcpServers` object included when nothing else is left in it: kanbo does not\n * record which files it created, so it leaves the structure rather than guess.\n * A file it cannot read as a JSON object is not touched at all.\n */\nexport function planJsonMcpServerRemoval(path: string): FileChange {\n const existing = readJsonIfObject(path)\n if (!existing || !isPlainObject(existing.mcpServers) || !(MCP_SERVER_NAME in existing.mcpServers)) {\n return { path, next: null }\n }\n const { [MCP_SERVER_NAME]: _removed, ...others } = existing.mcpServers\n return { path, next: `${JSON.stringify({ ...existing, mcpServers: others }, null, 2)}\\n` }\n}\n\n/**\n * The command a JSON registration starts the server with: `undefined` when the\n * file registers no `kanbo` server, `null` when it does but names no command\n * (a URL server, say).\n */\nexport function readJsonMcpCommand(path: string): string | null | undefined {\n const existing = readJsonIfObject(path)\n const servers = existing?.mcpServers\n if (!isPlainObject(servers) || !isPlainObject(servers[MCP_SERVER_NAME])) {\n return undefined\n }\n const command = servers[MCP_SERVER_NAME].command\n return typeof command === 'string' && command.trim() ? command.trim() : null\n}\n\n/** Codex keeps its servers in TOML tables, so the table is appended rather than merged. */\nexport function planCodexMcpServer(path: string): FileChange {\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : ''\n if (findCodexSections(existing).length > 0) {\n return { path, next: null }\n }\n const table = `[mcp_servers.${MCP_SERVER_NAME}]\\ncommand = \"${MCP_COMMAND}\"\\nargs = ${JSON.stringify(MCP_ARGS)}\\n`\n return { path, next: existing.trim() ? `${existing.replace(/\\s*$/, '')}\\n\\n${table}` : table }\n}\n\n/**\n * The same file without the `kanbo` table and its subtables. A table runs from\n * its header to the next header, so every other table — and every line before\n * the first — is kept byte for byte.\n */\nexport function planCodexMcpServerRemoval(path: string): FileChange {\n if (!existsSync(path)) {\n return { path, next: null }\n }\n const existing = readFileSync(path, 'utf8')\n const sections = findCodexSections(existing)\n if (sections.length === 0) {\n return { path, next: null }\n }\n const lines = existing.split('\\n')\n const removed = new Set(sections.flatMap(({ start, end }) => range(start, end)))\n const kept = lines.filter((_, index) => !removed.has(index))\n const next = kept.join('\\n').replace(/^\\n+/, '')\n return { path, next: next.trim() ? `${next.replace(/\\s*$/, '')}\\n` : '' }\n}\n\n/** The command the Codex table starts the server with, with the same `undefined`/`null` meaning as the JSON one. */\nexport function readCodexMcpCommand(path: string): string | null | undefined {\n if (!existsSync(path)) {\n return undefined\n }\n const text = readFileSync(path, 'utf8')\n const main = findCodexSections(text).find(section => section.main)\n if (!main) {\n return undefined\n }\n const body = text.split('\\n').slice(main.start, main.end).join('\\n')\n const match = TOML_COMMAND_PATTERN.exec(body)\n return match ? (match[1] ?? match[2] ?? null) : null\n}\n\n/** Is the server registered with Claude Code for every project of this user (`~/.claude.json`, read only)? */\nexport function readClaudeUserMcpCommand(): string | null | undefined {\n return readJsonMcpCommand(globalMcpConfigPath('claude'))\n}\n\n/** What happened to a registration that goes through the `claude` command. */\nexport interface ClaudeCliOutcome {\n /** `ran` when `claude` did it, `manual` when the person has to run the line themselves. */\n state: 'ran' | 'manual'\n command: string\n /** Why it is the person's to run, when it is. */\n reason?: string\n}\n\n/**\n * Run `claude mcp …` when Claude Code is here to run it, and hand the line to\n * the person when it is not — or when it failed, with the first line it said.\n * `~/.claude.json` is Claude Code's own state file and is never edited by hand.\n */\nexport function runClaudeMcp(argv: readonly string[]): ClaudeCliOutcome {\n const command = argv.join(' ')\n const executable = findOnPath(argv[0]!)\n if (!executable) {\n return { state: 'manual', command, reason: `${argv[0]} is not on PATH` }\n }\n const result = spawnSync(executable, argv.slice(1), { encoding: 'utf8', timeout: 60_000 })\n if (result.status === 0) {\n return { state: 'ran', command }\n }\n const said = `${result.stderr ?? ''}${result.stdout ?? ''}`.trim().split('\\n')[0]\n return {\n state: 'manual',\n command,\n reason: result.error ? result.error.message : `${argv[0]} exited ${result.status ?? 'on a signal'}${said ? `: ${said}` : ''}`,\n }\n}\n\n/** The line ranges (`end` exclusive) of the `kanbo` table and its subtables. */\nfunction findCodexSections(text: string): { start: number, end: number, main: boolean }[] {\n const lines = text.split('\\n')\n const sections: { start: number, end: number, main: boolean }[] = []\n for (let index = 0; index < lines.length; index += 1) {\n const match = CODEX_HEADER_PATTERN.exec(lines[index]!)\n if (!match) {\n continue\n }\n let end = index + 1\n while (end < lines.length && !TOML_HEADER_PATTERN.test(lines[end]!)) {\n end += 1\n }\n sections.push({ start: index, end, main: match[1] === undefined })\n index = end - 1\n }\n return sections\n}\n\nfunction range(start: number, end: number): number[] {\n return Array.from({ length: end - start }, (_, offset) => start + offset)\n}\n\n/**\n * The tool's configuration file as an object to merge into — or a refusal.\n *\n * A file that is valid JSON but not an object (a list, a string, `null`) is\n * something this command has no idea how to merge into, and spreading it would\n * quietly throw the person's file away. The file is left exactly as it is and\n * they are told which one to look at.\n */\nfunction readJsonConfig(path: string): { mcpServers?: Record<string, unknown> } {\n if (!existsSync(path)) {\n return {}\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'))\n }\n catch {\n throw new CliError(1, `${path} is not valid JSON. Fix or remove it, then run kanbo init again.`)\n }\n if (!isPlainObject(parsed)) {\n throw new CliError(1, `${path} does not hold a JSON object. Fix or remove it, then run kanbo init again.`)\n }\n if (parsed.mcpServers !== undefined && !isPlainObject(parsed.mcpServers)) {\n throw new CliError(1, `${path} has an \"mcpServers\" that is not an object. Fix it, then run kanbo init again.`)\n }\n return parsed as { mcpServers?: Record<string, unknown> }\n}\n\n/** The file as a JSON object, or `null` when it is missing or anything else — for reading and removal, which never refuse. */\nfunction readJsonIfObject(path: string): Record<string, unknown> | null {\n if (!existsSync(path)) {\n return null\n }\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n return isPlainObject(parsed) ? parsed : null\n }\n catch {\n return null\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { existsSync, realpathSync, statSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'\n\nimport { maskDatabaseUrl } from '../domain/database-url'\nimport { BoardError } from '../domain/errors'\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport { assertBoardSchema, openBoardDatabase } from '../sqlite/open-database'\nimport { isAgentShell } from './actor'\nimport type { KanboBinding } from './binding'\nimport { BINDING_FILE_PATH, readBinding } from './binding'\nimport type { BoardTarget } from './db-target'\nimport { resolveDbTarget } from './db-target'\nimport { describeFailure } from './failure'\nimport { openPostgresBoard } from './postgres-board'\nimport { assertInstalledBoard, FILE_SCHEMA_OUTDATED_MESSAGE, SCHEMA_OUTDATED_MESSAGE } from './schema-guard'\nimport { GLOBAL_INSTRUCTION_BLOCK, INSTRUCTION_BLOCK, readInstructionBlock } from './setup/instructions'\nimport { MCP_COMMAND, readCodexMcpCommand, readJsonMcpCommand } from './setup/mcp-config'\nimport type { McpClient } from './setup/paths'\nimport {\n findAllOnPath,\n findOnPath,\n GLOBAL_INSTRUCTION_CLIENTS,\n globalInstructionPath,\n globalMcpConfigPath,\n MCP_CLIENTS,\n PROJECT_INSTRUCTION_FILES,\n projectMcpConfigPath,\n} from './setup/paths'\n\n/**\n * `kanbo doctor` — the checks for the ways kanbo can be set up wrong without\n * any command saying so: a binding nobody can read, a board older than this\n * build, an instruction block written by an older kanbo, an MCP registration\n * whose command a client cannot start, a server that does not answer, another\n * `kanbo` earlier on `PATH`, a person's own shell marked as an agent's.\n *\n * Every check says `ok`, `warn` or `fail`, and every one that is not `ok`\n * carries the command or edit that fixes it. Nothing here writes anything.\n */\n\nexport type DoctorStatus = 'ok' | 'warn' | 'fail'\n\nexport interface DoctorFinding {\n check: string\n status: DoctorStatus\n detail: string\n /** What to do about it, on every finding that is not `ok`. */\n fix?: string\n}\n\nexport interface DoctorInput {\n /** The folder the doctor was run in; the project is looked for at or above it. */\n cwd: string\n /** The script this `kanbo` is running from, to tell it apart from another one on `PATH`. */\n self: string | null\n /** How long the MCP server may take to answer `initialize`. */\n handshakeTimeoutMs: number\n}\n\n/** How long an external board may take to answer before the doctor gives up on it. */\nconst POSTGRES_TIMEOUT_MS = 5_000\n\n/** A project folder: the one with `.kanbo/` in it, and the binding that folder has, if any. */\nexport interface ProjectState {\n root: string\n binding: KanboBinding | null\n}\n\n/** One MCP registration found in a tool's configuration. */\ninterface Registration {\n client: McpClient\n path: string\n command: string | null\n}\n\nexport async function collectDoctorFindings(input: DoctorInput): Promise<DoctorFinding[]> {\n const findings: DoctorFinding[] = [\n { check: 'version', status: 'ok', detail: `kanbo ${KANBO_PACKAGE_VERSION}${input.self ? ` (${input.self})` : ''}` },\n checkPath(input.self),\n ]\n\n const project = findKanboProject(input.cwd)\n findings.push(checkProject(project))\n const target = project?.binding ? resolveTarget(project.root) : null\n if (project?.binding && !project.binding.databaseUrl) {\n findings.push(await checkSqlite())\n }\n if (project?.binding && findings.every(finding => finding.check !== 'sqlite' || finding.status === 'ok')) {\n findings.push(...await checkBoard(target))\n }\n\n findings.push(...checkInstructions(project?.root ?? null))\n\n const registrations = findRegistrations(project?.root ?? null)\n findings.push(...checkRegistrations(registrations))\n if (project?.binding && !(target instanceof Error)) {\n findings.push(await checkHandshake(registrations, project.root, input.handshakeTimeoutMs))\n }\n\n findings.push(checkActor())\n return findings\n}\n\n/** Is the `kanbo` a client would start this one? */\nfunction checkPath(self: string | null): DoctorFinding {\n const all = findAllOnPath(MCP_COMMAND)\n if (all.length === 0) {\n return {\n check: 'path',\n status: 'warn',\n detail: 'No kanbo on PATH: a shell or MCP client that runs `kanbo` will not find it.',\n fix: 'Install kanbo globally (see docs/installation.md), or put the directory that holds it on PATH.',\n }\n }\n const first = all[0]!\n if (self && realPath(first) !== realPath(self)) {\n return {\n check: 'path',\n status: 'warn',\n detail: `${first} comes first on PATH and is not this kanbo (${self}): agents and MCP clients start that one.`,\n fix: 'Remove the other install, or put this one first on PATH.',\n }\n }\n return { check: 'path', status: 'ok', detail: `kanbo on PATH: ${first}` }\n}\n\n/** The nearest folder at or above `cwd` with a `.kanbo/` directory, and its binding. */\nexport function findKanboProject(cwd: string): ProjectState | null {\n let directory = resolve(cwd)\n while (true) {\n const kanboDirectory = join(directory, dirname(BINDING_FILE_PATH))\n if (existsSync(kanboDirectory) && statSync(kanboDirectory).isDirectory()) {\n return { root: directory, binding: readBinding(join(directory, BINDING_FILE_PATH)) }\n }\n const parent = dirname(directory)\n if (parent === directory) {\n return null\n }\n directory = parent\n }\n}\n\nfunction checkProject(project: ProjectState | null): DoctorFinding {\n if (!project) {\n return {\n check: 'binding',\n status: 'warn',\n detail: 'Not inside a kanbo project (no .kanbo/ here or above); board checks are skipped.',\n fix: 'Run kanbo init --file in a project to give it a board.',\n }\n }\n const path = join(project.root, BINDING_FILE_PATH)\n if (!project.binding) {\n return {\n check: 'binding',\n status: 'fail',\n detail: existsSync(path)\n ? `${path} is not a binding this build can read.`\n : `${join(project.root, dirname(BINDING_FILE_PATH))} has no ${BINDING_FILE_PATH.split(/[\\\\/]/).pop()}: nothing says which board this project is on.`,\n fix: `Run kanbo init (with --file, or the --database-url you used) in ${project.root} to bind it again.`,\n }\n }\n return { check: 'binding', status: 'ok', detail: `${path}: workspace ${project.binding.workspaceId}` }\n}\n\nfunction resolveTarget(root: string): BoardTarget | Error {\n try {\n return resolveDbTarget({ cwd: root })\n }\n catch (error) {\n return error instanceof Error ? error : new Error(String(error))\n }\n}\n\n/** The native module a board file needs, loaded on a database of its own in memory. */\nasync function checkSqlite(): Promise<DoctorFinding> {\n try {\n (await openBoardDatabase(':memory:')).close()\n return { check: 'sqlite', status: 'ok', detail: 'better-sqlite3 loads.' }\n }\n catch (error) {\n return {\n check: 'sqlite',\n status: 'fail',\n detail: `better-sqlite3 cannot be loaded: ${describeFailure(error).message}`,\n fix: 'Install better-sqlite3 next to kanbo (npm install -g better-sqlite3 for a global kanbo).',\n }\n }\n}\n\n/** The board itself: it opens, and its schema is this build's. */\nasync function checkBoard(target: BoardTarget | Error | null): Promise<DoctorFinding[]> {\n if (target === null) {\n return []\n }\n if (target instanceof Error) {\n return [{ check: 'board', status: 'fail', detail: describeFailure(target).message, fix: 'Run kanbo init in the project to bind it to a board.' }]\n }\n if (target.kind === 'postgres') {\n return [await checkPostgresBoard(target.url)]\n }\n\n const board = await openBoardDatabase(target.path)\n try {\n assertBoardSchema(board.database)\n return [{ check: 'board', status: 'ok', detail: `${target.path} opens; its schema is current.` }]\n }\n catch (error) {\n const outdated = error instanceof BoardError && error.code === 'board_schema_outdated'\n const ownFile = target.owner === 'kanbo'\n return [{\n check: 'board',\n status: 'fail',\n detail: outdated\n ? `${target.path}: ${ownFile ? FILE_SCHEMA_OUTDATED_MESSAGE : SCHEMA_OUTDATED_MESSAGE}`\n : `${target.path}: ${describeFailure(error).message}`,\n fix: ownFile ? 'Run kanbo migrate.' : 'Start the app this database belongs to, so it applies its migrations.',\n }]\n }\n finally {\n board.close()\n }\n}\n\nasync function checkPostgresBoard(url: string): Promise<DoctorFinding> {\n const where = maskDatabaseUrl(url)\n let timer: NodeJS.Timeout | undefined\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`no answer within ${POSTGRES_TIMEOUT_MS / 1000}s`)), POSTGRES_TIMEOUT_MS)\n })\n try {\n const board = await Promise.race([openPostgresBoard(url), timeout])\n try {\n await Promise.race([assertInstalledBoard(board.database), timeout])\n }\n finally {\n void board.close()\n }\n return { check: 'board', status: 'ok', detail: `${where} answers; its schema is current.` }\n }\n catch (error) {\n return { check: 'board', status: 'fail', detail: `${where}: ${describeFailure(error).message}`, fix: 'Check the connection string, then run kanbo migrate.' }\n }\n finally {\n clearTimeout(timer)\n }\n}\n\n/**\n * Every instruction block kanbo could have written, compared with the one this\n * build writes. Only files that carry a block are reported: a file without one\n * is not a fault, but having no block anywhere is worth a warning.\n */\nfunction checkInstructions(root: string | null): DoctorFinding[] {\n const findings: DoctorFinding[] = []\n if (root) {\n for (const [target, name] of Object.entries(PROJECT_INSTRUCTION_FILES)) {\n const path = join(root, name)\n const block = readInstructionBlock(path)\n if (block !== null) {\n findings.push(blockFinding(path, block === INSTRUCTION_BLOCK, `cd ${root} && kanbo init --instructions ${target} --yes`))\n }\n }\n }\n for (const client of GLOBAL_INSTRUCTION_CLIENTS) {\n const path = globalInstructionPath(client)\n const block = readInstructionBlock(path)\n if (block !== null) {\n findings.push(blockFinding(path, block === GLOBAL_INSTRUCTION_BLOCK, `kanbo init --global --instructions ${client}`))\n }\n }\n if (findings.length === 0) {\n findings.push({\n check: 'instructions',\n status: 'warn',\n detail: 'No kanbo instruction block in this project\\'s or your own agent files: agents learn the rules only from the MCP server.',\n fix: 'Run kanbo init --instructions claude in the project, or kanbo init --global.',\n })\n }\n return findings\n}\n\nfunction blockFinding(path: string, current: boolean, fix: string): DoctorFinding {\n return current\n ? { check: 'instructions', status: 'ok', detail: `${path}: block is current.` }\n : { check: 'instructions', status: 'fail', detail: `${path}: block differs from the one this kanbo writes (stale).`, fix: `Run ${fix} to rewrite it.` }\n}\n\n/** Every `kanbo` registration in the project's and the person's own tool configuration. */\nfunction findRegistrations(root: string | null): Registration[] {\n const found: Registration[] = []\n for (const client of MCP_CLIENTS) {\n const paths = [...(root ? [projectMcpConfigPath(root, client)] : []), globalMcpConfigPath(client)]\n for (const path of paths) {\n const command = client === 'codex' ? readCodexMcpCommand(path) : readJsonMcpCommand(path)\n if (command !== undefined) {\n found.push({ client, path, command })\n }\n }\n }\n return found\n}\n\nfunction checkRegistrations(registrations: Registration[]): DoctorFinding[] {\n if (registrations.length === 0) {\n return [{\n check: 'mcp',\n status: 'warn',\n detail: 'No MCP client has the kanbo server registered.',\n fix: 'Run kanbo init --mcp claude,codex,cursor in the project, or kanbo init --global.',\n }]\n }\n return registrations.map(({ client, path, command }) => {\n const check = `mcp:${client}`\n if (command === null) {\n return { check, status: 'warn', detail: `${path}: the kanbo entry names no command.`, fix: `Set \"command\" to kanbo in ${path}.` }\n }\n const resolved = findOnPath(command)\n return resolved\n ? { check, status: 'ok', detail: `${path}: starts ${command} (${resolved}).` }\n : {\n check,\n status: 'fail',\n detail: `${path}: starts ${command}, which is not on PATH — the client cannot start the server.`,\n fix: `Install kanbo globally, or change the command in ${path} to a path that exists.`,\n }\n })\n}\n\n/**\n * Start the server a client would start, from the project folder, and ask it\n * `initialize`. It has to call itself `kanbo` and send its instructions.\n */\nasync function checkHandshake(registrations: Registration[], root: string, timeoutMs: number): Promise<DoctorFinding> {\n const command = registrations.map(entry => entry.command && findOnPath(entry.command)).find(Boolean) ?? findOnPath(MCP_COMMAND)\n if (!command) {\n return { check: 'mcp:handshake', status: 'warn', detail: 'No kanbo to start, so the MCP server was not tried.', fix: 'Put kanbo on PATH.' }\n }\n\n const transport = new StdioClientTransport({ command, args: ['mcp'], cwd: root, env: stringEnvironment(), stderr: 'pipe' })\n let stderr = ''\n transport.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n const client = new Client({ name: 'kanbo-doctor', version: KANBO_PACKAGE_VERSION })\n try {\n await client.connect(transport, { timeout: timeoutMs })\n const name = client.getServerVersion()?.name\n const instructions = client.getInstructions()\n if (name !== 'kanbo' || !instructions?.trim()) {\n return {\n check: 'mcp:handshake',\n status: 'fail',\n detail: `${command} mcp answered as ${name ?? 'nothing'}${instructions?.trim() ? '' : ', without instructions'}.`,\n fix: 'Put a current kanbo first on PATH.',\n }\n }\n return { check: 'mcp:handshake', status: 'ok', detail: `${command} mcp answers initialize as kanbo, with instructions.` }\n }\n catch (error) {\n const said = stderr.trim().split('\\n')[0]\n return {\n check: 'mcp:handshake',\n status: 'fail',\n detail: `${command} mcp did not answer initialize: ${said || describeFailure(error).message}`,\n fix: 'Run kanbo mcp in the project yourself to see why it does not start.',\n }\n }\n finally {\n await client.close().catch(() => {})\n }\n}\n\nfunction checkActor(): DoctorFinding {\n if (isAgentShell()) {\n return {\n check: 'actor',\n status: 'warn',\n detail: 'This shell says it belongs to an agent (KANBO_ACTOR_KIND=agent): approve and return refuse here.',\n fix: 'If this is your own terminal, remove KANBO_ACTOR_KIND from your shell profile.',\n }\n }\n return { check: 'actor', status: 'ok', detail: 'This shell is a person\\'s.' }\n}\n\n/** The environment as the SDK wants it: every value a string, so `KANBO_*` reaches the server as it would from a client. */\nfunction stringEnvironment(): Record<string, string> {\n return Object.fromEntries(Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined))\n}\n\nfunction realPath(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n","import type { Command } from 'commander'\n\nimport { KANBO_PACKAGE_VERSION } from '../../package-version'\nimport type { DoctorFinding } from '../doctor'\nimport { collectDoctorFindings } from '../doctor'\nimport { CliError } from '../output'\n\n/**\n * `kanbo doctor` — check this install, this project and the agent tools that\n * reach it, and say what to do about anything wrong. Exits `1` when a check\n * fails; a warning alone does not.\n */\ninterface DoctorOptions {\n json?: boolean\n}\n\n/** How long `kanbo mcp` may take to answer `initialize`. */\nconst HANDSHAKE_TIMEOUT_MS = 10_000\n\nconst MARKS: Record<DoctorFinding['status'], string> = { ok: 'ok ', warn: 'warn', fail: 'FAIL' }\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('check this install, this project\\'s binding and board, instruction blocks and MCP registrations')\n .option('--json', 'print the findings as JSON')\n .action(async (options: DoctorOptions) => {\n const findings = await collectDoctorFindings({\n cwd: process.cwd(),\n self: process.argv[1] ?? null,\n handshakeTimeoutMs: HANDSHAKE_TIMEOUT_MS,\n })\n\n if (options.json) {\n console.log(JSON.stringify({ version: KANBO_PACKAGE_VERSION, findings }, null, 2))\n }\n else {\n for (const finding of findings) {\n console.log(`${MARKS[finding.status]} ${finding.check.padEnd(14)} ${finding.detail}`)\n if (finding.fix) {\n console.log(`${' '.repeat(21)}fix: ${finding.fix}`)\n }\n }\n }\n\n const failed = findings.filter(finding => finding.status === 'fail').length\n if (failed > 0) {\n throw new CliError(1, `kanbo doctor: ${failed} check${failed === 1 ? '' : 's'} failed.`)\n }\n })\n}\n","import { join } from 'node:path'\n\n/**\n * Where a board file of a project's own goes when nobody names a path —\n * `kanbo init --file` writes it here, and an app that attaches a project's\n * board file can import the same constant rather than spelling it again.\n */\nexport const DEFAULT_BOARD_FILE_PATH = join('.kanbo', 'board.db')\n","import { CliError } from '../output'\n\n/**\n * Show a plan of file changes and ask before carrying it out.\n *\n * `--yes` is the answer given in advance. A shell with nobody at its terminal\n * cannot be asked, so it is refused rather than taken as a yes: these commands\n * write into a person's own configuration, which is the kind of thing a person\n * says yes to. When the caller asked for machine output, stdout belongs to the\n * result and the plan goes to stderr.\n */\nexport async function confirmPlan(input: {\n preview: string\n question: string\n yes: boolean | undefined\n machineOutput: boolean\n /** The command to name in the refusal, e.g. `kanbo init --global`. */\n command: string\n}): Promise<boolean> {\n const print = input.machineOutput ? console.error : console.log\n print(input.preview)\n if (input.yes) {\n return true\n }\n if (!isInteractive()) {\n throw new CliError(1, `Nothing was changed. Run ${input.command} again with --yes to do this without being asked.`)\n }\n const prompts = await import('@clack/prompts')\n const answer = await prompts.confirm({ message: input.question })\n return answer === true\n}\n\n/** Is there a person on the other end of this shell to ask? */\nexport function isInteractive(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY)\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\n/**\n * One file kanbo may write, worked out before anything is written.\n *\n * Every setup command — `kanbo init`, `kanbo init --global`, `kanbo uninstall`\n * — first works out what each file would say afterwards, so the person can be\n * shown the whole plan and asked once, and a file that already says it is\n * reported as `unchanged` rather than rewritten.\n */\nexport interface FileChange {\n path: string\n /** The file's contents afterwards, or `null` when it already says this. */\n next: string | null\n}\n\n/** What happened to one file this command may write. */\nexport interface FileOutcome {\n path: string\n /** `written` when the file changed, `unchanged` when it already said this. */\n state: 'written' | 'unchanged'\n}\n\n/** Write the change, creating the directory it lives in, and say what happened. */\nexport function applyFileChange(change: FileChange): FileOutcome {\n if (change.next === null) {\n return { path: change.path, state: 'unchanged' }\n }\n mkdirSync(dirname(change.path), { recursive: true })\n writeFileSync(change.path, change.next)\n return { path: change.path, state: 'written' }\n}\n","import type { CliResult } from '../output'\nimport { CliError, printResult, readFormat } from '../output'\nimport { confirmPlan } from '../setup/confirm'\nimport type { FileChange } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { GLOBAL_INSTRUCTION_BLOCK, planInstructionBlock } from '../setup/instructions'\nimport {\n CLAUDE_USER_ADD,\n planCodexMcpServer,\n planJsonMcpServer,\n readClaudeUserMcpCommand,\n runClaudeMcp,\n} from '../setup/mcp-config'\nimport type { GlobalInstructionClient, McpClient } from '../setup/paths'\nimport { GLOBAL_INSTRUCTION_CLIENTS, globalInstructionPath, globalMcpConfigPath, MCP_CLIENTS } from '../setup/paths'\nimport type { InitOptions } from './init'\n\n/**\n * `kanbo init --global` — this person's own agent tools, told about kanbo once\n * for every project.\n *\n * It binds no board and creates none: the block it writes says \"a project with\n * `.kanbo/` is on a board\", and a project becomes one with a plain `kanbo init`.\n * The MCP registrations are the same `kanbo mcp` every project registration\n * starts, which resolves the board from the folder the client starts it in.\n *\n * Every change is worked out first and shown; nothing is written until the\n * person says yes (or said `--yes`), and a file that already says it is\n * reported `unchanged`.\n */\n\n/** Written when `--instructions` is not given. Gemini is asked for by name. */\nconst DEFAULT_INSTRUCTION_CLIENTS: GlobalInstructionClient[] = ['claude', 'codex']\n\n/** The flags that are about binding a project, which `--global` does not do. */\nconst PROJECT_ONLY_FLAGS = [\n ['file', '--file'],\n ['db', '--db'],\n ['databaseUrl', '--database-url'],\n ['workspace', '--workspace'],\n ['board', '--board'],\n ['identifier', '--identifier'],\n ['agentUrl', '--agent-url'],\n] as const satisfies readonly (readonly [keyof InitOptions, string])[]\n\n/** What happened to one MCP registration. */\ninterface GlobalMcpOutcome {\n client: McpClient\n path: string\n /** `manual` when the person has to run `command` themselves. */\n state: 'written' | 'unchanged' | 'manual'\n command?: string\n reason?: string\n}\n\ninterface GlobalInstructionOutcome {\n client: GlobalInstructionClient\n path: string\n state: 'written' | 'unchanged'\n}\n\nexport async function initGlobal(options: InitOptions): Promise<void> {\n const format = readFormat(options)\n const given = PROJECT_ONLY_FLAGS.filter(([key]) => options[key] !== undefined).map(([, flag]) => flag)\n if (given.length > 0) {\n throw new CliError(1, `--global binds no board; ${given.join(', ')} ${given.length === 1 ? 'is' : 'are'} for a project. `\n + 'Run kanbo init in the project for that.')\n }\n\n const instructionClients = parseGlobalInstructions(options.instructions)\n const mcpClients = options.mcp ?? [...MCP_CLIENTS]\n\n const instructionChanges = instructionClients.map(client => ({\n client,\n change: planInstructionBlock(globalInstructionPath(client), GLOBAL_INSTRUCTION_BLOCK),\n }))\n const fileMcpChanges = mcpClients.filter(client => client !== 'claude').map(client => ({\n client,\n change: planGlobalMcpFile(client),\n }))\n const claudeNeeded = mcpClients.includes('claude') && readClaudeUserMcpCommand() === undefined\n\n const pending = [...instructionChanges, ...fileMcpChanges].filter(({ change }) => change.next !== null)\n let confirmed = true\n if (pending.length > 0 || claudeNeeded) {\n confirmed = await confirmPlan({\n preview: describePlan(pending.map(({ change }) => change), claudeNeeded),\n question: 'Make these changes?',\n yes: options.yes,\n machineOutput: Boolean(options.json || format),\n command: 'kanbo init --global',\n })\n }\n if (!confirmed) {\n throw new CliError(1, 'Nothing was changed.')\n }\n\n const instructions: GlobalInstructionOutcome[] = instructionChanges.map(({ client, change }) => ({\n client,\n ...applyFileChange(change),\n }))\n const mcp: GlobalMcpOutcome[] = mcpClients.map((client) => {\n if (client === 'claude') {\n return registerWithClaude(claudeNeeded)\n }\n const planned = fileMcpChanges.find(entry => entry.client === client)!\n return { client, ...applyFileChange(planned.change) }\n })\n\n printResult(describeResult(instructions, mcp), options)\n}\n\nfunction planGlobalMcpFile(client: Exclude<McpClient, 'claude'>): FileChange {\n const path = globalMcpConfigPath(client)\n return client === 'codex' ? planCodexMcpServer(path) : planJsonMcpServer(path)\n}\n\n/** Claude Code's user-scope registration goes through its own command, never an edit of `~/.claude.json`. */\nfunction registerWithClaude(needed: boolean): GlobalMcpOutcome {\n const path = globalMcpConfigPath('claude')\n if (!needed) {\n return { client: 'claude', path, state: 'unchanged' }\n }\n const outcome = runClaudeMcp(CLAUDE_USER_ADD)\n return outcome.state === 'ran'\n ? { client: 'claude', path, state: 'written', command: outcome.command }\n : { client: 'claude', path, state: 'manual', command: outcome.command, reason: outcome.reason }\n}\n\nfunction describePlan(changes: FileChange[], claudeNeeded: boolean): string {\n const lines = ['kanbo init --global will:']\n for (const change of changes) {\n lines.push(` write ${change.path}`)\n }\n if (claudeNeeded) {\n lines.push(` run ${CLAUDE_USER_ADD.join(' ')}`)\n }\n lines.push('No board is created. Narrow this with --instructions and --mcp.')\n return lines.join('\\n')\n}\n\nfunction describeResult(instructions: GlobalInstructionOutcome[], mcp: GlobalMcpOutcome[]): CliResult {\n const lines: string[] = []\n for (const outcome of instructions) {\n lines.push(`Instructions: ${outcome.path} (${outcome.state})`)\n }\n for (const outcome of mcp) {\n if (outcome.state === 'manual') {\n lines.push(`MCP: ${outcome.client} — run this yourself: ${outcome.command} (${outcome.reason})`)\n }\n else {\n lines.push(`MCP: ${outcome.path} (${outcome.state})`)\n }\n }\n lines.push('A project uses the board once it has run kanbo init. Check everything with kanbo doctor.')\n return {\n value: { scope: 'global', instructions, mcp },\n text: lines.join('\\n'),\n fields: ['scope', 'instructions', 'mcp'],\n }\n}\n\nfunction parseGlobalInstructions(value: string | undefined): GlobalInstructionClient[] {\n if (value === undefined) {\n return DEFAULT_INSTRUCTION_CLIENTS\n }\n const names = value.split(',').map(name => name.trim()).filter(name => name.length > 0)\n if (names.length === 1 && names[0] === 'none') {\n return []\n }\n return names.map((name) => {\n const client = GLOBAL_INSTRUCTION_CLIENTS.find(candidate => candidate === name)\n if (!client) {\n throw new CliError(1, `Unknown instructions target \"${name}\" for --global. `\n + `Use one or more of ${GLOBAL_INSTRUCTION_CLIENTS.join(', ')}, or none.`)\n }\n return client\n })\n}\n","import { existsSync, mkdirSync, renameSync, rmSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve } from 'node:path'\n\nimport type { Command } from 'commander'\n\nimport { DEFAULT_BOARD_FILE_PATH } from '../../default-board-file-path'\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport type { BoardWorkspaceIdentity } from '../../domain/numbering'\nimport { migrateBoardFile } from '../../sqlite/migrate'\nimport type { BoardFileContents } from '../../sqlite/open-database'\nimport { markBoardFileOwnedByKanbo, openBoardDatabase, readBoardFileContents } from '../../sqlite/open-database'\nimport type { SqliteDatabase } from '../../sqlite/transaction'\nimport type { KanboBinding } from '../binding'\nimport { ignoreBoardFile, readProjectBinding, writeBinding } from '../binding'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand } from '../command'\nimport type { BoardTarget } from '../db-target'\nimport { describeTarget, resolveDatabaseUrl, resolveDbTarget, resolveHostDbPath } from '../db-target'\nimport type { CliResult } from '../output'\nimport { CliError, EXIT_NOT_RESOLVED, printResult, readFormat } from '../output'\nimport { isInteractive } from '../setup/confirm'\nimport type { FileOutcome } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { INSTRUCTION_BLOCK, planInstructionBlock } from '../setup/instructions'\nimport { planCodexMcpServer, planJsonMcpServer } from '../setup/mcp-config'\nimport type { McpClient, ProjectInstructionTarget } from '../setup/paths'\nimport { MCP_CLIENTS, PROJECT_INSTRUCTION_FILES, projectMcpConfigPath } from '../setup/paths'\nimport { resolveWorkspace } from '../workspace'\nimport { initGlobal } from './init-global'\n\n/**\n * `kanbo init` — tying a project folder to a board, and telling the agents\n * that work in it what the board expects.\n *\n * Three things come out of it, and each is optional except the first: the\n * binding, so no later command has to be told which workspace this is; a block\n * in the project's agent instructions, so an agent reads the board's rules\n * without being handed them; and a registration of the board's MCP server with\n * whichever tools the person actually uses. Nothing is written to a file the\n * person did not ask for, and nothing is written twice.\n */\n\n/**\n * What a person is told when an external board is named and nothing says which\n * workspace it is about. There is no table to look the answer up in, and a card\n * numbered under a guess carries that guess for the rest of its life.\n */\nconst EXTERNAL_WORKSPACE_MESSAGE\n = 'Could not tell which workspace this project is, and an external board holds no workspaces table to look '\n + 'it up in. Run this again with --workspace <id> --identifier <KEY>, or with --db (or KANBO_DB_PATH) naming '\n + 'a host database that knows this project.'\n\n/** What a person is told when a board file of the project's own and an external one are both named at once. */\nconst FILE_AND_DATABASE_URL_MESSAGE\n = 'One board at a time: pass --file for a board file of this project\\'s own or --database-url for an external '\n + 'board, not both.'\n\n/**\n * What a person is told when their agents are about to reach the board as its\n * owner. Not a refusal: the board works this way from the first minute, and the\n * rule an agent is missing is one the database keeps, not this command.\n */\nconst AGENT_URL_MISSING_WARNING\n = 'kanbo: agents in this project will reach the board with the connection string you gave, which owns it. '\n + 'Until you run \"kanbo roles apply\" and record the agent role\\'s own string with --agent-url, an agent '\n + 'here can approve its own work.'\n\ntype InstructionTarget = ProjectInstructionTarget | 'none'\n\nexport interface InitOptions extends BoardCommandOptions {\n /** Set up this person's own agent tools for every project, instead of binding this one. */\n global?: boolean\n board?: string\n identifier?: string\n agentUrl?: string\n /** Parsed per mode: one project file, or a list of the person's own files with `--global`. */\n instructions?: string\n mcp?: McpClient[]\n yes?: boolean\n /**\n * `--file` with no value parses to `true` (commander's rule for an optional\n * option argument); `--file <path>` parses to that path, relative to the\n * project root.\n */\n file?: string | boolean\n}\n\nexport function registerInitCommand(program: Command): void {\n // Not `withBoardOptions`: `--db` means something else here, and the two\n // board flags stand together rather than refusing each other. The rest is\n // spelled the same as everywhere else.\n program\n .command('init')\n .description('bind this project to a board, and tell its agents about it')\n .option('--db <path>', 'host database file to read this project\\'s workspace from')\n .option('--database-url <url>', 'external Postgres board to bind this project to')\n .option('--file [path]', `use a board file of this project's own instead of a host database or an `\n + `external board (default ${DEFAULT_BOARD_FILE_PATH})`)\n .option('--workspace <nameOrId>', 'workspace this project is')\n .option('--json <fields>', 'print only these comma-separated fields, as JSON')\n .option('--format <format>', 'output format: json or pretty')\n .option('--board <id>', 'the board inside the workspace, when it has more than one')\n .option('--identifier <key>', 'what this workspace\\'s card keys start with, for an external board')\n .option('--agent-url <url>', 'the connection string agents get, when it is not the one above')\n .option('--instructions <file>', 'where to write the instruction block: claude, agents or none; with '\n + '--global, a comma-separated list of claude, codex, gemini, or none')\n .option('--mcp <clients>', `register the board's MCP server with ${MCP_CLIENTS.join(', ')}`, parseMcpClients)\n .option('--global', 'set up your own agent tools for every project instead of binding this one; binds no board')\n .option('--yes', 'take the defaults instead of asking')\n .action(async (options: InitOptions) => {\n if (options.global) {\n await initGlobal(options)\n return\n }\n // Refused before anything is written, as a parse-time check would be.\n if (options.instructions !== undefined) {\n parseInstructions(options.instructions)\n }\n const projectDir = process.cwd()\n if (options.file !== undefined) {\n await initOwnFile(projectDir, options)\n return\n }\n const target = resolveInitTarget(projectDir, options)\n\n if (target.kind === 'postgres') {\n await initExternalBoard(projectDir, target, options)\n return\n }\n await runBoardCommand(options, 'read', async session => await initProject(\n projectDir,\n target,\n session.workspace,\n options,\n ))\n })\n}\n\n/**\n * Which board `init` is binding this project to.\n *\n * `--db` means something else here than it does in every other command: it\n * names the host database this one reads the workspace off, not the\n * board being bound. So the external board is asked for on its own — the flag,\n * the environment, then the binding this project already has — and only when\n * there is none is the board file itself the board. Always the person's\n * connection string: binding a project is not something an agent does.\n */\nfunction resolveInitTarget(projectDir: string, options: InitOptions): BoardTarget {\n const url = resolveDatabaseUrl({ explicitUrl: options.databaseUrl, cwd: projectDir, asAgent: false })\n return url ? { kind: 'postgres', url } : resolveDbTarget({ explicitPath: options.db, cwd: projectDir })\n}\n\n/**\n * Bind a project to an external board.\n *\n * The board is never opened. `init` is what a person runs *before*\n * `kanbo migrate`, and a database that holds no board yet is not a reason to\n * refuse to write down where it will be — nor could it answer the one question\n * that matters here, since it has no `workspaces` table to be asked.\n */\nasync function initExternalBoard(\n projectDir: string,\n target: Extract<BoardTarget, { kind: 'postgres' }>,\n options: InitOptions,\n): Promise<void> {\n readFormat(options)\n const workspace = await resolveExternalWorkspace(projectDir, options)\n printResult(await initProject(projectDir, target, workspace, options), options)\n}\n\n/**\n * Create and use a board file of this project's own — `.kanbo/board.db` by\n * default — with no host app and no Postgres anywhere near it.\n *\n * The file is migrated with this package's own chain right here, because\n * nothing else ever will: a host app migrates its own database and an\n * external database is migrated by `kanbo migrate`, but a file of this\n * package's own has no other owner to do it later — `kanbo migrate` runs the\n * same chain again afterwards, and idempotently, for the day it falls behind a\n * newer build (ruling 5-5). The marker written right after is what every later\n * command reads to know not to look for a host `workspaces` table in it\n * (ruling 5-4).\n *\n * The workspace is settled before a single byte is written, and nothing here\n * opens a host database at all (ruling 5-9): this is the one command that has\n * to work on a machine with nothing else installed, and the answer it needs —\n * an id and a card-key stem — is either typed, already in the binding, or the\n * folder's own name. Asking a host database first would turn `--workspace <id>`\n * into a refusal on every machine that has one, and leave a board file behind\n * with no binding beside it.\n */\nasync function initOwnFile(projectDir: string, options: InitOptions): Promise<void> {\n readFormat(options)\n if (options.databaseUrl) {\n throw new CliError(1, FILE_AND_DATABASE_URL_MESSAGE)\n }\n\n const relativePath = typeof options.file === 'string' && options.file.trim() ? options.file.trim() : DEFAULT_BOARD_FILE_PATH\n const absolutePath = resolve(projectDir, relativePath)\n const workspace = resolveOwnFileWorkspace(projectDir, options)\n\n await createOwnBoardFile(absolutePath)\n ignoreBoardFile(projectDir, absolutePath)\n\n const target: BoardTarget = { kind: 'sqlite', path: absolutePath, owner: 'kanbo' }\n printResult(await initProject(projectDir, target, workspace, options), options)\n}\n\n/**\n * Put a migrated, marked board at this path — or leave the folder exactly as it\n * was found (ruling 5-9).\n *\n * A new board is built beside its own path and moved onto it at the end, so a\n * migration that fails half-way leaves no half-built file for the next command\n * to open and believe in. `rename` within one directory is the filesystem's own\n * atomic swap; a temporary name elsewhere would be a copy across devices and\n * not atomic at all. A file that is already there is a different question — it\n * may hold this project's cards — so it is migrated in place, idempotently,\n * which is what makes a second `kanbo init --file` a no-op.\n *\n * A file that is there and is *not* this package's is refused, for the reason\n * any careful host refuses it: migrating a board into somebody's own\n * database leaves it readable but contaminated, and only a different path\n * helps.\n */\nasync function createOwnBoardFile(absolutePath: string): Promise<void> {\n mkdirSync(dirname(absolutePath), { recursive: true })\n if (existsSync(absolutePath)) {\n assertOwnBoardFile(absolutePath)\n await migrateBoardFileAt(absolutePath)\n return\n }\n\n const temporaryPath = `${absolutePath}.${process.pid}.tmp`\n try {\n await migrateBoardFileAt(temporaryPath)\n renameSync(temporaryPath, absolutePath)\n }\n catch (error) {\n // Its WAL siblings too: SQLite removes them on a clean close, and a close\n // is exactly what a failure half-way through may not have reached.\n for (const leftover of [temporaryPath, `${temporaryPath}-wal`, `${temporaryPath}-shm`]) {\n rmSync(leftover, { force: true })\n }\n throw error\n }\n}\n\n/** Refuse a file already at the board's path that this package did not write. */\nfunction assertOwnBoardFile(absolutePath: string): void {\n let contents: BoardFileContents\n try {\n contents = readBoardFileContents(absolutePath)\n }\n catch {\n throw new CliError(1, notABoardFileMessage(absolutePath))\n }\n if (!contents.empty && contents.owner !== 'kanbo') {\n throw new CliError(1, notABoardFileMessage(absolutePath))\n }\n}\n\nfunction notABoardFileMessage(absolutePath: string): string {\n return `${absolutePath} already holds a database of its own. Name another path with --file, `\n + 'or move that file out of the way.'\n}\n\nasync function migrateBoardFileAt(path: string): Promise<void> {\n const board = await openBoardDatabase(path)\n try {\n migrateBoardFile(board.database)\n markBoardFileOwnedByKanbo(board.database)\n }\n finally {\n board.close()\n }\n}\n\n/** The three things `init` writes, whichever kind of board it was given. */\nasync function initProject(\n projectDir: string,\n target: BoardTarget,\n workspace: BoardWorkspaceIdentity,\n options: InitOptions,\n): Promise<CliResult> {\n warnAboutOwnerAccess(target, options)\n const bindingPath = writeBinding(projectDir, buildBinding(projectDir, target, workspace, options))\n const instructions = await writeInstructions(projectDir, options)\n const mcp = await registerMcpServers(projectDir, options)\n\n return {\n value: {\n binding: bindingPath,\n workspaceId: workspace.id,\n boardId: options.board ?? null,\n database: describeTarget(target),\n instructions,\n mcp,\n },\n text: describeInit(workspace, target, bindingPath, instructions, mcp),\n }\n}\n\n/**\n * What the binding says afterwards.\n *\n * Merged into what this project already recorded, never replacing it: every\n * field this command line did not speak about keeps the value it had. A person\n * re-running `init` to refresh the instruction block said nothing about the\n * board, and a second run that dropped the connection string would leave the\n * project bound to nothing.\n *\n * The fields an external board adds are written only when there is one: they\n * are what every later command reaches the database by, and a project that has\n * nothing to do with one should not carry a connection string at all.\n *\n * This file is the only place a connection string is written down, and\n * `writeBinding` is what keeps it out of git. The MCP registrations below are\n * the project's own shared files and carry no secret at all — `kanbo mcp`\n * resolves its board from this binding like every other command.\n */\nfunction buildBinding(\n projectDir: string,\n target: BoardTarget,\n workspace: BoardWorkspaceIdentity,\n options: InitOptions,\n): KanboBinding {\n const existing = readProjectBinding(projectDir)\n const ownFile = target.kind === 'sqlite' && target.owner === 'kanbo'\n const hasWorkspacesTable = target.kind === 'sqlite' && target.owner === 'host'\n const binding: KanboBinding = {\n schemaVersion: 1,\n workspaceId: workspace.id,\n boardId: options.board ?? existing?.boardId ?? null,\n dbPath: ownFile ? relative(projectDir, target.path) : existing?.dbPath ?? null,\n }\n if (hasWorkspacesTable) {\n return binding\n }\n\n // Neither an external board nor a board file of this project's own has a\n // `workspaces` row to read the card-key stem off later, so it is written\n // down here — the one thing the two share, alongside `databaseUrl` below,\n // which only a Postgres board has any use for.\n const withIdentifier: KanboBinding = { ...binding, identifier: workspace.identifier }\n if (target.kind !== 'postgres') {\n return withIdentifier\n }\n\n const agentUrl = options.agentUrl?.trim() || existing?.agentDatabaseUrl\n return {\n ...withIdentifier,\n databaseUrl: target.url,\n ...(agentUrl ? { agentDatabaseUrl: agentUrl } : {}),\n }\n}\n\n/**\n * What the host database and the flags say about the workspace a board with no\n * `workspaces` table is being bound to — never the whole answer by itself,\n * since either piece may be missing.\n */\ninterface WorkspaceGuess {\n local: BoardWorkspaceIdentity | null\n id?: string\n identifier?: string\n}\n\n/**\n * Ask the host database first, because the ordinary case is a person moving a\n * project they already work on onto a board of its own, and their own machine\n * already knows both answers; what they typed on the command line wins over\n * it. Callers decide what to do when a piece is still missing: an external\n * board refuses outright, while a board file of this project's own has the\n * folder's own name to fall back on.\n */\nasync function guessWorkspace(projectDir: string, options: InitOptions): Promise<WorkspaceGuess> {\n const local = await readLocalWorkspace(projectDir, options)\n return {\n local,\n id: local?.id ?? options.workspace?.trim(),\n identifier: options.identifier?.trim() || local?.identifier,\n }\n}\n\n/**\n * Which workspace an external board's cards belong to, and what their keys\n * start with.\n *\n * When neither the host database nor the flags say the whole answer, the\n * command refuses rather than inventing a key: a card numbered under a guess\n * carries that guess for the rest of its life, and an external database is\n * shared in a way a board file of this project's own is not.\n */\nasync function resolveExternalWorkspace(projectDir: string, options: InitOptions): Promise<BoardWorkspaceIdentity> {\n const guess = await guessWorkspace(projectDir, options)\n if (!guess.id || !guess.identifier) {\n throw new CliError(EXIT_NOT_RESOLVED, EXTERNAL_WORKSPACE_MESSAGE)\n }\n return { id: guess.id, identifier: guess.identifier, name: guess.local?.id === guess.id ? guess.local.name : guess.identifier }\n}\n\n/**\n * The same question, for a board file of this project's own — asked of three\n * things, none of which is a database (ruling 5-9).\n *\n * `kanbo init --file` is the one command this package promises works with\n * nothing else installed, so there is nothing to refuse about it and nothing to\n * look up: what the person typed wins, then the id this project is already\n * bound to, and failing both the folder's own name, slugged, stands in for the\n * id and for the card-key stem `readIssuePrefix` derives from it.\n *\n * The binding comes second rather than last because the id in it is the id the\n * cards in the file are already numbered under — a second `init` that fell back\n * to the slug would leave the project pointing at a workspace with no cards in\n * it (ruling 3-16). It is also how a host app and the command line stay on one\n * board: an app that attaches the file may rewrite the binding to its own\n * workspace id (ruling 5-7), and this is what keeps it. The card key follows the id it\n * belongs to: naming a different workspace on the command line does not inherit\n * the old one's stem.\n */\nfunction resolveOwnFileWorkspace(projectDir: string, options: InitOptions): BoardWorkspaceIdentity {\n const bound = readProjectBinding(projectDir)\n const slug = deriveProjectSlug(projectDir)\n const id = options.workspace?.trim() || bound?.workspaceId.trim() || slug\n const boundIdentifier = id === bound?.workspaceId.trim() ? bound.identifier?.trim() : null\n const identifier = options.identifier?.trim() || boundIdentifier || slug\n return { id, identifier, name: identifier }\n}\n\n/** A workspace id and card-key stem from the project folder's own name, for the machine with nothing else to ask. */\nfunction deriveProjectSlug(projectDir: string): string {\n const slug = basename(resolve(projectDir)).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n return slug || 'project'\n}\n\n/**\n * The workspace this folder is, as a host database on this machine knows it —\n * or nothing at all, when there is no host database here to ask.\n *\n * A workspace that database *refuses* is a different answer entirely, and it is\n * only swallowed when the person named nothing: not knowing which workspace an\n * unnamed folder is, is what `--workspace` and `--identifier` are for. A name\n * they did type and that database rejects — no such workspace, or two by that\n * name — has to reach them, because the alternative is writing what they typed\n * into the binding as if it were an id and numbering cards under a workspace\n * that exists nowhere.\n */\nasync function readLocalWorkspace(projectDir: string, options: InitOptions): Promise<BoardWorkspaceIdentity | null> {\n const board = await openHostDatabase(options)\n if (!board) {\n return null\n }\n\n try {\n return resolveWorkspace(\n { kind: 'sqlite', database: board.database },\n { explicit: options.workspace, cwd: projectDir },\n )\n }\n catch (error) {\n if (options.workspace?.trim()) {\n throw error\n }\n return null\n }\n finally {\n board.close()\n }\n}\n\n/**\n * The host database `--db` or `KANBO_DB_PATH` names, or nothing — a machine is\n * allowed to have none. `resolveHostDbPath` rather than the ordinary resolver:\n * a project bound to a board file of its own by an earlier `kanbo init --file`\n * still asks this, and that binding names its own file, not a host database.\n */\nasync function openHostDatabase(options: InitOptions): Promise<{ database: SqliteDatabase, close: () => void } | null> {\n try {\n return await openBoardDatabase(resolveHostDbPath(options.db))\n }\n catch {\n return null\n }\n}\n\n/**\n * Say so when the agents of this project will reach the board as its owner.\n *\n * On stderr, because stdout belongs to the result, and once — a person who has\n * already recorded an agent connection string hears nothing.\n */\nfunction warnAboutOwnerAccess(target: BoardTarget, options: InitOptions): void {\n if (target.kind === 'postgres' && !options.agentUrl?.trim()) {\n console.error(AGENT_URL_MISSING_WARNING)\n }\n}\n\n/**\n * Put the block in the file the caller named — or, when they named none, show\n * them what it says and ask.\n *\n * A shell that is nobody's terminal is never asked anything: an `init` in a\n * script prints the block and stops there, because writing into a project's\n * instructions is the kind of thing a person says yes to.\n */\nasync function writeInstructions(projectDir: string, options: InitOptions): Promise<FileOutcome | null> {\n const target = options.instructions === undefined ? await askInstructionTarget(options) : parseInstructions(options.instructions)\n if (target === 'none') {\n return null\n }\n return applyFileChange(planInstructionBlock(join(projectDir, PROJECT_INSTRUCTION_FILES[target]), INSTRUCTION_BLOCK))\n}\n\nasync function askInstructionTarget(options: InitOptions): Promise<InstructionTarget> {\n showBlock(options)\n if (options.yes || !isInteractive()) {\n return 'none'\n }\n\n const prompts = await import('@clack/prompts')\n const answer = await prompts.select({\n message: 'Add this block to the project\\'s agent instructions?',\n options: [\n { value: 'claude' as const, label: 'CLAUDE.md' },\n { value: 'agents' as const, label: 'AGENTS.md' },\n { value: 'none' as const, label: 'Neither — I will paste it myself' },\n ],\n })\n return prompts.isCancel(answer) ? 'none' : answer\n}\n\n/**\n * Show the block to whoever is about to decide where it goes — unless the\n * caller asked for machine output, in which case stdout belongs to the result\n * and nothing else may be written there, prompt or no prompt.\n */\nfunction showBlock(options: InitOptions): void {\n if (!options.json && !options.format) {\n console.log(`${INSTRUCTION_BLOCK}\\n`)\n }\n}\n\n/**\n * Tell the tools the person uses about the board's MCP server.\n *\n * Only the ones they named, and only when the tool does not already know: a\n * `kanbo` entry that is already there belongs to them — it may point at\n * a different binary or carry arguments of its own — and overwriting it would\n * be this command deciding something it was not asked to decide.\n */\nasync function registerMcpServers(projectDir: string, options: InitOptions): Promise<FileOutcome[]> {\n const clients = options.mcp ?? await askMcpClients(options)\n return clients.map(client => registerMcpServer(projectDir, client))\n}\n\nasync function askMcpClients(options: InitOptions): Promise<McpClient[]> {\n if (options.yes || !isInteractive()) {\n return []\n }\n\n const prompts = await import('@clack/prompts')\n const answer = await prompts.multiselect({\n message: 'Register the board\\'s MCP server with which tools?',\n options: [\n { value: 'claude' as const, label: 'Claude Code (.mcp.json)' },\n { value: 'codex' as const, label: 'Codex (.codex/config.toml)' },\n { value: 'cursor' as const, label: 'Cursor (.cursor/mcp.json)' },\n ],\n required: false,\n })\n return prompts.isCancel(answer) ? [] : answer\n}\n\nfunction registerMcpServer(projectDir: string, client: McpClient): FileOutcome {\n const path = projectMcpConfigPath(projectDir, client)\n return applyFileChange(client === 'codex' ? planCodexMcpServer(path) : planJsonMcpServer(path))\n}\n\nfunction describeInit(\n workspace: BoardWorkspaceIdentity,\n target: BoardTarget,\n bindingPath: string,\n instructions: FileOutcome | null,\n mcp: FileOutcome[],\n): string {\n const lines = [`Bound this project to ${workspace.name} — ${bindingPath}`]\n if (target.kind === 'postgres') {\n lines.push(`Board: ${maskDatabaseUrl(target.url)}`)\n }\n else if (target.owner === 'kanbo') {\n lines.push(`Board file: ${target.path}`)\n }\n if (instructions) {\n lines.push(`Instructions: ${instructions.path} (${instructions.state})`)\n }\n for (const outcome of mcp) {\n lines.push(`MCP: ${outcome.path} (${outcome.state})`)\n }\n return lines.join('\\n')\n}\n\nfunction parseInstructions(value: string): InstructionTarget {\n if (value !== 'claude' && value !== 'agents' && value !== 'none') {\n throw new CliError(1, `Unknown instructions target \"${value}\". Use claude, agents or none.`)\n }\n return value\n}\n\nfunction parseMcpClients(value: string): McpClient[] {\n return value.split(',').map(name => name.trim()).filter(name => name.length > 0).map((name) => {\n const client = MCP_CLIENTS.find(candidate => candidate === name)\n if (!client) {\n throw new CliError(1, `Unknown MCP client \"${name}\". Use one or more of ${MCP_CLIENTS.join(', ')}.`)\n }\n return client\n })\n}\n","import type { EntryRule } from '../domain/entry-rules'\nimport { ENTRY_RULES, readEntryRules } from '../domain/entry-rules'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { Issue, IssueRun } from '../sqlite/schema'\n\n/**\n * What the board's tools ask for, and what comes back.\n *\n * A tool knows one operation and nothing about how it is performed: the same\n * `kanbo_card_move` runs against a server over HTTP inside an app's own MCP\n * server, and against the board itself inside `kanbo mcp`, because both transports answer this interface. Which one a tool\n * holds is the caller's decision, made once, and no tool can tell the\n * difference.\n *\n * The workspace is not a parameter here. Each transport is built for one board\n * — the CLI resolves it the way every `kanbo` command does, the server takes\n * it from the runtime it was launched for — and a tool that could name a\n * workspace could name one the agent was never given.\n */\n\n/** A card, as a tool hands it to an agent. */\nexport interface KanboCardResult {\n /** The issue key, which is also the row id: `MAN-012`. */\n id: string\n number: number\n title: string\n description: string | null\n /** The column's name, `null` when the card sits in no column. */\n column: string | null\n /** The same column as the slug `kanbo_card_move` takes. */\n columnSlug: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n /** Every run the card has ever had; the next launch is attempt `attemptCount + 1`. */\n attemptCount: number\n /** The run going on right now, `null` when nobody is working on the card. */\n activeRun: KanboActiveRunResult | null\n updatedAt: number\n}\n\n/**\n * A card read on its own, as `kanbo_card_get` hands it to an agent: the card,\n * and the sub-cards under it in board order. Its parent is `parentIssueId`.\n */\nexport interface KanboCardDetailResult extends KanboCardResult {\n subCards: Pick<KanboCardResult, 'id' | 'title' | 'columnSlug'>[]\n}\n\n/** The run of a card that is going on right now. */\nexport interface KanboActiveRunResult {\n id: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n}\n\n/**\n * A column of the board, as a tool hands it to an agent.\n *\n * What the column means is its description, written for exactly this reader;\n * the category behind it is the board's own classification of the column and says\n * nothing an agent moving a card can act on.\n */\nexport interface KanboColumnResult {\n id: string\n name: string\n /** What `kanbo_card_move` calls this column. */\n slug: string\n description: string | null\n order: number\n /** What a card must satisfy before an agent may move it here; empty for nothing. */\n entryRules: EntryRule[]\n}\n\n/**\n * A milestone read as a sprint, as `kanbo_sprints` hands it to an agent: its\n * dates (unix seconds), whether it is the one running now, and how many of its\n * cards are still open and how many are done.\n */\nexport interface KanboSprintResult {\n id: string\n title: string\n status: 'open' | 'closed'\n startDate: number | null\n dueDate: number | null\n current: boolean\n openCards: number\n doneCards: number\n}\n\n/** A comment, as `kanbo_card_comment` hands it back. */\nexport interface KanboCommentResult {\n id: string\n issueId: string\n content: string\n createdAt: number\n}\n\n/** A pull request a card names, as the pull-request tools hand it back. */\nexport interface KanboPullRequestResult {\n id: string\n issueId: string\n owner: string\n repo: string\n number: number\n url: string\n createdAt: number\n}\n\n/** One launch of a card, as the run tools hand it back. */\nexport interface KanboRunResult {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n endedAt: number | null\n /** Where the caller's own log of this run lives, if it said — `claude:<id>` or `codex:<id>` (ruling 7-1), else `null`. */\n externalSessionRef: string | null\n}\n\n/**\n * What each tool asks its transport for. They are named rather than written\n * inline so the interface below reads as a list of operations, and kept inside\n * this module because a caller reaches them through `KanboToolTransport`.\n */\n\n/** What `kanbo_card_create` needs to open a card. */\ninterface KanboCardCreateInput {\n /** Optional: a card without one is titled with its own key. */\n title?: string\n description?: string\n /** The column to open it in, by slug or name; the board's first column when absent. */\n column?: string\n /** The card this one belongs under, making it a sub-card. */\n parent?: string\n executionMode?: Issue['executionMode']\n}\n\n/** The fields `kanbo_card_update` may change. */\ninterface KanboCardUpdateInput {\n card: string\n title?: string\n description?: string\n priority?: Issue['priority']\n labels?: string[]\n executionMode?: Issue['executionMode']\n}\n\n/** What `kanbo_run_start` records about a launch. */\ninterface KanboRunStartInput {\n card: string\n agent: string\n branch?: string\n executionMode?: Issue['executionMode']\n /** Where the agent's own log of this run lives: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n session?: string\n}\n\n/** How `kanbo_run_finish` says a run ended. */\ninterface KanboRunFinishInput {\n run: string\n state: 'finished' | 'failed' | 'stopped'\n errorText?: string\n}\n\n/** One method per thing the board's tools do. Both transports answer all of it. */\nexport interface KanboToolTransport {\n prime: () => Promise<{ text: string }>\n ready: (input: { limit?: number }) => Promise<KanboCardResult[]>\n columns: () => Promise<KanboColumnResult[]>\n sprints: () => Promise<KanboSprintResult[]>\n cardGet: (input: { card: string }) => Promise<KanboCardDetailResult>\n cardList: (input: { column?: string, limit?: number }) => Promise<KanboCardResult[]>\n cardCreate: (input: KanboCardCreateInput) => Promise<KanboCardResult>\n cardUpdate: (input: KanboCardUpdateInput) => Promise<KanboCardResult>\n cardMove: (input: { card: string, column: string }) => Promise<KanboCardResult>\n cardComment: (input: { card: string, content: string }) => Promise<KanboCommentResult>\n cardLinkPullRequest: (input: { card: string, url: string }) => Promise<KanboPullRequestResult>\n cardPullRequests: (input: { card: string }) => Promise<KanboPullRequestResult[]>\n statusLine: (input: { card: string, text: string }) => Promise<KanboCardResult>\n waitApproval: (input: { card: string, text?: string }) => Promise<KanboCardResult>\n runStart: (input: KanboRunStartInput) => Promise<KanboRunResult>\n runFinish: (input: KanboRunFinishInput) => Promise<KanboRunResult>\n}\n\n/**\n * A card as either transport already has it: the row's own fields with the\n * labels decoded, plus what the run registry says about it.\n */\nexport interface KanboCardFacts {\n id: string\n number: number\n title: string\n description: string | null\n statusId: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n updatedAt: number\n attemptCount: number\n activeRun: KanboActiveRunResult | null\n}\n\n/**\n * The card a tool prints, from the card a transport holds.\n *\n * Both transports project here so the JSON an agent reads is the same whether\n * it came from the server or from the board file — including the column, which\n * neither of them carries on the card itself: the row keeps a foreign key, and\n * an agent moving a card needs the slug.\n *\n * Every field is named rather than spread. A card the board file answers with\n * carries more than this — the agent and session ids behind a run among them —\n * and passing an object through by reference would print whatever it happened\n * to hold, which is both a different shape from the server's answer and host\n * facts the agent was never meant to see.\n */\nexport function projectKanboCard(card: KanboCardFacts, columns: KanboColumnResult[]): KanboCardResult {\n const column = columns.find(candidate => candidate.id === card.statusId) ?? null\n return {\n id: card.id,\n number: card.number,\n title: card.title,\n description: card.description,\n column: column?.name ?? null,\n columnSlug: column?.slug ?? null,\n statusLine: card.statusLine,\n waitingFor: card.waitingFor,\n priority: card.priority,\n labels: card.labels,\n executionMode: card.executionMode,\n parentIssueId: card.parentIssueId,\n attemptCount: card.attemptCount,\n activeRun: projectKanboActiveRun(card.activeRun),\n updatedAt: card.updatedAt,\n }\n}\n\n/** A sub-card, as the card above it lists it: enough to name it and read it next. */\nexport function projectKanboSubCard(\n card: Pick<KanboCardFacts, 'id' | 'title' | 'statusId'>,\n columns: KanboColumnResult[],\n): KanboCardDetailResult['subCards'][number] {\n return {\n id: card.id,\n title: card.title,\n columnSlug: columns.find(candidate => candidate.id === card.statusId)?.slug ?? null,\n }\n}\n\n/** The run going on right now, as much of it as the agent working the card needs. */\nfunction projectKanboActiveRun(run: KanboActiveRunResult | null): KanboActiveRunResult | null {\n return run === null\n ? null\n : {\n id: run.id,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n startedAt: run.startedAt,\n }\n}\n\n/**\n * One launch of a card, from the run row either transport holds.\n *\n * What the row carries beyond this — the worktree path, the chat session\n * driving it, who launched it — belongs to the host that launched the run, not\n * to the agent that asked for one.\n */\nexport function projectKanboRun(run: {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n endedAt: number | null\n /** Absent from a server that predates it — reads the same as `null`. */\n externalSessionRef?: string | null\n}): KanboRunResult {\n return {\n id: run.id,\n issueId: run.issueId,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n startedAt: run.startedAt,\n endedAt: run.endedAt,\n externalSessionRef: run.externalSessionRef ?? null,\n }\n}\n\n/**\n * A pull-request link, from the row either transport holds — without who made\n * it, which is the board's record rather than anything the agent acts on.\n */\nexport function projectKanboPullRequest(link: KanboPullRequestResult): KanboPullRequestResult {\n return {\n id: link.id,\n issueId: link.issueId,\n owner: link.owner,\n repo: link.repo,\n number: link.number,\n url: link.url,\n createdAt: link.createdAt,\n }\n}\n\n/** A sprint, from the one `listSprints` answers with on either end — the description and timestamps left behind. */\nexport function projectKanboSprint(sprint: {\n id: string\n title: string\n status: 'open' | 'closed'\n startDate: number | null\n dueDate: number | null\n current: boolean\n cards: { open: number, done: number }\n}): KanboSprintResult {\n return {\n id: sprint.id,\n title: sprint.title,\n status: sprint.status,\n startDate: sprint.startDate,\n dueDate: sprint.dueDate,\n current: sprint.current,\n openCards: sprint.cards.open,\n doneCards: sprint.cards.done,\n }\n}\n\n/** A column of the board, from the row either transport holds. */\nexport function projectKanboColumn(column: {\n id: string\n name: string\n description: string | null\n order: number\n entryRules?: string | readonly string[] | null\n}): KanboColumnResult {\n const stored = column.entryRules\n return {\n id: column.id,\n name: column.name,\n slug: normalizeStatusName(column.name),\n description: column.description,\n order: column.order,\n entryRules: typeof stored === 'object' && stored !== null\n ? ENTRY_RULES.filter(rule => stored.includes(rule))\n : readEntryRules({ entryRules: stored ?? null }),\n }\n}\n","import { openBoardSession, requireCard, requireRun } from '../cli/command'\nimport { toIssueView } from '../domain/issue-view'\nimport { requireColumn } from '../ops/columns'\nimport type { BoardActor } from '../ops/types'\nimport type { Issue } from '../sqlite/schema'\nimport type { KanboCardResult, KanboColumnResult, KanboToolTransport } from './transport'\nimport {\n projectKanboCard,\n projectKanboColumn,\n projectKanboPullRequest,\n projectKanboRun,\n projectKanboSprint,\n projectKanboSubCard,\n} from './transport'\n\n/**\n * The board's tools, run against the board itself.\n *\n * This is the transport `kanbo mcp` serves: an agent working in a checkout\n * with no server running still drives its card, because the board — a\n * file or a database of its own — and the operations that give it meaning ship\n * with this package. Everything a `kanbo` command decides is decided here too,\n * by calling the same code: which board, which workspace, which card a\n * reference names, and the refusal to write to one this build cannot speak for.\n */\n\n/** Where the board is, whose board it is, and who the writes are filed under. */\nexport interface KanboDbTransportInput {\n /** The board file to open; resolved the way every `kanbo` command resolves it when absent. */\n dbPath?: string\n /** The external board to open instead; resolved the same way when absent. */\n databaseUrl?: string\n /** The workspace to work on; resolved from the binding or the working directory when absent. */\n workspaceId?: string\n /**\n * Who every write is filed under. Never taken from a tool argument: an agent\n * that could name its own kind could name `user`, and the one rule the board\n * has about people would be worth nothing.\n */\n actor: BoardActor\n}\n\n/** The transport, and the board it holds open. */\nexport interface KanboDbTransport extends KanboToolTransport {\n /** Let go of the board. The server that holds this transport closes it. */\n close: () => Promise<void>\n}\n\nexport async function createDbTransport(input: KanboDbTransportInput): Promise<KanboDbTransport> {\n // Opened for reading: a board file older than this build is still worth\n // looking at, and every write below asks the same guard a `kanbo` command\n // asks. A client talks to this server for as long as it likes, and the board\n // may be migrated underneath it in the meantime.\n const session = await openBoardSession(\n { db: input.dbPath, databaseUrl: input.databaseUrl, workspace: input.workspaceId, asAgent: true },\n 'read',\n )\n const { ops, store, workspace } = session\n const workspaceId = workspace.id\n const actor = input.actor\n\n async function readColumns(): Promise<KanboColumnResult[]> {\n return (await ops.listColumns(workspaceId)).map(projectKanboColumn)\n }\n\n /** One card with what the run registry says about it, in the shape a tool prints. */\n async function project(card: Issue, columns?: KanboColumnResult[]): Promise<KanboCardResult> {\n const runs = await ops.readBoardProjectionForIssue(card.id)\n return projectKanboCard({ ...toIssueView(card), ...runs }, columns ?? await readColumns())\n }\n\n async function projectAll(cards: Issue[]): Promise<KanboCardResult[]> {\n const columns = await readColumns()\n const runs = await ops.readBoardProjectionForIssues(cards.map(card => card.id))\n return cards.map(card => projectKanboCard({\n ...toIssueView(card),\n ...(runs.find(projection => projection.issueId === card.id) ?? { attemptCount: 0, activeRun: null }),\n }, columns))\n }\n\n /** The card a tool named: the key, the key without its padding, or the number. */\n async function resolveCard(reference: string): Promise<Issue> {\n return await requireCard(session, reference)\n }\n\n return {\n close: session.close,\n\n prime: async () => ({ text: await ops.buildPrimeText(workspaceId) }),\n\n ready: async ({ limit }) => await projectAll(await ops.listReady({ workspaceId, limit })),\n\n columns: readColumns,\n\n sprints: async () => (await ops.listSprints(workspaceId)).map(projectKanboSprint),\n\n cardGet: async ({ card }) => {\n const found = await resolveCard(card)\n const columns = await readColumns()\n const subCards = (await store.issues.listInBoardOrder(found.workspaceId))\n .filter(row => row.parentIssueId === found.id)\n return {\n ...await project(found, columns),\n subCards: subCards.map(row => projectKanboSubCard(row, columns)),\n }\n },\n\n cardList: async ({ column, limit }) => {\n const wanted = column ? await requireColumn(store, workspaceId, column) : null\n const cards = (await store.issues.listInBoardOrder(workspaceId))\n .filter(card => !wanted || card.statusId === wanted.id)\n .slice(0, limit ?? Number.POSITIVE_INFINITY)\n return await projectAll(cards)\n },\n\n cardCreate: async ({ title, description, column, parent, executionMode }) => {\n await session.assertWritable()\n const parentCard = parent ? await resolveCard(parent) : null\n return await project(await ops.createCard({\n workspace,\n title,\n description,\n statusName: column,\n parentIssueId: parentCard?.id,\n executionMode,\n }, actor))\n },\n\n cardUpdate: async ({ card, ...patch }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.updateCard(existing.id, patch, actor))\n },\n\n cardMove: async ({ card, column }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.moveCard(existing.id, column, actor))\n },\n\n cardComment: async ({ card, content }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n const comment = await ops.addComment({ issueId: existing.id, content }, actor)\n return { id: comment.id, issueId: comment.issueId, content: comment.content, createdAt: comment.createdAt }\n },\n\n cardLinkPullRequest: async ({ card, url }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return projectKanboPullRequest(await ops.linkPullRequest(existing.id, url, actor))\n },\n\n cardPullRequests: async ({ card }) => {\n const existing = await resolveCard(card)\n return (await ops.listPullRequests(existing.id)).map(projectKanboPullRequest)\n },\n\n statusLine: async ({ card, text }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.setStatusLine(existing.id, text, actor))\n },\n\n waitApproval: async ({ card, text }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.waitApproval(existing.id, { statusLine: text }, actor))\n },\n\n runStart: async ({ card, agent, branch, executionMode, session: externalSessionRef }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n // Filed as launched from outside, because it was: no app's chat session\n // started this, and a board that claimed otherwise would send someone\n // looking for a session that never existed.\n return projectKanboRun(await ops.startRun(existing.id, {\n agentName: agent,\n branch,\n executionMode,\n externalSessionRef,\n launchedByKind: 'external',\n }, actor))\n },\n\n runFinish: async ({ run, state, errorText }) => {\n await session.assertWritable()\n const existing = await requireRun(session, run)\n return projectKanboRun(await ops.finishRun(existing.id, { state, errorText }, actor))\n },\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\n\nimport { buildCapabilitiesManifest } from '../capabilities/manifest'\nimport { renderCapabilitiesMarkdown } from '../capabilities/markdown'\n\n/** The URI a client reads the manifest as JSON from. */\nexport const KANBO_CAPABILITIES_RESOURCE_URI = 'kanbo://capabilities'\n\n/** The URI a client reads the same manifest from, laid out as Markdown. */\nexport const KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI = 'kanbo://capabilities.md'\n\n/**\n * The board's own capabilities manifest, as two MCP resources rather than a\n * tool: a client reads it once, before it ever calls a board tool, to learn\n * what the tools take and what the board promises and refuses. `kanbo\n * capabilities` prints the same two shapes from a terminal.\n *\n * Registered on the server `kanbo mcp` builds for itself rather than on\n * `registerKanboTools` — the function an app's own MCP server calls to add the\n * board's tools to an inventory of its own — so an agent inside such an app\n * reads the manifest the way that app documents everything else, and a client\n * with no server around it still gets the manifest from the one server it is\n * talking to.\n */\nexport function registerKanboCapabilitiesResources(server: McpServer): void {\n server.registerResource(\n 'kanbo-capabilities',\n KANBO_CAPABILITIES_RESOURCE_URI,\n {\n title: 'Kanbo capabilities',\n description: 'The board\\'s tools with their input schemas, its CLI commands, the rules a card travels by, '\n + 'the canonical strings the board writes, the roles it enforces, and what it refuses — as JSON.',\n mimeType: 'application/json',\n },\n (uri): ReadResourceResult => ({\n contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(buildCapabilitiesManifest(), null, 2) }],\n }),\n )\n\n server.registerResource(\n 'kanbo-capabilities-markdown',\n KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI,\n {\n title: 'Kanbo capabilities (Markdown)',\n description: 'The same manifest as kanbo://capabilities, laid out as Markdown — suitable for pasting '\n + 'into a CLAUDE.md or an AGENTS.md.',\n mimeType: 'text/markdown',\n },\n (uri): ReadResourceResult => ({\n contents: [{ uri: uri.href, mimeType: 'text/markdown', text: renderCapabilitiesMarkdown(buildCapabilitiesManifest()) }],\n }),\n )\n}\n","import {\n COMMENT_RULE,\n MOVE_CARD_RULE,\n PERSON_ONLY_RULE,\n STATUS_LINE_RULE,\n SUBTASK_RULE,\n WAIT_FOR_PERSON_RULE,\n} from '../ops/agent-rules'\nimport { KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI } from './resources'\nimport type { KanboToolName } from './tool-names'\n\n/** A tool name as the instructions print it — typed, so a renamed tool fails the build here. */\nfunction tool(name: KanboToolName): string {\n return `\\`${name}\\``\n}\n\n/**\n * What `kanbo mcp` tells a client when it connects — the `instructions` of the\n * MCP initialize result, which a client hands its model before any tool is\n * called. An agent with nothing but this server connected learns from it how\n * to work the board, with no instruction file in the project.\n *\n * Static on purpose: it is sent before any board is resolved, so it says\n * nothing about columns — `kanbo_prime` returns those. The rules are the\n * sentences `ops/agent-rules.ts` holds for every surface, with the tool beside\n * each; the full manifest stays a resource.\n */\nexport const KANBO_MCP_INSTRUCTIONS = [\n 'kanbo is a kanban board shared by people and agents; these tools read and change its cards.',\n '',\n `- At the start of a session call ${tool('kanbo_prime')} — it returns this board's columns and what each one means.`,\n `- Take work from ${tool('kanbo_ready')}.`,\n `- ${MOVE_CARD_RULE} (${tool('kanbo_card_move')})`,\n `- ${STATUS_LINE_RULE} (${tool('kanbo_status_line')})`,\n `- ${COMMENT_RULE} (${tool('kanbo_card_comment')})`,\n `- ${SUBTASK_RULE} (${tool('kanbo_card_create')} with \\`parent\\`)`,\n `- ${WAIT_FOR_PERSON_RULE} (${tool('kanbo_wait_approval')})`,\n `- ${PERSON_ONLY_RULE}`,\n `- Every tool, rule and canonical string: the resource \\`${KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI}\\`.`,\n].join('\\n')\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\n\nimport { maskDatabaseUrls } from '../../domain/database-url'\nimport type { ColumnRefusalDetails } from '../../domain/entry-rules'\nimport { describeColumnRefusal } from '../../domain/entry-rules'\nimport { BoardError } from '../../domain/errors'\nimport type { KanboToolName } from '../tool-names'\nimport type { KanboToolTransport } from '../transport'\n\n/**\n * What every board tool is, and the one way all of them answer.\n *\n * A tool is a name, a schema and exactly one call into the transport. It never\n * throws: a board rule the caller broke, a card that is not there, a server\n * that refused — all of it comes back as a tool error the model can read and\n * act on, because an exception out of a handler reaches the model as a\n * transport failure with nothing in it to correct.\n */\n\n/** The zod raw shape `McpServer.registerTool` takes. */\nexport type KanboToolShape = Record<string, z.ZodType>\n\n/**\n * What a tool call answers with: the result, or the reason it did not happen.\n *\n * It is the MCP result the protocol defines, narrowed to what the board's tools\n * actually produce — one block of text, and whether that text is a failure.\n */\nexport interface KanboToolResult extends CallToolResult {\n content: { type: 'text', text: string }[]\n isError?: true\n}\n\n/** One tool of the board, ready to register against any transport. */\nexport interface KanboTool {\n name: KanboToolName\n title: string\n description: string\n inputSchema: KanboToolShape\n run: (transport: KanboToolTransport, input: unknown) => Promise<KanboToolResult>\n}\n\n/**\n * Declare a tool: what it is called, what it takes, and the one thing it does.\n *\n * The arguments are parsed here as well as by the MCP server that dispatched\n * them, which is what lets the body be written against a typed input while the\n * list of tools stays one plain array a test can walk. What comes back is JSON,\n * unless the tool answers in prose and hands back a string — `kanbo_prime` is\n * the board talking to the agent, and quoting it would only make it harder to\n * read.\n */\nexport function defineKanboTool<Shape extends KanboToolShape>(definition: {\n name: KanboToolName\n title: string\n description: string\n inputSchema: Shape\n run: (transport: KanboToolTransport, input: z.output<z.ZodObject<Shape>>) => Promise<unknown>\n}): KanboTool {\n const schema = z.object(definition.inputSchema)\n return {\n name: definition.name,\n title: definition.title,\n description: definition.description,\n inputSchema: definition.inputSchema,\n run: async (transport, input) => {\n try {\n const answer = await definition.run(transport, schema.parse(input ?? {}))\n return toolText(typeof answer === 'string' ? answer : JSON.stringify(answer, null, 2))\n }\n catch (error) {\n return toolError(error)\n }\n },\n }\n}\n\n/**\n * The card a tool acts on. Every tool that takes one takes it under this name\n * and describes it the same way, so a model that learned the argument once\n * cannot be wrong about it on the next tool.\n */\nexport const cardArgument = z.string().min(1).describe('The card, by the id the board prints for it, for example MAN-012.')\n\n/** Where a card's work happens. */\nexport const executionModeArgument = z.enum(['worktree', 'main'])\n .describe('Where the work happens: worktree is a separate copy of the project, main is the project folder itself.')\n\n/** What a tool hands back when it did what it was asked. */\nfunction toolText(text: string): KanboToolResult {\n return { content: [{ type: 'text', text }] }\n}\n\n/**\n * Why the tool did not do what it was asked.\n *\n * A `BoardError` is a rule of the board the caller broke: its code names what\n * happened and its details say which value did it, so both are passed through\n * rather than translated into a sentence that could drift away from the rule.\n */\nfunction toolError(error: unknown): KanboToolResult {\n // A card held at a column's door carries a list; the agent reads it as one\n // line per rule, the same lines the command line prints.\n if (error instanceof BoardError && error.code === 'board_column_rules_unmet') {\n const text = describeColumnRefusal(error.details as unknown as ColumnRefusalDetails)\n return { content: [{ type: 'text', text }], isError: true }\n }\n if (error instanceof BoardError) {\n const details = error.details ? ` ${JSON.stringify(error.details)}` : ''\n return { content: [{ type: 'text', text: `${error.code}${details}` }], isError: true }\n }\n // Anything else is a message this package did not write — on an external\n // board, a driver's — and such a message is free to carry the whole\n // connection string in the middle of it.\n return {\n content: [{ type: 'text', text: maskDatabaseUrls(error instanceof Error ? error.message : String(error)) }],\n isError: true,\n }\n}\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** Anything a person has to read, written where they will find it. */\nexport const cardCommentTool = defineKanboTool({\n name: 'kanbo_card_comment',\n title: 'Comment on a card',\n description: 'Write a finding, a decision or an open question on the card, where a person reads it. Your transcript is not the card.',\n inputSchema: {\n card: cardArgument,\n content: z.string().min(1).describe('What to say, written for whoever opens this card next.'),\n },\n run: async (transport, input) => await transport.cardComment(input),\n})\n","import { z } from 'zod'\n\nimport { SUBTASK_WHEN_SENTENCE, subtaskSplitSentence } from '../../ops/agent-rules'\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/** What an agent is told about subtasks, word for word in the tool and in its `parent` field. */\nconst SUBTASK_SENTENCE = 'To create a subtask (sub-card) under another card, pass `parent` = that card\\'s id (its key, e.g. `MAN-012`).'\n\n/** A new card, or a sub-card of the one being worked on. */\nexport const cardCreateTool = defineKanboTool({\n name: 'kanbo_card_create',\n title: 'Create a card',\n description: `Put a new card on this board. ${SUBTASK_SENTENCE} ${SUBTASK_WHEN_SENTENCE} ${subtaskSplitSentence('pass `parent`')}`,\n inputSchema: {\n title: z.string().optional().describe('What the card is called. Leave it out and the card is titled with its own key; the description then says what the task is.'),\n description: z.string().optional().describe('What the card is about, spelled out for whoever picks it up.'),\n column: z.string().min(1).optional().describe('The column to open it in, by slug or name. The board\\'s first column when absent.'),\n parent: cardArgument.optional().describe(SUBTASK_SENTENCE),\n executionMode: executionModeArgument.optional(),\n },\n run: async (transport, input) => await transport.cardCreate(input),\n})\n","import { cardArgument, defineKanboTool } from './tool'\n\n/** One card in full. */\nexport const cardGetTool = defineKanboTool({\n name: 'kanbo_card_get',\n title: 'Read a card',\n description: 'Read one card in full: what it is for, the column it sits in, what it is doing right now, who it is waiting for, its parent (`parentIssueId`) and its subtasks (`subCards`).',\n inputSchema: {\n card: cardArgument,\n },\n run: async (transport, input) => await transport.cardGet(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** A pull request the card's work went into, named on the card so its facts reach it. */\nexport const cardLinkPrTool = defineKanboTool({\n name: 'kanbo_card_link_pr',\n title: 'Link a pull request to a card',\n description: 'Opened a pull request for this card? Link it here. Its state and CI results then reach the card as comments, '\n + 'without you writing them. Linking the same pull request again changes nothing.',\n inputSchema: {\n card: cardArgument,\n url: z.string().min(1).describe('The pull request, as https://github.com/<owner>/<repo>/pull/<number> or <owner>/<repo>#<number>.'),\n },\n run: async (transport, input) => await transport.cardLinkPullRequest(input),\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** The cards on the board, optionally only those in one column. */\nexport const cardListTool = defineKanboTool({\n name: 'kanbo_card_list',\n title: 'List cards',\n description: 'List the cards on this board in board order, optionally only the ones in one column.',\n inputSchema: {\n column: z.string().min(1).optional().describe('Only cards in this column, by its slug or name.'),\n limit: z.number().int().positive().optional().describe('How many cards to return. All of them when absent.'),\n },\n run: async (transport, input) => await transport.cardList(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** Moving a card, which is how the board tells the truth about it. */\nexport const cardMoveTool = defineKanboTool({\n name: 'kanbo_card_move',\n title: 'Move a card',\n description: 'Move a card to another column the moment its real state changes. Read kanbo_columns first: a card in the wrong column is a lie, '\n + 'and a column with entryRules refuses a card that does not meet them yet, listing what is missing.',\n inputSchema: {\n card: cardArgument,\n column: z.string().min(1).describe('The column to move it to, by its slug or name: in_progress, \"In Progress\".'),\n },\n run: async (transport, input) => await transport.cardMove(input),\n})\n","import { cardArgument, defineKanboTool } from './tool'\n\n/** The pull requests a card names. */\nexport const cardPullRequestsTool = defineKanboTool({\n name: 'kanbo_card_pull_requests',\n title: 'List a card\\'s pull requests',\n description: 'The pull requests linked to the card, in the order they were linked.',\n inputSchema: {\n card: cardArgument,\n },\n run: async (transport, input) => await transport.cardPullRequests(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/** What a card says: its title, what it is about, how urgent it is, how it is labelled. */\nexport const cardUpdateTool = defineKanboTool({\n name: 'kanbo_card_update',\n title: 'Update a card',\n description: 'Change what a card says: its title, its description, its priority, its labels or where its work happens.',\n inputSchema: {\n card: cardArgument,\n title: z.string().min(1).optional().describe('What the card is called.'),\n description: z.string().optional().describe('What the card is about. Replaces the description it has.'),\n priority: z.enum(['none', 'low', 'medium', 'high', 'urgent']).optional(),\n labels: z.array(z.string()).optional().describe('The labels the card carries, replacing the ones it has.'),\n executionMode: executionModeArgument.optional(),\n },\n run: async (transport, { card, ...patch }) => {\n // An update that names no field still writes: the board version goes up and\n // every reader is told the board changed when nothing did.\n if (Object.keys(patch).length === 0) {\n throw new Error('Nothing to update. Pass title, description, priority, labels or executionMode.')\n }\n return await transport.cardUpdate({ card, ...patch })\n },\n})\n","import { defineKanboTool } from './tool'\n\n/** The columns of the board, with the slug a card is moved by. */\nexport const columnsTool = defineKanboTool({\n name: 'kanbo_columns',\n title: 'Columns of the board',\n description: 'List this board\\'s columns in board order, each with the slug kanbo_card_move takes, what the column means here, '\n + 'and the entryRules a card must meet before you move it in.',\n inputSchema: {},\n run: async transport => await transport.columns(),\n})\n","import { defineKanboTool } from './tool'\n\n/** What the board tells an agent about itself, in the board's own words. */\nexport const primeTool = defineKanboTool({\n name: 'kanbo_prime',\n title: 'Read the board',\n description: 'Read what this board is: its columns, what each one means, and the rules a card travels by. Run this first.',\n inputSchema: {},\n run: async transport => (await transport.prime()).text,\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** The cards an agent may pick up right now. */\nexport const readyTool = defineKanboTool({\n name: 'kanbo_ready',\n title: 'Cards ready to pick up',\n description: 'List the cards that are spelled out, that nobody is working on and that are waiting for no one, in board order.',\n inputSchema: {\n limit: z.number().int().positive().optional().describe('How many cards to return. All of them when absent.'),\n },\n run: async (transport, input) => await transport.ready(input),\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** How a run ended. An app that launches agents ends the runs it started; this is for the ones nobody launched. */\nexport const runFinishTool = defineKanboTool({\n name: 'kanbo_run_finish',\n title: 'Finish a run',\n description: 'Record how a run you started ended, so the card stops saying someone is working on it.',\n inputSchema: {\n run: z.string().min(1).describe('The run, by the id kanbo_run_start gave back.'),\n state: z.enum(['finished', 'failed', 'stopped']).describe('How it ended.'),\n errorText: z.string().min(1).optional().describe('What went wrong, for a run that failed.'),\n },\n run: async (transport, input) => await transport.runFinish(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/**\n * Saying a card is being worked on.\n *\n * Only an agent that launched itself needs this. An app that launches agents writes the run row\n * before the agent it launched starts, which is why this tool is not published\n * to one.\n */\nexport const runStartTool = defineKanboTool({\n name: 'kanbo_run_start',\n title: 'Start a run',\n description: 'Record that you have started working on a card, so nobody else picks it up while you are on it.',\n inputSchema: {\n card: cardArgument,\n agent: z.string().min(1).describe('What to call whoever is working on the card.'),\n branch: z.string().min(1).optional().describe('The branch the work happens on.'),\n executionMode: executionModeArgument.optional(),\n session: z.string().min(1).optional().describe('Your own log of this run, if you have one: claude:<session id> from Claude Code, codex:<session id> from Codex (the session id Codex prints).'),\n },\n run: async (transport, input) => await transport.runStart(input),\n})\n","import { defineKanboTool } from './tool'\n\n/** The board's sprints, the current one marked. Read-only: closing a sprint is a person's (ruling 5x-4). */\nexport const sprintsTool = defineKanboTool({\n name: 'kanbo_sprints',\n title: 'Sprints of the board',\n description: 'List this board\\'s milestones as sprints by start date — dates in unix seconds, open and done card counts — '\n + 'with current: true on the open one running now. Read-only: closing a sprint is a person\\'s decision.',\n inputSchema: {},\n run: async transport => await transport.sprints(),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** What the card is doing right now, which is what a person watching sees. */\nexport const statusLineTool = defineKanboTool({\n name: 'kanbo_status_line',\n title: 'Set the status line',\n description: 'Say what the card is doing right now. Write one at every step, including before and after anything long-running.',\n inputSchema: {\n card: cardArgument,\n text: z.string().min(1).describe('One sentence, present tense, about what is happening on the card right now.'),\n },\n run: async (transport, input) => await transport.statusLine(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/**\n * The end of a turn that needs a person.\n *\n * There is no tool that approves. A person approves on the board, and the card\n * stays waiting until they do — an agent that could take its own card out of\n * waiting would make every card waiting for a person a lie.\n */\nexport const waitApprovalTool = defineKanboTool({\n name: 'kanbo_wait_approval',\n title: 'Hand the card to a person',\n description: 'Mark the card as waiting for a person and end your turn. A person approves or returns it; you will be told the answer.',\n inputSchema: {\n card: cardArgument,\n text: z.string().min(1).optional().describe('The status line to leave, saying what the person is being asked to look at.'),\n },\n run: async (transport, input) => await transport.waitApproval(input),\n})\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport type { KanboToolName } from '../tool-names'\nimport type { KanboToolTransport } from '../transport'\nimport { cardCommentTool } from './card-comment'\nimport { cardCreateTool } from './card-create'\nimport { cardGetTool } from './card-get'\nimport { cardLinkPrTool } from './card-link-pr'\nimport { cardListTool } from './card-list'\nimport { cardMoveTool } from './card-move'\nimport { cardPullRequestsTool } from './card-pull-requests'\nimport { cardUpdateTool } from './card-update'\nimport { columnsTool } from './columns'\nimport { primeTool } from './prime'\nimport { readyTool } from './ready'\nimport { runFinishTool } from './run-finish'\nimport { runStartTool } from './run-start'\nimport { sprintsTool } from './sprints'\nimport { statusLineTool } from './status-line'\nimport type { KanboTool } from './tool'\nimport { waitApprovalTool } from './wait-approval'\n\n/**\n * Every tool the board has, in the order it declares them.\n *\n * The list is the board's, not a server's: the same sixteen are registered\n * whether they run against a server or against the board directly, and the\n * only thing a host decides is which of them it publishes.\n */\nexport const KANBO_TOOLS: readonly KanboTool[] = [\n primeTool,\n readyTool,\n columnsTool,\n sprintsTool,\n cardGetTool,\n cardListTool,\n cardCreateTool,\n cardUpdateTool,\n cardMoveTool,\n cardCommentTool,\n cardLinkPrTool,\n cardPullRequestsTool,\n statusLineTool,\n waitApprovalTool,\n runStartTool,\n runFinishTool,\n]\n\n/**\n * The two tools an agent launched by an app has no use for: the app wrote the\n * run row before the agent started and ends it when the session settles. An agent\n * that launched itself — from a terminal, on another host — does have to record\n * its own run, which is why they exist at all.\n */\nconst RUN_TOOL_NAMES: readonly KanboToolName[] = ['kanbo_run_start', 'kanbo_run_finish']\n\n/** What a host decides about the board's tools. */\nexport interface KanboToolOptions {\n /** Publish `kanbo_run_start` and `kanbo_run_finish`. False for a host that manages runs itself. */\n includeRunTools: boolean\n}\n\n/** One tool bound to a transport, ready for an MCP server to register. */\nexport interface KanboToolRegistration {\n name: KanboToolName\n register: (server: McpServer) => void\n}\n\n/**\n * The board's tools, bound to the transport they run against.\n *\n * Nothing here reaches the board: the registration is what an MCP server needs\n * to publish the tool, and the transport is only called when a client actually\n * calls one.\n */\nexport function buildKanboTools(\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): KanboToolRegistration[] {\n return KANBO_TOOLS\n .filter(tool => options.includeRunTools || !RUN_TOOL_NAMES.includes(tool.name))\n .map(tool => ({\n name: tool.name,\n register: (server: McpServer) => {\n server.registerTool(\n tool.name,\n { title: tool.title, description: tool.description, inputSchema: tool.inputSchema },\n async (input: unknown) => await tool.run(transport, input),\n )\n },\n }))\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport { KANBO_MCP_INSTRUCTIONS } from './instructions'\nimport { registerKanboCapabilitiesResources } from './resources'\nimport type { KanboToolOptions } from './tools'\nimport { buildKanboTools } from './tools'\nimport type { KanboToolTransport } from './transport'\n\n/** The name a client sees this server under. */\nexport const KANBO_MCP_SERVER_NAME = 'kanbo'\n\n/** The version it reports. The tools it carries are the board's, and the board versions itself. */\nconst KANBO_MCP_SERVER_VERSION = KANBO_PACKAGE_VERSION\n\n/**\n * Add the board's tools to a server that already exists.\n *\n * This is how an app with an MCP server of its own carries them: one server holds every\n * tool an agent has, and the board's are part of that inventory rather than a\n * second server the runtime would have to be told about.\n */\nexport function registerKanboTools(\n server: McpServer,\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): void {\n for (const tool of buildKanboTools(transport, options)) {\n tool.register(server)\n }\n}\n\n/**\n * A server that is only the board — what `kanbo mcp` serves to a client that\n * brings no board tools of its own: an editor, an agent CLI, another machine.\n *\n * It carries the capabilities manifest as two resources, on top of the tools\n * `registerKanboTools` adds — a client here has no other way to learn the\n * board's rules, its canonical strings or its CLI equivalents before it calls\n * anything. An app's own MCP server does not get them through this\n * function: it documents its capabilities its own way, and adding a second\n * `kanbo://` resource namespace to an inventory that is not only the board's\n * is a decision for that server to make, not this one.\n *\n * For the same reason it alone sends `KANBO_MCP_INSTRUCTIONS` in the initialize\n * result: the instructions of a server that is only the board.\n */\nexport function createKanboMcpServer(\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): McpServer {\n const server = new McpServer({\n name: KANBO_MCP_SERVER_NAME,\n version: KANBO_MCP_SERVER_VERSION,\n }, { instructions: KANBO_MCP_INSTRUCTIONS })\n registerKanboTools(server, transport, options)\n registerKanboCapabilitiesResources(server)\n return server\n}\n","import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport type { Command } from 'commander'\n\nimport { createDbTransport } from '../../mcp/db-transport'\nimport { createKanboMcpServer } from '../../mcp/server'\nimport { createCliActor } from '../actor'\nimport { describeFailure } from '../failure'\n\n/**\n * `kanbo mcp` — the board as tools, for any MCP client.\n *\n * It serves the board directly — the file, or the external database the project\n * is bound to — because the tool that spawned it — Claude Code, Codex, Cursor —\n * may be the only thing running on this machine: an agent working in a checkout\n * with no server should still be able to drive its card. An app with an MCP\n * server of its own can hold the same tools there instead, over HTTP\n * (`kanbo/mcp`).\n *\n * Every write is filed under this shell, exactly as every `kanbo` command's\n * is, and all sixteen tools are published: an agent that started itself has to\n * record its own run, which is the one thing an app that launches agents does\n * for them.\n */\n\n/** What `kanbo mcp` is told, as every other command spells it. */\ninterface McpOptions {\n db?: string\n databaseUrl?: string\n workspace?: string\n}\n\nexport function registerMcpCommand(program: Command): void {\n program\n .command('mcp')\n .description('serve this board to an MCP client over stdio')\n // The output options every other command carries are absent on purpose:\n // stdout is the protocol here, and anything printed on it is a broken\n // session rather than a badly formatted one.\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to serve instead of a board file')\n .option('--workspace <nameOrId>', 'workspace the board tools are about')\n .action(async (options: McpOptions) => {\n const board = await createDbTransport({\n dbPath: options.db,\n databaseUrl: options.databaseUrl,\n workspaceId: options.workspace,\n actor: createCliActor(),\n })\n // Closed once, whichever way this ends. `onclose` is the client hanging\n // up and cannot be awaited — it is not an async hook — so the promise is\n // kept here and awaited by whoever gets there second. A connection that\n // fails to hang up is worth a line on stderr, which is not the protocol\n // stream, and never an unhandled rejection: that would turn a clean\n // shutdown into a crash.\n let closing: Promise<void> | undefined\n const close = (): Promise<void> => (closing ??= board.close().catch((error: unknown) => {\n console.error(describeFailure(error).message)\n }))\n\n const server = createKanboMcpServer(board, { includeRunTools: true })\n server.server.onclose = () => void close()\n try {\n await server.connect(new StdioServerTransport())\n }\n catch (error) {\n await close()\n throw error\n }\n })\n}\n","import type { Command } from 'commander'\n\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport { migrateBoardFile } from '../../sqlite/migrate'\nimport { openBoardDatabase } from '../../sqlite/open-database'\nimport type { BoardTargetCommandOptions } from '../command'\nimport { withTargetOptions } from '../command'\nimport type { BoardTarget } from '../db-target'\nimport { resolveDbTarget } from '../db-target'\nimport { CliError, printResult, readFormat } from '../output'\nimport { openPostgresBoard } from '../postgres-board'\n\n/**\n * `kanbo migrate` — creating the board in a database that has none, and\n * bringing one that has an older board up to date.\n *\n * It exists for the two homes this package owns: an external board, which has\n * no other owner at all, and a board file of a project's own, which this\n * package builds and this package alone keeps current (ruling 5-5). A host app's\n * own database is neither — its schema is the app's, and the app migrates it —\n * so this command refuses to touch it.\n *\n * It is the owner's command, so against an external board it always resolves\n * the board's own connection string and never the agent's, whatever shell it\n * is run from: `kanban_agent` may not create a table, and an administrator who\n * happens to be standing in an agent's shell would get a permission\n * error instead of a migration.\n */\n\n/** What a person is told when they point this at a host app's own database file. */\nexport const BOARD_FILE_NOT_MIGRATED_MESSAGE\n = 'This database file belongs to the app the board lives in, and that app migrates it. Pass --database-url, set '\n + 'KANBO_DATABASE_URL, or point --db at a board file of this project\\'s own (kanbo init --file).'\n\nexport function registerMigrateCommand(program: Command): void {\n withTargetOptions(program\n .command('migrate')\n .description('create or update the board schema in an external Postgres database, or a board file of its own'))\n .action(async (options: BoardTargetCommandOptions) => {\n readFormat(options)\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: false,\n })\n if (target.kind === 'sqlite') {\n await migrateOwnFile(target, options)\n return\n }\n\n const board = await openPostgresBoard(target.url)\n try {\n // Drizzle keeps what it applied in `__drizzle_migrations`, which is\n // what makes running this again say the same thing and change nothing.\n await board.migrate()\n }\n finally {\n await board.close()\n }\n\n const database = maskDatabaseUrl(target.url)\n printResult({ value: { database }, text: `The board schema is up to date — ${database}` }, options)\n })\n}\n\n/**\n * Migrate a board file — this package's own chain, and only on a file this\n * package created in the first place. A host app's database carries no such\n * marker and is refused: `kanbo migrate` is never how an app's own database\n * gets its schema.\n */\nasync function migrateOwnFile(\n target: Extract<BoardTarget, { kind: 'sqlite' }>,\n options: BoardTargetCommandOptions,\n): Promise<void> {\n if (target.owner !== 'kanbo') {\n throw new CliError(1, BOARD_FILE_NOT_MIGRATED_MESSAGE)\n }\n\n const board = await openBoardDatabase(target.path)\n try {\n // Drizzle's own migrator, same as `kanbo init --file`: idempotent by\n // `__drizzle_migrations`, so running this again says the same thing.\n migrateBoardFile(board.database)\n }\n finally {\n board.close()\n }\n\n printResult({\n value: { database: target.path },\n text: `The board schema is up to date — ${target.path}`,\n }, options)\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\n\n/**\n * `kanbo prime` — what the board tells an agent about itself.\n *\n * The first thing an agent should run, and the reason the instruction block\n * `kanbo init` writes says so: the columns of this board with their meaning,\n * and the rules a card travels by. The text belongs to the board, not to this\n * tool, so the command only prints what `buildPrimeText` says.\n */\nexport function registerPrimeCommand(program: Command): void {\n withBoardOptions(program\n .command('prime')\n .description('print the columns of this board and the rules a card travels by'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const text = await session.ops.buildPrimeText(session.workspace.id)\n return { value: { text }, text }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCommandOptions } from '../command'\nimport { parseCount, runBoardCommand, withBoardOptions } from '../command'\nimport { CARD_VIEW_FIELDS, describeCards, projectCard } from '../view'\n\ninterface ReadyOptions extends BoardCommandOptions {\n limit?: number\n}\n\n/**\n * `kanbo ready` — the cards an agent may pick up right now.\n *\n * What counts as ready is the board's rule, not this tool's: spelled out,\n * nobody working on it, nobody's turn but the agent's. An agent asking for work\n * takes the first line and starts there.\n */\nexport function registerReadyCommand(program: Command): void {\n withBoardOptions(program\n .command('ready')\n .description('cards that are spelled out, unclaimed and nobody else\\'s turn')\n .option('--limit <count>', 'how many cards to print', parseCount))\n .action(async (options: ReadyOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const columns = await session.ops.listColumns(session.workspace.id)\n const rows = await session.ops.listReady({ workspaceId: session.workspace.id, limit: options.limit })\n const runs = await session.ops.readBoardProjectionForIssues(rows.map(card => card.id))\n const cards = rows.map((card, index) => projectCard(card, columns, runs[index]))\n return { value: cards, text: describeCards(cards), fields: CARD_VIEW_FIELDS }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { describeCard, projectCard } from '../view'\n\ninterface ReturnOptions extends BoardCommandOptions {\n comment: string\n to?: string\n}\n\n/**\n * `kanbo return` — a person sending a card back with a reason.\n *\n * The reason is required because it is the point: it goes on the card as the\n * person's comment and as the status line, so the next agent to pick the card\n * up reads why it came back before anything else. Whatever was running on the\n * card is stopped in the same write, by the board.\n *\n * Nothing else happens here. A chat session parked on this card is woken by\n * the server, which is watching the board — the terminal does not have to know\n * that anything is waiting.\n */\nexport function registerReturnCommand(program: Command): void {\n withBoardOptions(program\n .command('return')\n .description('send a card back a column, with the reason on it')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--comment <text>', 'why the card is coming back')\n .option('--to <column>', 'the column to send it to, by slug, name or id'))\n .action(async (reference: string, options: ReturnOptions) => {\n const actor = requireHumanActor('return')\n\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const { card, stoppedRunIds } = await session.ops.returnCard(existing.id, {\n comment: options.comment,\n toStatusName: options.to,\n }, actor)\n\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(card.id),\n ])\n const view = projectCard(card, columns, runs)\n const stopped = stoppedRunIds.length > 0 ? `\\nStopped ${stoppedRunIds.length} run(s)` : ''\n return {\n value: { card: view, stoppedRunIds },\n text: `Returned ${view.id} to ${view.column ?? '—'}${stopped}\\n\\n${describeCard(view)}`,\n }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport { BOARD_AGENT_ROLE, BOARD_PERSON_ROLE, BOARD_ROLE_SQL } from '../../postgres/roles.sql'\nimport type { BoardTargetCommandOptions } from '../command'\nimport { withTargetOptions } from '../command'\nimport { resolveDbTarget } from '../db-target'\nimport { CliError, printResult, readFormat } from '../output'\nimport { openPostgresBoard } from '../postgres-board'\n\n/**\n * `kanbo roles` — the two roles an external board is worked through, and the\n * rules the database itself keeps about them.\n *\n * Two commands rather than one because applying them is a privileged act: the\n * SQL creates roles and triggers, which needs the owner of the board's tables,\n * and plenty of databases are administered by someone who will never run this\n * tool. `print` hands them the script to read and to apply themselves; `apply`\n * runs it for whoever already holds the owner's connection string.\n *\n * No password is ever accepted here, on the command line or anywhere else. A\n * password on a command line is in the shell's history and in the process list\n * of every other user on the machine, and the one place it belongs is the\n * `alter role` an administrator types themselves.\n *\n * `apply` is the owner's command, so it always resolves the board's own\n * connection string and never the agent's, whatever shell it is run from — the\n * role being configured cannot be the role doing the configuring. `print`\n * resolves nothing at all and therefore takes no options: it is one constant,\n * on stdout, for a person to read or redirect into `psql`.\n */\n\n/** What a person is told when there is no external board to apply the roles to. */\nexport const ROLES_NEED_A_DATABASE_MESSAGE\n = 'The board roles belong to an external Postgres board. A board file is protected by whoever can open it — '\n + 'pass --database-url or set KANBO_DATABASE_URL.'\n\nexport function registerRolesCommands(program: Command): void {\n const roles = program\n .command('roles')\n .description('the two database roles an external board is worked through')\n\n roles\n .command('print')\n .description('print the SQL that creates the roles and the rules they live under')\n .action(() => {\n console.log(BOARD_ROLE_SQL)\n })\n\n withTargetOptions(roles\n .command('apply')\n .description('run that SQL against the external board, as its owner'))\n .action(async (options: BoardTargetCommandOptions) => {\n readFormat(options)\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: false,\n })\n if (target.kind !== 'postgres') {\n throw new CliError(1, ROLES_NEED_A_DATABASE_MESSAGE)\n }\n\n const board = await openPostgresBoard(target.url)\n try {\n // Written to be run again: every role is created only if it is missing\n // and every trigger is replaced, so this is how the rules are updated too.\n await board.runScript(BOARD_ROLE_SQL)\n }\n finally {\n await board.close()\n }\n\n const database = maskDatabaseUrl(target.url)\n printResult({\n value: { database, roles: [BOARD_PERSON_ROLE, BOARD_AGENT_ROLE] },\n text: describeApplied(database),\n }, options)\n })\n}\n\n/**\n * What is left to do, said where the person is standing. The roles can log in\n * and have no password at all, which is not a state to leave a shared database\n * in and not one this tool can fix for them.\n */\nfunction describeApplied(database: string): string {\n return [\n `Applied the board roles to ${database} — ${BOARD_PERSON_ROLE} and ${BOARD_AGENT_ROLE}.`,\n '',\n 'Both were created with login and no password. Set one before handing out a connection string:',\n ` alter role ${BOARD_AGENT_ROLE} with password '…';`,\n ` alter role ${BOARD_PERSON_ROLE} with password '…';`,\n '',\n `A session must connect AS the role: the rules read current_user, so a login merely granted membership in `\n + `${BOARD_AGENT_ROLE} keeps its own name and none of them fires.`,\n ].join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport type { FinishRunInput } from '../../ops/runs'\nimport type { Issue, IssueRun } from '../../sqlite/schema'\nimport { createCliActor, requireHumanActor } from '../actor'\nimport type { BoardCommandOptions, BoardSession } from '../command'\nimport { parseExecutionMode, requireCard, requireRun, runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { projectRun, RUN_VIEW_FIELDS } from '../view'\n\nconst RUN_STATES = ['finished', 'failed', 'stopped'] as const\n\ninterface StartOptions extends BoardCommandOptions {\n agent: string\n branch?: string\n executionMode?: Issue['executionMode']\n session?: string\n}\n\ninterface AttachSessionOptions extends BoardCommandOptions {\n replace?: boolean\n}\n\ninterface FinishOptions extends BoardCommandOptions {\n state: FinishRunInput['state']\n errorText?: string\n}\n\n/**\n * `kanbo run …` — saying that a card is being worked on, and how that ended.\n *\n * A run launched from a terminal is recorded exactly like one an app launched:\n * the card gets a line saying who is on it and a comment naming the attempt, so\n * `kanbo ready` stops offering the card to the next agent that asks. Its\n * launch is filed as `external` — no app started this, and a board that\n * claimed otherwise would send someone looking for a session that never existed.\n */\nexport function registerRunCommands(program: Command): void {\n const run = program\n .command('run')\n .description('the launches of a card')\n\n withBoardOptions(run\n .command('start')\n .description('say that you are working on a card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--agent <name>', 'what to call whoever is working')\n .option('--branch <branch>', 'the branch the work happens on')\n .option('--execution-mode <mode>', 'worktree or main; defaults to what the card says', parseExecutionMode)\n .option('--session <ref>', 'your own log of this run: claude:<session id> or codex:<session id>'))\n .action(async (reference: string, options: StartOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const card = await requireCard(session, reference)\n const started = await session.ops.startRun(card.id, {\n agentName: options.agent,\n branch: options.branch,\n executionMode: options.executionMode,\n externalSessionRef: options.session,\n launchedByKind: 'external',\n }, createCliActor())\n const view = projectRun({ ...started, attempt: await attemptFor(session, started) })\n return { value: view, text: `Run ${view.id} started on ${card.id}`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('attach-session')\n .description('say which of your own logs a run is, while it runs or after it ended; a run keeps the first one named')\n .argument('<runId>', 'the run, as `kanbo run start` printed it')\n .argument('<ref>', 'claude:<session id> or codex:<session id>')\n .option('--replace', 'put this log in place of the one the run names; a person\\'s own terminal only'))\n .action(async (runId: string, ref: string, options: AttachSessionOptions) => {\n // Checked before the board is opened, the same as `approve`.\n const actor = options.replace ? requireHumanActor('run attach-session --replace') : null\n await runBoardCommand(options, 'write', async (session) => {\n const found = await requireRun(session, runId)\n const attached = actor\n ? await session.ops.clearRunSessionRef(found.id, actor, { replaceWith: ref })\n : await session.ops.recordRunSessionRef(found.id, ref)\n const view = projectRun({ ...attached, attempt: await attemptFor(session, attached) })\n return { value: view, text: `Run ${view.id} carries ${attached.externalSessionRef}`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('clear-session')\n .description('say that the log a run names is not its log; the run then names none')\n .argument('<runId>', 'the run, as `kanbo run start` printed it'))\n .action(async (runId: string, options: BoardCommandOptions) => {\n const actor = requireHumanActor('run clear-session')\n await runBoardCommand(options, 'write', async (session) => {\n const found = await requireRun(session, runId)\n const cleared = await session.ops.clearRunSessionRef(found.id, actor)\n const view = projectRun({ ...cleared, attempt: await attemptFor(session, cleared) })\n return { value: view, text: `Run ${view.id} names no log`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('finish')\n .description('say how a run ended')\n .argument('<runId>', 'the run, as `kanbo run start` printed it')\n .requiredOption('--state <state>', `one of ${RUN_STATES.join(', ')}`, parseRunState)\n .option('--error-text <text>', 'what went wrong, for a failed run'))\n .action(async (runId: string, options: FinishOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const run = await requireRun(session, runId)\n const finished = await session.ops.finishRun(run.id, {\n state: options.state,\n errorText: options.errorText,\n }, createCliActor())\n const view = projectRun({ ...finished, attempt: await attemptFor(session, finished) })\n return { value: view, text: `Run ${view.id} ${view.state}`, fields: RUN_VIEW_FIELDS }\n })\n })\n}\n\n/**\n * Which attempt a run is. `startRun` and `finishRun` hand back the row\n * itself; the count only exists once every run of the card is lined up.\n */\nasync function attemptFor(session: BoardSession, run: IssueRun): Promise<number> {\n const runs = await session.ops.listRuns(run.issueId)\n return runs.find(candidate => candidate.id === run.id)?.attempt ?? runs.length\n}\n\nfunction parseRunState(value: string): FinishRunInput['state'] {\n const state = RUN_STATES.find(candidate => candidate === value)\n if (!state) {\n throw new CliError(1, `Unknown run state \"${value}\". Use one of ${RUN_STATES.join(', ')}.`)\n }\n return state\n}\n","import type { BoardActor } from '../ops/types'\n\n/**\n * Who a request writes as.\n *\n * An app with sign-in knows its caller from the session a request carries.\n * This server has none to ask, so it says what `kanbo` says in a terminal: every\n * write is filed `external`, under the user who started the server. What a\n * request cannot do is name its own kind — a header that could say `user`\n * would make approval worth nothing — so the only thing a request may say\n * about itself is that it is *less*: `x-kanbo-actor: agent`.\n *\n * Acting for a person takes two things: the server was not started from a\n * shell started for an agent (the detection `kanbo approve` refuses\n * on), and the request presented the server's token. Without a token anyone\n * who can reach the port could be anything, so nobody acts for a person.\n */\n\n/** The header a request downgrades itself with. */\nexport const ACTOR_HEADER = 'x-kanbo-actor'\n\n/** Who the server may write as, fixed when it starts. */\nexport interface ServeActors {\n /** Every write is filed under this — `external`, as the command line files it. */\n writer: BoardActor\n /** The person who started the server, or `null` when it was started from an agent's shell. */\n person: BoardActor | null\n}\n\n/** Who one request writes as, by what it is doing. */\nexport interface RequestActors {\n /** Status lines, comments, runs, links, context refs — the writes anyone may make. */\n writer: BoardActor\n /**\n * Putting a card in a column — create, update, move, reorder, bulk. For a\n * person the column's entry rules warn (`unmetRules`) instead of refusing, as\n * they do for a person on the board page and in their own terminal.\n */\n placement: BoardActor\n /**\n * What only a person may do — approve, return, close a sprint, set a\n * column's entry rules, delete a comment the board wrote, drop a link somebody\n * else made. When the request may not act for one this is the writer, and the\n * board refuses it with its own 403.\n */\n person: BoardActor\n}\n\n/** What a request says about itself that decides its actors. */\nexport interface RequestIdentity {\n /** The `x-kanbo-actor` header, as many times as it was sent. */\n actorHeader: string | string[] | undefined\n /** Whether the request presented the server's token — only then may it act for a person. */\n authenticated: boolean\n}\n\n/**\n * The actors one request gets: the person's rights only for a request that\n * presented the token, on a server whose starter may act as one, and that did\n * not give them up with `agent` among the values of `x-kanbo-actor`.\n */\nexport function resolveRequestActors(actors: ServeActors, identity: RequestIdentity): RequestActors {\n const person = identity.authenticated && !saysAgent(identity.actorHeader) ? actors.person : null\n if (!person) {\n return { writer: actors.writer, placement: actors.writer, person: actors.writer }\n }\n return { writer: actors.writer, placement: { ...actors.writer, placesForPerson: true }, person }\n}\n\n/** Whether any comma-separated value of the header, however it is spelled or repeated, is `agent`. */\nfunction saysAgent(header: string | string[] | undefined): boolean {\n return [header ?? []].flat().flatMap(value => value.split(',')).some(value => value.trim().toLowerCase() === 'agent')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport type { ServerResponse } from 'node:http'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/**\n * The board page `kanbo serve` answers `/` with: a person opens the server's\n * address in a browser and works the board there.\n *\n * The page holds no data — everything it shows it asks the `/issues` routes\n * for, with the token the person typed — so it is served without the token.\n * Its script and stylesheet are built from `./page/` into `dist/page/` by\n * `tsdown.page.config.ts`; this module only finds them and answers with them,\n * under a policy that lets the page load nothing but itself and talk to\n * nothing but this server.\n */\n\n/** What the page may load and talk to: itself, and nothing else. */\nconst BOARD_PAGE_CSP = [\n 'default-src \\'none\\'',\n 'script-src \\'self\\'',\n 'style-src \\'self\\'',\n 'connect-src \\'self\\'',\n 'img-src \\'self\\' data:',\n 'base-uri \\'none\\'',\n 'form-action \\'none\\'',\n 'frame-ancestors \\'none\\'',\n].join('; ')\n\nconst SCRIPT_PATH = '/assets/board.js'\nconst STYLESHEET_PATH = '/assets/board.css'\n\n/** The document itself. No data in it; the `data:` icon keeps the browser from asking for `/favicon.ico`. */\nconst BOARD_PAGE_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Kanbo</title>\n<link rel=\"icon\" href=\"data:,\">\n<link rel=\"stylesheet\" href=\"${STYLESHEET_PATH}\">\n<script src=\"${SCRIPT_PATH}\" defer></script>\n</head>\n<body>\n<div id=\"app\"></div>\n</body>\n</html>\n`\n\n/** The built script and stylesheet, read once when the server starts. */\nexport interface BoardPageAssets {\n script: string\n stylesheet: string\n}\n\n/**\n * Where the built page is, as seen from the running file: `dist/page/` beside\n * the bundled binary, or the package's `dist/page/` from `src/serve/` in a\n * checkout — which exists only once `npm run build` has run.\n */\nexport function resolveBoardPageDirectory(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n const candidates = [join(here, 'page'), join(here, '..', '..', 'dist', 'page')]\n return candidates.find(directory => existsSync(join(directory, 'board.js'))) ?? candidates[1]\n}\n\n/** The page's two files from `directory`, or `null` when the page has not been built there. */\nexport function readBoardPageAssets(directory: string): BoardPageAssets | null {\n const script = join(directory, 'board.js')\n const stylesheet = join(directory, 'board.css')\n if (!existsSync(script) || !existsSync(stylesheet)) {\n return null\n }\n return { script: readFileSync(script, 'utf8'), stylesheet: readFileSync(stylesheet, 'utf8') }\n}\n\n/** Whether a path is one of the page's own. */\nexport function isBoardPagePath(pathname: string): boolean {\n return pathname === '/' || pathname === SCRIPT_PATH || pathname === STYLESHEET_PATH\n}\n\n/**\n * Answer a page path. With no built page, `/` says how to build it rather than\n * leaving a person at a blank screen or a `401`.\n */\nexport function sendBoardPage(response: ServerResponse, pathname: string, assets: BoardPageAssets | null): void {\n if (!assets) {\n const body = JSON.stringify({ code: 'not_found', message: 'The board page is not built. Run: npm run build' })\n response.writeHead(404, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) })\n response.end(body)\n return\n }\n const [contentType, body] = pathname === SCRIPT_PATH\n ? ['text/javascript; charset=utf-8', assets.script]\n : pathname === STYLESHEET_PATH\n ? ['text/css; charset=utf-8', assets.stylesheet]\n : ['text/html; charset=utf-8', BOARD_PAGE_HTML]\n response.writeHead(200, {\n 'content-type': contentType,\n 'content-length': Buffer.byteLength(body),\n 'cache-control': 'no-cache',\n 'content-security-policy': BOARD_PAGE_CSP,\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n })\n response.end(body)\n}\n","import type { BoardErrorCode } from '../domain/errors'\n\n/** What one board failure is over HTTP: the status, the sentence, and the code a client matches on when it is renamed. */\nexport interface BoardErrorResponse {\n status: number\n message: string\n code?: string\n}\n\n/**\n * What each board failure becomes over HTTP.\n *\n * The board names its failures and nothing else (`../domain/errors.ts`); the\n * status code and the sentence a client reads are decided here, once, for both\n * edges that serve it over HTTP — `kanbo serve` and any app that serves the\n * same routes — so a client written against one reads the other's failures the\n * same way. The\n * table is exhaustive on purpose — a new code in the package fails to compile\n * until this module says what it means to a caller — and it follows one rule: something the caller asked for that is not\n * there is 404, something the caller asked for that cannot be done is 400,\n * answering for a person — approving a card, sending it back or closing a\n * sprint — as anyone but one is 403, dropping a pull request link somebody else made is the same 403\n * for the same reason, and acting on a run that has already ended is 409,\n * because the answer would have been yes a moment earlier. An agent putting a\n * card in a column whose entry rules it does not meet yet is 409 for the same\n * reason turned around: the answer will be yes once the card meets them, and\n * `details.unmet` says what is missing.\n *\n * A failure may also be renamed here: the package names its codes after the\n * board, while every error the `/issues` routes answer with is named after the\n * issue, and a client matching on `issue_*` should not have to learn a second\n * spelling. The code carries through unchanged when no `code` is given.\n */\nexport const BOARD_ERROR_RESPONSES: Record<BoardErrorCode, BoardErrorResponse> = {\n board_approval_requires_user: { status: 403, message: 'Only a person can approve a card', code: 'issue_approval_requires_user' },\n board_column_rules_requires_user: { status: 403, message: 'Only a person can set what a column asks of a card', code: 'issue_column_rules_requires_user' },\n board_column_rules_unmet: { status: 409, message: 'The card does not meet what that column asks for yet', code: 'issue_column_rules_unmet' },\n board_entry_rule_invalid: { status: 400, message: 'Unknown column entry rule' },\n board_execution_mode_locked: { status: 400, message: 'Execution mode cannot change once the card has been launched' },\n board_pull_request_invalid: { status: 400, message: 'Not a GitHub pull request: give its URL or owner/repo#number' },\n board_pull_request_not_found: { status: 404, message: 'The card names no such pull request' },\n board_pull_request_not_yours: { status: 403, message: 'You did not link that pull request', code: 'issue_pull_request_not_yours' },\n board_return_no_previous_column: { status: 400, message: 'The card is already in the first column' },\n board_return_requires_user: { status: 403, message: 'Only a person can send a card back', code: 'issue_return_requires_user' },\n board_run_already_finished: { status: 409, message: 'Run has already finished' },\n board_run_not_found: { status: 404, message: 'Run not found' },\n board_run_session_ref_conflict: { status: 409, message: 'The run already names a different log of its own' },\n board_run_session_ref_invalid: { status: 400, message: 'Not an external session reference: give claude:<id> or codex:<id>' },\n board_run_session_ref_requires_user: { status: 403, message: 'Only a person can change or clear the log a run names' },\n board_schema_outdated: { status: 400, message: 'Board schema is outdated' },\n board_sprint_carry_invalid: { status: 400, message: 'Unfinished cards can only be carried to another open milestone of the same workspace' },\n board_sprint_close_requires_user: { status: 403, message: 'Only a person can close a sprint', code: 'issue_sprint_close_requires_user' },\n board_sprint_dates_invalid: { status: 400, message: 'A sprint cannot start after it is due' },\n issue_comment_delete_requires_user: { status: 403, message: 'Only a person can delete a comment the board wrote' },\n issue_comment_not_found: { status: 404, message: 'Comment not found' },\n issue_context_ref_invalid_index: { status: 400, message: 'Invalid context ref index' },\n issue_milestone_not_found: { status: 404, message: 'Milestone not found' },\n issue_not_found: { status: 404, message: 'Issue not found' },\n issue_parent_not_found: { status: 404, message: 'Parent issue not found' },\n issue_parent_self_reference: { status: 400, message: 'Issue cannot be its own parent' },\n issue_relation_not_found: { status: 404, message: 'Relation not found' },\n issue_relation_self_reference: { status: 400, message: 'An issue cannot be related to itself' },\n issue_status_name_empty: { status: 400, message: 'Status name must not be empty' },\n issue_status_not_found: { status: 404, message: 'Status not found' },\n issue_status_reference_conflict: { status: 400, message: 'Use either statusId or statusName, not both' },\n issue_workspace_not_found: { status: 404, message: 'Workspace not found' },\n}\n","import type { ZodError } from 'zod'\n\nimport { CliError, EXIT_SCHEMA_OUTDATED } from '../cli/output'\nimport { BoardError } from '../domain/errors'\nimport { BOARD_ERROR_RESPONSES } from './board-error-responses'\n\n/**\n * What a failed request answers with.\n *\n * The body is `{ code, message, details? }`, and a board failure goes through\n * `BOARD_ERROR_RESPONSES`, the one table every server of the `/issues` routes\n * answers with, so a client matching on a code or a status reads them alike.\n */\n\n/** The JSON body of every failure. */\nexport interface ServeErrorBody {\n code: string\n message: string\n details?: Record<string, unknown>\n}\n\n/** A failure the HTTP edge names itself — a request it cannot route, read or let in. */\nexport class ServeError extends Error {\n readonly status: number\n readonly code: string\n readonly details?: Record<string, unknown>\n\n constructor(status: number, code: string, message: string, details?: Record<string, unknown>) {\n super(message)\n this.status = status\n this.code = code\n this.details = details\n }\n}\n\n/** Where an invalid value was found. */\nexport type ValidationSource = 'body' | 'query' | 'params'\n\n/**\n * A request whose body, query or path did not have the shape the route takes:\n * 400 `validation_error`, with one `{ path, message }` per problem.\n */\nexport function validationError(source: ValidationSource, error: ZodError): ServeError {\n return new ServeError(400, 'validation_error', 'request validation failed', {\n source,\n issues: error.issues.map(issue => ({\n path: issue.path.length > 0 ? issue.path.map(String).join('.') : 'root',\n message: issue.message,\n })),\n })\n}\n\n/**\n * The status and body a failure answers with, or `null` for a failure nobody\n * named — a driver error, a bug — which the server answers as a 500 and says\n * nothing more about to the client.\n *\n * The schema guard is the command line's (`assertWritableBoard`), and it\n * refuses with a `CliError` a terminal prints; over HTTP it is the board's own\n * `board_schema_outdated`.\n */\nexport function readErrorResponse(error: unknown): { status: number, body: ServeErrorBody } | null {\n if (error instanceof ServeError) {\n return { status: error.status, body: withDetails({ code: error.code, message: error.message }, error.details) }\n }\n if (error instanceof CliError && error.exitCode === EXIT_SCHEMA_OUTDATED) {\n return readErrorResponse(new BoardError('board_schema_outdated'))\n }\n if (error instanceof BoardError) {\n const response = BOARD_ERROR_RESPONSES[error.code]\n return {\n status: response.status,\n body: withDetails({ code: response.code ?? error.code, message: response.message }, error.details),\n }\n }\n return null\n}\n\nfunction withDetails(body: ServeErrorBody, details: Record<string, unknown> | undefined): ServeErrorBody {\n return details === undefined ? body : { ...body, details }\n}\n","import type { z } from 'zod'\n\nimport type { RequestActors } from './actors'\nimport { validationError } from './errors'\n\n/**\n * A route table as small as the server needs: a method, a path with `:name`\n * segments, the zod shapes of its query and body, and a handler.\n *\n * Routes match in the order they are listed, so a path with a fixed segment —\n * `/issues/statuses` — is listed before the `/issues/:id` it would otherwise\n * fall into — the precedence most HTTP routers give static segments.\n */\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'\n\n/** What a handler is handed: the path parameters, the parsed query and body, and who the request writes as. */\ninterface RouteInput<TQuery, TBody> {\n params: Record<string, string>\n query: TQuery\n body: TBody\n actors: RequestActors\n}\n\n/** One route as it is declared. */\nexport interface RouteSpec<TQuery, TBody> {\n method: HttpMethod\n path: string\n query?: z.ZodType<TQuery>\n body?: z.ZodType<TBody>\n /**\n * Whether the route writes the board. A write asks the schema guard first, so\n * a board older than this build is never written to — the same guard every\n * `kanbo` command asks.\n */\n write?: boolean\n handle: (input: RouteInput<TQuery, TBody>) => Promise<unknown>\n}\n\n/** A route with its types erased, as the table holds it. */\nexport interface Route {\n method: HttpMethod\n segments: string[]\n write: boolean\n run: (input: { params: Record<string, string>, query: Record<string, string | string[]>, body: unknown, actors: RequestActors }) => Promise<unknown>\n}\n\n/** Declare a route; the query and body reach the handler parsed, or the request is a 400. */\nexport function route<TQuery = Record<string, never>, TBody = undefined>(spec: RouteSpec<TQuery, TBody>): Route {\n return {\n method: spec.method,\n segments: splitPath(spec.path),\n write: spec.write ?? false,\n run: async ({ params, query, body, actors }) => {\n const parsedQuery = spec.query ? spec.query.safeParse(query) : { success: true as const, data: query as TQuery }\n if (!parsedQuery.success) {\n throw validationError('query', parsedQuery.error)\n }\n // A body-less request to a route that takes one is read as `{}`: every\n // body with nothing required — approve, wait-approval — may be left out.\n const parsedBody = spec.body ? spec.body.safeParse(body ?? {}) : { success: true as const, data: undefined as TBody }\n if (!parsedBody.success) {\n throw validationError('body', parsedBody.error)\n }\n return await spec.handle({ params, query: parsedQuery.data, body: parsedBody.data, actors })\n },\n }\n}\n\n/** The route a request names and its path parameters, or `null` when no route does. */\nexport function matchRoute(routes: readonly Route[], method: string, path: string): { route: Route, params: Record<string, string> } | null {\n const segments = splitPath(path)\n for (const candidate of routes) {\n if (candidate.method !== method || candidate.segments.length !== segments.length) {\n continue\n }\n const params: Record<string, string> = {}\n const matches = candidate.segments.every((segment, index) => {\n if (segment.startsWith(':')) {\n params[segment.slice(1)] = segments[index]\n return segments[index].length > 0\n }\n return segment === segments[index]\n })\n if (matches) {\n return { route: candidate, params }\n }\n }\n return null\n}\n\n/**\n * A path as its segments, decoded, with a trailing slash ignored: `/issues`\n * and `/issues/` are the same list route. A segment that is not valid\n * percent-encoding is kept as it came, and names nothing on the board.\n */\nfunction splitPath(path: string): string[] {\n return path.split('/').filter(Boolean).map((segment) => {\n try {\n return decodeURIComponent(segment)\n }\n catch {\n return segment\n }\n })\n}\n","import { z } from 'zod'\n\n/**\n * The request shapes `kanbo serve` takes: the bodies and queries of the\n * board's `/issues` routes, in zod. An app that serves the same routes takes\n * the same shapes, so a client that builds a request for one builds it for the\n * other, and a field one does not take is not taken by the other either.\n */\n\nconst priority = z.enum(['none', 'low', 'medium', 'high', 'urgent'])\nconst category = z.enum(['triage', 'backlog', 'unstarted', 'started', 'completed', 'canceled'])\nconst executionMode = z.enum(['worktree', 'main'])\nconst milestoneStatus = z.enum(['open', 'closed'])\nconst relationType = z.enum(['blocks', 'duplicates', 'relates_to'])\nconst id = z.string().min(1)\n\n/**\n * The column's entry rules. Any string is taken, so an unknown rule is refused\n * by the board as `board_entry_rule_invalid` naming it, not by the schema.\n */\nconst entryRules = z.array(z.string()).nullable().optional()\n\n/** A list given as `a,b` or as the parameter repeated; `a=` is the empty list. */\nconst requiredCommaList = z.union([z.string(), z.array(z.string())])\n .transform(value => (Array.isArray(value) ? value : value.split(',')).map(item => item.trim()).filter(Boolean))\nconst commaList = requiredCommaList.optional()\n\n/** A number in a query string, where everything arrives as text. */\nconst queryNumber = z.string().regex(/^\\d+$/).transform(Number)\n\nexport const workspaceIdQuery = z.object({ workspaceId: id.optional() })\nexport const requiredWorkspaceIdQuery = z.object({ workspaceId: id })\nexport const workspaceIdsQuery = z.object({ workspaceIds: requiredCommaList })\nexport const searchQuery = z.object({ q: z.string(), limit: z.string().optional() })\nexport const readyQuery = z.object({ workspaceId: id, limit: queryNumber.optional() })\n\nexport const listIssuesQuery = z.object({\n workspaceId: id.optional(),\n workspaceIds: commaList,\n milestoneId: z.string().optional(),\n parentIssueId: z.string().optional(),\n priority: z.string().optional(),\n labels: commaList,\n statusId: z.string().optional(),\n})\n\nexport const createStatusBody = z.object({\n workspaceId: id,\n name: id,\n description: z.string().nullable().optional(),\n color: z.string().nullable().optional(),\n category: category.optional(),\n entryRules,\n})\n\nexport const updateStatusBody = z.object({\n name: id.optional(),\n description: z.string().nullable().optional(),\n color: z.string().nullable().optional(),\n entryRules,\n})\n\nexport const workspaceIdBody = z.object({ workspaceId: id })\n\nexport const reorderStatusesBody = z.object({ workspaceId: id, orderedIds: z.array(z.string()) })\n\nexport const createMilestoneBody = z.object({\n workspaceId: id,\n title: id,\n description: z.string().nullable().optional(),\n startDate: z.number().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n status: milestoneStatus.optional(),\n})\n\nexport const updateMilestoneBody = z.object({\n title: id.optional(),\n description: z.string().nullable().optional(),\n startDate: z.number().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n status: milestoneStatus.optional(),\n})\n\nexport const closeSprintBody = z.object({ carryTo: id.nullable().optional() })\n\nexport const createIssueBody = z.object({\n workspaceId: id,\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n parentIssueId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n statusName: id.nullable().optional(),\n dueDate: z.number().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n executionMode: executionMode.optional(),\n})\n\nexport const updateIssueBody = z.object({\n workspaceId: id.optional(),\n title: id.optional(),\n description: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n parentIssueId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n statusName: id.nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n order: z.number().optional(),\n executionMode: executionMode.optional(),\n})\n\nexport const reorderIssuesBody = z.object({\n workspaceId: id,\n orderedIds: z.array(z.string()),\n patch: z.object({\n issueIds: z.array(z.string()),\n fields: z.object({\n priority: priority.optional(),\n milestoneId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n }),\n }).optional(),\n})\n\nexport const bulkUpdateBody = z.object({\n issueIds: z.array(z.string()),\n update: z.object({\n statusId: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n }),\n})\n\nexport const setStatusLineBody = z.object({ statusLine: z.string().nullable() })\nexport const waitApprovalBody = z.object({ statusLine: z.string().nullable().optional() })\nexport const approveBody = z.object({ comment: z.string().nullable().optional() })\nexport const returnBody = z.object({ comment: id, toStatusName: id.nullable().optional() })\n\nexport const startRunBody = z.object({\n agentName: id,\n host: z.string().nullable().optional(),\n branch: z.string().nullable().optional(),\n executionMode: executionMode.optional(),\n launchedByKind: z.enum(['user', 'agent', 'external']).optional(),\n externalSessionRef: z.string().nullable().optional(),\n})\n\nexport const finishRunBody = z.object({\n state: z.enum(['finished', 'failed', 'stopped']),\n branch: z.string().nullable().optional(),\n worktreePath: z.string().nullable().optional(),\n errorText: z.string().nullable().optional(),\n externalSessionRef: z.string().nullable().optional(),\n})\n\nexport const addCommentBody = z.object({ content: id })\nexport const createRelationBody = z.object({ sourceIssueId: id, targetIssueId: id, type: relationType })\nexport const addContextRefBody = z.object({ ref: id })\nexport const addPullRequestBody = z.object({ url: id })\n","import type { BoardRunProjection } from '../board-store'\nimport type { IssueCommentAuthorView } from '../domain/activity-types'\nimport type { EntryRule, UnmetEntryRule } from '../domain/entry-rules'\nimport { readEntryRules } from '../domain/entry-rules'\nimport { commentAuthorReference } from '../domain/issue-activity'\nimport type { IssueActorReference, IssueView } from '../domain/issue-view'\nimport { isOutsideWriter, toIssueView } from '../domain/issue-view'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport type { BoardOps } from '../ops'\nimport type { IssueActivityResolver } from '../ops/activity'\nimport type { BoardCardWrite } from '../ops/cards'\nimport type { Issue, IssueComment, IssueStatus } from '../sqlite/schema'\n\n/**\n * The board's rows in the shapes the `/issues` routes answer with.\n *\n * Every shape here is built out of the package's own projections —\n * `toIssueView` and the run projection for a card, `readEntryRules` for a\n * column, `commentAuthorReference` for who wrote a comment — so an app serving\n * the same routes builds the same shapes, and a client written against one\n * reads the other unchanged. Where such an app fills in something only it\n * knows (an agent's name and avatar, a workspace's name), this server says the\n * id it holds instead.\n */\n\n/**\n * A card as the routes answer it: its row, decoded, with the run facts merged\n * in — how many times it has been launched and the run going on right now —\n * and, after a write that put it in a column whose entry rules it does not\n * meet, what it is missing.\n */\nexport type ServeCardView = IssueView & Omit<BoardRunProjection, 'issueId'> & { unmetRules?: UnmetEntryRule[] }\n\n/** A column, its entry rules read out of the stored JSON into a list. */\nexport type ServeStatusView = Omit<IssueStatus, 'entryRules'> & { entryRules: EntryRule[] }\n\n/** A comment, with who wrote it as a reader sees them. */\nexport type ServeCommentView = IssueComment & { author: IssueCommentAuthorView }\n\n/** Cards with their run facts, read in one projection for all of them; a card with no runs is `0` and `null`. */\nexport async function toCardViews(ops: BoardOps, cards: Issue[]): Promise<ServeCardView[]> {\n const projections = new Map(\n (await ops.readBoardProjectionForIssues(cards.map(card => card.id))).map(projection => [projection.issueId, projection]),\n )\n return cards.map(card => ({\n ...toIssueView(card),\n attemptCount: projections.get(card.id)?.attemptCount ?? 0,\n activeRun: projections.get(card.id)?.activeRun ?? null,\n }))\n}\n\n/** One card, the same way. */\nexport async function toCardView(ops: BoardOps, card: Issue): Promise<ServeCardView> {\n return (await toCardViews(ops, [card]))[0]\n}\n\n/** A card a write just placed, carrying `unmetRules` only when the write reported some. */\nexport async function toWrittenCardView(ops: BoardOps, card: BoardCardWrite): Promise<ServeCardView> {\n const { unmetRules, ...row } = card\n const view = await toCardView(ops, row)\n return unmetRules ? { ...view, unmetRules } : view\n}\n\nexport function toStatusView(status: IssueStatus): ServeStatusView {\n return { ...status, entryRules: readEntryRules(status) }\n}\n\nexport function toCommentView(comment: IssueComment): ServeCommentView {\n return { ...comment, author: resolveActor(commentAuthorReference(comment)) }\n}\n\n/**\n * Who a reader sees beside a row, from what the row holds and nothing else.\n *\n * For the kinds that need nobody else's tables: an outside writer is shown\n * under the name it gave, the board writing for itself is `kanbo`, and a person\n * is `You`. An agent and a provider target are rows of whatever app launched\n * them, which this server cannot read, so they are shown by the id the board\n * holds for them.\n */\nfunction resolveActor(actor: IssueActorReference): IssueCommentAuthorView {\n if (actor.kind === 'system') {\n const provenance = { authorId: actor.id, sourceChatSessionId: actor.sourceChatSessionId }\n if (isOutsideWriter(provenance)) {\n return { kind: 'system', id: provenance.authorId, displayName: provenance.authorId, avatarUrl: null, label: 'External' }\n }\n return { kind: 'system', id: null, displayName: 'kanbo', avatarUrl: null, label: 'System' }\n }\n if (actor.kind === 'agent') {\n return { kind: 'agent', id: actor.id, displayName: actor.id ?? 'Unknown agent', avatarUrl: null, label: null }\n }\n if (actor.kind === 'provider-target') {\n return { kind: 'provider-target', id: actor.id, displayName: actor.id ?? 'Unknown provider', avatarUrl: null, label: null }\n }\n return { kind: 'user', id: actor.id ?? '__self__', displayName: 'You', avatarUrl: null, label: null }\n}\n\n/**\n * What an Activity feed is read through here: the one workspace this server\n * serves, named by its identity; actors as `resolveActor` reads them; and an\n * agent assignee by its id.\n */\nexport function createActivityResolver(workspace: BoardWorkspaceIdentity): IssueActivityResolver {\n return {\n workspaceById: new Map([[workspace.id, { id: workspace.id, name: workspace.name }]]),\n resolveActor: async actor => resolveActor(actor),\n resolveAgentName: async agentId => agentId,\n }\n}\n","import type { BoardSession } from '../cli/command'\nimport { BoardError } from '../domain/errors'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { readChangeSeq, runBoardWrite } from '../ops/change-seq'\nimport type { BoardRunView } from '../ops/runs'\nimport type { Issue, IssueComment, IssueMilestone, IssueRelation, IssueRun, IssueStatus } from '../sqlite/schema'\nimport { ServeError } from './errors'\nimport type { Route } from './router'\nimport { route } from './router'\nimport {\n addCommentBody,\n addContextRefBody,\n addPullRequestBody,\n approveBody,\n bulkUpdateBody,\n closeSprintBody,\n createIssueBody,\n createMilestoneBody,\n createRelationBody,\n createStatusBody,\n finishRunBody,\n listIssuesQuery,\n readyQuery,\n reorderIssuesBody,\n reorderStatusesBody,\n requiredWorkspaceIdQuery,\n returnBody,\n searchQuery,\n setStatusLineBody,\n startRunBody,\n updateIssueBody,\n updateMilestoneBody,\n updateStatusBody,\n waitApprovalBody,\n workspaceIdBody,\n workspaceIdQuery,\n workspaceIdsQuery,\n} from './schemas'\nimport type { ServeCardView } from './views'\nimport {\n createActivityResolver,\n toCardView,\n toCardViews,\n toCommentView,\n toStatusView,\n toWrittenCardView,\n} from './views'\n\n/**\n * The board's `/issues` routes, for one board and one workspace.\n *\n * Every route the board operations can answer on their own. An app that serves\n * the same routes may hold every workspace of every board it can reach; this\n * server holds the one board and the one workspace it was started on. A\n * `workspaceId` naming any other is `issue_workspace_not_found`, and an id —\n * a card, a column, a milestone, a comment, a relation, a run — that belongs to\n * another workspace of the same database is not found either, exactly as if\n * the row were not there: one database may hold every project's board, and\n * a server started for one of them must not reach into the rest.\n */\n\nconst OK = { ok: true as const }\n\n/** How many cards a search answers with when the caller names no limit. */\nconst DEFAULT_SEARCH_LIMIT = 20\n\nexport function createIssueRoutes(session: BoardSession): Route[] {\n const { ops, store, workspace } = session\n const workspaceId = workspace.id\n const activityResolver = createActivityResolver(workspace)\n\n /** The served workspace, when that is the one the caller named — or named none. */\n function requireWorkspace(named: string | undefined): string {\n if (named !== undefined && named !== workspaceId) {\n throw new BoardError('issue_workspace_not_found', { workspaceId: named })\n }\n return workspaceId\n }\n\n async function requireCard(issueId: string): Promise<Issue> {\n const card = await store.issues.findInWorkspace(workspaceId, issueId)\n if (!card) {\n throw new BoardError('issue_not_found', { issueId })\n }\n return card\n }\n\n async function requireStatus(statusId: string): Promise<IssueStatus> {\n const status = await store.statuses.findInWorkspace(workspaceId, statusId)\n if (!status) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n return status\n }\n\n async function requireMilestone(milestoneId: string): Promise<IssueMilestone> {\n const milestone = await store.milestones.findInWorkspace(workspaceId, milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { milestoneId })\n }\n return milestone\n }\n\n /** Whether a row hanging off a card — a comment, a relation, a run — hangs off one of this workspace's. */\n async function isOnThisBoard(issueId: string): Promise<boolean> {\n return (await store.issues.findInWorkspace(workspaceId, issueId)) !== null\n }\n\n async function requireComment(commentId: string): Promise<IssueComment> {\n const comment = await store.comments.findById(commentId)\n if (!comment || !await isOnThisBoard(comment.issueId)) {\n throw new BoardError('issue_comment_not_found', { commentId })\n }\n return comment\n }\n\n async function requireRelation(relationId: string): Promise<IssueRelation> {\n const relation = await store.relations.findById(relationId)\n if (!relation || !await isOnThisBoard(relation.sourceIssueId)) {\n throw new BoardError('issue_relation_not_found', { relationId })\n }\n return relation\n }\n\n async function requireRun(runId: string): Promise<IssueRun> {\n const run = await store.runs.findById(runId)\n if (!run || !await isOnThisBoard(run.issueId)) {\n throw new BoardError('board_run_not_found', { runId })\n }\n return run\n }\n\n /**\n * Seed the standard columns before answering about them, the first time\n * anyone looks. It is a write, so it asks the schema guard — but\n * only when there is something to seed: a board older than this build that\n * already has columns is still one worth reading.\n */\n async function seedColumns(): Promise<void> {\n if (await store.statuses.countByWorkspace(workspaceId) === 0) {\n await session.assertWritable()\n await ops.ensureDefaultColumns(workspaceId)\n }\n }\n\n async function listStatusViews() {\n await seedColumns()\n return (await ops.listColumns(workspaceId)).map(toStatusView)\n }\n\n /** A run with the attempt number it is, counting from the card's first launch. */\n async function toRunView(run: IssueRun): Promise<BoardRunView> {\n const runs = await ops.listRuns(run.issueId)\n return { ...run, attempt: runs.findIndex(candidate => candidate.id === run.id) + 1 }\n }\n\n return [\n // Columns\n route({\n method: 'GET',\n path: '/issues/statuses',\n query: workspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n return await listStatusViews()\n },\n }),\n route({\n method: 'POST',\n path: '/issues/statuses',\n body: createStatusBody,\n write: true,\n handle: async ({ body, actors }) => toStatusView(await ops.createColumn({\n workspaceId: requireWorkspace(body.workspaceId),\n name: body.name,\n description: body.description ?? null,\n color: body.color ?? null,\n category: body.category ?? 'unstarted',\n entryRules: body.entryRules ?? null,\n }, actors.person)),\n }),\n route({\n method: 'POST',\n path: '/issues/statuses/reorder',\n body: reorderStatusesBody,\n write: true,\n handle: async ({ body }) => {\n requireWorkspace(body.workspaceId)\n for (const statusId of body.orderedIds) {\n await requireStatus(statusId)\n }\n await ops.reorderColumns(workspaceId, body.orderedIds)\n return OK\n },\n }),\n route({\n method: 'POST',\n path: '/issues/statuses/standard',\n body: workspaceIdBody,\n write: true,\n handle: async ({ body }) => (await ops.addStandardColumns(requireWorkspace(body.workspaceId))).statuses.map(toStatusView),\n }),\n route({\n method: 'PATCH',\n path: '/issues/statuses/:id',\n body: updateStatusBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireStatus(params.id)\n return toStatusView(await ops.updateColumn(params.id, body, actors.person))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/statuses/:id',\n write: true,\n handle: async ({ params }) => {\n await requireStatus(params.id)\n await ops.deleteColumn(params.id)\n return OK\n },\n }),\n\n // Milestones, and milestones read as sprints\n route({\n method: 'GET',\n path: '/issues/milestones',\n query: workspaceIdQuery,\n handle: async ({ query }) => await ops.listMilestones(requireWorkspace(query.workspaceId)),\n }),\n route({\n method: 'POST',\n path: '/issues/milestones',\n body: createMilestoneBody,\n write: true,\n handle: async ({ body }) => await ops.createMilestone({\n workspaceId: requireWorkspace(body.workspaceId),\n title: body.title,\n description: body.description ?? null,\n startDate: body.startDate ?? null,\n dueDate: body.dueDate ?? null,\n status: body.status ?? 'open',\n }),\n }),\n route({\n method: 'PATCH',\n path: '/issues/milestones/:id',\n body: updateMilestoneBody,\n write: true,\n handle: async ({ params, body }) => {\n await requireMilestone(params.id)\n return await ops.updateMilestone(params.id, body)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/milestones/:id',\n write: true,\n handle: async ({ params }) => {\n await requireMilestone(params.id)\n await ops.deleteMilestone(params.id)\n return OK\n },\n }),\n route({\n method: 'GET',\n path: '/issues/sprints',\n query: requiredWorkspaceIdQuery,\n handle: async ({ query }) => await ops.listSprints(requireWorkspace(query.workspaceId)),\n }),\n route({\n method: 'POST',\n path: '/issues/milestones/:id/close',\n body: closeSprintBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireMilestone(params.id)\n return await ops.closeSprint(params.id, body, actors.person)\n },\n }),\n\n // The board as a whole\n route({\n method: 'GET',\n path: '/issues/columns',\n query: workspaceIdsQuery,\n handle: async ({ query }) => {\n // Naming no workspace is a board of no columns.\n if (query.workspaceIds.length === 0) {\n return []\n }\n for (const named of query.workspaceIds) {\n requireWorkspace(named)\n }\n return (await listStatusViews()).map(status => ({\n name: status.name,\n normalizedName: normalizeStatusName(status.name),\n category: status.category,\n color: status.color,\n statuses: [{ workspaceId, statusId: status.id, entryRules: status.entryRules }],\n }))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/search',\n query: searchQuery,\n handle: async ({ query }) => {\n const limit = Number(query.limit) || DEFAULT_SEARCH_LIMIT\n const found = (await ops.searchCards(query.q, Number.POSITIVE_INFINITY))\n .filter(card => card.workspaceId === workspaceId)\n .slice(0, limit)\n return await toCardViews(ops, found)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/version',\n query: workspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n return { seq: await readChangeSeq(store) }\n },\n }),\n route({\n method: 'GET',\n path: '/issues/ready',\n query: readyQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n await seedColumns()\n return await toCardViews(ops, await ops.listReady({ workspaceId, limit: query.limit }))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/prime',\n query: requiredWorkspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n await seedColumns()\n return { text: await ops.buildPrimeText(workspaceId) }\n },\n }),\n\n // Cards\n route({\n method: 'GET',\n path: '/issues',\n query: listIssuesQuery,\n handle: async ({ query }) => {\n if (query.workspaceId !== undefined && query.workspaceIds !== undefined) {\n throw new ServeError(400, 'issue_workspace_filter_conflict', 'Use either workspaceId or workspaceIds, not both', {\n workspaceId: query.workspaceId,\n workspaceIds: query.workspaceIds,\n })\n }\n requireWorkspace(query.workspaceId)\n for (const named of query.workspaceIds ?? []) {\n requireWorkspace(named)\n }\n const cards = (await store.issues.listInBoardOrder(workspaceId))\n .filter(card => query.milestoneId === undefined || card.milestoneId === query.milestoneId)\n .filter(card => query.parentIssueId === undefined || card.parentIssueId === query.parentIssueId)\n .filter(card => query.priority === undefined || card.priority === query.priority)\n .filter(card => query.statusId === undefined || card.statusId === query.statusId)\n const views = await toCardViews(ops, cards)\n const labels = query.labels ?? []\n return views.filter(view => labels.every(label => view.labels.includes(label)))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/reorder',\n body: reorderIssuesBody,\n write: true,\n handle: async ({ body, actors }) => {\n requireWorkspace(body.workspaceId)\n await ops.reorderCards(workspaceId, body.orderedIds)\n // Field changes go through the card update, so history and entry\n // rules are exactly an inline edit's.\n const updated: ServeCardView[] = []\n if (body.patch) {\n for (const issueId of new Set(body.patch.issueIds)) {\n await requireCard(issueId)\n updated.push(await toWrittenCardView(ops, await ops.updateCard(issueId, body.patch.fields, actors.placement)))\n }\n }\n return updated\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id',\n handle: async ({ params }) => await toCardView(ops, await requireCard(params.id)),\n }),\n route({\n method: 'POST',\n path: '/issues',\n body: createIssueBody,\n write: true,\n handle: async ({ body, actors }) => {\n const { workspaceId: named, ...fields } = body\n requireWorkspace(named)\n return await toWrittenCardView(ops, await ops.createCard({ ...fields, workspace }, actors.placement))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/bulk',\n body: bulkUpdateBody,\n write: true,\n handle: async ({ body, actors }) => {\n // A card of another workspace is skipped, the way the board skips an\n // id it does not hold at all.\n const onThisBoard: string[] = []\n for (const issueId of new Set(body.issueIds)) {\n if (await isOnThisBoard(issueId)) {\n onThisBoard.push(issueId)\n }\n }\n return await ops.bulkUpdateCards(onThisBoard, body.update, actors.placement)\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/runs/:runId',\n body: finishRunBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireRun(params.runId)\n // What the run turned out to be running in and how it ended are one\n // write.\n const run = await runBoardWrite(store, async (scope) => {\n if (body.branch !== undefined || body.worktreePath !== undefined || body.externalSessionRef !== undefined) {\n await ops.attachRunExecution(params.runId, body, scope)\n }\n return await ops.finishRun(params.runId, body, actors.writer, scope)\n })\n return await toRunView(run)\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id/status/:statusName',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n return await toWrittenCardView(ops, await ops.moveCard(params.id, params.statusName, actors.placement))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id/status-line',\n body: setStatusLineBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.setStatusLine(params.id, body.statusLine, actors.writer))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/wait-approval',\n body: waitApprovalBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.waitApproval(params.id, body, actors.writer))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/approve',\n body: approveBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.approve(params.id, body, actors.person))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/return',\n body: returnBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, (await ops.returnCard(params.id, body, actors.person)).card)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/runs',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listRuns(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/runs',\n body: startRunBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toRunView(await ops.startRun(params.id, body, actors.writer))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id',\n body: updateIssueBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n if (body.workspaceId !== undefined) {\n requireWorkspace(body.workspaceId)\n }\n return await toWrittenCardView(ops, await ops.updateCard(params.id, body, actors.placement))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/activity',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listActivity(params.id, activityResolver)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/field-changes',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listFieldChanges(params.id)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n // No board tool or command deletes a card: a card that could be\n // deleted and written again is a way around waiting for a person, and\n // the agent role holds no `delete` on `issues` for the same reason. So\n // this route is a person's, like approval — the one refusal the board\n // does not make itself, because nothing else in the package deletes.\n if (actors.person.kind !== 'user') {\n throw new ServeError(403, 'issue_delete_requires_user', 'Only a person can delete a card', { issueId: params.id })\n }\n await ops.deleteCard(params.id)\n return OK\n },\n }),\n\n // Comments\n route({\n method: 'GET',\n path: '/issues/:id/comments',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return (await ops.listComments(params.id)).map(toCommentView)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/comments',\n body: addCommentBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return toCommentView(await ops.addComment({ issueId: params.id, content: body.content }, actors.writer))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/comments/:id',\n write: true,\n handle: async ({ params, actors }) => {\n await requireComment(params.id)\n await ops.deleteComment(params.id, actors.person)\n return OK\n },\n }),\n\n // What a card points at\n route({\n method: 'GET',\n path: '/issues/:id/relations',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listRelations(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/relations',\n body: createRelationBody,\n write: true,\n handle: async ({ body }) => {\n await requireCard(body.sourceIssueId)\n await requireCard(body.targetIssueId)\n return await ops.createRelation(body)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/relations/:id',\n write: true,\n handle: async ({ params }) => {\n await requireRelation(params.id)\n await ops.deleteRelation(params.id)\n return OK\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/pull-requests',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listPullRequestStandings(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/pull-requests',\n body: addPullRequestBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await ops.linkPullRequest(params.id, body.url, actors.writer)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id/pull-requests/:linkId',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n await ops.unlinkPullRequest(params.id, params.linkId, actors.person)\n return OK\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/context-refs',\n body: addContextRefBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.addContextRef(params.id, body.ref, actors.writer))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id/context-refs/:index',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.removeContextRef(params.id, Number(params.index), actors.writer))\n },\n }),\n ]\n}\n","import { createHash, timingSafeEqual } from 'node:crypto'\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport { createServer } from 'node:http'\nimport type { AddressInfo } from 'node:net'\n\nimport type { BoardSession } from '../cli/command'\nimport { describeFailure } from '../cli/failure'\nimport { CliError } from '../cli/output'\nimport type { ServeActors } from './actors'\nimport { ACTOR_HEADER, resolveRequestActors } from './actors'\nimport { isBoardPagePath, readBoardPageAssets, resolveBoardPageDirectory, sendBoardPage } from './board-page'\nimport type { ServeErrorBody } from './errors'\nimport { readErrorResponse, ServeError } from './errors'\nimport type { Route } from './router'\nimport { matchRoute } from './router'\nimport { createIssueRoutes } from './routes'\n\n/**\n * `kanbo serve` — the board over HTTP, with nothing but `node:http` under it.\n *\n * What it answers is `./routes.ts`; what this module owns is the door: who may\n * come in, from which origin, with how much, and in what order. Requests are\n * handled one at a time. A SQLite board is one connection whose transactions\n * may await, and two requests interleaving on it would land in each other's\n * transaction — the reason a long-running host of the board lines its writes up\n * one at a time. One at a time is that line, and on a board this size it costs\n * nothing a person would notice.\n */\n\n/** The most a request body may carry. A card is a few kilobytes; a megabyte is a mistake or an attack. */\nexport const SERVE_BODY_LIMIT_BYTES = 1024 * 1024\n\n/** What `kanbo serve` listens on when nothing says otherwise. */\nexport const DEFAULT_SERVE_HOST = '127.0.0.1'\nexport const DEFAULT_SERVE_PORT = 4318\n\n/** What a caller is told when a board is offered beyond this machine with nothing guarding it. */\nexport const SERVE_TOKEN_REQUIRED_MESSAGE\n = 'Serving the board beyond this machine needs a token. Pass --token or set KANBO_SERVE_TOKEN, '\n + 'or bind to 127.0.0.1.'\n\nconst ALLOWED_METHODS = 'GET, POST, PATCH, DELETE, OPTIONS'\nconst ALLOWED_HEADERS = `authorization, content-type, ${ACTOR_HEADER}`\n/** How long a browser may keep a preflight answer, in seconds. */\nconst PREFLIGHT_MAX_AGE_SECONDS = 600\n\nexport interface KanboServeOptions {\n /** The open board and workspace every route is about. The caller closes it. */\n session: BoardSession\n actors: ServeActors\n host: string\n /** `0` asks the system for a free port. */\n port: number\n /** When set, every request must carry `Authorization: Bearer <token>`. Required off loopback. */\n token?: string | null\n /** The exact origins a browser may call from; none by default. */\n corsOrigins?: readonly string[]\n /** Where the built board page is; by default `dist/page/`, found from the running file. */\n pageDirectory?: string\n /** Where a request that failed for a reason nobody named is written down. Never a token or a connection string. */\n log?: (line: string) => void\n}\n\nexport interface RunningKanboServer {\n /** The address to call, e.g. `http://127.0.0.1:4318`. */\n url: string\n port: number\n /** Stop listening and drop every open connection. The board stays open; its owner closes it. */\n close: () => Promise<void>\n}\n\n/**\n * Whether an address only this machine can reach. Anything else — `0.0.0.0`\n * included, which is every interface — is beyond it.\n */\nfunction isLoopbackHost(host: string): boolean {\n const bare = stripBrackets(host).toLowerCase()\n return bare === 'localhost' || bare === '::1' || /^127(?:\\.\\d{1,3}){3}$/.test(bare)\n}\n\n/** Refuse to offer the board beyond this machine unguarded. Asked before the board is even opened. */\nexport function assertServeBinding(host: string, token: string | null | undefined): void {\n if (!isLoopbackHost(host) && !token) {\n throw new CliError(1, SERVE_TOKEN_REQUIRED_MESSAGE)\n }\n}\n\nexport async function startKanboServer(options: KanboServeOptions): Promise<RunningKanboServer> {\n assertServeBinding(options.host, options.token)\n const routes = createIssueRoutes(options.session)\n // `[::1]` is how an address is written in a URL; `listen` takes it bare.\n const listenHost = stripBrackets(options.host)\n let boundPort = 0\n const handle = createRequestHandler(routes, options, () => boundPort)\n const server = createServer((request, response) => void handle(request, response))\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(options.port, listenHost, () => {\n server.off('error', reject)\n resolve()\n })\n })\n\n boundPort = (server.address() as AddressInfo).port\n return {\n url: `http://${urlHost(listenHost)}:${boundPort}`,\n port: boundPort,\n close: async () => await closeServer(server),\n }\n}\n\n/**\n * Who may act for a person on this server, said once when it starts: only a\n * request presenting the token does, and a server started from an agent's\n * shell has nobody who may.\n */\nexport function describePersonRights(actors: ServeActors): string {\n if (!actors.person) {\n return 'no request acts for a person (started from an agent\\'s shell)'\n }\n return `a request with the token acts for ${actors.person.id}`\n}\n\nfunction stripBrackets(host: string): string {\n return host.trim().replace(/^\\[(.*)\\]$/, '$1')\n}\n\nfunction urlHost(host: string): string {\n return host.includes(':') ? `[${host}]` : host\n}\n\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close(error => (error ? reject(error) : resolve()))\n // A client holding a keep-alive connection would otherwise hold the\n // shutdown open until it let go.\n server.closeAllConnections()\n })\n}\n\n/** Methods that change nothing, and so need no JSON content type to prove a script sent them. */\nconst SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])\n\nfunction createRequestHandler(routes: readonly Route[], options: KanboServeOptions, boundPort: () => number) {\n const token = options.token || null\n const corsOrigins = new Set(options.corsOrigins ?? [])\n const log = options.log ?? ((line: string) => console.error(line))\n const loopback = isLoopbackHost(options.host)\n const page = readBoardPageAssets(options.pageDirectory ?? resolveBoardPageDirectory())\n /** The tail of the line requests wait in. It never rejects, so one failure cannot stop the rest. */\n let queue: Promise<unknown> = Promise.resolve()\n\n /**\n * The `Host` a loopback server answers to: its own loopback names with the\n * port it is on. A page on another site that rebinds its own name to\n * 127.0.0.1 still sends that name, and is turned away.\n */\n function allowedHosts(): Set<string> {\n const port = boundPort()\n return new Set([\n `127.0.0.1:${port}`,\n `localhost:${port}`,\n `[::1]:${port}`,\n `${urlHost(stripBrackets(options.host)).toLowerCase()}:${port}`,\n ])\n }\n\n /**\n * The origins of this server's own page: `http://` and each name it answers\n * to. A browser names it on every write the page sends, and it is let in the\n * way a `--cors-origin` is — but with no CORS header needed, being the same\n * origin. Bound to `0.0.0.0` the server has no one name to be called by, so\n * a page opened by some address of it is named with `--cors-origin`.\n */\n function isOwnOrigin(origin: string): boolean {\n return [...allowedHosts()].some(host => origin.toLowerCase() === `http://${host}`)\n }\n\n return async (request: IncomingMessage, response: ServerResponse): Promise<void> => {\n const origin = request.headers.origin\n try {\n if (loopback && !allowedHosts().has((request.headers.host ?? '').toLowerCase())) {\n throw new ServeError(403, 'host_not_allowed', 'This server answers only to its own loopback address')\n }\n // A browser names the page a request comes from. A page that is not on\n // the list is refused whatever it asks, before anything is routed — a\n // \"simple\" request needs no preflight, and would otherwise be carried out\n // with only its answer hidden from the page.\n if (origin !== undefined && !corsOrigins.has(origin) && !isOwnOrigin(origin)) {\n throw new ServeError(403, 'origin_not_allowed', 'Requests from this origin are not allowed')\n }\n if (origin !== undefined && corsOrigins.has(origin)) {\n response.setHeader('access-control-allow-origin', origin)\n response.setHeader('vary', 'Origin')\n }\n\n // A preflight carries no credentials — the browser asks before it sends\n // them — so it is answered before the token is asked for, and says\n // nothing about the board.\n if (request.method === 'OPTIONS') {\n if (origin !== undefined && corsOrigins.has(origin)) {\n response.setHeader('access-control-allow-methods', ALLOWED_METHODS)\n response.setHeader('access-control-allow-headers', ALLOWED_HEADERS)\n response.setHeader('access-control-max-age', String(PREFLIGHT_MAX_AGE_SECONDS))\n }\n response.writeHead(204).end()\n return\n }\n\n // The page holds no data — it asks the routes below for everything, with\n // the token the person gives it — so it is served without the token.\n const url = new URL(request.url ?? '/', 'http://kanbo.invalid')\n if ((request.method === 'GET' || request.method === 'HEAD') && isBoardPagePath(url.pathname)) {\n sendBoardPage(response, url.pathname, page)\n return\n }\n\n if (token && !carriesToken(request, token)) {\n response.setHeader('www-authenticate', 'Bearer')\n throw new ServeError(401, 'unauthorized', 'A valid bearer token is required')\n }\n // A form or a `text/plain` fetch reaches a server without a preflight;\n // a JSON content type does not. Every write therefore has to say it.\n if (!SAFE_METHODS.has(request.method ?? 'GET') && !isJsonContentType(request.headers['content-type'])) {\n throw new ServeError(415, 'unsupported_media_type', 'Send the request as content-type: application/json')\n }\n const matched = matchRoute(routes, request.method ?? 'GET', url.pathname)\n if (!matched) {\n throw new ServeError(404, 'not_found', 'Not Found')\n }\n const body = await readJsonBody(request)\n // Acting for a person takes the token: without one configured, nobody\n // who can reach the port is known to be the person who started it.\n const actors = resolveRequestActors(options.actors, {\n actorHeader: request.headers[ACTOR_HEADER],\n authenticated: token !== null,\n })\n const result = queue.then(async () => {\n if (matched.route.write) {\n await options.session.assertWritable()\n }\n return await matched.route.run({ params: matched.params, query: readQuery(url.searchParams), body, actors })\n })\n queue = result.catch(() => undefined)\n sendJson(response, 200, await result)\n }\n catch (error) {\n const failure = readErrorResponse(error)\n if (failure) {\n if (failure.status === 413) {\n // The rest of the body is not read: the connection goes with the answer.\n response.setHeader('connection', 'close')\n response.once('finish', () => request.destroy())\n }\n else if (!request.complete) {\n // A body nobody read is not worth keeping the connection for.\n response.setHeader('connection', 'close')\n }\n sendJson(response, failure.status, failure.body)\n return\n }\n log(`kanbo serve: ${request.method} ${request.url?.split('?')[0]} failed: ${describeFailure(error).message}`)\n sendJson(response, 500, { code: 'internal_server_error', message: 'Internal Server Error' } satisfies ServeErrorBody)\n }\n }\n}\n\nfunction isJsonContentType(value: string | undefined): boolean {\n return value?.split(';')[0].trim().toLowerCase() === 'application/json'\n}\n\n/**\n * Whether the request carries the token, compared in constant time. Both sides\n * are hashed first, so the comparison is between two values of one length and\n * says nothing about how long the token is.\n */\nfunction carriesToken(request: IncomingMessage, token: string): boolean {\n const match = /^Bearer\\s+(.+)$/i.exec(request.headers.authorization ?? '')\n if (!match) {\n return false\n }\n const digest = (value: string): Buffer => createHash('sha256').update(value).digest()\n return timingSafeEqual(digest(match[1].trim()), digest(token))\n}\n\nfunction payloadTooLarge(): ServeError {\n return new ServeError(413, 'payload_too_large', `Request body is larger than ${SERVE_BODY_LIMIT_BYTES} bytes`, {\n limit: SERVE_BODY_LIMIT_BYTES,\n })\n}\n\n/**\n * The request body as JSON, `undefined` when there is none.\n *\n * A body that says it is over the limit is refused before a byte of it is\n * read, and one that turns out to be is refused the moment it passes the\n * limit; either way the rest is never read (the caller closes the connection).\n */\nasync function readJsonBody(request: IncomingMessage): Promise<unknown> {\n if (Number(request.headers['content-length'] ?? 0) > SERVE_BODY_LIMIT_BYTES) {\n throw payloadTooLarge()\n }\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of request as AsyncIterable<Buffer>) {\n size += chunk.length\n if (size > SERVE_BODY_LIMIT_BYTES) {\n throw payloadTooLarge()\n }\n chunks.push(chunk)\n }\n\n const text = Buffer.concat(chunks).toString('utf8')\n if (text.trim() === '') {\n return undefined\n }\n try {\n return JSON.parse(text)\n }\n catch {\n throw new ServeError(400, 'validation_error', 'request validation failed', {\n source: 'body',\n issues: [{ path: 'root', message: 'Body is not valid JSON' }],\n })\n }\n}\n\n/** The query string as the schemas read it: a repeated parameter is a list, a single one a string. */\nfunction readQuery(params: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {}\n for (const key of new Set(params.keys())) {\n const values = params.getAll(key)\n query[key] = values.length === 1 ? values[0] : values\n }\n return query\n}\n\nfunction sendJson(response: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value)\n response.writeHead(status, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n })\n response.end(body)\n}\n","import { spawn } from 'node:child_process'\nimport { randomBytes } from 'node:crypto'\n\nimport { assertServeBinding } from './server'\n\n/**\n * The token `kanbo serve` guards the board with, and how the person who\n * started it is handed the page.\n *\n * A token nobody gave is made up for the run, so a loopback server is never\n * open to anything that can reach the port. The person gets it as a link whose\n * fragment carries it — a fragment never reaches a server or its log — and the\n * page keeps it for the tab. Only a person's terminal is handed that link: a\n * shell an agent runs in, or output going anywhere but a terminal, is told a\n * token exists and never what it is, since whoever holds it acts for the person.\n */\n\n/** The token a server runs with, and whether it was made up for this run rather than given. */\nexport interface ServeAccess {\n token: string\n generated: boolean\n}\n\n/** The token given, or one made up for a loopback server; refused off loopback without one. */\nexport function resolveServeAccess(host: string, givenToken: string | null): ServeAccess {\n assertServeBinding(host, givenToken)\n if (givenToken) {\n return { token: givenToken, generated: false }\n }\n return { token: randomBytes(24).toString('base64url'), generated: true }\n}\n\nexport interface ServePagePresentation {\n /** The server's address, e.g. `http://127.0.0.1:4318`. */\n url: string\n access: ServeAccess\n /** Started from a shell that says it is an agent's (`KANBO_ACTOR_KIND=agent`). */\n agentShell: boolean\n /** Whether what is printed goes to a terminal rather than a pipe or a file. */\n terminal: boolean\n /** The person asked for the page to open (no `--no-open`). */\n open: boolean\n}\n\nexport interface ServePageOutput {\n print: (line: string) => void\n openInBrowser: (url: string) => void\n}\n\n/**\n * Hand the person the board page: a link printed once, and opened in the\n * browser unless they said not to. A generated token rides in the link's\n * fragment; one they gave is theirs already and is left out.\n */\nexport function presentServePage(presentation: ServePagePresentation, output: ServePageOutput): void {\n const { url, access, open } = presentation\n if (presentation.agentShell || !presentation.terminal) {\n if (access.generated) {\n output.print('kanbo serve: a token was generated for this run and is not shown here; set KANBO_SERVE_TOKEN to choose your own')\n }\n return\n }\n const link = access.generated ? `${url}/#token=${access.token}` : `${url}/`\n output.print(`kanbo serve: board page ${link}`)\n if (open) {\n output.openInBrowser(link)\n }\n}\n\n/** Open a link in the default browser, without waiting for it and without failing when there is none. */\nexport function openInBrowser(url: string): void {\n const [command, args] = process.platform === 'darwin'\n ? ['open', [url]]\n : process.platform === 'win32'\n ? ['cmd', ['/c', 'start', '\"\"', url]]\n : ['xdg-open', [url]]\n try {\n const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsVerbatimArguments: process.platform === 'win32' })\n child.on('error', () => {})\n child.unref()\n }\n catch {\n // No browser to open: the link printed above is enough.\n }\n}\n","import type { Command } from 'commander'\n\nimport { openInBrowser, presentServePage, resolveServeAccess } from '../../serve/access'\nimport type { ServeActors } from '../../serve/actors'\nimport {\n DEFAULT_SERVE_HOST,\n DEFAULT_SERVE_PORT,\n describePersonRights,\n startKanboServer,\n} from '../../serve/server'\nimport { createCliActor, createPersonActor, isAgentShell } from '../actor'\nimport { openBoardSession } from '../command'\nimport { describeFailure } from '../failure'\nimport { CliError } from '../output'\n\n/**\n * `kanbo serve` — the board over HTTP: the board page for a browser, and the\n * board's `/issues` routes for any other client.\n *\n * The routes serve the board and workspace this command resolves the way every\n * other command does, so a client written against them shows and edits this\n * board unchanged, whichever server answers them. Everything\n * about the door — the token, the origins, the body limit, who a request writes\n * as — is `../../serve/`.\n */\n\n/** What `kanbo serve` is told. */\ninterface ServeCommandOptions {\n port: string\n host: string\n db?: string\n databaseUrl?: string\n workspace?: string\n token?: string\n corsOrigin: string[]\n /** `false` under `--no-open`. */\n open: boolean\n}\n\nexport function registerServeCommand(program: Command): void {\n program\n .command('serve')\n .description('serve this board over HTTP, with a board page for the browser')\n .option('--port <port>', 'port to listen on', String(DEFAULT_SERVE_PORT))\n .option('--host <host>', 'address to bind; anything but loopback needs a token', DEFAULT_SERVE_HOST)\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to serve instead of a board file')\n .option('--workspace <nameOrId>', 'workspace the server is about')\n .option('--token <token>', 'bearer token every request must carry (or set KANBO_SERVE_TOKEN); made up for the run on loopback when absent')\n .option('--cors-origin <origin>', 'let a browser call from this exact origin; repeat for more', collect, [])\n .option('--no-open', 'print the board page link without opening it in the browser')\n .action(async (options: ServeCommandOptions) => {\n const port = parsePort(options.port)\n // Refused before the board is opened: a server that would not be allowed\n // to start has no business connecting to anything first.\n const access = resolveServeAccess(options.host, options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null)\n\n const session = await openBoardSession(\n { db: options.db, databaseUrl: options.databaseUrl, workspace: options.workspace },\n 'read',\n )\n const agentShell = isAgentShell()\n const actors: ServeActors = { writer: createCliActor(), person: agentShell ? null : createPersonActor() }\n\n let server: Awaited<ReturnType<typeof startKanboServer>>\n try {\n server = await startKanboServer({\n session,\n actors,\n host: options.host,\n port,\n token: access.token,\n corsOrigins: options.corsOrigin,\n })\n }\n catch (error) {\n await session.close()\n throw error\n }\n\n console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}, bearer token required${access.generated ? ' (generated for this run)' : ''}`)\n console.error(`kanbo serve: ${describePersonRights(actors)}`)\n presentServePage(\n { url: server.url, access, agentShell, terminal: process.stdout.isTTY === true, open: options.open },\n { print: line => console.log(line), openInBrowser },\n )\n\n let stopping = false\n const stop = async (): Promise<void> => {\n if (stopping) {\n return\n }\n stopping = true\n try {\n await server.close()\n await session.close()\n process.exit(0)\n }\n catch (error) {\n console.error(describeFailure(error).message)\n process.exit(1)\n }\n }\n process.once('SIGINT', () => void stop())\n process.once('SIGTERM', () => void stop())\n })\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value]\n}\n\n/** A port a caller typed, refused rather than guessed at when it is not one. */\nfunction parsePort(value: string): number {\n const port = Number(value)\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new CliError(1, `Expected a port between 0 and 65535, got \"${value}\".`)\n }\n return port\n}\n","import type { Command } from 'commander'\n\nimport { BoardError } from '../../domain/errors'\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { describeSprints, projectSprint, SPRINT_VIEW_FIELDS } from '../view'\n\ninterface CreateOptions extends BoardCommandOptions {\n title: string\n start: number\n due: number\n description?: string\n}\n\ninterface CloseOptions extends BoardCommandOptions {\n carryTo?: string\n}\n\nconst DAY_SECONDS = 86_400\n\n/**\n * A date as the command line takes it: `YYYY-MM-DD`, read as a UTC day, or a\n * unix-seconds number taken as it is. `end` turns a day into its last second,\n * so a sprint due on a day is still current throughout that day.\n */\nfunction parseDate(value: string, end: boolean): number {\n if (/^\\d+$/.test(value)) {\n return Number(value)\n }\n const day = /^\\d{4}-\\d{2}-\\d{2}$/.test(value) ? Date.parse(`${value}T00:00:00Z`) : Number.NaN\n if (Number.isNaN(day)) {\n throw new CliError(1, `Expected a date as YYYY-MM-DD or unix seconds, got \"${value}\".`)\n }\n return day / 1000 + (end ? DAY_SECONDS - 1 : 0)\n}\n\n/**\n * `kanbo sprint …` — milestones read as sprints (ruling 5x-3).\n *\n * Listing and creating are anybody's. Closing one is a person's decision\n * (ruling 5x-4) — what did not get done is a review of the work — so it is\n * refused in a shell started for an agent exactly like `approve`, before\n * the board is opened.\n */\nexport function registerSprintCommands(program: Command): void {\n const sprint = program\n .command('sprint')\n .description('milestones read as sprints: a start date, a due date, and the cards under them')\n\n withBoardOptions(sprint\n .command('list')\n .description('every milestone of this board by start date, the current sprint marked *'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const views = (await session.ops.listSprints(session.workspace.id)).map(projectSprint)\n return { value: views, text: describeSprints(views), fields: SPRINT_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(sprint\n .command('create')\n .description('create a sprint: a milestone with a start and a due date')\n .requiredOption('--title <title>', 'what the sprint is called')\n .requiredOption('--start <date>', 'first day, as YYYY-MM-DD (UTC) or unix seconds', value => parseDate(value, false))\n .requiredOption('--due <date>', 'last day, as YYYY-MM-DD (UTC, through its end) or unix seconds', value => parseDate(value, true))\n .option('--description <text>', 'what the sprint is for'))\n .action(async (options: CreateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const created = await session.ops.createMilestone({\n workspaceId: session.workspace.id,\n title: options.title,\n description: options.description ?? null,\n startDate: options.start,\n dueDate: options.due,\n })\n const current = await session.ops.currentSprint(session.workspace.id)\n const view = projectSprint({ ...created, cards: { open: 0, done: 0 }, current: current?.id === created.id })\n return { value: view, text: `Created ${view.id}\\n\\n${describeSprints([view])}`, fields: SPRINT_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(sprint\n .command('close')\n .description('close a sprint, carrying its unfinished cards to another open milestone (a person\\'s decision)')\n .argument('<id>', 'the milestone to close')\n .option('--carry-to <id>', 'the open milestone unfinished cards move to; without it they leave the milestone'))\n .action(async (id: string, options: CloseOptions) => {\n const actor = requireHumanActor('sprint close')\n\n await runBoardCommand(options, 'write', async (session) => {\n // A board can hold several workspaces: a sprint of another one is not\n // this workspace's to close, even by its id.\n if (!(await session.ops.listSprints(session.workspace.id)).some(sprint => sprint.id === id)) {\n throw new BoardError('issue_milestone_not_found', { milestoneId: id, workspaceId: session.workspace.id })\n }\n const result = await session.ops.closeSprint(id, { carryTo: options.carryTo ?? null }, actor)\n const destination = options.carryTo ? `to ${options.carryTo}` : 'out of the milestone'\n return {\n value: result,\n text: `Closed ${id}; ${result.carried} unfinished card${result.carried === 1 ? '' : 's'} carried ${destination}`,\n fields: ['carried', 'closed'],\n }\n })\n })\n}\n","import { existsSync, rmSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\nimport type { Command } from 'commander'\n\nimport { BINDING_FILE_PATH } from '../binding'\nimport { findKanboProject } from '../doctor'\nimport { CliError } from '../output'\nimport { confirmPlan, isInteractive } from '../setup/confirm'\nimport type { FileChange } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { planInstructionBlockRemoval } from '../setup/instructions'\nimport {\n CLAUDE_USER_REMOVE,\n planCodexMcpServerRemoval,\n planJsonMcpServerRemoval,\n readClaudeUserMcpCommand,\n runClaudeMcp,\n} from '../setup/mcp-config'\nimport type { McpClient } from '../setup/paths'\nimport {\n GLOBAL_INSTRUCTION_CLIENTS,\n globalInstructionPath,\n globalMcpConfigPath,\n PROJECT_INSTRUCTION_FILES,\n projectMcpConfigPath,\n} from '../setup/paths'\n\n/**\n * `kanbo uninstall` — take out what `kanbo init` wrote, and nothing else.\n *\n * The marked instruction block and the `kanbo` MCP entries go; the person's\n * own writing around the block and every other MCP server stay, and so does\n * every file, emptied or not — kanbo does not record which files it created,\n * so it does not delete any. Claude Code's user-scope registration goes out\n * the way it went in, through `claude mcp remove`.\n *\n * Boards are never touched unless `--purge` is given: then the project's\n * binding goes, and its board file only after a confirmation that names it —\n * `--yes` does not answer that one, and a shell nobody can ask keeps the file.\n */\ninterface UninstallOptions {\n project?: boolean\n global?: boolean\n purge?: boolean\n yes?: boolean\n json?: boolean\n}\n\n/** One thing the command did, or left for the person. */\ninterface UninstallAction {\n path: string\n /** `edited`: kanbo's part taken out. `deleted`: the file removed. `ran`/`manual`: a `claude` command. `kept`: left on purpose. */\n action: 'edited' | 'deleted' | 'ran' | 'manual' | 'kept'\n note?: string\n}\n\n/** A planned edit, with what it takes out. */\ninterface PlannedEdit {\n change: FileChange\n what: string\n}\n\nexport function registerUninstallCommand(program: Command): void {\n program\n .command('uninstall')\n .description('remove what kanbo init wrote: instruction blocks and kanbo MCP entries; boards stay unless --purge')\n .option('--project', 'only this project\\'s files')\n .option('--global', 'only your own user-level files')\n .option('--purge', 'also remove this project\\'s .kanbo/binding.json, and its board file after a confirmation naming it')\n .option('--yes', 'do it without asking (never deletes a board file)')\n .option('--json', 'print what was done as JSON')\n .action(async (options: UninstallOptions) => {\n await uninstall(options)\n })\n}\n\nasync function uninstall(options: UninstallOptions): Promise<void> {\n const both = !options.project && !options.global\n const withProject = both || Boolean(options.project)\n const withGlobal = both || Boolean(options.global)\n if (options.purge && !withProject) {\n throw new CliError(1, '--purge removes a project\\'s binding and board; run it without --global, in the project.')\n }\n\n const projectRoot = withProject ? (findKanboProject(process.cwd())?.root ?? process.cwd()) : null\n const edits = [\n ...(projectRoot ? planProjectEdits(projectRoot) : []),\n ...(withGlobal ? planGlobalEdits() : []),\n ].filter(edit => edit.change.next !== null)\n const claudeUser = withGlobal && readClaudeUserMcpCommand() !== undefined\n const bindingPath = projectRoot && options.purge ? join(projectRoot, BINDING_FILE_PATH) : null\n const purgeBinding = bindingPath !== null && existsSync(bindingPath)\n const boardFile = projectRoot && options.purge ? findOwnBoardFile(projectRoot) : null\n\n const actions: UninstallAction[] = []\n if (edits.length > 0 || claudeUser || purgeBinding || boardFile) {\n const confirmed = await confirmPlan({\n preview: describePlan(edits, claudeUser, purgeBinding ? bindingPath : null, boardFile),\n question: 'Remove these?',\n yes: options.yes,\n machineOutput: Boolean(options.json),\n command: 'kanbo uninstall',\n })\n if (!confirmed) {\n throw new CliError(1, 'Nothing was changed.')\n }\n\n for (const edit of edits) {\n applyFileChange(edit.change)\n actions.push({ path: edit.change.path, action: 'edited', note: edit.what })\n }\n if (claudeUser) {\n const outcome = runClaudeMcp(CLAUDE_USER_REMOVE)\n actions.push({ path: globalMcpConfigPath('claude'), action: outcome.state, note: outcome.state === 'ran' ? outcome.command : `run this yourself: ${outcome.command} (${outcome.reason})` })\n }\n if (purgeBinding) {\n rmSync(bindingPath)\n actions.push({ path: bindingPath, action: 'deleted', note: 'project binding' })\n }\n if (boardFile) {\n actions.push(await purgeBoardFile(boardFile))\n }\n }\n\n printActions(actions, options)\n}\n\nfunction planProjectEdits(root: string): PlannedEdit[] {\n return [\n ...Object.values(PROJECT_INSTRUCTION_FILES).map(name => ({\n change: planInstructionBlockRemoval(join(root, name)),\n what: 'kanbo instruction block',\n })),\n ...(['claude', 'codex', 'cursor'] as const).map(client => ({\n change: planMcpRemoval(projectMcpConfigPath(root, client), client),\n what: 'kanbo MCP server',\n })),\n ]\n}\n\nfunction planGlobalEdits(): PlannedEdit[] {\n return [\n ...GLOBAL_INSTRUCTION_CLIENTS.map(client => ({\n change: planInstructionBlockRemoval(globalInstructionPath(client)),\n what: 'kanbo instruction block',\n })),\n // Claude Code's `~/.claude.json` is its own state file: `claude mcp remove` edits it, not this.\n ...(['codex', 'cursor'] as const).map(client => ({\n change: planMcpRemoval(globalMcpConfigPath(client), client),\n what: 'kanbo MCP server',\n })),\n ]\n}\n\nfunction planMcpRemoval(path: string, client: McpClient): FileChange {\n return client === 'codex' ? planCodexMcpServerRemoval(path) : planJsonMcpServerRemoval(path)\n}\n\n/**\n * The board file `kanbo init --file` made for this project — the only board\n * `--purge` may offer to delete. A host app's database or an external board is\n * not kanbo's to delete, and a binding that names none offers nothing.\n */\nfunction findOwnBoardFile(root: string): string | null {\n const binding = findKanboProject(root)?.binding\n if (!binding?.dbPath) {\n return null\n }\n const path = resolve(root, binding.dbPath)\n return existsSync(path) ? path : null\n}\n\n/** Delete the board file only on a yes to a question that names it; `--yes` is not that answer. */\nasync function purgeBoardFile(path: string): Promise<UninstallAction> {\n if (!isInteractive()) {\n return { path, action: 'kept', note: 'board file: deleting it needs a confirmation in a terminal; delete it yourself if you mean to' }\n }\n const prompts = await import('@clack/prompts')\n const answer = await prompts.confirm({\n message: `Delete the board file ${path} and every card in it? This cannot be undone.`,\n initialValue: false,\n })\n if (answer !== true) {\n return { path, action: 'kept', note: 'board file' }\n }\n for (const file of [path, `${path}-wal`, `${path}-shm`]) {\n rmSync(file, { force: true })\n }\n return { path, action: 'deleted', note: 'board file' }\n}\n\nfunction describePlan(edits: PlannedEdit[], claudeUser: boolean, bindingPath: string | null, boardFile: string | null): string {\n const lines = ['kanbo uninstall will:']\n for (const edit of edits) {\n lines.push(` edit ${edit.change.path} (remove the ${edit.what})`)\n }\n if (claudeUser) {\n lines.push(` run ${CLAUDE_USER_REMOVE.join(' ')}`)\n }\n if (bindingPath) {\n lines.push(` delete ${bindingPath}`)\n }\n if (boardFile) {\n lines.push(` ask whether to delete the board file ${boardFile} (only in a terminal; --yes does not answer this)`)\n }\n lines.push('Files stay even when nothing is left in them. Boards stay unless you confirm deleting one.')\n return lines.join('\\n')\n}\n\nfunction printActions(actions: UninstallAction[], options: UninstallOptions): void {\n if (options.json) {\n console.log(JSON.stringify({ actions }, null, 2))\n return\n }\n if (actions.length === 0) {\n console.log('Nothing to remove: no kanbo block or MCP entry in the files kanbo writes.')\n return\n }\n for (const action of actions) {\n console.log(`${action.action.padEnd(8)} ${action.path}${action.note ? ` (${action.note})` : ''}`)\n }\n}\n","import type { Command } from 'commander'\n\nimport { registerApproveCommand } from './approve'\nimport { registerCapabilitiesCommand } from './capabilities'\nimport { registerCardCommands } from './card'\nimport { registerColumnsCommands } from './columns'\nimport { registerDoctorCommand } from './doctor'\nimport { registerInitCommand } from './init'\nimport { registerMcpCommand } from './mcp'\nimport { registerMigrateCommand } from './migrate'\nimport { registerPrimeCommand } from './prime'\nimport { registerReadyCommand } from './ready'\nimport { registerReturnCommand } from './return'\nimport { registerRolesCommands } from './roles'\nimport { registerRunCommands } from './run'\nimport { registerServeCommand } from './serve'\nimport { registerSprintCommands } from './sprint'\nimport { registerUninstallCommand } from './uninstall'\n\n/**\n * Every command the `kanbo` binary answers to, put on one program.\n *\n * It is its own module rather than a block inside `cli/index.ts` because two\n * callers need it and only one of them wants the binary: `index.ts` runs\n * `main()` on import, so a test that imported it would parse `process.argv`.\n * The other caller is `mcp/docs.test.ts`, which walks the program to check that\n * every command has a row in the README — a check that is worth nothing if the\n * list it walks is a second copy of this one.\n */\nexport function registerKanboCommands(program: Command): void {\n registerInitCommand(program)\n registerDoctorCommand(program)\n registerUninstallCommand(program)\n registerCapabilitiesCommand(program)\n registerPrimeCommand(program)\n registerReadyCommand(program)\n registerColumnsCommands(program)\n registerCardCommands(program)\n registerApproveCommand(program)\n registerReturnCommand(program)\n registerRunCommands(program)\n registerSprintCommands(program)\n registerMigrateCommand(program)\n registerRolesCommands(program)\n registerMcpCommand(program)\n registerServeCommand(program)\n}\n","/**\n * The board's tools, named once.\n *\n * An agent reaches this board through an MCP server, through a command line, or\n * through an HTTP API (`kanbo serve`, or an app that serves the same routes),\n * and the three must agree on what the board can be asked to do. The names live\n * here rather than in the MCP server so that a prompt built for an agent and the\n * tools that agent actually holds cannot drift apart: both read this list.\n *\n * Nothing here executes anything. These are names and the commands that mean\n * the same thing, for a prompt and for documentation.\n */\nexport const KANBO_TOOL_NAMES = [\n 'kanbo_prime',\n 'kanbo_ready',\n 'kanbo_columns',\n 'kanbo_sprints',\n 'kanbo_card_get',\n 'kanbo_card_list',\n 'kanbo_card_create',\n 'kanbo_card_update',\n 'kanbo_card_move',\n 'kanbo_card_comment',\n 'kanbo_card_link_pr',\n 'kanbo_card_pull_requests',\n 'kanbo_status_line',\n 'kanbo_wait_approval',\n 'kanbo_run_start',\n 'kanbo_run_finish',\n] as const\n\n/** One of the board's tools. */\nexport type KanboToolName = typeof KANBO_TOOL_NAMES[number]\n\n/** The command line that does what a tool does. */\nexport interface KanboToolCliEquivalent {\n /** The `kanbo` command, which needs no server. */\n kanbo: string\n}\n\n/**\n * What to type instead of calling the tool.\n *\n * An agent without MCP tools is not shut out of the board: every tool has a\n * command that does the same thing, and the card ends up saying the same\n * either way, because both call the same operations.\n */\nexport const KANBO_TOOL_CLI_EQUIVALENTS: Record<KanboToolName, KanboToolCliEquivalent> = {\n kanbo_prime: {\n kanbo: 'kanbo prime',\n },\n kanbo_ready: {\n kanbo: 'kanbo ready',\n },\n kanbo_columns: {\n kanbo: 'kanbo columns list',\n },\n kanbo_sprints: {\n kanbo: 'kanbo sprint list',\n },\n kanbo_card_get: {\n kanbo: 'kanbo card get <id>',\n },\n kanbo_card_list: {\n kanbo: 'kanbo card list',\n },\n kanbo_card_create: {\n kanbo: 'kanbo card create --description <text> [--title <title>] [--parent <id>]',\n },\n kanbo_card_update: {\n kanbo: 'kanbo card update <id> [--title <title>] [--description <text>]',\n },\n kanbo_card_move: {\n kanbo: 'kanbo card move <id> <column>',\n },\n kanbo_card_comment: {\n kanbo: 'kanbo card comment <id> --content <text>',\n },\n kanbo_card_link_pr: {\n kanbo: 'kanbo card pr add <id> <url>',\n },\n kanbo_card_pull_requests: {\n kanbo: 'kanbo card pr list <id>',\n },\n kanbo_status_line: {\n kanbo: 'kanbo card status-line <id> --text <text>',\n },\n kanbo_wait_approval: {\n kanbo: 'kanbo card wait-approval <id> [--text <text>]',\n },\n kanbo_run_start: {\n kanbo: 'kanbo run start <id> --agent <name> [--session <ref>]',\n },\n kanbo_run_finish: {\n kanbo: 'kanbo run finish <runId> --state <state>',\n },\n}\n","import { Command } from 'commander'\nimport { z } from 'zod'\n\nimport { HUMAN_ONLY_ACTIONS } from '../cli/actor'\nimport { registerKanboCommands } from '../cli/commands'\nimport { CLI_EXIT_CODES } from '../cli/output'\nimport type { EntryRule } from '../domain/entry-rules'\nimport { ENTRY_RULE_DESCRIPTIONS, ENTRY_RULES } from '../domain/entry-rules'\nimport type { KanboToolCliEquivalent } from '../mcp/tool-names'\nimport { KANBO_TOOL_CLI_EQUIVALENTS } from '../mcp/tool-names'\nimport { KANBO_TOOLS } from '../mcp/tools'\nimport { BOARD_RULES } from '../ops/agent-rules'\nimport { APPROVED_COMMENT } from '../ops/approval'\nimport { RETURNED_STATUS_LINE_PREFIX } from '../ops/cards'\nimport {\n LAUNCHED_STATUS_LINE_PREFIX,\n NO_REPORT_STATUS_LINE,\n RUN_FAILED_COMMENT,\n RUN_FAILED_COMMENT_PREFIX,\n RUN_FINISHED_COMMENT,\n RUN_STARTED_COMMENT_PREFIX,\n RUN_STOPPED_BY_YOU_COMMENT,\n RUN_STOPPED_COMMENT,\n RUN_STOPPED_REASON_PREFIX,\n STOPPED_BY_YOU_STATUS_LINE,\n} from '../ops/runs'\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport {\n AGENT_APPROVAL_COMMENT_REFUSAL,\n AGENT_WAITING_REFUSAL,\n BOARD_AGENT_ROLE,\n BOARD_PERSON_ROLE,\n} from '../postgres/roles.sql'\nimport { BOARD_SCHEMA_EPOCH } from '../schema-epoch'\n\n/**\n * A machine-readable description of this board, for an agent that has never\n * seen it before: an orchestrator deciding how to work a card, a host wiring\n * up its own tools against the board, anything that is not a person reading\n * the README.\n *\n * Every list below is read out of the same constants and registrations the\n * board itself runs on — `KANBO_TOOLS`, `registerKanboCommands`,\n * `BOARD_RULES`, the canonical strings `ops/cards.ts` and `ops/runs.ts` write,\n * the roles `roles.sql.ts` creates — rather than written out a second time.\n * `manifest.test.ts` is what keeps that true: it fails the day one of those\n * sources gains something this file was not told about.\n *\n * `buildCapabilitiesManifest` and `renderCapabilitiesMarkdown` are the one\n * source two servings come from: `kanbo capabilities` prints either shape\n * from a terminal, and `kanbo mcp` serves both as resources\n * (`kanbo://capabilities`, `kanbo://capabilities.md`).\n */\n\n/** The manifest's own shape version, bumped when a field is added or removed — not the package's version. */\nexport const CAPABILITIES_MANIFEST_VERSION = '3'\n\n/** The board's name and the build that is answering, as `kanbo mcp` itself reports them. */\nexport interface KanboCapabilitiesPackage {\n name: string\n version: string\n}\n\n/** One of the three places a board can live, and what tells them apart. */\nexport interface KanboCapabilityStorage {\n /** `own-file` is `kanbo init --file`; `host-file` is a host app's own database; `postgres` is external. */\n kind: 'host-file' | 'own-file' | 'postgres'\n label: string\n description: string\n /** The board schema generation this build speaks, from `BOARD_SCHEMA_EPOCH` — the same number in all three. */\n schemaEpoch: number\n}\n\n/** One of the board's MCP tools, with the JSON Schema an orchestrator validates a call against. */\nexport interface KanboCapabilityTool {\n name: string\n title: string\n description: string\n /** `tool.inputSchema` converted with zod's own `toJSONSchema` — never hand-written. */\n inputSchema: Record<string, unknown>\n /** What to type instead, from `KANBO_TOOL_CLI_EQUIVALENTS`: the `kanbo` command. */\n cli: KanboToolCliEquivalent\n}\n\n/** One flag a CLI command takes, as commander itself declares it. */\nexport interface KanboCapabilityCommandOption {\n /** The flag spelling commander prints in `--help`, e.g. `--title <title>`. */\n flags: string\n description: string\n /** Set by `.requiredOption(...)`: the command refuses to run without it. */\n mandatory: boolean\n}\n\n/** One positional argument a CLI command takes, as commander itself declares it. */\nexport interface KanboCapabilityCommandArgument {\n name: string\n description: string\n required: boolean\n}\n\n/** One leaf command of the `kanbo` binary — a command with no subcommands of its own. */\nexport interface KanboCapabilityCommand {\n /** How a person types it, without the leading `kanbo`: `card create`, `approve`. */\n path: string\n description: string\n arguments: KanboCapabilityCommandArgument[]\n options: KanboCapabilityCommandOption[]\n /** `true` for the commands `requireHumanActor` guards: refused in a shell that says it belongs to an agent. */\n humanOnly: boolean\n /** Every exit code this build promises, from `CLI_EXIT_CODES` — the same table for every command. */\n exitCodes: typeof CLI_EXIT_CODES\n}\n\n/** The two database roles an external board is worked through, and what the database itself refuses the agent. */\nexport interface KanboCapabilitiesRoles {\n person: string\n agent: string\n /** What the database says back when the agent role tries the two things that are a person's. */\n refusals: string[]\n}\n\n/** One rule a column may ask of a card before an agent moves the card in (ruling 6-1). */\nexport interface KanboCapabilityEntryRule {\n rule: EntryRule\n description: string\n}\n\n/** Everything an orchestrator can read about this board before it has opened one. */\nexport interface KanboCapabilities {\n version: string\n package: KanboCapabilitiesPackage\n storages: KanboCapabilityStorage[]\n tools: KanboCapabilityTool[]\n commands: KanboCapabilityCommand[]\n /** The board-independent rules `kanbo prime` states — no columns here, because a column is per board. */\n rules: string[]\n /** Every rule a column may ask of a card, from `ENTRY_RULES` — which columns ask for which is per board, in `kanbo_columns`. */\n entryRules: KanboCapabilityEntryRule[]\n /** The literal strings the board writes for a run's lifecycle, an approval and a return — prefixes end in the punctuation or space that precedes the part a caller fills in. */\n canonicalStrings: Record<string, string>\n roles: KanboCapabilitiesRoles\n limits: string[]\n}\n\n/** The package name and version this build reports — the same literal `kanbo mcp` and `kanbo --version` already use. */\nconst KANBO_PACKAGE: KanboCapabilitiesPackage = { name: 'kanbo', version: KANBO_PACKAGE_VERSION }\n\nconst KANBO_STORAGES: readonly KanboCapabilityStorage[] = [\n {\n kind: 'host-file',\n label: 'A host app\\'s own database file',\n description: 'The board is nine tables among an app\\'s own, beside a `workspaces` table the command line reads '\n + 'the project\\'s workspace from. The app migrates the file itself; this package applies no migrations to '\n + 'it and reads and writes a schema someone else created. The app names the file with `KANBO_DB_PATH`.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n {\n kind: 'own-file',\n label: 'A board file of a project\\'s own',\n description: '`.kanbo/board.db` by default (`kanbo init --file`). This package creates it, migrates it '\n + 'with its own drizzle-sqlite chain, and marks it so every later command reads it as its own rather than '\n + 'looking for a `workspaces` table that is not there.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n {\n kind: 'postgres',\n label: 'An external Postgres database',\n description: 'Nothing but the board — no other owner exists to migrate it. `kanbo migrate` is the only '\n + 'thing that ever does, and `kanbo roles apply` installs the two roles and the rules the database '\n + 'itself keeps about them.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n]\n\n/**\n * The literal strings the board writes on a card's behalf, keyed the way an\n * agent reading the README prose would name them. A key ending the value in a\n * space or a colon-space is a prefix: the rest of the line is filled in by\n * whatever the write was about, not part of the constant.\n */\nfunction buildCanonicalStrings(): Record<string, string> {\n return {\n launched: LAUNCHED_STATUS_LINE_PREFIX,\n stoppedByYou: STOPPED_BY_YOU_STATUS_LINE,\n returned: RETURNED_STATUS_LINE_PREFIX,\n noReport: NO_REPORT_STATUS_LINE,\n runStarted: RUN_STARTED_COMMENT_PREFIX,\n runFinished: RUN_FINISHED_COMMENT,\n runFailed: RUN_FAILED_COMMENT,\n runFailedReason: RUN_FAILED_COMMENT_PREFIX,\n runStoppedByYou: RUN_STOPPED_BY_YOU_COMMENT,\n runStopped: RUN_STOPPED_COMMENT,\n runStoppedReason: RUN_STOPPED_REASON_PREFIX,\n approved: APPROVED_COMMENT,\n }\n}\n\n/** Every board tool, with its schema converted and its CLI equivalent attached. */\nfunction buildTools(): KanboCapabilityTool[] {\n return KANBO_TOOLS.map(tool => ({\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: z.toJSONSchema(z.object(tool.inputSchema)) as Record<string, unknown>,\n cli: KANBO_TOOL_CLI_EQUIVALENTS[tool.name],\n }))\n}\n\n/** Every leaf command the `kanbo` binary answers to, walked off its own registration. */\nfunction buildCommands(): KanboCapabilityCommand[] {\n const program = new Command()\n registerKanboCommands(program)\n\n function walk(command: Command, prefix: string[]): KanboCapabilityCommand[] {\n return command.commands.flatMap((child) => {\n const path = [...prefix, child.name()]\n if (child.commands.length > 0) {\n return walk(child, path)\n }\n return [{\n path: path.join(' '),\n description: child.description(),\n arguments: child.registeredArguments.map(argument => ({\n name: argument.name(),\n description: argument.description,\n required: argument.required,\n })),\n options: child.options.map(option => ({\n flags: option.flags,\n description: option.description,\n mandatory: option.mandatory,\n })),\n humanOnly: (HUMAN_ONLY_ACTIONS as readonly string[]).includes(path.join(' ')),\n exitCodes: CLI_EXIT_CODES,\n }]\n })\n }\n\n return walk(program, [])\n}\n\n/** Everything this build of the board can tell an orchestrator about itself. */\nexport function buildCapabilitiesManifest(): KanboCapabilities {\n return {\n version: CAPABILITIES_MANIFEST_VERSION,\n package: KANBO_PACKAGE,\n storages: [...KANBO_STORAGES],\n tools: buildTools(),\n commands: buildCommands(),\n rules: [...BOARD_RULES],\n entryRules: ENTRY_RULES.map(rule => ({ rule, description: ENTRY_RULE_DESCRIPTIONS[rule] })),\n canonicalStrings: buildCanonicalStrings(),\n roles: {\n person: BOARD_PERSON_ROLE,\n agent: BOARD_AGENT_ROLE,\n refusals: [AGENT_WAITING_REFUSAL, AGENT_APPROVAL_COMMENT_REFUSAL],\n },\n limits: [\n 'Only a person may approve or return a card. `kanbo approve` and `kanbo return` refuse a shell that '\n + 'says it belongs to an agent (`KANBO_ACTOR_KIND=agent`, exit 4), and there is no flag past it.',\n 'On an external board the same two rules are the database\\'s own: a person\\'s decision cannot be taken '\n + 'back, written over, or erased by the agent role, whichever client sends the write.',\n 'Only a person may close a sprint. `kanbo sprint close` refuses an agent\\'s shell '\n + '(exit 4), and the operation refuses any actor but a person; there is no MCP tool that closes one — '\n + '`kanbo_sprints` only reads. Unlike approval, the database role does not enforce this.',\n `The agent role (\\`${BOARD_AGENT_ROLE}\\`) may not delete a card at all — it holds select, insert and `\n + 'update on `issues` and nothing more, even though it may delete freely on every other board table.',\n 'A column may ask a card to meet entry rules before it enters (`entryRules`). Anyone but a person moving, '\n + 'creating or bulk-moving a card into it early is refused with `board_column_rules_unmet` and one line per '\n + 'unmet rule (CLI exit 1); a person goes through and is told what is missing. Only a person sets the rules '\n + '— `kanbo columns rules` refuses an agent\\'s shell (exit 4). The database role does not '\n + 'enforce them.',\n 'No board tool or command deletes a card, agent role or not. Cancelling one is a move to another column. '\n + 'The one exception is `kanbo serve`\\'s `DELETE /issues/:id`, which it answers only for '\n + 'a request presenting its token, on a server a person started, that has not sent `x-kanbo-actor: agent` '\n + '(403 `issue_delete_requires_user` otherwise).',\n '`kanbo migrate` and `kanbo roles apply` refuse a host app\\'s own database file: the app migrates it, '\n + 'and this package never touches it.',\n 'A write to a board older than this build\\'s schema epoch is refused (exit 3) until `kanbo migrate` '\n + 'runs (on a host app\\'s database, until the app migrates it) — reads stay open either way.',\n 'There is no MCP tool that approves a card, and there never will be: `kanbo_wait_approval` hands the '\n + 'card to a person and ends the turn; only a person takes it back.',\n ],\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"manifest-DHV2fRdO.mjs","names":["requireCard","requireCard","requireCard","requireCard","requireRun","requireCard","requireCard","requireCard","requireCard","assertPostgresBoardSchema","describePlan"],"sources":["../../src/cli/output.ts","../../src/cli/actor.ts","../../src/domain/numbering.ts","../../src/domain/issue-view.ts","../../src/domain/issue-activity.ts","../../src/domain/time.ts","../../src/ops/types.ts","../../src/ops/activity.ts","../../src/ops/approval.ts","../../src/domain/field-changes.ts","../../src/domain/status-name.ts","../../src/ops/change-seq.ts","../../src/domain/entry-rules.ts","../../src/ops/columns.ts","../../src/domain/pull-request-fact-keys.ts","../../src/ops/entry-rules.ts","../../src/domain/external-session-ref.ts","../../src/ops/runs.ts","../../src/ops/cards.ts","../../src/ops/card-batch.ts","../../src/ops/context-refs.ts","../../src/ops/migrate.ts","../../src/ops/agent-rules.ts","../../src/ops/prime.ts","../../src/domain/pull-request-ref.ts","../../src/ops/pull-requests.ts","../../src/ops/ready.ts","../../src/ops/relations.ts","../../src/ops/sprints.ts","../../src/ops/index.ts","../../src/domain/database-url.ts","../../src/cli/binding.ts","../../src/cli/db-target.ts","../../src/cli/postgres-board.ts","../../src/cli/schema-guard.ts","../../src/cli/workspace.ts","../../src/cli/command.ts","../../src/domain/card-display-title.ts","../../src/cli/view.ts","../../src/cli/commands/approve.ts","../../src/capabilities/markdown.ts","../../src/cli/commands/capabilities.ts","../../src/cli/commands/card.ts","../../src/cli/commands/columns.ts","../../src/package-version.ts","../../src/cli/failure.ts","../../src/cli/setup/instructions.ts","../../src/cli/setup/paths.ts","../../src/cli/setup/mcp-config.ts","../../src/cli/doctor.ts","../../src/cli/commands/doctor.ts","../../src/default-board-file-path.ts","../../src/cli/setup/confirm.ts","../../src/cli/setup/file-change.ts","../../src/cli/commands/init-global.ts","../../src/cli/commands/init.ts","../../src/mcp/transport.ts","../../src/mcp/db-transport.ts","../../src/mcp/resources.ts","../../src/mcp/instructions.ts","../../src/mcp/tools/tool.ts","../../src/mcp/tools/card-comment.ts","../../src/mcp/tools/card-create.ts","../../src/mcp/tools/card-get.ts","../../src/mcp/tools/card-link-pr.ts","../../src/mcp/tools/card-list.ts","../../src/mcp/tools/card-move.ts","../../src/mcp/tools/card-pull-requests.ts","../../src/mcp/tools/card-update.ts","../../src/mcp/tools/columns.ts","../../src/mcp/tools/prime.ts","../../src/mcp/tools/ready.ts","../../src/mcp/tools/run-finish.ts","../../src/mcp/tools/run-start.ts","../../src/mcp/tools/sprints.ts","../../src/mcp/tools/status-line.ts","../../src/mcp/tools/wait-approval.ts","../../src/mcp/tools/index.ts","../../src/mcp/server.ts","../../src/cli/commands/mcp.ts","../../src/cli/commands/migrate.ts","../../src/cli/commands/prime.ts","../../src/cli/commands/ready.ts","../../src/cli/commands/return.ts","../../src/cli/commands/roles.ts","../../src/cli/commands/run.ts","../../src/serve/actors.ts","../../src/serve/board-page.ts","../../src/serve/board-error-responses.ts","../../src/serve/errors.ts","../../src/serve/router.ts","../../src/serve/schemas.ts","../../src/serve/views.ts","../../src/serve/routes.ts","../../src/serve/server.ts","../../src/serve/access.ts","../../src/cli/commands/serve.ts","../../src/cli/commands/sprint.ts","../../src/cli/commands/uninstall.ts","../../src/cli/commands/index.ts","../../src/mcp/tool-names.ts","../../src/capabilities/manifest.ts"],"sourcesContent":["/**\n * Everything the board's command line writes, and the code it leaves with.\n *\n * The contract is deliberately small: a command hands back a value and — when\n * there is a good way to say it in one breath — the text a person should read.\n * `--format` chooses between the two machine shapes, and `--json <fields>` is\n * the agent's shortcut: the same value with only the named fields kept, so a\n * script can ask for `title,column` and get exactly that.\n *\n * The exit codes live here too, because a failure is output as much as a\n * result is: the board tool promises `2` for \"I could not find what to work\n * on\", `3` for \"this database is older than I am\" and `4` for \"a person has to\n * do this\", and a caller may branch on them.\n */\n\n/** What a command produces: the value, and how to say it to a person. */\nexport interface CliResult {\n /** The machine-readable result, printed as JSON unless `text` takes over. */\n value: unknown\n /** What a person reads when no format was asked for. */\n text?: string\n /**\n * The fields `--json` may name, declared up front rather than read off a row:\n * a result that comes back as an empty list still tells a caller what it\n * could have asked for instead of guessing from nothing.\n */\n fields?: readonly string[]\n}\n\n/** The two machine shapes `--format` offers. */\nexport type CliFormat = 'json' | 'pretty'\n\n/** The output flags every board command carries. */\nexport interface CliOutputOptions {\n /** Comma-separated field names; printing keeps only these. */\n json?: string\n format?: string\n}\n\n/** Nothing to work on, or nothing told us what to work on. */\nexport const EXIT_NOT_RESOLVED = 2\n/** The database is older than the board schema this build speaks. */\nexport const EXIT_SCHEMA_OUTDATED = 3\n/** The command is a person's to run, and this shell is not a person. */\nexport const EXIT_HUMAN_ONLY = 4\n\n/**\n * Every exit code this tool promises, and what it means — the capabilities\n * manifest's `exitCodes` reads this rather than naming the codes again. `0` and\n * `1` are not declared above: they need no name of their own, success and \"I\n * could not do that\" being every command line's baseline.\n */\nexport const CLI_EXIT_CODES = [\n { code: 0, meaning: 'Success.' },\n { code: 1, meaning: 'The command did not do what it was asked. See the message.' },\n { code: EXIT_NOT_RESOLVED, meaning: 'Nothing to work on, or nothing told the command what to work on.' },\n { code: EXIT_SCHEMA_OUTDATED, meaning: 'The database is older than the board schema this build speaks.' },\n { code: EXIT_HUMAN_ONLY, meaning: 'The command is a person\\'s to run, and this shell is not a person.' },\n] as const satisfies readonly { code: number, meaning: string }[]\n\n/**\n * A failure with a promised exit code. Anything else that goes wrong leaves\n * with `1`, which is the command line saying only that it did not work.\n */\nexport class CliError extends Error {\n readonly exitCode: number\n\n constructor(exitCode: number, message: string) {\n super(message)\n this.name = 'CliError'\n this.exitCode = exitCode\n }\n}\n\n/** The format a caller asked for, or the error that says there is no such format. */\nexport function readFormat(options: CliOutputOptions): CliFormat | null {\n if (options.format === undefined) {\n return null\n }\n if (options.format !== 'json' && options.format !== 'pretty') {\n throw new CliError(1, `Unknown format \"${options.format}\". Use json or pretty.`)\n }\n return options.format\n}\n\n/**\n * Write the result out.\n *\n * Without a format and without `--json`, a command that offered text gets its\n * text: `kanbo prime` prints the board's rules the way an agent should read\n * them, not a JSON string with the newlines spelled out. Everything else falls\n * back to indented JSON, which is the honest answer when the value has no\n * one-breath form.\n */\nexport function printResult(result: CliResult, options: CliOutputOptions): void {\n const fields = readFields(options.json)\n const format = readFormat(options)\n\n if (fields) {\n warnUnknownFields(fields, result)\n console.log(JSON.stringify(selectFields(result.value, fields), null, 2))\n return\n }\n if (format === 'json') {\n console.log(JSON.stringify(result.value))\n return\n }\n if (format === 'pretty' || result.text === undefined) {\n console.log(JSON.stringify(result.value, null, 2))\n return\n }\n console.log(result.text)\n}\n\nfunction readFields(json: string | undefined): string[] | null {\n if (json === undefined) {\n return null\n }\n const fields = json.split(',').map(field => field.trim()).filter(field => field.length > 0)\n if (fields.length === 0) {\n throw new CliError(1, 'Pass --json a comma-separated list of fields, for example --json title,column.')\n }\n return fields\n}\n\n/**\n * Say so, on stderr, about every field a caller asked for that this result\n * does not carry — one line per field, so a typo is caught instead of quietly\n * printing an object with the field missing. Never the exit code: the fields\n * that do exist still print, and an agent that asked for nine good fields and\n * one bad one should get the nine.\n */\nfunction warnUnknownFields(fields: string[], result: CliResult): void {\n const available = declaredFields(result)\n for (const field of fields) {\n if (!available.includes(field)) {\n console.error(`kanbo: unknown field \"${field}\"; available: ${available.join(', ')}`)\n }\n }\n}\n\n/**\n * What a caller could have asked `--json` for. A result that names its fields\n * up front is trusted, because a list of them can be empty and still have\n * something to say; otherwise the first row of a list, or the record itself,\n * says what it carries.\n */\nfunction declaredFields(result: CliResult): string[] {\n if (result.fields) {\n return [...result.fields]\n }\n const sample = Array.isArray(result.value) ? result.value[0] : result.value\n return isRecord(sample) ? Object.keys(sample) : []\n}\n\n/**\n * The same value with only the named fields kept. A list keeps them on every\n * item; a single record keeps them on itself. A field the value does not carry\n * is simply absent, rather than printed as null — a caller asking for a field\n * that is not there should see that it is not there.\n */\nfunction selectFields(value: unknown, fields: string[]): unknown {\n if (Array.isArray(value)) {\n return value.map(item => selectFields(item, fields))\n }\n if (!isRecord(value)) {\n return value\n }\n return Object.fromEntries(fields\n .filter(field => Object.hasOwn(value, field))\n .map(field => [field, value[field]]))\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { hostname, userInfo } from 'node:os'\n\nimport type { BoardActor } from '../ops/types'\nimport { CliError, EXIT_HUMAN_ONLY } from './output'\n\n/**\n * Who the board is being written by, as far as a shell can tell.\n *\n * The HTTP route knows its actor from the session it was called with. A\n * terminal has no session, so the tool says the honest thing: this is something\n * outside any app, run by whoever owns this shell, on this machine. It is never\n * an argument — a caller that could name its own kind could name `user`, and\n * the one rule the board has about people would be worth nothing.\n */\n\n/** What a person is told when an agent's shell tries to answer for them. */\nexport const APPROVAL_IS_HUMAN_MESSAGE\n = 'Approval is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to approve it on the board or run \"kanbo approve\" in their own terminal.'\n\n/** The same, for sending a card back. */\nexport const RETURN_IS_HUMAN_MESSAGE\n = 'Return is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to return it on the board or run \"kanbo return\" in their own terminal.'\n\n/** The same, for closing a sprint (ruling 5x-4). */\nexport const SPRINT_CLOSE_IS_HUMAN_MESSAGE\n = 'Closing a sprint is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to close the sprint on the board or run \"kanbo sprint close\" in their own terminal.'\n\n/**\n * The same, for setting what a column asks of a card (ruling 6-1): an agent that\n * could clear a column's rules could walk any card past them.\n */\nexport const COLUMN_RULES_IS_HUMAN_MESSAGE\n = 'Column entry rules are for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to set them on the board or run \"kanbo columns rules\" in their own terminal.'\n\n/**\n * The same, for taking back the log a run names: an agent that could clear or\n * swap it could point a card's history at any log it liked.\n */\nexport const RUN_SESSION_REF_IS_HUMAN_MESSAGE\n = 'Changing or clearing the log a run names is for a person. This shell belongs to an agent (KANBO_ACTOR_KIND=agent). '\n + 'Ask a person to do it on the board or run \"kanbo run clear-session\" or \"kanbo run attach-session --replace\" in their own terminal.'\n\n/** The commands only a person may run, as typed after `kanbo` — the capabilities manifest reads this rather than naming them again. */\nexport const HUMAN_ONLY_ACTIONS = ['approve', 'return', 'sprint close', 'columns rules', 'run clear-session'] as const\n\n/** What only a person may do: the commands above, and one option of a command anyone may run. */\ntype HumanOnlyAction = typeof HUMAN_ONLY_ACTIONS[number] | 'run attach-session --replace'\n\nconst HUMAN_ONLY_MESSAGES: Record<HumanOnlyAction, string> = {\n 'approve': APPROVAL_IS_HUMAN_MESSAGE,\n 'return': RETURN_IS_HUMAN_MESSAGE,\n 'sprint close': SPRINT_CLOSE_IS_HUMAN_MESSAGE,\n 'columns rules': COLUMN_RULES_IS_HUMAN_MESSAGE,\n 'run clear-session': RUN_SESSION_REF_IS_HUMAN_MESSAGE,\n 'run attach-session --replace': RUN_SESSION_REF_IS_HUMAN_MESSAGE,\n}\n\n/** The actor every board write from this tool is filed under. */\nexport function createCliActor(): BoardActor {\n return {\n kind: 'external',\n id: process.env.KANBO_ACTOR_ID?.trim() || userInfo().username,\n name: hostname(),\n }\n}\n\n/**\n * The person on the other end of this shell — or the refusal that says there\n * is none.\n *\n * Approving, returning, closing a sprint, setting a column's entry rules and\n * taking back the log a run names are the things only a person may do, and a shell\n * that says it belongs to an agent is not one. There is no flag to get past this: an\n * agent that can approve its own work makes every card waiting for a person a\n * lie, and an escape hatch would be used by the first agent that read about it.\n */\nexport function requireHumanActor(action: HumanOnlyAction): BoardActor {\n if (isAgentShell()) {\n throw new CliError(EXIT_HUMAN_ONLY, HUMAN_ONLY_MESSAGES[action])\n }\n return createPersonActor()\n}\n\n/**\n * The actor a card placement — `card create`, `card update`, `card move` —\n * runs as. It is filed `external` like every other write from this tool\n * (ruling 6-6). In a person's own terminal it also carries `placesForPerson`,\n * so a column's entry rules warn that person instead of holding the card back\n * (ruling 6-5), exactly as they do on the board page. In an agent's shell it\n * does not, and the column refuses the card.\n */\nexport function createPlacementActor(): BoardActor {\n return isAgentShell() ? createCliActor() : { ...createCliActor(), placesForPerson: true }\n}\n\n/**\n * The person who owns this shell, as the board files a person. Only for a\n * caller that has already asked `isAgentShell` — `requireHumanActor` for a\n * command, `kanbo serve` once when it starts.\n */\nexport function createPersonActor(): BoardActor {\n return { kind: 'user', id: userInfo().username }\n}\n\n/**\n * Does this shell say it belongs to an agent?\n *\n * `KANBO_ACTOR_KIND=agent` is the one way to say so. An app that starts agents\n * sets it in every shell and process it starts for one; a person can set it\n * for an agent they run themselves. Two things ask. `approve`, `return`, `sprint close` and `columns rules` refuse such a shell outright, and the\n * board resolution hands it the agent role's own connection string when the\n * project has one — the same question, answered once here.\n */\nexport function isAgentShell(): boolean {\n return process.env.KANBO_ACTOR_KIND?.trim() === 'agent'\n}\n","import type { BoardStore } from '../board-store'\n\n/**\n * What numbering needs to know about a workspace: enough to build the key stem,\n * and nothing else. A host app's workspace row satisfies it; so does a workspace\n * a command-line tool resolved from a path, without the board reading anybody's\n * tables to find it.\n *\n * Written out rather than `Pick`ed from a row type, because the board has no row\n * to pick from: a workspace belongs to whoever tracks it, and this package declares only what it\n * needs of one.\n */\nexport interface BoardWorkspaceIdentity {\n id: string\n identifier: string\n name: string\n}\n\n/** The three-letter stem every issue key in the workspace starts with. */\nexport function readIssuePrefix(workspace: BoardWorkspaceIdentity): string {\n const raw = workspace.identifier || workspace.name || workspace.id\n const normalized = raw.toUpperCase().replace(/[^A-Z0-9]/g, '')\n return (normalized.slice(0, 3) || 'ISS').padEnd(3, 'X')\n}\n\nexport function formatIssueId(prefix: string, number: number): string {\n return `${prefix}-${number.toString().padStart(3, '0')}`\n}\n\n/**\n * The key and number the next issue of this workspace gets. The loop skips keys\n * an earlier prefix already took, so a renamed workspace never collides with its\n * own history.\n */\nexport async function nextIssueIdentity(workspace: BoardWorkspaceIdentity, store: BoardStore): Promise<{ id: string, number: number }> {\n const prefix = readIssuePrefix(workspace)\n let number = await store.issues.maxNumber(workspace.id) + 1\n\n while (true) {\n const id = formatIssueId(prefix, number)\n const existing = await store.issues.findById(id)\n if (!existing) {\n return { id, number }\n }\n number += 1\n }\n}\n\n// `tx` is the store the caller is working through: inside `store.transaction`\n// that is the transactional view, so the read sees what the transaction has\n// already written.\nexport async function nextIssueNumber(workspaceId: string, tx: BoardStore): Promise<number> {\n return await tx.issues.maxNumber(workspaceId) + 1\n}\n\nexport async function nextIssueOrder(workspaceId: string, tx: BoardStore): Promise<number> {\n return await tx.issues.maxOrder(workspaceId) + 1024\n}\n","import { z } from 'zod'\n\nimport type { Issue, IssueComment, IssueFieldChange } from '../sqlite/schema'\n\n/** An issue row with its `labels` column decoded from JSON. */\nexport type IssueView = Omit<Issue, 'labels'> & { labels: string[] }\n\n/**\n * Who a board change is attributed to. The `issue_field_changes.actor_kind`\n * column owns the union; comment authors carry the same kinds plus the\n * `system.*` markers that `normalizeCommentAuthorKind` folds away.\n */\nexport type IssueActorKind = IssueFieldChange['actorKind']\n\nconst IssueLabelsJsonSchema = z.string()\n .transform(raw => JSON.parse(raw))\n .pipe(z.array(z.string()))\n\nexport function toIssueView(issue: Issue): IssueView {\n return {\n ...issue,\n labels: IssueLabelsJsonSchema.parse(issue.labels),\n }\n}\n\n/**\n * Fold a comment's author kind onto the actor kinds a reader renders: only a\n * person, an agent and a provider target speak for themselves, so every\n * `system.*` marker — whatever it marks — is the board speaking as `system`.\n */\nexport function normalizeCommentAuthorKind(authorKind: IssueComment['authorKind']): IssueActorKind {\n if (authorKind === 'user' || authorKind === 'agent' || authorKind === 'provider-target') {\n return authorKind\n }\n return 'system'\n}\n\n/** Who a row is filed under, as the row stores it — what a host resolves into a name to show. */\nexport interface IssueActorReference {\n kind: IssueActorKind\n id: string | null\n sourceChatSessionId: string | null\n}\n\n/** What a stored row says about who wrote it: the name its writer gave, and the chat session it came from. */\nexport interface IssueWriterProvenance {\n authorId: IssueComment['authorId']\n sourceChatSessionId: IssueComment['sourceChatSessionId']\n}\n\n/**\n * Was this row written by an outside writer?\n *\n * An outside writer is stored as `system`, the same kind the board writes\n * under, so the kind alone cannot say. What a row carries can: a writer from\n * outside — the `kanbo` command line, an agent someone ran themselves — gives\n * a name and speaks from no chat session, while the board writing on its\n * own behalf gives no name (`BOARD_ACTOR`) and an app's agent runtime filed under\n * `system` always carries the session it was called in. The caller establishes\n * the `system` kind first; this answers the rest of the question, and says in\n * its type that such a writer has a name to show.\n */\nexport function isOutsideWriter(provenance: IssueWriterProvenance): provenance is IssueWriterProvenance & { authorId: string } {\n return provenance.authorId !== null && provenance.sourceChatSessionId === null\n}\n","import type { IssueComment } from '../sqlite/schema'\nimport type {\n IssueActivityCommentView,\n IssueActivityItemView,\n IssueActivityLookup,\n IssueActivityValueToken,\n IssueActivityValueView,\n} from './activity-types'\nimport type { IssueActorReference, IssueWriterProvenance } from './issue-view'\nimport { isOutsideWriter, normalizeCommentAuthorKind } from './issue-view'\n\nexport function activityKindOrder(kind: IssueActivityItemView['kind']): number {\n if (kind === 'created') {\n return 0\n }\n if (kind === 'field-change') {\n return 1\n }\n return 2\n}\n\n/**\n * The badge a system comment renders under. Delegation owns two of its own; the\n * rest of the `system.*` markers read as plain system comments until they earn\n * a badge.\n *\n * A plain `system` row is where provenance decides: a writer from outside —\n * the command line, an agent someone ran themselves — is stored as `system` too, and what it wrote is an ordinary comment\n * rather than a line about the board — `isOutsideWriter` is that question.\n */\nexport function readSystemCommentKind(\n authorKind: IssueComment['authorKind'],\n provenance: IssueWriterProvenance,\n): IssueActivityCommentView['systemKind'] {\n if (authorKind === 'system.delegated') {\n return 'delegated'\n }\n if (authorKind === 'system.undelegated') {\n return 'undelegated'\n }\n if (authorKind === 'user' || authorKind === 'agent' || authorKind === 'provider-target') {\n return null\n }\n if (authorKind === 'system' && isOutsideWriter(provenance)) {\n return null\n }\n return 'system'\n}\n\n/**\n * Who a comment is shown under. A `system.*` marker is the board's own line\n * about what happened — a run launched, a person's decision — so the id it\n * carries names whoever caused the line, never its author. Only a plain\n * `system` row can name a writer.\n */\nexport function commentAuthorReference(comment: IssueComment): IssueActorReference {\n const kind = normalizeCommentAuthorKind(comment.authorKind)\n const isBoardMarker = kind === 'system' && comment.authorKind !== 'system'\n return {\n kind,\n id: isBoardMarker ? null : comment.authorId,\n sourceChatSessionId: comment.sourceChatSessionId,\n }\n}\n\nexport function formatActivityIssueReference(value: string | null, lookup: IssueActivityLookup): IssueActivityValueView {\n if (!value) {\n return activityToken('no-parent')\n }\n const issue = lookup.issueById.get(value)\n if (!issue) {\n return activityToken('unknown-issue')\n }\n return { kind: 'text', text: `${issue.id} ${issue.title}` }\n}\n\nexport function formatPlainActivityValue(value: string | null): IssueActivityValueView {\n if (isEmptyActivityValue(value)) {\n return activityToken('empty')\n }\n\n const trimmed = value.trim()\n if (trimmed.length > 96) {\n return activityToken('changed')\n }\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) {\n return activityToken('changed')\n }\n if (/^(external_provider_target|provider_target|agent_session)_/.test(trimmed)) {\n return activityToken('changed')\n }\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return activityToken('changed')\n }\n return { kind: 'text', text: trimmed }\n}\n\nexport function parseActivityStringArray(value: string | null): string[] | null {\n if (value == null || value === '') {\n return []\n }\n\n try {\n const parsed = JSON.parse(value) as unknown\n if (Array.isArray(parsed) && parsed.every(item => typeof item === 'string')) {\n return parsed\n }\n }\n catch {\n return null\n }\n\n return null\n}\n\nexport function isEmptyActivityValue(value: string | null): value is null | '' | '[]' {\n return value == null || value === '' || value === '[]'\n}\n\nexport function activityText(text: string | null): IssueActivityValueView | null {\n const trimmed = text?.trim()\n if (!trimmed) {\n return null\n }\n return { kind: 'text', text: trimmed }\n}\n\nexport function activityToken(token: IssueActivityValueToken): IssueActivityValueView {\n return { kind: 'token', token }\n}\n","/**\n * Unix seconds — the unit every board timestamp column stores.\n *\n * The package carries its own clock, so it depends on nothing of its host's.\n */\nexport function currentUnixSeconds(): number {\n return Math.floor(Date.now() / 1000)\n}\n\nconst DAY_SECONDS = 86_400\n\n/**\n * A board date is a calendar day (ruling 5x-6): a start is kept as the first\n * second of its UTC day, a due date as the last, so a sprint runs through its\n * due day wherever the person reading it is.\n */\nexport function utcDayStart(seconds: number): number {\n return Math.floor(seconds / DAY_SECONDS) * DAY_SECONDS\n}\n\n/** The last second of the UTC day `seconds` falls on — see `utcDayStart`. */\nexport function utcDayEnd(seconds: number): number {\n return utcDayStart(seconds) + DAY_SECONDS - 1\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueComment, IssueFieldChange, IssueRun } from '../sqlite/schema'\n\n/**\n * Who is acting on the board.\n *\n * Four kinds reach it: a person, an agent an app launched and knows, a provider\n * target standing in for one, and anything else — a command-line tool, a\n * script, an agent someone ran themselves. `id` is whatever the host calls that\n * actor, or `null` when it has no name to give; `chatSessionId` is the app's\n * chat session the action came from, and means nothing to a board written from\n * outside.\n */\nexport interface BoardActor {\n kind: 'user' | 'agent' | 'provider-target' | 'external'\n id: string | null\n name?: string\n chatSessionId?: string | null\n /**\n * A person's own terminal placing a card (ruling 6-6): the write is still\n * filed under `kind`/`id`, but a column's entry rules warn instead of\n * refusing, as they do for a person on the board page. Only the entry-rule check reads\n * it; it is never stored.\n */\n placesForPerson?: boolean\n}\n\n/**\n * The board writing on its own behalf: the lines and comments the board produces\n * from a run's lifecycle, which no person and no agent typed.\n *\n * This matters beyond provenance. Whether a failed run ends with \"no report\"\n * depends on the agent having written a status line since it started, so a line\n * the board wrote itself must never be filed under the agent that was running.\n */\nexport const BOARD_ACTOR: BoardActor = { kind: 'external', id: null }\n\n/**\n * How an actor is stored on a comment or a field change. The board's four kinds\n * do not all exist in those columns: an outside writer is the system speaking,\n * while a provider target keeps its own kind, because an app's comment view\n * resolves it to the provider that wrote the comment.\n */\nexport function toStoredActorKind(actor: BoardActor): IssueFieldChange['actorKind'] {\n return actor.kind === 'external' ? 'system' : actor.kind\n}\n\n/**\n * How an actor is stored on a run. A run is launched by a person, by an agent,\n * or from outside; a provider target launches as the agent it stands in\n * for, because the run registry has no kind of its own for it.\n */\nexport function toRunLaunchedByKind(actor: BoardActor): IssueRun['launchedByKind'] {\n switch (actor.kind) {\n case 'user':\n return 'user'\n case 'agent':\n case 'provider-target':\n return 'agent'\n case 'external':\n return 'external'\n }\n}\n\n/** The card, or the error that says the board has no such card. */\nexport async function requireCard(store: BoardStore, issueId: string): Promise<Issue> {\n const card = await store.issues.findById(issueId)\n if (!card) {\n throw new BoardError('issue_not_found', { issueId })\n }\n return card\n}\n\n/**\n * Write what the card is doing right now, and record who said so.\n *\n * The column carries the current line; the field change carries its history,\n * and is the only place the actor and the time behind a line are kept — which is\n * what lets a reader tell a line the agent wrote from one the board wrote for\n * it. A line that says exactly what the card already says is not a change, and\n * writes nothing at all.\n */\nexport async function writeStatusLine(\n store: BoardStore,\n card: Issue,\n statusLine: string | null,\n actor: BoardActor,\n): Promise<Issue> {\n if (card.statusLine === statusLine) {\n return card\n }\n\n const now = currentUnixSeconds()\n await store.issues.update(card.id, { statusLine, updatedAt: now })\n await store.fieldChanges.create({\n id: randomUUID(),\n issueId: card.id,\n field: 'statusLine',\n fromValue: card.statusLine,\n toValue: statusLine,\n actorKind: toStoredActorKind(actor),\n actorId: actor.id ?? null,\n sourceChatSessionId: actor.chatSessionId ?? null,\n createdAt: now,\n })\n return { ...card, statusLine, updatedAt: now }\n}\n\n/** A comment as the board writes it, before the row exists. */\nexport interface BoardCommentInput {\n issueId: string\n content: string\n authorKind: IssueComment['authorKind']\n authorId?: string | null\n sourceChatSessionId?: string | null\n agentActivityId?: string | null\n /**\n * The key that makes the comment a fact rather than a message: writing it\n * again with the same key leaves the first one standing.\n */\n dedupeKey?: string | null\n}\n\nexport async function insertComment(store: BoardStore, input: BoardCommentInput): Promise<IssueComment> {\n const values = {\n id: randomUUID(),\n issueId: input.issueId,\n content: input.content,\n authorKind: input.authorKind,\n authorId: input.authorId ?? null,\n sourceChatSessionId: input.sourceChatSessionId ?? null,\n agentActivityId: input.agentActivityId ?? null,\n createdAt: currentUnixSeconds(),\n }\n\n if (input.dedupeKey == null) {\n return await store.comments.create(values)\n }\n return await store.comments.createOnce({ ...values, dedupeKey: input.dedupeKey })\n}\n","import type { BoardStore } from '../board-store'\nimport type {\n IssueActivityField,\n IssueActivityFieldChangeView,\n IssueActivityItemView,\n IssueActivityLookup,\n IssueActivityValueToken,\n IssueActivityValueView,\n IssueCommentAuthorView,\n} from '../domain/activity-types'\nimport {\n activityKindOrder,\n activityText,\n activityToken,\n commentAuthorReference,\n formatActivityIssueReference,\n formatPlainActivityValue,\n isEmptyActivityValue,\n parseActivityStringArray,\n readSystemCommentKind,\n} from '../domain/issue-activity'\nimport type { IssueActorReference } from '../domain/issue-view'\nimport type { IssueComment, IssueFieldChange } from '../sqlite/schema'\nimport { requireCard } from './types'\n\n/**\n * What the host knows and the board does not: the names of its workspaces, who\n * an actor is, and what an agent is called. A board holds ids for all three and\n * nothing more, so an Activity feed is the board's rows read through these.\n */\nexport interface IssueActivityResolver {\n workspaceById: IssueActivityLookup['workspaceById']\n /** Who a reader sees beside a row. */\n resolveActor: (actor: IssueActorReference) => Promise<IssueCommentAuthorView>\n /** The agent's name, or `null` when the host has no such agent. */\n resolveAgentName: (agentId: string) => Promise<string | null>\n}\n\n/**\n * Field changes the feed keeps out of sight. `executionMode` is recorded and\n * readable through the field-change history — where a card runs is a fact about\n * the work — but the card itself already says it, so the feed does not repeat\n * it. `waitingFor` is listed for the same reason and is not recorded at all:\n * whose turn it is shows in the status line the card is handed over with, and in\n * the approval and return comments around it.\n */\nconst HIDDEN_ACTIVITY_FIELDS = new Set([\n 'assigneeKind',\n 'contextRefs',\n 'delegateAgentId',\n 'delegateProviderTargetId',\n 'executionMode',\n 'order',\n 'waitingFor',\n])\n\nconst ACTIVITY_FIELD_BY_RAW_FIELD: Record<string, IssueActivityField> = {\n assigneeId: 'assignee',\n description: 'description',\n dueDate: 'due-date',\n labels: 'labels',\n milestoneId: 'milestone',\n parentIssueId: 'parent',\n priority: 'priority',\n statusId: 'status',\n statusLine: 'status-line',\n title: 'title',\n workspaceId: 'workspace',\n}\n\nconst PRIORITY_ACTIVITY_VALUE_TOKEN: Record<string, IssueActivityValueToken> = {\n none: 'priority-none',\n low: 'priority-low',\n medium: 'priority-medium',\n high: 'priority-high',\n urgent: 'priority-urgent',\n}\n\n/** The card's field history, oldest first — every tracked change, hidden ones included. */\nexport async function listFieldChanges(store: BoardStore, issueId: string): Promise<IssueFieldChange[]> {\n await requireCard(store, issueId)\n return await store.fieldChanges.listByIssue(issueId)\n}\n\n/**\n * The card's Activity feed: its creation, its visible field changes and its\n * comments, oldest first, with a creation before a change and a change before a\n * comment written in the same second.\n */\nexport async function listActivity(\n store: BoardStore,\n issueId: string,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityItemView[]> {\n const issue = await requireCard(store, issueId)\n const lookup: IssueActivityLookup = {\n issue,\n issueById: new Map((await store.issues.listAll()).map(row => [row.id, row])),\n milestoneById: new Map((await store.milestones.listAll()).map(row => [row.id, row])),\n statusById: new Map((await store.statuses.listAll()).map(row => [row.id, row])),\n workspaceById: resolver.workspaceById,\n }\n const commentItems = await Promise.all(\n (await store.comments.listByIssue(issueId)).map(async comment => await toCommentActivityItem(comment, resolver)),\n )\n const fieldChangeItems = (await Promise.all(\n (await store.fieldChanges.listByIssue(issueId)).map(async change => await toFieldChangeActivityItem(change, lookup, resolver)),\n ))\n .filter((item): item is IssueActivityItemView => item !== null)\n const createdItem: IssueActivityItemView = {\n id: `${issue.id}:created`,\n issueId: issue.id,\n kind: 'created',\n actor: await resolver.resolveActor({\n kind: issue.createdByKind,\n id: issue.createdById,\n sourceChatSessionId: issue.sourceChatSessionId,\n }),\n comment: null,\n fieldChange: null,\n sourceChatSessionId: issue.sourceChatSessionId,\n createdAt: issue.createdAt,\n }\n\n return [\n createdItem,\n ...fieldChangeItems,\n ...commentItems,\n ].toSorted((left, right) => left.createdAt - right.createdAt || activityKindOrder(left.kind) - activityKindOrder(right.kind))\n}\n\nasync function toCommentActivityItem(comment: IssueComment, resolver: IssueActivityResolver): Promise<IssueActivityItemView> {\n return {\n id: comment.id,\n issueId: comment.issueId,\n kind: 'comment',\n actor: await resolver.resolveActor(commentAuthorReference(comment)),\n comment: {\n content: comment.content,\n systemKind: readSystemCommentKind(comment.authorKind, comment),\n },\n fieldChange: null,\n sourceChatSessionId: comment.sourceChatSessionId,\n createdAt: comment.createdAt,\n }\n}\n\nasync function toFieldChangeActivityItem(\n change: IssueFieldChange,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityItemView | null> {\n if (HIDDEN_ACTIVITY_FIELDS.has(change.field)) {\n return null\n }\n\n const field = ACTIVITY_FIELD_BY_RAW_FIELD[change.field] ?? 'metadata'\n return {\n id: change.id,\n issueId: change.issueId,\n kind: 'field-change',\n actor: await resolver.resolveActor({\n kind: change.actorKind,\n id: change.actorId,\n sourceChatSessionId: change.sourceChatSessionId,\n }),\n comment: null,\n fieldChange: await formatActivityFieldChange(change, field, lookup, resolver),\n sourceChatSessionId: change.sourceChatSessionId,\n createdAt: change.createdAt,\n }\n}\n\nasync function formatActivityFieldChange(\n change: IssueFieldChange,\n field: IssueActivityField,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityFieldChangeView> {\n if (field === 'description') {\n if (isEmptyActivityValue(change.fromValue) && !isEmptyActivityValue(change.toValue)) {\n return { action: 'added-description', field, fromValue: null, toValue: null }\n }\n if (!isEmptyActivityValue(change.fromValue) && isEmptyActivityValue(change.toValue)) {\n return { action: 'cleared-description', field, fromValue: null, toValue: null }\n }\n return { action: 'updated-description', field, fromValue: null, toValue: null }\n }\n\n return {\n action: field === 'title' ? 'renamed-issue' : 'changed-field',\n field,\n fromValue: await formatActivityFieldValue(field, change.fromValue, lookup, resolver),\n toValue: await formatActivityFieldValue(field, change.toValue, lookup, resolver),\n }\n}\n\nasync function formatActivityFieldValue(\n field: IssueActivityField,\n value: string | null,\n lookup: IssueActivityLookup,\n resolver: IssueActivityResolver,\n): Promise<IssueActivityValueView> {\n switch (field) {\n case 'assignee':\n if (isEmptyActivityValue(value)) {\n return activityToken('unassigned')\n }\n if (value === '__self__') {\n return activityToken('current-user')\n }\n return activityText(await resolver.resolveAgentName(value)) ?? activityToken('unknown-user')\n\n case 'due-date': {\n if (isEmptyActivityValue(value)) {\n return activityToken('no-due-date')\n }\n const timestamp = Number(value)\n if (!Number.isFinite(timestamp)) {\n return activityToken('changed')\n }\n return { kind: 'date', timestamp }\n }\n\n case 'labels': {\n const labels = parseActivityStringArray(value)\n if (!labels || labels.length === 0) {\n return activityToken('no-labels')\n }\n return { kind: 'text', text: labels.join(', ') }\n }\n\n case 'milestone':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-milestone')\n }\n return activityText(lookup.milestoneById.get(value)?.title ?? null) ?? activityToken('unknown-milestone')\n\n case 'parent':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-parent')\n }\n return formatActivityIssueReference(value, lookup)\n\n case 'priority':\n return activityToken(PRIORITY_ACTIVITY_VALUE_TOKEN[value ?? ''] ?? 'priority-none')\n\n case 'status':\n if (isEmptyActivityValue(value)) {\n return activityToken('no-status')\n }\n return activityText(lookup.statusById.get(value)?.name ?? null) ?? activityToken('unknown-status')\n\n case 'title':\n return formatPlainActivityValue(value)\n\n case 'workspace':\n if (isEmptyActivityValue(value)) {\n return activityToken('empty')\n }\n return activityText(lookup.workspaceById.get(value)?.name ?? null) ?? formatPlainActivityValue(value)\n\n default:\n return formatPlainActivityValue(value)\n }\n}\n","import type { BoardStore } from '../board-store'\nimport type { IssueComment } from '../sqlite/schema'\nimport { requireCard } from './types'\n\n/** Where a card stands with the person who has to approve it. */\nexport interface BoardApproval {\n state: 'none' | 'pending' | 'approved' | 'returned'\n /** When the person decided, in unix seconds; `null` while nobody has. */\n decidedAt: number | null\n /** What the person wrote alongside the decision, if anything. */\n comment: string | null\n}\n\n/** The first line of the comment an approval writes; anything after it is the person's own. */\nexport const APPROVED_COMMENT = 'Approved'\n\n/**\n * Where the card stands with the person who has to approve it: waiting for one,\n * or carrying the last decision a person made — which is what a waiting session\n * compares against its own `since` to learn that the answer has arrived.\n */\nexport async function readApproval(store: BoardStore, issueId: string): Promise<BoardApproval> {\n const card = await requireCard(store, issueId)\n if (card.waitingFor === 'human') {\n return { state: 'pending', decidedAt: null, comment: null }\n }\n\n const decision = (await store.comments.listByIssue(issueId))\n .filter(comment => comment.authorKind === 'system.approved' || comment.authorKind === 'system.returned')\n .at(-1)\n if (!decision) {\n return { state: 'none', decidedAt: null, comment: null }\n }\n\n return {\n state: decision.authorKind === 'system.approved' ? 'approved' : 'returned',\n decidedAt: decision.createdAt,\n comment: humanComment(decision),\n }\n}\n\n/** What the person wrote beside a decision, with the board's own wording taken off. */\nfunction humanComment(decision: IssueComment): string | null {\n if (decision.authorKind !== 'system.approved') {\n return decision.content.trim() || null\n }\n const [first, ...rest] = decision.content.split('\\n')\n const written = (first === APPROVED_COMMENT ? rest.join('\\n') : decision.content).trim()\n return written || null\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { Issue, issueFieldChanges, issues } from '../sqlite/schema'\nimport type { IssueActorKind } from './issue-view'\n\n/**\n * The issue columns whose edits are written to the audit trail.\n *\n * `statusLine` is absent on purpose: `writeStatusLine` records every line it\n * writes itself, and no patch ever carries the column. `waitingFor` is absent\n * too — whose turn it is shows in the card's own line and in the\n * `system.approved` / `system.returned` comments around it, so a row saying the\n * same thing a third time would only crowd the history.\n */\nexport const TRACKED_FIELDS = [\n 'workspaceId',\n 'title',\n 'description',\n 'priority',\n 'labels',\n 'milestoneId',\n 'parentIssueId',\n 'statusId',\n 'assigneeKind',\n 'assigneeId',\n 'dueDate',\n 'delegateAgentId',\n 'delegateProviderTargetId',\n 'contextRefs',\n 'executionMode',\n 'order',\n] as const\n\n/** Who an issue edit is attributed to, with the chat session it came from. */\nexport interface IssueMutationActor {\n kind: IssueActorKind\n id: string | null\n sourceChatSessionId?: string | null\n}\n\n/**\n * The audit rows one patch produces: a row per tracked field whose stringified\n * value actually changed. Nothing is written here — the caller inserts what\n * comes back, so the diff stays a pure function of the row, the patch, the\n * actor and the clock.\n */\nexport function diffFieldChanges(\n before: Issue,\n updates: Partial<typeof issues.$inferInsert>,\n actor: IssueMutationActor,\n now: number,\n): Array<typeof issueFieldChanges.$inferInsert> {\n const rows: Array<typeof issueFieldChanges.$inferInsert> = []\n for (const field of TRACKED_FIELDS) {\n if (!(field in updates)) {\n continue\n }\n const dbField = field === 'labels' ? 'labels' : field\n const rawBefore = before[dbField as keyof Issue]\n const rawAfter = updates[field]\n const fromValue = rawBefore == null ? null : String(rawBefore)\n const toValue = rawAfter == null ? null : String(rawAfter)\n if (fromValue === toValue) {\n continue\n }\n rows.push({\n id: randomUUID(),\n issueId: before.id,\n field,\n fromValue,\n toValue,\n actorKind: actor.kind,\n actorId: actor.id ?? null,\n sourceChatSessionId: actor.sourceChatSessionId ?? null,\n createdAt: now,\n })\n }\n return rows\n}\n","/**\n * The columns a workspace gets the first time someone opens its board, in board\n * order. A workspace that already holds statuses is never re-seeded.\n */\nexport const DEFAULT_STATUSES = [\n { name: 'Backlog', description: 'Idea captured, not ready to work on', color: '#6b7280', category: 'backlog' as const },\n { name: 'To Do', description: 'Spelled out, ready to launch', color: '#9ca3af', category: 'unstarted' as const },\n { name: 'In Progress', description: 'The orchestrator and its agents are working', color: '#f59e0b', category: 'started' as const },\n { name: 'In Review', description: 'Work done; being checked or waiting for you', color: '#8b5cf6', category: 'started' as const },\n { name: 'Done', description: 'Accepted', color: '#22c55e', category: 'completed' as const },\n { name: 'Canceled', description: 'Canceled', color: '#6b7280', category: 'canceled' as const },\n] as const\n\n/**\n * The slug two status names have in common when they mean the same column:\n * case and the run of spaces or dashes between words are ignored, so `In\n * Progress`, `in-progress` and `IN_PROGRESS` all resolve to one status.\n */\nexport function normalizeStatusName(value: string): string {\n return value.trim().toLowerCase().replace(/[\\s-]+/g, '_')\n}\n","import type { BoardStore } from '../board-store'\nimport { currentUnixSeconds } from '../domain/time'\n\n/** The `kanban_meta` row every board write bumps. */\nconst BOARD_CHANGE_KEY = 'board'\n\n/** The row groups of the store — everything it offers except the transaction itself. */\ntype BoardStoreGroup = Exclude<keyof BoardStore, 'transaction'>\n\n/**\n * Every store method that changes a row, per group.\n *\n * The list is written out rather than derived from a naming rule so that a\n * method added to a group has to be classified by hand: a read that were treated\n * as a write would only bump the counter too often, but a write mistaken for a\n * read would leave readers on a board that silently changed. `satisfies` ties\n * each name to its group, so renaming a store method breaks the build here.\n */\nconst MUTATING_METHODS = {\n statuses: ['create', 'update', 'delete'],\n milestones: ['create', 'update', 'delete'],\n issues: ['create', 'update', 'updateMany', 'clearMilestoneReferences', 'clearParentReferences', 'clearDelegateReferences', 'delete'],\n comments: ['create', 'createOnce', 'delete'],\n runs: ['create', 'update'],\n relations: ['create', 'delete', 'deleteByIssue'],\n contextRefs: ['replace'],\n fieldChanges: ['create'],\n pullRequests: ['link', 'unlink'],\n meta: ['bumpRevision'],\n} satisfies { [TGroup in BoardStoreGroup]: Array<keyof BoardStore[TGroup]> }\n\n/**\n * One board write in progress: the store to work through, and — behind it — the\n * transaction and the change flag the whole write shares.\n *\n * An operation that takes a scope joins the write it belongs to instead of\n * opening one of its own, which is how a card operation that seeds columns, a\n * status line and a comment still counts as a single change.\n */\nexport interface BoardWriteScope<TStore extends BoardStore = BoardStore> {\n /** The transactional store, wrapped so every write through it is noticed. */\n readonly tx: TStore\n}\n\ninterface ChangeTracker {\n changed: boolean\n}\n\n/**\n * The same store, with every mutating method wrapped so that calling it marks\n * the write as having changed something.\n *\n * Groups the board does not own — the `lookups` a host composes beside it — are\n * carried over untouched, which is why the wrapper is built from the store's own\n * keys rather than from a fixed list. The cast restores the host's store type:\n * every property is either the original or a wrapper with the same signature.\n */\nfunction trackChanges<TStore extends BoardStore>(store: TStore, tracker: ChangeTracker): TStore {\n const wrapped = { ...store } as Record<string, unknown>\n\n for (const [group, methods] of Object.entries(MUTATING_METHODS)) {\n const source = store[group as BoardStoreGroup] as unknown as Record<string, (...args: unknown[]) => unknown>\n const replacement: Record<string, unknown> = { ...source }\n for (const method of methods) {\n replacement[method] = (...args: unknown[]) => {\n tracker.changed = true\n return source[method](...args)\n }\n }\n wrapped[group] = replacement\n }\n\n // A transaction opened through the wrapper keeps the callback inside it, so\n // writes made there are noticed too.\n wrapped.transaction = async (fn: (tx: TStore) => Promise<unknown>, options?: { mode?: 'deferred' | 'immediate' }) =>\n await store.transaction(async () => await fn(wrapped as TStore), options)\n\n return wrapped as TStore\n}\n\n/**\n * Run `fn` as one board write: everything it writes commits together, and the\n * board's change counter goes up by exactly one if it wrote anything at all.\n *\n * The counter is what tells a reader — the board UI polling, a CLI, another\n * agent — that the board is not the board it last read. It is bumped from the\n * outermost scope only, after `fn` returns and only when a mutating store method\n * was actually called, so a read-only pass and a transaction that rolls back\n * both leave it untouched. Pass `scope` to join a write that is already open\n * instead of starting a second one.\n *\n * The transaction takes its write lock up front (`immediate`), so two writers\n * racing for the same board file fail fast rather than half-way through. A write\n * that starts while the host already holds a transaction of its own becomes a\n * savepoint inside it — the board counter then commits, or rolls back, with\n * whatever the host is doing.\n *\n * The caller rule of the underlying transaction applies in full: `fn` must be\n * the only database-touching async chain in flight, and inside it only\n * operations that resolve without I/O may be awaited.\n */\nexport async function runBoardWrite<TStore extends BoardStore, TResult>(\n store: TStore,\n fn: (scope: BoardWriteScope<TStore>) => Promise<TResult>,\n scope?: BoardWriteScope<TStore>,\n): Promise<TResult> {\n if (scope) {\n return await fn(scope)\n }\n\n const tracker: ChangeTracker = { changed: false }\n return await store.transaction(async (tx) => {\n // A host that composes its own groups beside the board — an app's server\n // and its `lookups` — hands its own store to the callback; only the type of\n // `transaction` on the constraint says `BoardStore`.\n const result = await fn({ tx: trackChanges(tx as TStore, tracker) })\n if (tracker.changed) {\n await tx.meta.bumpRevision(BOARD_CHANGE_KEY, currentUnixSeconds())\n }\n return result\n }, { mode: 'immediate' })\n}\n\n/**\n * How many times the board has changed. A reader that holds a number and reads\n * the same one back is looking at the board it already has.\n */\nexport async function readChangeSeq(store: BoardStore): Promise<number> {\n return (await store.meta.read(BOARD_CHANGE_KEY))?.revision ?? 0\n}\n","import { z } from 'zod'\n\nimport type { IssueStatus } from '../sqlite/schema'\nimport { BoardError } from './errors'\n\n/**\n * What a column may ask of a card before an agent puts the card in it (ruling\n * 6-1). The set is fixed: each rule is one fact the board can check on its own.\n *\n * - `checklist_complete` — the description holds at least one markdown task\n * item, and every one of them is checked.\n * - `pull_request_linked` — the card names at least one pull request.\n * - `ci_green` — the newest CI verdict across the card's linked pull requests\n * is green.\n * - `approved` — a person's last decision on the card was to approve it.\n */\nexport const ENTRY_RULES = ['checklist_complete', 'pull_request_linked', 'ci_green', 'approved'] as const\n\nexport type EntryRule = typeof ENTRY_RULES[number]\n\n/** Why a rule is unmet, as a key a translated surface can name in its own words. */\nexport const UNMET_ENTRY_RULE_REASONS = [\n 'no_checklist',\n 'unchecked',\n 'no_pull_request',\n 'no_ci',\n 'ci_red',\n 'approval_pending',\n 'returned',\n 'not_approved',\n] as const\n\ntype UnmetEntryRuleReason = typeof UNMET_ENTRY_RULE_REASONS[number]\n\n/**\n * One rule a card does not meet yet: `detail` says what is missing in English\n * words the command line and MCP print; `reason` (with `checked` and `total`\n * for `unchecked`) says the same for a surface that translates it.\n */\nexport interface UnmetEntryRule {\n rule: EntryRule\n reason: UnmetEntryRuleReason\n detail: string\n checked?: number\n total?: number\n}\n\n/** What `evaluateEntryRules` needs to know beyond the card's own fields. */\nexport interface EntryRuleFacts {\n /** How many pull requests the card names. */\n pullRequestCount: number\n /** The newest CI verdict across those pull requests, or `null` while none has arrived. */\n latestCi: 'green' | 'red' | null\n /** Where the card stands with the person who approves it (`BoardApproval['state']`). */\n approval: 'none' | 'pending' | 'approved' | 'returned'\n}\n\nconst entryRulesSchema = z.array(z.enum(ENTRY_RULES))\n\n/**\n * The rules a caller asked for, as the column stores them: known rules only,\n * each once, in `ENTRY_RULES` order, and `null` for none — an empty list and no\n * list are the same column. An unknown rule is `board_entry_rule_invalid`,\n * before anything is written.\n */\nexport function serializeEntryRules(rules: readonly string[] | null): string | null {\n const parsed = entryRulesSchema.safeParse(rules ?? [])\n if (!parsed.success) {\n const invalid = (rules ?? []).filter(rule => !(ENTRY_RULES as readonly string[]).includes(rule))\n throw new BoardError('board_entry_rule_invalid', { rules: invalid, allowed: [...ENTRY_RULES] })\n }\n const chosen = ENTRY_RULES.filter(rule => parsed.data.includes(rule))\n return chosen.length > 0 ? JSON.stringify(chosen) : null\n}\n\n/**\n * The rules a column asks for. A rule this build does not know — written by a\n * newer one — is left out rather than failing the read: the column still\n * enforces everything this build can check.\n */\nexport function readEntryRules(column: Pick<IssueStatus, 'entryRules'>): EntryRule[] {\n if (!column.entryRules) {\n return []\n }\n const stored: string[] = JSON.parse(column.entryRules)\n return ENTRY_RULES.filter(rule => stored.includes(rule))\n}\n\n/** How many markdown task items a description holds, and how many are checked. */\nexport interface ChecklistProgress {\n total: number\n checked: number\n}\n\n/** A list item that is a task: `- [ ]`, `* [x]`, `+ [X]`, `1. [ ]`, at any depth. */\nconst TASK_ITEM = /^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+\\[([ x])\\](?:\\s|$)/i\n/** The line that opens or closes a fenced code block: three or more backticks or tildes. */\nconst FENCE = /^\\s*(`{3,}|~{3,})/\n\n/**\n * Count the task items of a markdown description.\n *\n * Every list item that starts with `[ ]` or `[x]` (either case) counts,\n * nested ones included — a sub-task is still a task. Lines inside a fenced code\n * block (```` ``` ```` or `~~~`) do not: a `- [ ]` there is an example someone\n * quoted, not a box anyone can tick. An indented code block cannot be told\n * apart from a nested list without a full markdown parser, so it counts; fence\n * the example to keep it out.\n */\nexport function readChecklist(description: string | null): ChecklistProgress {\n let total = 0\n let checked = 0\n let fence: string | null = null\n for (const line of (description ?? '').split(/\\r?\\n/)) {\n const marker = FENCE.exec(line)?.[1]\n if (fence) {\n if (marker && marker[0] === fence[0] && marker.length >= fence.length) {\n fence = null\n }\n continue\n }\n if (marker) {\n fence = marker\n continue\n }\n const item = TASK_ITEM.exec(line)\n if (item) {\n total++\n if (item[1] !== ' ') {\n checked++\n }\n }\n }\n return { total, checked }\n}\n\n/**\n * The rules a card does not meet, in the order the column lists them (ruling\n * 6-2). Pure: everything beyond the card's own description arrives in `facts`.\n */\nexport function evaluateEntryRules(\n card: { description: string | null },\n rules: readonly EntryRule[],\n facts: EntryRuleFacts,\n): { unmet: UnmetEntryRule[] } {\n const unmet: UnmetEntryRule[] = []\n for (const rule of rules) {\n const missing = unmetReason(rule, card, facts)\n if (missing) {\n unmet.push({ rule, ...missing })\n }\n }\n return { unmet }\n}\n\n/** Why the card fails `rule`, or `null` when it meets it. */\nfunction unmetReason(\n rule: EntryRule,\n card: { description: string | null },\n facts: EntryRuleFacts,\n): Omit<UnmetEntryRule, 'rule'> | null {\n switch (rule) {\n case 'checklist_complete': {\n const { total, checked } = readChecklist(card.description)\n if (total === 0) {\n return { reason: 'no_checklist', detail: 'no checklist in the description' }\n }\n return checked < total ? { reason: 'unchecked', detail: `${total - checked} of ${total} unchecked`, checked, total } : null\n }\n case 'pull_request_linked':\n return facts.pullRequestCount > 0 ? null : { reason: 'no_pull_request', detail: 'no pull request linked' }\n case 'ci_green':\n if (facts.latestCi === 'green') {\n return null\n }\n return facts.latestCi === 'red' ? { reason: 'ci_red', detail: 'CI is red' } : { reason: 'no_ci', detail: 'no CI result yet' }\n case 'approved':\n switch (facts.approval) {\n case 'approved':\n return null\n case 'pending':\n return { reason: 'approval_pending', detail: 'waiting for a person to approve' }\n case 'returned':\n return { reason: 'returned', detail: 'returned by a person, not approved' }\n case 'none':\n return { reason: 'not_approved', detail: 'not approved by a person' }\n }\n }\n}\n\n/** What `ENTRY_RULES` each mean, in the words the prime text and the manifest use. */\nexport const ENTRY_RULE_DESCRIPTIONS: Record<EntryRule, string> = {\n checklist_complete: 'every checkbox (`- [ ]`) in the card description is checked, and there is at least one',\n pull_request_linked: 'at least one pull request is linked to the card',\n ci_green: 'the newest CI result across the card\\'s linked pull requests is green',\n approved: 'a person has approved the card',\n}\n\n/** What `board_column_rules_unmet` carries in its details. */\nexport interface ColumnRefusalDetails {\n /** The card that was held back; `null` for one that was being created. */\n issueId: string | null\n column: string\n statusId: string\n unmet: UnmetEntryRule[]\n}\n\n/**\n * A refusal at a column's door as a person or an agent reads it: the error code\n * first, so a caller matching on it still can, then one line per unmet rule.\n * The command line and the MCP tools say it in exactly these words.\n */\nexport function describeColumnRefusal(details: ColumnRefusalDetails): string {\n const card = details.issueId ? `${details.issueId} cannot enter` : 'A new card cannot be created in'\n return [\n `board_column_rules_unmet: ${card} \"${details.column}\" yet:`,\n ...details.unmet.map(item => `- ${item.rule}: ${item.detail}`),\n ].join('\\n')\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { serializeEntryRules } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { DEFAULT_STATUSES, normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { IssueStatus, issueStatuses } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR } from './types'\n\n/** What a column is made of, beyond the workspace it belongs to and its position. */\nexport interface BoardColumnInput {\n workspaceId: string\n name: string\n description?: string | null\n color?: string | null\n category?: IssueStatus['category']\n /** What a card must satisfy before an agent may put it here — `ENTRY_RULES`, none by default. */\n entryRules?: readonly string[] | null\n}\n\n/** What `updateColumn` may change about a column. */\nexport interface BoardColumnPatch {\n name?: string\n description?: string | null\n color?: string | null\n /** `ENTRY_RULES`; `null` or an empty list asks for nothing. */\n entryRules?: readonly string[] | null\n}\n\n/** The columns of one workspace, in board order. */\nexport async function listColumns(store: BoardStore, workspaceId: string): Promise<IssueStatus[]> {\n return await store.statuses.listByWorkspace(workspaceId)\n}\n\n/**\n * The column the caller named, by id or by any spelling of its name — `In\n * Progress`, `in-progress` and `IN_PROGRESS` all reach the same one. `null` when\n * the workspace holds no such column.\n */\nexport async function findColumn(store: BoardStore, workspaceId: string, nameOrId: string): Promise<IssueStatus | null> {\n const columns = await listColumns(store, workspaceId)\n const byId = columns.find(column => column.id === nameOrId)\n if (byId) {\n return byId\n }\n const slug = normalizeStatusName(nameOrId)\n if (!slug) {\n throw new BoardError('issue_status_name_empty', { workspaceId })\n }\n return columns.find(column => normalizeStatusName(column.name) === slug) ?? null\n}\n\n/** The column the caller named, or the error that says the board has no such column. */\nexport async function requireColumn(store: BoardStore, workspaceId: string, nameOrId: string): Promise<IssueStatus> {\n const column = await findColumn(store, workspaceId, nameOrId)\n if (!column) {\n throw new BoardError('issue_status_not_found', {\n workspaceId,\n statusName: nameOrId,\n normalizedStatusName: normalizeStatusName(nameOrId),\n })\n }\n return column\n}\n\n/**\n * The columns of the workspace, seeding the standard six the first time someone\n * looks. A board that already holds a column of its own is never seeded: those\n * columns are the user's data, and a workspace that was pruned down to one\n * column meant it.\n */\nexport async function ensureDefaultColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus[]> {\n if (await store.statuses.countByWorkspace(workspaceId) > 0) {\n return await listColumns(store, workspaceId)\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n // Another writer may have seeded the same workspace between the count above\n // and this transaction; the unique index on `(workspace_id, name)` would\n // then refuse the insert, so the count is taken again under the write lock.\n if (await tx.statuses.countByWorkspace(workspaceId) > 0) {\n return await listColumns(tx, workspaceId)\n }\n for (const [order, status] of DEFAULT_STATUSES.entries()) {\n await insertColumn(tx, { workspaceId, ...status }, order)\n }\n return await listColumns(tx, workspaceId)\n }, scope)\n}\n\n/**\n * Only a person sets, changes or clears what a column asks of a card: an agent\n * that could clear a column's rules could walk any card past them.\n */\nfunction assertPersonSetsEntryRules(actor: BoardActor, details: Record<string, unknown>): void {\n if (actor.kind !== 'user') {\n throw new BoardError('board_column_rules_requires_user', { ...details, actorKind: actor.kind })\n }\n}\n\nexport async function createColumn<TStore extends BoardStore>(\n store: TStore,\n input: BoardColumnInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n if (serializeEntryRules(input.entryRules ?? null) !== null) {\n assertPersonSetsEntryRules(actor, { workspaceId: input.workspaceId, name: input.name })\n }\n return await runBoardWrite(store, async ({ tx }) => {\n // New columns land at the end of the board.\n const order = await tx.statuses.countByWorkspace(input.workspaceId)\n return await insertColumn(tx, input, order)\n }, scope)\n}\n\nexport async function updateColumn<TStore extends BoardStore>(\n store: TStore,\n statusId: string,\n patch: BoardColumnPatch,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n // Checked before the write starts: an unknown rule, or an actor who is not a\n // person, changes nothing.\n const entryRules = 'entryRules' in patch ? serializeEntryRules(patch.entryRules ?? null) : undefined\n if (entryRules !== undefined) {\n assertPersonSetsEntryRules(actor, { statusId })\n }\n return await runBoardWrite(store, async ({ tx }) => {\n const updates: Partial<typeof issueStatuses.$inferInsert> = {}\n if (patch.name !== undefined) {\n updates.name = patch.name\n }\n if ('description' in patch) {\n updates.description = patch.description ?? null\n }\n if ('color' in patch) {\n updates.color = patch.color ?? null\n }\n if (entryRules !== undefined) {\n updates.entryRules = entryRules\n }\n if (Object.keys(updates).length > 0) {\n await tx.statuses.update(statusId, updates)\n }\n const column = await tx.statuses.findById(statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n return column\n }, scope)\n}\n\n/**\n * Give a column the one line that tells an agent when a card belongs in it. The\n * column is named the way a person would name it — by id, or by any spelling of\n * its name.\n */\nexport async function describeColumn<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n nameOrId: string,\n description: string | null,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n return await runBoardWrite(store, async ({ tx }) => {\n const column = await requireColumn(tx, workspaceId, nameOrId)\n return await updateColumn(tx, column.id, { description }, BOARD_ACTOR, { tx })\n }, scope)\n}\n\n/**\n * Set what a card must satisfy before an agent may put it in the column\n * (ruling 6-1): `ENTRY_RULES`, or `null` / an empty list for nothing. The column\n * is named the way `describeColumn` names it.\n */\nexport async function setColumnEntryRules<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n nameOrId: string,\n entryRules: readonly string[] | null,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueStatus> {\n serializeEntryRules(entryRules)\n assertPersonSetsEntryRules(actor, { workspaceId, statusName: nameOrId })\n return await runBoardWrite(store, async ({ tx }) => {\n const column = await requireColumn(tx, workspaceId, nameOrId)\n return await updateColumn(tx, column.id, { entryRules }, actor, { tx })\n }, scope)\n}\n\nexport async function deleteColumn<TStore extends BoardStore>(\n store: TStore,\n statusId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n if (!await tx.statuses.findById(statusId)) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n // Cards pointing at the column keep their place on the board and lose their\n // column, the way the schema's `on delete set null` says they should.\n await tx.statuses.delete(statusId)\n }, scope)\n}\n\n/** Put the workspace's columns in the given order; unnamed columns keep their place. */\nexport async function reorderColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n orderedIds: string[],\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n for (const [order, statusId] of orderedIds.entries()) {\n await tx.statuses.update(statusId, { order })\n }\n }, scope)\n}\n\n/**\n * Bring a board that predates the standard set up to it, without touching the\n * columns its owner made.\n *\n * Each missing standard column goes directly after the nearest standard column\n * before it that the board already has — so `To Do` lands behind `Backlog` even\n * when the owner put three columns of their own in between — and at the front\n * when none of them is there. A board that already holds all six is left\n * untouched, down to the board version: running this twice must not look like a\n * change to anyone polling.\n */\nexport async function addStandardColumns<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ statuses: IssueStatus[], added: string[] }> {\n const present = await listColumns(store, workspaceId)\n const presentSlugs = new Set(present.map(column => normalizeStatusName(column.name)))\n const missing = DEFAULT_STATUSES.filter(status => !presentSlugs.has(normalizeStatusName(status.name)))\n if (missing.length === 0) {\n return { statuses: present, added: [] }\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n // The board as it will look, standard columns included, before anything is\n // written: inserting into this list is what decides the final order.\n const planned: Array<{ column: IssueStatus } | { standard: typeof DEFAULT_STATUSES[number] }> = present\n .map(column => ({ column }))\n\n for (const standard of missing) {\n planned.splice(placeAfterPrecedingStandard(planned, standard), 0, { standard })\n }\n\n const added: string[] = []\n for (const [order, entry] of planned.entries()) {\n if ('standard' in entry) {\n await insertColumn(tx, { workspaceId, ...entry.standard }, order)\n added.push(entry.standard.name)\n continue\n }\n if (entry.column.order !== order) {\n await tx.statuses.update(entry.column.id, { order })\n }\n }\n\n return { statuses: await listColumns(tx, workspaceId), added }\n }, scope)\n}\n\n/** Where a missing standard column goes: right after the last standard column before it. */\nfunction placeAfterPrecedingStandard(\n planned: Array<{ column: IssueStatus } | { standard: typeof DEFAULT_STATUSES[number] }>,\n standard: typeof DEFAULT_STATUSES[number],\n): number {\n const precedingSlugs = DEFAULT_STATUSES\n .slice(0, DEFAULT_STATUSES.indexOf(standard))\n .map(preceding => normalizeStatusName(preceding.name))\n\n for (const slug of precedingSlugs.toReversed()) {\n const index = planned.findIndex(entry => normalizeStatusName(\n 'standard' in entry ? entry.standard.name : entry.column.name,\n ) === slug)\n if (index >= 0) {\n return index + 1\n }\n }\n return 0\n}\n\nasync function insertColumn(\n store: BoardStore,\n input: BoardColumnInput,\n order: number,\n): Promise<IssueStatus> {\n return await store.statuses.create({\n id: randomUUID(),\n workspaceId: input.workspaceId,\n name: input.name,\n description: input.description ?? null,\n color: input.color ?? null,\n category: input.category ?? 'unstarted',\n entryRules: serializeEntryRules(input.entryRules ?? null),\n order,\n createdAt: currentUnixSeconds(),\n })\n}\n","/**\n * The dedupe keys the facts about one pull request are written under on a card.\n *\n * Whatever watches a pull request writes them — opened, merged, closed and a CI\n * verdict per head — and whatever reads a card's pull requests, this package's\n * `ci_green` entry rule included, reads them back (rulings 4-7, 6-2), so the spelling lives here,\n * with the board, rather than being copied into each. Owner and repo are lower-cased\n * because GitHub names are case-insensitive (ruling 4-6): `Acme/Repo#7` and\n * `acme/repo#7` key to the same facts.\n */\nexport interface PullRequestFactKeys {\n opened: string\n merged: string\n closed: string\n /** GitHub answered 404/422 for this link on enough consecutive ticks that the watcher dropped it (`pr-watcher.ts`). */\n gone: string\n /** What every CI verdict key of this pull request starts with; the head sha and `:green` or `:red` follow. */\n ciPrefix: string\n}\n\nexport function pullRequestFactKeys({ owner, repo, number }: { owner: string, repo: string, number: number }): PullRequestFactKeys {\n const key = `${owner.toLowerCase()}/${repo.toLowerCase()}#${number}`\n return {\n opened: `pr-opened:${key}`,\n merged: `pr-merged:${key}`,\n closed: `pr-closed:${key}`,\n gone: `pr-gone:${key}`,\n ciPrefix: `ci:${key}@`,\n }\n}\n","import type { BoardStore } from '../board-store'\nimport type { EntryRule, EntryRuleFacts, UnmetEntryRule } from '../domain/entry-rules'\nimport { evaluateEntryRules, readEntryRules } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { pullRequestFactKeys } from '../domain/pull-request-fact-keys'\nimport type { IssueStatus } from '../sqlite/schema'\nimport { readApproval } from './approval'\nimport type { BoardActor } from './types'\n\n/**\n * A card about to enter a column: its id — `null` while it is still being\n * created — and its description as it will be once the write lands.\n */\nexport interface EnteringCard {\n id: string | null\n description: string | null\n}\n\n/**\n * The column's entry rules the card does not meet (ruling 6-2). A column with\n * no rules answers at once, without reading anything; otherwise only the facts\n * the column's rules need are read.\n */\nasync function checkColumnEntry(\n store: BoardStore,\n card: EnteringCard,\n column: IssueStatus,\n): Promise<UnmetEntryRule[]> {\n const rules = readEntryRules(column)\n if (rules.length === 0) {\n return []\n }\n return evaluateEntryRules(card, rules, await readEntryRuleFacts(store, card.id, rules)).unmet\n}\n\n/**\n * Hold a card at the door of a column that asks for more than it has (ruling\n * 6-3). Anyone but a person — or a person's own terminal, `placesForPerson`\n * (ruling 6-6) — is refused with `board_column_rules_unmet`, which\n * names the column and every unmet rule; a person is let through, and the\n * unmet rules come back so the caller can warn them.\n */\nexport async function enforceColumnEntry(\n store: BoardStore,\n card: EnteringCard,\n column: IssueStatus,\n actor: BoardActor,\n): Promise<UnmetEntryRule[]> {\n const unmet = await checkColumnEntry(store, card, column)\n if (unmet.length > 0 && actor.kind !== 'user' && !actor.placesForPerson) {\n throw new BoardError('board_column_rules_unmet', {\n issueId: card.id,\n column: column.name,\n statusId: column.id,\n unmet,\n })\n }\n return unmet\n}\n\n/**\n * The same door for several cards entering one column at once — a bulk status\n * change. All or nothing: the first card an agent may not move refuses the\n * whole change. A person gets back the cards that entered with rules unmet.\n */\nexport async function enforceColumnEntryForCards(\n store: BoardStore,\n issueIds: string[],\n statusId: string,\n actor: BoardActor,\n): Promise<Array<{ issueId: string, unmet: UnmetEntryRule[] }>> {\n const column = await store.statuses.findById(statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n const entered: Array<{ issueId: string, unmet: UnmetEntryRule[] }> = []\n for (const card of await store.issues.listByIds(issueIds)) {\n if (card.statusId === column.id) {\n continue\n }\n const unmet = await enforceColumnEntry(store, card, column, actor)\n if (unmet.length > 0) {\n entered.push({ issueId: card.id, unmet })\n }\n }\n return entered\n}\n\n/**\n * What the rules need to know beyond the card's description. A card still\n * being created has no pull request, no CI verdict and no decision yet.\n */\nasync function readEntryRuleFacts(store: BoardStore, issueId: string | null, rules: EntryRule[]): Promise<EntryRuleFacts> {\n if (issueId === null) {\n return { pullRequestCount: 0, latestCi: null, approval: 'none' }\n }\n const links = rules.includes('pull_request_linked') || rules.includes('ci_green')\n ? await store.pullRequests.listByIssue(issueId)\n : []\n return {\n pullRequestCount: links.length,\n latestCi: rules.includes('ci_green') ? await readLatestCi(store, issueId, links) : null,\n approval: rules.includes('approved') ? (await readApproval(store, issueId)).state : 'none',\n }\n}\n\n/**\n * The newest CI verdict across the card's linked pull requests, read off the\n * facts' dedupe keys: `ci:<owner>/<repo>#<number>@<sha>:green|red`, the\n * spelling a pull request watcher writes. A verdict about a commit that is not one\n * of the card's linked pull requests does not count.\n */\nasync function readLatestCi(\n store: BoardStore,\n issueId: string,\n links: Array<{ owner: string, repo: string, number: number }>,\n): Promise<'green' | 'red' | null> {\n if (links.length === 0) {\n return null\n }\n const prefixes = links.map(link => pullRequestFactKeys(link).ciPrefix)\n const newest = (await store.comments.listByDedupeKeyPrefix(issueId, 'ci:'))\n .filter(comment => prefixes.some(prefix => comment.dedupeKey?.startsWith(prefix)))\n .at(-1)\n ?.dedupeKey\n if (newest?.endsWith(':green')) {\n return 'green'\n }\n return newest?.endsWith(':red') ? 'red' : null\n}\n","import { BoardError } from './errors'\n\n/**\n * Where an external agent's own log of a run lives, once it has said (ruling\n * 7-1): which tool wrote it, and the id that tool knows the session by.\n */\nexport interface ExternalSessionRef {\n provider: 'claude' | 'codex'\n /**\n * The tool's own id for the session — a Claude Code session id, or a Codex\n * session id (the one in the rollout's `session_meta`, which Codex prints).\n */\n id: string\n}\n\n/** `claude:<id>` or `codex:<id>`, the id made of anything a filename may hold. */\nconst EXTERNAL_SESSION_REF = /^(claude|codex):([\\w.-]+)$/\n\n/**\n * Read an external session reference out of what an agent passed to\n * `kanbo run start --session` or `kanbo run attach-session`: `claude:<id>` —\n * a Claude Code session id — or `codex:<id>` — a Codex session id. Anything\n * else is refused with `board_run_session_ref_invalid` rather than stored\n * unread, because a ref this build cannot parse back is a transcript nobody\n * can ever open.\n *\n * Doubles as the validator: a caller that only needs to know whether a string\n * is well formed calls this and catches the throw, the same as\n * `parsePullRequestRef`.\n */\nexport function parseExternalSessionRef(input: string): ExternalSessionRef {\n const match = EXTERNAL_SESSION_REF.exec(input.trim())\n if (!match) {\n throw new BoardError('board_run_session_ref_invalid', { value: input })\n }\n return { provider: match[1] as ExternalSessionRef['provider'], id: match[2]! }\n}\n\n/** The stored and printed form: `claude:<id>` or `codex:<id>`. */\nexport function formatExternalSessionRef(ref: ExternalSessionRef): string {\n return `${ref.provider}:${ref.id}`\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardRunProjection, BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { formatExternalSessionRef, parseExternalSessionRef } from '../domain/external-session-ref'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueRun } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR, insertComment, requireCard, toRunLaunchedByKind, writeStatusLine } from './types'\n\n/** What is known about a run when it starts. */\nexport interface StartRunInput {\n agentName: string\n agentId?: string | null\n host?: string | null\n /** Defaults to the mode the card carries. */\n executionMode?: Issue['executionMode']\n /** Defaults to the actor's own kind and id. */\n launchedByKind?: IssueRun['launchedByKind']\n launchedById?: string | null\n chatSessionId?: string | null\n branch?: string | null\n worktreePath?: string | null\n /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n externalSessionRef?: string | null\n}\n\n/** What a run turned out to be running in, once the host knows. */\nexport interface AttachRunExecutionInput {\n chatSessionId?: string | null\n branch?: string | null\n worktreePath?: string | null\n /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n externalSessionRef?: string | null\n}\n\n/**\n * Read and normalize an `externalSessionRef` a caller passed in — `undefined`\n * when the input named nothing, `null` when it named nothing on purpose, and\n * the canonical `claude:<id>` / `codex:<id>` form otherwise. Checked before the\n * write starts, the same as an entry rule: a ref this build cannot parse back\n * changes nothing on the board.\n */\nfunction readExternalSessionRef(value: string | null | undefined): string | null | undefined {\n if (value === undefined || value === null) {\n return value\n }\n return formatExternalSessionRef(parseExternalSessionRef(value))\n}\n\n/** How a run ended, and — for a failure or a stop — what to say about it. */\nexport interface FinishRunInput {\n state: 'finished' | 'failed' | 'stopped'\n errorText?: string | null\n /**\n * Why a `stopped` run ended, when the answer is neither a person pressing\n * stop nor the app that launched it tidying up quietly: the comment then reads\n * `Run stopped: <reason>` instead of `Run stopped`.\n */\n reason?: string\n /**\n * What the card says it is doing once that stop is recorded, whoever the\n * actor is. A stop with no line of its own leaves the rule as it was: the\n * person who pressed stop is named, and anything else says nothing.\n */\n statusLine?: string\n}\n\n/** A run with the attempt number it is, counting from the card's first launch. */\nexport type BoardRunView = IssueRun & { attempt: number }\n\n/** The status line written when a run fails with nothing to show for it. */\nexport const NO_REPORT_STATUS_LINE = 'agent exited with an error, no report'\n\n/** What a launch writes as the card's status line, ahead of the agent's own name. */\nexport const LAUNCHED_STATUS_LINE_PREFIX = 'launched '\n\n/** What a launch writes as the system comment, ahead of the agent, attempt and mode. */\nexport const RUN_STARTED_COMMENT_PREFIX = 'Run started: '\n\n/** The system comment a run that finished cleanly writes. */\nexport const RUN_FINISHED_COMMENT = 'Run finished'\n\n/** The system comment a failed run writes when it names no error text. */\nexport const RUN_FAILED_COMMENT = 'Run failed'\n\n/** What a failed run writes as the system comment, ahead of its own error text. */\nexport const RUN_FAILED_COMMENT_PREFIX = 'Run failed: '\n\n/** The system comment a stop writes when a person pressed stop. */\nexport const RUN_STOPPED_BY_YOU_COMMENT = 'Run stopped by you'\n\n/** The system comment a stop writes when nobody in particular pressed it. */\nexport const RUN_STOPPED_COMMENT = 'Run stopped'\n\n/** What a stop with a reason writes as the system comment, ahead of that reason. */\nexport const RUN_STOPPED_REASON_PREFIX = 'Run stopped: '\n\n/** The status line a stop writes when a person pressed stop and named no line of their own. */\nexport const STOPPED_BY_YOU_STATUS_LINE = 'stopped by you'\n\n/**\n * Record that the card is being worked on.\n *\n * The card gets a line saying who is on it and a comment naming the attempt, so\n * the board reads the same whether the agent was launched by an app or from\n * somewhere else entirely. The attempt number is simply how many runs the card\n * has had — a card whose runs were all launched elsewhere still counts them.\n */\nexport async function startRun<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: StartRunInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts: a session ref this build cannot parse\n // back changes nothing.\n const externalSessionRef = readExternalSessionRef(input.externalSessionRef) ?? null\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const attempt = await tx.runs.countByIssue(issueId) + 1\n const executionMode = input.executionMode ?? card.executionMode\n\n const run = await tx.runs.create({\n id: randomUUID(),\n issueId,\n launchedByKind: input.launchedByKind ?? toRunLaunchedByKind(actor),\n launchedById: input.launchedById ?? actor.id ?? null,\n agentId: input.agentId ?? null,\n agentName: input.agentName,\n host: input.host ?? null,\n executionMode,\n branch: input.branch ?? null,\n worktreePath: input.worktreePath ?? null,\n state: 'running',\n chatSessionId: input.chatSessionId ?? null,\n externalSessionRef,\n startedAt: currentUnixSeconds(),\n endedAt: null,\n })\n\n await writeStatusLine(tx, card, `${LAUNCHED_STATUS_LINE_PREFIX}${input.agentName}`, BOARD_ACTOR)\n await insertComment(tx, {\n issueId,\n authorKind: 'system.run',\n content: `${RUN_STARTED_COMMENT_PREFIX}${input.agentName} · attempt ${attempt} · ${executionMode}`,\n })\n\n return run\n }, scope)\n}\n\n/**\n * Fill in what the host only learns after the run is already going — the session\n * driving it, the branch and the worktree it works in. A run that has already\n * ended is history, and history is not edited. The agent's own log is only ever\n * filled, never replaced or cleared, the same as in `recordRunSessionRef`.\n */\nexport async function attachRunExecution<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n input: AttachRunExecutionInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = readExternalSessionRef(input.externalSessionRef)\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.state !== 'running') {\n throw new BoardError('board_run_already_finished', { runId, state: run.state })\n }\n\n const patch: Partial<IssueRun> = {}\n if (input.chatSessionId !== undefined) {\n patch.chatSessionId = input.chatSessionId\n }\n if (input.branch !== undefined) {\n patch.branch = input.branch\n }\n if (input.worktreePath !== undefined) {\n patch.worktreePath = input.worktreePath\n }\n if (externalSessionRef !== undefined && fillRunSessionRef(run, externalSessionRef)) {\n patch.externalSessionRef = externalSessionRef\n }\n if (Object.keys(patch).length > 0) {\n await tx.runs.update(runId, patch)\n }\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Name the log a run wrote, after the fact — the agent saying so with\n * `kanbo run attach-session`, or a person picking it out of the logs on this\n * machine once the run is over (ruling 7-2).\n *\n * Unlike `attachRunExecution`, this is allowed on a run that has ended: which\n * log the run wrote is part of its history, found later, not a change to it.\n * It only fills a run that named no log of its own. The same ref again changes\n * nothing; a different one is refused, because the first word on where the log\n * is may already have had a chat imported from it.\n */\nexport async function recordRunSessionRef<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n ref: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = formatExternalSessionRef(parseExternalSessionRef(ref))\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (!fillRunSessionRef(run, externalSessionRef)) {\n return run\n }\n await tx.runs.update(runId, { externalSessionRef })\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Take back the log a run names — a person saying the one recorded was wrong.\n *\n * The one way past the fill-only rule, and so a person's alone: an agent that\n * could clear or swap a ref could point a card's history at any log it liked.\n * Without `replaceWith` the run names no log afterwards; with it, the run names\n * that one instead. Allowed on a run that has ended, like `recordRunSessionRef`.\n * Whatever chat was imported from the old ref is the host's to forget.\n */\nexport async function clearRunSessionRef<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n actor: BoardActor,\n options: { replaceWith?: string } = {},\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_run_session_ref_requires_user', { runId, actorKind: actor.kind })\n }\n // Checked before the write starts, the same as in `startRun`.\n const externalSessionRef = readExternalSessionRef(options.replaceWith) ?? null\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.externalSessionRef === externalSessionRef) {\n return run\n }\n await tx.runs.update(runId, { externalSessionRef })\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/**\n * Whether naming `ref` as the run's log writes anything — the one rule every\n * path that touches `externalSessionRef` follows (ruling 7-2): the ref is only\n * ever filled. Naming the one the run already carries, or naming none on a run\n * that carries none, changes nothing; replacing or clearing one it carries is\n * refused, because a chat may already have been imported from it.\n */\nfunction fillRunSessionRef(run: IssueRun, ref: string | null): boolean {\n if (run.externalSessionRef === ref) {\n return false\n }\n if (run.externalSessionRef !== null) {\n throw new BoardError('board_run_session_ref_conflict', { runId: run.id, externalSessionRef: run.externalSessionRef })\n }\n return ref !== null\n}\n\n/**\n * End a run, and say on the card what that meant.\n *\n * Finishing is idempotent: a run that already ended comes back untouched, with\n * nothing written. An app that launches agents reaches this from several directions at once — the\n * session settling, a person pressing stop, a card being returned — and the\n * second of them must not double the comments or move the status line again.\n *\n * A completed run leaves the status line alone: whatever the agent last said it\n * was doing is a better final word than anything the app could write. A failure\n * only writes a line when nothing but the board wrote one since the launch,\n * because a card that says nothing after a crash is the one case where the\n * person looking at it learns nothing at all.\n */\nexport async function finishRun<TStore extends BoardStore>(\n store: TStore,\n runId: string,\n input: FinishRunInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRun> {\n return await runBoardWrite(store, async ({ tx }) => {\n const run = await requireRun(tx, runId)\n if (run.state !== 'running') {\n return run\n }\n\n await tx.runs.update(runId, { state: input.state, endedAt: currentUnixSeconds() })\n const card = await requireCard(tx, run.issueId)\n await insertComment(tx, {\n issueId: run.issueId,\n authorKind: 'system.run',\n content: runComment(input, actor),\n })\n\n if (input.state === 'failed' && !await reportedSince(tx, run)) {\n await writeStatusLine(tx, card, NO_REPORT_STATUS_LINE, BOARD_ACTOR)\n }\n if (input.state === 'stopped') {\n const line = stoppedStatusLine(input, actor)\n if (line) {\n await writeStatusLine(tx, card, line, BOARD_ACTOR)\n }\n }\n\n return await requireRun(tx, runId)\n }, scope)\n}\n\n/** End every run of the card that is still going. Returns the runs that were stopped. */\nexport async function stopRunningRuns<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<string[]> {\n return await runBoardWrite(store, async ({ tx }) => {\n const stopped: string[] = []\n for (const run of await tx.runs.listRunning(issueId)) {\n await finishRun(tx, run.id, { state: 'stopped' }, actor, { tx })\n stopped.push(run.id)\n }\n return stopped\n }, scope)\n}\n\n/** Every run of the card, oldest first, each carrying its attempt number. */\nexport async function listRuns(store: BoardStore, issueId: string): Promise<BoardRunView[]> {\n const runs = await store.runs.listByIssue(issueId)\n return runs.map((run, index) => ({ ...run, attempt: index + 1 }))\n}\n\n/** The run driving that chat session, for the facts an app learns session-first. */\nexport async function findRunByChatSessionId(store: BoardStore, chatSessionId: string): Promise<IssueRun | null> {\n return await store.runs.findByChatSessionId(chatSessionId)\n}\n\n/**\n * The run facts of a whole workspace in one statement: cards with no runs are\n * absent, because a sweep of the board only needs to hear about the ones that\n * have been launched.\n */\nexport async function readBoardProjection(store: BoardStore, workspaceId: string): Promise<BoardRunProjection[]> {\n return await store.runs.project({ workspaceId })\n}\n\n/** The run facts of the named cards, one entry per card asked for, in that order. */\nexport async function readBoardProjectionForIssues(store: BoardStore, issueIds: string[]): Promise<BoardRunProjection[]> {\n const found = new Map((await store.runs.project({ issueIds })).map(projection => [projection.issueId, projection]))\n return issueIds.map(issueId => found.get(issueId) ?? emptyProjection(issueId))\n}\n\n/** The run facts of one card, whether or not it has ever been launched. */\nexport async function readBoardProjectionForIssue(store: BoardStore, issueId: string): Promise<BoardRunProjection> {\n return (await readBoardProjectionForIssues(store, [issueId]))[0]\n}\n\nfunction emptyProjection(issueId: string): BoardRunProjection {\n return { issueId, attemptCount: 0, activeRun: null }\n}\n\nasync function requireRun(store: BoardStore, runId: string): Promise<IssueRun> {\n const run = await store.runs.findById(runId)\n if (!run) {\n throw new BoardError('board_run_not_found', { runId })\n }\n return run\n}\n\nfunction runComment(input: FinishRunInput, actor: BoardActor): string {\n if (input.state === 'finished') {\n return RUN_FINISHED_COMMENT\n }\n if (input.state === 'failed') {\n return input.errorText ? `${RUN_FAILED_COMMENT_PREFIX}${input.errorText}` : RUN_FAILED_COMMENT\n }\n if (input.reason) {\n return `${RUN_STOPPED_REASON_PREFIX}${input.reason}`\n }\n return actor.kind === 'user' ? RUN_STOPPED_BY_YOU_COMMENT : RUN_STOPPED_COMMENT\n}\n\n/**\n * What the card says about itself once a stop is recorded, or nothing at all.\n *\n * A stop that names its own line writes it whoever the actor is: the card was\n * showing work going on, and what ended it — the launching app restarting, say — is a fact\n * about the card, not about the person who happened to be holding the button.\n * Without one the old rule stands: only a person pressing stop is named, and\n * the app tidying up leaves the card's last word alone.\n */\nfunction stoppedStatusLine(input: FinishRunInput, actor: BoardActor): string | null {\n if (input.statusLine) {\n return input.statusLine\n }\n return actor.kind === 'user' ? STOPPED_BY_YOU_STATUS_LINE : null\n}\n\n/**\n * Did anything working this card say a word of its own since the run began?\n *\n * Not only the `agent` kind. An agent an app launched writes as `agent`, one a\n * provider target stands in for keeps that kind, and an agent launched from a\n * terminal or through `kanbo mcp` is stored as the system with a name — its\n * line is still the run's own report, and the last thing worth reading on a\n * card that then crashed. The board itself is the one writer that does not\n * count, and `BOARD_ACTOR` is the only one stored as the system with no name at\n * all, so that single pair is what this rules out.\n */\nasync function reportedSince(store: BoardStore, run: IssueRun): Promise<boolean> {\n const lines = await store.fieldChanges.listByIssueField(run.issueId, 'statusLine')\n return lines.some(line => line.createdAt >= run.startedAt\n && !(line.actorKind === 'system' && line.actorId === null))\n}\n","import type { BoardStore } from '../board-store'\nimport type { UnmetEntryRule } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { diffFieldChanges } from '../domain/field-changes'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport { nextIssueIdentity, nextIssueNumber, nextIssueOrder } from '../domain/numbering'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueComment, issues, IssueStatus } from '../sqlite/schema'\nimport { APPROVED_COMMENT } from './approval'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { ensureDefaultColumns, listColumns, requireColumn } from './columns'\nimport { enforceColumnEntry } from './entry-rules'\nimport { readBoardProjectionForIssue, stopRunningRuns } from './runs'\nimport type { BoardActor, BoardCommentInput } from './types'\nimport { BOARD_ACTOR, insertComment, requireCard, toStoredActorKind, writeStatusLine } from './types'\n\n/** Which column a caller means, when it may name either. */\ninterface ColumnReference {\n statusId?: string | null\n statusName?: string | null\n}\n\n/** A new card. The workspace comes in resolved: the board never reads anybody else's tables to find it. */\nexport interface CreateCardInput extends ColumnReference {\n workspace: BoardWorkspaceIdentity\n /** Optional: a card written as a description alone is titled with its own key. */\n title?: string | null\n description?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n parentIssueId?: string | null\n dueDate?: number | null\n assigneeKind?: string | null\n assigneeId?: string | null\n executionMode?: Issue['executionMode']\n}\n\n/** Everything a card's fields can be changed to. */\nexport interface UpdateCardInput extends ColumnReference {\n workspaceId?: string\n title?: string\n description?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n parentIssueId?: string | null\n assigneeKind?: string | null\n assigneeId?: string | null\n dueDate?: number | null\n order?: number\n executionMode?: Issue['executionMode']\n}\n\n/** What a return writes as the card's status line, ahead of the person's own reason. */\nexport const RETURNED_STATUS_LINE_PREFIX = 'returned by you: '\n\n/**\n * A card as a write that may have put it in a column hands it back. `unmetRules`\n * is there only when a person put the card in a column whose entry rules it does\n * not meet (ruling 6-3) — the write went through, and this is what to warn them\n * about. An agent never sees it: it is refused instead.\n */\nexport type BoardCardWrite = Issue & { unmetRules?: UnmetEntryRule[] }\n\n/** A card and the rules it entered its column without — the field only when there are some. */\nfunction withUnmetRules(card: Issue, unmet: UnmetEntryRule[]): BoardCardWrite {\n return unmet.length > 0 ? { ...card, unmetRules: unmet } : card\n}\n\n/** How many times a card creation retries a number another writer took first. */\nconst NUMBERING_ATTEMPTS = 3\n\n/**\n * Put a new card on the board.\n *\n * The key and the number are taken inside the write, because they are read from\n * the board itself: two writers picking `WOR-004` at the same moment is the one\n * collision the unique index cannot be argued out of, and the loser simply takes\n * the next free number instead. A failure that is not about numbering — a\n * milestone that no longer exists, a parent in another workspace — is not\n * retried; it would fail the same way three times.\n *\n * A card needs no title: one created without it (or with a blank one) is titled\n * with its key, so a person or an agent can write only what the task is.\n */\nexport async function createCard<TStore extends BoardStore>(\n store: TStore,\n input: CreateCardInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n const workspaceId = input.workspace.id\n\n return await runBoardWrite(store, async ({ tx }) => {\n const statusId = await resolveColumnReference(tx, workspaceId, input, { useDefaultWhenMissing: true })\n // The column's entry rules hold for a new card as for a moved one: a card\n // being created has no pull request and no decision yet, only the\n // checklist it is written with.\n const column = statusId === null ? null : await tx.statuses.findById(statusId)\n const unmet = column\n ? await enforceColumnEntry(tx, { id: null, description: input.description ?? null }, column, actor)\n : []\n const milestoneId = await requireMilestoneInWorkspace(tx, workspaceId, input.milestoneId ?? null)\n // The parent is checked before the key is generated: a key the board has not\n // handed out yet cannot be its own parent, so only the workspace matters.\n const parentIssueId = await requireParentInWorkspace(tx, workspaceId, input.parentIssueId ?? null)\n const order = await nextIssueOrder(workspaceId, tx)\n const now = currentUnixSeconds()\n\n for (let attempt = 1; ; attempt++) {\n const identity = await nextIssueIdentity(input.workspace, tx)\n try {\n return withUnmetRules(await tx.issues.create({\n id: identity.id,\n workspaceId,\n title: input.title?.trim() || identity.id,\n description: input.description ?? null,\n priority: input.priority ?? 'none',\n labels: JSON.stringify(input.labels ?? []),\n milestoneId,\n parentIssueId,\n statusId,\n number: identity.number,\n assigneeKind: input.assigneeKind ?? null,\n assigneeId: input.assigneeId ?? null,\n dueDate: input.dueDate ?? null,\n createdByKind: toStoredActorKind(actor),\n createdById: actor.id ?? '__self__',\n sourceChatSessionId: actor.chatSessionId ?? null,\n delegateAgentId: null,\n delegateProviderTargetId: null,\n contextRefs: '[]',\n executionMode: input.executionMode ?? 'worktree',\n order,\n createdAt: now,\n updatedAt: now,\n }), unmet)\n }\n catch (error) {\n // The board, not the driver, says whether this was a numbering\n // collision: the key or the number is now taken by a row that is not\n // this one. Anything else is a real failure and belongs to the caller.\n const numberTaken = await tx.issues.findByNumber(workspaceId, identity.number)\n const keyTaken = await tx.issues.findById(identity.id)\n if (attempt >= NUMBERING_ATTEMPTS || (!numberTaken && !keyTaken)) {\n throw error\n }\n }\n }\n }, scope)\n}\n\n/** Put a new card under an existing one, in the parent's workspace. */\nexport async function createSubCard<TStore extends BoardStore>(\n store: TStore,\n parentIssueId: string,\n input: Omit<CreateCardInput, 'parentIssueId'>,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const parent = await requireCard(tx, parentIssueId)\n return await createCard(tx, { ...input, parentIssueId: parent.id }, actor, { tx })\n }, scope)\n}\n\n/**\n * Change a card's fields, recording every tracked one in its history.\n *\n * Moving a card to another workspace is part of this: the card takes that\n * board's columns, drops a milestone or a parent that does not live there, goes\n * to the end of the board, and takes a new number if its own is already in use.\n * The execution mode is the one field that stops being editable — once a card\n * has been launched, where it runs is a fact about work that already happened.\n *\n * Naming a column the card is not in yet is putting it there, and the column's\n * entry rules are checked against the card as this write leaves it — a\n * description ticked off in the same update counts (ruling 6-3). So is the\n * first column a card lands in because it changed workspace (ruling 6-7): the\n * caller asked for the move, so it is not the board's own doing.\n */\nexport async function updateCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: UpdateCardInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const workspaceId = input.workspaceId ?? card.workspaceId\n const workspaceChanged = workspaceId !== card.workspaceId\n const updates: Partial<typeof issues.$inferInsert> = { updatedAt: currentUnixSeconds() }\n\n if ('workspaceId' in input) {\n updates.workspaceId = workspaceId\n }\n if (workspaceChanged) {\n if (!('statusId' in input) && !('statusName' in input)) {\n updates.statusId = await resolveColumnReference(tx, workspaceId, {}, { useDefaultWhenMissing: true })\n }\n if (!('milestoneId' in input)) {\n updates.milestoneId = card.milestoneId && await tx.milestones.findInWorkspace(workspaceId, card.milestoneId)\n ? card.milestoneId\n : null\n }\n if (!('parentIssueId' in input)) {\n updates.parentIssueId = card.parentIssueId && await tx.issues.findInWorkspace(workspaceId, card.parentIssueId)\n ? card.parentIssueId\n : null\n }\n if (input.order === undefined) {\n updates.order = await nextIssueOrder(workspaceId, tx)\n }\n if (await numberTakenByAnother(tx, workspaceId, card)) {\n updates.number = await nextIssueNumber(workspaceId, tx)\n }\n }\n if (input.title !== undefined) {\n updates.title = input.title\n }\n if ('description' in input) {\n updates.description = input.description ?? null\n }\n if (input.priority !== undefined) {\n updates.priority = input.priority\n }\n if (input.labels !== undefined) {\n updates.labels = JSON.stringify(input.labels)\n }\n if ('milestoneId' in input) {\n updates.milestoneId = await requireMilestoneInWorkspace(tx, workspaceId, input.milestoneId ?? null)\n }\n if ('parentIssueId' in input) {\n updates.parentIssueId = await requireParentInWorkspace(tx, workspaceId, input.parentIssueId ?? null, issueId)\n }\n if ('statusId' in input || 'statusName' in input) {\n updates.statusId = await resolveColumnReference(tx, workspaceId, input, { useDefaultWhenMissing: false })\n }\n // Named, or the first column of the board the card moved to (ruling 6-7):\n // either way the card is entering it.\n let unmet: UnmetEntryRule[] = []\n const column = updates.statusId && updates.statusId !== card.statusId\n ? await tx.statuses.findById(updates.statusId)\n : null\n if (column) {\n const description = 'description' in updates ? updates.description ?? null : card.description\n unmet = await enforceColumnEntry(tx, { id: card.id, description }, column, actor)\n }\n if ('assigneeKind' in input) {\n updates.assigneeKind = input.assigneeKind ?? null\n }\n if ('assigneeId' in input) {\n updates.assigneeId = input.assigneeId ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n if (input.order !== undefined) {\n updates.order = input.order\n }\n if (input.executionMode !== undefined && input.executionMode !== card.executionMode) {\n if ((await readBoardProjectionForIssue(tx, issueId)).attemptCount > 0) {\n throw new BoardError('board_execution_mode_locked', { issueId, executionMode: card.executionMode })\n }\n updates.executionMode = input.executionMode\n }\n\n await recordFieldChanges(tx, card, updates, actor)\n await tx.issues.update(issueId, updates)\n return withUnmetRules(await requireCard(tx, issueId), unmet)\n }, scope)\n}\n\n/**\n * Move a card to the column named by id, by slug or by name — through\n * `updateCard`, so the column's entry rules hold here too.\n */\nexport async function moveCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n nameOrId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BoardCardWrite> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await ensureDefaultColumns(tx, card.workspaceId, { tx })\n const column = await requireColumn(tx, card.workspaceId, nameOrId)\n return await updateCard(tx, issueId, { statusId: column.id }, actor, { tx })\n }, scope)\n}\n\n/** Say what the card is doing right now. */\nexport async function setStatusLine<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n statusLine: string | null,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await writeStatusLine(tx, card, statusLine, actor)\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Hand the card to a person and stop working on it. Only a person takes it back\n * — through `approve` or `returnCard` — which is the whole point of the flag.\n */\nexport async function waitApproval<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { statusLine?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n await tx.issues.update(issueId, { waitingFor: 'human', updatedAt: currentUnixSeconds() })\n if (input.statusLine !== undefined) {\n await writeStatusLine(tx, { ...card, waitingFor: 'human' }, input.statusLine, actor)\n }\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Accept the work: the card stops waiting, and the decision is written down.\n *\n * Nothing but a person may do this. On a local board file that is an agreement\n * rather than a lock — the same file is writable by anything that can open it —\n * but the agreement is worth having: an agent that could approve its own work\n * would make the whole waiting state meaningless.\n */\nexport async function approve<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { comment?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_approval_requires_user', { issueId, actorKind: actor.kind })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n await tx.issues.update(issueId, { waitingFor: null, updatedAt: currentUnixSeconds() })\n const comment = input.comment?.trim()\n await insertComment(tx, {\n issueId,\n authorKind: 'system.approved',\n content: comment ? `${APPROVED_COMMENT}\\n${comment}` : APPROVED_COMMENT,\n authorId: actor.id,\n sourceChatSessionId: actor.chatSessionId,\n })\n return await requireCard(tx, issueId)\n }, scope)\n}\n\n/**\n * Send the card back a column with a reason.\n *\n * The reason is the point: it goes on the card as the person's own comment and\n * as the status line, so the next agent to pick the card up reads why it came\n * back before anything else. Whatever was running on the card is stopped in the\n * same write — a card sitting a column earlier while an agent still works on it\n * is the state the board must never be left in.\n *\n * This is a person's, exactly as `approve` is. They are the two answers to a\n * card that is waiting for a human, and an agent that could give either of them\n * could undo the one state the board promises a person owns — the line it\n * leaves even says \"returned by you\".\n */\nexport async function returnCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n input: { comment: string, toStatusName?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ card: Issue, stoppedRunIds: string[] }> {\n return await runBoardWrite(store, async ({ tx }) => {\n await ensureDefaultColumns(tx, (await requireCard(tx, issueId)).workspaceId, { tx })\n const target = await assertReturnable(tx, issueId, input, actor)\n const card = await requireCard(tx, issueId)\n\n const updates = { statusId: target.id, waitingFor: null, updatedAt: currentUnixSeconds() } as const\n await recordFieldChanges(tx, card, updates, actor)\n await tx.issues.update(issueId, updates)\n await insertComment(tx, {\n issueId,\n authorKind: 'system.returned',\n content: input.comment,\n authorId: actor.id,\n sourceChatSessionId: actor.chatSessionId,\n })\n\n // The board stops the runs on its own behalf: the person is returning the\n // card, not pressing stop, and the line below is the one they should read.\n const stoppedRunIds = await stopRunningRuns(tx, issueId, BOARD_ACTOR, { tx })\n await writeStatusLine(tx, await requireCard(tx, issueId), `${RETURNED_STATUS_LINE_PREFIX}${input.comment}`, actor)\n\n return { card: await requireCard(tx, issueId), stoppedRunIds }\n }, scope)\n}\n\n/**\n * Every reason `returnCard` would refuse, asked on reads alone, and the column\n * the card would go back to when none applies.\n *\n * `returnCard` asks exactly this inside its write. A caller that has to undo\n * something of its own before the return — stop listening for the approval the\n * card is waiting on, say — asks it first, so a return the board refuses leaves\n * that untouched. The refusals are the same `BoardError` codes: an actor who is\n * not a person (`board_return_requires_user`), a card that is not there\n * (`issue_not_found`), a named column the workspace does not hold\n * (`issue_status_not_found`), and a card already in the first column with no\n * column named (`board_return_no_previous_column`).\n */\nexport async function assertReturnable(\n store: BoardStore,\n issueId: string,\n input: { toStatusName?: string | null },\n actor: BoardActor,\n): Promise<IssueStatus> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_return_requires_user', { issueId, actorKind: actor.kind })\n }\n\n const card = await requireCard(store, issueId)\n return input.toStatusName\n ? await requireColumn(store, card.workspaceId, input.toStatusName)\n : previousColumn(await listColumns(store, card.workspaceId), card)\n}\n\n/** A comment a person or an agent writes; the author defaults to the actor. */\nexport interface AddCommentInput extends Omit<BoardCommentInput, 'authorKind' | 'dedupeKey'> {\n authorKind?: BoardCommentInput['authorKind']\n}\n\n/** A fact the board records once, however many times it is told. */\nexport interface CreateCommentOnceInput {\n issueId: string\n authorKind: BoardCommentInput['authorKind']\n content: string\n dedupeKey: string\n actor: BoardActor\n}\n\n/** Write a comment on the card, as the actor unless the caller names another author. */\nexport async function addComment<TStore extends BoardStore>(\n store: TStore,\n input: AddCommentInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueComment> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.issueId)\n const authorKind = input.authorKind ?? toStoredActorKind(actor)\n return await insertComment(tx, {\n ...input,\n authorKind,\n authorId: input.authorId !== undefined ? input.authorId : actor.id,\n sourceChatSessionId: input.sourceChatSessionId !== undefined ? input.sourceChatSessionId : actor.chatSessionId,\n })\n }, scope)\n}\n\n/**\n * Write a fact on the card, once. A fact — a pull request opened, a check that\n * went green — is reported by whatever noticed it, as often as it notices;\n * `dedupeKey` is what keeps the card from saying it twice.\n */\nexport async function createCommentOnce<TStore extends BoardStore>(\n store: TStore,\n input: CreateCommentOnceInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueComment> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.issueId)\n return await insertComment(tx, {\n issueId: input.issueId,\n authorKind: input.authorKind,\n content: input.content,\n dedupeKey: input.dedupeKey,\n authorId: input.actor.id,\n sourceChatSessionId: input.actor.chatSessionId,\n })\n }, scope)\n}\n\n/** The card's comments, oldest first. */\nexport async function listComments(store: BoardStore, issueId: string): Promise<IssueComment[]> {\n await requireCard(store, issueId)\n return await store.comments.listByIssue(issueId)\n}\n\n/**\n * Delete a comment. A comment the board wrote — a decision (`system.*`) or a\n * fact it keeps once (`dedupeKey`, CI among them) — is a person's to delete:\n * the board reads the newest of them, so an agent deleting one could revive an\n * older approval or an older green CI run.\n */\nexport async function deleteComment<TStore extends BoardStore>(\n store: TStore,\n commentId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n const comment = await tx.comments.findById(commentId)\n if (!comment) {\n throw new BoardError('issue_comment_not_found', { commentId })\n }\n if (actor.kind !== 'user' && (comment.authorKind.startsWith('system.') || comment.dedupeKey !== null)) {\n throw new BoardError('issue_comment_delete_requires_user', { commentId, actorKind: toStoredActorKind(actor) })\n }\n await tx.comments.delete(commentId)\n }, scope)\n}\n\n/**\n * Take a card off the board, and hand back the ids of the runs that went with\n * it — a host that keeps rows of its own naming those runs lets go of them.\n *\n * Its children stay on the board without a parent, and every edge touching it\n * goes in both directions: an edge is not a child, and a counterpart left\n * holding half of one would render a relation to nothing.\n */\nexport async function deleteCard<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<{ runIds: string[] }> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const runIds = (await tx.runs.listByIssue(issueId)).map(run => run.id)\n await tx.issues.clearParentReferences(issueId, currentUnixSeconds())\n await tx.relations.deleteByIssue(issueId)\n await tx.issues.delete(issueId)\n return { runIds }\n }, scope)\n}\n\n/**\n * Cards whose key, number, title or description carry the text, ignoring case,\n * newest first and at most `limit` of them.\n *\n * Newest first is the order a host merging several boards' answers relies on:\n * each board's answer is already sorted on `createdAt`, so a stable merge on it\n * and a second cut to `limit` give the answer one board holding every card would.\n */\nexport async function searchCards(store: BoardStore, query: string, limit: number): Promise<Issue[]> {\n const needle = query.toLowerCase()\n return (await store.issues.listNewestFirst())\n .filter(card => [card.id, String(card.number), card.title, card.description ?? ''].join(' ').toLowerCase().includes(needle))\n .slice(0, limit)\n}\n\n/** The column before the card's own, or the error that says there is none. */\nfunction previousColumn(columns: IssueStatus[], card: Issue) {\n const index = columns.findIndex(column => column.id === card.statusId)\n const previous = index > 0 ? columns[index - 1] : undefined\n if (!previous) {\n throw new BoardError('board_return_no_previous_column', { issueId: card.id, statusId: card.statusId })\n }\n return previous\n}\n\n/** Append the history rows a patch earns, one per tracked field it actually changes. */\nexport async function recordFieldChanges(\n store: BoardStore,\n card: Issue,\n updates: Partial<typeof issues.$inferInsert>,\n actor: BoardActor,\n): Promise<void> {\n const rows = diffFieldChanges(card, updates, {\n kind: toStoredActorKind(actor),\n id: actor.id,\n sourceChatSessionId: actor.chatSessionId ?? null,\n }, currentUnixSeconds())\n for (const row of rows) {\n await store.fieldChanges.create(row)\n }\n}\n\n/**\n * The column a caller named, seeding the standard columns first so a board that\n * has never been opened still has somewhere to put the card. Naming both an id\n * and a name is a caller mistake, not a preference to resolve.\n */\nasync function resolveColumnReference(\n store: BoardStore,\n workspaceId: string,\n reference: ColumnReference,\n options: { useDefaultWhenMissing: boolean },\n): Promise<string | null> {\n if (reference.statusId != null && reference.statusName != null) {\n throw new BoardError('issue_status_reference_conflict', {\n statusId: reference.statusId,\n statusName: reference.statusName,\n })\n }\n if (reference.statusId == null && reference.statusName == null && !options.useDefaultWhenMissing) {\n return null\n }\n\n const columns = await ensureDefaultColumns(store, workspaceId, { tx: store })\n\n // A caller that named an id is answered about that id, and one that named a\n // name about that name: the failure has to hand back what was actually sent,\n // or the client is told it asked something it did not ask.\n if (reference.statusId != null) {\n const column = columns.find(candidate => candidate.id === reference.statusId)\n if (!column) {\n throw new BoardError('issue_status_not_found', { workspaceId, statusId: reference.statusId })\n }\n return column.id\n }\n\n if (reference.statusName != null) {\n const slug = normalizeStatusName(reference.statusName)\n if (!slug) {\n throw new BoardError('issue_status_name_empty', { workspaceId })\n }\n const column = columns.find(candidate => normalizeStatusName(candidate.name) === slug)\n if (!column) {\n throw new BoardError('issue_status_not_found', {\n workspaceId,\n statusName: reference.statusName,\n normalizedStatusName: slug,\n })\n }\n return column.id\n }\n\n return columns[0]?.id ?? null\n}\n\n/** The milestone, when it belongs to the workspace, or the error that says it does not. */\nexport async function requireMilestoneInWorkspace(\n store: BoardStore,\n workspaceId: string,\n milestoneId: string | null,\n): Promise<string | null> {\n if (milestoneId === null) {\n return null\n }\n const milestone = await store.milestones.findInWorkspace(workspaceId, milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { workspaceId, milestoneId })\n }\n return milestone.id\n}\n\nasync function requireParentInWorkspace(\n store: BoardStore,\n workspaceId: string,\n parentIssueId: string | null,\n issueId?: string,\n): Promise<string | null> {\n if (parentIssueId === null) {\n return null\n }\n if (parentIssueId === issueId) {\n throw new BoardError('issue_parent_self_reference', { issueId, parentIssueId })\n }\n if (!await store.issues.findInWorkspace(workspaceId, parentIssueId)) {\n throw new BoardError('issue_parent_not_found', { workspaceId, parentIssueId })\n }\n return parentIssueId\n}\n\n/** Is the card's own number already taken by another card in the target workspace? */\nexport async function numberTakenByAnother(store: BoardStore, workspaceId: string, card: Issue): Promise<boolean> {\n const holder = await store.issues.findByNumber(workspaceId, card.number)\n return holder !== null && holder.id !== card.id\n}\n","import type { BoardStore } from '../board-store'\nimport type { UnmetEntryRule } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, issues } from '../sqlite/schema'\nimport { recordFieldChanges, requireMilestoneInWorkspace } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { enforceColumnEntryForCards } from './entry-rules'\nimport type { BoardActor } from './types'\n\n/** The fields one change can set on many cards at once. */\nexport interface BulkUpdateCardsInput {\n statusId?: string | null\n priority?: Issue['priority']\n labels?: string[]\n milestoneId?: string | null\n assigneeKind?: string | null\n assigneeId?: string | null\n dueDate?: number | null\n}\n\n/** What a bulk change did: how many cards it wrote, and — for a person — which of them entered a column short of its rules. */\nexport interface BulkUpdateCardsResult {\n updated: number\n unmetRules?: Array<{ issueId: string, unmet: UnmetEntryRule[] }>\n}\n\n/** How far apart a reorder, or a move onto another board, spaces the cards it places. */\nexport const CARD_ORDER_GAP = 1024\n\n/**\n * Apply one change to many cards, in one write, each card's history recording\n * what changed on it.\n *\n * Every card is checked before anything is written: a column or a milestone\n * must belong to each card's own workspace, and the column's door holds for\n * every card at once — an agent moving one card that does not qualify moves\n * none (ruling 6-3). An id the board does not hold is skipped, not refused.\n */\nexport async function bulkUpdateCards<TStore extends BoardStore>(\n store: TStore,\n issueIds: string[],\n input: BulkUpdateCardsInput,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<BulkUpdateCardsResult> {\n if (issueIds.length === 0) {\n return { updated: 0 }\n }\n const uniqueIssueIds = [...new Set(issueIds)]\n const updates: Partial<typeof issues.$inferInsert> = { updatedAt: currentUnixSeconds() }\n if ('statusId' in input) {\n updates.statusId = input.statusId ?? null\n }\n if (input.priority !== undefined) {\n updates.priority = input.priority\n }\n if (input.labels !== undefined) {\n updates.labels = JSON.stringify(input.labels)\n }\n if ('milestoneId' in input) {\n updates.milestoneId = input.milestoneId ?? null\n }\n if ('assigneeKind' in input) {\n updates.assigneeKind = input.assigneeKind ?? null\n }\n if ('assigneeId' in input) {\n updates.assigneeId = input.assigneeId ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n const cards = await tx.issues.listByIds(uniqueIssueIds)\n for (const card of cards) {\n if (input.statusId != null && !await tx.statuses.findInWorkspace(card.workspaceId, input.statusId)) {\n throw new BoardError('issue_status_not_found', { workspaceId: card.workspaceId, statusId: input.statusId })\n }\n if ('milestoneId' in input) {\n await requireMilestoneInWorkspace(tx, card.workspaceId, input.milestoneId ?? null)\n }\n }\n const unmetRules = input.statusId\n ? await enforceColumnEntryForCards(tx, uniqueIssueIds, input.statusId, actor)\n : []\n for (const card of cards) {\n await recordFieldChanges(tx, card, updates, actor)\n }\n\n const updated = await tx.issues.updateMany(uniqueIssueIds, updates)\n return unmetRules.length > 0 ? { updated, unmetRules } : { updated }\n }, scope)\n}\n\n/**\n * Give the named cards of one workspace their positions in the order named.\n *\n * Positions are spaced by a gap rather than packed, so a card dropped between\n * two others later can take a midpoint without moving its neighbours. The whole\n * order is one write — a drag that half-applied would leave a board no client\n * could reconcile — and a card that is gone or belongs to another workspace is\n * skipped rather than failing the rest of the drop.\n */\nexport async function reorderCards<TStore extends BoardStore>(\n store: TStore,\n workspaceId: string,\n orderedIds: string[],\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n for (const [index, issueId] of orderedIds.entries()) {\n const card = await tx.issues.findById(issueId)\n if (!card || card.workspaceId !== workspaceId) {\n continue\n }\n await tx.issues.update(issueId, { order: (index + 1) * CARD_ORDER_GAP, updatedAt: currentUnixSeconds() })\n }\n }, scope)\n}\n","import { z } from 'zod'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue } from '../sqlite/schema'\nimport { recordFieldChanges } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { requireCard } from './types'\n\nconst ContextRefsJsonSchema = z.string()\n .transform(raw => JSON.parse(raw))\n .pipe(z.array(z.string()))\n\n/** Add a context ref at the end of the card's list, recording the change in its history. */\nexport async function addContextRef<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n ref: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const refs = ContextRefsJsonSchema.parse(card.contextRefs)\n refs.push(ref)\n return await replaceContextRefs(tx, card, refs, actor)\n }, scope)\n}\n\n/** Remove the card's context ref at `index`, or say there is none there. */\nexport async function removeContextRef<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n index: number,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<Issue> {\n return await runBoardWrite(store, async ({ tx }) => {\n const card = await requireCard(tx, issueId)\n const refs = ContextRefsJsonSchema.parse(card.contextRefs)\n if (index < 0 || index >= refs.length) {\n throw new BoardError('issue_context_ref_invalid_index', { issueId, index })\n }\n refs.splice(index, 1)\n return await replaceContextRefs(tx, card, refs, actor)\n }, scope)\n}\n\nasync function replaceContextRefs(store: BoardStore, card: Issue, refs: string[], actor: BoardActor): Promise<Issue> {\n const updates = { contextRefs: JSON.stringify(refs), updatedAt: currentUnixSeconds() }\n await recordFieldChanges(store, card, updates, actor)\n await store.contextRefs.replace(card.id, updates.contextRefs, updates.updatedAt)\n return await requireCard(store, card.id)\n}\n","import type { BoardStore } from '../board-store'\nimport { nextIssueNumber, nextIssueOrder } from '../domain/numbering'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { issues, IssueStatus } from '../sqlite/schema'\nimport { CARD_ORDER_GAP } from './card-batch'\nimport { numberTakenByAnother, recordFieldChanges } from './cards'\nimport { runBoardWrite } from './change-seq'\nimport { BOARD_ACTOR } from './types'\n\n/** Which workspace's cards move where, and how their columns and milestones are matched up. */\nexport interface MigrateCardsInput {\n sourceId: string\n targetId: string\n /**\n * Both workspaces' columns, in board order. The host reads them from each\n * workspace's own board, which is not always the board the cards are moved on:\n * a preview of a move across two boards reads the target's columns from the\n * target while the cards are still on the source.\n */\n sourceStatuses: IssueStatus[]\n targetStatuses: IssueStatus[]\n /** A source column name → the target column name it becomes, when the names differ. */\n statusMappings?: Record<string, string>\n /** A source milestone title → the target milestone title it becomes, when the titles differ. */\n milestoneMappings?: Record<string, string>\n /** Count what would happen, and write nothing. */\n dryRun?: boolean\n}\n\n/** What a move of every card from one workspace to another did — or, for a dry run, would do. */\nexport interface MigrateCardsResult {\n processed: number\n updated: number\n numbersReassigned: number\n statusesMapped: { from: string, to: string }[]\n milestonesMapped: { from: string, to: string | null }[]\n parentIssuesCleared: number\n}\n\n/**\n * Move every card of one workspace to another on this board, in one write.\n *\n * Each source column maps to the target column of the same name (or the name\n * `statusMappings` gives it), and to the target's first column when there is\n * none; each milestone maps by title the same way, and to no milestone when\n * there is none. A card whose number the target already uses takes the next\n * free one, the moved cards go to the end of the target board in their own\n * order, and a parent that is in neither workspace is let go of. Every changed\n * field is recorded in the card's history under the board's own actor.\n *\n * One write, because a half-moved board leaves cards split across both\n * workspaces with renumbering already spent on some of them.\n */\nexport async function migrateCards(store: BoardStore, input: MigrateCardsInput): Promise<MigrateCardsResult> {\n const { sourceId, targetId } = input\n const targetStatusByName = new Map(input.targetStatuses.map(status => [normalizeStatusName(status.name), status]))\n const defaultTargetStatusId = input.targetStatuses[0]?.id ?? null\n\n const statusIdMap = new Map<string, string | null>()\n const statusesMapped: MigrateCardsResult['statusesMapped'] = []\n for (const source of input.sourceStatuses) {\n const target = targetStatusByName.get(normalizeStatusName(input.statusMappings?.[source.name] ?? source.name))\n statusIdMap.set(source.id, target ? target.id : defaultTargetStatusId)\n statusesMapped.push({ from: source.name, to: target ? target.name : input.targetStatuses[0]?.name ?? '(none)' })\n }\n\n const targetMilestoneByTitle = new Map((await store.milestones.listByWorkspace(targetId))\n .map(milestone => [milestone.title.trim().toLowerCase(), milestone]))\n const milestoneIdMap = new Map<string, string | null>()\n const milestonesMapped: MigrateCardsResult['milestonesMapped'] = []\n for (const source of await store.milestones.listByWorkspace(sourceId)) {\n const target = targetMilestoneByTitle.get((input.milestoneMappings?.[source.title] ?? source.title).trim().toLowerCase())\n milestoneIdMap.set(source.id, target ? target.id : null)\n milestonesMapped.push({ from: source.title, to: target ? target.title : null })\n }\n\n const cards = await store.issues.listByWorkspace(sourceId)\n let numbersReassigned = 0\n let parentIssuesCleared = 0\n\n if (input.dryRun) {\n for (const card of cards) {\n if (await numberTakenByAnother(store, targetId, card)) {\n numbersReassigned++\n }\n if (card.parentIssueId) {\n const parent = await store.issues.findById(card.parentIssueId)\n if (parent && parent.workspaceId !== sourceId && parent.workspaceId !== targetId) {\n parentIssuesCleared++\n }\n }\n }\n return { processed: cards.length, updated: 0, numbersReassigned, statusesMapped, milestonesMapped, parentIssuesCleared }\n }\n\n await runBoardWrite(store, async ({ tx }) => {\n let order = await nextIssueOrder(targetId, tx)\n for (const card of cards) {\n const updates: Partial<typeof issues.$inferInsert> = {\n workspaceId: targetId,\n updatedAt: currentUnixSeconds(),\n statusId: card.statusId ? (statusIdMap.get(card.statusId) ?? defaultTargetStatusId) : defaultTargetStatusId,\n }\n if (card.milestoneId) {\n updates.milestoneId = milestoneIdMap.get(card.milestoneId) ?? null\n }\n if (await numberTakenByAnother(tx, targetId, card)) {\n updates.number = await nextIssueNumber(targetId, tx)\n numbersReassigned++\n }\n updates.order = order\n order += CARD_ORDER_GAP\n // A parent moving with the card, or already in the target, stays its parent.\n if (card.parentIssueId) {\n const parent = await tx.issues.findById(card.parentIssueId)\n if (parent && parent.workspaceId !== sourceId && parent.workspaceId !== targetId) {\n updates.parentIssueId = null\n parentIssuesCleared++\n }\n }\n\n await recordFieldChanges(tx, card, updates, BOARD_ACTOR)\n await tx.issues.update(card.id, updates)\n }\n })\n\n return { processed: cards.length, updated: cards.length, numbersReassigned, statusesMapped, milestonesMapped, parentIssuesCleared }\n}\n","/**\n * The sentences that tell an agent how to work this board, written once.\n *\n * Four surfaces say them: the text `kanbo prime` returns (`BOARD_RULES`), the\n * block `kanbo init` writes into a project's `CLAUDE.md` / `AGENTS.md`, the\n * `instructions` the `kanbo mcp` server sends when a client connects, and the\n * `kanbo_card_create` tool's description. Each surface adds its own way of\n * doing the thing — a command, a tool name — but the rule itself is read from\n * here, so the four cannot come to say different things.\n *\n * No sentence names a tool or a command: the same text reaches an agent holding\n * MCP tools, one with a command line, and one talking to a server over HTTP.\n */\n\nexport const MOVE_CARD_RULE = 'You move the card between columns yourself, at the moment its real state changes. The board never moves a card for you.'\n\nexport const STATUS_LINE_RULE = 'Write a status line at every step, including before and after anything long-running. One sentence, present tense, about what is happening right now.'\n\nexport const WAIT_FOR_PERSON_RULE = 'When you need a person, mark the card as waiting for approval and end your turn. You will be told when the answer comes.'\n\nexport const PERSON_ONLY_RULE = 'Only a person approves a card, and only a person takes one out of waiting. Never do either yourself.'\n\nexport const EXECUTION_MODE_RULE = 'Each card says where it runs: `worktree` means a separate copy of the project, `main` means the project folder itself. Work in the mode the card names.'\n\n/** When to split a card at all — said on its own where the surface names the parent its own way. */\nexport const SUBTASK_WHEN_SENTENCE = 'Create subtasks only when the person asks for them, or when a task has parts that can be done and checked separately (different stages, owners or pull requests); do not split small work you will finish in one go.'\n\n/** How to split one, with the way this surface names the parent card. */\nexport function subtaskSplitSentence(parentReference: string): string {\n return `When you do split a card, make the parts subtasks of it (${parentReference}), not separate top-level cards.`\n}\n\nexport const SUBTASK_RULE = `${SUBTASK_WHEN_SENTENCE} ${subtaskSplitSentence('the parent card\\'s id')}`\n\nexport const PULL_REQUEST_RULE = 'Opened a pull request? Link it to the card. Its state and CI checks then reach the card as comments on their own — nothing else tells the board a pull request exists.'\n\nexport const OWN_SESSION_RULE = 'Started the work yourself, not launched by an app that tracks the run for you? Say so when you start the run — `claude:<session id>` from Claude Code, `codex:<session id>` from Codex (the session id Codex prints) — so your own log of it can be found later.'\n\n/** Said by the `kanbo init` block and the MCP instructions, which name the comment command or tool beside it. */\nexport const COMMENT_RULE = 'Findings, results, decisions and questions go on the card as comments.'\n\nexport const BOARD_TOOLS_RULE = 'Read the column descriptions above before moving a card, and do all of this through the board tools or commands you have — a statement in your reply changes nothing.'\n\n/**\n * The rules of this board, as `kanbo prime` says them under its column list.\n *\n * They are about the board and nothing else. The board does not hand out a role, a\n * method or a pipeline — that belongs to the instructions the person wrote for\n * their own agent — so the list stops at what the board itself guarantees and\n * expects.\n *\n * Also the source `buildCapabilitiesManifest` reads its `rules` from — one list,\n * read by the prompt an agent sees and by the manifest an orchestrator reads\n * about the board.\n */\nexport const BOARD_RULES = [\n MOVE_CARD_RULE,\n STATUS_LINE_RULE,\n WAIT_FOR_PERSON_RULE,\n PERSON_ONLY_RULE,\n EXECUTION_MODE_RULE,\n SUBTASK_RULE,\n PULL_REQUEST_RULE,\n OWN_SESSION_RULE,\n BOARD_TOOLS_RULE,\n]\n","import type { BoardStore } from '../board-store'\nimport { ENTRY_RULE_DESCRIPTIONS, readEntryRules } from '../domain/entry-rules'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { IssueStatus } from '../sqlite/schema'\nimport { BOARD_RULES } from './agent-rules'\nimport { listColumns } from './columns'\n\n/**\n * What the board tells an agent about itself: the columns it has, what each one\n * means, and how a card is expected to travel across them.\n *\n * The text is the same whether it reaches the agent in a prompt an app builds\n * or through a tool the agent called on its own.\n */\nexport async function buildPrimeText(store: BoardStore, workspaceId: string): Promise<string> {\n const columns = await listColumns(store, workspaceId)\n const ruled = columns.some(column => readEntryRules(column).length > 0)\n const lines = [\n 'Columns of this board, in board order:',\n '',\n ...(columns.length > 0 ? columns.map(describeColumnLine) : ['- (this board has no columns yet)']),\n ...(ruled ? ['', ENTRY_RULES_NOTE] : []),\n '',\n 'How this board works:',\n '',\n ...BOARD_RULES.map(rule => `- ${rule}`),\n ]\n return lines.join('\\n')\n}\n\n/**\n * Said under the columns only when one of them has entry rules, so a board\n * without any reads exactly as it did before they existed.\n */\nconst ENTRY_RULES_NOTE = 'A column that lists requirements refuses a card that does not meet them yet, with the list of what is missing: do that work first, then move the card. A person may still move it.'\n\nfunction describeColumnLine(column: IssueStatus): string {\n const slug = normalizeStatusName(column.name)\n const rules = readEntryRules(column)\n const requires = rules.length > 0\n ? ` Requires: ${rules.map(rule => `${rule} (${ENTRY_RULE_DESCRIPTIONS[rule]})`).join('; ')}.`\n : ''\n return column.description\n ? `- ${column.name} (\\`${slug}\\`) — ${column.description}${requires}`\n : `- ${column.name} (\\`${slug}\\`)${requires ? ` —${requires}` : ''}`\n}\n","import { BoardError } from './errors'\n\n/** A GitHub pull request, as a card names it. */\nexport interface PullRequestRef {\n owner: string\n repo: string\n number: number\n /** The canonical `https://github.com/<owner>/<repo>/pull/<number>`, whatever form it was given in. */\n url: string\n}\n\n/**\n * The largest pull-request number a board can hold — `number` is a 32-bit\n * `integer` in the Postgres dialect.\n */\nconst MAX_PULL_REQUEST_NUMBER = 2_147_483_647\n\n/**\n * A GitHub owner (user or organisation) and a repository name, as GitHub allows\n * them; both patterns are read case-insensitively. An owner may carry `_`: an\n * Enterprise Managed User is `name_shortcode`.\n */\nconst OWNER = '[a-z0-9](?:[\\\\w-]*[a-z0-9])?'\nconst REPO = '[\\\\w.-]+'\n\n/** `https://github.com/<owner>/<repo>/pull/<n>`, with anything GitHub hangs off it — `/files`, a query, an anchor. */\nconst PULL_REQUEST_URL = new RegExp(`^https://(?:www\\\\.)?github\\\\.com/(${OWNER})/(${REPO})/pull/(\\\\d+)(?:[/?#].*)?$`, 'i')\n\n/** `<owner>/<repo>#<n>`, the short form GitHub itself writes. */\nconst PULL_REQUEST_SHORT_REF = new RegExp(`^(${OWNER})/(${REPO})#(\\\\d+)$`, 'i')\n\n/**\n * Read a pull request out of what a person or an agent typed: a GitHub pull\n * request URL or `owner/repo#number`. Anything else — an issue URL, a commit, a\n * host that is not GitHub, a bare number with no repository — is refused with\n * `board_pull_request_invalid` rather than guessed at, because a link the\n * watcher cannot resolve is a card that never hears about its pull request.\n *\n * GitHub owner and repository names are case-insensitive, so both are\n * lower-cased before they become a stored link or a dedupe key (ruling 4-6) —\n * `Acme/Repo#7` and `acme/repo#7` are the same pull request and must never\n * become two links or two \"opened\" lines.\n */\nexport function parsePullRequestRef(input: string): PullRequestRef {\n const value = input.trim()\n const match = PULL_REQUEST_URL.exec(value) ?? PULL_REQUEST_SHORT_REF.exec(value)\n const number = match ? Number(match[3]) : Number.NaN\n if (!match || !Number.isSafeInteger(number) || number < 1 || number > MAX_PULL_REQUEST_NUMBER || match[2] === '.' || match[2] === '..') {\n throw new BoardError('board_pull_request_invalid', { value: input })\n }\n const owner = match[1].toLowerCase()\n const repo = match[2].toLowerCase()\n return { owner, repo, number, url: `https://github.com/${owner}/${repo}/pull/${number}` }\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { pullRequestFactKeys } from '../domain/pull-request-fact-keys'\nimport { parsePullRequestRef } from '../domain/pull-request-ref'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { IssuePullRequest } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\nimport { BOARD_ACTOR, requireCard, toStoredActorKind } from './types'\n\n/**\n * Name a pull request on a card (ruling 4-1).\n *\n * `urlOrRef` is a GitHub pull request URL or `owner/repo#number`, and anything\n * else is refused before the board is touched. Naming one the card already\n * names hands back the standing link and writes nothing — so the board version\n * moves only when a link was really made. No comment is written: what the pull\n * request *is* — opened, merged, green — is a fact the watcher reads off GitHub\n * and writes itself.\n */\nexport async function linkPullRequest<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n urlOrRef: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssuePullRequest> {\n const ref = parsePullRequestRef(urlOrRef)\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const standing = (await tx.pullRequests.listByIssue(issueId))\n .find(link => link.owner === ref.owner && link.repo === ref.repo && link.number === ref.number)\n if (standing) {\n return standing\n }\n return await tx.pullRequests.link({\n id: randomUUID(),\n issueId,\n owner: ref.owner,\n repo: ref.repo,\n number: ref.number,\n url: ref.url,\n createdByKind: toStoredActorKind(actor),\n createdById: actor.id ?? null,\n createdAt: currentUnixSeconds(),\n })\n }, scope)\n}\n\n/**\n * Drop one pull request link off a card. A link the card does not name is\n * `board_pull_request_not_found`.\n *\n * Who may drop it depends on who linked it. A person, and the board writing\n * for itself (`BOARD_ACTOR` — the watcher's own \"PR is gone\" unlink), may drop\n * any link. Every other actor — an agent, a provider target, or anything\n * outside with an identity of its own — may only drop a link its own\n * stored kind and id created; naming another actor's link is\n * `board_pull_request_not_yours`, the same 403 an agent gets for a card it may\n * not approve.\n */\nexport async function unlinkPullRequest<TStore extends BoardStore>(\n store: TStore,\n issueId: string,\n linkId: string,\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssuePullRequest> {\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, issueId)\n const named = (await tx.pullRequests.listByIssue(issueId)).find(link => link.id === linkId)\n if (!named) {\n throw new BoardError('board_pull_request_not_found', { issueId, linkId })\n }\n if (!canUnlinkPullRequest(actor, named)) {\n throw new BoardError('board_pull_request_not_yours', { issueId, linkId })\n }\n const removed = await tx.pullRequests.unlink(issueId, linkId)\n if (!removed) {\n throw new BoardError('board_pull_request_not_found', { issueId, linkId })\n }\n return removed\n }, scope)\n}\n\n/** Whether `actor` may drop `link` — see `unlinkPullRequest`. */\nfunction canUnlinkPullRequest(actor: BoardActor, link: IssuePullRequest): boolean {\n if (actor.kind === 'user') {\n return true\n }\n if (actor.kind === BOARD_ACTOR.kind && actor.id === BOARD_ACTOR.id) {\n return true\n }\n return link.createdByKind === toStoredActorKind(actor) && link.createdById === actor.id\n}\n\n/** The pull requests a card names, in the order they were linked. */\nexport async function listPullRequests(store: BoardStore, issueId: string): Promise<IssuePullRequest[]> {\n await requireCard(store, issueId)\n return await store.pullRequests.listByIssue(issueId)\n}\n\n/**\n * A pull request a card names, with where it stands as the card's own facts\n * say (ruling 4-7): `merged` or `closed` once the watcher has written that,\n * `open` once it has written «opened», `unknown` before it has read GitHub at\n * all; `ci` is the newest CI verdict written for it, whatever head it was for.\n */\nexport type BoardPullRequestStanding = IssuePullRequest & {\n state: 'open' | 'merged' | 'closed' | 'unknown'\n ci: 'green' | 'red' | null\n}\n\n/**\n * The pull requests a card names, in the order they were linked, each with\n * where it stands — read off the dedupe keys of the facts the watcher wrote on\n * the card, not the text of its comments, and nothing asked of GitHub.\n */\nexport async function listPullRequestStandings(store: BoardStore, issueId: string): Promise<BoardPullRequestStanding[]> {\n const listed: BoardPullRequestStanding[] = []\n for (const link of await listPullRequests(store, issueId)) {\n listed.push({ ...link, ...await readPullRequestStanding(store, link) })\n }\n return listed\n}\n\n/** Three indexed reads for the state and one prefix listing for the newest CI verdict, whose key ends in `:green` or `:red`. */\nasync function readPullRequestStanding(\n store: BoardStore,\n link: IssuePullRequest,\n): Promise<Pick<BoardPullRequestStanding, 'state' | 'ci'>> {\n const keys = pullRequestFactKeys(link)\n const carries = async (key: string): Promise<boolean> => (await store.comments.findByDedupeKey(link.issueId, key)) !== null\n const state = await carries(keys.merged)\n ? 'merged'\n : await carries(keys.closed) ? 'closed' : await carries(keys.opened) ? 'open' : 'unknown'\n const newest = (await store.comments.listByDedupeKeyPrefix(link.issueId, keys.ciPrefix)).at(-1)?.dedupeKey\n const ci = newest?.endsWith(':green') ? 'green' : newest?.endsWith(':red') ? 'red' : null\n return { state, ci }\n}\n","import type { BoardStore } from '../board-store'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { Issue } from '../sqlite/schema'\nimport { listColumns } from './columns'\nimport { readBoardProjectionForIssues } from './runs'\n\n/** The column an agent takes its next card from. */\nconst READY_COLUMN_SLUG = 'to_do'\n\n/**\n * The cards an agent may pick up right now: spelled out, nobody working on them,\n * nobody's turn but the agent's.\n *\n * \"Ready\" is deliberately narrow. A card in Backlog is an idea, a card with a\n * run is taken, and a card waiting for a person belongs to that person until\n * they answer. What is left is the queue — in board order, so the card someone\n * dragged to the top is the one an agent asking for work gets.\n */\nexport async function listReady(\n store: BoardStore,\n input: { workspaceId: string, limit?: number },\n): Promise<Issue[]> {\n const column = (await listColumns(store, input.workspaceId))\n .find(candidate => normalizeStatusName(candidate.name) === READY_COLUMN_SLUG)\n if (!column) {\n return []\n }\n\n const candidates = (await store.issues.listInBoardOrder(input.workspaceId))\n .filter(card => card.statusId === column.id && card.waitingFor === null)\n const running = new Set((await readBoardProjectionForIssues(store, candidates.map(card => card.id)))\n .filter(projection => projection.activeRun !== null)\n .map(projection => projection.issueId))\n\n const ready = candidates.filter(card => !running.has(card.id))\n return input.limit === undefined ? ready : ready.slice(0, input.limit)\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds } from '../domain/time'\nimport type { Issue, IssueRelation } from '../sqlite/schema'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport { requireCard } from './types'\n\n/** The card on the other end of an edge, as much of it as a relation row shows. */\nexport type IssueRelationCounterpart = Pick<Issue, 'id' | 'workspaceId' | 'number' | 'title' | 'statusId' | 'priority'>\n\n/** An edge as seen from one of the two cards it touches. */\nexport type IssueRelationView = IssueRelation & {\n /** Orientation of the edge relative to the issue the relations were listed for. */\n direction: 'outgoing' | 'incoming'\n /** The issue on the other end of the edge, resolved across workspaces. */\n counterpart: IssueRelationCounterpart | null\n}\n\n/** A new edge between two cards. */\nexport interface CreateRelationInput {\n sourceIssueId: string\n targetIssueId: string\n type: IssueRelation['type']\n}\n\n/**\n * Every edge touching the card, in either direction, each with the card on its\n * other end. A counterpart the board no longer holds comes back as `null`\n * rather than dropping the edge.\n */\nexport async function listRelations(store: BoardStore, issueId: string): Promise<IssueRelationView[]> {\n await requireCard(store, issueId)\n const rows = await store.relations.listByIssue(issueId)\n const counterpartIds = [...new Set(rows.map(row => row.sourceIssueId === issueId ? row.targetIssueId : row.sourceIssueId))]\n const counterpartRows = counterpartIds.length > 0 ? await store.issues.listByIds(counterpartIds) : []\n const counterpartsById = new Map<string, IssueRelationCounterpart>(counterpartRows.map(row => [row.id, {\n id: row.id,\n workspaceId: row.workspaceId,\n number: row.number,\n title: row.title,\n statusId: row.statusId,\n priority: row.priority,\n }]))\n\n return rows.map((row) => {\n const outgoing = row.sourceIssueId === issueId\n return {\n ...row,\n direction: outgoing ? 'outgoing' : 'incoming',\n counterpart: counterpartsById.get(outgoing ? row.targetIssueId : row.sourceIssueId) ?? null,\n }\n })\n}\n\n/**\n * Draw an edge between two cards of this board.\n *\n * A card is never related to itself, and both cards must be on the board. The\n * same edge drawn twice is one edge: the standing row comes back and nothing is\n * written — `relates_to` is symmetric, so the reversed pair is the same edge,\n * while a directed type keeps its orientation.\n */\nexport async function createRelation<TStore extends BoardStore>(\n store: TStore,\n input: CreateRelationInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueRelation> {\n if (input.sourceIssueId === input.targetIssueId) {\n throw new BoardError('issue_relation_self_reference', { issueId: input.sourceIssueId })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n await requireCard(tx, input.sourceIssueId)\n await requireCard(tx, input.targetIssueId)\n const duplicate = await tx.relations.findMatching(input)\n if (duplicate) {\n return duplicate\n }\n return await tx.relations.create({\n id: randomUUID(),\n sourceIssueId: input.sourceIssueId,\n targetIssueId: input.targetIssueId,\n type: input.type,\n createdAt: currentUnixSeconds(),\n })\n }, scope)\n}\n\n/** Remove one edge, or say the board holds no such edge. */\nexport async function deleteRelation<TStore extends BoardStore>(\n store: TStore,\n relationId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n if (!await tx.relations.findById(relationId)) {\n throw new BoardError('issue_relation_not_found', { relationId })\n }\n await tx.relations.delete(relationId)\n }, scope)\n}\n","import { randomUUID } from 'node:crypto'\n\nimport type { BoardStore } from '../board-store'\nimport { BoardError } from '../domain/errors'\nimport { currentUnixSeconds, utcDayEnd, utcDayStart } from '../domain/time'\nimport type { IssueMilestone, IssueStatus } from '../sqlite/schema'\nimport { updateCard } from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport { runBoardWrite } from './change-seq'\nimport type { BoardActor } from './types'\n\n/**\n * A milestone read as a sprint (ruling 5x-3): the row itself, how many of its\n * cards are still open and how many are done, and whether it is the sprint\n * running right now.\n *\n * `done` counts the cards in a `completed` or `canceled` column — the ones\n * closing the sprint leaves where they are; `open` counts every other card,\n * a card in no column at all included — the ones closing it carries on.\n */\nexport interface BoardSprint extends IssueMilestone {\n cards: { open: number, done: number }\n current: boolean\n}\n\n/**\n * What a milestone is created with; `startDate` and `dueDate` are unix seconds,\n * kept as the first and the last second of their UTC days.\n */\nexport interface CreateMilestoneInput {\n workspaceId: string\n title: string\n description?: string | null\n startDate?: number | null\n dueDate?: number | null\n status?: IssueMilestone['status']\n}\n\n/** What of a milestone can change. */\nexport type UpdateMilestoneInput = Partial<Omit<CreateMilestoneInput, 'workspaceId'>>\n\n/** What closing a sprint did: how many unfinished cards it moved on, and that the sprint is closed. */\nexport interface CloseSprintResult {\n carried: number\n closed: true\n}\n\nconst FINISHED_CATEGORIES = new Set<IssueStatus['category']>(['completed', 'canceled'])\n\n/**\n * A milestone's dates as the calendar days they fall on (ruling 5x-6): the\n * start at the first second of its UTC day, the due date at the last. Only the\n * dates the input names are touched.\n */\nfunction toCalendarDays<T extends { startDate?: number | null, dueDate?: number | null }>(input: T): T {\n const days = { ...input }\n if (days.startDate != null) {\n days.startDate = utcDayStart(days.startDate)\n }\n if (days.dueDate != null) {\n days.dueDate = utcDayEnd(days.dueDate)\n }\n return days\n}\n\n/** A sprint that starts after it ends is not a sprint. */\nfunction assertDatesInOrder(startDate: number | null | undefined, dueDate: number | null | undefined): void {\n if (startDate != null && dueDate != null && startDate > dueDate) {\n throw new BoardError('board_sprint_dates_invalid', { startDate, dueDate })\n }\n}\n\n/** Whether a milestone is the sprint running at `now`: open, started, not yet due. */\nfunction isRunningAt(milestone: IssueMilestone, now: number): boolean {\n return milestone.status === 'open'\n && milestone.startDate !== null\n && milestone.dueDate !== null\n && milestone.startDate <= now\n && now <= milestone.dueDate\n}\n\n/**\n * Create a milestone — a sprint when it is given a start and a due date.\n * Milestones carry no history, so there is no actor to record.\n */\nexport async function createMilestone<TStore extends BoardStore>(\n store: TStore,\n rawInput: CreateMilestoneInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueMilestone> {\n const input = toCalendarDays(rawInput)\n assertDatesInOrder(input.startDate, input.dueDate)\n const now = currentUnixSeconds()\n return await runBoardWrite(store, async ({ tx }) => await tx.milestones.create({\n id: randomUUID(),\n workspaceId: input.workspaceId,\n title: input.title,\n description: input.description ?? null,\n startDate: input.startDate ?? null,\n dueDate: input.dueDate ?? null,\n status: input.status ?? 'open',\n createdAt: now,\n updatedAt: now,\n }), scope)\n}\n\n/** Change a milestone's title, description, dates or status. */\nexport async function updateMilestone<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n rawInput: UpdateMilestoneInput,\n scope?: BoardWriteScope<TStore>,\n): Promise<IssueMilestone> {\n const input = toCalendarDays(rawInput)\n return await runBoardWrite(store, async ({ tx }) => {\n const milestone = await requireMilestone(tx, milestoneId)\n assertDatesInOrder(\n 'startDate' in input ? input.startDate : milestone.startDate,\n 'dueDate' in input ? input.dueDate : milestone.dueDate,\n )\n const updates: Partial<IssueMilestone> = { updatedAt: currentUnixSeconds() }\n if (input.title !== undefined) {\n updates.title = input.title\n }\n if ('description' in input) {\n updates.description = input.description ?? null\n }\n if ('startDate' in input) {\n updates.startDate = input.startDate ?? null\n }\n if ('dueDate' in input) {\n updates.dueDate = input.dueDate ?? null\n }\n if (input.status !== undefined) {\n updates.status = input.status\n }\n await tx.milestones.update(milestoneId, updates)\n return await requireMilestone(tx, milestoneId)\n }, scope)\n}\n\n/** Milestones, newest first — one workspace's, or with `null` every workspace's on the board. */\nexport async function listMilestones(store: BoardStore, workspaceId: string | null): Promise<IssueMilestone[]> {\n return await store.milestones.listNewestFirst(workspaceId)\n}\n\n/**\n * Delete a milestone. Its cards stay where they are and simply belong to no\n * milestone any more — the reference is cleared in the same write, so no card\n * is ever left naming a milestone that is gone.\n */\nexport async function deleteMilestone<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n scope?: BoardWriteScope<TStore>,\n): Promise<void> {\n await runBoardWrite(store, async ({ tx }) => {\n await requireMilestone(tx, milestoneId)\n await tx.issues.clearMilestoneReferences(milestoneId, currentUnixSeconds())\n await tx.milestones.delete(milestoneId)\n }, scope)\n}\n\n/**\n * Every milestone of a workspace as a sprint, the one running at `now` marked\n * `current`. Ordered by start date, the milestones without one last, then by\n * due date and creation.\n */\nexport async function listSprints(\n store: BoardStore,\n workspaceId: string,\n now: number = currentUnixSeconds(),\n): Promise<BoardSprint[]> {\n const [milestones, cards, columns] = await Promise.all([\n store.milestones.listByWorkspace(workspaceId),\n store.issues.listByWorkspace(workspaceId),\n store.statuses.listByWorkspace(workspaceId),\n ])\n const finishedColumns = new Set(columns\n .filter(column => FINISHED_CATEGORIES.has(column.category))\n .map(column => column.id))\n const current = pickCurrent(milestones, now)\n\n return milestones\n .toSorted((a, b) =>\n (a.startDate ?? Number.MAX_SAFE_INTEGER) - (b.startDate ?? Number.MAX_SAFE_INTEGER)\n || (a.dueDate ?? Number.MAX_SAFE_INTEGER) - (b.dueDate ?? Number.MAX_SAFE_INTEGER)\n || a.createdAt - b.createdAt)\n .map((milestone) => {\n const own = cards.filter(card => card.milestoneId === milestone.id)\n const done = own.filter(card => card.statusId !== null && finishedColumns.has(card.statusId)).length\n return { ...milestone, cards: { open: own.length - done, done }, current: milestone.id === current?.id }\n })\n}\n\n/**\n * The sprint running at `now`: an open milestone with `start_date <= now <=\n * due_date`. When several overlap, the one that started last; when none does,\n * `null`.\n */\nexport async function currentSprint(\n store: BoardStore,\n workspaceId: string,\n now: number = currentUnixSeconds(),\n): Promise<BoardSprint | null> {\n return (await listSprints(store, workspaceId, now)).find(sprint => sprint.current) ?? null\n}\n\nfunction pickCurrent(milestones: IssueMilestone[], now: number): IssueMilestone | null {\n return milestones\n .filter(milestone => isRunningAt(milestone, now))\n .reduce<IssueMilestone | null>((latest, milestone) =>\n latest === null || (milestone.startDate ?? 0) > (latest.startDate ?? 0) ? milestone : latest, null)\n}\n\n/**\n * Close a sprint, carrying its unfinished cards on (rulings 5x-3, 5x-4).\n *\n * Only a person closes a sprint: deciding what did not get done is a review of\n * the work, and an agent is one of the parties being reviewed. Every card of the\n * sprint whose column is not `completed` or `canceled` moves to `carryTo` — an\n * open milestone of the same workspace — through `updateCard`, so each one gets\n * the same `milestoneId` history entry any other edit would. Without `carryTo`\n * those cards leave the milestone instead. The finished cards stay where they\n * are, and the sprint is closed, all in one write.\n *\n * Closing a sprint that is already closed does nothing and says so with\n * `carried: 0` — the board version does not move.\n */\nexport async function closeSprint<TStore extends BoardStore>(\n store: TStore,\n milestoneId: string,\n input: { carryTo?: string | null },\n actor: BoardActor,\n scope?: BoardWriteScope<TStore>,\n): Promise<CloseSprintResult> {\n if (actor.kind !== 'user') {\n throw new BoardError('board_sprint_close_requires_user', { milestoneId, actorKind: actor.kind })\n }\n\n return await runBoardWrite(store, async ({ tx }) => {\n const sprint = await requireMilestone(tx, milestoneId)\n if (sprint.status === 'closed') {\n return { carried: 0, closed: true }\n }\n const carryTo = input.carryTo ?? null\n if (carryTo !== null) {\n const target = await tx.milestones.findInWorkspace(sprint.workspaceId, carryTo)\n if (!target || target.status !== 'open' || target.id === sprint.id) {\n throw new BoardError('board_sprint_carry_invalid', { milestoneId, carryTo })\n }\n }\n\n const finishedColumns = new Set((await tx.statuses.listByWorkspace(sprint.workspaceId))\n .filter(column => FINISHED_CATEGORIES.has(column.category))\n .map(column => column.id))\n const unfinished = (await tx.issues.listByWorkspace(sprint.workspaceId))\n .filter(card => card.milestoneId === sprint.id && !(card.statusId !== null && finishedColumns.has(card.statusId)))\n for (const card of unfinished) {\n await updateCard(tx, card.id, { milestoneId: carryTo }, actor, { tx })\n }\n await tx.milestones.update(sprint.id, { status: 'closed', updatedAt: currentUnixSeconds() })\n return { carried: unfinished.length, closed: true }\n }, scope)\n}\n\nasync function requireMilestone(store: BoardStore, milestoneId: string): Promise<IssueMilestone> {\n const milestone = await store.milestones.findById(milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { milestoneId })\n }\n return milestone\n}\n","import type { BoardStore } from '../board-store'\nimport type { IssueActivityResolver } from './activity'\nimport { listActivity, listFieldChanges } from './activity'\nimport { readApproval } from './approval'\nimport type { BulkUpdateCardsInput } from './card-batch'\nimport { bulkUpdateCards, reorderCards } from './card-batch'\nimport type { AddCommentInput, CreateCardInput, CreateCommentOnceInput, UpdateCardInput } from './cards'\nimport {\n addComment,\n approve,\n assertReturnable,\n createCard,\n createCommentOnce,\n createSubCard,\n deleteCard,\n deleteComment,\n listComments,\n moveCard,\n returnCard,\n searchCards,\n setStatusLine,\n updateCard,\n waitApproval,\n} from './cards'\nimport type { BoardWriteScope } from './change-seq'\nimport type { BoardColumnInput, BoardColumnPatch } from './columns'\nimport {\n addStandardColumns,\n createColumn,\n deleteColumn,\n describeColumn,\n ensureDefaultColumns,\n findColumn,\n listColumns,\n reorderColumns,\n requireColumn,\n setColumnEntryRules,\n updateColumn,\n} from './columns'\nimport { addContextRef, removeContextRef } from './context-refs'\nimport { enforceColumnEntryForCards } from './entry-rules'\nimport type { MigrateCardsInput } from './migrate'\nimport { migrateCards } from './migrate'\nimport { buildPrimeText } from './prime'\nimport { linkPullRequest, listPullRequests, listPullRequestStandings, unlinkPullRequest } from './pull-requests'\nimport { listReady } from './ready'\nimport type { CreateRelationInput } from './relations'\nimport { createRelation, deleteRelation, listRelations } from './relations'\nimport type { AttachRunExecutionInput, FinishRunInput, StartRunInput } from './runs'\nimport {\n attachRunExecution,\n clearRunSessionRef,\n findRunByChatSessionId,\n finishRun,\n listRuns,\n readBoardProjection,\n readBoardProjectionForIssue,\n readBoardProjectionForIssues,\n recordRunSessionRef,\n startRun,\n stopRunningRuns,\n} from './runs'\nimport type { CreateMilestoneInput, UpdateMilestoneInput } from './sprints'\nimport { closeSprint, createMilestone, currentSprint, deleteMilestone, listMilestones, listSprints, updateMilestone } from './sprints'\nimport type { BoardActor } from './types'\n\n/**\n * Everything the board can be asked to do, bound to one store.\n *\n * This is the layer the semantics live in — numbering, columns, the status line,\n * approval, runs — and there is exactly one of it. `kanbo serve`, an app's server, the\n * command-line tool and the MCP server all call these operations rather than\n * writing rows of their own, which is why a card launched from a terminal and a\n * card launched from the app end up telling the same story.\n *\n * Every write takes an actor, and every write may take a `scope`: passing the\n * scope of a write already in progress joins it, so a caller composing several\n * operations gets one transaction and one board version bump.\n */\nexport function createBoardOps<TStore extends BoardStore>(store: TStore) {\n type Scope = BoardWriteScope<TStore>\n\n return {\n // Cards\n createCard: async (input: CreateCardInput, actor: BoardActor, scope?: Scope) =>\n await createCard(store, input, actor, scope),\n createSubCard: async (parentIssueId: string, input: Omit<CreateCardInput, 'parentIssueId'>, actor: BoardActor, scope?: Scope) =>\n await createSubCard(store, parentIssueId, input, actor, scope),\n updateCard: async (issueId: string, input: UpdateCardInput, actor: BoardActor, scope?: Scope) =>\n await updateCard(store, issueId, input, actor, scope),\n moveCard: async (issueId: string, nameOrId: string, actor: BoardActor, scope?: Scope) =>\n await moveCard(store, issueId, nameOrId, actor, scope),\n addComment: async (input: AddCommentInput, actor: BoardActor, scope?: Scope) =>\n await addComment(store, input, actor, scope),\n createCommentOnce: async (input: CreateCommentOnceInput, scope?: Scope) =>\n await createCommentOnce(store, input, scope),\n listComments: async (issueId: string) => await listComments(store, issueId),\n deleteComment: async (commentId: string, actor: BoardActor, scope?: Scope) =>\n await deleteComment(store, commentId, actor, scope),\n deleteCard: async (issueId: string, scope?: Scope) => await deleteCard(store, issueId, scope),\n searchCards: async (query: string, limit: number) => await searchCards(store, query, limit),\n\n // Many cards at once\n bulkUpdateCards: async (issueIds: string[], input: BulkUpdateCardsInput, actor: BoardActor, scope?: Scope) =>\n await bulkUpdateCards(store, issueIds, input, actor, scope),\n reorderCards: async (workspaceId: string, orderedIds: string[], scope?: Scope) =>\n await reorderCards(store, workspaceId, orderedIds, scope),\n migrateCards: async (input: MigrateCardsInput) => await migrateCards(store, input),\n\n // What a card points at, and what it has been through\n addContextRef: async (issueId: string, ref: string, actor: BoardActor, scope?: Scope) =>\n await addContextRef(store, issueId, ref, actor, scope),\n removeContextRef: async (issueId: string, index: number, actor: BoardActor, scope?: Scope) =>\n await removeContextRef(store, issueId, index, actor, scope),\n listRelations: async (issueId: string) => await listRelations(store, issueId),\n createRelation: async (input: CreateRelationInput, scope?: Scope) => await createRelation(store, input, scope),\n deleteRelation: async (relationId: string, scope?: Scope) => await deleteRelation(store, relationId, scope),\n listFieldChanges: async (issueId: string) => await listFieldChanges(store, issueId),\n listActivity: async (issueId: string, resolver: IssueActivityResolver) => await listActivity(store, issueId, resolver),\n\n // The card's own state: what it is doing, and whose turn it is\n setStatusLine: async (issueId: string, statusLine: string | null, actor: BoardActor, scope?: Scope) =>\n await setStatusLine(store, issueId, statusLine, actor, scope),\n waitApproval: async (issueId: string, input: { statusLine?: string | null }, actor: BoardActor, scope?: Scope) =>\n await waitApproval(store, issueId, input, actor, scope),\n approve: async (issueId: string, input: { comment?: string | null }, actor: BoardActor, scope?: Scope) =>\n await approve(store, issueId, input, actor, scope),\n returnCard: async (issueId: string, input: { comment: string, toStatusName?: string | null }, actor: BoardActor, scope?: Scope) =>\n await returnCard(store, issueId, input, actor, scope),\n assertReturnable: async (issueId: string, input: { toStatusName?: string | null }, actor: BoardActor) =>\n await assertReturnable(store, issueId, input, actor),\n readApproval: async (issueId: string) => await readApproval(store, issueId),\n\n // The pull requests a card names\n linkPullRequest: async (issueId: string, urlOrRef: string, actor: BoardActor, scope?: Scope) =>\n await linkPullRequest(store, issueId, urlOrRef, actor, scope),\n unlinkPullRequest: async (issueId: string, linkId: string, actor: BoardActor, scope?: Scope) =>\n await unlinkPullRequest(store, issueId, linkId, actor, scope),\n listPullRequests: async (issueId: string) => await listPullRequests(store, issueId),\n listPullRequestStandings: async (issueId: string) => await listPullRequestStandings(store, issueId),\n\n // Milestones, and milestones read as sprints\n listMilestones: async (workspaceId: string | null) => await listMilestones(store, workspaceId),\n deleteMilestone: async (milestoneId: string, scope?: Scope) => await deleteMilestone(store, milestoneId, scope),\n createMilestone: async (input: CreateMilestoneInput, scope?: Scope) => await createMilestone(store, input, scope),\n updateMilestone: async (milestoneId: string, input: UpdateMilestoneInput, scope?: Scope) =>\n await updateMilestone(store, milestoneId, input, scope),\n listSprints: async (workspaceId: string, now?: number) => await listSprints(store, workspaceId, now),\n currentSprint: async (workspaceId: string, now?: number) => await currentSprint(store, workspaceId, now),\n closeSprint: async (milestoneId: string, input: { carryTo?: string | null }, actor: BoardActor, scope?: Scope) =>\n await closeSprint(store, milestoneId, input, actor, scope),\n\n // Columns\n listColumns: async (workspaceId: string) => await listColumns(store, workspaceId),\n findColumn: async (workspaceId: string, nameOrId: string) => await findColumn(store, workspaceId, nameOrId),\n requireColumn: async (workspaceId: string, nameOrId: string) => await requireColumn(store, workspaceId, nameOrId),\n ensureDefaultColumns: async (workspaceId: string, scope?: Scope) => await ensureDefaultColumns(store, workspaceId, scope),\n createColumn: async (input: BoardColumnInput, actor: BoardActor, scope?: Scope) => await createColumn(store, input, actor, scope),\n updateColumn: async (statusId: string, patch: BoardColumnPatch, actor: BoardActor, scope?: Scope) =>\n await updateColumn(store, statusId, patch, actor, scope),\n describeColumn: async (workspaceId: string, nameOrId: string, description: string | null, scope?: Scope) =>\n await describeColumn(store, workspaceId, nameOrId, description, scope),\n setColumnEntryRules: async (workspaceId: string, nameOrId: string, entryRules: readonly string[] | null, actor: BoardActor, scope?: Scope) =>\n await setColumnEntryRules(store, workspaceId, nameOrId, entryRules, actor, scope),\n enforceColumnEntryForCards: async (issueIds: string[], statusId: string, actor: BoardActor) =>\n await enforceColumnEntryForCards(store, issueIds, statusId, actor),\n deleteColumn: async (statusId: string, scope?: Scope) => await deleteColumn(store, statusId, scope),\n reorderColumns: async (workspaceId: string, orderedIds: string[], scope?: Scope) =>\n await reorderColumns(store, workspaceId, orderedIds, scope),\n addStandardColumns: async (workspaceId: string, scope?: Scope) => await addStandardColumns(store, workspaceId, scope),\n\n // Runs\n startRun: async (issueId: string, input: StartRunInput, actor: BoardActor, scope?: Scope) =>\n await startRun(store, issueId, input, actor, scope),\n attachRunExecution: async (runId: string, input: AttachRunExecutionInput, scope?: Scope) =>\n await attachRunExecution(store, runId, input, scope),\n recordRunSessionRef: async (runId: string, ref: string, scope?: Scope) =>\n await recordRunSessionRef(store, runId, ref, scope),\n clearRunSessionRef: async (runId: string, actor: BoardActor, options?: { replaceWith?: string }, scope?: Scope) =>\n await clearRunSessionRef(store, runId, actor, options, scope),\n finishRun: async (runId: string, input: FinishRunInput, actor: BoardActor, scope?: Scope) =>\n await finishRun(store, runId, input, actor, scope),\n stopRunningRuns: async (issueId: string, actor: BoardActor, scope?: Scope) =>\n await stopRunningRuns(store, issueId, actor, scope),\n listRuns: async (issueId: string) => await listRuns(store, issueId),\n findRunByChatSessionId: async (chatSessionId: string) => await findRunByChatSessionId(store, chatSessionId),\n readBoardProjection: async (workspaceId: string) => await readBoardProjection(store, workspaceId),\n readBoardProjectionForIssues: async (issueIds: string[]) => await readBoardProjectionForIssues(store, issueIds),\n readBoardProjectionForIssue: async (issueId: string) => await readBoardProjectionForIssue(store, issueId),\n\n // The board as a whole\n listReady: async (input: { workspaceId: string, limit?: number }) => await listReady(store, input),\n buildPrimeText: async (workspaceId: string) => await buildPrimeText(store, workspaceId),\n }\n}\n\n/** The board's operations, as a type callers can name. */\nexport type BoardOps = ReturnType<typeof createBoardOps<BoardStore>>\n","/**\n * A board's connection string, as it may be said out loud.\n *\n * Everything a person needs to recognise which board they are talking to is\n * the host and the database name. The password is the one part that must never\n * reach a terminal, a log, a `--json` result or a tool's answer — and it is not\n * the only part: a connection string carries options in its query, and a\n * `?password=…` there is as much a secret as the one in front of the host.\n *\n * It is a fact about the value rather than about the command line, which is why\n * it lives here: the MCP tools hand back messages they did not write either,\n * and they must not be made to carry the board tool's own runtime to say them\n * safely.\n */\n\n/** What a connection string is called when there is nothing safe to print of it. */\nconst MASKED_UNREADABLE_URL = 'postgres://***'\n\n/** What is printed in place of a password, and of a query that may hide one. */\nconst MASK = '***'\n\n/**\n * A connection string inside a longer line. Anything but whitespace and the\n * quotes a message may wrap it in belongs to the URL — a password is allowed\n * every character a URL is, so the match has to be greedy to be safe.\n */\nconst DATABASE_URL_PATTERN = /\\bpostgres(?:ql)?:\\/\\/[^\\s'\"`]+/gi\n\n/**\n * The connection string with everything secret taken out of it.\n *\n * A string this cannot take apart is not examined to find out why — it is\n * replaced entirely, because whatever is wrong with it, the secret is still\n * somewhere inside. A missing scheme counts as that: `user:secret@host/db`\n * parses, as a `user:` URL whose whole password is in the path, and printing it\n * back would say the secret out loud.\n */\nexport function maskDatabaseUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (!parsed.host) {\n return MASKED_UNREADABLE_URL\n }\n if (parsed.password) {\n parsed.password = MASK\n }\n // Not read for what it holds: an option this build has never heard of may\n // still be a credential, and the query is not what tells a person which\n // board this is.\n if (parsed.search) {\n parsed.search = MASK\n }\n return parsed.toString()\n }\n catch {\n return MASKED_UNREADABLE_URL\n }\n}\n\n/**\n * Every connection string inside a line of text, masked.\n *\n * A message this package did not write — a driver's, a migrator's — may carry\n * the whole connection string in the middle of a sentence, and it reaches the\n * same terminal, or the same agent, as everything else.\n */\nexport function maskDatabaseUrls(text: string): string {\n return text.replace(DATABASE_URL_PATTERN, url => maskDatabaseUrl(url))\n}\n","import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\n\nimport { z } from 'zod'\n\n/**\n * What ties a project folder to a board.\n *\n * A person running `kanbo` in their project should not have to say which\n * workspace they mean, and the answer cannot live in any database: the folder\n * knows which project it is, a database does not know which folder the shell\n * is in. `kanbo init` writes the answer next to the code, in a directory kanbo\n * owns — never in the user's own `.gitignore`, which is theirs.\n */\n\n/** The directory kanbo keeps inside a project: the binding, and a board file of the project's own. */\nconst PROJECT_DIRECTORY = '.kanbo'\n\n/** The binding's own file name, which is also the line that keeps it out of git. */\nconst BINDING_FILE_NAME = 'binding.json'\n\n/** The binding file itself, relative to the project root. */\nexport const BINDING_FILE_PATH = join(PROJECT_DIRECTORY, BINDING_FILE_NAME)\n\n/** SQLite's two companions of a board file in WAL mode, which hold committed data of their own. */\nconst SQLITE_SIDE_FILES = ['-wal', '-shm'] as const\n\n/** Readable and writable by its owner and by nobody else: it holds connection strings. */\nconst BINDING_FILE_MODE = 0o600\n\n/**\n * The spellings of a `.gitignore` line that already cover the binding.\n *\n * Git's pattern language is not reimplemented here: the cost of failing to\n * recognise an exotic pattern is one redundant line in a file, and the cost of\n * guessing that some pattern covers the binding when it does not is a\n * workspace id committed to someone else's checkout.\n */\nconst BINDING_IGNORED_BY = new Set([BINDING_FILE_NAME, `/${BINDING_FILE_NAME}`, `./${BINDING_FILE_NAME}`, `**/${BINDING_FILE_NAME}`])\n\nconst BindingSchema = z.object({\n schemaVersion: z.literal(1),\n workspaceId: z.string().trim().min(1),\n boardId: z.string().trim().min(1).nullish(),\n /**\n * The board file `kanbo init --file` created for this project, relative to\n * the project root — `.kanbo/board.db` by default. Resolution honours it\n * between the connection-string checks and the `KANBO_DB_PATH` fallback: a\n * project bound to a file of its own falls back to *that* file rather than\n * a host database that may not exist on this machine. `null` on a project\n * that has never run `kanbo init --file`.\n */\n dbPath: z.string().nullish(),\n /**\n * The external board this project works on, when it has one. Unlike\n * `dbPath` this *is* read: a project bound to a database has no board file\n * to fall back to. It is a secret, and the binding is the one file here that\n * git never sees — which is why it may be written down at all.\n */\n databaseUrl: z.string().trim().min(1).nullish(),\n /**\n * The same board, as the agent role logs into it.\n *\n * A shell started for an agent — and the MCP server, whose whole\n * client is an agent — is given this one when it is there, so the rules the\n * database keeps about `kanban_agent` are the rules it actually meets. It is\n * a different login, not a hidden one; a project with only `databaseUrl`\n * hands that one to everybody.\n */\n agentDatabaseUrl: z.string().trim().min(1).nullish(),\n /**\n * What this workspace's card keys start with (`MAN` in `MAN-012`).\n *\n * An external board holds only the board: there is no `workspaces` row to\n * read a key off, so `kanbo init` writes the answer down here. On a board\n * file the row is still the truth and this is not read.\n */\n identifier: z.string().trim().min(1).nullish(),\n})\n\n/** The project's binding, as it is written and read back. */\nexport type KanboBinding = z.infer<typeof BindingSchema>\n\n/** A binding that was found, the file it was found in, and the project it belongs to. */\nexport interface FoundBinding {\n path: string\n /** The project root the binding is for — where `.kanbo` sits, and what a relative `dbPath` is relative to. */\n projectDir: string\n binding: KanboBinding\n}\n\n/**\n * The nearest binding at or above `startDir`.\n *\n * Walking up is what makes the tool usable from a subdirectory: an agent that\n * `cd`s into `packages/web` is still working on the same project. A file that\n * does not parse is not a binding — the walk continues past it, and the caller\n * ends up with the same error as a project that was never bound.\n */\nexport function findBinding(startDir: string): FoundBinding | null {\n let directory = resolve(startDir)\n\n while (true) {\n const path = join(directory, BINDING_FILE_PATH)\n const binding = readBinding(path)\n if (binding) {\n return { path, projectDir: directory, binding }\n }\n const parent = dirname(directory)\n if (parent === directory) {\n return null\n }\n directory = parent\n }\n}\n\n/**\n * The binding this project folder has of its own — not a parent's.\n *\n * `kanbo init` merges into it rather than replacing it: a person re-running\n * the command to refresh an instruction block never said anything about the\n * board, and a second `init` that quietly dropped the connection string would\n * leave a project bound to nothing.\n */\nexport function readProjectBinding(projectDir: string): KanboBinding | null {\n return readBinding(join(projectDir, BINDING_FILE_PATH))\n}\n\n/** The binding in that exact file, or `null` when there is none to read. */\nexport function readBinding(path: string): KanboBinding | null {\n if (!existsSync(path)) {\n return null\n }\n try {\n return BindingSchema.parse(JSON.parse(readFileSync(path, 'utf8')))\n }\n catch {\n return null\n }\n}\n\n/**\n * Bind the project folder to a board.\n *\n * The binding itself is kept out of git, because the workspace id in it means\n * nothing in anyone else's checkout. The ignore rule names the files kanbo\n * writes rather than `*`, so a person who does want something in `.kanbo`\n * committed can still add it. The user's own `.gitignore` is not touched at\n * all; it is not kanbo's file to edit.\n *\n * It is written `0600`, because it may hold one or two connection strings and\n * keeping a file out of a repository is not keeping it away from the other\n * accounts on the machine. `mode` only applies to a file this call creates, so\n * an existing one — a second `init`, or a file written before this rule — is\n * narrowed explicitly. Windows has no such bits and `chmodSync` does nothing\n * there, which is the right nothing: no permission to narrow, and no failure\n * to handle.\n */\nexport function writeBinding(projectDir: string, binding: KanboBinding): string {\n const directory = join(projectDir, PROJECT_DIRECTORY)\n mkdirSync(directory, { recursive: true })\n ignoreBindingFile(directory)\n\n // Written beside itself and renamed into place, so a crash half-way leaves\n // the old binding or the new one and never half of either.\n const path = join(projectDir, BINDING_FILE_PATH)\n const temporaryPath = `${path}.${process.pid}.tmp`\n try {\n writeFileSync(temporaryPath, `${JSON.stringify(BindingSchema.parse(binding), null, 2)}\\n`, { mode: BINDING_FILE_MODE })\n chmodSync(temporaryPath, BINDING_FILE_MODE)\n renameSync(temporaryPath, path)\n }\n catch (error) {\n rmSync(temporaryPath, { force: true })\n throw error\n }\n return path\n}\n\n/**\n * Make sure `.kanbo/.gitignore` covers the binding, and change nothing else\n * about it. Lines that are already there belong to whoever wrote them — this\n * appends one line, or none at all.\n */\nfunction ignoreBindingFile(directory: string): void {\n const path = join(directory, '.gitignore')\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : null\n if (existing !== null && existing.split('\\n').some(line => BINDING_IGNORED_BY.has(line.trim()))) {\n return\n }\n\n const kept = existing?.replace(/\\s*$/, '') ?? ''\n writeFileSync(path, kept ? `${kept}\\n${BINDING_FILE_NAME}\\n` : `${BINDING_FILE_NAME}\\n`)\n}\n\n/**\n * Make sure `.kanbo/.gitignore` also covers a board file of this project's\n * own — `kanbo init --file` calls this once, right after creating it — and\n * the two files SQLite leaves beside it in WAL mode, `-wal` and `-shm`, which\n * hold committed data of their own and are exactly as much a secret as the\n * database itself.\n *\n * Only when the file is inside `.kanbo`, which is where `--file` puts it by\n * default: the ignore rule only ever grows kanbo's own directory, and a path\n * chosen elsewhere in the project gets no line here, the same way the user's\n * own `.gitignore` is never touched at all.\n */\nexport function ignoreBoardFile(projectDir: string, absoluteDbPath: string): void {\n const directory = join(projectDir, PROJECT_DIRECTORY)\n const relativeToDirectory = relative(directory, absoluteDbPath)\n if (relativeToDirectory.startsWith('..') || isAbsolute(relativeToDirectory)) {\n return\n }\n\n mkdirSync(directory, { recursive: true })\n const wanted = [relativeToDirectory, ...SQLITE_SIDE_FILES.map(suffix => `${relativeToDirectory}${suffix}`)]\n const path = join(directory, '.gitignore')\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : null\n const already = new Set((existing ?? '').split('\\n').map(line => line.trim()))\n const missing = wanted.filter(line => !already.has(line))\n if (missing.length === 0) {\n return\n }\n\n const kept = existing?.replace(/\\s*$/, '') ?? ''\n writeFileSync(path, kept ? `${kept}\\n${missing.join('\\n')}\\n` : `${missing.join('\\n')}\\n`)\n}\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { maskDatabaseUrl } from '../domain/database-url'\nimport type { BoardFileOwner } from '../sqlite/open-database'\nimport { readBoardFileContents } from '../sqlite/open-database'\nimport { isAgentShell } from './actor'\nimport { findBinding } from './binding'\nimport { CliError, EXIT_NOT_RESOLVED } from './output'\n\n/**\n * Which board the command works on.\n *\n * A board is a SQLite file — one of a project's own (`kanbo init --file`), or\n * a host app's database the board lives inside — or a Postgres database of its\n * own, and the command line reaches all three. What the caller typed comes\n * first, and only one of `--db` and `--database-url` may be typed: together\n * they name two boards, which is a mistake to say out loud rather than a\n * precedence to resolve. Failing a flag, a connection string is looked for —\n * the environment, then the project's binding — because a project bound to a\n * database has no board file to fall back to, and a fallback that quietly wrote\n * to some local file instead is the one mistake worth making impossible.\n *\n * Failing a connection string, a board file is looked for: the project's own\n * binding first — `kanbo init --file` records the file it created — and only\n * then `KANBO_DB_PATH`, which is how an app that embeds the board tells the\n * shells it starts where its database is. Nothing here is created and nothing\n * is migrated — a file that is not there means the person has not run\n * `kanbo init --file` yet, and saying so is more useful than opening an empty\n * database they will never see. Whichever file is chosen, `readFileOwnerAt`\n * says whose file it is: the marker `kanbo init --file` leaves is what every\n * later command reads to decide whether it may look up a `workspaces` row in\n * it at all (ruling 5-4).\n */\n\nexport const DATABASE_NOT_FOUND_MESSAGE = 'No board found. Run \"kanbo init --file\" in the project, '\n + 'pass --db or --database-url, or set KANBO_DB_PATH or KANBO_DATABASE_URL.'\n\n/** What a caller is told when they name both kinds of board at once. */\nexport const TWO_BOARDS_MESSAGE\n = 'One board at a time: pass --db for a board file or --database-url for an external board, not both.'\n\n/** Where the board this command works on lives. */\nexport type BoardTarget\n = | { kind: 'sqlite', path: string, owner: BoardFileOwner }\n | { kind: 'postgres', url: string }\n\n/** What a caller typed, and where they typed it. */\nexport interface BoardTargetInput {\n /** What `--db` was given: a board file. */\n explicitPath?: string | null\n /** What `--database-url` was given: an external board's connection string. */\n explicitUrl?: string | null\n /** The directory the command was typed in; the binding is looked for at or above it. */\n cwd?: string\n /**\n * Reach the board as an agent rather than as whoever owns this shell. Never\n * typed: `kanbo mcp` sets it, because the client it serves is the agent, and\n * every other command asks the shell itself.\n */\n asAgent?: boolean\n}\n\n/**\n * The board to open, in the order a caller would expect to be obeyed: what\n * they typed, what they exported for this tool, what the project folder was\n * bound to, and only then the board file the environment names.\n *\n * A typed flag beats the environment, whichever kind of board it names: a\n * person who passes `--db` is looking at that file, and an exported connection\n * string left over from another project is not a reason to open something else.\n */\nexport function resolveDbTarget(input: BoardTargetInput = {}): BoardTarget {\n const explicitPath = input.explicitPath?.trim()\n const explicitUrl = input.explicitUrl?.trim()\n if (explicitPath && explicitUrl) {\n throw new CliError(1, TWO_BOARDS_MESSAGE)\n }\n if (explicitUrl) {\n return { kind: 'postgres', url: explicitUrl }\n }\n if (explicitPath) {\n return toSqliteTarget(resolveDbPath({ explicitPath }))\n }\n\n const url = resolveDatabaseUrl(input)\n return url ? { kind: 'postgres', url } : toSqliteTarget(resolveDbPath(input))\n}\n\n/** The sqlite half of a target: the path, and who wrote the file it names. */\nfunction toSqliteTarget(path: string): BoardTarget {\n return { kind: 'sqlite', path, owner: readFileOwnerAt(path) }\n}\n\n/**\n * Who wrote this file's board, without leaving a second connection open onto\n * it: opened, read, closed, before the caller opens the file for real.\n *\n * A file with no board in it yet still reads as a host database's — `assertBoardSchema`\n * is what says a board is missing or too old, once the caller actually tries to\n * use it. A path that is not a SQLite database at all is a different mistake\n * and gets its own answer: `kanbo card list --db ./README.md` used to leave\n * the driver's own sentence and its stack, where every other way of naming the\n * wrong board leaves one line and exit `2`.\n */\nfunction readFileOwnerAt(path: string): BoardFileOwner {\n try {\n return readBoardFileContents(path).owner\n }\n catch {\n throw new CliError(EXIT_NOT_RESOLVED, `${path} is not a board database this tool can open.`)\n }\n}\n\n/**\n * The external board named for this command, or `null` when none is.\n *\n * Apart from `resolveDbTarget` itself, this is for the two commands that do\n * not take a board the ordinary way: `kanbo init`, where `--db` names the\n * host database rather than the board being bound, and the administrative ones,\n * which never take the agent's login.\n */\nexport function resolveDatabaseUrl(input: BoardTargetInput = {}): string | null {\n return input.explicitUrl?.trim() || process.env.KANBO_DATABASE_URL?.trim() || readBoundDatabaseUrl(input)\n}\n\n/**\n * The external board this project is bound to, as this shell should reach it.\n *\n * A binding may carry two connection strings: the one a person uses, and the\n * one the agent role logs in with. An agent's shell — and the MCP server, whose\n * whole client is an agent — is given the second when it is there, so the rules\n * the database keeps about `kanban_agent` are the rules it actually meets.\n * Nothing about this is a secret from the agent: it is a different login, not a\n * hidden one, and a project with only one string hands that one to everybody.\n */\nfunction readBoundDatabaseUrl(input: BoardTargetInput): string | null {\n const bound = findBinding(input.cwd ?? process.cwd())?.binding\n if (!bound) {\n return null\n }\n const asAgent = input.asAgent ?? isAgentShell()\n return (asAgent ? bound.agentDatabaseUrl?.trim() : null) || bound.databaseUrl?.trim() || null\n}\n\n/**\n * The board file to open, in the order a caller would expect to be obeyed:\n * what they typed, the file this project was bound to by `kanbo init --file`,\n * and what they exported for this tool.\n *\n * The chosen path has to exist. A `--db` that is not there is a typo, not an\n * invitation to fall back to the real board and write to it by mistake.\n */\nfunction resolveDbPath(input: BoardTargetInput = {}): string {\n return requireExisting(input.explicitPath?.trim() || readBoundDbPath(input) || readEnvironmentDbPath())\n}\n\n/**\n * A host app's database on this machine, never through this project's own\n * `kanbo init --file` binding: what `--db` was given, or `KANBO_DB_PATH`.\n *\n * `kanbo init` calls this — not `resolveDbPath` — to find the database it\n * reads a workspace's id and card-key off before binding this project to a\n * database or a board file of its own. Whether *this* project already has a\n * board file of its own is not that question: a project bound to one by an\n * earlier `kanbo init --file` still has cards to read a workspace off if a\n * host database also knows this folder, and that file has no `workspaces`\n * table to be misread as one either way.\n */\nexport function resolveHostDbPath(explicitPath?: string | null): string {\n return requireExisting(explicitPath?.trim() || readEnvironmentDbPath())\n}\n\nfunction requireExisting(path: string | null): string {\n if (!path || !existsSync(path)) {\n throw new CliError(EXIT_NOT_RESOLVED, DATABASE_NOT_FOUND_MESSAGE)\n }\n return path\n}\n\n/**\n * The board file this project was bound to by `kanbo init --file`, resolved\n * against the project root the binding was found in — `dbPath` is written\n * relative to it, the same way a project's own `node_modules/.bin` is relative\n * to the project rather than to whichever subdirectory a shell happens to be\n * standing in.\n */\nfunction readBoundDbPath(input: BoardTargetInput): string | null {\n const found = findBinding(input.cwd ?? process.cwd())\n if (!found) {\n return null\n }\n const dbPath = found.binding.dbPath?.trim()\n return dbPath ? resolve(found.projectDir, dbPath) : null\n}\n\n/** The board file the environment names — the one an app that embeds the board exports for the shells it starts. */\nfunction readEnvironmentDbPath(): string | null {\n return process.env.KANBO_DB_PATH?.trim() || null\n}\n\n/** The board a command worked on, in the one line a person reads or a result carries. */\nexport function describeTarget(target: BoardTarget): string {\n return target.kind === 'postgres' ? maskDatabaseUrl(target.url) : target.path\n}\n","import { drizzle } from 'drizzle-orm/postgres-js'\nimport { migrate } from 'drizzle-orm/postgres-js/migrator'\nimport postgres from 'postgres'\n\nimport { getBoardMigrationsPath } from '../postgres/migrations-path'\nimport type { BoardPostgresDatabase } from '../postgres/open-database'\n\n/**\n * The one place the command line opens an external board.\n *\n * Everything a command does with a connection lives behind this: the board\n * itself for the store and the schema guard, the migration, and the role\n * script. A command never holds the driver, which is what lets the tests hand\n * the whole thing an embedded Postgres instead — a board in memory answers all\n * three the same way, and a real server is not something a unit test may need.\n */\n\n/** An open external board, and everything a command does with one. */\nexport interface OpenPostgresBoard {\n /** The board, for the store and for the guard that says whether it is a board at all. */\n database: BoardPostgresDatabase\n /** Bring it up to the schema this build speaks. Running it again does nothing. */\n migrate: () => Promise<void>\n /** Run a script of several statements as it is written — the role SQL is one. */\n runScript: (script: string) => Promise<void>\n close: () => Promise<void>\n}\n\n/** How a board is opened from a connection string. */\nexport type PostgresBoardOpener = (url: string) => Promise<OpenPostgresBoard>\n\n/**\n * How long a connection is given to say goodbye. The command is about to exit\n * either way; the timeout is there so a pooler that has already gone does not\n * hold the process open behind it.\n */\nconst CLOSE_TIMEOUT_SECONDS = 5\n\nlet openerForTests: PostgresBoardOpener | null = null\n\n/**\n * Open the board this connection string names.\n *\n * `prepare: false` because both of Supabase's poolers sit in front of the\n * database and neither carries a prepared statement across a connection, and\n * `max: 1` because a `kanbo` command is one command: a pool of one is the\n * whole of the concurrency there is, and it is also what makes `set role` and\n * a transaction mean what they say.\n */\nexport async function openPostgresBoard(url: string): Promise<OpenPostgresBoard> {\n return await (openerForTests ?? openWithPostgresJs)(url)\n}\n\nasync function openWithPostgresJs(url: string): Promise<OpenPostgresBoard> {\n const client = postgres(url, { prepare: false, max: 1 })\n const database = drizzle(client)\n return {\n database,\n migrate: async () => {\n await migrate(database, { migrationsFolder: getBoardMigrationsPath() })\n },\n // The role script is many statements and a `do $$ … $$` block, which is\n // the simple query protocol's job rather than a parameterised statement's.\n runScript: async (script: string) => {\n await client.unsafe(script).simple()\n },\n close: async () => {\n await client.end({ timeout: CLOSE_TIMEOUT_SECONDS })\n },\n }\n}\n\n/**\n * Hand the commands an already-open board instead of a connection.\n *\n * A `kanbo` test runs the commands in this process against a database it owns,\n * and an embedded Postgres listens on no socket, so there is no connection\n * string that could reach one. This is the seam, and it is deliberately a\n * function rather than an environment variable: a production path that can be\n * switched from outside is a production path that will be.\n */\nexport function overridePostgresOpenerForTests(opener: PostgresBoardOpener | null): void {\n openerForTests = opener\n}\n","import { BoardError } from '../domain/errors'\nimport type { BoardPostgresDatabase } from '../postgres/open-database'\nimport { assertBoardSchema as assertPostgresBoardSchema } from '../postgres/open-database'\nimport type { BoardFileOwner } from '../sqlite/open-database'\nimport { assertBoardSchema } from '../sqlite/open-database'\nimport type { SqliteDatabase } from '../sqlite/transaction'\nimport { CliError, EXIT_SCHEMA_OUTDATED } from './output'\n\nexport const SCHEMA_OUTDATED_MESSAGE\n = 'This database is older than the board schema. Let the app it belongs to apply its migrations first.'\n\n/** The same, for a board file this package created and migrates itself. */\nexport const FILE_SCHEMA_OUTDATED_MESSAGE\n = 'Board file is older than this build. Run kanbo migrate.'\n\nexport const SCHEMA_NOT_INSTALLED_MESSAGE\n = 'This database holds no board this build can speak for — it has none, or one an '\n + 'older kanbo created. Run kanbo migrate.'\n\n/**\n * Refuse to write to a board file this build cannot speak for.\n *\n * Which remedy a caller is told about depends on who migrates the file: a host\n * app migrates its own database, so an outdated one is read from at the\n * caller's own risk but never written to — a write shaped by this build's idea\n * of the schema is how a person's board gets a column full of nothing. A file\n * of this package's own is this package's to migrate, so the same refusal\n * points at `kanbo migrate` instead of at the app. Reads stay open either way,\n * because a person who cannot migrate right now should still be able to look at\n * their cards.\n */\nexport function assertWritableBoard(database: SqliteDatabase, owner: BoardFileOwner): void {\n try {\n assertBoardSchema(database)\n }\n catch (error) {\n if (error instanceof BoardError && error.code === 'board_schema_outdated') {\n throw new CliError(EXIT_SCHEMA_OUTDATED, owner === 'kanbo' ? FILE_SCHEMA_OUTDATED_MESSAGE : SCHEMA_OUTDATED_MESSAGE)\n }\n throw error\n }\n}\n\n/**\n * Refuse an external board this build cannot speak for — before reading it as\n * well as before writing to it.\n *\n * The asymmetry with the file above is the ownership. Nobody but this package\n * ever creates these tables, so a database with no board in it is not an older\n * board to be read carefully: it is a connection string pointing at something\n * else entirely, and reading eight tables that are not there says less than\n * naming the one command that would put them there. The same sentence answers\n * a board of another generation, which `kanbo migrate` also settles.\n */\nexport async function assertInstalledBoard(database: BoardPostgresDatabase): Promise<void> {\n try {\n await assertPostgresBoardSchema(database)\n }\n catch (error) {\n if (error instanceof BoardError && error.code === 'board_schema_outdated') {\n throw new CliError(EXIT_SCHEMA_OUTDATED, SCHEMA_NOT_INSTALLED_MESSAGE)\n }\n throw error\n }\n}\n","import { realpathSync } from 'node:fs'\nimport { resolve, sep } from 'node:path'\n\nimport { sql } from 'drizzle-orm'\nimport { z } from 'zod'\n\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport type { SqliteDatabase } from '../sqlite/transaction'\nimport { findBinding } from './binding'\nimport { CliError, EXIT_NOT_RESOLVED } from './output'\n\n/**\n * Which workspace the command is about.\n *\n * Cards are numbered per workspace and columns belong to one, so a command\n * typed in a project folder has to arrive at a workspace — its id, and the\n * identifier its card keys are built from — somehow. Where the answer comes\n * from is the one thing the two kinds of board do not share.\n *\n * In a host database — an app's own SQLite file the board lives inside — this\n * is the one place the tool reads a table it does not own: the record of which\n * folder is which workspace is the app's `workspaces` row (`id`, `name`,\n * `identifier`, `locator_json`). The module reads that row and nothing else —\n * the name, the identifier the issue key is built from, and the path inside the\n * locator — and never writes there. It reads it as four columns of raw SQL\n * rather than through a schema, because this package declares the board and\n * not the app: a table belonging to somebody else is read by name, or the library would carry\n * a copy of a schema it does not own (ruling 5-1).\n *\n * An external board, like a board file of a project's own, holds only the\n * board. There is no `workspaces` table to\n * read, so nothing can be looked up and everything has to have been said:\n * `--workspace`, the environment, or the binding `kanbo init` wrote.\n */\n\n/**\n * The part of a workspace locator this tool understands: which machine the\n * project is on, and where it is there. The app that writes the row owns the\n * full shape; a row that does not carry these two is not a locator this tool\n * can act on, and is skipped.\n */\nconst LocatorSchema = z.object({\n nodeId: z.string().trim().min(1),\n path: z.string().trim().min(1),\n})\n\n/** The node id a locator gives this machine. */\nconst LOCAL_NODE_ID = 'local'\n\n/**\n * The one message that has to work for `kanbo init` too. A command that told a\n * person to run `init` while they were running `init` would be answering a\n * question they did not ask, so what to do comes first and the binding second.\n */\nconst WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. Pass --workspace <name|id>, '\n + 'or run \"kanbo init\" in the project folder.'\n\n/** The same, for a board that cannot look a workspace up at all. */\nexport const EXTERNAL_WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. An external board holds no workspaces table, so pass '\n + '--workspace <id>, set KANBO_WORKSPACE_ID, or run \"kanbo init --database-url <url>\" in the project folder.'\n\n/** And the one that is missing only the key a card is numbered with. */\nexport const EXTERNAL_IDENTIFIER_NOT_RESOLVED_MESSAGE\n = 'Could not tell what this workspace\\'s card keys start with. An external board holds no workspaces table, '\n + 'so the key comes from this project\\'s binding: run '\n + '\"kanbo init --database-url <url> --workspace <id> --identifier <KEY>\" in the project folder.'\n\n/** The same two, for a board file of this project's own — which holds no `workspaces` table either. */\nexport const FILE_WORKSPACE_NOT_RESOLVED_MESSAGE\n = 'Could not tell which workspace this is. A board file of this project\\'s own holds no workspaces table, so '\n + 'pass --workspace <id>, set KANBO_WORKSPACE_ID, or run \"kanbo init --file\" in the project folder.'\n\nexport const FILE_IDENTIFIER_NOT_RESOLVED_MESSAGE\n = 'Could not tell what this workspace\\'s card keys start with. A board file of this project\\'s own holds no '\n + 'workspaces table, so the key comes from this project\\'s binding: run \"kanbo init --file --workspace <id> '\n + '--identifier <KEY>\" in the project folder.'\n\n/**\n * A host database's workspace, as much of one as this tool reads: the four\n * columns the statement below selects. The app's own row may have more, and\n * none of it is this package's business.\n */\ninterface HostWorkspaceRow {\n id: string\n name: string\n identifier: string\n locatorJson: string\n}\n\n/** Where a command's workspace is looked up — or the fact that it cannot be. */\nexport type BoardWorkspaceSource\n = | { kind: 'sqlite', database: SqliteDatabase }\n | { kind: 'postgres' }\n | { kind: 'file' }\n\n/** How a command names the workspace it means, and where it was typed. */\nexport interface WorkspaceResolutionInput {\n /** What `--workspace` was given: a workspace name or its id — an id only, on an external board. */\n explicit?: string | null\n /** The directory the command was typed in. */\n cwd: string\n}\n\n/**\n * The workspace to work on, and everything card numbering needs to know about\n * it. The board never reads anybody else's tables to build an issue key, so the\n * identity is resolved here, once, and handed over.\n */\nexport function resolveWorkspace(\n source: BoardWorkspaceSource,\n input: WorkspaceResolutionInput,\n): BoardWorkspaceIdentity {\n if (source.kind === 'sqlite') {\n return toIdentity(findLocalWorkspace(source.database, input))\n }\n return resolveBindingOnlyWorkspace(input, source.kind === 'file' ? FILE_MESSAGES : EXTERNAL_MESSAGES)\n}\n\n/**\n * The workspace row to work on, in the order a caller would expect to be\n * obeyed: what they typed, what they exported, what the project folder was\n * bound to, and finally the workspace whose own path contains the folder they\n * are standing in.\n *\n * Every path ends at a row that exists. A binding pointing at a workspace\n * someone deleted is a broken binding, and saying so beats numbering a card\n * into a workspace that is not there.\n */\nfunction findLocalWorkspace(database: SqliteDatabase, input: WorkspaceResolutionInput): HostWorkspaceRow {\n const rows = listWorkspaces(database)\n\n const explicit = input.explicit?.trim()\n if (explicit) {\n return findByNameOrId(rows, explicit)\n }\n\n const fromEnvironment = process.env.KANBO_WORKSPACE_ID?.trim()\n if (fromEnvironment) {\n return findByNameOrId(rows, fromEnvironment)\n }\n\n const bound = findBinding(input.cwd)\n if (bound) {\n return findByNameOrId(rows, bound.binding.workspaceId)\n }\n\n const enclosing = findEnclosingWorkspace(rows, input.cwd)\n if (enclosing) {\n return enclosing\n }\n\n throw new CliError(EXIT_NOT_RESOLVED, WORKSPACE_NOT_RESOLVED_MESSAGE)\n}\n\n/** The two messages a board with no `workspaces` table refuses with — one pair per kind of such a board. */\ninterface BindingOnlyMessages {\n workspace: string\n identifier: string\n}\n\nconst EXTERNAL_MESSAGES: BindingOnlyMessages = {\n workspace: EXTERNAL_WORKSPACE_NOT_RESOLVED_MESSAGE,\n identifier: EXTERNAL_IDENTIFIER_NOT_RESOLVED_MESSAGE,\n}\n\nconst FILE_MESSAGES: BindingOnlyMessages = {\n workspace: FILE_WORKSPACE_NOT_RESOLVED_MESSAGE,\n identifier: FILE_IDENTIFIER_NOT_RESOLVED_MESSAGE,\n}\n\n/**\n * The same question asked of a board that cannot answer it — an external\n * database or a board file of the project's own, which is the one difference\n * between the two that matters here: neither holds a `workspaces` table.\n *\n * Nothing is looked up: an id is what the caller said, and the key its cards\n * are numbered with is what `kanbo init` wrote into the binding. The binding's\n * key is only trusted for the workspace the binding is about — a `--workspace`\n * naming another one would otherwise number its cards with this project's\n * prefix, and two workspaces would print the same key.\n */\nfunction resolveBindingOnlyWorkspace(input: WorkspaceResolutionInput, messages: BindingOnlyMessages): BoardWorkspaceIdentity {\n const bound = findBinding(input.cwd)?.binding\n const id = input.explicit?.trim() || process.env.KANBO_WORKSPACE_ID?.trim() || bound?.workspaceId\n if (!id) {\n throw new CliError(EXIT_NOT_RESOLVED, messages.workspace)\n }\n\n const identifier = bound?.workspaceId === id ? bound?.identifier?.trim() : null\n if (!identifier) {\n throw new CliError(EXIT_NOT_RESOLVED, messages.identifier)\n }\n // Neither board has a name for a workspace either, and the key is the one\n // thing a person recognises it by.\n return { id, identifier, name: identifier }\n}\n\nfunction toIdentity(workspace: HostWorkspaceRow): BoardWorkspaceIdentity {\n return { id: workspace.id, identifier: workspace.identifier, name: workspace.name }\n}\n\nfunction listWorkspaces(database: SqliteDatabase): HostWorkspaceRow[] {\n return database\n .all<{ id: string, name: string, identifier: string, locator_json: string }>(\n sql`select id, name, identifier, locator_json from workspaces`,\n )\n .map(row => ({ id: row.id, name: row.name, identifier: row.identifier, locatorJson: row.locator_json }))\n}\n\n/**\n * The workspace a caller named, by id first and by name second.\n *\n * Names are not unique — two checkouts of the same repository are two\n * workspaces with one name — so a name that matches more than one is refused\n * rather than resolved to whichever row came back first. Guessing here would\n * put a card on the wrong board and say nothing.\n */\nfunction findByNameOrId(rows: HostWorkspaceRow[], nameOrId: string): HostWorkspaceRow {\n const byId = rows.find(row => row.id === nameOrId)\n if (byId) {\n return byId\n }\n\n const byName = rows.filter(row => row.name.toLowerCase() === nameOrId.toLowerCase())\n if (byName.length > 1) {\n throw new CliError(EXIT_NOT_RESOLVED, `More than one workspace is called \"${nameOrId}\": `\n + `${byName.map(row => row.id).join(', ')}. Name the one you mean by its id.`)\n }\n if (byName.length === 0) {\n throw new CliError(EXIT_NOT_RESOLVED, `No workspace in this database matches \"${nameOrId}\".`)\n }\n return byName[0]\n}\n\n/**\n * The workspace whose folder contains `cwd`. The deepest match wins: a worktree\n * registered inside a project is a workspace of its own, and standing in it\n * means working on it.\n *\n * Only workspaces on this machine are considered. A locator names a node as\n * well as a path, and a path on another machine that happens to read like this\n * one is a different folder entirely, so node ids are compared before paths. Both sides are compared as real paths, so a\n * project reached through a symlink still matches the workspace it is.\n */\nfunction findEnclosingWorkspace(rows: HostWorkspaceRow[], cwd: string): HostWorkspaceRow | null {\n const directory = canonicalPath(cwd)\n const candidates = rows\n .flatMap((row) => {\n const locator = readLocator(row)\n if (!locator || locator.nodeId !== LOCAL_NODE_ID) {\n return []\n }\n const path = canonicalPath(locator.path)\n return directory === path || directory.startsWith(path + sep) ? [{ row, path }] : []\n })\n .sort((left, right) => right.path.length - left.path.length)\n\n return candidates[0]?.row ?? null\n}\n\nfunction readLocator(row: HostWorkspaceRow): z.infer<typeof LocatorSchema> | null {\n try {\n return LocatorSchema.parse(JSON.parse(row.locatorJson))\n }\n catch {\n return null\n }\n}\n\n/** The path itself, with symlinks resolved — or the absolute path, when it is not there to resolve. */\nfunction canonicalPath(path: string): string {\n const absolute = resolve(path)\n try {\n return realpathSync(absolute)\n }\n catch {\n return absolute\n }\n}\n","import type { Command } from 'commander'\n\nimport type { BoardStore } from '../board-store'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport { readIssuePrefix } from '../domain/numbering'\nimport type { BoardOps } from '../ops'\nimport { createBoardOps } from '../ops'\nimport { createPostgresBoardStore } from '../postgres/board-store.postgres'\nimport { createSqliteBoardStore } from '../sqlite/board-store.sqlite'\nimport { openBoardDatabase } from '../sqlite/open-database'\nimport type { Issue, IssueRun } from '../sqlite/schema'\nimport type { BoardTarget } from './db-target'\nimport { resolveDbTarget } from './db-target'\nimport type { CliOutputOptions, CliResult } from './output'\nimport { CliError, EXIT_NOT_RESOLVED, printResult, readFormat } from './output'\nimport { openPostgresBoard } from './postgres-board'\nimport { assertInstalledBoard, assertWritableBoard } from './schema-guard'\nimport { resolveWorkspace } from './workspace'\n\n/**\n * What every board command needs before it can do its one thing: the board\n * file, the workspace, the operations bound to them — and, when it is over, the\n * way its result and its failures reach the caller.\n *\n * Commands are written against this rather than opening databases themselves,\n * so the rules that must hold for all of them hold in one place: a write never\n * touches a database older than this build, the connection is always closed,\n * and a failure always leaves with the exit code the tool promises for it.\n */\n\n/** The options that say which board a command works on, and how it answers. */\nexport interface BoardTargetCommandOptions extends CliOutputOptions {\n db?: string\n databaseUrl?: string\n /**\n * Reach the board as an agent rather than as whoever owns this shell. Not a\n * flag anyone types — `kanbo mcp` sets it, because the client it serves is\n * the agent.\n */\n asAgent?: boolean\n}\n\n/** The same, plus the workspace — which every command but `migrate` and `roles` is about. */\nexport interface BoardCommandOptions extends BoardTargetCommandOptions {\n workspace?: string\n}\n\n/** One open board, and everything a command does its work through. */\nexport interface BoardSession {\n ops: BoardOps\n store: BoardStore\n /** The workspace the command is about, and the key its cards are numbered with. */\n workspace: BoardWorkspaceIdentity\n /**\n * Refuse a write to a board this build cannot speak for. A command declares\n * a write when it opens the session; a server that holds one open for many\n * calls asks again before each of them.\n */\n assertWritable: () => Promise<void>\n}\n\n/** The same, held by a caller that outlives one command and closes it itself. */\nexport interface OpenBoardSession extends BoardSession {\n close: () => Promise<void>\n}\n\n/** Whether the command is going to write to the board. Writes are guarded; reads are not. */\nexport type BoardCommandMode = 'read' | 'write'\n\n/** The two modes a card can be worked in, as `issues.execution_mode` spells them. */\nconst EXECUTION_MODES = ['worktree', 'main'] as const\n\n/** Add the options that name a board and shape the answer, wherever the command sits in the tree. */\nexport function withTargetOptions(command: Command): Command {\n return command\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to work on instead of a board file')\n .option('--json <fields>', 'print only these comma-separated fields, as JSON')\n .option('--format <format>', 'output format: json or pretty')\n}\n\n/** The same, plus the workspace: the options every command about cards carries. */\nexport function withBoardOptions(command: Command): Command {\n return withTargetOptions(command)\n .option('--workspace <nameOrId>', 'workspace the command is about')\n}\n\n/**\n * Open the board the caller named and resolve the workspace it is about.\n *\n * The board stays open until the caller closes it: one command does its one\n * thing and closes, while `kanbo mcp` holds the same session open for as long\n * as a client is talking to it. A failure on the way out closes the board first\n * — a caller that never received a session has nothing to close.\n */\nexport async function openBoardSession(\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n): Promise<OpenBoardSession> {\n const cwd = process.cwd()\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: options.asAgent,\n cwd,\n })\n return target.kind === 'postgres'\n ? await openExternalSession(target, options, cwd)\n : await openFileSession(target, options, mode, cwd)\n}\n\nasync function openFileSession(\n target: Extract<BoardTarget, { kind: 'sqlite' }>,\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n cwd: string,\n): Promise<OpenBoardSession> {\n const board = await openBoardDatabase(target.path)\n try {\n const assertWritable = async (): Promise<void> => {\n assertWritableBoard(board.database, target.owner)\n }\n if (mode === 'write') {\n assertWritableBoard(board.database, target.owner)\n }\n // A file of this package's own holds no `workspaces` table to look up —\n // the same reason an external board resolves from the binding alone.\n const workspace = resolveWorkspace(\n target.owner === 'kanbo' ? { kind: 'file' } : { kind: 'sqlite', database: board.database },\n { explicit: options.workspace, cwd },\n )\n const store = createSqliteBoardStore({ database: () => board.database })\n return { ops: createBoardOps(store), store, workspace, assertWritable, close: async () => board.close() }\n }\n catch (error) {\n board.close()\n throw error\n }\n}\n\n/**\n * The same, on a board that is a database of its own.\n *\n * The guard runs whatever the command came to do. A board file that this build\n * cannot write to is still a board worth reading; a connection string pointing\n * at a database with no board in it is nothing to read at all, and every\n * command would otherwise fail one statement later with the driver's own words.\n */\nasync function openExternalSession(\n target: Extract<BoardTarget, { kind: 'postgres' }>,\n options: BoardCommandOptions,\n cwd: string,\n): Promise<OpenBoardSession> {\n const board = await openPostgresBoard(target.url)\n try {\n const assertWritable = async (): Promise<void> => {\n await assertInstalledBoard(board.database)\n }\n await assertWritable()\n const workspace = resolveWorkspace({ kind: 'postgres' }, { explicit: options.workspace, cwd })\n const store = createPostgresBoardStore({ database: board.database })\n return { ops: createBoardOps(store), store, workspace, assertWritable, close: board.close }\n }\n catch (error) {\n await board.close()\n throw error\n }\n}\n\n/**\n * Open the board, do the one thing, print the result, close the board.\n *\n * The output options are checked before anything is opened: a caller who\n * mistyped `--format` should hear about it instead of writing to the board and\n * then failing to print what they wrote.\n */\nexport async function runBoardCommand(\n options: BoardCommandOptions,\n mode: BoardCommandMode,\n body: (session: BoardSession) => Promise<CliResult>,\n): Promise<void> {\n readFormat(options)\n\n const session = await openBoardSession(options, mode)\n try {\n printResult(await body(session), options)\n }\n finally {\n await session.close()\n }\n}\n\n/** Where a card's work happens. A caller naming anything else is refused, not corrected. */\nexport function parseExecutionMode(value: string): Issue['executionMode'] {\n const mode = EXECUTION_MODES.find(candidate => candidate === value)\n if (!mode) {\n throw new CliError(1, `Unknown execution mode \"${value}\". Use ${EXECUTION_MODES.join(' or ')}.`)\n }\n return mode\n}\n\n/** A count a caller typed, refused rather than quietly rounded when it is not one. */\nexport function parseCount(value: string): number {\n const count = Number(value)\n if (!Number.isInteger(count) || count <= 0) {\n throw new CliError(1, `Expected a positive whole number, got \"${value}\".`)\n }\n return count\n}\n\n/**\n * The card a caller named, in the workspace the command is about.\n *\n * People and agents refer to a card by whatever they have in front of them: the\n * key printed on the board (`MAN-012`), the same key typed without the padding\n * (`MAN-12`), or just the number. All three mean one card in one workspace, and\n * a tool that only accepted the exact key would be wrong about that.\n *\n * Every lookup is scoped to the workspace, keys included. One database holds\n * every project's board, so an unscoped `findById` would let a key pasted from\n * another project — or from an agent that resolved the wrong workspace — move a\n * card on a board the caller is not even looking at.\n */\nexport async function requireCard(session: BoardSession, reference: string): Promise<Issue> {\n const trimmed = reference.trim()\n const byId = await session.store.issues.findInWorkspace(session.workspace.id, trimmed)\n if (byId) {\n return byId\n }\n\n const number = readCardNumber(session, trimmed)\n const byNumber = number === null ? null : await session.store.issues.findByNumber(session.workspace.id, number)\n if (!byNumber) {\n throw new CliError(EXIT_NOT_RESOLVED, `No card \"${reference}\" on this board.`)\n }\n return byNumber\n}\n\n/**\n * The run a caller named, in the workspace the command is about.\n *\n * A run id is opaque — there is no key to read a workspace off — so the card it\n * belongs to answers instead. One database holds every project's board, and a\n * run id pasted from another project would otherwise end a run on a board this\n * caller is not even looking at. The refusal is the one every other command\n * gives for something that is not on this board.\n */\nexport async function requireRun(session: BoardSession, runId: string): Promise<IssueRun> {\n const run = await session.store.runs.findById(runId.trim())\n const card = run ? await session.store.issues.findInWorkspace(session.workspace.id, run.issueId) : null\n if (!run || !card) {\n throw new CliError(EXIT_NOT_RESOLVED, `No run \"${runId}\" on this board.`)\n }\n return run\n}\n\n/**\n * The card number a reference names — but only when it is a reference to this\n * board's own cards.\n *\n * A bare number is one. A key is one only when its stem is this workspace's own\n * prefix: `MAN-12` and `MAN-012` are the same card, while `OTH-001` is another\n * board's key that happens to end in a number this board also uses, and reading\n * the digits off it would quietly hand back the wrong card.\n */\nfunction readCardNumber(session: BoardSession, reference: string): number | null {\n if (/^\\d+$/.test(reference)) {\n return Number(reference)\n }\n\n const separator = reference.lastIndexOf('-')\n const digits = reference.slice(separator + 1)\n if (separator < 0 || !/^\\d+$/.test(digits)) {\n return null\n }\n const prefix = readIssuePrefix(session.workspace)\n return reference.slice(0, separator).toUpperCase() === prefix ? Number(digits) : null\n}\n","/**\n * What a card is called wherever a person or an agent reads it by name.\n *\n * A card written as a description alone is titled with its own key (every\n * writer does that). Named by its key, such a card says nothing — so its name\n * is the first line of its description instead. A browser bundle that does not\n * load this package keeps the same rule on its own, tested with the same\n * examples.\n */\n\n/** How long a description line may run before it is cut with an ellipsis. */\nconst DESCRIPTION_LINE_LIMIT = 120\n\nexport function cardDisplayTitle(card: { id: string, title: string, description?: string | null }): string {\n if (card.title !== card.id) {\n return card.title\n }\n return descriptionFirstLine(card.description) ?? card.title\n}\n\nfunction descriptionFirstLine(description: string | null | undefined): string | null {\n for (const rawLine of (description ?? '').split('\\n')) {\n const line = stripMarkdown(rawLine)\n if (line) {\n return line.length > DESCRIPTION_LINE_LIMIT ? `${line.slice(0, DESCRIPTION_LINE_LIMIT - 1).trimEnd()}…` : line\n }\n }\n return null\n}\n\nfunction stripMarkdown(line: string): string {\n return line\n .replace(/^\\s*(?:>\\s*)+/, '')\n .replace(/^\\s*#{1,6}\\s+/, '')\n .replace(/^\\s*(?:[-*+]|\\d+[.)])\\s+(?:\\[[ x]\\]\\s+)?/i, '')\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/<[^>]+>/g, '')\n .replace(/[*_~`]/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n","import type { BoardActiveRun, BoardRunProjection } from '../board-store'\nimport { cardDisplayTitle } from '../domain/card-display-title'\nimport type { EntryRule } from '../domain/entry-rules'\nimport { readEntryRules } from '../domain/entry-rules'\nimport { toIssueView } from '../domain/issue-view'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { BoardRunView } from '../ops/runs'\nimport type { BoardSprint } from '../ops/sprints'\nimport type { Issue, IssueRun, IssueStatus } from '../sqlite/schema'\n\n/**\n * The board's rows as a command line prints them.\n *\n * A card row carries columns a terminal has no use for — the delegation\n * markers, the context refs, the chat session it came from — and hides the one\n * thing every caller wants, because the column is a foreign key rather than a\n * name. These projections are what `--json` selects fields from, so they are\n * also the shape a script is entitled to rely on.\n */\n\n/** A card, as the command line prints it. */\nexport interface CardView {\n /** The issue key, which is also the row id: `MAN-012`. */\n id: string\n number: number\n title: string\n description: string | null\n /** The column's name, `null` when the card sits in no column. */\n column: string | null\n /** The same column as the slug `kanbo card move` takes. */\n columnSlug: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n /** Every run the card has ever had; the next launch is attempt `attemptCount + 1`. */\n attemptCount: number\n /** The run going on right now, `null` when nobody is working on the card. */\n activeRun: CardActiveRunView | null\n updatedAt: number\n}\n\n/** Every field `kanbo card list|get --json` may ask for, in listing order. */\nexport const CARD_VIEW_FIELDS: readonly (keyof CardView)[] = [\n 'id',\n 'number',\n 'title',\n 'description',\n 'column',\n 'columnSlug',\n 'statusLine',\n 'waitingFor',\n 'priority',\n 'labels',\n 'executionMode',\n 'parentIssueId',\n 'attemptCount',\n 'activeRun',\n 'updatedAt',\n]\n\n/** The run going on right now, as much as a line in a card list needs. */\ninterface CardActiveRunView {\n id: string\n agentName: string\n state: IssueRun['state']\n startedAt: number\n}\n\n/** A column of the board, as the command line prints it. */\nexport interface ColumnView {\n id: string\n name: string\n /** What `kanbo card move` and the prime text call this column. */\n slug: string\n description: string | null\n category: IssueStatus['category']\n order: number\n /** What a card must satisfy before an agent may move it here; empty for nothing. */\n entryRules: EntryRule[]\n}\n\n/** A milestone read as a sprint, as the command line prints it. Dates are unix seconds. */\nexport interface SprintView {\n id: string\n title: string\n status: BoardSprint['status']\n startDate: number | null\n dueDate: number | null\n /** The open sprint running today — at most one per board. */\n current: boolean\n /** Cards not yet in a completed or canceled column: what closing the sprint would carry on. */\n openCards: number\n doneCards: number\n}\n\n/** Every field `kanbo sprint list --json` may ask for, in listing order. */\nexport const SPRINT_VIEW_FIELDS: readonly (keyof SprintView)[] = [\n 'id',\n 'title',\n 'status',\n 'startDate',\n 'dueDate',\n 'current',\n 'openCards',\n 'doneCards',\n]\n\n/** One launch of a card, as the command line prints it. */\nexport interface RunView {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n worktreePath: string | null\n startedAt: number\n endedAt: number | null\n /** Which launch of the card this is, counting from its first — `listRuns` counts them. */\n attempt: number\n chatSessionId: string | null\n /** Where the agent's own log of this run lives, once it said (ruling 7-1): `claude:<id>` or `codex:<id>`, else `null`. */\n externalSessionRef: string | null\n launchedByKind: IssueRun['launchedByKind']\n}\n\n/** Every field `kanbo run start|finish|attach-session --json` may ask for, in listing order. */\nexport const RUN_VIEW_FIELDS: readonly (keyof RunView)[] = [\n 'id',\n 'issueId',\n 'agentName',\n 'state',\n 'executionMode',\n 'branch',\n 'worktreePath',\n 'startedAt',\n 'endedAt',\n 'attempt',\n 'chatSessionId',\n 'externalSessionRef',\n 'launchedByKind',\n]\n\nexport function projectCard(card: Issue, columns: IssueStatus[], runs: BoardRunProjection): CardView {\n const view = toIssueView(card)\n const column = columns.find(candidate => candidate.id === card.statusId) ?? null\n\n return {\n id: view.id,\n number: view.number,\n title: view.title,\n description: view.description,\n column: column?.name ?? null,\n columnSlug: column ? normalizeStatusName(column.name) : null,\n statusLine: view.statusLine,\n waitingFor: view.waitingFor,\n priority: view.priority,\n labels: view.labels,\n executionMode: view.executionMode,\n parentIssueId: view.parentIssueId,\n attemptCount: runs.attemptCount,\n activeRun: projectCardActiveRun(runs.activeRun),\n updatedAt: view.updatedAt,\n }\n}\n\n/** The run going on right now, trimmed to what a card line needs — not the agent id, the branch or the session behind it. */\nfunction projectCardActiveRun(run: BoardActiveRun | null): CardActiveRunView | null {\n return run === null\n ? null\n : { id: run.id, agentName: run.agentName, state: run.state, startedAt: run.startedAt }\n}\n\nexport function projectColumn(column: IssueStatus): ColumnView {\n return {\n id: column.id,\n name: column.name,\n slug: normalizeStatusName(column.name),\n description: column.description,\n category: column.category,\n order: column.order,\n entryRules: readEntryRules(column),\n }\n}\n\nexport function projectSprint(sprint: BoardSprint): SprintView {\n return {\n id: sprint.id,\n title: sprint.title,\n status: sprint.status,\n startDate: sprint.startDate,\n dueDate: sprint.dueDate,\n current: sprint.current,\n openCards: sprint.cards.open,\n doneCards: sprint.cards.done,\n }\n}\n\nexport function projectRun(run: BoardRunView): RunView {\n return {\n id: run.id,\n issueId: run.issueId,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n worktreePath: run.worktreePath,\n startedAt: run.startedAt,\n endedAt: run.endedAt,\n attempt: run.attempt,\n chatSessionId: run.chatSessionId,\n externalSessionRef: run.externalSessionRef,\n launchedByKind: run.launchedByKind,\n }\n}\n\n/** One card per line: the key, the column it is in, and what it is called (`cardDisplayTitle`). */\nexport function describeCards(cards: CardView[]): string {\n if (cards.length === 0) {\n return 'No cards'\n }\n\n const keyWidth = Math.max(...cards.map(card => card.id.length))\n const columnWidth = Math.max(...cards.map(card => (card.column ?? '—').length))\n return cards\n .map(card => `${card.id.padEnd(keyWidth)} ${(card.column ?? '—').padEnd(columnWidth)} ${cardDisplayTitle(card)}`)\n .join('\\n')\n}\n\n/** The whole card, for a person who asked about exactly one. */\nexport function describeCard(card: CardView): string {\n const lines = [\n `${card.id} ${card.column ?? '—'} ${card.executionMode}`,\n cardDisplayTitle(card),\n ]\n if (card.description) {\n lines.push('', card.description)\n }\n const facts = [\n card.statusLine ? `status line: ${card.statusLine}` : null,\n card.waitingFor === 'human' ? 'waiting for a person' : null,\n card.labels.length > 0 ? `labels: ${card.labels.join(', ')}` : null,\n card.priority === 'none' ? null : `priority: ${card.priority}`,\n card.parentIssueId ? `parent: ${card.parentIssueId}` : null,\n ].filter(fact => fact !== null)\n if (facts.length > 0) {\n lines.push('', ...facts)\n }\n return lines.join('\\n')\n}\n\n/** One column per line, in board order, with the slug a card is moved by. */\nexport function describeColumns(columns: ColumnView[]): string {\n if (columns.length === 0) {\n return 'This board has no columns yet'\n }\n\n const slugWidth = Math.max(...columns.map(column => column.slug.length))\n return columns\n .map(column => [\n `${column.slug.padEnd(slugWidth)} ${column.name}`,\n column.description ? ` — ${column.description}` : '',\n column.entryRules.length > 0 ? ` [requires: ${column.entryRules.join(', ')}]` : '',\n ].join(''))\n .join('\\n')\n}\n\n/** A unix-seconds date as the UTC day it falls on, `—` for none. */\nfunction formatDay(seconds: number | null): string {\n return seconds === null ? '—' : new Date(seconds * 1000).toISOString().slice(0, 10)\n}\n\n/** One sprint per line: `*` on the current one, its dates, open and done cards, title and id. */\nexport function describeSprints(sprints: SprintView[]): string {\n if (sprints.length === 0) {\n return 'This board has no milestones yet'\n }\n\n return sprints\n .map(sprint => [\n sprint.current ? '*' : ' ',\n `${formatDay(sprint.startDate)} → ${formatDay(sprint.dueDate)}`,\n sprint.status.padEnd(6),\n `${sprint.openCards} open / ${sprint.doneCards} done`,\n `${sprint.title} (${sprint.id})`,\n ].join(' '))\n .join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { CARD_VIEW_FIELDS, describeCard, projectCard } from '../view'\n\ninterface ApproveOptions extends BoardCommandOptions {\n comment?: string\n}\n\n/**\n * `kanbo approve` — a person accepting the work on a card.\n *\n * The shell is checked before the board is even opened: a refusal has to leave\n * the database exactly as it found it, and the check costs nothing. The board\n * refuses a non-person actor of its own accord too — this is the same rule said\n * earlier, with a message a person can act on.\n */\nexport function registerApproveCommand(program: Command): void {\n withBoardOptions(program\n .command('approve')\n .description('accept the work on a card that is waiting for you')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--comment <text>', 'what to say alongside the decision'))\n .action(async (reference: string, options: ApproveOptions) => {\n const actor = requireHumanActor('approve')\n\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const approved = await session.ops.approve(existing.id, { comment: options.comment }, actor)\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(approved.id),\n ])\n const view = projectCard(approved, columns, runs)\n return { value: view, text: `Approved ${view.id}\\n\\n${describeCard(view)}`, fields: CARD_VIEW_FIELDS }\n })\n })\n}\n","import type { KanboCapabilities, KanboCapabilityCommand, KanboCapabilityTool } from './manifest'\n\n/**\n * The same manifest `buildCapabilitiesManifest` returns, as Markdown a person\n * can read and an agent can paste into its own `CLAUDE.md` or `AGENTS.md`.\n *\n * Nothing here decides what the board can do — it only lays the manifest's own\n * fields out under headings. `kanbo capabilities` prints this by default, and\n * `kanbo mcp` serves it as `kanbo://capabilities.md` beside the JSON.\n */\n\n/**\n * A table cell, safe from a pipe inside the text breaking the row it sits in —\n * the same escape the package README's own tables use for a command that\n * spells `\\|` between two states.\n */\nfunction cell(text: string): string {\n return text.replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ').trim()\n}\n\nfunction renderStorages(manifest: KanboCapabilities): string[] {\n const rows = manifest.storages.map(storage =>\n `| \\`${storage.kind}\\` | ${cell(storage.label)} | ${cell(storage.description)} | ${storage.schemaEpoch} |`)\n return [\n '## Storages',\n '',\n '| Kind | Label | Description | Schema epoch |',\n '| --- | --- | --- | --- |',\n ...rows,\n ]\n}\n\nfunction renderTool(tool: KanboCapabilityTool): string {\n return `| \\`${tool.name}\\` | \\`${cell(tool.cli.kanbo)}\\` | ${cell(tool.description)} |`\n}\n\nfunction renderTools(manifest: KanboCapabilities): string[] {\n return [\n '## Tools',\n '',\n 'Full JSON Schema for each tool\\'s input is in the JSON manifest (`kanbo capabilities --json` or '\n + '`kanbo://capabilities`) — this table is the tools themselves and what to type instead of calling one.',\n '',\n '| Tool | `kanbo` | Does |',\n '| --- | --- | --- |',\n ...manifest.tools.map(renderTool),\n ]\n}\n\nfunction renderCommand(command: KanboCapabilityCommand): string {\n const options = command.options.map(option => `\\`${option.flags}\\`${option.mandatory ? ' (required)' : ''}`).join(', ')\n const humanOnly = command.humanOnly ? ' **Person only.**' : ''\n return `| \\`kanbo ${command.path}\\` | ${cell(command.description)}${humanOnly} | ${options ? cell(options) : '—'} |`\n}\n\nfunction renderCommands(manifest: KanboCapabilities): string[] {\n return [\n '## Commands',\n '',\n '| Command | Does | Options |',\n '| --- | --- | --- |',\n ...manifest.commands.map(renderCommand),\n ]\n}\n\nfunction renderRules(manifest: KanboCapabilities): string[] {\n return ['## Rules', '', ...manifest.rules.map(rule => `- ${rule}`)]\n}\n\nfunction renderEntryRules(manifest: KanboCapabilities): string[] {\n return [\n '## Column entry rules',\n '',\n 'What a column may ask of a card before an agent moves it in. Which columns ask for which is per board: '\n + '`kanbo columns list` or `kanbo_columns`.',\n '',\n ...manifest.entryRules.map(entry => `- \\`${entry.rule}\\` — ${entry.description}`),\n ]\n}\n\nfunction renderCanonicalStrings(manifest: KanboCapabilities): string[] {\n const rows = Object.entries(manifest.canonicalStrings)\n .map(([name, value]) => `| ${cell(name)} | \\`${cell(value)}\\` |`)\n return [\n '## Canonical strings',\n '',\n 'What the board writes for you, on a status line or in a system comment. A value ending in a space or `: ` '\n + 'is a prefix — the rest of the line is whatever the write was about.',\n '',\n '| Name | Value |',\n '| --- | --- |',\n ...rows,\n ]\n}\n\nfunction renderRoles(manifest: KanboCapabilities): string[] {\n return [\n '## Roles',\n '',\n `- Person: \\`${manifest.roles.person}\\``,\n `- Agent: \\`${manifest.roles.agent}\\``,\n '',\n 'What the database itself refuses the agent role, on an external board, whichever client sends the write:',\n '',\n ...manifest.roles.refusals.map(refusal => `- ${refusal}`),\n ]\n}\n\nfunction renderLimits(manifest: KanboCapabilities): string[] {\n return ['## Limits', '', ...manifest.limits.map(limit => `- ${limit}`)]\n}\n\n/** The manifest, laid out under one heading per field — stable and diffable, never a snapshot to update by hand. */\nexport function renderCapabilitiesMarkdown(manifest: KanboCapabilities): string {\n return [\n '# Kanbo capabilities',\n '',\n `\\`${manifest.package.name}\\` ${manifest.package.version} — capabilities manifest version ${manifest.version}.`,\n '',\n ...renderStorages(manifest),\n '',\n ...renderTools(manifest),\n '',\n ...renderCommands(manifest),\n '',\n ...renderRules(manifest),\n '',\n ...renderEntryRules(manifest),\n '',\n ...renderCanonicalStrings(manifest),\n '',\n ...renderRoles(manifest),\n '',\n ...renderLimits(manifest),\n '',\n ].join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport { buildCapabilitiesManifest } from '../../capabilities/manifest'\nimport { renderCapabilitiesMarkdown } from '../../capabilities/markdown'\nimport { CliError } from '../output'\n\n/**\n * `kanbo capabilities` — the board's tools, commands, rules and limits, for\n * whoever is deciding how to work it rather than working a card right now.\n *\n * No board is opened. Everything this command prints is board-independent —\n * a board's columns are its own and `kanbo prime` is where those live — so\n * there is nothing here to fail on a missing `--db` or a stale schema, and\n * none of the ordinary board flags apply. `kanbo mcp` serves the same two\n * shapes as resources, `kanbo://capabilities` and `kanbo://capabilities.md`,\n * from the same `buildCapabilitiesManifest`.\n */\ninterface CapabilitiesOptions {\n json?: boolean\n markdown?: boolean\n}\n\nexport function registerCapabilitiesCommand(program: Command): void {\n program\n .command('capabilities')\n .description('the board\\'s tools, commands, rules and limits — machine-readable, no board needed')\n .option('--json', 'print the manifest as JSON')\n .option('--markdown', 'print the manifest as Markdown (the default)')\n .action((options: CapabilitiesOptions) => {\n if (options.json && options.markdown) {\n throw new CliError(1, 'Pass --json or --markdown, not both.')\n }\n\n const manifest = buildCapabilitiesManifest()\n console.log(options.json ? JSON.stringify(manifest, null, 2) : renderCapabilitiesMarkdown(manifest))\n })\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCardWrite, UpdateCardInput } from '../../ops/cards'\nimport type { Issue, IssuePullRequest } from '../../sqlite/schema'\nimport { createCliActor, createPlacementActor } from '../actor'\nimport type { BoardCommandOptions, BoardSession } from '../command'\nimport { parseCount, parseExecutionMode, requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport type { CardView } from '../view'\nimport { CARD_VIEW_FIELDS, describeCard, describeCards, projectCard } from '../view'\n\n/**\n * `kanbo card …` — the cards themselves.\n *\n * Every write here is filed under the shell that made it and goes through the\n * board's own operations, so a card an agent moved from a terminal reads on the\n * board exactly like one moved in the app: the same field history, the same\n * status line, the same board version bump for whoever is watching.\n */\n\nconst PRIORITIES = ['none', 'low', 'medium', 'high', 'urgent'] as const\n\ninterface ListOptions extends BoardCommandOptions {\n column?: string\n limit?: number\n}\n\ninterface CreateOptions extends BoardCommandOptions {\n title?: string\n description?: string\n column?: string\n parent?: string\n executionMode?: Issue['executionMode']\n}\n\ninterface UpdateOptions extends BoardCommandOptions {\n title?: string\n description?: string\n priority?: Issue['priority']\n labels?: string[]\n executionMode?: Issue['executionMode']\n}\n\ninterface TextOptions extends BoardCommandOptions {\n text: string\n}\n\ninterface ContentOptions extends BoardCommandOptions {\n content: string\n}\n\ninterface WaitApprovalOptions extends BoardCommandOptions {\n text?: string\n}\n\nexport function registerCardCommands(program: Command): void {\n const card = program\n .command('card')\n .description('the cards on this board')\n\n withBoardOptions(card\n .command('list')\n .description('cards in board order')\n .option('--column <column>', 'only cards in this column, by slug, name or id')\n .option('--limit <count>', 'how many cards to print', parseCount))\n .action(async (options: ListOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const columns = await session.ops.listColumns(session.workspace.id)\n const wanted = options.column\n ? await session.ops.findColumn(session.workspace.id, options.column)\n : null\n if (options.column && !wanted) {\n throw new CliError(1, `This board has no column \"${options.column}\".`)\n }\n\n const rows = (await session.store.issues.listInBoardOrder(session.workspace.id))\n .filter(row => !wanted || row.statusId === wanted.id)\n .slice(0, options.limit ?? Number.POSITIVE_INFINITY)\n const runs = await session.ops.readBoardProjectionForIssues(rows.map(row => row.id))\n const cards = rows.map((row, index) => projectCard(row, columns, runs[index]))\n return { value: cards, text: describeCards(cards), fields: CARD_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(card\n .command('get')\n .description('one card, by key or number')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12'))\n .action(async (reference: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n return await presentCard(session, await requireCard(session, reference))\n })\n })\n\n withBoardOptions(card\n .command('create')\n .description('put a new card on the board')\n .option('--title <title>', 'what the card is called; the card\\'s own key when absent')\n .option('--description <text>', 'what the card is about')\n .option('--column <column>', 'the column to put it in, by slug, name or id')\n .option('--parent <card>', 'the card this one belongs under')\n .option('--execution-mode <mode>', 'worktree or main', parseExecutionMode))\n .action(async (options: CreateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const parent = options.parent ? await requireCard(session, options.parent) : null\n const created = await session.ops.createCard({\n workspace: session.workspace,\n title: options.title,\n description: options.description,\n statusName: options.column,\n parentIssueId: parent?.id,\n executionMode: options.executionMode,\n }, createPlacementActor())\n return await presentCard(session, created)\n })\n })\n\n withBoardOptions(card\n .command('update')\n .description('change a card\\'s fields')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--title <title>', 'what the card is called')\n .option('--description <text>', 'what the card is about')\n .option('--priority <priority>', `one of ${PRIORITIES.join(', ')}`, parsePriority)\n .option('--labels <labels>', 'comma-separated labels, replacing the ones the card has', parseLabels)\n .option('--execution-mode <mode>', 'worktree or main', parseExecutionMode))\n .action(async (reference: string, options: UpdateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const input = readUpdateInput(options)\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.updateCard(existing.id, input, createPlacementActor()))\n })\n })\n\n withBoardOptions(card\n .command('move')\n .description('move a card to another column')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<column>', 'the column, by slug, name or id'))\n .action(async (reference: string, column: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.moveCard(existing.id, column, createPlacementActor()))\n })\n })\n\n withBoardOptions(card\n .command('status-line')\n .description('say what the card is doing right now')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--text <text>', 'one sentence, present tense'))\n .action(async (reference: string, options: TextOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n return await presentCard(session, await session.ops.setStatusLine(existing.id, options.text, createCliActor()))\n })\n })\n\n withBoardOptions(card\n .command('comment')\n .description('write a finding, a decision or a question on the card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--content <text>', 'what to say'))\n .action(async (reference: string, options: ContentOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const comment = await session.ops.addComment({ issueId: existing.id, content: options.content }, createCliActor())\n return {\n value: { id: comment.id, issueId: comment.issueId, content: comment.content, createdAt: comment.createdAt },\n text: `Commented on ${existing.id}`,\n }\n })\n })\n\n withBoardOptions(card\n .command('wait-approval')\n .description('hand the card to a person and end your turn')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .option('--text <text>', 'the status line to leave, saying what you need'))\n .action(async (reference: string, options: WaitApprovalOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const waiting = await session.ops.waitApproval(existing.id, { statusLine: options.text }, createCliActor())\n return await presentCard(session, waiting)\n })\n })\n\n registerPullRequestCommands(card)\n}\n\n/** The fields a pull-request link prints, and the ones `--json` may name. */\nconst PULL_REQUEST_FIELDS = ['id', 'issueId', 'owner', 'repo', 'number', 'url', 'createdAt'] as const\n\ntype PullRequestView = Pick<IssuePullRequest, typeof PULL_REQUEST_FIELDS[number]>\n\nfunction projectPullRequest(link: IssuePullRequest): PullRequestView {\n return {\n id: link.id,\n issueId: link.issueId,\n owner: link.owner,\n repo: link.repo,\n number: link.number,\n url: link.url,\n createdAt: link.createdAt,\n }\n}\n\nfunction describePullRequest(link: PullRequestView): string {\n return `${link.id} ${link.owner}/${link.repo}#${link.number} ${link.url}`\n}\n\n/**\n * `kanbo card pr …` — the pull requests a card names (ruling 4-1).\n *\n * Linking is all an agent does: what the pull request is — open, merged, green\n * — reaches the card as comments from whoever watches GitHub, so nothing here\n * writes one.\n */\nfunction registerPullRequestCommands(card: Command): void {\n const pr = card\n .command('pr')\n .description('the pull requests a card names')\n\n withBoardOptions(pr\n .command('add')\n .description('link a pull request to the card; linking it again changes nothing')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<url>', 'the pull request, as https://github.com/<owner>/<repo>/pull/<n> or <owner>/<repo>#<n>'))\n .action(async (reference: string, url: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const link = projectPullRequest(await session.ops.linkPullRequest(existing.id, url, createCliActor()))\n return {\n value: link,\n text: `Linked ${link.owner}/${link.repo}#${link.number} to ${existing.id} (${link.id})`,\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n\n withBoardOptions(pr\n .command('list')\n .description('the pull requests linked to the card, in the order they were linked')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12'))\n .action(async (reference: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const existing = await requireCard(session, reference)\n const links = (await session.ops.listPullRequests(existing.id)).map(projectPullRequest)\n return {\n value: links,\n text: links.length === 0 ? `${existing.id} names no pull requests.` : links.map(describePullRequest).join('\\n'),\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n\n withBoardOptions(pr\n .command('remove')\n .description('unlink a pull request from the card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .argument('<linkId>', 'the link, by the id `kanbo card pr list` prints'))\n .action(async (reference: string, linkId: string, options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const link = projectPullRequest(await session.ops.unlinkPullRequest(existing.id, linkId, createCliActor()))\n return {\n value: link,\n text: `Unlinked ${link.owner}/${link.repo}#${link.number} from ${existing.id}`,\n fields: PULL_REQUEST_FIELDS,\n }\n })\n })\n}\n\n/**\n * The card as both shapes the output contract offers. A card a person placed\n * in a column whose entry rules it does not meet (ruling 6-5) also leaves one\n * warning line per unmet rule on stderr, so the card prints as usual.\n */\nasync function presentCard(session: BoardSession, card: BoardCardWrite): Promise<{ value: CardView, text: string, fields: readonly string[] }> {\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(card.id),\n ])\n const view = projectCard(card, columns, runs)\n for (const item of card.unmetRules ?? []) {\n console.error(`kanbo: warning: ${view.id} entered \"${view.column ?? '—'}\" without ${item.rule}: ${item.detail}`)\n }\n return { value: view, text: describeCard(view), fields: CARD_VIEW_FIELDS }\n}\n\n/**\n * The fields `card update` was asked to change. A command that names none of\n * them is refused rather than obeyed: an empty update still bumps the board\n * version, and every reader would be told the board changed when it did not.\n */\nfunction readUpdateInput(options: UpdateOptions): UpdateCardInput {\n const input: UpdateCardInput = {}\n if (options.title !== undefined) {\n input.title = options.title\n }\n if (options.description !== undefined) {\n input.description = options.description\n }\n if (options.priority !== undefined) {\n input.priority = options.priority\n }\n if (options.labels !== undefined) {\n input.labels = options.labels\n }\n if (options.executionMode !== undefined) {\n input.executionMode = options.executionMode\n }\n\n if (Object.keys(input).length === 0) {\n throw new CliError(1, 'Nothing to update. Pass --title, --description, --priority, --labels or --execution-mode.')\n }\n return input\n}\n\nfunction parsePriority(value: string): Issue['priority'] {\n const priority = PRIORITIES.find(candidate => candidate === value)\n if (!priority) {\n throw new CliError(1, `Unknown priority \"${value}\". Use one of ${PRIORITIES.join(', ')}.`)\n }\n return priority\n}\n\nfunction parseLabels(value: string): string[] {\n return value.split(',').map(label => label.trim()).filter(label => label.length > 0)\n}\n","import type { Command } from 'commander'\n\nimport { ENTRY_RULES } from '../../domain/entry-rules'\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { describeColumns, projectColumn } from '../view'\n\ninterface DescribeColumnOptions extends BoardCommandOptions {\n text: string\n}\n\ninterface ColumnRulesOptions extends BoardCommandOptions {\n require?: string\n clear?: boolean\n}\n\n/**\n * `kanbo columns …` — the board's columns.\n *\n * Listing them is how a person finds the slug a card is moved by; describing\n * one is how a person tells every agent that will ever read this board when a\n * card belongs in it, in one line they only have to write once. Setting its\n * rules is how a person keeps an agent from moving a card in before it is\n * ready — a person's command, refused in an agent's shell. Adding the\n * standard set brings a board made before them up to date without touching the\n * columns its owner made.\n */\nexport function registerColumnsCommands(program: Command): void {\n const columns = program\n .command('columns')\n .description('the columns of this board')\n\n withBoardOptions(columns\n .command('list')\n .description('every column in board order, with the slug a card is moved by'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const views = (await session.ops.listColumns(session.workspace.id)).map(projectColumn)\n return { value: views, text: describeColumns(views) }\n })\n })\n\n withBoardOptions(columns\n .command('describe')\n .description('say in one line when a card belongs in a column')\n .argument('<column>', 'the column, by slug, name or id')\n .requiredOption('--text <text>', 'what the column means'))\n .action(async (column: string, options: DescribeColumnOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const updated = await session.ops.describeColumn(session.workspace.id, column, options.text)\n const view = projectColumn(updated)\n return { value: view, text: describeColumns([view]) }\n })\n })\n\n withBoardOptions(columns\n .command('rules')\n .description('set what a card must satisfy before an agent may move it into a column')\n .argument('<column>', 'the column, by slug, name or id')\n .option('--require <rules>', `comma-separated: ${ENTRY_RULES.join(', ')}`)\n .option('--clear', 'ask nothing of a card entering the column'))\n .action(async (column: string, options: ColumnRulesOptions) => {\n // Checked before the board is opened, like `approve`: a refusal leaves\n // the board exactly as it found it.\n const actor = requireHumanActor('columns rules')\n const rules = readRequestedRules(options)\n\n await runBoardCommand(options, 'write', async (session) => {\n const updated = await session.ops.setColumnEntryRules(session.workspace.id, column, rules, actor)\n const view = projectColumn(updated)\n return { value: view, text: describeColumns([view]) }\n })\n })\n\n withBoardOptions(columns\n .command('add-standard')\n .description('add any of the standard columns this board is missing'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const { statuses, added } = await session.ops.addStandardColumns(session.workspace.id)\n const views = statuses.map(projectColumn)\n return {\n value: { added, columns: views },\n text: added.length === 0\n ? 'This board already has every standard column'\n : `Added ${added.join(', ')}\\n\\n${describeColumns(views)}`,\n }\n })\n })\n}\n\n/** The rules `--require` names, or `null` for `--clear` — exactly one of the two. */\nfunction readRequestedRules(options: ColumnRulesOptions): string[] | null {\n if (options.clear && options.require !== undefined) {\n throw new CliError(1, 'Pass --require or --clear, not both.')\n }\n if (options.clear) {\n return null\n }\n if (options.require === undefined) {\n throw new CliError(1, `Pass --require ${ENTRY_RULES.join(',')} (any of them) or --clear.`)\n }\n return options.require.split(',').map(rule => rule.trim()).filter(Boolean)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst here = dirname(fileURLToPath(import.meta.url))\n\n/**\n * The package root's `package.json`, found relative to the file asking for\n * it — the same reason `migrations-path.ts` resolves a candidate rather than\n * a fixed `../`: this file's own compiled output does not always land at the\n * same depth. One level up is the package root from `src/package-version.ts`\n * in a checkout and from `dist/cli.cjs`'s single bundle; two levels up is the\n * same root from a `dist/lib/*` chunk, because the library build nests every\n * entry — and the shared chunk this file becomes part of — one directory\n * deeper than the CLI's one bundle.\n */\nfunction resolvePackageJsonPath(): string {\n const oneUp = join(here, '..', 'package.json')\n return existsSync(oneUp) ? oneUp : join(here, '..', '..', 'package.json')\n}\n\n/**\n * The version a build wrote in. `tsdown` replaces this identifier with the\n * literal from `package.json` in both builds (`tsdown.cli.config.ts`,\n * `tsdown.lib.config.ts`), because a built copy may live where no\n * `package.json` exists at all — an app that embeds the command may ship `dist/` alone.\n */\ndeclare const __KANBO_PACKAGE_VERSION__: string | undefined\n\n/**\n * This build's own version, one source for the CLI's `--version`, the MCP\n * server's reported version and the capabilities manifest's\n * `package.version`. A build carries it as a literal; running from source\n * (tests, `tsx`) reads the package's own `package.json` instead.\n */\nexport const KANBO_PACKAGE_VERSION: string\n = typeof __KANBO_PACKAGE_VERSION__ === 'string'\n ? __KANBO_PACKAGE_VERSION__\n : (JSON.parse(readFileSync(resolvePackageJsonPath(), 'utf-8')) as { version: string }).version\n","import { maskDatabaseUrls } from '../domain/database-url'\nimport type { ColumnRefusalDetails } from '../domain/entry-rules'\nimport { describeColumnRefusal } from '../domain/entry-rules'\nimport { BoardError } from '../domain/errors'\nimport { CliError } from './output'\n\n/**\n * What a failed command says, and what it leaves with.\n *\n * One place, because a failure is output as much as a result is: the exit code\n * a caller may branch on, the sentence a person reads, and the chain underneath\n * it that says what actually went wrong.\n */\n\n/** How far down a chain of causes is still worth reading. */\nconst CAUSE_DEPTH_LIMIT = 5\n\n/**\n * What to say about a failure, and what to leave with.\n *\n * A `CliError` already carries both. A `BoardError` is a rule of the board the\n * caller broke: its code is the honest name of what happened, and its details\n * say which value did it, so both are printed rather than translated into a\n * sentence that could drift away from the rule. The one exception is a card\n * held at a column's door: its details are a list, and a person reads it as\n * one line per rule the card does not meet yet (`describeColumnRefusal`).\n *\n * Whatever it is, its causes are printed under it. A driver's failure arrives\n * wrapped — `Failed query: create schema …` with the connection refused\n * underneath — and the wrapper alone tells a person nothing they can act on.\n * Every line goes through the masking on its way out, because a message this\n * tool did not write is free to carry the whole connection string in it.\n */\nexport function describeFailure(error: unknown): { message: string, exitCode: number } {\n const { message, exitCode } = readFailure(error)\n const causes = describeCauses(error, 1)\n return { message: maskDatabaseUrls([message, ...causes].join('\\n')), exitCode }\n}\n\nfunction readFailure(error: unknown): { message: string, exitCode: number } {\n if (error instanceof CliError) {\n return { message: error.message, exitCode: error.exitCode }\n }\n if (error instanceof BoardError && error.code === 'board_column_rules_unmet') {\n return { message: describeColumnRefusal(error.details as unknown as ColumnRefusalDetails), exitCode: 1 }\n }\n if (error instanceof BoardError) {\n const details = error.details ? ` ${JSON.stringify(error.details)}` : ''\n return { message: `${error.code}${details}`, exitCode: 1 }\n }\n return { message: error instanceof Error ? error.message : String(error), exitCode: 1 }\n}\n\n/**\n * The chain under a failure, one indented line per link.\n *\n * Bounded, because `cause` is an ordinary property and nothing stops it\n * pointing back at the error it came from; a chain longer than this has stopped\n * explaining anything anyway.\n */\nfunction describeCauses(error: unknown, depth: number): string[] {\n const cause = error instanceof Error ? (error as { cause?: unknown }).cause : undefined\n if (cause === undefined || cause === null || depth > CAUSE_DEPTH_LIMIT) {\n return []\n }\n const message = cause instanceof Error ? cause.message : String(cause)\n return [`${' '.repeat(depth)}Caused by: ${message}`, ...describeCauses(cause, depth + 1)]\n}\n","import { existsSync, readFileSync } from 'node:fs'\n\nimport {\n COMMENT_RULE,\n MOVE_CARD_RULE,\n OWN_SESSION_RULE,\n PERSON_ONLY_RULE,\n STATUS_LINE_RULE,\n SUBTASK_RULE,\n WAIT_FOR_PERSON_RULE,\n} from '../../ops/agent-rules'\nimport type { FileChange } from './file-change'\n\n/**\n * The instruction block kanbo writes into an agent's standing instructions,\n * and the three things done to it: written, found again, taken out.\n *\n * Two versions of the block exist. The project block says \"this project is on\n * a board\"; the global one, for a person's user-level file, says \"a project\n * with `.kanbo/` is on a board\" and binds nothing — one user file serves every\n * project, and most of them have no board.\n */\n\n/** The markers that make the block something this tool can find again. */\nexport const INSTRUCTION_START = '<!-- KANBO_START -->'\nexport const INSTRUCTION_END = '<!-- KANBO_END -->'\n\n/**\n * What to run and when, and nothing about how to do the work: the method is\n * the person's own instructions to their agent. The rules are the sentences\n * `ops/agent-rules.ts` holds for every surface, with the command beside each.\n */\nconst RULE_LINES = [\n '- Take a card from `kanbo ready`.',\n `- ${MOVE_CARD_RULE} (\\`kanbo card move <id> <column>\\`)`,\n `- ${STATUS_LINE_RULE} (\\`kanbo card status-line <id> --text \"...\"\\`)`,\n `- ${SUBTASK_RULE} (\\`kanbo card create --description \"...\" --parent <id>\\`)`,\n `- ${COMMENT_RULE} (\\`kanbo card comment <id> --content \"...\"\\`)`,\n `- ${WAIT_FOR_PERSON_RULE} (\\`kanbo card wait-approval <id>\\`)`,\n `- ${PERSON_ONLY_RULE}`,\n `- ${OWN_SESSION_RULE} (\\`kanbo run start <id> --agent <name> --session <ref>\\`)`,\n '- Run `kanbo capabilities` for the full list of tools, commands and rules.',\n]\n\n/** The block `kanbo init --instructions …` writes into a project's own file. */\nexport const INSTRUCTION_BLOCK = [\n INSTRUCTION_START,\n '## Kanbo board',\n '',\n 'Work on this project is tracked on a kanbo board. Before starting a task run `kanbo prime` —'\n + ' it prints the board\\'s columns and what each one means.',\n '',\n ...RULE_LINES,\n INSTRUCTION_END,\n].join('\\n')\n\n/** The block `kanbo init --global` writes into a person's user-level file. */\nexport const GLOBAL_INSTRUCTION_BLOCK = [\n INSTRUCTION_START,\n '## Kanbo boards',\n '',\n 'A project with a `.kanbo/` directory tracks its work on a kanbo board. In such a project, before starting a task'\n + ' run `kanbo prime` (or call the `kanbo_prime` tool) — it prints the board\\'s columns and what each one means.'\n + ' A project without `.kanbo/` has no board: ignore this section there, and do not create one — a person sets a'\n + ' board up with `kanbo init`.',\n '',\n ...RULE_LINES,\n INSTRUCTION_END,\n].join('\\n')\n\n/**\n * The block in that file, once.\n *\n * A file that already carries the markers has its block replaced rather than\n * appended to, so a current block changes nothing at all and an outdated one is\n * brought forward. Everything outside the markers is the person's own writing\n * and is never touched.\n */\nexport function planInstructionBlock(path: string, block: string): FileChange {\n const existing = readText(path)\n const next = withBlock(existing, block)\n return { path, next: existing === next ? null : next }\n}\n\n/**\n * The file without the block, or no change when it carries none. The file\n * itself stays even when nothing is left in it: kanbo does not record which\n * files it created, and a person's file is not this tool's to delete.\n */\nexport function planInstructionBlockRemoval(path: string): FileChange {\n const existing = readText(path)\n const bounds = existing === null ? null : findBlock(existing)\n if (existing === null || bounds === null) {\n return { path, next: null }\n }\n const before = existing.slice(0, bounds.start).replace(/\\s*$/, '')\n const after = existing.slice(bounds.end).trim()\n const joined = [before, after].filter(part => part.length > 0).join('\\n\\n')\n return { path, next: joined ? `${joined}\\n` : '' }\n}\n\n/** The block a file carries, markers included, or `null` when it carries none. */\nexport function readInstructionBlock(path: string): string | null {\n const existing = readText(path)\n const bounds = existing === null ? null : findBlock(existing)\n return existing === null || bounds === null ? null : existing.slice(bounds.start, bounds.end)\n}\n\nfunction withBlock(existing: string | null, block: string): string {\n if (existing === null || !existing.trim()) {\n return `${block}\\n`\n }\n const bounds = findBlock(existing)\n if (bounds) {\n return existing.slice(0, bounds.start) + block + existing.slice(bounds.end)\n }\n return `${existing.replace(/\\s*$/, '')}\\n\\n${block}\\n`\n}\n\nfunction findBlock(text: string): { start: number, end: number } | null {\n const start = text.indexOf(INSTRUCTION_START)\n const end = text.indexOf(INSTRUCTION_END)\n return start >= 0 && end > start ? { start, end: end + INSTRUCTION_END.length } : null\n}\n\nfunction readText(path: string): string | null {\n return existsSync(path) ? readFileSync(path, 'utf8') : null\n}\n","import { accessSync, constants, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, isAbsolute, join } from 'node:path'\n\n/**\n * Where each agent tool keeps the files kanbo writes into, for one project and\n * for the person's whole machine.\n *\n * Resolved on every call rather than at import, so `HOME` and `CODEX_HOME` set\n * by a test (or a person) are the ones obeyed. Each path was checked against\n * the tool's own documentation; `docs/agents.md` lists the pages.\n */\n\n/** The tools whose user-level instruction file `kanbo init --global` can write. */\nexport const GLOBAL_INSTRUCTION_CLIENTS = ['claude', 'codex', 'gemini'] as const\nexport type GlobalInstructionClient = typeof GLOBAL_INSTRUCTION_CLIENTS[number]\n\n/** The files a project's agents read their standing instructions from. */\nexport const PROJECT_INSTRUCTION_FILES = { claude: 'CLAUDE.md', agents: 'AGENTS.md' } as const\nexport type ProjectInstructionTarget = keyof typeof PROJECT_INSTRUCTION_FILES\n\n/** The tools that can be told about the board's MCP server. */\nexport const MCP_CLIENTS = ['claude', 'codex', 'cursor'] as const\nexport type McpClient = typeof MCP_CLIENTS[number]\n\n/** Where Codex keeps its global files: `$CODEX_HOME`, else `~/.codex`. */\nexport function codexHome(): string {\n return process.env.CODEX_HOME?.trim() || join(homedir(), '.codex')\n}\n\n/** The user-level instruction file of each tool. */\nexport function globalInstructionPath(client: GlobalInstructionClient): string {\n switch (client) {\n case 'claude': return join(homedir(), '.claude', 'CLAUDE.md')\n case 'codex': return join(codexHome(), 'AGENTS.md')\n case 'gemini': return join(homedir(), '.gemini', 'GEMINI.md')\n }\n}\n\n/**\n * The user-level MCP configuration of each tool. Claude Code's is\n * `~/.claude.json`, its own state file: kanbo reads it but never writes it —\n * `claude mcp add` and `claude mcp remove` do.\n */\nexport function globalMcpConfigPath(client: McpClient): string {\n switch (client) {\n case 'claude': return join(homedir(), '.claude.json')\n case 'codex': return join(codexHome(), 'config.toml')\n case 'cursor': return join(homedir(), '.cursor', 'mcp.json')\n }\n}\n\n/** The project-level MCP configuration of each tool. */\nexport function projectMcpConfigPath(projectDir: string, client: McpClient): string {\n switch (client) {\n case 'claude': return join(projectDir, '.mcp.json')\n case 'codex': return join(projectDir, '.codex', 'config.toml')\n case 'cursor': return join(projectDir, '.cursor', 'mcp.json')\n }\n}\n\n/**\n * Every executable of that name on `PATH`, in the order a shell would find\n * them. A path with a separator in it is looked at directly, as a shell does.\n */\nexport function findAllOnPath(command: string): string[] {\n if (isAbsolute(command) || command.includes('/')) {\n return isExecutableFile(command) ? [command] : []\n }\n const extensions = process.platform === 'win32'\n ? ['', ...(process.env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';')]\n : ['']\n const found: string[] = []\n for (const directory of (process.env.PATH ?? '').split(delimiter).filter(Boolean)) {\n for (const extension of extensions) {\n const candidate = join(directory, command + extension)\n if (isExecutableFile(candidate) && !found.includes(candidate)) {\n found.push(candidate)\n }\n }\n }\n return found\n}\n\n/** The executable a shell would start for that command, or `null` when there is none. */\nexport function findOnPath(command: string): string | null {\n return findAllOnPath(command)[0] ?? null\n}\n\nfunction isExecutableFile(path: string): boolean {\n try {\n if (!statSync(path).isFile()) {\n return false\n }\n accessSync(path, constants.X_OK)\n return true\n }\n catch {\n return false\n }\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { CliError } from '../output'\nimport type { FileChange } from './file-change'\nimport { findOnPath, globalMcpConfigPath } from './paths'\n\n/**\n * The board's MCP server entry, in the two formats the tools keep it in: the\n * `mcpServers` JSON map Claude Code (`.mcp.json`) and Cursor (`mcp.json`) read,\n * and the `[mcp_servers.<name>]` TOML table Codex reads (`config.toml`). Each\n * is planned before anything is written, found again by the doctor, and taken\n * out by `kanbo uninstall` without touching any other server.\n */\n\n/** The name the board's MCP server is registered under, in every tool. */\nexport const MCP_SERVER_NAME = 'kanbo'\n\n/** How every registration kanbo writes starts the server. */\nexport const MCP_COMMAND = 'kanbo'\nexport const MCP_ARGS = ['mcp'] as const\n\n/** The line that registers the server with Claude Code for every project of this user. */\nexport const CLAUDE_USER_ADD = ['claude', 'mcp', 'add', '--scope', 'user', MCP_SERVER_NAME, '--', MCP_COMMAND, ...MCP_ARGS]\n/** The line that takes that registration out again. */\nexport const CLAUDE_USER_REMOVE = ['claude', 'mcp', 'remove', MCP_SERVER_NAME, '--scope', 'user']\n\n/**\n * A Codex table header naming this server or one of its subtables\n * (`[mcp_servers.kanbo.env]`), in every spelling TOML allows for the key: bare,\n * double-quoted and single-quoted all name the same server, and a check that\n * only knew the bare one would write a second table beside a registration the\n * person already has.\n */\nconst CODEX_HEADER_PATTERN = new RegExp(\n String.raw`^\\s*\\[\\s*mcp_servers\\s*\\.\\s*(?:${MCP_SERVER_NAME}|\"${MCP_SERVER_NAME}\"|'${MCP_SERVER_NAME}')\\s*(\\.[^\\]]*)?\\]`,\n)\n\n/** Any table header, which is where a table's keys end. */\nconst TOML_HEADER_PATTERN = /^\\s*\\[/\n\n/** A `command = \"...\"` line, either string spelling. */\nconst TOML_COMMAND_PATTERN = /^\\s*command\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/m\n\n/**\n * The `mcpServers` map, merged rather than replaced: the file may already\n * describe servers that have nothing to do with this board. A `kanbo` entry\n * that is already there belongs to the person — it may point at a different\n * binary or carry arguments of its own — and is left as it is.\n */\nexport function planJsonMcpServer(path: string): FileChange {\n const existing = readJsonConfig(path)\n if (existing.mcpServers && MCP_SERVER_NAME in existing.mcpServers) {\n return { path, next: null }\n }\n const next = {\n ...existing,\n mcpServers: { ...existing.mcpServers, [MCP_SERVER_NAME]: { type: 'stdio', command: MCP_COMMAND, args: [...MCP_ARGS] } },\n }\n return { path, next: `${JSON.stringify(next, null, 2)}\\n` }\n}\n\n/**\n * The same file without the `kanbo` entry. Everything else stays, the\n * `mcpServers` object included when nothing else is left in it: kanbo does not\n * record which files it created, so it leaves the structure rather than guess.\n * A file it cannot read as a JSON object is not touched at all.\n */\nexport function planJsonMcpServerRemoval(path: string): FileChange {\n const existing = readJsonIfObject(path)\n if (!existing || !isPlainObject(existing.mcpServers) || !(MCP_SERVER_NAME in existing.mcpServers)) {\n return { path, next: null }\n }\n const { [MCP_SERVER_NAME]: _removed, ...others } = existing.mcpServers\n return { path, next: `${JSON.stringify({ ...existing, mcpServers: others }, null, 2)}\\n` }\n}\n\n/**\n * The command a JSON registration starts the server with: `undefined` when the\n * file registers no `kanbo` server, `null` when it does but names no command\n * (a URL server, say).\n */\nexport function readJsonMcpCommand(path: string): string | null | undefined {\n const existing = readJsonIfObject(path)\n const servers = existing?.mcpServers\n if (!isPlainObject(servers) || !isPlainObject(servers[MCP_SERVER_NAME])) {\n return undefined\n }\n const command = servers[MCP_SERVER_NAME].command\n return typeof command === 'string' && command.trim() ? command.trim() : null\n}\n\n/** Codex keeps its servers in TOML tables, so the table is appended rather than merged. */\nexport function planCodexMcpServer(path: string): FileChange {\n const existing = existsSync(path) ? readFileSync(path, 'utf8') : ''\n if (findCodexSections(existing).length > 0) {\n return { path, next: null }\n }\n const table = `[mcp_servers.${MCP_SERVER_NAME}]\\ncommand = \"${MCP_COMMAND}\"\\nargs = ${JSON.stringify(MCP_ARGS)}\\n`\n return { path, next: existing.trim() ? `${existing.replace(/\\s*$/, '')}\\n\\n${table}` : table }\n}\n\n/**\n * The same file without the `kanbo` table and its subtables. A table runs from\n * its header to the next header, so every other table — and every line before\n * the first — is kept byte for byte.\n */\nexport function planCodexMcpServerRemoval(path: string): FileChange {\n if (!existsSync(path)) {\n return { path, next: null }\n }\n const existing = readFileSync(path, 'utf8')\n const sections = findCodexSections(existing)\n if (sections.length === 0) {\n return { path, next: null }\n }\n const lines = existing.split('\\n')\n const removed = new Set(sections.flatMap(({ start, end }) => range(start, end)))\n const kept = lines.filter((_, index) => !removed.has(index))\n const next = kept.join('\\n').replace(/^\\n+/, '')\n return { path, next: next.trim() ? `${next.replace(/\\s*$/, '')}\\n` : '' }\n}\n\n/** The command the Codex table starts the server with, with the same `undefined`/`null` meaning as the JSON one. */\nexport function readCodexMcpCommand(path: string): string | null | undefined {\n if (!existsSync(path)) {\n return undefined\n }\n const text = readFileSync(path, 'utf8')\n const main = findCodexSections(text).find(section => section.main)\n if (!main) {\n return undefined\n }\n const body = text.split('\\n').slice(main.start, main.end).join('\\n')\n const match = TOML_COMMAND_PATTERN.exec(body)\n return match ? (match[1] ?? match[2] ?? null) : null\n}\n\n/** Is the server registered with Claude Code for every project of this user (`~/.claude.json`, read only)? */\nexport function readClaudeUserMcpCommand(): string | null | undefined {\n return readJsonMcpCommand(globalMcpConfigPath('claude'))\n}\n\n/** What happened to a registration that goes through the `claude` command. */\nexport interface ClaudeCliOutcome {\n /** `ran` when `claude` did it, `manual` when the person has to run the line themselves. */\n state: 'ran' | 'manual'\n command: string\n /** Why it is the person's to run, when it is. */\n reason?: string\n}\n\n/**\n * Run `claude mcp …` when Claude Code is here to run it, and hand the line to\n * the person when it is not — or when it failed, with the first line it said.\n * `~/.claude.json` is Claude Code's own state file and is never edited by hand.\n */\nexport function runClaudeMcp(argv: readonly string[]): ClaudeCliOutcome {\n const command = argv.join(' ')\n const executable = findOnPath(argv[0]!)\n if (!executable) {\n return { state: 'manual', command, reason: `${argv[0]} is not on PATH` }\n }\n const result = spawnSync(executable, argv.slice(1), { encoding: 'utf8', timeout: 60_000 })\n if (result.status === 0) {\n return { state: 'ran', command }\n }\n const said = `${result.stderr ?? ''}${result.stdout ?? ''}`.trim().split('\\n')[0]\n return {\n state: 'manual',\n command,\n reason: result.error ? result.error.message : `${argv[0]} exited ${result.status ?? 'on a signal'}${said ? `: ${said}` : ''}`,\n }\n}\n\n/** The line ranges (`end` exclusive) of the `kanbo` table and its subtables. */\nfunction findCodexSections(text: string): { start: number, end: number, main: boolean }[] {\n const lines = text.split('\\n')\n const sections: { start: number, end: number, main: boolean }[] = []\n for (let index = 0; index < lines.length; index += 1) {\n const match = CODEX_HEADER_PATTERN.exec(lines[index]!)\n if (!match) {\n continue\n }\n let end = index + 1\n while (end < lines.length && !TOML_HEADER_PATTERN.test(lines[end]!)) {\n end += 1\n }\n sections.push({ start: index, end, main: match[1] === undefined })\n index = end - 1\n }\n return sections\n}\n\nfunction range(start: number, end: number): number[] {\n return Array.from({ length: end - start }, (_, offset) => start + offset)\n}\n\n/**\n * The tool's configuration file as an object to merge into — or a refusal.\n *\n * A file that is valid JSON but not an object (a list, a string, `null`) is\n * something this command has no idea how to merge into, and spreading it would\n * quietly throw the person's file away. The file is left exactly as it is and\n * they are told which one to look at.\n */\nfunction readJsonConfig(path: string): { mcpServers?: Record<string, unknown> } {\n if (!existsSync(path)) {\n return {}\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'))\n }\n catch {\n throw new CliError(1, `${path} is not valid JSON. Fix or remove it, then run kanbo init again.`)\n }\n if (!isPlainObject(parsed)) {\n throw new CliError(1, `${path} does not hold a JSON object. Fix or remove it, then run kanbo init again.`)\n }\n if (parsed.mcpServers !== undefined && !isPlainObject(parsed.mcpServers)) {\n throw new CliError(1, `${path} has an \"mcpServers\" that is not an object. Fix it, then run kanbo init again.`)\n }\n return parsed as { mcpServers?: Record<string, unknown> }\n}\n\n/** The file as a JSON object, or `null` when it is missing or anything else — for reading and removal, which never refuse. */\nfunction readJsonIfObject(path: string): Record<string, unknown> | null {\n if (!existsSync(path)) {\n return null\n }\n try {\n const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))\n return isPlainObject(parsed) ? parsed : null\n }\n catch {\n return null\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { existsSync, realpathSync, statSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'\n\nimport { maskDatabaseUrl } from '../domain/database-url'\nimport { BoardError } from '../domain/errors'\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport { assertBoardSchema, openBoardDatabase } from '../sqlite/open-database'\nimport { isAgentShell } from './actor'\nimport type { KanboBinding } from './binding'\nimport { BINDING_FILE_PATH, readBinding } from './binding'\nimport type { BoardTarget } from './db-target'\nimport { resolveDbTarget } from './db-target'\nimport { describeFailure } from './failure'\nimport { openPostgresBoard } from './postgres-board'\nimport { assertInstalledBoard, FILE_SCHEMA_OUTDATED_MESSAGE, SCHEMA_OUTDATED_MESSAGE } from './schema-guard'\nimport { GLOBAL_INSTRUCTION_BLOCK, INSTRUCTION_BLOCK, readInstructionBlock } from './setup/instructions'\nimport { MCP_COMMAND, readCodexMcpCommand, readJsonMcpCommand } from './setup/mcp-config'\nimport type { McpClient } from './setup/paths'\nimport {\n findAllOnPath,\n findOnPath,\n GLOBAL_INSTRUCTION_CLIENTS,\n globalInstructionPath,\n globalMcpConfigPath,\n MCP_CLIENTS,\n PROJECT_INSTRUCTION_FILES,\n projectMcpConfigPath,\n} from './setup/paths'\n\n/**\n * `kanbo doctor` — the checks for the ways kanbo can be set up wrong without\n * any command saying so: a binding nobody can read, a board older than this\n * build, an instruction block written by an older kanbo, an MCP registration\n * whose command a client cannot start, a server that does not answer, another\n * `kanbo` earlier on `PATH`, a person's own shell marked as an agent's.\n *\n * Every check says `ok`, `warn` or `fail`, and every one that is not `ok`\n * carries the command or edit that fixes it. Nothing here writes anything.\n */\n\nexport type DoctorStatus = 'ok' | 'warn' | 'fail'\n\nexport interface DoctorFinding {\n check: string\n status: DoctorStatus\n detail: string\n /** What to do about it, on every finding that is not `ok`. */\n fix?: string\n}\n\nexport interface DoctorInput {\n /** The folder the doctor was run in; the project is looked for at or above it. */\n cwd: string\n /** The script this `kanbo` is running from, to tell it apart from another one on `PATH`. */\n self: string | null\n /** How long the MCP server may take to answer `initialize`. */\n handshakeTimeoutMs: number\n}\n\n/** How long an external board may take to answer before the doctor gives up on it. */\nconst POSTGRES_TIMEOUT_MS = 5_000\n\n/** A project folder: the one with `.kanbo/` in it, and the binding that folder has, if any. */\nexport interface ProjectState {\n root: string\n binding: KanboBinding | null\n}\n\n/** One MCP registration found in a tool's configuration. */\ninterface Registration {\n client: McpClient\n path: string\n command: string | null\n}\n\nexport async function collectDoctorFindings(input: DoctorInput): Promise<DoctorFinding[]> {\n const findings: DoctorFinding[] = [\n { check: 'version', status: 'ok', detail: `kanbo ${KANBO_PACKAGE_VERSION}${input.self ? ` (${input.self})` : ''}` },\n checkPath(input.self),\n ]\n\n const project = findKanboProject(input.cwd)\n findings.push(checkProject(project))\n const target = project?.binding ? resolveTarget(project.root) : null\n if (project?.binding && !project.binding.databaseUrl) {\n findings.push(await checkSqlite())\n }\n if (project?.binding && findings.every(finding => finding.check !== 'sqlite' || finding.status === 'ok')) {\n findings.push(...await checkBoard(target))\n }\n\n findings.push(...checkInstructions(project?.root ?? null))\n\n const registrations = findRegistrations(project?.root ?? null)\n findings.push(...checkRegistrations(registrations))\n if (project?.binding && !(target instanceof Error)) {\n findings.push(await checkHandshake(registrations, project.root, input.handshakeTimeoutMs))\n }\n\n findings.push(checkActor())\n return findings\n}\n\n/** Is the `kanbo` a client would start this one? */\nfunction checkPath(self: string | null): DoctorFinding {\n const all = findAllOnPath(MCP_COMMAND)\n if (all.length === 0) {\n return {\n check: 'path',\n status: 'warn',\n detail: 'No kanbo on PATH: a shell or MCP client that runs `kanbo` will not find it.',\n fix: 'Install kanbo globally (see docs/installation.md), or put the directory that holds it on PATH.',\n }\n }\n const first = all[0]!\n if (self && realPath(first) !== realPath(self)) {\n return {\n check: 'path',\n status: 'warn',\n detail: `${first} comes first on PATH and is not this kanbo (${self}): agents and MCP clients start that one.`,\n fix: 'Remove the other install, or put this one first on PATH.',\n }\n }\n return { check: 'path', status: 'ok', detail: `kanbo on PATH: ${first}` }\n}\n\n/** The nearest folder at or above `cwd` with a `.kanbo/` directory, and its binding. */\nexport function findKanboProject(cwd: string): ProjectState | null {\n let directory = resolve(cwd)\n while (true) {\n const kanboDirectory = join(directory, dirname(BINDING_FILE_PATH))\n if (existsSync(kanboDirectory) && statSync(kanboDirectory).isDirectory()) {\n return { root: directory, binding: readBinding(join(directory, BINDING_FILE_PATH)) }\n }\n const parent = dirname(directory)\n if (parent === directory) {\n return null\n }\n directory = parent\n }\n}\n\nfunction checkProject(project: ProjectState | null): DoctorFinding {\n if (!project) {\n return {\n check: 'binding',\n status: 'warn',\n detail: 'Not inside a kanbo project (no .kanbo/ here or above); board checks are skipped.',\n fix: 'Run kanbo init --file in a project to give it a board.',\n }\n }\n const path = join(project.root, BINDING_FILE_PATH)\n if (!project.binding) {\n return {\n check: 'binding',\n status: 'fail',\n detail: existsSync(path)\n ? `${path} is not a binding this build can read.`\n : `${join(project.root, dirname(BINDING_FILE_PATH))} has no ${BINDING_FILE_PATH.split(/[\\\\/]/).pop()}: nothing says which board this project is on.`,\n fix: `Run kanbo init (with --file, or the --database-url you used) in ${project.root} to bind it again.`,\n }\n }\n return { check: 'binding', status: 'ok', detail: `${path}: workspace ${project.binding.workspaceId}` }\n}\n\nfunction resolveTarget(root: string): BoardTarget | Error {\n try {\n return resolveDbTarget({ cwd: root })\n }\n catch (error) {\n return error instanceof Error ? error : new Error(String(error))\n }\n}\n\n/** The native module a board file needs, loaded on a database of its own in memory. */\nasync function checkSqlite(): Promise<DoctorFinding> {\n try {\n (await openBoardDatabase(':memory:')).close()\n return { check: 'sqlite', status: 'ok', detail: 'better-sqlite3 loads.' }\n }\n catch (error) {\n return {\n check: 'sqlite',\n status: 'fail',\n detail: `better-sqlite3 cannot be loaded: ${describeFailure(error).message}`,\n fix: 'Install better-sqlite3 next to kanbo (npm install -g better-sqlite3 for a global kanbo).',\n }\n }\n}\n\n/** The board itself: it opens, and its schema is this build's. */\nasync function checkBoard(target: BoardTarget | Error | null): Promise<DoctorFinding[]> {\n if (target === null) {\n return []\n }\n if (target instanceof Error) {\n return [{ check: 'board', status: 'fail', detail: describeFailure(target).message, fix: 'Run kanbo init in the project to bind it to a board.' }]\n }\n if (target.kind === 'postgres') {\n return [await checkPostgresBoard(target.url)]\n }\n\n const board = await openBoardDatabase(target.path)\n try {\n assertBoardSchema(board.database)\n return [{ check: 'board', status: 'ok', detail: `${target.path} opens; its schema is current.` }]\n }\n catch (error) {\n const outdated = error instanceof BoardError && error.code === 'board_schema_outdated'\n const ownFile = target.owner === 'kanbo'\n return [{\n check: 'board',\n status: 'fail',\n detail: outdated\n ? `${target.path}: ${ownFile ? FILE_SCHEMA_OUTDATED_MESSAGE : SCHEMA_OUTDATED_MESSAGE}`\n : `${target.path}: ${describeFailure(error).message}`,\n fix: ownFile ? 'Run kanbo migrate.' : 'Start the app this database belongs to, so it applies its migrations.',\n }]\n }\n finally {\n board.close()\n }\n}\n\nasync function checkPostgresBoard(url: string): Promise<DoctorFinding> {\n const where = maskDatabaseUrl(url)\n let timer: NodeJS.Timeout | undefined\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`no answer within ${POSTGRES_TIMEOUT_MS / 1000}s`)), POSTGRES_TIMEOUT_MS)\n })\n try {\n const board = await Promise.race([openPostgresBoard(url), timeout])\n try {\n await Promise.race([assertInstalledBoard(board.database), timeout])\n }\n finally {\n void board.close()\n }\n return { check: 'board', status: 'ok', detail: `${where} answers; its schema is current.` }\n }\n catch (error) {\n return { check: 'board', status: 'fail', detail: `${where}: ${describeFailure(error).message}`, fix: 'Check the connection string, then run kanbo migrate.' }\n }\n finally {\n clearTimeout(timer)\n }\n}\n\n/**\n * Every instruction block kanbo could have written, compared with the one this\n * build writes. Only files that carry a block are reported: a file without one\n * is not a fault, but having no block anywhere is worth a warning.\n */\nfunction checkInstructions(root: string | null): DoctorFinding[] {\n const findings: DoctorFinding[] = []\n if (root) {\n for (const [target, name] of Object.entries(PROJECT_INSTRUCTION_FILES)) {\n const path = join(root, name)\n const block = readInstructionBlock(path)\n if (block !== null) {\n findings.push(blockFinding(path, block === INSTRUCTION_BLOCK, `cd ${root} && kanbo init --instructions ${target} --yes`))\n }\n }\n }\n for (const client of GLOBAL_INSTRUCTION_CLIENTS) {\n const path = globalInstructionPath(client)\n const block = readInstructionBlock(path)\n if (block !== null) {\n findings.push(blockFinding(path, block === GLOBAL_INSTRUCTION_BLOCK, `kanbo init --global --instructions ${client}`))\n }\n }\n if (findings.length === 0) {\n findings.push({\n check: 'instructions',\n status: 'warn',\n detail: 'No kanbo instruction block in this project\\'s or your own agent files: agents learn the rules only from the MCP server.',\n fix: 'Run kanbo init --instructions claude in the project, or kanbo init --global.',\n })\n }\n return findings\n}\n\nfunction blockFinding(path: string, current: boolean, fix: string): DoctorFinding {\n return current\n ? { check: 'instructions', status: 'ok', detail: `${path}: block is current.` }\n : { check: 'instructions', status: 'fail', detail: `${path}: block differs from the one this kanbo writes (stale).`, fix: `Run ${fix} to rewrite it.` }\n}\n\n/** Every `kanbo` registration in the project's and the person's own tool configuration. */\nfunction findRegistrations(root: string | null): Registration[] {\n const found: Registration[] = []\n for (const client of MCP_CLIENTS) {\n const paths = [...(root ? [projectMcpConfigPath(root, client)] : []), globalMcpConfigPath(client)]\n for (const path of paths) {\n const command = client === 'codex' ? readCodexMcpCommand(path) : readJsonMcpCommand(path)\n if (command !== undefined) {\n found.push({ client, path, command })\n }\n }\n }\n return found\n}\n\nfunction checkRegistrations(registrations: Registration[]): DoctorFinding[] {\n if (registrations.length === 0) {\n return [{\n check: 'mcp',\n status: 'warn',\n detail: 'No MCP client has the kanbo server registered.',\n fix: 'Run kanbo init --mcp claude,codex,cursor in the project, or kanbo init --global.',\n }]\n }\n return registrations.map(({ client, path, command }) => {\n const check = `mcp:${client}`\n if (command === null) {\n return { check, status: 'warn', detail: `${path}: the kanbo entry names no command.`, fix: `Set \"command\" to kanbo in ${path}.` }\n }\n const resolved = findOnPath(command)\n return resolved\n ? { check, status: 'ok', detail: `${path}: starts ${command} (${resolved}).` }\n : {\n check,\n status: 'fail',\n detail: `${path}: starts ${command}, which is not on PATH — the client cannot start the server.`,\n fix: `Install kanbo globally, or change the command in ${path} to a path that exists.`,\n }\n })\n}\n\n/**\n * Start the server a client would start, from the project folder, and ask it\n * `initialize`. It has to call itself `kanbo` and send its instructions.\n */\nasync function checkHandshake(registrations: Registration[], root: string, timeoutMs: number): Promise<DoctorFinding> {\n const command = registrations.map(entry => entry.command && findOnPath(entry.command)).find(Boolean) ?? findOnPath(MCP_COMMAND)\n if (!command) {\n return { check: 'mcp:handshake', status: 'warn', detail: 'No kanbo to start, so the MCP server was not tried.', fix: 'Put kanbo on PATH.' }\n }\n\n const transport = new StdioClientTransport({ command, args: ['mcp'], cwd: root, env: stringEnvironment(), stderr: 'pipe' })\n let stderr = ''\n transport.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n const client = new Client({ name: 'kanbo-doctor', version: KANBO_PACKAGE_VERSION })\n try {\n await client.connect(transport, { timeout: timeoutMs })\n const name = client.getServerVersion()?.name\n const instructions = client.getInstructions()\n if (name !== 'kanbo' || !instructions?.trim()) {\n return {\n check: 'mcp:handshake',\n status: 'fail',\n detail: `${command} mcp answered as ${name ?? 'nothing'}${instructions?.trim() ? '' : ', without instructions'}.`,\n fix: 'Put a current kanbo first on PATH.',\n }\n }\n return { check: 'mcp:handshake', status: 'ok', detail: `${command} mcp answers initialize as kanbo, with instructions.` }\n }\n catch (error) {\n const said = stderr.trim().split('\\n')[0]\n return {\n check: 'mcp:handshake',\n status: 'fail',\n detail: `${command} mcp did not answer initialize: ${said || describeFailure(error).message}`,\n fix: 'Run kanbo mcp in the project yourself to see why it does not start.',\n }\n }\n finally {\n await client.close().catch(() => {})\n }\n}\n\nfunction checkActor(): DoctorFinding {\n if (isAgentShell()) {\n return {\n check: 'actor',\n status: 'warn',\n detail: 'This shell says it belongs to an agent (KANBO_ACTOR_KIND=agent): approve and return refuse here.',\n fix: 'If this is your own terminal, remove KANBO_ACTOR_KIND from your shell profile.',\n }\n }\n return { check: 'actor', status: 'ok', detail: 'This shell is a person\\'s.' }\n}\n\n/** The environment as the SDK wants it: every value a string, so `KANBO_*` reaches the server as it would from a client. */\nfunction stringEnvironment(): Record<string, string> {\n return Object.fromEntries(Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined))\n}\n\nfunction realPath(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n","import type { Command } from 'commander'\n\nimport { KANBO_PACKAGE_VERSION } from '../../package-version'\nimport type { DoctorFinding } from '../doctor'\nimport { collectDoctorFindings } from '../doctor'\nimport { CliError } from '../output'\n\n/**\n * `kanbo doctor` — check this install, this project and the agent tools that\n * reach it, and say what to do about anything wrong. Exits `1` when a check\n * fails; a warning alone does not.\n */\ninterface DoctorOptions {\n json?: boolean\n}\n\n/** How long `kanbo mcp` may take to answer `initialize`. */\nconst HANDSHAKE_TIMEOUT_MS = 10_000\n\nconst MARKS: Record<DoctorFinding['status'], string> = { ok: 'ok ', warn: 'warn', fail: 'FAIL' }\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('check this install, this project\\'s binding and board, instruction blocks and MCP registrations')\n .option('--json', 'print the findings as JSON')\n .action(async (options: DoctorOptions) => {\n const findings = await collectDoctorFindings({\n cwd: process.cwd(),\n self: process.argv[1] ?? null,\n handshakeTimeoutMs: HANDSHAKE_TIMEOUT_MS,\n })\n\n if (options.json) {\n console.log(JSON.stringify({ version: KANBO_PACKAGE_VERSION, findings }, null, 2))\n }\n else {\n for (const finding of findings) {\n console.log(`${MARKS[finding.status]} ${finding.check.padEnd(14)} ${finding.detail}`)\n if (finding.fix) {\n console.log(`${' '.repeat(21)}fix: ${finding.fix}`)\n }\n }\n }\n\n const failed = findings.filter(finding => finding.status === 'fail').length\n if (failed > 0) {\n throw new CliError(1, `kanbo doctor: ${failed} check${failed === 1 ? '' : 's'} failed.`)\n }\n })\n}\n","import { join } from 'node:path'\n\n/**\n * Where a board file of a project's own goes when nobody names a path —\n * `kanbo init --file` writes it here, and an app that attaches a project's\n * board file can import the same constant rather than spelling it again.\n */\nexport const DEFAULT_BOARD_FILE_PATH = join('.kanbo', 'board.db')\n","import { CliError } from '../output'\n\n/**\n * Show a plan of file changes and ask before carrying it out.\n *\n * `--yes` is the answer given in advance. A shell with nobody at its terminal\n * cannot be asked, so it is refused rather than taken as a yes: these commands\n * write into a person's own configuration, which is the kind of thing a person\n * says yes to. When the caller asked for machine output, stdout belongs to the\n * result and the plan goes to stderr.\n */\nexport async function confirmPlan(input: {\n preview: string\n question: string\n yes: boolean | undefined\n machineOutput: boolean\n /** The command to name in the refusal, e.g. `kanbo init --global`. */\n command: string\n}): Promise<boolean> {\n const print = input.machineOutput ? console.error : console.log\n print(input.preview)\n if (input.yes) {\n return true\n }\n if (!isInteractive()) {\n throw new CliError(1, `Nothing was changed. Run ${input.command} again with --yes to do this without being asked.`)\n }\n const prompts = await import('@clack/prompts')\n const answer = await prompts.confirm({ message: input.question })\n return answer === true\n}\n\n/** Is there a person on the other end of this shell to ask? */\nexport function isInteractive(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY)\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\n/**\n * One file kanbo may write, worked out before anything is written.\n *\n * Every setup command — `kanbo init`, `kanbo init --global`, `kanbo uninstall`\n * — first works out what each file would say afterwards, so the person can be\n * shown the whole plan and asked once, and a file that already says it is\n * reported as `unchanged` rather than rewritten.\n */\nexport interface FileChange {\n path: string\n /** The file's contents afterwards, or `null` when it already says this. */\n next: string | null\n}\n\n/** What happened to one file this command may write. */\nexport interface FileOutcome {\n path: string\n /** `written` when the file changed, `unchanged` when it already said this. */\n state: 'written' | 'unchanged'\n}\n\n/** Write the change, creating the directory it lives in, and say what happened. */\nexport function applyFileChange(change: FileChange): FileOutcome {\n if (change.next === null) {\n return { path: change.path, state: 'unchanged' }\n }\n mkdirSync(dirname(change.path), { recursive: true })\n writeFileSync(change.path, change.next)\n return { path: change.path, state: 'written' }\n}\n","import type { CliResult } from '../output'\nimport { CliError, printResult, readFormat } from '../output'\nimport { confirmPlan } from '../setup/confirm'\nimport type { FileChange } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { GLOBAL_INSTRUCTION_BLOCK, planInstructionBlock } from '../setup/instructions'\nimport {\n CLAUDE_USER_ADD,\n planCodexMcpServer,\n planJsonMcpServer,\n readClaudeUserMcpCommand,\n runClaudeMcp,\n} from '../setup/mcp-config'\nimport type { GlobalInstructionClient, McpClient } from '../setup/paths'\nimport { GLOBAL_INSTRUCTION_CLIENTS, globalInstructionPath, globalMcpConfigPath, MCP_CLIENTS } from '../setup/paths'\nimport type { InitOptions } from './init'\n\n/**\n * `kanbo init --global` — this person's own agent tools, told about kanbo once\n * for every project.\n *\n * It binds no board and creates none: the block it writes says \"a project with\n * `.kanbo/` is on a board\", and a project becomes one with a plain `kanbo init`.\n * The MCP registrations are the same `kanbo mcp` every project registration\n * starts, which resolves the board from the folder the client starts it in.\n *\n * Every change is worked out first and shown; nothing is written until the\n * person says yes (or said `--yes`), and a file that already says it is\n * reported `unchanged`.\n */\n\n/** Written when `--instructions` is not given. Gemini is asked for by name. */\nconst DEFAULT_INSTRUCTION_CLIENTS: GlobalInstructionClient[] = ['claude', 'codex']\n\n/** The flags that are about binding a project, which `--global` does not do. */\nconst PROJECT_ONLY_FLAGS = [\n ['file', '--file'],\n ['db', '--db'],\n ['databaseUrl', '--database-url'],\n ['workspace', '--workspace'],\n ['board', '--board'],\n ['identifier', '--identifier'],\n ['agentUrl', '--agent-url'],\n] as const satisfies readonly (readonly [keyof InitOptions, string])[]\n\n/** What happened to one MCP registration. */\ninterface GlobalMcpOutcome {\n client: McpClient\n path: string\n /** `manual` when the person has to run `command` themselves. */\n state: 'written' | 'unchanged' | 'manual'\n command?: string\n reason?: string\n}\n\ninterface GlobalInstructionOutcome {\n client: GlobalInstructionClient\n path: string\n state: 'written' | 'unchanged'\n}\n\nexport async function initGlobal(options: InitOptions): Promise<void> {\n const format = readFormat(options)\n const given = PROJECT_ONLY_FLAGS.filter(([key]) => options[key] !== undefined).map(([, flag]) => flag)\n if (given.length > 0) {\n throw new CliError(1, `--global binds no board; ${given.join(', ')} ${given.length === 1 ? 'is' : 'are'} for a project. `\n + 'Run kanbo init in the project for that.')\n }\n\n const instructionClients = parseGlobalInstructions(options.instructions)\n const mcpClients = options.mcp ?? [...MCP_CLIENTS]\n\n const instructionChanges = instructionClients.map(client => ({\n client,\n change: planInstructionBlock(globalInstructionPath(client), GLOBAL_INSTRUCTION_BLOCK),\n }))\n const fileMcpChanges = mcpClients.filter(client => client !== 'claude').map(client => ({\n client,\n change: planGlobalMcpFile(client),\n }))\n const claudeNeeded = mcpClients.includes('claude') && readClaudeUserMcpCommand() === undefined\n\n const pending = [...instructionChanges, ...fileMcpChanges].filter(({ change }) => change.next !== null)\n let confirmed = true\n if (pending.length > 0 || claudeNeeded) {\n confirmed = await confirmPlan({\n preview: describePlan(pending.map(({ change }) => change), claudeNeeded),\n question: 'Make these changes?',\n yes: options.yes,\n machineOutput: Boolean(options.json || format),\n command: 'kanbo init --global',\n })\n }\n if (!confirmed) {\n throw new CliError(1, 'Nothing was changed.')\n }\n\n const instructions: GlobalInstructionOutcome[] = instructionChanges.map(({ client, change }) => ({\n client,\n ...applyFileChange(change),\n }))\n const mcp: GlobalMcpOutcome[] = mcpClients.map((client) => {\n if (client === 'claude') {\n return registerWithClaude(claudeNeeded)\n }\n const planned = fileMcpChanges.find(entry => entry.client === client)!\n return { client, ...applyFileChange(planned.change) }\n })\n\n printResult(describeResult(instructions, mcp), options)\n}\n\nfunction planGlobalMcpFile(client: Exclude<McpClient, 'claude'>): FileChange {\n const path = globalMcpConfigPath(client)\n return client === 'codex' ? planCodexMcpServer(path) : planJsonMcpServer(path)\n}\n\n/** Claude Code's user-scope registration goes through its own command, never an edit of `~/.claude.json`. */\nfunction registerWithClaude(needed: boolean): GlobalMcpOutcome {\n const path = globalMcpConfigPath('claude')\n if (!needed) {\n return { client: 'claude', path, state: 'unchanged' }\n }\n const outcome = runClaudeMcp(CLAUDE_USER_ADD)\n return outcome.state === 'ran'\n ? { client: 'claude', path, state: 'written', command: outcome.command }\n : { client: 'claude', path, state: 'manual', command: outcome.command, reason: outcome.reason }\n}\n\nfunction describePlan(changes: FileChange[], claudeNeeded: boolean): string {\n const lines = ['kanbo init --global will:']\n for (const change of changes) {\n lines.push(` write ${change.path}`)\n }\n if (claudeNeeded) {\n lines.push(` run ${CLAUDE_USER_ADD.join(' ')}`)\n }\n lines.push('No board is created. Narrow this with --instructions and --mcp.')\n return lines.join('\\n')\n}\n\nfunction describeResult(instructions: GlobalInstructionOutcome[], mcp: GlobalMcpOutcome[]): CliResult {\n const lines: string[] = []\n for (const outcome of instructions) {\n lines.push(`Instructions: ${outcome.path} (${outcome.state})`)\n }\n for (const outcome of mcp) {\n if (outcome.state === 'manual') {\n lines.push(`MCP: ${outcome.client} — run this yourself: ${outcome.command} (${outcome.reason})`)\n }\n else {\n lines.push(`MCP: ${outcome.path} (${outcome.state})`)\n }\n }\n lines.push('A project uses the board once it has run kanbo init. Check everything with kanbo doctor.')\n return {\n value: { scope: 'global', instructions, mcp },\n text: lines.join('\\n'),\n fields: ['scope', 'instructions', 'mcp'],\n }\n}\n\nfunction parseGlobalInstructions(value: string | undefined): GlobalInstructionClient[] {\n if (value === undefined) {\n return DEFAULT_INSTRUCTION_CLIENTS\n }\n const names = value.split(',').map(name => name.trim()).filter(name => name.length > 0)\n if (names.length === 1 && names[0] === 'none') {\n return []\n }\n return names.map((name) => {\n const client = GLOBAL_INSTRUCTION_CLIENTS.find(candidate => candidate === name)\n if (!client) {\n throw new CliError(1, `Unknown instructions target \"${name}\" for --global. `\n + `Use one or more of ${GLOBAL_INSTRUCTION_CLIENTS.join(', ')}, or none.`)\n }\n return client\n })\n}\n","import { existsSync, mkdirSync, renameSync, rmSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve } from 'node:path'\n\nimport type { Command } from 'commander'\n\nimport { DEFAULT_BOARD_FILE_PATH } from '../../default-board-file-path'\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport type { BoardWorkspaceIdentity } from '../../domain/numbering'\nimport { migrateBoardFile } from '../../sqlite/migrate'\nimport type { BoardFileContents } from '../../sqlite/open-database'\nimport { markBoardFileOwnedByKanbo, openBoardDatabase, readBoardFileContents } from '../../sqlite/open-database'\nimport type { SqliteDatabase } from '../../sqlite/transaction'\nimport type { KanboBinding } from '../binding'\nimport { ignoreBoardFile, readProjectBinding, writeBinding } from '../binding'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand } from '../command'\nimport type { BoardTarget } from '../db-target'\nimport { describeTarget, resolveDatabaseUrl, resolveDbTarget, resolveHostDbPath } from '../db-target'\nimport type { CliResult } from '../output'\nimport { CliError, EXIT_NOT_RESOLVED, printResult, readFormat } from '../output'\nimport { isInteractive } from '../setup/confirm'\nimport type { FileOutcome } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { INSTRUCTION_BLOCK, planInstructionBlock } from '../setup/instructions'\nimport { planCodexMcpServer, planJsonMcpServer } from '../setup/mcp-config'\nimport type { McpClient, ProjectInstructionTarget } from '../setup/paths'\nimport { MCP_CLIENTS, PROJECT_INSTRUCTION_FILES, projectMcpConfigPath } from '../setup/paths'\nimport { resolveWorkspace } from '../workspace'\nimport { initGlobal } from './init-global'\n\n/**\n * `kanbo init` — tying a project folder to a board, and telling the agents\n * that work in it what the board expects.\n *\n * Three things come out of it, and each is optional except the first: the\n * binding, so no later command has to be told which workspace this is; a block\n * in the project's agent instructions, so an agent reads the board's rules\n * without being handed them; and a registration of the board's MCP server with\n * whichever tools the person actually uses. Nothing is written to a file the\n * person did not ask for, and nothing is written twice.\n */\n\n/**\n * What a person is told when an external board is named and nothing says which\n * workspace it is about. There is no table to look the answer up in, and a card\n * numbered under a guess carries that guess for the rest of its life.\n */\nconst EXTERNAL_WORKSPACE_MESSAGE\n = 'Could not tell which workspace this project is, and an external board holds no workspaces table to look '\n + 'it up in. Run this again with --workspace <id> --identifier <KEY>, or with --db (or KANBO_DB_PATH) naming '\n + 'a host database that knows this project.'\n\n/** What a person is told when a board file of the project's own and an external one are both named at once. */\nconst FILE_AND_DATABASE_URL_MESSAGE\n = 'One board at a time: pass --file for a board file of this project\\'s own or --database-url for an external '\n + 'board, not both.'\n\n/**\n * What a person is told when their agents are about to reach the board as its\n * owner. Not a refusal: the board works this way from the first minute, and the\n * rule an agent is missing is one the database keeps, not this command.\n */\nconst AGENT_URL_MISSING_WARNING\n = 'kanbo: agents in this project will reach the board with the connection string you gave, which owns it. '\n + 'Until you run \"kanbo roles apply\" and record the agent role\\'s own string with --agent-url, an agent '\n + 'here can approve its own work.'\n\ntype InstructionTarget = ProjectInstructionTarget | 'none'\n\nexport interface InitOptions extends BoardCommandOptions {\n /** Set up this person's own agent tools for every project, instead of binding this one. */\n global?: boolean\n board?: string\n identifier?: string\n agentUrl?: string\n /** Parsed per mode: one project file, or a list of the person's own files with `--global`. */\n instructions?: string\n mcp?: McpClient[]\n yes?: boolean\n /**\n * `--file` with no value parses to `true` (commander's rule for an optional\n * option argument); `--file <path>` parses to that path, relative to the\n * project root.\n */\n file?: string | boolean\n}\n\nexport function registerInitCommand(program: Command): void {\n // Not `withBoardOptions`: `--db` means something else here, and the two\n // board flags stand together rather than refusing each other. The rest is\n // spelled the same as everywhere else.\n program\n .command('init')\n .description('bind this project to a board, and tell its agents about it')\n .option('--db <path>', 'host database file to read this project\\'s workspace from')\n .option('--database-url <url>', 'external Postgres board to bind this project to')\n .option('--file [path]', `use a board file of this project's own instead of a host database or an `\n + `external board (default ${DEFAULT_BOARD_FILE_PATH})`)\n .option('--workspace <nameOrId>', 'workspace this project is')\n .option('--json <fields>', 'print only these comma-separated fields, as JSON')\n .option('--format <format>', 'output format: json or pretty')\n .option('--board <id>', 'the board inside the workspace, when it has more than one')\n .option('--identifier <key>', 'what this workspace\\'s card keys start with, for an external board')\n .option('--agent-url <url>', 'the connection string agents get, when it is not the one above')\n .option('--instructions <file>', 'where to write the instruction block: claude, agents or none; with '\n + '--global, a comma-separated list of claude, codex, gemini, or none')\n .option('--mcp <clients>', `register the board's MCP server with ${MCP_CLIENTS.join(', ')}`, parseMcpClients)\n .option('--global', 'set up your own agent tools for every project instead of binding this one; binds no board')\n .option('--yes', 'take the defaults instead of asking')\n .action(async (options: InitOptions) => {\n if (options.global) {\n await initGlobal(options)\n return\n }\n // Refused before anything is written, as a parse-time check would be.\n if (options.instructions !== undefined) {\n parseInstructions(options.instructions)\n }\n const projectDir = process.cwd()\n if (options.file !== undefined) {\n await initOwnFile(projectDir, options)\n return\n }\n const target = resolveInitTarget(projectDir, options)\n\n if (target.kind === 'postgres') {\n await initExternalBoard(projectDir, target, options)\n return\n }\n await runBoardCommand(options, 'read', async session => await initProject(\n projectDir,\n target,\n session.workspace,\n options,\n ))\n })\n}\n\n/**\n * Which board `init` is binding this project to.\n *\n * `--db` means something else here than it does in every other command: it\n * names the host database this one reads the workspace off, not the\n * board being bound. So the external board is asked for on its own — the flag,\n * the environment, then the binding this project already has — and only when\n * there is none is the board file itself the board. Always the person's\n * connection string: binding a project is not something an agent does.\n */\nfunction resolveInitTarget(projectDir: string, options: InitOptions): BoardTarget {\n const url = resolveDatabaseUrl({ explicitUrl: options.databaseUrl, cwd: projectDir, asAgent: false })\n return url ? { kind: 'postgres', url } : resolveDbTarget({ explicitPath: options.db, cwd: projectDir })\n}\n\n/**\n * Bind a project to an external board.\n *\n * The board is never opened. `init` is what a person runs *before*\n * `kanbo migrate`, and a database that holds no board yet is not a reason to\n * refuse to write down where it will be — nor could it answer the one question\n * that matters here, since it has no `workspaces` table to be asked.\n */\nasync function initExternalBoard(\n projectDir: string,\n target: Extract<BoardTarget, { kind: 'postgres' }>,\n options: InitOptions,\n): Promise<void> {\n readFormat(options)\n const workspace = await resolveExternalWorkspace(projectDir, options)\n printResult(await initProject(projectDir, target, workspace, options), options)\n}\n\n/**\n * Create and use a board file of this project's own — `.kanbo/board.db` by\n * default — with no host app and no Postgres anywhere near it.\n *\n * The file is migrated with this package's own chain right here, because\n * nothing else ever will: a host app migrates its own database and an\n * external database is migrated by `kanbo migrate`, but a file of this\n * package's own has no other owner to do it later — `kanbo migrate` runs the\n * same chain again afterwards, and idempotently, for the day it falls behind a\n * newer build (ruling 5-5). The marker written right after is what every later\n * command reads to know not to look for a host `workspaces` table in it\n * (ruling 5-4).\n *\n * The workspace is settled before a single byte is written, and nothing here\n * opens a host database at all (ruling 5-9): this is the one command that has\n * to work on a machine with nothing else installed, and the answer it needs —\n * an id and a card-key stem — is either typed, already in the binding, or the\n * folder's own name. Asking a host database first would turn `--workspace <id>`\n * into a refusal on every machine that has one, and leave a board file behind\n * with no binding beside it.\n */\nasync function initOwnFile(projectDir: string, options: InitOptions): Promise<void> {\n readFormat(options)\n if (options.databaseUrl) {\n throw new CliError(1, FILE_AND_DATABASE_URL_MESSAGE)\n }\n\n const relativePath = typeof options.file === 'string' && options.file.trim() ? options.file.trim() : DEFAULT_BOARD_FILE_PATH\n const absolutePath = resolve(projectDir, relativePath)\n const workspace = resolveOwnFileWorkspace(projectDir, options)\n\n await createOwnBoardFile(absolutePath)\n ignoreBoardFile(projectDir, absolutePath)\n\n const target: BoardTarget = { kind: 'sqlite', path: absolutePath, owner: 'kanbo' }\n printResult(await initProject(projectDir, target, workspace, options), options)\n}\n\n/**\n * Put a migrated, marked board at this path — or leave the folder exactly as it\n * was found (ruling 5-9).\n *\n * A new board is built beside its own path and moved onto it at the end, so a\n * migration that fails half-way leaves no half-built file for the next command\n * to open and believe in. `rename` within one directory is the filesystem's own\n * atomic swap; a temporary name elsewhere would be a copy across devices and\n * not atomic at all. A file that is already there is a different question — it\n * may hold this project's cards — so it is migrated in place, idempotently,\n * which is what makes a second `kanbo init --file` a no-op.\n *\n * A file that is there and is *not* this package's is refused, for the reason\n * any careful host refuses it: migrating a board into somebody's own\n * database leaves it readable but contaminated, and only a different path\n * helps.\n */\nasync function createOwnBoardFile(absolutePath: string): Promise<void> {\n mkdirSync(dirname(absolutePath), { recursive: true })\n if (existsSync(absolutePath)) {\n assertOwnBoardFile(absolutePath)\n await migrateBoardFileAt(absolutePath)\n return\n }\n\n const temporaryPath = `${absolutePath}.${process.pid}.tmp`\n try {\n await migrateBoardFileAt(temporaryPath)\n renameSync(temporaryPath, absolutePath)\n }\n catch (error) {\n // Its WAL siblings too: SQLite removes them on a clean close, and a close\n // is exactly what a failure half-way through may not have reached.\n for (const leftover of [temporaryPath, `${temporaryPath}-wal`, `${temporaryPath}-shm`]) {\n rmSync(leftover, { force: true })\n }\n throw error\n }\n}\n\n/** Refuse a file already at the board's path that this package did not write. */\nfunction assertOwnBoardFile(absolutePath: string): void {\n let contents: BoardFileContents\n try {\n contents = readBoardFileContents(absolutePath)\n }\n catch {\n throw new CliError(1, notABoardFileMessage(absolutePath))\n }\n if (!contents.empty && contents.owner !== 'kanbo') {\n throw new CliError(1, notABoardFileMessage(absolutePath))\n }\n}\n\nfunction notABoardFileMessage(absolutePath: string): string {\n return `${absolutePath} already holds a database of its own. Name another path with --file, `\n + 'or move that file out of the way.'\n}\n\nasync function migrateBoardFileAt(path: string): Promise<void> {\n const board = await openBoardDatabase(path)\n try {\n migrateBoardFile(board.database)\n markBoardFileOwnedByKanbo(board.database)\n }\n finally {\n board.close()\n }\n}\n\n/** The three things `init` writes, whichever kind of board it was given. */\nasync function initProject(\n projectDir: string,\n target: BoardTarget,\n workspace: BoardWorkspaceIdentity,\n options: InitOptions,\n): Promise<CliResult> {\n warnAboutOwnerAccess(target, options)\n const bindingPath = writeBinding(projectDir, buildBinding(projectDir, target, workspace, options))\n const instructions = await writeInstructions(projectDir, options)\n const mcp = await registerMcpServers(projectDir, options)\n\n return {\n value: {\n binding: bindingPath,\n workspaceId: workspace.id,\n boardId: options.board ?? null,\n database: describeTarget(target),\n instructions,\n mcp,\n },\n text: describeInit(workspace, target, bindingPath, instructions, mcp),\n }\n}\n\n/**\n * What the binding says afterwards.\n *\n * Merged into what this project already recorded, never replacing it: every\n * field this command line did not speak about keeps the value it had. A person\n * re-running `init` to refresh the instruction block said nothing about the\n * board, and a second run that dropped the connection string would leave the\n * project bound to nothing.\n *\n * The fields an external board adds are written only when there is one: they\n * are what every later command reaches the database by, and a project that has\n * nothing to do with one should not carry a connection string at all.\n *\n * This file is the only place a connection string is written down, and\n * `writeBinding` is what keeps it out of git. The MCP registrations below are\n * the project's own shared files and carry no secret at all — `kanbo mcp`\n * resolves its board from this binding like every other command.\n */\nfunction buildBinding(\n projectDir: string,\n target: BoardTarget,\n workspace: BoardWorkspaceIdentity,\n options: InitOptions,\n): KanboBinding {\n const existing = readProjectBinding(projectDir)\n const ownFile = target.kind === 'sqlite' && target.owner === 'kanbo'\n const hasWorkspacesTable = target.kind === 'sqlite' && target.owner === 'host'\n const binding: KanboBinding = {\n schemaVersion: 1,\n workspaceId: workspace.id,\n boardId: options.board ?? existing?.boardId ?? null,\n dbPath: ownFile ? relative(projectDir, target.path) : existing?.dbPath ?? null,\n }\n if (hasWorkspacesTable) {\n return binding\n }\n\n // Neither an external board nor a board file of this project's own has a\n // `workspaces` row to read the card-key stem off later, so it is written\n // down here — the one thing the two share, alongside `databaseUrl` below,\n // which only a Postgres board has any use for.\n const withIdentifier: KanboBinding = { ...binding, identifier: workspace.identifier }\n if (target.kind !== 'postgres') {\n return withIdentifier\n }\n\n const agentUrl = options.agentUrl?.trim() || existing?.agentDatabaseUrl\n return {\n ...withIdentifier,\n databaseUrl: target.url,\n ...(agentUrl ? { agentDatabaseUrl: agentUrl } : {}),\n }\n}\n\n/**\n * What the host database and the flags say about the workspace a board with no\n * `workspaces` table is being bound to — never the whole answer by itself,\n * since either piece may be missing.\n */\ninterface WorkspaceGuess {\n local: BoardWorkspaceIdentity | null\n id?: string\n identifier?: string\n}\n\n/**\n * Ask the host database first, because the ordinary case is a person moving a\n * project they already work on onto a board of its own, and their own machine\n * already knows both answers; what they typed on the command line wins over\n * it. Callers decide what to do when a piece is still missing: an external\n * board refuses outright, while a board file of this project's own has the\n * folder's own name to fall back on.\n */\nasync function guessWorkspace(projectDir: string, options: InitOptions): Promise<WorkspaceGuess> {\n const local = await readLocalWorkspace(projectDir, options)\n return {\n local,\n id: local?.id ?? options.workspace?.trim(),\n identifier: options.identifier?.trim() || local?.identifier,\n }\n}\n\n/**\n * Which workspace an external board's cards belong to, and what their keys\n * start with.\n *\n * When neither the host database nor the flags say the whole answer, the\n * command refuses rather than inventing a key: a card numbered under a guess\n * carries that guess for the rest of its life, and an external database is\n * shared in a way a board file of this project's own is not.\n */\nasync function resolveExternalWorkspace(projectDir: string, options: InitOptions): Promise<BoardWorkspaceIdentity> {\n const guess = await guessWorkspace(projectDir, options)\n if (!guess.id || !guess.identifier) {\n throw new CliError(EXIT_NOT_RESOLVED, EXTERNAL_WORKSPACE_MESSAGE)\n }\n return { id: guess.id, identifier: guess.identifier, name: guess.local?.id === guess.id ? guess.local.name : guess.identifier }\n}\n\n/**\n * The same question, for a board file of this project's own — asked of three\n * things, none of which is a database (ruling 5-9).\n *\n * `kanbo init --file` is the one command this package promises works with\n * nothing else installed, so there is nothing to refuse about it and nothing to\n * look up: what the person typed wins, then the id this project is already\n * bound to, and failing both the folder's own name, slugged, stands in for the\n * id and for the card-key stem `readIssuePrefix` derives from it.\n *\n * The binding comes second rather than last because the id in it is the id the\n * cards in the file are already numbered under — a second `init` that fell back\n * to the slug would leave the project pointing at a workspace with no cards in\n * it (ruling 3-16). It is also how a host app and the command line stay on one\n * board: an app that attaches the file may rewrite the binding to its own\n * workspace id (ruling 5-7), and this is what keeps it. The card key follows the id it\n * belongs to: naming a different workspace on the command line does not inherit\n * the old one's stem.\n */\nfunction resolveOwnFileWorkspace(projectDir: string, options: InitOptions): BoardWorkspaceIdentity {\n const bound = readProjectBinding(projectDir)\n const slug = deriveProjectSlug(projectDir)\n const id = options.workspace?.trim() || bound?.workspaceId.trim() || slug\n const boundIdentifier = id === bound?.workspaceId.trim() ? bound.identifier?.trim() : null\n const identifier = options.identifier?.trim() || boundIdentifier || slug\n return { id, identifier, name: identifier }\n}\n\n/** A workspace id and card-key stem from the project folder's own name, for the machine with nothing else to ask. */\nfunction deriveProjectSlug(projectDir: string): string {\n const slug = basename(resolve(projectDir)).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n return slug || 'project'\n}\n\n/**\n * The workspace this folder is, as a host database on this machine knows it —\n * or nothing at all, when there is no host database here to ask.\n *\n * A workspace that database *refuses* is a different answer entirely, and it is\n * only swallowed when the person named nothing: not knowing which workspace an\n * unnamed folder is, is what `--workspace` and `--identifier` are for. A name\n * they did type and that database rejects — no such workspace, or two by that\n * name — has to reach them, because the alternative is writing what they typed\n * into the binding as if it were an id and numbering cards under a workspace\n * that exists nowhere.\n */\nasync function readLocalWorkspace(projectDir: string, options: InitOptions): Promise<BoardWorkspaceIdentity | null> {\n const board = await openHostDatabase(options)\n if (!board) {\n return null\n }\n\n try {\n return resolveWorkspace(\n { kind: 'sqlite', database: board.database },\n { explicit: options.workspace, cwd: projectDir },\n )\n }\n catch (error) {\n if (options.workspace?.trim()) {\n throw error\n }\n return null\n }\n finally {\n board.close()\n }\n}\n\n/**\n * The host database `--db` or `KANBO_DB_PATH` names, or nothing — a machine is\n * allowed to have none. `resolveHostDbPath` rather than the ordinary resolver:\n * a project bound to a board file of its own by an earlier `kanbo init --file`\n * still asks this, and that binding names its own file, not a host database.\n */\nasync function openHostDatabase(options: InitOptions): Promise<{ database: SqliteDatabase, close: () => void } | null> {\n try {\n return await openBoardDatabase(resolveHostDbPath(options.db))\n }\n catch {\n return null\n }\n}\n\n/**\n * Say so when the agents of this project will reach the board as its owner.\n *\n * On stderr, because stdout belongs to the result, and once — a person who has\n * already recorded an agent connection string hears nothing.\n */\nfunction warnAboutOwnerAccess(target: BoardTarget, options: InitOptions): void {\n if (target.kind === 'postgres' && !options.agentUrl?.trim()) {\n console.error(AGENT_URL_MISSING_WARNING)\n }\n}\n\n/**\n * Put the block in the file the caller named — or, when they named none, show\n * them what it says and ask.\n *\n * A shell that is nobody's terminal is never asked anything: an `init` in a\n * script prints the block and stops there, because writing into a project's\n * instructions is the kind of thing a person says yes to.\n */\nasync function writeInstructions(projectDir: string, options: InitOptions): Promise<FileOutcome | null> {\n const target = options.instructions === undefined ? await askInstructionTarget(options) : parseInstructions(options.instructions)\n if (target === 'none') {\n return null\n }\n return applyFileChange(planInstructionBlock(join(projectDir, PROJECT_INSTRUCTION_FILES[target]), INSTRUCTION_BLOCK))\n}\n\nasync function askInstructionTarget(options: InitOptions): Promise<InstructionTarget> {\n showBlock(options)\n if (options.yes || !isInteractive()) {\n return 'none'\n }\n\n const prompts = await import('@clack/prompts')\n const answer = await prompts.select({\n message: 'Add this block to the project\\'s agent instructions?',\n options: [\n { value: 'claude' as const, label: 'CLAUDE.md' },\n { value: 'agents' as const, label: 'AGENTS.md' },\n { value: 'none' as const, label: 'Neither — I will paste it myself' },\n ],\n })\n return prompts.isCancel(answer) ? 'none' : answer\n}\n\n/**\n * Show the block to whoever is about to decide where it goes — unless the\n * caller asked for machine output, in which case stdout belongs to the result\n * and nothing else may be written there, prompt or no prompt.\n */\nfunction showBlock(options: InitOptions): void {\n if (!options.json && !options.format) {\n console.log(`${INSTRUCTION_BLOCK}\\n`)\n }\n}\n\n/**\n * Tell the tools the person uses about the board's MCP server.\n *\n * Only the ones they named, and only when the tool does not already know: a\n * `kanbo` entry that is already there belongs to them — it may point at\n * a different binary or carry arguments of its own — and overwriting it would\n * be this command deciding something it was not asked to decide.\n */\nasync function registerMcpServers(projectDir: string, options: InitOptions): Promise<FileOutcome[]> {\n const clients = options.mcp ?? await askMcpClients(options)\n return clients.map(client => registerMcpServer(projectDir, client))\n}\n\nasync function askMcpClients(options: InitOptions): Promise<McpClient[]> {\n if (options.yes || !isInteractive()) {\n return []\n }\n\n const prompts = await import('@clack/prompts')\n const answer = await prompts.multiselect({\n message: 'Register the board\\'s MCP server with which tools?',\n options: [\n { value: 'claude' as const, label: 'Claude Code (.mcp.json)' },\n { value: 'codex' as const, label: 'Codex (.codex/config.toml)' },\n { value: 'cursor' as const, label: 'Cursor (.cursor/mcp.json)' },\n ],\n required: false,\n })\n return prompts.isCancel(answer) ? [] : answer\n}\n\nfunction registerMcpServer(projectDir: string, client: McpClient): FileOutcome {\n const path = projectMcpConfigPath(projectDir, client)\n return applyFileChange(client === 'codex' ? planCodexMcpServer(path) : planJsonMcpServer(path))\n}\n\nfunction describeInit(\n workspace: BoardWorkspaceIdentity,\n target: BoardTarget,\n bindingPath: string,\n instructions: FileOutcome | null,\n mcp: FileOutcome[],\n): string {\n const lines = [`Bound this project to ${workspace.name} — ${bindingPath}`]\n if (target.kind === 'postgres') {\n lines.push(`Board: ${maskDatabaseUrl(target.url)}`)\n }\n else if (target.owner === 'kanbo') {\n lines.push(`Board file: ${target.path}`)\n }\n if (instructions) {\n lines.push(`Instructions: ${instructions.path} (${instructions.state})`)\n }\n for (const outcome of mcp) {\n lines.push(`MCP: ${outcome.path} (${outcome.state})`)\n }\n return lines.join('\\n')\n}\n\nfunction parseInstructions(value: string): InstructionTarget {\n if (value !== 'claude' && value !== 'agents' && value !== 'none') {\n throw new CliError(1, `Unknown instructions target \"${value}\". Use claude, agents or none.`)\n }\n return value\n}\n\nfunction parseMcpClients(value: string): McpClient[] {\n return value.split(',').map(name => name.trim()).filter(name => name.length > 0).map((name) => {\n const client = MCP_CLIENTS.find(candidate => candidate === name)\n if (!client) {\n throw new CliError(1, `Unknown MCP client \"${name}\". Use one or more of ${MCP_CLIENTS.join(', ')}.`)\n }\n return client\n })\n}\n","import type { EntryRule } from '../domain/entry-rules'\nimport { ENTRY_RULES, readEntryRules } from '../domain/entry-rules'\nimport { normalizeStatusName } from '../domain/status-name'\nimport type { Issue, IssueRun } from '../sqlite/schema'\n\n/**\n * What the board's tools ask for, and what comes back.\n *\n * A tool knows one operation and nothing about how it is performed: the same\n * `kanbo_card_move` runs against a server over HTTP inside an app's own MCP\n * server, and against the board itself inside `kanbo mcp`, because both transports answer this interface. Which one a tool\n * holds is the caller's decision, made once, and no tool can tell the\n * difference.\n *\n * The workspace is not a parameter here. Each transport is built for one board\n * — the CLI resolves it the way every `kanbo` command does, the server takes\n * it from the runtime it was launched for — and a tool that could name a\n * workspace could name one the agent was never given.\n */\n\n/** A card, as a tool hands it to an agent. */\nexport interface KanboCardResult {\n /** The issue key, which is also the row id: `MAN-012`. */\n id: string\n number: number\n title: string\n description: string | null\n /** The column's name, `null` when the card sits in no column. */\n column: string | null\n /** The same column as the slug `kanbo_card_move` takes. */\n columnSlug: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n /** Every run the card has ever had; the next launch is attempt `attemptCount + 1`. */\n attemptCount: number\n /** The run going on right now, `null` when nobody is working on the card. */\n activeRun: KanboActiveRunResult | null\n updatedAt: number\n}\n\n/**\n * A card read on its own, as `kanbo_card_get` hands it to an agent: the card,\n * and the sub-cards under it in board order. Its parent is `parentIssueId`.\n */\nexport interface KanboCardDetailResult extends KanboCardResult {\n subCards: Pick<KanboCardResult, 'id' | 'title' | 'columnSlug'>[]\n}\n\n/** The run of a card that is going on right now. */\nexport interface KanboActiveRunResult {\n id: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n}\n\n/**\n * A column of the board, as a tool hands it to an agent.\n *\n * What the column means is its description, written for exactly this reader;\n * the category behind it is the board's own classification of the column and says\n * nothing an agent moving a card can act on.\n */\nexport interface KanboColumnResult {\n id: string\n name: string\n /** What `kanbo_card_move` calls this column. */\n slug: string\n description: string | null\n order: number\n /** What a card must satisfy before an agent may move it here; empty for nothing. */\n entryRules: EntryRule[]\n}\n\n/**\n * A milestone read as a sprint, as `kanbo_sprints` hands it to an agent: its\n * dates (unix seconds), whether it is the one running now, and how many of its\n * cards are still open and how many are done.\n */\nexport interface KanboSprintResult {\n id: string\n title: string\n status: 'open' | 'closed'\n startDate: number | null\n dueDate: number | null\n current: boolean\n openCards: number\n doneCards: number\n}\n\n/** A comment, as `kanbo_card_comment` hands it back. */\nexport interface KanboCommentResult {\n id: string\n issueId: string\n content: string\n createdAt: number\n}\n\n/** A pull request a card names, as the pull-request tools hand it back. */\nexport interface KanboPullRequestResult {\n id: string\n issueId: string\n owner: string\n repo: string\n number: number\n url: string\n createdAt: number\n}\n\n/** One launch of a card, as the run tools hand it back. */\nexport interface KanboRunResult {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n endedAt: number | null\n /** Where the caller's own log of this run lives, if it said — `claude:<id>` or `codex:<id>` (ruling 7-1), else `null`. */\n externalSessionRef: string | null\n}\n\n/**\n * What each tool asks its transport for. They are named rather than written\n * inline so the interface below reads as a list of operations, and kept inside\n * this module because a caller reaches them through `KanboToolTransport`.\n */\n\n/** What `kanbo_card_create` needs to open a card. */\ninterface KanboCardCreateInput {\n /** Optional: a card without one is titled with its own key. */\n title?: string\n description?: string\n /** The column to open it in, by slug or name; the board's first column when absent. */\n column?: string\n /** The card this one belongs under, making it a sub-card. */\n parent?: string\n executionMode?: Issue['executionMode']\n}\n\n/** The fields `kanbo_card_update` may change. */\ninterface KanboCardUpdateInput {\n card: string\n title?: string\n description?: string\n priority?: Issue['priority']\n labels?: string[]\n executionMode?: Issue['executionMode']\n}\n\n/** What `kanbo_run_start` records about a launch. */\ninterface KanboRunStartInput {\n card: string\n agent: string\n branch?: string\n executionMode?: Issue['executionMode']\n /** Where the agent's own log of this run lives: `claude:<id>` or `codex:<id>` (ruling 7-1). */\n session?: string\n}\n\n/** How `kanbo_run_finish` says a run ended. */\ninterface KanboRunFinishInput {\n run: string\n state: 'finished' | 'failed' | 'stopped'\n errorText?: string\n}\n\n/** One method per thing the board's tools do. Both transports answer all of it. */\nexport interface KanboToolTransport {\n prime: () => Promise<{ text: string }>\n ready: (input: { limit?: number }) => Promise<KanboCardResult[]>\n columns: () => Promise<KanboColumnResult[]>\n sprints: () => Promise<KanboSprintResult[]>\n cardGet: (input: { card: string }) => Promise<KanboCardDetailResult>\n cardList: (input: { column?: string, limit?: number }) => Promise<KanboCardResult[]>\n cardCreate: (input: KanboCardCreateInput) => Promise<KanboCardResult>\n cardUpdate: (input: KanboCardUpdateInput) => Promise<KanboCardResult>\n cardMove: (input: { card: string, column: string }) => Promise<KanboCardResult>\n cardComment: (input: { card: string, content: string }) => Promise<KanboCommentResult>\n cardLinkPullRequest: (input: { card: string, url: string }) => Promise<KanboPullRequestResult>\n cardPullRequests: (input: { card: string }) => Promise<KanboPullRequestResult[]>\n statusLine: (input: { card: string, text: string }) => Promise<KanboCardResult>\n waitApproval: (input: { card: string, text?: string }) => Promise<KanboCardResult>\n runStart: (input: KanboRunStartInput) => Promise<KanboRunResult>\n runFinish: (input: KanboRunFinishInput) => Promise<KanboRunResult>\n}\n\n/**\n * A card as either transport already has it: the row's own fields with the\n * labels decoded, plus what the run registry says about it.\n */\nexport interface KanboCardFacts {\n id: string\n number: number\n title: string\n description: string | null\n statusId: string | null\n statusLine: string | null\n waitingFor: 'human' | null\n priority: Issue['priority']\n labels: string[]\n executionMode: Issue['executionMode']\n parentIssueId: string | null\n updatedAt: number\n attemptCount: number\n activeRun: KanboActiveRunResult | null\n}\n\n/**\n * The card a tool prints, from the card a transport holds.\n *\n * Both transports project here so the JSON an agent reads is the same whether\n * it came from the server or from the board file — including the column, which\n * neither of them carries on the card itself: the row keeps a foreign key, and\n * an agent moving a card needs the slug.\n *\n * Every field is named rather than spread. A card the board file answers with\n * carries more than this — the agent and session ids behind a run among them —\n * and passing an object through by reference would print whatever it happened\n * to hold, which is both a different shape from the server's answer and host\n * facts the agent was never meant to see.\n */\nexport function projectKanboCard(card: KanboCardFacts, columns: KanboColumnResult[]): KanboCardResult {\n const column = columns.find(candidate => candidate.id === card.statusId) ?? null\n return {\n id: card.id,\n number: card.number,\n title: card.title,\n description: card.description,\n column: column?.name ?? null,\n columnSlug: column?.slug ?? null,\n statusLine: card.statusLine,\n waitingFor: card.waitingFor,\n priority: card.priority,\n labels: card.labels,\n executionMode: card.executionMode,\n parentIssueId: card.parentIssueId,\n attemptCount: card.attemptCount,\n activeRun: projectKanboActiveRun(card.activeRun),\n updatedAt: card.updatedAt,\n }\n}\n\n/** A sub-card, as the card above it lists it: enough to name it and read it next. */\nexport function projectKanboSubCard(\n card: Pick<KanboCardFacts, 'id' | 'title' | 'statusId'>,\n columns: KanboColumnResult[],\n): KanboCardDetailResult['subCards'][number] {\n return {\n id: card.id,\n title: card.title,\n columnSlug: columns.find(candidate => candidate.id === card.statusId)?.slug ?? null,\n }\n}\n\n/** The run going on right now, as much of it as the agent working the card needs. */\nfunction projectKanboActiveRun(run: KanboActiveRunResult | null): KanboActiveRunResult | null {\n return run === null\n ? null\n : {\n id: run.id,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n startedAt: run.startedAt,\n }\n}\n\n/**\n * One launch of a card, from the run row either transport holds.\n *\n * What the row carries beyond this — the worktree path, the chat session\n * driving it, who launched it — belongs to the host that launched the run, not\n * to the agent that asked for one.\n */\nexport function projectKanboRun(run: {\n id: string\n issueId: string\n agentName: string\n state: IssueRun['state']\n executionMode: IssueRun['executionMode']\n branch: string | null\n startedAt: number\n endedAt: number | null\n /** Absent from a server that predates it — reads the same as `null`. */\n externalSessionRef?: string | null\n}): KanboRunResult {\n return {\n id: run.id,\n issueId: run.issueId,\n agentName: run.agentName,\n state: run.state,\n executionMode: run.executionMode,\n branch: run.branch,\n startedAt: run.startedAt,\n endedAt: run.endedAt,\n externalSessionRef: run.externalSessionRef ?? null,\n }\n}\n\n/**\n * A pull-request link, from the row either transport holds — without who made\n * it, which is the board's record rather than anything the agent acts on.\n */\nexport function projectKanboPullRequest(link: KanboPullRequestResult): KanboPullRequestResult {\n return {\n id: link.id,\n issueId: link.issueId,\n owner: link.owner,\n repo: link.repo,\n number: link.number,\n url: link.url,\n createdAt: link.createdAt,\n }\n}\n\n/** A sprint, from the one `listSprints` answers with on either end — the description and timestamps left behind. */\nexport function projectKanboSprint(sprint: {\n id: string\n title: string\n status: 'open' | 'closed'\n startDate: number | null\n dueDate: number | null\n current: boolean\n cards: { open: number, done: number }\n}): KanboSprintResult {\n return {\n id: sprint.id,\n title: sprint.title,\n status: sprint.status,\n startDate: sprint.startDate,\n dueDate: sprint.dueDate,\n current: sprint.current,\n openCards: sprint.cards.open,\n doneCards: sprint.cards.done,\n }\n}\n\n/** A column of the board, from the row either transport holds. */\nexport function projectKanboColumn(column: {\n id: string\n name: string\n description: string | null\n order: number\n entryRules?: string | readonly string[] | null\n}): KanboColumnResult {\n const stored = column.entryRules\n return {\n id: column.id,\n name: column.name,\n slug: normalizeStatusName(column.name),\n description: column.description,\n order: column.order,\n entryRules: typeof stored === 'object' && stored !== null\n ? ENTRY_RULES.filter(rule => stored.includes(rule))\n : readEntryRules({ entryRules: stored ?? null }),\n }\n}\n","import { openBoardSession, requireCard, requireRun } from '../cli/command'\nimport { toIssueView } from '../domain/issue-view'\nimport { requireColumn } from '../ops/columns'\nimport type { BoardActor } from '../ops/types'\nimport type { Issue } from '../sqlite/schema'\nimport type { KanboCardResult, KanboColumnResult, KanboToolTransport } from './transport'\nimport {\n projectKanboCard,\n projectKanboColumn,\n projectKanboPullRequest,\n projectKanboRun,\n projectKanboSprint,\n projectKanboSubCard,\n} from './transport'\n\n/**\n * The board's tools, run against the board itself.\n *\n * This is the transport `kanbo mcp` serves: an agent working in a checkout\n * with no server running still drives its card, because the board — a\n * file or a database of its own — and the operations that give it meaning ship\n * with this package. Everything a `kanbo` command decides is decided here too,\n * by calling the same code: which board, which workspace, which card a\n * reference names, and the refusal to write to one this build cannot speak for.\n */\n\n/** Where the board is, whose board it is, and who the writes are filed under. */\nexport interface KanboDbTransportInput {\n /** The board file to open; resolved the way every `kanbo` command resolves it when absent. */\n dbPath?: string\n /** The external board to open instead; resolved the same way when absent. */\n databaseUrl?: string\n /** The workspace to work on; resolved from the binding or the working directory when absent. */\n workspaceId?: string\n /**\n * Who every write is filed under. Never taken from a tool argument: an agent\n * that could name its own kind could name `user`, and the one rule the board\n * has about people would be worth nothing.\n */\n actor: BoardActor\n}\n\n/** The transport, and the board it holds open. */\nexport interface KanboDbTransport extends KanboToolTransport {\n /** Let go of the board. The server that holds this transport closes it. */\n close: () => Promise<void>\n}\n\nexport async function createDbTransport(input: KanboDbTransportInput): Promise<KanboDbTransport> {\n // Opened for reading: a board file older than this build is still worth\n // looking at, and every write below asks the same guard a `kanbo` command\n // asks. A client talks to this server for as long as it likes, and the board\n // may be migrated underneath it in the meantime.\n const session = await openBoardSession(\n { db: input.dbPath, databaseUrl: input.databaseUrl, workspace: input.workspaceId, asAgent: true },\n 'read',\n )\n const { ops, store, workspace } = session\n const workspaceId = workspace.id\n const actor = input.actor\n\n async function readColumns(): Promise<KanboColumnResult[]> {\n return (await ops.listColumns(workspaceId)).map(projectKanboColumn)\n }\n\n /** One card with what the run registry says about it, in the shape a tool prints. */\n async function project(card: Issue, columns?: KanboColumnResult[]): Promise<KanboCardResult> {\n const runs = await ops.readBoardProjectionForIssue(card.id)\n return projectKanboCard({ ...toIssueView(card), ...runs }, columns ?? await readColumns())\n }\n\n async function projectAll(cards: Issue[]): Promise<KanboCardResult[]> {\n const columns = await readColumns()\n const runs = await ops.readBoardProjectionForIssues(cards.map(card => card.id))\n return cards.map(card => projectKanboCard({\n ...toIssueView(card),\n ...(runs.find(projection => projection.issueId === card.id) ?? { attemptCount: 0, activeRun: null }),\n }, columns))\n }\n\n /** The card a tool named: the key, the key without its padding, or the number. */\n async function resolveCard(reference: string): Promise<Issue> {\n return await requireCard(session, reference)\n }\n\n return {\n close: session.close,\n\n prime: async () => ({ text: await ops.buildPrimeText(workspaceId) }),\n\n ready: async ({ limit }) => await projectAll(await ops.listReady({ workspaceId, limit })),\n\n columns: readColumns,\n\n sprints: async () => (await ops.listSprints(workspaceId)).map(projectKanboSprint),\n\n cardGet: async ({ card }) => {\n const found = await resolveCard(card)\n const columns = await readColumns()\n const subCards = (await store.issues.listInBoardOrder(found.workspaceId))\n .filter(row => row.parentIssueId === found.id)\n return {\n ...await project(found, columns),\n subCards: subCards.map(row => projectKanboSubCard(row, columns)),\n }\n },\n\n cardList: async ({ column, limit }) => {\n const wanted = column ? await requireColumn(store, workspaceId, column) : null\n const cards = (await store.issues.listInBoardOrder(workspaceId))\n .filter(card => !wanted || card.statusId === wanted.id)\n .slice(0, limit ?? Number.POSITIVE_INFINITY)\n return await projectAll(cards)\n },\n\n cardCreate: async ({ title, description, column, parent, executionMode }) => {\n await session.assertWritable()\n const parentCard = parent ? await resolveCard(parent) : null\n return await project(await ops.createCard({\n workspace,\n title,\n description,\n statusName: column,\n parentIssueId: parentCard?.id,\n executionMode,\n }, actor))\n },\n\n cardUpdate: async ({ card, ...patch }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.updateCard(existing.id, patch, actor))\n },\n\n cardMove: async ({ card, column }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.moveCard(existing.id, column, actor))\n },\n\n cardComment: async ({ card, content }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n const comment = await ops.addComment({ issueId: existing.id, content }, actor)\n return { id: comment.id, issueId: comment.issueId, content: comment.content, createdAt: comment.createdAt }\n },\n\n cardLinkPullRequest: async ({ card, url }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return projectKanboPullRequest(await ops.linkPullRequest(existing.id, url, actor))\n },\n\n cardPullRequests: async ({ card }) => {\n const existing = await resolveCard(card)\n return (await ops.listPullRequests(existing.id)).map(projectKanboPullRequest)\n },\n\n statusLine: async ({ card, text }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.setStatusLine(existing.id, text, actor))\n },\n\n waitApproval: async ({ card, text }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n return await project(await ops.waitApproval(existing.id, { statusLine: text }, actor))\n },\n\n runStart: async ({ card, agent, branch, executionMode, session: externalSessionRef }) => {\n await session.assertWritable()\n const existing = await resolveCard(card)\n // Filed as launched from outside, because it was: no app's chat session\n // started this, and a board that claimed otherwise would send someone\n // looking for a session that never existed.\n return projectKanboRun(await ops.startRun(existing.id, {\n agentName: agent,\n branch,\n executionMode,\n externalSessionRef,\n launchedByKind: 'external',\n }, actor))\n },\n\n runFinish: async ({ run, state, errorText }) => {\n await session.assertWritable()\n const existing = await requireRun(session, run)\n return projectKanboRun(await ops.finishRun(existing.id, { state, errorText }, actor))\n },\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\n\nimport { buildCapabilitiesManifest } from '../capabilities/manifest'\nimport { renderCapabilitiesMarkdown } from '../capabilities/markdown'\n\n/** The URI a client reads the manifest as JSON from. */\nexport const KANBO_CAPABILITIES_RESOURCE_URI = 'kanbo://capabilities'\n\n/** The URI a client reads the same manifest from, laid out as Markdown. */\nexport const KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI = 'kanbo://capabilities.md'\n\n/**\n * The board's own capabilities manifest, as two MCP resources rather than a\n * tool: a client reads it once, before it ever calls a board tool, to learn\n * what the tools take and what the board promises and refuses. `kanbo\n * capabilities` prints the same two shapes from a terminal.\n *\n * Registered on the server `kanbo mcp` builds for itself rather than on\n * `registerKanboTools` — the function an app's own MCP server calls to add the\n * board's tools to an inventory of its own — so an agent inside such an app\n * reads the manifest the way that app documents everything else, and a client\n * with no server around it still gets the manifest from the one server it is\n * talking to.\n */\nexport function registerKanboCapabilitiesResources(server: McpServer): void {\n server.registerResource(\n 'kanbo-capabilities',\n KANBO_CAPABILITIES_RESOURCE_URI,\n {\n title: 'Kanbo capabilities',\n description: 'The board\\'s tools with their input schemas, its CLI commands, the rules a card travels by, '\n + 'the canonical strings the board writes, the roles it enforces, and what it refuses — as JSON.',\n mimeType: 'application/json',\n },\n (uri): ReadResourceResult => ({\n contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(buildCapabilitiesManifest(), null, 2) }],\n }),\n )\n\n server.registerResource(\n 'kanbo-capabilities-markdown',\n KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI,\n {\n title: 'Kanbo capabilities (Markdown)',\n description: 'The same manifest as kanbo://capabilities, laid out as Markdown — suitable for pasting '\n + 'into a CLAUDE.md or an AGENTS.md.',\n mimeType: 'text/markdown',\n },\n (uri): ReadResourceResult => ({\n contents: [{ uri: uri.href, mimeType: 'text/markdown', text: renderCapabilitiesMarkdown(buildCapabilitiesManifest()) }],\n }),\n )\n}\n","import {\n COMMENT_RULE,\n MOVE_CARD_RULE,\n PERSON_ONLY_RULE,\n STATUS_LINE_RULE,\n SUBTASK_RULE,\n WAIT_FOR_PERSON_RULE,\n} from '../ops/agent-rules'\nimport { KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI } from './resources'\nimport type { KanboToolName } from './tool-names'\n\n/** A tool name as the instructions print it — typed, so a renamed tool fails the build here. */\nfunction tool(name: KanboToolName): string {\n return `\\`${name}\\``\n}\n\n/**\n * What `kanbo mcp` tells a client when it connects — the `instructions` of the\n * MCP initialize result, which a client hands its model before any tool is\n * called. An agent with nothing but this server connected learns from it how\n * to work the board, with no instruction file in the project.\n *\n * Static on purpose: it is sent before any board is resolved, so it says\n * nothing about columns — `kanbo_prime` returns those. The rules are the\n * sentences `ops/agent-rules.ts` holds for every surface, with the tool beside\n * each; the full manifest stays a resource.\n */\nexport const KANBO_MCP_INSTRUCTIONS = [\n 'kanbo is a kanban board shared by people and agents; these tools read and change its cards.',\n '',\n `- At the start of a session call ${tool('kanbo_prime')} — it returns this board's columns and what each one means.`,\n `- Take work from ${tool('kanbo_ready')}.`,\n `- ${MOVE_CARD_RULE} (${tool('kanbo_card_move')})`,\n `- ${STATUS_LINE_RULE} (${tool('kanbo_status_line')})`,\n `- ${COMMENT_RULE} (${tool('kanbo_card_comment')})`,\n `- ${SUBTASK_RULE} (${tool('kanbo_card_create')} with \\`parent\\`)`,\n `- ${WAIT_FOR_PERSON_RULE} (${tool('kanbo_wait_approval')})`,\n `- ${PERSON_ONLY_RULE}`,\n `- Every tool, rule and canonical string: the resource \\`${KANBO_CAPABILITIES_MARKDOWN_RESOURCE_URI}\\`.`,\n].join('\\n')\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\n\nimport { maskDatabaseUrls } from '../../domain/database-url'\nimport type { ColumnRefusalDetails } from '../../domain/entry-rules'\nimport { describeColumnRefusal } from '../../domain/entry-rules'\nimport { BoardError } from '../../domain/errors'\nimport type { KanboToolName } from '../tool-names'\nimport type { KanboToolTransport } from '../transport'\n\n/**\n * What every board tool is, and the one way all of them answer.\n *\n * A tool is a name, a schema and exactly one call into the transport. It never\n * throws: a board rule the caller broke, a card that is not there, a server\n * that refused — all of it comes back as a tool error the model can read and\n * act on, because an exception out of a handler reaches the model as a\n * transport failure with nothing in it to correct.\n */\n\n/** The zod raw shape `McpServer.registerTool` takes. */\nexport type KanboToolShape = Record<string, z.ZodType>\n\n/**\n * What a tool call answers with: the result, or the reason it did not happen.\n *\n * It is the MCP result the protocol defines, narrowed to what the board's tools\n * actually produce — one block of text, and whether that text is a failure.\n */\nexport interface KanboToolResult extends CallToolResult {\n content: { type: 'text', text: string }[]\n isError?: true\n}\n\n/** One tool of the board, ready to register against any transport. */\nexport interface KanboTool {\n name: KanboToolName\n title: string\n description: string\n inputSchema: KanboToolShape\n run: (transport: KanboToolTransport, input: unknown) => Promise<KanboToolResult>\n}\n\n/**\n * Declare a tool: what it is called, what it takes, and the one thing it does.\n *\n * The arguments are parsed here as well as by the MCP server that dispatched\n * them, which is what lets the body be written against a typed input while the\n * list of tools stays one plain array a test can walk. What comes back is JSON,\n * unless the tool answers in prose and hands back a string — `kanbo_prime` is\n * the board talking to the agent, and quoting it would only make it harder to\n * read.\n */\nexport function defineKanboTool<Shape extends KanboToolShape>(definition: {\n name: KanboToolName\n title: string\n description: string\n inputSchema: Shape\n run: (transport: KanboToolTransport, input: z.output<z.ZodObject<Shape>>) => Promise<unknown>\n}): KanboTool {\n const schema = z.object(definition.inputSchema)\n return {\n name: definition.name,\n title: definition.title,\n description: definition.description,\n inputSchema: definition.inputSchema,\n run: async (transport, input) => {\n try {\n const answer = await definition.run(transport, schema.parse(input ?? {}))\n return toolText(typeof answer === 'string' ? answer : JSON.stringify(answer, null, 2))\n }\n catch (error) {\n return toolError(error)\n }\n },\n }\n}\n\n/**\n * The card a tool acts on. Every tool that takes one takes it under this name\n * and describes it the same way, so a model that learned the argument once\n * cannot be wrong about it on the next tool.\n */\nexport const cardArgument = z.string().min(1).describe('The card, by the id the board prints for it, for example MAN-012.')\n\n/** Where a card's work happens. */\nexport const executionModeArgument = z.enum(['worktree', 'main'])\n .describe('Where the work happens: worktree is a separate copy of the project, main is the project folder itself.')\n\n/** What a tool hands back when it did what it was asked. */\nfunction toolText(text: string): KanboToolResult {\n return { content: [{ type: 'text', text }] }\n}\n\n/**\n * Why the tool did not do what it was asked.\n *\n * A `BoardError` is a rule of the board the caller broke: its code names what\n * happened and its details say which value did it, so both are passed through\n * rather than translated into a sentence that could drift away from the rule.\n */\nfunction toolError(error: unknown): KanboToolResult {\n // A card held at a column's door carries a list; the agent reads it as one\n // line per rule, the same lines the command line prints.\n if (error instanceof BoardError && error.code === 'board_column_rules_unmet') {\n const text = describeColumnRefusal(error.details as unknown as ColumnRefusalDetails)\n return { content: [{ type: 'text', text }], isError: true }\n }\n if (error instanceof BoardError) {\n const details = error.details ? ` ${JSON.stringify(error.details)}` : ''\n return { content: [{ type: 'text', text: `${error.code}${details}` }], isError: true }\n }\n // Anything else is a message this package did not write — on an external\n // board, a driver's — and such a message is free to carry the whole\n // connection string in the middle of it.\n return {\n content: [{ type: 'text', text: maskDatabaseUrls(error instanceof Error ? error.message : String(error)) }],\n isError: true,\n }\n}\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** Anything a person has to read, written where they will find it. */\nexport const cardCommentTool = defineKanboTool({\n name: 'kanbo_card_comment',\n title: 'Comment on a card',\n description: 'Write a finding, a decision or an open question on the card, where a person reads it. Your transcript is not the card.',\n inputSchema: {\n card: cardArgument,\n content: z.string().min(1).describe('What to say, written for whoever opens this card next.'),\n },\n run: async (transport, input) => await transport.cardComment(input),\n})\n","import { z } from 'zod'\n\nimport { SUBTASK_WHEN_SENTENCE, subtaskSplitSentence } from '../../ops/agent-rules'\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/** What an agent is told about subtasks, word for word in the tool and in its `parent` field. */\nconst SUBTASK_SENTENCE = 'To create a subtask (sub-card) under another card, pass `parent` = that card\\'s id (its key, e.g. `MAN-012`).'\n\n/** A new card, or a sub-card of the one being worked on. */\nexport const cardCreateTool = defineKanboTool({\n name: 'kanbo_card_create',\n title: 'Create a card',\n description: `Put a new card on this board. ${SUBTASK_SENTENCE} ${SUBTASK_WHEN_SENTENCE} ${subtaskSplitSentence('pass `parent`')}`,\n inputSchema: {\n title: z.string().optional().describe('What the card is called. Leave it out and the card is titled with its own key; the description then says what the task is.'),\n description: z.string().optional().describe('What the card is about, spelled out for whoever picks it up.'),\n column: z.string().min(1).optional().describe('The column to open it in, by slug or name. The board\\'s first column when absent.'),\n parent: cardArgument.optional().describe(SUBTASK_SENTENCE),\n executionMode: executionModeArgument.optional(),\n },\n run: async (transport, input) => await transport.cardCreate(input),\n})\n","import { cardArgument, defineKanboTool } from './tool'\n\n/** One card in full. */\nexport const cardGetTool = defineKanboTool({\n name: 'kanbo_card_get',\n title: 'Read a card',\n description: 'Read one card in full: what it is for, the column it sits in, what it is doing right now, who it is waiting for, its parent (`parentIssueId`) and its subtasks (`subCards`).',\n inputSchema: {\n card: cardArgument,\n },\n run: async (transport, input) => await transport.cardGet(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** A pull request the card's work went into, named on the card so its facts reach it. */\nexport const cardLinkPrTool = defineKanboTool({\n name: 'kanbo_card_link_pr',\n title: 'Link a pull request to a card',\n description: 'Opened a pull request for this card? Link it here. Its state and CI results then reach the card as comments, '\n + 'without you writing them. Linking the same pull request again changes nothing.',\n inputSchema: {\n card: cardArgument,\n url: z.string().min(1).describe('The pull request, as https://github.com/<owner>/<repo>/pull/<number> or <owner>/<repo>#<number>.'),\n },\n run: async (transport, input) => await transport.cardLinkPullRequest(input),\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** The cards on the board, optionally only those in one column. */\nexport const cardListTool = defineKanboTool({\n name: 'kanbo_card_list',\n title: 'List cards',\n description: 'List the cards on this board in board order, optionally only the ones in one column.',\n inputSchema: {\n column: z.string().min(1).optional().describe('Only cards in this column, by its slug or name.'),\n limit: z.number().int().positive().optional().describe('How many cards to return. All of them when absent.'),\n },\n run: async (transport, input) => await transport.cardList(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** Moving a card, which is how the board tells the truth about it. */\nexport const cardMoveTool = defineKanboTool({\n name: 'kanbo_card_move',\n title: 'Move a card',\n description: 'Move a card to another column the moment its real state changes. Read kanbo_columns first: a card in the wrong column is a lie, '\n + 'and a column with entryRules refuses a card that does not meet them yet, listing what is missing.',\n inputSchema: {\n card: cardArgument,\n column: z.string().min(1).describe('The column to move it to, by its slug or name: in_progress, \"In Progress\".'),\n },\n run: async (transport, input) => await transport.cardMove(input),\n})\n","import { cardArgument, defineKanboTool } from './tool'\n\n/** The pull requests a card names. */\nexport const cardPullRequestsTool = defineKanboTool({\n name: 'kanbo_card_pull_requests',\n title: 'List a card\\'s pull requests',\n description: 'The pull requests linked to the card, in the order they were linked.',\n inputSchema: {\n card: cardArgument,\n },\n run: async (transport, input) => await transport.cardPullRequests(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/** What a card says: its title, what it is about, how urgent it is, how it is labelled. */\nexport const cardUpdateTool = defineKanboTool({\n name: 'kanbo_card_update',\n title: 'Update a card',\n description: 'Change what a card says: its title, its description, its priority, its labels or where its work happens.',\n inputSchema: {\n card: cardArgument,\n title: z.string().min(1).optional().describe('What the card is called.'),\n description: z.string().optional().describe('What the card is about. Replaces the description it has.'),\n priority: z.enum(['none', 'low', 'medium', 'high', 'urgent']).optional(),\n labels: z.array(z.string()).optional().describe('The labels the card carries, replacing the ones it has.'),\n executionMode: executionModeArgument.optional(),\n },\n run: async (transport, { card, ...patch }) => {\n // An update that names no field still writes: the board version goes up and\n // every reader is told the board changed when nothing did.\n if (Object.keys(patch).length === 0) {\n throw new Error('Nothing to update. Pass title, description, priority, labels or executionMode.')\n }\n return await transport.cardUpdate({ card, ...patch })\n },\n})\n","import { defineKanboTool } from './tool'\n\n/** The columns of the board, with the slug a card is moved by. */\nexport const columnsTool = defineKanboTool({\n name: 'kanbo_columns',\n title: 'Columns of the board',\n description: 'List this board\\'s columns in board order, each with the slug kanbo_card_move takes, what the column means here, '\n + 'and the entryRules a card must meet before you move it in.',\n inputSchema: {},\n run: async transport => await transport.columns(),\n})\n","import { defineKanboTool } from './tool'\n\n/** What the board tells an agent about itself, in the board's own words. */\nexport const primeTool = defineKanboTool({\n name: 'kanbo_prime',\n title: 'Read the board',\n description: 'Read what this board is: its columns, what each one means, and the rules a card travels by. Run this first.',\n inputSchema: {},\n run: async transport => (await transport.prime()).text,\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** The cards an agent may pick up right now. */\nexport const readyTool = defineKanboTool({\n name: 'kanbo_ready',\n title: 'Cards ready to pick up',\n description: 'List the cards that are spelled out, that nobody is working on and that are waiting for no one, in board order.',\n inputSchema: {\n limit: z.number().int().positive().optional().describe('How many cards to return. All of them when absent.'),\n },\n run: async (transport, input) => await transport.ready(input),\n})\n","import { z } from 'zod'\n\nimport { defineKanboTool } from './tool'\n\n/** How a run ended. An app that launches agents ends the runs it started; this is for the ones nobody launched. */\nexport const runFinishTool = defineKanboTool({\n name: 'kanbo_run_finish',\n title: 'Finish a run',\n description: 'Record how a run you started ended, so the card stops saying someone is working on it.',\n inputSchema: {\n run: z.string().min(1).describe('The run, by the id kanbo_run_start gave back.'),\n state: z.enum(['finished', 'failed', 'stopped']).describe('How it ended.'),\n errorText: z.string().min(1).optional().describe('What went wrong, for a run that failed.'),\n },\n run: async (transport, input) => await transport.runFinish(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool, executionModeArgument } from './tool'\n\n/**\n * Saying a card is being worked on.\n *\n * Only an agent that launched itself needs this. An app that launches agents writes the run row\n * before the agent it launched starts, which is why this tool is not published\n * to one.\n */\nexport const runStartTool = defineKanboTool({\n name: 'kanbo_run_start',\n title: 'Start a run',\n description: 'Record that you have started working on a card, so nobody else picks it up while you are on it.',\n inputSchema: {\n card: cardArgument,\n agent: z.string().min(1).describe('What to call whoever is working on the card.'),\n branch: z.string().min(1).optional().describe('The branch the work happens on.'),\n executionMode: executionModeArgument.optional(),\n session: z.string().min(1).optional().describe('Your own log of this run, if you have one: claude:<session id> from Claude Code, codex:<session id> from Codex (the session id Codex prints).'),\n },\n run: async (transport, input) => await transport.runStart(input),\n})\n","import { defineKanboTool } from './tool'\n\n/** The board's sprints, the current one marked. Read-only: closing a sprint is a person's (ruling 5x-4). */\nexport const sprintsTool = defineKanboTool({\n name: 'kanbo_sprints',\n title: 'Sprints of the board',\n description: 'List this board\\'s milestones as sprints by start date — dates in unix seconds, open and done card counts — '\n + 'with current: true on the open one running now. Read-only: closing a sprint is a person\\'s decision.',\n inputSchema: {},\n run: async transport => await transport.sprints(),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/** What the card is doing right now, which is what a person watching sees. */\nexport const statusLineTool = defineKanboTool({\n name: 'kanbo_status_line',\n title: 'Set the status line',\n description: 'Say what the card is doing right now. Write one at every step, including before and after anything long-running.',\n inputSchema: {\n card: cardArgument,\n text: z.string().min(1).describe('One sentence, present tense, about what is happening on the card right now.'),\n },\n run: async (transport, input) => await transport.statusLine(input),\n})\n","import { z } from 'zod'\n\nimport { cardArgument, defineKanboTool } from './tool'\n\n/**\n * The end of a turn that needs a person.\n *\n * There is no tool that approves. A person approves on the board, and the card\n * stays waiting until they do — an agent that could take its own card out of\n * waiting would make every card waiting for a person a lie.\n */\nexport const waitApprovalTool = defineKanboTool({\n name: 'kanbo_wait_approval',\n title: 'Hand the card to a person',\n description: 'Mark the card as waiting for a person and end your turn. A person approves or returns it; you will be told the answer.',\n inputSchema: {\n card: cardArgument,\n text: z.string().min(1).optional().describe('The status line to leave, saying what the person is being asked to look at.'),\n },\n run: async (transport, input) => await transport.waitApproval(input),\n})\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport type { KanboToolName } from '../tool-names'\nimport type { KanboToolTransport } from '../transport'\nimport { cardCommentTool } from './card-comment'\nimport { cardCreateTool } from './card-create'\nimport { cardGetTool } from './card-get'\nimport { cardLinkPrTool } from './card-link-pr'\nimport { cardListTool } from './card-list'\nimport { cardMoveTool } from './card-move'\nimport { cardPullRequestsTool } from './card-pull-requests'\nimport { cardUpdateTool } from './card-update'\nimport { columnsTool } from './columns'\nimport { primeTool } from './prime'\nimport { readyTool } from './ready'\nimport { runFinishTool } from './run-finish'\nimport { runStartTool } from './run-start'\nimport { sprintsTool } from './sprints'\nimport { statusLineTool } from './status-line'\nimport type { KanboTool } from './tool'\nimport { waitApprovalTool } from './wait-approval'\n\n/**\n * Every tool the board has, in the order it declares them.\n *\n * The list is the board's, not a server's: the same sixteen are registered\n * whether they run against a server or against the board directly, and the\n * only thing a host decides is which of them it publishes.\n */\nexport const KANBO_TOOLS: readonly KanboTool[] = [\n primeTool,\n readyTool,\n columnsTool,\n sprintsTool,\n cardGetTool,\n cardListTool,\n cardCreateTool,\n cardUpdateTool,\n cardMoveTool,\n cardCommentTool,\n cardLinkPrTool,\n cardPullRequestsTool,\n statusLineTool,\n waitApprovalTool,\n runStartTool,\n runFinishTool,\n]\n\n/**\n * The two tools an agent launched by an app has no use for: the app wrote the\n * run row before the agent started and ends it when the session settles. An agent\n * that launched itself — from a terminal, on another host — does have to record\n * its own run, which is why they exist at all.\n */\nconst RUN_TOOL_NAMES: readonly KanboToolName[] = ['kanbo_run_start', 'kanbo_run_finish']\n\n/** What a host decides about the board's tools. */\nexport interface KanboToolOptions {\n /** Publish `kanbo_run_start` and `kanbo_run_finish`. False for a host that manages runs itself. */\n includeRunTools: boolean\n}\n\n/** One tool bound to a transport, ready for an MCP server to register. */\nexport interface KanboToolRegistration {\n name: KanboToolName\n register: (server: McpServer) => void\n}\n\n/**\n * The board's tools, bound to the transport they run against.\n *\n * Nothing here reaches the board: the registration is what an MCP server needs\n * to publish the tool, and the transport is only called when a client actually\n * calls one.\n */\nexport function buildKanboTools(\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): KanboToolRegistration[] {\n return KANBO_TOOLS\n .filter(tool => options.includeRunTools || !RUN_TOOL_NAMES.includes(tool.name))\n .map(tool => ({\n name: tool.name,\n register: (server: McpServer) => {\n server.registerTool(\n tool.name,\n { title: tool.title, description: tool.description, inputSchema: tool.inputSchema },\n async (input: unknown) => await tool.run(transport, input),\n )\n },\n }))\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport { KANBO_MCP_INSTRUCTIONS } from './instructions'\nimport { registerKanboCapabilitiesResources } from './resources'\nimport type { KanboToolOptions } from './tools'\nimport { buildKanboTools } from './tools'\nimport type { KanboToolTransport } from './transport'\n\n/** The name a client sees this server under. */\nexport const KANBO_MCP_SERVER_NAME = 'kanbo'\n\n/** The version it reports. The tools it carries are the board's, and the board versions itself. */\nconst KANBO_MCP_SERVER_VERSION = KANBO_PACKAGE_VERSION\n\n/**\n * Add the board's tools to a server that already exists.\n *\n * This is how an app with an MCP server of its own carries them: one server holds every\n * tool an agent has, and the board's are part of that inventory rather than a\n * second server the runtime would have to be told about.\n */\nexport function registerKanboTools(\n server: McpServer,\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): void {\n for (const tool of buildKanboTools(transport, options)) {\n tool.register(server)\n }\n}\n\n/**\n * A server that is only the board — what `kanbo mcp` serves to a client that\n * brings no board tools of its own: an editor, an agent CLI, another machine.\n *\n * It carries the capabilities manifest as two resources, on top of the tools\n * `registerKanboTools` adds — a client here has no other way to learn the\n * board's rules, its canonical strings or its CLI equivalents before it calls\n * anything. An app's own MCP server does not get them through this\n * function: it documents its capabilities its own way, and adding a second\n * `kanbo://` resource namespace to an inventory that is not only the board's\n * is a decision for that server to make, not this one.\n *\n * For the same reason it alone sends `KANBO_MCP_INSTRUCTIONS` in the initialize\n * result: the instructions of a server that is only the board.\n */\nexport function createKanboMcpServer(\n transport: KanboToolTransport,\n options: KanboToolOptions,\n): McpServer {\n const server = new McpServer({\n name: KANBO_MCP_SERVER_NAME,\n version: KANBO_MCP_SERVER_VERSION,\n }, { instructions: KANBO_MCP_INSTRUCTIONS })\n registerKanboTools(server, transport, options)\n registerKanboCapabilitiesResources(server)\n return server\n}\n","import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport type { Command } from 'commander'\n\nimport { createDbTransport } from '../../mcp/db-transport'\nimport { createKanboMcpServer } from '../../mcp/server'\nimport { createCliActor } from '../actor'\nimport { describeFailure } from '../failure'\n\n/**\n * `kanbo mcp` — the board as tools, for any MCP client.\n *\n * It serves the board directly — the file, or the external database the project\n * is bound to — because the tool that spawned it — Claude Code, Codex, Cursor —\n * may be the only thing running on this machine: an agent working in a checkout\n * with no server should still be able to drive its card. An app with an MCP\n * server of its own can hold the same tools there instead, over HTTP\n * (`kanbo/mcp`).\n *\n * Every write is filed under this shell, exactly as every `kanbo` command's\n * is, and all sixteen tools are published: an agent that started itself has to\n * record its own run, which is the one thing an app that launches agents does\n * for them.\n */\n\n/** What `kanbo mcp` is told, as every other command spells it. */\ninterface McpOptions {\n db?: string\n databaseUrl?: string\n workspace?: string\n}\n\nexport function registerMcpCommand(program: Command): void {\n program\n .command('mcp')\n .description('serve this board to an MCP client over stdio')\n // The output options every other command carries are absent on purpose:\n // stdout is the protocol here, and anything printed on it is a broken\n // session rather than a badly formatted one.\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to serve instead of a board file')\n .option('--workspace <nameOrId>', 'workspace the board tools are about')\n .action(async (options: McpOptions) => {\n const board = await createDbTransport({\n dbPath: options.db,\n databaseUrl: options.databaseUrl,\n workspaceId: options.workspace,\n actor: createCliActor(),\n })\n // Closed once, whichever way this ends. `onclose` is the client hanging\n // up and cannot be awaited — it is not an async hook — so the promise is\n // kept here and awaited by whoever gets there second. A connection that\n // fails to hang up is worth a line on stderr, which is not the protocol\n // stream, and never an unhandled rejection: that would turn a clean\n // shutdown into a crash.\n let closing: Promise<void> | undefined\n const close = (): Promise<void> => (closing ??= board.close().catch((error: unknown) => {\n console.error(describeFailure(error).message)\n }))\n\n const server = createKanboMcpServer(board, { includeRunTools: true })\n server.server.onclose = () => void close()\n try {\n await server.connect(new StdioServerTransport())\n }\n catch (error) {\n await close()\n throw error\n }\n })\n}\n","import type { Command } from 'commander'\n\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport { migrateBoardFile } from '../../sqlite/migrate'\nimport { openBoardDatabase } from '../../sqlite/open-database'\nimport type { BoardTargetCommandOptions } from '../command'\nimport { withTargetOptions } from '../command'\nimport type { BoardTarget } from '../db-target'\nimport { resolveDbTarget } from '../db-target'\nimport { CliError, printResult, readFormat } from '../output'\nimport { openPostgresBoard } from '../postgres-board'\n\n/**\n * `kanbo migrate` — creating the board in a database that has none, and\n * bringing one that has an older board up to date.\n *\n * It exists for the two homes this package owns: an external board, which has\n * no other owner at all, and a board file of a project's own, which this\n * package builds and this package alone keeps current (ruling 5-5). A host app's\n * own database is neither — its schema is the app's, and the app migrates it —\n * so this command refuses to touch it.\n *\n * It is the owner's command, so against an external board it always resolves\n * the board's own connection string and never the agent's, whatever shell it\n * is run from: `kanban_agent` may not create a table, and an administrator who\n * happens to be standing in an agent's shell would get a permission\n * error instead of a migration.\n */\n\n/** What a person is told when they point this at a host app's own database file. */\nexport const BOARD_FILE_NOT_MIGRATED_MESSAGE\n = 'This database file belongs to the app the board lives in, and that app migrates it. Pass --database-url, set '\n + 'KANBO_DATABASE_URL, or point --db at a board file of this project\\'s own (kanbo init --file).'\n\nexport function registerMigrateCommand(program: Command): void {\n withTargetOptions(program\n .command('migrate')\n .description('create or update the board schema in an external Postgres database, or a board file of its own'))\n .action(async (options: BoardTargetCommandOptions) => {\n readFormat(options)\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: false,\n })\n if (target.kind === 'sqlite') {\n await migrateOwnFile(target, options)\n return\n }\n\n const board = await openPostgresBoard(target.url)\n try {\n // Drizzle keeps what it applied in `__drizzle_migrations`, which is\n // what makes running this again say the same thing and change nothing.\n await board.migrate()\n }\n finally {\n await board.close()\n }\n\n const database = maskDatabaseUrl(target.url)\n printResult({ value: { database }, text: `The board schema is up to date — ${database}` }, options)\n })\n}\n\n/**\n * Migrate a board file — this package's own chain, and only on a file this\n * package created in the first place. A host app's database carries no such\n * marker and is refused: `kanbo migrate` is never how an app's own database\n * gets its schema.\n */\nasync function migrateOwnFile(\n target: Extract<BoardTarget, { kind: 'sqlite' }>,\n options: BoardTargetCommandOptions,\n): Promise<void> {\n if (target.owner !== 'kanbo') {\n throw new CliError(1, BOARD_FILE_NOT_MIGRATED_MESSAGE)\n }\n\n const board = await openBoardDatabase(target.path)\n try {\n // Drizzle's own migrator, same as `kanbo init --file`: idempotent by\n // `__drizzle_migrations`, so running this again says the same thing.\n migrateBoardFile(board.database)\n }\n finally {\n board.close()\n }\n\n printResult({\n value: { database: target.path },\n text: `The board schema is up to date — ${target.path}`,\n }, options)\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\n\n/**\n * `kanbo prime` — what the board tells an agent about itself.\n *\n * The first thing an agent should run, and the reason the instruction block\n * `kanbo init` writes says so: the columns of this board with their meaning,\n * and the rules a card travels by. The text belongs to the board, not to this\n * tool, so the command only prints what `buildPrimeText` says.\n */\nexport function registerPrimeCommand(program: Command): void {\n withBoardOptions(program\n .command('prime')\n .description('print the columns of this board and the rules a card travels by'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const text = await session.ops.buildPrimeText(session.workspace.id)\n return { value: { text }, text }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport type { BoardCommandOptions } from '../command'\nimport { parseCount, runBoardCommand, withBoardOptions } from '../command'\nimport { CARD_VIEW_FIELDS, describeCards, projectCard } from '../view'\n\ninterface ReadyOptions extends BoardCommandOptions {\n limit?: number\n}\n\n/**\n * `kanbo ready` — the cards an agent may pick up right now.\n *\n * What counts as ready is the board's rule, not this tool's: spelled out,\n * nobody working on it, nobody's turn but the agent's. An agent asking for work\n * takes the first line and starts there.\n */\nexport function registerReadyCommand(program: Command): void {\n withBoardOptions(program\n .command('ready')\n .description('cards that are spelled out, unclaimed and nobody else\\'s turn')\n .option('--limit <count>', 'how many cards to print', parseCount))\n .action(async (options: ReadyOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const columns = await session.ops.listColumns(session.workspace.id)\n const rows = await session.ops.listReady({ workspaceId: session.workspace.id, limit: options.limit })\n const runs = await session.ops.readBoardProjectionForIssues(rows.map(card => card.id))\n const cards = rows.map((card, index) => projectCard(card, columns, runs[index]))\n return { value: cards, text: describeCards(cards), fields: CARD_VIEW_FIELDS }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { requireCard, runBoardCommand, withBoardOptions } from '../command'\nimport { describeCard, projectCard } from '../view'\n\ninterface ReturnOptions extends BoardCommandOptions {\n comment: string\n to?: string\n}\n\n/**\n * `kanbo return` — a person sending a card back with a reason.\n *\n * The reason is required because it is the point: it goes on the card as the\n * person's comment and as the status line, so the next agent to pick the card\n * up reads why it came back before anything else. Whatever was running on the\n * card is stopped in the same write, by the board.\n *\n * Nothing else happens here. A chat session parked on this card is woken by\n * the server, which is watching the board — the terminal does not have to know\n * that anything is waiting.\n */\nexport function registerReturnCommand(program: Command): void {\n withBoardOptions(program\n .command('return')\n .description('send a card back a column, with the reason on it')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--comment <text>', 'why the card is coming back')\n .option('--to <column>', 'the column to send it to, by slug, name or id'))\n .action(async (reference: string, options: ReturnOptions) => {\n const actor = requireHumanActor('return')\n\n await runBoardCommand(options, 'write', async (session) => {\n const existing = await requireCard(session, reference)\n const { card, stoppedRunIds } = await session.ops.returnCard(existing.id, {\n comment: options.comment,\n toStatusName: options.to,\n }, actor)\n\n const [columns, runs] = await Promise.all([\n session.ops.listColumns(session.workspace.id),\n session.ops.readBoardProjectionForIssue(card.id),\n ])\n const view = projectCard(card, columns, runs)\n const stopped = stoppedRunIds.length > 0 ? `\\nStopped ${stoppedRunIds.length} run(s)` : ''\n return {\n value: { card: view, stoppedRunIds },\n text: `Returned ${view.id} to ${view.column ?? '—'}${stopped}\\n\\n${describeCard(view)}`,\n }\n })\n })\n}\n","import type { Command } from 'commander'\n\nimport { maskDatabaseUrl } from '../../domain/database-url'\nimport { BOARD_AGENT_ROLE, BOARD_PERSON_ROLE, BOARD_ROLE_SQL } from '../../postgres/roles.sql'\nimport type { BoardTargetCommandOptions } from '../command'\nimport { withTargetOptions } from '../command'\nimport { resolveDbTarget } from '../db-target'\nimport { CliError, printResult, readFormat } from '../output'\nimport { openPostgresBoard } from '../postgres-board'\n\n/**\n * `kanbo roles` — the two roles an external board is worked through, and the\n * rules the database itself keeps about them.\n *\n * Two commands rather than one because applying them is a privileged act: the\n * SQL creates roles and triggers, which needs the owner of the board's tables,\n * and plenty of databases are administered by someone who will never run this\n * tool. `print` hands them the script to read and to apply themselves; `apply`\n * runs it for whoever already holds the owner's connection string.\n *\n * No password is ever accepted here, on the command line or anywhere else. A\n * password on a command line is in the shell's history and in the process list\n * of every other user on the machine, and the one place it belongs is the\n * `alter role` an administrator types themselves.\n *\n * `apply` is the owner's command, so it always resolves the board's own\n * connection string and never the agent's, whatever shell it is run from — the\n * role being configured cannot be the role doing the configuring. `print`\n * resolves nothing at all and therefore takes no options: it is one constant,\n * on stdout, for a person to read or redirect into `psql`.\n */\n\n/** What a person is told when there is no external board to apply the roles to. */\nexport const ROLES_NEED_A_DATABASE_MESSAGE\n = 'The board roles belong to an external Postgres board. A board file is protected by whoever can open it — '\n + 'pass --database-url or set KANBO_DATABASE_URL.'\n\nexport function registerRolesCommands(program: Command): void {\n const roles = program\n .command('roles')\n .description('the two database roles an external board is worked through')\n\n roles\n .command('print')\n .description('print the SQL that creates the roles and the rules they live under')\n .action(() => {\n console.log(BOARD_ROLE_SQL)\n })\n\n withTargetOptions(roles\n .command('apply')\n .description('run that SQL against the external board, as its owner'))\n .action(async (options: BoardTargetCommandOptions) => {\n readFormat(options)\n const target = resolveDbTarget({\n explicitPath: options.db,\n explicitUrl: options.databaseUrl,\n asAgent: false,\n })\n if (target.kind !== 'postgres') {\n throw new CliError(1, ROLES_NEED_A_DATABASE_MESSAGE)\n }\n\n const board = await openPostgresBoard(target.url)\n try {\n // Written to be run again: every role is created only if it is missing\n // and every trigger is replaced, so this is how the rules are updated too.\n await board.runScript(BOARD_ROLE_SQL)\n }\n finally {\n await board.close()\n }\n\n const database = maskDatabaseUrl(target.url)\n printResult({\n value: { database, roles: [BOARD_PERSON_ROLE, BOARD_AGENT_ROLE] },\n text: describeApplied(database),\n }, options)\n })\n}\n\n/**\n * What is left to do, said where the person is standing. The roles can log in\n * and have no password at all, which is not a state to leave a shared database\n * in and not one this tool can fix for them.\n */\nfunction describeApplied(database: string): string {\n return [\n `Applied the board roles to ${database} — ${BOARD_PERSON_ROLE} and ${BOARD_AGENT_ROLE}.`,\n '',\n 'Both were created with login and no password. Set one before handing out a connection string:',\n ` alter role ${BOARD_AGENT_ROLE} with password '…';`,\n ` alter role ${BOARD_PERSON_ROLE} with password '…';`,\n '',\n `A session must connect AS the role: the rules read current_user, so a login merely granted membership in `\n + `${BOARD_AGENT_ROLE} keeps its own name and none of them fires.`,\n ].join('\\n')\n}\n","import type { Command } from 'commander'\n\nimport type { FinishRunInput } from '../../ops/runs'\nimport type { Issue, IssueRun } from '../../sqlite/schema'\nimport { createCliActor, requireHumanActor } from '../actor'\nimport type { BoardCommandOptions, BoardSession } from '../command'\nimport { parseExecutionMode, requireCard, requireRun, runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { projectRun, RUN_VIEW_FIELDS } from '../view'\n\nconst RUN_STATES = ['finished', 'failed', 'stopped'] as const\n\ninterface StartOptions extends BoardCommandOptions {\n agent: string\n branch?: string\n executionMode?: Issue['executionMode']\n session?: string\n}\n\ninterface AttachSessionOptions extends BoardCommandOptions {\n replace?: boolean\n}\n\ninterface FinishOptions extends BoardCommandOptions {\n state: FinishRunInput['state']\n errorText?: string\n}\n\n/**\n * `kanbo run …` — saying that a card is being worked on, and how that ended.\n *\n * A run launched from a terminal is recorded exactly like one an app launched:\n * the card gets a line saying who is on it and a comment naming the attempt, so\n * `kanbo ready` stops offering the card to the next agent that asks. Its\n * launch is filed as `external` — no app started this, and a board that\n * claimed otherwise would send someone looking for a session that never existed.\n */\nexport function registerRunCommands(program: Command): void {\n const run = program\n .command('run')\n .description('the launches of a card')\n\n withBoardOptions(run\n .command('start')\n .description('say that you are working on a card')\n .argument('<card>', 'the card, as MAN-012, MAN-12 or 12')\n .requiredOption('--agent <name>', 'what to call whoever is working')\n .option('--branch <branch>', 'the branch the work happens on')\n .option('--execution-mode <mode>', 'worktree or main; defaults to what the card says', parseExecutionMode)\n .option('--session <ref>', 'your own log of this run: claude:<session id> or codex:<session id>'))\n .action(async (reference: string, options: StartOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const card = await requireCard(session, reference)\n const started = await session.ops.startRun(card.id, {\n agentName: options.agent,\n branch: options.branch,\n executionMode: options.executionMode,\n externalSessionRef: options.session,\n launchedByKind: 'external',\n }, createCliActor())\n const view = projectRun({ ...started, attempt: await attemptFor(session, started) })\n return { value: view, text: `Run ${view.id} started on ${card.id}`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('attach-session')\n .description('say which of your own logs a run is, while it runs or after it ended; a run keeps the first one named')\n .argument('<runId>', 'the run, as `kanbo run start` printed it')\n .argument('<ref>', 'claude:<session id> or codex:<session id>')\n .option('--replace', 'put this log in place of the one the run names; a person\\'s own terminal only'))\n .action(async (runId: string, ref: string, options: AttachSessionOptions) => {\n // Checked before the board is opened, the same as `approve`.\n const actor = options.replace ? requireHumanActor('run attach-session --replace') : null\n await runBoardCommand(options, 'write', async (session) => {\n const found = await requireRun(session, runId)\n const attached = actor\n ? await session.ops.clearRunSessionRef(found.id, actor, { replaceWith: ref })\n : await session.ops.recordRunSessionRef(found.id, ref)\n const view = projectRun({ ...attached, attempt: await attemptFor(session, attached) })\n return { value: view, text: `Run ${view.id} carries ${attached.externalSessionRef}`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('clear-session')\n .description('say that the log a run names is not its log; the run then names none')\n .argument('<runId>', 'the run, as `kanbo run start` printed it'))\n .action(async (runId: string, options: BoardCommandOptions) => {\n const actor = requireHumanActor('run clear-session')\n await runBoardCommand(options, 'write', async (session) => {\n const found = await requireRun(session, runId)\n const cleared = await session.ops.clearRunSessionRef(found.id, actor)\n const view = projectRun({ ...cleared, attempt: await attemptFor(session, cleared) })\n return { value: view, text: `Run ${view.id} names no log`, fields: RUN_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(run\n .command('finish')\n .description('say how a run ended')\n .argument('<runId>', 'the run, as `kanbo run start` printed it')\n .requiredOption('--state <state>', `one of ${RUN_STATES.join(', ')}`, parseRunState)\n .option('--error-text <text>', 'what went wrong, for a failed run'))\n .action(async (runId: string, options: FinishOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const run = await requireRun(session, runId)\n const finished = await session.ops.finishRun(run.id, {\n state: options.state,\n errorText: options.errorText,\n }, createCliActor())\n const view = projectRun({ ...finished, attempt: await attemptFor(session, finished) })\n return { value: view, text: `Run ${view.id} ${view.state}`, fields: RUN_VIEW_FIELDS }\n })\n })\n}\n\n/**\n * Which attempt a run is. `startRun` and `finishRun` hand back the row\n * itself; the count only exists once every run of the card is lined up.\n */\nasync function attemptFor(session: BoardSession, run: IssueRun): Promise<number> {\n const runs = await session.ops.listRuns(run.issueId)\n return runs.find(candidate => candidate.id === run.id)?.attempt ?? runs.length\n}\n\nfunction parseRunState(value: string): FinishRunInput['state'] {\n const state = RUN_STATES.find(candidate => candidate === value)\n if (!state) {\n throw new CliError(1, `Unknown run state \"${value}\". Use one of ${RUN_STATES.join(', ')}.`)\n }\n return state\n}\n","import type { BoardActor } from '../ops/types'\n\n/**\n * Who a request writes as.\n *\n * An app with sign-in knows its caller from the session a request carries.\n * This server has none to ask, so it says what `kanbo` says in a terminal: every\n * write is filed `external`, under the user who started the server. What a\n * request cannot do is name its own kind — a header that could say `user`\n * would make approval worth nothing — so the only thing a request may say\n * about itself is that it is *less*: `x-kanbo-actor: agent`.\n *\n * Acting for a person takes two things: the server was not started from a\n * shell started for an agent (the detection `kanbo approve` refuses\n * on), and the request presented the server's token. Without a token anyone\n * who can reach the port could be anything, so nobody acts for a person.\n */\n\n/** The header a request downgrades itself with. */\nexport const ACTOR_HEADER = 'x-kanbo-actor'\n\n/** Who the server may write as, fixed when it starts. */\nexport interface ServeActors {\n /** Every write is filed under this — `external`, as the command line files it. */\n writer: BoardActor\n /** The person who started the server, or `null` when it was started from an agent's shell. */\n person: BoardActor | null\n}\n\n/** Who one request writes as, by what it is doing. */\nexport interface RequestActors {\n /** Status lines, comments, runs, links, context refs — the writes anyone may make. */\n writer: BoardActor\n /**\n * Putting a card in a column — create, update, move, reorder, bulk. For a\n * person the column's entry rules warn (`unmetRules`) instead of refusing, as\n * they do for a person on the board page and in their own terminal.\n */\n placement: BoardActor\n /**\n * What only a person may do — approve, return, close a sprint, set a\n * column's entry rules, delete a comment the board wrote, drop a link somebody\n * else made. When the request may not act for one this is the writer, and the\n * board refuses it with its own 403.\n */\n person: BoardActor\n}\n\n/** What a request says about itself that decides its actors. */\nexport interface RequestIdentity {\n /** The `x-kanbo-actor` header, as many times as it was sent. */\n actorHeader: string | string[] | undefined\n /** Whether the request presented the server's token — only then may it act for a person. */\n authenticated: boolean\n}\n\n/**\n * The actors one request gets: the person's rights only for a request that\n * presented the token, on a server whose starter may act as one, and that did\n * not give them up with `agent` among the values of `x-kanbo-actor`.\n */\nexport function resolveRequestActors(actors: ServeActors, identity: RequestIdentity): RequestActors {\n const person = identity.authenticated && !saysAgent(identity.actorHeader) ? actors.person : null\n if (!person) {\n return { writer: actors.writer, placement: actors.writer, person: actors.writer }\n }\n return { writer: actors.writer, placement: { ...actors.writer, placesForPerson: true }, person }\n}\n\n/** Whether any comma-separated value of the header, however it is spelled or repeated, is `agent`. */\nfunction saysAgent(header: string | string[] | undefined): boolean {\n return [header ?? []].flat().flatMap(value => value.split(',')).some(value => value.trim().toLowerCase() === 'agent')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport type { ServerResponse } from 'node:http'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/**\n * The board page `kanbo serve` answers `/` with: a person opens the server's\n * address in a browser and works the board there.\n *\n * The page holds no data — everything it shows it asks the `/issues` routes\n * for, with the token the person typed — so it is served without the token.\n * Its script and stylesheet are built from `./page/` into `dist/page/` by\n * `tsdown.page.config.ts`; this module only finds them and answers with them,\n * under a policy that lets the page load nothing but itself and talk to\n * nothing but this server.\n */\n\n/** What the page may load and talk to: itself, and nothing else. */\nconst BOARD_PAGE_CSP = [\n 'default-src \\'none\\'',\n 'script-src \\'self\\'',\n 'style-src \\'self\\'',\n 'connect-src \\'self\\'',\n 'img-src \\'self\\' data:',\n 'base-uri \\'none\\'',\n 'form-action \\'none\\'',\n 'frame-ancestors \\'none\\'',\n].join('; ')\n\nconst SCRIPT_PATH = '/assets/board.js'\nconst STYLESHEET_PATH = '/assets/board.css'\n\n/** The document itself. No data in it; the `data:` icon keeps the browser from asking for `/favicon.ico`. */\nconst BOARD_PAGE_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Kanbo</title>\n<link rel=\"icon\" href=\"data:,\">\n<link rel=\"stylesheet\" href=\"${STYLESHEET_PATH}\">\n<script src=\"${SCRIPT_PATH}\" defer></script>\n</head>\n<body>\n<div id=\"app\"></div>\n</body>\n</html>\n`\n\n/** The built script and stylesheet, read once when the server starts. */\nexport interface BoardPageAssets {\n script: string\n stylesheet: string\n}\n\n/**\n * Where the built page is, as seen from the running file: `dist/page/` beside\n * the bundled binary, or the package's `dist/page/` from `src/serve/` in a\n * checkout — which exists only once `npm run build` has run.\n */\nexport function resolveBoardPageDirectory(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n const candidates = [join(here, 'page'), join(here, '..', '..', 'dist', 'page')]\n return candidates.find(directory => existsSync(join(directory, 'board.js'))) ?? candidates[1]\n}\n\n/** The page's two files from `directory`, or `null` when the page has not been built there. */\nexport function readBoardPageAssets(directory: string): BoardPageAssets | null {\n const script = join(directory, 'board.js')\n const stylesheet = join(directory, 'board.css')\n if (!existsSync(script) || !existsSync(stylesheet)) {\n return null\n }\n return { script: readFileSync(script, 'utf8'), stylesheet: readFileSync(stylesheet, 'utf8') }\n}\n\n/** Whether a path is one of the page's own. */\nexport function isBoardPagePath(pathname: string): boolean {\n return pathname === '/' || pathname === SCRIPT_PATH || pathname === STYLESHEET_PATH\n}\n\n/**\n * Answer a page path. With no built page, `/` says how to build it rather than\n * leaving a person at a blank screen or a `401`.\n */\nexport function sendBoardPage(response: ServerResponse, pathname: string, assets: BoardPageAssets | null): void {\n if (!assets) {\n const body = JSON.stringify({ code: 'not_found', message: 'The board page is not built. Run: npm run build' })\n response.writeHead(404, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) })\n response.end(body)\n return\n }\n const [contentType, body] = pathname === SCRIPT_PATH\n ? ['text/javascript; charset=utf-8', assets.script]\n : pathname === STYLESHEET_PATH\n ? ['text/css; charset=utf-8', assets.stylesheet]\n : ['text/html; charset=utf-8', BOARD_PAGE_HTML]\n response.writeHead(200, {\n 'content-type': contentType,\n 'content-length': Buffer.byteLength(body),\n 'cache-control': 'no-cache',\n 'content-security-policy': BOARD_PAGE_CSP,\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n })\n response.end(body)\n}\n","import type { BoardErrorCode } from '../domain/errors'\n\n/** What one board failure is over HTTP: the status, the sentence, and the code a client matches on when it is renamed. */\nexport interface BoardErrorResponse {\n status: number\n message: string\n code?: string\n}\n\n/**\n * What each board failure becomes over HTTP.\n *\n * The board names its failures and nothing else (`../domain/errors.ts`); the\n * status code and the sentence a client reads are decided here, once, for both\n * edges that serve it over HTTP — `kanbo serve` and any app that serves the\n * same routes — so a client written against one reads the other's failures the\n * same way. The\n * table is exhaustive on purpose — a new code in the package fails to compile\n * until this module says what it means to a caller — and it follows one rule: something the caller asked for that is not\n * there is 404, something the caller asked for that cannot be done is 400,\n * answering for a person — approving a card, sending it back or closing a\n * sprint — as anyone but one is 403, dropping a pull request link somebody else made is the same 403\n * for the same reason, and acting on a run that has already ended is 409,\n * because the answer would have been yes a moment earlier. An agent putting a\n * card in a column whose entry rules it does not meet yet is 409 for the same\n * reason turned around: the answer will be yes once the card meets them, and\n * `details.unmet` says what is missing.\n *\n * A failure may also be renamed here: the package names its codes after the\n * board, while every error the `/issues` routes answer with is named after the\n * issue, and a client matching on `issue_*` should not have to learn a second\n * spelling. The code carries through unchanged when no `code` is given.\n */\nexport const BOARD_ERROR_RESPONSES: Record<BoardErrorCode, BoardErrorResponse> = {\n board_approval_requires_user: { status: 403, message: 'Only a person can approve a card', code: 'issue_approval_requires_user' },\n board_column_rules_requires_user: { status: 403, message: 'Only a person can set what a column asks of a card', code: 'issue_column_rules_requires_user' },\n board_column_rules_unmet: { status: 409, message: 'The card does not meet what that column asks for yet', code: 'issue_column_rules_unmet' },\n board_entry_rule_invalid: { status: 400, message: 'Unknown column entry rule' },\n board_execution_mode_locked: { status: 400, message: 'Execution mode cannot change once the card has been launched' },\n board_pull_request_invalid: { status: 400, message: 'Not a GitHub pull request: give its URL or owner/repo#number' },\n board_pull_request_not_found: { status: 404, message: 'The card names no such pull request' },\n board_pull_request_not_yours: { status: 403, message: 'You did not link that pull request', code: 'issue_pull_request_not_yours' },\n board_return_no_previous_column: { status: 400, message: 'The card is already in the first column' },\n board_return_requires_user: { status: 403, message: 'Only a person can send a card back', code: 'issue_return_requires_user' },\n board_run_already_finished: { status: 409, message: 'Run has already finished' },\n board_run_not_found: { status: 404, message: 'Run not found' },\n board_run_session_ref_conflict: { status: 409, message: 'The run already names a different log of its own' },\n board_run_session_ref_invalid: { status: 400, message: 'Not an external session reference: give claude:<id> or codex:<id>' },\n board_run_session_ref_requires_user: { status: 403, message: 'Only a person can change or clear the log a run names' },\n board_schema_outdated: { status: 400, message: 'Board schema is outdated' },\n board_sprint_carry_invalid: { status: 400, message: 'Unfinished cards can only be carried to another open milestone of the same workspace' },\n board_sprint_close_requires_user: { status: 403, message: 'Only a person can close a sprint', code: 'issue_sprint_close_requires_user' },\n board_sprint_dates_invalid: { status: 400, message: 'A sprint cannot start after it is due' },\n issue_comment_delete_requires_user: { status: 403, message: 'Only a person can delete a comment the board wrote' },\n issue_comment_not_found: { status: 404, message: 'Comment not found' },\n issue_context_ref_invalid_index: { status: 400, message: 'Invalid context ref index' },\n issue_milestone_not_found: { status: 404, message: 'Milestone not found' },\n issue_not_found: { status: 404, message: 'Issue not found' },\n issue_parent_not_found: { status: 404, message: 'Parent issue not found' },\n issue_parent_self_reference: { status: 400, message: 'Issue cannot be its own parent' },\n issue_relation_not_found: { status: 404, message: 'Relation not found' },\n issue_relation_self_reference: { status: 400, message: 'An issue cannot be related to itself' },\n issue_status_name_empty: { status: 400, message: 'Status name must not be empty' },\n issue_status_not_found: { status: 404, message: 'Status not found' },\n issue_status_reference_conflict: { status: 400, message: 'Use either statusId or statusName, not both' },\n issue_workspace_not_found: { status: 404, message: 'Workspace not found' },\n}\n","import type { ZodError } from 'zod'\n\nimport { CliError, EXIT_SCHEMA_OUTDATED } from '../cli/output'\nimport { BoardError } from '../domain/errors'\nimport { BOARD_ERROR_RESPONSES } from './board-error-responses'\n\n/**\n * What a failed request answers with.\n *\n * The body is `{ code, message, details? }`, and a board failure goes through\n * `BOARD_ERROR_RESPONSES`, the one table every server of the `/issues` routes\n * answers with, so a client matching on a code or a status reads them alike.\n */\n\n/** The JSON body of every failure. */\nexport interface ServeErrorBody {\n code: string\n message: string\n details?: Record<string, unknown>\n}\n\n/** A failure the HTTP edge names itself — a request it cannot route, read or let in. */\nexport class ServeError extends Error {\n readonly status: number\n readonly code: string\n readonly details?: Record<string, unknown>\n\n constructor(status: number, code: string, message: string, details?: Record<string, unknown>) {\n super(message)\n this.status = status\n this.code = code\n this.details = details\n }\n}\n\n/** Where an invalid value was found. */\nexport type ValidationSource = 'body' | 'query' | 'params'\n\n/**\n * A request whose body, query or path did not have the shape the route takes:\n * 400 `validation_error`, with one `{ path, message }` per problem.\n */\nexport function validationError(source: ValidationSource, error: ZodError): ServeError {\n return new ServeError(400, 'validation_error', 'request validation failed', {\n source,\n issues: error.issues.map(issue => ({\n path: issue.path.length > 0 ? issue.path.map(String).join('.') : 'root',\n message: issue.message,\n })),\n })\n}\n\n/**\n * The status and body a failure answers with, or `null` for a failure nobody\n * named — a driver error, a bug — which the server answers as a 500 and says\n * nothing more about to the client.\n *\n * The schema guard is the command line's (`assertWritableBoard`), and it\n * refuses with a `CliError` a terminal prints; over HTTP it is the board's own\n * `board_schema_outdated`.\n */\nexport function readErrorResponse(error: unknown): { status: number, body: ServeErrorBody } | null {\n if (error instanceof ServeError) {\n return { status: error.status, body: withDetails({ code: error.code, message: error.message }, error.details) }\n }\n if (error instanceof CliError && error.exitCode === EXIT_SCHEMA_OUTDATED) {\n return readErrorResponse(new BoardError('board_schema_outdated'))\n }\n if (error instanceof BoardError) {\n const response = BOARD_ERROR_RESPONSES[error.code]\n return {\n status: response.status,\n body: withDetails({ code: response.code ?? error.code, message: response.message }, error.details),\n }\n }\n return null\n}\n\nfunction withDetails(body: ServeErrorBody, details: Record<string, unknown> | undefined): ServeErrorBody {\n return details === undefined ? body : { ...body, details }\n}\n","import type { z } from 'zod'\n\nimport type { RequestActors } from './actors'\nimport { validationError } from './errors'\n\n/**\n * A route table as small as the server needs: a method, a path with `:name`\n * segments, the zod shapes of its query and body, and a handler.\n *\n * Routes match in the order they are listed, so a path with a fixed segment —\n * `/issues/statuses` — is listed before the `/issues/:id` it would otherwise\n * fall into — the precedence most HTTP routers give static segments.\n */\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'\n\n/** What a handler is handed: the path parameters, the parsed query and body, and who the request writes as. */\ninterface RouteInput<TQuery, TBody> {\n params: Record<string, string>\n query: TQuery\n body: TBody\n actors: RequestActors\n}\n\n/** One route as it is declared. */\nexport interface RouteSpec<TQuery, TBody> {\n method: HttpMethod\n path: string\n query?: z.ZodType<TQuery>\n body?: z.ZodType<TBody>\n /**\n * Whether the route writes the board. A write asks the schema guard first, so\n * a board older than this build is never written to — the same guard every\n * `kanbo` command asks.\n */\n write?: boolean\n handle: (input: RouteInput<TQuery, TBody>) => Promise<unknown>\n}\n\n/** A route with its types erased, as the table holds it. */\nexport interface Route {\n method: HttpMethod\n segments: string[]\n write: boolean\n run: (input: { params: Record<string, string>, query: Record<string, string | string[]>, body: unknown, actors: RequestActors }) => Promise<unknown>\n}\n\n/** Declare a route; the query and body reach the handler parsed, or the request is a 400. */\nexport function route<TQuery = Record<string, never>, TBody = undefined>(spec: RouteSpec<TQuery, TBody>): Route {\n return {\n method: spec.method,\n segments: splitPath(spec.path),\n write: spec.write ?? false,\n run: async ({ params, query, body, actors }) => {\n const parsedQuery = spec.query ? spec.query.safeParse(query) : { success: true as const, data: query as TQuery }\n if (!parsedQuery.success) {\n throw validationError('query', parsedQuery.error)\n }\n // A body-less request to a route that takes one is read as `{}`: every\n // body with nothing required — approve, wait-approval — may be left out.\n const parsedBody = spec.body ? spec.body.safeParse(body ?? {}) : { success: true as const, data: undefined as TBody }\n if (!parsedBody.success) {\n throw validationError('body', parsedBody.error)\n }\n return await spec.handle({ params, query: parsedQuery.data, body: parsedBody.data, actors })\n },\n }\n}\n\n/** The route a request names and its path parameters, or `null` when no route does. */\nexport function matchRoute(routes: readonly Route[], method: string, path: string): { route: Route, params: Record<string, string> } | null {\n const segments = splitPath(path)\n for (const candidate of routes) {\n if (candidate.method !== method || candidate.segments.length !== segments.length) {\n continue\n }\n const params: Record<string, string> = {}\n const matches = candidate.segments.every((segment, index) => {\n if (segment.startsWith(':')) {\n params[segment.slice(1)] = segments[index]\n return segments[index].length > 0\n }\n return segment === segments[index]\n })\n if (matches) {\n return { route: candidate, params }\n }\n }\n return null\n}\n\n/**\n * A path as its segments, decoded, with a trailing slash ignored: `/issues`\n * and `/issues/` are the same list route. A segment that is not valid\n * percent-encoding is kept as it came, and names nothing on the board.\n */\nfunction splitPath(path: string): string[] {\n return path.split('/').filter(Boolean).map((segment) => {\n try {\n return decodeURIComponent(segment)\n }\n catch {\n return segment\n }\n })\n}\n","import { z } from 'zod'\n\n/**\n * The request shapes `kanbo serve` takes: the bodies and queries of the\n * board's `/issues` routes, in zod. An app that serves the same routes takes\n * the same shapes, so a client that builds a request for one builds it for the\n * other, and a field one does not take is not taken by the other either.\n */\n\nconst priority = z.enum(['none', 'low', 'medium', 'high', 'urgent'])\nconst category = z.enum(['triage', 'backlog', 'unstarted', 'started', 'completed', 'canceled'])\nconst executionMode = z.enum(['worktree', 'main'])\nconst milestoneStatus = z.enum(['open', 'closed'])\nconst relationType = z.enum(['blocks', 'duplicates', 'relates_to'])\nconst id = z.string().min(1)\n\n/**\n * The column's entry rules. Any string is taken, so an unknown rule is refused\n * by the board as `board_entry_rule_invalid` naming it, not by the schema.\n */\nconst entryRules = z.array(z.string()).nullable().optional()\n\n/** A list given as `a,b` or as the parameter repeated; `a=` is the empty list. */\nconst requiredCommaList = z.union([z.string(), z.array(z.string())])\n .transform(value => (Array.isArray(value) ? value : value.split(',')).map(item => item.trim()).filter(Boolean))\nconst commaList = requiredCommaList.optional()\n\n/** A number in a query string, where everything arrives as text. */\nconst queryNumber = z.string().regex(/^\\d+$/).transform(Number)\n\nexport const workspaceIdQuery = z.object({ workspaceId: id.optional() })\nexport const requiredWorkspaceIdQuery = z.object({ workspaceId: id })\nexport const workspaceIdsQuery = z.object({ workspaceIds: requiredCommaList })\nexport const searchQuery = z.object({ q: z.string(), limit: z.string().optional() })\nexport const readyQuery = z.object({ workspaceId: id, limit: queryNumber.optional() })\n\nexport const listIssuesQuery = z.object({\n workspaceId: id.optional(),\n workspaceIds: commaList,\n milestoneId: z.string().optional(),\n parentIssueId: z.string().optional(),\n priority: z.string().optional(),\n labels: commaList,\n statusId: z.string().optional(),\n})\n\nexport const createStatusBody = z.object({\n workspaceId: id,\n name: id,\n description: z.string().nullable().optional(),\n color: z.string().nullable().optional(),\n category: category.optional(),\n entryRules,\n})\n\nexport const updateStatusBody = z.object({\n name: id.optional(),\n description: z.string().nullable().optional(),\n color: z.string().nullable().optional(),\n entryRules,\n})\n\nexport const workspaceIdBody = z.object({ workspaceId: id })\n\nexport const reorderStatusesBody = z.object({ workspaceId: id, orderedIds: z.array(z.string()) })\n\nexport const createMilestoneBody = z.object({\n workspaceId: id,\n title: id,\n description: z.string().nullable().optional(),\n startDate: z.number().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n status: milestoneStatus.optional(),\n})\n\nexport const updateMilestoneBody = z.object({\n title: id.optional(),\n description: z.string().nullable().optional(),\n startDate: z.number().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n status: milestoneStatus.optional(),\n})\n\nexport const closeSprintBody = z.object({ carryTo: id.nullable().optional() })\n\nexport const createIssueBody = z.object({\n workspaceId: id,\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n parentIssueId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n statusName: id.nullable().optional(),\n dueDate: z.number().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n executionMode: executionMode.optional(),\n})\n\nexport const updateIssueBody = z.object({\n workspaceId: id.optional(),\n title: id.optional(),\n description: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n parentIssueId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n statusName: id.nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n order: z.number().optional(),\n executionMode: executionMode.optional(),\n})\n\nexport const reorderIssuesBody = z.object({\n workspaceId: id,\n orderedIds: z.array(z.string()),\n patch: z.object({\n issueIds: z.array(z.string()),\n fields: z.object({\n priority: priority.optional(),\n milestoneId: z.string().nullable().optional(),\n statusId: z.string().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n }),\n }).optional(),\n})\n\nexport const bulkUpdateBody = z.object({\n issueIds: z.array(z.string()),\n update: z.object({\n statusId: z.string().nullable().optional(),\n priority: priority.optional(),\n labels: z.array(z.string()).optional(),\n milestoneId: z.string().nullable().optional(),\n assigneeKind: z.string().nullable().optional(),\n assigneeId: z.string().nullable().optional(),\n dueDate: z.number().nullable().optional(),\n }),\n})\n\nexport const setStatusLineBody = z.object({ statusLine: z.string().nullable() })\nexport const waitApprovalBody = z.object({ statusLine: z.string().nullable().optional() })\nexport const approveBody = z.object({ comment: z.string().nullable().optional() })\nexport const returnBody = z.object({ comment: id, toStatusName: id.nullable().optional() })\n\nexport const startRunBody = z.object({\n agentName: id,\n host: z.string().nullable().optional(),\n branch: z.string().nullable().optional(),\n executionMode: executionMode.optional(),\n launchedByKind: z.enum(['user', 'agent', 'external']).optional(),\n externalSessionRef: z.string().nullable().optional(),\n})\n\nexport const finishRunBody = z.object({\n state: z.enum(['finished', 'failed', 'stopped']),\n branch: z.string().nullable().optional(),\n worktreePath: z.string().nullable().optional(),\n errorText: z.string().nullable().optional(),\n externalSessionRef: z.string().nullable().optional(),\n})\n\nexport const addCommentBody = z.object({ content: id })\nexport const createRelationBody = z.object({ sourceIssueId: id, targetIssueId: id, type: relationType })\nexport const addContextRefBody = z.object({ ref: id })\nexport const addPullRequestBody = z.object({ url: id })\n","import type { BoardRunProjection } from '../board-store'\nimport type { IssueCommentAuthorView } from '../domain/activity-types'\nimport type { EntryRule, UnmetEntryRule } from '../domain/entry-rules'\nimport { readEntryRules } from '../domain/entry-rules'\nimport { commentAuthorReference } from '../domain/issue-activity'\nimport type { IssueActorReference, IssueView } from '../domain/issue-view'\nimport { isOutsideWriter, toIssueView } from '../domain/issue-view'\nimport type { BoardWorkspaceIdentity } from '../domain/numbering'\nimport type { BoardOps } from '../ops'\nimport type { IssueActivityResolver } from '../ops/activity'\nimport type { BoardCardWrite } from '../ops/cards'\nimport type { Issue, IssueComment, IssueStatus } from '../sqlite/schema'\n\n/**\n * The board's rows in the shapes the `/issues` routes answer with.\n *\n * Every shape here is built out of the package's own projections —\n * `toIssueView` and the run projection for a card, `readEntryRules` for a\n * column, `commentAuthorReference` for who wrote a comment — so an app serving\n * the same routes builds the same shapes, and a client written against one\n * reads the other unchanged. Where such an app fills in something only it\n * knows (an agent's name and avatar, a workspace's name), this server says the\n * id it holds instead.\n */\n\n/**\n * A card as the routes answer it: its row, decoded, with the run facts merged\n * in — how many times it has been launched and the run going on right now —\n * and, after a write that put it in a column whose entry rules it does not\n * meet, what it is missing.\n */\nexport type ServeCardView = IssueView & Omit<BoardRunProjection, 'issueId'> & { unmetRules?: UnmetEntryRule[] }\n\n/** A column, its entry rules read out of the stored JSON into a list. */\nexport type ServeStatusView = Omit<IssueStatus, 'entryRules'> & { entryRules: EntryRule[] }\n\n/** A comment, with who wrote it as a reader sees them. */\nexport type ServeCommentView = IssueComment & { author: IssueCommentAuthorView }\n\n/** Cards with their run facts, read in one projection for all of them; a card with no runs is `0` and `null`. */\nexport async function toCardViews(ops: BoardOps, cards: Issue[]): Promise<ServeCardView[]> {\n const projections = new Map(\n (await ops.readBoardProjectionForIssues(cards.map(card => card.id))).map(projection => [projection.issueId, projection]),\n )\n return cards.map(card => ({\n ...toIssueView(card),\n attemptCount: projections.get(card.id)?.attemptCount ?? 0,\n activeRun: projections.get(card.id)?.activeRun ?? null,\n }))\n}\n\n/** One card, the same way. */\nexport async function toCardView(ops: BoardOps, card: Issue): Promise<ServeCardView> {\n return (await toCardViews(ops, [card]))[0]\n}\n\n/** A card a write just placed, carrying `unmetRules` only when the write reported some. */\nexport async function toWrittenCardView(ops: BoardOps, card: BoardCardWrite): Promise<ServeCardView> {\n const { unmetRules, ...row } = card\n const view = await toCardView(ops, row)\n return unmetRules ? { ...view, unmetRules } : view\n}\n\nexport function toStatusView(status: IssueStatus): ServeStatusView {\n return { ...status, entryRules: readEntryRules(status) }\n}\n\nexport function toCommentView(comment: IssueComment): ServeCommentView {\n return { ...comment, author: resolveActor(commentAuthorReference(comment)) }\n}\n\n/**\n * Who a reader sees beside a row, from what the row holds and nothing else.\n *\n * For the kinds that need nobody else's tables: an outside writer is shown\n * under the name it gave, the board writing for itself is `kanbo`, and a person\n * is `You`. An agent and a provider target are rows of whatever app launched\n * them, which this server cannot read, so they are shown by the id the board\n * holds for them.\n */\nfunction resolveActor(actor: IssueActorReference): IssueCommentAuthorView {\n if (actor.kind === 'system') {\n const provenance = { authorId: actor.id, sourceChatSessionId: actor.sourceChatSessionId }\n if (isOutsideWriter(provenance)) {\n return { kind: 'system', id: provenance.authorId, displayName: provenance.authorId, avatarUrl: null, label: 'External' }\n }\n return { kind: 'system', id: null, displayName: 'kanbo', avatarUrl: null, label: 'System' }\n }\n if (actor.kind === 'agent') {\n return { kind: 'agent', id: actor.id, displayName: actor.id ?? 'Unknown agent', avatarUrl: null, label: null }\n }\n if (actor.kind === 'provider-target') {\n return { kind: 'provider-target', id: actor.id, displayName: actor.id ?? 'Unknown provider', avatarUrl: null, label: null }\n }\n return { kind: 'user', id: actor.id ?? '__self__', displayName: 'You', avatarUrl: null, label: null }\n}\n\n/**\n * What an Activity feed is read through here: the one workspace this server\n * serves, named by its identity; actors as `resolveActor` reads them; and an\n * agent assignee by its id.\n */\nexport function createActivityResolver(workspace: BoardWorkspaceIdentity): IssueActivityResolver {\n return {\n workspaceById: new Map([[workspace.id, { id: workspace.id, name: workspace.name }]]),\n resolveActor: async actor => resolveActor(actor),\n resolveAgentName: async agentId => agentId,\n }\n}\n","import type { BoardSession } from '../cli/command'\nimport { BoardError } from '../domain/errors'\nimport { normalizeStatusName } from '../domain/status-name'\nimport { readChangeSeq, runBoardWrite } from '../ops/change-seq'\nimport type { BoardRunView } from '../ops/runs'\nimport type { Issue, IssueComment, IssueMilestone, IssueRelation, IssueRun, IssueStatus } from '../sqlite/schema'\nimport { ServeError } from './errors'\nimport type { Route } from './router'\nimport { route } from './router'\nimport {\n addCommentBody,\n addContextRefBody,\n addPullRequestBody,\n approveBody,\n bulkUpdateBody,\n closeSprintBody,\n createIssueBody,\n createMilestoneBody,\n createRelationBody,\n createStatusBody,\n finishRunBody,\n listIssuesQuery,\n readyQuery,\n reorderIssuesBody,\n reorderStatusesBody,\n requiredWorkspaceIdQuery,\n returnBody,\n searchQuery,\n setStatusLineBody,\n startRunBody,\n updateIssueBody,\n updateMilestoneBody,\n updateStatusBody,\n waitApprovalBody,\n workspaceIdBody,\n workspaceIdQuery,\n workspaceIdsQuery,\n} from './schemas'\nimport type { ServeCardView } from './views'\nimport {\n createActivityResolver,\n toCardView,\n toCardViews,\n toCommentView,\n toStatusView,\n toWrittenCardView,\n} from './views'\n\n/**\n * The board's `/issues` routes, for one board and one workspace.\n *\n * Every route the board operations can answer on their own. An app that serves\n * the same routes may hold every workspace of every board it can reach; this\n * server holds the one board and the one workspace it was started on. A\n * `workspaceId` naming any other is `issue_workspace_not_found`, and an id —\n * a card, a column, a milestone, a comment, a relation, a run — that belongs to\n * another workspace of the same database is not found either, exactly as if\n * the row were not there: one database may hold every project's board, and\n * a server started for one of them must not reach into the rest.\n */\n\nconst OK = { ok: true as const }\n\n/** How many cards a search answers with when the caller names no limit. */\nconst DEFAULT_SEARCH_LIMIT = 20\n\nexport function createIssueRoutes(session: BoardSession): Route[] {\n const { ops, store, workspace } = session\n const workspaceId = workspace.id\n const activityResolver = createActivityResolver(workspace)\n\n /** The served workspace, when that is the one the caller named — or named none. */\n function requireWorkspace(named: string | undefined): string {\n if (named !== undefined && named !== workspaceId) {\n throw new BoardError('issue_workspace_not_found', { workspaceId: named })\n }\n return workspaceId\n }\n\n async function requireCard(issueId: string): Promise<Issue> {\n const card = await store.issues.findInWorkspace(workspaceId, issueId)\n if (!card) {\n throw new BoardError('issue_not_found', { issueId })\n }\n return card\n }\n\n async function requireStatus(statusId: string): Promise<IssueStatus> {\n const status = await store.statuses.findInWorkspace(workspaceId, statusId)\n if (!status) {\n throw new BoardError('issue_status_not_found', { statusId })\n }\n return status\n }\n\n async function requireMilestone(milestoneId: string): Promise<IssueMilestone> {\n const milestone = await store.milestones.findInWorkspace(workspaceId, milestoneId)\n if (!milestone) {\n throw new BoardError('issue_milestone_not_found', { milestoneId })\n }\n return milestone\n }\n\n /** Whether a row hanging off a card — a comment, a relation, a run — hangs off one of this workspace's. */\n async function isOnThisBoard(issueId: string): Promise<boolean> {\n return (await store.issues.findInWorkspace(workspaceId, issueId)) !== null\n }\n\n async function requireComment(commentId: string): Promise<IssueComment> {\n const comment = await store.comments.findById(commentId)\n if (!comment || !await isOnThisBoard(comment.issueId)) {\n throw new BoardError('issue_comment_not_found', { commentId })\n }\n return comment\n }\n\n async function requireRelation(relationId: string): Promise<IssueRelation> {\n const relation = await store.relations.findById(relationId)\n if (!relation || !await isOnThisBoard(relation.sourceIssueId)) {\n throw new BoardError('issue_relation_not_found', { relationId })\n }\n return relation\n }\n\n async function requireRun(runId: string): Promise<IssueRun> {\n const run = await store.runs.findById(runId)\n if (!run || !await isOnThisBoard(run.issueId)) {\n throw new BoardError('board_run_not_found', { runId })\n }\n return run\n }\n\n /**\n * Seed the standard columns before answering about them, the first time\n * anyone looks. It is a write, so it asks the schema guard — but\n * only when there is something to seed: a board older than this build that\n * already has columns is still one worth reading.\n */\n async function seedColumns(): Promise<void> {\n if (await store.statuses.countByWorkspace(workspaceId) === 0) {\n await session.assertWritable()\n await ops.ensureDefaultColumns(workspaceId)\n }\n }\n\n async function listStatusViews() {\n await seedColumns()\n return (await ops.listColumns(workspaceId)).map(toStatusView)\n }\n\n /** A run with the attempt number it is, counting from the card's first launch. */\n async function toRunView(run: IssueRun): Promise<BoardRunView> {\n const runs = await ops.listRuns(run.issueId)\n return { ...run, attempt: runs.findIndex(candidate => candidate.id === run.id) + 1 }\n }\n\n return [\n // Columns\n route({\n method: 'GET',\n path: '/issues/statuses',\n query: workspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n return await listStatusViews()\n },\n }),\n route({\n method: 'POST',\n path: '/issues/statuses',\n body: createStatusBody,\n write: true,\n handle: async ({ body, actors }) => toStatusView(await ops.createColumn({\n workspaceId: requireWorkspace(body.workspaceId),\n name: body.name,\n description: body.description ?? null,\n color: body.color ?? null,\n category: body.category ?? 'unstarted',\n entryRules: body.entryRules ?? null,\n }, actors.person)),\n }),\n route({\n method: 'POST',\n path: '/issues/statuses/reorder',\n body: reorderStatusesBody,\n write: true,\n handle: async ({ body }) => {\n requireWorkspace(body.workspaceId)\n for (const statusId of body.orderedIds) {\n await requireStatus(statusId)\n }\n await ops.reorderColumns(workspaceId, body.orderedIds)\n return OK\n },\n }),\n route({\n method: 'POST',\n path: '/issues/statuses/standard',\n body: workspaceIdBody,\n write: true,\n handle: async ({ body }) => (await ops.addStandardColumns(requireWorkspace(body.workspaceId))).statuses.map(toStatusView),\n }),\n route({\n method: 'PATCH',\n path: '/issues/statuses/:id',\n body: updateStatusBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireStatus(params.id)\n return toStatusView(await ops.updateColumn(params.id, body, actors.person))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/statuses/:id',\n write: true,\n handle: async ({ params }) => {\n await requireStatus(params.id)\n await ops.deleteColumn(params.id)\n return OK\n },\n }),\n\n // Milestones, and milestones read as sprints\n route({\n method: 'GET',\n path: '/issues/milestones',\n query: workspaceIdQuery,\n handle: async ({ query }) => await ops.listMilestones(requireWorkspace(query.workspaceId)),\n }),\n route({\n method: 'POST',\n path: '/issues/milestones',\n body: createMilestoneBody,\n write: true,\n handle: async ({ body }) => await ops.createMilestone({\n workspaceId: requireWorkspace(body.workspaceId),\n title: body.title,\n description: body.description ?? null,\n startDate: body.startDate ?? null,\n dueDate: body.dueDate ?? null,\n status: body.status ?? 'open',\n }),\n }),\n route({\n method: 'PATCH',\n path: '/issues/milestones/:id',\n body: updateMilestoneBody,\n write: true,\n handle: async ({ params, body }) => {\n await requireMilestone(params.id)\n return await ops.updateMilestone(params.id, body)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/milestones/:id',\n write: true,\n handle: async ({ params }) => {\n await requireMilestone(params.id)\n await ops.deleteMilestone(params.id)\n return OK\n },\n }),\n route({\n method: 'GET',\n path: '/issues/sprints',\n query: requiredWorkspaceIdQuery,\n handle: async ({ query }) => await ops.listSprints(requireWorkspace(query.workspaceId)),\n }),\n route({\n method: 'POST',\n path: '/issues/milestones/:id/close',\n body: closeSprintBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireMilestone(params.id)\n return await ops.closeSprint(params.id, body, actors.person)\n },\n }),\n\n // The board as a whole\n route({\n method: 'GET',\n path: '/issues/columns',\n query: workspaceIdsQuery,\n handle: async ({ query }) => {\n // Naming no workspace is a board of no columns.\n if (query.workspaceIds.length === 0) {\n return []\n }\n for (const named of query.workspaceIds) {\n requireWorkspace(named)\n }\n return (await listStatusViews()).map(status => ({\n name: status.name,\n normalizedName: normalizeStatusName(status.name),\n category: status.category,\n color: status.color,\n statuses: [{ workspaceId, statusId: status.id, entryRules: status.entryRules }],\n }))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/search',\n query: searchQuery,\n handle: async ({ query }) => {\n const limit = Number(query.limit) || DEFAULT_SEARCH_LIMIT\n const found = (await ops.searchCards(query.q, Number.POSITIVE_INFINITY))\n .filter(card => card.workspaceId === workspaceId)\n .slice(0, limit)\n return await toCardViews(ops, found)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/version',\n query: workspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n return { seq: await readChangeSeq(store) }\n },\n }),\n route({\n method: 'GET',\n path: '/issues/ready',\n query: readyQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n await seedColumns()\n return await toCardViews(ops, await ops.listReady({ workspaceId, limit: query.limit }))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/prime',\n query: requiredWorkspaceIdQuery,\n handle: async ({ query }) => {\n requireWorkspace(query.workspaceId)\n await seedColumns()\n return { text: await ops.buildPrimeText(workspaceId) }\n },\n }),\n\n // Cards\n route({\n method: 'GET',\n path: '/issues',\n query: listIssuesQuery,\n handle: async ({ query }) => {\n if (query.workspaceId !== undefined && query.workspaceIds !== undefined) {\n throw new ServeError(400, 'issue_workspace_filter_conflict', 'Use either workspaceId or workspaceIds, not both', {\n workspaceId: query.workspaceId,\n workspaceIds: query.workspaceIds,\n })\n }\n requireWorkspace(query.workspaceId)\n for (const named of query.workspaceIds ?? []) {\n requireWorkspace(named)\n }\n const cards = (await store.issues.listInBoardOrder(workspaceId))\n .filter(card => query.milestoneId === undefined || card.milestoneId === query.milestoneId)\n .filter(card => query.parentIssueId === undefined || card.parentIssueId === query.parentIssueId)\n .filter(card => query.priority === undefined || card.priority === query.priority)\n .filter(card => query.statusId === undefined || card.statusId === query.statusId)\n const views = await toCardViews(ops, cards)\n const labels = query.labels ?? []\n return views.filter(view => labels.every(label => view.labels.includes(label)))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/reorder',\n body: reorderIssuesBody,\n write: true,\n handle: async ({ body, actors }) => {\n requireWorkspace(body.workspaceId)\n await ops.reorderCards(workspaceId, body.orderedIds)\n // Field changes go through the card update, so history and entry\n // rules are exactly an inline edit's.\n const updated: ServeCardView[] = []\n if (body.patch) {\n for (const issueId of new Set(body.patch.issueIds)) {\n await requireCard(issueId)\n updated.push(await toWrittenCardView(ops, await ops.updateCard(issueId, body.patch.fields, actors.placement)))\n }\n }\n return updated\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id',\n handle: async ({ params }) => await toCardView(ops, await requireCard(params.id)),\n }),\n route({\n method: 'POST',\n path: '/issues',\n body: createIssueBody,\n write: true,\n handle: async ({ body, actors }) => {\n const { workspaceId: named, ...fields } = body\n requireWorkspace(named)\n return await toWrittenCardView(ops, await ops.createCard({ ...fields, workspace }, actors.placement))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/bulk',\n body: bulkUpdateBody,\n write: true,\n handle: async ({ body, actors }) => {\n // A card of another workspace is skipped, the way the board skips an\n // id it does not hold at all.\n const onThisBoard: string[] = []\n for (const issueId of new Set(body.issueIds)) {\n if (await isOnThisBoard(issueId)) {\n onThisBoard.push(issueId)\n }\n }\n return await ops.bulkUpdateCards(onThisBoard, body.update, actors.placement)\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/runs/:runId',\n body: finishRunBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireRun(params.runId)\n // What the run turned out to be running in and how it ended are one\n // write.\n const run = await runBoardWrite(store, async (scope) => {\n if (body.branch !== undefined || body.worktreePath !== undefined || body.externalSessionRef !== undefined) {\n await ops.attachRunExecution(params.runId, body, scope)\n }\n return await ops.finishRun(params.runId, body, actors.writer, scope)\n })\n return await toRunView(run)\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id/status/:statusName',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n return await toWrittenCardView(ops, await ops.moveCard(params.id, params.statusName, actors.placement))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id/status-line',\n body: setStatusLineBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.setStatusLine(params.id, body.statusLine, actors.writer))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/wait-approval',\n body: waitApprovalBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.waitApproval(params.id, body, actors.writer))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/approve',\n body: approveBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.approve(params.id, body, actors.person))\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/return',\n body: returnBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, (await ops.returnCard(params.id, body, actors.person)).card)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/runs',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listRuns(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/runs',\n body: startRunBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toRunView(await ops.startRun(params.id, body, actors.writer))\n },\n }),\n route({\n method: 'PATCH',\n path: '/issues/:id',\n body: updateIssueBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n if (body.workspaceId !== undefined) {\n requireWorkspace(body.workspaceId)\n }\n return await toWrittenCardView(ops, await ops.updateCard(params.id, body, actors.placement))\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/activity',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listActivity(params.id, activityResolver)\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/field-changes',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listFieldChanges(params.id)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n // No board tool or command deletes a card: a card that could be\n // deleted and written again is a way around waiting for a person, and\n // the agent role holds no `delete` on `issues` for the same reason. So\n // this route is a person's, like approval — the one refusal the board\n // does not make itself, because nothing else in the package deletes.\n if (actors.person.kind !== 'user') {\n throw new ServeError(403, 'issue_delete_requires_user', 'Only a person can delete a card', { issueId: params.id })\n }\n await ops.deleteCard(params.id)\n return OK\n },\n }),\n\n // Comments\n route({\n method: 'GET',\n path: '/issues/:id/comments',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return (await ops.listComments(params.id)).map(toCommentView)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/comments',\n body: addCommentBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return toCommentView(await ops.addComment({ issueId: params.id, content: body.content }, actors.writer))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/comments/:id',\n write: true,\n handle: async ({ params, actors }) => {\n await requireComment(params.id)\n await ops.deleteComment(params.id, actors.person)\n return OK\n },\n }),\n\n // What a card points at\n route({\n method: 'GET',\n path: '/issues/:id/relations',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listRelations(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/relations',\n body: createRelationBody,\n write: true,\n handle: async ({ body }) => {\n await requireCard(body.sourceIssueId)\n await requireCard(body.targetIssueId)\n return await ops.createRelation(body)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/relations/:id',\n write: true,\n handle: async ({ params }) => {\n await requireRelation(params.id)\n await ops.deleteRelation(params.id)\n return OK\n },\n }),\n route({\n method: 'GET',\n path: '/issues/:id/pull-requests',\n handle: async ({ params }) => {\n await requireCard(params.id)\n return await ops.listPullRequestStandings(params.id)\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/pull-requests',\n body: addPullRequestBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await ops.linkPullRequest(params.id, body.url, actors.writer)\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id/pull-requests/:linkId',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n await ops.unlinkPullRequest(params.id, params.linkId, actors.person)\n return OK\n },\n }),\n route({\n method: 'POST',\n path: '/issues/:id/context-refs',\n body: addContextRefBody,\n write: true,\n handle: async ({ params, body, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.addContextRef(params.id, body.ref, actors.writer))\n },\n }),\n route({\n method: 'DELETE',\n path: '/issues/:id/context-refs/:index',\n write: true,\n handle: async ({ params, actors }) => {\n await requireCard(params.id)\n return await toCardView(ops, await ops.removeContextRef(params.id, Number(params.index), actors.writer))\n },\n }),\n ]\n}\n","import { createHash, timingSafeEqual } from 'node:crypto'\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport { createServer } from 'node:http'\nimport type { AddressInfo } from 'node:net'\n\nimport type { BoardSession } from '../cli/command'\nimport { describeFailure } from '../cli/failure'\nimport { CliError } from '../cli/output'\nimport type { ServeActors } from './actors'\nimport { ACTOR_HEADER, resolveRequestActors } from './actors'\nimport { isBoardPagePath, readBoardPageAssets, resolveBoardPageDirectory, sendBoardPage } from './board-page'\nimport type { ServeErrorBody } from './errors'\nimport { readErrorResponse, ServeError } from './errors'\nimport type { Route } from './router'\nimport { matchRoute } from './router'\nimport { createIssueRoutes } from './routes'\n\n/**\n * `kanbo serve` — the board over HTTP, with nothing but `node:http` under it.\n *\n * What it answers is `./routes.ts`; what this module owns is the door: who may\n * come in, from which origin, with how much, and in what order. Requests are\n * handled one at a time. A SQLite board is one connection whose transactions\n * may await, and two requests interleaving on it would land in each other's\n * transaction — the reason a long-running host of the board lines its writes up\n * one at a time. One at a time is that line, and on a board this size it costs\n * nothing a person would notice.\n */\n\n/** The most a request body may carry. A card is a few kilobytes; a megabyte is a mistake or an attack. */\nexport const SERVE_BODY_LIMIT_BYTES = 1024 * 1024\n\n/** What `kanbo serve` listens on when nothing says otherwise. */\nexport const DEFAULT_SERVE_HOST = '127.0.0.1'\nexport const DEFAULT_SERVE_PORT = 4318\n\n/** What a caller is told when a board is offered beyond this machine with nothing guarding it. */\nexport const SERVE_TOKEN_REQUIRED_MESSAGE\n = 'Serving the board beyond this machine needs a token. Pass --token or set KANBO_SERVE_TOKEN, '\n + 'or bind to 127.0.0.1.'\n\nconst ALLOWED_METHODS = 'GET, POST, PATCH, DELETE, OPTIONS'\nconst ALLOWED_HEADERS = `authorization, content-type, ${ACTOR_HEADER}`\n/** How long a browser may keep a preflight answer, in seconds. */\nconst PREFLIGHT_MAX_AGE_SECONDS = 600\n\nexport interface KanboServeOptions {\n /** The open board and workspace every route is about. The caller closes it. */\n session: BoardSession\n actors: ServeActors\n host: string\n /** `0` asks the system for a free port. */\n port: number\n /** When set, every request must carry `Authorization: Bearer <token>`. Required off loopback. */\n token?: string | null\n /** The exact origins a browser may call from; none by default. */\n corsOrigins?: readonly string[]\n /** Where the built board page is; by default `dist/page/`, found from the running file. */\n pageDirectory?: string\n /** Where a request that failed for a reason nobody named is written down. Never a token or a connection string. */\n log?: (line: string) => void\n}\n\nexport interface RunningKanboServer {\n /** The address to call, e.g. `http://127.0.0.1:4318`. */\n url: string\n port: number\n /** Stop listening and drop every open connection. The board stays open; its owner closes it. */\n close: () => Promise<void>\n}\n\n/**\n * Whether an address only this machine can reach. Anything else — `0.0.0.0`\n * included, which is every interface — is beyond it.\n */\nfunction isLoopbackHost(host: string): boolean {\n const bare = stripBrackets(host).toLowerCase()\n return bare === 'localhost' || bare === '::1' || /^127(?:\\.\\d{1,3}){3}$/.test(bare)\n}\n\n/** Refuse to offer the board beyond this machine unguarded. Asked before the board is even opened. */\nexport function assertServeBinding(host: string, token: string | null | undefined): void {\n if (!isLoopbackHost(host) && !token) {\n throw new CliError(1, SERVE_TOKEN_REQUIRED_MESSAGE)\n }\n}\n\nexport async function startKanboServer(options: KanboServeOptions): Promise<RunningKanboServer> {\n assertServeBinding(options.host, options.token)\n const routes = createIssueRoutes(options.session)\n // `[::1]` is how an address is written in a URL; `listen` takes it bare.\n const listenHost = stripBrackets(options.host)\n let boundPort = 0\n const handle = createRequestHandler(routes, options, () => boundPort)\n const server = createServer((request, response) => void handle(request, response))\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(options.port, listenHost, () => {\n server.off('error', reject)\n resolve()\n })\n })\n\n boundPort = (server.address() as AddressInfo).port\n return {\n url: `http://${urlHost(listenHost)}:${boundPort}`,\n port: boundPort,\n close: async () => await closeServer(server),\n }\n}\n\n/**\n * Who may act for a person on this server, said once when it starts: only a\n * request presenting the token does, and a server started from an agent's\n * shell has nobody who may.\n */\nexport function describePersonRights(actors: ServeActors): string {\n if (!actors.person) {\n return 'no request acts for a person (started from an agent\\'s shell)'\n }\n return `a request with the token acts for ${actors.person.id}`\n}\n\nfunction stripBrackets(host: string): string {\n return host.trim().replace(/^\\[(.*)\\]$/, '$1')\n}\n\nfunction urlHost(host: string): string {\n return host.includes(':') ? `[${host}]` : host\n}\n\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close(error => (error ? reject(error) : resolve()))\n // A client holding a keep-alive connection would otherwise hold the\n // shutdown open until it let go.\n server.closeAllConnections()\n })\n}\n\n/** Methods that change nothing, and so need no JSON content type to prove a script sent them. */\nconst SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])\n\nfunction createRequestHandler(routes: readonly Route[], options: KanboServeOptions, boundPort: () => number) {\n const token = options.token || null\n const corsOrigins = new Set(options.corsOrigins ?? [])\n const log = options.log ?? ((line: string) => console.error(line))\n const loopback = isLoopbackHost(options.host)\n const page = readBoardPageAssets(options.pageDirectory ?? resolveBoardPageDirectory())\n /** The tail of the line requests wait in. It never rejects, so one failure cannot stop the rest. */\n let queue: Promise<unknown> = Promise.resolve()\n\n /**\n * The `Host` a loopback server answers to: its own loopback names with the\n * port it is on. A page on another site that rebinds its own name to\n * 127.0.0.1 still sends that name, and is turned away.\n */\n function allowedHosts(): Set<string> {\n const port = boundPort()\n return new Set([\n `127.0.0.1:${port}`,\n `localhost:${port}`,\n `[::1]:${port}`,\n `${urlHost(stripBrackets(options.host)).toLowerCase()}:${port}`,\n ])\n }\n\n /**\n * The origins of this server's own page: `http://` and each name it answers\n * to. A browser names it on every write the page sends, and it is let in the\n * way a `--cors-origin` is — but with no CORS header needed, being the same\n * origin. Bound to `0.0.0.0` the server has no one name to be called by, so\n * a page opened by some address of it is named with `--cors-origin`.\n */\n function isOwnOrigin(origin: string): boolean {\n return [...allowedHosts()].some(host => origin.toLowerCase() === `http://${host}`)\n }\n\n return async (request: IncomingMessage, response: ServerResponse): Promise<void> => {\n const origin = request.headers.origin\n try {\n if (loopback && !allowedHosts().has((request.headers.host ?? '').toLowerCase())) {\n throw new ServeError(403, 'host_not_allowed', 'This server answers only to its own loopback address')\n }\n // A browser names the page a request comes from. A page that is not on\n // the list is refused whatever it asks, before anything is routed — a\n // \"simple\" request needs no preflight, and would otherwise be carried out\n // with only its answer hidden from the page.\n if (origin !== undefined && !corsOrigins.has(origin) && !isOwnOrigin(origin)) {\n throw new ServeError(403, 'origin_not_allowed', 'Requests from this origin are not allowed')\n }\n if (origin !== undefined && corsOrigins.has(origin)) {\n response.setHeader('access-control-allow-origin', origin)\n response.setHeader('vary', 'Origin')\n }\n\n // A preflight carries no credentials — the browser asks before it sends\n // them — so it is answered before the token is asked for, and says\n // nothing about the board.\n if (request.method === 'OPTIONS') {\n if (origin !== undefined && corsOrigins.has(origin)) {\n response.setHeader('access-control-allow-methods', ALLOWED_METHODS)\n response.setHeader('access-control-allow-headers', ALLOWED_HEADERS)\n response.setHeader('access-control-max-age', String(PREFLIGHT_MAX_AGE_SECONDS))\n }\n response.writeHead(204).end()\n return\n }\n\n // The page holds no data — it asks the routes below for everything, with\n // the token the person gives it — so it is served without the token.\n const url = new URL(request.url ?? '/', 'http://kanbo.invalid')\n if ((request.method === 'GET' || request.method === 'HEAD') && isBoardPagePath(url.pathname)) {\n sendBoardPage(response, url.pathname, page)\n return\n }\n\n if (token && !carriesToken(request, token)) {\n response.setHeader('www-authenticate', 'Bearer')\n throw new ServeError(401, 'unauthorized', 'A valid bearer token is required')\n }\n // A form or a `text/plain` fetch reaches a server without a preflight;\n // a JSON content type does not. Every write therefore has to say it.\n if (!SAFE_METHODS.has(request.method ?? 'GET') && !isJsonContentType(request.headers['content-type'])) {\n throw new ServeError(415, 'unsupported_media_type', 'Send the request as content-type: application/json')\n }\n const matched = matchRoute(routes, request.method ?? 'GET', url.pathname)\n if (!matched) {\n throw new ServeError(404, 'not_found', 'Not Found')\n }\n const body = await readJsonBody(request)\n // Acting for a person takes the token: without one configured, nobody\n // who can reach the port is known to be the person who started it.\n const actors = resolveRequestActors(options.actors, {\n actorHeader: request.headers[ACTOR_HEADER],\n authenticated: token !== null,\n })\n const result = queue.then(async () => {\n if (matched.route.write) {\n await options.session.assertWritable()\n }\n return await matched.route.run({ params: matched.params, query: readQuery(url.searchParams), body, actors })\n })\n queue = result.catch(() => undefined)\n sendJson(response, 200, await result)\n }\n catch (error) {\n const failure = readErrorResponse(error)\n if (failure) {\n if (failure.status === 413) {\n // The rest of the body is not read: the connection goes with the answer.\n response.setHeader('connection', 'close')\n response.once('finish', () => request.destroy())\n }\n else if (!request.complete) {\n // A body nobody read is not worth keeping the connection for.\n response.setHeader('connection', 'close')\n }\n sendJson(response, failure.status, failure.body)\n return\n }\n log(`kanbo serve: ${request.method} ${request.url?.split('?')[0]} failed: ${describeFailure(error).message}`)\n sendJson(response, 500, { code: 'internal_server_error', message: 'Internal Server Error' } satisfies ServeErrorBody)\n }\n }\n}\n\nfunction isJsonContentType(value: string | undefined): boolean {\n return value?.split(';')[0].trim().toLowerCase() === 'application/json'\n}\n\n/**\n * Whether the request carries the token, compared in constant time. Both sides\n * are hashed first, so the comparison is between two values of one length and\n * says nothing about how long the token is.\n */\nfunction carriesToken(request: IncomingMessage, token: string): boolean {\n const match = /^Bearer\\s+(.+)$/i.exec(request.headers.authorization ?? '')\n if (!match) {\n return false\n }\n const digest = (value: string): Buffer => createHash('sha256').update(value).digest()\n return timingSafeEqual(digest(match[1].trim()), digest(token))\n}\n\nfunction payloadTooLarge(): ServeError {\n return new ServeError(413, 'payload_too_large', `Request body is larger than ${SERVE_BODY_LIMIT_BYTES} bytes`, {\n limit: SERVE_BODY_LIMIT_BYTES,\n })\n}\n\n/**\n * The request body as JSON, `undefined` when there is none.\n *\n * A body that says it is over the limit is refused before a byte of it is\n * read, and one that turns out to be is refused the moment it passes the\n * limit; either way the rest is never read (the caller closes the connection).\n */\nasync function readJsonBody(request: IncomingMessage): Promise<unknown> {\n if (Number(request.headers['content-length'] ?? 0) > SERVE_BODY_LIMIT_BYTES) {\n throw payloadTooLarge()\n }\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of request as AsyncIterable<Buffer>) {\n size += chunk.length\n if (size > SERVE_BODY_LIMIT_BYTES) {\n throw payloadTooLarge()\n }\n chunks.push(chunk)\n }\n\n const text = Buffer.concat(chunks).toString('utf8')\n if (text.trim() === '') {\n return undefined\n }\n try {\n return JSON.parse(text)\n }\n catch {\n throw new ServeError(400, 'validation_error', 'request validation failed', {\n source: 'body',\n issues: [{ path: 'root', message: 'Body is not valid JSON' }],\n })\n }\n}\n\n/** The query string as the schemas read it: a repeated parameter is a list, a single one a string. */\nfunction readQuery(params: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {}\n for (const key of new Set(params.keys())) {\n const values = params.getAll(key)\n query[key] = values.length === 1 ? values[0] : values\n }\n return query\n}\n\nfunction sendJson(response: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value)\n response.writeHead(status, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body),\n })\n response.end(body)\n}\n","import { spawn } from 'node:child_process'\nimport { randomBytes } from 'node:crypto'\n\nimport { assertServeBinding } from './server'\n\n/**\n * The token `kanbo serve` guards the board with, and how the person who\n * started it is handed the page.\n *\n * A token nobody gave is made up for the run, so a loopback server is never\n * open to anything that can reach the port. The person gets it as a link whose\n * fragment carries it — a fragment never reaches a server or its log — and the\n * page keeps it for the tab. Only a person's terminal is handed that link: a\n * shell an agent runs in, or output going anywhere but a terminal, is told a\n * token exists and never what it is, since whoever holds it acts for the person.\n */\n\n/** The token a server runs with, and whether it was made up for this run rather than given. */\nexport interface ServeAccess {\n token: string\n generated: boolean\n}\n\n/** The token given, or one made up for a loopback server; refused off loopback without one. */\nexport function resolveServeAccess(host: string, givenToken: string | null): ServeAccess {\n assertServeBinding(host, givenToken)\n if (givenToken) {\n return { token: givenToken, generated: false }\n }\n return { token: randomBytes(24).toString('base64url'), generated: true }\n}\n\nexport interface ServePagePresentation {\n /** The server's address, e.g. `http://127.0.0.1:4318`. */\n url: string\n access: ServeAccess\n /** Started from a shell that says it is an agent's (`KANBO_ACTOR_KIND=agent`). */\n agentShell: boolean\n /** Whether what is printed goes to a terminal rather than a pipe or a file. */\n terminal: boolean\n /** The person asked for the page to open (no `--no-open`). */\n open: boolean\n}\n\nexport interface ServePageOutput {\n print: (line: string) => void\n openInBrowser: (url: string) => void\n}\n\n/**\n * Hand the person the board page: a link printed once, and opened in the\n * browser unless they said not to. A generated token rides in the link's\n * fragment; one they gave is theirs already and is left out.\n */\nexport function presentServePage(presentation: ServePagePresentation, output: ServePageOutput): void {\n const { url, access, open } = presentation\n if (presentation.agentShell || !presentation.terminal) {\n if (access.generated) {\n output.print('kanbo serve: a token was generated for this run and is not shown here; set KANBO_SERVE_TOKEN to choose your own')\n }\n return\n }\n const link = access.generated ? `${url}/#token=${access.token}` : `${url}/`\n output.print(`kanbo serve: board page ${link}`)\n if (open) {\n output.openInBrowser(link)\n }\n}\n\n/** Open a link in the default browser, without waiting for it and without failing when there is none. */\nexport function openInBrowser(url: string): void {\n const [command, args] = process.platform === 'darwin'\n ? ['open', [url]]\n : process.platform === 'win32'\n ? ['cmd', ['/c', 'start', '\"\"', url]]\n : ['xdg-open', [url]]\n try {\n const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsVerbatimArguments: process.platform === 'win32' })\n child.on('error', () => {})\n child.unref()\n }\n catch {\n // No browser to open: the link printed above is enough.\n }\n}\n","import type { Command } from 'commander'\n\nimport { openInBrowser, presentServePage, resolveServeAccess } from '../../serve/access'\nimport type { ServeActors } from '../../serve/actors'\nimport {\n DEFAULT_SERVE_HOST,\n DEFAULT_SERVE_PORT,\n describePersonRights,\n startKanboServer,\n} from '../../serve/server'\nimport { createCliActor, createPersonActor, isAgentShell } from '../actor'\nimport { openBoardSession } from '../command'\nimport { describeFailure } from '../failure'\nimport { CliError } from '../output'\n\n/**\n * `kanbo serve` — the board over HTTP: the board page for a browser, and the\n * board's `/issues` routes for any other client.\n *\n * The routes serve the board and workspace this command resolves the way every\n * other command does, so a client written against them shows and edits this\n * board unchanged, whichever server answers them. Everything\n * about the door — the token, the origins, the body limit, who a request writes\n * as — is `../../serve/`.\n */\n\n/** What `kanbo serve` is told. */\ninterface ServeCommandOptions {\n port: string\n host: string\n db?: string\n databaseUrl?: string\n workspace?: string\n token?: string\n corsOrigin: string[]\n /** `false` under `--no-open`. */\n open: boolean\n}\n\nexport function registerServeCommand(program: Command): void {\n program\n .command('serve')\n .description('serve this board over HTTP, with a board page for the browser')\n .option('--port <port>', 'port to listen on', String(DEFAULT_SERVE_PORT))\n .option('--host <host>', 'address to bind; anything but loopback needs a token', DEFAULT_SERVE_HOST)\n .option('--db <path>', 'board database file to open')\n .option('--database-url <url>', 'external Postgres board to serve instead of a board file')\n .option('--workspace <nameOrId>', 'workspace the server is about')\n .option('--token <token>', 'bearer token every request must carry (or set KANBO_SERVE_TOKEN); made up for the run on loopback when absent')\n .option('--cors-origin <origin>', 'let a browser call from this exact origin; repeat for more', collect, [])\n .option('--no-open', 'print the board page link without opening it in the browser')\n .action(async (options: ServeCommandOptions) => {\n const port = parsePort(options.port)\n // Refused before the board is opened: a server that would not be allowed\n // to start has no business connecting to anything first.\n const access = resolveServeAccess(options.host, options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null)\n\n const session = await openBoardSession(\n { db: options.db, databaseUrl: options.databaseUrl, workspace: options.workspace },\n 'read',\n )\n const agentShell = isAgentShell()\n const actors: ServeActors = { writer: createCliActor(), person: agentShell ? null : createPersonActor() }\n\n let server: Awaited<ReturnType<typeof startKanboServer>>\n try {\n server = await startKanboServer({\n session,\n actors,\n host: options.host,\n port,\n token: access.token,\n corsOrigins: options.corsOrigin,\n })\n }\n catch (error) {\n await session.close()\n throw error\n }\n\n console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}, bearer token required${access.generated ? ' (generated for this run)' : ''}`)\n console.error(`kanbo serve: ${describePersonRights(actors)}`)\n presentServePage(\n { url: server.url, access, agentShell, terminal: process.stdout.isTTY === true, open: options.open },\n { print: line => console.log(line), openInBrowser },\n )\n\n let stopping = false\n const stop = async (): Promise<void> => {\n if (stopping) {\n return\n }\n stopping = true\n try {\n await server.close()\n await session.close()\n process.exit(0)\n }\n catch (error) {\n console.error(describeFailure(error).message)\n process.exit(1)\n }\n }\n process.once('SIGINT', () => void stop())\n process.once('SIGTERM', () => void stop())\n })\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value]\n}\n\n/** A port a caller typed, refused rather than guessed at when it is not one. */\nfunction parsePort(value: string): number {\n const port = Number(value)\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new CliError(1, `Expected a port between 0 and 65535, got \"${value}\".`)\n }\n return port\n}\n","import type { Command } from 'commander'\n\nimport { BoardError } from '../../domain/errors'\nimport { requireHumanActor } from '../actor'\nimport type { BoardCommandOptions } from '../command'\nimport { runBoardCommand, withBoardOptions } from '../command'\nimport { CliError } from '../output'\nimport { describeSprints, projectSprint, SPRINT_VIEW_FIELDS } from '../view'\n\ninterface CreateOptions extends BoardCommandOptions {\n title: string\n start: number\n due: number\n description?: string\n}\n\ninterface CloseOptions extends BoardCommandOptions {\n carryTo?: string\n}\n\nconst DAY_SECONDS = 86_400\n\n/**\n * A date as the command line takes it: `YYYY-MM-DD`, read as a UTC day, or a\n * unix-seconds number taken as it is. `end` turns a day into its last second,\n * so a sprint due on a day is still current throughout that day.\n */\nfunction parseDate(value: string, end: boolean): number {\n if (/^\\d+$/.test(value)) {\n return Number(value)\n }\n const day = /^\\d{4}-\\d{2}-\\d{2}$/.test(value) ? Date.parse(`${value}T00:00:00Z`) : Number.NaN\n if (Number.isNaN(day)) {\n throw new CliError(1, `Expected a date as YYYY-MM-DD or unix seconds, got \"${value}\".`)\n }\n return day / 1000 + (end ? DAY_SECONDS - 1 : 0)\n}\n\n/**\n * `kanbo sprint …` — milestones read as sprints (ruling 5x-3).\n *\n * Listing and creating are anybody's. Closing one is a person's decision\n * (ruling 5x-4) — what did not get done is a review of the work — so it is\n * refused in a shell started for an agent exactly like `approve`, before\n * the board is opened.\n */\nexport function registerSprintCommands(program: Command): void {\n const sprint = program\n .command('sprint')\n .description('milestones read as sprints: a start date, a due date, and the cards under them')\n\n withBoardOptions(sprint\n .command('list')\n .description('every milestone of this board by start date, the current sprint marked *'))\n .action(async (options: BoardCommandOptions) => {\n await runBoardCommand(options, 'read', async (session) => {\n const views = (await session.ops.listSprints(session.workspace.id)).map(projectSprint)\n return { value: views, text: describeSprints(views), fields: SPRINT_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(sprint\n .command('create')\n .description('create a sprint: a milestone with a start and a due date')\n .requiredOption('--title <title>', 'what the sprint is called')\n .requiredOption('--start <date>', 'first day, as YYYY-MM-DD (UTC) or unix seconds', value => parseDate(value, false))\n .requiredOption('--due <date>', 'last day, as YYYY-MM-DD (UTC, through its end) or unix seconds', value => parseDate(value, true))\n .option('--description <text>', 'what the sprint is for'))\n .action(async (options: CreateOptions) => {\n await runBoardCommand(options, 'write', async (session) => {\n const created = await session.ops.createMilestone({\n workspaceId: session.workspace.id,\n title: options.title,\n description: options.description ?? null,\n startDate: options.start,\n dueDate: options.due,\n })\n const current = await session.ops.currentSprint(session.workspace.id)\n const view = projectSprint({ ...created, cards: { open: 0, done: 0 }, current: current?.id === created.id })\n return { value: view, text: `Created ${view.id}\\n\\n${describeSprints([view])}`, fields: SPRINT_VIEW_FIELDS }\n })\n })\n\n withBoardOptions(sprint\n .command('close')\n .description('close a sprint, carrying its unfinished cards to another open milestone (a person\\'s decision)')\n .argument('<id>', 'the milestone to close')\n .option('--carry-to <id>', 'the open milestone unfinished cards move to; without it they leave the milestone'))\n .action(async (id: string, options: CloseOptions) => {\n const actor = requireHumanActor('sprint close')\n\n await runBoardCommand(options, 'write', async (session) => {\n // A board can hold several workspaces: a sprint of another one is not\n // this workspace's to close, even by its id.\n if (!(await session.ops.listSprints(session.workspace.id)).some(sprint => sprint.id === id)) {\n throw new BoardError('issue_milestone_not_found', { milestoneId: id, workspaceId: session.workspace.id })\n }\n const result = await session.ops.closeSprint(id, { carryTo: options.carryTo ?? null }, actor)\n const destination = options.carryTo ? `to ${options.carryTo}` : 'out of the milestone'\n return {\n value: result,\n text: `Closed ${id}; ${result.carried} unfinished card${result.carried === 1 ? '' : 's'} carried ${destination}`,\n fields: ['carried', 'closed'],\n }\n })\n })\n}\n","import { existsSync, rmSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\nimport type { Command } from 'commander'\n\nimport { BINDING_FILE_PATH } from '../binding'\nimport { findKanboProject } from '../doctor'\nimport { CliError } from '../output'\nimport { confirmPlan, isInteractive } from '../setup/confirm'\nimport type { FileChange } from '../setup/file-change'\nimport { applyFileChange } from '../setup/file-change'\nimport { planInstructionBlockRemoval } from '../setup/instructions'\nimport {\n CLAUDE_USER_REMOVE,\n planCodexMcpServerRemoval,\n planJsonMcpServerRemoval,\n readClaudeUserMcpCommand,\n runClaudeMcp,\n} from '../setup/mcp-config'\nimport type { McpClient } from '../setup/paths'\nimport {\n GLOBAL_INSTRUCTION_CLIENTS,\n globalInstructionPath,\n globalMcpConfigPath,\n PROJECT_INSTRUCTION_FILES,\n projectMcpConfigPath,\n} from '../setup/paths'\n\n/**\n * `kanbo uninstall` — take out what `kanbo init` wrote, and nothing else.\n *\n * The marked instruction block and the `kanbo` MCP entries go; the person's\n * own writing around the block and every other MCP server stay, and so does\n * every file, emptied or not — kanbo does not record which files it created,\n * so it does not delete any. Claude Code's user-scope registration goes out\n * the way it went in, through `claude mcp remove`.\n *\n * Boards are never touched unless `--purge` is given: then the project's\n * binding goes, and its board file only after a confirmation that names it —\n * `--yes` does not answer that one, and a shell nobody can ask keeps the file.\n */\ninterface UninstallOptions {\n project?: boolean\n global?: boolean\n purge?: boolean\n yes?: boolean\n json?: boolean\n}\n\n/** One thing the command did, or left for the person. */\ninterface UninstallAction {\n path: string\n /** `edited`: kanbo's part taken out. `deleted`: the file removed. `ran`/`manual`: a `claude` command. `kept`: left on purpose. */\n action: 'edited' | 'deleted' | 'ran' | 'manual' | 'kept'\n note?: string\n}\n\n/** A planned edit, with what it takes out. */\ninterface PlannedEdit {\n change: FileChange\n what: string\n}\n\nexport function registerUninstallCommand(program: Command): void {\n program\n .command('uninstall')\n .description('remove what kanbo init wrote: instruction blocks and kanbo MCP entries; boards stay unless --purge')\n .option('--project', 'only this project\\'s files')\n .option('--global', 'only your own user-level files')\n .option('--purge', 'also remove this project\\'s .kanbo/binding.json, and its board file after a confirmation naming it')\n .option('--yes', 'do it without asking (never deletes a board file)')\n .option('--json', 'print what was done as JSON')\n .action(async (options: UninstallOptions) => {\n await uninstall(options)\n })\n}\n\nasync function uninstall(options: UninstallOptions): Promise<void> {\n const both = !options.project && !options.global\n const withProject = both || Boolean(options.project)\n const withGlobal = both || Boolean(options.global)\n if (options.purge && !withProject) {\n throw new CliError(1, '--purge removes a project\\'s binding and board; run it without --global, in the project.')\n }\n\n const projectRoot = withProject ? (findKanboProject(process.cwd())?.root ?? process.cwd()) : null\n const edits = [\n ...(projectRoot ? planProjectEdits(projectRoot) : []),\n ...(withGlobal ? planGlobalEdits() : []),\n ].filter(edit => edit.change.next !== null)\n const claudeUser = withGlobal && readClaudeUserMcpCommand() !== undefined\n const bindingPath = projectRoot && options.purge ? join(projectRoot, BINDING_FILE_PATH) : null\n const purgeBinding = bindingPath !== null && existsSync(bindingPath)\n const boardFile = projectRoot && options.purge ? findOwnBoardFile(projectRoot) : null\n\n const actions: UninstallAction[] = []\n if (edits.length > 0 || claudeUser || purgeBinding || boardFile) {\n const confirmed = await confirmPlan({\n preview: describePlan(edits, claudeUser, purgeBinding ? bindingPath : null, boardFile),\n question: 'Remove these?',\n yes: options.yes,\n machineOutput: Boolean(options.json),\n command: 'kanbo uninstall',\n })\n if (!confirmed) {\n throw new CliError(1, 'Nothing was changed.')\n }\n\n for (const edit of edits) {\n applyFileChange(edit.change)\n actions.push({ path: edit.change.path, action: 'edited', note: edit.what })\n }\n if (claudeUser) {\n const outcome = runClaudeMcp(CLAUDE_USER_REMOVE)\n actions.push({ path: globalMcpConfigPath('claude'), action: outcome.state, note: outcome.state === 'ran' ? outcome.command : `run this yourself: ${outcome.command} (${outcome.reason})` })\n }\n if (purgeBinding) {\n rmSync(bindingPath)\n actions.push({ path: bindingPath, action: 'deleted', note: 'project binding' })\n }\n if (boardFile) {\n actions.push(await purgeBoardFile(boardFile))\n }\n }\n\n printActions(actions, options)\n}\n\nfunction planProjectEdits(root: string): PlannedEdit[] {\n return [\n ...Object.values(PROJECT_INSTRUCTION_FILES).map(name => ({\n change: planInstructionBlockRemoval(join(root, name)),\n what: 'kanbo instruction block',\n })),\n ...(['claude', 'codex', 'cursor'] as const).map(client => ({\n change: planMcpRemoval(projectMcpConfigPath(root, client), client),\n what: 'kanbo MCP server',\n })),\n ]\n}\n\nfunction planGlobalEdits(): PlannedEdit[] {\n return [\n ...GLOBAL_INSTRUCTION_CLIENTS.map(client => ({\n change: planInstructionBlockRemoval(globalInstructionPath(client)),\n what: 'kanbo instruction block',\n })),\n // Claude Code's `~/.claude.json` is its own state file: `claude mcp remove` edits it, not this.\n ...(['codex', 'cursor'] as const).map(client => ({\n change: planMcpRemoval(globalMcpConfigPath(client), client),\n what: 'kanbo MCP server',\n })),\n ]\n}\n\nfunction planMcpRemoval(path: string, client: McpClient): FileChange {\n return client === 'codex' ? planCodexMcpServerRemoval(path) : planJsonMcpServerRemoval(path)\n}\n\n/**\n * The board file `kanbo init --file` made for this project — the only board\n * `--purge` may offer to delete. A host app's database or an external board is\n * not kanbo's to delete, and a binding that names none offers nothing.\n */\nfunction findOwnBoardFile(root: string): string | null {\n const binding = findKanboProject(root)?.binding\n if (!binding?.dbPath) {\n return null\n }\n const path = resolve(root, binding.dbPath)\n return existsSync(path) ? path : null\n}\n\n/** Delete the board file only on a yes to a question that names it; `--yes` is not that answer. */\nasync function purgeBoardFile(path: string): Promise<UninstallAction> {\n if (!isInteractive()) {\n return { path, action: 'kept', note: 'board file: deleting it needs a confirmation in a terminal; delete it yourself if you mean to' }\n }\n const prompts = await import('@clack/prompts')\n const answer = await prompts.confirm({\n message: `Delete the board file ${path} and every card in it? This cannot be undone.`,\n initialValue: false,\n })\n if (answer !== true) {\n return { path, action: 'kept', note: 'board file' }\n }\n for (const file of [path, `${path}-wal`, `${path}-shm`]) {\n rmSync(file, { force: true })\n }\n return { path, action: 'deleted', note: 'board file' }\n}\n\nfunction describePlan(edits: PlannedEdit[], claudeUser: boolean, bindingPath: string | null, boardFile: string | null): string {\n const lines = ['kanbo uninstall will:']\n for (const edit of edits) {\n lines.push(` edit ${edit.change.path} (remove the ${edit.what})`)\n }\n if (claudeUser) {\n lines.push(` run ${CLAUDE_USER_REMOVE.join(' ')}`)\n }\n if (bindingPath) {\n lines.push(` delete ${bindingPath}`)\n }\n if (boardFile) {\n lines.push(` ask whether to delete the board file ${boardFile} (only in a terminal; --yes does not answer this)`)\n }\n lines.push('Files stay even when nothing is left in them. Boards stay unless you confirm deleting one.')\n return lines.join('\\n')\n}\n\nfunction printActions(actions: UninstallAction[], options: UninstallOptions): void {\n if (options.json) {\n console.log(JSON.stringify({ actions }, null, 2))\n return\n }\n if (actions.length === 0) {\n console.log('Nothing to remove: no kanbo block or MCP entry in the files kanbo writes.')\n return\n }\n for (const action of actions) {\n console.log(`${action.action.padEnd(8)} ${action.path}${action.note ? ` (${action.note})` : ''}`)\n }\n}\n","import type { Command } from 'commander'\n\nimport { registerApproveCommand } from './approve'\nimport { registerCapabilitiesCommand } from './capabilities'\nimport { registerCardCommands } from './card'\nimport { registerColumnsCommands } from './columns'\nimport { registerDoctorCommand } from './doctor'\nimport { registerInitCommand } from './init'\nimport { registerMcpCommand } from './mcp'\nimport { registerMigrateCommand } from './migrate'\nimport { registerPrimeCommand } from './prime'\nimport { registerReadyCommand } from './ready'\nimport { registerReturnCommand } from './return'\nimport { registerRolesCommands } from './roles'\nimport { registerRunCommands } from './run'\nimport { registerServeCommand } from './serve'\nimport { registerSprintCommands } from './sprint'\nimport { registerUninstallCommand } from './uninstall'\n\n/**\n * Every command the `kanbo` binary answers to, put on one program.\n *\n * It is its own module rather than a block inside `cli/index.ts` because two\n * callers need it and only one of them wants the binary: `index.ts` runs\n * `main()` on import, so a test that imported it would parse `process.argv`.\n * The other caller is `mcp/docs.test.ts`, which walks the program to check that\n * every command has a row in the README — a check that is worth nothing if the\n * list it walks is a second copy of this one.\n */\nexport function registerKanboCommands(program: Command): void {\n registerInitCommand(program)\n registerDoctorCommand(program)\n registerUninstallCommand(program)\n registerCapabilitiesCommand(program)\n registerPrimeCommand(program)\n registerReadyCommand(program)\n registerColumnsCommands(program)\n registerCardCommands(program)\n registerApproveCommand(program)\n registerReturnCommand(program)\n registerRunCommands(program)\n registerSprintCommands(program)\n registerMigrateCommand(program)\n registerRolesCommands(program)\n registerMcpCommand(program)\n registerServeCommand(program)\n}\n","/**\n * The board's tools, named once.\n *\n * An agent reaches this board through an MCP server, through a command line, or\n * through an HTTP API (`kanbo serve`, or an app that serves the same routes),\n * and the three must agree on what the board can be asked to do. The names live\n * here rather than in the MCP server so that a prompt built for an agent and the\n * tools that agent actually holds cannot drift apart: both read this list.\n *\n * Nothing here executes anything. These are names and the commands that mean\n * the same thing, for a prompt and for documentation.\n */\nexport const KANBO_TOOL_NAMES = [\n 'kanbo_prime',\n 'kanbo_ready',\n 'kanbo_columns',\n 'kanbo_sprints',\n 'kanbo_card_get',\n 'kanbo_card_list',\n 'kanbo_card_create',\n 'kanbo_card_update',\n 'kanbo_card_move',\n 'kanbo_card_comment',\n 'kanbo_card_link_pr',\n 'kanbo_card_pull_requests',\n 'kanbo_status_line',\n 'kanbo_wait_approval',\n 'kanbo_run_start',\n 'kanbo_run_finish',\n] as const\n\n/** One of the board's tools. */\nexport type KanboToolName = typeof KANBO_TOOL_NAMES[number]\n\n/** The command line that does what a tool does. */\nexport interface KanboToolCliEquivalent {\n /** The `kanbo` command, which needs no server. */\n kanbo: string\n}\n\n/**\n * What to type instead of calling the tool.\n *\n * An agent without MCP tools is not shut out of the board: every tool has a\n * command that does the same thing, and the card ends up saying the same\n * either way, because both call the same operations.\n */\nexport const KANBO_TOOL_CLI_EQUIVALENTS: Record<KanboToolName, KanboToolCliEquivalent> = {\n kanbo_prime: {\n kanbo: 'kanbo prime',\n },\n kanbo_ready: {\n kanbo: 'kanbo ready',\n },\n kanbo_columns: {\n kanbo: 'kanbo columns list',\n },\n kanbo_sprints: {\n kanbo: 'kanbo sprint list',\n },\n kanbo_card_get: {\n kanbo: 'kanbo card get <id>',\n },\n kanbo_card_list: {\n kanbo: 'kanbo card list',\n },\n kanbo_card_create: {\n kanbo: 'kanbo card create --description <text> [--title <title>] [--parent <id>]',\n },\n kanbo_card_update: {\n kanbo: 'kanbo card update <id> [--title <title>] [--description <text>]',\n },\n kanbo_card_move: {\n kanbo: 'kanbo card move <id> <column>',\n },\n kanbo_card_comment: {\n kanbo: 'kanbo card comment <id> --content <text>',\n },\n kanbo_card_link_pr: {\n kanbo: 'kanbo card pr add <id> <url>',\n },\n kanbo_card_pull_requests: {\n kanbo: 'kanbo card pr list <id>',\n },\n kanbo_status_line: {\n kanbo: 'kanbo card status-line <id> --text <text>',\n },\n kanbo_wait_approval: {\n kanbo: 'kanbo card wait-approval <id> [--text <text>]',\n },\n kanbo_run_start: {\n kanbo: 'kanbo run start <id> --agent <name> [--session <ref>]',\n },\n kanbo_run_finish: {\n kanbo: 'kanbo run finish <runId> --state <state>',\n },\n}\n","import { Command } from 'commander'\nimport { z } from 'zod'\n\nimport { HUMAN_ONLY_ACTIONS } from '../cli/actor'\nimport { registerKanboCommands } from '../cli/commands'\nimport { CLI_EXIT_CODES } from '../cli/output'\nimport type { EntryRule } from '../domain/entry-rules'\nimport { ENTRY_RULE_DESCRIPTIONS, ENTRY_RULES } from '../domain/entry-rules'\nimport type { KanboToolCliEquivalent } from '../mcp/tool-names'\nimport { KANBO_TOOL_CLI_EQUIVALENTS } from '../mcp/tool-names'\nimport { KANBO_TOOLS } from '../mcp/tools'\nimport { BOARD_RULES } from '../ops/agent-rules'\nimport { APPROVED_COMMENT } from '../ops/approval'\nimport { RETURNED_STATUS_LINE_PREFIX } from '../ops/cards'\nimport {\n LAUNCHED_STATUS_LINE_PREFIX,\n NO_REPORT_STATUS_LINE,\n RUN_FAILED_COMMENT,\n RUN_FAILED_COMMENT_PREFIX,\n RUN_FINISHED_COMMENT,\n RUN_STARTED_COMMENT_PREFIX,\n RUN_STOPPED_BY_YOU_COMMENT,\n RUN_STOPPED_COMMENT,\n RUN_STOPPED_REASON_PREFIX,\n STOPPED_BY_YOU_STATUS_LINE,\n} from '../ops/runs'\nimport { KANBO_PACKAGE_VERSION } from '../package-version'\nimport {\n AGENT_APPROVAL_COMMENT_REFUSAL,\n AGENT_WAITING_REFUSAL,\n BOARD_AGENT_ROLE,\n BOARD_PERSON_ROLE,\n} from '../postgres/roles.sql'\nimport { BOARD_SCHEMA_EPOCH } from '../schema-epoch'\n\n/**\n * A machine-readable description of this board, for an agent that has never\n * seen it before: an orchestrator deciding how to work a card, a host wiring\n * up its own tools against the board, anything that is not a person reading\n * the README.\n *\n * Every list below is read out of the same constants and registrations the\n * board itself runs on — `KANBO_TOOLS`, `registerKanboCommands`,\n * `BOARD_RULES`, the canonical strings `ops/cards.ts` and `ops/runs.ts` write,\n * the roles `roles.sql.ts` creates — rather than written out a second time.\n * `manifest.test.ts` is what keeps that true: it fails the day one of those\n * sources gains something this file was not told about.\n *\n * `buildCapabilitiesManifest` and `renderCapabilitiesMarkdown` are the one\n * source two servings come from: `kanbo capabilities` prints either shape\n * from a terminal, and `kanbo mcp` serves both as resources\n * (`kanbo://capabilities`, `kanbo://capabilities.md`).\n */\n\n/** The manifest's own shape version, bumped when a field is added or removed — not the package's version. */\nexport const CAPABILITIES_MANIFEST_VERSION = '3'\n\n/** The board's name and the build that is answering, as `kanbo mcp` itself reports them. */\nexport interface KanboCapabilitiesPackage {\n name: string\n version: string\n}\n\n/** One of the three places a board can live, and what tells them apart. */\nexport interface KanboCapabilityStorage {\n /** `own-file` is `kanbo init --file`; `host-file` is a host app's own database; `postgres` is external. */\n kind: 'host-file' | 'own-file' | 'postgres'\n label: string\n description: string\n /** The board schema generation this build speaks, from `BOARD_SCHEMA_EPOCH` — the same number in all three. */\n schemaEpoch: number\n}\n\n/** One of the board's MCP tools, with the JSON Schema an orchestrator validates a call against. */\nexport interface KanboCapabilityTool {\n name: string\n title: string\n description: string\n /** `tool.inputSchema` converted with zod's own `toJSONSchema` — never hand-written. */\n inputSchema: Record<string, unknown>\n /** What to type instead, from `KANBO_TOOL_CLI_EQUIVALENTS`: the `kanbo` command. */\n cli: KanboToolCliEquivalent\n}\n\n/** One flag a CLI command takes, as commander itself declares it. */\nexport interface KanboCapabilityCommandOption {\n /** The flag spelling commander prints in `--help`, e.g. `--title <title>`. */\n flags: string\n description: string\n /** Set by `.requiredOption(...)`: the command refuses to run without it. */\n mandatory: boolean\n}\n\n/** One positional argument a CLI command takes, as commander itself declares it. */\nexport interface KanboCapabilityCommandArgument {\n name: string\n description: string\n required: boolean\n}\n\n/** One leaf command of the `kanbo` binary — a command with no subcommands of its own. */\nexport interface KanboCapabilityCommand {\n /** How a person types it, without the leading `kanbo`: `card create`, `approve`. */\n path: string\n description: string\n arguments: KanboCapabilityCommandArgument[]\n options: KanboCapabilityCommandOption[]\n /** `true` for the commands `requireHumanActor` guards: refused in a shell that says it belongs to an agent. */\n humanOnly: boolean\n /** Every exit code this build promises, from `CLI_EXIT_CODES` — the same table for every command. */\n exitCodes: typeof CLI_EXIT_CODES\n}\n\n/** The two database roles an external board is worked through, and what the database itself refuses the agent. */\nexport interface KanboCapabilitiesRoles {\n person: string\n agent: string\n /** What the database says back when the agent role tries the two things that are a person's. */\n refusals: string[]\n}\n\n/** One rule a column may ask of a card before an agent moves the card in (ruling 6-1). */\nexport interface KanboCapabilityEntryRule {\n rule: EntryRule\n description: string\n}\n\n/** Everything an orchestrator can read about this board before it has opened one. */\nexport interface KanboCapabilities {\n version: string\n package: KanboCapabilitiesPackage\n storages: KanboCapabilityStorage[]\n tools: KanboCapabilityTool[]\n commands: KanboCapabilityCommand[]\n /** The board-independent rules `kanbo prime` states — no columns here, because a column is per board. */\n rules: string[]\n /** Every rule a column may ask of a card, from `ENTRY_RULES` — which columns ask for which is per board, in `kanbo_columns`. */\n entryRules: KanboCapabilityEntryRule[]\n /** The literal strings the board writes for a run's lifecycle, an approval and a return — prefixes end in the punctuation or space that precedes the part a caller fills in. */\n canonicalStrings: Record<string, string>\n roles: KanboCapabilitiesRoles\n limits: string[]\n}\n\n/** The package name and version this build reports — the same literal `kanbo mcp` and `kanbo --version` already use. */\nconst KANBO_PACKAGE: KanboCapabilitiesPackage = { name: 'kanbo', version: KANBO_PACKAGE_VERSION }\n\nconst KANBO_STORAGES: readonly KanboCapabilityStorage[] = [\n {\n kind: 'host-file',\n label: 'A host app\\'s own database file',\n description: 'The board is nine tables among an app\\'s own, beside a `workspaces` table the command line reads '\n + 'the project\\'s workspace from. The app migrates the file itself; this package applies no migrations to '\n + 'it and reads and writes a schema someone else created. The app names the file with `KANBO_DB_PATH`.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n {\n kind: 'own-file',\n label: 'A board file of a project\\'s own',\n description: '`.kanbo/board.db` by default (`kanbo init --file`). This package creates it, migrates it '\n + 'with its own drizzle-sqlite chain, and marks it so every later command reads it as its own rather than '\n + 'looking for a `workspaces` table that is not there.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n {\n kind: 'postgres',\n label: 'An external Postgres database',\n description: 'Nothing but the board — no other owner exists to migrate it. `kanbo migrate` is the only '\n + 'thing that ever does, and `kanbo roles apply` installs the two roles and the rules the database '\n + 'itself keeps about them.',\n schemaEpoch: BOARD_SCHEMA_EPOCH,\n },\n]\n\n/**\n * The literal strings the board writes on a card's behalf, keyed the way an\n * agent reading the README prose would name them. A key ending the value in a\n * space or a colon-space is a prefix: the rest of the line is filled in by\n * whatever the write was about, not part of the constant.\n */\nfunction buildCanonicalStrings(): Record<string, string> {\n return {\n launched: LAUNCHED_STATUS_LINE_PREFIX,\n stoppedByYou: STOPPED_BY_YOU_STATUS_LINE,\n returned: RETURNED_STATUS_LINE_PREFIX,\n noReport: NO_REPORT_STATUS_LINE,\n runStarted: RUN_STARTED_COMMENT_PREFIX,\n runFinished: RUN_FINISHED_COMMENT,\n runFailed: RUN_FAILED_COMMENT,\n runFailedReason: RUN_FAILED_COMMENT_PREFIX,\n runStoppedByYou: RUN_STOPPED_BY_YOU_COMMENT,\n runStopped: RUN_STOPPED_COMMENT,\n runStoppedReason: RUN_STOPPED_REASON_PREFIX,\n approved: APPROVED_COMMENT,\n }\n}\n\n/** Every board tool, with its schema converted and its CLI equivalent attached. */\nfunction buildTools(): KanboCapabilityTool[] {\n return KANBO_TOOLS.map(tool => ({\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: z.toJSONSchema(z.object(tool.inputSchema)) as Record<string, unknown>,\n cli: KANBO_TOOL_CLI_EQUIVALENTS[tool.name],\n }))\n}\n\n/** Every leaf command the `kanbo` binary answers to, walked off its own registration. */\nfunction buildCommands(): KanboCapabilityCommand[] {\n const program = new Command()\n registerKanboCommands(program)\n\n function walk(command: Command, prefix: string[]): KanboCapabilityCommand[] {\n return command.commands.flatMap((child) => {\n const path = [...prefix, child.name()]\n if (child.commands.length > 0) {\n return walk(child, path)\n }\n return [{\n path: path.join(' '),\n description: child.description(),\n arguments: child.registeredArguments.map(argument => ({\n name: argument.name(),\n description: argument.description,\n required: argument.required,\n })),\n options: child.options.map(option => ({\n flags: option.flags,\n description: option.description,\n mandatory: option.mandatory,\n })),\n humanOnly: (HUMAN_ONLY_ACTIONS as readonly string[]).includes(path.join(' ')),\n exitCodes: CLI_EXIT_CODES,\n }]\n })\n }\n\n return walk(program, [])\n}\n\n/** Everything this build of the board can tell an orchestrator about itself. */\nexport function buildCapabilitiesManifest(): KanboCapabilities {\n return {\n version: CAPABILITIES_MANIFEST_VERSION,\n package: KANBO_PACKAGE,\n storages: [...KANBO_STORAGES],\n tools: buildTools(),\n commands: buildCommands(),\n rules: [...BOARD_RULES],\n entryRules: ENTRY_RULES.map(rule => ({ rule, description: ENTRY_RULE_DESCRIPTIONS[rule] })),\n canonicalStrings: buildCanonicalStrings(),\n roles: {\n person: BOARD_PERSON_ROLE,\n agent: BOARD_AGENT_ROLE,\n refusals: [AGENT_WAITING_REFUSAL, AGENT_APPROVAL_COMMENT_REFUSAL],\n },\n limits: [\n 'Only a person may approve or return a card. `kanbo approve` and `kanbo return` refuse a shell that '\n + 'says it belongs to an agent (`KANBO_ACTOR_KIND=agent`, exit 4), and there is no flag past it.',\n 'On an external board the same two rules are the database\\'s own: a person\\'s decision cannot be taken '\n + 'back, written over, or erased by the agent role, whichever client sends the write.',\n 'Only a person may close a sprint. `kanbo sprint close` refuses an agent\\'s shell '\n + '(exit 4), and the operation refuses any actor but a person; there is no MCP tool that closes one — '\n + '`kanbo_sprints` only reads. Unlike approval, the database role does not enforce this.',\n `The agent role (\\`${BOARD_AGENT_ROLE}\\`) may not delete a card at all — it holds select, insert and `\n + 'update on `issues` and nothing more, even though it may delete freely on every other board table.',\n 'A column may ask a card to meet entry rules before it enters (`entryRules`). Anyone but a person moving, '\n + 'creating or bulk-moving a card into it early is refused with `board_column_rules_unmet` and one line per '\n + 'unmet rule (CLI exit 1); a person goes through and is told what is missing. Only a person sets the rules '\n + '— `kanbo columns rules` refuses an agent\\'s shell (exit 4). The database role does not '\n + 'enforce them.',\n 'No board tool or command deletes a card, agent role or not. Cancelling one is a move to another column. '\n + 'The one exception is `kanbo serve`\\'s `DELETE /issues/:id`, which it answers only for '\n + 'a request presenting its token, on a server a person started, that has not sent `x-kanbo-actor: agent` '\n + '(403 `issue_delete_requires_user` otherwise).',\n '`kanbo migrate` and `kanbo roles apply` refuse a host app\\'s own database file: the app migrates it, '\n + 'and this package never touches it.',\n 'A write to a board older than this build\\'s schema epoch is refused (exit 3) until `kanbo migrate` '\n + 'runs (on a host app\\'s database, until the app migrates it) — reads stay open either way.',\n 'There is no MCP tool that approves a card, and there never will be: `kanbo_wait_approval` hands the '\n + 'card to a person and ends the turn; only a person takes it back.',\n ],\n }\n}\n"],"mappings":"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"}