kanbo-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/better-sqlite3-fU3G4xnX.cjs +2254 -0
  4. package/dist/better-sqlite3-fU3G4xnX.cjs.map +1 -0
  5. package/dist/cli.cjs +42949 -0
  6. package/dist/cli.cjs.map +1 -0
  7. package/dist/dist-Cd6wg8pp.cjs +942 -0
  8. package/dist/dist-Cd6wg8pp.cjs.map +1 -0
  9. package/dist/drizzle-postgres/0000_board.sql +135 -0
  10. package/dist/drizzle-postgres/0001_history_order.sql +2 -0
  11. package/dist/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
  12. package/dist/drizzle-postgres/0003_milestone_start_date.sql +1 -0
  13. package/dist/drizzle-postgres/0004_status_entry_rules.sql +1 -0
  14. package/dist/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
  15. package/dist/drizzle-postgres/README.md +61 -0
  16. package/dist/drizzle-postgres/meta/0000_snapshot.json +1078 -0
  17. package/dist/drizzle-postgres/meta/0001_snapshot.json +1090 -0
  18. package/dist/drizzle-postgres/meta/0002_snapshot.json +1239 -0
  19. package/dist/drizzle-postgres/meta/0003_snapshot.json +1245 -0
  20. package/dist/drizzle-postgres/meta/0004_snapshot.json +1251 -0
  21. package/dist/drizzle-postgres/meta/0005_snapshot.json +1257 -0
  22. package/dist/drizzle-postgres/meta/_journal.json +48 -0
  23. package/dist/drizzle-sqlite/0000_board.sql +133 -0
  24. package/dist/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
  25. package/dist/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
  26. package/dist/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
  27. package/dist/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
  28. package/dist/drizzle-sqlite/README.md +67 -0
  29. package/dist/drizzle-sqlite/meta/0000_snapshot.json +946 -0
  30. package/dist/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
  31. package/dist/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
  32. package/dist/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
  33. package/dist/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
  34. package/dist/drizzle-sqlite/meta/_journal.json +41 -0
  35. package/dist/lib/board-store-B16YarvX.d.mts +281 -0
  36. package/dist/lib/board-store-BHHo_ZlR.d.cts +281 -0
  37. package/dist/lib/index.cjs +340 -0
  38. package/dist/lib/index.cjs.map +1 -0
  39. package/dist/lib/index.d.cts +1608 -0
  40. package/dist/lib/index.d.mts +1608 -0
  41. package/dist/lib/index.mjs +284 -0
  42. package/dist/lib/index.mjs.map +1 -0
  43. package/dist/lib/manifest-C0EcwwgO.mjs +7866 -0
  44. package/dist/lib/manifest-C0EcwwgO.mjs.map +1 -0
  45. package/dist/lib/manifest-CpZOcB_6.cjs +8272 -0
  46. package/dist/lib/manifest-CpZOcB_6.cjs.map +1 -0
  47. package/dist/lib/mcp/index.cjs +184 -0
  48. package/dist/lib/mcp/index.cjs.map +1 -0
  49. package/dist/lib/mcp/index.d.cts +346 -0
  50. package/dist/lib/mcp/index.d.mts +346 -0
  51. package/dist/lib/mcp/index.mjs +173 -0
  52. package/dist/lib/mcp/index.mjs.map +1 -0
  53. package/dist/lib/migrate-BhYi9cXw.mjs +656 -0
  54. package/dist/lib/migrate-BhYi9cXw.mjs.map +1 -0
  55. package/dist/lib/migrate-DnMjTCvu.cjs +709 -0
  56. package/dist/lib/migrate-DnMjTCvu.cjs.map +1 -0
  57. package/dist/lib/migrations-path-CZxVOgeD.cjs +109 -0
  58. package/dist/lib/migrations-path-CZxVOgeD.cjs.map +1 -0
  59. package/dist/lib/migrations-path-Ciwan537.mjs +80 -0
  60. package/dist/lib/migrations-path-Ciwan537.mjs.map +1 -0
  61. package/dist/lib/postgres/index.cjs +56 -0
  62. package/dist/lib/postgres/index.cjs.map +1 -0
  63. package/dist/lib/postgres/index.d.cts +3403 -0
  64. package/dist/lib/postgres/index.d.mts +3403 -0
  65. package/dist/lib/postgres/index.mjs +35 -0
  66. package/dist/lib/postgres/index.mjs.map +1 -0
  67. package/dist/lib/roles.sql-B9rt-x4o.cjs +1033 -0
  68. package/dist/lib/roles.sql-B9rt-x4o.cjs.map +1 -0
  69. package/dist/lib/roles.sql-CvyCGt38.mjs +920 -0
  70. package/dist/lib/roles.sql-CvyCGt38.mjs.map +1 -0
  71. package/dist/lib/schema-epoch-ZyumVL_A.d.cts +16 -0
  72. package/dist/lib/schema-epoch-ZyumVL_A.d.mts +16 -0
  73. package/dist/lib/schema-fsHqNAip.d.cts +3612 -0
  74. package/dist/lib/schema-fsHqNAip.d.mts +3612 -0
  75. package/dist/lib/sqlite/index.cjs +23 -0
  76. package/dist/lib/sqlite/index.d.cts +175 -0
  77. package/dist/lib/sqlite/index.d.mts +175 -0
  78. package/dist/lib/sqlite/index.mjs +4 -0
  79. package/dist/lib/sqlite/schema.cjs +266 -0
  80. package/dist/lib/sqlite/schema.cjs.map +1 -0
  81. package/dist/lib/sqlite/schema.d.cts +2 -0
  82. package/dist/lib/sqlite/schema.d.mts +2 -0
  83. package/dist/lib/sqlite/schema.mjs +256 -0
  84. package/dist/lib/sqlite/schema.mjs.map +1 -0
  85. package/dist/lib/tool-names-9cmCn0gP.d.cts +112 -0
  86. package/dist/lib/tool-names-Dxg1UTUl.d.mts +112 -0
  87. package/dist/page/board.css +477 -0
  88. package/dist/page/board.js +2 -0
  89. package/drizzle-postgres/0000_board.sql +135 -0
  90. package/drizzle-postgres/0001_history_order.sql +2 -0
  91. package/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
  92. package/drizzle-postgres/0003_milestone_start_date.sql +1 -0
  93. package/drizzle-postgres/0004_status_entry_rules.sql +1 -0
  94. package/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
  95. package/drizzle-postgres/README.md +61 -0
  96. package/drizzle-postgres/meta/0000_snapshot.json +1078 -0
  97. package/drizzle-postgres/meta/0001_snapshot.json +1090 -0
  98. package/drizzle-postgres/meta/0002_snapshot.json +1239 -0
  99. package/drizzle-postgres/meta/0003_snapshot.json +1245 -0
  100. package/drizzle-postgres/meta/0004_snapshot.json +1251 -0
  101. package/drizzle-postgres/meta/0005_snapshot.json +1257 -0
  102. package/drizzle-postgres/meta/_journal.json +48 -0
  103. package/drizzle-sqlite/0000_board.sql +133 -0
  104. package/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
  105. package/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
  106. package/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
  107. package/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
  108. package/drizzle-sqlite/README.md +67 -0
  109. package/drizzle-sqlite/meta/0000_snapshot.json +946 -0
  110. package/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
  111. package/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
  112. package/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
  113. package/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
  114. package/drizzle-sqlite/meta/_journal.json +41 -0
  115. package/package.json +147 -0
