@j0hanz/code-assistant 0.9.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.
Files changed (54) hide show
  1. package/README.md +437 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +72 -0
  4. package/dist/lib/concurrency.d.ts +13 -0
  5. package/dist/lib/concurrency.js +77 -0
  6. package/dist/lib/config.d.ts +43 -0
  7. package/dist/lib/config.js +87 -0
  8. package/dist/lib/diff.d.ts +49 -0
  9. package/dist/lib/diff.js +241 -0
  10. package/dist/lib/errors.d.ts +8 -0
  11. package/dist/lib/errors.js +69 -0
  12. package/dist/lib/format.d.ts +14 -0
  13. package/dist/lib/format.js +33 -0
  14. package/dist/lib/gemini.d.ts +45 -0
  15. package/dist/lib/gemini.js +833 -0
  16. package/dist/lib/progress.d.ts +72 -0
  17. package/dist/lib/progress.js +204 -0
  18. package/dist/lib/tools.d.ts +274 -0
  19. package/dist/lib/tools.js +646 -0
  20. package/dist/prompts/index.d.ts +11 -0
  21. package/dist/prompts/index.js +96 -0
  22. package/dist/resources/index.d.ts +12 -0
  23. package/dist/resources/index.js +115 -0
  24. package/dist/resources/instructions.d.ts +1 -0
  25. package/dist/resources/instructions.js +71 -0
  26. package/dist/resources/server-config.d.ts +1 -0
  27. package/dist/resources/server-config.js +75 -0
  28. package/dist/resources/tool-catalog.d.ts +1 -0
  29. package/dist/resources/tool-catalog.js +30 -0
  30. package/dist/resources/tool-info.d.ts +5 -0
  31. package/dist/resources/tool-info.js +105 -0
  32. package/dist/resources/workflows.d.ts +1 -0
  33. package/dist/resources/workflows.js +59 -0
  34. package/dist/schemas/inputs.d.ts +21 -0
  35. package/dist/schemas/inputs.js +46 -0
  36. package/dist/schemas/outputs.d.ts +121 -0
  37. package/dist/schemas/outputs.js +162 -0
  38. package/dist/server.d.ts +6 -0
  39. package/dist/server.js +88 -0
  40. package/dist/tools/analyze-complexity.d.ts +2 -0
  41. package/dist/tools/analyze-complexity.js +50 -0
  42. package/dist/tools/analyze-pr-impact.d.ts +2 -0
  43. package/dist/tools/analyze-pr-impact.js +62 -0
  44. package/dist/tools/detect-api-breaking.d.ts +2 -0
  45. package/dist/tools/detect-api-breaking.js +49 -0
  46. package/dist/tools/generate-diff.d.ts +2 -0
  47. package/dist/tools/generate-diff.js +140 -0
  48. package/dist/tools/generate-review-summary.d.ts +2 -0
  49. package/dist/tools/generate-review-summary.js +71 -0
  50. package/dist/tools/generate-test-plan.d.ts +2 -0
  51. package/dist/tools/generate-test-plan.js +67 -0
  52. package/dist/tools/index.d.ts +2 -0
  53. package/dist/tools/index.js +19 -0
  54. package/package.json +79 -0
