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