@@ -0,0 +1,1608 @@
1
+ import { a as IssuePullRequest, c as IssueStatus, f as issueFieldChanges, i as IssueMilestone, l as KanbanMeta, n as IssueComment, o as IssueRelation, r as IssueFieldChange, s as IssueRun, t as Issue, v as issues } from "./schema-fsHqNAip.mjs";
2
+ import { a as BoardStore, c as IssueContextRefStore, d as IssuePullRequestStore, f as IssueRelationStore, g as KanbanMetaStore, h as IssueStatusStore, i as BoardRunProjectionScope, l as IssueFieldChangeStore, m as IssueRunStore, n as BoardLinkedPullRequest, o as BoardTransactionMode, p as IssueRowStore, r as BoardRunProjection, s as IssueCommentStore, t as BoardActiveRun, u as IssueMilestoneStore } from "./board-store-B16YarvX.mjs";
3
+ import { a as ENTRY_RULES, c as EntryRuleFacts, d as evaluateEntryRules, f as readChecklist, i as KanboToolName, l as UNMET_ENTRY_RULE_REASONS, m as serializeEntryRules, n as KANBO_TOOL_NAMES, o as ENTRY_RULE_DESCRIPTIONS, p as readEntryRules, r as KanboToolCliEquivalent, s as EntryRule, t as KANBO_TOOL_CLI_EQUIVALENTS, u as UnmetEntryRule } from "./tool-names-Dxg1UTUl.mjs";
4
+ import { z } from "zod";
5
+ //#region src/cli/output.d.ts
6
+ /**
7
+ * Every exit code this tool promises, and what it means — the capabilities
8
+ * manifest's `exitCodes` reads this rather than naming the codes again. `0` and
9
+ * `1` are not declared above: they need no name of their own, success and "I
10
+ * could not do that" being every command line's baseline.
11
+ */
12
+ declare const CLI_EXIT_CODES: readonly [{
13
+ readonly code: 0;
14
+ readonly meaning: "Success.";
15
+ }, {
16
+ readonly code: 1;
17
+ readonly meaning: "The command did not do what it was asked. See the message.";
18
+ }, {
19
+ readonly code: 2;
20
+ readonly meaning: "Nothing to work on, or nothing told the command what to work on.";
21
+ }, {
22
+ readonly code: 3;
23
+ readonly meaning: "The database is older than the board schema this build speaks.";
24
+ }, {
25
+ readonly code: 4;
26
+ readonly meaning: "The command is a person's to run, and this shell is not a person.";
27
+ }];
28
+ //#endregion
29
+ //#region src/capabilities/manifest.d.ts
30
+ /**
31
+ * A machine-readable description of this board, for an agent that has never
32
+ * seen it before: an orchestrator deciding how to work a card, a host wiring
33
+ * up its own tools against the board, anything that is not a person reading
34
+ * the README.
35
+ *
36
+ * Every list below is read out of the same constants and registrations the
37
+ * board itself runs on — `KANBO_TOOLS`, `registerKanboCommands`,
38
+ * `BOARD_RULES`, the canonical strings `ops/cards.ts` and `ops/runs.ts` write,
39
+ * the roles `roles.sql.ts` creates — rather than written out a second time.
40
+ * `manifest.test.ts` is what keeps that true: it fails the day one of those
41
+ * sources gains something this file was not told about.
42
+ *
43
+ * `buildCapabilitiesManifest` and `renderCapabilitiesMarkdown` are the one
44
+ * source two servings come from: `kanbo capabilities` prints either shape
45
+ * from a terminal, and `kanbo mcp` serves both as resources
46
+ * (`kanbo://capabilities`, `kanbo://capabilities.md`).
47
+ */
48
+ /** The manifest's own shape version, bumped when a field is added or removed — not the package's version. */
49
+ declare const CAPABILITIES_MANIFEST_VERSION = "3";
50
+ /** The board's name and the build that is answering, as `kanbo mcp` itself reports them. */
51
+ interface KanboCapabilitiesPackage {
52
+ name: string;
53
+ version: string;
54
+ }
55
+ /** One of the three places a board can live, and what tells them apart. */
56
+ interface KanboCapabilityStorage {
57
+ /** `own-file` is `kanbo init --file`; `host-file` is a host app's own database; `postgres` is external. */
58
+ kind: 'host-file' | 'own-file' | 'postgres';
59
+ label: string;
60
+ description: string;
61
+ /** The board schema generation this build speaks, from `BOARD_SCHEMA_EPOCH` — the same number in all three. */
62
+ schemaEpoch: number;
63
+ }
64
+ /** One of the board's MCP tools, with the JSON Schema an orchestrator validates a call against. */
65
+ interface KanboCapabilityTool {
66
+ name: string;
67
+ title: string;
68
+ description: string;
69
+ /** `tool.inputSchema` converted with zod's own `toJSONSchema` — never hand-written. */
70
+ inputSchema: Record<string, unknown>;
71
+ /** What to type instead, from `KANBO_TOOL_CLI_EQUIVALENTS`: the `kanbo` command. */
72
+ cli: KanboToolCliEquivalent;
73
+ }
74
+ /** One flag a CLI command takes, as commander itself declares it. */
75
+ interface KanboCapabilityCommandOption {
76
+ /** The flag spelling commander prints in `--help`, e.g. `--title <title>`. */
77
+ flags: string;
78
+ description: string;
79
+ /** Set by `.requiredOption(...)`: the command refuses to run without it. */
80
+ mandatory: boolean;
81
+ }
82
+ /** One positional argument a CLI command takes, as commander itself declares it. */
83
+ interface KanboCapabilityCommandArgument {
84
+ name: string;
85
+ description: string;
86
+ required: boolean;
87
+ }
88
+ /** One leaf command of the `kanbo` binary — a command with no subcommands of its own. */
89
+ interface KanboCapabilityCommand {
90
+ /** How a person types it, without the leading `kanbo`: `card create`, `approve`. */
91
+ path: string;
92
+ description: string;
93
+ arguments: KanboCapabilityCommandArgument[];
94
+ options: KanboCapabilityCommandOption[];
95
+ /** `true` for the commands `requireHumanActor` guards: refused in a shell that says it belongs to an agent. */
96
+ humanOnly: boolean;
97
+ /** Every exit code this build promises, from `CLI_EXIT_CODES` — the same table for every command. */
98
+ exitCodes: typeof CLI_EXIT_CODES;
99
+ }
100
+ /** The two database roles an external board is worked through, and what the database itself refuses the agent. */
101
+ interface KanboCapabilitiesRoles {
102
+ person: string;
103
+ agent: string;
104
+ /** What the database says back when the agent role tries the two things that are a person's. */
105
+ refusals: string[];
106
+ }
107
+ /** One rule a column may ask of a card before an agent moves the card in (ruling 6-1). */
108
+ interface KanboCapabilityEntryRule {
109
+ rule: EntryRule;
110
+ description: string;
111
+ }
112
+ /** Everything an orchestrator can read about this board before it has opened one. */
113
+ interface KanboCapabilities {
114
+ version: string;
115
+ package: KanboCapabilitiesPackage;
116
+ storages: KanboCapabilityStorage[];
117
+ tools: KanboCapabilityTool[];
118
+ commands: KanboCapabilityCommand[];
119
+ /** The board-independent rules `kanbo prime` states — no columns here, because a column is per board. */
120
+ rules: string[];
121
+ /** Every rule a column may ask of a card, from `ENTRY_RULES` — which columns ask for which is per board, in `kanbo_columns`. */
122
+ entryRules: KanboCapabilityEntryRule[];
123
+ /** 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. */
124
+ canonicalStrings: Record<string, string>;
125
+ roles: KanboCapabilitiesRoles;
126
+ limits: string[];
127
+ }
128
+ /** Everything this build of the board can tell an orchestrator about itself. */
129
+ declare function buildCapabilitiesManifest(): KanboCapabilities;
130
+ //#endregion
131
+ //#region src/capabilities/markdown.d.ts
132
+ /** The manifest, laid out under one heading per field — stable and diffable, never a snapshot to update by hand. */
133
+ declare function renderCapabilitiesMarkdown(manifest: KanboCapabilities): string;
134
+ //#endregion
135
+ //#region src/cli/binding.d.ts
136
+ /** The binding file itself, relative to the project root. */
137
+ declare const BINDING_FILE_PATH: string;
138
+ declare const BindingSchema: z.ZodObject<{
139
+ schemaVersion: z.ZodLiteral<1>;
140
+ workspaceId: z.ZodString;
141
+ boardId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
142
+ dbPath: z.ZodOptional<z.ZodNullable<z.ZodString>>;
143
+ databaseUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
144
+ agentDatabaseUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
145
+ identifier: z.ZodOptional<z.ZodNullable<z.ZodString>>;
146
+ }, z.core.$strip>;
147
+ /** The project's binding, as it is written and read back. */
148
+ type KanboBinding = z.infer<typeof BindingSchema>;
149
+ /**
150
+ * The binding this project folder has of its own — not a parent's.
151
+ *
152
+ * `kanbo init` merges into it rather than replacing it: a person re-running
153
+ * the command to refresh an instruction block never said anything about the
154
+ * board, and a second `init` that quietly dropped the connection string would
155
+ * leave a project bound to nothing.
156
+ */
157
+ declare function readProjectBinding(projectDir: string): KanboBinding | null;
158
+ /** The binding in that exact file, or `null` when there is none to read. */
159
+ declare function readBinding(path: string): KanboBinding | null;
160
+ /**
161
+ * Bind the project folder to a board.
162
+ *
163
+ * The binding itself is kept out of git, because the workspace id in it means
164
+ * nothing in anyone else's checkout. The ignore rule names the files kanbo
165
+ * writes rather than `*`, so a person who does want something in `.kanbo`
166
+ * committed can still add it. The user's own `.gitignore` is not touched at
167
+ * all; it is not kanbo's file to edit.
168
+ *
169
+ * It is written `0600`, because it may hold one or two connection strings and
170
+ * keeping a file out of a repository is not keeping it away from the other
171
+ * accounts on the machine. `mode` only applies to a file this call creates, so
172
+ * an existing one — a second `init`, or a file written before this rule — is
173
+ * narrowed explicitly. Windows has no such bits and `chmodSync` does nothing
174
+ * there, which is the right nothing: no permission to narrow, and no failure
175
+ * to handle.
176
+ */
177
+ declare function writeBinding(projectDir: string, binding: KanboBinding): string;
178
+ /**
179
+ * Make sure `.kanbo/.gitignore` also covers a board file of this project's
180
+ * own — `kanbo init --file` calls this once, right after creating it — and
181
+ * the two files SQLite leaves beside it in WAL mode, `-wal` and `-shm`, which
182
+ * hold committed data of their own and are exactly as much a secret as the
183
+ * database itself.
184
+ *
185
+ * Only when the file is inside `.kanbo`, which is where `--file` puts it by
186
+ * default: the ignore rule only ever grows kanbo's own directory, and a path
187
+ * chosen elsewhere in the project gets no line here, the same way the user's
188
+ * own `.gitignore` is never touched at all.
189
+ */
190
+ declare function ignoreBoardFile(projectDir: string, absoluteDbPath: string): void;
191
+ //#endregion
192
+ //#region src/default-board-file-path.d.ts
193
+ /**
194
+ * Where a board file of a project's own goes when nobody names a path —
195
+ * `kanbo init --file` writes it here, and an app that attaches a project's
196
+ * board file can import the same constant rather than spelling it again.
197
+ */
198
+ declare const DEFAULT_BOARD_FILE_PATH: string;
199
+ //#endregion
200
+ //#region src/domain/issue-view.d.ts
201
+ /** An issue row with its `labels` column decoded from JSON. */
202
+ type IssueView = Omit<Issue, 'labels'> & {
203
+ labels: string[];
204
+ };
205
+ /**
206
+ * Who a board change is attributed to. The `issue_field_changes.actor_kind`
207
+ * column owns the union; comment authors carry the same kinds plus the
208
+ * `system.*` markers that `normalizeCommentAuthorKind` folds away.
209
+ */
210
+ type IssueActorKind = IssueFieldChange['actorKind'];
211
+ declare function toIssueView(issue: Issue): IssueView;
212
+ /**
213
+ * Fold a comment's author kind onto the actor kinds a reader renders: only a
214
+ * person, an agent and a provider target speak for themselves, so every
215
+ * `system.*` marker — whatever it marks — is the board speaking as `system`.
216
+ */
217
+ declare function normalizeCommentAuthorKind(authorKind: IssueComment['authorKind']): IssueActorKind;
218
+ /** Who a row is filed under, as the row stores it — what a host resolves into a name to show. */
219
+ interface IssueActorReference {
220
+ kind: IssueActorKind;
221
+ id: string | null;
222
+ sourceChatSessionId: string | null;
223
+ }
224
+ /** What a stored row says about who wrote it: the name its writer gave, and the chat session it came from. */
225
+ interface IssueWriterProvenance {
226
+ authorId: IssueComment['authorId'];
227
+ sourceChatSessionId: IssueComment['sourceChatSessionId'];
228
+ }
229
+ /**
230
+ * Was this row written by an outside writer?
231
+ *
232
+ * An outside writer is stored as `system`, the same kind the board writes
233
+ * under, so the kind alone cannot say. What a row carries can: a writer from
234
+ * outside — the `kanbo` command line, an agent someone ran themselves — gives
235
+ * a name and speaks from no chat session, while the board writing on its
236
+ * own behalf gives no name (`BOARD_ACTOR`) and an app's agent runtime filed under
237
+ * `system` always carries the session it was called in. The caller establishes
238
+ * the `system` kind first; this answers the rest of the question, and says in
239
+ * its type that such a writer has a name to show.
240
+ */
241
+ declare function isOutsideWriter(provenance: IssueWriterProvenance): provenance is IssueWriterProvenance & {
242
+ authorId: string;
243
+ };
244
+ //#endregion
245
+ //#region src/domain/numbering.d.ts
246
+ /**
247
+ * What numbering needs to know about a workspace: enough to build the key stem,
248
+ * and nothing else. A host app's workspace row satisfies it; so does a workspace
249
+ * a command-line tool resolved from a path, without the board reading anybody's
250
+ * tables to find it.
251
+ *
252
+ * Written out rather than `Pick`ed from a row type, because the board has no row
253
+ * to pick from: a workspace belongs to whoever tracks it, and this package declares only what it
254
+ * needs of one.
255
+ */
256
+ interface BoardWorkspaceIdentity {
257
+ id: string;
258
+ identifier: string;
259
+ name: string;
260
+ }
261
+ /** The three-letter stem every issue key in the workspace starts with. */
262
+ declare function readIssuePrefix(workspace: BoardWorkspaceIdentity): string;
263
+ declare function formatIssueId(prefix: string, number: number): string;
264
+ /**
265
+ * The key and number the next issue of this workspace gets. The loop skips keys
266
+ * an earlier prefix already took, so a renamed workspace never collides with its
267
+ * own history.
268
+ */
269
+ declare function nextIssueIdentity(workspace: BoardWorkspaceIdentity, store: BoardStore): Promise<{
270
+ id: string;
271
+ number: number;
272
+ }>;
273
+ declare function nextIssueNumber(workspaceId: string, tx: BoardStore): Promise<number>;
274
+ declare function nextIssueOrder(workspaceId: string, tx: BoardStore): Promise<number>;
275
+ //#endregion
276
+ //#region src/domain/activity-types.d.ts
277
+ type IssueActivityValueToken = 'changed' | 'current-user' | 'empty' | 'no-due-date' | 'no-labels' | 'no-milestone' | 'no-parent' | 'no-status' | 'priority-high' | 'priority-low' | 'priority-medium' | 'priority-none' | 'priority-urgent' | 'unassigned' | 'unknown-issue' | 'unknown-milestone' | 'unknown-status' | 'unknown-user';
278
+ type IssueActivityValueView = {
279
+ kind: 'date';
280
+ timestamp: number;
281
+ } | {
282
+ kind: 'text';
283
+ text: string;
284
+ } | {
285
+ kind: 'token';
286
+ token: IssueActivityValueToken;
287
+ };
288
+ type IssueActivityField = 'assignee' | 'description' | 'due-date' | 'labels' | 'metadata' | 'milestone' | 'parent' | 'priority' | 'status' | 'status-line' | 'title' | 'workspace';
289
+ type IssueActivityAction = 'added-description' | 'changed-field' | 'cleared-description' | 'renamed-issue' | 'updated-description';
290
+ interface IssueActivityFieldChangeView {
291
+ action: IssueActivityAction;
292
+ field: IssueActivityField | null;
293
+ fromValue: IssueActivityValueView | null;
294
+ toValue: IssueActivityValueView | null;
295
+ }
296
+ interface IssueActivityCommentView {
297
+ content: string;
298
+ systemKind: 'delegated' | 'system' | 'undelegated' | null;
299
+ }
300
+ /** The actor a board shows beside a comment or an activity row. */
301
+ interface IssueCommentAuthorView {
302
+ kind: IssueActorKind;
303
+ id: string | null;
304
+ displayName: string;
305
+ avatarUrl: string | null;
306
+ label: string | null;
307
+ }
308
+ interface IssueActivityItemView {
309
+ id: string;
310
+ issueId: string;
311
+ kind: 'comment' | 'created' | 'field-change';
312
+ actor: IssueCommentAuthorView;
313
+ comment: IssueActivityCommentView | null;
314
+ fieldChange: IssueActivityFieldChangeView | null;
315
+ sourceChatSessionId: string | null;
316
+ createdAt: number;
317
+ }
318
+ /**
319
+ * The rows an Activity projection resolves ids against, read once per issue so
320
+ * formatting a change never goes back to the store.
321
+ */
322
+ interface IssueActivityLookup {
323
+ issue: Issue;
324
+ issueById: Map<string, Pick<Issue, 'id' | 'title'>>;
325
+ milestoneById: Map<string, Pick<IssueMilestone, 'id' | 'title'>>;
326
+ statusById: Map<string, Pick<IssueStatus, 'id' | 'name'>>;
327
+ workspaceById: Map<string, Pick<BoardWorkspaceIdentity, 'id' | 'name'>>;
328
+ }
329
+ //#endregion
330
+ //#region src/domain/card-display-title.d.ts
331
+ /**
332
+ * What a card is called wherever a person or an agent reads it by name.
333
+ *
334
+ * A card written as a description alone is titled with its own key (every
335
+ * writer does that). Named by its key, such a card says nothing — so its name
336
+ * is the first line of its description instead. A browser bundle that does not
337
+ * load this package keeps the same rule on its own, tested with the same
338
+ * examples.
339
+ */
340
+ declare function cardDisplayTitle(card: {
341
+ id: string;
342
+ title: string;
343
+ description?: string | null;
344
+ }): string;
345
+ //#endregion
346
+ //#region src/domain/database-url.d.ts
347
+ /**
348
+ * A board's connection string, as it may be said out loud.
349
+ *
350
+ * Everything a person needs to recognise which board they are talking to is
351
+ * the host and the database name. The password is the one part that must never
352
+ * reach a terminal, a log, a `--json` result or a tool's answer — and it is not
353
+ * the only part: a connection string carries options in its query, and a
354
+ * `?password=…` there is as much a secret as the one in front of the host.
355
+ *
356
+ * It is a fact about the value rather than about the command line, which is why
357
+ * it lives here: the MCP tools hand back messages they did not write either,
358
+ * and they must not be made to carry the board tool's own runtime to say them
359
+ * safely.
360
+ */
361
+ /**
362
+ * The connection string with everything secret taken out of it.
363
+ *
364
+ * A string this cannot take apart is not examined to find out why — it is
365
+ * replaced entirely, because whatever is wrong with it, the secret is still
366
+ * somewhere inside. A missing scheme counts as that: `user:secret@host/db`
367
+ * parses, as a `user:` URL whose whole password is in the path, and printing it
368
+ * back would say the secret out loud.
369
+ */
370
+ declare function maskDatabaseUrl(url: string): string;
371
+ /**
372
+ * Every connection string inside a line of text, masked.
373
+ *
374
+ * A message this package did not write — a driver's, a migrator's — may carry
375
+ * the whole connection string in the middle of a sentence, and it reaches the
376
+ * same terminal, or the same agent, as everything else.
377
+ */
378
+ declare function maskDatabaseUrls(text: string): string;
379
+ //#endregion
380
+ //#region src/domain/errors.d.ts
381
+ /**
382
+ * Every failure the board itself can name. HTTP status codes are not part of
383
+ * this list: what each code is over HTTP is `BOARD_ERROR_RESPONSES`
384
+ * (`../serve/board-error-responses.ts`), the one table `kanbo serve` and any app
385
+ * serving the board over HTTP answer with.
386
+ *
387
+ * Five of them are about where a board lives rather than about what is on it,
388
+ * and they are named here for the same reason as the rest: a host that keeps
389
+ * one board in a file and another in a database still answers its callers in
390
+ * one vocabulary. `board_storage_not_prepared` is a connection string pointing
391
+ * at a database that holds no board this build can speak for,
392
+ * `board_storage_unavailable` is one the host could not reach at all,
393
+ * `board_storage_not_configured` is a workspace that names no board to look at,
394
+ * `board_storage_not_empty` is a board still holding cards when something asked
395
+ * to point the workspace somewhere else, and `board_mixed_workspaces` is a
396
+ * single operation naming cards that turned out to live in two different
397
+ * boards — which no transaction can commit as one.
398
+ */
399
+ type BoardErrorCode = 'board_approval_requires_user' | 'board_column_rules_requires_user' | 'board_column_rules_unmet' | 'board_copy_conflict' | 'board_entry_rule_invalid' | 'board_execution_mode_locked' | 'board_mixed_workspaces' | 'board_pull_request_invalid' | 'board_pull_request_not_found' | 'board_pull_request_not_yours' | 'board_return_no_previous_column' | 'board_return_requires_user' | 'board_run_already_finished' | 'board_run_not_found' | 'board_run_session_ref_conflict' | 'board_run_session_ref_invalid' | 'board_run_session_ref_requires_user' | 'board_schema_outdated' | 'board_sprint_carry_invalid' | 'board_sprint_close_requires_user' | 'board_sprint_dates_invalid' | 'board_storage_not_configured' | 'board_storage_not_empty' | 'board_storage_not_prepared' | 'board_storage_unavailable' | 'issue_comment_delete_requires_user' | 'issue_comment_not_found' | 'issue_context_ref_invalid_index' | 'issue_milestone_not_found' | 'issue_not_found' | 'issue_parent_not_found' | 'issue_parent_self_reference' | 'issue_relation_not_found' | 'issue_relation_self_reference' | 'issue_status_name_empty' | 'issue_status_not_found' | 'issue_status_reference_conflict' | 'issue_workspace_not_found';
400
+ /** A board rule the caller broke, carrying the offending values in `details`. */
401
+ declare class BoardError extends Error {
402
+ readonly code: BoardErrorCode;
403
+ readonly details?: Record<string, unknown>;
404
+ constructor(code: BoardErrorCode, details?: Record<string, unknown>);
405
+ }
406
+ //#endregion
407
+ //#region src/domain/external-session-ref.d.ts
408
+ /**
409
+ * Where an external agent's own log of a run lives, once it has said (ruling
410
+ * 7-1): which tool wrote it, and the id that tool knows the session by.
411
+ */
412
+ interface ExternalSessionRef {
413
+ provider: 'claude' | 'codex';
414
+ /**
415
+ * The tool's own id for the session — a Claude Code session id, or a Codex
416
+ * session id (the one in the rollout's `session_meta`, which Codex prints).
417
+ */
418
+ id: string;
419
+ }
420
+ /**
421
+ * Read an external session reference out of what an agent passed to
422
+ * `kanbo run start --session` or `kanbo run attach-session`: `claude:<id>` —
423
+ * a Claude Code session id — or `codex:<id>` — a Codex session id. Anything
424
+ * else is refused with `board_run_session_ref_invalid` rather than stored
425
+ * unread, because a ref this build cannot parse back is a transcript nobody
426
+ * can ever open.
427
+ *
428
+ * Doubles as the validator: a caller that only needs to know whether a string
429
+ * is well formed calls this and catches the throw, the same as
430
+ * `parsePullRequestRef`.
431
+ */
432
+ declare function parseExternalSessionRef(input: string): ExternalSessionRef;
433
+ /** The stored and printed form: `claude:<id>` or `codex:<id>`. */
434
+ declare function formatExternalSessionRef(ref: ExternalSessionRef): string;
435
+ //#endregion
436
+ //#region src/domain/field-changes.d.ts
437
+ /**
438
+ * The issue columns whose edits are written to the audit trail.
439
+ *
440
+ * `statusLine` is absent on purpose: `writeStatusLine` records every line it
441
+ * writes itself, and no patch ever carries the column. `waitingFor` is absent
442
+ * too — whose turn it is shows in the card's own line and in the
443
+ * `system.approved` / `system.returned` comments around it, so a row saying the
444
+ * same thing a third time would only crowd the history.
445
+ */
446
+ declare const TRACKED_FIELDS: readonly ["workspaceId", "title", "description", "priority", "labels", "milestoneId", "parentIssueId", "statusId", "assigneeKind", "assigneeId", "dueDate", "delegateAgentId", "delegateProviderTargetId", "contextRefs", "executionMode", "order"];
447
+ /** Who an issue edit is attributed to, with the chat session it came from. */
448
+ interface IssueMutationActor {
449
+ kind: IssueActorKind;
450
+ id: string | null;
451
+ sourceChatSessionId?: string | null;
452
+ }
453
+ /**
454
+ * The audit rows one patch produces: a row per tracked field whose stringified
455
+ * value actually changed. Nothing is written here — the caller inserts what
456
+ * comes back, so the diff stays a pure function of the row, the patch, the
457
+ * actor and the clock.
458
+ */
459
+ declare function diffFieldChanges(before: Issue, updates: Partial<typeof issues.$inferInsert>, actor: IssueMutationActor, now: number): Array<typeof issueFieldChanges.$inferInsert>;
460
+ //#endregion
461
+ //#region src/domain/issue-activity.d.ts
462
+ declare function activityKindOrder(kind: IssueActivityItemView['kind']): number;
463
+ /**
464
+ * The badge a system comment renders under. Delegation owns two of its own; the
465
+ * rest of the `system.*` markers read as plain system comments until they earn
466
+ * a badge.
467
+ *
468
+ * A plain `system` row is where provenance decides: a writer from outside —
469
+ * the command line, an agent someone ran themselves — is stored as `system` too, and what it wrote is an ordinary comment
470
+ * rather than a line about the board — `isOutsideWriter` is that question.
471
+ */
472
+ declare function readSystemCommentKind(authorKind: IssueComment['authorKind'], provenance: IssueWriterProvenance): IssueActivityCommentView['systemKind'];
473
+ /**
474
+ * Who a comment is shown under. A `system.*` marker is the board's own line
475
+ * about what happened — a run launched, a person's decision — so the id it
476
+ * carries names whoever caused the line, never its author. Only a plain
477
+ * `system` row can name a writer.
478
+ */
479
+ declare function commentAuthorReference(comment: IssueComment): IssueActorReference;
480
+ declare function formatActivityIssueReference(value: string | null, lookup: IssueActivityLookup): IssueActivityValueView;
481
+ declare function formatPlainActivityValue(value: string | null): IssueActivityValueView;
482
+ declare function parseActivityStringArray(value: string | null): string[] | null;
483
+ declare function isEmptyActivityValue(value: string | null): value is null | '' | '[]';
484
+ declare function activityText(text: string | null): IssueActivityValueView | null;
485
+ declare function activityToken(token: IssueActivityValueToken): IssueActivityValueView;
486
+ //#endregion
487
+ //#region src/domain/pull-request-fact-keys.d.ts
488
+ /**
489
+ * The dedupe keys the facts about one pull request are written under on a card.
490
+ *
491
+ * Whatever watches a pull request writes them — opened, merged, closed and a CI
492
+ * verdict per head — and whatever reads a card's pull requests, this package's
493
+ * `ci_green` entry rule included, reads them back (rulings 4-7, 6-2), so the spelling lives here,
494
+ * with the board, rather than being copied into each. Owner and repo are lower-cased
495
+ * because GitHub names are case-insensitive (ruling 4-6): `Acme/Repo#7` and
496
+ * `acme/repo#7` key to the same facts.
497
+ */
498
+ interface PullRequestFactKeys {
499
+ opened: string;
500
+ merged: string;
501
+ closed: string;
502
+ /** GitHub answered 404/422 for this link on enough consecutive ticks that the watcher dropped it (`pr-watcher.ts`). */
503
+ gone: string;
504
+ /** What every CI verdict key of this pull request starts with; the head sha and `:green` or `:red` follow. */
505
+ ciPrefix: string;
506
+ }
507
+ declare function pullRequestFactKeys({ owner, repo, number }: {
508
+ owner: string;
509
+ repo: string;
510
+ number: number;
511
+ }): PullRequestFactKeys;
512
+ //#endregion
513
+ //#region src/domain/status-name.d.ts
514
+ /**
515
+ * The columns a workspace gets the first time someone opens its board, in board
516
+ * order. A workspace that already holds statuses is never re-seeded.
517
+ */
518
+ declare const DEFAULT_STATUSES: readonly [{
519
+ readonly name: "Backlog";
520
+ readonly description: "Idea captured, not ready to work on";
521
+ readonly color: "#6b7280";
522
+ readonly category: "backlog";
523
+ }, {
524
+ readonly name: "To Do";
525
+ readonly description: "Spelled out, ready to launch";
526
+ readonly color: "#9ca3af";
527
+ readonly category: "unstarted";
528
+ }, {
529
+ readonly name: "In Progress";
530
+ readonly description: "The orchestrator and its agents are working";
531
+ readonly color: "#f59e0b";
532
+ readonly category: "started";
533
+ }, {
534
+ readonly name: "In Review";
535
+ readonly description: "Work done; being checked or waiting for you";
536
+ readonly color: "#8b5cf6";
537
+ readonly category: "started";
538
+ }, {
539
+ readonly name: "Done";
540
+ readonly description: "Accepted";
541
+ readonly color: "#22c55e";
542
+ readonly category: "completed";
543
+ }, {
544
+ readonly name: "Canceled";
545
+ readonly description: "Canceled";
546
+ readonly color: "#6b7280";
547
+ readonly category: "canceled";
548
+ }];
549
+ /**
550
+ * The slug two status names have in common when they mean the same column:
551
+ * case and the run of spaces or dashes between words are ignored, so `In
552
+ * Progress`, `in-progress` and `IN_PROGRESS` all resolve to one status.
553
+ */
554
+ declare function normalizeStatusName(value: string): string;
555
+ //#endregion
556
+ //#region src/domain/time.d.ts
557
+ /**
558
+ * Unix seconds — the unit every board timestamp column stores.
559
+ *
560
+ * The package carries its own clock, so it depends on nothing of its host's.
561
+ */
562
+ declare function currentUnixSeconds(): number;
563
+ //#endregion
564
+ //#region src/ops/change-seq.d.ts
565
+ /**
566
+ * One board write in progress: the store to work through, and — behind it — the
567
+ * transaction and the change flag the whole write shares.
568
+ *
569
+ * An operation that takes a scope joins the write it belongs to instead of
570
+ * opening one of its own, which is how a card operation that seeds columns, a
571
+ * status line and a comment still counts as a single change.
572
+ */
573
+ interface BoardWriteScope<TStore extends BoardStore = BoardStore> {
574
+ /** The transactional store, wrapped so every write through it is noticed. */
575
+ readonly tx: TStore;
576
+ }
577
+ /**
578
+ * Run `fn` as one board write: everything it writes commits together, and the
579
+ * board's change counter goes up by exactly one if it wrote anything at all.
580
+ *
581
+ * The counter is what tells a reader — the board UI polling, a CLI, another
582
+ * agent — that the board is not the board it last read. It is bumped from the
583
+ * outermost scope only, after `fn` returns and only when a mutating store method
584
+ * was actually called, so a read-only pass and a transaction that rolls back
585
+ * both leave it untouched. Pass `scope` to join a write that is already open
586
+ * instead of starting a second one.
587
+ *
588
+ * The transaction takes its write lock up front (`immediate`), so two writers
589
+ * racing for the same board file fail fast rather than half-way through. A write
590
+ * that starts while the host already holds a transaction of its own becomes a
591
+ * savepoint inside it — the board counter then commits, or rolls back, with
592
+ * whatever the host is doing.
593
+ *
594
+ * The caller rule of the underlying transaction applies in full: `fn` must be
595
+ * the only database-touching async chain in flight, and inside it only
596
+ * operations that resolve without I/O may be awaited.
597
+ */
598
+ declare function runBoardWrite<TStore extends BoardStore, TResult>(store: TStore, fn: (scope: BoardWriteScope<TStore>) => Promise<TResult>, scope?: BoardWriteScope<TStore>): Promise<TResult>;
599
+ /**
600
+ * How many times the board has changed. A reader that holds a number and reads
601
+ * the same one back is looking at the board it already has.
602
+ */
603
+ declare function readChangeSeq(store: BoardStore): Promise<number>;
604
+ //#endregion
605
+ //#region src/ops/types.d.ts
606
+ /**
607
+ * Who is acting on the board.
608
+ *
609
+ * Four kinds reach it: a person, an agent an app launched and knows, a provider
610
+ * target standing in for one, and anything else — a command-line tool, a
611
+ * script, an agent someone ran themselves. `id` is whatever the host calls that
612
+ * actor, or `null` when it has no name to give; `chatSessionId` is the app's
613
+ * chat session the action came from, and means nothing to a board written from
614
+ * outside.
615
+ */
616
+ interface BoardActor {
617
+ kind: 'user' | 'agent' | 'provider-target' | 'external';
618
+ id: string | null;
619
+ name?: string;
620
+ chatSessionId?: string | null;
621
+ /**
622
+ * A person's own terminal placing a card (ruling 6-6): the write is still
623
+ * filed under `kind`/`id`, but a column's entry rules warn instead of
624
+ * refusing, as they do for a person on the board page. Only the entry-rule check reads
625
+ * it; it is never stored.
626
+ */
627
+ placesForPerson?: boolean;
628
+ }
629
+ /**
630
+ * The board writing on its own behalf: the lines and comments the board produces
631
+ * from a run's lifecycle, which no person and no agent typed.
632
+ *
633
+ * This matters beyond provenance. Whether a failed run ends with "no report"
634
+ * depends on the agent having written a status line since it started, so a line
635
+ * the board wrote itself must never be filed under the agent that was running.
636
+ */
637
+ declare const BOARD_ACTOR: BoardActor;
638
+ /** A comment as the board writes it, before the row exists. */
639
+ interface BoardCommentInput {
640
+ issueId: string;
641
+ content: string;
642
+ authorKind: IssueComment['authorKind'];
643
+ authorId?: string | null;
644
+ sourceChatSessionId?: string | null;
645
+ agentActivityId?: string | null;
646
+ /**
647
+ * The key that makes the comment a fact rather than a message: writing it
648
+ * again with the same key leaves the first one standing.
649
+ */
650
+ dedupeKey?: string | null;
651
+ }
652
+ //#endregion
653
+ //#region src/ops/cards.d.ts
654
+ /** Which column a caller means, when it may name either. */
655
+ interface ColumnReference {
656
+ statusId?: string | null;
657
+ statusName?: string | null;
658
+ }
659
+ /** A new card. The workspace comes in resolved: the board never reads anybody else's tables to find it. */
660
+ interface CreateCardInput extends ColumnReference {
661
+ workspace: BoardWorkspaceIdentity;
662
+ /** Optional: a card written as a description alone is titled with its own key. */
663
+ title?: string | null;
664
+ description?: string | null;
665
+ priority?: Issue['priority'];
666
+ labels?: string[];
667
+ milestoneId?: string | null;
668
+ parentIssueId?: string | null;
669
+ dueDate?: number | null;
670
+ assigneeKind?: string | null;
671
+ assigneeId?: string | null;
672
+ executionMode?: Issue['executionMode'];
673
+ }
674
+ /** Everything a card's fields can be changed to. */
675
+ interface UpdateCardInput extends ColumnReference {
676
+ workspaceId?: string;
677
+ title?: string;
678
+ description?: string | null;
679
+ priority?: Issue['priority'];
680
+ labels?: string[];
681
+ milestoneId?: string | null;
682
+ parentIssueId?: string | null;
683
+ assigneeKind?: string | null;
684
+ assigneeId?: string | null;
685
+ dueDate?: number | null;
686
+ order?: number;
687
+ executionMode?: Issue['executionMode'];
688
+ }
689
+ /**
690
+ * A card as a write that may have put it in a column hands it back. `unmetRules`
691
+ * is there only when a person put the card in a column whose entry rules it does
692
+ * not meet (ruling 6-3) — the write went through, and this is what to warn them
693
+ * about. An agent never sees it: it is refused instead.
694
+ */
695
+ type BoardCardWrite = Issue & {
696
+ unmetRules?: UnmetEntryRule[];
697
+ };
698
+ /** A comment a person or an agent writes; the author defaults to the actor. */
699
+ interface AddCommentInput extends Omit<BoardCommentInput, 'authorKind' | 'dedupeKey'> {
700
+ authorKind?: BoardCommentInput['authorKind'];
701
+ }
702
+ /** A fact the board records once, however many times it is told. */
703
+ interface CreateCommentOnceInput {
704
+ issueId: string;
705
+ authorKind: BoardCommentInput['authorKind'];
706
+ content: string;
707
+ dedupeKey: string;
708
+ actor: BoardActor;
709
+ }
710
+ //#endregion
711
+ //#region src/ops/card-batch.d.ts
712
+ /** The fields one change can set on many cards at once. */
713
+ interface BulkUpdateCardsInput {
714
+ statusId?: string | null;
715
+ priority?: Issue['priority'];
716
+ labels?: string[];
717
+ milestoneId?: string | null;
718
+ assigneeKind?: string | null;
719
+ assigneeId?: string | null;
720
+ dueDate?: number | null;
721
+ }
722
+ /** What a bulk change did: how many cards it wrote, and — for a person — which of them entered a column short of its rules. */
723
+ interface BulkUpdateCardsResult {
724
+ updated: number;
725
+ unmetRules?: Array<{
726
+ issueId: string;
727
+ unmet: UnmetEntryRule[];
728
+ }>;
729
+ }
730
+ //#endregion
731
+ //#region src/ops/migrate.d.ts
732
+ /** Which workspace's cards move where, and how their columns and milestones are matched up. */
733
+ interface MigrateCardsInput {
734
+ sourceId: string;
735
+ targetId: string;
736
+ /**
737
+ * Both workspaces' columns, in board order. The host reads them from each
738
+ * workspace's own board, which is not always the board the cards are moved on:
739
+ * a preview of a move across two boards reads the target's columns from the
740
+ * target while the cards are still on the source.
741
+ */
742
+ sourceStatuses: IssueStatus[];
743
+ targetStatuses: IssueStatus[];
744
+ /** A source column name → the target column name it becomes, when the names differ. */
745
+ statusMappings?: Record<string, string>;
746
+ /** A source milestone title → the target milestone title it becomes, when the titles differ. */
747
+ milestoneMappings?: Record<string, string>;
748
+ /** Count what would happen, and write nothing. */
749
+ dryRun?: boolean;
750
+ }
751
+ /** What a move of every card from one workspace to another did — or, for a dry run, would do. */
752
+ interface MigrateCardsResult {
753
+ processed: number;
754
+ updated: number;
755
+ numbersReassigned: number;
756
+ statusesMapped: {
757
+ from: string;
758
+ to: string;
759
+ }[];
760
+ milestonesMapped: {
761
+ from: string;
762
+ to: string | null;
763
+ }[];
764
+ parentIssuesCleared: number;
765
+ }
766
+ //#endregion
767
+ //#region src/ops/relations.d.ts
768
+ /** The card on the other end of an edge, as much of it as a relation row shows. */
769
+ type IssueRelationCounterpart = Pick<Issue, 'id' | 'workspaceId' | 'number' | 'title' | 'statusId' | 'priority'>;
770
+ /** An edge as seen from one of the two cards it touches. */
771
+ type IssueRelationView = IssueRelation & {
772
+ /** Orientation of the edge relative to the issue the relations were listed for. */
773
+ direction: 'outgoing' | 'incoming';
774
+ /** The issue on the other end of the edge, resolved across workspaces. */
775
+ counterpart: IssueRelationCounterpart | null;
776
+ };
777
+ /** A new edge between two cards. */
778
+ interface CreateRelationInput {
779
+ sourceIssueId: string;
780
+ targetIssueId: string;
781
+ type: IssueRelation['type'];
782
+ }
783
+ //#endregion
784
+ //#region src/ops/approval.d.ts
785
+ /** Where a card stands with the person who has to approve it. */
786
+ interface BoardApproval {
787
+ state: 'none' | 'pending' | 'approved' | 'returned';
788
+ /** When the person decided, in unix seconds; `null` while nobody has. */
789
+ decidedAt: number | null;
790
+ /** What the person wrote alongside the decision, if anything. */
791
+ comment: string | null;
792
+ }
793
+ //#endregion
794
+ //#region src/ops/pull-requests.d.ts
795
+ /**
796
+ * A pull request a card names, with where it stands as the card's own facts
797
+ * say (ruling 4-7): `merged` or `closed` once the watcher has written that,
798
+ * `open` once it has written «opened», `unknown` before it has read GitHub at
799
+ * all; `ci` is the newest CI verdict written for it, whatever head it was for.
800
+ */
801
+ type BoardPullRequestStanding = IssuePullRequest & {
802
+ state: 'open' | 'merged' | 'closed' | 'unknown';
803
+ ci: 'green' | 'red' | null;
804
+ };
805
+ //#endregion
806
+ //#region src/ops/sprints.d.ts
807
+ /**
808
+ * A milestone read as a sprint (ruling 5x-3): the row itself, how many of its
809
+ * cards are still open and how many are done, and whether it is the sprint
810
+ * running right now.
811
+ *
812
+ * `done` counts the cards in a `completed` or `canceled` column — the ones
813
+ * closing the sprint leaves where they are; `open` counts every other card,
814
+ * a card in no column at all included — the ones closing it carries on.
815
+ */
816
+ interface BoardSprint extends IssueMilestone {
817
+ cards: {
818
+ open: number;
819
+ done: number;
820
+ };
821
+ current: boolean;
822
+ }
823
+ /**
824
+ * What a milestone is created with; `startDate` and `dueDate` are unix seconds,
825
+ * kept as the first and the last second of their UTC days.
826
+ */
827
+ interface CreateMilestoneInput {
828
+ workspaceId: string;
829
+ title: string;
830
+ description?: string | null;
831
+ startDate?: number | null;
832
+ dueDate?: number | null;
833
+ status?: IssueMilestone['status'];
834
+ }
835
+ /** What of a milestone can change. */
836
+ type UpdateMilestoneInput = Partial<Omit<CreateMilestoneInput, 'workspaceId'>>;
837
+ /** What closing a sprint did: how many unfinished cards it moved on, and that the sprint is closed. */
838
+ interface CloseSprintResult {
839
+ carried: number;
840
+ closed: true;
841
+ }
842
+ //#endregion
843
+ //#region src/ops/runs.d.ts
844
+ /** What is known about a run when it starts. */
845
+ interface StartRunInput {
846
+ agentName: string;
847
+ agentId?: string | null;
848
+ host?: string | null;
849
+ /** Defaults to the mode the card carries. */
850
+ executionMode?: Issue['executionMode'];
851
+ /** Defaults to the actor's own kind and id. */
852
+ launchedByKind?: IssueRun['launchedByKind'];
853
+ launchedById?: string | null;
854
+ chatSessionId?: string | null;
855
+ branch?: string | null;
856
+ worktreePath?: string | null;
857
+ /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */
858
+ externalSessionRef?: string | null;
859
+ }
860
+ /** What a run turned out to be running in, once the host knows. */
861
+ interface AttachRunExecutionInput {
862
+ chatSessionId?: string | null;
863
+ branch?: string | null;
864
+ worktreePath?: string | null;
865
+ /** Where the agent's own log of this run lives, if it said: `claude:<id>` or `codex:<id>` (ruling 7-1). */
866
+ externalSessionRef?: string | null;
867
+ }
868
+ /** How a run ended, and — for a failure or a stop — what to say about it. */
869
+ interface FinishRunInput {
870
+ state: 'finished' | 'failed' | 'stopped';
871
+ errorText?: string | null;
872
+ /**
873
+ * Why a `stopped` run ended, when the answer is neither a person pressing
874
+ * stop nor the app that launched it tidying up quietly: the comment then reads
875
+ * `Run stopped: <reason>` instead of `Run stopped`.
876
+ */
877
+ reason?: string;
878
+ /**
879
+ * What the card says it is doing once that stop is recorded, whoever the
880
+ * actor is. A stop with no line of its own leaves the rule as it was: the
881
+ * person who pressed stop is named, and anything else says nothing.
882
+ */
883
+ statusLine?: string;
884
+ }
885
+ /** A run with the attempt number it is, counting from the card's first launch. */
886
+ type BoardRunView = IssueRun & {
887
+ attempt: number;
888
+ };
889
+ //#endregion
890
+ //#region src/ops/activity.d.ts
891
+ /**
892
+ * What the host knows and the board does not: the names of its workspaces, who
893
+ * an actor is, and what an agent is called. A board holds ids for all three and
894
+ * nothing more, so an Activity feed is the board's rows read through these.
895
+ */
896
+ interface IssueActivityResolver {
897
+ workspaceById: IssueActivityLookup['workspaceById'];
898
+ /** Who a reader sees beside a row. */
899
+ resolveActor: (actor: IssueActorReference) => Promise<IssueCommentAuthorView>;
900
+ /** The agent's name, or `null` when the host has no such agent. */
901
+ resolveAgentName: (agentId: string) => Promise<string | null>;
902
+ }
903
+ //#endregion
904
+ //#region src/ops/columns.d.ts
905
+ /** What a column is made of, beyond the workspace it belongs to and its position. */
906
+ interface BoardColumnInput {
907
+ workspaceId: string;
908
+ name: string;
909
+ description?: string | null;
910
+ color?: string | null;
911
+ category?: IssueStatus['category'];
912
+ /** What a card must satisfy before an agent may put it here — `ENTRY_RULES`, none by default. */
913
+ entryRules?: readonly string[] | null;
914
+ }
915
+ /** What `updateColumn` may change about a column. */
916
+ interface BoardColumnPatch {
917
+ name?: string;
918
+ description?: string | null;
919
+ color?: string | null;
920
+ /** `ENTRY_RULES`; `null` or an empty list asks for nothing. */
921
+ entryRules?: readonly string[] | null;
922
+ }
923
+ //#endregion
924
+ //#region src/ops/index.d.ts
925
+ /**
926
+ * Everything the board can be asked to do, bound to one store.
927
+ *
928
+ * This is the layer the semantics live in — numbering, columns, the status line,
929
+ * approval, runs — and there is exactly one of it. `kanbo serve`, an app's server, the
930
+ * command-line tool and the MCP server all call these operations rather than
931
+ * writing rows of their own, which is why a card launched from a terminal and a
932
+ * card launched from the app end up telling the same story.
933
+ *
934
+ * Every write takes an actor, and every write may take a `scope`: passing the
935
+ * scope of a write already in progress joins it, so a caller composing several
936
+ * operations gets one transaction and one board version bump.
937
+ */
938
+ declare function createBoardOps<TStore extends BoardStore>(store: TStore): {
939
+ createCard: (input: CreateCardInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<BoardCardWrite>;
940
+ createSubCard: (parentIssueId: string, input: Omit<CreateCardInput, "parentIssueId">, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<BoardCardWrite>;
941
+ updateCard: (issueId: string, input: UpdateCardInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<BoardCardWrite>;
942
+ moveCard: (issueId: string, nameOrId: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<BoardCardWrite>;
943
+ addComment: (input: AddCommentInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
944
+ id: string;
945
+ createdAt: number;
946
+ sourceChatSessionId: string | null;
947
+ issueId: string;
948
+ content: string;
949
+ authorKind: "user" | "agent" | "system" | "provider-target" | "system.delegated" | "system.undelegated" | "system.run" | "system.approved" | "system.returned" | "system.pr";
950
+ authorId: string | null;
951
+ agentActivityId: string | null;
952
+ dedupeKey: string | null;
953
+ }>;
954
+ createCommentOnce: (input: CreateCommentOnceInput, scope?: BoardWriteScope<TStore>) => Promise<{
955
+ id: string;
956
+ createdAt: number;
957
+ sourceChatSessionId: string | null;
958
+ issueId: string;
959
+ content: string;
960
+ authorKind: "user" | "agent" | "system" | "provider-target" | "system.delegated" | "system.undelegated" | "system.run" | "system.approved" | "system.returned" | "system.pr";
961
+ authorId: string | null;
962
+ agentActivityId: string | null;
963
+ dedupeKey: string | null;
964
+ }>;
965
+ listComments: (issueId: string) => Promise<{
966
+ id: string;
967
+ createdAt: number;
968
+ sourceChatSessionId: string | null;
969
+ issueId: string;
970
+ content: string;
971
+ authorKind: "user" | "agent" | "system" | "provider-target" | "system.delegated" | "system.undelegated" | "system.run" | "system.approved" | "system.returned" | "system.pr";
972
+ authorId: string | null;
973
+ agentActivityId: string | null;
974
+ dedupeKey: string | null;
975
+ }[]>;
976
+ deleteComment: (commentId: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<void>;
977
+ deleteCard: (issueId: string, scope?: BoardWriteScope<TStore>) => Promise<{
978
+ runIds: string[];
979
+ }>;
980
+ searchCards: (query: string, limit: number) => Promise<{
981
+ number: number;
982
+ id: string;
983
+ workspaceId: string;
984
+ description: string | null;
985
+ order: number;
986
+ createdAt: number;
987
+ title: string;
988
+ dueDate: number | null;
989
+ updatedAt: number;
990
+ statusId: string | null;
991
+ milestoneId: string | null;
992
+ parentIssueId: string | null;
993
+ priority: "none" | "low" | "medium" | "high" | "urgent";
994
+ labels: string;
995
+ assigneeKind: string | null;
996
+ assigneeId: string | null;
997
+ createdByKind: "user" | "agent" | "system" | "provider-target";
998
+ createdById: string;
999
+ sourceChatSessionId: string | null;
1000
+ delegateAgentId: string | null;
1001
+ delegateProviderTargetId: string | null;
1002
+ contextRefs: string;
1003
+ statusLine: string | null;
1004
+ waitingFor: "human" | null;
1005
+ executionMode: "worktree" | "main";
1006
+ }[]>;
1007
+ bulkUpdateCards: (issueIds: string[], input: BulkUpdateCardsInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<BulkUpdateCardsResult>;
1008
+ reorderCards: (workspaceId: string, orderedIds: string[], scope?: BoardWriteScope<TStore>) => Promise<void>;
1009
+ migrateCards: (input: MigrateCardsInput) => Promise<MigrateCardsResult>;
1010
+ addContextRef: (issueId: string, ref: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1011
+ number: number;
1012
+ id: string;
1013
+ workspaceId: string;
1014
+ description: string | null;
1015
+ order: number;
1016
+ createdAt: number;
1017
+ title: string;
1018
+ dueDate: number | null;
1019
+ updatedAt: number;
1020
+ statusId: string | null;
1021
+ milestoneId: string | null;
1022
+ parentIssueId: string | null;
1023
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1024
+ labels: string;
1025
+ assigneeKind: string | null;
1026
+ assigneeId: string | null;
1027
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1028
+ createdById: string;
1029
+ sourceChatSessionId: string | null;
1030
+ delegateAgentId: string | null;
1031
+ delegateProviderTargetId: string | null;
1032
+ contextRefs: string;
1033
+ statusLine: string | null;
1034
+ waitingFor: "human" | null;
1035
+ executionMode: "worktree" | "main";
1036
+ }>;
1037
+ removeContextRef: (issueId: string, index: number, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1038
+ number: number;
1039
+ id: string;
1040
+ workspaceId: string;
1041
+ description: string | null;
1042
+ order: number;
1043
+ createdAt: number;
1044
+ title: string;
1045
+ dueDate: number | null;
1046
+ updatedAt: number;
1047
+ statusId: string | null;
1048
+ milestoneId: string | null;
1049
+ parentIssueId: string | null;
1050
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1051
+ labels: string;
1052
+ assigneeKind: string | null;
1053
+ assigneeId: string | null;
1054
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1055
+ createdById: string;
1056
+ sourceChatSessionId: string | null;
1057
+ delegateAgentId: string | null;
1058
+ delegateProviderTargetId: string | null;
1059
+ contextRefs: string;
1060
+ statusLine: string | null;
1061
+ waitingFor: "human" | null;
1062
+ executionMode: "worktree" | "main";
1063
+ }>;
1064
+ listRelations: (issueId: string) => Promise<IssueRelationView[]>;
1065
+ createRelation: (input: CreateRelationInput, scope?: BoardWriteScope<TStore>) => Promise<{
1066
+ id: string;
1067
+ createdAt: number;
1068
+ sourceIssueId: string;
1069
+ targetIssueId: string;
1070
+ type: "blocks" | "duplicates" | "relates_to";
1071
+ }>;
1072
+ deleteRelation: (relationId: string, scope?: BoardWriteScope<TStore>) => Promise<void>;
1073
+ listFieldChanges: (issueId: string) => Promise<{
1074
+ id: string;
1075
+ createdAt: number;
1076
+ sourceChatSessionId: string | null;
1077
+ issueId: string;
1078
+ field: string;
1079
+ fromValue: string | null;
1080
+ toValue: string | null;
1081
+ actorKind: "user" | "agent" | "system" | "provider-target";
1082
+ actorId: string | null;
1083
+ }[]>;
1084
+ listActivity: (issueId: string, resolver: IssueActivityResolver) => Promise<IssueActivityItemView[]>;
1085
+ setStatusLine: (issueId: string, statusLine: string | null, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1086
+ number: number;
1087
+ id: string;
1088
+ workspaceId: string;
1089
+ description: string | null;
1090
+ order: number;
1091
+ createdAt: number;
1092
+ title: string;
1093
+ dueDate: number | null;
1094
+ updatedAt: number;
1095
+ statusId: string | null;
1096
+ milestoneId: string | null;
1097
+ parentIssueId: string | null;
1098
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1099
+ labels: string;
1100
+ assigneeKind: string | null;
1101
+ assigneeId: string | null;
1102
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1103
+ createdById: string;
1104
+ sourceChatSessionId: string | null;
1105
+ delegateAgentId: string | null;
1106
+ delegateProviderTargetId: string | null;
1107
+ contextRefs: string;
1108
+ statusLine: string | null;
1109
+ waitingFor: "human" | null;
1110
+ executionMode: "worktree" | "main";
1111
+ }>;
1112
+ waitApproval: (issueId: string, input: {
1113
+ statusLine?: string | null;
1114
+ }, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1115
+ number: number;
1116
+ id: string;
1117
+ workspaceId: string;
1118
+ description: string | null;
1119
+ order: number;
1120
+ createdAt: number;
1121
+ title: string;
1122
+ dueDate: number | null;
1123
+ updatedAt: number;
1124
+ statusId: string | null;
1125
+ milestoneId: string | null;
1126
+ parentIssueId: string | null;
1127
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1128
+ labels: string;
1129
+ assigneeKind: string | null;
1130
+ assigneeId: string | null;
1131
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1132
+ createdById: string;
1133
+ sourceChatSessionId: string | null;
1134
+ delegateAgentId: string | null;
1135
+ delegateProviderTargetId: string | null;
1136
+ contextRefs: string;
1137
+ statusLine: string | null;
1138
+ waitingFor: "human" | null;
1139
+ executionMode: "worktree" | "main";
1140
+ }>;
1141
+ approve: (issueId: string, input: {
1142
+ comment?: string | null;
1143
+ }, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1144
+ number: number;
1145
+ id: string;
1146
+ workspaceId: string;
1147
+ description: string | null;
1148
+ order: number;
1149
+ createdAt: number;
1150
+ title: string;
1151
+ dueDate: number | null;
1152
+ updatedAt: number;
1153
+ statusId: string | null;
1154
+ milestoneId: string | null;
1155
+ parentIssueId: string | null;
1156
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1157
+ labels: string;
1158
+ assigneeKind: string | null;
1159
+ assigneeId: string | null;
1160
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1161
+ createdById: string;
1162
+ sourceChatSessionId: string | null;
1163
+ delegateAgentId: string | null;
1164
+ delegateProviderTargetId: string | null;
1165
+ contextRefs: string;
1166
+ statusLine: string | null;
1167
+ waitingFor: "human" | null;
1168
+ executionMode: "worktree" | "main";
1169
+ }>;
1170
+ returnCard: (issueId: string, input: {
1171
+ comment: string;
1172
+ toStatusName?: string | null;
1173
+ }, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1174
+ card: Issue;
1175
+ stoppedRunIds: string[];
1176
+ }>;
1177
+ readApproval: (issueId: string) => Promise<BoardApproval>;
1178
+ linkPullRequest: (issueId: string, urlOrRef: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1179
+ number: number;
1180
+ id: string;
1181
+ createdAt: number;
1182
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1183
+ createdById: string | null;
1184
+ issueId: string;
1185
+ owner: string;
1186
+ repo: string;
1187
+ url: string;
1188
+ }>;
1189
+ unlinkPullRequest: (issueId: string, linkId: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1190
+ number: number;
1191
+ id: string;
1192
+ createdAt: number;
1193
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1194
+ createdById: string | null;
1195
+ issueId: string;
1196
+ owner: string;
1197
+ repo: string;
1198
+ url: string;
1199
+ }>;
1200
+ listPullRequests: (issueId: string) => Promise<{
1201
+ number: number;
1202
+ id: string;
1203
+ createdAt: number;
1204
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1205
+ createdById: string | null;
1206
+ issueId: string;
1207
+ owner: string;
1208
+ repo: string;
1209
+ url: string;
1210
+ }[]>;
1211
+ listPullRequestStandings: (issueId: string) => Promise<BoardPullRequestStanding[]>;
1212
+ listMilestones: (workspaceId: string | null) => Promise<{
1213
+ id: string;
1214
+ workspaceId: string;
1215
+ description: string | null;
1216
+ createdAt: number;
1217
+ title: string;
1218
+ startDate: number | null;
1219
+ dueDate: number | null;
1220
+ status: "open" | "closed";
1221
+ updatedAt: number;
1222
+ }[]>;
1223
+ deleteMilestone: (milestoneId: string, scope?: BoardWriteScope<TStore>) => Promise<void>;
1224
+ createMilestone: (input: CreateMilestoneInput, scope?: BoardWriteScope<TStore>) => Promise<{
1225
+ id: string;
1226
+ workspaceId: string;
1227
+ description: string | null;
1228
+ createdAt: number;
1229
+ title: string;
1230
+ startDate: number | null;
1231
+ dueDate: number | null;
1232
+ status: "open" | "closed";
1233
+ updatedAt: number;
1234
+ }>;
1235
+ updateMilestone: (milestoneId: string, input: UpdateMilestoneInput, scope?: BoardWriteScope<TStore>) => Promise<{
1236
+ id: string;
1237
+ workspaceId: string;
1238
+ description: string | null;
1239
+ createdAt: number;
1240
+ title: string;
1241
+ startDate: number | null;
1242
+ dueDate: number | null;
1243
+ status: "open" | "closed";
1244
+ updatedAt: number;
1245
+ }>;
1246
+ listSprints: (workspaceId: string, now?: number) => Promise<BoardSprint[]>;
1247
+ currentSprint: (workspaceId: string, now?: number) => Promise<BoardSprint | null>;
1248
+ closeSprint: (milestoneId: string, input: {
1249
+ carryTo?: string | null;
1250
+ }, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<CloseSprintResult>;
1251
+ listColumns: (workspaceId: string) => Promise<{
1252
+ id: string;
1253
+ workspaceId: string;
1254
+ name: string;
1255
+ description: string | null;
1256
+ color: string | null;
1257
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1258
+ order: number;
1259
+ entryRules: string | null;
1260
+ createdAt: number;
1261
+ }[]>;
1262
+ findColumn: (workspaceId: string, nameOrId: string) => Promise<{
1263
+ id: string;
1264
+ workspaceId: string;
1265
+ name: string;
1266
+ description: string | null;
1267
+ color: string | null;
1268
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1269
+ order: number;
1270
+ entryRules: string | null;
1271
+ createdAt: number;
1272
+ } | null>;
1273
+ requireColumn: (workspaceId: string, nameOrId: string) => Promise<{
1274
+ id: string;
1275
+ workspaceId: string;
1276
+ name: string;
1277
+ description: string | null;
1278
+ color: string | null;
1279
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1280
+ order: number;
1281
+ entryRules: string | null;
1282
+ createdAt: number;
1283
+ }>;
1284
+ ensureDefaultColumns: (workspaceId: string, scope?: BoardWriteScope<TStore>) => Promise<{
1285
+ id: string;
1286
+ workspaceId: string;
1287
+ name: string;
1288
+ description: string | null;
1289
+ color: string | null;
1290
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1291
+ order: number;
1292
+ entryRules: string | null;
1293
+ createdAt: number;
1294
+ }[]>;
1295
+ createColumn: (input: BoardColumnInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1296
+ id: string;
1297
+ workspaceId: string;
1298
+ name: string;
1299
+ description: string | null;
1300
+ color: string | null;
1301
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1302
+ order: number;
1303
+ entryRules: string | null;
1304
+ createdAt: number;
1305
+ }>;
1306
+ updateColumn: (statusId: string, patch: BoardColumnPatch, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1307
+ id: string;
1308
+ workspaceId: string;
1309
+ name: string;
1310
+ description: string | null;
1311
+ color: string | null;
1312
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1313
+ order: number;
1314
+ entryRules: string | null;
1315
+ createdAt: number;
1316
+ }>;
1317
+ describeColumn: (workspaceId: string, nameOrId: string, description: string | null, scope?: BoardWriteScope<TStore>) => Promise<{
1318
+ id: string;
1319
+ workspaceId: string;
1320
+ name: string;
1321
+ description: string | null;
1322
+ color: string | null;
1323
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1324
+ order: number;
1325
+ entryRules: string | null;
1326
+ createdAt: number;
1327
+ }>;
1328
+ setColumnEntryRules: (workspaceId: string, nameOrId: string, entryRules: readonly string[] | null, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1329
+ id: string;
1330
+ workspaceId: string;
1331
+ name: string;
1332
+ description: string | null;
1333
+ color: string | null;
1334
+ category: "triage" | "backlog" | "unstarted" | "started" | "completed" | "canceled";
1335
+ order: number;
1336
+ entryRules: string | null;
1337
+ createdAt: number;
1338
+ }>;
1339
+ enforceColumnEntryForCards: (issueIds: string[], statusId: string, actor: BoardActor) => Promise<{
1340
+ issueId: string;
1341
+ unmet: UnmetEntryRule[];
1342
+ }[]>;
1343
+ deleteColumn: (statusId: string, scope?: BoardWriteScope<TStore>) => Promise<void>;
1344
+ reorderColumns: (workspaceId: string, orderedIds: string[], scope?: BoardWriteScope<TStore>) => Promise<void>;
1345
+ addStandardColumns: (workspaceId: string, scope?: BoardWriteScope<TStore>) => Promise<{
1346
+ statuses: IssueStatus[];
1347
+ added: string[];
1348
+ }>;
1349
+ startRun: (issueId: string, input: StartRunInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1350
+ id: string;
1351
+ executionMode: "worktree" | "main";
1352
+ issueId: string;
1353
+ launchedByKind: "user" | "agent" | "external";
1354
+ launchedById: string | null;
1355
+ agentId: string | null;
1356
+ agentName: string;
1357
+ host: string | null;
1358
+ branch: string | null;
1359
+ worktreePath: string | null;
1360
+ state: "running" | "finished" | "failed" | "stopped";
1361
+ chatSessionId: string | null;
1362
+ externalSessionRef: string | null;
1363
+ startedAt: number;
1364
+ endedAt: number | null;
1365
+ }>;
1366
+ attachRunExecution: (runId: string, input: AttachRunExecutionInput, scope?: BoardWriteScope<TStore>) => Promise<{
1367
+ id: string;
1368
+ executionMode: "worktree" | "main";
1369
+ issueId: string;
1370
+ launchedByKind: "user" | "agent" | "external";
1371
+ launchedById: string | null;
1372
+ agentId: string | null;
1373
+ agentName: string;
1374
+ host: string | null;
1375
+ branch: string | null;
1376
+ worktreePath: string | null;
1377
+ state: "running" | "finished" | "failed" | "stopped";
1378
+ chatSessionId: string | null;
1379
+ externalSessionRef: string | null;
1380
+ startedAt: number;
1381
+ endedAt: number | null;
1382
+ }>;
1383
+ recordRunSessionRef: (runId: string, ref: string, scope?: BoardWriteScope<TStore>) => Promise<{
1384
+ id: string;
1385
+ executionMode: "worktree" | "main";
1386
+ issueId: string;
1387
+ launchedByKind: "user" | "agent" | "external";
1388
+ launchedById: string | null;
1389
+ agentId: string | null;
1390
+ agentName: string;
1391
+ host: string | null;
1392
+ branch: string | null;
1393
+ worktreePath: string | null;
1394
+ state: "running" | "finished" | "failed" | "stopped";
1395
+ chatSessionId: string | null;
1396
+ externalSessionRef: string | null;
1397
+ startedAt: number;
1398
+ endedAt: number | null;
1399
+ }>;
1400
+ clearRunSessionRef: (runId: string, actor: BoardActor, options?: {
1401
+ replaceWith?: string;
1402
+ }, scope?: BoardWriteScope<TStore>) => Promise<{
1403
+ id: string;
1404
+ executionMode: "worktree" | "main";
1405
+ issueId: string;
1406
+ launchedByKind: "user" | "agent" | "external";
1407
+ launchedById: string | null;
1408
+ agentId: string | null;
1409
+ agentName: string;
1410
+ host: string | null;
1411
+ branch: string | null;
1412
+ worktreePath: string | null;
1413
+ state: "running" | "finished" | "failed" | "stopped";
1414
+ chatSessionId: string | null;
1415
+ externalSessionRef: string | null;
1416
+ startedAt: number;
1417
+ endedAt: number | null;
1418
+ }>;
1419
+ finishRun: (runId: string, input: FinishRunInput, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<{
1420
+ id: string;
1421
+ executionMode: "worktree" | "main";
1422
+ issueId: string;
1423
+ launchedByKind: "user" | "agent" | "external";
1424
+ launchedById: string | null;
1425
+ agentId: string | null;
1426
+ agentName: string;
1427
+ host: string | null;
1428
+ branch: string | null;
1429
+ worktreePath: string | null;
1430
+ state: "running" | "finished" | "failed" | "stopped";
1431
+ chatSessionId: string | null;
1432
+ externalSessionRef: string | null;
1433
+ startedAt: number;
1434
+ endedAt: number | null;
1435
+ }>;
1436
+ stopRunningRuns: (issueId: string, actor: BoardActor, scope?: BoardWriteScope<TStore>) => Promise<string[]>;
1437
+ listRuns: (issueId: string) => Promise<BoardRunView[]>;
1438
+ findRunByChatSessionId: (chatSessionId: string) => Promise<{
1439
+ id: string;
1440
+ executionMode: "worktree" | "main";
1441
+ issueId: string;
1442
+ launchedByKind: "user" | "agent" | "external";
1443
+ launchedById: string | null;
1444
+ agentId: string | null;
1445
+ agentName: string;
1446
+ host: string | null;
1447
+ branch: string | null;
1448
+ worktreePath: string | null;
1449
+ state: "running" | "finished" | "failed" | "stopped";
1450
+ chatSessionId: string | null;
1451
+ externalSessionRef: string | null;
1452
+ startedAt: number;
1453
+ endedAt: number | null;
1454
+ } | null>;
1455
+ readBoardProjection: (workspaceId: string) => Promise<BoardRunProjection[]>;
1456
+ readBoardProjectionForIssues: (issueIds: string[]) => Promise<BoardRunProjection[]>;
1457
+ readBoardProjectionForIssue: (issueId: string) => Promise<BoardRunProjection>;
1458
+ listReady: (input: {
1459
+ workspaceId: string;
1460
+ limit?: number;
1461
+ }) => Promise<{
1462
+ number: number;
1463
+ id: string;
1464
+ workspaceId: string;
1465
+ description: string | null;
1466
+ order: number;
1467
+ createdAt: number;
1468
+ title: string;
1469
+ dueDate: number | null;
1470
+ updatedAt: number;
1471
+ statusId: string | null;
1472
+ milestoneId: string | null;
1473
+ parentIssueId: string | null;
1474
+ priority: "none" | "low" | "medium" | "high" | "urgent";
1475
+ labels: string;
1476
+ assigneeKind: string | null;
1477
+ assigneeId: string | null;
1478
+ createdByKind: "user" | "agent" | "system" | "provider-target";
1479
+ createdById: string;
1480
+ sourceChatSessionId: string | null;
1481
+ delegateAgentId: string | null;
1482
+ delegateProviderTargetId: string | null;
1483
+ contextRefs: string;
1484
+ statusLine: string | null;
1485
+ waitingFor: "human" | null;
1486
+ executionMode: "worktree" | "main";
1487
+ }[]>;
1488
+ buildPrimeText: (workspaceId: string) => Promise<string>;
1489
+ };
1490
+ /** The board's operations, as a type callers can name. */
1491
+ type BoardOps = ReturnType<typeof createBoardOps<BoardStore>>;
1492
+ //#endregion
1493
+ //#region src/ops/board-copy.d.ts
1494
+ /** How many rows of each kind a copy wrote into the target board. */
1495
+ interface BoardCopyCounts {
1496
+ statuses: number;
1497
+ milestones: number;
1498
+ issues: number;
1499
+ comments: number;
1500
+ runs: number;
1501
+ relations: number;
1502
+ fieldChanges: number;
1503
+ /** Cards that carried refs, not rows: context refs are a column on the card. */
1504
+ contextRefs: number;
1505
+ pullRequests: number;
1506
+ }
1507
+ /** Everything one workspace has on a board, read before anything is written. */
1508
+ interface BoardSlice {
1509
+ statuses: IssueStatus[];
1510
+ milestones: IssueMilestone[];
1511
+ /** In an order where a card's parent always comes before it. */
1512
+ issues: Issue[];
1513
+ comments: IssueComment[];
1514
+ runs: IssueRun[];
1515
+ relations: IssueRelation[];
1516
+ fieldChanges: IssueFieldChange[];
1517
+ pullRequests: IssuePullRequest[];
1518
+ /** What reading it had to leave behind, said to a person. */
1519
+ warnings: string[];
1520
+ }
1521
+ /** Nothing copied — what a move that had nothing to move reports. */
1522
+ declare function emptyCopyCounts(): BoardCopyCounts;
1523
+ /**
1524
+ * Everything the workspace has on this board, read in one pass before a single
1525
+ * row is written anywhere.
1526
+ *
1527
+ * Links are collected by id because an edge is listed by both of the cards it
1528
+ * touches, and one reaching a card outside this workspace is left behind rather
1529
+ * than copied: the target holds this workspace's board and nothing else, so the
1530
+ * far end of that edge is not there to point at.
1531
+ */
1532
+ declare function readBoardSlice(from: BoardStore, workspaceId: string): Promise<BoardSlice>;
1533
+ /**
1534
+ * Write a slice into the target board, in one transaction, and say what was
1535
+ * written and what a person should know about it — the slice's own warnings
1536
+ * first.
1537
+ *
1538
+ * It is idempotent by id. A row the target already has is left exactly as it
1539
+ * is and counted as already present, so a copy interrupted half-way is finished
1540
+ * by running it again, and a second run of a finished one copies nothing. A
1541
+ * clash that is not the same row — a column name or a card number the target
1542
+ * holds under another id — refuses the whole copy before its first insert
1543
+ * (`board_copy_conflict`, ruling 3-55).
1544
+ */
1545
+ declare function writeBoardSlice(to: BoardStore, slice: BoardSlice, workspaceId: string): Promise<{
1546
+ copied: BoardCopyCounts;
1547
+ warnings: string[];
1548
+ }>;
1549
+ /**
1550
+ * Empty the slice off the board it was read from, in a transaction of its own.
1551
+ *
1552
+ * Only the cards, the columns and the milestones are named: comments, runs,
1553
+ * links, pull request links and the field-change history hang off a card by a
1554
+ * foreign key that cascades on both engines, so deleting the card takes them
1555
+ * with it.
1556
+ */
1557
+ declare function deleteBoardSlice(from: BoardStore, slice: BoardSlice): Promise<void>;
1558
+ //#endregion
1559
+ //#region src/serve/board-error-responses.d.ts
1560
+ /** What one board failure is over HTTP: the status, the sentence, and the code a client matches on when it is renamed. */
1561
+ interface BoardErrorResponse {
1562
+ status: number;
1563
+ message: string;
1564
+ code?: string;
1565
+ }
1566
+ /**
1567
+ * What each board failure becomes over HTTP.
1568
+ *
1569
+ * The board names its failures and nothing else (`../domain/errors.ts`); the
1570
+ * status code and the sentence a client reads are decided here, once, for both
1571
+ * edges that serve it over HTTP — `kanbo serve` and any app that serves the
1572
+ * same routes — so a client written against one reads the other's failures the
1573
+ * same way. The
1574
+ * table is exhaustive on purpose — a new code in the package fails to compile
1575
+ * until this module says what it means to a caller — and it follows one rule: something the caller asked for that is not
1576
+ * there is 404, something the caller asked for that cannot be done is 400,
1577
+ * answering for a person — approving a card, sending it back or closing a
1578
+ * sprint — as anyone but one is 403, dropping a pull request link somebody else made is the same 403
1579
+ * for the same reason, and acting on a run that has already ended is 409,
1580
+ * because the answer would have been yes a moment earlier. An agent putting a
1581
+ * card in a column whose entry rules it does not meet yet is 409 for the same
1582
+ * reason turned around: the answer will be yes once the card meets them, and
1583
+ * `details.unmet` says what is missing.
1584
+ *
1585
+ * The five failures about where a board is kept read the same rule. A database
1586
+ * that holds no board this build can speak for is 409 — the setting is there,
1587
+ * the board behind it is not yet, and `board-storage prepare` is what settles
1588
+ * that — and one nobody could reach at all is 503, because nothing about the
1589
+ * request was wrong. A workspace naming no board at all is 404: the caller
1590
+ * asked about a setting that is not there. A board still holding cards when
1591
+ * somebody asks to point the workspace elsewhere is 409, because the answer
1592
+ * would have been yes on an empty board and `board-storage migrate` is what
1593
+ * makes it one. An operation naming cards from two different boards is 400: no
1594
+ * transaction reaches both, so the caller asked for something that cannot be
1595
+ * done. A copy that would land on a card number or a column name the target
1596
+ * already holds under a different id is 409 for the same reason the others
1597
+ * are: the answer would have been yes on a target that did not hold that
1598
+ * board, and `details.conflicts` names every value that clashed.
1599
+ *
1600
+ * A failure may also be renamed here: the package names its codes after the
1601
+ * board, while every error the `/issues` routes answer with is named after the
1602
+ * issue, and a client matching on `issue_*` should not have to learn a second
1603
+ * spelling. The code carries through unchanged when no `code` is given.
1604
+ */
1605
+ declare const BOARD_ERROR_RESPONSES: Record<BoardErrorCode, BoardErrorResponse>;
1606
+ //#endregion
1607
+ export { type AddCommentInput, type AttachRunExecutionInput, BINDING_FILE_PATH, BOARD_ACTOR, BOARD_ERROR_RESPONSES, type BoardActiveRun, type BoardActor, type BoardApproval, type BoardCardWrite, type BoardColumnInput, type BoardColumnPatch, type BoardCopyCounts, BoardError, type BoardErrorCode, type BoardErrorResponse, type BoardLinkedPullRequest, type BoardOps, type BoardPullRequestStanding, type BoardRunProjection, type BoardRunProjectionScope, type BoardRunView, type BoardSlice, type BoardSprint, type BoardStore, type BoardTransactionMode, type BoardWorkspaceIdentity, type BoardWriteScope, type BulkUpdateCardsInput, type BulkUpdateCardsResult, CAPABILITIES_MANIFEST_VERSION, type CloseSprintResult, type CreateCardInput, type CreateCommentOnceInput, type CreateMilestoneInput, type CreateRelationInput, DEFAULT_BOARD_FILE_PATH, DEFAULT_STATUSES, ENTRY_RULES, ENTRY_RULE_DESCRIPTIONS, type EntryRule, type EntryRuleFacts, type ExternalSessionRef, type FinishRunInput, type Issue, type IssueActivityAction, type IssueActivityCommentView, type IssueActivityField, type IssueActivityFieldChangeView, type IssueActivityItemView, type IssueActivityLookup, type IssueActivityResolver, type IssueActivityValueToken, type IssueActivityValueView, type IssueActorKind, type IssueActorReference, type IssueComment, type IssueCommentAuthorView, type IssueCommentStore, type IssueContextRefStore, type IssueFieldChange, type IssueFieldChangeStore, type IssueMilestone, type IssueMilestoneStore, type IssueMutationActor, type IssuePullRequest, type IssuePullRequestStore, type IssueRelation, type IssueRelationCounterpart, type IssueRelationStore, type IssueRelationView, type IssueRowStore, type IssueRun, type IssueRunStore, type IssueStatus, type IssueStatusStore, type IssueView, KANBO_TOOL_CLI_EQUIVALENTS, KANBO_TOOL_NAMES, type KanbanMeta, type KanbanMetaStore, type KanboBinding, type KanboCapabilities, type KanboCapabilitiesPackage, type KanboCapabilitiesRoles, type KanboCapabilityCommand, type KanboCapabilityCommandArgument, type KanboCapabilityCommandOption, type KanboCapabilityEntryRule, type KanboCapabilityStorage, type KanboCapabilityTool, type KanboToolCliEquivalent, type KanboToolName, type MigrateCardsInput, type MigrateCardsResult, type PullRequestFactKeys, type StartRunInput, TRACKED_FIELDS, UNMET_ENTRY_RULE_REASONS, type UnmetEntryRule, type UpdateCardInput, type UpdateMilestoneInput, activityKindOrder, activityText, activityToken, buildCapabilitiesManifest, cardDisplayTitle, commentAuthorReference, createBoardOps, currentUnixSeconds, deleteBoardSlice, diffFieldChanges, emptyCopyCounts, evaluateEntryRules, formatActivityIssueReference, formatExternalSessionRef, formatIssueId, formatPlainActivityValue, ignoreBoardFile, isEmptyActivityValue, isOutsideWriter, maskDatabaseUrl, maskDatabaseUrls, nextIssueIdentity, nextIssueNumber, nextIssueOrder, normalizeCommentAuthorKind, normalizeStatusName, parseActivityStringArray, parseExternalSessionRef, pullRequestFactKeys, readBinding, readBoardSlice, readChangeSeq, readChecklist, readEntryRules, readIssuePrefix, readProjectBinding, readSystemCommentKind, renderCapabilitiesMarkdown, runBoardWrite, serializeEntryRules, toIssueView, writeBinding, writeBoardSlice };
1608
+ //# sourceMappingURL=index.d.mts.map