devbonzai 2.0.4 → 2.0.6
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/cli.js +137 -5
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -91,6 +91,7 @@ app.get('/', (req, res) => {
|
|
|
91
91
|
'POST /delete': 'Delete file or directory (body: {path})',
|
|
92
92
|
'POST /move': 'Move file or folder (body: {source, destination})',
|
|
93
93
|
'POST /open-cursor': 'Open Cursor (body: {path, line?})',
|
|
94
|
+
'POST /analyze_prompt': 'Analyze what files would be modified (body: {prompt})',
|
|
94
95
|
'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
|
|
95
96
|
'POST /revert_job': 'Revert to a previous commit (body: {beforeCommit})',
|
|
96
97
|
'POST /shutdown': 'Gracefully shutdown the server'
|
|
@@ -1275,6 +1276,120 @@ app.post('/open-cursor', (req, res) => {
|
|
|
1275
1276
|
}
|
|
1276
1277
|
});
|
|
1277
1278
|
|
|
1279
|
+
// Analyze prompt endpoint - asks agent what files it would modify without making changes
|
|
1280
|
+
app.post('/analyze_prompt', (req, res) => {
|
|
1281
|
+
console.log('🔵 [analyze_prompt] Endpoint hit');
|
|
1282
|
+
const { prompt } = req.body;
|
|
1283
|
+
console.log('🔵 [analyze_prompt] Received prompt:', prompt ? \`\${prompt.substring(0, 50)}...\` : 'none');
|
|
1284
|
+
|
|
1285
|
+
if (!prompt || typeof prompt !== 'string') {
|
|
1286
|
+
console.log('❌ [analyze_prompt] Error: prompt required');
|
|
1287
|
+
return res.status(400).json({ error: 'prompt required' });
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Configurable timeout (default 2 minutes for analysis)
|
|
1291
|
+
const timeoutMs = parseInt(req.body.timeout) || 2 * 60 * 1000;
|
|
1292
|
+
let timeoutId = null;
|
|
1293
|
+
let responseSent = false;
|
|
1294
|
+
|
|
1295
|
+
// Build analysis prompt - ask agent to list files without making changes
|
|
1296
|
+
const analysisPrompt = \`You are analyzing a coding task. Do NOT make any changes to any files. Only analyze and list the files you would need to modify to complete this task.
|
|
1297
|
+
|
|
1298
|
+
Respond ONLY with valid JSON in this exact format (no other text):
|
|
1299
|
+
{"files": [{"path": "path/to/file.ext", "reason": "brief reason for modification"}]}
|
|
1300
|
+
|
|
1301
|
+
If no files need modification, respond with: {"files": []}
|
|
1302
|
+
|
|
1303
|
+
Task to analyze: \${prompt}\`;
|
|
1304
|
+
|
|
1305
|
+
const args = ['--print', '--force', '--workspace', '.', analysisPrompt];
|
|
1306
|
+
|
|
1307
|
+
console.log('🔵 [analyze_prompt] Spawning cursor-agent process...');
|
|
1308
|
+
const proc = spawn(
|
|
1309
|
+
'cursor-agent',
|
|
1310
|
+
args,
|
|
1311
|
+
{
|
|
1312
|
+
cwd: ROOT,
|
|
1313
|
+
env: process.env,
|
|
1314
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
1315
|
+
}
|
|
1316
|
+
);
|
|
1317
|
+
|
|
1318
|
+
console.log('🔵 [analyze_prompt] Process spawned, PID:', proc.pid);
|
|
1319
|
+
|
|
1320
|
+
let stdout = '';
|
|
1321
|
+
let stderr = '';
|
|
1322
|
+
|
|
1323
|
+
timeoutId = setTimeout(() => {
|
|
1324
|
+
if (!responseSent && proc && !proc.killed) {
|
|
1325
|
+
console.log('⏱️ [analyze_prompt] Timeout reached, killing process...');
|
|
1326
|
+
proc.kill('SIGTERM');
|
|
1327
|
+
setTimeout(() => {
|
|
1328
|
+
if (!proc.killed) proc.kill('SIGKILL');
|
|
1329
|
+
}, 5000);
|
|
1330
|
+
|
|
1331
|
+
if (!responseSent) {
|
|
1332
|
+
responseSent = true;
|
|
1333
|
+
res.status(500).json({
|
|
1334
|
+
error: 'Process timeout',
|
|
1335
|
+
message: \`Analysis exceeded timeout of \${timeoutMs / 1000} seconds\`
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}, timeoutMs);
|
|
1340
|
+
|
|
1341
|
+
proc.stdout.on('data', (d) => {
|
|
1342
|
+
stdout += d.toString();
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
proc.stderr.on('data', (d) => {
|
|
1346
|
+
stderr += d.toString();
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
proc.on('error', (error) => {
|
|
1350
|
+
console.log('❌ [analyze_prompt] Process error:', error.message);
|
|
1351
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1352
|
+
if (!responseSent) {
|
|
1353
|
+
responseSent = true;
|
|
1354
|
+
return res.status(500).json({ error: error.message });
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
proc.on('close', (code, signal) => {
|
|
1359
|
+
console.log('🔵 [analyze_prompt] Process closed with code:', code);
|
|
1360
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1361
|
+
|
|
1362
|
+
if (!responseSent) {
|
|
1363
|
+
responseSent = true;
|
|
1364
|
+
|
|
1365
|
+
// Try to parse JSON from the output
|
|
1366
|
+
try {
|
|
1367
|
+
// Look for JSON in the output - it might be wrapped in other text
|
|
1368
|
+
const jsonMatch = stdout.match(/\\{[\\s\\S]*"files"[\\s\\S]*\\}/);
|
|
1369
|
+
if (jsonMatch) {
|
|
1370
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
1371
|
+
console.log('✅ [analyze_prompt] Parsed files:', parsed.files);
|
|
1372
|
+
res.json({ files: parsed.files || [] });
|
|
1373
|
+
} else {
|
|
1374
|
+
console.log('⚠️ [analyze_prompt] No JSON found in output, returning raw');
|
|
1375
|
+
res.json({
|
|
1376
|
+
files: [],
|
|
1377
|
+
raw: stdout,
|
|
1378
|
+
warning: 'Could not parse structured response'
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
} catch (parseError) {
|
|
1382
|
+
console.log('⚠️ [analyze_prompt] JSON parse error:', parseError.message);
|
|
1383
|
+
res.json({
|
|
1384
|
+
files: [],
|
|
1385
|
+
raw: stdout,
|
|
1386
|
+
warning: 'Could not parse JSON: ' + parseError.message
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1278
1393
|
app.post('/prompt_agent', (req, res) => {
|
|
1279
1394
|
console.log('🔵 [prompt_agent] Endpoint hit');
|
|
1280
1395
|
const { prompt } = req.body;
|
|
@@ -1294,18 +1409,35 @@ app.post('/prompt_agent', (req, res) => {
|
|
|
1294
1409
|
console.log('⚠️ [prompt_agent] Could not get beforeCommit:', e.message);
|
|
1295
1410
|
}
|
|
1296
1411
|
|
|
1297
|
-
//
|
|
1412
|
+
// Capture initial state of modified files (files already dirty before job starts)
|
|
1413
|
+
const initiallyModifiedFiles = new Set();
|
|
1414
|
+
try {
|
|
1415
|
+
const initialStatus = execSync('git status --short', { cwd: ROOT }).toString();
|
|
1416
|
+
initialStatus.split('\\n').filter(Boolean).forEach(line => {
|
|
1417
|
+
const filePath = line.substring(3).trim();
|
|
1418
|
+
if (filePath) initiallyModifiedFiles.add(filePath);
|
|
1419
|
+
});
|
|
1420
|
+
console.log('🔵 [prompt_agent] Initially modified files:', Array.from(initiallyModifiedFiles));
|
|
1421
|
+
} catch (e) {
|
|
1422
|
+
console.log('⚠️ [prompt_agent] Could not get initial status:', e.message);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Set up file change tracking - only track NEW changes during job
|
|
1298
1426
|
const changedFiles = new Set();
|
|
1299
1427
|
const pollInterval = setInterval(() => {
|
|
1300
1428
|
try {
|
|
1301
1429
|
const status = execSync('git status --short', { cwd: ROOT }).toString();
|
|
1302
1430
|
status.split('\\n').filter(Boolean).forEach(line => {
|
|
1303
1431
|
const filePath = line.substring(3).trim(); // Remove status prefix (XY + space)
|
|
1304
|
-
if
|
|
1432
|
+
// Only add if this file was NOT already modified before the job started
|
|
1433
|
+
if (filePath && !initiallyModifiedFiles.has(filePath)) {
|
|
1434
|
+
const wasNew = !changedFiles.has(filePath);
|
|
1435
|
+
changedFiles.add(filePath);
|
|
1436
|
+
if (wasNew) {
|
|
1437
|
+
console.log('📁 [prompt_agent] New file changed:', filePath);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1305
1440
|
});
|
|
1306
|
-
if (changedFiles.size > 0) {
|
|
1307
|
-
console.log('📁 [prompt_agent] Changed files:', Array.from(changedFiles));
|
|
1308
|
-
}
|
|
1309
1441
|
} catch (e) {
|
|
1310
1442
|
// Ignore git status errors
|
|
1311
1443
|
}
|