@qelos/aidev 0.9.0 → 0.11.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 (55) hide show
  1. package/.env.aidev.example +17 -2
  2. package/README.md +2 -1
  3. package/dist/ai/anthropicSdk.d.ts +35 -0
  4. package/dist/ai/anthropicSdk.d.ts.map +1 -0
  5. package/dist/ai/anthropicSdk.js +209 -0
  6. package/dist/ai/anthropicSdk.js.map +1 -0
  7. package/dist/ai/index.d.ts.map +1 -1
  8. package/dist/ai/index.js +2 -0
  9. package/dist/ai/index.js.map +1 -1
  10. package/dist/commands/accepted.d.ts +7 -0
  11. package/dist/commands/accepted.d.ts.map +1 -1
  12. package/dist/commands/accepted.js +18 -0
  13. package/dist/commands/accepted.js.map +1 -1
  14. package/dist/commands/init.d.ts +4 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +40 -4
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/run.d.ts +22 -0
  19. package/dist/commands/run.d.ts.map +1 -1
  20. package/dist/commands/run.js +507 -5
  21. package/dist/commands/run.js.map +1 -1
  22. package/dist/config.d.ts +15 -0
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +28 -2
  25. package/dist/config.js.map +1 -1
  26. package/dist/github.d.ts.map +1 -1
  27. package/dist/github.js +8 -1
  28. package/dist/github.js.map +1 -1
  29. package/dist/permissions.d.ts.map +1 -1
  30. package/dist/permissions.js +27 -0
  31. package/dist/permissions.js.map +1 -1
  32. package/dist/providers/clickup.d.ts.map +1 -1
  33. package/dist/providers/clickup.js +2 -0
  34. package/dist/providers/clickup.js.map +1 -1
  35. package/dist/providers/jira.d.ts.map +1 -1
  36. package/dist/providers/jira.js +2 -1
  37. package/dist/providers/jira.js.map +1 -1
  38. package/dist/providers/linear.d.ts.map +1 -1
  39. package/dist/providers/linear.js +2 -1
  40. package/dist/providers/linear.js.map +1 -1
  41. package/dist/providers/local.d.ts.map +1 -1
  42. package/dist/providers/local.js +1 -0
  43. package/dist/providers/local.js.map +1 -1
  44. package/dist/providers/monday.d.ts.map +1 -1
  45. package/dist/providers/monday.js +1 -0
  46. package/dist/providers/monday.js.map +1 -1
  47. package/dist/providers/notion.d.ts.map +1 -1
  48. package/dist/providers/notion.js +1 -0
  49. package/dist/providers/notion.js.map +1 -1
  50. package/dist/providers/trello.d.ts.map +1 -1
  51. package/dist/providers/trello.js +2 -0
  52. package/dist/providers/trello.js.map +1 -1
  53. package/dist/types.d.ts +3 -1
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +2 -1
@@ -38,6 +38,10 @@ exports.getPendingStatus = getPendingStatus;
38
38
  exports.getOpenStatus = getOpenStatus;
39
39
  exports.getInReviewStatus = getInReviewStatus;
40
40
  exports.getRunSkipReason = getRunSkipReason;
41
+ exports.isThinkingTask = isThinkingTask;
42
+ exports.isPlanningTask = isPlanningTask;
43
+ exports.buildPlanningAnalysisPrompt = buildPlanningAnalysisPrompt;
44
+ exports.parsePlanningResponse = parsePlanningResponse;
41
45
  exports.sortTasksByPriority = sortTasksByPriority;
42
46
  exports.cleanupStaleThinkingArtifacts = cleanupStaleThinkingArtifacts;
43
47
  exports.writeTaskPlan = writeTaskPlan;
@@ -55,6 +59,7 @@ exports.checkNeedsClarification = checkNeedsClarification;
55
59
  exports.buildConflictResolutionPrompt = buildConflictResolutionPrompt;
56
60
  exports.buildImplementPrompt = buildImplementPrompt;
57
61
  exports.splitFailedSubtask = splitFailedSubtask;
62
+ exports.implementPlanningTask = implementPlanningTask;
58
63
  exports.buildPRBody = buildPRBody;
59
64
  exports.tryCreatePR = tryCreatePR;
60
65
  exports.buildPRUrl = buildPRUrl;
@@ -64,6 +69,9 @@ exports.buildNonCodeCompletionComment = buildNonCodeCompletionComment;
64
69
  exports.hasAidevComment = hasAidevComment;
