@mrciphersmith/keryx 0.2.9

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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +351 -0
  3. package/dist/cli.js +48427 -0
  4. package/dist/proxy-worker.js +381 -0
  5. package/package.json +63 -0
  6. package/src/gdgraph/affected.test.ts +100 -0
  7. package/src/gdgraph/affected.ts +135 -0
  8. package/src/gdgraph/build-integrity.test.ts +193 -0
  9. package/src/gdgraph/build-lang.test.ts +399 -0
  10. package/src/gdgraph/build.test.ts +120 -0
  11. package/src/gdgraph/build.ts +804 -0
  12. package/src/gdgraph/config.test.ts +47 -0
  13. package/src/gdgraph/config.ts +113 -0
  14. package/src/gdgraph/core-sources.test.ts +99 -0
  15. package/src/gdgraph/core-sources.ts +20 -0
  16. package/src/gdgraph/enrich.ts +79 -0
  17. package/src/gdgraph/fallback.test.ts +153 -0
  18. package/src/gdgraph/find.test.ts +78 -0
  19. package/src/gdgraph/find.ts +107 -0
  20. package/src/gdgraph/pagerank.ts +129 -0
  21. package/src/gdgraph/path.test.ts +56 -0
  22. package/src/gdgraph/path.ts +112 -0
  23. package/src/gdgraph/query.ts +133 -0
  24. package/src/gdgraph/repomap.test.ts +110 -0
  25. package/src/gdgraph/repomap.ts +193 -0
  26. package/src/gdgraph/service.test.ts +59 -0
  27. package/src/gdgraph/service.ts +51 -0
  28. package/src/gdgraph/staleness.ts +20 -0
  29. package/src/gdgraph/symbol.test.ts +89 -0
  30. package/src/gdgraph/symbol.ts +123 -0
  31. package/src/gdgraph/symbols-capability.test.ts +41 -0
  32. package/src/gdgraph/symbols-capability.ts +49 -0
  33. package/src/gdgraph/target.ts +56 -0
  34. package/src/gdgraph/treesitter/adapter.test.ts +249 -0
  35. package/src/gdgraph/treesitter/adapter.ts +213 -0
  36. package/src/gdgraph/treesitter/extract.test.ts +278 -0
  37. package/src/gdgraph/treesitter/extract.ts +389 -0
  38. package/src/gdgraph/treesitter/grammars.ts +110 -0
  39. package/src/gdgraph/treesitter/no-treesitter-import.test.ts +51 -0
  40. package/src/gdgraph/treesitter/resolve-calls.test.ts +38 -0
  41. package/src/gdgraph/types.ts +63 -0
  42. package/src/gdskills/bundled/rules/core/api-contracts.mdc +148 -0
  43. package/src/gdskills/bundled/rules/core/async-patterns.mdc +185 -0
  44. package/src/gdskills/bundled/rules/core/clean-architecture.mdc +163 -0
  45. package/src/gdskills/bundled/rules/core/code-review-ai-assistant.mdc +49 -0
  46. package/src/gdskills/bundled/rules/core/code-review-b091-profile.mdc +48 -0
  47. package/src/gdskills/bundled/rules/core/code-style-patterns.mdc +54 -0
  48. package/src/gdskills/bundled/rules/core/commit-message-formatting.mdc +25 -0
  49. package/src/gdskills/bundled/rules/core/database-patterns.mdc +162 -0
  50. package/src/gdskills/bundled/rules/core/documentation-management.mdc +106 -0
  51. package/src/gdskills/bundled/rules/core/error-handling.mdc +163 -0
  52. package/src/gdskills/bundled/rules/core/execution-metrics.md +99 -0
  53. package/src/gdskills/bundled/rules/core/frontend-assistant.mdc +40 -0
  54. package/src/gdskills/bundled/rules/core/git-rules.mdc +37 -0
  55. package/src/gdskills/bundled/rules/core/gproject-contracts.mdc +198 -0
  56. package/src/gdskills/bundled/rules/core/implementation-doc-mandate.mdc +149 -0
  57. package/src/gdskills/bundled/rules/core/implementation-plans.mdc +58 -0
  58. package/src/gdskills/bundled/rules/core/jobs-documentation.mdc +221 -0
  59. package/src/gdskills/bundled/rules/core/mobx-store-template.mdc +561 -0
  60. package/src/gdskills/bundled/rules/core/model-selection.mdc +53 -0
  61. package/src/gdskills/bundled/rules/core/nestjs-dto.mdc +50 -0
  62. package/src/gdskills/bundled/rules/core/playwright-testing.mdc +321 -0
  63. package/src/gdskills/bundled/rules/core/requirements-management.mdc +35 -0
  64. package/src/gdskills/bundled/rules/core/requirements-package-standard.mdc +103 -0
  65. package/src/gdskills/bundled/rules/core/review-agent-profile.mdc +49 -0
  66. package/src/gdskills/bundled/rules/core/review-strict-profile.mdc +44 -0
  67. package/src/gdskills/bundled/rules/core/rule-management-workflow.mdc +62 -0
  68. package/src/gdskills/bundled/rules/core/security-baseline.mdc +155 -0
  69. package/src/gdskills/bundled/rules/core/shared-definitions.mdc +79 -0
  70. package/src/gdskills/bundled/rules/core/skill-lifecycle.mdc +55 -0
  71. package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +88 -0
  72. package/src/gdskills/bundled/rules/core/solid-principles.mdc +149 -0
  73. package/src/gdskills/bundled/rules/core/storybook-guidelines.mdc +36 -0
  74. package/src/gdskills/bundled/rules/core/subagent-context-construction.md +107 -0
  75. package/src/gdskills/bundled/rules/core/subagent-status-protocol.md +169 -0
  76. package/src/gdskills/bundled/rules/core/tdd-workflow.mdc +96 -0
  77. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +47 -0
  78. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +55 -0
  79. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +352 -0
  80. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +39 -0
  81. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +29 -0
  82. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +567 -0
  83. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +567 -0
  84. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +655 -0
  85. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +567 -0
  86. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +567 -0
  87. package/src/gdskills/bundled/skills/orchestration/context-collector/input-contract.schema.json +134 -0
  88. package/src/gdskills/bundled/skills/orchestration/context-collector/orchestrator-prompt.md +183 -0
  89. package/src/gdskills/bundled/skills/orchestration/context-collector/output-contract.schema.json +208 -0
  90. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +1412 -0
  91. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +1412 -0
  92. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.detail.md +735 -0
  93. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +468 -0
  94. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +1412 -0
  95. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +1412 -0
  96. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.md +62 -0
  97. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.template.md +79 -0
  98. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/input-contract.schema.json +247 -0
  99. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/orchestrator-prompt.md +362 -0
  100. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/output-contract.schema.json +87 -0
  101. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +105 -0
  102. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +105 -0
  103. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +165 -0
  104. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +379 -0
  105. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json +33 -0
  106. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/output-contract.schema.json +41 -0
  107. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +307 -0
  108. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +307 -0
  109. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +373 -0
  110. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +307 -0
  111. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +307 -0
  112. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/input-contract.schema.json +130 -0
  113. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/issue-request.template.md +50 -0
  114. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +155 -0
  115. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/output-contract.schema.json +61 -0
  116. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +365 -0
  117. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +365 -0
  118. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +373 -0
  119. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +365 -0
  120. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +365 -0
  121. package/src/gdskills/bundled/skills/orchestration/job-documenter/input-contract.schema.json +55 -0
  122. package/src/gdskills/bundled/skills/orchestration/job-documenter/output-contract.schema.json +58 -0
  123. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +1701 -0
  124. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +1701 -0
  125. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1734 -0
  126. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +1701 -0
  127. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +1701 -0
  128. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/input-contract.schema.json +188 -0
  129. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/orchestrator-prompt.md +179 -0
  130. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/output-contract.schema.json +103 -0
  131. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json +164 -0
  132. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +395 -0
  133. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +395 -0
  134. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +556 -0
  135. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +395 -0
  136. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +395 -0
  137. package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +204 -0
  138. package/src/gdskills/bundled/skills/orchestration/task-implementer/orchestrator-prompt.md +206 -0
  139. package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +51 -0
  140. package/src/gdskills/bundled/skills/orchestration/task-implementer/task-request.template.md +111 -0
  141. package/src/gdskills/bundled/skills/planning/autodoc-analyst/SKILL.md +163 -0
  142. package/src/gdskills/bundled/skills/planning/autodoc-architect/SKILL.md +161 -0
  143. package/src/gdskills/bundled/skills/planning/autodoc-assembler/SKILL.md +128 -0
  144. package/src/gdskills/bundled/skills/planning/autodoc-orchestrator/SKILL.md +321 -0
  145. package/src/gdskills/bundled/skills/planning/autodoc-scanner/SKILL.md +166 -0
  146. package/src/gdskills/bundled/skills/planning/autodoc-writer/SKILL.md +253 -0
  147. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.claude.md +46 -0
  148. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +90 -0
  149. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +90 -0
  150. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +90 -0
  151. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +33 -0
  152. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +33 -0
  153. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.md +190 -0
  154. package/src/gdskills/bundled/skills/planning/docpack-orchestrator/SKILL.md +142 -0
  155. package/src/gdskills/bundled/skills/planning/docpack-orchestrator/input-contract.schema.json +31 -0
  156. package/src/gdskills/bundled/skills/planning/docpack-orchestrator/output-contract.schema.json +31 -0
  157. package/src/gdskills/bundled/skills/planning/docpack-review/SKILL.md +80 -0
  158. package/src/gdskills/bundled/skills/planning/docpack-review/input-contract.schema.json +20 -0
  159. package/src/gdskills/bundled/skills/planning/docpack-review/output-contract.schema.json +36 -0
  160. package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +181 -0
  161. package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +181 -0
  162. package/src/gdskills/bundled/skills/planning/interview/SKILL.md +187 -0
  163. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.claude.md +94 -0
  164. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +99 -0
  165. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +99 -0
  166. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +105 -0
  167. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +33 -0
  168. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +33 -0
  169. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.md +237 -0
  170. package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +33 -0
  171. package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +33 -0
  172. package/src/gdskills/bundled/skills/planning/planner/SKILL.md +190 -0
  173. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +193 -0
  174. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +193 -0
  175. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +193 -0
  176. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +193 -0
  177. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +193 -0
  178. package/src/gdskills/bundled/skills/planning/prd-creator/input-contract.schema.json +63 -0
  179. package/src/gdskills/bundled/skills/planning/prd-creator/output-contract.schema.json +175 -0
  180. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +31 -0
  181. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +31 -0
  182. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.md +153 -0
  183. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +32 -0
  184. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +32 -0
  185. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.md +166 -0
  186. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +44 -0
  187. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +44 -0
  188. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.md +243 -0
  189. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +43 -0
  190. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +43 -0
  191. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.md +193 -0
  192. package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +47 -0
  193. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +87 -0
  194. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +87 -0
  195. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +87 -0
  196. package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +100 -0
  197. package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +100 -0
  198. package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +100 -0
  199. package/src/gdskills/bundled/skills/quality/changelog/SKILL.claude.md +45 -0
  200. package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +84 -0
  201. package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +84 -0
  202. package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +84 -0
  203. package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +66 -0
  204. package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +66 -0
  205. package/src/gdskills/bundled/skills/quality/commit/SKILL.md +66 -0
  206. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.claude.md +40 -0
  207. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +66 -0
  208. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +66 -0
  209. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +66 -0
  210. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.claude.md +45 -0
  211. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +81 -0
  212. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +81 -0
  213. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +81 -0
  214. package/src/gdskills/bundled/skills/quality/deploy/SKILL.claude.md +42 -0
  215. package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +70 -0
  216. package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +70 -0
  217. package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +70 -0
  218. package/src/gdskills/bundled/skills/quality/metaproject-security/SKILL.md +103 -0
  219. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.claude.md +48 -0
  220. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +83 -0
  221. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +83 -0
  222. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +83 -0
  223. package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +75 -0
  224. package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +75 -0
  225. package/src/gdskills/bundled/skills/quality/pr/SKILL.md +75 -0
  226. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +364 -0
  227. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +295 -0
  228. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +378 -0
  229. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +364 -0
  230. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +38 -0
  231. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/input-contract.schema.json +53 -0
  232. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/output-contract.schema.json +46 -0
  233. package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +52 -0
  234. package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +52 -0
  235. package/src/gdskills/bundled/skills/quality/push/SKILL.md +52 -0
  236. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.claude.md +40 -0
  237. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +61 -0
  238. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +61 -0
  239. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.md +61 -0
  240. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.claude.md +30 -0
  241. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +75 -0
  242. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +75 -0
  243. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +75 -0
  244. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +64 -0
  245. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +74 -0
  246. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +339 -0
  247. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +48 -0
  248. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +44 -0
  249. package/src/gdskills/bundled/skills/quality/tests-creator/input-contract.schema.json +121 -0
  250. package/src/gdskills/bundled/skills/quality/tests-creator/output-contract.schema.json +127 -0
  251. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +204 -0
  252. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +204 -0
  253. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +203 -0
  254. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +204 -0
  255. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +204 -0
  256. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.codex.md +209 -0
  257. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.cursor.md +209 -0
  258. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.md +208 -0
  259. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.opencode.md +209 -0
  260. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.zed.md +209 -0
  261. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +263 -0
  262. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +263 -0
  263. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +258 -0
  264. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +263 -0
  265. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +263 -0
  266. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +175 -0
  267. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +175 -0
  268. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +168 -0
  269. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +175 -0
  270. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +175 -0
  271. package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +360 -0
  272. package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +337 -0
  273. package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +477 -0
  274. package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +93 -0
  275. package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +119 -0
  276. package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +595 -0
  277. package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +144 -0
  278. package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +519 -0
  279. package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +323 -0
  280. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +927 -0
  281. package/src/gdskills/bundled/skills/review/review-orchestrator/input-contract.schema.json +153 -0
  282. package/src/gdskills/bundled/skills/review/review-orchestrator/output-contract.schema.json +77 -0
  283. package/src/gdskills/bundled/skills/review/review-orchestrator/review-context.schema.json +389 -0
  284. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +189 -0
  285. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +241 -0
  286. package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +344 -0
  287. package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +327 -0
  288. package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +348 -0
  289. package/src/gdskills/bundled/skills/review/review-strict/SKILL.md +328 -0
  290. package/src/gdskills/bundled/skills/review/review-style/SKILL.md +343 -0
  291. package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +134 -0
  292. package/src/gdskills/bundled/skills/shared/git-merge-base.md +33 -0
  293. package/src/gdskills/contracts/agent-event.schema.json +32 -0
  294. package/src/gdskills/contracts/orchestrator-state.schema.json +58 -0
  295. package/src/gdskills/contracts/review-finding.schema.json +59 -0
  296. package/src/gdskills/contracts/subagent-dispatch.schema.json +123 -0
  297. package/src/gdskills/contracts/subagent-result.schema.json +136 -0
