task-o-matic-core 0.1.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 (447) hide show
  1. package/README.md +646 -0
  2. package/dist/index.d.ts +27 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +46 -0
  5. package/dist/lib/ai-service/ai-operations.d.ts +45 -0
  6. package/dist/lib/ai-service/ai-operations.d.ts.map +1 -0
  7. package/dist/lib/ai-service/ai-operations.js +60 -0
  8. package/dist/lib/ai-service/base-operations.d.ts +43 -0
  9. package/dist/lib/ai-service/base-operations.d.ts.map +1 -0
  10. package/dist/lib/ai-service/base-operations.js +119 -0
  11. package/dist/lib/ai-service/documentation-operations.d.ts +18 -0
  12. package/dist/lib/ai-service/documentation-operations.d.ts.map +1 -0
  13. package/dist/lib/ai-service/documentation-operations.js +308 -0
  14. package/dist/lib/ai-service/filesystem-tools.d.ts +69 -0
  15. package/dist/lib/ai-service/filesystem-tools.d.ts.map +1 -0
  16. package/dist/lib/ai-service/filesystem-tools.js +70 -0
  17. package/dist/lib/ai-service/json-parser.d.ts +34 -0
  18. package/dist/lib/ai-service/json-parser.d.ts.map +1 -0
  19. package/dist/lib/ai-service/json-parser.js +177 -0
  20. package/dist/lib/ai-service/mcp-client.d.ts +9 -0
  21. package/dist/lib/ai-service/mcp-client.d.ts.map +1 -0
  22. package/dist/lib/ai-service/mcp-client.js +48 -0
  23. package/dist/lib/ai-service/model-provider.d.ts +12 -0
  24. package/dist/lib/ai-service/model-provider.d.ts.map +1 -0
  25. package/dist/lib/ai-service/model-provider.js +146 -0
  26. package/dist/lib/ai-service/prd-operations.d.ts +25 -0
  27. package/dist/lib/ai-service/prd-operations.d.ts.map +1 -0
  28. package/dist/lib/ai-service/prd-operations.js +592 -0
  29. package/dist/lib/ai-service/research-tools.d.ts +4 -0
  30. package/dist/lib/ai-service/research-tools.d.ts.map +1 -0
  31. package/dist/lib/ai-service/research-tools.js +8 -0
  32. package/dist/lib/ai-service/retry-handler.d.ts +8 -0
  33. package/dist/lib/ai-service/retry-handler.d.ts.map +1 -0
  34. package/dist/lib/ai-service/retry-handler.js +63 -0
  35. package/dist/lib/ai-service/task-operations.d.ts +13 -0
  36. package/dist/lib/ai-service/task-operations.d.ts.map +1 -0
  37. package/dist/lib/ai-service/task-operations.js +220 -0
  38. package/dist/lib/benchmark/registry.d.ts +11 -0
  39. package/dist/lib/benchmark/registry.d.ts.map +1 -0
  40. package/dist/lib/benchmark/registry.js +212 -0
  41. package/dist/lib/benchmark/runner.d.ts +6 -0
  42. package/dist/lib/benchmark/runner.d.ts.map +1 -0
  43. package/dist/lib/benchmark/runner.js +150 -0
  44. package/dist/lib/benchmark/storage.d.ts +13 -0
  45. package/dist/lib/benchmark/storage.d.ts.map +1 -0
  46. package/dist/lib/benchmark/storage.js +100 -0
  47. package/dist/lib/benchmark/types.d.ts +104 -0
  48. package/dist/lib/benchmark/types.d.ts.map +1 -0
  49. package/dist/lib/benchmark/types.js +2 -0
  50. package/dist/lib/better-t-stack-cli.d.ts +50 -0
  51. package/dist/lib/better-t-stack-cli.d.ts.map +1 -0
  52. package/dist/lib/better-t-stack-cli.js +428 -0
  53. package/dist/lib/bootstrap/cli-bootstrap.d.ts +14 -0
  54. package/dist/lib/bootstrap/cli-bootstrap.d.ts.map +1 -0
  55. package/dist/lib/bootstrap/cli-bootstrap.js +322 -0
  56. package/dist/lib/bootstrap/index.d.ts +3 -0
  57. package/dist/lib/bootstrap/index.d.ts.map +1 -0
  58. package/dist/lib/bootstrap/index.js +18 -0
  59. package/dist/lib/bootstrap/medusa-bootstrap.d.ts +14 -0
  60. package/dist/lib/bootstrap/medusa-bootstrap.d.ts.map +1 -0
  61. package/dist/lib/bootstrap/medusa-bootstrap.js +215 -0
  62. package/dist/lib/config-validation.d.ts +215 -0
  63. package/dist/lib/config-validation.d.ts.map +1 -0
  64. package/dist/lib/config-validation.js +254 -0
  65. package/dist/lib/config.d.ts +55 -0
  66. package/dist/lib/config.d.ts.map +1 -0
  67. package/dist/lib/config.js +351 -0
  68. package/dist/lib/context-builder.d.ts +66 -0
  69. package/dist/lib/context-builder.d.ts.map +1 -0
  70. package/dist/lib/context-builder.js +322 -0
  71. package/dist/lib/executors/claude-code-executor.d.ts +9 -0
  72. package/dist/lib/executors/claude-code-executor.d.ts.map +1 -0
  73. package/dist/lib/executors/claude-code-executor.js +69 -0
  74. package/dist/lib/executors/codex-executor.d.ts +9 -0
  75. package/dist/lib/executors/codex-executor.d.ts.map +1 -0
  76. package/dist/lib/executors/codex-executor.js +73 -0
  77. package/dist/lib/executors/executor-factory.d.ts +5 -0
  78. package/dist/lib/executors/executor-factory.d.ts.map +1 -0
  79. package/dist/lib/executors/executor-factory.js +27 -0
  80. package/dist/lib/executors/gemini-executor.d.ts +9 -0
  81. package/dist/lib/executors/gemini-executor.d.ts.map +1 -0
  82. package/dist/lib/executors/gemini-executor.js +67 -0
  83. package/dist/lib/executors/kilo-executor.d.ts +9 -0
  84. package/dist/lib/executors/kilo-executor.d.ts.map +1 -0
  85. package/dist/lib/executors/kilo-executor.js +69 -0
  86. package/dist/lib/executors/opencode-executor.d.ts +9 -0
  87. package/dist/lib/executors/opencode-executor.d.ts.map +1 -0
  88. package/dist/lib/executors/opencode-executor.js +67 -0
  89. package/dist/lib/git-utils.d.ts +88 -0
  90. package/dist/lib/git-utils.d.ts.map +1 -0
  91. package/dist/lib/git-utils.js +242 -0
  92. package/dist/lib/hooks.d.ts +73 -0
  93. package/dist/lib/hooks.d.ts.map +1 -0
  94. package/dist/lib/hooks.js +62 -0
  95. package/dist/lib/index.d.ts +100 -0
  96. package/dist/lib/index.d.ts.map +1 -0
  97. package/dist/lib/index.js +143 -0
  98. package/dist/lib/logger.d.ts +20 -0
  99. package/dist/lib/logger.d.ts.map +1 -0
  100. package/dist/lib/logger.js +32 -0
  101. package/dist/lib/notifications.d.ts +7 -0
  102. package/dist/lib/notifications.d.ts.map +1 -0
  103. package/dist/lib/notifications.js +81 -0
  104. package/dist/lib/prompt-builder.d.ts +70 -0
  105. package/dist/lib/prompt-builder.d.ts.map +1 -0
  106. package/dist/lib/prompt-builder.js +344 -0
  107. package/dist/lib/prompt-registry.d.ts +22 -0
  108. package/dist/lib/prompt-registry.d.ts.map +1 -0
  109. package/dist/lib/prompt-registry.js +409 -0
  110. package/dist/lib/provider-defaults.json +32 -0
  111. package/dist/lib/storage/file-system.d.ts +57 -0
  112. package/dist/lib/storage/file-system.d.ts.map +1 -0
  113. package/dist/lib/storage/file-system.js +638 -0
  114. package/dist/lib/storage/storage-callbacks.d.ts +17 -0
  115. package/dist/lib/storage/storage-callbacks.d.ts.map +1 -0
  116. package/dist/lib/storage/storage-callbacks.js +94 -0
  117. package/dist/lib/storage/types.d.ts +43 -0
  118. package/dist/lib/storage/types.d.ts.map +1 -0
  119. package/dist/lib/storage/types.js +2 -0
  120. package/dist/lib/task-execution-core.d.ts +7 -0
  121. package/dist/lib/task-execution-core.d.ts.map +1 -0
  122. package/dist/lib/task-execution-core.js +381 -0
  123. package/dist/lib/task-execution.d.ts +7 -0
  124. package/dist/lib/task-execution.d.ts.map +1 -0
  125. package/dist/lib/task-execution.js +40 -0
  126. package/dist/lib/task-loop-execution.d.ts +7 -0
  127. package/dist/lib/task-loop-execution.d.ts.map +1 -0
  128. package/dist/lib/task-loop-execution.js +156 -0
  129. package/dist/lib/task-planning.d.ts +29 -0
  130. package/dist/lib/task-planning.d.ts.map +1 -0
  131. package/dist/lib/task-planning.js +103 -0
  132. package/dist/lib/task-review.d.ts +27 -0
  133. package/dist/lib/task-review.d.ts.map +1 -0
  134. package/dist/lib/task-review.js +103 -0
  135. package/dist/lib/validation.d.ts +26 -0
  136. package/dist/lib/validation.d.ts.map +1 -0
  137. package/dist/lib/validation.js +98 -0
  138. package/dist/prompts/documentation-detection.d.ts +2 -0
  139. package/dist/prompts/documentation-detection.d.ts.map +1 -0
  140. package/dist/prompts/documentation-detection.js +24 -0
  141. package/dist/prompts/documentation-recap.d.ts +3 -0
  142. package/dist/prompts/documentation-recap.d.ts.map +1 -0
  143. package/dist/prompts/documentation-recap.js +13 -0
  144. package/dist/prompts/index.d.ts +15 -0
  145. package/dist/prompts/index.d.ts.map +1 -0
  146. package/dist/prompts/index.js +30 -0
  147. package/dist/prompts/prd-combination.d.ts +2 -0
  148. package/dist/prompts/prd-combination.d.ts.map +1 -0
  149. package/dist/prompts/prd-combination.js +35 -0
  150. package/dist/prompts/prd-generation.d.ts +2 -0
  151. package/dist/prompts/prd-generation.d.ts.map +1 -0
  152. package/dist/prompts/prd-generation.js +49 -0
  153. package/dist/prompts/prd-parsing.d.ts +3 -0
  154. package/dist/prompts/prd-parsing.d.ts.map +1 -0
  155. package/dist/prompts/prd-parsing.js +172 -0
  156. package/dist/prompts/prd-question-answer.d.ts +3 -0
  157. package/dist/prompts/prd-question-answer.d.ts.map +1 -0
  158. package/dist/prompts/prd-question-answer.js +27 -0
  159. package/dist/prompts/prd-question.d.ts +3 -0
  160. package/dist/prompts/prd-question.d.ts.map +1 -0
  161. package/dist/prompts/prd-question.js +40 -0
  162. package/dist/prompts/prd-rework.d.ts +3 -0
  163. package/dist/prompts/prd-rework.d.ts.map +1 -0
  164. package/dist/prompts/prd-rework.js +81 -0
  165. package/dist/prompts/prd-suggest-stack.d.ts +3 -0
  166. package/dist/prompts/prd-suggest-stack.d.ts.map +1 -0
  167. package/dist/prompts/prd-suggest-stack.js +99 -0
  168. package/dist/prompts/task-breakdown.d.ts +3 -0
  169. package/dist/prompts/task-breakdown.d.ts.map +1 -0
  170. package/dist/prompts/task-breakdown.js +151 -0
  171. package/dist/prompts/task-enhancement.d.ts +3 -0
  172. package/dist/prompts/task-enhancement.d.ts.map +1 -0
  173. package/dist/prompts/task-enhancement.js +140 -0
  174. package/dist/prompts/task-execution.d.ts +3 -0
  175. package/dist/prompts/task-execution.d.ts.map +1 -0
  176. package/dist/prompts/task-execution.js +24 -0
  177. package/dist/prompts/task-planning.d.ts +3 -0
  178. package/dist/prompts/task-planning.d.ts.map +1 -0
  179. package/dist/prompts/task-planning.js +66 -0
  180. package/dist/prompts/workflow-assistance.d.ts +32 -0
  181. package/dist/prompts/workflow-assistance.d.ts.map +1 -0
  182. package/dist/prompts/workflow-assistance.js +130 -0
  183. package/dist/prompts/workflow-prompts.d.ts +9 -0
  184. package/dist/prompts/workflow-prompts.d.ts.map +1 -0
  185. package/dist/prompts/workflow-prompts.js +93 -0
  186. package/dist/services/benchmark.d.ts +26 -0
  187. package/dist/services/benchmark.d.ts.map +1 -0
  188. package/dist/services/benchmark.js +343 -0
  189. package/dist/services/prd.d.ts +136 -0
  190. package/dist/services/prd.d.ts.map +1 -0
  191. package/dist/services/prd.js +550 -0
  192. package/dist/services/tasks.d.ts +388 -0
  193. package/dist/services/tasks.d.ts.map +1 -0
  194. package/dist/services/tasks.js +1150 -0
  195. package/dist/services/workflow-ai-assistant.d.ts +74 -0
  196. package/dist/services/workflow-ai-assistant.d.ts.map +1 -0
  197. package/dist/services/workflow-ai-assistant.js +175 -0
  198. package/dist/services/workflow-benchmark.d.ts +34 -0
  199. package/dist/services/workflow-benchmark.d.ts.map +1 -0
  200. package/dist/services/workflow-benchmark.js +318 -0
  201. package/dist/services/workflow.d.ts +107 -0
  202. package/dist/services/workflow.d.ts.map +1 -0
  203. package/dist/services/workflow.js +580 -0
  204. package/dist/test/hooks.test.d.ts +2 -0
  205. package/dist/test/hooks.test.d.ts.map +1 -0
  206. package/dist/test/hooks.test.js +67 -0
  207. package/dist/test/integration/callbacks.test.d.ts +2 -0
  208. package/dist/test/integration/callbacks.test.d.ts.map +1 -0
  209. package/dist/test/integration/callbacks.test.js +64 -0
  210. package/dist/test/lib/ai-service/task-operations.test.d.ts +2 -0
  211. package/dist/test/lib/ai-service/task-operations.test.d.ts.map +1 -0
  212. package/dist/test/lib/ai-service/task-operations.test.js +362 -0
  213. package/dist/test/lib/config.test.d.ts +2 -0
  214. package/dist/test/lib/config.test.d.ts.map +1 -0
  215. package/dist/test/lib/config.test.js +128 -0
  216. package/dist/test/lib/git-utils.test.d.ts +2 -0
  217. package/dist/test/lib/git-utils.test.d.ts.map +1 -0
  218. package/dist/test/lib/git-utils.test.js +168 -0
  219. package/dist/test/mocks/mock-ai-operations.d.ts +15 -0
  220. package/dist/test/mocks/mock-ai-operations.d.ts.map +1 -0
  221. package/dist/test/mocks/mock-ai-operations.js +107 -0
  222. package/dist/test/mocks/mock-context-builder.d.ts +10 -0
  223. package/dist/test/mocks/mock-context-builder.d.ts.map +1 -0
  224. package/dist/test/mocks/mock-context-builder.js +81 -0
  225. package/dist/test/mocks/mock-model-provider.d.ts +7 -0
  226. package/dist/test/mocks/mock-model-provider.d.ts.map +1 -0
  227. package/dist/test/mocks/mock-model-provider.js +21 -0
  228. package/dist/test/mocks/mock-service-factory.d.ts +11 -0
  229. package/dist/test/mocks/mock-service-factory.d.ts.map +1 -0
  230. package/dist/test/mocks/mock-service-factory.js +61 -0
  231. package/dist/test/mocks/mock-storage.d.ts +50 -0
  232. package/dist/test/mocks/mock-storage.d.ts.map +1 -0
  233. package/dist/test/mocks/mock-storage.js +145 -0
  234. package/dist/test/model-parsing.test.d.ts +2 -0
  235. package/dist/test/model-parsing.test.d.ts.map +1 -0
  236. package/dist/test/model-parsing.test.js +73 -0
  237. package/dist/test/services/task-service.test.d.ts +2 -0
  238. package/dist/test/services/task-service.test.d.ts.map +1 -0
  239. package/dist/test/services/task-service.test.js +459 -0
  240. package/dist/test/storage.test.d.ts +2 -0
  241. package/dist/test/storage.test.d.ts.map +1 -0
  242. package/dist/test/storage.test.js +207 -0
  243. package/dist/test/task-loop-git.test.d.ts +2 -0
  244. package/dist/test/task-loop-git.test.d.ts.map +1 -0
  245. package/dist/test/task-loop-git.test.js +95 -0
  246. package/dist/test/test-mock-setup.d.ts +26 -0
  247. package/dist/test/test-mock-setup.d.ts.map +1 -0
  248. package/dist/test/test-mock-setup.js +41 -0
  249. package/dist/test/test-setup.d.ts +9 -0
  250. package/dist/test/test-setup.d.ts.map +1 -0
  251. package/dist/test/test-setup.js +44 -0
  252. package/dist/test/test-utils.d.ts +22 -0
  253. package/dist/test/test-utils.d.ts.map +1 -0
  254. package/dist/test/test-utils.js +37 -0
  255. package/dist/test/utils/ai-operation-utility.test.d.ts +2 -0
  256. package/dist/test/utils/ai-operation-utility.test.d.ts.map +1 -0
  257. package/dist/test/utils/ai-operation-utility.test.js +290 -0
  258. package/dist/test/utils/error-handling.test.d.ts +2 -0
  259. package/dist/test/utils/error-handling.test.d.ts.map +1 -0
  260. package/dist/test/utils/error-handling.test.js +231 -0
  261. package/dist/test/utils/file-utils.test.d.ts +2 -0
  262. package/dist/test/utils/file-utils.test.d.ts.map +1 -0
  263. package/dist/test/utils/file-utils.test.js +76 -0
  264. package/dist/test/utils/id-generator.test.d.ts +2 -0
  265. package/dist/test/utils/id-generator.test.d.ts.map +1 -0
  266. package/dist/test/utils/id-generator.test.js +41 -0
  267. package/dist/test/utils/model-parser.test.d.ts +2 -0
  268. package/dist/test/utils/model-parser.test.d.ts.map +1 -0
  269. package/dist/test/utils/model-parser.test.js +65 -0
  270. package/dist/test/validation.test.d.ts +2 -0
  271. package/dist/test/validation.test.d.ts.map +1 -0
  272. package/dist/test/validation.test.js +22 -0
  273. package/dist/types/callbacks.d.ts +30 -0
  274. package/dist/types/callbacks.d.ts.map +1 -0
  275. package/dist/types/callbacks.js +2 -0
  276. package/dist/types/index.d.ts +435 -0
  277. package/dist/types/index.d.ts.map +1 -0
  278. package/dist/types/index.js +30 -0
  279. package/dist/types/mcp.d.ts +3 -0
  280. package/dist/types/mcp.d.ts.map +1 -0
  281. package/dist/types/mcp.js +3 -0
  282. package/dist/types/options.d.ts +112 -0
  283. package/dist/types/options.d.ts.map +1 -0
  284. package/dist/types/options.js +2 -0
  285. package/dist/types/results.d.ts +200 -0
  286. package/dist/types/results.d.ts.map +1 -0
  287. package/dist/types/results.js +2 -0
  288. package/dist/types/workflow-options.d.ts +82 -0
  289. package/dist/types/workflow-options.d.ts.map +1 -0
  290. package/dist/types/workflow-options.js +2 -0
  291. package/dist/types/workflow-results.d.ts +82 -0
  292. package/dist/types/workflow-results.d.ts.map +1 -0
  293. package/dist/types/workflow-results.js +2 -0
  294. package/dist/utils/ai-config-builder.d.ts +14 -0
  295. package/dist/utils/ai-config-builder.d.ts.map +1 -0
  296. package/dist/utils/ai-config-builder.js +22 -0
  297. package/dist/utils/ai-operation-utility.d.ts +142 -0
  298. package/dist/utils/ai-operation-utility.d.ts.map +1 -0
  299. package/dist/utils/ai-operation-utility.js +303 -0
  300. package/dist/utils/ai-service-factory.d.ts +34 -0
  301. package/dist/utils/ai-service-factory.d.ts.map +1 -0
  302. package/dist/utils/ai-service-factory.js +99 -0
  303. package/dist/utils/error-utils.d.ts +70 -0
  304. package/dist/utils/error-utils.d.ts.map +1 -0
  305. package/dist/utils/error-utils.js +104 -0
  306. package/dist/utils/file-utils.d.ts +107 -0
  307. package/dist/utils/file-utils.d.ts.map +1 -0
  308. package/dist/utils/file-utils.js +171 -0
  309. package/dist/utils/id-generator.d.ts +92 -0
  310. package/dist/utils/id-generator.d.ts.map +1 -0
  311. package/dist/utils/id-generator.js +146 -0
  312. package/dist/utils/metadata-utils.d.ts +40 -0
  313. package/dist/utils/metadata-utils.d.ts.map +1 -0
  314. package/dist/utils/metadata-utils.js +43 -0
  315. package/dist/utils/model-executor-parser.d.ts +38 -0
  316. package/dist/utils/model-executor-parser.d.ts.map +1 -0
  317. package/dist/utils/model-executor-parser.js +69 -0
  318. package/dist/utils/model-parser.d.ts +6 -0
  319. package/dist/utils/model-parser.d.ts.map +1 -0
  320. package/dist/utils/model-parser.js +49 -0
  321. package/dist/utils/stack-formatter.d.ts +12 -0
  322. package/dist/utils/stack-formatter.d.ts.map +1 -0
  323. package/dist/utils/stack-formatter.js +36 -0
  324. package/dist/utils/storage-utils.d.ts +49 -0
  325. package/dist/utils/storage-utils.d.ts.map +1 -0
  326. package/dist/utils/storage-utils.js +80 -0
  327. package/dist/utils/streaming-utils.d.ts +38 -0
  328. package/dist/utils/streaming-utils.d.ts.map +1 -0
  329. package/dist/utils/streaming-utils.js +64 -0
  330. package/dist/utils/task-o-matic-error.d.ts +206 -0
  331. package/dist/utils/task-o-matic-error.d.ts.map +1 -0
  332. package/dist/utils/task-o-matic-error.js +304 -0
  333. package/package.json +40 -0
  334. package/src/index.ts +36 -0
  335. package/src/lib/ai-service/ai-operations.ts +310 -0
  336. package/src/lib/ai-service/base-operations.ts +139 -0
  337. package/src/lib/ai-service/documentation-operations.ts +438 -0
  338. package/src/lib/ai-service/filesystem-tools.ts +73 -0
  339. package/src/lib/ai-service/gemini-proxy.ts.bak +52 -0
  340. package/src/lib/ai-service/json-parser.ts +203 -0
  341. package/src/lib/ai-service/mcp-client.ts +54 -0
  342. package/src/lib/ai-service/model-provider.ts +192 -0
  343. package/src/lib/ai-service/prd-operations.ts +854 -0
  344. package/src/lib/ai-service/research-tools.ts +207 -0
  345. package/src/lib/ai-service/retry-handler.ts +89 -0
  346. package/src/lib/ai-service/task-operations.ts +342 -0
  347. package/src/lib/benchmark/registry.ts +307 -0
  348. package/src/lib/benchmark/runner.ts +190 -0
  349. package/src/lib/benchmark/storage.ts +140 -0
  350. package/src/lib/benchmark/types.ts +121 -0
  351. package/src/lib/better-t-stack-cli.ts +524 -0
  352. package/src/lib/bootstrap/cli-bootstrap.ts +397 -0
  353. package/src/lib/bootstrap/index.ts +2 -0
  354. package/src/lib/bootstrap/medusa-bootstrap.ts +261 -0
  355. package/src/lib/config-validation.ts +278 -0
  356. package/src/lib/config.ts +435 -0
  357. package/src/lib/context-builder.ts +383 -0
  358. package/src/lib/executors/claude-code-executor.ts +83 -0
  359. package/src/lib/executors/codex-executor.ts +85 -0
  360. package/src/lib/executors/executor-factory.ts +28 -0
  361. package/src/lib/executors/gemini-executor.ts +80 -0
  362. package/src/lib/executors/kilo-executor.ts +83 -0
  363. package/src/lib/executors/opencode-executor.ts +81 -0
  364. package/src/lib/git-utils.ts +334 -0
  365. package/src/lib/hooks.ts +121 -0
  366. package/src/lib/index.ts +166 -0
  367. package/src/lib/logger.ts +43 -0
  368. package/src/lib/notifications.ts +103 -0
  369. package/src/lib/prompt-builder.ts +471 -0
  370. package/src/lib/prompt-registry.ts +491 -0
  371. package/src/lib/provider-defaults.json +32 -0
  372. package/src/lib/storage/file-system.ts +864 -0
  373. package/src/lib/storage/storage-callbacks.ts +120 -0
  374. package/src/lib/storage/types.ts +58 -0
  375. package/src/lib/task-execution-core.ts +591 -0
  376. package/src/lib/task-execution.ts +59 -0
  377. package/src/lib/task-loop-execution.ts +214 -0
  378. package/src/lib/task-planning.ts +157 -0
  379. package/src/lib/task-review.ts +138 -0
  380. package/src/lib/validation.ts +140 -0
  381. package/src/prompts/documentation-detection.ts +21 -0
  382. package/src/prompts/documentation-recap.ts +11 -0
  383. package/src/prompts/index.ts +14 -0
  384. package/src/prompts/prd-combination.ts +32 -0
  385. package/src/prompts/prd-generation.ts +46 -0
  386. package/src/prompts/prd-parsing.ts +170 -0
  387. package/src/prompts/prd-question-answer.ts +25 -0
  388. package/src/prompts/prd-question.ts +38 -0
  389. package/src/prompts/prd-rework.ts +79 -0
  390. package/src/prompts/prd-suggest-stack.ts +97 -0
  391. package/src/prompts/task-breakdown.ts +149 -0
  392. package/src/prompts/task-enhancement.ts +138 -0
  393. package/src/prompts/task-execution.ts +22 -0
  394. package/src/prompts/task-planning.ts +64 -0
  395. package/src/prompts/workflow-assistance.ts +151 -0
  396. package/src/prompts/workflow-prompts.ts +97 -0
  397. package/src/services/benchmark.ts +433 -0
  398. package/src/services/prd.ts +845 -0
  399. package/src/services/tasks.ts +1515 -0
  400. package/src/services/workflow-ai-assistant.ts +298 -0
  401. package/src/services/workflow-benchmark.ts +339 -0
  402. package/src/services/workflow.ts +779 -0
  403. package/src/test/hooks.test.ts +77 -0
  404. package/src/test/integration/callbacks.test.ts +39 -0
  405. package/src/test/lib/ai-service/task-operations.test.ts +430 -0
  406. package/src/test/lib/config.test.ts +150 -0
  407. package/src/test/lib/git-utils.test.ts +198 -0
  408. package/src/test/mocks/mock-ai-operations.ts +205 -0
  409. package/src/test/mocks/mock-context-builder.ts +84 -0
  410. package/src/test/mocks/mock-model-provider.ts +21 -0
  411. package/src/test/mocks/mock-service-factory.ts +64 -0
  412. package/src/test/mocks/mock-storage.ts +204 -0
  413. package/src/test/model-parsing.test.ts +78 -0
  414. package/src/test/services/task-service.test.ts +551 -0
  415. package/src/test/storage.test.ts +206 -0
  416. package/src/test/task-loop-git.test.ts +142 -0
  417. package/src/test/test-mock-setup.ts +46 -0
  418. package/src/test/test-setup.ts +48 -0
  419. package/src/test/test-utils.ts +45 -0
  420. package/src/test/utils/ai-operation-utility.test.ts +306 -0
  421. package/src/test/utils/error-handling.test.ts +241 -0
  422. package/src/test/utils/file-utils.test.ts +80 -0
  423. package/src/test/utils/id-generator.test.ts +44 -0
  424. package/src/test/utils/model-parser.test.ts +67 -0
  425. package/src/test/validation.test.ts +19 -0
  426. package/src/types/callbacks.ts +14 -0
  427. package/src/types/index.ts +628 -0
  428. package/src/types/mcp.ts +5 -0
  429. package/src/types/options.ts +165 -0
  430. package/src/types/results.ts +216 -0
  431. package/src/types/workflow-options.ts +113 -0
  432. package/src/types/workflow-results.ts +87 -0
  433. package/src/utils/ai-config-builder.ts +33 -0
  434. package/src/utils/ai-operation-utility.ts +380 -0
  435. package/src/utils/ai-service-factory.ts +125 -0
  436. package/src/utils/error-utils.ts +124 -0
  437. package/src/utils/file-utils.ts +197 -0
  438. package/src/utils/id-generator.ts +168 -0
  439. package/src/utils/metadata-utils.ts +48 -0
  440. package/src/utils/model-executor-parser.ts +80 -0
  441. package/src/utils/model-parser.ts +58 -0
  442. package/src/utils/stack-formatter.ts +53 -0
  443. package/src/utils/storage-utils.ts +94 -0
  444. package/src/utils/streaming-utils.ts +91 -0
  445. package/src/utils/task-o-matic-error.ts +393 -0
  446. package/tsconfig.json +20 -0
  447. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,845 @@