65
70
  exports.filterAutomatedComments = filterAutomatedComments;
66
71
  exports.hasHumanComment = hasHumanComment;
72
+ exports.buildNonCodeAnalysisPrompt = buildNonCodeAnalysisPrompt;
73
+ exports.buildNonCodeSubtaskPrompt = buildNonCodeSubtaskPrompt;
74
+ exports.buildNonCodeThinkingCompletionComment = buildNonCodeThinkingCompletionComment;
67
75
  exports.parseReplyDirectives = parseReplyDirectives;
68
76
  exports.buildReviewPrompt = buildReviewPrompt;
69
77
  exports.buildReviewCompletionComment = buildReviewCompletionComment;
@@ -77,6 +85,7 @@ const diagnostics_1 = require("../diagnostics");
77
85
  const lockfile_1 = require("../lockfile");
78
86
  const hooks_1 = require("../hooks");
79
87
  const sessions_1 = require("../sessions");
88
+ const accepted_1 = require("./accepted");
80
89
  const SKIP_STATUSES = new Set(['closed', 'done', 'cancelled', 'complete']);
81
90
  const NO_PRIORITY = Number.MAX_SAFE_INTEGER;
82
91
  const SLEEPING_MARKER = 'machine appears to be asleep';
@@ -141,6 +150,92 @@ function isThinkingTask(task, config) {
141
150
  const tag = config.thinkingTag.toLowerCase();
142
151
  return task.tags.some((t) => t.toLowerCase() === tag);
143
152
  }
153
+ function isPlanningTask(task, config) {
154
+ if (!config.planningTag)
155
+ return false;
156
+ const tag = config.planningTag.toLowerCase();
157
+ return task.tags.some((t) => t.toLowerCase() === tag);
158
+ }
159
+ function buildPlanningAnalysisPrompt(task, context) {
160
+ const tagsLine = task.tags.length > 0
161
+ ? `\nParent task tags: ${task.tags.join(', ')}`
162
+ : '';
163
+ return `You are a senior software architect operating in PLANNING MODE. Your job is to break a parent task into a list of fully self-contained sub-task tickets that will be pushed to the task management provider and worked on independently — each by a different agent, in isolation, with NO access to the parent task or to its sibling sub-tasks.
164
+
165
+ Parent task name: ${task.name}
166
+
167
+ Parent task description:
168
+ ${task.description || '(no description provided)'}${tagsLine}
169
+ ${context}
170
+
171
+ Decide one of:
172
+ (a) If critical information is missing and you cannot produce useful self-contained sub-tasks without it, return a single clarification question.
173
+ (b) Otherwise, return a list of sub-task drafts.
174
+
175
+ CRITICAL — each sub-task description MUST be fully isolated:
176
+ - Do NOT reference the parent task, sibling sub-tasks, or "the plan".
177
+ - Do NOT use phrases like "as discussed above", "see the parent ticket", "from step 1", or "after the previous sub-task". The agent executing this sub-task will not see any of that.
178
+ - Include every file path, function name, schema, constraint, reasoning, and reference the executing agent will need to complete the work standalone.
179
+ - Restate any shared context (architecture decisions, conventions, motivation) that is required to do the work correctly.
180
+ - Each description should read like its own complete ticket — title, what to change, why, where, and acceptance criteria.
181
+
182
+ Sub-task priority is optional and uses an integer 1–4 (1 = urgent, 4 = low). Omit the field if you have no opinion.
183
+
184
+ Respond with valid JSON only — no markdown fences, no extra text:
185
+ {
186
+ "clarification": "question text, or null if no clarification is needed",
187
+ "subtasks": [
188
+ {
189
+ "title": "Short, specific title",
190
+ "description": "Fully self-contained ticket body (markdown ok). Include all paths, references, and reasoning needed to do this work without seeing the parent ticket.",
191
+ "priority": 2
192
+ }
193
+ ]
194
+ }
195
+
196
+ If you set "clarification" to a non-null question, "subtasks" must be an empty array. If you provide sub-tasks, "clarification" must be null.`;
197
+ }
198
+ function parsePlanningResponse(output) {
199
+ const jsonMatch = output.match(/\{[\s\S]*\}/);
200
+ if (!jsonMatch)
201
+ return null;
202
+ let parsed;
203
+ try {
204
+ parsed = JSON.parse(jsonMatch[0]);
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ if (!parsed || typeof parsed !== 'object')
210
+ return null;
211
+ const obj = parsed;
212
+ let clarification;
213
+ if (typeof obj.clarification === 'string') {
214
+ const trimmed = obj.clarification.trim();
215
+ if (trimmed.length > 0 && trimmed.toLowerCase() !== 'null') {
216
+ clarification = trimmed;
217
+ }
218
+ }
219
+ const rawSubtasks = Array.isArray(obj.subtasks) ? obj.subtasks : [];
220
+ const subtasks = [];
221
+ for (const s of rawSubtasks) {
222
+ if (!s || typeof s !== 'object')
223
+ continue;
224
+ const entry = s;
225
+ const title = typeof entry.title === 'string' ? entry.title.trim() : '';
226
+ const description = typeof entry.description === 'string' ? entry.description.trim() : '';
227
+ if (!title || !description)
228
+ continue;
229
+ const draft = { title, description };
230
+ if (typeof entry.priority === 'number' && Number.isFinite(entry.priority)) {
231
+ draft.priority = entry.priority;
232
+ }
233
+ subtasks.push(draft);
234
+ }
235
+ if (!clarification && subtasks.length === 0)
236
+ return null;
237
+ return clarification ? { clarification, subtasks } : { subtasks };
238
+ }
144
239
  function sortTasksByPriority(tasks) {
145
240
  return [...tasks].sort((a, b) => (a.priority ?? NO_PRIORITY) - (b.priority ?? NO_PRIORITY));
146
241
  }
@@ -447,7 +542,10 @@ async function processTask(task, filter, config, provider, runners, screenAvaila
447
542
  return 'skipped';
448
543
  }
449
544
  }
