ai-sdlc 0.2.0 → 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/dist/agents/implementation.d.ts +30 -1
- package/dist/agents/implementation.d.ts.map +1 -1
- package/dist/agents/implementation.js +172 -17
- package/dist/agents/implementation.js.map +1 -1
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +61 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/orchestrator.js +443 -0
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/planning.d.ts +1 -1
- package/dist/agents/planning.d.ts.map +1 -1
- package/dist/agents/planning.js +33 -1
- package/dist/agents/planning.js.map +1 -1
- package/dist/agents/review.d.ts +37 -1
- package/dist/agents/review.d.ts.map +1 -1
- package/dist/agents/review.js +319 -44
- package/dist/agents/review.js.map +1 -1
- package/dist/agents/rework.d.ts.map +1 -1
- package/dist/agents/rework.js +3 -1
- package/dist/agents/rework.js.map +1 -1
- package/dist/agents/single-task.d.ts +41 -0
- package/dist/agents/single-task.d.ts.map +1 -0
- package/dist/agents/single-task.js +357 -0
- package/dist/agents/single-task.js.map +1 -0
- package/dist/agents/verification.d.ts.map +1 -1
- package/dist/agents/verification.js +26 -12
- package/dist/agents/verification.js.map +1 -1
- package/dist/cli/commands.d.ts +7 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +560 -48
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +5 -0
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +20 -9
- package/dist/cli/runner.js.map +1 -1
- package/dist/core/client.d.ts +19 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +191 -5
- package/dist/core/client.js.map +1 -1
- package/dist/core/config.d.ts +9 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +51 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/llm-utils.d.ts +103 -0
- package/dist/core/llm-utils.d.ts.map +1 -0
- package/dist/core/llm-utils.js +368 -0
- package/dist/core/llm-utils.js.map +1 -0
- package/dist/core/process-manager.d.ts +15 -0
- package/dist/core/process-manager.d.ts.map +1 -0
- package/dist/core/process-manager.js +132 -0
- package/dist/core/process-manager.js.map +1 -0
- package/dist/core/story.d.ts +35 -1
- package/dist/core/story.d.ts.map +1 -1
- package/dist/core/story.js +107 -1
- package/dist/core/story.js.map +1 -1
- package/dist/core/task-parser.d.ts +59 -0
- package/dist/core/task-parser.d.ts.map +1 -0
- package/dist/core/task-parser.js +235 -0
- package/dist/core/task-parser.js.map +1 -0
- package/dist/core/task-progress.d.ts +92 -0
- package/dist/core/task-progress.d.ts.map +1 -0
- package/dist/core/task-progress.js +280 -0
- package/dist/core/task-progress.js.map +1 -0
- package/dist/core/worktree.d.ts +111 -2
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +310 -2
- package/dist/core/worktree.js.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/services/error-classifier.d.ts +119 -0
- package/dist/services/error-classifier.d.ts.map +1 -0
- package/dist/services/error-classifier.js +182 -0
- package/dist/services/error-classifier.js.map +1 -0
- package/dist/types/index.d.ts +230 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -2
- package/templates/story.md +5 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { getLogger } from './logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts structured tasks from implementation plan markdown content.
|
|
4
|
+
* Returns empty array if no tasks section found (logs warning).
|
|
5
|
+
* Skips malformed tasks with warnings, continues parsing.
|
|
6
|
+
*
|
|
7
|
+
* @param content - Markdown content containing implementation tasks
|
|
8
|
+
* @returns Array of parsed implementation tasks
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const content = `
|
|
13
|
+
* ## Implementation Tasks
|
|
14
|
+
* - [ ] **T1**: Create auth service
|
|
15
|
+
* - Files: \`src/auth.ts\`
|
|
16
|
+
* - Dependencies: none
|
|
17
|
+
* `;
|
|
18
|
+
* const tasks = parseImplementationTasks(content);
|
|
19
|
+
* // => [{ id: 'T1', description: 'Create auth service', ... }]
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function parseImplementationTasks(content) {
|
|
23
|
+
const logger = getLogger();
|
|
24
|
+
const tasks = [];
|
|
25
|
+
// Find the Implementation Tasks section
|
|
26
|
+
const sectionMatch = content.match(/##\s+Implementation\s+Tasks/i);
|
|
27
|
+
if (!sectionMatch) {
|
|
28
|
+
logger.warn('task-parser', 'No Implementation Tasks section found in content');
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
// Extract content after the Implementation Tasks header
|
|
32
|
+
const startIndex = sectionMatch.index + sectionMatch[0].length;
|
|
33
|
+
const remainingContent = content.substring(startIndex);
|
|
34
|
+
// Split into lines for processing
|
|
35
|
+
const lines = remainingContent.split('\n');
|
|
36
|
+
let currentTask = null;
|
|
37
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38
|
+
const line = lines[i];
|
|
39
|
+
// Stop if we hit another h2 header
|
|
40
|
+
if (line.match(/^##\s+/)) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
// Match task line: - [ ] or - [x] followed by **T{n}**: description
|
|
44
|
+
const taskMatch = line.match(/^-\s+\[([ x])\]\s+\*\*T(\d+)\*\*:\s*(.+)$/i);
|
|
45
|
+
if (taskMatch) {
|
|
46
|
+
// Save previous task if exists
|
|
47
|
+
if (currentTask) {
|
|
48
|
+
tasks.push(currentTask);
|
|
49
|
+
}
|
|
50
|
+
const [, checkbox, taskNum, description] = taskMatch;
|
|
51
|
+
const status = checkbox.toLowerCase() === 'x' ? 'completed' : 'pending';
|
|
52
|
+
currentTask = {
|
|
53
|
+
id: `T${taskNum}`,
|
|
54
|
+
description: description.trim(),
|
|
55
|
+
status,
|
|
56
|
+
};
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
// Match metadata lines (indented with - Field: value)
|
|
60
|
+
if (currentTask) {
|
|
61
|
+
// Match metadata with flexible indentation (spaces or tabs)
|
|
62
|
+
const metadataMatch = line.match(/^\s+-\s+(files|dependencies):\s*(.*)$/i);
|
|
63
|
+
if (metadataMatch) {
|
|
64
|
+
const [, fieldName, value] = metadataMatch;
|
|
65
|
+
const field = fieldName.toLowerCase();
|
|
66
|
+
const trimmedValue = value.trim();
|
|
67
|
+
if (field === 'files') {
|
|
68
|
+
if (trimmedValue) {
|
|
69
|
+
// Split by comma, trim, remove backticks
|
|
70
|
+
currentTask.files = trimmedValue
|
|
71
|
+
.split(',')
|
|
72
|
+
.map(f => f.trim().replace(/`/g, ''))
|
|
73
|
+
.filter(f => f.length > 0);
|
|
74
|
+
if (currentTask.files.length === 0) {
|
|
75
|
+
currentTask.files = undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (field === 'dependencies') {
|
|
80
|
+
if (trimmedValue && trimmedValue.toLowerCase() !== 'none') {
|
|
81
|
+
// Split by comma and trim
|
|
82
|
+
currentTask.dependencies = trimmedValue
|
|
83
|
+
.split(',')
|
|
84
|
+
.map(d => d.trim())
|
|
85
|
+
.filter(d => d.length > 0);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// "none" or empty means no dependencies
|
|
89
|
+
currentTask.dependencies = [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Don't forget the last task
|
|
96
|
+
if (currentTask) {
|
|
97
|
+
tasks.push(currentTask);
|
|
98
|
+
}
|
|
99
|
+
logger.debug('task-parser', `Parsed ${tasks.length} tasks from content`);
|
|
100
|
+
return tasks;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Converts task objects back to markdown format.
|
|
104
|
+
* Useful for updating plan files after task status changes.
|
|
105
|
+
*
|
|
106
|
+
* @param tasks - Array of implementation tasks to format
|
|
107
|
+
* @returns Markdown string representing the tasks
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const tasks = [
|
|
112
|
+
* { id: 'T1', description: 'Create service', status: 'pending', files: ['src/service.ts'] }
|
|
113
|
+
* ];
|
|
114
|
+
* const markdown = formatImplementationTasks(tasks);
|
|
115
|
+
* // => "## Implementation Tasks\n\n- [ ] **T1**: Create service\n - Files: `src/service.ts`"
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function formatImplementationTasks(tasks) {
|
|
119
|
+
const lines = ['## Implementation Tasks', ''];
|
|
120
|
+
for (const task of tasks) {
|
|
121
|
+
// Map status to checkbox
|
|
122
|
+
const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
123
|
+
// Task line
|
|
124
|
+
lines.push(`- ${checkbox} **${task.id}**: ${task.description}`);
|
|
125
|
+
// Files metadata (if present)
|
|
126
|
+
if (task.files !== undefined) {
|
|
127
|
+
const filesStr = task.files.map(f => `\`${f}\``).join(', ');
|
|
128
|
+
lines.push(` - Files: ${filesStr}`);
|
|
129
|
+
}
|
|
130
|
+
// Dependencies metadata (if present)
|
|
131
|
+
if (task.dependencies !== undefined) {
|
|
132
|
+
const depsStr = task.dependencies.length === 0 ? 'none' : task.dependencies.join(', ');
|
|
133
|
+
lines.push(` - Dependencies: ${depsStr}`);
|
|
134
|
+
}
|
|
135
|
+
// Blank line between tasks
|
|
136
|
+
lines.push('');
|
|
137
|
+
}
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validates task format and dependencies.
|
|
142
|
+
* Checks for circular dependencies and missing references.
|
|
143
|
+
*
|
|
144
|
+
* @param content - Markdown content containing implementation tasks
|
|
145
|
+
* @returns Validation result with errors and warnings
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const result = validateTaskFormat(content);
|
|
150
|
+
* if (!result.valid) {
|
|
151
|
+
* console.error('Validation errors:', result.errors);
|
|
152
|
+
* }
|
|
153
|
+
* if (result.warnings.length > 0) {
|
|
154
|
+
* console.warn('Validation warnings:', result.warnings);
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export function validateTaskFormat(content) {
|
|
159
|
+
const tasks = parseImplementationTasks(content);
|
|
160
|
+
const errors = [];
|
|
161
|
+
const warnings = [];
|
|
162
|
+
// Build a set of all task IDs for reference checking
|
|
163
|
+
const taskIds = new Set(tasks.map(t => t.id));
|
|
164
|
+
// Check for missing dependency references
|
|
165
|
+
for (const task of tasks) {
|
|
166
|
+
if (task.dependencies) {
|
|
167
|
+
for (const depId of task.dependencies) {
|
|
168
|
+
if (!taskIds.has(depId)) {
|
|
169
|
+
warnings.push(`Task ${task.id} depends on ${depId}, which does not exist`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check for circular dependencies using DFS
|
|
175
|
+
const circularErrors = detectCircularDependencies(tasks);
|
|
176
|
+
errors.push(...circularErrors);
|
|
177
|
+
return {
|
|
178
|
+
valid: errors.length === 0,
|
|
179
|
+
errors,
|
|
180
|
+
warnings,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Detects circular dependencies in the task graph using depth-first search.
|
|
185
|
+
* Returns an array of error messages describing any circular dependencies found.
|
|
186
|
+
*
|
|
187
|
+
* Algorithm: O(V + E) where V = number of tasks, E = number of dependency edges
|
|
188
|
+
*
|
|
189
|
+
* @param tasks - Array of tasks to check
|
|
190
|
+
* @returns Array of error messages (empty if no circular dependencies)
|
|
191
|
+
*/
|
|
192
|
+
function detectCircularDependencies(tasks) {
|
|
193
|
+
const errors = [];
|
|
194
|
+
// Build adjacency list: task ID -> array of task IDs it depends on
|
|
195
|
+
const graph = new Map();
|
|
196
|
+
for (const task of tasks) {
|
|
197
|
+
graph.set(task.id, task.dependencies || []);
|
|
198
|
+
}
|
|
199
|
+
// Track visited nodes and recursion stack
|
|
200
|
+
const visited = new Set();
|
|
201
|
+
const recStack = new Set();
|
|
202
|
+
function dfs(taskId, path) {
|
|
203
|
+
if (recStack.has(taskId)) {
|
|
204
|
+
// Found a cycle - build error message
|
|
205
|
+
const cycleStart = path.indexOf(taskId);
|
|
206
|
+
const cycle = [...path.slice(cycleStart), taskId];
|
|
207
|
+
errors.push(`Circular dependency detected: ${cycle.join(' → ')}`);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
if (visited.has(taskId)) {
|
|
211
|
+
return false; // Already processed this node
|
|
212
|
+
}
|
|
213
|
+
visited.add(taskId);
|
|
214
|
+
recStack.add(taskId);
|
|
215
|
+
const newPath = [...path, taskId];
|
|
216
|
+
const dependencies = graph.get(taskId) || [];
|
|
217
|
+
for (const depId of dependencies) {
|
|
218
|
+
if (dfs(depId, newPath)) {
|
|
219
|
+
// Propagate cycle detection up (but only report once)
|
|
220
|
+
recStack.delete(taskId);
|
|
221
|
+
return false; // Don't report multiple times
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
recStack.delete(taskId);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
// Check each task as potential starting point
|
|
228
|
+
for (const task of tasks) {
|
|
229
|
+
if (!visited.has(task.id)) {
|
|
230
|
+
dfs(task.id, []);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return errors;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=task-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-parser.js","sourceRoot":"","sources":["../../src/core/task-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAyB,EAAE,CAAC;IAEvC,wCAAwC;IACxC,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACnE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,kDAAkD,CAAC,CAAC;QAC/E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,wDAAwD;IACxD,MAAM,UAAU,GAAG,YAAY,CAAC,KAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAChE,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAEvD,kCAAkC;IAClC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,WAAW,GAA8B,IAAI,CAAC;IAElD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtB,mCAAmC;QACnC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM;QACR,CAAC;QAED,oEAAoE;QACpE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC3E,IAAI,SAAS,EAAE,CAAC;YACd,+BAA+B;YAC/B,IAAI,WAAW,EAAE,CAAC;gBAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,CAAC;YAED,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC;YACrD,MAAM,MAAM,GAAe,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;YAEpF,WAAW,GAAG;gBACZ,EAAE,EAAE,IAAI,OAAO,EAAE;gBACjB,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE;gBAC/B,MAAM;aACP,CAAC;YACF,SAAS;QACX,CAAC;QAED,sDAAsD;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,4DAA4D;YAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC3E,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,GAAG,aAAa,CAAC;gBAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;gBACtC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;gBAElC,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;oBACtB,IAAI,YAAY,EAAE,CAAC;wBACjB,yCAAyC;wBACzC,WAAW,CAAC,KAAK,GAAG,YAAY;6BAC7B,KAAK,CAAC,GAAG,CAAC;6BACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;6BACpC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;wBAE7B,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;4BACnC,WAAW,CAAC,KAAK,GAAG,SAAS,CAAC;wBAChC,CAAC;oBACH,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;oBACpC,IAAI,YAAY,IAAI,YAAY,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;wBAC1D,0BAA0B;wBAC1B,WAAW,CAAC,YAAY,GAAG,YAAY;6BACpC,KAAK,CAAC,GAAG,CAAC;6BACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;6BAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBAC/B,CAAC;yBAAM,CAAC;wBACN,wCAAwC;wBACxC,WAAW,CAAC,YAAY,GAAG,EAAE,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,qBAAqB,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAA2B;IACnE,MAAM,KAAK,GAAa,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC;IAExD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,yBAAyB;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAE7D,YAAY;QACZ,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,MAAM,IAAI,CAAC,EAAE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAEhE,8BAA8B;QAC9B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;QACvC,CAAC;QAED,qCAAqC;QACrC,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvF,KAAK,CAAC,IAAI,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,2BAA2B;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,KAAK,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,qDAAqD;IACrD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9C,0CAA0C;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,QAAQ,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,eAAe,KAAK,wBAAwB,CAAC,CAAC;gBAC7E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,MAAM,cAAc,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;IAE/B,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,0BAA0B,CAAC,KAA2B;IAC7D,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,mEAAmE;IACnE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,0CAA0C;IAC1C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,SAAS,GAAG,CAAC,MAAc,EAAE,IAAc;QACzC,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,sCAAsC;YACtC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,iCAAiC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC,CAAC,8BAA8B;QAC9C,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QAElC,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC7C,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;gBACxB,sDAAsD;gBACtD,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACxB,OAAO,KAAK,CAAC,CAAC,8BAA8B;YAC9C,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8CAA8C;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task progress tracking for resumable implementations
|
|
3
|
+
*
|
|
4
|
+
* Persists task-level progress in story files as markdown tables,
|
|
5
|
+
* enabling orchestrator to resume from last completed task after interruptions.
|
|
6
|
+
*/
|
|
7
|
+
import { TaskProgress, TaskStatus } from '../types/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parse progress table from markdown content
|
|
10
|
+
*
|
|
11
|
+
* Expected format:
|
|
12
|
+
* ## Task Progress
|
|
13
|
+
*
|
|
14
|
+
* | Task | Status | Started | Completed |
|
|
15
|
+
* |------|--------|---------|-----------|
|
|
16
|
+
* | T1 | completed | 2026-01-16T10:00:00Z | 2026-01-16T10:05:00Z |
|
|
17
|
+
* | T2 | in_progress | 2026-01-16T10:05:30Z | - |
|
|
18
|
+
*
|
|
19
|
+
* @param content - Story file markdown content
|
|
20
|
+
* @returns Array of TaskProgress objects (empty if section missing or corrupted)
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseProgressTable(content: string): TaskProgress[];
|
|
23
|
+
/**
|
|
24
|
+
* Generate markdown table from TaskProgress array
|
|
25
|
+
*
|
|
26
|
+
* @param progress - Array of task progress entries
|
|
27
|
+
* @returns Markdown table string
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateProgressTable(progress: TaskProgress[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* Read story file content
|
|
32
|
+
*
|
|
33
|
+
* @param storyPath - Absolute path to story.md file
|
|
34
|
+
* @returns Story file content
|
|
35
|
+
* @throws Error if file doesn't exist or can't be read
|
|
36
|
+
*/
|
|
37
|
+
export declare function readStoryFile(storyPath: string): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Write story file content atomically with retry logic
|
|
40
|
+
*
|
|
41
|
+
* @param storyPath - Absolute path to story.md file
|
|
42
|
+
* @param content - New story content
|
|
43
|
+
* @throws Error after all retries exhausted
|
|
44
|
+
*/
|
|
45
|
+
export declare function writeStoryFile(storyPath: string, content: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Get all task progress entries from story file
|
|
48
|
+
*
|
|
49
|
+
* @param storyPath - Absolute path to story.md file
|
|
50
|
+
* @returns Array of TaskProgress objects (empty if section missing)
|
|
51
|
+
*/
|
|
52
|
+
export declare function getTaskProgress(storyPath: string): Promise<TaskProgress[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Initialize progress tracking for a list of tasks
|
|
55
|
+
*
|
|
56
|
+
* Creates "## Task Progress" section with all tasks in 'pending' status.
|
|
57
|
+
* If section already exists, logs warning and skips initialization.
|
|
58
|
+
*
|
|
59
|
+
* @param storyPath - Absolute path to story.md file
|
|
60
|
+
* @param taskIds - Array of task IDs (e.g., ['T1', 'T2', 'T3'])
|
|
61
|
+
*/
|
|
62
|
+
export declare function initializeTaskProgress(storyPath: string, taskIds: string[]): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Update a specific task's status and timestamps
|
|
65
|
+
*
|
|
66
|
+
* Modifies the task row in the progress table and writes back to disk atomically.
|
|
67
|
+
* Sets timestamps based on status transitions:
|
|
68
|
+
* - 'in_progress': Sets startedAt (if not already set)
|
|
69
|
+
* - 'completed' or 'failed': Sets completedAt
|
|
70
|
+
*
|
|
71
|
+
* @param storyPath - Absolute path to story.md file
|
|
72
|
+
* @param taskId - Task ID to update (e.g., 'T1')
|
|
73
|
+
* @param status - New status
|
|
74
|
+
* @param error - Optional error message (only used with 'failed' status)
|
|
75
|
+
* @throws Error if task not found in progress table
|
|
76
|
+
*/
|
|
77
|
+
export declare function updateTaskProgress(storyPath: string, taskId: string, status: TaskStatus, error?: string): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Get list of task IDs with status 'pending'
|
|
80
|
+
*
|
|
81
|
+
* @param storyPath - Absolute path to story.md file
|
|
82
|
+
* @returns Array of pending task IDs
|
|
83
|
+
*/
|
|
84
|
+
export declare function getPendingTasks(storyPath: string): Promise<string[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Get the task ID currently in 'in_progress' status
|
|
87
|
+
*
|
|
88
|
+
* @param storyPath - Absolute path to story.md file
|
|
89
|
+
* @returns Task ID or null if no task is in progress
|
|
90
|
+
*/
|
|
91
|
+
export declare function getCurrentTask(storyPath: string): Promise<string | null>;
|
|
92
|
+
//# sourceMappingURL=task-progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-progress.d.ts","sourceRoot":"","sources":["../../src/core/task-progress.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI7D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAiFlE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAetE;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYtE;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BtF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGhF;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAmDf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAK1E;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAI9E"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task progress tracking for resumable implementations
|
|
3
|
+
*
|
|
4
|
+
* Persists task-level progress in story files as markdown tables,
|
|
5
|
+
* enabling orchestrator to resume from last completed task after interruptions.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import writeFileAtomic from 'write-file-atomic';
|
|
10
|
+
const TASK_PROGRESS_SECTION = '## Task Progress';
|
|
11
|
+
/**
|
|
12
|
+
* Parse progress table from markdown content
|
|
13
|
+
*
|
|
14
|
+
* Expected format:
|
|
15
|
+
* ## Task Progress
|
|
16
|
+
*
|
|
17
|
+
* | Task | Status | Started | Completed |
|
|
18
|
+
* |------|--------|---------|-----------|
|
|
19
|
+
* | T1 | completed | 2026-01-16T10:00:00Z | 2026-01-16T10:05:00Z |
|
|
20
|
+
* | T2 | in_progress | 2026-01-16T10:05:30Z | - |
|
|
21
|
+
*
|
|
22
|
+
* @param content - Story file markdown content
|
|
23
|
+
* @returns Array of TaskProgress objects (empty if section missing or corrupted)
|
|
24
|
+
*/
|
|
25
|
+
export function parseProgressTable(content) {
|
|
26
|
+
const sectionIndex = content.indexOf(TASK_PROGRESS_SECTION);
|
|
27
|
+
if (sectionIndex === -1) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
// Find the table rows (skip header and separator)
|
|
31
|
+
const afterSection = content.slice(sectionIndex);
|
|
32
|
+
const lines = afterSection.split('\n');
|
|
33
|
+
const taskProgress = [];
|
|
34
|
+
let foundTable = false;
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
// Skip section header, empty lines, and markdown table separator
|
|
38
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.match(/^\|[\s-:|]+\|$/)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Stop at next section
|
|
42
|
+
if (trimmed.startsWith('##') && !trimmed.includes(TASK_PROGRESS_SECTION)) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
// Parse table row
|
|
46
|
+
if (trimmed.startsWith('|')) {
|
|
47
|
+
foundTable = true;
|
|
48
|
+
const cells = trimmed
|
|
49
|
+
.split('|')
|
|
50
|
+
.map(cell => cell.trim())
|
|
51
|
+
.filter(cell => cell.length > 0);
|
|
52
|
+
// Skip header row
|
|
53
|
+
if (cells[0] === 'Task' || cells[1] === 'Status') {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Validate we have at least 4 columns
|
|
57
|
+
if (cells.length < 4) {
|
|
58
|
+
console.warn(`[task-progress] Skipping malformed table row: ${trimmed}`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const [taskId, statusStr, startedStr, completedStr] = cells;
|
|
62
|
+
// Validate status
|
|
63
|
+
const validStatuses = ['pending', 'in_progress', 'completed', 'failed'];
|
|
64
|
+
if (!validStatuses.includes(statusStr)) {
|
|
65
|
+
console.warn(`[task-progress] Invalid status '${statusStr}' for task ${taskId}, skipping row`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const progress = {
|
|
69
|
+
taskId,
|
|
70
|
+
status: statusStr,
|
|
71
|
+
};
|
|
72
|
+
// Parse timestamps (handle "-" as missing)
|
|
73
|
+
if (startedStr && startedStr !== '-') {
|
|
74
|
+
progress.startedAt = startedStr;
|
|
75
|
+
}
|
|
76
|
+
if (completedStr && completedStr !== '-') {
|
|
77
|
+
progress.completedAt = completedStr;
|
|
78
|
+
}
|
|
79
|
+
// Handle error column if present (5th column)
|
|
80
|
+
if (cells.length >= 5 && cells[4] && cells[4] !== '-') {
|
|
81
|
+
progress.error = cells[4];
|
|
82
|
+
}
|
|
83
|
+
taskProgress.push(progress);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (foundTable && taskProgress.length === 0) {
|
|
87
|
+
console.warn('[task-progress] Found progress table but no valid task rows, table may be corrupted');
|
|
88
|
+
}
|
|
89
|
+
return taskProgress;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Generate markdown table from TaskProgress array
|
|
93
|
+
*
|
|
94
|
+
* @param progress - Array of task progress entries
|
|
95
|
+
* @returns Markdown table string
|
|
96
|
+
*/
|
|
97
|
+
export function generateProgressTable(progress) {
|
|
98
|
+
const rows = [];
|
|
99
|
+
// Header
|
|
100
|
+
rows.push('| Task | Status | Started | Completed |');
|
|
101
|
+
rows.push('|------|--------|---------|-----------|');
|
|
102
|
+
// Task rows
|
|
103
|
+
for (const task of progress) {
|
|
104
|
+
const started = task.startedAt || '-';
|
|
105
|
+
const completed = task.completedAt || '-';
|
|
106
|
+
rows.push(`| ${task.taskId} | ${task.status} | ${started} | ${completed} |`);
|
|
107
|
+
}
|
|
108
|
+
return rows.join('\n');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Read story file content
|
|
112
|
+
*
|
|
113
|
+
* @param storyPath - Absolute path to story.md file
|
|
114
|
+
* @returns Story file content
|
|
115
|
+
* @throws Error if file doesn't exist or can't be read
|
|
116
|
+
*/
|
|
117
|
+
export async function readStoryFile(storyPath) {
|
|
118
|
+
try {
|
|
119
|
+
return await fs.promises.readFile(storyPath, 'utf-8');
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error.code === 'ENOENT') {
|
|
123
|
+
throw new Error(`Story file not found: ${path.basename(storyPath)}`);
|
|
124
|
+
}
|
|
125
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
126
|
+
throw new Error(`Permission denied reading story file: ${path.basename(storyPath)}`);
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`Failed to read story file: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Write story file content atomically with retry logic
|
|
133
|
+
*
|
|
134
|
+
* @param storyPath - Absolute path to story.md file
|
|
135
|
+
* @param content - New story content
|
|
136
|
+
* @throws Error after all retries exhausted
|
|
137
|
+
*/
|
|
138
|
+
export async function writeStoryFile(storyPath, content) {
|
|
139
|
+
const maxRetries = 3;
|
|
140
|
+
const retryDelays = [100, 200, 400]; // Exponential backoff
|
|
141
|
+
// Ensure parent directory exists first (outside retry loop)
|
|
142
|
+
const storyDir = path.dirname(storyPath);
|
|
143
|
+
await fs.promises.mkdir(storyDir, { recursive: true });
|
|
144
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
145
|
+
try {
|
|
146
|
+
// Atomic write
|
|
147
|
+
await writeFileAtomic(storyPath, content, { encoding: 'utf-8' });
|
|
148
|
+
return; // Success
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const isLastAttempt = attempt === maxRetries - 1;
|
|
152
|
+
// Don't retry on permission errors - throw immediately
|
|
153
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
154
|
+
throw new Error(`Permission denied writing story file: ${path.basename(storyPath)}`);
|
|
155
|
+
}
|
|
156
|
+
if (isLastAttempt) {
|
|
157
|
+
throw new Error(`Failed to write story file after ${maxRetries} attempts`);
|
|
158
|
+
}
|
|
159
|
+
// Wait before retry
|
|
160
|
+
await new Promise(resolve => setTimeout(resolve, retryDelays[attempt]));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get all task progress entries from story file
|
|
166
|
+
*
|
|
167
|
+
* @param storyPath - Absolute path to story.md file
|
|
168
|
+
* @returns Array of TaskProgress objects (empty if section missing)
|
|
169
|
+
*/
|
|
170
|
+
export async function getTaskProgress(storyPath) {
|
|
171
|
+
const content = await readStoryFile(storyPath);
|
|
172
|
+
return parseProgressTable(content);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Initialize progress tracking for a list of tasks
|
|
176
|
+
*
|
|
177
|
+
* Creates "## Task Progress" section with all tasks in 'pending' status.
|
|
178
|
+
* If section already exists, logs warning and skips initialization.
|
|
179
|
+
*
|
|
180
|
+
* @param storyPath - Absolute path to story.md file
|
|
181
|
+
* @param taskIds - Array of task IDs (e.g., ['T1', 'T2', 'T3'])
|
|
182
|
+
*/
|
|
183
|
+
export async function initializeTaskProgress(storyPath, taskIds) {
|
|
184
|
+
const content = await readStoryFile(storyPath);
|
|
185
|
+
// Check if progress section already exists
|
|
186
|
+
if (content.includes(TASK_PROGRESS_SECTION)) {
|
|
187
|
+
console.warn('[task-progress] Progress section already exists, skipping initialization');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Create initial progress entries (all pending)
|
|
191
|
+
const progress = taskIds.map(taskId => ({
|
|
192
|
+
taskId,
|
|
193
|
+
status: 'pending',
|
|
194
|
+
}));
|
|
195
|
+
const progressTable = generateProgressTable(progress);
|
|
196
|
+
const progressSection = `\n${TASK_PROGRESS_SECTION}\n\n${progressTable}\n`;
|
|
197
|
+
// Append to end of file
|
|
198
|
+
const newContent = content.trimEnd() + '\n' + progressSection;
|
|
199
|
+
await writeStoryFile(storyPath, newContent);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Update a specific task's status and timestamps
|
|
203
|
+
*
|
|
204
|
+
* Modifies the task row in the progress table and writes back to disk atomically.
|
|
205
|
+
* Sets timestamps based on status transitions:
|
|
206
|
+
* - 'in_progress': Sets startedAt (if not already set)
|
|
207
|
+
* - 'completed' or 'failed': Sets completedAt
|
|
208
|
+
*
|
|
209
|
+
* @param storyPath - Absolute path to story.md file
|
|
210
|
+
* @param taskId - Task ID to update (e.g., 'T1')
|
|
211
|
+
* @param status - New status
|
|
212
|
+
* @param error - Optional error message (only used with 'failed' status)
|
|
213
|
+
* @throws Error if task not found in progress table
|
|
214
|
+
*/
|
|
215
|
+
export async function updateTaskProgress(storyPath, taskId, status, error) {
|
|
216
|
+
const content = await readStoryFile(storyPath);
|
|
217
|
+
const progress = parseProgressTable(content);
|
|
218
|
+
// Find task to update
|
|
219
|
+
const taskIndex = progress.findIndex(t => t.taskId === taskId);
|
|
220
|
+
if (taskIndex === -1) {
|
|
221
|
+
throw new Error(`Task ${taskId} not found in progress table`);
|
|
222
|
+
}
|
|
223
|
+
const task = progress[taskIndex];
|
|
224
|
+
const now = new Date().toISOString();
|
|
225
|
+
// Update status
|
|
226
|
+
task.status = status;
|
|
227
|
+
// Set timestamps based on status transition
|
|
228
|
+
if (status === 'in_progress' && !task.startedAt) {
|
|
229
|
+
task.startedAt = now;
|
|
230
|
+
}
|
|
231
|
+
if (status === 'completed' || status === 'failed') {
|
|
232
|
+
task.completedAt = now;
|
|
233
|
+
}
|
|
234
|
+
// Store error message if provided
|
|
235
|
+
if (error) {
|
|
236
|
+
task.error = error;
|
|
237
|
+
}
|
|
238
|
+
// Regenerate table
|
|
239
|
+
const newTable = generateProgressTable(progress);
|
|
240
|
+
// Replace progress section in content
|
|
241
|
+
const sectionIndex = content.indexOf(TASK_PROGRESS_SECTION);
|
|
242
|
+
if (sectionIndex === -1) {
|
|
243
|
+
throw new Error('Progress section disappeared during update, this should not happen');
|
|
244
|
+
}
|
|
245
|
+
// Find next section or end of file
|
|
246
|
+
const afterSection = content.slice(sectionIndex + TASK_PROGRESS_SECTION.length);
|
|
247
|
+
const nextSectionMatch = afterSection.match(/\n## [^#]/);
|
|
248
|
+
const nextSectionOffset = nextSectionMatch
|
|
249
|
+
? sectionIndex + TASK_PROGRESS_SECTION.length + nextSectionMatch.index
|
|
250
|
+
: content.length;
|
|
251
|
+
// Rebuild content
|
|
252
|
+
const beforeSection = content.slice(0, sectionIndex);
|
|
253
|
+
const afterNextSection = content.slice(nextSectionOffset);
|
|
254
|
+
const newContent = `${beforeSection}${TASK_PROGRESS_SECTION}\n\n${newTable}\n${afterNextSection}`;
|
|
255
|
+
await writeStoryFile(storyPath, newContent);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Get list of task IDs with status 'pending'
|
|
259
|
+
*
|
|
260
|
+
* @param storyPath - Absolute path to story.md file
|
|
261
|
+
* @returns Array of pending task IDs
|
|
262
|
+
*/
|
|
263
|
+
export async function getPendingTasks(storyPath) {
|
|
264
|
+
const progress = await getTaskProgress(storyPath);
|
|
265
|
+
return progress
|
|
266
|
+
.filter(task => task.status === 'pending')
|
|
267
|
+
.map(task => task.taskId);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get the task ID currently in 'in_progress' status
|
|
271
|
+
*
|
|
272
|
+
* @param storyPath - Absolute path to story.md file
|
|
273
|
+
* @returns Task ID or null if no task is in progress
|
|
274
|
+
*/
|
|
275
|
+
export async function getCurrentTask(storyPath) {
|
|
276
|
+
const progress = await getTaskProgress(storyPath);
|
|
277
|
+
const currentTask = progress.find(task => task.status === 'in_progress');
|
|
278
|
+
return currentTask ? currentTask.taskId : null;
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=task-progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-progress.js","sourceRoot":"","sources":["../../src/core/task-progress.ts"],"names":[],"mappings":"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"}
|