kanban-lite 1.0.9 → 1.0.13
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/dist/cli.js +1015 -407
- package/dist/extension.js +897 -347
- package/dist/mcp-server.js +550 -189
- package/dist/sdk/index.cjs +1223 -0
- package/dist/sdk/index.mjs +1168 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
- package/dist/sdk/sdk/index.d.ts +4 -3
- package/dist/sdk/sdk/migration.d.ts +6 -0
- package/dist/sdk/sdk/types.d.ts +3 -5
- package/dist/sdk/shared/config.d.ts +23 -10
- package/dist/sdk/shared/types.d.ts +18 -4
- package/dist/standalone-webview/index.js +27 -27
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +831 -328
- package/dist/webview/index.js +45 -45
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/package.json +4 -3
- package/src/cli/index.ts +217 -95
- package/src/extension/KanbanPanel.ts +49 -22
- package/src/mcp-server/index.ts +138 -62
- package/src/sdk/KanbanSDK.ts +283 -77
- package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
- package/src/sdk/__tests__/migration.test.ts +269 -0
- package/src/sdk/__tests__/multi-board.test.ts +449 -0
- package/src/sdk/index.ts +4 -3
- package/src/sdk/migration.ts +52 -0
- package/src/sdk/types.ts +3 -6
- package/src/shared/config.ts +144 -22
- package/src/shared/types.ts +14 -5
- package/src/standalone/__tests__/server.integration.test.ts +38 -37
- package/src/standalone/server.ts +239 -21
- package/src/webview/App.tsx +17 -7
- package/src/webview/components/Toolbar.tsx +99 -3
- package/src/webview/store/index.ts +11 -3
- package/.kanban/backlog/1-test1.md +0 -30
- package/.kanban.json +0 -42
package/src/cli/index.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as path from 'path'
|
|
2
2
|
import * as fs from 'fs/promises'
|
|
3
3
|
import { KanbanSDK } from '../sdk/KanbanSDK'
|
|
4
|
-
import type { Feature,
|
|
4
|
+
import type { Feature, Priority } from '../shared/types'
|
|
5
5
|
import { loadWebhooks, createWebhook, deleteWebhook } from '../standalone/webhooks'
|
|
6
6
|
import { readConfig, writeConfig, configToSettings, settingsToConfig } from '../shared/config'
|
|
7
7
|
import type { CardDisplaySettings } from '../shared/types'
|
|
8
8
|
|
|
9
|
-
const VALID_STATUSES: FeatureStatus[] = ['backlog', 'todo', 'in-progress', 'review', 'done']
|
|
10
9
|
const VALID_PRIORITIES: Priority[] = ['critical', 'high', 'medium', 'low']
|
|
11
10
|
|
|
12
11
|
// --- Arg parsing ---
|
|
@@ -36,6 +35,19 @@ function parseArgs(argv: string[]): { command: string; positional: string[]; fla
|
|
|
36
35
|
return { command, positional, flags }
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
// --- Board ID helper ---
|
|
39
|
+
|
|
40
|
+
function getBoardId(flags: Record<string, string | true>): string | undefined {
|
|
41
|
+
return typeof flags.board === 'string' ? flags.board : undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- Dynamic status validation ---
|
|
45
|
+
|
|
46
|
+
async function getValidStatuses(sdk: KanbanSDK, boardId?: string): Promise<string[]> {
|
|
47
|
+
const columns = await sdk.listColumns(boardId)
|
|
48
|
+
return columns.map(c => c.id)
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
// --- Resolve features directory ---
|
|
40
52
|
|
|
41
53
|
async function findWorkspaceRoot(startDir: string): Promise<string> {
|
|
@@ -129,6 +141,9 @@ function formatCardDetail(c: Feature): string {
|
|
|
129
141
|
` Modified: ${c.modified}`,
|
|
130
142
|
` File: ${c.filePath}`,
|
|
131
143
|
]
|
|
144
|
+
if (c.boardId) {
|
|
145
|
+
lines.push(` Board: ${c.boardId}`)
|
|
146
|
+
}
|
|
132
147
|
if (c.completedAt) {
|
|
133
148
|
lines.push(` Completed: ${c.completedAt}`)
|
|
134
149
|
}
|
|
@@ -143,7 +158,8 @@ function formatCardDetail(c: Feature): string {
|
|
|
143
158
|
// --- Card Commands ---
|
|
144
159
|
|
|
145
160
|
async function cmdList(sdk: KanbanSDK, flags: Record<string, string | true>): Promise<void> {
|
|
146
|
-
|
|
161
|
+
const boardId = getBoardId(flags)
|
|
162
|
+
let cards = await sdk.listCards(undefined, boardId)
|
|
147
163
|
|
|
148
164
|
if (typeof flags.status === 'string') {
|
|
149
165
|
cards = cards.filter(c => c.status === flags.status)
|
|
@@ -183,10 +199,11 @@ async function cmdShow(sdk: KanbanSDK, positional: string[], flags: Record<strin
|
|
|
183
199
|
process.exit(1)
|
|
184
200
|
}
|
|
185
201
|
|
|
186
|
-
const
|
|
202
|
+
const boardId = getBoardId(flags)
|
|
203
|
+
const card = await sdk.getCard(cardId, boardId)
|
|
187
204
|
if (!card) {
|
|
188
205
|
// Try partial match
|
|
189
|
-
const all = await sdk.listCards()
|
|
206
|
+
const all = await sdk.listCards(undefined, boardId)
|
|
190
207
|
const matches = all.filter(c => c.id.includes(cardId))
|
|
191
208
|
if (matches.length === 1) {
|
|
192
209
|
if (flags.json) {
|
|
@@ -218,10 +235,14 @@ async function cmdAdd(sdk: KanbanSDK, flags: Record<string, string | true>): Pro
|
|
|
218
235
|
process.exit(1)
|
|
219
236
|
}
|
|
220
237
|
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
238
|
+
const boardId = getBoardId(flags)
|
|
239
|
+
const status = typeof flags.status === 'string' ? flags.status : undefined
|
|
240
|
+
if (status) {
|
|
241
|
+
const validStatuses = await getValidStatuses(sdk, boardId)
|
|
242
|
+
if (!validStatuses.includes(status)) {
|
|
243
|
+
console.error(red(`Invalid status: ${status}. Must be one of: ${validStatuses.join(', ')}`))
|
|
244
|
+
process.exit(1)
|
|
245
|
+
}
|
|
225
246
|
}
|
|
226
247
|
|
|
227
248
|
const priority = (typeof flags.priority === 'string' ? flags.priority : 'medium') as Priority
|
|
@@ -237,7 +258,7 @@ async function cmdAdd(sdk: KanbanSDK, flags: Record<string, string | true>): Pro
|
|
|
237
258
|
|
|
238
259
|
const content = `# ${title}${body ? '\n\n' + body : ''}`
|
|
239
260
|
|
|
240
|
-
const card = await sdk.createCard({ content, status, priority, assignee, dueDate, labels })
|
|
261
|
+
const card = await sdk.createCard({ content, status, priority, assignee, dueDate, labels, boardId })
|
|
241
262
|
|
|
242
263
|
if (flags.json) {
|
|
243
264
|
console.log(JSON.stringify(card, null, 2))
|
|
@@ -250,37 +271,25 @@ async function cmdAdd(sdk: KanbanSDK, flags: Record<string, string | true>): Pro
|
|
|
250
271
|
|
|
251
272
|
async function cmdMove(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
252
273
|
const cardId = positional[0]
|
|
253
|
-
const newStatus = positional[1]
|
|
274
|
+
const newStatus = positional[1]
|
|
254
275
|
|
|
255
276
|
if (!cardId || !newStatus) {
|
|
256
277
|
console.error(red('Usage: kl move <id> <status> [--position <n>]'))
|
|
257
278
|
process.exit(1)
|
|
258
279
|
}
|
|
259
|
-
|
|
260
|
-
|
|
280
|
+
|
|
281
|
+
const boardId = getBoardId(flags)
|
|
282
|
+
const validStatuses = await getValidStatuses(sdk, boardId)
|
|
283
|
+
if (!validStatuses.includes(newStatus)) {
|
|
284
|
+
console.error(red(`Invalid status: ${newStatus}. Must be one of: ${validStatuses.join(', ')}`))
|
|
261
285
|
process.exit(1)
|
|
262
286
|
}
|
|
263
287
|
|
|
264
288
|
// Support partial ID match
|
|
265
|
-
|
|
266
|
-
const card = await sdk.getCard(cardId)
|
|
267
|
-
if (!card) {
|
|
268
|
-
const all = await sdk.listCards()
|
|
269
|
-
const matches = all.filter(c => c.id.includes(cardId))
|
|
270
|
-
if (matches.length === 1) {
|
|
271
|
-
resolvedId = matches[0].id
|
|
272
|
-
} else if (matches.length > 1) {
|
|
273
|
-
console.error(red(`Multiple cards match "${cardId}":`))
|
|
274
|
-
for (const m of matches) console.error(` ${m.id}`)
|
|
275
|
-
process.exit(1)
|
|
276
|
-
} else {
|
|
277
|
-
console.error(red(`Card not found: ${cardId}`))
|
|
278
|
-
process.exit(1)
|
|
279
|
-
}
|
|
280
|
-
}
|
|
289
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
281
290
|
|
|
282
291
|
const position = typeof flags.position === 'string' ? parseInt(flags.position, 10) : undefined
|
|
283
|
-
const updated = await sdk.moveCard(resolvedId, newStatus, position)
|
|
292
|
+
const updated = await sdk.moveCard(resolvedId, newStatus, position, boardId)
|
|
284
293
|
console.log(green(`Moved ${updated.id} → ${colorStatus(newStatus)}`))
|
|
285
294
|
}
|
|
286
295
|
|
|
@@ -291,31 +300,19 @@ async function cmdEdit(sdk: KanbanSDK, positional: string[], flags: Record<strin
|
|
|
291
300
|
process.exit(1)
|
|
292
301
|
}
|
|
293
302
|
|
|
303
|
+
const boardId = getBoardId(flags)
|
|
304
|
+
|
|
294
305
|
// Support partial ID match
|
|
295
|
-
|
|
296
|
-
const card = await sdk.getCard(cardId)
|
|
297
|
-
if (!card) {
|
|
298
|
-
const all = await sdk.listCards()
|
|
299
|
-
const matches = all.filter(c => c.id.includes(cardId))
|
|
300
|
-
if (matches.length === 1) {
|
|
301
|
-
resolvedId = matches[0].id
|
|
302
|
-
} else if (matches.length > 1) {
|
|
303
|
-
console.error(red(`Multiple cards match "${cardId}":`))
|
|
304
|
-
for (const m of matches) console.error(` ${m.id}`)
|
|
305
|
-
process.exit(1)
|
|
306
|
-
} else {
|
|
307
|
-
console.error(red(`Card not found: ${cardId}`))
|
|
308
|
-
process.exit(1)
|
|
309
|
-
}
|
|
310
|
-
}
|
|
306
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
311
307
|
|
|
312
308
|
const updates: Partial<Feature> = {}
|
|
313
309
|
if (typeof flags.status === 'string') {
|
|
314
|
-
|
|
315
|
-
|
|
310
|
+
const validStatuses = await getValidStatuses(sdk, boardId)
|
|
311
|
+
if (!validStatuses.includes(flags.status)) {
|
|
312
|
+
console.error(red(`Invalid status: ${flags.status}. Must be one of: ${validStatuses.join(', ')}`))
|
|
316
313
|
process.exit(1)
|
|
317
314
|
}
|
|
318
|
-
updates.status = flags.status
|
|
315
|
+
updates.status = flags.status
|
|
319
316
|
}
|
|
320
317
|
if (typeof flags.priority === 'string') {
|
|
321
318
|
if (!VALID_PRIORITIES.includes(flags.priority as Priority)) {
|
|
@@ -333,36 +330,23 @@ async function cmdEdit(sdk: KanbanSDK, positional: string[], flags: Record<strin
|
|
|
333
330
|
process.exit(1)
|
|
334
331
|
}
|
|
335
332
|
|
|
336
|
-
const updated = await sdk.updateCard(resolvedId, updates)
|
|
333
|
+
const updated = await sdk.updateCard(resolvedId, updates, boardId)
|
|
337
334
|
console.log(green(`Updated: ${updated.id}`))
|
|
338
335
|
}
|
|
339
336
|
|
|
340
|
-
async function cmdDelete(sdk: KanbanSDK, positional: string[]): Promise<void> {
|
|
337
|
+
async function cmdDelete(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
341
338
|
const cardId = positional[0]
|
|
342
339
|
if (!cardId) {
|
|
343
340
|
console.error(red('Usage: kl delete <id>'))
|
|
344
341
|
process.exit(1)
|
|
345
342
|
}
|
|
346
343
|
|
|
344
|
+
const boardId = getBoardId(flags)
|
|
345
|
+
|
|
347
346
|
// Support partial ID match
|
|
348
|
-
|
|
349
|
-
const card = await sdk.getCard(cardId)
|
|
350
|
-
if (!card) {
|
|
351
|
-
const all = await sdk.listCards()
|
|
352
|
-
const matches = all.filter(c => c.id.includes(cardId))
|
|
353
|
-
if (matches.length === 1) {
|
|
354
|
-
resolvedId = matches[0].id
|
|
355
|
-
} else if (matches.length > 1) {
|
|
356
|
-
console.error(red(`Multiple cards match "${cardId}":`))
|
|
357
|
-
for (const m of matches) console.error(` ${m.id}`)
|
|
358
|
-
process.exit(1)
|
|
359
|
-
} else {
|
|
360
|
-
console.error(red(`Card not found: ${cardId}`))
|
|
361
|
-
process.exit(1)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
347
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
364
348
|
|
|
365
|
-
await sdk.deleteCard(resolvedId)
|
|
349
|
+
await sdk.deleteCard(resolvedId, boardId)
|
|
366
350
|
console.log(green(`Deleted: ${resolvedId}`))
|
|
367
351
|
}
|
|
368
352
|
|
|
@@ -371,16 +355,131 @@ async function cmdInit(sdk: KanbanSDK): Promise<void> {
|
|
|
371
355
|
console.log(green(`Initialized: ${sdk.featuresDir}`))
|
|
372
356
|
}
|
|
373
357
|
|
|
358
|
+
// --- Board Commands ---
|
|
359
|
+
|
|
360
|
+
async function cmdBoards(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>, workspaceRoot: string): Promise<void> {
|
|
361
|
+
const subcommand = positional[0] || 'list'
|
|
362
|
+
|
|
363
|
+
switch (subcommand) {
|
|
364
|
+
case 'list': {
|
|
365
|
+
const boards = sdk.listBoards()
|
|
366
|
+
if (flags.json) {
|
|
367
|
+
console.log(JSON.stringify(boards, null, 2))
|
|
368
|
+
} else if (boards.length === 0) {
|
|
369
|
+
console.log(dim(' No boards found.'))
|
|
370
|
+
} else {
|
|
371
|
+
console.log(` ${dim('ID'.padEnd(20))} ${dim('NAME'.padEnd(20))} ${dim('DESCRIPTION')}`)
|
|
372
|
+
console.log(dim(' ' + '-'.repeat(60)))
|
|
373
|
+
for (const b of boards) {
|
|
374
|
+
console.log(` ${bold(b.id.padEnd(20))} ${b.name.padEnd(20)} ${b.description || '-'}`)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
break
|
|
378
|
+
}
|
|
379
|
+
case 'add': {
|
|
380
|
+
const id = typeof flags.id === 'string' ? flags.id : ''
|
|
381
|
+
const name = typeof flags.name === 'string' ? flags.name : ''
|
|
382
|
+
if (!id || !name) {
|
|
383
|
+
console.error(red('Usage: kl boards add --id <id> --name <name> [--description <desc>]'))
|
|
384
|
+
process.exit(1)
|
|
385
|
+
}
|
|
386
|
+
const description = typeof flags.description === 'string' ? flags.description : undefined
|
|
387
|
+
const board = await sdk.createBoard(id, name, { description })
|
|
388
|
+
if (flags.json) {
|
|
389
|
+
console.log(JSON.stringify(board, null, 2))
|
|
390
|
+
} else {
|
|
391
|
+
console.log(green(`Created board: ${board.id} (${board.name})`))
|
|
392
|
+
}
|
|
393
|
+
break
|
|
394
|
+
}
|
|
395
|
+
case 'show': {
|
|
396
|
+
const boardId = positional[1]
|
|
397
|
+
if (!boardId) {
|
|
398
|
+
console.error(red('Usage: kl boards show <id>'))
|
|
399
|
+
process.exit(1)
|
|
400
|
+
}
|
|
401
|
+
const board = sdk.getBoard(boardId)
|
|
402
|
+
if (flags.json) {
|
|
403
|
+
console.log(JSON.stringify(board, null, 2))
|
|
404
|
+
} else {
|
|
405
|
+
console.log(`${bold(board.name)}`)
|
|
406
|
+
console.log(` ID: ${boardId}`)
|
|
407
|
+
if (board.description) console.log(` Description: ${board.description}`)
|
|
408
|
+
console.log(` Columns: ${board.columns.map(c => c.name).join(', ')}`)
|
|
409
|
+
console.log(` Next Card: ${board.nextCardId}`)
|
|
410
|
+
console.log(` Default: status=${board.defaultStatus}, priority=${board.defaultPriority}`)
|
|
411
|
+
}
|
|
412
|
+
break
|
|
413
|
+
}
|
|
414
|
+
case 'remove':
|
|
415
|
+
case 'rm': {
|
|
416
|
+
const boardId = positional[1]
|
|
417
|
+
if (!boardId) {
|
|
418
|
+
console.error(red('Usage: kl boards remove <id>'))
|
|
419
|
+
process.exit(1)
|
|
420
|
+
}
|
|
421
|
+
await sdk.deleteBoard(boardId)
|
|
422
|
+
console.log(green(`Removed board: ${boardId}`))
|
|
423
|
+
break
|
|
424
|
+
}
|
|
425
|
+
case 'default': {
|
|
426
|
+
const boardId = positional[1]
|
|
427
|
+
if (!boardId) {
|
|
428
|
+
const config = readConfig(workspaceRoot)
|
|
429
|
+
console.log(config.defaultBoard)
|
|
430
|
+
break
|
|
431
|
+
}
|
|
432
|
+
const config = readConfig(workspaceRoot)
|
|
433
|
+
if (!config.boards[boardId]) {
|
|
434
|
+
console.error(red(`Board not found: ${boardId}`))
|
|
435
|
+
process.exit(1)
|
|
436
|
+
}
|
|
437
|
+
config.defaultBoard = boardId
|
|
438
|
+
writeConfig(workspaceRoot, config)
|
|
439
|
+
console.log(green(`Default board set to: ${boardId}`))
|
|
440
|
+
break
|
|
441
|
+
}
|
|
442
|
+
default:
|
|
443
|
+
console.error(red(`Unknown boards subcommand: ${subcommand}`))
|
|
444
|
+
console.error('Available: list, add, show, remove, default')
|
|
445
|
+
process.exit(1)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// --- Transfer Command ---
|
|
450
|
+
|
|
451
|
+
async function cmdTransfer(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
452
|
+
const cardId = positional[0]
|
|
453
|
+
if (!cardId) {
|
|
454
|
+
console.error(red('Usage: kl transfer <card-id> --from <board> --to <board> [--status <status>]'))
|
|
455
|
+
process.exit(1)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const fromBoard = typeof flags.from === 'string' ? flags.from : undefined
|
|
459
|
+
const toBoard = typeof flags.to === 'string' ? flags.to : undefined
|
|
460
|
+
|
|
461
|
+
if (!fromBoard || !toBoard) {
|
|
462
|
+
console.error(red('Both --from and --to are required'))
|
|
463
|
+
process.exit(1)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const targetStatus = typeof flags.status === 'string' ? flags.status : undefined
|
|
467
|
+
const resolvedId = await resolveCardId(sdk, cardId, fromBoard)
|
|
468
|
+
const card = await sdk.transferCard(resolvedId, fromBoard, toBoard, targetStatus)
|
|
469
|
+
console.log(green(`Transferred ${card.id} from ${fromBoard} → ${toBoard} (${colorStatus(card.status)})`))
|
|
470
|
+
}
|
|
471
|
+
|
|
374
472
|
// --- Attachment Commands ---
|
|
375
473
|
|
|
376
474
|
async function cmdAttach(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
377
475
|
const subcommand = positional[0] || 'list'
|
|
378
476
|
const cardId = positional[1]
|
|
477
|
+
const boardId = getBoardId(flags)
|
|
379
478
|
|
|
380
479
|
if (subcommand !== 'list' && subcommand !== 'add' && subcommand !== 'rm' && subcommand !== 'remove') {
|
|
381
480
|
// If first positional looks like a card ID, treat it as "list <cardId>"
|
|
382
|
-
const resolvedId = await resolveCardId(sdk, subcommand)
|
|
383
|
-
const attachments = await sdk.listAttachments(resolvedId)
|
|
481
|
+
const resolvedId = await resolveCardId(sdk, subcommand, boardId)
|
|
482
|
+
const attachments = await sdk.listAttachments(resolvedId, boardId)
|
|
384
483
|
if (flags.json) {
|
|
385
484
|
console.log(JSON.stringify(attachments, null, 2))
|
|
386
485
|
} else if (attachments.length === 0) {
|
|
@@ -397,8 +496,8 @@ async function cmdAttach(sdk: KanbanSDK, positional: string[], flags: Record<str
|
|
|
397
496
|
console.error(red('Usage: kl attach list <card-id>'))
|
|
398
497
|
process.exit(1)
|
|
399
498
|
}
|
|
400
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
401
|
-
const attachments = await sdk.listAttachments(resolvedId)
|
|
499
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
500
|
+
const attachments = await sdk.listAttachments(resolvedId, boardId)
|
|
402
501
|
if (flags.json) {
|
|
403
502
|
console.log(JSON.stringify(attachments, null, 2))
|
|
404
503
|
} else if (attachments.length === 0) {
|
|
@@ -418,8 +517,8 @@ async function cmdAttach(sdk: KanbanSDK, positional: string[], flags: Record<str
|
|
|
418
517
|
console.error(red('Usage: kl attach add <card-id> <file-path>'))
|
|
419
518
|
process.exit(1)
|
|
420
519
|
}
|
|
421
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
422
|
-
const updated = await sdk.addAttachment(resolvedId, filePath)
|
|
520
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
521
|
+
const updated = await sdk.addAttachment(resolvedId, filePath, boardId)
|
|
423
522
|
console.log(green(`Attached to ${updated.id}: ${path.basename(filePath)}`))
|
|
424
523
|
break
|
|
425
524
|
}
|
|
@@ -434,19 +533,19 @@ async function cmdAttach(sdk: KanbanSDK, positional: string[], flags: Record<str
|
|
|
434
533
|
console.error(red('Usage: kl attach remove <card-id> <filename>'))
|
|
435
534
|
process.exit(1)
|
|
436
535
|
}
|
|
437
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
438
|
-
const updated = await sdk.removeAttachment(resolvedId, filename)
|
|
536
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
537
|
+
const updated = await sdk.removeAttachment(resolvedId, filename, boardId)
|
|
439
538
|
console.log(green(`Removed from ${updated.id}: ${filename}`))
|
|
440
539
|
break
|
|
441
540
|
}
|
|
442
541
|
}
|
|
443
542
|
}
|
|
444
543
|
|
|
445
|
-
async function resolveCardId(sdk: KanbanSDK, cardId: string): Promise<string> {
|
|
446
|
-
const card = await sdk.getCard(cardId)
|
|
544
|
+
async function resolveCardId(sdk: KanbanSDK, cardId: string, boardId?: string): Promise<string> {
|
|
545
|
+
const card = await sdk.getCard(cardId, boardId)
|
|
447
546
|
if (card) return cardId
|
|
448
547
|
|
|
449
|
-
const all = await sdk.listCards()
|
|
548
|
+
const all = await sdk.listCards(undefined, boardId)
|
|
450
549
|
const matches = all.filter(c => c.id.includes(cardId))
|
|
451
550
|
if (matches.length === 1) return matches[0].id
|
|
452
551
|
if (matches.length > 1) {
|
|
@@ -462,11 +561,12 @@ async function resolveCardId(sdk: KanbanSDK, cardId: string): Promise<string> {
|
|
|
462
561
|
|
|
463
562
|
async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
464
563
|
const subcommand = positional[0] || 'list'
|
|
564
|
+
const boardId = getBoardId(flags)
|
|
465
565
|
|
|
466
566
|
if (subcommand !== 'list' && subcommand !== 'add' && subcommand !== 'edit' && subcommand !== 'remove' && subcommand !== 'rm') {
|
|
467
567
|
// If first positional looks like a card ID, treat it as "list <cardId>"
|
|
468
|
-
const resolvedId = await resolveCardId(sdk, subcommand)
|
|
469
|
-
const comments = await sdk.listComments(resolvedId)
|
|
568
|
+
const resolvedId = await resolveCardId(sdk, subcommand, boardId)
|
|
569
|
+
const comments = await sdk.listComments(resolvedId, boardId)
|
|
470
570
|
if (flags.json) {
|
|
471
571
|
console.log(JSON.stringify(comments, null, 2))
|
|
472
572
|
} else if (comments.length === 0) {
|
|
@@ -489,8 +589,8 @@ async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
489
589
|
console.error(red('Usage: kl comment list <card-id>'))
|
|
490
590
|
process.exit(1)
|
|
491
591
|
}
|
|
492
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
493
|
-
const comments = await sdk.listComments(resolvedId)
|
|
592
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
593
|
+
const comments = await sdk.listComments(resolvedId, boardId)
|
|
494
594
|
if (flags.json) {
|
|
495
595
|
console.log(JSON.stringify(comments, null, 2))
|
|
496
596
|
} else if (comments.length === 0) {
|
|
@@ -519,8 +619,8 @@ async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
519
619
|
console.error(red('Error: --body is required'))
|
|
520
620
|
process.exit(1)
|
|
521
621
|
}
|
|
522
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
523
|
-
const card = await sdk.addComment(resolvedId, author, body)
|
|
622
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
623
|
+
const card = await sdk.addComment(resolvedId, author, body, boardId)
|
|
524
624
|
const added = card.comments[card.comments.length - 1]
|
|
525
625
|
if (flags.json) {
|
|
526
626
|
console.log(JSON.stringify(added, null, 2))
|
|
@@ -544,10 +644,10 @@ async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
544
644
|
console.error(red('Error: --body is required'))
|
|
545
645
|
process.exit(1)
|
|
546
646
|
}
|
|
547
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
548
|
-
await sdk.updateComment(resolvedId, commentId, body)
|
|
647
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
648
|
+
await sdk.updateComment(resolvedId, commentId, body, boardId)
|
|
549
649
|
if (flags.json) {
|
|
550
|
-
const comments = await sdk.listComments(resolvedId)
|
|
650
|
+
const comments = await sdk.listComments(resolvedId, boardId)
|
|
551
651
|
const updated = comments.find(c => c.id === commentId)
|
|
552
652
|
console.log(JSON.stringify(updated, null, 2))
|
|
553
653
|
} else {
|
|
@@ -566,8 +666,8 @@ async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
566
666
|
console.error(red('Usage: kl comment remove <card-id> <comment-id>'))
|
|
567
667
|
process.exit(1)
|
|
568
668
|
}
|
|
569
|
-
const resolvedId = await resolveCardId(sdk, cardId)
|
|
570
|
-
await sdk.deleteComment(resolvedId, commentId)
|
|
669
|
+
const resolvedId = await resolveCardId(sdk, cardId, boardId)
|
|
670
|
+
await sdk.deleteComment(resolvedId, commentId, boardId)
|
|
571
671
|
console.log(green(`Deleted comment ${commentId}`))
|
|
572
672
|
break
|
|
573
673
|
}
|
|
@@ -578,10 +678,11 @@ async function cmdComment(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
578
678
|
|
|
579
679
|
async function cmdColumns(sdk: KanbanSDK, positional: string[], flags: Record<string, string | true>): Promise<void> {
|
|
580
680
|
const subcommand = positional[0] || 'list'
|
|
681
|
+
const boardId = getBoardId(flags)
|
|
581
682
|
|
|
582
683
|
switch (subcommand) {
|
|
583
684
|
case 'list': {
|
|
584
|
-
const columns = await sdk.listColumns()
|
|
685
|
+
const columns = await sdk.listColumns(boardId)
|
|
585
686
|
if (flags.json) {
|
|
586
687
|
console.log(JSON.stringify(columns, null, 2))
|
|
587
688
|
} else {
|
|
@@ -601,7 +702,7 @@ async function cmdColumns(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
601
702
|
console.error(red('Usage: kl columns add --id <id> --name <name> [--color <hex>]'))
|
|
602
703
|
process.exit(1)
|
|
603
704
|
}
|
|
604
|
-
const columns = await sdk.addColumn({ id, name, color })
|
|
705
|
+
const columns = await sdk.addColumn({ id, name, color }, boardId)
|
|
605
706
|
console.log(green(`Added column: ${id} (${name})`))
|
|
606
707
|
if (flags.json) console.log(JSON.stringify(columns, null, 2))
|
|
607
708
|
break
|
|
@@ -619,7 +720,7 @@ async function cmdColumns(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
619
720
|
console.error(red('No updates specified. Use --name or --color'))
|
|
620
721
|
process.exit(1)
|
|
621
722
|
}
|
|
622
|
-
const columns = await sdk.updateColumn(columnId, updates)
|
|
723
|
+
const columns = await sdk.updateColumn(columnId, updates, boardId)
|
|
623
724
|
console.log(green(`Updated column: ${columnId}`))
|
|
624
725
|
if (flags.json) console.log(JSON.stringify(columns, null, 2))
|
|
625
726
|
break
|
|
@@ -631,7 +732,7 @@ async function cmdColumns(sdk: KanbanSDK, positional: string[], flags: Record<st
|
|
|
631
732
|
console.error(red('Usage: kl columns remove <id>'))
|
|
632
733
|
process.exit(1)
|
|
633
734
|
}
|
|
634
|
-
const columns = await sdk.removeColumn(columnId)
|
|
735
|
+
const columns = await sdk.removeColumn(columnId, boardId)
|
|
635
736
|
console.log(green(`Removed column: ${columnId}`))
|
|
636
737
|
if (flags.json) console.log(JSON.stringify(columns, null, 2))
|
|
637
738
|
break
|
|
@@ -821,6 +922,14 @@ ${bold('Card Commands:')}
|
|
|
821
922
|
edit <id> [--field value] Update card fields
|
|
822
923
|
delete <id> Delete a card
|
|
823
924
|
|
|
925
|
+
${bold('Board Commands:')}
|
|
926
|
+
boards List boards
|
|
927
|
+
boards add Create a board (--id, --name, --description)
|
|
928
|
+
boards show <id> Show board details
|
|
929
|
+
boards remove <id> Remove a board
|
|
930
|
+
boards default [id] Get or set the default board
|
|
931
|
+
transfer <id> Transfer a card (--from, --to, --status)
|
|
932
|
+
|
|
824
933
|
${bold('Attachment Commands:')}
|
|
825
934
|
attach <id> List attachments on a card
|
|
826
935
|
attach add <id> <path> Attach a file to a card
|
|
@@ -856,6 +965,7 @@ ${bold('Other:')}
|
|
|
856
965
|
|
|
857
966
|
${bold('Global Options:')}
|
|
858
967
|
--dir <path> Features directory (default: .kanban)
|
|
968
|
+
--board <id> Target board (default: default board)
|
|
859
969
|
--json Output as JSON
|
|
860
970
|
|
|
861
971
|
${bold('List Filters:')}
|
|
@@ -873,6 +983,11 @@ ${bold('Add/Edit Options:')}
|
|
|
873
983
|
--due <date> Due date
|
|
874
984
|
--label <l1,l2> Labels (comma-separated)
|
|
875
985
|
|
|
986
|
+
${bold('Transfer Options:')}
|
|
987
|
+
--from <board> Source board (required)
|
|
988
|
+
--to <board> Destination board (required)
|
|
989
|
+
--status <status> Target status in destination board
|
|
990
|
+
|
|
876
991
|
${bold('Webhook Options:')}
|
|
877
992
|
--url <url> Webhook target URL (required for add)
|
|
878
993
|
--events <e1,e2> Events to subscribe to (default: *)
|
|
@@ -928,7 +1043,14 @@ async function main(): Promise<void> {
|
|
|
928
1043
|
break
|
|
929
1044
|
case 'delete':
|
|
930
1045
|
case 'rm':
|
|
931
|
-
await cmdDelete(sdk, positional)
|
|
1046
|
+
await cmdDelete(sdk, positional, flags)
|
|
1047
|
+
break
|
|
1048
|
+
case 'boards':
|
|
1049
|
+
case 'board':
|
|
1050
|
+
await cmdBoards(sdk, positional, flags, workspaceRoot)
|
|
1051
|
+
break
|
|
1052
|
+
case 'transfer':
|
|
1053
|
+
await cmdTransfer(sdk, positional, flags)
|
|
932
1054
|
break
|
|
933
1055
|
case 'attach':
|
|
934
1056
|
await cmdAttach(sdk, positional, flags)
|