kanbo-cli 0.2.1 → 0.3.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 (85) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +16 -47
  3. package/dist/{better-sqlite3-fU3G4xnX.cjs → better-sqlite3-DPdSZ6sH.cjs} +244 -247
  4. package/dist/cli-main.cjs +51208 -0
  5. package/dist/cli.cjs +32 -45216
  6. package/dist/dist-IHRTF-b5.cjs +2552 -0
  7. package/dist/lib/{tool-names-WfH4IURz.d.cts → approval-BsBk2xVr.d.cts} +72 -3
  8. package/dist/lib/{tool-names-3q_GEZej.d.mts → approval-LRZ6ontC.d.mts} +72 -3
  9. package/dist/lib/{board-store-BvWn9xTB.d.mts → board-store-D_Mpfb84.d.mts} +75 -2
  10. package/dist/lib/{board-store-BfszzAJB.d.cts → board-store-Dx-mw2I6.d.cts} +75 -2
  11. package/dist/lib/index.cjs +8 -2
  12. package/dist/lib/index.d.cts +202 -91
  13. package/dist/lib/index.d.mts +202 -91
  14. package/dist/lib/index.mjs +3 -3
  15. package/dist/lib/{manifest-D9nCRtXM.cjs → manifest-QPoQl8tn.cjs} +6881 -1556
  16. package/dist/lib/{manifest-DHV2fRdO.mjs → manifest-v2e9x_C8.mjs} +6795 -1556
  17. package/dist/lib/mcp/index.cjs +156 -21
  18. package/dist/lib/mcp/index.d.cts +140 -5
  19. package/dist/lib/mcp/index.d.mts +140 -5
  20. package/dist/lib/mcp/index.mjs +152 -22
  21. package/dist/lib/{migrate-DnMjTCvu.cjs → migrate-Dca5WdYd.cjs} +144 -14
  22. package/dist/lib/{migrate-BhYi9cXw.mjs → migrate-DtBeW3Fs.mjs} +134 -16
  23. package/dist/lib/{migrations-path-Ciwan537.mjs → migrations-path-ClaYRNwk.mjs} +5 -7
  24. package/dist/lib/{migrations-path-CZxVOgeD.cjs → migrations-path-DBCE17dT.cjs} +5 -7
  25. package/dist/lib/postgres/index.cjs +2 -4
  26. package/dist/lib/postgres/index.d.cts +2 -3
  27. package/dist/lib/postgres/index.d.mts +2 -3
  28. package/dist/lib/postgres/index.mjs +2 -4
  29. package/dist/lib/{roles.sql-CvyCGt38.mjs → roles.sql-BoOZSkBp.mjs} +96 -5
  30. package/dist/lib/{roles.sql-B9rt-x4o.cjs → roles.sql-E58x7mLS.cjs} +95 -4
  31. package/dist/lib/schema-D0MyXcej.d.cts +1 -2
  32. package/dist/lib/schema-D0MyXcej.d.mts +1 -2
  33. package/dist/lib/schema-epoch-ZyumVL_A.d.cts +1 -2
  34. package/dist/lib/schema-epoch-ZyumVL_A.d.mts +1 -2
  35. package/dist/lib/sqlite/index.cjs +2 -2
  36. package/dist/lib/sqlite/index.d.cts +3 -4
  37. package/dist/lib/sqlite/index.d.mts +3 -4
  38. package/dist/lib/sqlite/index.mjs +2 -2
  39. package/dist/lib/sqlite/schema.cjs +0 -2
  40. package/dist/lib/sqlite/schema.mjs +0 -2
  41. package/package.json +13 -10
  42. package/dist/better-sqlite3-fU3G4xnX.cjs.map +0 -1
  43. package/dist/cli.cjs.map +0 -1
  44. package/dist/dist-BsCwQFEl.cjs +0 -989
  45. package/dist/dist-BsCwQFEl.cjs.map +0 -1
  46. package/dist/lib/manifest-D9nCRtXM.cjs.map +0 -1
  47. package/dist/lib/manifest-DHV2fRdO.mjs.map +0 -1
  48. package/dist/lib/mcp/index.cjs.map +0 -1
  49. package/dist/lib/mcp/index.mjs.map +0 -1
  50. package/dist/lib/migrate-BhYi9cXw.mjs.map +0 -1
  51. package/dist/lib/migrate-DnMjTCvu.cjs.map +0 -1
  52. package/dist/lib/migrations-path-CZxVOgeD.cjs.map +0 -1
  53. package/dist/lib/migrations-path-Ciwan537.mjs.map +0 -1
  54. package/dist/lib/postgres/index.cjs.map +0 -1
  55. package/dist/lib/postgres/index.mjs.map +0 -1
  56. package/dist/lib/roles.sql-B9rt-x4o.cjs.map +0 -1
  57. package/dist/lib/roles.sql-CvyCGt38.mjs.map +0 -1
  58. package/dist/lib/sqlite/schema.cjs.map +0 -1
  59. package/dist/lib/sqlite/schema.mjs.map +0 -1
  60. package/drizzle-postgres/0000_board.sql +0 -135
  61. package/drizzle-postgres/0001_history_order.sql +0 -2
  62. package/drizzle-postgres/0002_issue_pull_requests.sql +0 -16
  63. package/drizzle-postgres/0003_milestone_start_date.sql +0 -1
  64. package/drizzle-postgres/0004_status_entry_rules.sql +0 -1
  65. package/drizzle-postgres/0005_run_external_session_ref.sql +0 -1
  66. package/drizzle-postgres/README.md +0 -61
  67. package/drizzle-postgres/meta/0000_snapshot.json +0 -1078
  68. package/drizzle-postgres/meta/0001_snapshot.json +0 -1090
  69. package/drizzle-postgres/meta/0002_snapshot.json +0 -1239
  70. package/drizzle-postgres/meta/0003_snapshot.json +0 -1245
  71. package/drizzle-postgres/meta/0004_snapshot.json +0 -1251
  72. package/drizzle-postgres/meta/0005_snapshot.json +0 -1257
  73. package/drizzle-postgres/meta/_journal.json +0 -48
  74. package/drizzle-sqlite/0000_board.sql +0 -133
  75. package/drizzle-sqlite/0001_issue_pull_requests.sql +0 -15
  76. package/drizzle-sqlite/0002_milestone_start_date.sql +0 -1
  77. package/drizzle-sqlite/0003_status_entry_rules.sql +0 -1
  78. package/drizzle-sqlite/0004_run_external_session_ref.sql +0 -1
  79. package/drizzle-sqlite/README.md +0 -67
  80. package/drizzle-sqlite/meta/0000_snapshot.json +0 -946
  81. package/drizzle-sqlite/meta/0001_snapshot.json +0 -1054
  82. package/drizzle-sqlite/meta/0002_snapshot.json +0 -1061
  83. package/drizzle-sqlite/meta/0003_snapshot.json +0 -1068
  84. package/drizzle-sqlite/meta/0004_snapshot.json +0 -1075
  85. package/drizzle-sqlite/meta/_journal.json +0 -41
