ferix-code 0.0.2-beta.11 → 0.0.2-beta.13
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 +88 -2
- package/dist/index.js +358 -249
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ import { Command } from "commander";
|
|
|
71
71
|
// package.json
|
|
72
72
|
var package_default = {
|
|
73
73
|
name: "ferix-code",
|
|
74
|
-
version: "0.0.2-beta.
|
|
74
|
+
version: "0.0.2-beta.13",
|
|
75
75
|
description: "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
76
76
|
type: "module",
|
|
77
77
|
bin: {
|
|
@@ -118,7 +118,7 @@ var package_default = {
|
|
|
118
118
|
|
|
119
119
|
// src/program.ts
|
|
120
120
|
init_esm_shims();
|
|
121
|
-
import { Effect as
|
|
121
|
+
import { Effect as Effect25, Stream as Stream11 } from "effect";
|
|
122
122
|
|
|
123
123
|
// src/consumers/index.ts
|
|
124
124
|
init_esm_shims();
|
|
@@ -616,9 +616,11 @@ function separator(width) {
|
|
|
616
616
|
);
|
|
617
617
|
}
|
|
618
618
|
function borderedLine(content, width) {
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
|
|
619
|
+
const innerWidth = width - 4;
|
|
620
|
+
const finalContent = stripAnsi(content).length > innerWidth ? truncate(content, innerWidth) : content;
|
|
621
|
+
const finalStripped = stripAnsi(finalContent);
|
|
622
|
+
const padding = Math.max(0, innerWidth - finalStripped.length);
|
|
623
|
+
return `${pc11.cyan(box.vertical)} ${finalContent}${" ".repeat(padding)} ${pc11.cyan(box.vertical)}`;
|
|
622
624
|
}
|
|
623
625
|
function emptyBorderedLine(width) {
|
|
624
626
|
const repeatCount = Math.max(0, width - 2);
|
|
@@ -2374,7 +2376,7 @@ ${errorText}
|
|
|
2374
2376
|
|
|
2375
2377
|
// src/layers/index.ts
|
|
2376
2378
|
init_esm_shims();
|
|
2377
|
-
import { Layer as
|
|
2379
|
+
import { Layer as Layer15 } from "effect";
|
|
2378
2380
|
|
|
2379
2381
|
// src/layers/git/file-system.ts
|
|
2380
2382
|
init_esm_shims();
|
|
@@ -2768,6 +2770,7 @@ init_esm_shims();
|
|
|
2768
2770
|
// src/domain/schemas/config.ts
|
|
2769
2771
|
init_esm_shims();
|
|
2770
2772
|
import { Schema as S } from "effect";
|
|
2773
|
+
var ProviderNameSchema = S.Literal("claude", "cursor");
|
|
2771
2774
|
var PhasePromptOverridesSchema = S.Struct({
|
|
2772
2775
|
breakdown: S.optional(S.String),
|
|
2773
2776
|
planning: S.optional(S.String),
|
|
@@ -2791,7 +2794,9 @@ var LoopConfigSchema = S.Struct({
|
|
|
2791
2794
|
push: S.optional(S.Boolean),
|
|
2792
2795
|
pr: S.optional(S.Boolean),
|
|
2793
2796
|
verbose: S.optional(S.Boolean),
|
|
2794
|
-
prompts: S.optional(PromptConfigSchema)
|
|
2797
|
+
prompts: S.optional(PromptConfigSchema),
|
|
2798
|
+
/** LLM provider to use. Defaults to "claude". */
|
|
2799
|
+
provider: S.optional(ProviderNameSchema)
|
|
2795
2800
|
});
|
|
2796
2801
|
var LoopSummarySchema = S.Struct({
|
|
2797
2802
|
iterations: S.Number,
|
|
@@ -3663,10 +3668,9 @@ var MemoryGuardrails = {
|
|
|
3663
3668
|
layer: layer2
|
|
3664
3669
|
};
|
|
3665
3670
|
|
|
3666
|
-
// src/layers/llm/
|
|
3671
|
+
// src/layers/llm/mock.ts
|
|
3667
3672
|
init_esm_shims();
|
|
3668
|
-
import {
|
|
3669
|
-
import { Effect as Effect9, Layer as Layer5, Stream as Stream5 } from "effect";
|
|
3673
|
+
import { Effect as Effect8, Layer as Layer5, Schema as S14, Stream as Stream4 } from "effect";
|
|
3670
3674
|
|
|
3671
3675
|
// src/services/llm.ts
|
|
3672
3676
|
init_esm_shims();
|
|
@@ -3674,12 +3678,59 @@ import { Context as Context3 } from "effect";
|
|
|
3674
3678
|
var LLM = class extends Context3.Tag("@ferix/LLM")() {
|
|
3675
3679
|
};
|
|
3676
3680
|
|
|
3681
|
+
// src/layers/llm/mock.ts
|
|
3682
|
+
var MockLLMConfigSchema = S14.Struct({
|
|
3683
|
+
events: S14.Array(LLMEventSchema),
|
|
3684
|
+
delayMs: S14.optional(S14.Number)
|
|
3685
|
+
});
|
|
3686
|
+
function createMockLLM(config) {
|
|
3687
|
+
return {
|
|
3688
|
+
execute: (_prompt, _options) => {
|
|
3689
|
+
const baseStream = Stream4.fromIterable(config.events);
|
|
3690
|
+
if (config.delayMs !== void 0 && config.delayMs > 0) {
|
|
3691
|
+
const delay = config.delayMs;
|
|
3692
|
+
return baseStream.pipe(Stream4.tap(() => Effect8.sleep(delay)));
|
|
3693
|
+
}
|
|
3694
|
+
return baseStream;
|
|
3695
|
+
}
|
|
3696
|
+
};
|
|
3697
|
+
}
|
|
3698
|
+
var defaultMockEvents = [
|
|
3699
|
+
{ _tag: "Text", text: "Processing task...\n" },
|
|
3700
|
+
{ _tag: "ToolStart", tool: "Read" },
|
|
3701
|
+
{ _tag: "ToolEnd", tool: "Read" },
|
|
3702
|
+
{ _tag: "Text", text: "Task completed successfully.\n" },
|
|
3703
|
+
{
|
|
3704
|
+
_tag: "Done",
|
|
3705
|
+
output: "Processing task...\nTask completed successfully.\n"
|
|
3706
|
+
}
|
|
3707
|
+
];
|
|
3708
|
+
var defaultMock = createMockLLM({ events: defaultMockEvents });
|
|
3709
|
+
var Live5 = Layer5.succeed(LLM, defaultMock);
|
|
3710
|
+
function layer3(config) {
|
|
3711
|
+
return Layer5.succeed(LLM, createMockLLM(config));
|
|
3712
|
+
}
|
|
3713
|
+
var Mock = {
|
|
3714
|
+
Live: Live5,
|
|
3715
|
+
layer: layer3,
|
|
3716
|
+
createMockLLM
|
|
3717
|
+
};
|
|
3718
|
+
|
|
3719
|
+
// src/layers/llm/providers/index.ts
|
|
3720
|
+
init_esm_shims();
|
|
3721
|
+
import { Effect as Effect12 } from "effect";
|
|
3722
|
+
|
|
3723
|
+
// src/layers/llm/providers/claude.ts
|
|
3724
|
+
init_esm_shims();
|
|
3725
|
+
import { spawn } from "child_process";
|
|
3726
|
+
import { Effect as Effect10, Layer as Layer6, Stream as Stream6 } from "effect";
|
|
3727
|
+
|
|
3677
3728
|
// src/layers/llm/stream.ts
|
|
3678
3729
|
init_esm_shims();
|
|
3679
3730
|
import { createInterface } from "readline";
|
|
3680
|
-
import { Effect as
|
|
3731
|
+
import { Effect as Effect9, Stream as Stream5 } from "effect";
|
|
3681
3732
|
|
|
3682
|
-
// src/layers/llm/parsers.ts
|
|
3733
|
+
// src/layers/llm/providers/parsers/claude.ts
|
|
3683
3734
|
init_esm_shims();
|
|
3684
3735
|
function parseJsonLine(line) {
|
|
3685
3736
|
if (!line.startsWith("{")) {
|
|
@@ -3795,7 +3846,7 @@ function processJsonLine(json, outputChunks, toolState, emit) {
|
|
|
3795
3846
|
}
|
|
3796
3847
|
}
|
|
3797
3848
|
function createEventStream(child) {
|
|
3798
|
-
return
|
|
3849
|
+
return Stream5.async((emit) => {
|
|
3799
3850
|
const outputChunks = [];
|
|
3800
3851
|
const toolState = { currentTool: "", inputChunks: [] };
|
|
3801
3852
|
const stdout = child.stdout;
|
|
@@ -3803,7 +3854,7 @@ function createEventStream(child) {
|
|
|
3803
3854
|
emit.fail(
|
|
3804
3855
|
new LLMError({ message: "Failed to get stdout from child process" })
|
|
3805
3856
|
);
|
|
3806
|
-
return
|
|
3857
|
+
return Effect9.void;
|
|
3807
3858
|
}
|
|
3808
3859
|
const rl = createInterface({
|
|
3809
3860
|
input: stdout,
|
|
@@ -3842,17 +3893,18 @@ function createEventStream(child) {
|
|
|
3842
3893
|
})
|
|
3843
3894
|
);
|
|
3844
3895
|
});
|
|
3845
|
-
return
|
|
3896
|
+
return Effect9.sync(() => {
|
|
3846
3897
|
child.kill("SIGTERM");
|
|
3847
3898
|
});
|
|
3848
3899
|
});
|
|
3849
3900
|
}
|
|
3850
3901
|
|
|
3851
|
-
// src/layers/llm/claude
|
|
3852
|
-
var
|
|
3902
|
+
// src/layers/llm/providers/claude.ts
|
|
3903
|
+
var ClaudeProvider = {
|
|
3904
|
+
name: "claude",
|
|
3853
3905
|
execute: (prompt, options) => {
|
|
3854
|
-
return
|
|
3855
|
-
|
|
3906
|
+
return Stream6.unwrap(
|
|
3907
|
+
Effect10.sync(() => {
|
|
3856
3908
|
const child = spawn(
|
|
3857
3909
|
"claude",
|
|
3858
3910
|
[
|
|
@@ -3879,56 +3931,72 @@ var make3 = {
|
|
|
3879
3931
|
);
|
|
3880
3932
|
}
|
|
3881
3933
|
};
|
|
3882
|
-
var
|
|
3934
|
+
var make3 = ClaudeProvider;
|
|
3935
|
+
var Live6 = Layer6.succeed(LLM, make3);
|
|
3883
3936
|
var ClaudeCLI = {
|
|
3884
|
-
Live:
|
|
3937
|
+
Live: Live6,
|
|
3938
|
+
Provider: ClaudeProvider
|
|
3885
3939
|
};
|
|
3886
3940
|
|
|
3887
|
-
// src/layers/llm/
|
|
3941
|
+
// src/layers/llm/providers/cursor.ts
|
|
3888
3942
|
init_esm_shims();
|
|
3889
|
-
import {
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3943
|
+
import { spawn as spawn2 } from "child_process";
|
|
3944
|
+
import { Effect as Effect11, Layer as Layer7, Stream as Stream7 } from "effect";
|
|
3945
|
+
var CursorProvider = {
|
|
3946
|
+
name: "cursor",
|
|
3947
|
+
execute: (prompt, options) => {
|
|
3948
|
+
return Stream7.unwrap(
|
|
3949
|
+
Effect11.sync(() => {
|
|
3950
|
+
const child = spawn2(
|
|
3951
|
+
"cursor-agent",
|
|
3952
|
+
[
|
|
3953
|
+
"--print",
|
|
3954
|
+
"--force",
|
|
3955
|
+
"--output-format",
|
|
3956
|
+
"stream-json",
|
|
3957
|
+
"-p",
|
|
3958
|
+
prompt
|
|
3959
|
+
],
|
|
3960
|
+
{
|
|
3961
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
3962
|
+
cwd: options?.cwd,
|
|
3963
|
+
env: {
|
|
3964
|
+
...process.env,
|
|
3965
|
+
FORCE_COLOR: "1"
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
);
|
|
3969
|
+
return createEventStream(child);
|
|
3970
|
+
})
|
|
3971
|
+
);
|
|
3972
|
+
}
|
|
3973
|
+
};
|
|
3974
|
+
var make4 = CursorProvider;
|
|
3975
|
+
var Live7 = Layer7.succeed(LLM, make4);
|
|
3976
|
+
var CursorCLI = {
|
|
3977
|
+
Live: Live7,
|
|
3978
|
+
Provider: CursorProvider
|
|
3979
|
+
};
|
|
3980
|
+
|
|
3981
|
+
// src/layers/llm/providers/index.ts
|
|
3982
|
+
function createProviderLayer(name) {
|
|
3983
|
+
switch (name) {
|
|
3984
|
+
case "claude":
|
|
3985
|
+
return ClaudeCLI.Live;
|
|
3986
|
+
case "cursor":
|
|
3987
|
+
return CursorCLI.Live;
|
|
3988
|
+
default: {
|
|
3989
|
+
const _exhaustive = name;
|
|
3990
|
+
return _exhaustive;
|
|
3903
3991
|
}
|
|
3904
|
-
};
|
|
3905
|
-
}
|
|
3906
|
-
var defaultMockEvents = [
|
|
3907
|
-
{ _tag: "Text", text: "Processing task...\n" },
|
|
3908
|
-
{ _tag: "ToolStart", tool: "Read" },
|
|
3909
|
-
{ _tag: "ToolEnd", tool: "Read" },
|
|
3910
|
-
{ _tag: "Text", text: "Task completed successfully.\n" },
|
|
3911
|
-
{
|
|
3912
|
-
_tag: "Done",
|
|
3913
|
-
output: "Processing task...\nTask completed successfully.\n"
|
|
3914
3992
|
}
|
|
3915
|
-
];
|
|
3916
|
-
var defaultMock = createMockLLM({ events: defaultMockEvents });
|
|
3917
|
-
var Live6 = Layer6.succeed(LLM, defaultMock);
|
|
3918
|
-
function layer3(config) {
|
|
3919
|
-
return Layer6.succeed(LLM, createMockLLM(config));
|
|
3920
3993
|
}
|
|
3921
|
-
var Mock = {
|
|
3922
|
-
Live: Live6,
|
|
3923
|
-
layer: layer3,
|
|
3924
|
-
createMockLLM
|
|
3925
|
-
};
|
|
3926
3994
|
|
|
3927
3995
|
// src/layers/plan/file-system.ts
|
|
3928
3996
|
init_esm_shims();
|
|
3929
3997
|
import { access as access2, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3930
3998
|
import { join as join3 } from "path";
|
|
3931
|
-
import { Effect as
|
|
3999
|
+
import { Effect as Effect13, Layer as Layer8 } from "effect";
|
|
3932
4000
|
|
|
3933
4001
|
// src/services/plan-store.ts
|
|
3934
4002
|
init_esm_shims();
|
|
@@ -3939,14 +4007,14 @@ var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
|
|
|
3939
4007
|
// src/layers/plan/file-system.ts
|
|
3940
4008
|
var PLANS_DIR2 = ".ferix/plans";
|
|
3941
4009
|
function ensureDir2(dirPath) {
|
|
3942
|
-
return
|
|
4010
|
+
return Effect13.tryPromise({
|
|
3943
4011
|
try: () => mkdir3(dirPath, { recursive: true }),
|
|
3944
4012
|
catch: (error) => new PlanStoreError({
|
|
3945
4013
|
message: `Failed to create directory: ${dirPath}`,
|
|
3946
4014
|
operation: "create",
|
|
3947
4015
|
cause: error
|
|
3948
4016
|
})
|
|
3949
|
-
}).pipe(
|
|
4017
|
+
}).pipe(Effect13.asVoid);
|
|
3950
4018
|
}
|
|
3951
4019
|
function getSessionDir2(sessionId) {
|
|
3952
4020
|
return join3(process.cwd(), PLANS_DIR2, sessionId);
|
|
@@ -3961,8 +4029,8 @@ function serializePlan(plan) {
|
|
|
3961
4029
|
return JSON.stringify(plan, null, 2);
|
|
3962
4030
|
}
|
|
3963
4031
|
function deserializePlan(json, planId) {
|
|
3964
|
-
return
|
|
3965
|
-
const parsed = yield*
|
|
4032
|
+
return Effect13.gen(function* () {
|
|
4033
|
+
const parsed = yield* Effect13.try({
|
|
3966
4034
|
try: () => JSON.parse(json),
|
|
3967
4035
|
catch: (error) => new PlanStoreError({
|
|
3968
4036
|
message: `Invalid JSON in plan file: ${String(error)}`,
|
|
@@ -3971,7 +4039,7 @@ function deserializePlan(json, planId) {
|
|
|
3971
4039
|
})
|
|
3972
4040
|
});
|
|
3973
4041
|
const validated = yield* decodePlanData(parsed).pipe(
|
|
3974
|
-
|
|
4042
|
+
Effect13.mapError(
|
|
3975
4043
|
(error) => new PlanStoreError({
|
|
3976
4044
|
message: `Plan validation failed: ${String(error)}`,
|
|
3977
4045
|
operation: "load",
|
|
@@ -3985,11 +4053,11 @@ function deserializePlan(json, planId) {
|
|
|
3985
4053
|
};
|
|
3986
4054
|
});
|
|
3987
4055
|
}
|
|
3988
|
-
var
|
|
3989
|
-
create: (sessionId, plan) =>
|
|
4056
|
+
var make5 = {
|
|
4057
|
+
create: (sessionId, plan) => Effect13.gen(function* () {
|
|
3990
4058
|
const sessionDir = getSessionDir2(sessionId);
|
|
3991
4059
|
yield* ensureDir2(sessionDir);
|
|
3992
|
-
const existingPlans = yield*
|
|
4060
|
+
const existingPlans = yield* Effect13.tryPromise({
|
|
3993
4061
|
try: async () => {
|
|
3994
4062
|
try {
|
|
3995
4063
|
const files = await readdir(sessionDir);
|
|
@@ -4006,7 +4074,7 @@ var make4 = {
|
|
|
4006
4074
|
const planId = generatePlanId(existingPlans + 1);
|
|
4007
4075
|
const planPath = getPlanPath(sessionId, planId);
|
|
4008
4076
|
const fullPlan = { ...plan, id: planId };
|
|
4009
|
-
yield*
|
|
4077
|
+
yield* Effect13.tryPromise({
|
|
4010
4078
|
try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
|
|
4011
4079
|
catch: (error) => new PlanStoreError({
|
|
4012
4080
|
message: `Failed to write plan file: ${planPath}`,
|
|
@@ -4016,10 +4084,10 @@ var make4 = {
|
|
|
4016
4084
|
});
|
|
4017
4085
|
return planId;
|
|
4018
4086
|
}),
|
|
4019
|
-
load: (planId, sessionId) =>
|
|
4087
|
+
load: (planId, sessionId) => Effect13.gen(function* () {
|
|
4020
4088
|
if (sessionId) {
|
|
4021
4089
|
const planPath = getPlanPath(sessionId, planId);
|
|
4022
|
-
const content = yield*
|
|
4090
|
+
const content = yield* Effect13.tryPromise({
|
|
4023
4091
|
try: () => readFile2(planPath, "utf-8"),
|
|
4024
4092
|
catch: (error) => new PlanStoreError({
|
|
4025
4093
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4029,7 +4097,7 @@ var make4 = {
|
|
|
4029
4097
|
});
|
|
4030
4098
|
return yield* deserializePlan(content, planId);
|
|
4031
4099
|
}
|
|
4032
|
-
const sessionDirs = yield*
|
|
4100
|
+
const sessionDirs = yield* Effect13.tryPromise({
|
|
4033
4101
|
try: async () => {
|
|
4034
4102
|
const plansDir = join3(process.cwd(), PLANS_DIR2);
|
|
4035
4103
|
const dirs = await readdir(plansDir);
|
|
@@ -4043,7 +4111,7 @@ var make4 = {
|
|
|
4043
4111
|
});
|
|
4044
4112
|
for (const sid of sessionDirs) {
|
|
4045
4113
|
const planPath = getPlanPath(sid, planId);
|
|
4046
|
-
const exists = yield*
|
|
4114
|
+
const exists = yield* Effect13.tryPromise({
|
|
4047
4115
|
try: async () => {
|
|
4048
4116
|
await access2(planPath);
|
|
4049
4117
|
return true;
|
|
@@ -4052,9 +4120,9 @@ var make4 = {
|
|
|
4052
4120
|
message: "File not found",
|
|
4053
4121
|
operation: "load"
|
|
4054
4122
|
})
|
|
4055
|
-
}).pipe(
|
|
4123
|
+
}).pipe(Effect13.orElseSucceed(() => false));
|
|
4056
4124
|
if (exists) {
|
|
4057
|
-
const content = yield*
|
|
4125
|
+
const content = yield* Effect13.tryPromise({
|
|
4058
4126
|
try: () => readFile2(planPath, "utf-8"),
|
|
4059
4127
|
catch: (error) => new PlanStoreError({
|
|
4060
4128
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4065,16 +4133,16 @@ var make4 = {
|
|
|
4065
4133
|
return yield* deserializePlan(content, planId);
|
|
4066
4134
|
}
|
|
4067
4135
|
}
|
|
4068
|
-
return yield*
|
|
4136
|
+
return yield* Effect13.fail(
|
|
4069
4137
|
new PlanStoreError({
|
|
4070
4138
|
message: `Plan not found: ${planId}`,
|
|
4071
4139
|
operation: "load"
|
|
4072
4140
|
})
|
|
4073
4141
|
);
|
|
4074
4142
|
}),
|
|
4075
|
-
update: (planId, plan) =>
|
|
4143
|
+
update: (planId, plan) => Effect13.gen(function* () {
|
|
4076
4144
|
const planPath = getPlanPath(plan.sessionId, planId);
|
|
4077
|
-
yield*
|
|
4145
|
+
yield* Effect13.tryPromise({
|
|
4078
4146
|
try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
|
|
4079
4147
|
catch: (error) => new PlanStoreError({
|
|
4080
4148
|
message: `Failed to update plan file: ${planPath}`,
|
|
@@ -4083,9 +4151,9 @@ var make4 = {
|
|
|
4083
4151
|
})
|
|
4084
4152
|
});
|
|
4085
4153
|
}),
|
|
4086
|
-
list: (sessionId) =>
|
|
4154
|
+
list: (sessionId) => Effect13.gen(function* () {
|
|
4087
4155
|
const sessionDir = getSessionDir2(sessionId);
|
|
4088
|
-
const files = yield*
|
|
4156
|
+
const files = yield* Effect13.tryPromise({
|
|
4089
4157
|
try: async () => {
|
|
4090
4158
|
try {
|
|
4091
4159
|
return await readdir(sessionDir);
|
|
@@ -4102,17 +4170,17 @@ var make4 = {
|
|
|
4102
4170
|
return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
|
|
4103
4171
|
})
|
|
4104
4172
|
};
|
|
4105
|
-
var
|
|
4173
|
+
var Live8 = Layer8.succeed(PlanStore, make5);
|
|
4106
4174
|
var FileSystemPlan = {
|
|
4107
|
-
Live:
|
|
4175
|
+
Live: Live8
|
|
4108
4176
|
};
|
|
4109
4177
|
|
|
4110
4178
|
// src/layers/plan/memory.ts
|
|
4111
4179
|
init_esm_shims();
|
|
4112
|
-
import { Effect as
|
|
4180
|
+
import { Effect as Effect14, Layer as Layer9, Ref as Ref5 } from "effect";
|
|
4113
4181
|
function createMemoryPlanStore(stateRef) {
|
|
4114
4182
|
return {
|
|
4115
|
-
create: (sessionId, plan) =>
|
|
4183
|
+
create: (sessionId, plan) => Effect14.gen(function* () {
|
|
4116
4184
|
const state = yield* Ref5.get(stateRef);
|
|
4117
4185
|
if (!state.has(sessionId)) {
|
|
4118
4186
|
state.set(sessionId, /* @__PURE__ */ new Map());
|
|
@@ -4127,7 +4195,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4127
4195
|
yield* Ref5.set(stateRef, state);
|
|
4128
4196
|
return planId;
|
|
4129
4197
|
}),
|
|
4130
|
-
load: (planId, sessionId) =>
|
|
4198
|
+
load: (planId, sessionId) => Effect14.gen(function* () {
|
|
4131
4199
|
const state = yield* Ref5.get(stateRef);
|
|
4132
4200
|
if (sessionId) {
|
|
4133
4201
|
const sessionPlans = state.get(sessionId);
|
|
@@ -4135,7 +4203,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4135
4203
|
if (plan) {
|
|
4136
4204
|
return plan;
|
|
4137
4205
|
}
|
|
4138
|
-
return yield*
|
|
4206
|
+
return yield* Effect14.fail(
|
|
4139
4207
|
new PlanStoreError({
|
|
4140
4208
|
message: `Plan not found: ${planId}`,
|
|
4141
4209
|
operation: "load"
|
|
@@ -4148,18 +4216,18 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4148
4216
|
return plan;
|
|
4149
4217
|
}
|
|
4150
4218
|
}
|
|
4151
|
-
return yield*
|
|
4219
|
+
return yield* Effect14.fail(
|
|
4152
4220
|
new PlanStoreError({
|
|
4153
4221
|
message: `Plan not found: ${planId}`,
|
|
4154
4222
|
operation: "load"
|
|
4155
4223
|
})
|
|
4156
4224
|
);
|
|
4157
4225
|
}),
|
|
4158
|
-
update: (planId, plan) =>
|
|
4226
|
+
update: (planId, plan) => Effect14.gen(function* () {
|
|
4159
4227
|
const state = yield* Ref5.get(stateRef);
|
|
4160
4228
|
const sessionPlans = state.get(plan.sessionId);
|
|
4161
4229
|
if (!sessionPlans) {
|
|
4162
|
-
return yield*
|
|
4230
|
+
return yield* Effect14.fail(
|
|
4163
4231
|
new PlanStoreError({
|
|
4164
4232
|
message: `Session not found: ${plan.sessionId}`,
|
|
4165
4233
|
operation: "update"
|
|
@@ -4169,7 +4237,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4169
4237
|
sessionPlans.set(planId, plan);
|
|
4170
4238
|
yield* Ref5.set(stateRef, state);
|
|
4171
4239
|
}),
|
|
4172
|
-
list: (sessionId) =>
|
|
4240
|
+
list: (sessionId) => Effect14.gen(function* () {
|
|
4173
4241
|
const state = yield* Ref5.get(stateRef);
|
|
4174
4242
|
const sessionPlans = state.get(sessionId);
|
|
4175
4243
|
if (!sessionPlans) {
|
|
@@ -4180,17 +4248,17 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4180
4248
|
};
|
|
4181
4249
|
}
|
|
4182
4250
|
function layer4() {
|
|
4183
|
-
return
|
|
4251
|
+
return Layer9.effect(
|
|
4184
4252
|
PlanStore,
|
|
4185
|
-
|
|
4253
|
+
Effect14.gen(function* () {
|
|
4186
4254
|
const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
|
|
4187
4255
|
return createMemoryPlanStore(stateRef);
|
|
4188
4256
|
})
|
|
4189
4257
|
);
|
|
4190
4258
|
}
|
|
4191
|
-
var
|
|
4259
|
+
var Live9 = layer4();
|
|
4192
4260
|
var MemoryPlan = {
|
|
4193
|
-
Live:
|
|
4261
|
+
Live: Live9,
|
|
4194
4262
|
layer: layer4
|
|
4195
4263
|
};
|
|
4196
4264
|
|
|
@@ -4198,7 +4266,7 @@ var MemoryPlan = {
|
|
|
4198
4266
|
init_esm_shims();
|
|
4199
4267
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
4200
4268
|
import { join as join4 } from "path";
|
|
4201
|
-
import { DateTime as DateTime3, Effect as
|
|
4269
|
+
import { DateTime as DateTime3, Effect as Effect15, Layer as Layer10 } from "effect";
|
|
4202
4270
|
|
|
4203
4271
|
// src/services/progress-store.ts
|
|
4204
4272
|
init_esm_shims();
|
|
@@ -4209,14 +4277,14 @@ var ProgressStore = class extends Context5.Tag("@ferix/ProgressStore")() {
|
|
|
4209
4277
|
// src/layers/progress/file-system.ts
|
|
4210
4278
|
var PLANS_DIR3 = ".ferix/plans";
|
|
4211
4279
|
function ensureDir3(dirPath) {
|
|
4212
|
-
return
|
|
4280
|
+
return Effect15.tryPromise({
|
|
4213
4281
|
try: () => mkdir4(dirPath, { recursive: true }),
|
|
4214
4282
|
catch: (error) => new ProgressStoreError({
|
|
4215
4283
|
message: `Failed to create directory: ${dirPath}`,
|
|
4216
4284
|
operation: "append",
|
|
4217
4285
|
cause: error
|
|
4218
4286
|
})
|
|
4219
|
-
}).pipe(
|
|
4287
|
+
}).pipe(Effect15.asVoid);
|
|
4220
4288
|
}
|
|
4221
4289
|
function getSessionDir3(sessionId) {
|
|
4222
4290
|
return join4(process.cwd(), PLANS_DIR3, sessionId);
|
|
@@ -4228,8 +4296,8 @@ function serializeProgress(progress) {
|
|
|
4228
4296
|
return JSON.stringify(progress, null, 2);
|
|
4229
4297
|
}
|
|
4230
4298
|
function deserializeProgress(json) {
|
|
4231
|
-
return
|
|
4232
|
-
const parsed = yield*
|
|
4299
|
+
return Effect15.gen(function* () {
|
|
4300
|
+
const parsed = yield* Effect15.try({
|
|
4233
4301
|
try: () => JSON.parse(json),
|
|
4234
4302
|
catch: (error) => new ProgressStoreError({
|
|
4235
4303
|
message: `Invalid JSON in progress file: ${String(error)}`,
|
|
@@ -4238,7 +4306,7 @@ function deserializeProgress(json) {
|
|
|
4238
4306
|
})
|
|
4239
4307
|
});
|
|
4240
4308
|
const validated = yield* decodeProgressFile(parsed).pipe(
|
|
4241
|
-
|
|
4309
|
+
Effect15.mapError(
|
|
4242
4310
|
(error) => new ProgressStoreError({
|
|
4243
4311
|
message: `Progress validation failed: ${String(error)}`,
|
|
4244
4312
|
operation: "load",
|
|
@@ -4256,12 +4324,12 @@ function createEmptyProgress(sessionId, createdAt) {
|
|
|
4256
4324
|
entries: []
|
|
4257
4325
|
};
|
|
4258
4326
|
}
|
|
4259
|
-
var
|
|
4260
|
-
append: (sessionId, entry) =>
|
|
4327
|
+
var make6 = {
|
|
4328
|
+
append: (sessionId, entry) => Effect15.gen(function* () {
|
|
4261
4329
|
const sessionDir = getSessionDir3(sessionId);
|
|
4262
4330
|
yield* ensureDir3(sessionDir);
|
|
4263
4331
|
const progressPath = getProgressPath(sessionId);
|
|
4264
|
-
const existing = yield*
|
|
4332
|
+
const existing = yield* Effect15.tryPromise({
|
|
4265
4333
|
try: async () => {
|
|
4266
4334
|
try {
|
|
4267
4335
|
const content = await readFile3(progressPath, "utf-8");
|
|
@@ -4279,7 +4347,7 @@ var make5 = {
|
|
|
4279
4347
|
let progress;
|
|
4280
4348
|
if (existing) {
|
|
4281
4349
|
progress = yield* deserializeProgress(existing).pipe(
|
|
4282
|
-
|
|
4350
|
+
Effect15.mapError(
|
|
4283
4351
|
(err) => new ProgressStoreError({
|
|
4284
4352
|
message: err.message,
|
|
4285
4353
|
operation: "append",
|
|
@@ -4295,7 +4363,7 @@ var make5 = {
|
|
|
4295
4363
|
...progress,
|
|
4296
4364
|
entries: [...progress.entries, entry]
|
|
4297
4365
|
};
|
|
4298
|
-
yield*
|
|
4366
|
+
yield* Effect15.tryPromise({
|
|
4299
4367
|
try: () => writeFile3(progressPath, serializeProgress(updatedProgress), "utf-8"),
|
|
4300
4368
|
catch: (error) => new ProgressStoreError({
|
|
4301
4369
|
message: `Failed to write progress file: ${progressPath}`,
|
|
@@ -4304,9 +4372,9 @@ var make5 = {
|
|
|
4304
4372
|
})
|
|
4305
4373
|
});
|
|
4306
4374
|
}),
|
|
4307
|
-
load: (sessionId) =>
|
|
4375
|
+
load: (sessionId) => Effect15.gen(function* () {
|
|
4308
4376
|
const progressPath = getProgressPath(sessionId);
|
|
4309
|
-
const content = yield*
|
|
4377
|
+
const content = yield* Effect15.tryPromise({
|
|
4310
4378
|
try: async () => {
|
|
4311
4379
|
try {
|
|
4312
4380
|
return await readFile3(progressPath, "utf-8");
|
|
@@ -4326,23 +4394,23 @@ var make5 = {
|
|
|
4326
4394
|
}
|
|
4327
4395
|
return yield* deserializeProgress(content);
|
|
4328
4396
|
}),
|
|
4329
|
-
getRecent: (sessionId, count) =>
|
|
4330
|
-
const progress = yield*
|
|
4397
|
+
getRecent: (sessionId, count) => Effect15.gen(function* () {
|
|
4398
|
+
const progress = yield* make6.load(sessionId);
|
|
4331
4399
|
const entries = progress.entries;
|
|
4332
4400
|
return entries.slice(-count);
|
|
4333
4401
|
})
|
|
4334
4402
|
};
|
|
4335
|
-
var
|
|
4403
|
+
var Live10 = Layer10.succeed(ProgressStore, make6);
|
|
4336
4404
|
var FileSystemProgress = {
|
|
4337
|
-
Live:
|
|
4405
|
+
Live: Live10
|
|
4338
4406
|
};
|
|
4339
4407
|
|
|
4340
4408
|
// src/layers/progress/memory.ts
|
|
4341
4409
|
init_esm_shims();
|
|
4342
|
-
import { DateTime as DateTime4, Effect as
|
|
4410
|
+
import { DateTime as DateTime4, Effect as Effect16, Layer as Layer11, Ref as Ref6 } from "effect";
|
|
4343
4411
|
function createMemoryProgressStore(stateRef) {
|
|
4344
4412
|
return {
|
|
4345
|
-
append: (sessionId, entry) =>
|
|
4413
|
+
append: (sessionId, entry) => Effect16.gen(function* () {
|
|
4346
4414
|
const state = yield* Ref6.get(stateRef);
|
|
4347
4415
|
let progress = state.get(sessionId);
|
|
4348
4416
|
if (!progress) {
|
|
@@ -4360,7 +4428,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4360
4428
|
state.set(sessionId, updatedProgress);
|
|
4361
4429
|
yield* Ref6.set(stateRef, state);
|
|
4362
4430
|
}),
|
|
4363
|
-
load: (sessionId) =>
|
|
4431
|
+
load: (sessionId) => Effect16.gen(function* () {
|
|
4364
4432
|
const state = yield* Ref6.get(stateRef);
|
|
4365
4433
|
const progress = state.get(sessionId);
|
|
4366
4434
|
if (!progress) {
|
|
@@ -4373,7 +4441,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4373
4441
|
}
|
|
4374
4442
|
return progress;
|
|
4375
4443
|
}),
|
|
4376
|
-
getRecent: (sessionId, count) =>
|
|
4444
|
+
getRecent: (sessionId, count) => Effect16.gen(function* () {
|
|
4377
4445
|
const state = yield* Ref6.get(stateRef);
|
|
4378
4446
|
const progress = state.get(sessionId);
|
|
4379
4447
|
if (!progress) {
|
|
@@ -4384,17 +4452,17 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4384
4452
|
};
|
|
4385
4453
|
}
|
|
4386
4454
|
function layer5() {
|
|
4387
|
-
return
|
|
4455
|
+
return Layer11.effect(
|
|
4388
4456
|
ProgressStore,
|
|
4389
|
-
|
|
4457
|
+
Effect16.gen(function* () {
|
|
4390
4458
|
const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
|
|
4391
4459
|
return createMemoryProgressStore(stateRef);
|
|
4392
4460
|
})
|
|
4393
4461
|
);
|
|
4394
4462
|
}
|
|
4395
|
-
var
|
|
4463
|
+
var Live11 = layer5();
|
|
4396
4464
|
var MemoryProgress = {
|
|
4397
|
-
Live:
|
|
4465
|
+
Live: Live11,
|
|
4398
4466
|
layer: layer5
|
|
4399
4467
|
};
|
|
4400
4468
|
|
|
@@ -4402,7 +4470,7 @@ var MemoryProgress = {
|
|
|
4402
4470
|
init_esm_shims();
|
|
4403
4471
|
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
4404
4472
|
import { join as join5 } from "path";
|
|
4405
|
-
import { DateTime as DateTime5, Effect as
|
|
4473
|
+
import { DateTime as DateTime5, Effect as Effect17, Layer as Layer12 } from "effect";
|
|
4406
4474
|
import { humanId } from "human-id";
|
|
4407
4475
|
|
|
4408
4476
|
// src/services/session-store.ts
|
|
@@ -4418,14 +4486,14 @@ function generateSessionId(timestampMs) {
|
|
|
4418
4486
|
return `${id}-${timestampMs}`;
|
|
4419
4487
|
}
|
|
4420
4488
|
function ensureDir4(dirPath) {
|
|
4421
|
-
return
|
|
4489
|
+
return Effect17.tryPromise({
|
|
4422
4490
|
try: () => mkdir5(dirPath, { recursive: true }),
|
|
4423
4491
|
catch: (error) => new SessionStoreError({
|
|
4424
4492
|
message: `Failed to create directory: ${dirPath}`,
|
|
4425
4493
|
operation: "create",
|
|
4426
4494
|
cause: error
|
|
4427
4495
|
})
|
|
4428
|
-
}).pipe(
|
|
4496
|
+
}).pipe(Effect17.asVoid);
|
|
4429
4497
|
}
|
|
4430
4498
|
function getSessionPath(sessionId) {
|
|
4431
4499
|
return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
|
|
@@ -4434,8 +4502,8 @@ function serializeSession(session) {
|
|
|
4434
4502
|
return JSON.stringify(session, null, 2);
|
|
4435
4503
|
}
|
|
4436
4504
|
function deserializeSession(json) {
|
|
4437
|
-
return
|
|
4438
|
-
const parsed = yield*
|
|
4505
|
+
return Effect17.gen(function* () {
|
|
4506
|
+
const parsed = yield* Effect17.try({
|
|
4439
4507
|
try: () => JSON.parse(json),
|
|
4440
4508
|
catch: (error) => new SessionStoreError({
|
|
4441
4509
|
message: `Invalid JSON in session file: ${String(error)}`,
|
|
@@ -4444,7 +4512,7 @@ function deserializeSession(json) {
|
|
|
4444
4512
|
})
|
|
4445
4513
|
});
|
|
4446
4514
|
const validated = yield* decodeSession(parsed).pipe(
|
|
4447
|
-
|
|
4515
|
+
Effect17.mapError(
|
|
4448
4516
|
(error) => new SessionStoreError({
|
|
4449
4517
|
message: `Session validation failed: ${String(error)}`,
|
|
4450
4518
|
operation: "get",
|
|
@@ -4455,8 +4523,8 @@ function deserializeSession(json) {
|
|
|
4455
4523
|
return validated;
|
|
4456
4524
|
});
|
|
4457
4525
|
}
|
|
4458
|
-
var
|
|
4459
|
-
create: (originalTask) =>
|
|
4526
|
+
var make7 = {
|
|
4527
|
+
create: (originalTask) => Effect17.gen(function* () {
|
|
4460
4528
|
const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
|
|
4461
4529
|
yield* ensureDir4(sessionsDir);
|
|
4462
4530
|
const now = yield* DateTime5.now;
|
|
@@ -4470,7 +4538,7 @@ var make6 = {
|
|
|
4470
4538
|
completedTasks: []
|
|
4471
4539
|
};
|
|
4472
4540
|
const sessionPath = getSessionPath(sessionId);
|
|
4473
|
-
yield*
|
|
4541
|
+
yield* Effect17.tryPromise({
|
|
4474
4542
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4475
4543
|
catch: (error) => new SessionStoreError({
|
|
4476
4544
|
message: `Failed to write session file: ${sessionPath}`,
|
|
@@ -4480,9 +4548,9 @@ var make6 = {
|
|
|
4480
4548
|
});
|
|
4481
4549
|
return session;
|
|
4482
4550
|
}),
|
|
4483
|
-
get: (sessionId) =>
|
|
4551
|
+
get: (sessionId) => Effect17.gen(function* () {
|
|
4484
4552
|
const sessionPath = getSessionPath(sessionId);
|
|
4485
|
-
const content = yield*
|
|
4553
|
+
const content = yield* Effect17.tryPromise({
|
|
4486
4554
|
try: () => readFile4(sessionPath, "utf-8"),
|
|
4487
4555
|
catch: (error) => new SessionStoreError({
|
|
4488
4556
|
message: `Failed to read session file: ${sessionPath}`,
|
|
@@ -4492,9 +4560,9 @@ var make6 = {
|
|
|
4492
4560
|
});
|
|
4493
4561
|
return yield* deserializeSession(content);
|
|
4494
4562
|
}),
|
|
4495
|
-
update: (sessionId, session) =>
|
|
4563
|
+
update: (sessionId, session) => Effect17.gen(function* () {
|
|
4496
4564
|
const sessionPath = getSessionPath(sessionId);
|
|
4497
|
-
yield*
|
|
4565
|
+
yield* Effect17.tryPromise({
|
|
4498
4566
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4499
4567
|
catch: (error) => new SessionStoreError({
|
|
4500
4568
|
message: `Failed to update session file: ${sessionPath}`,
|
|
@@ -4504,17 +4572,17 @@ var make6 = {
|
|
|
4504
4572
|
});
|
|
4505
4573
|
})
|
|
4506
4574
|
};
|
|
4507
|
-
var
|
|
4575
|
+
var Live12 = Layer12.succeed(SessionStore, make7);
|
|
4508
4576
|
var FileSystemSession = {
|
|
4509
|
-
Live:
|
|
4577
|
+
Live: Live12
|
|
4510
4578
|
};
|
|
4511
4579
|
|
|
4512
4580
|
// src/layers/session/memory.ts
|
|
4513
4581
|
init_esm_shims();
|
|
4514
|
-
import { DateTime as DateTime6, Effect as
|
|
4582
|
+
import { DateTime as DateTime6, Effect as Effect18, Layer as Layer13, Ref as Ref7 } from "effect";
|
|
4515
4583
|
function createMemorySessionStore(stateRef, counterRef) {
|
|
4516
4584
|
return {
|
|
4517
|
-
create: (originalTask) =>
|
|
4585
|
+
create: (originalTask) => Effect18.gen(function* () {
|
|
4518
4586
|
const state = yield* Ref7.get(stateRef);
|
|
4519
4587
|
const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
|
|
4520
4588
|
const sessionId = `test-session-${counter}`;
|
|
@@ -4530,11 +4598,11 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4530
4598
|
yield* Ref7.set(stateRef, state);
|
|
4531
4599
|
return session;
|
|
4532
4600
|
}),
|
|
4533
|
-
get: (sessionId) =>
|
|
4601
|
+
get: (sessionId) => Effect18.gen(function* () {
|
|
4534
4602
|
const state = yield* Ref7.get(stateRef);
|
|
4535
4603
|
const session = state.get(sessionId);
|
|
4536
4604
|
if (!session) {
|
|
4537
|
-
return yield*
|
|
4605
|
+
return yield* Effect18.fail(
|
|
4538
4606
|
new SessionStoreError({
|
|
4539
4607
|
message: `Session not found: ${sessionId}`,
|
|
4540
4608
|
operation: "get"
|
|
@@ -4543,10 +4611,10 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4543
4611
|
}
|
|
4544
4612
|
return session;
|
|
4545
4613
|
}),
|
|
4546
|
-
update: (sessionId, session) =>
|
|
4614
|
+
update: (sessionId, session) => Effect18.gen(function* () {
|
|
4547
4615
|
const state = yield* Ref7.get(stateRef);
|
|
4548
4616
|
if (!state.has(sessionId)) {
|
|
4549
|
-
return yield*
|
|
4617
|
+
return yield* Effect18.fail(
|
|
4550
4618
|
new SessionStoreError({
|
|
4551
4619
|
message: `Session not found: ${sessionId}`,
|
|
4552
4620
|
operation: "update"
|
|
@@ -4559,24 +4627,24 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4559
4627
|
};
|
|
4560
4628
|
}
|
|
4561
4629
|
function layer6() {
|
|
4562
|
-
return
|
|
4630
|
+
return Layer13.effect(
|
|
4563
4631
|
SessionStore,
|
|
4564
|
-
|
|
4632
|
+
Effect18.gen(function* () {
|
|
4565
4633
|
const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
|
|
4566
4634
|
const counterRef = yield* Ref7.make(0);
|
|
4567
4635
|
return createMemorySessionStore(stateRef, counterRef);
|
|
4568
4636
|
})
|
|
4569
4637
|
);
|
|
4570
4638
|
}
|
|
4571
|
-
var
|
|
4639
|
+
var Live13 = layer6();
|
|
4572
4640
|
var MemorySession = {
|
|
4573
|
-
Live:
|
|
4641
|
+
Live: Live13,
|
|
4574
4642
|
layer: layer6
|
|
4575
4643
|
};
|
|
4576
4644
|
|
|
4577
4645
|
// src/layers/signal/ferix-parser.ts
|
|
4578
4646
|
init_esm_shims();
|
|
4579
|
-
import { Effect as
|
|
4647
|
+
import { Effect as Effect19, Layer as Layer14, Ref as Ref8 } from "effect";
|
|
4580
4648
|
|
|
4581
4649
|
// src/services/signal-parser.ts
|
|
4582
4650
|
init_esm_shims();
|
|
@@ -5086,10 +5154,10 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
5086
5154
|
// src/layers/signal/ferix-parser.ts
|
|
5087
5155
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
5088
5156
|
function createAccumulatorImpl() {
|
|
5089
|
-
return
|
|
5157
|
+
return Effect19.gen(function* () {
|
|
5090
5158
|
const chunksRef = yield* Ref8.make([]);
|
|
5091
5159
|
const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
|
|
5092
|
-
const feed = (text) =>
|
|
5160
|
+
const feed = (text) => Effect19.gen(function* () {
|
|
5093
5161
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5094
5162
|
chunks.push(text);
|
|
5095
5163
|
const buffer = chunks.join("");
|
|
@@ -5117,7 +5185,7 @@ function createAccumulatorImpl() {
|
|
|
5117
5185
|
yield* Ref8.set(emittedRef, emitted);
|
|
5118
5186
|
return newSignals;
|
|
5119
5187
|
});
|
|
5120
|
-
const flush = () =>
|
|
5188
|
+
const flush = () => Effect19.gen(function* () {
|
|
5121
5189
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5122
5190
|
const buffer = chunks.join("");
|
|
5123
5191
|
yield* Ref8.set(chunksRef, []);
|
|
@@ -5132,17 +5200,41 @@ function createAccumulatorImpl() {
|
|
|
5132
5200
|
return { feed, flush };
|
|
5133
5201
|
});
|
|
5134
5202
|
}
|
|
5135
|
-
var
|
|
5136
|
-
parse: (text) =>
|
|
5203
|
+
var make8 = {
|
|
5204
|
+
parse: (text) => Effect19.succeed(signalSpecRegistry.parseAll(text)),
|
|
5137
5205
|
createAccumulator: createAccumulatorImpl
|
|
5138
5206
|
};
|
|
5139
|
-
var
|
|
5207
|
+
var Live14 = Layer14.succeed(SignalParser, make8);
|
|
5140
5208
|
var FerixParser = {
|
|
5141
|
-
Live:
|
|
5209
|
+
Live: Live14
|
|
5210
|
+
};
|
|
5211
|
+
|
|
5212
|
+
// src/layers/llm/types.ts
|
|
5213
|
+
init_esm_shims();
|
|
5214
|
+
var PROVIDER_CONFIGS = {
|
|
5215
|
+
claude: {
|
|
5216
|
+
name: "claude",
|
|
5217
|
+
cliCommand: "claude",
|
|
5218
|
+
args: [
|
|
5219
|
+
"--permission-mode",
|
|
5220
|
+
"acceptEdits",
|
|
5221
|
+
"--output-format",
|
|
5222
|
+
"stream-json",
|
|
5223
|
+
"--verbose",
|
|
5224
|
+
"--include-partial-messages"
|
|
5225
|
+
],
|
|
5226
|
+
permissions: "acceptEdits"
|
|
5227
|
+
},
|
|
5228
|
+
cursor: {
|
|
5229
|
+
name: "cursor",
|
|
5230
|
+
cliCommand: "cursor-agent",
|
|
5231
|
+
args: ["--print", "--force", "--output-format", "stream-json"],
|
|
5232
|
+
permissions: "acceptEdits"
|
|
5233
|
+
}
|
|
5142
5234
|
};
|
|
5143
5235
|
|
|
5144
5236
|
// src/layers/index.ts
|
|
5145
|
-
var ProductionLayers =
|
|
5237
|
+
var ProductionLayers = Layer15.mergeAll(
|
|
5146
5238
|
ClaudeCLI.Live,
|
|
5147
5239
|
FerixParser.Live,
|
|
5148
5240
|
FileSystemPlan.Live,
|
|
@@ -5151,7 +5243,19 @@ var ProductionLayers = Layer14.mergeAll(
|
|
|
5151
5243
|
FileSystemGuardrails.Live,
|
|
5152
5244
|
FileSystemGit.Live
|
|
5153
5245
|
);
|
|
5154
|
-
|
|
5246
|
+
function createProductionLayers(provider = "claude") {
|
|
5247
|
+
const llmLayer = createProviderLayer(provider);
|
|
5248
|
+
return Layer15.mergeAll(
|
|
5249
|
+
llmLayer,
|
|
5250
|
+
FerixParser.Live,
|
|
5251
|
+
FileSystemPlan.Live,
|
|
5252
|
+
FileSystemSession.Live,
|
|
5253
|
+
FileSystemProgress.Live,
|
|
5254
|
+
FileSystemGuardrails.Live,
|
|
5255
|
+
FileSystemGit.Live
|
|
5256
|
+
);
|
|
5257
|
+
}
|
|
5258
|
+
var TestLayers = Layer15.mergeAll(
|
|
5155
5259
|
Mock.Live,
|
|
5156
5260
|
FerixParser.Live,
|
|
5157
5261
|
MemoryPlan.Live,
|
|
@@ -5161,7 +5265,7 @@ var TestLayers = Layer14.mergeAll(
|
|
|
5161
5265
|
MemoryGit.Live
|
|
5162
5266
|
);
|
|
5163
5267
|
function createTestLayers(events) {
|
|
5164
|
-
return
|
|
5268
|
+
return Layer15.mergeAll(
|
|
5165
5269
|
Mock.layer({ events }),
|
|
5166
5270
|
FerixParser.Live,
|
|
5167
5271
|
MemoryPlan.layer(),
|
|
@@ -5177,27 +5281,27 @@ init_esm_shims();
|
|
|
5177
5281
|
|
|
5178
5282
|
// src/orchestrator/loop.ts
|
|
5179
5283
|
init_esm_shims();
|
|
5180
|
-
import { DateTime as DateTime10, Effect as
|
|
5284
|
+
import { DateTime as DateTime10, Effect as Effect24, Option, pipe as pipe3, Ref as Ref12, Stream as Stream10 } from "effect";
|
|
5181
5285
|
|
|
5182
5286
|
// src/orchestrator/discovery.ts
|
|
5183
5287
|
init_esm_shims();
|
|
5184
|
-
import { DateTime as DateTime8, Effect as
|
|
5288
|
+
import { DateTime as DateTime8, Effect as Effect22, pipe, Ref as Ref10, Stream as Stream8 } from "effect";
|
|
5185
5289
|
|
|
5186
5290
|
// src/layers/plan/task-generation.ts
|
|
5187
5291
|
init_esm_shims();
|
|
5188
5292
|
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
5189
5293
|
import { join as join6 } from "path";
|
|
5190
|
-
import { Effect as
|
|
5294
|
+
import { Effect as Effect20 } from "effect";
|
|
5191
5295
|
var PLANS_DIR4 = ".ferix/plans";
|
|
5192
5296
|
function ensureDir5(dirPath) {
|
|
5193
|
-
return
|
|
5297
|
+
return Effect20.tryPromise({
|
|
5194
5298
|
try: () => mkdir6(dirPath, { recursive: true }),
|
|
5195
5299
|
catch: (error) => new PlanStoreError({
|
|
5196
5300
|
message: `Failed to create directory: ${dirPath}`,
|
|
5197
5301
|
operation: "create",
|
|
5198
5302
|
cause: error
|
|
5199
5303
|
})
|
|
5200
|
-
}).pipe(
|
|
5304
|
+
}).pipe(Effect20.asVoid);
|
|
5201
5305
|
}
|
|
5202
5306
|
function getSessionDir4(sessionId) {
|
|
5203
5307
|
return join6(process.cwd(), PLANS_DIR4, sessionId);
|
|
@@ -5206,12 +5310,12 @@ function getTasksMdPath(sessionId) {
|
|
|
5206
5310
|
return join6(getSessionDir4(sessionId), "tasks.md");
|
|
5207
5311
|
}
|
|
5208
5312
|
function writeTasksMd(sessionId, tasks) {
|
|
5209
|
-
return
|
|
5313
|
+
return Effect20.gen(function* () {
|
|
5210
5314
|
const sessionDir = getSessionDir4(sessionId);
|
|
5211
5315
|
yield* ensureDir5(sessionDir);
|
|
5212
5316
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
5213
5317
|
const content = formatTasksMd(tasks);
|
|
5214
|
-
yield*
|
|
5318
|
+
yield* Effect20.tryPromise({
|
|
5215
5319
|
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
5216
5320
|
catch: (error) => new PlanStoreError({
|
|
5217
5321
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
@@ -5393,7 +5497,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
5393
5497
|
|
|
5394
5498
|
// src/orchestrator/plan-updates.ts
|
|
5395
5499
|
init_esm_shims();
|
|
5396
|
-
import { DateTime as DateTime7, Effect as
|
|
5500
|
+
import { DateTime as DateTime7, Effect as Effect21, Ref as Ref9 } from "effect";
|
|
5397
5501
|
|
|
5398
5502
|
// src/orchestrator/plan-updates/index.ts
|
|
5399
5503
|
init_esm_shims();
|
|
@@ -5698,9 +5802,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5698
5802
|
tasks: plan.tasks
|
|
5699
5803
|
}) : planStore.update(plan.id, plan);
|
|
5700
5804
|
return storeOp.pipe(
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
(error) =>
|
|
5805
|
+
Effect21.map(() => null),
|
|
5806
|
+
Effect21.catchAll(
|
|
5807
|
+
(error) => Effect21.succeed({
|
|
5704
5808
|
_tag: "PlanUpdateFailed",
|
|
5705
5809
|
operation,
|
|
5706
5810
|
error: error.message,
|
|
@@ -5710,7 +5814,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5710
5814
|
);
|
|
5711
5815
|
}
|
|
5712
5816
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
5713
|
-
return
|
|
5817
|
+
return Effect21.gen(function* () {
|
|
5714
5818
|
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
5715
5819
|
const now = yield* DateTime7.now;
|
|
5716
5820
|
const timestamp = DateTime7.formatIso(now);
|
|
@@ -5732,7 +5836,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
5732
5836
|
});
|
|
5733
5837
|
}
|
|
5734
5838
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
5735
|
-
return
|
|
5839
|
+
return Effect21.gen(function* () {
|
|
5736
5840
|
const state = yield* Ref9.get(persistenceStateRef);
|
|
5737
5841
|
if (!(state.dirty && state.pendingOperation)) {
|
|
5738
5842
|
return [];
|
|
@@ -6006,12 +6110,12 @@ Begin.`);
|
|
|
6006
6110
|
|
|
6007
6111
|
// src/orchestrator/discovery.ts
|
|
6008
6112
|
function processTextSignals(signalParser, text, context) {
|
|
6009
|
-
return
|
|
6113
|
+
return Effect22.gen(function* () {
|
|
6010
6114
|
const events = [];
|
|
6011
6115
|
const parsedSignals = [];
|
|
6012
6116
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6013
|
-
|
|
6014
|
-
(error) =>
|
|
6117
|
+
Effect22.tapError(
|
|
6118
|
+
(error) => Effect22.logDebug(
|
|
6015
6119
|
"Signal parsing failed, continuing with empty signals",
|
|
6016
6120
|
{
|
|
6017
6121
|
error: String(error),
|
|
@@ -6019,7 +6123,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6019
6123
|
}
|
|
6020
6124
|
)
|
|
6021
6125
|
),
|
|
6022
|
-
|
|
6126
|
+
Effect22.orElseSucceed(() => [])
|
|
6023
6127
|
);
|
|
6024
6128
|
for (const signal of signals) {
|
|
6025
6129
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6029,7 +6133,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6029
6133
|
});
|
|
6030
6134
|
}
|
|
6031
6135
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
6032
|
-
return
|
|
6136
|
+
return Effect22.gen(function* () {
|
|
6033
6137
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6034
6138
|
const events = [domainEvent];
|
|
6035
6139
|
const allSignals = [];
|
|
@@ -6073,8 +6177,8 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
6073
6177
|
}));
|
|
6074
6178
|
}
|
|
6075
6179
|
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
|
|
6076
|
-
return
|
|
6077
|
-
|
|
6180
|
+
return Stream8.unwrap(
|
|
6181
|
+
Effect22.gen(function* () {
|
|
6078
6182
|
const startTimeUtc = yield* DateTime8.now;
|
|
6079
6183
|
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
6080
6184
|
const persistenceStateRef = yield* Ref10.make({
|
|
@@ -6092,16 +6196,16 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6092
6196
|
};
|
|
6093
6197
|
const prompt = buildDiscoveryPrompt(config);
|
|
6094
6198
|
const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
|
|
6095
|
-
|
|
6199
|
+
Stream8.mapError(
|
|
6096
6200
|
(e) => new OrchestratorError({
|
|
6097
6201
|
message: `LLM execution failed during discovery: ${String(e)}`,
|
|
6098
6202
|
phase: "discovery",
|
|
6099
6203
|
cause: e
|
|
6100
6204
|
})
|
|
6101
6205
|
),
|
|
6102
|
-
|
|
6103
|
-
(llmEvent) =>
|
|
6104
|
-
|
|
6206
|
+
Stream8.flatMap(
|
|
6207
|
+
(llmEvent) => Stream8.unwrap(
|
|
6208
|
+
Effect22.gen(function* () {
|
|
6105
6209
|
const now = yield* DateTime8.now;
|
|
6106
6210
|
const context = {
|
|
6107
6211
|
timestamp: DateTime8.toEpochMillis(now)
|
|
@@ -6122,13 +6226,13 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6122
6226
|
);
|
|
6123
6227
|
events.push(...planEvents);
|
|
6124
6228
|
}
|
|
6125
|
-
return
|
|
6229
|
+
return Stream8.fromIterable(events);
|
|
6126
6230
|
})
|
|
6127
6231
|
)
|
|
6128
6232
|
),
|
|
6129
6233
|
// Convert LLM errors to LoopFailed events
|
|
6130
|
-
|
|
6131
|
-
(error) =>
|
|
6234
|
+
Stream8.catchAll(
|
|
6235
|
+
(error) => Stream8.succeed({
|
|
6132
6236
|
_tag: "LoopFailed",
|
|
6133
6237
|
error: {
|
|
6134
6238
|
message: error.message,
|
|
@@ -6137,8 +6241,8 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6137
6241
|
})
|
|
6138
6242
|
)
|
|
6139
6243
|
);
|
|
6140
|
-
const completionStream =
|
|
6141
|
-
|
|
6244
|
+
const completionStream = Stream8.fromEffect(
|
|
6245
|
+
Effect22.gen(function* () {
|
|
6142
6246
|
const persistEvents = yield* flushPlanPersistence(
|
|
6143
6247
|
planStore,
|
|
6144
6248
|
currentPlanRef,
|
|
@@ -6149,12 +6253,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6149
6253
|
if (plan && plan.tasks.length > 0) {
|
|
6150
6254
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
6151
6255
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
6152
|
-
|
|
6153
|
-
(error) =>
|
|
6256
|
+
Effect22.tapError(
|
|
6257
|
+
(error) => Effect22.logDebug("Failed to write tasks.md, continuing", {
|
|
6154
6258
|
error: String(error)
|
|
6155
6259
|
})
|
|
6156
6260
|
),
|
|
6157
|
-
|
|
6261
|
+
Effect22.orElseSucceed(() => void 0)
|
|
6158
6262
|
);
|
|
6159
6263
|
}
|
|
6160
6264
|
const endTimeUtc = yield* DateTime8.now;
|
|
@@ -6166,12 +6270,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6166
6270
|
};
|
|
6167
6271
|
return [...persistEvents, discoveryCompleted];
|
|
6168
6272
|
})
|
|
6169
|
-
).pipe(
|
|
6273
|
+
).pipe(Stream8.flatMap((events) => Stream8.fromIterable(events)));
|
|
6170
6274
|
return pipe(
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6275
|
+
Stream8.succeed(discoveryStarted),
|
|
6276
|
+
Stream8.concat(Stream8.succeed(analysingToolUse)),
|
|
6277
|
+
Stream8.concat(llmStream),
|
|
6278
|
+
Stream8.concat(completionStream)
|
|
6175
6279
|
);
|
|
6176
6280
|
})
|
|
6177
6281
|
);
|
|
@@ -6179,15 +6283,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6179
6283
|
|
|
6180
6284
|
// src/orchestrator/iteration.ts
|
|
6181
6285
|
init_esm_shims();
|
|
6182
|
-
import { DateTime as DateTime9, Effect as
|
|
6286
|
+
import { DateTime as DateTime9, Effect as Effect23, pipe as pipe2, Ref as Ref11, Stream as Stream9 } from "effect";
|
|
6183
6287
|
function processTextSignals2(signalParser, text, context) {
|
|
6184
|
-
return
|
|
6288
|
+
return Effect23.gen(function* () {
|
|
6185
6289
|
const events = [];
|
|
6186
6290
|
let completed = false;
|
|
6187
6291
|
const parsedSignals = [];
|
|
6188
6292
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6189
|
-
|
|
6190
|
-
(error) =>
|
|
6293
|
+
Effect23.tapError(
|
|
6294
|
+
(error) => Effect23.logDebug(
|
|
6191
6295
|
"Signal parsing failed, continuing with empty signals",
|
|
6192
6296
|
{
|
|
6193
6297
|
error: String(error),
|
|
@@ -6195,7 +6299,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6195
6299
|
}
|
|
6196
6300
|
)
|
|
6197
6301
|
),
|
|
6198
|
-
|
|
6302
|
+
Effect23.orElseSucceed(() => [])
|
|
6199
6303
|
);
|
|
6200
6304
|
for (const signal of signals) {
|
|
6201
6305
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6208,7 +6312,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6208
6312
|
});
|
|
6209
6313
|
}
|
|
6210
6314
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
6211
|
-
return
|
|
6315
|
+
return Effect23.gen(function* () {
|
|
6212
6316
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6213
6317
|
const events = [domainEvent];
|
|
6214
6318
|
let completed = false;
|
|
@@ -6240,8 +6344,8 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
6240
6344
|
});
|
|
6241
6345
|
}
|
|
6242
6346
|
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
6243
|
-
return
|
|
6244
|
-
|
|
6347
|
+
return Stream9.unwrap(
|
|
6348
|
+
Effect23.gen(function* () {
|
|
6245
6349
|
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
6246
6350
|
const persistenceStateRef = yield* Ref11.make({
|
|
6247
6351
|
dirty: false,
|
|
@@ -6255,16 +6359,16 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6255
6359
|
buildPrompt(config, iteration, currentPlan),
|
|
6256
6360
|
worktreePath ? { cwd: worktreePath } : void 0
|
|
6257
6361
|
).pipe(
|
|
6258
|
-
|
|
6362
|
+
Stream9.mapError(
|
|
6259
6363
|
(e) => new OrchestratorError({
|
|
6260
6364
|
message: `LLM execution failed: ${String(e)}`,
|
|
6261
6365
|
phase: "iteration",
|
|
6262
6366
|
cause: e
|
|
6263
6367
|
})
|
|
6264
6368
|
),
|
|
6265
|
-
|
|
6266
|
-
(llmEvent) =>
|
|
6267
|
-
|
|
6369
|
+
Stream9.flatMap(
|
|
6370
|
+
(llmEvent) => Stream9.unwrap(
|
|
6371
|
+
Effect23.gen(function* () {
|
|
6268
6372
|
const now = yield* DateTime9.now;
|
|
6269
6373
|
const context = {
|
|
6270
6374
|
timestamp: DateTime9.toEpochMillis(now)
|
|
@@ -6288,13 +6392,13 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6288
6392
|
if (result.completed) {
|
|
6289
6393
|
yield* Ref11.set(loopCompletedRef, true);
|
|
6290
6394
|
}
|
|
6291
|
-
return
|
|
6395
|
+
return Stream9.fromIterable(events);
|
|
6292
6396
|
})
|
|
6293
6397
|
)
|
|
6294
6398
|
),
|
|
6295
6399
|
// Convert LLM errors to LoopFailed events with iteration context
|
|
6296
|
-
|
|
6297
|
-
(error) =>
|
|
6400
|
+
Stream9.catchAll(
|
|
6401
|
+
(error) => Stream9.succeed({
|
|
6298
6402
|
_tag: "LoopFailed",
|
|
6299
6403
|
error: {
|
|
6300
6404
|
message: error.message,
|
|
@@ -6304,8 +6408,8 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6304
6408
|
})
|
|
6305
6409
|
)
|
|
6306
6410
|
);
|
|
6307
|
-
const completionStream =
|
|
6308
|
-
|
|
6411
|
+
const completionStream = Stream9.fromEffect(
|
|
6412
|
+
Effect23.gen(function* () {
|
|
6309
6413
|
const persistEvents = yield* flushPlanPersistence(
|
|
6310
6414
|
planStore,
|
|
6311
6415
|
currentPlanRef,
|
|
@@ -6317,11 +6421,11 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6317
6421
|
};
|
|
6318
6422
|
return [...persistEvents, iterCompleted];
|
|
6319
6423
|
})
|
|
6320
|
-
).pipe(
|
|
6424
|
+
).pipe(Stream9.flatMap((events) => Stream9.fromIterable(events)));
|
|
6321
6425
|
return pipe2(
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6426
|
+
Stream9.succeed(iterStarted),
|
|
6427
|
+
Stream9.concat(llmStream),
|
|
6428
|
+
Stream9.concat(completionStream)
|
|
6325
6429
|
);
|
|
6326
6430
|
})
|
|
6327
6431
|
);
|
|
@@ -6329,15 +6433,15 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6329
6433
|
|
|
6330
6434
|
// src/orchestrator/loop.ts
|
|
6331
6435
|
function runLoop(config) {
|
|
6332
|
-
return
|
|
6333
|
-
|
|
6436
|
+
return Stream10.unwrap(
|
|
6437
|
+
Effect24.gen(function* () {
|
|
6334
6438
|
const llm = yield* LLM;
|
|
6335
6439
|
const signalParser = yield* SignalParser;
|
|
6336
6440
|
const sessionStore = yield* SessionStore;
|
|
6337
6441
|
const planStore = yield* PlanStore;
|
|
6338
6442
|
const git = yield* Git;
|
|
6339
6443
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
6340
|
-
|
|
6444
|
+
Effect24.mapError(
|
|
6341
6445
|
(e) => new OrchestratorError({
|
|
6342
6446
|
message: `Failed to create session: ${e.message}`,
|
|
6343
6447
|
phase: "setup",
|
|
@@ -6346,7 +6450,7 @@ function runLoop(config) {
|
|
|
6346
6450
|
)
|
|
6347
6451
|
);
|
|
6348
6452
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
6349
|
-
|
|
6453
|
+
Effect24.mapError(
|
|
6350
6454
|
(e) => new OrchestratorError({
|
|
6351
6455
|
message: `Failed to create worktree: ${e.message}`,
|
|
6352
6456
|
phase: "setup",
|
|
@@ -6360,12 +6464,12 @@ function runLoop(config) {
|
|
|
6360
6464
|
worktreePath,
|
|
6361
6465
|
branchName
|
|
6362
6466
|
}).pipe(
|
|
6363
|
-
|
|
6364
|
-
(error) =>
|
|
6467
|
+
Effect24.tapError(
|
|
6468
|
+
(error) => Effect24.logDebug("Failed to update session with worktree info", {
|
|
6365
6469
|
error: String(error)
|
|
6366
6470
|
})
|
|
6367
6471
|
),
|
|
6368
|
-
|
|
6472
|
+
Effect24.orElseSucceed(() => void 0)
|
|
6369
6473
|
);
|
|
6370
6474
|
const startTimeUtc = yield* DateTime10.now;
|
|
6371
6475
|
const startTime = DateTime10.toEpochMillis(startTimeUtc);
|
|
@@ -6393,9 +6497,9 @@ function runLoop(config) {
|
|
|
6393
6497
|
session.id,
|
|
6394
6498
|
worktreePath
|
|
6395
6499
|
);
|
|
6396
|
-
const iterationsStream =
|
|
6500
|
+
const iterationsStream = Stream10.unfoldEffect(
|
|
6397
6501
|
1,
|
|
6398
|
-
(iteration) =>
|
|
6502
|
+
(iteration) => Effect24.gen(function* () {
|
|
6399
6503
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6400
6504
|
if (completed || iteration > maxIterations) {
|
|
6401
6505
|
return Option.none();
|
|
@@ -6403,7 +6507,7 @@ function runLoop(config) {
|
|
|
6403
6507
|
return Option.some([iteration, iteration + 1]);
|
|
6404
6508
|
})
|
|
6405
6509
|
).pipe(
|
|
6406
|
-
|
|
6510
|
+
Stream10.flatMap(
|
|
6407
6511
|
(iteration) => createIterationStream(
|
|
6408
6512
|
llm,
|
|
6409
6513
|
signalParser,
|
|
@@ -6427,17 +6531,17 @@ function runLoop(config) {
|
|
|
6427
6531
|
worktreePath
|
|
6428
6532
|
);
|
|
6429
6533
|
return pipe3(
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6534
|
+
Stream10.succeed(loopStarted),
|
|
6535
|
+
Stream10.concat(Stream10.succeed(worktreeCreated)),
|
|
6536
|
+
Stream10.concat(discoveryStream),
|
|
6537
|
+
Stream10.concat(iterationsStream),
|
|
6538
|
+
Stream10.concat(completionStream)
|
|
6435
6539
|
);
|
|
6436
6540
|
}).pipe(
|
|
6437
6541
|
// Also catch setup errors (e.g., session creation failure)
|
|
6438
|
-
|
|
6439
|
-
(error) =>
|
|
6440
|
-
|
|
6542
|
+
Effect24.catchAll(
|
|
6543
|
+
(error) => Effect24.succeed(
|
|
6544
|
+
Stream10.succeed({
|
|
6441
6545
|
_tag: "LoopFailed",
|
|
6442
6546
|
error: {
|
|
6443
6547
|
message: error.message,
|
|
@@ -6450,19 +6554,19 @@ function runLoop(config) {
|
|
|
6450
6554
|
);
|
|
6451
6555
|
}
|
|
6452
6556
|
function createCompletionStream(sessionStore, git, session, config, startTime, loopCompletedRef, _worktreePath) {
|
|
6453
|
-
return
|
|
6454
|
-
|
|
6557
|
+
return Stream10.unwrap(
|
|
6558
|
+
Effect24.gen(function* () {
|
|
6455
6559
|
const endTimeUtc = yield* DateTime10.now;
|
|
6456
6560
|
const durationMs = DateTime10.toEpochMillis(endTimeUtc) - startTime;
|
|
6457
6561
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6458
6562
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
6459
|
-
|
|
6460
|
-
(error) =>
|
|
6563
|
+
Effect24.tapError(
|
|
6564
|
+
(error) => Effect24.logDebug("Final commit failed, continuing", {
|
|
6461
6565
|
sessionId: session.id,
|
|
6462
6566
|
error: String(error)
|
|
6463
6567
|
})
|
|
6464
6568
|
),
|
|
6465
|
-
|
|
6569
|
+
Effect24.orElseSucceed(() => void 0)
|
|
6466
6570
|
);
|
|
6467
6571
|
const summary = {
|
|
6468
6572
|
iterations: config.maxIterations,
|
|
@@ -6475,16 +6579,16 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6475
6579
|
...session,
|
|
6476
6580
|
status: completed ? "completed" : "paused"
|
|
6477
6581
|
}).pipe(
|
|
6478
|
-
|
|
6479
|
-
(error) =>
|
|
6582
|
+
Effect24.tapError(
|
|
6583
|
+
(error) => Effect24.logDebug("Session update failed, continuing", {
|
|
6480
6584
|
sessionId: session.id,
|
|
6481
6585
|
error: String(error)
|
|
6482
6586
|
})
|
|
6483
6587
|
),
|
|
6484
|
-
|
|
6588
|
+
Effect24.orElseSucceed(() => void 0)
|
|
6485
6589
|
);
|
|
6486
6590
|
const loopCompleted = { _tag: "LoopCompleted", summary };
|
|
6487
|
-
return
|
|
6591
|
+
return Stream10.succeed(loopCompleted);
|
|
6488
6592
|
})
|
|
6489
6593
|
);
|
|
6490
6594
|
}
|
|
@@ -6493,12 +6597,11 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6493
6597
|
function run(options) {
|
|
6494
6598
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
6495
6599
|
const events = runLoop(config);
|
|
6496
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6497
|
-
const
|
|
6498
|
-
|
|
6499
|
-
);
|
|
6600
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream11.tap((event) => Effect25.sync(() => onEvent(event)))) : events;
|
|
6601
|
+
const layers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
6602
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream11.provideLayer(layers));
|
|
6500
6603
|
if (consumerType === "none") {
|
|
6501
|
-
return eventsWithLayers.pipe(
|
|
6604
|
+
return eventsWithLayers.pipe(Stream11.runDrain);
|
|
6502
6605
|
}
|
|
6503
6606
|
const consumer = consumerType === "tui" ? createTUIConsumer() : createHeadlessConsumer();
|
|
6504
6607
|
return consumer.consume(eventsWithLayers);
|
|
@@ -6506,19 +6609,19 @@ function run(options) {
|
|
|
6506
6609
|
function runTest(options, mockEvents) {
|
|
6507
6610
|
const { config, onEvent } = options;
|
|
6508
6611
|
const events = runLoop(config);
|
|
6509
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6612
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream11.tap((event) => Effect25.sync(() => onEvent(event)))) : events;
|
|
6510
6613
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6511
|
-
const eventsWithLayers = eventsWithCallback.pipe(
|
|
6512
|
-
return eventsWithLayers.pipe(
|
|
6614
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream11.provideLayer(layers));
|
|
6615
|
+
return eventsWithLayers.pipe(Stream11.runDrain);
|
|
6513
6616
|
}
|
|
6514
6617
|
function collectEvents(config, mockEvents) {
|
|
6515
6618
|
const events = runLoop(config);
|
|
6516
6619
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6517
|
-
return events.pipe(
|
|
6620
|
+
return events.pipe(Stream11.provideLayer(layers), Stream11.runCollect).pipe(Effect25.map((chunk) => Array.from(chunk)));
|
|
6518
6621
|
}
|
|
6519
6622
|
function main(config) {
|
|
6520
6623
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
6521
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
6624
|
+
return run({ config, consumer: consumerType }).pipe(Effect25.runPromise);
|
|
6522
6625
|
}
|
|
6523
6626
|
|
|
6524
6627
|
// src/services/index.ts
|
|
@@ -6526,14 +6629,15 @@ init_esm_shims();
|
|
|
6526
6629
|
|
|
6527
6630
|
// src/index.ts
|
|
6528
6631
|
var program = new Command();
|
|
6529
|
-
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) => {
|
|
6632
|
+
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").option("--provider <name>", "LLM provider to use (claude, cursor)", "claude").action(async (task, options) => {
|
|
6530
6633
|
const config = {
|
|
6531
6634
|
task,
|
|
6532
6635
|
maxIterations: Number.parseInt(options.iterations, 10),
|
|
6533
6636
|
verifyCommands: options.verify || [],
|
|
6534
6637
|
branch: options.branch,
|
|
6535
6638
|
push: options.push,
|
|
6536
|
-
pr: options.pr
|
|
6639
|
+
pr: options.pr,
|
|
6640
|
+
provider: options.provider
|
|
6537
6641
|
};
|
|
6538
6642
|
try {
|
|
6539
6643
|
await main(config);
|
|
@@ -6563,6 +6667,7 @@ export {
|
|
|
6563
6667
|
CriterionPassedSignalSchema,
|
|
6564
6668
|
CriterionSchema,
|
|
6565
6669
|
CriterionStatusSchema,
|
|
6670
|
+
CursorCLI,
|
|
6566
6671
|
DiscoveryCompletedEventSchema,
|
|
6567
6672
|
DiscoveryStartedEventSchema,
|
|
6568
6673
|
DomainEventSchema,
|
|
@@ -6618,6 +6723,7 @@ export {
|
|
|
6618
6723
|
Mock,
|
|
6619
6724
|
Mock as MockLLM,
|
|
6620
6725
|
OrchestratorError,
|
|
6726
|
+
PROVIDER_CONFIGS,
|
|
6621
6727
|
ParseError,
|
|
6622
6728
|
PhaseBasicInfoSchema,
|
|
6623
6729
|
PhaseCompletedEventSchema,
|
|
@@ -6650,6 +6756,7 @@ export {
|
|
|
6650
6756
|
ProgressStoreError,
|
|
6651
6757
|
ProgressUpdatedEventSchema,
|
|
6652
6758
|
PromptConfigSchema,
|
|
6759
|
+
ProviderNameSchema,
|
|
6653
6760
|
ReviewCompleteDataSchema,
|
|
6654
6761
|
ReviewCompleteEventSchema,
|
|
6655
6762
|
ReviewCompleteSignalSchema,
|
|
@@ -6688,6 +6795,8 @@ export {
|
|
|
6688
6795
|
buildPrompt,
|
|
6689
6796
|
collectEvents,
|
|
6690
6797
|
createHeadlessConsumer,
|
|
6798
|
+
createProductionLayers,
|
|
6799
|
+
createProviderLayer,
|
|
6691
6800
|
createTUIConsumer,
|
|
6692
6801
|
createTestLayers,
|
|
6693
6802
|
decodeGuardrail,
|