@qelos/aidev 0.2.13 → 0.3.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/.env.aidev.example +62 -58
- package/CONTRIBUTING.md +78 -78
- package/LICENSE +21 -21
- package/README.md +467 -455
- package/dist/__tests__/ai-runners.test.js +44 -4
- package/dist/__tests__/ai-runners.test.js.map +1 -1
- package/dist/__tests__/config.test.js +6 -4
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/init.test.js +2 -0
- package/dist/__tests__/init.test.js.map +1 -1
- package/dist/__tests__/local-provider.test.js +31 -31
- package/dist/__tests__/lockfile.test.js +1 -1
- package/dist/__tests__/lockfile.test.js.map +1 -1
- package/dist/__tests__/platform.test.js +16 -5
- package/dist/__tests__/platform.test.js.map +1 -1
- package/dist/__tests__/providers.test.js +128 -0
- package/dist/__tests__/providers.test.js.map +1 -1
- package/dist/__tests__/run.test.js +55 -0
- package/dist/__tests__/run.test.js.map +1 -1
- package/dist/ai/antigravity.d.ts +12 -0
- package/dist/ai/antigravity.d.ts.map +1 -0
- package/dist/ai/antigravity.js +64 -0
- package/dist/ai/antigravity.js.map +1 -0
- package/dist/ai/claude.d.ts.map +1 -1
- package/dist/ai/claude.js +1 -10
- package/dist/ai/claude.js.map +1 -1
- package/dist/ai/cursor.d.ts.map +1 -1
- package/dist/ai/cursor.js +1 -7
- package/dist/ai/cursor.js.map +1 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/windsurf.d.ts.map +1 -1
- package/dist/ai/windsurf.js +1 -7
- package/dist/ai/windsurf.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/commands/help.js +61 -61
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +9 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/run.d.ts +3 -3
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +154 -134
- package/dist/commands/run.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/git.d.ts +4 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +11 -0
- package/dist/git.js.map +1 -1
- package/dist/github.js +23 -23
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +12 -0
- package/dist/permissions.js.map +1 -1
- package/dist/platform.d.ts +5 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +21 -0
- package/dist/platform.js.map +1 -1
- package/dist/providers/clickup.d.ts.map +1 -1
- package/dist/providers/clickup.js +2 -1
- package/dist/providers/clickup.js.map +1 -1
- package/dist/providers/jira.d.ts.map +1 -1
- package/dist/providers/jira.js +2 -1
- package/dist/providers/jira.js.map +1 -1
- package/dist/providers/linear.js +71 -71
- package/dist/providers/linear.js.map +1 -1
- package/dist/providers/monday.js +39 -39
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +50 -50
- package/scripts/run-tests.cjs +18 -0
package/dist/commands/run.js
CHANGED
|
@@ -219,7 +219,7 @@ async function processTask(task, filter, config, provider, runners, screenAvaila
|
|
|
219
219
|
const comments = await provider.getComments(task.id);
|
|
220
220
|
const trigger = hasTriggerWord(comments, config.triggerWord);
|
|
221
221
|
if (isPending) {
|
|
222
|
-
const reply = hasHumanReply(comments);
|
|
222
|
+
const reply = hasHumanReply(comments, config.commentPrefix);
|
|
223
223
|
if (!reply && !trigger) {
|
|
224
224
|
logger_1.logger.info(`[${task.id}] "${task.name}" skipped — pending task has no human reply or trigger word ("${config.triggerWord}")`);
|
|
225
225
|
return 'skipped';
|
|
@@ -236,18 +236,18 @@ async function processTask(task, filter, config, provider, runners, screenAvaila
|
|
|
236
236
|
logger_1.logger.info(`Trigger word "${config.triggerWord}" found — re-processing task`);
|
|
237
237
|
}
|
|
238
238
|
if (!screenAvailable) {
|
|
239
|
-
await notifySleeping(task, provider);
|
|
239
|
+
await notifySleeping(task, provider, config.commentPrefix);
|
|
240
240
|
return 'skipped';
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
else {
|
|
244
244
|
if (!screenAvailable) {
|
|
245
|
-
await notifySleeping(task, provider);
|
|
245
|
+
await notifySleeping(task, provider, config.commentPrefix);
|
|
246
246
|
return 'skipped';
|
|
247
247
|
}
|
|
248
248
|
const clarification = await checkNeedsClarification(task, config, provider, runners);
|
|
249
249
|
if (clarification) {
|
|
250
|
-
await provider.postComment(task.id,
|
|
250
|
+
await provider.postComment(task.id, `${config.commentPrefix} ${clarification}`);
|
|
251
251
|
await provider.updateStatus(task.id, getPendingStatus(config));
|
|
252
252
|
logger_1.logger.info(`Posted clarification question, set status to ${getPendingStatus(config)}`);
|
|
253
253
|
return 'skipped';
|
|
@@ -261,11 +261,11 @@ async function processTask(task, filter, config, provider, runners, screenAvaila
|
|
|
261
261
|
}
|
|
262
262
|
return 'processed';
|
|
263
263
|
}
|
|
264
|
-
function hasHumanReply(comments) {
|
|
264
|
+
function hasHumanReply(comments, commentPrefix = '[aidev]') {
|
|
265
265
|
if (comments.length < 2)
|
|
266
266
|
return false;
|
|
267
267
|
const lastComment = comments[comments.length - 1];
|
|
268
|
-
return !lastComment.text.includes(
|
|
268
|
+
return !lastComment.text.includes(commentPrefix);
|
|
269
269
|
}
|
|
270
270
|
/**
|
|
271
271
|
* Returns true if the last comment contains the trigger word.
|
|
@@ -282,7 +282,7 @@ function hasTriggerWord(comments, triggerWord) {
|
|
|
282
282
|
return true;
|
|
283
283
|
return false;
|
|
284
284
|
}
|
|
285
|
-
async function notifySleeping(task, provider) {
|
|
285
|
+
async function notifySleeping(task, provider, commentPrefix) {
|
|
286
286
|
try {
|
|
287
287
|
const comments = await provider.getComments(task.id);
|
|
288
288
|
const lastComment = comments.length > 0 ? comments[comments.length - 1] : null;
|
|
@@ -295,7 +295,7 @@ async function notifySleeping(task, provider) {
|
|
|
295
295
|
// If we can't check comments, still attempt to post
|
|
296
296
|
}
|
|
297
297
|
try {
|
|
298
|
-
await provider.postComment(task.id,
|
|
298
|
+
await provider.postComment(task.id, `${commentPrefix} Cannot work on this task — the ${SLEEPING_MARKER} or the screen is locked. ` +
|
|
299
299
|
'AI agents require an active display session to operate. Please wake the machine and unlock the screen so I can continue.');
|
|
300
300
|
logger_1.logger.info(`[${task.id}] Posted sleep notification`);
|
|
301
301
|
}
|
|
@@ -313,16 +313,16 @@ async function checkNeedsClarification(task, config, provider, runners) {
|
|
|
313
313
|
logger_1.logger.warn('No AI runner available — skipping clarification check');
|
|
314
314
|
return null;
|
|
315
315
|
}
|
|
316
|
-
const clarificationPrompt = `You are a senior software developer reviewing a task.
|
|
317
|
-
Determine if the following task has enough information to implement without further clarification.
|
|
318
|
-
|
|
319
|
-
Task name: ${task.name}
|
|
320
|
-
Task description: ${task.description || '(no description)'}
|
|
321
|
-
|
|
322
|
-
Respond with valid JSON only:
|
|
323
|
-
{
|
|
324
|
-
"clear": true|false,
|
|
325
|
-
"question": "question to ask if not clear, or null"
|
|
316
|
+
const clarificationPrompt = `You are a senior software developer reviewing a task.
|
|
317
|
+
Determine if the following task has enough information to implement without further clarification.
|
|
318
|
+
|
|
319
|
+
Task name: ${task.name}
|
|
320
|
+
Task description: ${task.description || '(no description)'}
|
|
321
|
+
|
|
322
|
+
Respond with valid JSON only:
|
|
323
|
+
{
|
|
324
|
+
"clear": true|false,
|
|
325
|
+
"question": "question to ask if not clear, or null"
|
|
326
326
|
}`;
|
|
327
327
|
for (const runner of availableRunners) {
|
|
328
328
|
const result = await runner.run(clarificationPrompt);
|
|
@@ -350,30 +350,30 @@ Respond with valid JSON only:
|
|
|
350
350
|
return null;
|
|
351
351
|
}
|
|
352
352
|
function buildConflictResolutionPrompt(task, conflictFiles, context) {
|
|
353
|
-
return `You are resolving merge conflicts in a software development task branch.
|
|
354
|
-
|
|
355
|
-
The task branch has fallen behind the base branch and has merge conflicts that need to be resolved.
|
|
356
|
-
|
|
357
|
-
## Task context (DO NOT break this — the task must still work after conflict resolution)
|
|
358
|
-
|
|
359
|
-
Task: ${task.name}
|
|
360
|
-
|
|
361
|
-
Description:
|
|
362
|
-
${task.description || '(no description provided)'}
|
|
363
|
-
${context}
|
|
364
|
-
|
|
365
|
-
## Merge conflicts
|
|
366
|
-
|
|
367
|
-
The following files have merge conflicts with conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...):
|
|
368
|
-
${conflictFiles.map((f) => `- ${f}`).join('\n')}
|
|
369
|
-
|
|
370
|
-
## Instructions
|
|
371
|
-
|
|
372
|
-
1. Open each conflicting file and resolve the conflict markers
|
|
373
|
-
2. Keep BOTH the task's changes AND the base branch updates where possible
|
|
374
|
-
3. If the base branch changed something the task also changed, prefer the task's intent but make sure it works with the new base branch code
|
|
375
|
-
4. Remove all conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...)
|
|
376
|
-
5. Make sure the code compiles and is consistent after resolution
|
|
353
|
+
return `You are resolving merge conflicts in a software development task branch.
|
|
354
|
+
|
|
355
|
+
The task branch has fallen behind the base branch and has merge conflicts that need to be resolved.
|
|
356
|
+
|
|
357
|
+
## Task context (DO NOT break this — the task must still work after conflict resolution)
|
|
358
|
+
|
|
359
|
+
Task: ${task.name}
|
|
360
|
+
|
|
361
|
+
Description:
|
|
362
|
+
${task.description || '(no description provided)'}
|
|
363
|
+
${context}
|
|
364
|
+
|
|
365
|
+
## Merge conflicts
|
|
366
|
+
|
|
367
|
+
The following files have merge conflicts with conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...):
|
|
368
|
+
${conflictFiles.map((f) => `- ${f}`).join('\n')}
|
|
369
|
+
|
|
370
|
+
## Instructions
|
|
371
|
+
|
|
372
|
+
1. Open each conflicting file and resolve the conflict markers
|
|
373
|
+
2. Keep BOTH the task's changes AND the base branch updates where possible
|
|
374
|
+
3. If the base branch changed something the task also changed, prefer the task's intent but make sure it works with the new base branch code
|
|
375
|
+
4. Remove all conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...)
|
|
376
|
+
5. Make sure the code compiles and is consistent after resolution
|
|
377
377
|
6. Do NOT make any changes beyond what is needed to resolve the conflicts`;
|
|
378
378
|
}
|
|
379
379
|
async function resolveConflictsWithAI(task, config, provider, runners, context, branchName) {
|
|
@@ -396,7 +396,7 @@ async function resolveConflictsWithAI(task, config, provider, runners, context,
|
|
|
396
396
|
logger_1.logger.warn(`Branch has conflicts with ${config.githubBaseBranch} in ${check.conflictFiles.length} file(s): ` +
|
|
397
397
|
check.conflictFiles.join(', '));
|
|
398
398
|
try {
|
|
399
|
-
await provider.postComment(task.id,
|
|
399
|
+
await provider.postComment(task.id, `${config.commentPrefix} Branch \`${branchName}\` has merge conflicts with \`${config.githubBaseBranch}\` ` +
|
|
400
400
|
`in ${check.conflictFiles.length} file(s). Attempting automatic resolution...`);
|
|
401
401
|
}
|
|
402
402
|
catch { /* ignore */ }
|
|
@@ -425,7 +425,7 @@ async function resolveConflictsWithAI(task, config, provider, runners, context,
|
|
|
425
425
|
logger_1.logger.error('All AI runners failed to resolve merge conflicts');
|
|
426
426
|
git.abortMerge();
|
|
427
427
|
try {
|
|
428
|
-
await provider.postComment(task.id,
|
|
428
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to automatically resolve merge conflicts. Manual intervention needed to rebase/merge the branch.`);
|
|
429
429
|
}
|
|
430
430
|
catch { /* ignore */ }
|
|
431
431
|
return false;
|
|
@@ -440,7 +440,7 @@ async function resolveConflictsWithAI(task, config, provider, runners, context,
|
|
|
440
440
|
}
|
|
441
441
|
logger_1.logger.success('Merge conflicts resolved successfully');
|
|
442
442
|
try {
|
|
443
|
-
await provider.postComment(task.id,
|
|
443
|
+
await provider.postComment(task.id, `${config.commentPrefix} Merge conflicts resolved automatically.`);
|
|
444
444
|
}
|
|
445
445
|
catch { /* ignore */ }
|
|
446
446
|
}
|
|
@@ -494,7 +494,7 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
494
494
|
try {
|
|
495
495
|
await provider.updateStatus(task.id, 'in progress');
|
|
496
496
|
const verb = branchExists ? 'Continuing' : 'Starting';
|
|
497
|
-
await provider.postComment(task.id,
|
|
497
|
+
await provider.postComment(task.id, `${config.commentPrefix} ${verb} implementation on branch \`${branchName}\``);
|
|
498
498
|
}
|
|
499
499
|
catch (err) {
|
|
500
500
|
logger_1.logger.warn(`Could not update task status: ${err}`);
|
|
@@ -502,7 +502,7 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
502
502
|
if (branchExists) {
|
|
503
503
|
if (!git.fetchAndCheckoutBranch(config.gitRemote, branchName)) {
|
|
504
504
|
logger_1.logger.error(`Failed to checkout existing branch ${branchName}`);
|
|
505
|
-
await provider.postComment(task.id,
|
|
505
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to checkout existing branch. Manual intervention needed.`);
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
508
|
logger_1.logger.info(`Continuing on existing branch: ${branchName}`);
|
|
@@ -510,14 +510,14 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
510
510
|
else {
|
|
511
511
|
if (!git.createBranchFromRemote(config.gitRemote, config.githubBaseBranch, branchName)) {
|
|
512
512
|
logger_1.logger.error(`Failed to create branch ${branchName} from ${config.gitRemote}/${config.githubBaseBranch}`);
|
|
513
|
-
await provider.postComment(task.id,
|
|
513
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to prepare git branch. Manual intervention needed.`);
|
|
514
514
|
return;
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
let context = '';
|
|
518
518
|
try {
|
|
519
519
|
const comments = await provider.getComments(task.id);
|
|
520
|
-
const humanComments = filterAutomatedComments(comments);
|
|
520
|
+
const humanComments = filterAutomatedComments(comments, config.commentPrefix);
|
|
521
521
|
if (humanComments.length > 0) {
|
|
522
522
|
context = '\n\nConversation context:\n' + humanComments.map((c) => `${c.author}: ${c.text}`).join('\n');
|
|
523
523
|
}
|
|
@@ -555,6 +555,11 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
555
555
|
implemented = true;
|
|
556
556
|
break;
|
|
557
557
|
}
|
|
558
|
+
if (result.success && !git.hasChanges() && git.hasCommitsAhead(config.gitRemote, config.githubBaseBranch)) {
|
|
559
|
+
logger_1.logger.info(`${runner.name} produced no new file changes, but branch already has commits ahead of ${config.githubBaseBranch}`);
|
|
560
|
+
implemented = true;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
558
563
|
if (!result.success) {
|
|
559
564
|
logger_1.logger.warn(`${runner.name} failed — trying next runner`);
|
|
560
565
|
previousNotes = `Previous runner (${runner.name}) output:\n${result.output}\nErrors:\n${result.error}`;
|
|
@@ -567,20 +572,22 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
567
572
|
if (!implemented) {
|
|
568
573
|
logger_1.logger.error('All AI runners failed or produced no changes');
|
|
569
574
|
const diagnostics = (0, diagnostics_1.collectAndLogDiagnostics)();
|
|
570
|
-
await provider.postComment(task.id,
|
|
575
|
+
await provider.postComment(task.id, `${config.commentPrefix} All AI runners failed. Manual implementation needed.\n\n${diagnostics}`);
|
|
571
576
|
if (!branchExists) {
|
|
572
577
|
git.deleteBranch(branchName);
|
|
573
578
|
}
|
|
574
579
|
return;
|
|
575
580
|
}
|
|
576
|
-
// Commit and push
|
|
577
|
-
if (
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
581
|
+
// Commit and push (only if there are new changes to commit)
|
|
582
|
+
if (git.hasChanges()) {
|
|
583
|
+
if (!git.addAll() || !git.commit(`${config.commentPrefix} Implement: ${task.name}\n\nTask: ${task.url}`, branchName)) {
|
|
584
|
+
logger_1.logger.error('Failed to commit changes');
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (!git.push(config.gitRemote, branchName)) {
|
|
588
|
+
logger_1.logger.error('Failed to push branch');
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
584
591
|
}
|
|
585
592
|
if (reviewThreads.length > 0) {
|
|
586
593
|
resolveHandledThreads(reviewThreads);
|
|
@@ -598,14 +605,14 @@ async function implementTask(task, branchName, branchExists, config, provider, r
|
|
|
598
605
|
logger_1.logger.success(`Task implemented: branch ${branchName} pushed`);
|
|
599
606
|
}
|
|
600
607
|
function buildImplementPrompt(task, context) {
|
|
601
|
-
return `You are implementing a software development task. Make the necessary code changes to complete the task described below.
|
|
602
|
-
|
|
603
|
-
Task: ${task.name}
|
|
604
|
-
|
|
605
|
-
Description:
|
|
606
|
-
${task.description || '(no description provided)'}
|
|
607
|
-
${context}
|
|
608
|
-
|
|
608
|
+
return `You are implementing a software development task. Make the necessary code changes to complete the task described below.
|
|
609
|
+
|
|
610
|
+
Task: ${task.name}
|
|
611
|
+
|
|
612
|
+
Description:
|
|
613
|
+
${task.description || '(no description provided)'}
|
|
614
|
+
${context}
|
|
615
|
+
|
|
609
616
|
Please implement the required changes. Focus on correctness and follow the existing code style in the project.`;
|
|
610
617
|
}
|
|
611
618
|
async function analyzeAndPlan(task, context, runners) {
|
|
@@ -614,28 +621,28 @@ async function analyzeAndPlan(task, context, runners) {
|
|
|
614
621
|
logger_1.logger.error('No AI runner available for task analysis');
|
|
615
622
|
return null;
|
|
616
623
|
}
|
|
617
|
-
const analysisPrompt = `You are a senior software architect breaking down a development task into smaller, sequential implementation steps.
|
|
618
|
-
|
|
619
|
-
Task name: ${task.name}
|
|
620
|
-
|
|
621
|
-
Description:
|
|
622
|
-
${task.description || '(no description provided)'}
|
|
623
|
-
${context}
|
|
624
|
-
|
|
625
|
-
Analyze this task and break it into smaller, independently implementable sub-tasks that should be executed sequentially. Each sub-task should be a coherent unit of work that can be committed separately.
|
|
626
|
-
|
|
627
|
-
Respond with valid JSON only — no markdown fences, no extra text:
|
|
628
|
-
{
|
|
629
|
-
"instructions": "Detailed implementation instructions in markdown covering the full task — architecture decisions, key files to modify, edge cases to handle, testing approach",
|
|
630
|
-
"subtasks": [
|
|
631
|
-
{
|
|
632
|
-
"id": 1,
|
|
633
|
-
"title": "Short title for the sub-task",
|
|
634
|
-
"description": "Detailed description of what to implement in this step, including specific files and functions to change"
|
|
635
|
-
}
|
|
636
|
-
]
|
|
637
|
-
}
|
|
638
|
-
|
|
624
|
+
const analysisPrompt = `You are a senior software architect breaking down a development task into smaller, sequential implementation steps.
|
|
625
|
+
|
|
626
|
+
Task name: ${task.name}
|
|
627
|
+
|
|
628
|
+
Description:
|
|
629
|
+
${task.description || '(no description provided)'}
|
|
630
|
+
${context}
|
|
631
|
+
|
|
632
|
+
Analyze this task and break it into smaller, independently implementable sub-tasks that should be executed sequentially. Each sub-task should be a coherent unit of work that can be committed separately.
|
|
633
|
+
|
|
634
|
+
Respond with valid JSON only — no markdown fences, no extra text:
|
|
635
|
+
{
|
|
636
|
+
"instructions": "Detailed implementation instructions in markdown covering the full task — architecture decisions, key files to modify, edge cases to handle, testing approach",
|
|
637
|
+
"subtasks": [
|
|
638
|
+
{
|
|
639
|
+
"id": 1,
|
|
640
|
+
"title": "Short title for the sub-task",
|
|
641
|
+
"description": "Detailed description of what to implement in this step, including specific files and functions to change"
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
|
|
639
646
|
Keep sub-tasks focused: 2-6 sub-tasks is ideal. Order them by dependency (foundation first).`;
|
|
640
647
|
logger_1.logger.info('Analyzing task and creating implementation plan...');
|
|
641
648
|
const result = await runner.run(analysisPrompt);
|
|
@@ -682,20 +689,20 @@ async function executeSubTask(subtask, task, plan, config, runners, reviewContex
|
|
|
682
689
|
.filter((s) => s.status === 'done')
|
|
683
690
|
.map((s) => ` - [done] ${s.id}. ${s.title}`)
|
|
684
691
|
.join('\n');
|
|
685
|
-
const prompt = `You are implementing step ${subtask.id} of a multi-step task.
|
|
686
|
-
|
|
687
|
-
Overall task: ${task.name}
|
|
688
|
-
${task.description ? `\nTask description:\n${task.description}` : ''}
|
|
689
|
-
|
|
690
|
-
## Full implementation instructions
|
|
691
|
-
${instructions}
|
|
692
|
-
${reviewContext || ''}
|
|
693
|
-
## Progress
|
|
694
|
-
${completedSteps || '(no steps completed yet)'}
|
|
695
|
-
|
|
696
|
-
## Current step: ${subtask.id}. ${subtask.title}
|
|
697
|
-
${subtask.description}
|
|
698
|
-
|
|
692
|
+
const prompt = `You are implementing step ${subtask.id} of a multi-step task.
|
|
693
|
+
|
|
694
|
+
Overall task: ${task.name}
|
|
695
|
+
${task.description ? `\nTask description:\n${task.description}` : ''}
|
|
696
|
+
|
|
697
|
+
## Full implementation instructions
|
|
698
|
+
${instructions}
|
|
699
|
+
${reviewContext || ''}
|
|
700
|
+
## Progress
|
|
701
|
+
${completedSteps || '(no steps completed yet)'}
|
|
702
|
+
|
|
703
|
+
## Current step: ${subtask.id}. ${subtask.title}
|
|
704
|
+
${subtask.description}
|
|
705
|
+
|
|
699
706
|
Implement ONLY this step. Focus on correctness and follow the existing code style.`;
|
|
700
707
|
let implemented = false;
|
|
701
708
|
let previousNotes = '';
|
|
@@ -724,7 +731,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
724
731
|
try {
|
|
725
732
|
await provider.updateStatus(task.id, 'in progress');
|
|
726
733
|
const verb = branchExists ? 'Continuing' : 'Starting';
|
|
727
|
-
await provider.postComment(task.id,
|
|
734
|
+
await provider.postComment(task.id, `${config.commentPrefix} ${verb} implementation on branch \`${branchName}\` (thinking mode — will analyze and break into sub-tasks)`);
|
|
728
735
|
}
|
|
729
736
|
catch (err) {
|
|
730
737
|
logger_1.logger.warn(`Could not update task status: ${err}`);
|
|
@@ -732,7 +739,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
732
739
|
if (branchExists) {
|
|
733
740
|
if (!git.fetchAndCheckoutBranch(config.gitRemote, branchName)) {
|
|
734
741
|
logger_1.logger.error(`Failed to checkout existing branch ${branchName}`);
|
|
735
|
-
await provider.postComment(task.id,
|
|
742
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to checkout existing branch. Manual intervention needed.`);
|
|
736
743
|
return;
|
|
737
744
|
}
|
|
738
745
|
logger_1.logger.info(`Continuing on existing branch: ${branchName}`);
|
|
@@ -740,14 +747,14 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
740
747
|
else {
|
|
741
748
|
if (!git.createBranchFromRemote(config.gitRemote, config.githubBaseBranch, branchName)) {
|
|
742
749
|
logger_1.logger.error(`Failed to create branch ${branchName} from ${config.gitRemote}/${config.githubBaseBranch}`);
|
|
743
|
-
await provider.postComment(task.id,
|
|
750
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to prepare git branch. Manual intervention needed.`);
|
|
744
751
|
return;
|
|
745
752
|
}
|
|
746
753
|
}
|
|
747
754
|
let context = '';
|
|
748
755
|
try {
|
|
749
756
|
const comments = await provider.getComments(task.id);
|
|
750
|
-
const humanComments = filterAutomatedComments(comments);
|
|
757
|
+
const humanComments = filterAutomatedComments(comments, config.commentPrefix);
|
|
751
758
|
if (humanComments.length > 0) {
|
|
752
759
|
context = '\n\nConversation context:\n' + humanComments.map((c) => `${c.author}: ${c.text}`).join('\n');
|
|
753
760
|
}
|
|
@@ -780,7 +787,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
780
787
|
plan = await analyzeAndPlan(task, context, runners);
|
|
781
788
|
if (!plan) {
|
|
782
789
|
logger_1.logger.error('Failed to create implementation plan');
|
|
783
|
-
await provider.postComment(task.id,
|
|
790
|
+
await provider.postComment(task.id, `${config.commentPrefix} Failed to analyze and break down the task. Manual implementation needed.`);
|
|
784
791
|
cleanupThinkingFiles(task.id);
|
|
785
792
|
if (!branchExists)
|
|
786
793
|
git.deleteBranch(branchName);
|
|
@@ -788,7 +795,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
788
795
|
}
|
|
789
796
|
logger_1.logger.info(`Task broken into ${plan.subtasks.length} sub-tasks`);
|
|
790
797
|
try {
|
|
791
|
-
await provider.postComment(task.id,
|
|
798
|
+
await provider.postComment(task.id, `${config.commentPrefix} Task analyzed and broken into ${plan.subtasks.length} sub-tasks:\n\n${formatSubtaskList(plan)}`);
|
|
792
799
|
}
|
|
793
800
|
catch (err) {
|
|
794
801
|
logger_1.logger.warn(`Failed to post breakdown comment: ${err}`);
|
|
@@ -811,12 +818,12 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
811
818
|
logger_1.logger.error(` Step ${subtask.id} failed: ${subtask.title}`);
|
|
812
819
|
const diagnostics = (0, diagnostics_1.collectAndLogDiagnostics)();
|
|
813
820
|
try {
|
|
814
|
-
await provider.postComment(task.id,
|
|
821
|
+
await provider.postComment(task.id, `${config.commentPrefix} Step ${subtask.id} failed: ${subtask.title}\n\n${formatSubtaskList(plan)}\n\n${diagnostics}`);
|
|
815
822
|
}
|
|
816
823
|
catch { /* ignore */ }
|
|
817
824
|
break;
|
|
818
825
|
}
|
|
819
|
-
if (!git.addAll() || !git.commit(
|
|
826
|
+
if (!git.addAll() || !git.commit(`${config.commentPrefix} Step ${subtask.id}: ${subtask.title}\n\nTask: ${task.url}`, branchName)) {
|
|
820
827
|
subtask.status = 'failed';
|
|
821
828
|
writeTaskPlan(plan);
|
|
822
829
|
allSucceeded = false;
|
|
@@ -834,7 +841,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
834
841
|
writeTaskPlan(plan);
|
|
835
842
|
logger_1.logger.success(` Step ${subtask.id} complete: ${subtask.title}`);
|
|
836
843
|
try {
|
|
837
|
-
await provider.postComment(task.id,
|
|
844
|
+
await provider.postComment(task.id, `${config.commentPrefix} Step ${subtask.id} complete: ${subtask.title}\n\n${formatSubtaskList(plan)}`);
|
|
838
845
|
}
|
|
839
846
|
catch { /* ignore */ }
|
|
840
847
|
}
|
|
@@ -843,7 +850,7 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
|
|
|
843
850
|
logger_1.logger.error('Thinking task did not complete all sub-tasks');
|
|
844
851
|
try {
|
|
845
852
|
const diagnostics = (0, diagnostics_1.collectAndLogDiagnostics)();
|
|
846
|
-
await provider.postComment(task.id,
|
|
853
|
+
await provider.postComment(task.id, `${config.commentPrefix} Thinking task did not complete all sub-tasks. Manual intervention needed.\n\n${diagnostics}`);
|
|
847
854
|
}
|
|
848
855
|
catch { /* ignore */ }
|
|
849
856
|
return;
|
|
@@ -883,7 +890,7 @@ function buildPRUrl(config, branch) {
|
|
|
883
890
|
}
|
|
884
891
|
function buildCompletionComment(branch, prUrl, config) {
|
|
885
892
|
const lines = [
|
|
886
|
-
|
|
893
|
+
`${config.commentPrefix} Implementation complete!`,
|
|
887
894
|
``,
|
|
888
895
|
`Branch: \`${branch}\``,
|
|
889
896
|
];
|
|
@@ -894,19 +901,32 @@ function buildCompletionComment(branch, prUrl, config) {
|
|
|
894
901
|
return lines.join('\n');
|
|
895
902
|
}
|
|
896
903
|
function buildNonCodePrompt(task, context) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
904
|
+
const hasComments = context.trim().length > 0;
|
|
905
|
+
if (hasComments) {
|
|
906
|
+
return `You are handling a non-code task. This task does NOT require code changes — it requires a thoughtful, verbal response.
|
|
907
|
+
|
|
908
|
+
Task: ${task.name}
|
|
909
|
+
|
|
910
|
+
Original description:
|
|
911
|
+
${task.description || '(no description provided)'}
|
|
912
|
+
${context}
|
|
913
|
+
|
|
914
|
+
IMPORTANT: The conversation above contains follow-up comments. Focus on the LATEST comment as the primary request to address — it may refine, override, or follow up on the original description. Use the original description and earlier comments only as background context.
|
|
915
|
+
|
|
916
|
+
Please provide a clear, detailed response. Your response will be posted as a comment on the task ticket, so write it as a direct answer or explanation addressed to the person who wrote the latest comment.`;
|
|
917
|
+
}
|
|
918
|
+
return `You are handling a non-code task. This task does NOT require code changes — it requires a thoughtful, verbal response.
|
|
919
|
+
|
|
920
|
+
Task: ${task.name}
|
|
921
|
+
|
|
922
|
+
Description:
|
|
923
|
+
${task.description || '(no description provided)'}
|
|
924
|
+
|
|
905
925
|
Please provide a clear, detailed response to this task. Your response will be posted as a comment on the task ticket, so write it as a direct answer or explanation addressed to the person who created the task.`;
|
|
906
926
|
}
|
|
907
927
|
function buildNonCodeCompletionComment(config, agentResponse) {
|
|
908
928
|
const lines = [
|
|
909
|
-
|
|
929
|
+
`${config.commentPrefix} Non-code task complete!`,
|
|
910
930
|
];
|
|
911
931
|
if (agentResponse) {
|
|
912
932
|
lines.push(``, `---`, ``, agentResponse);
|
|
@@ -914,11 +934,11 @@ function buildNonCodeCompletionComment(config, agentResponse) {
|
|
|
914
934
|
lines.push(``, `Status set to: ${getInReviewStatus(config)}`);
|
|
915
935
|
return lines.join('\n');
|
|
916
936
|
}
|
|
917
|
-
function hasAidevComment(comments) {
|
|
918
|
-
return comments.some((c) => c.text.includes(
|
|
937
|
+
function hasAidevComment(comments, commentPrefix = '[aidev]') {
|
|
938
|
+
return comments.some((c) => c.text.includes(commentPrefix));
|
|
919
939
|
}
|
|
920
|
-
function filterAutomatedComments(comments) {
|
|
921
|
-
return comments.filter((c) => !c.text.includes(
|
|
940
|
+
function filterAutomatedComments(comments, commentPrefix = '[aidev]') {
|
|
941
|
+
return comments.filter((c) => !c.text.includes(commentPrefix));
|
|
922
942
|
}
|
|
923
943
|
async function processNonCodeTask(task, filter, config, provider, runners, screenAvailable) {
|
|
924
944
|
const pendingStatus = getPendingStatus(config);
|
|
@@ -931,11 +951,11 @@ async function processNonCodeTask(task, filter, config, provider, runners, scree
|
|
|
931
951
|
return 'skipped';
|
|
932
952
|
}
|
|
933
953
|
const comments = await provider.getComments(task.id);
|
|
934
|
-
const wasProcessed = hasAidevComment(comments);
|
|
954
|
+
const wasProcessed = hasAidevComment(comments, config.commentPrefix);
|
|
935
955
|
if (isPending || wasProcessed) {
|
|
936
956
|
const trigger = hasTriggerWord(comments, config.triggerWord);
|
|
937
957
|
if (isPending) {
|
|
938
|
-
const reply = hasHumanReply(comments);
|
|
958
|
+
const reply = hasHumanReply(comments, config.commentPrefix);
|
|
939
959
|
if (!reply && !trigger) {
|
|
940
960
|
logger_1.logger.info(`[${task.id}] "${task.name}" skipped — pending with no human reply or trigger word ("${config.triggerWord}")`);
|
|
941
961
|
return 'skipped';
|
|
@@ -952,18 +972,18 @@ async function processNonCodeTask(task, filter, config, provider, runners, scree
|
|
|
952
972
|
logger_1.logger.info(`Trigger word "${config.triggerWord}" found — re-processing non-code task`);
|
|
953
973
|
}
|
|
954
974
|
if (!screenAvailable) {
|
|
955
|
-
await notifySleeping(task, provider);
|
|
975
|
+
await notifySleeping(task, provider, config.commentPrefix);
|
|
956
976
|
return 'skipped';
|
|
957
977
|
}
|
|
958
978
|
}
|
|
959
979
|
else {
|
|
960
980
|
if (!screenAvailable) {
|
|
961
|
-
await notifySleeping(task, provider);
|
|
981
|
+
await notifySleeping(task, provider, config.commentPrefix);
|
|
962
982
|
return 'skipped';
|
|
963
983
|
}
|
|
964
984
|
const clarification = await checkNeedsClarification(task, config, provider, runners);
|
|
965
985
|
if (clarification) {
|
|
966
|
-
await provider.postComment(task.id,
|
|
986
|
+
await provider.postComment(task.id, `${config.commentPrefix} ${clarification}`);
|
|
967
987
|
await provider.updateStatus(task.id, getPendingStatus(config));
|
|
968
988
|
logger_1.logger.info(`Posted clarification question, set status to ${getPendingStatus(config)}`);
|
|
969
989
|
return 'skipped';
|
|
@@ -976,7 +996,7 @@ async function implementNonCodeTask(task, config, provider, runners) {
|
|
|
976
996
|
logger_1.logger.info(`Implementing non-code task: ${task.name}`);
|
|
977
997
|
try {
|
|
978
998
|
await provider.updateStatus(task.id, 'in progress');
|
|
979
|
-
await provider.postComment(task.id,
|
|
999
|
+
await provider.postComment(task.id, `${config.commentPrefix} Starting non-code task execution`);
|
|
980
1000
|
}
|
|
981
1001
|
catch (err) {
|
|
982
1002
|
logger_1.logger.warn(`Could not update task status: ${err}`);
|
|
@@ -984,7 +1004,7 @@ async function implementNonCodeTask(task, config, provider, runners) {
|
|
|
984
1004
|
let context = '';
|
|
985
1005
|
try {
|
|
986
1006
|
const comments = await provider.getComments(task.id);
|
|
987
|
-
const humanComments = filterAutomatedComments(comments);
|
|
1007
|
+
const humanComments = filterAutomatedComments(comments, config.commentPrefix);
|
|
988
1008
|
if (humanComments.length > 0) {
|
|
989
1009
|
context = '\n\nConversation context:\n' + humanComments.map((c) => `${c.author}: ${c.text}`).join('\n');
|
|
990
1010
|
}
|
|
@@ -1014,7 +1034,7 @@ async function implementNonCodeTask(task, config, provider, runners) {
|
|
|
1014
1034
|
if (!implemented) {
|
|
1015
1035
|
logger_1.logger.error('All AI runners failed');
|
|
1016
1036
|
const diagnostics = (0, diagnostics_1.collectAndLogDiagnostics)();
|
|
1017
|
-
await provider.postComment(task.id,
|
|
1037
|
+
await provider.postComment(task.id, `${config.commentPrefix} All AI runners failed. Manual intervention needed.\n\n${diagnostics}`);
|
|
1018
1038
|
return;
|
|
1019
1039
|
}
|
|
1020
1040
|
try {
|