agkan 2.10.0 → 2.12.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.
Files changed (48) hide show
  1. package/README.ja.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/board/boardRenderer.d.ts +18 -0
  4. package/dist/board/boardRenderer.d.ts.map +1 -0
  5. package/dist/board/boardRenderer.js +273 -0
  6. package/dist/board/boardRenderer.js.map +1 -0
  7. package/dist/board/boardRoutes.d.ts +23 -0
  8. package/dist/board/boardRoutes.d.ts.map +1 -0
  9. package/dist/board/boardRoutes.js +273 -0
  10. package/dist/board/boardRoutes.js.map +1 -0
  11. package/dist/board/boardScript.d.ts +2 -0
  12. package/dist/board/boardScript.d.ts.map +1 -0
  13. package/dist/board/boardScript.js +1202 -0
  14. package/dist/board/boardScript.js.map +1 -0
  15. package/dist/board/boardStyles.d.ts +2 -0
  16. package/dist/board/boardStyles.d.ts.map +1 -0
  17. package/dist/board/boardStyles.js +171 -0
  18. package/dist/board/boardStyles.js.map +1 -0
  19. package/dist/board/client/board.js +1160 -0
  20. package/dist/board/server.d.ts +3 -1
  21. package/dist/board/server.d.ts.map +1 -1
  22. package/dist/board/server.js +14 -1301
  23. package/dist/board/server.js.map +1 -1
  24. package/dist/cli/commands/init.d.ts.map +1 -1
  25. package/dist/cli/commands/init.js +31 -4
  26. package/dist/cli/commands/init.js.map +1 -1
  27. package/dist/cli/commands/task/update.d.ts.map +1 -1
  28. package/dist/cli/commands/task/update.js +1 -3
  29. package/dist/cli/commands/task/update.js.map +1 -1
  30. package/dist/db/adapters/sqlite-adapter.js +2 -0
  31. package/dist/db/adapters/sqlite-adapter.js.map +1 -1
  32. package/dist/db/connection.js +2 -2
  33. package/dist/db/connection.js.map +1 -1
  34. package/dist/services/CommentService.d.ts +7 -0
  35. package/dist/services/CommentService.d.ts.map +1 -1
  36. package/dist/services/CommentService.js +25 -0
  37. package/dist/services/CommentService.js.map +1 -1
  38. package/dist/services/MetadataService.js +1 -0
  39. package/dist/services/MetadataService.js.map +1 -1
  40. package/dist/services/TagService.js +1 -0
  41. package/dist/services/TagService.js.map +1 -1
  42. package/dist/services/TaskBlockService.js +2 -0
  43. package/dist/services/TaskBlockService.js.map +1 -1
  44. package/dist/services/TaskService.js +1 -0
  45. package/dist/services/TaskService.js.map +1 -1
  46. package/dist/services/TaskTagService.js +3 -0
  47. package/dist/services/TaskTagService.js.map +1 -1
  48. package/package.json +8 -5
