@_mustachio/ai-review-agent 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/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # @_mustachio/ai-review-agent
2
+
3
+ AI-powered pull request code review agent. Works as a CLI tool, npm library, or GitHub Action. Supports GitHub and Bitbucket.
4
+
5
+ ## Features
6
+
7
+ - **Inline comments** on specific lines of the PR diff
8
+ - **Hunk-aware context** — reads code surrounding changed lines, not just the diff
9
+ - **Diff chunking** — splits large PRs into parallel review chunks
10
+ - **Custom rules** — point at a file or directory of coding standards (SOLID, CLEAN, etc.)
11
+ - **Configurable prompt** — bring your own review prompt template
12
+ - **Multi-platform** — GitHub Actions and Bitbucket Pipelines
13
+ - **JSON output** — use `--output-only` for scripting and custom integrations
14
+ - **Prompt injection mitigation** — PR metadata wrapped in delimiters
15
+ - **Stale review cleanup** — dismisses/deletes previous AI reviews on new commits
16
+ - **Binary file detection** — skips images, compiled files, etc.
17
+
18
+ ## Quick Start
19
+
20
+ ### npx (any CI or local)
21
+
22
+ ```bash
23
+ npx @_mustachio/ai-review-agent \
24
+ --platform github \
25
+ --token $GH_TOKEN \
26
+ --rules ./docs/standards/
27
+ ```
28
+
29
+ ### GitHub Actions
30
+
31
+ ```yaml
32
+ name: AI Code Review
33
+
34
+ on:
35
+ pull_request:
36
+ types: [opened, synchronize, reopened]
37
+
38
+ jobs:
39
+ review:
40
+ runs-on: ubuntu-latest
41
+ permissions:
42
+ pull-requests: write
43
+ contents: read
44
+ env:
45
+ GH_TOKEN: ${{ github.token }}
46
+
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ with:
50
+ fetch-depth: 0
51
+
52
+ - uses: actions/setup-node@v4
53
+ with:
54
+ node-version: '24'
55
+
56
+ - uses: ./packages/ai-review-agent
57
+ with:
58
+ severity-threshold: 'blocking'
59
+ rules: docs/standards/standards.md
60
+ opencode-config: ${{ github.workspace }}/.github/opencode.json
61
+ ```
62
+
63
+ When published, replace `uses: ./packages/ai-review-agent` with:
64
+
65
+ ```yaml
66
+ - uses: the-human-mustachio/ai-review-agent@v1
67
+ ```
68
+
69
+ ### Bitbucket Pipelines
70
+
71
+ ```yaml
72
+ pipelines:
73
+ pull-requests:
74
+ '**':
75
+ - step:
76
+ name: AI Code Review
77
+ script:
78
+ - npx @_mustachio/ai-review-agent --rules ./docs/standards/ --severity-threshold blocking
79
+ # BB_TOKEN must be set as a secured repository variable
80
+ # with pullrequest:write and repository:write scopes
81
+ ```
82
+
83
+ ### Local / JSON Output
84
+
85
+ ```bash
86
+ # Just get the review as JSON (no comments posted)
87
+ npx @_mustachio/ai-review-agent \
88
+ --output-only \
89
+ --base-branch main \
90
+ --rules ./standards/
91
+ ```
92
+
93
+ ## CLI Options
94
+
95
+ ```
96
+ PLATFORMS:
97
+ --platform <name> github, bitbucket (auto-detected if omitted)
98
+ --token <token> API token (or set GH_TOKEN / BB_TOKEN env var)
99
+
100
+ PR METADATA (auto-detected in CI):
101
+ --pr-number <n> PR number
102
+ --pr-title <title> PR title
103
+ --pr-author <author> PR author
104
+ --pr-body <body> PR description
105
+ --base-branch <branch> Base branch (default: main)
106
+
107
+ REVIEW OPTIONS:
108
+ --prompt <path> Custom prompt template
109
+ --rules <path> Rules file or directory
110
+ --exclude <patterns> Comma-separated exclude globs
111
+ --max-diff-size <n> Max diff chars per chunk (default: 100000)
112
+ --severity-threshold <s> Fail threshold: blocking, warning, info
113
+ --opencode-version <v> Pinned opencode-ai version (default: 0.2.21)
114
+ --opencode-config <path> OpenCode config file
115
+ --api-key <key> AI provider API key
116
+ --post-review <bool> Post approve/request-changes (default: true)
117
+
118
+ OUTPUT:
119
+ --output-only Print JSON to stdout, exit 0 (approved) or 1 (rejected)
120
+ --help Show help
121
+ ```
122
+
123
+ ## GitHub Action Inputs
124
+
125
+ | Input | Description | Default |
126
+ |-------|-------------|---------|
127
+ | `api-key` | AI provider API key (optional if set in env) | |
128
+ | `severity-threshold` | Fail threshold: `blocking`, `warning`, `info` | `blocking` |
129
+ | `prompt` | Path to custom prompt template | built-in |
130
+ | `post-review` | Post approve/request-changes review | `true` |
131
+ | `max-diff-size` | Max diff size per chunk (chars) | `100000` |
132
+ | `exclude-patterns` | Comma-separated exclude globs | |
133
+ | `opencode-config` | Path to OpenCode config file | |
134
+ | `rules` | Path to rules file or directory | |
135
+ | `opencode-version` | Pinned opencode-ai version | `0.2.21` |
136
+
137
+ ## Custom Rules
138
+
139
+ Point `--rules` at a single file or a directory:
140
+
141
+ ```bash
142
+ # Single file
143
+ --rules docs/standards/standards.md
144
+
145
+ # Directory (all files concatenated with headers)
146
+ --rules .github/review-rules/
147
+ ```
148
+
149
+ Example rules directory:
150
+
151
+ ```
152
+ .github/review-rules/
153
+ SOLID.md
154
+ CLEAN.md
155
+ security.md
156
+ testing.md
157
+ ```
158
+
159
+ Each file's content is injected into the review prompt as evaluation criteria.
160
+
161
+ ## Output Format
162
+
163
+ The review JSON (from `--output-only` or the internal pipeline):
164
+
165
+ ```json
166
+ {
167
+ "approve": false,
168
+ "summary": "PR introduces a clean architecture violation.",
169
+ "issues": [
170
+ {
171
+ "severity": "blocking",
172
+ "message": "Core imports from infra — violates dependency rule",
173
+ "file": "packages/core/src/organization/organization.usecase.ts",
174
+ "line": 3,
175
+ "endLine": 4
176
+ }
177
+ ],
178
+ "recommendation": "Remove the infra import and use a repository port."
179
+ }
180
+ ```
181
+
182
+ ## Platform Differences
183
+
184
+ | Capability | GitHub | Bitbucket |
185
+ |---|---|---|
186
+ | Inline comments | PR review API | Individual comment API |
187
+ | Block merge | `REQUEST_CHANGES` review | Build status `FAILED` |
188
+ | Allow merge | `APPROVE` review | Build status `SUCCESSFUL` + approve |
189
+ | Stale cleanup | Dismiss previous reviews | Delete previous comments |
190
+ | Auth token | Automatic `GITHUB_TOKEN` | Manual `BB_TOKEN` (secured variable) |
191
+ | PR metadata | From event payload | Fetched via API |
192
+
193
+ ## Library Usage
194
+
195
+ ```js
196
+ const { runFullReview } = require('@_mustachio/ai-review-agent');
197
+
198
+ const review = await runFullReview({
199
+ baseBranch: 'main',
200
+ prTitle: 'Add feature X',
201
+ prAuthor: 'dev',
202
+ prBody: 'Description here',
203
+ prNumber: 42,
204
+ rulesPath: './standards.md',
205
+ });
206
+
207
+ console.log(review.approve); // true/false
208
+ console.log(review.issues); // [{severity, message, file, line}]
209
+ ```
210
+
211
+ ## License
212
+
213
+ MIT
package/action.yml ADDED
@@ -0,0 +1,57 @@
1
+ name: 'AI Code Review'
2
+ description: 'AI-powered PR code review using OpenCode'
3
+ branding:
4
+ icon: 'eye'
5
+ color: 'purple'
6
+
7
+ inputs:
8
+ api-key:
9
+ description: 'API key for the AI provider (e.g. Anthropic, OpenAI). Optional if already set in environment.'
10
+ required: false
11
+ default: ''
12
+ severity-threshold:
13
+ description: 'Minimum severity to block merge (blocking, warning, info)'
14
+ required: false
15
+ default: 'blocking'
16
+ prompt:
17
+ description: 'Path to a custom prompt template file'
18
+ required: false
19
+ default: ''
20
+ post-review:
21
+ description: 'Whether to post a PR review (approve/request-changes) in addition to a comment'
22
+ required: false
23
+ default: 'true'
24
+ max-diff-size:
25
+ description: 'Maximum diff size in characters before truncation (default: 100000)'
26
+ required: false
27
+ default: '100000'
28
+ exclude-patterns:
29
+ description: 'Comma-separated glob patterns of files to exclude from the diff (e.g. "*.lock,dist/**")'
30
+ required: false
31
+ default: ''
32
+ opencode-config:
33
+ description: 'Path to OpenCode config file'
34
+ required: false
35
+ default: ''
36
+ rules:
37
+ description: 'Path to a file or directory of review rules. If a directory, all files are loaded and concatenated. Rules are injected into the prompt as evaluation criteria.'
38
+ required: false
39
+ default: ''
40
+ opencode-version:
41
+ description: 'Pinned version of opencode-ai to install (default: 0.2.21)'
42
+ required: false
43
+ default: '0.2.21'
44
+
45
+ outputs:
46
+ approved:
47
+ description: 'Whether the review approved the PR (true/false)'
48
+ summary:
49
+ description: 'Review summary text'
50
+ issues-count:
51
+ description: 'Total number of issues found'
52
+ blocking-count:
53
+ description: 'Number of blocking issues found'
54
+
55
+ runs:
56
+ using: 'node24'
57
+ main: 'dist/index.js'
package/bin/cli.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { runFullReview, shouldFailForThreshold, countBySeverity } = require('../src/core/engine');
4
+ const githubPlatform = require('../src/platforms/github');
5
+ const bitbucketPlatform = require('../src/platforms/bitbucket');
6
+
7
+ const PLATFORMS = {
8
+ github: githubPlatform,
9
+ bitbucket: bitbucketPlatform,
10
+ };
11
+
12
+ async function main() {
13
+ const args = parseArgs(process.argv.slice(2));
14
+
15
+ if (args.help) {
16
+ printUsage();
17
+ process.exit(0);
18
+ }
19
+
20
+ const log = console.log;
21
+
22
+ // Detect or use specified platform
23
+ let platformName = args.platform;
24
+ if (!platformName && !args['output-only']) {
25
+ for (const [name, platform] of Object.entries(PLATFORMS)) {
26
+ if (platform.detect()) {
27
+ platformName = name;
28
+ log(`Auto-detected platform: ${name}`);
29
+ break;
30
+ }
31
+ }
32
+ }
33
+
34
+ // Get PR metadata — from platform or CLI args
35
+ let prMeta;
36
+ if (platformName && !args['output-only']) {
37
+ const platform = PLATFORMS[platformName];
38
+ if (!platform) {
39
+ console.error(`Unknown platform: ${platformName}. Supported: ${Object.keys(PLATFORMS).join(', ')}`);
40
+ process.exit(1);
41
+ }
42
+ const token = args.token || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.BB_TOKEN || process.env.BITBUCKET_TOKEN;
43
+ prMeta = await platform.getPrMetadata(token);
44
+ } else {
45
+ // Manual mode — require CLI args
46
+ prMeta = {
47
+ prNumber: args['pr-number'] || '0',
48
+ prTitle: args['pr-title'] || '',
49
+ prAuthor: args['pr-author'] || '',
50
+ prBody: args['pr-body'] || '',
51
+ baseBranch: args['base-branch'] || 'main',
52
+ };
53
+ }
54
+
55
+ // Run the review
56
+ const review = await runFullReview({
57
+ baseBranch: prMeta.baseBranch,
58
+ prTitle: prMeta.prTitle,
59
+ prAuthor: prMeta.prAuthor,
60
+ prBody: prMeta.prBody,
61
+ prNumber: prMeta.prNumber,
62
+ promptPath: args.prompt || undefined,
63
+ rulesPath: args.rules || undefined,
64
+ excludePatterns: args.exclude || '',
65
+ maxDiffSize: parseInt(args['max-diff-size'] || '100000', 10),
66
+ opencodeVersion: args['opencode-version'] || undefined,
67
+ opencodeConfig: args['opencode-config'] || undefined,
68
+ apiKey: args['api-key'] || undefined,
69
+ log,
70
+ });
71
+
72
+ // Output JSON if requested
73
+ if (args['output-only']) {
74
+ console.log(JSON.stringify(review, null, 2));
75
+ process.exit(review.approve ? 0 : 1);
76
+ }
77
+
78
+ // Post to platform
79
+ const token = args.token || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.BB_TOKEN || process.env.BITBUCKET_TOKEN;
80
+ if (!token) {
81
+ console.error('Error: No token provided. Use --token or set GH_TOKEN / BB_TOKEN env var.');
82
+ process.exit(1);
83
+ }
84
+
85
+ const platform = PLATFORMS[platformName];
86
+ await platform.postReview(review, {
87
+ prNumber: prMeta.prNumber,
88
+ token,
89
+ postReview: args['post-review'] !== 'false',
90
+ log,
91
+ });
92
+
93
+ // Check threshold
94
+ const threshold = args['severity-threshold'] || 'blocking';
95
+ const fail = shouldFailForThreshold(review, threshold);
96
+ if (fail) {
97
+ console.error(fail);
98
+ process.exit(1);
99
+ }
100
+
101
+ const blockingCount = countBySeverity(review.issues, 'blocking');
102
+ log(`Review complete. Approved: ${review.approve}. Issues: ${review.issues.length} (${blockingCount} blocking).`);
103
+ }
104
+
105
+ function parseArgs(argv) {
106
+ const args = {};
107
+ for (let i = 0; i < argv.length; i++) {
108
+ const arg = argv[i];
109
+ if (arg.startsWith('--')) {
110
+ const key = arg.slice(2);
111
+ const next = argv[i + 1];
112
+ if (!next || next.startsWith('--')) {
113
+ args[key] = true;
114
+ } else {
115
+ args[key] = next;
116
+ i++;
117
+ }
118
+ }
119
+ }
120
+ return args;
121
+ }
122
+
123
+ function printUsage() {
124
+ console.log(`
125
+ ai-review-agent - AI-powered code review for pull requests
126
+
127
+ USAGE:
128
+ ai-review-agent [options]
129
+
130
+ PLATFORMS:
131
+ --platform <name> Platform to use: github, bitbucket (auto-detected if omitted)
132
+ --token <token> API token for posting reviews (or set GH_TOKEN / BB_TOKEN env var)
133
+
134
+ PR METADATA (auto-detected in CI, required for --output-only):
135
+ --pr-number <n> PR number
136
+ --pr-title <title> PR title
137
+ --pr-author <author> PR author
138
+ --pr-body <body> PR description
139
+ --base-branch <branch> Base branch (default: main)
140
+
141
+ REVIEW OPTIONS:
142
+ --prompt <path> Path to custom prompt template
143
+ --rules <path> Path to rules file or directory
144
+ --exclude <patterns> Comma-separated glob patterns to exclude
145
+ --max-diff-size <n> Max diff size in chars (default: 100000)
146
+ --severity-threshold <s> Fail threshold: blocking, warning, info (default: blocking)
147
+ --opencode-version <v> Pinned opencode-ai version
148
+ --opencode-config <path> Path to OpenCode config file
149
+ --api-key <key> API key for AI provider
150
+ --post-review <bool> Post approve/request-changes review (default: true)
151
+
152
+ OUTPUT:
153
+ --output-only Print JSON review to stdout and exit (no platform posting)
154
+ --help Show this help
155
+ `);
156
+ }
157
+
158
+ main().catch((err) => {
159
+ console.error(`Fatal: ${err.message}`);
160
+ process.exit(1);
161
+ });
@@ -0,0 +1,41 @@
1
+ You are an expert code reviewer. All context is provided below — do NOT use any tools, do NOT read any files, do NOT explore the codebase.
2
+
3
+ Respond with ONLY valid JSON (no markdown, no code blocks, no extra text).
4
+
5
+ Schema:
6
+ {
7
+ "approve": boolean,
8
+ "summary": "1-2 sentence overview",
9
+ "issues": [
10
+ {
11
+ "severity": "blocking" | "warning" | "info",
12
+ "message": "concise description under 120 chars",
13
+ "file": "path/to/file.ts",
14
+ "line": 42,
15
+ "endLine": 45
16
+ }
17
+ ],
18
+ "recommendation": "brief next steps"
19
+ }
20
+
21
+ Rules:
22
+ - approve=true only if NO blocking issues
23
+ - blocking = bugs, security issues, breaking changes
24
+ - warning = code quality, missing tests, improvements
25
+ - info = minor suggestions, style nits
26
+ - "file" must be an exact path from the diff header (e.g. the "b/" path)
27
+ - "line" must be a line number from the diff's @@ hunk headers (the "+" side)
28
+ - "endLine" is optional, only include if the issue spans multiple lines
29
+ - Maximum 10 issues total
30
+ - Some files (lockfiles, build artifacts, the review action itself) are intentionally excluded. Do NOT flag missing files unless the diff has broken imports.
31
+
32
+ {{RULES}}
33
+
34
+ PR Title: {{PR_TITLE}}
35
+ Author: {{PR_AUTHOR}}
36
+ Description: {{PR_BODY}}
37
+
38
+ {{CONTEXT}}
39
+
40
+ Diff:
41
+ {{DIFF}}