@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.
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/cli.js +48427 -0
- package/dist/proxy-worker.js +381 -0
- package/package.json +63 -0
- package/src/gdgraph/affected.test.ts +100 -0
- package/src/gdgraph/affected.ts +135 -0
- package/src/gdgraph/build-integrity.test.ts +193 -0
- package/src/gdgraph/build-lang.test.ts +399 -0
- package/src/gdgraph/build.test.ts +120 -0
- package/src/gdgraph/build.ts +804 -0
- package/src/gdgraph/config.test.ts +47 -0
- package/src/gdgraph/config.ts +113 -0
- package/src/gdgraph/core-sources.test.ts +99 -0
- package/src/gdgraph/core-sources.ts +20 -0
- package/src/gdgraph/enrich.ts +79 -0
- package/src/gdgraph/fallback.test.ts +153 -0
- package/src/gdgraph/find.test.ts +78 -0
- package/src/gdgraph/find.ts +107 -0
- package/src/gdgraph/pagerank.ts +129 -0
- package/src/gdgraph/path.test.ts +56 -0
- package/src/gdgraph/path.ts +112 -0
- package/src/gdgraph/query.ts +133 -0
- package/src/gdgraph/repomap.test.ts +110 -0
- package/src/gdgraph/repomap.ts +193 -0
- package/src/gdgraph/service.test.ts +59 -0
- package/src/gdgraph/service.ts +51 -0
- package/src/gdgraph/staleness.ts +20 -0
- package/src/gdgraph/symbol.test.ts +89 -0
- package/src/gdgraph/symbol.ts +123 -0
- package/src/gdgraph/symbols-capability.test.ts +41 -0
- package/src/gdgraph/symbols-capability.ts +49 -0
- package/src/gdgraph/target.ts +56 -0
- package/src/gdgraph/treesitter/adapter.test.ts +249 -0
- package/src/gdgraph/treesitter/adapter.ts +213 -0
- package/src/gdgraph/treesitter/extract.test.ts +278 -0
- package/src/gdgraph/treesitter/extract.ts +389 -0
- package/src/gdgraph/treesitter/grammars.ts +110 -0
- package/src/gdgraph/treesitter/no-treesitter-import.test.ts +51 -0
- package/src/gdgraph/treesitter/resolve-calls.test.ts +38 -0
- package/src/gdgraph/types.ts +63 -0
- package/src/gdskills/bundled/rules/core/api-contracts.mdc +148 -0
- package/src/gdskills/bundled/rules/core/async-patterns.mdc +185 -0
- package/src/gdskills/bundled/rules/core/clean-architecture.mdc +163 -0
- package/src/gdskills/bundled/rules/core/code-review-ai-assistant.mdc +49 -0
- package/src/gdskills/bundled/rules/core/code-review-b091-profile.mdc +48 -0
- package/src/gdskills/bundled/rules/core/code-style-patterns.mdc +54 -0
- package/src/gdskills/bundled/rules/core/commit-message-formatting.mdc +25 -0
- package/src/gdskills/bundled/rules/core/database-patterns.mdc +162 -0
- package/src/gdskills/bundled/rules/core/documentation-management.mdc +106 -0
- package/src/gdskills/bundled/rules/core/error-handling.mdc +163 -0
- package/src/gdskills/bundled/rules/core/execution-metrics.md +99 -0
- package/src/gdskills/bundled/rules/core/frontend-assistant.mdc +40 -0
- package/src/gdskills/bundled/rules/core/git-rules.mdc +37 -0
- package/src/gdskills/bundled/rules/core/gproject-contracts.mdc +198 -0
- package/src/gdskills/bundled/rules/core/implementation-doc-mandate.mdc +149 -0
- package/src/gdskills/bundled/rules/core/implementation-plans.mdc +58 -0
- package/src/gdskills/bundled/rules/core/jobs-documentation.mdc +221 -0
- package/src/gdskills/bundled/rules/core/mobx-store-template.mdc +561 -0
- package/src/gdskills/bundled/rules/core/model-selection.mdc +53 -0
- package/src/gdskills/bundled/rules/core/nestjs-dto.mdc +50 -0
- package/src/gdskills/bundled/rules/core/playwright-testing.mdc +321 -0
- package/src/gdskills/bundled/rules/core/requirements-management.mdc +35 -0
- package/src/gdskills/bundled/rules/core/requirements-package-standard.mdc +103 -0
- package/src/gdskills/bundled/rules/core/review-agent-profile.mdc +49 -0
- package/src/gdskills/bundled/rules/core/review-strict-profile.mdc +44 -0
- package/src/gdskills/bundled/rules/core/rule-management-workflow.mdc +62 -0
- package/src/gdskills/bundled/rules/core/security-baseline.mdc +155 -0
- package/src/gdskills/bundled/rules/core/shared-definitions.mdc +79 -0
- package/src/gdskills/bundled/rules/core/skill-lifecycle.mdc +55 -0
- package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +88 -0
- package/src/gdskills/bundled/rules/core/solid-principles.mdc +149 -0
- package/src/gdskills/bundled/rules/core/storybook-guidelines.mdc +36 -0
- package/src/gdskills/bundled/rules/core/subagent-context-construction.md +107 -0
- package/src/gdskills/bundled/rules/core/subagent-status-protocol.md +169 -0
- package/src/gdskills/bundled/rules/core/tdd-workflow.mdc +96 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +47 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +55 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +352 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +39 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +29 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +567 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +567 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +655 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +567 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +567 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/input-contract.schema.json +134 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/orchestrator-prompt.md +183 -0
- package/src/gdskills/bundled/skills/orchestration/context-collector/output-contract.schema.json +208 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +1412 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +1412 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.detail.md +735 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +468 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +1412 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +1412 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.md +62 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.template.md +79 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/input-contract.schema.json +247 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/orchestrator-prompt.md +362 -0
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/output-contract.schema.json +87 -0
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +105 -0
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +105 -0
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +165 -0
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +379 -0
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json +33 -0
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/output-contract.schema.json +41 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +307 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +307 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +373 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +307 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +307 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/input-contract.schema.json +130 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/issue-request.template.md +50 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +155 -0
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/output-contract.schema.json +61 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +365 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +365 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +373 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +365 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +365 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/input-contract.schema.json +55 -0
- package/src/gdskills/bundled/skills/orchestration/job-documenter/output-contract.schema.json +58 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +1701 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +1701 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1734 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +1701 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +1701 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/input-contract.schema.json +188 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/orchestrator-prompt.md +179 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/output-contract.schema.json +103 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json +164 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +395 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +395 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +556 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +395 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +395 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +204 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/orchestrator-prompt.md +206 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +51 -0
- package/src/gdskills/bundled/skills/orchestration/task-implementer/task-request.template.md +111 -0
- package/src/gdskills/bundled/skills/planning/autodoc-analyst/SKILL.md +163 -0
- package/src/gdskills/bundled/skills/planning/autodoc-architect/SKILL.md +161 -0
- package/src/gdskills/bundled/skills/planning/autodoc-assembler/SKILL.md +128 -0
- package/src/gdskills/bundled/skills/planning/autodoc-orchestrator/SKILL.md +321 -0
- package/src/gdskills/bundled/skills/planning/autodoc-scanner/SKILL.md +166 -0
- package/src/gdskills/bundled/skills/planning/autodoc-writer/SKILL.md +253 -0
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.claude.md +46 -0
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +90 -0
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +90 -0
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +90 -0
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +33 -0
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +33 -0
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/planning/docpack-orchestrator/SKILL.md +142 -0
- package/src/gdskills/bundled/skills/planning/docpack-orchestrator/input-contract.schema.json +31 -0
- package/src/gdskills/bundled/skills/planning/docpack-orchestrator/output-contract.schema.json +31 -0
- package/src/gdskills/bundled/skills/planning/docpack-review/SKILL.md +80 -0
- package/src/gdskills/bundled/skills/planning/docpack-review/input-contract.schema.json +20 -0
- package/src/gdskills/bundled/skills/planning/docpack-review/output-contract.schema.json +36 -0
- package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +181 -0
- package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +181 -0
- package/src/gdskills/bundled/skills/planning/interview/SKILL.md +187 -0
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.claude.md +94 -0
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +99 -0
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +99 -0
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +105 -0
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +33 -0
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +33 -0
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.md +237 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +33 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +33 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +193 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +193 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +193 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +193 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +193 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/input-contract.schema.json +63 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/output-contract.schema.json +175 -0
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +31 -0
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +31 -0
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.md +153 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +32 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +32 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.md +166 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +44 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +44 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.md +243 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +43 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +43 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.md +193 -0
- package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +47 -0
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +87 -0
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +87 -0
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +87 -0
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +100 -0
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +100 -0
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +100 -0
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.claude.md +45 -0
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +84 -0
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +84 -0
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +84 -0
- package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +66 -0
- package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +66 -0
- package/src/gdskills/bundled/skills/quality/commit/SKILL.md +66 -0
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.claude.md +40 -0
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +66 -0
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +66 -0
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +66 -0
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.claude.md +45 -0
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +81 -0
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +81 -0
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +81 -0
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.claude.md +42 -0
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +70 -0
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +70 -0
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +70 -0
- package/src/gdskills/bundled/skills/quality/metaproject-security/SKILL.md +103 -0
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.claude.md +48 -0
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +83 -0
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +83 -0
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +83 -0
- package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +75 -0
- package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +75 -0
- package/src/gdskills/bundled/skills/quality/pr/SKILL.md +75 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +364 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +295 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +378 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +364 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +38 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/input-contract.schema.json +53 -0
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/output-contract.schema.json +46 -0
- package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +52 -0
- package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +52 -0
- package/src/gdskills/bundled/skills/quality/push/SKILL.md +52 -0
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.claude.md +40 -0
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +61 -0
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +61 -0
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.md +61 -0
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.claude.md +30 -0
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +75 -0
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +75 -0
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +75 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +64 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +74 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +339 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +48 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +44 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/input-contract.schema.json +121 -0
- package/src/gdskills/bundled/skills/quality/tests-creator/output-contract.schema.json +127 -0
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +204 -0
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +204 -0
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +203 -0
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +204 -0
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +204 -0
- package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.codex.md +209 -0
- package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.cursor.md +209 -0
- package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.md +208 -0
- package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.opencode.md +209 -0
- package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.zed.md +209 -0
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +263 -0
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +263 -0
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +258 -0
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +263 -0
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +263 -0
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +175 -0
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +175 -0
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +168 -0
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +175 -0
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +175 -0
- package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +360 -0
- package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +337 -0
- package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +477 -0
- package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +93 -0
- package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +119 -0
- package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +595 -0
- package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +144 -0
- package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +519 -0
- package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +323 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +927 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/input-contract.schema.json +153 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/output-contract.schema.json +77 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/review-context.schema.json +389 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +189 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +241 -0
- package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +344 -0
- package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +327 -0
- package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +348 -0
- package/src/gdskills/bundled/skills/review/review-strict/SKILL.md +328 -0
- package/src/gdskills/bundled/skills/review/review-style/SKILL.md +343 -0
- package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +134 -0
- package/src/gdskills/bundled/skills/shared/git-merge-base.md +33 -0
- package/src/gdskills/contracts/agent-event.schema.json +32 -0
- package/src/gdskills/contracts/orchestrator-state.schema.json +58 -0
- package/src/gdskills/contracts/review-finding.schema.json +59 -0
- package/src/gdskills/contracts/subagent-dispatch.schema.json +123 -0
- 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
|
+
}
|