task-o-matic 0.0.10 → 0.0.12

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 (117) hide show
  1. package/dist/cli/display/progress.d.ts +1 -1
  2. package/dist/cli/display/progress.d.ts.map +1 -1
  3. package/dist/cli/display/progress.js +16 -13
  4. package/dist/commands/benchmark.js +70 -2
  5. package/dist/commands/init.js +48 -27
  6. package/dist/commands/prd.d.ts.map +1 -1
  7. package/dist/commands/prd.js +201 -0
  8. package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
  9. package/dist/commands/tasks/execute-loop.js +21 -16
  10. package/dist/commands/tasks/execute.d.ts.map +1 -1
  11. package/dist/commands/tasks/execute.js +4 -0
  12. package/dist/commands/workflow.d.ts.map +1 -1
  13. package/dist/commands/workflow.js +56 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/lib/ai-service/ai-operations.d.ts +13 -10
  17. package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
  18. package/dist/lib/ai-service/ai-operations.js +35 -995
  19. package/dist/lib/ai-service/base-operations.d.ts +13 -0
  20. package/dist/lib/ai-service/base-operations.d.ts.map +1 -0
  21. package/dist/lib/ai-service/base-operations.js +79 -0
  22. package/dist/lib/ai-service/documentation-operations.d.ts +18 -0
  23. package/dist/lib/ai-service/documentation-operations.d.ts.map +1 -0
  24. package/dist/lib/ai-service/documentation-operations.js +301 -0
  25. package/dist/lib/ai-service/prd-operations.d.ts +14 -0
  26. package/dist/lib/ai-service/prd-operations.d.ts.map +1 -0
  27. package/dist/lib/ai-service/prd-operations.js +398 -0
  28. package/dist/lib/ai-service/task-operations.d.ts +12 -0
  29. package/dist/lib/ai-service/task-operations.d.ts.map +1 -0
  30. package/dist/lib/ai-service/task-operations.js +225 -0
  31. package/dist/lib/benchmark/registry.d.ts.map +1 -1
  32. package/dist/lib/benchmark/registry.js +127 -0
  33. package/dist/lib/better-t-stack-cli.d.ts +4 -1
  34. package/dist/lib/better-t-stack-cli.d.ts.map +1 -1
  35. package/dist/lib/better-t-stack-cli.js +126 -5
  36. package/dist/lib/config.d.ts +13 -6
  37. package/dist/lib/config.d.ts.map +1 -1
  38. package/dist/lib/config.js +90 -48
  39. package/dist/lib/context-builder.d.ts +13 -1
  40. package/dist/lib/context-builder.d.ts.map +1 -1
  41. package/dist/lib/context-builder.js +68 -36
  42. package/dist/lib/executors/claude-code-executor.d.ts +5 -2
  43. package/dist/lib/executors/claude-code-executor.d.ts.map +1 -1
  44. package/dist/lib/executors/claude-code-executor.js +30 -3
  45. package/dist/lib/executors/codex-executor.d.ts +5 -2
  46. package/dist/lib/executors/codex-executor.d.ts.map +1 -1
  47. package/dist/lib/executors/codex-executor.js +30 -3
  48. package/dist/lib/executors/executor-factory.d.ts +2 -2
  49. package/dist/lib/executors/executor-factory.d.ts.map +1 -1
  50. package/dist/lib/executors/executor-factory.js +5 -5
  51. package/dist/lib/executors/gemini-executor.d.ts +5 -2
  52. package/dist/lib/executors/gemini-executor.d.ts.map +1 -1
  53. package/dist/lib/executors/gemini-executor.js +30 -3
  54. package/dist/lib/executors/opencode-executor.d.ts +5 -2
  55. package/dist/lib/executors/opencode-executor.d.ts.map +1 -1
  56. package/dist/lib/executors/opencode-executor.js +32 -7
  57. package/dist/lib/prompt-builder.d.ts +11 -0
  58. package/dist/lib/prompt-builder.d.ts.map +1 -1
  59. package/dist/lib/prompt-builder.js +60 -0
  60. package/dist/lib/prompt-registry.d.ts.map +1 -1
  61. package/dist/lib/prompt-registry.js +158 -0
  62. package/dist/lib/storage/file-system.d.ts +3 -7
  63. package/dist/lib/storage/file-system.d.ts.map +1 -1
  64. package/dist/lib/storage/file-system.js +50 -230
  65. package/dist/lib/storage/storage-callbacks.d.ts +17 -0
  66. package/dist/lib/storage/storage-callbacks.d.ts.map +1 -0
  67. package/dist/lib/storage/storage-callbacks.js +94 -0
  68. package/dist/lib/task-execution.d.ts.map +1 -1
  69. package/dist/lib/task-execution.js +29 -51
  70. package/dist/lib/task-loop-execution.d.ts.map +1 -1
  71. package/dist/lib/task-loop-execution.js +234 -42
  72. package/dist/prompts/documentation-recap.d.ts +3 -0
  73. package/dist/prompts/documentation-recap.d.ts.map +1 -0
  74. package/dist/prompts/documentation-recap.js +13 -0
  75. package/dist/prompts/index.d.ts +6 -0
  76. package/dist/prompts/index.d.ts.map +1 -1
  77. package/dist/prompts/index.js +6 -0
  78. package/dist/prompts/prd-combination.d.ts +2 -0
  79. package/dist/prompts/prd-combination.d.ts.map +1 -0
  80. package/dist/prompts/prd-combination.js +35 -0
  81. package/dist/prompts/prd-generation.d.ts +2 -0
  82. package/dist/prompts/prd-generation.d.ts.map +1 -0
  83. package/dist/prompts/prd-generation.js +49 -0
  84. package/dist/prompts/prd-question-answer.d.ts +3 -0
  85. package/dist/prompts/prd-question-answer.d.ts.map +1 -0
  86. package/dist/prompts/prd-question-answer.js +27 -0
  87. package/dist/prompts/task-execution.d.ts +3 -0
  88. package/dist/prompts/task-execution.d.ts.map +1 -0
  89. package/dist/prompts/task-execution.js +21 -0
  90. package/dist/prompts/workflow-prompts.d.ts +9 -0
  91. package/dist/prompts/workflow-prompts.d.ts.map +1 -0
  92. package/dist/prompts/workflow-prompts.js +93 -0
  93. package/dist/services/prd.d.ts +43 -0
  94. package/dist/services/prd.d.ts.map +1 -1
  95. package/dist/services/prd.js +121 -0
  96. package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
  97. package/dist/services/workflow-ai-assistant.js +73 -134
  98. package/dist/services/workflow.d.ts +10 -0
  99. package/dist/services/workflow.d.ts.map +1 -1
  100. package/dist/services/workflow.js +118 -40
  101. package/dist/test/integration/callbacks.test.d.ts +2 -0
  102. package/dist/test/integration/callbacks.test.d.ts.map +1 -0
  103. package/dist/test/integration/callbacks.test.js +64 -0
  104. package/dist/types/callbacks.d.ts +9 -6
  105. package/dist/types/callbacks.d.ts.map +1 -1
  106. package/dist/types/index.d.ts +17 -2
  107. package/dist/types/index.d.ts.map +1 -1
  108. package/dist/types/workflow-options.d.ts +7 -0
  109. package/dist/types/workflow-options.d.ts.map +1 -1
  110. package/dist/utils/ai-service-factory.d.ts +15 -1
  111. package/dist/utils/ai-service-factory.d.ts.map +1 -1
  112. package/dist/utils/ai-service-factory.js +29 -1
  113. package/dist/utils/streaming-options.d.ts +1 -1
  114. package/dist/utils/streaming-options.d.ts.map +1 -1
  115. package/dist/utils/streaming-options.js +31 -18
  116. package/docs/agents/cli.md +191 -0
  117. package/package.json +3 -2
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Simple storage callbacks - just read/write key-value pairs
3
+ * This allows the library to be used with ANY storage backend (DB, Redis, S3, etc.)
4
+ */
5
+ export interface StorageCallbacks {
6
+ read: (key: string) => Promise<string | null>;
7
+ write: (key: string, value: string) => Promise<void>;
8
+ delete: (key: string) => Promise<void>;
9
+ list: (prefix?: string) => Promise<string[]>;
10
+ exists: (key: string) => Promise<boolean>;
11
+ }
12
+ /**
13
+ * Default file system callbacks (Node.js environment)
14
+ * Uses fs/promises to implement the storage interface
15
+ */
16
+ export declare function createFileSystemCallbacks(baseDir?: string): StorageCallbacks;
17
+ //# sourceMappingURL=storage-callbacks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-callbacks.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/storage-callbacks.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAE/B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAG9C,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGrD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAG7C,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,MAAsB,GAC9B,gBAAgB,CAwFlB"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFileSystemCallbacks = createFileSystemCallbacks;
4
+ const promises_1 = require("fs/promises");
5
+ const path_1 = require("path");
6
+ const fs_1 = require("fs");
7
+ /**
8
+ * Default file system callbacks (Node.js environment)
9
+ * Uses fs/promises to implement the storage interface
10
+ */
11
+ function createFileSystemCallbacks(baseDir = process.cwd()) {
12
+ const resolvePath = (key) => (0, path_1.join)(baseDir, key);
13
+ const ensureDir = (filePath) => {
14
+ const dir = (0, path_1.dirname)(filePath);
15
+ if (!(0, fs_1.existsSync)(dir)) {
16
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
17
+ }
18
+ };
19
+ return {
20
+ read: async (key) => {
21
+ try {
22
+ const path = resolvePath(key);
23
+ if (!(0, fs_1.existsSync)(path))
24
+ return null;
25
+ return await (0, promises_1.readFile)(path, "utf-8");
26
+ }
27
+ catch (error) {
28
+ if (error.code === "ENOENT")
29
+ return null;
30
+ throw error;
31
+ }
32
+ },
33
+ write: async (key, value) => {
34
+ const path = resolvePath(key);
35
+ ensureDir(path);
36
+ await (0, promises_1.writeFile)(path, value, "utf-8");
37
+ },
38
+ delete: async (key) => {
39
+ try {
40
+ const path = resolvePath(key);
41
+ if ((0, fs_1.existsSync)(path)) {
42
+ await (0, promises_1.unlink)(path);
43
+ }
44
+ }
45
+ catch (error) {
46
+ // Ignore if already gone
47
+ if (error.code !== "ENOENT")
48
+ throw error;
49
+ }
50
+ },
51
+ list: async (prefix) => {
52
+ const searchPath = prefix ? resolvePath(prefix) : baseDir;
53
+ try {
54
+ // If it's a directory, recursively list all files in it
55
+ if ((0, fs_1.existsSync)(searchPath) && (await (0, promises_1.stat)(searchPath)).isDirectory()) {
56
+ const files = [];
57
+ const scan = async (dir, currentPrefix) => {
58
+ const items = await (0, promises_1.readdir)(dir);
59
+ for (const item of items) {
60
+ const fullPath = (0, path_1.join)(dir, item);
61
+ const itemPrefix = currentPrefix
62
+ ? (0, path_1.join)(currentPrefix, item)
63
+ : item;
64
+ if ((await (0, promises_1.stat)(fullPath)).isDirectory()) {
65
+ await scan(fullPath, itemPrefix);
66
+ }
67
+ else {
68
+ files.push(itemPrefix);
69
+ }
70
+ }
71
+ };
72
+ await scan(searchPath, prefix || "");
73
+ return files;
74
+ }
75
+ // If it's a file prefix (e.g. "tasks/task-")
76
+ const dir = (0, path_1.dirname)(searchPath);
77
+ if ((0, fs_1.existsSync)(dir)) {
78
+ const files = await (0, promises_1.readdir)(dir);
79
+ const namePrefix = prefix ? prefix.split("/").pop() || "" : "";
80
+ return files
81
+ .filter((f) => f.startsWith(namePrefix))
82
+ .map((f) => (0, path_1.join)((0, path_1.dirname)(prefix || ""), f));
83
+ }
84
+ return [];
85
+ }
86
+ catch (error) {
87
+ return [];
88
+ }
89
+ },
90
+ exists: async (key) => {
91
+ return (0, fs_1.existsSync)(resolvePath(key));
92
+ },
93
+ };
94
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAgB,MAAM,UAAU,CAAC;AA8L5D,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE5E"}
1
+ {"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAgC,MAAM,UAAU,CAAC;AA0J5E,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgF5E"}
@@ -9,8 +9,9 @@ const executor_factory_1 = require("./executors/executor-factory");
9
9
  const validation_1 = require("./validation");
10
10
  const ai_service_factory_1 = require("../utils/ai-service-factory");
11
11
  const hooks_1 = require("./hooks");
12
+ const prompt_builder_1 = require("./prompt-builder");
12
13
  const chalk_1 = __importDefault(require("chalk"));
13
- async function executeSingleTask(taskId, tool, dry) {
14
+ async function executeSingleTask(taskId, tool, dry, executorConfig) {
14
15
  // Load task
15
16
  const task = await tasks_1.taskService.getTask(taskId);
16
17
  if (!task) {
@@ -20,50 +21,20 @@ async function executeSingleTask(taskId, tool, dry) {
20
21
  // Build comprehensive execution message with full context
21
22
  const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
22
23
  const taskContext = await contextBuilder.buildContext(taskId);
23
- // Build execution message with ALL context
24
- const messageParts = [];
25
- // Add task plan if available
24
+ // Get task plan if available
26
25
  const planData = await tasks_1.taskService.getTaskPlan(taskId);
27
- if (planData) {
28
- messageParts.push(`# Task Plan\n\n${planData.plan}\n`);
26
+ // Build execution prompt using PromptBuilder
27
+ const promptResult = prompt_builder_1.PromptBuilder.buildExecutionPrompt({
28
+ taskTitle: task.title,
29
+ taskDescription: task.description,
30
+ taskPlan: planData?.plan,
31
+ stack: taskContext.stack,
32
+ documentation: taskContext.documentation,
33
+ });
34
+ if (!promptResult.success) {
35
+ throw new Error(`Failed to build execution prompt: ${promptResult.error}`);
29
36
  }
30
- else {
31
- messageParts.push(`# Task: ${task.title}\n\n${task.description || "No description"}\n`);
32
- }
33
- // Add PRD context if available
34
- if (taskContext.prdContent) {
35
- messageParts.push(`\n# Product Requirements Document\n\n${taskContext.prdContent}\n`);
36
- }
37
- // Add stack/technology context
38
- if (taskContext.stack) {
39
- messageParts.push(`\n# Technology Stack\n\n`);
40
- messageParts.push(`- **Project**: ${taskContext.stack.projectName}\n`);
41
- messageParts.push(`- **Frontend**: ${taskContext.stack.frontend}\n`);
42
- messageParts.push(`- **Backend**: ${taskContext.stack.backend}\n`);
43
- if (taskContext.stack.database !== "none") {
44
- messageParts.push(`- **Database**: ${taskContext.stack.database}\n`);
45
- }
46
- if (taskContext.stack.orm !== "none") {
47
- messageParts.push(`- **ORM**: ${taskContext.stack.orm}\n`);
48
- }
49
- messageParts.push(`- **Auth**: ${taskContext.stack.auth}\n`);
50
- if (taskContext.stack.addons.length > 0) {
51
- messageParts.push(`- **Addons**: ${taskContext.stack.addons.join(", ")}\n`);
52
- }
53
- messageParts.push(`- **Package Manager**: ${taskContext.stack.packageManager}\n`);
54
- }
55
- // Add documentation context if available
56
- if (taskContext.documentation) {
57
- messageParts.push(`\n# Documentation Context\n\n`);
58
- messageParts.push(`${taskContext.documentation.recap}\n`);
59
- if (taskContext.documentation.files.length > 0) {
60
- messageParts.push(`\n**Relevant Documentation Files**:\n`);
61
- taskContext.documentation.files.forEach((file) => {
62
- messageParts.push(`- ${file.path}\n`);
63
- });
64
- }
65
- }
66
- const executionMessage = messageParts.join("");
37
+ const executionMessage = promptResult.prompt;
67
38
  if (!dry) {
68
39
  // Update task status to in-progress
69
40
  await tasks_1.taskService.setTaskStatus(taskId, "in-progress");
@@ -73,8 +44,8 @@ async function executeSingleTask(taskId, tool, dry) {
73
44
  await hooks_1.hooks.emit("execution:start", { taskId, tool });
74
45
  try {
75
46
  // Create executor and run
76
- const executor = executor_factory_1.ExecutorFactory.create(tool);
77
- await executor.execute(executionMessage, dry);
47
+ const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
48
+ await executor.execute(executionMessage, dry, executorConfig);
78
49
  if (!dry) {
79
50
  // Update task status to completed
80
51
  await tasks_1.taskService.setTaskStatus(taskId, "completed");
@@ -97,7 +68,7 @@ async function executeSingleTask(taskId, tool, dry) {
97
68
  throw error;
98
69
  }
99
70
  }
100
- async function executeTaskWithSubtasks(taskId, tool, dry) {
71
+ async function executeTaskWithSubtasks(taskId, tool, dry, executorConfig) {
101
72
  const task = await tasks_1.taskService.getTask(taskId);
102
73
  if (!task) {
103
74
  throw new Error(`Task with ID ${taskId} not found`);
@@ -115,7 +86,7 @@ async function executeTaskWithSubtasks(taskId, tool, dry) {
115
86
  const subtask = subtasks[i];
116
87
  console.log(chalk_1.default.cyan(`\n[${i + 1}/${subtasks.length}] Executing subtask: ${subtask.title} (${subtask.id})`));
117
88
  try {
118
- await executeTaskWithSubtasks(subtask.id, tool, dry);
89
+ await executeTaskWithSubtasks(subtask.id, tool, dry, executorConfig);
119
90
  }
120
91
  catch (error) {
121
92
  console.error(chalk_1.default.red(`❌ Failed to execute subtask ${subtask.id}: ${error instanceof Error ? error.message : "Unknown error"}`));
@@ -132,7 +103,14 @@ async function executeTaskWithSubtasks(taskId, tool, dry) {
132
103
  }
133
104
  }
134
105
  async function executeTask(options) {
135
- const { taskId, tool = "opencode", message, dry = false, validate = [], } = options;
106
+ const { taskId, tool = "opencode", message, model, continueSession, dry = false, validate = [], } = options;
107
+ // Build executor config from options
108
+ const executorConfig = model || continueSession
109
+ ? {
110
+ model,
111
+ continueLastSession: continueSession,
112
+ }
113
+ : undefined;
136
114
  // If custom message is provided, execute just this task (ignore subtasks)
137
115
  if (message) {
138
116
  const task = await tasks_1.taskService.getTask(taskId);
@@ -147,8 +125,8 @@ async function executeTask(options) {
147
125
  // Emit execution:start event
148
126
  await hooks_1.hooks.emit("execution:start", { taskId, tool });
149
127
  try {
150
- const executor = executor_factory_1.ExecutorFactory.create(tool);
151
- await executor.execute(message, dry);
128
+ const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
129
+ await executor.execute(message, dry, executorConfig);
152
130
  if (!dry) {
153
131
  await tasks_1.taskService.setTaskStatus(taskId, "completed");
154
132
  console.log(chalk_1.default.green("✅ Task execution completed successfully"));
@@ -173,7 +151,7 @@ async function executeTask(options) {
173
151
  return;
174
152
  }
175
153
  // No custom message - execute recursively with subtasks
176
- await executeTaskWithSubtasks(taskId, tool, dry);
154
+ await executeTaskWithSubtasks(taskId, tool, dry, executorConfig);
177
155
  // Run validations after all subtasks complete
178
156
  await (0, validation_1.runValidations)(validate, dry);
179
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAIlB,MAAM,UAAU,CAAC;AAYlB;;;GAGG;AACH;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE;IACR,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,OAAO,CAAC;CAChC,EACD,MAAM,GAAE,CACN,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAa,EAC5D,KAAK,GAAE,GAAuB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+G/C;AAsXD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CA0K5B"}
1
+ {"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAMlB,MAAM,UAAU,CAAC;AAgBlB;;;GAGG;AACH;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE;IACR,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,OAAO,CAAC;CAChC,EACD,MAAM,GAAE,CACN,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAa,EAC5D,KAAK,GAAE,GAAuB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+G/C;AA6oBD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAmK5B"}
@@ -9,10 +9,13 @@ const tasks_1 = require("../services/tasks");
9
9
  const executor_factory_1 = require("./executors/executor-factory");
10
10
  const ai_service_factory_1 = require("../utils/ai-service-factory");
11
11
  const hooks_1 = require("./hooks");
12
+ const prompt_builder_1 = require("./prompt-builder");
12
13
  const chalk_1 = __importDefault(require("chalk"));
13
14
  const child_process_1 = require("child_process");
14
15
  const util_1 = require("util");
15
16
  const ai_service_factory_2 = require("../utils/ai-service-factory");
17
+ const fs_1 = require("fs");
18
+ const inquirer_1 = __importDefault(require("inquirer"));
16
19
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
17
20
  /**
18
21
  * Extract commit message and file list from AI conversation/output
@@ -152,10 +155,104 @@ async function runVerificationCommands(commands, dry) {
152
155
  /**
153
156
  * Execute a single task with retry logic and error correction
154
157
  */
155
- async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries, dry, tryModels) {
158
+ async function executeTaskWithRetry(task, tool, config, dry) {
159
+ const { maxRetries = 3, verificationCommands = [], tryModels, plan, planModel, reviewPlan, review, reviewModel, autoCommit, } = config;
156
160
  const attempts = [];
157
161
  let currentAttempt = 1;
158
162
  let lastError;
163
+ let planContent;
164
+ // ----------------------------------------------------------------------
165
+ // PLANNING PHASE
166
+ // ----------------------------------------------------------------------
167
+ if (plan) {
168
+ console.log(chalk_1.default.blue.bold(`\n🧠 Starting Planning Phase for Task: ${task.title}`));
169
+ const planFileName = `task-${task.id}-plan.md`;
170
+ const planExecutor = planModel
171
+ ? planModel.split(":")[0]
172
+ : tool;
173
+ const planModelName = planModel ? planModel.split(":")[1] : undefined;
174
+ let planningPrompt = `You are a senior software architect. Analyze the following task and create a detailed implementation plan.
175
+
176
+ Task: ${task.title}
177
+ Description: ${task.description || "No description provided."}
178
+
179
+ Requirements:
180
+ 1. Analyze the task requirements.
181
+ 2. Create a detailed step-by-step implementation plan.
182
+ 3. Identify necessary file changes.
183
+ 4. Write this plan to a file named "${planFileName}" in the current directory.
184
+ 5. Do NOT implement the code yet, just create the plan file.
185
+
186
+ Please create the "${planFileName}" file now.`;
187
+ console.log(chalk_1.default.cyan(` Using executor for planning: ${planExecutor}${planModelName ? ` (${planModelName})` : ""}`));
188
+ // Create executor for planning
189
+ const planningConfig = {
190
+ model: planModelName,
191
+ continueLastSession: false,
192
+ };
193
+ const executor = executor_factory_1.ExecutorFactory.create(planExecutor, planningConfig);
194
+ try {
195
+ let planningComplete = false;
196
+ while (!planningComplete) {
197
+ await executor.execute(planningPrompt, dry, planningConfig);
198
+ if (!dry) {
199
+ // Verify plan file exists and read it
200
+ if ((0, fs_1.existsSync)(planFileName)) {
201
+ planContent = (0, fs_1.readFileSync)(planFileName, "utf-8");
202
+ console.log(chalk_1.default.green(`✅ Plan created successfully: ${planFileName}`));
203
+ // Human Review Loop
204
+ if (reviewPlan) {
205
+ console.log(chalk_1.default.yellow(`\n👀 Pausing for Human Review of the Plan: ${planFileName}`));
206
+ console.log(chalk_1.default.cyan("You can edit the file now."));
207
+ const { feedback } = await inquirer_1.default.prompt([
208
+ {
209
+ type: "input",
210
+ name: "feedback",
211
+ message: "Enter feedback to refine the plan (or press Enter to approve and continue):",
212
+ },
213
+ ]);
214
+ if (feedback && feedback.trim() !== "") {
215
+ console.log(chalk_1.default.blue("🔄 Refining plan based on feedback..."));
216
+ planningPrompt = `The user provided the following feedback on the plan you just created:
217
+
218
+ "${feedback}"
219
+
220
+ Please update the plan file "${planFileName}" to incorporate this feedback.`;
221
+ // Continue loop to regenerate plan
222
+ continue;
223
+ }
224
+ }
225
+ // Auto-commit plan if enabled (only after approval)
226
+ if (autoCommit) {
227
+ try {
228
+ console.log(chalk_1.default.blue(`📦 Staging plan file: ${planFileName}`));
229
+ await execAsync(`git add ${planFileName}`);
230
+ await execAsync(`git commit -m "docs: create implementation plan for task ${task.id}"`);
231
+ console.log(chalk_1.default.green("✅ Plan committed successfully"));
232
+ }
233
+ catch (e) {
234
+ console.warn(chalk_1.default.yellow(`⚠️ Failed to commit plan: ${e instanceof Error ? e.message : "Unknown error"}`));
235
+ }
236
+ }
237
+ planningComplete = true;
238
+ }
239
+ else {
240
+ console.warn(chalk_1.default.yellow(`⚠️ Plan file ${planFileName} was not created by the executor.`));
241
+ planningComplete = true; // Exit loop to avoid infinite retry if file not created
242
+ }
243
+ }
244
+ else {
245
+ planningComplete = true; // Dry run
246
+ }
247
+ }
248
+ }
249
+ catch (error) {
250
+ console.error(chalk_1.default.red(`❌ Planning phase failed: ${error instanceof Error ? error.message : String(error)}`));
251
+ }
252
+ }
253
+ // ----------------------------------------------------------------------
254
+ // EXECUTION PHASE
255
+ // ----------------------------------------------------------------------
159
256
  while (currentAttempt <= maxRetries) {
160
257
  // Determine which executor and model to use for this attempt
161
258
  let currentExecutor = tool;
@@ -194,52 +291,47 @@ async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries
194
291
  // Build execution message with context
195
292
  const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
196
293
  const taskContext = await contextBuilder.buildContext(task.id);
197
- const messageParts = [];
198
- // Add retry context if this is a retry attempt
294
+ // Build retry context if this is a retry attempt
295
+ let retryContext = "";
199
296
  if (currentAttempt > 1 && lastError) {
200
- messageParts.push(`# RETRY ATTEMPT ${currentAttempt}/${maxRetries}\n\n`);
297
+ const retryParts = [];
298
+ retryParts.push(`# RETRY ATTEMPT ${currentAttempt}/${maxRetries}\n\n`);
201
299
  // Add model escalation context
202
300
  if (currentModel) {
203
- messageParts.push(`**Note**: You are ${currentExecutor} using the ${currentModel} model. This is a more capable model than the previous attempt.\n\n`);
301
+ retryParts.push(`**Note**: You are ${currentExecutor} using the ${currentModel} model. This is a more capable model than the previous attempt.\n\n`);
204
302
  }
205
- messageParts.push(`## Previous Attempt Failed With Error:\n\n${lastError}\n\n`);
206
- messageParts.push(`Please analyze the error carefully and fix it. The error might be due to:\n`);
207
- messageParts.push(`- Syntax errors\n`);
208
- messageParts.push(`- Logic errors\n`);
209
- messageParts.push(`- Missing dependencies or imports\n`);
210
- messageParts.push(`- Incorrect configuration\n`);
211
- messageParts.push(`- Build or test failures\n\n`);
212
- messageParts.push(`Please fix the error above and complete the task successfully.\n\n`);
213
- }
214
- // Add task plan if available
215
- const planData = await tasks_1.taskService.getTaskPlan(task.id);
216
- if (planData) {
217
- messageParts.push(`# Task Plan\n\n${planData.plan}\n`);
218
- }
219
- else {
220
- messageParts.push(`# Task: ${task.title}\n\n${task.description || "No description"}\n`);
303
+ retryParts.push(`## Previous Attempt Failed With Error:\n\n${lastError}\n\n`);
304
+ retryParts.push(`Please analyze the error carefully and fix it. The error might be due to:\n`);
305
+ retryParts.push(`- Syntax errors\n`);
306
+ retryParts.push(`- Logic errors\n`);
307
+ retryParts.push(`- Missing dependencies or imports\n`);
308
+ retryParts.push(`- Incorrect configuration\n`);
309
+ retryParts.push(`- Build or test failures\n\n`);
310
+ retryParts.push(`Please fix the error above and complete the task successfully.\n\n`);
311
+ retryContext = retryParts.join("");
221
312
  }
222
- // Add PRD context if available
223
- if (taskContext.prdContent) {
224
- messageParts.push(`\n# Product Requirements Document\n\n${taskContext.prdContent}\n`);
313
+ // Get task plan if available (from planning phase or service)
314
+ const storedPlanData = await tasks_1.taskService.getTaskPlan(task.id);
315
+ let finalPlan;
316
+ if (planContent) {
317
+ finalPlan = `${planContent}\n\nPlease follow this plan to implement the task.`;
225
318
  }
226
- // Add stack/technology context
227
- if (taskContext.stack) {
228
- messageParts.push(`\n# Technology Stack\n\n`);
229
- messageParts.push(`- **Project**: ${taskContext.stack.projectName}\n`);
230
- messageParts.push(`- **Frontend**: ${taskContext.stack.frontend}\n`);
231
- messageParts.push(`- **Backend**: ${taskContext.stack.backend}\n`);
232
- if (taskContext.stack.database !== "none") {
233
- messageParts.push(`- **Database**: ${taskContext.stack.database}\n`);
234
- }
235
- messageParts.push(`- **Auth**: ${taskContext.stack.auth}\n`);
319
+ else if (storedPlanData) {
320
+ finalPlan = storedPlanData.plan;
236
321
  }
237
- // Add documentation context if available
238
- if (taskContext.documentation) {
239
- messageParts.push(`\n# Documentation Context\n\n`);
240
- messageParts.push(`${taskContext.documentation.recap}\n`);
322
+ // Build execution prompt using PromptBuilder
323
+ const promptResult = prompt_builder_1.PromptBuilder.buildExecutionPrompt({
324
+ taskTitle: task.title,
325
+ taskDescription: task.description,
326
+ taskPlan: finalPlan,
327
+ stack: taskContext.stack,
328
+ documentation: taskContext.documentation,
329
+ retryContext,
330
+ });
331
+ if (!promptResult.success) {
332
+ throw new Error(`Failed to build execution prompt: ${promptResult.error}`);
241
333
  }
242
- const executionMessage = messageParts.join("");
334
+ const executionMessage = promptResult.prompt;
243
335
  // Update task status to in-progress
244
336
  if (!dry) {
245
337
  await tasks_1.taskService.setTaskStatus(task.id, "in-progress");
@@ -251,7 +343,16 @@ async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries
251
343
  tool: currentExecutor,
252
344
  });
253
345
  // Create executor and run
254
- const executor = executor_factory_1.ExecutorFactory.create(currentExecutor);
346
+ // Build executor config
347
+ const executorConfig = {
348
+ model: currentModel,
349
+ continueLastSession: currentAttempt > 1, // Resume session on retries
350
+ };
351
+ // Log session resumption
352
+ if (currentAttempt > 1) {
353
+ console.log(chalk_1.default.cyan("🔄 Resuming previous session to provide error feedback to AI"));
354
+ }
355
+ const executor = executor_factory_1.ExecutorFactory.create(currentExecutor, executorConfig);
255
356
  // Add model info to execution message if specified
256
357
  let finalExecutionMessage = executionMessage;
257
358
  if (currentModel) {
@@ -259,7 +360,7 @@ async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries
259
360
  `**Model Configuration**: Using ${currentModel}\n\n` +
260
361
  executionMessage;
261
362
  }
262
- await executor.execute(finalExecutionMessage, dry);
363
+ await executor.execute(finalExecutionMessage, dry, executorConfig);
263
364
  // Run verification commands
264
365
  const verificationResults = await runVerificationCommands(verificationCommands, dry);
265
366
  // Check if all verifications passed
@@ -281,6 +382,97 @@ async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries
281
382
  currentAttempt++;
282
383
  continue;
283
384
  }
385
+ // ----------------------------------------------------------------------
386
+ // AI REVIEW PHASE
387
+ // ----------------------------------------------------------------------
388
+ if (review && !dry) {
389
+ console.log(chalk_1.default.blue.bold("\n🕵️ Starting AI Review Phase..."));
390
+ try {
391
+ // Get git diff
392
+ const { stdout: diff } = await execAsync("git diff HEAD");
393
+ if (!diff.trim()) {
394
+ console.log(chalk_1.default.yellow("⚠️ No changes detected to review."));
395
+ }
396
+ else {
397
+ const reviewExecutor = reviewModel
398
+ ? reviewModel.split(":")[0]
399
+ : tool;
400
+ const reviewModelName = reviewModel
401
+ ? reviewModel.split(":")[1]
402
+ : undefined;
403
+ console.log(chalk_1.default.cyan(` Using executor for review: ${reviewExecutor}${reviewModelName ? ` (${reviewModelName})` : ""}`));
404
+ const reviewPrompt = `You are a strict code reviewer. Review the following changes for the task.
405
+
406
+ Task: ${task.title}
407
+ Plan: ${planContent || "No plan provided."}
408
+
409
+ Git Diff:
410
+ ${diff.substring(0, 10000)}
411
+
412
+ Analyze the changes for:
413
+ 1. Correctness (does it solve the task?)
414
+ 2. Code Quality (clean code, best practices)
415
+ 3. Potential Bugs
416
+
417
+ Return a JSON object:
418
+ {
419
+ "approved": boolean,
420
+ "feedback": "Detailed feedback explaining why it was rejected or approved"
421
+ }
422
+ `;
423
+ const reviewConfig = {
424
+ model: reviewModelName,
425
+ continueLastSession: false,
426
+ };
427
+ // We use a separate executor instance for review to avoid polluting the main context
428
+ // But wait, ExecutorFactory.create returns a new instance usually.
429
+ // However, we need to capture the output which is usually streamed to stdout/file.
430
+ // The current Executor interface doesn't easily return the output string directly if it's designed for side-effects.
431
+ // But we can use getAIOperations().generateText directly for this since it's a simple Q&A.
432
+ const aiOps = (0, ai_service_factory_2.getAIOperations)();
433
+ // We need to construct a proper AI config for getAIOperations
434
+ // This is a bit hacky, ideally we'd use the executor abstraction, but we need the return value.
435
+ // Let's assume we can use the default AI provider for review for now, or try to respect the reviewModel.
436
+ // Actually, let's use the executor but we need to capture its output.
437
+ // The current executor implementation writes to files/stdout.
438
+ // Let's use aiOps directly for the review to get the JSON response.
439
+ const aiResponse = await aiOps.streamText(reviewPrompt);
440
+ const jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
441
+ if (jsonMatch) {
442
+ const reviewResult = JSON.parse(jsonMatch[0]);
443
+ if (!reviewResult.approved) {
444
+ lastError = `AI Review Failed:\n${reviewResult.feedback}`;
445
+ attempts.push({
446
+ attemptNumber: currentAttempt,
447
+ success: false,
448
+ error: lastError,
449
+ executor: currentExecutor,
450
+ model: currentModel,
451
+ verificationResults,
452
+ timestamp: Date.now() - attemptStartTime,
453
+ });
454
+ console.log(chalk_1.default.red(`❌ AI Review Rejected Changes: ${reviewResult.feedback}`));
455
+ // Revert changes? Or just let the next attempt fix them?
456
+ // Usually better to let the next attempt see the bad code and fix it.
457
+ // But we might want to undo if it's really bad.
458
+ // For now, let's keep the changes so the AI can see what it did wrong.
459
+ currentAttempt++;
460
+ continue;
461
+ }
462
+ else {
463
+ console.log(chalk_1.default.green(`✅ AI Review Approved: ${reviewResult.feedback}`));
464
+ }
465
+ }
466
+ else {
467
+ console.warn(chalk_1.default.yellow("⚠️ Could not parse AI review response. Assuming approval."));
468
+ }
469
+ }
470
+ }
471
+ catch (error) {
472
+ console.error(chalk_1.default.red(`❌ AI Review failed: ${error instanceof Error ? error.message : String(error)}`));
473
+ // If review crashes, do we fail the task? Maybe safe to warn and proceed.
474
+ }
475
+ }
284
476
  // Success! Extract commit info
285
477
  let commitInfo;
286
478
  if (!dry) {
@@ -412,7 +604,7 @@ async function executeTaskLoop(options) {
412
604
  const task = tasksToExecute[i];
413
605
  console.log(chalk_1.default.blue.bold(`\n${"=".repeat(60)}\n📌 Task ${i + 1}/${tasksToExecute.length}: ${task.title} (${task.id})\n${"=".repeat(60)}\n`));
414
606
  // Execute task with retry logic
415
- const attempts = await executeTaskWithRetry(task, tool, verificationCommands, maxRetries, dry, config.tryModels);
607
+ const attempts = await executeTaskWithRetry(task, tool, config, dry);
416
608
  // Check if task succeeded
417
609
  const lastAttempt = attempts[attempts.length - 1];
418
610
  const succeeded = lastAttempt.success;
@@ -0,0 +1,3 @@
1
+ export declare const DOCUMENTATION_RECAP_PROMPT = "Create a concise recap of the documentation fetched for these libraries:\n\nLibraries:\n{LIBRARIES_LIST}\n\nDocumentation Contents:\n{DOCUMENTATION_CONTENTS}\n\nPlease provide a 2-3 sentence summary of what documentation is available and how it relates to the task.";
2
+ export declare const DOCUMENTATION_RECAP_SYSTEM_PROMPT = "You are a technical writer who creates concise summaries of documentation collections.";
3
+ //# sourceMappingURL=documentation-recap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentation-recap.d.ts","sourceRoot":"","sources":["../../src/prompts/documentation-recap.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,8QAQkE,CAAC;AAE1G,eAAO,MAAM,iCAAiC,2FAA2F,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DOCUMENTATION_RECAP_SYSTEM_PROMPT = exports.DOCUMENTATION_RECAP_PROMPT = void 0;
4
+ exports.DOCUMENTATION_RECAP_PROMPT = `Create a concise recap of the documentation fetched for these libraries:
5
+
6
+ Libraries:
7
+ {LIBRARIES_LIST}
8
+
9
+ Documentation Contents:
10
+ {DOCUMENTATION_CONTENTS}
11
+
12
+ Please provide a 2-3 sentence summary of what documentation is available and how it relates to the task.`;
13
+ exports.DOCUMENTATION_RECAP_SYSTEM_PROMPT = `You are a technical writer who creates concise summaries of documentation collections.`;
@@ -4,5 +4,11 @@ export * from "./task-enhancement";
4
4
  export * from "./prd-rework";
5
5
  export * from "./documentation-detection";
6
6
  export * from "./task-planning";
7
+ export * from "./task-execution";
7
8
  export * from "./prd-question";
9
+ export * from "./prd-generation";
10
+ export * from "./prd-combination";
11
+ export * from "./documentation-recap";
12
+ export * from "./prd-question-answer";
13
+ export * from "./workflow-prompts";
8
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}