ferix-code 0.0.2-beta.0 → 0.0.2-beta.10

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 +688 -21
  2. package/dist/index.js +1943 -813
  3. package/package.json +11 -12
package/dist/index.js CHANGED
@@ -71,11 +71,12 @@ import { Command } from "commander";
71
71
  // package.json
72
72
  var package_default = {
73
73
  name: "ferix-code",
74
- version: "0.0.2-beta.0",
74
+ version: "0.0.2-beta.10",
75
75
  description: "Composable RALPH loops for AI coding agents - v2 with Effect",
76
76
  type: "module",
77
77
  bin: {
78
- "ferix-code": "./dist/index.js"
78
+ ferix: "dist/index.js",
79
+ "ferix-code": "dist/index.js"
79
80
  },
80
81
  files: [
81
82
  "dist"
@@ -85,22 +86,20 @@ var package_default = {
85
86
  dev: "tsup --watch",
86
87
  "check-types": "tsc --noEmit",
87
88
  test: "bun test",
88
- "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"
89
91
  },
90
92
  dependencies: {
91
- "@effect/platform": "^0.72.0",
92
- "@effect/platform-node": "^0.66.0",
93
- commander: "catalog:",
94
- effect: "^3.12.0",
93
+ commander: "^14.0.0",
94
+ effect: "^3.19.15",
95
95
  "human-id": "^4.1.3",
96
- picocolors: "catalog:"
96
+ picocolors: "^1.1.1"
97
97
  },
98
98
  devDependencies: {
99
- "@ferix/typescript-config": "*",
100
99
  "@types/bun": "latest",
101
- "@types/node": "catalog:",
102
- tsup: "catalog:",
103
- typescript: "catalog:"
100
+ "@types/node": "^22.15.3",
101
+ tsup: "^8.4.0",
102
+ typescript: "5.9.2"
104
103
  },
105
104
  publishConfig: {
106
105
  access: "public"
@@ -119,7 +118,7 @@ var package_default = {
119
118
 
120
119
  // src/program.ts
121
120
  init_esm_shims();
122
- import { Effect as Effect17, Stream as Stream10 } from "effect";
121
+ import { Effect as Effect23, Stream as Stream10 } from "effect";
123
122
 
124
123
  // src/consumers/index.ts
125
124
  init_esm_shims();
@@ -127,7 +126,7 @@ init_esm_shims();
127
126
  // src/consumers/headless/consumer.ts
128
127
  init_esm_shims();
129
128
  import { Cause, Effect, Stream } from "effect";
130
- import pc9 from "picocolors";
129
+ import pc10 from "picocolors";
131
130
 
132
131
  // src/consumers/headless/formatters/index.ts
133
132
  init_esm_shims();
@@ -308,6 +307,17 @@ var taskCompletedFormatter = {
308
307
  headlessFormatterRegistry.register(tasksDefinedFormatter);
309
308
  headlessFormatterRegistry.register(taskCompletedFormatter);
310
309
 
310
+ // src/consumers/headless/formatters/worktree.ts
311
+ init_esm_shims();
312
+ import pc9 from "picocolors";
313
+ var worktreeCreatedFormatter = {
314
+ tag: "WorktreeCreated",
315
+ format: (event) => pc9.cyan(
316
+ `[WORKTREE] Branch: ${event.branchName} | Path: ${event.worktreePath}`
317
+ )
318
+ };
319
+ headlessFormatterRegistry.register(worktreeCreatedFormatter);
320
+
311
321
  // src/consumers/headless/formatters/index.ts
312
322
  function formatEvent(event) {
313
323
  return headlessFormatterRegistry.format(event);
@@ -316,37 +326,37 @@ function formatEvent(event) {
316
326
  // src/consumers/headless/consumer.ts
317
327
  function formatErrorToLines(err, lines) {
318
328
  if (err instanceof Error) {
319
- lines.push(pc9.red(` - ${err.name}: ${err.message}`));
329
+ lines.push(pc10.red(` - ${err.name}: ${err.message}`));
320
330
  if (err.stack) {
321
331
  lines.push("");
322
- lines.push(pc9.yellow("Stack trace:"));
332
+ lines.push(pc10.yellow("Stack trace:"));
323
333
  for (const stackLine of err.stack.split("\n").slice(1)) {
324
- lines.push(pc9.dim(` ${stackLine.trim()}`));
334
+ lines.push(pc10.dim(` ${stackLine.trim()}`));
325
335
  }
326
336
  }
327
337
  } else {
328
- lines.push(pc9.red(` - ${String(err)}`));
338
+ lines.push(pc10.red(` - ${String(err)}`));
329
339
  }
330
340
  }
331
341
  function formatCauseVerbose(cause) {
332
342
  const lines = [];
333
- const separator2 = pc9.red(
343
+ const separator2 = pc10.red(
334
344
  "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
335
345
  );
336
346
  const causeType = Cause.isFailure(cause) ? "Failure" : "Other";
337
347
  lines.push("");
338
348
  lines.push(separator2);
339
- lines.push(pc9.red(" ERROR"));
349
+ lines.push(pc10.red(" ERROR"));
340
350
  lines.push(separator2);
341
351
  lines.push("");
342
- lines.push(`${pc9.yellow("Type:")} ${causeType}`);
352
+ lines.push(`${pc10.yellow("Type:")} ${causeType}`);
343
353
  lines.push("");
344
- lines.push(pc9.yellow("Details:"));
354
+ lines.push(pc10.yellow("Details:"));
345
355
  lines.push(Cause.pretty(cause));
346
356
  const failures = Cause.failures(cause);
347
357
  if (failures.length > 0) {
348
358
  lines.push("");
349
- lines.push(pc9.yellow("Failures:"));
359
+ lines.push(pc10.yellow("Failures:"));
350
360
  for (const failure of failures) {
351
361
  formatErrorToLines(failure, lines);
352
362
  }
@@ -354,7 +364,7 @@ function formatCauseVerbose(cause) {
354
364
  const defects = Cause.defects(cause);
355
365
  if (defects.length > 0) {
356
366
  lines.push("");
357
- lines.push(pc9.yellow("Defects (unexpected errors):"));
367
+ lines.push(pc10.yellow("Defects (unexpected errors):"));
358
368
  for (const defect of defects) {
359
369
  formatErrorToLines(defect, lines);
360
370
  }
@@ -535,40 +545,40 @@ init_esm_shims();
535
545
 
536
546
  // src/consumers/tui/tags/primitives.ts
537
547
  init_esm_shims();
538
- import pc11 from "picocolors";
548
+ import pc12 from "picocolors";
539
549
 
540
550
  // src/consumers/tui/render/primitives.ts
541
551
  init_esm_shims();
542
- import pc10 from "picocolors";
552
+ import pc11 from "picocolors";
543
553
  var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
544
554
  var colors = {
545
- brand: pc10.magenta,
546
- success: pc10.green,
547
- warning: pc10.yellow,
548
- error: pc10.red,
549
- info: pc10.cyan,
550
- muted: pc10.dim,
551
- highlight: pc10.bold,
552
- brightWhite: (s) => pc10.bold(pc10.white(s)),
553
- brightMagenta: (s) => pc10.bold(pc10.magenta(s)),
554
- brightCyan: (s) => pc10.bold(pc10.cyan(s)),
555
- brightGreen: (s) => pc10.bold(pc10.green(s)),
556
- brightYellow: (s) => pc10.bold(pc10.yellow(s)),
557
- brightRed: (s) => pc10.bold(pc10.red(s))
555
+ brand: pc11.magenta,
556
+ success: pc11.green,
557
+ warning: pc11.yellow,
558
+ error: pc11.red,
559
+ info: pc11.cyan,
560
+ muted: pc11.dim,
561
+ highlight: pc11.bold,
562
+ brightWhite: (s) => pc11.bold(pc11.white(s)),
563
+ brightMagenta: (s) => pc11.bold(pc11.magenta(s)),
564
+ brightCyan: (s) => pc11.bold(pc11.cyan(s)),
565
+ brightGreen: (s) => pc11.bold(pc11.green(s)),
566
+ brightYellow: (s) => pc11.bold(pc11.yellow(s)),
567
+ brightRed: (s) => pc11.bold(pc11.red(s))
558
568
  };
559
569
  var toolColors = {
560
- Read: pc10.cyan,
561
- Edit: pc10.yellow,
562
- Write: pc10.green,
563
- Bash: pc10.magenta,
564
- Glob: pc10.blue,
565
- Grep: pc10.blue,
570
+ Read: pc11.cyan,
571
+ Edit: pc11.yellow,
572
+ Write: pc11.green,
573
+ Bash: pc11.magenta,
574
+ Glob: pc11.blue,
575
+ Grep: pc11.blue,
566
576
  Task: colors.brightWhite,
567
- WebFetch: pc10.cyan,
568
- WebSearch: pc10.blue
577
+ WebFetch: pc11.cyan,
578
+ WebSearch: pc11.blue
569
579
  };
570
580
  function getToolColor(tool) {
571
- return toolColors[tool] || pc10.white;
581
+ return toolColors[tool] || pc11.white;
572
582
  }
573
583
  var symbols = {
574
584
  diamond: "\u25C6",
@@ -595,24 +605,24 @@ var box = {
595
605
  };
596
606
  function topBorder(width) {
597
607
  const repeatCount = Math.max(0, width - 2);
598
- return pc10.cyan(
608
+ return pc11.cyan(
599
609
  `${box.topLeft}${box.horizontal.repeat(repeatCount)}${box.topRight}`
600
610
  );
601
611
  }
602
612
  function separator(width) {
603
613
  const repeatCount = Math.max(0, width - 2);
604
- return pc10.cyan(
614
+ return pc11.cyan(
605
615
  `${box.teeRight}${box.horizontal.repeat(repeatCount)}${box.teeLeft}`
606
616
  );
607
617
  }
608
618
  function borderedLine(content, width) {
609
619
  const stripped = stripAnsi(content);
610
620
  const padding = Math.max(0, width - stripped.length - 4);
611
- return `${pc10.cyan(box.vertical)} ${content}${" ".repeat(padding)} ${pc10.cyan(box.vertical)}`;
621
+ return `${pc11.cyan(box.vertical)} ${content}${" ".repeat(padding)} ${pc11.cyan(box.vertical)}`;
612
622
  }
613
623
  function emptyBorderedLine(width) {
614
624
  const repeatCount = Math.max(0, width - 2);
615
- return `${pc10.cyan(box.vertical)}${" ".repeat(repeatCount)}${pc10.cyan(box.vertical)}`;
625
+ return `${pc11.cyan(box.vertical)}${" ".repeat(repeatCount)}${pc11.cyan(box.vertical)}`;
616
626
  }
617
627
  function stripAnsi(str) {
618
628
  return str.replace(ANSI_PATTERN, "");
@@ -709,31 +719,31 @@ function taskListHeader(width) {
709
719
  }
710
720
  const label = ` ${symbols.diamond} TASKS ${symbols.diamond} `;
711
721
  const sideLen = Math.max(1, Math.floor((innerWidth - label.length) / 2));
712
- return `${pc11.cyan("\u250C")}${pc11.cyan(box.singleHorizontal.repeat(sideLen))}${colors.brand(label)}${pc11.cyan(box.singleHorizontal.repeat(sideLen))}`;
722
+ return `${pc12.cyan("\u250C")}${pc12.cyan(box.singleHorizontal.repeat(sideLen))}${colors.brand(label)}${pc12.cyan(box.singleHorizontal.repeat(sideLen))}`;
713
723
  }
714
724
  function taskListFooter() {
715
- return pc11.cyan("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
725
+ return pc12.cyan("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
716
726
  }
717
727
  function taskLine(id, description) {
718
- return `${pc11.cyan("\u2502")} ${colors.brightMagenta(`[${id}]`)} ${description}`;
728
+ return `${pc12.cyan("\u2502")} ${colors.brightMagenta(`[${id}]`)} ${description}`;
719
729
  }
720
730
  function taskDone(id) {
721
731
  return `${colors.success(symbols.checkmark)} ${colors.muted(`Task ${id} complete`)}`;
722
732
  }
723
733
  function phasesHeader(taskId) {
724
- return `${pc11.cyan("\u2502")} ${colors.muted(`Phases for task ${taskId}:`)}`;
734
+ return `${pc12.cyan("\u2502")} ${colors.muted(`Phases for task ${taskId}:`)}`;
725
735
  }
726
736
  function phaseLine(id, description) {
727
- return `${pc11.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
737
+ return `${pc12.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
728
738
  }
729
739
  function phaseStart(id) {
730
- return `${pc11.cyan("\u2502")} ${colors.warning(symbols.bulletFilled)} ${colors.muted(`Phase ${id} started`)}`;
740
+ return `${pc12.cyan("\u2502")} ${colors.warning(symbols.bulletFilled)} ${colors.muted(`Phase ${id} started`)}`;
731
741
  }
732
742
  function phaseDone(id) {
733
- return `${pc11.cyan("\u2502")} ${colors.success(symbols.checkmark)} ${colors.muted(`Phase ${id} complete`)}`;
743
+ return `${pc12.cyan("\u2502")} ${colors.success(symbols.checkmark)} ${colors.muted(`Phase ${id} complete`)}`;
734
744
  }
735
745
  function phaseFailed(id, reason) {
736
- return `${pc11.cyan("\u2502")} ${colors.error(symbols.cross)} ${colors.muted(`Phase ${id} failed:`)} ${colors.error(reason)}`;
746
+ return `${pc12.cyan("\u2502")} ${colors.error(symbols.cross)} ${colors.muted(`Phase ${id} failed:`)} ${colors.error(reason)}`;
737
747
  }
738
748
  function criterionPassed(id) {
739
749
  return `${colors.success(symbols.checkmark)} ${colors.muted(`Criterion ${id} passed`)}`;
@@ -769,10 +779,10 @@ function reviewFailedBanner(width) {
769
779
  );
770
780
  }
771
781
  function criteriaHeader(taskId) {
772
- return `${pc11.cyan("\u2502")} ${colors.muted(`Success criteria for task ${taskId}:`)}`;
782
+ return `${pc12.cyan("\u2502")} ${colors.muted(`Success criteria for task ${taskId}:`)}`;
773
783
  }
774
784
  function criterionLine(id, description) {
775
- return `${pc11.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
785
+ return `${pc12.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
776
786
  }
777
787
 
778
788
  // src/consumers/tui/tags/handlers/completion.ts
@@ -1045,11 +1055,11 @@ init_esm_shims();
1045
1055
 
1046
1056
  // src/consumers/tui/tools/registry.ts
1047
1057
  init_esm_shims();
1048
- import pc12 from "picocolors";
1049
- var brightWhite = (s) => pc12.bold(pc12.white(s));
1058
+ import pc13 from "picocolors";
1059
+ var brightWhite = (s) => pc13.bold(pc13.white(s));
1050
1060
  function createToolDisplayRegistry() {
1051
1061
  const configs = /* @__PURE__ */ new Map();
1052
- const defaultColor = pc12.white;
1062
+ const defaultColor = pc13.white;
1053
1063
  return {
1054
1064
  register(config) {
1055
1065
  configs.set(config.tool, config);
@@ -1086,32 +1096,32 @@ var toolDisplayRegistry = createToolDisplayRegistry();
1086
1096
  toolDisplayRegistry.register({
1087
1097
  tool: "Read",
1088
1098
  inputKey: "file_path",
1089
- color: pc12.cyan
1099
+ color: pc13.cyan
1090
1100
  });
1091
1101
  toolDisplayRegistry.register({
1092
1102
  tool: "Edit",
1093
1103
  inputKey: "file_path",
1094
- color: pc12.yellow
1104
+ color: pc13.yellow
1095
1105
  });
1096
1106
  toolDisplayRegistry.register({
1097
1107
  tool: "Write",
1098
1108
  inputKey: "file_path",
1099
- color: pc12.green
1109
+ color: pc13.green
1100
1110
  });
1101
1111
  toolDisplayRegistry.register({
1102
1112
  tool: "Bash",
1103
1113
  inputKey: "command",
1104
- color: pc12.magenta
1114
+ color: pc13.magenta
1105
1115
  });
1106
1116
  toolDisplayRegistry.register({
1107
1117
  tool: "Glob",
1108
1118
  inputKey: "pattern",
1109
- color: pc12.blue
1119
+ color: pc13.blue
1110
1120
  });
1111
1121
  toolDisplayRegistry.register({
1112
1122
  tool: "Grep",
1113
1123
  inputKey: "pattern",
1114
- color: pc12.blue
1124
+ color: pc13.blue
1115
1125
  });
1116
1126
  toolDisplayRegistry.register({
1117
1127
  tool: "Task",
@@ -1121,12 +1131,12 @@ toolDisplayRegistry.register({
1121
1131
  toolDisplayRegistry.register({
1122
1132
  tool: "WebFetch",
1123
1133
  inputKey: "url",
1124
- color: pc12.cyan
1134
+ color: pc13.cyan
1125
1135
  });
1126
1136
  toolDisplayRegistry.register({
1127
1137
  tool: "WebSearch",
1128
1138
  inputKey: "query",
1129
- color: pc12.blue
1139
+ color: pc13.blue
1130
1140
  });
1131
1141
 
1132
1142
  // src/consumers/tui/tools/index.ts
@@ -1306,6 +1316,34 @@ stateReducerRegistry.register(loopStartedReducer);
1306
1316
  stateReducerRegistry.register(loopCompletedReducer);
1307
1317
  stateReducerRegistry.register(loopFailedReducer);
1308
1318
 
1319
+ // src/consumers/tui/reducers/progress.ts
1320
+ init_esm_shims();
1321
+ var learningRecordedReducer = {
1322
+ tag: "LearningRecorded",
1323
+ reduce: (state, event) => {
1324
+ const category = event.category ? `[${event.category}] ` : "";
1325
+ const line = `Learning: ${category}${event.content}`;
1326
+ return appendOutput(state, `${line}
1327
+ `);
1328
+ }
1329
+ };
1330
+ var guardrailAddedReducer = {
1331
+ tag: "GuardrailAdded",
1332
+ reduce: (state, event) => {
1333
+ const severityIcon = event.severity === "critical" ? "[critical]" : "[warn]";
1334
+ const line = `${severityIcon} Guardrail: ${event.pattern}`;
1335
+ return appendOutput(state, `${line}
1336
+ `);
1337
+ }
1338
+ };
1339
+ var progressUpdatedReducer = {
1340
+ tag: "ProgressUpdated",
1341
+ reduce: (state) => state
1342
+ };
1343
+ stateReducerRegistry.register(learningRecordedReducer);
1344
+ stateReducerRegistry.register(guardrailAddedReducer);
1345
+ stateReducerRegistry.register(progressUpdatedReducer);
1346
+
1309
1347
  // src/consumers/tui/reducers/tasks.ts
1310
1348
  init_esm_shims();
1311
1349
  var tasksDefinedReducer = {
@@ -1386,6 +1424,21 @@ stateReducerRegistry.register(criterionPassedReducer);
1386
1424
  stateReducerRegistry.register(criterionFailedReducer);
1387
1425
  stateReducerRegistry.register(taskCompletedReducer);
1388
1426
 
1427
+ // src/consumers/tui/reducers/worktree.ts
1428
+ init_esm_shims();
1429
+ var worktreeCreatedReducer = {
1430
+ tag: "WorktreeCreated",
1431
+ reduce: (state, event) => appendOutput(
1432
+ state,
1433
+ `
1434
+ Worktree created
1435
+ Branch: ${event.branchName}
1436
+ Path: ${event.worktreePath}
1437
+ `
1438
+ )
1439
+ };
1440
+ stateReducerRegistry.register(worktreeCreatedReducer);
1441
+
1389
1442
  // src/consumers/tui/reducers/index.ts
1390
1443
  function reduce(state, event) {
1391
1444
  return stateReducerRegistry.reduce(state, event);
@@ -1464,7 +1517,7 @@ init_esm_shims();
1464
1517
 
1465
1518
  // src/consumers/tui/render/footer.ts
1466
1519
  init_esm_shims();
1467
- import pc13 from "picocolors";
1520
+ import pc14 from "picocolors";
1468
1521
  function hint(key, description) {
1469
1522
  return `${colors.brand(symbols.diamond)} ${colors.brightWhite(key)} ${colors.muted(description)}`;
1470
1523
  }
@@ -1513,7 +1566,7 @@ function renderFooter(state, width, outputHeight) {
1513
1566
  const content = hints.join(" ");
1514
1567
  const stripped = stripAnsi(content);
1515
1568
  const padding = Math.max(0, width - stripped.length - 4);
1516
- return `${pc13.cyan(box.bottomLeft)}${pc13.cyan(box.horizontal)} ${content}${pc13.cyan(box.horizontal.repeat(Math.max(1, padding)))}${pc13.cyan(box.bottomRight)}`;
1569
+ return `${pc14.cyan(box.bottomLeft)}${pc14.cyan(box.horizontal)} ${content}${pc14.cyan(box.horizontal.repeat(Math.max(1, padding)))}${pc14.cyan(box.bottomRight)}`;
1517
1570
  }
1518
1571
 
1519
1572
  // src/consumers/tui/render/status-bar.ts
@@ -1592,7 +1645,7 @@ init_esm_shims();
1592
1645
 
1593
1646
  // src/consumers/tui/render/task-detail.ts
1594
1647
  init_esm_shims();
1595
- import pc14 from "picocolors";
1648
+ import pc15 from "picocolors";
1596
1649
  function renderPhase(phase, isLast, width) {
1597
1650
  const lines = [];
1598
1651
  const prefix = isLast ? symbols.treeLast : symbols.treeMiddle;
@@ -1657,7 +1710,7 @@ function renderEmptyState(height, width) {
1657
1710
  return lines;
1658
1711
  }
1659
1712
  function renderTaskHeader(task, width, innerWidth) {
1660
- const sepLine = pc14.cyan(box.horizontal.repeat(innerWidth));
1713
+ const sepLine = pc15.cyan(box.horizontal.repeat(innerWidth));
1661
1714
  return [
1662
1715
  emptyBorderedLine(width),
1663
1716
  borderedLine(
@@ -1731,7 +1784,7 @@ function renderGitSection(state, width, innerWidth) {
1731
1784
  return [];
1732
1785
  }
1733
1786
  const lines = [];
1734
- const sepLine = pc14.cyan(box.horizontal.repeat(innerWidth));
1787
+ const sepLine = pc15.cyan(box.horizontal.repeat(innerWidth));
1735
1788
  const pushStatus = state.gitPushed ? colors.success(symbols.checkmark) : colors.muted("Not pushed");
1736
1789
  lines.push(borderedLine(sepLine, width));
1737
1790
  lines.push(
@@ -1810,7 +1863,7 @@ init_esm_shims();
1810
1863
 
1811
1864
  // src/consumers/tui/render/tasks-list.ts
1812
1865
  init_esm_shims();
1813
- import pc15 from "picocolors";
1866
+ import pc16 from "picocolors";
1814
1867
  function renderTaskLine(task, isSelected, width) {
1815
1868
  const innerWidth = width - 4;
1816
1869
  const parts = [];
@@ -1874,7 +1927,7 @@ function renderTasksListView(state, height, width) {
1874
1927
  lines.push(emptyBorderedLine(width));
1875
1928
  const headerText = `${colors.brand(symbols.diamond)} ${colors.brightWhite("TASKS")} ${colors.brand(symbols.diamond)}`;
1876
1929
  lines.push(borderedLine(headerText, width));
1877
- const sepLine = pc15.cyan(box.horizontal.repeat(innerWidth));
1930
+ const sepLine = pc16.cyan(box.horizontal.repeat(innerWidth));
1878
1931
  lines.push(borderedLine(sepLine, width));
1879
1932
  lines.push(emptyBorderedLine(width));
1880
1933
  const gitLines = renderGitInfo(state, width);
@@ -2190,6 +2243,7 @@ var ANSIOutput = class {
2190
2243
  init_esm_shims();
2191
2244
 
2192
2245
  // src/consumers/tui/consumer.ts
2246
+ var CLOCK_REFRESH_INTERVAL_MS = 1e3;
2193
2247
  function formatErrorToLines2(err, lines) {
2194
2248
  if (err instanceof Error) {
2195
2249
  lines.push(` - ${err.name}: ${err.message}`);
@@ -2249,6 +2303,17 @@ function createTUIConsumer() {
2249
2303
  const inputFiber = yield* Effect3.forkDaemon(
2250
2304
  runInputLoop(stateRef, output)
2251
2305
  );
2306
+ const clockFiber = yield* Effect3.forkDaemon(
2307
+ Effect3.forever(
2308
+ Effect3.gen(function* () {
2309
+ yield* Effect3.sleep(CLOCK_REFRESH_INTERVAL_MS);
2310
+ const state = yield* Ref2.get(stateRef);
2311
+ if (state.status === "running") {
2312
+ yield* Effect3.sync(() => safeRender(state, output));
2313
+ }
2314
+ })
2315
+ )
2316
+ );
2252
2317
  const processEvents = events.pipe(
2253
2318
  Stream3.runForEach(
2254
2319
  (event) => Effect3.gen(function* () {
@@ -2300,6 +2365,7 @@ ${errorText}
2300
2365
  })
2301
2366
  );
2302
2367
  }
2368
+ yield* Fiber.interrupt(clockFiber);
2303
2369
  unsubscribeResize();
2304
2370
  cleanup();
2305
2371
  })
@@ -2308,23 +2374,15 @@ ${errorText}
2308
2374
 
2309
2375
  // src/layers/index.ts
2310
2376
  init_esm_shims();
2311
- import { Layer as Layer8 } from "effect";
2312
-
2313
- // src/layers/llm/claude-cli.ts
2314
- init_esm_shims();
2315
- import { spawn } from "child_process";
2316
- import { Effect as Effect5, Layer, Stream as Stream5 } from "effect";
2317
-
2318
- // src/services/llm.ts
2319
- init_esm_shims();
2320
- import { Context } from "effect";
2321
- var LLM = class extends Context.Tag("@ferix/LLM")() {
2322
- };
2377
+ import { Layer as Layer14 } from "effect";
2323
2378
 
2324
- // src/layers/llm/stream.ts
2379
+ // src/layers/git/file-system.ts
2325
2380
  init_esm_shims();
2326
- import { createInterface } from "readline";
2327
- import { Effect as Effect4, Stream as Stream4 } from "effect";
2381
+ import { exec } from "child_process";
2382
+ import { access, mkdir, rm } from "fs/promises";
2383
+ import { join } from "path";
2384
+ import { promisify } from "util";
2385
+ import { Effect as Effect4, Layer } from "effect";
2328
2386
 
2329
2387
  // src/domain/errors.ts
2330
2388
  init_esm_shims();
@@ -2337,288 +2395,346 @@ var PlanStoreError = class extends Data.TaggedError("PlanStoreError") {
2337
2395
  };
2338
2396
  var SessionStoreError = class extends Data.TaggedError("SessionStoreError") {
2339
2397
  };
2398
+ var ProgressStoreError = class extends Data.TaggedError("ProgressStoreError") {
2399
+ };
2400
+ var GuardrailsStoreError = class extends Data.TaggedError(
2401
+ "GuardrailsStoreError"
2402
+ ) {
2403
+ };
2340
2404
  var OrchestratorError = class extends Data.TaggedError("OrchestratorError") {
2341
2405
  };
2406
+ var GitError = class extends Data.TaggedError("GitError") {
2407
+ };
2342
2408
 
2343
- // src/layers/llm/parsers.ts
2409
+ // src/services/git.ts
2344
2410
  init_esm_shims();
2345
- function parseJsonLine(line) {
2346
- if (!line.startsWith("{")) {
2347
- return null;
2348
- }
2349
- try {
2350
- return JSON.parse(line);
2351
- } catch {
2352
- return null;
2353
- }
2354
- }
2355
- function isTextContent(json) {
2356
- return typeof json === "object" && json !== null && "type" in json && typeof json.type === "string";
2357
- }
2358
- function isToolUse(json) {
2359
- return typeof json === "object" && json !== null && "type" in json && "content_block" in json;
2360
- }
2361
- function extractText(json) {
2362
- if (!isTextContent(json)) {
2363
- return null;
2364
- }
2365
- if (json.type === "content_block_delta") {
2366
- const delta = json;
2367
- if (delta.delta?.type === "text_delta" && delta.delta.text) {
2368
- return delta.delta.text;
2369
- }
2370
- }
2371
- if (json.type === "assistant" && json.message?.content) {
2372
- for (const block of json.message.content) {
2373
- if (block.type === "text" && block.text) {
2374
- return block.text;
2375
- }
2411
+ import { Context } from "effect";
2412
+ var Git = class extends Context.Tag("@ferix/Git")() {
2413
+ };
2414
+
2415
+ // src/layers/git/file-system.ts
2416
+ var execAsync = promisify(exec);
2417
+ var WORKTREES_DIR = ".ferix/worktrees";
2418
+ var BRANCH_PREFIX = "ferix";
2419
+ function getWorktreeDir(sessionId) {
2420
+ return join(process.cwd(), WORKTREES_DIR, sessionId);
2421
+ }
2422
+ function getBranchName(sessionId) {
2423
+ return `${BRANCH_PREFIX}/${sessionId}`;
2424
+ }
2425
+ function gitExec(command, cwd) {
2426
+ return Effect4.tryPromise({
2427
+ try: async () => {
2428
+ const { stdout } = await execAsync(command, { cwd });
2429
+ return stdout.trim();
2430
+ },
2431
+ catch: (error) => {
2432
+ const execError = error;
2433
+ return new GitError({
2434
+ message: execError.stderr || execError.message || String(error),
2435
+ operation: "status",
2436
+ cause: error
2437
+ });
2376
2438
  }
2377
- }
2378
- return null;
2439
+ });
2379
2440
  }
2380
- function isToolInputDelta(json) {
2381
- return typeof json === "object" && json !== null && "type" in json && json.type === "content_block_delta" && "delta" in json;
2441
+ function directoryExists(dirPath) {
2442
+ return Effect4.tryPromise({
2443
+ try: async () => {
2444
+ await access(dirPath);
2445
+ return true;
2446
+ },
2447
+ catch: () => new Error("Directory does not exist")
2448
+ }).pipe(Effect4.orElseSucceed(() => false));
2382
2449
  }
2383
- function extractToolInfo(json) {
2384
- if (typeof json === "object" && json !== null && "type" in json && json.type === "content_block_stop") {
2385
- return {
2386
- type: "end",
2387
- name: "unknown"
2388
- };
2389
- }
2390
- if (!isToolUse(json)) {
2391
- if (isToolInputDelta(json) && json.delta?.type === "input_json_delta" && json.delta.partial_json) {
2392
- return {
2393
- type: "input_delta",
2394
- name: "",
2395
- partialJson: json.delta.partial_json
2396
- };
2450
+ var make = {
2451
+ createWorktree: (sessionId, baseBranch) => Effect4.gen(function* () {
2452
+ const worktreeDir = getWorktreeDir(sessionId);
2453
+ const branchName = getBranchName(sessionId);
2454
+ const worktreesBase = join(process.cwd(), WORKTREES_DIR);
2455
+ yield* Effect4.tryPromise({
2456
+ try: () => mkdir(worktreesBase, { recursive: true }),
2457
+ catch: (error) => new GitError({
2458
+ message: `Failed to create worktrees directory: ${String(error)}`,
2459
+ operation: "createWorktree",
2460
+ cause: error
2461
+ })
2462
+ });
2463
+ const exists = yield* directoryExists(worktreeDir);
2464
+ if (exists) {
2465
+ return worktreeDir;
2397
2466
  }
2398
- return null;
2399
- }
2400
- if (json.type === "content_block_start" && json.content_block?.type === "tool_use") {
2401
- return {
2402
- type: "start",
2403
- name: json.content_block.name || "unknown"
2404
- };
2405
- }
2406
- return null;
2407
- }
2408
- function safeParseJson(jsonStr) {
2409
- try {
2410
- return JSON.parse(jsonStr);
2411
- } catch {
2412
- return null;
2413
- }
2414
- }
2415
- function unwrapStreamEvent(json) {
2416
- if (typeof json === "object" && json !== null && "type" in json && json.type === "stream_event" && "event" in json) {
2417
- return json.event;
2418
- }
2419
- return json;
2420
- }
2421
-
2422
- // src/layers/llm/stream.ts
2423
- function handleToolEvent(toolInfo, toolState, emit) {
2424
- if (toolInfo.type === "start") {
2425
- toolState.currentTool = toolInfo.name;
2426
- toolState.inputChunks.length = 0;
2427
- emit.single({ _tag: "ToolStart", tool: toolInfo.name });
2428
- return;
2429
- }
2430
- if (toolInfo.type === "input_delta" && toolInfo.partialJson) {
2431
- toolState.inputChunks.push(toolInfo.partialJson);
2432
- return;
2433
- }
2434
- if (toolInfo.type === "end" && toolState.currentTool) {
2435
- const inputJson = toolState.inputChunks.join("");
2436
- const input = safeParseJson(inputJson);
2437
- if (input !== null) {
2438
- emit.single({ _tag: "ToolUse", tool: toolState.currentTool, input });
2467
+ const baseRef = baseBranch || "HEAD";
2468
+ const command = `git worktree add "${worktreeDir}" -b "${branchName}" ${baseRef}`;
2469
+ yield* gitExec(command).pipe(
2470
+ Effect4.mapError(
2471
+ (error) => new GitError({
2472
+ message: `Failed to create worktree: ${error.message}`,
2473
+ operation: "createWorktree",
2474
+ cause: error
2475
+ })
2476
+ )
2477
+ );
2478
+ return worktreeDir;
2479
+ }),
2480
+ removeWorktree: (sessionId) => Effect4.gen(function* () {
2481
+ const worktreeDir = getWorktreeDir(sessionId);
2482
+ const branchName = getBranchName(sessionId);
2483
+ const exists = yield* directoryExists(worktreeDir);
2484
+ if (!exists) {
2485
+ return;
2439
2486
  }
2440
- emit.single({ _tag: "ToolEnd", tool: toolState.currentTool });
2441
- toolState.currentTool = "";
2442
- toolState.inputChunks.length = 0;
2443
- }
2444
- }
2445
- function processJsonLine(json, outputChunks, toolState, emit) {
2446
- const event = unwrapStreamEvent(json);
2447
- const text = extractText(event);
2448
- if (text) {
2449
- outputChunks.push(text);
2450
- emit.single({ _tag: "Text", text });
2451
- return;
2452
- }
2453
- const toolInfo = extractToolInfo(event);
2454
- if (toolInfo) {
2455
- handleToolEvent(toolInfo, toolState, emit);
2456
- }
2457
- }
2458
- function createEventStream(child) {
2459
- return Stream4.async((emit) => {
2460
- const outputChunks = [];
2461
- const toolState = { currentTool: "", inputChunks: [] };
2462
- const stdout = child.stdout;
2463
- if (!stdout) {
2464
- emit.fail(
2465
- new LLMError({ message: "Failed to get stdout from child process" })
2487
+ yield* gitExec(`git worktree remove "${worktreeDir}" --force`).pipe(
2488
+ Effect4.mapError(
2489
+ (error) => new GitError({
2490
+ message: `Failed to remove worktree: ${error.message}`,
2491
+ operation: "removeWorktree",
2492
+ cause: error
2493
+ })
2494
+ ),
2495
+ // If git worktree remove fails, try manual cleanup
2496
+ Effect4.catchAll(
2497
+ () => Effect4.tryPromise({
2498
+ try: () => rm(worktreeDir, { recursive: true, force: true }),
2499
+ catch: (error) => new GitError({
2500
+ message: `Failed to remove worktree directory: ${String(error)}`,
2501
+ operation: "removeWorktree",
2502
+ cause: error
2503
+ })
2504
+ })
2505
+ )
2506
+ );
2507
+ yield* gitExec(`git branch -D "${branchName}"`).pipe(
2508
+ Effect4.catchAll(() => Effect4.succeed(void 0))
2509
+ );
2510
+ yield* gitExec("git worktree prune").pipe(
2511
+ Effect4.catchAll(() => Effect4.succeed(void 0))
2512
+ );
2513
+ }),
2514
+ getWorktreePath: (sessionId) => Effect4.gen(function* () {
2515
+ const worktreeDir = getWorktreeDir(sessionId);
2516
+ const exists = yield* directoryExists(worktreeDir);
2517
+ return exists ? worktreeDir : void 0;
2518
+ }),
2519
+ commitChanges: (sessionId, message) => Effect4.gen(function* () {
2520
+ const worktreeDir = getWorktreeDir(sessionId);
2521
+ const exists = yield* directoryExists(worktreeDir);
2522
+ if (!exists) {
2523
+ return yield* Effect4.fail(
2524
+ new GitError({
2525
+ message: `Worktree not found for session: ${sessionId}`,
2526
+ operation: "commit"
2527
+ })
2466
2528
  );
2467
- return Effect4.void;
2468
2529
  }
2469
- const rl = createInterface({
2470
- input: stdout,
2471
- crlfDelay: Number.POSITIVE_INFINITY
2472
- });
2473
- rl.on("line", (line) => {
2474
- const json = parseJsonLine(line);
2475
- if (json) {
2476
- processJsonLine(json, outputChunks, toolState, emit);
2477
- }
2478
- });
2479
- child.stderr?.on("data", (data) => {
2480
- const text = data.toString().trim();
2481
- if (text) {
2482
- emit.single({ _tag: "Text", text: `[stderr] ${text}` });
2483
- }
2484
- });
2485
- child.on("close", (exitCode) => {
2486
- if (exitCode !== 0) {
2487
- emit.fail(
2488
- new LLMError({
2489
- message: `Claude CLI exited with code ${exitCode}`
2530
+ yield* gitExec("git add -A", worktreeDir).pipe(
2531
+ Effect4.mapError(
2532
+ (error) => new GitError({
2533
+ message: `Failed to stage changes: ${error.message}`,
2534
+ operation: "commit",
2535
+ cause: error
2536
+ })
2537
+ )
2538
+ );
2539
+ const status = yield* gitExec("git status --porcelain", worktreeDir).pipe(
2540
+ Effect4.catchAll(() => Effect4.succeed(""))
2541
+ );
2542
+ if (!status) {
2543
+ const head = yield* gitExec("git rev-parse HEAD", worktreeDir).pipe(
2544
+ Effect4.mapError(
2545
+ (error) => new GitError({
2546
+ message: `Failed to get HEAD: ${error.message}`,
2547
+ operation: "commit",
2548
+ cause: error
2490
2549
  })
2491
- );
2492
- } else {
2493
- const fullOutput = outputChunks.join("");
2494
- emit.single({ _tag: "Done", output: fullOutput });
2495
- emit.end();
2496
- }
2497
- });
2498
- child.on("error", (error) => {
2499
- emit.fail(
2500
- new LLMError({
2501
- message: error.message,
2550
+ )
2551
+ );
2552
+ return head;
2553
+ }
2554
+ const escapedMessage = message.replace(/"/g, '\\"');
2555
+ yield* gitExec(`git commit -m "${escapedMessage}"`, worktreeDir).pipe(
2556
+ Effect4.mapError(
2557
+ (error) => new GitError({
2558
+ message: `Failed to commit: ${error.message}`,
2559
+ operation: "commit",
2560
+ cause: error
2561
+ })
2562
+ )
2563
+ );
2564
+ const hash = yield* gitExec("git rev-parse HEAD", worktreeDir).pipe(
2565
+ Effect4.mapError(
2566
+ (error) => new GitError({
2567
+ message: `Failed to get commit hash: ${error.message}`,
2568
+ operation: "commit",
2502
2569
  cause: error
2503
2570
  })
2571
+ )
2572
+ );
2573
+ return hash;
2574
+ }),
2575
+ pushBranch: (sessionId) => Effect4.gen(function* () {
2576
+ const worktreeDir = getWorktreeDir(sessionId);
2577
+ const branchName = getBranchName(sessionId);
2578
+ const exists = yield* directoryExists(worktreeDir);
2579
+ if (!exists) {
2580
+ return yield* Effect4.fail(
2581
+ new GitError({
2582
+ message: `Worktree not found for session: ${sessionId}`,
2583
+ operation: "push"
2584
+ })
2504
2585
  );
2505
- });
2506
- return Effect4.sync(() => {
2507
- child.kill("SIGTERM");
2508
- });
2509
- });
2510
- }
2511
-
2512
- // src/layers/llm/claude-cli.ts
2513
- var make = {
2514
- execute: (prompt) => {
2515
- return Stream5.unwrap(
2516
- Effect5.sync(() => {
2517
- const child = spawn(
2518
- "claude",
2519
- [
2520
- "--permission-mode",
2521
- "acceptEdits",
2522
- "--output-format",
2523
- "stream-json",
2524
- "--verbose",
2525
- "--include-partial-messages",
2526
- "-p",
2527
- prompt
2528
- ],
2529
- {
2530
- stdio: ["inherit", "pipe", "pipe"],
2531
- env: {
2532
- ...process.env,
2533
- FORCE_COLOR: "1"
2534
- }
2535
- }
2536
- );
2537
- return createEventStream(child);
2538
- })
2586
+ }
2587
+ yield* gitExec(`git push -u origin "${branchName}"`, worktreeDir).pipe(
2588
+ Effect4.mapError(
2589
+ (error) => new GitError({
2590
+ message: `Failed to push branch: ${error.message}`,
2591
+ operation: "push",
2592
+ cause: error
2593
+ })
2594
+ )
2539
2595
  );
2540
- }
2596
+ }),
2597
+ createPR: (sessionId, title, body) => Effect4.gen(function* () {
2598
+ const worktreeDir = getWorktreeDir(sessionId);
2599
+ const exists = yield* directoryExists(worktreeDir);
2600
+ if (!exists) {
2601
+ return yield* Effect4.fail(
2602
+ new GitError({
2603
+ message: `Worktree not found for session: ${sessionId}`,
2604
+ operation: "createPR"
2605
+ })
2606
+ );
2607
+ }
2608
+ const escapedTitle = title.replace(/"/g, '\\"');
2609
+ const escapedBody = body.replace(/"/g, '\\"');
2610
+ const prUrl = yield* gitExec(
2611
+ `gh pr create --title "${escapedTitle}" --body "${escapedBody}"`,
2612
+ worktreeDir
2613
+ ).pipe(
2614
+ Effect4.mapError(
2615
+ (error) => new GitError({
2616
+ message: `Failed to create PR: ${error.message}`,
2617
+ operation: "createPR",
2618
+ cause: error
2619
+ })
2620
+ )
2621
+ );
2622
+ return prUrl;
2623
+ }),
2624
+ getBranchName
2541
2625
  };
2542
- var Live = Layer.succeed(LLM, make);
2543
- var ClaudeCLI = {
2626
+ var Live = Layer.succeed(Git, make);
2627
+ var FileSystemGit = {
2544
2628
  Live
2545
2629
  };
2546
2630
 
2547
- // src/layers/llm/mock.ts
2548
- init_esm_shims();
2549
- import { Effect as Effect6, Layer as Layer2, Schema as S2, Stream as Stream6 } from "effect";
2550
-
2551
- // src/domain/schemas/llm.ts
2631
+ // src/layers/git/memory.ts
2552
2632
  init_esm_shims();
2553
- import { Schema as S } from "effect";
2554
- var TextEventSchema = S.TaggedStruct("Text", {
2555
- text: S.String
2556
- });
2557
- var ToolStartEventSchema = S.TaggedStruct("ToolStart", {
2558
- tool: S.String
2559
- });
2560
- var ToolUseEventSchema = S.TaggedStruct("ToolUse", {
2561
- tool: S.String,
2562
- input: S.Unknown
2563
- });
2564
- var ToolEndEventSchema = S.TaggedStruct("ToolEnd", {
2565
- tool: S.String
2566
- });
2567
- var DoneEventSchema = S.TaggedStruct("Done", {
2568
- output: S.String
2569
- });
2570
- var LLMEventSchema = S.Union(
2571
- TextEventSchema,
2572
- ToolStartEventSchema,
2573
- ToolUseEventSchema,
2574
- ToolEndEventSchema,
2575
- DoneEventSchema
2576
- );
2577
- var decodeLLMEvent = S.decodeUnknown(LLMEventSchema);
2578
-
2579
- // src/layers/llm/mock.ts
2580
- var MockLLMConfigSchema = S2.Struct({
2581
- events: S2.Array(LLMEventSchema),
2582
- delayMs: S2.optional(S2.Number)
2583
- });
2584
- function createMockLLM(config) {
2633
+ import { Effect as Effect5, Layer as Layer2, Ref as Ref3 } from "effect";
2634
+ var BRANCH_PREFIX2 = "ferix";
2635
+ function getBranchName2(sessionId) {
2636
+ return `${BRANCH_PREFIX2}/${sessionId}`;
2637
+ }
2638
+ function createMemoryGitService(stateRef, commitCounterRef) {
2585
2639
  return {
2586
- execute: (_prompt) => {
2587
- const baseStream = Stream6.fromIterable(config.events);
2588
- if (config.delayMs !== void 0 && config.delayMs > 0) {
2589
- const delay = config.delayMs;
2590
- return baseStream.pipe(Stream6.tap(() => Effect6.sleep(delay)));
2640
+ createWorktree: (sessionId, _baseBranch) => Effect5.gen(function* () {
2641
+ const state = yield* Ref3.get(stateRef);
2642
+ const existing = state.get(sessionId);
2643
+ if (existing) {
2644
+ return existing.path;
2591
2645
  }
2592
- return baseStream;
2593
- }
2646
+ const path2 = `.ferix/worktrees/${sessionId}`;
2647
+ const branch = getBranchName2(sessionId);
2648
+ const worktree = {
2649
+ path: path2,
2650
+ branch,
2651
+ commits: []
2652
+ };
2653
+ state.set(sessionId, worktree);
2654
+ yield* Ref3.set(stateRef, state);
2655
+ return path2;
2656
+ }),
2657
+ removeWorktree: (sessionId) => Effect5.gen(function* () {
2658
+ const state = yield* Ref3.get(stateRef);
2659
+ state.delete(sessionId);
2660
+ yield* Ref3.set(stateRef, state);
2661
+ }),
2662
+ getWorktreePath: (sessionId) => Effect5.gen(function* () {
2663
+ const state = yield* Ref3.get(stateRef);
2664
+ const worktree = state.get(sessionId);
2665
+ return worktree?.path;
2666
+ }),
2667
+ commitChanges: (sessionId, message) => Effect5.gen(function* () {
2668
+ const state = yield* Ref3.get(stateRef);
2669
+ const worktree = state.get(sessionId);
2670
+ if (!worktree) {
2671
+ return yield* Effect5.fail(
2672
+ new GitError({
2673
+ message: `Worktree not found for session: ${sessionId}`,
2674
+ operation: "commit"
2675
+ })
2676
+ );
2677
+ }
2678
+ const counter = yield* Ref3.updateAndGet(commitCounterRef, (n) => n + 1);
2679
+ const hash = `test-commit-${counter}`;
2680
+ const updatedWorktree = {
2681
+ ...worktree,
2682
+ commits: [...worktree.commits, `${hash}: ${message}`]
2683
+ };
2684
+ state.set(sessionId, updatedWorktree);
2685
+ yield* Ref3.set(stateRef, state);
2686
+ return hash;
2687
+ }),
2688
+ pushBranch: (sessionId) => Effect5.gen(function* () {
2689
+ const state = yield* Ref3.get(stateRef);
2690
+ const worktree = state.get(sessionId);
2691
+ if (!worktree) {
2692
+ return yield* Effect5.fail(
2693
+ new GitError({
2694
+ message: `Worktree not found for session: ${sessionId}`,
2695
+ operation: "push"
2696
+ })
2697
+ );
2698
+ }
2699
+ }),
2700
+ createPR: (sessionId, title, _body) => Effect5.gen(function* () {
2701
+ const state = yield* Ref3.get(stateRef);
2702
+ const worktree = state.get(sessionId);
2703
+ if (!worktree) {
2704
+ return yield* Effect5.fail(
2705
+ new GitError({
2706
+ message: `Worktree not found for session: ${sessionId}`,
2707
+ operation: "createPR"
2708
+ })
2709
+ );
2710
+ }
2711
+ const slug = title.toLowerCase().replace(/\s+/g, "-").slice(0, 30);
2712
+ return `https://github.com/test/repo/pull/${slug}`;
2713
+ }),
2714
+ getBranchName: getBranchName2
2594
2715
  };
2595
2716
  }
2596
- var defaultMockEvents = [
2597
- { _tag: "Text", text: "Processing task...\n" },
2598
- { _tag: "ToolStart", tool: "Read" },
2599
- { _tag: "ToolEnd", tool: "Read" },
2600
- { _tag: "Text", text: "Task completed successfully.\n" },
2601
- {
2602
- _tag: "Done",
2603
- output: "Processing task...\nTask completed successfully.\n"
2604
- }
2605
- ];
2606
- var defaultMock = createMockLLM({ events: defaultMockEvents });
2607
- var Live2 = Layer2.succeed(LLM, defaultMock);
2608
- function layer(config) {
2609
- return Layer2.succeed(LLM, createMockLLM(config));
2717
+ function layer() {
2718
+ return Layer2.effect(
2719
+ Git,
2720
+ Effect5.gen(function* () {
2721
+ const stateRef = yield* Ref3.make(/* @__PURE__ */ new Map());
2722
+ const commitCounterRef = yield* Ref3.make(0);
2723
+ return createMemoryGitService(stateRef, commitCounterRef);
2724
+ })
2725
+ );
2610
2726
  }
2611
- var Mock = {
2727
+ var Live2 = layer();
2728
+ var MemoryGit = {
2612
2729
  Live: Live2,
2613
- layer,
2614
- createMockLLM
2730
+ layer
2615
2731
  };
2616
2732
 
2617
- // src/layers/plan/file-system.ts
2733
+ // src/layers/guardrails/file-system.ts
2618
2734
  init_esm_shims();
2619
- import { access, mkdir, readdir, readFile, writeFile } from "fs/promises";
2620
- import { join } from "path";
2621
- import { Effect as Effect7, Layer as Layer3 } from "effect";
2735
+ import { mkdir as mkdir2, readFile, writeFile } from "fs/promises";
2736
+ import { join as join2 } from "path";
2737
+ import { DateTime, Effect as Effect6, Layer as Layer3 } from "effect";
2622
2738
 
2623
2739
  // src/domain/index.ts
2624
2740
  init_esm_shims();
@@ -2628,55 +2744,55 @@ init_esm_shims();
2628
2744
 
2629
2745
  // src/domain/schemas/config.ts
2630
2746
  init_esm_shims();
2631
- import { Schema as S3 } from "effect";
2632
- var PhasePromptOverridesSchema = S3.Struct({
2633
- breakdown: S3.optional(S3.String),
2634
- planning: S3.optional(S3.String),
2635
- execution: S3.optional(S3.String),
2636
- check: S3.optional(S3.String),
2637
- verify: S3.optional(S3.String),
2638
- review: S3.optional(S3.String),
2639
- completion: S3.optional(S3.String)
2640
- });
2641
- var PromptConfigSchema = S3.Struct({
2642
- systemPrompt: S3.optional(S3.String),
2643
- phases: S3.optional(PhasePromptOverridesSchema),
2644
- additionalContext: S3.optional(S3.String)
2645
- });
2646
- var LoopConfigSchema = S3.Struct({
2647
- task: S3.String,
2648
- maxIterations: S3.Number,
2649
- verifyCommands: S3.Array(S3.String),
2650
- sessionId: S3.optional(S3.String),
2651
- branch: S3.optional(S3.String),
2652
- push: S3.optional(S3.Boolean),
2653
- pr: S3.optional(S3.Boolean),
2654
- verbose: S3.optional(S3.Boolean),
2655
- prompts: S3.optional(PromptConfigSchema)
2656
- });
2657
- var LoopSummarySchema = S3.Struct({
2658
- iterations: S3.Number,
2659
- success: S3.Boolean,
2660
- sessionId: S3.String,
2661
- completedTasks: S3.Array(S3.String),
2662
- durationMs: S3.Number
2663
- });
2664
- var LoopErrorSchema = S3.Struct({
2665
- message: S3.String,
2666
- phase: S3.String,
2667
- iteration: S3.optional(S3.Number)
2668
- });
2669
- var decodeLoopConfig = S3.decodeUnknown(LoopConfigSchema);
2747
+ import { Schema as S } from "effect";
2748
+ var PhasePromptOverridesSchema = S.Struct({
2749
+ breakdown: S.optional(S.String),
2750
+ planning: S.optional(S.String),
2751
+ execution: S.optional(S.String),
2752
+ check: S.optional(S.String),
2753
+ verify: S.optional(S.String),
2754
+ review: S.optional(S.String),
2755
+ completion: S.optional(S.String)
2756
+ });
2757
+ var PromptConfigSchema = S.Struct({
2758
+ systemPrompt: S.optional(S.String),
2759
+ phases: S.optional(PhasePromptOverridesSchema),
2760
+ additionalContext: S.optional(S.String)
2761
+ });
2762
+ var LoopConfigSchema = S.Struct({
2763
+ task: S.String,
2764
+ maxIterations: S.Number,
2765
+ verifyCommands: S.Array(S.String),
2766
+ sessionId: S.optional(S.String),
2767
+ branch: S.optional(S.String),
2768
+ push: S.optional(S.Boolean),
2769
+ pr: S.optional(S.Boolean),
2770
+ verbose: S.optional(S.Boolean),
2771
+ prompts: S.optional(PromptConfigSchema)
2772
+ });
2773
+ var LoopSummarySchema = S.Struct({
2774
+ iterations: S.Number,
2775
+ success: S.Boolean,
2776
+ sessionId: S.String,
2777
+ completedTasks: S.Array(S.String),
2778
+ durationMs: S.Number
2779
+ });
2780
+ var LoopErrorSchema = S.Struct({
2781
+ message: S.String,
2782
+ phase: S.String,
2783
+ iteration: S.optional(S.Number)
2784
+ });
2785
+ var decodeLoopConfig = S.decodeUnknown(LoopConfigSchema);
2670
2786
 
2671
2787
  // src/domain/schemas/events.ts
2672
2788
  init_esm_shims();
2673
- import { Schema as S6 } from "effect";
2789
+ import { Schema as S4 } from "effect";
2674
2790
 
2675
2791
  // src/domain/schemas/plan.ts
2676
2792
  init_esm_shims();
2677
- import { Brand, Schema as S4 } from "effect";
2793
+ import { Brand, Schema as S2 } from "effect";
2678
2794
  var PlanId = Brand.nominal();
2679
- var TaskStatusSchema = S4.Literal(
2795
+ var TaskStatusSchema = S2.Literal(
2680
2796
  "pending",
2681
2797
  "planning",
2682
2798
  "in_progress",
@@ -2684,114 +2800,114 @@ var TaskStatusSchema = S4.Literal(
2684
2800
  "failed",
2685
2801
  "skipped"
2686
2802
  );
2687
- var PhaseStatusSchema = S4.Literal(
2803
+ var PhaseStatusSchema = S2.Literal(
2688
2804
  "pending",
2689
2805
  "in_progress",
2690
2806
  "done",
2691
2807
  "failed"
2692
2808
  );
2693
- var CriterionStatusSchema = S4.Literal("pending", "passed", "failed");
2694
- var PhaseSchema = S4.Struct({
2695
- id: S4.String,
2696
- description: S4.String,
2809
+ var CriterionStatusSchema = S2.Literal("pending", "passed", "failed");
2810
+ var PhaseSchema = S2.Struct({
2811
+ id: S2.String,
2812
+ description: S2.String,
2697
2813
  status: PhaseStatusSchema
2698
2814
  });
2699
- var CriterionSchema = S4.Struct({
2700
- id: S4.String,
2701
- description: S4.String,
2815
+ var CriterionSchema = S2.Struct({
2816
+ id: S2.String,
2817
+ description: S2.String,
2702
2818
  status: CriterionStatusSchema,
2703
- failureReason: S4.optional(S4.String)
2819
+ failureReason: S2.optional(S2.String)
2704
2820
  });
2705
- var TaskSchema = S4.Struct({
2706
- id: S4.String,
2707
- title: S4.String,
2708
- description: S4.String,
2821
+ var TaskSchema = S2.Struct({
2822
+ id: S2.String,
2823
+ title: S2.String,
2824
+ description: S2.String,
2709
2825
  status: TaskStatusSchema,
2710
- phases: S4.Array(PhaseSchema),
2711
- criteria: S4.Array(CriterionSchema),
2712
- filesToModify: S4.Array(S4.String),
2713
- attempts: S4.Number,
2714
- completionNotes: S4.optional(S4.String)
2715
- });
2716
- var PlanDataSchema = S4.Struct({
2717
- sessionId: S4.String,
2718
- createdAt: S4.String,
2719
- originalTask: S4.String,
2720
- context: S4.optional(S4.String),
2721
- tasks: S4.Array(TaskSchema)
2722
- });
2723
- var PlanSchema = S4.Struct({
2724
- id: S4.String,
2725
- sessionId: S4.String,
2726
- createdAt: S4.String,
2727
- originalTask: S4.String,
2728
- context: S4.optional(S4.String),
2729
- tasks: S4.Array(TaskSchema)
2730
- });
2731
- var decodePlan = S4.decodeUnknown(PlanSchema);
2732
- var decodePlanData = S4.decodeUnknown(PlanDataSchema);
2826
+ phases: S2.Array(PhaseSchema),
2827
+ criteria: S2.Array(CriterionSchema),
2828
+ filesToModify: S2.Array(S2.String),
2829
+ attempts: S2.Number,
2830
+ completionNotes: S2.optional(S2.String)
2831
+ });
2832
+ var PlanDataSchema = S2.Struct({
2833
+ sessionId: S2.String,
2834
+ createdAt: S2.String,
2835
+ originalTask: S2.String,
2836
+ context: S2.optional(S2.String),
2837
+ tasks: S2.Array(TaskSchema)
2838
+ });
2839
+ var PlanSchema = S2.Struct({
2840
+ id: S2.String,
2841
+ sessionId: S2.String,
2842
+ createdAt: S2.String,
2843
+ originalTask: S2.String,
2844
+ context: S2.optional(S2.String),
2845
+ tasks: S2.Array(TaskSchema)
2846
+ });
2847
+ var decodePlan = S2.decodeUnknown(PlanSchema);
2848
+ var decodePlanData = S2.decodeUnknown(PlanDataSchema);
2733
2849
 
2734
2850
  // src/domain/schemas/shared.ts
2735
2851
  init_esm_shims();
2736
- import { Schema as S5 } from "effect";
2737
- var TaskBasicInfoSchema = S5.Struct({
2738
- id: S5.String,
2739
- title: S5.String,
2740
- description: S5.String
2852
+ import { Schema as S3 } from "effect";
2853
+ var TaskBasicInfoSchema = S3.Struct({
2854
+ id: S3.String,
2855
+ title: S3.String,
2856
+ description: S3.String
2741
2857
  });
2742
- var PhaseBasicInfoSchema = S5.Struct({
2743
- id: S5.String,
2744
- description: S5.String
2858
+ var PhaseBasicInfoSchema = S3.Struct({
2859
+ id: S3.String,
2860
+ description: S3.String
2745
2861
  });
2746
- var CriterionBasicInfoSchema = S5.Struct({
2747
- id: S5.String,
2748
- description: S5.String
2862
+ var CriterionBasicInfoSchema = S3.Struct({
2863
+ id: S3.String,
2864
+ description: S3.String
2749
2865
  });
2750
- var TasksDefinedDataSchema = S5.Struct({
2751
- tasks: S5.Array(TaskBasicInfoSchema)
2866
+ var TasksDefinedDataSchema = S3.Struct({
2867
+ tasks: S3.Array(TaskBasicInfoSchema)
2752
2868
  });
2753
- var PhasesDefinedDataSchema = S5.Struct({
2754
- taskId: S5.String,
2755
- phases: S5.Array(PhaseBasicInfoSchema)
2869
+ var PhasesDefinedDataSchema = S3.Struct({
2870
+ taskId: S3.String,
2871
+ phases: S3.Array(PhaseBasicInfoSchema)
2756
2872
  });
2757
- var CriteriaDefinedDataSchema = S5.Struct({
2758
- taskId: S5.String,
2759
- criteria: S5.Array(CriterionBasicInfoSchema)
2873
+ var CriteriaDefinedDataSchema = S3.Struct({
2874
+ taskId: S3.String,
2875
+ criteria: S3.Array(CriterionBasicInfoSchema)
2760
2876
  });
2761
- var PhaseIdDataSchema = S5.Struct({
2762
- phaseId: S5.String
2877
+ var PhaseIdDataSchema = S3.Struct({
2878
+ phaseId: S3.String
2763
2879
  });
2764
- var PhaseFailedDataSchema = S5.Struct({
2765
- phaseId: S5.String,
2766
- reason: S5.String
2880
+ var PhaseFailedDataSchema = S3.Struct({
2881
+ phaseId: S3.String,
2882
+ reason: S3.String
2767
2883
  });
2768
- var CriterionIdDataSchema = S5.Struct({
2769
- criterionId: S5.String
2884
+ var CriterionIdDataSchema = S3.Struct({
2885
+ criterionId: S3.String
2770
2886
  });
2771
- var CriterionFailedDataSchema = S5.Struct({
2772
- criterionId: S5.String,
2773
- reason: S5.String
2887
+ var CriterionFailedDataSchema = S3.Struct({
2888
+ criterionId: S3.String,
2889
+ reason: S3.String
2774
2890
  });
2775
- var ReviewCompleteDataSchema = S5.Struct({
2776
- changesMade: S5.Boolean
2891
+ var ReviewCompleteDataSchema = S3.Struct({
2892
+ changesMade: S3.Boolean
2777
2893
  });
2778
- var TaskCompleteSignalDataSchema = S5.Struct({
2779
- taskId: S5.String,
2780
- summary: S5.String,
2781
- filesModified: S5.Array(S5.String),
2782
- filesCreated: S5.Array(S5.String)
2894
+ var TaskCompleteSignalDataSchema = S3.Struct({
2895
+ taskId: S3.String,
2896
+ summary: S3.String,
2897
+ filesModified: S3.Array(S3.String),
2898
+ filesCreated: S3.Array(S3.String)
2783
2899
  });
2784
- var TaskCompleteDataSchema = S5.Struct({
2785
- taskId: S5.String,
2786
- summary: S5.String
2900
+ var TaskCompleteDataSchema = S3.Struct({
2901
+ taskId: S3.String,
2902
+ summary: S3.String
2787
2903
  });
2788
2904
 
2789
2905
  // src/domain/schemas/events.ts
2790
- var taggedEvent = (tag, fields) => S6.TaggedStruct(tag, fields);
2791
- var taggedFromData = (tag, dataSchema, extraFields = {}) => S6.TaggedStruct(tag, { ...dataSchema.fields, ...extraFields });
2906
+ var taggedEvent = (tag, fields) => S4.TaggedStruct(tag, fields);
2907
+ var taggedFromData = (tag, dataSchema, extraFields = {}) => S4.TaggedStruct(tag, { ...dataSchema.fields, ...extraFields });
2792
2908
  var LoopStartedEventSchema = taggedEvent("LoopStarted", {
2793
2909
  config: LoopConfigSchema,
2794
- timestamp: S6.Number
2910
+ timestamp: S4.Number
2795
2911
  });
2796
2912
  var LoopCompletedEventSchema = taggedEvent("LoopCompleted", {
2797
2913
  summary: LoopSummarySchema
@@ -2800,30 +2916,30 @@ var LoopFailedEventSchema = taggedEvent("LoopFailed", {
2800
2916
  error: LoopErrorSchema
2801
2917
  });
2802
2918
  var DiscoveryStartedEventSchema = taggedEvent("DiscoveryStarted", {
2803
- timestamp: S6.Number
2919
+ timestamp: S4.Number
2804
2920
  });
2805
2921
  var DiscoveryCompletedEventSchema = taggedEvent("DiscoveryCompleted", {
2806
- taskCount: S6.Number,
2807
- timestamp: S6.Number
2922
+ taskCount: S4.Number,
2923
+ timestamp: S4.Number
2808
2924
  });
2809
2925
  var IterationStartedEventSchema = taggedEvent("IterationStarted", {
2810
- iteration: S6.Number
2926
+ iteration: S4.Number
2811
2927
  });
2812
2928
  var IterationCompletedEventSchema = taggedEvent("IterationCompleted", {
2813
- iteration: S6.Number
2929
+ iteration: S4.Number
2814
2930
  });
2815
2931
  var LLMTextEventSchema = taggedEvent("LLMText", {
2816
- text: S6.String
2932
+ text: S4.String
2817
2933
  });
2818
2934
  var LLMToolStartEventSchema = taggedEvent("LLMToolStart", {
2819
- tool: S6.String
2935
+ tool: S4.String
2820
2936
  });
2821
2937
  var LLMToolUseEventSchema = taggedEvent("LLMToolUse", {
2822
- tool: S6.String,
2823
- input: S6.Unknown
2938
+ tool: S4.String,
2939
+ input: S4.Unknown
2824
2940
  });
2825
2941
  var LLMToolEndEventSchema = taggedEvent("LLMToolEnd", {
2826
- tool: S6.String
2942
+ tool: S4.String
2827
2943
  });
2828
2944
  var TasksDefinedEventSchema = taggedFromData(
2829
2945
  "TasksDefined",
@@ -2840,17 +2956,17 @@ var CriteriaDefinedEventSchema = taggedFromData(
2840
2956
  var PhaseStartedEventSchema = taggedFromData(
2841
2957
  "PhaseStarted",
2842
2958
  PhaseIdDataSchema,
2843
- { timestamp: S6.Number }
2959
+ { timestamp: S4.Number }
2844
2960
  );
2845
2961
  var PhaseCompletedEventSchema = taggedFromData(
2846
2962
  "PhaseCompleted",
2847
2963
  PhaseIdDataSchema,
2848
- { timestamp: S6.Number }
2964
+ { timestamp: S4.Number }
2849
2965
  );
2850
2966
  var PhaseFailedEventSchema = taggedFromData(
2851
2967
  "PhaseFailed",
2852
2968
  PhaseFailedDataSchema,
2853
- { timestamp: S6.Number }
2969
+ { timestamp: S4.Number }
2854
2970
  );
2855
2971
  var CriterionPassedEventSchema = taggedFromData(
2856
2972
  "CriterionPassed",
@@ -2860,9 +2976,9 @@ var CriterionFailedEventSchema = taggedFromData(
2860
2976
  "CriterionFailed",
2861
2977
  CriterionFailedDataSchema
2862
2978
  );
2863
- var CheckPassedEventSchema = S6.TaggedStruct("CheckPassed", {});
2979
+ var CheckPassedEventSchema = S4.TaggedStruct("CheckPassed", {});
2864
2980
  var CheckFailedEventSchema = taggedEvent("CheckFailed", {
2865
- failedCriteria: S6.Array(S6.String)
2981
+ failedCriteria: S4.Array(S4.String)
2866
2982
  });
2867
2983
  var ReviewCompleteEventSchema = taggedFromData(
2868
2984
  "ReviewComplete",
@@ -2871,7 +2987,7 @@ var ReviewCompleteEventSchema = taggedFromData(
2871
2987
  var TaskCompletedEventSchema = taggedFromData(
2872
2988
  "TaskCompleted",
2873
2989
  TaskCompleteDataSchema,
2874
- { timestamp: S6.Number }
2990
+ { timestamp: S4.Number }
2875
2991
  );
2876
2992
  var PlanCreatedEventSchema = taggedEvent("PlanCreated", {
2877
2993
  plan: PlanSchema
@@ -2880,11 +2996,43 @@ var PlanUpdatedEventSchema = taggedEvent("PlanUpdated", {
2880
2996
  plan: PlanSchema
2881
2997
  });
2882
2998
  var PlanUpdateFailedEventSchema = taggedEvent("PlanUpdateFailed", {
2883
- operation: S6.Literal("create", "update"),
2884
- error: S6.String,
2885
- planId: S6.optional(S6.String)
2999
+ operation: S4.Literal("create", "update"),
3000
+ error: S4.String,
3001
+ planId: S4.optional(S4.String)
3002
+ });
3003
+ var LearningRecordedEventSchema = taggedEvent("LearningRecorded", {
3004
+ iteration: S4.Number,
3005
+ content: S4.String,
3006
+ category: S4.optional(S4.Literal("success", "failure", "optimization")),
3007
+ timestamp: S4.Number
3008
+ });
3009
+ var GuardrailAddedEventSchema = taggedEvent("GuardrailAdded", {
3010
+ id: S4.String,
3011
+ iteration: S4.Number,
3012
+ pattern: S4.String,
3013
+ sign: S4.String,
3014
+ avoidance: S4.String,
3015
+ severity: S4.Literal("warning", "critical"),
3016
+ timestamp: S4.Number
3017
+ });
3018
+ var ProgressUpdatedEventSchema = taggedEvent("ProgressUpdated", {
3019
+ sessionId: S4.String,
3020
+ iteration: S4.Number,
3021
+ taskId: S4.String,
3022
+ action: S4.Literal("started", "completed", "failed", "learning"),
3023
+ timestamp: S4.Number
3024
+ });
3025
+ var WorktreeCreatedEventSchema = taggedEvent("WorktreeCreated", {
3026
+ sessionId: S4.String,
3027
+ worktreePath: S4.String,
3028
+ branchName: S4.String,
3029
+ timestamp: S4.Number
3030
+ });
3031
+ var WorktreeRemovedEventSchema = taggedEvent("WorktreeRemoved", {
3032
+ sessionId: S4.String,
3033
+ timestamp: S4.Number
2886
3034
  });
2887
- var DomainEventSchema = S6.Union(
3035
+ var DomainEventSchema = S4.Union(
2888
3036
  LoopStartedEventSchema,
2889
3037
  LoopCompletedEventSchema,
2890
3038
  LoopFailedEventSchema,
@@ -2910,7 +3058,12 @@ var DomainEventSchema = S6.Union(
2910
3058
  TaskCompletedEventSchema,
2911
3059
  PlanCreatedEventSchema,
2912
3060
  PlanUpdatedEventSchema,
2913
- PlanUpdateFailedEventSchema
3061
+ PlanUpdateFailedEventSchema,
3062
+ LearningRecordedEventSchema,
3063
+ GuardrailAddedEventSchema,
3064
+ ProgressUpdatedEventSchema,
3065
+ WorktreeCreatedEventSchema,
3066
+ WorktreeRemovedEventSchema
2914
3067
  );
2915
3068
  var DomainEventUtils = {
2916
3069
  isLLMEvent: (e) => e._tag.startsWith("LLM"),
@@ -2920,6 +3073,55 @@ var DomainEventUtils = {
2920
3073
  isDiscoveryEvent: (e) => e._tag.startsWith("Discovery")
2921
3074
  };
2922
3075
 
3076
+ // src/domain/schemas/guardrails.ts
3077
+ init_esm_shims();
3078
+ import { Schema as S5 } from "effect";
3079
+ var GuardrailSeveritySchema = S5.Literal("warning", "critical");
3080
+ var GuardrailSchema = S5.Struct({
3081
+ id: S5.String,
3082
+ createdAt: S5.String,
3083
+ iteration: S5.Number,
3084
+ pattern: S5.String,
3085
+ sign: S5.String,
3086
+ avoidance: S5.String,
3087
+ severity: GuardrailSeveritySchema
3088
+ });
3089
+ var GuardrailsFileSchema = S5.Struct({
3090
+ sessionId: S5.String,
3091
+ createdAt: S5.String,
3092
+ guardrails: S5.Array(GuardrailSchema)
3093
+ });
3094
+ var decodeGuardrail = S5.decodeUnknown(GuardrailSchema);
3095
+ var decodeGuardrailsFile = S5.decodeUnknown(GuardrailsFileSchema);
3096
+
3097
+ // src/domain/schemas/llm.ts
3098
+ init_esm_shims();
3099
+ import { Schema as S6 } from "effect";
3100
+ var TextEventSchema = S6.TaggedStruct("Text", {
3101
+ text: S6.String
3102
+ });
3103
+ var ToolStartEventSchema = S6.TaggedStruct("ToolStart", {
3104
+ tool: S6.String
3105
+ });
3106
+ var ToolUseEventSchema = S6.TaggedStruct("ToolUse", {
3107
+ tool: S6.String,
3108
+ input: S6.Unknown
3109
+ });
3110
+ var ToolEndEventSchema = S6.TaggedStruct("ToolEnd", {
3111
+ tool: S6.String
3112
+ });
3113
+ var DoneEventSchema = S6.TaggedStruct("Done", {
3114
+ output: S6.String
3115
+ });
3116
+ var LLMEventSchema = S6.Union(
3117
+ TextEventSchema,
3118
+ ToolStartEventSchema,
3119
+ ToolUseEventSchema,
3120
+ ToolEndEventSchema,
3121
+ DoneEventSchema
3122
+ );
3123
+ var decodeLLMEvent = S6.decodeUnknown(LLMEventSchema);
3124
+
2923
3125
  // src/domain/schemas/logger.ts
2924
3126
  init_esm_shims();
2925
3127
  import { Schema as S7 } from "effect";
@@ -2948,29 +3150,57 @@ var RunOptionsDataSchema = S8.Struct({
2948
3150
  consumer: S8.optional(ConsumerTypeSchema)
2949
3151
  });
2950
3152
 
2951
- // src/domain/schemas/session.ts
3153
+ // src/domain/schemas/progress.ts
2952
3154
  init_esm_shims();
2953
3155
  import { Schema as S9 } from "effect";
2954
- var SessionStatusSchema = S9.Literal(
3156
+ var ProgressActionSchema = S9.Literal(
3157
+ "started",
3158
+ "completed",
3159
+ "failed",
3160
+ "learning"
3161
+ );
3162
+ var ProgressEntrySchema = S9.Struct({
3163
+ iteration: S9.Number,
3164
+ timestamp: S9.String,
3165
+ taskId: S9.String,
3166
+ action: ProgressActionSchema,
3167
+ summary: S9.String,
3168
+ learnings: S9.optional(S9.Array(S9.String)),
3169
+ filesModified: S9.optional(S9.Array(S9.String))
3170
+ });
3171
+ var ProgressFileSchema = S9.Struct({
3172
+ sessionId: S9.String,
3173
+ createdAt: S9.String,
3174
+ entries: S9.Array(ProgressEntrySchema)
3175
+ });
3176
+ var decodeProgressEntry = S9.decodeUnknown(ProgressEntrySchema);
3177
+ var decodeProgressFile = S9.decodeUnknown(ProgressFileSchema);
3178
+
3179
+ // src/domain/schemas/session.ts
3180
+ init_esm_shims();
3181
+ import { Schema as S10 } from "effect";
3182
+ var SessionStatusSchema = S10.Literal(
2955
3183
  "active",
2956
3184
  "completed",
2957
3185
  "failed",
2958
3186
  "paused"
2959
3187
  );
2960
- var SessionSchema = S9.Struct({
2961
- id: S9.String,
2962
- createdAt: S9.String,
3188
+ var SessionSchema = S10.Struct({
3189
+ id: S10.String,
3190
+ createdAt: S10.String,
2963
3191
  status: SessionStatusSchema,
2964
- originalTask: S9.String,
2965
- completedTasks: S9.Array(S9.String),
2966
- currentTaskId: S9.optional(S9.String)
3192
+ originalTask: S10.String,
3193
+ completedTasks: S10.Array(S10.String),
3194
+ currentTaskId: S10.optional(S10.String),
3195
+ worktreePath: S10.optional(S10.String),
3196
+ branchName: S10.optional(S10.String)
2967
3197
  });
2968
- var decodeSession = S9.decodeUnknown(SessionSchema);
3198
+ var decodeSession = S10.decodeUnknown(SessionSchema);
2969
3199
 
2970
3200
  // src/domain/schemas/signals.ts
2971
3201
  init_esm_shims();
2972
- import { Schema as S10 } from "effect";
2973
- var taggedFromData2 = (tag, dataSchema) => S10.TaggedStruct(tag, dataSchema.fields);
3202
+ import { Schema as S11 } from "effect";
3203
+ var taggedFromData2 = (tag, dataSchema) => S11.TaggedStruct(tag, dataSchema.fields);
2974
3204
  var TasksDefinedSignalSchema = taggedFromData2(
2975
3205
  "TasksDefined",
2976
3206
  TasksDefinedDataSchema
@@ -3003,8 +3233,8 @@ var CriterionFailedSignalSchema = taggedFromData2(
3003
3233
  "CriterionFailed",
3004
3234
  CriterionFailedDataSchema
3005
3235
  );
3006
- var CheckPassedSignalSchema = S10.TaggedStruct("CheckPassed", {});
3007
- var CheckFailedSignalSchema = S10.TaggedStruct("CheckFailed", {});
3236
+ var CheckPassedSignalSchema = S11.TaggedStruct("CheckPassed", {});
3237
+ var CheckFailedSignalSchema = S11.TaggedStruct("CheckFailed", {});
3008
3238
  var ReviewCompleteSignalSchema = taggedFromData2(
3009
3239
  "ReviewComplete",
3010
3240
  ReviewCompleteDataSchema
@@ -3013,8 +3243,23 @@ var TaskCompleteSignalSchema = taggedFromData2(
3013
3243
  "TaskComplete",
3014
3244
  TaskCompleteSignalDataSchema
3015
3245
  );
3016
- var LoopCompleteSignalSchema = S10.TaggedStruct("LoopComplete", {});
3017
- var SignalSchema = S10.Union(
3246
+ var LoopCompleteSignalSchema = S11.TaggedStruct("LoopComplete", {});
3247
+ var LearningCategorySchema = S11.Literal(
3248
+ "success",
3249
+ "failure",
3250
+ "optimization"
3251
+ );
3252
+ var LearningSignalSchema = S11.TaggedStruct("Learning", {
3253
+ content: S11.String,
3254
+ category: S11.optional(LearningCategorySchema)
3255
+ });
3256
+ var GuardrailSignalSchema = S11.TaggedStruct("Guardrail", {
3257
+ pattern: S11.String,
3258
+ sign: S11.String,
3259
+ avoidance: S11.String,
3260
+ severity: S11.Literal("warning", "critical")
3261
+ });
3262
+ var SignalSchema = S11.Union(
3018
3263
  TasksDefinedSignalSchema,
3019
3264
  PhasesDefinedSignalSchema,
3020
3265
  CriteriaDefinedSignalSchema,
@@ -3027,26 +3272,28 @@ var SignalSchema = S10.Union(
3027
3272
  CheckFailedSignalSchema,
3028
3273
  ReviewCompleteSignalSchema,
3029
3274
  TaskCompleteSignalSchema,
3030
- LoopCompleteSignalSchema
3275
+ LoopCompleteSignalSchema,
3276
+ LearningSignalSchema,
3277
+ GuardrailSignalSchema
3031
3278
  );
3032
- var decodeSignal = S10.decodeUnknown(SignalSchema);
3033
- var decodeSignalSync = S10.decodeUnknownSync(SignalSchema);
3279
+ var decodeSignal = S11.decodeUnknown(SignalSchema);
3280
+ var decodeSignalSync = S11.decodeUnknownSync(SignalSchema);
3034
3281
 
3035
3282
  // src/domain/schemas/task-generation.ts
3036
3283
  init_esm_shims();
3037
- import { Schema as S11 } from "effect";
3038
- var GeneratedTaskStatusSchema = S11.Literal(
3284
+ import { Schema as S12 } from "effect";
3285
+ var GeneratedTaskStatusSchema = S12.Literal(
3039
3286
  "pending",
3040
3287
  "in_progress",
3041
3288
  "done",
3042
3289
  "failed"
3043
3290
  );
3044
- var GeneratedTaskSchema = S11.Struct({
3045
- id: S11.String,
3046
- title: S11.String,
3291
+ var GeneratedTaskSchema = S12.Struct({
3292
+ id: S12.String,
3293
+ title: S12.String,
3047
3294
  status: GeneratedTaskStatusSchema
3048
3295
  });
3049
- var GeneratedTaskListSchema = S11.Array(GeneratedTaskSchema);
3296
+ var GeneratedTaskListSchema = S12.Array(GeneratedTaskSchema);
3050
3297
  var STATUS_ICONS = {
3051
3298
  done: "[x]",
3052
3299
  in_progress: "[~]",
@@ -3105,15 +3352,15 @@ function parseTasksMd(content) {
3105
3352
 
3106
3353
  // src/domain/schemas/tui.ts
3107
3354
  init_esm_shims();
3108
- import { Schema as S12 } from "effect";
3109
- var ViewModeSchema = S12.Literal("logs", "tasks", "detail");
3110
- var LoopStatusSchema = S12.Literal(
3355
+ import { Schema as S13 } from "effect";
3356
+ var ViewModeSchema = S13.Literal("logs", "tasks", "detail");
3357
+ var LoopStatusSchema = S13.Literal(
3111
3358
  "idle",
3112
3359
  "running",
3113
3360
  "complete",
3114
3361
  "error"
3115
3362
  );
3116
- var ExecutionModeSchema = S12.Literal(
3363
+ var ExecutionModeSchema = S13.Literal(
3117
3364
  "idle",
3118
3365
  "discovery",
3119
3366
  "breakdown",
@@ -3123,98 +3370,566 @@ var ExecutionModeSchema = S12.Literal(
3123
3370
  "verifying",
3124
3371
  "reviewing"
3125
3372
  );
3126
- var TUIPhaseStatusSchema = S12.Literal(
3373
+ var TUIPhaseStatusSchema = S13.Literal(
3127
3374
  "pending",
3128
3375
  "in_progress",
3129
3376
  "done",
3130
3377
  "failed"
3131
3378
  );
3132
- var TUICriterionStatusSchema = S12.Literal(
3379
+ var TUICriterionStatusSchema = S13.Literal(
3133
3380
  "pending",
3134
3381
  "passed",
3135
3382
  "failed"
3136
3383
  );
3137
- var TUITaskStatusSchema = S12.Literal(
3384
+ var TUITaskStatusSchema = S13.Literal(
3138
3385
  "pending",
3139
3386
  "in_progress",
3140
3387
  "done",
3141
3388
  "failed"
3142
3389
  );
3143
- var TUIPhaseSchema = S12.Struct({
3144
- id: S12.String,
3145
- description: S12.String,
3390
+ var TUIPhaseSchema = S13.Struct({
3391
+ id: S13.String,
3392
+ description: S13.String,
3146
3393
  status: TUIPhaseStatusSchema,
3147
- startedAt: S12.optional(S12.Number),
3148
- completedAt: S12.optional(S12.Number)
3394
+ startedAt: S13.optional(S13.Number),
3395
+ completedAt: S13.optional(S13.Number)
3149
3396
  });
3150
- var TUICriterionSchema = S12.Struct({
3151
- id: S12.String,
3152
- description: S12.String,
3397
+ var TUICriterionSchema = S13.Struct({
3398
+ id: S13.String,
3399
+ description: S13.String,
3153
3400
  status: TUICriterionStatusSchema,
3154
- failureReason: S12.optional(S12.String)
3401
+ failureReason: S13.optional(S13.String)
3155
3402
  });
3156
- var TUITaskSchema = S12.Struct({
3157
- id: S12.String,
3158
- title: S12.String,
3403
+ var TUITaskSchema = S13.Struct({
3404
+ id: S13.String,
3405
+ title: S13.String,
3159
3406
  status: TUITaskStatusSchema,
3160
- phases: S12.Array(TUIPhaseSchema),
3161
- criteria: S12.Array(TUICriterionSchema),
3162
- startedAt: S12.optional(S12.Number),
3163
- completedAt: S12.optional(S12.Number)
3407
+ phases: S13.Array(TUIPhaseSchema),
3408
+ criteria: S13.Array(TUICriterionSchema),
3409
+ startedAt: S13.optional(S13.Number),
3410
+ completedAt: S13.optional(S13.Number)
3164
3411
  });
3165
- var TUIStateSchema = S12.Struct({
3412
+ var TUIStateSchema = S13.Struct({
3166
3413
  // Loop info
3167
- task: S12.String,
3168
- iteration: S12.Number,
3169
- maxIterations: S12.Number,
3414
+ task: S13.String,
3415
+ iteration: S13.Number,
3416
+ maxIterations: S13.Number,
3170
3417
  status: LoopStatusSchema,
3171
- startTime: S12.Number,
3418
+ startTime: S13.Number,
3172
3419
  // Discovery phase
3173
- discoveryInProgress: S12.Boolean,
3174
- discoveryCompleted: S12.Boolean,
3420
+ discoveryInProgress: S13.Boolean,
3421
+ discoveryCompleted: S13.Boolean,
3175
3422
  // Current activity
3176
3423
  executionMode: ExecutionModeSchema,
3177
- currentTool: S12.optional(S12.String),
3178
- currentTaskId: S12.optional(S12.String),
3424
+ currentTool: S13.optional(S13.String),
3425
+ currentTaskId: S13.optional(S13.String),
3179
3426
  // Output
3180
- outputLines: S12.Array(S12.String),
3181
- partialLine: S12.String,
3427
+ outputLines: S13.Array(S13.String),
3428
+ partialLine: S13.String,
3182
3429
  // Tasks
3183
- tasks: S12.Array(TUITaskSchema),
3430
+ tasks: S13.Array(TUITaskSchema),
3184
3431
  // Navigation
3185
3432
  viewMode: ViewModeSchema,
3186
- selectedTaskIndex: S12.Number,
3187
- scrollOffset: S12.Number,
3188
- userScrolled: S12.Boolean,
3433
+ selectedTaskIndex: S13.Number,
3434
+ scrollOffset: S13.Number,
3435
+ userScrolled: S13.Boolean,
3189
3436
  // Git
3190
- gitBranch: S12.optional(S12.String),
3191
- gitPushed: S12.Boolean,
3192
- prUrl: S12.optional(S12.String)
3437
+ gitBranch: S13.optional(S13.String),
3438
+ gitPushed: S13.Boolean,
3439
+ prUrl: S13.optional(S13.String)
3193
3440
  });
3194
3441
 
3442
+ // src/services/guardrails-store.ts
3443
+ init_esm_shims();
3444
+ import { Context as Context2 } from "effect";
3445
+ var GuardrailsStore = class extends Context2.Tag("@ferix/GuardrailsStore")() {
3446
+ };
3447
+
3448
+ // src/layers/guardrails/file-system.ts
3449
+ var PLANS_DIR = ".ferix/plans";
3450
+ function ensureDir(dirPath) {
3451
+ return Effect6.tryPromise({
3452
+ try: () => mkdir2(dirPath, { recursive: true }),
3453
+ catch: (error) => new GuardrailsStoreError({
3454
+ message: `Failed to create directory: ${dirPath}`,
3455
+ operation: "add",
3456
+ cause: error
3457
+ })
3458
+ }).pipe(Effect6.asVoid);
3459
+ }
3460
+ function getSessionDir(sessionId) {
3461
+ return join2(process.cwd(), PLANS_DIR, sessionId);
3462
+ }
3463
+ function getGuardrailsPath(sessionId) {
3464
+ return join2(getSessionDir(sessionId), "guardrails.json");
3465
+ }
3466
+ function serializeGuardrails(guardrails) {
3467
+ return JSON.stringify(guardrails, null, 2);
3468
+ }
3469
+ function deserializeGuardrails(json) {
3470
+ return Effect6.gen(function* () {
3471
+ const parsed = yield* Effect6.try({
3472
+ try: () => JSON.parse(json),
3473
+ catch: (error) => new GuardrailsStoreError({
3474
+ message: `Invalid JSON in guardrails file: ${String(error)}`,
3475
+ operation: "load",
3476
+ cause: error
3477
+ })
3478
+ });
3479
+ const validated = yield* decodeGuardrailsFile(parsed).pipe(
3480
+ Effect6.mapError(
3481
+ (error) => new GuardrailsStoreError({
3482
+ message: `Guardrails validation failed: ${String(error)}`,
3483
+ operation: "load",
3484
+ cause: error
3485
+ })
3486
+ )
3487
+ );
3488
+ return validated;
3489
+ });
3490
+ }
3491
+ function createEmptyGuardrails(sessionId, createdAt) {
3492
+ return {
3493
+ sessionId,
3494
+ createdAt,
3495
+ guardrails: []
3496
+ };
3497
+ }
3498
+ var make2 = {
3499
+ add: (sessionId, guardrail) => Effect6.gen(function* () {
3500
+ const sessionDir = getSessionDir(sessionId);
3501
+ yield* ensureDir(sessionDir);
3502
+ const guardrailsPath = getGuardrailsPath(sessionId);
3503
+ const existing = yield* Effect6.tryPromise({
3504
+ try: async () => {
3505
+ try {
3506
+ const content = await readFile(guardrailsPath, "utf-8");
3507
+ return content;
3508
+ } catch {
3509
+ return null;
3510
+ }
3511
+ },
3512
+ catch: (error) => new GuardrailsStoreError({
3513
+ message: `Failed to read guardrails file: ${guardrailsPath}`,
3514
+ operation: "add",
3515
+ cause: error
3516
+ })
3517
+ });
3518
+ let guardrails;
3519
+ if (existing) {
3520
+ guardrails = yield* deserializeGuardrails(existing).pipe(
3521
+ Effect6.mapError(
3522
+ (err) => new GuardrailsStoreError({
3523
+ message: err.message,
3524
+ operation: "add",
3525
+ cause: err
3526
+ })
3527
+ )
3528
+ );
3529
+ } else {
3530
+ const now = yield* DateTime.now;
3531
+ guardrails = createEmptyGuardrails(sessionId, DateTime.formatIso(now));
3532
+ }
3533
+ const updatedGuardrails = {
3534
+ ...guardrails,
3535
+ guardrails: [...guardrails.guardrails, guardrail]
3536
+ };
3537
+ yield* Effect6.tryPromise({
3538
+ try: () => writeFile(
3539
+ guardrailsPath,
3540
+ serializeGuardrails(updatedGuardrails),
3541
+ "utf-8"
3542
+ ),
3543
+ catch: (error) => new GuardrailsStoreError({
3544
+ message: `Failed to write guardrails file: ${guardrailsPath}`,
3545
+ operation: "add",
3546
+ cause: error
3547
+ })
3548
+ });
3549
+ }),
3550
+ load: (sessionId) => Effect6.gen(function* () {
3551
+ const guardrailsPath = getGuardrailsPath(sessionId);
3552
+ const content = yield* Effect6.tryPromise({
3553
+ try: async () => {
3554
+ try {
3555
+ return await readFile(guardrailsPath, "utf-8");
3556
+ } catch {
3557
+ return null;
3558
+ }
3559
+ },
3560
+ catch: (error) => new GuardrailsStoreError({
3561
+ message: `Failed to read guardrails file: ${guardrailsPath}`,
3562
+ operation: "load",
3563
+ cause: error
3564
+ })
3565
+ });
3566
+ if (content === null) {
3567
+ const now = yield* DateTime.now;
3568
+ return createEmptyGuardrails(sessionId, DateTime.formatIso(now));
3569
+ }
3570
+ return yield* deserializeGuardrails(content);
3571
+ }),
3572
+ getActive: (sessionId) => Effect6.gen(function* () {
3573
+ const guardrails = yield* make2.load(sessionId);
3574
+ return guardrails.guardrails;
3575
+ })
3576
+ };
3577
+ var Live3 = Layer3.succeed(GuardrailsStore, make2);
3578
+ var FileSystemGuardrails = {
3579
+ Live: Live3
3580
+ };
3581
+
3582
+ // src/layers/guardrails/memory.ts
3583
+ init_esm_shims();
3584
+ import { DateTime as DateTime2, Effect as Effect7, Layer as Layer4, Ref as Ref4 } from "effect";
3585
+ function createMemoryGuardrailsStore(stateRef) {
3586
+ return {
3587
+ add: (sessionId, guardrail) => Effect7.gen(function* () {
3588
+ const state = yield* Ref4.get(stateRef);
3589
+ let guardrails = state.get(sessionId);
3590
+ if (!guardrails) {
3591
+ const now = yield* DateTime2.now;
3592
+ guardrails = {
3593
+ sessionId,
3594
+ createdAt: DateTime2.formatIso(now),
3595
+ guardrails: []
3596
+ };
3597
+ }
3598
+ const updatedGuardrails = {
3599
+ ...guardrails,
3600
+ guardrails: [...guardrails.guardrails, guardrail]
3601
+ };
3602
+ state.set(sessionId, updatedGuardrails);
3603
+ yield* Ref4.set(stateRef, state);
3604
+ }),
3605
+ load: (sessionId) => Effect7.gen(function* () {
3606
+ const state = yield* Ref4.get(stateRef);
3607
+ const guardrails = state.get(sessionId);
3608
+ if (!guardrails) {
3609
+ const now = yield* DateTime2.now;
3610
+ return {
3611
+ sessionId,
3612
+ createdAt: DateTime2.formatIso(now),
3613
+ guardrails: []
3614
+ };
3615
+ }
3616
+ return guardrails;
3617
+ }),
3618
+ getActive: (sessionId) => Effect7.gen(function* () {
3619
+ const state = yield* Ref4.get(stateRef);
3620
+ const guardrails = state.get(sessionId);
3621
+ if (!guardrails) {
3622
+ return [];
3623
+ }
3624
+ return guardrails.guardrails;
3625
+ })
3626
+ };
3627
+ }
3628
+ function layer2() {
3629
+ return Layer4.effect(
3630
+ GuardrailsStore,
3631
+ Effect7.gen(function* () {
3632
+ const stateRef = yield* Ref4.make(/* @__PURE__ */ new Map());
3633
+ return createMemoryGuardrailsStore(stateRef);
3634
+ })
3635
+ );
3636
+ }
3637
+ var Live4 = layer2();
3638
+ var MemoryGuardrails = {
3639
+ Live: Live4,
3640
+ layer: layer2
3641
+ };
3642
+
3643
+ // src/layers/llm/claude-cli.ts
3644
+ init_esm_shims();
3645
+ import { spawn } from "child_process";
3646
+ import { Effect as Effect9, Layer as Layer5, Stream as Stream5 } from "effect";
3647
+
3648
+ // src/services/llm.ts
3649
+ init_esm_shims();
3650
+ import { Context as Context3 } from "effect";
3651
+ var LLM = class extends Context3.Tag("@ferix/LLM")() {
3652
+ };
3653
+
3654
+ // src/layers/llm/stream.ts
3655
+ init_esm_shims();
3656
+ import { createInterface } from "readline";
3657
+ import { Effect as Effect8, Stream as Stream4 } from "effect";
3658
+
3659
+ // src/layers/llm/parsers.ts
3660
+ init_esm_shims();
3661
+ function parseJsonLine(line) {
3662
+ if (!line.startsWith("{")) {
3663
+ return null;
3664
+ }
3665
+ try {
3666
+ return JSON.parse(line);
3667
+ } catch {
3668
+ return null;
3669
+ }
3670
+ }
3671
+ function isTextContent(json) {
3672
+ return typeof json === "object" && json !== null && "type" in json && typeof json.type === "string";
3673
+ }
3674
+ function isToolUse(json) {
3675
+ return typeof json === "object" && json !== null && "type" in json && "content_block" in json;
3676
+ }
3677
+ function extractText(json) {
3678
+ if (!isTextContent(json)) {
3679
+ return null;
3680
+ }
3681
+ if (json.type === "content_block_delta") {
3682
+ const delta = json;
3683
+ if (delta.delta?.type === "text_delta" && delta.delta.text) {
3684
+ return delta.delta.text;
3685
+ }
3686
+ }
3687
+ if (json.type === "assistant" && json.message?.content) {
3688
+ for (const block of json.message.content) {
3689
+ if (block.type === "text" && block.text) {
3690
+ return block.text;
3691
+ }
3692
+ }
3693
+ }
3694
+ return null;
3695
+ }
3696
+ function isToolInputDelta(json) {
3697
+ return typeof json === "object" && json !== null && "type" in json && json.type === "content_block_delta" && "delta" in json;
3698
+ }
3699
+ function extractToolInfo(json) {
3700
+ if (typeof json === "object" && json !== null && "type" in json && json.type === "content_block_stop") {
3701
+ return {
3702
+ type: "end",
3703
+ name: "unknown"
3704
+ };
3705
+ }
3706
+ if (!isToolUse(json)) {
3707
+ if (isToolInputDelta(json) && json.delta?.type === "input_json_delta" && json.delta.partial_json) {
3708
+ return {
3709
+ type: "input_delta",
3710
+ name: "",
3711
+ partialJson: json.delta.partial_json
3712
+ };
3713
+ }
3714
+ return null;
3715
+ }
3716
+ if (json.type === "content_block_start" && json.content_block?.type === "tool_use") {
3717
+ return {
3718
+ type: "start",
3719
+ name: json.content_block.name || "unknown"
3720
+ };
3721
+ }
3722
+ return null;
3723
+ }
3724
+ function safeParseJson(jsonStr) {
3725
+ try {
3726
+ return JSON.parse(jsonStr);
3727
+ } catch {
3728
+ return null;
3729
+ }
3730
+ }
3731
+ function unwrapStreamEvent(json) {
3732
+ if (typeof json === "object" && json !== null && "type" in json && json.type === "stream_event" && "event" in json) {
3733
+ return json.event;
3734
+ }
3735
+ return json;
3736
+ }
3737
+
3738
+ // src/layers/llm/stream.ts
3739
+ function handleToolEvent(toolInfo, toolState, emit) {
3740
+ if (toolInfo.type === "start") {
3741
+ toolState.currentTool = toolInfo.name;
3742
+ toolState.inputChunks.length = 0;
3743
+ emit.single({ _tag: "ToolStart", tool: toolInfo.name });
3744
+ return;
3745
+ }
3746
+ if (toolInfo.type === "input_delta" && toolInfo.partialJson) {
3747
+ toolState.inputChunks.push(toolInfo.partialJson);
3748
+ return;
3749
+ }
3750
+ if (toolInfo.type === "end" && toolState.currentTool) {
3751
+ const inputJson = toolState.inputChunks.join("");
3752
+ const input = safeParseJson(inputJson);
3753
+ if (input !== null) {
3754
+ emit.single({ _tag: "ToolUse", tool: toolState.currentTool, input });
3755
+ }
3756
+ emit.single({ _tag: "ToolEnd", tool: toolState.currentTool });
3757
+ toolState.currentTool = "";
3758
+ toolState.inputChunks.length = 0;
3759
+ }
3760
+ }
3761
+ function processJsonLine(json, outputChunks, toolState, emit) {
3762
+ const event = unwrapStreamEvent(json);
3763
+ const text = extractText(event);
3764
+ if (text) {
3765
+ outputChunks.push(text);
3766
+ emit.single({ _tag: "Text", text });
3767
+ return;
3768
+ }
3769
+ const toolInfo = extractToolInfo(event);
3770
+ if (toolInfo) {
3771
+ handleToolEvent(toolInfo, toolState, emit);
3772
+ }
3773
+ }
3774
+ function createEventStream(child) {
3775
+ return Stream4.async((emit) => {
3776
+ const outputChunks = [];
3777
+ const toolState = { currentTool: "", inputChunks: [] };
3778
+ const stdout = child.stdout;
3779
+ if (!stdout) {
3780
+ emit.fail(
3781
+ new LLMError({ message: "Failed to get stdout from child process" })
3782
+ );
3783
+ return Effect8.void;
3784
+ }
3785
+ const rl = createInterface({
3786
+ input: stdout,
3787
+ crlfDelay: Number.POSITIVE_INFINITY
3788
+ });
3789
+ rl.on("line", (line) => {
3790
+ const json = parseJsonLine(line);
3791
+ if (json) {
3792
+ processJsonLine(json, outputChunks, toolState, emit);
3793
+ }
3794
+ });
3795
+ child.stderr?.on("data", (data) => {
3796
+ const text = data.toString().trim();
3797
+ if (text) {
3798
+ emit.single({ _tag: "Text", text: `[stderr] ${text}` });
3799
+ }
3800
+ });
3801
+ child.on("close", (exitCode) => {
3802
+ if (exitCode !== 0) {
3803
+ emit.fail(
3804
+ new LLMError({
3805
+ message: `Claude CLI exited with code ${exitCode}`
3806
+ })
3807
+ );
3808
+ } else {
3809
+ const fullOutput = outputChunks.join("");
3810
+ emit.single({ _tag: "Done", output: fullOutput });
3811
+ emit.end();
3812
+ }
3813
+ });
3814
+ child.on("error", (error) => {
3815
+ emit.fail(
3816
+ new LLMError({
3817
+ message: error.message,
3818
+ cause: error
3819
+ })
3820
+ );
3821
+ });
3822
+ return Effect8.sync(() => {
3823
+ child.kill("SIGTERM");
3824
+ });
3825
+ });
3826
+ }
3827
+
3828
+ // src/layers/llm/claude-cli.ts
3829
+ var make3 = {
3830
+ execute: (prompt, options) => {
3831
+ return Stream5.unwrap(
3832
+ Effect9.sync(() => {
3833
+ const child = spawn(
3834
+ "claude",
3835
+ [
3836
+ "--permission-mode",
3837
+ "acceptEdits",
3838
+ "--output-format",
3839
+ "stream-json",
3840
+ "--verbose",
3841
+ "--include-partial-messages",
3842
+ "-p",
3843
+ prompt
3844
+ ],
3845
+ {
3846
+ stdio: ["inherit", "pipe", "pipe"],
3847
+ cwd: options?.cwd,
3848
+ env: {
3849
+ ...process.env,
3850
+ FORCE_COLOR: "1"
3851
+ }
3852
+ }
3853
+ );
3854
+ return createEventStream(child);
3855
+ })
3856
+ );
3857
+ }
3858
+ };
3859
+ var Live5 = Layer5.succeed(LLM, make3);
3860
+ var ClaudeCLI = {
3861
+ Live: Live5
3862
+ };
3863
+
3864
+ // src/layers/llm/mock.ts
3865
+ init_esm_shims();
3866
+ import { Effect as Effect10, Layer as Layer6, Schema as S14, Stream as Stream6 } from "effect";
3867
+ var MockLLMConfigSchema = S14.Struct({
3868
+ events: S14.Array(LLMEventSchema),
3869
+ delayMs: S14.optional(S14.Number)
3870
+ });
3871
+ function createMockLLM(config) {
3872
+ return {
3873
+ execute: (_prompt, _options) => {
3874
+ const baseStream = Stream6.fromIterable(config.events);
3875
+ if (config.delayMs !== void 0 && config.delayMs > 0) {
3876
+ const delay = config.delayMs;
3877
+ return baseStream.pipe(Stream6.tap(() => Effect10.sleep(delay)));
3878
+ }
3879
+ return baseStream;
3880
+ }
3881
+ };
3882
+ }
3883
+ var defaultMockEvents = [
3884
+ { _tag: "Text", text: "Processing task...\n" },
3885
+ { _tag: "ToolStart", tool: "Read" },
3886
+ { _tag: "ToolEnd", tool: "Read" },
3887
+ { _tag: "Text", text: "Task completed successfully.\n" },
3888
+ {
3889
+ _tag: "Done",
3890
+ output: "Processing task...\nTask completed successfully.\n"
3891
+ }
3892
+ ];
3893
+ var defaultMock = createMockLLM({ events: defaultMockEvents });
3894
+ var Live6 = Layer6.succeed(LLM, defaultMock);
3895
+ function layer3(config) {
3896
+ return Layer6.succeed(LLM, createMockLLM(config));
3897
+ }
3898
+ var Mock = {
3899
+ Live: Live6,
3900
+ layer: layer3,
3901
+ createMockLLM
3902
+ };
3903
+
3904
+ // src/layers/plan/file-system.ts
3905
+ init_esm_shims();
3906
+ import { access as access2, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3907
+ import { join as join3 } from "path";
3908
+ import { Effect as Effect11, Layer as Layer7 } from "effect";
3909
+
3195
3910
  // src/services/plan-store.ts
3196
3911
  init_esm_shims();
3197
- import { Context as Context2 } from "effect";
3198
- var PlanStore = class extends Context2.Tag("@ferix/PlanStore")() {
3912
+ import { Context as Context4 } from "effect";
3913
+ var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
3199
3914
  };
3200
3915
 
3201
3916
  // src/layers/plan/file-system.ts
3202
- var PLANS_DIR = ".ferix/plans";
3203
- function ensureDir(dirPath) {
3204
- return Effect7.tryPromise({
3205
- try: () => mkdir(dirPath, { recursive: true }),
3917
+ var PLANS_DIR2 = ".ferix/plans";
3918
+ function ensureDir2(dirPath) {
3919
+ return Effect11.tryPromise({
3920
+ try: () => mkdir3(dirPath, { recursive: true }),
3206
3921
  catch: (error) => new PlanStoreError({
3207
3922
  message: `Failed to create directory: ${dirPath}`,
3208
3923
  operation: "create",
3209
3924
  cause: error
3210
3925
  })
3211
- }).pipe(Effect7.asVoid);
3926
+ }).pipe(Effect11.asVoid);
3212
3927
  }
3213
- function getSessionDir(sessionId) {
3214
- return join(process.cwd(), PLANS_DIR, sessionId);
3928
+ function getSessionDir2(sessionId) {
3929
+ return join3(process.cwd(), PLANS_DIR2, sessionId);
3215
3930
  }
3216
3931
  function getPlanPath(sessionId, planId) {
3217
- return join(getSessionDir(sessionId), `${planId}.json`);
3932
+ return join3(getSessionDir2(sessionId), `${planId}.json`);
3218
3933
  }
3219
3934
  function generatePlanId(taskNumber) {
3220
3935
  return PlanId(`task-${taskNumber}`);
@@ -3223,8 +3938,8 @@ function serializePlan(plan) {
3223
3938
  return JSON.stringify(plan, null, 2);
3224
3939
  }
3225
3940
  function deserializePlan(json, planId) {
3226
- return Effect7.gen(function* () {
3227
- const parsed = yield* Effect7.try({
3941
+ return Effect11.gen(function* () {
3942
+ const parsed = yield* Effect11.try({
3228
3943
  try: () => JSON.parse(json),
3229
3944
  catch: (error) => new PlanStoreError({
3230
3945
  message: `Invalid JSON in plan file: ${String(error)}`,
@@ -3233,7 +3948,7 @@ function deserializePlan(json, planId) {
3233
3948
  })
3234
3949
  });
3235
3950
  const validated = yield* decodePlanData(parsed).pipe(
3236
- Effect7.mapError(
3951
+ Effect11.mapError(
3237
3952
  (error) => new PlanStoreError({
3238
3953
  message: `Plan validation failed: ${String(error)}`,
3239
3954
  operation: "load",
@@ -3247,11 +3962,11 @@ function deserializePlan(json, planId) {
3247
3962
  };
3248
3963
  });
3249
3964
  }
3250
- var make2 = {
3251
- create: (sessionId, plan) => Effect7.gen(function* () {
3252
- const sessionDir = getSessionDir(sessionId);
3253
- yield* ensureDir(sessionDir);
3254
- const existingPlans = yield* Effect7.tryPromise({
3965
+ var make4 = {
3966
+ create: (sessionId, plan) => Effect11.gen(function* () {
3967
+ const sessionDir = getSessionDir2(sessionId);
3968
+ yield* ensureDir2(sessionDir);
3969
+ const existingPlans = yield* Effect11.tryPromise({
3255
3970
  try: async () => {
3256
3971
  try {
3257
3972
  const files = await readdir(sessionDir);
@@ -3268,8 +3983,8 @@ var make2 = {
3268
3983
  const planId = generatePlanId(existingPlans + 1);
3269
3984
  const planPath = getPlanPath(sessionId, planId);
3270
3985
  const fullPlan = { ...plan, id: planId };
3271
- yield* Effect7.tryPromise({
3272
- try: () => writeFile(planPath, serializePlan(fullPlan), "utf-8"),
3986
+ yield* Effect11.tryPromise({
3987
+ try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
3273
3988
  catch: (error) => new PlanStoreError({
3274
3989
  message: `Failed to write plan file: ${planPath}`,
3275
3990
  operation: "create",
@@ -3278,11 +3993,11 @@ var make2 = {
3278
3993
  });
3279
3994
  return planId;
3280
3995
  }),
3281
- load: (planId, sessionId) => Effect7.gen(function* () {
3996
+ load: (planId, sessionId) => Effect11.gen(function* () {
3282
3997
  if (sessionId) {
3283
3998
  const planPath = getPlanPath(sessionId, planId);
3284
- const content = yield* Effect7.tryPromise({
3285
- try: () => readFile(planPath, "utf-8"),
3999
+ const content = yield* Effect11.tryPromise({
4000
+ try: () => readFile2(planPath, "utf-8"),
3286
4001
  catch: (error) => new PlanStoreError({
3287
4002
  message: `Failed to read plan file: ${planPath}`,
3288
4003
  operation: "load",
@@ -3291,9 +4006,9 @@ var make2 = {
3291
4006
  });
3292
4007
  return yield* deserializePlan(content, planId);
3293
4008
  }
3294
- const sessionDirs = yield* Effect7.tryPromise({
4009
+ const sessionDirs = yield* Effect11.tryPromise({
3295
4010
  try: async () => {
3296
- const plansDir = join(process.cwd(), PLANS_DIR);
4011
+ const plansDir = join3(process.cwd(), PLANS_DIR2);
3297
4012
  const dirs = await readdir(plansDir);
3298
4013
  return dirs;
3299
4014
  },
@@ -3305,19 +4020,19 @@ var make2 = {
3305
4020
  });
3306
4021
  for (const sid of sessionDirs) {
3307
4022
  const planPath = getPlanPath(sid, planId);
3308
- const exists = yield* Effect7.tryPromise({
4023
+ const exists = yield* Effect11.tryPromise({
3309
4024
  try: async () => {
3310
- await access(planPath);
4025
+ await access2(planPath);
3311
4026
  return true;
3312
4027
  },
3313
4028
  catch: () => new PlanStoreError({
3314
4029
  message: "File not found",
3315
4030
  operation: "load"
3316
4031
  })
3317
- }).pipe(Effect7.orElseSucceed(() => false));
4032
+ }).pipe(Effect11.orElseSucceed(() => false));
3318
4033
  if (exists) {
3319
- const content = yield* Effect7.tryPromise({
3320
- try: () => readFile(planPath, "utf-8"),
4034
+ const content = yield* Effect11.tryPromise({
4035
+ try: () => readFile2(planPath, "utf-8"),
3321
4036
  catch: (error) => new PlanStoreError({
3322
4037
  message: `Failed to read plan file: ${planPath}`,
3323
4038
  operation: "load",
@@ -3327,17 +4042,17 @@ var make2 = {
3327
4042
  return yield* deserializePlan(content, planId);
3328
4043
  }
3329
4044
  }
3330
- return yield* Effect7.fail(
4045
+ return yield* Effect11.fail(
3331
4046
  new PlanStoreError({
3332
4047
  message: `Plan not found: ${planId}`,
3333
4048
  operation: "load"
3334
4049
  })
3335
4050
  );
3336
4051
  }),
3337
- update: (planId, plan) => Effect7.gen(function* () {
4052
+ update: (planId, plan) => Effect11.gen(function* () {
3338
4053
  const planPath = getPlanPath(plan.sessionId, planId);
3339
- yield* Effect7.tryPromise({
3340
- try: () => writeFile(planPath, serializePlan(plan), "utf-8"),
4054
+ yield* Effect11.tryPromise({
4055
+ try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
3341
4056
  catch: (error) => new PlanStoreError({
3342
4057
  message: `Failed to update plan file: ${planPath}`,
3343
4058
  operation: "update",
@@ -3345,9 +4060,9 @@ var make2 = {
3345
4060
  })
3346
4061
  });
3347
4062
  }),
3348
- list: (sessionId) => Effect7.gen(function* () {
3349
- const sessionDir = getSessionDir(sessionId);
3350
- const files = yield* Effect7.tryPromise({
4063
+ list: (sessionId) => Effect11.gen(function* () {
4064
+ const sessionDir = getSessionDir2(sessionId);
4065
+ const files = yield* Effect11.tryPromise({
3351
4066
  try: async () => {
3352
4067
  try {
3353
4068
  return await readdir(sessionDir);
@@ -3364,18 +4079,18 @@ var make2 = {
3364
4079
  return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
3365
4080
  })
3366
4081
  };
3367
- var Live3 = Layer3.succeed(PlanStore, make2);
4082
+ var Live7 = Layer7.succeed(PlanStore, make4);
3368
4083
  var FileSystemPlan = {
3369
- Live: Live3
4084
+ Live: Live7
3370
4085
  };
3371
4086
 
3372
4087
  // src/layers/plan/memory.ts
3373
4088
  init_esm_shims();
3374
- import { Effect as Effect8, Layer as Layer4, Ref as Ref3 } from "effect";
4089
+ import { Effect as Effect12, Layer as Layer8, Ref as Ref5 } from "effect";
3375
4090
  function createMemoryPlanStore(stateRef) {
3376
4091
  return {
3377
- create: (sessionId, plan) => Effect8.gen(function* () {
3378
- const state = yield* Ref3.get(stateRef);
4092
+ create: (sessionId, plan) => Effect12.gen(function* () {
4093
+ const state = yield* Ref5.get(stateRef);
3379
4094
  if (!state.has(sessionId)) {
3380
4095
  state.set(sessionId, /* @__PURE__ */ new Map());
3381
4096
  }
@@ -3386,18 +4101,18 @@ function createMemoryPlanStore(stateRef) {
3386
4101
  const planId = PlanId(`task-${sessionPlans.size + 1}`);
3387
4102
  const fullPlan = { ...plan, id: planId };
3388
4103
  sessionPlans.set(planId, fullPlan);
3389
- yield* Ref3.set(stateRef, state);
4104
+ yield* Ref5.set(stateRef, state);
3390
4105
  return planId;
3391
4106
  }),
3392
- load: (planId, sessionId) => Effect8.gen(function* () {
3393
- const state = yield* Ref3.get(stateRef);
4107
+ load: (planId, sessionId) => Effect12.gen(function* () {
4108
+ const state = yield* Ref5.get(stateRef);
3394
4109
  if (sessionId) {
3395
4110
  const sessionPlans = state.get(sessionId);
3396
4111
  const plan = sessionPlans?.get(planId);
3397
4112
  if (plan) {
3398
4113
  return plan;
3399
4114
  }
3400
- return yield* Effect8.fail(
4115
+ return yield* Effect12.fail(
3401
4116
  new PlanStoreError({
3402
4117
  message: `Plan not found: ${planId}`,
3403
4118
  operation: "load"
@@ -3410,18 +4125,18 @@ function createMemoryPlanStore(stateRef) {
3410
4125
  return plan;
3411
4126
  }
3412
4127
  }
3413
- return yield* Effect8.fail(
4128
+ return yield* Effect12.fail(
3414
4129
  new PlanStoreError({
3415
4130
  message: `Plan not found: ${planId}`,
3416
4131
  operation: "load"
3417
4132
  })
3418
4133
  );
3419
4134
  }),
3420
- update: (planId, plan) => Effect8.gen(function* () {
3421
- const state = yield* Ref3.get(stateRef);
4135
+ update: (planId, plan) => Effect12.gen(function* () {
4136
+ const state = yield* Ref5.get(stateRef);
3422
4137
  const sessionPlans = state.get(plan.sessionId);
3423
4138
  if (!sessionPlans) {
3424
- return yield* Effect8.fail(
4139
+ return yield* Effect12.fail(
3425
4140
  new PlanStoreError({
3426
4141
  message: `Session not found: ${plan.sessionId}`,
3427
4142
  operation: "update"
@@ -3429,10 +4144,10 @@ function createMemoryPlanStore(stateRef) {
3429
4144
  );
3430
4145
  }
3431
4146
  sessionPlans.set(planId, plan);
3432
- yield* Ref3.set(stateRef, state);
4147
+ yield* Ref5.set(stateRef, state);
3433
4148
  }),
3434
- list: (sessionId) => Effect8.gen(function* () {
3435
- const state = yield* Ref3.get(stateRef);
4149
+ list: (sessionId) => Effect12.gen(function* () {
4150
+ const state = yield* Ref5.get(stateRef);
3436
4151
  const sessionPlans = state.get(sessionId);
3437
4152
  if (!sessionPlans) {
3438
4153
  return [];
@@ -3441,32 +4156,236 @@ function createMemoryPlanStore(stateRef) {
3441
4156
  })
3442
4157
  };
3443
4158
  }
3444
- function layer2() {
3445
- return Layer4.effect(
4159
+ function layer4() {
4160
+ return Layer8.effect(
3446
4161
  PlanStore,
3447
- Effect8.gen(function* () {
3448
- const stateRef = yield* Ref3.make(/* @__PURE__ */ new Map());
4162
+ Effect12.gen(function* () {
4163
+ const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
3449
4164
  return createMemoryPlanStore(stateRef);
3450
4165
  })
3451
4166
  );
3452
4167
  }
3453
- var Live4 = layer2();
4168
+ var Live8 = layer4();
3454
4169
  var MemoryPlan = {
3455
- Live: Live4,
3456
- layer: layer2
4170
+ Live: Live8,
4171
+ layer: layer4
4172
+ };
4173
+
4174
+ // src/layers/progress/file-system.ts
4175
+ init_esm_shims();
4176
+ import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
4177
+ import { join as join4 } from "path";
4178
+ import { DateTime as DateTime3, Effect as Effect13, Layer as Layer9 } from "effect";
4179
+
4180
+ // src/services/progress-store.ts
4181
+ init_esm_shims();
4182
+ import { Context as Context5 } from "effect";
4183
+ var ProgressStore = class extends Context5.Tag("@ferix/ProgressStore")() {
4184
+ };
4185
+
4186
+ // src/layers/progress/file-system.ts
4187
+ var PLANS_DIR3 = ".ferix/plans";
4188
+ function ensureDir3(dirPath) {
4189
+ return Effect13.tryPromise({
4190
+ try: () => mkdir4(dirPath, { recursive: true }),
4191
+ catch: (error) => new ProgressStoreError({
4192
+ message: `Failed to create directory: ${dirPath}`,
4193
+ operation: "append",
4194
+ cause: error
4195
+ })
4196
+ }).pipe(Effect13.asVoid);
4197
+ }
4198
+ function getSessionDir3(sessionId) {
4199
+ return join4(process.cwd(), PLANS_DIR3, sessionId);
4200
+ }
4201
+ function getProgressPath(sessionId) {
4202
+ return join4(getSessionDir3(sessionId), "progress.json");
4203
+ }
4204
+ function serializeProgress(progress) {
4205
+ return JSON.stringify(progress, null, 2);
4206
+ }
4207
+ function deserializeProgress(json) {
4208
+ return Effect13.gen(function* () {
4209
+ const parsed = yield* Effect13.try({
4210
+ try: () => JSON.parse(json),
4211
+ catch: (error) => new ProgressStoreError({
4212
+ message: `Invalid JSON in progress file: ${String(error)}`,
4213
+ operation: "load",
4214
+ cause: error
4215
+ })
4216
+ });
4217
+ const validated = yield* decodeProgressFile(parsed).pipe(
4218
+ Effect13.mapError(
4219
+ (error) => new ProgressStoreError({
4220
+ message: `Progress validation failed: ${String(error)}`,
4221
+ operation: "load",
4222
+ cause: error
4223
+ })
4224
+ )
4225
+ );
4226
+ return validated;
4227
+ });
4228
+ }
4229
+ function createEmptyProgress(sessionId, createdAt) {
4230
+ return {
4231
+ sessionId,
4232
+ createdAt,
4233
+ entries: []
4234
+ };
4235
+ }
4236
+ var make5 = {
4237
+ append: (sessionId, entry) => Effect13.gen(function* () {
4238
+ const sessionDir = getSessionDir3(sessionId);
4239
+ yield* ensureDir3(sessionDir);
4240
+ const progressPath = getProgressPath(sessionId);
4241
+ const existing = yield* Effect13.tryPromise({
4242
+ try: async () => {
4243
+ try {
4244
+ const content = await readFile3(progressPath, "utf-8");
4245
+ return content;
4246
+ } catch {
4247
+ return null;
4248
+ }
4249
+ },
4250
+ catch: (error) => new ProgressStoreError({
4251
+ message: `Failed to read progress file: ${progressPath}`,
4252
+ operation: "append",
4253
+ cause: error
4254
+ })
4255
+ });
4256
+ let progress;
4257
+ if (existing) {
4258
+ progress = yield* deserializeProgress(existing).pipe(
4259
+ Effect13.mapError(
4260
+ (err) => new ProgressStoreError({
4261
+ message: err.message,
4262
+ operation: "append",
4263
+ cause: err
4264
+ })
4265
+ )
4266
+ );
4267
+ } else {
4268
+ const now = yield* DateTime3.now;
4269
+ progress = createEmptyProgress(sessionId, DateTime3.formatIso(now));
4270
+ }
4271
+ const updatedProgress = {
4272
+ ...progress,
4273
+ entries: [...progress.entries, entry]
4274
+ };
4275
+ yield* Effect13.tryPromise({
4276
+ try: () => writeFile3(progressPath, serializeProgress(updatedProgress), "utf-8"),
4277
+ catch: (error) => new ProgressStoreError({
4278
+ message: `Failed to write progress file: ${progressPath}`,
4279
+ operation: "append",
4280
+ cause: error
4281
+ })
4282
+ });
4283
+ }),
4284
+ load: (sessionId) => Effect13.gen(function* () {
4285
+ const progressPath = getProgressPath(sessionId);
4286
+ const content = yield* Effect13.tryPromise({
4287
+ try: async () => {
4288
+ try {
4289
+ return await readFile3(progressPath, "utf-8");
4290
+ } catch {
4291
+ return null;
4292
+ }
4293
+ },
4294
+ catch: (error) => new ProgressStoreError({
4295
+ message: `Failed to read progress file: ${progressPath}`,
4296
+ operation: "load",
4297
+ cause: error
4298
+ })
4299
+ });
4300
+ if (content === null) {
4301
+ const now = yield* DateTime3.now;
4302
+ return createEmptyProgress(sessionId, DateTime3.formatIso(now));
4303
+ }
4304
+ return yield* deserializeProgress(content);
4305
+ }),
4306
+ getRecent: (sessionId, count) => Effect13.gen(function* () {
4307
+ const progress = yield* make5.load(sessionId);
4308
+ const entries = progress.entries;
4309
+ return entries.slice(-count);
4310
+ })
4311
+ };
4312
+ var Live9 = Layer9.succeed(ProgressStore, make5);
4313
+ var FileSystemProgress = {
4314
+ Live: Live9
4315
+ };
4316
+
4317
+ // src/layers/progress/memory.ts
4318
+ init_esm_shims();
4319
+ import { DateTime as DateTime4, Effect as Effect14, Layer as Layer10, Ref as Ref6 } from "effect";
4320
+ function createMemoryProgressStore(stateRef) {
4321
+ return {
4322
+ append: (sessionId, entry) => Effect14.gen(function* () {
4323
+ const state = yield* Ref6.get(stateRef);
4324
+ let progress = state.get(sessionId);
4325
+ if (!progress) {
4326
+ const now = yield* DateTime4.now;
4327
+ progress = {
4328
+ sessionId,
4329
+ createdAt: DateTime4.formatIso(now),
4330
+ entries: []
4331
+ };
4332
+ }
4333
+ const updatedProgress = {
4334
+ ...progress,
4335
+ entries: [...progress.entries, entry]
4336
+ };
4337
+ state.set(sessionId, updatedProgress);
4338
+ yield* Ref6.set(stateRef, state);
4339
+ }),
4340
+ load: (sessionId) => Effect14.gen(function* () {
4341
+ const state = yield* Ref6.get(stateRef);
4342
+ const progress = state.get(sessionId);
4343
+ if (!progress) {
4344
+ const now = yield* DateTime4.now;
4345
+ return {
4346
+ sessionId,
4347
+ createdAt: DateTime4.formatIso(now),
4348
+ entries: []
4349
+ };
4350
+ }
4351
+ return progress;
4352
+ }),
4353
+ getRecent: (sessionId, count) => Effect14.gen(function* () {
4354
+ const state = yield* Ref6.get(stateRef);
4355
+ const progress = state.get(sessionId);
4356
+ if (!progress) {
4357
+ return [];
4358
+ }
4359
+ return progress.entries.slice(-count);
4360
+ })
4361
+ };
4362
+ }
4363
+ function layer5() {
4364
+ return Layer10.effect(
4365
+ ProgressStore,
4366
+ Effect14.gen(function* () {
4367
+ const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
4368
+ return createMemoryProgressStore(stateRef);
4369
+ })
4370
+ );
4371
+ }
4372
+ var Live10 = layer5();
4373
+ var MemoryProgress = {
4374
+ Live: Live10,
4375
+ layer: layer5
3457
4376
  };
3458
4377
 
3459
4378
  // src/layers/session/file-system.ts
3460
4379
  init_esm_shims();
3461
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
3462
- import { join as join2 } from "path";
3463
- import { DateTime, Effect as Effect9, Layer as Layer5 } from "effect";
4380
+ import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
4381
+ import { join as join5 } from "path";
4382
+ import { DateTime as DateTime5, Effect as Effect15, Layer as Layer11 } from "effect";
3464
4383
  import { humanId } from "human-id";
3465
4384
 
3466
4385
  // src/services/session-store.ts
3467
4386
  init_esm_shims();
3468
- import { Context as Context3 } from "effect";
3469
- var SessionStore = class extends Context3.Tag("@ferix/SessionStore")() {
4387
+ import { Context as Context6 } from "effect";
4388
+ var SessionStore = class extends Context6.Tag("@ferix/SessionStore")() {
3470
4389
  };
3471
4390
 
3472
4391
  // src/layers/session/file-system.ts
@@ -3475,25 +4394,25 @@ function generateSessionId(timestampMs) {
3475
4394
  const id = humanId({ separator: "-", capitalize: false });
3476
4395
  return `${id}-${timestampMs}`;
3477
4396
  }
3478
- function ensureDir2(dirPath) {
3479
- return Effect9.tryPromise({
3480
- try: () => mkdir2(dirPath, { recursive: true }),
4397
+ function ensureDir4(dirPath) {
4398
+ return Effect15.tryPromise({
4399
+ try: () => mkdir5(dirPath, { recursive: true }),
3481
4400
  catch: (error) => new SessionStoreError({
3482
4401
  message: `Failed to create directory: ${dirPath}`,
3483
4402
  operation: "create",
3484
4403
  cause: error
3485
4404
  })
3486
- }).pipe(Effect9.asVoid);
4405
+ }).pipe(Effect15.asVoid);
3487
4406
  }
3488
4407
  function getSessionPath(sessionId) {
3489
- return join2(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
4408
+ return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
3490
4409
  }
3491
4410
  function serializeSession(session) {
3492
4411
  return JSON.stringify(session, null, 2);
3493
4412
  }
3494
4413
  function deserializeSession(json) {
3495
- return Effect9.gen(function* () {
3496
- const parsed = yield* Effect9.try({
4414
+ return Effect15.gen(function* () {
4415
+ const parsed = yield* Effect15.try({
3497
4416
  try: () => JSON.parse(json),
3498
4417
  catch: (error) => new SessionStoreError({
3499
4418
  message: `Invalid JSON in session file: ${String(error)}`,
@@ -3502,7 +4421,7 @@ function deserializeSession(json) {
3502
4421
  })
3503
4422
  });
3504
4423
  const validated = yield* decodeSession(parsed).pipe(
3505
- Effect9.mapError(
4424
+ Effect15.mapError(
3506
4425
  (error) => new SessionStoreError({
3507
4426
  message: `Session validation failed: ${String(error)}`,
3508
4427
  operation: "get",
@@ -3513,23 +4432,23 @@ function deserializeSession(json) {
3513
4432
  return validated;
3514
4433
  });
3515
4434
  }
3516
- var make3 = {
3517
- create: (originalTask) => Effect9.gen(function* () {
3518
- const sessionsDir = join2(process.cwd(), SESSIONS_DIR);
3519
- yield* ensureDir2(sessionsDir);
3520
- const now = yield* DateTime.now;
3521
- const timestampMs = DateTime.toEpochMillis(now);
4435
+ var make6 = {
4436
+ create: (originalTask) => Effect15.gen(function* () {
4437
+ const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
4438
+ yield* ensureDir4(sessionsDir);
4439
+ const now = yield* DateTime5.now;
4440
+ const timestampMs = DateTime5.toEpochMillis(now);
3522
4441
  const sessionId = generateSessionId(timestampMs);
3523
4442
  const session = {
3524
4443
  id: sessionId,
3525
- createdAt: DateTime.formatIso(now),
4444
+ createdAt: DateTime5.formatIso(now),
3526
4445
  status: "active",
3527
4446
  originalTask,
3528
4447
  completedTasks: []
3529
4448
  };
3530
4449
  const sessionPath = getSessionPath(sessionId);
3531
- yield* Effect9.tryPromise({
3532
- try: () => writeFile2(sessionPath, serializeSession(session), "utf-8"),
4450
+ yield* Effect15.tryPromise({
4451
+ try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
3533
4452
  catch: (error) => new SessionStoreError({
3534
4453
  message: `Failed to write session file: ${sessionPath}`,
3535
4454
  operation: "create",
@@ -3538,10 +4457,10 @@ var make3 = {
3538
4457
  });
3539
4458
  return session;
3540
4459
  }),
3541
- get: (sessionId) => Effect9.gen(function* () {
4460
+ get: (sessionId) => Effect15.gen(function* () {
3542
4461
  const sessionPath = getSessionPath(sessionId);
3543
- const content = yield* Effect9.tryPromise({
3544
- try: () => readFile2(sessionPath, "utf-8"),
4462
+ const content = yield* Effect15.tryPromise({
4463
+ try: () => readFile4(sessionPath, "utf-8"),
3545
4464
  catch: (error) => new SessionStoreError({
3546
4465
  message: `Failed to read session file: ${sessionPath}`,
3547
4466
  operation: "get",
@@ -3550,10 +4469,10 @@ var make3 = {
3550
4469
  });
3551
4470
  return yield* deserializeSession(content);
3552
4471
  }),
3553
- update: (sessionId, session) => Effect9.gen(function* () {
4472
+ update: (sessionId, session) => Effect15.gen(function* () {
3554
4473
  const sessionPath = getSessionPath(sessionId);
3555
- yield* Effect9.tryPromise({
3556
- try: () => writeFile2(sessionPath, serializeSession(session), "utf-8"),
4474
+ yield* Effect15.tryPromise({
4475
+ try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
3557
4476
  catch: (error) => new SessionStoreError({
3558
4477
  message: `Failed to update session file: ${sessionPath}`,
3559
4478
  operation: "update",
@@ -3562,37 +4481,37 @@ var make3 = {
3562
4481
  });
3563
4482
  })
3564
4483
  };
3565
- var Live5 = Layer5.succeed(SessionStore, make3);
4484
+ var Live11 = Layer11.succeed(SessionStore, make6);
3566
4485
  var FileSystemSession = {
3567
- Live: Live5
4486
+ Live: Live11
3568
4487
  };
3569
4488
 
3570
4489
  // src/layers/session/memory.ts
3571
4490
  init_esm_shims();
3572
- import { DateTime as DateTime2, Effect as Effect10, Layer as Layer6, Ref as Ref4 } from "effect";
4491
+ import { DateTime as DateTime6, Effect as Effect16, Layer as Layer12, Ref as Ref7 } from "effect";
3573
4492
  function createMemorySessionStore(stateRef, counterRef) {
3574
4493
  return {
3575
- create: (originalTask) => Effect10.gen(function* () {
3576
- const state = yield* Ref4.get(stateRef);
3577
- const counter = yield* Ref4.updateAndGet(counterRef, (n) => n + 1);
4494
+ create: (originalTask) => Effect16.gen(function* () {
4495
+ const state = yield* Ref7.get(stateRef);
4496
+ const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
3578
4497
  const sessionId = `test-session-${counter}`;
3579
- const now = yield* DateTime2.now;
4498
+ const now = yield* DateTime6.now;
3580
4499
  const session = {
3581
4500
  id: sessionId,
3582
- createdAt: DateTime2.formatIso(now),
4501
+ createdAt: DateTime6.formatIso(now),
3583
4502
  status: "active",
3584
4503
  originalTask,
3585
4504
  completedTasks: []
3586
4505
  };
3587
4506
  state.set(sessionId, session);
3588
- yield* Ref4.set(stateRef, state);
4507
+ yield* Ref7.set(stateRef, state);
3589
4508
  return session;
3590
4509
  }),
3591
- get: (sessionId) => Effect10.gen(function* () {
3592
- const state = yield* Ref4.get(stateRef);
4510
+ get: (sessionId) => Effect16.gen(function* () {
4511
+ const state = yield* Ref7.get(stateRef);
3593
4512
  const session = state.get(sessionId);
3594
4513
  if (!session) {
3595
- return yield* Effect10.fail(
4514
+ return yield* Effect16.fail(
3596
4515
  new SessionStoreError({
3597
4516
  message: `Session not found: ${sessionId}`,
3598
4517
  operation: "get"
@@ -3601,10 +4520,10 @@ function createMemorySessionStore(stateRef, counterRef) {
3601
4520
  }
3602
4521
  return session;
3603
4522
  }),
3604
- update: (sessionId, session) => Effect10.gen(function* () {
3605
- const state = yield* Ref4.get(stateRef);
4523
+ update: (sessionId, session) => Effect16.gen(function* () {
4524
+ const state = yield* Ref7.get(stateRef);
3606
4525
  if (!state.has(sessionId)) {
3607
- return yield* Effect10.fail(
4526
+ return yield* Effect16.fail(
3608
4527
  new SessionStoreError({
3609
4528
  message: `Session not found: ${sessionId}`,
3610
4529
  operation: "update"
@@ -3612,34 +4531,34 @@ function createMemorySessionStore(stateRef, counterRef) {
3612
4531
  );
3613
4532
  }
3614
4533
  state.set(sessionId, session);
3615
- yield* Ref4.set(stateRef, state);
4534
+ yield* Ref7.set(stateRef, state);
3616
4535
  })
3617
4536
  };
3618
4537
  }
3619
- function layer3() {
3620
- return Layer6.effect(
4538
+ function layer6() {
4539
+ return Layer12.effect(
3621
4540
  SessionStore,
3622
- Effect10.gen(function* () {
3623
- const stateRef = yield* Ref4.make(/* @__PURE__ */ new Map());
3624
- const counterRef = yield* Ref4.make(0);
4541
+ Effect16.gen(function* () {
4542
+ const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
4543
+ const counterRef = yield* Ref7.make(0);
3625
4544
  return createMemorySessionStore(stateRef, counterRef);
3626
4545
  })
3627
4546
  );
3628
4547
  }
3629
- var Live6 = layer3();
4548
+ var Live12 = layer6();
3630
4549
  var MemorySession = {
3631
- Live: Live6,
3632
- layer: layer3
4550
+ Live: Live12,
4551
+ layer: layer6
3633
4552
  };
3634
4553
 
3635
4554
  // src/layers/signal/ferix-parser.ts
3636
4555
  init_esm_shims();
3637
- import { Effect as Effect11, Layer as Layer7, Ref as Ref5 } from "effect";
4556
+ import { Effect as Effect17, Layer as Layer13, Ref as Ref8 } from "effect";
3638
4557
 
3639
4558
  // src/services/signal-parser.ts
3640
4559
  init_esm_shims();
3641
- import { Context as Context4 } from "effect";
3642
- var SignalParser = class extends Context4.Tag("@ferix/SignalParser")() {
4560
+ import { Context as Context7 } from "effect";
4561
+ var SignalParser = class extends Context7.Tag("@ferix/SignalParser")() {
3643
4562
  };
3644
4563
 
3645
4564
  // src/layers/signal/specs/index.ts
@@ -3647,7 +4566,7 @@ init_esm_shims();
3647
4566
 
3648
4567
  // src/layers/signal/specs/check.ts
3649
4568
  init_esm_shims();
3650
- import { Schema as S13 } from "effect";
4569
+ import { Schema as S15 } from "effect";
3651
4570
 
3652
4571
  // src/layers/signal/specs/registry.ts
3653
4572
  init_esm_shims();
@@ -3704,7 +4623,7 @@ var checkPassedSpec = {
3704
4623
  parse: (text) => {
3705
4624
  if (CHECK_PASSED.test(text)) {
3706
4625
  const raw = { _tag: "CheckPassed" };
3707
- const result = S13.decodeUnknownEither(CheckPassedSignalSchema)(raw);
4626
+ const result = S15.decodeUnknownEither(CheckPassedSignalSchema)(raw);
3708
4627
  if (result._tag === "Right") {
3709
4628
  return [result.right];
3710
4629
  }
@@ -3720,7 +4639,7 @@ var checkFailedSpec = {
3720
4639
  parse: (text) => {
3721
4640
  if (CHECK_FAILED.test(text)) {
3722
4641
  const raw = { _tag: "CheckFailed" };
3723
- const result = S13.decodeUnknownEither(CheckFailedSignalSchema)(raw);
4642
+ const result = S15.decodeUnknownEither(CheckFailedSignalSchema)(raw);
3724
4643
  if (result._tag === "Right") {
3725
4644
  return [result.right];
3726
4645
  }
@@ -3734,7 +4653,7 @@ signalSpecRegistry.register(checkFailedSpec);
3734
4653
 
3735
4654
  // src/layers/signal/specs/criteria.ts
3736
4655
  init_esm_shims();
3737
- import { Schema as S14 } from "effect";
4656
+ import { Schema as S16 } from "effect";
3738
4657
  var CRITERIA_BLOCK = /<ferix:criteria task="(\d+)">([\s\S]*?)<\/ferix:criteria>/g;
3739
4658
  var CRITERION = /<criterion id="([^"]+)">([^<]+)<\/criterion>/g;
3740
4659
  var CRITERION_PASSED = /<ferix:criterion-passed id="([\d.c]+)"\/>/g;
@@ -3765,7 +4684,7 @@ var criteriaDefinedSpec = {
3765
4684
  taskId: match[1],
3766
4685
  criteria
3767
4686
  };
3768
- const result = S14.decodeUnknownEither(CriteriaDefinedSignalSchema)(raw);
4687
+ const result = S16.decodeUnknownEither(CriteriaDefinedSignalSchema)(raw);
3769
4688
  if (result._tag === "Right") {
3770
4689
  signals.push(result.right);
3771
4690
  }
@@ -3784,7 +4703,7 @@ var criterionPassedSpec = {
3784
4703
  for (const m of text.matchAll(resetRegex(CRITERION_PASSED))) {
3785
4704
  if (m[1]) {
3786
4705
  const raw = { _tag: "CriterionPassed", criterionId: m[1] };
3787
- const result = S14.decodeUnknownEither(CriterionPassedSignalSchema)(raw);
4706
+ const result = S16.decodeUnknownEither(CriterionPassedSignalSchema)(raw);
3788
4707
  if (result._tag === "Right") {
3789
4708
  signals.push(result.right);
3790
4709
  }
@@ -3807,7 +4726,7 @@ var criterionFailedSpec = {
3807
4726
  criterionId: m[1],
3808
4727
  reason: m[2] || "Unknown reason"
3809
4728
  };
3810
- const result = S14.decodeUnknownEither(CriterionFailedSignalSchema)(raw);
4729
+ const result = S16.decodeUnknownEither(CriterionFailedSignalSchema)(raw);
3811
4730
  if (result._tag === "Right") {
3812
4731
  signals.push(result.right);
3813
4732
  }
@@ -3821,9 +4740,88 @@ signalSpecRegistry.register(criteriaDefinedSpec);
3821
4740
  signalSpecRegistry.register(criterionPassedSpec);
3822
4741
  signalSpecRegistry.register(criterionFailedSpec);
3823
4742
 
4743
+ // src/layers/signal/specs/guardrail.ts
4744
+ init_esm_shims();
4745
+ import { Schema as S17 } from "effect";
4746
+ var GUARDRAIL = /<ferix:guardrail\s+severity="(warning|critical)"\s*>([\s\S]*?)<\/ferix:guardrail>/g;
4747
+ var PATTERN = /<pattern>([\s\S]*?)<\/pattern>/;
4748
+ var SIGN = /<sign>([\s\S]*?)<\/sign>/;
4749
+ var AVOIDANCE = /<avoidance>([\s\S]*?)<\/avoidance>/;
4750
+ var guardrailSpec = {
4751
+ tag: "Guardrail",
4752
+ closingTag: "</ferix:guardrail>",
4753
+ schema: GuardrailSignalSchema,
4754
+ parse: (text) => {
4755
+ const signals = [];
4756
+ const matches = text.matchAll(GUARDRAIL);
4757
+ for (const match of matches) {
4758
+ const severity = match[1];
4759
+ const content = match[2] || "";
4760
+ const patternMatch = content.match(PATTERN);
4761
+ const signMatch = content.match(SIGN);
4762
+ const avoidanceMatch = content.match(AVOIDANCE);
4763
+ const pattern = patternMatch?.[1]?.trim() || "";
4764
+ const sign = signMatch?.[1]?.trim() || "";
4765
+ const avoidance = avoidanceMatch?.[1]?.trim() || "";
4766
+ if (!(pattern && sign && avoidance)) {
4767
+ continue;
4768
+ }
4769
+ const raw = {
4770
+ _tag: "Guardrail",
4771
+ pattern,
4772
+ sign,
4773
+ avoidance,
4774
+ severity
4775
+ };
4776
+ const result = S17.decodeUnknownEither(GuardrailSignalSchema)(raw);
4777
+ if (result._tag === "Right") {
4778
+ signals.push(result.right);
4779
+ }
4780
+ }
4781
+ return signals;
4782
+ },
4783
+ keyFields: (s) => s.pattern.slice(0, 50)
4784
+ };
4785
+ signalSpecRegistry.register(guardrailSpec);
4786
+
4787
+ // src/layers/signal/specs/learning.ts
4788
+ init_esm_shims();
4789
+ import { Schema as S18 } from "effect";
4790
+ var LEARNING = /<ferix:learning(?:\s+category="(success|failure|optimization)")?\s*>([\s\S]*?)<\/ferix:learning>/g;
4791
+ var learningSpec = {
4792
+ tag: "Learning",
4793
+ closingTag: "</ferix:learning>",
4794
+ schema: LearningSignalSchema,
4795
+ parse: (text) => {
4796
+ const signals = [];
4797
+ const matches = text.matchAll(LEARNING);
4798
+ for (const match of matches) {
4799
+ const category = match[1];
4800
+ const content = match[2]?.trim() || "";
4801
+ if (!content) {
4802
+ continue;
4803
+ }
4804
+ const raw = {
4805
+ _tag: "Learning",
4806
+ content
4807
+ };
4808
+ if (category) {
4809
+ raw.category = category;
4810
+ }
4811
+ const result = S18.decodeUnknownEither(LearningSignalSchema)(raw);
4812
+ if (result._tag === "Right") {
4813
+ signals.push(result.right);
4814
+ }
4815
+ }
4816
+ return signals;
4817
+ },
4818
+ keyFields: (s) => s.content.slice(0, 50)
4819
+ };
4820
+ signalSpecRegistry.register(learningSpec);
4821
+
3824
4822
  // src/layers/signal/specs/loop-complete.ts
3825
4823
  init_esm_shims();
3826
- import { Schema as S15 } from "effect";
4824
+ import { Schema as S19 } from "effect";
3827
4825
  var LOOP_COMPLETE = /<ferix:complete>/;
3828
4826
  var loopCompleteSpec = {
3829
4827
  tag: "LoopComplete",
@@ -3832,7 +4830,7 @@ var loopCompleteSpec = {
3832
4830
  parse: (text) => {
3833
4831
  if (LOOP_COMPLETE.test(text)) {
3834
4832
  const raw = { _tag: "LoopComplete" };
3835
- const result = S15.decodeUnknownEither(LoopCompleteSignalSchema)(raw);
4833
+ const result = S19.decodeUnknownEither(LoopCompleteSignalSchema)(raw);
3836
4834
  if (result._tag === "Right") {
3837
4835
  return [result.right];
3838
4836
  }
@@ -3845,7 +4843,7 @@ signalSpecRegistry.register(loopCompleteSpec);
3845
4843
 
3846
4844
  // src/layers/signal/specs/phases.ts
3847
4845
  init_esm_shims();
3848
- import { Schema as S16 } from "effect";
4846
+ import { Schema as S20 } from "effect";
3849
4847
  var PHASES_BLOCK = /<ferix:phases task="(\d+)">([\s\S]*?)<\/ferix:phases>/;
3850
4848
  var PHASE = /<phase id="([\d.]+)">([^<]+)<\/phase>/g;
3851
4849
  var PHASE_START = /<ferix:phase-start id="([\d.]+)"\/>/g;
@@ -3878,7 +4876,7 @@ var phasesDefinedSpec = {
3878
4876
  taskId: match[1],
3879
4877
  phases
3880
4878
  };
3881
- const result = S16.decodeUnknownEither(PhasesDefinedSignalSchema)(raw);
4879
+ const result = S20.decodeUnknownEither(PhasesDefinedSignalSchema)(raw);
3882
4880
  if (result._tag === "Left") {
3883
4881
  return [];
3884
4882
  }
@@ -3895,7 +4893,7 @@ var phaseStartedSpec = {
3895
4893
  for (const m of text.matchAll(resetRegex2(PHASE_START))) {
3896
4894
  if (m[1]) {
3897
4895
  const raw = { _tag: "PhaseStarted", phaseId: m[1] };
3898
- const result = S16.decodeUnknownEither(PhaseStartedSignalSchema)(raw);
4896
+ const result = S20.decodeUnknownEither(PhaseStartedSignalSchema)(raw);
3899
4897
  if (result._tag === "Right") {
3900
4898
  signals.push(result.right);
3901
4899
  }
@@ -3914,7 +4912,7 @@ var phaseCompletedSpec = {
3914
4912
  for (const m of text.matchAll(resetRegex2(PHASE_DONE))) {
3915
4913
  if (m[1]) {
3916
4914
  const raw = { _tag: "PhaseCompleted", phaseId: m[1] };
3917
- const result = S16.decodeUnknownEither(PhaseCompletedSignalSchema)(raw);
4915
+ const result = S20.decodeUnknownEither(PhaseCompletedSignalSchema)(raw);
3918
4916
  if (result._tag === "Right") {
3919
4917
  signals.push(result.right);
3920
4918
  }
@@ -3937,7 +4935,7 @@ var phaseFailedSpec = {
3937
4935
  phaseId: m[1],
3938
4936
  reason: m[2] || "Unknown reason"
3939
4937
  };
3940
- const result = S16.decodeUnknownEither(PhaseFailedSignalSchema)(raw);
4938
+ const result = S20.decodeUnknownEither(PhaseFailedSignalSchema)(raw);
3941
4939
  if (result._tag === "Right") {
3942
4940
  signals.push(result.right);
3943
4941
  }
@@ -3954,7 +4952,7 @@ signalSpecRegistry.register(phaseFailedSpec);
3954
4952
 
3955
4953
  // src/layers/signal/specs/review.ts
3956
4954
  init_esm_shims();
3957
- import { Schema as S17 } from "effect";
4955
+ import { Schema as S21 } from "effect";
3958
4956
  var REVIEW_COMPLETE = /<ferix:review-complete\/>/;
3959
4957
  var REVIEW_CHANGES = /<ferix:review-changes-made\/>/;
3960
4958
  var reviewCompleteSpec = {
@@ -3967,7 +4965,7 @@ var reviewCompleteSpec = {
3967
4965
  }
3968
4966
  const changesMade = REVIEW_CHANGES.test(text);
3969
4967
  const raw = { _tag: "ReviewComplete", changesMade };
3970
- const result = S17.decodeUnknownEither(ReviewCompleteSignalSchema)(raw);
4968
+ const result = S21.decodeUnknownEither(ReviewCompleteSignalSchema)(raw);
3971
4969
  if (result._tag === "Right") {
3972
4970
  return [result.right];
3973
4971
  }
@@ -3979,7 +4977,7 @@ signalSpecRegistry.register(reviewCompleteSpec);
3979
4977
 
3980
4978
  // src/layers/signal/specs/task-complete.ts
3981
4979
  init_esm_shims();
3982
- import { Schema as S18 } from "effect";
4980
+ import { Schema as S22 } from "effect";
3983
4981
  var TASK_COMPLETE = /<ferix:task-complete id="(\d+)">([\s\S]*?)<\/ferix:task-complete>/;
3984
4982
  var SUMMARY = /<summary>([\s\S]*?)<\/summary>/;
3985
4983
  var FILES_MODIFIED = /<files-modified>([\s\S]*?)<\/files-modified>/;
@@ -4010,7 +5008,7 @@ var taskCompleteSpec = {
4010
5008
  filesModified: parseFileList(filesModifiedMatch?.[1]),
4011
5009
  filesCreated: parseFileList(filesCreatedMatch?.[1])
4012
5010
  };
4013
- const result = S18.decodeUnknownEither(TaskCompleteSignalSchema)(raw);
5011
+ const result = S22.decodeUnknownEither(TaskCompleteSignalSchema)(raw);
4014
5012
  if (result._tag === "Right") {
4015
5013
  return [result.right];
4016
5014
  }
@@ -4022,7 +5020,7 @@ signalSpecRegistry.register(taskCompleteSpec);
4022
5020
 
4023
5021
  // src/layers/signal/specs/tasks.ts
4024
5022
  init_esm_shims();
4025
- import { Schema as S19 } from "effect";
5023
+ import { Schema as S23 } from "effect";
4026
5024
  var TASKS_BLOCK = /<ferix:tasks>([\s\S]*?)<\/ferix:tasks>/;
4027
5025
  var TASK = /<task id="(\d+)">([^<]+)<\/task>/g;
4028
5026
  function resetRegex3(pattern) {
@@ -4052,7 +5050,7 @@ var tasksDefinedSpec = {
4052
5050
  return [];
4053
5051
  }
4054
5052
  const raw = { _tag: "TasksDefined", tasks };
4055
- const result = S19.decodeUnknownEither(TasksDefinedSignalSchema)(raw);
5053
+ const result = S23.decodeUnknownEither(TasksDefinedSignalSchema)(raw);
4056
5054
  if (result._tag === "Left") {
4057
5055
  return [];
4058
5056
  }
@@ -4065,20 +5063,20 @@ signalSpecRegistry.register(tasksDefinedSpec);
4065
5063
  // src/layers/signal/ferix-parser.ts
4066
5064
  var MAX_BUFFER_SIZE = 1024 * 1024;
4067
5065
  function createAccumulatorImpl() {
4068
- return Effect11.gen(function* () {
4069
- const chunksRef = yield* Ref5.make([]);
4070
- const emittedRef = yield* Ref5.make(/* @__PURE__ */ new Set());
4071
- const feed = (text) => Effect11.gen(function* () {
4072
- const chunks = yield* Ref5.get(chunksRef);
5066
+ return Effect17.gen(function* () {
5067
+ const chunksRef = yield* Ref8.make([]);
5068
+ const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
5069
+ const feed = (text) => Effect17.gen(function* () {
5070
+ const chunks = yield* Ref8.get(chunksRef);
4073
5071
  chunks.push(text);
4074
5072
  const buffer = chunks.join("");
4075
5073
  if (buffer.length > MAX_BUFFER_SIZE) {
4076
- yield* Ref5.set(chunksRef, [
5074
+ yield* Ref8.set(chunksRef, [
4077
5075
  buffer.slice(buffer.length - MAX_BUFFER_SIZE)
4078
5076
  ]);
4079
5077
  }
4080
5078
  const signals = signalSpecRegistry.parseAll(buffer);
4081
- const emitted = yield* Ref5.get(emittedRef);
5079
+ const emitted = yield* Ref8.get(emittedRef);
4082
5080
  const newSignals = signals.filter((signal) => {
4083
5081
  const key = signalSpecRegistry.getSignalKey(signal);
4084
5082
  if (emitted.has(key)) {
@@ -4090,55 +5088,64 @@ function createAccumulatorImpl() {
4090
5088
  if (newSignals.length > 0) {
4091
5089
  const lastEndPos = signalSpecRegistry.findLastCompleteSignalEnd(buffer);
4092
5090
  if (lastEndPos > 0 && lastEndPos < buffer.length) {
4093
- yield* Ref5.set(chunksRef, [buffer.slice(lastEndPos)]);
5091
+ yield* Ref8.set(chunksRef, [buffer.slice(lastEndPos)]);
4094
5092
  }
4095
5093
  }
4096
- yield* Ref5.set(emittedRef, emitted);
5094
+ yield* Ref8.set(emittedRef, emitted);
4097
5095
  return newSignals;
4098
5096
  });
4099
- const flush = () => Effect11.gen(function* () {
4100
- const chunks = yield* Ref5.get(chunksRef);
5097
+ const flush = () => Effect17.gen(function* () {
5098
+ const chunks = yield* Ref8.get(chunksRef);
4101
5099
  const buffer = chunks.join("");
4102
- yield* Ref5.set(chunksRef, []);
4103
- const emitted = yield* Ref5.get(emittedRef);
5100
+ yield* Ref8.set(chunksRef, []);
5101
+ const emitted = yield* Ref8.get(emittedRef);
4104
5102
  const signals = signalSpecRegistry.parseAll(buffer);
4105
5103
  const result = signals.filter(
4106
5104
  (signal) => !emitted.has(signalSpecRegistry.getSignalKey(signal))
4107
5105
  );
4108
- yield* Ref5.set(emittedRef, /* @__PURE__ */ new Set());
5106
+ yield* Ref8.set(emittedRef, /* @__PURE__ */ new Set());
4109
5107
  return result;
4110
5108
  });
4111
5109
  return { feed, flush };
4112
5110
  });
4113
5111
  }
4114
- var make4 = {
4115
- parse: (text) => Effect11.succeed(signalSpecRegistry.parseAll(text)),
5112
+ var make7 = {
5113
+ parse: (text) => Effect17.succeed(signalSpecRegistry.parseAll(text)),
4116
5114
  createAccumulator: createAccumulatorImpl
4117
5115
  };
4118
- var Live7 = Layer7.succeed(SignalParser, make4);
5116
+ var Live13 = Layer13.succeed(SignalParser, make7);
4119
5117
  var FerixParser = {
4120
- Live: Live7
5118
+ Live: Live13
4121
5119
  };
4122
5120
 
4123
5121
  // src/layers/index.ts
4124
- var ProductionLayers = Layer8.mergeAll(
5122
+ var ProductionLayers = Layer14.mergeAll(
4125
5123
  ClaudeCLI.Live,
4126
5124
  FerixParser.Live,
4127
5125
  FileSystemPlan.Live,
4128
- FileSystemSession.Live
5126
+ FileSystemSession.Live,
5127
+ FileSystemProgress.Live,
5128
+ FileSystemGuardrails.Live,
5129
+ FileSystemGit.Live
4129
5130
  );
4130
- var TestLayers = Layer8.mergeAll(
5131
+ var TestLayers = Layer14.mergeAll(
4131
5132
  Mock.Live,
4132
5133
  FerixParser.Live,
4133
5134
  MemoryPlan.Live,
4134
- MemorySession.Live
5135
+ MemorySession.Live,
5136
+ MemoryProgress.Live,
5137
+ MemoryGuardrails.Live,
5138
+ MemoryGit.Live
4135
5139
  );
4136
5140
  function createTestLayers(events) {
4137
- return Layer8.mergeAll(
5141
+ return Layer14.mergeAll(
4138
5142
  Mock.layer({ events }),
4139
5143
  FerixParser.Live,
4140
5144
  MemoryPlan.layer(),
4141
- MemorySession.layer()
5145
+ MemorySession.layer(),
5146
+ MemoryProgress.layer(),
5147
+ MemoryGuardrails.layer(),
5148
+ MemoryGit.layer()
4142
5149
  );
4143
5150
  }
4144
5151
 
@@ -4147,42 +5154,42 @@ init_esm_shims();
4147
5154
 
4148
5155
  // src/orchestrator/loop.ts
4149
5156
  init_esm_shims();
4150
- import { DateTime as DateTime6, Effect as Effect16, Option, pipe as pipe3, Ref as Ref9, Stream as Stream9 } from "effect";
5157
+ import { DateTime as DateTime10, Effect as Effect22, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
4151
5158
 
4152
5159
  // src/orchestrator/discovery.ts
4153
5160
  init_esm_shims();
4154
- import { DateTime as DateTime4, Effect as Effect14, pipe, Ref as Ref7, Stream as Stream7 } from "effect";
5161
+ import { DateTime as DateTime8, Effect as Effect20, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
4155
5162
 
4156
5163
  // src/layers/plan/task-generation.ts
4157
5164
  init_esm_shims();
4158
- import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
4159
- import { join as join3 } from "path";
4160
- import { Effect as Effect12 } from "effect";
4161
- var PLANS_DIR2 = ".ferix/plans";
4162
- function ensureDir3(dirPath) {
4163
- return Effect12.tryPromise({
4164
- try: () => mkdir3(dirPath, { recursive: true }),
5165
+ import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
5166
+ import { join as join6 } from "path";
5167
+ import { Effect as Effect18 } from "effect";
5168
+ var PLANS_DIR4 = ".ferix/plans";
5169
+ function ensureDir5(dirPath) {
5170
+ return Effect18.tryPromise({
5171
+ try: () => mkdir6(dirPath, { recursive: true }),
4165
5172
  catch: (error) => new PlanStoreError({
4166
5173
  message: `Failed to create directory: ${dirPath}`,
4167
5174
  operation: "create",
4168
5175
  cause: error
4169
5176
  })
4170
- }).pipe(Effect12.asVoid);
5177
+ }).pipe(Effect18.asVoid);
4171
5178
  }
4172
- function getSessionDir2(sessionId) {
4173
- return join3(process.cwd(), PLANS_DIR2, sessionId);
5179
+ function getSessionDir4(sessionId) {
5180
+ return join6(process.cwd(), PLANS_DIR4, sessionId);
4174
5181
  }
4175
5182
  function getTasksMdPath(sessionId) {
4176
- return join3(getSessionDir2(sessionId), "tasks.md");
5183
+ return join6(getSessionDir4(sessionId), "tasks.md");
4177
5184
  }
4178
5185
  function writeTasksMd(sessionId, tasks) {
4179
- return Effect12.gen(function* () {
4180
- const sessionDir = getSessionDir2(sessionId);
4181
- yield* ensureDir3(sessionDir);
5186
+ return Effect18.gen(function* () {
5187
+ const sessionDir = getSessionDir4(sessionId);
5188
+ yield* ensureDir5(sessionDir);
4182
5189
  const tasksMdPath = getTasksMdPath(sessionId);
4183
5190
  const content = formatTasksMd(tasks);
4184
- yield* Effect12.tryPromise({
4185
- try: () => writeFile3(tasksMdPath, content, "utf-8"),
5191
+ yield* Effect18.tryPromise({
5192
+ try: () => writeFile5(tasksMdPath, content, "utf-8"),
4186
5193
  catch: (error) => new PlanStoreError({
4187
5194
  message: `Failed to write tasks.md: ${tasksMdPath}`,
4188
5195
  operation: "create",
@@ -4326,6 +5333,32 @@ eventMappingRegistry.registerSignalMapper({
4326
5333
  }
4327
5334
  })
4328
5335
  });
5336
+ eventMappingRegistry.registerSignalMapper({
5337
+ tag: "Learning",
5338
+ map: (signal, context) => ({
5339
+ _tag: "LearningRecorded",
5340
+ iteration: 0,
5341
+ // Will be overwritten by iteration stream
5342
+ content: signal.content,
5343
+ category: signal.category,
5344
+ timestamp: context.timestamp
5345
+ })
5346
+ });
5347
+ eventMappingRegistry.registerSignalMapper({
5348
+ tag: "Guardrail",
5349
+ map: (signal, context) => ({
5350
+ _tag: "GuardrailAdded",
5351
+ id: `gr-pending-${context.timestamp}`,
5352
+ // Temp ID, real one assigned in iteration
5353
+ iteration: 0,
5354
+ // Will be overwritten by iteration stream
5355
+ pattern: signal.pattern,
5356
+ sign: signal.sign,
5357
+ avoidance: signal.avoidance,
5358
+ severity: signal.severity,
5359
+ timestamp: context.timestamp
5360
+ })
5361
+ });
4329
5362
 
4330
5363
  // src/orchestrator/mapping/index.ts
4331
5364
  function mapLLMEventToDomain(event, context) {
@@ -4337,7 +5370,7 @@ function mapSignalToDomain(signal, context) {
4337
5370
 
4338
5371
  // src/orchestrator/plan-updates.ts
4339
5372
  init_esm_shims();
4340
- import { DateTime as DateTime3, Effect as Effect13, Ref as Ref6 } from "effect";
5373
+ import { DateTime as DateTime7, Effect as Effect19, Ref as Ref9 } from "effect";
4341
5374
 
4342
5375
  // src/orchestrator/plan-updates/index.ts
4343
5376
  init_esm_shims();
@@ -4549,6 +5582,20 @@ planUpdateRegistry.register({
4549
5582
  } : void 0
4550
5583
  });
4551
5584
 
5585
+ // src/orchestrator/plan-updates/handlers/guardrail.ts
5586
+ init_esm_shims();
5587
+ planUpdateRegistry.register({
5588
+ tag: "Guardrail",
5589
+ handle: (_signal, _currentPlan, _context) => void 0
5590
+ });
5591
+
5592
+ // src/orchestrator/plan-updates/handlers/learning.ts
5593
+ init_esm_shims();
5594
+ planUpdateRegistry.register({
5595
+ tag: "Learning",
5596
+ handle: (_signal, _currentPlan, _context) => void 0
5597
+ });
5598
+
4552
5599
  // src/orchestrator/plan-updates/handlers/phase-lifecycle.ts
4553
5600
  init_esm_shims();
4554
5601
  planUpdateRegistry.register({
@@ -4628,9 +5675,9 @@ function persistPlanUpdate(planStore, plan, operation) {
4628
5675
  tasks: plan.tasks
4629
5676
  }) : planStore.update(plan.id, plan);
4630
5677
  return storeOp.pipe(
4631
- Effect13.map(() => null),
4632
- Effect13.catchAll(
4633
- (error) => Effect13.succeed({
5678
+ Effect19.map(() => null),
5679
+ Effect19.catchAll(
5680
+ (error) => Effect19.succeed({
4634
5681
  _tag: "PlanUpdateFailed",
4635
5682
  operation,
4636
5683
  error: error.message,
@@ -4640,10 +5687,10 @@ function persistPlanUpdate(planStore, plan, operation) {
4640
5687
  );
4641
5688
  }
4642
5689
  function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
4643
- return Effect13.gen(function* () {
4644
- const currentPlan = yield* Ref6.get(currentPlanRef);
4645
- const now = yield* DateTime3.now;
4646
- const timestamp = DateTime3.formatIso(now);
5690
+ return Effect19.gen(function* () {
5691
+ const currentPlan = yield* Ref9.get(currentPlanRef);
5692
+ const now = yield* DateTime7.now;
5693
+ const timestamp = DateTime7.formatIso(now);
4647
5694
  const updateResult = computePlanUpdate(signal, currentPlan, {
4648
5695
  sessionId,
4649
5696
  originalTask,
@@ -4653,8 +5700,8 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
4653
5700
  return [];
4654
5701
  }
4655
5702
  const { plan, operation, eventTag } = updateResult;
4656
- yield* Ref6.set(currentPlanRef, plan);
4657
- yield* Ref6.update(persistenceStateRef, (state) => ({
5703
+ yield* Ref9.set(currentPlanRef, plan);
5704
+ yield* Ref9.update(persistenceStateRef, (state) => ({
4658
5705
  dirty: true,
4659
5706
  pendingOperation: state.pendingOperation === "create" ? "create" : operation
4660
5707
  }));
@@ -4662,12 +5709,12 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
4662
5709
  });
4663
5710
  }
4664
5711
  function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
4665
- return Effect13.gen(function* () {
4666
- const state = yield* Ref6.get(persistenceStateRef);
5712
+ return Effect19.gen(function* () {
5713
+ const state = yield* Ref9.get(persistenceStateRef);
4667
5714
  if (!(state.dirty && state.pendingOperation)) {
4668
5715
  return [];
4669
5716
  }
4670
- const plan = yield* Ref6.get(currentPlanRef);
5717
+ const plan = yield* Ref9.get(currentPlanRef);
4671
5718
  if (!plan) {
4672
5719
  return [];
4673
5720
  }
@@ -4680,7 +5727,7 @@ function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
4680
5727
  if (failureEvent) {
4681
5728
  events.push(failureEvent);
4682
5729
  }
4683
- yield* Ref6.set(persistenceStateRef, {
5730
+ yield* Ref9.set(persistenceStateRef, {
4684
5731
  dirty: false,
4685
5732
  pendingOperation: null
4686
5733
  });
@@ -4739,9 +5786,12 @@ Use these XML-like tags to communicate structured information:
4739
5786
  <files-created>new-file.ts</files-created>
4740
5787
  </ferix:task-complete>
4741
5788
 
4742
- ### Loop Completion
5789
+ ### Loop Completion (use ONLY when ALL tasks are done)
4743
5790
  <ferix:complete>
4744
5791
 
5792
+ \u26A0\uFE0F IMPORTANT: Only emit <ferix:complete> after ALL tasks in the plan are complete.
5793
+ After completing a single task, emit <ferix:task-complete> and continue to the next task.
5794
+
4745
5795
  IMPORTANT: Always emit signals on their own lines, never inside markdown code blocks.`;
4746
5796
  var DISCOVERY_SYSTEM_PROMPT = `You are in the DISCOVERY phase of a ralph loop - an iterative AI coding workflow.
4747
5797
 
@@ -4782,14 +5832,17 @@ Review the code for quality:
4782
5832
  - When done, emit <ferix:review-complete/>`;
4783
5833
  var DEFAULT_COMPLETION_PROMPT = `## Completion
4784
5834
 
4785
- When the task is complete, emit:
5835
+ When you finish the CURRENT task, emit:
4786
5836
  <ferix:task-complete id="N">
4787
5837
  <summary>What was accomplished</summary>
4788
5838
  <files-modified>list of modified files</files-modified>
4789
5839
  <files-created>list of new files</files-created>
4790
5840
  </ferix:task-complete>
4791
5841
 
4792
- When ALL tasks are complete, emit <ferix:complete>`;
5842
+ After emitting task-complete, CONTINUE working on the next pending task.
5843
+
5844
+ \u26A0\uFE0F ONLY emit <ferix:complete> when ALL tasks in the plan are done.
5845
+ Do NOT emit <ferix:complete> after completing just one task - continue to the next task instead.`;
4793
5846
  function getPhasePrompt(phase, prompts, defaultPrompt) {
4794
5847
  return prompts?.phases?.[phase] ?? defaultPrompt;
4795
5848
  }
@@ -4930,12 +5983,12 @@ Begin.`);
4930
5983
 
4931
5984
  // src/orchestrator/discovery.ts
4932
5985
  function processTextSignals(signalParser, text, context) {
4933
- return Effect14.gen(function* () {
5986
+ return Effect20.gen(function* () {
4934
5987
  const events = [];
4935
5988
  const parsedSignals = [];
4936
5989
  const signals = yield* signalParser.parse(text).pipe(
4937
- Effect14.tapError(
4938
- (error) => Effect14.logDebug(
5990
+ Effect20.tapError(
5991
+ (error) => Effect20.logDebug(
4939
5992
  "Signal parsing failed, continuing with empty signals",
4940
5993
  {
4941
5994
  error: String(error),
@@ -4943,7 +5996,7 @@ function processTextSignals(signalParser, text, context) {
4943
5996
  }
4944
5997
  )
4945
5998
  ),
4946
- Effect14.orElseSucceed(() => [])
5999
+ Effect20.orElseSucceed(() => [])
4947
6000
  );
4948
6001
  for (const signal of signals) {
4949
6002
  events.push(mapSignalToDomain(signal, context));
@@ -4953,7 +6006,7 @@ function processTextSignals(signalParser, text, context) {
4953
6006
  });
4954
6007
  }
4955
6008
  function processLLMEvent(signalParser, llmEvent, context) {
4956
- return Effect14.gen(function* () {
6009
+ return Effect20.gen(function* () {
4957
6010
  const domainEvent = mapLLMEventToDomain(llmEvent, context);
4958
6011
  const events = [domainEvent];
4959
6012
  const allSignals = [];
@@ -4996,12 +6049,12 @@ function planTasksToGeneratedTasks(plan) {
4996
6049
  status: mapTaskStatus(task.status)
4997
6050
  }));
4998
6051
  }
4999
- function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId) {
6052
+ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
5000
6053
  return Stream7.unwrap(
5001
- Effect14.gen(function* () {
5002
- const startTimeUtc = yield* DateTime4.now;
5003
- const startTime = DateTime4.toEpochMillis(startTimeUtc);
5004
- const persistenceStateRef = yield* Ref7.make({
6054
+ Effect20.gen(function* () {
6055
+ const startTimeUtc = yield* DateTime8.now;
6056
+ const startTime = DateTime8.toEpochMillis(startTimeUtc);
6057
+ const persistenceStateRef = yield* Ref10.make({
5005
6058
  dirty: false,
5006
6059
  pendingOperation: null
5007
6060
  });
@@ -5015,7 +6068,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5015
6068
  input: {}
5016
6069
  };
5017
6070
  const prompt = buildDiscoveryPrompt(config);
5018
- const llmStream = llm.execute(prompt).pipe(
6071
+ const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
5019
6072
  Stream7.mapError(
5020
6073
  (e) => new OrchestratorError({
5021
6074
  message: `LLM execution failed during discovery: ${String(e)}`,
@@ -5025,10 +6078,10 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5025
6078
  ),
5026
6079
  Stream7.flatMap(
5027
6080
  (llmEvent) => Stream7.unwrap(
5028
- Effect14.gen(function* () {
5029
- const now = yield* DateTime4.now;
6081
+ Effect20.gen(function* () {
6082
+ const now = yield* DateTime8.now;
5030
6083
  const context = {
5031
- timestamp: DateTime4.toEpochMillis(now)
6084
+ timestamp: DateTime8.toEpochMillis(now)
5032
6085
  };
5033
6086
  const result = yield* processLLMEvent(
5034
6087
  signalParser,
@@ -5062,27 +6115,27 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5062
6115
  )
5063
6116
  );
5064
6117
  const completionStream = Stream7.fromEffect(
5065
- Effect14.gen(function* () {
6118
+ Effect20.gen(function* () {
5066
6119
  const persistEvents = yield* flushPlanPersistence(
5067
6120
  planStore,
5068
6121
  currentPlanRef,
5069
6122
  persistenceStateRef
5070
6123
  );
5071
- const plan = yield* Ref7.get(currentPlanRef);
6124
+ const plan = yield* Ref10.get(currentPlanRef);
5072
6125
  const taskCount = plan?.tasks.length ?? 0;
5073
6126
  if (plan && plan.tasks.length > 0) {
5074
6127
  const generatedTasks = planTasksToGeneratedTasks(plan);
5075
6128
  yield* writeTasksMd(sessionId, generatedTasks).pipe(
5076
- Effect14.tapError(
5077
- (error) => Effect14.logDebug("Failed to write tasks.md, continuing", {
6129
+ Effect20.tapError(
6130
+ (error) => Effect20.logDebug("Failed to write tasks.md, continuing", {
5078
6131
  error: String(error)
5079
6132
  })
5080
6133
  ),
5081
- Effect14.orElseSucceed(() => void 0)
6134
+ Effect20.orElseSucceed(() => void 0)
5082
6135
  );
5083
6136
  }
5084
- const endTimeUtc = yield* DateTime4.now;
5085
- const endTime = DateTime4.toEpochMillis(endTimeUtc);
6137
+ const endTimeUtc = yield* DateTime8.now;
6138
+ const endTime = DateTime8.toEpochMillis(endTimeUtc);
5086
6139
  const discoveryCompleted = {
5087
6140
  _tag: "DiscoveryCompleted",
5088
6141
  taskCount,
@@ -5103,15 +6156,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
5103
6156
 
5104
6157
  // src/orchestrator/iteration.ts
5105
6158
  init_esm_shims();
5106
- import { DateTime as DateTime5, Effect as Effect15, pipe as pipe2, Ref as Ref8, Stream as Stream8 } from "effect";
6159
+ import { DateTime as DateTime9, Effect as Effect21, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
5107
6160
  function processTextSignals2(signalParser, text, context) {
5108
- return Effect15.gen(function* () {
6161
+ return Effect21.gen(function* () {
5109
6162
  const events = [];
5110
6163
  let completed = false;
5111
6164
  const parsedSignals = [];
5112
6165
  const signals = yield* signalParser.parse(text).pipe(
5113
- Effect15.tapError(
5114
- (error) => Effect15.logDebug(
6166
+ Effect21.tapError(
6167
+ (error) => Effect21.logDebug(
5115
6168
  "Signal parsing failed, continuing with empty signals",
5116
6169
  {
5117
6170
  error: String(error),
@@ -5119,7 +6172,7 @@ function processTextSignals2(signalParser, text, context) {
5119
6172
  }
5120
6173
  )
5121
6174
  ),
5122
- Effect15.orElseSucceed(() => [])
6175
+ Effect21.orElseSucceed(() => [])
5123
6176
  );
5124
6177
  for (const signal of signals) {
5125
6178
  events.push(mapSignalToDomain(signal, context));
@@ -5132,7 +6185,7 @@ function processTextSignals2(signalParser, text, context) {
5132
6185
  });
5133
6186
  }
5134
6187
  function processLLMEvent2(signalParser, llmEvent, context) {
5135
- return Effect15.gen(function* () {
6188
+ return Effect21.gen(function* () {
5136
6189
  const domainEvent = mapLLMEventToDomain(llmEvent, context);
5137
6190
  const events = [domainEvent];
5138
6191
  let completed = false;
@@ -5163,11 +6216,11 @@ function processLLMEvent2(signalParser, llmEvent, context) {
5163
6216
  return { events, completed, signals: allSignals };
5164
6217
  });
5165
6218
  }
5166
- function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId) {
6219
+ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
5167
6220
  return Stream8.unwrap(
5168
- Effect15.gen(function* () {
5169
- const currentPlan = yield* Ref8.get(currentPlanRef);
5170
- const persistenceStateRef = yield* Ref8.make({
6221
+ Effect21.gen(function* () {
6222
+ const currentPlan = yield* Ref11.get(currentPlanRef);
6223
+ const persistenceStateRef = yield* Ref11.make({
5171
6224
  dirty: false,
5172
6225
  pendingOperation: null
5173
6226
  });
@@ -5175,7 +6228,10 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5175
6228
  _tag: "IterationStarted",
5176
6229
  iteration
5177
6230
  };
5178
- const llmStream = llm.execute(buildPrompt(config, iteration, currentPlan)).pipe(
6231
+ const llmStream = llm.execute(
6232
+ buildPrompt(config, iteration, currentPlan),
6233
+ worktreePath ? { cwd: worktreePath } : void 0
6234
+ ).pipe(
5179
6235
  Stream8.mapError(
5180
6236
  (e) => new OrchestratorError({
5181
6237
  message: `LLM execution failed: ${String(e)}`,
@@ -5185,10 +6241,10 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5185
6241
  ),
5186
6242
  Stream8.flatMap(
5187
6243
  (llmEvent) => Stream8.unwrap(
5188
- Effect15.gen(function* () {
5189
- const now = yield* DateTime5.now;
6244
+ Effect21.gen(function* () {
6245
+ const now = yield* DateTime9.now;
5190
6246
  const context = {
5191
- timestamp: DateTime5.toEpochMillis(now)
6247
+ timestamp: DateTime9.toEpochMillis(now)
5192
6248
  };
5193
6249
  const result = yield* processLLMEvent2(
5194
6250
  signalParser,
@@ -5207,7 +6263,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5207
6263
  events.push(...planEvents);
5208
6264
  }
5209
6265
  if (result.completed) {
5210
- yield* Ref8.set(loopCompletedRef, true);
6266
+ yield* Ref11.set(loopCompletedRef, true);
5211
6267
  }
5212
6268
  return Stream8.fromIterable(events);
5213
6269
  })
@@ -5226,7 +6282,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5226
6282
  )
5227
6283
  );
5228
6284
  const completionStream = Stream8.fromEffect(
5229
- Effect15.gen(function* () {
6285
+ Effect21.gen(function* () {
5230
6286
  const persistEvents = yield* flushPlanPersistence(
5231
6287
  planStore,
5232
6288
  currentPlanRef,
@@ -5251,13 +6307,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
5251
6307
  // src/orchestrator/loop.ts
5252
6308
  function runLoop(config) {
5253
6309
  return Stream9.unwrap(
5254
- Effect16.gen(function* () {
6310
+ Effect22.gen(function* () {
5255
6311
  const llm = yield* LLM;
5256
6312
  const signalParser = yield* SignalParser;
5257
6313
  const sessionStore = yield* SessionStore;
5258
6314
  const planStore = yield* PlanStore;
6315
+ const git = yield* Git;
5259
6316
  const session = yield* sessionStore.create(config.task).pipe(
5260
- Effect16.mapError(
6317
+ Effect22.mapError(
5261
6318
  (e) => new OrchestratorError({
5262
6319
  message: `Failed to create session: ${e.message}`,
5263
6320
  phase: "setup",
@@ -5265,10 +6322,39 @@ function runLoop(config) {
5265
6322
  })
5266
6323
  )
5267
6324
  );
5268
- const startTimeUtc = yield* DateTime6.now;
5269
- const startTime = DateTime6.toEpochMillis(startTimeUtc);
5270
- const loopCompletedRef = yield* Ref9.make(false);
5271
- const currentPlanRef = yield* Ref9.make(void 0);
6325
+ const worktreePath = yield* git.createWorktree(session.id).pipe(
6326
+ Effect22.mapError(
6327
+ (e) => new OrchestratorError({
6328
+ message: `Failed to create worktree: ${e.message}`,
6329
+ phase: "setup",
6330
+ cause: e
6331
+ })
6332
+ )
6333
+ );
6334
+ const branchName = git.getBranchName(session.id);
6335
+ yield* sessionStore.update(session.id, {
6336
+ ...session,
6337
+ worktreePath,
6338
+ branchName
6339
+ }).pipe(
6340
+ Effect22.tapError(
6341
+ (error) => Effect22.logDebug("Failed to update session with worktree info", {
6342
+ error: String(error)
6343
+ })
6344
+ ),
6345
+ Effect22.orElseSucceed(() => void 0)
6346
+ );
6347
+ const startTimeUtc = yield* DateTime10.now;
6348
+ const startTime = DateTime10.toEpochMillis(startTimeUtc);
6349
+ const worktreeCreated = {
6350
+ _tag: "WorktreeCreated",
6351
+ sessionId: session.id,
6352
+ worktreePath,
6353
+ branchName,
6354
+ timestamp: startTime
6355
+ };
6356
+ const loopCompletedRef = yield* Ref12.make(false);
6357
+ const currentPlanRef = yield* Ref12.make(void 0);
5272
6358
  const maxIterations = config.maxIterations === 0 ? Number.POSITIVE_INFINITY : config.maxIterations;
5273
6359
  const loopStarted = {
5274
6360
  _tag: "LoopStarted",
@@ -5281,12 +6367,13 @@ function runLoop(config) {
5281
6367
  planStore,
5282
6368
  currentPlanRef,
5283
6369
  config,
5284
- session.id
6370
+ session.id,
6371
+ worktreePath
5285
6372
  );
5286
6373
  const iterationsStream = Stream9.unfoldEffect(
5287
6374
  1,
5288
- (iteration) => Effect16.gen(function* () {
5289
- const completed = yield* Ref9.get(loopCompletedRef);
6375
+ (iteration) => Effect22.gen(function* () {
6376
+ const completed = yield* Ref12.get(loopCompletedRef);
5290
6377
  if (completed || iteration > maxIterations) {
5291
6378
  return Option.none();
5292
6379
  }
@@ -5302,27 +6389,31 @@ function runLoop(config) {
5302
6389
  loopCompletedRef,
5303
6390
  config,
5304
6391
  iteration,
5305
- session.id
6392
+ session.id,
6393
+ worktreePath
5306
6394
  )
5307
6395
  )
5308
6396
  );
5309
6397
  const completionStream = createCompletionStream(
5310
6398
  sessionStore,
6399
+ git,
5311
6400
  session,
5312
6401
  config,
5313
6402
  startTime,
5314
- loopCompletedRef
6403
+ loopCompletedRef,
6404
+ worktreePath
5315
6405
  );
5316
6406
  return pipe3(
5317
6407
  Stream9.succeed(loopStarted),
6408
+ Stream9.concat(Stream9.succeed(worktreeCreated)),
5318
6409
  Stream9.concat(discoveryStream),
5319
6410
  Stream9.concat(iterationsStream),
5320
6411
  Stream9.concat(completionStream)
5321
6412
  );
5322
6413
  }).pipe(
5323
6414
  // Also catch setup errors (e.g., session creation failure)
5324
- Effect16.catchAll(
5325
- (error) => Effect16.succeed(
6415
+ Effect22.catchAll(
6416
+ (error) => Effect22.succeed(
5326
6417
  Stream9.succeed({
5327
6418
  _tag: "LoopFailed",
5328
6419
  error: {
@@ -5335,12 +6426,21 @@ function runLoop(config) {
5335
6426
  )
5336
6427
  );
5337
6428
  }
5338
- function createCompletionStream(sessionStore, session, config, startTime, loopCompletedRef) {
5339
- return Stream9.fromEffect(
5340
- Effect16.gen(function* () {
5341
- const endTimeUtc = yield* DateTime6.now;
5342
- const durationMs = DateTime6.toEpochMillis(endTimeUtc) - startTime;
5343
- const completed = yield* Ref9.get(loopCompletedRef);
6429
+ function createCompletionStream(sessionStore, git, session, config, startTime, loopCompletedRef, _worktreePath) {
6430
+ return Stream9.unwrap(
6431
+ Effect22.gen(function* () {
6432
+ const endTimeUtc = yield* DateTime10.now;
6433
+ const durationMs = DateTime10.toEpochMillis(endTimeUtc) - startTime;
6434
+ const completed = yield* Ref12.get(loopCompletedRef);
6435
+ yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
6436
+ Effect22.tapError(
6437
+ (error) => Effect22.logDebug("Final commit failed, continuing", {
6438
+ sessionId: session.id,
6439
+ error: String(error)
6440
+ })
6441
+ ),
6442
+ Effect22.orElseSucceed(() => void 0)
6443
+ );
5344
6444
  const summary = {
5345
6445
  iterations: config.maxIterations,
5346
6446
  success: completed,
@@ -5352,16 +6452,16 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
5352
6452
  ...session,
5353
6453
  status: completed ? "completed" : "paused"
5354
6454
  }).pipe(
5355
- Effect16.tapError(
5356
- (error) => Effect16.logDebug("Session update failed, continuing", {
6455
+ Effect22.tapError(
6456
+ (error) => Effect22.logDebug("Session update failed, continuing", {
5357
6457
  sessionId: session.id,
5358
6458
  error: String(error)
5359
6459
  })
5360
6460
  ),
5361
- Effect16.orElseSucceed(() => void 0)
6461
+ Effect22.orElseSucceed(() => void 0)
5362
6462
  );
5363
- const event = { _tag: "LoopCompleted", summary };
5364
- return event;
6463
+ const loopCompleted = { _tag: "LoopCompleted", summary };
6464
+ return Stream9.succeed(loopCompleted);
5365
6465
  })
5366
6466
  );
5367
6467
  }
@@ -5370,7 +6470,7 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
5370
6470
  function run(options) {
5371
6471
  const { config, consumer: consumerType = "headless", onEvent } = options;
5372
6472
  const events = runLoop(config);
5373
- const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect17.sync(() => onEvent(event)))) : events;
6473
+ const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
5374
6474
  const eventsWithLayers = eventsWithCallback.pipe(
5375
6475
  Stream10.provideLayer(ProductionLayers)
5376
6476
  );
@@ -5383,7 +6483,7 @@ function run(options) {
5383
6483
  function runTest(options, mockEvents) {
5384
6484
  const { config, onEvent } = options;
5385
6485
  const events = runLoop(config);
5386
- const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect17.sync(() => onEvent(event)))) : events;
6486
+ const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
5387
6487
  const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
5388
6488
  const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
5389
6489
  return eventsWithLayers.pipe(Stream10.runDrain);
@@ -5391,11 +6491,11 @@ function runTest(options, mockEvents) {
5391
6491
  function collectEvents(config, mockEvents) {
5392
6492
  const events = runLoop(config);
5393
6493
  const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
5394
- return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect17.map((chunk) => Array.from(chunk)));
6494
+ return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect23.map((chunk) => Array.from(chunk)));
5395
6495
  }
5396
6496
  function main(config) {
5397
6497
  const consumerType = process.stdout.isTTY ? "tui" : "headless";
5398
- return run({ config, consumer: consumerType }).pipe(Effect17.runPromise);
6498
+ return run({ config, consumer: consumerType }).pipe(Effect23.runPromise);
5399
6499
  }
5400
6500
 
5401
6501
  // src/services/index.ts
@@ -5403,7 +6503,7 @@ init_esm_shims();
5403
6503
 
5404
6504
  // src/index.ts
5405
6505
  var program = new Command();
5406
- program.name("ferix-code").description("Composable RALPH loops for AI coding agents").version(package_default.version).argument("<task>", "Task description or path to PRD file").option("-i, --iterations <n>", "Maximum iterations", "1").option("-v, --verify <commands...>", "Verification commands").option("--branch <name>", "Git branch to create").option("--push", "Push branch after completion").option("--pr", "Create PR after pushing").action(async (task, options) => {
6506
+ program.name("ferix-code").description("Composable RALPH loops for AI coding agents").version(package_default.version, "-v, --version", "Output the version number").argument("<task>", "Task description or path to PRD file").option("-i, --iterations <n>", "Maximum iterations", "1").option("-c, --verify <commands...>", "Verification commands to run").option("--branch <name>", "Git branch to create").option("--push", "Push branch after completion").option("--pr", "Create PR after pushing").action(async (task, options) => {
5407
6507
  const config = {
5408
6508
  task,
5409
6509
  maxIterations: Number.parseInt(options.iterations, 10),
@@ -5448,11 +6548,23 @@ export {
5448
6548
  ExecutionModeSchema,
5449
6549
  FerixParser,
5450
6550
  FileLoggerConfigSchema,
6551
+ FileSystemGit,
6552
+ FileSystemGuardrails,
5451
6553
  FileSystemPlan,
6554
+ FileSystemProgress,
5452
6555
  FileSystemSession,
5453
6556
  GeneratedTaskListSchema,
5454
6557
  GeneratedTaskSchema,
5455
6558
  GeneratedTaskStatusSchema,
6559
+ Git,
6560
+ GitError,
6561
+ GuardrailAddedEventSchema,
6562
+ GuardrailSchema,
6563
+ GuardrailSeveritySchema,
6564
+ GuardrailSignalSchema,
6565
+ GuardrailsFileSchema,
6566
+ GuardrailsStore,
6567
+ GuardrailsStoreError,
5456
6568
  IterationCompletedEventSchema,
5457
6569
  IterationStartedEventSchema,
5458
6570
  LLM,
@@ -5462,6 +6574,9 @@ export {
5462
6574
  LLMToolEndEventSchema,
5463
6575
  LLMToolStartEventSchema,
5464
6576
  LLMToolUseEventSchema,
6577
+ LearningCategorySchema,
6578
+ LearningRecordedEventSchema,
6579
+ LearningSignalSchema,
5465
6580
  LogEntrySchema,
5466
6581
  LogLevelSchema,
5467
6582
  LoopCompleteSignalSchema,
@@ -5472,7 +6587,10 @@ export {
5472
6587
  LoopStartedEventSchema,
5473
6588
  LoopStatusSchema,
5474
6589
  LoopSummarySchema,
6590
+ MemoryGit,
6591
+ MemoryGuardrails,
5475
6592
  MemoryPlan,
6593
+ MemoryProgress,
5476
6594
  MemorySession,
5477
6595
  Mock,
5478
6596
  Mock as MockLLM,
@@ -5502,6 +6620,12 @@ export {
5502
6620
  PlanUpdateFailedEventSchema,
5503
6621
  PlanUpdatedEventSchema,
5504
6622
  ProductionLayers,
6623
+ ProgressActionSchema,
6624
+ ProgressEntrySchema,
6625
+ ProgressFileSchema,
6626
+ ProgressStore,
6627
+ ProgressStoreError,
6628
+ ProgressUpdatedEventSchema,
5505
6629
  PromptConfigSchema,
5506
6630
  ReviewCompleteDataSchema,
5507
6631
  ReviewCompleteEventSchema,
@@ -5536,15 +6660,21 @@ export {
5536
6660
  ToolStartEventSchema,
5537
6661
  ToolUseEventSchema,
5538
6662
  ViewModeSchema,
6663
+ WorktreeCreatedEventSchema,
6664
+ WorktreeRemovedEventSchema,
5539
6665
  buildPrompt,
5540
6666
  collectEvents,
5541
6667
  createHeadlessConsumer,
5542
6668
  createTUIConsumer,
5543
6669
  createTestLayers,
6670
+ decodeGuardrail,
6671
+ decodeGuardrailsFile,
5544
6672
  decodeLLMEvent,
5545
6673
  decodeLoopConfig,
5546
6674
  decodePlan,
5547
6675
  decodePlanData,
6676
+ decodeProgressEntry,
6677
+ decodeProgressFile,
5548
6678
  decodeSession,
5549
6679
  decodeSignal,
5550
6680
  decodeSignalSync,