@goodandready/dsh-kanban 0.2.2 → 0.2.7

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 CHANGED
@@ -21,6 +21,16 @@
21
21
  <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
22
22
  </p>
23
23
 
24
+ <table align="center">
25
+ <tr>
26
+ <td align="center">
27
+ ⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
28
+ <br><br>
29
+ 🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
24
34
  </div>
25
35
 
26
36
  ---
@@ -21,6 +21,16 @@
21
21
  <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
22
22
  </p>
23
23
 
24
+ <table align="center">
25
+ <tr>
26
+ <td align="center">
27
+ ⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
28
+ <br><br>
29
+ 🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
24
34
  </div>
25
35
 
26
36
  ---
@@ -21,6 +21,16 @@
21
21
  <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
22
22
  </p>
23
23
 
24
+ <table align="center">
25
+ <tr>
26
+ <td align="center">
27
+ ⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
28
+ <br><br>
29
+ 🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
24
34
  </div>
25
35
 
26
36
  ---
package/lib/board-tool.js CHANGED
@@ -367,3 +367,154 @@ export function boardCommentAddDefinition({ store }) {
367
367
  },
368
368
  }
369
369
  }
370
+
371
+ /** Определение board_decompose (#248) */
372
+ export function boardDecomposeDefinition({ store, gitea }) {
373
+ return {
374
+ name: 'board_decompose',
375
+ description: 'Decompose a task into child subtasks, converting the task into an Epic with linked progress tracking.',
376
+ parameters: {
377
+ subtasks: {
378
+ type: 'array',
379
+ items: { type: 'string' },
380
+ description: 'List of child subtask titles to create.',
381
+ required: true,
382
+ },
383
+ create_gitea_issues: {
384
+ type: 'boolean',
385
+ description: 'Whether to create linked Gitea issues for each subtask if repository is linked.',
386
+ },
387
+ task: {
388
+ type: 'string',
389
+ description: 'Task reference (`repo#number` or card id). Required if this session carries multiple tasks.',
390
+ },
391
+ },
392
+ output: {
393
+ schema: { type: 'string', description: 'Status and list of created subtasks.' },
394
+ render: (_args, value) => [{ type: 'text', text: String(value) }],
395
+ },
396
+ async execute({ subtasks, create_gitea_issues, task: ref }, exec) {
397
+ const picked = pickTask(store, exec, ref)
398
+ if (picked.error !== undefined) return picked.error
399
+ const task = picked.task
400
+ if (!Array.isArray(subtasks) || subtasks.length === 0) {
401
+ return 'At least one subtask title is required in `subtasks`.'
402
+ }
403
+
404
+ store.updateTask(task.id, { isEpic: true })
405
+ const created = []
406
+ const giteaClient = typeof gitea === 'function' ? gitea() : gitea
407
+ const createGitea = create_gitea_issues === true && Boolean(task.owner && task.repo && giteaClient)
408
+
409
+ for (const item of subtasks) {
410
+ const title = (typeof item === 'string' ? item : item?.title || '').trim()
411
+ if (!title) continue
412
+ const body = typeof item === 'object' && item?.body ? String(item.body).trim() : ''
413
+ let issueNumber = null
414
+ let issueUrl = null
415
+
416
+ if (createGitea) {
417
+ try {
418
+ const issueBody = body ? `${body}\n\nParent epic: #${task.issueNumber || task.id}` : `Parent epic: #${task.issueNumber || task.id}`
419
+ const giteaIssue = await giteaClient.createIssue({
420
+ owner: task.owner,
421
+ repo: task.repo,
422
+ title,
423
+ body: issueBody,
424
+ })
425
+ if (giteaIssue?.number) {
426
+ issueNumber = giteaIssue.number
427
+ issueUrl = giteaIssue.html_url || null
428
+ }
429
+ } catch {}
430
+ }
431
+
432
+ const sub = store.createTask({
433
+ board: task.board || 'main',
434
+ column: 'backlog',
435
+ title,
436
+ body,
437
+ parentId: task.id,
438
+ owner: task.owner || null,
439
+ repo: task.repo || null,
440
+ issueNumber,
441
+ issueUrl,
442
+ workspaceId: task.workspaceId || '',
443
+ })
444
+ created.push(sub)
445
+ }
446
+
447
+ store.addTransition({
448
+ taskId: task.id,
449
+ fromCol: task.column,
450
+ toCol: task.column,
451
+ source: 'tool',
452
+ detail: `agent decomposed task into ${created.length} subtasks`,
453
+ })
454
+
455
+ return `Decomposed task "${task.title}" into ${created.length} subtasks:\n`
456
+ + created.map((s, i) => `${i + 1}. [${s.id.slice(0, 8)}] ${s.title}${s.issueNumber ? ` (#${s.issueNumber})` : ''}`).join('\n')
457
+ },
458
+ }
459
+ }
460
+
461
+ /** Определение board_checklist_item (#248) */
462
+ export function boardChecklistItemDefinition({ store }) {
463
+ return {
464
+ name: 'board_checklist_item',
465
+ description: 'Toggle or check a specific DoD checklist item by text or index without replacing the entire list.',
466
+ parameters: {
467
+ text: {
468
+ type: 'string',
469
+ description: 'Text substring matching the checklist item to toggle.',
470
+ },
471
+ index: {
472
+ type: 'number',
473
+ description: 'Zero-based index of the checklist item to toggle.',
474
+ },
475
+ done: {
476
+ type: 'boolean',
477
+ description: 'Explicit completion state (true to mark done, false to unmark). If omitted, toggles state.',
478
+ },
479
+ task: {
480
+ type: 'string',
481
+ description: 'Task reference (`repo#number` or card id). Required if this session carries multiple tasks.',
482
+ },
483
+ },
484
+ output: {
485
+ schema: { type: 'string', description: 'Status confirmation.' },
486
+ render: (_args, value) => [{ type: 'text', text: String(value) }],
487
+ },
488
+ async execute({ text, index, done, task: ref }, exec) {
489
+ const picked = pickTask(store, exec, ref)
490
+ if (picked.error !== undefined) return picked.error
491
+ const task = picked.task
492
+ const checklist = Array.isArray(task.checklist) ? [...task.checklist] : []
493
+ let targetIndex = -1
494
+
495
+ if (typeof index === 'number' && index >= 0 && index < checklist.length) {
496
+ targetIndex = index
497
+ } else if (typeof text === 'string' && text.trim()) {
498
+ const q = text.trim().toLowerCase()
499
+ targetIndex = checklist.findIndex((item) => {
500
+ const t = typeof item === 'string' ? item : item?.text
501
+ return typeof t === 'string' && t.toLowerCase().includes(q)
502
+ })
503
+ }
504
+
505
+ if (targetIndex === -1) {
506
+ return `Checklist item matching index=${index} / text="${text}" not found.`
507
+ }
508
+
509
+ const item = checklist[targetIndex]
510
+ const oldDone = typeof item === 'object' && item !== null ? Boolean(item.done) : false
511
+ const newDone = done !== undefined ? Boolean(done) : !oldDone
512
+ const title = typeof item === 'string' ? item : (item?.text || '')
513
+ checklist[targetIndex] = { text: title, done: newDone }
514
+
515
+ store.updateChecklist(task.id, checklist)
516
+ return `Checklist item #${targetIndex + 1} "${title}" marked as ${newDone ? 'DONE' : 'PENDING'}.`
517
+ },
518
+ }
519
+ }
520
+