@goodandready/dsh-kanban 0.2.18 → 0.2.19
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/index.js +22 -5
- package/lib/intake.js +5 -5
- package/lib/store.js +0 -1
- package/lib/templates.js +29 -29
- package/lib/workflow-bridge.js +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -75,7 +75,7 @@ import {
|
|
|
75
75
|
boardDecomposeDefinition,
|
|
76
76
|
boardChecklistItemDefinition,
|
|
77
77
|
} from './board-tool.js'
|
|
78
|
-
import { verifySignature, parseEvent } from './webhook.js'
|
|
78
|
+
import { verifySignature, parseEvent, detectWebhookProvider } from './webhook.js'
|
|
79
79
|
import { planOutbound, planAssign, createOutbox } from './outbound.js'
|
|
80
80
|
import { computeNextRun } from './cron.js'
|
|
81
81
|
import { PowerInhibitor } from './power.js'
|
|
@@ -659,7 +659,10 @@ export function apply(ctx, config) {
|
|
|
659
659
|
ctx.effect(() => ctx.webServer.register({
|
|
660
660
|
kind: 'exact',
|
|
661
661
|
path: '/dsh-kanban/sessions',
|
|
662
|
-
handler: (req, res) => {
|
|
662
|
+
handler: (req, res) => {
|
|
663
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
664
|
+
reply(res, { sessions: liveSessions({ store, agents: ctx.agents }) })
|
|
665
|
+
},
|
|
663
666
|
}), 'dsh-kanban: live sessions')
|
|
664
667
|
|
|
665
668
|
ctx.effect(() => ctx.webServer.register({
|
|
@@ -697,7 +700,10 @@ export function apply(ctx, config) {
|
|
|
697
700
|
ctx.effect(() => ctx.webServer.register({
|
|
698
701
|
kind: 'exact',
|
|
699
702
|
path: '/dsh-kanban/archive',
|
|
700
|
-
handler: (req, res) => {
|
|
703
|
+
handler: (req, res) => {
|
|
704
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
705
|
+
reply(res, listArchive({ store }))
|
|
706
|
+
},
|
|
701
707
|
}), 'dsh-kanban: archive')
|
|
702
708
|
|
|
703
709
|
ctx.effect(() => ctx.webServer.register({
|
|
@@ -721,7 +727,7 @@ export function apply(ctx, config) {
|
|
|
721
727
|
if (req.method !== 'POST') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
722
728
|
try {
|
|
723
729
|
const raw = await readRaw(req)
|
|
724
|
-
const isGithub =
|
|
730
|
+
const isGithub = detectWebhookProvider(req.headers) === 'github'
|
|
725
731
|
const secretRef = isGithub
|
|
726
732
|
? (getConfig().githubWebhookSecretRef || getConfig().webhookSecretRef)
|
|
727
733
|
: getConfig().webhookSecretRef
|
|
@@ -804,6 +810,7 @@ export function apply(ctx, config) {
|
|
|
804
810
|
kind: 'exact',
|
|
805
811
|
path: '/dsh-kanban/board',
|
|
806
812
|
handler: (req, res) => {
|
|
813
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
807
814
|
const url = urlOf(req)
|
|
808
815
|
reply(res, buildBoard({
|
|
809
816
|
store,
|
|
@@ -849,6 +856,7 @@ export function apply(ctx, config) {
|
|
|
849
856
|
kind: 'exact',
|
|
850
857
|
path: '/dsh-kanban/metrics',
|
|
851
858
|
handler: (req, res) => {
|
|
859
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
852
860
|
const url = urlOf(req)
|
|
853
861
|
const days = Number(url.searchParams.get('staleDays'))
|
|
854
862
|
reply(res, boardMetrics({
|
|
@@ -864,13 +872,17 @@ export function apply(ctx, config) {
|
|
|
864
872
|
path: '/dsh-kanban/whoami',
|
|
865
873
|
// Пустой логин — законный ответ: Gitea может быть не настроен или не
|
|
866
874
|
// отвечать. Браузер по нему просто не рисует «Мои», а не показывает ноль.
|
|
867
|
-
handler: async (req, res) => {
|
|
875
|
+
handler: async (req, res) => {
|
|
876
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
877
|
+
reply(res, { login: await whoAmI() })
|
|
878
|
+
},
|
|
868
879
|
}), 'dsh-kanban: whoami')
|
|
869
880
|
|
|
870
881
|
ctx.effect(() => ctx.webServer.register({
|
|
871
882
|
kind: 'exact',
|
|
872
883
|
path: '/dsh-kanban/export.csv',
|
|
873
884
|
handler: async (req, res) => {
|
|
885
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
874
886
|
const u = urlOf(req)
|
|
875
887
|
const board = u.searchParams.get('board') || 'main'
|
|
876
888
|
const csv = exportTasksCsv({ store, board })
|
|
@@ -902,6 +914,7 @@ export function apply(ctx, config) {
|
|
|
902
914
|
kind: 'exact',
|
|
903
915
|
path: '/dsh-kanban/templates',
|
|
904
916
|
handler: (req, res) => {
|
|
917
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
905
918
|
const u = new URL(req.url, 'http://localhost')
|
|
906
919
|
const tid = u.searchParams.get('id')
|
|
907
920
|
if (tid) {
|
|
@@ -1317,6 +1330,7 @@ export function apply(ctx, config) {
|
|
|
1317
1330
|
kind: 'exact',
|
|
1318
1331
|
path: '/dsh-kanban/gitea/repos',
|
|
1319
1332
|
handler: async (req, res) => {
|
|
1333
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
1320
1334
|
const url = urlOf(req)
|
|
1321
1335
|
reply(res, await searchRepos({ gitea: git, query: url.searchParams.get('q') || '' }))
|
|
1322
1336
|
},
|
|
@@ -1326,6 +1340,7 @@ export function apply(ctx, config) {
|
|
|
1326
1340
|
kind: 'exact',
|
|
1327
1341
|
path: '/dsh-kanban/gitea/issues',
|
|
1328
1342
|
handler: async (req, res) => {
|
|
1343
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
1329
1344
|
const url = urlOf(req)
|
|
1330
1345
|
reply(res, await listImportable({
|
|
1331
1346
|
gitea: git,
|
|
@@ -1363,6 +1378,7 @@ export function apply(ctx, config) {
|
|
|
1363
1378
|
kind: 'exact',
|
|
1364
1379
|
path: '/dsh-kanban/models',
|
|
1365
1380
|
handler: async (req, res) => {
|
|
1381
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
1366
1382
|
const current = ctx.get('agentDefaultModel')?.currentSelection?.() ?? null
|
|
1367
1383
|
const llmService = ctx.get('llm')
|
|
1368
1384
|
let providers = []
|
|
@@ -1391,6 +1407,7 @@ export function apply(ctx, config) {
|
|
|
1391
1407
|
kind: 'prefix',
|
|
1392
1408
|
path: '/dsh-kanban/session',
|
|
1393
1409
|
handler: (req, res) => {
|
|
1410
|
+
if (req.method !== 'GET') { writeJson(res, 405, { error: 'method-not-allowed' }); return }
|
|
1394
1411
|
const m = /^\/dsh-kanban\/session\/([^/]+)\/task\/?$/.exec(urlOf(req).pathname)
|
|
1395
1412
|
if (m === null) { writeJson(res, 404, { error: 'not-found' }); return }
|
|
1396
1413
|
reply(res, taskBySession({ store, sessionId: decodeURIComponent(m[1]), agents: ctx.agents }))
|
package/lib/intake.js
CHANGED
|
@@ -49,18 +49,18 @@ export function watchedRepos({ config, owner }) {
|
|
|
49
49
|
*/
|
|
50
50
|
export function shouldTake({ store, owner, repo, issue }) {
|
|
51
51
|
const number = issue?.number
|
|
52
|
-
if (typeof number !== 'number') return { take: false, why: '
|
|
52
|
+
if (typeof number !== 'number') return { take: false, why: 'missing-number' }
|
|
53
53
|
// Pull request в Gitea — тоже issue, но работой на доске он не является:
|
|
54
54
|
// он ход работы, а не сама работа.
|
|
55
55
|
if (issue?.pull_request !== undefined && issue?.pull_request !== null) {
|
|
56
|
-
return { take: false, why: '
|
|
56
|
+
return { take: false, why: 'pull-request-ignored' }
|
|
57
57
|
}
|
|
58
|
-
if (issue?.state === 'closed') return { take: false, why: 'issue
|
|
58
|
+
if (issue?.state === 'closed') return { take: false, why: 'issue-closed' }
|
|
59
59
|
if (store.findTaskByIssue({ owner, repo, issueNumber: number }) !== undefined) {
|
|
60
|
-
return { take: false, why: '
|
|
60
|
+
return { take: false, why: 'already-exists' }
|
|
61
61
|
}
|
|
62
62
|
if (store.isDismissed({ owner, repo, issueNumber: number })) {
|
|
63
|
-
return { take: false, why: '
|
|
63
|
+
return { take: false, why: 'tombstone-ignored' }
|
|
64
64
|
}
|
|
65
65
|
return { take: true, why: '' }
|
|
66
66
|
}
|
package/lib/store.js
CHANGED
|
@@ -208,7 +208,6 @@ export function openStore({ dir }) {
|
|
|
208
208
|
db.exec('PRAGMA journal_mode = WAL')
|
|
209
209
|
db.exec('PRAGMA foreign_keys = ON')
|
|
210
210
|
db.exec('PRAGMA busy_timeout = 5000')
|
|
211
|
-
db.exec('PRAGMA busy_timeout = 5000')
|
|
212
211
|
db.exec(SCHEMA)
|
|
213
212
|
// База могла быть создана прежней версией. Добавляем недостающие столбцы
|
|
214
213
|
// поштучно: ALTER на существующий столбец бросает, и это штатный ответ
|
package/lib/templates.js
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Task templates for rapid standard work decomposition (#220, #304).
|
|
2
2
|
|
|
3
3
|
export const TASK_TEMPLATES = [
|
|
4
4
|
{
|
|
5
5
|
id: 'feature',
|
|
6
|
-
title: '
|
|
7
|
-
promptTemplate: '
|
|
6
|
+
title: 'New Feature',
|
|
7
|
+
promptTemplate: 'Implement new feature:\n- Goal:\n- Requirements:\n- Scope of changes:',
|
|
8
8
|
checklist: [
|
|
9
|
-
{ id: '1', text: '
|
|
10
|
-
{ id: '2', text: '
|
|
11
|
-
{ id: '3', text: '
|
|
12
|
-
{ id: '4', text: '
|
|
9
|
+
{ id: '1', text: 'Architecture plan and design contract agreed', required: true, completed: false },
|
|
10
|
+
{ id: '2', text: 'Code adheres to project guidelines and standards', required: true, completed: false },
|
|
11
|
+
{ id: '3', text: 'Unit and integration tests added and passing', required: true, completed: false },
|
|
12
|
+
{ id: '4', text: 'Documentation updated (README, docs/)', required: true, completed: false },
|
|
13
13
|
],
|
|
14
14
|
priority: 'medium',
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
id: 'bugfix',
|
|
18
|
-
title: '
|
|
19
|
-
promptTemplate: '
|
|
18
|
+
title: 'Bug Fix',
|
|
19
|
+
promptTemplate: 'Localize and resolve defect:\n- Reproduction steps:\n- Expected behavior:\n- Actual behavior:',
|
|
20
20
|
checklist: [
|
|
21
|
-
{ id: '1', text: '
|
|
22
|
-
{ id: '2', text: '
|
|
23
|
-
{ id: '3', text: '
|
|
21
|
+
{ id: '1', text: 'Defect reproduced with isolated test case', required: true, completed: false },
|
|
22
|
+
{ id: '2', text: 'Root cause fixed without regressions', required: true, completed: false },
|
|
23
|
+
{ id: '3', text: 'Regression test added to test suite', required: true, completed: false },
|
|
24
24
|
],
|
|
25
25
|
priority: 'high',
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
id: 'refactor',
|
|
29
|
-
title: '
|
|
30
|
-
promptTemplate: '
|
|
29
|
+
title: 'Refactoring & Optimization',
|
|
30
|
+
promptTemplate: 'Refactor module preserving external behavior:\n- Target module:\n- Architectural issue:\n- Completion criteria:',
|
|
31
31
|
checklist: [
|
|
32
|
-
{ id: '1', text: '
|
|
33
|
-
{ id: '2', text: '
|
|
34
|
-
{ id: '3', text: '
|
|
32
|
+
{ id: '1', text: 'Covered by existing tests (100% green)', required: true, completed: false },
|
|
33
|
+
{ id: '2', text: 'Structure simplified and technical debt eliminated', required: true, completed: false },
|
|
34
|
+
{ id: '3', text: 'Performance verified (no degradation)', required: true, completed: false },
|
|
35
35
|
],
|
|
36
36
|
priority: 'low',
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
id: 'security',
|
|
40
|
-
title: '
|
|
41
|
-
promptTemplate: '
|
|
40
|
+
title: 'Security & Dependency Audit',
|
|
41
|
+
promptTemplate: 'Verify security and dependency vulnerabilities:\n- Audit scope:\n- Dependency revision:',
|
|
42
42
|
checklist: [
|
|
43
|
-
{ id: '1', text: '
|
|
44
|
-
{ id: '2', text: '
|
|
45
|
-
{ id: '3', text: '
|
|
43
|
+
{ id: '1', text: 'Supply-chain and dependency licenses verified', required: true, completed: false },
|
|
44
|
+
{ id: '2', text: 'Known CVEs mitigated or resolved', required: true, completed: false },
|
|
45
|
+
{ id: '3', text: 'Secrets and credentials verified clean of leaks', required: true, completed: false },
|
|
46
46
|
],
|
|
47
47
|
priority: 'high',
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
id: 'release',
|
|
51
|
-
title: '
|
|
52
|
-
promptTemplate: '
|
|
51
|
+
title: 'Release Preparation',
|
|
52
|
+
promptTemplate: 'Prepare package release:\n- Version:\n- Changelog summary:\n- Pre-release verification:',
|
|
53
53
|
checklist: [
|
|
54
|
-
{ id: '1', text: '
|
|
55
|
-
{ id: '2', text: '
|
|
56
|
-
{ id: '3', text: '
|
|
57
|
-
{ id: '4', text: '
|
|
54
|
+
{ id: '1', text: 'All repository tests pass (npm test)', required: true, completed: false },
|
|
55
|
+
{ id: '2', text: 'Core compatibility confirmed (npm run compat)', required: true, completed: false },
|
|
56
|
+
{ id: '3', text: 'Package archive size verified (< 256 KiB)', required: true, completed: false },
|
|
57
|
+
{ id: '4', text: 'Documentation and changelog updated', required: true, completed: false },
|
|
58
58
|
],
|
|
59
59
|
priority: 'medium',
|
|
60
60
|
},
|
|
@@ -62,4 +62,4 @@ export const TASK_TEMPLATES = [
|
|
|
62
62
|
|
|
63
63
|
export function getTemplateById(id) {
|
|
64
64
|
return TASK_TEMPLATES.find((t) => t.id === id)
|
|
65
|
-
}
|
|
65
|
+
}
|
package/lib/workflow-bridge.js
CHANGED
|
@@ -23,7 +23,7 @@ export const DEFAULT_COLUMN_ROLES = {
|
|
|
23
23
|
done: 'docs',
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const LABEL_ROLE_PATTERNS = [
|
|
27
27
|
{ role: 'spec', patterns: ['spec', 'specification', 'requirements', 'requirements-analysis', 'анализ', 'тз'] },
|
|
28
28
|
{ role: 'architecture', patterns: ['architecture', 'архитектура', 'contract'] },
|
|
29
29
|
{ role: 'ui_design', patterns: ['ui', 'ui_design', 'design', 'ux', 'дизайн'] },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-kanban",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
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",
|