package/README.ja.md CHANGED
@@ -27,7 +27,7 @@ agkanをClaude Codeのスキル(タスクの自動実行、プランニング
27
27
 
28
28
  ### 前提条件
29
29
 
30
- - Node.js 18以上
30
+ - Node.js 20以上
31
31
  - npm
32
32
 
33
33
  ### npmからインストール(推奨)
package/README.md CHANGED
@@ -28,7 +28,7 @@ To use agkan with Claude Code skills (automated task execution, planning, review
28
28
 
29
29
  ### Prerequisites
30
30
 
31
- - Node.js 18 or higher
31
+ - Node.js 20 or higher
32
32
  - npm
33
33
 
34
34
  ### Install from npm (Recommended)
@@ -0,0 +1,18 @@
1
+ import { Task, TaskStatus } from '../models';
2
+ import { Tag } from '../models/Tag';
3
+ import { StorageProvider } from '../db/types/storage';
4
+ export declare const STATUSES: TaskStatus[];
5
+ export declare const STATUS_LABELS: Record<TaskStatus, string>;
6
+ export declare const STATUS_COLORS: Record<TaskStatus, string>;
7
+ export declare function renderCard(task: Task, tags: Tag[]): string;
8
+ export declare function renderColumn(status: TaskStatus, tasks: Task[], tagMap: Map<number, Tag[]>): string;
9
+ export declare function renderBoard(tasksByStatus: Map<TaskStatus, Task[]>, tagMap: Map<number, Tag[]>, boardTitle?: string): string;
10
+ export declare function sortByPriority(tasks: Task[]): Task[];
11
+ export declare function buildBoardCardsPayload(tasksByStatus: Map<TaskStatus, Task[]>, tagMap: Map<number, Tag[]>): {
12
+ status: TaskStatus;
13
+ html: string;
14
+ count: number;
15
+ }[];
16
+ export declare function buildTasksByStatus(tasks: Task[]): Map<TaskStatus, Task[]>;
17
+ export declare function getBoardUpdatedAt(database: StorageProvider): string | null;
18
+ //# sourceMappingURL=boardRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardRenderer.d.ts","sourceRoot":"","sources":["../../src/board/boardRenderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,UAAU,EAA8B,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,eAAO,MAAM,QAAQ,EAAE,UAAU,EAA8E,CAAC;AAEhH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAQpD,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAQpD,CAAC;AAWF,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAgB1D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAkBlG;AA2FD,wBAAgB,WAAW,CACzB,aAAa,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,EACtC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAC1B,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAuCR;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAMpD;AAED,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,EACtC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GACzB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAMvD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAYzE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAmB1E"}
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.STATUS_COLORS = exports.STATUS_LABELS = exports.STATUSES = void 0;
37
+ exports.renderCard = renderCard;
38
+ exports.renderColumn = renderColumn;
39
+ exports.renderBoard = renderBoard;
40
+ exports.sortByPriority = sortByPriority;
41
+ exports.buildBoardCardsPayload = buildBoardCardsPayload;
42
+ exports.buildTasksByStatus = buildTasksByStatus;
43
+ exports.getBoardUpdatedAt = getBoardUpdatedAt;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const models_1 = require("../models");
47
+ const boardStyles_1 = require("./boardStyles");
48
+ exports.STATUSES = ['icebox', 'backlog', 'ready', 'in_progress', 'review', 'done', 'closed'];
49
+ exports.STATUS_LABELS = {
50
+ icebox: 'Icebox',
51
+ backlog: 'Backlog',
52
+ ready: 'Ready',
53
+ in_progress: 'In Progress',
54
+ review: 'Review',
55
+ done: 'Done',
56
+ closed: 'Closed',
57
+ };
58
+ exports.STATUS_COLORS = {
59
+ icebox: '#6b7280',
60
+ backlog: '#3b82f6',
61
+ ready: '#8b5cf6',
62
+ in_progress: '#f97316',
63
+ review: '#eab308',
64
+ done: '#22c55e',
65
+ closed: '#374151',
66
+ };
67
+ function escapeHtml(text) {
68
+ return text
69
+ .replace(/&/g, '&amp;')
70
+ .replace(/</g, '&lt;')
71
+ .replace(/>/g, '&gt;')
72
+ .replace(/"/g, '&quot;')
73
+ .replace(/'/g, '&#039;');
74
+ }
75
+ function renderCard(task, tags) {
76
+ const priority = task.priority;
77
+ const priorityBadge = priority
78
+ ? `<span class="priority priority-${escapeHtml(priority)}">${escapeHtml(priority)}</span>`
79
+ : '';
80
+ const tagBadges = tags.map((t) => `<span class="tag">${escapeHtml(t.name)}</span>`).join('');
81
+ return `
82
+ <div class="card" draggable="true" data-id="${task.id}" data-status="${task.status}">
83
+ <div class="card-header">
84
+ <span class="card-id">#${task.id}</span>
85
+ ${priorityBadge}
86
+ </div>
87
+ <div class="card-title">${escapeHtml(task.title)}</div>
88
+ ${tagBadges ? `<div class="card-tags">${tagBadges}</div>` : ''}
89
+ </div>`;
90
+ }
91
+ function renderColumn(status, tasks, tagMap) {
92
+ const color = exports.STATUS_COLORS[status];
93
+ const label = exports.STATUS_LABELS[status];
94
+ const cards = tasks.map((t) => renderCard(t, tagMap.get(t.id) || [])).join('');
95
+ return `
96
+ <div class="column" data-status="${status}">
97
+ <div class="column-header" style="border-top-color:${color}">
98
+ <span class="column-title" style="color:${color}">${label}</span>
99
+ <span class="column-header-right">
100
+ <span class="column-count">${tasks.length}</span>
101
+ <button class="add-btn" data-status="${status}" title="Add task">+</button>
102
+ </span>
103
+ </div>
104
+ <div class="column-body" id="col-${status}">
105
+ ${cards}
106
+ </div>
107
+ </div>`;
108
+ }
109
+ const BOARD_PRIORITY_OPTIONS = models_1.PRIORITIES.map((p) => `<option value="${p}">${p.charAt(0).toUpperCase() + p.slice(1)}</option>`).join('\n ');
110
+ function getAddTaskModal() {
111
+ return `
112
+ <div class="modal-overlay" id="add-modal">
113
+ <div class="modal">
114
+ <h2>Add Task</h2>
115
+ <label for="add-title">Title</label>
116
+ <input type="text" id="add-title" placeholder="Task title">
117
+ <label for="add-body">Description</label>
118
+ <textarea id="add-body" placeholder="Optional"></textarea>
119
+ <label for="add-priority">Priority</label>
120
+ <select id="add-priority">
121
+ <option value="">None</option>
122
+ ${BOARD_PRIORITY_OPTIONS}
123
+ </select>
124
+ <input type="hidden" id="add-status">
125
+ <div class="modal-actions">
126
+ <button id="add-cancel">Cancel</button>
127
+ <button id="add-submit" class="primary">Add</button>
128
+ </div>
129
+ </div>
130
+ </div>`;
131
+ }
132
+ function getContextMenuAndToast() {
133
+ return `
134
+ <div class="context-menu" id="context-menu">
135
+ <div class="context-menu-item danger" id="ctx-delete">Delete task</div>
136
+ </div>
137
+ <div class="toast" id="toast">Failed to update task</div>`;
138
+ }
139
+ function getPurgeAndVersionModals() {
140
+ return `
141
+ <div class="modal-overlay" id="purge-confirm-modal">
142
+ <div class="modal">
143
+ <h2>Purge Tasks</h2>
144
+ <p style="font-size:13px;color:#64748b;margin-bottom:16px;">Delete all done/closed tasks older than 3 days. This action cannot be undone.</p>
145
+ <p id="purge-result" style="font-size:13px;color:#16a34a;min-height:18px;margin-bottom:8px;"></p>
146
+ <div class="modal-actions">
147
+ <button id="purge-cancel-btn">Cancel</button>
148
+ <button id="purge-confirm-btn" class="primary" style="background:#dc2626;border-color:#dc2626;">Purge</button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ <div class="modal-overlay" id="version-info-modal">
153
+ <div class="modal" style="width:320px;">
154
+ <h2>Version Info</h2>
155
+ <p id="version-info-text" style="font-size:14px;color:#1e293b;margin-bottom:16px;"></p>
156
+ <div class="modal-actions">
157
+ <button id="version-info-close">Close</button>
158
+ </div>
159
+ </div>
160
+ </div>`;
161
+ }
162
+ function loadClientBundle() {
163
+ // Try resolved path (works in compiled dist/ and in development)
164
+ const candidates = [
165
+ path.join(__dirname, 'client', 'board.js'),
166
+ path.join(__dirname, '..', '..', 'dist', 'board', 'client', 'board.js'),
167
+ ];
168
+ for (const bundlePath of candidates) {
169
+ try {
170
+ return fs.readFileSync(bundlePath, 'utf8');
171
+ }
172
+ catch {
173
+ // Try next candidate
174
+ }
175
+ }
176
+ throw new Error(`Client bundle not found. Tried: ${candidates.join(', ')}. Run 'npm run build' to generate it.`);
177
+ }
178
+ function getBoardBodyStatic() {
179
+ const clientBundle = loadClientBundle();
180
+ const script = `
181
+ const statusColors = ${JSON.stringify(exports.STATUS_COLORS)};
182
+ const allStatuses = ${JSON.stringify(exports.STATUSES)};
183
+ const statusLabels = ${JSON.stringify(exports.STATUS_LABELS)};
184
+ const allPriorities = ${JSON.stringify(models_1.PRIORITIES)};
185
+ ${clientBundle}`;
186
+ return `${getAddTaskModal()}${getContextMenuAndToast()}${getPurgeAndVersionModals()}
187
+ <script>${script}
188
+ </script>`;
189
+ }
190
+ function renderBoard(tasksByStatus, tagMap, boardTitle) {
191
+ const columns = exports.STATUSES.map((status) => renderColumn(status, tasksByStatus.get(status) || [], tagMap)).join('');
192
+ const titleHtml = boardTitle ? `<span class="board-title">${escapeHtml(boardTitle)}</span>` : '';
193
+ const boardBodyStatic = getBoardBodyStatic();
194
+ return `<!DOCTYPE html>
195
+ <html lang="en">
196
+ <head>
197
+ <meta charset="UTF-8">
198
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
199
+ <title>agkan board</title>
200
+ <style>${boardStyles_1.BOARD_STYLES}
201
+ </style>
202
+ </head>
203
+ <body>
204
+ <header><h1>agkan board</h1>${titleHtml}<div class="burger-menu-wrapper"><button class="burger-menu-btn" id="burger-menu-btn" title="Menu" aria-label="Menu"><span></span><span></span><span></span></button><div class="burger-menu-dropdown" id="burger-menu-dropdown"><div class="burger-menu-item danger" id="burger-purge-tasks">&#128465; Purge Tasks</div><div class="burger-menu-item" id="burger-version-info">&#8505; Version Info</div></div></div></header>
205
+ <div class="filter-bar" id="filter-bar">
206
+ <div class="filter-group">
207
+ <span class="filter-label">Priority</span>
208
+ <button class="filter-priority-btn" data-priority="critical">critical</button>
209
+ <button class="filter-priority-btn" data-priority="high">high</button>
210
+ <button class="filter-priority-btn" data-priority="medium">medium</button>
211
+ <button class="filter-priority-btn" data-priority="low">low</button>
212
+ </div>
213
+ <div class="filter-group">
214
+ <span class="filter-label">Tags</span>
215
+ <div id="filter-tags-control" style="display:flex;align-items:center;gap:4px;flex-wrap:nowrap;"></div>
216
+ </div>
217
+ <div class="filter-group">
218
+ <span class="filter-label">Assignee</span>
219
+ <input type="text" id="filter-assignee" class="filter-assignee-input" placeholder="Filter by assignee">
220
+ </div>
221
+ <button class="filter-clear-btn" id="filter-clear">Clear filters</button>
222
+ </div>
223
+ <div class="board-container">
224
+ <div class="board">${columns}</div>${boardBodyStatic}
225
+ </div>
226
+ </body>
227
+ </html>`;
228
+ }
229
+ function sortByPriority(tasks) {
230
+ return [...tasks].sort((a, b) => {
231
+ const oa = a.priority ? models_1.PRIORITY_ORDER[a.priority] : 4;
232
+ const ob = b.priority ? models_1.PRIORITY_ORDER[b.priority] : 4;
233
+ return oa - ob;
234
+ });
235
+ }
236
+ function buildBoardCardsPayload(tasksByStatus, tagMap) {
237
+ return exports.STATUSES.map((status) => {
238
+ const tasks = tasksByStatus.get(status) || [];
239
+ const html = tasks.map((t) => renderCard(t, tagMap.get(t.id) || [])).join('');
240
+ return { status, html, count: tasks.length };
241
+ });
242
+ }
243
+ function buildTasksByStatus(tasks) {
244
+ const tasksByStatus = new Map();
245
+ for (const status of exports.STATUSES) {
246
+ tasksByStatus.set(status, []);
247
+ }
248
+ for (const task of tasks) {
249
+ tasksByStatus.get(task.status)?.push(task);
250
+ }
251
+ for (const [status, statusTasks] of tasksByStatus) {
252
+ tasksByStatus.set(status, sortByPriority(statusTasks));
253
+ }
254
+ return tasksByStatus;
255
+ }
256
+ function getBoardUpdatedAt(database) {
257
+ const baseRow = database
258
+ .prepare(`
259
+ SELECT MAX(updated_at) as max_updated_at FROM (
260
+ SELECT updated_at FROM tasks UNION ALL SELECT updated_at FROM task_metadata
261
+ )
262
+ `)
263
+ .get();
264
+ const tagsRow = database
265
+ .prepare(`
266
+ SELECT MAX(created_at) as max_created_at, COUNT(*) as count FROM task_tags
267
+ `)
268
+ .get();
269
+ if (baseRow.max_updated_at === null && tagsRow.max_created_at === null)
270
+ return null;
271
+ return `${baseRow.max_updated_at}|${tagsRow.max_created_at}|${tagsRow.count}`;
272
+ }
273
+ //# sourceMappingURL=boardRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardRenderer.js","sourceRoot":"","sources":["../../src/board/boardRenderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { Hono } from 'hono';
2
+ import { TaskService } from '../services/TaskService';
3
+ import { TaskTagService } from '../services/TaskTagService';
4
+ import { TagService } from '../services/TagService';
5
+ import { MetadataService } from '../services/MetadataService';
6
+ import { CommentService } from '../services/CommentService';
7
+ import { TaskBlockService } from '../services/TaskBlockService';
8
+ import { StorageProvider } from '../db/types/storage';
9
+ export type BoardServices = {
10
+ ts: TaskService;
11
+ tts: TaskTagService;
12
+ tags: TagService;
13
+ ms: MetadataService;
14
+ cs: CommentService;
15
+ tbs: TaskBlockService;
16
+ database: StorageProvider;
17
+ boardTitle?: string;
18
+ configDir: string;
19
+ };
20
+ export declare function registerTaskApiRoutes(app: Hono, { ts, tts, tags, ms, cs, tbs }: BoardServices): void;
21
+ export declare function registerConfigApiRoutes(app: Hono, configDir: string): void;
22
+ export declare function registerBoardRoutes(app: Hono, services: BoardServices): void;
23
+ //# sourceMappingURL=boardRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardRoutes.d.ts","sourceRoot":"","sources":["../../src/board/boardRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAItD,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,WAAW,CAAC;IAChB,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,eAAe,CAAC;IACpB,EAAE,EAAE,cAAc,CAAC;IACnB,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAuMF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,aAAa,GAAG,IAAI,CAKpG;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAuB1E;AA0BD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAmB5E"}
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerTaskApiRoutes = registerTaskApiRoutes;
4
+ exports.registerConfigApiRoutes = registerConfigApiRoutes;
5
+ exports.registerBoardRoutes = registerBoardRoutes;
6
+ const models_1 = require("../models");
7
+ const boardConfig_1 = require("./boardConfig");
8
+ const boardRenderer_1 = require("./boardRenderer");
9
+ function buildTaskUpdateInput(body) {
10
+ const input = {};
11
+ if (body.status !== undefined) {
12
+ if (!boardRenderer_1.STATUSES.includes(body.status)) {
13
+ return { input, error: 'Invalid status' };
14
+ }
15
+ input.status = body.status;
16
+ }
17
+ if (body.title !== undefined) {
18
+ if (!body.title.trim()) {
19
+ return { input, error: 'Title cannot be empty' };
20
+ }
21
+ input.title = body.title.trim();
22
+ }
23
+ if (body.body !== undefined) {
24
+ input.body = body.body ?? '';
25
+ }
26
+ if (body.priority !== undefined) {
27
+ input.priority = body.priority && (0, models_1.isPriority)(body.priority) ? body.priority : null;
28
+ }
29
+ return { input };
30
+ }
31
+ function registerTaskCrudRoutes(app, ts, tts, tbs, ms) {
32
+ app.get('/api/tasks', (c) => c.json({ tasks: ts.listTasks({}, 'id', 'asc') }));
33
+ app.post('/api/tasks', async (c) => {
34
+ const body = await c.req.json();
35
+ if (!body.title || typeof body.title !== 'string' || !body.title.trim()) {
36
+ return c.json({ error: 'Title is required' }, 400);
37
+ }
38
+ const status = body.status && boardRenderer_1.STATUSES.includes(body.status) ? body.status : 'backlog';
39
+ const priority = body.priority && (0, models_1.isPriority)(body.priority) ? body.priority : undefined;
40
+ return c.json(ts.createTask({ title: body.title.trim(), body: body.body || undefined, status, priority }), 201);
41
+ });
42
+ app.get('/api/tasks/:id', (c) => {
43
+ const id = Number(c.req.param('id'));
44
+ if (isNaN(id))
45
+ return c.json({ error: 'Invalid task id' }, 400);
46
+ const task = ts.getTask(id);
47
+ if (!task)
48
+ return c.json({ error: 'Task not found' }, 404);
49
+ const parent = task.parent_id ? ts.getTask(task.parent_id) : null;
50
+ const blockedByIds = tbs.getBlockerTaskIds(id);
51
+ const blockingIds = tbs.getBlockedTaskIds(id);
52
+ const blockedBy = blockedByIds.map((bid) => ts.getTask(bid)).filter(Boolean);
53
+ const blocking = blockingIds.map((bid) => ts.getTask(bid)).filter(Boolean);
54
+ return c.json({ task, tags: tts.getTagsForTask(id), metadata: ms.listMetadata(id), parent, blockedBy, blocking });
55
+ });
56
+ app.patch('/api/tasks/:id', async (c) => {
57
+ const id = Number(c.req.param('id'));
58
+ if (isNaN(id))
59
+ return c.json({ error: 'Invalid task id' }, 400);
60
+ const { input, error } = buildTaskUpdateInput(await c.req.json());
61
+ if (error)
62
+ return c.json({ error }, 400);
63
+ const task = ts.updateTask(id, input);
64
+ if (!task)
65
+ return c.json({ error: 'Task not found' }, 404);
66
+ return c.json(task);
67
+ });
68
+ app.delete('/api/tasks/:id', (c) => {
69
+ const id = Number(c.req.param('id'));
70
+ if (isNaN(id))
71
+ return c.json({ error: 'Invalid task id' }, 400);
72
+ if (!ts.getTask(id))
73
+ return c.json({ error: 'Task not found' }, 404);
74
+ ts.deleteTask(id);
75
+ return c.json({ success: true });
76
+ });
77
+ }
78
+ function registerCommentRoutes(app, cs, ts) {
79
+ app.get('/api/tasks/:id/comments', (c) => {
80
+ const id = Number(c.req.param('id'));
81
+ if (isNaN(id))
82
+ return c.json({ error: 'Invalid task id' }, 400);
83
+ if (!ts.getTask(id))
84
+ return c.json({ error: 'Task not found' }, 404);
85
+ return c.json({ comments: cs.listComments(id) });
86
+ });
87
+ app.post('/api/tasks/:id/comments', async (c) => {
88
+ const id = Number(c.req.param('id'));
89
+ if (isNaN(id))
90
+ return c.json({ error: 'Invalid task id' }, 400);
91
+ if (!ts.getTask(id))
92
+ return c.json({ error: 'Task not found' }, 404);
93
+ const body = await c.req.json();
94
+ if (!body.content || typeof body.content !== 'string') {
95
+ return c.json({ error: 'Content is required' }, 400);
96
+ }
97
+ try {
98
+ const comment = cs.addComment({ task_id: id, content: body.content, author: body.author });
99
+ return c.json(comment, 201);
100
+ }
101
+ catch (e) {
102
+ return c.json({ error: e instanceof Error ? e.message : 'Invalid input' }, 400);
103
+ }
104
+ });
105
+ app.get('/api/comments/:id', (c) => {
106
+ const id = Number(c.req.param('id'));
107
+ if (isNaN(id))
108
+ return c.json({ error: 'Invalid comment id' }, 400);
109
+ const comment = cs.getComment(id);
110
+ if (!comment)
111
+ return c.json({ error: 'Comment not found' }, 404);
112
+ return c.json(comment);
113
+ });
114
+ app.patch('/api/comments/:id', async (c) => {
115
+ const id = Number(c.req.param('id'));
116
+ if (isNaN(id))
117
+ return c.json({ error: 'Invalid comment id' }, 400);
118
+ const body = await c.req.json();
119
+ if (!body.content || typeof body.content !== 'string') {
120
+ return c.json({ error: 'Content is required' }, 400);
121
+ }
122
+ try {
123
+ const comment = cs.updateComment(id, body.content);
124
+ if (!comment)
125
+ return c.json({ error: 'Comment not found' }, 404);
126
+ return c.json(comment);
127
+ }
128
+ catch (e) {
129
+ return c.json({ error: e instanceof Error ? e.message : 'Invalid input' }, 400);
130
+ }
131
+ });
132
+ app.delete('/api/comments/:id', (c) => {
133
+ const id = Number(c.req.param('id'));
134
+ if (isNaN(id))
135
+ return c.json({ error: 'Invalid comment id' }, 400);
136
+ const deleted = cs.deleteComment(id);
137
+ if (!deleted)
138
+ return c.json({ error: 'Comment not found' }, 404);
139
+ return c.json({ success: true });
140
+ });
141
+ }
142
+ function registerTagRoutes(app, tts, tags, ts) {
143
+ app.post('/api/tasks/:id/tags', async (c) => {
144
+ const id = Number(c.req.param('id'));
145
+ if (isNaN(id))
146
+ return c.json({ error: 'Invalid task id' }, 400);
147
+ const body = await c.req.json();
148
+ if (body.tagId === undefined || body.tagId === null)
149
+ return c.json({ error: 'tagId is required' }, 400);
150
+ const tagId = Number(body.tagId);
151
+ if (!ts.getTask(id))
152
+ return c.json({ error: 'Task not found' }, 404);
153
+ if (!tags.getTag(tagId))
154
+ return c.json({ error: 'Tag not found' }, 404);
155
+ tts.addTagToTask({ task_id: id, tag_id: tagId });
156
+ return c.json({ success: true }, 201);
157
+ });
158
+ app.delete('/api/tasks/:id/tags/:tagId', (c) => {
159
+ const id = Number(c.req.param('id'));
160
+ if (isNaN(id))
161
+ return c.json({ error: 'Invalid task id' }, 400);
162
+ const tagId = Number(c.req.param('tagId'));
163
+ if (isNaN(tagId))
164
+ return c.json({ error: 'Invalid tag id' }, 400);
165
+ const removed = tts.removeTagFromTask(id, tagId);
166
+ if (!removed)
167
+ return c.json({ error: 'Tag not attached to task' }, 404);
168
+ return c.json({ success: true });
169
+ });
170
+ app.get('/api/tags', (c) => {
171
+ const allTags = tags.listTags();
172
+ return c.json({ tags: allTags });
173
+ });
174
+ }
175
+ function registerUtilityRoutes(app, ts) {
176
+ app.post('/api/tasks/purge', async (c) => {
177
+ const body = (await c.req.json().catch(() => ({})));
178
+ let beforeDate;
179
+ if (body.beforeDate !== undefined) {
180
+ const parsed = new Date(body.beforeDate);
181
+ if (isNaN(parsed.getTime())) {
182
+ return c.json({ error: 'Invalid beforeDate. Use ISO 8601 format.' }, 400);
183
+ }
184
+ beforeDate = parsed.toISOString();
185
+ }
186
+ else {
187
+ const d = new Date();
188
+ d.setDate(d.getDate() - 3);
189
+ beforeDate = d.toISOString();
190
+ }
191
+ const tasks = ts.purgeTasksBefore(beforeDate, ['done', 'closed'], false);
192
+ return c.json({
193
+ count: tasks.length,
194
+ tasks: tasks.map((t) => ({ id: t.id, title: t.title, status: t.status, updated_at: t.updated_at })),
195
+ });
196
+ });
197
+ app.get('/api/version', (c) => {
198
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
199
+ const { version } = require('../../package.json');
200
+ return c.json({ version });
201
+ });
202
+ }
203
+ function registerTaskApiRoutes(app, { ts, tts, tags, ms, cs, tbs }) {
204
+ registerTaskCrudRoutes(app, ts, tts, tbs, ms);
205
+ registerCommentRoutes(app, cs, ts);
206
+ registerTagRoutes(app, tts, tags, ts);
207
+ registerUtilityRoutes(app, ts);
208
+ }
209
+ function registerConfigApiRoutes(app, configDir) {
210
+ app.get('/api/config', (c) => {
211
+ const boardConfig = (0, boardConfig_1.readBoardConfig)(configDir);
212
+ return c.json({ board: boardConfig });
213
+ });
214
+ app.put('/api/config', async (c) => {
215
+ const body = await c.req.json();
216
+ const boardBody = body?.board ?? {};
217
+ if (boardBody.detailPaneWidth !== undefined) {
218
+ const width = boardBody.detailPaneWidth;
219
+ if (typeof width !== 'number' || !Number.isFinite(width)) {
220
+ return c.json({ error: 'detailPaneWidth must be a number' }, 400);
221
+ }
222
+ if (width > boardConfig_1.DETAIL_PANE_MAX_WIDTH) {
223
+ return c.json({ error: `detailPaneWidth must not exceed ${boardConfig_1.DETAIL_PANE_MAX_WIDTH}` }, 400);
224
+ }
225
+ (0, boardConfig_1.writeBoardConfig)(configDir, { detailPaneWidth: width });
226
+ }
227
+ return c.json({ success: true });
228
+ });
229
+ }
230
+ function parseBoardCardFilters(query) {
231
+ const filters = {};
232
+ if (query.tags) {
233
+ const tagIds = query.tags
234
+ .split(',')
235
+ .map((s) => Number(s.trim()))
236
+ .filter((n) => !isNaN(n) && n > 0);
237
+ if (tagIds.length > 0)
238
+ filters.tagIds = tagIds;
239
+ }
240
+ if (query.priority) {
241
+ const priorities = query.priority
242
+ .split(',')
243
+ .map((s) => s.trim())
244
+ .filter((s) => s.length > 0);
245
+ if (priorities.length > 0)
246
+ filters.priority = priorities;
247
+ }
248
+ if (query.assignee && query.assignee.trim()) {
249
+ filters.assignees = query.assignee.trim();
250
+ }
251
+ return filters;
252
+ }
253
+ function registerBoardRoutes(app, services) {
254
+ const { ts, tts, database, boardTitle, configDir } = services;
255
+ app.get('/', (c) => {
256
+ const tasksByStatus = (0, boardRenderer_1.buildTasksByStatus)(ts.listTasks({}, 'id', 'asc'));
257
+ return c.html((0, boardRenderer_1.renderBoard)(tasksByStatus, tts.getAllTaskTags(), boardTitle));
258
+ });
259
+ app.get('/api/board/updated-at', (c) => c.json({ updatedAt: (0, boardRenderer_1.getBoardUpdatedAt)(database) }));
260
+ app.get('/api/board/cards', (c) => {
261
+ const filters = parseBoardCardFilters({
262
+ tags: c.req.query('tags'),
263
+ priority: c.req.query('priority'),
264
+ assignee: c.req.query('assignee'),
265
+ });
266
+ const tasksByStatus = (0, boardRenderer_1.buildTasksByStatus)(ts.listTasks(filters, 'id', 'asc'));
267
+ const columns = (0, boardRenderer_1.buildBoardCardsPayload)(tasksByStatus, tts.getAllTaskTags());
268
+ return c.json({ columns });
269
+ });
270
+ registerTaskApiRoutes(app, services);
271
+ registerConfigApiRoutes(app, configDir);
272
+ }
273
+ //# sourceMappingURL=boardRoutes.js.map