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,278 @@
1
+ import { z } from "zod";
2
+ import {
3
+ TaskOMaticError,
4
+ TaskOMaticErrorCodes,
5
+ } from "../utils/task-o-matic-error";
6
+
7
+ /**
8
+ * Zod schema for AI Provider
9
+ */
10
+ import { AI_PROVIDERS_LIST } from "../types";
11
+
12
+ /**
13
+ * Zod schema for AI Provider
14
+ */
15
+ export const AIProviderSchema = z.enum(AI_PROVIDERS_LIST, {
16
+ errorMap: () => ({
17
+ message: `Provider must be one of: ${AI_PROVIDERS_LIST.join(", ")}`,
18
+ }),
19
+ });
20
+
21
+ /**
22
+ * Zod schema for AI Configuration
23
+ */
24
+ export const AIConfigSchema = z.object({
25
+ provider: AIProviderSchema,
26
+ model: z.string().min(1, "Model name cannot be empty"),
27
+ apiKey: z.string().optional(),
28
+ baseURL: z
29
+ .string()
30
+ .url("Base URL must be a valid URL")
31
+ .optional()
32
+ .or(z.literal("")),
33
+ maxTokens: z
34
+ .number()
35
+ .int("Max tokens must be an integer")
36
+ .positive("Max tokens must be positive")
37
+ .max(1000000, "Max tokens cannot exceed 1,000,000")
38
+ .optional(),
39
+ temperature: z
40
+ .number()
41
+ .min(0, "Temperature must be between 0 and 2")
42
+ .max(2, "Temperature must be between 0 and 2")
43
+ .optional(),
44
+ context7Enabled: z.boolean().optional(),
45
+ reasoning: z
46
+ .object({
47
+ maxTokens: z
48
+ .number()
49
+ .int("Reasoning max tokens must be an integer")
50
+ .positive("Reasoning max tokens must be positive")
51
+ .max(100000, "Reasoning max tokens cannot exceed 100,000")
52
+ .optional(),
53
+ })
54
+ .optional(),
55
+ });
56
+
57
+ /**
58
+ * Zod schema for full Config object
59
+ */
60
+ export const ConfigSchema = z.object({
61
+ ai: AIConfigSchema,
62
+ workingDirectory: z.string().optional(),
63
+ });
64
+
65
+ /**
66
+ * Validation result type
67
+ */
68
+ export interface ValidationResult<T> {
69
+ success: boolean;
70
+ data?: T;
71
+ errors?: Array<{
72
+ path: string;
73
+ message: string;
74
+ }>;
75
+ }
76
+
77
+ /**
78
+ * Validate AI configuration
79
+ *
80
+ * @param config - AI configuration to validate
81
+ * @returns Validated and typed configuration
82
+ * @throws {TaskOMaticError} If validation fails
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const aiConfig = {
87
+ * provider: "openai",
88
+ * model: "gpt-4",
89
+ * temperature: 0.7,
90
+ * maxTokens: 4000
91
+ * };
92
+ *
93
+ * const validated = validateAIConfig(aiConfig);
94
+ * // validated is now type-safe and guaranteed valid
95
+ * ```
96
+ */
97
+ export function validateAIConfig(
98
+ config: unknown
99
+ ): z.infer<typeof AIConfigSchema> {
100
+ try {
101
+ return AIConfigSchema.parse(config);
102
+ } catch (error) {
103
+ if (error instanceof z.ZodError) {
104
+ const errors = error.errors.map((err) => ({
105
+ path: err.path.join("."),
106
+ message: err.message,
107
+ }));
108
+
109
+ throw new TaskOMaticError("AI configuration validation failed", {
110
+ code: TaskOMaticErrorCodes.CONFIGURATION_ERROR,
111
+ context: JSON.stringify(config, null, 2),
112
+ suggestions: [
113
+ "Check that all required fields are present",
114
+ "Verify provider is one of: openai, anthropic, openrouter, custom",
115
+ "Ensure model name is not empty",
116
+ "Check temperature is between 0 and 2",
117
+ "Verify maxTokens is a positive integer",
118
+ ...errors.map((e) => `Fix ${e.path}: ${e.message}`),
119
+ ],
120
+ metadata: { errors },
121
+ });
122
+ }
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Validate full configuration object
129
+ *
130
+ * @param config - Configuration object to validate
131
+ * @returns Validated and typed configuration
132
+ * @throws {TaskOMaticError} If validation fails
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const config = {
137
+ * ai: {
138
+ * provider: "anthropic",
139
+ * model: "claude-sonnet-4.5",
140
+ * temperature: 0.5
141
+ * },
142
+ * workingDirectory: "/path/to/project"
143
+ * };
144
+ *
145
+ * const validated = validateConfig(config);
146
+ * ```
147
+ */
148
+ export function validateConfig(config: unknown): z.infer<typeof ConfigSchema> {
149
+ try {
150
+ return ConfigSchema.parse(config);
151
+ } catch (error) {
152
+ if (error instanceof z.ZodError) {
153
+ const errors = error.errors.map((err) => ({
154
+ path: err.path.join("."),
155
+ message: err.message,
156
+ }));
157
+
158
+ throw new TaskOMaticError("Configuration validation failed", {
159
+ code: TaskOMaticErrorCodes.CONFIGURATION_ERROR,
160
+ context: JSON.stringify(config, null, 2),
161
+ suggestions: [
162
+ "Check that all required fields are present",
163
+ "Verify the configuration structure is correct",
164
+ ...errors.map((e) => `Fix ${e.path}: ${e.message}`),
165
+ ],
166
+ metadata: { errors },
167
+ });
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Safe validation that returns a result object instead of throwing
175
+ *
176
+ * @param config - Configuration to validate
177
+ * @returns Validation result with success flag and data or errors
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * const result = safeValidateConfig(userInput);
182
+ * if (result.success) {
183
+ * console.log("Valid config:", result.data);
184
+ * } else {
185
+ * console.error("Validation errors:", result.errors);
186
+ * }
187
+ * ```
188
+ */
189
+ export function safeValidateConfig(
190
+ config: unknown
191
+ ): ValidationResult<z.infer<typeof ConfigSchema>> {
192
+ const result = ConfigSchema.safeParse(config);
193
+
194
+ if (result.success) {
195
+ return {
196
+ success: true,
197
+ data: result.data,
198
+ };
199
+ } else {
200
+ return {
201
+ success: false,
202
+ errors: result.error.errors.map((err) => ({
203
+ path: err.path.join("."),
204
+ message: err.message,
205
+ })),
206
+ };
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Safe AI config validation
212
+ *
213
+ * @param config - AI configuration to validate
214
+ * @returns Validation result with success flag and data or errors
215
+ */
216
+ export function safeValidateAIConfig(
217
+ config: unknown
218
+ ): ValidationResult<z.infer<typeof AIConfigSchema>> {
219
+ const result = AIConfigSchema.safeParse(config);
220
+
221
+ if (result.success) {
222
+ return {
223
+ success: true,
224
+ data: result.data,
225
+ };
226
+ } else {
227
+ return {
228
+ success: false,
229
+ errors: result.error.errors.map((err) => ({
230
+ path: err.path.join("."),
231
+ message: err.message,
232
+ })),
233
+ };
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Validate partial AI config (for updates)
239
+ *
240
+ * @param config - Partial AI configuration to validate
241
+ * @returns Validated partial configuration
242
+ * @throws {TaskOMaticError} If validation fails
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * // Only validating fields that are present
247
+ * const updates = {
248
+ * temperature: 0.8
249
+ * };
250
+ *
251
+ * const validated = validatePartialAIConfig(updates);
252
+ * ```
253
+ */
254
+ export function validatePartialAIConfig(
255
+ config: unknown
256
+ ): Partial<z.infer<typeof AIConfigSchema>> {
257
+ try {
258
+ return AIConfigSchema.partial().parse(config);
259
+ } catch (error) {
260
+ if (error instanceof z.ZodError) {
261
+ const errors = error.errors.map((err) => ({
262
+ path: err.path.join("."),
263
+ message: err.message,
264
+ }));
265
+
266
+ throw new TaskOMaticError("AI configuration update validation failed", {
267
+ code: TaskOMaticErrorCodes.CONFIGURATION_ERROR,
268
+ context: JSON.stringify(config, null, 2),
269
+ suggestions: [
270
+ "Check that field values are valid",
271
+ ...errors.map((e) => `Fix ${e.path}: ${e.message}`),
272
+ ],
273
+ metadata: { errors },
274
+ });
275
+ }
276
+ throw error;
277
+ }
278
+ }
@@ -0,0 +1,435 @@
1
+ import { join, resolve } from "path";
2
+ import { cwd } from "process";
3
+ import { AIConfig, EnvAIConfig, ProviderDefaults, AIProvider } from "../types";
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { config as dotenvConfig } from "dotenv";
6
+ import { validateConfig, validatePartialAIConfig } from "./config-validation";
7
+ import {
8
+ createStandardError,
9
+ TaskOMaticErrorCodes,
10
+ } from "../utils/task-o-matic-error";
11
+ import { logger } from "./logger";
12
+
13
+ export interface Config {
14
+ ai: AIConfig;
15
+ workingDirectory?: string;
16
+ }
17
+
18
+ export interface ConfigCallbacks {
19
+ read: (key: string) => Promise<string | null>;
20
+ write: (key: string, value: string) => Promise<void>;
21
+ getEnv: (key: string) => string | undefined;
22
+ }
23
+
24
+ // Provider-specific sensible defaults for 2025
25
+ // Externalized to JSON for easy updates
26
+ import PROVIDER_DEFAULTS from "./provider-defaults.json";
27
+ export { PROVIDER_DEFAULTS as providerDefaults };
28
+
29
+ function getApiKeyFromEnv(
30
+ provider: AIProvider,
31
+ getEnv: (key: string) => string | undefined
32
+ ): string | undefined {
33
+ switch (provider) {
34
+ case "openrouter":
35
+ return getEnv("OPENROUTER_API_KEY");
36
+ case "anthropic":
37
+ return getEnv("ANTHROPIC_API_KEY");
38
+ case "openai":
39
+ return getEnv("OPENAI_API_KEY");
40
+ case "custom":
41
+ return getEnv("CUSTOM_API_KEY");
42
+ case "zai":
43
+ return getEnv("ZAI_API_KEY");
44
+ default:
45
+ return undefined;
46
+ }
47
+ }
48
+
49
+ export function createDefaultConfigCallbacks(
50
+ workingDir: string = cwd()
51
+ ): ConfigCallbacks {
52
+ // Ensure dotenv is loaded for the default callbacks
53
+ const envPath = join(workingDir, ".env");
54
+ if (existsSync(envPath)) {
55
+ dotenvConfig({ path: envPath });
56
+ }
57
+
58
+ return {
59
+ read: async (key: string) => {
60
+ // If key is relative, assume it's in .task-o-matic dir relative to workingDir
61
+ // But ConfigManager usually passes "config.json"
62
+ // We need to resolve it.
63
+ // For default callbacks, we replicate the original behavior:
64
+ // .task-o-matic/config.json in workingDir
65
+
66
+ const configPath = join(workingDir, ".task-o-matic", key);
67
+ if (existsSync(configPath)) {
68
+ return readFileSync(configPath, "utf-8");
69
+ }
70
+ return null;
71
+ },
72
+ write: async (key: string, value: string) => {
73
+ const taskOMaticDir = join(workingDir, ".task-o-matic");
74
+ if (!existsSync(taskOMaticDir)) {
75
+ mkdirSync(taskOMaticDir, { recursive: true });
76
+ }
77
+ const configPath = join(taskOMaticDir, key);
78
+ writeFileSync(configPath, value, "utf-8");
79
+ },
80
+ getEnv: (key: string) => process.env[key],
81
+ };
82
+ }
83
+
84
+ export class ConfigManager {
85
+ private config: Config | null = null;
86
+ private customWorkingDir: string | null = null;
87
+ private callbacks: ConfigCallbacks;
88
+
89
+ constructor(callbacks?: ConfigCallbacks, workingDirectory?: string) {
90
+ if (workingDirectory) {
91
+ this.customWorkingDir = resolve(workingDirectory);
92
+ }
93
+
94
+ // If callbacks not provided, create defaults using the working directory
95
+ this.callbacks =
96
+ callbacks || createDefaultConfigCallbacks(this.getWorkingDirectory());
97
+
98
+ // We cannot await in constructor.
99
+ // Consumers MUST call load() or ensure it's loaded before accessing config.
100
+ // For backward compatibility in CLI (where sync access was common),
101
+ // we might need a way to force sync load if using default callbacks?
102
+ // But we want to support async callbacks.
103
+ // So we enforce async initialization pattern.
104
+ }
105
+
106
+ setWorkingDirectory(dir: string): void {
107
+ this.customWorkingDir = resolve(dir);
108
+ // Re-create default callbacks to point to new dir
109
+ this.callbacks = createDefaultConfigCallbacks(this.getWorkingDirectory());
110
+ // Invalidate config so next access requires reload
111
+ this.config = null;
112
+ }
113
+
114
+ setCallbacks(callbacks: ConfigCallbacks): void {
115
+ this.callbacks = callbacks;
116
+ this.config = null;
117
+ }
118
+
119
+ getWorkingDirectory(): string {
120
+ return this.customWorkingDir || cwd();
121
+ }
122
+
123
+ getTaskOMaticDir(): string {
124
+ return join(this.getWorkingDirectory(), ".task-o-matic");
125
+ }
126
+
127
+ private loadEnvConfig(): Partial<AIConfig> {
128
+ const providerStr = this.callbacks.getEnv("AI_PROVIDER")?.toLowerCase();
129
+ const provider = providerStr ? (providerStr as AIProvider) : undefined;
130
+
131
+ // Check if provider is valid if set
132
+ // (This validation is loose here, strict validation happens in validateConfig)
133
+
134
+ const maxTokensStr = this.callbacks.getEnv("AI_MAX_TOKENS");
135
+ const tempStr = this.callbacks.getEnv("AI_TEMPERATURE");
136
+ const modelStr = this.callbacks.getEnv("AI_MODEL");
137
+
138
+ const config: Partial<AIConfig> = {};
139
+ if (provider) config.provider = provider;
140
+ if (modelStr) config.model = modelStr;
141
+ if (maxTokensStr) config.maxTokens = parseInt(maxTokensStr);
142
+ if (tempStr) config.temperature = parseFloat(tempStr);
143
+
144
+ // API Key logic needs to know the provider.
145
+ // If provider is not in env, we should check the resolved provider...
146
+ // But here we only return partial env overrides.
147
+ // The API key fetching logic in getApiKeyFromEnv depends on the provider.
148
+ // If the provider comes from file, we can't know it here easily if we are just returning overrides.
149
+ // However, getApiKeyFromEnv is a helper.
150
+
151
+ // We can fetch ALL potential API keys? No, that's messy.
152
+ // Or we just fetch the API key if AI_PROVIDER is set?
153
+ // If AI_PROVIDER is NOT set, we don't know which env var to read for the key yet
154
+ // (unless we assume the default or the file one).
155
+
156
+ // If we return undefined for apiKey here, it might overwrite the file one?
157
+ // No, undefined properties don't overwrite if we use spread correctly?
158
+ // { ...a, ...b } where b has undefined properties...
159
+ // In JS/TS spread { ...{a:1}, ...{a: undefined} } results in {a: undefined}.
160
+ // So we must NOT include undefined keys in the returned object.
161
+
162
+ if (provider) {
163
+ const key = getApiKeyFromEnv(provider, this.callbacks.getEnv);
164
+ if (key) config.apiKey = key;
165
+ } else {
166
+ // If provider not in env, we might miss the API key from env if the user
167
+ // intended to use file-provider + env-key.
168
+ // This is a bit tricky with the current structure.
169
+ // But let's stick to the fix for provider override first.
170
+ // We will deal with API key resolution merging later or assume user sets both in env.
171
+
172
+ // Actually, we can check all possible keys?
173
+ // Or better: The merge logic in load() is complex.
174
+
175
+ // Let's defer API key resolution to after we know the final provider?
176
+ // But load() does: ai: { ...defaultConfig.ai, ...fileConfig.ai, ...envConfig }
177
+
178
+ // If I change loadEnvConfig to NOT return apiKey if provider is missing,
179
+ // then apiKey will come from fileConfig or defaultConfig.
180
+ }
181
+
182
+ return config;
183
+ }
184
+
185
+ async load(): Promise<Config> {
186
+ const envConfig = this.loadEnvConfig();
187
+
188
+ // Default provider is openrouter
189
+ const defaultProvider = envConfig.provider || "openrouter";
190
+ // We can try to get API key for default provider if not in envConfig
191
+ let defaultApiKey = envConfig.apiKey;
192
+ if (!defaultApiKey) {
193
+ defaultApiKey = getApiKeyFromEnv(defaultProvider, this.callbacks.getEnv);
194
+ }
195
+
196
+ const defaultConfig: Config = {
197
+ ai: {
198
+ provider: "openrouter",
199
+ model: "z-ai/glm-4.6",
200
+ maxTokens: 32768,
201
+ temperature: 0.5,
202
+ apiKey: defaultApiKey, // This might be overwritten by file or envConfig later
203
+ },
204
+ };
205
+
206
+ try {
207
+ // Use relative path "config.json" - callbacks handle resolution
208
+ const configData = await this.callbacks.read("config.json");
209
+
210
+ if (configData) {
211
+ const fileConfig = JSON.parse(configData);
212
+
213
+ // We need to resolve the final provider to get the correct API key from env
214
+ // if it wasn't provided in envConfig.
215
+ const finalProvider = envConfig.provider || fileConfig.ai?.provider || defaultConfig.ai.provider;
216
+
217
+ // If envConfig didn't provide a key, check if we can get it based on finalProvider
218
+ let finalApiKey = envConfig.apiKey;
219
+ if (!finalApiKey) {
220
+ finalApiKey = getApiKeyFromEnv(finalProvider, this.callbacks.getEnv);
221
+ }
222
+
223
+ // We construct a refined envConfig that includes the correct API key
224
+ const refinedEnvConfig = { ...envConfig };
225
+ if (finalApiKey) {
226
+ refinedEnvConfig.apiKey = finalApiKey;
227
+ }
228
+
229
+ const mergedConfig = {
230
+ ...defaultConfig,
231
+ ...fileConfig,
232
+ ai: { ...defaultConfig.ai, ...fileConfig.ai, ...refinedEnvConfig },
233
+ };
234
+
235
+ // Validate the merged configuration
236
+ const validatedConfig = validateConfig(mergedConfig);
237
+ this.config = validatedConfig;
238
+
239
+ if (this.config && this.config.workingDirectory) {
240
+ this.customWorkingDir = this.config.workingDirectory;
241
+ }
242
+ } else {
243
+ // Apply envConfig to defaults
244
+ const mergedConfig = {
245
+ ...defaultConfig,
246
+ ai: { ...defaultConfig.ai, ...envConfig }
247
+ };
248
+ this.config = validateConfig(mergedConfig);
249
+ }
250
+ } catch (error) {
251
+ logger.warn(`Failed to read or validate config, using defaults: ${error}`);
252
+ // Even defaults should be validated
253
+ this.config = validateConfig(defaultConfig);
254
+ }
255
+
256
+ return this.config!;
257
+ }
258
+
259
+ async save(): Promise<void> {
260
+ if (!this.config) {
261
+ throw createStandardError(
262
+ TaskOMaticErrorCodes.CONFIGURATION_ERROR,
263
+ "Config not loaded, cannot save.",
264
+ {
265
+ suggestions: ["Call await configManager.load() before saving"],
266
+ }
267
+ );
268
+ }
269
+ try {
270
+ await this.callbacks.write(
271
+ "config.json",
272
+ JSON.stringify(this.config, null, 2)
273
+ );
274
+ } catch (error) {
275
+ logger.error(`Failed to save config: ${error}`);
276
+ throw error;
277
+ }
278
+ }
279
+
280
+ getConfig(): Config {
281
+ if (!this.config) {
282
+ // If we are here, it means load() wasn't called or hasn't finished.
283
+ // Since we can't be async here, we must throw or return defaults.
284
+ // Returning defaults might hide issues.
285
+ // Throwing forces users to await load().
286
+ throw createStandardError(
287
+ TaskOMaticErrorCodes.CONFIGURATION_ERROR,
288
+ "Config not loaded. Call await configManager.load() first.",
289
+ {
290
+ context: "Configuration must be loaded before access",
291
+ suggestions: [
292
+ "Call await configManager.load() first",
293
+ "Check initialization order",
294
+ ],
295
+ }
296
+ );
297
+ }
298
+ return this.config;
299
+ }
300
+
301
+ getAIConfig(): AIConfig {
302
+ return this.getConfig().ai;
303
+ }
304
+
305
+ async setAIConfig(aiConfig: Partial<AIConfig>): Promise<void> {
306
+ if (!this.config) {
307
+ await this.load();
308
+ }
309
+
310
+ // Validate the partial config before merging
311
+ const validatedPartial = validatePartialAIConfig(aiConfig);
312
+
313
+ // Merge and validate the full config
314
+ const mergedAIConfig = { ...this.config!.ai, ...validatedPartial };
315
+ const validatedConfig = validateConfig({
316
+ ...this.config!,
317
+ ai: mergedAIConfig,
318
+ });
319
+
320
+ this.config = validatedConfig;
321
+ await this.save();
322
+ }
323
+
324
+ setConfig(config: Config): void {
325
+ this.config = config;
326
+ }
327
+
328
+ // Helper for legacy code that might need path (deprecated for direct use)
329
+ getConfigFilePath(): string {
330
+ return join(this.getTaskOMaticDir(), "config.json");
331
+ }
332
+
333
+ /**
334
+ * Validate configuration independently of load().
335
+ * Can be used to validate config before applying changes.
336
+ */
337
+ validate(configToValidate?: Partial<Config>): {
338
+ valid: boolean;
339
+ errors: string[];
340
+ } {
341
+ const errors: string[] = [];
342
+
343
+ const config = configToValidate || this.config;
344
+ if (!config) {
345
+ return {
346
+ valid: false,
347
+ errors: [
348
+ "No configuration to validate. Either provide a config or call load() first.",
349
+ ],
350
+ };
351
+ }
352
+
353
+ // Validate AI config
354
+ if (config.ai) {
355
+ const { provider, model, apiKey, maxTokens, temperature } = config.ai;
356
+
357
+ // Validate provider
358
+ if (
359
+ provider &&
360
+ ![
361
+ "openrouter",
362
+ "anthropic",
363
+ "openai",
364
+ "custom",
365
+ "gemini",
366
+ "zai",
367
+ ].includes(provider)
368
+ ) {
369
+ errors.push(
370
+ `Invalid provider: ${provider}. Must be one of: openrouter, anthropic, openai, custom, gemini, zai`
371
+ );
372
+ }
373
+
374
+ // Validate model
375
+ if (model !== undefined && typeof model !== "string") {
376
+ errors.push("Model must be a string");
377
+ }
378
+
379
+ // Validate maxTokens
380
+ if (maxTokens !== undefined) {
381
+ if (
382
+ typeof maxTokens !== "number" ||
383
+ maxTokens < 1 ||
384
+ maxTokens > 200000
385
+ ) {
386
+ errors.push("maxTokens must be a number between 1 and 200000");
387
+ }
388
+ }
389
+
390
+ // Validate temperature
391
+ if (temperature !== undefined) {
392
+ if (
393
+ typeof temperature !== "number" ||
394
+ temperature < 0 ||
395
+ temperature > 2
396
+ ) {
397
+ errors.push("temperature must be a number between 0 and 2");
398
+ }
399
+ }
400
+
401
+ // Warn about missing API key (not an error, just a warning)
402
+ if (!apiKey && provider !== "custom") {
403
+ // This is a soft validation - API key can be set via env vars
404
+ }
405
+ }
406
+
407
+ return {
408
+ valid: errors.length === 0,
409
+ errors,
410
+ };
411
+ }
412
+ }
413
+
414
+ export const configManager = new ConfigManager();
415
+
416
+ /**
417
+ * Helper function to set working directory and reload config.
418
+ * Combines the common pattern of setWorkingDirectory + load.
419
+ *
420
+ * @param dir - Working directory path
421
+ * @param manager - ConfigManager instance (defaults to singleton)
422
+ * @returns Loaded config
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * await setupWorkingDirectory("/path/to/project");
427
+ * ```
428
+ */
429
+ export async function setupWorkingDirectory(
430
+ dir: string,
431
+ manager: ConfigManager = configManager
432
+ ): Promise<Config> {
433
+ manager.setWorkingDirectory(dir);
434
+ return await manager.load();
435
+ }