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