sofia-cli 0.1.1 → 0.1.4

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 (136) hide show
  1. package/README.md +42 -20
  2. package/dist/infra/deploy.sh +193 -0
  3. package/dist/infra/gather-env.sh +211 -0
  4. package/dist/infra/infra/deploy.sh +193 -0
  5. package/dist/infra/infra/gather-env.sh +211 -0
  6. package/dist/infra/infra/main.bicep +90 -0
  7. package/dist/infra/infra/main.bicepparam +18 -0
  8. package/dist/infra/infra/resources.bicep +134 -0
  9. package/dist/infra/infra/teardown.sh +114 -0
  10. package/dist/infra/main.bicep +90 -0
  11. package/dist/infra/main.bicepparam +18 -0
  12. package/dist/infra/resources.bicep +134 -0
  13. package/dist/infra/teardown.sh +114 -0
  14. package/dist/src/cli/developCommand.js +0 -2
  15. package/dist/src/cli/index.js +8 -1
  16. package/dist/src/cli/workshopCommand.js +1 -1
  17. package/dist/src/develop/index.js +1 -1
  18. package/dist/src/develop/pocUtils.js +228 -0
  19. package/dist/src/develop/ralphLoop.js +3 -3
  20. package/dist/src/shared/data/cards.json +655 -670
  21. package/docs/architecture.md +2 -1
  22. package/package.json +5 -3
  23. package/src/cli/developCommand.ts +1 -3
  24. package/src/cli/index.ts +11 -1
  25. package/src/cli/workshopCommand.ts +21 -17
  26. package/src/develop/dynamicScaffolder.ts +36 -30
  27. package/src/develop/index.ts +13 -2
  28. package/src/develop/pocUtils.ts +296 -0
  29. package/src/develop/ralphLoop.ts +8 -28
  30. package/src/develop/templateRegistry.ts +19 -18
  31. package/src/shared/data/cards.json +655 -670
  32. package/tests/e2e/developE2e.spec.ts +3 -61
  33. package/tests/e2e/developFailureE2e.spec.ts +34 -38
  34. package/tests/integration/pocGithubMcp.spec.ts +29 -39
  35. package/tests/integration/pocLocalFallback.spec.ts +29 -39
  36. package/tests/integration/ralphLoopFlow.spec.ts +46 -66
  37. package/tests/integration/ralphLoopPartial.spec.ts +30 -37
  38. package/tests/unit/develop/githubMcpAdapter.spec.ts +0 -134
  39. package/tests/unit/develop/outputValidator.spec.ts +45 -21
  40. package/tests/unit/develop/ralphLoop.spec.ts +58 -94
  41. package/tsconfig.json +2 -1
  42. package/vitest.workspace.ts +5 -0
  43. package/dist/src/develop/pocScaffolder.js +0 -542
  44. package/dist/tests/e2e/developE2e.spec.js +0 -126
  45. package/dist/tests/e2e/developFailureE2e.spec.js +0 -247
  46. package/dist/tests/e2e/developPty.spec.js +0 -75
  47. package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +0 -84
  48. package/dist/tests/e2e/harness.spec.js +0 -83
  49. package/dist/tests/e2e/mcpLive.spec.js +0 -120
  50. package/dist/tests/e2e/newSession.e2e.spec.js +0 -177
  51. package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +0 -62
  52. package/dist/tests/e2e/workiqEnrichment.spec.js +0 -56
  53. package/dist/tests/e2e/zavaSimulation.spec.js +0 -452
  54. package/dist/tests/fixtures/test-fixture-project/src/add.js +0 -3
  55. package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +0 -6
  56. package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +0 -8
  57. package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +0 -10
  58. package/dist/tests/fixtures/test-fixture-project/vitest.config.js +0 -6
  59. package/dist/tests/integration/autoStartConversation.spec.js +0 -138
  60. package/dist/tests/integration/defaultCommand.spec.js +0 -147
  61. package/dist/tests/integration/directCommandNonTty.spec.js +0 -224
  62. package/dist/tests/integration/directCommandTty.spec.js +0 -151
  63. package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +0 -175
  64. package/dist/tests/integration/exportArtifacts.spec.js +0 -202
  65. package/dist/tests/integration/exportFallbackFlow.spec.js +0 -99
  66. package/dist/tests/integration/mcpDegradationFlow.spec.js +0 -190
  67. package/dist/tests/integration/mcpTransportFlow.spec.js +0 -139
  68. package/dist/tests/integration/newSessionFlow.spec.js +0 -343
  69. package/dist/tests/integration/pocGithubMcp.spec.js +0 -186
  70. package/dist/tests/integration/pocLocalFallback.spec.js +0 -171
  71. package/dist/tests/integration/pocScaffold.spec.js +0 -163
  72. package/dist/tests/integration/ralphLoopFlow.spec.js +0 -359
  73. package/dist/tests/integration/ralphLoopPartial.spec.js +0 -368
  74. package/dist/tests/integration/resumeAndBacktrack.spec.js +0 -247
  75. package/dist/tests/integration/spinnerLifecycle.spec.js +0 -220
  76. package/dist/tests/integration/summarizationFlow.spec.js +0 -115
  77. package/dist/tests/integration/testRunnerReal.spec.js +0 -52
  78. package/dist/tests/integration/webSearchAgent.spec.js +0 -128
  79. package/dist/tests/live/copilotSdkLive.spec.js +0 -107
  80. package/dist/tests/live/zavaFullWorkshop.spec.js +0 -392
  81. package/dist/tests/setup/loadEnv.js +0 -3
  82. package/dist/tests/unit/cli/developCommand.spec.js +0 -567
  83. package/dist/tests/unit/cli/directCommands.spec.js +0 -279
  84. package/dist/tests/unit/cli/envLoader.spec.js +0 -58
  85. package/dist/tests/unit/cli/ioContext.spec.js +0 -119
  86. package/dist/tests/unit/cli/preflight.spec.js +0 -108
  87. package/dist/tests/unit/cli/statusCommand.spec.js +0 -111
  88. package/dist/tests/unit/cli/workshopClientFallback.spec.js +0 -80
  89. package/dist/tests/unit/cli/workshopCommand.spec.js +0 -329
  90. package/dist/tests/unit/config/vitestEnvSetup.spec.js +0 -13
  91. package/dist/tests/unit/develop/checkpointState.spec.js +0 -315
  92. package/dist/tests/unit/develop/codeGenerator.spec.js +0 -355
  93. package/dist/tests/unit/develop/githubMcpAdapter.spec.js +0 -231
  94. package/dist/tests/unit/develop/mcpContextEnricher.spec.js +0 -433
  95. package/dist/tests/unit/develop/outputValidator.spec.js +0 -119
  96. package/dist/tests/unit/develop/pocScaffolder.spec.js +0 -353
  97. package/dist/tests/unit/develop/ralphLoop.spec.js +0 -1248
  98. package/dist/tests/unit/develop/templateRegistry.spec.js +0 -85
  99. package/dist/tests/unit/develop/testRunner.spec.js +0 -249
  100. package/dist/tests/unit/infraBicep.spec.js +0 -92
  101. package/dist/tests/unit/infraDeploy.spec.js +0 -82
  102. package/dist/tests/unit/infraTeardown.spec.js +0 -63
  103. package/dist/tests/unit/logging/logger.spec.js +0 -43
  104. package/dist/tests/unit/loop/conversationLoop.spec.js +0 -592
  105. package/dist/tests/unit/loop/phaseSummarizer.spec.js +0 -141
  106. package/dist/tests/unit/loop/streamingMarkdown.spec.js +0 -147
  107. package/dist/tests/unit/mcp/mcpManager.spec.js +0 -279
  108. package/dist/tests/unit/mcp/mcpTransport.spec.js +0 -529
  109. package/dist/tests/unit/mcp/retryPolicy.spec.js +0 -218
  110. package/dist/tests/unit/mcp/timeoutValidation.spec.js +0 -46
  111. package/dist/tests/unit/mcp/webSearch.spec.js +0 -567
  112. package/dist/tests/unit/phases/contextSummarizer.spec.js +0 -140
  113. package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +0 -93
  114. package/dist/tests/unit/phases/discoveryEnricher.spec.js +0 -411
  115. package/dist/tests/unit/phases/phaseExtractors.spec.js +0 -352
  116. package/dist/tests/unit/phases/phaseHandlers.spec.js +0 -425
  117. package/dist/tests/unit/prompts/promptLoader.spec.js +0 -118
  118. package/dist/tests/unit/schemas/pocSchemas.spec.js +0 -412
  119. package/dist/tests/unit/schemas/session.spec.js +0 -257
  120. package/dist/tests/unit/sessions/exportPaths.spec.js +0 -31
  121. package/dist/tests/unit/sessions/exportWriter.spec.js +0 -655
  122. package/dist/tests/unit/sessions/sessionManager.spec.js +0 -151
  123. package/dist/tests/unit/sessions/sessionStore.spec.js +0 -116
  124. package/dist/tests/unit/shared/activitySpinner.spec.js +0 -175
  125. package/dist/tests/unit/shared/cardsLoader.spec.js +0 -76
  126. package/dist/tests/unit/shared/copilotClient.spec.js +0 -155
  127. package/dist/tests/unit/shared/errorClassifier.spec.js +0 -131
  128. package/dist/tests/unit/shared/events.spec.js +0 -55
  129. package/dist/tests/unit/shared/markdownRenderer.spec.js +0 -35
  130. package/dist/tests/unit/shared/markdownRendererChunks.spec.js +0 -70
  131. package/dist/tests/unit/shared/tableRenderer.spec.js +0 -34
  132. package/dist/vitest.config.js +0 -14
  133. package/dist/vitest.live.config.js +0 -18
  134. package/src/develop/pocScaffolder.ts +0 -646
  135. package/tests/integration/pocScaffold.spec.ts +0 -220
  136. package/tests/unit/develop/pocScaffolder.spec.ts +0 -451
