@gracefultools/astrid-sdk 0.7.16 → 0.8.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 +127 -341
- package/dist/channel/channel.d.ts +33 -0
- package/dist/channel/channel.d.ts.map +1 -0
- package/dist/channel/channel.js +90 -0
- package/dist/channel/channel.js.map +1 -0
- package/dist/channel/index.d.ts +13 -0
- package/dist/channel/index.d.ts.map +1 -0
- package/dist/channel/index.js +23 -0
- package/dist/channel/index.js.map +1 -0
- package/dist/channel/message-formatter.d.ts +14 -0
- package/dist/channel/message-formatter.d.ts.map +1 -0
- package/dist/channel/message-formatter.js +71 -0
- package/dist/channel/message-formatter.js.map +1 -0
- package/dist/channel/oauth-client.d.ts +15 -0
- package/dist/channel/oauth-client.d.ts.map +1 -0
- package/dist/channel/oauth-client.js +45 -0
- package/dist/channel/oauth-client.js.map +1 -0
- package/dist/channel/rest-client.d.ts +16 -0
- package/dist/channel/rest-client.d.ts.map +1 -0
- package/dist/channel/rest-client.js +66 -0
- package/dist/channel/rest-client.js.map +1 -0
- package/dist/channel/session-mapper.d.ts +14 -0
- package/dist/channel/session-mapper.d.ts.map +1 -0
- package/dist/channel/session-mapper.js +37 -0
- package/dist/channel/session-mapper.js.map +1 -0
- package/dist/channel/sse-client.d.ts +31 -0
- package/dist/channel/sse-client.d.ts.map +1 -0
- package/dist/channel/sse-client.js +171 -0
- package/dist/channel/sse-client.js.map +1 -0
- package/dist/channel/types.d.ts +65 -0
- package/dist/channel/types.d.ts.map +1 -0
- package/dist/channel/types.js +3 -0
- package/dist/channel/types.js.map +1 -0
- package/dist/config/agent-workflow.js +7 -7
- package/dist/index.d.ts +1 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -30
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/agent-config.d.ts.map +1 -1
- package/dist/utils/agent-config.js +14 -0
- package/dist/utils/agent-config.js.map +1 -1
- package/openclaw.plugin.json +25 -0
- package/package.json +66 -77
- package/templates/.astrid.config.json +60 -60
- package/templates/ASTRID.template.md +74 -74
- package/dist/bin/cli.d.ts +0 -14
- package/dist/bin/cli.d.ts.map +0 -1
- package/dist/bin/cli.js +0 -1610
- package/dist/bin/cli.js.map +0 -1
- package/dist/executors/claude.d.ts +0 -65
- package/dist/executors/claude.d.ts.map +0 -1
- package/dist/executors/claude.js +0 -838
- package/dist/executors/claude.js.map +0 -1
- package/dist/executors/gemini.d.ts +0 -23
- package/dist/executors/gemini.d.ts.map +0 -1
- package/dist/executors/gemini.js +0 -558
- package/dist/executors/gemini.js.map +0 -1
- package/dist/executors/openai.d.ts +0 -17
- package/dist/executors/openai.d.ts.map +0 -1
- package/dist/executors/openai.js +0 -614
- package/dist/executors/openai.js.map +0 -1
- package/dist/executors/shared/index.d.ts +0 -9
- package/dist/executors/shared/index.d.ts.map +0 -1
- package/dist/executors/shared/index.js +0 -21
- package/dist/executors/shared/index.js.map +0 -1
- package/dist/executors/shared/tool-executor.d.ts +0 -52
- package/dist/executors/shared/tool-executor.d.ts.map +0 -1
- package/dist/executors/shared/tool-executor.js +0 -262
- package/dist/executors/shared/tool-executor.js.map +0 -1
- package/dist/executors/shared/tool-schemas.d.ts +0 -61
- package/dist/executors/shared/tool-schemas.d.ts.map +0 -1
- package/dist/executors/shared/tool-schemas.js +0 -135
- package/dist/executors/shared/tool-schemas.js.map +0 -1
- package/dist/executors/terminal-base.d.ts +0 -207
- package/dist/executors/terminal-base.d.ts.map +0 -1
- package/dist/executors/terminal-base.js +0 -552
- package/dist/executors/terminal-base.js.map +0 -1
- package/dist/executors/terminal-claude.d.ts +0 -116
- package/dist/executors/terminal-claude.d.ts.map +0 -1
- package/dist/executors/terminal-claude.js +0 -700
- package/dist/executors/terminal-claude.js.map +0 -1
- package/dist/executors/terminal-executors.test.d.ts +0 -8
- package/dist/executors/terminal-executors.test.d.ts.map +0 -1
- package/dist/executors/terminal-executors.test.js +0 -469
- package/dist/executors/terminal-executors.test.js.map +0 -1
- package/dist/executors/terminal-gemini.d.ts +0 -50
- package/dist/executors/terminal-gemini.d.ts.map +0 -1
- package/dist/executors/terminal-gemini.js +0 -401
- package/dist/executors/terminal-gemini.js.map +0 -1
- package/dist/executors/terminal-openai.d.ts +0 -50
- package/dist/executors/terminal-openai.d.ts.map +0 -1
- package/dist/executors/terminal-openai.js +0 -405
- package/dist/executors/terminal-openai.js.map +0 -1
- package/dist/server/astrid-client.d.ts +0 -77
- package/dist/server/astrid-client.d.ts.map +0 -1
- package/dist/server/astrid-client.js +0 -125
- package/dist/server/astrid-client.js.map +0 -1
- package/dist/server/index.d.ts +0 -38
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -408
- package/dist/server/index.js.map +0 -1
- package/dist/server/repo-manager.d.ts +0 -41
- package/dist/server/repo-manager.d.ts.map +0 -1
- package/dist/server/repo-manager.js +0 -177
- package/dist/server/repo-manager.js.map +0 -1
- package/dist/server/session-manager.d.ts +0 -93
- package/dist/server/session-manager.d.ts.map +0 -1
- package/dist/server/session-manager.js +0 -217
- package/dist/server/session-manager.js.map +0 -1
- package/dist/server/webhook-signature.d.ts +0 -23
- package/dist/server/webhook-signature.d.ts.map +0 -1
- package/dist/server/webhook-signature.js +0 -74
- package/dist/server/webhook-signature.js.map +0 -1
package/dist/executors/claude.js
DELETED
|
@@ -1,838 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Claude Agent SDK Executor
|
|
4
|
-
*
|
|
5
|
-
* Executes code implementation using Claude Code's native tools (Read, Write, Edit, Bash)
|
|
6
|
-
* instead of generating code via API and parsing JSON responses.
|
|
7
|
-
*
|
|
8
|
-
* This provides:
|
|
9
|
-
* - Better code quality (real file editing vs generated text)
|
|
10
|
-
* - Native error handling and recovery
|
|
11
|
-
* - Actual test execution
|
|
12
|
-
* - Real git operations
|
|
13
|
-
*/
|
|
14
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
-
}
|
|
20
|
-
Object.defineProperty(o, k2, desc);
|
|
21
|
-
}) : (function(o, m, k, k2) {
|
|
22
|
-
if (k2 === undefined) k2 = k;
|
|
23
|
-
o[k2] = m[k];
|
|
24
|
-
}));
|
|
25
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
-
}) : function(o, v) {
|
|
28
|
-
o["default"] = v;
|
|
29
|
-
});
|
|
30
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
-
var ownKeys = function(o) {
|
|
32
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
-
var ar = [];
|
|
34
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
-
return ar;
|
|
36
|
-
};
|
|
37
|
-
return ownKeys(o);
|
|
38
|
-
};
|
|
39
|
-
return function (mod) {
|
|
40
|
-
if (mod && mod.__esModule) return mod;
|
|
41
|
-
var result = {};
|
|
42
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
-
__setModuleDefault(result, mod);
|
|
44
|
-
return result;
|
|
45
|
-
};
|
|
46
|
-
})();
|
|
47
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.verifyChanges = verifyChanges;
|
|
49
|
-
exports.planWithClaude = planWithClaude;
|
|
50
|
-
exports.executeWithClaude = executeWithClaude;
|
|
51
|
-
exports.getGitHubUser = getGitHubUser;
|
|
52
|
-
exports.prepareRepository = prepareRepository;
|
|
53
|
-
const claude_agent_sdk_1 = require("@anthropic-ai/claude-agent-sdk");
|
|
54
|
-
const fs = __importStar(require("fs/promises"));
|
|
55
|
-
const path = __importStar(require("path"));
|
|
56
|
-
const index_js_1 = require("../config/index.js");
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// REPOSITORY CONTEXT LOADING
|
|
59
|
-
// ============================================================================
|
|
60
|
-
/**
|
|
61
|
-
* Load ASTRID.md from the repository if it exists
|
|
62
|
-
* Also loads README.md as fallback context
|
|
63
|
-
*/
|
|
64
|
-
async function loadAstridMd(repoPath, maxLength = 16000) {
|
|
65
|
-
const astridPath = path.join(repoPath, 'ASTRID.md');
|
|
66
|
-
try {
|
|
67
|
-
const content = await fs.readFile(astridPath, 'utf-8');
|
|
68
|
-
return content.length > maxLength ? content.substring(0, maxLength) + '\n\n[ASTRID.md truncated...]' : content;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// Try README.md as fallback
|
|
72
|
-
try {
|
|
73
|
-
const readmePath = path.join(repoPath, 'README.md');
|
|
74
|
-
const content = await fs.readFile(readmePath, 'utf-8');
|
|
75
|
-
const truncated = content.length > maxLength / 2
|
|
76
|
-
? content.substring(0, maxLength / 2) + '\n\n[README.md truncated...]'
|
|
77
|
-
: content;
|
|
78
|
-
return `## Project Context (from README.md)\n\n${truncated}`;
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Detect the test command for the repository
|
|
87
|
-
*/
|
|
88
|
-
async function detectTestCommand(repoPath) {
|
|
89
|
-
const packageJsonPath = path.join(repoPath, 'package.json');
|
|
90
|
-
try {
|
|
91
|
-
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
92
|
-
const pkg = JSON.parse(content);
|
|
93
|
-
// Prefer quick checks over full test suites for faster feedback
|
|
94
|
-
if (pkg.scripts?.['predeploy:quick']) {
|
|
95
|
-
return 'npm run predeploy:quick';
|
|
96
|
-
}
|
|
97
|
-
if (pkg.scripts?.typecheck) {
|
|
98
|
-
return 'npm run typecheck';
|
|
99
|
-
}
|
|
100
|
-
if (pkg.scripts?.['type-check']) {
|
|
101
|
-
return 'npm run type-check';
|
|
102
|
-
}
|
|
103
|
-
if (pkg.scripts?.build) {
|
|
104
|
-
return 'npm run build';
|
|
105
|
-
}
|
|
106
|
-
if (pkg.scripts?.predeploy) {
|
|
107
|
-
return 'npm run predeploy';
|
|
108
|
-
}
|
|
109
|
-
if (pkg.scripts?.test) {
|
|
110
|
-
return 'npm test';
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
// No package.json or invalid JSON
|
|
115
|
-
}
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Verify changes work by running tests/build
|
|
120
|
-
* Returns { success, output, error }
|
|
121
|
-
*/
|
|
122
|
-
async function verifyChanges(repoPath, logger) {
|
|
123
|
-
const { execSync } = await import('child_process');
|
|
124
|
-
const log = logger || (() => { });
|
|
125
|
-
const testCommand = await detectTestCommand(repoPath);
|
|
126
|
-
if (!testCommand) {
|
|
127
|
-
log('info', 'No verification command found, skipping verification');
|
|
128
|
-
return { success: true, output: 'No verification command available' };
|
|
129
|
-
}
|
|
130
|
-
log('info', `Running verification: ${testCommand}`);
|
|
131
|
-
try {
|
|
132
|
-
const output = execSync(testCommand, {
|
|
133
|
-
cwd: repoPath,
|
|
134
|
-
encoding: 'utf-8',
|
|
135
|
-
timeout: 300000, // 5 minutes for verification
|
|
136
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
137
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
138
|
-
});
|
|
139
|
-
log('info', 'Verification passed');
|
|
140
|
-
return { success: true, output: output.slice(-2000) }; // Last 2000 chars
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
const err = error;
|
|
144
|
-
const combinedOutput = `${err.stdout || ''}\n${err.stderr || ''}`.slice(-3000);
|
|
145
|
-
log('error', 'Verification failed', { error: err.message });
|
|
146
|
-
return {
|
|
147
|
-
success: false,
|
|
148
|
-
output: combinedOutput,
|
|
149
|
-
error: err.message || 'Verification command failed'
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Detect iOS project and return build info
|
|
155
|
-
*/
|
|
156
|
-
async function detectiOSProject(repoPath) {
|
|
157
|
-
const possiblePaths = ['ios-app', 'ios', 'iOS', '.'];
|
|
158
|
-
for (const subdir of possiblePaths) {
|
|
159
|
-
const searchPath = path.join(repoPath, subdir);
|
|
160
|
-
try {
|
|
161
|
-
const entries = await fs.readdir(searchPath);
|
|
162
|
-
const xcodeproj = entries.find(e => e.endsWith('.xcodeproj'));
|
|
163
|
-
if (xcodeproj) {
|
|
164
|
-
const schemeName = xcodeproj.replace('.xcodeproj', '');
|
|
165
|
-
const projectPath = subdir === '.' ? xcodeproj : `${subdir}/${xcodeproj}`;
|
|
166
|
-
return {
|
|
167
|
-
hasIOSProject: true,
|
|
168
|
-
projectPath,
|
|
169
|
-
schemeName,
|
|
170
|
-
buildCommand: `cd ${subdir === '.' ? '.' : subdir} && xcodebuild -scheme "${schemeName}" -destination "platform=iOS Simulator,name=iPhone 16" -configuration Debug build 2>&1 | tail -50`
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
// Directory doesn't exist
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
// ============================================================================
|
|
181
|
-
// PLANNING
|
|
182
|
-
// ============================================================================
|
|
183
|
-
/**
|
|
184
|
-
* Generate an implementation plan using Claude Agent SDK
|
|
185
|
-
*/
|
|
186
|
-
async function planWithClaude(taskTitle, taskDescription, config) {
|
|
187
|
-
const log = config.logger || (() => { });
|
|
188
|
-
const onProgress = config.onProgress || (() => { });
|
|
189
|
-
log('info', 'Starting Claude Agent SDK planning', {
|
|
190
|
-
repoPath: config.repoPath,
|
|
191
|
-
taskTitle
|
|
192
|
-
});
|
|
193
|
-
onProgress('Initializing Claude Code for planning...');
|
|
194
|
-
// Load project-specific configuration
|
|
195
|
-
const astridConfig = await (0, index_js_1.loadAstridConfig)(config.repoPath);
|
|
196
|
-
const astridMd = await loadAstridMd(config.repoPath);
|
|
197
|
-
if (astridMd) {
|
|
198
|
-
log('info', 'Loaded ASTRID.md from repository');
|
|
199
|
-
onProgress('Loaded project context from ASTRID.md');
|
|
200
|
-
}
|
|
201
|
-
// Detect platform from config
|
|
202
|
-
const platform = (0, index_js_1.detectPlatform)(astridConfig, taskTitle, taskDescription || '');
|
|
203
|
-
// Generate prompts from config
|
|
204
|
-
const structurePrompt = (0, index_js_1.generateStructurePrompt)(astridConfig);
|
|
205
|
-
const platformHints = (0, index_js_1.generatePlatformHints)(platform);
|
|
206
|
-
const initialGlobPattern = (0, index_js_1.getInitialGlobPattern)(astridConfig, platform);
|
|
207
|
-
if (config.previousContext?.hasBeenProcessedBefore) {
|
|
208
|
-
log('info', 'Task has previous context', {
|
|
209
|
-
attempts: config.previousContext.previousAttempts.length,
|
|
210
|
-
feedbackItems: config.previousContext.userFeedback.length
|
|
211
|
-
});
|
|
212
|
-
onProgress('Considering previous attempts and user feedback...');
|
|
213
|
-
}
|
|
214
|
-
const prompt = buildPlanningPrompt(taskTitle, taskDescription, astridMd, config.previousContext);
|
|
215
|
-
const options = {
|
|
216
|
-
cwd: config.repoPath,
|
|
217
|
-
permissionMode: 'default',
|
|
218
|
-
maxTurns: config.maxTurns || 30,
|
|
219
|
-
maxBudgetUsd: config.maxBudgetUsd || 2.0,
|
|
220
|
-
allowedTools: ['Read', 'Glob', 'Grep', 'Bash'],
|
|
221
|
-
settingSources: [],
|
|
222
|
-
systemPrompt: {
|
|
223
|
-
type: 'preset',
|
|
224
|
-
preset: 'claude_code',
|
|
225
|
-
append: `
|
|
226
|
-
You are an expert software engineer analyzing a codebase to create an implementation plan.
|
|
227
|
-
|
|
228
|
-
${structurePrompt}
|
|
229
|
-
${platformHints}
|
|
230
|
-
${astridConfig.customInstructions ? `\n## Custom Instructions\n${astridConfig.customInstructions}\n` : ''}
|
|
231
|
-
|
|
232
|
-
## EXPLORATION WORKFLOW (MANDATORY)
|
|
233
|
-
|
|
234
|
-
You MUST follow this workflow before creating a plan:
|
|
235
|
-
|
|
236
|
-
### Step 1: Understand Project Structure
|
|
237
|
-
- Use Glob with patterns like "**/*.{ts,tsx,js,jsx}" to find source files
|
|
238
|
-
- Use Glob with patterns like "**/package.json" to find project roots
|
|
239
|
-
- Read key config files: package.json, tsconfig.json, etc.
|
|
240
|
-
|
|
241
|
-
### Step 2: Find Relevant Code
|
|
242
|
-
- Use Grep to search for related terms, functions, or patterns
|
|
243
|
-
- Read files that are likely to need changes
|
|
244
|
-
- Read adjacent files to understand context and patterns
|
|
245
|
-
|
|
246
|
-
### Step 3: Analyze Patterns
|
|
247
|
-
- Note coding conventions (naming, structure, imports)
|
|
248
|
-
- Note testing patterns if tests exist
|
|
249
|
-
- Note type patterns and interfaces
|
|
250
|
-
|
|
251
|
-
### Step 4: Create Precise Plan
|
|
252
|
-
After thorough exploration, create a surgical implementation plan.
|
|
253
|
-
|
|
254
|
-
CRITICAL RULES:
|
|
255
|
-
1. DO NOT modify any files - this is READ-ONLY exploration
|
|
256
|
-
2. DO NOT use Write or Edit tools
|
|
257
|
-
3. MUST read at least 3-5 relevant files before creating a plan
|
|
258
|
-
4. MUST use Grep to find related code patterns
|
|
259
|
-
${platform ? `5. For ${platform.name} tasks, START with pattern: "${initialGlobPattern}"` : ''}
|
|
260
|
-
|
|
261
|
-
After exploring, output ONLY a JSON block with this exact structure:
|
|
262
|
-
\`\`\`json
|
|
263
|
-
{
|
|
264
|
-
"summary": "Brief summary of what needs to be done",
|
|
265
|
-
"approach": "High-level approach with specific technical details",
|
|
266
|
-
"files": [
|
|
267
|
-
{
|
|
268
|
-
"path": "path/to/file.ts",
|
|
269
|
-
"purpose": "Why this file needs changes",
|
|
270
|
-
"changes": "Specific, detailed changes to make"
|
|
271
|
-
}
|
|
272
|
-
],
|
|
273
|
-
"estimatedComplexity": "simple|medium|complex",
|
|
274
|
-
"considerations": ["Edge case 1", "Testing requirement", "Pattern to follow"]
|
|
275
|
-
}
|
|
276
|
-
\`\`\`
|
|
277
|
-
|
|
278
|
-
PLANNING RULES:
|
|
279
|
-
- Maximum 8 files in the plan
|
|
280
|
-
- Be SURGICAL: only list files that MUST change
|
|
281
|
-
- Include SPECIFIC file paths you discovered (no guessing)
|
|
282
|
-
- Follow existing patterns in the codebase
|
|
283
|
-
- Consider edge cases and error handling
|
|
284
|
-
- Note any tests that need updating
|
|
285
|
-
${astridConfig.protectedPaths?.length ? `- DO NOT modify these paths: ${astridConfig.protectedPaths.join(', ')}` : ''}
|
|
286
|
-
`
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
const env = { ...process.env };
|
|
290
|
-
if (config.apiKey) {
|
|
291
|
-
env.ANTHROPIC_API_KEY = config.apiKey;
|
|
292
|
-
}
|
|
293
|
-
options.env = env;
|
|
294
|
-
try {
|
|
295
|
-
const messages = [];
|
|
296
|
-
let result = null;
|
|
297
|
-
const exploredFiles = [];
|
|
298
|
-
onProgress('Claude Code is exploring the codebase...');
|
|
299
|
-
for await (const message of (0, claude_agent_sdk_1.query)({ prompt, options })) {
|
|
300
|
-
messages.push(message);
|
|
301
|
-
if (message.type === 'assistant') {
|
|
302
|
-
const content = message.message.content;
|
|
303
|
-
if (Array.isArray(content)) {
|
|
304
|
-
for (const block of content) {
|
|
305
|
-
if (block.type === 'text' && block.text) {
|
|
306
|
-
onProgress(`Analyzing: ${block.text.substring(0, 100)}...`);
|
|
307
|
-
}
|
|
308
|
-
if (block.type === 'tool_use') {
|
|
309
|
-
onProgress(`Exploring: ${block.name}`);
|
|
310
|
-
log('info', 'Tool use (planning)', { tool: block.name, input: block.input });
|
|
311
|
-
if (block.name === 'Read' && typeof block.input === 'object' && block.input !== null) {
|
|
312
|
-
const input = block.input;
|
|
313
|
-
if (input.file_path) {
|
|
314
|
-
exploredFiles.push({
|
|
315
|
-
path: input.file_path,
|
|
316
|
-
content: '',
|
|
317
|
-
relevance: 'Explored during planning'
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (message.type === 'result') {
|
|
326
|
-
result = message;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (!result) {
|
|
330
|
-
throw new Error('No result received from Claude Agent SDK');
|
|
331
|
-
}
|
|
332
|
-
if (result.subtype !== 'success') {
|
|
333
|
-
const errorResult = result;
|
|
334
|
-
throw new Error(`Planning failed: ${result.subtype} - ${errorResult.errors?.join(', ') || 'Unknown error'}`);
|
|
335
|
-
}
|
|
336
|
-
log('info', 'Claude Agent SDK planning completed', {
|
|
337
|
-
turns: result.num_turns,
|
|
338
|
-
costUsd: result.total_cost_usd,
|
|
339
|
-
filesExplored: exploredFiles.length
|
|
340
|
-
});
|
|
341
|
-
const plan = parsePlanFromResult(result, exploredFiles, log);
|
|
342
|
-
if (!plan) {
|
|
343
|
-
throw new Error('Could not parse implementation plan from Claude output');
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
success: true,
|
|
347
|
-
plan,
|
|
348
|
-
usage: {
|
|
349
|
-
inputTokens: result.usage.input_tokens,
|
|
350
|
-
outputTokens: result.usage.output_tokens,
|
|
351
|
-
costUSD: result.total_cost_usd
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
357
|
-
log('error', 'Claude Agent SDK planning failed', { error: errorMessage });
|
|
358
|
-
return {
|
|
359
|
-
success: false,
|
|
360
|
-
error: errorMessage
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
// ============================================================================
|
|
365
|
-
// EXECUTION
|
|
366
|
-
// ============================================================================
|
|
367
|
-
/**
|
|
368
|
-
* Execute an implementation plan using Claude Agent SDK
|
|
369
|
-
*/
|
|
370
|
-
async function executeWithClaude(plan, taskTitle, taskDescription, config) {
|
|
371
|
-
const log = config.logger || (() => { });
|
|
372
|
-
const onProgress = config.onProgress || (() => { });
|
|
373
|
-
log('info', 'Starting Claude Agent SDK execution', {
|
|
374
|
-
repoPath: config.repoPath,
|
|
375
|
-
filesInPlan: plan.files.length,
|
|
376
|
-
complexity: plan.estimatedComplexity
|
|
377
|
-
});
|
|
378
|
-
onProgress('Initializing Claude Code agent...');
|
|
379
|
-
const astridMd = await loadAstridMd(config.repoPath);
|
|
380
|
-
const testCommand = await detectTestCommand(config.repoPath);
|
|
381
|
-
const iosProject = await detectiOSProject(config.repoPath);
|
|
382
|
-
if (astridMd)
|
|
383
|
-
log('info', 'Loaded ASTRID.md from repository');
|
|
384
|
-
if (testCommand)
|
|
385
|
-
log('info', `Detected test command: ${testCommand}`);
|
|
386
|
-
if (iosProject?.hasIOSProject) {
|
|
387
|
-
log('info', `Detected iOS project: ${iosProject.projectPath}`);
|
|
388
|
-
onProgress(`Found iOS project: ${iosProject.schemeName}`);
|
|
389
|
-
}
|
|
390
|
-
const prompt = buildImplementationPrompt(plan, taskTitle, taskDescription, astridMd, testCommand, iosProject);
|
|
391
|
-
let testingInstructions = '';
|
|
392
|
-
if (testCommand) {
|
|
393
|
-
testingInstructions += `
|
|
394
|
-
WEB/NODE TESTING:
|
|
395
|
-
1. After making code changes, run: ${testCommand}
|
|
396
|
-
2. If tests fail, fix the issues before completing`;
|
|
397
|
-
}
|
|
398
|
-
if (iosProject?.hasIOSProject) {
|
|
399
|
-
testingInstructions += `
|
|
400
|
-
|
|
401
|
-
iOS BUILD REQUIREMENTS (CRITICAL):
|
|
402
|
-
1. If you modified ANY Swift files, you MUST run the iOS build
|
|
403
|
-
2. Build command: ${iosProject.buildCommand}
|
|
404
|
-
3. If the build fails, FIX THE ERRORS before completing`;
|
|
405
|
-
}
|
|
406
|
-
const options = {
|
|
407
|
-
cwd: config.repoPath,
|
|
408
|
-
permissionMode: 'acceptEdits',
|
|
409
|
-
maxTurns: config.maxTurns || 50,
|
|
410
|
-
maxBudgetUsd: config.maxBudgetUsd || 5.0,
|
|
411
|
-
allowedTools: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash'],
|
|
412
|
-
settingSources: [],
|
|
413
|
-
systemPrompt: {
|
|
414
|
-
type: 'preset',
|
|
415
|
-
preset: 'claude_code',
|
|
416
|
-
append: `
|
|
417
|
-
You are an expert software engineer implementing a coding task from an approved plan.
|
|
418
|
-
|
|
419
|
-
## IMPLEMENTATION WORKFLOW (MANDATORY)
|
|
420
|
-
|
|
421
|
-
### Step 1: Verify Understanding
|
|
422
|
-
- Re-read the files in the plan to confirm your approach
|
|
423
|
-
- Check import paths and dependencies
|
|
424
|
-
- Understand the existing code patterns
|
|
425
|
-
|
|
426
|
-
### Step 2: Implement Changes
|
|
427
|
-
- Make changes one file at a time
|
|
428
|
-
- Follow existing code style and patterns
|
|
429
|
-
- Write complete, production-ready code (no TODOs or placeholders)
|
|
430
|
-
- Handle edge cases and errors properly
|
|
431
|
-
|
|
432
|
-
### Step 3: Verify Changes
|
|
433
|
-
- After making changes, run the test/build command
|
|
434
|
-
- If tests fail, FIX the issues before continuing
|
|
435
|
-
- Do not proceed to the next file until current changes pass
|
|
436
|
-
|
|
437
|
-
### Step 4: Final Verification
|
|
438
|
-
- Run the full test suite one more time
|
|
439
|
-
- Confirm all changes are complete
|
|
440
|
-
|
|
441
|
-
CRITICAL RULES:
|
|
442
|
-
1. Follow the implementation plan exactly
|
|
443
|
-
2. Create/modify ONLY the files specified in the plan
|
|
444
|
-
3. Write complete, working code - no placeholders or TODOs
|
|
445
|
-
4. Run tests/builds after making changes - they MUST pass
|
|
446
|
-
5. If verification fails, FIX THE ISSUES before completing
|
|
447
|
-
6. Do NOT commit changes - just make the file edits
|
|
448
|
-
${testingInstructions}
|
|
449
|
-
|
|
450
|
-
After completing all changes AND verification passes, output a JSON block:
|
|
451
|
-
\`\`\`json
|
|
452
|
-
{
|
|
453
|
-
"completed": true,
|
|
454
|
-
"filesModified": ["path/to/file1.ts"],
|
|
455
|
-
"testsRun": true,
|
|
456
|
-
"testsPassed": true,
|
|
457
|
-
"commitMessage": "brief commit message following conventional commits",
|
|
458
|
-
"prTitle": "feat/fix/chore: Short descriptive title",
|
|
459
|
-
"prDescription": "## Summary\\n\\nWhat this PR does and why.\\n\\n## Changes\\n\\n- Change 1\\n- Change 2"
|
|
460
|
-
}
|
|
461
|
-
\`\`\`
|
|
462
|
-
`
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
const env = { ...process.env };
|
|
466
|
-
if (config.apiKey) {
|
|
467
|
-
env.ANTHROPIC_API_KEY = config.apiKey;
|
|
468
|
-
}
|
|
469
|
-
options.env = env;
|
|
470
|
-
try {
|
|
471
|
-
const messages = [];
|
|
472
|
-
let result = null;
|
|
473
|
-
onProgress('Claude Code is analyzing the codebase...');
|
|
474
|
-
for await (const message of (0, claude_agent_sdk_1.query)({ prompt, options })) {
|
|
475
|
-
messages.push(message);
|
|
476
|
-
if (message.type === 'assistant') {
|
|
477
|
-
const content = message.message.content;
|
|
478
|
-
if (Array.isArray(content)) {
|
|
479
|
-
for (const block of content) {
|
|
480
|
-
if (block.type === 'text' && block.text) {
|
|
481
|
-
onProgress(`Working: ${block.text.substring(0, 100)}...`);
|
|
482
|
-
}
|
|
483
|
-
if (block.type === 'tool_use') {
|
|
484
|
-
onProgress(`Using tool: ${block.name}`);
|
|
485
|
-
log('info', 'Tool use', { tool: block.name, input: block.input });
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (message.type === 'result') {
|
|
491
|
-
result = message;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
if (!result) {
|
|
495
|
-
throw new Error('No result received from Claude Agent SDK');
|
|
496
|
-
}
|
|
497
|
-
if (result.subtype !== 'success') {
|
|
498
|
-
const errorResult = result;
|
|
499
|
-
throw new Error(`Execution failed: ${result.subtype} - ${errorResult.errors?.join(', ') || 'Unknown error'}`);
|
|
500
|
-
}
|
|
501
|
-
log('info', 'Claude Agent SDK execution completed', {
|
|
502
|
-
turns: result.num_turns,
|
|
503
|
-
costUsd: result.total_cost_usd,
|
|
504
|
-
inputTokens: result.usage.input_tokens,
|
|
505
|
-
outputTokens: result.usage.output_tokens
|
|
506
|
-
});
|
|
507
|
-
const executionResult = await parseExecutionResult(config.repoPath, result, plan, log);
|
|
508
|
-
return {
|
|
509
|
-
...executionResult,
|
|
510
|
-
usage: {
|
|
511
|
-
inputTokens: result.usage.input_tokens,
|
|
512
|
-
outputTokens: result.usage.output_tokens,
|
|
513
|
-
costUSD: result.total_cost_usd
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
catch (error) {
|
|
518
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
519
|
-
log('error', 'Claude Agent SDK execution failed', { error: errorMessage });
|
|
520
|
-
return {
|
|
521
|
-
success: false,
|
|
522
|
-
files: [],
|
|
523
|
-
commitMessage: '',
|
|
524
|
-
prTitle: '',
|
|
525
|
-
prDescription: '',
|
|
526
|
-
error: errorMessage
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
// ============================================================================
|
|
531
|
-
// REPOSITORY PREPARATION
|
|
532
|
-
// ============================================================================
|
|
533
|
-
/**
|
|
534
|
-
* Fetch the authenticated GitHub user's info from the token
|
|
535
|
-
*/
|
|
536
|
-
async function getGitHubUser(githubToken) {
|
|
537
|
-
try {
|
|
538
|
-
const response = await fetch('https://api.github.com/user', {
|
|
539
|
-
headers: {
|
|
540
|
-
'Authorization': `token ${githubToken}`,
|
|
541
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
if (!response.ok) {
|
|
545
|
-
throw new Error(`GitHub API error: ${response.status}`);
|
|
546
|
-
}
|
|
547
|
-
const data = await response.json();
|
|
548
|
-
return data;
|
|
549
|
-
}
|
|
550
|
-
catch {
|
|
551
|
-
return { login: 'unknown', email: null, name: null };
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Prepare a repository for execution
|
|
556
|
-
*/
|
|
557
|
-
async function prepareRepository(repoOwner, repoName, branch, githubToken, workDir, gitAuthor) {
|
|
558
|
-
const { execSync } = await import('child_process');
|
|
559
|
-
const os = await import('os');
|
|
560
|
-
const tempDir = workDir || await fs.mkdtemp(path.join(os.tmpdir(), 'claude-agent-'));
|
|
561
|
-
const repoPath = path.join(tempDir, repoName);
|
|
562
|
-
// Clone with more history for context (depth 50 instead of 1)
|
|
563
|
-
const cloneUrl = `https://x-access-token:${githubToken}@github.com/${repoOwner}/${repoName}.git`;
|
|
564
|
-
execSync(`git clone --depth 50 --branch ${branch} ${cloneUrl} ${repoPath}`, {
|
|
565
|
-
stdio: 'pipe',
|
|
566
|
-
timeout: 180000
|
|
567
|
-
});
|
|
568
|
-
// Install dependencies if package.json exists
|
|
569
|
-
const packageJsonPath = path.join(repoPath, 'package.json');
|
|
570
|
-
try {
|
|
571
|
-
await fs.access(packageJsonPath);
|
|
572
|
-
console.log('📦 Installing dependencies...');
|
|
573
|
-
execSync('npm install --legacy-peer-deps', {
|
|
574
|
-
cwd: repoPath,
|
|
575
|
-
stdio: 'pipe',
|
|
576
|
-
timeout: 300000 // 5 minutes for npm install
|
|
577
|
-
});
|
|
578
|
-
console.log('✅ Dependencies installed');
|
|
579
|
-
}
|
|
580
|
-
catch {
|
|
581
|
-
// No package.json or npm install failed - continue anyway
|
|
582
|
-
}
|
|
583
|
-
// Use configurable git author for commits
|
|
584
|
-
// Priority: 1) env vars, 2) passed gitAuthor, 3) fetch from GitHub API
|
|
585
|
-
let gitEmail = process.env.VERCEL_GIT_EMAIL || process.env.AI_AGENT_GIT_EMAIL;
|
|
586
|
-
let gitName = process.env.VERCEL_GIT_NAME || process.env.AI_AGENT_GIT_NAME;
|
|
587
|
-
if (!gitEmail && gitAuthor?.email) {
|
|
588
|
-
gitEmail = gitAuthor.email;
|
|
589
|
-
gitName = gitAuthor.name || gitName;
|
|
590
|
-
}
|
|
591
|
-
// If still no email, fetch from GitHub API (for Vercel preview compatibility)
|
|
592
|
-
if (!gitEmail) {
|
|
593
|
-
const user = await getGitHubUser(githubToken);
|
|
594
|
-
if (user.email) {
|
|
595
|
-
gitEmail = user.email;
|
|
596
|
-
gitName = gitName || user.name || user.login;
|
|
597
|
-
}
|
|
598
|
-
else {
|
|
599
|
-
// Use noreply email for Vercel compatibility
|
|
600
|
-
gitEmail = `${user.login}@users.noreply.github.com`;
|
|
601
|
-
gitName = gitName || user.name || user.login;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
gitName = gitName || 'Astrid AI Agent';
|
|
605
|
-
execSync(`git config user.email "${gitEmail}"`, { cwd: repoPath });
|
|
606
|
-
execSync(`git config user.name "${gitName}"`, { cwd: repoPath });
|
|
607
|
-
return {
|
|
608
|
-
repoPath,
|
|
609
|
-
cleanup: async () => {
|
|
610
|
-
try {
|
|
611
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
612
|
-
}
|
|
613
|
-
catch {
|
|
614
|
-
// Ignore cleanup errors
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
// ============================================================================
|
|
620
|
-
// HELPER FUNCTIONS
|
|
621
|
-
// ============================================================================
|
|
622
|
-
function buildPlanningPrompt(taskTitle, taskDescription, astridMd, previousContext) {
|
|
623
|
-
const contextSection = astridMd
|
|
624
|
-
? `## Project Context (from ASTRID.md)\n\n${astridMd}\n\n---\n\n`
|
|
625
|
-
: '';
|
|
626
|
-
let previousAttemptsSection = '';
|
|
627
|
-
if (previousContext?.hasBeenProcessedBefore) {
|
|
628
|
-
previousAttemptsSection = `## Previous Attempts & User Feedback\n\n`;
|
|
629
|
-
if (previousContext.previousAttempts.length > 0) {
|
|
630
|
-
previousAttemptsSection += `### Previous Attempts\n`;
|
|
631
|
-
previousContext.previousAttempts.forEach((attempt, i) => {
|
|
632
|
-
previousAttemptsSection += `\n**Attempt ${i + 1}:**\n`;
|
|
633
|
-
if (attempt.planSummary)
|
|
634
|
-
previousAttemptsSection += `- Previous plan: ${attempt.planSummary}\n`;
|
|
635
|
-
if (attempt.filesModified?.length)
|
|
636
|
-
previousAttemptsSection += `- Files modified: ${attempt.filesModified.slice(0, 5).join(', ')}\n`;
|
|
637
|
-
if (attempt.prUrl)
|
|
638
|
-
previousAttemptsSection += `- PR created: ${attempt.prUrl}\n`;
|
|
639
|
-
if (attempt.outcome)
|
|
640
|
-
previousAttemptsSection += `- Outcome: ${attempt.outcome}\n`;
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
if (previousContext.userFeedback.length > 0) {
|
|
644
|
-
previousAttemptsSection += `\n### User Feedback (Address These)\n`;
|
|
645
|
-
previousContext.userFeedback.forEach(feedback => {
|
|
646
|
-
previousAttemptsSection += `\n> "${feedback.content}"\n`;
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
previousAttemptsSection += `\n---\n\n`;
|
|
650
|
-
}
|
|
651
|
-
return `# Planning Task
|
|
652
|
-
|
|
653
|
-
${contextSection}${previousAttemptsSection}## Task to Implement
|
|
654
|
-
**${taskTitle}**
|
|
655
|
-
|
|
656
|
-
${taskDescription || 'No additional description provided.'}
|
|
657
|
-
|
|
658
|
-
## Your Mission
|
|
659
|
-
|
|
660
|
-
1. **Check for ASTRID.md** - Read project context
|
|
661
|
-
2. **Explore the codebase** - Understand structure and patterns
|
|
662
|
-
3. **Read key files** - Understand implementations you'll change
|
|
663
|
-
4. **Create an implementation plan** - List specific files to modify
|
|
664
|
-
|
|
665
|
-
Begin exploration now.`;
|
|
666
|
-
}
|
|
667
|
-
function buildImplementationPrompt(plan, taskTitle, taskDescription, astridMd, testCommand, iosProject) {
|
|
668
|
-
const filesSection = plan.files
|
|
669
|
-
.map(f => `- **${f.path}**: ${f.purpose}\n Changes: ${f.changes}`)
|
|
670
|
-
.join('\n');
|
|
671
|
-
const contextSection = astridMd
|
|
672
|
-
? `## Project Context\n\n${astridMd.substring(0, 4000)}${astridMd.length > 4000 ? '\n\n[truncated...]' : ''}\n\n---\n\n`
|
|
673
|
-
: '';
|
|
674
|
-
let testingSection = '## Testing Requirements\n\n';
|
|
675
|
-
if (testCommand) {
|
|
676
|
-
testingSection += `**Test Command:** \`${testCommand}\`\n\n`;
|
|
677
|
-
}
|
|
678
|
-
if (iosProject?.hasIOSProject) {
|
|
679
|
-
testingSection += `**iOS Build:** \`${iosProject.buildCommand}\`\n\n`;
|
|
680
|
-
}
|
|
681
|
-
return `# Implementation Task
|
|
682
|
-
|
|
683
|
-
${contextSection}## Task
|
|
684
|
-
**${taskTitle}**
|
|
685
|
-
${taskDescription || ''}
|
|
686
|
-
|
|
687
|
-
## Approved Implementation Plan
|
|
688
|
-
|
|
689
|
-
### Summary
|
|
690
|
-
${plan.summary}
|
|
691
|
-
|
|
692
|
-
### Approach
|
|
693
|
-
${plan.approach}
|
|
694
|
-
|
|
695
|
-
### Files to Modify
|
|
696
|
-
${filesSection}
|
|
697
|
-
|
|
698
|
-
### Complexity
|
|
699
|
-
${plan.estimatedComplexity}
|
|
700
|
-
|
|
701
|
-
${testingSection}
|
|
702
|
-
|
|
703
|
-
Begin implementation now.`;
|
|
704
|
-
}
|
|
705
|
-
function parsePlanFromResult(result, exploredFiles, log) {
|
|
706
|
-
const resultText = result.result || '';
|
|
707
|
-
const jsonMatch = resultText.match(/```json\s*([\s\S]*?)\s*```/);
|
|
708
|
-
if (!jsonMatch) {
|
|
709
|
-
log('warn', 'No JSON block found in planning output', {});
|
|
710
|
-
return null;
|
|
711
|
-
}
|
|
712
|
-
try {
|
|
713
|
-
const parsed = JSON.parse(jsonMatch[1]);
|
|
714
|
-
if (!parsed.summary || !parsed.files || !Array.isArray(parsed.files)) {
|
|
715
|
-
log('warn', 'Invalid plan structure', { parsed });
|
|
716
|
-
return null;
|
|
717
|
-
}
|
|
718
|
-
// Validate plan has at least 1 file
|
|
719
|
-
if (parsed.files.length === 0) {
|
|
720
|
-
log('warn', 'Plan has no files to modify', { parsed });
|
|
721
|
-
return null;
|
|
722
|
-
}
|
|
723
|
-
const plan = {
|
|
724
|
-
summary: parsed.summary,
|
|
725
|
-
approach: parsed.approach || parsed.summary,
|
|
726
|
-
files: parsed.files.map((f) => ({
|
|
727
|
-
path: f.path,
|
|
728
|
-
purpose: f.purpose || 'Implementation',
|
|
729
|
-
changes: f.changes || 'See plan'
|
|
730
|
-
})),
|
|
731
|
-
estimatedComplexity: parsed.estimatedComplexity || 'medium',
|
|
732
|
-
considerations: parsed.considerations || [],
|
|
733
|
-
exploredFiles
|
|
734
|
-
};
|
|
735
|
-
log('info', 'Successfully parsed implementation plan', {
|
|
736
|
-
filesInPlan: plan.files.length,
|
|
737
|
-
filesExplored: plan.exploredFiles?.length || 0
|
|
738
|
-
});
|
|
739
|
-
return plan;
|
|
740
|
-
}
|
|
741
|
-
catch (error) {
|
|
742
|
-
log('error', 'Failed to parse plan JSON', { error: String(error), jsonText: jsonMatch[1] });
|
|
743
|
-
return null;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
async function parseExecutionResult(repoPath, result, plan, log) {
|
|
747
|
-
const { execSync } = await import('child_process');
|
|
748
|
-
try {
|
|
749
|
-
const gitStatusRaw = execSync('git status --porcelain', {
|
|
750
|
-
cwd: repoPath,
|
|
751
|
-
encoding: 'utf-8'
|
|
752
|
-
});
|
|
753
|
-
const lines = gitStatusRaw.split('\n').filter(line => line.trim().length > 0);
|
|
754
|
-
if (lines.length === 0) {
|
|
755
|
-
log('warn', 'No file changes detected by git', {});
|
|
756
|
-
return {
|
|
757
|
-
success: false,
|
|
758
|
-
files: [],
|
|
759
|
-
commitMessage: '',
|
|
760
|
-
prTitle: '',
|
|
761
|
-
prDescription: '',
|
|
762
|
-
error: 'No file changes were made'
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
const files = [];
|
|
766
|
-
for (const line of lines) {
|
|
767
|
-
const status = line.substring(0, 2).trim();
|
|
768
|
-
const filePath = line.substring(3).trim();
|
|
769
|
-
let action;
|
|
770
|
-
if (status === 'D' || status === ' D') {
|
|
771
|
-
action = 'delete';
|
|
772
|
-
}
|
|
773
|
-
else if (status === '?' || status === 'A' || status === '??') {
|
|
774
|
-
action = 'create';
|
|
775
|
-
}
|
|
776
|
-
else {
|
|
777
|
-
action = 'modify';
|
|
778
|
-
}
|
|
779
|
-
let content = '';
|
|
780
|
-
if (action !== 'delete') {
|
|
781
|
-
const fullPath = path.join(repoPath, filePath);
|
|
782
|
-
try {
|
|
783
|
-
content = await fs.readFile(fullPath, 'utf-8');
|
|
784
|
-
}
|
|
785
|
-
catch {
|
|
786
|
-
log('warn', `Could not read file: ${filePath}`, {});
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
files.push({ path: filePath, content, action });
|
|
790
|
-
}
|
|
791
|
-
log('info', 'Parsed file changes from git', {
|
|
792
|
-
totalFiles: files.length,
|
|
793
|
-
created: files.filter(f => f.action === 'create').length,
|
|
794
|
-
modified: files.filter(f => f.action === 'modify').length,
|
|
795
|
-
deleted: files.filter(f => f.action === 'delete').length
|
|
796
|
-
});
|
|
797
|
-
const resultText = result.result || '';
|
|
798
|
-
const jsonMatch = resultText.match(/```json\s*([\s\S]*?)\s*```/);
|
|
799
|
-
let commitMessage = `feat: ${plan.summary.substring(0, 50)}`;
|
|
800
|
-
let prTitle = plan.summary;
|
|
801
|
-
let prDescription = plan.approach;
|
|
802
|
-
if (jsonMatch) {
|
|
803
|
-
try {
|
|
804
|
-
const parsed = JSON.parse(jsonMatch[1]);
|
|
805
|
-
if (parsed.commitMessage)
|
|
806
|
-
commitMessage = parsed.commitMessage;
|
|
807
|
-
if (parsed.prTitle)
|
|
808
|
-
prTitle = parsed.prTitle;
|
|
809
|
-
if (parsed.prDescription)
|
|
810
|
-
prDescription = parsed.prDescription;
|
|
811
|
-
}
|
|
812
|
-
catch {
|
|
813
|
-
log('warn', 'Could not parse JSON output from Claude', {});
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return {
|
|
817
|
-
success: true,
|
|
818
|
-
files,
|
|
819
|
-
commitMessage,
|
|
820
|
-
prTitle,
|
|
821
|
-
prDescription
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
catch (error) {
|
|
825
|
-
log('error', 'Failed to parse execution result', {
|
|
826
|
-
error: error instanceof Error ? error.message : String(error)
|
|
827
|
-
});
|
|
828
|
-
return {
|
|
829
|
-
success: false,
|
|
830
|
-
files: [],
|
|
831
|
-
commitMessage: '',
|
|
832
|
-
prTitle: '',
|
|
833
|
-
prDescription: '',
|
|
834
|
-
error: `Failed to parse file changes: ${error instanceof Error ? error.message : String(error)}`
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
//# sourceMappingURL=claude.js.map
|