@pixelbyte-software/pixcode 1.47.5 → 1.48.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 (109) hide show
  1. package/README.md +16 -30
  2. package/README.tr.md +1 -1
  3. package/dist/api-automation.html +1 -1
  4. package/dist/assets/index-DlAWSDTA.js +818 -0
  5. package/dist/assets/index-g9NMlzYt.css +32 -0
  6. package/dist/assets/{vendor-codemirror-CzSp4P1a.js → vendor-codemirror-CIYNS698.js} +8 -8
  7. package/dist/docs.html +10 -10
  8. package/dist/features.html +3 -3
  9. package/dist/index.html +3 -3
  10. package/dist/landing.html +21 -23
  11. package/dist/llms-full.txt +4 -8
  12. package/dist/llms.txt +5 -6
  13. package/dist/openapi.yaml +7 -7
  14. package/dist-server/server/index.js +4 -6
  15. package/dist-server/server/index.js.map +1 -1
  16. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -1
  17. package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js.map +1 -1
  18. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js +1 -1
  19. package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -1
  20. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +1 -1
  21. package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -1
  22. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +1 -1
  23. package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -1
  24. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +1 -1
  25. package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -1
  26. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +1 -1
  27. package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -1
  28. package/dist-server/server/modules/orchestration/a2a/agent-card.js +4 -4
  29. package/dist-server/server/modules/orchestration/a2a/agent-card.js.map +1 -1
  30. package/dist-server/server/modules/orchestration/a2a/routes.js +6 -2
  31. package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
  32. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +62 -0
  33. package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -0
  34. package/dist-server/server/modules/orchestration/index.js +2 -1
  35. package/dist-server/server/modules/orchestration/index.js.map +1 -1
  36. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +2 -5
  37. package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -1
  38. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +1 -20
  39. package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -1
  40. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +4 -37
  41. package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
  42. package/dist-server/server/modules/orchestration/tasks/task-run-graph.js +4 -62
  43. package/dist-server/server/modules/orchestration/tasks/task-run-graph.js.map +1 -1
  44. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +14 -14
  45. package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
  46. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js +2 -2
  47. package/dist-server/server/modules/orchestration/workflows/workflow-trace.js.map +1 -1
  48. package/dist-server/server/projects.js +0 -160
  49. package/dist-server/server/projects.js.map +1 -1
  50. package/dist-server/server/routes/mcp-utils.js +0 -18
  51. package/dist-server/server/routes/mcp-utils.js.map +1 -1
  52. package/dist-server/server/services/public-api-manifest.js +0 -5
  53. package/dist-server/server/services/public-api-manifest.js.map +1 -1
  54. package/dist-server/server/services/telegram/control-center.js +2 -144
  55. package/dist-server/server/services/telegram/control-center.js.map +1 -1
  56. package/dist-server/server/services/telegram/translations.js +2 -14
  57. package/dist-server/server/services/telegram/translations.js.map +1 -1
  58. package/package.json +1 -2
  59. package/scripts/smoke/default-landing-routing.mjs +7 -16
  60. package/scripts/smoke/{a2a-roundtrip.mjs → hermes-roundtrip.mjs} +23 -23
  61. package/scripts/smoke/mac-desktop-runtime.mjs +1 -7
  62. package/scripts/smoke/orchestration-live-run.mjs +1 -1
  63. package/scripts/smoke/orchestration-model-sync.mjs +2 -2
  64. package/scripts/smoke/orchestration-user-facing-output.mjs +2 -2
  65. package/scripts/smoke/pixcode-workbench-1-48.mjs +55 -0
  66. package/scripts/smoke/taskmaster-config.mjs +21 -56
  67. package/scripts/smoke/taskmaster-execution-telegram.mjs +1 -50
  68. package/scripts/smoke/taskmaster-onboarding.mjs +1 -50
  69. package/scripts/smoke/taskmaster-run-graph.mjs +1 -53
  70. package/scripts/smoke/vscode-workbench-layout.mjs +25 -62
  71. package/scripts/smoke/vscode-workbench-polish.mjs +19 -5
  72. package/server/index.js +5 -7
  73. package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +1 -1
  74. package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +1 -1
  75. package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +1 -1
  76. package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +1 -1
  77. package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +1 -1
  78. package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +1 -1
  79. package/server/modules/orchestration/a2a/agent-card.ts +4 -4
  80. package/server/modules/orchestration/a2a/routes.ts +7 -2
  81. package/server/modules/orchestration/hermes/hermes.routes.ts +69 -0
  82. package/server/modules/orchestration/index.ts +2 -1
  83. package/server/modules/orchestration/tasks/orchestration-task-store.ts +2 -6
  84. package/server/modules/orchestration/tasks/orchestration-task.routes.ts +1 -20
  85. package/server/modules/orchestration/tasks/orchestration-task.service.ts +4 -41
  86. package/server/modules/orchestration/tasks/orchestration-task.types.ts +2 -4
  87. package/server/modules/orchestration/tasks/task-run-graph.ts +6 -70
  88. package/server/modules/orchestration/workflows/workflow-runner.ts +14 -14
  89. package/server/modules/orchestration/workflows/workflow-trace.ts +2 -2
  90. package/server/modules/orchestration/workflows/workflow.types.ts +1 -1
  91. package/server/projects.js +0 -170
  92. package/server/routes/mcp-utils.js +0 -19
  93. package/server/services/public-api-manifest.js +0 -5
  94. package/server/services/telegram/control-center.js +2 -153
  95. package/server/services/telegram/translations.js +2 -14
  96. package/dist/assets/index-BBdWwJi6.css +0 -32
  97. package/dist/assets/index-DfqcgNYJ.js +0 -889
  98. package/dist-server/server/routes/taskmaster.js +0 -1793
  99. package/dist-server/server/routes/taskmaster.js.map +0 -1
  100. package/dist-server/server/services/taskmaster-config.js +0 -128
  101. package/dist-server/server/services/taskmaster-config.js.map +0 -1
  102. package/dist-server/server/utils/mcp-detector.js +0 -134
  103. package/dist-server/server/utils/mcp-detector.js.map +0 -1
  104. package/dist-server/server/utils/taskmaster-websocket.js +0 -118
  105. package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
  106. package/server/routes/taskmaster.js +0 -1918
  107. package/server/services/taskmaster-config.js +0 -146
  108. package/server/utils/mcp-detector.js +0 -147
  109. package/server/utils/taskmaster-websocket.js +0 -129