@@ -15,7 +15,9 @@ import { join } from 'node:path';
15
15
  import { tmpdir } from 'node:os';
16
16
 
17
17
  import { RalphLoop } from '../../../src/develop/ralphLoop.js';
18
- import { PocScaffolder, validatePocOutput } from '../../../src/develop/pocScaffolder.js';
18
+ import { validatePocOutput } from '../../../src/develop/pocUtils.js';
19
+ import * as pocUtils from '../../../src/develop/pocUtils.js';
20
+ import { generateDynamicScaffold } from '../../../src/develop/dynamicScaffolder.js';
19
21
  import { TestRunner } from '../../../src/develop/testRunner.js';
20
22
  import { GitHubMcpAdapter } from '../../../src/develop/githubMcpAdapter.js';
21
23
  import type { WorkshopSession } from '../../../src/shared/schemas/session.js';
@@ -50,14 +52,19 @@ vi.mock('node:child_process', async (importOriginal) => {
50
52
  });
51
53
 
52
54
  // Mock validatePocOutput — default: valid
53
- vi.mock('../../../src/develop/pocScaffolder.js', async (importOriginal) => {
54
- const actual = await importOriginal<typeof import('../../../src/develop/pocScaffolder.js')>();
55
+ vi.mock('../../../src/develop/pocUtils.js', async (importOriginal) => {
56
+ const actual = await importOriginal<typeof import('../../../src/develop/pocUtils.js')>();
55
57
  return {
56
58
  ...actual,
57
59
  validatePocOutput: vi.fn().mockResolvedValue({ valid: true, missingFiles: [], errors: [] }),
58
60
  };
59
61
  });
60
62
 
63
+ // Mock generateDynamicScaffold to create minimal files for tests
64
+ vi.mock('../../../src/develop/dynamicScaffolder.js', () => ({
65
+ generateDynamicScaffold: vi.fn(),
66
+ }));
67
+
61
68
  // ── Helpers ───────────────────────────────────────────────────────────────────
62
69
 
63
70
  function makeSession(overrides?: Partial<WorkshopSession>): WorkshopSession {
@@ -178,40 +185,27 @@ function makeAlwaysFailingTestRunner(): TestRunner {
178
185
  } as unknown as TestRunner;
179
186
  }
180
187
 
181
- function makeFakeScaffolder(outputDir: string): PocScaffolder {
182
- return {
183
- scaffold: vi.fn().mockImplementation(async () => {
184
- // Create minimal required files
185
- const { writeFile, mkdir } = await import('node:fs/promises');
186
- await mkdir(join(outputDir, 'src'), { recursive: true });
187
- await mkdir(join(outputDir, 'tests'), { recursive: true });
188
- await writeFile(
189
- join(outputDir, 'package.json'),
190
- JSON.stringify({
191
- name: 'test-poc',
192
- scripts: { test: 'vitest run' },
193
- dependencies: {},
194
- devDependencies: {},
195
- }),
196
- 'utf-8',
197
- );
198
- await writeFile(join(outputDir, 'src', 'index.ts'), 'export function main() {}', 'utf-8');
199
- return {
200
- createdFiles: ['package.json', 'src/index.ts'],
201
- skippedFiles: [],
202
- context: {
203
- projectName: 'test-poc',
204
- ideaTitle: 'Test',
205
- ideaDescription: 'Test',
206
- techStack: { language: 'TypeScript', runtime: 'Node.js 20', testRunner: 'npm test' },
207
- planSummary: 'Test',
208
- sessionId: 'ralph-test-session',
209
- outputDir,
210
- },
211
- };
212
- }),
213
- getTemplateFiles: () => ['package.json', 'src/index.ts'],
214
- } as unknown as PocScaffolder;
188
+ function setupDynamicScaffoldMock(outputDir: string): void {
189
+ vi.mocked(generateDynamicScaffold).mockImplementation(async () => {
190
+ const { writeFile, mkdir } = await import('node:fs/promises');
191
+ await mkdir(join(outputDir, 'src'), { recursive: true });
192
+ await mkdir(join(outputDir, 'tests'), { recursive: true });
193
+ await writeFile(
194
+ join(outputDir, 'package.json'),
195
+ JSON.stringify({
196
+ name: 'test-poc',
197
+ scripts: { test: 'vitest run' },
198
+ dependencies: {},
199
+ devDependencies: {},
200
+ }),
201
+ 'utf-8',
202
+ );
203
+ await writeFile(join(outputDir, 'src', 'index.ts'), 'export function main() {}', 'utf-8');
204
+ return {
205
+ createdFiles: ['package.json', 'src/index.ts'],
206
+ techStack: { language: 'TypeScript', runtime: 'Node.js 20', testRunner: 'npm test' },
207
+ };
208
+ });
215
209
  }
216
210
 
217
211
  // ── Tests ─────────────────────────────────────────────────────────────────────
@@ -268,7 +262,7 @@ describe('RalphLoop', () => {
268
262
  const io = makeIo();
269
263
  const client = makePassingClient();
270
264
  const testRunner = makePassingTestRunner();
271
- const scaffolder = makeFakeScaffolder(tmpDir);
265
+ setupDynamicScaffoldMock(tmpDir);
272
266
 
273
267
  const ralph = new RalphLoop({
274
268
  client,
@@ -277,7 +271,6 @@ describe('RalphLoop', () => {
277
271
  outputDir: tmpDir,
278
272
  maxIterations: 5,
279
273
  testRunner,
280
- scaffolder,
281
274
  });
282
275
 
283
276
  const result = await ralph.run();
@@ -291,7 +284,7 @@ describe('RalphLoop', () => {
291
284
  const io = makeIo();
292
285
  const client = makePassingClient();
293
286
  const testRunner = makePassingTestRunner();
294
- const scaffolder = makeFakeScaffolder(tmpDir);
287
+ setupDynamicScaffoldMock(tmpDir);
295
288
 
296
289
  const ralph = new RalphLoop({
297
290
  client,
@@ -300,7 +293,6 @@ describe('RalphLoop', () => {
300
293
  outputDir: tmpDir,
301
294
  maxIterations: 5,
302
295
  testRunner,
303
- scaffolder,
304
296
  });
305
297
 
306
298
  const result = await ralph.run();
@@ -314,7 +306,7 @@ describe('RalphLoop', () => {
314
306
  const io = makeIo();
315
307
  const client = makePassingClient();
316
308
  const testRunner = makePassingTestRunner();
317
- const scaffolder = makeFakeScaffolder(tmpDir);
309
+ setupDynamicScaffoldMock(tmpDir);
318
310
  const onSessionUpdate = vi.fn().mockResolvedValue(undefined);
319
311
 
320
312
  const ralph = new RalphLoop({
@@ -324,7 +316,6 @@ describe('RalphLoop', () => {
324
316
  outputDir: tmpDir,
325
317
  maxIterations: 3,
326
318
  testRunner,
327
- scaffolder,
328
319
  onSessionUpdate,
329
320
  });
330
321
 
@@ -339,7 +330,7 @@ describe('RalphLoop', () => {
339
330
  const io = makeIo();
340
331
  const client = makePassingClient();
341
332
  const testRunner = makePassingTestRunner();
342
- const scaffolder = makeFakeScaffolder(tmpDir);
333
+ setupDynamicScaffoldMock(tmpDir);
343
334
 
344
335
  const ralph = new RalphLoop({
345
336
  client,
@@ -348,7 +339,6 @@ describe('RalphLoop', () => {
348
339
  outputDir: tmpDir,
349
340
  maxIterations: 5,
350
341
  testRunner,
351
- scaffolder,
352
342
  });
353
343
 
354
344
  const result = await ralph.run();
@@ -366,7 +356,7 @@ describe('RalphLoop', () => {
366
356
  const io = makeIo();
367
357
  const client = makePassingClient();
368
358
  const testRunner = makeAlwaysFailingTestRunner();
369
- const scaffolder = makeFakeScaffolder(tmpDir);
359
+ setupDynamicScaffoldMock(tmpDir);
370
360
 
371
361
  const ralph = new RalphLoop({
372
362
  client,
@@ -375,7 +365,6 @@ describe('RalphLoop', () => {
375
365
  outputDir: tmpDir,
376
366
  maxIterations: 3,
377
367
  testRunner,
378
- scaffolder,
379
368
  });
380
369
 
381
370
  const result = await ralph.run();
@@ -402,7 +391,7 @@ describe('RalphLoop', () => {
402
391
  } satisfies TestResults),
403
392
  } as unknown as TestRunner;
404
393
 
405
- const scaffolder = makeFakeScaffolder(tmpDir);
394
+ setupDynamicScaffoldMock(tmpDir);
406
395
 
407
396
  const ralph = new RalphLoop({
408
397
  client,
@@ -411,7 +400,6 @@ describe('RalphLoop', () => {
411
400
  outputDir: tmpDir,
412
401
  maxIterations: 2,
413
402
  testRunner,
414
- scaffolder,
415
403
  });
416
404
 
417
405
  const result = await ralph.run();
@@ -425,7 +413,7 @@ describe('RalphLoop', () => {
425
413
  const io = makeIo();
426
414
  const client = makePassingClient();
427
415
  const testRunner = makeAlwaysFailingTestRunner();
428
- const scaffolder = makeFakeScaffolder(tmpDir);
416
+ setupDynamicScaffoldMock(tmpDir);
429
417
 
430
418
  const ralph = new RalphLoop({
431
419
  client,
@@ -434,7 +422,6 @@ describe('RalphLoop', () => {
434
422
  outputDir: tmpDir,
435
423
  maxIterations: 2,
436
424
  testRunner,
437
- scaffolder,
438
425
  });
439
426
 
440
427
  const result = await ralph.run();
@@ -447,7 +434,7 @@ describe('RalphLoop', () => {
447
434
  const session = makeSession();
448
435
  const io = makeIo();
449
436
  const testRunner = makeAlwaysFailingTestRunner();
450
- const scaffolder = makeFakeScaffolder(tmpDir);
437
+ setupDynamicScaffoldMock(tmpDir);
451
438
 
452
439
  // Client that emits SIGINT mid-generation to simulate Ctrl+C
453
440
  const client: CopilotClient = {
@@ -474,7 +461,6 @@ describe('RalphLoop', () => {
474
461
  outputDir: tmpDir,
475
462
  maxIterations: 5,
476
463
  testRunner,
477
- scaffolder,
478
464
  onSessionUpdate: async (s) => {
479
465
  sessionUpdates.push(s);
480
466
  },
@@ -497,7 +483,7 @@ describe('RalphLoop', () => {
497
483
  const io = makeIo();
498
484
  const client = makePassingClient();
499
485
  const testRunner = makePassingTestRunner();
500
- const scaffolder = makeFakeScaffolder(tmpDir);
486
+ setupDynamicScaffoldMock(tmpDir);
501
487
  const events: string[] = [];
502
488
 
503
489
  const ralph = new RalphLoop({
@@ -507,7 +493,6 @@ describe('RalphLoop', () => {
507
493
  outputDir: tmpDir,
508
494
  maxIterations: 3,
509
495
  testRunner,
510
- scaffolder,
511
496
  onEvent: (e) => events.push(e.type),
512
497
  });
513
498
 
@@ -523,7 +508,7 @@ describe('RalphLoop', () => {
523
508
  const io = makeIo();
524
509
  const client = makePassingClient();
525
510
  const testRunner = makePassingTestRunner();
526
- const scaffolder = makeFakeScaffolder(tmpDir);
511
+ setupDynamicScaffoldMock(tmpDir);
527
512
 
528
513
  const ralph = new RalphLoop({
529
514
  client,
@@ -532,7 +517,6 @@ describe('RalphLoop', () => {
532
517
  outputDir: tmpDir,
533
518
  maxIterations: 3,
534
519
  testRunner,
535
- scaffolder,
536
520
  });
537
521
 
538
522
  const result = await ralph.run();
@@ -545,7 +529,7 @@ describe('RalphLoop', () => {
545
529
  const session = makeSession();
546
530
  const io = makeIo();
547
531
  const client = makePassingClient();
548
- const scaffolder = makeFakeScaffolder(tmpDir);
532
+ setupDynamicScaffoldMock(tmpDir);
549
533
 
550
534
  // First test run fails, second (final run after loop) passes
551
535
  let runCount = 0;
@@ -582,7 +566,6 @@ describe('RalphLoop', () => {
582
566
  outputDir: tmpDir,
583
567
  maxIterations: 2, // scaffold + 1 iterate = 2 iterations, then final test
584
568
  testRunner,
585
- scaffolder,
586
569
  });
587
570
 
588
571
  const result = await ralph.run();
@@ -598,7 +581,7 @@ describe('RalphLoop', () => {
598
581
  const session = makeSession();
599
582
  const io = makeIo();
600
583
  const client = makePassingClient();
601
- const scaffolder = makeFakeScaffolder(tmpDir);
584
+ setupDynamicScaffoldMock(tmpDir);
602
585
  let persistedSession: WorkshopSession | null = null;
603
586
 
604
587
  // Slow test runner: yields after first call so SIGINT can fire
@@ -633,7 +616,6 @@ describe('RalphLoop', () => {
633
616
  outputDir: tmpDir,
634
617
  maxIterations: 10,
635
618
  testRunner,
636
- scaffolder,
637
619
  onSessionUpdate,
638
620
  });
639
621
 
@@ -659,7 +641,7 @@ describe('RalphLoop', () => {
659
641
  const session = makeSession();
660
642
  const io = makeIo();
661
643
  const client = makePassingClient();
662
- const scaffolder = makeFakeScaffolder(tmpDir);
644
+ setupDynamicScaffoldMock(tmpDir);
663
645
 
664
646
  // Partially passing test runner that delays so SIGINT can fire
665
647
  let runCount = 0;
@@ -688,7 +670,6 @@ describe('RalphLoop', () => {
688
670
  outputDir: tmpDir,
689
671
  maxIterations: 10,
690
672
  testRunner,
691
- scaffolder,
692
673
  });
693
674
 
694
675
  const runPromise = ralph.run();
@@ -755,7 +736,7 @@ describe('RalphLoop', () => {
755
736
  }),
756
737
  } as unknown as TestRunner;
757
738
 
758
- const scaffolder = makeFakeScaffolder(tmpDir);
739
+ setupDynamicScaffoldMock(tmpDir);
759
740
 
760
741
  // Create a mock GitHub adapter that captures pushFiles calls
761
742
  const pushFilesMock = vi.fn().mockResolvedValue({ available: true, commitSha: 'abc123' });
@@ -777,7 +758,6 @@ describe('RalphLoop', () => {
777
758
  outputDir: tmpDir,
778
759
  maxIterations: 5,
779
760
  testRunner,
780
- scaffolder,
781
761
  });
782
762
 
783
763
  await ralph.run();
@@ -826,7 +806,6 @@ describe('RalphLoop', () => {
826
806
  outputDir: tmpDir,
827
807
  maxIterations: 5,
828
808
  testRunner,
829
- scaffolder: makeFakeScaffolder(tmpDir),
830
809
  });
831
810
 
832
811
  const result = await ralph.run();
@@ -865,7 +844,6 @@ describe('RalphLoop', () => {
865
844
  outputDir: tmpDir,
866
845
  maxIterations: 5,
867
846
  testRunner,
868
- scaffolder: makeFakeScaffolder(tmpDir),
869
847
  });
870
848
 
871
849
  const result = await ralph.run();
@@ -918,7 +896,6 @@ describe('RalphLoop', () => {
918
896
  outputDir: tmpDir,
919
897
  maxIterations: 10,
920
898
  testRunner,
921
- scaffolder: makeFakeScaffolder(tmpDir),
922
899
  checkpoint: {
923
900
  hasPriorRun: true,
924
901
  completedIterations: 2,
@@ -940,7 +917,7 @@ describe('RalphLoop', () => {
940
917
  it('skips scaffold when checkpoint says canSkipScaffold=true (T014)', async () => {
941
918
  const io = makeIo();
942
919
  const testRunner = makePassingTestRunner();
943
- const scaffolder = makeFakeScaffolder(tmpDir);
920
+ setupDynamicScaffoldMock(tmpDir);
944
921
 
945
922
  const session = makeSession({
946
923
  poc: {
@@ -964,7 +941,6 @@ describe('RalphLoop', () => {
964
941
  outputDir: tmpDir,
965
942
  maxIterations: 10,
966
943
  testRunner,
967
- scaffolder,
968
944
  checkpoint: {
969
945
  hasPriorRun: true,
970
946
  completedIterations: 1,
@@ -979,11 +955,9 @@ describe('RalphLoop', () => {
979
955
  await ralph.run();
980
956
 
981
957
  // Scaffold should NOT have been called — it was skipped
982
- expect(scaffolder.scaffold).not.toHaveBeenCalled();
958
+ expect(generateDynamicScaffold).not.toHaveBeenCalled();
983
959
  // Should log that scaffold was skipped
984
- expect(io.writeActivity).toHaveBeenCalledWith(
985
- expect.stringContaining('Skipping scaffold'),
986
- );
960
+ expect(io.writeActivity).toHaveBeenCalledWith(expect.stringContaining('Skipping scaffold'));
987
961
  });
988
962
 
989
963
  it('pops incomplete last iteration and re-runs it (T015, FR-001a)', async () => {
@@ -1021,7 +995,6 @@ describe('RalphLoop', () => {
1021
995
  outputDir: tmpDir,
1022
996
  maxIterations: 10,
1023
997
  testRunner,
1024
- scaffolder: makeFakeScaffolder(tmpDir),
1025
998
  checkpoint: {
1026
999
  hasPriorRun: true,
1027
1000
  completedIterations: 1,
@@ -1045,7 +1018,7 @@ describe('RalphLoop', () => {
1045
1018
  it('re-scaffolds when output directory is missing but iterations exist (T018, FR-007)', async () => {
1046
1019
  const io = makeIo();
1047
1020
  const testRunner = makePassingTestRunner();
1048
- const scaffolder = makeFakeScaffolder(tmpDir);
1021
+ setupDynamicScaffoldMock(tmpDir);
1049
1022
 
1050
1023
  const session = makeSession({
1051
1024
  poc: {
@@ -1069,7 +1042,6 @@ describe('RalphLoop', () => {
1069
1042
  outputDir: tmpDir,
1070
1043
  maxIterations: 10,
1071
1044
  testRunner,
1072
- scaffolder,
1073
1045
  checkpoint: {
1074
1046
  hasPriorRun: true,
1075
1047
  completedIterations: 1,
@@ -1084,10 +1056,8 @@ describe('RalphLoop', () => {
1084
1056
  await ralph.run();
1085
1057
 
1086
1058
  // Scaffold SHOULD have been called since canSkipScaffold is false
1087
- expect(scaffolder.scaffold).toHaveBeenCalled();
1088
- expect(io.writeActivity).toHaveBeenCalledWith(
1089
- expect.stringContaining('re-scaffolding'),
1090
- );
1059
+ expect(generateDynamicScaffold).toHaveBeenCalled();
1060
+ expect(io.writeActivity).toHaveBeenCalledWith(expect.stringContaining('re-scaffolding'));
1091
1061
  });
1092
1062
  });
1093
1063
 
@@ -1099,7 +1069,7 @@ describe('RalphLoop', () => {
1099
1069
  const io = makeIo();
1100
1070
  const client = makePassingClient();
1101
1071
  const testRunner = makePassingTestRunner();
1102
- const scaffolder = makeFakeScaffolder(tmpDir);
1072
+ setupDynamicScaffoldMock(tmpDir);
1103
1073
 
1104
1074
  const pushOrder: string[] = [];
1105
1075
  const pushFilesMock = vi.fn().mockImplementation(async () => {
@@ -1133,7 +1103,6 @@ describe('RalphLoop', () => {
1133
1103
  outputDir: tmpDir,
1134
1104
  maxIterations: 3,
1135
1105
  testRunner,
1136
- scaffolder,
1137
1106
  });
1138
1107
 
1139
1108
  await ralph.run();
@@ -1168,7 +1137,7 @@ describe('RalphLoop', () => {
1168
1137
  it('always re-runs dependency install even when scaffolding is skipped (T065, FR-003)', async () => {
1169
1138
  const io = makeIo();
1170
1139
  const testRunner = makePassingTestRunner();
1171
- const scaffolder = makeFakeScaffolder(tmpDir);
1140
+ setupDynamicScaffoldMock(tmpDir);
1172
1141
 
1173
1142
  const session = makeSession({
1174
1143
  poc: {
@@ -1192,7 +1161,6 @@ describe('RalphLoop', () => {
1192
1161
  outputDir: tmpDir,
1193
1162
  maxIterations: 10,
1194
1163
  testRunner,
1195
- scaffolder,
1196
1164
  checkpoint: {
1197
1165
  hasPriorRun: true,
1198
1166
  completedIterations: 1,
@@ -1207,7 +1175,7 @@ describe('RalphLoop', () => {
1207
1175
  await ralph.run();
1208
1176
 
1209
1177
  // Scaffold should be skipped
1210
- expect(scaffolder.scaffold).not.toHaveBeenCalled();
1178
+ expect(generateDynamicScaffold).not.toHaveBeenCalled();
1211
1179
  // But install should still run
1212
1180
  expect(io.writeActivity).toHaveBeenCalledWith(
1213
1181
  expect.stringContaining('Re-running dependency installation'),
@@ -1277,7 +1245,6 @@ describe('RalphLoop', () => {
1277
1245
  outputDir: tmpDir,
1278
1246
  maxIterations: 4,
1279
1247
  testRunner,
1280
- scaffolder: makeFakeScaffolder(tmpDir),
1281
1248
  checkpoint: {
1282
1249
  hasPriorRun: true,
1283
1250
  completedIterations: 2,
@@ -1303,7 +1270,7 @@ describe('RalphLoop', () => {
1303
1270
  it('resume decision logging emits info-level messages (T067, FR-007a)', async () => {
1304
1271
  const io = makeIo();
1305
1272
  const testRunner = makePassingTestRunner();
1306
- const scaffolder = makeFakeScaffolder(tmpDir);
1273
+ setupDynamicScaffoldMock(tmpDir);
1307
1274
 
1308
1275
  const session = makeSession({
1309
1276
  poc: {
@@ -1327,7 +1294,6 @@ describe('RalphLoop', () => {
1327
1294
  outputDir: tmpDir,
1328
1295
  maxIterations: 10,
1329
1296
  testRunner,
1330
- scaffolder,
1331
1297
  checkpoint: {
1332
1298
  hasPriorRun: true,
1333
1299
  completedIterations: 1,
@@ -1355,7 +1321,7 @@ describe('RalphLoop', () => {
1355
1321
  describe('TODO marker rescan after iteration (T073)', () => {
1356
1322
  it('calls scanAndRecordTodos after each failing iteration', async () => {
1357
1323
  const scanSpy = vi
1358
- .spyOn(PocScaffolder, 'scanAndRecordTodos')
1324
+ .spyOn(pocUtils, 'scanAndRecordTodos')
1359
1325
  .mockResolvedValue({ totalInitial: 3, remaining: 2, markers: [] });
1360
1326
 
1361
1327
  const session = makeSession();
@@ -1370,7 +1336,7 @@ describe('RalphLoop', () => {
1370
1336
  };
1371
1337
  const testRunner = makeAlwaysFailingTestRunner();
1372
1338
  const client = makePassingClient();
1373
- const scaffolder = makeFakeScaffolder(tmpDir);
1339
+ setupDynamicScaffoldMock(tmpDir);
1374
1340
 
1375
1341
  const ralph = new RalphLoop({
1376
1342
  client,
@@ -1379,7 +1345,6 @@ describe('RalphLoop', () => {
1379
1345
  outputDir: tmpDir,
1380
1346
  maxIterations: 2,
1381
1347
  testRunner,
1382
- scaffolder,
1383
1348
  });
1384
1349
 
1385
1350
  await ralph.run();
@@ -1412,7 +1377,7 @@ describe('RalphLoop', () => {
1412
1377
  const io = makeIo();
1413
1378
  const session = makeSession();
1414
1379
  const testRunner = makeAlwaysFailingTestRunner();
1415
- const scaffolder = makeFakeScaffolder(tmpDir);
1380
+ setupDynamicScaffoldMock(tmpDir);
1416
1381
 
1417
1382
  const ralph = new RalphLoop({
1418
1383
  client,
@@ -1421,7 +1386,6 @@ describe('RalphLoop', () => {
1421
1386
  outputDir: tmpDir,
1422
1387
  maxIterations: 2,
1423
1388
  testRunner,
1424
- scaffolder,
1425
1389
  });
1426
1390
 
1427
1391
  await ralph.run();
package/tsconfig.json CHANGED
@@ -16,5 +16,6 @@
16
16
  "zod": ["src/vendor/zod"]
17
17
  }
18
18
  },
19
- "include": ["src/**/*", "tests/**/*"]
19
+ "include": ["src/**/*"],
20
+ "exclude": ["tests/**/*"]
20
21
  }
@@ -0,0 +1,5 @@
1
+ export default [
2
+ 'vitest.config.ts',
3
+ 'vitest.live.config.ts',
4
+ '!tests/fixtures/**',
5
+ ];