wind-agent-cli 1.37.2 → 1.38.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 (510) hide show
  1. package/LICENSE +718 -718
  2. package/README.md +17 -2
  3. package/dist/api-docs.html +901 -901
  4. package/dist/assets/{CodeEditor-B2Wf7Fg0.js → CodeEditor-FvgsnJ7J.js} +6 -6
  5. package/dist/assets/FileTree-NLULrBDi.js +1 -0
  6. package/dist/assets/GitPanel-51KUBbEW.js +4 -0
  7. package/dist/assets/{MarkdownPreview-C0tqYnVu.js → MarkdownPreview-PEEWVWeS.js} +17 -27
  8. package/dist/assets/Shell-BEx66cW_.js +76 -0
  9. package/dist/assets/Shell-DrlLKa8f.css +1 -0
  10. package/dist/assets/StandaloneShell-BkuUCPZP.js +1 -0
  11. package/dist/assets/TaskMasterPanel-DfQG80SF.js +2 -0
  12. package/dist/assets/abnfDiagram-N423BO3Z-Cc0ul87U.js +1 -0
  13. package/dist/assets/arc-BAuugHw_.js +1 -0
  14. package/dist/assets/architectureDiagram-T3A2C74G-DA3G7QZD.js +36 -0
  15. package/dist/assets/{blockDiagram-VBNYF7ZC-DpaEf3mv.js → blockDiagram-VBNYF7ZC-tJ12IJs3.js} +4 -4
  16. package/dist/assets/c4Diagram-5PPSVZJV-5BTMCXL4.js +10 -0
  17. package/dist/assets/channel-BGDgNNQz.js +1 -0
  18. package/dist/assets/{chunk-2GRJ4B5K-DoSdNo8u.js → chunk-2GRJ4B5K-BPaI06Ou.js} +1 -1
  19. package/dist/assets/{chunk-2Q5K7J3B-CE7I3uMn.js → chunk-2Q5K7J3B-HV4zmsEe.js} +1 -1
  20. package/dist/assets/chunk-5RXB4S5H-BAwutJoU.js +231 -0
  21. package/dist/assets/{chunk-5VM5RSS4-B55XgqHZ.js → chunk-5VM5RSS4-B9QAJPrv.js} +1 -1
  22. package/dist/assets/{chunk-6Q2QTUOP-BS1KpefW.js → chunk-6Q2QTUOP-BOIW8bzI.js} +1 -1
  23. package/dist/assets/chunk-GF5L2VYU-DD13o00-.js +206 -0
  24. package/dist/assets/{chunk-JWPE2WC7-DOtkNhMm.js → chunk-JWPE2WC7-CFBm23VK.js} +1 -1
  25. package/dist/assets/{chunk-KBJHAD2P-BXLjviqM.js → chunk-KBJHAD2P-C0i0v8Op.js} +1 -1
  26. package/dist/assets/{chunk-RYQCIY6F-C50fjNNz.js → chunk-RYQCIY6F-DfeK0taV.js} +1 -1
  27. package/dist/assets/{chunk-XXDRQBXY-QBYqCkb7.js → chunk-XXDRQBXY-BKWvf-QE.js} +1 -1
  28. package/dist/assets/classDiagram-JCYQIIEL-C2SNM4oy.js +1 -0
  29. package/dist/assets/classDiagram-v2-OCEON4UE-C2SNM4oy.js +1 -0
  30. package/dist/assets/cose-bilkent-JH36ORCC-CBE8oM7L.js +1 -0
  31. package/dist/assets/{cynefin-VYW2F7L2-DnJa_OlQ.js → cynefin-VYW2F7L2-DziGp35A.js} +11 -23
  32. package/dist/assets/cynefinDiagram-MW4NZA55-CNvtHl39.js +62 -0
  33. package/dist/assets/cytoscape.esm-yzknjiTM.js +321 -0
  34. package/dist/assets/dagre-VZM6K2ZE-Bz9lsU7m.js +4 -0
  35. package/dist/assets/{diagram-7IWD3JNH-pFzs9HEu.js → diagram-7IWD3JNH-CV7D3VSd.js} +5 -5
  36. package/dist/assets/{diagram-B4RE2ZJO-CcehQC7x.js → diagram-B4RE2ZJO-Dj4EjDNC.js} +2 -2
  37. package/dist/assets/diagram-LBJQPF4R-wJ6FHvf-.js +24 -0
  38. package/dist/assets/diagram-Q27KOJAE-DMFYMhOB.js +24 -0
  39. package/dist/assets/diagram-UB23O5K3-DeBSbWAI.js +41 -0
  40. package/dist/assets/ebnfDiagram-BXEA7PRR-2NnJSGhT.js +1 -0
  41. package/dist/assets/erDiagram-JOGREHBK-DrSlg8hr.js +85 -0
  42. package/dist/assets/feature-chat-CvOCyaLe.js +88 -0
  43. package/dist/assets/feature-workspace-CAB6bgPy.js +39 -0
  44. package/dist/assets/{flowDiagram-UKHOOZJN-CJV8fQPR.js → flowDiagram-UKHOOZJN-C0pejU6W.js} +4 -4
  45. package/dist/assets/ganttDiagram-PKOTCBZU-BM0VCUUz.js +292 -0
  46. package/dist/assets/gitGraphDiagram-DS77QQ5N-SbLG0c5_.js +106 -0
  47. package/dist/assets/graph-DOmOIIwC.js +1 -0
  48. package/dist/assets/{index-GFhXZyl2.js → index-BflGfU1C.js} +3 -3
  49. package/dist/assets/index-Btkf0_rU.js +1 -0
  50. package/dist/assets/index-Cr5MbHuj.css +1 -0
  51. package/dist/assets/index-Gqazqfs3.js +1 -0
  52. package/dist/assets/index-nyrkoQK6.js +35 -0
  53. package/dist/assets/infoDiagram-6WML65LV-Cf5DSa66.js +2 -0
  54. package/dist/assets/ishikawaDiagram-WSZJBQD7-DwyFutKy.js +70 -0
  55. package/dist/assets/journeyDiagram-NVQOT4AX-Df0FRhLo.js +139 -0
  56. package/dist/assets/{kanban-definition-27J2QSJJ-B3iL0ztJ.js → kanban-definition-27J2QSJJ-HmpbjrZz.js} +6 -6
  57. package/dist/assets/{layout-D6BstSzF.js → layout-D-LzfAck.js} +1 -1
  58. package/dist/assets/{linear-Cwe9dEKA.js → linear-DOM01LBe.js} +1 -1
  59. package/dist/assets/map-DxJ2ADlA.js +1 -0
  60. package/dist/assets/mermaid.core-C2Ma-_S6.js +306 -0
  61. package/dist/assets/{mindmap-definition-FAOFIHXS-BgfBYMzr.js → mindmap-definition-FAOFIHXS-B4GOIn76.js} +4 -4
  62. package/dist/assets/pegDiagram-VL7TDLO6-BHur7aH2.js +1 -0
  63. package/dist/assets/{pieDiagram-7S7Q4E2Y-DeSrxc4I.js → pieDiagram-7S7Q4E2Y-Cw0UELLV.js} +2 -2
  64. package/dist/assets/quadrantDiagram-CIZ2JOQS-CB2BrX3g.js +7 -0
  65. package/dist/assets/railroadDiagram-AXF67PYL-DPYZYmLX.js +1 -0
  66. package/dist/assets/requirementDiagram-LRYGKXZP-DSX4IJ1n.js +84 -0
  67. package/dist/assets/sankeyDiagram-W5VNT64P-TenC3ZQo.js +40 -0
  68. package/dist/assets/sequenceDiagram-SI44F4Z6-DltGsf3F.js +162 -0
  69. package/dist/assets/sizeCapture-X5ZJPWSS-D7llwbJZ.js +1 -0
  70. package/dist/assets/stateDiagram-OKZ733FA-C-hAvbnY.js +1 -0
  71. package/dist/assets/stateDiagram-v2-UEYNNEHI-DaYSFcRQ.js +1 -0
  72. package/dist/assets/swimlanes-SLNWSIFB-DWD30fv6.js +2 -0
  73. package/dist/assets/swimlanesDiagram-ULZ7WXOC-LTkF2T6t.js +8 -0
  74. package/dist/assets/timeline-definition-Z64GVDOM-quWmimSE.js +120 -0
  75. package/dist/assets/vendor-data-DsEZeNPr.js +4 -0
  76. package/dist/assets/vendor-i18n-B6HjLCnN.js +1 -0
  77. package/dist/assets/vendor-markdown-DkITEES2.js +298 -0
  78. package/dist/assets/vendor-react-C2GTiUCy.js +10 -0
  79. package/dist/assets/vendor-ui-CAUHSABu.js +49 -0
  80. package/dist/assets/vennDiagram-T6HMQDX7-aUQcm8h5.js +34 -0
  81. package/dist/assets/{wardleyDiagram-T6FBY63Y-BNCpsFZV.js → wardleyDiagram-T6FBY63Y-BZ0bwsFQ.js} +2 -2
  82. package/dist/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
  83. package/dist/assets/xychartDiagram-ELKLHX3M-DOVzAq63.js +7 -0
  84. package/dist/clear-cache.html +85 -85
  85. package/dist/convert-icons.md +52 -52
  86. package/dist/favicon.png +0 -0
  87. package/dist/favicon.svg +8 -8
  88. package/dist/generate-icons.js +48 -48
  89. package/dist/icons/codex-white.svg +3 -3
  90. package/dist/icons/codex.svg +3 -3
  91. package/dist/icons/cursor-white.svg +11 -11
  92. package/dist/icons/icon-128x128.png +0 -0
  93. package/dist/icons/icon-128x128.svg +11 -11
  94. package/dist/icons/icon-144x144.png +0 -0
  95. package/dist/icons/icon-144x144.svg +11 -11
  96. package/dist/icons/icon-152x152.png +0 -0
  97. package/dist/icons/icon-152x152.svg +11 -11
  98. package/dist/icons/icon-180x180.png +0 -0
  99. package/dist/icons/icon-192x192.png +0 -0
  100. package/dist/icons/icon-192x192.svg +11 -11
  101. package/dist/icons/icon-384x384.png +0 -0
  102. package/dist/icons/icon-384x384.svg +11 -11
  103. package/dist/icons/icon-512x512.png +0 -0
  104. package/dist/icons/icon-512x512.svg +11 -11
  105. package/dist/icons/icon-72x72.png +0 -0
  106. package/dist/icons/icon-72x72.svg +11 -11
  107. package/dist/icons/icon-96x96.png +0 -0
  108. package/dist/icons/icon-96x96.svg +11 -11
  109. package/dist/icons/icon-maskable-192x192.png +0 -0
  110. package/dist/icons/icon-maskable-512x512.png +0 -0
  111. package/dist/icons/icon-template.svg +11 -11
  112. package/dist/icons/new-icon.png +0 -0
  113. package/dist/index.html +48 -58
  114. package/dist/logo-128.png +0 -0
  115. package/dist/logo-256.png +0 -0
  116. package/dist/logo.svg +17 -17
  117. package/dist/manifest.webmanifest +1 -0
  118. package/dist/push-sw.js +48 -0
  119. package/dist/screenshots/setup-screen-mobile.png +0 -0
  120. package/dist/screenshots/setup-screen.png +0 -0
  121. package/dist/sw.js +1 -124
  122. package/dist/workbox-ae5c0bb2.js +1 -0
  123. package/dist-server/server/index.js +1 -1
  124. package/dist-server/server/index.js.map +1 -1
  125. package/dist-server/server/load-env.js +4 -2
  126. package/dist-server/server/load-env.js.map +1 -1
  127. package/dist-server/server/modules/agent/agent.routes.js +1 -1
  128. package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
  129. package/dist-server/server/modules/browser-use/browser-use.service.js +1 -1
  130. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  131. package/dist-server/server/modules/cli/cli.service.js +37 -37
  132. package/dist-server/server/modules/cli/cli.service.js.map +1 -1
  133. package/dist-server/server/modules/cli/sandbox.service.js +43 -43
  134. package/dist-server/server/modules/cli/sandbox.service.js.map +1 -1
  135. package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +74 -74
  136. package/dist-server/server/modules/commands/commands.routes.js +25 -25
  137. package/dist-server/server/modules/commands/commands.routes.js.map +1 -1
  138. package/dist-server/server/modules/commands/tests/commands.test.js +1 -1
  139. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -1
  140. package/dist-server/server/modules/database/connection.js +1 -1
  141. package/dist-server/server/modules/database/connection.js.map +1 -1
  142. package/dist-server/server/modules/database/migrations.js +190 -190
  143. package/dist-server/server/modules/database/repositories/api-keys.js +3 -3
  144. package/dist-server/server/modules/database/repositories/github-tokens.js +2 -2
  145. package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js +28 -28
  146. package/dist-server/server/modules/database/repositories/notification-preferences.js +4 -4
  147. package/dist-server/server/modules/database/repositories/projects.db.js +61 -61
  148. package/dist-server/server/modules/database/repositories/provider-models.js +35 -35
  149. package/dist-server/server/modules/database/repositories/push-subscriptions.js +5 -5
  150. package/dist-server/server/modules/database/repositories/scan-state.db.js +5 -5
  151. package/dist-server/server/modules/database/repositories/sessions.db.js +93 -93
  152. package/dist-server/server/modules/database/schema.js +177 -177
  153. package/dist-server/server/modules/database/tests/projects.db.integration.test.js +3 -2
  154. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
  155. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +6 -6
  156. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +4 -3
  157. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
  158. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +2 -1
  159. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  160. package/dist-server/server/modules/git/git.routes.js +17 -17
  161. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +1 -1
  162. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
  163. package/dist-server/server/modules/notifications/vapid-keys.service.js +1 -1
  164. package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -1
  165. package/dist-server/server/modules/projects/tests/project-management.service.test.js +2 -1
  166. package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -1
  167. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +2 -2
  168. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -1
  169. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +1 -1
  170. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  171. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +1 -1
  172. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  173. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  174. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +13 -13
  175. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +9 -9
  176. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js +17 -17
  177. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +25 -25
  178. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +27 -27
  179. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +9 -9
  180. package/dist-server/server/modules/providers/services/sessions.service.js +1 -1
  181. package/dist-server/server/modules/providers/tests/claude-auth.test.js +8 -0
  182. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
  183. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +1 -1
  184. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  185. package/dist-server/server/modules/providers/tests/mcp.test.js +3 -3
  186. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +98 -98
  187. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  188. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +18 -18
  189. package/dist-server/server/modules/providers/tests/provider.routes.test.js +1 -1
  190. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
  191. package/dist-server/server/modules/system/system.service.js +1 -1
  192. package/dist-server/server/modules/system/system.service.js.map +1 -1
  193. package/dist-server/server/modules/system/tests/system.service.test.js +1 -1
  194. package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -1
  195. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +423 -422
  196. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
  197. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +1 -1
  198. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  199. package/dist-server/server/modules/worktrees/services/worktree-list.service.js +1 -1
  200. package/dist-server/server/modules/worktrees/services/worktree-open.service.js +1 -1
  201. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +1 -1
  202. package/electron/cloud.js +260 -260
  203. package/electron/desktopNotifications.js +378 -378
  204. package/electron/desktopWindow.js +766 -766
  205. package/electron/launcher/index.html +14 -14
  206. package/electron/launcher/launcher.css +801 -801
  207. package/electron/launcher/launcher.js +687 -687
  208. package/electron/localServer.js +549 -549
  209. package/electron/main.js +944 -944
  210. package/electron/preload.cjs +60 -60
  211. package/electron/scripts/generate-macos-icon.js +62 -62
  212. package/electron/serverInstaller.js +277 -277
  213. package/electron/tabs.js +87 -87
  214. package/electron/viewHost.js +331 -331
  215. package/package.json +262 -247
  216. package/public/api-docs.html +901 -901
  217. package/scripts/build-client.mjs +30 -0
  218. package/scripts/fix-node-pty.js +67 -67
  219. package/scripts/generate-pwa-icons.ps1 +33 -0
  220. package/scripts/promote-dist-server.mjs +50 -50
  221. package/scripts/release/build-server-bundle.js +176 -176
  222. package/scripts/release/prepare-desktop-app.js +152 -152
  223. package/scripts/verify-pwa-build.mjs +37 -0
  224. package/server/index.ts +399 -399
  225. package/server/load-env.ts +49 -47
  226. package/server/modules/agent/agent.module.ts +52 -52
  227. package/server/modules/agent/agent.routes.ts +1287 -1287
  228. package/server/modules/agent/index.ts +2 -2
  229. package/server/modules/agent/tests/agent.routes.test.ts +205 -205
  230. package/server/modules/assets/assets.routes.ts +150 -150
  231. package/server/modules/assets/index.ts +3 -3
  232. package/server/modules/assets/services/image-assets.service.ts +127 -127
  233. package/server/modules/assets/tests/image-assets.service.test.ts +74 -74
  234. package/server/modules/auth/auth.middleware.ts +163 -163
  235. package/server/modules/auth/auth.module.ts +38 -38
  236. package/server/modules/auth/auth.routes.ts +57 -57
  237. package/server/modules/auth/auth.service.ts +155 -155
  238. package/server/modules/auth/index.ts +9 -9
  239. package/server/modules/auth/tests/auth.service.test.ts +95 -95
  240. package/server/modules/browser-use/browser-use-mcp.routes.ts +120 -120
  241. package/server/modules/browser-use/browser-use-mcp.ts +386 -386
  242. package/server/modules/browser-use/browser-use-runtime.ts +10 -10
  243. package/server/modules/browser-use/browser-use.routes.ts +96 -96
  244. package/server/modules/browser-use/browser-use.service.ts +836 -836
  245. package/server/modules/browser-use/index.ts +11 -11
  246. package/server/modules/browser-use/tests/browser-use.service.test.ts +10 -10
  247. package/server/modules/cli/cli.module.ts +93 -93
  248. package/server/modules/cli/cli.service.ts +248 -248
  249. package/server/modules/cli/cli.ts +19 -19
  250. package/server/modules/cli/index.ts +2 -2
  251. package/server/modules/cli/sandbox.service.ts +398 -398
  252. package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +186 -186
  253. package/server/modules/cli/tests/cli.service.test.ts +86 -86
  254. package/server/modules/cli/tests/sandbox.service.test.ts +58 -58
  255. package/server/modules/commands/commands.module.ts +22 -22
  256. package/server/modules/commands/commands.routes.ts +587 -587
  257. package/server/modules/commands/index.ts +2 -2
  258. package/server/modules/commands/tests/commands.test.ts +110 -110
  259. package/server/modules/database/connection.ts +144 -143
  260. package/server/modules/database/index.ts +17 -17
  261. package/server/modules/database/init-db.ts +17 -17
  262. package/server/modules/database/migrations.ts +515 -515
  263. package/server/modules/database/repositories/api-keys.ts +119 -119
  264. package/server/modules/database/repositories/app-config.ts +53 -53
  265. package/server/modules/database/repositories/credentials.ts +106 -106
  266. package/server/modules/database/repositories/github-tokens.ts +100 -100
  267. package/server/modules/database/repositories/notification-channel-endpoints.ts +153 -153
  268. package/server/modules/database/repositories/notification-preferences.ts +117 -117
  269. package/server/modules/database/repositories/projects.db.ts +196 -196
  270. package/server/modules/database/repositories/provider-models.ts +168 -168
  271. package/server/modules/database/repositories/push-subscriptions.ts +80 -80
  272. package/server/modules/database/repositories/scan-state.db.ts +42 -42
  273. package/server/modules/database/repositories/sessions.db.ts +524 -524
  274. package/server/modules/database/repositories/users.ts +140 -140
  275. package/server/modules/database/repositories/vapid-keys.ts +57 -57
  276. package/server/modules/database/schema.ts +210 -210
  277. package/server/modules/database/tests/projects.db.integration.test.ts +73 -72
  278. package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -125
  279. package/server/modules/database/tests/sessions-provider-mapping.test.ts +110 -109
  280. package/server/modules/database/tests/sessions.db.integration.test.ts +117 -116
  281. package/server/modules/file-tree/file-tree.module.ts +116 -116
  282. package/server/modules/file-tree/file-tree.routes.ts +258 -258
  283. package/server/modules/file-tree/file-tree.service.ts +675 -675
  284. package/server/modules/file-tree/index.ts +2 -2
  285. package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -157
  286. package/server/modules/file-tree/tests/file-tree.service.test.ts +371 -371
  287. package/server/modules/git/git-branch.service.ts +43 -43
  288. package/server/modules/git/git-parsing.service.ts +66 -66
  289. package/server/modules/git/git.module.ts +22 -22
  290. package/server/modules/git/git.routes.ts +1596 -1596
  291. package/server/modules/git/index.ts +2 -2
  292. package/server/modules/git/tests/git-branch.service.test.ts +60 -60
  293. package/server/modules/git/tests/git-init.routes.test.ts +132 -132
  294. package/server/modules/git/tests/git.test.ts +106 -106
  295. package/server/modules/notifications/index.ts +24 -24
  296. package/server/modules/notifications/notifications.routes.ts +127 -127
  297. package/server/modules/notifications/services/desktop-notification-clients.service.ts +124 -124
  298. package/server/modules/notifications/services/notification-orchestrator.service.js +310 -310
  299. package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -55
  300. package/server/modules/notifications/vapid-keys.service.ts +38 -38
  301. package/server/modules/notifications/websocket/desktop-notifications-websocket.service.ts +109 -109
  302. package/server/modules/plugins/index.ts +9 -9
  303. package/server/modules/plugins/plugin-process.service.ts +217 -217
  304. package/server/modules/plugins/plugin-registry.service.ts +459 -459
  305. package/server/modules/plugins/plugins.module.ts +33 -33
  306. package/server/modules/plugins/plugins.routes.ts +66 -66
  307. package/server/modules/plugins/plugins.service.ts +147 -147
  308. package/server/modules/plugins/tests/plugins.service.test.ts +32 -32
  309. package/server/modules/projects/index.ts +11 -11
  310. package/server/modules/projects/projects.routes.ts +273 -273
  311. package/server/modules/projects/services/project-clone.service.ts +323 -323
  312. package/server/modules/projects/services/project-delete.service.ts +90 -90
  313. package/server/modules/projects/services/project-management.service.ts +144 -144
  314. package/server/modules/projects/services/project-star.service.ts +78 -78
  315. package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -248
  316. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +314 -314
  317. package/server/modules/projects/tests/project-clone.service.test.ts +183 -183
  318. package/server/modules/projects/tests/project-management.service.test.ts +118 -117
  319. package/server/modules/projects/tests/project-star.service.test.ts +123 -123
  320. package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -105
  321. package/server/modules/providers/README.md +380 -380
  322. package/server/modules/providers/index.ts +10 -10
  323. package/server/modules/providers/list/claude/claude-auth.provider.ts +160 -160
  324. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  325. package/server/modules/providers/list/claude/claude-models.provider.ts +294 -294
  326. package/server/modules/providers/list/claude/claude-runtime.provider.js +1068 -1068
  327. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +203 -203
  328. package/server/modules/providers/list/claude/claude-sessions.provider.ts +684 -684
  329. package/server/modules/providers/list/claude/claude-skills.provider.ts +265 -265
  330. package/server/modules/providers/list/claude/claude.provider.ts +30 -30
  331. package/server/modules/providers/list/codex/codex-app-server.client.ts +2 -2
  332. package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -100
  333. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  334. package/server/modules/providers/list/codex/codex-models.provider.ts +123 -123
  335. package/server/modules/providers/list/codex/codex-runtime.provider.js +533 -533
  336. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +203 -203
  337. package/server/modules/providers/list/codex/codex-sessions.provider.ts +938 -938
  338. package/server/modules/providers/list/codex/codex-skills.provider.ts +73 -73
  339. package/server/modules/providers/list/codex/codex.provider.ts +28 -28
  340. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  341. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  342. package/server/modules/providers/list/cursor/cursor-models.provider.ts +119 -119
  343. package/server/modules/providers/list/cursor/cursor-runtime.provider.js +385 -385
  344. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +158 -159
  345. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +654 -654
  346. package/server/modules/providers/list/cursor/cursor-skills.provider.ts +39 -39
  347. package/server/modules/providers/list/cursor/cursor.provider.ts +30 -30
  348. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +110 -110
  349. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -228
  350. package/server/modules/providers/list/opencode/opencode-models.provider.ts +203 -203
  351. package/server/modules/providers/list/opencode/opencode-runtime.provider.js +434 -434
  352. package/server/modules/providers/list/opencode/opencode-runtime.provider.test.js +261 -261
  353. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +179 -179
  354. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +500 -500
  355. package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -78
  356. package/server/modules/providers/list/opencode/opencode.provider.ts +30 -30
  357. package/server/modules/providers/provider.registry.ts +36 -36
  358. package/server/modules/providers/provider.routes.ts +877 -877
  359. package/server/modules/providers/services/mcp.service.ts +109 -109
  360. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  361. package/server/modules/providers/services/provider-capabilities.service.ts +97 -97
  362. package/server/modules/providers/services/provider-models.service.ts +401 -401
  363. package/server/modules/providers/services/provider-runtime.service.ts +108 -108
  364. package/server/modules/providers/services/provider-token-usage.service.ts +357 -357
  365. package/server/modules/providers/services/session-conversations-search.service.ts +1321 -1321
  366. package/server/modules/providers/services/session-synchronizer.service.ts +74 -74
  367. package/server/modules/providers/services/sessions-watcher.service.ts +247 -247
  368. package/server/modules/providers/services/sessions.service.ts +471 -471
  369. package/server/modules/providers/services/skills.service.ts +39 -39
  370. package/server/modules/providers/shared/base/abstract.provider.ts +32 -32
  371. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  372. package/server/modules/providers/shared/skills/skills.provider.ts +287 -287
  373. package/server/modules/providers/tests/claude-auth.test.ts +157 -150
  374. package/server/modules/providers/tests/claude-sessions.test.ts +61 -61
  375. package/server/modules/providers/tests/codex-sessions.test.ts +231 -231
  376. package/server/modules/providers/tests/mcp.test.ts +349 -349
  377. package/server/modules/providers/tests/opencode-models.test.ts +41 -41
  378. package/server/modules/providers/tests/opencode-sessions.test.ts +522 -522
  379. package/server/modules/providers/tests/provider-attachment-history.test.ts +247 -247
  380. package/server/modules/providers/tests/provider-models.service.test.ts +353 -353
  381. package/server/modules/providers/tests/provider-runtime.service.test.ts +138 -138
  382. package/server/modules/providers/tests/provider-token-usage.service.test.ts +186 -186
  383. package/server/modules/providers/tests/provider.routes.test.ts +244 -244
  384. package/server/modules/providers/tests/sessions-details.test.ts +73 -73
  385. package/server/modules/providers/tests/sessions.service.test.ts +127 -127
  386. package/server/modules/providers/tests/skills.test.ts +692 -692
  387. package/server/modules/settings/index.ts +2 -2
  388. package/server/modules/settings/settings.module.ts +50 -50
  389. package/server/modules/settings/settings.routes.ts +51 -51
  390. package/server/modules/settings/settings.service.ts +186 -186
  391. package/server/modules/settings/tests/settings.service.test.ts +54 -54
  392. package/server/modules/system/index.ts +2 -2
  393. package/server/modules/system/system.module.ts +62 -62
  394. package/server/modules/system/system.routes.ts +21 -21
  395. package/server/modules/system/system.service.ts +76 -76
  396. package/server/modules/system/tests/system.service.test.ts +118 -118
  397. package/server/modules/taskmaster/index.ts +2 -2
  398. package/server/modules/taskmaster/taskmaster.module.ts +24 -24
  399. package/server/modules/taskmaster/taskmaster.routes.ts +1483 -1482
  400. package/server/modules/taskmaster/taskmaster.service.ts +132 -132
  401. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -144
  402. package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -97
  403. package/server/modules/user/index.ts +2 -2
  404. package/server/modules/user/tests/user.service.test.ts +61 -61
  405. package/server/modules/user/user.module.ts +58 -58
  406. package/server/modules/user/user.routes.ts +50 -50
  407. package/server/modules/user/user.service.ts +85 -85
  408. package/server/modules/voice/index.ts +2 -2
  409. package/server/modules/voice/tests/voice.service.test.ts +98 -98
  410. package/server/modules/voice/voice.module.ts +47 -47
  411. package/server/modules/voice/voice.routes.ts +113 -113
  412. package/server/modules/voice/voice.service.ts +192 -192
  413. package/server/modules/websocket/README.md +273 -273
  414. package/server/modules/websocket/index.ts +3 -3
  415. package/server/modules/websocket/services/chat-run-registry.service.ts +343 -343
  416. package/server/modules/websocket/services/chat-session-writer.service.ts +145 -145
  417. package/server/modules/websocket/services/chat-websocket.service.ts +425 -425
  418. package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -65
  419. package/server/modules/websocket/services/shell-websocket.service.ts +503 -503
  420. package/server/modules/websocket/services/websocket-auth.service.ts +59 -59
  421. package/server/modules/websocket/services/websocket-server.service.ts +124 -124
  422. package/server/modules/websocket/services/websocket-state.service.ts +16 -16
  423. package/server/modules/websocket/services/websocket-writer.service.ts +38 -38
  424. package/server/modules/websocket/tests/chat-attachment-filter.test.ts +59 -59
  425. package/server/modules/websocket/tests/chat-run-registry.test.ts +282 -282
  426. package/server/modules/websocket/tests/shell-websocket.service.test.ts +100 -100
  427. package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -78
  428. package/server/modules/worktrees/index.ts +2 -2
  429. package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -53
  430. package/server/modules/worktrees/services/worktree-create.service.ts +104 -104
  431. package/server/modules/worktrees/services/worktree-git.service.ts +207 -207
  432. package/server/modules/worktrees/services/worktree-list.service.ts +143 -143
  433. package/server/modules/worktrees/services/worktree-merge.service.ts +157 -157
  434. package/server/modules/worktrees/services/worktree-open.service.ts +85 -85
  435. package/server/modules/worktrees/services/worktree-remove.service.ts +88 -88
  436. package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -68
  437. package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -142
  438. package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -84
  439. package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -45
  440. package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -213
  441. package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -122
  442. package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -227
  443. package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -199
  444. package/server/modules/worktrees/worktrees.module.ts +113 -113
  445. package/server/modules/worktrees/worktrees.routes.ts +116 -116
  446. package/server/shared/claude-cli-path.ts +139 -139
  447. package/server/shared/frontmatter.ts +18 -18
  448. package/server/shared/image-attachments.ts +435 -435
  449. package/server/shared/interfaces.ts +179 -179
  450. package/server/shared/tests/claude-cli-path.test.ts +61 -61
  451. package/server/shared/tests/image-attachments.test.ts +353 -353
  452. package/server/shared/tests/slice-tail-page.test.ts +42 -42
  453. package/server/shared/types.ts +1228 -1228
  454. package/server/shared/utils.ts +1161 -1161
  455. package/server/tsconfig.json +40 -40
  456. package/shared/networkHosts.js +22 -22
  457. package/dist/assets/FileTree-N2IkkHlv.js +0 -127
  458. package/dist/assets/GitPanel-DZCeuFZ2.js +0 -29
  459. package/dist/assets/PluginTabContent-Bnuf-fb8.js +0 -1
  460. package/dist/assets/Shell-BfFnMZ-f.js +0 -81
  461. package/dist/assets/Shell-qxJ8_QYu.css +0 -32
  462. package/dist/assets/StandaloneShell-QmyYwNAl.js +0 -1
  463. package/dist/assets/TaskMasterPanel-fMsmvN9L.js +0 -32
  464. package/dist/assets/abnfDiagram-N423BO3Z-DjPU7HLs.js +0 -1
  465. package/dist/assets/arc-Pdq6Tvr3.js +0 -1
  466. package/dist/assets/architectureDiagram-T3A2C74G-D0JnT0Vh.js +0 -36
  467. package/dist/assets/c4Diagram-5PPSVZJV-MpqlPGFi.js +0 -10
  468. package/dist/assets/channel-Bm0ZLE4z.js +0 -1
  469. package/dist/assets/chunk-5RXB4S5H-B4UQ8snD.js +0 -231
  470. package/dist/assets/chunk-GF5L2VYU-DkEPedJP.js +0 -206
  471. package/dist/assets/classDiagram-JCYQIIEL-DqEOcNxw.js +0 -1
  472. package/dist/assets/classDiagram-v2-OCEON4UE-DqEOcNxw.js +0 -1
  473. package/dist/assets/code-xml-VK1YfShP.js +0 -6
  474. package/dist/assets/cose-bilkent-JH36ORCC-DrVTo1Nh.js +0 -1
  475. package/dist/assets/cynefinDiagram-MW4NZA55-CCxBjw9E.js +0 -62
  476. package/dist/assets/cytoscape.esm-C10L9Icf.js +0 -331
  477. package/dist/assets/dagre-VZM6K2ZE-C_GvE4lu.js +0 -4
  478. package/dist/assets/diagram-LBJQPF4R-hfKcrrxe.js +0 -24
  479. package/dist/assets/diagram-Q27KOJAE-C1semJfK.js +0 -24
  480. package/dist/assets/diagram-UB23O5K3-57hvXQXO.js +0 -41
  481. package/dist/assets/ebnfDiagram-BXEA7PRR-9KIAlAVw.js +0 -1
  482. package/dist/assets/erDiagram-JOGREHBK-B7OzJ767.js +0 -85
  483. package/dist/assets/ganttDiagram-PKOTCBZU-CbMNWWQu.js +0 -292
  484. package/dist/assets/gitGraphDiagram-DS77QQ5N-I_vZ5waO.js +0 -106
  485. package/dist/assets/graph-Cx-EqisW.js +0 -1
  486. package/dist/assets/index-DFnVoqpx.js +0 -1174
  487. package/dist/assets/index-DlSeAkw_.js +0 -11
  488. package/dist/assets/index-ZHwIojzi.js +0 -1
  489. package/dist/assets/index-t1w8KNxn.css +0 -1
  490. package/dist/assets/infoDiagram-6WML65LV-Dh20Tsbq.js +0 -2
  491. package/dist/assets/ishikawaDiagram-WSZJBQD7-DH2Sm3ko.js +0 -70
  492. package/dist/assets/journeyDiagram-NVQOT4AX-BNFQqlVu.js +0 -139
  493. package/dist/assets/map-CV9uif0g.js +0 -1
  494. package/dist/assets/mermaid.core-BYRQSshi.js +0 -312
  495. package/dist/assets/pegDiagram-VL7TDLO6-CNYpECr8.js +0 -1
  496. package/dist/assets/quadrantDiagram-CIZ2JOQS-BXVVQsa6.js +0 -7
  497. package/dist/assets/railroadDiagram-AXF67PYL-JTroIW8F.js +0 -1
  498. package/dist/assets/requirementDiagram-LRYGKXZP-C1Z_inLz.js +0 -84
  499. package/dist/assets/sankeyDiagram-W5VNT64P-DCJgKFUk.js +0 -40
  500. package/dist/assets/sequenceDiagram-SI44F4Z6-D8Wrx5Vc.js +0 -162
  501. package/dist/assets/sizeCapture-X5ZJPWSS-CXqD68vD.js +0 -1
  502. package/dist/assets/stateDiagram-OKZ733FA-CF0Ekqjo.js +0 -1
  503. package/dist/assets/stateDiagram-v2-UEYNNEHI-DARLpEFJ.js +0 -1
  504. package/dist/assets/swimlanes-SLNWSIFB-DVIax_k4.js +0 -2
  505. package/dist/assets/swimlanesDiagram-ULZ7WXOC-CrgDgaY8.js +0 -8
  506. package/dist/assets/timeline-definition-Z64GVDOM-DSqk3ApG.js +0 -120
  507. package/dist/assets/vendor-react-whEHfigN.js +0 -59
  508. package/dist/assets/vennDiagram-T6HMQDX7-DNbTqyG5.js +0 -34
  509. package/dist/assets/xychartDiagram-ELKLHX3M-XsVZphHr.js +0 -7
  510. package/dist/manifest.json +0 -61