@@ -1,4 +1,4 @@
1
- import { c as IssueStatus } from "./schema-D0MyXcej.cjs";
1
+ import { c as IssueStatus, n as IssueComment, r as IssueFieldChange, t as Issue } from "./schema-D0MyXcej.cjs";
2
2
  //#region src/domain/entry-rules.d.ts
3
3
  /**
4
4
  * What a column may ask of a card before an agent puts the card in it (ruling
@@ -108,5 +108,74 @@ interface KanboToolCliEquivalent {
108
108
  */
109
109
  declare const KANBO_TOOL_CLI_EQUIVALENTS: Record<KanboToolName, KanboToolCliEquivalent>;
110
110
  //#endregion
111
- export { ENTRY_RULES as a, EntryRuleFacts as c, evaluateEntryRules as d, readChecklist as f, KanboToolName as i, UNMET_ENTRY_RULE_REASONS as l, serializeEntryRules as m, KANBO_TOOL_NAMES as n, ENTRY_RULE_DESCRIPTIONS as o, readEntryRules as p, KanboToolCliEquivalent as r, EntryRule as s, KANBO_TOOL_CLI_EQUIVALENTS as t, UnmetEntryRule as u };
112
- //# sourceMappingURL=tool-names-WfH4IURz.d.cts.map
111
+ //#region src/domain/issue-view.d.ts
112
+ /** An issue row with its `labels` column decoded from JSON. */
113
+ type IssueView = Omit<Issue, 'labels'> & {
114
+ labels: string[];
115
+ };
116
+ /**
117
+ * Who a board change is attributed to. The `issue_field_changes.actor_kind`
118
+ * column owns the union; comment authors carry the same kinds plus the
119
+ * `system.*` markers that `normalizeCommentAuthorKind` folds away.
120
+ */
121
+ type IssueActorKind = IssueFieldChange['actorKind'];
122
+ declare function toIssueView(issue: Issue): IssueView;
123
+ /**
124
+ * Fold a comment's author kind onto the actor kinds a reader renders: only a
125
+ * person, an agent and a provider target speak for themselves, so every
126
+ * `system.*` marker — whatever it marks — is the board speaking as `system`.
127
+ */
128
+ declare function normalizeCommentAuthorKind(authorKind: string): IssueActorKind;
129
+ /** Who a row is filed under, as the row stores it — what a host resolves into a name to show. */
130
+ interface IssueActorReference {
131
+ kind: IssueActorKind;
132
+ id: string | null;
133
+ sourceChatSessionId: string | null;
134
+ }
135
+ /** What a stored row says about who wrote it: the name its writer gave, and the chat session it came from. */
136
+ interface IssueWriterProvenance {
137
+ authorId: IssueComment['authorId'];
138
+ sourceChatSessionId: IssueComment['sourceChatSessionId'];
139
+ }
140
+ /**
141
+ * Was this row written by an outside writer?
142
+ *
143
+ * An outside writer is stored as `system`, the same kind the board writes
144
+ * under, so the kind alone cannot say. What a row carries can: a writer from
145
+ * outside — the `kanbo` command line, an agent someone ran themselves — gives
146
+ * a name and speaks from no chat session, while the board writing on its
147
+ * own behalf gives no name (`BOARD_ACTOR`) and an app's agent runtime filed under
148
+ * `system` always carries the session it was called in. The caller establishes
149
+ * the `system` kind first; this answers the rest of the question, and says in
150
+ * its type that such a writer has a name to show.
151
+ */
152
+ declare function isOutsideWriter(provenance: IssueWriterProvenance): provenance is IssueWriterProvenance & {
153
+ authorId: string;
154
+ };
155
+ //#endregion
156
+ //#region src/ops/approval.d.ts
157
+ /** Where a card stands with the person who has to approve it. */
158
+ interface BoardApproval {
159
+ state: 'none' | 'pending' | 'approved' | 'returned';
160
+ /** When the person decided, in unix seconds; `null` while nobody has. */
161
+ decidedAt: number | null;
162
+ /** What the person wrote alongside the decision, if anything. */
163
+ comment: string | null;
164
+ }
165
+ /** A card's latest comment, as a list row carries it. */
166
+ interface CardLastComment {
167
+ /** Who wrote it; a person's decision (an approval, a return) is the person's. */
168
+ author: IssueActorKind;
169
+ /** Its first `LAST_COMMENT_PREVIEW_LENGTH` characters, `…` where it was cut. */
170
+ text: string;
171
+ createdAt: number;
172
+ }
173
+ /** What a list says beside a card whose turn is a person's, or which a person just sent back. */
174
+ interface CardAttention {
175
+ /** A person sent it back and nobody has picked it up again (`BoardCardQuery.returned`). */
176
+ returned: boolean;
177
+ /** Its latest comment, for a card waiting for a person or returned; `null` for the rest. */
178
+ lastComment: CardLastComment | null;
179
+ }
180
+ //#endregion
181
+ export { readEntryRules as C, readChecklist as S, EntryRule as _, IssueActorReference as a, UnmetEntryRule as b, isOutsideWriter as c, KANBO_TOOL_CLI_EQUIVALENTS as d, KANBO_TOOL_NAMES as f, ENTRY_RULE_DESCRIPTIONS as g, ENTRY_RULES as h, IssueActorKind as i, normalizeCommentAuthorKind as l, KanboToolName as m, CardAttention as n, IssueView as o, KanboToolCliEquivalent as p, CardLastComment as r, IssueWriterProvenance as s, BoardApproval as t, toIssueView as u, EntryRuleFacts as v, serializeEntryRules as w, evaluateEntryRules as x, UNMET_ENTRY_RULE_REASONS as y };
@@ -1,4 +1,4 @@
1
- import { c as IssueStatus } from "./schema-D0MyXcej.mjs";
1
+ import { c as IssueStatus, n as IssueComment, r as IssueFieldChange, t as Issue } from "./schema-D0MyXcej.mjs";
2
2
  //#region src/domain/entry-rules.d.ts
