@rootplatform/review-my-pr 1.0.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 +164 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/clipboard.d.ts +2 -0
- package/dist/lib/clipboard.js +10 -0
- package/dist/lib/clipboard.js.map +1 -0
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +127 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/prompt.d.ts +16 -0
- package/dist/lib/prompt.js +164 -0
- package/dist/lib/prompt.js.map +1 -0
- package/dist/review.d.ts +5 -0
- package/dist/review.js +67 -0
- package/dist/review.js.map +1 -0
- package/dist/sea-bundle.cjs +6123 -0
- package/dist/sea-bundle.js +6131 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# review-my-pr
|
|
2
|
+
|
|
3
|
+
CLI tool that generates a code review prompt from your git diff and copies it to the clipboard. Paste it into any LLM chat to get an instant code review.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
Run from any git repository on a feature branch:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Auto-detects main/master as the base branch
|
|
12
|
+
review-my-pr
|
|
13
|
+
|
|
14
|
+
# Specify a base branch
|
|
15
|
+
review-my-pr --base develop
|
|
16
|
+
|
|
17
|
+
# Exclude additional file patterns
|
|
18
|
+
review-my-pr --exclude "*.generated.ts" --exclude "docs/**"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Options
|
|
22
|
+
|
|
23
|
+
| Flag | Description |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `-b, --base <branch>` | Base branch to diff against. Auto-detects `origin/main`, `origin/master`, `main`, or `master` if not specified. |
|
|
26
|
+
| `-e, --exclude <pattern>` | Additional git pathspec exclusion pattern. Can be repeated for multiple patterns. |
|
|
27
|
+
| `-V, --version` | Show version number |
|
|
28
|
+
| `-h, --help` | Show help |
|
|
29
|
+
|
|
30
|
+
### Example Output
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Generating diff from origin/master...
|
|
34
|
+
✅ Diff generated successfully
|
|
35
|
+
✅ Found 2 changed file(s) (65 lines)
|
|
36
|
+
|
|
37
|
+
✅ Review prompt copied to clipboard!
|
|
38
|
+
Just paste it into your chat with Cmd+V
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
Create a `.review-my-pr/` directory in your repository root to customize the review prompt. The directory contains two files:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
.review-my-pr/
|
|
47
|
+
prompt.md # The review prompt template
|
|
48
|
+
config.json # Category definitions and settings
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If this directory is not present, a sensible default prompt is used.
|
|
52
|
+
|
|
53
|
+
### `prompt.md` - Prompt Template
|
|
54
|
+
|
|
55
|
+
Use these placeholders in your template and they will be replaced with actual values:
|
|
56
|
+
|
|
57
|
+
| Placeholder | Description |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `{{DIFF}}` | The full git diff content |
|
|
60
|
+
| `{{BASE_BRANCH}}` | The base branch name (e.g. `origin/master`) |
|
|
61
|
+
| `{{CHANGED_FILES}}` | List of all changed files |
|
|
62
|
+
| `{{SRC_FILES}}` | Source files only (excludes tests, mocks, fixtures) |
|
|
63
|
+
| `{{TEST_FILES}}` | Test/spec files only |
|
|
64
|
+
| `{{MIGRATION_FILES}}` | Database migration files |
|
|
65
|
+
| `{{HTTP_FILES}}` | HTTP layer files |
|
|
66
|
+
| `{{PR_TITLE}}` | PR title (requires `gh` CLI) |
|
|
67
|
+
| `{{PR_BODY}}` | PR description body (requires `gh` CLI) |
|
|
68
|
+
| `{{PR_METADATA}}` | Formatted PR title + description block |
|
|
69
|
+
| `{{CATEGORY.NAME}}` | Custom category defined in `config.json` (see below) |
|
|
70
|
+
|
|
71
|
+
If `{{DIFF}}` is not present in your template, the diff will be appended automatically at the end.
|
|
72
|
+
|
|
73
|
+
### `config.json` - Custom File Categories
|
|
74
|
+
|
|
75
|
+
Define custom file categories to group changed files by regex patterns. Reference them in `prompt.md` with `{{CATEGORY.NAME}}`.
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"categories": {
|
|
80
|
+
"SRC_FILES": {
|
|
81
|
+
"exclude": "\\.(test|spec)\\.(ts|tsx|js|jsx)$|__mocks__|fixtures"
|
|
82
|
+
},
|
|
83
|
+
"TEST_FILES": {
|
|
84
|
+
"pattern": "\\.(test|spec)\\.(ts|tsx|js|jsx)$"
|
|
85
|
+
},
|
|
86
|
+
"MIGRATION_FILES": {
|
|
87
|
+
"pattern": "migrations/\\d+_.*\\.(ts|js)$"
|
|
88
|
+
},
|
|
89
|
+
"SCHEMAS": {
|
|
90
|
+
"pattern": "\\.graphql$|\\.schema\\.ts$"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Property | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `pattern` | Regex to include matching files. If omitted, all files are included (before `exclude` is applied). |
|
|
99
|
+
| `exclude` | Regex to exclude matching files. Applied after `pattern`. |
|
|
100
|
+
|
|
101
|
+
Categories are independent -- a file can appear in multiple categories.
|
|
102
|
+
|
|
103
|
+
### Example
|
|
104
|
+
|
|
105
|
+
`.review-my-pr/config.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"categories": {
|
|
110
|
+
"SRC_FILES": { "exclude": "\\.(test|spec)\\.(ts|tsx|js|jsx)$|__mocks__|fixtures" },
|
|
111
|
+
"TEST_FILES": { "pattern": "\\.(test|spec)\\.(ts|tsx|js|jsx)$" }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`.review-my-pr/prompt.md`:
|
|
117
|
+
|
|
118
|
+
```markdown
|
|
119
|
+
You are an expert code reviewer for our TypeScript backend.
|
|
120
|
+
|
|
121
|
+
## Context
|
|
122
|
+
- Stack: TypeScript, Node.js, PostgreSQL
|
|
123
|
+
- Focus: bugs, security, performance, test coverage
|
|
124
|
+
|
|
125
|
+
## Source Files
|
|
126
|
+
{{CATEGORY.SRC_FILES}}
|
|
127
|
+
|
|
128
|
+
## Test Files
|
|
129
|
+
{{CATEGORY.TEST_FILES}}
|
|
130
|
+
|
|
131
|
+
## Diff
|
|
132
|
+
|
|
133
|
+
\`\`\`diff
|
|
134
|
+
{{DIFF}}
|
|
135
|
+
\`\`\`
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The built-in placeholders (`{{SRC_FILES}}`, `{{TEST_FILES}}`, etc.) still work and use the default categorization patterns. Custom categories via `{{CATEGORY.*}}` give you full control over the regex patterns.
|
|
139
|
+
|
|
140
|
+
## Default Exclusions
|
|
141
|
+
|
|
142
|
+
The following file patterns are always excluded from the diff:
|
|
143
|
+
|
|
144
|
+
- `**/package-lock.json`
|
|
145
|
+
- `**/yarn.lock`
|
|
146
|
+
- `**/pnpm-lock.yaml`
|
|
147
|
+
- `**/test-results/**`
|
|
148
|
+
- `**/playwright-report/**`
|
|
149
|
+
|
|
150
|
+
Use `--exclude` to add more patterns on top of these.
|
|
151
|
+
|
|
152
|
+
## PR Metadata
|
|
153
|
+
|
|
154
|
+
If you have the [GitHub CLI](https://cli.github.com/) (`gh`) installed and your branch has an open PR, the tool will automatically include the PR title and description in the prompt.
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone <repo-url>
|
|
160
|
+
cd review-my-pr
|
|
161
|
+
npm install
|
|
162
|
+
npm run build
|
|
163
|
+
npm link # makes `review-my-pr` available globally
|
|
164
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { runReview } from "./review.js";
|
|
4
|
+
const program = new Command();
|
|
5
|
+
program
|
|
6
|
+
.name("review-my-pr")
|
|
7
|
+
.description("Generate a code review prompt from your git diff and copy it to the clipboard")
|
|
8
|
+
.version("1.0.0")
|
|
9
|
+
.option("-b, --base <branch>", "base branch to diff against (default: auto-detect main/master)")
|
|
10
|
+
.option("-e, --exclude <pattern>", "additional git pathspec exclusion (repeatable)", (val, acc) => [...acc, val], [])
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
await runReview({
|
|
13
|
+
base: options.base,
|
|
14
|
+
exclude: options.exclude,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
program.parse();
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CACV,+EAA+E,CAChF;KACA,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,qBAAqB,EAAE,gEAAgE,CAAC;KAC/F,MAAM,CACL,yBAAyB,EACzB,gDAAgD,EAChD,CAAC,GAAW,EAAE,GAAa,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,EAC7C,EAAc,CACf;KACA,MAAM,CAAC,KAAK,EAAE,OAA8C,EAAE,EAAE;IAC/D,MAAM,SAAS,CAAC;QACd,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import clipboardy from "clipboardy";
|
|
2
|
+
export async function copyToClipboard(text) {
|
|
3
|
+
await clipboardy.write(text);
|
|
4
|
+
}
|
|
5
|
+
export function getPasteHint() {
|
|
6
|
+
return process.platform === "darwin"
|
|
7
|
+
? "Just paste it into your chat with Cmd+V"
|
|
8
|
+
: "Just paste it into your chat with Ctrl+V";
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/lib/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,CAAC,yCAAyC;QAC3C,CAAC,CAAC,0CAA0C,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function isGitRepo(): boolean;
|
|
2
|
+
export declare function getRepoRoot(): string;
|
|
3
|
+
export declare function detectBaseBranch(explicit?: string): string;
|
|
4
|
+
export declare function ensureBranchUpToDate(baseBranch: string): void;
|
|
5
|
+
export interface DiffResult {
|
|
6
|
+
content: string;
|
|
7
|
+
changedFiles: string[];
|
|
8
|
+
lineCount: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function getDiff(baseBranch: string, extraExclusions?: string[]): DiffResult;
|
|
11
|
+
export interface CategorizedFiles {
|
|
12
|
+
source: string[];
|
|
13
|
+
test: string[];
|
|
14
|
+
migration: string[];
|
|
15
|
+
http: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function categorizeFiles(files: string[]): CategorizedFiles;
|
|
18
|
+
export interface CategoryDefinition {
|
|
19
|
+
pattern?: string;
|
|
20
|
+
exclude?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function categorizeFilesWithConfig(files: string[], categories: Record<string, CategoryDefinition>): Record<string, string[]>;
|
|
23
|
+
export interface PrMetadata {
|
|
24
|
+
title: string;
|
|
25
|
+
body: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function getPrMetadata(): PrMetadata | null;
|
package/dist/lib/git.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
2
|
+
export function isGitRepo() {
|
|
3
|
+
try {
|
|
4
|
+
execSync("git rev-parse --is-inside-work-tree", { stdio: "pipe" });
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function getRepoRoot() {
|
|
12
|
+
return execSync("git rev-parse --show-toplevel", { stdio: "pipe" })
|
|
13
|
+
.toString()
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
function branchExists(branch) {
|
|
17
|
+
try {
|
|
18
|
+
execSync(`git rev-parse --verify ${branch}`, { stdio: "pipe" });
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function detectBaseBranch(explicit) {
|
|
26
|
+
if (explicit) {
|
|
27
|
+
if (!branchExists(explicit)) {
|
|
28
|
+
throw new Error(`Branch '${explicit}' does not exist`);
|
|
29
|
+
}
|
|
30
|
+
return explicit;
|
|
31
|
+
}
|
|
32
|
+
const candidates = ["origin/main", "origin/master", "main", "master"];
|
|
33
|
+
for (const branch of candidates) {
|
|
34
|
+
if (branchExists(branch)) {
|
|
35
|
+
return branch;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error("Could not detect a base branch. No main/master branch found.\n" +
|
|
39
|
+
"Use --base <branch> to specify one explicitly.");
|
|
40
|
+
}
|
|
41
|
+
export function ensureBranchUpToDate(baseBranch) {
|
|
42
|
+
const behindCount = execFileSync("git", ["rev-list", "--count", `HEAD..${baseBranch}`], { stdio: "pipe" })
|
|
43
|
+
.toString()
|
|
44
|
+
.trim();
|
|
45
|
+
if (parseInt(behindCount, 10) > 0) {
|
|
46
|
+
throw new Error(`Your branch is behind ${baseBranch} by ${behindCount} commit(s). ` +
|
|
47
|
+
`Please update your branch and try again.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const DEFAULT_EXCLUSIONS = [
|
|
51
|
+
":(exclude)**/package-lock.json",
|
|
52
|
+
":(exclude)**/yarn.lock",
|
|
53
|
+
":(exclude)**/pnpm-lock.yaml",
|
|
54
|
+
":(exclude)**/test-results/**",
|
|
55
|
+
":(exclude)**/playwright-report/**",
|
|
56
|
+
];
|
|
57
|
+
export function getDiff(baseBranch, extraExclusions = []) {
|
|
58
|
+
const exclusions = [
|
|
59
|
+
...DEFAULT_EXCLUSIONS,
|
|
60
|
+
...extraExclusions.map((e) => `:(exclude)${e}`),
|
|
61
|
+
];
|
|
62
|
+
const pathspec = ["--", ".", ...exclusions];
|
|
63
|
+
const content = execFileSync("git", ["diff", baseBranch, ...pathspec], {
|
|
64
|
+
stdio: "pipe",
|
|
65
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
66
|
+
})
|
|
67
|
+
.toString()
|
|
68
|
+
.trim();
|
|
69
|
+
const changedFiles = execFileSync("git", ["diff", "--name-only", baseBranch, ...pathspec], { stdio: "pipe", maxBuffer: 10 * 1024 * 1024 })
|
|
70
|
+
.toString()
|
|
71
|
+
.trim()
|
|
72
|
+
.split("\n")
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
75
|
+
return { content, changedFiles, lineCount };
|
|
76
|
+
}
|
|
77
|
+
export function categorizeFiles(files) {
|
|
78
|
+
const testPattern = /\.(test|spec)\.(ts|tsx|js|jsx)$/;
|
|
79
|
+
const mockPattern = /__mocks__|fixtures/;
|
|
80
|
+
const migrationPattern = /migrations\/\d+_.*\.(ts|js)$/;
|
|
81
|
+
const httpPattern = /\/http\//;
|
|
82
|
+
const test = files.filter((f) => testPattern.test(f));
|
|
83
|
+
const migration = files.filter((f) => migrationPattern.test(f));
|
|
84
|
+
const http = files.filter((f) => httpPattern.test(f));
|
|
85
|
+
const source = files.filter((f) => !testPattern.test(f) && !mockPattern.test(f));
|
|
86
|
+
return { source, test, migration, http };
|
|
87
|
+
}
|
|
88
|
+
export function categorizeFilesWithConfig(files, categories) {
|
|
89
|
+
const result = {};
|
|
90
|
+
for (const [name, def] of Object.entries(categories)) {
|
|
91
|
+
const includeRe = def.pattern ? new RegExp(def.pattern) : null;
|
|
92
|
+
const excludeRe = def.exclude ? new RegExp(def.exclude) : null;
|
|
93
|
+
result[name] = files.filter((f) => {
|
|
94
|
+
const included = includeRe ? includeRe.test(f) : true;
|
|
95
|
+
const excluded = excludeRe ? excludeRe.test(f) : false;
|
|
96
|
+
return included && !excluded;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
export function getPrMetadata() {
|
|
102
|
+
try {
|
|
103
|
+
execSync("which gh", { stdio: "pipe" });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const title = execSync("gh pr view --json title -q .title", {
|
|
110
|
+
stdio: "pipe",
|
|
111
|
+
})
|
|
112
|
+
.toString()
|
|
113
|
+
.trim();
|
|
114
|
+
const body = execSync("gh pr view --json body -q .body", {
|
|
115
|
+
stdio: "pipe",
|
|
116
|
+
})
|
|
117
|
+
.toString()
|
|
118
|
+
.trim();
|
|
119
|
+
if (!title)
|
|
120
|
+
return null;
|
|
121
|
+
return { title, body };
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CategorizedFiles, CategoryDefinition, PrMetadata } from "./git.js";
|
|
2
|
+
export interface ReviewConfig {
|
|
3
|
+
categories?: Record<string, CategoryDefinition>;
|
|
4
|
+
}
|
|
5
|
+
export interface PromptContext {
|
|
6
|
+
baseBranch: string;
|
|
7
|
+
diffContent: string;
|
|
8
|
+
changedFiles: string[];
|
|
9
|
+
categorizedFiles: CategorizedFiles;
|
|
10
|
+
customCategories?: Record<string, string[]>;
|
|
11
|
+
prMetadata: PrMetadata | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function hasCustomPrompt(repoRoot: string): boolean;
|
|
14
|
+
export declare function loadTemplate(repoRoot: string): string;
|
|
15
|
+
export declare function loadConfig(repoRoot: string): ReviewConfig;
|
|
16
|
+
export declare function buildPrompt(template: string, context: PromptContext): string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const CONFIG_DIR = ".review-my-pr";
|
|
4
|
+
const PROMPT_FILE = "prompt.md";
|
|
5
|
+
const CONFIG_FILE = "config.json";
|
|
6
|
+
const DEFAULT_PROMPT = `You are an expert code reviewer. Below is a git diff comparing a feature branch to {{BASE_BRANCH}}.
|
|
7
|
+
|
|
8
|
+
{{PR_METADATA}}
|
|
9
|
+
## Your Task
|
|
10
|
+
|
|
11
|
+
### 0. High-Level Summary
|
|
12
|
+
In 2-3 sentences, describe:
|
|
13
|
+
- **Product impact**: What does this change deliver for users or customers?
|
|
14
|
+
- **Engineering approach**: Key patterns or architectural decisions.
|
|
15
|
+
|
|
16
|
+
### 1. Critical Review Focus
|
|
17
|
+
Prioritize your review on these areas (in order of importance):
|
|
18
|
+
|
|
19
|
+
**🐛 Bugs & Logic Errors** (HIGHEST PRIORITY)
|
|
20
|
+
- Off-by-one errors, null/undefined handling, race conditions
|
|
21
|
+
- Incorrect conditional logic, missing edge cases
|
|
22
|
+
- Type mismatches, incorrect API contract usage
|
|
23
|
+
- Transaction handling issues (missing rollback, deadlocks)
|
|
24
|
+
|
|
25
|
+
**💥 Breaking Changes** (CRITICAL)
|
|
26
|
+
- API contract changes (request/response shape changes)
|
|
27
|
+
- Database schema changes without proper migrations
|
|
28
|
+
- Backward compatibility issues
|
|
29
|
+
|
|
30
|
+
**🔒 Security Issues**
|
|
31
|
+
- Authentication/authorization bypasses
|
|
32
|
+
- Injection vulnerabilities (SQL, command, etc.)
|
|
33
|
+
- Sensitive data exposure in logs or responses
|
|
34
|
+
- Missing access control checks
|
|
35
|
+
|
|
36
|
+
**⚡ Performance Regressions**
|
|
37
|
+
- N+1 query patterns
|
|
38
|
+
- Missing database indexes on new columns
|
|
39
|
+
- Unbounded queries without pagination
|
|
40
|
+
- Unnecessary computation in hot paths
|
|
41
|
+
|
|
42
|
+
**🧪 Test Coverage Gaps**
|
|
43
|
+
- New code paths without tests
|
|
44
|
+
- Modified logic without updated tests
|
|
45
|
+
- Missing error case coverage
|
|
46
|
+
|
|
47
|
+
### 2. Review Guidelines
|
|
48
|
+
- **Only report issues you're confident about (>80% certainty)**
|
|
49
|
+
- **Don't flag stylistic preferences** if the code follows existing patterns in the codebase
|
|
50
|
+
- **If you see an unfamiliar pattern**, assume it may be a project convention before flagging
|
|
51
|
+
- **Focus on functional correctness** over cosmetic improvements
|
|
52
|
+
- **Skip categories with no relevant changes** - don't force issues where none exist
|
|
53
|
+
|
|
54
|
+
### 3. Issue Format
|
|
55
|
+
For each real issue found:
|
|
56
|
+
|
|
57
|
+
📍 \`path/to/file.ts:line-range\`
|
|
58
|
+
**[🔴 Critical | 🟠 Major | 🟡 Minor | 🔵 Enhancement]** Brief title
|
|
59
|
+
|
|
60
|
+
→ **Problem**: What's wrong and why it matters
|
|
61
|
+
→ **Fix**: Specific suggestion or code snippet
|
|
62
|
+
|
|
63
|
+
**Confidence**: 🔴 Certain / 🟡 Likely / 🟢 Suggestion
|
|
64
|
+
|
|
65
|
+
### 4. Output Structure
|
|
66
|
+
|
|
67
|
+
\`\`\`
|
|
68
|
+
## Summary
|
|
69
|
+
[2-3 sentence overview]
|
|
70
|
+
|
|
71
|
+
## Prioritized Issues
|
|
72
|
+
|
|
73
|
+
### 🔴 Critical
|
|
74
|
+
[Issues that must be fixed before merge - bugs, security, breaking changes]
|
|
75
|
+
|
|
76
|
+
### 🟠 Major
|
|
77
|
+
[Issues that should be fixed - logic problems, missing error handling]
|
|
78
|
+
|
|
79
|
+
### 🟡 Minor
|
|
80
|
+
[Issues worth addressing - code clarity, minor optimizations]
|
|
81
|
+
|
|
82
|
+
### 🔵 Enhancements
|
|
83
|
+
[Nice-to-haves - suggestions for improvement]
|
|
84
|
+
|
|
85
|
+
## ✅ Highlights
|
|
86
|
+
[Positive findings, well-implemented patterns, good practices observed]
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
## Changed Files
|
|
90
|
+
{{CHANGED_FILES}}
|
|
91
|
+
|
|
92
|
+
## Diff
|
|
93
|
+
|
|
94
|
+
\`\`\`diff
|
|
95
|
+
{{DIFF}}
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
**CRITICAL INSTRUCTION**: Your primary job is catching bugs and breaking changes. Be thorough but avoid false positives. If something looks unusual but might be intentional, note it as a question rather than an issue.`;
|
|
101
|
+
export function hasCustomPrompt(repoRoot) {
|
|
102
|
+
return existsSync(join(repoRoot, CONFIG_DIR, PROMPT_FILE));
|
|
103
|
+
}
|
|
104
|
+
export function loadTemplate(repoRoot) {
|
|
105
|
+
const promptPath = join(repoRoot, CONFIG_DIR, PROMPT_FILE);
|
|
106
|
+
if (existsSync(promptPath)) {
|
|
107
|
+
return readFileSync(promptPath, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
return DEFAULT_PROMPT;
|
|
110
|
+
}
|
|
111
|
+
export function loadConfig(repoRoot) {
|
|
112
|
+
const configPath = join(repoRoot, CONFIG_DIR, CONFIG_FILE);
|
|
113
|
+
if (!existsSync(configPath)) {
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function formatFileList(files) {
|
|
124
|
+
if (files.length === 0)
|
|
125
|
+
return "_None_";
|
|
126
|
+
return files.map((f) => `- ${f}`).join("\n");
|
|
127
|
+
}
|
|
128
|
+
function buildPrMetadataBlock(meta) {
|
|
129
|
+
if (!meta)
|
|
130
|
+
return "";
|
|
131
|
+
return `## PR Metadata
|
|
132
|
+
**Title**: ${meta.title}
|
|
133
|
+
**Description**: ${meta.body}
|
|
134
|
+
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
export function buildPrompt(template, context) {
|
|
138
|
+
const { baseBranch, diffContent, changedFiles, categorizedFiles, customCategories, prMetadata, } = context;
|
|
139
|
+
const diffBlock = diffContent;
|
|
140
|
+
const fencedDiff = "```diff\n" + diffContent + "\n```";
|
|
141
|
+
let prompt = template;
|
|
142
|
+
prompt = prompt.replaceAll("{{BASE_BRANCH}}", baseBranch);
|
|
143
|
+
prompt = prompt.replaceAll("{{CHANGED_FILES}}", formatFileList(changedFiles));
|
|
144
|
+
prompt = prompt.replaceAll("{{SRC_FILES}}", formatFileList(categorizedFiles.source));
|
|
145
|
+
prompt = prompt.replaceAll("{{TEST_FILES}}", formatFileList(categorizedFiles.test));
|
|
146
|
+
prompt = prompt.replaceAll("{{MIGRATION_FILES}}", formatFileList(categorizedFiles.migration));
|
|
147
|
+
prompt = prompt.replaceAll("{{HTTP_FILES}}", formatFileList(categorizedFiles.http));
|
|
148
|
+
prompt = prompt.replaceAll("{{PR_TITLE}}", prMetadata?.title ?? "");
|
|
149
|
+
prompt = prompt.replaceAll("{{PR_BODY}}", prMetadata?.body ?? "");
|
|
150
|
+
prompt = prompt.replaceAll("{{PR_METADATA}}", buildPrMetadataBlock(prMetadata));
|
|
151
|
+
if (customCategories) {
|
|
152
|
+
for (const [name, files] of Object.entries(customCategories)) {
|
|
153
|
+
prompt = prompt.replaceAll(`{{CATEGORY.${name}}}`, formatFileList(files));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (prompt.includes("{{DIFF}}")) {
|
|
157
|
+
prompt = prompt.replaceAll("{{DIFF}}", diffBlock);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
prompt += "\n\n## Diff\n\n" + fencedDiff;
|
|
161
|
+
}
|
|
162
|
+
return prompt;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/lib/prompt.ts"],"names":[],"mappings":"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"}
|
package/dist/review.d.ts
ADDED
package/dist/review.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { isGitRepo, getRepoRoot, detectBaseBranch, ensureBranchUpToDate, getDiff, categorizeFiles, categorizeFilesWithConfig, getPrMetadata, } from "./lib/git.js";
|
|
2
|
+
import { loadTemplate, loadConfig, buildPrompt, hasCustomPrompt } from "./lib/prompt.js";
|
|
3
|
+
import { copyToClipboard, getPasteHint } from "./lib/clipboard.js";
|
|
4
|
+
export async function runReview(options) {
|
|
5
|
+
if (!isGitRepo()) {
|
|
6
|
+
console.error("❌ Not a git repository. Run this command from inside a git repo.");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const repoRoot = getRepoRoot();
|
|
10
|
+
let baseBranch;
|
|
11
|
+
try {
|
|
12
|
+
baseBranch = detectBaseBranch(options.base);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
console.error(`❌ ${err.message}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
ensureBranchUpToDate(baseBranch);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error(`❌ ${err.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
console.log(`Generating diff from ${baseBranch}...`);
|
|
26
|
+
const diff = getDiff(baseBranch, options.exclude);
|
|
27
|
+
if (!diff.content) {
|
|
28
|
+
console.error(`❌ No changes found compared to ${baseBranch}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.log("✅ Diff generated successfully");
|
|
32
|
+
console.log(`✅ Found ${diff.changedFiles.length} changed file(s) (${diff.lineCount} lines)`);
|
|
33
|
+
console.log("");
|
|
34
|
+
if (diff.lineCount > 2000) {
|
|
35
|
+
console.log(`⚠️ Large diff (${diff.lineCount} lines). Consider reviewing in chunks for better results.`);
|
|
36
|
+
console.log("");
|
|
37
|
+
}
|
|
38
|
+
const categorized = categorizeFiles(diff.changedFiles);
|
|
39
|
+
const prMetadata = getPrMetadata();
|
|
40
|
+
const template = loadTemplate(repoRoot);
|
|
41
|
+
const config = loadConfig(repoRoot);
|
|
42
|
+
const isCustom = hasCustomPrompt(repoRoot);
|
|
43
|
+
const customCategories = config.categories
|
|
44
|
+
? categorizeFilesWithConfig(diff.changedFiles, config.categories)
|
|
45
|
+
: undefined;
|
|
46
|
+
const prompt = buildPrompt(template, {
|
|
47
|
+
baseBranch,
|
|
48
|
+
diffContent: diff.content,
|
|
49
|
+
changedFiles: diff.changedFiles,
|
|
50
|
+
categorizedFiles: categorized,
|
|
51
|
+
customCategories,
|
|
52
|
+
prMetadata,
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
await copyToClipboard(prompt);
|
|
56
|
+
if (isCustom) {
|
|
57
|
+
console.log("📋 Using custom prompt from .review-my-pr/prompt.md");
|
|
58
|
+
}
|
|
59
|
+
console.log("✅ Review prompt copied to clipboard!");
|
|
60
|
+
console.log(getPasteHint());
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.error("❌ Failed to copy to clipboard. Outputting prompt to stdout:\n");
|
|
64
|
+
console.log(prompt);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
|