@krr2020/taskflow-core 0.1.0-beta.4 → 0.1.0-beta.5
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/README.md +1 -1
- package/dist/cli/index.js +41 -3
- package/dist/commands/base.d.ts +41 -0
- package/dist/commands/base.js +141 -0
- package/dist/commands/configure.d.ts +29 -0
- package/dist/commands/configure.js +187 -0
- package/dist/commands/init.js +15 -1
- package/dist/commands/prd/create.d.ts +1 -1
- package/dist/commands/prd/create.js +26 -8
- package/dist/commands/tasks/generate.d.ts +1 -1
- package/dist/commands/tasks/generate.js +81 -55
- package/dist/commands/upgrade.js +35 -2
- package/dist/commands/workflow/check.d.ts +16 -0
- package/dist/commands/workflow/check.js +355 -32
- package/dist/commands/workflow/do.js +157 -62
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/lib/config-paths.js +6 -1
- package/dist/lib/file-validator.d.ts +119 -0
- package/dist/lib/file-validator.js +291 -0
- package/dist/lib/log-parser.d.ts +91 -0
- package/dist/lib/log-parser.js +178 -0
- package/dist/lib/retrospective.d.ts +27 -0
- package/dist/lib/retrospective.js +110 -0
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/types.js +12 -8
- package/dist/llm/base.d.ts +52 -0
- package/dist/llm/base.js +35 -0
- package/dist/llm/factory.d.ts +39 -0
- package/dist/llm/factory.js +102 -0
- package/dist/llm/index.d.ts +7 -0
- package/dist/llm/index.js +7 -0
- package/dist/llm/model-selector.d.ts +71 -0
- package/dist/llm/model-selector.js +139 -0
- package/dist/llm/providers/anthropic.d.ts +31 -0
- package/dist/llm/providers/anthropic.js +116 -0
- package/dist/llm/providers/index.d.ts +6 -0
- package/dist/llm/providers/index.js +6 -0
- package/dist/llm/providers/ollama.d.ts +28 -0
- package/dist/llm/providers/ollama.js +91 -0
- package/dist/llm/providers/openai-compatible.d.ts +30 -0
- package/dist/llm/providers/openai-compatible.js +93 -0
- package/dist/schemas/config.d.ts +82 -0
- package/dist/schemas/config.js +35 -0
- package/package.json +43 -43
- package/dist/lib/package-manager.d.ts +0 -17
- package/dist/lib/package-manager.js +0 -53
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Validator
|
|
3
|
+
* Validates code files using LLM to provide fix suggestions and guidance
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
/**
|
|
7
|
+
* File Validator class
|
|
8
|
+
* Validates code files using LLM for analysis and fix suggestions
|
|
9
|
+
*/
|
|
10
|
+
export class FileValidator {
|
|
11
|
+
options;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate a file
|
|
17
|
+
*/
|
|
18
|
+
async validate(filePath) {
|
|
19
|
+
const content = await readFile(filePath, "utf-8");
|
|
20
|
+
return this.validateContent(filePath, content);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate multiple files
|
|
24
|
+
*/
|
|
25
|
+
async validateFiles(filePaths) {
|
|
26
|
+
const results = [];
|
|
27
|
+
for (const filePath of filePaths) {
|
|
28
|
+
const result = await this.validate(filePath);
|
|
29
|
+
results.push(result);
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate file content
|
|
35
|
+
*/
|
|
36
|
+
async validateContent(filePath, content) {
|
|
37
|
+
const provider = this.options.provider;
|
|
38
|
+
// Create validation prompt
|
|
39
|
+
const messages = [
|
|
40
|
+
{
|
|
41
|
+
role: "user",
|
|
42
|
+
content: this.createValidationPrompt(filePath, content),
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
try {
|
|
46
|
+
// Call LLM for validation
|
|
47
|
+
const response = await provider.generate(messages, {
|
|
48
|
+
maxTokens: 2000,
|
|
49
|
+
temperature: 0.3, // Lower temperature for more deterministic results
|
|
50
|
+
});
|
|
51
|
+
// Parse LLM response
|
|
52
|
+
const issues = this.parseValidationResponse(response.content);
|
|
53
|
+
const suggestions = this.extractSuggestions(issues);
|
|
54
|
+
// Determine if passed
|
|
55
|
+
const hasErrors = issues.some((i) => i.severity === "error");
|
|
56
|
+
const hasWarnings = issues.some((i) => i.severity === "warning");
|
|
57
|
+
const summary = this.createSummary(issues, filePath);
|
|
58
|
+
return {
|
|
59
|
+
file: filePath,
|
|
60
|
+
passed: !hasErrors && !hasWarnings,
|
|
61
|
+
issues,
|
|
62
|
+
suggestions,
|
|
63
|
+
summary,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
// Fallback to basic validation if LLM fails
|
|
68
|
+
return this.createFallbackResult(filePath, content, error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create validation prompt
|
|
73
|
+
*/
|
|
74
|
+
createValidationPrompt(filePath, content) {
|
|
75
|
+
const fileName = filePath.split("/").pop() || "";
|
|
76
|
+
const fileExtension = fileName.split(".").pop() || "";
|
|
77
|
+
const prompt = `Analyze the following ${fileExtension} file and identify any issues:
|
|
78
|
+
|
|
79
|
+
File: ${fileName}
|
|
80
|
+
Path: ${filePath}
|
|
81
|
+
|
|
82
|
+
\`\`\`$${fileExtension}
|
|
83
|
+
${this.truncateContent(content)}
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
Please provide:
|
|
87
|
+
1. Any syntax errors or type errors
|
|
88
|
+
2. Any logical errors or bugs
|
|
89
|
+
3. Code quality issues (naming, complexity, etc.)
|
|
90
|
+
4. Security concerns
|
|
91
|
+
5. Performance concerns
|
|
92
|
+
6. Suggested fixes for issues found
|
|
93
|
+
|
|
94
|
+
Format your response as JSON:
|
|
95
|
+
\`\`\`json
|
|
96
|
+
{
|
|
97
|
+
"issues": [
|
|
98
|
+
{
|
|
99
|
+
"severity": "error" | "warning" | "info",
|
|
100
|
+
"line": number,
|
|
101
|
+
"message": string,
|
|
102
|
+
"code": string,
|
|
103
|
+
"suggestedFix": string
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"suggestions": ["string", "string"]
|
|
107
|
+
}
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
If no issues are found, respond with:
|
|
111
|
+
\`\`\`json
|
|
112
|
+
{
|
|
113
|
+
"issues": [],
|
|
114
|
+
"suggestions": ["File looks good"]
|
|
115
|
+
}
|
|
116
|
+
\`\`\``;
|
|
117
|
+
return prompt;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Truncate content to fit within context limits
|
|
121
|
+
*/
|
|
122
|
+
truncateContent(content, maxLength = 8000) {
|
|
123
|
+
if (content.length <= maxLength) {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
// Return first part with indicator
|
|
127
|
+
return `${content.substring(0, maxLength)}\n\n... (content truncated)`;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse validation response from LLM
|
|
131
|
+
*/
|
|
132
|
+
parseValidationResponse(response) {
|
|
133
|
+
try {
|
|
134
|
+
// Try to extract JSON from response
|
|
135
|
+
const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/);
|
|
136
|
+
if (jsonMatch?.[1]) {
|
|
137
|
+
const parsed = JSON.parse(jsonMatch[1]);
|
|
138
|
+
return parsed.issues ?? [];
|
|
139
|
+
}
|
|
140
|
+
// Try to parse entire response as JSON
|
|
141
|
+
const parsed = JSON.parse(response);
|
|
142
|
+
return parsed.issues ?? [];
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// If parsing fails, return empty issues
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Extract suggestions from issues
|
|
151
|
+
*/
|
|
152
|
+
extractSuggestions(issues) {
|
|
153
|
+
const suggestions = [];
|
|
154
|
+
for (const issue of issues) {
|
|
155
|
+
if (issue.suggestedFix) {
|
|
156
|
+
suggestions.push(issue.suggestedFix);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Add general suggestions if needed
|
|
160
|
+
if (issues.length === 0) {
|
|
161
|
+
suggestions.push("No issues found in the file");
|
|
162
|
+
}
|
|
163
|
+
return suggestions;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create validation summary
|
|
167
|
+
*/
|
|
168
|
+
createSummary(issues, filePath) {
|
|
169
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
170
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
171
|
+
const infoCount = issues.filter((i) => i.severity === "info").length;
|
|
172
|
+
const fileName = filePath.split("/").pop() || "";
|
|
173
|
+
if (errorCount === 0 && warningCount === 0 && infoCount === 0) {
|
|
174
|
+
return `${fileName}: No issues found`;
|
|
175
|
+
}
|
|
176
|
+
const parts = [`${fileName}:`];
|
|
177
|
+
if (errorCount > 0) {
|
|
178
|
+
parts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
179
|
+
}
|
|
180
|
+
if (warningCount > 0) {
|
|
181
|
+
parts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
182
|
+
}
|
|
183
|
+
if (infoCount > 0) {
|
|
184
|
+
parts.push(`${infoCount} info`);
|
|
185
|
+
}
|
|
186
|
+
return parts.join(" ");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create fallback result when LLM fails
|
|
190
|
+
*/
|
|
191
|
+
createFallbackResult(filePath, content, error) {
|
|
192
|
+
const fileName = filePath.split("/").pop() || "";
|
|
193
|
+
// Basic validation checks
|
|
194
|
+
const issues = [];
|
|
195
|
+
if (content.trim().length === 0) {
|
|
196
|
+
issues.push({
|
|
197
|
+
severity: "error",
|
|
198
|
+
message: "File is empty",
|
|
199
|
+
code: "EMPTY_FILE",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (content.length > 100000) {
|
|
203
|
+
issues.push({
|
|
204
|
+
severity: "warning",
|
|
205
|
+
message: "File is very large, may affect validation",
|
|
206
|
+
code: "LARGE_FILE",
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
file: filePath,
|
|
211
|
+
passed: issues.length === 0,
|
|
212
|
+
issues,
|
|
213
|
+
suggestions: [
|
|
214
|
+
`Validation error: ${error.message}`,
|
|
215
|
+
"AI validation unavailable - showing basic checks only",
|
|
216
|
+
],
|
|
217
|
+
summary: `${fileName}: Basic validation only (${issues.length} issue${issues.length > 1 ? "s" : ""})`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Format validation result for display
|
|
222
|
+
*/
|
|
223
|
+
formatResult(result) {
|
|
224
|
+
const lines = [];
|
|
225
|
+
lines.push(`\n${"=".repeat(60)}`);
|
|
226
|
+
lines.push(`File: ${result.file}`);
|
|
227
|
+
lines.push(`Status: ${result.passed ? "✓ PASSED" : "✗ FAILED"}`);
|
|
228
|
+
lines.push("=".repeat(60));
|
|
229
|
+
lines.push("");
|
|
230
|
+
if (result.issues.length > 0) {
|
|
231
|
+
lines.push("Issues:");
|
|
232
|
+
lines.push("");
|
|
233
|
+
for (const issue of result.issues.slice(0, this.options.maxIssues || 10)) {
|
|
234
|
+
const icon = issue.severity === "error"
|
|
235
|
+
? "✗"
|
|
236
|
+
: issue.severity === "warning"
|
|
237
|
+
? "⚠"
|
|
238
|
+
: "ℹ";
|
|
239
|
+
lines.push(` ${icon} ${issue.message}`);
|
|
240
|
+
if (issue.line) {
|
|
241
|
+
lines.push(` Line: ${issue.line}`);
|
|
242
|
+
}
|
|
243
|
+
if (issue.code) {
|
|
244
|
+
lines.push(` Code: ${issue.code}`);
|
|
245
|
+
}
|
|
246
|
+
if (issue.suggestedFix) {
|
|
247
|
+
lines.push(` Fix: ${issue.suggestedFix}`);
|
|
248
|
+
}
|
|
249
|
+
lines.push("");
|
|
250
|
+
}
|
|
251
|
+
if (result.issues.length > (this.options.maxIssues || 10)) {
|
|
252
|
+
const remaining = result.issues.length - (this.options.maxIssues || 10);
|
|
253
|
+
lines.push(` ... and ${remaining} more issue${remaining > 1 ? "s" : ""} (truncated)`);
|
|
254
|
+
lines.push("");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (result.suggestions.length > 0) {
|
|
258
|
+
lines.push("Suggestions:");
|
|
259
|
+
lines.push("");
|
|
260
|
+
for (const suggestion of result.suggestions) {
|
|
261
|
+
lines.push(` • ${suggestion}`);
|
|
262
|
+
}
|
|
263
|
+
lines.push("");
|
|
264
|
+
}
|
|
265
|
+
lines.push(`Summary: ${result.summary}`);
|
|
266
|
+
lines.push("");
|
|
267
|
+
return lines.join("\n");
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Format multiple validation results
|
|
271
|
+
*/
|
|
272
|
+
formatResults(results) {
|
|
273
|
+
const lines = [];
|
|
274
|
+
lines.push(`\n${"=".repeat(80)}`);
|
|
275
|
+
lines.push("Validation Report");
|
|
276
|
+
lines.push("=".repeat(80));
|
|
277
|
+
const passedCount = results.filter((r) => r.passed).length;
|
|
278
|
+
const failedCount = results.filter((r) => !r.passed).length;
|
|
279
|
+
const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);
|
|
280
|
+
lines.push(`Files: ${results.length}`);
|
|
281
|
+
lines.push(`Passed: ${passedCount}`);
|
|
282
|
+
lines.push(`Failed: ${failedCount}`);
|
|
283
|
+
lines.push(`Total Issues: ${totalIssues}`);
|
|
284
|
+
lines.push("=".repeat(80));
|
|
285
|
+
lines.push("");
|
|
286
|
+
for (const result of results) {
|
|
287
|
+
lines.push(this.formatResult(result));
|
|
288
|
+
}
|
|
289
|
+
return lines.join("\n");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Parser
|
|
3
|
+
* Extracts errors and diagnostic information from build/test logs
|
|
4
|
+
*/
|
|
5
|
+
export interface ParsedError {
|
|
6
|
+
/**
|
|
7
|
+
* File path (relative or absolute)
|
|
8
|
+
*/
|
|
9
|
+
file: string;
|
|
10
|
+
/**
|
|
11
|
+
* Line number where error occurred
|
|
12
|
+
*/
|
|
13
|
+
line: number;
|
|
14
|
+
/**
|
|
15
|
+
* Column number where error occurred
|
|
16
|
+
*/
|
|
17
|
+
column: number;
|
|
18
|
+
/**
|
|
19
|
+
* Error message
|
|
20
|
+
*/
|
|
21
|
+
message: string;
|
|
22
|
+
/**
|
|
23
|
+
* Error code or type (e.g., TS2322, E404)
|
|
24
|
+
*/
|
|
25
|
+
code: string;
|
|
26
|
+
/**
|
|
27
|
+
* Error severity (error, warning, info)
|
|
28
|
+
*/
|
|
29
|
+
severity: "error" | "warning" | "info";
|
|
30
|
+
/**
|
|
31
|
+
* Context lines around the error
|
|
32
|
+
*/
|
|
33
|
+
context?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Raw log line
|
|
36
|
+
*/
|
|
37
|
+
raw: string;
|
|
38
|
+
}
|
|
39
|
+
export interface LogParseResult {
|
|
40
|
+
/**
|
|
41
|
+
* Parsed errors
|
|
42
|
+
*/
|
|
43
|
+
errors: ParsedError[];
|
|
44
|
+
/**
|
|
45
|
+
* Warning count
|
|
46
|
+
*/
|
|
47
|
+
warningCount: number;
|
|
48
|
+
/**
|
|
49
|
+
* Error count
|
|
50
|
+
*/
|
|
51
|
+
errorCount: number;
|
|
52
|
+
/**
|
|
53
|
+
* Success indicator (no errors found)
|
|
54
|
+
*/
|
|
55
|
+
success: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Log Parser class
|
|
59
|
+
* Parses build/test logs to extract errors and diagnostics
|
|
60
|
+
*/
|
|
61
|
+
export declare class LogParser {
|
|
62
|
+
private patterns;
|
|
63
|
+
/**
|
|
64
|
+
* Parse log string
|
|
65
|
+
*/
|
|
66
|
+
parse(logContent: string): LogParseResult;
|
|
67
|
+
/**
|
|
68
|
+
* Parse log file
|
|
69
|
+
*/
|
|
70
|
+
parseFile(filePath: string): Promise<LogParseResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Create error from regex match
|
|
73
|
+
*/
|
|
74
|
+
private createErrorFromMatch;
|
|
75
|
+
/**
|
|
76
|
+
* Group errors by file
|
|
77
|
+
*/
|
|
78
|
+
groupErrorsByFile(errors: ParsedError[]): Map<string, ParsedError[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Filter errors by severity
|
|
81
|
+
*/
|
|
82
|
+
filterBySeverity(errors: ParsedError[], severity: "error" | "warning" | "info"): ParsedError[];
|
|
83
|
+
/**
|
|
84
|
+
* Filter errors by file
|
|
85
|
+
*/
|
|
86
|
+
filterByFile(errors: ParsedError[], filePattern: string): ParsedError[];
|
|
87
|
+
/**
|
|
88
|
+
* Format error for display
|
|
89
|
+
*/
|
|
90
|
+
formatError(error: ParsedError): string;
|
|
91
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Parser
|
|
3
|
+
* Extracts errors and diagnostic information from build/test logs
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
/**
|
|
7
|
+
* Log Parser class
|
|
8
|
+
* Parses build/test logs to extract errors and diagnostics
|
|
9
|
+
*/
|
|
10
|
+
export class LogParser {
|
|
11
|
+
patterns = [
|
|
12
|
+
{
|
|
13
|
+
name: "TypeScript error",
|
|
14
|
+
regex: /([^:\n]+?):(\d+):(\d+) - error (TS\d+): (.+)/g,
|
|
15
|
+
severity: "error",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "TypeScript warning",
|
|
19
|
+
regex: /([^:\n]+?):(\d+):(\d+) - warning (TS\d+): (.+)/g,
|
|
20
|
+
severity: "warning",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "ESLint error",
|
|
24
|
+
regex: /([^:\n]+?):(\d+):(\d+) error (.+)/g,
|
|
25
|
+
severity: "error",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "ESLint warning",
|
|
29
|
+
regex: /([^:\n]+?):(\d+):(\d+) warning (.+)/g,
|
|
30
|
+
severity: "warning",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "Test failure",
|
|
34
|
+
regex: /FAIL\s+([^\s]+?)(?:\n|$)/g,
|
|
35
|
+
severity: "error",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "Build error",
|
|
39
|
+
regex: /error\s+(.+?)(?:\n|$)/g,
|
|
40
|
+
severity: "error",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "Compilation error",
|
|
44
|
+
regex: /([^:\n]+?):(\d+): error: (.+)/g,
|
|
45
|
+
severity: "error",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Parse log string
|
|
50
|
+
*/
|
|
51
|
+
parse(logContent) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
for (const pattern of this.patterns) {
|
|
54
|
+
let match = pattern.regex.exec(logContent);
|
|
55
|
+
while (match !== null) {
|
|
56
|
+
const error = this.createErrorFromMatch(match, pattern);
|
|
57
|
+
if (error) {
|
|
58
|
+
errors.push(error);
|
|
59
|
+
}
|
|
60
|
+
match = pattern.regex.exec(logContent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const errorCount = errors.filter((e) => e.severity === "error").length;
|
|
64
|
+
const warningCount = errors.filter((e) => e.severity === "warning").length;
|
|
65
|
+
return {
|
|
66
|
+
errors,
|
|
67
|
+
errorCount,
|
|
68
|
+
warningCount,
|
|
69
|
+
success: errorCount === 0,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse log file
|
|
74
|
+
*/
|
|
75
|
+
async parseFile(filePath) {
|
|
76
|
+
const content = await readFile(filePath, "utf-8");
|
|
77
|
+
return this.parse(content);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create error from regex match
|
|
81
|
+
*/
|
|
82
|
+
createErrorFromMatch(match, pattern) {
|
|
83
|
+
const raw = match[0];
|
|
84
|
+
// Try to extract file, line, column, message, code
|
|
85
|
+
let file = "";
|
|
86
|
+
let line = 0;
|
|
87
|
+
let column = 0;
|
|
88
|
+
let message = raw;
|
|
89
|
+
let code = "";
|
|
90
|
+
switch (pattern.name) {
|
|
91
|
+
case "TypeScript error":
|
|
92
|
+
case "TypeScript warning":
|
|
93
|
+
file = match[1] || "";
|
|
94
|
+
line = Number.parseInt(match[2] || "0", 10);
|
|
95
|
+
column = Number.parseInt(match[3] || "0", 10);
|
|
96
|
+
code = match[4]?.split(":")[0] || "";
|
|
97
|
+
message = match[4] || raw;
|
|
98
|
+
break;
|
|
99
|
+
case "ESLint error":
|
|
100
|
+
case "ESLint warning":
|
|
101
|
+
file = match[1] || "";
|
|
102
|
+
line = Number.parseInt(match[2] || "0", 10);
|
|
103
|
+
column = Number.parseInt(match[3] || "0", 10);
|
|
104
|
+
message = match[4] || raw;
|
|
105
|
+
break;
|
|
106
|
+
case "Test failure":
|
|
107
|
+
file = match[1] || "";
|
|
108
|
+
message = `Test failed: ${match[1]}`;
|
|
109
|
+
break;
|
|
110
|
+
case "Build error":
|
|
111
|
+
message = match[1] || raw;
|
|
112
|
+
break;
|
|
113
|
+
case "Compilation error":
|
|
114
|
+
file = match[1] || "";
|
|
115
|
+
line = Number.parseInt(match[2] || "0", 10);
|
|
116
|
+
message = match[3] || raw;
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
message = raw;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
file,
|
|
123
|
+
line,
|
|
124
|
+
column,
|
|
125
|
+
message,
|
|
126
|
+
code,
|
|
127
|
+
severity: pattern.severity,
|
|
128
|
+
raw,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Group errors by file
|
|
133
|
+
*/
|
|
134
|
+
groupErrorsByFile(errors) {
|
|
135
|
+
const grouped = new Map();
|
|
136
|
+
for (const error of errors) {
|
|
137
|
+
if (error.file) {
|
|
138
|
+
const existing = grouped.get(error.file) || [];
|
|
139
|
+
existing.push(error);
|
|
140
|
+
grouped.set(error.file, existing);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return grouped;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Filter errors by severity
|
|
147
|
+
*/
|
|
148
|
+
filterBySeverity(errors, severity) {
|
|
149
|
+
return errors.filter((e) => e.severity === severity);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Filter errors by file
|
|
153
|
+
*/
|
|
154
|
+
filterByFile(errors, filePattern) {
|
|
155
|
+
const regex = new RegExp(filePattern);
|
|
156
|
+
return errors.filter((e) => regex.test(e.file));
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Format error for display
|
|
160
|
+
*/
|
|
161
|
+
formatError(error) {
|
|
162
|
+
const parts = [];
|
|
163
|
+
if (error.file) {
|
|
164
|
+
parts.push(error.file);
|
|
165
|
+
if (error.line) {
|
|
166
|
+
parts.push(`:${error.line}`);
|
|
167
|
+
if (error.column) {
|
|
168
|
+
parts.push(`:${error.column}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (error.code) {
|
|
173
|
+
parts.push(`[${error.code}]`);
|
|
174
|
+
}
|
|
175
|
+
parts.push(`- ${error.message}`);
|
|
176
|
+
return parts.join(" ");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Retrospective module for error pattern management
|
|
3
3
|
*/
|
|
4
|
+
import type { ParsedError } from "./log-parser.js";
|
|
4
5
|
import type { Criticality, ErrorCategory, RetrospectiveItem } from "./types.js";
|
|
6
|
+
export interface NewPattern {
|
|
7
|
+
category: ErrorCategory | string;
|
|
8
|
+
pattern: string;
|
|
9
|
+
solution: string;
|
|
10
|
+
criticality: Criticality | string;
|
|
11
|
+
errorCode?: string;
|
|
12
|
+
affectedFiles: string[];
|
|
13
|
+
}
|
|
5
14
|
export declare function getRetrospectiveFilePath(refDir: string): string;
|
|
6
15
|
export declare function loadRetrospective(refDir: string): RetrospectiveItem[];
|
|
7
16
|
export declare function parseRetrospectiveContent(content: string): RetrospectiveItem[];
|
|
@@ -23,3 +32,21 @@ export declare const VALID_CRITICALITIES: Criticality[];
|
|
|
23
32
|
export declare function isValidCategory(category: string): category is ErrorCategory;
|
|
24
33
|
export declare function isValidCriticality(criticality: string): criticality is Criticality;
|
|
25
34
|
export declare function printRetroAddUsage(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Read retrospective file content
|
|
37
|
+
*/
|
|
38
|
+
export declare function readRetrospectiveBeforeWork(refDir: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Extract NEW error patterns from parsed errors
|
|
41
|
+
* Compares against existing retrospective to avoid duplicates
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractNewPatterns(errors: ParsedError[], refDir: string): NewPattern[];
|
|
44
|
+
/**
|
|
45
|
+
* Append new patterns to retrospective file
|
|
46
|
+
* Uses existing addRetrospectiveEntry for each pattern
|
|
47
|
+
*/
|
|
48
|
+
export declare function appendNewPatternsToRetrospective(refDir: string, patterns: NewPattern[]): number[];
|
|
49
|
+
/**
|
|
50
|
+
* Format a new pattern for display (before adding to retrospective)
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatNewPatternForDisplay(pattern: NewPattern): string;
|