@goodandready/dsh-kanban 0.2.21 → 0.2.23
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/lib/board-tool.js +8 -5
- package/lib/client.js +45 -3
- package/lib/fractional-indexing.js +582 -0
- package/lib/git-provider.js +11 -2
- package/lib/github.js +10 -0
- package/lib/import.js +11 -10
- package/lib/index.js +33 -8
- package/lib/launcher.js +160 -53
- package/lib/order.js +20 -50
- package/lib/outbound.js +26 -10
- package/lib/progress-dump.js +16 -11
- package/lib/routes.js +41 -25
- package/lib/store.js +156 -6
- package/lib/sync.js +6 -5
- package/lib/updater.js +1 -1
- package/lib/workflow-bridge.js +44 -18
- package/lib/worktree.js +21 -10
- package/package.json +1 -1
package/lib/worktree.js
CHANGED
|
@@ -30,10 +30,11 @@ export async function defaultGitRunner(args, cwd) {
|
|
|
30
30
|
maxBuffer: 10 * 1024 * 1024,
|
|
31
31
|
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
32
32
|
})
|
|
33
|
-
return { stdout: stdout.trim(), stderr: stderr.trim(), code: 0 }
|
|
33
|
+
return { stdout: stdout.trim(), stderr: stderr.trim(), code: 0, exitCode: 0 }
|
|
34
34
|
} catch (err) {
|
|
35
35
|
const error = new Error(`git ${args.join(' ')} failed: ${err.stderr || err.message}`)
|
|
36
36
|
error.code = err.code || 1
|
|
37
|
+
error.exitCode = err.code || 1
|
|
37
38
|
error.stdout = err.stdout ? String(err.stdout).trim() : ''
|
|
38
39
|
error.stderr = err.stderr ? String(err.stderr).trim() : ''
|
|
39
40
|
throw error
|
|
@@ -264,7 +265,7 @@ export async function checkWorktreeDirty({ worktreePath, gitRunner = defaultGitR
|
|
|
264
265
|
.filter((line) => !isSubmoduleDrift(worktreePath, line))
|
|
265
266
|
return { dirty: files.length > 0, files }
|
|
266
267
|
} catch (error) {
|
|
267
|
-
return { dirty:
|
|
268
|
+
return { dirty: true, files: [], error: String(error?.message ?? error) }
|
|
268
269
|
}
|
|
269
270
|
}
|
|
270
271
|
|
|
@@ -289,6 +290,13 @@ export async function removeTaskWorktree({
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
const dirtyCheck = await checkWorktreeDirty({ worktreePath, gitRunner })
|
|
293
|
+
if (dirtyCheck.error) {
|
|
294
|
+
return {
|
|
295
|
+
removed: false,
|
|
296
|
+
error: 'worktree-status-error',
|
|
297
|
+
detail: dirtyCheck.error,
|
|
298
|
+
}
|
|
299
|
+
}
|
|
292
300
|
if (dirtyCheck.dirty && !force) {
|
|
293
301
|
return {
|
|
294
302
|
removed: false,
|
|
@@ -431,21 +439,21 @@ export async function getTaskDiff({ worktreePath, baseRef, gitRunner = defaultGi
|
|
|
431
439
|
let diffOut = ''
|
|
432
440
|
if (base) {
|
|
433
441
|
const res = await gitRunner(['diff', `${base}...HEAD`], worktreePath)
|
|
434
|
-
if (res
|
|
442
|
+
if ((res?.exitCode ?? res?.code ?? 0) === 0) {
|
|
435
443
|
diffOut = res.stdout
|
|
436
444
|
}
|
|
437
445
|
}
|
|
438
446
|
|
|
439
447
|
// Include uncommitted changes (unstaged + staged)
|
|
440
448
|
const unstaged = await gitRunner(['diff', 'HEAD'], worktreePath)
|
|
441
|
-
if (unstaged
|
|
449
|
+
if ((unstaged?.exitCode ?? unstaged?.code ?? 0) === 0 && unstaged.stdout.trim() !== '') {
|
|
442
450
|
diffOut = diffOut ? `${diffOut}\n\n# Uncommitted Changes:\n${unstaged.stdout}` : unstaged.stdout
|
|
443
451
|
}
|
|
444
452
|
|
|
445
453
|
// Fallback to plain git diff if base...HEAD produced no output
|
|
446
454
|
if (!diffOut.trim()) {
|
|
447
455
|
const fallback = await gitRunner(['diff'], worktreePath)
|
|
448
|
-
if (fallback
|
|
456
|
+
if ((fallback?.exitCode ?? fallback?.code ?? 0) === 0) diffOut = fallback.stdout
|
|
449
457
|
}
|
|
450
458
|
|
|
451
459
|
// Count affected files
|
|
@@ -481,10 +489,10 @@ export async function getTaskCommits({ worktreePath, baseRef, gitRunner = defaul
|
|
|
481
489
|
|
|
482
490
|
const range = base ? `${base}..HEAD` : '-n 20'
|
|
483
491
|
const logRes = await gitRunner(['log', range, '--pretty=format:%h%x09%an%x09%ad%x09%s', '--date=short'], worktreePath)
|
|
484
|
-
if (logRes
|
|
492
|
+
if ((logRes?.exitCode ?? logRes?.code ?? 0) !== 0 || !logRes.stdout.trim()) {
|
|
485
493
|
// Fallback to last 10 commits
|
|
486
494
|
const fallback = await gitRunner(['log', '-n', '10', '--pretty=format:%h%x09%an%x09%ad%x09%s', '--date=short'], worktreePath)
|
|
487
|
-
if (fallback
|
|
495
|
+
if ((fallback?.exitCode ?? fallback?.code ?? 0) !== 0 || !fallback.stdout.trim()) return []
|
|
488
496
|
return fallback.stdout.trim().split('\n').map(parseCommitLine).filter(Boolean)
|
|
489
497
|
}
|
|
490
498
|
|
|
@@ -521,6 +529,9 @@ export async function mergeTaskWorktree({
|
|
|
521
529
|
|
|
522
530
|
// 1. Verify worktree cleanliness
|
|
523
531
|
const dirtyCheck = await checkWorktreeDirty({ worktreePath: task.worktree, gitRunner })
|
|
532
|
+
if (dirtyCheck.error) {
|
|
533
|
+
return { success: false, merged: false, error: 'worktree-status-error', detail: dirtyCheck.error }
|
|
534
|
+
}
|
|
524
535
|
if (dirtyCheck.dirty) {
|
|
525
536
|
return { success: false, merged: false, error: 'worktree-dirty', files: dirtyCheck.files }
|
|
526
537
|
}
|
|
@@ -528,7 +539,7 @@ export async function mergeTaskWorktree({
|
|
|
528
539
|
// 2. Locate main repository root
|
|
529
540
|
let mainRepoDir = ''
|
|
530
541
|
const revParse = await gitRunner(['rev-parse', '--git-common-dir'], task.worktree)
|
|
531
|
-
if (revParse
|
|
542
|
+
if ((revParse?.exitCode ?? revParse?.code ?? 0) === 0 && revParse.stdout.trim()) {
|
|
532
543
|
const commonDir = revParse.stdout.trim()
|
|
533
544
|
mainRepoDir = commonDir.endsWith('/.git') ? commonDir.slice(0, -5) : commonDir
|
|
534
545
|
}
|
|
@@ -551,7 +562,7 @@ export async function mergeTaskWorktree({
|
|
|
551
562
|
const currentBranch = await gitRunner(['branch', '--show-current'], mainRepoDir)
|
|
552
563
|
if (currentBranch.stdout.trim() !== target) {
|
|
553
564
|
const checkoutRes = await gitRunner(['checkout', '--', target], mainRepoDir)
|
|
554
|
-
if (checkoutRes
|
|
565
|
+
if ((checkoutRes?.exitCode ?? checkoutRes?.code ?? 0) !== 0) {
|
|
555
566
|
return { success: false, merged: false, error: 'checkout-target-failed', detail: checkoutRes.stderr }
|
|
556
567
|
}
|
|
557
568
|
}
|
|
@@ -559,7 +570,7 @@ export async function mergeTaskWorktree({
|
|
|
559
570
|
// 5. Execute git merge --no-ff
|
|
560
571
|
const mergeMsg = `Merge branch '${task.branch}' for task ${task.id}`
|
|
561
572
|
const mergeRes = await gitRunner(['merge', '--no-ff', task.branch, '-m', mergeMsg], mainRepoDir)
|
|
562
|
-
if (mergeRes
|
|
573
|
+
if ((mergeRes?.exitCode ?? mergeRes?.code ?? 0) !== 0) {
|
|
563
574
|
// Roll back merge on conflict
|
|
564
575
|
await gitRunner(['merge', '--abort'], mainRepoDir)
|
|
565
576
|
return { success: false, merged: false, error: 'merge-conflict', detail: mergeRes.stderr || mergeRes.stdout }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-kanban",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.23",
|
|
4
4
|
"description": "Visual Kanban board for DeepSeek Harness: Gitea and GitHub-backed tasks, workflow columns, and a dedicated agent session per task.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|