@goodandready/dsh-kanban 0.2.2 → 0.2.8
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 +24 -0
- package/{docs/README.ru.md → README.ru.md} +10 -0
- package/{docs/README.zh.md → README.zh.md} +21 -0
- package/lib/board-tool.js +151 -0
- package/lib/client.js +1020 -763
- package/lib/commands.js +6 -11
- package/lib/config.js +31 -29
- package/lib/events.js +2 -2
- package/lib/filters.js +2 -2
- package/lib/gitea.js +15 -0
- package/lib/index.js +171 -142
- package/lib/power.js +1 -1
- package/lib/progress-dump.js +3 -3
- package/lib/routes.js +403 -5
- package/lib/store.js +59 -9
- package/lib/sync.js +26 -3
- package/lib/task-service.js +56 -0
- package/lib/updater.js +338 -0
- package/lib/worktree.js +1 -1
- package/package.json +7 -3
- package/docs/design/DESIGN.md +0 -94
- package/docs/plans/2026-08-26-dsh-kanban-0.1.md +0 -169
- package/docs/plans/2026-09-07-quality-acceptance-gateway.md +0 -25
- package/docs/plans/2026-09-08-concurrency-safety.md +0 -8
- package/docs/plans/2026-09-08-diff-viewer-merge.md +0 -7
- package/docs/plans/2026-09-08-session-worktree-stability.md +0 -33
- package/docs/plans/2026-09-10-issue-238-settings-scope.md +0 -13
package/lib/commands.js
CHANGED
|
@@ -23,28 +23,23 @@ const TABLE = {
|
|
|
23
23
|
project: {
|
|
24
24
|
'backlog': { stops: true },
|
|
25
25
|
'in-progress': {
|
|
26
|
-
instruction: '
|
|
26
|
+
instruction: 'Start or continue implementing this task following standard workflow.',
|
|
27
27
|
},
|
|
28
28
|
'review': {
|
|
29
|
-
instruction: '
|
|
30
|
-
+ 'и запроси проверку.',
|
|
29
|
+
instruction: 'Prepare work for review: mark pull request ready for review and request code review.',
|
|
31
30
|
},
|
|
32
31
|
'deploy': {
|
|
33
|
-
|
|
34
|
-
// прямо, иначе агент будет спрашивать разрешение, которое уже получил.
|
|
35
|
-
instruction: 'Влей pull request и выкати. Перенос карточки в Deploy — это и есть '
|
|
36
|
-
+ 'явное «ок» человека на deploy, отдельно спрашивать не нужно.',
|
|
32
|
+
instruction: 'Merge pull request and deploy. Moving card to Deploy serves as explicit OK for deploy; do not ask separately.',
|
|
37
33
|
},
|
|
38
34
|
'cleanup': {
|
|
39
|
-
instruction: '
|
|
40
|
-
+ 'запиши сделанное в issue.',
|
|
35
|
+
instruction: 'Clean up after task: delete branch in Gitea, worktree, and local branch; record summary in issue.',
|
|
41
36
|
},
|
|
42
37
|
'done': { humanOnly: true },
|
|
43
38
|
},
|
|
44
39
|
simple: {
|
|
45
40
|
'backlog': { stops: true },
|
|
46
|
-
'in-progress': { instruction: '
|
|
47
|
-
'review': { instruction: '
|
|
41
|
+
'in-progress': { instruction: 'Start or continue work on this task.' },
|
|
42
|
+
'review': { instruction: 'Present result and wait for human response.' },
|
|
48
43
|
'done': { humanOnly: true },
|
|
49
44
|
},
|
|
50
45
|
}
|
package/lib/config.js
CHANGED
|
@@ -30,7 +30,7 @@ export const COLUMN_ORDER = ['backlog', 'in-progress', 'review', 'deploy', 'clea
|
|
|
30
30
|
export const BOARD_KINDS = ['project', 'simple']
|
|
31
31
|
|
|
32
32
|
/** Вид доски по умолчанию: неизвестный вид откатывается сюда, а не в пустоту. */
|
|
33
|
-
|
|
33
|
+
const DEFAULT_KIND = 'project'
|
|
34
34
|
|
|
35
35
|
const COLUMNS_BY_KIND = {
|
|
36
36
|
project: COLUMN_ORDER,
|
|
@@ -49,8 +49,8 @@ export function normalizeKind(kind) {
|
|
|
49
49
|
|
|
50
50
|
/** Заводимые при первом запуске доски. Их ровно две, и обе нужны сразу. */
|
|
51
51
|
export const DEFAULT_BOARDS = [
|
|
52
|
-
{ id: 'main', title: '
|
|
53
|
-
{ id: 'simple', title: '
|
|
52
|
+
{ id: 'main', title: 'Project Board', kind: 'project' },
|
|
53
|
+
{ id: 'simple', title: 'Simple Board', kind: 'simple' },
|
|
54
54
|
]
|
|
55
55
|
|
|
56
56
|
/** Значения по умолчанию. Из них же собирается схема в `lib/index.js`. */
|
|
@@ -59,7 +59,7 @@ export const CONFIG_DEFAULTS = {
|
|
|
59
59
|
giteaTokenRef: 'GITEA_TOKEN',
|
|
60
60
|
defaultProjectRoot: '',
|
|
61
61
|
startPrompt: '',
|
|
62
|
-
replyInstruction: '
|
|
62
|
+
replyInstruction: '',
|
|
63
63
|
wipInProgress: 3,
|
|
64
64
|
wipReview: 0,
|
|
65
65
|
syncIntervalSec: 120,
|
|
@@ -86,31 +86,33 @@ export const CONFIG_DEFAULTS = {
|
|
|
86
86
|
|
|
87
87
|
/** Пояснения к полям; повторно используются схемой и карточкой настроек. */
|
|
88
88
|
export const CONFIG_HINTS = {
|
|
89
|
-
giteaUrl: '
|
|
90
|
-
giteaTokenRef: '
|
|
91
|
-
defaultProjectRoot: '
|
|
92
|
-
startPrompt: '
|
|
93
|
-
replyInstruction: '
|
|
94
|
-
wipInProgress: '
|
|
95
|
-
wipReview: '
|
|
96
|
-
syncIntervalSec: '
|
|
97
|
-
staleAfterMin: '
|
|
98
|
-
giteaOwner: '
|
|
99
|
-
columnNames: '
|
|
100
|
-
watchRepos: '
|
|
101
|
-
archiveAfterDays: '
|
|
102
|
-
boardToolEnabled: '
|
|
103
|
-
webhookSecretRef: '
|
|
104
|
-
pushToGitea: '
|
|
105
|
-
worktreeIsolation: '
|
|
106
|
-
worktreeRoot: '
|
|
107
|
-
cronEnabled: '
|
|
108
|
-
preventIdleSleep: '
|
|
109
|
-
progressDumpEnabled: '
|
|
110
|
-
permissionGateEnabled: '
|
|
111
|
-
sessionDefaultPermission: '
|
|
112
|
-
announceToAgent: '
|
|
113
|
-
|
|
89
|
+
giteaUrl: 'Gitea or Forgejo instance URL, e.g. https://gitea.example.com. Empty disables import.',
|
|
90
|
+
giteaTokenRef: 'Name of DSH credential holding the access token. Do not enter raw token.',
|
|
91
|
+
defaultProjectRoot: 'Directory containing project repositories. Empty uses harness working directory.',
|
|
92
|
+
startPrompt: 'Template for initial message to agent session. Empty uses built-in default.',
|
|
93
|
+
replyInstruction: 'Instruction appended to initial message (e.g. language/format constraints). Empty adds nothing.',
|
|
94
|
+
wipInProgress: 'Maximum task cards allowed in "In Progress" column. 0 means unlimited.',
|
|
95
|
+
wipReview: 'Maximum task cards allowed in "Review" column. 0 means unlimited.',
|
|
96
|
+
syncIntervalSec: 'Interval in seconds for polling Gitea synchronization. 0 disables polling.',
|
|
97
|
+
staleAfterMin: 'Minutes of inactivity before active task session is flagged stale. 0 disables.',
|
|
98
|
+
giteaOwner: 'Default Gitea organization/user to track. Auto-detected if single organization.',
|
|
99
|
+
columnNames: 'Custom column title mappings as "id=Name" pairs comma-separated.',
|
|
100
|
+
watchRepos: 'Filter sync to specific repository names comma-separated. Empty watches all repos.',
|
|
101
|
+
archiveAfterDays: 'Days completed tasks remain before automatic archiving. 0 disables.',
|
|
102
|
+
boardToolEnabled: 'Allow autonomous agent to advance cards via board_move tool.',
|
|
103
|
+
webhookSecretRef: 'Name of DSH credential holding Gitea webhook secret.',
|
|
104
|
+
pushToGitea: 'Automatically close corresponding Gitea issue when card moves to Done.',
|
|
105
|
+
worktreeIsolation: 'Isolate task sessions in dedicated git worktrees to prevent workspace collisions.',
|
|
106
|
+
worktreeRoot: 'Custom root directory for task worktrees. Empty defaults to $DSH_HOME/worktrees.',
|
|
107
|
+
cronEnabled: 'Enable background scheduled task execution via cron expressions.',
|
|
108
|
+
preventIdleSleep: 'Prevent host sleep while task agent sessions are running.',
|
|
109
|
+
progressDumpEnabled: 'Enable PROGRESSDUMP state checkpoints between agent sessions.',
|
|
110
|
+
permissionGateEnabled: 'Require human confirmation before launching elevated permission tasks.',
|
|
111
|
+
sessionDefaultPermission: 'Default permission tier for task sessions (read-only, workspace-write, full).',
|
|
112
|
+
announceToAgent: 'Post stage change announcement messages directly into task agent session.',
|
|
113
|
+
maxConcurrentSessions: 'Maximum number of simultaneous active agent sessions on the board.',
|
|
114
|
+
workspaceClaimBoundaries: 'Enforce workspace ownership boundaries to prevent cross-workspace task execution.',
|
|
115
|
+
autoSyncSessions: 'Automatically sync and attach live background agent sessions to cards.',
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/** Поле настроек с пределом числа карточек; задан не у всех колонок. */
|
package/lib/events.js
CHANGED
|
@@ -42,7 +42,7 @@ export class KanbanEventHub {
|
|
|
42
42
|
|
|
43
43
|
const cleanup = () => {
|
|
44
44
|
this.clients.delete(res)
|
|
45
|
-
try { res.end() } catch {}
|
|
45
|
+
try { res.end() } catch { /* client already disconnected */ }
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
if (typeof req.on === 'function') req.on('close', cleanup)
|
|
@@ -82,7 +82,7 @@ export class KanbanEventHub {
|
|
|
82
82
|
try {
|
|
83
83
|
client.write('event: close\ndata: {}\n\n')
|
|
84
84
|
client.end()
|
|
85
|
-
} catch {}
|
|
85
|
+
} catch { /* client already closed */ }
|
|
86
86
|
}
|
|
87
87
|
this.clients.clear()
|
|
88
88
|
}
|
package/lib/filters.js
CHANGED
|
@@ -41,7 +41,7 @@ export const NOBODY = '—'
|
|
|
41
41
|
export const NO_MILESTONE = '—'
|
|
42
42
|
|
|
43
43
|
/** Порядок приоритетов в отборе: срочное сверху. */
|
|
44
|
-
|
|
44
|
+
const PRIORITY_ORDER = ['critical', 'high', 'medium', 'low']
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Порядок известных пространств.
|
|
@@ -279,7 +279,7 @@ export function liveRank(task) {
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
/** Вес приоритета при подъёме: срочное впереди тихого. */
|
|
282
|
-
|
|
282
|
+
function priorityWeight(priority) {
|
|
283
283
|
const at = PRIORITY_ORDER.indexOf(priority)
|
|
284
284
|
return at === -1 ? PRIORITY_ORDER.length : at
|
|
285
285
|
}
|
package/lib/gitea.js
CHANGED
|
@@ -287,6 +287,21 @@ export function createGiteaClient({ getConfig, resolveToken, fetchImpl = fetch,
|
|
|
287
287
|
})
|
|
288
288
|
},
|
|
289
289
|
|
|
290
|
+
/** Создать Pull Request в репозитории. */
|
|
291
|
+
async createPullRequest({ owner, repo, title, body, head, base = 'main' }) {
|
|
292
|
+
const name = String(title ?? '').trim()
|
|
293
|
+
if (name === '') throw Object.assign(new Error('пустой заголовок'), { code: 'bad-title' })
|
|
294
|
+
const h = String(head ?? '').trim()
|
|
295
|
+
const b = String(base ?? 'main').trim()
|
|
296
|
+
if (!h) throw Object.assign(new Error('ветка head обязательна'), { code: 'bad-head' })
|
|
297
|
+
return request('POST', repoPath(owner, repo, '/pulls'), {
|
|
298
|
+
title: name,
|
|
299
|
+
body: String(body ?? ''),
|
|
300
|
+
head: h,
|
|
301
|
+
base: b,
|
|
302
|
+
})
|
|
303
|
+
},
|
|
304
|
+
|
|
290
305
|
/**
|
|
291
306
|
* Ветки репозитория: по ним видно, начата ли работа и сделан ли cleanup.
|
|
292
307
|
*
|