octocode-cli 1.2.6 → 1.2.7
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/LICENSE +21 -63
- package/README.md +85 -142
- package/out/octocode-cli.js +7026 -6945
- package/package.json +8 -6
- package/skills/README.md +97 -120
- package/skills/octocode-code-engineer/.claude/settings.local.json +18 -0
- package/skills/octocode-code-engineer/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +255 -0
- package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +223 -0
- package/skills/octocode-code-engineer/README.md +178 -0
- package/skills/octocode-code-engineer/SKILL.md +418 -0
- package/skills/octocode-code-engineer/coverage/architecture.ts.html +7828 -0
- package/skills/octocode-code-engineer/coverage/ast-helpers.ts.html +211 -0
- package/skills/octocode-code-engineer/coverage/ast-search.ts.html +1795 -0
- package/skills/octocode-code-engineer/coverage/base.css +224 -0
- package/skills/octocode-code-engineer/coverage/block-navigation.js +87 -0
- package/skills/octocode-code-engineer/coverage/cache.ts.html +376 -0
- package/skills/octocode-code-engineer/coverage/cli.ts.html +982 -0
- package/skills/octocode-code-engineer/coverage/clover.xml +3217 -0
- package/skills/octocode-code-engineer/coverage/collect-effects.ts.html +664 -0
- package/skills/octocode-code-engineer/coverage/collect-input-sources.ts.html +577 -0
- package/skills/octocode-code-engineer/coverage/collect-performance.ts.html +331 -0
- package/skills/octocode-code-engineer/coverage/collect-prototype-pollution.ts.html +421 -0
- package/skills/octocode-code-engineer/coverage/collect-security.ts.html +604 -0
- package/skills/octocode-code-engineer/coverage/collect-test-profile.ts.html +589 -0
- package/skills/octocode-code-engineer/coverage/coverage-final.json +30 -0
- package/skills/octocode-code-engineer/coverage/dependencies.ts.html +997 -0
- package/skills/octocode-code-engineer/coverage/dependency-summary.ts.html +688 -0
- package/skills/octocode-code-engineer/coverage/discovery.ts.html +322 -0
- package/skills/octocode-code-engineer/coverage/favicon.png +0 -0
- package/skills/octocode-code-engineer/coverage/graph-analytics.ts.html +1510 -0
- package/skills/octocode-code-engineer/coverage/index.html +536 -0
- package/skills/octocode-code-engineer/coverage/index.ts.html +826 -0
- package/skills/octocode-code-engineer/coverage/metrics.ts.html +553 -0
- package/skills/octocode-code-engineer/coverage/pipeline.ts.html +2044 -0
- package/skills/octocode-code-engineer/coverage/prettify.css +1 -0
- package/skills/octocode-code-engineer/coverage/prettify.js +2 -0
- package/skills/octocode-code-engineer/coverage/report-analysis.ts.html +1570 -0
- package/skills/octocode-code-engineer/coverage/report-writer.ts.html +1102 -0
- package/skills/octocode-code-engineer/coverage/security-detectors.ts.html +1747 -0
- package/skills/octocode-code-engineer/coverage/semantic-detectors.ts.html +2152 -0
- package/skills/octocode-code-engineer/coverage/semantic.ts.html +1897 -0
- package/skills/octocode-code-engineer/coverage/sort-arrow-sprite.png +0 -0
- package/skills/octocode-code-engineer/coverage/sorter.js +210 -0
- package/skills/octocode-code-engineer/coverage/summary-md.ts.html +1222 -0
- package/skills/octocode-code-engineer/coverage/test-quality-detectors.ts.html +1039 -0
- package/skills/octocode-code-engineer/coverage/tree-sitter-analyzer.ts.html +955 -0
- package/skills/octocode-code-engineer/coverage/ts-analyzer.ts.html +1213 -0
- package/skills/octocode-code-engineer/coverage/types.ts.html +2473 -0
- package/skills/octocode-code-engineer/coverage/utils.ts.html +820 -0
- package/skills/octocode-code-engineer/eslint.config.mjs +54 -0
- package/skills/octocode-code-engineer/minify-scripts.mjs +32 -0
- package/skills/octocode-code-engineer/package.json +54 -0
- package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +95 -0
- package/skills/octocode-code-engineer/references/architecture-techniques.md +121 -0
- package/skills/octocode-code-engineer/references/ast-search.md +210 -0
- package/skills/octocode-code-engineer/references/ast-tree-search.md +151 -0
- package/skills/octocode-code-engineer/references/cli-reference.md +167 -0
- package/skills/octocode-code-engineer/references/concepts.md +107 -0
- package/skills/octocode-code-engineer/references/finding-categories.md +128 -0
- package/skills/octocode-code-engineer/references/improvement-roadmap.md +304 -0
- package/skills/octocode-code-engineer/references/output-files.md +144 -0
- package/skills/octocode-code-engineer/references/playbooks.md +204 -0
- package/skills/octocode-code-engineer/references/present-results.md +136 -0
- package/skills/octocode-code-engineer/references/tool-workflows.md +566 -0
- package/skills/octocode-code-engineer/references/validate-investigate.md +225 -0
- package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +1 -0
- package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +1 -0
- package/skills/octocode-code-engineer/scripts/analysis/discovery.js +1 -0
- package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +1 -0
- package/skills/octocode-code-engineer/scripts/analysis/semantic.js +1 -0
- package/skills/octocode-code-engineer/scripts/ast/helpers.js +1 -0
- package/skills/octocode-code-engineer/scripts/ast/metrics.js +1 -0
- package/skills/octocode-code-engineer/scripts/ast/search.js +2 -0
- package/skills/octocode-code-engineer/scripts/ast/tree-search.js +2 -0
- package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +1 -0
- package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/chains.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/effects.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/performance.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/security.js +1 -0
- package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +1 -0
- package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +1 -0
- package/skills/octocode-code-engineer/scripts/common/utils.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/coupling.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/cycle.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/import-style.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/index.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/security.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/semantic.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/shared.js +1 -0
- package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +1 -0
- package/skills/octocode-code-engineer/scripts/index.js +1 -0
- package/skills/octocode-code-engineer/scripts/pipeline/cache.js +1 -0
- package/skills/octocode-code-engineer/scripts/pipeline/cli.js +1 -0
- package/skills/octocode-code-engineer/scripts/pipeline/main.js +2 -0
- package/skills/octocode-code-engineer/scripts/reporting/analysis.js +1 -0
- package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +1 -0
- package/skills/octocode-code-engineer/scripts/reporting/writer.js +1 -0
- package/skills/octocode-code-engineer/scripts/types/constants.js +1 -0
- package/skills/octocode-code-engineer/scripts/types/index.js +1 -0
- package/skills/octocode-code-engineer/scripts/types/interfaces.js +1 -0
- package/skills/octocode-code-engineer/src/analysis/dependencies.test.ts +545 -0
- package/skills/octocode-code-engineer/src/analysis/dependencies.ts +406 -0
- package/skills/octocode-code-engineer/src/analysis/dependency-summary.test.ts +566 -0
- package/skills/octocode-code-engineer/src/analysis/dependency-summary.ts +257 -0
- package/skills/octocode-code-engineer/src/analysis/discovery.test.ts +420 -0
- package/skills/octocode-code-engineer/src/analysis/discovery.ts +87 -0
- package/skills/octocode-code-engineer/src/analysis/graph-analytics.test.ts +449 -0
- package/skills/octocode-code-engineer/src/analysis/graph-analytics.ts +534 -0
- package/skills/octocode-code-engineer/src/analysis/semantic.test.ts +1533 -0
- package/skills/octocode-code-engineer/src/analysis/semantic.ts +830 -0
- package/skills/octocode-code-engineer/src/ast/helpers.test.ts +185 -0
- package/skills/octocode-code-engineer/src/ast/helpers.ts +62 -0
- package/skills/octocode-code-engineer/src/ast/metrics.test.ts +304 -0
- package/skills/octocode-code-engineer/src/ast/metrics.ts +204 -0
- package/skills/octocode-code-engineer/src/ast/search.test.ts +647 -0
- package/skills/octocode-code-engineer/src/ast/search.ts +648 -0
- package/skills/octocode-code-engineer/src/ast/tree-search.test.ts +199 -0
- package/skills/octocode-code-engineer/src/ast/tree-search.ts +392 -0
- package/skills/octocode-code-engineer/src/ast/tree-sitter.test.ts +407 -0
- package/skills/octocode-code-engineer/src/ast/tree-sitter.ts +402 -0
- package/skills/octocode-code-engineer/src/ast/ts-analyzer.test.ts +1864 -0
- package/skills/octocode-code-engineer/src/ast/ts-analyzer.ts +509 -0
- package/skills/octocode-code-engineer/src/collectors/chains.ts +74 -0
- package/skills/octocode-code-engineer/src/collectors/effects.test.ts +490 -0
- package/skills/octocode-code-engineer/src/collectors/effects.ts +332 -0
- package/skills/octocode-code-engineer/src/collectors/input-sources.test.ts +144 -0
- package/skills/octocode-code-engineer/src/collectors/input-sources.ts +196 -0
- package/skills/octocode-code-engineer/src/collectors/performance.test.ts +82 -0
- package/skills/octocode-code-engineer/src/collectors/performance.ts +141 -0
- package/skills/octocode-code-engineer/src/collectors/prototype-pollution.test.ts +55 -0
- package/skills/octocode-code-engineer/src/collectors/prototype-pollution.ts +162 -0
- package/skills/octocode-code-engineer/src/collectors/security.test.ts +124 -0
- package/skills/octocode-code-engineer/src/collectors/security.ts +309 -0
- package/skills/octocode-code-engineer/src/collectors/test-profile.test.ts +97 -0
- package/skills/octocode-code-engineer/src/collectors/test-profile.ts +269 -0
- package/skills/octocode-code-engineer/src/common/is-direct-run.test.ts +32 -0
- package/skills/octocode-code-engineer/src/common/is-direct-run.ts +13 -0
- package/skills/octocode-code-engineer/src/common/utils.test.ts +463 -0
- package/skills/octocode-code-engineer/src/common/utils.ts +304 -0
- package/skills/octocode-code-engineer/src/detectors/code-quality.ts +966 -0
- package/skills/octocode-code-engineer/src/detectors/cohesion.ts +539 -0
- package/skills/octocode-code-engineer/src/detectors/coupling.ts +323 -0
- package/skills/octocode-code-engineer/src/detectors/cycle.ts +349 -0
- package/skills/octocode-code-engineer/src/detectors/dead-code.ts +320 -0
- package/skills/octocode-code-engineer/src/detectors/import-style.ts +376 -0
- package/skills/octocode-code-engineer/src/detectors/index.test.ts +3061 -0
- package/skills/octocode-code-engineer/src/detectors/index.ts +88 -0
- package/skills/octocode-code-engineer/src/detectors/security.test.ts +882 -0
- package/skills/octocode-code-engineer/src/detectors/security.ts +821 -0
- package/skills/octocode-code-engineer/src/detectors/semantic.ts +758 -0
- package/skills/octocode-code-engineer/src/detectors/shared.ts +49 -0
- package/skills/octocode-code-engineer/src/detectors/test-quality.test.ts +388 -0
- package/skills/octocode-code-engineer/src/detectors/test-quality.ts +367 -0
- package/skills/octocode-code-engineer/src/index.test.ts +4425 -0
- package/skills/octocode-code-engineer/src/index.ts +403 -0
- package/skills/octocode-code-engineer/src/pipeline/cache.test.ts +199 -0
- package/skills/octocode-code-engineer/src/pipeline/cache.ts +130 -0
- package/skills/octocode-code-engineer/src/pipeline/cli.test.ts +493 -0
- package/skills/octocode-code-engineer/src/pipeline/cli.ts +344 -0
- package/skills/octocode-code-engineer/src/pipeline/main.test.ts +174 -0
- package/skills/octocode-code-engineer/src/pipeline/main.ts +1074 -0
- package/skills/octocode-code-engineer/src/pipeline.test.ts +84 -0
- package/skills/octocode-code-engineer/src/reporting/analysis.test.ts +782 -0
- package/skills/octocode-code-engineer/src/reporting/analysis.ts +688 -0
- package/skills/octocode-code-engineer/src/reporting/output-contract.test.ts +463 -0
- package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +421 -0
- package/skills/octocode-code-engineer/src/reporting/summary-md.ts +714 -0
- package/skills/octocode-code-engineer/src/reporting/writer.ts +430 -0
- package/skills/octocode-code-engineer/src/sanity.test.ts +47 -0
- package/skills/octocode-code-engineer/src/types/constants.ts +248 -0
- package/skills/octocode-code-engineer/src/types/index.ts +80 -0
- package/skills/octocode-code-engineer/src/types/interfaces.ts +682 -0
- package/skills/octocode-code-engineer/tsconfig.json +17 -0
- package/skills/octocode-code-engineer/vitest.config.ts +8 -0
- package/skills/octocode-documentation-writer/README.md +113 -0
- package/skills/octocode-documentation-writer/SKILL.md +886 -0
- package/skills/octocode-documentation-writer/references/agent-discovery-analysis.md +453 -0
- package/skills/octocode-documentation-writer/references/agent-documentation-writer.md +255 -0
- package/skills/octocode-documentation-writer/references/agent-engineer-questions.md +247 -0
- package/skills/octocode-documentation-writer/references/agent-orchestrator.md +370 -0
- package/skills/octocode-documentation-writer/references/agent-qa-validator.md +227 -0
- package/skills/octocode-documentation-writer/references/agent-researcher.md +250 -0
- package/skills/octocode-documentation-writer/schemas/analysis-schema.json +886 -0
- package/skills/octocode-documentation-writer/schemas/discovery-tasks.json +96 -0
- package/skills/octocode-documentation-writer/schemas/documentation-structure.json +373 -0
- package/skills/octocode-documentation-writer/schemas/partial-discovery-schema.json +102 -0
- package/skills/octocode-documentation-writer/schemas/partial-research-schema.json +98 -0
- package/skills/octocode-documentation-writer/schemas/qa-results-schema.json +113 -0
- package/skills/octocode-documentation-writer/schemas/questions-schema.json +228 -0
- package/skills/octocode-documentation-writer/schemas/research-schema.json +104 -0
- package/skills/octocode-documentation-writer/schemas/state-schema.json +222 -0
- package/skills/octocode-documentation-writer/schemas/work-assignments-schema.json +74 -0
- package/skills/octocode-plan/SKILL.md +122 -116
- package/skills/octocode-prompt-optimizer/SKILL.md +617 -0
- package/skills/octocode-pull-request-reviewer/README.md +249 -0
- package/skills/octocode-pull-request-reviewer/SKILL.md +479 -0
- package/skills/octocode-pull-request-reviewer/references/dependency-check.md +74 -0
- package/skills/octocode-pull-request-reviewer/references/domain-reviewers.md +24 -0
- package/skills/octocode-pull-request-reviewer/references/execution-lifecycle.md +441 -0
- package/skills/octocode-pull-request-reviewer/references/flow-analysis-protocol.md +64 -0
- package/skills/octocode-pull-request-reviewer/references/output-template.md +174 -0
- package/skills/octocode-pull-request-reviewer/references/parallel-agent-protocol.md +182 -0
- package/skills/octocode-pull-request-reviewer/references/review-guidelines.md +26 -0
- package/skills/octocode-pull-request-reviewer/references/verification-checklist.md +40 -0
- package/skills/octocode-research/.claude/settings.local.json +46 -0
- package/skills/octocode-research/.octocode/plan/code-review-fixes/plan.md +312 -0
- package/skills/octocode-research/.octocode/plan/code-review-fixes/research.md +212 -0
- package/skills/octocode-research/.octocode/plans/NODE_SERVER_START_PLAN.md +755 -0
- package/skills/octocode-research/.octocode/research/code-review/research.md +371 -0
- package/skills/octocode-research/.octocode/review/IMPROVEMENTS.md +391 -0
- package/skills/octocode-research/.octocode/review/REVIEW_PLAN.md +289 -0
- package/skills/octocode-research/.octocode/review/REVIEW_REPORT.md +356 -0
- package/skills/octocode-research/AGENTS.md +349 -0
- package/skills/octocode-research/README.md +494 -0
- package/skills/octocode-research/SKILL.md +652 -274
- package/skills/octocode-research/docs/API_REFERENCE.md +562 -0
- package/skills/octocode-research/docs/ARCHITECTURE.md +554 -0
- package/skills/octocode-research/docs/FLOWS.md +577 -0
- package/skills/octocode-research/docs/OVERVIEW.md +564 -0
- package/skills/octocode-research/docs/SERVER_FLOWS.md +631 -0
- package/skills/octocode-research/ecosystem.config.cjs +88 -0
- package/skills/octocode-research/eslint.config.mjs +27 -0
- package/skills/octocode-research/package.json +84 -0
- package/skills/octocode-research/references/GUARDRAILS.md +40 -0
- package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +178 -0
- package/skills/octocode-research/references/roast-prompt.md +149 -0
- package/skills/octocode-research/scripts/server-init.d.ts +2 -0
- package/skills/octocode-research/scripts/server-init.js +2 -0
- package/skills/octocode-research/scripts/server.d.ts +8 -0
- package/skills/octocode-research/scripts/server.js +445 -0
- package/skills/octocode-research/src/__tests__/integration/circuitBreaker.test.ts +205 -0
- package/skills/octocode-research/src/__tests__/integration/routes.test.ts +374 -0
- package/skills/octocode-research/src/__tests__/unit/circuitBreaker.test.ts +245 -0
- package/skills/octocode-research/src/__tests__/unit/errorHandler.test.ts +183 -0
- package/skills/octocode-research/src/__tests__/unit/httpPreprocess.test.ts +157 -0
- package/skills/octocode-research/src/__tests__/unit/logger.test.ts +143 -0
- package/skills/octocode-research/src/__tests__/unit/queryParser.test.ts +130 -0
- package/skills/octocode-research/src/__tests__/unit/responseBuilder.test.ts +469 -0
- package/skills/octocode-research/src/__tests__/unit/retry.test.ts +205 -0
- package/skills/octocode-research/src/index.ts +186 -0
- package/skills/octocode-research/src/mcpCache.ts +49 -0
- package/skills/octocode-research/src/middleware/errorHandler.ts +65 -0
- package/skills/octocode-research/src/middleware/logger.ts +61 -0
- package/skills/octocode-research/src/middleware/queryParser.ts +115 -0
- package/skills/octocode-research/src/middleware/readiness.ts +17 -0
- package/skills/octocode-research/src/routes/github.ts +197 -0
- package/skills/octocode-research/src/routes/local.ts +175 -0
- package/skills/octocode-research/src/routes/lsp.ts +177 -0
- package/skills/octocode-research/src/routes/package.ts +127 -0
- package/skills/octocode-research/src/routes/prompts.ts +138 -0
- package/skills/octocode-research/src/routes/tools.ts +677 -0
- package/skills/octocode-research/src/server-init.ts +363 -0
- package/skills/octocode-research/src/server.ts +285 -0
- package/skills/octocode-research/src/types/errorGuards.ts +151 -0
- package/skills/octocode-research/src/types/express.d.ts +76 -0
- package/skills/octocode-research/src/types/guards.ts +98 -0
- package/skills/octocode-research/src/types/mcp.ts +119 -0
- package/skills/octocode-research/src/types/responses.ts +199 -0
- package/skills/octocode-research/src/types/toolTypes.ts +33 -0
- package/skills/octocode-research/src/utils/asyncTimeout.ts +116 -0
- package/skills/octocode-research/src/utils/circuitBreaker.ts +492 -0
- package/skills/octocode-research/src/utils/colors.ts +53 -0
- package/skills/octocode-research/src/utils/errorQueue.ts +71 -0
- package/skills/octocode-research/src/utils/logEmoji.ts +103 -0
- package/skills/octocode-research/src/utils/logger.ts +413 -0
- package/skills/octocode-research/src/utils/resilience.ts +169 -0
- package/skills/octocode-research/src/utils/responseBuilder.ts +495 -0
- package/skills/octocode-research/src/utils/responseFactory.ts +100 -0
- package/skills/octocode-research/src/utils/responseParser.ts +272 -0
- package/skills/octocode-research/src/utils/retry.ts +280 -0
- package/skills/octocode-research/src/utils/routeFactory.ts +117 -0
- package/skills/octocode-research/src/utils/url.ts +20 -0
- package/skills/octocode-research/src/validation/httpPreprocess.ts +155 -0
- package/skills/octocode-research/src/validation/index.ts +2 -0
- package/skills/octocode-research/src/validation/schemas.ts +578 -0
- package/skills/octocode-research/src/validation/toolCallSchema.ts +132 -0
- package/skills/octocode-research/tsconfig.json +21 -0
- package/skills/octocode-research/tsdown.config.ts +42 -0
- package/skills/octocode-research/vitest.config.ts +20 -0
- package/skills/octocode-researcher/SKILL.md +461 -0
- package/skills/octocode-researcher/references/fallbacks.md +120 -0
- package/skills/{octocode-local-search → octocode-researcher}/references/tool-reference.md +132 -49
- package/skills/{octocode-local-search → octocode-researcher}/references/workflow-patterns.md +204 -4
- package/skills/octocode-rfc-generator/SKILL.md +223 -0
- package/skills/octocode-rfc-generator/references/rfc-template.md +193 -0
- package/skills/octocode-roast/SKILL.md +63 -21
- package/skills/octocode-implement/SKILL.md +0 -293
- package/skills/octocode-implement/references/execution-phases.md +0 -317
- package/skills/octocode-implement/references/tool-reference.md +0 -403
- package/skills/octocode-implement/references/workflow-patterns.md +0 -385
- package/skills/octocode-local-search/SKILL.md +0 -449
- package/skills/octocode-pr-review/SKILL.md +0 -391
- package/skills/octocode-pr-review/references/domain-reviewers.md +0 -105
- package/skills/octocode-pr-review/references/execution-lifecycle.md +0 -116
- package/skills/octocode-pr-review/references/research-flows.md +0 -75
- package/skills/octocode-research/references/tool-reference.md +0 -304
- package/skills/octocode-research/references/workflow-patterns.md +0 -325
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response utilities for route handlers.
|
|
3
|
+
* Provides type-safe extractors and helpers for processing MCP responses.
|
|
4
|
+
*
|
|
5
|
+
* @module utils/responseFactory
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FileMatch, PaginationInfo } from '../types/responses.js';
|
|
9
|
+
import { extractFiles, extractPagination, extractTotalMatches } from '../types/responses.js';
|
|
10
|
+
import { isObject, hasProperty, isArray, hasStringProperty, hasNumberProperty } from '../types/guards.js';
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Common Extractors (Type-Safe)
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract file matches from search results with proper typing
|
|
18
|
+
*/
|
|
19
|
+
export function extractFileMatches(data: unknown): FileMatch[] {
|
|
20
|
+
return extractFiles(data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extract pagination info with proper typing
|
|
25
|
+
*/
|
|
26
|
+
export function extractPaginationInfo(data: unknown): PaginationInfo | undefined {
|
|
27
|
+
return extractPagination(data);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract total match count with proper typing
|
|
32
|
+
*/
|
|
33
|
+
export function extractMatchCount(data: unknown): number {
|
|
34
|
+
return extractTotalMatches(data);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Safely extract string property
|
|
39
|
+
*/
|
|
40
|
+
export function safeString(obj: unknown, key: string, fallback = ''): string {
|
|
41
|
+
if (hasStringProperty(obj, key)) {
|
|
42
|
+
return obj[key];
|
|
43
|
+
}
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Safely extract number property
|
|
49
|
+
*/
|
|
50
|
+
export function safeNumber(obj: unknown, key: string, fallback = 0): number {
|
|
51
|
+
if (hasNumberProperty(obj, key)) {
|
|
52
|
+
return obj[key];
|
|
53
|
+
}
|
|
54
|
+
return fallback;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Safely extract array property
|
|
59
|
+
*/
|
|
60
|
+
export function safeArray<T>(obj: unknown, key: string): T[] {
|
|
61
|
+
if (isObject(obj) && hasProperty(obj, key) && isArray(obj[key])) {
|
|
62
|
+
return obj[key] as T[];
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extract match locations from a file result
|
|
69
|
+
*/
|
|
70
|
+
export function extractMatchLocations(matches: unknown[]): Array<{
|
|
71
|
+
line: number;
|
|
72
|
+
column?: number;
|
|
73
|
+
value?: string;
|
|
74
|
+
byteOffset?: number;
|
|
75
|
+
charOffset?: number;
|
|
76
|
+
}> {
|
|
77
|
+
return matches.map((m) => {
|
|
78
|
+
if (!isObject(m)) return { line: 0 };
|
|
79
|
+
return {
|
|
80
|
+
line: safeNumber(m, 'line', 0),
|
|
81
|
+
column: hasNumberProperty(m, 'column') ? m.column : undefined,
|
|
82
|
+
value: hasStringProperty(m, 'value') ? m.value.trim() : undefined,
|
|
83
|
+
byteOffset: hasNumberProperty(m, 'byteOffset') ? m.byteOffset : undefined,
|
|
84
|
+
charOffset: hasNumberProperty(m, 'charOffset') ? m.charOffset : undefined,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Transform pagination from MCP format to skill format
|
|
91
|
+
*/
|
|
92
|
+
export function transformPagination(pagination: unknown): { page: number; total: number; hasMore: boolean } | undefined {
|
|
93
|
+
if (!isObject(pagination)) return undefined;
|
|
94
|
+
|
|
95
|
+
const currentPage = safeNumber(pagination, 'currentPage', 1);
|
|
96
|
+
const totalPages = safeNumber(pagination, 'totalPages', 1);
|
|
97
|
+
const hasMore = hasProperty(pagination, 'hasMore') && pagination.hasMore === true;
|
|
98
|
+
|
|
99
|
+
return { page: currentPage, total: totalPages, hasMore };
|
|
100
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse MCP tool responses to extract structured data
|
|
3
|
+
*
|
|
4
|
+
* MCP tools return responses in this format:
|
|
5
|
+
* {
|
|
6
|
+
* content: [{ type: 'text', text: yamlString }],
|
|
7
|
+
* structuredContent?: object, // Not always present
|
|
8
|
+
* isError: boolean
|
|
9
|
+
* }
|
|
10
|
+
*
|
|
11
|
+
* The YAML contains:
|
|
12
|
+
* - instructions: string
|
|
13
|
+
* - results: [{ id, status, data, mainResearchGoal, researchGoal, reasoning }]
|
|
14
|
+
* - hasResultsStatusHints / emptyStatusHints / errorStatusHints: string[]
|
|
15
|
+
*
|
|
16
|
+
* This utility extracts data AND preserves the valuable MCP hints.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import yaml from 'js-yaml';
|
|
20
|
+
|
|
21
|
+
interface McpToolResponse {
|
|
22
|
+
content?: Array<{ type: string; text?: string }>;
|
|
23
|
+
structuredContent?: Record<string, unknown>;
|
|
24
|
+
isError?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Research context from the MCP response
|
|
29
|
+
*/
|
|
30
|
+
export interface ResearchContext {
|
|
31
|
+
mainResearchGoal?: string;
|
|
32
|
+
researchGoal?: string;
|
|
33
|
+
reasoning?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Single result item from bulk response
|
|
38
|
+
*/
|
|
39
|
+
export interface BulkResultItem {
|
|
40
|
+
id: number;
|
|
41
|
+
status: 'hasResults' | 'empty' | 'error';
|
|
42
|
+
data: Record<string, unknown>;
|
|
43
|
+
research: ResearchContext;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parsed response with data, hints, and research context
|
|
48
|
+
*/
|
|
49
|
+
export interface ParsedResponse {
|
|
50
|
+
data: Record<string, unknown>;
|
|
51
|
+
isError: boolean;
|
|
52
|
+
/** MCP workflow hints - critical for agent guidance */
|
|
53
|
+
hints: string[];
|
|
54
|
+
/** Research context preserved from the query */
|
|
55
|
+
research: ResearchContext;
|
|
56
|
+
/** Raw status from MCP (hasResults, empty, error) */
|
|
57
|
+
status: 'hasResults' | 'empty' | 'error' | 'unknown';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parsed bulk response with all results
|
|
62
|
+
*/
|
|
63
|
+
export interface ParsedBulkResponse {
|
|
64
|
+
/** All results from bulk query */
|
|
65
|
+
results: BulkResultItem[];
|
|
66
|
+
/** Categorized hints by status */
|
|
67
|
+
hints: {
|
|
68
|
+
hasResults: string[];
|
|
69
|
+
empty: string[];
|
|
70
|
+
error: string[];
|
|
71
|
+
};
|
|
72
|
+
/** Bulk operation instructions */
|
|
73
|
+
instructions: string;
|
|
74
|
+
/** True if all queries failed */
|
|
75
|
+
isError: boolean;
|
|
76
|
+
/** Count of results by status */
|
|
77
|
+
counts: {
|
|
78
|
+
total: number;
|
|
79
|
+
hasResults: number;
|
|
80
|
+
empty: number;
|
|
81
|
+
error: number;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract structured data from an MCP tool response
|
|
87
|
+
*
|
|
88
|
+
* Priority:
|
|
89
|
+
* 1. Use structuredContent if available (direct object access)
|
|
90
|
+
* 2. Parse YAML from content[0].text and extract results[0].data
|
|
91
|
+
*
|
|
92
|
+
* Also extracts:
|
|
93
|
+
* - MCP hints (hasResultsStatusHints, emptyStatusHints, errorStatusHints)
|
|
94
|
+
* - Research context (mainResearchGoal, researchGoal, reasoning)
|
|
95
|
+
*/
|
|
96
|
+
export function parseToolResponse(response: McpToolResponse): ParsedResponse {
|
|
97
|
+
const emptyResult: ParsedResponse = {
|
|
98
|
+
data: {},
|
|
99
|
+
isError: true,
|
|
100
|
+
hints: [],
|
|
101
|
+
research: {},
|
|
102
|
+
status: 'unknown',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Option 1: structuredContent is available (preferred for data, but no hints)
|
|
106
|
+
if (response.structuredContent && typeof response.structuredContent === 'object') {
|
|
107
|
+
return {
|
|
108
|
+
data: response.structuredContent,
|
|
109
|
+
isError: Boolean(response.isError),
|
|
110
|
+
hints: [], // structuredContent doesn't include hints
|
|
111
|
+
research: {},
|
|
112
|
+
status: 'unknown',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Option 2: Parse YAML from content text (includes hints!)
|
|
117
|
+
if (response.content && response.content[0]?.text) {
|
|
118
|
+
try {
|
|
119
|
+
const parsed = yaml.load(response.content[0].text) as Record<string, unknown>;
|
|
120
|
+
|
|
121
|
+
// Extract hints based on status
|
|
122
|
+
let hints: string[] = [];
|
|
123
|
+
if (Array.isArray(parsed.hasResultsStatusHints)) {
|
|
124
|
+
hints = parsed.hasResultsStatusHints as string[];
|
|
125
|
+
} else if (Array.isArray(parsed.emptyStatusHints)) {
|
|
126
|
+
hints = parsed.emptyStatusHints as string[];
|
|
127
|
+
} else if (Array.isArray(parsed.errorStatusHints)) {
|
|
128
|
+
hints = parsed.errorStatusHints as string[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Extract data from results[0].data (bulk response format)
|
|
132
|
+
if (parsed && Array.isArray(parsed.results) && parsed.results.length > 0) {
|
|
133
|
+
const firstResult = parsed.results[0] as Record<string, unknown>;
|
|
134
|
+
const resultStatus = String(firstResult.status || 'unknown');
|
|
135
|
+
|
|
136
|
+
// Extract research context
|
|
137
|
+
const research: ResearchContext = {
|
|
138
|
+
mainResearchGoal: typeof firstResult.mainResearchGoal === 'string'
|
|
139
|
+
? firstResult.mainResearchGoal : undefined,
|
|
140
|
+
researchGoal: typeof firstResult.researchGoal === 'string'
|
|
141
|
+
? firstResult.researchGoal : undefined,
|
|
142
|
+
reasoning: typeof firstResult.reasoning === 'string'
|
|
143
|
+
? firstResult.reasoning : undefined,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (firstResult.data && typeof firstResult.data === 'object') {
|
|
147
|
+
return {
|
|
148
|
+
data: firstResult.data as Record<string, unknown>,
|
|
149
|
+
isError: resultStatus === 'error',
|
|
150
|
+
hints,
|
|
151
|
+
research,
|
|
152
|
+
status: resultStatus as ParsedResponse['status'],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Fallback: return parsed object directly
|
|
158
|
+
return {
|
|
159
|
+
data: parsed || {},
|
|
160
|
+
isError: Boolean(response.isError),
|
|
161
|
+
hints,
|
|
162
|
+
research: {},
|
|
163
|
+
status: 'unknown',
|
|
164
|
+
};
|
|
165
|
+
} catch {
|
|
166
|
+
// YAML parsing failed, return empty
|
|
167
|
+
return emptyResult;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// No data found
|
|
172
|
+
return emptyResult;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Convenience function to get data field with type safety
|
|
177
|
+
*/
|
|
178
|
+
export function getDataField<T>(
|
|
179
|
+
response: McpToolResponse,
|
|
180
|
+
field: string,
|
|
181
|
+
defaultValue: T
|
|
182
|
+
): T {
|
|
183
|
+
const { data } = parseToolResponse(response);
|
|
184
|
+
const value = data[field];
|
|
185
|
+
return value !== undefined ? (value as T) : defaultValue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parse ALL results from a bulk MCP tool response.
|
|
190
|
+
* Use this when handling multiple queries to get all results.
|
|
191
|
+
*
|
|
192
|
+
* @param response - Raw MCP tool response
|
|
193
|
+
* @returns ParsedBulkResponse with all results and categorized hints
|
|
194
|
+
*/
|
|
195
|
+
export function parseToolResponseBulk(response: McpToolResponse): ParsedBulkResponse {
|
|
196
|
+
const emptyResult: ParsedBulkResponse = {
|
|
197
|
+
results: [],
|
|
198
|
+
hints: { hasResults: [], empty: [], error: [] },
|
|
199
|
+
instructions: '',
|
|
200
|
+
isError: true,
|
|
201
|
+
counts: { total: 0, hasResults: 0, empty: 0, error: 0 },
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Parse YAML from content text
|
|
205
|
+
if (!response.content || !response.content[0]?.text) {
|
|
206
|
+
return emptyResult;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const parsed = yaml.load(response.content[0].text) as Record<string, unknown>;
|
|
211
|
+
|
|
212
|
+
if (!parsed || !Array.isArray(parsed.results)) {
|
|
213
|
+
return emptyResult;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Extract all results
|
|
217
|
+
const results: BulkResultItem[] = [];
|
|
218
|
+
let hasResultsCount = 0;
|
|
219
|
+
let emptyCount = 0;
|
|
220
|
+
let errorCount = 0;
|
|
221
|
+
|
|
222
|
+
for (const result of parsed.results) {
|
|
223
|
+
if (!result || typeof result !== 'object') continue;
|
|
224
|
+
|
|
225
|
+
const r = result as Record<string, unknown>;
|
|
226
|
+
const status = String(r.status || 'unknown') as BulkResultItem['status'];
|
|
227
|
+
|
|
228
|
+
if (status === 'hasResults') hasResultsCount++;
|
|
229
|
+
else if (status === 'empty') emptyCount++;
|
|
230
|
+
else if (status === 'error') errorCount++;
|
|
231
|
+
|
|
232
|
+
results.push({
|
|
233
|
+
id: typeof r.id === 'number' ? r.id : results.length + 1,
|
|
234
|
+
status,
|
|
235
|
+
data: (r.data && typeof r.data === 'object' ? r.data : {}) as Record<string, unknown>,
|
|
236
|
+
research: {
|
|
237
|
+
mainResearchGoal: typeof r.mainResearchGoal === 'string' ? r.mainResearchGoal : undefined,
|
|
238
|
+
researchGoal: typeof r.researchGoal === 'string' ? r.researchGoal : undefined,
|
|
239
|
+
reasoning: typeof r.reasoning === 'string' ? r.reasoning : undefined,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Extract categorized hints
|
|
245
|
+
const hints = {
|
|
246
|
+
hasResults: Array.isArray(parsed.hasResultsStatusHints)
|
|
247
|
+
? (parsed.hasResultsStatusHints as string[])
|
|
248
|
+
: [],
|
|
249
|
+
empty: Array.isArray(parsed.emptyStatusHints)
|
|
250
|
+
? (parsed.emptyStatusHints as string[])
|
|
251
|
+
: [],
|
|
252
|
+
error: Array.isArray(parsed.errorStatusHints)
|
|
253
|
+
? (parsed.errorStatusHints as string[])
|
|
254
|
+
: [],
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
results,
|
|
259
|
+
hints,
|
|
260
|
+
instructions: typeof parsed.instructions === 'string' ? parsed.instructions : '',
|
|
261
|
+
isError: errorCount === results.length && results.length > 0,
|
|
262
|
+
counts: {
|
|
263
|
+
total: results.length,
|
|
264
|
+
hasResults: hasResultsCount,
|
|
265
|
+
empty: emptyCount,
|
|
266
|
+
error: errorCount,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
} catch {
|
|
270
|
+
return emptyResult;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry utilities with exponential backoff for resilient tool calls.
|
|
3
|
+
*
|
|
4
|
+
* @module utils/retry
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
getErrorStatus,
|
|
9
|
+
getErrorHeader,
|
|
10
|
+
hasStatusIn,
|
|
11
|
+
hasCodeIn,
|
|
12
|
+
messageMatches,
|
|
13
|
+
} from '../types/errorGuards.js';
|
|
14
|
+
|
|
15
|
+
export interface RetryConfig {
|
|
16
|
+
maxAttempts: number;
|
|
17
|
+
initialDelayMs: number;
|
|
18
|
+
maxDelayMs: number;
|
|
19
|
+
backoffMultiplier: number;
|
|
20
|
+
retryOn: (error: unknown) => boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pre-configured retry strategies for different tool categories
|
|
25
|
+
*/
|
|
26
|
+
export const RETRY_CONFIGS = {
|
|
27
|
+
/**
|
|
28
|
+
* LSP tools - may need warm-up time
|
|
29
|
+
*/
|
|
30
|
+
lsp: {
|
|
31
|
+
maxAttempts: 3,
|
|
32
|
+
initialDelayMs: 500,
|
|
33
|
+
maxDelayMs: 5000,
|
|
34
|
+
backoffMultiplier: 2,
|
|
35
|
+
retryOn: (err: unknown) =>
|
|
36
|
+
isLspNotReady(err) || isTimeout(err) || isConnectionRefused(err),
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* GitHub API - rate limits and server errors
|
|
41
|
+
*/
|
|
42
|
+
github: {
|
|
43
|
+
maxAttempts: 3,
|
|
44
|
+
initialDelayMs: 1000,
|
|
45
|
+
maxDelayMs: 30000,
|
|
46
|
+
backoffMultiplier: 3,
|
|
47
|
+
retryOn: (err: unknown) =>
|
|
48
|
+
isRateLimited(err) || isServerError(err) || isTimeout(err),
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Package APIs (npm/PyPI) - similar to GitHub
|
|
53
|
+
*/
|
|
54
|
+
package: {
|
|
55
|
+
maxAttempts: 3,
|
|
56
|
+
initialDelayMs: 1000,
|
|
57
|
+
maxDelayMs: 15000,
|
|
58
|
+
backoffMultiplier: 2,
|
|
59
|
+
retryOn: (err: unknown) =>
|
|
60
|
+
isRateLimited(err) || isServerError(err) || isTimeout(err),
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Local file operations - quick retries
|
|
65
|
+
*/
|
|
66
|
+
local: {
|
|
67
|
+
maxAttempts: 2,
|
|
68
|
+
initialDelayMs: 100,
|
|
69
|
+
maxDelayMs: 1000,
|
|
70
|
+
backoffMultiplier: 2,
|
|
71
|
+
retryOn: (err: unknown) => isFileBusy(err) || isTimeout(err),
|
|
72
|
+
},
|
|
73
|
+
} as const satisfies Record<string, RetryConfig>;
|
|
74
|
+
|
|
75
|
+
type RetryCategory = keyof typeof RETRY_CONFIGS;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Context for retry logging
|
|
79
|
+
*/
|
|
80
|
+
interface RetryContext {
|
|
81
|
+
tool: string;
|
|
82
|
+
params?: unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Execute an operation with retry logic and exponential backoff.
|
|
87
|
+
*
|
|
88
|
+
* @param operation - Async function to execute
|
|
89
|
+
* @param config - Retry configuration
|
|
90
|
+
* @param context - Optional context for logging
|
|
91
|
+
* @returns Result of the operation
|
|
92
|
+
* @throws Last error if all retries exhausted
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const result = await withRetry(
|
|
97
|
+
* () => lspGotoDefinition({ queries }),
|
|
98
|
+
* RETRY_CONFIGS.lsp,
|
|
99
|
+
* { tool: 'lspGotoDefinition' }
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export async function withRetry<T>(
|
|
104
|
+
operation: () => Promise<T>,
|
|
105
|
+
config: RetryConfig,
|
|
106
|
+
context?: RetryContext
|
|
107
|
+
): Promise<T> {
|
|
108
|
+
let lastError: unknown;
|
|
109
|
+
let delay = config.initialDelayMs;
|
|
110
|
+
|
|
111
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
112
|
+
try {
|
|
113
|
+
return await operation();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
lastError = error;
|
|
116
|
+
|
|
117
|
+
// Don't retry if error type doesn't match or last attempt
|
|
118
|
+
if (!config.retryOn(error) || attempt === config.maxAttempts) {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Log retry attempt
|
|
123
|
+
const toolName = context?.tool || 'operation';
|
|
124
|
+
console.log(
|
|
125
|
+
`⟳ Retry ${attempt}/${config.maxAttempts} for ${toolName} in ${delay}ms`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
await sleep(delay);
|
|
129
|
+
delay = Math.min(delay * config.backoffMultiplier, config.maxDelayMs);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
throw lastError;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Convenience wrapper that selects config by category
|
|
138
|
+
*/
|
|
139
|
+
export async function withCategoryRetry<T>(
|
|
140
|
+
category: RetryCategory,
|
|
141
|
+
operation: () => Promise<T>,
|
|
142
|
+
context?: RetryContext
|
|
143
|
+
): Promise<T> {
|
|
144
|
+
return withRetry(operation, RETRY_CONFIGS[category], context);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// =============================================================================
|
|
148
|
+
// Error Type Detection
|
|
149
|
+
// =============================================================================
|
|
150
|
+
|
|
151
|
+
// Error codes for reliable detection (check these first)
|
|
152
|
+
const RATE_LIMIT_CODES = [403, 429] as const;
|
|
153
|
+
const RATE_LIMIT_PATTERNS = [/rate\s*limit/i, /too\s*many\s*requests/i] as const;
|
|
154
|
+
|
|
155
|
+
const LSP_ERROR_CODES = ['LSP_NOT_READY', 'LSP_NOT_INITIALIZED', 'ECONNREFUSED'] as const;
|
|
156
|
+
const LSP_ERROR_PATTERNS = [/not initialized/i, /server not started/i, /lsp.*not.*ready/i] as const;
|
|
157
|
+
|
|
158
|
+
const TIMEOUT_CODES = ['ETIMEDOUT', 'ESOCKETTIMEDOUT', 'ECONNRESET'] as const;
|
|
159
|
+
const TIMEOUT_PATTERNS = [/timeout/i, /timed?\s*out/i] as const;
|
|
160
|
+
|
|
161
|
+
const FILE_BUSY_CODES = ['EBUSY', 'EAGAIN', 'ENOTEMPTY'] as const;
|
|
162
|
+
|
|
163
|
+
const CONNECTION_REFUSED_CODES = ['ECONNREFUSED', 'ENOTFOUND', 'EHOSTUNREACH'] as const;
|
|
164
|
+
|
|
165
|
+
const SYMBOL_NOT_FOUND_CODES = ['SYMBOL_NOT_FOUND', 'NOT_FOUND'] as const;
|
|
166
|
+
const SYMBOL_NOT_FOUND_PATTERNS = [/symbol\s*not\s*found/i, /definition\s*not\s*found/i] as const;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if error indicates GitHub rate limiting
|
|
170
|
+
*/
|
|
171
|
+
function isRateLimited(err: unknown): boolean {
|
|
172
|
+
// Check status codes first (more reliable)
|
|
173
|
+
if (hasStatusIn(err, RATE_LIMIT_CODES)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Fall back to message patterns
|
|
178
|
+
return messageMatches(err, RATE_LIMIT_PATTERNS);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check if error indicates LSP server not ready
|
|
183
|
+
*/
|
|
184
|
+
function isLspNotReady(err: unknown): boolean {
|
|
185
|
+
// Check error codes first (more reliable)
|
|
186
|
+
if (hasCodeIn(err, LSP_ERROR_CODES)) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Fall back to message patterns
|
|
191
|
+
return messageMatches(err, LSP_ERROR_PATTERNS);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check if error is a timeout
|
|
196
|
+
*/
|
|
197
|
+
function isTimeout(err: unknown): boolean {
|
|
198
|
+
// Check error codes first (more reliable)
|
|
199
|
+
if (hasCodeIn(err, TIMEOUT_CODES)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Fall back to message patterns
|
|
204
|
+
return messageMatches(err, TIMEOUT_PATTERNS);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Check if error is a server error (5xx)
|
|
209
|
+
*/
|
|
210
|
+
function isServerError(err: unknown): boolean {
|
|
211
|
+
const status = getErrorStatus(err);
|
|
212
|
+
return status !== undefined && status >= 500 && status < 600;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if file is busy/locked
|
|
217
|
+
*/
|
|
218
|
+
function isFileBusy(err: unknown): boolean {
|
|
219
|
+
return hasCodeIn(err, FILE_BUSY_CODES);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check if connection was refused
|
|
224
|
+
*/
|
|
225
|
+
function isConnectionRefused(err: unknown): boolean {
|
|
226
|
+
return hasCodeIn(err, CONNECTION_REFUSED_CODES);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if symbol was not found (LSP)
|
|
231
|
+
*/
|
|
232
|
+
export function isSymbolNotFound(err: unknown): boolean {
|
|
233
|
+
// Check error codes first (more reliable)
|
|
234
|
+
if (hasCodeIn(err, SYMBOL_NOT_FOUND_CODES)) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Fall back to message patterns
|
|
239
|
+
return messageMatches(err, SYMBOL_NOT_FOUND_PATTERNS);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// =============================================================================
|
|
243
|
+
// Utilities
|
|
244
|
+
// =============================================================================
|
|
245
|
+
|
|
246
|
+
const sleep = (ms: number): Promise<void> =>
|
|
247
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get suggested retry delay from error (if available)
|
|
251
|
+
*/
|
|
252
|
+
export function getRetryAfter(err: unknown): number | null {
|
|
253
|
+
if (isRateLimited(err)) {
|
|
254
|
+
const retryAfter = getErrorHeader(err, 'retry-after');
|
|
255
|
+
if (retryAfter) {
|
|
256
|
+
return parseInt(retryAfter, 10) * 1000;
|
|
257
|
+
}
|
|
258
|
+
return 60000; // Default 60s for rate limits
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (isLspNotReady(err)) {
|
|
262
|
+
return 3000; // 3s for LSP warm-up
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Determine if an error is recoverable
|
|
270
|
+
*/
|
|
271
|
+
export function isRecoverable(err: unknown): boolean {
|
|
272
|
+
return (
|
|
273
|
+
isRateLimited(err) ||
|
|
274
|
+
isLspNotReady(err) ||
|
|
275
|
+
isTimeout(err) ||
|
|
276
|
+
isServerError(err) ||
|
|
277
|
+
isFileBusy(err) ||
|
|
278
|
+
isConnectionRefused(err)
|
|
279
|
+
);
|
|
280
|
+
}
|