@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.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.js +41 -3
  3. package/dist/commands/base.d.ts +41 -0
  4. package/dist/commands/base.js +141 -0
  5. package/dist/commands/configure.d.ts +29 -0
  6. package/dist/commands/configure.js +187 -0
  7. package/dist/commands/init.js +15 -1
  8. package/dist/commands/prd/create.d.ts +1 -1
  9. package/dist/commands/prd/create.js +26 -8
  10. package/dist/commands/tasks/generate.d.ts +1 -1
  11. package/dist/commands/tasks/generate.js +81 -55
  12. package/dist/commands/upgrade.js +35 -2
  13. package/dist/commands/workflow/check.d.ts +16 -0
  14. package/dist/commands/workflow/check.js +355 -32
  15. package/dist/commands/workflow/do.js +157 -62
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +6 -0
  18. package/dist/lib/config-paths.js +6 -1
  19. package/dist/lib/file-validator.d.ts +119 -0
  20. package/dist/lib/file-validator.js +291 -0
  21. package/dist/lib/log-parser.d.ts +91 -0
  22. package/dist/lib/log-parser.js +178 -0
  23. package/dist/lib/retrospective.d.ts +27 -0
  24. package/dist/lib/retrospective.js +110 -0
  25. package/dist/lib/types.d.ts +8 -0
  26. package/dist/lib/types.js +12 -8
  27. package/dist/llm/base.d.ts +52 -0
  28. package/dist/llm/base.js +35 -0
  29. package/dist/llm/factory.d.ts +39 -0
  30. package/dist/llm/factory.js +102 -0
  31. package/dist/llm/index.d.ts +7 -0
  32. package/dist/llm/index.js +7 -0
  33. package/dist/llm/model-selector.d.ts +71 -0
  34. package/dist/llm/model-selector.js +139 -0
  35. package/dist/llm/providers/anthropic.d.ts +31 -0
  36. package/dist/llm/providers/anthropic.js +116 -0
  37. package/dist/llm/providers/index.d.ts +6 -0
  38. package/dist/llm/providers/index.js +6 -0
  39. package/dist/llm/providers/ollama.d.ts +28 -0
  40. package/dist/llm/providers/ollama.js +91 -0
  41. package/dist/llm/providers/openai-compatible.d.ts +30 -0
  42. package/dist/llm/providers/openai-compatible.js +93 -0
  43. package/dist/schemas/config.d.ts +82 -0
  44. package/dist/schemas/config.js +35 -0
  45. package/package.json +43 -43
  46. package/dist/lib/package-manager.d.ts +0 -17
  47. 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;