ferix-code 0.0.2-beta.5 → 0.0.2-beta.7

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 (3) hide show
  1. package/dist/index.d.ts +226 -20
  2. package/dist/index.js +747 -305
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -71,7 +71,7 @@ import { Command } from "commander";
71
71
  // package.json
72
72
  var package_default = {
73
73
  name: "ferix-code",
74
- version: "0.0.2-beta.5",
74
+ version: "0.0.2-beta.7",
75
75
  description: "Composable RALPH loops for AI coding agents - v2 with Effect",
76
76
  type: "module",
77
77
  bin: {
@@ -86,7 +86,8 @@ var package_default = {
86
86
  dev: "tsup --watch",
87
87
  "check-types": "tsc --noEmit",
88
88
  test: "bun test",
89
- "test:watch": "bun test --watch"
89
+ "test:watch": "bun test --watch",
90
+ bump: "npm version prerelease --preid=beta --workspaces=false && bun run build && bun publish --tag beta"
90
91
  },
91
92
  dependencies: {
92
93
  commander: "^14.0.0",
@@ -117,7 +118,7 @@ var package_default = {
117
118
 
118
119
  // src/program.ts
119
120
  init_esm_shims();
120
- import { Effect as Effect21, Stream as Stream10 } from "effect";
121
+ import { Effect as Effect23, Stream as Stream10 } from "effect";
121
122
 
122
123
  // src/consumers/index.ts
123
124
  init_esm_shims();
@@ -2216,6 +2217,7 @@ var ANSIOutput = class {
2216
2217
  init_esm_shims();
2217
2218
 
2218
2219
  // src/consumers/tui/consumer.ts
2220
+ var CLOCK_REFRESH_INTERVAL_MS = 1e3;
2219
2221
  function formatErrorToLines2(err, lines) {
2220
2222
  if (err instanceof Error) {
2221
2223
  lines.push(` - ${err.name}: ${err.message}`);
@@ -2275,6 +2277,17 @@ function createTUIConsumer() {
2275
2277
  const inputFiber = yield* Effect3.forkDaemon(
2276
2278
  runInputLoop(stateRef, output)
2277
2279
  );
2280
+ const clockFiber = yield* Effect3.forkDaemon(
2281
+ Effect3.forever(
2282
+ Effect3.gen(function* () {
2283
+ yield* Effect3.sleep(CLOCK_REFRESH_INTERVAL_MS);
2284
+ const state = yield* Ref2.get(stateRef);
2285
+ if (state.status === "running") {
2286
+ yield* Effect3.sync(() => safeRender(state, output));
2287
+ }
2288
+ })
2289
+ )
2290
+ );
2278
2291
  const processEvents = events.pipe(
2279
2292
  Stream3.runForEach(
2280
2293
  (event) => Effect3.gen(function* () {
@@ -2326,6 +2339,7 @@ ${errorText}
2326
2339
  })
2327
2340
  );
2328
2341
  }
2342
+ yield* Fiber.interrupt(clockFiber);
2329
2343
  unsubscribeResize();
2330
2344
  cleanup();
2331
2345
  })
@@ -2334,13 +2348,15 @@ ${errorText}
2334
2348
 
2335
2349
  // src/layers/index.ts
2336
2350
  init_esm_shims();
2337
- import { Layer as Layer12 } from "effect";
2351
+ import { Layer as Layer14 } from "effect";
2338
2352
 
2339
- // src/layers/guardrails/file-system.ts
2353
+ // src/layers/git/file-system.ts
2340
2354
  init_esm_shims();
2341
- import { mkdir, readFile, writeFile } from "fs/promises";
2355
+ import { exec } from "child_process";
2356
+ import { access, mkdir, rm } from "fs/promises";
2342
2357
  import { join } from "path";
2343
- import { DateTime, Effect as Effect4, Layer } from "effect";
2358
+ import { promisify } from "util";
2359
+ import { Effect as Effect4, Layer } from "effect";
2344
2360
 
2345
2361
  // src/domain/errors.ts
2346
2362
  init_esm_shims();
@@ -2361,6 +2377,338 @@ var GuardrailsStoreError = class extends Data.TaggedError(
2361
2377
  };
2362
2378
  var OrchestratorError = class extends Data.TaggedError("OrchestratorError") {
2363
2379
  };
2380
+ var GitError = class extends Data.TaggedError("GitError") {
2381
+ };
2382
+
2383
+ // src/services/git.ts
2384
+ init_esm_shims();
2385
+ import { Context } from "effect";
2386
+ var Git = class extends Context.Tag("@ferix/Git")() {
2387
+ };
2388
+
2389
+ // src/layers/git/file-system.ts
2390
+ var execAsync = promisify(exec);
2391
+ var WORKTREES_DIR = ".ferix/worktrees";
2392
+ var BRANCH_PREFIX = "ferix";
2393
+ function getWorktreeDir(sessionId) {
2394
+ return join(process.cwd(), WORKTREES_DIR, sessionId);
2395
+ }
2396
+ function getBranchName(sessionId) {
2397
+ return `${BRANCH_PREFIX}/${sessionId}`;
2398
+ }
2399
+ function gitExec(command, cwd) {
2400
+ return Effect4.tryPromise({
2401
+ try: async () => {
2402
+ const { stdout } = await execAsync(command, { cwd });
2403
+ return stdout.trim();
2404
+ },
2405
+ catch: (error) => {
2406
+ const execError = error;
2407
+ return new GitError({
2408
+ message: execError.stderr || execError.message || String(error),
2409
+ operation: "status",
2410
+ cause: error
2411
+ });
2412
+ }
2413
+ });
2414
+ }
2415
+ function directoryExists(dirPath) {
2416
+ return Effect4.tryPromise({
2417
+ try: async () => {
2418
+ await access(dirPath);
2419
+ return true;
2420
+ },
2421
+ catch: () => new Error("Directory does not exist")
2422
+ }).pipe(Effect4.orElseSucceed(() => false));
2423
+ }
2424
+ var make = {
2425
+ createWorktree: (sessionId, baseBranch) => Effect4.gen(function* () {
2426
+ const worktreeDir = getWorktreeDir(sessionId);
2427
+ const branchName = getBranchName(sessionId);
2428
+ const worktreesBase = join(process.cwd(), WORKTREES_DIR);
2429
+ yield* Effect4.tryPromise({
2430
+ try: () => mkdir(worktreesBase, { recursive: true }),
2431
+ catch: (error) => new GitError({
2432
+ message: `Failed to create worktrees directory: ${String(error)}`,
2433
+ operation: "createWorktree",
2434
+ cause: error
2435
+ })
2436
+ });
2437
+ const exists = yield* directoryExists(worktreeDir);
2438
+ if (exists) {
2439
+ return worktreeDir;
2440
+ }
2441
+ const baseRef = baseBranch || "HEAD";
2442
+ const command = `git worktree add "${worktreeDir}" -b "${branchName}" ${baseRef}`;
2443
+ yield* gitExec(command).pipe(
2444
+ Effect4.mapError(
2445
+ (error) => new GitError({
2446
+ message: `Failed to create worktree: ${error.message}`,
2447
+ operation: "createWorktree",
2448
+ cause: error
2449
+ })
2450
+ )
2451
+ );
2452
+ return worktreeDir;
2453
+ }),
2454
+ removeWorktree: (sessionId) => Effect4.gen(function* () {
2455
+ const worktreeDir = getWorktreeDir(sessionId);
2456
+ const branchName = getBranchName(sessionId);
2457
+ const exists = yield* directoryExists(worktreeDir);
2458
+ if (!exists) {
2459
+ return;
2460
+ }
2461
+ yield* gitExec(`git worktree remove "${worktreeDir}" --force`).pipe(
2462
+ Effect4.mapError(
2463
+ (error) => new GitError({
2464
+ message: `Failed to remove worktree: ${error.message}`,
2465
+ operation: "removeWorktree",
2466
+ cause: error
2467
+ })
2468
+ ),
2469
+ // If git worktree remove fails, try manual cleanup
2470
+ Effect4.catchAll(
2471
+ () => Effect4.tryPromise({
2472
+ try: () => rm(worktreeDir, { recursive: true, force: true }),
2473
+ catch: (error) => new GitError({
2474
+ message: `Failed to remove worktree directory: ${String(error)}`,
2475
+ operation: "removeWorktree",
2476
+ cause: error
2477
+ })
2478
+ })
2479
+ )
2480
+ );
2481
+ yield* gitExec(`git branch -D "${branchName}"`).pipe(
2482
+ Effect4.catchAll(() => Effect4.succeed(void 0))
2483
+ );
2484
+ yield* gitExec("git worktree prune").pipe(
2485
+ Effect4.catchAll(() => Effect4.succeed(void 0))
2486
+ );
2487
+ }),
2488
+ getWorktreePath: (sessionId) => Effect4.gen(function* () {
2489
+ const worktreeDir = getWorktreeDir(sessionId);
2490
+ const exists = yield* directoryExists(worktreeDir);
2491
+ return exists ? worktreeDir : void 0;
2492
+ }),
2493
+ commitChanges: (sessionId, message) => Effect4.gen(function* () {
2494
+ const worktreeDir = getWorktreeDir(sessionId);
2495
+ const exists = yield* directoryExists(worktreeDir);
2496
+ if (!exists) {
2497
+ return yield* Effect4.fail(
2498
+ new GitError({
2499
+ message: `Worktree not found for session: ${sessionId}`,
2500
+ operation: "commit"
2501
+ })
2502
+ );
2503
+ }
2504
+ yield* gitExec("git add -A", worktreeDir).pipe(
2505
+ Effect4.mapError(
2506
+ (error) => new GitError({
2507
+ message: `Failed to stage changes: ${error.message}`,
2508
+ operation: "commit",
2509
+ cause: error
2510
+ })
2511
+ )
2512
+ );
2513
+ const status = yield* gitExec("git status --porcelain", worktreeDir).pipe(
2514
+ Effect4.catchAll(() => Effect4.succeed(""))
2515
+ );
2516
+ if (!status) {
2517
+ const head = yield* gitExec("git rev-parse HEAD", worktreeDir).pipe(
2518
+ Effect4.mapError(
2519
+ (error) => new GitError({
2520
+ message: `Failed to get HEAD: ${error.message}`,
2521
+ operation: "commit",
2522
+ cause: error
2523
+ })
2524
+ )
2525
+ );
2526
+ return head;
2527
+ }
2528
+ const escapedMessage = message.replace(/"/g, '\\"');
2529
+ yield* gitExec(`git commit -m "${escapedMessage}"`, worktreeDir).pipe(
2530
+ Effect4.mapError(
2531
+ (error) => new GitError({
2532
+ message: `Failed to commit: ${error.message}`,
2533
+ operation: "commit",
2534
+ cause: error
2535
+ })
2536
+ )
2537
+ );
2538
+ const hash = yield* gitExec("git rev-parse HEAD", worktreeDir).pipe(
2539
+ Effect4.mapError(
2540
+ (error) => new GitError({
2541
+ message: `Failed to get commit hash: ${error.message}`,
2542
+ operation: "commit",
2543
+ cause: error
2544
+ })
2545
+ )
2546
+ );
2547
+ return hash;
2548
+ }),
2549
+ pushBranch: (sessionId) => Effect4.gen(function* () {
2550
+ const worktreeDir = getWorktreeDir(sessionId);
2551
+ const branchName = getBranchName(sessionId);
2552
+ const exists = yield* directoryExists(worktreeDir);
2553
+ if (!exists) {
2554
+ return yield* Effect4.fail(
2555
+ new GitError({
2556
+ message: `Worktree not found for session: ${sessionId}`,
2557
+ operation: "push"
2558
+ })
2559
+ );
2560
+ }
2561
+ yield* gitExec(`git push -u origin "${branchName}"`, worktreeDir).pipe(
2562
+ Effect4.mapError(
2563
+ (error) => new GitError({
2564
+ message: `Failed to push branch: ${error.message}`,
2565
+ operation: "push",
2566
+ cause: error
2567
+ })
2568
+ )
2569
+ );
2570
+ }),
2571
+ createPR: (sessionId, title, body) => Effect4.gen(function* () {
2572
+ const worktreeDir = getWorktreeDir(sessionId);
2573
+ const exists = yield* directoryExists(worktreeDir);
2574
+ if (!exists) {
2575
+ return yield* Effect4.fail(
2576
+ new GitError({
2577
+ message: `Worktree not found for session: ${sessionId}`,
2578
+ operation: "createPR"
2579
+ })
2580
+ );
2581
+ }
2582
+ const escapedTitle = title.replace(/"/g, '\\"');
2583
+ const escapedBody = body.replace(/"/g, '\\"');
2584
+ const prUrl = yield* gitExec(
2585
+ `gh pr create --title "${escapedTitle}" --body "${escapedBody}"`,
2586
+ worktreeDir
2587
+ ).pipe(
2588
+ Effect4.mapError(
2589
+ (error) => new GitError({
2590
+ message: `Failed to create PR: ${error.message}`,
2591
+ operation: "createPR",
2592
+ cause: error
2593
+ })
2594
+ )
2595
+ );
2596
+ return prUrl;
2597
+ }),
2598
+ getBranchName
2599
+ };
2600
+ var Live = Layer.succeed(Git, make);
2601
+ var FileSystemGit = {
2602
+ Live
2603
+ };
2604
+
2605
+ // src/layers/git/memory.ts
2606
+ init_esm_shims();
2607
+ import { Effect as Effect5, Layer as Layer2, Ref as Ref3 } from "effect";
2608
+ var BRANCH_PREFIX2 = "ferix";
2609
+ function getBranchName2(sessionId) {
2610
+ return `${BRANCH_PREFIX2}/${sessionId}`;
2611
+ }
2612
+ function createMemoryGitService(stateRef, commitCounterRef) {
2613
+ return {
2614
+ createWorktree: (sessionId, _baseBranch) => Effect5.gen(function* () {
2615
+ const state = yield* Ref3.get(stateRef);
2616
+ const existing = state.get(sessionId);
2617
+ if (existing) {
2618
+ return existing.path;
2619
+ }
2620
+ const path2 = `.ferix/worktrees/${sessionId}`;
2621
+ const branch = getBranchName2(sessionId);
2622
+ const worktree = {
2623
+ path: path2,
2624
+ branch,
2625
+ commits: []
2626
+ };
2627
+ state.set(sessionId, worktree);
2628
+ yield* Ref3.set(stateRef, state);
2629
+ return path2;
2630
+ }),
2631
+ removeWorktree: (sessionId) => Effect5.gen(function* () {
2632
+ const state = yield* Ref3.get(stateRef);
2633
+ state.delete(sessionId);
2634
+ yield* Ref3.set(stateRef, state);
2635
+ }),
2636
+ getWorktreePath: (sessionId) => Effect5.gen(function* () {
2637
+ const state = yield* Ref3.get(stateRef);
2638
+ const worktree = state.get(sessionId);
2639
+ return worktree?.path;
2640
+ }),
2641
+ commitChanges: (sessionId, message) => Effect5.gen(function* () {
2642
+ const state = yield* Ref3.get(stateRef);
2643
+ const worktree = state.get(sessionId);
2644
+ if (!worktree) {
2645
+ return yield* Effect5.fail(
2646
+ new GitError({
2647
+ message: `Worktree not found for session: ${sessionId}`,
2648
+ operation: "commit"
2649
+ })
2650
+ );
2651
+ }
2652
+ const counter = yield* Ref3.updateAndGet(commitCounterRef, (n) => n + 1);
2653
+ const hash = `test-commit-${counter}`;
2654
+ const updatedWorktree = {
2655
+ ...worktree,
2656
+ commits: [...worktree.commits, `${hash}: ${message}`]
2657
+ };
2658
+ state.set(sessionId, updatedWorktree);
2659
+ yield* Ref3.set(stateRef, state);
2660
+ return hash;
2661
+ }),
2662
+ pushBranch: (sessionId) => Effect5.gen(function* () {
2663
+ const state = yield* Ref3.get(stateRef);
2664
+ const worktree = state.get(sessionId);
2665
+ if (!worktree) {
2666
+ return yield* Effect5.fail(
2667
+ new GitError({
2668
+ message: `Worktree not found for session: ${sessionId}`,
2669
+ operation: "push"
2670
+ })
2671
+ );
2672
+ }
2673
+ }),
2674
+ createPR: (sessionId, title, _body) => Effect5.gen(function* () {
2675
+ const state = yield* Ref3.get(stateRef);
2676
+ const worktree = state.get(sessionId);
2677
+ if (!worktree) {
2678
+ return yield* Effect5.fail(
2679
+ new GitError({
2680
+ message: `Worktree not found for session: ${sessionId}`,
2681
+ operation: "createPR"
2682
+ })
2683
+ );
2684
+ }
2685
+ const slug = title.toLowerCase().replace(/\s+/g, "-").slice(0, 30);
2686
+ return `https://github.com/test/repo/pull/${slug}`;
2687
+ }),
2688
+ getBranchName: getBranchName2
2689
+ };
2690
+ }
2691
+ function layer() {
2692
+ return Layer2.effect(
2693
+ Git,
2694
+ Effect5.gen(function* () {
2695
+ const stateRef = yield* Ref3.make(/* @__PURE__ */ new Map());
2696
+ const commitCounterRef = yield* Ref3.make(0);
2697
+ return createMemoryGitService(stateRef, commitCounterRef);
2698
+ })
2699
+ );
2700
+ }
2701
+ var Live2 = layer();
2702
+ var MemoryGit = {
2703
+ Live: Live2,
2704
+ layer
2705
+ };
2706
+
2707
+ // src/layers/guardrails/file-system.ts
2708
+ init_esm_shims();
2709
+ import { mkdir as mkdir2, readFile, writeFile } from "fs/promises";
2710
+ import { join as join2 } from "path";
2711
+ import { DateTime, Effect as Effect6, Layer as Layer3 } from "effect";
2364
2712
 
2365
2713
  // src/domain/index.ts
2366
2714
  init_esm_shims();
@@ -2648,6 +2996,16 @@ var ProgressUpdatedEventSchema = taggedEvent("ProgressUpdated", {
2648
2996
  action: S4.Literal("started", "completed", "failed", "learning"),
2649
2997
  timestamp: S4.Number
2650
2998
  });
2999
+ var WorktreeCreatedEventSchema = taggedEvent("WorktreeCreated", {
3000
+ sessionId: S4.String,
3001
+ worktreePath: S4.String,
3002
+ branchName: S4.String,
3003
+ timestamp: S4.Number
3004
+ });
3005
+ var WorktreeRemovedEventSchema = taggedEvent("WorktreeRemoved", {
3006
+ sessionId: S4.String,
3007
+ timestamp: S4.Number
3008
+ });
2651
3009
  var DomainEventSchema = S4.Union(
2652
3010
  LoopStartedEventSchema,
2653
3011
  LoopCompletedEventSchema,
@@ -2677,7 +3035,9 @@ var DomainEventSchema = S4.Union(
2677
3035
  PlanUpdateFailedEventSchema,
2678
3036
  LearningRecordedEventSchema,
2679
3037
  GuardrailAddedEventSchema,
2680
- ProgressUpdatedEventSchema
3038
+ ProgressUpdatedEventSchema,
3039
+ WorktreeCreatedEventSchema,
3040
+ WorktreeRemovedEventSchema
2681
3041
  );
2682
3042
  var DomainEventUtils = {
2683
3043
  isLLMEvent: (e) => e._tag.startsWith("LLM"),
@@ -2805,7 +3165,9 @@ var SessionSchema = S10.Struct({
2805
3165
  status: SessionStatusSchema,
2806
3166
  originalTask: S10.String,
2807
3167
  completedTasks: S10.Array(S10.String),
2808
- currentTaskId: S10.optional(S10.String)
3168
+ currentTaskId: S10.optional(S10.String),
3169
+ worktreePath: S10.optional(S10.String),
3170
+ branchName: S10.optional(S10.String)
2809
3171
  });
2810
3172
  var decodeSession = S10.decodeUnknown(SessionSchema);
2811
3173
 
@@ -3053,34 +3415,34 @@ var TUIStateSchema = S13.Struct({
3053
3415
 
3054
3416
  // src/services/guardrails-store.ts
3055
3417
  init_esm_shims();
3056
- import { Context } from "effect";
3057
- var GuardrailsStore = class extends Context.Tag("@ferix/GuardrailsStore")() {
3418
+ import { Context as Context2 } from "effect";
3419
+ var GuardrailsStore = class extends Context2.Tag("@ferix/GuardrailsStore")() {
3058
3420
  };
3059
3421
 
3060
3422
  // src/layers/guardrails/file-system.ts
3061
3423
  var PLANS_DIR = ".ferix/plans";
3062
3424
  function ensureDir(dirPath) {
3063
- return Effect4.tryPromise({
3064
- try: () => mkdir(dirPath, { recursive: true }),
3425
+ return Effect6.tryPromise({
3426
+ try: () => mkdir2(dirPath, { recursive: true }),
3065
3427
  catch: (error) => new GuardrailsStoreError({
3066
3428
  message: `Failed to create directory: ${dirPath}`,
3067
3429
  operation: "add",
3068
3430
  cause: error
3069
3431
  })
3070
- }).pipe(Effect4.asVoid);
3432
+ }).pipe(Effect6.asVoid);
3071
3433
  }
3072
3434
  function getSessionDir(sessionId) {
3073
- return join(process.cwd(), PLANS_DIR, sessionId);
3435
+ return join2(process.cwd(), PLANS_DIR, sessionId);
3074
3436
  }
3075
3437
  function getGuardrailsPath(sessionId) {
3076
- return join(getSessionDir(sessionId), "guardrails.json");
3438
+ return join2(getSessionDir(sessionId), "guardrails.json");
3077
3439
  }
3078
3440
  function serializeGuardrails(guardrails) {
3079
3441
  return JSON.stringify(guardrails, null, 2);
3080
3442
  }
3081
3443
  function deserializeGuardrails(json) {
3082
- return Effect4.gen(function* () {
3083
- const parsed = yield* Effect4.try({
3444
+ return Effect6.gen(function* () {
3445
+ const parsed = yield* Effect6.try({
3084
3446
  try: () => JSON.parse(json),
3085
3447
  catch: (error) => new GuardrailsStoreError({
3086
3448
  message: `Invalid JSON in guardrails file: ${String(error)}`,
@@ -3089,7 +3451,7 @@ function deserializeGuardrails(json) {
3089
3451
  })
3090
3452
  });
3091
3453
  const validated = yield* decodeGuardrailsFile(parsed).pipe(
3092
- Effect4.mapError(
3454
+ Effect6.mapError(
3093
3455
  (error) => new GuardrailsStoreError({
3094
3456
  message: `Guardrails validation failed: ${String(error)}`,
3095
3457
  operation: "load",
@@ -3107,12 +3469,12 @@ function createEmptyGuardrails(sessionId, createdAt) {
3107
3469
  guardrails: []
3108
3470
  };
3109
3471
  }
3110
- var make = {
3111
- add: (sessionId, guardrail) => Effect4.gen(function* () {
3472
+ var make2 = {
3473
+ add: (sessionId, guardrail) => Effect6.gen(function* () {
3112
3474
  const sessionDir = getSessionDir(sessionId);
3113
3475
  yield* ensureDir(sessionDir);
3114
3476
  const guardrailsPath = getGuardrailsPath(sessionId);
3115
- const existing = yield* Effect4.tryPromise({
3477
+ const existing = yield* Effect6.tryPromise({
3116
3478
  try: async () => {
3117
3479
  try {
3118
3480
  const content = await readFile(guardrailsPath, "utf-8");
@@ -3130,7 +3492,7 @@ var make = {
3130
3492
  let guardrails;
3131
3493
  if (existing) {
3132
3494
  guardrails = yield* deserializeGuardrails(existing).pipe(
3133
- Effect4.mapError(
3495
+ Effect6.mapError(
3134
3496
  (err) => new GuardrailsStoreError({
3135
3497
  message: err.message,
3136
3498
  operation: "add",
@@ -3146,7 +3508,7 @@ var make = {
3146
3508
  ...guardrails,
3147
3509
  guardrails: [...guardrails.guardrails, guardrail]
3148
3510
  };
3149
- yield* Effect4.tryPromise({
3511
+ yield* Effect6.tryPromise({
3150
3512
  try: () => writeFile(
3151
3513
  guardrailsPath,
3152
3514
  serializeGuardrails(updatedGuardrails),
@@ -3159,9 +3521,9 @@ var make = {
3159
3521
  })
3160
3522
  });
3161
3523
  }),
3162
- load: (sessionId) => Effect4.gen(function* () {
3524
+ load: (sessionId) => Effect6.gen(function* () {
3163
3525
  const guardrailsPath = getGuardrailsPath(sessionId);
3164
- const content = yield* Effect4.tryPromise({
3526
+ const content = yield* Effect6.tryPromise({
3165
3527
  try: async () => {
3166
3528
  try {
3167
3529
  return await readFile(guardrailsPath, "utf-8");
@@ -3181,23 +3543,23 @@ var make = {
3181
3543
  }
3182
3544
  return yield* deserializeGuardrails(content);
3183
3545
  }),
3184
- getActive: (sessionId) => Effect4.gen(function* () {
3185
- const guardrails = yield* make.load(sessionId);
3546
+ getActive: (sessionId) => Effect6.gen(function* () {
3547
+ const guardrails = yield* make2.load(sessionId);
3186
3548
  return guardrails.guardrails;
3187
3549
  })
3188
3550
  };
3189
- var Live = Layer.succeed(GuardrailsStore, make);
3551
+ var Live3 = Layer3.succeed(GuardrailsStore, make2);
3190
3552
  var FileSystemGuardrails = {
3191
- Live
3553
+ Live: Live3
3192
3554
  };
3193
3555
 
3194
3556
  // src/layers/guardrails/memory.ts
3195
3557
  init_esm_shims();
3196
- import { DateTime as DateTime2, Effect as Effect5, Layer as Layer2, Ref as Ref3 } from "effect";
3558
+ import { DateTime as DateTime2, Effect as Effect7, Layer as Layer4, Ref as Ref4 } from "effect";
3197
3559
  function createMemoryGuardrailsStore(stateRef) {
3198
3560
  return {
3199
- add: (sessionId, guardrail) => Effect5.gen(function* () {
3200
- const state = yield* Ref3.get(stateRef);
3561
+ add: (sessionId, guardrail) => Effect7.gen(function* () {
3562
+ const state = yield* Ref4.get(stateRef);
3201
3563
  let guardrails = state.get(sessionId);
3202
3564
  if (!guardrails) {
3203
3565
  const now = yield* DateTime2.now;
@@ -3212,10 +3574,10 @@ function createMemoryGuardrailsStore(stateRef) {
3212
3574
  guardrails: [...guardrails.guardrails, guardrail]
3213
3575
  };
3214
3576
  state.set(sessionId, updatedGuardrails);
3215
- yield* Ref3.set(stateRef, state);
3577
+ yield* Ref4.set(stateRef, state);
3216
3578
  }),
3217
- load: (sessionId) => Effect5.gen(function* () {
3218
- const state = yield* Ref3.get(stateRef);
3579
+ load: (sessionId) => Effect7.gen(function* () {
3580
+ const state = yield* Ref4.get(stateRef);
3219
3581
  const guardrails = state.get(sessionId);
3220
3582
  if (!guardrails) {
3221
3583
  const now = yield* DateTime2.now;
@@ -3227,8 +3589,8 @@ function createMemoryGuardrailsStore(stateRef) {
3227
3589
  }
3228
3590
  return guardrails;
3229
3591
  }),
3230
- getActive: (sessionId) => Effect5.gen(function* () {
3231
- const state = yield* Ref3.get(stateRef);
3592
+ getActive: (sessionId) => Effect7.gen(function* () {
3593
+ const state = yield* Ref4.get(stateRef);
3232
3594
  const guardrails = state.get(sessionId);
3233
3595
  if (!guardrails) {
3234
3596
  return [];
@@ -3237,36 +3599,36 @@ function createMemoryGuardrailsStore(stateRef) {
3237
3599
  })
3238
3600
  };
3239
3601
  }
3240
- function layer() {
3241
- return Layer2.effect(
3602
+ function layer2() {
3603
+ return Layer4.effect(
3242
3604
  GuardrailsStore,
3243
- Effect5.gen(function* () {
3244
- const stateRef = yield* Ref3.make(/* @__PURE__ */ new Map());
3605
+ Effect7.gen(function* () {
3606
+ const stateRef = yield* Ref4.make(/* @__PURE__ */ new Map());
3245
3607
  return createMemoryGuardrailsStore(stateRef);
3246
3608
  })
3247
3609
  );
3248
3610
  }
3249
- var Live2 = layer();
3611
+ var Live4 = layer2();
3250
3612
  var MemoryGuardrails = {
3251
- Live: Live2,
3252
- layer
3613
+ Live: Live4,
3614
+ layer: layer2
3253
3615
  };
3254
3616
 
3255
3617
  // src/layers/llm/claude-cli.ts
3256
3618
  init_esm_shims();
3257
3619
  import { spawn } from "child_process";
3258
- import { Effect as Effect7, Layer as Layer3, Stream as Stream5 } from "effect";
3620
+ import { Effect as Effect9, Layer as Layer5, Stream as Stream5 } from "effect";
3259
3621
 
3260
3622
  // src/services/llm.ts
3261
3623
  init_esm_shims();
3262
- import { Context as Context2 } from "effect";
3263
- var LLM = class extends Context2.Tag("@ferix/LLM")() {
3624
+ import { Context as Context3 } from "effect";
3625
+ var LLM = class extends Context3.Tag("@ferix/LLM")() {
3264
3626
  };
3265
3627
 
3266
3628
  // src/layers/llm/stream.ts
3267
3629
  init_esm_shims();
3268
3630
  import { createInterface } from "readline";
3269
- import { Effect as Effect6, Stream as Stream4 } from "effect";
3631
+ import { Effect as Effect8, Stream as Stream4 } from "effect";
3270
3632
 
3271
3633
  // src/layers/llm/parsers.ts
3272
3634
  init_esm_shims();
@@ -3392,7 +3754,7 @@ function createEventStream(child) {
3392
3754
  emit.fail(
3393
3755
  new LLMError({ message: "Failed to get stdout from child process" })
3394
3756
  );
3395
- return Effect6.void;
3757
+ return Effect8.void;
3396
3758
  }
3397
3759
  const rl = createInterface({
3398
3760
  input: stdout,
@@ -3431,17 +3793,17 @@ function createEventStream(child) {
3431
3793
  })
3432
3794
  );
3433
3795
  });
3434
- return Effect6.sync(() => {
3796
+ return Effect8.sync(() => {
3435
3797
  child.kill("SIGTERM");
3436
3798
  });
3437
3799
  });
3438
3800
  }
3439
3801
 
3440
3802
  // src/layers/llm/claude-cli.ts
3441
- var make2 = {
3442
- execute: (prompt) => {
3803
+ var make3 = {
3804
+ execute: (prompt, options) => {
3443
3805
  return Stream5.unwrap(
3444
- Effect7.sync(() => {
3806
+ Effect9.sync(() => {
3445
3807
  const child = spawn(
3446
3808
  "claude",
3447
3809
  [
@@ -3456,6 +3818,7 @@ var make2 = {
3456
3818
  ],
3457
3819
  {
3458
3820
  stdio: ["inherit", "pipe", "pipe"],
3821
+ cwd: options?.cwd,
3459
3822
  env: {
3460
3823
  ...process.env,
3461
3824
  FORCE_COLOR: "1"
@@ -3467,25 +3830,25 @@ var make2 = {
3467
3830
  );
3468
3831
  }
3469
3832
  };
3470
- var Live3 = Layer3.succeed(LLM, make2);
3833
+ var Live5 = Layer5.succeed(LLM, make3);
3471
3834
  var ClaudeCLI = {
3472
- Live: Live3
3835
+ Live: Live5
3473
3836
  };
3474
3837
 
3475
3838
  // src/layers/llm/mock.ts
3476
3839
  init_esm_shims();
3477
- import { Effect as Effect8, Layer as Layer4, Schema as S14, Stream as Stream6 } from "effect";
3840
+ import { Effect as Effect10, Layer as Layer6, Schema as S14, Stream as Stream6 } from "effect";
3478
3841
  var MockLLMConfigSchema = S14.Struct({
3479
3842
  events: S14.Array(LLMEventSchema),
3480
3843
  delayMs: S14.optional(S14.Number)
3481
3844
  });
3482
3845
  function createMockLLM(config) {
3483
3846
  return {
3484
- execute: (_prompt) => {
3847
+ execute: (_prompt, _options) => {
3485
3848
  const baseStream = Stream6.fromIterable(config.events);
3486
3849
  if (config.delayMs !== void 0 && config.delayMs > 0) {
3487
3850
  const delay = config.delayMs;
3488
- return baseStream.pipe(Stream6.tap(() => Effect8.sleep(delay)));
3851
+ return baseStream.pipe(Stream6.tap(() => Effect10.sleep(delay)));
3489
3852
  }
3490
3853
  return baseStream;
3491
3854
  }
@@ -3502,45 +3865,45 @@ var defaultMockEvents = [
3502
3865
  }
3503
3866
  ];
3504
3867
  var defaultMock = createMockLLM({ events: defaultMockEvents });
3505
- var Live4 = Layer4.succeed(LLM, defaultMock);
3506
- function layer2(config) {
3507
- return Layer4.succeed(LLM, createMockLLM(config));
3868
+ var Live6 = Layer6.succeed(LLM, defaultMock);
3869
+ function layer3(config) {
3870
+ return Layer6.succeed(LLM, createMockLLM(config));
3508
3871
  }
3509
3872
  var Mock = {
3510
- Live: Live4,
3511
- layer: layer2,
3873
+ Live: Live6,
3874
+ layer: layer3,
3512
3875
  createMockLLM
3513
3876
  };
3514
3877
 
3515
3878
  // src/layers/plan/file-system.ts
3516
3879
  init_esm_shims();
3517
- import { access, mkdir as mkdir2, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3518
- import { join as join2 } from "path";
3519
- import { Effect as Effect9, Layer as Layer5 } from "effect";
3880
+ import { access as access2, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3881
+ import { join as join3 } from "path";
3882
+ import { Effect as Effect11, Layer as Layer7 } from "effect";
3520
3883
 
3521
3884
  // src/services/plan-store.ts
3522
3885
  init_esm_shims();
3523
- import { Context as Context3 } from "effect";
3524
- var PlanStore = class extends Context3.Tag("@ferix/PlanStore")() {
3886
+ import { Context as Context4 } from "effect";
3887
+ var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
3525
3888
  };
3526
3889
 
3527
3890
  // src/layers/plan/file-system.ts
3528
3891
  var PLANS_DIR2 = ".ferix/plans";
3529
3892
  function ensureDir2(dirPath) {
3530
- return Effect9.tryPromise({
3531
- try: () => mkdir2(dirPath, { recursive: true }),
3893
+ return Effect11.tryPromise({
3894
+ try: () => mkdir3(dirPath, { recursive: true }),
3532
3895
  catch: (error) => new PlanStoreError({
3533
3896
  message: `Failed to create directory: ${dirPath}`,
3534
3897
  operation: "create",
3535
3898
  cause: error
3536
3899
  })
3537
- }).pipe(Effect9.asVoid);
3900
+ }).pipe(Effect11.asVoid);
3538
3901
  }
3539
3902
  function getSessionDir2(sessionId) {
3540
- return join2(process.cwd(), PLANS_DIR2, sessionId);
3903
+ return join3(process.cwd(), PLANS_DIR2, sessionId);
3541
3904
  }
3542
3905
  function getPlanPath(sessionId, planId) {
3543
- return join2(getSessionDir2(sessionId), `${planId}.json`);
3906
+ return join3(getSessionDir2(sessionId), `${planId}.json`);
3544
3907
  }
3545
3908
  function generatePlanId(taskNumber) {
3546
3909
  return PlanId(`task-${taskNumber}`);
@@ -3549,8 +3912,8 @@ function serializePlan(plan) {
3549
3912
  return JSON.stringify(plan, null, 2);
3550
3913
  }
3551
3914
  function deserializePlan(json, planId) {
3552
- return Effect9.gen(function* () {
3553
- const parsed = yield* Effect9.try({
3915
+ return Effect11.gen(function* () {
3916
+ const parsed = yield* Effect11.try({
3554
3917
  try: () => JSON.parse(json),
3555
3918
  catch: (error) => new PlanStoreError({
3556
3919
  message: `Invalid JSON in plan file: ${String(error)}`,
@@ -3559,7 +3922,7 @@ function deserializePlan(json, planId) {
3559
3922
  })
3560
3923
  });
3561
3924
  const validated = yield* decodePlanData(parsed).pipe(
3562
- Effect9.mapError(
3925
+ Effect11.mapError(
3563
3926
  (error) => new PlanStoreError({
3564
3927
  message: `Plan validation failed: ${String(error)}`,
3565
3928
  operation: "load",
@@ -3573,11 +3936,11 @@ function deserializePlan(json, planId) {
3573
3936
  };
3574
3937
  });
3575
3938
  }
3576
- var make3 = {
3577
- create: (sessionId, plan) => Effect9.gen(function* () {
3939
+ var make4 = {
3940
+ create: (sessionId, plan) => Effect11.gen(function* () {
3578
3941
  const sessionDir = getSessionDir2(sessionId);
3579
3942
  yield* ensureDir2(sessionDir);
3580
- const existingPlans = yield* Effect9.tryPromise({
3943
+ const existingPlans = yield* Effect11.tryPromise({
3581
3944
  try: async () => {
3582
3945
  try {
3583
3946
  const files = await readdir(sessionDir);
@@ -3594,7 +3957,7 @@ var make3 = {
3594
3957
  const planId = generatePlanId(existingPlans + 1);
3595
3958
  const planPath = getPlanPath(sessionId, planId);
3596
3959
  const fullPlan = { ...plan, id: planId };
3597
- yield* Effect9.tryPromise({
3960
+ yield* Effect11.tryPromise({
3598
3961
  try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
3599
3962
  catch: (error) => new PlanStoreError({
3600
3963
  message: `Failed to write plan file: ${planPath}`,
@@ -3604,10 +3967,10 @@ var make3 = {
3604
3967
  });
3605
3968
  return planId;
3606
3969
  }),
3607
- load: (planId, sessionId) => Effect9.gen(function* () {
3970
+ load: (planId, sessionId) => Effect11.gen(function* () {
3608
3971
  if (sessionId) {
3609
3972
  const planPath = getPlanPath(sessionId, planId);
3610
- const content = yield* Effect9.tryPromise({
3973
+ const content = yield* Effect11.tryPromise({
3611
3974
  try: () => readFile2(planPath, "utf-8"),
3612
3975
  catch: (error) => new PlanStoreError({
3613
3976
  message: `Failed to read plan file: ${planPath}`,
@@ -3617,9 +3980,9 @@ var make3 = {
3617
3980
  });
3618
3981
  return yield* deserializePlan(content, planId);
3619
3982
  }
3620
- const sessionDirs = yield* Effect9.tryPromise({
3983
+ const sessionDirs = yield* Effect11.tryPromise({
3621
3984
  try: async () => {
3622
- const plansDir = join2(process.cwd(), PLANS_DIR2);
3985
+ const plansDir = join3(process.cwd(), PLANS_DIR2);
3623
3986
  const dirs = await readdir(plansDir);
3624
3987
  return dirs;
3625
3988
  },
@@ -3631,18 +3994,18 @@ var make3 = {
3631
3994
  });
3632
3995
  for (const sid of sessionDirs) {
3633
3996
  const planPath = getPlanPath(sid, planId);
3634
- const exists = yield* Effect9.tryPromise({
3997
+ const exists = yield* Effect11.tryPromise({
3635
3998
  try: async () => {
3636
- await access(planPath);
3999
+ await access2(planPath);
3637
4000
  return true;
3638
4001
  },
3639
4002
  catch: () => new PlanStoreError({
3640
4003
  message: "File not found",
3641
4004
  operation: "load"
3642
4005
  })
3643
- }).pipe(Effect9.orElseSucceed(() => false));
4006
+ }).pipe(Effect11.orElseSucceed(() => false));
3644
4007
  if (exists) {
3645
- const content = yield* Effect9.tryPromise({
4008
+ const content = yield* Effect11.tryPromise({
3646
4009
  try: () => readFile2(planPath, "utf-8"),
3647
4010
  catch: (error) => new PlanStoreError({
3648
4011
  message: `Failed to read plan file: ${planPath}`,
@@ -3653,16 +4016,16 @@ var make3 = {
3653
4016
  return yield* deserializePlan(content, planId);
3654
4017
  }
3655
4018
  }
3656
- return yield* Effect9.fail(
4019
+ return yield* Effect11.fail(
3657
4020
  new PlanStoreError({
3658
4021
  message: `Plan not found: ${planId}`,
3659
4022
  operation: "load"
3660
4023
  })
3661
4024
  );
3662
4025
  }),
3663
- update: (planId, plan) => Effect9.gen(function* () {
4026
+ update: (planId, plan) => Effect11.gen(function* () {
3664
4027
  const planPath = getPlanPath(plan.sessionId, planId);
3665
- yield* Effect9.tryPromise({
4028
+ yield* Effect11.tryPromise({
3666
4029
  try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
3667
4030
  catch: (error) => new PlanStoreError({
3668
4031
  message: `Failed to update plan file: ${planPath}`,
@@ -3671,9 +4034,9 @@ var make3 = {
3671
4034
  })
3672
4035
  });
3673
4036
  }),
3674
- list: (sessionId) => Effect9.gen(function* () {
4037
+ list: (sessionId) => Effect11.gen(function* () {
3675
4038
  const sessionDir = getSessionDir2(sessionId);
3676
- const files = yield* Effect9.tryPromise({
4039
+ const files = yield* Effect11.tryPromise({
3677
4040
  try: async () => {
3678
4041
  try {
3679
4042
  return await readdir(sessionDir);
@@ -3690,18 +4053,18 @@ var make3 = {
3690
4053
  return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
3691
4054
  })
3692
4055
  };
3693
- var Live5 = Layer5.succeed(PlanStore, make3);
4056
+ var Live7 = Layer7.succeed(PlanStore, make4);
3694
4057
  var FileSystemPlan = {
3695
- Live: Live5
4058
+ Live: Live7
3696
4059
  };
3697
4060
 
3698
4061
  // src/layers/plan/memory.ts
3699
4062
  init_esm_shims();
3700
- import { Effect as Effect10, Layer as Layer6, Ref as Ref4 } from "effect";
4063
+ import { Effect as Effect12, Layer as Layer8, Ref as Ref5 } from "effect";
3701
4064
  function createMemoryPlanStore(stateRef) {
3702
4065
  return {
3703
- create: (sessionId, plan) => Effect10.gen(function* () {
3704
- const state = yield* Ref4.get(stateRef);
4066
+ create: (sessionId, plan) => Effect12.gen(function* () {
4067
+ const state = yield* Ref5.get(stateRef);
3705
4068
  if (!state.has(sessionId)) {
3706
4069
  state.set(sessionId, /* @__PURE__ */ new Map());
3707
4070
  }
@@ -3712,18 +4075,18 @@ function createMemoryPlanStore(stateRef) {
3712
4075
  const planId = PlanId(`task-${sessionPlans.size + 1}`);
3713
4076
  const fullPlan = { ...plan, id: planId };
3714
4077
  sessionPlans.set(planId, fullPlan);
3715
- yield* Ref4.set(stateRef, state);
4078
+ yield* Ref5.set(stateRef, state);
3716
4079
  return planId;
3717
4080
  }),
3718
- load: (planId, sessionId) => Effect10.gen(function* () {
3719
- const state = yield* Ref4.get(stateRef);
4081
+ load: (planId, sessionId) => Effect12.gen(function* () {
4082
+ const state = yield* Ref5.get(stateRef);
3720
4083
  if (sessionId) {
3721
4084
  const sessionPlans = state.get(sessionId);
3722
4085
  const plan = sessionPlans?.get(planId);
3723
4086
  if (plan) {
3724
4087
  return plan;
3725
4088
  }
3726
- return yield* Effect10.fail(
4089
+ return yield* Effect12.fail(
3727
4090
  new PlanStoreError({
3728
4091
  message: `Plan not found: ${planId}`,
3729
4092
  operation: "load"
@@ -3736,18 +4099,18 @@ function createMemoryPlanStore(stateRef) {
3736
4099
  return plan;
3737
4100
  }
3738
4101
  }
3739
- return yield* Effect10.fail(
4102
+ return yield* Effect12.fail(
3740
4103
  new PlanStoreError({
3741
4104
  message: `Plan not found: ${planId}`,
3742
4105
  operation: "load"
3743
4106
  })
3744
4107
  );
3745
4108
  }),
3746
- update: (planId, plan) => Effect10.gen(function* () {
3747
- const state = yield* Ref4.get(stateRef);
4109
+ update: (planId, plan) => Effect12.gen(function* () {
4110
+ const state = yield* Ref5.get(stateRef);
3748
4111
  const sessionPlans = state.get(plan.sessionId);
3749
4112
  if (!sessionPlans) {
3750
- return yield* Effect10.fail(
4113
+ return yield* Effect12.fail(
3751
4114
  new PlanStoreError({
3752
4115
  message: `Session not found: ${plan.sessionId}`,
3753
4116
  operation: "update"
@@ -3755,10 +4118,10 @@ function createMemoryPlanStore(stateRef) {
3755
4118
  );
3756
4119
  }
3757
4120
  sessionPlans.set(planId, plan);
3758
- yield* Ref4.set(stateRef, state);
4121
+ yield* Ref5.set(stateRef, state);
3759
4122
  }),
3760
- list: (sessionId) => Effect10.gen(function* () {
3761
- const state = yield* Ref4.get(stateRef);
4123
+ list: (sessionId) => Effect12.gen(function* () {
4124
+ const state = yield* Ref5.get(stateRef);
3762
4125
  const sessionPlans = state.get(sessionId);
3763
4126
  if (!sessionPlans) {
3764
4127
  return [];
@@ -3767,57 +4130,57 @@ function createMemoryPlanStore(stateRef) {
3767
4130
  })
3768
4131
  };
3769
4132
  }
3770
- function layer3() {
3771
- return Layer6.effect(
4133
+ function layer4() {
4134
+ return Layer8.effect(
3772
4135
  PlanStore,
3773
- Effect10.gen(function* () {
3774
- const stateRef = yield* Ref4.make(/* @__PURE__ */ new Map());
4136
+ Effect12.gen(function* () {
4137
+ const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
3775
4138
  return createMemoryPlanStore(stateRef);
3776
4139
  })
3777
4140
  );
3778
4141
  }
3779
- var Live6 = layer3();
4142
+ var Live8 = layer4();
3780
4143
  var MemoryPlan = {
3781
- Live: Live6,
3782
- layer: layer3
4144
+ Live: Live8,
4145
+ layer: layer4
3783
4146
  };
3784
4147
 
3785
4148
  // src/layers/progress/file-system.ts
3786
4149
  init_esm_shims();
3787
- import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
3788
- import { join as join3 } from "path";
3789
- import { DateTime as DateTime3, Effect as Effect11, Layer as Layer7 } from "effect";
4150
+ import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
4151
+ import { join as join4 } from "path";
4152
+ import { DateTime as DateTime3, Effect as Effect13, Layer as Layer9 } from "effect";
3790
4153
 
3791
4154
  // src/services/progress-store.ts
3792
4155
  init_esm_shims();
3793
- import { Context as Context4 } from "effect";
3794
- var ProgressStore = class extends Context4.Tag("@ferix/ProgressStore")() {
4156
+ import { Context as Context5 } from "effect";
4157
+ var ProgressStore = class extends Context5.Tag("@ferix/ProgressStore")() {
3795
4158
  };
3796
4159
 
3797
4160
  // src/layers/progress/file-system.ts
3798
4161
  var PLANS_DIR3 = ".ferix/plans";
3799
4162
  function ensureDir3(dirPath) {
3800
- return Effect11.tryPromise({
3801
- try: () => mkdir3(dirPath, { recursive: true }),
4163
+ return Effect13.tryPromise({
4164
+ try: () => mkdir4(dirPath, { recursive: true }),
3802
4165
  catch: (error) => new ProgressStoreError({
3803
4166
  message: `Failed to create directory: ${dirPath}`,
3804
4167
  operation: "append",
3805
4168
  cause: error
3806
4169
  })
3807
- }).pipe(Effect11.asVoid);
4170
+ }).pipe(Effect13.asVoid);
3808
4171
  }
3809
4172
  function getSessionDir3(sessionId) {
3810
- return join3(process.cwd(), PLANS_DIR3, sessionId);
4173
+ return join4(process.cwd(), PLANS_DIR3, sessionId);
3811
4174
  }
3812
4175
  function getProgressPath(sessionId) {
3813
- return join3(getSessionDir3(sessionId), "progress.json");
4176
+ return join4(getSessionDir3(sessionId), "progress.json");
3814
4177
  }
3815
4178
  function serializeProgress(progress) {
3816
4179
  return JSON.stringify(progress, null, 2);
3817
4180
  }
3818
4181
  function deserializeProgress(json) {
3819
- return Effect11.gen(function* () {
3820
- const parsed = yield* Effect11.try({
4182
+ return Effect13.gen(function* () {
4183
+ const parsed = yield* Effect13.try({
3821
4184
  try: () => JSON.parse(json),
3822
4185
  catch: (error) => new ProgressStoreError({
3823
4186
  message: `Invalid JSON in progress file: ${String(error)}`,
@@ -3826,7 +4189,7 @@ function deserializeProgress(json) {
3826
4189
  })
3827
4190
  });
3828
4191
  const validated = yield* decodeProgressFile(parsed).pipe(
3829
- Effect11.mapError(
4192
+ Effect13.mapError(
3830
4193
  (error) => new ProgressStoreError({
3831
4194
  message: `Progress validation failed: ${String(error)}`,
3832
4195
  operation: "load",
@@ -3844,12 +4207,12 @@ function createEmptyProgress(sessionId, createdAt) {
3844
4207
  entries: []
3845
4208
  };
3846
4209
  }
3847
- var make4 = {
3848
- append: (sessionId, entry) => Effect11.gen(function* () {
4210
+ var make5 = {
4211
+ append: (sessionId, entry) => Effect13.gen(function* () {
3849
4212
  const sessionDir = getSessionDir3(sessionId);
3850
4213
  yield* ensureDir3(sessionDir);
3851
4214
  const progressPath = getProgressPath(sessionId);
3852
- const existing = yield* Effect11.tryPromise({
4215
+ const existing = yield* Effect13.tryPromise({
3853
4216
  try: async () => {
3854
4217
  try {
3855
4218
  const content = await readFile3(progressPath, "utf-8");
@@ -3867,7 +4230,7 @@ var make4 = {
3867
4230
  let progress;
3868
4231
  if (existing) {
3869
4232
  progress = yield* deserializeProgress(existing).pipe(
3870
- Effect11.mapError(
4233
+ Effect13.mapError(
3871
4234
  (err) => new ProgressStoreError({
3872
4235
  message: err.message,
3873
4236
  operation: "append",
@@ -3883,7 +4246,7 @@ var make4 = {
3883
4246
  ...progress,
3884
4247
  entries: [...progress.entries, entry]
3885
4248
  };
3886
- yield* Effect11.tryPromise({
4249
+ yield* Effect13.tryPromise({
3887
4250
  try: () => writeFile3(progressPath, serializeProgress(updatedProgress), "utf-8"),
3888
4251
  catch: (error) => new ProgressStoreError({
3889
4252
  message: `Failed to write progress file: ${progressPath}`,
@@ -3892,9 +4255,9 @@ var make4 = {
3892
4255
  })
3893
4256
  });
3894
4257
  }),
3895
- load: (sessionId) => Effect11.gen(function* () {
4258
+ load: (sessionId) => Effect13.gen(function* () {
3896
4259
  const progressPath = getProgressPath(sessionId);
3897
- const content = yield* Effect11.tryPromise({
4260
+ const content = yield* Effect13.tryPromise({
3898
4261
  try: async () => {
3899
4262
  try {
3900
4263
  return await readFile3(progressPath, "utf-8");
@@ -3914,24 +4277,24 @@ var make4 = {
3914
4277
  }
3915
4278
  return yield* deserializeProgress(content);
3916
4279
  }),
3917
- getRecent: (sessionId, count) => Effect11.gen(function* () {
3918
- const progress = yield* make4.load(sessionId);
4280
+ getRecent: (sessionId, count) => Effect13.gen(function* () {
4281
+ const progress = yield* make5.load(sessionId);
3919
4282
  const entries = progress.entries;
3920
4283
  return entries.slice(-count);
3921
4284
  })
3922
4285
  };
3923
- var Live7 = Layer7.succeed(ProgressStore, make4);
4286
+ var Live9 = Layer9.succeed(ProgressStore, make5);
3924
4287
  var FileSystemProgress = {
3925
- Live: Live7
4288
+ Live: Live9
3926
4289
  };
3927
4290
 
3928
4291
  // src/layers/progress/memory.ts
3929
4292
  init_esm_shims();
3930
- import { DateTime as DateTime4, Effect as Effect12, Layer as Layer8, Ref as Ref5 } from "effect";
4293
+ import { DateTime as DateTime4, Effect as Effect14, Layer as Layer10, Ref as Ref6 } from "effect";
3931
4294
  function createMemoryProgressStore(stateRef) {
3932
4295
  return {
3933
- append: (sessionId, entry) => Effect12.gen(function* () {
3934
- const state = yield* Ref5.get(stateRef);
4296
+ append: (sessionId, entry) => Effect14.gen(function* () {
4297
+ const state = yield* Ref6.get(stateRef);
3935
4298
  let progress = state.get(sessionId);
3936
4299
  if (!progress) {
3937
4300
  const now = yield* DateTime4.now;
@@ -3946,10 +4309,10 @@ function createMemoryProgressStore(stateRef) {
3946
4309
  entries: [...progress.entries, entry]
3947
4310
  };
3948
4311
  state.set(sessionId, updatedProgress);
3949
- yield* Ref5.set(stateRef, state);
4312
+ yield* Ref6.set(stateRef, state);
3950
4313
  }),
3951
- load: (sessionId) => Effect12.gen(function* () {
3952
- const state = yield* Ref5.get(stateRef);
4314
+ load: (sessionId) => Effect14.gen(function* () {
4315
+ const state = yield* Ref6.get(stateRef);
3953
4316
  const progress = state.get(sessionId);
3954
4317
  if (!progress) {
3955
4318
  const now = yield* DateTime4.now;
@@ -3961,8 +4324,8 @@ function createMemoryProgressStore(stateRef) {
3961
4324
  }
3962
4325
  return progress;
3963
4326
  }),
3964
- getRecent: (sessionId, count) => Effect12.gen(function* () {
3965
- const state = yield* Ref5.get(stateRef);
4327
+ getRecent: (sessionId, count) => Effect14.gen(function* () {
4328
+ const state = yield* Ref6.get(stateRef);
3966
4329
  const progress = state.get(sessionId);
3967
4330
  if (!progress) {
3968
4331
  return [];
@@ -3971,32 +4334,32 @@ function createMemoryProgressStore(stateRef) {
3971
4334
  })
3972
4335
  };
3973
4336
  }
3974
- function layer4() {
3975
- return Layer8.effect(
4337
+ function layer5() {
4338
+ return Layer10.effect(
3976
4339
  ProgressStore,
3977
- Effect12.gen(function* () {
3978
- const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
4340
+ Effect14.gen(function* () {
4341
+ const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
3979
4342
  return createMemoryProgressStore(stateRef);
3980
4343
  })
3981
4344
  );
3982
4345
  }
3983
- var Live8 = layer4();
4346
+ var Live10 = layer5();
3984
4347
  var MemoryProgress = {
3985
- Live: Live8,
3986
- layer: layer4
4348
+ Live: Live10,
4349
+ layer: layer5
3987
4350
  };
3988
4351
 
3989
4352
  // src/layers/session/file-system.ts
3990
4353
  init_esm_shims();
3991
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
3992
- import { join as join4 } from "path";
3993
- import { DateTime as DateTime5, Effect as Effect13, Layer as Layer9 } from "effect";
4354
+ import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
4355
+ import { join as join5 } from "path";
4356
+ import { DateTime as DateTime5, Effect as Effect15, Layer as Layer11 } from "effect";
3994
4357
  import { humanId } from "human-id";
3995
4358
 
3996
4359
  // src/services/session-store.ts
3997
4360
  init_esm_shims();
3998
- import { Context as Context5 } from "effect";
3999
- var SessionStore = class extends Context5.Tag("@ferix/SessionStore")() {
4361
+ import { Context as Context6 } from "effect";
4362
+ var SessionStore = class extends Context6.Tag("@ferix/SessionStore")() {
4000
4363
  };
4001
4364
 
4002
4365
  // src/layers/session/file-system.ts
@@ -4006,24 +4369,24 @@ function generateSessionId(timestampMs) {
4006
4369
  return `${id}-${timestampMs}`;
4007
4370
  }
4008
4371
  function ensureDir4(dirPath) {
4009
- return Effect13.tryPromise({
4010
- try: () => mkdir4(dirPath, { recursive: true }),
4372
+ return Effect15.tryPromise({
4373
+ try: () => mkdir5(dirPath, { recursive: true }),
4011
4374
  catch: (error) => new SessionStoreError({
4012
4375
  message: `Failed to create directory: ${dirPath}`,
4013
4376
  operation: "create",
4014
4377
  cause: error
4015
4378
  })
4016
- }).pipe(Effect13.asVoid);
4379
+ }).pipe(Effect15.asVoid);
4017
4380
  }
4018
4381
  function getSessionPath(sessionId) {
4019
- return join4(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
4382
+ return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
4020
4383
  }
4021
4384
  function serializeSession(session) {
4022
4385
  return JSON.stringify(session, null, 2);
4023
4386
  }
4024
4387
  function deserializeSession(json) {
4025
- return Effect13.gen(function* () {
4026
- const parsed = yield* Effect13.try({
4388
+ return Effect15.gen(function* () {
4389
+ const parsed = yield* Effect15.try({
4027
4390
  try: () => JSON.parse(json),
4028
4391
  catch: (error) => new SessionStoreError({
4029
4392
  message: `Invalid JSON in session file: ${String(error)}`,
@@ -4032,7 +4395,7 @@ function deserializeSession(json) {
4032
4395
  })
4033
4396
  });
4034
4397
  const validated = yield* decodeSession(parsed).pipe(
4035
- Effect13.mapError(
4398
+ Effect15.mapError(
4036
4399
  (error) => new SessionStoreError({
4037
4400
  message: `Session validation failed: ${String(error)}`,
4038
4401
  operation: "get",
@@ -4043,9 +4406,9 @@ function deserializeSession(json) {
4043
4406
  return validated;
4044
4407
  });
4045
4408
  }
4046
- var make5 = {
4047
- create: (originalTask) => Effect13.gen(function* () {
4048
- const sessionsDir = join4(process.cwd(), SESSIONS_DIR);
4409
+ var make6 = {
4410
+ create: (originalTask) => Effect15.gen(function* () {
4411
+ const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
4049
4412
  yield* ensureDir4(sessionsDir);
4050
4413
  const now = yield* DateTime5.now;
4051
4414
  const timestampMs = DateTime5.toEpochMillis(now);
@@ -4058,7 +4421,7 @@ var make5 = {
4058
4421
  completedTasks: []
4059
4422
  };
4060
4423
  const sessionPath = getSessionPath(sessionId);
4061
- yield* Effect13.tryPromise({
4424
+ yield* Effect15.tryPromise({
4062
4425
  try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
4063
4426
  catch: (error) => new SessionStoreError({
4064
4427
  message: `Failed to write session file: ${sessionPath}`,
@@ -4068,9 +4431,9 @@ var make5 = {
4068
4431
  });
4069
4432
  return session;
4070
4433
  }),
4071
- get: (sessionId) => Effect13.gen(function* () {
4434
+ get: (sessionId) => Effect15.gen(function* () {
4072
4435
  const sessionPath = getSessionPath(sessionId);
4073
- const content = yield* Effect13.tryPromise({
4436
+ const content = yield* Effect15.tryPromise({
4074
4437
  try: () => readFile4(sessionPath, "utf-8"),
4075
4438
  catch: (error) => new SessionStoreError({
4076
4439
  message: `Failed to read session file: ${sessionPath}`,
@@ -4080,9 +4443,9 @@ var make5 = {
4080
4443
  });
4081
4444
  return yield* deserializeSession(content);
4082
4445
  }),
4083
- update: (sessionId, session) => Effect13.gen(function* () {
4446
+ update: (sessionId, session) => Effect15.gen(function* () {
4084
4447
  const sessionPath = getSessionPath(sessionId);
4085
- yield* Effect13.tryPromise({
4448
+ yield* Effect15.tryPromise({
4086
4449
  try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
4087
4450
  catch: (error) => new SessionStoreError({
4088
4451
  message: `Failed to update session file: ${sessionPath}`,
@@ -4092,19 +4455,19 @@ var make5 = {
4092
4455
  });
4093
4456
  })
4094
4457
  };
4095
- var Live9 = Layer9.succeed(SessionStore, make5);
4458
+ var Live11 = Layer11.succeed(SessionStore, make6);
4096
4459
  var FileSystemSession = {
4097
- Live: Live9
4460
+ Live: Live11
4098
4461
  };
4099
4462
 
4100
4463
  // src/layers/session/memory.ts
4101
4464
  init_esm_shims();
4102
- import { DateTime as DateTime6, Effect as Effect14, Layer as Layer10, Ref as Ref6 } from "effect";
4465
+ import { DateTime as DateTime6, Effect as Effect16, Layer as Layer12, Ref as Ref7 } from "effect";
4103
4466
  function createMemorySessionStore(stateRef, counterRef) {
4104
4467
  return {
4105
- create: (originalTask) => Effect14.gen(function* () {
4106
- const state = yield* Ref6.get(stateRef);
4107
- const counter = yield* Ref6.updateAndGet(counterRef, (n) => n + 1);
4468
+ create: (originalTask) => Effect16.gen(function* () {
4469
+ const state = yield* Ref7.get(stateRef);
4470
+ const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
4108
4471
  const sessionId = `test-session-${counter}`;
4109
4472
  const now = yield* DateTime6.now;
4110
4473
  const session = {
@@ -4115,14 +4478,14 @@ function createMemorySessionStore(stateRef, counterRef) {
4115
4478
  completedTasks: []
4116
4479
  };
4117
4480
  state.set(sessionId, session);
4118
- yield* Ref6.set(stateRef, state);
4481
+ yield* Ref7.set(stateRef, state);
4119
4482
  return session;
4120
4483
  }),
4121
- get: (sessionId) => Effect14.gen(function* () {
4122
- const state = yield* Ref6.get(stateRef);
4484
+ get: (sessionId) => Effect16.gen(function* () {
4485
+ const state = yield* Ref7.get(stateRef);
4123
4486
  const session = state.get(sessionId);
4124
4487
  if (!session) {
4125
- return yield* Effect14.fail(
4488
+ return yield* Effect16.fail(
4126
4489
  new SessionStoreError({
4127
4490
  message: `Session not found: ${sessionId}`,
4128
4491
  operation: "get"
@@ -4131,10 +4494,10 @@ function createMemorySessionStore(stateRef, counterRef) {
4131
4494
  }
4132
4495
  return session;
4133
4496
  }),
4134
- update: (sessionId, session) => Effect14.gen(function* () {
4135
- const state = yield* Ref6.get(stateRef);
4497
+ update: (sessionId, session) => Effect16.gen(function* () {
4498
+ const state = yield* Ref7.get(stateRef);
4136
4499
  if (!state.has(sessionId)) {
4137
- return yield* Effect14.fail(
4500
+ return yield* Effect16.fail(
4138
4501
  new SessionStoreError({
4139
4502
  message: `Session not found: ${sessionId}`,
4140
4503
  operation: "update"
@@ -4142,34 +4505,34 @@ function createMemorySessionStore(stateRef, counterRef) {
4142
4505
  );
4143
4506
  }
4144
4507
  state.set(sessionId, session);
4145
- yield* Ref6.set(stateRef, state);
4508
+ yield* Ref7.set(stateRef, state);
4146
4509
  })
4147
4510
  };
4148
4511
  }
4149
- function layer5() {
4150
- return Layer10.effect(
4512
+ function layer6() {
4513
+ return Layer12.effect(
4151
4514
  SessionStore,
4152
- Effect14.gen(function* () {
4153
- const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
4154
- const counterRef = yield* Ref6.make(0);
4515
+ Effect16.gen(function* () {
4516
+ const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
4517
+ const counterRef = yield* Ref7.make(0);
4155
4518
  return createMemorySessionStore(stateRef, counterRef);
4156
4519
  })
4157
4520
  );
4158
4521
  }
4159
- var Live10 = layer5();
4522
+ var Live12 = layer6();
4160
4523
  var MemorySession = {
4161
- Live: Live10,
4162
- layer: layer5
4524
+ Live: Live12,
4525
+ layer: layer6
4163
4526
  };
4164
4527
 
4165
4528
  // src/layers/signal/ferix-parser.ts
4166
4529
  init_esm_shims();
4167
- import { Effect as Effect15, Layer as Layer11, Ref as Ref7 } from "effect";
4530
+ import { Effect as Effect17, Layer as Layer13, Ref as Ref8 } from "effect";
4168
4531
 
4169
4532
  // src/services/signal-parser.ts
4170
4533
  init_esm_shims();
4171
- import { Context as Context6 } from "effect";
4172
- var SignalParser = class extends Context6.Tag("@ferix/SignalParser")() {
4534
+ import { Context as Context7 } from "effect";
4535
+ var SignalParser = class extends Context7.Tag("@ferix/SignalParser")() {
4173
4536
  };
4174
4537
 
4175
4538
  // src/layers/signal/specs/index.ts
@@ -4674,20 +5037,20 @@ signalSpecRegistry.register(tasksDefinedSpec);
4674
5037
  // src/layers/signal/ferix-parser.ts
4675
5038
  var MAX_BUFFER_SIZE = 1024 * 1024;
4676
5039
  function createAccumulatorImpl() {
4677
- return Effect15.gen(function* () {
4678
- const chunksRef = yield* Ref7.make([]);
4679
- const emittedRef = yield* Ref7.make(/* @__PURE__ */ new Set());
4680
- const feed = (text) => Effect15.gen(function* () {
4681
- const chunks = yield* Ref7.get(chunksRef);
5040
+ return Effect17.gen(function* () {
5041
+ const chunksRef = yield* Ref8.make([]);
5042
+ const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
5043
+ const feed = (text) => Effect17.gen(function* () {
5044
+ const chunks = yield* Ref8.get(chunksRef);
4682
5045
  chunks.push(text);
4683
5046
  const buffer = chunks.join("");
4684
5047
  if (buffer.length > MAX_BUFFER_SIZE) {
4685
- yield* Ref7.set(chunksRef, [
5048
+ yield* Ref8.set(chunksRef, [
4686
5049
  buffer.slice(buffer.length - MAX_BUFFER_SIZE)
4687
5050
  ]);
4688
5051
  }
4689
5052
  const signals = signalSpecRegistry.parseAll(buffer);
4690
- const emitted = yield* Ref7.get(emittedRef);
5053
+ const emitted = yield* Ref8.get(emittedRef);
4691
5054
  const newSignals = signals.filter((signal) => {
4692
5055
  const key = signalSpecRegistry.getSignalKey(signal);
4693
5056
  if (emitted.has(key)) {
@@ -4699,61 +5062,64 @@ function createAccumulatorImpl() {
4699
5062
  if (newSignals.length > 0) {
4700
5063
  const lastEndPos = signalSpecRegistry.findLastCompleteSignalEnd(buffer);
4701
5064
  if (lastEndPos > 0 && lastEndPos < buffer.length) {
4702
- yield* Ref7.set(chunksRef, [buffer.slice(lastEndPos)]);
5065
+ yield* Ref8.set(chunksRef, [buffer.slice(lastEndPos)]);
4703
5066
  }
4704
5067
  }
4705
- yield* Ref7.set(emittedRef, emitted);
5068
+ yield* Ref8.set(emittedRef, emitted);
4706
5069
  return newSignals;
4707
5070
  });
4708
- const flush = () => Effect15.gen(function* () {
4709
- const chunks = yield* Ref7.get(chunksRef);
5071
+ const flush = () => Effect17.gen(function* () {
5072
+ const chunks = yield* Ref8.get(chunksRef);
4710
5073
  const buffer = chunks.join("");
4711
- yield* Ref7.set(chunksRef, []);
4712
- const emitted = yield* Ref7.get(emittedRef);
5074
+ yield* Ref8.set(chunksRef, []);
5075
+ const emitted = yield* Ref8.get(emittedRef);
4713
5076
  const signals = signalSpecRegistry.parseAll(buffer);
4714
5077
  const result = signals.filter(
4715
5078
  (signal) => !emitted.has(signalSpecRegistry.getSignalKey(signal))
4716
5079
  );
4717
- yield* Ref7.set(emittedRef, /* @__PURE__ */ new Set());
5080
+ yield* Ref8.set(emittedRef, /* @__PURE__ */ new Set());
4718
5081
  return result;
4719
5082
  });
4720
5083
  return { feed, flush };
4721
5084
  });
4722
5085
  }
4723
- var make6 = {
4724
- parse: (text) => Effect15.succeed(signalSpecRegistry.parseAll(text)),
5086
+ var make7 = {
5087
+ parse: (text) => Effect17.succeed(signalSpecRegistry.parseAll(text)),
4725
5088
  createAccumulator: createAccumulatorImpl
4726
5089
  };
4727
- var Live11 = Layer11.succeed(SignalParser, make6);
5090
+ var Live13 = Layer13.succeed(SignalParser, make7);
4728
5091
  var FerixParser = {
4729
- Live: Live11
5092
+ Live: Live13
4730
5093
  };
4731
5094
 
4732
5095
  // src/layers/index.ts
4733
- var ProductionLayers = Layer12.mergeAll(
5096
+ var ProductionLayers = Layer14.mergeAll(
4734
5097
  ClaudeCLI.Live,
4735
5098
  FerixParser.Live,
4736
5099
  FileSystemPlan.Live,
4737
5100
  FileSystemSession.Live,
4738
5101
  FileSystemProgress.Live,
4739
- FileSystemGuardrails.Live
5102
+ FileSystemGuardrails.Live,
5103
+ FileSystemGit.Live
4740
5104
  );
4741
- var TestLayers = Layer12.mergeAll(
5105
+ var TestLayers = Layer14.mergeAll(
4742
5106
  Mock.Live,
4743
5107
  FerixParser.Live,
4744
5108
  MemoryPlan.Live,
4745
5109
  MemorySession.Live,
4746
5110
  MemoryProgress.Live,
4747
- MemoryGuardrails.Live
5111
+ MemoryGuardrails.Live,
5112
+ MemoryGit.Live
4748
5113
  );
4749
5114
  function createTestLayers(events) {
4750
- return Layer12.mergeAll(
5115
+ return Layer14.mergeAll(
4751
5116
  Mock.layer({ events }),
4752
5117
  FerixParser.Live,
4753
5118
  MemoryPlan.layer(),
4754
5119
  MemorySession.layer(),
4755
5120
  MemoryProgress.layer(),
4756
- MemoryGuardrails.layer()
5121
+ MemoryGuardrails.layer(),
5122
+ MemoryGit.layer()
4757
5123
  );
4758
5124
  }
4759
5125
 
@@ -4762,41 +5128,41 @@ init_esm_shims();
4762
5128
 
4763
5129
  // src/orchestrator/loop.ts
4764
5130
  init_esm_shims();
4765
- import { DateTime as DateTime10, Effect as Effect20, Option, pipe as pipe3, Ref as Ref11, Stream as Stream9 } from "effect";
5131
+ import { DateTime as DateTime10, Effect as Effect22, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
4766
5132
 
4767
5133
  // src/orchestrator/discovery.ts
4768
5134
  init_esm_shims();
4769
- import { DateTime as DateTime8, Effect as Effect18, pipe, Ref as Ref9, Stream as Stream7 } from "effect";
5135
+ import { DateTime as DateTime8, Effect as Effect20, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
4770
5136
 
4771
5137
  // src/layers/plan/task-generation.ts
4772
5138
  init_esm_shims();
4773
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
4774
- import { join as join5 } from "path";
4775
- import { Effect as Effect16 } from "effect";
5139
+ import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
5140
+ import { join as join6 } from "path";
5141
+ import { Effect as Effect18 } from "effect";
4776
5142
  var PLANS_DIR4 = ".ferix/plans";
4777
5143
  function ensureDir5(dirPath) {
4778
- return Effect16.tryPromise({
4779
- try: () => mkdir5(dirPath, { recursive: true }),
5144
+ return Effect18.tryPromise({
5145
+ try: () => mkdir6(dirPath, { recursive: true }),
4780
5146
  catch: (error) => new PlanStoreError({
4781
5147
  message: `Failed to create directory: ${dirPath}`,
4782
5148
  operation: "create",
4783
5149
  cause: error
4784
5150
  })
4785
- }).pipe(Effect16.asVoid);
5151
+ }).pipe(Effect18.asVoid);
4786
5152
  }
4787
5153
  function getSessionDir4(sessionId) {
4788
- return join5(process.cwd(), PLANS_DIR4, sessionId);
5154
+ return join6(process.cwd(), PLANS_DIR4, sessionId);
4789
5155
  }
4790
5156
  function getTasksMdPath(sessionId) {
4791
- return join5(getSessionDir4(sessionId), "tasks.md");
5157
+ return join6(getSessionDir4(sessionId), "tasks.md");
4792
5158
  }
4793
5159
  function writeTasksMd(sessionId, tasks) {
4794
- return Effect16.gen(function* () {
5160
+ return Effect18.gen(function* () {
4795
5161
  const sessionDir = getSessionDir4(sessionId);
4796
5162
  yield* ensureDir5(sessionDir);
4797
5163
  const tasksMdPath = getTasksMdPath(sessionId);
4798
5164
  const content = formatTasksMd(tasks);
4799
- yield* Effect16.tryPromise({
5165
+ yield* Effect18.tryPromise({
4800
5166
  try: () => writeFile5(tasksMdPath, content, "utf-8"),
4801
5167
  catch: (error) => new PlanStoreError({
4802
5168
  message: `Failed to write tasks.md: ${tasksMdPath}`,
@@ -4978,7 +5344,7 @@ function mapSignalToDomain(signal, context) {
4978
5344
 
4979
5345
  // src/orchestrator/plan-updates.ts
4980
5346
  init_esm_shims();
4981
- import { DateTime as DateTime7, Effect as Effect17, Ref as Ref8 } from "effect";
5347
+ import { DateTime as DateTime7, Effect as Effect19, Ref as Ref9 } from "effect";
4982
5348
 
4983
5349
  // src/orchestrator/plan-updates/index.ts
4984
5350
  init_esm_shims();
@@ -5283,9 +5649,9 @@ function persistPlanUpdate(planStore, plan, operation) {
5283
5649
  tasks: plan.tasks
5284
5650
  }) : planStore.update(plan.id, plan);
5285
5651
  return storeOp.pipe(
5286
- Effect17.map(() => null),
5287
- Effect17.catchAll(
5288
- (error) => Effect17.succeed({
5652
+ Effect19.map(() => null),
5653
+ Effect19.catchAll(
5654
+ (error) => Effect19.succeed({
5289
5655
  _tag: "PlanUpdateFailed",
5290
5656
  operation,
5291
5657
  error: error.message,
@@ -5295,8 +5661,8 @@ function persistPlanUpdate(planStore, plan, operation) {
5295
5661
  );
5296
5662
  }
5297
5663
  function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
5298
- return Effect17.gen(function* () {
5299
- const currentPlan = yield* Ref8.get(currentPlanRef);
5664
+ return Effect19.gen(function* () {
5665
+ const currentPlan = yield* Ref9.get(currentPlanRef);
5300
5666
  const now = yield* DateTime7.now;
5301
5667
  const timestamp = DateTime7.formatIso(now);
5302
5668
  const updateResult = computePlanUpdate(signal, currentPlan, {
@@ -5308,8 +5674,8 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
5308
5674
  return [];
5309
5675
  }
5310
5676
  const { plan, operation, eventTag } = updateResult;
5311
- yield* Ref8.set(currentPlanRef, plan);
5312
- yield* Ref8.update(persistenceStateRef, (state) => ({
5677
+ yield* Ref9.set(currentPlanRef, plan);
5678
+ yield* Ref9.update(persistenceStateRef, (state) => ({
5313
5679
  dirty: true,
5314
5680
  pendingOperation: state.pendingOperation === "create" ? "create" : operation
5315
5681
  }));
@@ -5317,12 +5683,12 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
5317
5683
  });
5318
5684
  }
5319
5685
  function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
5320
- return Effect17.gen(function* () {
5321
- const state = yield* Ref8.get(persistenceStateRef);
5686
+ return Effect19.gen(function* () {
5687
+ const state = yield* Ref9.get(persistenceStateRef);
5322
5688
  if (!(state.dirty && state.pendingOperation)) {
5323
5689
  return [];
5324
5690
  }
5325
- const plan = yield* Ref8.get(currentPlanRef);
5691
+ const plan = yield* Ref9.get(currentPlanRef);
5326
5692
  if (!plan) {
5327
5693
  return [];
5328
5694
  }
@@ -5335,7 +5701,7 @@ function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
5335
5701
  if (failureEvent) {
5336
5702
  events.push(failureEvent);
5337
5703
  }
5338
- yield* Ref8.set(persistenceStateRef, {
5704
+ yield* Ref9.set(persistenceStateRef, {
5339
5705
  dirty: false,
5340
5706
  pendingOperation: null
5341
5707
  });
@@ -5394,9 +5760,12 @@ Use these XML-like tags to communicate structured information:
5394
5760
  <files-created>new-file.ts</files-created>
5395
5761
  </ferix:task-complete>
5396
5762
 
5397
- ### Loop Completion
5763
+ ### Loop Completion (use ONLY when ALL tasks are done)
5398
5764
  <ferix:complete>
5399
5765
 
5766
+ \u26A0\uFE0F IMPORTANT: Only emit <ferix:complete> after ALL tasks in the plan are complete.
5767
+ After completing a single task, emit <ferix:task-complete> and continue to the next task.
5768
+
5400
5769
  IMPORTANT: Always emit signals on their own lines, never inside markdown code blocks.`;
5401
5770
  var DISCOVERY_SYSTEM_PROMPT = `You are in the DISCOVERY phase of a ralph loop - an iterative AI coding workflow.
5402
5771
 
@@ -5437,14 +5806,17 @@ Review the code for quality:
5437
5806
  - When done, emit <ferix:review-complete/>`;
5438
5807
  var DEFAULT_COMPLETION_PROMPT = `## Completion
5439
5808
 
5440
- When the task is complete, emit:
5809
+ When you finish the CURRENT task, emit:
5441
5810
  <ferix:task-complete id="N">
5442
5811
  <summary>What was accomplished</summary>
5443
5812
  <files-modified>list of modified files</files-modified>
5444
5813
  <files-created>list of new files</files-created>
5445
5814
  </ferix:task-complete>
5446
5815
 
5447
- When ALL tasks are complete, emit <ferix:complete>`;
5816
+ After emitting task-complete, CONTINUE working on the next pending task.
5817
+
5818
+ \u26A0\uFE0F ONLY emit <ferix:complete> when ALL tasks in the plan are done.
5819
+ Do NOT emit <ferix:complete> after completing just one task - continue to the next task instead.`;
5448
5820
  function getPhasePrompt(phase, prompts, defaultPrompt) {
5449
5821
  return prompts?.phases?.[phase] ?? defaultPrompt;
5450
5822
  }
@@ -5585,12 +5957,12 @@ Begin.`);
5585
5957
 
5586
5958
  // src/orchestrator/discovery.ts
5587
5959
  function processTextSignals(signalParser, text, context) {
5588
- return Effect18.gen(function* () {
5960
+ return Effect20.gen(function* () {
5589
5961
  const events = [];
5590
5962
  const parsedSignals = [];
5591
5963
  const signals = yield* signalParser.parse(text).pipe(
5592
- Effect18.tapError(
5593
- (error) => Effect18.logDebug(
5964
+ Effect20.tapError(
5965
+ (error) => Effect20.logDebug(
5594
5966
  "Signal parsing failed, continuing with empty signals",
5595
5967
  {
5596
5968
  error: String(error),
@@ -5598,7 +5970,7 @@ function processTextSignals(signalParser, text, context) {
5598
5970
  }
5599
5971
  )
5600
5972
  ),
5601
- Effect18.orElseSucceed(() => [])
5973
+ Effect20.orElseSucceed(() => [])
5602
5974
  );
5603
5975
  for (const signal of signals) {
5604
5976
  events.push(mapSignalToDomain(signal, context));
@@ -5608,7 +5980,7 @@ function processTextSignals(signalParser, text, context) {
5608
5980
  });
5609
5981
  }
5610
5982
  function processLLMEvent(signalParser, llmEvent, context) {
5611
- return Effect18.gen(function* () {
5983
+ return Effect20.gen(function* () {
5612
5984
  const domainEvent = mapLLMEventToDomain(llmEvent, context);
5613
5985
  const events = [domainEvent];
5614
5986
  const allSignals = [];
@@ -5651,12 +6023,12 @@ function planTasksToGeneratedTasks(plan) {
5651
6023
  status: mapTaskStatus(task.status)
5652
6024
  }));
5653
6025
  }
5654
- function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId) {
6026
+ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
5655
6027
  return Stream7.unwrap(
5656
- Effect18.gen(function* () {
6028
+ Effect20.gen(function* () {
5657
6029
  const startTimeUtc = yield* DateTime8.now;
5658
6030
  const startTime = DateTime8.toEpochMillis(startTimeUtc);
5659
- const persistenceStateRef = yield* Ref9.make({
6031
+ const persistenceStateRef = yield* Ref10.make({
5660
6032
  dirty: false,
5661
6033
  pendingOperation: null
5662
6034
  });
@@ -5670,7 +6042,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5670
6042
  input: {}
5671
6043
  };
5672
6044
  const prompt = buildDiscoveryPrompt(config);
5673
- const llmStream = llm.execute(prompt).pipe(
6045
+ const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
5674
6046
  Stream7.mapError(
5675
6047
  (e) => new OrchestratorError({
5676
6048
  message: `LLM execution failed during discovery: ${String(e)}`,
@@ -5680,7 +6052,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5680
6052
  ),
5681
6053
  Stream7.flatMap(
5682
6054
  (llmEvent) => Stream7.unwrap(
5683
- Effect18.gen(function* () {
6055
+ Effect20.gen(function* () {
5684
6056
  const now = yield* DateTime8.now;
5685
6057
  const context = {
5686
6058
  timestamp: DateTime8.toEpochMillis(now)
@@ -5717,23 +6089,23 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5717
6089
  )
5718
6090
  );
5719
6091
  const completionStream = Stream7.fromEffect(
5720
- Effect18.gen(function* () {
6092
+ Effect20.gen(function* () {
5721
6093
  const persistEvents = yield* flushPlanPersistence(
5722
6094
  planStore,
5723
6095
  currentPlanRef,
5724
6096
  persistenceStateRef
5725
6097
  );
5726
- const plan = yield* Ref9.get(currentPlanRef);
6098
+ const plan = yield* Ref10.get(currentPlanRef);
5727
6099
  const taskCount = plan?.tasks.length ?? 0;
5728
6100
  if (plan && plan.tasks.length > 0) {
5729
6101
  const generatedTasks = planTasksToGeneratedTasks(plan);
5730
6102
  yield* writeTasksMd(sessionId, generatedTasks).pipe(
5731
- Effect18.tapError(
5732
- (error) => Effect18.logDebug("Failed to write tasks.md, continuing", {
6103
+ Effect20.tapError(
6104
+ (error) => Effect20.logDebug("Failed to write tasks.md, continuing", {
5733
6105
  error: String(error)
5734
6106
  })
5735
6107
  ),
5736
- Effect18.orElseSucceed(() => void 0)
6108
+ Effect20.orElseSucceed(() => void 0)
5737
6109
  );
5738
6110
  }
5739
6111
  const endTimeUtc = yield* DateTime8.now;
@@ -5758,15 +6130,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5758
6130
 
5759
6131
  // src/orchestrator/iteration.ts
5760
6132
  init_esm_shims();
5761
- import { DateTime as DateTime9, Effect as Effect19, pipe as pipe2, Ref as Ref10, Stream as Stream8 } from "effect";
6133
+ import { DateTime as DateTime9, Effect as Effect21, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
5762
6134
  function processTextSignals2(signalParser, text, context) {
5763
- return Effect19.gen(function* () {
6135
+ return Effect21.gen(function* () {
5764
6136
  const events = [];
5765
6137
  let completed = false;
5766
6138
  const parsedSignals = [];
5767
6139
  const signals = yield* signalParser.parse(text).pipe(
5768
- Effect19.tapError(
5769
- (error) => Effect19.logDebug(
6140
+ Effect21.tapError(
6141
+ (error) => Effect21.logDebug(
5770
6142
  "Signal parsing failed, continuing with empty signals",
5771
6143
  {
5772
6144
  error: String(error),
@@ -5774,7 +6146,7 @@ function processTextSignals2(signalParser, text, context) {
5774
6146
  }
5775
6147
  )
5776
6148
  ),
5777
- Effect19.orElseSucceed(() => [])
6149
+ Effect21.orElseSucceed(() => [])
5778
6150
  );
5779
6151
  for (const signal of signals) {
5780
6152
  events.push(mapSignalToDomain(signal, context));
@@ -5787,7 +6159,7 @@ function processTextSignals2(signalParser, text, context) {
5787
6159
  });
5788
6160
  }
5789
6161
  function processLLMEvent2(signalParser, llmEvent, context) {
5790
- return Effect19.gen(function* () {
6162
+ return Effect21.gen(function* () {
5791
6163
  const domainEvent = mapLLMEventToDomain(llmEvent, context);
5792
6164
  const events = [domainEvent];
5793
6165
  let completed = false;
@@ -5818,11 +6190,11 @@ function processLLMEvent2(signalParser, llmEvent, context) {
5818
6190
  return { events, completed, signals: allSignals };
5819
6191
  });
5820
6192
  }
5821
- function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId) {
6193
+ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
5822
6194
  return Stream8.unwrap(
5823
- Effect19.gen(function* () {
5824
- const currentPlan = yield* Ref10.get(currentPlanRef);
5825
- const persistenceStateRef = yield* Ref10.make({
6195
+ Effect21.gen(function* () {
6196
+ const currentPlan = yield* Ref11.get(currentPlanRef);
6197
+ const persistenceStateRef = yield* Ref11.make({
5826
6198
  dirty: false,
5827
6199
  pendingOperation: null
5828
6200
  });
@@ -5830,7 +6202,10 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5830
6202
  _tag: "IterationStarted",
5831
6203
  iteration
5832
6204
  };
5833
- const llmStream = llm.execute(buildPrompt(config, iteration, currentPlan)).pipe(
6205
+ const llmStream = llm.execute(
6206
+ buildPrompt(config, iteration, currentPlan),
6207
+ worktreePath ? { cwd: worktreePath } : void 0
6208
+ ).pipe(
5834
6209
  Stream8.mapError(
5835
6210
  (e) => new OrchestratorError({
5836
6211
  message: `LLM execution failed: ${String(e)}`,
@@ -5840,7 +6215,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5840
6215
  ),
5841
6216
  Stream8.flatMap(
5842
6217
  (llmEvent) => Stream8.unwrap(
5843
- Effect19.gen(function* () {
6218
+ Effect21.gen(function* () {
5844
6219
  const now = yield* DateTime9.now;
5845
6220
  const context = {
5846
6221
  timestamp: DateTime9.toEpochMillis(now)
@@ -5862,7 +6237,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5862
6237
  events.push(...planEvents);
5863
6238
  }
5864
6239
  if (result.completed) {
5865
- yield* Ref10.set(loopCompletedRef, true);
6240
+ yield* Ref11.set(loopCompletedRef, true);
5866
6241
  }
5867
6242
  return Stream8.fromIterable(events);
5868
6243
  })
@@ -5881,7 +6256,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5881
6256
  )
5882
6257
  );
5883
6258
  const completionStream = Stream8.fromEffect(
5884
- Effect19.gen(function* () {
6259
+ Effect21.gen(function* () {
5885
6260
  const persistEvents = yield* flushPlanPersistence(
5886
6261
  planStore,
5887
6262
  currentPlanRef,
@@ -5906,13 +6281,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5906
6281
  // src/orchestrator/loop.ts
5907
6282
  function runLoop(config) {
5908
6283
  return Stream9.unwrap(
5909
- Effect20.gen(function* () {
6284
+ Effect22.gen(function* () {
5910
6285
  const llm = yield* LLM;
5911
6286
  const signalParser = yield* SignalParser;
5912
6287
  const sessionStore = yield* SessionStore;
5913
6288
  const planStore = yield* PlanStore;
6289
+ const git = yield* Git;
5914
6290
  const session = yield* sessionStore.create(config.task).pipe(
5915
- Effect20.mapError(
6291
+ Effect22.mapError(
5916
6292
  (e) => new OrchestratorError({
5917
6293
  message: `Failed to create session: ${e.message}`,
5918
6294
  phase: "setup",
@@ -5920,10 +6296,39 @@ function runLoop(config) {
5920
6296
  })
5921
6297
  )
5922
6298
  );
6299
+ const worktreePath = yield* git.createWorktree(session.id).pipe(
6300
+ Effect22.mapError(
6301
+ (e) => new OrchestratorError({
6302
+ message: `Failed to create worktree: ${e.message}`,
6303
+ phase: "setup",
6304
+ cause: e
6305
+ })
6306
+ )
6307
+ );
6308
+ const branchName = git.getBranchName(session.id);
6309
+ yield* sessionStore.update(session.id, {
6310
+ ...session,
6311
+ worktreePath,
6312
+ branchName
6313
+ }).pipe(
6314
+ Effect22.tapError(
6315
+ (error) => Effect22.logDebug("Failed to update session with worktree info", {
6316
+ error: String(error)
6317
+ })
6318
+ ),
6319
+ Effect22.orElseSucceed(() => void 0)
6320
+ );
5923
6321
  const startTimeUtc = yield* DateTime10.now;
5924
6322
  const startTime = DateTime10.toEpochMillis(startTimeUtc);
5925
- const loopCompletedRef = yield* Ref11.make(false);
5926
- const currentPlanRef = yield* Ref11.make(void 0);
6323
+ const worktreeCreated = {
6324
+ _tag: "WorktreeCreated",
6325
+ sessionId: session.id,
6326
+ worktreePath,
6327
+ branchName,
6328
+ timestamp: startTime
6329
+ };
6330
+ const loopCompletedRef = yield* Ref12.make(false);
6331
+ const currentPlanRef = yield* Ref12.make(void 0);
5927
6332
  const maxIterations = config.maxIterations === 0 ? Number.POSITIVE_INFINITY : config.maxIterations;
5928
6333
  const loopStarted = {
5929
6334
  _tag: "LoopStarted",
@@ -5936,12 +6341,13 @@ function runLoop(config) {
5936
6341
  planStore,
5937
6342
  currentPlanRef,
5938
6343
  config,
5939
- session.id
6344
+ session.id,
6345
+ worktreePath
5940
6346
  );
5941
6347
  const iterationsStream = Stream9.unfoldEffect(
5942
6348
  1,
5943
- (iteration) => Effect20.gen(function* () {
5944
- const completed = yield* Ref11.get(loopCompletedRef);
6349
+ (iteration) => Effect22.gen(function* () {
6350
+ const completed = yield* Ref12.get(loopCompletedRef);
5945
6351
  if (completed || iteration > maxIterations) {
5946
6352
  return Option.none();
5947
6353
  }
@@ -5957,27 +6363,31 @@ function runLoop(config) {
5957
6363
  loopCompletedRef,
5958
6364
  config,
5959
6365
  iteration,
5960
- session.id
6366
+ session.id,
6367
+ worktreePath
5961
6368
  )
5962
6369
  )
5963
6370
  );
5964
6371
  const completionStream = createCompletionStream(
5965
6372
  sessionStore,
6373
+ git,
5966
6374
  session,
5967
6375
  config,
5968
6376
  startTime,
5969
- loopCompletedRef
6377
+ loopCompletedRef,
6378
+ worktreePath
5970
6379
  );
5971
6380
  return pipe3(
5972
6381
  Stream9.succeed(loopStarted),
6382
+ Stream9.concat(Stream9.succeed(worktreeCreated)),
5973
6383
  Stream9.concat(discoveryStream),
5974
6384
  Stream9.concat(iterationsStream),
5975
6385
  Stream9.concat(completionStream)
5976
6386
  );
5977
6387
  }).pipe(
5978
6388
  // Also catch setup errors (e.g., session creation failure)
5979
- Effect20.catchAll(
5980
- (error) => Effect20.succeed(
6389
+ Effect22.catchAll(
6390
+ (error) => Effect22.succeed(
5981
6391
  Stream9.succeed({
5982
6392
  _tag: "LoopFailed",
5983
6393
  error: {
@@ -5990,12 +6400,38 @@ function runLoop(config) {
5990
6400
  )
5991
6401
  );
5992
6402
  }
5993
- function createCompletionStream(sessionStore, session, config, startTime, loopCompletedRef) {
5994
- return Stream9.fromEffect(
5995
- Effect20.gen(function* () {
6403
+ function createCompletionStream(sessionStore, git, session, config, startTime, loopCompletedRef, worktreePath) {
6404
+ return Stream9.unwrap(
6405
+ Effect22.gen(function* () {
5996
6406
  const endTimeUtc = yield* DateTime10.now;
5997
6407
  const durationMs = DateTime10.toEpochMillis(endTimeUtc) - startTime;
5998
- const completed = yield* Ref11.get(loopCompletedRef);
6408
+ const completed = yield* Ref12.get(loopCompletedRef);
6409
+ yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
6410
+ Effect22.tapError(
6411
+ (error) => Effect22.logDebug("Final commit failed, continuing cleanup", {
6412
+ sessionId: session.id,
6413
+ error: String(error)
6414
+ })
6415
+ ),
6416
+ Effect22.orElseSucceed(() => void 0)
6417
+ );
6418
+ yield* git.removeWorktree(session.id).pipe(
6419
+ Effect22.tapError(
6420
+ (error) => Effect22.logDebug("Worktree cleanup failed, continuing", {
6421
+ sessionId: session.id,
6422
+ worktreePath,
6423
+ error: String(error)
6424
+ })
6425
+ ),
6426
+ Effect22.orElseSucceed(() => void 0)
6427
+ );
6428
+ const cleanupTimeUtc = yield* DateTime10.now;
6429
+ const cleanupTime = DateTime10.toEpochMillis(cleanupTimeUtc);
6430
+ const worktreeRemoved = {
6431
+ _tag: "WorktreeRemoved",
6432
+ sessionId: session.id,
6433
+ timestamp: cleanupTime
6434
+ };
5999
6435
  const summary = {
6000
6436
  iterations: config.maxIterations,
6001
6437
  success: completed,
@@ -6007,16 +6443,16 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
6007
6443
  ...session,
6008
6444
  status: completed ? "completed" : "paused"
6009
6445
  }).pipe(
6010
- Effect20.tapError(
6011
- (error) => Effect20.logDebug("Session update failed, continuing", {
6446
+ Effect22.tapError(
6447
+ (error) => Effect22.logDebug("Session update failed, continuing", {
6012
6448
  sessionId: session.id,
6013
6449
  error: String(error)
6014
6450
  })
6015
6451
  ),
6016
- Effect20.orElseSucceed(() => void 0)
6452
+ Effect22.orElseSucceed(() => void 0)
6017
6453
  );
6018
- const event = { _tag: "LoopCompleted", summary };
6019
- return event;
6454
+ const loopCompleted = { _tag: "LoopCompleted", summary };
6455
+ return Stream9.fromIterable([worktreeRemoved, loopCompleted]);
6020
6456
  })
6021
6457
  );
6022
6458
  }
@@ -6025,7 +6461,7 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
6025
6461
  function run(options) {
6026
6462
  const { config, consumer: consumerType = "headless", onEvent } = options;
6027
6463
  const events = runLoop(config);
6028
- const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect21.sync(() => onEvent(event)))) : events;
6464
+ const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
6029
6465
  const eventsWithLayers = eventsWithCallback.pipe(
6030
6466
  Stream10.provideLayer(ProductionLayers)
6031
6467
  );
@@ -6038,7 +6474,7 @@ function run(options) {
6038
6474
  function runTest(options, mockEvents) {
6039
6475
  const { config, onEvent } = options;
6040
6476
  const events = runLoop(config);
6041
- const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect21.sync(() => onEvent(event)))) : events;
6477
+ const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
6042
6478
  const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
6043
6479
  const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
6044
6480
  return eventsWithLayers.pipe(Stream10.runDrain);
@@ -6046,11 +6482,11 @@ function runTest(options, mockEvents) {
6046
6482
  function collectEvents(config, mockEvents) {
6047
6483
  const events = runLoop(config);
6048
6484
  const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
6049
- return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect21.map((chunk) => Array.from(chunk)));
6485
+ return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect23.map((chunk) => Array.from(chunk)));
6050
6486
  }
6051
6487
  function main(config) {
6052
6488
  const consumerType = process.stdout.isTTY ? "tui" : "headless";
6053
- return run({ config, consumer: consumerType }).pipe(Effect21.runPromise);
6489
+ return run({ config, consumer: consumerType }).pipe(Effect23.runPromise);
6054
6490
  }
6055
6491
 
6056
6492
  // src/services/index.ts
@@ -6103,6 +6539,7 @@ export {
6103
6539
  ExecutionModeSchema,
6104
6540
  FerixParser,
6105
6541
  FileLoggerConfigSchema,
6542
+ FileSystemGit,
6106
6543
  FileSystemGuardrails,
6107
6544
  FileSystemPlan,
6108
6545
  FileSystemProgress,
@@ -6110,6 +6547,8 @@ export {
6110
6547
  GeneratedTaskListSchema,
6111
6548
  GeneratedTaskSchema,
6112
6549
  GeneratedTaskStatusSchema,
6550
+ Git,
6551
+ GitError,
6113
6552
  GuardrailAddedEventSchema,
6114
6553
  GuardrailSchema,
6115
6554
  GuardrailSeveritySchema,
@@ -6139,6 +6578,7 @@ export {
6139
6578
  LoopStartedEventSchema,
6140
6579
  LoopStatusSchema,
6141
6580
  LoopSummarySchema,
6581
+ MemoryGit,
6142
6582
  MemoryGuardrails,
6143
6583
  MemoryPlan,
6144
6584
  MemoryProgress,
@@ -6211,6 +6651,8 @@ export {
6211
6651
  ToolStartEventSchema,
6212
6652
  ToolUseEventSchema,
6213
6653
  ViewModeSchema,
6654
+ WorktreeCreatedEventSchema,
6655
+ WorktreeRemovedEventSchema,
6214
6656
  buildPrompt,
6215
6657
  collectEvents,
6216
6658
  createHeadlessConsumer,