@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
|
@@ -17,10 +17,10 @@ const debugHandler = createDebug('auto:server-implementer:slice:handler');
|
|
|
17
17
|
const debugProcess = createDebug('auto:server-implementer:slice:process');
|
|
18
18
|
const debugResult = createDebug('auto:server-implementer:slice:result');
|
|
19
19
|
|
|
20
|
-
export type
|
|
21
|
-
'
|
|
20
|
+
export type ImplementMomentCommand = Command<
|
|
21
|
+
'ImplementMoment',
|
|
22
22
|
{
|
|
23
|
-
|
|
23
|
+
momentPath: string;
|
|
24
24
|
context?: {
|
|
25
25
|
previousOutputs?: string;
|
|
26
26
|
attemptNumber?: number;
|
|
@@ -31,36 +31,36 @@ export type ImplementSliceCommand = Command<
|
|
|
31
31
|
}
|
|
32
32
|
>;
|
|
33
33
|
|
|
34
|
-
export type
|
|
35
|
-
'
|
|
34
|
+
export type MomentImplementedEvent = Event<
|
|
35
|
+
'MomentImplemented',
|
|
36
36
|
{
|
|
37
|
-
|
|
37
|
+
momentPath: string;
|
|
38
38
|
filesImplemented: string[];
|
|
39
39
|
}
|
|
40
40
|
>;
|
|
41
41
|
|
|
42
|
-
export type
|
|
43
|
-
'
|
|
42
|
+
export type MomentImplementationFailedEvent = Event<
|
|
43
|
+
'MomentImplementationFailed',
|
|
44
44
|
{
|
|
45
|
-
|
|
45
|
+
momentPath: string;
|
|
46
46
|
error: string;
|
|
47
47
|
}
|
|
48
48
|
>;
|
|
49
49
|
|
|
50
|
-
export type
|
|
50
|
+
export type ImplementMomentEvents = MomentImplementedEvent | MomentImplementationFailedEvent;
|
|
51
51
|
|
|
52
52
|
export const commandHandler = defineCommandHandler<
|
|
53
|
-
|
|
54
|
-
(command:
|
|
53
|
+
ImplementMomentCommand,
|
|
54
|
+
(command: ImplementMomentCommand) => Promise<MomentImplementedEvent | MomentImplementationFailedEvent>
|
|
55
55
|
>({
|
|
56
|
-
name: '
|
|
57
|
-
displayName: 'Implement
|
|
56
|
+
name: 'ImplementMoment',
|
|
57
|
+
displayName: 'Implement Moment',
|
|
58
58
|
alias: 'implement:slice',
|
|
59
59
|
description: 'AI implements a specific server slice',
|
|
60
60
|
category: 'implement',
|
|
61
61
|
icon: 'layers',
|
|
62
62
|
fields: {
|
|
63
|
-
|
|
63
|
+
momentPath: {
|
|
64
64
|
description: 'Path to the slice directory to implement',
|
|
65
65
|
required: true,
|
|
66
66
|
},
|
|
@@ -74,24 +74,26 @@ export const commandHandler = defineCommandHandler<
|
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
76
|
examples: [
|
|
77
|
-
'$ auto implement:slice --slice-path=./server/src/domain/
|
|
77
|
+
'$ auto implement:slice --slice-path=./server/src/domain/narratives/seasonal-assistant/enters-shopping-criteria-into-assistant',
|
|
78
78
|
],
|
|
79
79
|
events: [
|
|
80
|
-
{ name: '
|
|
81
|
-
{ name: '
|
|
80
|
+
{ name: 'MomentImplemented', displayName: 'Moment Implemented' },
|
|
81
|
+
{ name: 'MomentImplementationFailed', displayName: 'Moment Implementation Failed' },
|
|
82
82
|
],
|
|
83
|
-
handle: async (
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
handle: async (
|
|
84
|
+
command: ImplementMomentCommand,
|
|
85
|
+
): Promise<MomentImplementedEvent | MomentImplementationFailedEvent> => {
|
|
86
|
+
debug('CommandHandler executing for ImplementMoment');
|
|
87
|
+
const result = await handleImplementMomentCommand(command);
|
|
86
88
|
|
|
87
|
-
if (result.type === '
|
|
89
|
+
if (result.type === 'MomentImplemented') {
|
|
88
90
|
debug('Command handler completed: success');
|
|
89
|
-
debug('✅
|
|
90
|
-
debug('
|
|
91
|
+
debug('✅ Moment implementation completed successfully');
|
|
92
|
+
debug(' Moment: %s', path.basename(result.data.momentPath));
|
|
91
93
|
debug(' Files implemented: %d', result.data.filesImplemented.length);
|
|
92
94
|
} else {
|
|
93
95
|
debug('Command handler completed: failure - %s', result.data.error);
|
|
94
|
-
debug('❌
|
|
96
|
+
debug('❌ Moment implementation failed: %s', result.data.error);
|
|
95
97
|
}
|
|
96
98
|
return result;
|
|
97
99
|
},
|
|
@@ -155,14 +157,14 @@ Return only the corrected full contents of ${targetFile}, no commentary, no mark
|
|
|
155
157
|
`.trim();
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
async function addMarkersToFiles(
|
|
160
|
+
async function addMarkersToFiles(momentPath: string, contextFiles: Record<string, string>): Promise<void> {
|
|
159
161
|
const filesToMark = Object.entries(contextFiles).filter(([, content]) => needsImplementation(content));
|
|
160
162
|
debugProcess(`Found ${filesToMark.length} files needing implementation markers`);
|
|
161
163
|
|
|
162
164
|
for (const [filename, content] of filesToMark) {
|
|
163
165
|
if (!hasImplementationMarker(content)) {
|
|
164
166
|
const markedContent = addImplementationMarker(content);
|
|
165
|
-
await writeFile(path.join(
|
|
167
|
+
await writeFile(path.join(momentPath, filename), markedContent, 'utf-8');
|
|
166
168
|
contextFiles[filename] = markedContent;
|
|
167
169
|
debugProcess(`Added implementation marker to ${filename}`);
|
|
168
170
|
}
|
|
@@ -170,7 +172,7 @@ async function addMarkersToFiles(slicePath: string, contextFiles: Record<string,
|
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
async function implementFile(
|
|
173
|
-
|
|
175
|
+
momentPath: string,
|
|
174
176
|
targetFile: string,
|
|
175
177
|
contextFiles: Record<string, string>,
|
|
176
178
|
retryContext?: { previousOutputs?: string; attemptNumber?: number },
|
|
@@ -196,27 +198,27 @@ async function implementFile(
|
|
|
196
198
|
let cleanedCode = extractCodeBlock(aiOutput);
|
|
197
199
|
cleanedCode = addImplementationMarker(cleanedCode);
|
|
198
200
|
|
|
199
|
-
const filePath = path.join(
|
|
201
|
+
const filePath = path.join(momentPath, targetFile);
|
|
200
202
|
await writeFile(filePath, cleanedCode, 'utf-8');
|
|
201
203
|
debugProcess(`Successfully implemented ${targetFile}`);
|
|
202
204
|
|
|
203
205
|
contextFiles[targetFile] = cleanedCode;
|
|
204
206
|
}
|
|
205
207
|
|
|
206
|
-
async function
|
|
207
|
-
|
|
208
|
+
async function implementMoment(
|
|
209
|
+
momentPath: string,
|
|
208
210
|
context?: { previousOutputs?: string; attemptNumber?: number },
|
|
209
211
|
aiOptions?: { maxTokens?: number },
|
|
210
212
|
): Promise<{ success: boolean; filesImplemented: string[]; error?: string }> {
|
|
211
|
-
const
|
|
213
|
+
const momentName = path.basename(momentPath);
|
|
212
214
|
|
|
213
|
-
debugProcess(`Implementing slice: ${
|
|
215
|
+
debugProcess(`Implementing slice: ${momentName}`);
|
|
214
216
|
|
|
215
217
|
try {
|
|
216
|
-
const contextFiles = await loadContextFiles(
|
|
218
|
+
const contextFiles = await loadContextFiles(momentPath);
|
|
217
219
|
debugProcess(`Loaded ${Object.keys(contextFiles).join(', ')} files from slice`);
|
|
218
220
|
|
|
219
|
-
await addMarkersToFiles(
|
|
221
|
+
await addMarkersToFiles(momentPath, contextFiles);
|
|
220
222
|
|
|
221
223
|
const filesToImplement = findFilesToImplement(contextFiles, hasImplementationMarker);
|
|
222
224
|
debugProcess(`Found ${filesToImplement.length} files with markers to implement`);
|
|
@@ -228,7 +230,7 @@ async function implementSlice(
|
|
|
228
230
|
|
|
229
231
|
const implementedFiles: string[] = [];
|
|
230
232
|
for (const [targetFile] of filesToImplement) {
|
|
231
|
-
await implementFile(
|
|
233
|
+
await implementFile(momentPath, targetFile, contextFiles, context, aiOptions);
|
|
232
234
|
implementedFiles.push(targetFile);
|
|
233
235
|
}
|
|
234
236
|
|
|
@@ -240,12 +242,12 @@ async function implementSlice(
|
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
244
|
|
|
243
|
-
function logCommandDebugInfo(command:
|
|
244
|
-
const rawData = command.data as
|
|
245
|
-
const
|
|
245
|
+
function logCommandDebugInfo(command: ImplementMomentCommand, resolvedMomentPath?: string): void {
|
|
246
|
+
const rawData = command.data as ImplementMomentCommand['data'] & { path?: string };
|
|
247
|
+
const momentPath = rawData.momentPath ?? rawData.path;
|
|
246
248
|
const { context } = command.data;
|
|
247
|
-
debug('Handling
|
|
248
|
-
debug('
|
|
249
|
+
debug('Handling ImplementMomentCommand');
|
|
250
|
+
debug(' Moment path: %s', resolvedMomentPath ?? momentPath);
|
|
249
251
|
debug(' Context provided: %s', context ? 'yes' : 'no');
|
|
250
252
|
if (context) {
|
|
251
253
|
debug(' Attempt number: %d', context.attemptNumber ?? 1);
|
|
@@ -255,16 +257,16 @@ function logCommandDebugInfo(command: ImplementSliceCommand, resolvedSlicePath?:
|
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
function createFailedEvent(
|
|
258
|
-
command:
|
|
260
|
+
command: ImplementMomentCommand,
|
|
259
261
|
error: string,
|
|
260
|
-
|
|
261
|
-
):
|
|
262
|
-
const rawData = command.data as
|
|
263
|
-
const
|
|
262
|
+
resolvedMomentPath?: string,
|
|
263
|
+
): MomentImplementationFailedEvent {
|
|
264
|
+
const rawData = command.data as ImplementMomentCommand['data'] & { path?: string };
|
|
265
|
+
const momentPath = resolvedMomentPath ?? rawData.momentPath ?? rawData.path;
|
|
264
266
|
return {
|
|
265
|
-
type: '
|
|
267
|
+
type: 'MomentImplementationFailed',
|
|
266
268
|
data: {
|
|
267
|
-
|
|
269
|
+
momentPath,
|
|
268
270
|
error,
|
|
269
271
|
},
|
|
270
272
|
timestamp: new Date(),
|
|
@@ -274,16 +276,16 @@ function createFailedEvent(
|
|
|
274
276
|
}
|
|
275
277
|
|
|
276
278
|
function createSuccessEvent(
|
|
277
|
-
command:
|
|
279
|
+
command: ImplementMomentCommand,
|
|
278
280
|
filesImplemented: string[],
|
|
279
|
-
|
|
280
|
-
):
|
|
281
|
-
const rawData = command.data as
|
|
282
|
-
const
|
|
281
|
+
resolvedMomentPath?: string,
|
|
282
|
+
): MomentImplementedEvent {
|
|
283
|
+
const rawData = command.data as ImplementMomentCommand['data'] & { path?: string };
|
|
284
|
+
const momentPath = resolvedMomentPath ?? rawData.momentPath ?? rawData.path;
|
|
283
285
|
return {
|
|
284
|
-
type: '
|
|
286
|
+
type: 'MomentImplemented',
|
|
285
287
|
data: {
|
|
286
|
-
|
|
288
|
+
momentPath,
|
|
287
289
|
filesImplemented,
|
|
288
290
|
},
|
|
289
291
|
timestamp: new Date(),
|
|
@@ -299,35 +301,35 @@ function logRetryContext(context: { previousOutputs?: string; attemptNumber?: nu
|
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
303
|
|
|
302
|
-
export async function
|
|
303
|
-
command:
|
|
304
|
-
): Promise<
|
|
305
|
-
const rawData = command.data as
|
|
306
|
-
const
|
|
304
|
+
export async function handleImplementMomentCommand(
|
|
305
|
+
command: ImplementMomentCommand,
|
|
306
|
+
): Promise<MomentImplementedEvent | MomentImplementationFailedEvent> {
|
|
307
|
+
const rawData = command.data as ImplementMomentCommand['data'] & { path?: string };
|
|
308
|
+
const momentPath = rawData.momentPath ?? rawData.path;
|
|
307
309
|
const { context } = command.data;
|
|
308
310
|
const aiOptions = command.data.aiOptions;
|
|
309
311
|
|
|
310
|
-
if (
|
|
311
|
-
debugHandler('ERROR: No slice path provided. Expected
|
|
312
|
-
return createFailedEvent(command, 'No slice path provided. Expected
|
|
312
|
+
if (momentPath === undefined || momentPath === null || momentPath === '') {
|
|
313
|
+
debugHandler('ERROR: No slice path provided. Expected momentPath or path parameter');
|
|
314
|
+
return createFailedEvent(command, 'No slice path provided. Expected momentPath parameter');
|
|
313
315
|
}
|
|
314
316
|
|
|
315
|
-
logCommandDebugInfo(command,
|
|
317
|
+
logCommandDebugInfo(command, momentPath);
|
|
316
318
|
|
|
317
319
|
try {
|
|
318
|
-
const sliceRoot = path.resolve(
|
|
320
|
+
const sliceRoot = path.resolve(momentPath);
|
|
319
321
|
debugHandler('Resolved paths:');
|
|
320
|
-
debugHandler('
|
|
322
|
+
debugHandler(' Moment root: %s', sliceRoot);
|
|
321
323
|
|
|
322
324
|
if (!existsSync(sliceRoot)) {
|
|
323
|
-
debugHandler('ERROR:
|
|
324
|
-
return createFailedEvent(command, `
|
|
325
|
+
debugHandler('ERROR: Moment directory not found at %s', sliceRoot);
|
|
326
|
+
return createFailedEvent(command, `Moment directory not found at: ${sliceRoot}`, sliceRoot);
|
|
325
327
|
}
|
|
326
328
|
|
|
327
329
|
debugProcess('Starting slice implementation for: %s', sliceRoot);
|
|
328
330
|
logRetryContext(context);
|
|
329
331
|
|
|
330
|
-
const result = await
|
|
332
|
+
const result = await implementMoment(sliceRoot, context, aiOptions);
|
|
331
333
|
|
|
332
334
|
if (!result.success) {
|
|
333
335
|
return createFailedEvent(command, result.error ?? 'Implementation failed', sliceRoot);
|
|
@@ -336,13 +338,13 @@ export async function handleImplementSliceCommand(
|
|
|
336
338
|
debugResult('Process succeeded');
|
|
337
339
|
debugResult('Files implemented: %d', result.filesImplemented.length);
|
|
338
340
|
debugResult('Files: %s', result.filesImplemented.join(', '));
|
|
339
|
-
debugResult('Returning success event:
|
|
341
|
+
debugResult('Returning success event: MomentImplemented');
|
|
340
342
|
|
|
341
343
|
return createSuccessEvent(command, result.filesImplemented, sliceRoot);
|
|
342
344
|
} catch (error) {
|
|
343
345
|
debug('ERROR: Exception caught: %O', error);
|
|
344
346
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
345
|
-
return createFailedEvent(command, errorMessage,
|
|
347
|
+
return createFailedEvent(command, errorMessage, momentPath);
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { commandHandler as implementServerHandler } from './commands/implement-server';
|
|
2
|
-
import { commandHandler as
|
|
2
|
+
import { commandHandler as implementMomentHandler } from './commands/implement-slice';
|
|
3
3
|
|
|
4
|
-
export const COMMANDS = [implementServerHandler,
|
|
5
|
-
export { implementServerHandler,
|
|
4
|
+
export const COMMANDS = [implementServerHandler, implementMomentHandler];
|
|
5
|
+
export { implementServerHandler, implementMomentHandler };
|
|
6
6
|
export type {
|
|
7
7
|
ImplementServerCommand,
|
|
8
8
|
ImplementServerEvents,
|
|
@@ -10,9 +10,9 @@ export type {
|
|
|
10
10
|
ServerImplementedEvent,
|
|
11
11
|
} from './commands/implement-server';
|
|
12
12
|
export {
|
|
13
|
-
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
type
|
|
17
|
-
type
|
|
13
|
+
handleImplementMomentCommand,
|
|
14
|
+
type ImplementMomentCommand,
|
|
15
|
+
type ImplementMomentEvents,
|
|
16
|
+
type MomentImplementationFailedEvent,
|
|
17
|
+
type MomentImplementedEvent,
|
|
18
18
|
} from './commands/implement-slice';
|
|
@@ -5,7 +5,7 @@ You are a software engineer implementing @auto-implement files in a sliced event
|
|
|
5
5
|
|
|
6
6
|
- Architecture: event-sourced CQRS with @event-driven-io/emmett
|
|
7
7
|
- Language: TypeScript with type-graphql
|
|
8
|
-
-
|
|
8
|
+
- Structured moments: Command, Reaction, Query moments
|
|
9
9
|
- Each slice has scaffolded files with implementation instructions marked with comments or TODOs
|
|
10
10
|
|
|
11
11
|
## 2. TASK
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
import { buildContextSections } from './buildContextSections';
|
|
3
3
|
|
|
4
4
|
describe('buildContextSections', () => {
|
|
5
|
-
it('separates shared files,
|
|
5
|
+
it('separates shared files, moment files, and spec files into labeled sections', () => {
|
|
6
6
|
const context: Record<string, string> = {
|
|
7
7
|
'decide.ts': 'export function decide() {}',
|
|
8
8
|
'state.ts': 'export type State = {}',
|
|
@@ -20,7 +20,7 @@ describe('buildContextSections', () => {
|
|
|
20
20
|
'export type Foo = string;',
|
|
21
21
|
'',
|
|
22
22
|
'---',
|
|
23
|
-
'🧠 Other files in the same
|
|
23
|
+
'🧠 Other files in the same moment:',
|
|
24
24
|
'// File: state.ts',
|
|
25
25
|
'export type State = {}',
|
|
26
26
|
'',
|
|
@@ -41,7 +41,7 @@ describe('buildContextSections', () => {
|
|
|
41
41
|
const result = buildContextSections('decide.ts', context);
|
|
42
42
|
|
|
43
43
|
expect(result).toBe(
|
|
44
|
-
['---', '🧠 Other files in the same
|
|
44
|
+
['---', '🧠 Other files in the same moment:', '// File: state.ts', 'export type State = {}'].join('\n'),
|
|
45
45
|
);
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -62,7 +62,7 @@ describe('buildContextSections', () => {
|
|
|
62
62
|
'export type Foo = string;',
|
|
63
63
|
'',
|
|
64
64
|
'---',
|
|
65
|
-
'🧠 Other files in the same
|
|
65
|
+
'🧠 Other files in the same moment:',
|
|
66
66
|
'// File: evolve.ts',
|
|
67
67
|
'export function evolve() {}',
|
|
68
68
|
].join('\n'),
|
|
@@ -88,14 +88,14 @@ describe('buildContextSections', () => {
|
|
|
88
88
|
'Use Status.ACTIVE not "active"',
|
|
89
89
|
'',
|
|
90
90
|
'---',
|
|
91
|
-
'🧠 Other files in the same
|
|
91
|
+
'🧠 Other files in the same moment:',
|
|
92
92
|
'// File: evolve.ts',
|
|
93
93
|
'export function evolve() {}',
|
|
94
94
|
].join('\n'),
|
|
95
95
|
);
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
it('excludes target file from
|
|
98
|
+
it('excludes target file from moment files section', () => {
|
|
99
99
|
const context: Record<string, string> = {
|
|
100
100
|
'decide.ts': 'export function decide() {}',
|
|
101
101
|
'evolve.ts': 'export function evolve() {}',
|
|
@@ -5,7 +5,7 @@ export function buildContextSections(
|
|
|
5
5
|
): string {
|
|
6
6
|
const sharedFiles = Object.entries(context).filter(([name]) => name.startsWith('shared/'));
|
|
7
7
|
const specs = Object.entries(context).filter(([name]) => name.endsWith('.specs.ts'));
|
|
8
|
-
const
|
|
8
|
+
const momentFiles = Object.entries(context).filter(
|
|
9
9
|
([name]) => name !== targetFile && !name.startsWith('shared/') && !name.endsWith('.specs.ts'),
|
|
10
10
|
);
|
|
11
11
|
|
|
@@ -18,8 +18,8 @@ export function buildContextSections(
|
|
|
18
18
|
result += '\n\n';
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
result += `---\n🧠 Other files in the same
|
|
22
|
-
result +=
|
|
21
|
+
result += `---\n🧠 Other files in the same moment:\n`;
|
|
22
|
+
result += momentFiles.map(([name, content]) => `// File: ${name}\n${content}`).join('\n\n');
|
|
23
23
|
|
|
24
24
|
if (specs.length > 0) {
|
|
25
25
|
result += `\n\n---\n🧪 Test specifications (READ-ONLY reference):\n`;
|
|
@@ -5,26 +5,26 @@ import { loadContextFiles } from './loadContextFiles';
|
|
|
5
5
|
|
|
6
6
|
describe('loadContextFiles', () => {
|
|
7
7
|
let tmpDir: string;
|
|
8
|
-
let
|
|
8
|
+
let momentDir: string;
|
|
9
9
|
let sharedDir: string;
|
|
10
10
|
|
|
11
11
|
beforeEach(async () => {
|
|
12
12
|
tmpDir = await fs.mkdtemp(path.join(import.meta.dirname, '.tmp-'));
|
|
13
13
|
sharedDir = path.join(tmpDir, 'shared');
|
|
14
|
-
|
|
15
|
-
await fs.mkdir(
|
|
14
|
+
momentDir = path.join(tmpDir, 'src', 'moments', 'some-moment');
|
|
15
|
+
await fs.mkdir(momentDir, { recursive: true });
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
afterEach(async () => {
|
|
19
19
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
it('loads all .ts files from
|
|
23
|
-
await fs.writeFile(path.join(
|
|
24
|
-
await fs.writeFile(path.join(
|
|
25
|
-
await fs.writeFile(path.join(
|
|
22
|
+
it('loads all .ts files from moment directory including specs', async () => {
|
|
23
|
+
await fs.writeFile(path.join(momentDir, 'decide.ts'), 'export function decide() {}');
|
|
24
|
+
await fs.writeFile(path.join(momentDir, 'state.ts'), 'export type State = {}');
|
|
25
|
+
await fs.writeFile(path.join(momentDir, 'decide.specs.ts'), 'it("works", () => {})');
|
|
26
26
|
|
|
27
|
-
const result = await loadContextFiles(
|
|
27
|
+
const result = await loadContextFiles(momentDir);
|
|
28
28
|
|
|
29
29
|
expect(result).toEqual({
|
|
30
30
|
'decide.ts': 'export function decide() {}',
|
|
@@ -35,10 +35,10 @@ describe('loadContextFiles', () => {
|
|
|
35
35
|
|
|
36
36
|
it('integrates shared context files with real filenames', async () => {
|
|
37
37
|
await fs.mkdir(sharedDir, { recursive: true });
|
|
38
|
-
await fs.writeFile(path.join(
|
|
38
|
+
await fs.writeFile(path.join(momentDir, 'decide.ts'), 'export function decide() {}');
|
|
39
39
|
await fs.writeFile(path.join(sharedDir, 'types.ts'), 'export type Foo = string;');
|
|
40
40
|
|
|
41
|
-
const result = await loadContextFiles(
|
|
41
|
+
const result = await loadContextFiles(momentDir);
|
|
42
42
|
|
|
43
43
|
expect(result).toEqual({
|
|
44
44
|
'decide.ts': 'export function decide() {}',
|
|
@@ -46,8 +46,8 @@ describe('loadContextFiles', () => {
|
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it('returns empty record for empty
|
|
50
|
-
const result = await loadContextFiles(
|
|
49
|
+
it('returns empty record for empty moment directory', async () => {
|
|
50
|
+
const result = await loadContextFiles(momentDir);
|
|
51
51
|
|
|
52
52
|
expect(result).toEqual({});
|
|
53
53
|
});
|
|
@@ -3,13 +3,13 @@ import path from 'node:path';
|
|
|
3
3
|
import fg from 'fast-glob';
|
|
4
4
|
import { loadSharedContext } from './loadSharedContext';
|
|
5
5
|
|
|
6
|
-
export async function loadContextFiles(
|
|
7
|
-
const files = await fg(['*.ts'], { cwd:
|
|
6
|
+
export async function loadContextFiles(momentDir: string): Promise<Record<string, string>> {
|
|
7
|
+
const files = await fg(['*.ts'], { cwd: momentDir });
|
|
8
8
|
const context: Record<string, string> = {};
|
|
9
9
|
for (const file of files) {
|
|
10
|
-
context[file] = await readFile(path.join(
|
|
10
|
+
context[file] = await readFile(path.join(momentDir, file), 'utf-8');
|
|
11
11
|
}
|
|
12
|
-
const sharedFiles = await loadSharedContext(
|
|
12
|
+
const sharedFiles = await loadSharedContext(momentDir);
|
|
13
13
|
Object.assign(context, sharedFiles);
|
|
14
14
|
return context;
|
|
15
15
|
}
|
|
@@ -5,14 +5,14 @@ import { loadSharedContext } from './loadSharedContext';
|
|
|
5
5
|
|
|
6
6
|
describe('loadSharedContext', () => {
|
|
7
7
|
let tmpDir: string;
|
|
8
|
-
let
|
|
8
|
+
let momentDir: string;
|
|
9
9
|
let sharedDir: string;
|
|
10
10
|
|
|
11
11
|
beforeEach(async () => {
|
|
12
12
|
tmpDir = await fs.mkdtemp(path.join(import.meta.dirname, '.tmp-'));
|
|
13
13
|
sharedDir = path.join(tmpDir, 'shared');
|
|
14
|
-
|
|
15
|
-
await fs.mkdir(
|
|
14
|
+
momentDir = path.join(tmpDir, 'src', 'moments', 'some-moment');
|
|
15
|
+
await fs.mkdir(momentDir, { recursive: true });
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
afterEach(async () => {
|
|
@@ -20,14 +20,14 @@ describe('loadSharedContext', () => {
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it('returns empty record when shared directory does not exist', async () => {
|
|
23
|
-
expect(await loadSharedContext(
|
|
23
|
+
expect(await loadSharedContext(momentDir)).toEqual({});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('returns empty record when shared directory has no .ts files', async () => {
|
|
27
27
|
await fs.mkdir(sharedDir, { recursive: true });
|
|
28
28
|
await fs.writeFile(path.join(sharedDir, 'readme.md'), '# Readme');
|
|
29
29
|
|
|
30
|
-
expect(await loadSharedContext(
|
|
30
|
+
expect(await loadSharedContext(momentDir)).toEqual({});
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
it('returns all .ts files from shared directory keyed by real path sorted by name', async () => {
|
|
@@ -35,7 +35,7 @@ describe('loadSharedContext', () => {
|
|
|
35
35
|
await fs.writeFile(path.join(sharedDir, 'types.ts'), 'export type Foo = string;');
|
|
36
36
|
await fs.writeFile(path.join(sharedDir, 'enums.ts'), 'export enum Bar { A = "a" }');
|
|
37
37
|
|
|
38
|
-
const result = await loadSharedContext(
|
|
38
|
+
const result = await loadSharedContext(momentDir);
|
|
39
39
|
|
|
40
40
|
expect(result).toEqual({
|
|
41
41
|
'shared/enums.ts': 'export enum Bar { A = "a" }',
|
|
@@ -47,7 +47,7 @@ describe('loadSharedContext', () => {
|
|
|
47
47
|
await fs.mkdir(sharedDir, { recursive: true });
|
|
48
48
|
await fs.writeFile(path.join(sharedDir, 'types.ts'), 'export type X = number;');
|
|
49
49
|
|
|
50
|
-
const result = await loadSharedContext(
|
|
50
|
+
const result = await loadSharedContext(momentDir);
|
|
51
51
|
|
|
52
52
|
expect(result).toEqual({
|
|
53
53
|
'shared/types.ts': 'export type X = number;',
|
|
@@ -3,8 +3,8 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import fg from 'fast-glob';
|
|
5
5
|
|
|
6
|
-
export async function loadSharedContext(
|
|
7
|
-
const sharedDir = path.resolve(
|
|
6
|
+
export async function loadSharedContext(momentDir: string): Promise<Record<string, string>> {
|
|
7
|
+
const sharedDir = path.resolve(momentDir, '../../../shared');
|
|
8
8
|
if (!existsSync(sharedDir)) return {};
|
|
9
9
|
|
|
10
10
|
const sharedFiles = await fg(['*.ts'], { cwd: sharedDir });
|