3
3
  /**
4
4
  * What a column may ask of a card before an agent puts the card in it (ruling
@@ -108,5 +108,74 @@ interface KanboToolCliEquivalent {
108
108
  */
109
109
  declare const KANBO_TOOL_CLI_EQUIVALENTS: Record<KanboToolName, KanboToolCliEquivalent>;
110
110
  //#endregion
111
- export { ENTRY_RULES as a, EntryRuleFacts as c, evaluateEntryRules as d, readChecklist as f, KanboToolName as i, UNMET_ENTRY_RULE_REASONS as l, serializeEntryRules as m, KANBO_TOOL_NAMES as n, ENTRY_RULE_DESCRIPTIONS as o, readEntryRules as p, KanboToolCliEquivalent as r, EntryRule as s, KANBO_TOOL_CLI_EQUIVALENTS as t, UnmetEntryRule as u };
112
- //# sourceMappingURL=tool-names-3q_GEZej.d.mts.map
111
+ //#region src/domain/issue-view.d.ts
112
+ /** An issue row with its `labels` column decoded from JSON. */
113
+ type IssueView = Omit<Issue, 'labels'> & {
114
+ labels: string[];
115
+ };
116
+ /**
117
+ * Who a board change is attributed to. The `issue_field_changes.actor_kind`
118
+ * column owns the union; comment authors carry the same kinds plus the
119
+ * `system.*` markers that `normalizeCommentAuthorKind` folds away.
120
+ */
121
+ type IssueActorKind = IssueFieldChange['actorKind'];
122
+ declare function toIssueView(issue: Issue): IssueView;
123
+ /**
124
+ * Fold a comment's author kind onto the actor kinds a reader renders: only a
125
+ * person, an agent and a provider target speak for themselves, so every
126
+ * `system.*` marker — whatever it marks — is the board speaking as `system`.
127
+ */
128
+ declare function normalizeCommentAuthorKind(authorKind: string): IssueActorKind;
129
+ /** Who a row is filed under, as the row stores it — what a host resolves into a name to show. */
130
+ interface IssueActorReference {
131
+ kind: IssueActorKind;
132
+ id: string | null;
133
+ sourceChatSessionId: string | null;
134
+ }
135
+ /** What a stored row says about who wrote it: the name its writer gave, and the chat session it came from. */
136
+ interface IssueWriterProvenance {
137
+ authorId: IssueComment['authorId'];
138
+ sourceChatSessionId: IssueComment['sourceChatSessionId'];
139
+ }
140
+ /**
141
+ * Was this row written by an outside writer?
142
+ *
143
+ * An outside writer is stored as `system`, the same kind the board writes
144
+ * under, so the kind alone cannot say. What a row carries can: a writer from
145
+ * outside — the `kanbo` command line, an agent someone ran themselves — gives
146
+ * a name and speaks from no chat session, while the board writing on its
147
+ * own behalf gives no name (`BOARD_ACTOR`) and an app's agent runtime filed under
148
+ * `system` always carries the session it was called in. The caller establishes
149
+ * the `system` kind first; this answers the rest of the question, and says in
150
+ * its type that such a writer has a name to show.
151
+ */
152
+ declare function isOutsideWriter(provenance: IssueWriterProvenance): provenance is IssueWriterProvenance & {
153
+ authorId: string;
154
+ };
155
+ //#endregion
156
+ //#region src/ops/approval.d.ts
157
+ /** Where a card stands with the person who has to approve it. */
158
+ interface BoardApproval {
159
+ state: 'none' | 'pending' | 'approved' | 'returned';
160
+ /** When the person decided, in unix seconds; `null` while nobody has. */
161
+ decidedAt: number | null;
162
+ /** What the person wrote alongside the decision, if anything. */
163
+ comment: string | null;
164
+ }
165
+ /** A card's latest comment, as a list row carries it. */
166
+ interface CardLastComment {
167
+ /** Who wrote it; a person's decision (an approval, a return) is the person's. */
168
+ author: IssueActorKind;
169
+ /** Its first `LAST_COMMENT_PREVIEW_LENGTH` characters, `…` where it was cut. */
170
+ text: string;
171
+ createdAt: number;
172
+ }
173
+ /** What a list says beside a card whose turn is a person's, or which a person just sent back. */
174
+ interface CardAttention {
175
+ /** A person sent it back and nobody has picked it up again (`BoardCardQuery.returned`). */
176
+ returned: boolean;
177
+ /** Its latest comment, for a card waiting for a person or returned; `null` for the rest. */
178
+ lastComment: CardLastComment | null;
179
+ }
180
+ //#endregion
181
+ export { readEntryRules as C, readChecklist as S, EntryRule as _, IssueActorReference as a, UnmetEntryRule as b, isOutsideWriter as c, KANBO_TOOL_CLI_EQUIVALENTS as d, KANBO_TOOL_NAMES as f, ENTRY_RULE_DESCRIPTIONS as g, ENTRY_RULES as h, IssueActorKind as i, normalizeCommentAuthorKind as l, KanboToolName as m, CardAttention as n, IssueView as o, KanboToolCliEquivalent as p, CardLastComment as r, IssueWriterProvenance as s, BoardApproval as t, toIssueView as u, EntryRuleFacts as v, serializeEntryRules as w, evaluateEntryRules as x, UNMET_ENTRY_RULE_REASONS as y };
@@ -9,6 +9,13 @@ interface IssueStatusStore {
9
9
  /** Every status, in no particular order. */
10
10
  listAll: () => Promise<IssueStatus[]>;
11
11
  findById: (statusId: string) => Promise<IssueStatus | null>;
12
+ /**
13
+ * The status, read inside a write so no other writer can put a card in it or
14
+ * delete it until this transaction ends: `select … for update` on Postgres.
15
+ * A SQLite write already holds the whole file (`begin immediate`), so there
16
+ * it is `findById`.
17
+ */
18
+ lockById: (statusId: string) => Promise<IssueStatus | null>;
12
19
  /** The status, only when it belongs to the given workspace. */
13
20
  findInWorkspace: (workspaceId: string, statusId: string) => Promise<IssueStatus | null>;
14
21
  countByWorkspace: (workspaceId: string) => Promise<number>;
@@ -41,6 +48,17 @@ interface IssueRowStore {
41
48
  listAll: () => Promise<Issue[]>;
42
49
  /** Issues of one workspace, in no particular order. */
43
50
  listByWorkspace: (workspaceId: string) => Promise<Issue[]>;
51
+ /**
52
+ * The cards of one workspace a query picks, in board order, one page of them,
53
+ * and how many it picks in all — in one statement. The filters are conjoined;
54
+ * a filter left out does not narrow.
55
+ */
56
+ listPage: (query: BoardCardQuery) => Promise<BoardCardPage>;
57
+ /**
58
+ * How many cards of the workspace each milestone holds, per column — one
59
+ * statement, cards with no milestone left out. What a sprint list counts.
60
+ */
61
+ countByMilestone: (workspaceId: string) => Promise<BoardMilestoneCardCount[]>;
44
62
  /** Issues with the given ids, in no particular order. Missing ids are simply absent. */
45
63
  listByIds: (issueIds: string[]) => Promise<Issue[]>;
46
64
  findById: (issueId: string) => Promise<Issue | null>;
@@ -78,10 +96,66 @@ interface IssueRowStore {
78
96
  }, updatedAt: number) => Promise<number>;
79
97
  delete: (issueId: string) => Promise<void>;
80
98
  }
