claude-issue-solver 1.23.1 → 1.24.1
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 +23 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +259 -0
- package/dist/commands/review.js +133 -16
- package/dist/index.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,7 @@ claude-issue --help
|
|
|
157
157
|
| `claude-issue show <number>` | - | Show full issue details |
|
|
158
158
|
| `claude-issue pr <number>` | - | Create PR for solved issue |
|
|
159
159
|
| `claude-issue review [number]` | - | Review PRs with AI suggestions |
|
|
160
|
+
| `claude-issue config` | - | Manage settings (bot token) |
|
|
160
161
|
| `claude-issue clean [number]` | `rm` | Remove worktree and branch |
|
|
161
162
|
| `claude-issue go [number]` | - | Navigate to worktree |
|
|
162
163
|
| `claude-issue init` | - | Setup wizard for requirements |
|
|
@@ -176,6 +177,11 @@ claude-issue --help
|
|
|
176
177
|
- `-a, --all` - Clean all issue worktrees (with confirmation)
|
|
177
178
|
- `-m, --merged` - Clean only worktrees with merged PRs (no confirmation)
|
|
178
179
|
|
|
180
|
+
**`config` command:**
|
|
181
|
+
- `cis config` - Show current configuration
|
|
182
|
+
- `cis config bot-token` - Set up a bot token for reviews
|
|
183
|
+
- `cis config --clear` - Clear all configuration
|
|
184
|
+
|
|
179
185
|
## How it works
|
|
180
186
|
|
|
181
187
|
1. **Fetches issue** - Gets title and description from GitHub
|
|
@@ -256,6 +262,23 @@ Branches are named `issue-{number}-{slug}` where the slug is:
|
|
|
256
262
|
- Bracket prefixes removed (e.g., `[Bug]` is stripped)
|
|
257
263
|
- Duplicate consecutive words removed (e.g., `fix-fix-bug` → `fix-bug`)
|
|
258
264
|
|
|
265
|
+
### AI Code Review
|
|
266
|
+
The `review` command lets Claude review PRs and post suggestions:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
cis review # Select PRs to review (parallel)
|
|
270
|
+
cis review 42 # Review specific issue's PR
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Claude auto-detects whether you're reviewing your own PR or someone else's:
|
|
274
|
+
- **Your own PR**: Posts comments with suggestions (GitHub limitation)
|
|
275
|
+
- **Someone else's PR**: Can approve or request changes
|
|
276
|
+
|
|
277
|
+
**Bot Token (Optional)**: Set up a bot token to get full review capabilities on your own PRs:
|
|
278
|
+
```bash
|
|
279
|
+
cis config bot-token # Interactive setup with instructions
|
|
280
|
+
```
|
|
281
|
+
|
|
259
282
|
## Tips
|
|
260
283
|
|
|
261
284
|
- PRs are created automatically when Claude makes commits - no need to wait until the end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface Config {
|
|
2
|
+
botToken?: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function getConfig(): Config;
|
|
5
|
+
export declare function saveConfig(config: Config): void;
|
|
6
|
+
export declare function getBotToken(): string | undefined;
|
|
7
|
+
export declare function configCommand(action?: string, value?: string): Promise<void>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getConfig = getConfig;
|
|
40
|
+
exports.saveConfig = saveConfig;
|
|
41
|
+
exports.getBotToken = getBotToken;
|
|
42
|
+
exports.configCommand = configCommand;
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
const CONFIG_DIR = path.join(os.homedir(), '.claude-issue-solver');
|
|
50
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
51
|
+
function getConfig() {
|
|
52
|
+
try {
|
|
53
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
54
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore errors
|
|
59
|
+
}
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
function saveConfig(config) {
|
|
63
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
64
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
65
|
+
}
|
|
66
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
67
|
+
}
|
|
68
|
+
function getBotToken() {
|
|
69
|
+
return getConfig().botToken;
|
|
70
|
+
}
|
|
71
|
+
function validateToken(token) {
|
|
72
|
+
try {
|
|
73
|
+
const output = (0, child_process_1.execSync)('gh api user --jq .login', {
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
76
|
+
env: { ...process.env, GH_TOKEN: token },
|
|
77
|
+
});
|
|
78
|
+
return { valid: true, login: output.trim() };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return { valid: false, error: error.message };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function configCommand(action, value) {
|
|
85
|
+
// Handle clear action
|
|
86
|
+
if (action === '--clear' || action === 'clear') {
|
|
87
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
88
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
89
|
+
console.log(chalk_1.default.green('\n✅ Configuration cleared.\n'));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(chalk_1.default.dim('\nNo configuration to clear.\n'));
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Handle bot-token action
|
|
97
|
+
if (action === 'bot-token') {
|
|
98
|
+
if (value) {
|
|
99
|
+
// Direct token provided
|
|
100
|
+
console.log(chalk_1.default.dim('\nValidating token...'));
|
|
101
|
+
const result = validateToken(value);
|
|
102
|
+
if (result.valid) {
|
|
103
|
+
const config = getConfig();
|
|
104
|
+
config.botToken = value;
|
|
105
|
+
saveConfig(config);
|
|
106
|
+
console.log(chalk_1.default.green(`\n✅ Bot token saved! Authenticated as: ${result.login}\n`));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(chalk_1.default.red('\n❌ Invalid token. Please check and try again.\n'));
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Interactive setup
|
|
114
|
+
await setupBotToken();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Show current config
|
|
118
|
+
const config = getConfig();
|
|
119
|
+
console.log(chalk_1.default.bold('\nClaude Issue Solver Configuration\n'));
|
|
120
|
+
if (config.botToken) {
|
|
121
|
+
const result = validateToken(config.botToken);
|
|
122
|
+
if (result.valid) {
|
|
123
|
+
console.log(` Bot token: ${chalk_1.default.green('✓ Configured')} (${result.login})`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.log(` Bot token: ${chalk_1.default.yellow('⚠ Invalid or expired')}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.log(` Bot token: ${chalk_1.default.dim('Not configured')}`);
|
|
131
|
+
}
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(chalk_1.default.dim('Commands:'));
|
|
134
|
+
console.log(chalk_1.default.dim(' cis config bot-token Set up a bot token for reviews'));
|
|
135
|
+
console.log(chalk_1.default.dim(' cis config --clear Clear all configuration'));
|
|
136
|
+
console.log();
|
|
137
|
+
}
|
|
138
|
+
async function setupBotToken() {
|
|
139
|
+
console.log(chalk_1.default.bold('\n🤖 Bot Token Setup\n'));
|
|
140
|
+
console.log(`A bot token allows Claude to post formal reviews (approve/request changes)
|
|
141
|
+
on your own PRs. Without it, Claude can only post comments on your own PRs.
|
|
142
|
+
|
|
143
|
+
${chalk_1.default.bold('You have two options:')}\n`);
|
|
144
|
+
const { choice } = await inquirer_1.default.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: 'list',
|
|
147
|
+
name: 'choice',
|
|
148
|
+
message: 'How would you like to set up the bot?',
|
|
149
|
+
choices: [
|
|
150
|
+
{ name: 'Use my existing account (create a token)', value: 'same-account' },
|
|
151
|
+
{ name: 'Create a separate bot account (recommended for teams)', value: 'new-account' },
|
|
152
|
+
{ name: 'I already have a token', value: 'have-token' },
|
|
153
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
]);
|
|
157
|
+
if (choice === 'cancel') {
|
|
158
|
+
console.log(chalk_1.default.dim('\nCancelled.\n'));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (choice === 'have-token') {
|
|
162
|
+
await promptForToken();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (choice === 'new-account') {
|
|
166
|
+
console.log(chalk_1.default.bold('\n📝 Creating a Bot Account\n'));
|
|
167
|
+
console.log(`1. Sign out of GitHub or open an incognito window
|
|
168
|
+
2. Create a new account (e.g., ${chalk_1.default.cyan('yourname-bot')})
|
|
169
|
+
- Use email alias: ${chalk_1.default.cyan('you+bot@gmail.com')}
|
|
170
|
+
3. Add the bot as a collaborator to your repos:
|
|
171
|
+
- Repo → Settings → Collaborators → Add the bot
|
|
172
|
+
4. Log into the bot account and create a token (next step)
|
|
173
|
+
`);
|
|
174
|
+
const { ready } = await inquirer_1.default.prompt([
|
|
175
|
+
{
|
|
176
|
+
type: 'confirm',
|
|
177
|
+
name: 'ready',
|
|
178
|
+
message: 'Ready to create a token for the bot account?',
|
|
179
|
+
default: true,
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
if (!ready) {
|
|
183
|
+
console.log(chalk_1.default.dim('\nRun `cis config bot-token` when ready.\n'));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Open GitHub token page
|
|
188
|
+
console.log(chalk_1.default.bold('\n🔑 Creating a Personal Access Token\n'));
|
|
189
|
+
console.log(`I'll open GitHub's token creation page. Create a token with:
|
|
190
|
+
|
|
191
|
+
${chalk_1.default.bold('Settings:')}
|
|
192
|
+
• Token name: ${chalk_1.default.cyan('claude-issue-solver-bot')}
|
|
193
|
+
• Expiration: ${chalk_1.default.cyan('90 days')} (or your preference)
|
|
194
|
+
• Repository access: ${chalk_1.default.cyan('Only select repositories')} (pick your repos)
|
|
195
|
+
|
|
196
|
+
${chalk_1.default.bold('Permissions needed:')}
|
|
197
|
+
• ${chalk_1.default.cyan('Pull requests')}: Read and write
|
|
198
|
+
• ${chalk_1.default.cyan('Contents')}: Read (to see PR diffs)
|
|
199
|
+
`);
|
|
200
|
+
const { openBrowser } = await inquirer_1.default.prompt([
|
|
201
|
+
{
|
|
202
|
+
type: 'confirm',
|
|
203
|
+
name: 'openBrowser',
|
|
204
|
+
message: 'Open GitHub token page in browser?',
|
|
205
|
+
default: true,
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
if (openBrowser) {
|
|
209
|
+
const tokenUrl = 'https://github.com/settings/personal-access-tokens/new';
|
|
210
|
+
try {
|
|
211
|
+
if (process.platform === 'darwin') {
|
|
212
|
+
(0, child_process_1.execSync)(`open "${tokenUrl}"`, { stdio: 'pipe' });
|
|
213
|
+
}
|
|
214
|
+
else if (process.platform === 'linux') {
|
|
215
|
+
(0, child_process_1.execSync)(`xdg-open "${tokenUrl}"`, { stdio: 'pipe' });
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.log(chalk_1.default.dim(`\nOpen this URL: ${tokenUrl}\n`));
|
|
219
|
+
}
|
|
220
|
+
console.log(chalk_1.default.dim('\nOpened GitHub in your browser.\n'));
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
console.log(chalk_1.default.dim(`\nOpen this URL: ${tokenUrl}\n`));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
console.log(chalk_1.default.dim('\nGo to: https://github.com/settings/personal-access-tokens/new\n'));
|
|
228
|
+
}
|
|
229
|
+
await promptForToken();
|
|
230
|
+
}
|
|
231
|
+
async function promptForToken() {
|
|
232
|
+
const { token } = await inquirer_1.default.prompt([
|
|
233
|
+
{
|
|
234
|
+
type: 'password',
|
|
235
|
+
name: 'token',
|
|
236
|
+
message: 'Paste your token here:',
|
|
237
|
+
mask: '*',
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
if (!token) {
|
|
241
|
+
console.log(chalk_1.default.dim('\nNo token provided.\n'));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
console.log(chalk_1.default.dim('\nValidating token...'));
|
|
245
|
+
const result = validateToken(token);
|
|
246
|
+
if (result.valid) {
|
|
247
|
+
const config = getConfig();
|
|
248
|
+
config.botToken = token;
|
|
249
|
+
saveConfig(config);
|
|
250
|
+
console.log(chalk_1.default.green(`\n✅ Bot token saved!`));
|
|
251
|
+
console.log(chalk_1.default.dim(` Authenticated as: ${result.login}`));
|
|
252
|
+
console.log(chalk_1.default.dim(` Config stored in: ${CONFIG_FILE}\n`));
|
|
253
|
+
console.log(`Now when you run ${chalk_1.default.cyan('cis review')}, Claude can post formal reviews.\n`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log(chalk_1.default.red('\n❌ Invalid token. Please check and try again.'));
|
|
257
|
+
console.log(chalk_1.default.dim(' Run `cis config bot-token` to retry.\n'));
|
|
258
|
+
}
|
|
259
|
+
}
|
package/dist/commands/review.js
CHANGED
|
@@ -47,6 +47,7 @@ const child_process_1 = require("child_process");
|
|
|
47
47
|
const github_1 = require("../utils/github");
|
|
48
48
|
const git_1 = require("../utils/git");
|
|
49
49
|
const helpers_1 = require("../utils/helpers");
|
|
50
|
+
const config_1 = require("./config");
|
|
50
51
|
async function reviewCommand(issueNumber) {
|
|
51
52
|
const spinner = (0, ora_1.default)(`Fetching issue #${issueNumber}...`).start();
|
|
52
53
|
const issue = (0, github_1.getIssue)(issueNumber);
|
|
@@ -151,6 +152,9 @@ async function reviewCommand(issueNumber) {
|
|
|
151
152
|
catch {
|
|
152
153
|
console.log(chalk_1.default.yellow('Could not fetch PR diff, Claude will review the files directly.'));
|
|
153
154
|
}
|
|
155
|
+
// Check for bot token early to include in prompt
|
|
156
|
+
const botToken = (0, config_1.getBotToken)();
|
|
157
|
+
const ghCmd = botToken ? `GH_TOKEN=\${BOT_TOKEN} gh` : 'gh';
|
|
154
158
|
// Build the review prompt
|
|
155
159
|
const prompt = `You are reviewing PR #${prNumber} for issue #${issueNumber}: ${issue.title}
|
|
156
160
|
|
|
@@ -167,10 +171,19 @@ Review the code changes in this PR. Look for:
|
|
|
167
171
|
6. Performance problems
|
|
168
172
|
|
|
169
173
|
## How to Leave Feedback
|
|
170
|
-
|
|
174
|
+
${botToken ? `
|
|
175
|
+
**IMPORTANT: A bot token is configured. Use this prefix for ALL gh commands:**
|
|
176
|
+
\`\`\`bash
|
|
177
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ...
|
|
178
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr comment ...
|
|
179
|
+
\`\`\`
|
|
180
|
+
The BOT_TOKEN environment variable is already set in this terminal.
|
|
181
|
+
|
|
182
|
+
You can use formal reviews (approve/request-changes):
|
|
171
183
|
|
|
172
184
|
\`\`\`bash
|
|
173
|
-
|
|
185
|
+
# Post suggestions as review comments
|
|
186
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${prNumber} --comment --body "**File: path/to/file.ts**
|
|
174
187
|
|
|
175
188
|
Description of the issue...
|
|
176
189
|
|
|
@@ -178,20 +191,57 @@ Description of the issue...
|
|
|
178
191
|
// Your suggested fix here
|
|
179
192
|
\\\`\\\`\\\`
|
|
180
193
|
"
|
|
194
|
+
|
|
195
|
+
# Final verdict
|
|
196
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${prNumber} --approve --body "LGTM! Code looks good."
|
|
197
|
+
# OR
|
|
198
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${prNumber} --request-changes --body "Please address the issues above."
|
|
199
|
+
\`\`\`
|
|
200
|
+
` : `
|
|
201
|
+
First, check if you can post formal reviews by running these commands:
|
|
202
|
+
\`\`\`bash
|
|
203
|
+
# Get PR author
|
|
204
|
+
PR_AUTHOR=$(gh pr view ${prNumber} --json author --jq .author.login)
|
|
205
|
+
# Get current user
|
|
206
|
+
CURRENT_USER=$(gh api user --jq .login)
|
|
207
|
+
echo "PR author: $PR_AUTHOR, Current user: $CURRENT_USER"
|
|
181
208
|
\`\`\`
|
|
182
209
|
|
|
183
|
-
|
|
210
|
+
### If PR author ≠ Current user (reviewing someone else's PR):
|
|
211
|
+
You can use formal reviews with approve/request-changes:
|
|
184
212
|
|
|
185
|
-
For a final review summary:
|
|
186
213
|
\`\`\`bash
|
|
187
|
-
|
|
214
|
+
# Post suggestions as review comments
|
|
215
|
+
gh pr review ${prNumber} --comment --body "**File: path/to/file.ts**
|
|
188
216
|
|
|
189
|
-
|
|
190
|
-
|
|
217
|
+
Description of the issue...
|
|
218
|
+
|
|
219
|
+
\\\`\\\`\\\`suggestion
|
|
220
|
+
// Your suggested fix here
|
|
221
|
+
\\\`\\\`\\\`
|
|
191
222
|
"
|
|
223
|
+
|
|
224
|
+
# Final verdict
|
|
225
|
+
gh pr review ${prNumber} --approve --body "LGTM! Code looks good."
|
|
226
|
+
# OR
|
|
227
|
+
gh pr review ${prNumber} --request-changes --body "Please address the issues above."
|
|
192
228
|
\`\`\`
|
|
193
229
|
|
|
194
|
-
|
|
230
|
+
### If PR author = Current user (reviewing your own PR):
|
|
231
|
+
You can only post comments (GitHub doesn't allow self-review):
|
|
232
|
+
|
|
233
|
+
\`\`\`bash
|
|
234
|
+
gh pr comment ${prNumber} --body "**File: path/to/file.ts**
|
|
235
|
+
|
|
236
|
+
Description of the issue...
|
|
237
|
+
|
|
238
|
+
\\\`\\\`\\\`suggestion
|
|
239
|
+
// Your suggested fix here
|
|
240
|
+
\\\`\\\`\\\`
|
|
241
|
+
"
|
|
242
|
+
\`\`\`
|
|
243
|
+
`}
|
|
244
|
+
The \`suggestion\` code block creates a "Commit suggestion" button on GitHub.
|
|
195
245
|
|
|
196
246
|
## PR Diff
|
|
197
247
|
${diffContent ? `\n\`\`\`diff\n${diffContent.slice(0, 50000)}\n\`\`\`\n` : 'Run `gh pr diff ' + prNumber + '` to see the changes.'}
|
|
@@ -200,11 +250,19 @@ Start by examining the diff and the changed files, then provide your review.`;
|
|
|
200
250
|
// Write prompt to a file
|
|
201
251
|
const promptFile = path.join(worktreePath, '.claude-review-prompt.txt');
|
|
202
252
|
fs.writeFileSync(promptFile, prompt);
|
|
253
|
+
// Bot token already fetched above
|
|
254
|
+
const botTokenEnv = botToken ? `export BOT_TOKEN="${botToken}"\nexport GH_TOKEN="${botToken}"` : '# No bot token configured';
|
|
255
|
+
const botNote = botToken
|
|
256
|
+
? 'Using bot token for reviews (can approve/request changes)'
|
|
257
|
+
: 'No bot token - using your account (may have limitations on own PRs)';
|
|
203
258
|
// Create runner script for review
|
|
204
259
|
const runnerScript = path.join(worktreePath, '.claude-review-runner.sh');
|
|
205
260
|
const runnerContent = `#!/bin/bash
|
|
206
261
|
cd "${worktreePath}"
|
|
207
262
|
|
|
263
|
+
# Set bot token if configured
|
|
264
|
+
${botTokenEnv}
|
|
265
|
+
|
|
208
266
|
# Set terminal title
|
|
209
267
|
echo -ne "\\033]0;Review PR #${prNumber}: ${issue.title.replace(/"/g, '\\"').slice(0, 50)}\\007"
|
|
210
268
|
|
|
@@ -213,6 +271,8 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
213
271
|
echo ""
|
|
214
272
|
echo "Issue #${issueNumber}: ${issue.title.replace(/"/g, '\\"')}"
|
|
215
273
|
echo ""
|
|
274
|
+
echo "${botNote}"
|
|
275
|
+
echo ""
|
|
216
276
|
echo "Claude will review the PR and post suggestions."
|
|
217
277
|
echo "You can commit suggestions directly on GitHub."
|
|
218
278
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
@@ -388,6 +448,8 @@ async function launchReviewForPR(pr, projectRoot, projectName) {
|
|
|
388
448
|
issueBody = issue.body;
|
|
389
449
|
}
|
|
390
450
|
}
|
|
451
|
+
// Check for bot token
|
|
452
|
+
const botToken = (0, config_1.getBotToken)();
|
|
391
453
|
// Build the review prompt
|
|
392
454
|
const prompt = `You are reviewing PR #${pr.number}: ${pr.title}
|
|
393
455
|
${pr.issueNumber ? `\n## Related Issue #${pr.issueNumber}\n${issueBody}\n` : ''}
|
|
@@ -401,10 +463,19 @@ Review the code changes in this PR. Look for:
|
|
|
401
463
|
6. Performance problems
|
|
402
464
|
|
|
403
465
|
## How to Leave Feedback
|
|
404
|
-
|
|
466
|
+
${botToken ? `
|
|
467
|
+
**IMPORTANT: A bot token is configured. Use this prefix for ALL gh commands:**
|
|
468
|
+
\`\`\`bash
|
|
469
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ...
|
|
470
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr comment ...
|
|
471
|
+
\`\`\`
|
|
472
|
+
The BOT_TOKEN environment variable is already set in this terminal.
|
|
473
|
+
|
|
474
|
+
You can use formal reviews (approve/request-changes):
|
|
405
475
|
|
|
406
476
|
\`\`\`bash
|
|
407
|
-
|
|
477
|
+
# Post suggestions as review comments
|
|
478
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${pr.number} --comment --body "**File: path/to/file.ts**
|
|
408
479
|
|
|
409
480
|
Description of the issue...
|
|
410
481
|
|
|
@@ -412,20 +483,57 @@ Description of the issue...
|
|
|
412
483
|
// Your suggested fix here
|
|
413
484
|
\\\`\\\`\\\`
|
|
414
485
|
"
|
|
486
|
+
|
|
487
|
+
# Final verdict
|
|
488
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${pr.number} --approve --body "LGTM! Code looks good."
|
|
489
|
+
# OR
|
|
490
|
+
GH_TOKEN=\${BOT_TOKEN} gh pr review ${pr.number} --request-changes --body "Please address the issues above."
|
|
491
|
+
\`\`\`
|
|
492
|
+
` : `
|
|
493
|
+
First, check if you can post formal reviews by running these commands:
|
|
494
|
+
\`\`\`bash
|
|
495
|
+
# Get PR author
|
|
496
|
+
PR_AUTHOR=$(gh pr view ${pr.number} --json author --jq .author.login)
|
|
497
|
+
# Get current user
|
|
498
|
+
CURRENT_USER=$(gh api user --jq .login)
|
|
499
|
+
echo "PR author: $PR_AUTHOR, Current user: $CURRENT_USER"
|
|
415
500
|
\`\`\`
|
|
416
501
|
|
|
417
|
-
|
|
502
|
+
### If PR author ≠ Current user (reviewing someone else's PR):
|
|
503
|
+
You can use formal reviews with approve/request-changes:
|
|
418
504
|
|
|
419
|
-
For a final review summary:
|
|
420
505
|
\`\`\`bash
|
|
421
|
-
|
|
506
|
+
# Post suggestions as review comments
|
|
507
|
+
gh pr review ${pr.number} --comment --body "**File: path/to/file.ts**
|
|
422
508
|
|
|
423
|
-
|
|
424
|
-
|
|
509
|
+
Description of the issue...
|
|
510
|
+
|
|
511
|
+
\\\`\\\`\\\`suggestion
|
|
512
|
+
// Your suggested fix here
|
|
513
|
+
\\\`\\\`\\\`
|
|
425
514
|
"
|
|
515
|
+
|
|
516
|
+
# Final verdict
|
|
517
|
+
gh pr review ${pr.number} --approve --body "LGTM! Code looks good."
|
|
518
|
+
# OR
|
|
519
|
+
gh pr review ${pr.number} --request-changes --body "Please address the issues above."
|
|
426
520
|
\`\`\`
|
|
427
521
|
|
|
428
|
-
|
|
522
|
+
### If PR author = Current user (reviewing your own PR):
|
|
523
|
+
You can only post comments (GitHub doesn't allow self-review):
|
|
524
|
+
|
|
525
|
+
\`\`\`bash
|
|
526
|
+
gh pr comment ${pr.number} --body "**File: path/to/file.ts**
|
|
527
|
+
|
|
528
|
+
Description of the issue...
|
|
529
|
+
|
|
530
|
+
\\\`\\\`\\\`suggestion
|
|
531
|
+
// Your suggested fix here
|
|
532
|
+
\\\`\\\`\\\`
|
|
533
|
+
"
|
|
534
|
+
\`\`\`
|
|
535
|
+
`}
|
|
536
|
+
The \`suggestion\` code block creates a "Commit suggestion" button on GitHub.
|
|
429
537
|
|
|
430
538
|
## PR Diff
|
|
431
539
|
${diffContent ? `\n\`\`\`diff\n${diffContent.slice(0, 50000)}\n\`\`\`\n` : 'Run `gh pr diff ' + pr.number + '` to see the changes.'}
|
|
@@ -434,12 +542,19 @@ Start by examining the diff and the changed files, then provide your review.`;
|
|
|
434
542
|
// Write prompt to a file
|
|
435
543
|
const promptFile = path.join(worktreePath, '.claude-review-prompt.txt');
|
|
436
544
|
fs.writeFileSync(promptFile, prompt);
|
|
545
|
+
const botTokenEnv = botToken ? `export BOT_TOKEN="${botToken}"\nexport GH_TOKEN="${botToken}"` : '# No bot token configured';
|
|
546
|
+
const botNote = botToken
|
|
547
|
+
? 'Using bot token for reviews (can approve/request changes)'
|
|
548
|
+
: 'No bot token - using your account (may have limitations on own PRs)';
|
|
437
549
|
// Create runner script for review
|
|
438
550
|
const runnerScript = path.join(worktreePath, '.claude-review-runner.sh');
|
|
439
551
|
const escapedTitle = pr.title.replace(/"/g, '\\"').slice(0, 50);
|
|
440
552
|
const runnerContent = `#!/bin/bash
|
|
441
553
|
cd "${worktreePath}"
|
|
442
554
|
|
|
555
|
+
# Set bot token if configured
|
|
556
|
+
${botTokenEnv}
|
|
557
|
+
|
|
443
558
|
# Set terminal title
|
|
444
559
|
echo -ne "\\033]0;Review PR #${pr.number}: ${escapedTitle}\\007"
|
|
445
560
|
|
|
@@ -448,6 +563,8 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
448
563
|
echo ""
|
|
449
564
|
echo "${escapedTitle}"
|
|
450
565
|
echo ""
|
|
566
|
+
echo "${botNote}"
|
|
567
|
+
echo ""
|
|
451
568
|
echo "Claude will review the PR and post suggestions."
|
|
452
569
|
echo "You can commit suggestions directly on GitHub."
|
|
453
570
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const new_1 = require("./commands/new");
|
|
|
18
18
|
const init_1 = require("./commands/init");
|
|
19
19
|
const show_1 = require("./commands/show");
|
|
20
20
|
const review_1 = require("./commands/review");
|
|
21
|
+
const config_1 = require("./commands/config");
|
|
21
22
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
22
23
|
const packageJson = require('../package.json');
|
|
23
24
|
const program = new commander_1.Command();
|
|
@@ -168,4 +169,11 @@ program
|
|
|
168
169
|
await (0, review_1.selectReviewCommand)();
|
|
169
170
|
}
|
|
170
171
|
});
|
|
172
|
+
// Config command - manage settings
|
|
173
|
+
program
|
|
174
|
+
.command('config [action] [value]')
|
|
175
|
+
.description('Manage configuration (bot-token, --clear)')
|
|
176
|
+
.action(async (action, value) => {
|
|
177
|
+
await (0, config_1.configCommand)(action, value);
|
|
178
|
+
});
|
|
171
179
|
program.parse();
|