@ariso-ai/ivan 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.
Files changed (150) hide show
  1. package/README.md +412 -0
  2. package/dist/agent.d.ts +11 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +48 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/config/config.d.ts +20 -0
  7. package/dist/config/config.d.ts.map +1 -0
  8. package/dist/config/config.js +187 -0
  9. package/dist/config/config.js.map +1 -0
  10. package/dist/config.d.ts +46 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +414 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/database/database.d.ts +12 -0
  15. package/dist/database/database.d.ts.map +1 -0
  16. package/dist/database/database.js +45 -0
  17. package/dist/database/database.js.map +1 -0
  18. package/dist/database/migration.d.ts +11 -0
  19. package/dist/database/migration.d.ts.map +1 -0
  20. package/dist/database/migration.js +64 -0
  21. package/dist/database/migration.js.map +1 -0
  22. package/dist/database/migrations/001_create_jobs_table.d.ts +3 -0
  23. package/dist/database/migrations/001_create_jobs_table.d.ts.map +1 -0
  24. package/dist/database/migrations/001_create_jobs_table.js +14 -0
  25. package/dist/database/migrations/001_create_jobs_table.js.map +1 -0
  26. package/dist/database/migrations/001_initial_schema.d.ts +3 -0
  27. package/dist/database/migrations/001_initial_schema.d.ts.map +1 -0
  28. package/dist/database/migrations/001_initial_schema.js +66 -0
  29. package/dist/database/migrations/001_initial_schema.js.map +1 -0
  30. package/dist/database/migrations/002_create_tasks_table.d.ts +3 -0
  31. package/dist/database/migrations/002_create_tasks_table.d.ts.map +1 -0
  32. package/dist/database/migrations/002_create_tasks_table.js +16 -0
  33. package/dist/database/migrations/002_create_tasks_table.js.map +1 -0
  34. package/dist/database/migrations/003_add_log_to_tasks.d.ts +3 -0
  35. package/dist/database/migrations/003_add_log_to_tasks.d.ts.map +1 -0
  36. package/dist/database/migrations/003_add_log_to_tasks.js +11 -0
  37. package/dist/database/migrations/003_add_log_to_tasks.js.map +1 -0
  38. package/dist/database/migrations/004_add_branch_to_tasks.d.ts +3 -0
  39. package/dist/database/migrations/004_add_branch_to_tasks.d.ts.map +1 -0
  40. package/dist/database/migrations/004_add_branch_to_tasks.js +11 -0
  41. package/dist/database/migrations/004_add_branch_to_tasks.js.map +1 -0
  42. package/dist/database/migrations/005_add_type_to_tasks.d.ts +3 -0
  43. package/dist/database/migrations/005_add_type_to_tasks.d.ts.map +1 -0
  44. package/dist/database/migrations/005_add_type_to_tasks.js +11 -0
  45. package/dist/database/migrations/005_add_type_to_tasks.js.map +1 -0
  46. package/dist/database/migrations/006_add_comment_url_and_commit_to_tasks.d.ts +3 -0
  47. package/dist/database/migrations/006_add_comment_url_and_commit_to_tasks.d.ts.map +1 -0
  48. package/dist/database/migrations/006_add_comment_url_and_commit_to_tasks.js +15 -0
  49. package/dist/database/migrations/006_add_comment_url_and_commit_to_tasks.js.map +1 -0
  50. package/dist/database/migrations/006_add_comment_url_to_tasks.d.ts +3 -0
  51. package/dist/database/migrations/006_add_comment_url_to_tasks.d.ts.map +1 -0
  52. package/dist/database/migrations/006_add_comment_url_to_tasks.js +14 -0
  53. package/dist/database/migrations/006_add_comment_url_to_tasks.js.map +1 -0
  54. package/dist/database/migrations/007_add_commit_to_tasks.d.ts +3 -0
  55. package/dist/database/migrations/007_add_commit_to_tasks.d.ts.map +1 -0
  56. package/dist/database/migrations/007_add_commit_to_tasks.js +13 -0
  57. package/dist/database/migrations/007_add_commit_to_tasks.js.map +1 -0
  58. package/dist/database/migrations/008_add_lint_and_test_task_type.d.ts +3 -0
  59. package/dist/database/migrations/008_add_lint_and_test_task_type.d.ts.map +1 -0
  60. package/dist/database/migrations/008_add_lint_and_test_task_type.js +42 -0
  61. package/dist/database/migrations/008_add_lint_and_test_task_type.js.map +1 -0
  62. package/dist/database/migrations/index.d.ts +3 -0
  63. package/dist/database/migrations/index.d.ts.map +1 -0
  64. package/dist/database/migrations/index.js +19 -0
  65. package/dist/database/migrations/index.js.map +1 -0
  66. package/dist/database/types.d.ts +34 -0
  67. package/dist/database/types.d.ts.map +1 -0
  68. package/dist/database/types.js +2 -0
  69. package/dist/database/types.js.map +1 -0
  70. package/dist/database.d.ts +13 -0
  71. package/dist/database.d.ts.map +1 -0
  72. package/dist/database.js +32 -0
  73. package/dist/database.js.map +1 -0
  74. package/dist/index.d.ts +3 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +285 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/scripts/task-executor.d.ts +3 -0
  79. package/dist/scripts/task-executor.d.ts.map +1 -0
  80. package/dist/scripts/task-executor.js +139 -0
  81. package/dist/scripts/task-executor.js.map +1 -0
  82. package/dist/scripts/task-planner.d.ts +3 -0
  83. package/dist/scripts/task-planner.d.ts.map +1 -0
  84. package/dist/scripts/task-planner.js +81 -0
  85. package/dist/scripts/task-planner.js.map +1 -0
  86. package/dist/services/address-executor.d.ts +13 -0
  87. package/dist/services/address-executor.d.ts.map +1 -0
  88. package/dist/services/address-executor.js +202 -0
  89. package/dist/services/address-executor.js.map +1 -0
  90. package/dist/services/address-task-executor.d.ts +19 -0
  91. package/dist/services/address-task-executor.d.ts.map +1 -0
  92. package/dist/services/address-task-executor.js +736 -0
  93. package/dist/services/address-task-executor.js.map +1 -0
  94. package/dist/services/claude-cli-executor.d.ts +14 -0
  95. package/dist/services/claude-cli-executor.d.ts.map +1 -0
  96. package/dist/services/claude-cli-executor.js +241 -0
  97. package/dist/services/claude-cli-executor.js.map +1 -0
  98. package/dist/services/claude-executor.d.ts +14 -0
  99. package/dist/services/claude-executor.d.ts.map +1 -0
  100. package/dist/services/claude-executor.js +274 -0
  101. package/dist/services/claude-executor.js.map +1 -0
  102. package/dist/services/claude-planner.d.ts +15 -0
  103. package/dist/services/claude-planner.d.ts.map +1 -0
  104. package/dist/services/claude-planner.js +107 -0
  105. package/dist/services/claude-planner.js.map +1 -0
  106. package/dist/services/docker-orchestrator.d.ts +11 -0
  107. package/dist/services/docker-orchestrator.d.ts.map +1 -0
  108. package/dist/services/docker-orchestrator.js +85 -0
  109. package/dist/services/docker-orchestrator.js.map +1 -0
  110. package/dist/services/executor-factory.d.ts +14 -0
  111. package/dist/services/executor-factory.d.ts.map +1 -0
  112. package/dist/services/executor-factory.js +14 -0
  113. package/dist/services/executor-factory.js.map +1 -0
  114. package/dist/services/git-manager.d.ts +36 -0
  115. package/dist/services/git-manager.d.ts.map +1 -0
  116. package/dist/services/git-manager.js +728 -0
  117. package/dist/services/git-manager.js.map +1 -0
  118. package/dist/services/index.d.ts +9 -0
  119. package/dist/services/index.d.ts.map +1 -0
  120. package/dist/services/index.js +9 -0
  121. package/dist/services/index.js.map +1 -0
  122. package/dist/services/job-manager.d.ts +30 -0
  123. package/dist/services/job-manager.d.ts.map +1 -0
  124. package/dist/services/job-manager.js +337 -0
  125. package/dist/services/job-manager.js.map +1 -0
  126. package/dist/services/openai-service.d.ts +14 -0
  127. package/dist/services/openai-service.d.ts.map +1 -0
  128. package/dist/services/openai-service.js +186 -0
  129. package/dist/services/openai-service.js.map +1 -0
  130. package/dist/services/pr-service.d.ts +31 -0
  131. package/dist/services/pr-service.d.ts.map +1 -0
  132. package/dist/services/pr-service.js +291 -0
  133. package/dist/services/pr-service.js.map +1 -0
  134. package/dist/services/repository-manager.d.ts +12 -0
  135. package/dist/services/repository-manager.d.ts.map +1 -0
  136. package/dist/services/repository-manager.js +101 -0
  137. package/dist/services/repository-manager.js.map +1 -0
  138. package/dist/services/task-executor.d.ts +20 -0
  139. package/dist/services/task-executor.d.ts.map +1 -0
  140. package/dist/services/task-executor.js +717 -0
  141. package/dist/services/task-executor.js.map +1 -0
  142. package/dist/types/non-interactive-config.d.ts +28 -0
  143. package/dist/types/non-interactive-config.d.ts.map +1 -0
  144. package/dist/types/non-interactive-config.js +2 -0
  145. package/dist/types/non-interactive-config.js.map +1 -0
  146. package/dist/web-server.d.ts +16 -0
  147. package/dist/web-server.d.ts.map +1 -0
  148. package/dist/web-server.js +488 -0
  149. package/dist/web-server.js.map +1 -0
  150. package/package.json +71 -0
