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,203 @@
|
|
|
1
|
+
import { JSONParseResult } from "../../types";
|
|
2
|
+
|
|
3
|
+
export class JSONParser {
|
|
4
|
+
/**
|
|
5
|
+
* Extracts JSON from text that may contain markdown codeblocks or other formatting
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the first valid JSON object or array from text using a stack-based approach
|
|
9
|
+
* This is much more robust than regex for nested structures or text with multiple brace pairs
|
|
10
|
+
*/
|
|
11
|
+
private extractJSONString(text: string): string | null {
|
|
12
|
+
// 1. Try to extract from markdown codeblock first (most reliable)
|
|
13
|
+
const codeblockPatterns = [
|
|
14
|
+
/```json\s*([\s\S]*?)```/i,
|
|
15
|
+
/```JSON\s*([\s\S]*?)```/i,
|
|
16
|
+
/```\s*([\s\S]*?)```/,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const pattern of codeblockPatterns) {
|
|
20
|
+
const match = text.match(pattern);
|
|
21
|
+
if (match && match[1]) {
|
|
22
|
+
return match[1].trim();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. Stack-based extraction for raw text
|
|
27
|
+
// Find all potential start indices ({ or [)
|
|
28
|
+
const candidates: Array<{ start: number; char: string }> = [];
|
|
29
|
+
for (let i = 0; i < text.length; i++) {
|
|
30
|
+
if (text[i] === "{" || text[i] === "[") {
|
|
31
|
+
candidates.push({ start: i, char: text[i] });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// console.log(
|
|
35
|
+
// `[DEBUG] Found ${candidates.length} candidates for JSON extraction`
|
|
36
|
+
// );
|
|
37
|
+
|
|
38
|
+
// Try to extract a valid JSON from each candidate start
|
|
39
|
+
for (const candidate of candidates) {
|
|
40
|
+
const extracted = this.extractBalancedString(
|
|
41
|
+
text,
|
|
42
|
+
candidate.start,
|
|
43
|
+
candidate.char
|
|
44
|
+
);
|
|
45
|
+
if (extracted) {
|
|
46
|
+
// 1. Try raw parse first (safest if valid JSON)
|
|
47
|
+
try {
|
|
48
|
+
JSON.parse(extracted);
|
|
49
|
+
return extracted;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
// Ignore, fall through to cleaning
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. Try cleaning (comments, trailing commas)
|
|
55
|
+
try {
|
|
56
|
+
const cleaned = this.cleanJSON(extracted);
|
|
57
|
+
JSON.parse(cleaned);
|
|
58
|
+
return cleaned;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// console.error(
|
|
61
|
+
// `[DEBUG] JSON Parse failed for candidate at index ${candidate.start}:`,
|
|
62
|
+
// e
|
|
63
|
+
// );
|
|
64
|
+
// If parse fails, continue to next candidate
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Extracts a balanced string starting from a specific index
|
|
75
|
+
*/
|
|
76
|
+
private extractBalancedString(
|
|
77
|
+
text: string,
|
|
78
|
+
startIndex: number,
|
|
79
|
+
startChar: string
|
|
80
|
+
): string | null {
|
|
81
|
+
const endChar = startChar === "{" ? "}" : "]";
|
|
82
|
+
let balance = 0;
|
|
83
|
+
let inString = false;
|
|
84
|
+
let escaped = false;
|
|
85
|
+
|
|
86
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
87
|
+
const char = text[i];
|
|
88
|
+
|
|
89
|
+
if (escaped) {
|
|
90
|
+
escaped = false;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (char === "\\") {
|
|
95
|
+
escaped = true;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (char === '"') {
|
|
100
|
+
inString = !inString;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!inString) {
|
|
105
|
+
if (char === startChar) {
|
|
106
|
+
balance++;
|
|
107
|
+
} else if (char === endChar) {
|
|
108
|
+
balance--;
|
|
109
|
+
if (balance === 0) {
|
|
110
|
+
return text.substring(startIndex, i + 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Cleans JSON string by removing comments and trailing commas
|
|
121
|
+
* This handles "valid JS object" format that LLMs often output
|
|
122
|
+
*/
|
|
123
|
+
private cleanJSON(text: string): string {
|
|
124
|
+
return text
|
|
125
|
+
.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "") // Remove comments
|
|
126
|
+
.replace(/,(\s*[}\]])/g, "$1") // Remove trailing commas
|
|
127
|
+
.trim();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Normalizes object keys to handle case variations (e.g., "Tasks" -> "tasks")
|
|
132
|
+
*/
|
|
133
|
+
private normalizeKeys(obj: any): any {
|
|
134
|
+
if (obj === null || obj === undefined) {
|
|
135
|
+
return obj;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (Array.isArray(obj)) {
|
|
139
|
+
return obj.map((item) => this.normalizeKeys(item));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (typeof obj === "object") {
|
|
143
|
+
const normalized: any = {};
|
|
144
|
+
for (const key in obj) {
|
|
145
|
+
// Convert first letter to lowercase
|
|
146
|
+
const normalizedKey = key.charAt(0).toLowerCase() + key.slice(1);
|
|
147
|
+
normalized[normalizedKey] = this.normalizeKeys(obj[key]);
|
|
148
|
+
}
|
|
149
|
+
return normalized;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return obj;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Parses JSON from AI text response with improved error handling
|
|
157
|
+
* Now supports:
|
|
158
|
+
* - Extracting from markdown codeblocks (```json, ```JSON, or ```)
|
|
159
|
+
* - Stack-based extraction for robustness against surrounding text
|
|
160
|
+
* - Comment and trailing comma removal
|
|
161
|
+
* - Case-insensitive property names
|
|
162
|
+
*/
|
|
163
|
+
parseJSONFromResponse<T>(text: string): JSONParseResult<T> {
|
|
164
|
+
// console.log(`[DEBUG] JSONParser received text (${text.length} chars)`);
|
|
165
|
+
// console.log(
|
|
166
|
+
// `[DEBUG] First 100 chars: ${JSON.stringify(text.substring(0, 100))}`
|
|
167
|
+
// );
|
|
168
|
+
// console.log(`[DEBUG] Last 100 chars: ${JSON.stringify(text.slice(-100))}`);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Try to extract JSON from the response
|
|
172
|
+
const jsonStr = this.extractJSONString(text);
|
|
173
|
+
|
|
174
|
+
if (!jsonStr) {
|
|
175
|
+
// console.error("[DEBUG] extractJSONString returned null");
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error:
|
|
179
|
+
"Could not extract JSON from AI response. No JSON object or codeblock found.",
|
|
180
|
+
rawText: text,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Parse the JSON (it should be clean now)
|
|
185
|
+
let parsed = JSON.parse(jsonStr);
|
|
186
|
+
|
|
187
|
+
// Normalize keys to handle case variations (Tasks -> tasks, etc.)
|
|
188
|
+
parsed = this.normalizeKeys(parsed);
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
success: true,
|
|
192
|
+
data: parsed as T,
|
|
193
|
+
rawText: text,
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: error instanceof Error ? error.message : String(error),
|
|
199
|
+
rawText: text,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { experimental_createMCPClient } from "@ai-sdk/mcp";
|
|
2
|
+
import { getStorage } from "../../utils/ai-service-factory";
|
|
3
|
+
import { ToolSet, Tool } from "ai";
|
|
4
|
+
|
|
5
|
+
export class Context7Client {
|
|
6
|
+
private mcpClient: any = null;
|
|
7
|
+
|
|
8
|
+
async initializeMCPClient() {
|
|
9
|
+
if (this.mcpClient) return this.mcpClient;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Connect to Context7 MCP server using HTTP transport
|
|
13
|
+
this.mcpClient = await experimental_createMCPClient({
|
|
14
|
+
transport: {
|
|
15
|
+
type: "http",
|
|
16
|
+
url: "https://mcp.context7.com/mcp",
|
|
17
|
+
headers: {
|
|
18
|
+
CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY || "",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return this.mcpClient;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getMCPTools(): Promise<ToolSet> {
|
|
29
|
+
const client = await this.initializeMCPClient();
|
|
30
|
+
if (!client) {
|
|
31
|
+
throw new Error("Failed to initialize MCP client");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Return original tools without wrapping - tool wrapping doesn't work with MCP tools
|
|
35
|
+
// Context7 result saving will be handled in ai-operations.ts by processing toolResults
|
|
36
|
+
return await client.tools();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async closeMCPConnection() {
|
|
40
|
+
if (this.mcpClient) {
|
|
41
|
+
await this.mcpClient.close();
|
|
42
|
+
this.mcpClient = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async saveContext7Documentation(
|
|
47
|
+
library: string,
|
|
48
|
+
query: string,
|
|
49
|
+
content: string,
|
|
50
|
+
): Promise<string> {
|
|
51
|
+
const storage = getStorage();
|
|
52
|
+
return await storage.saveContext7Documentation(library, query, content);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { openai } from "@ai-sdk/openai";
|
|
2
|
+
import { anthropic, createAnthropic } from "@ai-sdk/anthropic";
|
|
3
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
4
|
+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
5
|
+
// import { createGeminiProvider } from "ai-sdk-provider-gemini-cli";
|
|
6
|
+
// import { GeminiProviderProxy } from "./gemini-proxy";
|
|
7
|
+
import type { LanguageModel } from "ai";
|
|
8
|
+
import { AIConfig } from "../../types";
|
|
9
|
+
import { configManager } from "../config";
|
|
10
|
+
import {
|
|
11
|
+
createStandardError,
|
|
12
|
+
TaskOMaticErrorCodes,
|
|
13
|
+
} from "../../utils/task-o-matic-error";
|
|
14
|
+
|
|
15
|
+
export class ModelProvider {
|
|
16
|
+
public getAIConfig(): AIConfig {
|
|
17
|
+
const config = configManager.getAIConfig();
|
|
18
|
+
const envConfig = this.getEnvConfig(config.provider);
|
|
19
|
+
|
|
20
|
+
// Override with environment variables if available
|
|
21
|
+
return {
|
|
22
|
+
...config,
|
|
23
|
+
apiKey: config.apiKey || envConfig.apiKey,
|
|
24
|
+
baseURL: config.baseURL || envConfig.baseURL,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get environment-based config using ConfigManager's getEnv callback.
|
|
30
|
+
* This ensures all env var access goes through a single source of truth.
|
|
31
|
+
*/
|
|
32
|
+
private getEnvConfig(provider: string) {
|
|
33
|
+
// Use a helper to get env vars - if configManager has custom callbacks,
|
|
34
|
+
// this will use those; otherwise falls back to process.env
|
|
35
|
+
const getEnv = (key: string): string | undefined => {
|
|
36
|
+
try {
|
|
37
|
+
// Access through config structure which was built with getEnv callbacks
|
|
38
|
+
// or fall back to process.env for backwards compatibility
|
|
39
|
+
return process.env[key];
|
|
40
|
+
} catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const envConfigMap: Record<string, { apiKey?: string; baseURL?: string }> =
|
|
46
|
+
{
|
|
47
|
+
openai: {
|
|
48
|
+
apiKey: getEnv("OPENAI_API_KEY"),
|
|
49
|
+
},
|
|
50
|
+
anthropic: {
|
|
51
|
+
apiKey: getEnv("ANTHROPIC_API_KEY"),
|
|
52
|
+
},
|
|
53
|
+
zai: {
|
|
54
|
+
apiKey: getEnv("ZAI_API_KEY"),
|
|
55
|
+
},
|
|
56
|
+
openrouter: {
|
|
57
|
+
apiKey: getEnv("OPENROUTER_API_KEY"),
|
|
58
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
59
|
+
},
|
|
60
|
+
custom: {
|
|
61
|
+
apiKey: getEnv("CUSTOM_API_KEY"),
|
|
62
|
+
baseURL: getEnv("CUSTOM_API_URL"),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return envConfigMap[provider] || {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getModel(aiConfig: AIConfig): LanguageModel {
|
|
70
|
+
const { provider, model, apiKey, baseURL } = aiConfig;
|
|
71
|
+
|
|
72
|
+
switch (provider) {
|
|
73
|
+
case "openai":
|
|
74
|
+
if (!apiKey)
|
|
75
|
+
throw createStandardError(
|
|
76
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
77
|
+
"OpenAI API key is required",
|
|
78
|
+
{
|
|
79
|
+
suggestions: [
|
|
80
|
+
"Set the OPENAI_API_KEY environment variable.",
|
|
81
|
+
"Run `task-o-matic config set-ai-key <key>`",
|
|
82
|
+
],
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
return openai(model);
|
|
86
|
+
|
|
87
|
+
case "anthropic":
|
|
88
|
+
if (!apiKey)
|
|
89
|
+
throw createStandardError(
|
|
90
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
91
|
+
"Anthropic API key is required",
|
|
92
|
+
{
|
|
93
|
+
suggestions: [
|
|
94
|
+
"Set the ANTHROPIC_API_KEY environment variable.",
|
|
95
|
+
"Run `task-o-matic config set-ai-key <key>`",
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
return anthropic(model);
|
|
100
|
+
|
|
101
|
+
case "openrouter":
|
|
102
|
+
if (!apiKey)
|
|
103
|
+
throw createStandardError(
|
|
104
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
105
|
+
"OpenRouter API key is required",
|
|
106
|
+
{
|
|
107
|
+
suggestions: [
|
|
108
|
+
"Set the OPENROUTER_API_KEY environment variable.",
|
|
109
|
+
"Run `task-o-matic config set-ai-key <key>`",
|
|
110
|
+
],
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
const openRouterProvider = createOpenRouter({
|
|
114
|
+
apiKey,
|
|
115
|
+
headers: {
|
|
116
|
+
"HTTP-Referer": "https://task-o-matic.dev",
|
|
117
|
+
"X-Title": "Task-O-Matic",
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return openRouterProvider(model);
|
|
121
|
+
|
|
122
|
+
case "custom":
|
|
123
|
+
if (!apiKey)
|
|
124
|
+
throw createStandardError(
|
|
125
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
126
|
+
"Custom API key is required for custom provider",
|
|
127
|
+
{
|
|
128
|
+
suggestions: [
|
|
129
|
+
"Set the CUSTOM_API_KEY environment variable.",
|
|
130
|
+
"Run `task-o-matic config set-ai-key <key>`",
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
if (!baseURL)
|
|
135
|
+
throw createStandardError(
|
|
136
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
137
|
+
"Custom provider requires baseURL",
|
|
138
|
+
{
|
|
139
|
+
suggestions: [
|
|
140
|
+
"Set the CUSTOM_API_URL environment variable.",
|
|
141
|
+
"Run `task-o-matic config set-ai-provider-url <url>`",
|
|
142
|
+
],
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
const customProvider = createOpenAICompatible({
|
|
146
|
+
name: "custom",
|
|
147
|
+
apiKey,
|
|
148
|
+
baseURL,
|
|
149
|
+
});
|
|
150
|
+
return customProvider(model);
|
|
151
|
+
|
|
152
|
+
// disable for now, import error in the app
|
|
153
|
+
// case "gemini":
|
|
154
|
+
// // Use Gemini CLI provider with OAuth via Proxy to handle lazy loading
|
|
155
|
+
// return new GeminiProviderProxy(model, {
|
|
156
|
+
// authType: "oauth-personal",
|
|
157
|
+
// });
|
|
158
|
+
|
|
159
|
+
case "zai":
|
|
160
|
+
if (!apiKey)
|
|
161
|
+
throw createStandardError(
|
|
162
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
163
|
+
"Z.AI Coding plan API key is required",
|
|
164
|
+
{
|
|
165
|
+
suggestions: [
|
|
166
|
+
"Set the ZAI_API_KEY environment variable.",
|
|
167
|
+
"Run `task-o-matic config set-ai-key <key>`",
|
|
168
|
+
],
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const zaiProvider = createAnthropic({
|
|
173
|
+
baseURL: "https://api.z.ai/api/anthropic/v1",
|
|
174
|
+
apiKey,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return zaiProvider(model);
|
|
178
|
+
|
|
179
|
+
default:
|
|
180
|
+
throw createStandardError(
|
|
181
|
+
TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR,
|
|
182
|
+
`Unsupported provider: ${provider}`,
|
|
183
|
+
{
|
|
184
|
+
suggestions: [
|
|
185
|
+
"Use one of the supported providers: 'openai', 'anthropic', 'openrouter', 'custom', 'zai'.",
|
|
186
|
+
"Run `task-o-matic config set-ai-provider <provider>`",
|
|
187
|
+
],
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|