ai-code-reviewer-plus 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.
package/dist/cli.js ADDED
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+
3
+ // src/cli.ts
4
+ var import_node_util2 = require("util");
5
+
6
+ // src/collectors/diff-collector.ts
7
+ var import_node_child_process = require("child_process");
8
+ var import_node_util = require("util");
9
+
10
+ // src/errors.ts
11
+ var GitCommandError = class _GitCommandError extends Error {
12
+ constructor(options) {
13
+ const msg = options.message ?? `git ${[options.command, ...options.args ?? []].join(" ")} failed${options.exitCode != null ? ` (exit ${options.exitCode})` : ""}`;
14
+ super(msg);
15
+ this.name = "GitCommandError";
16
+ this.command = options.command ?? "git";
17
+ this.args = options.args ?? [];
18
+ this.exitCode = options.exitCode;
19
+ this.cwd = options.cwd;
20
+ if (options.cause && Error.captureStackTrace) {
21
+ Error.captureStackTrace(this, _GitCommandError);
22
+ }
23
+ }
24
+ };
25
+
26
+ // src/collectors/diff-collector.ts
27
+ var exec = (0, import_node_util.promisify)(import_node_child_process.execFile);
28
+ async function collectDiff(options) {
29
+ const { root, targetBranch = "main", commitHash } = options;
30
+ const args = ["diff", "--no-color"];
31
+ if (commitHash) {
32
+ args.push(commitHash);
33
+ } else {
34
+ args.push(targetBranch, "HEAD");
35
+ }
36
+ try {
37
+ const { stdout } = await exec("git", args, { cwd: root, maxBuffer: 50 * 1024 * 1024 });
38
+ return parseDiffOutput(stdout);
39
+ } catch (err) {
40
+ throw new GitCommandError({
41
+ command: "diff",
42
+ args,
43
+ cwd: root,
44
+ message: "Failed to collect git diff",
45
+ cause: err instanceof Error ? err : void 0
46
+ });
47
+ }
48
+ }
49
+ function parseDiffOutput(stdout) {
50
+ if (!stdout.trim()) return [];
51
+ const diffs = [];
52
+ const fileSections = stdout.split(/^diff --git /m).filter((s) => s.trim());
53
+ for (const section of fileSections) {
54
+ const diff = parseFileSection(section);
55
+ if (diff) diffs.push(diff);
56
+ }
57
+ return diffs;
58
+ }
59
+ function parseFileSection(section) {
60
+ const lines = section.split("\n");
61
+ const headerLine = lines[0];
62
+ const pathMatch = headerLine?.match(/^a\/(.+) b\/(.+)/);
63
+ if (!pathMatch) return null;
64
+ const file = pathMatch[2];
65
+ const from = pathMatch[1];
66
+ let status = "modified", currentHunk = null, additions = 0, deletions = 0;
67
+ if (lines.some((l) => l.startsWith("new file mode"))) {
68
+ status = "added";
69
+ } else if (lines.some((l) => l.startsWith("deleted file mode"))) {
70
+ status = "deleted";
71
+ } else if (lines.some((l) => l.startsWith("rename from "))) {
72
+ status = "renamed";
73
+ }
74
+ const hunks = [];
75
+ for (const line of lines) {
76
+ const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
77
+ if (hunkMatch) {
78
+ if (currentHunk) hunks.push(currentHunk);
79
+ currentHunk = {
80
+ header: line,
81
+ oldStart: Number(hunkMatch[1]),
82
+ oldCount: Number(hunkMatch[2] ?? 1),
83
+ newStart: Number(hunkMatch[3]),
84
+ newCount: Number(hunkMatch[4] ?? 1),
85
+ lines: []
86
+ };
87
+ continue;
88
+ }
89
+ if (line.startsWith("+") && currentHunk) {
90
+ additions++;
91
+ currentHunk.lines.push(line);
92
+ } else if (line.startsWith("-") && currentHunk && !line.startsWith("---")) {
93
+ deletions++;
94
+ currentHunk.lines.push(line);
95
+ } else if (line.startsWith(" ") && currentHunk) {
96
+ currentHunk.lines.push(line);
97
+ }
98
+ }
99
+ if (currentHunk) hunks.push(currentHunk);
100
+ return {
101
+ file,
102
+ from: status === "renamed" || status === "deleted" ? from : void 0,
103
+ status,
104
+ additions,
105
+ deletions,
106
+ hunks
107
+ };
108
+ }
109
+
110
+ // src/collectors/project-detector.ts
111
+ var import_node_fs = require("fs");
112
+ var import_node_path = require("path");
113
+ async function detectProject(root) {
114
+ const markers = [
115
+ { type: "node", file: "package.json" },
116
+ { type: "go", file: "go.mod" },
117
+ { type: "python", file: "requirements.txt" },
118
+ { type: "rust", file: "Cargo.toml" }
119
+ ];
120
+ for (const marker of markers) {
121
+ const filePath = (0, import_node_path.join)(root, marker.file);
122
+ if ((0, import_node_fs.existsSync)(filePath)) {
123
+ if (marker.type === "node") {
124
+ return detectNodeProject(root, filePath);
125
+ }
126
+ if (marker.type === "go") {
127
+ return {
128
+ type: "go",
129
+ language: "go",
130
+ packageManager: "go mod",
131
+ root
132
+ };
133
+ }
134
+ if (marker.type === "python") {
135
+ return {
136
+ type: "python",
137
+ language: "python",
138
+ packageManager: "pip",
139
+ root
140
+ };
141
+ }
142
+ }
143
+ }
144
+ return {
145
+ type: "unknown",
146
+ language: "unknown",
147
+ root
148
+ };
149
+ }
150
+ function detectNodeProject(root, packageJsonPath) {
151
+ try {
152
+ const content = (0, import_node_fs.readFileSync)(packageJsonPath, "utf-8");
153
+ const pkg = JSON.parse(content);
154
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
155
+ if (deps.vue || deps.Vue) {
156
+ const version = deps.vue || deps.Vue || "";
157
+ const isVue3 = version.startsWith("^3") || version.startsWith("3");
158
+ return {
159
+ type: "vue",
160
+ framework: isVue3 ? "vue3" : "vue2",
161
+ language: "typescript",
162
+ packageManager: detectPackageManager(root),
163
+ root
164
+ };
165
+ }
166
+ if (deps.react || deps.React) {
167
+ const version = deps.react || deps.React || "";
168
+ const isReact18 = version.startsWith("^18") || version.startsWith("18");
169
+ return {
170
+ type: "react",
171
+ framework: isReact18 ? "react18" : "react",
172
+ language: "typescript",
173
+ packageManager: detectPackageManager(root),
174
+ root
175
+ };
176
+ }
177
+ return {
178
+ type: "node",
179
+ language: "javascript",
180
+ packageManager: detectPackageManager(root),
181
+ root
182
+ };
183
+ } catch {
184
+ return {
185
+ type: "node",
186
+ language: "javascript",
187
+ packageManager: "npm",
188
+ root
189
+ };
190
+ }
191
+ }
192
+ function detectPackageManager(root) {
193
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(root, "pnpm-lock.yaml"))) return "pnpm";
194
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(root, "yarn.lock"))) return "yarn";
195
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(root, "package-lock.json"))) return "npm";
196
+ return "npm";
197
+ }
198
+
199
+ // src/types.ts
200
+ var VERSION = "0.1.0";
201
+
202
+ // src/cli.ts
203
+ var COMMANDS = ["review", "review-file", "review-commit", "help", "version"];
204
+ function printHelp() {
205
+ console.log(`
206
+ \u{1F50D} AI Code Reviewer v${VERSION} \u2014 Intelligent Code Review
207
+
208
+ Usage:
209
+ npx ai-code-reviewer-plus <command> [options]
210
+
211
+ Commands:
212
+ review [--branch <name>] Review diff against target branch
213
+ review-file <file> Review a single file
214
+ review-commit <hash> Review a specific commit
215
+ help Show this help
216
+ version Show version
217
+
218
+ Examples:
219
+ npx ai-code-reviewer-plus review
220
+ npx ai-code-reviewer-plus review --branch develop
221
+ `);
222
+ }
223
+ function printVersion() {
224
+ console.log(`ai-code-reviewer-plus v${VERSION}`);
225
+ }
226
+ async function runReview(args) {
227
+ const { values } = (0, import_node_util2.parseArgs)({
228
+ args,
229
+ options: { branch: { type: "string", short: "b" } },
230
+ strict: false
231
+ });
232
+ const root = process.cwd();
233
+ const targetBranch = typeof values.branch === "string" ? values.branch : "main";
234
+ console.log(`
235
+ \u{1F50D} Reviewing diff against ${targetBranch}...
236
+ `);
237
+ try {
238
+ const project = await detectProject(root);
239
+ console.log(`\u{1F4C1} Project: ${project.framework || project.type} (${project.language})`);
240
+ const diffs = await collectDiff({ root, targetBranch });
241
+ if (diffs.length === 0) {
242
+ console.log("\n\u2705 No changes detected.\n");
243
+ return;
244
+ }
245
+ console.log(`\u{1F4DD} Files changed: ${diffs.length}
246
+ `);
247
+ for (const diff of diffs) {
248
+ const status = diff.status === "added" ? "\u2795" : diff.status === "deleted" ? "\u274C" : "\u{1F4DD}";
249
+ console.log(` ${status} ${diff.file} (+${diff.additions}/-${diff.deletions})`);
250
+ }
251
+ console.log(
252
+ `
253
+ \u{1F4A1} For AI-powered analysis, use the Claude Code Skill: /review ${targetBranch}
254
+ `
255
+ );
256
+ } catch (err) {
257
+ console.error("Error:", err instanceof Error ? err.message : "Unknown error");
258
+ process.exit(1);
259
+ }
260
+ }
261
+ async function runReviewFile(args) {
262
+ const file = args[0];
263
+ if (!file) {
264
+ console.error("Error: missing file argument");
265
+ console.log("Usage: npx ai-code-reviewer-plus review-file <file>");
266
+ process.exit(1);
267
+ }
268
+ console.log(`
269
+ \u{1F50D} Reviewing file: ${file}
270
+ \u{1F4A1} Use Claude Code Skill: /review-file ${file}
271
+ `);
272
+ }
273
+ async function runReviewCommit(args) {
274
+ const hash = args[0];
275
+ if (!hash) {
276
+ console.error("Error: missing hash argument");
277
+ console.log("Usage: npx ai-code-reviewer-plus review-commit <hash>");
278
+ process.exit(1);
279
+ }
280
+ console.log(
281
+ `
282
+ \u{1F50D} Reviewing commit: ${hash}
283
+ \u{1F4A1} Use Claude Code Skill: /review-commit ${hash}
284
+ `
285
+ );
286
+ }
287
+ async function main() {
288
+ const args = process.argv.slice(2);
289
+ const command = args[0];
290
+ if (!command || !COMMANDS.includes(command)) {
291
+ printHelp();
292
+ process.exit(1);
293
+ }
294
+ switch (command) {
295
+ case "help":
296
+ printHelp();
297
+ break;
298
+ case "version":
299
+ printVersion();
300
+ break;
301
+ case "review":
302
+ await runReview(args.slice(1));
303
+ break;
304
+ case "review-file":
305
+ await runReviewFile(args.slice(1));
306
+ break;
307
+ case "review-commit":
308
+ await runReviewCommit(args.slice(1));
309
+ break;
310
+ }
311
+ }
312
+ main().catch((err) => {
313
+ console.error("Error:", err.message);
314
+ process.exit(1);
315
+ });
package/dist/cli.mjs ADDED
@@ -0,0 +1,121 @@
1
+ import {
2
+ VERSION,
3
+ collectDiff,
4
+ detectProject
5
+ } from "./chunk-VRJ4NJAF.mjs";
6
+
7
+ // src/cli.ts
8
+ import { parseArgs } from "util";
9
+ var COMMANDS = ["review", "review-file", "review-commit", "help", "version"];
10
+ function printHelp() {
11
+ console.log(`
12
+ \u{1F50D} AI Code Reviewer v${VERSION} \u2014 Intelligent Code Review
13
+
14
+ Usage:
15
+ npx ai-code-reviewer-plus <command> [options]
16
+
17
+ Commands:
18
+ review [--branch <name>] Review diff against target branch
19
+ review-file <file> Review a single file
20
+ review-commit <hash> Review a specific commit
21
+ help Show this help
22
+ version Show version
23
+
24
+ Examples:
25
+ npx ai-code-reviewer-plus review
26
+ npx ai-code-reviewer-plus review --branch develop
27
+ `);
28
+ }
29
+ function printVersion() {
30
+ console.log(`ai-code-reviewer-plus v${VERSION}`);
31
+ }
32
+ async function runReview(args) {
33
+ const { values } = parseArgs({
34
+ args,
35
+ options: { branch: { type: "string", short: "b" } },
36
+ strict: false
37
+ });
38
+ const root = process.cwd();
39
+ const targetBranch = typeof values.branch === "string" ? values.branch : "main";
40
+ console.log(`
41
+ \u{1F50D} Reviewing diff against ${targetBranch}...
42
+ `);
43
+ try {
44
+ const project = await detectProject(root);
45
+ console.log(`\u{1F4C1} Project: ${project.framework || project.type} (${project.language})`);
46
+ const diffs = await collectDiff({ root, targetBranch });
47
+ if (diffs.length === 0) {
48
+ console.log("\n\u2705 No changes detected.\n");
49
+ return;
50
+ }
51
+ console.log(`\u{1F4DD} Files changed: ${diffs.length}
52
+ `);
53
+ for (const diff of diffs) {
54
+ const status = diff.status === "added" ? "\u2795" : diff.status === "deleted" ? "\u274C" : "\u{1F4DD}";
55
+ console.log(` ${status} ${diff.file} (+${diff.additions}/-${diff.deletions})`);
56
+ }
57
+ console.log(
58
+ `
59
+ \u{1F4A1} For AI-powered analysis, use the Claude Code Skill: /review ${targetBranch}
60
+ `
61
+ );
62
+ } catch (err) {
63
+ console.error("Error:", err instanceof Error ? err.message : "Unknown error");
64
+ process.exit(1);
65
+ }
66
+ }
67
+ async function runReviewFile(args) {
68
+ const file = args[0];
69
+ if (!file) {
70
+ console.error("Error: missing file argument");
71
+ console.log("Usage: npx ai-code-reviewer-plus review-file <file>");
72
+ process.exit(1);
73
+ }
74
+ console.log(`
75
+ \u{1F50D} Reviewing file: ${file}
76
+ \u{1F4A1} Use Claude Code Skill: /review-file ${file}
77
+ `);
78
+ }
79
+ async function runReviewCommit(args) {
80
+ const hash = args[0];
81
+ if (!hash) {
82
+ console.error("Error: missing hash argument");
83
+ console.log("Usage: npx ai-code-reviewer-plus review-commit <hash>");
84
+ process.exit(1);
85
+ }
86
+ console.log(
87
+ `
88
+ \u{1F50D} Reviewing commit: ${hash}
89
+ \u{1F4A1} Use Claude Code Skill: /review-commit ${hash}
90
+ `
91
+ );
92
+ }
93
+ async function main() {
94
+ const args = process.argv.slice(2);
95
+ const command = args[0];
96
+ if (!command || !COMMANDS.includes(command)) {
97
+ printHelp();
98
+ process.exit(1);
99
+ }
100
+ switch (command) {
101
+ case "help":
102
+ printHelp();
103
+ break;
104
+ case "version":
105
+ printVersion();
106
+ break;
107
+ case "review":
108
+ await runReview(args.slice(1));
109
+ break;
110
+ case "review-file":
111
+ await runReviewFile(args.slice(1));
112
+ break;
113
+ case "review-commit":
114
+ await runReviewCommit(args.slice(1));
115
+ break;
116
+ }
117
+ }
118
+ main().catch((err) => {
119
+ console.error("Error:", err.message);
120
+ process.exit(1);
121
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Git diff collector — parses `git diff` output into structured data
3
+ */
4
+ import type { ParsedDiff } from '../types';
5
+ /**
6
+ * Collect diff between branches or commits
7
+ */
8
+ export declare function collectDiff(options: {
9
+ root: string;
10
+ targetBranch?: string;
11
+ commitHash?: string;
12
+ }): Promise<ParsedDiff[]>;
13
+ /**
14
+ * Collect diff for a single commit
15
+ */
16
+ export declare function collectCommitDiff(options: {
17
+ root: string;
18
+ hash: string;
19
+ }): Promise<ParsedDiff[]>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Project type detector — identifies project framework and language
3
+ */
4
+ import type { ProjectInfo } from '../types';
5
+ /**
6
+ * Detect project type from filesystem markers
7
+ */
8
+ export declare function detectProject(root: string): Promise<ProjectInfo>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Structured error classes for AI Code Reviewer
3
+ */
4
+ /** Error thrown when a git command fails */
5
+ export declare class GitCommandError extends Error {
6
+ /** The git command that failed */
7
+ readonly command: string;
8
+ /** The arguments passed to git */
9
+ readonly args: readonly string[];
10
+ /** The exit code (if available) */
11
+ readonly exitCode: number | undefined;
12
+ /** The working directory where the command ran */
13
+ readonly cwd: string | undefined;
14
+ constructor(options: {
15
+ command?: string;
16
+ args?: readonly string[];
17
+ exitCode?: number;
18
+ cwd?: string;
19
+ message?: string;
20
+ cause?: Error;
21
+ });
22
+ }
23
+ /** Error thrown when parsing fails */
24
+ export declare class ParseError extends Error {
25
+ /** What was being parsed */
26
+ readonly parser: string;
27
+ /** The raw input that failed to parse */
28
+ readonly rawInput: string;
29
+ constructor(options: {
30
+ parser: string;
31
+ rawInput: string;
32
+ message?: string;
33
+ });
34
+ }
35
+ /** Error thrown when configuration is invalid */
36
+ export declare class ConfigError extends Error {
37
+ /** Configuration file path */
38
+ readonly filePath: string | undefined;
39
+ /** Specific field that's invalid */
40
+ readonly field: string | undefined;
41
+ constructor(options: {
42
+ filePath?: string;
43
+ field?: string;
44
+ message?: string;
45
+ });
46
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * AI Code Reviewer — Entry module
3
+ *
4
+ * Exports all public APIs
5
+ */
6
+ export { analyzeCode, analyzeDiffs, getAvailableRules } from './analyzers';
7
+ export { collectCommitDiff, collectDiff } from './collectors/diff-collector';
8
+ export { detectProject } from './collectors/project-detector';
9
+ export { ConfigError, GitCommandError, ParseError } from './errors';
10
+ export type { CommitReviewOptions, DiffHunk, FileReviewOptions, ParsedDiff, ProjectInfo, ReviewDimension, ReviewerConfig, ReviewFinding, ReviewOptions, ReviewResult, SeverityLevel, } from './types';
11
+ export { VERSION } from './types';
12
+ export { getDefaultConfig, loadConfigFile, mergeConfig } from './utils/config';
13
+ export { formatDiffSummary, formatDuration, formatFinding, formatReviewReport, formatSummary, } from './utils/format';