99
+ /**
100
+ * Which cards of a workspace `listPage` reads, and which page of them.
101
+ *
102
+ * Every filter is optional and they all apply together. Text is matched
103
+ * ignoring case — ASCII case on SQLite, where `like` folds nothing else; every
104
+ * letter on Postgres (`ilike`).
105
+ */
106
+ interface BoardCardQuery {
107
+ workspaceId: string;
108
+ /** Cards in any of these columns. An empty list picks nothing. */
109
+ statusIds?: readonly string[];
110
+ /** `true`: only cards waiting for a person; `false`: only cards waiting for no one. */
111
+ waitingForPerson?: boolean;
112
+ /** Only the cards directly under this card. */
113
+ parentIssueId?: string;
114
+ /** `true`: only cards with a run going on now; `false`: only cards with none. */
115
+ hasActiveRun?: boolean;
116
+ /**
117
+ * `true`: only cards a person sent back that nobody has picked up again —
118
+ * the card's latest decision (`system.approved` or `system.returned`
119
+ * comment, the one `readApproval` reads) is a return, it is not waiting for a
120
+ * person, and no run is going on on it. `false`: every other card.
121
+ */
122
+ returned?: boolean;
123
+ /** Cards whose key, title or description contains this text. */
124
+ text?: string;
125
+ /** Cards changed at or after this moment, in unix seconds. */
126
+ updatedSince?: number;
127
+ /** Cards carrying every one of these labels. */
128
+ labels?: readonly string[];
129
+ /** Cards of any of these priorities. An empty list picks nothing. */
130
+ priorities?: readonly Issue['priority'][];
131
+ /** How many picked cards to skip, in board order. */
132
+ offset?: number;
133
+ /** How many picked cards to return at most; every one when absent. */
134
+ limit?: number;
135
+ }
136
+ /** One page of the cards a `BoardCardQuery` picks. */
137
+ interface BoardCardPage {
138
+ /** The page, in board order. */
139
+ cards: Issue[];
140
+ /** How many cards the query picks in all, before `offset` and `limit`. */
141
+ total: number;
142
+ }
143
+ /** How many cards of one milestone sit in one column. */
144
+ interface BoardMilestoneCardCount {
145
+ milestoneId: string;
146
+ statusId: string | null;
147
+ count: number;
148
+ }
81
149
  /** Comments on an issue. */