@@ -1,1918 +0,0 @@
1
- /**
2
- * TASKMASTER API ROUTES
3
- * ====================
4
- *
5
- * This module provides API endpoints for TaskMaster integration including:
6
- * - .taskmaster folder detection in project directories
7
- * - MCP server configuration detection
8
- * - TaskMaster state and metadata management
9
- */
10
-
11
- import fs, { promises as fsPromises } from 'fs';
12
- import path from 'path';
13
- import { spawn } from 'child_process';
14
-
15
- import express from 'express';
16
- import crossSpawn from 'cross-spawn';
17
-
18
- import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
19
- import { buildTaskRunGraph } from '@/modules/orchestration/tasks/task-run-graph.js';
20
- import { workflowRunner } from '@/modules/orchestration/workflows/workflow-runner.js';
21
- import { workflowStore } from '@/modules/orchestration/workflows/workflow-store.js';
22
-
23
- import { extractProjectDirectory } from '../projects.js';
24
- import {
25
- cancelInstallJob,
26
- buildCliSpawnEnv,
27
- createInstallJob,
28
- findExecutableOnPath,
29
- getInstallJob,
30
- snapshotDonePayload
31
- } from '../services/install-jobs.js';
32
- import {
33
- buildTaskMasterEnv,
34
- getTaskMasterConfigSummary,
35
- saveTaskMasterConfig
36
- } from '../services/taskmaster-config.js';
37
- import { broadcastTaskMasterProjectUpdate, broadcastTaskMasterTasksUpdate } from '../utils/taskmaster-websocket.js';
38
- import { detectTaskMasterMCPServer } from '../utils/mcp-detector.js';
39
-
40
- const router = express.Router();
41
-
42
- /**
43
- * Check if TaskMaster CLI is installed globally
44
- * @returns {Promise<Object>} Installation status result
45
- */
46
- async function buildTaskMasterCliEnv(baseEnv = process.env) {
47
- return buildTaskMasterEnv(buildCliSpawnEnv(baseEnv));
48
- }
49
-
50
- function findTaskMasterExecutable(env) {
51
- const taskMasterPath = findExecutableOnPath('task-master', env);
52
- if (taskMasterPath) {
53
- return { path: taskMasterPath, name: 'task-master' };
54
- }
55
-
56
- const legacyPath = findExecutableOnPath('task-master-ai', env);
57
- if (legacyPath) {
58
- return { path: legacyPath, name: 'task-master-ai' };
59
- }
60
-
61
- return null;
62
- }
63
-
64
- async function checkTaskMasterInstallation() {
65
- const env = await buildTaskMasterCliEnv();
66
-
67
- return new Promise((resolve) => {
68
- const executable = findTaskMasterExecutable(env);
69
-
70
- if (!executable) {
71
- resolve({
72
- isInstalled: false,
73
- installPath: null,
74
- version: null,
75
- binary: null,
76
- reason: 'TaskMaster CLI not found in PATH'
77
- });
78
- return;
79
- }
80
-
81
- const versionChild = crossSpawn(executable.path, ['--version'], {
82
- stdio: ['ignore', 'pipe', 'pipe'],
83
- env,
84
- windowsHide: true,
85
- });
86
-
87
- let versionOutput = '';
88
-
89
- versionChild.stdout?.on('data', (data) => {
90
- versionOutput += data.toString();
91
- });
92
-
93
- versionChild.on('close', (versionCode) => {
94
- resolve({
95
- isInstalled: true,
96
- installPath: executable.path,
97
- binary: executable.name,
98
- version: versionCode === 0 ? versionOutput.trim() : 'unknown',
99
- reason: null
100
- });
101
- });
102
-
103
- versionChild.on('error', () => {
104
- resolve({
105
- isInstalled: true,
106
- installPath: executable.path,
107
- binary: executable.name,
108
- version: 'unknown',
109
- reason: null
110
- });
111
- });
112
- });
113
- }
114
-
115
-
116
- async function readTaskMasterTasks(projectName) {
117
- const projectPath = await extractProjectDirectory(projectName);
118
- const tasksFilePath = path.join(projectPath, '.taskmaster', 'tasks', 'tasks.json');
119
-
120
- await fsPromises.access(tasksFilePath);
121
- const tasksContent = await fsPromises.readFile(tasksFilePath, 'utf8');
122
- const tasksData = JSON.parse(tasksContent);
123
-
124
- let tasks = [];
125
- let currentTag = 'master';
126
-
127
- if (Array.isArray(tasksData)) {
128
- tasks = tasksData;
129
- } else if (tasksData.tasks) {
130
- tasks = tasksData.tasks;
131
- } else if (tasksData[currentTag] && tasksData[currentTag].tasks) {
132
- tasks = tasksData[currentTag].tasks;
133
- } else if (tasksData.master && tasksData.master.tasks) {
134
- tasks = tasksData.master.tasks;
135
- } else {
136
- const firstTag = Object.keys(tasksData).find((key) =>
137
- tasksData[key].tasks && Array.isArray(tasksData[key].tasks)
138
- );
139
- if (firstTag) {
140
- tasks = tasksData[firstTag].tasks;
141
- currentTag = firstTag;
142
- }
143
- }
144
-
145
- const transformedTasks = tasks.map((task) => ({
146
- id: task.id,
147
- title: task.title || 'Untitled Task',
148
- description: task.description || '',
149
- status: task.status || 'pending',
150
- priority: task.priority || 'medium',
151
- dependencies: task.dependencies || [],
152
- createdAt: task.createdAt || task.created || new Date().toISOString(),
153
- updatedAt: task.updatedAt || task.updated || new Date().toISOString(),
154
- details: task.details || '',
155
- testStrategy: task.testStrategy || task.test_strategy || '',
156
- subtasks: task.subtasks || []
157
- }));
158
-
159
- return { projectPath, transformedTasks, currentTag };
160
- }
161
-
162
- function taskMasterExecutionDescription(task) {
163
- return [
164
- task.description ? `Description:\n${task.description}` : '',
165
- task.details ? `Details:\n${task.details}` : '',
166
- task.testStrategy ? `Test strategy:\n${task.testStrategy}` : '',
167
- Array.isArray(task.dependencies) && task.dependencies.length
168
- ? `Dependencies: ${task.dependencies.join(', ')}`
169
- : '',
170
- ].filter(Boolean).join('\n\n');
171
- }
172
-
173
- function taskGraphForTask(projectId, task) {
174
- return buildTaskRunGraph({
175
- projectId,
176
- taskmasterId: String(task.id),
177
- taskmasterTask: task,
178
- });
179
- }
180
-
181
- function attachTaskGraph(projectId, task) {
182
- return {
183
- ...task,
184
- taskGraph: taskGraphForTask(projectId, task),
185
- };
186
- }
187
-
188
- function buildTaskMasterQueueSummary(projectName, projectPath, tasks) {
189
- const normalized = tasks.map((task) => ({
190
- ...task,
191
- queueState: ['done', 'completed', 'cancelled', 'canceled'].includes(String(task.status || '').toLowerCase())
192
- ? 'finished'
193
- : String(task.status || 'pending') === 'in-progress'
194
- ? 'running'
195
- : 'queued',
196
- }));
197
- return {
198
- projectName,
199
- projectPath,
200
- queue: normalized,
201
- totals: {
202
- all: normalized.length,
203
- queued: normalized.filter((task) => task.queueState === 'queued').length,
204
- running: normalized.filter((task) => task.queueState === 'running').length,
205
- finished: normalized.filter((task) => task.queueState === 'finished').length,
206
- },
207
- timestamp: new Date().toISOString(),
208
- };
209
- }
210
-
211
- // API Routes
212
-
213
- /**
214
- * GET /api/taskmaster/config
215
- * Return redacted TaskMaster provider env configuration.
216
- */
217
- router.get('/config', async (req, res) => {
218
- try {
219
- res.json({
220
- success: true,
221
- config: await getTaskMasterConfigSummary()
222
- });
223
- } catch (error) {
224
- console.error('Error reading TaskMaster config:', error);
225
- res.status(500).json({
226
- success: false,
227
- error: 'Failed to read TaskMaster configuration',
228
- message: error.message
229
- });
230
- }
231
- });
232
-
233
- /**
234
- * PUT /api/taskmaster/config
235
- * Persist TaskMaster provider env configuration.
236
- */
237
- router.put('/config', async (req, res) => {
238
- try {
239
- res.json({
240
- success: true,
241
- config: await saveTaskMasterConfig(req.body || {})
242
- });
243
- } catch (error) {
244
- console.error('Error saving TaskMaster config:', error);
245
- res.status(500).json({
246
- success: false,
247
- error: 'Failed to save TaskMaster configuration',
248
- message: error.message
249
- });
250
- }
251
- });
252
-
253
- /**
254
- * GET /api/taskmaster/installation-status
255
- * Check if TaskMaster CLI is installed on the system
256
- */
257
- router.get('/installation-status', async (req, res) => {
258
- try {
259
- const installationStatus = await checkTaskMasterInstallation();
260
-
261
- // Also check for MCP server configuration
262
- const mcpStatus = await detectTaskMasterMCPServer();
263
-
264
- res.json({
265
- success: true,
266
- installation: installationStatus,
267
- mcpServer: mcpStatus,
268
- isReady: installationStatus.isInstalled && mcpStatus.hasMCPServer
269
- });
270
- } catch (error) {
271
- console.error('Error checking TaskMaster installation:', error);
272
- res.status(500).json({
273
- success: false,
274
- error: 'Failed to check TaskMaster installation status',
275
- installation: {
276
- isInstalled: false,
277
- reason: `Server error: ${error.message}`
278
- },
279
- mcpServer: {
280
- hasMCPServer: false,
281
- reason: `Server error: ${error.message}`
282
- },
283
- isReady: false
284
- });
285
- }
286
- });
287
-
288
- /**
289
- * POST /api/taskmaster/install
290
- * Install TaskMaster CLI into Pixcode's sandboxed CLI bin.
291
- */
292
- router.post('/install', async (req, res) => {
293
- try {
294
- const job = createInstallJob({
295
- provider: 'taskmaster',
296
- installCmd: 'npm install -g task-master',
297
- packageName: 'task-master'
298
- });
299
-
300
- res.json({
301
- success: true,
302
- jobId: job.id,
303
- provider: 'taskmaster',
304
- packageName: 'task-master',
305
- startedAt: job.startedAt
306
- });
307
- } catch (error) {
308
- console.error('TaskMaster install start error:', error);
309
- res.status(500).json({
310
- success: false,
311
- error: 'Failed to start TaskMaster install',
312
- message: error.message
313
- });
314
- }
315
- });
316
-
317
- /**
318
- * GET /api/taskmaster/install/:jobId/stream
319
- * Replay and stream TaskMaster install output.
320
- */
321
- router.get('/install/:jobId/stream', async (req, res) => {
322
- const job = getInstallJob(req.params.jobId);
323
- if (!job || job.provider !== 'taskmaster') {
324
- return res.status(404).json({
325
- success: false,
326
- error: 'Install job not found or already expired'
327
- });
328
- }
329
-
330
- res.setHeader('Content-Type', 'text/event-stream');
331
- res.setHeader('Cache-Control', 'no-cache, no-transform');
332
- res.setHeader('Connection', 'keep-alive');
333
- res.setHeader('X-Accel-Buffering', 'no');
334
- if (typeof res.flushHeaders === 'function') res.flushHeaders();
335
-
336
- let closed = false;
337
- const write = (event, payload) => {
338
- if (closed) return;
339
- try {
340
- res.write(`event: ${event}\n`);
341
- res.write(`data: ${JSON.stringify(payload)}\n\n`);
342
- } catch {
343
- // Socket is gone.
344
- }
345
- };
346
-
347
- try { res.write(': start\n\n'); } catch { /* noop */ }
348
- const heartbeat = setInterval(() => {
349
- if (!closed) {
350
- try { res.write(': ping\n\n'); } catch { /* noop */ }
351
- }
352
- }, 5000);
353
-
354
- for (const entry of job.logs) {
355
- write('log', { stream: entry.stream, chunk: entry.chunk });
356
- }
357
-
358
- const cleanup = () => {
359
- if (closed) return;
360
- closed = true;
361
- clearInterval(heartbeat);
362
- job.emitter.off('log', onLog);
363
- job.emitter.off('done', onDone);
364
- };
365
- const onLog = (entry) => {
366
- write('log', { stream: entry.stream, chunk: entry.chunk });
367
- };
368
- const onDone = (payload) => {
369
- write('done', payload);
370
- cleanup();
371
- try { res.end(); } catch { /* noop */ }
372
- };
373
-
374
- if (job.status !== 'running') {
375
- write('done', snapshotDonePayload(job));
376
- cleanup();
377
- try { res.end(); } catch { /* noop */ }
378
- return;
379
- }
380
-
381
- job.emitter.on('log', onLog);
382
- job.emitter.once('done', onDone);
383
-
384
- req.on('close', cleanup);
385
- });
386
-
387
- /**
388
- * DELETE /api/taskmaster/install/:jobId
389
- * Cancel a running TaskMaster install job.
390
- */
391
- router.delete('/install/:jobId', async (req, res) => {
392
- const job = getInstallJob(req.params.jobId);
393
- if (!job || job.provider !== 'taskmaster') {
394
- return res.status(404).json({
395
- success: false,
396
- error: 'Install job not found'
397
- });
398
- }
399
-
400
- res.json({ success: true, cancelled: cancelInstallJob(req.params.jobId) });
401
- });
402
-
403
- /**
404
- * GET /api/taskmaster/tasks/:projectName
405
- * Load actual tasks from .taskmaster/tasks/tasks.json
406
- */
407
- router.get('/tasks/:projectName', async (req, res) => {
408
- try {
409
- const { projectName } = req.params;
410
- const projectId = typeof req.query.projectId === 'string' && req.query.projectId.trim()
411
- ? req.query.projectId.trim()
412
- : projectName;
413
-
414
- const { projectPath, transformedTasks, currentTag } = await readTaskMasterTasks(projectName);
415
- const tasksWithGraph = transformedTasks.map((task) => attachTaskGraph(projectId, task));
416
-
417
- res.json({
418
- projectName,
419
- projectPath,
420
- tasks: tasksWithGraph,
421
- currentTag,
422
- totalTasks: tasksWithGraph.length,
423
- tasksByStatus: {
424
- pending: tasksWithGraph.filter(t => t.status === 'pending').length,
425
- 'in-progress': tasksWithGraph.filter(t => t.status === 'in-progress').length,
426
- done: tasksWithGraph.filter(t => t.status === 'done').length,
427
- review: tasksWithGraph.filter(t => t.status === 'review').length,
428
- deferred: tasksWithGraph.filter(t => t.status === 'deferred').length,
429
- cancelled: tasksWithGraph.filter(t => t.status === 'cancelled').length
430
- },
431
- timestamp: new Date().toISOString()
432
- });
433
-
434
- } catch (error) {
435
- if (error?.code === 'ENOENT') {
436
- return res.json({
437
- projectName: req.params.projectName,
438
- tasks: [],
439
- message: 'No tasks.json file found'
440
- });
441
- }
442
- if (String(error?.message || '').includes('does not exist')) {
443
- return res.status(404).json({
444
- error: 'Project not found',
445
- message: `Project "${req.params.projectName}" does not exist`
446
- });
447
- }
448
- console.error('TaskMaster tasks loading error:', error);
449
- res.status(500).json({
450
- error: 'Failed to load TaskMaster tasks',
451
- message: error.message
452
- });
453
- }
454
- });
455
-
456
- /**
457
- * GET /api/taskmaster/queue/:projectName
458
- * Stable automation endpoint for remote UI, Telegram, and external clients.
459
- */
460
- router.get('/queue/:projectName', async (req, res) => {
461
- try {
462
- const { projectName } = req.params;
463
- const { projectPath, transformedTasks } = await readTaskMasterTasks(projectName);
464
- res.json(buildTaskMasterQueueSummary(projectName, projectPath, transformedTasks));
465
- } catch (error) {
466
- if (error?.code === 'ENOENT') {
467
- return res.json(buildTaskMasterQueueSummary(req.params.projectName, null, []));
468
- }
469
- console.error('TaskMaster queue loading error:', error);
470
- res.status(500).json({
471
- error: 'Failed to load TaskMaster queue',
472
- message: error.message
473
- });
474
- }
475
- });
476
-
477
- /**
478
- * GET /api/taskmaster/task/:projectName/:taskId
479
- * Load a single TaskMaster item with queue metadata.
480
- */
481
- router.get('/task/:projectName/:taskId', async (req, res) => {
482
- try {
483
- const { projectName, taskId } = req.params;
484
- const projectId = typeof req.query.projectId === 'string' && req.query.projectId.trim()
485
- ? req.query.projectId.trim()
486
- : projectName;
487
- const { projectPath, transformedTasks } = await readTaskMasterTasks(projectName);
488
- const task = transformedTasks.find((candidate) => String(candidate.id) === String(taskId));
489
- if (!task) {
490
- return res.status(404).json({
491
- success: false,
492
- error: 'TaskMaster task not found',
493
- message: `Task "${taskId}" was not found in project "${projectName}"`
494
- });
495
- }
496
- res.json({
497
- success: true,
498
- projectName,
499
- projectPath,
500
- task: attachTaskGraph(projectId, task),
501
- taskGraph: taskGraphForTask(projectId, task),
502
- execution: {
503
- supportsProvider: true,
504
- supportsModel: true,
505
- supportsFallbackProvider: true,
506
- supportsPermissionMode: true,
507
- supportsWorkerSlot: true,
508
- },
509
- });
510
- } catch (error) {
511
- console.error('TaskMaster task detail error:', error);
512
- res.status(500).json({
513
- success: false,
514
- error: 'Failed to load TaskMaster task',
515
- message: error.message
516
- });
517
- }
518
- });
519
-
520
- /**
521
- * POST /api/taskmaster/execute/:projectName/:taskId
522
- * Import a TaskMaster task into orchestration and dispatch it to a CLI agent.
523
- */
524
- router.post('/execute/:projectName/:taskId', async (req, res) => {
525
- try {
526
- const { projectName, taskId } = req.params;
527
- const adapterId = typeof req.body?.adapterId === 'string'
528
- ? req.body.adapterId
529
- : typeof req.body?.provider === 'string'
530
- ? req.body.provider
531
- : '';
532
- const model = typeof req.body?.model === 'string' ? req.body.model : undefined;
533
- const permissionMode = typeof req.body?.permissionMode === 'string' ? req.body.permissionMode : undefined;
534
- const workflowId = typeof req.body?.workflowId === 'string' ? req.body.workflowId : undefined;
535
- const fallbackProvider = typeof req.body?.fallbackProvider === 'string' ? req.body.fallbackProvider : undefined;
536
- const workerSlot = Number.isInteger(req.body?.workerSlot) ? req.body.workerSlot : undefined;
537
- const isolation = ['host', 'worktree', 'docker'].includes(req.body?.isolation)
538
- ? req.body.isolation
539
- : 'worktree';
540
- const projectId = typeof req.body?.projectId === 'string' ? req.body.projectId : projectName;
541
-
542
- if (!adapterId) {
543
- return res.status(400).json({
544
- success: false,
545
- error: 'Missing adapterId',
546
- message: 'adapterId or provider is required'
547
- });
548
- }
549
-
550
- const { projectPath, transformedTasks } = await readTaskMasterTasks(projectName);
551
- const task = transformedTasks.find((candidate) => String(candidate.id) === String(taskId));
552
- if (!task) {
553
- return res.status(404).json({
554
- success: false,
555
- error: 'TaskMaster task not found',
556
- message: `Task "${taskId}" was not found in project "${projectName}"`
557
- });
558
- }
559
-
560
- const orchestrationTask = orchestrationTaskService.upsertFromTaskMaster({
561
- projectId,
562
- taskmasterId: String(task.id),
563
- title: `TaskMaster #${task.id}: ${task.title}`,
564
- description: taskMasterExecutionDescription(task),
565
- metadata: {
566
- provider: adapterId,
567
- model,
568
- permissionMode,
569
- fallbackProvider,
570
- workerSlot,
571
- },
572
- });
573
-
574
- let dispatchedTask;
575
- let workflowRun;
576
- if (workflowId) {
577
- const workflow = workflowStore.getWorkflow(workflowId);
578
- if (!workflow) {
579
- return res.status(404).json({
580
- success: false,
581
- error: 'Workflow not found',
582
- message: `Workflow "${workflowId}" was not found`
583
- });
584
- }
585
- workflowRun = workflowRunner.start(workflow, taskMasterExecutionDescription(task), {
586
- projectId,
587
- projectName,
588
- projectPath,
589
- selectedProjectPath: projectPath,
590
- taskmasterId: String(task.id),
591
- taskmasterTaskTitle: task.title,
592
- orchestrationTaskId: orchestrationTask.id,
593
- workflowName: workflow.name,
594
- settings: {
595
- isolation,
596
- keepWorkspace: true,
597
- baseRef: 'HEAD',
598
- },
599
- taskGraph: {
600
- taskmasterId: String(task.id),
601
- orchestrationTaskId: orchestrationTask.id,
602
- source: 'taskmaster',
603
- },
604
- });
605
- dispatchedTask = orchestrationTaskService.linkWorkflowRun(orchestrationTask.id, workflowRun) ?? orchestrationTask;
606
- } else {
607
- dispatchedTask = await orchestrationTaskService.dispatch(orchestrationTask.id, {
608
- adapterId,
609
- isolation,
610
- projectPath,
611
- model,
612
- permissionMode,
613
- fallbackProvider,
614
- workerSlot,
615
- });
616
- }
617
-
618
- res.json({
619
- success: true,
620
- projectName,
621
- projectPath,
622
- taskmasterTask: task,
623
- execution: {
624
- provider: adapterId,
625
- model,
626
- permissionMode,
627
- fallbackProvider,
628
- workerSlot,
629
- workflowId,
630
- },
631
- task: dispatchedTask,
632
- run: workflowRun,
633
- taskGraph: taskGraphForTask(projectId, task),
634
- });
635
- } catch (error) {
636
- console.error('TaskMaster execute error:', error);
637
- res.status(500).json({
638
- success: false,
639
- error: 'Failed to execute TaskMaster task',
640
- message: error.message
641
- });
642
- }
643
- });
644
-
645
- /**
646
- * POST /api/taskmaster/sync-orchestration/:projectName
647
- * One-way sync: TaskMaster -> Orchestration tasks
648
- */
649
- router.post('/sync-orchestration/:projectName', async (req, res) => {
650
- try {
651
- const { projectName } = req.params;
652
- const { transformedTasks } = await readTaskMasterTasks(projectName);
653
-
654
- const projectId = typeof req.body?.projectId === 'string' && req.body.projectId.trim()
655
- ? req.body.projectId.trim()
656
- : projectName;
657
-
658
- const syncedTasks = transformedTasks.map((task) => {
659
- const taskGraph = taskGraphForTask(projectId, task);
660
- return orchestrationTaskService.upsertFromTaskMaster({
661
- projectId,
662
- taskmasterId: String(task.id),
663
- title: task.title,
664
- description: task.description,
665
- acceptanceCriteria: taskGraph.acceptanceCriteria,
666
- changedFiles: taskGraph.changedFiles,
667
- });
668
- });
669
-
670
- res.json({
671
- success: true,
672
- projectName,
673
- projectId,
674
- synced: syncedTasks.length,
675
- tasks: syncedTasks,
676
- timestamp: new Date().toISOString(),
677
- });
678
- } catch (error) {
679
- if (error?.code === 'ENOENT') {
680
- return res.json({ success: true, projectName: req.params.projectName, synced: 0, tasks: [] });
681
- }
682
- console.error('TaskMaster orchestration sync error:', error);
683
- res.status(500).json({
684
- success: false,
685
- error: 'Failed to sync TaskMaster tasks to orchestration',
686
- message: error.message,
687
- });
688
- }
689
- });
690
-
691
- /**
692
- * GET /api/taskmaster/prd/:projectName
693
- * List all PRD files in the project's .taskmaster/docs directory
694
- */
695
- router.get('/prd/:projectName', async (req, res) => {
696
- try {
697
- const { projectName } = req.params;
698
-
699
- // Get project path
700
- let projectPath;
701
- try {
702
- projectPath = await extractProjectDirectory(projectName);
703
- } catch (error) {
704
- return res.status(404).json({
705
- error: 'Project not found',
706
- message: `Project "${projectName}" does not exist`
707
- });
708
- }
709
-
710
- const docsPath = path.join(projectPath, '.taskmaster', 'docs');
711
-
712
- // Check if docs directory exists
713
- try {
714
- await fsPromises.access(docsPath, fs.constants.R_OK);
715
- } catch (error) {
716
- return res.json({
717
- projectName,
718
- prdFiles: [],
719
- message: 'No .taskmaster/docs directory found'
720
- });
721
- }
722
-
723
- // Read directory and filter for PRD files
724
- try {
725
- const files = await fsPromises.readdir(docsPath);
726
- const prdFiles = [];
727
-
728
- for (const file of files) {
729
- const filePath = path.join(docsPath, file);
730
- const stats = await fsPromises.stat(filePath);
731
-
732
- if (stats.isFile() && (file.endsWith('.txt') || file.endsWith('.md'))) {
733
- prdFiles.push({
734
- name: file,
735
- path: path.relative(projectPath, filePath),
736
- size: stats.size,
737
- modified: stats.mtime.toISOString(),
738
- created: stats.birthtime.toISOString()
739
- });
740
- }
741
- }
742
-
743
- res.json({
744
- projectName,
745
- projectPath,
746
- prdFiles: prdFiles.sort((a, b) => new Date(b.modified) - new Date(a.modified)),
747
- timestamp: new Date().toISOString()
748
- });
749
-
750
- } catch (readError) {
751
- console.error('Error reading docs directory:', readError);
752
- return res.status(500).json({
753
- error: 'Failed to read PRD files',
754
- message: readError.message
755
- });
756
- }
757
-
758
- } catch (error) {
759
- console.error('PRD list error:', error);
760
- res.status(500).json({
761
- error: 'Failed to list PRD files',
762
- message: error.message
763
- });
764
- }
765
- });
766
-
767
- /**
768
- * POST /api/taskmaster/prd/:projectName
769
- * Create or update a PRD file in the project's .taskmaster/docs directory
770
- */
771
- router.post('/prd/:projectName', async (req, res) => {
772
- try {
773
- const { projectName } = req.params;
774
- const { fileName, content } = req.body;
775
-
776
- if (!fileName || !content) {
777
- return res.status(400).json({
778
- error: 'Missing required fields',
779
- message: 'fileName and content are required'
780
- });
781
- }
782
-
783
- // Validate filename
784
- if (!fileName.match(/^[\w\-. ]+\.(txt|md)$/)) {
785
- return res.status(400).json({
786
- error: 'Invalid filename',
787
- message: 'Filename must end with .txt or .md and contain only alphanumeric characters, spaces, dots, and dashes'
788
- });
789
- }
790
-
791
- // Get project path
792
- let projectPath;
793
- try {
794
- projectPath = await extractProjectDirectory(projectName);
795
- } catch (error) {
796
- return res.status(404).json({
797
- error: 'Project not found',
798
- message: `Project "${projectName}" does not exist`
799
- });
800
- }
801
-
802
- const docsPath = path.join(projectPath, '.taskmaster', 'docs');
803
- const filePath = path.join(docsPath, fileName);
804
-
805
- // Ensure docs directory exists
806
- try {
807
- await fsPromises.mkdir(docsPath, { recursive: true });
808
- } catch (error) {
809
- console.error('Failed to create docs directory:', error);
810
- return res.status(500).json({
811
- error: 'Failed to create directory',
812
- message: error.message
813
- });
814
- }
815
-
816
- // Write the PRD file
817
- try {
818
- await fsPromises.writeFile(filePath, content, 'utf8');
819
-
820
- // Get file stats
821
- const stats = await fsPromises.stat(filePath);
822
-
823
- res.json({
824
- projectName,
825
- projectPath,
826
- fileName,
827
- filePath: path.relative(projectPath, filePath),
828
- size: stats.size,
829
- created: stats.birthtime.toISOString(),
830
- modified: stats.mtime.toISOString(),
831
- message: 'PRD file saved successfully',
832
- timestamp: new Date().toISOString()
833
- });
834
-
835
- } catch (writeError) {
836
- console.error('Failed to write PRD file:', writeError);
837
- return res.status(500).json({
838
- error: 'Failed to write PRD file',
839
- message: writeError.message
840
- });
841
- }
842
-
843
- } catch (error) {
844
- console.error('PRD create/update error:', error);
845
- res.status(500).json({
846
- error: 'Failed to create/update PRD file',
847
- message: error.message
848
- });
849
- }
850
- });
851
-
852
- /**
853
- * GET /api/taskmaster/prd/:projectName/:fileName
854
- * Get content of a specific PRD file
855
- */
856
- router.get('/prd/:projectName/:fileName', async (req, res) => {
857
- try {
858
- const { projectName, fileName } = req.params;
859
-
860
- // Get project path
861
- let projectPath;
862
- try {
863
- projectPath = await extractProjectDirectory(projectName);
864
- } catch (error) {
865
- return res.status(404).json({
866
- error: 'Project not found',
867
- message: `Project "${projectName}" does not exist`
868
- });
869
- }
870
-
871
- const filePath = path.join(projectPath, '.taskmaster', 'docs', fileName);
872
-
873
- // Check if file exists
874
- try {
875
- await fsPromises.access(filePath, fs.constants.R_OK);
876
- } catch (error) {
877
- return res.status(404).json({
878
- error: 'PRD file not found',
879
- message: `File "${fileName}" does not exist`
880
- });
881
- }
882
-
883
- // Read file content
884
- try {
885
- const content = await fsPromises.readFile(filePath, 'utf8');
886
- const stats = await fsPromises.stat(filePath);
887
-
888
- res.json({
889
- projectName,
890
- projectPath,
891
- fileName,
892
- filePath: path.relative(projectPath, filePath),
893
- content,
894
- size: stats.size,
895
- created: stats.birthtime.toISOString(),
896
- modified: stats.mtime.toISOString(),
897
- timestamp: new Date().toISOString()
898
- });
899
-
900
- } catch (readError) {
901
- console.error('Failed to read PRD file:', readError);
902
- return res.status(500).json({
903
- error: 'Failed to read PRD file',
904
- message: readError.message
905
- });
906
- }
907
-
908
- } catch (error) {
909
- console.error('PRD read error:', error);
910
- res.status(500).json({
911
- error: 'Failed to read PRD file',
912
- message: error.message
913
- });
914
- }
915
- });
916
-
917
- /**
918
- * POST /api/taskmaster/init/:projectName
919
- * Initialize TaskMaster in a project
920
- */
921
- router.post('/init/:projectName', async (req, res) => {
922
- try {
923
- const { projectName } = req.params;
924
-
925
- // Get project path
926
- let projectPath;
927
- try {
928
- projectPath = await extractProjectDirectory(projectName);
929
- } catch (error) {
930
- return res.status(404).json({
931
- error: 'Project not found',
932
- message: `Project "${projectName}" does not exist`
933
- });
934
- }
935
-
936
- // Check if TaskMaster is already initialized
937
- const taskMasterPath = path.join(projectPath, '.taskmaster');
938
- try {
939
- await fsPromises.access(taskMasterPath, fs.constants.F_OK);
940
- return res.status(400).json({
941
- error: 'TaskMaster already initialized',
942
- message: 'TaskMaster is already configured for this project'
943
- });
944
- } catch (error) {
945
- // Directory doesn't exist, we can proceed
946
- }
947
-
948
- // Run taskmaster init command
949
- const initProcess = spawn('npx', ['task-master', 'init'], {
950
- cwd: projectPath,
951
- stdio: ['pipe', 'pipe', 'pipe'],
952
- env: await buildTaskMasterCliEnv()
953
- });
954
-
955
- let stdout = '';
956
- let stderr = '';
957
-
958
- initProcess.stdout.on('data', (data) => {
959
- stdout += data.toString();
960
- });
961
-
962
- initProcess.stderr.on('data', (data) => {
963
- stderr += data.toString();
964
- });
965
-
966
- initProcess.on('close', (code) => {
967
- if (code === 0) {
968
- // Broadcast TaskMaster project update via WebSocket
969
- if (req.app.locals.wss) {
970
- broadcastTaskMasterProjectUpdate(
971
- req.app.locals.wss,
972
- projectName,
973
- { hasTaskmaster: true, status: 'initialized' }
974
- );
975
- }
976
-
977
- res.json({
978
- projectName,
979
- projectPath,
980
- message: 'TaskMaster initialized successfully',
981
- output: stdout,
982
- timestamp: new Date().toISOString()
983
- });
984
- } else {
985
- console.error('TaskMaster init failed:', stderr);
986
- res.status(500).json({
987
- error: 'Failed to initialize TaskMaster',
988
- message: stderr || stdout,
989
- code
990
- });
991
- }
992
- });
993
-
994
- // Send 'yes' responses to automated prompts
995
- initProcess.stdin.write('yes\n');
996
- initProcess.stdin.end();
997
-
998
- } catch (error) {
999
- console.error('TaskMaster init error:', error);
1000
- res.status(500).json({
1001
- error: 'Failed to initialize TaskMaster',
1002
- message: error.message
1003
- });
1004
- }
1005
- });
1006
-
1007
- /**
1008
- * POST /api/taskmaster/add-task/:projectName
1009
- * Add a new task to the project
1010
- */
1011
- router.post('/add-task/:projectName', async (req, res) => {
1012
- try {
1013
- const { projectName } = req.params;
1014
- const { prompt, title, description, priority = 'medium', dependencies } = req.body;
1015
-
1016
- if (!prompt && (!title || !description)) {
1017
- return res.status(400).json({
1018
- error: 'Missing required parameters',
1019
- message: 'Either "prompt" or both "title" and "description" are required'
1020
- });
1021
- }
1022
-
1023
- // Get project path
1024
- let projectPath;
1025
- try {
1026
- projectPath = await extractProjectDirectory(projectName);
1027
- } catch (error) {
1028
- return res.status(404).json({
1029
- error: 'Project not found',
1030
- message: `Project "${projectName}" does not exist`
1031
- });
1032
- }
1033
-
1034
- // Build the task-master add-task command
1035
- const args = ['task-master-ai', 'add-task'];
1036
-
1037
- if (prompt) {
1038
- args.push('--prompt', prompt);
1039
- args.push('--research'); // Use research for AI-generated tasks
1040
- } else {
1041
- args.push('--prompt', `Create a task titled "${title}" with description: ${description}`);
1042
- }
1043
-
1044
- if (priority) {
1045
- args.push('--priority', priority);
1046
- }
1047
-
1048
- if (dependencies) {
1049
- args.push('--dependencies', dependencies);
1050
- }
1051
-
1052
- // Run task-master add-task command
1053
- const addTaskProcess = spawn('npx', args, {
1054
- cwd: projectPath,
1055
- stdio: ['pipe', 'pipe', 'pipe'],
1056
- env: await buildTaskMasterCliEnv()
1057
- });
1058
-
1059
- let stdout = '';
1060
- let stderr = '';
1061
-
1062
- addTaskProcess.stdout.on('data', (data) => {
1063
- stdout += data.toString();
1064
- });
1065
-
1066
- addTaskProcess.stderr.on('data', (data) => {
1067
- stderr += data.toString();
1068
- });
1069
-
1070
- addTaskProcess.on('close', (code) => {
1071
- console.log('Add task process completed with code:', code);
1072
- console.log('Stdout:', stdout);
1073
- console.log('Stderr:', stderr);
1074
-
1075
- if (code === 0) {
1076
- // Broadcast task update via WebSocket
1077
- if (req.app.locals.wss) {
1078
- broadcastTaskMasterTasksUpdate(
1079
- req.app.locals.wss,
1080
- projectName
1081
- );
1082
- }
1083
-
1084
- res.json({
1085
- projectName,
1086
- projectPath,
1087
- message: 'Task added successfully',
1088
- output: stdout,
1089
- timestamp: new Date().toISOString()
1090
- });
1091
- } else {
1092
- console.error('Add task failed:', stderr);
1093
- res.status(500).json({
1094
- error: 'Failed to add task',
1095
- message: stderr || stdout,
1096
- code
1097
- });
1098
- }
1099
- });
1100
-
1101
- addTaskProcess.stdin.end();
1102
-
1103
- } catch (error) {
1104
- console.error('Add task error:', error);
1105
- res.status(500).json({
1106
- error: 'Failed to add task',
1107
- message: error.message
1108
- });
1109
- }
1110
- });
1111
-
1112
- /**
1113
- * PUT /api/taskmaster/update-task/:projectName/:taskId
1114
- * Update a specific task using TaskMaster CLI
1115
- */
1116
- router.put('/update-task/:projectName/:taskId', async (req, res) => {
1117
- try {
1118
- const { projectName, taskId } = req.params;
1119
- const { title, description, status, priority, details } = req.body;
1120
-
1121
- // Get project path
1122
- let projectPath;
1123
- try {
1124
- projectPath = await extractProjectDirectory(projectName);
1125
- } catch (error) {
1126
- return res.status(404).json({
1127
- error: 'Project not found',
1128
- message: `Project "${projectName}" does not exist`
1129
- });
1130
- }
1131
-
1132
- // If only updating status, use set-status command
1133
- if (status && Object.keys(req.body).length === 1) {
1134
- const setStatusProcess = spawn('npx', ['task-master-ai', 'set-status', `--id=${taskId}`, `--status=${status}`], {
1135
- cwd: projectPath,
1136
- stdio: ['pipe', 'pipe', 'pipe'],
1137
- env: await buildTaskMasterCliEnv()
1138
- });
1139
-
1140
- let stdout = '';
1141
- let stderr = '';
1142
-
1143
- setStatusProcess.stdout.on('data', (data) => {
1144
- stdout += data.toString();
1145
- });
1146
-
1147
- setStatusProcess.stderr.on('data', (data) => {
1148
- stderr += data.toString();
1149
- });
1150
-
1151
- setStatusProcess.on('close', (code) => {
1152
- if (code === 0) {
1153
- // Broadcast task update via WebSocket
1154
- if (req.app.locals.wss) {
1155
- broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectName);
1156
- }
1157
-
1158
- res.json({
1159
- projectName,
1160
- projectPath,
1161
- taskId,
1162
- message: 'Task status updated successfully',
1163
- output: stdout,
1164
- timestamp: new Date().toISOString()
1165
- });
1166
- } else {
1167
- console.error('Set task status failed:', stderr);
1168
- res.status(500).json({
1169
- error: 'Failed to update task status',
1170
- message: stderr || stdout,
1171
- code
1172
- });
1173
- }
1174
- });
1175
-
1176
- setStatusProcess.stdin.end();
1177
- } else {
1178
- // For other updates, use update-task command with a prompt describing the changes
1179
- const updates = [];
1180
- if (title) updates.push(`title: "${title}"`);
1181
- if (description) updates.push(`description: "${description}"`);
1182
- if (priority) updates.push(`priority: "${priority}"`);
1183
- if (details) updates.push(`details: "${details}"`);
1184
-
1185
- const prompt = `Update task with the following changes: ${updates.join(', ')}`;
1186
-
1187
- const updateProcess = spawn('npx', ['task-master-ai', 'update-task', `--id=${taskId}`, `--prompt=${prompt}`], {
1188
- cwd: projectPath,
1189
- stdio: ['pipe', 'pipe', 'pipe'],
1190
- env: await buildTaskMasterCliEnv()
1191
- });
1192
-
1193
- let stdout = '';
1194
- let stderr = '';
1195
-
1196
- updateProcess.stdout.on('data', (data) => {
1197
- stdout += data.toString();
1198
- });
1199
-
1200
- updateProcess.stderr.on('data', (data) => {
1201
- stderr += data.toString();
1202
- });
1203
-
1204
- updateProcess.on('close', (code) => {
1205
- if (code === 0) {
1206
- // Broadcast task update via WebSocket
1207
- if (req.app.locals.wss) {
1208
- broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectName);
1209
- }
1210
-
1211
- res.json({
1212
- projectName,
1213
- projectPath,
1214
- taskId,
1215
- message: 'Task updated successfully',
1216
- output: stdout,
1217
- timestamp: new Date().toISOString()
1218
- });
1219
- } else {
1220
- console.error('Update task failed:', stderr);
1221
- res.status(500).json({
1222
- error: 'Failed to update task',
1223
- message: stderr || stdout,
1224
- code
1225
- });
1226
- }
1227
- });
1228
-
1229
- updateProcess.stdin.end();
1230
- }
1231
-
1232
- } catch (error) {
1233
- console.error('Update task error:', error);
1234
- res.status(500).json({
1235
- error: 'Failed to update task',
1236
- message: error.message
1237
- });
1238
- }
1239
- });
1240
-
1241
- /**
1242
- * POST /api/taskmaster/parse-prd/:projectName
1243
- * Parse a PRD file to generate tasks
1244
- */
1245
- router.post('/parse-prd/:projectName', async (req, res) => {
1246
- try {
1247
- const { projectName } = req.params;
1248
- const { fileName = 'prd.txt', numTasks, append = false } = req.body;
1249
-
1250
- // Get project path
1251
- let projectPath;
1252
- try {
1253
- projectPath = await extractProjectDirectory(projectName);
1254
- } catch (error) {
1255
- return res.status(404).json({
1256
- error: 'Project not found',
1257
- message: `Project "${projectName}" does not exist`
1258
- });
1259
- }
1260
-
1261
- const prdPath = path.join(projectPath, '.taskmaster', 'docs', fileName);
1262
-
1263
- // Check if PRD file exists
1264
- try {
1265
- await fsPromises.access(prdPath, fs.constants.F_OK);
1266
- } catch (error) {
1267
- return res.status(404).json({
1268
- error: 'PRD file not found',
1269
- message: `File "${fileName}" does not exist in .taskmaster/docs/`
1270
- });
1271
- }
1272
-
1273
- // Build the command args
1274
- const args = ['task-master-ai', 'parse-prd', prdPath];
1275
-
1276
- if (numTasks) {
1277
- args.push('--num-tasks', numTasks.toString());
1278
- }
1279
-
1280
- if (append) {
1281
- args.push('--append');
1282
- }
1283
-
1284
- args.push('--research'); // Use research for better PRD parsing
1285
-
1286
- // Run task-master parse-prd command
1287
- const parsePRDProcess = spawn('npx', args, {
1288
- cwd: projectPath,
1289
- stdio: ['pipe', 'pipe', 'pipe'],
1290
- env: await buildTaskMasterCliEnv()
1291
- });
1292
-
1293
- let stdout = '';
1294
- let stderr = '';
1295
-
1296
- parsePRDProcess.stdout.on('data', (data) => {
1297
- stdout += data.toString();
1298
- });
1299
-
1300
- parsePRDProcess.stderr.on('data', (data) => {
1301
- stderr += data.toString();
1302
- });
1303
-
1304
- parsePRDProcess.on('close', (code) => {
1305
- if (code === 0) {
1306
- // Broadcast task update via WebSocket
1307
- if (req.app.locals.wss) {
1308
- broadcastTaskMasterTasksUpdate(
1309
- req.app.locals.wss,
1310
- projectName
1311
- );
1312
- }
1313
-
1314
- res.json({
1315
- projectName,
1316
- projectPath,
1317
- prdFile: fileName,
1318
- message: 'PRD parsed and tasks generated successfully',
1319
- output: stdout,
1320
- timestamp: new Date().toISOString()
1321
- });
1322
- } else {
1323
- console.error('Parse PRD failed:', stderr);
1324
- res.status(500).json({
1325
- error: 'Failed to parse PRD',
1326
- message: stderr || stdout,
1327
- code
1328
- });
1329
- }
1330
- });
1331
-
1332
- parsePRDProcess.stdin.end();
1333
-
1334
- } catch (error) {
1335
- console.error('Parse PRD error:', error);
1336
- res.status(500).json({
1337
- error: 'Failed to parse PRD',
1338
- message: error.message
1339
- });
1340
- }
1341
- });
1342
-
1343
- /**
1344
- * GET /api/taskmaster/prd-templates
1345
- * Get available PRD templates
1346
- */
1347
- router.get('/prd-templates', async (req, res) => {
1348
- try {
1349
- // Return built-in templates
1350
- const templates = [
1351
- {
1352
- id: 'web-app',
1353
- name: 'Web Application',
1354
- description: 'Template for web application projects with frontend and backend components',
1355
- category: 'web',
1356
- content: `# Product Requirements Document - Web Application
1357
-
1358
- ## Overview
1359
- **Product Name:** [Your App Name]
1360
- **Version:** 1.0
1361
- **Date:** ${new Date().toISOString().split('T')[0]}
1362
- **Author:** [Your Name]
1363
-
1364
- ## Executive Summary
1365
- Brief description of what this web application will do and why it's needed.
1366
-
1367
- ## Product Goals
1368
- - Goal 1: [Specific measurable goal]
1369
- - Goal 2: [Specific measurable goal]
1370
- - Goal 3: [Specific measurable goal]
1371
-
1372
- ## User Stories
1373
- ### Core Features
1374
- 1. **User Registration & Authentication**
1375
- - As a user, I want to create an account so I can access personalized features
1376
- - As a user, I want to log in securely so my data is protected
1377
- - As a user, I want to reset my password if I forget it
1378
-
1379
- 2. **Main Application Features**
1380
- - As a user, I want to [core feature 1] so I can [benefit]
1381
- - As a user, I want to [core feature 2] so I can [benefit]
1382
- - As a user, I want to [core feature 3] so I can [benefit]
1383
-
1384
- 3. **User Interface**
1385
- - As a user, I want a responsive design so I can use the app on any device
1386
- - As a user, I want intuitive navigation so I can easily find features
1387
-
1388
- ## Technical Requirements
1389
- ### Frontend
1390
- - Framework: React/Vue/Angular or vanilla JavaScript
1391
- - Styling: CSS framework (Tailwind, Bootstrap, etc.)
1392
- - State Management: Redux/Vuex/Context API
1393
- - Build Tools: Webpack/Vite
1394
- - Testing: Jest/Vitest for unit tests
1395
-
1396
- ### Backend
1397
- - Runtime: Node.js/Python/Java
1398
- - Database: PostgreSQL/MySQL/MongoDB
1399
- - API: RESTful API or GraphQL
1400
- - Authentication: JWT tokens
1401
- - Testing: Integration and unit tests
1402
-
1403
- ### Infrastructure
1404
- - Hosting: Cloud provider (AWS, Azure, GCP)
1405
- - CI/CD: GitHub Actions/GitLab CI
1406
- - Monitoring: Application monitoring tools
1407
- - Security: HTTPS, input validation, rate limiting
1408
-
1409
- ## Success Metrics
1410
- - User engagement metrics
1411
- - Performance benchmarks (load time < 2s)
1412
- - Error rates < 1%
1413
- - User satisfaction scores
1414
-
1415
- ## Timeline
1416
- - Phase 1: Core functionality (4-6 weeks)
1417
- - Phase 2: Advanced features (2-4 weeks)
1418
- - Phase 3: Polish and launch (2 weeks)
1419
-
1420
- ## Constraints & Assumptions
1421
- - Budget constraints
1422
- - Technical limitations
1423
- - Team size and expertise
1424
- - Timeline constraints`
1425
- },
1426
- {
1427
- id: 'api',
1428
- name: 'REST API',
1429
- description: 'Template for REST API development projects',
1430
- category: 'backend',
1431
- content: `# Product Requirements Document - REST API
1432
-
1433
- ## Overview
1434
- **API Name:** [Your API Name]
1435
- **Version:** v1.0
1436
- **Date:** ${new Date().toISOString().split('T')[0]}
1437
- **Author:** [Your Name]
1438
-
1439
- ## Executive Summary
1440
- Description of the API's purpose, target users, and primary use cases.
1441
-
1442
- ## API Goals
1443
- - Goal 1: Provide secure data access
1444
- - Goal 2: Ensure scalable architecture
1445
- - Goal 3: Maintain high availability (99.9% uptime)
1446
-
1447
- ## Functional Requirements
1448
- ### Core Endpoints
1449
- 1. **Authentication Endpoints**
1450
- - POST /api/auth/login - User authentication
1451
- - POST /api/auth/logout - User logout
1452
- - POST /api/auth/refresh - Token refresh
1453
- - POST /api/auth/register - User registration
1454
-
1455
- 2. **Data Management Endpoints**
1456
- - GET /api/resources - List resources with pagination
1457
- - GET /api/resources/{id} - Get specific resource
1458
- - POST /api/resources - Create new resource
1459
- - PUT /api/resources/{id} - Update existing resource
1460
- - DELETE /api/resources/{id} - Delete resource
1461
-
1462
- 3. **Administrative Endpoints**
1463
- - GET /api/admin/users - Manage users (admin only)
1464
- - GET /api/admin/analytics - System analytics
1465
- - POST /api/admin/backup - Trigger system backup
1466
-
1467
- ## Technical Requirements
1468
- ### API Design
1469
- - RESTful architecture following OpenAPI 3.0 specification
1470
- - JSON request/response format
1471
- - Consistent error response format
1472
- - API versioning strategy
1473
-
1474
- ### Authentication & Security
1475
- - JWT token-based authentication
1476
- - Role-based access control (RBAC)
1477
- - Rate limiting (100 requests/minute per user)
1478
- - Input validation and sanitization
1479
- - HTTPS enforcement
1480
-
1481
- ### Database
1482
- - Database type: [PostgreSQL/MongoDB/MySQL]
1483
- - Connection pooling
1484
- - Database migrations
1485
- - Backup and recovery procedures
1486
-
1487
- ### Performance Requirements
1488
- - Response time: < 200ms for 95% of requests
1489
- - Throughput: 1000+ requests/second
1490
- - Concurrent users: 10,000+
1491
- - Database query optimization
1492
-
1493
- ### Documentation
1494
- - Auto-generated API documentation (Swagger/OpenAPI)
1495
- - Code examples for common use cases
1496
- - SDK development for major languages
1497
- - Postman collection for testing
1498
-
1499
- ## Error Handling
1500
- - Standardized error codes and messages
1501
- - Proper HTTP status codes
1502
- - Detailed error logging
1503
- - Graceful degradation strategies
1504
-
1505
- ## Testing Strategy
1506
- - Unit tests (80%+ coverage)
1507
- - Integration tests for all endpoints
1508
- - Load testing and performance testing
1509
- - Security testing (OWASP compliance)
1510
-
1511
- ## Monitoring & Logging
1512
- - Application performance monitoring
1513
- - Error tracking and alerting
1514
- - Access logs and audit trails
1515
- - Health check endpoints
1516
-
1517
- ## Deployment
1518
- - Containerized deployment (Docker)
1519
- - CI/CD pipeline setup
1520
- - Environment management (dev, staging, prod)
1521
- - Blue-green deployment strategy
1522
-
1523
- ## Success Metrics
1524
- - API uptime > 99.9%
1525
- - Average response time < 200ms
1526
- - Zero critical security vulnerabilities
1527
- - Developer adoption metrics`
1528
- },
1529
- {
1530
- id: 'mobile-app',
1531
- name: 'Mobile Application',
1532
- description: 'Template for mobile app development projects (iOS/Android)',
1533
- category: 'mobile',
1534
- content: `# Product Requirements Document - Mobile Application
1535
-
1536
- ## Overview
1537
- **App Name:** [Your App Name]
1538
- **Platform:** iOS / Android / Cross-platform
1539
- **Version:** 1.0
1540
- **Date:** ${new Date().toISOString().split('T')[0]}
1541
- **Author:** [Your Name]
1542
-
1543
- ## Executive Summary
1544
- Brief description of the mobile app's purpose, target audience, and key value proposition.
1545
-
1546
- ## Product Goals
1547
- - Goal 1: [Specific user engagement goal]
1548
- - Goal 2: [Specific functionality goal]
1549
- - Goal 3: [Specific performance goal]
1550
-
1551
- ## User Stories
1552
- ### Core Features
1553
- 1. **Onboarding & Authentication**
1554
- - As a new user, I want a simple onboarding process
1555
- - As a user, I want to sign up with email or social media
1556
- - As a user, I want biometric authentication for security
1557
-
1558
- 2. **Main App Features**
1559
- - As a user, I want [core feature 1] accessible from home screen
1560
- - As a user, I want [core feature 2] to work offline
1561
- - As a user, I want to sync data across devices
1562
-
1563
- 3. **User Experience**
1564
- - As a user, I want intuitive navigation patterns
1565
- - As a user, I want fast loading times
1566
- - As a user, I want accessibility features
1567
-
1568
- ## Technical Requirements
1569
- ### Mobile Development
1570
- - **Cross-platform:** React Native / Flutter / Xamarin
1571
- - **Native:** Swift (iOS) / Kotlin (Android)
1572
- - **State Management:** Redux / MobX / Provider
1573
- - **Navigation:** React Navigation / Flutter Navigation
1574
-
1575
- ### Backend Integration
1576
- - REST API or GraphQL integration
1577
- - Real-time features (WebSockets/Push notifications)
1578
- - Offline data synchronization
1579
- - Background processing
1580
-
1581
- ### Device Features
1582
- - Camera and photo library access
1583
- - GPS location services
1584
- - Push notifications
1585
- - Biometric authentication
1586
- - Device storage
1587
-
1588
- ### Performance Requirements
1589
- - App launch time < 3 seconds
1590
- - Screen transition animations < 300ms
1591
- - Memory usage optimization
1592
- - Battery usage optimization
1593
-
1594
- ## Platform-Specific Considerations
1595
- ### iOS Requirements
1596
- - iOS 13.0+ minimum version
1597
- - App Store guidelines compliance
1598
- - iOS design guidelines (Human Interface Guidelines)
1599
- - TestFlight beta testing
1600
-
1601
- ### Android Requirements
1602
- - Android 8.0+ (API level 26) minimum
1603
- - Google Play Store guidelines
1604
- - Material Design guidelines
1605
- - Google Play Console testing
1606
-
1607
- ## User Interface Design
1608
- - Responsive design for different screen sizes
1609
- - Dark mode support
1610
- - Accessibility compliance (WCAG 2.1)
1611
- - Consistent design system
1612
-
1613
- ## Security & Privacy
1614
- - Secure data storage (Keychain/Keystore)
1615
- - API communication encryption
1616
- - Privacy policy compliance (GDPR/CCPA)
1617
- - App security best practices
1618
-
1619
- ## Testing Strategy
1620
- - Unit testing (80%+ coverage)
1621
- - UI/E2E testing (Detox/Appium)
1622
- - Device testing on multiple screen sizes
1623
- - Performance testing
1624
- - Security testing
1625
-
1626
- ## App Store Deployment
1627
- - App store optimization (ASO)
1628
- - App icons and screenshots
1629
- - Store listing content
1630
- - Release management strategy
1631
-
1632
- ## Analytics & Monitoring
1633
- - User analytics (Firebase/Analytics)
1634
- - Crash reporting (Crashlytics/Sentry)
1635
- - Performance monitoring
1636
- - User feedback collection
1637
-
1638
- ## Success Metrics
1639
- - App store ratings > 4.0
1640
- - User retention rates
1641
- - Daily/Monthly active users
1642
- - App performance metrics
1643
- - Conversion rates`
1644
- },
1645
- {
1646
- id: 'data-analysis',
1647
- name: 'Data Analysis Project',
1648
- description: 'Template for data analysis and visualization projects',
1649
- category: 'data',
1650
- content: `# Product Requirements Document - Data Analysis Project
1651
-
1652
- ## Overview
1653
- **Project Name:** [Your Analysis Project]
1654
- **Analysis Type:** [Descriptive/Predictive/Prescriptive]
1655
- **Date:** ${new Date().toISOString().split('T')[0]}
1656
- **Author:** [Your Name]
1657
-
1658
- ## Executive Summary
1659
- Description of the business problem, data sources, and expected insights.
1660
-
1661
- ## Project Goals
1662
- - Goal 1: [Specific business question to answer]
1663
- - Goal 2: [Specific prediction to make]
1664
- - Goal 3: [Specific recommendation to provide]
1665
-
1666
- ## Business Requirements
1667
- ### Key Questions
1668
- 1. What patterns exist in the current data?
1669
- 2. What factors influence [target variable]?
1670
- 3. What predictions can be made for [future outcome]?
1671
- 4. What recommendations can improve [business metric]?
1672
-
1673
- ### Success Criteria
1674
- - Actionable insights for stakeholders
1675
- - Statistical significance in findings
1676
- - Reproducible analysis pipeline
1677
- - Clear visualization and reporting
1678
-
1679
- ## Data Requirements
1680
- ### Data Sources
1681
- 1. **Primary Data**
1682
- - Source: [Database/API/Files]
1683
- - Format: [CSV/JSON/SQL]
1684
- - Size: [Volume estimate]
1685
- - Update frequency: [Real-time/Daily/Monthly]
1686
-
1687
- 2. **External Data**
1688
- - Third-party APIs
1689
- - Public datasets
1690
- - Market research data
1691
-
1692
- ### Data Quality Requirements
1693
- - Data completeness (< 5% missing values)
1694
- - Data accuracy validation
1695
- - Data consistency checks
1696
- - Historical data availability
1697
-
1698
- ## Technical Requirements
1699
- ### Data Pipeline
1700
- - Data extraction and ingestion
1701
- - Data cleaning and preprocessing
1702
- - Data transformation and feature engineering
1703
- - Data validation and quality checks
1704
-
1705
- ### Analysis Tools
1706
- - **Programming:** Python/R/SQL
1707
- - **Libraries:** pandas, numpy, scikit-learn, matplotlib
1708
- - **Visualization:** Tableau, PowerBI, or custom dashboards
1709
- - **Version Control:** Git for code and DVC for data
1710
-
1711
- ### Computing Resources
1712
- - Local development environment
1713
- - Cloud computing (AWS/GCP/Azure) if needed
1714
- - Database access and permissions
1715
- - Storage requirements
1716
-
1717
- ## Analysis Methodology
1718
- ### Data Exploration
1719
- 1. Descriptive statistics and data profiling
1720
- 2. Data visualization and pattern identification
1721
- 3. Correlation analysis
1722
- 4. Outlier detection and handling
1723
-
1724
- ### Statistical Analysis
1725
- 1. Hypothesis formulation
1726
- 2. Statistical testing
1727
- 3. Confidence intervals
1728
- 4. Effect size calculations
1729
-
1730
- ### Machine Learning (if applicable)
1731
- 1. Feature selection and engineering
1732
- 2. Model selection and training
1733
- 3. Cross-validation and evaluation
1734
- 4. Model interpretation and explainability
1735
-
1736
- ## Deliverables
1737
- ### Reports
1738
- - Executive summary for stakeholders
1739
- - Technical analysis report
1740
- - Data quality report
1741
- - Methodology documentation
1742
-
1743
- ### Visualizations
1744
- - Interactive dashboards
1745
- - Static charts and graphs
1746
- - Data story presentations
1747
- - Key findings infographics
1748
-
1749
- ### Code & Documentation
1750
- - Reproducible analysis scripts
1751
- - Data pipeline code
1752
- - Documentation and comments
1753
- - Testing and validation code
1754
-
1755
- ## Timeline
1756
- - Phase 1: Data collection and exploration (2 weeks)
1757
- - Phase 2: Analysis and modeling (3 weeks)
1758
- - Phase 3: Reporting and visualization (1 week)
1759
- - Phase 4: Stakeholder presentation (1 week)
1760
-
1761
- ## Risks & Assumptions
1762
- - Data availability and quality risks
1763
- - Technical complexity assumptions
1764
- - Resource and timeline constraints
1765
- - Stakeholder engagement assumptions
1766
-
1767
- ## Success Metrics
1768
- - Stakeholder satisfaction with insights
1769
- - Accuracy of predictions (if applicable)
1770
- - Business impact of recommendations
1771
- - Reproducibility of results`
1772
- }
1773
- ];
1774
-
1775
- res.json({
1776
- templates,
1777
- timestamp: new Date().toISOString()
1778
- });
1779
-
1780
- } catch (error) {
1781
- console.error('PRD templates error:', error);
1782
- res.status(500).json({
1783
- error: 'Failed to get PRD templates',
1784
- message: error.message
1785
- });
1786
- }
1787
- });
1788
-
1789
- /**
1790
- * POST /api/taskmaster/apply-template/:projectName
1791
- * Apply a PRD template to create a new PRD file
1792
- */
1793
- router.post('/apply-template/:projectName', async (req, res) => {
1794
- try {
1795
- const { projectName } = req.params;
1796
- const { templateId, fileName = 'prd.txt', customizations = {} } = req.body;
1797
-
1798
- if (!templateId) {
1799
- return res.status(400).json({
1800
- error: 'Missing required parameter',
1801
- message: 'templateId is required'
1802
- });
1803
- }
1804
-
1805
- // Get project path
1806
- let projectPath;
1807
- try {
1808
- projectPath = await extractProjectDirectory(projectName);
1809
- } catch (error) {
1810
- return res.status(404).json({
1811
- error: 'Project not found',
1812
- message: `Project "${projectName}" does not exist`
1813
- });
1814
- }
1815
-
1816
- // Get the template content (this would normally fetch from the templates list)
1817
- const templates = await getAvailableTemplates();
1818
- const template = templates.find(t => t.id === templateId);
1819
-
1820
- if (!template) {
1821
- return res.status(404).json({
1822
- error: 'Template not found',
1823
- message: `Template "${templateId}" does not exist`
1824
- });
1825
- }
1826
-
1827
- // Apply customizations to template content
1828
- let content = template.content;
1829
-
1830
- // Replace placeholders with customizations
1831
- for (const [key, value] of Object.entries(customizations)) {
1832
- const placeholder = `[${key}]`;
1833
- content = content.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g'), value);
1834
- }
1835
-
1836
- // Ensure .taskmaster/docs directory exists
1837
- const docsDir = path.join(projectPath, '.taskmaster', 'docs');
1838
- try {
1839
- await fsPromises.mkdir(docsDir, { recursive: true });
1840
- } catch (error) {
1841
- console.error('Failed to create docs directory:', error);
1842
- }
1843
-
1844
- const filePath = path.join(docsDir, fileName);
1845
-
1846
- // Write the template content to the file
1847
- try {
1848
- await fsPromises.writeFile(filePath, content, 'utf8');
1849
-
1850
- res.json({
1851
- projectName,
1852
- projectPath,
1853
- templateId,
1854
- templateName: template.name,
1855
- fileName,
1856
- filePath: filePath,
1857
- message: 'PRD template applied successfully',
1858
- timestamp: new Date().toISOString()
1859
- });
1860
-
1861
- } catch (writeError) {
1862
- console.error('Failed to write PRD template:', writeError);
1863
- return res.status(500).json({
1864
- error: 'Failed to write PRD template',
1865
- message: writeError.message
1866
- });
1867
- }
1868
-
1869
- } catch (error) {
1870
- console.error('Apply template error:', error);
1871
- res.status(500).json({
1872
- error: 'Failed to apply PRD template',
1873
- message: error.message
1874
- });
1875
- }
1876
- });
1877
-
1878
- // Helper function to get available templates
1879
- async function getAvailableTemplates() {
1880
- // This could be extended to read from files or database
1881
- return [
1882
- {
1883
- id: 'web-app',
1884
- name: 'Web Application',
1885
- description: 'Template for web application projects',
1886
- category: 'web',
1887
- content: `# Product Requirements Document - Web Application
1888
-
1889
- ## Overview
1890
- **Product Name:** [Your App Name]
1891
- **Version:** 1.0
1892
- **Date:** ${new Date().toISOString().split('T')[0]}
1893
- **Author:** [Your Name]
1894
-
1895
- ## Executive Summary
1896
- Brief description of what this web application will do and why it's needed.
1897
-
1898
- ## User Stories
1899
- 1. As a user, I want [feature] so I can [benefit]
1900
- 2. As a user, I want [feature] so I can [benefit]
1901
- 3. As a user, I want [feature] so I can [benefit]
1902
-
1903
- ## Technical Requirements
1904
- - Frontend framework
1905
- - Backend services
1906
- - Database requirements
1907
- - Security considerations
1908
-
1909
- ## Success Metrics
1910
- - User engagement metrics
1911
- - Performance benchmarks
1912
- - Business objectives`
1913
- },
1914
- // Add other templates here if needed
1915
- ];
1916
- }
1917
-
1918
- export default router;