ferix-code 0.0.2-beta.1 → 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.
- package/dist/index.d.ts +688 -21
- package/dist/index.js +1938 -807
- package/package.json +5 -5
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.
|
|
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
|
-
|
|
78
|
+
ferix: "dist/index.js",
|
|
79
|
+
"ferix-code": "dist/index.js"
|
|
79
80
|
},
|
|
80
81
|
files: [
|
|
81
82
|
"dist"
|
|
@@ -85,13 +86,12 @@ 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
93
|
commander: "^14.0.0",
|
|
94
|
-
effect: "^3.
|
|
94
|
+
effect: "^3.19.15",
|
|
95
95
|
"human-id": "^4.1.3",
|
|
96
96
|
picocolors: "^1.1.1"
|
|
97
97
|
},
|
|
@@ -118,7 +118,7 @@ var package_default = {
|
|
|
118
118
|
|
|
119
119
|
// src/program.ts
|
|
120
120
|
init_esm_shims();
|
|
121
|
-
import { Effect as
|
|
121
|
+
import { Effect as Effect23, Stream as Stream10 } from "effect";
|
|
122
122
|
|
|
123
123
|
// src/consumers/index.ts
|
|
124
124
|
init_esm_shims();
|
|
@@ -126,7 +126,7 @@ init_esm_shims();
|
|
|
126
126
|
// src/consumers/headless/consumer.ts
|
|
127
127
|
init_esm_shims();
|
|
128
128
|
import { Cause, Effect, Stream } from "effect";
|
|
129
|
-
import
|
|
129
|
+
import pc10 from "picocolors";
|
|
130
130
|
|
|
131
131
|
// src/consumers/headless/formatters/index.ts
|
|
132
132
|
init_esm_shims();
|
|
@@ -307,6 +307,17 @@ var taskCompletedFormatter = {
|
|
|
307
307
|
headlessFormatterRegistry.register(tasksDefinedFormatter);
|
|
308
308
|
headlessFormatterRegistry.register(taskCompletedFormatter);
|
|
309
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
|
+
|
|
310
321
|
// src/consumers/headless/formatters/index.ts
|
|
311
322
|
function formatEvent(event) {
|
|
312
323
|
return headlessFormatterRegistry.format(event);
|
|
@@ -315,37 +326,37 @@ function formatEvent(event) {
|
|
|
315
326
|
// src/consumers/headless/consumer.ts
|
|
316
327
|
function formatErrorToLines(err, lines) {
|
|
317
328
|
if (err instanceof Error) {
|
|
318
|
-
lines.push(
|
|
329
|
+
lines.push(pc10.red(` - ${err.name}: ${err.message}`));
|
|
319
330
|
if (err.stack) {
|
|
320
331
|
lines.push("");
|
|
321
|
-
lines.push(
|
|
332
|
+
lines.push(pc10.yellow("Stack trace:"));
|
|
322
333
|
for (const stackLine of err.stack.split("\n").slice(1)) {
|
|
323
|
-
lines.push(
|
|
334
|
+
lines.push(pc10.dim(` ${stackLine.trim()}`));
|
|
324
335
|
}
|
|
325
336
|
}
|
|
326
337
|
} else {
|
|
327
|
-
lines.push(
|
|
338
|
+
lines.push(pc10.red(` - ${String(err)}`));
|
|
328
339
|
}
|
|
329
340
|
}
|
|
330
341
|
function formatCauseVerbose(cause) {
|
|
331
342
|
const lines = [];
|
|
332
|
-
const separator2 =
|
|
343
|
+
const separator2 = pc10.red(
|
|
333
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"
|
|
334
345
|
);
|
|
335
346
|
const causeType = Cause.isFailure(cause) ? "Failure" : "Other";
|
|
336
347
|
lines.push("");
|
|
337
348
|
lines.push(separator2);
|
|
338
|
-
lines.push(
|
|
349
|
+
lines.push(pc10.red(" ERROR"));
|
|
339
350
|
lines.push(separator2);
|
|
340
351
|
lines.push("");
|
|
341
|
-
lines.push(`${
|
|
352
|
+
lines.push(`${pc10.yellow("Type:")} ${causeType}`);
|
|
342
353
|
lines.push("");
|
|
343
|
-
lines.push(
|
|
354
|
+
lines.push(pc10.yellow("Details:"));
|
|
344
355
|
lines.push(Cause.pretty(cause));
|
|
345
356
|
const failures = Cause.failures(cause);
|
|
346
357
|
if (failures.length > 0) {
|
|
347
358
|
lines.push("");
|
|
348
|
-
lines.push(
|
|
359
|
+
lines.push(pc10.yellow("Failures:"));
|
|
349
360
|
for (const failure of failures) {
|
|
350
361
|
formatErrorToLines(failure, lines);
|
|
351
362
|
}
|
|
@@ -353,7 +364,7 @@ function formatCauseVerbose(cause) {
|
|
|
353
364
|
const defects = Cause.defects(cause);
|
|
354
365
|
if (defects.length > 0) {
|
|
355
366
|
lines.push("");
|
|
356
|
-
lines.push(
|
|
367
|
+
lines.push(pc10.yellow("Defects (unexpected errors):"));
|
|
357
368
|
for (const defect of defects) {
|
|
358
369
|
formatErrorToLines(defect, lines);
|
|
359
370
|
}
|
|
@@ -534,40 +545,40 @@ init_esm_shims();
|
|
|
534
545
|
|
|
535
546
|
// src/consumers/tui/tags/primitives.ts
|
|
536
547
|
init_esm_shims();
|
|
537
|
-
import
|
|
548
|
+
import pc12 from "picocolors";
|
|
538
549
|
|
|
539
550
|
// src/consumers/tui/render/primitives.ts
|
|
540
551
|
init_esm_shims();
|
|
541
|
-
import
|
|
552
|
+
import pc11 from "picocolors";
|
|
542
553
|
var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
543
554
|
var colors = {
|
|
544
|
-
brand:
|
|
545
|
-
success:
|
|
546
|
-
warning:
|
|
547
|
-
error:
|
|
548
|
-
info:
|
|
549
|
-
muted:
|
|
550
|
-
highlight:
|
|
551
|
-
brightWhite: (s) =>
|
|
552
|
-
brightMagenta: (s) =>
|
|
553
|
-
brightCyan: (s) =>
|
|
554
|
-
brightGreen: (s) =>
|
|
555
|
-
brightYellow: (s) =>
|
|
556
|
-
brightRed: (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))
|
|
557
568
|
};
|
|
558
569
|
var toolColors = {
|
|
559
|
-
Read:
|
|
560
|
-
Edit:
|
|
561
|
-
Write:
|
|
562
|
-
Bash:
|
|
563
|
-
Glob:
|
|
564
|
-
Grep:
|
|
570
|
+
Read: pc11.cyan,
|
|
571
|
+
Edit: pc11.yellow,
|
|
572
|
+
Write: pc11.green,
|
|
573
|
+
Bash: pc11.magenta,
|
|
574
|
+
Glob: pc11.blue,
|
|
575
|
+
Grep: pc11.blue,
|
|
565
576
|
Task: colors.brightWhite,
|
|
566
|
-
WebFetch:
|
|
567
|
-
WebSearch:
|
|
577
|
+
WebFetch: pc11.cyan,
|
|
578
|
+
WebSearch: pc11.blue
|
|
568
579
|
};
|
|
569
580
|
function getToolColor(tool) {
|
|
570
|
-
return toolColors[tool] ||
|
|
581
|
+
return toolColors[tool] || pc11.white;
|
|
571
582
|
}
|
|
572
583
|
var symbols = {
|
|
573
584
|
diamond: "\u25C6",
|
|
@@ -594,24 +605,24 @@ var box = {
|
|
|
594
605
|
};
|
|
595
606
|
function topBorder(width) {
|
|
596
607
|
const repeatCount = Math.max(0, width - 2);
|
|
597
|
-
return
|
|
608
|
+
return pc11.cyan(
|
|
598
609
|
`${box.topLeft}${box.horizontal.repeat(repeatCount)}${box.topRight}`
|
|
599
610
|
);
|
|
600
611
|
}
|
|
601
612
|
function separator(width) {
|
|
602
613
|
const repeatCount = Math.max(0, width - 2);
|
|
603
|
-
return
|
|
614
|
+
return pc11.cyan(
|
|
604
615
|
`${box.teeRight}${box.horizontal.repeat(repeatCount)}${box.teeLeft}`
|
|
605
616
|
);
|
|
606
617
|
}
|
|
607
618
|
function borderedLine(content, width) {
|
|
608
619
|
const stripped = stripAnsi(content);
|
|
609
620
|
const padding = Math.max(0, width - stripped.length - 4);
|
|
610
|
-
return `${
|
|
621
|
+
return `${pc11.cyan(box.vertical)} ${content}${" ".repeat(padding)} ${pc11.cyan(box.vertical)}`;
|
|
611
622
|
}
|
|
612
623
|
function emptyBorderedLine(width) {
|
|
613
624
|
const repeatCount = Math.max(0, width - 2);
|
|
614
|
-
return `${
|
|
625
|
+
return `${pc11.cyan(box.vertical)}${" ".repeat(repeatCount)}${pc11.cyan(box.vertical)}`;
|
|
615
626
|
}
|
|
616
627
|
function stripAnsi(str) {
|
|
617
628
|
return str.replace(ANSI_PATTERN, "");
|
|
@@ -708,31 +719,31 @@ function taskListHeader(width) {
|
|
|
708
719
|
}
|
|
709
720
|
const label = ` ${symbols.diamond} TASKS ${symbols.diamond} `;
|
|
710
721
|
const sideLen = Math.max(1, Math.floor((innerWidth - label.length) / 2));
|
|
711
|
-
return `${
|
|
722
|
+
return `${pc12.cyan("\u250C")}${pc12.cyan(box.singleHorizontal.repeat(sideLen))}${colors.brand(label)}${pc12.cyan(box.singleHorizontal.repeat(sideLen))}`;
|
|
712
723
|
}
|
|
713
724
|
function taskListFooter() {
|
|
714
|
-
return
|
|
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");
|
|
715
726
|
}
|
|
716
727
|
function taskLine(id, description) {
|
|
717
|
-
return `${
|
|
728
|
+
return `${pc12.cyan("\u2502")} ${colors.brightMagenta(`[${id}]`)} ${description}`;
|
|
718
729
|
}
|
|
719
730
|
function taskDone(id) {
|
|
720
731
|
return `${colors.success(symbols.checkmark)} ${colors.muted(`Task ${id} complete`)}`;
|
|
721
732
|
}
|
|
722
733
|
function phasesHeader(taskId) {
|
|
723
|
-
return `${
|
|
734
|
+
return `${pc12.cyan("\u2502")} ${colors.muted(`Phases for task ${taskId}:`)}`;
|
|
724
735
|
}
|
|
725
736
|
function phaseLine(id, description) {
|
|
726
|
-
return `${
|
|
737
|
+
return `${pc12.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
|
|
727
738
|
}
|
|
728
739
|
function phaseStart(id) {
|
|
729
|
-
return `${
|
|
740
|
+
return `${pc12.cyan("\u2502")} ${colors.warning(symbols.bulletFilled)} ${colors.muted(`Phase ${id} started`)}`;
|
|
730
741
|
}
|
|
731
742
|
function phaseDone(id) {
|
|
732
|
-
return `${
|
|
743
|
+
return `${pc12.cyan("\u2502")} ${colors.success(symbols.checkmark)} ${colors.muted(`Phase ${id} complete`)}`;
|
|
733
744
|
}
|
|
734
745
|
function phaseFailed(id, reason) {
|
|
735
|
-
return `${
|
|
746
|
+
return `${pc12.cyan("\u2502")} ${colors.error(symbols.cross)} ${colors.muted(`Phase ${id} failed:`)} ${colors.error(reason)}`;
|
|
736
747
|
}
|
|
737
748
|
function criterionPassed(id) {
|
|
738
749
|
return `${colors.success(symbols.checkmark)} ${colors.muted(`Criterion ${id} passed`)}`;
|
|
@@ -768,10 +779,10 @@ function reviewFailedBanner(width) {
|
|
|
768
779
|
);
|
|
769
780
|
}
|
|
770
781
|
function criteriaHeader(taskId) {
|
|
771
|
-
return `${
|
|
782
|
+
return `${pc12.cyan("\u2502")} ${colors.muted(`Success criteria for task ${taskId}:`)}`;
|
|
772
783
|
}
|
|
773
784
|
function criterionLine(id, description) {
|
|
774
|
-
return `${
|
|
785
|
+
return `${pc12.cyan("\u2502")} ${colors.muted(symbols.treeMiddle)} ${colors.muted(symbols.bulletEmpty)} ${colors.muted(`[${id}]`)} ${description}`;
|
|
775
786
|
}
|
|
776
787
|
|
|
777
788
|
// src/consumers/tui/tags/handlers/completion.ts
|
|
@@ -1044,11 +1055,11 @@ init_esm_shims();
|
|
|
1044
1055
|
|
|
1045
1056
|
// src/consumers/tui/tools/registry.ts
|
|
1046
1057
|
init_esm_shims();
|
|
1047
|
-
import
|
|
1048
|
-
var brightWhite = (s) =>
|
|
1058
|
+
import pc13 from "picocolors";
|
|
1059
|
+
var brightWhite = (s) => pc13.bold(pc13.white(s));
|
|
1049
1060
|
function createToolDisplayRegistry() {
|
|
1050
1061
|
const configs = /* @__PURE__ */ new Map();
|
|
1051
|
-
const defaultColor =
|
|
1062
|
+
const defaultColor = pc13.white;
|
|
1052
1063
|
return {
|
|
1053
1064
|
register(config) {
|
|
1054
1065
|
configs.set(config.tool, config);
|
|
@@ -1085,32 +1096,32 @@ var toolDisplayRegistry = createToolDisplayRegistry();
|
|
|
1085
1096
|
toolDisplayRegistry.register({
|
|
1086
1097
|
tool: "Read",
|
|
1087
1098
|
inputKey: "file_path",
|
|
1088
|
-
color:
|
|
1099
|
+
color: pc13.cyan
|
|
1089
1100
|
});
|
|
1090
1101
|
toolDisplayRegistry.register({
|
|
1091
1102
|
tool: "Edit",
|
|
1092
1103
|
inputKey: "file_path",
|
|
1093
|
-
color:
|
|
1104
|
+
color: pc13.yellow
|
|
1094
1105
|
});
|
|
1095
1106
|
toolDisplayRegistry.register({
|
|
1096
1107
|
tool: "Write",
|
|
1097
1108
|
inputKey: "file_path",
|
|
1098
|
-
color:
|
|
1109
|
+
color: pc13.green
|
|
1099
1110
|
});
|
|
1100
1111
|
toolDisplayRegistry.register({
|
|
1101
1112
|
tool: "Bash",
|
|
1102
1113
|
inputKey: "command",
|
|
1103
|
-
color:
|
|
1114
|
+
color: pc13.magenta
|
|
1104
1115
|
});
|
|
1105
1116
|
toolDisplayRegistry.register({
|
|
1106
1117
|
tool: "Glob",
|
|
1107
1118
|
inputKey: "pattern",
|
|
1108
|
-
color:
|
|
1119
|
+
color: pc13.blue
|
|
1109
1120
|
});
|
|
1110
1121
|
toolDisplayRegistry.register({
|
|
1111
1122
|
tool: "Grep",
|
|
1112
1123
|
inputKey: "pattern",
|
|
1113
|
-
color:
|
|
1124
|
+
color: pc13.blue
|
|
1114
1125
|
});
|
|
1115
1126
|
toolDisplayRegistry.register({
|
|
1116
1127
|
tool: "Task",
|
|
@@ -1120,12 +1131,12 @@ toolDisplayRegistry.register({
|
|
|
1120
1131
|
toolDisplayRegistry.register({
|
|
1121
1132
|
tool: "WebFetch",
|
|
1122
1133
|
inputKey: "url",
|
|
1123
|
-
color:
|
|
1134
|
+
color: pc13.cyan
|
|
1124
1135
|
});
|
|
1125
1136
|
toolDisplayRegistry.register({
|
|
1126
1137
|
tool: "WebSearch",
|
|
1127
1138
|
inputKey: "query",
|
|
1128
|
-
color:
|
|
1139
|
+
color: pc13.blue
|
|
1129
1140
|
});
|
|
1130
1141
|
|
|
1131
1142
|
// src/consumers/tui/tools/index.ts
|
|
@@ -1305,6 +1316,34 @@ stateReducerRegistry.register(loopStartedReducer);
|
|
|
1305
1316
|
stateReducerRegistry.register(loopCompletedReducer);
|
|
1306
1317
|
stateReducerRegistry.register(loopFailedReducer);
|
|
1307
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
|
+
|
|
1308
1347
|
// src/consumers/tui/reducers/tasks.ts
|
|
1309
1348
|
init_esm_shims();
|
|
1310
1349
|
var tasksDefinedReducer = {
|
|
@@ -1385,6 +1424,21 @@ stateReducerRegistry.register(criterionPassedReducer);
|
|
|
1385
1424
|
stateReducerRegistry.register(criterionFailedReducer);
|
|
1386
1425
|
stateReducerRegistry.register(taskCompletedReducer);
|
|
1387
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
|
+
|
|
1388
1442
|
// src/consumers/tui/reducers/index.ts
|
|
1389
1443
|
function reduce(state, event) {
|
|
1390
1444
|
return stateReducerRegistry.reduce(state, event);
|
|
@@ -1463,7 +1517,7 @@ init_esm_shims();
|
|
|
1463
1517
|
|
|
1464
1518
|
// src/consumers/tui/render/footer.ts
|
|
1465
1519
|
init_esm_shims();
|
|
1466
|
-
import
|
|
1520
|
+
import pc14 from "picocolors";
|
|
1467
1521
|
function hint(key, description) {
|
|
1468
1522
|
return `${colors.brand(symbols.diamond)} ${colors.brightWhite(key)} ${colors.muted(description)}`;
|
|
1469
1523
|
}
|
|
@@ -1512,7 +1566,7 @@ function renderFooter(state, width, outputHeight) {
|
|
|
1512
1566
|
const content = hints.join(" ");
|
|
1513
1567
|
const stripped = stripAnsi(content);
|
|
1514
1568
|
const padding = Math.max(0, width - stripped.length - 4);
|
|
1515
|
-
return `${
|
|
1569
|
+
return `${pc14.cyan(box.bottomLeft)}${pc14.cyan(box.horizontal)} ${content}${pc14.cyan(box.horizontal.repeat(Math.max(1, padding)))}${pc14.cyan(box.bottomRight)}`;
|
|
1516
1570
|
}
|
|
1517
1571
|
|
|
1518
1572
|
// src/consumers/tui/render/status-bar.ts
|
|
@@ -1591,7 +1645,7 @@ init_esm_shims();
|
|
|
1591
1645
|
|
|
1592
1646
|
// src/consumers/tui/render/task-detail.ts
|
|
1593
1647
|
init_esm_shims();
|
|
1594
|
-
import
|
|
1648
|
+
import pc15 from "picocolors";
|
|
1595
1649
|
function renderPhase(phase, isLast, width) {
|
|
1596
1650
|
const lines = [];
|
|
1597
1651
|
const prefix = isLast ? symbols.treeLast : symbols.treeMiddle;
|
|
@@ -1656,7 +1710,7 @@ function renderEmptyState(height, width) {
|
|
|
1656
1710
|
return lines;
|
|
1657
1711
|
}
|
|
1658
1712
|
function renderTaskHeader(task, width, innerWidth) {
|
|
1659
|
-
const sepLine =
|
|
1713
|
+
const sepLine = pc15.cyan(box.horizontal.repeat(innerWidth));
|
|
1660
1714
|
return [
|
|
1661
1715
|
emptyBorderedLine(width),
|
|
1662
1716
|
borderedLine(
|
|
@@ -1730,7 +1784,7 @@ function renderGitSection(state, width, innerWidth) {
|
|
|
1730
1784
|
return [];
|
|
1731
1785
|
}
|
|
1732
1786
|
const lines = [];
|
|
1733
|
-
const sepLine =
|
|
1787
|
+
const sepLine = pc15.cyan(box.horizontal.repeat(innerWidth));
|
|
1734
1788
|
const pushStatus = state.gitPushed ? colors.success(symbols.checkmark) : colors.muted("Not pushed");
|
|
1735
1789
|
lines.push(borderedLine(sepLine, width));
|
|
1736
1790
|
lines.push(
|
|
@@ -1809,7 +1863,7 @@ init_esm_shims();
|
|
|
1809
1863
|
|
|
1810
1864
|
// src/consumers/tui/render/tasks-list.ts
|
|
1811
1865
|
init_esm_shims();
|
|
1812
|
-
import
|
|
1866
|
+
import pc16 from "picocolors";
|
|
1813
1867
|
function renderTaskLine(task, isSelected, width) {
|
|
1814
1868
|
const innerWidth = width - 4;
|
|
1815
1869
|
const parts = [];
|
|
@@ -1873,7 +1927,7 @@ function renderTasksListView(state, height, width) {
|
|
|
1873
1927
|
lines.push(emptyBorderedLine(width));
|
|
1874
1928
|
const headerText = `${colors.brand(symbols.diamond)} ${colors.brightWhite("TASKS")} ${colors.brand(symbols.diamond)}`;
|
|
1875
1929
|
lines.push(borderedLine(headerText, width));
|
|
1876
|
-
const sepLine =
|
|
1930
|
+
const sepLine = pc16.cyan(box.horizontal.repeat(innerWidth));
|
|
1877
1931
|
lines.push(borderedLine(sepLine, width));
|
|
1878
1932
|
lines.push(emptyBorderedLine(width));
|
|
1879
1933
|
const gitLines = renderGitInfo(state, width);
|
|
@@ -2189,6 +2243,7 @@ var ANSIOutput = class {
|
|
|
2189
2243
|
init_esm_shims();
|
|
2190
2244
|
|
|
2191
2245
|
// src/consumers/tui/consumer.ts
|
|
2246
|
+
var CLOCK_REFRESH_INTERVAL_MS = 1e3;
|
|
2192
2247
|
function formatErrorToLines2(err, lines) {
|
|
2193
2248
|
if (err instanceof Error) {
|
|
2194
2249
|
lines.push(` - ${err.name}: ${err.message}`);
|
|
@@ -2248,6 +2303,17 @@ function createTUIConsumer() {
|
|
|
2248
2303
|
const inputFiber = yield* Effect3.forkDaemon(
|
|
2249
2304
|
runInputLoop(stateRef, output)
|
|
2250
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
|
+
);
|
|
2251
2317
|
const processEvents = events.pipe(
|
|
2252
2318
|
Stream3.runForEach(
|
|
2253
2319
|
(event) => Effect3.gen(function* () {
|
|
@@ -2299,6 +2365,7 @@ ${errorText}
|
|
|
2299
2365
|
})
|
|
2300
2366
|
);
|
|
2301
2367
|
}
|
|
2368
|
+
yield* Fiber.interrupt(clockFiber);
|
|
2302
2369
|
unsubscribeResize();
|
|
2303
2370
|
cleanup();
|
|
2304
2371
|
})
|
|
@@ -2307,23 +2374,15 @@ ${errorText}
|
|
|
2307
2374
|
|
|
2308
2375
|
// src/layers/index.ts
|
|
2309
2376
|
init_esm_shims();
|
|
2310
|
-
import { Layer as
|
|
2311
|
-
|
|
2312
|
-
// src/layers/llm/claude-cli.ts
|
|
2313
|
-
init_esm_shims();
|
|
2314
|
-
import { spawn } from "child_process";
|
|
2315
|
-
import { Effect as Effect5, Layer, Stream as Stream5 } from "effect";
|
|
2316
|
-
|
|
2317
|
-
// src/services/llm.ts
|
|
2318
|
-
init_esm_shims();
|
|
2319
|
-
import { Context } from "effect";
|
|
2320
|
-
var LLM = class extends Context.Tag("@ferix/LLM")() {
|
|
2321
|
-
};
|
|
2377
|
+
import { Layer as Layer14 } from "effect";
|
|
2322
2378
|
|
|
2323
|
-
// src/layers/
|
|
2379
|
+
// src/layers/git/file-system.ts
|
|
2324
2380
|
init_esm_shims();
|
|
2325
|
-
import {
|
|
2326
|
-
import {
|
|
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";
|
|
2327
2386
|
|
|
2328
2387
|
// src/domain/errors.ts
|
|
2329
2388
|
init_esm_shims();
|
|
@@ -2336,288 +2395,346 @@ var PlanStoreError = class extends Data.TaggedError("PlanStoreError") {
|
|
|
2336
2395
|
};
|
|
2337
2396
|
var SessionStoreError = class extends Data.TaggedError("SessionStoreError") {
|
|
2338
2397
|
};
|
|
2398
|
+
var ProgressStoreError = class extends Data.TaggedError("ProgressStoreError") {
|
|
2399
|
+
};
|
|
2400
|
+
var GuardrailsStoreError = class extends Data.TaggedError(
|
|
2401
|
+
"GuardrailsStoreError"
|
|
2402
|
+
) {
|
|
2403
|
+
};
|
|
2339
2404
|
var OrchestratorError = class extends Data.TaggedError("OrchestratorError") {
|
|
2340
2405
|
};
|
|
2406
|
+
var GitError = class extends Data.TaggedError("GitError") {
|
|
2407
|
+
};
|
|
2341
2408
|
|
|
2342
|
-
// src/
|
|
2409
|
+
// src/services/git.ts
|
|
2343
2410
|
init_esm_shims();
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
for (const block of json.message.content) {
|
|
2372
|
-
if (block.type === "text" && block.text) {
|
|
2373
|
-
return block.text;
|
|
2374
|
-
}
|
|
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
|
+
});
|
|
2375
2438
|
}
|
|
2376
|
-
}
|
|
2377
|
-
return null;
|
|
2439
|
+
});
|
|
2378
2440
|
}
|
|
2379
|
-
function
|
|
2380
|
-
return
|
|
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));
|
|
2381
2449
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
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;
|
|
2396
2466
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
return json.event;
|
|
2417
|
-
}
|
|
2418
|
-
return json;
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
// src/layers/llm/stream.ts
|
|
2422
|
-
function handleToolEvent(toolInfo, toolState, emit) {
|
|
2423
|
-
if (toolInfo.type === "start") {
|
|
2424
|
-
toolState.currentTool = toolInfo.name;
|
|
2425
|
-
toolState.inputChunks.length = 0;
|
|
2426
|
-
emit.single({ _tag: "ToolStart", tool: toolInfo.name });
|
|
2427
|
-
return;
|
|
2428
|
-
}
|
|
2429
|
-
if (toolInfo.type === "input_delta" && toolInfo.partialJson) {
|
|
2430
|
-
toolState.inputChunks.push(toolInfo.partialJson);
|
|
2431
|
-
return;
|
|
2432
|
-
}
|
|
2433
|
-
if (toolInfo.type === "end" && toolState.currentTool) {
|
|
2434
|
-
const inputJson = toolState.inputChunks.join("");
|
|
2435
|
-
const input = safeParseJson(inputJson);
|
|
2436
|
-
if (input !== null) {
|
|
2437
|
-
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;
|
|
2438
2486
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
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
|
+
})
|
|
2465
2528
|
);
|
|
2466
|
-
return Effect4.void;
|
|
2467
2529
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
new LLMError({
|
|
2488
|
-
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
|
|
2489
2549
|
})
|
|
2490
|
-
)
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
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",
|
|
2501
2569
|
cause: error
|
|
2502
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
|
+
})
|
|
2503
2585
|
);
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
execute: (prompt) => {
|
|
2514
|
-
return Stream5.unwrap(
|
|
2515
|
-
Effect5.sync(() => {
|
|
2516
|
-
const child = spawn(
|
|
2517
|
-
"claude",
|
|
2518
|
-
[
|
|
2519
|
-
"--permission-mode",
|
|
2520
|
-
"acceptEdits",
|
|
2521
|
-
"--output-format",
|
|
2522
|
-
"stream-json",
|
|
2523
|
-
"--verbose",
|
|
2524
|
-
"--include-partial-messages",
|
|
2525
|
-
"-p",
|
|
2526
|
-
prompt
|
|
2527
|
-
],
|
|
2528
|
-
{
|
|
2529
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
2530
|
-
env: {
|
|
2531
|
-
...process.env,
|
|
2532
|
-
FORCE_COLOR: "1"
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
);
|
|
2536
|
-
return createEventStream(child);
|
|
2537
|
-
})
|
|
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
|
+
)
|
|
2538
2595
|
);
|
|
2539
|
-
}
|
|
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
|
|
2540
2625
|
};
|
|
2541
|
-
var Live = Layer.succeed(
|
|
2542
|
-
var
|
|
2626
|
+
var Live = Layer.succeed(Git, make);
|
|
2627
|
+
var FileSystemGit = {
|
|
2543
2628
|
Live
|
|
2544
2629
|
};
|
|
2545
2630
|
|
|
2546
|
-
// src/layers/
|
|
2547
|
-
init_esm_shims();
|
|
2548
|
-
import { Effect as Effect6, Layer as Layer2, Schema as S2, Stream as Stream6 } from "effect";
|
|
2549
|
-
|
|
2550
|
-
// src/domain/schemas/llm.ts
|
|
2631
|
+
// src/layers/git/memory.ts
|
|
2551
2632
|
init_esm_shims();
|
|
2552
|
-
import {
|
|
2553
|
-
var
|
|
2554
|
-
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
});
|
|
2559
|
-
var ToolUseEventSchema = S.TaggedStruct("ToolUse", {
|
|
2560
|
-
tool: S.String,
|
|
2561
|
-
input: S.Unknown
|
|
2562
|
-
});
|
|
2563
|
-
var ToolEndEventSchema = S.TaggedStruct("ToolEnd", {
|
|
2564
|
-
tool: S.String
|
|
2565
|
-
});
|
|
2566
|
-
var DoneEventSchema = S.TaggedStruct("Done", {
|
|
2567
|
-
output: S.String
|
|
2568
|
-
});
|
|
2569
|
-
var LLMEventSchema = S.Union(
|
|
2570
|
-
TextEventSchema,
|
|
2571
|
-
ToolStartEventSchema,
|
|
2572
|
-
ToolUseEventSchema,
|
|
2573
|
-
ToolEndEventSchema,
|
|
2574
|
-
DoneEventSchema
|
|
2575
|
-
);
|
|
2576
|
-
var decodeLLMEvent = S.decodeUnknown(LLMEventSchema);
|
|
2577
|
-
|
|
2578
|
-
// src/layers/llm/mock.ts
|
|
2579
|
-
var MockLLMConfigSchema = S2.Struct({
|
|
2580
|
-
events: S2.Array(LLMEventSchema),
|
|
2581
|
-
delayMs: S2.optional(S2.Number)
|
|
2582
|
-
});
|
|
2583
|
-
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) {
|
|
2584
2639
|
return {
|
|
2585
|
-
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
return
|
|
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;
|
|
2590
2645
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
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
|
|
2593
2715
|
};
|
|
2594
2716
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
];
|
|
2605
|
-
var defaultMock = createMockLLM({ events: defaultMockEvents });
|
|
2606
|
-
var Live2 = Layer2.succeed(LLM, defaultMock);
|
|
2607
|
-
function layer(config) {
|
|
2608
|
-
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
|
+
);
|
|
2609
2726
|
}
|
|
2610
|
-
var
|
|
2727
|
+
var Live2 = layer();
|
|
2728
|
+
var MemoryGit = {
|
|
2611
2729
|
Live: Live2,
|
|
2612
|
-
layer
|
|
2613
|
-
createMockLLM
|
|
2730
|
+
layer
|
|
2614
2731
|
};
|
|
2615
2732
|
|
|
2616
|
-
// src/layers/
|
|
2733
|
+
// src/layers/guardrails/file-system.ts
|
|
2617
2734
|
init_esm_shims();
|
|
2618
|
-
import {
|
|
2619
|
-
import { join } from "path";
|
|
2620
|
-
import { Effect as
|
|
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";
|
|
2621
2738
|
|
|
2622
2739
|
// src/domain/index.ts
|
|
2623
2740
|
init_esm_shims();
|
|
@@ -2627,55 +2744,55 @@ init_esm_shims();
|
|
|
2627
2744
|
|
|
2628
2745
|
// src/domain/schemas/config.ts
|
|
2629
2746
|
init_esm_shims();
|
|
2630
|
-
import { Schema as
|
|
2631
|
-
var PhasePromptOverridesSchema =
|
|
2632
|
-
breakdown:
|
|
2633
|
-
planning:
|
|
2634
|
-
execution:
|
|
2635
|
-
check:
|
|
2636
|
-
verify:
|
|
2637
|
-
review:
|
|
2638
|
-
completion:
|
|
2639
|
-
});
|
|
2640
|
-
var PromptConfigSchema =
|
|
2641
|
-
systemPrompt:
|
|
2642
|
-
phases:
|
|
2643
|
-
additionalContext:
|
|
2644
|
-
});
|
|
2645
|
-
var LoopConfigSchema =
|
|
2646
|
-
task:
|
|
2647
|
-
maxIterations:
|
|
2648
|
-
verifyCommands:
|
|
2649
|
-
sessionId:
|
|
2650
|
-
branch:
|
|
2651
|
-
push:
|
|
2652
|
-
pr:
|
|
2653
|
-
verbose:
|
|
2654
|
-
prompts:
|
|
2655
|
-
});
|
|
2656
|
-
var LoopSummarySchema =
|
|
2657
|
-
iterations:
|
|
2658
|
-
success:
|
|
2659
|
-
sessionId:
|
|
2660
|
-
completedTasks:
|
|
2661
|
-
durationMs:
|
|
2662
|
-
});
|
|
2663
|
-
var LoopErrorSchema =
|
|
2664
|
-
message:
|
|
2665
|
-
phase:
|
|
2666
|
-
iteration:
|
|
2667
|
-
});
|
|
2668
|
-
var decodeLoopConfig =
|
|
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);
|
|
2669
2786
|
|
|
2670
2787
|
// src/domain/schemas/events.ts
|
|
2671
2788
|
init_esm_shims();
|
|
2672
|
-
import { Schema as
|
|
2789
|
+
import { Schema as S4 } from "effect";
|
|
2673
2790
|
|
|
2674
2791
|
// src/domain/schemas/plan.ts
|
|
2675
2792
|
init_esm_shims();
|
|
2676
|
-
import { Brand, Schema as
|
|
2793
|
+
import { Brand, Schema as S2 } from "effect";
|
|
2677
2794
|
var PlanId = Brand.nominal();
|
|
2678
|
-
var TaskStatusSchema =
|
|
2795
|
+
var TaskStatusSchema = S2.Literal(
|
|
2679
2796
|
"pending",
|
|
2680
2797
|
"planning",
|
|
2681
2798
|
"in_progress",
|
|
@@ -2683,114 +2800,114 @@ var TaskStatusSchema = S4.Literal(
|
|
|
2683
2800
|
"failed",
|
|
2684
2801
|
"skipped"
|
|
2685
2802
|
);
|
|
2686
|
-
var PhaseStatusSchema =
|
|
2803
|
+
var PhaseStatusSchema = S2.Literal(
|
|
2687
2804
|
"pending",
|
|
2688
2805
|
"in_progress",
|
|
2689
2806
|
"done",
|
|
2690
2807
|
"failed"
|
|
2691
2808
|
);
|
|
2692
|
-
var CriterionStatusSchema =
|
|
2693
|
-
var PhaseSchema =
|
|
2694
|
-
id:
|
|
2695
|
-
description:
|
|
2809
|
+
var CriterionStatusSchema = S2.Literal("pending", "passed", "failed");
|
|
2810
|
+
var PhaseSchema = S2.Struct({
|
|
2811
|
+
id: S2.String,
|
|
2812
|
+
description: S2.String,
|
|
2696
2813
|
status: PhaseStatusSchema
|
|
2697
2814
|
});
|
|
2698
|
-
var CriterionSchema =
|
|
2699
|
-
id:
|
|
2700
|
-
description:
|
|
2815
|
+
var CriterionSchema = S2.Struct({
|
|
2816
|
+
id: S2.String,
|
|
2817
|
+
description: S2.String,
|
|
2701
2818
|
status: CriterionStatusSchema,
|
|
2702
|
-
failureReason:
|
|
2819
|
+
failureReason: S2.optional(S2.String)
|
|
2703
2820
|
});
|
|
2704
|
-
var TaskSchema =
|
|
2705
|
-
id:
|
|
2706
|
-
title:
|
|
2707
|
-
description:
|
|
2821
|
+
var TaskSchema = S2.Struct({
|
|
2822
|
+
id: S2.String,
|
|
2823
|
+
title: S2.String,
|
|
2824
|
+
description: S2.String,
|
|
2708
2825
|
status: TaskStatusSchema,
|
|
2709
|
-
phases:
|
|
2710
|
-
criteria:
|
|
2711
|
-
filesToModify:
|
|
2712
|
-
attempts:
|
|
2713
|
-
completionNotes:
|
|
2714
|
-
});
|
|
2715
|
-
var PlanDataSchema =
|
|
2716
|
-
sessionId:
|
|
2717
|
-
createdAt:
|
|
2718
|
-
originalTask:
|
|
2719
|
-
context:
|
|
2720
|
-
tasks:
|
|
2721
|
-
});
|
|
2722
|
-
var PlanSchema =
|
|
2723
|
-
id:
|
|
2724
|
-
sessionId:
|
|
2725
|
-
createdAt:
|
|
2726
|
-
originalTask:
|
|
2727
|
-
context:
|
|
2728
|
-
tasks:
|
|
2729
|
-
});
|
|
2730
|
-
var decodePlan =
|
|
2731
|
-
var decodePlanData =
|
|
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);
|
|
2732
2849
|
|
|
2733
2850
|
// src/domain/schemas/shared.ts
|
|
2734
2851
|
init_esm_shims();
|
|
2735
|
-
import { Schema as
|
|
2736
|
-
var TaskBasicInfoSchema =
|
|
2737
|
-
id:
|
|
2738
|
-
title:
|
|
2739
|
-
description:
|
|
2852
|
+
import { Schema as S3 } from "effect";
|
|
2853
|
+
var TaskBasicInfoSchema = S3.Struct({
|
|
2854
|
+
id: S3.String,
|
|
2855
|
+
title: S3.String,
|
|
2856
|
+
description: S3.String
|
|
2740
2857
|
});
|
|
2741
|
-
var PhaseBasicInfoSchema =
|
|
2742
|
-
id:
|
|
2743
|
-
description:
|
|
2858
|
+
var PhaseBasicInfoSchema = S3.Struct({
|
|
2859
|
+
id: S3.String,
|
|
2860
|
+
description: S3.String
|
|
2744
2861
|
});
|
|
2745
|
-
var CriterionBasicInfoSchema =
|
|
2746
|
-
id:
|
|
2747
|
-
description:
|
|
2862
|
+
var CriterionBasicInfoSchema = S3.Struct({
|
|
2863
|
+
id: S3.String,
|
|
2864
|
+
description: S3.String
|
|
2748
2865
|
});
|
|
2749
|
-
var TasksDefinedDataSchema =
|
|
2750
|
-
tasks:
|
|
2866
|
+
var TasksDefinedDataSchema = S3.Struct({
|
|
2867
|
+
tasks: S3.Array(TaskBasicInfoSchema)
|
|
2751
2868
|
});
|
|
2752
|
-
var PhasesDefinedDataSchema =
|
|
2753
|
-
taskId:
|
|
2754
|
-
phases:
|
|
2869
|
+
var PhasesDefinedDataSchema = S3.Struct({
|
|
2870
|
+
taskId: S3.String,
|
|
2871
|
+
phases: S3.Array(PhaseBasicInfoSchema)
|
|
2755
2872
|
});
|
|
2756
|
-
var CriteriaDefinedDataSchema =
|
|
2757
|
-
taskId:
|
|
2758
|
-
criteria:
|
|
2873
|
+
var CriteriaDefinedDataSchema = S3.Struct({
|
|
2874
|
+
taskId: S3.String,
|
|
2875
|
+
criteria: S3.Array(CriterionBasicInfoSchema)
|
|
2759
2876
|
});
|
|
2760
|
-
var PhaseIdDataSchema =
|
|
2761
|
-
phaseId:
|
|
2877
|
+
var PhaseIdDataSchema = S3.Struct({
|
|
2878
|
+
phaseId: S3.String
|
|
2762
2879
|
});
|
|
2763
|
-
var PhaseFailedDataSchema =
|
|
2764
|
-
phaseId:
|
|
2765
|
-
reason:
|
|
2880
|
+
var PhaseFailedDataSchema = S3.Struct({
|
|
2881
|
+
phaseId: S3.String,
|
|
2882
|
+
reason: S3.String
|
|
2766
2883
|
});
|
|
2767
|
-
var CriterionIdDataSchema =
|
|
2768
|
-
criterionId:
|
|
2884
|
+
var CriterionIdDataSchema = S3.Struct({
|
|
2885
|
+
criterionId: S3.String
|
|
2769
2886
|
});
|
|
2770
|
-
var CriterionFailedDataSchema =
|
|
2771
|
-
criterionId:
|
|
2772
|
-
reason:
|
|
2887
|
+
var CriterionFailedDataSchema = S3.Struct({
|
|
2888
|
+
criterionId: S3.String,
|
|
2889
|
+
reason: S3.String
|
|
2773
2890
|
});
|
|
2774
|
-
var ReviewCompleteDataSchema =
|
|
2775
|
-
changesMade:
|
|
2891
|
+
var ReviewCompleteDataSchema = S3.Struct({
|
|
2892
|
+
changesMade: S3.Boolean
|
|
2776
2893
|
});
|
|
2777
|
-
var TaskCompleteSignalDataSchema =
|
|
2778
|
-
taskId:
|
|
2779
|
-
summary:
|
|
2780
|
-
filesModified:
|
|
2781
|
-
filesCreated:
|
|
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)
|
|
2782
2899
|
});
|
|
2783
|
-
var TaskCompleteDataSchema =
|
|
2784
|
-
taskId:
|
|
2785
|
-
summary:
|
|
2900
|
+
var TaskCompleteDataSchema = S3.Struct({
|
|
2901
|
+
taskId: S3.String,
|
|
2902
|
+
summary: S3.String
|
|
2786
2903
|
});
|
|
2787
2904
|
|
|
2788
2905
|
// src/domain/schemas/events.ts
|
|
2789
|
-
var taggedEvent = (tag, fields) =>
|
|
2790
|
-
var taggedFromData = (tag, dataSchema, extraFields = {}) =>
|
|
2906
|
+
var taggedEvent = (tag, fields) => S4.TaggedStruct(tag, fields);
|
|
2907
|
+
var taggedFromData = (tag, dataSchema, extraFields = {}) => S4.TaggedStruct(tag, { ...dataSchema.fields, ...extraFields });
|
|
2791
2908
|
var LoopStartedEventSchema = taggedEvent("LoopStarted", {
|
|
2792
2909
|
config: LoopConfigSchema,
|
|
2793
|
-
timestamp:
|
|
2910
|
+
timestamp: S4.Number
|
|
2794
2911
|
});
|
|
2795
2912
|
var LoopCompletedEventSchema = taggedEvent("LoopCompleted", {
|
|
2796
2913
|
summary: LoopSummarySchema
|
|
@@ -2799,30 +2916,30 @@ var LoopFailedEventSchema = taggedEvent("LoopFailed", {
|
|
|
2799
2916
|
error: LoopErrorSchema
|
|
2800
2917
|
});
|
|
2801
2918
|
var DiscoveryStartedEventSchema = taggedEvent("DiscoveryStarted", {
|
|
2802
|
-
timestamp:
|
|
2919
|
+
timestamp: S4.Number
|
|
2803
2920
|
});
|
|
2804
2921
|
var DiscoveryCompletedEventSchema = taggedEvent("DiscoveryCompleted", {
|
|
2805
|
-
taskCount:
|
|
2806
|
-
timestamp:
|
|
2922
|
+
taskCount: S4.Number,
|
|
2923
|
+
timestamp: S4.Number
|
|
2807
2924
|
});
|
|
2808
2925
|
var IterationStartedEventSchema = taggedEvent("IterationStarted", {
|
|
2809
|
-
iteration:
|
|
2926
|
+
iteration: S4.Number
|
|
2810
2927
|
});
|
|
2811
2928
|
var IterationCompletedEventSchema = taggedEvent("IterationCompleted", {
|
|
2812
|
-
iteration:
|
|
2929
|
+
iteration: S4.Number
|
|
2813
2930
|
});
|
|
2814
2931
|
var LLMTextEventSchema = taggedEvent("LLMText", {
|
|
2815
|
-
text:
|
|
2932
|
+
text: S4.String
|
|
2816
2933
|
});
|
|
2817
2934
|
var LLMToolStartEventSchema = taggedEvent("LLMToolStart", {
|
|
2818
|
-
tool:
|
|
2935
|
+
tool: S4.String
|
|
2819
2936
|
});
|
|
2820
2937
|
var LLMToolUseEventSchema = taggedEvent("LLMToolUse", {
|
|
2821
|
-
tool:
|
|
2822
|
-
input:
|
|
2938
|
+
tool: S4.String,
|
|
2939
|
+
input: S4.Unknown
|
|
2823
2940
|
});
|
|
2824
2941
|
var LLMToolEndEventSchema = taggedEvent("LLMToolEnd", {
|
|
2825
|
-
tool:
|
|
2942
|
+
tool: S4.String
|
|
2826
2943
|
});
|
|
2827
2944
|
var TasksDefinedEventSchema = taggedFromData(
|
|
2828
2945
|
"TasksDefined",
|
|
@@ -2839,17 +2956,17 @@ var CriteriaDefinedEventSchema = taggedFromData(
|
|
|
2839
2956
|
var PhaseStartedEventSchema = taggedFromData(
|
|
2840
2957
|
"PhaseStarted",
|
|
2841
2958
|
PhaseIdDataSchema,
|
|
2842
|
-
{ timestamp:
|
|
2959
|
+
{ timestamp: S4.Number }
|
|
2843
2960
|
);
|
|
2844
2961
|
var PhaseCompletedEventSchema = taggedFromData(
|
|
2845
2962
|
"PhaseCompleted",
|
|
2846
2963
|
PhaseIdDataSchema,
|
|
2847
|
-
{ timestamp:
|
|
2964
|
+
{ timestamp: S4.Number }
|
|
2848
2965
|
);
|
|
2849
2966
|
var PhaseFailedEventSchema = taggedFromData(
|
|
2850
2967
|
"PhaseFailed",
|
|
2851
2968
|
PhaseFailedDataSchema,
|
|
2852
|
-
{ timestamp:
|
|
2969
|
+
{ timestamp: S4.Number }
|
|
2853
2970
|
);
|
|
2854
2971
|
var CriterionPassedEventSchema = taggedFromData(
|
|
2855
2972
|
"CriterionPassed",
|
|
@@ -2859,9 +2976,9 @@ var CriterionFailedEventSchema = taggedFromData(
|
|
|
2859
2976
|
"CriterionFailed",
|
|
2860
2977
|
CriterionFailedDataSchema
|
|
2861
2978
|
);
|
|
2862
|
-
var CheckPassedEventSchema =
|
|
2979
|
+
var CheckPassedEventSchema = S4.TaggedStruct("CheckPassed", {});
|
|
2863
2980
|
var CheckFailedEventSchema = taggedEvent("CheckFailed", {
|
|
2864
|
-
failedCriteria:
|
|
2981
|
+
failedCriteria: S4.Array(S4.String)
|
|
2865
2982
|
});
|
|
2866
2983
|
var ReviewCompleteEventSchema = taggedFromData(
|
|
2867
2984
|
"ReviewComplete",
|
|
@@ -2870,7 +2987,7 @@ var ReviewCompleteEventSchema = taggedFromData(
|
|
|
2870
2987
|
var TaskCompletedEventSchema = taggedFromData(
|
|
2871
2988
|
"TaskCompleted",
|
|
2872
2989
|
TaskCompleteDataSchema,
|
|
2873
|
-
{ timestamp:
|
|
2990
|
+
{ timestamp: S4.Number }
|
|
2874
2991
|
);
|
|
2875
2992
|
var PlanCreatedEventSchema = taggedEvent("PlanCreated", {
|
|
2876
2993
|
plan: PlanSchema
|
|
@@ -2879,11 +2996,43 @@ var PlanUpdatedEventSchema = taggedEvent("PlanUpdated", {
|
|
|
2879
2996
|
plan: PlanSchema
|
|
2880
2997
|
});
|
|
2881
2998
|
var PlanUpdateFailedEventSchema = taggedEvent("PlanUpdateFailed", {
|
|
2882
|
-
operation:
|
|
2883
|
-
error:
|
|
2884
|
-
planId:
|
|
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
|
|
2885
3034
|
});
|
|
2886
|
-
var DomainEventSchema =
|
|
3035
|
+
var DomainEventSchema = S4.Union(
|
|
2887
3036
|
LoopStartedEventSchema,
|
|
2888
3037
|
LoopCompletedEventSchema,
|
|
2889
3038
|
LoopFailedEventSchema,
|
|
@@ -2909,7 +3058,12 @@ var DomainEventSchema = S6.Union(
|
|
|
2909
3058
|
TaskCompletedEventSchema,
|
|
2910
3059
|
PlanCreatedEventSchema,
|
|
2911
3060
|
PlanUpdatedEventSchema,
|
|
2912
|
-
PlanUpdateFailedEventSchema
|
|
3061
|
+
PlanUpdateFailedEventSchema,
|
|
3062
|
+
LearningRecordedEventSchema,
|
|
3063
|
+
GuardrailAddedEventSchema,
|
|
3064
|
+
ProgressUpdatedEventSchema,
|
|
3065
|
+
WorktreeCreatedEventSchema,
|
|
3066
|
+
WorktreeRemovedEventSchema
|
|
2913
3067
|
);
|
|
2914
3068
|
var DomainEventUtils = {
|
|
2915
3069
|
isLLMEvent: (e) => e._tag.startsWith("LLM"),
|
|
@@ -2919,6 +3073,55 @@ var DomainEventUtils = {
|
|
|
2919
3073
|
isDiscoveryEvent: (e) => e._tag.startsWith("Discovery")
|
|
2920
3074
|
};
|
|
2921
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
|
+
|
|
2922
3125
|
// src/domain/schemas/logger.ts
|
|
2923
3126
|
init_esm_shims();
|
|
2924
3127
|
import { Schema as S7 } from "effect";
|
|
@@ -2947,29 +3150,57 @@ var RunOptionsDataSchema = S8.Struct({
|
|
|
2947
3150
|
consumer: S8.optional(ConsumerTypeSchema)
|
|
2948
3151
|
});
|
|
2949
3152
|
|
|
2950
|
-
// src/domain/schemas/
|
|
3153
|
+
// src/domain/schemas/progress.ts
|
|
2951
3154
|
init_esm_shims();
|
|
2952
3155
|
import { Schema as S9 } from "effect";
|
|
2953
|
-
var
|
|
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(
|
|
2954
3183
|
"active",
|
|
2955
3184
|
"completed",
|
|
2956
3185
|
"failed",
|
|
2957
3186
|
"paused"
|
|
2958
3187
|
);
|
|
2959
|
-
var SessionSchema =
|
|
2960
|
-
id:
|
|
2961
|
-
createdAt:
|
|
3188
|
+
var SessionSchema = S10.Struct({
|
|
3189
|
+
id: S10.String,
|
|
3190
|
+
createdAt: S10.String,
|
|
2962
3191
|
status: SessionStatusSchema,
|
|
2963
|
-
originalTask:
|
|
2964
|
-
completedTasks:
|
|
2965
|
-
currentTaskId:
|
|
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)
|
|
2966
3197
|
});
|
|
2967
|
-
var decodeSession =
|
|
3198
|
+
var decodeSession = S10.decodeUnknown(SessionSchema);
|
|
2968
3199
|
|
|
2969
3200
|
// src/domain/schemas/signals.ts
|
|
2970
3201
|
init_esm_shims();
|
|
2971
|
-
import { Schema as
|
|
2972
|
-
var taggedFromData2 = (tag, dataSchema) =>
|
|
3202
|
+
import { Schema as S11 } from "effect";
|
|
3203
|
+
var taggedFromData2 = (tag, dataSchema) => S11.TaggedStruct(tag, dataSchema.fields);
|
|
2973
3204
|
var TasksDefinedSignalSchema = taggedFromData2(
|
|
2974
3205
|
"TasksDefined",
|
|
2975
3206
|
TasksDefinedDataSchema
|
|
@@ -3002,8 +3233,8 @@ var CriterionFailedSignalSchema = taggedFromData2(
|
|
|
3002
3233
|
"CriterionFailed",
|
|
3003
3234
|
CriterionFailedDataSchema
|
|
3004
3235
|
);
|
|
3005
|
-
var CheckPassedSignalSchema =
|
|
3006
|
-
var CheckFailedSignalSchema =
|
|
3236
|
+
var CheckPassedSignalSchema = S11.TaggedStruct("CheckPassed", {});
|
|
3237
|
+
var CheckFailedSignalSchema = S11.TaggedStruct("CheckFailed", {});
|
|
3007
3238
|
var ReviewCompleteSignalSchema = taggedFromData2(
|
|
3008
3239
|
"ReviewComplete",
|
|
3009
3240
|
ReviewCompleteDataSchema
|
|
@@ -3012,8 +3243,23 @@ var TaskCompleteSignalSchema = taggedFromData2(
|
|
|
3012
3243
|
"TaskComplete",
|
|
3013
3244
|
TaskCompleteSignalDataSchema
|
|
3014
3245
|
);
|
|
3015
|
-
var LoopCompleteSignalSchema =
|
|
3016
|
-
var
|
|
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(
|
|
3017
3263
|
TasksDefinedSignalSchema,
|
|
3018
3264
|
PhasesDefinedSignalSchema,
|
|
3019
3265
|
CriteriaDefinedSignalSchema,
|
|
@@ -3026,26 +3272,28 @@ var SignalSchema = S10.Union(
|
|
|
3026
3272
|
CheckFailedSignalSchema,
|
|
3027
3273
|
ReviewCompleteSignalSchema,
|
|
3028
3274
|
TaskCompleteSignalSchema,
|
|
3029
|
-
LoopCompleteSignalSchema
|
|
3275
|
+
LoopCompleteSignalSchema,
|
|
3276
|
+
LearningSignalSchema,
|
|
3277
|
+
GuardrailSignalSchema
|
|
3030
3278
|
);
|
|
3031
|
-
var decodeSignal =
|
|
3032
|
-
var decodeSignalSync =
|
|
3279
|
+
var decodeSignal = S11.decodeUnknown(SignalSchema);
|
|
3280
|
+
var decodeSignalSync = S11.decodeUnknownSync(SignalSchema);
|
|
3033
3281
|
|
|
3034
3282
|
// src/domain/schemas/task-generation.ts
|
|
3035
3283
|
init_esm_shims();
|
|
3036
|
-
import { Schema as
|
|
3037
|
-
var GeneratedTaskStatusSchema =
|
|
3284
|
+
import { Schema as S12 } from "effect";
|
|
3285
|
+
var GeneratedTaskStatusSchema = S12.Literal(
|
|
3038
3286
|
"pending",
|
|
3039
3287
|
"in_progress",
|
|
3040
3288
|
"done",
|
|
3041
3289
|
"failed"
|
|
3042
3290
|
);
|
|
3043
|
-
var GeneratedTaskSchema =
|
|
3044
|
-
id:
|
|
3045
|
-
title:
|
|
3291
|
+
var GeneratedTaskSchema = S12.Struct({
|
|
3292
|
+
id: S12.String,
|
|
3293
|
+
title: S12.String,
|
|
3046
3294
|
status: GeneratedTaskStatusSchema
|
|
3047
3295
|
});
|
|
3048
|
-
var GeneratedTaskListSchema =
|
|
3296
|
+
var GeneratedTaskListSchema = S12.Array(GeneratedTaskSchema);
|
|
3049
3297
|
var STATUS_ICONS = {
|
|
3050
3298
|
done: "[x]",
|
|
3051
3299
|
in_progress: "[~]",
|
|
@@ -3104,15 +3352,15 @@ function parseTasksMd(content) {
|
|
|
3104
3352
|
|
|
3105
3353
|
// src/domain/schemas/tui.ts
|
|
3106
3354
|
init_esm_shims();
|
|
3107
|
-
import { Schema as
|
|
3108
|
-
var ViewModeSchema =
|
|
3109
|
-
var LoopStatusSchema =
|
|
3355
|
+
import { Schema as S13 } from "effect";
|
|
3356
|
+
var ViewModeSchema = S13.Literal("logs", "tasks", "detail");
|
|
3357
|
+
var LoopStatusSchema = S13.Literal(
|
|
3110
3358
|
"idle",
|
|
3111
3359
|
"running",
|
|
3112
3360
|
"complete",
|
|
3113
3361
|
"error"
|
|
3114
3362
|
);
|
|
3115
|
-
var ExecutionModeSchema =
|
|
3363
|
+
var ExecutionModeSchema = S13.Literal(
|
|
3116
3364
|
"idle",
|
|
3117
3365
|
"discovery",
|
|
3118
3366
|
"breakdown",
|
|
@@ -3122,98 +3370,566 @@ var ExecutionModeSchema = S12.Literal(
|
|
|
3122
3370
|
"verifying",
|
|
3123
3371
|
"reviewing"
|
|
3124
3372
|
);
|
|
3125
|
-
var TUIPhaseStatusSchema =
|
|
3373
|
+
var TUIPhaseStatusSchema = S13.Literal(
|
|
3126
3374
|
"pending",
|
|
3127
3375
|
"in_progress",
|
|
3128
3376
|
"done",
|
|
3129
3377
|
"failed"
|
|
3130
3378
|
);
|
|
3131
|
-
var TUICriterionStatusSchema =
|
|
3379
|
+
var TUICriterionStatusSchema = S13.Literal(
|
|
3132
3380
|
"pending",
|
|
3133
3381
|
"passed",
|
|
3134
3382
|
"failed"
|
|
3135
3383
|
);
|
|
3136
|
-
var TUITaskStatusSchema =
|
|
3384
|
+
var TUITaskStatusSchema = S13.Literal(
|
|
3137
3385
|
"pending",
|
|
3138
3386
|
"in_progress",
|
|
3139
3387
|
"done",
|
|
3140
3388
|
"failed"
|
|
3141
3389
|
);
|
|
3142
|
-
var TUIPhaseSchema =
|
|
3143
|
-
id:
|
|
3144
|
-
description:
|
|
3390
|
+
var TUIPhaseSchema = S13.Struct({
|
|
3391
|
+
id: S13.String,
|
|
3392
|
+
description: S13.String,
|
|
3145
3393
|
status: TUIPhaseStatusSchema,
|
|
3146
|
-
startedAt:
|
|
3147
|
-
completedAt:
|
|
3394
|
+
startedAt: S13.optional(S13.Number),
|
|
3395
|
+
completedAt: S13.optional(S13.Number)
|
|
3148
3396
|
});
|
|
3149
|
-
var TUICriterionSchema =
|
|
3150
|
-
id:
|
|
3151
|
-
description:
|
|
3397
|
+
var TUICriterionSchema = S13.Struct({
|
|
3398
|
+
id: S13.String,
|
|
3399
|
+
description: S13.String,
|
|
3152
3400
|
status: TUICriterionStatusSchema,
|
|
3153
|
-
failureReason:
|
|
3401
|
+
failureReason: S13.optional(S13.String)
|
|
3154
3402
|
});
|
|
3155
|
-
var TUITaskSchema =
|
|
3156
|
-
id:
|
|
3157
|
-
title:
|
|
3403
|
+
var TUITaskSchema = S13.Struct({
|
|
3404
|
+
id: S13.String,
|
|
3405
|
+
title: S13.String,
|
|
3158
3406
|
status: TUITaskStatusSchema,
|
|
3159
|
-
phases:
|
|
3160
|
-
criteria:
|
|
3161
|
-
startedAt:
|
|
3162
|
-
completedAt:
|
|
3407
|
+
phases: S13.Array(TUIPhaseSchema),
|
|
3408
|
+
criteria: S13.Array(TUICriterionSchema),
|
|
3409
|
+
startedAt: S13.optional(S13.Number),
|
|
3410
|
+
completedAt: S13.optional(S13.Number)
|
|
3163
3411
|
});
|
|
3164
|
-
var TUIStateSchema =
|
|
3412
|
+
var TUIStateSchema = S13.Struct({
|
|
3165
3413
|
// Loop info
|
|
3166
|
-
task:
|
|
3167
|
-
iteration:
|
|
3168
|
-
maxIterations:
|
|
3414
|
+
task: S13.String,
|
|
3415
|
+
iteration: S13.Number,
|
|
3416
|
+
maxIterations: S13.Number,
|
|
3169
3417
|
status: LoopStatusSchema,
|
|
3170
|
-
startTime:
|
|
3418
|
+
startTime: S13.Number,
|
|
3171
3419
|
// Discovery phase
|
|
3172
|
-
discoveryInProgress:
|
|
3173
|
-
discoveryCompleted:
|
|
3420
|
+
discoveryInProgress: S13.Boolean,
|
|
3421
|
+
discoveryCompleted: S13.Boolean,
|
|
3174
3422
|
// Current activity
|
|
3175
3423
|
executionMode: ExecutionModeSchema,
|
|
3176
|
-
currentTool:
|
|
3177
|
-
currentTaskId:
|
|
3424
|
+
currentTool: S13.optional(S13.String),
|
|
3425
|
+
currentTaskId: S13.optional(S13.String),
|
|
3178
3426
|
// Output
|
|
3179
|
-
outputLines:
|
|
3180
|
-
partialLine:
|
|
3427
|
+
outputLines: S13.Array(S13.String),
|
|
3428
|
+
partialLine: S13.String,
|
|
3181
3429
|
// Tasks
|
|
3182
|
-
tasks:
|
|
3430
|
+
tasks: S13.Array(TUITaskSchema),
|
|
3183
3431
|
// Navigation
|
|
3184
3432
|
viewMode: ViewModeSchema,
|
|
3185
|
-
selectedTaskIndex:
|
|
3186
|
-
scrollOffset:
|
|
3187
|
-
userScrolled:
|
|
3433
|
+
selectedTaskIndex: S13.Number,
|
|
3434
|
+
scrollOffset: S13.Number,
|
|
3435
|
+
userScrolled: S13.Boolean,
|
|
3188
3436
|
// Git
|
|
3189
|
-
gitBranch:
|
|
3190
|
-
gitPushed:
|
|
3191
|
-
prUrl:
|
|
3437
|
+
gitBranch: S13.optional(S13.String),
|
|
3438
|
+
gitPushed: S13.Boolean,
|
|
3439
|
+
prUrl: S13.optional(S13.String)
|
|
3192
3440
|
});
|
|
3193
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
|
+
|
|
3194
3910
|
// src/services/plan-store.ts
|
|
3195
3911
|
init_esm_shims();
|
|
3196
|
-
import { Context as
|
|
3197
|
-
var PlanStore = class extends
|
|
3912
|
+
import { Context as Context4 } from "effect";
|
|
3913
|
+
var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
|
|
3198
3914
|
};
|
|
3199
3915
|
|
|
3200
3916
|
// src/layers/plan/file-system.ts
|
|
3201
|
-
var
|
|
3202
|
-
function
|
|
3203
|
-
return
|
|
3204
|
-
try: () =>
|
|
3917
|
+
var PLANS_DIR2 = ".ferix/plans";
|
|
3918
|
+
function ensureDir2(dirPath) {
|
|
3919
|
+
return Effect11.tryPromise({
|
|
3920
|
+
try: () => mkdir3(dirPath, { recursive: true }),
|
|
3205
3921
|
catch: (error) => new PlanStoreError({
|
|
3206
3922
|
message: `Failed to create directory: ${dirPath}`,
|
|
3207
3923
|
operation: "create",
|
|
3208
3924
|
cause: error
|
|
3209
3925
|
})
|
|
3210
|
-
}).pipe(
|
|
3926
|
+
}).pipe(Effect11.asVoid);
|
|
3211
3927
|
}
|
|
3212
|
-
function
|
|
3213
|
-
return
|
|
3928
|
+
function getSessionDir2(sessionId) {
|
|
3929
|
+
return join3(process.cwd(), PLANS_DIR2, sessionId);
|
|
3214
3930
|
}
|
|
3215
3931
|
function getPlanPath(sessionId, planId) {
|
|
3216
|
-
return
|
|
3932
|
+
return join3(getSessionDir2(sessionId), `${planId}.json`);
|
|
3217
3933
|
}
|
|
3218
3934
|
function generatePlanId(taskNumber) {
|
|
3219
3935
|
return PlanId(`task-${taskNumber}`);
|
|
@@ -3222,8 +3938,8 @@ function serializePlan(plan) {
|
|
|
3222
3938
|
return JSON.stringify(plan, null, 2);
|
|
3223
3939
|
}
|
|
3224
3940
|
function deserializePlan(json, planId) {
|
|
3225
|
-
return
|
|
3226
|
-
const parsed = yield*
|
|
3941
|
+
return Effect11.gen(function* () {
|
|
3942
|
+
const parsed = yield* Effect11.try({
|
|
3227
3943
|
try: () => JSON.parse(json),
|
|
3228
3944
|
catch: (error) => new PlanStoreError({
|
|
3229
3945
|
message: `Invalid JSON in plan file: ${String(error)}`,
|
|
@@ -3232,7 +3948,7 @@ function deserializePlan(json, planId) {
|
|
|
3232
3948
|
})
|
|
3233
3949
|
});
|
|
3234
3950
|
const validated = yield* decodePlanData(parsed).pipe(
|
|
3235
|
-
|
|
3951
|
+
Effect11.mapError(
|
|
3236
3952
|
(error) => new PlanStoreError({
|
|
3237
3953
|
message: `Plan validation failed: ${String(error)}`,
|
|
3238
3954
|
operation: "load",
|
|
@@ -3246,11 +3962,11 @@ function deserializePlan(json, planId) {
|
|
|
3246
3962
|
};
|
|
3247
3963
|
});
|
|
3248
3964
|
}
|
|
3249
|
-
var
|
|
3250
|
-
create: (sessionId, plan) =>
|
|
3251
|
-
const sessionDir =
|
|
3252
|
-
yield*
|
|
3253
|
-
const existingPlans = yield*
|
|
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({
|
|
3254
3970
|
try: async () => {
|
|
3255
3971
|
try {
|
|
3256
3972
|
const files = await readdir(sessionDir);
|
|
@@ -3267,8 +3983,8 @@ var make2 = {
|
|
|
3267
3983
|
const planId = generatePlanId(existingPlans + 1);
|
|
3268
3984
|
const planPath = getPlanPath(sessionId, planId);
|
|
3269
3985
|
const fullPlan = { ...plan, id: planId };
|
|
3270
|
-
yield*
|
|
3271
|
-
try: () =>
|
|
3986
|
+
yield* Effect11.tryPromise({
|
|
3987
|
+
try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
|
|
3272
3988
|
catch: (error) => new PlanStoreError({
|
|
3273
3989
|
message: `Failed to write plan file: ${planPath}`,
|
|
3274
3990
|
operation: "create",
|
|
@@ -3277,11 +3993,11 @@ var make2 = {
|
|
|
3277
3993
|
});
|
|
3278
3994
|
return planId;
|
|
3279
3995
|
}),
|
|
3280
|
-
load: (planId, sessionId) =>
|
|
3996
|
+
load: (planId, sessionId) => Effect11.gen(function* () {
|
|
3281
3997
|
if (sessionId) {
|
|
3282
3998
|
const planPath = getPlanPath(sessionId, planId);
|
|
3283
|
-
const content = yield*
|
|
3284
|
-
try: () =>
|
|
3999
|
+
const content = yield* Effect11.tryPromise({
|
|
4000
|
+
try: () => readFile2(planPath, "utf-8"),
|
|
3285
4001
|
catch: (error) => new PlanStoreError({
|
|
3286
4002
|
message: `Failed to read plan file: ${planPath}`,
|
|
3287
4003
|
operation: "load",
|
|
@@ -3290,9 +4006,9 @@ var make2 = {
|
|
|
3290
4006
|
});
|
|
3291
4007
|
return yield* deserializePlan(content, planId);
|
|
3292
4008
|
}
|
|
3293
|
-
const sessionDirs = yield*
|
|
4009
|
+
const sessionDirs = yield* Effect11.tryPromise({
|
|
3294
4010
|
try: async () => {
|
|
3295
|
-
const plansDir =
|
|
4011
|
+
const plansDir = join3(process.cwd(), PLANS_DIR2);
|
|
3296
4012
|
const dirs = await readdir(plansDir);
|
|
3297
4013
|
return dirs;
|
|
3298
4014
|
},
|
|
@@ -3304,19 +4020,19 @@ var make2 = {
|
|
|
3304
4020
|
});
|
|
3305
4021
|
for (const sid of sessionDirs) {
|
|
3306
4022
|
const planPath = getPlanPath(sid, planId);
|
|
3307
|
-
const exists = yield*
|
|
4023
|
+
const exists = yield* Effect11.tryPromise({
|
|
3308
4024
|
try: async () => {
|
|
3309
|
-
await
|
|
4025
|
+
await access2(planPath);
|
|
3310
4026
|
return true;
|
|
3311
4027
|
},
|
|
3312
4028
|
catch: () => new PlanStoreError({
|
|
3313
4029
|
message: "File not found",
|
|
3314
4030
|
operation: "load"
|
|
3315
4031
|
})
|
|
3316
|
-
}).pipe(
|
|
4032
|
+
}).pipe(Effect11.orElseSucceed(() => false));
|
|
3317
4033
|
if (exists) {
|
|
3318
|
-
const content = yield*
|
|
3319
|
-
try: () =>
|
|
4034
|
+
const content = yield* Effect11.tryPromise({
|
|
4035
|
+
try: () => readFile2(planPath, "utf-8"),
|
|
3320
4036
|
catch: (error) => new PlanStoreError({
|
|
3321
4037
|
message: `Failed to read plan file: ${planPath}`,
|
|
3322
4038
|
operation: "load",
|
|
@@ -3326,17 +4042,17 @@ var make2 = {
|
|
|
3326
4042
|
return yield* deserializePlan(content, planId);
|
|
3327
4043
|
}
|
|
3328
4044
|
}
|
|
3329
|
-
return yield*
|
|
4045
|
+
return yield* Effect11.fail(
|
|
3330
4046
|
new PlanStoreError({
|
|
3331
4047
|
message: `Plan not found: ${planId}`,
|
|
3332
4048
|
operation: "load"
|
|
3333
4049
|
})
|
|
3334
4050
|
);
|
|
3335
4051
|
}),
|
|
3336
|
-
update: (planId, plan) =>
|
|
4052
|
+
update: (planId, plan) => Effect11.gen(function* () {
|
|
3337
4053
|
const planPath = getPlanPath(plan.sessionId, planId);
|
|
3338
|
-
yield*
|
|
3339
|
-
try: () =>
|
|
4054
|
+
yield* Effect11.tryPromise({
|
|
4055
|
+
try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
|
|
3340
4056
|
catch: (error) => new PlanStoreError({
|
|
3341
4057
|
message: `Failed to update plan file: ${planPath}`,
|
|
3342
4058
|
operation: "update",
|
|
@@ -3344,9 +4060,9 @@ var make2 = {
|
|
|
3344
4060
|
})
|
|
3345
4061
|
});
|
|
3346
4062
|
}),
|
|
3347
|
-
list: (sessionId) =>
|
|
3348
|
-
const sessionDir =
|
|
3349
|
-
const files = yield*
|
|
4063
|
+
list: (sessionId) => Effect11.gen(function* () {
|
|
4064
|
+
const sessionDir = getSessionDir2(sessionId);
|
|
4065
|
+
const files = yield* Effect11.tryPromise({
|
|
3350
4066
|
try: async () => {
|
|
3351
4067
|
try {
|
|
3352
4068
|
return await readdir(sessionDir);
|
|
@@ -3363,18 +4079,18 @@ var make2 = {
|
|
|
3363
4079
|
return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
|
|
3364
4080
|
})
|
|
3365
4081
|
};
|
|
3366
|
-
var
|
|
4082
|
+
var Live7 = Layer7.succeed(PlanStore, make4);
|
|
3367
4083
|
var FileSystemPlan = {
|
|
3368
|
-
Live:
|
|
4084
|
+
Live: Live7
|
|
3369
4085
|
};
|
|
3370
4086
|
|
|
3371
4087
|
// src/layers/plan/memory.ts
|
|
3372
4088
|
init_esm_shims();
|
|
3373
|
-
import { Effect as
|
|
4089
|
+
import { Effect as Effect12, Layer as Layer8, Ref as Ref5 } from "effect";
|
|
3374
4090
|
function createMemoryPlanStore(stateRef) {
|
|
3375
4091
|
return {
|
|
3376
|
-
create: (sessionId, plan) =>
|
|
3377
|
-
const state = yield*
|
|
4092
|
+
create: (sessionId, plan) => Effect12.gen(function* () {
|
|
4093
|
+
const state = yield* Ref5.get(stateRef);
|
|
3378
4094
|
if (!state.has(sessionId)) {
|
|
3379
4095
|
state.set(sessionId, /* @__PURE__ */ new Map());
|
|
3380
4096
|
}
|
|
@@ -3385,18 +4101,18 @@ function createMemoryPlanStore(stateRef) {
|
|
|
3385
4101
|
const planId = PlanId(`task-${sessionPlans.size + 1}`);
|
|
3386
4102
|
const fullPlan = { ...plan, id: planId };
|
|
3387
4103
|
sessionPlans.set(planId, fullPlan);
|
|
3388
|
-
yield*
|
|
4104
|
+
yield* Ref5.set(stateRef, state);
|
|
3389
4105
|
return planId;
|
|
3390
4106
|
}),
|
|
3391
|
-
load: (planId, sessionId) =>
|
|
3392
|
-
const state = yield*
|
|
4107
|
+
load: (planId, sessionId) => Effect12.gen(function* () {
|
|
4108
|
+
const state = yield* Ref5.get(stateRef);
|
|
3393
4109
|
if (sessionId) {
|
|
3394
4110
|
const sessionPlans = state.get(sessionId);
|
|
3395
4111
|
const plan = sessionPlans?.get(planId);
|
|
3396
4112
|
if (plan) {
|
|
3397
4113
|
return plan;
|
|
3398
4114
|
}
|
|
3399
|
-
return yield*
|
|
4115
|
+
return yield* Effect12.fail(
|
|
3400
4116
|
new PlanStoreError({
|
|
3401
4117
|
message: `Plan not found: ${planId}`,
|
|
3402
4118
|
operation: "load"
|
|
@@ -3409,18 +4125,18 @@ function createMemoryPlanStore(stateRef) {
|
|
|
3409
4125
|
return plan;
|
|
3410
4126
|
}
|
|
3411
4127
|
}
|
|
3412
|
-
return yield*
|
|
4128
|
+
return yield* Effect12.fail(
|
|
3413
4129
|
new PlanStoreError({
|
|
3414
4130
|
message: `Plan not found: ${planId}`,
|
|
3415
4131
|
operation: "load"
|
|
3416
4132
|
})
|
|
3417
4133
|
);
|
|
3418
4134
|
}),
|
|
3419
|
-
update: (planId, plan) =>
|
|
3420
|
-
const state = yield*
|
|
4135
|
+
update: (planId, plan) => Effect12.gen(function* () {
|
|
4136
|
+
const state = yield* Ref5.get(stateRef);
|
|
3421
4137
|
const sessionPlans = state.get(plan.sessionId);
|
|
3422
4138
|
if (!sessionPlans) {
|
|
3423
|
-
return yield*
|
|
4139
|
+
return yield* Effect12.fail(
|
|
3424
4140
|
new PlanStoreError({
|
|
3425
4141
|
message: `Session not found: ${plan.sessionId}`,
|
|
3426
4142
|
operation: "update"
|
|
@@ -3428,10 +4144,10 @@ function createMemoryPlanStore(stateRef) {
|
|
|
3428
4144
|
);
|
|
3429
4145
|
}
|
|
3430
4146
|
sessionPlans.set(planId, plan);
|
|
3431
|
-
yield*
|
|
4147
|
+
yield* Ref5.set(stateRef, state);
|
|
3432
4148
|
}),
|
|
3433
|
-
list: (sessionId) =>
|
|
3434
|
-
const state = yield*
|
|
4149
|
+
list: (sessionId) => Effect12.gen(function* () {
|
|
4150
|
+
const state = yield* Ref5.get(stateRef);
|
|
3435
4151
|
const sessionPlans = state.get(sessionId);
|
|
3436
4152
|
if (!sessionPlans) {
|
|
3437
4153
|
return [];
|
|
@@ -3440,32 +4156,236 @@ function createMemoryPlanStore(stateRef) {
|
|
|
3440
4156
|
})
|
|
3441
4157
|
};
|
|
3442
4158
|
}
|
|
3443
|
-
function
|
|
3444
|
-
return
|
|
4159
|
+
function layer4() {
|
|
4160
|
+
return Layer8.effect(
|
|
3445
4161
|
PlanStore,
|
|
3446
|
-
|
|
3447
|
-
const stateRef = yield*
|
|
4162
|
+
Effect12.gen(function* () {
|
|
4163
|
+
const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
|
|
3448
4164
|
return createMemoryPlanStore(stateRef);
|
|
3449
4165
|
})
|
|
3450
4166
|
);
|
|
3451
4167
|
}
|
|
3452
|
-
var
|
|
4168
|
+
var Live8 = layer4();
|
|
3453
4169
|
var MemoryPlan = {
|
|
3454
|
-
Live:
|
|
3455
|
-
layer:
|
|
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
|
|
3456
4376
|
};
|
|
3457
4377
|
|
|
3458
4378
|
// src/layers/session/file-system.ts
|
|
3459
4379
|
init_esm_shims();
|
|
3460
|
-
import { mkdir as
|
|
3461
|
-
import { join as
|
|
3462
|
-
import { DateTime, Effect as
|
|
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";
|
|
3463
4383
|
import { humanId } from "human-id";
|
|
3464
4384
|
|
|
3465
4385
|
// src/services/session-store.ts
|
|
3466
4386
|
init_esm_shims();
|
|
3467
|
-
import { Context as
|
|
3468
|
-
var SessionStore = class extends
|
|
4387
|
+
import { Context as Context6 } from "effect";
|
|
4388
|
+
var SessionStore = class extends Context6.Tag("@ferix/SessionStore")() {
|
|
3469
4389
|
};
|
|
3470
4390
|
|
|
3471
4391
|
// src/layers/session/file-system.ts
|
|
@@ -3474,25 +4394,25 @@ function generateSessionId(timestampMs) {
|
|
|
3474
4394
|
const id = humanId({ separator: "-", capitalize: false });
|
|
3475
4395
|
return `${id}-${timestampMs}`;
|
|
3476
4396
|
}
|
|
3477
|
-
function
|
|
3478
|
-
return
|
|
3479
|
-
try: () =>
|
|
4397
|
+
function ensureDir4(dirPath) {
|
|
4398
|
+
return Effect15.tryPromise({
|
|
4399
|
+
try: () => mkdir5(dirPath, { recursive: true }),
|
|
3480
4400
|
catch: (error) => new SessionStoreError({
|
|
3481
4401
|
message: `Failed to create directory: ${dirPath}`,
|
|
3482
4402
|
operation: "create",
|
|
3483
4403
|
cause: error
|
|
3484
4404
|
})
|
|
3485
|
-
}).pipe(
|
|
4405
|
+
}).pipe(Effect15.asVoid);
|
|
3486
4406
|
}
|
|
3487
4407
|
function getSessionPath(sessionId) {
|
|
3488
|
-
return
|
|
4408
|
+
return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
|
|
3489
4409
|
}
|
|
3490
4410
|
function serializeSession(session) {
|
|
3491
4411
|
return JSON.stringify(session, null, 2);
|
|
3492
4412
|
}
|
|
3493
4413
|
function deserializeSession(json) {
|
|
3494
|
-
return
|
|
3495
|
-
const parsed = yield*
|
|
4414
|
+
return Effect15.gen(function* () {
|
|
4415
|
+
const parsed = yield* Effect15.try({
|
|
3496
4416
|
try: () => JSON.parse(json),
|
|
3497
4417
|
catch: (error) => new SessionStoreError({
|
|
3498
4418
|
message: `Invalid JSON in session file: ${String(error)}`,
|
|
@@ -3501,7 +4421,7 @@ function deserializeSession(json) {
|
|
|
3501
4421
|
})
|
|
3502
4422
|
});
|
|
3503
4423
|
const validated = yield* decodeSession(parsed).pipe(
|
|
3504
|
-
|
|
4424
|
+
Effect15.mapError(
|
|
3505
4425
|
(error) => new SessionStoreError({
|
|
3506
4426
|
message: `Session validation failed: ${String(error)}`,
|
|
3507
4427
|
operation: "get",
|
|
@@ -3512,23 +4432,23 @@ function deserializeSession(json) {
|
|
|
3512
4432
|
return validated;
|
|
3513
4433
|
});
|
|
3514
4434
|
}
|
|
3515
|
-
var
|
|
3516
|
-
create: (originalTask) =>
|
|
3517
|
-
const sessionsDir =
|
|
3518
|
-
yield*
|
|
3519
|
-
const now = yield*
|
|
3520
|
-
const timestampMs =
|
|
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);
|
|
3521
4441
|
const sessionId = generateSessionId(timestampMs);
|
|
3522
4442
|
const session = {
|
|
3523
4443
|
id: sessionId,
|
|
3524
|
-
createdAt:
|
|
4444
|
+
createdAt: DateTime5.formatIso(now),
|
|
3525
4445
|
status: "active",
|
|
3526
4446
|
originalTask,
|
|
3527
4447
|
completedTasks: []
|
|
3528
4448
|
};
|
|
3529
4449
|
const sessionPath = getSessionPath(sessionId);
|
|
3530
|
-
yield*
|
|
3531
|
-
try: () =>
|
|
4450
|
+
yield* Effect15.tryPromise({
|
|
4451
|
+
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
3532
4452
|
catch: (error) => new SessionStoreError({
|
|
3533
4453
|
message: `Failed to write session file: ${sessionPath}`,
|
|
3534
4454
|
operation: "create",
|
|
@@ -3537,10 +4457,10 @@ var make3 = {
|
|
|
3537
4457
|
});
|
|
3538
4458
|
return session;
|
|
3539
4459
|
}),
|
|
3540
|
-
get: (sessionId) =>
|
|
4460
|
+
get: (sessionId) => Effect15.gen(function* () {
|
|
3541
4461
|
const sessionPath = getSessionPath(sessionId);
|
|
3542
|
-
const content = yield*
|
|
3543
|
-
try: () =>
|
|
4462
|
+
const content = yield* Effect15.tryPromise({
|
|
4463
|
+
try: () => readFile4(sessionPath, "utf-8"),
|
|
3544
4464
|
catch: (error) => new SessionStoreError({
|
|
3545
4465
|
message: `Failed to read session file: ${sessionPath}`,
|
|
3546
4466
|
operation: "get",
|
|
@@ -3549,10 +4469,10 @@ var make3 = {
|
|
|
3549
4469
|
});
|
|
3550
4470
|
return yield* deserializeSession(content);
|
|
3551
4471
|
}),
|
|
3552
|
-
update: (sessionId, session) =>
|
|
4472
|
+
update: (sessionId, session) => Effect15.gen(function* () {
|
|
3553
4473
|
const sessionPath = getSessionPath(sessionId);
|
|
3554
|
-
yield*
|
|
3555
|
-
try: () =>
|
|
4474
|
+
yield* Effect15.tryPromise({
|
|
4475
|
+
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
3556
4476
|
catch: (error) => new SessionStoreError({
|
|
3557
4477
|
message: `Failed to update session file: ${sessionPath}`,
|
|
3558
4478
|
operation: "update",
|
|
@@ -3561,37 +4481,37 @@ var make3 = {
|
|
|
3561
4481
|
});
|
|
3562
4482
|
})
|
|
3563
4483
|
};
|
|
3564
|
-
var
|
|
4484
|
+
var Live11 = Layer11.succeed(SessionStore, make6);
|
|
3565
4485
|
var FileSystemSession = {
|
|
3566
|
-
Live:
|
|
4486
|
+
Live: Live11
|
|
3567
4487
|
};
|
|
3568
4488
|
|
|
3569
4489
|
// src/layers/session/memory.ts
|
|
3570
4490
|
init_esm_shims();
|
|
3571
|
-
import { DateTime as
|
|
4491
|
+
import { DateTime as DateTime6, Effect as Effect16, Layer as Layer12, Ref as Ref7 } from "effect";
|
|
3572
4492
|
function createMemorySessionStore(stateRef, counterRef) {
|
|
3573
4493
|
return {
|
|
3574
|
-
create: (originalTask) =>
|
|
3575
|
-
const state = yield*
|
|
3576
|
-
const counter = yield*
|
|
4494
|
+
create: (originalTask) => Effect16.gen(function* () {
|
|
4495
|
+
const state = yield* Ref7.get(stateRef);
|
|
4496
|
+
const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
|
|
3577
4497
|
const sessionId = `test-session-${counter}`;
|
|
3578
|
-
const now = yield*
|
|
4498
|
+
const now = yield* DateTime6.now;
|
|
3579
4499
|
const session = {
|
|
3580
4500
|
id: sessionId,
|
|
3581
|
-
createdAt:
|
|
4501
|
+
createdAt: DateTime6.formatIso(now),
|
|
3582
4502
|
status: "active",
|
|
3583
4503
|
originalTask,
|
|
3584
4504
|
completedTasks: []
|
|
3585
4505
|
};
|
|
3586
4506
|
state.set(sessionId, session);
|
|
3587
|
-
yield*
|
|
4507
|
+
yield* Ref7.set(stateRef, state);
|
|
3588
4508
|
return session;
|
|
3589
4509
|
}),
|
|
3590
|
-
get: (sessionId) =>
|
|
3591
|
-
const state = yield*
|
|
4510
|
+
get: (sessionId) => Effect16.gen(function* () {
|
|
4511
|
+
const state = yield* Ref7.get(stateRef);
|
|
3592
4512
|
const session = state.get(sessionId);
|
|
3593
4513
|
if (!session) {
|
|
3594
|
-
return yield*
|
|
4514
|
+
return yield* Effect16.fail(
|
|
3595
4515
|
new SessionStoreError({
|
|
3596
4516
|
message: `Session not found: ${sessionId}`,
|
|
3597
4517
|
operation: "get"
|
|
@@ -3600,10 +4520,10 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
3600
4520
|
}
|
|
3601
4521
|
return session;
|
|
3602
4522
|
}),
|
|
3603
|
-
update: (sessionId, session) =>
|
|
3604
|
-
const state = yield*
|
|
4523
|
+
update: (sessionId, session) => Effect16.gen(function* () {
|
|
4524
|
+
const state = yield* Ref7.get(stateRef);
|
|
3605
4525
|
if (!state.has(sessionId)) {
|
|
3606
|
-
return yield*
|
|
4526
|
+
return yield* Effect16.fail(
|
|
3607
4527
|
new SessionStoreError({
|
|
3608
4528
|
message: `Session not found: ${sessionId}`,
|
|
3609
4529
|
operation: "update"
|
|
@@ -3611,34 +4531,34 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
3611
4531
|
);
|
|
3612
4532
|
}
|
|
3613
4533
|
state.set(sessionId, session);
|
|
3614
|
-
yield*
|
|
4534
|
+
yield* Ref7.set(stateRef, state);
|
|
3615
4535
|
})
|
|
3616
4536
|
};
|
|
3617
4537
|
}
|
|
3618
|
-
function
|
|
3619
|
-
return
|
|
4538
|
+
function layer6() {
|
|
4539
|
+
return Layer12.effect(
|
|
3620
4540
|
SessionStore,
|
|
3621
|
-
|
|
3622
|
-
const stateRef = yield*
|
|
3623
|
-
const counterRef = yield*
|
|
4541
|
+
Effect16.gen(function* () {
|
|
4542
|
+
const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
|
|
4543
|
+
const counterRef = yield* Ref7.make(0);
|
|
3624
4544
|
return createMemorySessionStore(stateRef, counterRef);
|
|
3625
4545
|
})
|
|
3626
4546
|
);
|
|
3627
4547
|
}
|
|
3628
|
-
var
|
|
4548
|
+
var Live12 = layer6();
|
|
3629
4549
|
var MemorySession = {
|
|
3630
|
-
Live:
|
|
3631
|
-
layer:
|
|
4550
|
+
Live: Live12,
|
|
4551
|
+
layer: layer6
|
|
3632
4552
|
};
|
|
3633
4553
|
|
|
3634
4554
|
// src/layers/signal/ferix-parser.ts
|
|
3635
4555
|
init_esm_shims();
|
|
3636
|
-
import { Effect as
|
|
4556
|
+
import { Effect as Effect17, Layer as Layer13, Ref as Ref8 } from "effect";
|
|
3637
4557
|
|
|
3638
4558
|
// src/services/signal-parser.ts
|
|
3639
4559
|
init_esm_shims();
|
|
3640
|
-
import { Context as
|
|
3641
|
-
var SignalParser = class extends
|
|
4560
|
+
import { Context as Context7 } from "effect";
|
|
4561
|
+
var SignalParser = class extends Context7.Tag("@ferix/SignalParser")() {
|
|
3642
4562
|
};
|
|
3643
4563
|
|
|
3644
4564
|
// src/layers/signal/specs/index.ts
|
|
@@ -3646,7 +4566,7 @@ init_esm_shims();
|
|
|
3646
4566
|
|
|
3647
4567
|
// src/layers/signal/specs/check.ts
|
|
3648
4568
|
init_esm_shims();
|
|
3649
|
-
import { Schema as
|
|
4569
|
+
import { Schema as S15 } from "effect";
|
|
3650
4570
|
|
|
3651
4571
|
// src/layers/signal/specs/registry.ts
|
|
3652
4572
|
init_esm_shims();
|
|
@@ -3703,7 +4623,7 @@ var checkPassedSpec = {
|
|
|
3703
4623
|
parse: (text) => {
|
|
3704
4624
|
if (CHECK_PASSED.test(text)) {
|
|
3705
4625
|
const raw = { _tag: "CheckPassed" };
|
|
3706
|
-
const result =
|
|
4626
|
+
const result = S15.decodeUnknownEither(CheckPassedSignalSchema)(raw);
|
|
3707
4627
|
if (result._tag === "Right") {
|
|
3708
4628
|
return [result.right];
|
|
3709
4629
|
}
|
|
@@ -3719,7 +4639,7 @@ var checkFailedSpec = {
|
|
|
3719
4639
|
parse: (text) => {
|
|
3720
4640
|
if (CHECK_FAILED.test(text)) {
|
|
3721
4641
|
const raw = { _tag: "CheckFailed" };
|
|
3722
|
-
const result =
|
|
4642
|
+
const result = S15.decodeUnknownEither(CheckFailedSignalSchema)(raw);
|
|
3723
4643
|
if (result._tag === "Right") {
|
|
3724
4644
|
return [result.right];
|
|
3725
4645
|
}
|
|
@@ -3733,7 +4653,7 @@ signalSpecRegistry.register(checkFailedSpec);
|
|
|
3733
4653
|
|
|
3734
4654
|
// src/layers/signal/specs/criteria.ts
|
|
3735
4655
|
init_esm_shims();
|
|
3736
|
-
import { Schema as
|
|
4656
|
+
import { Schema as S16 } from "effect";
|
|
3737
4657
|
var CRITERIA_BLOCK = /<ferix:criteria task="(\d+)">([\s\S]*?)<\/ferix:criteria>/g;
|
|
3738
4658
|
var CRITERION = /<criterion id="([^"]+)">([^<]+)<\/criterion>/g;
|
|
3739
4659
|
var CRITERION_PASSED = /<ferix:criterion-passed id="([\d.c]+)"\/>/g;
|
|
@@ -3764,7 +4684,7 @@ var criteriaDefinedSpec = {
|
|
|
3764
4684
|
taskId: match[1],
|
|
3765
4685
|
criteria
|
|
3766
4686
|
};
|
|
3767
|
-
const result =
|
|
4687
|
+
const result = S16.decodeUnknownEither(CriteriaDefinedSignalSchema)(raw);
|
|
3768
4688
|
if (result._tag === "Right") {
|
|
3769
4689
|
signals.push(result.right);
|
|
3770
4690
|
}
|
|
@@ -3783,7 +4703,7 @@ var criterionPassedSpec = {
|
|
|
3783
4703
|
for (const m of text.matchAll(resetRegex(CRITERION_PASSED))) {
|
|
3784
4704
|
if (m[1]) {
|
|
3785
4705
|
const raw = { _tag: "CriterionPassed", criterionId: m[1] };
|
|
3786
|
-
const result =
|
|
4706
|
+
const result = S16.decodeUnknownEither(CriterionPassedSignalSchema)(raw);
|
|
3787
4707
|
if (result._tag === "Right") {
|
|
3788
4708
|
signals.push(result.right);
|
|
3789
4709
|
}
|
|
@@ -3806,7 +4726,7 @@ var criterionFailedSpec = {
|
|
|
3806
4726
|
criterionId: m[1],
|
|
3807
4727
|
reason: m[2] || "Unknown reason"
|
|
3808
4728
|
};
|
|
3809
|
-
const result =
|
|
4729
|
+
const result = S16.decodeUnknownEither(CriterionFailedSignalSchema)(raw);
|
|
3810
4730
|
if (result._tag === "Right") {
|
|
3811
4731
|
signals.push(result.right);
|
|
3812
4732
|
}
|
|
@@ -3820,9 +4740,88 @@ signalSpecRegistry.register(criteriaDefinedSpec);
|
|
|
3820
4740
|
signalSpecRegistry.register(criterionPassedSpec);
|
|
3821
4741
|
signalSpecRegistry.register(criterionFailedSpec);
|
|
3822
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
|
+
|
|
3823
4822
|
// src/layers/signal/specs/loop-complete.ts
|
|
3824
4823
|
init_esm_shims();
|
|
3825
|
-
import { Schema as
|
|
4824
|
+
import { Schema as S19 } from "effect";
|
|
3826
4825
|
var LOOP_COMPLETE = /<ferix:complete>/;
|
|
3827
4826
|
var loopCompleteSpec = {
|
|
3828
4827
|
tag: "LoopComplete",
|
|
@@ -3831,7 +4830,7 @@ var loopCompleteSpec = {
|
|
|
3831
4830
|
parse: (text) => {
|
|
3832
4831
|
if (LOOP_COMPLETE.test(text)) {
|
|
3833
4832
|
const raw = { _tag: "LoopComplete" };
|
|
3834
|
-
const result =
|
|
4833
|
+
const result = S19.decodeUnknownEither(LoopCompleteSignalSchema)(raw);
|
|
3835
4834
|
if (result._tag === "Right") {
|
|
3836
4835
|
return [result.right];
|
|
3837
4836
|
}
|
|
@@ -3844,7 +4843,7 @@ signalSpecRegistry.register(loopCompleteSpec);
|
|
|
3844
4843
|
|
|
3845
4844
|
// src/layers/signal/specs/phases.ts
|
|
3846
4845
|
init_esm_shims();
|
|
3847
|
-
import { Schema as
|
|
4846
|
+
import { Schema as S20 } from "effect";
|
|
3848
4847
|
var PHASES_BLOCK = /<ferix:phases task="(\d+)">([\s\S]*?)<\/ferix:phases>/;
|
|
3849
4848
|
var PHASE = /<phase id="([\d.]+)">([^<]+)<\/phase>/g;
|
|
3850
4849
|
var PHASE_START = /<ferix:phase-start id="([\d.]+)"\/>/g;
|
|
@@ -3877,7 +4876,7 @@ var phasesDefinedSpec = {
|
|
|
3877
4876
|
taskId: match[1],
|
|
3878
4877
|
phases
|
|
3879
4878
|
};
|
|
3880
|
-
const result =
|
|
4879
|
+
const result = S20.decodeUnknownEither(PhasesDefinedSignalSchema)(raw);
|
|
3881
4880
|
if (result._tag === "Left") {
|
|
3882
4881
|
return [];
|
|
3883
4882
|
}
|
|
@@ -3894,7 +4893,7 @@ var phaseStartedSpec = {
|
|
|
3894
4893
|
for (const m of text.matchAll(resetRegex2(PHASE_START))) {
|
|
3895
4894
|
if (m[1]) {
|
|
3896
4895
|
const raw = { _tag: "PhaseStarted", phaseId: m[1] };
|
|
3897
|
-
const result =
|
|
4896
|
+
const result = S20.decodeUnknownEither(PhaseStartedSignalSchema)(raw);
|
|
3898
4897
|
if (result._tag === "Right") {
|
|
3899
4898
|
signals.push(result.right);
|
|
3900
4899
|
}
|
|
@@ -3913,7 +4912,7 @@ var phaseCompletedSpec = {
|
|
|
3913
4912
|
for (const m of text.matchAll(resetRegex2(PHASE_DONE))) {
|
|
3914
4913
|
if (m[1]) {
|
|
3915
4914
|
const raw = { _tag: "PhaseCompleted", phaseId: m[1] };
|
|
3916
|
-
const result =
|
|
4915
|
+
const result = S20.decodeUnknownEither(PhaseCompletedSignalSchema)(raw);
|
|
3917
4916
|
if (result._tag === "Right") {
|
|
3918
4917
|
signals.push(result.right);
|
|
3919
4918
|
}
|
|
@@ -3936,7 +4935,7 @@ var phaseFailedSpec = {
|
|
|
3936
4935
|
phaseId: m[1],
|
|
3937
4936
|
reason: m[2] || "Unknown reason"
|
|
3938
4937
|
};
|
|
3939
|
-
const result =
|
|
4938
|
+
const result = S20.decodeUnknownEither(PhaseFailedSignalSchema)(raw);
|
|
3940
4939
|
if (result._tag === "Right") {
|
|
3941
4940
|
signals.push(result.right);
|
|
3942
4941
|
}
|
|
@@ -3953,7 +4952,7 @@ signalSpecRegistry.register(phaseFailedSpec);
|
|
|
3953
4952
|
|
|
3954
4953
|
// src/layers/signal/specs/review.ts
|
|
3955
4954
|
init_esm_shims();
|
|
3956
|
-
import { Schema as
|
|
4955
|
+
import { Schema as S21 } from "effect";
|
|
3957
4956
|
var REVIEW_COMPLETE = /<ferix:review-complete\/>/;
|
|
3958
4957
|
var REVIEW_CHANGES = /<ferix:review-changes-made\/>/;
|
|
3959
4958
|
var reviewCompleteSpec = {
|
|
@@ -3966,7 +4965,7 @@ var reviewCompleteSpec = {
|
|
|
3966
4965
|
}
|
|
3967
4966
|
const changesMade = REVIEW_CHANGES.test(text);
|
|
3968
4967
|
const raw = { _tag: "ReviewComplete", changesMade };
|
|
3969
|
-
const result =
|
|
4968
|
+
const result = S21.decodeUnknownEither(ReviewCompleteSignalSchema)(raw);
|
|
3970
4969
|
if (result._tag === "Right") {
|
|
3971
4970
|
return [result.right];
|
|
3972
4971
|
}
|
|
@@ -3978,7 +4977,7 @@ signalSpecRegistry.register(reviewCompleteSpec);
|
|
|
3978
4977
|
|
|
3979
4978
|
// src/layers/signal/specs/task-complete.ts
|
|
3980
4979
|
init_esm_shims();
|
|
3981
|
-
import { Schema as
|
|
4980
|
+
import { Schema as S22 } from "effect";
|
|
3982
4981
|
var TASK_COMPLETE = /<ferix:task-complete id="(\d+)">([\s\S]*?)<\/ferix:task-complete>/;
|
|
3983
4982
|
var SUMMARY = /<summary>([\s\S]*?)<\/summary>/;
|
|
3984
4983
|
var FILES_MODIFIED = /<files-modified>([\s\S]*?)<\/files-modified>/;
|
|
@@ -4009,7 +5008,7 @@ var taskCompleteSpec = {
|
|
|
4009
5008
|
filesModified: parseFileList(filesModifiedMatch?.[1]),
|
|
4010
5009
|
filesCreated: parseFileList(filesCreatedMatch?.[1])
|
|
4011
5010
|
};
|
|
4012
|
-
const result =
|
|
5011
|
+
const result = S22.decodeUnknownEither(TaskCompleteSignalSchema)(raw);
|
|
4013
5012
|
if (result._tag === "Right") {
|
|
4014
5013
|
return [result.right];
|
|
4015
5014
|
}
|
|
@@ -4021,7 +5020,7 @@ signalSpecRegistry.register(taskCompleteSpec);
|
|
|
4021
5020
|
|
|
4022
5021
|
// src/layers/signal/specs/tasks.ts
|
|
4023
5022
|
init_esm_shims();
|
|
4024
|
-
import { Schema as
|
|
5023
|
+
import { Schema as S23 } from "effect";
|
|
4025
5024
|
var TASKS_BLOCK = /<ferix:tasks>([\s\S]*?)<\/ferix:tasks>/;
|
|
4026
5025
|
var TASK = /<task id="(\d+)">([^<]+)<\/task>/g;
|
|
4027
5026
|
function resetRegex3(pattern) {
|
|
@@ -4051,7 +5050,7 @@ var tasksDefinedSpec = {
|
|
|
4051
5050
|
return [];
|
|
4052
5051
|
}
|
|
4053
5052
|
const raw = { _tag: "TasksDefined", tasks };
|
|
4054
|
-
const result =
|
|
5053
|
+
const result = S23.decodeUnknownEither(TasksDefinedSignalSchema)(raw);
|
|
4055
5054
|
if (result._tag === "Left") {
|
|
4056
5055
|
return [];
|
|
4057
5056
|
}
|
|
@@ -4064,20 +5063,20 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
4064
5063
|
// src/layers/signal/ferix-parser.ts
|
|
4065
5064
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
4066
5065
|
function createAccumulatorImpl() {
|
|
4067
|
-
return
|
|
4068
|
-
const chunksRef = yield*
|
|
4069
|
-
const emittedRef = yield*
|
|
4070
|
-
const feed = (text) =>
|
|
4071
|
-
const chunks = yield*
|
|
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);
|
|
4072
5071
|
chunks.push(text);
|
|
4073
5072
|
const buffer = chunks.join("");
|
|
4074
5073
|
if (buffer.length > MAX_BUFFER_SIZE) {
|
|
4075
|
-
yield*
|
|
5074
|
+
yield* Ref8.set(chunksRef, [
|
|
4076
5075
|
buffer.slice(buffer.length - MAX_BUFFER_SIZE)
|
|
4077
5076
|
]);
|
|
4078
5077
|
}
|
|
4079
5078
|
const signals = signalSpecRegistry.parseAll(buffer);
|
|
4080
|
-
const emitted = yield*
|
|
5079
|
+
const emitted = yield* Ref8.get(emittedRef);
|
|
4081
5080
|
const newSignals = signals.filter((signal) => {
|
|
4082
5081
|
const key = signalSpecRegistry.getSignalKey(signal);
|
|
4083
5082
|
if (emitted.has(key)) {
|
|
@@ -4089,55 +5088,64 @@ function createAccumulatorImpl() {
|
|
|
4089
5088
|
if (newSignals.length > 0) {
|
|
4090
5089
|
const lastEndPos = signalSpecRegistry.findLastCompleteSignalEnd(buffer);
|
|
4091
5090
|
if (lastEndPos > 0 && lastEndPos < buffer.length) {
|
|
4092
|
-
yield*
|
|
5091
|
+
yield* Ref8.set(chunksRef, [buffer.slice(lastEndPos)]);
|
|
4093
5092
|
}
|
|
4094
5093
|
}
|
|
4095
|
-
yield*
|
|
5094
|
+
yield* Ref8.set(emittedRef, emitted);
|
|
4096
5095
|
return newSignals;
|
|
4097
5096
|
});
|
|
4098
|
-
const flush = () =>
|
|
4099
|
-
const chunks = yield*
|
|
5097
|
+
const flush = () => Effect17.gen(function* () {
|
|
5098
|
+
const chunks = yield* Ref8.get(chunksRef);
|
|
4100
5099
|
const buffer = chunks.join("");
|
|
4101
|
-
yield*
|
|
4102
|
-
const emitted = yield*
|
|
5100
|
+
yield* Ref8.set(chunksRef, []);
|
|
5101
|
+
const emitted = yield* Ref8.get(emittedRef);
|
|
4103
5102
|
const signals = signalSpecRegistry.parseAll(buffer);
|
|
4104
5103
|
const result = signals.filter(
|
|
4105
5104
|
(signal) => !emitted.has(signalSpecRegistry.getSignalKey(signal))
|
|
4106
5105
|
);
|
|
4107
|
-
yield*
|
|
5106
|
+
yield* Ref8.set(emittedRef, /* @__PURE__ */ new Set());
|
|
4108
5107
|
return result;
|
|
4109
5108
|
});
|
|
4110
5109
|
return { feed, flush };
|
|
4111
5110
|
});
|
|
4112
5111
|
}
|
|
4113
|
-
var
|
|
4114
|
-
parse: (text) =>
|
|
5112
|
+
var make7 = {
|
|
5113
|
+
parse: (text) => Effect17.succeed(signalSpecRegistry.parseAll(text)),
|
|
4115
5114
|
createAccumulator: createAccumulatorImpl
|
|
4116
5115
|
};
|
|
4117
|
-
var
|
|
5116
|
+
var Live13 = Layer13.succeed(SignalParser, make7);
|
|
4118
5117
|
var FerixParser = {
|
|
4119
|
-
Live:
|
|
5118
|
+
Live: Live13
|
|
4120
5119
|
};
|
|
4121
5120
|
|
|
4122
5121
|
// src/layers/index.ts
|
|
4123
|
-
var ProductionLayers =
|
|
5122
|
+
var ProductionLayers = Layer14.mergeAll(
|
|
4124
5123
|
ClaudeCLI.Live,
|
|
4125
5124
|
FerixParser.Live,
|
|
4126
5125
|
FileSystemPlan.Live,
|
|
4127
|
-
FileSystemSession.Live
|
|
5126
|
+
FileSystemSession.Live,
|
|
5127
|
+
FileSystemProgress.Live,
|
|
5128
|
+
FileSystemGuardrails.Live,
|
|
5129
|
+
FileSystemGit.Live
|
|
4128
5130
|
);
|
|
4129
|
-
var TestLayers =
|
|
5131
|
+
var TestLayers = Layer14.mergeAll(
|
|
4130
5132
|
Mock.Live,
|
|
4131
5133
|
FerixParser.Live,
|
|
4132
5134
|
MemoryPlan.Live,
|
|
4133
|
-
MemorySession.Live
|
|
5135
|
+
MemorySession.Live,
|
|
5136
|
+
MemoryProgress.Live,
|
|
5137
|
+
MemoryGuardrails.Live,
|
|
5138
|
+
MemoryGit.Live
|
|
4134
5139
|
);
|
|
4135
5140
|
function createTestLayers(events) {
|
|
4136
|
-
return
|
|
5141
|
+
return Layer14.mergeAll(
|
|
4137
5142
|
Mock.layer({ events }),
|
|
4138
5143
|
FerixParser.Live,
|
|
4139
5144
|
MemoryPlan.layer(),
|
|
4140
|
-
MemorySession.layer()
|
|
5145
|
+
MemorySession.layer(),
|
|
5146
|
+
MemoryProgress.layer(),
|
|
5147
|
+
MemoryGuardrails.layer(),
|
|
5148
|
+
MemoryGit.layer()
|
|
4141
5149
|
);
|
|
4142
5150
|
}
|
|
4143
5151
|
|
|
@@ -4146,42 +5154,42 @@ init_esm_shims();
|
|
|
4146
5154
|
|
|
4147
5155
|
// src/orchestrator/loop.ts
|
|
4148
5156
|
init_esm_shims();
|
|
4149
|
-
import { DateTime as
|
|
5157
|
+
import { DateTime as DateTime10, Effect as Effect22, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
|
|
4150
5158
|
|
|
4151
5159
|
// src/orchestrator/discovery.ts
|
|
4152
5160
|
init_esm_shims();
|
|
4153
|
-
import { DateTime as
|
|
5161
|
+
import { DateTime as DateTime8, Effect as Effect20, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
|
|
4154
5162
|
|
|
4155
5163
|
// src/layers/plan/task-generation.ts
|
|
4156
5164
|
init_esm_shims();
|
|
4157
|
-
import { mkdir as
|
|
4158
|
-
import { join as
|
|
4159
|
-
import { Effect as
|
|
4160
|
-
var
|
|
4161
|
-
function
|
|
4162
|
-
return
|
|
4163
|
-
try: () =>
|
|
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 }),
|
|
4164
5172
|
catch: (error) => new PlanStoreError({
|
|
4165
5173
|
message: `Failed to create directory: ${dirPath}`,
|
|
4166
5174
|
operation: "create",
|
|
4167
5175
|
cause: error
|
|
4168
5176
|
})
|
|
4169
|
-
}).pipe(
|
|
5177
|
+
}).pipe(Effect18.asVoid);
|
|
4170
5178
|
}
|
|
4171
|
-
function
|
|
4172
|
-
return
|
|
5179
|
+
function getSessionDir4(sessionId) {
|
|
5180
|
+
return join6(process.cwd(), PLANS_DIR4, sessionId);
|
|
4173
5181
|
}
|
|
4174
5182
|
function getTasksMdPath(sessionId) {
|
|
4175
|
-
return
|
|
5183
|
+
return join6(getSessionDir4(sessionId), "tasks.md");
|
|
4176
5184
|
}
|
|
4177
5185
|
function writeTasksMd(sessionId, tasks) {
|
|
4178
|
-
return
|
|
4179
|
-
const sessionDir =
|
|
4180
|
-
yield*
|
|
5186
|
+
return Effect18.gen(function* () {
|
|
5187
|
+
const sessionDir = getSessionDir4(sessionId);
|
|
5188
|
+
yield* ensureDir5(sessionDir);
|
|
4181
5189
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
4182
5190
|
const content = formatTasksMd(tasks);
|
|
4183
|
-
yield*
|
|
4184
|
-
try: () =>
|
|
5191
|
+
yield* Effect18.tryPromise({
|
|
5192
|
+
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
4185
5193
|
catch: (error) => new PlanStoreError({
|
|
4186
5194
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
4187
5195
|
operation: "create",
|
|
@@ -4325,6 +5333,32 @@ eventMappingRegistry.registerSignalMapper({
|
|
|
4325
5333
|
}
|
|
4326
5334
|
})
|
|
4327
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
|
+
});
|
|
4328
5362
|
|
|
4329
5363
|
// src/orchestrator/mapping/index.ts
|
|
4330
5364
|
function mapLLMEventToDomain(event, context) {
|
|
@@ -4336,7 +5370,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
4336
5370
|
|
|
4337
5371
|
// src/orchestrator/plan-updates.ts
|
|
4338
5372
|
init_esm_shims();
|
|
4339
|
-
import { DateTime as
|
|
5373
|
+
import { DateTime as DateTime7, Effect as Effect19, Ref as Ref9 } from "effect";
|
|
4340
5374
|
|
|
4341
5375
|
// src/orchestrator/plan-updates/index.ts
|
|
4342
5376
|
init_esm_shims();
|
|
@@ -4548,6 +5582,20 @@ planUpdateRegistry.register({
|
|
|
4548
5582
|
} : void 0
|
|
4549
5583
|
});
|
|
4550
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
|
+
|
|
4551
5599
|
// src/orchestrator/plan-updates/handlers/phase-lifecycle.ts
|
|
4552
5600
|
init_esm_shims();
|
|
4553
5601
|
planUpdateRegistry.register({
|
|
@@ -4627,9 +5675,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
4627
5675
|
tasks: plan.tasks
|
|
4628
5676
|
}) : planStore.update(plan.id, plan);
|
|
4629
5677
|
return storeOp.pipe(
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
(error) =>
|
|
5678
|
+
Effect19.map(() => null),
|
|
5679
|
+
Effect19.catchAll(
|
|
5680
|
+
(error) => Effect19.succeed({
|
|
4633
5681
|
_tag: "PlanUpdateFailed",
|
|
4634
5682
|
operation,
|
|
4635
5683
|
error: error.message,
|
|
@@ -4639,10 +5687,10 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
4639
5687
|
);
|
|
4640
5688
|
}
|
|
4641
5689
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
4642
|
-
return
|
|
4643
|
-
const currentPlan = yield*
|
|
4644
|
-
const now = yield*
|
|
4645
|
-
const timestamp =
|
|
5690
|
+
return Effect19.gen(function* () {
|
|
5691
|
+
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
5692
|
+
const now = yield* DateTime7.now;
|
|
5693
|
+
const timestamp = DateTime7.formatIso(now);
|
|
4646
5694
|
const updateResult = computePlanUpdate(signal, currentPlan, {
|
|
4647
5695
|
sessionId,
|
|
4648
5696
|
originalTask,
|
|
@@ -4652,8 +5700,8 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
4652
5700
|
return [];
|
|
4653
5701
|
}
|
|
4654
5702
|
const { plan, operation, eventTag } = updateResult;
|
|
4655
|
-
yield*
|
|
4656
|
-
yield*
|
|
5703
|
+
yield* Ref9.set(currentPlanRef, plan);
|
|
5704
|
+
yield* Ref9.update(persistenceStateRef, (state) => ({
|
|
4657
5705
|
dirty: true,
|
|
4658
5706
|
pendingOperation: state.pendingOperation === "create" ? "create" : operation
|
|
4659
5707
|
}));
|
|
@@ -4661,12 +5709,12 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
4661
5709
|
});
|
|
4662
5710
|
}
|
|
4663
5711
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
4664
|
-
return
|
|
4665
|
-
const state = yield*
|
|
5712
|
+
return Effect19.gen(function* () {
|
|
5713
|
+
const state = yield* Ref9.get(persistenceStateRef);
|
|
4666
5714
|
if (!(state.dirty && state.pendingOperation)) {
|
|
4667
5715
|
return [];
|
|
4668
5716
|
}
|
|
4669
|
-
const plan = yield*
|
|
5717
|
+
const plan = yield* Ref9.get(currentPlanRef);
|
|
4670
5718
|
if (!plan) {
|
|
4671
5719
|
return [];
|
|
4672
5720
|
}
|
|
@@ -4679,7 +5727,7 @@ function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
|
4679
5727
|
if (failureEvent) {
|
|
4680
5728
|
events.push(failureEvent);
|
|
4681
5729
|
}
|
|
4682
|
-
yield*
|
|
5730
|
+
yield* Ref9.set(persistenceStateRef, {
|
|
4683
5731
|
dirty: false,
|
|
4684
5732
|
pendingOperation: null
|
|
4685
5733
|
});
|
|
@@ -4738,9 +5786,12 @@ Use these XML-like tags to communicate structured information:
|
|
|
4738
5786
|
<files-created>new-file.ts</files-created>
|
|
4739
5787
|
</ferix:task-complete>
|
|
4740
5788
|
|
|
4741
|
-
### Loop Completion
|
|
5789
|
+
### Loop Completion (use ONLY when ALL tasks are done)
|
|
4742
5790
|
<ferix:complete>
|
|
4743
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
|
+
|
|
4744
5795
|
IMPORTANT: Always emit signals on their own lines, never inside markdown code blocks.`;
|
|
4745
5796
|
var DISCOVERY_SYSTEM_PROMPT = `You are in the DISCOVERY phase of a ralph loop - an iterative AI coding workflow.
|
|
4746
5797
|
|
|
@@ -4781,14 +5832,17 @@ Review the code for quality:
|
|
|
4781
5832
|
- When done, emit <ferix:review-complete/>`;
|
|
4782
5833
|
var DEFAULT_COMPLETION_PROMPT = `## Completion
|
|
4783
5834
|
|
|
4784
|
-
When the task
|
|
5835
|
+
When you finish the CURRENT task, emit:
|
|
4785
5836
|
<ferix:task-complete id="N">
|
|
4786
5837
|
<summary>What was accomplished</summary>
|
|
4787
5838
|
<files-modified>list of modified files</files-modified>
|
|
4788
5839
|
<files-created>list of new files</files-created>
|
|
4789
5840
|
</ferix:task-complete>
|
|
4790
5841
|
|
|
4791
|
-
|
|
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.`;
|
|
4792
5846
|
function getPhasePrompt(phase, prompts, defaultPrompt) {
|
|
4793
5847
|
return prompts?.phases?.[phase] ?? defaultPrompt;
|
|
4794
5848
|
}
|
|
@@ -4929,12 +5983,12 @@ Begin.`);
|
|
|
4929
5983
|
|
|
4930
5984
|
// src/orchestrator/discovery.ts
|
|
4931
5985
|
function processTextSignals(signalParser, text, context) {
|
|
4932
|
-
return
|
|
5986
|
+
return Effect20.gen(function* () {
|
|
4933
5987
|
const events = [];
|
|
4934
5988
|
const parsedSignals = [];
|
|
4935
5989
|
const signals = yield* signalParser.parse(text).pipe(
|
|
4936
|
-
|
|
4937
|
-
(error) =>
|
|
5990
|
+
Effect20.tapError(
|
|
5991
|
+
(error) => Effect20.logDebug(
|
|
4938
5992
|
"Signal parsing failed, continuing with empty signals",
|
|
4939
5993
|
{
|
|
4940
5994
|
error: String(error),
|
|
@@ -4942,7 +5996,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
4942
5996
|
}
|
|
4943
5997
|
)
|
|
4944
5998
|
),
|
|
4945
|
-
|
|
5999
|
+
Effect20.orElseSucceed(() => [])
|
|
4946
6000
|
);
|
|
4947
6001
|
for (const signal of signals) {
|
|
4948
6002
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -4952,7 +6006,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
4952
6006
|
});
|
|
4953
6007
|
}
|
|
4954
6008
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
4955
|
-
return
|
|
6009
|
+
return Effect20.gen(function* () {
|
|
4956
6010
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
4957
6011
|
const events = [domainEvent];
|
|
4958
6012
|
const allSignals = [];
|
|
@@ -4995,12 +6049,12 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
4995
6049
|
status: mapTaskStatus(task.status)
|
|
4996
6050
|
}));
|
|
4997
6051
|
}
|
|
4998
|
-
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId) {
|
|
6052
|
+
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
|
|
4999
6053
|
return Stream7.unwrap(
|
|
5000
|
-
|
|
5001
|
-
const startTimeUtc = yield*
|
|
5002
|
-
const startTime =
|
|
5003
|
-
const persistenceStateRef = yield*
|
|
6054
|
+
Effect20.gen(function* () {
|
|
6055
|
+
const startTimeUtc = yield* DateTime8.now;
|
|
6056
|
+
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
6057
|
+
const persistenceStateRef = yield* Ref10.make({
|
|
5004
6058
|
dirty: false,
|
|
5005
6059
|
pendingOperation: null
|
|
5006
6060
|
});
|
|
@@ -5014,7 +6068,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
5014
6068
|
input: {}
|
|
5015
6069
|
};
|
|
5016
6070
|
const prompt = buildDiscoveryPrompt(config);
|
|
5017
|
-
const llmStream = llm.execute(prompt).pipe(
|
|
6071
|
+
const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
|
|
5018
6072
|
Stream7.mapError(
|
|
5019
6073
|
(e) => new OrchestratorError({
|
|
5020
6074
|
message: `LLM execution failed during discovery: ${String(e)}`,
|
|
@@ -5024,10 +6078,10 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
5024
6078
|
),
|
|
5025
6079
|
Stream7.flatMap(
|
|
5026
6080
|
(llmEvent) => Stream7.unwrap(
|
|
5027
|
-
|
|
5028
|
-
const now = yield*
|
|
6081
|
+
Effect20.gen(function* () {
|
|
6082
|
+
const now = yield* DateTime8.now;
|
|
5029
6083
|
const context = {
|
|
5030
|
-
timestamp:
|
|
6084
|
+
timestamp: DateTime8.toEpochMillis(now)
|
|
5031
6085
|
};
|
|
5032
6086
|
const result = yield* processLLMEvent(
|
|
5033
6087
|
signalParser,
|
|
@@ -5061,27 +6115,27 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
5061
6115
|
)
|
|
5062
6116
|
);
|
|
5063
6117
|
const completionStream = Stream7.fromEffect(
|
|
5064
|
-
|
|
6118
|
+
Effect20.gen(function* () {
|
|
5065
6119
|
const persistEvents = yield* flushPlanPersistence(
|
|
5066
6120
|
planStore,
|
|
5067
6121
|
currentPlanRef,
|
|
5068
6122
|
persistenceStateRef
|
|
5069
6123
|
);
|
|
5070
|
-
const plan = yield*
|
|
6124
|
+
const plan = yield* Ref10.get(currentPlanRef);
|
|
5071
6125
|
const taskCount = plan?.tasks.length ?? 0;
|
|
5072
6126
|
if (plan && plan.tasks.length > 0) {
|
|
5073
6127
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
5074
6128
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
5075
|
-
|
|
5076
|
-
(error) =>
|
|
6129
|
+
Effect20.tapError(
|
|
6130
|
+
(error) => Effect20.logDebug("Failed to write tasks.md, continuing", {
|
|
5077
6131
|
error: String(error)
|
|
5078
6132
|
})
|
|
5079
6133
|
),
|
|
5080
|
-
|
|
6134
|
+
Effect20.orElseSucceed(() => void 0)
|
|
5081
6135
|
);
|
|
5082
6136
|
}
|
|
5083
|
-
const endTimeUtc = yield*
|
|
5084
|
-
const endTime =
|
|
6137
|
+
const endTimeUtc = yield* DateTime8.now;
|
|
6138
|
+
const endTime = DateTime8.toEpochMillis(endTimeUtc);
|
|
5085
6139
|
const discoveryCompleted = {
|
|
5086
6140
|
_tag: "DiscoveryCompleted",
|
|
5087
6141
|
taskCount,
|
|
@@ -5102,15 +6156,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
5102
6156
|
|
|
5103
6157
|
// src/orchestrator/iteration.ts
|
|
5104
6158
|
init_esm_shims();
|
|
5105
|
-
import { DateTime as
|
|
6159
|
+
import { DateTime as DateTime9, Effect as Effect21, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
|
|
5106
6160
|
function processTextSignals2(signalParser, text, context) {
|
|
5107
|
-
return
|
|
6161
|
+
return Effect21.gen(function* () {
|
|
5108
6162
|
const events = [];
|
|
5109
6163
|
let completed = false;
|
|
5110
6164
|
const parsedSignals = [];
|
|
5111
6165
|
const signals = yield* signalParser.parse(text).pipe(
|
|
5112
|
-
|
|
5113
|
-
(error) =>
|
|
6166
|
+
Effect21.tapError(
|
|
6167
|
+
(error) => Effect21.logDebug(
|
|
5114
6168
|
"Signal parsing failed, continuing with empty signals",
|
|
5115
6169
|
{
|
|
5116
6170
|
error: String(error),
|
|
@@ -5118,7 +6172,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
5118
6172
|
}
|
|
5119
6173
|
)
|
|
5120
6174
|
),
|
|
5121
|
-
|
|
6175
|
+
Effect21.orElseSucceed(() => [])
|
|
5122
6176
|
);
|
|
5123
6177
|
for (const signal of signals) {
|
|
5124
6178
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -5131,7 +6185,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
5131
6185
|
});
|
|
5132
6186
|
}
|
|
5133
6187
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
5134
|
-
return
|
|
6188
|
+
return Effect21.gen(function* () {
|
|
5135
6189
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
5136
6190
|
const events = [domainEvent];
|
|
5137
6191
|
let completed = false;
|
|
@@ -5162,11 +6216,11 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
5162
6216
|
return { events, completed, signals: allSignals };
|
|
5163
6217
|
});
|
|
5164
6218
|
}
|
|
5165
|
-
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId) {
|
|
6219
|
+
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
5166
6220
|
return Stream8.unwrap(
|
|
5167
|
-
|
|
5168
|
-
const currentPlan = yield*
|
|
5169
|
-
const persistenceStateRef = yield*
|
|
6221
|
+
Effect21.gen(function* () {
|
|
6222
|
+
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
6223
|
+
const persistenceStateRef = yield* Ref11.make({
|
|
5170
6224
|
dirty: false,
|
|
5171
6225
|
pendingOperation: null
|
|
5172
6226
|
});
|
|
@@ -5174,7 +6228,10 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
5174
6228
|
_tag: "IterationStarted",
|
|
5175
6229
|
iteration
|
|
5176
6230
|
};
|
|
5177
|
-
const llmStream = llm.execute(
|
|
6231
|
+
const llmStream = llm.execute(
|
|
6232
|
+
buildPrompt(config, iteration, currentPlan),
|
|
6233
|
+
worktreePath ? { cwd: worktreePath } : void 0
|
|
6234
|
+
).pipe(
|
|
5178
6235
|
Stream8.mapError(
|
|
5179
6236
|
(e) => new OrchestratorError({
|
|
5180
6237
|
message: `LLM execution failed: ${String(e)}`,
|
|
@@ -5184,10 +6241,10 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
5184
6241
|
),
|
|
5185
6242
|
Stream8.flatMap(
|
|
5186
6243
|
(llmEvent) => Stream8.unwrap(
|
|
5187
|
-
|
|
5188
|
-
const now = yield*
|
|
6244
|
+
Effect21.gen(function* () {
|
|
6245
|
+
const now = yield* DateTime9.now;
|
|
5189
6246
|
const context = {
|
|
5190
|
-
timestamp:
|
|
6247
|
+
timestamp: DateTime9.toEpochMillis(now)
|
|
5191
6248
|
};
|
|
5192
6249
|
const result = yield* processLLMEvent2(
|
|
5193
6250
|
signalParser,
|
|
@@ -5206,7 +6263,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
5206
6263
|
events.push(...planEvents);
|
|
5207
6264
|
}
|
|
5208
6265
|
if (result.completed) {
|
|
5209
|
-
yield*
|
|
6266
|
+
yield* Ref11.set(loopCompletedRef, true);
|
|
5210
6267
|
}
|
|
5211
6268
|
return Stream8.fromIterable(events);
|
|
5212
6269
|
})
|
|
@@ -5225,7 +6282,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
5225
6282
|
)
|
|
5226
6283
|
);
|
|
5227
6284
|
const completionStream = Stream8.fromEffect(
|
|
5228
|
-
|
|
6285
|
+
Effect21.gen(function* () {
|
|
5229
6286
|
const persistEvents = yield* flushPlanPersistence(
|
|
5230
6287
|
planStore,
|
|
5231
6288
|
currentPlanRef,
|
|
@@ -5250,13 +6307,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
5250
6307
|
// src/orchestrator/loop.ts
|
|
5251
6308
|
function runLoop(config) {
|
|
5252
6309
|
return Stream9.unwrap(
|
|
5253
|
-
|
|
6310
|
+
Effect22.gen(function* () {
|
|
5254
6311
|
const llm = yield* LLM;
|
|
5255
6312
|
const signalParser = yield* SignalParser;
|
|
5256
6313
|
const sessionStore = yield* SessionStore;
|
|
5257
6314
|
const planStore = yield* PlanStore;
|
|
6315
|
+
const git = yield* Git;
|
|
5258
6316
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
5259
|
-
|
|
6317
|
+
Effect22.mapError(
|
|
5260
6318
|
(e) => new OrchestratorError({
|
|
5261
6319
|
message: `Failed to create session: ${e.message}`,
|
|
5262
6320
|
phase: "setup",
|
|
@@ -5264,10 +6322,39 @@ function runLoop(config) {
|
|
|
5264
6322
|
})
|
|
5265
6323
|
)
|
|
5266
6324
|
);
|
|
5267
|
-
const
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
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);
|
|
5271
6358
|
const maxIterations = config.maxIterations === 0 ? Number.POSITIVE_INFINITY : config.maxIterations;
|
|
5272
6359
|
const loopStarted = {
|
|
5273
6360
|
_tag: "LoopStarted",
|
|
@@ -5280,12 +6367,13 @@ function runLoop(config) {
|
|
|
5280
6367
|
planStore,
|
|
5281
6368
|
currentPlanRef,
|
|
5282
6369
|
config,
|
|
5283
|
-
session.id
|
|
6370
|
+
session.id,
|
|
6371
|
+
worktreePath
|
|
5284
6372
|
);
|
|
5285
6373
|
const iterationsStream = Stream9.unfoldEffect(
|
|
5286
6374
|
1,
|
|
5287
|
-
(iteration) =>
|
|
5288
|
-
const completed = yield*
|
|
6375
|
+
(iteration) => Effect22.gen(function* () {
|
|
6376
|
+
const completed = yield* Ref12.get(loopCompletedRef);
|
|
5289
6377
|
if (completed || iteration > maxIterations) {
|
|
5290
6378
|
return Option.none();
|
|
5291
6379
|
}
|
|
@@ -5301,27 +6389,31 @@ function runLoop(config) {
|
|
|
5301
6389
|
loopCompletedRef,
|
|
5302
6390
|
config,
|
|
5303
6391
|
iteration,
|
|
5304
|
-
session.id
|
|
6392
|
+
session.id,
|
|
6393
|
+
worktreePath
|
|
5305
6394
|
)
|
|
5306
6395
|
)
|
|
5307
6396
|
);
|
|
5308
6397
|
const completionStream = createCompletionStream(
|
|
5309
6398
|
sessionStore,
|
|
6399
|
+
git,
|
|
5310
6400
|
session,
|
|
5311
6401
|
config,
|
|
5312
6402
|
startTime,
|
|
5313
|
-
loopCompletedRef
|
|
6403
|
+
loopCompletedRef,
|
|
6404
|
+
worktreePath
|
|
5314
6405
|
);
|
|
5315
6406
|
return pipe3(
|
|
5316
6407
|
Stream9.succeed(loopStarted),
|
|
6408
|
+
Stream9.concat(Stream9.succeed(worktreeCreated)),
|
|
5317
6409
|
Stream9.concat(discoveryStream),
|
|
5318
6410
|
Stream9.concat(iterationsStream),
|
|
5319
6411
|
Stream9.concat(completionStream)
|
|
5320
6412
|
);
|
|
5321
6413
|
}).pipe(
|
|
5322
6414
|
// Also catch setup errors (e.g., session creation failure)
|
|
5323
|
-
|
|
5324
|
-
(error) =>
|
|
6415
|
+
Effect22.catchAll(
|
|
6416
|
+
(error) => Effect22.succeed(
|
|
5325
6417
|
Stream9.succeed({
|
|
5326
6418
|
_tag: "LoopFailed",
|
|
5327
6419
|
error: {
|
|
@@ -5334,12 +6426,21 @@ function runLoop(config) {
|
|
|
5334
6426
|
)
|
|
5335
6427
|
);
|
|
5336
6428
|
}
|
|
5337
|
-
function createCompletionStream(sessionStore, session, config, startTime, loopCompletedRef) {
|
|
5338
|
-
return Stream9.
|
|
5339
|
-
|
|
5340
|
-
const endTimeUtc = yield*
|
|
5341
|
-
const durationMs =
|
|
5342
|
-
const completed = yield*
|
|
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
|
+
);
|
|
5343
6444
|
const summary = {
|
|
5344
6445
|
iterations: config.maxIterations,
|
|
5345
6446
|
success: completed,
|
|
@@ -5351,16 +6452,16 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
|
|
|
5351
6452
|
...session,
|
|
5352
6453
|
status: completed ? "completed" : "paused"
|
|
5353
6454
|
}).pipe(
|
|
5354
|
-
|
|
5355
|
-
(error) =>
|
|
6455
|
+
Effect22.tapError(
|
|
6456
|
+
(error) => Effect22.logDebug("Session update failed, continuing", {
|
|
5356
6457
|
sessionId: session.id,
|
|
5357
6458
|
error: String(error)
|
|
5358
6459
|
})
|
|
5359
6460
|
),
|
|
5360
|
-
|
|
6461
|
+
Effect22.orElseSucceed(() => void 0)
|
|
5361
6462
|
);
|
|
5362
|
-
const
|
|
5363
|
-
return
|
|
6463
|
+
const loopCompleted = { _tag: "LoopCompleted", summary };
|
|
6464
|
+
return Stream9.succeed(loopCompleted);
|
|
5364
6465
|
})
|
|
5365
6466
|
);
|
|
5366
6467
|
}
|
|
@@ -5369,7 +6470,7 @@ function createCompletionStream(sessionStore, session, config, startTime, loopCo
|
|
|
5369
6470
|
function run(options) {
|
|
5370
6471
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
5371
6472
|
const events = runLoop(config);
|
|
5372
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
6473
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
|
|
5373
6474
|
const eventsWithLayers = eventsWithCallback.pipe(
|
|
5374
6475
|
Stream10.provideLayer(ProductionLayers)
|
|
5375
6476
|
);
|
|
@@ -5382,7 +6483,7 @@ function run(options) {
|
|
|
5382
6483
|
function runTest(options, mockEvents) {
|
|
5383
6484
|
const { config, onEvent } = options;
|
|
5384
6485
|
const events = runLoop(config);
|
|
5385
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
6486
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect23.sync(() => onEvent(event)))) : events;
|
|
5386
6487
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
5387
6488
|
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
5388
6489
|
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
@@ -5390,11 +6491,11 @@ function runTest(options, mockEvents) {
|
|
|
5390
6491
|
function collectEvents(config, mockEvents) {
|
|
5391
6492
|
const events = runLoop(config);
|
|
5392
6493
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
5393
|
-
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(
|
|
6494
|
+
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect23.map((chunk) => Array.from(chunk)));
|
|
5394
6495
|
}
|
|
5395
6496
|
function main(config) {
|
|
5396
6497
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
5397
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
6498
|
+
return run({ config, consumer: consumerType }).pipe(Effect23.runPromise);
|
|
5398
6499
|
}
|
|
5399
6500
|
|
|
5400
6501
|
// src/services/index.ts
|
|
@@ -5402,7 +6503,7 @@ init_esm_shims();
|
|
|
5402
6503
|
|
|
5403
6504
|
// src/index.ts
|
|
5404
6505
|
var program = new Command();
|
|
5405
|
-
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("-
|
|
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) => {
|
|
5406
6507
|
const config = {
|
|
5407
6508
|
task,
|
|
5408
6509
|
maxIterations: Number.parseInt(options.iterations, 10),
|
|
@@ -5447,11 +6548,23 @@ export {
|
|
|
5447
6548
|
ExecutionModeSchema,
|
|
5448
6549
|
FerixParser,
|
|
5449
6550
|
FileLoggerConfigSchema,
|
|
6551
|
+
FileSystemGit,
|
|
6552
|
+
FileSystemGuardrails,
|
|
5450
6553
|
FileSystemPlan,
|
|
6554
|
+
FileSystemProgress,
|
|
5451
6555
|
FileSystemSession,
|
|
5452
6556
|
GeneratedTaskListSchema,
|
|
5453
6557
|
GeneratedTaskSchema,
|
|
5454
6558
|
GeneratedTaskStatusSchema,
|
|
6559
|
+
Git,
|
|
6560
|
+
GitError,
|
|
6561
|
+
GuardrailAddedEventSchema,
|
|
6562
|
+
GuardrailSchema,
|
|
6563
|
+
GuardrailSeveritySchema,
|
|
6564
|
+
GuardrailSignalSchema,
|
|
6565
|
+
GuardrailsFileSchema,
|
|
6566
|
+
GuardrailsStore,
|
|
6567
|
+
GuardrailsStoreError,
|
|
5455
6568
|
IterationCompletedEventSchema,
|
|
5456
6569
|
IterationStartedEventSchema,
|
|
5457
6570
|
LLM,
|
|
@@ -5461,6 +6574,9 @@ export {
|
|
|
5461
6574
|
LLMToolEndEventSchema,
|
|
5462
6575
|
LLMToolStartEventSchema,
|
|
5463
6576
|
LLMToolUseEventSchema,
|
|
6577
|
+
LearningCategorySchema,
|
|
6578
|
+
LearningRecordedEventSchema,
|
|
6579
|
+
LearningSignalSchema,
|
|
5464
6580
|
LogEntrySchema,
|
|
5465
6581
|
LogLevelSchema,
|
|
5466
6582
|
LoopCompleteSignalSchema,
|
|
@@ -5471,7 +6587,10 @@ export {
|
|
|
5471
6587
|
LoopStartedEventSchema,
|
|
5472
6588
|
LoopStatusSchema,
|
|
5473
6589
|
LoopSummarySchema,
|
|
6590
|
+
MemoryGit,
|
|
6591
|
+
MemoryGuardrails,
|
|
5474
6592
|
MemoryPlan,
|
|
6593
|
+
MemoryProgress,
|
|
5475
6594
|
MemorySession,
|
|
5476
6595
|
Mock,
|
|
5477
6596
|
Mock as MockLLM,
|
|
@@ -5501,6 +6620,12 @@ export {
|
|
|
5501
6620
|
PlanUpdateFailedEventSchema,
|
|
5502
6621
|
PlanUpdatedEventSchema,
|
|
5503
6622
|
ProductionLayers,
|
|
6623
|
+
ProgressActionSchema,
|
|
6624
|
+
ProgressEntrySchema,
|
|
6625
|
+
ProgressFileSchema,
|
|
6626
|
+
ProgressStore,
|
|
6627
|
+
ProgressStoreError,
|
|
6628
|
+
ProgressUpdatedEventSchema,
|
|
5504
6629
|
PromptConfigSchema,
|
|
5505
6630
|
ReviewCompleteDataSchema,
|
|
5506
6631
|
ReviewCompleteEventSchema,
|
|
@@ -5535,15 +6660,21 @@ export {
|
|
|
5535
6660
|
ToolStartEventSchema,
|
|
5536
6661
|
ToolUseEventSchema,
|
|
5537
6662
|
ViewModeSchema,
|
|
6663
|
+
WorktreeCreatedEventSchema,
|
|
6664
|
+
WorktreeRemovedEventSchema,
|
|
5538
6665
|
buildPrompt,
|
|
5539
6666
|
collectEvents,
|
|
5540
6667
|
createHeadlessConsumer,
|
|
5541
6668
|
createTUIConsumer,
|
|
5542
6669
|
createTestLayers,
|
|
6670
|
+
decodeGuardrail,
|
|
6671
|
+
decodeGuardrailsFile,
|
|
5543
6672
|
decodeLLMEvent,
|
|
5544
6673
|
decodeLoopConfig,
|
|
5545
6674
|
decodePlan,
|
|
5546
6675
|
decodePlanData,
|
|
6676
|
+
decodeProgressEntry,
|
|
6677
|
+
decodeProgressFile,
|
|
5547
6678
|
decodeSession,
|
|
5548
6679
|
decodeSignal,
|
|
5549
6680
|
decodeSignalSync,
|