@shareworker/code-review-mcp 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflect.d.ts","sourceRoot":"","sources":["../src/reflect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAe,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3E;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,aAAa,CAAC,CAkGxB"}
@@ -0,0 +1,208 @@
1
+ import { getDiffForFile, getFileContent } from "./git.js";
2
+ import { parseFileDiffs, getAddedLineNumbers, splitAndNormalize, normalizeLine } from "./diff-parser.js";
3
+ /**
4
+ * Deterministic validation of a positioned comment.
5
+ * Returns keep or drop. Does not call LLM.
6
+ *
7
+ * Three checks:
8
+ * 1. line_in_hunk: Are start_line/end_line within the diff hunk's changed line range?
9
+ * 2. existing_code_found: Does the existing_code snippet actually exist in the file?
10
+ * (not applicable when existing_code is empty/undefined — passes by default)
11
+ * 3. existing_code_in_diff: Is at least one line of existing_code within the diff's changed lines?
12
+ * (not applicable when existing_code is empty/undefined — passes by default)
13
+ *
14
+ * Verdict: any check fails → drop; all pass → keep.
15
+ * Semantic-level reflection is the host LLM's responsibility.
16
+ */
17
+ export async function reflectComment(repo, input) {
18
+ const path = input.path.replace(/\\/g, "/");
19
+ const diffRef = input.diffRef ?? "HEAD";
20
+ const checks = [
21
+ { name: "line_in_hunk", passed: false },
22
+ { name: "existing_code_found", passed: false },
23
+ { name: "existing_code_in_diff", passed: false },
24
+ ];
25
+ // Read the diff for this file.
26
+ let diffText = "";
27
+ let hasDiff = true;
28
+ try {
29
+ diffText = await getDiffForFile(repo, diffRef, path);
30
+ }
31
+ catch {
32
+ hasDiff = false;
33
+ }
34
+ // Read the file content (try ref first, then worktree).
35
+ // Note: `git show <range>:<path>` returns empty string for range refs, so
36
+ // we treat empty content as "not found at ref" and fall back to worktree.
37
+ let fileContent = null;
38
+ try {
39
+ const atRef = await getFileContent(repo, diffRef, path);
40
+ fileContent = atRef && atRef.length > 0 ? atRef : null;
41
+ }
42
+ catch {
43
+ fileContent = null;
44
+ }
45
+ if (fileContent === null) {
46
+ try {
47
+ const atWorktree = await getFileContent(repo, "WORKTREE", path);
48
+ fileContent = atWorktree && atWorktree.length > 0 ? atWorktree : null;
49
+ }
50
+ catch {
51
+ fileContent = null;
52
+ }
53
+ }
54
+ // File not found → all checks fail, drop.
55
+ if (fileContent === null) {
56
+ return {
57
+ verdict: "drop",
58
+ reason: "file not found",
59
+ checks: [
60
+ { name: "line_in_hunk", passed: false },
61
+ { name: "existing_code_found", passed: false },
62
+ { name: "existing_code_in_diff", passed: false },
63
+ ],
64
+ };
65
+ }
66
+ // Check 1: line_in_hunk
67
+ // For new files (untracked / full-file-add), all lines are "changed" → passes vacuously.
68
+ // For positioning failure (0,0), fails.
69
+ // For workspace mode untracked files: `git diff HEAD -- <path>` returns empty string
70
+ // (file not tracked), but the file exists in worktree → treat as new file (all lines changed).
71
+ const isUntracked = hasDiff && !diffText && fileContent !== null && fileContent.length > 0;
72
+ let lineInHunk;
73
+ if (isUntracked) {
74
+ // Untracked file: all lines are "changed" → any valid line range passes.
75
+ lineInHunk = input.startLine > 0 && input.endLine >= input.startLine;
76
+ }
77
+ else {
78
+ lineInHunk = checkLineInHunk(input.startLine, input.endLine, diffText, hasDiff);
79
+ }
80
+ checks[0] = { name: "line_in_hunk", passed: lineInHunk };
81
+ // Determine if existing_code is applicable.
82
+ const hasExistingCode = !!input.existingCode && input.existingCode.trim() !== "";
83
+ // Check 2: existing_code_found
84
+ if (!hasExistingCode) {
85
+ checks[1] = { name: "existing_code_found", passed: true }; // not applicable
86
+ }
87
+ else {
88
+ const found = checkExistingCodeFound(input.existingCode, fileContent);
89
+ checks[1] = { name: "existing_code_found", passed: found };
90
+ }
91
+ // Check 3: existing_code_in_diff
92
+ if (!hasExistingCode) {
93
+ checks[2] = { name: "existing_code_in_diff", passed: true }; // not applicable
94
+ }
95
+ else if (isUntracked) {
96
+ // Untracked file: all code is "new" → all existing_code is in the diff.
97
+ checks[2] = { name: "existing_code_in_diff", passed: true };
98
+ }
99
+ else if (!hasDiff || !diffText) {
100
+ // No diff available (e.g., invalid ref) → can't verify → fail.
101
+ checks[2] = { name: "existing_code_in_diff", passed: false };
102
+ }
103
+ else {
104
+ const inDiff = checkExistingCodeInDiff(input.existingCode, diffText);
105
+ checks[2] = { name: "existing_code_in_diff", passed: inDiff };
106
+ }
107
+ // Verdict: any check fails → drop.
108
+ const allPass = checks.every((c) => c.passed);
109
+ return {
110
+ verdict: allPass ? "keep" : "drop",
111
+ reason: allPass ? "passed all checks" : describeFailure(checks),
112
+ checks,
113
+ };
114
+ }
115
+ /**
116
+ * Check if start_line/end_line fall within the diff's changed line range.
117
+ * - For new files (full-file-add, all lines added), all lines are "changed" → passes.
118
+ * - For positioning failure (0,0), fails.
119
+ * - For empty/invalid diff, fails.
120
+ */
121
+ function checkLineInHunk(startLine, endLine, diffText, hasDiff) {
122
+ if (startLine <= 0 || endLine < startLine)
123
+ return false;
124
+ if (!hasDiff || !diffText)
125
+ return false;
126
+ const fileDiffs = parseFileDiffs(diffText);
127
+ const fileDiff = fileDiffs[0];
128
+ if (!fileDiff)
129
+ return false;
130
+ // New file: all lines are "changed" → any line is in hunk.
131
+ if (fileDiff.isNew)
132
+ return true;
133
+ // Collect all added line numbers across hunks.
134
+ const addedLines = getAddedLineNumbers(fileDiff.hunks);
135
+ if (addedLines.size === 0)
136
+ return false;
137
+ // The comment's line range must overlap with at least one added line.
138
+ for (let line = startLine; line <= endLine; line++) {
139
+ if (addedLines.has(line))
140
+ return true;
141
+ }
142
+ return false;
143
+ }
144
+ /**
145
+ * Check if existing_code exists in the file content.
146
+ * Normalizes both sides (trim, strip diff markers) and checks if all
147
+ * non-blank target lines appear consecutively in the file.
148
+ */
149
+ function checkExistingCodeFound(existingCode, fileContent) {
150
+ const targetLines = splitAndNormalize(existingCode);
151
+ if (targetLines.length === 0)
152
+ return true;
153
+ const fileLines = fileContent.split("\n").map((l) => normalizeLine(l));
154
+ // Check for consecutive match (same logic as matchInFileContent but simpler — just existence).
155
+ for (let i = 0; i <= fileLines.length - targetLines.length; i++) {
156
+ let matched = true;
157
+ for (let j = 0; j < targetLines.length; j++) {
158
+ if (fileLines[i + j] !== targetLines[j]) {
159
+ matched = false;
160
+ break;
161
+ }
162
+ }
163
+ if (matched)
164
+ return true;
165
+ }
166
+ return false;
167
+ }
168
+ /**
169
+ * Check if at least one line of existing_code falls within the diff's changed lines.
170
+ * "Changed lines" = added lines (new-side) + deleted lines (old-side content).
171
+ * We check if any normalized target line matches any normalized added/deleted line.
172
+ */
173
+ function checkExistingCodeInDiff(existingCode, diffText) {
174
+ const targetLines = splitAndNormalize(existingCode);
175
+ if (targetLines.length === 0)
176
+ return true;
177
+ const fileDiffs = parseFileDiffs(diffText);
178
+ const fileDiff = fileDiffs[0];
179
+ if (!fileDiff)
180
+ return false;
181
+ // Collect normalized content of all changed lines (added + deleted).
182
+ const changedContents = new Set();
183
+ for (const hunk of fileDiff.hunks) {
184
+ for (const line of hunk.lines) {
185
+ if (line.type === "added" || line.type === "deleted") {
186
+ const n = normalizeLine(line.content);
187
+ if (n !== "")
188
+ changedContents.add(n);
189
+ }
190
+ }
191
+ }
192
+ // At least one target line must match a changed line.
193
+ for (const target of targetLines) {
194
+ if (changedContents.has(target))
195
+ return true;
196
+ }
197
+ return false;
198
+ }
199
+ /**
200
+ * Describe which check(s) failed for the drop reason.
201
+ */
202
+ function describeFailure(checks) {
203
+ const failed = checks.filter((c) => !c.passed).map((c) => c.name);
204
+ if (failed.length === 0)
205
+ return "passed all checks";
206
+ return `failed: ${failed.join(", ")}`;
207
+ }
208
+ //# sourceMappingURL=reflect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflect.js","sourceRoot":"","sources":["../src/reflect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { MatchRulesResult } from "./types.js";
2
+ /**
3
+ * Built-in generic default rule, aligned with open-code-review's default.md.
4
+ * Covers correctness, security, performance, maintainability, test coverage.
5
+ */
6
+ export declare const BUILT_IN_DEFAULT_RULE = "#### Correctness\nIs the logic correct? Are there missing boundary conditions?\nAre exceptions handled properly?\nIs it thread-safe in concurrent scenarios?\n\n#### Security\nAre there security vulnerabilities such as SQL injection or XSS?\nIs sensitive information handled correctly?\nIs permission validation complete?\n\n#### Performance\nAre there obvious performance issues (e.g., N+1 queries, unnecessary loops)?\nAre resources properly released?\n\n#### Maintainability\nIs the code clear and easy to understand?\nDo names accurately express intent?\nDoes it follow the project's existing code style and architecture patterns?\n\n#### Test Coverage\nDo critical logic paths have corresponding test cases?\nDo test cases cover boundary conditions?";
7
+ /**
8
+ * Match review rules for a file path, returning a ready-to-use prompt_section.
9
+ *
10
+ * Priority: repo .code-review/rules.json > home ~/.code-review/rules.json > built-in default.
11
+ * First match wins at each layer; the first layer with a match takes precedence.
12
+ */
13
+ export declare function matchRules(repo: string, path: string): Promise<MatchRulesResult>;
14
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,YAAY,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,svBAqBO,CAAC;AA0C1C;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,CAAC,CAsC3B"}
package/dist/rules.js ADDED
@@ -0,0 +1,123 @@
1
+ import { minimatch } from "minimatch";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ /**
6
+ * Built-in generic default rule, aligned with open-code-review's default.md.
7
+ * Covers correctness, security, performance, maintainability, test coverage.
8
+ */
9
+ export const BUILT_IN_DEFAULT_RULE = `#### Correctness
10
+ Is the logic correct? Are there missing boundary conditions?
11
+ Are exceptions handled properly?
12
+ Is it thread-safe in concurrent scenarios?
13
+
14
+ #### Security
15
+ Are there security vulnerabilities such as SQL injection or XSS?
16
+ Is sensitive information handled correctly?
17
+ Is permission validation complete?
18
+
19
+ #### Performance
20
+ Are there obvious performance issues (e.g., N+1 queries, unnecessary loops)?
21
+ Are resources properly released?
22
+
23
+ #### Maintainability
24
+ Is the code clear and easy to understand?
25
+ Do names accurately express intent?
26
+ Does it follow the project's existing code style and architecture patterns?
27
+
28
+ #### Test Coverage
29
+ Do critical logic paths have corresponding test cases?
30
+ Do test cases cover boundary conditions?`;
31
+ /**
32
+ * Load a rules.json file, returning null if missing or unparseable.
33
+ * Parse failures log a warning and return null (caller falls back to default).
34
+ * Accepts both `path` and `pattern` as the glob field key (spec uses `path`).
35
+ */
36
+ async function loadRulesFile(dir) {
37
+ const filePath = join(dir, ".code-review", "rules.json");
38
+ try {
39
+ const content = await readFile(filePath, "utf8");
40
+ const raw = JSON.parse(content);
41
+ // Normalize: rules entries may use `path` or `pattern` — map to `pattern`.
42
+ if (raw?.rules && Array.isArray(raw.rules)) {
43
+ raw.rules = raw.rules.map((r) => ({
44
+ pattern: r.pattern ?? r.path,
45
+ rule: r.rule,
46
+ }));
47
+ }
48
+ return raw;
49
+ }
50
+ catch (err) {
51
+ if (err?.code === "ENOENT")
52
+ return null; // missing: silent fallback
53
+ // Parse error or other failure: warn and fall back.
54
+ console.warn(`[code-review-mcp] Failed to parse ${filePath}: ${err?.message ?? err}. Falling back to default rules.`);
55
+ return null;
56
+ }
57
+ }
58
+ /**
59
+ * Match a file path against a list of path rules (first match wins).
60
+ * Case-insensitive, supports glob patterns including ** and brace expansion.
61
+ */
62
+ function matchPathRules(path, rules) {
63
+ const lowerPath = path.toLowerCase();
64
+ for (const rule of rules) {
65
+ if (minimatch(lowerPath, rule.pattern.toLowerCase())) {
66
+ return rule;
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ /**
72
+ * Match review rules for a file path, returning a ready-to-use prompt_section.
73
+ *
74
+ * Priority: repo .code-review/rules.json > home ~/.code-review/rules.json > built-in default.
75
+ * First match wins at each layer; the first layer with a match takes precedence.
76
+ */
77
+ export async function matchRules(repo, path) {
78
+ const normalizedPath = path.replace(/\\/g, "/");
79
+ // Layer 1: repo rules.
80
+ const repoConfig = await loadRulesFile(repo);
81
+ if (repoConfig?.rules) {
82
+ const matched = matchPathRules(normalizedPath, repoConfig.rules);
83
+ if (matched) {
84
+ return {
85
+ path: normalizedPath,
86
+ matchedRules: [matched],
87
+ promptSection: formatPromptSection([matched.rule]),
88
+ usedDefault: false,
89
+ };
90
+ }
91
+ }
92
+ // Layer 2: home rules.
93
+ const homeConfig = await loadRulesFile(homedir());
94
+ if (homeConfig?.rules) {
95
+ const matched = matchPathRules(normalizedPath, homeConfig.rules);
96
+ if (matched) {
97
+ return {
98
+ path: normalizedPath,
99
+ matchedRules: [matched],
100
+ promptSection: formatPromptSection([matched.rule]),
101
+ usedDefault: false,
102
+ };
103
+ }
104
+ }
105
+ // Layer 3: built-in default.
106
+ return {
107
+ path: normalizedPath,
108
+ matchedRules: [],
109
+ promptSection: formatPromptSection([BUILT_IN_DEFAULT_RULE]),
110
+ usedDefault: true,
111
+ };
112
+ }
113
+ /**
114
+ * Format rule texts into a single prompt_section string.
115
+ */
116
+ function formatPromptSection(rules) {
117
+ if (rules.length === 0)
118
+ return "";
119
+ if (rules.length === 1)
120
+ return rules[0];
121
+ return rules.map((r) => `- ${r}`).join("\n");
122
+ }
123
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /** A single line within a diff hunk. */
2
+ export type HunkLineType = "context" | "added" | "deleted";
3
+ export interface HunkLine {
4
+ type: HunkLineType;
5
+ /** Content without the leading +/-/space diff marker. */
6
+ content: string;
7
+ }
8
+ /** One @@ ... @@ block in a unified diff. */
9
+ export interface Hunk {
10
+ /** Starting line in the old file (1-indexed). */
11
+ oldStart: number;
12
+ oldCount: number;
13
+ /** Starting line in the new file (1-indexed). */
14
+ newStart: number;
15
+ newCount: number;
16
+ lines: HunkLine[];
17
+ }
18
+ /** A parsed diff for a single file. */
19
+ export interface FileDiff {
20
+ oldPath: string;
21
+ newPath: string;
22
+ /** Raw unified diff text for this file (including headers). */
23
+ diff: string;
24
+ hunks: Hunk[];
25
+ isBinary: boolean;
26
+ isNew: boolean;
27
+ isDeleted: boolean;
28
+ isRenamed: boolean;
29
+ insertions: number;
30
+ deletions: number;
31
+ }
32
+ /** A file selected for review. */
33
+ export interface ReviewTarget {
34
+ path: string;
35
+ diff: string;
36
+ additions: number;
37
+ deletions: number;
38
+ status: "added" | "modified" | "deleted" | "renamed";
39
+ }
40
+ /** A path -> rule entry. */
41
+ export interface PathRule {
42
+ pattern: string;
43
+ rule: string;
44
+ }
45
+ /** Filter configuration from .code-review/rules.json. */
46
+ export interface FilterConfig {
47
+ include: string[];
48
+ exclude: string[];
49
+ }
50
+ /** Rules configuration from .code-review/rules.json. */
51
+ export interface RulesConfig {
52
+ filters?: FilterConfig;
53
+ rules?: PathRule[];
54
+ }
55
+ /** A bundle of related files reviewed together. */
56
+ export interface BundleFile {
57
+ path: string;
58
+ diff: string;
59
+ additions: number;
60
+ deletions: number;
61
+ }
62
+ export type BundleReason = "test_source_pair" | "i18n_variants" | "single_file";
63
+ export interface FileBundle {
64
+ id: string;
65
+ files: BundleFile[];
66
+ totalChars: number;
67
+ bundleReason: BundleReason;
68
+ }
69
+ /** Input for position_comment. */
70
+ export interface PositionInput {
71
+ path: string;
72
+ content: string;
73
+ existingCode?: string;
74
+ suggestionCode?: string;
75
+ hintLine?: number;
76
+ diffRef?: string;
77
+ repo?: string;
78
+ }
79
+ export type LocatedBy = "text_match" | "hunk_align" | "failed";
80
+ export interface PositionResult {
81
+ path: string;
82
+ startLine: number;
83
+ endLine: number;
84
+ locatedBy: LocatedBy;
85
+ }
86
+ /** Input for reflect_comment. */
87
+ export interface ReflectInput {
88
+ path: string;
89
+ content: string;
90
+ startLine: number;
91
+ endLine: number;
92
+ existingCode?: string;
93
+ diffRef?: string;
94
+ repo?: string;
95
+ }
96
+ export interface CheckResult {
97
+ name: "line_in_hunk" | "existing_code_found" | "existing_code_in_diff";
98
+ passed: boolean;
99
+ }
100
+ export interface ReflectResult {
101
+ verdict: "keep" | "drop";
102
+ reason: string;
103
+ checks: CheckResult[];
104
+ }
105
+ /** Output of get_review_targets. */
106
+ export interface ReviewTargetsResult {
107
+ diffRef: string;
108
+ files: ReviewTarget[];
109
+ totalFiles: number;
110
+ filteredOut: number;
111
+ }
112
+ /** Output of match_rules. */
113
+ export interface MatchRulesResult {
114
+ path: string;
115
+ matchedRules: PathRule[];
116
+ promptSection: string;
117
+ usedDefault: boolean;
118
+ }
119
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // Shared types for the code-review MCP server.
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+CAA+C"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@shareworker/code-review-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server exposing the deterministic engineering layer of code review as tools for host agents (Claude Code, Codex, Devin). The server never calls an LLM.",
5
+ "type": "module",
6
+ "bin": {
7
+ "code-review-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skills",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "start": "node dist/index.js",
21
+ "prepublishOnly": "tsc && vitest run"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "code-review",
26
+ "model-context-protocol",
27
+ "claude",
28
+ "codex",
29
+ "devin",
30
+ "ai",
31
+ "llm"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/shareworker/code-review-mcp.git"
36
+ },
37
+ "homepage": "https://github.com/shareworker/code-review-mcp#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/shareworker/code-review-mcp/issues"
40
+ },
41
+ "author": "shareworker",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.0.4",
45
+ "diff": "^7.0.0",
46
+ "minimatch": "^9.0.9",
47
+ "simple-git": "^3.27.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/diff": "^6.0.0",
51
+ "@types/node": "^22.10.0",
52
+ "typescript": "^5.7.0",
53
+ "vitest": "^2.1.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=20"
57
+ }
58
+ }