82
150
  interface IssueCommentStore {
83
151
  /** Comments of one issue, oldest first. */
84
152
  listByIssue: (issueId: string) => Promise<IssueComment[]>;
153
+ /**
154
+ * The latest comment of each of these issues — among those of `authorKinds`
155
+ * when given — in one statement, in no particular order. An issue with none
156
+ * is absent. "Latest" is the order `listByIssue` lists them in, its last.
157
+ */
158
+ listLatestByIssues: (issueIds: readonly string[], authorKinds?: readonly IssueComment['authorKind'][]) => Promise<IssueComment[]>;
85
159
  findById: (commentId: string) => Promise<IssueComment | null>;
86
160
  /**
87
161
  * The comment on that issue carrying `dedupeKey`, or `null` — one read of the
@@ -277,5 +351,4 @@ interface BoardStore {
277
351
  }) => Promise<T>;
278
352
  }
279
353
  //#endregion
280
- export { BoardStore as a, IssueContextRefStore as c, IssuePullRequestStore as d, IssueRelationStore as f, KanbanMetaStore as g, IssueStatusStore as h, BoardRunProjectionScope as i, IssueFieldChangeStore as l, IssueRunStore as m, BoardLinkedPullRequest as n, BoardTransactionMode as o, IssueRowStore as p, BoardRunProjection as r, IssueCommentStore as s, BoardActiveRun as t, IssueMilestoneStore as u };
281
- //# sourceMappingURL=board-store-BvWn9xTB.d.mts.map
354
+ export { IssueRunStore as _, BoardMilestoneCardCount as a, BoardStore as c, IssueContextRefStore as d, IssueFieldChangeStore as f, IssueRowStore as g, IssueRelationStore as h, BoardLinkedPullRequest as i, BoardTransactionMode as l, IssuePullRequestStore as m, BoardCardPage as n, BoardRunProjection as o, IssueMilestoneStore as p, BoardCardQuery as r, BoardRunProjectionScope as s, BoardActiveRun as t, IssueCommentStore as u, IssueStatusStore as v, KanbanMetaStore as y };
@@ -9,6 +9,13 @@ interface IssueStatusStore {
9
9
  /** Every status, in no particular order. */
10
10
  listAll: () => Promise<IssueStatus[]>;
11
11
  findById: (statusId: string) => Promise<IssueStatus | null>;
12
+ /**
13
+ * The status, read inside a write so no other writer can put a card in it or
14
+ * delete it until this transaction ends: `select … for update` on Postgres.
15
+ * A SQLite write already holds the whole file (`begin immediate`), so there
16
+ * it is `findById`.
17
+ */
18
+ lockById: (statusId: string) => Promise<IssueStatus | null>;
12
19
  /** The status, only when it belongs to the given workspace. */
13
20
  findInWorkspace: (workspaceId: string, statusId: string) => Promise<IssueStatus | null>;
14
21
  countByWorkspace: (workspaceId: string) => Promise<number>;
@@ -41,6 +48,17 @@ interface IssueRowStore {
41
48
  listAll: () => Promise<Issue[]>;
42
49
  /** Issues of one workspace, in no particular order. */
43
50
  listByWorkspace: (workspaceId: string) => Promise<Issue[]>;
51
+ /**
52
+ * The cards of one workspace a query picks, in board order, one page of them,
53
+ * and how many it picks in all — in one statement. The filters are conjoined;
54
+ * a filter left out does not narrow.
55
+ */
56
+ listPage: (query: BoardCardQuery) => Promise<BoardCardPage>;
57
+ /**
58
+ * How many cards of the workspace each milestone holds, per column — one
59
+ * statement, cards with no milestone left out. What a sprint list counts.
60
+ */
61
+ countByMilestone: (workspaceId: string) => Promise<BoardMilestoneCardCount[]>;
44
62
  /** Issues with the given ids, in no particular order. Missing ids are simply absent. */
45
63
  listByIds: (issueIds: string[]) => Promise<Issue[]>;
46
64
  findById: (issueId: string) => Promise<Issue | null>;
@@ -78,10 +96,66 @@ interface IssueRowStore {
78
96
  }, updatedAt: number) => Promise<number>;
79
97
  delete: (issueId: string) => Promise<void>;
80
98
  }
