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,864 @@
1
+ import { Task, CreateTaskRequest, TaskAIMetadata } from "../../types";
2
+ import { configManager } from "../config";
3
+ import { TaskRepository } from "./types";
4
+ import {
5
+ StorageCallbacks,
6
+ createFileSystemCallbacks,
7
+ } from "./storage-callbacks";
8
+ import {
9
+ TaskOMaticError,
10
+ TaskOMaticErrorCodes,
11
+ formatStorageError,
12
+ createStandardError,
13
+ } from "../../utils/task-o-matic-error";
14
+ import { logger } from "../logger";
15
+
16
+ interface TasksData {
17
+ tasks: Task[];
18
+ nextId: number;
19
+ }
20
+
21
+ export class FileSystemStorage implements TaskRepository {
22
+ private callbacks: StorageCallbacks;
23
+
24
+ constructor(callbacks?: StorageCallbacks) {
25
+ // If no callbacks provided, use default file system callbacks
26
+ // We use configManager to get the base directory for the default implementation
27
+ this.callbacks =
28
+ callbacks || createFileSystemCallbacks(configManager.getTaskOMaticDir());
29
+ }
30
+
31
+ public sanitizeForFilename(name: string): string {
32
+ return name.replace(/[\/\?%*:|"<>]/g, "-");
33
+ }
34
+
35
+ private validateTaskId(taskId: string): void {
36
+ if (!taskId || typeof taskId !== "string" || taskId.trim() === "") {
37
+ throw createStandardError(
38
+ TaskOMaticErrorCodes.INVALID_INPUT,
39
+ "Task ID must be a non-empty string",
40
+ {
41
+ context: "validateTaskId",
42
+ suggestions: ["Provide a valid task ID string"],
43
+ metadata: { taskId },
44
+ }
45
+ );
46
+ }
47
+ }
48
+
49
+ private validateTaskRequest(task: CreateTaskRequest): void {
50
+ if (!task || typeof task !== "object") {
51
+ throw createStandardError(
52
+ TaskOMaticErrorCodes.INVALID_INPUT,
53
+ "Task request must be a valid object",
54
+ {
55
+ context: "validateTaskRequest",
56
+ suggestions: ["Provide a valid task request object"],
57
+ }
58
+ );
59
+ }
60
+ if (
61
+ !task.title ||
62
+ typeof task.title !== "string" ||
63
+ task.title.trim() === ""
64
+ ) {
65
+ throw createStandardError(
66
+ TaskOMaticErrorCodes.INVALID_INPUT,
67
+ "Task title is required and must be a non-empty string",
68
+ {
69
+ context: "validateTaskRequest - title validation",
70
+ suggestions: ["Provide a non-empty title for the task"],
71
+ }
72
+ );
73
+ }
74
+ if (task.parentId && typeof task.parentId !== "string") {
75
+ throw createStandardError(
76
+ TaskOMaticErrorCodes.INVALID_INPUT,
77
+ "Parent ID must be a string if provided",
78
+ {
79
+ context: "validateTaskRequest - parentId validation",
80
+ suggestions: ["Provide a valid parent ID string or omit the field"],
81
+ metadata: { parentId: task.parentId },
82
+ }
83
+ );
84
+ }
85
+ if (
86
+ task.estimatedEffort &&
87
+ !["small", "medium", "large"].includes(task.estimatedEffort)
88
+ ) {
89
+ throw createStandardError(
90
+ TaskOMaticErrorCodes.INVALID_INPUT,
91
+ "Estimated effort must be 'small', 'medium', or 'large'",
92
+ {
93
+ context: "validateTaskRequest - estimatedEffort validation",
94
+ suggestions: ["Use 'small', 'medium', or 'large' for estimatedEffort"],
95
+ metadata: { estimatedEffort: task.estimatedEffort },
96
+ }
97
+ );
98
+ }
99
+ if (task.dependencies && !Array.isArray(task.dependencies)) {
100
+ throw createStandardError(
101
+ TaskOMaticErrorCodes.INVALID_INPUT,
102
+ "Dependencies must be an array if provided",
103
+ {
104
+ context: "validateTaskRequest - dependencies validation",
105
+ suggestions: ["Provide dependencies as an array of task IDs"],
106
+ }
107
+ );
108
+ }
109
+ if (task.tags && !Array.isArray(task.tags)) {
110
+ throw createStandardError(
111
+ TaskOMaticErrorCodes.INVALID_INPUT,
112
+ "Tags must be an array if provided",
113
+ {
114
+ context: "validateTaskRequest - tags validation",
115
+ suggestions: ["Provide tags as an array of strings"],
116
+ }
117
+ );
118
+ }
119
+ }
120
+
121
+ private async loadTasksData(): Promise<TasksData> {
122
+ try {
123
+ const content = await this.callbacks.read("tasks.json");
124
+ if (!content) {
125
+ return { tasks: [], nextId: 1 };
126
+ }
127
+ return JSON.parse(content);
128
+ } catch (error) {
129
+ logger.error(`Failed to read tasks file: ${error}`);
130
+ return { tasks: [], nextId: 1 };
131
+ }
132
+ }
133
+
134
+ private async saveTasksData(data: TasksData): Promise<void> {
135
+ try {
136
+ await this.callbacks.write("tasks.json", JSON.stringify(data, null, 2));
137
+ } catch (error) {
138
+ throw formatStorageError(
139
+ "write tasks.json",
140
+ error instanceof Error ? error : undefined
141
+ );
142
+ }
143
+ }
144
+
145
+ private findTaskInHierarchy(
146
+ tasks: Task[],
147
+ id: string
148
+ ): { task: Task | null; parent: Task | null; index: number } {
149
+ for (let i = 0; i < tasks.length; i++) {
150
+ const task = tasks[i];
151
+ if (task.id === id) {
152
+ return { task, parent: null, index: i };
153
+ }
154
+ if (task.subtasks) {
155
+ const result = this.findTaskInHierarchy(task.subtasks, id);
156
+ if (result.task) {
157
+ return { ...result, parent: task };
158
+ }
159
+ }
160
+ }
161
+ return { task: null, parent: null, index: -1 };
162
+ }
163
+
164
+ private flattenTasks(tasks: Task[]): Task[] {
165
+ const result: Task[] = [];
166
+ for (const task of tasks) {
167
+ const { subtasks, ...taskWithoutSubtasks } = task;
168
+ result.push(taskWithoutSubtasks);
169
+ if (subtasks) {
170
+ result.push(...this.flattenTasks(subtasks));
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+
176
+ // Tasks
177
+ async getTasks(): Promise<Task[]> {
178
+ const data = await this.loadTasksData();
179
+ return this.flattenTasks(data.tasks);
180
+ }
181
+
182
+ async getTopLevelTasks(): Promise<Task[]> {
183
+ const data = await this.loadTasksData();
184
+ return data.tasks;
185
+ }
186
+
187
+ async getTask(id: string): Promise<Task | null> {
188
+ this.validateTaskId(id);
189
+
190
+ const data = await this.loadTasksData();
191
+ const result = this.findTaskInHierarchy(data.tasks, id);
192
+ return result.task;
193
+ }
194
+
195
+ async createTask(
196
+ task: CreateTaskRequest,
197
+ aiMetadata?: TaskAIMetadata
198
+ ): Promise<Task> {
199
+ this.validateTaskRequest(task);
200
+ const data = await this.loadTasksData();
201
+ let id: string;
202
+
203
+ if (task.parentId) {
204
+ const parentResult = this.findTaskInHierarchy(data.tasks, task.parentId);
205
+ if (!parentResult.task) {
206
+ throw createStandardError(
207
+ TaskOMaticErrorCodes.TASK_NOT_FOUND,
208
+ `Parent task with ID ${task.parentId} not found`,
209
+ {
210
+ context: "createTask - parent validation",
211
+ suggestions: [
212
+ "Create the parent task first",
213
+ "Check that the parent ID is correct",
214
+ "List all tasks to see available parent IDs",
215
+ ],
216
+ metadata: { parentId: task.parentId },
217
+ }
218
+ );
219
+ }
220
+
221
+ const siblingCount = (parentResult.task.subtasks?.length || 0) + 1;
222
+ id = `${task.parentId}.${siblingCount}`;
223
+ } else {
224
+ if (task.id && typeof task.id === "string") {
225
+ id = task.id;
226
+ } else {
227
+ id = data.nextId.toString();
228
+ data.nextId++;
229
+ }
230
+ }
231
+
232
+ if (task.dependencies && task.dependencies.length > 0) {
233
+ for (const depId of task.dependencies) {
234
+ const depExists = this.taskExists(data.tasks, depId);
235
+ if (!depExists) {
236
+ throw createStandardError(
237
+ TaskOMaticErrorCodes.TASK_NOT_FOUND,
238
+ `Dependency task not found: ${depId}`,
239
+ {
240
+ context: "createTask - dependency validation",
241
+ suggestions: [
242
+ "Create the dependency task first",
243
+ "Check that the dependency ID is correct",
244
+ "Remove the dependency from the task",
245
+ ],
246
+ metadata: { dependencyId: depId, taskDependencies: task.dependencies },
247
+ }
248
+ );
249
+ }
250
+ }
251
+
252
+ if (
253
+ this.wouldCreateCircularDependency(data.tasks, id, task.dependencies)
254
+ ) {
255
+ throw createStandardError(
256
+ TaskOMaticErrorCodes.STORAGE_INTEGRITY_ERROR,
257
+ `Circular dependency detected for task ${id}`,
258
+ {
259
+ context: "createTask - circular dependency check",
260
+ suggestions: [
261
+ "Remove circular dependencies from the task",
262
+ "Review the dependency chain",
263
+ ],
264
+ metadata: { taskId: id, dependencies: task.dependencies },
265
+ }
266
+ );
267
+ }
268
+ }
269
+
270
+ let contentFile: string | undefined;
271
+ let description = task.description || "";
272
+
273
+ if (task.content && task.content.length > 200) {
274
+ contentFile = await this.saveTaskContent(id, task.content);
275
+ description = task.description || task.content.substring(0, 200) + "...";
276
+ } else if (task.content) {
277
+ description = task.description || task.content;
278
+ }
279
+
280
+ const newTask: Task = {
281
+ id,
282
+ title: task.title,
283
+ description,
284
+ contentFile,
285
+ status: task.status || "todo",
286
+ estimatedEffort: task.estimatedEffort,
287
+ dependencies: task.dependencies,
288
+ tags: task.tags,
289
+ subtasks: [],
290
+ createdAt: Date.now(),
291
+ updatedAt: Date.now(),
292
+ prdFile: task.prdFile,
293
+ };
294
+
295
+ if (task.parentId) {
296
+ const parentResult = this.findTaskInHierarchy(data.tasks, task.parentId);
297
+ if (parentResult.task) {
298
+ if (!parentResult.task.subtasks) {
299
+ parentResult.task.subtasks = [];
300
+ }
301
+ parentResult.task.subtasks.push(newTask);
302
+ }
303
+ } else {
304
+ data.tasks.push(newTask);
305
+ }
306
+
307
+ await this.saveTasksData(data);
308
+
309
+ if (aiMetadata) {
310
+ await this.saveTaskAIMetadata({
311
+ ...aiMetadata,
312
+ taskId: id,
313
+ generatedAt: Date.now(),
314
+ });
315
+ }
316
+
317
+ return newTask;
318
+ }
319
+
320
+ async updateTask(id: string, updates: Partial<Task>): Promise<Task | null> {
321
+ this.validateTaskId(id);
322
+ if (!updates || typeof updates !== "object") {
323
+ throw createStandardError(
324
+ TaskOMaticErrorCodes.INVALID_INPUT,
325
+ "Updates must be a valid object",
326
+ {
327
+ context: "updateTask - updates validation",
328
+ suggestions: ["Provide a valid updates object with task properties"],
329
+ }
330
+ );
331
+ }
332
+
333
+ const data = await this.loadTasksData();
334
+ const result = this.findTaskInHierarchy(data.tasks, id);
335
+
336
+ if (!result.task) return null;
337
+
338
+ const updatedTask: Task = {
339
+ ...result.task,
340
+ ...updates,
341
+ id,
342
+ updatedAt: Date.now(),
343
+ };
344
+
345
+ if (result.parent) {
346
+ const parentIndex = result.parent.subtasks!.findIndex((t) => t.id === id);
347
+ result.parent.subtasks![parentIndex] = updatedTask;
348
+ } else {
349
+ const taskIndex = data.tasks.findIndex((t) => t.id === id);
350
+ data.tasks[taskIndex] = updatedTask;
351
+ }
352
+
353
+ await this.saveTasksData(data);
354
+ return updatedTask;
355
+ }
356
+
357
+ async deleteTask(id: string): Promise<boolean> {
358
+ this.validateTaskId(id);
359
+
360
+ const data = await this.loadTasksData();
361
+ const result = this.findTaskInHierarchy(data.tasks, id);
362
+
363
+ if (!result.task) return false;
364
+
365
+ if (result.parent) {
366
+ const parentIndex = result.parent.subtasks!.findIndex((t) => t.id === id);
367
+ result.parent.subtasks!.splice(parentIndex, 1);
368
+ } else {
369
+ const taskIndex = data.tasks.findIndex((t) => t.id === id);
370
+ data.tasks.splice(taskIndex, 1);
371
+ }
372
+
373
+ await this.saveTasksData(data);
374
+ return true;
375
+ }
376
+
377
+ async getSubtasks(parentId: string): Promise<Task[]> {
378
+ const data = await this.loadTasksData();
379
+ const result = this.findTaskInHierarchy(data.tasks, parentId);
380
+ return result.task?.subtasks || [];
381
+ }
382
+
383
+ private taskExists(tasks: Task[], taskId: string): boolean {
384
+ const exactMatch = this.findTaskInHierarchy(tasks, taskId).task;
385
+ if (exactMatch) return true;
386
+
387
+ const taskPrefixedId = taskId.startsWith("task-")
388
+ ? taskId.substring(5)
389
+ : `task-${taskId}`;
390
+ const prefixedMatch = this.findTaskInHierarchy(tasks, taskPrefixedId).task;
391
+ if (prefixedMatch) return true;
392
+
393
+ if (taskId.startsWith("task-")) {
394
+ const numericId = taskId.substring(5);
395
+ const numericMatch = this.findTaskInHierarchy(tasks, numericId).task;
396
+ if (numericMatch) return true;
397
+ }
398
+
399
+ return false;
400
+ }
401
+
402
+ private wouldCreateCircularDependency(
403
+ tasks: Task[],
404
+ newTaskId: string,
405
+ dependencies: string[]
406
+ ): boolean {
407
+ const visited = new Set<string>();
408
+
409
+ const checkCircular = (taskId: string): boolean => {
410
+ if (visited.has(taskId)) {
411
+ return true;
412
+ }
413
+
414
+ if (taskId === newTaskId) {
415
+ return true;
416
+ }
417
+
418
+ visited.add(taskId);
419
+
420
+ const task = this.findTaskInHierarchy(tasks, taskId).task;
421
+ if (task && task.dependencies) {
422
+ for (const depId of task.dependencies) {
423
+ if (checkCircular(depId)) {
424
+ return true;
425
+ }
426
+ }
427
+ }
428
+
429
+ visited.delete(taskId);
430
+ return false;
431
+ };
432
+
433
+ for (const depId of dependencies) {
434
+ visited.clear();
435
+ if (checkCircular(depId)) {
436
+ return true;
437
+ }
438
+ }
439
+
440
+ return false;
441
+ }
442
+
443
+ private async loadAIMetadata(): Promise<TaskAIMetadata[]> {
444
+ try {
445
+ const content = await this.callbacks.read("ai-metadata.json");
446
+ if (!content) return [];
447
+ return JSON.parse(content);
448
+ } catch (error) {
449
+ logger.error(`Failed to read AI metadata file: ${error}`);
450
+ return [];
451
+ }
452
+ }
453
+
454
+ private async saveAIMetadata(metadata: TaskAIMetadata[]): Promise<void> {
455
+ await this.callbacks.write(
456
+ "ai-metadata.json",
457
+ JSON.stringify(metadata, null, 2)
458
+ );
459
+ }
460
+
461
+ async getTaskAIMetadata(taskId: string): Promise<TaskAIMetadata | null> {
462
+ const metadata = await this.loadAIMetadata();
463
+ return metadata.find((meta) => meta.taskId === taskId) || null;
464
+ }
465
+
466
+ async saveTaskAIMetadata(metadata: TaskAIMetadata): Promise<void> {
467
+ const allMetadata = await this.loadAIMetadata();
468
+ const existingIndex = allMetadata.findIndex(
469
+ (meta) => meta.taskId === metadata.taskId
470
+ );
471
+
472
+ if (existingIndex >= 0) {
473
+ allMetadata[existingIndex] = metadata;
474
+ } else {
475
+ allMetadata.push(metadata);
476
+ }
477
+
478
+ await this.saveAIMetadata(allMetadata);
479
+ }
480
+
481
+ async deleteTaskAIMetadata(taskId: string): Promise<void> {
482
+ const metadata = await this.loadAIMetadata();
483
+ const filtered = metadata.filter((meta) => meta.taskId !== taskId);
484
+ await this.saveAIMetadata(filtered);
485
+ }
486
+
487
+ async saveTaskContent(taskId: string, content: string): Promise<string> {
488
+ this.validateTaskId(taskId);
489
+ if (typeof content !== "string") {
490
+ throw createStandardError(
491
+ TaskOMaticErrorCodes.INVALID_INPUT,
492
+ "Content must be a string",
493
+ {
494
+ context: "Content validation",
495
+ suggestions: ["Provide content as a string"],
496
+ }
497
+ );
498
+ }
499
+
500
+ const contentFileName = `tasks/${taskId}.md`;
501
+
502
+ try {
503
+ await this.callbacks.write(contentFileName, content);
504
+ } catch (error) {
505
+ throw formatStorageError(
506
+ "write task content",
507
+ error instanceof Error ? error : undefined
508
+ );
509
+ }
510
+ return contentFileName;
511
+ }
512
+
513
+ async saveEnhancedTaskContent(
514
+ taskId: string,
515
+ content: string
516
+ ): Promise<string> {
517
+ this.validateTaskId(taskId);
518
+ if (typeof content !== "string") {
519
+ throw createStandardError(
520
+ TaskOMaticErrorCodes.INVALID_INPUT,
521
+ "Content must be a string",
522
+ {
523
+ context: "Content validation",
524
+ suggestions: ["Provide content as a string"],
525
+ }
526
+ );
527
+ }
528
+
529
+ const contentFileName = `tasks/enhanced/${taskId}.md`;
530
+
531
+ try {
532
+ await this.callbacks.write(contentFileName, content);
533
+ } catch (error) {
534
+ throw formatStorageError(
535
+ "write enhanced task content",
536
+ error instanceof Error ? error : undefined
537
+ );
538
+ }
539
+ return contentFileName;
540
+ }
541
+
542
+ async getTaskContent(taskId: string): Promise<string | null> {
543
+ this.validateTaskId(taskId);
544
+
545
+ const contentFileName = `tasks/${taskId}.md`;
546
+
547
+ try {
548
+ return await this.callbacks.read(contentFileName);
549
+ } catch (error) {
550
+ logger.error(`Failed to read task content for ${taskId}: ${error}`);
551
+ return null;
552
+ }
553
+ }
554
+
555
+ async deleteTaskContent(taskId: string): Promise<void> {
556
+ this.validateTaskId(taskId);
557
+ const contentFileName = `tasks/${taskId}.md`;
558
+ await this.callbacks.delete(contentFileName);
559
+ }
560
+
561
+ async saveContext7Documentation(
562
+ library: string,
563
+ query: string,
564
+ content: string
565
+ ): Promise<string> {
566
+ const sanitizedLibrary = this.sanitizeForFilename(library);
567
+ const sanitizedQuery = this.sanitizeForFilename(query);
568
+
569
+ const filePath = `docs/${sanitizedLibrary}/${sanitizedQuery}.md`;
570
+ await this.callbacks.write(filePath, content);
571
+
572
+ return filePath;
573
+ }
574
+
575
+ async getDocumentationFile(fileName: string): Promise<string | null> {
576
+ const filePath = `docs/${fileName}`;
577
+ try {
578
+ return await this.callbacks.read(filePath);
579
+ } catch (error) {
580
+ logger.error(`Failed to read documentation file ${fileName}: ${error}`);
581
+ return null;
582
+ }
583
+ }
584
+
585
+ async listDocumentationFiles(): Promise<string[]> {
586
+ try {
587
+ const files = await this.callbacks.list("docs/");
588
+ // Filter for markdown/text files and remove "docs/" prefix for compatibility
589
+ return files
590
+ .filter((f) => f.endsWith(".md") || f.endsWith(".txt"))
591
+ .map((f) => (f.startsWith("docs/") ? f.substring(5) : f));
592
+ } catch (error) {
593
+ logger.error(`Failed to list documentation files: ${error}`);
594
+ return [];
595
+ }
596
+ }
597
+
598
+ async migrateTaskContent(): Promise<number> {
599
+ const data = await this.loadTasksData();
600
+ let migratedCount = 0;
601
+
602
+ const migrateTask = async (task: Task): Promise<Task> => {
603
+ let updatedTask = { ...task };
604
+
605
+ if ("content" in task && (task as any).content && !task.contentFile) {
606
+ const oldContent = (task as any).content;
607
+
608
+ if (oldContent.length > 200) {
609
+ const contentFile = `tasks/${task.id}.md`;
610
+ await this.callbacks.write(contentFile, oldContent);
611
+
612
+ updatedTask.contentFile = contentFile;
613
+ updatedTask.description =
614
+ task.description || oldContent.substring(0, 200) + "...";
615
+ } else {
616
+ updatedTask.description = task.description || oldContent;
617
+ }
618
+
619
+ const { content: _, ...taskWithoutContent } = updatedTask as any;
620
+ updatedTask = taskWithoutContent;
621
+ migratedCount++;
622
+ }
623
+
624
+ if (task.subtasks) {
625
+ updatedTask.subtasks = await Promise.all(
626
+ task.subtasks.map(migrateTask)
627
+ );
628
+ }
629
+
630
+ return updatedTask;
631
+ };
632
+
633
+ data.tasks = await Promise.all(data.tasks.map(migrateTask));
634
+
635
+ if (migratedCount > 0) {
636
+ await this.saveTasksData(data);
637
+ }
638
+
639
+ return migratedCount;
640
+ }
641
+
642
+ async cleanupOrphanedContent(): Promise<number> {
643
+ const data = await this.loadTasksData();
644
+ const allTasks = this.flattenTasks(data.tasks);
645
+ const validContentFiles = new Set(
646
+ allTasks
647
+ .filter((task) => task.contentFile)
648
+ .map((task) => task.contentFile!)
649
+ );
650
+
651
+ let cleanedCount = 0;
652
+
653
+ try {
654
+ const files = await this.callbacks.list("tasks/");
655
+
656
+ for (const file of files) {
657
+ // file is a full key like "tasks/123.md"
658
+ // validContentFiles contains relative paths like "tasks/123.md"
659
+ // So we can check directly
660
+
661
+ if (file.endsWith(".md")) {
662
+ if (!validContentFiles.has(file)) {
663
+ try {
664
+ await this.callbacks.delete(file);
665
+ cleanedCount++;
666
+ logger.info(`Cleaned up orphaned content file: ${file}`);
667
+ } catch (error) {
668
+ logger.error(`Failed to delete orphaned file ${file}: ${error}`);
669
+ }
670
+ }
671
+ }
672
+ }
673
+ } catch (error) {
674
+ logger.error(`Failed to cleanup orphaned content: ${error}`);
675
+ }
676
+
677
+ return cleanedCount;
678
+ }
679
+
680
+ async validateStorageIntegrity(): Promise<{
681
+ isValid: boolean;
682
+ issues: string[];
683
+ }> {
684
+ const issues: string[] = [];
685
+
686
+ try {
687
+ const data = await this.loadTasksData();
688
+ if (!Array.isArray(data.tasks)) {
689
+ issues.push("Tasks data is not an array");
690
+ }
691
+
692
+ if (typeof data.nextId !== "number" || data.nextId < 1) {
693
+ issues.push("Invalid nextId in tasks data");
694
+ }
695
+
696
+ const allTasks = this.flattenTasks(data.tasks);
697
+ const taskIds = allTasks.map((task) => task.id);
698
+ const duplicateIds = taskIds.filter(
699
+ (id, index) => taskIds.indexOf(id) !== index
700
+ );
701
+ if (duplicateIds.length > 0) {
702
+ issues.push(`Duplicate task IDs found: ${duplicateIds.join(", ")}`);
703
+ }
704
+
705
+ for (const task of allTasks) {
706
+ if (task.dependencies) {
707
+ for (const depId of task.dependencies) {
708
+ if (!taskIds.includes(depId)) {
709
+ issues.push(`Task ${task.id} has invalid dependency: ${depId}`);
710
+ }
711
+ }
712
+ }
713
+ }
714
+ } catch (error) {
715
+ issues.push(
716
+ `Storage validation error: ${
717
+ error instanceof Error ? error.message : "Unknown error"
718
+ }`
719
+ );
720
+ }
721
+
722
+ return {
723
+ isValid: issues.length === 0,
724
+ issues,
725
+ };
726
+ }
727
+
728
+ async savePlan(taskId: string, plan: string): Promise<void> {
729
+ this.validateTaskId(taskId);
730
+
731
+ const planFile = `plans/${taskId}.json`;
732
+
733
+ try {
734
+ const planData = {
735
+ taskId,
736
+ plan,
737
+ createdAt: Date.now(),
738
+ updatedAt: Date.now(),
739
+ };
740
+
741
+ await this.callbacks.write(planFile, JSON.stringify(planData, null, 2));
742
+ } catch (error) {
743
+ throw formatStorageError(
744
+ `write plan for task ${taskId}`,
745
+ error instanceof Error ? error : undefined
746
+ );
747
+ }
748
+ }
749
+
750
+ async getPlan(
751
+ taskId: string
752
+ ): Promise<{ plan: string; createdAt: number; updatedAt: number } | null> {
753
+ this.validateTaskId(taskId);
754
+
755
+ const planFile = `plans/${taskId}.json`;
756
+
757
+ try {
758
+ const content = await this.callbacks.read(planFile);
759
+ if (!content) return null;
760
+ return JSON.parse(content);
761
+ } catch (error) {
762
+ throw formatStorageError(
763
+ `read plan for task ${taskId}`,
764
+ error instanceof Error ? error : undefined
765
+ );
766
+ }
767
+ }
768
+
769
+ async listPlans(): Promise<
770
+ Array<{
771
+ taskId: string;
772
+ plan: string;
773
+ createdAt: number;
774
+ updatedAt: number;
775
+ }>
776
+ > {
777
+ try {
778
+ const plans: Array<{
779
+ taskId: string;
780
+ plan: string;
781
+ createdAt: number;
782
+ updatedAt: number;
783
+ }> = [];
784
+
785
+ const files = await this.callbacks.list("plans/");
786
+
787
+ for (const file of files) {
788
+ if (file.endsWith(".json")) {
789
+ // file is "plans/123.json"
790
+ const taskId = file.replace("plans/", "").replace(".json", "");
791
+ const planData = await this.getPlan(taskId);
792
+ if (planData) {
793
+ plans.push({
794
+ taskId,
795
+ plan: planData.plan,
796
+ createdAt: planData.createdAt,
797
+ updatedAt: planData.updatedAt,
798
+ });
799
+ }
800
+ }
801
+ }
802
+
803
+ return plans.sort((a, b) => b.updatedAt - a.updatedAt);
804
+ } catch (error) {
805
+ throw formatStorageError(
806
+ "list plans",
807
+ error instanceof Error ? error : undefined
808
+ );
809
+ }
810
+ }
811
+
812
+ async deletePlan(taskId: string): Promise<boolean> {
813
+ this.validateTaskId(taskId);
814
+ const planFile = `plans/${taskId}.json`;
815
+ try {
816
+ await this.callbacks.delete(planFile);
817
+ return true;
818
+ } catch (error) {
819
+ return false;
820
+ }
821
+ }
822
+
823
+ async saveTaskDocumentation(
824
+ taskId: string,
825
+ documentation: string
826
+ ): Promise<string> {
827
+ this.validateTaskId(taskId);
828
+ if (typeof documentation !== "string") {
829
+ throw createStandardError(
830
+ TaskOMaticErrorCodes.INVALID_INPUT,
831
+ "Documentation must be a string",
832
+ {
833
+ context: "Documentation validation",
834
+ suggestions: ["Provide documentation as a string"],
835
+ }
836
+ );
837
+ }
838
+
839
+ const documentationFileName = `docs/tasks/${taskId}.md`;
840
+
841
+ try {
842
+ await this.callbacks.write(documentationFileName, documentation);
843
+ } catch (error) {
844
+ throw formatStorageError(
845
+ "write task documentation",
846
+ error instanceof Error ? error : undefined
847
+ );
848
+ }
849
+ return documentationFileName;
850
+ }
851
+
852
+ async getTaskDocumentation(taskId: string): Promise<string | null> {
853
+ this.validateTaskId(taskId);
854
+
855
+ const documentationFileName = `docs/tasks/${taskId}.md`;
856
+
857
+ try {
858
+ return await this.callbacks.read(documentationFileName);
859
+ } catch (error) {
860
+ logger.error(`Failed to read task documentation for ${taskId}: ${error}`);
861
+ return null;
862
+ }
863
+ }
864
+ }