@@ -0,0 +1,121 @@
1
+ import { z } from 'zod';
2
+ export declare const DefaultOutputSchema: z.ZodObject<{
3
+ ok: z.ZodBoolean;
4
+ result: z.ZodOptional<z.ZodUnknown>;
5
+ error: z.ZodOptional<z.ZodObject<{
6
+ code: z.ZodString;
7
+ message: z.ZodString;
8
+ retryable: z.ZodOptional<z.ZodBoolean>;
9
+ kind: z.ZodOptional<z.ZodEnum<{
10
+ validation: "validation";
11
+ budget: "budget";
12
+ upstream: "upstream";
13
+ timeout: "timeout";
14
+ cancelled: "cancelled";
15
+ busy: "busy";
16
+ internal: "internal";
17
+ }>>;
18
+ }, z.core.$strict>>;
19
+ }, z.core.$strict>;
20
+ export declare const PrImpactResultSchema: z.ZodObject<{
21
+ severity: z.ZodEnum<{
22
+ low: "low";
23
+ medium: "medium";
24
+ high: "high";
25
+ critical: "critical";
26
+ }>;
27
+ categories: z.ZodArray<z.ZodEnum<{
28
+ breaking_change: "breaking_change";
29
+ api_change: "api_change";
30
+ schema_change: "schema_change";
31
+ config_change: "config_change";
32
+ dependency_update: "dependency_update";
33
+ security_fix: "security_fix";
34
+ deprecation: "deprecation";
35
+ performance_change: "performance_change";
36
+ bug_fix: "bug_fix";
37
+ feature_addition: "feature_addition";
38
+ }>>;
39
+ summary: z.ZodString;
40
+ breakingChanges: z.ZodArray<z.ZodString>;
41
+ affectedAreas: z.ZodArray<z.ZodString>;
42
+ rollbackComplexity: z.ZodEnum<{
43
+ trivial: "trivial";
44
+ moderate: "moderate";
45
+ complex: "complex";
46
+ irreversible: "irreversible";
47
+ }>;
48
+ }, z.core.$strict>;
49
+ export declare const ReviewSummaryResultSchema: z.ZodObject<{
50
+ summary: z.ZodString;
51
+ overallRisk: z.ZodEnum<{
52
+ low: "low";
53
+ medium: "medium";
54
+ high: "high";
55
+ }>;
56
+ keyChanges: z.ZodArray<z.ZodString>;
57
+ recommendation: z.ZodString;
58
+ stats: z.ZodObject<{
59
+ filesChanged: z.ZodNumber;
60
+ linesAdded: z.ZodNumber;
61
+ linesRemoved: z.ZodNumber;
62
+ }, z.core.$strict>;
63
+ }, z.core.$strict>;
64
+ export declare const TestCaseSchema: z.ZodObject<{
65
+ name: z.ZodString;
66
+ type: z.ZodEnum<{
67
+ unit: "unit";
68
+ integration: "integration";
69
+ e2e: "e2e";
70
+ regression: "regression";
71
+ security: "security";
72
+ performance: "performance";
73
+ }>;
74
+ file: z.ZodString;
75
+ description: z.ZodString;
76
+ pseudoCode: z.ZodString;
77
+ priority: z.ZodEnum<{
78
+ must_have: "must_have";
79
+ should_have: "should_have";
80
+ nice_to_have: "nice_to_have";
81
+ }>;
82
+ }, z.core.$strict>;
83
+ export declare const TestPlanResultSchema: z.ZodObject<{
84
+ summary: z.ZodString;
85
+ testCases: z.ZodArray<z.ZodObject<{
86
+ name: z.ZodString;
87
+ type: z.ZodEnum<{
88
+ unit: "unit";
89
+ integration: "integration";
90
+ e2e: "e2e";
91
+ regression: "regression";
92
+ security: "security";
93
+ performance: "performance";
94
+ }>;
95
+ file: z.ZodString;
96
+ description: z.ZodString;
97
+ pseudoCode: z.ZodString;
98
+ priority: z.ZodEnum<{
99
+ must_have: "must_have";
100
+ should_have: "should_have";
101
+ nice_to_have: "nice_to_have";
102
+ }>;
103
+ }, z.core.$strict>>;
104
+ coverageSummary: z.ZodString;
105
+ }, z.core.$strict>;
106
+ export declare const AnalyzeComplexityResultSchema: z.ZodObject<{
107
+ timeComplexity: z.ZodString;
108
+ spaceComplexity: z.ZodString;
109
+ explanation: z.ZodString;
110
+ potentialBottlenecks: z.ZodArray<z.ZodString>;
111
+ isDegradation: z.ZodBoolean;
112
+ }, z.core.$strict>;
113
+ export declare const DetectApiBreakingResultSchema: z.ZodObject<{
114
+ hasBreakingChanges: z.ZodBoolean;
115
+ breakingChanges: z.ZodArray<z.ZodObject<{
116
+ element: z.ZodString;
117
+ natureOfChange: z.ZodString;
118
+ consumerImpact: z.ZodString;
119
+ suggestedMitigation: z.ZodString;
120
+ }, z.core.$strict>>;
121
+ }, z.core.$strict>;
@@ -0,0 +1,162 @@
1
+ import { z } from 'zod';
2
+ const OUTPUT_LIMITS = {
3
+ reviewDiffResult: {
4
+ summary: { min: 1, max: 2_000 },
5
+ findingsMax: 50,
6
+ testsNeeded: { minItems: 0, maxItems: 20, itemMin: 1, itemMax: 300 },
7
+ },
8
+ complexity: {
9
+ timeComplexity: { min: 1, max: 200 },
10
+ spaceComplexity: { min: 1, max: 200 },
11
+ explanation: { min: 1, max: 2_000 },
12
+ bottleneck: { min: 1, max: 500, maxItems: 10 },
13
+ },
14
+ apiBreaking: {
15
+ element: { min: 1, max: 300 },
16
+ natureOfChange: { min: 1, max: 500 },
17
+ consumerImpact: { min: 1, max: 500 },
18
+ suggestedMitigation: { min: 1, max: 500 },
19
+ maxItems: 20,
20
+ },
21
+ };
22
+ const QUALITY_RISK_LEVELS = ['low', 'medium', 'high', 'critical'];
23
+ const MERGE_RISK_LEVELS = ['low', 'medium', 'high'];
24
+ const REVIEW_SUMMARY_LIMITS = OUTPUT_LIMITS.reviewDiffResult.summary;
25
+ const ERROR_KINDS = [
26
+ 'validation',
27
+ 'budget',
28
+ 'upstream',
29
+ 'timeout',
30
+ 'cancelled',
31
+ 'busy',
32
+ 'internal',
33
+ ];
34
+ function createBoundedString(min, max, description) {
35
+ return z.string().min(min).max(max).describe(description);
36
+ }
37
+ function createBoundedStringArray(itemMin, itemMax, minItems, maxItems, description) {
38
+ return z
39
+ .array(z.string().min(itemMin).max(itemMax))
40
+ .min(minItems)
41
+ .max(maxItems)
42
+ .describe(description);
43
+ }
44
+ function createReviewSummarySchema(description) {
45
+ return z
46
+ .string()
47
+ .min(REVIEW_SUMMARY_LIMITS.min)
48
+ .max(REVIEW_SUMMARY_LIMITS.max)
49
+ .describe(description);
50
+ }
51
+ const mergeRiskSchema = z
52
+ .enum(MERGE_RISK_LEVELS)
53
+ .describe('High-level merge risk.');
54
+ export const DefaultOutputSchema = z.strictObject({
55
+ ok: z.boolean().describe('Whether the tool completed successfully.'),
56
+ result: z.unknown().optional().describe('Successful result payload.'),
57
+ error: z
58
+ .strictObject({
59
+ code: z.string().describe('Stable error code for callers.'),
60
+ message: z.string().describe('Human readable error details.'),
61
+ retryable: z
62
+ .boolean()
63
+ .optional()
64
+ .describe('Whether the client should retry this request.'),
65
+ kind: z
66
+ .enum(ERROR_KINDS)
67
+ .optional()
68
+ .describe('Machine-readable error category.'),
69
+ })
70
+ .optional()
71
+ .describe('Error payload when ok is false.'),
72
+ });
73
+ export const PrImpactResultSchema = z.strictObject({
74
+ severity: z.enum(QUALITY_RISK_LEVELS).describe('Overall severity.'),
75
+ categories: z
76
+ .array(z.enum([
77
+ 'breaking_change',
78
+ 'api_change',
79
+ 'schema_change',
80
+ 'config_change',
81
+ 'dependency_update',
82
+ 'security_fix',
83
+ 'deprecation',
84
+ 'performance_change',
85
+ 'bug_fix',
86
+ 'feature_addition',
87
+ ]))
88
+ .min(0)
89
+ .max(10)
90
+ .describe('Impact categories.'),
91
+ summary: z.string().min(1).max(1000).describe('Concise summary.'),
92
+ breakingChanges: createBoundedStringArray(1, 500, 0, 10, 'Specific breaking changes.'),
93
+ affectedAreas: createBoundedStringArray(1, 200, 0, 20, 'Impacted subsystems/files.'),
94
+ rollbackComplexity: z
95
+ .enum(['trivial', 'moderate', 'complex', 'irreversible'])
96
+ .describe('Revert difficulty.'),
97
+ });
98
+ export const ReviewSummaryResultSchema = z.strictObject({
99
+ summary: createReviewSummarySchema('PR summary.'),
100
+ overallRisk: mergeRiskSchema,
101
+ keyChanges: createBoundedStringArray(1, 300, 1, 15, 'Key changes (significance desc).'),
102
+ recommendation: z.string().min(1).max(500).describe('Merge recommendation.'),
103
+ stats: z
104
+ .strictObject({
105
+ filesChanged: z.number().int().min(0).describe('Files changed.'),
106
+ linesAdded: z.number().int().min(0).describe('Lines added.'),
107
+ linesRemoved: z.number().int().min(0).describe('Lines removed.'),
108
+ })
109
+ .describe('Change statistics (computed from diff before Gemini call).'),
110
+ });
111
+ export const TestCaseSchema = z.strictObject({
112
+ name: z.string().min(1).max(200).describe('Test case name.'),
113
+ type: z
114
+ .enum([
115
+ 'unit',
116
+ 'integration',
117
+ 'e2e',
118
+ 'regression',
119
+ 'security',
120
+ 'performance',
121
+ ])
122
+ .describe('Test category.'),
123
+ file: z.string().min(1).max(500).describe('Test file path.'),
124
+ description: z.string().min(1).max(1000).describe('Verification goal.'),
125
+ pseudoCode: z.string().min(1).max(2000).describe('Pseudocode/starter.'),
126
+ priority: z
127
+ .enum(['must_have', 'should_have', 'nice_to_have'])
128
+ .describe('Priority.'),
129
+ });
130
+ export const TestPlanResultSchema = z.strictObject({
131
+ summary: z.string().min(1).max(1000).describe('Plan overview.'),
132
+ testCases: z
133
+ .array(TestCaseSchema)
134
+ .min(1)
135
+ .max(30)
136
+ .describe('Test cases (must_have first).'),
137
+ coverageSummary: z
138
+ .string()
139
+ .min(1)
140
+ .max(500)
141
+ .describe('Coverage gaps addressed.'),
142
+ });
143
+ export const AnalyzeComplexityResultSchema = z.strictObject({
144
+ timeComplexity: createBoundedString(OUTPUT_LIMITS.complexity.timeComplexity.min, OUTPUT_LIMITS.complexity.timeComplexity.max, 'Big-O time complexity (e.g. O(n log n)).'),
145
+ spaceComplexity: createBoundedString(OUTPUT_LIMITS.complexity.spaceComplexity.min, OUTPUT_LIMITS.complexity.spaceComplexity.max, 'Big-O space complexity (e.g. O(n)).'),
146
+ explanation: createBoundedString(OUTPUT_LIMITS.complexity.explanation.min, OUTPUT_LIMITS.complexity.explanation.max, 'Analysis explanation (loops, recursion).'),
147
+ potentialBottlenecks: createBoundedStringArray(OUTPUT_LIMITS.complexity.bottleneck.min, OUTPUT_LIMITS.complexity.bottleneck.max, 0, OUTPUT_LIMITS.complexity.bottleneck.maxItems, 'Potential bottlenecks.'),
148
+ isDegradation: z.boolean().describe('True if degradation vs original.'),
149
+ });
150
+ export const DetectApiBreakingResultSchema = z.strictObject({
151
+ hasBreakingChanges: z.boolean().describe('True if breaking.'),
152
+ breakingChanges: z
153
+ .array(z.strictObject({
154
+ element: createBoundedString(OUTPUT_LIMITS.apiBreaking.element.min, OUTPUT_LIMITS.apiBreaking.element.max, 'Changed element (signature/field/export).'),
155
+ natureOfChange: createBoundedString(OUTPUT_LIMITS.apiBreaking.natureOfChange.min, OUTPUT_LIMITS.apiBreaking.natureOfChange.max, 'Change details & breaking reason.'),
156
+ consumerImpact: createBoundedString(OUTPUT_LIMITS.apiBreaking.consumerImpact.min, OUTPUT_LIMITS.apiBreaking.consumerImpact.max, 'Consumer impact.'),
157
+ suggestedMitigation: createBoundedString(OUTPUT_LIMITS.apiBreaking.suggestedMitigation.min, OUTPUT_LIMITS.apiBreaking.suggestedMitigation.max, 'Mitigation strategy.'),
158
+ }))
159
+ .min(0)
160
+ .max(OUTPUT_LIMITS.apiBreaking.maxItems)
161
+ .describe('Breaking changes list.'),
162
+ });
@@ -0,0 +1,6 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export interface ServerHandle {
3
+ server: McpServer;
4
+ shutdown: () => Promise<void>;
5
+ }
6
+ export declare function createServer(): ServerHandle;
package/dist/server.js ADDED
@@ -0,0 +1,88 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { findPackageJSON } from 'node:module';
3
+ import { InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js';
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { z } from 'zod';
6
+ import { initDiffStore } from './lib/diff.js';
7
+ import { getErrorMessage } from './lib/errors.js';
8
+ import { registerAllPrompts } from './prompts/index.js';
9
+ import { registerAllResources } from './resources/index.js';
10
+ import { buildServerInstructions } from './resources/instructions.js';
11
+ import { registerAllTools } from './tools/index.js';
12
+ const SERVER_NAME = 'code-assistant';
13
+ const UTF8_ENCODING = 'utf8';
14
+ const PackageJsonSchema = z.object({
15
+ version: z.string().min(1),
16
+ });
17
+ const TASK_TOOL_CALL_CAPABILITY = {
18
+ tools: {
19
+ call: {},
20
+ },
21
+ };
22
+ const SERVER_CAPABILITIES = {
23
+ logging: {},
24
+ completions: {},
25
+ prompts: {},
26
+ resources: { subscribe: true },
27
+ tools: {},
28
+ tasks: {
29
+ list: {},
30
+ cancel: {},
31
+ requests: TASK_TOOL_CALL_CAPABILITY,
32
+ },
33
+ };
34
+ function readUtf8File(path) {
35
+ try {
36
+ return readFileSync(path, UTF8_ENCODING);
37
+ }
38
+ catch (error) {
39
+ throw new Error(`Unable to read ${path}: ${getErrorMessage(error)}`, {
40
+ cause: error,
41
+ });
42
+ }
43
+ }
44
+ function parsePackageVersion(packageJsonText, packageJsonPath) {
45
+ try {
46
+ const json = JSON.parse(packageJsonText);
47
+ return PackageJsonSchema.parse(json).version;
48
+ }
49
+ catch (error) {
50
+ throw new Error(`Invalid package.json at ${packageJsonPath}: ${getErrorMessage(error)}`, { cause: error });
51
+ }
52
+ }
53
+ function loadVersion() {
54
+ const packageJsonPath = findPackageJSON(import.meta.url);
55
+ if (!packageJsonPath) {
56
+ throw new Error(`Unable to locate package.json for ${SERVER_NAME}.`);
57
+ }
58
+ const packageJsonText = readUtf8File(packageJsonPath);
59
+ return parsePackageVersion(packageJsonText, packageJsonPath);
60
+ }
61
+ const SERVER_VERSION = loadVersion();
62
+ const SERVER_INSTRUCTIONS = buildServerInstructions();
63
+ function createMcpServer(taskStore) {
64
+ return new McpServer({
65
+ name: SERVER_NAME,
66
+ version: SERVER_VERSION,
67
+ }, {
68
+ instructions: SERVER_INSTRUCTIONS,
69
+ taskStore,
70
+ capabilities: SERVER_CAPABILITIES,
71
+ });
72
+ }
73
+ function registerServerCapabilities(server) {
74
+ initDiffStore(server);
75
+ registerAllTools(server);
76
+ registerAllResources(server, SERVER_INSTRUCTIONS);
77
+ registerAllPrompts(server, SERVER_INSTRUCTIONS);
78
+ }
79
+ export function createServer() {
80
+ const taskStore = new InMemoryTaskStore();
81
+ const server = createMcpServer(taskStore);
82
+ registerServerCapabilities(server);
83
+ const shutdown = async () => {
84
+ await server.close();
85
+ taskStore.cleanup();
86
+ };
87
+ return { server, shutdown };
88
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerAnalyzeComplexityTool(server: McpServer): void;
@@ -0,0 +1,50 @@
1
+ import { buildLanguageDiffPrompt } from '../lib/format.js';
2
+ import { getDiffContextSnapshot } from '../lib/tools.js';
3
+ import { buildStructuredToolExecutionOptions, registerStructuredToolTask, requireToolContract, } from '../lib/tools.js';
4
+ import { AnalyzeComplexityInputSchema } from '../schemas/inputs.js';
5
+ import { AnalyzeComplexityResultSchema } from '../schemas/outputs.js';
6
+ const SYSTEM_INSTRUCTION = `
7
+ <role>
8
+ Algorithm Complexity Analyst.
9
+ You are an expert in Big-O analysis and performance optimization.
10
+ </role>
11
+
12
+ <task>
13
+ Analyze the time and space complexity of the code changes:
14
+ - Compare new complexity vs. original implementation.
15
+ - Detect performance degradation (regression).
16
+ - Identify bottlenecks (nested loops, recursion, allocations).
17
+ </task>
18
+
19
+ <constraints>
20
+ - Focus on the changed code paths.
21
+ - Flag degradation only if complexity class worsens (e.g., O(n) -> O(n^2)).
22
+ - Return valid JSON matching the schema.
23
+ </constraints>
24
+ `;
25
+ const TOOL_CONTRACT = requireToolContract('analyze_time_space_complexity');
26
+ export function registerAnalyzeComplexityTool(server) {
27
+ registerStructuredToolTask(server, {
28
+ name: 'analyze_time_space_complexity',
29
+ title: 'Analyze Time & Space Complexity',
30
+ description: 'Analyze Big-O complexity. Prerequisite: generate_diff. Auto-infer language.',
31
+ inputSchema: AnalyzeComplexityInputSchema,
32
+ fullInputSchema: AnalyzeComplexityInputSchema,
33
+ resultSchema: AnalyzeComplexityResultSchema,
34
+ errorCode: 'E_ANALYZE_COMPLEXITY',
35
+ ...buildStructuredToolExecutionOptions(TOOL_CONTRACT),
36
+ requiresDiff: true,
37
+ progressContext: (input) => input.language ?? 'auto-detect',
38
+ formatOutcome: (result) => result.isDegradation
39
+ ? 'Performance degradation detected'
40
+ : 'No degradation',
41
+ formatOutput: (result) => `Time=${result.timeComplexity}, Space=${result.spaceComplexity}. ${result.explanation}`,
42
+ buildPrompt: (input, ctx) => {
43
+ const { diff } = getDiffContextSnapshot(ctx);
44
+ return {
45
+ systemInstruction: SYSTEM_INSTRUCTION,
46
+ prompt: buildLanguageDiffPrompt(input.language, diff, 'Based on the diff above, analyze the Big-O time and space complexity.'),
47
+ };
48
+ },
49
+ });
50
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerAnalyzePrImpactTool(server: McpServer): void;
@@ -0,0 +1,62 @@
1
+ import { computeDiffStatsAndSummaryFromFiles } from '../lib/diff.js';
2
+ import { formatLanguageSegment } from '../lib/format.js';
3
+ import { getDiffContextSnapshot } from '../lib/tools.js';
4
+ import { buildStructuredToolExecutionOptions, registerStructuredToolTask, requireToolContract, } from '../lib/tools.js';
5
+ import { AnalyzePrImpactInputSchema } from '../schemas/inputs.js';
6
+ import { PrImpactResultSchema } from '../schemas/outputs.js';
7
+ const SYSTEM_INSTRUCTION = `
8
+ <role>
9
+ Technical Change Analyst.
10
+ You are a strict, objective auditor of code changes.
11
+ </role>
12
+
13
+ <task>
14
+ Analyze the unified diff to assess:
15
+ - Severity (low/medium/high/critical)
16
+ - Risk categories (security, stability, etc.)
17
+ - Breaking changes (API, contract, schema)
18
+ - Rollback complexity
19
+ </task>
20
+
21
+ <constraints>
22
+ - Base analysis ONLY on the provided diff. No external inference.
23
+ - Ignore formatting/style changes unless they affect logic.
24
+ - Return valid JSON matching the schema.
25
+ </constraints>
26
+ `;
27
+ const TOOL_CONTRACT = requireToolContract('analyze_pr_impact');
28
+ export function registerAnalyzePrImpactTool(server) {
29
+ registerStructuredToolTask(server, {
30
+ name: 'analyze_pr_impact',
31
+ title: 'Analyze PR Impact',
32
+ description: 'Assess impact and risk from cached diff. Prerequisite: generate_diff. Auto-infer repo/language.',
33
+ inputSchema: AnalyzePrImpactInputSchema,
34
+ fullInputSchema: AnalyzePrImpactInputSchema,
35
+ resultSchema: PrImpactResultSchema,
36
+ errorCode: 'E_ANALYZE_IMPACT',
37
+ ...buildStructuredToolExecutionOptions(TOOL_CONTRACT),
38
+ requiresDiff: true,
39
+ progressContext: (input) => input.repository,
40
+ formatOutcome: (result) => `severity: ${result.severity}`,
41
+ formatOutput: (result) => `[${result.severity}] ${result.summary}`,
42
+ buildPrompt: (input, ctx) => {
43
+ const { diff, parsedFiles } = getDiffContextSnapshot(ctx);
44
+ const { stats, summary: fileSummary } = computeDiffStatsAndSummaryFromFiles(parsedFiles);
45
+ const languageSegment = formatLanguageSegment(input.language);
46
+ return {
47
+ systemInstruction: SYSTEM_INSTRUCTION,
48
+ prompt: `
49
+ Repository: ${input.repository}${languageSegment}
50
+ Change Stats: ${stats.files} files, +${stats.added} lines, -${stats.deleted} lines.
51
+ Changed Files:
52
+ ${fileSummary}
53
+
54
+ Diff:
55
+ ${diff}
56
+
57
+ Based on the diff and change stats above, analyze the PR impact.
58
+ `,
59
+ };
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerDetectApiBreakingTool(server: McpServer): void;
@@ -0,0 +1,49 @@
1
+ import { buildLanguageDiffPrompt } from '../lib/format.js';
2
+ import { getDiffContextSnapshot } from '../lib/tools.js';
3
+ import { buildStructuredToolExecutionOptions, registerStructuredToolTask, requireToolContract, } from '../lib/tools.js';
4
+ import { DetectApiBreakingInputSchema } from '../schemas/inputs.js';
5
+ import { DetectApiBreakingResultSchema } from '../schemas/outputs.js';
6
+ const SYSTEM_INSTRUCTION = `
7
+ <role>
8
+ API Compatibility Analyst.
9
+ You are a strict guardian of public interfaces and contracts.
10
+ </role>
11
+
12
+ <task>
13
+ Detect breaking changes in public APIs, interfaces, or schemas:
14
+ - Identify changes that require consumer code modification.
15
+ - classify the nature, impact, and mitigation for each break.
16
+ </task>
17
+
18
+ <constraints>
19
+ - Definition: Breaking change = backwards-incompatible modification.
20
+ - Ignore internal/private APIs unless exported.
21
+ - Return valid JSON matching the schema.
22
+ </constraints>
23
+ `;
24
+ const TOOL_CONTRACT = requireToolContract('detect_api_breaking_changes');
25
+ export function registerDetectApiBreakingTool(server) {
26
+ registerStructuredToolTask(server, {
27
+ name: 'detect_api_breaking_changes',
28
+ title: 'Detect API Breaking Changes',
29
+ description: 'Detect breaking API changes. Prerequisite: generate_diff. Auto-infer language.',
30
+ inputSchema: DetectApiBreakingInputSchema,
31
+ fullInputSchema: DetectApiBreakingInputSchema,
32
+ resultSchema: DetectApiBreakingResultSchema,
33
+ errorCode: 'E_DETECT_API_BREAKING',
34
+ ...buildStructuredToolExecutionOptions(TOOL_CONTRACT),
35
+ requiresDiff: true,
36
+ progressContext: (input) => input.language ?? 'auto-detect',
37
+ formatOutcome: (result) => `${result.breakingChanges.length} breaking change(s) found`,
38
+ formatOutput: (result) => result.hasBreakingChanges
39
+ ? `${result.breakingChanges.length} breaking changes found.`
40
+ : 'No breaking changes.',
41
+ buildPrompt: (input, ctx) => {
42
+ const { diff } = getDiffContextSnapshot(ctx);
43
+ return {
44
+ systemInstruction: SYSTEM_INSTRUCTION,
45
+ prompt: buildLanguageDiffPrompt(input.language, diff, 'Based on the diff above, detect any breaking API changes.'),
46
+ };
47
+ },
48
+ });
49
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerGenerateDiffTool(server: McpServer): void;