@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,96 @@
1
+ import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
2
+ import { z } from 'zod';
3
+ import { toInlineCode } from '../lib/format.js';
4
+ import { getToolContract, getToolContractNames, INSPECTION_FOCUS_AREAS, } from '../lib/tools.js';
5
+ export const PROMPT_DEFINITIONS = [
6
+ {
7
+ name: 'get-help',
8
+ title: 'Get Help',
9
+ description: 'Server instructions.',
10
+ },
11
+ {
12
+ name: 'review-guide',
13
+ title: 'Review Guide',
14
+ description: 'Workflow guide for tool/focus area.',
15
+ },
16
+ ];
17
+ const TOOLS = getToolContractNames();
18
+ const TOOL_DESCRIPTION_TEXT = 'Select tool for review guide.';
19
+ const FOCUS_DESCRIPTION_TEXT = 'Select focus area.';
20
+ const FOCUS_AREA_GUIDES = {
21
+ security: 'Focus: Injection, Auth, Crypto, OWASP.',
22
+ correctness: 'Focus: Logic, Edge Cases, Types.',
23
+ performance: 'Focus: Complexity, Memory, Latency.',
24
+ regressions: 'Focus: Behavior Changes, Breaking APIs.',
25
+ tests: 'Focus: Coverage, Error Paths.',
26
+ maintainability: 'Focus: Complexity, Readability, Patterns.',
27
+ concurrency: 'Focus: Races, Deadlocks, Atomicity.',
28
+ };
29
+ function isFocusArea(value) {
30
+ return INSPECTION_FOCUS_AREAS.includes(value);
31
+ }
32
+ function completeByPrefix(values, prefix) {
33
+ return values.filter((value) => value.startsWith(prefix));
34
+ }
35
+ function getToolGuide(tool) {
36
+ const contract = getToolContract(tool);
37
+ if (!contract) {
38
+ return `Use ${toInlineCode(tool)} to analyze your code changes.`;
39
+ }
40
+ const modelLine = buildToolModelLine(contract);
41
+ return `Tool: ${contract.name}\n${modelLine}\nOutput: ${contract.outputShape}\nUse: ${contract.purpose}`;
42
+ }
43
+ function buildToolModelLine(contract) {
44
+ if (contract.thinkingLevel !== undefined) {
45
+ return `Model: ${contract.model} (thinking level ${contract.thinkingLevel}, output cap ${contract.maxOutputTokens}).`;
46
+ }
47
+ return `Model: ${contract.model} (output cap ${contract.maxOutputTokens}).`;
48
+ }
49
+ function createPromptResponse(description, text) {
50
+ return {
51
+ description,
52
+ messages: [
53
+ {
54
+ role: 'user',
55
+ content: { type: 'text', text },
56
+ },
57
+ ],
58
+ };
59
+ }
60
+ function getFocusAreaGuide(focusArea) {
61
+ return isFocusArea(focusArea)
62
+ ? FOCUS_AREA_GUIDES[focusArea]
63
+ : `Focus on ${focusArea} concerns.`;
64
+ }
65
+ function registerHelpPrompt(server, instructions) {
66
+ const def = PROMPT_DEFINITIONS[0];
67
+ server.registerPrompt(def.name, {
68
+ title: def.title,
69
+ description: def.description,
70
+ }, () => createPromptResponse(def.description, instructions));
71
+ }
72
+ function buildReviewGuideText(tool, focusArea) {
73
+ const toolCode = toInlineCode(tool);
74
+ return (`# Guide: ${tool} / ${focusArea}\n\n` +
75
+ `## Tool: ${toolCode}\n${getToolGuide(tool)}\n\n` +
76
+ `## Focus: ${focusArea}\n${getFocusAreaGuide(focusArea)}`);
77
+ }
78
+ function registerReviewGuidePrompt(server) {
79
+ const def = PROMPT_DEFINITIONS[1];
80
+ server.registerPrompt(def.name, {
81
+ title: def.title,
82
+ description: def.description,
83
+ argsSchema: {
84
+ tool: completable(z.string().optional().describe(TOOL_DESCRIPTION_TEXT), (value) => completeByPrefix(TOOLS, value ?? '')),
85
+ focusArea: completable(z.string().optional().describe(FOCUS_DESCRIPTION_TEXT), (value) => completeByPrefix(INSPECTION_FOCUS_AREAS, value ?? '')),
86
+ },
87
+ }, (args) => {
88
+ const selectedTool = args.tool ?? TOOLS[0] ?? 'analyze_pr_impact';
89
+ const selectedFocus = args.focusArea ?? INSPECTION_FOCUS_AREAS[0];
90
+ return createPromptResponse(`Code review guide: ${selectedTool} / ${selectedFocus}`, buildReviewGuideText(selectedTool, selectedFocus));
91
+ });
92
+ }
93
+ export function registerAllPrompts(server, instructions) {
94
+ registerHelpPrompt(server, instructions);
95
+ registerReviewGuidePrompt(server);
96
+ }
@@ -0,0 +1,12 @@
1
+ import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export interface StaticResourceDef {
3
+ id: string;
4
+ uri: string;
5
+ title: string;
6
+ description: string;
7
+ priority: number;
8
+ content?: () => string;
9
+ }
10
+ export declare const STATIC_RESOURCES: readonly StaticResourceDef[];
11
+ export declare const DIFF_RESOURCE_DESCRIPTION = "The most recently generated diff, cached by generate_diff. Read by all review tools automatically.";
12
+ export declare function registerAllResources(server: McpServer, instructions: string): void;
@@ -0,0 +1,115 @@
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { DIFF_RESOURCE_URI, getDiff } from '../lib/diff.js';
3
+ import { buildServerConfig } from './server-config.js';
4
+ import { buildToolCatalog } from './tool-catalog.js';
5
+ import { getToolInfo, getToolInfoNames } from './tool-info.js';
6
+ import { buildWorkflowGuide } from './workflows.js';
7
+ const RESOURCE_MIME_TYPE = 'text/markdown';
8
+ const PATCH_MIME_TYPE = 'text/x-patch';
9
+ const RESOURCE_AUDIENCE = ['assistant'];
10
+ const TOOL_INFO_RESOURCE_URI = 'internal://tool-info/{toolName}';
11
+ function completeByPrefix(values, prefix) {
12
+ return values.filter((value) => value.startsWith(prefix));
13
+ }
14
+ function createMarkdownContent(uri, text) {
15
+ return { uri: uri.href, mimeType: RESOURCE_MIME_TYPE, text };
16
+ }
17
+ function createPatchContent(uri, text) {
18
+ return { uri: uri.href, mimeType: PATCH_MIME_TYPE, text };
19
+ }
20
+ function createResourceAnnotations(priority) {
21
+ return { audience: [...RESOURCE_AUDIENCE], priority };
22
+ }
23
+ function formatUnknownToolMessage(name) {
24
+ return `Unknown tool: ${name}`;
25
+ }
26
+ function formatDiffResourceText() {
27
+ const slot = getDiff();
28
+ if (!slot) {
29
+ return '# No diff cached. Call generate_diff first.';
30
+ }
31
+ return `# Diff — ${slot.mode} — ${slot.generatedAt}\n# ${slot.stats.files} file(s), +${slot.stats.added} -${slot.stats.deleted}\n\n${slot.diff}`;
32
+ }
33
+ export const STATIC_RESOURCES = [
34
+ {
35
+ id: 'server-instructions',
36
+ uri: 'internal://instructions',
37
+ title: 'Server Instructions',
38
+ description: 'Complete server usage instructions.',
39
+ priority: 0.8,
40
+ },
41
+ {
42
+ id: 'tool-catalog',
43
+ uri: 'internal://tool-catalog',
44
+ title: 'Tool Catalog',
45
+ description: 'Tool reference: models, params, outputs, data flow.',
46
+ priority: 1.0,
47
+ content: buildToolCatalog,
48
+ },
49
+ {
50
+ id: 'workflows',
51
+ uri: 'internal://workflows',
52
+ title: 'Workflow Reference',
53
+ description: 'Recommended workflows and tool sequences.',
54
+ priority: 0.9,
55
+ content: buildWorkflowGuide,
56
+ },
57
+ {
58
+ id: 'server-config',
59
+ uri: 'internal://server-config',
60
+ title: 'Server Configuration',
61
+ description: 'Runtime configuration and limits.',
62
+ priority: 0.7,
63
+ content: buildServerConfig,
64
+ },
65
+ ];
66
+ function resolveStaticResourceContentOverride(resourceId, instructions) {
67
+ return resourceId === 'server-instructions' ? instructions : undefined;
68
+ }
69
+ function registerStaticResource(server, def, contentOverride) {
70
+ const content = contentOverride ?? def.content?.() ?? '';
71
+ server.registerResource(def.id, def.uri, {
72
+ title: def.title,
73
+ description: def.description,
74
+ mimeType: RESOURCE_MIME_TYPE,
75
+ annotations: createResourceAnnotations(def.priority),
76
+ }, (uri) => ({ contents: [createMarkdownContent(uri, content)] }));
77
+ }
78
+ function registerToolInfoResources(server) {
79
+ const toolNames = getToolInfoNames();
80
+ server.registerResource('tool-info', new ResourceTemplate(TOOL_INFO_RESOURCE_URI, {
81
+ list: undefined,
82
+ complete: {
83
+ toolName: (value) => completeByPrefix(toolNames, value),
84
+ },
85
+ }), {
86
+ title: 'Tool Info',
87
+ description: 'Per-tool reference: model, params, output, gotchas.',
88
+ mimeType: RESOURCE_MIME_TYPE,
89
+ annotations: createResourceAnnotations(0.6),
90
+ }, (uri, { toolName }) => {
91
+ const name = typeof toolName === 'string' ? toolName : '';
92
+ const info = getToolInfo(name);
93
+ const text = info ?? formatUnknownToolMessage(name);
94
+ return { contents: [createMarkdownContent(uri, text)] };
95
+ });
96
+ }
97
+ export const DIFF_RESOURCE_DESCRIPTION = 'The most recently generated diff, cached by generate_diff. Read by all review tools automatically.';
98
+ function registerDiffResource(server) {
99
+ server.registerResource('diff-current', DIFF_RESOURCE_URI, {
100
+ title: 'Current Diff',
101
+ description: DIFF_RESOURCE_DESCRIPTION,
102
+ mimeType: PATCH_MIME_TYPE,
103
+ annotations: createResourceAnnotations(1.0),
104
+ }, (uri) => ({
105
+ contents: [createPatchContent(uri, formatDiffResourceText())],
106
+ }));
107
+ }
108
+ export function registerAllResources(server, instructions) {
109
+ for (const def of STATIC_RESOURCES) {
110
+ const override = resolveStaticResourceContentOverride(def.id, instructions);
111
+ registerStaticResource(server, def, override);
112
+ }
113
+ registerToolInfoResources(server);
114
+ registerDiffResource(server);
115
+ }
@@ -0,0 +1 @@
1
+ export declare function buildServerInstructions(): string;
@@ -0,0 +1,71 @@
1
+ import { toBulletedList, toInlineCode } from '../lib/format.js';
2
+ import { getToolContracts } from '../lib/tools.js';
3
+ import { PROMPT_DEFINITIONS } from '../prompts/index.js';
4
+ import { DIFF_RESOURCE_DESCRIPTION, STATIC_RESOURCES } from './index.js';
5
+ import { getSharedConstraints } from './tool-info.js';
6
+ const PROMPT_LIST = PROMPT_DEFINITIONS.map((def) => `${toInlineCode(def.name)}: ${def.description}`);
7
+ const RESOURCE_LIST = [
8
+ ...STATIC_RESOURCES.map((def) => `${toInlineCode(def.uri)}: ${def.description}`),
9
+ `${toInlineCode('internal://tool-info/{toolName}')}: Per-tool contract details.`,
10
+ `${toInlineCode('diff://current')}: ${DIFF_RESOURCE_DESCRIPTION}`,
11
+ ];
12
+ function formatParameterLine(parameter) {
13
+ const req = parameter.required ? 'req' : 'opt';
14
+ return ` - \`${parameter.name}\` (${parameter.type}, ${req}): ${parameter.constraints}`;
15
+ }
16
+ function formatToolSection(contract) {
17
+ const parameterLines = contract.params.map((parameter) => formatParameterLine(parameter));
18
+ if (contract.model === 'none') {
19
+ return `### \`${contract.name}\` (Sync)
20
+ ${contract.purpose}
21
+ - **Params**:
22
+ ${parameterLines.join('\n')}
23
+ - **Output**: \`${contract.outputShape}\``;
24
+ }
25
+ const modelInfo = [
26
+ 'Flash',
27
+ contract.thinkingLevel ? `Thinking:${contract.thinkingLevel}` : '',
28
+ `${Math.round(contract.timeoutMs / 1_000)}s`,
29
+ `MaxTokens:${contract.maxOutputTokens}`,
30
+ ]
31
+ .filter(Boolean)
32
+ .join(', ');
33
+ return `### \`${contract.name}\` (${modelInfo})
34
+ ${contract.purpose}
35
+ - **Params**:
36
+ ${parameterLines.join('\n')}
37
+ - **Output**: \`${contract.outputShape}\``;
38
+ }
39
+ export function buildServerInstructions() {
40
+ const contracts = getToolContracts();
41
+ const toolNames = contracts
42
+ .map((contract) => `\`${contract.name}\``)
43
+ .join(', ');
44
+ const toolSections = contracts.map((contract) => formatToolSection(contract));
45
+ const constraintLines = toBulletedList(getSharedConstraints());
46
+ return `# CODE ASSISTANT MCP
47
+
48
+ ## CORE
49
+ - Domain: Gemini-powered code analysis.
50
+ - Capabilities: tools, resources (subscribe), prompts, logging, completions, tasks.
51
+ - Tools: ${toolNames}
52
+
53
+ ## PROMPTS
54
+ ${toBulletedList(PROMPT_LIST)}
55
+
56
+ ## RESOURCES
57
+ ${toBulletedList(RESOURCE_LIST)}
58
+
59
+ ## TOOLS
60
+ ${toolSections.join('\n\n')}
61
+
62
+ ## CONSTRAINTS
63
+ ${constraintLines}
64
+
65
+ ## TASK LIFECYCLE
66
+ - Progress steps (0–6): starting → validating input → building prompt → calling model → validating response → finalizing → done.
67
+ - Status messages update at each phase for task introspection.
68
+ - Schema repair: on validation failure, retries with error feedback (configurable via \`GEMINI_SCHEMA_RETRIES\`).
69
+ - Task terminal states: \`completed\` and \`failed\`; cancellations are surfaced as \`failed\` with \`error.kind=cancelled\`.
70
+ `;
71
+ }
@@ -0,0 +1 @@
1
+ export declare function buildServerConfig(): string;
@@ -0,0 +1,75 @@
1
+ import { createCachedEnvInt } from '../lib/config.js';
2
+ import { FLASH_MODEL } from '../lib/config.js';
3
+ import { formatThinkingLevel, formatTimeoutSeconds, formatUsNumber, } from '../lib/format.js';
4
+ import { toInlineCode } from '../lib/format.js';
5
+ import { getToolContracts } from '../lib/tools.js';
6
+ const DEFAULT_MAX_DIFF_CHARS = 120_000;
7
+ const DEFAULT_MAX_CONCURRENT_CALLS = 10;
8
+ const DEFAULT_CONCURRENT_WAIT_MS = 2_000;
9
+ const DEFAULT_SAFETY_THRESHOLD = 'BLOCK_NONE';
10
+ const GEMINI_HARM_BLOCK_THRESHOLD_ENV_VAR = 'GEMINI_HARM_BLOCK_THRESHOLD';
11
+ const GEMINI_MODEL_ENV_VAR = 'GEMINI_MODEL';
12
+ const GEMINI_BATCH_MODE_ENV_VAR = 'GEMINI_BATCH_MODE';
13
+ const diffCharsConfig = createCachedEnvInt('MAX_DIFF_CHARS', DEFAULT_MAX_DIFF_CHARS);
14
+ const concurrentCallsConfig = createCachedEnvInt('MAX_CONCURRENT_CALLS', DEFAULT_MAX_CONCURRENT_CALLS);
15
+ const concurrentBatchCallsConfig = createCachedEnvInt('MAX_CONCURRENT_BATCH_CALLS', 2);
16
+ const concurrentWaitConfig = createCachedEnvInt('MAX_CONCURRENT_CALLS_WAIT_MS', DEFAULT_CONCURRENT_WAIT_MS);
17
+ function getModelOverride() {
18
+ return process.env[GEMINI_MODEL_ENV_VAR] ?? FLASH_MODEL;
19
+ }
20
+ function getBatchMode() {
21
+ return process.env[GEMINI_BATCH_MODE_ENV_VAR] ?? 'off';
22
+ }
23
+ function getSafetyThreshold() {
24
+ return (process.env[GEMINI_HARM_BLOCK_THRESHOLD_ENV_VAR] ?? DEFAULT_SAFETY_THRESHOLD);
25
+ }
26
+ export function buildServerConfig() {
27
+ const maxDiffChars = diffCharsConfig.get();
28
+ const maxConcurrent = concurrentCallsConfig.get();
29
+ const maxConcurrentBatch = concurrentBatchCallsConfig.get();
30
+ const concurrentWaitMs = concurrentWaitConfig.get();
31
+ const defaultModel = getModelOverride();
32
+ const batchMode = getBatchMode();
33
+ const safetyThreshold = getSafetyThreshold();
34
+ const toolRows = getToolContracts()
35
+ .filter((contract) => contract.model !== 'none')
36
+ .map((contract) => {
37
+ return `| ${toInlineCode(contract.name)} | ${toInlineCode(contract.model)} | ${formatThinkingLevel(contract.thinkingLevel, '—')} | ${formatTimeoutSeconds(contract.timeoutMs)} | ${formatUsNumber(contract.maxOutputTokens)} |`;
38
+ })
39
+ .join('\n');
40
+ return `# Server Configuration
41
+
42
+ ## Input Limits
43
+
44
+ | Limit | Value | Env |
45
+ |-------|-------|-----|
46
+ | Diff limit | ${formatUsNumber(maxDiffChars)} chars | ${toInlineCode('MAX_DIFF_CHARS')} |
47
+ | Concurrency limit | ${maxConcurrent} | ${toInlineCode('MAX_CONCURRENT_CALLS')} |
48
+ | Batch concurrency limit | ${maxConcurrentBatch} | ${toInlineCode('MAX_CONCURRENT_BATCH_CALLS')} |
49
+ | Wait timeout | ${formatUsNumber(concurrentWaitMs)}ms | ${toInlineCode('MAX_CONCURRENT_CALLS_WAIT_MS')} |
50
+ | Batch mode | ${batchMode} | ${toInlineCode('GEMINI_BATCH_MODE')} |
51
+
52
+ ## Model Assignments
53
+
54
+ Default model: ${toInlineCode(defaultModel)} (override with ${toInlineCode('GEMINI_MODEL')})
55
+
56
+ | Tool | Model | Thinking Level | Timeout | Max Output Tokens |
57
+ |------|-------|----------------|---------|-------------------|
58
+ ${toolRows}
59
+
60
+ ## Safety
61
+
62
+ - Harm block threshold: ${toInlineCode(safetyThreshold)}
63
+ - Override with ${toInlineCode('GEMINI_HARM_BLOCK_THRESHOLD')} (BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE)
64
+
65
+ ## API Keys
66
+
67
+ - Set ${toInlineCode('GEMINI_API_KEY')} or ${toInlineCode('GOOGLE_API_KEY')} environment variable (required)
68
+
69
+ ## Batch Mode
70
+
71
+ - ${toInlineCode('GEMINI_BATCH_MODE')}: ${toInlineCode('off')} (default) or ${toInlineCode('inline')}
72
+ - ${toInlineCode('GEMINI_BATCH_POLL_INTERVAL_MS')}: poll cadence for batch status checks
73
+ - ${toInlineCode('GEMINI_BATCH_TIMEOUT_MS')}: max wait for batch completion
74
+ `;
75
+ }
@@ -0,0 +1 @@
1
+ export declare function buildToolCatalog(): string;
@@ -0,0 +1,30 @@
1
+ import { toInlineCode } from '../lib/format.js';
2
+ import { buildCoreContextPack } from './tool-info.js';
3
+ const TOOL_CATALOG_CONTENT = `# Tool Catalog Details
4
+
5
+ ## Optional Parameters
6
+
7
+ - ${toInlineCode('language')}: Primary language hint (auto-detects). All analysis tools.
8
+ - ${toInlineCode('testFramework')}: Framework hint. ${toInlineCode('generate_test_plan')} only.
9
+ - ${toInlineCode('maxTestCases')}: Output cap (1–30). ${toInlineCode('generate_test_plan')} only.
10
+
11
+ ## Cross-Tool Data Flow
12
+
13
+ \`\`\`
14
+ analyze_pr_impact ──→ severity/categories ──→ triage decision
15
+
16
+ generate_review_summary ──→ overallRisk ──────┤
17
+
18
+ diff ──→ generate_test_plan
19
+ \`\`\`
20
+
21
+ ## When to Use Each Tool
22
+
23
+ - **Triage**: ${toInlineCode('analyze_pr_impact')}, ${toInlineCode('generate_review_summary')}.
24
+ - **Tests**: ${toInlineCode('generate_test_plan')}.
25
+ - **Complexity**: ${toInlineCode('analyze_time_space_complexity')}.
26
+ - **Breaking API**: ${toInlineCode('detect_api_breaking_changes')}.
27
+ `;
28
+ export function buildToolCatalog() {
29
+ return `${buildCoreContextPack()}\n\n${TOOL_CATALOG_CONTENT}`;
30
+ }
@@ -0,0 +1,5 @@
1
+ export declare function buildCoreContextPack(): string;
2
+ export declare function getSharedConstraints(): readonly string[];
3
+ export declare function getToolInfoNames(): string[];
4
+ export declare function getToolInfo(toolName: string): string | undefined;
5
+ export declare function getToolPurpose(toolName: string): string | undefined;
@@ -0,0 +1,105 @@
1
+ import { formatThinkingLevel, formatTimeoutSeconds, formatUsNumber, } from '../lib/format.js';
2
+ import { toBulletedList, toInlineCode } from '../lib/format.js';
3
+ import { getToolContract, getToolContracts } from '../lib/tools.js';
4
+ const GLOBAL_CONSTRAINTS = [
5
+ 'Diff budget: <= 120K chars.',
6
+ 'Structured output: tools return both `structuredContent` and JSON text `content`.',
7
+ ];
8
+ function formatParameterRow(entry) {
9
+ return `| ${entry.name} | ${entry.type} | ${entry.required ? 'Yes' : 'No'} | ${entry.constraints} | ${entry.description} |`;
10
+ }
11
+ function toToolInfoEntry(contract) {
12
+ const parameterRows = [
13
+ '| Param | Type | Required | Constraints | Description |',
14
+ '|-------|------|----------|-------------|-------------|',
15
+ ...contract.params.map((parameter) => formatParameterRow(parameter)),
16
+ ];
17
+ return {
18
+ name: contract.name,
19
+ purpose: contract.purpose,
20
+ model: contract.model,
21
+ thinkingLevel: formatThinkingLevel(contract.thinkingLevel),
22
+ timeout: formatTimeoutSeconds(contract.timeoutMs),
23
+ maxOutputTokens: formatUsNumber(contract.maxOutputTokens),
24
+ params: parameterRows.join('\n'),
25
+ outputShape: `\`${contract.outputShape}\``,
26
+ gotchas: contract.gotchas,
27
+ crossToolFlow: contract.crossToolFlow,
28
+ ...(contract.constraints
29
+ ? { constraints: contract.constraints }
30
+ : undefined),
31
+ };
32
+ }
33
+ const TOOL_INFO_ENTRIES = Object.fromEntries(getToolContracts().map((contract) => [
34
+ contract.name,
35
+ toToolInfoEntry(contract),
36
+ ]));
37
+ const TOOL_NAMES = Object.keys(TOOL_INFO_ENTRIES).sort((a, b) => a.localeCompare(b));
38
+ function collectToolConstraints(entries) {
39
+ const constraints = new Set();
40
+ for (const [toolName, entry] of Object.entries(entries)) {
41
+ for (const constraint of entry.constraints ?? []) {
42
+ constraints.add(`\`${toolName}\`: ${constraint}`);
43
+ }
44
+ }
45
+ return Array.from(constraints).sort((a, b) => a.localeCompare(b));
46
+ }
47
+ function formatToolInfo(entry) {
48
+ const constraints = [...entry.gotchas, ...entry.crossToolFlow];
49
+ return `# ${entry.name}
50
+ ${entry.purpose}
51
+
52
+ ## Model
53
+ \`${entry.model}\` (Thinking: ${entry.thinkingLevel}, Timeout: ${entry.timeout}, Tokens: ${entry.maxOutputTokens})
54
+
55
+ ## Parameters
56
+ ${entry.params}
57
+
58
+ ## Output
59
+ ${entry.outputShape}
60
+
61
+ ## Constraints
62
+ ${constraints.map((item) => `- ${item}`).join('\n')}
63
+ `;
64
+ }
65
+ function formatCompactToolRow(entry) {
66
+ return `| ${toInlineCode(entry.name)} | ${entry.model} | ${entry.timeout} | ${entry.maxOutputTokens} | ${entry.purpose} |`;
67
+ }
68
+ export function buildCoreContextPack() {
69
+ const rows = TOOL_NAMES.flatMap((toolName) => {
70
+ const entry = TOOL_INFO_ENTRIES[toolName];
71
+ return entry ? [formatCompactToolRow(entry)] : [];
72
+ });
73
+ return `# Core Context Pack
74
+
75
+ ## Server Essentials
76
+ - Domain: Gemini-powered MCP server for code analysis.
77
+ - Surface: 7 analysis tools + internal resources + guided prompts.
78
+ - Transport: stdio with task lifecycle support.
79
+
80
+ ## Tool Matrix
81
+ | Tool | Model | Timeout | Max Output Tokens | Purpose |
82
+ |------|-------|---------|-------------------|---------|
83
+ ${rows.join('\n')}
84
+
85
+ ## Shared Constraints
86
+ ${toBulletedList(getSharedConstraints())}
87
+ `;
88
+ }
89
+ export function getSharedConstraints() {
90
+ const toolConstraints = collectToolConstraints(TOOL_INFO_ENTRIES);
91
+ return [...GLOBAL_CONSTRAINTS, ...toolConstraints];
92
+ }
93
+ export function getToolInfoNames() {
94
+ return TOOL_NAMES;
95
+ }
96
+ export function getToolInfo(toolName) {
97
+ const entry = TOOL_INFO_ENTRIES[toolName];
98
+ if (!entry) {
99
+ return undefined;
100
+ }
101
+ return formatToolInfo(entry);
102
+ }
103
+ export function getToolPurpose(toolName) {
104
+ return getToolContract(toolName)?.purpose;
105
+ }
@@ -0,0 +1 @@
1
+ export declare function buildWorkflowGuide(): string;
@@ -0,0 +1,59 @@
1
+ import { toBulletedList } from '../lib/format.js';
2
+ import { getToolContracts } from '../lib/tools.js';
3
+ import { getSharedConstraints } from './tool-info.js';
4
+ function buildWorkflowToolReference() {
5
+ const contracts = getToolContracts();
6
+ return contracts
7
+ .map((c) => `### \`${c.name}\`\n- **Purpose:** ${c.purpose}\n- **Model:** ${c.model}\n- **Output:** \`${c.outputShape}\``)
8
+ .join('\n\n');
9
+ }
10
+ export function buildWorkflowGuide() {
11
+ return `# Workflow Reference
12
+
13
+ ## A: Full PR Review
14
+
15
+ 1. \`generate_review_summary\` → \`{overallRisk, keyChanges[], recommendation, stats}\`
16
+
17
+ ## B: Impact Assessment
18
+
19
+ 1. \`analyze_pr_impact\` → \`{severity, categories[], breakingChanges[], rollbackComplexity}\`
20
+ 2. \`generate_review_summary\` → complementary merge recommendation
21
+
22
+ > Use when categorization (breaking, api) or rollback assessment needed.
23
+
24
+ ## C: Test Coverage
25
+
26
+ 1. \`generate_test_plan\` → \`{testCases[], coverageSummary}\`
27
+ 2. Review by priority: \`must_have\` → \`should_have\` → \`nice_to_have\`
28
+
29
+ > Combine with review tools.
30
+
31
+ ## D: Complexity & Breaking Changes
32
+
33
+ 1. \`analyze_time_space_complexity\` → \`{timeComplexity, spaceComplexity, isDegradation}\`
34
+ 2. \`detect_api_breaking_changes\` → \`{hasBreakingChanges, breakingChanges[]}\`
35
+
36
+ > Use for algorithm or API changes. Diff-only input.
37
+
38
+ ## Shared Constraints
39
+ ${toBulletedList(getSharedConstraints())}
40
+
41
+ ## Tool Reference
42
+
43
+ ${buildWorkflowToolReference()}
44
+
45
+ ## Output Shape Reference
46
+
47
+ ### Finding
48
+ \`{severity, file, line, title, explanation, recommendation}\`
49
+
50
+ ### Search/Replace Block
51
+ \`{file, search, replace, explanation}\`
52
+
53
+ ### Test Case
54
+ \`{name, type, file, description, pseudoCode, priority}\`
55
+
56
+ ### Breaking Change
57
+ \`{element, natureOfChange, consumerImpact, suggestedMitigation}\`
58
+ `;
59
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ export declare const AnalyzePrImpactInputSchema: z.ZodObject<{
3
+ repository: z.ZodString;
4
+ language: z.ZodOptional<z.ZodString>;
5
+ }, z.core.$strict>;
6
+ export declare const GenerateReviewSummaryInputSchema: z.ZodObject<{
7
+ repository: z.ZodString;
8
+ language: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strict>;
10
+ export declare const GenerateTestPlanInputSchema: z.ZodObject<{
11
+ repository: z.ZodString;
12
+ language: z.ZodOptional<z.ZodString>;
13
+ testFramework: z.ZodOptional<z.ZodString>;
14
+ maxTestCases: z.ZodOptional<z.ZodNumber>;
15
+ }, z.core.$strict>;
16
+ export declare const AnalyzeComplexityInputSchema: z.ZodObject<{
17
+ language: z.ZodOptional<z.ZodString>;
18
+ }, z.core.$strict>;
19
+ export declare const DetectApiBreakingInputSchema: z.ZodObject<{
20
+ language: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strict>;
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod';
2
+ const INPUT_LIMITS = {
3
+ repository: { min: 1, max: 200 },
4
+ language: { min: 2, max: 32 },
5
+ testFramework: { min: 1, max: 50 },
6
+ maxTestCases: { min: 1, max: 30 },
7
+ };
8
+ function createBoundedString(min, max, description) {
9
+ return z.string().min(min).max(max).describe(description);
10
+ }
11
+ function createOptionalBoundedString(min, max, description) {
12
+ return createBoundedString(min, max, description).optional();
13
+ }
14
+ const LANGUAGE_DESCRIPTION = 'Primary language (e.g. TypeScript). Auto-infer from files.';
15
+ const REPOSITORY_DESCRIPTION = 'Repo ID (owner/repo). Auto-infer from git/dir.';
16
+ function createLanguageSchema() {
17
+ return createOptionalBoundedString(INPUT_LIMITS.language.min, INPUT_LIMITS.language.max, LANGUAGE_DESCRIPTION);
18
+ }
19
+ function createRepositorySchema() {
20
+ return createBoundedString(INPUT_LIMITS.repository.min, INPUT_LIMITS.repository.max, REPOSITORY_DESCRIPTION);
21
+ }
22
+ function createOptionalBoundedInteger(min, max, description) {
23
+ return z.number().int().min(min).max(max).optional().describe(description);
24
+ }
25
+ const RepositorySchema = createRepositorySchema();
26
+ const LanguageSchema = createLanguageSchema();
27
+ export const AnalyzePrImpactInputSchema = z.strictObject({
28
+ repository: RepositorySchema,
29
+ language: LanguageSchema,
30
+ });
31
+ export const GenerateReviewSummaryInputSchema = z.strictObject({
32
+ repository: RepositorySchema,
33
+ language: LanguageSchema,
34
+ });
35
+ export const GenerateTestPlanInputSchema = z.strictObject({
36
+ repository: RepositorySchema,
37
+ language: LanguageSchema,
38
+ testFramework: createOptionalBoundedString(INPUT_LIMITS.testFramework.min, INPUT_LIMITS.testFramework.max, 'Test framework (jest, pytest, etc). Auto-infer.'),
39
+ maxTestCases: createOptionalBoundedInteger(INPUT_LIMITS.maxTestCases.min, INPUT_LIMITS.maxTestCases.max, 'Max test cases (1-30). Default: 15.'),
40
+ });
41
+ export const AnalyzeComplexityInputSchema = z.strictObject({
42
+ language: LanguageSchema,
43
+ });
44
+ export const DetectApiBreakingInputSchema = z.strictObject({
45
+ language: LanguageSchema,
46
+ });