nayan-ai 1.0.0-beta.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.
@@ -0,0 +1,26 @@
1
+ name: Publish nayan-ai package to npm
2
+ on: [workflow_dispatch]
3
+
4
+ jobs:
5
+ publish:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v4
9
+
10
+ - name: Set up Node.js
11
+ uses: actions/setup-node@v4
12
+ with:
13
+ node-version: 20
14
+ registry-url: 'https://registry.npmjs.org/'
15
+ always-auth: true
16
+
17
+ - name: Install dependencies
18
+ run: npm install
19
+
20
+ - name: Build
21
+ run: npm run build
22
+
23
+ - name: Publish to npm
24
+ run: npm publish --access public
25
+ env:
26
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }}
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Nayan AI 🤖
2
+
3
+ A CLI tool that uses [Codex CLI](https://github.com/openai/codex) to review GitHub Pull Requests for bugs, security vulnerabilities, performance issues, error handling, edge cases, and code quality.
4
+
5
+ ## Features
6
+
7
+ - **🤖 Agentic Review**: Uses Codex CLI's intelligent coding agent for deep code analysis
8
+ - **🐛 Bug Detection**: Finds logic errors, null pointer issues, race conditions, and edge cases
9
+ - **🔐 Security Analysis**: Detects SQL injection, XSS, hardcoded secrets, and auth issues
10
+ - **⚡ Performance Checks**: Identifies memory leaks, N+1 queries, and unnecessary computations
11
+ - **🛡️ Error Handling**: Catches missing try/catch, unhandled promises, and silent failures
12
+ - **✅ Test Coverage**: Checks if tests are added for new functionality
13
+ - **🖥️ Local Execution**: Run reviews from your terminal, no GitHub Actions required
14
+ - **🏢 Enterprise Support**: Works with GitHub Enterprise Server (auto-detected)
15
+ - **🔒 Private Repos**: Full support for private repositories
16
+ - **💬 Inline Comments**: Posts review comments directly on the relevant lines of code
17
+ - **📊 Summary Report**: Provides an overview of all issues found
18
+
19
+ ## Installation
20
+
21
+ ### Prerequisites
22
+
23
+ 1. **Node.js 18+** - Required runtime
24
+ 2. **Codex CLI** - Login to Codex CLI first:
25
+ ```bash
26
+ npx @openai/codex login
27
+ ```
28
+
29
+ ### Install nayan-ai
30
+
31
+ ```bash
32
+ npm install -g nayan-ai
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Basic Usage
38
+
39
+ ```bash
40
+ # Review a PR using URL
41
+ nayan-ai https://github.com/owner/repo/pull/123 --token ghp_xxx
42
+ ```
43
+
44
+ ### Options
45
+
46
+ | Option | Description |
47
+ |--------|--------------------------------------------------------|
48
+ | `-t, --token` | GitHub personal access token to access code (required) |
49
+ | `-d, --dry` | Analyze without posting comments to Github |
50
+ | `-i, --inline` | Post inline comments on files instead of summary |
51
+
52
+ ## License
53
+
54
+ MIT
@@ -0,0 +1,3 @@
1
+ import type { CodeIssue, ReviewComment } from './types.js';
2
+ export declare function issuesToReviewComments(issues: CodeIssue[]): ReviewComment[];
3
+ export declare function generateSummary(issues: CodeIssue[]): string;
@@ -0,0 +1,66 @@
1
+ export function issuesToReviewComments(issues) {
2
+ return issues.map((issue) => {
3
+ const severityIcon = issue.severity === 'error' ? '🔴' : issue.severity === 'warning' ? '🟡' : '🔵';
4
+ const categoryIcon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
5
+ let body = `${severityIcon} **${categoryIcon} ${capitalize(issue.category)}**\n\n${issue.message}`;
6
+ if (issue.suggestion) {
7
+ body += `\n\n💡 **Suggestion:** ${issue.suggestion}`;
8
+ }
9
+ return {
10
+ path: issue.filename,
11
+ line: issue.line,
12
+ side: 'RIGHT',
13
+ body,
14
+ };
15
+ });
16
+ }
17
+ export function generateSummary(issues) {
18
+ if (issues.length === 0) {
19
+ return '## ✅ AI Code Review Complete\n\nNo issues found. The code looks good!';
20
+ }
21
+ const bySeverity = {
22
+ error: issues.filter((i) => i.severity === 'error'),
23
+ warning: issues.filter((i) => i.severity === 'warning'),
24
+ info: issues.filter((i) => i.severity === 'info'),
25
+ };
26
+ let summary = `## 🤖 AI Code Review Complete\n\nFound **${issues.length}** issue(s) in this PR.\n\n`;
27
+ // Group issues by severity and list them
28
+ if (bySeverity.error.length > 0) {
29
+ summary += `### 🔴 Errors (${bySeverity.error.length})\n\n`;
30
+ bySeverity.error.forEach((issue) => {
31
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
32
+ summary += `- ${icon} **\`${issue.filename}:${issue.line}\`**\n ${issue.message}`;
33
+ if (issue.suggestion) {
34
+ summary += `\n 💡 *${issue.suggestion}*`;
35
+ }
36
+ summary += '\n\n';
37
+ });
38
+ }
39
+ if (bySeverity.warning.length > 0) {
40
+ summary += `### 🟡 Warnings (${bySeverity.warning.length})\n\n`;
41
+ bySeverity.warning.forEach((issue) => {
42
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
43
+ summary += `- ${icon} **\`${issue.filename}:${issue.line}\`**\n ${issue.message}`;
44
+ if (issue.suggestion) {
45
+ summary += `\n 💡 *${issue.suggestion}*`;
46
+ }
47
+ summary += '\n\n';
48
+ });
49
+ }
50
+ if (bySeverity.info.length > 0) {
51
+ summary += `### 🔵 Info (${bySeverity.info.length})\n\n`;
52
+ bySeverity.info.forEach((issue) => {
53
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
54
+ summary += `- ${icon} **\`${issue.filename}:${issue.line}\`**\n ${issue.message}`;
55
+ if (issue.suggestion) {
56
+ summary += `\n 💡 *${issue.suggestion}*`;
57
+ }
58
+ summary += '\n\n';
59
+ });
60
+ }
61
+ return summary.trim();
62
+ }
63
+ function capitalize(str) {
64
+ return str.charAt(0).toUpperCase() + str.slice(1);
65
+ }
66
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { CodeIssue } from './types.js';
2
+ export interface ClaudeOptions {
3
+ verbose?: boolean;
4
+ }
5
+ export declare function analyzeWithClaude(repoPath: string, baseBranch: string | undefined, options: ClaudeOptions): Promise<CodeIssue[]>;
package/dist/claude.js ADDED
@@ -0,0 +1,118 @@
1
+ import { spawn } from 'child_process';
2
+ import chalk from 'chalk';
3
+ import { getReviewPrompt } from './prompt.js';
4
+ export async function analyzeWithClaude(repoPath, baseBranch = 'origin/main', options) {
5
+ const response = await runClaudeExec(repoPath, baseBranch, options);
6
+ return parseClaudeResponse(response);
7
+ }
8
+ async function runClaudeExec(repoPath, baseBranch, options) {
9
+ const prompt = getReviewPrompt(baseBranch);
10
+ return new Promise((resolve, reject) => {
11
+ // Use claude with -p for print mode and --output-format json
12
+ const args = [
13
+ '-p',
14
+ '--output-format', 'json',
15
+ '--dangerously-skip-permissions',
16
+ prompt
17
+ ];
18
+ if (options.verbose) {
19
+ console.log(`\n[Claude] Running: claude ${args.slice(0, 3).join(' ')} "<prompt>"`);
20
+ console.log(`[Claude] Working directory: ${repoPath}`);
21
+ }
22
+ const startTime = Date.now();
23
+ const child = spawn('claude', args, {
24
+ cwd: repoPath,
25
+ stdio: ['pipe', 'pipe', 'pipe'],
26
+ });
27
+ let stdout = '';
28
+ let stderr = '';
29
+ child.stdout.on('data', (data) => {
30
+ const chunk = data.toString();
31
+ stdout += chunk;
32
+ if (options.verbose) {
33
+ process.stdout.write(chunk);
34
+ }
35
+ else {
36
+ // Show progress dots
37
+ process.stdout.write(chalk.gray('.'));
38
+ }
39
+ });
40
+ child.stderr.on('data', (data) => {
41
+ const chunk = data.toString();
42
+ stderr += chunk;
43
+ if (options.verbose) {
44
+ process.stderr.write(chunk);
45
+ }
46
+ });
47
+ child.on('close', (code) => {
48
+ const elapsed = Date.now() - startTime;
49
+ if (!options.verbose) {
50
+ console.log(); // New line after progress dots
51
+ }
52
+ console.log(`\n[Claude] Completed in ${(elapsed / 1000).toFixed(1)}s`);
53
+ if (code !== 0) {
54
+ const errorMsg = stderr || stdout || 'Unknown error';
55
+ reject(new Error(`Claude review failed (exit ${code}): ${errorMsg.slice(0, 500)}`));
56
+ return;
57
+ }
58
+ resolve(stdout);
59
+ });
60
+ child.on('error', (err) => {
61
+ if (err.code === 'ENOENT') {
62
+ reject(new Error('claude CLI not found. Install Claude Code CLI first: https://code.claude.com'));
63
+ return;
64
+ }
65
+ reject(err);
66
+ });
67
+ });
68
+ }
69
+ function parseClaudeResponse(response) {
70
+ // Claude with --output-format json outputs a JSON object with result field
71
+ try {
72
+ const parsed = JSON.parse(response);
73
+ // Claude JSON output has a 'result' field with the text response
74
+ const text = parsed.result || parsed.text || response;
75
+ // Try to extract JSON from the text
76
+ const jsonMatch = text.match(/\{\s*"issues"\s*:\s*\[[\s\S]*?\]\s*\}/);
77
+ if (jsonMatch) {
78
+ const issuesJson = JSON.parse(jsonMatch[0]);
79
+ if (issuesJson.issues && Array.isArray(issuesJson.issues)) {
80
+ return issuesJson.issues
81
+ .filter((item) => item.message)
82
+ .map((item) => ({
83
+ filename: item.filename || 'unknown',
84
+ line: item.line || 0,
85
+ category: item.category || 'functionality',
86
+ severity: item.severity || 'info',
87
+ message: item.message,
88
+ suggestion: item.suggestion,
89
+ }));
90
+ }
91
+ }
92
+ }
93
+ catch {
94
+ // Not valid JSON wrapper, try direct extraction
95
+ }
96
+ // Fallback: try to find raw JSON in the response
97
+ const jsonMatch = response.match(/\{\s*"issues"\s*:\s*\[[\s\S]*?\]\s*\}/);
98
+ if (jsonMatch) {
99
+ try {
100
+ const parsed = JSON.parse(jsonMatch[0]);
101
+ return (parsed.issues || [])
102
+ .filter((item) => item.message)
103
+ .map((item) => ({
104
+ filename: item.filename || 'unknown',
105
+ line: item.line || 0,
106
+ category: item.category || 'functionality',
107
+ severity: item.severity || 'info',
108
+ message: item.message,
109
+ suggestion: item.suggestion,
110
+ }));
111
+ }
112
+ catch {
113
+ // ignore
114
+ }
115
+ }
116
+ return [];
117
+ }
118
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import { GitHubClient, parseFiles, parsePRReference } from './github.js';
6
+ import { issuesToReviewComments, generateSummary } from './analyzer.js';
7
+ import { analyzeWithCodex } from './codex.js';
8
+ import { analyzeWithClaude } from './claude.js';
9
+ import { cloneRepo } from './repo.js';
10
+ import { createRequire } from 'module';
11
+ const require = createRequire(import.meta.url);
12
+ const { version } = require('../package.json');
13
+ program
14
+ .name('nayan-ai')
15
+ .description('AI-powered PR code reviewer using Codex CLI')
16
+ .version(version)
17
+ .argument('<pr-url>', 'GitHub PR URL (e.g., https://github.com/owner/repo/pull/123)')
18
+ .requiredOption('-t, --token <token>', 'GitHub personal access token')
19
+ .option('-v, --verbose', 'Show real-time output from Codex CLI', false)
20
+ .option('-d, --dry', 'Only analyze, don\'t post comments', false)
21
+ .option('-i, --inline', 'Post inline comments instead of summary', false)
22
+ .option('-l, --llm <provider>', 'LLM provider: codex (default) or claude', 'codex')
23
+ .option('--format <format>', 'Output format: text, json', 'text')
24
+ .action(run);
25
+ program.parse();
26
+ async function run(prUrl, options) {
27
+ try {
28
+ const prInfo = parsePRReference(prUrl);
29
+ const githubUrl = prInfo.githubUrl;
30
+ console.log(chalk.bold.blue('\n🤖 Nayan AI - PR Reviewer'));
31
+ console.log('━'.repeat(40));
32
+ console.log(` Repository: ${chalk.cyan(`${prInfo.owner}/${prInfo.repo}`)}`);
33
+ console.log(` PR Number: ${chalk.cyan(`#${prInfo.number}`)}`);
34
+ if (githubUrl) {
35
+ console.log(` GitHub: ${chalk.cyan(githubUrl)}`);
36
+ }
37
+ if (options.dry) {
38
+ console.log(` Mode: ${chalk.yellow('Dry Run (no comments will be posted)')}`);
39
+ }
40
+ console.log('━'.repeat(40) + '\n');
41
+ const github = new GitHubClient(options.token, githubUrl);
42
+ // Fetch PR details
43
+ let spinner = ora('Fetching PR details...').start();
44
+ const pr = await github.getPullRequest(prInfo);
45
+ spinner.succeed(`PR: ${pr.title}`);
46
+ // Fetch changed files
47
+ spinner = ora('Fetching changed files...').start();
48
+ const files = await github.getPullRequestFiles(prInfo);
49
+ const fileChanges = parseFiles(files);
50
+ spinner.succeed(`Found ${fileChanges.length} files with changes`);
51
+ // Clone and analyze with Codex CLI
52
+ spinner = ora('Cloning repository...').start();
53
+ const repo = await cloneRepo(prInfo, options.token, githubUrl);
54
+ let issues;
55
+ try {
56
+ spinner.succeed('Repository cloned');
57
+ const llmName = options.llm === 'claude' ? 'Claude Code' : 'Codex';
58
+ console.log(chalk.cyan(`Running code review with ${llmName} CLI...\n`));
59
+ if (options.llm === 'claude') {
60
+ const claudeOpts = { verbose: options.verbose };
61
+ issues = await analyzeWithClaude(repo.path, 'origin/main', claudeOpts);
62
+ }
63
+ else {
64
+ const codexOpts = { verbose: options.verbose };
65
+ issues = await analyzeWithCodex(repo.path, 'origin/main', codexOpts);
66
+ }
67
+ console.log(chalk.green(`\n✔ Analysis complete: ${issues.length} issues found`));
68
+ }
69
+ finally {
70
+ await repo.cleanup();
71
+ }
72
+ // Display results
73
+ console.log(chalk.bold('\n📋 Review Summary'));
74
+ console.log('─'.repeat(41));
75
+ if (options.format === 'json') {
76
+ console.log(JSON.stringify(issues, null, 2));
77
+ }
78
+ else {
79
+ printIssuesSummary(issues);
80
+ }
81
+ // Post to GitHub
82
+ if (!options.dry) {
83
+ spinner = ora('Posting review to GitHub...').start();
84
+ if (options.inline) {
85
+ // Post inline comments on specific lines
86
+ const reviewComments = issuesToReviewComments(issues);
87
+ const summary = generateSummary(issues);
88
+ await github.postReview(prInfo, pr.head.sha, summary, reviewComments);
89
+ spinner.succeed('Inline review posted to GitHub');
90
+ }
91
+ else {
92
+ // Post summary as a PR-level comment (default)
93
+ const summary = generateSummary(issues);
94
+ await github.postComment(prInfo, summary);
95
+ spinner.succeed('Review summary posted to GitHub');
96
+ }
97
+ }
98
+ else {
99
+ console.log(chalk.yellow('\nDry run mode - no comments posted to GitHub'));
100
+ }
101
+ console.log(chalk.green('\n✅ Review complete!\n'));
102
+ }
103
+ catch (error) {
104
+ console.error(chalk.red('\nError:'), error instanceof Error ? error.message : error);
105
+ process.exit(1);
106
+ }
107
+ }
108
+ function printIssuesSummary(issues) {
109
+ if (issues.length === 0) {
110
+ console.log(chalk.green(' No issues found! The code looks good.'));
111
+ return;
112
+ }
113
+ const bySeverity = {
114
+ error: issues.filter((i) => i.severity === 'error'),
115
+ warning: issues.filter((i) => i.severity === 'warning'),
116
+ info: issues.filter((i) => i.severity === 'info'),
117
+ };
118
+ // Print errors
119
+ if (bySeverity.error.length > 0) {
120
+ console.log(chalk.red.bold(`\n 🔴 Errors (${bySeverity.error.length}):`));
121
+ for (const issue of bySeverity.error) {
122
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
123
+ console.log(chalk.red(` ${icon} ${issue.filename}:${issue.line}`));
124
+ console.log(` ${issue.message}`);
125
+ if (issue.suggestion) {
126
+ console.log(chalk.dim(` 💡 ${issue.suggestion}`));
127
+ }
128
+ }
129
+ }
130
+ // Print warnings
131
+ if (bySeverity.warning.length > 0) {
132
+ console.log(chalk.yellow.bold(`\n 🟡 Warnings (${bySeverity.warning.length}):`));
133
+ for (const issue of bySeverity.warning) {
134
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
135
+ console.log(chalk.yellow(` ${icon} ${issue.filename}:${issue.line}`));
136
+ console.log(` ${issue.message}`);
137
+ if (issue.suggestion) {
138
+ console.log(chalk.dim(` 💡 ${issue.suggestion}`));
139
+ }
140
+ }
141
+ }
142
+ // Print info
143
+ if (bySeverity.info.length > 0) {
144
+ console.log(chalk.blue.bold(`\n 🔵 Info (${bySeverity.info.length}):`));
145
+ for (const issue of bySeverity.info) {
146
+ const icon = issue.category === 'functionality' ? '🐛' : issue.category === 'readability' ? '📖' : '⚡';
147
+ console.log(chalk.blue(` ${icon} ${issue.filename}:${issue.line}`));
148
+ console.log(` ${issue.message}`);
149
+ if (issue.suggestion) {
150
+ console.log(chalk.dim(` 💡 ${issue.suggestion}`));
151
+ }
152
+ }
153
+ }
154
+ }
155
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { CodeIssue } from './types.js';
2
+ export interface CodexOptions {
3
+ verbose?: boolean;
4
+ }
5
+ export declare function analyzeWithCodex(repoPath: string, baseBranch: string | undefined, options: CodexOptions): Promise<CodeIssue[]>;
package/dist/codex.js ADDED
@@ -0,0 +1,129 @@
1
+ import { spawn } from 'child_process';
2
+ import { logProcessor } from './logs.js';
3
+ import { getReviewPrompt } from './prompt.js';
4
+ export async function analyzeWithCodex(repoPath, baseBranch = 'origin/main', options) {
5
+ const response = await runCodexExec(repoPath, baseBranch, options);
6
+ return parseCodexResponse(response);
7
+ }
8
+ async function runCodexExec(repoPath, baseBranch, options) {
9
+ const prompt = getReviewPrompt(baseBranch);
10
+ return new Promise((resolve, reject) => {
11
+ // Use codex exec with --json for structured output
12
+ const args = ['@openai/codex', 'exec', '--json', '--full-auto', prompt];
13
+ if (options.verbose) {
14
+ console.log(`\n[Codex] Running: npx ${args.join(' ')}`);
15
+ console.log(`[Codex] Working directory: ${repoPath}`);
16
+ }
17
+ const startTime = Date.now();
18
+ const child = spawn('npx', args, {
19
+ cwd: repoPath,
20
+ stdio: ['pipe', 'pipe', 'pipe'],
21
+ });
22
+ let stdout = '';
23
+ let stderr = '';
24
+ // Reset log processor for new analysis
25
+ logProcessor.reset();
26
+ child.stdout.on('data', (data) => {
27
+ const chunk = data.toString();
28
+ stdout += chunk;
29
+ // Parse JSONL and show progress
30
+ for (const line of chunk.split('\n')) {
31
+ if (!line.trim())
32
+ continue;
33
+ try {
34
+ const event = JSON.parse(line);
35
+ if (options.verbose) {
36
+ console.log(JSON.stringify(event, null, 2));
37
+ }
38
+ else {
39
+ // Use log processor for nice interactive output
40
+ logProcessor.processEvent(event);
41
+ }
42
+ }
43
+ catch {
44
+ // Not JSON
45
+ }
46
+ }
47
+ });
48
+ child.stderr.on('data', (data) => {
49
+ const chunk = data.toString();
50
+ stderr += chunk;
51
+ if (options.verbose) {
52
+ process.stderr.write(chunk);
53
+ }
54
+ });
55
+ child.on('close', (code) => {
56
+ const elapsed = Date.now() - startTime;
57
+ console.log(`\n[Codex] Completed in ${(elapsed / 1000).toFixed(1)}s`);
58
+ if (code !== 0) {
59
+ const errorMsg = stderr || stdout || 'Unknown error';
60
+ reject(new Error(`Codex review failed (exit ${code}): ${errorMsg.slice(0, 500)}`));
61
+ return;
62
+ }
63
+ resolve(stdout);
64
+ });
65
+ child.on('error', (err) => {
66
+ if (err.code === 'ENOENT') {
67
+ reject(new Error('npx not found. Install Node.js/npm (Node 18+) to run nayan-ai.'));
68
+ return;
69
+ }
70
+ reject(err);
71
+ });
72
+ });
73
+ }
74
+ function parseCodexResponse(response) {
75
+ // codex exec --json outputs JSONL - parse each line looking for agent_message with issues
76
+ const lines = response.split('\n');
77
+ for (const line of lines) {
78
+ if (!line.trim())
79
+ continue;
80
+ try {
81
+ const event = JSON.parse(line);
82
+ // Look for agent_message in item.completed events
83
+ if (event.type === 'item.completed' && event.item?.type === 'agent_message') {
84
+ const text = event.item.text;
85
+ if (text) {
86
+ // Parse the JSON from the agent's message
87
+ const issuesJson = JSON.parse(text);
88
+ if (issuesJson.issues && Array.isArray(issuesJson.issues)) {
89
+ return issuesJson.issues
90
+ .filter((item) => item.message)
91
+ .map((item) => ({
92
+ filename: item.filename || 'unknown',
93
+ line: item.line || 0,
94
+ category: item.category || 'functionality',
95
+ severity: item.severity || 'info',
96
+ message: item.message,
97
+ suggestion: item.suggestion,
98
+ }));
99
+ }
100
+ }
101
+ }
102
+ }
103
+ catch {
104
+ // Not valid JSON, skip
105
+ }
106
+ }
107
+ // Fallback: try to find raw JSON in the response
108
+ const jsonMatch = response.match(/\{\s*"issues"\s*:\s*\[[\s\S]*?\]\s*\}/);
109
+ if (jsonMatch) {
110
+ try {
111
+ const parsed = JSON.parse(jsonMatch[0]);
112
+ return (parsed.issues || [])
113
+ .filter((item) => item.message)
114
+ .map((item) => ({
115
+ filename: item.filename || 'unknown',
116
+ line: item.line || 0,
117
+ category: item.category || 'functionality',
118
+ severity: item.severity || 'info',
119
+ message: item.message,
120
+ suggestion: item.suggestion,
121
+ }));
122
+ }
123
+ catch {
124
+ // ignore
125
+ }
126
+ }
127
+ return [];
128
+ }
129
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../src/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { PRInfo, PullRequest, PullRequestFile, FileChange, ReviewComment } from './types.js';
2
+ export declare class GitHubClient {
3
+ private token;
4
+ private apiBase;
5
+ constructor(token: string, githubUrl?: string);
6
+ private fetch;
7
+ getPullRequest(pr: PRInfo): Promise<PullRequest>;
8
+ getPullRequestFiles(pr: PRInfo): Promise<PullRequestFile[]>;
9
+ postReview(pr: PRInfo, commitId: string, body: string, comments: ReviewComment[]): Promise<void>;
10
+ postComment(pr: PRInfo, body: string): Promise<void>;
11
+ }
12
+ export declare function parseFiles(files: PullRequestFile[]): FileChange[];
13
+ export declare function parsePRReference(input: string): PRInfo & {
14
+ githubUrl?: string;
15
+ };