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