450
- if (isThinkingTask(task, config)) {
545
+ if (isPlanningTask(task, config)) {
546
+ await implementPlanningTask(task, config, provider, runners, hooks, vm);
547
+ }
548
+ else if (isThinkingTask(task, config)) {
451
549
  await implementThinkingTask(task, branchName, branchExists, config, provider, runners, hooks, vm);
452
550
  }
453
551
  else {
@@ -883,6 +981,8 @@ ${context}
883
981
 
884
982
  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.
885
983
 
984
+ CRITICAL: Every sub-task MUST result in actual file modifications (create, edit, or delete files) that can be committed to git. A sub-task that produces no file changes is treated as a failure. Do NOT create sub-tasks that are pure investigation, verification, or read-only steps — for example "check if this folder exists", "review the existing code", "decide on an approach", "verify the build passes", or "run the tests". Any necessary investigation must happen inside a sub-task that also produces concrete file changes (e.g. fold the discovery into the step that applies the resulting edits). Each sub-task's description must name specific files and functions to add, modify, or remove.
985
+
886
986
  Respond with valid JSON only — no markdown fences, no extra text:
887
987
  {
888
988
  "taskSummary": "2-5 short sentences: overall goal and constraints only — for reuse in each sub-task prompt (no step-by-step detail; that goes in instructions and subtasks)",
@@ -891,7 +991,7 @@ Respond with valid JSON only — no markdown fences, no extra text:
891
991
  {
892
992
  "id": 1,
893
993
  "title": "Short title for the sub-task",
894
- "description": "Detailed description of what to implement in this step, including specific files and functions to change"
994
+ "description": "Detailed description of what to implement in this step, including specific files and functions to change. Must describe concrete file modifications."
895
995
  }
896
996
  ]
897
997
  }
@@ -971,11 +1071,13 @@ ${diagnostics}
971
1071
 
972
1072
  Split the failed sub-task above into exactly two smaller, independently implementable sub-tasks that together achieve the original goal. Each new sub-task should be a coherent unit of work that can be committed separately, ordered by dependency (foundation first). Take the diagnostics into account so the split actually addresses what broke.
973
1073
 
1074
+ CRITICAL: Each new sub-task MUST result in actual file modifications (create, edit, or delete files) that can be committed to git. Do NOT produce sub-tasks that are pure investigation, verification, or read-only steps (e.g. "check if this folder exists", "review the existing code", "verify the build passes"). Each description must name specific files and functions to change.
1075
+
974
1076
  Respond with valid JSON only — no markdown fences, no extra text:
975
1077
  {
976
1078
  "subtasks": [
977
- { "title": "Short title for the first new sub-task", "description": "Detailed description of what to implement in this step" },
978
- { "title": "Short title for the second new sub-task", "description": "Detailed description of what to implement in this step" }
1079
+ { "title": "Short title for the first new sub-task", "description": "Detailed description of what to implement in this step, including specific files to change" },
1080
+ { "title": "Short title for the second new sub-task", "description": "Detailed description of what to implement in this step, including specific files to change" }
979
1081
  ]
980
1082
  }
981
1083
 
@@ -1277,6 +1379,137 @@ async function implementThinkingTask(task, branchName, branchExists, config, pro
1277
1379
  }
1278
1380
  logger_1.logger.success(`Thinking task implemented: branch ${branchName} pushed`);
1279
1381
  }
