@auto-engineer/server-implementer 1.139.0 → 1.140.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +3 -3
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +17 -0
- package/DEBUG.md +4 -4
- package/README.md +4 -4
- package/dist/src/agent/detectShadowsInSlice.specs.js +6 -6
- package/dist/src/agent/detectShadowsInSlice.specs.js.map +1 -1
- package/dist/src/agent/runAllSlices.d.ts +1 -1
- package/dist/src/agent/runAllSlices.d.ts.map +1 -1
- package/dist/src/agent/runAllSlices.js +7 -7
- package/dist/src/agent/runAllSlices.js.map +1 -1
- package/dist/src/agent/runFlows.d.ts +1 -1
- package/dist/src/agent/runFlows.d.ts.map +1 -1
- package/dist/src/agent/runFlows.js +20 -20
- package/dist/src/agent/runFlows.js.map +1 -1
- package/dist/src/agent/runSlice.d.ts +4 -4
- package/dist/src/agent/runSlice.d.ts.map +1 -1
- package/dist/src/agent/runSlice.js +64 -64
- package/dist/src/agent/runSlice.js.map +1 -1
- package/dist/src/agent/runTests.d.ts +1 -1
- package/dist/src/agent/runTests.d.ts.map +1 -1
- package/dist/src/agent/runTests.js +12 -12
- package/dist/src/agent/runTests.js.map +1 -1
- package/dist/src/commands/implement-server.d.ts +1 -1
- package/dist/src/commands/implement-server.d.ts.map +1 -1
- package/dist/src/commands/implement-server.js +17 -17
- package/dist/src/commands/implement-server.js.map +1 -1
- package/dist/src/commands/implement-slice.d.ts +10 -10
- package/dist/src/commands/implement-slice.d.ts.map +1 -1
- package/dist/src/commands/implement-slice.js +47 -47
- package/dist/src/commands/implement-slice.js.map +1 -1
- package/dist/src/index.d.ts +5 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/prompts/systemPrompt.d.ts +1 -1
- package/dist/src/prompts/systemPrompt.d.ts.map +1 -1
- package/dist/src/prompts/systemPrompt.js +1 -1
- package/dist/src/utils/buildContextSections.js +3 -3
- package/dist/src/utils/buildContextSections.js.map +1 -1
- package/dist/src/utils/buildContextSections.specs.js +6 -6
- package/dist/src/utils/buildContextSections.specs.js.map +1 -1
- package/dist/src/utils/loadContextFiles.d.ts +1 -1
- package/dist/src/utils/loadContextFiles.d.ts.map +1 -1
- package/dist/src/utils/loadContextFiles.js +4 -4
- package/dist/src/utils/loadContextFiles.js.map +1 -1
- package/dist/src/utils/loadContextFiles.specs.js +12 -12
- package/dist/src/utils/loadContextFiles.specs.js.map +1 -1
- package/dist/src/utils/loadSharedContext.d.ts +1 -1
- package/dist/src/utils/loadSharedContext.d.ts.map +1 -1
- package/dist/src/utils/loadSharedContext.js +2 -2
- package/dist/src/utils/loadSharedContext.js.map +1 -1
- package/dist/src/utils/loadSharedContext.specs.js +7 -7
- package/dist/src/utils/loadSharedContext.specs.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/agent/detectShadowsInSlice.specs.ts +6 -6
- package/src/agent/runAllSlices.ts +7 -7
- package/src/agent/runFlows.ts +21 -21
- package/src/agent/runSlice.ts +65 -65
- package/src/agent/runTests.ts +12 -12
- package/src/commands/implement-server.ts +18 -18
- package/src/commands/implement-slice.ts +73 -71
- package/src/index.ts +8 -8
- package/src/prompts/systemPrompt.ts +1 -1
- package/src/utils/buildContextSections.specs.ts +6 -6
- package/src/utils/buildContextSections.ts +3 -3
- package/src/utils/loadContextFiles.specs.ts +12 -12
- package/src/utils/loadContextFiles.ts +4 -4
- package/src/utils/loadSharedContext.specs.ts +7 -7
- package/src/utils/loadSharedContext.ts +2 -2
package/package.json
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"debug": "^4.3.4",
|
|
19
19
|
"fast-glob": "^3.3.3",
|
|
20
20
|
"vite": "^5.4.1",
|
|
21
|
-
"@auto-engineer/model-factory": "1.
|
|
22
|
-
"@auto-engineer/message-bus": "1.
|
|
21
|
+
"@auto-engineer/model-factory": "1.140.0",
|
|
22
|
+
"@auto-engineer/message-bus": "1.140.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"glob": "^11.0.3",
|
|
30
30
|
"tsx": "^4.20.3",
|
|
31
31
|
"typescript": "^5.8.3",
|
|
32
|
-
"@auto-engineer/cli": "1.
|
|
32
|
+
"@auto-engineer/cli": "1.140.0"
|
|
33
33
|
},
|
|
34
|
-
"version": "1.
|
|
34
|
+
"version": "1.140.0",
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc && tsx ../../scripts/fix-esm-imports.ts",
|
|
37
37
|
"test": "vitest run --reporter=dot",
|
|
@@ -2,9 +2,9 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import {
|
|
5
|
+
import { detectShadowsInMoment } from './runSlice';
|
|
6
6
|
|
|
7
|
-
describe('
|
|
7
|
+
describe('detectShadowsInMoment', () => {
|
|
8
8
|
let tempDir: string;
|
|
9
9
|
|
|
10
10
|
beforeEach(async () => {
|
|
@@ -18,7 +18,7 @@ describe('detectShadowsInSlice', () => {
|
|
|
18
18
|
it('returns empty results for clean files', async () => {
|
|
19
19
|
await writeFile(path.join(tempDir, 'projection.ts'), `import type { MyState } from './state';\nconst x = 1;\n`);
|
|
20
20
|
|
|
21
|
-
const result = await
|
|
21
|
+
const result = await detectShadowsInMoment(tempDir);
|
|
22
22
|
|
|
23
23
|
expect(result).toEqual({ errors: '', failedFiles: [] });
|
|
24
24
|
});
|
|
@@ -29,7 +29,7 @@ describe('detectShadowsInSlice', () => {
|
|
|
29
29
|
`import type { MyState } from './state';\ntype MyState = { name: string };\n`,
|
|
30
30
|
);
|
|
31
31
|
|
|
32
|
-
const result = await
|
|
32
|
+
const result = await detectShadowsInMoment(tempDir);
|
|
33
33
|
|
|
34
34
|
expect(result).toEqual({
|
|
35
35
|
errors:
|
|
@@ -44,7 +44,7 @@ describe('detectShadowsInSlice', () => {
|
|
|
44
44
|
`import type { MyState } from './state';\ntype MyState = { name: string };\n`,
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
const result = await
|
|
47
|
+
const result = await detectShadowsInMoment(tempDir);
|
|
48
48
|
|
|
49
49
|
expect(result).toEqual({ errors: '', failedFiles: [] });
|
|
50
50
|
});
|
|
@@ -53,7 +53,7 @@ describe('detectShadowsInSlice', () => {
|
|
|
53
53
|
await writeFile(path.join(tempDir, 'a.ts'), `import type { Alpha } from './types';\ntype Alpha = { x: number };\n`);
|
|
54
54
|
await writeFile(path.join(tempDir, 'b.ts'), `import type { Beta } from './types';\ninterface Beta { y: string }\n`);
|
|
55
55
|
|
|
56
|
-
const result = await
|
|
56
|
+
const result = await detectShadowsInMoment(tempDir);
|
|
57
57
|
|
|
58
58
|
expect(result.failedFiles).toEqual([path.join(tempDir, 'a.ts'), path.join(tempDir, 'b.ts')]);
|
|
59
59
|
expect(result.errors).toContain('CONTRACT VIOLATION in a.ts');
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fg from 'fast-glob';
|
|
3
|
-
import {
|
|
3
|
+
import { runMoment } from './runSlice';
|
|
4
4
|
|
|
5
|
-
export async function
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
for (const
|
|
9
|
-
await
|
|
5
|
+
export async function runAllMoments(sceneDir: string): Promise<void> {
|
|
6
|
+
const sceneName = path.basename(sceneDir);
|
|
7
|
+
const momentDirs = await fg(`${sceneDir}/**/*/`, { onlyDirectories: true });
|
|
8
|
+
for (const momentDir of momentDirs) {
|
|
9
|
+
await runMoment(momentDir, sceneName);
|
|
10
10
|
}
|
|
11
|
-
console.log('✅ All
|
|
11
|
+
console.log('✅ All moments processed');
|
|
12
12
|
}
|
package/src/agent/runFlows.ts
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import createDebug from 'debug';
|
|
3
3
|
import fg from 'fast-glob';
|
|
4
|
-
import {
|
|
4
|
+
import { runAllMoments } from './runAllSlices';
|
|
5
5
|
|
|
6
|
-
const debug = createDebug('auto:server-implementer:
|
|
7
|
-
const
|
|
6
|
+
const debug = createDebug('auto:server-implementer:scenes');
|
|
7
|
+
const debugScene = createDebug('auto:server-implementer:scenes:scene');
|
|
8
8
|
|
|
9
|
-
export async function
|
|
10
|
-
debug('Running
|
|
9
|
+
export async function runScenes(baseDir: string): Promise<void> {
|
|
10
|
+
debug('Running scenes from base directory: %s', baseDir);
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const sceneDirs = await fg('*', {
|
|
13
13
|
cwd: baseDir,
|
|
14
14
|
onlyDirectories: true,
|
|
15
15
|
absolute: true,
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
debug('Found %d
|
|
19
|
-
if (
|
|
18
|
+
debug('Found %d scene directories', sceneDirs.length);
|
|
19
|
+
if (sceneDirs.length > 0) {
|
|
20
20
|
debug(
|
|
21
|
-
'
|
|
22
|
-
|
|
21
|
+
'Scene directories: %o',
|
|
22
|
+
sceneDirs.map((d) => path.basename(d)),
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
console.log(`🚀 Found ${
|
|
27
|
-
for (const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
console.log(`🚀 Found ${sceneDirs.length} scenes`);
|
|
27
|
+
for (const sceneDir of sceneDirs) {
|
|
28
|
+
const sceneName = path.basename(sceneDir);
|
|
29
|
+
debugScene('Processing scene: %s', sceneName);
|
|
30
|
+
debugScene(' Path: %s', sceneDir);
|
|
31
31
|
|
|
32
|
-
console.log(`📂 Processing
|
|
32
|
+
console.log(`📂 Processing scene: ${sceneName}`);
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
await
|
|
36
|
-
|
|
35
|
+
await runAllMoments(sceneDir);
|
|
36
|
+
debugScene('Scene %s completed successfully', sceneName);
|
|
37
37
|
} catch (error) {
|
|
38
|
-
|
|
38
|
+
debugScene('ERROR: Scene %s failed: %O', sceneName, error);
|
|
39
39
|
throw error;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
debug('All %d
|
|
44
|
-
console.log('✅ All
|
|
43
|
+
debug('All %d scenes processed successfully', sceneDirs.length);
|
|
44
|
+
console.log('✅ All scenes processed');
|
|
45
45
|
}
|
package/src/agent/runSlice.ts
CHANGED
|
@@ -34,34 +34,34 @@ export type VitestTestResult = {
|
|
|
34
34
|
assertionResults: VitestAssertionResult[];
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export async function
|
|
38
|
-
const
|
|
39
|
-
console.log(`✏️ Implementing slice: ${
|
|
40
|
-
const contextFiles = await loadContextFiles(
|
|
37
|
+
export async function runMoment(momentDir: string, scene: string): Promise<void> {
|
|
38
|
+
const momentName = path.basename(momentDir);
|
|
39
|
+
console.log(`✏️ Implementing slice: ${momentName} for scene: ${scene}`);
|
|
40
|
+
const contextFiles = await loadContextFiles(momentDir);
|
|
41
41
|
const filesToImplement = findFilesToImplement(contextFiles, needsImplementation);
|
|
42
42
|
for (const [targetFile] of filesToImplement) {
|
|
43
|
-
await implementFileFromAI(
|
|
43
|
+
await implementFileFromAI(momentDir, targetFile, contextFiles);
|
|
44
44
|
}
|
|
45
|
-
const result = await runTestsAndTypecheck(
|
|
46
|
-
reportTestAndTypecheckResults(
|
|
45
|
+
const result = await runTestsAndTypecheck(momentDir);
|
|
46
|
+
reportTestAndTypecheckResults(momentDir, scene, result);
|
|
47
47
|
if (result.success) {
|
|
48
48
|
console.log(`✅ All tests and checks passed on first attempt.`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
await retryFailedFiles(
|
|
51
|
+
await retryFailedFiles(momentDir, scene, result);
|
|
52
52
|
if (result.failedTestFiles.length > 0) {
|
|
53
|
-
await retryFailedTests(
|
|
53
|
+
await retryFailedTests(momentDir, scene, result);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async function retryFailedFiles(
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
momentDir: string,
|
|
59
|
+
scene: string,
|
|
60
60
|
initialResult: TestAndTypecheckResult,
|
|
61
61
|
depth = 0,
|
|
62
62
|
): Promise<TestAndTypecheckResult> {
|
|
63
63
|
const MAX_RECURSION_DEPTH = 2;
|
|
64
|
-
let contextFiles = await loadContextFiles(
|
|
64
|
+
let contextFiles = await loadContextFiles(momentDir);
|
|
65
65
|
let result = initialResult;
|
|
66
66
|
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
67
67
|
if (result.failedTypecheckFiles.length === 0) {
|
|
@@ -69,18 +69,18 @@ async function retryFailedFiles(
|
|
|
69
69
|
break;
|
|
70
70
|
}
|
|
71
71
|
console.log(`🔁 Typecheck retry attempt ${attempt} for ${result.failedTypecheckFiles.length} files...`);
|
|
72
|
-
contextFiles = await loadContextFiles(
|
|
72
|
+
contextFiles = await loadContextFiles(momentDir);
|
|
73
73
|
for (const filePath of result.failedTypecheckFiles) {
|
|
74
74
|
const fileName = path.basename(filePath);
|
|
75
75
|
const retryPrompt = buildRetryPrompt(fileName, contextFiles, result.testErrors, result.typecheckErrors);
|
|
76
|
-
console.log(`🔧 Retrying typecheck error in ${fileName} in
|
|
76
|
+
console.log(`🔧 Retrying typecheck error in ${fileName} in scene ${scene}...`);
|
|
77
77
|
const { text: aiOutput } = await generateText({ model: createModelFromEnv(), prompt: retryPrompt });
|
|
78
78
|
const cleanedCode = extractCodeBlock(aiOutput);
|
|
79
|
-
await writeFile(path.join(
|
|
79
|
+
await writeFile(path.join(momentDir, fileName), cleanedCode, 'utf-8');
|
|
80
80
|
console.log(`♻️ Updated ${fileName} to fix typecheck errors`);
|
|
81
81
|
}
|
|
82
|
-
result = await runTestsAndTypecheck(
|
|
83
|
-
reportTestAndTypecheckResults(
|
|
82
|
+
result = await runTestsAndTypecheck(momentDir);
|
|
83
|
+
reportTestAndTypecheckResults(momentDir, scene, result);
|
|
84
84
|
}
|
|
85
85
|
if (result.failedTypecheckFiles.length > 0) {
|
|
86
86
|
if (depth >= MAX_RECURSION_DEPTH) {
|
|
@@ -93,10 +93,10 @@ async function retryFailedFiles(
|
|
|
93
93
|
testErrors: '', // Clear test errors since we're only fixing typecheck
|
|
94
94
|
failedTestFiles: [], // Clear failed test files
|
|
95
95
|
};
|
|
96
|
-
result = await retryFailedFiles(
|
|
96
|
+
result = await retryFailedFiles(momentDir, scene, typecheckOnlyResult, depth + 1);
|
|
97
97
|
// After fixing typecheck, re-run everything to get fresh results
|
|
98
|
-
const freshResult = await runTestsAndTypecheck(
|
|
99
|
-
reportTestAndTypecheckResults(
|
|
98
|
+
const freshResult = await runTestsAndTypecheck(momentDir);
|
|
99
|
+
reportTestAndTypecheckResults(momentDir, scene, freshResult);
|
|
100
100
|
result = freshResult;
|
|
101
101
|
if (result.failedTestFiles.length === 0) {
|
|
102
102
|
console.log(`✅ All test issues resolved after fixing type errors.`);
|
|
@@ -154,8 +154,8 @@ Return only the corrected full contents of ${targetFile}, no commentary, no mark
|
|
|
154
154
|
`.trim();
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
async function implementFileFromAI(
|
|
158
|
-
const filePath = path.join(
|
|
157
|
+
async function implementFileFromAI(momentDir: string, targetFile: string, contextFiles: Record<string, string>) {
|
|
158
|
+
const filePath = path.join(momentDir, targetFile);
|
|
159
159
|
const prompt = buildInitialPrompt(targetFile, contextFiles);
|
|
160
160
|
console.log(`🔮 Analysing and Implementing ${targetFile}`);
|
|
161
161
|
const { text: aiOutput } = await generateText({ model: createModelFromEnv(), prompt });
|
|
@@ -165,12 +165,12 @@ async function implementFileFromAI(sliceDir: string, targetFile: string, context
|
|
|
165
165
|
console.log(`♻ Implemented ${targetFile}`);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
export async function runTestsAndTypecheck(
|
|
169
|
-
const rootDir = await findProjectRoot(
|
|
170
|
-
const testResult = await runTests(
|
|
171
|
-
const typecheckResult = await runTypecheck(
|
|
172
|
-
const shadowResult = await
|
|
173
|
-
const assertionResult = await
|
|
168
|
+
export async function runTestsAndTypecheck(momentDir: string): Promise<TestAndTypecheckResult> {
|
|
169
|
+
const rootDir = await findProjectRoot(momentDir);
|
|
170
|
+
const testResult = await runTests(momentDir, rootDir);
|
|
171
|
+
const typecheckResult = await runTypecheck(momentDir, rootDir);
|
|
172
|
+
const shadowResult = await detectShadowsInMoment(momentDir);
|
|
173
|
+
const assertionResult = await detectAssertionsInMoment(momentDir);
|
|
174
174
|
const failedTypecheckFiles = [
|
|
175
175
|
...typecheckResult.failedTypecheckFiles,
|
|
176
176
|
...shadowResult.failedFiles,
|
|
@@ -193,38 +193,38 @@ export async function runTestsAndTypecheck(sliceDir: string): Promise<TestAndTyp
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
export async function
|
|
197
|
-
const files = await fg(['*.ts'], { cwd:
|
|
196
|
+
export async function detectShadowsInMoment(momentDir: string): Promise<{ errors: string; failedFiles: string[] }> {
|
|
197
|
+
const files = await fg(['*.ts'], { cwd: momentDir, ignore: ['*.spec.ts', '*.specs.ts', '*.test.ts'] });
|
|
198
198
|
const errors: string[] = [];
|
|
199
199
|
const failedFiles: string[] = [];
|
|
200
200
|
for (const file of files) {
|
|
201
|
-
const content = await readFile(path.join(
|
|
201
|
+
const content = await readFile(path.join(momentDir, file), 'utf-8');
|
|
202
202
|
const warning = buildShadowWarning(content, file);
|
|
203
203
|
if (warning.length > 0) {
|
|
204
204
|
errors.push(warning);
|
|
205
|
-
failedFiles.push(path.join(
|
|
205
|
+
failedFiles.push(path.join(momentDir, file));
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
return { errors: errors.join('\n'), failedFiles };
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
export async function
|
|
212
|
-
const files = await fg(['*.ts'], { cwd:
|
|
211
|
+
export async function detectAssertionsInMoment(momentDir: string): Promise<{ errors: string; failedFiles: string[] }> {
|
|
212
|
+
const files = await fg(['*.ts'], { cwd: momentDir, ignore: ['*.spec.ts', '*.specs.ts', '*.test.ts'] });
|
|
213
213
|
const errors: string[] = [];
|
|
214
214
|
const failedFiles: string[] = [];
|
|
215
215
|
for (const file of files) {
|
|
216
|
-
const content = await readFile(path.join(
|
|
216
|
+
const content = await readFile(path.join(momentDir, file), 'utf-8');
|
|
217
217
|
const warning = buildTypeAssertionWarning(content, file);
|
|
218
218
|
if (warning.length > 0) {
|
|
219
219
|
errors.push(warning);
|
|
220
|
-
failedFiles.push(path.join(
|
|
220
|
+
failedFiles.push(path.join(momentDir, file));
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
return { errors: errors.join('\n'), failedFiles };
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
async function retryFailedTests(
|
|
227
|
-
let contextFiles = await loadContextFiles(
|
|
226
|
+
async function retryFailedTests(momentDir: string, scene: string, result: TestAndTypecheckResult) {
|
|
227
|
+
let contextFiles = await loadContextFiles(momentDir);
|
|
228
228
|
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
229
229
|
if (result.failedTestFiles.length === 0) {
|
|
230
230
|
console.log(`✅ Test failures resolved after attempt ${attempt - 1}`);
|
|
@@ -266,34 +266,34 @@ No commentary or markdown outside the code block.
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
const [, fileName, code] = match;
|
|
269
|
-
const absPath = path.join(
|
|
269
|
+
const absPath = path.join(momentDir, fileName.trim());
|
|
270
270
|
console.log('🔧 Applying AI fix to:', absPath);
|
|
271
271
|
await writeFile(absPath, code.trim(), 'utf-8');
|
|
272
272
|
console.log(`♻️ Updated ${fileName.trim()} to fix tests`);
|
|
273
|
-
contextFiles = await loadContextFiles(
|
|
274
|
-
result = await runTestsAndTypecheck(
|
|
275
|
-
reportTestAndTypecheckResults(
|
|
273
|
+
contextFiles = await loadContextFiles(momentDir);
|
|
274
|
+
result = await runTestsAndTypecheck(momentDir);
|
|
275
|
+
reportTestAndTypecheckResults(momentDir, scene, result);
|
|
276
276
|
// If test fix introduced a new type error, handle it before continuing
|
|
277
277
|
if (result.failedTypecheckFiles.length > 0) {
|
|
278
278
|
console.log(`⚠️ Fixing tests caused typecheck errors. Retrying typecheck fixes...`);
|
|
279
|
-
result = await retryFailedFiles(
|
|
279
|
+
result = await retryFailedFiles(momentDir, scene, result);
|
|
280
280
|
if (result.failedTestFiles.length === 0) {
|
|
281
281
|
console.log(`✅ All test issues resolved after fixing type errors.`);
|
|
282
282
|
break;
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
|
-
contextFiles = await loadContextFiles(
|
|
285
|
+
contextFiles = await loadContextFiles(momentDir);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
if (result.failedTestFiles.length > 0) {
|
|
289
289
|
console.error(`❌ Some test failures remain after retry attempts.`);
|
|
290
290
|
for (const file of result.failedTestFiles) {
|
|
291
|
-
console.log(` - ${path.relative(
|
|
291
|
+
console.log(` - ${path.relative(momentDir, file)}`);
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
async function runTypecheck(
|
|
296
|
+
async function runTypecheck(momentDir: string, rootDir: string) {
|
|
297
297
|
try {
|
|
298
298
|
const result = await execa('npx', ['tsc', '--noEmit'], {
|
|
299
299
|
cwd: rootDir,
|
|
@@ -302,14 +302,14 @@ async function runTypecheck(sliceDir: string, rootDir: string) {
|
|
|
302
302
|
});
|
|
303
303
|
const output = (result.stdout ?? '') + (result.stderr ?? '');
|
|
304
304
|
if (result.exitCode !== 0 || output.includes('error')) {
|
|
305
|
-
return await processTypecheckOutput(output,
|
|
305
|
+
return await processTypecheckOutput(output, momentDir, rootDir);
|
|
306
306
|
}
|
|
307
307
|
return { success: true, typecheckErrors: '', failedTypecheckFiles: [] };
|
|
308
308
|
} catch (err: unknown) {
|
|
309
309
|
const execaErr = err as { stdout?: string; stderr?: string };
|
|
310
310
|
const output = (execaErr.stdout ?? '') + (execaErr.stderr ?? '');
|
|
311
311
|
console.error('TypeScript execution error:', output);
|
|
312
|
-
const files = await fg(['*.ts'], { cwd:
|
|
312
|
+
const files = await fg(['*.ts'], { cwd: momentDir, absolute: true });
|
|
313
313
|
return { success: false, typecheckErrors: output, failedTypecheckFiles: files };
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -322,7 +322,7 @@ function getTypecheckPatterns(): RegExp[] {
|
|
|
322
322
|
];
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
function extractFailedFiles(output: string, patterns: RegExp[], rootDir: string,
|
|
325
|
+
function extractFailedFiles(output: string, patterns: RegExp[], rootDir: string, momentDir?: string): string[] {
|
|
326
326
|
const failedFiles = new Set<string>();
|
|
327
327
|
|
|
328
328
|
for (const pattern of patterns) {
|
|
@@ -330,9 +330,9 @@ function extractFailedFiles(output: string, patterns: RegExp[], rootDir: string,
|
|
|
330
330
|
const filePath = match[1] ? path.resolve(rootDir, match[1]) : '';
|
|
331
331
|
|
|
332
332
|
const notNodeModules = !filePath.includes('node_modules');
|
|
333
|
-
const
|
|
333
|
+
const inMoment = momentDir === undefined || filePath.startsWith(momentDir);
|
|
334
334
|
|
|
335
|
-
if (notNodeModules &&
|
|
335
|
+
if (notNodeModules && inMoment) {
|
|
336
336
|
failedFiles.add(filePath);
|
|
337
337
|
}
|
|
338
338
|
}
|
|
@@ -341,15 +341,15 @@ function extractFailedFiles(output: string, patterns: RegExp[], rootDir: string,
|
|
|
341
341
|
return Array.from(failedFiles);
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
async function processTypecheckOutput(output: string,
|
|
345
|
-
const relativePath = path.relative(rootDir,
|
|
344
|
+
async function processTypecheckOutput(output: string, momentDir: string, rootDir: string) {
|
|
345
|
+
const relativePath = path.relative(rootDir, momentDir);
|
|
346
346
|
const filtered = output
|
|
347
347
|
.split('\n')
|
|
348
348
|
.filter((line) => {
|
|
349
349
|
const hasError = line.includes('error TS') || line.includes('): error');
|
|
350
350
|
const notNodeModules = !line.includes('node_modules');
|
|
351
|
-
const
|
|
352
|
-
return hasError && notNodeModules &&
|
|
351
|
+
const hasMomentPath = line.includes(relativePath) || line.includes(momentDir);
|
|
352
|
+
return hasError && notNodeModules && hasMomentPath;
|
|
353
353
|
})
|
|
354
354
|
.join('\n');
|
|
355
355
|
|
|
@@ -357,7 +357,7 @@ async function processTypecheckOutput(output: string, sliceDir: string, rootDir:
|
|
|
357
357
|
return { success: true, typecheckErrors: '', failedTypecheckFiles: [] };
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
const failedFiles = await processTypecheckFailure(filtered, rootDir,
|
|
360
|
+
const failedFiles = await processTypecheckFailure(filtered, rootDir, momentDir);
|
|
361
361
|
return {
|
|
362
362
|
success: false,
|
|
363
363
|
typecheckErrors: filtered,
|
|
@@ -365,12 +365,12 @@ async function processTypecheckOutput(output: string, sliceDir: string, rootDir:
|
|
|
365
365
|
};
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
async function processTypecheckFailure(output: string, rootDir: string,
|
|
368
|
+
async function processTypecheckFailure(output: string, rootDir: string, momentDir: string): Promise<string[]> {
|
|
369
369
|
const patterns = getTypecheckPatterns();
|
|
370
|
-
let failed = extractFailedFiles(output, patterns, rootDir,
|
|
370
|
+
let failed = extractFailedFiles(output, patterns, rootDir, momentDir);
|
|
371
371
|
|
|
372
372
|
if (failed.length === 0 && output.includes('error')) {
|
|
373
|
-
failed = await fg(['*.ts'], { cwd:
|
|
373
|
+
failed = await fg(['*.ts'], { cwd: momentDir, absolute: true });
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
return failed;
|
|
@@ -389,22 +389,22 @@ async function findProjectRoot(startDir: string): Promise<string> {
|
|
|
389
389
|
throw new Error('❌ Could not find project root');
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
function reportTestAndTypecheckResults(
|
|
393
|
-
const
|
|
392
|
+
function reportTestAndTypecheckResults(momentDir: string, scene: string, result: TestAndTypecheckResult) {
|
|
393
|
+
const momentName = path.basename(momentDir);
|
|
394
394
|
if (result.success) {
|
|
395
|
-
console.log(`✅ All Tests and checks passed for: ${
|
|
395
|
+
console.log(`✅ All Tests and checks passed for: ${momentName} in scene ${scene}`);
|
|
396
396
|
return;
|
|
397
397
|
}
|
|
398
|
-
console.error(`❌ ${
|
|
398
|
+
console.error(`❌ ${momentName} in scene ${scene} failed tests or type-checks.`);
|
|
399
399
|
if (result.failedTestFiles.length) {
|
|
400
|
-
const files = result.failedTestFiles.map((f) => path.relative(
|
|
400
|
+
const files = result.failedTestFiles.map((f) => path.relative(momentDir, f));
|
|
401
401
|
console.log(`🧪 Failed test files: ${files.join(', ')}`);
|
|
402
402
|
if (result.testErrors) {
|
|
403
403
|
console.log(`📝 Test errors:\n${result.testErrors}`);
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
if (result.failedTypecheckFiles.length) {
|
|
407
|
-
const files = result.failedTypecheckFiles.map((f) => path.relative(
|
|
407
|
+
const files = result.failedTypecheckFiles.map((f) => path.relative(momentDir, f));
|
|
408
408
|
console.log(`📐 Failed typecheck files: ${files.join(', ')}`);
|
|
409
409
|
if (result.typecheckErrors) {
|
|
410
410
|
console.log(`📝 Typecheck errors:\n${result.typecheckErrors}`);
|
package/src/agent/runTests.ts
CHANGED
|
@@ -15,17 +15,17 @@ export type TestResult = {
|
|
|
15
15
|
failedTestFiles: string[];
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export async function runTests(
|
|
19
|
-
console.log(`🔍 Running tests in ${path.basename(
|
|
20
|
-
const testFiles = await fg(['*.spec.ts', '*.specs.ts'], { cwd:
|
|
18
|
+
export async function runTests(momentDir: string, rootDir: string): Promise<TestResult> {
|
|
19
|
+
console.log(`🔍 Running tests in ${path.basename(momentDir)}...`);
|
|
20
|
+
const testFiles = await fg(['*.spec.ts', '*.specs.ts'], { cwd: momentDir });
|
|
21
21
|
if (testFiles.length === 0) {
|
|
22
|
-
console.warn(`⚠️ No test files found in ${
|
|
22
|
+
console.warn(`⚠️ No test files found in ${momentDir}`);
|
|
23
23
|
return { success: true, testErrors: '', failedTestFiles: [] };
|
|
24
24
|
} else {
|
|
25
|
-
console.log(`🔍 Found test files in ${path.basename(
|
|
25
|
+
console.log(`🔍 Found test files in ${path.basename(momentDir)}:`, testFiles);
|
|
26
26
|
}
|
|
27
|
-
const relativePaths = testFiles.map((f) => path.join(
|
|
28
|
-
const reportPath = path.join(
|
|
27
|
+
const relativePaths = testFiles.map((f) => path.join(momentDir, f).replace(`${rootDir}/`, ''));
|
|
28
|
+
const reportPath = path.join(momentDir, 'vitest-results.json');
|
|
29
29
|
try {
|
|
30
30
|
const { stdout, stderr } = await execa(
|
|
31
31
|
'npx',
|
|
@@ -52,9 +52,9 @@ export async function runTests(sliceDir: string, rootDir: string): Promise<TestR
|
|
|
52
52
|
const fileName = r.name ?? r.file;
|
|
53
53
|
if (!fileName) {
|
|
54
54
|
console.warn('⚠️ Skipping test result with missing name or file:', r);
|
|
55
|
-
return path.join(
|
|
55
|
+
return path.join(momentDir, 'unknown');
|
|
56
56
|
}
|
|
57
|
-
return path.join(
|
|
57
|
+
return path.join(momentDir, path.basename(fileName));
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
const failedTestSummaries = testResults
|
|
@@ -87,7 +87,7 @@ export async function runTests(sliceDir: string, rootDir: string): Promise<TestR
|
|
|
87
87
|
return {
|
|
88
88
|
success: false,
|
|
89
89
|
testErrors: stdout || stderr || 'Failed to parse test results',
|
|
90
|
-
failedTestFiles: testFiles.map((f) => path.join(
|
|
90
|
+
failedTestFiles: testFiles.map((f) => path.join(momentDir, f)),
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
} else {
|
|
@@ -95,7 +95,7 @@ export async function runTests(sliceDir: string, rootDir: string): Promise<TestR
|
|
|
95
95
|
return {
|
|
96
96
|
success: false,
|
|
97
97
|
testErrors: stdout || stderr || 'Test execution failed - no report generated',
|
|
98
|
-
failedTestFiles: testFiles.map((f) => path.join(
|
|
98
|
+
failedTestFiles: testFiles.map((f) => path.join(momentDir, f)),
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
} catch (err: unknown) {
|
|
@@ -105,7 +105,7 @@ export async function runTests(sliceDir: string, rootDir: string): Promise<TestR
|
|
|
105
105
|
return {
|
|
106
106
|
success: false,
|
|
107
107
|
testErrors: output || 'Test execution failed with no output',
|
|
108
|
-
failedTestFiles: testFiles.map((f) => path.join(
|
|
108
|
+
failedTestFiles: testFiles.map((f) => path.join(momentDir, f)),
|
|
109
109
|
};
|
|
110
110
|
} finally {
|
|
111
111
|
// Clean up the report file
|
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { type Command, defineCommandHandler, type Event } from '@auto-engineer/message-bus';
|
|
4
4
|
import createDebug from 'debug';
|
|
5
|
-
import {
|
|
5
|
+
import { runScenes } from '../agent/runFlows';
|
|
6
6
|
|
|
7
7
|
const debug = createDebug('auto:server-implementer:command');
|
|
8
8
|
const debugHandler = createDebug('auto:server-implementer:command:handler');
|
|
@@ -20,7 +20,7 @@ export type ServerImplementedEvent = Event<
|
|
|
20
20
|
'ServerImplemented',
|
|
21
21
|
{
|
|
22
22
|
serverDirectory: string;
|
|
23
|
-
|
|
23
|
+
scenesImplemented: number;
|
|
24
24
|
}
|
|
25
25
|
>;
|
|
26
26
|
|
|
@@ -64,8 +64,8 @@ export const commandHandler = defineCommandHandler<
|
|
|
64
64
|
if (result.type === 'ServerImplemented') {
|
|
65
65
|
debug('Command handler completed: success');
|
|
66
66
|
debug('✅ Server implementation completed successfully');
|
|
67
|
-
if (result.data.
|
|
68
|
-
debug(' %d
|
|
67
|
+
if (result.data.scenesImplemented > 0) {
|
|
68
|
+
debug(' %d scenes implemented', result.data.scenesImplemented);
|
|
69
69
|
}
|
|
70
70
|
} else {
|
|
71
71
|
debug('Command handler completed: failure - %s', result.data.error);
|
|
@@ -87,19 +87,19 @@ async function handleImplementServerCommandInternal(
|
|
|
87
87
|
|
|
88
88
|
try {
|
|
89
89
|
const serverRoot = path.resolve(serverDirectory);
|
|
90
|
-
const
|
|
90
|
+
const scenesDir = path.join(serverRoot, 'src', 'domain', 'narratives');
|
|
91
91
|
|
|
92
92
|
debugHandler('Resolved paths:');
|
|
93
93
|
debugHandler(' Server root: %s', serverRoot);
|
|
94
|
-
debugHandler('
|
|
94
|
+
debugHandler(' Scenes directory: %s', scenesDir);
|
|
95
95
|
|
|
96
|
-
if (!existsSync(
|
|
97
|
-
debugHandler('ERROR:
|
|
96
|
+
if (!existsSync(scenesDir)) {
|
|
97
|
+
debugHandler('ERROR: Scenes directory not found at %s', scenesDir);
|
|
98
98
|
return {
|
|
99
99
|
type: 'ServerImplementationFailed',
|
|
100
100
|
data: {
|
|
101
101
|
serverDirectory,
|
|
102
|
-
error: `
|
|
102
|
+
error: `Scenes directory not found at: ${scenesDir}`,
|
|
103
103
|
},
|
|
104
104
|
timestamp: new Date(),
|
|
105
105
|
requestId: command.requestId,
|
|
@@ -107,25 +107,25 @@ async function handleImplementServerCommandInternal(
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
debugProcess('Starting
|
|
110
|
+
debugProcess('Starting scene runner for directory: %s', scenesDir);
|
|
111
111
|
|
|
112
|
-
// Run the
|
|
113
|
-
await
|
|
112
|
+
// Run the scenes directly without spawning
|
|
113
|
+
await runScenes(scenesDir);
|
|
114
114
|
|
|
115
|
-
debugProcess('
|
|
115
|
+
debugProcess('Scene runner completed successfully');
|
|
116
116
|
|
|
117
|
-
// For now, we don't have a way to count
|
|
118
|
-
// This could be enhanced by having
|
|
119
|
-
const
|
|
117
|
+
// For now, we don't have a way to count scenes, so we'll use 0
|
|
118
|
+
// This could be enhanced by having runScenes return statistics
|
|
119
|
+
const scenesImplemented = 0;
|
|
120
120
|
|
|
121
121
|
debugResult('Process succeeded');
|
|
122
|
-
debugResult('
|
|
122
|
+
debugResult('Scenes implemented: %d', scenesImplemented);
|
|
123
123
|
|
|
124
124
|
const successEvent: ServerImplementedEvent = {
|
|
125
125
|
type: 'ServerImplemented',
|
|
126
126
|
data: {
|
|
127
127
|
serverDirectory,
|
|
128
|
-
|
|
128
|
+
scenesImplemented,
|
|
129
129
|
},
|
|
130
130
|
timestamp: new Date(),
|
|
131
131
|
requestId: command.requestId,
|