kanbo-cli 0.1.2 → 0.2.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.
- package/README.md +1 -0
- package/dist/cli.cjs +10553 -8430
- package/dist/cli.cjs.map +1 -1
- package/dist/{dist-Cd6wg8pp.cjs → dist-BsCwQFEl.cjs} +48 -1
- package/dist/dist-BsCwQFEl.cjs.map +1 -0
- package/dist/lib/index.cjs +1 -259
- package/dist/lib/index.d.cts +15 -95
- package/dist/lib/index.d.mts +15 -95
- package/dist/lib/index.mjs +2 -256
- package/dist/lib/{manifest-NRhhhOkC.mjs → manifest-BwpC-4BO.mjs} +1217 -297
- package/dist/lib/manifest-BwpC-4BO.mjs.map +1 -0
- package/dist/lib/{manifest-CmiuDqH-.cjs → manifest-RDyoGrVR.cjs} +1300 -380
- package/dist/lib/manifest-RDyoGrVR.cjs.map +1 -0
- package/dist/lib/mcp/index.cjs +1 -1
- package/dist/lib/mcp/index.mjs +1 -1
- package/dist/lib/migrations-path-CZxVOgeD.cjs.map +1 -1
- package/dist/lib/migrations-path-Ciwan537.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/dist-Cd6wg8pp.cjs.map +0 -1
- package/dist/lib/index.cjs.map +0 -1
- package/dist/lib/index.mjs.map +0 -1
- package/dist/lib/manifest-CmiuDqH-.cjs.map +0 -1
- package/dist/lib/manifest-NRhhhOkC.mjs.map +0 -1
package/dist/lib/index.mjs
CHANGED
|
@@ -1,257 +1,3 @@
|
|
|
1
|
-
import { $ as commentAuthorReference, A as maskDatabaseUrls, B as readEntryRules, C as cardDisplayTitle, D as readProjectBinding, E as readBinding, F as ENTRY_RULES, G as normalizeStatusName, H as readChangeSeq, I as ENTRY_RULE_DESCRIPTIONS, J as BOARD_ACTOR, K as TRACKED_FIELDS, L as UNMET_ENTRY_RULE_REASONS, M as formatExternalSessionRef, N as parseExternalSessionRef, O as writeBinding, P as pullRequestFactKeys, Q as activityToken, R as evaluateEntryRules, S as renderCapabilitiesMarkdown, T as ignoreBoardFile, U as runBoardWrite, V as serializeEntryRules, W as DEFAULT_STATUSES, X as activityKindOrder, Y as currentUnixSeconds, Z as activityText, a as BOARD_ERROR_RESPONSES, at as isOutsideWriter, ct as formatIssueId, dt as nextIssueOrder, et as formatActivityIssueReference, ft as readIssuePrefix, i as KANBO_TOOL_NAMES, it as readSystemCommentKind, j as createBoardOps, k as maskDatabaseUrl, lt as nextIssueIdentity, n as buildCapabilitiesManifest, nt as isEmptyActivityValue, ot as normalizeCommentAuthorKind, q as diffFieldChanges, r as KANBO_TOOL_CLI_EQUIVALENTS, rt as parseActivityStringArray, st as toIssueView, t as CAPABILITIES_MANIFEST_VERSION, tt as formatPlainActivityValue, ut as nextIssueNumber, w as BINDING_FILE_PATH, x as DEFAULT_BOARD_FILE_PATH, z as readChecklist } from "./manifest-
|
|
1
|
+
import { $ as commentAuthorReference, A as maskDatabaseUrls, B as readEntryRules, C as cardDisplayTitle, D as readProjectBinding, E as readBinding, F as ENTRY_RULES, G as normalizeStatusName, H as readChangeSeq, I as ENTRY_RULE_DESCRIPTIONS, J as BOARD_ACTOR, K as TRACKED_FIELDS, L as UNMET_ENTRY_RULE_REASONS, M as formatExternalSessionRef, N as parseExternalSessionRef, O as writeBinding, P as pullRequestFactKeys, Q as activityToken, R as evaluateEntryRules, S as renderCapabilitiesMarkdown, T as ignoreBoardFile, U as runBoardWrite, V as serializeEntryRules, W as DEFAULT_STATUSES, X as activityKindOrder, Y as currentUnixSeconds, Z as activityText, a as BOARD_ERROR_RESPONSES, at as isOutsideWriter, ct as formatIssueId, dt as nextIssueOrder, et as formatActivityIssueReference, ft as readIssuePrefix, i as KANBO_TOOL_NAMES, it as readSystemCommentKind, j as createBoardOps, k as maskDatabaseUrl, lt as nextIssueIdentity, n as buildCapabilitiesManifest, nt as isEmptyActivityValue, ot as normalizeCommentAuthorKind, q as diffFieldChanges, r as KANBO_TOOL_CLI_EQUIVALENTS, rt as parseActivityStringArray, st as toIssueView, t as CAPABILITIES_MANIFEST_VERSION, tt as formatPlainActivityValue, ut as nextIssueNumber, w as BINDING_FILE_PATH, x as DEFAULT_BOARD_FILE_PATH, z as readChecklist } from "./manifest-BwpC-4BO.mjs";
|
|
2
2
|
import { a as BoardError } from "./migrations-path-Ciwan537.mjs";
|
|
3
|
-
|
|
4
|
-
/** Nothing copied — what a move that had nothing to move reports. */
|
|
5
|
-
function emptyCopyCounts() {
|
|
6
|
-
return {
|
|
7
|
-
statuses: 0,
|
|
8
|
-
milestones: 0,
|
|
9
|
-
issues: 0,
|
|
10
|
-
comments: 0,
|
|
11
|
-
runs: 0,
|
|
12
|
-
relations: 0,
|
|
13
|
-
fieldChanges: 0,
|
|
14
|
-
contextRefs: 0,
|
|
15
|
-
pullRequests: 0
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Everything the workspace has on this board, read in one pass before a single
|
|
20
|
-
* row is written anywhere.
|
|
21
|
-
*
|
|
22
|
-
* Links are collected by id because an edge is listed by both of the cards it
|
|
23
|
-
* touches, and one reaching a card outside this workspace is left behind rather
|
|
24
|
-
* than copied: the target holds this workspace's board and nothing else, so the
|
|
25
|
-
* far end of that edge is not there to point at.
|
|
26
|
-
*/
|
|
27
|
-
async function readBoardSlice(from, workspaceId) {
|
|
28
|
-
const warnings = [];
|
|
29
|
-
const statuses = await from.statuses.listByWorkspace(workspaceId);
|
|
30
|
-
const milestones = await from.milestones.listByWorkspace(workspaceId);
|
|
31
|
-
const issues = orderParentsFirst(await from.issues.listByWorkspace(workspaceId));
|
|
32
|
-
const issueIds = new Set(issues.map((issue) => issue.id));
|
|
33
|
-
const comments = [];
|
|
34
|
-
const runs = [];
|
|
35
|
-
const relations = /* @__PURE__ */ new Map();
|
|
36
|
-
const fieldChanges = [];
|
|
37
|
-
const pullRequests = [];
|
|
38
|
-
const skippedRelations = /* @__PURE__ */ new Set();
|
|
39
|
-
for (const issue of issues) {
|
|
40
|
-
comments.push(...await from.comments.listByIssue(issue.id));
|
|
41
|
-
runs.push(...await from.runs.listByIssue(issue.id));
|
|
42
|
-
for (const relation of await from.relations.listByIssue(issue.id)) {
|
|
43
|
-
if (issueIds.has(relation.sourceIssueId) && issueIds.has(relation.targetIssueId)) {
|
|
44
|
-
relations.set(relation.id, relation);
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
skippedRelations.add(relation.id);
|
|
48
|
-
}
|
|
49
|
-
fieldChanges.push(...await from.fieldChanges.listByIssue(issue.id));
|
|
50
|
-
pullRequests.push(...await from.pullRequests.listByIssue(issue.id));
|
|
51
|
-
}
|
|
52
|
-
if (skippedRelations.size > 0) warnings.push(`${skippedRelations.size} links to cards in other workspaces were not moved`);
|
|
53
|
-
return {
|
|
54
|
-
statuses,
|
|
55
|
-
milestones,
|
|
56
|
-
issues,
|
|
57
|
-
comments,
|
|
58
|
-
runs,
|
|
59
|
-
relations: [...relations.values()],
|
|
60
|
-
fieldChanges,
|
|
61
|
-
pullRequests,
|
|
62
|
-
warnings
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Write a slice into the target board, in one transaction, and say what was
|
|
67
|
-
* written and what a person should know about it — the slice's own warnings
|
|
68
|
-
* first.
|
|
69
|
-
*
|
|
70
|
-
* It is idempotent by id. A row the target already has is left exactly as it
|
|
71
|
-
* is and counted as already present, so a copy interrupted half-way is finished
|
|
72
|
-
* by running it again, and a second run of a finished one copies nothing. A
|
|
73
|
-
* clash that is not the same row — a column name or a card number the target
|
|
74
|
-
* holds under another id — refuses the whole copy before its first insert
|
|
75
|
-
* (`board_copy_conflict`, ruling 3-55).
|
|
76
|
-
*/
|
|
77
|
-
async function writeBoardSlice(to, slice, workspaceId) {
|
|
78
|
-
const warnings = [...slice.warnings];
|
|
79
|
-
const copied = emptyCopyCounts();
|
|
80
|
-
let alreadyPresent = 0;
|
|
81
|
-
await to.transaction(async (tx) => {
|
|
82
|
-
await assertTheCopyFits(tx, slice, workspaceId);
|
|
83
|
-
for (const status of slice.statuses) {
|
|
84
|
-
if (await tx.statuses.findById(status.id)) {
|
|
85
|
-
alreadyPresent += 1;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
await tx.statuses.create(status);
|
|
89
|
-
copied.statuses += 1;
|
|
90
|
-
}
|
|
91
|
-
for (const milestone of slice.milestones) {
|
|
92
|
-
if (await tx.milestones.findById(milestone.id)) {
|
|
93
|
-
alreadyPresent += 1;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
await tx.milestones.create(milestone);
|
|
97
|
-
copied.milestones += 1;
|
|
98
|
-
}
|
|
99
|
-
const movingIssueIds = new Set(slice.issues.map((issue) => issue.id));
|
|
100
|
-
for (const issue of slice.issues) {
|
|
101
|
-
if (await tx.issues.findById(issue.id)) {
|
|
102
|
-
alreadyPresent += 1;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
const parentIsReachable = issue.parentIssueId === null || movingIssueIds.has(issue.parentIssueId) || await tx.issues.findById(issue.parentIssueId) !== null;
|
|
106
|
-
if (!parentIsReachable) warnings.push(`${issue.id} lost its parent card, which is not on this board`);
|
|
107
|
-
const { contextRefs, ...row } = issue;
|
|
108
|
-
await tx.issues.create({
|
|
109
|
-
...row,
|
|
110
|
-
parentIssueId: parentIsReachable ? issue.parentIssueId : null
|
|
111
|
-
});
|
|
112
|
-
copied.issues += 1;
|
|
113
|
-
if (contextRefs !== "[]") {
|
|
114
|
-
await tx.contextRefs.replace(issue.id, contextRefs, issue.updatedAt);
|
|
115
|
-
copied.contextRefs += 1;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
for (const comment of slice.comments) {
|
|
119
|
-
if (await tx.comments.findById(comment.id)) {
|
|
120
|
-
alreadyPresent += 1;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
await tx.comments.create(comment);
|
|
124
|
-
copied.comments += 1;
|
|
125
|
-
}
|
|
126
|
-
for (const run of slice.runs) {
|
|
127
|
-
if (await tx.runs.findById(run.id)) {
|
|
128
|
-
alreadyPresent += 1;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
await tx.runs.create(run);
|
|
132
|
-
copied.runs += 1;
|
|
133
|
-
}
|
|
134
|
-
for (const relation of slice.relations) {
|
|
135
|
-
if (await tx.relations.findById(relation.id)) {
|
|
136
|
-
alreadyPresent += 1;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
await tx.relations.create(relation);
|
|
140
|
-
copied.relations += 1;
|
|
141
|
-
}
|
|
142
|
-
const changesByIssue = /* @__PURE__ */ new Map();
|
|
143
|
-
for (const change of slice.fieldChanges) changesByIssue.set(change.issueId, [...changesByIssue.get(change.issueId) ?? [], change]);
|
|
144
|
-
for (const [issueId, changes] of changesByIssue) {
|
|
145
|
-
const present = new Set((await tx.fieldChanges.listByIssue(issueId)).map((change) => change.id));
|
|
146
|
-
for (const change of changes) {
|
|
147
|
-
if (present.has(change.id)) {
|
|
148
|
-
alreadyPresent += 1;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
await tx.fieldChanges.create(change);
|
|
152
|
-
copied.fieldChanges += 1;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const linksByIssue = /* @__PURE__ */ new Map();
|
|
156
|
-
for (const link of slice.pullRequests) linksByIssue.set(link.issueId, [...linksByIssue.get(link.issueId) ?? [], link]);
|
|
157
|
-
for (const [issueId, links] of linksByIssue) {
|
|
158
|
-
const present = new Set((await tx.pullRequests.listByIssue(issueId)).map((link) => link.id));
|
|
159
|
-
for (const link of links) {
|
|
160
|
-
if (present.has(link.id) || (await tx.pullRequests.link(link)).id !== link.id) {
|
|
161
|
-
alreadyPresent += 1;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
copied.pullRequests += 1;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
await tx.meta.bumpRevision("board", currentUnixSeconds());
|
|
168
|
-
});
|
|
169
|
-
if (alreadyPresent > 0) warnings.push(`${alreadyPresent} rows were already present on the target board and were left as they are`);
|
|
170
|
-
return {
|
|
171
|
-
copied,
|
|
172
|
-
warnings
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Empty the slice off the board it was read from, in a transaction of its own.
|
|
177
|
-
*
|
|
178
|
-
* Only the cards, the columns and the milestones are named: comments, runs,
|
|
179
|
-
* links, pull request links and the field-change history hang off a card by a
|
|
180
|
-
* foreign key that cascades on both engines, so deleting the card takes them
|
|
181
|
-
* with it.
|
|
182
|
-
*/
|
|
183
|
-
async function deleteBoardSlice(from, slice) {
|
|
184
|
-
await from.transaction(async (tx) => {
|
|
185
|
-
for (const issue of slice.issues) await tx.issues.delete(issue.id);
|
|
186
|
-
for (const milestone of slice.milestones) await tx.milestones.delete(milestone.id);
|
|
187
|
-
for (const status of slice.statuses) await tx.statuses.delete(status.id);
|
|
188
|
-
await tx.meta.bumpRevision("board", currentUnixSeconds());
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* What a copy would collide with on the target, said before a row is written.
|
|
193
|
-
*
|
|
194
|
-
* The board carries two uniqueness rules that a *second* board of the same
|
|
195
|
-
* workspace can break: a column's name within its workspace, and a card's
|
|
196
|
-
* number within it. In practice it is the columns that collide: a standard set
|
|
197
|
-
* created a second time carries the same names under new ids, while a card's id
|
|
198
|
-
* is its key (`BST-001`), so two generations of card *number* 1 are two rows
|
|
199
|
-
* with one id — which the copy treats as already present rather than as a
|
|
200
|
-
* clash. The number check covers the case where they are not: a workspace whose
|
|
201
|
-
* identifier changed between the two generations numbers from 1 again under
|
|
202
|
-
* different ids.
|
|
203
|
-
*
|
|
204
|
-
* A row the target already has *by id* is not a collision — it is the
|
|
205
|
-
* idempotent half of the copy, which is why the holder's id is compared rather
|
|
206
|
-
* than merely counted. Milestones need no check: neither engine makes their
|
|
207
|
-
* titles unique.
|
|
208
|
-
*/
|
|
209
|
-
async function assertTheCopyFits(tx, slice, workspaceId) {
|
|
210
|
-
const conflicts = [];
|
|
211
|
-
const numbers = new Map((await tx.issues.listByWorkspace(workspaceId)).map((issue) => [issue.number, issue.id]));
|
|
212
|
-
for (const issue of slice.issues) {
|
|
213
|
-
const holder = numbers.get(issue.number);
|
|
214
|
-
if (holder !== void 0 && holder !== issue.id) conflicts.push({
|
|
215
|
-
kind: "card",
|
|
216
|
-
value: issue.number,
|
|
217
|
-
heldBy: holder
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
const names = new Map((await tx.statuses.listByWorkspace(workspaceId)).map((status) => [status.name, status.id]));
|
|
221
|
-
for (const status of slice.statuses) {
|
|
222
|
-
const holder = names.get(status.name);
|
|
223
|
-
if (holder !== void 0 && holder !== status.id) conflicts.push({
|
|
224
|
-
kind: "column",
|
|
225
|
-
value: status.name,
|
|
226
|
-
heldBy: holder
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
if (conflicts.length > 0) throw new BoardError("board_copy_conflict", {
|
|
230
|
-
workspaceId,
|
|
231
|
-
conflicts
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* The same cards, ordered so that a parent is always written before its
|
|
236
|
-
* children. The column is a foreign key onto the same table, so any other order
|
|
237
|
-
* writes a card pointing at one that is not there yet.
|
|
238
|
-
*/
|
|
239
|
-
function orderParentsFirst(issues) {
|
|
240
|
-
const byId = new Map(issues.map((issue) => [issue.id, issue]));
|
|
241
|
-
const ordered = [];
|
|
242
|
-
const placed = /* @__PURE__ */ new Set();
|
|
243
|
-
function place(issue, ancestors) {
|
|
244
|
-
if (placed.has(issue.id) || ancestors.has(issue.id)) return;
|
|
245
|
-
ancestors.add(issue.id);
|
|
246
|
-
const parent = issue.parentIssueId === null ? void 0 : byId.get(issue.parentIssueId);
|
|
247
|
-
if (parent) place(parent, ancestors);
|
|
248
|
-
placed.add(issue.id);
|
|
249
|
-
ordered.push(issue);
|
|
250
|
-
}
|
|
251
|
-
for (const issue of issues) place(issue, /* @__PURE__ */ new Set());
|
|
252
|
-
return ordered;
|
|
253
|
-
}
|
|
254
|
-
//#endregion
|
|
255
|
-
export { BINDING_FILE_PATH, BOARD_ACTOR, BOARD_ERROR_RESPONSES, BoardError, CAPABILITIES_MANIFEST_VERSION, DEFAULT_BOARD_FILE_PATH, DEFAULT_STATUSES, ENTRY_RULES, ENTRY_RULE_DESCRIPTIONS, KANBO_TOOL_CLI_EQUIVALENTS, KANBO_TOOL_NAMES, TRACKED_FIELDS, UNMET_ENTRY_RULE_REASONS, 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 };
|
|
256
|
-
|
|
257
|
-
//# sourceMappingURL=index.mjs.map
|
|
3
|
+
export { BINDING_FILE_PATH, BOARD_ACTOR, BOARD_ERROR_RESPONSES, BoardError, CAPABILITIES_MANIFEST_VERSION, DEFAULT_BOARD_FILE_PATH, DEFAULT_STATUSES, ENTRY_RULES, ENTRY_RULE_DESCRIPTIONS, KANBO_TOOL_CLI_EQUIVALENTS, KANBO_TOOL_NAMES, TRACKED_FIELDS, UNMET_ENTRY_RULE_REASONS, activityKindOrder, activityText, activityToken, buildCapabilitiesManifest, cardDisplayTitle, commentAuthorReference, createBoardOps, currentUnixSeconds, diffFieldChanges, evaluateEntryRules, formatActivityIssueReference, formatExternalSessionRef, formatIssueId, formatPlainActivityValue, ignoreBoardFile, isEmptyActivityValue, isOutsideWriter, maskDatabaseUrl, maskDatabaseUrls, nextIssueIdentity, nextIssueNumber, nextIssueOrder, normalizeCommentAuthorKind, normalizeStatusName, parseActivityStringArray, parseExternalSessionRef, pullRequestFactKeys, readBinding, readChangeSeq, readChecklist, readEntryRules, readIssuePrefix, readProjectBinding, readSystemCommentKind, renderCapabilitiesMarkdown, runBoardWrite, serializeEntryRules, toIssueView, writeBinding };
|