@cloudcli-ai/cloudcli 1.29.2 → 1.29.4

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