@@ -1,1482 +1,1483 @@
1
- // @ts-nocheck -- temporary while Taskmaster workflows are extracted into the injected service.
2
- /**
3
- * TASKMASTER API ROUTES
4
- * ====================
5
- *
6
- * This module provides API endpoints for TaskMaster integration including:
7
- * - .taskmaster folder detection in project directories
8
- * - MCP server configuration detection
9
- * - TaskMaster state and metadata management
10
- */
11
-
12
- import path from 'path';
13
-
14
- import express from 'express';
15
-
16
- import type { createTaskmasterService } from './taskmaster.service.js';
17
- // cross-spawn: drop-in spawn with Windows .cmd/PATHEXT resolution — required
18
- // here since task-master/npx are .cmd shims on Windows.
19
-
20
- type TaskmasterRouterDependencies = {
21
- fileSystem: typeof import('node:fs');
22
- fileSystemPromises: typeof import('node:fs/promises');
23
- spawnProcess: typeof import('cross-spawn').default;
24
- resolveProjectPathById(projectId: string): string | null;
25
- taskmasterService: ReturnType<typeof createTaskmasterService>;
26
- };
27
-
28
- function broadcastTaskMasterUpdate(wss, type, projectId, payloadKey, payload) {
29
- if (!wss || !projectId) return;
30
- const message = JSON.stringify({
31
- type,
32
- projectId,
33
- ...(payload === undefined ? {} : { [payloadKey]: payload }),
34
- timestamp: new Date().toISOString(),
35
- });
36
- wss.clients.forEach((client) => {
37
- if (client.readyState === 1) client.send(message);
38
- });
39
- }
40
-
41
- function broadcastTaskMasterProjectUpdate(wss, projectId, taskMasterData) {
42
- broadcastTaskMasterUpdate(
43
- wss,
44
- 'taskmaster-project-updated',
45
- projectId,
46
- 'taskMasterData',
47
- taskMasterData,
48
- );
49
- }
50
-
51
- function broadcastTaskMasterTasksUpdate(wss, projectId, tasksData) {
52
- broadcastTaskMasterUpdate(wss, 'taskmaster-tasks-updated', projectId, 'tasksData', tasksData);
53
- }
54
-
55
- /**
56
- * Resolve the absolute project directory from a DB-assigned `projectId`.
57
- *
58
- * TaskMaster routes used to accept a Claude-encoded folder name (`projectName`)
59
- * and derive the path from JSONL history. After the projectId migration the
60
- * only identifier we accept is the primary key of the `projects` table, so
61
- * every handler calls this helper and 404s when the id is unknown.
62
- */
63
- /** Creates Taskmaster routes around explicit filesystem, process, MCP, and project adapters. */
64
- export function createTaskmasterRouter(dependencies: TaskmasterRouterDependencies): express.Router {
65
- const fs = dependencies.fileSystem;
66
- const fsPromises = dependencies.fileSystemPromises;
67
- const spawn = dependencies.spawnProcess;
68
- const taskmasterService = dependencies.taskmasterService;
69
- const resolveProjectPathFromId = async (projectId) => projectId
70
- ? dependencies.resolveProjectPathById(projectId)
71
- : null;
72
- const router = express.Router();
73
-
74
- function runTaskmasterProcess(command, args, options, onComplete) {
75
- const child = spawn(command, args, options);
76
- let stdout = '';
77
- let stderr = '';
78
- let settled = false;
79
-
80
- child.stdout.on('data', (data) => {
81
- stdout += data.toString();
82
- });
83
- child.stderr.on('data', (data) => {
84
- stderr += data.toString();
85
- });
86
-
87
- const settle = (code, error = null) => {
88
- if (settled) return;
89
- settled = true;
90
- onComplete({ code, error, stdout, stderr });
91
- };
92
-
93
- child.once('error', (error) => settle(null, error));
94
- child.once('close', (code) => settle(code));
95
- return child;
96
- }
97
-
98
- /**
99
- * Check if TaskMaster CLI is installed globally
100
- * @returns {Promise<Object>} Installation status result
101
- */
102
- async function checkTaskMasterInstallation() {
103
- return new Promise((resolve) => {
104
- // Check if task-master command is available
105
- const child = spawn('which', ['task-master'], {
106
- stdio: ['ignore', 'pipe', 'pipe'],
107
- shell: true
108
- });
109
-
110
- let output = '';
111
- let errorOutput = '';
112
-
113
- child.stdout.on('data', (data) => {
114
- output += data.toString();
115
- });
116
-
117
- child.stderr.on('data', (data) => {
118
- errorOutput += data.toString();
119
- });
120
-
121
- child.on('close', (code) => {
122
- if (code === 0 && output.trim()) {
123
- // TaskMaster is installed, get version
124
- const versionChild = spawn('task-master', ['--version'], {
125
- stdio: ['ignore', 'pipe', 'pipe'],
126
- shell: true
127
- });
128
-
129
- let versionOutput = '';
130
-
131
- versionChild.stdout.on('data', (data) => {
132
- versionOutput += data.toString();
133
- });
134
-
135
- versionChild.on('close', (versionCode) => {
136
- resolve({
137
- isInstalled: true,
138
- installPath: output.trim(),
139
- version: versionCode === 0 ? versionOutput.trim() : 'unknown',
140
- reason: null
141
- });
142
- });
143
-
144
- versionChild.on('error', () => {
145
- resolve({
146
- isInstalled: true,
147
- installPath: output.trim(),
148
- version: 'unknown',
149
- reason: null
150
- });
151
- });
152
- } else {
153
- resolve({
154
- isInstalled: false,
155
- installPath: null,
156
- version: null,
157
- reason: 'TaskMaster CLI not found in PATH'
158
- });
159
- }
160
- });
161
-
162
- child.on('error', (error) => {
163
- resolve({
164
- isInstalled: false,
165
- installPath: null,
166
- version: null,
167
- reason: `Error checking installation: ${error.message}`
168
- });
169
- });
170
- });
171
- }
172
-
173
- // API Routes
174
-
175
- /**
176
- * GET /api/taskmaster/installation-status
177
- * Check if TaskMaster CLI is installed on the system
178
- */
179
- router.get('/installation-status', async (req, res) => {
180
- try {
181
- const installationStatus = await checkTaskMasterInstallation();
182
-
183
- // Also check for MCP server configuration
184
- const mcpStatus = await taskmasterService.detectMcpServer();
185
-
186
- res.json({
187
- success: true,
188
- installation: installationStatus,
189
- mcpServer: mcpStatus,
190
- isReady: installationStatus.isInstalled && mcpStatus.hasMCPServer
191
- });
192
- } catch (error) {
193
- console.error('Error checking TaskMaster installation:', error);
194
- res.status(500).json({
195
- success: false,
196
- error: 'Failed to check TaskMaster installation status',
197
- installation: {
198
- isInstalled: false,
199
- reason: `Server error: ${error.message}`
200
- },
201
- mcpServer: {
202
- hasMCPServer: false,
203
- reason: `Server error: ${error.message}`
204
- },
205
- isReady: false
206
- });
207
- }
208
- });
209
-
210
- /**
211
- * GET /api/taskmaster/mcp-status
212
- * Check whether TaskMaster is configured in the user's Claude MCP settings.
213
- */
214
- router.get('/mcp-status', async (_req, res) => {
215
- try {
216
- res.json(await taskmasterService.detectMcpServer());
217
- } catch (error) {
218
- const message = error instanceof Error ? error.message : 'Unknown error';
219
- console.error('TaskMaster MCP detection error:', error);
220
- res.status(500).json({
221
- error: 'Failed to detect TaskMaster MCP server',
222
- message
223
- });
224
- }
225
- });
226
-
227
- /**
228
- * GET /api/taskmaster/tasks/:projectId
229
- * Load actual tasks from .taskmaster/tasks/tasks.json
230
- *
231
- * `projectId` is the DB primary key of the project; the folder is resolved via
232
- * the projects table rather than extracted from Claude JSONL history.
233
- */
234
- router.get('/tasks/:projectId', async (req, res) => {
235
- try {
236
- const { projectId } = req.params;
237
-
238
- // Get project path via the DB; the legacy JSONL-based resolver is gone.
239
- const projectPath = await resolveProjectPathFromId(projectId);
240
- if (!projectPath) {
241
- return res.status(404).json({
242
- error: 'Project not found',
243
- message: `Project "${projectId}" does not exist`
244
- });
245
- }
246
-
247
- const taskMasterPath = path.join(projectPath, '.taskmaster');
248
- const tasksFilePath = path.join(taskMasterPath, 'tasks', 'tasks.json');
249
-
250
- // Check if tasks file exists
251
- try {
252
- await fsPromises.access(tasksFilePath);
253
- } catch (error) {
254
- return res.json({
255
- projectId,
256
- tasks: [],
257
- message: 'No tasks.json file found'
258
- });
259
- }
260
-
261
- // Read and parse tasks file
262
- try {
263
- const tasksContent = await fsPromises.readFile(tasksFilePath, 'utf8');
264
- const tasksData = JSON.parse(tasksContent);
265
-
266
- let tasks = [];
267
- let currentTag = 'master';
268
-
269
- // Handle both tagged and legacy formats
270
- if (Array.isArray(tasksData)) {
271
- // Legacy format
272
- tasks = tasksData;
273
- } else if (tasksData.tasks) {
274
- // Simple format with tasks array
275
- tasks = tasksData.tasks;
276
- } else {
277
- // Tagged format - get tasks from current tag or master
278
- if (tasksData[currentTag] && tasksData[currentTag].tasks) {
279
- tasks = tasksData[currentTag].tasks;
280
- } else if (tasksData.master && tasksData.master.tasks) {
281
- tasks = tasksData.master.tasks;
282
- } else {
283
- // Get tasks from first available tag
284
- const firstTag = Object.keys(tasksData).find(key =>
285
- tasksData[key].tasks && Array.isArray(tasksData[key].tasks)
286
- );
287
- if (firstTag) {
288
- tasks = tasksData[firstTag].tasks;
289
- currentTag = firstTag;
290
- }
291
- }
292
- }
293
-
294
- // Transform tasks to ensure all have required fields
295
- const transformedTasks = tasks.map(task => ({
296
- id: task.id,
297
- title: task.title || 'Untitled Task',
298
- description: task.description || '',
299
- status: task.status || 'pending',
300
- priority: task.priority || 'medium',
301
- dependencies: task.dependencies || [],
302
- createdAt: task.createdAt || task.created || new Date().toISOString(),
303
- updatedAt: task.updatedAt || task.updated || new Date().toISOString(),
304
- details: task.details || '',
305
- testStrategy: task.testStrategy || task.test_strategy || '',
306
- subtasks: task.subtasks || []
307
- }));
308
-
309
- res.json({
310
- projectId,
311
- projectPath,
312
- tasks: transformedTasks,
313
- currentTag,
314
- totalTasks: transformedTasks.length,
315
- tasksByStatus: {
316
- pending: transformedTasks.filter(t => t.status === 'pending').length,
317
- 'in-progress': transformedTasks.filter(t => t.status === 'in-progress').length,
318
- done: transformedTasks.filter(t => t.status === 'done').length,
319
- review: transformedTasks.filter(t => t.status === 'review').length,
320
- deferred: transformedTasks.filter(t => t.status === 'deferred').length,
321
- cancelled: transformedTasks.filter(t => t.status === 'cancelled').length
322
- },
323
- timestamp: new Date().toISOString()
324
- });
325
-
326
- } catch (parseError) {
327
- console.error('Failed to parse tasks.json:', parseError);
328
- return res.status(500).json({
329
- error: 'Failed to parse tasks file',
330
- message: parseError.message
331
- });
332
- }
333
-
334
- } catch (error) {
335
- console.error('TaskMaster tasks loading error:', error);
336
- res.status(500).json({
337
- error: 'Failed to load TaskMaster tasks',
338
- message: error.message
339
- });
340
- }
341
- });
342
-
343
- /**
344
- * GET /api/taskmaster/prd/:projectId
345
- * List all PRD files in the project's .taskmaster/docs directory
346
- */
347
- router.get('/prd/:projectId', async (req, res) => {
348
- try {
349
- const { projectId } = req.params;
350
-
351
- // projectId → projectPath lookup through the DB (post-migration).
352
- const projectPath = await resolveProjectPathFromId(projectId);
353
- if (!projectPath) {
354
- return res.status(404).json({
355
- error: 'Project not found',
356
- message: `Project "${projectId}" does not exist`
357
- });
358
- }
359
-
360
- const docsPath = path.join(projectPath, '.taskmaster', 'docs');
361
-
362
- // Check if docs directory exists
363
- try {
364
- await fsPromises.access(docsPath, fs.constants.R_OK);
365
- } catch (error) {
366
- return res.json({
367
- projectId,
368
- prdFiles: [],
369
- message: 'No .taskmaster/docs directory found'
370
- });
371
- }
372
-
373
- // Read directory and filter for PRD files
374
- try {
375
- const files = await fsPromises.readdir(docsPath);
376
- const prdFiles = [];
377
-
378
- for (const file of files) {
379
- const filePath = path.join(docsPath, file);
380
- const stats = await fsPromises.stat(filePath);
381
-
382
- if (stats.isFile() && (file.endsWith('.txt') || file.endsWith('.md'))) {
383
- prdFiles.push({
384
- name: file,
385
- path: path.relative(projectPath, filePath),
386
- size: stats.size,
387
- modified: stats.mtime.toISOString(),
388
- created: stats.birthtime.toISOString()
389
- });
390
- }
391
- }
392
-
393
- res.json({
394
- projectId,
395
- projectPath,
396
- prdFiles: prdFiles.sort((a, b) => new Date(b.modified) - new Date(a.modified)),
397
- timestamp: new Date().toISOString()
398
- });
399
-
400
- } catch (readError) {
401
- console.error('Error reading docs directory:', readError);
402
- return res.status(500).json({
403
- error: 'Failed to read PRD files',
404
- message: readError.message
405
- });
406
- }
407
-
408
- } catch (error) {
409
- console.error('PRD list error:', error);
410
- res.status(500).json({
411
- error: 'Failed to list PRD files',
412
- message: error.message
413
- });
414
- }
415
- });
416
-
417
- /**
418
- * POST /api/taskmaster/prd/:projectId
419
- * Create or update a PRD file in the project's .taskmaster/docs directory
420
- */
421
- router.post('/prd/:projectId', async (req, res) => {
422
- try {
423
- const { projectId } = req.params;
424
- const { fileName, content } = req.body;
425
-
426
- if (!fileName || !content) {
427
- return res.status(400).json({
428
- error: 'Missing required fields',
429
- message: 'fileName and content are required'
430
- });
431
- }
432
-
433
- // Validate filename
434
- if (!fileName.match(/^[\w\-. ]+\.(txt|md)$/)) {
435
- return res.status(400).json({
436
- error: 'Invalid filename',
437
- message: 'Filename must end with .txt or .md and contain only alphanumeric characters, spaces, dots, and dashes'
438
- });
439
- }
440
-
441
- // Resolve the project folder through the DB using the projectId param.
442
- const projectPath = await resolveProjectPathFromId(projectId);
443
- if (!projectPath) {
444
- return res.status(404).json({
445
- error: 'Project not found',
446
- message: `Project "${projectId}" does not exist`
447
- });
448
- }
449
-
450
- const docsPath = path.join(projectPath, '.taskmaster', 'docs');
451
- const filePath = path.join(docsPath, fileName);
452
-
453
- // Ensure docs directory exists
454
- try {
455
- await fsPromises.mkdir(docsPath, { recursive: true });
456
- } catch (error) {
457
- console.error('Failed to create docs directory:', error);
458
- return res.status(500).json({
459
- error: 'Failed to create directory',
460
- message: error.message
461
- });
462
- }
463
-
464
- // Write the PRD file
465
- try {
466
- await fsPromises.writeFile(filePath, content, 'utf8');
467
-
468
- // Get file stats
469
- const stats = await fsPromises.stat(filePath);
470
-
471
- res.json({
472
- projectId,
473
- projectPath,
474
- fileName,
475
- filePath: path.relative(projectPath, filePath),
476
- size: stats.size,
477
- created: stats.birthtime.toISOString(),
478
- modified: stats.mtime.toISOString(),
479
- message: 'PRD file saved successfully',
480
- timestamp: new Date().toISOString()
481
- });
482
-
483
- } catch (writeError) {
484
- console.error('Failed to write PRD file:', writeError);
485
- return res.status(500).json({
486
- error: 'Failed to write PRD file',
487
- message: writeError.message
488
- });
489
- }
490
-
491
- } catch (error) {
492
- console.error('PRD create/update error:', error);
493
- res.status(500).json({
494
- error: 'Failed to create/update PRD file',
495
- message: error.message
496
- });
497
- }
498
- });
499
-
500
- /**
501
- * GET /api/taskmaster/prd/:projectId/:fileName
502
- * Get content of a specific PRD file
503
- */
504
- router.get('/prd/:projectId/:fileName', async (req, res) => {
505
- try {
506
- const { projectId, fileName } = req.params;
507
-
508
- const projectPath = await resolveProjectPathFromId(projectId);
509
- if (!projectPath) {
510
- return res.status(404).json({
511
- error: 'Project not found',
512
- message: `Project "${projectId}" does not exist`
513
- });
514
- }
515
-
516
- const filePath = path.join(projectPath, '.taskmaster', 'docs', fileName);
517
-
518
- // Check if file exists
519
- try {
520
- await fsPromises.access(filePath, fs.constants.R_OK);
521
- } catch (error) {
522
- return res.status(404).json({
523
- error: 'PRD file not found',
524
- message: `File "${fileName}" does not exist`
525
- });
526
- }
527
-
528
- // Read file content
529
- try {
530
- const content = await fsPromises.readFile(filePath, 'utf8');
531
- const stats = await fsPromises.stat(filePath);
532
-
533
- res.json({
534
- projectId,
535
- projectPath,
536
- fileName,
537
- filePath: path.relative(projectPath, filePath),
538
- content,
539
- size: stats.size,
540
- created: stats.birthtime.toISOString(),
541
- modified: stats.mtime.toISOString(),
542
- timestamp: new Date().toISOString()
543
- });
544
-
545
- } catch (readError) {
546
- console.error('Failed to read PRD file:', readError);
547
- return res.status(500).json({
548
- error: 'Failed to read PRD file',
549
- message: readError.message
550
- });
551
- }
552
-
553
- } catch (error) {
554
- console.error('PRD read error:', error);
555
- res.status(500).json({
556
- error: 'Failed to read PRD file',
557
- message: error.message
558
- });
559
- }
560
- });
561
-
562
- /**
563
- * POST /api/taskmaster/init/:projectId
564
- * Initialize TaskMaster in a project
565
- */
566
- router.post('/init/:projectId', async (req, res) => {
567
- try {
568
- const { projectId } = req.params;
569
-
570
- const projectPath = await resolveProjectPathFromId(projectId);
571
- if (!projectPath) {
572
- return res.status(404).json({
573
- error: 'Project not found',
574
- message: `Project "${projectId}" does not exist`
575
- });
576
- }
577
-
578
- // Check if TaskMaster is already initialized
579
- const taskMasterPath = path.join(projectPath, '.taskmaster');
580
- try {
581
- await fsPromises.access(taskMasterPath, fs.constants.F_OK);
582
- return res.status(400).json({
583
- error: 'TaskMaster already initialized',
584
- message: 'TaskMaster is already configured for this project'
585
- });
586
- } catch (error) {
587
- // Directory doesn't exist, we can proceed
588
- }
589
-
590
- // Run taskmaster init command
591
- const initProcess = runTaskmasterProcess('npx', ['task-master', 'init'], {
592
- cwd: projectPath,
593
- stdio: ['pipe', 'pipe', 'pipe']
594
- }, ({ code, error, stdout, stderr }) => {
595
- if (code === 0) {
596
- // Broadcast TaskMaster project update via WebSocket. The
597
- // WebSocket payload keeps using `projectId` so the frontend
598
- // can match notifications against the current selection.
599
- if (req.app.locals.wss) {
600
- broadcastTaskMasterProjectUpdate(
601
- req.app.locals.wss,
602
- projectId,
603
- { hasTaskmaster: true, status: 'initialized' }
604
- );
605
- }
606
-
607
- res.json({
608
- projectId,
609
- projectPath,
610
- message: 'TaskMaster initialized successfully',
611
- output: stdout,
612
- timestamp: new Date().toISOString()
613
- });
614
- } else {
615
- console.error('TaskMaster init failed:', stderr);
616
- res.status(500).json({
617
- error: 'Failed to initialize TaskMaster',
618
- message: error?.message || stderr || stdout,
619
- code
620
- });
621
- }
622
- });
623
-
624
- // Send 'yes' responses to automated prompts
625
- initProcess.stdin.write('yes\n');
626
- initProcess.stdin.end();
627
-
628
- } catch (error) {
629
- console.error('TaskMaster init error:', error);
630
- res.status(500).json({
631
- error: 'Failed to initialize TaskMaster',
632
- message: error.message
633
- });
634
- }
635
- });
636
-
637
- /**
638
- * POST /api/taskmaster/add-task/:projectId
639
- * Add a new task to the project
640
- */
641
- router.post('/add-task/:projectId', async (req, res) => {
642
- try {
643
- const { projectId } = req.params;
644
- const { prompt, title, description, priority = 'medium', dependencies } = req.body;
645
-
646
- if (!prompt && (!title || !description)) {
647
- return res.status(400).json({
648
- error: 'Missing required parameters',
649
- message: 'Either "prompt" or both "title" and "description" are required'
650
- });
651
- }
652
-
653
- const projectPath = await resolveProjectPathFromId(projectId);
654
- if (!projectPath) {
655
- return res.status(404).json({
656
- error: 'Project not found',
657
- message: `Project "${projectId}" does not exist`
658
- });
659
- }
660
-
661
- // Build the task-master add-task command
662
- const args = ['task-master-ai', 'add-task'];
663
-
664
- if (prompt) {
665
- args.push('--prompt', prompt);
666
- args.push('--research'); // Use research for AI-generated tasks
667
- } else {
668
- args.push('--prompt', `Create a task titled "${title}" with description: ${description}`);
669
- }
670
-
671
- if (priority) {
672
- args.push('--priority', priority);
673
- }
674
-
675
- if (dependencies) {
676
- args.push('--dependencies', dependencies);
677
- }
678
-
679
- // Run task-master add-task command
680
- const addTaskProcess = runTaskmasterProcess('npx', args, {
681
- cwd: projectPath,
682
- stdio: ['pipe', 'pipe', 'pipe']
683
- }, ({ code, error, stdout, stderr }) => {
684
- console.log('Add task process completed with code:', code);
685
- console.log('Stdout:', stdout);
686
- console.log('Stderr:', stderr);
687
-
688
- if (code === 0) {
689
- // Broadcast task update via WebSocket using the projectId so
690
- // clients subscribed to this project get notified immediately.
691
- if (req.app.locals.wss) {
692
- broadcastTaskMasterTasksUpdate(
693
- req.app.locals.wss,
694
- projectId
695
- );
696
- }
697
-
698
- res.json({
699
- projectId,
700
- projectPath,
701
- message: 'Task added successfully',
702
- output: stdout,
703
- timestamp: new Date().toISOString()
704
- });
705
- } else {
706
- console.error('Add task failed:', stderr);
707
- res.status(500).json({
708
- error: 'Failed to add task',
709
- message: error?.message || stderr || stdout,
710
- code
711
- });
712
- }
713
- });
714
-
715
- addTaskProcess.stdin.end();
716
-
717
- } catch (error) {
718
- console.error('Add task error:', error);
719
- res.status(500).json({
720
- error: 'Failed to add task',
721
- message: error.message
722
- });
723
- }
724
- });
725
-
726
- /**
727
- * PUT /api/taskmaster/update-task/:projectId/:taskId
728
- * Update a specific task using TaskMaster CLI
729
- */
730
- router.put('/update-task/:projectId/:taskId', async (req, res) => {
731
- try {
732
- const { projectId, taskId } = req.params;
733
- const { title, description, status, priority, details } = req.body;
734
-
735
- const projectPath = await resolveProjectPathFromId(projectId);
736
- if (!projectPath) {
737
- return res.status(404).json({
738
- error: 'Project not found',
739
- message: `Project "${projectId}" does not exist`
740
- });
741
- }
742
-
743
- // If only updating status, use set-status command
744
- if (status && Object.keys(req.body).length === 1) {
745
- const setStatusProcess = runTaskmasterProcess('npx', ['task-master-ai', 'set-status', `--id=${taskId}`, `--status=${status}`], {
746
- cwd: projectPath,
747
- stdio: ['pipe', 'pipe', 'pipe']
748
- }, ({ code, error, stdout, stderr }) => {
749
- if (code === 0) {
750
- // Broadcast task update via WebSocket
751
- if (req.app.locals.wss) {
752
- broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectId);
753
- }
754
-
755
- res.json({
756
- projectId,
757
- projectPath,
758
- taskId,
759
- message: 'Task status updated successfully',
760
- output: stdout,
761
- timestamp: new Date().toISOString()
762
- });
763
- } else {
764
- console.error('Set task status failed:', stderr);
765
- res.status(500).json({
766
- error: 'Failed to update task status',
767
- message: error?.message || stderr || stdout,
768
- code
769
- });
770
- }
771
- });
772
-
773
- setStatusProcess.stdin.end();
774
- } else {
775
- // For other updates, use update-task command with a prompt describing the changes
776
- const updates = [];
777
- if (title) updates.push(`title: "${title}"`);
778
- if (description) updates.push(`description: "${description}"`);
779
- if (priority) updates.push(`priority: "${priority}"`);
780
- if (details) updates.push(`details: "${details}"`);
781
-
782
- const prompt = `Update task with the following changes: ${updates.join(', ')}`;
783
-
784
- const updateProcess = runTaskmasterProcess('npx', ['task-master-ai', 'update-task', `--id=${taskId}`, `--prompt=${prompt}`], {
785
- cwd: projectPath,
786
- stdio: ['pipe', 'pipe', 'pipe']
787
- }, ({ code, error, stdout, stderr }) => {
788
- if (code === 0) {
789
- // Broadcast task update via WebSocket
790
- if (req.app.locals.wss) {
791
- broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectId);
792
- }
793
-
794
- res.json({
795
- projectId,
796
- projectPath,
797
- taskId,
798
- message: 'Task updated successfully',
799
- output: stdout,
800
- timestamp: new Date().toISOString()
801
- });
802
- } else {
803
- console.error('Update task failed:', stderr);
804
- res.status(500).json({
805
- error: 'Failed to update task',
806
- message: error?.message || stderr || stdout,
807
- code
808
- });
809
- }
810
- });
811
-
812
- updateProcess.stdin.end();
813
- }
814
-
815
- } catch (error) {
816
- console.error('Update task error:', error);
817
- res.status(500).json({
818
- error: 'Failed to update task',
819
- message: error.message
820
- });
821
- }
822
- });
823
-
824
- /**
825
- * POST /api/taskmaster/parse-prd/:projectId
826
- * Parse a PRD file to generate tasks
827
- */
828
- router.post('/parse-prd/:projectId', async (req, res) => {
829
- try {
830
- const { projectId } = req.params;
831
- const { fileName = 'prd.txt', numTasks, append = false } = req.body;
832
-
833
- const projectPath = await resolveProjectPathFromId(projectId);
834
- if (!projectPath) {
835
- return res.status(404).json({
836
- error: 'Project not found',
837
- message: `Project "${projectId}" does not exist`
838
- });
839
- }
840
-
841
- const prdPath = path.join(projectPath, '.taskmaster', 'docs', fileName);
842
-
843
- // Check if PRD file exists
844
- try {
845
- await fsPromises.access(prdPath, fs.constants.F_OK);
846
- } catch (error) {
847
- return res.status(404).json({
848
- error: 'PRD file not found',
849
- message: `File "${fileName}" does not exist in .taskmaster/docs/`
850
- });
851
- }
852
-
853
- // Build the command args
854
- const args = ['task-master-ai', 'parse-prd', prdPath];
855
-
856
- if (numTasks) {
857
- args.push('--num-tasks', numTasks.toString());
858
- }
859
-
860
- if (append) {
861
- args.push('--append');
862
- }
863
-
864
- args.push('--research'); // Use research for better PRD parsing
865
-
866
- // Run task-master parse-prd command
867
- const parsePRDProcess = runTaskmasterProcess('npx', args, {
868
- cwd: projectPath,
869
- stdio: ['pipe', 'pipe', 'pipe']
870
- }, ({ code, error, stdout, stderr }) => {
871
- if (code === 0) {
872
- // Broadcast task update via WebSocket
873
- if (req.app.locals.wss) {
874
- broadcastTaskMasterTasksUpdate(
875
- req.app.locals.wss,
876
- projectId
877
- );
878
- }
879
-
880
- res.json({
881
- projectId,
882
- projectPath,
883
- prdFile: fileName,
884
- message: 'PRD parsed and tasks generated successfully',
885
- output: stdout,
886
- timestamp: new Date().toISOString()
887
- });
888
- } else {
889
- console.error('Parse PRD failed:', stderr);
890
- res.status(500).json({
891
- error: 'Failed to parse PRD',
892
- message: error?.message || stderr || stdout,
893
- code
894
- });
895
- }
896
- });
897
-
898
- parsePRDProcess.stdin.end();
899
-
900
- } catch (error) {
901
- console.error('Parse PRD error:', error);
902
- res.status(500).json({
903
- error: 'Failed to parse PRD',
904
- message: error.message
905
- });
906
- }
907
- });
908
-
909
- /**
910
- * GET /api/taskmaster/prd-templates
911
- * Get available PRD templates
912
- */
913
- router.get('/prd-templates', async (req, res) => {
914
- try {
915
- // Return built-in templates
916
- const templates = [
917
- {
918
- id: 'web-app',
919
- name: 'Web Application',
920
- description: 'Template for web application projects with frontend and backend components',
921
- category: 'web',
922
- content: `# Product Requirements Document - Web Application
923
-
924
- ## Overview
925
- **Product Name:** [Your App Name]
926
- **Version:** 1.0
927
- **Date:** ${new Date().toISOString().split('T')[0]}
928
- **Author:** [Your Name]
929
-
930
- ## Executive Summary
931
- Brief description of what this web application will do and why it's needed.
932
-
933
- ## Product Goals
934
- - Goal 1: [Specific measurable goal]
935
- - Goal 2: [Specific measurable goal]
936
- - Goal 3: [Specific measurable goal]
937
-
938
- ## User Stories
939
- ### Core Features
940
- 1. **User Registration & Authentication**
941
- - As a user, I want to create an account so I can access personalized features
942
- - As a user, I want to log in securely so my data is protected
943
- - As a user, I want to reset my password if I forget it
944
-
945
- 2. **Main Application Features**
946
- - As a user, I want to [core feature 1] so I can [benefit]
947
- - As a user, I want to [core feature 2] so I can [benefit]
948
- - As a user, I want to [core feature 3] so I can [benefit]
949
-
950
- 3. **User Interface**
951
- - As a user, I want a responsive design so I can use the app on any device
952
- - As a user, I want intuitive navigation so I can easily find features
953
-
954
- ## Technical Requirements
955
- ### Frontend
956
- - Framework: React/Vue/Angular or vanilla JavaScript
957
- - Styling: CSS framework (Tailwind, Bootstrap, etc.)
958
- - State Management: Redux/Vuex/Context API
959
- - Build Tools: Webpack/Vite
960
- - Testing: Jest/Vitest for unit tests
961
-
962
- ### Backend
963
- - Runtime: Node.js/Python/Java
964
- - Database: PostgreSQL/MySQL/MongoDB
965
- - API: RESTful API or GraphQL
966
- - Authentication: JWT tokens
967
- - Testing: Integration and unit tests
968
-
969
- ### Infrastructure
970
- - Hosting: Cloud provider (AWS, Azure, GCP)
971
- - CI/CD: GitHub Actions/GitLab CI
972
- - Monitoring: Application monitoring tools
973
- - Security: HTTPS, input validation, rate limiting
974
-
975
- ## Success Metrics
976
- - User engagement metrics
977
- - Performance benchmarks (load time < 2s)
978
- - Error rates < 1%
979
- - User satisfaction scores
980
-
981
- ## Timeline
982
- - Phase 1: Core functionality (4-6 weeks)
983
- - Phase 2: Advanced features (2-4 weeks)
984
- - Phase 3: Polish and launch (2 weeks)
985
-
986
- ## Constraints & Assumptions
987
- - Budget constraints
988
- - Technical limitations
989
- - Team size and expertise
990
- - Timeline constraints`
991
- },
992
- {
993
- id: 'api',
994
- name: 'REST API',
995
- description: 'Template for REST API development projects',
996
- category: 'backend',
997
- content: `# Product Requirements Document - REST API
998
-
999
- ## Overview
1000
- **API Name:** [Your API Name]
1001
- **Version:** v1.0
1002
- **Date:** ${new Date().toISOString().split('T')[0]}
1003
- **Author:** [Your Name]
1004
-
1005
- ## Executive Summary
1006
- Description of the API's purpose, target users, and primary use cases.
1007
-
1008
- ## API Goals
1009
- - Goal 1: Provide secure data access
1010
- - Goal 2: Ensure scalable architecture
1011
- - Goal 3: Maintain high availability (99.9% uptime)
1012
-
1013
- ## Functional Requirements
1014
- ### Core Endpoints
1015
- 1. **Authentication Endpoints**
1016
- - POST /api/auth/login - User authentication
1017
- - POST /api/auth/logout - User logout
1018
- - POST /api/auth/refresh - Token refresh
1019
- - POST /api/auth/register - User registration
1020
-
1021
- 2. **Data Management Endpoints**
1022
- - GET /api/resources - List resources with pagination
1023
- - GET /api/resources/{id} - Get specific resource
1024
- - POST /api/resources - Create new resource
1025
- - PUT /api/resources/{id} - Update existing resource
1026
- - DELETE /api/resources/{id} - Delete resource
1027
-
1028
- 3. **Administrative Endpoints**
1029
- - GET /api/admin/users - Manage users (admin only)
1030
- - GET /api/admin/analytics - System analytics
1031
- - POST /api/admin/backup - Trigger system backup
1032
-
1033
- ## Technical Requirements
1034
- ### API Design
1035
- - RESTful architecture following OpenAPI 3.0 specification
1036
- - JSON request/response format
1037
- - Consistent error response format
1038
- - API versioning strategy
1039
-
1040
- ### Authentication & Security
1041
- - JWT token-based authentication
1042
- - Role-based access control (RBAC)
1043
- - Rate limiting (100 requests/minute per user)
1044
- - Input validation and sanitization
1045
- - HTTPS enforcement
1046
-
1047
- ### Database
1048
- - Database type: [PostgreSQL/MongoDB/MySQL]
1049
- - Connection pooling
1050
- - Database migrations
1051
- - Backup and recovery procedures
1052
-
1053
- ### Performance Requirements
1054
- - Response time: < 200ms for 95% of requests
1055
- - Throughput: 1000+ requests/second
1056
- - Concurrent users: 10,000+
1057
- - Database query optimization
1058
-
1059
- ### Documentation
1060
- - Auto-generated API documentation (Swagger/OpenAPI)
1061
- - Code examples for common use cases
1062
- - SDK development for major languages
1063
- - Postman collection for testing
1064
-
1065
- ## Error Handling
1066
- - Standardized error codes and messages
1067
- - Proper HTTP status codes
1068
- - Detailed error logging
1069
- - Graceful degradation strategies
1070
-
1071
- ## Testing Strategy
1072
- - Unit tests (80%+ coverage)
1073
- - Integration tests for all endpoints
1074
- - Load testing and performance testing
1075
- - Security testing (OWASP compliance)
1076
-
1077
- ## Monitoring & Logging
1078
- - Application performance monitoring
1079
- - Error tracking and alerting
1080
- - Access logs and audit trails
1081
- - Health check endpoints
1082
-
1083
- ## Deployment
1084
- - Containerized deployment (Docker)
1085
- - CI/CD pipeline setup
1086
- - Environment management (dev, staging, prod)
1087
- - Blue-green deployment strategy
1088
-
1089
- ## Success Metrics
1090
- - API uptime > 99.9%
1091
- - Average response time < 200ms
1092
- - Zero critical security vulnerabilities
1093
- - Developer adoption metrics`
1094
- },
1095
- {
1096
- id: 'mobile-app',
1097
- name: 'Mobile Application',
1098
- description: 'Template for mobile app development projects (iOS/Android)',
1099
- category: 'mobile',
1100
- content: `# Product Requirements Document - Mobile Application
1101
-
1102
- ## Overview
1103
- **App Name:** [Your App Name]
1104
- **Platform:** iOS / Android / Cross-platform
1105
- **Version:** 1.0
1106
- **Date:** ${new Date().toISOString().split('T')[0]}
1107
- **Author:** [Your Name]
1108
-
1109
- ## Executive Summary
1110
- Brief description of the mobile app's purpose, target audience, and key value proposition.
1111
-
1112
- ## Product Goals
1113
- - Goal 1: [Specific user engagement goal]
1114
- - Goal 2: [Specific functionality goal]
1115
- - Goal 3: [Specific performance goal]
1116
-
1117
- ## User Stories
1118
- ### Core Features
1119
- 1. **Onboarding & Authentication**
1120
- - As a new user, I want a simple onboarding process
1121
- - As a user, I want to sign up with email or social media
1122
- - As a user, I want biometric authentication for security
1123
-
1124
- 2. **Main App Features**
1125
- - As a user, I want [core feature 1] accessible from home screen
1126
- - As a user, I want [core feature 2] to work offline
1127
- - As a user, I want to sync data across devices
1128
-
1129
- 3. **User Experience**
1130
- - As a user, I want intuitive navigation patterns
1131
- - As a user, I want fast loading times
1132
- - As a user, I want accessibility features
1133
-
1134
- ## Technical Requirements
1135
- ### Mobile Development
1136
- - **Cross-platform:** React Native / Flutter / Xamarin
1137
- - **Native:** Swift (iOS) / Kotlin (Android)
1138
- - **State Management:** Redux / MobX / Provider
1139
- - **Navigation:** React Navigation / Flutter Navigation
1140
-
1141
- ### Backend Integration
1142
- - REST API or GraphQL integration
1143
- - Real-time features (WebSockets/Push notifications)
1144
- - Offline data synchronization
1145
- - Background processing
1146
-
1147
- ### Device Features
1148
- - Camera and photo library access
1149
- - GPS location services
1150
- - Push notifications
1151
- - Biometric authentication
1152
- - Device storage
1153
-
1154
- ### Performance Requirements
1155
- - App launch time < 3 seconds
1156
- - Screen transition animations < 300ms
1157
- - Memory usage optimization
1158
- - Battery usage optimization
1159
-
1160
- ## Platform-Specific Considerations
1161
- ### iOS Requirements
1162
- - iOS 13.0+ minimum version
1163
- - App Store guidelines compliance
1164
- - iOS design guidelines (Human Interface Guidelines)
1165
- - TestFlight beta testing
1166
-
1167
- ### Android Requirements
1168
- - Android 8.0+ (API level 26) minimum
1169
- - Google Play Store guidelines
1170
- - Material Design guidelines
1171
- - Google Play Console testing
1172
-
1173
- ## User Interface Design
1174
- - Responsive design for different screen sizes
1175
- - Dark mode support
1176
- - Accessibility compliance (WCAG 2.1)
1177
- - Consistent design system
1178
-
1179
- ## Security & Privacy
1180
- - Secure data storage (Keychain/Keystore)
1181
- - API communication encryption
1182
- - Privacy policy compliance (GDPR/CCPA)
1183
- - App security best practices
1184
-
1185
- ## Testing Strategy
1186
- - Unit testing (80%+ coverage)
1187
- - UI/E2E testing (Detox/Appium)
1188
- - Device testing on multiple screen sizes
1189
- - Performance testing
1190
- - Security testing
1191
-
1192
- ## App Store Deployment
1193
- - App store optimization (ASO)
1194
- - App icons and screenshots
1195
- - Store listing content
1196
- - Release management strategy
1197
-
1198
- ## Analytics & Monitoring
1199
- - User analytics (Firebase/Analytics)
1200
- - Crash reporting (Crashlytics/Sentry)
1201
- - Performance monitoring
1202
- - User feedback collection
1203
-
1204
- ## Success Metrics
1205
- - App store ratings > 4.0
1206
- - User retention rates
1207
- - Daily/Monthly active users
1208
- - App performance metrics
1209
- - Conversion rates`
1210
- },
1211
- {
1212
- id: 'data-analysis',
1213
- name: 'Data Analysis Project',
1214
- description: 'Template for data analysis and visualization projects',
1215
- category: 'data',
1216
- content: `# Product Requirements Document - Data Analysis Project
1217
-
1218
- ## Overview
1219
- **Project Name:** [Your Analysis Project]
1220
- **Analysis Type:** [Descriptive/Predictive/Prescriptive]
1221
- **Date:** ${new Date().toISOString().split('T')[0]}
1222
- **Author:** [Your Name]
1223
-
1224
- ## Executive Summary
1225
- Description of the business problem, data sources, and expected insights.
1226
-
1227
- ## Project Goals
1228
- - Goal 1: [Specific business question to answer]
1229
- - Goal 2: [Specific prediction to make]
1230
- - Goal 3: [Specific recommendation to provide]
1231
-
1232
- ## Business Requirements
1233
- ### Key Questions
1234
- 1. What patterns exist in the current data?
1235
- 2. What factors influence [target variable]?
1236
- 3. What predictions can be made for [future outcome]?
1237
- 4. What recommendations can improve [business metric]?
1238
-
1239
- ### Success Criteria
1240
- - Actionable insights for stakeholders
1241
- - Statistical significance in findings
1242
- - Reproducible analysis pipeline
1243
- - Clear visualization and reporting
1244
-
1245
- ## Data Requirements
1246
- ### Data Sources
1247
- 1. **Primary Data**
1248
- - Source: [Database/API/Files]
1249
- - Format: [CSV/JSON/SQL]
1250
- - Size: [Volume estimate]
1251
- - Update frequency: [Real-time/Daily/Monthly]
1252
-
1253
- 2. **External Data**
1254
- - Third-party APIs
1255
- - Public datasets
1256
- - Market research data
1257
-
1258
- ### Data Quality Requirements
1259
- - Data completeness (< 5% missing values)
1260
- - Data accuracy validation
1261
- - Data consistency checks
1262
- - Historical data availability
1263
-
1264
- ## Technical Requirements
1265
- ### Data Pipeline
1266
- - Data extraction and ingestion
1267
- - Data cleaning and preprocessing
1268
- - Data transformation and feature engineering
1269
- - Data validation and quality checks
1270
-
1271
- ### Analysis Tools
1272
- - **Programming:** Python/R/SQL
1273
- - **Libraries:** pandas, numpy, scikit-learn, matplotlib
1274
- - **Visualization:** Tableau, PowerBI, or custom dashboards
1275
- - **Version Control:** Git for code and DVC for data
1276
-
1277
- ### Computing Resources
1278
- - Local development environment
1279
- - Cloud computing (AWS/GCP/Azure) if needed
1280
- - Database access and permissions
1281
- - Storage requirements
1282
-
1283
- ## Analysis Methodology
1284
- ### Data Exploration
1285
- 1. Descriptive statistics and data profiling
1286
- 2. Data visualization and pattern identification
1287
- 3. Correlation analysis
1288
- 4. Outlier detection and handling
1289
-
1290
- ### Statistical Analysis
1291
- 1. Hypothesis formulation
1292
- 2. Statistical testing
1293
- 3. Confidence intervals
1294
- 4. Effect size calculations
1295
-
1296
- ### Machine Learning (if applicable)
1297
- 1. Feature selection and engineering
1298
- 2. Model selection and training
1299
- 3. Cross-validation and evaluation
1300
- 4. Model interpretation and explainability
1301
-
1302
- ## Deliverables
1303
- ### Reports
1304
- - Executive summary for stakeholders
1305
- - Technical analysis report
1306
- - Data quality report
1307
- - Methodology documentation
1308
-
1309
- ### Visualizations
1310
- - Interactive dashboards
1311
- - Static charts and graphs
1312
- - Data story presentations
1313
- - Key findings infographics
1314
-
1315
- ### Code & Documentation
1316
- - Reproducible analysis scripts
1317
- - Data pipeline code
1318
- - Documentation and comments
1319
- - Testing and validation code
1320
-
1321
- ## Timeline
1322
- - Phase 1: Data collection and exploration (2 weeks)
1323
- - Phase 2: Analysis and modeling (3 weeks)
1324
- - Phase 3: Reporting and visualization (1 week)
1325
- - Phase 4: Stakeholder presentation (1 week)
1326
-
1327
- ## Risks & Assumptions
1328
- - Data availability and quality risks
1329
- - Technical complexity assumptions
1330
- - Resource and timeline constraints
1331
- - Stakeholder engagement assumptions
1332
-
1333
- ## Success Metrics
1334
- - Stakeholder satisfaction with insights
1335
- - Accuracy of predictions (if applicable)
1336
- - Business impact of recommendations
1337
- - Reproducibility of results`
1338
- }
1339
- ];
1340
-
1341
- res.json({
1342
- templates,
1343
- timestamp: new Date().toISOString()
1344
- });
1345
-
1346
- } catch (error) {
1347
- console.error('PRD templates error:', error);
1348
- res.status(500).json({
1349
- error: 'Failed to get PRD templates',
1350
- message: error.message
1351
- });
1352
- }
1353
- });
1354
-
1355
- /**
1356
- * POST /api/taskmaster/apply-template/:projectId
1357
- * Apply a PRD template to create a new PRD file
1358
- */
1359
- router.post('/apply-template/:projectId', async (req, res) => {
1360
- try {
1361
- const { projectId } = req.params;
1362
- const { templateId, fileName = 'prd.txt', customizations = {} } = req.body;
1363
-
1364
- if (!templateId) {
1365
- return res.status(400).json({
1366
- error: 'Missing required parameter',
1367
- message: 'templateId is required'
1368
- });
1369
- }
1370
-
1371
- const projectPath = await resolveProjectPathFromId(projectId);
1372
- if (!projectPath) {
1373
- return res.status(404).json({
1374
- error: 'Project not found',
1375
- message: `Project "${projectId}" does not exist`
1376
- });
1377
- }
1378
-
1379
- // Get the template content (this would normally fetch from the templates list)
1380
- const templates = await getAvailableTemplates();
1381
- const template = templates.find(t => t.id === templateId);
1382
-
1383
- if (!template) {
1384
- return res.status(404).json({
1385
- error: 'Template not found',
1386
- message: `Template "${templateId}" does not exist`
1387
- });
1388
- }
1389
-
1390
- // Apply customizations to template content
1391
- let content = template.content;
1392
-
1393
- // Replace placeholders with customizations
1394
- for (const [key, value] of Object.entries(customizations)) {
1395
- const placeholder = `[${key}]`;
1396
- content = content.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g'), value);
1397
- }
1398
-
1399
- // Ensure .taskmaster/docs directory exists
1400
- const docsDir = path.join(projectPath, '.taskmaster', 'docs');
1401
- try {
1402
- await fsPromises.mkdir(docsDir, { recursive: true });
1403
- } catch (error) {
1404
- console.error('Failed to create docs directory:', error);
1405
- }
1406
-
1407
- const filePath = path.join(docsDir, fileName);
1408
-
1409
- // Write the template content to the file
1410
- try {
1411
- await fsPromises.writeFile(filePath, content, 'utf8');
1412
-
1413
- res.json({
1414
- projectId,
1415
- projectPath,
1416
- templateId,
1417
- templateName: template.name,
1418
- fileName,
1419
- filePath: filePath,
1420
- message: 'PRD template applied successfully',
1421
- timestamp: new Date().toISOString()
1422
- });
1423
-
1424
- } catch (writeError) {
1425
- console.error('Failed to write PRD template:', writeError);
1426
- return res.status(500).json({
1427
- error: 'Failed to write PRD template',
1428
- message: writeError.message
1429
- });
1430
- }
1431
-
1432
- } catch (error) {
1433
- console.error('Apply template error:', error);
1434
- res.status(500).json({
1435
- error: 'Failed to apply PRD template',
1436
- message: error.message
1437
- });
1438
- }
1439
- });
1440
-
1441
- // Helper function to get available templates
1442
- async function getAvailableTemplates() {
1443
- // This could be extended to read from files or database
1444
- return [
1445
- {
1446
- id: 'web-app',
1447
- name: 'Web Application',
1448
- description: 'Template for web application projects',
1449
- category: 'web',
1450
- content: `# Product Requirements Document - Web Application
1451
-
1452
- ## Overview
1453
- **Product Name:** [Your App Name]
1454
- **Version:** 1.0
1455
- **Date:** ${new Date().toISOString().split('T')[0]}
1456
- **Author:** [Your Name]
1457
-
1458
- ## Executive Summary
1459
- Brief description of what this web application will do and why it's needed.
1460
-
1461
- ## User Stories
1462
- 1. As a user, I want [feature] so I can [benefit]
1463
- 2. As a user, I want [feature] so I can [benefit]
1464
- 3. As a user, I want [feature] so I can [benefit]
1465
-
1466
- ## Technical Requirements
1467
- - Frontend framework
1468
- - Backend services
1469
- - Database requirements
1470
- - Security considerations
1471
-
1472
- ## Success Metrics
1473
- - User engagement metrics
1474
- - Performance benchmarks
1475
- - Business objectives`
1476
- },
1477
- // Add other templates here if needed
1478
- ];
1479
- }
1480
-
1481
- return router;
1482
- }
1
+ // @ts-nocheck -- temporary while Taskmaster workflows are extracted into the injected service.
2
+ /**
3
+ * TASKMASTER API ROUTES
4
+ * ====================
5
+ *
6
+ * This module provides API endpoints for TaskMaster integration including:
7
+ * - .taskmaster folder detection in project directories
8
+ * - MCP server configuration detection
9
+ * - TaskMaster state and metadata management
10
+ */
11
+
12
+ import path from 'path';
13
+
14
+ import express from 'express';
15
+
16
+ import type { createTaskmasterService } from './taskmaster.service.js';
17
+ // cross-spawn: drop-in spawn with Windows .cmd/PATHEXT resolution — required
18
+ // here since task-master/npx are .cmd shims on Windows.
19
+
20
+ type TaskmasterRouterDependencies = {
21
+ fileSystem: typeof import('node:fs');
22
+ fileSystemPromises: typeof import('node:fs/promises');
23
+ spawnProcess: typeof import('cross-spawn').default;
24
+ resolveProjectPathById(projectId: string): string | null;
25
+ taskmasterService: ReturnType<typeof createTaskmasterService>;
26
+ };
27
+
28
+ function broadcastTaskMasterUpdate(wss, type, projectId, payloadKey, payload) {
29
+ if (!wss || !projectId) return;
30
+ const message = JSON.stringify({
31
+ type,
32
+ projectId,
33
+ ...(payload === undefined ? {} : { [payloadKey]: payload }),
34
+ timestamp: new Date().toISOString(),
35
+ });
36
+ wss.clients.forEach((client) => {
37
+ if (client.readyState === 1) client.send(message);
38
+ });
39
+ }
40
+
41
+ function broadcastTaskMasterProjectUpdate(wss, projectId, taskMasterData) {
42
+ broadcastTaskMasterUpdate(
43
+ wss,
44
+ 'taskmaster-project-updated',
45
+ projectId,
46
+ 'taskMasterData',
47
+ taskMasterData,
48
+ );
49
+ }
50
+
51
+ function broadcastTaskMasterTasksUpdate(wss, projectId, tasksData) {
52
+ broadcastTaskMasterUpdate(wss, 'taskmaster-tasks-updated', projectId, 'tasksData', tasksData);
53
+ }
54
+
55
+ /**
56
+ * Resolve the absolute project directory from a DB-assigned `projectId`.
57
+ *
58
+ * TaskMaster routes used to accept a Claude-encoded folder name (`projectName`)
59
+ * and derive the path from JSONL history. After the projectId migration the
60
+ * only identifier we accept is the primary key of the `projects` table, so
61
+ * every handler calls this helper and 404s when the id is unknown.
62
+ */
63
+ /** Creates Taskmaster routes around explicit filesystem, process, MCP, and project adapters. */
64
+ export function createTaskmasterRouter(dependencies: TaskmasterRouterDependencies): express.Router {
65
+ const fs = dependencies.fileSystem;
66
+ const fsPromises = dependencies.fileSystemPromises;
67
+ const spawn = dependencies.spawnProcess;
68
+ const taskmasterService = dependencies.taskmasterService;
69
+ const resolveProjectPathFromId = async (projectId) => projectId
70
+ ? dependencies.resolveProjectPathById(projectId)
71
+ : null;
72
+ const router = express.Router();
73
+
74
+ function runTaskmasterProcess(command, args, options, onComplete) {
75
+ const child = spawn(command, args, options);
76
+ let stdout = '';
77
+ let stderr = '';
78
+ let settled = false;
79
+
80
+ child.stdout.on('data', (data) => {
81
+ stdout += data.toString();
82
+ });
83
+ child.stderr.on('data', (data) => {
84
+ stderr += data.toString();
85
+ });
86
+
87
+ const settle = (code, error = null) => {
88
+ if (settled) return;
89
+ settled = true;
90
+ onComplete({ code, error, stdout, stderr });
91
+ };
92
+
93
+ child.once('error', (error) => settle(null, error));
94
+ child.once('close', (code) => settle(code));
95
+ return child;
96
+ }
97
+
98
+ /**
99
+ * Check if TaskMaster CLI is installed globally
100
+ * @returns {Promise<Object>} Installation status result
101
+ */
102
+ async function checkTaskMasterInstallation() {
103
+ return new Promise((resolve) => {
104
+ // Check if task-master command is available
105
+ const locator = process.platform === 'win32' ? 'where.exe' : 'which';
106
+ const child = spawn(locator, ['task-master'], {
107
+ stdio: ['ignore', 'pipe', 'pipe'],
108
+ shell: false
109
+ });
110
+
111
+ let output = '';
112
+ let errorOutput = '';
113
+
114
+ child.stdout.on('data', (data) => {
115
+ output += data.toString();
116
+ });
117
+
118
+ child.stderr.on('data', (data) => {
119
+ errorOutput += data.toString();
120
+ });
121
+
122
+ child.on('close', (code) => {
123
+ if (code === 0 && output.trim()) {
124
+ // TaskMaster is installed, get version
125
+ const versionChild = spawn('task-master', ['--version'], {
126
+ stdio: ['ignore', 'pipe', 'pipe'],
127
+ shell: false
128
+ });
129
+
130
+ let versionOutput = '';
131
+
132
+ versionChild.stdout.on('data', (data) => {
133
+ versionOutput += data.toString();
134
+ });
135
+
136
+ versionChild.on('close', (versionCode) => {
137
+ resolve({
138
+ isInstalled: true,
139
+ installPath: output.trim(),
140
+ version: versionCode === 0 ? versionOutput.trim() : 'unknown',
141
+ reason: null
142
+ });
143
+ });
144
+
145
+ versionChild.on('error', () => {
146
+ resolve({
147
+ isInstalled: true,
148
+ installPath: output.trim(),
149
+ version: 'unknown',
150
+ reason: null
151
+ });
152
+ });
153
+ } else {
154
+ resolve({
155
+ isInstalled: false,
156
+ installPath: null,
157
+ version: null,
158
+ reason: 'TaskMaster CLI not found in PATH'
159
+ });
160
+ }
161
+ });
162
+
163
+ child.on('error', (error) => {
164
+ resolve({
165
+ isInstalled: false,
166
+ installPath: null,
167
+ version: null,
168
+ reason: `Error checking installation: ${error.message}`
169
+ });
170
+ });
171
+ });
172
+ }
173
+
174
+ // API Routes
175
+
176
+ /**
177
+ * GET /api/taskmaster/installation-status
178
+ * Check if TaskMaster CLI is installed on the system
179
+ */
180
+ router.get('/installation-status', async (req, res) => {
181
+ try {
182
+ const installationStatus = await checkTaskMasterInstallation();
183
+
184
+ // Also check for MCP server configuration
185
+ const mcpStatus = await taskmasterService.detectMcpServer();
186
+
187
+ res.json({
188
+ success: true,
189
+ installation: installationStatus,
190
+ mcpServer: mcpStatus,
191
+ isReady: installationStatus.isInstalled && mcpStatus.hasMCPServer
192
+ });
193
+ } catch (error) {
194
+ console.error('Error checking TaskMaster installation:', error);
195
+ res.status(500).json({
196
+ success: false,
197
+ error: 'Failed to check TaskMaster installation status',
198
+ installation: {
199
+ isInstalled: false,
200
+ reason: `Server error: ${error.message}`
201
+ },
202
+ mcpServer: {
203
+ hasMCPServer: false,
204
+ reason: `Server error: ${error.message}`
205
+ },
206
+ isReady: false
207
+ });
208
+ }
209
+ });
210
+
211
+ /**
212
+ * GET /api/taskmaster/mcp-status
213
+ * Check whether TaskMaster is configured in the user's Claude MCP settings.
214
+ */
215
+ router.get('/mcp-status', async (_req, res) => {
216
+ try {
217
+ res.json(await taskmasterService.detectMcpServer());
218
+ } catch (error) {
219
+ const message = error instanceof Error ? error.message : 'Unknown error';
220
+ console.error('TaskMaster MCP detection error:', error);
221
+ res.status(500).json({
222
+ error: 'Failed to detect TaskMaster MCP server',
223
+ message
224
+ });
225
+ }
226
+ });
227
+
228
+ /**
229
+ * GET /api/taskmaster/tasks/:projectId
230
+ * Load actual tasks from .taskmaster/tasks/tasks.json
231
+ *
232
+ * `projectId` is the DB primary key of the project; the folder is resolved via
233
+ * the projects table rather than extracted from Claude JSONL history.
234
+ */
235
+ router.get('/tasks/:projectId', async (req, res) => {
236
+ try {
237
+ const { projectId } = req.params;
238
+
239
+ // Get project path via the DB; the legacy JSONL-based resolver is gone.
240
+ const projectPath = await resolveProjectPathFromId(projectId);
241
+ if (!projectPath) {
242
+ return res.status(404).json({
243
+ error: 'Project not found',
244
+ message: `Project "${projectId}" does not exist`
245
+ });
246
+ }
247
+
248
+ const taskMasterPath = path.join(projectPath, '.taskmaster');
249
+ const tasksFilePath = path.join(taskMasterPath, 'tasks', 'tasks.json');
250
+
251
+ // Check if tasks file exists
252
+ try {
253
+ await fsPromises.access(tasksFilePath);
254
+ } catch (error) {
255
+ return res.json({
256
+ projectId,
257
+ tasks: [],
258
+ message: 'No tasks.json file found'
259
+ });
260
+ }
261
+
262
+ // Read and parse tasks file
263
+ try {
264
+ const tasksContent = await fsPromises.readFile(tasksFilePath, 'utf8');
265
+ const tasksData = JSON.parse(tasksContent);
266
+
267
+ let tasks = [];
268
+ let currentTag = 'master';
269
+
270
+ // Handle both tagged and legacy formats
271
+ if (Array.isArray(tasksData)) {
272
+ // Legacy format
273
+ tasks = tasksData;
274
+ } else if (tasksData.tasks) {
275
+ // Simple format with tasks array
276
+ tasks = tasksData.tasks;
277
+ } else {
278
+ // Tagged format - get tasks from current tag or master
279
+ if (tasksData[currentTag] && tasksData[currentTag].tasks) {
280
+ tasks = tasksData[currentTag].tasks;
281
+ } else if (tasksData.master && tasksData.master.tasks) {
282
+ tasks = tasksData.master.tasks;
283
+ } else {
284
+ // Get tasks from first available tag
285
+ const firstTag = Object.keys(tasksData).find(key =>
286
+ tasksData[key].tasks && Array.isArray(tasksData[key].tasks)
287
+ );
288
+ if (firstTag) {
289
+ tasks = tasksData[firstTag].tasks;
290
+ currentTag = firstTag;
291
+ }
292
+ }
293
+ }
294
+
295
+ // Transform tasks to ensure all have required fields
296
+ const transformedTasks = tasks.map(task => ({
297
+ id: task.id,
298
+ title: task.title || 'Untitled Task',
299
+ description: task.description || '',
300
+ status: task.status || 'pending',
301
+ priority: task.priority || 'medium',
302
+ dependencies: task.dependencies || [],
303
+ createdAt: task.createdAt || task.created || new Date().toISOString(),
304
+ updatedAt: task.updatedAt || task.updated || new Date().toISOString(),
305
+ details: task.details || '',
306
+ testStrategy: task.testStrategy || task.test_strategy || '',
307
+ subtasks: task.subtasks || []
308
+ }));
309
+
310
+ res.json({
311
+ projectId,
312
+ projectPath,
313
+ tasks: transformedTasks,
314
+ currentTag,
315
+ totalTasks: transformedTasks.length,
316
+ tasksByStatus: {
317
+ pending: transformedTasks.filter(t => t.status === 'pending').length,
318
+ 'in-progress': transformedTasks.filter(t => t.status === 'in-progress').length,
319
+ done: transformedTasks.filter(t => t.status === 'done').length,
320
+ review: transformedTasks.filter(t => t.status === 'review').length,
321
+ deferred: transformedTasks.filter(t => t.status === 'deferred').length,
322
+ cancelled: transformedTasks.filter(t => t.status === 'cancelled').length
323
+ },
324
+ timestamp: new Date().toISOString()
325
+ });
326
+
327
+ } catch (parseError) {
328
+ console.error('Failed to parse tasks.json:', parseError);
329
+ return res.status(500).json({
330
+ error: 'Failed to parse tasks file',
331
+ message: parseError.message
332
+ });
333
+ }
334
+
335
+ } catch (error) {
336
+ console.error('TaskMaster tasks loading error:', error);
337
+ res.status(500).json({
338
+ error: 'Failed to load TaskMaster tasks',
339
+ message: error.message
340
+ });
341
+ }
342
+ });
343
+
344
+ /**
345
+ * GET /api/taskmaster/prd/:projectId
346
+ * List all PRD files in the project's .taskmaster/docs directory
347
+ */
348
+ router.get('/prd/:projectId', async (req, res) => {
349
+ try {
350
+ const { projectId } = req.params;
351
+
352
+ // projectId → projectPath lookup through the DB (post-migration).
353
+ const projectPath = await resolveProjectPathFromId(projectId);
354
+ if (!projectPath) {
355
+ return res.status(404).json({
356
+ error: 'Project not found',
357
+ message: `Project "${projectId}" does not exist`
358
+ });
359
+ }
360
+
361
+ const docsPath = path.join(projectPath, '.taskmaster', 'docs');
362
+
363
+ // Check if docs directory exists
364
+ try {
365
+ await fsPromises.access(docsPath, fs.constants.R_OK);
366
+ } catch (error) {
367
+ return res.json({
368
+ projectId,
369
+ prdFiles: [],
370
+ message: 'No .taskmaster/docs directory found'
371
+ });
372
+ }
373
+
374
+ // Read directory and filter for PRD files
375
+ try {
376
+ const files = await fsPromises.readdir(docsPath);
377
+ const prdFiles = [];
378
+
379
+ for (const file of files) {
380
+ const filePath = path.join(docsPath, file);
381
+ const stats = await fsPromises.stat(filePath);
382
+
383
+ if (stats.isFile() && (file.endsWith('.txt') || file.endsWith('.md'))) {
384
+ prdFiles.push({
385
+ name: file,
386
+ path: path.relative(projectPath, filePath),
387
+ size: stats.size,
388
+ modified: stats.mtime.toISOString(),
389
+ created: stats.birthtime.toISOString()
390
+ });
391
+ }
392
+ }
393
+
394
+ res.json({
395
+ projectId,
396
+ projectPath,
397
+ prdFiles: prdFiles.sort((a, b) => new Date(b.modified) - new Date(a.modified)),
398
+ timestamp: new Date().toISOString()
399
+ });
400
+
401
+ } catch (readError) {
402
+ console.error('Error reading docs directory:', readError);
403
+ return res.status(500).json({
404
+ error: 'Failed to read PRD files',
405
+ message: readError.message
406
+ });
407
+ }
408
+
409
+ } catch (error) {
410
+ console.error('PRD list error:', error);
411
+ res.status(500).json({
412
+ error: 'Failed to list PRD files',
413
+ message: error.message
414
+ });
415
+ }
416
+ });
417
+
418
+ /**
419
+ * POST /api/taskmaster/prd/:projectId
420
+ * Create or update a PRD file in the project's .taskmaster/docs directory
421
+ */
422
+ router.post('/prd/:projectId', async (req, res) => {
423
+ try {
424
+ const { projectId } = req.params;
425
+ const { fileName, content } = req.body;
426
+
427
+ if (!fileName || !content) {
428
+ return res.status(400).json({
429
+ error: 'Missing required fields',
430
+ message: 'fileName and content are required'
431
+ });
432
+ }
433
+
434
+ // Validate filename
435
+ if (!fileName.match(/^[\w\-. ]+\.(txt|md)$/)) {
436
+ return res.status(400).json({
437
+ error: 'Invalid filename',
438
+ message: 'Filename must end with .txt or .md and contain only alphanumeric characters, spaces, dots, and dashes'
439
+ });
440
+ }
441
+
442
+ // Resolve the project folder through the DB using the projectId param.
443
+ const projectPath = await resolveProjectPathFromId(projectId);
444
+ if (!projectPath) {
445
+ return res.status(404).json({
446
+ error: 'Project not found',
447
+ message: `Project "${projectId}" does not exist`
448
+ });
449
+ }
450
+
451
+ const docsPath = path.join(projectPath, '.taskmaster', 'docs');
452
+ const filePath = path.join(docsPath, fileName);
453
+
454
+ // Ensure docs directory exists
455
+ try {
456
+ await fsPromises.mkdir(docsPath, { recursive: true });
457
+ } catch (error) {
458
+ console.error('Failed to create docs directory:', error);
459
+ return res.status(500).json({
460
+ error: 'Failed to create directory',
461
+ message: error.message
462
+ });
463
+ }
464
+
465
+ // Write the PRD file
466
+ try {
467
+ await fsPromises.writeFile(filePath, content, 'utf8');
468
+
469
+ // Get file stats
470
+ const stats = await fsPromises.stat(filePath);
471
+
472
+ res.json({
473
+ projectId,
474
+ projectPath,
475
+ fileName,
476
+ filePath: path.relative(projectPath, filePath),
477
+ size: stats.size,
478
+ created: stats.birthtime.toISOString(),
479
+ modified: stats.mtime.toISOString(),
480
+ message: 'PRD file saved successfully',
481
+ timestamp: new Date().toISOString()
482
+ });
483
+
484
+ } catch (writeError) {
485
+ console.error('Failed to write PRD file:', writeError);
486
+ return res.status(500).json({
487
+ error: 'Failed to write PRD file',
488
+ message: writeError.message
489
+ });
490
+ }
491
+
492
+ } catch (error) {
493
+ console.error('PRD create/update error:', error);
494
+ res.status(500).json({
495
+ error: 'Failed to create/update PRD file',
496
+ message: error.message
497
+ });
498
+ }
499
+ });
500
+
501
+ /**
502
+ * GET /api/taskmaster/prd/:projectId/:fileName
503
+ * Get content of a specific PRD file
504
+ */
505
+ router.get('/prd/:projectId/:fileName', async (req, res) => {
506
+ try {
507
+ const { projectId, fileName } = req.params;
508
+
509
+ const projectPath = await resolveProjectPathFromId(projectId);
510
+ if (!projectPath) {
511
+ return res.status(404).json({
512
+ error: 'Project not found',
513
+ message: `Project "${projectId}" does not exist`
514
+ });
515
+ }
516
+
517
+ const filePath = path.join(projectPath, '.taskmaster', 'docs', fileName);
518
+
519
+ // Check if file exists
520
+ try {
521
+ await fsPromises.access(filePath, fs.constants.R_OK);
522
+ } catch (error) {
523
+ return res.status(404).json({
524
+ error: 'PRD file not found',
525
+ message: `File "${fileName}" does not exist`
526
+ });
527
+ }
528
+
529
+ // Read file content
530
+ try {
531
+ const content = await fsPromises.readFile(filePath, 'utf8');
532
+ const stats = await fsPromises.stat(filePath);
533
+
534
+ res.json({
535
+ projectId,
536
+ projectPath,
537
+ fileName,
538
+ filePath: path.relative(projectPath, filePath),
539
+ content,
540
+ size: stats.size,
541
+ created: stats.birthtime.toISOString(),
542
+ modified: stats.mtime.toISOString(),
543
+ timestamp: new Date().toISOString()
544
+ });
545
+
546
+ } catch (readError) {
547
+ console.error('Failed to read PRD file:', readError);
548
+ return res.status(500).json({
549
+ error: 'Failed to read PRD file',
550
+ message: readError.message
551
+ });
552
+ }
553
+
554
+ } catch (error) {
555
+ console.error('PRD read error:', error);
556
+ res.status(500).json({
557
+ error: 'Failed to read PRD file',
558
+ message: error.message
559
+ });
560
+ }
561
+ });
562
+
563
+ /**
564
+ * POST /api/taskmaster/init/:projectId
565
+ * Initialize TaskMaster in a project
566
+ */
567
+ router.post('/init/:projectId', async (req, res) => {
568
+ try {
569
+ const { projectId } = req.params;
570
+
571
+ const projectPath = await resolveProjectPathFromId(projectId);
572
+ if (!projectPath) {
573
+ return res.status(404).json({
574
+ error: 'Project not found',
575
+ message: `Project "${projectId}" does not exist`
576
+ });
577
+ }
578
+
579
+ // Check if TaskMaster is already initialized
580
+ const taskMasterPath = path.join(projectPath, '.taskmaster');
581
+ try {
582
+ await fsPromises.access(taskMasterPath, fs.constants.F_OK);
583
+ return res.status(400).json({
584
+ error: 'TaskMaster already initialized',
585
+ message: 'TaskMaster is already configured for this project'
586
+ });
587
+ } catch (error) {
588
+ // Directory doesn't exist, we can proceed
589
+ }
590
+
591
+ // Run taskmaster init command
592
+ const initProcess = runTaskmasterProcess('npx', ['task-master', 'init'], {
593
+ cwd: projectPath,
594
+ stdio: ['pipe', 'pipe', 'pipe']
595
+ }, ({ code, error, stdout, stderr }) => {
596
+ if (code === 0) {
597
+ // Broadcast TaskMaster project update via WebSocket. The
598
+ // WebSocket payload keeps using `projectId` so the frontend
599
+ // can match notifications against the current selection.
600
+ if (req.app.locals.wss) {
601
+ broadcastTaskMasterProjectUpdate(
602
+ req.app.locals.wss,
603
+ projectId,
604
+ { hasTaskmaster: true, status: 'initialized' }
605
+ );
606
+ }
607
+
608
+ res.json({
609
+ projectId,
610
+ projectPath,
611
+ message: 'TaskMaster initialized successfully',
612
+ output: stdout,
613
+ timestamp: new Date().toISOString()
614
+ });
615
+ } else {
616
+ console.error('TaskMaster init failed:', stderr);
617
+ res.status(500).json({
618
+ error: 'Failed to initialize TaskMaster',
619
+ message: error?.message || stderr || stdout,
620
+ code
621
+ });
622
+ }
623
+ });
624
+
625
+ // Send 'yes' responses to automated prompts
626
+ initProcess.stdin.write('yes\n');
627
+ initProcess.stdin.end();
628
+
629
+ } catch (error) {
630
+ console.error('TaskMaster init error:', error);
631
+ res.status(500).json({
632
+ error: 'Failed to initialize TaskMaster',
633
+ message: error.message
634
+ });
635
+ }
636
+ });
637
+
638
+ /**
639
+ * POST /api/taskmaster/add-task/:projectId
640
+ * Add a new task to the project
641
+ */
642
+ router.post('/add-task/:projectId', async (req, res) => {
643
+ try {
644
+ const { projectId } = req.params;
645
+ const { prompt, title, description, priority = 'medium', dependencies } = req.body;
646
+
647
+ if (!prompt && (!title || !description)) {
648
+ return res.status(400).json({
649
+ error: 'Missing required parameters',
650
+ message: 'Either "prompt" or both "title" and "description" are required'
651
+ });
652
+ }
653
+
654
+ const projectPath = await resolveProjectPathFromId(projectId);
655
+ if (!projectPath) {
656
+ return res.status(404).json({
657
+ error: 'Project not found',
658
+ message: `Project "${projectId}" does not exist`
659
+ });
660
+ }
661
+
662
+ // Build the task-master add-task command
663
+ const args = ['task-master-ai', 'add-task'];
664
+
665
+ if (prompt) {
666
+ args.push('--prompt', prompt);
667
+ args.push('--research'); // Use research for AI-generated tasks
668
+ } else {
669
+ args.push('--prompt', `Create a task titled "${title}" with description: ${description}`);
670
+ }
671
+
672
+ if (priority) {
673
+ args.push('--priority', priority);
674
+ }
675
+
676
+ if (dependencies) {
677
+ args.push('--dependencies', dependencies);
678
+ }
679
+
680
+ // Run task-master add-task command
681
+ const addTaskProcess = runTaskmasterProcess('npx', args, {
682
+ cwd: projectPath,
683
+ stdio: ['pipe', 'pipe', 'pipe']
684
+ }, ({ code, error, stdout, stderr }) => {
685
+ console.log('Add task process completed with code:', code);
686
+ console.log('Stdout:', stdout);
687
+ console.log('Stderr:', stderr);
688
+
689
+ if (code === 0) {
690
+ // Broadcast task update via WebSocket using the projectId so
691
+ // clients subscribed to this project get notified immediately.
692
+ if (req.app.locals.wss) {
693
+ broadcastTaskMasterTasksUpdate(
694
+ req.app.locals.wss,
695
+ projectId
696
+ );
697
+ }
698
+
699
+ res.json({
700
+ projectId,
701
+ projectPath,
702
+ message: 'Task added successfully',
703
+ output: stdout,
704
+ timestamp: new Date().toISOString()
705
+ });
706
+ } else {
707
+ console.error('Add task failed:', stderr);
708
+ res.status(500).json({
709
+ error: 'Failed to add task',
710
+ message: error?.message || stderr || stdout,
711
+ code
712
+ });
713
+ }
714
+ });
715
+
716
+ addTaskProcess.stdin.end();
717
+
718
+ } catch (error) {
719
+ console.error('Add task error:', error);
720
+ res.status(500).json({
721
+ error: 'Failed to add task',
722
+ message: error.message
723
+ });
724
+ }
725
+ });
726
+
727
+ /**
728
+ * PUT /api/taskmaster/update-task/:projectId/:taskId
729
+ * Update a specific task using TaskMaster CLI
730
+ */
731
+ router.put('/update-task/:projectId/:taskId', async (req, res) => {
732
+ try {
733
+ const { projectId, taskId } = req.params;
734
+ const { title, description, status, priority, details } = req.body;
735
+
736
+ const projectPath = await resolveProjectPathFromId(projectId);
737
+ if (!projectPath) {
738
+ return res.status(404).json({
739
+ error: 'Project not found',
740
+ message: `Project "${projectId}" does not exist`
741
+ });
742
+ }
743
+
744
+ // If only updating status, use set-status command
745
+ if (status && Object.keys(req.body).length === 1) {
746
+ const setStatusProcess = runTaskmasterProcess('npx', ['task-master-ai', 'set-status', `--id=${taskId}`, `--status=${status}`], {
747
+ cwd: projectPath,
748
+ stdio: ['pipe', 'pipe', 'pipe']
749
+ }, ({ code, error, stdout, stderr }) => {
750
+ if (code === 0) {
751
+ // Broadcast task update via WebSocket
752
+ if (req.app.locals.wss) {
753
+ broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectId);
754
+ }
755
+
756
+ res.json({
757
+ projectId,
758
+ projectPath,
759
+ taskId,
760
+ message: 'Task status updated successfully',
761
+ output: stdout,
762
+ timestamp: new Date().toISOString()
763
+ });
764
+ } else {
765
+ console.error('Set task status failed:', stderr);
766
+ res.status(500).json({
767
+ error: 'Failed to update task status',
768
+ message: error?.message || stderr || stdout,
769
+ code
770
+ });
771
+ }
772
+ });
773
+
774
+ setStatusProcess.stdin.end();
775
+ } else {
776
+ // For other updates, use update-task command with a prompt describing the changes
777
+ const updates = [];
778
+ if (title) updates.push(`title: "${title}"`);
779
+ if (description) updates.push(`description: "${description}"`);
780
+ if (priority) updates.push(`priority: "${priority}"`);
781
+ if (details) updates.push(`details: "${details}"`);
782
+
783
+ const prompt = `Update task with the following changes: ${updates.join(', ')}`;
784
+
785
+ const updateProcess = runTaskmasterProcess('npx', ['task-master-ai', 'update-task', `--id=${taskId}`, `--prompt=${prompt}`], {
786
+ cwd: projectPath,
787
+ stdio: ['pipe', 'pipe', 'pipe']
788
+ }, ({ code, error, stdout, stderr }) => {
789
+ if (code === 0) {
790
+ // Broadcast task update via WebSocket
791
+ if (req.app.locals.wss) {
792
+ broadcastTaskMasterTasksUpdate(req.app.locals.wss, projectId);
793
+ }
794
+
795
+ res.json({
796
+ projectId,
797
+ projectPath,
798
+ taskId,
799
+ message: 'Task updated successfully',
800
+ output: stdout,
801
+ timestamp: new Date().toISOString()
802
+ });
803
+ } else {
804
+ console.error('Update task failed:', stderr);
805
+ res.status(500).json({
806
+ error: 'Failed to update task',
807
+ message: error?.message || stderr || stdout,
808
+ code
809
+ });
810
+ }
811
+ });
812
+
813
+ updateProcess.stdin.end();
814
+ }
815
+
816
+ } catch (error) {
817
+ console.error('Update task error:', error);
818
+ res.status(500).json({
819
+ error: 'Failed to update task',
820
+ message: error.message
821
+ });
822
+ }
823
+ });
824
+
825
+ /**
826
+ * POST /api/taskmaster/parse-prd/:projectId
827
+ * Parse a PRD file to generate tasks
828
+ */
829
+ router.post('/parse-prd/:projectId', async (req, res) => {
830
+ try {
831
+ const { projectId } = req.params;
832
+ const { fileName = 'prd.txt', numTasks, append = false } = req.body;
833
+
834
+ const projectPath = await resolveProjectPathFromId(projectId);
835
+ if (!projectPath) {
836
+ return res.status(404).json({
837
+ error: 'Project not found',
838
+ message: `Project "${projectId}" does not exist`
839
+ });
840
+ }
841
+
842
+ const prdPath = path.join(projectPath, '.taskmaster', 'docs', fileName);
843
+
844
+ // Check if PRD file exists
845
+ try {
846
+ await fsPromises.access(prdPath, fs.constants.F_OK);
847
+ } catch (error) {
848
+ return res.status(404).json({
849
+ error: 'PRD file not found',
850
+ message: `File "${fileName}" does not exist in .taskmaster/docs/`
851
+ });
852
+ }
853
+
854
+ // Build the command args
855
+ const args = ['task-master-ai', 'parse-prd', prdPath];
856
+
857
+ if (numTasks) {
858
+ args.push('--num-tasks', numTasks.toString());
859
+ }
860
+
861
+ if (append) {
862
+ args.push('--append');
863
+ }
864
+
865
+ args.push('--research'); // Use research for better PRD parsing
866
+
867
+ // Run task-master parse-prd command
868
+ const parsePRDProcess = runTaskmasterProcess('npx', args, {
869
+ cwd: projectPath,
870
+ stdio: ['pipe', 'pipe', 'pipe']
871
+ }, ({ code, error, stdout, stderr }) => {
872
+ if (code === 0) {
873
+ // Broadcast task update via WebSocket
874
+ if (req.app.locals.wss) {
875
+ broadcastTaskMasterTasksUpdate(
876
+ req.app.locals.wss,
877
+ projectId
878
+ );
879
+ }
880
+
881
+ res.json({
882
+ projectId,
883
+ projectPath,
884
+ prdFile: fileName,
885
+ message: 'PRD parsed and tasks generated successfully',
886
+ output: stdout,
887
+ timestamp: new Date().toISOString()
888
+ });
889
+ } else {
890
+ console.error('Parse PRD failed:', stderr);
891
+ res.status(500).json({
892
+ error: 'Failed to parse PRD',
893
+ message: error?.message || stderr || stdout,
894
+ code
895
+ });
896
+ }
897
+ });
898
+
899
+ parsePRDProcess.stdin.end();
900
+
901
+ } catch (error) {
902
+ console.error('Parse PRD error:', error);
903
+ res.status(500).json({
904
+ error: 'Failed to parse PRD',
905
+ message: error.message
906
+ });
907
+ }
908
+ });
909
+
910
+ /**
911
+ * GET /api/taskmaster/prd-templates
912
+ * Get available PRD templates
913
+ */
914
+ router.get('/prd-templates', async (req, res) => {
915
+ try {
916
+ // Return built-in templates
917
+ const templates = [
918
+ {
919
+ id: 'web-app',
920
+ name: 'Web Application',
921
+ description: 'Template for web application projects with frontend and backend components',
922
+ category: 'web',
923
+ content: `# Product Requirements Document - Web Application
924
+
925
+ ## Overview
926
+ **Product Name:** [Your App Name]
927
+ **Version:** 1.0
928
+ **Date:** ${new Date().toISOString().split('T')[0]}
929
+ **Author:** [Your Name]
930
+
931
+ ## Executive Summary
932
+ Brief description of what this web application will do and why it's needed.
933
+
934
+ ## Product Goals
935
+ - Goal 1: [Specific measurable goal]
936
+ - Goal 2: [Specific measurable goal]
937
+ - Goal 3: [Specific measurable goal]
938
+
939
+ ## User Stories
940
+ ### Core Features
941
+ 1. **User Registration & Authentication**
942
+ - As a user, I want to create an account so I can access personalized features
943
+ - As a user, I want to log in securely so my data is protected
944
+ - As a user, I want to reset my password if I forget it
945
+
946
+ 2. **Main Application Features**
947
+ - As a user, I want to [core feature 1] so I can [benefit]
948
+ - As a user, I want to [core feature 2] so I can [benefit]
949
+ - As a user, I want to [core feature 3] so I can [benefit]
950
+
951
+ 3. **User Interface**
952
+ - As a user, I want a responsive design so I can use the app on any device
953
+ - As a user, I want intuitive navigation so I can easily find features
954
+
955
+ ## Technical Requirements
956
+ ### Frontend
957
+ - Framework: React/Vue/Angular or vanilla JavaScript
958
+ - Styling: CSS framework (Tailwind, Bootstrap, etc.)
959
+ - State Management: Redux/Vuex/Context API
960
+ - Build Tools: Webpack/Vite
961
+ - Testing: Jest/Vitest for unit tests
962
+
963
+ ### Backend
964
+ - Runtime: Node.js/Python/Java
965
+ - Database: PostgreSQL/MySQL/MongoDB
966
+ - API: RESTful API or GraphQL
967
+ - Authentication: JWT tokens
968
+ - Testing: Integration and unit tests
969
+
970
+ ### Infrastructure
971
+ - Hosting: Cloud provider (AWS, Azure, GCP)
972
+ - CI/CD: GitHub Actions/GitLab CI
973
+ - Monitoring: Application monitoring tools
974
+ - Security: HTTPS, input validation, rate limiting
975
+
976
+ ## Success Metrics
977
+ - User engagement metrics
978
+ - Performance benchmarks (load time < 2s)
979
+ - Error rates < 1%
980
+ - User satisfaction scores
981
+
982
+ ## Timeline
983
+ - Phase 1: Core functionality (4-6 weeks)
984
+ - Phase 2: Advanced features (2-4 weeks)
985
+ - Phase 3: Polish and launch (2 weeks)
986
+
987
+ ## Constraints & Assumptions
988
+ - Budget constraints
989
+ - Technical limitations
990
+ - Team size and expertise
991
+ - Timeline constraints`
992
+ },
993
+ {
994
+ id: 'api',
995
+ name: 'REST API',
996
+ description: 'Template for REST API development projects',
997
+ category: 'backend',
998
+ content: `# Product Requirements Document - REST API
999
+
1000
+ ## Overview
1001
+ **API Name:** [Your API Name]
1002
+ **Version:** v1.0
1003
+ **Date:** ${new Date().toISOString().split('T')[0]}
1004
+ **Author:** [Your Name]
1005
+
1006
+ ## Executive Summary
1007
+ Description of the API's purpose, target users, and primary use cases.
1008
+
1009
+ ## API Goals
1010
+ - Goal 1: Provide secure data access
1011
+ - Goal 2: Ensure scalable architecture
1012
+ - Goal 3: Maintain high availability (99.9% uptime)
1013
+
1014
+ ## Functional Requirements
1015
+ ### Core Endpoints
1016
+ 1. **Authentication Endpoints**
1017
+ - POST /api/auth/login - User authentication
1018
+ - POST /api/auth/logout - User logout
1019
+ - POST /api/auth/refresh - Token refresh
1020
+ - POST /api/auth/register - User registration
1021
+
1022
+ 2. **Data Management Endpoints**
1023
+ - GET /api/resources - List resources with pagination
1024
+ - GET /api/resources/{id} - Get specific resource
1025
+ - POST /api/resources - Create new resource
1026
+ - PUT /api/resources/{id} - Update existing resource
1027
+ - DELETE /api/resources/{id} - Delete resource
1028
+
1029
+ 3. **Administrative Endpoints**
1030
+ - GET /api/admin/users - Manage users (admin only)
1031
+ - GET /api/admin/analytics - System analytics
1032
+ - POST /api/admin/backup - Trigger system backup
1033
+
1034
+ ## Technical Requirements
1035
+ ### API Design
1036
+ - RESTful architecture following OpenAPI 3.0 specification
1037
+ - JSON request/response format
1038
+ - Consistent error response format
1039
+ - API versioning strategy
1040
+
1041
+ ### Authentication & Security
1042
+ - JWT token-based authentication
1043
+ - Role-based access control (RBAC)
1044
+ - Rate limiting (100 requests/minute per user)
1045
+ - Input validation and sanitization
1046
+ - HTTPS enforcement
1047
+
1048
+ ### Database
1049
+ - Database type: [PostgreSQL/MongoDB/MySQL]
1050
+ - Connection pooling
1051
+ - Database migrations
1052
+ - Backup and recovery procedures
1053
+
1054
+ ### Performance Requirements
1055
+ - Response time: < 200ms for 95% of requests
1056
+ - Throughput: 1000+ requests/second
1057
+ - Concurrent users: 10,000+
1058
+ - Database query optimization
1059
+
1060
+ ### Documentation
1061
+ - Auto-generated API documentation (Swagger/OpenAPI)
1062
+ - Code examples for common use cases
1063
+ - SDK development for major languages
1064
+ - Postman collection for testing
1065
+
1066
+ ## Error Handling
1067
+ - Standardized error codes and messages
1068
+ - Proper HTTP status codes
1069
+ - Detailed error logging
1070
+ - Graceful degradation strategies
1071
+
1072
+ ## Testing Strategy
1073
+ - Unit tests (80%+ coverage)
1074
+ - Integration tests for all endpoints
1075
+ - Load testing and performance testing
1076
+ - Security testing (OWASP compliance)
1077
+
1078
+ ## Monitoring & Logging
1079
+ - Application performance monitoring
1080
+ - Error tracking and alerting
1081
+ - Access logs and audit trails
1082
+ - Health check endpoints
1083
+
1084
+ ## Deployment
1085
+ - Containerized deployment (Docker)
1086
+ - CI/CD pipeline setup
1087
+ - Environment management (dev, staging, prod)
1088
+ - Blue-green deployment strategy
1089
+
1090
+ ## Success Metrics
1091
+ - API uptime > 99.9%
1092
+ - Average response time < 200ms
1093
+ - Zero critical security vulnerabilities
1094
+ - Developer adoption metrics`
1095
+ },
1096
+ {
1097
+ id: 'mobile-app',
1098
+ name: 'Mobile Application',
1099
+ description: 'Template for mobile app development projects (iOS/Android)',
1100
+ category: 'mobile',
1101
+ content: `# Product Requirements Document - Mobile Application
1102
+
1103
+ ## Overview
1104
+ **App Name:** [Your App Name]
1105
+ **Platform:** iOS / Android / Cross-platform
1106
+ **Version:** 1.0
1107
+ **Date:** ${new Date().toISOString().split('T')[0]}
1108
+ **Author:** [Your Name]
1109
+
1110
+ ## Executive Summary
1111
+ Brief description of the mobile app's purpose, target audience, and key value proposition.
1112
+
1113
+ ## Product Goals
1114
+ - Goal 1: [Specific user engagement goal]
1115
+ - Goal 2: [Specific functionality goal]
1116
+ - Goal 3: [Specific performance goal]
1117
+
1118
+ ## User Stories
1119
+ ### Core Features
1120
+ 1. **Onboarding & Authentication**
1121
+ - As a new user, I want a simple onboarding process
1122
+ - As a user, I want to sign up with email or social media
1123
+ - As a user, I want biometric authentication for security
1124
+
1125
+ 2. **Main App Features**
1126
+ - As a user, I want [core feature 1] accessible from home screen
1127
+ - As a user, I want [core feature 2] to work offline
1128
+ - As a user, I want to sync data across devices
1129
+
1130
+ 3. **User Experience**
1131
+ - As a user, I want intuitive navigation patterns
1132
+ - As a user, I want fast loading times
1133
+ - As a user, I want accessibility features
1134
+
1135
+ ## Technical Requirements
1136
+ ### Mobile Development
1137
+ - **Cross-platform:** React Native / Flutter / Xamarin
1138
+ - **Native:** Swift (iOS) / Kotlin (Android)
1139
+ - **State Management:** Redux / MobX / Provider
1140
+ - **Navigation:** React Navigation / Flutter Navigation
1141
+
1142
+ ### Backend Integration
1143
+ - REST API or GraphQL integration
1144
+ - Real-time features (WebSockets/Push notifications)
1145
+ - Offline data synchronization
1146
+ - Background processing
1147
+
1148
+ ### Device Features
1149
+ - Camera and photo library access
1150
+ - GPS location services
1151
+ - Push notifications
1152
+ - Biometric authentication
1153
+ - Device storage
1154
+
1155
+ ### Performance Requirements
1156
+ - App launch time < 3 seconds
1157
+ - Screen transition animations < 300ms
1158
+ - Memory usage optimization
1159
+ - Battery usage optimization
1160
+
1161
+ ## Platform-Specific Considerations
1162
+ ### iOS Requirements
1163
+ - iOS 13.0+ minimum version
1164
+ - App Store guidelines compliance
1165
+ - iOS design guidelines (Human Interface Guidelines)
1166
+ - TestFlight beta testing
1167
+
1168
+ ### Android Requirements
1169
+ - Android 8.0+ (API level 26) minimum
1170
+ - Google Play Store guidelines
1171
+ - Material Design guidelines
1172
+ - Google Play Console testing
1173
+
1174
+ ## User Interface Design
1175
+ - Responsive design for different screen sizes
1176
+ - Dark mode support
1177
+ - Accessibility compliance (WCAG 2.1)
1178
+ - Consistent design system
1179
+
1180
+ ## Security & Privacy
1181
+ - Secure data storage (Keychain/Keystore)
1182
+ - API communication encryption
1183
+ - Privacy policy compliance (GDPR/CCPA)
1184
+ - App security best practices
1185
+
1186
+ ## Testing Strategy
1187
+ - Unit testing (80%+ coverage)
1188
+ - UI/E2E testing (Detox/Appium)
1189
+ - Device testing on multiple screen sizes
1190
+ - Performance testing
1191
+ - Security testing
1192
+
1193
+ ## App Store Deployment
1194
+ - App store optimization (ASO)
1195
+ - App icons and screenshots
1196
+ - Store listing content
1197
+ - Release management strategy
1198
+
1199
+ ## Analytics & Monitoring
1200
+ - User analytics (Firebase/Analytics)
1201
+ - Crash reporting (Crashlytics/Sentry)
1202
+ - Performance monitoring
1203
+ - User feedback collection
1204
+
1205
+ ## Success Metrics
1206
+ - App store ratings > 4.0
1207
+ - User retention rates
1208
+ - Daily/Monthly active users
1209
+ - App performance metrics
1210
+ - Conversion rates`
1211
+ },
1212
+ {
1213
+ id: 'data-analysis',
1214
+ name: 'Data Analysis Project',
1215
+ description: 'Template for data analysis and visualization projects',
1216
+ category: 'data',
1217
+ content: `# Product Requirements Document - Data Analysis Project
1218
+
1219
+ ## Overview
1220
+ **Project Name:** [Your Analysis Project]
1221
+ **Analysis Type:** [Descriptive/Predictive/Prescriptive]
1222
+ **Date:** ${new Date().toISOString().split('T')[0]}
1223
+ **Author:** [Your Name]
1224
+
1225
+ ## Executive Summary
1226
+ Description of the business problem, data sources, and expected insights.
1227
+
1228
+ ## Project Goals
1229
+ - Goal 1: [Specific business question to answer]
1230
+ - Goal 2: [Specific prediction to make]
1231
+ - Goal 3: [Specific recommendation to provide]
1232
+
1233
+ ## Business Requirements
1234
+ ### Key Questions
1235
+ 1. What patterns exist in the current data?
1236
+ 2. What factors influence [target variable]?
1237
+ 3. What predictions can be made for [future outcome]?
1238
+ 4. What recommendations can improve [business metric]?
1239
+
1240
+ ### Success Criteria
1241
+ - Actionable insights for stakeholders
1242
+ - Statistical significance in findings
1243
+ - Reproducible analysis pipeline
1244
+ - Clear visualization and reporting
1245
+
1246
+ ## Data Requirements
1247
+ ### Data Sources
1248
+ 1. **Primary Data**
1249
+ - Source: [Database/API/Files]
1250
+ - Format: [CSV/JSON/SQL]
1251
+ - Size: [Volume estimate]
1252
+ - Update frequency: [Real-time/Daily/Monthly]
1253
+
1254
+ 2. **External Data**
1255
+ - Third-party APIs
1256
+ - Public datasets
1257
+ - Market research data
1258
+
1259
+ ### Data Quality Requirements
1260
+ - Data completeness (< 5% missing values)
1261
+ - Data accuracy validation
1262
+ - Data consistency checks
1263
+ - Historical data availability
1264
+
1265
+ ## Technical Requirements
1266
+ ### Data Pipeline
1267
+ - Data extraction and ingestion
1268
+ - Data cleaning and preprocessing
1269
+ - Data transformation and feature engineering
1270
+ - Data validation and quality checks
1271
+
1272
+ ### Analysis Tools
1273
+ - **Programming:** Python/R/SQL
1274
+ - **Libraries:** pandas, numpy, scikit-learn, matplotlib
1275
+ - **Visualization:** Tableau, PowerBI, or custom dashboards
1276
+ - **Version Control:** Git for code and DVC for data
1277
+
1278
+ ### Computing Resources
1279
+ - Local development environment
1280
+ - Cloud computing (AWS/GCP/Azure) if needed
1281
+ - Database access and permissions
1282
+ - Storage requirements
1283
+
1284
+ ## Analysis Methodology
1285
+ ### Data Exploration
1286
+ 1. Descriptive statistics and data profiling
1287
+ 2. Data visualization and pattern identification
1288
+ 3. Correlation analysis
1289
+ 4. Outlier detection and handling
1290
+
1291
+ ### Statistical Analysis
1292
+ 1. Hypothesis formulation
1293
+ 2. Statistical testing
1294
+ 3. Confidence intervals
1295
+ 4. Effect size calculations
1296
+
1297
+ ### Machine Learning (if applicable)
1298
+ 1. Feature selection and engineering
1299
+ 2. Model selection and training
1300
+ 3. Cross-validation and evaluation
1301
+ 4. Model interpretation and explainability
1302
+
1303
+ ## Deliverables
1304
+ ### Reports
1305
+ - Executive summary for stakeholders
1306
+ - Technical analysis report
1307
+ - Data quality report
1308
+ - Methodology documentation
1309
+
1310
+ ### Visualizations
1311
+ - Interactive dashboards
1312
+ - Static charts and graphs
1313
+ - Data story presentations
1314
+ - Key findings infographics
1315
+
1316
+ ### Code & Documentation
1317
+ - Reproducible analysis scripts
1318
+ - Data pipeline code
1319
+ - Documentation and comments
1320
+ - Testing and validation code
1321
+
1322
+ ## Timeline
1323
+ - Phase 1: Data collection and exploration (2 weeks)
1324
+ - Phase 2: Analysis and modeling (3 weeks)
1325
+ - Phase 3: Reporting and visualization (1 week)
1326
+ - Phase 4: Stakeholder presentation (1 week)
1327
+
1328
+ ## Risks & Assumptions
1329
+ - Data availability and quality risks
1330
+ - Technical complexity assumptions
1331
+ - Resource and timeline constraints
1332
+ - Stakeholder engagement assumptions
1333
+
1334
+ ## Success Metrics
1335
+ - Stakeholder satisfaction with insights
1336
+ - Accuracy of predictions (if applicable)
1337
+ - Business impact of recommendations
1338
+ - Reproducibility of results`
1339
+ }
1340
+ ];
1341
+
1342
+ res.json({
1343
+ templates,
1344
+ timestamp: new Date().toISOString()
1345
+ });
1346
+
1347
+ } catch (error) {
1348
+ console.error('PRD templates error:', error);
1349
+ res.status(500).json({
1350
+ error: 'Failed to get PRD templates',
1351
+ message: error.message
1352
+ });
1353
+ }
1354
+ });
1355
+
1356
+ /**
1357
+ * POST /api/taskmaster/apply-template/:projectId
1358
+ * Apply a PRD template to create a new PRD file
1359
+ */
1360
+ router.post('/apply-template/:projectId', async (req, res) => {
1361
+ try {
1362
+ const { projectId } = req.params;
1363
+ const { templateId, fileName = 'prd.txt', customizations = {} } = req.body;
1364
+
1365
+ if (!templateId) {
1366
+ return res.status(400).json({
1367
+ error: 'Missing required parameter',
1368
+ message: 'templateId is required'
1369
+ });
1370
+ }
1371
+
1372
+ const projectPath = await resolveProjectPathFromId(projectId);
1373
+ if (!projectPath) {
1374
+ return res.status(404).json({
1375
+ error: 'Project not found',
1376
+ message: `Project "${projectId}" does not exist`
1377
+ });
1378
+ }
1379
+
1380
+ // Get the template content (this would normally fetch from the templates list)
1381
+ const templates = await getAvailableTemplates();
1382
+ const template = templates.find(t => t.id === templateId);
1383
+
1384
+ if (!template) {
1385
+ return res.status(404).json({
1386
+ error: 'Template not found',
1387
+ message: `Template "${templateId}" does not exist`
1388
+ });
1389
+ }
1390
+
1391
+ // Apply customizations to template content
1392
+ let content = template.content;
1393
+
1394
+ // Replace placeholders with customizations
1395
+ for (const [key, value] of Object.entries(customizations)) {
1396
+ const placeholder = `[${key}]`;
1397
+ content = content.replace(new RegExp(placeholder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g'), value);
1398
+ }
1399
+
1400
+ // Ensure .taskmaster/docs directory exists
1401
+ const docsDir = path.join(projectPath, '.taskmaster', 'docs');
1402
+ try {
1403
+ await fsPromises.mkdir(docsDir, { recursive: true });
1404
+ } catch (error) {
1405
+ console.error('Failed to create docs directory:', error);
1406
+ }
1407
+
1408
+ const filePath = path.join(docsDir, fileName);
1409
+
1410
+ // Write the template content to the file
1411
+ try {
1412
+ await fsPromises.writeFile(filePath, content, 'utf8');
1413
+
1414
+ res.json({
1415
+ projectId,
1416
+ projectPath,
1417
+ templateId,
1418
+ templateName: template.name,
1419
+ fileName,
1420
+ filePath: filePath,
1421
+ message: 'PRD template applied successfully',
1422
+ timestamp: new Date().toISOString()
1423
+ });
1424
+
1425
+ } catch (writeError) {
1426
+ console.error('Failed to write PRD template:', writeError);
1427
+ return res.status(500).json({
1428
+ error: 'Failed to write PRD template',
1429
+ message: writeError.message
1430
+ });
1431
+ }
1432
+
1433
+ } catch (error) {
1434
+ console.error('Apply template error:', error);
1435
+ res.status(500).json({
1436
+ error: 'Failed to apply PRD template',
1437
+ message: error.message
1438
+ });
1439
+ }
1440
+ });
1441
+
1442
+ // Helper function to get available templates
1443
+ async function getAvailableTemplates() {
1444
+ // This could be extended to read from files or database
1445
+ return [
1446
+ {
1447
+ id: 'web-app',
1448
+ name: 'Web Application',
1449
+ description: 'Template for web application projects',
1450
+ category: 'web',
1451
+ content: `# Product Requirements Document - Web Application
1452
+
1453
+ ## Overview
1454
+ **Product Name:** [Your App Name]
1455
+ **Version:** 1.0
1456
+ **Date:** ${new Date().toISOString().split('T')[0]}
1457
+ **Author:** [Your Name]
1458
+
1459
+ ## Executive Summary
1460
+ Brief description of what this web application will do and why it's needed.
1461
+
1462
+ ## User Stories
1463
+ 1. As a user, I want [feature] so I can [benefit]
1464
+ 2. As a user, I want [feature] so I can [benefit]
1465
+ 3. As a user, I want [feature] so I can [benefit]
1466
+
1467
+ ## Technical Requirements
1468
+ - Frontend framework
1469
+ - Backend services
1470
+ - Database requirements
1471
+ - Security considerations
1472
+
1473
+ ## Success Metrics
1474
+ - User engagement metrics
1475
+ - Performance benchmarks
1476
+ - Business objectives`
1477
+ },
1478
+ // Add other templates here if needed
1479
+ ];
1480
+ }
1481
+
1482
+ return router;
1483
+ }