@dotsetlabs/bellwether 0.10.0
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/CHANGELOG.md +291 -0
- package/LICENSE +21 -0
- package/README.md +739 -0
- package/dist/auth/credentials.d.ts +64 -0
- package/dist/auth/credentials.js +218 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/keychain.d.ts +64 -0
- package/dist/auth/keychain.js +268 -0
- package/dist/baseline/ab-testing.d.ts +80 -0
- package/dist/baseline/ab-testing.js +236 -0
- package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
- package/dist/baseline/ai-compatibility-scorer.js +606 -0
- package/dist/baseline/calibration.d.ts +77 -0
- package/dist/baseline/calibration.js +136 -0
- package/dist/baseline/category-matching.d.ts +85 -0
- package/dist/baseline/category-matching.js +289 -0
- package/dist/baseline/change-impact-analyzer.d.ts +98 -0
- package/dist/baseline/change-impact-analyzer.js +592 -0
- package/dist/baseline/comparator.d.ts +64 -0
- package/dist/baseline/comparator.js +916 -0
- package/dist/baseline/confidence.d.ts +55 -0
- package/dist/baseline/confidence.js +122 -0
- package/dist/baseline/converter.d.ts +61 -0
- package/dist/baseline/converter.js +585 -0
- package/dist/baseline/dependency-analyzer.d.ts +89 -0
- package/dist/baseline/dependency-analyzer.js +567 -0
- package/dist/baseline/deprecation-tracker.d.ts +133 -0
- package/dist/baseline/deprecation-tracker.js +322 -0
- package/dist/baseline/diff.d.ts +55 -0
- package/dist/baseline/diff.js +1584 -0
- package/dist/baseline/documentation-scorer.d.ts +205 -0
- package/dist/baseline/documentation-scorer.js +466 -0
- package/dist/baseline/embeddings.d.ts +118 -0
- package/dist/baseline/embeddings.js +251 -0
- package/dist/baseline/error-analyzer.d.ts +198 -0
- package/dist/baseline/error-analyzer.js +721 -0
- package/dist/baseline/evaluation/evaluator.d.ts +42 -0
- package/dist/baseline/evaluation/evaluator.js +323 -0
- package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
- package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
- package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
- package/dist/baseline/evaluation/golden-dataset.js +717 -0
- package/dist/baseline/evaluation/index.d.ts +15 -0
- package/dist/baseline/evaluation/index.js +15 -0
- package/dist/baseline/evaluation/types.d.ts +186 -0
- package/dist/baseline/evaluation/types.js +8 -0
- package/dist/baseline/external-dependency-detector.d.ts +181 -0
- package/dist/baseline/external-dependency-detector.js +524 -0
- package/dist/baseline/golden-output.d.ts +162 -0
- package/dist/baseline/golden-output.js +636 -0
- package/dist/baseline/health-scorer.d.ts +174 -0
- package/dist/baseline/health-scorer.js +451 -0
- package/dist/baseline/incremental-checker.d.ts +97 -0
- package/dist/baseline/incremental-checker.js +174 -0
- package/dist/baseline/index.d.ts +31 -0
- package/dist/baseline/index.js +42 -0
- package/dist/baseline/migration-generator.d.ts +137 -0
- package/dist/baseline/migration-generator.js +554 -0
- package/dist/baseline/migrations.d.ts +60 -0
- package/dist/baseline/migrations.js +197 -0
- package/dist/baseline/performance-tracker.d.ts +214 -0
- package/dist/baseline/performance-tracker.js +577 -0
- package/dist/baseline/pr-comment-generator.d.ts +117 -0
- package/dist/baseline/pr-comment-generator.js +546 -0
- package/dist/baseline/response-fingerprint.d.ts +127 -0
- package/dist/baseline/response-fingerprint.js +728 -0
- package/dist/baseline/response-schema-tracker.d.ts +129 -0
- package/dist/baseline/response-schema-tracker.js +420 -0
- package/dist/baseline/risk-scorer.d.ts +54 -0
- package/dist/baseline/risk-scorer.js +434 -0
- package/dist/baseline/saver.d.ts +89 -0
- package/dist/baseline/saver.js +554 -0
- package/dist/baseline/scenario-generator.d.ts +151 -0
- package/dist/baseline/scenario-generator.js +905 -0
- package/dist/baseline/schema-compare.d.ts +86 -0
- package/dist/baseline/schema-compare.js +557 -0
- package/dist/baseline/schema-evolution.d.ts +189 -0
- package/dist/baseline/schema-evolution.js +467 -0
- package/dist/baseline/semantic.d.ts +203 -0
- package/dist/baseline/semantic.js +908 -0
- package/dist/baseline/synonyms.d.ts +60 -0
- package/dist/baseline/synonyms.js +386 -0
- package/dist/baseline/telemetry.d.ts +165 -0
- package/dist/baseline/telemetry.js +294 -0
- package/dist/baseline/test-pruner.d.ts +120 -0
- package/dist/baseline/test-pruner.js +387 -0
- package/dist/baseline/types.d.ts +449 -0
- package/dist/baseline/types.js +5 -0
- package/dist/baseline/version.d.ts +138 -0
- package/dist/baseline/version.js +206 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/response-cache.d.ts +151 -0
- package/dist/cache/response-cache.js +287 -0
- package/dist/ci/index.d.ts +60 -0
- package/dist/ci/index.js +342 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +352 -0
- package/dist/cli/commands/badge.d.ts +3 -0
- package/dist/cli/commands/badge.js +74 -0
- package/dist/cli/commands/baseline-accept.d.ts +15 -0
- package/dist/cli/commands/baseline-accept.js +178 -0
- package/dist/cli/commands/baseline-migrate.d.ts +12 -0
- package/dist/cli/commands/baseline-migrate.js +164 -0
- package/dist/cli/commands/baseline.d.ts +14 -0
- package/dist/cli/commands/baseline.js +449 -0
- package/dist/cli/commands/beta.d.ts +10 -0
- package/dist/cli/commands/beta.js +231 -0
- package/dist/cli/commands/check.d.ts +11 -0
- package/dist/cli/commands/check.js +820 -0
- package/dist/cli/commands/cloud/badge.d.ts +3 -0
- package/dist/cli/commands/cloud/badge.js +74 -0
- package/dist/cli/commands/cloud/diff.d.ts +6 -0
- package/dist/cli/commands/cloud/diff.js +79 -0
- package/dist/cli/commands/cloud/history.d.ts +6 -0
- package/dist/cli/commands/cloud/history.js +102 -0
- package/dist/cli/commands/cloud/link.d.ts +9 -0
- package/dist/cli/commands/cloud/link.js +119 -0
- package/dist/cli/commands/cloud/login.d.ts +7 -0
- package/dist/cli/commands/cloud/login.js +499 -0
- package/dist/cli/commands/cloud/projects.d.ts +6 -0
- package/dist/cli/commands/cloud/projects.js +44 -0
- package/dist/cli/commands/cloud/shared.d.ts +7 -0
- package/dist/cli/commands/cloud/shared.js +42 -0
- package/dist/cli/commands/cloud/teams.d.ts +8 -0
- package/dist/cli/commands/cloud/teams.js +169 -0
- package/dist/cli/commands/cloud/upload.d.ts +8 -0
- package/dist/cli/commands/cloud/upload.js +181 -0
- package/dist/cli/commands/contract.d.ts +11 -0
- package/dist/cli/commands/contract.js +280 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.js +82 -0
- package/dist/cli/commands/eval.d.ts +9 -0
- package/dist/cli/commands/eval.js +187 -0
- package/dist/cli/commands/explore.d.ts +11 -0
- package/dist/cli/commands/explore.js +437 -0
- package/dist/cli/commands/feedback.d.ts +9 -0
- package/dist/cli/commands/feedback.js +174 -0
- package/dist/cli/commands/golden.d.ts +12 -0
- package/dist/cli/commands/golden.js +407 -0
- package/dist/cli/commands/history.d.ts +10 -0
- package/dist/cli/commands/history.js +202 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +219 -0
- package/dist/cli/commands/interview.d.ts +3 -0
- package/dist/cli/commands/interview.js +903 -0
- package/dist/cli/commands/link.d.ts +10 -0
- package/dist/cli/commands/link.js +169 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +499 -0
- package/dist/cli/commands/preset.d.ts +33 -0
- package/dist/cli/commands/preset.js +297 -0
- package/dist/cli/commands/profile.d.ts +33 -0
- package/dist/cli/commands/profile.js +286 -0
- package/dist/cli/commands/registry.d.ts +11 -0
- package/dist/cli/commands/registry.js +146 -0
- package/dist/cli/commands/shared.d.ts +79 -0
- package/dist/cli/commands/shared.js +196 -0
- package/dist/cli/commands/teams.d.ts +8 -0
- package/dist/cli/commands/teams.js +169 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.js +500 -0
- package/dist/cli/commands/upload.d.ts +8 -0
- package/dist/cli/commands/upload.js +223 -0
- package/dist/cli/commands/validate-config.d.ts +6 -0
- package/dist/cli/commands/validate-config.js +35 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.js +283 -0
- package/dist/cli/commands/watch.d.ts +12 -0
- package/dist/cli/commands/watch.js +253 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +178 -0
- package/dist/cli/interactive.d.ts +47 -0
- package/dist/cli/interactive.js +216 -0
- package/dist/cli/output/terminal-reporter.d.ts +19 -0
- package/dist/cli/output/terminal-reporter.js +104 -0
- package/dist/cli/output.d.ts +226 -0
- package/dist/cli/output.js +438 -0
- package/dist/cli/utils/env.d.ts +5 -0
- package/dist/cli/utils/env.js +14 -0
- package/dist/cli/utils/progress.d.ts +59 -0
- package/dist/cli/utils/progress.js +206 -0
- package/dist/cli/utils/server-context.d.ts +10 -0
- package/dist/cli/utils/server-context.js +36 -0
- package/dist/cloud/auth.d.ts +144 -0
- package/dist/cloud/auth.js +374 -0
- package/dist/cloud/client.d.ts +24 -0
- package/dist/cloud/client.js +65 -0
- package/dist/cloud/http-client.d.ts +38 -0
- package/dist/cloud/http-client.js +215 -0
- package/dist/cloud/index.d.ts +23 -0
- package/dist/cloud/index.js +25 -0
- package/dist/cloud/mock-client.d.ts +107 -0
- package/dist/cloud/mock-client.js +545 -0
- package/dist/cloud/types.d.ts +515 -0
- package/dist/cloud/types.js +15 -0
- package/dist/config/defaults.d.ts +160 -0
- package/dist/config/defaults.js +169 -0
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.js +122 -0
- package/dist/config/template.d.ts +42 -0
- package/dist/config/template.js +647 -0
- package/dist/config/validator.d.ts +2112 -0
- package/dist/config/validator.js +658 -0
- package/dist/constants/cloud.d.ts +107 -0
- package/dist/constants/cloud.js +110 -0
- package/dist/constants/core.d.ts +521 -0
- package/dist/constants/core.js +556 -0
- package/dist/constants/testing.d.ts +1283 -0
- package/dist/constants/testing.js +1568 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/contract/index.d.ts +6 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/validator.d.ts +177 -0
- package/dist/contract/validator.js +574 -0
- package/dist/cost/index.d.ts +6 -0
- package/dist/cost/index.js +5 -0
- package/dist/cost/tracker.d.ts +134 -0
- package/dist/cost/tracker.js +313 -0
- package/dist/discovery/discovery.d.ts +16 -0
- package/dist/discovery/discovery.js +173 -0
- package/dist/discovery/types.d.ts +51 -0
- package/dist/discovery/types.js +2 -0
- package/dist/docs/agents.d.ts +3 -0
- package/dist/docs/agents.js +995 -0
- package/dist/docs/contract.d.ts +51 -0
- package/dist/docs/contract.js +1681 -0
- package/dist/docs/generator.d.ts +4 -0
- package/dist/docs/generator.js +4 -0
- package/dist/docs/html-reporter.d.ts +9 -0
- package/dist/docs/html-reporter.js +757 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/junit-reporter.d.ts +18 -0
- package/dist/docs/junit-reporter.js +210 -0
- package/dist/docs/report.d.ts +14 -0
- package/dist/docs/report.js +44 -0
- package/dist/docs/sarif-reporter.d.ts +19 -0
- package/dist/docs/sarif-reporter.js +335 -0
- package/dist/docs/shared.d.ts +35 -0
- package/dist/docs/shared.js +162 -0
- package/dist/docs/templates.d.ts +12 -0
- package/dist/docs/templates.js +76 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +6 -0
- package/dist/errors/retry.d.ts +92 -0
- package/dist/errors/retry.js +323 -0
- package/dist/errors/types.d.ts +321 -0
- package/dist/errors/types.js +584 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/interview/dependency-resolver.d.ts +11 -0
- package/dist/interview/dependency-resolver.js +32 -0
- package/dist/interview/interviewer.d.ts +232 -0
- package/dist/interview/interviewer.js +1939 -0
- package/dist/interview/mock-response-generator.d.ts +7 -0
- package/dist/interview/mock-response-generator.js +102 -0
- package/dist/interview/orchestrator.d.ts +237 -0
- package/dist/interview/orchestrator.js +1296 -0
- package/dist/interview/rate-limiter.d.ts +15 -0
- package/dist/interview/rate-limiter.js +55 -0
- package/dist/interview/response-validator.d.ts +10 -0
- package/dist/interview/response-validator.js +132 -0
- package/dist/interview/schema-inferrer.d.ts +8 -0
- package/dist/interview/schema-inferrer.js +71 -0
- package/dist/interview/schema-test-generator.d.ts +71 -0
- package/dist/interview/schema-test-generator.js +834 -0
- package/dist/interview/smart-value-generator.d.ts +155 -0
- package/dist/interview/smart-value-generator.js +554 -0
- package/dist/interview/stateful-test-runner.d.ts +19 -0
- package/dist/interview/stateful-test-runner.js +106 -0
- package/dist/interview/types.d.ts +561 -0
- package/dist/interview/types.js +2 -0
- package/dist/llm/anthropic.d.ts +41 -0
- package/dist/llm/anthropic.js +355 -0
- package/dist/llm/client.d.ts +123 -0
- package/dist/llm/client.js +42 -0
- package/dist/llm/factory.d.ts +38 -0
- package/dist/llm/factory.js +145 -0
- package/dist/llm/fallback.d.ts +140 -0
- package/dist/llm/fallback.js +379 -0
- package/dist/llm/index.d.ts +18 -0
- package/dist/llm/index.js +15 -0
- package/dist/llm/ollama.d.ts +37 -0
- package/dist/llm/ollama.js +330 -0
- package/dist/llm/openai.d.ts +25 -0
- package/dist/llm/openai.js +320 -0
- package/dist/llm/token-budget.d.ts +161 -0
- package/dist/llm/token-budget.js +395 -0
- package/dist/logging/logger.d.ts +70 -0
- package/dist/logging/logger.js +130 -0
- package/dist/metrics/collector.d.ts +106 -0
- package/dist/metrics/collector.js +547 -0
- package/dist/metrics/index.d.ts +7 -0
- package/dist/metrics/index.js +7 -0
- package/dist/metrics/prometheus.d.ts +20 -0
- package/dist/metrics/prometheus.js +241 -0
- package/dist/metrics/types.d.ts +209 -0
- package/dist/metrics/types.js +5 -0
- package/dist/persona/builtins.d.ts +54 -0
- package/dist/persona/builtins.js +219 -0
- package/dist/persona/index.d.ts +8 -0
- package/dist/persona/index.js +8 -0
- package/dist/persona/loader.d.ts +30 -0
- package/dist/persona/loader.js +190 -0
- package/dist/persona/types.d.ts +144 -0
- package/dist/persona/types.js +5 -0
- package/dist/persona/validation.d.ts +94 -0
- package/dist/persona/validation.js +332 -0
- package/dist/prompts/index.d.ts +5 -0
- package/dist/prompts/index.js +5 -0
- package/dist/prompts/templates.d.ts +180 -0
- package/dist/prompts/templates.js +431 -0
- package/dist/registry/client.d.ts +49 -0
- package/dist/registry/client.js +191 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +6 -0
- package/dist/registry/types.d.ts +140 -0
- package/dist/registry/types.js +6 -0
- package/dist/scenarios/evaluator.d.ts +43 -0
- package/dist/scenarios/evaluator.js +206 -0
- package/dist/scenarios/index.d.ts +10 -0
- package/dist/scenarios/index.js +9 -0
- package/dist/scenarios/loader.d.ts +20 -0
- package/dist/scenarios/loader.js +285 -0
- package/dist/scenarios/types.d.ts +153 -0
- package/dist/scenarios/types.js +8 -0
- package/dist/security/index.d.ts +17 -0
- package/dist/security/index.js +18 -0
- package/dist/security/payloads.d.ts +61 -0
- package/dist/security/payloads.js +268 -0
- package/dist/security/security-tester.d.ts +42 -0
- package/dist/security/security-tester.js +582 -0
- package/dist/security/types.d.ts +166 -0
- package/dist/security/types.js +8 -0
- package/dist/transport/base-transport.d.ts +59 -0
- package/dist/transport/base-transport.js +38 -0
- package/dist/transport/http-transport.d.ts +67 -0
- package/dist/transport/http-transport.js +238 -0
- package/dist/transport/mcp-client.d.ts +141 -0
- package/dist/transport/mcp-client.js +496 -0
- package/dist/transport/sse-transport.d.ts +88 -0
- package/dist/transport/sse-transport.js +316 -0
- package/dist/transport/stdio-transport.d.ts +43 -0
- package/dist/transport/stdio-transport.js +238 -0
- package/dist/transport/types.d.ts +125 -0
- package/dist/transport/types.js +16 -0
- package/dist/utils/concurrency.d.ts +123 -0
- package/dist/utils/concurrency.js +213 -0
- package/dist/utils/formatters.d.ts +16 -0
- package/dist/utils/formatters.js +37 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jsonpath.d.ts +87 -0
- package/dist/utils/jsonpath.js +326 -0
- package/dist/utils/markdown.d.ts +113 -0
- package/dist/utils/markdown.js +265 -0
- package/dist/utils/network.d.ts +14 -0
- package/dist/utils/network.js +17 -0
- package/dist/utils/sanitize.d.ts +92 -0
- package/dist/utils/sanitize.js +191 -0
- package/dist/utils/semantic.d.ts +194 -0
- package/dist/utils/semantic.js +1051 -0
- package/dist/utils/smart-truncate.d.ts +94 -0
- package/dist/utils/smart-truncate.js +361 -0
- package/dist/utils/timeout.d.ts +153 -0
- package/dist/utils/timeout.js +205 -0
- package/dist/utils/yaml-parser.d.ts +58 -0
- package/dist/utils/yaml-parser.js +86 -0
- package/dist/validation/index.d.ts +32 -0
- package/dist/validation/index.js +32 -0
- package/dist/validation/semantic-test-generator.d.ts +50 -0
- package/dist/validation/semantic-test-generator.js +176 -0
- package/dist/validation/semantic-types.d.ts +66 -0
- package/dist/validation/semantic-types.js +94 -0
- package/dist/validation/semantic-validator.d.ts +38 -0
- package/dist/validation/semantic-validator.js +340 -0
- package/dist/verification/index.d.ts +6 -0
- package/dist/verification/index.js +5 -0
- package/dist/verification/types.d.ts +133 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/verifier.d.ts +30 -0
- package/dist/verification/verifier.js +309 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +48 -0
- package/dist/workflow/auto-generator.d.ts +27 -0
- package/dist/workflow/auto-generator.js +513 -0
- package/dist/workflow/discovery.d.ts +40 -0
- package/dist/workflow/discovery.js +195 -0
- package/dist/workflow/executor.d.ts +82 -0
- package/dist/workflow/executor.js +611 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/loader.d.ts +24 -0
- package/dist/workflow/loader.js +194 -0
- package/dist/workflow/state-tracker.d.ts +98 -0
- package/dist/workflow/state-tracker.js +424 -0
- package/dist/workflow/types.d.ts +337 -0
- package/dist/workflow/types.js +5 -0
- package/package.json +94 -0
- package/schemas/bellwether-check.schema.json +651 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced GitHub PR comment generation for schema changes.
|
|
3
|
+
*
|
|
4
|
+
* This module generates detailed, actionable PR comments that help reviewers
|
|
5
|
+
* understand the impact of schema changes on downstream consumers.
|
|
6
|
+
*/
|
|
7
|
+
import { PR_COMMENTS } from '../constants.js';
|
|
8
|
+
/**
|
|
9
|
+
* Get badge color for severity level.
|
|
10
|
+
*/
|
|
11
|
+
export function getBadgeColor(severity) {
|
|
12
|
+
return PR_COMMENTS.BADGE_COLORS[severity];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate a shields.io badge URL.
|
|
16
|
+
*/
|
|
17
|
+
export function generateBadgeUrl(label, message, color) {
|
|
18
|
+
const encodedLabel = encodeURIComponent(label);
|
|
19
|
+
const encodedMessage = encodeURIComponent(message);
|
|
20
|
+
return `https://img.shields.io/badge/${encodedLabel}-${encodedMessage}-${color}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate a markdown badge.
|
|
24
|
+
*/
|
|
25
|
+
export function generateBadgeMarkdown(label, message, color) {
|
|
26
|
+
const url = generateBadgeUrl(label, message, color);
|
|
27
|
+
return ``;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate the summary section.
|
|
31
|
+
*/
|
|
32
|
+
function generateSummarySection(diff) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
// High-level stats
|
|
35
|
+
const stats = [
|
|
36
|
+
diff.toolsAdded.length > 0 ? `**${diff.toolsAdded.length}** tools added` : null,
|
|
37
|
+
diff.toolsRemoved.length > 0 ? `**${diff.toolsRemoved.length}** tools removed` : null,
|
|
38
|
+
diff.toolsModified.length > 0 ? `**${diff.toolsModified.length}** tools modified` : null,
|
|
39
|
+
].filter(Boolean);
|
|
40
|
+
if (stats.length > 0) {
|
|
41
|
+
lines.push(stats.join(' | '));
|
|
42
|
+
lines.push('');
|
|
43
|
+
}
|
|
44
|
+
// Change counts by severity
|
|
45
|
+
if (diff.breakingCount > 0 || diff.warningCount > 0 || diff.infoCount > 0) {
|
|
46
|
+
const counts = [];
|
|
47
|
+
if (diff.breakingCount > 0)
|
|
48
|
+
counts.push(`🔴 ${diff.breakingCount} breaking`);
|
|
49
|
+
if (diff.warningCount > 0)
|
|
50
|
+
counts.push(`🟠 ${diff.warningCount} warnings`);
|
|
51
|
+
if (diff.infoCount > 0)
|
|
52
|
+
counts.push(`🔵 ${diff.infoCount} info`);
|
|
53
|
+
lines.push(counts.join(' | '));
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
title: 'Summary',
|
|
57
|
+
content: lines.join('\n'),
|
|
58
|
+
priority: 1,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate breaking changes section.
|
|
63
|
+
*/
|
|
64
|
+
function generateBreakingChangesSection(diff, config) {
|
|
65
|
+
const breakingChanges = diff.behaviorChanges.filter(c => c.severity === 'breaking');
|
|
66
|
+
if (breakingChanges.length === 0) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const lines = ['> ⚠️ **These changes may break existing integrations**', ''];
|
|
70
|
+
// Group by tool
|
|
71
|
+
const byTool = new Map();
|
|
72
|
+
for (const change of breakingChanges) {
|
|
73
|
+
const existing = byTool.get(change.tool) || [];
|
|
74
|
+
existing.push(change);
|
|
75
|
+
byTool.set(change.tool, existing);
|
|
76
|
+
}
|
|
77
|
+
let toolCount = 0;
|
|
78
|
+
for (const [toolName, changes] of byTool) {
|
|
79
|
+
if (toolCount >= (config.maxDetailedTools ?? PR_COMMENTS.MAX_DETAILED_TOOLS)) {
|
|
80
|
+
lines.push(`\n*...and ${byTool.size - toolCount} more tools with breaking changes*`);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
lines.push(`### \`${toolName}\``);
|
|
84
|
+
lines.push('');
|
|
85
|
+
const displayChanges = changes.slice(0, config.maxChangesPerTool ?? PR_COMMENTS.MAX_CHANGES_PER_TOOL);
|
|
86
|
+
for (const change of displayChanges) {
|
|
87
|
+
lines.push(`- **${formatAspect(change.aspect)}**: ${change.description}`);
|
|
88
|
+
if (change.before && change.after) {
|
|
89
|
+
lines.push(` - Before: \`${truncate(change.before)}\``);
|
|
90
|
+
lines.push(` - After: \`${truncate(change.after)}\``);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (changes.length > displayChanges.length) {
|
|
94
|
+
lines.push(` - *...and ${changes.length - displayChanges.length} more changes*`);
|
|
95
|
+
}
|
|
96
|
+
lines.push('');
|
|
97
|
+
toolCount++;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
title: '🔴 Breaking Changes',
|
|
101
|
+
content: lines.join('\n'),
|
|
102
|
+
priority: 2,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Generate tools added section.
|
|
107
|
+
*/
|
|
108
|
+
function generateToolsAddedSection(diff) {
|
|
109
|
+
if (diff.toolsAdded.length === 0) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const lines = [];
|
|
113
|
+
for (const tool of diff.toolsAdded.slice(0, PR_COMMENTS.MAX_DETAILED_TOOLS)) {
|
|
114
|
+
lines.push(`- \`${tool}\``);
|
|
115
|
+
}
|
|
116
|
+
if (diff.toolsAdded.length > PR_COMMENTS.MAX_DETAILED_TOOLS) {
|
|
117
|
+
lines.push(`- *...and ${diff.toolsAdded.length - PR_COMMENTS.MAX_DETAILED_TOOLS} more*`);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
title: '✅ Tools Added',
|
|
121
|
+
content: lines.join('\n'),
|
|
122
|
+
priority: 4,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Generate tools removed section.
|
|
127
|
+
*/
|
|
128
|
+
function generateToolsRemovedSection(diff) {
|
|
129
|
+
if (diff.toolsRemoved.length === 0) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const lines = [
|
|
133
|
+
'> ⚠️ **Removing tools is a breaking change for consumers**',
|
|
134
|
+
'',
|
|
135
|
+
];
|
|
136
|
+
for (const tool of diff.toolsRemoved) {
|
|
137
|
+
lines.push(`- ~~\`${tool}\`~~`);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
title: '❌ Tools Removed',
|
|
141
|
+
content: lines.join('\n'),
|
|
142
|
+
priority: 3,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Generate modified tools section.
|
|
147
|
+
*/
|
|
148
|
+
function generateModifiedToolsSection(diff, config) {
|
|
149
|
+
// Filter out breaking changes (shown separately)
|
|
150
|
+
const nonBreakingModified = diff.toolsModified.filter(t => !t.changes.some(c => c.severity === 'breaking'));
|
|
151
|
+
if (nonBreakingModified.length === 0) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const lines = [];
|
|
155
|
+
const maxTools = config.maxDetailedTools ?? PR_COMMENTS.MAX_DETAILED_TOOLS;
|
|
156
|
+
for (const toolDiff of nonBreakingModified.slice(0, maxTools)) {
|
|
157
|
+
lines.push(`### \`${toolDiff.tool}\``);
|
|
158
|
+
lines.push('');
|
|
159
|
+
const changes = toolDiff.changes.slice(0, config.maxChangesPerTool ?? PR_COMMENTS.MAX_CHANGES_PER_TOOL);
|
|
160
|
+
for (const change of changes) {
|
|
161
|
+
const icon = change.severity === 'warning' ? '🟠' : '🔵';
|
|
162
|
+
lines.push(`- ${icon} **${formatAspect(change.aspect)}**: ${change.description}`);
|
|
163
|
+
}
|
|
164
|
+
if (toolDiff.changes.length > changes.length) {
|
|
165
|
+
lines.push(`- *...and ${toolDiff.changes.length - changes.length} more changes*`);
|
|
166
|
+
}
|
|
167
|
+
lines.push('');
|
|
168
|
+
}
|
|
169
|
+
if (nonBreakingModified.length > maxTools) {
|
|
170
|
+
lines.push(`*...and ${nonBreakingModified.length - maxTools} more modified tools*`);
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
title: '📝 Tools Modified',
|
|
174
|
+
content: lines.join('\n'),
|
|
175
|
+
priority: 5,
|
|
176
|
+
collapsed: nonBreakingModified.length > 3,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Generate migration guide section.
|
|
181
|
+
*/
|
|
182
|
+
function generateMigrationSection(guide, config) {
|
|
183
|
+
if (!guide || guide.steps.length === 0 || !config.includeMigrationExamples) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const lines = [
|
|
187
|
+
`**Estimated effort**: ${guide.estimatedEffort.toUpperCase()}`,
|
|
188
|
+
`**Breaking changes**: ${guide.stats.breakingChangesCount}`,
|
|
189
|
+
'',
|
|
190
|
+
'### Steps',
|
|
191
|
+
'',
|
|
192
|
+
];
|
|
193
|
+
const maxSteps = config.maxMigrationExamples ?? PR_COMMENTS.MAX_MIGRATION_EXAMPLES;
|
|
194
|
+
const steps = guide.steps.slice(0, maxSteps);
|
|
195
|
+
for (let i = 0; i < steps.length; i++) {
|
|
196
|
+
const step = steps[i];
|
|
197
|
+
lines.push(`${i + 1}. **${step.title}**`);
|
|
198
|
+
lines.push(` - Tool: \`${step.toolName}\``);
|
|
199
|
+
if (step.codeExamples && step.codeExamples.length > 0) {
|
|
200
|
+
const example = step.codeExamples[0];
|
|
201
|
+
lines.push('');
|
|
202
|
+
lines.push(' ```' + (example.language || ''));
|
|
203
|
+
lines.push(' // Before:');
|
|
204
|
+
lines.push(' ' + example.before.split('\n').join('\n '));
|
|
205
|
+
lines.push('');
|
|
206
|
+
lines.push(' // After:');
|
|
207
|
+
lines.push(' ' + example.after.split('\n').join('\n '));
|
|
208
|
+
lines.push(' ```');
|
|
209
|
+
}
|
|
210
|
+
lines.push('');
|
|
211
|
+
}
|
|
212
|
+
if (guide.steps.length > maxSteps) {
|
|
213
|
+
lines.push(`*...and ${guide.steps.length - maxSteps} more migration steps*`);
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
title: '🔄 Migration Guide',
|
|
217
|
+
content: lines.join('\n'),
|
|
218
|
+
priority: 6,
|
|
219
|
+
collapsed: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Generate affected workflows section.
|
|
224
|
+
*/
|
|
225
|
+
function generateAffectedWorkflowsSection(workflows, config) {
|
|
226
|
+
if (!workflows || workflows.length === 0) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const lines = [];
|
|
230
|
+
const maxWorkflows = config.maxAffectedWorkflows ?? PR_COMMENTS.MAX_AFFECTED_WORKFLOWS;
|
|
231
|
+
for (const workflow of workflows.slice(0, maxWorkflows)) {
|
|
232
|
+
const icon = workflow.severity === 'breaking' ? '🔴' : workflow.severity === 'warning' ? '🟠' : '🔵';
|
|
233
|
+
lines.push(`- ${icon} **${workflow.name}**: ${workflow.description}`);
|
|
234
|
+
lines.push(` - Tools: ${workflow.affectedTools.map(t => `\`${t}\``).join(', ')}`);
|
|
235
|
+
}
|
|
236
|
+
if (workflows.length > maxWorkflows) {
|
|
237
|
+
lines.push(`\n*...and ${workflows.length - maxWorkflows} more affected workflows*`);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
title: '⚡ Affected Workflows',
|
|
241
|
+
content: lines.join('\n'),
|
|
242
|
+
priority: 7,
|
|
243
|
+
collapsed: true,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Generate action items based on changes.
|
|
248
|
+
*/
|
|
249
|
+
function generateActionItems(diff) {
|
|
250
|
+
const items = [];
|
|
251
|
+
if (diff.toolsRemoved.length > 0) {
|
|
252
|
+
items.push('[ ] Update consumer applications to handle removed tools');
|
|
253
|
+
}
|
|
254
|
+
if (diff.breakingCount > 0) {
|
|
255
|
+
items.push('[ ] Review breaking changes with team before merging');
|
|
256
|
+
items.push('[ ] Update documentation for changed tool schemas');
|
|
257
|
+
}
|
|
258
|
+
const schemaChanges = diff.toolsModified.filter(t => t.schemaChanged);
|
|
259
|
+
if (schemaChanges.length > 0) {
|
|
260
|
+
items.push('[ ] Verify client SDKs are updated for schema changes');
|
|
261
|
+
}
|
|
262
|
+
if (diff.toolsAdded.length > 0) {
|
|
263
|
+
items.push('[ ] Add tests for new tools');
|
|
264
|
+
items.push('[ ] Update API documentation');
|
|
265
|
+
}
|
|
266
|
+
// Add testing recommendations
|
|
267
|
+
if (diff.severity === 'breaking' || diff.severity === 'warning') {
|
|
268
|
+
items.push('[ ] Run integration tests with downstream consumers');
|
|
269
|
+
}
|
|
270
|
+
return items;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Format a behavior aspect for display.
|
|
274
|
+
*/
|
|
275
|
+
function formatAspect(aspect) {
|
|
276
|
+
return aspect
|
|
277
|
+
.replace(/_/g, ' ')
|
|
278
|
+
.replace(/\b\w/g, c => c.toUpperCase());
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Truncate a string for display.
|
|
282
|
+
*/
|
|
283
|
+
function truncate(value, maxLength = PR_COMMENTS.VALUE_TRUNCATE_LENGTH) {
|
|
284
|
+
if (value.length <= maxLength)
|
|
285
|
+
return value;
|
|
286
|
+
return value.substring(0, maxLength - 3) + '...';
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Render a collapsible section.
|
|
290
|
+
*/
|
|
291
|
+
function renderCollapsibleSection(title, content) {
|
|
292
|
+
return `<details>\n<summary>${title}</summary>\n\n${content}\n\n</details>`;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Generate footer with metadata.
|
|
296
|
+
*/
|
|
297
|
+
function generateFooter(config) {
|
|
298
|
+
const lines = ['---'];
|
|
299
|
+
const parts = [];
|
|
300
|
+
if (config.baseBranch && config.headBranch) {
|
|
301
|
+
parts.push(`Comparing \`${config.baseBranch}\` → \`${config.headBranch}\``);
|
|
302
|
+
}
|
|
303
|
+
parts.push('Generated by [Bellwether](https://bellwether.sh)');
|
|
304
|
+
lines.push(parts.join(' | '));
|
|
305
|
+
return lines.join('\n');
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Generate a complete PR comment for a behavioral diff.
|
|
309
|
+
*/
|
|
310
|
+
export function generatePRComment(diff, config = {}, migrationGuide, affectedWorkflows) {
|
|
311
|
+
const { includeMigrationExamples = true, useCollapsibleSections = true, } = config;
|
|
312
|
+
// Determine badge
|
|
313
|
+
const badgeLabel = 'Schema Drift';
|
|
314
|
+
let badgeMessage;
|
|
315
|
+
let badgeColor;
|
|
316
|
+
if (diff.severity === 'breaking') {
|
|
317
|
+
badgeMessage = `${diff.breakingCount} breaking`;
|
|
318
|
+
badgeColor = 'red';
|
|
319
|
+
}
|
|
320
|
+
else if (diff.severity === 'warning') {
|
|
321
|
+
badgeMessage = `${diff.warningCount} warnings`;
|
|
322
|
+
badgeColor = 'orange';
|
|
323
|
+
}
|
|
324
|
+
else if (diff.severity === 'info') {
|
|
325
|
+
badgeMessage = 'changes detected';
|
|
326
|
+
badgeColor = 'blue';
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
badgeMessage = 'no changes';
|
|
330
|
+
badgeColor = 'green';
|
|
331
|
+
}
|
|
332
|
+
// Generate sections
|
|
333
|
+
const sections = [];
|
|
334
|
+
sections.push(generateSummarySection(diff));
|
|
335
|
+
const breakingSection = generateBreakingChangesSection(diff, config);
|
|
336
|
+
if (breakingSection)
|
|
337
|
+
sections.push(breakingSection);
|
|
338
|
+
const removedSection = generateToolsRemovedSection(diff);
|
|
339
|
+
if (removedSection)
|
|
340
|
+
sections.push(removedSection);
|
|
341
|
+
const addedSection = generateToolsAddedSection(diff);
|
|
342
|
+
if (addedSection)
|
|
343
|
+
sections.push(addedSection);
|
|
344
|
+
const modifiedSection = generateModifiedToolsSection(diff, config);
|
|
345
|
+
if (modifiedSection)
|
|
346
|
+
sections.push(modifiedSection);
|
|
347
|
+
if (includeMigrationExamples) {
|
|
348
|
+
const migrationSection = generateMigrationSection(migrationGuide, config);
|
|
349
|
+
if (migrationSection)
|
|
350
|
+
sections.push(migrationSection);
|
|
351
|
+
}
|
|
352
|
+
const workflowsSection = generateAffectedWorkflowsSection(affectedWorkflows || [], config);
|
|
353
|
+
if (workflowsSection)
|
|
354
|
+
sections.push(workflowsSection);
|
|
355
|
+
// Sort by priority
|
|
356
|
+
sections.sort((a, b) => a.priority - b.priority);
|
|
357
|
+
// Generate action items
|
|
358
|
+
const actionItems = generateActionItems(diff);
|
|
359
|
+
// Generate footer
|
|
360
|
+
const footer = generateFooter(config);
|
|
361
|
+
// Generate title
|
|
362
|
+
const title = diff.severity === 'none'
|
|
363
|
+
? '✅ No Schema Drift Detected'
|
|
364
|
+
: diff.severity === 'breaking'
|
|
365
|
+
? '🚨 Breaking Schema Changes Detected'
|
|
366
|
+
: diff.severity === 'warning'
|
|
367
|
+
? '⚠️ Schema Changes Detected'
|
|
368
|
+
: 'ℹ️ Minor Schema Changes';
|
|
369
|
+
// Render full markdown
|
|
370
|
+
const markdownLines = [
|
|
371
|
+
`## ${title}`,
|
|
372
|
+
'',
|
|
373
|
+
generateBadgeMarkdown(badgeLabel, badgeMessage, badgeColor),
|
|
374
|
+
'',
|
|
375
|
+
];
|
|
376
|
+
// Add sections
|
|
377
|
+
for (const section of sections) {
|
|
378
|
+
if (useCollapsibleSections && section.collapsed) {
|
|
379
|
+
markdownLines.push(renderCollapsibleSection(`### ${section.title}`, section.content));
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
markdownLines.push(`### ${section.title}`);
|
|
383
|
+
markdownLines.push('');
|
|
384
|
+
markdownLines.push(section.content);
|
|
385
|
+
}
|
|
386
|
+
markdownLines.push('');
|
|
387
|
+
}
|
|
388
|
+
// Add action items
|
|
389
|
+
if (actionItems.length > 0) {
|
|
390
|
+
markdownLines.push('### 📋 Action Items');
|
|
391
|
+
markdownLines.push('');
|
|
392
|
+
for (const item of actionItems) {
|
|
393
|
+
markdownLines.push(`- ${item}`);
|
|
394
|
+
}
|
|
395
|
+
markdownLines.push('');
|
|
396
|
+
}
|
|
397
|
+
// Add footer
|
|
398
|
+
markdownLines.push(footer);
|
|
399
|
+
return {
|
|
400
|
+
title,
|
|
401
|
+
summary: diff.summary,
|
|
402
|
+
badge: {
|
|
403
|
+
label: badgeLabel,
|
|
404
|
+
color: badgeColor,
|
|
405
|
+
message: badgeMessage,
|
|
406
|
+
},
|
|
407
|
+
sections,
|
|
408
|
+
actionItems,
|
|
409
|
+
footer,
|
|
410
|
+
markdown: markdownLines.join('\n'),
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Generate a compact PR comment for simple diffs.
|
|
415
|
+
*/
|
|
416
|
+
export function generateCompactPRComment(diff) {
|
|
417
|
+
if (diff.severity === 'none') {
|
|
418
|
+
return '✅ **No schema drift detected** - baseline matches current server state.';
|
|
419
|
+
}
|
|
420
|
+
const lines = [];
|
|
421
|
+
// Header with badge
|
|
422
|
+
const badgeColor = getBadgeColor(diff.severity);
|
|
423
|
+
const badgeMessage = diff.severity === 'breaking'
|
|
424
|
+
? `${diff.breakingCount} breaking`
|
|
425
|
+
: diff.severity === 'warning'
|
|
426
|
+
? `${diff.warningCount} warnings`
|
|
427
|
+
: 'changes detected';
|
|
428
|
+
lines.push(`## Schema Drift Report ${generateBadgeMarkdown('drift', badgeMessage, badgeColor)}`);
|
|
429
|
+
lines.push('');
|
|
430
|
+
// Quick stats
|
|
431
|
+
const stats = [];
|
|
432
|
+
if (diff.toolsAdded.length > 0)
|
|
433
|
+
stats.push(`+${diff.toolsAdded.length} added`);
|
|
434
|
+
if (diff.toolsRemoved.length > 0)
|
|
435
|
+
stats.push(`-${diff.toolsRemoved.length} removed`);
|
|
436
|
+
if (diff.toolsModified.length > 0)
|
|
437
|
+
stats.push(`~${diff.toolsModified.length} modified`);
|
|
438
|
+
if (stats.length > 0) {
|
|
439
|
+
lines.push(`**Tools**: ${stats.join(', ')}`);
|
|
440
|
+
}
|
|
441
|
+
// Breaking changes summary
|
|
442
|
+
if (diff.breakingCount > 0) {
|
|
443
|
+
lines.push('');
|
|
444
|
+
lines.push('**Breaking changes:**');
|
|
445
|
+
const breakingChanges = diff.behaviorChanges
|
|
446
|
+
.filter(c => c.severity === 'breaking')
|
|
447
|
+
.slice(0, 5);
|
|
448
|
+
for (const change of breakingChanges) {
|
|
449
|
+
lines.push(`- \`${change.tool}\`: ${change.description}`);
|
|
450
|
+
}
|
|
451
|
+
if (diff.breakingCount > 5) {
|
|
452
|
+
lines.push(`- *...and ${diff.breakingCount - 5} more*`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
lines.push('');
|
|
456
|
+
lines.push('---');
|
|
457
|
+
lines.push('*Generated by [Bellwether](https://bellwether.sh)*');
|
|
458
|
+
return lines.join('\n');
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Generate a status check summary for CI.
|
|
462
|
+
*/
|
|
463
|
+
export function generateCIStatusSummary(diff) {
|
|
464
|
+
if (diff.severity === 'breaking') {
|
|
465
|
+
return {
|
|
466
|
+
conclusion: 'failure',
|
|
467
|
+
title: `${diff.breakingCount} breaking change(s) detected`,
|
|
468
|
+
summary: `Schema drift analysis found ${diff.breakingCount} breaking changes that may affect consumers.`,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (diff.severity === 'warning') {
|
|
472
|
+
return {
|
|
473
|
+
conclusion: 'neutral',
|
|
474
|
+
title: `${diff.warningCount} warning(s) detected`,
|
|
475
|
+
summary: `Schema drift analysis found ${diff.warningCount} changes that should be reviewed.`,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
if (diff.severity === 'info') {
|
|
479
|
+
return {
|
|
480
|
+
conclusion: 'success',
|
|
481
|
+
title: 'Minor changes detected',
|
|
482
|
+
summary: `Schema drift analysis found ${diff.infoCount} informational change(s).`,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
conclusion: 'success',
|
|
487
|
+
title: 'No drift detected',
|
|
488
|
+
summary: 'Schema matches the baseline - no changes detected.',
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Generate diff visualization as a table.
|
|
493
|
+
*/
|
|
494
|
+
export function generateDiffTable(toolDiffs) {
|
|
495
|
+
if (toolDiffs.length === 0) {
|
|
496
|
+
return '*No tool modifications*';
|
|
497
|
+
}
|
|
498
|
+
const lines = [
|
|
499
|
+
'| Tool | Changes | Schema | Description |',
|
|
500
|
+
'|------|---------|--------|-------------|',
|
|
501
|
+
];
|
|
502
|
+
for (const toolDiff of toolDiffs.slice(0, PR_COMMENTS.MAX_DETAILED_TOOLS)) {
|
|
503
|
+
const breakingCount = toolDiff.changes.filter(c => c.severity === 'breaking').length;
|
|
504
|
+
const warningCount = toolDiff.changes.filter(c => c.severity === 'warning').length;
|
|
505
|
+
const infoCount = toolDiff.changes.filter(c => c.severity === 'info').length;
|
|
506
|
+
const changeIndicators = [];
|
|
507
|
+
if (breakingCount > 0)
|
|
508
|
+
changeIndicators.push(`🔴${breakingCount}`);
|
|
509
|
+
if (warningCount > 0)
|
|
510
|
+
changeIndicators.push(`🟠${warningCount}`);
|
|
511
|
+
if (infoCount > 0)
|
|
512
|
+
changeIndicators.push(`🔵${infoCount}`);
|
|
513
|
+
const schemaStatus = toolDiff.schemaChanged ? '⚠️ Changed' : '✅ OK';
|
|
514
|
+
const descStatus = toolDiff.descriptionChanged ? '⚠️ Changed' : '✅ OK';
|
|
515
|
+
lines.push(`| \`${toolDiff.tool}\` | ${changeIndicators.join(' ') || '—'} | ${schemaStatus} | ${descStatus} |`);
|
|
516
|
+
}
|
|
517
|
+
if (toolDiffs.length > PR_COMMENTS.MAX_DETAILED_TOOLS) {
|
|
518
|
+
lines.push(`| *...and ${toolDiffs.length - PR_COMMENTS.MAX_DETAILED_TOOLS} more* | | | |`);
|
|
519
|
+
}
|
|
520
|
+
return lines.join('\n');
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Determine if a PR comment should block merge.
|
|
524
|
+
*/
|
|
525
|
+
export function shouldBlockMerge(diff, strictMode = true) {
|
|
526
|
+
if (strictMode) {
|
|
527
|
+
return diff.severity === 'breaking';
|
|
528
|
+
}
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get emoji for severity level.
|
|
533
|
+
*/
|
|
534
|
+
export function getSeverityEmoji(severity) {
|
|
535
|
+
switch (severity) {
|
|
536
|
+
case 'breaking':
|
|
537
|
+
return '🔴';
|
|
538
|
+
case 'warning':
|
|
539
|
+
return '🟠';
|
|
540
|
+
case 'info':
|
|
541
|
+
return '🔵';
|
|
542
|
+
case 'none':
|
|
543
|
+
return '✅';
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
//# sourceMappingURL=pr-comment-generator.js.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response fingerprinting for structural drift detection.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes MCP tool responses to create deterministic fingerprints
|
|
5
|
+
* that capture response structure, shape, and characteristics without
|
|
6
|
+
* requiring LLM analysis.
|
|
7
|
+
*/
|
|
8
|
+
import type { MCPToolCallResult } from '../transport/types.js';
|
|
9
|
+
import type { EnhancedErrorAnalysis } from './error-analyzer.js';
|
|
10
|
+
/**
|
|
11
|
+
* Content type classification for responses.
|
|
12
|
+
*/
|
|
13
|
+
export type ResponseContentType = 'text' | 'object' | 'array' | 'primitive' | 'empty' | 'error' | 'mixed' | 'binary';
|
|
14
|
+
/**
|
|
15
|
+
* Size classification for responses.
|
|
16
|
+
*/
|
|
17
|
+
export type ResponseSize = 'tiny' | 'small' | 'medium' | 'large';
|
|
18
|
+
/**
|
|
19
|
+
* Fingerprint of a tool's response structure.
|
|
20
|
+
*/
|
|
21
|
+
export interface ResponseFingerprint {
|
|
22
|
+
/** Hash of the response structure (keys, types, nesting) */
|
|
23
|
+
structureHash: string;
|
|
24
|
+
/** Primary content type of the response */
|
|
25
|
+
contentType: ResponseContentType;
|
|
26
|
+
/** Top-level field names if response is an object */
|
|
27
|
+
fields?: string[];
|
|
28
|
+
/** Structure hash of array items (if array response) */
|
|
29
|
+
arrayItemStructure?: string;
|
|
30
|
+
/** Approximate response size category */
|
|
31
|
+
size: ResponseSize;
|
|
32
|
+
/** Whether the response is empty/has no meaningful content */
|
|
33
|
+
isEmpty: boolean;
|
|
34
|
+
/** Number of successful responses used to build this fingerprint */
|
|
35
|
+
sampleCount: number;
|
|
36
|
+
/** Confidence score (0-1) based on response consistency */
|
|
37
|
+
confidence: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Inferred JSON schema from response samples.
|
|
41
|
+
*/
|
|
42
|
+
export interface InferredSchema {
|
|
43
|
+
type: string;
|
|
44
|
+
properties?: Record<string, InferredSchema>;
|
|
45
|
+
items?: InferredSchema;
|
|
46
|
+
required?: string[];
|
|
47
|
+
nullable?: boolean;
|
|
48
|
+
enum?: unknown[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalized error pattern for drift detection.
|
|
52
|
+
*/
|
|
53
|
+
export interface ErrorPattern {
|
|
54
|
+
/** Normalized error category */
|
|
55
|
+
category: 'validation' | 'not_found' | 'permission' | 'timeout' | 'internal' | 'unknown';
|
|
56
|
+
/** Pattern hash for comparison */
|
|
57
|
+
patternHash: string;
|
|
58
|
+
/** Example error message (first occurrence) */
|
|
59
|
+
example: string;
|
|
60
|
+
/** Count of occurrences */
|
|
61
|
+
count: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Result of analyzing multiple tool responses.
|
|
65
|
+
*/
|
|
66
|
+
export interface ResponseAnalysis {
|
|
67
|
+
/** Aggregated response fingerprint */
|
|
68
|
+
fingerprint: ResponseFingerprint;
|
|
69
|
+
/** Inferred output schema from successful responses */
|
|
70
|
+
inferredSchema?: InferredSchema;
|
|
71
|
+
/** Error patterns observed */
|
|
72
|
+
errorPatterns: ErrorPattern[];
|
|
73
|
+
/** Enhanced error analyses with root cause and remediation */
|
|
74
|
+
enhancedErrorAnalyses?: EnhancedErrorAnalysis[];
|
|
75
|
+
/** Whether responses were consistent across samples */
|
|
76
|
+
isConsistent: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Analyze multiple tool responses to create a comprehensive fingerprint.
|
|
80
|
+
*/
|
|
81
|
+
export declare function analyzeResponses(responses: Array<{
|
|
82
|
+
response: MCPToolCallResult | null;
|
|
83
|
+
error: string | null;
|
|
84
|
+
}>): ResponseAnalysis;
|
|
85
|
+
/**
|
|
86
|
+
* Infer a JSON schema from a sample value.
|
|
87
|
+
*/
|
|
88
|
+
export declare function inferSchemaFromValue(value: unknown): InferredSchema;
|
|
89
|
+
/**
|
|
90
|
+
* Compare two response fingerprints and return differences.
|
|
91
|
+
*/
|
|
92
|
+
export interface FingerprintDiff {
|
|
93
|
+
/** Whether the fingerprints are identical */
|
|
94
|
+
identical: boolean;
|
|
95
|
+
/** List of changes detected */
|
|
96
|
+
changes: FingerprintChange[];
|
|
97
|
+
/** Overall significance of changes */
|
|
98
|
+
significance: 'none' | 'low' | 'medium' | 'high';
|
|
99
|
+
}
|
|
100
|
+
export interface FingerprintChange {
|
|
101
|
+
aspect: 'structure' | 'content_type' | 'fields' | 'array_items' | 'size' | 'emptiness';
|
|
102
|
+
description: string;
|
|
103
|
+
before: string;
|
|
104
|
+
after: string;
|
|
105
|
+
breaking: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Compare two response fingerprints.
|
|
109
|
+
*/
|
|
110
|
+
export declare function compareFingerprints(previous: ResponseFingerprint | undefined, current: ResponseFingerprint | undefined): FingerprintDiff;
|
|
111
|
+
/**
|
|
112
|
+
* Compare error patterns between baselines.
|
|
113
|
+
*/
|
|
114
|
+
export interface ErrorPatternDiff {
|
|
115
|
+
/** New error patterns that didn't exist before */
|
|
116
|
+
added: ErrorPattern[];
|
|
117
|
+
/** Error patterns that no longer occur */
|
|
118
|
+
removed: ErrorPattern[];
|
|
119
|
+
/** Whether error behavior changed significantly */
|
|
120
|
+
behaviorChanged: boolean;
|
|
121
|
+
}
|
|
122
|
+
export declare function compareErrorPatterns(previous: ErrorPattern[] | undefined, current: ErrorPattern[] | undefined): ErrorPatternDiff;
|
|
123
|
+
/**
|
|
124
|
+
* Compute a hash for the inferred schema for comparison.
|
|
125
|
+
*/
|
|
126
|
+
export declare function computeInferredSchemaHash(schema: InferredSchema | undefined): string;
|
|
127
|
+
//# sourceMappingURL=response-fingerprint.d.ts.map
|