octocode-cli 1.2.5 → 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.
Files changed (303) hide show
  1. package/LICENSE +21 -63
  2. package/README.md +86 -109
  3. package/out/octocode-cli.js +7027 -7014
  4. package/package.json +8 -6
  5. package/skills/README.md +97 -120
  6. package/skills/octocode-code-engineer/.claude/settings.local.json +18 -0
  7. package/skills/octocode-code-engineer/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +255 -0
  8. package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +223 -0
  9. package/skills/octocode-code-engineer/README.md +178 -0
  10. package/skills/octocode-code-engineer/SKILL.md +418 -0
  11. package/skills/octocode-code-engineer/coverage/architecture.ts.html +7828 -0
  12. package/skills/octocode-code-engineer/coverage/ast-helpers.ts.html +211 -0
  13. package/skills/octocode-code-engineer/coverage/ast-search.ts.html +1795 -0
  14. package/skills/octocode-code-engineer/coverage/base.css +224 -0
  15. package/skills/octocode-code-engineer/coverage/block-navigation.js +87 -0
  16. package/skills/octocode-code-engineer/coverage/cache.ts.html +376 -0
  17. package/skills/octocode-code-engineer/coverage/cli.ts.html +982 -0
  18. package/skills/octocode-code-engineer/coverage/clover.xml +3217 -0
  19. package/skills/octocode-code-engineer/coverage/collect-effects.ts.html +664 -0
  20. package/skills/octocode-code-engineer/coverage/collect-input-sources.ts.html +577 -0
  21. package/skills/octocode-code-engineer/coverage/collect-performance.ts.html +331 -0
  22. package/skills/octocode-code-engineer/coverage/collect-prototype-pollution.ts.html +421 -0
  23. package/skills/octocode-code-engineer/coverage/collect-security.ts.html +604 -0
  24. package/skills/octocode-code-engineer/coverage/collect-test-profile.ts.html +589 -0
  25. package/skills/octocode-code-engineer/coverage/coverage-final.json +30 -0
  26. package/skills/octocode-code-engineer/coverage/dependencies.ts.html +997 -0
  27. package/skills/octocode-code-engineer/coverage/dependency-summary.ts.html +688 -0
  28. package/skills/octocode-code-engineer/coverage/discovery.ts.html +322 -0
  29. package/skills/octocode-code-engineer/coverage/favicon.png +0 -0
  30. package/skills/octocode-code-engineer/coverage/graph-analytics.ts.html +1510 -0
  31. package/skills/octocode-code-engineer/coverage/index.html +536 -0
  32. package/skills/octocode-code-engineer/coverage/index.ts.html +826 -0
  33. package/skills/octocode-code-engineer/coverage/metrics.ts.html +553 -0
  34. package/skills/octocode-code-engineer/coverage/pipeline.ts.html +2044 -0
  35. package/skills/octocode-code-engineer/coverage/prettify.css +1 -0
  36. package/skills/octocode-code-engineer/coverage/prettify.js +2 -0
  37. package/skills/octocode-code-engineer/coverage/report-analysis.ts.html +1570 -0
  38. package/skills/octocode-code-engineer/coverage/report-writer.ts.html +1102 -0
  39. package/skills/octocode-code-engineer/coverage/security-detectors.ts.html +1747 -0
  40. package/skills/octocode-code-engineer/coverage/semantic-detectors.ts.html +2152 -0
  41. package/skills/octocode-code-engineer/coverage/semantic.ts.html +1897 -0
  42. package/skills/octocode-code-engineer/coverage/sort-arrow-sprite.png +0 -0
  43. package/skills/octocode-code-engineer/coverage/sorter.js +210 -0
  44. package/skills/octocode-code-engineer/coverage/summary-md.ts.html +1222 -0
  45. package/skills/octocode-code-engineer/coverage/test-quality-detectors.ts.html +1039 -0
  46. package/skills/octocode-code-engineer/coverage/tree-sitter-analyzer.ts.html +955 -0
  47. package/skills/octocode-code-engineer/coverage/ts-analyzer.ts.html +1213 -0
  48. package/skills/octocode-code-engineer/coverage/types.ts.html +2473 -0
  49. package/skills/octocode-code-engineer/coverage/utils.ts.html +820 -0
  50. package/skills/octocode-code-engineer/eslint.config.mjs +54 -0
  51. package/skills/octocode-code-engineer/minify-scripts.mjs +32 -0
  52. package/skills/octocode-code-engineer/package.json +54 -0
  53. package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +95 -0
  54. package/skills/octocode-code-engineer/references/architecture-techniques.md +121 -0
  55. package/skills/octocode-code-engineer/references/ast-search.md +210 -0
  56. package/skills/octocode-code-engineer/references/ast-tree-search.md +151 -0
  57. package/skills/octocode-code-engineer/references/cli-reference.md +167 -0
  58. package/skills/octocode-code-engineer/references/concepts.md +107 -0
  59. package/skills/octocode-code-engineer/references/finding-categories.md +128 -0
  60. package/skills/octocode-code-engineer/references/improvement-roadmap.md +304 -0
  61. package/skills/octocode-code-engineer/references/output-files.md +144 -0
  62. package/skills/octocode-code-engineer/references/playbooks.md +204 -0
  63. package/skills/octocode-code-engineer/references/present-results.md +136 -0
  64. package/skills/octocode-code-engineer/references/tool-workflows.md +566 -0
  65. package/skills/octocode-code-engineer/references/validate-investigate.md +225 -0
  66. package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +1 -0
  67. package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +1 -0
  68. package/skills/octocode-code-engineer/scripts/analysis/discovery.js +1 -0
  69. package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +1 -0
  70. package/skills/octocode-code-engineer/scripts/analysis/semantic.js +1 -0
  71. package/skills/octocode-code-engineer/scripts/ast/helpers.js +1 -0
  72. package/skills/octocode-code-engineer/scripts/ast/metrics.js +1 -0
  73. package/skills/octocode-code-engineer/scripts/ast/search.js +2 -0
  74. package/skills/octocode-code-engineer/scripts/ast/tree-search.js +2 -0
  75. package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +1 -0
  76. package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +1 -0
  77. package/skills/octocode-code-engineer/scripts/collectors/chains.js +1 -0
  78. package/skills/octocode-code-engineer/scripts/collectors/effects.js +1 -0
  79. package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +1 -0
  80. package/skills/octocode-code-engineer/scripts/collectors/performance.js +1 -0
  81. package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +1 -0
  82. package/skills/octocode-code-engineer/scripts/collectors/security.js +1 -0
  83. package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +1 -0
  84. package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +1 -0
  85. package/skills/octocode-code-engineer/scripts/common/utils.js +1 -0
  86. package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +1 -0
  87. package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +1 -0
  88. package/skills/octocode-code-engineer/scripts/detectors/coupling.js +1 -0
  89. package/skills/octocode-code-engineer/scripts/detectors/cycle.js +1 -0
  90. package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +1 -0
  91. package/skills/octocode-code-engineer/scripts/detectors/import-style.js +1 -0
  92. package/skills/octocode-code-engineer/scripts/detectors/index.js +1 -0
  93. package/skills/octocode-code-engineer/scripts/detectors/security.js +1 -0
  94. package/skills/octocode-code-engineer/scripts/detectors/semantic.js +1 -0
  95. package/skills/octocode-code-engineer/scripts/detectors/shared.js +1 -0
  96. package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +1 -0
  97. package/skills/octocode-code-engineer/scripts/index.js +1 -0
  98. package/skills/octocode-code-engineer/scripts/pipeline/cache.js +1 -0
  99. package/skills/octocode-code-engineer/scripts/pipeline/cli.js +1 -0
  100. package/skills/octocode-code-engineer/scripts/pipeline/main.js +2 -0
  101. package/skills/octocode-code-engineer/scripts/reporting/analysis.js +1 -0
  102. package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +1 -0
  103. package/skills/octocode-code-engineer/scripts/reporting/writer.js +1 -0
  104. package/skills/octocode-code-engineer/scripts/types/constants.js +1 -0
  105. package/skills/octocode-code-engineer/scripts/types/index.js +1 -0
  106. package/skills/octocode-code-engineer/scripts/types/interfaces.js +1 -0
  107. package/skills/octocode-code-engineer/src/analysis/dependencies.test.ts +545 -0
  108. package/skills/octocode-code-engineer/src/analysis/dependencies.ts +406 -0
  109. package/skills/octocode-code-engineer/src/analysis/dependency-summary.test.ts +566 -0
  110. package/skills/octocode-code-engineer/src/analysis/dependency-summary.ts +257 -0
  111. package/skills/octocode-code-engineer/src/analysis/discovery.test.ts +420 -0
  112. package/skills/octocode-code-engineer/src/analysis/discovery.ts +87 -0
  113. package/skills/octocode-code-engineer/src/analysis/graph-analytics.test.ts +449 -0
  114. package/skills/octocode-code-engineer/src/analysis/graph-analytics.ts +534 -0
  115. package/skills/octocode-code-engineer/src/analysis/semantic.test.ts +1533 -0
  116. package/skills/octocode-code-engineer/src/analysis/semantic.ts +830 -0
  117. package/skills/octocode-code-engineer/src/ast/helpers.test.ts +185 -0
  118. package/skills/octocode-code-engineer/src/ast/helpers.ts +62 -0
  119. package/skills/octocode-code-engineer/src/ast/metrics.test.ts +304 -0
  120. package/skills/octocode-code-engineer/src/ast/metrics.ts +204 -0
  121. package/skills/octocode-code-engineer/src/ast/search.test.ts +647 -0
  122. package/skills/octocode-code-engineer/src/ast/search.ts +648 -0
  123. package/skills/octocode-code-engineer/src/ast/tree-search.test.ts +199 -0
  124. package/skills/octocode-code-engineer/src/ast/tree-search.ts +392 -0
  125. package/skills/octocode-code-engineer/src/ast/tree-sitter.test.ts +407 -0
  126. package/skills/octocode-code-engineer/src/ast/tree-sitter.ts +402 -0
  127. package/skills/octocode-code-engineer/src/ast/ts-analyzer.test.ts +1864 -0
  128. package/skills/octocode-code-engineer/src/ast/ts-analyzer.ts +509 -0
  129. package/skills/octocode-code-engineer/src/collectors/chains.ts +74 -0
  130. package/skills/octocode-code-engineer/src/collectors/effects.test.ts +490 -0
  131. package/skills/octocode-code-engineer/src/collectors/effects.ts +332 -0
  132. package/skills/octocode-code-engineer/src/collectors/input-sources.test.ts +144 -0
  133. package/skills/octocode-code-engineer/src/collectors/input-sources.ts +196 -0
  134. package/skills/octocode-code-engineer/src/collectors/performance.test.ts +82 -0
  135. package/skills/octocode-code-engineer/src/collectors/performance.ts +141 -0
  136. package/skills/octocode-code-engineer/src/collectors/prototype-pollution.test.ts +55 -0
  137. package/skills/octocode-code-engineer/src/collectors/prototype-pollution.ts +162 -0
  138. package/skills/octocode-code-engineer/src/collectors/security.test.ts +124 -0
  139. package/skills/octocode-code-engineer/src/collectors/security.ts +309 -0
  140. package/skills/octocode-code-engineer/src/collectors/test-profile.test.ts +97 -0
  141. package/skills/octocode-code-engineer/src/collectors/test-profile.ts +269 -0
  142. package/skills/octocode-code-engineer/src/common/is-direct-run.test.ts +32 -0
  143. package/skills/octocode-code-engineer/src/common/is-direct-run.ts +13 -0
  144. package/skills/octocode-code-engineer/src/common/utils.test.ts +463 -0
  145. package/skills/octocode-code-engineer/src/common/utils.ts +304 -0
  146. package/skills/octocode-code-engineer/src/detectors/code-quality.ts +966 -0
  147. package/skills/octocode-code-engineer/src/detectors/cohesion.ts +539 -0
  148. package/skills/octocode-code-engineer/src/detectors/coupling.ts +323 -0
  149. package/skills/octocode-code-engineer/src/detectors/cycle.ts +349 -0
  150. package/skills/octocode-code-engineer/src/detectors/dead-code.ts +320 -0
  151. package/skills/octocode-code-engineer/src/detectors/import-style.ts +376 -0
  152. package/skills/octocode-code-engineer/src/detectors/index.test.ts +3061 -0
  153. package/skills/octocode-code-engineer/src/detectors/index.ts +88 -0
  154. package/skills/octocode-code-engineer/src/detectors/security.test.ts +882 -0
  155. package/skills/octocode-code-engineer/src/detectors/security.ts +821 -0
  156. package/skills/octocode-code-engineer/src/detectors/semantic.ts +758 -0
  157. package/skills/octocode-code-engineer/src/detectors/shared.ts +49 -0
  158. package/skills/octocode-code-engineer/src/detectors/test-quality.test.ts +388 -0
  159. package/skills/octocode-code-engineer/src/detectors/test-quality.ts +367 -0
  160. package/skills/octocode-code-engineer/src/index.test.ts +4425 -0
  161. package/skills/octocode-code-engineer/src/index.ts +403 -0
  162. package/skills/octocode-code-engineer/src/pipeline/cache.test.ts +199 -0
  163. package/skills/octocode-code-engineer/src/pipeline/cache.ts +130 -0
  164. package/skills/octocode-code-engineer/src/pipeline/cli.test.ts +493 -0
  165. package/skills/octocode-code-engineer/src/pipeline/cli.ts +344 -0
  166. package/skills/octocode-code-engineer/src/pipeline/main.test.ts +174 -0
  167. package/skills/octocode-code-engineer/src/pipeline/main.ts +1074 -0
  168. package/skills/octocode-code-engineer/src/pipeline.test.ts +84 -0
  169. package/skills/octocode-code-engineer/src/reporting/analysis.test.ts +782 -0
  170. package/skills/octocode-code-engineer/src/reporting/analysis.ts +688 -0
  171. package/skills/octocode-code-engineer/src/reporting/output-contract.test.ts +463 -0
  172. package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +421 -0
  173. package/skills/octocode-code-engineer/src/reporting/summary-md.ts +714 -0
  174. package/skills/octocode-code-engineer/src/reporting/writer.ts +430 -0
  175. package/skills/octocode-code-engineer/src/sanity.test.ts +47 -0
  176. package/skills/octocode-code-engineer/src/types/constants.ts +248 -0
  177. package/skills/octocode-code-engineer/src/types/index.ts +80 -0
  178. package/skills/octocode-code-engineer/src/types/interfaces.ts +682 -0
  179. package/skills/octocode-code-engineer/tsconfig.json +17 -0
  180. package/skills/octocode-code-engineer/vitest.config.ts +8 -0
  181. package/skills/octocode-documentation-writer/README.md +113 -0
  182. package/skills/octocode-documentation-writer/SKILL.md +886 -0
  183. package/skills/octocode-documentation-writer/references/agent-discovery-analysis.md +453 -0
  184. package/skills/octocode-documentation-writer/references/agent-documentation-writer.md +255 -0
  185. package/skills/octocode-documentation-writer/references/agent-engineer-questions.md +247 -0
  186. package/skills/octocode-documentation-writer/references/agent-orchestrator.md +370 -0
  187. package/skills/octocode-documentation-writer/references/agent-qa-validator.md +227 -0
  188. package/skills/octocode-documentation-writer/references/agent-researcher.md +250 -0
  189. package/skills/octocode-documentation-writer/schemas/analysis-schema.json +886 -0
  190. package/skills/octocode-documentation-writer/schemas/discovery-tasks.json +96 -0
  191. package/skills/octocode-documentation-writer/schemas/documentation-structure.json +373 -0
  192. package/skills/octocode-documentation-writer/schemas/partial-discovery-schema.json +102 -0
  193. package/skills/octocode-documentation-writer/schemas/partial-research-schema.json +98 -0
  194. package/skills/octocode-documentation-writer/schemas/qa-results-schema.json +113 -0
  195. package/skills/octocode-documentation-writer/schemas/questions-schema.json +228 -0
  196. package/skills/octocode-documentation-writer/schemas/research-schema.json +104 -0
  197. package/skills/octocode-documentation-writer/schemas/state-schema.json +222 -0
  198. package/skills/octocode-documentation-writer/schemas/work-assignments-schema.json +74 -0
  199. package/skills/octocode-plan/SKILL.md +122 -116
  200. package/skills/octocode-prompt-optimizer/SKILL.md +617 -0
  201. package/skills/octocode-pull-request-reviewer/README.md +249 -0
  202. package/skills/octocode-pull-request-reviewer/SKILL.md +479 -0
  203. package/skills/octocode-pull-request-reviewer/references/dependency-check.md +74 -0
  204. package/skills/octocode-pull-request-reviewer/references/domain-reviewers.md +24 -0
  205. package/skills/octocode-pull-request-reviewer/references/execution-lifecycle.md +441 -0
  206. package/skills/octocode-pull-request-reviewer/references/flow-analysis-protocol.md +64 -0
  207. package/skills/octocode-pull-request-reviewer/references/output-template.md +174 -0
  208. package/skills/octocode-pull-request-reviewer/references/parallel-agent-protocol.md +182 -0
  209. package/skills/octocode-pull-request-reviewer/references/review-guidelines.md +26 -0
  210. package/skills/octocode-pull-request-reviewer/references/verification-checklist.md +40 -0
  211. package/skills/octocode-research/.claude/settings.local.json +46 -0
  212. package/skills/octocode-research/.octocode/plan/code-review-fixes/plan.md +312 -0
  213. package/skills/octocode-research/.octocode/plan/code-review-fixes/research.md +212 -0
  214. package/skills/octocode-research/.octocode/plans/NODE_SERVER_START_PLAN.md +755 -0
  215. package/skills/octocode-research/.octocode/research/code-review/research.md +371 -0
  216. package/skills/octocode-research/.octocode/review/IMPROVEMENTS.md +391 -0
  217. package/skills/octocode-research/.octocode/review/REVIEW_PLAN.md +289 -0
  218. package/skills/octocode-research/.octocode/review/REVIEW_REPORT.md +356 -0
  219. package/skills/octocode-research/AGENTS.md +349 -0
  220. package/skills/octocode-research/README.md +494 -0
  221. package/skills/octocode-research/SKILL.md +652 -274
  222. package/skills/octocode-research/docs/API_REFERENCE.md +562 -0
  223. package/skills/octocode-research/docs/ARCHITECTURE.md +554 -0
  224. package/skills/octocode-research/docs/FLOWS.md +577 -0
  225. package/skills/octocode-research/docs/OVERVIEW.md +564 -0
  226. package/skills/octocode-research/docs/SERVER_FLOWS.md +631 -0
  227. package/skills/octocode-research/ecosystem.config.cjs +88 -0
  228. package/skills/octocode-research/eslint.config.mjs +27 -0
  229. package/skills/octocode-research/package.json +84 -0
  230. package/skills/octocode-research/references/GUARDRAILS.md +40 -0
  231. package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +178 -0
  232. package/skills/octocode-research/references/roast-prompt.md +149 -0
  233. package/skills/octocode-research/scripts/server-init.d.ts +2 -0
  234. package/skills/octocode-research/scripts/server-init.js +2 -0
  235. package/skills/octocode-research/scripts/server.d.ts +8 -0
  236. package/skills/octocode-research/scripts/server.js +445 -0
  237. package/skills/octocode-research/src/__tests__/integration/circuitBreaker.test.ts +205 -0
  238. package/skills/octocode-research/src/__tests__/integration/routes.test.ts +374 -0
  239. package/skills/octocode-research/src/__tests__/unit/circuitBreaker.test.ts +245 -0
  240. package/skills/octocode-research/src/__tests__/unit/errorHandler.test.ts +183 -0
  241. package/skills/octocode-research/src/__tests__/unit/httpPreprocess.test.ts +157 -0
  242. package/skills/octocode-research/src/__tests__/unit/logger.test.ts +143 -0
  243. package/skills/octocode-research/src/__tests__/unit/queryParser.test.ts +130 -0
  244. package/skills/octocode-research/src/__tests__/unit/responseBuilder.test.ts +469 -0
  245. package/skills/octocode-research/src/__tests__/unit/retry.test.ts +205 -0
  246. package/skills/octocode-research/src/index.ts +186 -0
  247. package/skills/octocode-research/src/mcpCache.ts +49 -0
  248. package/skills/octocode-research/src/middleware/errorHandler.ts +65 -0
  249. package/skills/octocode-research/src/middleware/logger.ts +61 -0
  250. package/skills/octocode-research/src/middleware/queryParser.ts +115 -0
  251. package/skills/octocode-research/src/middleware/readiness.ts +17 -0
  252. package/skills/octocode-research/src/routes/github.ts +197 -0
  253. package/skills/octocode-research/src/routes/local.ts +175 -0
  254. package/skills/octocode-research/src/routes/lsp.ts +177 -0
  255. package/skills/octocode-research/src/routes/package.ts +127 -0
  256. package/skills/octocode-research/src/routes/prompts.ts +138 -0
  257. package/skills/octocode-research/src/routes/tools.ts +677 -0
  258. package/skills/octocode-research/src/server-init.ts +363 -0
  259. package/skills/octocode-research/src/server.ts +285 -0
  260. package/skills/octocode-research/src/types/errorGuards.ts +151 -0
  261. package/skills/octocode-research/src/types/express.d.ts +76 -0
  262. package/skills/octocode-research/src/types/guards.ts +98 -0
  263. package/skills/octocode-research/src/types/mcp.ts +119 -0
  264. package/skills/octocode-research/src/types/responses.ts +199 -0
  265. package/skills/octocode-research/src/types/toolTypes.ts +33 -0
  266. package/skills/octocode-research/src/utils/asyncTimeout.ts +116 -0
  267. package/skills/octocode-research/src/utils/circuitBreaker.ts +492 -0
  268. package/skills/octocode-research/src/utils/colors.ts +53 -0
  269. package/skills/octocode-research/src/utils/errorQueue.ts +71 -0
  270. package/skills/octocode-research/src/utils/logEmoji.ts +103 -0
  271. package/skills/octocode-research/src/utils/logger.ts +413 -0
  272. package/skills/octocode-research/src/utils/resilience.ts +169 -0
  273. package/skills/octocode-research/src/utils/responseBuilder.ts +495 -0
  274. package/skills/octocode-research/src/utils/responseFactory.ts +100 -0
  275. package/skills/octocode-research/src/utils/responseParser.ts +272 -0
  276. package/skills/octocode-research/src/utils/retry.ts +280 -0
  277. package/skills/octocode-research/src/utils/routeFactory.ts +117 -0
  278. package/skills/octocode-research/src/utils/url.ts +20 -0
  279. package/skills/octocode-research/src/validation/httpPreprocess.ts +155 -0
  280. package/skills/octocode-research/src/validation/index.ts +2 -0
  281. package/skills/octocode-research/src/validation/schemas.ts +578 -0
  282. package/skills/octocode-research/src/validation/toolCallSchema.ts +132 -0
  283. package/skills/octocode-research/tsconfig.json +21 -0
  284. package/skills/octocode-research/tsdown.config.ts +42 -0
  285. package/skills/octocode-research/vitest.config.ts +20 -0
  286. package/skills/octocode-researcher/SKILL.md +461 -0
  287. package/skills/octocode-researcher/references/fallbacks.md +120 -0
  288. package/skills/{octocode-local-search → octocode-researcher}/references/tool-reference.md +132 -49
  289. package/skills/{octocode-local-search → octocode-researcher}/references/workflow-patterns.md +204 -4
  290. package/skills/octocode-rfc-generator/SKILL.md +223 -0
  291. package/skills/octocode-rfc-generator/references/rfc-template.md +193 -0
  292. package/skills/octocode-roast/SKILL.md +63 -21
  293. package/skills/octocode-implement/SKILL.md +0 -293
  294. package/skills/octocode-implement/references/execution-phases.md +0 -317
  295. package/skills/octocode-implement/references/tool-reference.md +0 -403
  296. package/skills/octocode-implement/references/workflow-patterns.md +0 -385
  297. package/skills/octocode-local-search/SKILL.md +0 -449
  298. package/skills/octocode-pr-review/SKILL.md +0 -391
  299. package/skills/octocode-pr-review/references/domain-reviewers.md +0 -105
  300. package/skills/octocode-pr-review/references/execution-lifecycle.md +0 -116
  301. package/skills/octocode-pr-review/references/research-flows.md +0 -75
  302. package/skills/octocode-research/references/tool-reference.md +0 -304
  303. package/skills/octocode-research/references/workflow-patterns.md +0 -325