99
+ /**
100
+ * Which cards of a workspace `listPage` reads, and which page of them.
101
+ *
102
+ * Every filter is optional and they all apply together. Text is matched
103
+ * ignoring case — ASCII case on SQLite, where `like` folds nothing else; every
104
+ * letter on Postgres (`ilike`).
105
+ */
106
+ interface BoardCardQuery {
107
+ workspaceId: string;
108
+ /** Cards in any of these columns. An empty list picks nothing. */
109
+ statusIds?: readonly string[];
110
+ /** `true`: only cards waiting for a person; `false`: only cards waiting for no one. */
111
+ waitingForPerson?: boolean;
112
+ /** Only the cards directly under this card. */
113
+ parentIssueId?: string;
114
+ /** `true`: only cards with a run going on now; `false`: only cards with none. */
115
+ hasActiveRun?: boolean;
116
+ /**
117
+ * `true`: only cards a person sent back that nobody has picked up again —
118
+ * the card's latest decision (`system.approved` or `system.returned`
119
+ * comment, the one `readApproval` reads) is a return, it is not waiting for a
120
+ * person, and no run is going on on it. `false`: every other card.
121
+ */
122
+ returned?: boolean;
123
+ /** Cards whose key, title or description contains this text. */
124
+ text?: string;
125
+ /** Cards changed at or after this moment, in unix seconds. */
126
+ updatedSince?: number;
127
+ /** Cards carrying every one of these labels. */
128
+ labels?: readonly string[];
129
+ /** Cards of any of these priorities. An empty list picks nothing. */
130
+ priorities?: readonly Issue['priority'][];
131
+ /** How many picked cards to skip, in board order. */
132
+ offset?: number;
133
+ /** How many picked cards to return at most; every one when absent. */
134
+ limit?: number;
135
+ }
136
+ /** One page of the cards a `BoardCardQuery` picks. */
137
+ interface BoardCardPage {
138
+ /** The page, in board order. */
139
+ cards: Issue[];
140
+ /** How many cards the query picks in all, before `offset` and `limit`. */
141
+ total: number;
142
+ }
143
+ /** How many cards of one milestone sit in one column. */
144
+ interface BoardMilestoneCardCount {
145
+ milestoneId: string;
146
+ statusId: string | null;
147
+ count: number;
148
+ }
81
149
  /** Comments on an issue. */