1382
+ async function implementPlanningTask(task, config, provider, runners, _hooks = {}, _vm) {
1383
+ logger_1.logger.info(`Implementing planning task: ${task.name}`);
1384
+ try {
1385
+ await provider.updateStatus(task.id, 'in progress');
1386
+ await provider.postComment(task.id, `${config.commentPrefix} Starting planning mode — analyzing task and drafting sub-tickets`);
1387
+ }
1388
+ catch (err) {
1389
+ logger_1.logger.warn(`Could not update task status: ${err}`);
1390
+ }
1391
+ let context = '';
1392
+ try {
1393
+ const comments = await provider.getComments(task.id);
1394
+ context = await buildConversationContext(task.id, comments, config, runners);
1395
+ }
1396
+ catch {
1397
+ // ignore
1398
+ }
1399
+ const prompt = buildPlanningAnalysisPrompt(task, context);
1400
+ let parsed = null;
1401
+ let previousNotes = '';
1402
+ for (const runner of runners) {
1403
+ if (!runner.isAvailable())
1404
+ continue;
1405
+ logger_1.logger.info(`Running ${runner.name} for planning analysis...`);
1406
+ const result = await runner.run(prompt, previousNotes || undefined);
1407
+ if (!result.success) {
1408
+ logger_1.logger.warn(`${runner.name} planning analysis failed — trying next runner`);
1409
+ previousNotes = `Previous runner (${runner.name}) output:\n${result.output}\nErrors:\n${result.error}`;
1410
+ continue;
1411
+ }
1412
+ parsed = parsePlanningResponse(result.output);
1413
+ if (parsed)
1414
+ break;
1415
+ logger_1.logger.warn(`${runner.name} planning analysis returned unparseable output — trying next runner`);
1416
+ previousNotes = `Previous runner (${runner.name}) produced unparseable output:\n${result.output}`;
1417
+ }
1418
+ if (!parsed) {
1419
+ logger_1.logger.error('Planning produced no sub-tasks');
1420
+ try {
1421
+ await provider.postComment(task.id, `${config.commentPrefix} Planning produced no sub-tasks`);
1422
+ }
1423
+ catch { /* ignore */ }
1424
+ return;
1425
+ }
1426
+ if (parsed.clarification) {
1427
+ try {
1428
+ await provider.postComment(task.id, `${config.commentPrefix} ${parsed.clarification}`);
1429
+ await provider.updateStatus(task.id, getPendingStatus(config));
1430
+ logger_1.logger.info(`Posted planning clarification, set status to ${getPendingStatus(config)}`);
1431
+ }
1432
+ catch (err) {
1433
+ logger_1.logger.warn(`Failed to post clarification or set pending status: ${err instanceof Error ? err.message : err}`);
1434
+ }
1435
+ return;
1436
+ }
1437
+ if (parsed.subtasks.length === 0) {
1438
+ logger_1.logger.error('Planning produced no sub-tasks');
1439
+ try {
1440
+ await provider.postComment(task.id, `${config.commentPrefix} Planning produced no sub-tasks`);
1441
+ }
1442
+ catch { /* ignore */ }
1443
+ return;
1444
+ }
1445
+ const planningTagLower = config.planningTag.toLowerCase();
1446
+ const ticketTags = task.tags.filter((t) => t.toLowerCase() !== planningTagLower);
1447
+ const created = [];
1448
+ const failures = [];
1449
+ for (const draft of parsed.subtasks) {
1450
+ try {
1451
+ const result = await provider.createTask({
1452
+ title: draft.title,
1453
+ description: draft.description,
1454
+ tags: ticketTags,
1455
+ priority: draft.priority,
1456
+ listId: task.sourceListId,
1457
+ });
1458
+ created.push({ title: draft.title, url: result.url, id: result.id });
1459
+ logger_1.logger.success(` Created sub-ticket "${draft.title}" — ${result.url}`);
1460
+ }
1461
+ catch (err) {
1462
+ const message = err instanceof Error ? err.message : String(err);
1463
+ logger_1.logger.warn(` Failed to create sub-ticket "${draft.title}": ${message}`);
1464
+ failures.push({ title: draft.title, error: message });
1465
+ }
1466
+ }
1467
+ const summaryLines = [
1468
+ `${config.commentPrefix} Planning complete — created ${created.length} ticket${created.length === 1 ? '' : 's'}` +
1469
+ (failures.length > 0 ? ` (${failures.length} failed)` : '') +
1470
+ ':',
1471
+ ];
1472
+ if (created.length > 0) {
1473
+ summaryLines.push('');
1474
+ for (const c of created) {
1475
+ summaryLines.push(`- ${c.title} — ${c.url}`);
1476
+ }
1477
+ }
1478
+ if (failures.length > 0) {
1479
+ summaryLines.push('', 'Failed to create:');
1480
+ for (const f of failures) {
1481
+ summaryLines.push(`- ${f.title} — ${f.error}`);
1482
+ }
1483
+ }
1484
+ try {
1485
+ await provider.postComment(task.id, summaryLines.join('\n'));
1486
+ }
1487
+ catch (err) {
1488
+ logger_1.logger.warn(`Failed to post planning summary: ${err instanceof Error ? err.message : err}`);
1489
+ }
1490
+ if (config.planningTag && provider.removeTag) {
1491
+ try {
1492
+ await provider.removeTag(task.id, config.planningTag);
1493
+ }
1494
+ catch (err) {
1495
+ logger_1.logger.warn(`Could not remove planning tag: ${err instanceof Error ? err.message : err}`);
1496
+ }
1497
+ }
1498
+ const doneStatus = await (0, accepted_1.resolveDoneStatus)(config, provider);
1499
+ if (doneStatus) {
1500
+ try {
1501
+ await provider.updateStatus(task.id, doneStatus);
1502
+ logger_1.logger.info(`Planning task transitioned to "${doneStatus}"`);
1503
+ }
1504
+ catch (err) {
1505
+ logger_1.logger.warn(`Failed to transition planning task to "${doneStatus}": ${err instanceof Error ? err.message : err}`);
1506
+ }
1507
+ }
1508
+ else {
1509
+ logger_1.logger.warn('No done status configured or detectable — leaving planning task in current status');
1510
+ }
1511
+ logger_1.logger.success(`Planning task complete: ${task.name}`);
1512
+ }
1280
1513
  /**
1281
1514
  * Attempts to create a PR via `gh` CLI. Falls back to a compare URL if gh is
1282
1515
  * unavailable or PR creation fails.
@@ -1444,7 +1677,15 @@ async function processNonCodeTask(task, filter, config, provider, runners, scree
1444
1677
  return 'skipped';
1445
1678
  }
1446
1679
  }
1447
- await implementNonCodeTask(task, config, provider, runners, hooks, vm);
1680
+ if (isPlanningTask(task, config)) {
1681
+ await implementPlanningTask(task, config, provider, runners, hooks, vm);
1682
+ }
1683
+ else if (isThinkingTask(task, config)) {
1684
+ await implementNonCodeThinkingTask(task, config, provider, runners, hooks, vm);
1685
+ }
1686
+ else {
1687
+ await implementNonCodeTask(task, config, provider, runners, hooks, vm);
1688
+ }
1448
1689
  return 'processed';
1449
1690
  }
1450
1691
  async function implementNonCodeTask(task, config, provider, runners, hooks = {}, vm) {
@@ -1512,6 +1753,267 @@ async function implementNonCodeTask(task, config, provider, runners, hooks = {},
1512
1753
  }
1513
1754
  logger_1.logger.success(`Non-code task complete: ${task.name}`);
1514
1755
  }
1756
+ function buildNonCodeAnalysisPrompt(task, context) {
1757
+ return `You are a senior analyst breaking down a non-code task (research, investigation, documentation, communication, planning, etc.) into smaller, sequential sub-tasks.
1758
+
1759
+ Each sub-task will be executed in order. Each later sub-task will receive a short summary of every earlier sub-task's result, so subsequent steps can build on previous findings.
1760
+
1761
+ Task name: ${task.name}
1762
+
1763
+ Description:
1764
+ ${task.description || '(no description provided)'}
1765
+ ${context}
1766
+
1767
+ Analyze this task and break it into 2-8 focused, sequential sub-tasks. Each sub-task should be a coherent unit of work that produces a textual outcome (analysis, summary, draft, list of findings, etc.) — no code changes are expected.
1768
+
1769
+ Respond with valid JSON only — no markdown fences, no extra text:
1770
+ {
1771
+ "taskSummary": "2-5 short sentences: overall goal and constraints only — reused as compact context for each sub-task",
1772
+ "subtasks": [
1773
+ {
1774
+ "id": 1,
1775
+ "title": "Short title for the sub-task",
1776
+ "description": "Detailed description of what to investigate, produce, or decide in this step"
1777
+ }
1778
+ ]
1779
+ }
1780
+
1781
+ Order sub-tasks by dependency (foundation first). Keep titles short — they will appear in ticket comments.`;
1782
+ }
1783
+ function buildNonCodeSubtaskPrompt(subtask, task, plan, previousResults, reviewContext) {
1784
+ const completedSteps = plan.subtasks
1785
+ .filter((s) => s.status === 'done')
1786
+ .map((s) => ` - [done] ${formatSubtaskId(s.id)} ${s.title}`)
1787
+ .join('\n');
1788
+ const previousSection = previousResults.length > 0
1789
+ ? `\n## Summaries from previous sub-tasks\nUse these findings as context — do not repeat work already done.\n\n${previousResults
1790
+ .map((r) => `### ${formatSubtaskId(r.id)} ${r.title}\n${r.summary}`)
1791
+ .join('\n\n')}\n`
1792
+ : '';
1793
+ const summary = plan.taskSummary?.trim();
1794
+ const goalSection = summary
1795
+ ? `\nGoal (concise):\n${summary}\n`
1796
+ : task.description?.trim()
1797
+ ? `\nTask description:\n${task.description.trim()}\n`
1798
+ : '';
1799
+ return `You are working on step ${formatSubtaskId(subtask.id)} of a multi-step non-code task. No code changes are expected — produce a clear textual response.
1800
+
1801
+ Overall task: ${task.name}${goalSection}${previousSection}${reviewContext || ''}## Progress
1802
+ ${completedSteps || '(no steps completed yet)'}
1803
+
1804
+ ## Current step: ${formatSubtaskId(subtask.id)} ${subtask.title}
1805
+ ${subtask.description}
1806
+
1807
+ Focus ONLY on this step. Write the response so it can be posted directly as a ticket comment — clear, self-contained, and addressed to the task's stakeholders. Do not include preambles like "Here's text you can paste"; output only the content itself.`;
1808
+ }
1809
+ function buildNonCodeThinkingCompletionComment(config) {
1810
+ return [
1811
+ `${config.commentPrefix} Non-code task complete!`,
1812
+ ``,
1813
+ `All sub-tasks finished. Individual summaries were posted above.`,
1814
+ ``,
1815
+ `Status set to: ${getInReviewStatus(config)}`,
1816
+ ].join('\n');
1817
+ }
1818
+ async function analyzeAndPlanNonCode(task, context, runners) {
1819
+ const runner = runners.find((r) => r.isAvailable());
1820
+ if (!runner) {
1821
+ logger_1.logger.error('No AI runner available for non-code task analysis');
1822
+ return null;
1823
+ }
1824
+ const analysisPrompt = buildNonCodeAnalysisPrompt(task, context);
1825
+ logger_1.logger.info('Analyzing non-code task and creating sub-task plan...');
1826
+ const result = await runner.run(analysisPrompt);
1827
+ if (!result.success) {
1828
+ logger_1.logger.error('Non-code task analysis failed');
1829
+ return null;
1830
+ }
1831
+ try {
1832
+ const jsonMatch = result.output.match(/\{[\s\S]*\}/);
1833
+ if (!jsonMatch) {
1834
+ logger_1.logger.error('Could not parse non-code analysis response — no JSON found');
1835
+ return null;
1836
+ }
1837
+ const parsed = JSON.parse(jsonMatch[0]);
1838
+ if (!parsed.subtasks || parsed.subtasks.length === 0) {
1839
+ logger_1.logger.error('Non-code analysis produced no sub-tasks');
1840
+ return null;
1841
+ }
1842
+ const taskSummaryRaw = typeof parsed.taskSummary === 'string' ? parsed.taskSummary.trim() : '';
1843
+ const plan = {
1844
+ taskId: task.id,
1845
+ taskName: task.name,
1846
+ ...(taskSummaryRaw ? { taskSummary: taskSummaryRaw } : {}),
1847
+ subtasks: parsed.subtasks.map((s, i) => ({
1848
+ id: s.id ?? i + 1,
1849
+ title: s.title,
1850
+ description: s.description,
1851
+ status: 'pending',
1852
+ attempts: 0,
1853
+ })),
1854
+ };
1855
+ return plan;
1856
+ }
1857
+ catch (err) {
1858
+ logger_1.logger.error(`Failed to parse non-code analysis response: ${err}`);
1859
+ return null;
1860
+ }
1861
+ }
1862
+ async function implementNonCodeThinkingTask(task, config, provider, runners, hooks = {}, vm) {
1863
+ logger_1.logger.info(`Implementing non-code thinking task: ${task.name}`);
1864
+ try {
1865
+ await provider.updateStatus(task.id, 'in progress');
1866
+ await provider.postComment(task.id, `${config.commentPrefix} Starting non-code task execution (thinking mode — will analyze and break into sub-tasks)`);
1867
+ }
1868
+ catch (err) {
1869
+ logger_1.logger.warn(`Could not update task status: ${err}`);
1870
+ }
1871
+ let context = '';
1872
+ try {
1873
+ const comments = await provider.getComments(task.id);
1874
+ context = await buildConversationContext(task.id, comments, config, runners);
1875
+ }
1876
+ catch {
1877
+ // ignore
1878
+ }
1879
+ const plan = await analyzeAndPlanNonCode(task, context, runners);
1880
+ if (!plan) {
1881
+ logger_1.logger.error('Failed to create non-code task plan');
1882
+ await provider.postComment(task.id, `${config.commentPrefix} Failed to analyze and break down the non-code task. Manual intervention needed.`);
1883
+ return;
1884
+ }
1885
+ logger_1.logger.info(`Non-code task broken into ${plan.subtasks.length} sub-tasks`);
1886
+ try {
1887
+ await provider.postComment(task.id, `${config.commentPrefix} Task analyzed and broken into ${plan.subtasks.length} sub-tasks:\n\n${formatSubtaskList(plan)}`);
1888
+ }
1889
+ catch (err) {
1890
+ logger_1.logger.warn(`Failed to post non-code breakdown comment: ${err}`);
1891
+ }
1892
+ // beforeThinkingTask hook — may adjust subtask titles/descriptions before execution.
1893
+ // branchName is empty because non-code tasks don't create a branch.
1894
+ if (vm) {
1895
+ const thinkCtx = {
1896
+ task,
1897
+ config,
1898
+ branchName: '',
1899
+ subtasks: plan.subtasks.map((s) => ({
1900
+ id: s.id,
1901
+ title: s.title,
1902
+ description: s.description,
1903
+ status: s.status,
1904
+ })),
1905
+ };
1906
+ const modified = await (0, hooks_1.executeHook)(hooks, 'beforeThinkingTask', thinkCtx, vm);
1907
+ const prevById = new Map(plan.subtasks.map((s) => [s.id, s]));
1908
+ plan.subtasks = modified.subtasks.map((s) => {
1909
+ const prev = prevById.get(s.id);
1910
+ return {
1911
+ id: s.id,
1912
+ title: s.title,
1913
+ description: s.description,
1914
+ status: (prev?.status ?? s.status),
1915
+ attempts: prev?.attempts ?? 0,
1916
+ lastError: prev?.lastError,
1917
+ };
1918
+ });
1919
+ }
1920
+ const previousResults = [];
1921
+ let allSucceeded = true;
1922
+ for (const subtask of plan.subtasks) {
1923
+ subtask.status = 'running';
1924
+ subtask.attempts = (subtask.attempts ?? 0) + 1;
1925
+ logger_1.logger.info(` Starting non-code step ${formatSubtaskId(subtask.id)}: ${subtask.title}`);
1926
+ const prompt = buildNonCodeSubtaskPrompt(subtask, task, plan, previousResults, undefined);
1927
+ let summary = '';
1928
+ let success = false;
1929
+ let previousNotes = '';
1930
+ for (const runner of runners) {
1931
+ if (!runner.isAvailable())
1932
+ continue;
1933
+ logger_1.logger.info(` Running ${runner.name} for step ${formatSubtaskId(subtask.id)}...`);
1934
+ const result = await runner.run(prompt, previousNotes || undefined);
1935
+ if (result.success && result.output.trim().length > 0) {
1936
+ summary = result.output.trim();
1937
+ success = true;
1938
+ break;
1939
+ }
1940
+ if (!result.success) {
1941
+ logger_1.logger.warn(` ${runner.name} failed — trying next runner`);
1942
+ previousNotes = `Previous runner (${runner.name}) output:\n${result.output}\nErrors:\n${result.error}`;
1943
+ }
1944
+ else {
1945
+ logger_1.logger.warn(` ${runner.name} produced empty output — trying next runner`);
1946
+ previousNotes = `Previous runner (${runner.name}) produced empty output.`;
1947
+ }
1948
+ }
1949
+ if (!success) {
1950
+ subtask.status = 'failed';
1951
+ subtask.lastError = previousNotes;
1952
+ allSucceeded = false;
1953
+ logger_1.logger.error(` Non-code step ${formatSubtaskId(subtask.id)} failed: ${subtask.title}`);
1954
+ try {
1955
+ await provider.postComment(task.id, `${config.commentPrefix} Step ${formatSubtaskId(subtask.id)} failed: ${subtask.title}\n\n${formatSubtaskList(plan)}`);
1956
+ }
1957
+ catch { /* ignore */ }
1958
+ break;
1959
+ }
1960
+ // Post the sub-task summary BEFORE marking it done — per the task spec, the
1961
+ // comment is posted, then the sub-task is announced as complete.
1962
+ try {
1963
+ await provider.postComment(task.id, `${config.commentPrefix} Step ${formatSubtaskId(subtask.id)}: ${subtask.title}\n\n---\n\n${summary}`);
1964
+ }
1965
+ catch (err) {
1966
+ logger_1.logger.warn(`Failed to post summary for step ${formatSubtaskId(subtask.id)}: ${err}`);
1967
+ }
1968
+ previousResults.push({ id: subtask.id, title: subtask.title, summary });
1969
+ subtask.status = 'done';
1970
+ logger_1.logger.success(` Non-code step ${formatSubtaskId(subtask.id)} complete: ${subtask.title}`);
1971
+ try {
1972
+ await provider.postComment(task.id, `${config.commentPrefix} Step ${formatSubtaskId(subtask.id)} complete: ${subtask.title}\n\n${formatSubtaskList(plan)}`);
1973
+ }
1974
+ catch { /* ignore */ }
1975
+ }
1976
+ if (!allSucceeded) {
1977
+ logger_1.logger.error('Non-code thinking task did not complete all sub-tasks');
1978
+ try {
1979
+ await provider.postComment(task.id, `${config.commentPrefix} Non-code thinking task did not complete all sub-tasks. Manual intervention needed.`);
1980
+ }
1981
+ catch { /* ignore */ }
1982
+ return;
1983
+ }
1984
+ try {
1985
+ await provider.postComment(task.id, buildNonCodeThinkingCompletionComment(config));
1986
+ await provider.updateStatus(task.id, getInReviewStatus(config));
1987
+ if (config.thinkingTag && provider.removeTag) {
1988
+ try {
1989
+ await provider.removeTag(task.id, config.thinkingTag);
1990
+ }
1991
+ catch (err) {
1992
+ logger_1.logger.warn(`Could not remove thinking tag: ${err instanceof Error ? err.message : err}`);
1993
+ }
1994
+ }
1995
+ }
1996
+ catch (err) {
1997
+ logger_1.logger.warn(`Non-code thinking task done but failed to update task: ${err instanceof Error ? err.message : err}`);
1998
+ }
1999
+ // afterThinkingTask hook
2000
+ if (vm) {
2001
+ const afterCtx = {
2002
+ task,
2003
+ config,
2004
+ branchName: '',
2005
+ subtasks: plan.subtasks.map((s) => ({
2006
+ id: s.id,
2007
+ title: s.title,
2008
+ description: s.description,
2009
+ status: s.status,
2010
+ })),
2011
+ success: true,
2012
+ };
2013
+ await (0, hooks_1.executeHook)(hooks, 'afterThinkingTask', afterCtx, vm);
2014
+ }
2015
+ logger_1.logger.success(`Non-code thinking task complete: ${task.name}`);
2016
+ }
1515
2017
  // ─── Review task processing ─────────────────────────────────────────────────
1516
2018
  const REPLY_REGEX = /<!-- AIDEV-REPLY ([\w=+/]+) -->([\s\S]*?)<!-- \/AIDEV-REPLY -->/g;
1517
2019
  function parseReplyDirectives(output) {