kanbango 3.6.2 → 3.8.0
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/.ai/lessons.jsonl +5 -0
- package/.ai/retro/last-run.json +1 -1
- package/.ait-quality.yml +1 -0
- package/AGENTS.md +3 -1
- package/CHANGELOG.md +38 -0
- package/LLM_AGENTS.md +1 -1
- package/README.md +34 -3
- package/agent-playbook.js +30 -13
- package/bin/kanban.js +62 -7
- package/config-wizard.js +441 -0
- package/index.html +67 -36
- package/kanban.js +429 -20
- package/mcp-server.js +87 -22
- package/package.json +1 -1
- package/plan.js +31 -27
- package/tests/run.js +5 -0
- package/workflow.js +364 -18
package/.ai/lessons.jsonl
CHANGED
|
@@ -7,3 +7,8 @@
|
|
|
7
7
|
{"id":"260813-9t0","ts":"2026-08-13","scope":"proj","tags":["mcp","errors","dx"],"rule":"DO MCP missing-field errors: put Valid list + Example JSON + sent keys in message so the agent can retry without guessi…","when":"MCP tool validation / agent-facing erro…","sev":2,"hits":1}
|
|
8
8
|
{"id":"260819-ztl","ts":"2026-08-19","scope":"proj","tags":["config","kanban","validation"],"rule":"DO Field-policy config: skip only ENOENT; invalid JSON/shape must throw CONFIG_INVALID, never silently fall back to def…","when":"config load / required fields","sev":2,"hits":1}
|
|
9
9
|
{"id":"260825-d68","ts":"2026-08-25","scope":"proj","tags":["http","adr"],"rule":"DO HTTP POST/PATCH must forward adr and evidence the same way MCP update does — doCreate already persisted evidence, on…","when":"http adr evidence","sev":2,"hits":1}
|
|
10
|
+
{"id":"260826-bvz","ts":"2026-08-26","scope":"proj","tags":["gui","testing","config"],"rule":"DO Test dynamic GUI columns through runtime config, not static HTML IDs.","when":"config-driven GUI","sev":2,"hits":1}
|
|
11
|
+
{"id":"260827-mmv","ts":"2026-08-27","scope":"proj","tags":["plan","mcp","qa"],"rule":"DO When create maps steps→subtasks, assert subtasks win if both sent; evidence accepts any of diff|summary|test_command…","when":"plan_create / MCP create steps","sev":2,"hits":1}
|
|
12
|
+
{"id":"260827-m2q","ts":"2026-08-27","scope":"proj","tags":["plan","mcp","evidence"],"rule":"DO When create maps steps→subtasks, assert subtasks win if both sent; evidence accepts any of diff|summary|test_command","when":"plan_create / MCP create steps","sev":2,"hits":1}
|
|
13
|
+
{"id":"260916-9im","ts":"2026-09-16","scope":"proj","tags":["kanban","mcp","context","depends-on"],"rule":"DO Agent wake: one kanban_read operation=context instead of list+show; store depends_on and files on the task so gates …","when":"kanban MCP agent context","sev":2,"hits":1}
|
|
14
|
+
{"id":"260916-guc","ts":"2026-09-16","scope":"proj","tags":["kanban","mcp","depends-on","dag"],"rule":"DO On depends_on writes detect cycles immediately (CIRCULAR_DEPENDENCY with details.cycle); expose blocked/blocks on li…","when":"kanban DAG depends_on","sev":2,"hits":1}
|
package/.ai/retro/last-run.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"ts":"2026-
|
|
1
|
+
{"ts":"2026-09-16T10:23:54.497Z","sessionID":"ses_f5682fb36ffeOu9sf2lXfIUCGB","added":["260916-guc"],"bumped":[]}
|
package/.ait-quality.yml
CHANGED
package/AGENTS.md
CHANGED
|
@@ -8,6 +8,8 @@ Source of truth: `agent-playbook.js` (also MCP tool descriptions + `kanban_read`
|
|
|
8
8
|
|
|
9
9
|
- hierarchy: epic (context) → task (work+plan) → subtasks (steps)
|
|
10
10
|
- list: `col` filter, `view=summary`; keep `task_id`s; no full-board re-list after every write
|
|
11
|
+
- context: one-shot compass (active or next unblocked planned, else idle); optional `epic`
|
|
12
|
+
- `depends_on` must be done before active/gates (`TASK_BLOCKED`); cycle → `CIRCULAR_DEPENDENCY`; `files[]` = touched paths
|
|
11
13
|
- list_epics default = live only (hides done + archived); `include_archived` / `status` to widen
|
|
12
14
|
- list tasks default hides tasks under done/archived epics; show/show_epic by id always works
|
|
13
15
|
- show: `view=execution` while coding; `full` only if needed
|
|
@@ -17,7 +19,7 @@ Source of truth: `agent-playbook.js` (also MCP tool descriptions + `kanban_read`
|
|
|
17
19
|
- create once with `description`, `specs`, `in_scope`, `out_of_scope`, `acceptance_criteria`
|
|
18
20
|
- move/update/delete: `return=none`; subtasks = full array replace (no toggle)
|
|
19
21
|
- cleanup: `delete` (task), `epic_delete` (cascade), `epic_archive` / `epic_unarchive`
|
|
20
|
-
-
|
|
22
|
+
- `steps` on create/plan_create → subtasks (no forced TDD); runner optional; optional plan_*: plan_create → plan_advance → plan_evidence (diff|summary|test_command) → plan_done
|
|
21
23
|
- gui: `status` before `start`; `stop` only owned; `external_running` = do not kill
|
|
22
24
|
|
|
23
25
|
## Build & Development Commands
|
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Changed
|
|
11
|
+
- `plan_create` / `detectTestRunner`: missing test runner returns `null` (no `NO_TEST_RUNNER` hard fail)
|
|
12
|
+
- Plan subtasks = only provided `steps` (no forced Write tests / red / green)
|
|
13
|
+
- MCP `create` accepts `steps[]` as subtask shortcut when `subtasks` omitted
|
|
14
|
+
- `plan_evidence`: require at least one of `diff` | `summary` | `test_command`; test logs/exit_code optional
|
|
15
|
+
|
|
16
|
+
## [3.8.0] - 2026-09-16
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Agent-first context snapshot with dependency-aware task selection.
|
|
20
|
+
- `depends_on` DAG validation, cycle detection, blocked/downstream visibility, and `files` scope tracking.
|
|
21
|
+
- Context next actions and `unblocked_tasks` after completing a dependency.
|
|
22
|
+
- Planned follow-up tasks for strict evidence gates, actionable MCP errors, and an agent cockpit GUI.
|
|
23
|
+
|
|
24
|
+
## [3.7.0] - 2026-08-26
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Interactive config TUI (`config-wizard.js`): checkbox gates/agents on `kanban init` / `kanban mcp-init` when `backlog/kanbango.json` is missing (TTY only; defaults all on)
|
|
28
|
+
- `kanban config` re-opens the wizard (TTY required); malformed existing JSON → `CONFIG_INVALID`, file not replaced
|
|
29
|
+
- Non-TTY init/mcp-init print a one-line hint to run `kanban config` (no hang, no invented file)
|
|
30
|
+
- Tests: `tests/config-wizard.test.js` (fake TTY streams, cancel/raw-mode cleanup, CLI non-TTY)
|
|
31
|
+
|
|
10
32
|
## [3.6.2] - 2026-08-25
|
|
11
33
|
|
|
34
|
+
### Added
|
|
35
|
+
- Project board config in `backlog/kanbango.json`:
|
|
36
|
+
- `columns.testing|review`: `{ enabled, label }` (defaults: both enabled, labels Testing/Review)
|
|
37
|
+
- `workflow.testing_agent|review_agent`: string (legacy) or `{ enabled, name }`
|
|
38
|
+
- Disabled gate column: hidden from GUI/MCP/CLI, transitions rebuild, existing cards auto-migrate to `planned` (idempotent, no config trail)
|
|
39
|
+
- Agent `enabled: false` with column on = manual gate (no spawn, card stays)
|
|
40
|
+
- GUI `GET /api/config` + dynamic board columns/labels from config
|
|
41
|
+
- MCP column enums reflect active columns after config load
|
|
42
|
+
- `plan_done` lands in first enabled gate or `done`
|
|
43
|
+
- Tests: `tests/board-config.test.js`
|
|
44
|
+
|
|
12
45
|
### Changed
|
|
13
46
|
- `move` / `updateTask` column changes validate workflow transitions
|
|
14
47
|
- Illegal move returns `INVALID_TRANSITION` with `allowed_columns` in details and hint
|
|
15
48
|
- Allowed: icebox→planned; planned→active|icebox|testing; active→planned|testing|icebox; testing→active|review; review→active|done; done→active
|
|
16
49
|
- `active→done` is illegal (cannot skip testing/review)
|
|
17
50
|
- MCP `column` description and playbook document the map; GUI arrows only show legal neighbors
|
|
51
|
+
- CLI `list` / `show` / `epic list` load board config (migrate disabled-gate cards)
|
|
52
|
+
- MCP `tools/list` fails on `CONFIG_INVALID` instead of advertising default columns
|
|
53
|
+
|
|
54
|
+
### Note
|
|
55
|
+
- `.ait-quality.yml` kept as optional local scan policy only (CLI `console.log` = UX). No ait-quality dependency or runtime invoke.
|
|
18
56
|
|
|
19
57
|
## [3.6.1] - 2026-08-25
|
|
20
58
|
|
package/LLM_AGENTS.md
CHANGED
|
@@ -486,7 +486,7 @@ Kanbango MCP — token rules:
|
|
|
486
486
|
- show: view=execution while coding; full only if needed
|
|
487
487
|
- create once with description,specs,in_scope,out_of_scope,acceptance_criteria
|
|
488
488
|
- move/update: return=none; subtasks=full array replace
|
|
489
|
-
-
|
|
489
|
+
- steps on create/plan_create → subtasks (no forced TDD); runner optional; optional plan_*: plan_create → plan_advance → plan_evidence (diff|summary|test_command) → plan_done
|
|
490
490
|
- gui: status before start; stop only owned; external_running = do not kill
|
|
491
491
|
```
|
|
492
492
|
|
package/README.md
CHANGED
|
@@ -22,9 +22,12 @@ npx kanbango --help
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
# Initialize backlog directories
|
|
25
|
+
# Initialize backlog directories (TTY: config wizard for QA/review gates)
|
|
26
26
|
kanban init
|
|
27
27
|
|
|
28
|
+
# Re-open project config wizard (writes backlog/kanbango.json)
|
|
29
|
+
kanban config
|
|
30
|
+
|
|
28
31
|
# Start web GUI (stable project port; prints the real URL)
|
|
29
32
|
kanban serve
|
|
30
33
|
|
|
@@ -73,8 +76,35 @@ Epic status is **derived** from child task columns (not a board column).
|
|
|
73
76
|
| `active` | In progress (keep to 1–2 tasks) |
|
|
74
77
|
| `planned` | Ready to implement |
|
|
75
78
|
| `icebox` | Nice-to-have / frozen |
|
|
79
|
+
| `testing` | Optional QA gate (default on) |
|
|
80
|
+
| `review` | Optional review gate (default on) |
|
|
76
81
|
| `done` | Completed |
|
|
77
82
|
|
|
83
|
+
### Project config (`backlog/kanbango.json`)
|
|
84
|
+
|
|
85
|
+
Optional. Missing file = six columns, workflow agents off.
|
|
86
|
+
|
|
87
|
+
On a **TTY**, `kanban init` / `kanban mcp-init` open a checkbox wizard when the file is missing (defaults: testing + qa-tester + review + temida all on). Non-TTY (CI, pipes, MCP) never blocks or creates a config; it prints a hint to run `kanban config` in a terminal. `kanban config` always opens the wizard (TTY required); invalid existing JSON is **not** overwritten (error + fix/remove).
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"columns": {
|
|
92
|
+
"testing": { "enabled": false, "label": "QA" },
|
|
93
|
+
"review": { "enabled": true, "label": "Review" }
|
|
94
|
+
},
|
|
95
|
+
"workflow": {
|
|
96
|
+
"enabled": true,
|
|
97
|
+
"testing_agent": { "enabled": true, "name": "qa-tester" },
|
|
98
|
+
"review_agent": { "enabled": false, "name": "temida" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- Only `testing` and `review` are configurable (core columns stay on).
|
|
104
|
+
- `enabled: false` on a gate column hides it and moves existing cards in that column to `planned`.
|
|
105
|
+
- Agent `enabled: false` keeps the column as a **manual** gate (no auto spawn).
|
|
106
|
+
- Legacy string agent names still work: `"testing_agent": "qa-tester"`.
|
|
107
|
+
|
|
78
108
|
## Data Structure
|
|
79
109
|
|
|
80
110
|
Tasks are JSON files in `backlog/<column>/`:
|
|
@@ -163,14 +193,15 @@ Your agent stays in sync with your real board — every change is persisted as J
|
|
|
163
193
|
|
|
164
194
|
| Command | Description |
|
|
165
195
|
|---------|-------------|
|
|
166
|
-
| `kanban init` | Create backlog
|
|
196
|
+
| `kanban init` | Create backlog dirs; TTY wizard if `kanbango.json` missing |
|
|
197
|
+
| `kanban config` | Interactive project config wizard (`backlog/kanbango.json`) |
|
|
167
198
|
| `kanban serve [PORT]` | Start web GUI (stable project port, or PORT / KANBANGO_GUI_PORT) |
|
|
168
199
|
| `kanban list [--col <col>] [--json]` | List tasks |
|
|
169
200
|
| `kanban show <ID>` | Show task details |
|
|
170
201
|
| `kanban add <TITLE>` | Add a new task |
|
|
171
202
|
| `kanban move <ID> <COL>` | Move task |
|
|
172
203
|
| `kanban mcp` | Start MCP server (stdio) |
|
|
173
|
-
| `kanban mcp-init` | Generate MCP config files |
|
|
204
|
+
| `kanban mcp-init` | Generate MCP config files; TTY wizard if project config is missing |
|
|
174
205
|
| `kanban plan <action> --json '{...}'` | Accepted-plan workflow (create/advance/evidence/done/status) |
|
|
175
206
|
|
|
176
207
|
## Web GUI
|
package/agent-playbook.js
CHANGED
|
@@ -8,6 +8,8 @@ const DROP_IN_RULE = [
|
|
|
8
8
|
'Kanbango MCP — token rules:',
|
|
9
9
|
'- hierarchy: epic (context) → task (work+plan) → subtasks (steps)',
|
|
10
10
|
'- list: col filter, view=summary; keep task_ids; no full-board re-list after writes',
|
|
11
|
+
'- context: one-shot compass (active or next unblocked planned, else idle); optional epic=E001; prefer over list+show',
|
|
12
|
+
'- context next_action: advance|evidence|move_testing|move_review|move_done|fix_gate|start|idle',
|
|
11
13
|
'- list_epics default = live only (hides done + archived); include_archived / status to widen',
|
|
12
14
|
'- list tasks default hides tasks under done/archived epics; show/show_epic by id always works',
|
|
13
15
|
'- show: view=execution while coding; full only if needed',
|
|
@@ -17,11 +19,16 @@ const DROP_IN_RULE = [
|
|
|
17
19
|
'- create once with description,specs,in_scope,out_of_scope,acceptance_criteria',
|
|
18
20
|
'- move/update/delete: return=none; subtasks=full array replace',
|
|
19
21
|
'- cleanup: delete (task), epic_delete (cascade tasks), epic_archive / epic_unarchive',
|
|
20
|
-
'-
|
|
21
|
-
'-
|
|
22
|
+
'- steps on create/plan_create become subtasks (no forced TDD steps); test runner optional (null if none)',
|
|
23
|
+
'- optional plan_*: plan_create → plan_advance → plan_evidence (diff|summary|test_command) → plan_done',
|
|
24
|
+
'- plan_done completes into first enabled gate (testing|review) or done; after implement move testing when that column is enabled',
|
|
25
|
+
'- depends_on: task ids that must be done before active/gates; blocked move → TASK_BLOCKED; cycle → CIRCULAR_DEPENDENCY',
|
|
26
|
+
'- list/show: blocked + unmet_dependencies + blocks; move to done returns unblocked_tasks',
|
|
27
|
+
'- files: touched paths on create/update/plan_evidence (scope for QA/review)',
|
|
22
28
|
'- move validates transitions; illegal move → INVALID_TRANSITION + allowed_columns',
|
|
23
|
-
'- allowed: icebox→planned; planned→active|icebox|testing; active→planned|testing|icebox; testing→active|review; review→active|done; done→active',
|
|
24
|
-
'-
|
|
29
|
+
'- default allowed: icebox→planned; planned→active|icebox|testing; active→planned|testing|icebox; testing→active|review; review→active|done; done→active',
|
|
30
|
+
'- backlog/kanbango.json may disable testing/review columns (labels + migrate cards to planned) and/or disable QA/Temida spawn (manual gate)',
|
|
31
|
+
'- do not skip enabled gates: active→done illegal when testing|review on; gates spawn agents only when workflow+agent enabled',
|
|
25
32
|
'- gui: status before start; stop only owned; external_running = do not kill'
|
|
26
33
|
].join('\n');
|
|
27
34
|
|
|
@@ -29,7 +36,7 @@ const TOOL_DESCRIPTIONS = {
|
|
|
29
36
|
kanban_read: [
|
|
30
37
|
'Read board. TOKEN RULES: list defaults to view=summary (id/title/col/progress only).',
|
|
31
38
|
'Hierarchy: epic (container/context) → task (work) → subtasks (steps).',
|
|
32
|
-
'Always pass col when possible. Prefer show+view=execution over full.',
|
|
39
|
+
'Always pass col when possible. Prefer operation=context on wake, then show+view=execution over full.',
|
|
33
40
|
'show task (planning/execution/full) includes epic_goals (parent epic goals; "" if none) — use as compass; check progress toward goal.',
|
|
34
41
|
'execution/full also include adr[]; show_epic full rolls up child adrs (not stored on epic file).',
|
|
35
42
|
'list_epics default live only (empty|planned|active; hides done+archived).',
|
|
@@ -37,6 +44,8 @@ const TOOL_DESCRIPTIONS = {
|
|
|
37
44
|
'list hides tasks under done/archived epics unless include_archived/include_done; show/show_epic by id always works.',
|
|
38
45
|
'Do not re-list the whole board after every write — keep task_id from create/move.',
|
|
39
46
|
'Task IDs numeric ("014"); epic IDs "E001". views: summary|planning|execution|full; fields[] overrides view.',
|
|
47
|
+
'operation=context returns compact next_action (advance|evidence|move_*|fix_gate|start|idle); optional epic filter.',
|
|
48
|
+
'planning/execution/full include blocked, unmet_dependencies, blocks; summary includes blocked.',
|
|
40
49
|
'operation=help returns this playbook as short text (no board I/O).'
|
|
41
50
|
].join(' '),
|
|
42
51
|
|
|
@@ -44,19 +53,22 @@ const TOOL_DESCRIPTIONS = {
|
|
|
44
53
|
'Write board / plan. TOKEN RULES: one create with all planning fields beats many updates;',
|
|
45
54
|
'after write use return=none (or summary). Do not dump full task unless needed.',
|
|
46
55
|
'Actions: create|move|update|delete (daily); epic_create|epic_update|epic_archive|epic_unarchive|epic_delete;',
|
|
47
|
-
'plan_create→plan_advance→plan_evidence→plan_done (
|
|
48
|
-
'After implement: move testing (return=none). Illegal move → INVALID_TRANSITION with allowed_columns.',
|
|
49
|
-
'
|
|
50
|
-
'
|
|
56
|
+
'optional plan_create→plan_advance→plan_evidence→plan_done (plan_done → first enabled gate or done).',
|
|
57
|
+
'After implement: move testing when enabled (return=none). Illegal move → INVALID_TRANSITION with allowed_columns.',
|
|
58
|
+
'depends_on must be done before active/gates (TASK_BLOCKED). Cycle → CIRCULAR_DEPENDENCY.',
|
|
59
|
+
'files[] stores touched paths. move to done returns unblocked_tasks.',
|
|
60
|
+
'Default transitions: icebox→planned; planned→active|icebox|testing; active→planned|testing|icebox;',
|
|
61
|
+
'testing→active|review; review→active|done; done→active. Project may disable testing/review via backlog/kanbango.json.',
|
|
51
62
|
'epic_create: title + description/goals/in_scope/out_of_scope. Link tasks via epic=E001.',
|
|
52
63
|
'Log architecture decisions on the task: update with adr={decision,why} appends one entry (why this choice).',
|
|
53
64
|
'delete: task_id (hard remove). epic_delete: epic_id, always cascades child tasks.',
|
|
54
65
|
'epic_archive/epic_unarchive: epic_id — hide/restore initiative without deleting history.',
|
|
55
66
|
'create/plan_create: title required; also send description,specs,in_scope,out_of_scope,acceptance_criteria',
|
|
56
|
-
'(missing → warnings, not failure).
|
|
57
|
-
'(
|
|
67
|
+
'(missing → warnings, not failure). steps[] → subtasks (create when subtasks omitted; plan_create as-is, no forced TDD).',
|
|
68
|
+
'plan_create runner optional (null if none). move: task_id+column. update: task_id + fields or subtasks[] full list',
|
|
69
|
+
'(no toggle). plan_evidence: at least one of diff|summary|test_command; stdout/stderr/exit_code optional — truncate logs.',
|
|
58
70
|
'Example create: {"action":"create","title":"Ship image","epic":"E001","description":"...","specs":"...",',
|
|
59
|
-
'"in_scope":["CLI"],"out_of_scope":["GUI"],"acceptance_criteria":["
|
|
71
|
+
'"in_scope":["CLI"],"out_of_scope":["GUI"],"acceptance_criteria":["done"],"steps":["Impl"],"col":"planned"}'
|
|
60
72
|
].join(' '),
|
|
61
73
|
|
|
62
74
|
kanban_gui: [
|
|
@@ -77,7 +89,12 @@ const MUST_CONTAIN = [
|
|
|
77
89
|
'epic_archive',
|
|
78
90
|
'epic_delete',
|
|
79
91
|
'epic_goals',
|
|
80
|
-
'adr'
|
|
92
|
+
'adr',
|
|
93
|
+
'context',
|
|
94
|
+
'depends_on',
|
|
95
|
+
'TASK_BLOCKED',
|
|
96
|
+
'CIRCULAR_DEPENDENCY',
|
|
97
|
+
'unblocked_tasks'
|
|
81
98
|
];
|
|
82
99
|
|
|
83
100
|
function playbookHelpPayload() {
|
package/bin/kanban.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const kanban = require('../kanban.js');
|
|
4
4
|
const plan = require('../plan.js');
|
|
5
5
|
const guiRegistry = require('../gui-registry.js');
|
|
6
|
+
const configWizard = require('../config-wizard.js');
|
|
6
7
|
const http = require('http');
|
|
7
8
|
const fs = require('fs');
|
|
8
9
|
const path = require('path');
|
|
@@ -101,15 +102,35 @@ async function cliInit() {
|
|
|
101
102
|
+ '- `active/` — w trakcie (max 1-2)\n'
|
|
102
103
|
+ '- `planned/` — zaplanowane\n'
|
|
103
104
|
+ '- `icebox/` — zamrozone / nice-to-have\n'
|
|
104
|
-
+ '- `testing/` — bramka QA (async agent)\n'
|
|
105
|
-
+ '- `review/` — bramka review / Temida (
|
|
105
|
+
+ '- `testing/` — bramka QA (async agent; optional via kanbango.json)\n'
|
|
106
|
+
+ '- `review/` — bramka review / Temida (optional via kanbango.json)\n'
|
|
106
107
|
+ '- `done/` — ukonczone\n'
|
|
107
|
-
+ '- `epics/` — first-class epic containers (context for initiatives)\n'
|
|
108
|
+
+ '- `epics/` — first-class epic containers (context for initiatives)\n'
|
|
109
|
+
+ '- `kanbango.json` — optional project config (columns + workflow agents)\n',
|
|
108
110
|
'utf-8'
|
|
109
111
|
);
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
console.log(`✓ Backlog w: ${BACKLOG}`);
|
|
115
|
+
await configWizard.maybeCreateConfigOnInit({ cwd: process.cwd() });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function exitConfigError(error) {
|
|
119
|
+
console.error(`✗ ${error.message}`);
|
|
120
|
+
if (error.hint) console.error(` ${error.hint}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function cliConfig() {
|
|
125
|
+
try {
|
|
126
|
+
await kanban.ensureBacklogDir();
|
|
127
|
+
await configWizard.runConfigCommand({ cwd: process.cwd() });
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error && (error.code === 'NOT_TTY' || error.code === 'CONFIG_INVALID')) {
|
|
130
|
+
exitConfigError(error);
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
113
134
|
}
|
|
114
135
|
|
|
115
136
|
function sha256Hex(content) {
|
|
@@ -275,9 +296,25 @@ async function cliMcpInit(options) {
|
|
|
275
296
|
if (!force) {
|
|
276
297
|
console.log(' (użyj --force, aby nadpisać istniejące pliki)');
|
|
277
298
|
}
|
|
299
|
+
|
|
300
|
+
await kanban.ensureBacklogDir();
|
|
301
|
+
await configWizard.maybeCreateConfigOnInit({ cwd });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function ensureProjectConfig() {
|
|
305
|
+
try {
|
|
306
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (error && error.code === 'CONFIG_INVALID') {
|
|
309
|
+
console.error(`✗ ${error.message}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
278
314
|
}
|
|
279
315
|
|
|
280
316
|
async function cliList(colFilter, epicFilter, asJson, listOptions = {}) {
|
|
317
|
+
await ensureProjectConfig();
|
|
281
318
|
await kanban.migrateEpicGroups();
|
|
282
319
|
const epics = await kanban.listEpicEntities();
|
|
283
320
|
let tasks = await kanban.allTasks();
|
|
@@ -316,6 +353,7 @@ async function cliList(colFilter, epicFilter, asJson, listOptions = {}) {
|
|
|
316
353
|
|
|
317
354
|
async function cliShow(taskId) {
|
|
318
355
|
try {
|
|
356
|
+
await ensureProjectConfig();
|
|
319
357
|
const task = await kanban.getTask(taskId);
|
|
320
358
|
console.log(`ID: ${shortId(task.id)}`);
|
|
321
359
|
console.log(`Plik: ${taskFilePath(task)}`);
|
|
@@ -343,6 +381,7 @@ async function cliShow(taskId) {
|
|
|
343
381
|
}
|
|
344
382
|
|
|
345
383
|
async function cliEpicList(asJson, listOptions = {}) {
|
|
384
|
+
await ensureProjectConfig();
|
|
346
385
|
await kanban.migrateEpicGroups();
|
|
347
386
|
const tasks = await kanban.allTasks();
|
|
348
387
|
const epics = await kanban.listEpicEntities();
|
|
@@ -437,17 +476,19 @@ async function cliDelete(taskId) {
|
|
|
437
476
|
}
|
|
438
477
|
|
|
439
478
|
async function cliMove(taskId, column) {
|
|
440
|
-
|
|
441
|
-
|
|
479
|
+
try {
|
|
480
|
+
await ensureProjectConfig();
|
|
481
|
+
await kanban.updateTask(taskId, { column });
|
|
442
482
|
console.log(`✓ ${shortId(taskId)} → ${column}`);
|
|
443
|
-
}
|
|
444
|
-
console.error(`✗
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error(`✗ ${error.message}`);
|
|
445
485
|
process.exit(1);
|
|
446
486
|
}
|
|
447
487
|
}
|
|
448
488
|
|
|
449
489
|
async function cliAdd(title, column, epicGroup) {
|
|
450
490
|
try {
|
|
491
|
+
await ensureProjectConfig();
|
|
451
492
|
const task = await kanban.doCreate(title, column, epicGroup);
|
|
452
493
|
console.log(`✓ Utworzono ${shortId(task.id)} w ${column} [${task.epic_group}]`);
|
|
453
494
|
console.log(` Plik: ${taskFilePath(task)}`);
|
|
@@ -528,7 +569,15 @@ async function serveWeb(port) {
|
|
|
528
569
|
return;
|
|
529
570
|
}
|
|
530
571
|
|
|
572
|
+
if (requestPath === '/api/config' && req.method === 'GET') {
|
|
573
|
+
const workflow = require('../workflow.js');
|
|
574
|
+
const config = await workflow.ensureBoardConfig();
|
|
575
|
+
sendJson(res, 200, workflow.shapePublicConfig(config));
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
|
|
531
579
|
if (requestPath === '/api/board') {
|
|
580
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
532
581
|
await kanban.migrateEpicGroups();
|
|
533
582
|
const includeArchived = url.searchParams.get('include_archived') === 'true';
|
|
534
583
|
const epics = await kanban.listEpicEntities();
|
|
@@ -542,6 +591,7 @@ async function serveWeb(port) {
|
|
|
542
591
|
}
|
|
543
592
|
|
|
544
593
|
if (requestPath === '/api/epics' && req.method === 'GET') {
|
|
594
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
545
595
|
await kanban.migrateEpicGroups();
|
|
546
596
|
const tasks = await kanban.allTasks();
|
|
547
597
|
const epics = await kanban.listEpicEntities();
|
|
@@ -559,6 +609,7 @@ async function serveWeb(port) {
|
|
|
559
609
|
}
|
|
560
610
|
|
|
561
611
|
if (requestPath === '/api/epics' && req.method === 'POST') {
|
|
612
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
562
613
|
const body = await readBody(req);
|
|
563
614
|
// Real epic container when no task column is provided.
|
|
564
615
|
if (body.column === undefined && body.as_task !== true) {
|
|
@@ -595,6 +646,7 @@ async function serveWeb(port) {
|
|
|
595
646
|
}
|
|
596
647
|
|
|
597
648
|
if (requestPath === '/api/tasks' && req.method === 'POST') {
|
|
649
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
598
650
|
const body = await readBody(req);
|
|
599
651
|
const task = await kanban.doCreate(
|
|
600
652
|
body.title || '',
|
|
@@ -657,6 +709,7 @@ async function serveWeb(port) {
|
|
|
657
709
|
|
|
658
710
|
const moveMatch = requestPath.match(/^\/api\/(?:epics|tasks)\/([^/]+)\/move$/);
|
|
659
711
|
if (moveMatch) {
|
|
712
|
+
await require('../workflow.js').ensureBoardConfig();
|
|
660
713
|
const taskId = moveMatch[1];
|
|
661
714
|
const body = await readBody(req);
|
|
662
715
|
const task = await kanban.updateTask(taskId, { column: body.column || '' });
|
|
@@ -863,6 +916,8 @@ async function main() {
|
|
|
863
916
|
await serveWeb(port);
|
|
864
917
|
} else if (cmd === 'init') {
|
|
865
918
|
await cliInit();
|
|
919
|
+
} else if (cmd === 'config') {
|
|
920
|
+
await cliConfig();
|
|
866
921
|
} else if (cmd === 'mcp-init') {
|
|
867
922
|
let useNpx = false;
|
|
868
923
|
let onlyClaude = false;
|