@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.
- package/README.md +437 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +72 -0
- package/dist/lib/concurrency.d.ts +13 -0
- package/dist/lib/concurrency.js +77 -0
- package/dist/lib/config.d.ts +43 -0
- package/dist/lib/config.js +87 -0
- package/dist/lib/diff.d.ts +49 -0
- package/dist/lib/diff.js +241 -0
- package/dist/lib/errors.d.ts +8 -0
- package/dist/lib/errors.js +69 -0
- package/dist/lib/format.d.ts +14 -0
- package/dist/lib/format.js +33 -0
- package/dist/lib/gemini.d.ts +45 -0
- package/dist/lib/gemini.js +833 -0
- package/dist/lib/progress.d.ts +72 -0
- package/dist/lib/progress.js +204 -0
- package/dist/lib/tools.d.ts +274 -0
- package/dist/lib/tools.js +646 -0
- package/dist/prompts/index.d.ts +11 -0
- package/dist/prompts/index.js +96 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.js +115 -0
- package/dist/resources/instructions.d.ts +1 -0
- package/dist/resources/instructions.js +71 -0
- package/dist/resources/server-config.d.ts +1 -0
- package/dist/resources/server-config.js +75 -0
- package/dist/resources/tool-catalog.d.ts +1 -0
- package/dist/resources/tool-catalog.js +30 -0
- package/dist/resources/tool-info.d.ts +5 -0
- package/dist/resources/tool-info.js +105 -0
- package/dist/resources/workflows.d.ts +1 -0
- package/dist/resources/workflows.js +59 -0
- package/dist/schemas/inputs.d.ts +21 -0
- package/dist/schemas/inputs.js +46 -0
- package/dist/schemas/outputs.d.ts +121 -0
- package/dist/schemas/outputs.js +162 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +88 -0
- package/dist/tools/analyze-complexity.d.ts +2 -0
- package/dist/tools/analyze-complexity.js +50 -0
- package/dist/tools/analyze-pr-impact.d.ts +2 -0
- package/dist/tools/analyze-pr-impact.js +62 -0
- package/dist/tools/detect-api-breaking.d.ts +2 -0
- package/dist/tools/detect-api-breaking.js +49 -0
- package/dist/tools/generate-diff.d.ts +2 -0
- package/dist/tools/generate-diff.js +140 -0
- package/dist/tools/generate-review-summary.d.ts +2 -0
- package/dist/tools/generate-review-summary.js +71 -0
- package/dist/tools/generate-test-plan.d.ts +2 -0
- package/dist/tools/generate-test-plan.js +67 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +19 -0
- package/package.json +79 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { cleanDiff, computeDiffStatsFromFiles, DIFF_RESOURCE_URI, isEmptyDiff, NOISY_EXCLUDE_PATHSPECS, parseDiffFiles, storeDiff, } from '../lib/diff.js';
|
|
5
|
+
import { wrapToolHandler } from '../lib/tools.js';
|
|
6
|
+
import { createErrorToolResponse, createToolResponse } from '../lib/tools.js';
|
|
7
|
+
import { DefaultOutputSchema } from '../schemas/outputs.js';
|
|
8
|
+
const GIT_TIMEOUT_MS = 30_000;
|
|
9
|
+
const GIT_MAX_BUFFER = 10 * 1024 * 1024; // 10 MB
|
|
10
|
+
const MAX_GIT_ROOT_CACHE_SIZE = 50;
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
const gitRootByCwd = new Map();
|
|
13
|
+
async function findGitRoot(cwd = process.cwd()) {
|
|
14
|
+
const cached = gitRootByCwd.get(cwd);
|
|
15
|
+
if (cached) {
|
|
16
|
+
return cached;
|
|
17
|
+
}
|
|
18
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', '--show-toplevel'], {
|
|
19
|
+
cwd,
|
|
20
|
+
encoding: 'utf8',
|
|
21
|
+
});
|
|
22
|
+
const gitRoot = stdout.trim();
|
|
23
|
+
cacheGitRoot(cwd, gitRoot);
|
|
24
|
+
return gitRoot;
|
|
25
|
+
}
|
|
26
|
+
function cacheGitRoot(cwd, gitRoot) {
|
|
27
|
+
if (gitRootByCwd.size >= MAX_GIT_ROOT_CACHE_SIZE) {
|
|
28
|
+
gitRootByCwd.clear();
|
|
29
|
+
}
|
|
30
|
+
gitRootByCwd.set(cwd, gitRoot);
|
|
31
|
+
}
|
|
32
|
+
function buildGitArgs(mode) {
|
|
33
|
+
const args = ['diff', '--no-color', '--no-ext-diff'];
|
|
34
|
+
if (mode === 'staged') {
|
|
35
|
+
args.push('--cached');
|
|
36
|
+
}
|
|
37
|
+
// '--' separates flags from pathspecs. Everything after it is a
|
|
38
|
+
// pathspec, never interpreted as a flag — prevents flag injection.
|
|
39
|
+
args.push('--', ...NOISY_EXCLUDE_PATHSPECS);
|
|
40
|
+
return args;
|
|
41
|
+
}
|
|
42
|
+
function describeModeHint(mode) {
|
|
43
|
+
return mode === 'staged'
|
|
44
|
+
? 'staged with git add'
|
|
45
|
+
: 'modified but not yet staged (git add)';
|
|
46
|
+
}
|
|
47
|
+
function classifyGitError(err) {
|
|
48
|
+
if (err.code === 'ENOENT') {
|
|
49
|
+
return { retryable: false, kind: 'validation' };
|
|
50
|
+
}
|
|
51
|
+
if (err.killed === true) {
|
|
52
|
+
return { retryable: false, kind: 'timeout' };
|
|
53
|
+
}
|
|
54
|
+
if (typeof err.code === 'number') {
|
|
55
|
+
const stderr = err.stderr?.toLowerCase() ?? '';
|
|
56
|
+
if (stderr.includes('not a git repository') ||
|
|
57
|
+
stderr.includes('not a git repo')) {
|
|
58
|
+
return { retryable: false, kind: 'validation' };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { retryable: false, kind: 'internal' };
|
|
62
|
+
}
|
|
63
|
+
function formatGitFailureMessage(err) {
|
|
64
|
+
if (typeof err.code === 'number') {
|
|
65
|
+
const stderr = err.stderr?.trim() ?? 'unknown error';
|
|
66
|
+
return `git exited with code ${String(err.code)}: ${stderr}. Ensure the working directory is a git repository.`;
|
|
67
|
+
}
|
|
68
|
+
return `Failed to run git: ${err.message}. Ensure git is installed and the working directory is a git repository.`;
|
|
69
|
+
}
|
|
70
|
+
async function runGitDiff(mode) {
|
|
71
|
+
const gitRoot = await findGitRoot();
|
|
72
|
+
const args = buildGitArgs(mode);
|
|
73
|
+
const { stdout } = await execFileAsync('git', args, {
|
|
74
|
+
cwd: gitRoot,
|
|
75
|
+
encoding: 'utf8',
|
|
76
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
77
|
+
timeout: GIT_TIMEOUT_MS,
|
|
78
|
+
});
|
|
79
|
+
return cleanDiff(stdout);
|
|
80
|
+
}
|
|
81
|
+
function buildGitErrorResponse(error) {
|
|
82
|
+
const err = error;
|
|
83
|
+
return createErrorToolResponse('E_GENERATE_DIFF', formatGitFailureMessage(err), undefined, classifyGitError(err));
|
|
84
|
+
}
|
|
85
|
+
async function generateDiffToolResponse(mode) {
|
|
86
|
+
try {
|
|
87
|
+
const diff = await runGitDiff(mode);
|
|
88
|
+
if (isEmptyDiff(diff)) {
|
|
89
|
+
return createNoChangesResponse(mode);
|
|
90
|
+
}
|
|
91
|
+
return createSuccessResponse(diff, mode);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return buildGitErrorResponse(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function createNoChangesResponse(mode) {
|
|
98
|
+
return createErrorToolResponse('E_NO_CHANGES', `No ${mode} changes found in the current branch. Make sure you have changes that are ${describeModeHint(mode)}.`, undefined, { retryable: false, kind: 'validation' });
|
|
99
|
+
}
|
|
100
|
+
function createSuccessResponse(diff, mode) {
|
|
101
|
+
const parsedFiles = parseDiffFiles(diff);
|
|
102
|
+
const stats = computeDiffStatsFromFiles(parsedFiles);
|
|
103
|
+
const generatedAt = new Date().toISOString();
|
|
104
|
+
storeDiff({ diff, parsedFiles, stats, generatedAt, mode });
|
|
105
|
+
const summary = `Diff cached: ${stats.files} files (+${stats.added}, -${stats.deleted})`;
|
|
106
|
+
return createToolResponse({
|
|
107
|
+
ok: true,
|
|
108
|
+
result: {
|
|
109
|
+
diffRef: DIFF_RESOURCE_URI,
|
|
110
|
+
stats,
|
|
111
|
+
generatedAt,
|
|
112
|
+
mode,
|
|
113
|
+
message: summary,
|
|
114
|
+
},
|
|
115
|
+
}, summary);
|
|
116
|
+
}
|
|
117
|
+
export function registerGenerateDiffTool(server) {
|
|
118
|
+
server.registerTool('generate_diff', {
|
|
119
|
+
title: 'Generate Diff',
|
|
120
|
+
description: 'Generate a diff of the current branch working changes and cache it for all review tools. You MUST call this tool before calling any other review tool. Use "unstaged" for working-tree changes not yet staged, or "staged" for changes already added with git add.',
|
|
121
|
+
inputSchema: z.strictObject({
|
|
122
|
+
mode: z
|
|
123
|
+
.enum(['unstaged', 'staged'])
|
|
124
|
+
.describe('"unstaged": working-tree changes not yet staged. "staged": changes added to the index with git add.'),
|
|
125
|
+
}),
|
|
126
|
+
outputSchema: DefaultOutputSchema,
|
|
127
|
+
annotations: {
|
|
128
|
+
readOnlyHint: false,
|
|
129
|
+
idempotentHint: true,
|
|
130
|
+
openWorldHint: false,
|
|
131
|
+
destructiveHint: false,
|
|
132
|
+
},
|
|
133
|
+
}, wrapToolHandler({
|
|
134
|
+
toolName: 'generate_diff',
|
|
135
|
+
progressContext: (input) => input.mode,
|
|
136
|
+
}, async (input) => {
|
|
137
|
+
const { mode } = input;
|
|
138
|
+
return generateDiffToolResponse(mode);
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { formatLanguageSegment } from '../lib/format.js';
|
|
2
|
+
import { getDiffContextSnapshot } from '../lib/tools.js';
|
|
3
|
+
import { buildStructuredToolExecutionOptions, registerStructuredToolTask, requireToolContract, } from '../lib/tools.js';
|
|
4
|
+
import { GenerateReviewSummaryInputSchema } from '../schemas/inputs.js';
|
|
5
|
+
import { ReviewSummaryResultSchema } from '../schemas/outputs.js';
|
|
6
|
+
const ReviewSummaryModelSchema = ReviewSummaryResultSchema.omit({
|
|
7
|
+
stats: true,
|
|
8
|
+
});
|
|
9
|
+
const TOOL_CONTRACT = requireToolContract('generate_review_summary');
|
|
10
|
+
const SYSTEM_INSTRUCTION = `
|
|
11
|
+
<role>
|
|
12
|
+
Senior Code Reviewer.
|
|
13
|
+
You are a pragmatic engineer focused on stability and maintainability.
|
|
14
|
+
</role>
|
|
15
|
+
|
|
16
|
+
<task>
|
|
17
|
+
Summarize the pull request based on the diff:
|
|
18
|
+
- Assess overall risk (low/medium/high).
|
|
19
|
+
- Highlight key logic/behavior changes.
|
|
20
|
+
- Recommend action: merge, squash, or block.
|
|
21
|
+
</task>
|
|
22
|
+
|
|
23
|
+
<constraints>
|
|
24
|
+
- Focus on logic and behavior; ignore style, formatting, and typos.
|
|
25
|
+
- Be concise and actionable.
|
|
26
|
+
- Return valid JSON matching the schema.
|
|
27
|
+
</constraints>
|
|
28
|
+
`;
|
|
29
|
+
export function registerGenerateReviewSummaryTool(server) {
|
|
30
|
+
registerStructuredToolTask(server, {
|
|
31
|
+
name: 'generate_review_summary',
|
|
32
|
+
title: 'Generate Review Summary',
|
|
33
|
+
description: 'Summarize diff and risk level. Prerequisite: generate_diff. Auto-infer repo/language.',
|
|
34
|
+
inputSchema: GenerateReviewSummaryInputSchema,
|
|
35
|
+
fullInputSchema: GenerateReviewSummaryInputSchema,
|
|
36
|
+
resultSchema: ReviewSummaryModelSchema,
|
|
37
|
+
errorCode: 'E_REVIEW_SUMMARY',
|
|
38
|
+
...buildStructuredToolExecutionOptions(TOOL_CONTRACT),
|
|
39
|
+
requiresDiff: true,
|
|
40
|
+
progressContext: (input) => input.repository,
|
|
41
|
+
formatOutcome: (result) => `risk: ${result.overallRisk}`,
|
|
42
|
+
transformResult: (_input, result, ctx) => {
|
|
43
|
+
const { stats } = getDiffContextSnapshot(ctx);
|
|
44
|
+
return {
|
|
45
|
+
...result,
|
|
46
|
+
stats: {
|
|
47
|
+
filesChanged: stats.files,
|
|
48
|
+
linesAdded: stats.added,
|
|
49
|
+
linesRemoved: stats.deleted,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
formatOutput: (result) => `${result.summary}\nRecommendation: ${result.recommendation}`,
|
|
54
|
+
buildPrompt: (input, ctx) => {
|
|
55
|
+
const { diff, stats } = getDiffContextSnapshot(ctx);
|
|
56
|
+
const languageSegment = formatLanguageSegment(input.language);
|
|
57
|
+
return {
|
|
58
|
+
systemInstruction: SYSTEM_INSTRUCTION,
|
|
59
|
+
prompt: `
|
|
60
|
+
Repository: ${input.repository}${languageSegment}
|
|
61
|
+
Stats: ${stats.files} files, +${stats.added}, -${stats.deleted}
|
|
62
|
+
|
|
63
|
+
Diff:
|
|
64
|
+
${diff}
|
|
65
|
+
|
|
66
|
+
Based on the diff and stats above, summarize the PR and provide a merge recommendation.
|
|
67
|
+
`,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { computeDiffStatsAndPathsFromFiles } from '../lib/diff.js';
|
|
2
|
+
import { formatOptionalLines } from '../lib/format.js';
|
|
3
|
+
import { getDiffContextSnapshot } from '../lib/tools.js';
|
|
4
|
+
import { buildStructuredToolExecutionOptions, registerStructuredToolTask, requireToolContract, } from '../lib/tools.js';
|
|
5
|
+
import { GenerateTestPlanInputSchema } from '../schemas/inputs.js';
|
|
6
|
+
import { TestPlanResultSchema } from '../schemas/outputs.js';
|
|
7
|
+
const SYSTEM_INSTRUCTION = `
|
|
8
|
+
<role>
|
|
9
|
+
QA Automation Architect.
|
|
10
|
+
You are an expert in test strategy and coverage analysis.
|
|
11
|
+
</role>
|
|
12
|
+
|
|
13
|
+
<task>
|
|
14
|
+
Generate a prioritized test plan for the provided diff:
|
|
15
|
+
- Focus on negative cases, edge cases, and boundary conditions.
|
|
16
|
+
- Target branch coverage and integration points.
|
|
17
|
+
</task>
|
|
18
|
+
|
|
19
|
+
<constraints>
|
|
20
|
+
- Focus on observable behavior changes.
|
|
21
|
+
- Ignore internal refactors that do not affect contract.
|
|
22
|
+
- Return valid JSON matching the schema.
|
|
23
|
+
</constraints>
|
|
24
|
+
`;
|
|
25
|
+
const TOOL_CONTRACT = requireToolContract('generate_test_plan');
|
|
26
|
+
export function registerGenerateTestPlanTool(server) {
|
|
27
|
+
registerStructuredToolTask(server, {
|
|
28
|
+
name: 'generate_test_plan',
|
|
29
|
+
title: 'Generate Test Plan',
|
|
30
|
+
description: 'Generate test cases. Prerequisite: generate_diff. Auto-infer repo/language/framework.',
|
|
31
|
+
inputSchema: GenerateTestPlanInputSchema,
|
|
32
|
+
fullInputSchema: GenerateTestPlanInputSchema,
|
|
33
|
+
resultSchema: TestPlanResultSchema,
|
|
34
|
+
errorCode: 'E_GENERATE_TEST_PLAN',
|
|
35
|
+
...buildStructuredToolExecutionOptions(TOOL_CONTRACT),
|
|
36
|
+
requiresDiff: true,
|
|
37
|
+
progressContext: (input) => input.repository,
|
|
38
|
+
formatOutcome: (result) => `${result.testCases.length} test cases`,
|
|
39
|
+
formatOutput: (result) => `${result.summary}\n${result.testCases.length} test cases.`,
|
|
40
|
+
transformResult: (input, result) => {
|
|
41
|
+
const cappedTestCases = result.testCases.slice(0, input.maxTestCases ?? result.testCases.length);
|
|
42
|
+
return { ...result, testCases: cappedTestCases };
|
|
43
|
+
},
|
|
44
|
+
buildPrompt: (input, ctx) => {
|
|
45
|
+
const { diff, parsedFiles } = getDiffContextSnapshot(ctx);
|
|
46
|
+
const { stats, paths } = computeDiffStatsAndPathsFromFiles(parsedFiles);
|
|
47
|
+
const optionalLines = formatOptionalLines([
|
|
48
|
+
{ label: 'Language', value: input.language },
|
|
49
|
+
{ label: 'Test Framework', value: input.testFramework },
|
|
50
|
+
{ label: 'Max Test Cases', value: input.maxTestCases },
|
|
51
|
+
]);
|
|
52
|
+
return {
|
|
53
|
+
systemInstruction: SYSTEM_INSTRUCTION,
|
|
54
|
+
prompt: `
|
|
55
|
+
Repository: ${input.repository}${optionalLines}
|
|
56
|
+
Stats: ${stats.files} files, +${stats.added}, -${stats.deleted}
|
|
57
|
+
Changed Files: ${paths.join(', ')}
|
|
58
|
+
|
|
59
|
+
Diff:
|
|
60
|
+
${diff}
|
|
61
|
+
|
|
62
|
+
Based on the diff and stats above, generate an actionable test plan.
|
|
63
|
+
`,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { registerAnalyzeComplexityTool } from './analyze-complexity.js';
|
|
2
|
+
import { registerAnalyzePrImpactTool } from './analyze-pr-impact.js';
|
|
3
|
+
import { registerDetectApiBreakingTool } from './detect-api-breaking.js';
|
|
4
|
+
import { registerGenerateDiffTool } from './generate-diff.js';
|
|
5
|
+
import { registerGenerateReviewSummaryTool } from './generate-review-summary.js';
|
|
6
|
+
import { registerGenerateTestPlanTool } from './generate-test-plan.js';
|
|
7
|
+
const TOOL_REGISTRARS = [
|
|
8
|
+
registerGenerateDiffTool,
|
|
9
|
+
registerAnalyzePrImpactTool,
|
|
10
|
+
registerGenerateReviewSummaryTool,
|
|
11
|
+
registerGenerateTestPlanTool,
|
|
12
|
+
registerAnalyzeComplexityTool,
|
|
13
|
+
registerDetectApiBreakingTool,
|
|
14
|
+
];
|
|
15
|
+
export function registerAllTools(server) {
|
|
16
|
+
for (const registrar of TOOL_REGISTRARS) {
|
|
17
|
+
registrar(server);
|
|
18
|
+
}
|
|
19
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@j0hanz/code-assistant",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"mcpName": "io.github.j0hanz/code-assistant",
|
|
5
|
+
"description": "Gemini-powered MCP server for code analysis.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"code-assistant-mcp": "dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"author": "j0hanz",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/j0hanz/code-assistant.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/j0hanz/code-assistant#readme",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"clean": "node scripts/tasks.mjs clean",
|
|
32
|
+
"build": "node scripts/tasks.mjs build",
|
|
33
|
+
"copy:assets": "node scripts/tasks.mjs copy:assets",
|
|
34
|
+
"prepare": "npm run build",
|
|
35
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
36
|
+
"dev:run": "node --env-file=.env --watch dist/index.js",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"format": "prettier --write .",
|
|
39
|
+
"type-check": "node scripts/tasks.mjs type-check",
|
|
40
|
+
"type-check:src": "node node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
|
|
41
|
+
"type-check:tests": "node node_modules/typescript/bin/tsc -p tsconfig.test.json --noEmit",
|
|
42
|
+
"type-check:diagnostics": "tsc --noEmit --extendedDiagnostics",
|
|
43
|
+
"type-check:trace": "node -e \"require('fs').rmSync('.ts-trace',{recursive:true,force:true})\" && tsc --noEmit --generateTrace .ts-trace",
|
|
44
|
+
"lint": "eslint .",
|
|
45
|
+
"lint:tests": "eslint src/__tests__",
|
|
46
|
+
"lint:fix": "eslint . --fix",
|
|
47
|
+
"test": "node scripts/tasks.mjs test",
|
|
48
|
+
"test:fast": "node --test --import tsx/esm src/__tests__/**/*.test.ts node-tests/**/*.test.ts",
|
|
49
|
+
"test:coverage": "node scripts/tasks.mjs test --coverage",
|
|
50
|
+
"knip": "knip",
|
|
51
|
+
"knip:fix": "knip --fix",
|
|
52
|
+
"inspector": "npm run build && npx -y @modelcontextprotocol/inspector node dist/index.js",
|
|
53
|
+
"prepublishOnly": "npm run lint && npm run type-check && npm run build"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@google/genai": "^1.43.0",
|
|
57
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
58
|
+
"parse-diff": "^0.11.1",
|
|
59
|
+
"zod": "^4.3.6"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@eslint/js": "^10.0.1",
|
|
63
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
64
|
+
"@types/node": "^24",
|
|
65
|
+
"eslint": "^10.0.2",
|
|
66
|
+
"eslint-config-prettier": "^10.1.8",
|
|
67
|
+
"eslint-plugin-de-morgan": "^2.1.1",
|
|
68
|
+
"eslint-plugin-depend": "^1.4.0",
|
|
69
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
70
|
+
"knip": "^5.85.0",
|
|
71
|
+
"prettier": "^3.8.1",
|
|
72
|
+
"tsx": "^4.21.0",
|
|
73
|
+
"typescript": "^5.9.3",
|
|
74
|
+
"typescript-eslint": "^8.56.1"
|
|
75
|
+
},
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": ">=24"
|
|
78
|
+
}
|
|
79
|
+
}
|