@@ -0,0 +1,647 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+
7
+ import {
8
+ type AstSearchOptions,
9
+ PRESETS,
10
+ collectSearchFiles,
11
+ formatTextOutput,
12
+ parseSearchArgs,
13
+ runSearch,
14
+ searchFile,
15
+ } from './search.js';
16
+
17
+ describe('parseSearchArgs', () => {
18
+ it('returns defaults when no args given', () => {
19
+ const { opts } = parseSearchArgs([]);
20
+ expect(opts.pattern).toBeNull();
21
+ expect(opts.kind).toBeNull();
22
+ expect(opts.preset).toBeNull();
23
+ expect(opts.json).toBe(false);
24
+ expect(opts.limit).toBe(500);
25
+ expect(opts.includeTests).toBe(false);
26
+ });
27
+
28
+ it('parses --pattern with separate arg', () => {
29
+ const { opts } = parseSearchArgs(['--pattern', 'console.log($A)']);
30
+ expect(opts.pattern).toBe('console.log($A)');
31
+ });
32
+
33
+ it('parses -p shorthand', () => {
34
+ const { opts } = parseSearchArgs(['-p', 'foo($BAR)']);
35
+ expect(opts.pattern).toBe('foo($BAR)');
36
+ });
37
+
38
+ it('parses --pattern= syntax', () => {
39
+ const { opts } = parseSearchArgs(['--pattern=console.log($$$ARGS)']);
40
+ expect(opts.pattern).toBe('console.log($$$ARGS)');
41
+ });
42
+
43
+ it('parses --kind with separate arg', () => {
44
+ const { opts } = parseSearchArgs(['--kind', 'function_declaration']);
45
+ expect(opts.kind).toBe('function_declaration');
46
+ });
47
+
48
+ it('parses -k shorthand', () => {
49
+ const { opts } = parseSearchArgs(['-k', 'class_declaration']);
50
+ expect(opts.kind).toBe('class_declaration');
51
+ });
52
+
53
+ it('parses --kind= syntax', () => {
54
+ const { opts } = parseSearchArgs(['--kind=arrow_function']);
55
+ expect(opts.kind).toBe('arrow_function');
56
+ });
57
+
58
+ it('parses --preset with separate arg', () => {
59
+ const { opts } = parseSearchArgs(['--preset', 'empty-catch']);
60
+ expect(opts.preset).toBe('empty-catch');
61
+ });
62
+
63
+ it('parses --preset= syntax', () => {
64
+ const { opts } = parseSearchArgs(['--preset=console-log']);
65
+ expect(opts.preset).toBe('console-log');
66
+ });
67
+
68
+ it('parses --json flag', () => {
69
+ const { opts } = parseSearchArgs(['--json']);
70
+ expect(opts.json).toBe(true);
71
+ });
72
+
73
+ it('parses --limit', () => {
74
+ const { opts } = parseSearchArgs(['--limit', '50']);
75
+ expect(opts.limit).toBe(50);
76
+ });
77
+
78
+ it('parses --include-tests', () => {
79
+ const { opts } = parseSearchArgs(['--include-tests']);
80
+ expect(opts.includeTests).toBe(true);
81
+ });
82
+
83
+ it('parses --root', () => {
84
+ const { opts } = parseSearchArgs(['--root', '/tmp/myrepo']);
85
+ expect(opts.root).toBe('/tmp/myrepo');
86
+ });
87
+
88
+ it('parses --root= syntax', () => {
89
+ const { opts } = parseSearchArgs(['--root=/tmp/other']);
90
+ expect(opts.root).toBe('/tmp/other');
91
+ });
92
+
93
+ it('parses --context / -C', () => {
94
+ expect(parseSearchArgs(['--context', '3']).opts.context).toBe(3);
95
+ expect(parseSearchArgs(['-C', '5']).opts.context).toBe(5);
96
+ });
97
+
98
+ it('parses --list-presets', () => {
99
+ const { listPresets } = parseSearchArgs(['--list-presets']);
100
+ expect(listPresets).toBe(true);
101
+ });
102
+
103
+ it('falls back to defaults for NaN limit', () => {
104
+ const { opts } = parseSearchArgs(['--limit', 'abc']);
105
+ expect(opts.limit).toBe(500);
106
+ });
107
+
108
+ it('handles multiple flags together', () => {
109
+ const { opts } = parseSearchArgs([
110
+ '-p',
111
+ 'console.log($A)',
112
+ '--json',
113
+ '--limit',
114
+ '10',
115
+ '--include-tests',
116
+ ]);
117
+ expect(opts.pattern).toBe('console.log($A)');
118
+ expect(opts.json).toBe(true);
119
+ expect(opts.limit).toBe(10);
120
+ expect(opts.includeTests).toBe(true);
121
+ });
122
+
123
+ it('parses --rule JSON', () => {
124
+ const rule = '{"rule":{"kind":"catch_clause"}}';
125
+ const { opts } = parseSearchArgs(['--rule', rule]);
126
+ expect(opts.rule).toEqual({ rule: { kind: 'catch_clause' } });
127
+ });
128
+ });
129
+
130
+ describe('PRESETS', () => {
131
+ it('has expected presets defined', () => {
132
+ expect(PRESETS['empty-catch']).toBeDefined();
133
+ expect(PRESETS['console-log']).toBeDefined();
134
+ expect(PRESETS['debugger']).toBeDefined();
135
+ expect(PRESETS['any-type']).toBeDefined();
136
+ expect(PRESETS['todo-fixme']).toBeDefined();
137
+ expect(PRESETS['switch-no-default']).toBeDefined();
138
+ expect(PRESETS['nested-ternary']).toBeDefined();
139
+ expect(PRESETS['throw-string']).toBeDefined();
140
+ });
141
+
142
+ it('all presets have description and rule', () => {
143
+ for (const [name, preset] of Object.entries(PRESETS)) {
144
+ expect(preset.description, `${name} missing description`).toBeTruthy();
145
+ expect(preset.rule, `${name} missing rule`).toBeDefined();
146
+ }
147
+ });
148
+ });
149
+
150
+ describe('searchFile', () => {
151
+ it('finds pattern matches in TypeScript', () => {
152
+ const source = `
153
+ function greet(name: string) {
154
+ console.log("Hello", name);
155
+ console.log("Done");
156
+ }
157
+ `;
158
+ const matches = searchFile(
159
+ 'test.ts',
160
+ source,
161
+ 'console.log($$$ARGS)',
162
+ 'console.log($$$ARGS)',
163
+ 100
164
+ );
165
+ expect(matches.length).toBe(2);
166
+ expect(matches[0].kind).toBe('call_expression');
167
+ expect(matches[0].lineStart).toBeGreaterThan(0);
168
+ expect(matches[0].file).toBe('test.ts');
169
+ });
170
+
171
+ it('finds kind matches', () => {
172
+ const source = `
173
+ function foo() { return 1; }
174
+ const bar = () => 2;
175
+ function baz() { return 3; }
176
+ `;
177
+ const matches = searchFile(
178
+ 'test.ts',
179
+ source,
180
+ { rule: { kind: 'function_declaration' } },
181
+ null,
182
+ 100
183
+ );
184
+ expect(matches.length).toBe(2);
185
+ expect(matches.every(m => m.kind === 'function_declaration')).toBe(true);
186
+ });
187
+
188
+ it('respects limit', () => {
189
+ const source = `
190
+ console.log(1);
191
+ console.log(2);
192
+ console.log(3);
193
+ console.log(4);
194
+ console.log(5);
195
+ `;
196
+ const matches = searchFile(
197
+ 'test.ts',
198
+ source,
199
+ 'console.log($A)',
200
+ 'console.log($A)',
201
+ 3
202
+ );
203
+ expect(matches.length).toBe(3);
204
+ });
205
+
206
+ it('finds empty catch blocks with preset rule', () => {
207
+ const source = `
208
+ try { doStuff(); } catch (e) {}
209
+ try { other(); } catch (e) { handle(e); }
210
+ try { another(); } catch (e) {
211
+ }
212
+ `;
213
+ const preset = PRESETS['empty-catch'];
214
+ const matches = searchFile('test.ts', source, preset, null, 100);
215
+ expect(matches.length).toBeGreaterThanOrEqual(1);
216
+ expect(matches.every(m => m.kind === 'catch_clause')).toBe(true);
217
+ });
218
+
219
+ it('finds debugger statements', () => {
220
+ const source = `
221
+ function debug() {
222
+ debugger;
223
+ return true;
224
+ }
225
+ `;
226
+ const preset = PRESETS['debugger'];
227
+ const matches = searchFile('test.ts', source, preset, null, 100);
228
+ expect(matches.length).toBe(1);
229
+ expect(matches[0].kind).toBe('debugger_statement');
230
+ });
231
+
232
+ it('finds any type annotations', () => {
233
+ const source = `
234
+ function foo(a: any, b: string): any {
235
+ const x: any = {};
236
+ return x;
237
+ }
238
+ `;
239
+ const preset = PRESETS['any-type'];
240
+ const matches = searchFile('test.ts', source, preset, null, 100);
241
+ expect(matches.length).toBe(3);
242
+ });
243
+
244
+ it('finds nested ternary expressions', () => {
245
+ const source = `
246
+ const x = a ? (b ? 1 : 2) : 3;
247
+ const y = a ? 1 : 2;
248
+ `;
249
+ const preset = PRESETS['nested-ternary'];
250
+ const matches = searchFile('test.ts', source, preset, null, 100);
251
+ expect(matches.length).toBe(1);
252
+ });
253
+
254
+ it('finds switch without default', () => {
255
+ const source = `
256
+ switch (x) {
257
+ case 1: break;
258
+ case 2: break;
259
+ }
260
+ switch (y) {
261
+ case 1: break;
262
+ default: break;
263
+ }
264
+ `;
265
+ const preset = PRESETS['switch-no-default'];
266
+ const matches = searchFile('test.ts', source, preset, null, 100);
267
+ expect(matches.length).toBe(1);
268
+ });
269
+
270
+ it('finds class declarations', () => {
271
+ const source = `
272
+ class Foo {}
273
+ class Bar extends Foo {}
274
+ const fn = () => {};
275
+ `;
276
+ const preset = PRESETS['class-declaration'];
277
+ const matches = searchFile('test.ts', source, preset, null, 100);
278
+ expect(matches.length).toBe(2);
279
+ });
280
+
281
+ it('finds throw string patterns', () => {
282
+ const source = `
283
+ function bad() { throw "oops"; }
284
+ function good() { throw new Error("oops"); }
285
+ `;
286
+ const preset = PRESETS['throw-string'];
287
+ const matches = searchFile('test.ts', source, preset, null, 100);
288
+ expect(matches.length).toBe(1);
289
+ });
290
+
291
+ it('finds non-null assertions', () => {
292
+ const source = `
293
+ const x = obj!.foo;
294
+ const y = arr![0];
295
+ const z = normal.foo;
296
+ `;
297
+ const preset = PRESETS['non-null-assertion'];
298
+ const matches = searchFile('test.ts', source, preset, null, 100);
299
+ expect(matches.length).toBe(2);
300
+ });
301
+
302
+ it('returns empty array when no matches', () => {
303
+ const source = 'const x = 1;';
304
+ const matches = searchFile(
305
+ 'test.ts',
306
+ source,
307
+ 'console.log($A)',
308
+ 'console.log($A)',
309
+ 100
310
+ );
311
+ expect(matches.length).toBe(0);
312
+ });
313
+
314
+ it('extracts meta variables from pattern', () => {
315
+ const source = 'console.log("hello", 42);';
316
+ const matches = searchFile(
317
+ 'test.ts',
318
+ source,
319
+ 'console.log($$$ARGS)',
320
+ 'console.log($$$ARGS)',
321
+ 100
322
+ );
323
+ expect(matches.length).toBe(1);
324
+ });
325
+
326
+ it('handles JSX files', () => {
327
+ const source = `
328
+ function App() {
329
+ console.log("render");
330
+ return <div>Hello</div>;
331
+ }
332
+ `;
333
+ const matches = searchFile(
334
+ 'test.tsx',
335
+ source,
336
+ 'console.log($$$ARGS)',
337
+ 'console.log($$$ARGS)',
338
+ 100
339
+ );
340
+ expect(matches.length).toBe(1);
341
+ });
342
+ });
343
+
344
+ describe('runSearch', () => {
345
+ let tmpDir: string;
346
+
347
+ function writeFile(name: string, content: string): string {
348
+ const filePath = path.join(tmpDir, name);
349
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
350
+ fs.writeFileSync(filePath, content, 'utf8');
351
+ return filePath;
352
+ }
353
+
354
+ function defaultOpts(
355
+ overrides: Partial<AstSearchOptions> = {}
356
+ ): AstSearchOptions {
357
+ return {
358
+ root: tmpDir,
359
+ pattern: null,
360
+ kind: null,
361
+ preset: null,
362
+ rule: null,
363
+ json: false,
364
+ limit: 500,
365
+ includeTests: false,
366
+ ignoreDirs: new Set(['.git', 'node_modules', 'dist']),
367
+ context: 0,
368
+ ...overrides,
369
+ };
370
+ }
371
+
372
+ beforeEach(() => {
373
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-search-test-'));
374
+ });
375
+
376
+ afterEach(() => {
377
+ fs.rmSync(tmpDir, { recursive: true, force: true });
378
+ });
379
+
380
+ it('searches with pattern across files', () => {
381
+ const f1 = writeFile('src/a.ts', 'console.log("a");\nconsole.log("b");');
382
+ const f2 = writeFile('src/b.ts', 'console.log("c");');
383
+ writeFile('src/c.ts', 'const x = 1;');
384
+
385
+ const opts = defaultOpts({ pattern: 'console.log($A)' });
386
+ const result = runSearch(
387
+ [f1, f2, path.join(tmpDir, 'src/c.ts')],
388
+ opts,
389
+ tmpDir
390
+ );
391
+ expect(result.totalMatches).toBe(3);
392
+ expect(result.totalFiles).toBe(2);
393
+ expect(result.queryType).toBe('pattern');
394
+ });
395
+
396
+ it('searches with preset', () => {
397
+ const f1 = writeFile(
398
+ 'x.ts',
399
+ 'try { a(); } catch(e) {}\ntry { b(); } catch(e) { log(e); }'
400
+ );
401
+ const opts = defaultOpts({ preset: 'empty-catch' });
402
+ const result = runSearch([f1], opts, tmpDir);
403
+ expect(result.totalMatches).toBeGreaterThanOrEqual(1);
404
+ expect(result.queryType).toBe('preset');
405
+ });
406
+
407
+ it('searches with kind', () => {
408
+ const f1 = writeFile(
409
+ 'fn.ts',
410
+ 'function foo() {}\nfunction bar() {}\nconst x = 1;'
411
+ );
412
+ const opts = defaultOpts({ kind: 'function_declaration' });
413
+ const result = runSearch([f1], opts, tmpDir);
414
+ expect(result.totalMatches).toBe(2);
415
+ expect(result.queryType).toBe('kind');
416
+ });
417
+
418
+ it('respects limit across files', () => {
419
+ const f1 = writeFile(
420
+ 'a.ts',
421
+ 'console.log(1);\nconsole.log(2);\nconsole.log(3);'
422
+ );
423
+ const f2 = writeFile('b.ts', 'console.log(4);\nconsole.log(5);');
424
+ const opts = defaultOpts({ pattern: 'console.log($A)', limit: 3 });
425
+ const result = runSearch([f1, f2], opts, tmpDir);
426
+ expect(result.totalMatches).toBe(3);
427
+ });
428
+
429
+ it('throws on unknown preset', () => {
430
+ const f1 = writeFile('x.ts', 'const x = 1;');
431
+ const opts = defaultOpts({ preset: 'nonexistent' });
432
+ expect(() => runSearch([f1], opts, tmpDir)).toThrow(
433
+ /Unknown preset.*nonexistent/
434
+ );
435
+ });
436
+
437
+ it('throws when no search mode provided', () => {
438
+ const f1 = writeFile('x.ts', 'const x = 1;');
439
+ const opts = defaultOpts();
440
+ expect(() => runSearch([f1], opts, tmpDir)).toThrow(/Must provide/);
441
+ });
442
+ });
443
+
444
+ describe('collectSearchFiles', () => {
445
+ let tmpDir: string;
446
+
447
+ beforeEach(() => {
448
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-search-files-'));
449
+ });
450
+
451
+ afterEach(() => {
452
+ fs.rmSync(tmpDir, { recursive: true, force: true });
453
+ });
454
+
455
+ it('collects .ts and .js files', () => {
456
+ fs.writeFileSync(path.join(tmpDir, 'a.ts'), '', 'utf8');
457
+ fs.writeFileSync(path.join(tmpDir, 'b.js'), '', 'utf8');
458
+ fs.writeFileSync(path.join(tmpDir, 'c.txt'), '', 'utf8');
459
+ const files = collectSearchFiles(tmpDir, {
460
+ includeTests: false,
461
+ ignoreDirs: new Set(),
462
+ });
463
+ expect(files).toHaveLength(2);
464
+ expect(files.some(f => f.endsWith('a.ts'))).toBe(true);
465
+ expect(files.some(f => f.endsWith('b.js'))).toBe(true);
466
+ });
467
+
468
+ it('excludes test files by default', () => {
469
+ fs.writeFileSync(path.join(tmpDir, 'foo.ts'), '', 'utf8');
470
+ fs.writeFileSync(path.join(tmpDir, 'foo.test.ts'), '', 'utf8');
471
+ fs.writeFileSync(path.join(tmpDir, 'foo.spec.ts'), '', 'utf8');
472
+ const files = collectSearchFiles(tmpDir, {
473
+ includeTests: false,
474
+ ignoreDirs: new Set(),
475
+ });
476
+ expect(files).toHaveLength(1);
477
+ expect(files[0]).toMatch(/foo\.ts$/);
478
+ });
479
+
480
+ it('includes test files when flag is set', () => {
481
+ fs.writeFileSync(path.join(tmpDir, 'foo.ts'), '', 'utf8');
482
+ fs.writeFileSync(path.join(tmpDir, 'foo.test.ts'), '', 'utf8');
483
+ const files = collectSearchFiles(tmpDir, {
484
+ includeTests: true,
485
+ ignoreDirs: new Set(),
486
+ });
487
+ expect(files).toHaveLength(2);
488
+ });
489
+
490
+ it('skips ignored directories', () => {
491
+ fs.mkdirSync(path.join(tmpDir, 'node_modules'), { recursive: true });
492
+ fs.writeFileSync(path.join(tmpDir, 'node_modules', 'lib.ts'), '', 'utf8');
493
+ fs.writeFileSync(path.join(tmpDir, 'main.ts'), '', 'utf8');
494
+ const files = collectSearchFiles(tmpDir, {
495
+ includeTests: false,
496
+ ignoreDirs: new Set(['node_modules']),
497
+ });
498
+ expect(files).toHaveLength(1);
499
+ expect(files[0]).toMatch(/main\.ts$/);
500
+ });
501
+
502
+ it('skips .d.ts files', () => {
503
+ fs.writeFileSync(path.join(tmpDir, 'index.ts'), '', 'utf8');
504
+ fs.writeFileSync(path.join(tmpDir, 'index.d.ts'), '', 'utf8');
505
+ const files = collectSearchFiles(tmpDir, {
506
+ includeTests: false,
507
+ ignoreDirs: new Set(),
508
+ });
509
+ expect(files).toHaveLength(1);
510
+ expect(files[0]).toMatch(/index\.ts$/);
511
+ });
512
+ });
513
+
514
+ describe('parseSearchArgs --rule error handling', () => {
515
+ it('throws user-friendly error for invalid JSON', () => {
516
+ expect(() => parseSearchArgs(['--rule', 'not-json'])).toThrow(
517
+ /Invalid --rule JSON/
518
+ );
519
+ });
520
+
521
+ it('includes the bad input in the error message', () => {
522
+ expect(() => parseSearchArgs(['--rule', '{broken'])).toThrow(/\{broken/);
523
+ });
524
+
525
+ it('handles missing --rule value gracefully', () => {
526
+ expect(() => parseSearchArgs(['--rule'])).toThrow(/Invalid --rule JSON/);
527
+ });
528
+ });
529
+
530
+ describe('meta-variable extraction', () => {
531
+ it('extracts single $VAR without duplicating variadic $$$VAR', () => {
532
+ const source = 'console.log("hello");';
533
+ const matches = searchFile(
534
+ 'test.ts',
535
+ source,
536
+ 'console.$METHOD($$$ARGS)',
537
+ 'console.$METHOD($$$ARGS)',
538
+ 100
539
+ );
540
+ expect(matches.length).toBe(1);
541
+ const vars = matches[0].metaVariables!;
542
+ expect(vars['$METHOD']).toBe('log');
543
+ expect(vars['$$$ARGS']).toBe('"hello"');
544
+ expect(Object.keys(vars)).toHaveLength(2);
545
+ });
546
+
547
+ it('does not produce spurious single-var entries for variadic names', () => {
548
+ const source = 'fn(1, 2, 3);';
549
+ const matches = searchFile(
550
+ 'test.ts',
551
+ source,
552
+ 'fn($$$ITEMS)',
553
+ 'fn($$$ITEMS)',
554
+ 100
555
+ );
556
+ expect(matches.length).toBe(1);
557
+ const vars = matches[0].metaVariables!;
558
+ expect(vars['$$$ITEMS']).toBeDefined();
559
+ expect(vars['$ITEMS']).toBeUndefined();
560
+ });
561
+
562
+ it('handles pattern with both single and variadic meta-vars', () => {
563
+ const source = 'import { foo, bar } from "lodash";';
564
+ const matches = searchFile(
565
+ 'test.ts',
566
+ source,
567
+ 'import { $$$NAMES } from $MOD',
568
+ 'import { $$$NAMES } from $MOD',
569
+ 100
570
+ );
571
+ expect(matches.length).toBe(1);
572
+ const vars = matches[0].metaVariables!;
573
+ expect(vars['$MOD']).toBe('"lodash"');
574
+ expect(vars['$$$NAMES']).toBeDefined();
575
+ });
576
+ });
577
+
578
+ describe('formatTextOutput with context', () => {
579
+ let tmpDir: string;
580
+
581
+ beforeEach(() => {
582
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-ctx-'));
583
+ });
584
+
585
+ afterEach(() => {
586
+ fs.rmSync(tmpDir, { recursive: true, force: true });
587
+ });
588
+
589
+ function defaultOpts(
590
+ overrides: Partial<AstSearchOptions> = {}
591
+ ): AstSearchOptions {
592
+ return {
593
+ root: tmpDir,
594
+ pattern: null,
595
+ kind: null,
596
+ preset: null,
597
+ rule: null,
598
+ json: false,
599
+ limit: 500,
600
+ includeTests: false,
601
+ ignoreDirs: new Set(['.git', 'node_modules', 'dist']),
602
+ context: 0,
603
+ ...overrides,
604
+ };
605
+ }
606
+
607
+ it('shows context lines around matches when context > 0', () => {
608
+ const source = 'line1\nline2\nconsole.log("hit");\nline4\nline5\n';
609
+ const filePath = path.join(tmpDir, 'ctx.ts');
610
+ fs.writeFileSync(filePath, source, 'utf8');
611
+
612
+ const opts = defaultOpts({ pattern: 'console.log($$$A)', context: 1 });
613
+ const result = runSearch([filePath], opts, tmpDir);
614
+
615
+ expect(result._sourceByFile).toBeDefined();
616
+ expect(result._sourceByFile!.size).toBe(1);
617
+
618
+ const output = formatTextOutput(result, opts, tmpDir);
619
+ expect(output).toContain('line2');
620
+ expect(output).toContain('console.log("hit")');
621
+ expect(output).toContain('line4');
622
+ expect(output).toContain('>');
623
+ });
624
+
625
+ it('does not include _sourceByFile when context is 0', () => {
626
+ const source = 'console.log("x");\n';
627
+ const filePath = path.join(tmpDir, 'noctx.ts');
628
+ fs.writeFileSync(filePath, source, 'utf8');
629
+
630
+ const opts = defaultOpts({ pattern: 'console.log($$$A)', context: 0 });
631
+ const result = runSearch([filePath], opts, tmpDir);
632
+ expect(result._sourceByFile).toBeUndefined();
633
+ });
634
+
635
+ it('clamps context at file boundaries', () => {
636
+ const source = 'console.log("first line");\nline2\n';
637
+ const filePath = path.join(tmpDir, 'edge.ts');
638
+ fs.writeFileSync(filePath, source, 'utf8');
639
+
640
+ const opts = defaultOpts({ pattern: 'console.log($$$A)', context: 5 });
641
+ const result = runSearch([filePath], opts, tmpDir);
642
+ const output = formatTextOutput(result, opts, tmpDir);
643
+ expect(output).toContain('console.log("first line")');
644
+ expect(output).toContain('line2');
645
+ expect(output).not.toContain('undefined');
646
+ });
647
+ });