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 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}
@@ -1 +1 @@
1
- {"ts":"2026-08-25T07:24:47.875Z","sessionID":"ses_fcd476c1effeADbTA3UAQxjKMA","added":["260825-d68"],"bumped":[]}
1
+ {"ts":"2026-09-16T10:23:54.497Z","sessionID":"ses_f5682fb36ffeOu9sf2lXfIUCGB","added":["260916-guc"],"bumped":[]}
package/.ait-quality.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  # kanbango quality policy
2
2
  # CLI/GUI entrypoints use console.log as user-facing output (not debug leftovers).
3
+ # This file is local policy only — kanbango does not depend on or invoke ait-quality.
3
4
  version: 1
4
5
  include: []
5
6
  exclude:
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
- - non-trivial work: `plan_create``plan_advance``plan_evidence` (real tests, truncated logs) → `plan_done`
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
- - non-trivial: plan_create → plan_advance → plan_evidence (real tests, truncated logs) → plan_done
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 directory structure |
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
- '- non-trivial: plan_create plan_advance → plan_evidence (real tests, truncated logs) plan_done',
21
- '- plan_done completes into testing gate (not done); after implement move testing (return=none)',
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
- '- do not skip gates: active→done is illegal; testing/review spawn async agents when enabled',
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 (non-trivial only; plan_done → testing gate).',
48
- 'After implement: move testing (return=none). Illegal move → INVALID_TRANSITION with allowed_columns.',
49
- 'Transitions: icebox→planned; planned→active|icebox|testing; activeplanned|testing|icebox;',
50
- 'testing→active|review; review→active|done; done→active. active→done is illegal.',
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). move: task_id+column. update: task_id + fields or subtasks[] full list',
57
- '(no toggle). plan_evidence needs real test run: diff,test_command,stdout,stderr,exit_code truncate logs.',
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":["npm test passes"],"col":"planned"}'
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 (async agent)\n'
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
- const success = await kanban.doMove(taskId, column);
441
- if (success) {
479
+ try {
480
+ await ensureProjectConfig();
481
+ await kanban.updateTask(taskId, { column });
442
482
  console.log(`✓ ${shortId(taskId)} → ${column}`);
443
- } else {
444
- console.error(`✗ Nie znaleziono: ${taskId}`);
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;