1
+ import {
2
+ createStandardError,
3
+ TaskOMaticErrorCodes,
4
+ } from "../utils/task-o-matic-error";
5
+ import {
6
+ readFileSync,
7
+ existsSync,
8
+ copyFileSync,
9
+ writeFileSync,
10
+ mkdirSync,
11
+ } from "fs";
12
+ import { join, basename, relative } from "path";
13
+ import { getAIOperations, getStorage } from "../utils/ai-service-factory";
14
+ import { buildAIConfig, AIOptions } from "../utils/ai-config-builder";
15
+ import { AIConfig, StreamingOptions } from "../types";
16
+ import { PRDParseResult, SuggestStackResult } from "../types/results";
17
+ import { configManager, setupWorkingDirectory } from "../lib/config";
18
+ import { isValidAIProvider } from "../lib/validation";
19
+ import { ProgressCallback } from "../types/callbacks";
20
+ import { createMetricsStreamingOptions } from "../utils/streaming-utils";
21
+ import {
22
+ validateFileExists,
23
+ savePRDFile,
24
+ saveStackFile,
25
+ } from "../utils/file-utils";
26
+
27
+ /**
28
+ * Dependencies for PRDService
29
+ */
30
+ export interface PRDServiceDependencies {
31
+ storage?: ReturnType<typeof getStorage>;
32
+ aiOperations?: ReturnType<typeof getAIOperations>;
33
+ }
34
+
35
+ /**
36
+ * PRDService - Business logic for PRD operations
37
+ * Handles PRD parsing, task extraction, and PRD improvement
38
+ */
39
+ export class PRDService {
40
+ private storage: ReturnType<typeof getStorage>;
41
+ private aiOperations: ReturnType<typeof getAIOperations>;
42
+
43
+ /**
44
+ * Create a new PRDService
45
+ *
46
+ * @param dependencies - Optional dependencies to inject (for testing)
47
+ */
48
+ constructor(dependencies: PRDServiceDependencies = {}) {
49
+ // Use injected dependencies or fall back to singletons
50
+ this.storage = dependencies.storage ?? getStorage();
51
+ this.aiOperations = dependencies.aiOperations ?? getAIOperations();
52
+ }
53
+ async parsePRD(input: {
54
+ file: string;
55
+ workingDirectory?: string; // Working directory passed from CLI layer
56
+ enableFilesystemTools?: boolean;
57
+ aiOptions?: AIOptions;
58
+ promptOverride?: string;
59
+ messageOverride?: string;
60
+ streamingOptions?: StreamingOptions;
61
+ callbacks?: ProgressCallback;
62
+ }): Promise<PRDParseResult> {
63
+ const startTime = Date.now();
64
+ const steps: Array<{
65
+ step: string;
66
+ status: "completed" | "failed";
67
+ duration: number;
68
+ details?: any;
69
+ }> = [];
70
+
71
+ input.callbacks?.onProgress?.({
72
+ type: "started",
73
+ message: "Starting PRD parsing...",
74
+ });
75
+
76
+ // Validate file exists (DRY fix 1.2)
77
+ validateFileExists(input.file, `PRD file not found: ${input.file}`);
78
+
79
+ // Ensure we're in a task-o-matic project
80
+ const taskOMaticDir = configManager.getTaskOMaticDir();
81
+ if (!existsSync(taskOMaticDir)) {
82
+ throw createStandardError(
83
+ TaskOMaticErrorCodes.CONFIGURATION_ERROR,
84
+ "Not a task-o-matic project. Run 'task-o-matic init init' first.",
85
+ {
86
+ suggestions: ["Run `task-o-matic init init` in your project root."],
87
+ }
88
+ );
89
+ }
90
+
91
+ // Set working directory and reload config (DRY fix 1.4)
92
+ const workingDir = input.workingDirectory || process.cwd();
93
+ await setupWorkingDirectory(workingDir);
94
+
95
+ input.callbacks?.onProgress?.({
96
+ type: "progress",
97
+ message: "Reading PRD file...",
98
+ });
99
+
100
+ const prdContent = readFileSync(input.file, "utf-8");
101
+
102
+ // Save PRD file to .task-o-matic/prd directory
103
+ input.callbacks?.onProgress?.({
104
+ type: "progress",
105
+ message: "Saving PRD to project directory...",
106
+ });
107
+
108
+ const stepStart1 = Date.now();
109
+ const prdDir = join(taskOMaticDir, "prd");
110
+ const prdFileName = basename(input.file);
111
+ const savedPrdPath = join(prdDir, prdFileName);
112
+
113
+ // Ensure PRD directory exists
114
+ if (!existsSync(prdDir)) {
115
+ mkdirSync(prdDir, { recursive: true });
116
+ }
117
+
118
+ // Copy PRD file to project directory
119
+ copyFileSync(input.file, savedPrdPath);
120
+
121
+ // Get relative path from .task-o-matic directory for storage
122
+ const relativePrdPath = relative(taskOMaticDir, savedPrdPath);
123
+
124
+ steps.push({
125
+ step: "Save PRD File",
126
+ status: "completed",
127
+ duration: Date.now() - stepStart1,
128
+ });
129
+
130
+ // Validate AI provider if specified
131
+ if (
132
+ input.aiOptions?.aiProvider &&
133
+ !isValidAIProvider(input.aiOptions.aiProvider)
134
+ ) {
135
+ throw createStandardError(
136
+ TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
137
+ `Invalid AI provider: ${input.aiOptions.aiProvider}`,
138
+ {
139
+ suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
140
+ }
141
+ );
142
+ }
143
+
144
+ const aiConfig = buildAIConfig(input.aiOptions);
145
+
146
+ input.callbacks?.onProgress?.({
147
+ type: "progress",
148
+ message: "Parsing PRD with AI...",
149
+ });
150
+
151
+ const stepStart2 = Date.now();
152
+
153
+ // Use utility to wrap streaming options and capture metrics (DRY fix 1.1)
154
+ const { options: metricsStreamingOptions, getMetrics } =
155
+ createMetricsStreamingOptions(input.streamingOptions, stepStart2);
156
+
157
+ const result = await this.aiOperations.parsePRD(
158
+ prdContent,
159
+ aiConfig,
160
+ input.promptOverride,
161
+ input.messageOverride,
162
+ metricsStreamingOptions,
163
+ undefined, // retryConfig
164
+ workingDir, // Pass working directory to AI operations
165
+ input.enableFilesystemTools
166
+ );
167
+
168
+ // Extract metrics after AI call
169
+ const { tokenUsage, timeToFirstToken } = getMetrics();
170
+
171
+ steps.push({
172
+ step: "AI Parsing",
173
+ status: "completed",
174
+ duration: Date.now() - stepStart2,
175
+ details: { tasksFound: result.tasks.length },
176
+ });
177
+
178
+ input.callbacks?.onProgress?.({
179
+ type: "progress",
180
+ message: `Creating ${result.tasks.length} tasks...`,
181
+ });
182
+
183
+ // Create tasks
184
+ const stepStart3 = Date.now();
185
+ const createdTasks = [];
186
+
187
+ for (let i = 0; i < result.tasks.length; i++) {
188
+ const task = result.tasks[i];
189
+
190
+ input.callbacks?.onProgress?.({
191
+ type: "progress",
192
+ message: `Creating task ${i + 1}/${result.tasks.length}: ${task.title}`,
193
+ current: i + 1,
194
+ total: result.tasks.length,
195
+ });
196
+
197
+ const createdTask = await this.storage.createTask({
198
+ id: task.id, // Preserve AI-generated ID for dependencies
199
+ title: task.title,
200
+ description: task.description,
201
+ content: task.content,
202
+ estimatedEffort: task.estimatedEffort,
203
+ status: "todo",
204
+ dependencies: task.dependencies,
205
+ tags: task.tags,
206
+ prdFile: relativePrdPath, // Reference to the PRD file
207
+ });
208
+
209
+ createdTasks.push(createdTask);
210
+
211
+ // Update AI metadata with the actual task ID
212
+ const aiMetadata = {
213
+ taskId: createdTask.id,
214
+ aiGenerated: true,
215
+ aiPrompt: input.promptOverride || "Parse PRD and extract tasks",
216
+ confidence: result.confidence,
217
+ aiProvider: input.aiOptions?.aiProvider,
218
+ aiModel: input.aiOptions?.aiModel,
219
+ generatedAt: Date.now(),
220
+ };
221
+
222
+ await this.storage.saveTaskAIMetadata(aiMetadata);
223
+ }
224
+
225
+ steps.push({
226
+ step: "Create Tasks",
227
+ status: "completed",
228
+ duration: Date.now() - stepStart3,
229
+ details: { count: createdTasks.length },
230
+ });
231
+
232
+ input.callbacks?.onProgress?.({
233
+ type: "completed",
234
+ message: `Successfully created ${createdTasks.length} tasks from PRD`,
235
+ });
236
+
237
+ const duration = Date.now() - startTime;
238
+
239
+ // Calculate cost if token usage is available
240
+ let cost: number | undefined;
241
+ if (tokenUsage) {
242
+ // Cost calculation would depend on the model
243
+ // For now, we'll leave it undefined and can add pricing later
244
+ // This matches the benchmark pattern where cost calculation is done elsewhere
245
+ }
246
+
247
+ return {
248
+ success: true,
249
+ prd: {
250
+ overview: result.summary || "",
251
+ objectives: [],
252
+ features: [],
253
+ },
254
+ tasks: createdTasks,
255
+ stats: {
256
+ tasksCreated: createdTasks.length,
257
+ duration,
258
+ aiProvider: input.aiOptions?.aiProvider || "default",
259
+ aiModel: input.aiOptions?.aiModel || "default",
260
+ tokenUsage,
261
+ timeToFirstToken,
262
+ cost,
263
+ },
264
+ steps,
265
+ };
266
+ }
267
+
268
+ async generateQuestions(input: {
269
+ file: string;
270
+ workingDirectory?: string;
271
+ enableFilesystemTools?: boolean;
272
+ aiOptions?: AIOptions;
273
+ promptOverride?: string;
274
+ messageOverride?: string;
275
+ streamingOptions?: StreamingOptions;
276
+ callbacks?: ProgressCallback;
277
+ }): Promise<string[]> {
278
+ input.callbacks?.onProgress?.({
279
+ type: "started",
280
+ message: "Generating clarifying questions...",
281
+ });
282
+
283
+ // Validate file exists (DRY fix 1.2)
284
+ validateFileExists(input.file, `PRD file not found: ${input.file}`);
285
+
286
+ // Set working directory and reload config (DRY fix 1.4)
287
+ const workingDir = input.workingDirectory || process.cwd();
288
+ await setupWorkingDirectory(workingDir);
289
+
290
+ input.callbacks?.onProgress?.({
291
+ type: "progress",
292
+ message: "Reading PRD file...",
293
+ });
294
+
295
+ const prdContent = readFileSync(input.file, "utf-8");
296
+
297
+ if (
298
+ input.aiOptions?.aiProvider &&
299
+ !isValidAIProvider(input.aiOptions.aiProvider)
300
+ ) {
301
+ throw createStandardError(
302
+ TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
303
+ `Invalid AI provider: ${input.aiOptions.aiProvider}`,
304
+ {
305
+ suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
306
+ }
307
+ );
308
+ }
309
+
310
+ const aiConfig = buildAIConfig(input.aiOptions);
311
+
312
+ input.callbacks?.onProgress?.({
313
+ type: "progress",
314
+ message: "Analyzing PRD with AI...",
315
+ });
316
+
317
+ const questions = await this.aiOperations.generatePRDQuestions(
318
+ prdContent,
319
+ aiConfig,
320
+ input.promptOverride,
321
+ input.messageOverride,
322
+ input.streamingOptions,
323
+ undefined,
324
+ workingDir,
325
+ input.enableFilesystemTools
326
+ );
327
+
328
+ input.callbacks?.onProgress?.({
329
+ type: "completed",
330
+ message: `Generated ${questions.length} questions`,
331
+ });
332
+
333
+ return questions;
334
+ }
335
+
336
+ async reworkPRD(input: {
337
+ file: string;
338
+ feedback: string;
339
+ output?: string;
340
+ workingDirectory?: string; // Working directory passed from CLI layer
341
+ enableFilesystemTools?: boolean;
342
+ aiOptions?: AIOptions;
343
+ promptOverride?: string;
344
+ messageOverride?: string;
345
+ streamingOptions?: StreamingOptions;
346
+ callbacks?: ProgressCallback;
347
+ }): Promise<string> {
348
+ input.callbacks?.onProgress?.({
349
+ type: "started",
350
+ message: "Starting PRD improvement...",
351
+ });
352
+
353
+ // Validate file exists (DRY fix 1.2)
354
+ validateFileExists(input.file, `PRD file not found: ${input.file}`);
355
+
356
+ // Set working directory and reload config (DRY fix 1.4)
357
+ const workingDir = input.workingDirectory || process.cwd();
358
+ await setupWorkingDirectory(workingDir);
359
+
360
+ input.callbacks?.onProgress?.({
361
+ type: "progress",
362
+ message: "Reading PRD file...",
363
+ });
364
+
365
+ const prdContent = readFileSync(input.file, "utf-8");
366
+
367
+ // Validate AI provider if specified
368
+ if (
369
+ input.aiOptions?.aiProvider &&
370
+ !isValidAIProvider(input.aiOptions.aiProvider)
371
+ ) {
372
+ throw createStandardError(
373
+ TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
374
+ `Invalid AI provider: ${input.aiOptions.aiProvider}`,
375
+ {
376
+ suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
377
+ }
378
+ );
379
+ }
380
+
381
+ const aiConfig = buildAIConfig(input.aiOptions);
382
+
383
+ input.callbacks?.onProgress?.({
384
+ type: "progress",
385
+ message: "Calling AI to improve PRD...",
386
+ });
387
+
388
+ const improvedPRD = await this.aiOperations.reworkPRD(
389
+ prdContent,
390
+ input.feedback,
391
+ aiConfig,
392
+ input.promptOverride,
393
+ input.messageOverride,
394
+ input.streamingOptions,
395
+ undefined, // retryConfig
396
+ workingDir, // Pass working directory to AI operations
397
+ input.enableFilesystemTools
398
+ );
399
+
400
+ input.callbacks?.onProgress?.({
401
+ type: "progress",
402
+ message: "Saving improved PRD...",
403
+ });
404
+
405
+ const outputPath = input.output || input.file;
406
+ writeFileSync(outputPath, improvedPRD);
407
+
408
+ input.callbacks?.onProgress?.({
409
+ type: "completed",
410
+ message: `PRD improved and saved to ${outputPath}`,
411
+ });
412
+
413
+ return outputPath;
414
+ }
415
+
416
+ async refinePRDWithQuestions(input: {
417
+ file: string;
418
+ questionMode: "user" | "ai";
419
+ answers?: Record<string, string>; // Pre-provided answers (user mode)
420
+ questionAIOptions?: AIOptions; // Optional override for answering (defaults to main AI)
421
+ workingDirectory?: string;
422
+ enableFilesystemTools?: boolean;
423
+ aiOptions?: AIOptions; // Main AI config
424
+ streamingOptions?: StreamingOptions;
425
+ callbacks?: ProgressCallback;
426
+ }): Promise<{
427
+ questions: string[];
428
+ answers: Record<string, string>;
429
+ refinedPRDPath: string;
430
+ }> {
431
+ input.callbacks?.onProgress?.({
432
+ type: "started",
433
+ message: "Starting PRD question/refine process...",
434
+ });
435
+
436
+ // Step 1: Generate questions
437
+ input.callbacks?.onProgress?.({
438
+ type: "progress",
439
+ message: "Generating clarifying questions...",
440
+ });
441
+
442
+ const questions = await this.generateQuestions({
443
+ file: input.file,
444
+ workingDirectory: input.workingDirectory,
445
+ enableFilesystemTools: input.enableFilesystemTools,
446
+ aiOptions: input.aiOptions,
447
+ streamingOptions: input.streamingOptions,
448
+ callbacks: input.callbacks,
449
+ });
450
+
451
+ if (questions.length === 0) {
452
+ input.callbacks?.onProgress?.({
453
+ type: "completed",
454
+ message: "No questions generated - PRD appears complete",
455
+ });
456
+
457
+ return {
458
+ questions: [],
459
+ answers: {},
460
+ refinedPRDPath: input.file,
461
+ };
462
+ }
463
+
464
+ // Step 2: Get stack info for context
465
+ const workingDir = input.workingDirectory || process.cwd();
466
+ const PromptBuilder = (await import("../lib/prompt-builder")).PromptBuilder;
467
+ let stackInfo = "";
468
+ try {
469
+ stackInfo = await PromptBuilder.detectStackInfo(workingDir);
470
+ if (stackInfo === "Not detected") {
471
+ stackInfo = "";
472
+ }
473
+ } catch (error) {
474
+ // Stack info not available
475
+ }
476
+
477
+ // Step 3: Get answers
478
+ let answers: Record<string, string>;
479
+
480
+ if (input.questionMode === "user") {
481
+ // User mode: return questions for CLI to prompt user
482
+ // Answers should be provided in input.answers
483
+ if (!input.answers || Object.keys(input.answers).length === 0) {
484
+ throw createStandardError(
485
+ TaskOMaticErrorCodes.INVALID_INPUT,
486
+ "User mode selected but no answers provided. CLI layer should collect answers."
487
+ );
488
+ }
489
+ answers = input.answers;
490
+ } else {
491
+ // AI mode: use AI to answer questions with context
492
+ input.callbacks?.onProgress?.({
493
+ type: "progress",
494
+ message: "AI is answering questions...",
495
+ });
496
+
497
+ const prdContent = readFileSync(input.file, "utf-8");
498
+
499
+ // Use questionAIOptions if provided, otherwise use main aiOptions
500
+ const answeringAIConfig = buildAIConfig(
501
+ input.questionAIOptions || input.aiOptions
502
+ );
503
+
504
+ answers = await this.aiOperations.answerPRDQuestions(
505
+ prdContent,
506
+ questions,
507
+ answeringAIConfig,
508
+ {
509
+ stackInfo,
510
+ },
511
+ input.streamingOptions
512
+ );
513
+ }
514
+
515
+ // Step 4: Format questions + answers as structured feedback
516
+ let feedback =
517
+ "Please incorporate the following clarifications into the PRD:\n\n";
518
+ questions.forEach((q, i) => {
519
+ feedback += `Q${i + 1}: ${q}\nA: ${
520
+ answers[q] || "No answer provided"
521
+ }\n\n`;
522
+ });
523
+
524
+ // Step 5: Automatically call reworkPRD with formatted feedback
525
+ input.callbacks?.onProgress?.({
526
+ type: "progress",
527
+ message: "Refining PRD with answers...",
528
+ });
529
+
530
+ const refinedPRDPath = await this.reworkPRD({
531
+ file: input.file,
532
+ feedback,
533
+ workingDirectory: input.workingDirectory,
534
+ enableFilesystemTools: input.enableFilesystemTools,
535
+ aiOptions: input.aiOptions,
536
+ streamingOptions: input.streamingOptions,
537
+ callbacks: input.callbacks,
538
+ });
539
+
540
+ input.callbacks?.onProgress?.({
541
+ type: "completed",
542
+ message: `PRD refined with ${questions.length} questions answered`,
543
+ });
544
+
545
+ return {
546
+ questions,
547
+ answers,
548
+ refinedPRDPath,
549
+ };
550
+ }
551
+
552
+ async generatePRD(input: {
553
+ description: string;
554
+ outputDir?: string;
555
+ filename?: string;
556
+ aiOptions?: AIOptions;
557
+ streamingOptions?: StreamingOptions;
558
+ callbacks?: ProgressCallback;
559
+ }): Promise<{
560
+ path: string;
561
+ content: string;
562
+ stats: {
563
+ duration: number;
564
+ tokenUsage?: { prompt: number; completion: number; total: number };
565
+ timeToFirstToken?: number;
566
+ cost?: number;
567
+ };
568
+ }> {
569
+ const startTime = Date.now();
570
+
571
+ input.callbacks?.onProgress?.({
572
+ type: "started",
573
+ message: "Generating PRD...",
574
+ });
575
+
576
+ // Use utility to wrap streaming options and capture metrics
577
+ const { options: metricsStreamingOptions, getMetrics } =
578
+ createMetricsStreamingOptions(input.streamingOptions, startTime);
579
+
580
+ const aiConfig = buildAIConfig(input.aiOptions);
581
+
582
+ const content = await this.aiOperations.generatePRD(
583
+ input.description,
584
+ aiConfig,
585
+ undefined,
586
+ undefined,
587
+ metricsStreamingOptions
588
+ );
589
+
590
+ // Get metrics after AI operation
591
+ const { tokenUsage, timeToFirstToken } = getMetrics();
592
+
593
+ // Calculate cost if needed
594
+ let cost: number | undefined;
595
+ if (tokenUsage && tokenUsage.total > 0) {
596
+ cost = tokenUsage.total * 0.000001; // Placeholder cost calculation
597
+ }
598
+
599
+ // Save file using utility
600
+ const path = savePRDFile(content, input.filename, input.outputDir);
601
+
602
+ input.callbacks?.onProgress?.({
603
+ type: "completed",
604
+ message: `PRD generated and saved to ${path}`,
605
+ });
606
+
607
+ return {
608
+ path,
609
+ content,
610
+ stats: {
611
+ duration: Date.now() - startTime,
612
+ tokenUsage,
613
+ timeToFirstToken,
614
+ cost,
615
+ },
616
+ };
617
+ }
618
+
619
+ async combinePRDs(input: {
620
+ prds: string[];
621
+ originalDescription: string;
622
+ outputDir?: string;
623
+ filename?: string;
624
+ aiOptions?: AIOptions;
625
+ streamingOptions?: StreamingOptions;
626
+ callbacks?: ProgressCallback;
627
+ }): Promise<{
628
+ path: string;
629
+ content: string;
630
+ stats: {
631
+ duration: number;
632
+ tokenUsage?: { prompt: number; completion: number; total: number };
633
+ timeToFirstToken?: number;
634
+ cost?: number;
635
+ };
636
+ }> {
637
+ const startTime = Date.now();
638
+
639
+ input.callbacks?.onProgress?.({
640
+ type: "started",
641
+ message: "Combining PRDs...",
642
+ });
643
+
644
+ // Use utility to wrap streaming options and capture metrics
645
+ const { options: metricsStreamingOptions, getMetrics } =
646
+ createMetricsStreamingOptions(input.streamingOptions, startTime);
647
+
648
+ const aiConfig = buildAIConfig(input.aiOptions);
649
+
650
+ const content = await this.aiOperations.combinePRDs(
651
+ input.prds,
652
+ input.originalDescription,
653
+ aiConfig,
654
+ undefined,
655
+ undefined,
656
+ metricsStreamingOptions
657
+ );
658
+
659
+ // Get metrics after AI operation
660
+ const { tokenUsage, timeToFirstToken } = getMetrics();
661
+
662
+ // Calculate cost if needed
663
+ let cost: number | undefined;
664
+ if (tokenUsage && tokenUsage.total > 0) {
665
+ cost = tokenUsage.total * 0.000001;
666
+ }
667
+
668
+ // Save file using utility (defaults to "prd.md" if no filename, so we provide the default for combinePRDs)
669
+ const path = savePRDFile(
670
+ content,
671
+ input.filename || "prd-master.md",
672
+ input.outputDir
673
+ );
674
+
675
+ input.callbacks?.onProgress?.({
676
+ type: "completed",
677
+ message: `Master PRD saved to ${path}`,
678
+ });
679
+
680
+ return {
681
+ path,
682
+ content,
683
+ stats: {
684
+ duration: Date.now() - startTime,
685
+ tokenUsage,
686
+ timeToFirstToken,
687
+ cost,
688
+ },
689
+ };
690
+ }
691
+
692
+ /**
693
+ * Suggest a technology stack based on PRD analysis.
694
+ */
695
+ async suggestStack(input: {
696
+ file?: string;
697
+ content?: string;
698
+ projectName?: string;
699
+ output?: string;
700
+ workingDirectory?: string;
701
+ enableFilesystemTools?: boolean;
702
+ save?: boolean;
703
+ aiOptions?: AIOptions;
704
+ promptOverride?: string;
705
+ messageOverride?: string;
706
+ streamingOptions?: StreamingOptions;
707
+ callbacks?: ProgressCallback;
708
+ }): Promise<SuggestStackResult> {
709
+ const startTime = Date.now();
710
+
711
+ input.callbacks?.onProgress?.({
712
+ type: "started",
713
+ message: "Analyzing PRD for stack suggestion...",
714
+ });
715
+
716
+ // Validate mutual exclusivity
717
+ if (input.file && input.content) {
718
+ throw createStandardError(
719
+ TaskOMaticErrorCodes.INVALID_INPUT,
720
+ "Cannot specify both --file and --content",
721
+ {
722
+ suggestions: ["Use either --file OR --content, not both."],
723
+ }
724
+ );
725
+ }
726
+
727
+ if (!input.file && !input.content) {
728
+ throw createStandardError(
729
+ TaskOMaticErrorCodes.INVALID_INPUT,
730
+ "Must specify either --file or --content",
731
+ {
732
+ suggestions: [
733
+ "Provide a PRD file with --file or content with --content.",
734
+ ],
735
+ }
736
+ );
737
+ }
738
+
739
+ // Get PRD content
740
+ let prdContent: string;
741
+ if (input.file) {
742
+ validateFileExists(input.file, `PRD file not found: ${input.file}`);
743
+ prdContent = readFileSync(input.file, "utf-8");
744
+ } else {
745
+ prdContent = input.content!;
746
+ }
747
+
748
+ // Set working directory
749
+ const workingDir = input.workingDirectory || process.cwd();
750
+ await setupWorkingDirectory(workingDir);
751
+
752
+ // Validate AI provider if specified
753
+ if (
754
+ input.aiOptions?.aiProvider &&
755
+ !isValidAIProvider(input.aiOptions.aiProvider)
756
+ ) {
757
+ throw createStandardError(
758
+ TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
759
+ `Invalid AI provider: ${input.aiOptions.aiProvider}`,
760
+ {
761
+ suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
762
+ }
763
+ );
764
+ }
765
+
766
+ const aiConfig = buildAIConfig(input.aiOptions);
767
+
768
+ input.callbacks?.onProgress?.({
769
+ type: "progress",
770
+ message: "Calling AI to analyze PRD...",
771
+ });
772
+
773
+ // Use utility to wrap streaming options and capture metrics
774
+ const { options: metricsStreamingOptions, getMetrics } =
775
+ createMetricsStreamingOptions(input.streamingOptions, startTime);
776
+
777
+ const result = await this.aiOperations.suggestStack(
778
+ prdContent,
779
+ input.projectName,
780
+ aiConfig,
781
+ input.promptOverride,
782
+ input.messageOverride,
783
+ metricsStreamingOptions,
784
+ undefined, // retryConfig
785
+ workingDir,
786
+ input.enableFilesystemTools
787
+ );
788
+
789
+ // Get metrics after AI operation
790
+ const { tokenUsage, timeToFirstToken } = getMetrics();
791
+
792
+ // Calculate cost if needed
793
+ let cost: number | undefined;
794
+ if (tokenUsage && tokenUsage.total > 0) {
795
+ cost = tokenUsage.total * 0.000001; // Placeholder cost calculation
796
+ }
797
+
798
+ // Save if requested
799
+ let savedPath: string | undefined;
800
+ if (input.save || input.output) {
801
+ input.callbacks?.onProgress?.({
802
+ type: "progress",
803
+ message: "Saving stack configuration...",
804
+ });
805
+ savedPath = saveStackFile(result.config, input.output);
806
+ }
807
+
808
+ input.callbacks?.onProgress?.({
809
+ type: "completed",
810
+ message: savedPath
811
+ ? `Stack suggestion saved to ${savedPath}`
812
+ : "Stack suggestion complete",
813
+ });
814
+
815
+ return {
816
+ success: true,
817
+ stack: result.config,
818
+ reasoning: result.reasoning,
819
+ savedPath,
820
+ stats: {
821
+ duration: Date.now() - startTime,
822
+ tokenUsage,
823
+ timeToFirstToken,
824
+ cost,
825
+ },
826
+ };
827
+ }
828
+ }
829
+
830
+ // Lazy singleton instance - only created when first accessed
831
+ let prdServiceInstance: PRDService | undefined;
832
+
833
+ export function getPRDService(): PRDService {
834
+ if (!prdServiceInstance) {
835
+ prdServiceInstance = new PRDService();
836
+ }
837
+ return prdServiceInstance;
838
+ }
839
+
840
+ // Backward compatibility: export as const but use getter
841
+ export const prdService = new Proxy({} as PRDService, {
842
+ get(target, prop) {
843
+ return (getPRDService() as any)[prop];
844
+ },
845
+ });