@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,381 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/harness/process/sandbox/proxy-worker.ts
|
|
3
|
+
import { parentPort, workerData } from "worker_threads";
|
|
4
|
+
|
|
5
|
+
// src/harness/process/sandbox/proxy.ts
|
|
6
|
+
import http from "http";
|
|
7
|
+
import https from "https";
|
|
8
|
+
import net from "net";
|
|
9
|
+
function normalizeHost(host) {
|
|
10
|
+
return host.toLowerCase().replace(/\.$/, "");
|
|
11
|
+
}
|
|
12
|
+
function matchesAllowlist(host, allowed) {
|
|
13
|
+
const h = normalizeHost(host);
|
|
14
|
+
if (h.length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
for (const pattern of allowed) {
|
|
18
|
+
const p = normalizeHost(pattern);
|
|
19
|
+
if (p.startsWith("*.")) {
|
|
20
|
+
const base = p.slice(2);
|
|
21
|
+
if (base.length > 0 && (h === base || h.endsWith(`.${base}`))) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
} else if (h === p) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
function substituteValue(value, masks) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
return;
|
|
33
|
+
const one = (s) => {
|
|
34
|
+
let out = s;
|
|
35
|
+
for (const m of masks)
|
|
36
|
+
out = out.split(m.sentinel).join(m.realValue);
|
|
37
|
+
return out;
|
|
38
|
+
};
|
|
39
|
+
return Array.isArray(value) ? value.map(one) : one(value);
|
|
40
|
+
}
|
|
41
|
+
function applyMasks(headers, masks, hostname) {
|
|
42
|
+
const applicable = masks.filter((m) => matchesAllowlist(hostname, m.injectHosts));
|
|
43
|
+
if (applicable.length === 0)
|
|
44
|
+
return headers;
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
47
|
+
out[key] = substituteValue(value, applicable);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function httpTarget(req) {
|
|
52
|
+
const raw = req.url ?? "";
|
|
53
|
+
try {
|
|
54
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
55
|
+
const u = new URL(raw);
|
|
56
|
+
return { hostname: u.hostname, port: u.port ? Number(u.port) : 80 };
|
|
57
|
+
}
|
|
58
|
+
} catch {}
|
|
59
|
+
const hostHeader = req.headers.host;
|
|
60
|
+
if (hostHeader) {
|
|
61
|
+
const [hostname, port] = hostHeader.split(":");
|
|
62
|
+
if (hostname)
|
|
63
|
+
return { hostname, port: port ? Number(port) : 80 };
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
async function createAllowlistProxy(opts) {
|
|
68
|
+
const host = opts.host ?? "127.0.0.1";
|
|
69
|
+
const allowed = opts.allowedDomains;
|
|
70
|
+
const decide = (d) => {
|
|
71
|
+
opts.onDecision?.(d);
|
|
72
|
+
return d.allowed;
|
|
73
|
+
};
|
|
74
|
+
const server = http.createServer((req, res) => {
|
|
75
|
+
const target = httpTarget(req);
|
|
76
|
+
const hostname = target?.hostname ?? "";
|
|
77
|
+
if (!target || !decide({ host: hostname, allowed: matchesAllowlist(hostname, allowed), kind: "http" })) {
|
|
78
|
+
res.writeHead(403, { "content-type": "text/plain" });
|
|
79
|
+
res.end("blocked by keryx sandbox network allowlist");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const headers = applyMasks(req.headers, opts.masks ?? [], target.hostname);
|
|
83
|
+
const upstream = http.request({ host: target.hostname, port: target.port, method: req.method, path: pathFromUrl(req.url), headers }, (up) => {
|
|
84
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
85
|
+
up.pipe(res);
|
|
86
|
+
});
|
|
87
|
+
upstream.on("error", () => {
|
|
88
|
+
if (!res.headersSent)
|
|
89
|
+
res.writeHead(502);
|
|
90
|
+
res.end("upstream error");
|
|
91
|
+
});
|
|
92
|
+
req.pipe(upstream);
|
|
93
|
+
});
|
|
94
|
+
const makeMitmHandler = (pinnedHost, pinnedPort) => (req, res) => {
|
|
95
|
+
const hostHeader = req.headers.host ?? "";
|
|
96
|
+
const [rawHost, rawPort] = hostHeader.split(":");
|
|
97
|
+
const hostname = rawHost ?? "";
|
|
98
|
+
const upstreamPort = rawPort ? Number(rawPort) : pinnedPort;
|
|
99
|
+
const permitted = matchesAllowlist(hostname, allowed) && normalizeHost(hostname) === pinnedHost && upstreamPort === pinnedPort;
|
|
100
|
+
if (!decide({ host: hostname, allowed: permitted, kind: "http" })) {
|
|
101
|
+
res.writeHead(403, { "content-type": "text/plain" });
|
|
102
|
+
res.end("blocked by keryx sandbox network allowlist");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const headers = applyMasks(req.headers, opts.masks ?? [], hostname);
|
|
106
|
+
const upstreamReq = https.request({
|
|
107
|
+
host: hostname,
|
|
108
|
+
port: upstreamPort,
|
|
109
|
+
method: req.method,
|
|
110
|
+
path: req.url ?? "/",
|
|
111
|
+
headers,
|
|
112
|
+
servername: hostname,
|
|
113
|
+
...opts.upstreamCa !== undefined ? { ca: opts.upstreamCa } : {}
|
|
114
|
+
}, (up) => {
|
|
115
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
116
|
+
up.pipe(res);
|
|
117
|
+
});
|
|
118
|
+
upstreamReq.on("error", () => {
|
|
119
|
+
if (!res.headersSent)
|
|
120
|
+
res.writeHead(502);
|
|
121
|
+
res.end("upstream error");
|
|
122
|
+
});
|
|
123
|
+
req.pipe(upstreamReq);
|
|
124
|
+
};
|
|
125
|
+
const mitmServers = new Map;
|
|
126
|
+
const mitmPortFor = async (hostname, targetPort, ca) => {
|
|
127
|
+
const host2 = normalizeHost(hostname);
|
|
128
|
+
const key = `${host2}:${targetPort}`;
|
|
129
|
+
const hit = mitmServers.get(key);
|
|
130
|
+
if (hit)
|
|
131
|
+
return hit.port;
|
|
132
|
+
const leaf = await ca.issueLeaf(host2);
|
|
133
|
+
const server2 = https.createServer({ key: leaf.keyPem, cert: leaf.certPem }, makeMitmHandler(host2, targetPort));
|
|
134
|
+
await new Promise((resolve) => server2.listen(0, "127.0.0.1", () => resolve()));
|
|
135
|
+
const addr2 = server2.address();
|
|
136
|
+
const port2 = addr2 && typeof addr2 === "object" ? addr2.port : 0;
|
|
137
|
+
mitmServers.set(key, { server: server2, port: port2 });
|
|
138
|
+
return port2;
|
|
139
|
+
};
|
|
140
|
+
server.on("connect", (req, clientSocket, head) => {
|
|
141
|
+
const [reqHost, reqPort] = (req.url ?? "").split(":");
|
|
142
|
+
const hostname = reqHost ?? "";
|
|
143
|
+
const port2 = Number(reqPort) || 443;
|
|
144
|
+
if (!decide({ host: hostname, allowed: matchesAllowlist(hostname, allowed), kind: "connect" })) {
|
|
145
|
+
clientSocket.write(`HTTP/1.1 403 Forbidden\r
|
|
146
|
+
\r
|
|
147
|
+
`);
|
|
148
|
+
clientSocket.end();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const ca = opts.tlsTerminate;
|
|
152
|
+
if (ca) {
|
|
153
|
+
(async () => {
|
|
154
|
+
try {
|
|
155
|
+
const terminatorPort = await mitmPortFor(hostname, port2, ca);
|
|
156
|
+
clientSocket.write(`HTTP/1.1 200 Connection Established\r
|
|
157
|
+
\r
|
|
158
|
+
`);
|
|
159
|
+
const internal = net.connect(terminatorPort, "127.0.0.1", () => {
|
|
160
|
+
if (head && head.length > 0)
|
|
161
|
+
internal.write(head);
|
|
162
|
+
clientSocket.pipe(internal);
|
|
163
|
+
internal.pipe(clientSocket);
|
|
164
|
+
});
|
|
165
|
+
internal.on("error", () => clientSocket.destroy());
|
|
166
|
+
clientSocket.on("error", () => internal.destroy());
|
|
167
|
+
} catch {
|
|
168
|
+
clientSocket.write(`HTTP/1.1 502 Bad Gateway\r
|
|
169
|
+
\r
|
|
170
|
+
`);
|
|
171
|
+
clientSocket.end();
|
|
172
|
+
}
|
|
173
|
+
})();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const upstream = net.connect(port2, hostname, () => {
|
|
177
|
+
clientSocket.write(`HTTP/1.1 200 Connection Established\r
|
|
178
|
+
\r
|
|
179
|
+
`);
|
|
180
|
+
if (head && head.length > 0)
|
|
181
|
+
upstream.write(head);
|
|
182
|
+
upstream.pipe(clientSocket);
|
|
183
|
+
clientSocket.pipe(upstream);
|
|
184
|
+
});
|
|
185
|
+
upstream.on("error", () => {
|
|
186
|
+
clientSocket.write(`HTTP/1.1 502 Bad Gateway\r
|
|
187
|
+
\r
|
|
188
|
+
`);
|
|
189
|
+
clientSocket.end();
|
|
190
|
+
});
|
|
191
|
+
clientSocket.on("error", () => upstream.destroy());
|
|
192
|
+
});
|
|
193
|
+
await new Promise((resolve) => server.listen(opts.port ?? 0, host, () => resolve()));
|
|
194
|
+
const addr = server.address();
|
|
195
|
+
const port = addr && typeof addr === "object" ? addr.port : 0;
|
|
196
|
+
return {
|
|
197
|
+
host,
|
|
198
|
+
port,
|
|
199
|
+
close: async () => {
|
|
200
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
201
|
+
await Promise.all([...mitmServers.values()].map(({ server: s }) => new Promise((resolve) => s.close(() => resolve()))));
|
|
202
|
+
mitmServers.clear();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function pathFromUrl(url) {
|
|
207
|
+
const raw = url ?? "/";
|
|
208
|
+
try {
|
|
209
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
210
|
+
const u = new URL(raw);
|
|
211
|
+
return `${u.pathname}${u.search}`;
|
|
212
|
+
}
|
|
213
|
+
} catch {}
|
|
214
|
+
return raw;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/harness/process/sandbox/tls-ca.ts
|
|
218
|
+
import { spawn } from "child_process";
|
|
219
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
220
|
+
import { tmpdir } from "os";
|
|
221
|
+
import path from "path";
|
|
222
|
+
function run(bin, args) {
|
|
223
|
+
return new Promise((resolve, reject) => {
|
|
224
|
+
const proc = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
225
|
+
let out = "";
|
|
226
|
+
let err = "";
|
|
227
|
+
proc.stdout.on("data", (d) => out += d);
|
|
228
|
+
proc.stderr.on("data", (d) => err += d);
|
|
229
|
+
proc.on("error", reject);
|
|
230
|
+
proc.on("close", (code) => code === 0 ? resolve(out) : reject(new Error(`${bin} ${args[0]} failed (exit ${code}): ${err.trim()}`)));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
var CA_CONFIG = `[req]
|
|
234
|
+
distinguished_name = dn
|
|
235
|
+
x509_extensions = v3_ca
|
|
236
|
+
prompt = no
|
|
237
|
+
|
|
238
|
+
[dn]
|
|
239
|
+
CN = keryx sandbox run CA
|
|
240
|
+
|
|
241
|
+
[v3_ca]
|
|
242
|
+
basicConstraints = critical,CA:TRUE,pathlen:0
|
|
243
|
+
keyUsage = critical,keyCertSign,cRLSign
|
|
244
|
+
subjectKeyIdentifier = hash
|
|
245
|
+
`;
|
|
246
|
+
function leafConfig(host) {
|
|
247
|
+
const isIp = /^\d{1,3}(\.\d{1,3}){3}$/.test(host);
|
|
248
|
+
const san = isIp ? `IP:${host}` : `DNS:${host}`;
|
|
249
|
+
return `[req]
|
|
250
|
+
distinguished_name = dn
|
|
251
|
+
req_extensions = v3_req
|
|
252
|
+
prompt = no
|
|
253
|
+
|
|
254
|
+
[dn]
|
|
255
|
+
CN = ${host}
|
|
256
|
+
|
|
257
|
+
[v3_req]
|
|
258
|
+
basicConstraints = CA:FALSE
|
|
259
|
+
keyUsage = critical,digitalSignature,keyEncipherment
|
|
260
|
+
extendedKeyUsage = serverAuth
|
|
261
|
+
subjectAltName = ${san}
|
|
262
|
+
`;
|
|
263
|
+
}
|
|
264
|
+
async function createRunCa(options = {}) {
|
|
265
|
+
const openssl = options.openssl ?? "openssl";
|
|
266
|
+
const days = String(options.days ?? 1);
|
|
267
|
+
const root = await mkdtemp(path.join(options.tmpDir ?? tmpdir(), "keryx-ca-"));
|
|
268
|
+
await mkdir(root, { recursive: true, mode: 448 });
|
|
269
|
+
const caKey = path.join(root, "ca.key");
|
|
270
|
+
const caCert = path.join(root, "ca.crt");
|
|
271
|
+
const caCfg = path.join(root, "ca.cnf");
|
|
272
|
+
await writeFile(caCfg, CA_CONFIG, { mode: 384 });
|
|
273
|
+
await run(openssl, [
|
|
274
|
+
"req",
|
|
275
|
+
"-x509",
|
|
276
|
+
"-nodes",
|
|
277
|
+
"-newkey",
|
|
278
|
+
"rsa:2048",
|
|
279
|
+
"-keyout",
|
|
280
|
+
caKey,
|
|
281
|
+
"-out",
|
|
282
|
+
caCert,
|
|
283
|
+
"-days",
|
|
284
|
+
days,
|
|
285
|
+
"-config",
|
|
286
|
+
caCfg
|
|
287
|
+
]);
|
|
288
|
+
const caCertPem = await readFile(caCert, "utf8");
|
|
289
|
+
const cache = new Map;
|
|
290
|
+
return {
|
|
291
|
+
caCertPem,
|
|
292
|
+
async issueLeaf(host) {
|
|
293
|
+
const key = host.toLowerCase();
|
|
294
|
+
const hit = cache.get(key);
|
|
295
|
+
if (hit)
|
|
296
|
+
return hit;
|
|
297
|
+
const safe = key.replace(/[^a-z0-9.-]/g, "_");
|
|
298
|
+
const leafKey = path.join(root, `${safe}.key`);
|
|
299
|
+
const leafCsr = path.join(root, `${safe}.csr`);
|
|
300
|
+
const leafCrt = path.join(root, `${safe}.crt`);
|
|
301
|
+
const leafCfg = path.join(root, `${safe}.cnf`);
|
|
302
|
+
await writeFile(leafCfg, leafConfig(key), { mode: 384 });
|
|
303
|
+
await run(openssl, [
|
|
304
|
+
"req",
|
|
305
|
+
"-new",
|
|
306
|
+
"-nodes",
|
|
307
|
+
"-newkey",
|
|
308
|
+
"rsa:2048",
|
|
309
|
+
"-keyout",
|
|
310
|
+
leafKey,
|
|
311
|
+
"-out",
|
|
312
|
+
leafCsr,
|
|
313
|
+
"-config",
|
|
314
|
+
leafCfg
|
|
315
|
+
]);
|
|
316
|
+
await run(openssl, [
|
|
317
|
+
"x509",
|
|
318
|
+
"-req",
|
|
319
|
+
"-in",
|
|
320
|
+
leafCsr,
|
|
321
|
+
"-CA",
|
|
322
|
+
caCert,
|
|
323
|
+
"-CAkey",
|
|
324
|
+
caKey,
|
|
325
|
+
"-CAcreateserial",
|
|
326
|
+
"-out",
|
|
327
|
+
leafCrt,
|
|
328
|
+
"-days",
|
|
329
|
+
days,
|
|
330
|
+
"-extfile",
|
|
331
|
+
leafCfg,
|
|
332
|
+
"-extensions",
|
|
333
|
+
"v3_req"
|
|
334
|
+
]);
|
|
335
|
+
const issued = {
|
|
336
|
+
certPem: await readFile(leafCrt, "utf8"),
|
|
337
|
+
keyPem: await readFile(leafKey, "utf8")
|
|
338
|
+
};
|
|
339
|
+
cache.set(key, issued);
|
|
340
|
+
return issued;
|
|
341
|
+
},
|
|
342
|
+
async dispose() {
|
|
343
|
+
cache.clear();
|
|
344
|
+
await rm(root, { recursive: true, force: true });
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/harness/process/sandbox/proxy-worker.ts
|
|
350
|
+
async function main() {
|
|
351
|
+
if (!parentPort) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const data = workerData ?? {};
|
|
355
|
+
let ca;
|
|
356
|
+
if (data.tlsTerminate === true) {
|
|
357
|
+
ca = await createRunCa();
|
|
358
|
+
}
|
|
359
|
+
const port = parentPort;
|
|
360
|
+
const proxy = await createAllowlistProxy({
|
|
361
|
+
allowedDomains: Array.isArray(data.allowedDomains) ? data.allowedDomains : [],
|
|
362
|
+
...typeof data.host === "string" ? { host: data.host } : {},
|
|
363
|
+
...Array.isArray(data.masks) ? { masks: data.masks } : {},
|
|
364
|
+
...ca ? { tlsTerminate: ca } : {},
|
|
365
|
+
onDecision: (d) => {
|
|
366
|
+
port.postMessage({ type: "decision", host: d.host, allowed: d.allowed, kind: d.kind });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
parentPort.postMessage({
|
|
370
|
+
type: "ready",
|
|
371
|
+
port: proxy.port,
|
|
372
|
+
host: proxy.host,
|
|
373
|
+
...ca ? { caCertPem: ca.caCertPem } : {}
|
|
374
|
+
});
|
|
375
|
+
parentPort.on("message", (msg) => {
|
|
376
|
+
if (msg && msg.type === "close") {
|
|
377
|
+
proxy.close().then(() => ca ? ca.dispose() : undefined).then(() => process.exit(0));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mrciphersmith/keryx",
|
|
3
|
+
"version": "0.2.9",
|
|
4
|
+
"description": "CLI-first metaproject workspace for AI agents, code graph, project memory, health reports, testing context, wiki, skills, and task flows.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+ssh://git@github.com/MrCipherSmith/keryx.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai-agents",
|
|
17
|
+
"agent-tooling",
|
|
18
|
+
"code-graph",
|
|
19
|
+
"project-memory",
|
|
20
|
+
"developer-tools",
|
|
21
|
+
"bun",
|
|
22
|
+
"typescript",
|
|
23
|
+
"cli",
|
|
24
|
+
"knowledge-base",
|
|
25
|
+
"multi-agent"
|
|
26
|
+
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"keryx": "./dist/cli.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"keryx": "bun ./src/cli.ts",
|
|
32
|
+
"build": "bun build ./src/cli.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core && bun build ./src/harness/process/sandbox/proxy-worker.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core",
|
|
33
|
+
"prepare": "bun run build",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "bun test",
|
|
36
|
+
"check": "tsc --noEmit && bun test",
|
|
37
|
+
"check:doc-links": "bun scripts/check-doc-links.ts",
|
|
38
|
+
"test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"src/gdgraph",
|
|
43
|
+
"src/gdskills/bundled",
|
|
44
|
+
"src/gdskills/contracts",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"README.md",
|
|
47
|
+
"package.json"
|
|
48
|
+
],
|
|
49
|
+
"dependencies": {},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
|
+
"@opentui/core": "^0.4.5",
|
|
53
|
+
"web-tree-sitter": "^0.22.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/bun": "latest",
|
|
57
|
+
"bun-types": "latest",
|
|
58
|
+
"typescript": "^5"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"bun": ">=1.1.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
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 { computeAffected } from "./affected";
|
|
6
|
+
import { getAffected } from "./query";
|
|
7
|
+
import type { GraphData } from "./types";
|
|
8
|
+
|
|
9
|
+
const FIXTURE_DIR = fileURLToPath(new URL("../../fixtures/transitive-closure/", import.meta.url));
|
|
10
|
+
|
|
11
|
+
async function loadFixtureGraph(): Promise<GraphData> {
|
|
12
|
+
const raw = JSON.parse(await readFile(path.join(FIXTURE_DIR, "graph.json"), "utf8"));
|
|
13
|
+
return { nodes: raw.nodes, edges: raw.edges };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function loadExpected(): Promise<any> {
|
|
17
|
+
return JSON.parse(await readFile(path.join(FIXTURE_DIR, "expected.json"), "utf8"));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The pre-block default renderer, replicated verbatim so we can assert the new
|
|
21
|
+
// path is byte-for-byte identical at depth 1 (AC2.2).
|
|
22
|
+
function renderDefault(result: { target: string; dependencies: string[]; dependents: string[] }): string {
|
|
23
|
+
const lines: string[] = [];
|
|
24
|
+
lines.push(`# Affected context for ${result.target}`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
lines.push("## Dependencies");
|
|
27
|
+
printList(lines, result.dependencies);
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push("## Dependents");
|
|
30
|
+
printList(lines, result.dependents);
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function printList(lines: string[], items: string[]): void {
|
|
35
|
+
if (items.length === 0) {
|
|
36
|
+
lines.push("- none");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
for (const item of items) {
|
|
40
|
+
lines.push(`- ${item}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test("AC2.1 — exact N-hop dependent closure per target per depth", async () => {
|
|
45
|
+
const graph = await loadFixtureGraph();
|
|
46
|
+
const expected = await loadExpected();
|
|
47
|
+
|
|
48
|
+
for (const [target, byDepth] of Object.entries(expected.closures) as [string, Record<string, string[]>][]) {
|
|
49
|
+
for (const [depthKey, expectedSet] of Object.entries(byDepth)) {
|
|
50
|
+
const result = computeAffected(graph, target, { depth: Number(depthKey) });
|
|
51
|
+
expect(result.dependents).toEqual(expectedSet);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("AC2.3 — cyclic fixture terminates and is deterministic across runs", async () => {
|
|
57
|
+
const graph = await loadFixtureGraph();
|
|
58
|
+
const a = computeAffected(graph, "src/x.ts", { depth: 10 });
|
|
59
|
+
const b = computeAffected(graph, "src/x.ts", { depth: 10 });
|
|
60
|
+
expect(a.dependents).toEqual(["src/y.ts", "src/z.ts"]);
|
|
61
|
+
expect(a.dependents).toEqual(b.dependents);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("AC2.2 — default / --depth 1 renderer is byte-identical to pre-block getAffected", async () => {
|
|
65
|
+
const graph = await loadFixtureGraph();
|
|
66
|
+
for (const target of ["src/a.ts", "src/b.ts", "src/e.ts", "src/x.ts"]) {
|
|
67
|
+
const legacy = getAffected(graph, target);
|
|
68
|
+
const next = computeAffected(graph, target, { depth: 1 });
|
|
69
|
+
|
|
70
|
+
// Underlying data is set-equal (dependents + dependencies).
|
|
71
|
+
expect(next.dependents).toEqual(legacy.dependents);
|
|
72
|
+
expect(next.dependencies).toEqual(legacy.dependencies);
|
|
73
|
+
// Rendered stdout is byte-for-byte identical.
|
|
74
|
+
expect(renderDefault(next)).toBe(renderDefault(legacy));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("AC2.2 — no-flag default depth equals depth 1", async () => {
|
|
79
|
+
const graph = await loadFixtureGraph();
|
|
80
|
+
const noFlag = computeAffected(graph, "src/a.ts");
|
|
81
|
+
const depth1 = computeAffected(graph, "src/a.ts", { depth: 1 });
|
|
82
|
+
expect(noFlag.dependents).toEqual(depth1.dependents);
|
|
83
|
+
expect(renderDefault(noFlag)).toBe(renderDefault(depth1));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("AC2.4 — ranked output ordered hop asc → fanIn desc → path asc", async () => {
|
|
87
|
+
const graph = await loadFixtureGraph();
|
|
88
|
+
const expected = await loadExpected();
|
|
89
|
+
const result = computeAffected(graph, "src/a.ts", { depth: 4, ranked: true });
|
|
90
|
+
expect(result.ranked).toEqual(expected.ranked["src/a.ts"]["4"]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("AC2.4 — dependencies are the unchanged one-hop forward set", async () => {
|
|
94
|
+
const graph = await loadFixtureGraph();
|
|
95
|
+
const expected = await loadExpected();
|
|
96
|
+
for (const [target, deps] of Object.entries(expected.dependencies) as [string, string[]][]) {
|
|
97
|
+
const result = computeAffected(graph, target, { depth: 3 });
|
|
98
|
+
expect(result.dependencies).toEqual(deps);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Pure N-hop transitive `affected` (specification.md §8.2; T-B3, B2).
|
|
2
|
+
//
|
|
3
|
+
// A BFS over the REVERSE-DEPENDENT relation: an edge `from → to` means `from`
|
|
4
|
+
// depends on `to`, so `to`'s dependents are all the `from`s. Given a target we
|
|
5
|
+
// walk outward hop-by-hop to `depth`, collecting the exact transitive dependent
|
|
6
|
+
// closure. Pure over the in-memory graph — no dep, no network, no I/O (B-3,
|
|
7
|
+
// B-4, AC2.5).
|
|
8
|
+
//
|
|
9
|
+
// **Back-compat invariant (B-3, AC2.2):** at `depth === 1` the `dependents` set
|
|
10
|
+
// equals today's `getAffected().dependents` (edges where `kind !== "unresolved"
|
|
11
|
+
// && to === target`), and `dependencies` is the unchanged one-hop forward set,
|
|
12
|
+
// so the default renderer output is byte-for-byte identical.
|
|
13
|
+
|
|
14
|
+
import type { GraphData } from "./types";
|
|
15
|
+
import { resolveGraphTarget } from "./target";
|
|
16
|
+
|
|
17
|
+
export interface RankedDependent {
|
|
18
|
+
path: string;
|
|
19
|
+
// BFS distance from the target (1 = direct dependent).
|
|
20
|
+
hop: number;
|
|
21
|
+
// Number of inbound (non-unresolved) import edges — the fan-in centrality.
|
|
22
|
+
fanIn: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AffectedResult {
|
|
26
|
+
target: string;
|
|
27
|
+
depth: number;
|
|
28
|
+
dependencies: string[];
|
|
29
|
+
dependents: string[];
|
|
30
|
+
// Blast-radius ranking, ordered hop asc → fanIn desc → path asc (AC2.4).
|
|
31
|
+
ranked: RankedDependent[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AffectedOptions {
|
|
35
|
+
depth?: number;
|
|
36
|
+
ranked?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Compute the transitive dependent closure to `depth`. Cycle-safe via a visited
|
|
40
|
+
// set (AC2.3). Deterministic + sorted across repeated runs.
|
|
41
|
+
export function computeAffected(
|
|
42
|
+
graph: GraphData,
|
|
43
|
+
target: string,
|
|
44
|
+
options: AffectedOptions = {},
|
|
45
|
+
): AffectedResult {
|
|
46
|
+
const depth = normalizeDepth(options.depth);
|
|
47
|
+
// Exact-then-suffix, refusing an ambiguous suffix (see ./target.ts).
|
|
48
|
+
const resolvedTarget = resolveGraphTarget(graph, target);
|
|
49
|
+
|
|
50
|
+
// Reverse-dependent adjacency: to → [from, ...] over non-unresolved edges,
|
|
51
|
+
// matching today's `getAffected` dependent relation exactly at hop 1.
|
|
52
|
+
const dependentsOf = new Map<string, string[]>();
|
|
53
|
+
const fanIn = new Map<string, number>();
|
|
54
|
+
for (const edge of graph.edges) {
|
|
55
|
+
if (edge.kind === "unresolved") {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const bucket = dependentsOf.get(edge.to);
|
|
59
|
+
if (bucket) {
|
|
60
|
+
bucket.push(edge.from);
|
|
61
|
+
} else {
|
|
62
|
+
dependentsOf.set(edge.to, [edge.from]);
|
|
63
|
+
}
|
|
64
|
+
fanIn.set(edge.to, (fanIn.get(edge.to) ?? 0) + 1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const seen = new Set<string>();
|
|
68
|
+
const hopOf = new Map<string, number>();
|
|
69
|
+
let frontier = new Set<string>([resolvedTarget]);
|
|
70
|
+
|
|
71
|
+
for (let hop = 1; hop <= depth; hop += 1) {
|
|
72
|
+
const next = new Set<string>();
|
|
73
|
+
for (const current of frontier) {
|
|
74
|
+
for (const dependent of dependentsOf.get(current) ?? []) {
|
|
75
|
+
if (dependent === resolvedTarget || seen.has(dependent)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
next.add(dependent);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (next.size === 0) {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
for (const dependent of next) {
|
|
85
|
+
seen.add(dependent);
|
|
86
|
+
if (!hopOf.has(dependent)) {
|
|
87
|
+
hopOf.set(dependent, hop);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
frontier = next;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Plain lexicographic sort (default `Array#sort`) — MUST match today's
|
|
94
|
+
// `getAffected` ordering exactly so depth-1 stdout stays byte-identical.
|
|
95
|
+
const dependents = [...seen].sort();
|
|
96
|
+
|
|
97
|
+
// One-hop forward set (dependencies) — unchanged from today.
|
|
98
|
+
const dependencies = graph.edges
|
|
99
|
+
.filter((edge) => edge.kind !== "unresolved" && edge.from === resolvedTarget)
|
|
100
|
+
.map((edge) => edge.to)
|
|
101
|
+
.sort();
|
|
102
|
+
|
|
103
|
+
const ranked: RankedDependent[] = options.ranked
|
|
104
|
+
? dependents
|
|
105
|
+
.map((dependentPath) => ({
|
|
106
|
+
path: dependentPath,
|
|
107
|
+
hop: hopOf.get(dependentPath) ?? 0,
|
|
108
|
+
fanIn: fanIn.get(dependentPath) ?? 0,
|
|
109
|
+
}))
|
|
110
|
+
.sort(rankOrder)
|
|
111
|
+
: [];
|
|
112
|
+
|
|
113
|
+
return { target: resolvedTarget, depth, dependencies, dependents, ranked };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Total, deterministic order: hop asc → fanIn desc → path asc (AC2.4).
|
|
117
|
+
function rankOrder(a: RankedDependent, b: RankedDependent): number {
|
|
118
|
+
if (a.hop !== b.hop) {
|
|
119
|
+
return a.hop - b.hop;
|
|
120
|
+
}
|
|
121
|
+
if (a.fanIn !== b.fanIn) {
|
|
122
|
+
return b.fanIn - a.fanIn;
|
|
123
|
+
}
|
|
124
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function normalizeDepth(depth: number | undefined): number {
|
|
128
|
+
if (depth === undefined || !Number.isFinite(depth)) {
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
const floored = Math.floor(depth);
|
|
132
|
+
return floored < 1 ? 1 : floored;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// De-duplicated `dedupeDependents` uniqueness guaranteed by the visited set.
|