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