ai-sdlc 0.2.0 → 0.2.1-alpha.1
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.map +1 -1
- package/dist/agents/implementation.js +64 -0
- 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 +10 -0
- package/dist/agents/review.d.ts.map +1 -1
- package/dist/agents/review.js +138 -25
- package/dist/agents/review.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/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +22 -3
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +4 -8
- package/dist/cli/runner.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +4 -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/story.d.ts +11 -1
- package/dist/core/story.d.ts.map +1 -1
- package/dist/core/story.js +33 -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/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 +183 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -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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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Error Classification Service
|
|
3
|
+
*
|
|
4
|
+
* Classifies TypeScript compiler errors into two categories:
|
|
5
|
+
* - Source errors: Root causes in production code (e.g., missing type definitions)
|
|
6
|
+
* - Cascading errors: Downstream effects caused by source errors (e.g., test file imports failing)
|
|
7
|
+
*
|
|
8
|
+
* This enables the implementation agent to prioritize fixing root causes first,
|
|
9
|
+
* which often resolves multiple cascading errors automatically.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Structured representation of a TypeScript compiler error
|
|
13
|
+
*/
|
|
14
|
+
export interface TypeScriptError {
|
|
15
|
+
/** The file path where the error occurred */
|
|
16
|
+
filePath: string;
|
|
17
|
+
/** The line number (1-indexed) where the error occurred */
|
|
18
|
+
line?: number;
|
|
19
|
+
/** The column number (1-indexed) where the error occurred */
|
|
20
|
+
column?: number;
|
|
21
|
+
/** The TypeScript error code (e.g., "TS2322") */
|
|
22
|
+
code: string;
|
|
23
|
+
/** The error message text */
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Result of classifying and sorting TypeScript errors
|
|
28
|
+
*/
|
|
29
|
+
export interface ClassifiedErrors {
|
|
30
|
+
/** Source errors that are root causes and should be fixed first */
|
|
31
|
+
source: TypeScriptError[];
|
|
32
|
+
/** Cascading errors that may resolve automatically when source errors are fixed */
|
|
33
|
+
cascading: TypeScriptError[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Error classification type
|
|
37
|
+
*/
|
|
38
|
+
export type ErrorClassification = 'source' | 'cascading';
|
|
39
|
+
/**
|
|
40
|
+
* Parses TypeScript compiler output and extracts structured error information.
|
|
41
|
+
*
|
|
42
|
+
* Expected format: `<filePath>(<line>,<col>): error <code>: <message>`
|
|
43
|
+
* Example: `src/app.tsx(59,12): error TS2322: Type 'string' is not assignable to type 'number'.`
|
|
44
|
+
*
|
|
45
|
+
* @param buildOutput - Raw output from TypeScript compiler (tsc)
|
|
46
|
+
* @returns Array of structured TypeScript errors
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const output = "src/app.tsx(59,12): error TS2322: Type 'string' is not assignable to type 'number'.";
|
|
51
|
+
* const errors = parseTypeScriptErrors(output);
|
|
52
|
+
* // errors[0] = { filePath: 'src/app.tsx', line: 59, column: 12, code: 'TS2322', message: '...' }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseTypeScriptErrors(buildOutput: string): TypeScriptError[];
|
|
56
|
+
/**
|
|
57
|
+
* Determines if a file path points to a test file.
|
|
58
|
+
*
|
|
59
|
+
* Test files are identified by:
|
|
60
|
+
* - File extension: .test.ts, .test.tsx, .spec.ts, .spec.tsx, .test.js, .test.jsx, .spec.js, .spec.jsx
|
|
61
|
+
* - Directory patterns: tests/, __tests__/
|
|
62
|
+
*
|
|
63
|
+
* @param filePath - The file path to check
|
|
64
|
+
* @returns true if the file is a test file, false otherwise
|
|
65
|
+
*/
|
|
66
|
+
export declare function isTestFile(filePath: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Determines if a file path points to a type definition file.
|
|
69
|
+
*
|
|
70
|
+
* Type definition files are identified by:
|
|
71
|
+
* - File extension: .d.ts
|
|
72
|
+
* - Directory patterns: types/, @types/
|
|
73
|
+
*
|
|
74
|
+
* @param filePath - The file path to check
|
|
75
|
+
* @returns true if the file is a type definition file, false otherwise
|
|
76
|
+
*/
|
|
77
|
+
export declare function isTypeDefinitionFile(filePath: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Classifies a single TypeScript error as either a source error (root cause)
|
|
80
|
+
* or a cascading error (downstream effect).
|
|
81
|
+
*
|
|
82
|
+
* Classification logic:
|
|
83
|
+
* 1. Known cascading-only codes (TS2345) → cascading
|
|
84
|
+
* 2. Known source codes (TS2304, TS2339) in non-test files → source
|
|
85
|
+
* 3. Context-dependent codes (TS2307, TS2322) → analyze file path
|
|
86
|
+
* 4. Conservative default → cascading
|
|
87
|
+
*
|
|
88
|
+
* @param error - The TypeScript error to classify
|
|
89
|
+
* @returns 'source' if root cause, 'cascading' if downstream effect
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const error = { code: 'TS2304', filePath: 'src/app.ts', message: 'Cannot find name Foo' };
|
|
94
|
+
* const classification = classifyError(error); // 'source'
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function classifyError(error: TypeScriptError): ErrorClassification;
|
|
98
|
+
/**
|
|
99
|
+
* Classifies and sorts multiple TypeScript errors into source and cascading categories.
|
|
100
|
+
*
|
|
101
|
+
* Source errors are prioritized for fixing as they may automatically resolve
|
|
102
|
+
* multiple cascading errors.
|
|
103
|
+
*
|
|
104
|
+
* @param errors - Array of TypeScript errors to classify
|
|
105
|
+
* @returns Object with separated source and cascading error arrays
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const errors = [
|
|
110
|
+
* { code: 'TS2304', filePath: 'src/app.ts', message: '...' },
|
|
111
|
+
* { code: 'TS2307', filePath: 'tests/app.test.ts', message: '...' }
|
|
112
|
+
* ];
|
|
113
|
+
* const classified = classifyAndSortErrors(errors);
|
|
114
|
+
* // classified.source = [{ code: 'TS2304', ... }]
|
|
115
|
+
* // classified.cascading = [{ code: 'TS2307', ... }]
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function classifyAndSortErrors(errors: TypeScriptError[]): ClassifiedErrors;
|
|
119
|
+
//# sourceMappingURL=error-classifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-classifier.d.ts","sourceRoot":"","sources":["../../src/services/error-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,mFAAmF;IACnF,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,EAAE,CAyB5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAmBpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAmB9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,mBAAmB,CAgCzE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAcjF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Error Classification Service
|
|
3
|
+
*
|
|
4
|
+
* Classifies TypeScript compiler errors into two categories:
|
|
5
|
+
* - Source errors: Root causes in production code (e.g., missing type definitions)
|
|
6
|
+
* - Cascading errors: Downstream effects caused by source errors (e.g., test file imports failing)
|
|
7
|
+
*
|
|
8
|
+
* This enables the implementation agent to prioritize fixing root causes first,
|
|
9
|
+
* which often resolves multiple cascading errors automatically.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Parses TypeScript compiler output and extracts structured error information.
|
|
13
|
+
*
|
|
14
|
+
* Expected format: `<filePath>(<line>,<col>): error <code>: <message>`
|
|
15
|
+
* Example: `src/app.tsx(59,12): error TS2322: Type 'string' is not assignable to type 'number'.`
|
|
16
|
+
*
|
|
17
|
+
* @param buildOutput - Raw output from TypeScript compiler (tsc)
|
|
18
|
+
* @returns Array of structured TypeScript errors
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const output = "src/app.tsx(59,12): error TS2322: Type 'string' is not assignable to type 'number'.";
|
|
23
|
+
* const errors = parseTypeScriptErrors(output);
|
|
24
|
+
* // errors[0] = { filePath: 'src/app.tsx', line: 59, column: 12, code: 'TS2322', message: '...' }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function parseTypeScriptErrors(buildOutput) {
|
|
28
|
+
if (!buildOutput || buildOutput.trim() === '') {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const errors = [];
|
|
32
|
+
// Regex pattern to match TypeScript error format:
|
|
33
|
+
// <filePath>(<line>,<col>): error <code>: <message>
|
|
34
|
+
// Supports both Windows (C:\...) and Unix (/...) paths
|
|
35
|
+
const tsErrorPattern = /^(.+?)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)$/gm;
|
|
36
|
+
let match;
|
|
37
|
+
while ((match = tsErrorPattern.exec(buildOutput)) !== null) {
|
|
38
|
+
const [, filePath, lineStr, colStr, code, message] = match;
|
|
39
|
+
errors.push({
|
|
40
|
+
filePath: filePath.trim(),
|
|
41
|
+
line: parseInt(lineStr, 10),
|
|
42
|
+
column: parseInt(colStr, 10),
|
|
43
|
+
code: code.trim(),
|
|
44
|
+
message: message.trim(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return errors;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Determines if a file path points to a test file.
|
|
51
|
+
*
|
|
52
|
+
* Test files are identified by:
|
|
53
|
+
* - File extension: .test.ts, .test.tsx, .spec.ts, .spec.tsx, .test.js, .test.jsx, .spec.js, .spec.jsx
|
|
54
|
+
* - Directory patterns: tests/, __tests__/
|
|
55
|
+
*
|
|
56
|
+
* @param filePath - The file path to check
|
|
57
|
+
* @returns true if the file is a test file, false otherwise
|
|
58
|
+
*/
|
|
59
|
+
export function isTestFile(filePath) {
|
|
60
|
+
// Check for test file extensions
|
|
61
|
+
if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filePath)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
// Check for test directories (handle both Windows and Unix path separators)
|
|
65
|
+
// Also handle paths that start with the directory name (e.g., "tests/app.ts")
|
|
66
|
+
if (/(^|[\\/])tests?[\\/]/.test(filePath)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
// Check for __tests__ directory
|
|
70
|
+
// Also handle paths that start with __tests__ (e.g., "__tests__/app.ts")
|
|
71
|
+
if (/(^|[\\/])__tests__[\\/]/.test(filePath)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Determines if a file path points to a type definition file.
|
|
78
|
+
*
|
|
79
|
+
* Type definition files are identified by:
|
|
80
|
+
* - File extension: .d.ts
|
|
81
|
+
* - Directory patterns: types/, @types/
|
|
82
|
+
*
|
|
83
|
+
* @param filePath - The file path to check
|
|
84
|
+
* @returns true if the file is a type definition file, false otherwise
|
|
85
|
+
*/
|
|
86
|
+
export function isTypeDefinitionFile(filePath) {
|
|
87
|
+
// Check for .d.ts extension
|
|
88
|
+
if (filePath.endsWith('.d.ts')) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
// Check for types/ directory (handle both Windows and Unix path separators)
|
|
92
|
+
// Also handle paths that start with types/ (e.g., "types/global.ts")
|
|
93
|
+
if (/(^|[\\/])types[\\/]/.test(filePath)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// Check for @types/ directory
|
|
97
|
+
// Also handle paths that start with @types/ (e.g., "@types/custom/foo.ts")
|
|
98
|
+
if (/(^|[\\/])@types[\\/]/.test(filePath)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Classifies a single TypeScript error as either a source error (root cause)
|
|
105
|
+
* or a cascading error (downstream effect).
|
|
106
|
+
*
|
|
107
|
+
* Classification logic:
|
|
108
|
+
* 1. Known cascading-only codes (TS2345) → cascading
|
|
109
|
+
* 2. Known source codes (TS2304, TS2339) in non-test files → source
|
|
110
|
+
* 3. Context-dependent codes (TS2307, TS2322) → analyze file path
|
|
111
|
+
* 4. Conservative default → cascading
|
|
112
|
+
*
|
|
113
|
+
* @param error - The TypeScript error to classify
|
|
114
|
+
* @returns 'source' if root cause, 'cascading' if downstream effect
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const error = { code: 'TS2304', filePath: 'src/app.ts', message: 'Cannot find name Foo' };
|
|
119
|
+
* const classification = classifyError(error); // 'source'
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function classifyError(error) {
|
|
123
|
+
const { code, filePath } = error;
|
|
124
|
+
// Known cascading-only error codes
|
|
125
|
+
const CASCADING_ONLY_CODES = ['TS2345']; // Argument type mismatch
|
|
126
|
+
// Known source error codes (when in non-test files)
|
|
127
|
+
const SOURCE_ERROR_CODES = ['TS2304', 'TS2339']; // Cannot find name, Property does not exist
|
|
128
|
+
// Priority 1: Known cascading-only codes
|
|
129
|
+
if (CASCADING_ONLY_CODES.includes(code)) {
|
|
130
|
+
return 'cascading';
|
|
131
|
+
}
|
|
132
|
+
// Priority 2: Known source codes in non-test files
|
|
133
|
+
if (SOURCE_ERROR_CODES.includes(code) && !isTestFile(filePath)) {
|
|
134
|
+
return 'source';
|
|
135
|
+
}
|
|
136
|
+
// Priority 3: Context-dependent codes need path analysis
|
|
137
|
+
if (code === 'TS2307') {
|
|
138
|
+
// Module not found: source in src, cascading in tests
|
|
139
|
+
return isTestFile(filePath) ? 'cascading' : 'source';
|
|
140
|
+
}
|
|
141
|
+
if (code === 'TS2322') {
|
|
142
|
+
// Type mismatch: source in type definitions, cascading elsewhere
|
|
143
|
+
return isTypeDefinitionFile(filePath) ? 'source' : 'cascading';
|
|
144
|
+
}
|
|
145
|
+
// Priority 4: Conservative default (when uncertain)
|
|
146
|
+
return 'cascading';
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Classifies and sorts multiple TypeScript errors into source and cascading categories.
|
|
150
|
+
*
|
|
151
|
+
* Source errors are prioritized for fixing as they may automatically resolve
|
|
152
|
+
* multiple cascading errors.
|
|
153
|
+
*
|
|
154
|
+
* @param errors - Array of TypeScript errors to classify
|
|
155
|
+
* @returns Object with separated source and cascading error arrays
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const errors = [
|
|
160
|
+
* { code: 'TS2304', filePath: 'src/app.ts', message: '...' },
|
|
161
|
+
* { code: 'TS2307', filePath: 'tests/app.test.ts', message: '...' }
|
|
162
|
+
* ];
|
|
163
|
+
* const classified = classifyAndSortErrors(errors);
|
|
164
|
+
* // classified.source = [{ code: 'TS2304', ... }]
|
|
165
|
+
* // classified.cascading = [{ code: 'TS2307', ... }]
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export function classifyAndSortErrors(errors) {
|
|
169
|
+
const source = [];
|
|
170
|
+
const cascading = [];
|
|
171
|
+
for (const error of errors) {
|
|
172
|
+
const classification = classifyError(error);
|
|
173
|
+
if (classification === 'source') {
|
|
174
|
+
source.push(error);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
cascading.push(error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { source, cascading };
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=error-classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-classifier.js","sourceRoot":"","sources":["../../src/services/error-classifier.ts"],"names":[],"mappings":"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"}
|