@@ -0,0 +1,278 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { expect, test } from "bun:test";
5
+ import { extractSymbolLayer, type TsNode } from "./extract";
6
+ import type { CallEdge, SymbolNode } from "../types";
7
+
8
+ const FIXTURE_DIR = fileURLToPath(new URL("../../../fixtures/symbol-graph/", import.meta.url));
9
+
10
+ // --- Minimal mock syntax-tree builder (a structural stand-in for a parsed
11
+ // `web-tree-sitter` tree; the extractor is dependency-free). ---
12
+ interface MockOptions {
13
+ type: string;
14
+ line?: number;
15
+ endLine?: number;
16
+ text?: string;
17
+ fields?: Record<string, TsNode | null>;
18
+ namedChildren?: TsNode[];
19
+ }
20
+
21
+ function mk(o: MockOptions): TsNode {
22
+ const line = o.line ?? 1;
23
+ const endLine = o.endLine ?? line;
24
+ const named = o.namedChildren ?? [];
25
+ return {
26
+ type: o.type,
27
+ text: o.text ?? "",
28
+ startPosition: { row: line - 1, column: 0 },
29
+ endPosition: { row: endLine - 1, column: 0 },
30
+ childForFieldName: (field: string) => o.fields?.[field] ?? null,
31
+ namedChildren: named,
32
+ children: named,
33
+ };
34
+ }
35
+
36
+ const idNode = (text: string): TsNode => mk({ type: "identifier", text });
37
+ const paramsNode = (): TsNode => mk({ type: "formal_parameters", text: "()" });
38
+ const callTo = (name: string, line: number): TsNode =>
39
+ mk({ type: "call_expression", line, fields: { function: idNode(name) }, text: `${name}()` });
40
+
41
+ // Build the tree modeling `fixtures/symbol-graph/source.ts`.
42
+ export function buildWidgetTree(): TsNode {
43
+ const alphaBody = mk({ type: "statement_block", line: 1, namedChildren: [callTo("helper", 1)] });
44
+ const funcAlpha = mk({
45
+ type: "function_declaration",
46
+ line: 1,
47
+ endLine: 1,
48
+ fields: { name: idNode("alpha"), parameters: paramsNode() },
49
+ namedChildren: [alphaBody],
50
+ });
51
+ const funcHelper = mk({
52
+ type: "function_declaration",
53
+ line: 2,
54
+ endLine: 2,
55
+ fields: { name: idNode("helper"), parameters: paramsNode() },
56
+ });
57
+ const renderBody = mk({ type: "statement_block", line: 4, namedChildren: [callTo("alpha", 4)] });
58
+ const methodRender = mk({
59
+ type: "method_definition",
60
+ line: 4,
61
+ endLine: 4,
62
+ fields: { name: idNode("render"), parameters: paramsNode() },
63
+ namedChildren: [renderBody],
64
+ });
65
+ const classBody = mk({ type: "class_body", line: 3, endLine: 5, namedChildren: [methodRender] });
66
+ const classWidget = mk({
67
+ type: "class_declaration",
68
+ line: 3,
69
+ endLine: 5,
70
+ fields: { name: idNode("Widget") },
71
+ namedChildren: [classBody],
72
+ });
73
+ const interfaceShape = mk({
74
+ type: "interface_declaration",
75
+ line: 6,
76
+ endLine: 6,
77
+ fields: { name: idNode("Shape") },
78
+ });
79
+ return mk({
80
+ type: "program",
81
+ line: 1,
82
+ endLine: 6,
83
+ namedChildren: [funcAlpha, funcHelper, classWidget, interfaceShape],
84
+ });
85
+ }
86
+
87
+ async function loadJsonl<T>(file: string): Promise<T[]> {
88
+ const content = await readFile(file, "utf8");
89
+ return content
90
+ .split("\n")
91
+ .map((line) => line.trim())
92
+ .filter(Boolean)
93
+ .map((line) => JSON.parse(line) as T);
94
+ }
95
+
96
+ function prf(produced: Set<string>, expected: Set<string>): { precision: number; recall: number } {
97
+ let truePos = 0;
98
+ for (const item of produced) {
99
+ if (expected.has(item)) {
100
+ truePos += 1;
101
+ }
102
+ }
103
+ return {
104
+ precision: produced.size === 0 ? 0 : truePos / produced.size,
105
+ recall: expected.size === 0 ? 0 : truePos / expected.size,
106
+ };
107
+ }
108
+
109
+ test("AC1.2/AC1.3 — extracted symbols/calls hit precision ≥ 0.90 and recall ≥ 0.85", async () => {
110
+ const layer = extractSymbolLayer(buildWidgetTree(), "src/widget.ts", "typescript");
111
+ const expectedSymbols = await loadJsonl<SymbolNode>(path.join(FIXTURE_DIR, "expected", "symbols.jsonl"));
112
+ const expectedCalls = await loadJsonl<CallEdge>(path.join(FIXTURE_DIR, "expected", "calls.jsonl"));
113
+
114
+ const symbolKey = (s: SymbolNode) => `${s.id}|${s.kind}|${s.startLine}`;
115
+ const callKey = (c: CallEdge) => `${c.kind}|${c.from}|${c.to}`;
116
+
117
+ const symbolPrf = prf(new Set(layer.symbols.map(symbolKey)), new Set(expectedSymbols.map(symbolKey)));
118
+ const callPrf = prf(new Set(layer.calls.map(callKey)), new Set(expectedCalls.map(callKey)));
119
+
120
+ expect(symbolPrf.precision).toBeGreaterThanOrEqual(0.9);
121
+ expect(symbolPrf.recall).toBeGreaterThanOrEqual(0.85);
122
+ expect(callPrf.precision).toBeGreaterThanOrEqual(0.9);
123
+ expect(callPrf.recall).toBeGreaterThanOrEqual(0.85);
124
+ });
125
+
126
+ test("AC1.1 — emits function/class/method/interface nodes + calls/defines/unresolved edges", () => {
127
+ const layer = extractSymbolLayer(buildWidgetTree(), "src/widget.ts", "typescript");
128
+ const kinds = new Set(layer.symbols.map((symbol) => symbol.kind));
129
+ expect(kinds).toEqual(new Set(["function", "class", "method", "interface"]));
130
+ const callKinds = new Set(layer.calls.map((call) => call.kind));
131
+ expect(callKinds.has("defines")).toBe(true);
132
+ expect(callKinds.has("calls")).toBe(true);
133
+ });
134
+
135
+ test("AC1.4 — symbol ids are stable + content-independent; re-run is byte-identical", () => {
136
+ const a = extractSymbolLayer(buildWidgetTree(), "src/widget.ts", "typescript");
137
+ const b = extractSymbolLayer(buildWidgetTree(), "src/widget.ts", "typescript");
138
+ expect(JSON.stringify(a)).toBe(JSON.stringify(b));
139
+ expect(a.symbols.map((symbol) => symbol.id)).toEqual([
140
+ "src/widget.ts#alpha",
141
+ "src/widget.ts#helper",
142
+ "src/widget.ts#Widget",
143
+ "src/widget.ts#Widget.render",
144
+ "src/widget.ts#Shape",
145
+ ]);
146
+ });
147
+
148
+ test("AC1.4 — colliding names get a positional @startLine disambiguator only", () => {
149
+ const idNodeLocal = (text: string): TsNode => mk({ type: "identifier", text });
150
+ const dup1 = mk({
151
+ type: "function_declaration",
152
+ line: 1,
153
+ endLine: 1,
154
+ fields: { name: idNodeLocal("dup"), parameters: paramsNode() },
155
+ });
156
+ const dup2 = mk({
157
+ type: "function_declaration",
158
+ line: 5,
159
+ endLine: 5,
160
+ fields: { name: idNodeLocal("dup"), parameters: paramsNode() },
161
+ });
162
+ const unique = mk({
163
+ type: "function_declaration",
164
+ line: 9,
165
+ endLine: 9,
166
+ fields: { name: idNodeLocal("only"), parameters: paramsNode() },
167
+ });
168
+ const root = mk({ type: "program", line: 1, endLine: 9, namedChildren: [dup1, dup2, unique] });
169
+ const layer = extractSymbolLayer(root, "src/dup.ts", "typescript");
170
+ const ids = layer.symbols.map((symbol) => symbol.id);
171
+ expect(ids).toEqual(["src/dup.ts#dup@1", "src/dup.ts#dup@5", "src/dup.ts#only"]);
172
+ });
173
+
174
+ test("unresolved calls to unknown callees are kept as unresolved-call edges", () => {
175
+ const idNodeLocal = (text: string): TsNode => mk({ type: "identifier", text });
176
+ const body = mk({
177
+ type: "statement_block",
178
+ line: 1,
179
+ namedChildren: [
180
+ mk({ type: "call_expression", line: 1, fields: { function: idNodeLocal("externalThing") }, text: "externalThing()" }),
181
+ ],
182
+ });
183
+ const fn = mk({
184
+ type: "function_declaration",
185
+ line: 1,
186
+ endLine: 2,
187
+ fields: { name: idNodeLocal("main"), parameters: paramsNode() },
188
+ namedChildren: [body],
189
+ });
190
+ const root = mk({ type: "program", line: 1, endLine: 2, namedChildren: [fn] });
191
+ const layer = extractSymbolLayer(root, "src/main.ts", "typescript");
192
+ const unresolved = layer.calls.filter((call) => call.kind === "unresolved-call");
193
+ expect(unresolved).toHaveLength(1);
194
+ expect(unresolved[0]?.to).toBe("externalThing");
195
+ expect(unresolved[0]?.resolved).toBe(false);
196
+ });
197
+
198
+ test("Java class and method extraction", () => {
199
+ const idNode = (text: string): TsNode => mk({ type: "identifier", text });
200
+ const methodBody = mk({ type: "block", line: 4, namedChildren: [
201
+ mk({ type: "method_invocation", line: 4, fields: { method: idNode("helper") }, text: "helper()" }),
202
+ ]});
203
+ const methodHelper = mk({
204
+ type: "method_declaration",
205
+ line: 3,
206
+ endLine: 3,
207
+ fields: { name: idNode("helper"), parameters: mk({ type: "formal_parameters", text: "()" }) },
208
+ });
209
+ const methodMain = mk({
210
+ type: "method_declaration",
211
+ line: 4,
212
+ endLine: 5,
213
+ fields: { name: idNode("main"), parameters: mk({ type: "formal_parameters", text: "()" }) },
214
+ namedChildren: [methodBody],
215
+ });
216
+ const classBody = mk({ type: "class_body", line: 2, endLine: 6, namedChildren: [methodHelper, methodMain] });
217
+ const classApp = mk({
218
+ type: "class_declaration",
219
+ line: 1,
220
+ endLine: 6,
221
+ fields: { name: idNode("App") },
222
+ namedChildren: [classBody],
223
+ });
224
+ const root = mk({ type: "program", line: 1, endLine: 6, namedChildren: [classApp] });
225
+ const layer = extractSymbolLayer(root, "src/App.java", "java");
226
+
227
+ expect(layer.symbols).toHaveLength(3); // App class + 2 methods
228
+ const classSymbol = layer.symbols.find((s) => s.name === "App");
229
+ expect(classSymbol).toBeDefined();
230
+ expect(classSymbol?.kind).toBe("class");
231
+
232
+ const methods = layer.symbols.filter((s) => s.kind === "method");
233
+ expect(methods).toHaveLength(2);
234
+ });
235
+
236
+ test("Python function and class extraction", () => {
237
+ const idNode = (text: string): TsNode => mk({ type: "identifier", text });
238
+ const funcBody = mk({ type: "block", line: 4, namedChildren: [
239
+ mk({ type: "call", line: 4, fields: { function: idNode("helper") }, text: "helper()" }),
240
+ ]});
241
+ const funcHelper = mk({
242
+ type: "function_definition",
243
+ line: 2,
244
+ endLine: 2,
245
+ fields: { name: idNode("helper"), parameters: mk({ type: "parameters", text: "()" }) },
246
+ });
247
+ const funcMain = mk({
248
+ type: "function_definition",
249
+ line: 4,
250
+ endLine: 5,
251
+ fields: { name: idNode("main"), parameters: mk({ type: "parameters", text: "()" }) },
252
+ namedChildren: [funcBody],
253
+ });
254
+ const methodProcess = mk({
255
+ type: "function_definition",
256
+ line: 8,
257
+ endLine: 8,
258
+ fields: { name: idNode("process"), parameters: mk({ type: "parameters", text: "(self)" }) },
259
+ });
260
+ const classBody = mk({ type: "block", line: 7, endLine: 9, namedChildren: [methodProcess] });
261
+ const classApp = mk({
262
+ type: "class_definition",
263
+ line: 6,
264
+ endLine: 9,
265
+ fields: { name: idNode("App") },
266
+ namedChildren: [classBody],
267
+ });
268
+ const root = mk({ type: "program", line: 1, endLine: 9, namedChildren: [funcHelper, funcMain, classApp] });
269
+ const layer = extractSymbolLayer(root, "src/main.py", "python");
270
+
271
+ expect(layer.symbols).toHaveLength(4); // 2 functions + 1 class + 1 nested function (process)
272
+ const classSymbol = layer.symbols.find((s) => s.name === "App");
273
+ expect(classSymbol).toBeDefined();
274
+ expect(classSymbol?.kind).toBe("class");
275
+
276
+ const functions = layer.symbols.filter((s) => s.kind === "function");
277
+ expect(functions.length).toBeGreaterThan(0); // Should have at least the top-level functions
278
+ });
@@ -0,0 +1,389 @@
1
+ // Tree-sitter symbol/call extraction (specification.md §8.1, §9; T-B12).
2
+ //
3
+ // PURE structural walk over a parsed tree, producing a sorted, stable
4
+ // `SymbolLayer`. This module NEVER imports `web-tree-sitter` (C0-2/C0-14) — the
5
+ // adapter parses and passes the root node through the minimal `TsNode` shape
6
+ // below, so extraction is dependency-free and unit-testable with a plain mock
7
+ // tree. Determinism: symbols sorted by (path, startLine, name); calls sorted by
8
+ // (from, to, kind); ids are content-independent positional
9
+ // (`<path>#<Container>.<name>`, `@<startLine>` only on name collision).
10
+
11
+ import type { CallEdge, SymbolKind, SymbolLayer, SymbolNode } from "../types";
12
+
13
+ // The minimal syntax-node contract the walk needs. `web-tree-sitter`'s SyntaxNode
14
+ // is structurally compatible; a test can supply a hand-built mock.
15
+ export interface TsNode {
16
+ type: string;
17
+ text: string;
18
+ startPosition: { row: number; column: number };
19
+ endPosition: { row: number; column: number };
20
+ childForFieldName(field: string): TsNode | null;
21
+ readonly namedChildren: TsNode[];
22
+ readonly children: TsNode[];
23
+ }
24
+
25
+ type Language = "typescript" | "javascript" | "java" | "python";
26
+
27
+ const FUNCTION_TYPES = new Set([
28
+ // TypeScript/JavaScript
29
+ "function_declaration", "function_signature", "generator_function_declaration",
30
+ // Python
31
+ "function_definition",
32
+ ]);
33
+ const METHOD_TYPES = new Set([
34
+ // TypeScript/JavaScript
35
+ "method_definition", "method_signature",
36
+ // Java
37
+ "method_declaration",
38
+ ]);
39
+ const CLASS_TYPES = new Set([
40
+ // TypeScript/JavaScript
41
+ "class_declaration", "class",
42
+ // Java
43
+ "class_declaration",
44
+ // Python
45
+ "class_definition",
46
+ ]);
47
+ const INTERFACE_TYPES = new Set([
48
+ // TypeScript/JavaScript
49
+ "interface_declaration",
50
+ // Java
51
+ "interface_declaration",
52
+ ]);
53
+ const CALL_TYPES = new Set([
54
+ // TypeScript/JavaScript
55
+ "call_expression", "new_expression",
56
+ // Java
57
+ "method_invocation",
58
+ // Python
59
+ "call",
60
+ ]);
61
+ const ARROW_HOLDER_TYPES = new Set([
62
+ // TypeScript/JavaScript
63
+ "lexical_declaration", "variable_declaration",
64
+ ]);
65
+
66
+ interface RawSymbol {
67
+ base: string;
68
+ node: SymbolNode;
69
+ }
70
+
71
+ // Extract the symbol layer from a parsed tree root. `filePath` is the owning
72
+ // file path (matches a file GraphNode.path).
73
+ export function extractSymbolLayer(root: TsNode, filePath: string, language: Language): SymbolLayer {
74
+ const raw: RawSymbol[] = [];
75
+ const calls: CallEdge[] = [];
76
+
77
+ // Walk the tree, tracking the enclosing class/interface container + the
78
+ // nearest enclosing symbol id (for call attribution).
79
+ const visit = (node: TsNode, container: string | null, enclosingSymbolId: string | null): void => {
80
+ let nextContainer = container;
81
+ let nextEnclosing = enclosingSymbolId;
82
+
83
+ const symbol = symbolFromNode(node, filePath, container, language);
84
+ if (symbol) {
85
+ raw.push(symbol);
86
+ nextEnclosing = symbol.node.id;
87
+ if (symbol.node.kind === "class" || symbol.node.kind === "interface") {
88
+ nextContainer = symbol.node.name;
89
+ }
90
+ } else if (ARROW_HOLDER_TYPES.has(node.type)) {
91
+ // `const foo = () => {}` / `const Bar = function () {}` — arrow/const fns.
92
+ for (const declared of arrowFunctionSymbols(node, filePath, container, language)) {
93
+ raw.push(declared);
94
+ // Descend into the arrow body under this symbol's id below.
95
+ }
96
+ }
97
+
98
+ if (CALL_TYPES.has(node.type)) {
99
+ const call = callFromNode(node, filePath, enclosingSymbolId);
100
+ if (call) {
101
+ calls.push(call);
102
+ }
103
+ }
104
+
105
+ for (const child of node.namedChildren) {
106
+ visit(child, nextContainer, nextEnclosing);
107
+ }
108
+ };
109
+
110
+ visit(root, null, null);
111
+
112
+ // Disambiguate id collisions with a positional `@<startLine>` suffix.
113
+ const baseCounts = new Map<string, number>();
114
+ for (const entry of raw) {
115
+ baseCounts.set(entry.base, (baseCounts.get(entry.base) ?? 0) + 1);
116
+ }
117
+ const symbolNodes: SymbolNode[] = raw.map((entry) => {
118
+ if ((baseCounts.get(entry.base) ?? 0) > 1) {
119
+ return { ...entry.node, id: `${entry.base}@${entry.node.startLine}` };
120
+ }
121
+ return entry.node;
122
+ });
123
+
124
+ // `defines` edges: file → each symbol (containment).
125
+ const defines: CallEdge[] = symbolNodes.map((symbol) => ({
126
+ id: `defines:${filePath}->${symbol.id}`,
127
+ from: filePath,
128
+ to: symbol.id,
129
+ kind: "defines",
130
+ resolved: true,
131
+ }));
132
+
133
+ // Resolve CALL targets to same-file symbols by name; else unresolved-call.
134
+ const nameToId = new Map<string, string>();
135
+ for (const symbol of symbolNodes) {
136
+ if (!nameToId.has(symbol.name)) {
137
+ nameToId.set(symbol.name, symbol.id);
138
+ }
139
+ }
140
+ const resolvedCalls: CallEdge[] = calls.map((call, index) => {
141
+ const calleeName = lastSegment(call.to);
142
+ const target = nameToId.get(calleeName);
143
+ if (target) {
144
+ return {
145
+ id: `call:${call.from}->${target}:${index}`,
146
+ from: call.from,
147
+ to: target,
148
+ kind: "calls",
149
+ resolved: true,
150
+ };
151
+ }
152
+ return {
153
+ id: `unresolved:${call.from}->${call.to}:${index}`,
154
+ from: call.from,
155
+ to: call.to,
156
+ kind: "unresolved-call",
157
+ resolved: false,
158
+ };
159
+ });
160
+
161
+ const sortedSymbols = symbolNodes.slice().sort(compareSymbols);
162
+ const sortedCalls = [...defines, ...resolvedCalls].sort(compareCalls);
163
+
164
+ // Reassign call ids after sort so they are stable + positional-free of index
165
+ // ordering surprises (ids derived from endpoints, not array position).
166
+ const stableCalls = sortedCalls.map((call) => ({
167
+ ...call,
168
+ id: `${call.kind}:${call.from}=>${call.to}`,
169
+ }));
170
+ // De-duplicate identical edges (same from/to/kind) produced by repeats.
171
+ const seen = new Set<string>();
172
+ const dedupedCalls: CallEdge[] = [];
173
+ for (const call of stableCalls) {
174
+ const key = `${call.kind}|${call.from}|${call.to}`;
175
+ if (seen.has(key)) {
176
+ continue;
177
+ }
178
+ seen.add(key);
179
+ dedupedCalls.push(call);
180
+ }
181
+
182
+ return { symbols: sortedSymbols, calls: dedupedCalls };
183
+ }
184
+
185
+ // Cross-file call resolution (global pass). `extractSymbolLayer` runs per file,
186
+ // so it can only resolve calls whose target is defined in the SAME file; every
187
+ // cross-file call is left as `unresolved-call` — which made callers/impact
188
+ // under-report badly (e.g. `TasksApi.runPipeline()` from another file). This
189
+ // pass runs AFTER all files are aggregated: it upgrades an `unresolved-call` to
190
+ // a resolved `calls` edge when the callee's last segment maps to EXACTLY ONE
191
+ // symbol across the whole graph. Ambiguous names (collisions) are left
192
+ // unresolved — never guess a wrong edge. Self-edges are dropped.
193
+ export function resolveCrossFileCalls(symbols: SymbolNode[], calls: CallEdge[]): CallEdge[] {
194
+ const nameCount = new Map<string, number>();
195
+ const nameToId = new Map<string, string>();
196
+ for (const symbol of symbols) {
197
+ nameCount.set(symbol.name, (nameCount.get(symbol.name) ?? 0) + 1);
198
+ if (!nameToId.has(symbol.name)) {
199
+ nameToId.set(symbol.name, symbol.id);
200
+ }
201
+ }
202
+
203
+ const seen = new Set<string>();
204
+ const out: CallEdge[] = [];
205
+ for (const call of calls) {
206
+ let edge = call;
207
+ if (call.kind === "unresolved-call") {
208
+ const name = lastSegment(call.to);
209
+ const target = nameCount.get(name) === 1 ? nameToId.get(name) : undefined;
210
+ if (target && target !== call.from) {
211
+ edge = { id: `calls:${call.from}=>${target}`, from: call.from, to: target, kind: "calls", resolved: true };
212
+ }
213
+ }
214
+ const key = `${edge.kind}|${edge.from}|${edge.to}`;
215
+ if (seen.has(key)) continue;
216
+ seen.add(key);
217
+ out.push(edge);
218
+ }
219
+ return out;
220
+ }
221
+
222
+ function symbolFromNode(
223
+ node: TsNode,
224
+ filePath: string,
225
+ container: string | null,
226
+ language: Language,
227
+ ): RawSymbol | null {
228
+ const kind = symbolKind(node.type);
229
+ if (!kind) {
230
+ return null;
231
+ }
232
+ const name = nameOf(node);
233
+ if (!name) {
234
+ return null;
235
+ }
236
+ return makeSymbol(node, filePath, container, language, kind, name);
237
+ }
238
+
239
+ function arrowFunctionSymbols(
240
+ node: TsNode,
241
+ filePath: string,
242
+ container: string | null,
243
+ language: Language,
244
+ ): RawSymbol[] {
245
+ const out: RawSymbol[] = [];
246
+ for (const declarator of node.namedChildren) {
247
+ if (declarator.type !== "variable_declarator") {
248
+ continue;
249
+ }
250
+ const value = declarator.childForFieldName("value");
251
+ if (!value) {
252
+ continue;
253
+ }
254
+ if (value.type === "arrow_function" || value.type === "function" || value.type === "function_expression") {
255
+ const nameNode = declarator.childForFieldName("name");
256
+ const name = nameNode?.text;
257
+ if (name) {
258
+ out.push(makeSymbol(declarator, filePath, container, language, "function", name));
259
+ }
260
+ }
261
+ }
262
+ return out;
263
+ }
264
+
265
+ function makeSymbol(
266
+ node: TsNode,
267
+ filePath: string,
268
+ container: string | null,
269
+ language: Language,
270
+ kind: SymbolKind,
271
+ name: string,
272
+ ): RawSymbol {
273
+ const startLine = node.startPosition.row + 1;
274
+ const endLine = node.endPosition.row + 1;
275
+ const base = container ? `${filePath}#${container}.${name}` : `${filePath}#${name}`;
276
+ const symbol: SymbolNode = {
277
+ id: base,
278
+ kind,
279
+ path: filePath,
280
+ name,
281
+ container,
282
+ startLine,
283
+ endLine,
284
+ language,
285
+ signature: renderSignature(node, kind, container, name),
286
+ };
287
+ return { base, node: symbol };
288
+ }
289
+
290
+ function callFromNode(node: TsNode, filePath: string, enclosingSymbolId: string | null): CallEdge | null {
291
+ // Extract function/method reference from call expressions
292
+ const fn = node.childForFieldName("function")
293
+ ?? node.childForFieldName("constructor")
294
+ ?? node.childForFieldName("method"); // Java method_invocation
295
+
296
+ let calleeText: string;
297
+ if (fn?.text) {
298
+ calleeText = firstLine(fn.text);
299
+ } else if (node.text) {
300
+ // For Python and Java: fall back to extracting from full text
301
+ calleeText = firstLine(node.text);
302
+ } else {
303
+ return null;
304
+ }
305
+
306
+ if (!calleeText) {
307
+ return null;
308
+ }
309
+ return {
310
+ id: "pending",
311
+ from: enclosingSymbolId ?? filePath,
312
+ to: calleeText,
313
+ kind: "calls",
314
+ resolved: false,
315
+ };
316
+ }
317
+
318
+ function symbolKind(type: string): SymbolKind | null {
319
+ if (FUNCTION_TYPES.has(type)) {
320
+ return "function";
321
+ }
322
+ if (METHOD_TYPES.has(type)) {
323
+ return "method";
324
+ }
325
+ if (CLASS_TYPES.has(type)) {
326
+ return "class";
327
+ }
328
+ if (INTERFACE_TYPES.has(type)) {
329
+ return "interface";
330
+ }
331
+ return null;
332
+ }
333
+
334
+ function nameOf(node: TsNode): string | null {
335
+ const nameNode = node.childForFieldName("name");
336
+ if (nameNode && nameNode.text) {
337
+ return firstLine(nameNode.text);
338
+ }
339
+ return null;
340
+ }
341
+
342
+ function renderSignature(node: TsNode, kind: SymbolKind, container: string | null, name: string): string {
343
+ const params = node.childForFieldName("parameters");
344
+ const paramText = params ? firstLine(params.text) : "()";
345
+ const prefix = container ? `${container}.` : "";
346
+
347
+ switch (kind) {
348
+ case "class":
349
+ return `class ${name}`;
350
+ case "interface":
351
+ return `interface ${name}`;
352
+ case "method":
353
+ return `${prefix}${name}${paramText}`;
354
+ case "function":
355
+ return `${name}${paramText}`;
356
+ default:
357
+ return `${name}${paramText}`;
358
+ }
359
+ }
360
+
361
+ function lastSegment(callee: string): string {
362
+ const withoutCall = callee.replace(/\(.*$/s, "");
363
+ const parts = withoutCall.split(".");
364
+ return (parts[parts.length - 1] ?? withoutCall).trim();
365
+ }
366
+
367
+ function firstLine(text: string): string {
368
+ return text.split("\n")[0]?.trim() ?? "";
369
+ }
370
+
371
+ function compareSymbols(a: SymbolNode, b: SymbolNode): number {
372
+ if (a.path !== b.path) {
373
+ return a.path < b.path ? -1 : 1;
374
+ }
375
+ if (a.startLine !== b.startLine) {
376
+ return a.startLine - b.startLine;
377
+ }
378
+ return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
379
+ }
380
+
381
+ function compareCalls(a: CallEdge, b: CallEdge): number {
382
+ if (a.from !== b.from) {
383
+ return a.from < b.from ? -1 : 1;
384
+ }
385
+ if (a.to !== b.to) {
386
+ return a.to < b.to ? -1 : 1;
387
+ }
388
+ return a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0;
389
+ }