@delegance/claude-autopilot 5.0.1 → 5.0.3
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/dist/src/adapters/review-engine/codex.js +13 -1
- package/dist/src/cli/index.js +39 -1
- package/dist/src/cli/preflight.js +17 -4
- package/dist/src/cli/scan.js +12 -0
- package/package.json +4 -3
- package/dist/presets/go/rules/go-sql-injection.d.ts.map +0 -1
- package/dist/presets/go/rules/go-sql-injection.js.map +0 -1
- package/dist/presets/nextjs-supabase/rules/supabase-rls-bypass.d.ts.map +0 -1
- package/dist/presets/nextjs-supabase/rules/supabase-rls-bypass.js.map +0 -1
- package/dist/presets/python-fastapi/rules/fastapi-missing-auth.d.ts.map +0 -1
- package/dist/presets/python-fastapi/rules/fastapi-missing-auth.js.map +0 -1
- package/dist/presets/rails-postgres/rules/rails-sql-injection.d.ts.map +0 -1
- package/dist/presets/rails-postgres/rules/rails-sql-injection.js.map +0 -1
- package/dist/presets/t3/rules/t3-server-only.d.ts.map +0 -1
- package/dist/presets/t3/rules/t3-server-only.js.map +0 -1
- package/dist/src/adapters/base.d.ts.map +0 -1
- package/dist/src/adapters/base.js.map +0 -1
- package/dist/src/adapters/council/claude.d.ts.map +0 -1
- package/dist/src/adapters/council/claude.js.map +0 -1
- package/dist/src/adapters/council/openai.d.ts.map +0 -1
- package/dist/src/adapters/council/openai.js.map +0 -1
- package/dist/src/adapters/council/types.d.ts.map +0 -1
- package/dist/src/adapters/council/types.js.map +0 -1
- package/dist/src/adapters/loader.d.ts.map +0 -1
- package/dist/src/adapters/loader.js.map +0 -1
- package/dist/src/adapters/migration-runner/supabase.d.ts.map +0 -1
- package/dist/src/adapters/migration-runner/supabase.js.map +0 -1
- package/dist/src/adapters/migration-runner/types.d.ts.map +0 -1
- package/dist/src/adapters/migration-runner/types.js.map +0 -1
- package/dist/src/adapters/review-bot-parser/cursor.d.ts.map +0 -1
- package/dist/src/adapters/review-bot-parser/cursor.js.map +0 -1
- package/dist/src/adapters/review-bot-parser/declarative-base.d.ts.map +0 -1
- package/dist/src/adapters/review-bot-parser/declarative-base.js.map +0 -1
- package/dist/src/adapters/review-bot-parser/types.d.ts.map +0 -1
- package/dist/src/adapters/review-bot-parser/types.js.map +0 -1
- package/dist/src/adapters/review-engine/auto.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/auto.js.map +0 -1
- package/dist/src/adapters/review-engine/claude.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/claude.js.map +0 -1
- package/dist/src/adapters/review-engine/codex.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/codex.js.map +0 -1
- package/dist/src/adapters/review-engine/gemini.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/gemini.js.map +0 -1
- package/dist/src/adapters/review-engine/openai-compatible.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/openai-compatible.js.map +0 -1
- package/dist/src/adapters/review-engine/parse-output.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/parse-output.js.map +0 -1
- package/dist/src/adapters/review-engine/prompt-builder.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/prompt-builder.js.map +0 -1
- package/dist/src/adapters/review-engine/types.d.ts.map +0 -1
- package/dist/src/adapters/review-engine/types.js.map +0 -1
- package/dist/src/adapters/vcs-host/commit-status.d.ts.map +0 -1
- package/dist/src/adapters/vcs-host/commit-status.js.map +0 -1
- package/dist/src/adapters/vcs-host/github.d.ts.map +0 -1
- package/dist/src/adapters/vcs-host/github.js.map +0 -1
- package/dist/src/adapters/vcs-host/types.d.ts.map +0 -1
- package/dist/src/adapters/vcs-host/types.js.map +0 -1
- package/dist/src/cli/_pkg-root.d.ts.map +0 -1
- package/dist/src/cli/_pkg-root.js.map +0 -1
- package/dist/src/cli/autoregress-bridge.d.ts.map +0 -1
- package/dist/src/cli/autoregress-bridge.js.map +0 -1
- package/dist/src/cli/baseline.d.ts.map +0 -1
- package/dist/src/cli/baseline.js.map +0 -1
- package/dist/src/cli/ci.d.ts.map +0 -1
- package/dist/src/cli/ci.js.map +0 -1
- package/dist/src/cli/costs.d.ts.map +0 -1
- package/dist/src/cli/costs.js.map +0 -1
- package/dist/src/cli/council.d.ts.map +0 -1
- package/dist/src/cli/council.js.map +0 -1
- package/dist/src/cli/detector.d.ts.map +0 -1
- package/dist/src/cli/detector.js.map +0 -1
- package/dist/src/cli/explain.d.ts.map +0 -1
- package/dist/src/cli/explain.js.map +0 -1
- package/dist/src/cli/fix.d.ts.map +0 -1
- package/dist/src/cli/fix.js.map +0 -1
- package/dist/src/cli/hook.d.ts.map +0 -1
- package/dist/src/cli/hook.js.map +0 -1
- package/dist/src/cli/ignore-helper.d.ts.map +0 -1
- package/dist/src/cli/ignore-helper.js.map +0 -1
- package/dist/src/cli/index.d.ts.map +0 -1
- package/dist/src/cli/index.js.map +0 -1
- package/dist/src/cli/lsp.d.ts.map +0 -1
- package/dist/src/cli/lsp.js.map +0 -1
- package/dist/src/cli/mcp.d.ts.map +0 -1
- package/dist/src/cli/mcp.js.map +0 -1
- package/dist/src/cli/migrate-v4.d.ts.map +0 -1
- package/dist/src/cli/migrate-v4.js.map +0 -1
- package/dist/src/cli/pr-comment.d.ts.map +0 -1
- package/dist/src/cli/pr-comment.js.map +0 -1
- package/dist/src/cli/pr-desc.d.ts.map +0 -1
- package/dist/src/cli/pr-desc.js.map +0 -1
- package/dist/src/cli/pr-review-comments.d.ts.map +0 -1
- package/dist/src/cli/pr-review-comments.js.map +0 -1
- package/dist/src/cli/pr.d.ts.map +0 -1
- package/dist/src/cli/pr.js.map +0 -1
- package/dist/src/cli/preflight.d.ts.map +0 -1
- package/dist/src/cli/preflight.js.map +0 -1
- package/dist/src/cli/report.d.ts.map +0 -1
- package/dist/src/cli/report.js.map +0 -1
- package/dist/src/cli/run.d.ts.map +0 -1
- package/dist/src/cli/run.js.map +0 -1
- package/dist/src/cli/scan.d.ts.map +0 -1
- package/dist/src/cli/scan.js.map +0 -1
- package/dist/src/cli/setup.d.ts.map +0 -1
- package/dist/src/cli/setup.js.map +0 -1
- package/dist/src/cli/test-gen.d.ts.map +0 -1
- package/dist/src/cli/test-gen.js.map +0 -1
- package/dist/src/cli/triage.d.ts.map +0 -1
- package/dist/src/cli/triage.js.map +0 -1
- package/dist/src/cli/watch.d.ts.map +0 -1
- package/dist/src/cli/watch.js.map +0 -1
- package/dist/src/cli/worker.d.ts.map +0 -1
- package/dist/src/cli/worker.js.map +0 -1
- package/dist/src/core/cache/cached-engine.d.ts.map +0 -1
- package/dist/src/core/cache/cached-engine.js.map +0 -1
- package/dist/src/core/cache/review-cache.d.ts.map +0 -1
- package/dist/src/core/cache/review-cache.js.map +0 -1
- package/dist/src/core/chunking/index.d.ts.map +0 -1
- package/dist/src/core/chunking/index.js.map +0 -1
- package/dist/src/core/chunking/risk-ranker.d.ts.map +0 -1
- package/dist/src/core/chunking/risk-ranker.js.map +0 -1
- package/dist/src/core/config/loader.d.ts.map +0 -1
- package/dist/src/core/config/loader.js.map +0 -1
- package/dist/src/core/config/preset-resolver.d.ts.map +0 -1
- package/dist/src/core/config/preset-resolver.js.map +0 -1
- package/dist/src/core/config/schema.d.ts.map +0 -1
- package/dist/src/core/config/schema.js.map +0 -1
- package/dist/src/core/config/types.d.ts.map +0 -1
- package/dist/src/core/config/types.js.map +0 -1
- package/dist/src/core/council/config.d.ts.map +0 -1
- package/dist/src/core/council/config.js.map +0 -1
- package/dist/src/core/council/context.d.ts.map +0 -1
- package/dist/src/core/council/context.js.map +0 -1
- package/dist/src/core/council/runner.d.ts.map +0 -1
- package/dist/src/core/council/runner.js.map +0 -1
- package/dist/src/core/council/types.d.ts.map +0 -1
- package/dist/src/core/council/types.js.map +0 -1
- package/dist/src/core/detect/git-context.d.ts.map +0 -1
- package/dist/src/core/detect/git-context.js.map +0 -1
- package/dist/src/core/detect/llm-key.d.ts.map +0 -1
- package/dist/src/core/detect/llm-key.js.map +0 -1
- package/dist/src/core/detect/protected-paths.d.ts.map +0 -1
- package/dist/src/core/detect/protected-paths.js.map +0 -1
- package/dist/src/core/detect/provider-usage.d.ts.map +0 -1
- package/dist/src/core/detect/provider-usage.js.map +0 -1
- package/dist/src/core/detect/stack.d.ts.map +0 -1
- package/dist/src/core/detect/stack.js.map +0 -1
- package/dist/src/core/detect/workspaces.d.ts.map +0 -1
- package/dist/src/core/detect/workspaces.js.map +0 -1
- package/dist/src/core/errors.d.ts.map +0 -1
- package/dist/src/core/errors.js.map +0 -1
- package/dist/src/core/findings/dedup.d.ts.map +0 -1
- package/dist/src/core/findings/dedup.js.map +0 -1
- package/dist/src/core/findings/types.d.ts.map +0 -1
- package/dist/src/core/findings/types.js.map +0 -1
- package/dist/src/core/fix/generator.d.ts.map +0 -1
- package/dist/src/core/fix/generator.js.map +0 -1
- package/dist/src/core/git/diff-hunks.d.ts.map +0 -1
- package/dist/src/core/git/diff-hunks.js.map +0 -1
- package/dist/src/core/git/touched-files.d.ts.map +0 -1
- package/dist/src/core/git/touched-files.js.map +0 -1
- package/dist/src/core/ignore/index.d.ts.map +0 -1
- package/dist/src/core/ignore/index.js.map +0 -1
- package/dist/src/core/index.d.ts.map +0 -1
- package/dist/src/core/index.js.map +0 -1
- package/dist/src/core/logging/ndjson-writer.d.ts.map +0 -1
- package/dist/src/core/logging/ndjson-writer.js.map +0 -1
- package/dist/src/core/logging/redaction.d.ts.map +0 -1
- package/dist/src/core/logging/redaction.js.map +0 -1
- package/dist/src/core/mcp/concurrency.d.ts.map +0 -1
- package/dist/src/core/mcp/concurrency.js.map +0 -1
- package/dist/src/core/mcp/handlers/fix-finding.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/fix-finding.js.map +0 -1
- package/dist/src/core/mcp/handlers/get-capabilities.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/get-capabilities.js.map +0 -1
- package/dist/src/core/mcp/handlers/get-findings.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/get-findings.js.map +0 -1
- package/dist/src/core/mcp/handlers/review-diff.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/review-diff.js.map +0 -1
- package/dist/src/core/mcp/handlers/scan-files.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/scan-files.js.map +0 -1
- package/dist/src/core/mcp/handlers/validate-fix.d.ts.map +0 -1
- package/dist/src/core/mcp/handlers/validate-fix.js.map +0 -1
- package/dist/src/core/mcp/run-store.d.ts.map +0 -1
- package/dist/src/core/mcp/run-store.js.map +0 -1
- package/dist/src/core/mcp/workspace.d.ts.map +0 -1
- package/dist/src/core/mcp/workspace.js.map +0 -1
- package/dist/src/core/persist/baseline.d.ts.map +0 -1
- package/dist/src/core/persist/baseline.js.map +0 -1
- package/dist/src/core/persist/cost-log.d.ts.map +0 -1
- package/dist/src/core/persist/cost-log.js.map +0 -1
- package/dist/src/core/persist/findings-cache.d.ts.map +0 -1
- package/dist/src/core/persist/findings-cache.js.map +0 -1
- package/dist/src/core/persist/triage.d.ts.map +0 -1
- package/dist/src/core/persist/triage.js.map +0 -1
- package/dist/src/core/phases/static-rules.d.ts.map +0 -1
- package/dist/src/core/phases/static-rules.js.map +0 -1
- package/dist/src/core/phases/tests.d.ts.map +0 -1
- package/dist/src/core/phases/tests.js.map +0 -1
- package/dist/src/core/pipeline/review-phase.d.ts.map +0 -1
- package/dist/src/core/pipeline/review-phase.js.map +0 -1
- package/dist/src/core/pipeline/run.d.ts.map +0 -1
- package/dist/src/core/pipeline/run.js.map +0 -1
- package/dist/src/core/runtime/idempotency.d.ts.map +0 -1
- package/dist/src/core/runtime/idempotency.js.map +0 -1
- package/dist/src/core/runtime/lock.d.ts.map +0 -1
- package/dist/src/core/runtime/lock.js.map +0 -1
- package/dist/src/core/runtime/state.d.ts.map +0 -1
- package/dist/src/core/runtime/state.js.map +0 -1
- package/dist/src/core/schema-alignment/detector.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/detector.js.map +0 -1
- package/dist/src/core/schema-alignment/extractor/index.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/extractor/index.js.map +0 -1
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js.map +0 -1
- package/dist/src/core/schema-alignment/extractor/sql.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/extractor/sql.js.map +0 -1
- package/dist/src/core/schema-alignment/llm-check.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/llm-check.js.map +0 -1
- package/dist/src/core/schema-alignment/scanner.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/scanner.js.map +0 -1
- package/dist/src/core/schema-alignment/types.d.ts.map +0 -1
- package/dist/src/core/schema-alignment/types.js.map +0 -1
- package/dist/src/core/shell.d.ts.map +0 -1
- package/dist/src/core/shell.js.map +0 -1
- package/dist/src/core/static-rules/registry.d.ts.map +0 -1
- package/dist/src/core/static-rules/registry.js.map +0 -1
- package/dist/src/core/static-rules/rules/brand-tokens.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/brand-tokens.js.map +0 -1
- package/dist/src/core/static-rules/rules/console-log.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/console-log.js.map +0 -1
- package/dist/src/core/static-rules/rules/hardcoded-secrets.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/hardcoded-secrets.js.map +0 -1
- package/dist/src/core/static-rules/rules/insecure-redirect.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/insecure-redirect.js.map +0 -1
- package/dist/src/core/static-rules/rules/large-file.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/large-file.js.map +0 -1
- package/dist/src/core/static-rules/rules/missing-auth.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/missing-auth.js.map +0 -1
- package/dist/src/core/static-rules/rules/missing-tests.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/missing-tests.js.map +0 -1
- package/dist/src/core/static-rules/rules/npm-audit.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/npm-audit.js.map +0 -1
- package/dist/src/core/static-rules/rules/package-lock-sync.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/package-lock-sync.js.map +0 -1
- package/dist/src/core/static-rules/rules/schema-alignment.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/schema-alignment.js.map +0 -1
- package/dist/src/core/static-rules/rules/sql-injection.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/sql-injection.js.map +0 -1
- package/dist/src/core/static-rules/rules/ssrf.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/ssrf.js.map +0 -1
- package/dist/src/core/static-rules/rules/todo-fixme.d.ts.map +0 -1
- package/dist/src/core/static-rules/rules/todo-fixme.js.map +0 -1
- package/dist/src/core/static-rules/tailwind-extractor.d.ts.map +0 -1
- package/dist/src/core/static-rules/tailwind-extractor.js.map +0 -1
- package/dist/src/core/test-gen/coverage-analyzer.d.ts.map +0 -1
- package/dist/src/core/test-gen/coverage-analyzer.js.map +0 -1
- package/dist/src/core/test-gen/framework-detector.d.ts.map +0 -1
- package/dist/src/core/test-gen/framework-detector.js.map +0 -1
- package/dist/src/core/test-gen/test-writer.d.ts.map +0 -1
- package/dist/src/core/test-gen/test-writer.js.map +0 -1
- package/dist/src/core/ui/design-context-loader.d.ts.map +0 -1
- package/dist/src/core/ui/design-context-loader.js.map +0 -1
- package/dist/src/core/worker/client.d.ts.map +0 -1
- package/dist/src/core/worker/client.js.map +0 -1
- package/dist/src/core/worker/lockfile.d.ts.map +0 -1
- package/dist/src/core/worker/lockfile.js.map +0 -1
- package/dist/src/core/worker/server.d.ts.map +0 -1
- package/dist/src/core/worker/server.js.map +0 -1
- package/dist/src/formatters/github-annotations.d.ts.map +0 -1
- package/dist/src/formatters/github-annotations.js.map +0 -1
- package/dist/src/formatters/index.d.ts.map +0 -1
- package/dist/src/formatters/index.js.map +0 -1
- package/dist/src/formatters/junit.d.ts.map +0 -1
- package/dist/src/formatters/junit.js.map +0 -1
- package/dist/src/formatters/sarif.d.ts.map +0 -1
- package/dist/src/formatters/sarif.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/src/adapters/base.ts +0 -19
- package/src/adapters/council/claude.ts +0 -41
- package/src/adapters/council/openai.ts +0 -40
- package/src/adapters/council/types.ts +0 -7
- package/src/adapters/loader.ts +0 -108
- package/src/adapters/migration-runner/supabase.ts +0 -56
- package/src/adapters/migration-runner/types.ts +0 -36
- package/src/adapters/review-bot-parser/cursor.ts +0 -13
- package/src/adapters/review-bot-parser/declarative-base.ts +0 -64
- package/src/adapters/review-bot-parser/types.ts +0 -9
- package/src/adapters/review-engine/auto.ts +0 -94
- package/src/adapters/review-engine/claude.ts +0 -100
- package/src/adapters/review-engine/codex.ts +0 -82
- package/src/adapters/review-engine/gemini.ts +0 -105
- package/src/adapters/review-engine/openai-compatible.ts +0 -100
- package/src/adapters/review-engine/parse-output.ts +0 -74
- package/src/adapters/review-engine/prompt-builder.ts +0 -19
- package/src/adapters/review-engine/types.ts +0 -19
- package/src/adapters/vcs-host/commit-status.ts +0 -39
- package/src/adapters/vcs-host/github.ts +0 -77
- package/src/adapters/vcs-host/types.ts +0 -44
- package/src/cli/_pkg-root.ts +0 -85
- package/src/cli/autoregress-bridge.ts +0 -30
- package/src/cli/baseline.ts +0 -125
- package/src/cli/ci.ts +0 -45
- package/src/cli/costs.ts +0 -80
- package/src/cli/council.ts +0 -96
- package/src/cli/detector.ts +0 -92
- package/src/cli/explain.ts +0 -197
- package/src/cli/fix.ts +0 -249
- package/src/cli/hook.ts +0 -124
- package/src/cli/ignore-helper.ts +0 -116
- package/src/cli/index.ts +0 -612
- package/src/cli/lsp.ts +0 -200
- package/src/cli/mcp.ts +0 -206
- package/src/cli/migrate-v4.ts +0 -388
- package/src/cli/pr-comment.ts +0 -139
- package/src/cli/pr-desc.ts +0 -168
- package/src/cli/pr-review-comments.ts +0 -92
- package/src/cli/pr.ts +0 -76
- package/src/cli/preflight.ts +0 -235
- package/src/cli/report.ts +0 -186
- package/src/cli/run.ts +0 -425
- package/src/cli/scan.ts +0 -233
- package/src/cli/setup.ts +0 -191
- package/src/cli/test-gen.ts +0 -125
- package/src/cli/triage.ts +0 -137
- package/src/cli/watch.ts +0 -190
- package/src/cli/worker.ts +0 -109
- package/src/core/.gitkeep +0 -0
- package/src/core/cache/cached-engine.ts +0 -32
- package/src/core/cache/review-cache.ts +0 -70
- package/src/core/chunking/index.ts +0 -113
- package/src/core/chunking/risk-ranker.ts +0 -56
- package/src/core/config/loader.ts +0 -53
- package/src/core/config/preset-resolver.ts +0 -46
- package/src/core/config/schema.ts +0 -181
- package/src/core/config/types.ts +0 -98
- package/src/core/council/config.ts +0 -71
- package/src/core/council/context.ts +0 -17
- package/src/core/council/runner.ts +0 -83
- package/src/core/council/types.ts +0 -45
- package/src/core/detect/git-context.ts +0 -27
- package/src/core/detect/llm-key.ts +0 -89
- package/src/core/detect/protected-paths.ts +0 -63
- package/src/core/detect/provider-usage.ts +0 -74
- package/src/core/detect/stack.ts +0 -153
- package/src/core/detect/workspaces.ts +0 -103
- package/src/core/errors.ts +0 -37
- package/src/core/findings/dedup.ts +0 -14
- package/src/core/findings/types.ts +0 -39
- package/src/core/fix/generator.ts +0 -149
- package/src/core/git/diff-hunks.ts +0 -86
- package/src/core/git/touched-files.ts +0 -73
- package/src/core/ignore/index.ts +0 -54
- package/src/core/index.ts +0 -1
- package/src/core/logging/ndjson-writer.ts +0 -37
- package/src/core/logging/redaction.ts +0 -19
- package/src/core/mcp/concurrency.ts +0 -16
- package/src/core/mcp/handlers/fix-finding.ts +0 -126
- package/src/core/mcp/handlers/get-capabilities.ts +0 -62
- package/src/core/mcp/handlers/get-findings.ts +0 -36
- package/src/core/mcp/handlers/review-diff.ts +0 -65
- package/src/core/mcp/handlers/scan-files.ts +0 -65
- package/src/core/mcp/handlers/validate-fix.ts +0 -41
- package/src/core/mcp/run-store.ts +0 -85
- package/src/core/mcp/workspace.ts +0 -35
- package/src/core/persist/baseline.ts +0 -112
- package/src/core/persist/cost-log.ts +0 -30
- package/src/core/persist/findings-cache.ts +0 -43
- package/src/core/persist/triage.ts +0 -112
- package/src/core/phases/static-rules.ts +0 -93
- package/src/core/phases/tests.ts +0 -51
- package/src/core/pipeline/review-phase.ts +0 -182
- package/src/core/pipeline/run.ts +0 -116
- package/src/core/runtime/idempotency.ts +0 -6
- package/src/core/runtime/lock.ts +0 -29
- package/src/core/runtime/state.ts +0 -97
- package/src/core/schema-alignment/detector.ts +0 -59
- package/src/core/schema-alignment/extractor/index.ts +0 -24
- package/src/core/schema-alignment/extractor/prisma.ts +0 -21
- package/src/core/schema-alignment/extractor/sql.ts +0 -99
- package/src/core/schema-alignment/llm-check.ts +0 -91
- package/src/core/schema-alignment/scanner.ts +0 -107
- package/src/core/schema-alignment/types.ts +0 -43
- package/src/core/shell.ts +0 -48
- package/src/core/static-rules/registry.ts +0 -59
- package/src/core/static-rules/rules/brand-tokens.ts +0 -145
- package/src/core/static-rules/rules/console-log.ts +0 -42
- package/src/core/static-rules/rules/hardcoded-secrets.ts +0 -83
- package/src/core/static-rules/rules/insecure-redirect.ts +0 -67
- package/src/core/static-rules/rules/large-file.ts +0 -37
- package/src/core/static-rules/rules/missing-auth.ts +0 -70
- package/src/core/static-rules/rules/missing-tests.ts +0 -57
- package/src/core/static-rules/rules/npm-audit.ts +0 -38
- package/src/core/static-rules/rules/package-lock-sync.ts +0 -54
- package/src/core/static-rules/rules/schema-alignment.ts +0 -132
- package/src/core/static-rules/rules/sql-injection.ts +0 -71
- package/src/core/static-rules/rules/ssrf.ts +0 -63
- package/src/core/static-rules/rules/todo-fixme.ts +0 -40
- package/src/core/static-rules/tailwind-extractor.ts +0 -38
- package/src/core/test-gen/coverage-analyzer.ts +0 -93
- package/src/core/test-gen/framework-detector.ts +0 -21
- package/src/core/test-gen/test-writer.ts +0 -33
- package/src/core/ui/design-context-loader.ts +0 -87
- package/src/core/worker/client.ts +0 -46
- package/src/core/worker/lockfile.ts +0 -38
- package/src/core/worker/server.ts +0 -81
- package/src/formatters/github-annotations.ts +0 -36
- package/src/formatters/index.ts +0 -3
- package/src/formatters/junit.ts +0 -52
- package/src/formatters/sarif.ts +0 -103
- package/src/index.ts +0 -3
package/src/core/pipeline/run.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import type { GuardrailConfig } from '../config/types.ts';
|
|
2
|
-
import type { StaticRule } from '../phases/static-rules.ts';
|
|
3
|
-
import type { ReviewEngine } from '../../adapters/review-engine/types.ts';
|
|
4
|
-
import type { Finding } from '../findings/types.ts';
|
|
5
|
-
import type { StaticRulesPhaseResult } from '../phases/static-rules.ts';
|
|
6
|
-
import type { TestsPhaseResult } from '../phases/tests.ts';
|
|
7
|
-
import type { ReviewPhaseResult } from './review-phase.ts';
|
|
8
|
-
import { runStaticRulesPhase } from '../phases/static-rules.ts';
|
|
9
|
-
import { runTestsPhase } from '../phases/tests.ts';
|
|
10
|
-
import { runReviewPhase } from './review-phase.ts';
|
|
11
|
-
|
|
12
|
-
export type PhaseResult = StaticRulesPhaseResult | TestsPhaseResult | ReviewPhaseResult;
|
|
13
|
-
|
|
14
|
-
export interface RunInput {
|
|
15
|
-
touchedFiles: string[];
|
|
16
|
-
config: GuardrailConfig;
|
|
17
|
-
reviewEngine?: ReviewEngine;
|
|
18
|
-
staticRules?: StaticRule[];
|
|
19
|
-
cwd?: string;
|
|
20
|
-
gitSummary?: string;
|
|
21
|
-
base?: string;
|
|
22
|
-
skipReview?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface RunResult {
|
|
26
|
-
status: 'pass' | 'warn' | 'fail';
|
|
27
|
-
phases: PhaseResult[];
|
|
28
|
-
allFindings: Finding[];
|
|
29
|
-
totalCostUSD?: number;
|
|
30
|
-
durationMs: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function runGuardrail(input: RunInput): Promise<RunResult> {
|
|
34
|
-
const start = Date.now();
|
|
35
|
-
const phases: PhaseResult[] = [];
|
|
36
|
-
let totalCostUSD: number | undefined;
|
|
37
|
-
|
|
38
|
-
const pipelineCfg = input.config.pipeline ?? {};
|
|
39
|
-
// Default true: when the user wires up a review engine they expect it to actually run.
|
|
40
|
-
// Skipping LLM review on static-fail is exactly the "silent skip" behavior the v4.0
|
|
41
|
-
// reviewer flagged — the bugs the LLM is best at often ride alongside one a static
|
|
42
|
-
// rule already caught.
|
|
43
|
-
const runReviewOnStaticFail = pipelineCfg.runReviewOnStaticFail !== false;
|
|
44
|
-
const runReviewOnTestFail = pipelineCfg.runReviewOnTestFail === true;
|
|
45
|
-
|
|
46
|
-
// Static-rules phase — tests always run afterward, regardless of status. The
|
|
47
|
-
// runReviewOnStaticFail flag only gates the LLM review phase (matching its name);
|
|
48
|
-
// skipping tests on a static-fail would be surprising and asymmetric with
|
|
49
|
-
// runReviewOnTestFail which only gates the review phase too.
|
|
50
|
-
let staticFailed = false;
|
|
51
|
-
if (input.staticRules && input.staticRules.length > 0) {
|
|
52
|
-
const result = await runStaticRulesPhase({
|
|
53
|
-
touchedFiles: input.touchedFiles,
|
|
54
|
-
rules: input.staticRules,
|
|
55
|
-
config: input.config,
|
|
56
|
-
engine: input.reviewEngine,
|
|
57
|
-
});
|
|
58
|
-
phases.push(result);
|
|
59
|
-
staticFailed = result.status === 'fail';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// skipReview short-circuit: skip tests and review phases entirely
|
|
63
|
-
if (input.skipReview) {
|
|
64
|
-
const allFindings = phases.flatMap(p => p.findings ?? []);
|
|
65
|
-
const hasCritical = allFindings.some(f => f.severity === 'critical');
|
|
66
|
-
return {
|
|
67
|
-
status: hasCritical ? 'fail' : 'pass',
|
|
68
|
-
phases,
|
|
69
|
-
allFindings,
|
|
70
|
-
totalCostUSD: undefined,
|
|
71
|
-
durationMs: Date.now() - start,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Tests phase — always runs (unless skipReview above). The runReviewOnTestFail
|
|
76
|
-
// flag only gates the subsequent review phase.
|
|
77
|
-
const testsResult = await runTestsPhase({
|
|
78
|
-
touchedFiles: input.touchedFiles,
|
|
79
|
-
testCommand: input.config.testCommand,
|
|
80
|
-
cwd: input.cwd,
|
|
81
|
-
});
|
|
82
|
-
phases.push(testsResult);
|
|
83
|
-
const testsFailed = testsResult.status === 'fail';
|
|
84
|
-
|
|
85
|
-
// Review phase (optional — only when engine is provided, not gated off by flags)
|
|
86
|
-
const skipReviewDueToStatic = staticFailed && !runReviewOnStaticFail;
|
|
87
|
-
const skipReviewDueToTests = testsFailed && !runReviewOnTestFail;
|
|
88
|
-
if (input.reviewEngine && !skipReviewDueToStatic && !skipReviewDueToTests) {
|
|
89
|
-
const costCfg = input.config.cost as { maxPerRun?: number; budgetUSD?: number } | undefined;
|
|
90
|
-
const budgetUSD = costCfg?.maxPerRun ?? costCfg?.budgetUSD;
|
|
91
|
-
const reviewResult = await runReviewPhase({
|
|
92
|
-
touchedFiles: input.touchedFiles,
|
|
93
|
-
engine: input.reviewEngine,
|
|
94
|
-
config: input.config,
|
|
95
|
-
cwd: input.cwd,
|
|
96
|
-
gitSummary: input.gitSummary,
|
|
97
|
-
budgetRemainingUSD: budgetUSD,
|
|
98
|
-
base: input.base,
|
|
99
|
-
});
|
|
100
|
-
phases.push(reviewResult);
|
|
101
|
-
if (reviewResult.costUSD !== undefined) {
|
|
102
|
-
totalCostUSD = (totalCostUSD ?? 0) + reviewResult.costUSD;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return finalize(phases, start, totalCostUSD);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function finalize(phases: PhaseResult[], start: number, totalCostUSD: number | undefined): RunResult {
|
|
110
|
-
const allFindings: Finding[] = phases.flatMap(p => p.findings);
|
|
111
|
-
// Trust each phase's own status — it accounts for autofixes and dedup
|
|
112
|
-
const anyFail = phases.some(p => p.status === 'fail');
|
|
113
|
-
const anyWarn = phases.some(p => p.status === 'warn');
|
|
114
|
-
const status: RunResult['status'] = anyFail ? 'fail' : anyWarn ? 'warn' : 'pass';
|
|
115
|
-
return { status, phases, allFindings, totalCostUSD, durationMs: Date.now() - start };
|
|
116
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
export function idempotencyKey(runId: string, step: string, inputs: Record<string, unknown>): string {
|
|
4
|
-
const serialized = JSON.stringify({ runId, step, inputs });
|
|
5
|
-
return createHash('sha256').update(serialized).digest('hex').slice(0, 16);
|
|
6
|
-
}
|
package/src/core/runtime/lock.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { GuardrailError } from '../errors.ts';
|
|
4
|
-
|
|
5
|
-
export interface LockHandle {
|
|
6
|
-
release(): Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function acquireLock(runId: string, lockDir = '.claude'): LockHandle {
|
|
10
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
11
|
-
const lockPath = path.join(lockDir, '.lock');
|
|
12
|
-
try {
|
|
13
|
-
fs.writeFileSync(
|
|
14
|
-
lockPath,
|
|
15
|
-
JSON.stringify({ runId, pid: process.pid, acquiredAt: new Date().toISOString() }),
|
|
16
|
-
{ flag: 'wx' }
|
|
17
|
-
);
|
|
18
|
-
} catch (err) {
|
|
19
|
-
throw new GuardrailError('Another autopilot run holds the lock', {
|
|
20
|
-
code: 'concurrency_lock',
|
|
21
|
-
details: { lockPath, cause: err instanceof Error ? err.message : String(err) },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
release: async () => {
|
|
26
|
-
try { await fs.promises.unlink(lockPath); } catch { /* best effort */ }
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { GuardrailError } from '../errors.ts';
|
|
4
|
-
|
|
5
|
-
export type PipelineStep =
|
|
6
|
-
| 'plan' | 'worktree' | 'implement' | 'migrate' | 'validate'
|
|
7
|
-
| 'push' | 'create-pr' | 'review' | 'bugbot';
|
|
8
|
-
|
|
9
|
-
export type StepStatus = 'pending' | 'in-progress' | 'completed' | 'failed' | 'skipped';
|
|
10
|
-
export type RunStatus = 'in-progress' | 'completed' | 'failed' | 'superseded';
|
|
11
|
-
|
|
12
|
-
export interface StepState {
|
|
13
|
-
status: StepStatus;
|
|
14
|
-
idempotencyKey?: string;
|
|
15
|
-
artifact?: string;
|
|
16
|
-
errorCode?: string;
|
|
17
|
-
attempts?: number;
|
|
18
|
-
lastCommitSha?: string;
|
|
19
|
-
appliedMigrations?: string[];
|
|
20
|
-
prNumber?: number;
|
|
21
|
-
alreadyExisted?: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface RunState {
|
|
25
|
-
runId: string;
|
|
26
|
-
topic: string;
|
|
27
|
-
startedAt: string;
|
|
28
|
-
lastUpdatedAt: string;
|
|
29
|
-
status: RunStatus;
|
|
30
|
-
currentStep: PipelineStep | null;
|
|
31
|
-
steps: Record<PipelineStep, StepState>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const ALL_STEPS: readonly PipelineStep[] = Object.freeze([
|
|
35
|
-
'plan', 'worktree', 'implement', 'migrate', 'validate', 'push', 'create-pr', 'review', 'bugbot',
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
function stateFile(runId: string, runsDir: string): string {
|
|
39
|
-
return path.join(runsDir, runId, 'state.json');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function writeAtomic(file: string, content: string): Promise<void> {
|
|
43
|
-
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
44
|
-
await fs.writeFile(tmp, content, 'utf8');
|
|
45
|
-
await fs.rename(tmp, file);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface CreateRunStateOptions {
|
|
49
|
-
runId: string;
|
|
50
|
-
topic: string;
|
|
51
|
-
runsDir?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function createRunState(options: CreateRunStateOptions): Promise<RunState> {
|
|
55
|
-
const runsDir = options.runsDir ?? path.join('.claude', 'runs');
|
|
56
|
-
await fs.mkdir(path.join(runsDir, options.runId), { recursive: true });
|
|
57
|
-
const now = new Date().toISOString();
|
|
58
|
-
const stepsInit = {} as Record<PipelineStep, StepState>;
|
|
59
|
-
for (const step of ALL_STEPS) stepsInit[step] = { status: 'pending' };
|
|
60
|
-
const state: RunState = {
|
|
61
|
-
runId: options.runId, topic: options.topic,
|
|
62
|
-
startedAt: now, lastUpdatedAt: now,
|
|
63
|
-
status: 'in-progress', currentStep: null, steps: stepsInit,
|
|
64
|
-
};
|
|
65
|
-
await writeAtomic(stateFile(options.runId, runsDir), JSON.stringify(state, null, 2));
|
|
66
|
-
return state;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function loadRunState(runId: string, runsDir?: string): Promise<RunState> {
|
|
70
|
-
const dir = runsDir ?? path.join('.claude', 'runs');
|
|
71
|
-
const file = stateFile(runId, dir);
|
|
72
|
-
try {
|
|
73
|
-
return JSON.parse(await fs.readFile(file, 'utf8')) as RunState;
|
|
74
|
-
} catch (err) {
|
|
75
|
-
throw new GuardrailError(`Run state not found: ${runId}`, {
|
|
76
|
-
code: 'user_input',
|
|
77
|
-
details: { runId, file, cause: err instanceof Error ? err.message : String(err) },
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface UpdateStepOptions {
|
|
83
|
-
runId: string;
|
|
84
|
-
runsDir?: string;
|
|
85
|
-
step: PipelineStep;
|
|
86
|
-
update: Partial<StepState>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export async function updateStepStatus(options: UpdateStepOptions): Promise<RunState> {
|
|
90
|
-
const runsDir = options.runsDir ?? path.join('.claude', 'runs');
|
|
91
|
-
const state = await loadRunState(options.runId, runsDir);
|
|
92
|
-
state.steps[options.step] = { ...state.steps[options.step], ...options.update };
|
|
93
|
-
state.lastUpdatedAt = new Date().toISOString();
|
|
94
|
-
if (options.update.status === 'in-progress') state.currentStep = options.step;
|
|
95
|
-
await writeAtomic(stateFile(options.runId, runsDir), JSON.stringify(state, null, 2));
|
|
96
|
-
return state;
|
|
97
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// src/core/schema-alignment/detector.ts
|
|
2
|
-
import type { SchemaAlignmentConfig } from './types.ts';
|
|
3
|
-
|
|
4
|
-
const DEFAULT_PATTERNS: RegExp[] = [
|
|
5
|
-
/data[/\\]deltas[/\\].+\.sql$/,
|
|
6
|
-
/supabase[/\\]migrations[/\\].+\.sql$/,
|
|
7
|
-
/prisma[/\\]migrations[/\\].+\.sql$/,
|
|
8
|
-
/prisma[/\\]schema\.prisma$/,
|
|
9
|
-
/db[/\\]migrate[/\\].+\.rb$/,
|
|
10
|
-
/drizzle[/\\].+\.ts$/,
|
|
11
|
-
/[/\\]migrations[/\\].+\.py$/,
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
function globToPattern(glob: string): RegExp {
|
|
15
|
-
let escaped = glob;
|
|
16
|
-
|
|
17
|
-
// Escape regex special chars except * and /
|
|
18
|
-
escaped = escaped.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
19
|
-
|
|
20
|
-
// Replace **/ with placeholder (matches zero or more intermediate directories)
|
|
21
|
-
escaped = escaped.replace(/\*\*\//g, '___DSTAR_SLASH___');
|
|
22
|
-
|
|
23
|
-
// Replace remaining ** with placeholder
|
|
24
|
-
escaped = escaped.replace(/\*\*/g, '___DSTAR___');
|
|
25
|
-
|
|
26
|
-
// Replace / with placeholder BEFORE handling * so we don't mess up character classes
|
|
27
|
-
escaped = escaped.replace(/\//g, '___SLASH___');
|
|
28
|
-
|
|
29
|
-
// Replace single * with placeholder (to preserve it before / restoration)
|
|
30
|
-
escaped = escaped.replace(/\*/g, '___STAR___');
|
|
31
|
-
|
|
32
|
-
// Now restore placeholders in the right order
|
|
33
|
-
// Restore / as [/\\\\] to match both forward and back slashes
|
|
34
|
-
// NOTE: In RegExp constructor, \\ becomes \, so [/\\] needs to be [/\\\\]
|
|
35
|
-
escaped = escaped.replace(/___SLASH___/g, '[/\\\\]');
|
|
36
|
-
|
|
37
|
-
// Restore * as [^/]* (matches anything except /)
|
|
38
|
-
escaped = escaped.replace(/___STAR___/g, '[^/]*');
|
|
39
|
-
|
|
40
|
-
// Restore **/ as optional directory segments with trailing separator
|
|
41
|
-
escaped = escaped.replace(/___DSTAR_SLASH___/g, '(?:.*[/\\\\])?');
|
|
42
|
-
|
|
43
|
-
// Restore remaining ** as .* (matches anything including /)
|
|
44
|
-
escaped = escaped.replace(/___DSTAR___/g, '.*');
|
|
45
|
-
|
|
46
|
-
const re = new RegExp(escaped + '$');
|
|
47
|
-
return re;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function detect(touchedFiles: string[], config?: SchemaAlignmentConfig): string[] {
|
|
51
|
-
if (config?.enabled === false) return [];
|
|
52
|
-
|
|
53
|
-
const patterns = [...DEFAULT_PATTERNS];
|
|
54
|
-
for (const glob of config?.migrationGlobs ?? []) {
|
|
55
|
-
patterns.push(globToPattern(glob));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return touchedFiles.filter(f => patterns.some(re => re.test(f)));
|
|
59
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// src/core/schema-alignment/extractor/index.ts
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import type { SchemaEntity } from '../types.ts';
|
|
5
|
-
import { extractFromSql } from './sql.ts';
|
|
6
|
-
import { extractFromPrisma } from './prisma.ts';
|
|
7
|
-
|
|
8
|
-
export function extract(filePath: string): SchemaEntity[] {
|
|
9
|
-
let content: string;
|
|
10
|
-
try {
|
|
11
|
-
content = fs.readFileSync(filePath, 'utf8');
|
|
12
|
-
} catch {
|
|
13
|
-
return [];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
17
|
-
const base = path.basename(filePath).toLowerCase();
|
|
18
|
-
|
|
19
|
-
if (ext === '.sql') return extractFromSql(content);
|
|
20
|
-
if (base === 'schema.prisma' || ext === '.prisma') return extractFromPrisma(content);
|
|
21
|
-
|
|
22
|
-
process.stderr.write(`[schema-alignment] no extractor for ${ext} files — skipping ${path.basename(filePath)}\n`);
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// src/core/schema-alignment/extractor/prisma.ts
|
|
2
|
-
import type { SchemaEntity } from '../types.ts';
|
|
3
|
-
|
|
4
|
-
export function extractFromPrisma(content: string): SchemaEntity[] {
|
|
5
|
-
const entities: SchemaEntity[] = [];
|
|
6
|
-
// Match model blocks: model Name { ... }
|
|
7
|
-
const modelRe = /^model\s+(\w+)\s*\{([^}]+)\}/gm;
|
|
8
|
-
for (const modelMatch of content.matchAll(modelRe)) {
|
|
9
|
-
const table = modelMatch[1]!;
|
|
10
|
-
entities.push({ table, operation: 'create_table' });
|
|
11
|
-
const body = modelMatch[2]!;
|
|
12
|
-
// Match field lines: fieldName TypeName ...
|
|
13
|
-
const fieldRe = /^\s+(\w+)\s+\S/gm;
|
|
14
|
-
for (const fieldMatch of body.matchAll(fieldRe)) {
|
|
15
|
-
const column = fieldMatch[1]!;
|
|
16
|
-
if (column.startsWith('@') || column === 'id') continue;
|
|
17
|
-
entities.push({ table, column, operation: 'add_column' });
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return entities;
|
|
21
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// src/core/schema-alignment/extractor/sql.ts
|
|
2
|
-
import type { SchemaEntity } from '../types.ts';
|
|
3
|
-
|
|
4
|
-
function unquote(s: string | undefined): string {
|
|
5
|
-
if (!s) return '';
|
|
6
|
-
return s.replace(/^["'`]|["'`]$/g, '');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// Identifier: quoted or unquoted word (no schema prefix captured)
|
|
10
|
-
const ID = /(?:"([^"]+)"|`([^`]+)`|(\w+))/;
|
|
11
|
-
const SCHEMA_OPT = /(?:\w+\.)?/;
|
|
12
|
-
|
|
13
|
-
// Keywords that can follow ADD/DROP but are NOT columns
|
|
14
|
-
const NON_COLUMN_KEYWORDS = new Set([
|
|
15
|
-
'CONSTRAINT', 'INDEX', 'PRIMARY', 'FOREIGN', 'UNIQUE', 'CHECK',
|
|
16
|
-
'KEY', 'EXCLUDE', 'REFERENCES',
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
export function extractFromSql(content: string): SchemaEntity[] {
|
|
20
|
-
// Strip comments before processing
|
|
21
|
-
const normalized = content
|
|
22
|
-
.replace(/--[^\n]*/g, ' ')
|
|
23
|
-
.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
24
|
-
.replace(/\s+/g, ' ');
|
|
25
|
-
|
|
26
|
-
const entities: SchemaEntity[] = [];
|
|
27
|
-
|
|
28
|
-
// CREATE TABLE [IF NOT EXISTS] [schema.]name
|
|
29
|
-
const createTableRe = new RegExp(
|
|
30
|
-
`CREATE\\s+(?:OR\\s+REPLACE\\s+)?TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${SCHEMA_OPT.source}${ID.source}`,
|
|
31
|
-
'gi',
|
|
32
|
-
);
|
|
33
|
-
for (const m of normalized.matchAll(createTableRe)) {
|
|
34
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
35
|
-
if (table && table.toUpperCase() !== 'EXISTS') entities.push({ table, operation: 'create_table' });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ALTER TABLE [schema.]name ADD [COLUMN] [IF NOT EXISTS] col
|
|
39
|
-
const addColRe = new RegExp(
|
|
40
|
-
`ALTER\\s+TABLE\\s+(?:ONLY\\s+)?${SCHEMA_OPT.source}${ID.source}\\s+ADD\\s+(?:COLUMN\\s+)?(?:IF\\s+NOT\\s+EXISTS\\s+)?${ID.source}`,
|
|
41
|
-
'gi',
|
|
42
|
-
);
|
|
43
|
-
for (const m of normalized.matchAll(addColRe)) {
|
|
44
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
45
|
-
const column = unquote(m[4] ?? m[5] ?? m[6]);
|
|
46
|
-
if (!table || !column) continue;
|
|
47
|
-
// Skip non-column ADD operations (CONSTRAINT, INDEX, PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK)
|
|
48
|
-
if (NON_COLUMN_KEYWORDS.has(column.toUpperCase())) continue;
|
|
49
|
-
entities.push({ table, column, operation: 'add_column' });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ALTER TABLE [schema.]name DROP [COLUMN] [IF EXISTS] col
|
|
53
|
-
const dropColRe = new RegExp(
|
|
54
|
-
`ALTER\\s+TABLE\\s+(?:ONLY\\s+)?${SCHEMA_OPT.source}${ID.source}\\s+DROP\\s+(?:COLUMN\\s+)?(?:IF\\s+EXISTS\\s+)?${ID.source}`,
|
|
55
|
-
'gi',
|
|
56
|
-
);
|
|
57
|
-
for (const m of normalized.matchAll(dropColRe)) {
|
|
58
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
59
|
-
const column = unquote(m[4] ?? m[5] ?? m[6]);
|
|
60
|
-
if (!table || !column) continue;
|
|
61
|
-
// Skip non-column DROP operations (CONSTRAINT, INDEX, PRIMARY KEY, FOREIGN KEY)
|
|
62
|
-
if (NON_COLUMN_KEYWORDS.has(column.toUpperCase())) continue;
|
|
63
|
-
entities.push({ table, column, operation: 'drop_column' });
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ALTER TABLE [schema.]name RENAME [COLUMN] old TO new
|
|
67
|
-
const renameColRe = new RegExp(
|
|
68
|
-
`ALTER\\s+TABLE\\s+(?:ONLY\\s+)?${SCHEMA_OPT.source}${ID.source}\\s+RENAME\\s+(?:COLUMN\\s+)?${ID.source}\\s+TO\\s+${ID.source}`,
|
|
69
|
-
'gi',
|
|
70
|
-
);
|
|
71
|
-
for (const m of normalized.matchAll(renameColRe)) {
|
|
72
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
73
|
-
const oldName = unquote(m[4] ?? m[5] ?? m[6]);
|
|
74
|
-
const column = unquote(m[7] ?? m[8] ?? m[9]);
|
|
75
|
-
if (table && column) entities.push({ table, column, operation: 'rename_column', oldName });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// CREATE TYPE [schema.]name
|
|
79
|
-
const createTypeRe = new RegExp(
|
|
80
|
-
`CREATE\\s+TYPE\\s+${SCHEMA_OPT.source}${ID.source}`,
|
|
81
|
-
'gi',
|
|
82
|
-
);
|
|
83
|
-
for (const m of normalized.matchAll(createTypeRe)) {
|
|
84
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
85
|
-
if (table) entities.push({ table, operation: 'create_type' });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ALTER TYPE [schema.]name ADD VALUE ...
|
|
89
|
-
const alterTypeRe = new RegExp(
|
|
90
|
-
`ALTER\\s+TYPE\\s+${SCHEMA_OPT.source}${ID.source}\\s+ADD\\s+VALUE`,
|
|
91
|
-
'gi',
|
|
92
|
-
);
|
|
93
|
-
for (const m of normalized.matchAll(alterTypeRe)) {
|
|
94
|
-
const table = unquote(m[1] ?? m[2] ?? m[3]);
|
|
95
|
-
if (table) entities.push({ table, operation: 'create_type' });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return entities;
|
|
99
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// src/core/schema-alignment/llm-check.ts
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import type { ReviewEngine } from '../../adapters/review-engine/types.ts';
|
|
5
|
-
import type { SchemaEntity, LayerScanResult, AlignmentFinding } from './types.ts';
|
|
6
|
-
|
|
7
|
-
const TOTAL_CHAR_BUDGET = 6000;
|
|
8
|
-
|
|
9
|
-
function truncateTop(text: string, maxChars: number): string {
|
|
10
|
-
if (text.length <= maxChars) return text;
|
|
11
|
-
const dropped = text.length - maxChars;
|
|
12
|
-
return `<!-- [schema-alignment: truncated ${dropped} chars] -->\n` + text.slice(dropped);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function runLlmCheck(
|
|
16
|
-
migrationFiles: string[],
|
|
17
|
-
gapResults: LayerScanResult[],
|
|
18
|
-
engine: ReviewEngine,
|
|
19
|
-
): Promise<AlignmentFinding[]> {
|
|
20
|
-
let budget = TOTAL_CHAR_BUDGET;
|
|
21
|
-
const migrationSnippets: string[] = [];
|
|
22
|
-
|
|
23
|
-
for (const f of migrationFiles) {
|
|
24
|
-
if (budget <= 0) break;
|
|
25
|
-
let content: string;
|
|
26
|
-
try { content = fs.readFileSync(f, 'utf8'); } catch { continue; }
|
|
27
|
-
const snippet = truncateTop(content, Math.floor(budget * 0.6));
|
|
28
|
-
migrationSnippets.push(`### Migration: ${path.basename(f)}\n\`\`\`sql\n${snippet}\n\`\`\``);
|
|
29
|
-
budget -= snippet.length;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const entitySummary = gapResults.map(r => {
|
|
33
|
-
const isDestructive = r.entity.operation === 'drop_column' || r.entity.operation === 'rename_column';
|
|
34
|
-
const gaps = isDestructive
|
|
35
|
-
? [r.typeLayer, r.apiLayer, r.uiLayer]
|
|
36
|
-
.map((e, i) => e !== null ? (['type', 'api', 'ui'][i]) : null)
|
|
37
|
-
.filter(Boolean).join(', ')
|
|
38
|
-
: [r.typeLayer === null ? 'type' : null, r.apiLayer === null ? 'api' : null, r.uiLayer === null ? 'ui' : null]
|
|
39
|
-
.filter(Boolean).join(', ');
|
|
40
|
-
return `- ${r.entity.operation} ${r.entity.table}${r.entity.column ? '.' + r.entity.column : ''}: ${isDestructive ? 'stale ref in' : 'missing in'} [${gaps}]`;
|
|
41
|
-
}).join('\n');
|
|
42
|
-
|
|
43
|
-
const prompt = [
|
|
44
|
-
'You are reviewing schema-layer alignment for a software project.',
|
|
45
|
-
'',
|
|
46
|
-
migrationSnippets.length > 0
|
|
47
|
-
? `The following migration files were changed:\n\n${migrationSnippets.join('\n\n')}`
|
|
48
|
-
: '(no readable migration files)',
|
|
49
|
-
'',
|
|
50
|
-
`The structural scan found these potential alignment gaps:\n${entitySummary || '(none)'}`,
|
|
51
|
-
'',
|
|
52
|
-
'For each gap, determine if it is a real problem. Return findings as a JSON array:',
|
|
53
|
-
'[{ "table": "name", "column": "name_or_null", "operation": "add_column", "layer": "type", "file": "path/to/relevant/file.ts (optional)", "message": "explanation", "severity": "warning", "confidence": "high" }]',
|
|
54
|
-
'Return only valid JSON, no prose.',
|
|
55
|
-
].join('\n');
|
|
56
|
-
|
|
57
|
-
let rawOutput: string;
|
|
58
|
-
try {
|
|
59
|
-
const result = await engine.review({ content: prompt, kind: 'file-batch' });
|
|
60
|
-
rawOutput = result.rawOutput;
|
|
61
|
-
} catch {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const jsonMatch = rawOutput.match(/\[[\s\S]*\]/);
|
|
66
|
-
if (!jsonMatch) return [];
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const parsed = JSON.parse(jsonMatch[0]) as Array<{
|
|
70
|
-
table: string; column?: string; operation: string;
|
|
71
|
-
layer: string; message: string; severity: string; confidence: string;
|
|
72
|
-
file?: string;
|
|
73
|
-
}>;
|
|
74
|
-
return parsed
|
|
75
|
-
.filter(item => item.table && item.layer && item.message)
|
|
76
|
-
.map(item => ({
|
|
77
|
-
entity: {
|
|
78
|
-
table: item.table,
|
|
79
|
-
column: item.column,
|
|
80
|
-
operation: item.operation as SchemaEntity['operation'],
|
|
81
|
-
},
|
|
82
|
-
layer: item.layer as AlignmentFinding['layer'],
|
|
83
|
-
message: item.message,
|
|
84
|
-
file: item.file,
|
|
85
|
-
severity: (item.severity === 'error' ? 'error' : 'warning') as AlignmentFinding['severity'],
|
|
86
|
-
confidence: (['high', 'medium', 'low'].includes(item.confidence) ? item.confidence : 'medium') as AlignmentFinding['confidence'],
|
|
87
|
-
}));
|
|
88
|
-
} catch {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import type { SchemaEntity, Evidence, LayerScanResult, SchemaAlignmentConfig } from './types.ts';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_ROOTS = {
|
|
6
|
-
types: ['types/', 'src/types/', 'lib/types/'],
|
|
7
|
-
api: ['app/api/', 'lib/', 'services/', 'src/routes/'],
|
|
8
|
-
ui: ['app/', 'src/', 'components/'],
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function* walkFiles(dir: string): Generator<string> {
|
|
12
|
-
if (!fs.existsSync(dir)) return;
|
|
13
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
14
|
-
if (entry.isDirectory()) {
|
|
15
|
-
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
16
|
-
yield* walkFiles(path.join(dir, entry.name));
|
|
17
|
-
} else if (entry.isFile()) {
|
|
18
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
19
|
-
if (CODE_EXTS.has(ext)) yield path.join(dir, entry.name);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const IGNORED_DIRS = new Set([
|
|
25
|
-
'node_modules', '.next', 'dist', 'build', 'coverage', '.git',
|
|
26
|
-
'.turbo', '.cache', '.vercel', 'out', '.nuxt', 'target',
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
const CODE_EXTS = new Set([
|
|
30
|
-
'.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs',
|
|
31
|
-
'.vue', '.svelte', '.astro', '.py', '.rb', '.go', '.rs',
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
function resolveRoots(roots: string[], cwd: string): string[] {
|
|
35
|
-
return roots.map(r => path.isAbsolute(r) ? r : path.join(cwd, r));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function isUnder(filePath: string, dir: string): boolean {
|
|
39
|
-
const rel = path.relative(dir, filePath);
|
|
40
|
-
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function searchLayer(
|
|
44
|
-
roots: string[],
|
|
45
|
-
pattern: RegExp,
|
|
46
|
-
cwd: string,
|
|
47
|
-
excludedDirs: string[] = [],
|
|
48
|
-
): Evidence | null {
|
|
49
|
-
for (const root of roots) {
|
|
50
|
-
const dir = path.isAbsolute(root) ? root : path.join(cwd, root);
|
|
51
|
-
for (const filePath of walkFiles(dir)) {
|
|
52
|
-
// Skip files that belong to an excluded layer's root (avoids UI root
|
|
53
|
-
// `app/` reporting API evidence from `app/api/`, etc.)
|
|
54
|
-
if (excludedDirs.some(excl => isUnder(filePath, excl))) continue;
|
|
55
|
-
let content: string;
|
|
56
|
-
try { content = fs.readFileSync(filePath, 'utf8'); } catch { continue; }
|
|
57
|
-
const lines = content.split('\n');
|
|
58
|
-
for (let i = 0; i < lines.length; i++) {
|
|
59
|
-
if (pattern.test(lines[i]!)) {
|
|
60
|
-
return {
|
|
61
|
-
file: filePath,
|
|
62
|
-
line: i + 1,
|
|
63
|
-
snippet: lines[i]!.trim().slice(0, 120),
|
|
64
|
-
confidence: 'high',
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function escapeRe(s: string): string {
|
|
74
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function scanLayers(
|
|
78
|
-
entities: SchemaEntity[],
|
|
79
|
-
cwd: string,
|
|
80
|
-
config?: SchemaAlignmentConfig,
|
|
81
|
-
): LayerScanResult[] {
|
|
82
|
-
const roots = {
|
|
83
|
-
types: config?.layerRoots?.types ?? DEFAULT_ROOTS.types,
|
|
84
|
-
api: config?.layerRoots?.api ?? DEFAULT_ROOTS.api,
|
|
85
|
-
ui: config?.layerRoots?.ui ?? DEFAULT_ROOTS.ui,
|
|
86
|
-
};
|
|
87
|
-
const resolvedTypeRoots = resolveRoots(roots.types, cwd);
|
|
88
|
-
const resolvedApiRoots = resolveRoots(roots.api, cwd);
|
|
89
|
-
|
|
90
|
-
return entities.map(entity => {
|
|
91
|
-
const isDestructive = entity.operation === 'drop_column' || entity.operation === 'rename_column';
|
|
92
|
-
const searchName = isDestructive
|
|
93
|
-
? (entity.oldName ?? entity.column ?? entity.table)
|
|
94
|
-
: (entity.column ?? entity.table);
|
|
95
|
-
|
|
96
|
-
const pattern = new RegExp(`\\b${escapeRe(searchName)}\\b`);
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
entity,
|
|
100
|
-
typeLayer: searchLayer(roots.types, pattern, cwd),
|
|
101
|
-
// API search excludes type roots (prevents lib/ from picking up lib/types/ matches)
|
|
102
|
-
apiLayer: searchLayer(roots.api, pattern, cwd, resolvedTypeRoots),
|
|
103
|
-
// UI search excludes both type and API roots (prevents app/ from picking up app/api/)
|
|
104
|
-
uiLayer: searchLayer(roots.ui, pattern, cwd, [...resolvedTypeRoots, ...resolvedApiRoots]),
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
}
|