@@ -0,0 +1,186 @@
1
+ import OpenAI from 'openai';
2
+ import { ConfigManager } from '../config.js';
3
+ export class OpenAIService {
4
+ openai = null;
5
+ configManager;
6
+ constructor() {
7
+ this.configManager = new ConfigManager();
8
+ }
9
+ async ensureInitialized() {
10
+ if (this.openai)
11
+ return;
12
+ let config = this.configManager.getConfig();
13
+ if (!config?.openaiApiKey || config.openaiApiKey === '') {
14
+ // Prompt for the API key
15
+ await this.configManager.promptForMissingConfig('openaiApiKey');
16
+ config = this.configManager.getConfig();
17
+ }
18
+ if (!config?.openaiApiKey) {
19
+ throw new Error('Failed to obtain OpenAI API key');
20
+ }
21
+ this.openai = new OpenAI({
22
+ apiKey: config.openaiApiKey
23
+ });
24
+ }
25
+ async getClient() {
26
+ await this.ensureInitialized();
27
+ if (!this.openai) {
28
+ throw new Error('OpenAI client not initialized');
29
+ }
30
+ return this.openai;
31
+ }
32
+ async generateCommitMessage(diff, changedFiles) {
33
+ await this.ensureInitialized();
34
+ const prompt = `
35
+ Generate a concise git commit message for the following changes.
36
+
37
+ Changed files:
38
+ ${changedFiles.map(file => `- ${file}`).join('\n')}
39
+
40
+ Git diff:
41
+ \`\`\`
42
+ ${diff}
43
+ \`\`\`
44
+
45
+ Rules:
46
+ - Use conventional commit format (feat:, fix:, refactor:, etc.)
47
+ - Keep it under 50 characters for the subject line
48
+ - Focus on what was changed and why
49
+ - Be specific and clear
50
+
51
+ Return only the commit message, nothing else.`;
52
+ try {
53
+ if (!this.openai) {
54
+ throw new Error('OpenAI client not initialized');
55
+ }
56
+ const response = await this.openai.chat.completions.create({
57
+ model: 'gpt-4o-mini',
58
+ messages: [
59
+ {
60
+ role: 'user',
61
+ content: prompt
62
+ }
63
+ ],
64
+ max_tokens: 100,
65
+ temperature: 0.3
66
+ });
67
+ const message = response.choices[0]?.message?.content?.trim();
68
+ if (!message) {
69
+ throw new Error('No commit message generated');
70
+ }
71
+ return message;
72
+ }
73
+ catch (error) {
74
+ console.error('Failed to generate commit message:', error);
75
+ return 'chore: update code via Ivan';
76
+ }
77
+ }
78
+ async generatePullRequestDescription(taskDescription, diff, changedFiles) {
79
+ await this.ensureInitialized();
80
+ // Truncate diff if it's too large (keep under 30K characters for the prompt)
81
+ // This leaves room for the response and other parts of the prompt
82
+ const MAX_DIFF_LENGTH = 30000;
83
+ let truncatedDiff = diff;
84
+ let diffWasTruncated = false;
85
+ if (diff.length > MAX_DIFF_LENGTH) {
86
+ // Take first and last portions of the diff to show context
87
+ const firstPart = diff.substring(0, MAX_DIFF_LENGTH / 2);
88
+ const lastPart = diff.substring(diff.length - MAX_DIFF_LENGTH / 2);
89
+ truncatedDiff = `${firstPart}\n\n... (diff truncated - ${diff.length} total characters) ...\n\n${lastPart}`;
90
+ diffWasTruncated = true;
91
+ }
92
+ const prompt = `
93
+ Generate a pull request title and description for the following task and changes.
94
+
95
+ Task: ${taskDescription}
96
+
97
+ Changed files:
98
+ ${changedFiles.map(file => `- ${file}`).join('\n')}
99
+
100
+ Git diff${diffWasTruncated ? ' (truncated for brevity)' : ''}:
101
+ \`\`\`
102
+ ${truncatedDiff}
103
+ \`\`\`
104
+
105
+ Generate:
106
+ 1. A concise PR title (MUST be under 250 characters to fit GitHub's 256 character limit)
107
+ 2. A concise PR description (MUST be under 10000 characters) with:
108
+ - Brief summary of changes (2-3 sentences)
109
+ - List of main changes (bullet points)
110
+ - Any important notes
111
+
112
+ Keep the description focused and concise. Do NOT include the full diff in the description.`;
113
+ try {
114
+ if (!this.openai) {
115
+ throw new Error('OpenAI client not initialized');
116
+ }
117
+ const response = await this.openai.chat.completions.create({
118
+ model: 'gpt-4o-mini',
119
+ messages: [
120
+ {
121
+ role: 'user',
122
+ content: prompt
123
+ }
124
+ ],
125
+ response_format: {
126
+ type: 'json_schema',
127
+ json_schema: {
128
+ name: 'pull_request',
129
+ strict: true,
130
+ schema: {
131
+ type: 'object',
132
+ properties: {
133
+ title: {
134
+ type: 'string',
135
+ description: 'The PR title (must be under 250 characters)'
136
+ },
137
+ body: {
138
+ type: 'string',
139
+ description: 'The PR description'
140
+ }
141
+ },
142
+ required: ['title', 'body'],
143
+ additionalProperties: false
144
+ }
145
+ }
146
+ },
147
+ max_tokens: 1000,
148
+ temperature: 0.3
149
+ });
150
+ const content = response.choices[0]?.message?.content?.trim();
151
+ if (!content) {
152
+ throw new Error('No PR description generated');
153
+ }
154
+ const parsed = JSON.parse(content);
155
+ // Ensure title doesn't exceed GitHub's 256 character limit
156
+ let title = `Ivan: ${parsed.title || taskDescription}`;
157
+ if (title.length > 256) {
158
+ title = title.substring(0, 253) + '...';
159
+ }
160
+ // Ensure the PR body doesn't exceed GitHub's limit (65536 characters)
161
+ // Leave some room for the attribution footer that GitManager will add
162
+ const MAX_BODY_LENGTH = 65000;
163
+ let body = parsed.body || `Implemented: ${taskDescription}\n\n🤖 Generated with Ivan`;
164
+ if (body.length > MAX_BODY_LENGTH) {
165
+ body = body.substring(0, MAX_BODY_LENGTH) + '\n\n... (description truncated)';
166
+ }
167
+ return {
168
+ title,
169
+ body
170
+ };
171
+ }
172
+ catch (error) {
173
+ console.error('Failed to generate PR description:', error);
174
+ // Ensure fallback title doesn't exceed GitHub's 256 character limit
175
+ let fallbackTitle = `Ivan: ${taskDescription}`;
176
+ if (fallbackTitle.length > 256) {
177
+ fallbackTitle = fallbackTitle.substring(0, 253) + '...';
178
+ }
179
+ return {
180
+ title: fallbackTitle,
181
+ body: `Implemented: ${taskDescription}\n\nChanged files:\n${changedFiles.map(file => `- ${file}`).join('\n')}\n\n🤖 Generated with Ivan`
182
+ };
183
+ }
184
+ }
185
+ }
186
+ //# sourceMappingURL=openai-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-service.js","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface PullRequest {
2
+ number: number;
3
+ title: string;
4
+ branch: string;
5
+ url: string;
6
+ hasUnaddressedComments: boolean;
7
+ hasFailingChecks: boolean;
8
+ unaddressedComments: PRComment[];
9
+ failingChecks: string[];
10
+ hasTestOrLintFailures: boolean;
11
+ testOrLintFailures: string[];
12
+ }
13
+ export interface PRComment {
14
+ id: string;
15
+ author: string;
16
+ body: string;
17
+ createdAt: string;
18
+ path?: string;
19
+ line?: number;
20
+ }
21
+ export declare class PRService {
22
+ private workingDir;
23
+ constructor(workingDir: string);
24
+ getSpecificPRWithIssues(prNumber: number): Promise<PullRequest[]>;
25
+ getOpenPRsWithIssues(fromUser?: string): Promise<PullRequest[]>;
26
+ getUnaddressedComments(prNumber: number): Promise<PRComment[]>;
27
+ private getFailingChecks;
28
+ checkoutPRBranch(prNumber: number): Promise<void>;
29
+ getFailingActionLogs(prNumber: number): Promise<string>;
30
+ }
31
+ //# sourceMappingURL=pr-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-service.d.ts","sourceRoot":"","sources":["../../src/services/pr-service.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,sBAAsB,EAAE,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,SAAS,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgEjE,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA6D/D,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YA0FtD,gBAAgB;IAkDxB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAwD9D"}
@@ -0,0 +1,291 @@
1
+ import { execSync } from 'child_process';
2
+ import chalk from 'chalk';
3
+ export class PRService {
4
+ workingDir;
5
+ constructor(workingDir) {
6
+ this.workingDir = workingDir;
7
+ }
8
+ async getSpecificPRWithIssues(prNumber) {
9
+ try {
10
+ // Get specific PR
11
+ const prJson = execSync(`gh pr view ${prNumber} --json number,title,headRefName,url,state`, {
12
+ cwd: this.workingDir,
13
+ encoding: 'utf-8'
14
+ });
15
+ const pr = JSON.parse(prJson);
16
+ // Check if PR is open
17
+ if (pr.state !== 'OPEN') {
18
+ console.log(chalk.yellow(`⚠️ PR #${prNumber} is not open (status: ${pr.state})`));
19
+ return [];
20
+ }
21
+ const pullRequest = {
22
+ number: pr.number,
23
+ title: pr.title,
24
+ branch: pr.headRefName,
25
+ url: pr.url,
26
+ hasUnaddressedComments: false,
27
+ hasFailingChecks: false,
28
+ unaddressedComments: [],
29
+ failingChecks: [],
30
+ hasTestOrLintFailures: false,
31
+ testOrLintFailures: []
32
+ };
33
+ // Check for unaddressed comments
34
+ const comments = await this.getUnaddressedComments(pr.number);
35
+ if (comments.length > 0) {
36
+ pullRequest.hasUnaddressedComments = true;
37
+ pullRequest.unaddressedComments = comments;
38
+ }
39
+ // Check for failing checks
40
+ const { allFailures, testOrLintFailures } = await this.getFailingChecks(pr.number);
41
+ if (allFailures.length > 0) {
42
+ pullRequest.hasFailingChecks = true;
43
+ pullRequest.failingChecks = allFailures;
44
+ }
45
+ if (testOrLintFailures.length > 0) {
46
+ pullRequest.hasTestOrLintFailures = true;
47
+ pullRequest.testOrLintFailures = testOrLintFailures;
48
+ }
49
+ // Only include PR if it has issues
50
+ if (pullRequest.hasUnaddressedComments || pullRequest.hasFailingChecks) {
51
+ return [pullRequest];
52
+ }
53
+ return [];
54
+ }
55
+ catch (error) {
56
+ const errorMessage = error instanceof Error ? error.message : String(error);
57
+ if (errorMessage.includes('no pull requests found')) {
58
+ console.error(chalk.red(`❌ PR #${prNumber} not found`));
59
+ }
60
+ else {
61
+ console.error(chalk.red(`Error fetching PR #${prNumber}:`), error);
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ async getOpenPRsWithIssues(fromUser) {
67
+ try {
68
+ // Get all open PRs, optionally filtered by author
69
+ let command = 'gh pr list --state open --json number,title,headRefName,url,author';
70
+ if (fromUser) {
71
+ command += ` --author ${fromUser}`;
72
+ }
73
+ const prsJson = execSync(command, {
74
+ cwd: this.workingDir,
75
+ encoding: 'utf-8'
76
+ });
77
+ const prs = JSON.parse(prsJson);
78
+ const pullRequests = [];
79
+ for (const pr of prs) {
80
+ const pullRequest = {
81
+ number: pr.number,
82
+ title: pr.title,
83
+ branch: pr.headRefName,
84
+ url: pr.url,
85
+ hasUnaddressedComments: false,
86
+ hasFailingChecks: false,
87
+ unaddressedComments: [],
88
+ failingChecks: [],
89
+ hasTestOrLintFailures: false,
90
+ testOrLintFailures: []
91
+ };
92
+ // Check for unaddressed comments
93
+ const comments = await this.getUnaddressedComments(pr.number);
94
+ if (comments.length > 0) {
95
+ pullRequest.hasUnaddressedComments = true;
96
+ pullRequest.unaddressedComments = comments;
97
+ }
98
+ // Check for failing checks
99
+ const { allFailures, testOrLintFailures } = await this.getFailingChecks(pr.number);
100
+ if (allFailures.length > 0) {
101
+ pullRequest.hasFailingChecks = true;
102
+ pullRequest.failingChecks = allFailures;
103
+ }
104
+ if (testOrLintFailures.length > 0) {
105
+ pullRequest.hasTestOrLintFailures = true;
106
+ pullRequest.testOrLintFailures = testOrLintFailures;
107
+ }
108
+ // Only include PRs that have issues
109
+ if (pullRequest.hasUnaddressedComments || pullRequest.hasFailingChecks) {
110
+ pullRequests.push(pullRequest);
111
+ }
112
+ }
113
+ return pullRequests;
114
+ }
115
+ catch (error) {
116
+ console.error(chalk.red('Error fetching PRs:'), error);
117
+ throw error;
118
+ }
119
+ }
120
+ async getUnaddressedComments(prNumber) {
121
+ try {
122
+ // Get PR owner and repo name
123
+ const repoInfo = execSync('gh repo view --json owner,name', {
124
+ cwd: this.workingDir,
125
+ encoding: 'utf-8'
126
+ });
127
+ const { owner, name: repoName } = JSON.parse(repoInfo);
128
+ // Use GraphQL to get review threads with resolved status
129
+ const graphqlQuery = `
130
+ query {
131
+ repository(owner: "${owner.login}", name: "${repoName}") {
132
+ pullRequest(number: ${prNumber}) {
133
+ reviewThreads(first: 100) {
134
+ nodes {
135
+ isResolved
136
+ comments(first: 100) {
137
+ nodes {
138
+ id
139
+ databaseId
140
+ body
141
+ author {
142
+ login
143
+ }
144
+ createdAt
145
+ path
146
+ line
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ `;
155
+ const graphqlResult = execSync(`gh api graphql -f query='${graphqlQuery}'`, {
156
+ cwd: this.workingDir,
157
+ encoding: 'utf-8'
158
+ });
159
+ const result = JSON.parse(graphqlResult);
160
+ const threads = result.data?.repository?.pullRequest?.reviewThreads?.nodes || [];
161
+ const unaddressedComments = [];
162
+ // Process each thread
163
+ for (const thread of threads) {
164
+ // Skip resolved threads
165
+ if (thread.isResolved) {
166
+ continue;
167
+ }
168
+ const comments = thread.comments?.nodes || [];
169
+ if (comments.length === 0) {
170
+ continue;
171
+ }
172
+ // Get the first comment (the main review comment)
173
+ const firstComment = comments[0];
174
+ // Check if there are replies (more than one comment in thread)
175
+ const hasReplies = comments.length > 1;
176
+ if (!hasReplies && firstComment.path) {
177
+ // Only include if it's an inline code comment (has a path) and has no replies
178
+ unaddressedComments.push({
179
+ id: firstComment.databaseId ? firstComment.databaseId.toString() : firstComment.id,
180
+ author: firstComment.author.login,
181
+ body: firstComment.body,
182
+ createdAt: firstComment.createdAt,
183
+ path: firstComment.path,
184
+ line: firstComment.line
185
+ });
186
+ }
187
+ }
188
+ return unaddressedComments;
189
+ }
190
+ catch {
191
+ // If there's an error fetching comments, return empty array
192
+ return [];
193
+ }
194
+ }
195
+ async getFailingChecks(prNumber) {
196
+ try {
197
+ const checksJson = execSync(`gh pr checks ${prNumber} --json name,state`, {
198
+ cwd: this.workingDir,
199
+ encoding: 'utf-8'
200
+ });
201
+ const checks = JSON.parse(checksJson);
202
+ const failingChecks = [];
203
+ const testOrLintFailures = [];
204
+ for (const check of checks) {
205
+ if (check.state === 'FAILURE' || check.state === 'ERROR') {
206
+ failingChecks.push(check.name);
207
+ // Check if this is a test or lint failure
208
+ const checkNameLower = check.name.toLowerCase();
209
+ if (checkNameLower.includes('test') ||
210
+ checkNameLower.includes('lint') ||
211
+ checkNameLower.includes('eslint') ||
212
+ checkNameLower.includes('prettier') ||
213
+ checkNameLower.includes('jest') ||
214
+ checkNameLower.includes('mocha') ||
215
+ checkNameLower.includes('pytest') ||
216
+ checkNameLower.includes('ruff') ||
217
+ checkNameLower.includes('black') ||
218
+ checkNameLower.includes('flake8') ||
219
+ checkNameLower.includes('mypy') ||
220
+ checkNameLower.includes('typecheck') ||
221
+ checkNameLower.includes('type-check') ||
222
+ checkNameLower.includes('tsc') ||
223
+ checkNameLower.includes('clippy') ||
224
+ checkNameLower.includes('rustfmt')) {
225
+ testOrLintFailures.push(check.name);
226
+ }
227
+ }
228
+ }
229
+ return { allFailures: failingChecks, testOrLintFailures };
230
+ }
231
+ catch {
232
+ // If there's an error fetching checks, return empty arrays
233
+ return { allFailures: [], testOrLintFailures: [] };
234
+ }
235
+ }
236
+ async checkoutPRBranch(prNumber) {
237
+ execSync(`gh pr checkout ${prNumber}`, {
238
+ cwd: this.workingDir,
239
+ stdio: 'inherit'
240
+ });
241
+ }
242
+ async getFailingActionLogs(prNumber) {
243
+ try {
244
+ // Get the checks with their workflow information
245
+ const checksJson = execSync(`gh pr checks ${prNumber} --json name,state,link,workflow`, {
246
+ cwd: this.workingDir,
247
+ encoding: 'utf-8'
248
+ });
249
+ const checks = JSON.parse(checksJson);
250
+ let failingLogs = '';
251
+ for (const check of checks) {
252
+ if (check.state === 'FAILURE' || check.state === 'ERROR') {
253
+ // Extract run ID from the link (format: https://github.com/owner/repo/actions/runs/123456789/job/987654321)
254
+ const runIdMatch = check.link?.match(/\/runs\/(\d+)/);
255
+ if (runIdMatch) {
256
+ const runId = runIdMatch[1];
257
+ try {
258
+ // Get the failed logs for this run
259
+ const logs = execSync(`gh run view ${runId} --log-failed`, {
260
+ cwd: this.workingDir,
261
+ encoding: 'utf-8',
262
+ maxBuffer: 10 * 1024 * 1024 // 10MB buffer for large logs
263
+ });
264
+ if (logs) {
265
+ failingLogs += `\n\n=== Failed logs for ${check.name} ===\n`;
266
+ // Truncate logs if too large (keep last 5000 chars per check)
267
+ if (logs.length > 5000) {
268
+ failingLogs += '... (truncated) ...\n';
269
+ failingLogs += logs.substring(logs.length - 5000);
270
+ }
271
+ else {
272
+ failingLogs += logs;
273
+ }
274
+ }
275
+ }
276
+ catch (error) {
277
+ // If we can't get logs for this specific run, continue with others
278
+ console.error(`Failed to get logs for run ${runId}:`, error);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ return failingLogs;
284
+ }
285
+ catch (error) {
286
+ console.error('Error fetching action logs:', error);
287
+ return '';
288
+ }
289
+ }
290
+ }
291
+ //# sourceMappingURL=pr-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-service.js","sourceRoot":"","sources":["../../src/services/pr-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare class RepositoryManager {
2
+ getValidWorkingDirectory(): Promise<string>;
3
+ private isGitRepository;
4
+ private isIvanRepository;
5
+ private isValidTargetRepository;
6
+ private promptForRepositoryPath;
7
+ getRepositoryInfo(workingDir: string): {
8
+ name: string;
9
+ branch: string;
10
+ };
11
+ }
12
+ //# sourceMappingURL=repository-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-manager.d.ts","sourceRoot":"","sources":["../../src/services/repository-manager.ts"],"names":[],"mappings":"AAMA,qBAAa,iBAAiB;IACtB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC;IAkBjD,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,uBAAuB;YAIjB,uBAAuB;IA8CrC,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;CAaxE"}
@@ -0,0 +1,101 @@
1
+ import { execSync } from 'child_process';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ export class RepositoryManager {
7
+ async getValidWorkingDirectory() {
8
+ const currentDir = process.cwd();
9
+ if (this.isValidTargetRepository(currentDir)) {
10
+ return currentDir;
11
+ }
12
+ console.log(chalk.yellow('⚠️ Current directory is not a valid target repository'));
13
+ if (!this.isGitRepository(currentDir)) {
14
+ console.log(chalk.red('❌ Not a git repository'));
15
+ }
16
+ else if (this.isIvanRepository(currentDir)) {
17
+ console.log(chalk.red('❌ Cannot run Ivan on itself'));
18
+ }
19
+ return await this.promptForRepositoryPath();
20
+ }
21
+ isGitRepository(dir) {
22
+ try {
23
+ execSync('git rev-parse --git-dir', {
24
+ cwd: dir,
25
+ stdio: 'ignore'
26
+ });
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ isIvanRepository(dir) {
34
+ try {
35
+ const packageJsonPath = path.join(dir, 'package.json');
36
+ if (!fs.existsSync(packageJsonPath)) {
37
+ return false;
38
+ }
39
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
40
+ return packageJson.name === '@ariso-ai/ivan' || packageJson.name === 'ivan';
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ isValidTargetRepository(dir) {
47
+ return this.isGitRepository(dir) && !this.isIvanRepository(dir);
48
+ }
49
+ async promptForRepositoryPath() {
50
+ console.log('');
51
+ console.log(chalk.blue('📁 Please specify the repository where Ivan should work'));
52
+ while (true) {
53
+ const { repositoryPath } = await inquirer.prompt([
54
+ {
55
+ type: 'input',
56
+ name: 'repositoryPath',
57
+ message: 'Enter the path to your target repository:',
58
+ validate: (input) => {
59
+ if (!input || input.trim().length === 0) {
60
+ return 'Please enter a valid path';
61
+ }
62
+ return true;
63
+ }
64
+ }
65
+ ]);
66
+ const fullPath = path.resolve(repositoryPath.trim());
67
+ if (!fs.existsSync(fullPath)) {
68
+ console.log(chalk.red('❌ Path does not exist'));
69
+ continue;
70
+ }
71
+ if (!fs.statSync(fullPath).isDirectory()) {
72
+ console.log(chalk.red('❌ Path is not a directory'));
73
+ continue;
74
+ }
75
+ if (!this.isGitRepository(fullPath)) {
76
+ console.log(chalk.red('❌ Path is not a git repository'));
77
+ continue;
78
+ }
79
+ if (this.isIvanRepository(fullPath)) {
80
+ console.log(chalk.red('❌ Cannot run Ivan on itself'));
81
+ continue;
82
+ }
83
+ console.log(chalk.green(`✅ Using repository: ${fullPath}`));
84
+ return fullPath;
85
+ }
86
+ }
87
+ getRepositoryInfo(workingDir) {
88
+ try {
89
+ const repoName = path.basename(workingDir);
90
+ const branch = execSync('git branch --show-current', {
91
+ cwd: workingDir,
92
+ encoding: 'utf8'
93
+ }).trim();
94
+ return { name: repoName, branch };
95
+ }
96
+ catch {
97
+ return { name: path.basename(workingDir), branch: 'unknown' };
98
+ }
99
+ }
100
+ }
101
+ //# sourceMappingURL=repository-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-manager.js","sourceRoot":"","sources":["../../src/services/repository-manager.ts"],"names":[],"mappings":"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"}