82
150
  interface IssueCommentStore {
83
151
  /** Comments of one issue, oldest first. */
84
152
  listByIssue: (issueId: string) => Promise<IssueComment[]>;
153
+ /**
154
+ * The latest comment of each of these issues — among those of `authorKinds`
155
+ * when given — in one statement, in no particular order. An issue with none
156
+ * is absent. "Latest" is the order `listByIssue` lists them in, its last.
157
+ */
158
+ listLatestByIssues: (issueIds: readonly string[], authorKinds?: readonly IssueComment['authorKind'][]) => Promise<IssueComment[]>;
85
159
  findById: (commentId: string) => Promise<IssueComment | null>;
86
160
  /**
87
161
  * The comment on that issue carrying `dedupeKey`, or `null` — one read of the
@@ -277,5 +351,4 @@ interface BoardStore {
277
351
  }) => Promise<T>;
278
352
  }
279
353
  //#endregion
280
- export { BoardStore as a, IssueContextRefStore as c, IssuePullRequestStore as d, IssueRelationStore as f, KanbanMetaStore as g, IssueStatusStore as h, BoardRunProjectionScope as i, IssueFieldChangeStore as l, IssueRunStore as m, BoardLinkedPullRequest as n, BoardTransactionMode as o, IssueRowStore as p, BoardRunProjection as r, IssueCommentStore as s, BoardActiveRun as t, IssueMilestoneStore as u };
281
- //# sourceMappingURL=board-store-BfszzAJB.d.cts.map
354
+ export { IssueRunStore as _, BoardMilestoneCardCount as a, BoardStore as c, IssueContextRefStore as d, IssueFieldChangeStore as f, IssueRowStore as g, IssueRelationStore as h, BoardLinkedPullRequest as i, BoardTransactionMode as l, IssuePullRequestStore as m, BoardCardPage as n, BoardRunProjection as o, IssueMilestoneStore as p, BoardCardQuery as r, BoardRunProjectionScope as s, BoardActiveRun as t, IssueCommentStore as u, IssueStatusStore as v, KanbanMetaStore as y };
@@ -1,11 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_manifest = require("./manifest-D9nCRtXM.cjs");
3
- const require_migrations_path = require("./migrations-path-CZxVOgeD.cjs");
2
+ const require_manifest = require("./manifest-QPoQl8tn.cjs");
3
+ const require_migrations_path = require("./migrations-path-DBCE17dT.cjs");
4
4
  exports.BINDING_FILE_PATH = require_manifest.BINDING_FILE_PATH;
5
5
  exports.BOARD_ACTOR = require_manifest.BOARD_ACTOR;
6
6
  exports.BOARD_ERROR_RESPONSES = require_manifest.BOARD_ERROR_RESPONSES;
7
7
  exports.BoardError = require_migrations_path.BoardError;
8
8
  exports.CAPABILITIES_MANIFEST_VERSION = require_manifest.CAPABILITIES_MANIFEST_VERSION;
9
+ exports.CARD_KEY_PATTERN = require_manifest.CARD_KEY_PATTERN;
10
+ exports.COLUMN_CATALOGUE = require_manifest.COLUMN_CATALOGUE;
11
+ exports.COLUMN_TEMPLATES = require_manifest.COLUMN_TEMPLATES;
12
+ exports.COLUMN_TEMPLATE_IDS = require_manifest.COLUMN_TEMPLATE_IDS;
9
13
  exports.DEFAULT_BOARD_FILE_PATH = require_manifest.DEFAULT_BOARD_FILE_PATH;
10
14
  exports.DEFAULT_STATUSES = require_manifest.DEFAULT_STATUSES;
11
15
  exports.ENTRY_RULES = require_manifest.ENTRY_RULES;
@@ -31,6 +35,7 @@ exports.formatPlainActivityValue = require_manifest.formatPlainActivityValue;
31
35
  exports.ignoreBoardFile = require_manifest.ignoreBoardFile;
32
36
  exports.isEmptyActivityValue = require_manifest.isEmptyActivityValue;
33
37
  exports.isOutsideWriter = require_manifest.isOutsideWriter;
38
+ exports.isValidCardKey = require_manifest.isValidCardKey;
34
39
  exports.maskDatabaseUrl = require_manifest.maskDatabaseUrl;
35
40
  exports.maskDatabaseUrls = require_manifest.maskDatabaseUrls;
36
41
  exports.nextIssueIdentity = require_manifest.nextIssueIdentity;
@@ -51,5 +56,6 @@ exports.readSystemCommentKind = require_manifest.readSystemCommentKind;
51
56
  exports.renderCapabilitiesMarkdown = require_manifest.renderCapabilitiesMarkdown;
52
57
  exports.runBoardWrite = require_manifest.runBoardWrite;
53
58
  exports.serializeEntryRules = require_manifest.serializeEntryRules;
59
+ exports.suggestCardKey = require_manifest.suggestCardKey;
54
60
  exports.toIssueView = require_manifest.toIssueView;
55
61
  exports.writeBinding = require_manifest.writeBinding;