@gzmagyari/kanbanboard 1.0.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/API.md ADDED
@@ -0,0 +1,1256 @@
1
+ # Kanban Dashboard — API Reference
2
+
3
+ Base URL: `http://127.0.0.1:3000`
4
+
5
+ All endpoints return JSON. All timestamps are Unix milliseconds.
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Health & Config](#health--config)
12
+ - [Projects](#projects)
13
+ - [Tasks](#tasks)
14
+ - [Task Hierarchy](#task-hierarchy)
15
+ - [Next Sub-ticket](#next-sub-ticket)
16
+ - [Comments & Progress Updates](#comments--progress-updates)
17
+ - [Automation Tasks](#automation-tasks)
18
+ - [Automation Helpers](#automation-helpers)
19
+ - [AI: Task Merge](#ai-task-merge)
20
+ - [AI: Project Init](#ai-project-init)
21
+ - [AI: Evolve Hierarchy](#ai-evolve-hierarchy)
22
+ - [AI: Remove Concept](#ai-remove-concept)
23
+ - [Error Handling](#error-handling)
24
+ - [Object Schemas](#object-schemas)
25
+
26
+ ---
27
+
28
+ ## Health & Config
29
+
30
+ ### `GET /api/health`
31
+
32
+ Basic liveness check.
33
+
34
+ **Response** `200`
35
+ ```json
36
+ { "ok": true }
37
+ ```
38
+
39
+ ---
40
+
41
+ ### `GET /api/columns`
42
+
43
+ Returns the valid status columns for tasks and automation tasks.
44
+
45
+ **Response** `200`
46
+ ```json
47
+ {
48
+ "taskColumns": [
49
+ { "key": "ideas", "name": "Ideas" },
50
+ { "key": "todo", "name": "To do" },
51
+ { "key": "in_progress", "name": "In Progress" },
52
+ { "key": "review", "name": "Review" },
53
+ { "key": "testing", "name": "Testing" },
54
+ { "key": "done", "name": "Done" }
55
+ ],
56
+ "autoColumns": [
57
+ { "key": "ideas", "name": "Ideas" },
58
+ { "key": "active", "name": "Active" }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ### `GET /api/ai/health`
66
+
67
+ Returns LLM configuration status. No API key required.
68
+
69
+ **Response** `200`
70
+ ```json
71
+ {
72
+ "enabled": true,
73
+ "base_url": "https://openrouter.ai/api/v1",
74
+ "model": "google/gemini-3-pro-preview",
75
+ "api_key_required": false,
76
+ "rate_limit_per_min": 60
77
+ }
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Projects
83
+
84
+ ### `GET /api/projects`
85
+
86
+ Lists all projects with task and automation counts.
87
+
88
+ **Response** `200`
89
+ ```json
90
+ {
91
+ "projects": [
92
+ {
93
+ "id": "default",
94
+ "name": "Default",
95
+ "scope_text": "",
96
+ "created_at": 1770500000000,
97
+ "updated_at": 1770500000000,
98
+ "task_count": 42,
99
+ "automation_count": 2
100
+ }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ### `POST /api/projects`
108
+
109
+ Creates a new project.
110
+
111
+ **Request**
112
+ ```json
113
+ {
114
+ "name": "My Project",
115
+ "scope_text": "A web application for managing widgets."
116
+ }
117
+ ```
118
+
119
+ | Field | Type | Required | Default |
120
+ |-------|------|----------|---------|
121
+ | `name` | string | yes | — |
122
+ | `scope_text` | string | no | `""` |
123
+
124
+ **Response** `201`
125
+ ```json
126
+ {
127
+ "project": {
128
+ "id": "pH1ZSGuW5i",
129
+ "name": "My Project",
130
+ "scope_text": "A web application for managing widgets.",
131
+ "created_at": 1770572000000,
132
+ "updated_at": 1770572000000
133
+ }
134
+ }
135
+ ```
136
+
137
+ **Errors**
138
+ - `400` — `name` is missing or empty
139
+
140
+ ---
141
+
142
+ ### `GET /api/projects/:id`
143
+
144
+ Returns a single project.
145
+
146
+ **Example** `GET /api/projects/pH1ZSGuW5i`
147
+
148
+ **Response** `200`
149
+ ```json
150
+ {
151
+ "project": {
152
+ "id": "pH1ZSGuW5i",
153
+ "name": "My Project",
154
+ "scope_text": "A web application for managing widgets.",
155
+ "created_at": 1770572000000,
156
+ "updated_at": 1770572000000
157
+ }
158
+ }
159
+ ```
160
+
161
+ **Errors**
162
+ - `404` — project not found
163
+
164
+ ---
165
+
166
+ ### `PATCH /api/projects/:id`
167
+
168
+ Updates a project's name and/or scope.
169
+
170
+ **Request**
171
+ ```json
172
+ {
173
+ "name": "Renamed Project",
174
+ "scope_text": "Updated scope description."
175
+ }
176
+ ```
177
+
178
+ All fields are optional. Only provided fields are updated.
179
+
180
+ **Response** `200` — same shape as GET
181
+
182
+ **Errors**
183
+ - `404` — project not found
184
+ - `400` — name is empty string
185
+
186
+ ---
187
+
188
+ ### `DELETE /api/projects/:id`
189
+
190
+ Deletes a project. All tasks in the project are reassigned to the `default` project first.
191
+
192
+ **Response** `200`
193
+ ```json
194
+ { "ok": true }
195
+ ```
196
+
197
+ **Errors**
198
+ - `400` — cannot delete the default project
199
+ - `404` — project not found
200
+
201
+ ---
202
+
203
+ ## Tasks
204
+
205
+ ### `GET /api/tasks`
206
+
207
+ Lists tasks with optional filters.
208
+
209
+ **Query Parameters**
210
+
211
+ | Param | Type | Description |
212
+ |-------|------|-------------|
213
+ | `status` | string | Filter by status (e.g. `todo`, `in_progress`) |
214
+ | `project_id` | string | Filter by project |
215
+ | `parent_id` | string | Filter by parent. Use `null` or empty for root tasks |
216
+ | `include_deleted` | boolean | Include soft-deleted tasks. Default `false` |
217
+
218
+ **Example** `GET /api/tasks?project_id=pH1ZSGuW5i&status=todo`
219
+
220
+ **Response** `200`
221
+ ```json
222
+ {
223
+ "tasks": [
224
+ {
225
+ "id": "bY0pcKdSUx",
226
+ "title": "Root Task",
227
+ "description": "A root-level task",
228
+ "status": "todo",
229
+ "detail_text": "",
230
+ "created_at": 1770572611089,
231
+ "updated_at": 1770572611089,
232
+ "project_id": "pH1ZSGuW5i",
233
+ "parent_id": null,
234
+ "deleted_at": null
235
+ }
236
+ ]
237
+ }
238
+ ```
239
+
240
+ **Errors**
241
+ - `400` — invalid status or project_id
242
+
243
+ ---
244
+
245
+ ### `POST /api/tasks`
246
+
247
+ Creates a new task.
248
+
249
+ **Request**
250
+ ```json
251
+ {
252
+ "title": "Implement login page",
253
+ "description": "Build the login form with email/password fields",
254
+ "status": "todo",
255
+ "detail_text": "Use Vuetify form components...",
256
+ "project_id": "pH1ZSGuW5i",
257
+ "parent_id": "bY0pcKdSUx"
258
+ }
259
+ ```
260
+
261
+ | Field | Type | Required | Default |
262
+ |-------|------|----------|---------|
263
+ | `title` | string | yes | — |
264
+ | `description` | string | no | `""` |
265
+ | `status` | string | no | `"todo"` |
266
+ | `detail_text` | string | no | `""` |
267
+ | `project_id` | string | no | `"default"` |
268
+ | `parent_id` | string | no | `null` |
269
+
270
+ **Response** `201`
271
+ ```json
272
+ {
273
+ "task": {
274
+ "id": "OnXAVEF7D1",
275
+ "title": "Implement login page",
276
+ "description": "Build the login form with email/password fields",
277
+ "status": "todo",
278
+ "detail_text": "Use Vuetify form components...",
279
+ "created_at": 1770572617938,
280
+ "updated_at": 1770572617938,
281
+ "project_id": "pH1ZSGuW5i",
282
+ "parent_id": "bY0pcKdSUx",
283
+ "deleted_at": null
284
+ }
285
+ }
286
+ ```
287
+
288
+ **Side effects**
289
+ - Parent task's `updated_at` is bumped when `parent_id` is set
290
+
291
+ **Errors**
292
+ - `400` — missing title, invalid status, project not found, parent not found or in different project
293
+
294
+ ---
295
+
296
+ ### `GET /api/tasks/:id`
297
+
298
+ Returns a task with its comments and progress updates.
299
+
300
+ **Example** `GET /api/tasks/bY0pcKdSUx`
301
+
302
+ **Response** `200`
303
+ ```json
304
+ {
305
+ "task": { "...": "task object" },
306
+ "comments": [
307
+ {
308
+ "id": 4,
309
+ "entity_type": "task",
310
+ "entity_id": "bY0pcKdSUx",
311
+ "author": "Gabor",
312
+ "text": "This is a test comment",
313
+ "created_at": 1770572931850
314
+ }
315
+ ],
316
+ "updates": [
317
+ {
318
+ "id": 299,
319
+ "entity_type": "task",
320
+ "entity_id": "bY0pcKdSUx",
321
+ "author": "Jarvis",
322
+ "text": "Progress: 50% complete",
323
+ "created_at": 1770572935085
324
+ }
325
+ ]
326
+ }
327
+ ```
328
+
329
+ Updates are capped at 50 most recent, ordered newest first.
330
+
331
+ **Errors**
332
+ - `404` — task not found or soft-deleted
333
+
334
+ ---
335
+
336
+ ### `PATCH /api/tasks/:id`
337
+
338
+ Updates a task. Only provided fields are changed.
339
+
340
+ **Request**
341
+ ```json
342
+ {
343
+ "title": "Updated title",
344
+ "status": "in_progress",
345
+ "parent_id": "bY0pcKdSUx",
346
+ "project_id": "pH1ZSGuW5i"
347
+ }
348
+ ```
349
+
350
+ All fields are optional.
351
+
352
+ **Validation rules**
353
+ - `parent_id` cannot be the task itself (`400`)
354
+ - `parent_id` cannot create a cycle — i.e. cannot set parent to own descendant (`400`)
355
+ - `parent_id` must be in the same project (`400`)
356
+ - Changing `project_id` detaches the task from its parent (sets `parent_id = null`)
357
+
358
+ **Response** `200` — returns updated task object
359
+
360
+ **Errors**
361
+ - `404` — task not found
362
+ - `400` — invalid status, self-referencing parent, cycle detected, project mismatch
363
+
364
+ ---
365
+
366
+ ### `DELETE /api/tasks/:id`
367
+
368
+ Hard-deletes a task and its entire subtree (all descendants, comments, and updates).
369
+
370
+ **Example** `DELETE /api/tasks/tRYxSsybNq`
371
+
372
+ **Response** `200`
373
+ ```json
374
+ { "ok": true }
375
+ ```
376
+
377
+ **Errors**
378
+ - `404` — task not found
379
+
380
+ ---
381
+
382
+ ## Task Hierarchy
383
+
384
+ ### `GET /api/tasks/:id/ancestors`
385
+
386
+ Returns the ancestor path from root down to (optionally including) the task.
387
+
388
+ **Query Parameters**
389
+
390
+ | Param | Type | Default | Description |
391
+ |-------|------|---------|-------------|
392
+ | `include_self` | boolean | `false` | Include the task itself at the end of the path |
393
+
394
+ **Example** `GET /api/tasks/dzUFSKZQRr/ancestors?include_self=1`
395
+
396
+ **Response** `200`
397
+ ```json
398
+ {
399
+ "path": [
400
+ {
401
+ "id": "bY0pcKdSUx",
402
+ "title": "Root Task",
403
+ "status": "in_progress",
404
+ "parent_id": null,
405
+ "depth": 2,
406
+ "...": "other task fields"
407
+ },
408
+ {
409
+ "id": "OnXAVEF7D1",
410
+ "title": "Child Task",
411
+ "parent_id": "bY0pcKdSUx",
412
+ "depth": 1,
413
+ "...": "..."
414
+ },
415
+ {
416
+ "id": "dzUFSKZQRr",
417
+ "title": "Grandchild Task",
418
+ "parent_id": "OnXAVEF7D1",
419
+ "depth": 0,
420
+ "...": "..."
421
+ }
422
+ ]
423
+ }
424
+ ```
425
+
426
+ Path is ordered root → leaf. `depth` counts distance from the target task (0 = self).
427
+
428
+ **Errors**
429
+ - `404` — task not found
430
+
431
+ ---
432
+
433
+ ### `GET /api/tasks/:id/descendants`
434
+
435
+ Returns all descendants of a task.
436
+
437
+ **Query Parameters**
438
+
439
+ | Param | Type | Default | Description |
440
+ |-------|------|---------|-------------|
441
+ | `mode` | string | `"flat"` | `"flat"` for a flat list, `"tree"` for nested structure |
442
+ | `include_self` | boolean | `false` | Include the root task itself |
443
+
444
+ **Example (flat)** `GET /api/tasks/bY0pcKdSUx/descendants?mode=flat`
445
+
446
+ **Response** `200`
447
+ ```json
448
+ {
449
+ "descendants": [
450
+ {
451
+ "id": "OnXAVEF7D1",
452
+ "title": "Child Task",
453
+ "parent_id": "bY0pcKdSUx",
454
+ "depth": 1,
455
+ "...": "..."
456
+ },
457
+ {
458
+ "id": "dzUFSKZQRr",
459
+ "title": "Grandchild Task",
460
+ "parent_id": "OnXAVEF7D1",
461
+ "depth": 2,
462
+ "...": "..."
463
+ }
464
+ ]
465
+ }
466
+ ```
467
+
468
+ **Example (tree)** `GET /api/tasks/bY0pcKdSUx/descendants?mode=tree`
469
+
470
+ **Response** `200`
471
+ ```json
472
+ {
473
+ "tree": {
474
+ "task": { "id": "bY0pcKdSUx", "title": "Root Task", "...": "..." },
475
+ "depth": 0,
476
+ "children": [
477
+ {
478
+ "task": { "id": "OnXAVEF7D1", "title": "Child Task", "...": "..." },
479
+ "depth": 1,
480
+ "children": [
481
+ {
482
+ "task": { "id": "dzUFSKZQRr", "title": "Grandchild Task", "...": "..." },
483
+ "depth": 2,
484
+ "children": []
485
+ }
486
+ ]
487
+ }
488
+ ]
489
+ }
490
+ }
491
+ ```
492
+
493
+ **Errors**
494
+ - `404` — task not found
495
+
496
+ ---
497
+
498
+ ### `GET /api/tasks/:id/context`
499
+
500
+ Returns a full context bundle for a task: project info, ancestor path, children, and a rendered text summary suitable for LLM consumption.
501
+
502
+ **Example** `GET /api/tasks/OnXAVEF7D1/context`
503
+
504
+ **Response** `200`
505
+ ```json
506
+ {
507
+ "project": { "id": "pH1ZSGuW5i", "name": "My Project", "...": "..." },
508
+ "path": [
509
+ { "id": "bY0pcKdSUx", "title": "Root Task", "depth": 1, "...": "..." }
510
+ ],
511
+ "task": { "id": "OnXAVEF7D1", "title": "Child Task", "...": "..." },
512
+ "children": [
513
+ { "id": "dzUFSKZQRr", "title": "Grandchild Task", "...": "..." }
514
+ ],
515
+ "agent_instructions": "Project: My Project (#pH1ZSGuW5i)\n\nProject scope:\n...\n\nTicket path (root -> selected):\n- [in_progress] Root Task (#bY0pcKdSUx)\n\nSelected ticket details:\nTitle: Child Task\n..."
516
+ }
517
+ ```
518
+
519
+ **Errors**
520
+ - `404` — task not found
521
+
522
+ ---
523
+
524
+ ## Next Sub-ticket
525
+
526
+ ### `POST /api/tasks/:id/next`
527
+
528
+ Picks the next best leaf sub-ticket to work on. Supports heuristic (local sort) or LLM-powered selection.
529
+
530
+ **Request**
531
+ ```json
532
+ {
533
+ "strategy": "auto",
534
+ "exclude_status": ["done"],
535
+ "prefer_status": ["in_progress", "todo", "review", "testing", "ideas"],
536
+ "max_candidates": 40
537
+ }
538
+ ```
539
+
540
+ | Field | Type | Required | Default |
541
+ |-------|------|----------|---------|
542
+ | `strategy` | string | no | `"auto"` — uses LLM if available, falls back to heuristic |
543
+ | `exclude_status` | string or string[] | no | `["done"]` |
544
+ | `prefer_status` | string or string[] | no | `["in_progress","todo","review","testing","ideas"]` |
545
+ | `max_candidates` | number | no | `40` |
546
+
547
+ **Strategy options**
548
+ - `"auto"` — tries LLM first, falls back to heuristic on failure
549
+ - `"llm"` — always uses LLM (fails if LLM unavailable)
550
+ - `"heuristic"` — never uses LLM; sorts by prefer_status order then oldest first
551
+
552
+ **Response** `200` (task found)
553
+ ```json
554
+ {
555
+ "selected": {
556
+ "id": "dzUFSKZQRr",
557
+ "title": "Grandchild Task",
558
+ "status": "todo",
559
+ "...": "..."
560
+ },
561
+ "strategy": "llm",
562
+ "rationale": "The database schema is the foundational dependency...",
563
+ "llm_run_id": "abc123",
564
+ "candidate_count": 5,
565
+ "context": {
566
+ "project": { "...": "..." },
567
+ "path": [ "..." ],
568
+ "task": { "...": "..." },
569
+ "children": [ "..." ],
570
+ "agent_instructions": "..."
571
+ }
572
+ }
573
+ ```
574
+
575
+ **Response** `200` (no candidates)
576
+ ```json
577
+ {
578
+ "selected": null,
579
+ "strategy": "none",
580
+ "rationale": "No leaf sub-tickets found...",
581
+ "candidate_count": 0
582
+ }
583
+ ```
584
+
585
+ **Errors**
586
+ - `404` — task not found
587
+
588
+ ---
589
+
590
+ ## Comments & Progress Updates
591
+
592
+ ### `POST /api/tasks/:id/comments`
593
+
594
+ Adds a comment to a task.
595
+
596
+ **Request**
597
+ ```json
598
+ {
599
+ "text": "Need to clarify requirements with stakeholder",
600
+ "author": "Gabor"
601
+ }
602
+ ```
603
+
604
+ | Field | Type | Required | Default |
605
+ |-------|------|----------|---------|
606
+ | `text` | string | yes | — |
607
+ | `author` | string | no | `"Gabor"` |
608
+
609
+ **Response** `201`
610
+ ```json
611
+ {
612
+ "comment": {
613
+ "id": 4,
614
+ "entity_type": "task",
615
+ "entity_id": "bY0pcKdSUx",
616
+ "author": "Gabor",
617
+ "text": "Need to clarify requirements with stakeholder",
618
+ "created_at": 1770572931850
619
+ }
620
+ }
621
+ ```
622
+
623
+ **Side effects** — bumps the task's `updated_at`
624
+
625
+ **Errors**
626
+ - `404` — task not found
627
+ - `400` — `text` is missing
628
+
629
+ ---
630
+
631
+ ### `POST /api/tasks/:id/updates`
632
+
633
+ Adds a progress update to a task.
634
+
635
+ **Request**
636
+ ```json
637
+ {
638
+ "text": "Completed the database schema migration",
639
+ "author": "Jarvis"
640
+ }
641
+ ```
642
+
643
+ | Field | Type | Required | Default |
644
+ |-------|------|----------|---------|
645
+ | `text` | string | yes | — |
646
+ | `author` | string | no | `"Jarvis"` |
647
+
648
+ **Response** `201`
649
+ ```json
650
+ {
651
+ "update": {
652
+ "id": 299,
653
+ "entity_type": "task",
654
+ "entity_id": "bY0pcKdSUx",
655
+ "author": "Jarvis",
656
+ "text": "Completed the database schema migration",
657
+ "created_at": 1770572935085
658
+ }
659
+ }
660
+ ```
661
+
662
+ **Side effects** — bumps the task's `updated_at`
663
+
664
+ **Errors**
665
+ - `404` — task not found
666
+ - `400` — `text` is missing
667
+
668
+ ---
669
+
670
+ ## Automation Tasks
671
+
672
+ Automation tasks come in two types: `recurring` (cron-based) and `scheduled` (one-shot at a specific time).
673
+
674
+ ### `GET /api/automation/:type`
675
+
676
+ Lists automation tasks of a given type.
677
+
678
+ **URL parameters** — `:type` is `recurring` or `scheduled`
679
+
680
+ **Query Parameters**
681
+
682
+ | Param | Type | Description |
683
+ |-------|------|-------------|
684
+ | `status` | string | Filter by status (`ideas` or `active`) |
685
+ | `project_id` | string | Filter by project |
686
+
687
+ **Example** `GET /api/automation/recurring?status=active`
688
+
689
+ **Response** `200`
690
+ ```json
691
+ {
692
+ "tasks": [
693
+ {
694
+ "id": "44d4003vU2",
695
+ "type": "recurring",
696
+ "title": "Weekly Review",
697
+ "description": "Review all tasks weekly",
698
+ "status": "active",
699
+ "detail_text": "",
700
+ "project_id": "pH1ZSGuW5i",
701
+ "schedule_kind": "cron",
702
+ "expr": "0 9 * * 1",
703
+ "tz": "America/New_York",
704
+ "at_iso": null,
705
+ "cron_job_id": null,
706
+ "cron_enabled": 1,
707
+ "last_synced_at": null,
708
+ "last_sync_error": null,
709
+ "created_at": 1770572993713,
710
+ "updated_at": 1770573019024
711
+ }
712
+ ]
713
+ }
714
+ ```
715
+
716
+ ---
717
+
718
+ ### `POST /api/automation/:type`
719
+
720
+ Creates a new automation task.
721
+
722
+ **URL parameters** — `:type` is `recurring` or `scheduled`
723
+
724
+ **Request (recurring/cron)**
725
+ ```json
726
+ {
727
+ "title": "Weekly Review",
728
+ "description": "Review all tasks weekly",
729
+ "schedule_kind": "cron",
730
+ "expr": "0 9 * * 1",
731
+ "tz": "America/New_York",
732
+ "project_id": "pH1ZSGuW5i"
733
+ }
734
+ ```
735
+
736
+ **Request (scheduled/at)**
737
+ ```json
738
+ {
739
+ "title": "Deploy Release",
740
+ "description": "Deploy v2.0 release",
741
+ "schedule_kind": "at",
742
+ "at_iso": "2026-03-01T10:00:00Z",
743
+ "project_id": "pH1ZSGuW5i"
744
+ }
745
+ ```
746
+
747
+ | Field | Type | Required | Default | Notes |
748
+ |-------|------|----------|---------|-------|
749
+ | `title` | string | yes | — | |
750
+ | `description` | string | no | `""` | |
751
+ | `status` | string | no | `"ideas"` | Must be `ideas` or `active` |
752
+ | `detail_text` | string | no | `""` | |
753
+ | `project_id` | string | no | `"default"` | |
754
+ | `schedule_kind` | string | yes | — | `"cron"` or `"at"` |
755
+ | `expr` | string | if cron | — | Cron expression (e.g. `0 9 * * 1`) |
756
+ | `tz` | string | if cron | — | Timezone (e.g. `America/New_York`) |
757
+ | `at_iso` | string | if at | — | ISO 8601 datetime |
758
+
759
+ **Constraints**
760
+ - Recurring tasks must use `schedule_kind: "cron"` (cannot use `"at"`)
761
+
762
+ **Response** `201` — returns the automation task object
763
+
764
+ **Errors**
765
+ - `400` — missing required fields, invalid schedule_kind, recurring with `at` kind
766
+
767
+ ---
768
+
769
+ ### `GET /api/automation/:type/:id`
770
+
771
+ Returns an automation task with its comments and updates.
772
+
773
+ **Example** `GET /api/automation/recurring/44d4003vU2`
774
+
775
+ **Response** `200`
776
+ ```json
777
+ {
778
+ "task": { "...": "automation task object" },
779
+ "comments": [ "..." ],
780
+ "updates": [ "..." ]
781
+ }
782
+ ```
783
+
784
+ **Errors**
785
+ - `404` — task not found
786
+ - `400` — invalid type
787
+
788
+ ---
789
+
790
+ ### `PATCH /api/automation/:type/:id`
791
+
792
+ Updates an automation task. Only provided fields are changed.
793
+
794
+ **Request**
795
+ ```json
796
+ {
797
+ "title": "Updated Weekly Review",
798
+ "status": "active",
799
+ "expr": "0 10 * * 1"
800
+ }
801
+ ```
802
+
803
+ All fields are optional. Schedule fields (`schedule_kind`, `expr`, `tz`, `at_iso`) are re-validated together when any of them change.
804
+
805
+ **Special behavior**
806
+ - Setting `status: "ideas"` automatically sets `cron_enabled: 0`
807
+ - Setting `status: "active"` automatically sets `cron_enabled: 1`
808
+
809
+ **Additional fields** (for sync agent use)
810
+
811
+ | Field | Type | Description |
812
+ |-------|------|-------------|
813
+ | `cron_job_id` | string | External cron service job ID |
814
+ | `cron_enabled` | boolean | Whether cron is enabled |
815
+ | `last_synced_at` | number | Last sync timestamp (ms) |
816
+ | `last_sync_error` | string | Last sync error message |
817
+
818
+ **Response** `200` — returns updated automation task object
819
+
820
+ **Errors**
821
+ - `404` — task not found
822
+ - `400` — invalid type, status, or schedule
823
+
824
+ ---
825
+
826
+ ### `DELETE /api/automation/:type/:id`
827
+
828
+ Deletes an automation task and its comments/updates.
829
+
830
+ **Response** `200`
831
+ ```json
832
+ { "ok": true }
833
+ ```
834
+
835
+ **Errors**
836
+ - `404` — task not found
837
+ - `400` — invalid type
838
+
839
+ ---
840
+
841
+ ### `POST /api/automation/:type/:id/comments`
842
+
843
+ Adds a comment to an automation task. Same interface as task comments.
844
+
845
+ **Request**
846
+ ```json
847
+ {
848
+ "text": "Adjusted schedule to run at 10am",
849
+ "author": "Gabor"
850
+ }
851
+ ```
852
+
853
+ **Response** `201` — comment object with `entity_type` set to `"recurring"` or `"scheduled"`
854
+
855
+ ---
856
+
857
+ ### `POST /api/automation/:type/:id/updates`
858
+
859
+ Adds a progress update to an automation task. Same interface as task updates.
860
+
861
+ **Request**
862
+ ```json
863
+ {
864
+ "text": "Synced with cron-job.org successfully",
865
+ "author": "Jarvis"
866
+ }
867
+ ```
868
+
869
+ **Response** `201` — update object with `entity_type` set to `"recurring"` or `"scheduled"`
870
+
871
+ ---
872
+
873
+ ## Automation Helpers
874
+
875
+ These endpoints are designed for the external sync agent.
876
+
877
+ ### `GET /api/automation-active`
878
+
879
+ Returns all automation tasks with `status = 'active'` (both recurring and scheduled).
880
+
881
+ **Response** `200`
882
+ ```json
883
+ {
884
+ "tasks": [ "...automation task objects..." ]
885
+ }
886
+ ```
887
+
888
+ ---
889
+
890
+ ### `GET /api/automation-sync-snapshot`
891
+
892
+ Returns all automation tasks (regardless of status) for full sync.
893
+
894
+ **Response** `200`
895
+ ```json
896
+ {
897
+ "tasks": [ "...automation task objects..." ]
898
+ }
899
+ ```
900
+
901
+ ---
902
+
903
+ ## AI: Task Merge
904
+
905
+ ### `POST /api/ai/tasks/merge`
906
+
907
+ Creates a new task from a natural-language description. The LLM enhances the title/description and optionally breaks it down into sub-tasks.
908
+
909
+ **Request**
910
+ ```json
911
+ {
912
+ "text": "Implement user authentication with login, registration, and password reset",
913
+ "project_id": "pH1ZSGuW5i",
914
+ "breakdown": true,
915
+ "max_depth": 2,
916
+ "status": "todo",
917
+ "parent_id": "bY0pcKdSUx",
918
+ "apply": true
919
+ }
920
+ ```
921
+
922
+ | Field | Type | Required | Default | Notes |
923
+ |-------|------|----------|---------|-------|
924
+ | `text` | string | yes | — | Natural-language task description |
925
+ | `project_id` | string | no | `"default"` | |
926
+ | `breakdown` | boolean | no | `false` | Create sub-tasks |
927
+ | `max_depth` | number | no | `3` | Max nesting depth (1–6) |
928
+ | `status` | string | no | `"todo"` | Root task status |
929
+ | `parent_id` | string | no | `null` | Attach under existing task |
930
+ | `apply` | boolean | no | `true` | `false` for dry-run |
931
+
932
+ **Response (apply=true)** `200`
933
+ ```json
934
+ {
935
+ "plan_id": "QyOvX8gckSoR",
936
+ "llm_run_id": "abc123def456",
937
+ "applied": true,
938
+ "rationale": "Created task with 6 sub-tasks covering backend and frontend...",
939
+ "root_task": {
940
+ "id": "LkpwlcsGpj",
941
+ "title": "Implement User Authentication System",
942
+ "description": "Full authentication system with login, registration...",
943
+ "status": "todo",
944
+ "parent_id": null,
945
+ "project_id": "pH1ZSGuW5i",
946
+ "...": "..."
947
+ },
948
+ "created_tasks": [
949
+ { "id": "LkpwlcsGpj", "title": "Implement User Authentication System", "parent_id": null, "...": "..." },
950
+ { "id": "6eQIP0ar3U", "title": "Backend: User Database Schema", "parent_id": "LkpwlcsGpj", "...": "..." },
951
+ { "id": "xUGDlxxdnP", "title": "Backend: Auth API Endpoints", "parent_id": "LkpwlcsGpj", "...": "..." }
952
+ ]
953
+ }
954
+ ```
955
+
956
+ **Response (apply=false, dry-run)** `200`
957
+ ```json
958
+ {
959
+ "plan_id": "vdZScM0uu4It",
960
+ "llm_run_id": "abc123def456",
961
+ "applied": false,
962
+ "plan": {
963
+ "actions": [
964
+ {
965
+ "type": "create_task",
966
+ "temp_id": "t0",
967
+ "title": "Implement Dark Mode Support",
968
+ "description": "Add a global dark mode toggle...",
969
+ "detail_text": "",
970
+ "status": "todo",
971
+ "parent_id": null,
972
+ "parent_temp_id": null
973
+ },
974
+ {
975
+ "type": "create_task",
976
+ "temp_id": "t1",
977
+ "title": "Theme Configuration Store",
978
+ "parent_temp_id": "t0",
979
+ "...": "..."
980
+ }
981
+ ],
982
+ "root_temp_id": "t0",
983
+ "rationale": "Breaking down into theme store, component updates, and toggle UI..."
984
+ }
985
+ }
986
+ ```
987
+
988
+ **Errors**
989
+ - `400` — missing `text`, invalid status/project/parent, LLM not enabled
990
+ - `502` — LLM response parsing failure
991
+
992
+ ---
993
+
994
+ ## AI: Project Init
995
+
996
+ ### `POST /api/ai/projects/:id/init`
997
+
998
+ Scans a local repository and generates a project scope description. Optionally creates seed tasks.
999
+
1000
+ **Request**
1001
+ ```json
1002
+ {
1003
+ "path": "c:/xampp/htdocs/KanbanDashboard",
1004
+ "create_seed_tasks": true,
1005
+ "max_depth": 3,
1006
+ "apply": true
1007
+ }
1008
+ ```
1009
+
1010
+ | Field | Type | Required | Default | Notes |
1011
+ |-------|------|----------|---------|-------|
1012
+ | `path` | string | yes | — | Local filesystem path to scan |
1013
+ | `create_seed_tasks` | boolean | no | `false` | Generate initial tasks |
1014
+ | `max_depth` | number | no | `3` | Max task nesting (1–6) |
1015
+ | `apply` | boolean | no | `true` | `false` for dry-run |
1016
+
1017
+ **Path restrictions** — controlled by environment variables:
1018
+ - `AI_INIT_ALLOWED_ROOTS` — comma-separated list of allowed root paths
1019
+ - `AI_INIT_ALLOW_ANY=1` — allow any path (for development)
1020
+
1021
+ **Response (apply=true)** `200`
1022
+ ```json
1023
+ {
1024
+ "plan_id": "abc123",
1025
+ "llm_run_id": "def456",
1026
+ "applied": true,
1027
+ "project": {
1028
+ "id": "pH1ZSGuW5i",
1029
+ "name": "My Project",
1030
+ "scope_text": "# Jarvis Kanban Dashboard\n\nAn intelligent task management system...",
1031
+ "...": "..."
1032
+ },
1033
+ "scope_text": "# Jarvis Kanban Dashboard\n\nAn intelligent task management system...",
1034
+ "created_tasks": [
1035
+ { "id": "J8ijTp_Mr_", "title": "Implement Cron Sync Agent", "parent_id": null, "...": "..." },
1036
+ { "id": "WUNcgBsi5r", "title": "Add Frontend Tests", "parent_id": null, "...": "..." }
1037
+ ]
1038
+ }
1039
+ ```
1040
+
1041
+ The project's `scope_text` is updated in the database when `apply=true`.
1042
+
1043
+ **Errors**
1044
+ - `404` — project not found
1045
+ - `400` — path not allowed, LLM not enabled
1046
+ - `502` — LLM response parsing failure
1047
+
1048
+ ---
1049
+
1050
+ ## AI: Evolve Hierarchy
1051
+
1052
+ ### `POST /api/ai/projects/:id/evolve`
1053
+
1054
+ Proposes and optionally creates new tasks to advance a project toward a goal.
1055
+
1056
+ **Request**
1057
+ ```json
1058
+ {
1059
+ "goal": "Add comprehensive error handling and logging across all API endpoints",
1060
+ "max_depth": 3,
1061
+ "max_new_tasks": 15,
1062
+ "apply": true
1063
+ }
1064
+ ```
1065
+
1066
+ | Field | Type | Required | Default | Notes |
1067
+ |-------|------|----------|---------|-------|
1068
+ | `goal` | string | no | `""` | Direction for evolution |
1069
+ | `max_depth` | number | no | `3` | Max nesting (1–6) |
1070
+ | `max_new_tasks` | number | no | `15` | Max tasks to create (1–50) |
1071
+ | `apply` | boolean | no | `true` | `false` for dry-run |
1072
+
1073
+ **Response (apply=true)** `200`
1074
+ ```json
1075
+ {
1076
+ "plan_id": "j527T-MwDM2a",
1077
+ "llm_run_id": "abc123",
1078
+ "applied": true,
1079
+ "rationale": "Created error handling epic with 5 sub-tasks...",
1080
+ "created_tasks": [
1081
+ { "id": "3ACdJSHHJx", "title": "Implement Comprehensive Error Handling", "parent_id": null, "...": "..." },
1082
+ { "id": "ePdlgfl6Ex", "title": "Backend: Integrate Structured Logger", "parent_id": "3ACdJSHHJx", "...": "..." }
1083
+ ]
1084
+ }
1085
+ ```
1086
+
1087
+ New tasks default to `status: "ideas"`.
1088
+
1089
+ **Response (apply=false)** `200`
1090
+ ```json
1091
+ {
1092
+ "plan_id": "qr-yO9eaQhGT",
1093
+ "llm_run_id": "abc123",
1094
+ "applied": false,
1095
+ "plan": {
1096
+ "actions": [ "...create_task actions..." ],
1097
+ "rationale": "..."
1098
+ }
1099
+ }
1100
+ ```
1101
+
1102
+ **Errors**
1103
+ - `404` — project not found
1104
+ - `400` — LLM not enabled
1105
+ - `502` — LLM response parsing failure
1106
+
1107
+ ---
1108
+
1109
+ ## AI: Remove Concept
1110
+
1111
+ ### `POST /api/ai/projects/:id/remove-concept`
1112
+
1113
+ Uses the LLM to identify tasks related to a concept and either soft-deletes them or moves them to the Ideas column.
1114
+
1115
+ **Request**
1116
+ ```json
1117
+ {
1118
+ "concept": "CI/CD pipeline",
1119
+ "mode": "soft_delete",
1120
+ "apply": true
1121
+ }
1122
+ ```
1123
+
1124
+ | Field | Type | Required | Default | Notes |
1125
+ |-------|------|----------|---------|-------|
1126
+ | `concept` | string | yes | — | The concept to remove |
1127
+ | `mode` | string | no | `"soft_delete"` | `"soft_delete"` or `"move_to_ideas"` |
1128
+ | `apply` | boolean | no | `true` | `false` for dry-run |
1129
+
1130
+ **Response (apply=true)** `200`
1131
+ ```json
1132
+ {
1133
+ "plan_id": "cLO1xMRFvlKn",
1134
+ "llm_run_id": "c_9np7nnEVBC",
1135
+ "applied": true,
1136
+ "rationale": "Removing the CI/CD task as requested...",
1137
+ "affected_ids": ["8yNV7C0S4K"],
1138
+ "affected_count": 1
1139
+ }
1140
+ ```
1141
+
1142
+ **Mode behavior**
1143
+ - `soft_delete` — sets `deleted_at` on matched tasks; they no longer appear in listings
1144
+ - `move_to_ideas` — sets `status = 'ideas'` on matched tasks
1145
+
1146
+ **Errors**
1147
+ - `404` — project not found
1148
+ - `400` — missing concept, LLM not enabled
1149
+ - `502` — LLM response parsing failure
1150
+
1151
+ ---
1152
+
1153
+ ## Error Handling
1154
+
1155
+ All errors return JSON:
1156
+
1157
+ ```json
1158
+ { "error": "description of the error" }
1159
+ ```
1160
+
1161
+ **Status codes**
1162
+
1163
+ | Code | Meaning |
1164
+ |------|---------|
1165
+ | `400` | Bad request — validation error, missing required field |
1166
+ | `401` | Unauthorized — invalid or missing `X-API-Key` (AI endpoints only, when configured) |
1167
+ | `404` | Not found — resource doesn't exist or is soft-deleted |
1168
+ | `429` | Rate limited — AI endpoint rate limit exceeded |
1169
+ | `502` | Bad gateway — LLM returned unparseable or invalid response |
1170
+
1171
+ **API route guard** — any `GET /api/*` route that doesn't match a defined endpoint returns `404` JSON (not the SPA HTML fallback). Non-API routes serve the frontend.
1172
+
1173
+ ---
1174
+
1175
+ ## Object Schemas
1176
+
1177
+ ### Task
1178
+
1179
+ ```typescript
1180
+ {
1181
+ id: string, // nanoid
1182
+ title: string,
1183
+ description: string,
1184
+ status: string, // "ideas" | "todo" | "in_progress" | "review" | "testing" | "done"
1185
+ detail_text: string,
1186
+ project_id: string,
1187
+ parent_id: string | null,
1188
+ deleted_at: number | null, // soft-delete timestamp (ms) or null
1189
+ created_at: number, // Unix ms
1190
+ updated_at: number // Unix ms
1191
+ }
1192
+ ```
1193
+
1194
+ ### Automation Task
1195
+
1196
+ ```typescript
1197
+ {
1198
+ id: string,
1199
+ type: "recurring" | "scheduled",
1200
+ title: string,
1201
+ description: string,
1202
+ status: string, // "ideas" | "active"
1203
+ detail_text: string,
1204
+ project_id: string,
1205
+ schedule_kind: "cron" | "at",
1206
+ expr: string | null, // cron expression
1207
+ tz: string | null, // timezone
1208
+ at_iso: string | null, // ISO 8601 datetime
1209
+ cron_job_id: string | null, // external cron service ID
1210
+ cron_enabled: 0 | 1,
1211
+ last_synced_at: number | null,
1212
+ last_sync_error: string | null,
1213
+ created_at: number,
1214
+ updated_at: number
1215
+ }
1216
+ ```
1217
+
1218
+ ### Project
1219
+
1220
+ ```typescript
1221
+ {
1222
+ id: string,
1223
+ name: string,
1224
+ scope_text: string,
1225
+ created_at: number,
1226
+ updated_at: number,
1227
+ task_count?: number, // only in GET /api/projects list
1228
+ automation_count?: number // only in GET /api/projects list
1229
+ }
1230
+ ```
1231
+
1232
+ ### Comment
1233
+
1234
+ ```typescript
1235
+ {
1236
+ id: number,
1237
+ entity_type: "task" | "recurring" | "scheduled",
1238
+ entity_id: string,
1239
+ author: string,
1240
+ text: string,
1241
+ created_at: number
1242
+ }
1243
+ ```
1244
+
1245
+ ### Progress Update
1246
+
1247
+ ```typescript
1248
+ {
1249
+ id: number,
1250
+ entity_type: "task" | "recurring" | "scheduled",
1251
+ entity_id: string,
1252
+ author: string,
1253
+ text: string,
1254
+ created_at: number
1255
+ }
1256
+ ```