@delegance/claude-autopilot 5.0.1 → 5.0.2
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/cli/index.js +39 -1
- package/dist/src/cli/preflight.js +17 -4
- 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/detect/stack.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
|
|
4
|
-
function readJson(p: string): Record<string, unknown> | null {
|
|
5
|
-
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function fileContains(p: string, needle: string): boolean {
|
|
9
|
-
try { return fs.readFileSync(p, 'utf8').includes(needle); } catch { return false; }
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function readFile(p: string): string {
|
|
13
|
-
try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function version(deps: Record<string, string>, name: string): string | null {
|
|
17
|
-
const v = deps[name];
|
|
18
|
-
if (!v) return null;
|
|
19
|
-
return v.replace(/^[\^~>=<\s]+/, '').split('.')[0] ?? null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Infers a human-readable stack description from project files.
|
|
24
|
-
* Returns null if nothing definitive is found (caller should omit from prompt).
|
|
25
|
-
*/
|
|
26
|
-
export function detectStack(cwd: string): string | null {
|
|
27
|
-
// Go
|
|
28
|
-
const goMod = path.join(cwd, 'go.mod');
|
|
29
|
-
if (fs.existsSync(goMod)) {
|
|
30
|
-
const content = readFile(goMod);
|
|
31
|
-
const parts = ['Go'];
|
|
32
|
-
if (content.includes('gin-gonic/gin')) parts.push('Gin');
|
|
33
|
-
else if (content.includes('labstack/echo')) parts.push('Echo');
|
|
34
|
-
else if (content.includes('gofiber/fiber')) parts.push('Fiber');
|
|
35
|
-
else if (content.includes('go-chi/chi')) parts.push('Chi');
|
|
36
|
-
if (content.includes('database/sql') || content.includes('sqlx') || content.includes('pgx')) parts.push('PostgreSQL');
|
|
37
|
-
if (content.includes('gorm.io')) parts.push('GORM');
|
|
38
|
-
if (content.includes('redis')) parts.push('Redis');
|
|
39
|
-
return parts.join(' + ');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Rust
|
|
43
|
-
const cargoToml = path.join(cwd, 'Cargo.toml');
|
|
44
|
-
if (fs.existsSync(cargoToml)) {
|
|
45
|
-
const content = readFile(cargoToml);
|
|
46
|
-
const parts = ['Rust'];
|
|
47
|
-
if (content.includes('actix-web')) parts.push('Actix-Web');
|
|
48
|
-
else if (content.includes('axum')) parts.push('Axum');
|
|
49
|
-
else if (content.includes('warp')) parts.push('Warp');
|
|
50
|
-
if (content.includes('sqlx') || content.includes('diesel')) parts.push('PostgreSQL');
|
|
51
|
-
if (content.includes('serde')) parts.push('Serde');
|
|
52
|
-
if (content.includes('tokio')) parts.push('Tokio async');
|
|
53
|
-
return parts.join(' + ');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Ruby / Rails
|
|
57
|
-
const gemfile = path.join(cwd, 'Gemfile');
|
|
58
|
-
if (fs.existsSync(gemfile)) {
|
|
59
|
-
const content = readFile(gemfile);
|
|
60
|
-
const parts: string[] = [];
|
|
61
|
-
if (content.includes("'rails'") || content.includes('"rails"')) parts.push('Ruby on Rails');
|
|
62
|
-
else if (content.includes("'sinatra'") || content.includes('"sinatra"')) parts.push('Sinatra');
|
|
63
|
-
else parts.push('Ruby');
|
|
64
|
-
if (content.includes('pg') || content.includes('postgresql')) parts.push('PostgreSQL');
|
|
65
|
-
else if (content.includes('mysql')) parts.push('MySQL');
|
|
66
|
-
else if (content.includes('sqlite')) parts.push('SQLite');
|
|
67
|
-
if (content.includes('rspec')) parts.push('RSpec');
|
|
68
|
-
if (content.includes('sidekiq')) parts.push('Sidekiq');
|
|
69
|
-
return parts.join(' + ');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Python
|
|
73
|
-
const reqTxt = path.join(cwd, 'requirements.txt');
|
|
74
|
-
const pyproject = path.join(cwd, 'pyproject.toml');
|
|
75
|
-
const hasFastapi = fileContains(reqTxt, 'fastapi') || fileContains(pyproject, 'fastapi');
|
|
76
|
-
const hasDjango = fileContains(reqTxt, 'django') || fileContains(pyproject, 'django');
|
|
77
|
-
const hasFlask = fileContains(reqTxt, 'flask') || fileContains(pyproject, 'flask');
|
|
78
|
-
if (hasFastapi || hasDjango || hasFlask || fs.existsSync(reqTxt) || fs.existsSync(pyproject)) {
|
|
79
|
-
const parts: string[] = [];
|
|
80
|
-
if (hasFastapi) parts.push('FastAPI');
|
|
81
|
-
else if (hasDjango) parts.push('Django');
|
|
82
|
-
else if (hasFlask) parts.push('Flask');
|
|
83
|
-
else parts.push('Python');
|
|
84
|
-
const combined = readFile(reqTxt) + readFile(pyproject);
|
|
85
|
-
if (combined.includes('sqlalchemy') || combined.includes('SQLAlchemy')) parts.push('SQLAlchemy');
|
|
86
|
-
if (combined.includes('postgresql') || combined.includes('psycopg')) parts.push('PostgreSQL');
|
|
87
|
-
if (combined.includes('pydantic')) parts.push('Pydantic');
|
|
88
|
-
if (combined.includes('celery')) parts.push('Celery');
|
|
89
|
-
return parts.join(' + ');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Node / JS / TS
|
|
93
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
94
|
-
if (!fs.existsSync(pkgPath)) return null;
|
|
95
|
-
const pkg = readJson(pkgPath);
|
|
96
|
-
if (!pkg) return null;
|
|
97
|
-
|
|
98
|
-
const deps: Record<string, string> = {
|
|
99
|
-
...(pkg['dependencies'] as Record<string, string> ?? {}),
|
|
100
|
-
...(pkg['devDependencies'] as Record<string, string> ?? {}),
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const parts: string[] = [];
|
|
104
|
-
const isTs = 'typescript' in deps || fs.existsSync(path.join(cwd, 'tsconfig.json'));
|
|
105
|
-
|
|
106
|
-
// Framework
|
|
107
|
-
if ('next' in deps) {
|
|
108
|
-
const v = version(deps, 'next');
|
|
109
|
-
parts.push(v ? `Next.js ${v}` : 'Next.js');
|
|
110
|
-
} else if ('nuxt' in deps || 'nuxt3' in deps) {
|
|
111
|
-
parts.push('Nuxt');
|
|
112
|
-
} else if ('remix' in deps || '@remix-run/react' in deps) {
|
|
113
|
-
parts.push('Remix');
|
|
114
|
-
} else if ('astro' in deps) {
|
|
115
|
-
parts.push('Astro');
|
|
116
|
-
} else if ('express' in deps) {
|
|
117
|
-
parts.push('Express');
|
|
118
|
-
} else if ('fastify' in deps) {
|
|
119
|
-
parts.push('Fastify');
|
|
120
|
-
} else if ('hono' in deps) {
|
|
121
|
-
parts.push('Hono');
|
|
122
|
-
} else if ('react' in deps) {
|
|
123
|
-
parts.push('React');
|
|
124
|
-
} else if ('vue' in deps) {
|
|
125
|
-
parts.push('Vue');
|
|
126
|
-
} else if ('svelte' in deps || '@sveltejs/kit' in deps) {
|
|
127
|
-
parts.push('SvelteKit');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Database / ORM
|
|
131
|
-
if ('@supabase/supabase-js' in deps) parts.push('Supabase');
|
|
132
|
-
if ('prisma' in deps || '@prisma/client' in deps) parts.push('Prisma');
|
|
133
|
-
if ('drizzle-orm' in deps) parts.push('Drizzle');
|
|
134
|
-
if ('typeorm' in deps) parts.push('TypeORM');
|
|
135
|
-
if ('mongoose' in deps) parts.push('MongoDB');
|
|
136
|
-
|
|
137
|
-
// Meta-frameworks / routers
|
|
138
|
-
if ('@trpc/server' in deps) parts.push('tRPC');
|
|
139
|
-
if ('graphql' in deps && ('apollo-server' in deps || '@apollo/server' in deps)) parts.push('GraphQL/Apollo');
|
|
140
|
-
|
|
141
|
-
// Auth
|
|
142
|
-
if ('next-auth' in deps || '@auth/core' in deps) parts.push('NextAuth');
|
|
143
|
-
if ('clerk' in deps || '@clerk/nextjs' in deps) parts.push('Clerk');
|
|
144
|
-
|
|
145
|
-
// UI
|
|
146
|
-
if ('tailwindcss' in deps) parts.push('Tailwind CSS');
|
|
147
|
-
|
|
148
|
-
// Language suffix
|
|
149
|
-
if (isTs) parts.push('TypeScript');
|
|
150
|
-
|
|
151
|
-
if (parts.length === 0) return null;
|
|
152
|
-
return parts.join(' + ');
|
|
153
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
|
|
4
|
-
export interface Workspace {
|
|
5
|
-
name: string;
|
|
6
|
-
dir: string; // absolute path
|
|
7
|
-
rel: string; // relative to root
|
|
8
|
-
testCommand?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function readJson(p: string): Record<string, unknown> | null {
|
|
12
|
-
try { return JSON.parse(fs.readFileSync(p, 'utf8')) as Record<string, unknown>; } catch { return null; }
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function globDirs(root: string, patterns: string[]): string[] {
|
|
16
|
-
const results: string[] = [];
|
|
17
|
-
for (const pattern of patterns) {
|
|
18
|
-
// Support "packages/*" and "apps/*" style globs (one level deep only)
|
|
19
|
-
const parts = pattern.split('/');
|
|
20
|
-
if (parts.length === 2 && parts[1] === '*') {
|
|
21
|
-
const base = path.join(root, parts[0]!);
|
|
22
|
-
if (!fs.existsSync(base)) continue;
|
|
23
|
-
for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
|
|
24
|
-
if (entry.isDirectory()) results.push(path.join(base, entry.name));
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
const abs = path.join(root, pattern);
|
|
28
|
-
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) results.push(abs);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return results;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function detectTestCommand(dir: string): string | undefined {
|
|
35
|
-
const pkg = readJson(path.join(dir, 'package.json'));
|
|
36
|
-
if (pkg?.scripts && typeof (pkg.scripts as Record<string, unknown>).test === 'string') {
|
|
37
|
-
return `npm test --prefix ${dir}`;
|
|
38
|
-
}
|
|
39
|
-
if (fs.existsSync(path.join(dir, 'go.mod'))) return `go test ./... -C ${dir}`;
|
|
40
|
-
if (fs.existsSync(path.join(dir, 'Cargo.toml'))) return `cargo test --manifest-path ${path.join(dir, 'Cargo.toml')}`;
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Detect npm/yarn/pnpm workspaces, Turborepo, Nx, Go multi-module. */
|
|
45
|
-
export function detectWorkspaces(cwd: string): Workspace[] | null {
|
|
46
|
-
const pkg = readJson(path.join(cwd, 'package.json')) as { workspaces?: string[] | { packages?: string[] }; name?: string } | null;
|
|
47
|
-
|
|
48
|
-
// npm/yarn workspaces
|
|
49
|
-
let wsDirs: string[] = [];
|
|
50
|
-
if (pkg?.workspaces) {
|
|
51
|
-
const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : (pkg.workspaces.packages ?? []);
|
|
52
|
-
wsDirs = globDirs(cwd, patterns);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Turborepo — pnpm-workspace.yaml or turbo.json
|
|
56
|
-
if (wsDirs.length === 0 && fs.existsSync(path.join(cwd, 'pnpm-workspace.yaml'))) {
|
|
57
|
-
try {
|
|
58
|
-
const raw = fs.readFileSync(path.join(cwd, 'pnpm-workspace.yaml'), 'utf8');
|
|
59
|
-
const matches = raw.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm) ?? [];
|
|
60
|
-
const patterns = matches.map(m => m.replace(/^\s*-\s*['"]?/, '').replace(/['"]?\s*$/, '').trim());
|
|
61
|
-
wsDirs = globDirs(cwd, patterns);
|
|
62
|
-
} catch { /* ignore */ }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Turborepo fallback: packages/ and apps/ dirs
|
|
66
|
-
if (wsDirs.length === 0 && fs.existsSync(path.join(cwd, 'turbo.json'))) {
|
|
67
|
-
wsDirs = globDirs(cwd, ['packages/*', 'apps/*']);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Nx: check nx.json + libs/ + apps/
|
|
71
|
-
if (wsDirs.length === 0 && fs.existsSync(path.join(cwd, 'nx.json'))) {
|
|
72
|
-
wsDirs = globDirs(cwd, ['libs/*', 'apps/*', 'packages/*']);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (wsDirs.length === 0) return null;
|
|
76
|
-
|
|
77
|
-
return wsDirs
|
|
78
|
-
.filter(d => fs.existsSync(d))
|
|
79
|
-
.map(d => {
|
|
80
|
-
const rel = path.relative(cwd, d);
|
|
81
|
-
const pkgJson = readJson(path.join(d, 'package.json')) as { name?: string } | null;
|
|
82
|
-
return {
|
|
83
|
-
name: pkgJson?.name ?? rel,
|
|
84
|
-
dir: d,
|
|
85
|
-
rel,
|
|
86
|
-
testCommand: detectTestCommand(d),
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Given a list of touched files, return which workspaces they belong to. */
|
|
92
|
-
export function mapFilesToWorkspaces(files: string[], workspaces: Workspace[], cwd: string): Map<Workspace, string[]> {
|
|
93
|
-
const result = new Map<Workspace, string[]>();
|
|
94
|
-
for (const file of files) {
|
|
95
|
-
const abs = path.isAbsolute(file) ? file : path.resolve(cwd, file);
|
|
96
|
-
const ws = workspaces.find(w => abs.startsWith(w.dir + path.sep) || abs === w.dir);
|
|
97
|
-
if (ws) {
|
|
98
|
-
if (!result.has(ws)) result.set(ws, []);
|
|
99
|
-
result.get(ws)!.push(file);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
103
|
-
}
|
package/src/core/errors.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// src/core/errors.ts
|
|
2
|
-
|
|
3
|
-
export type ErrorCode =
|
|
4
|
-
| 'auth' | 'rate_limit' | 'transient_network' | 'invalid_config'
|
|
5
|
-
| 'adapter_bug' | 'user_input' | 'budget_exceeded' | 'concurrency_lock' | 'superseded';
|
|
6
|
-
|
|
7
|
-
export interface GuardrailErrorOptions {
|
|
8
|
-
code: ErrorCode;
|
|
9
|
-
retryable?: boolean;
|
|
10
|
-
provider?: string;
|
|
11
|
-
step?: string;
|
|
12
|
-
details?: Record<string, unknown>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const DEFAULT_RETRYABLE: Record<ErrorCode, boolean> = {
|
|
16
|
-
auth: false, rate_limit: true, transient_network: true, invalid_config: false,
|
|
17
|
-
adapter_bug: false, user_input: false, budget_exceeded: false,
|
|
18
|
-
concurrency_lock: false, superseded: false,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export class GuardrailError extends Error {
|
|
22
|
-
code: ErrorCode;
|
|
23
|
-
retryable: boolean;
|
|
24
|
-
provider?: string;
|
|
25
|
-
step?: string;
|
|
26
|
-
details: Record<string, unknown>;
|
|
27
|
-
|
|
28
|
-
constructor(message: string, options: GuardrailErrorOptions) {
|
|
29
|
-
super(message);
|
|
30
|
-
this.name = 'GuardrailError';
|
|
31
|
-
this.code = options.code;
|
|
32
|
-
this.retryable = options.retryable ?? DEFAULT_RETRYABLE[options.code];
|
|
33
|
-
this.provider = options.provider;
|
|
34
|
-
this.step = options.step;
|
|
35
|
-
this.details = options.details ?? {};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Finding } from './types.ts';
|
|
2
|
-
|
|
3
|
-
export function findingContentKey(f: Finding): string {
|
|
4
|
-
return `${f.file}|${f.line ?? ''}|${f.severity}|${f.message.slice(0, 40)}`;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function dedupFindings(findings: Finding[]): Finding[] {
|
|
8
|
-
const seen = new Map<string, Finding>();
|
|
9
|
-
for (const f of findings) {
|
|
10
|
-
const key = findingContentKey(f);
|
|
11
|
-
if (!seen.has(key)) seen.set(key, f);
|
|
12
|
-
}
|
|
13
|
-
return Array.from(seen.values());
|
|
14
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// src/core/findings/types.ts
|
|
2
|
-
|
|
3
|
-
export type FindingSource = 'static-rules' | 'review-engine' | 'pipeline' | `review-bot:${string}`;
|
|
4
|
-
export type Severity = 'critical' | 'warning' | 'note';
|
|
5
|
-
|
|
6
|
-
export interface Finding {
|
|
7
|
-
id: string;
|
|
8
|
-
source: FindingSource;
|
|
9
|
-
severity: Severity;
|
|
10
|
-
category: string;
|
|
11
|
-
file: string;
|
|
12
|
-
line?: number;
|
|
13
|
-
message: string;
|
|
14
|
-
suggestion?: string;
|
|
15
|
-
protectedPath: boolean;
|
|
16
|
-
createdAt: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type TriageVerdict = 'real_bug' | 'false_positive' | 'low_value';
|
|
20
|
-
export type TriageAction = 'auto_fix' | 'propose_patch' | 'ask_question' | 'dismiss' | 'needs_human';
|
|
21
|
-
|
|
22
|
-
export interface TriageRecord {
|
|
23
|
-
findingId: string;
|
|
24
|
-
verdict: TriageVerdict;
|
|
25
|
-
confidence: number;
|
|
26
|
-
reason: string;
|
|
27
|
-
action: TriageAction;
|
|
28
|
-
recordedAt: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type FixStatus = 'fixed' | 'reverted' | 'human_required' | 'skipped';
|
|
32
|
-
|
|
33
|
-
export interface FixAttempt {
|
|
34
|
-
findingId: string;
|
|
35
|
-
attemptedAt: string;
|
|
36
|
-
status: FixStatus;
|
|
37
|
-
commitSha?: string;
|
|
38
|
-
notes?: string;
|
|
39
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import type { Finding } from '../findings/types.ts';
|
|
4
|
-
import type { ReviewEngine } from '../../adapters/review-engine/types.ts';
|
|
5
|
-
|
|
6
|
-
export const CONTEXT_LINES = 20;
|
|
7
|
-
|
|
8
|
-
// LLM error / refusal phrases that indicate a bad output
|
|
9
|
-
const REFUSAL_PHRASES = [
|
|
10
|
-
'i cannot', "i can't", 'i am unable', 'as an ai', 'as a language model',
|
|
11
|
-
'i apologize', "i'm sorry", 'cannot safely', 'would require', 'error:',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
export interface GenerateResult {
|
|
15
|
-
status: 'ok' | 'cannot_fix' | 'rejected' | 'error';
|
|
16
|
-
reason?: string;
|
|
17
|
-
originalLines?: string[];
|
|
18
|
-
replacementLines?: string[];
|
|
19
|
-
startLine?: number;
|
|
20
|
-
endLine?: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function validateReplacement(original: string[], replacement: string[], _finding: Finding): string | null {
|
|
24
|
-
if (replacement.length === 0) return 'LLM returned empty output';
|
|
25
|
-
|
|
26
|
-
// Reject obvious LLM refusals
|
|
27
|
-
const joined = replacement.join(' ').toLowerCase();
|
|
28
|
-
for (const phrase of REFUSAL_PHRASES) {
|
|
29
|
-
if (joined.includes(phrase)) return `LLM refused: "${replacement[0]?.slice(0, 60)}"`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Reject if line count ballooned more than 3x (likely hallucination)
|
|
33
|
-
if (replacement.length > original.length * 3 + 10) {
|
|
34
|
-
return `Suspicious: replacement is ${replacement.length} lines vs original ${original.length}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Reject if the replacement is identical (LLM made no change)
|
|
38
|
-
if (replacement.join('\n') === original.join('\n')) {
|
|
39
|
-
return 'LLM returned identical code — no change made';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function buildUnifiedDiff(
|
|
46
|
-
original: string[],
|
|
47
|
-
replacement: string[],
|
|
48
|
-
filePath: string,
|
|
49
|
-
startLine: number,
|
|
50
|
-
opts: { color?: boolean } = {},
|
|
51
|
-
): string {
|
|
52
|
-
// Colors default on for CLI ergonomics; MCP callers pass color:false so ANSI
|
|
53
|
-
// escapes don't leak into JSON responses that machine clients must parse.
|
|
54
|
-
const useColor = opts.color !== false;
|
|
55
|
-
const C = {
|
|
56
|
-
reset: '\x1b[0m', green: '\x1b[32m', red: '\x1b[31m',
|
|
57
|
-
};
|
|
58
|
-
const fmt = (c: keyof typeof C, t: string) => useColor ? `${C[c]}${t}${C.reset}` : t;
|
|
59
|
-
const lines: string[] = [`--- ${filePath}`, `+++ ${filePath} (proposed fix)`, `@@ -${startLine},${original.length} +${startLine},${replacement.length} @@`];
|
|
60
|
-
for (const l of original) lines.push(fmt('red', `- ${l}`));
|
|
61
|
-
for (const l of replacement) lines.push(fmt('green', `+ ${l}`));
|
|
62
|
-
return lines.join('\n');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function generateFix(finding: Finding, engine: ReviewEngine, cwd: string): Promise<GenerateResult> {
|
|
66
|
-
// MCP handlers can pass through findings loaded from disk — validate required
|
|
67
|
-
// fields rather than trusting the CLI pre-filter path.
|
|
68
|
-
if (!finding.file || typeof finding.file !== 'string') {
|
|
69
|
-
return { status: 'cannot_fix', reason: 'finding.file missing' };
|
|
70
|
-
}
|
|
71
|
-
if (typeof finding.line !== 'number' || !Number.isFinite(finding.line) || finding.line < 1) {
|
|
72
|
-
return { status: 'cannot_fix', reason: 'finding.line missing or invalid' };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const absPath = path.resolve(cwd, finding.file);
|
|
76
|
-
let fileLines: string[];
|
|
77
|
-
try {
|
|
78
|
-
fileLines = fs.readFileSync(absPath, 'utf8').split('\n');
|
|
79
|
-
} catch {
|
|
80
|
-
return { status: 'cannot_fix', reason: 'file not readable' };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const lineIdx = finding.line - 1;
|
|
84
|
-
if (lineIdx < 0 || lineIdx >= fileLines.length) {
|
|
85
|
-
return { status: 'cannot_fix', reason: 'line out of range' };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const startIdx = Math.max(0, lineIdx - CONTEXT_LINES);
|
|
89
|
-
const endIdx = Math.min(fileLines.length - 1, lineIdx + CONTEXT_LINES);
|
|
90
|
-
const contextLines = fileLines.slice(startIdx, endIdx + 1);
|
|
91
|
-
const startLine = startIdx + 1;
|
|
92
|
-
|
|
93
|
-
const numbered = contextLines.map((l, i) => {
|
|
94
|
-
const n = startLine + i;
|
|
95
|
-
return `${n === finding.line ? '>>>' : ' '} ${String(n).padStart(4)}: ${l}`;
|
|
96
|
-
}).join('\n');
|
|
97
|
-
|
|
98
|
-
const prompt = [
|
|
99
|
-
`File: ${finding.file}`,
|
|
100
|
-
`Finding (line ${finding.line}): [${finding.severity.toUpperCase()}] ${finding.message}`,
|
|
101
|
-
finding.suggestion ? `Suggestion: ${finding.suggestion}` : '',
|
|
102
|
-
'',
|
|
103
|
-
'Relevant lines (>>> marks the finding):',
|
|
104
|
-
'```',
|
|
105
|
-
numbered,
|
|
106
|
-
'```',
|
|
107
|
-
'',
|
|
108
|
-
`Rewrite ONLY lines ${startLine}–${endIdx + 1} to fix this finding.`,
|
|
109
|
-
'Rules:',
|
|
110
|
-
'- Output ONLY the replacement lines with no explanation, no markdown fences, no line numbers',
|
|
111
|
-
'- Preserve indentation exactly',
|
|
112
|
-
'- Make the minimal change needed — do not refactor unrelated code',
|
|
113
|
-
'- If the fix cannot be done safely in this context, output exactly: CANNOT_FIX',
|
|
114
|
-
].filter(Boolean).join('\n');
|
|
115
|
-
|
|
116
|
-
let rawOutput: string;
|
|
117
|
-
try {
|
|
118
|
-
const output = await engine.review({ content: prompt, kind: 'file-batch' });
|
|
119
|
-
rawOutput = output.rawOutput.trim();
|
|
120
|
-
} catch (err) {
|
|
121
|
-
return { status: 'error', reason: `LLM error: ${err instanceof Error ? err.message : String(err)}` };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (rawOutput === 'CANNOT_FIX' || rawOutput.startsWith('CANNOT_FIX')) {
|
|
125
|
-
return { status: 'cannot_fix', reason: 'LLM: cannot fix safely in this context' };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Strip markdown fences if the model added them despite instructions
|
|
129
|
-
const cleaned = rawOutput
|
|
130
|
-
.replace(/^```[a-zA-Z]*\n?/, '')
|
|
131
|
-
.replace(/\n?```$/, '')
|
|
132
|
-
.trimEnd();
|
|
133
|
-
|
|
134
|
-
const replacementLines = cleaned.split('\n');
|
|
135
|
-
const originalLines = contextLines;
|
|
136
|
-
|
|
137
|
-
const validationError = validateReplacement(originalLines, replacementLines, finding);
|
|
138
|
-
if (validationError) {
|
|
139
|
-
return { status: 'rejected', reason: validationError };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
status: 'ok',
|
|
144
|
-
originalLines,
|
|
145
|
-
replacementLines,
|
|
146
|
-
startLine,
|
|
147
|
-
endLine: endIdx + 1,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { runSafe } from '../shell.ts';
|
|
2
|
-
|
|
3
|
-
export interface FileDiff {
|
|
4
|
-
file: string;
|
|
5
|
-
hunks: string; // unified diff content for this file (header + hunks)
|
|
6
|
-
additions: number;
|
|
7
|
-
deletions: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns per-file unified diffs for the given files between base and HEAD.
|
|
12
|
-
* Falls back to working-tree diff (unstaged) when base diff is empty for a file.
|
|
13
|
-
*/
|
|
14
|
-
export function getFileDiffs(cwd: string, base: string, files: string[]): FileDiff[] {
|
|
15
|
-
if (files.length === 0) return [];
|
|
16
|
-
|
|
17
|
-
// Get full diff in one shot — more efficient than per-file calls
|
|
18
|
-
const raw = runSafe('git', ['diff', base, 'HEAD', '--unified=3', '--', ...files], { cwd })
|
|
19
|
-
?? runSafe('git', ['diff', 'HEAD', '--unified=3', '--', ...files], { cwd })
|
|
20
|
-
?? '';
|
|
21
|
-
|
|
22
|
-
return parseUnifiedDiff(raw, files);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Parses unified diff output into per-file FileDiff entries.
|
|
27
|
-
* Only returns files that actually have diff content.
|
|
28
|
-
*/
|
|
29
|
-
export function parseUnifiedDiff(raw: string, requestedFiles: string[]): FileDiff[] {
|
|
30
|
-
if (!raw.trim()) return [];
|
|
31
|
-
|
|
32
|
-
const results: FileDiff[] = [];
|
|
33
|
-
const sections = raw.split(/^(?=diff --git )/m).filter(Boolean);
|
|
34
|
-
|
|
35
|
-
const requested = new Set(requestedFiles.map(f => f.replace(/\\/g, '/')));
|
|
36
|
-
|
|
37
|
-
for (const section of sections) {
|
|
38
|
-
// Extract b/ filename from diff header: diff --git a/src/foo.ts b/src/foo.ts
|
|
39
|
-
const headerMatch = section.match(/^diff --git a\/.+ b\/(.+)$/m);
|
|
40
|
-
if (!headerMatch) continue;
|
|
41
|
-
const file = headerMatch[1]!.trim();
|
|
42
|
-
if (!requested.has(file)) continue;
|
|
43
|
-
|
|
44
|
-
// Strip the git binary/index header lines, keep hunk content
|
|
45
|
-
const hunkStart = section.indexOf('@@');
|
|
46
|
-
const hunks = hunkStart >= 0 ? section.slice(hunkStart) : '';
|
|
47
|
-
if (!hunks.trim()) continue;
|
|
48
|
-
|
|
49
|
-
let additions = 0;
|
|
50
|
-
let deletions = 0;
|
|
51
|
-
for (const line of hunks.split('\n')) {
|
|
52
|
-
if (line.startsWith('+') && !line.startsWith('+++')) additions++;
|
|
53
|
-
if (line.startsWith('-') && !line.startsWith('---')) deletions++;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
results.push({ file, hunks: hunks.trimEnd(), additions, deletions });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return results;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Formats FileDiff entries into a review-ready string.
|
|
64
|
-
* Total size is bounded by maxChars (default 120K chars ≈ 30K tokens).
|
|
65
|
-
*/
|
|
66
|
-
export function formatDiffContent(diffs: FileDiff[], maxChars = 120_000): string {
|
|
67
|
-
const parts: string[] = [];
|
|
68
|
-
let total = 0;
|
|
69
|
-
let skipped = 0;
|
|
70
|
-
|
|
71
|
-
for (const d of diffs) {
|
|
72
|
-
const section = `## ${d.file} (+${d.additions}/-${d.deletions})\n\`\`\`diff\n${d.hunks}\n\`\`\``;
|
|
73
|
-
if (total + section.length > maxChars) {
|
|
74
|
-
skipped++;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
parts.push(section);
|
|
78
|
-
total += section.length;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (skipped > 0) {
|
|
82
|
-
parts.push(`[${skipped} file${skipped !== 1 ? 's' : ''} omitted — diff exceeded size limit]`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return parts.join('\n\n');
|
|
86
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { runSafe } from '../shell.ts';
|
|
2
|
-
|
|
3
|
-
const IGNORE_PREFIXES = [
|
|
4
|
-
'node_modules/',
|
|
5
|
-
'dist/',
|
|
6
|
-
'build/',
|
|
7
|
-
'.next/',
|
|
8
|
-
'.nuxt/',
|
|
9
|
-
'out/',
|
|
10
|
-
'coverage/',
|
|
11
|
-
'.turbo/',
|
|
12
|
-
'.cache/',
|
|
13
|
-
'vendor/',
|
|
14
|
-
'__pycache__/',
|
|
15
|
-
'.venv/',
|
|
16
|
-
'venv/',
|
|
17
|
-
'target/', // Rust/Java
|
|
18
|
-
'.gradle/',
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
function isIgnored(file: string): boolean {
|
|
22
|
-
return IGNORE_PREFIXES.some(p => file.startsWith(p));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface TouchedFilesOptions {
|
|
26
|
-
cwd?: string;
|
|
27
|
-
base?: string; // e.g. 'HEAD~1', 'main', a SHA — defaults to HEAD~1
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns the list of files changed relative to `base` (default HEAD~1).
|
|
32
|
-
* Falls back to `git status --short` unstaged/staged files if the diff fails
|
|
33
|
-
* (e.g. first commit, no parent).
|
|
34
|
-
*/
|
|
35
|
-
export function resolveGitTouchedFiles(options: TouchedFilesOptions = {}): string[] {
|
|
36
|
-
const cwd = options.cwd ?? process.cwd();
|
|
37
|
-
const base = options.base ?? 'HEAD~1';
|
|
38
|
-
|
|
39
|
-
// Primary: diff against base
|
|
40
|
-
const diffOut = runSafe('git', ['diff', '--name-only', base, 'HEAD'], { cwd });
|
|
41
|
-
if (diffOut !== null && diffOut.trim().length > 0) {
|
|
42
|
-
return parseFileList(diffOut);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Fallback: staged + unstaged working tree changes
|
|
46
|
-
const statusOut = runSafe('git', ['status', '--short'], { cwd });
|
|
47
|
-
if (statusOut !== null && statusOut.trim().length > 0) {
|
|
48
|
-
return parseStatusOutput(statusOut);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseFileList(output: string): string[] {
|
|
55
|
-
return [...new Set(output.split('\n').map(l => l.trim()).filter(Boolean).filter(f => !isIgnored(f)))];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function parseStatusOutput(output: string): string[] {
|
|
59
|
-
const files = new Set<string>();
|
|
60
|
-
for (const line of output.split('\n')) {
|
|
61
|
-
const trimmed = line.trim();
|
|
62
|
-
if (!trimmed) continue;
|
|
63
|
-
// format: XY filename (or XY old -> new for renames)
|
|
64
|
-
const parts = trimmed.replace(/^\S+\s+/, '');
|
|
65
|
-
const renamed = parts.match(/^(.+)\s+->\s+(.+)$/);
|
|
66
|
-
if (renamed) {
|
|
67
|
-
files.add(renamed[2]!.trim());
|
|
68
|
-
} else {
|
|
69
|
-
files.add(parts.trim());
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return [...files].filter(f => !isIgnored(f));
|
|
73
|
-
}
|