ferix-code 0.0.3-beta.1 → 0.0.3
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.js +677 -234
- package/package.json +1 -1
- package/dist/chunk-ENDDBBCW.js +0 -74
- package/dist/chunk-ILHJ3OBF.js +0 -37
- package/dist/chunk-XIEB473E.js +0 -358
- package/dist/prompt-YXBU2FXF.js +0 -14
- package/dist/task-generation-YN2MKKY4.js +0 -50
package/dist/index.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// ../../node_modules/.bun/tsup@8.5.1+7ba260eccd18f7a2/node_modules/tsup/assets/esm_shims.js
|
|
24
|
+
import path from "path";
|
|
25
|
+
import { fileURLToPath } from "url";
|
|
26
|
+
var init_esm_shims = __esm({
|
|
27
|
+
"../../node_modules/.bun/tsup@8.5.1+7ba260eccd18f7a2/node_modules/tsup/assets/esm_shims.js"() {
|
|
28
|
+
"use strict";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
24
31
|
|
|
25
32
|
// src/commands/code/consumers/tui/tags/registry.ts
|
|
26
33
|
var registry_exports = {};
|
|
@@ -63,7 +70,7 @@ import { Command } from "commander";
|
|
|
63
70
|
// package.json
|
|
64
71
|
var package_default = {
|
|
65
72
|
name: "ferix-code",
|
|
66
|
-
version: "0.0.3
|
|
73
|
+
version: "0.0.3",
|
|
67
74
|
description: "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
68
75
|
type: "module",
|
|
69
76
|
bin: {
|
|
@@ -123,7 +130,7 @@ init_esm_shims();
|
|
|
123
130
|
|
|
124
131
|
// src/commands/code/action.ts
|
|
125
132
|
init_esm_shims();
|
|
126
|
-
import { Effect as
|
|
133
|
+
import { Effect as Effect20, Layer as Layer12, Stream as Stream9 } from "effect";
|
|
127
134
|
import { humanId as humanId2 } from "human-id";
|
|
128
135
|
|
|
129
136
|
// src/commands/code/consumers/index.ts
|
|
@@ -2613,6 +2620,38 @@ import { dirname, join } from "path";
|
|
|
2613
2620
|
import { promisify } from "util";
|
|
2614
2621
|
import { Effect as Effect4, Layer } from "effect";
|
|
2615
2622
|
|
|
2623
|
+
// src/commands/code/domain/errors.ts
|
|
2624
|
+
init_esm_shims();
|
|
2625
|
+
import { Data } from "effect";
|
|
2626
|
+
var LLMError = class extends Data.TaggedError("LLMError") {
|
|
2627
|
+
};
|
|
2628
|
+
var ParseError = class extends Data.TaggedError("ParseError") {
|
|
2629
|
+
};
|
|
2630
|
+
var PlanStoreError = class extends Data.TaggedError("PlanStoreError") {
|
|
2631
|
+
};
|
|
2632
|
+
var SessionStoreError = class extends Data.TaggedError("SessionStoreError") {
|
|
2633
|
+
};
|
|
2634
|
+
var ProgressStoreError = class extends Data.TaggedError("ProgressStoreError") {
|
|
2635
|
+
};
|
|
2636
|
+
var GuardrailsStoreError = class extends Data.TaggedError(
|
|
2637
|
+
"GuardrailsStoreError"
|
|
2638
|
+
) {
|
|
2639
|
+
};
|
|
2640
|
+
var OrchestratorError = class extends Data.TaggedError("OrchestratorError") {
|
|
2641
|
+
};
|
|
2642
|
+
var GitError = class extends Data.TaggedError("GitError") {
|
|
2643
|
+
};
|
|
2644
|
+
var TokenBudgetError = class extends Data.TaggedError("TokenBudgetError") {
|
|
2645
|
+
};
|
|
2646
|
+
var RetryExhaustedError = class extends Data.TaggedError(
|
|
2647
|
+
"RetryExhaustedError"
|
|
2648
|
+
) {
|
|
2649
|
+
};
|
|
2650
|
+
var StateStoreError = class extends Data.TaggedError("StateStoreError") {
|
|
2651
|
+
};
|
|
2652
|
+
var PromptStoreError = class extends Data.TaggedError("PromptStoreError") {
|
|
2653
|
+
};
|
|
2654
|
+
|
|
2616
2655
|
// src/commands/code/services/git.ts
|
|
2617
2656
|
init_esm_shims();
|
|
2618
2657
|
import { Context } from "effect";
|
|
@@ -3753,12 +3792,37 @@ var SessionStateSchema = S15.Struct({
|
|
|
3753
3792
|
});
|
|
3754
3793
|
var decodeSessionState = S15.decodeUnknown(SessionStateSchema);
|
|
3755
3794
|
|
|
3756
|
-
// src/commands/code/domain/schemas/
|
|
3795
|
+
// src/commands/code/domain/schemas/task-generation.ts
|
|
3757
3796
|
init_esm_shims();
|
|
3758
3797
|
import { Schema as S16 } from "effect";
|
|
3759
|
-
var
|
|
3760
|
-
|
|
3761
|
-
|
|
3798
|
+
var GeneratedTaskStatusSchema = S16.Literal(
|
|
3799
|
+
"pending",
|
|
3800
|
+
"in_progress",
|
|
3801
|
+
"done",
|
|
3802
|
+
"failed"
|
|
3803
|
+
);
|
|
3804
|
+
var GeneratedTaskSchema = S16.Struct({
|
|
3805
|
+
id: S16.String,
|
|
3806
|
+
title: S16.String,
|
|
3807
|
+
description: S16.String,
|
|
3808
|
+
status: GeneratedTaskStatusSchema,
|
|
3809
|
+
steps: S16.Array(S16.String)
|
|
3810
|
+
});
|
|
3811
|
+
var TasksFileSchema = S16.Struct({
|
|
3812
|
+
sessionId: S16.String,
|
|
3813
|
+
originalTask: S16.String,
|
|
3814
|
+
tasks: S16.Array(GeneratedTaskSchema)
|
|
3815
|
+
});
|
|
3816
|
+
function formatTasksJson(tasksFile) {
|
|
3817
|
+
return JSON.stringify(tasksFile, null, 2);
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
// src/commands/code/domain/schemas/tui.ts
|
|
3821
|
+
init_esm_shims();
|
|
3822
|
+
import { Schema as S17 } from "effect";
|
|
3823
|
+
var ViewModeSchema = S17.Literal("logs", "tasks", "detail");
|
|
3824
|
+
var LoopStatusSchema = S17.Literal("idle", "running", "complete", "error");
|
|
3825
|
+
var ExecutionModeSchema = S17.Literal(
|
|
3762
3826
|
"idle",
|
|
3763
3827
|
"discovery",
|
|
3764
3828
|
"breakdown",
|
|
@@ -3768,73 +3832,73 @@ var ExecutionModeSchema = S16.Literal(
|
|
|
3768
3832
|
"verifying",
|
|
3769
3833
|
"reviewing"
|
|
3770
3834
|
);
|
|
3771
|
-
var TUIPhaseStatusSchema =
|
|
3835
|
+
var TUIPhaseStatusSchema = S17.Literal(
|
|
3772
3836
|
"pending",
|
|
3773
3837
|
"in_progress",
|
|
3774
3838
|
"done",
|
|
3775
3839
|
"failed"
|
|
3776
3840
|
);
|
|
3777
|
-
var TUICriterionStatusSchema =
|
|
3778
|
-
var TUITaskStatusSchema =
|
|
3841
|
+
var TUICriterionStatusSchema = S17.Literal("pending", "passed", "failed");
|
|
3842
|
+
var TUITaskStatusSchema = S17.Literal(
|
|
3779
3843
|
"pending",
|
|
3780
3844
|
"in_progress",
|
|
3781
3845
|
"done",
|
|
3782
3846
|
"failed"
|
|
3783
3847
|
);
|
|
3784
|
-
var TUIPhaseSchema =
|
|
3785
|
-
id:
|
|
3786
|
-
description:
|
|
3848
|
+
var TUIPhaseSchema = S17.Struct({
|
|
3849
|
+
id: S17.String,
|
|
3850
|
+
description: S17.String,
|
|
3787
3851
|
status: TUIPhaseStatusSchema,
|
|
3788
|
-
startedAt:
|
|
3789
|
-
completedAt:
|
|
3852
|
+
startedAt: S17.optional(S17.Number),
|
|
3853
|
+
completedAt: S17.optional(S17.Number)
|
|
3790
3854
|
});
|
|
3791
|
-
var TUICriterionSchema =
|
|
3792
|
-
id:
|
|
3793
|
-
description:
|
|
3855
|
+
var TUICriterionSchema = S17.Struct({
|
|
3856
|
+
id: S17.String,
|
|
3857
|
+
description: S17.String,
|
|
3794
3858
|
status: TUICriterionStatusSchema,
|
|
3795
|
-
failureReason:
|
|
3859
|
+
failureReason: S17.optional(S17.String)
|
|
3796
3860
|
});
|
|
3797
|
-
var TUITaskSchema =
|
|
3798
|
-
id:
|
|
3799
|
-
title:
|
|
3861
|
+
var TUITaskSchema = S17.Struct({
|
|
3862
|
+
id: S17.String,
|
|
3863
|
+
title: S17.String,
|
|
3800
3864
|
status: TUITaskStatusSchema,
|
|
3801
|
-
phases:
|
|
3802
|
-
criteria:
|
|
3803
|
-
startedAt:
|
|
3804
|
-
completedAt:
|
|
3865
|
+
phases: S17.Array(TUIPhaseSchema),
|
|
3866
|
+
criteria: S17.Array(TUICriterionSchema),
|
|
3867
|
+
startedAt: S17.optional(S17.Number),
|
|
3868
|
+
completedAt: S17.optional(S17.Number)
|
|
3805
3869
|
});
|
|
3806
|
-
var TUIStateSchema =
|
|
3870
|
+
var TUIStateSchema = S17.Struct({
|
|
3807
3871
|
// Loop info
|
|
3808
|
-
task:
|
|
3809
|
-
iteration:
|
|
3810
|
-
maxIterations:
|
|
3872
|
+
task: S17.String,
|
|
3873
|
+
iteration: S17.Number,
|
|
3874
|
+
maxIterations: S17.Number,
|
|
3811
3875
|
status: LoopStatusSchema,
|
|
3812
|
-
startTime:
|
|
3876
|
+
startTime: S17.Number,
|
|
3813
3877
|
// Discovery phase
|
|
3814
|
-
discoveryInProgress:
|
|
3815
|
-
discoveryCompleted:
|
|
3878
|
+
discoveryInProgress: S17.Boolean,
|
|
3879
|
+
discoveryCompleted: S17.Boolean,
|
|
3816
3880
|
// Current activity
|
|
3817
3881
|
executionMode: ExecutionModeSchema,
|
|
3818
|
-
currentTool:
|
|
3819
|
-
currentTaskId:
|
|
3882
|
+
currentTool: S17.optional(S17.String),
|
|
3883
|
+
currentTaskId: S17.optional(S17.String),
|
|
3820
3884
|
// Output
|
|
3821
|
-
outputLines:
|
|
3822
|
-
partialLine:
|
|
3885
|
+
outputLines: S17.Array(S17.String),
|
|
3886
|
+
partialLine: S17.String,
|
|
3823
3887
|
// Tasks
|
|
3824
|
-
tasks:
|
|
3888
|
+
tasks: S17.Array(TUITaskSchema),
|
|
3825
3889
|
// Navigation
|
|
3826
3890
|
viewMode: ViewModeSchema,
|
|
3827
|
-
selectedTaskIndex:
|
|
3828
|
-
scrollOffset:
|
|
3829
|
-
userScrolled:
|
|
3891
|
+
selectedTaskIndex: S17.Number,
|
|
3892
|
+
scrollOffset: S17.Number,
|
|
3893
|
+
userScrolled: S17.Boolean,
|
|
3830
3894
|
// Git
|
|
3831
|
-
gitBranch:
|
|
3832
|
-
gitPushed:
|
|
3833
|
-
prUrl:
|
|
3895
|
+
gitBranch: S17.optional(S17.String),
|
|
3896
|
+
gitPushed: S17.Boolean,
|
|
3897
|
+
prUrl: S17.optional(S17.String),
|
|
3834
3898
|
// YOLO mode (skip permission prompts)
|
|
3835
|
-
yolo:
|
|
3899
|
+
yolo: S17.Boolean,
|
|
3836
3900
|
// Debug mode (logging enabled)
|
|
3837
|
-
debug:
|
|
3901
|
+
debug: S17.Boolean
|
|
3838
3902
|
});
|
|
3839
3903
|
|
|
3840
3904
|
// src/commands/code/services/guardrails-store.ts
|
|
@@ -3983,23 +4047,23 @@ import { Effect as Effect8 } from "effect";
|
|
|
3983
4047
|
|
|
3984
4048
|
// src/commands/code/layers/llm/types.ts
|
|
3985
4049
|
init_esm_shims();
|
|
3986
|
-
import { Schema as
|
|
3987
|
-
var PermissionModeSchema =
|
|
3988
|
-
var ProviderConfigSchema =
|
|
4050
|
+
import { Schema as S18 } from "effect";
|
|
4051
|
+
var PermissionModeSchema = S18.Literal("acceptEdits", "yolo", "prompt");
|
|
4052
|
+
var ProviderConfigSchema = S18.Struct({
|
|
3989
4053
|
/** Provider name */
|
|
3990
4054
|
name: ProviderNameSchema,
|
|
3991
4055
|
/** CLI command to execute */
|
|
3992
|
-
cliCommand:
|
|
4056
|
+
cliCommand: S18.String,
|
|
3993
4057
|
/** Default arguments for the CLI */
|
|
3994
|
-
args:
|
|
4058
|
+
args: S18.Array(S18.String),
|
|
3995
4059
|
/** Environment variables to pass */
|
|
3996
|
-
env:
|
|
4060
|
+
env: S18.optional(S18.Record({ key: S18.String, value: S18.String })),
|
|
3997
4061
|
/** Permission mode for the CLI */
|
|
3998
|
-
permissions:
|
|
4062
|
+
permissions: S18.optional(PermissionModeSchema),
|
|
3999
4063
|
/** URL for installation instructions */
|
|
4000
|
-
installUrl:
|
|
4064
|
+
installUrl: S18.String,
|
|
4001
4065
|
/** Arguments to pass when YOLO mode is enabled */
|
|
4002
|
-
yoloArgs:
|
|
4066
|
+
yoloArgs: S18.optional(S18.Array(S18.String))
|
|
4003
4067
|
});
|
|
4004
4068
|
var RAW_PROVIDER_CONFIGS = {
|
|
4005
4069
|
claude: {
|
|
@@ -4033,12 +4097,12 @@ var RAW_PROVIDER_CONFIGS = {
|
|
|
4033
4097
|
yoloArgs: ["--yolo"]
|
|
4034
4098
|
}
|
|
4035
4099
|
};
|
|
4036
|
-
var ProviderConfigsSchema =
|
|
4100
|
+
var ProviderConfigsSchema = S18.Record({
|
|
4037
4101
|
key: ProviderNameSchema,
|
|
4038
4102
|
value: ProviderConfigSchema
|
|
4039
4103
|
});
|
|
4040
4104
|
var validateProviderConfigs = () => {
|
|
4041
|
-
const decoded =
|
|
4105
|
+
const decoded = S18.decodeUnknownSync(ProviderConfigsSchema)(
|
|
4042
4106
|
RAW_PROVIDER_CONFIGS
|
|
4043
4107
|
);
|
|
4044
4108
|
return decoded;
|
|
@@ -4759,7 +4823,7 @@ var FileSystemProgress = {
|
|
|
4759
4823
|
init_esm_shims();
|
|
4760
4824
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
4761
4825
|
import { dirname as dirname2, join as join5 } from "path";
|
|
4762
|
-
import { fileURLToPath } from "url";
|
|
4826
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4763
4827
|
import { Effect as Effect11, Layer as Layer6 } from "effect";
|
|
4764
4828
|
|
|
4765
4829
|
// src/commands/code/services/prompt-store.ts
|
|
@@ -4770,7 +4834,7 @@ var PromptStore = class extends Context6.Tag("@ferix/PromptStore")() {
|
|
|
4770
4834
|
|
|
4771
4835
|
// src/commands/code/layers/prompt/file-system.ts
|
|
4772
4836
|
var PLANS_DIR4 = ".ferix/plans";
|
|
4773
|
-
var __dirname2 = dirname2(
|
|
4837
|
+
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
4774
4838
|
var BUNDLED_TEMPLATE_PATH = join5(__dirname2, "../../templates/PROMPT.md");
|
|
4775
4839
|
function ensureDir4(dirPath) {
|
|
4776
4840
|
return Effect11.tryPromise({
|
|
@@ -5211,7 +5275,7 @@ signalSpecRegistry.register(learningSpec);
|
|
|
5211
5275
|
|
|
5212
5276
|
// src/commands/code/layers/signal/specs/loop-complete.ts
|
|
5213
5277
|
init_esm_shims();
|
|
5214
|
-
import { Schema as
|
|
5278
|
+
import { Schema as S19 } from "effect";
|
|
5215
5279
|
var LOOP_COMPLETE = /<ferix:complete>/;
|
|
5216
5280
|
var loopCompleteSpec = {
|
|
5217
5281
|
tag: "LoopComplete",
|
|
@@ -5220,7 +5284,7 @@ var loopCompleteSpec = {
|
|
|
5220
5284
|
parse: (text) => {
|
|
5221
5285
|
if (LOOP_COMPLETE.test(text)) {
|
|
5222
5286
|
const raw = { _tag: "LoopComplete" };
|
|
5223
|
-
const result =
|
|
5287
|
+
const result = S19.decodeUnknownEither(LoopCompleteSignalSchema)(raw);
|
|
5224
5288
|
if (result._tag === "Right") {
|
|
5225
5289
|
return [result.right];
|
|
5226
5290
|
}
|
|
@@ -5333,7 +5397,7 @@ signalSpecRegistry.register(phaseFailedSpec);
|
|
|
5333
5397
|
|
|
5334
5398
|
// src/commands/code/layers/signal/specs/review.ts
|
|
5335
5399
|
init_esm_shims();
|
|
5336
|
-
import { Schema as
|
|
5400
|
+
import { Schema as S20 } from "effect";
|
|
5337
5401
|
var REVIEW_COMPLETE = /<ferix:review-complete\/>/;
|
|
5338
5402
|
var REVIEW_CHANGES = /<ferix:review-changes-made\/>/;
|
|
5339
5403
|
var reviewCompleteSpec = {
|
|
@@ -5346,7 +5410,7 @@ var reviewCompleteSpec = {
|
|
|
5346
5410
|
}
|
|
5347
5411
|
const changesMade = REVIEW_CHANGES.test(text);
|
|
5348
5412
|
const raw = { _tag: "ReviewComplete", changesMade };
|
|
5349
|
-
const result =
|
|
5413
|
+
const result = S20.decodeUnknownEither(ReviewCompleteSignalSchema)(raw);
|
|
5350
5414
|
if (result._tag === "Right") {
|
|
5351
5415
|
return [result.right];
|
|
5352
5416
|
}
|
|
@@ -5389,7 +5453,7 @@ signalSpecRegistry.register(sessionNameSpec);
|
|
|
5389
5453
|
|
|
5390
5454
|
// src/commands/code/layers/signal/specs/task-complete.ts
|
|
5391
5455
|
init_esm_shims();
|
|
5392
|
-
import { Schema as
|
|
5456
|
+
import { Schema as S21 } from "effect";
|
|
5393
5457
|
var TASK_COMPLETE = /<ferix:task-complete id="(\d+)">([\s\S]*?)<\/ferix:task-complete>/;
|
|
5394
5458
|
var SUMMARY = /<summary>([\s\S]*?)<\/summary>/;
|
|
5395
5459
|
var FILES_MODIFIED = /<files-modified>([\s\S]*?)<\/files-modified>/;
|
|
@@ -5420,7 +5484,7 @@ var taskCompleteSpec = {
|
|
|
5420
5484
|
filesModified: parseFileList(filesModifiedMatch?.[1]),
|
|
5421
5485
|
filesCreated: parseFileList(filesCreatedMatch?.[1])
|
|
5422
5486
|
};
|
|
5423
|
-
const result =
|
|
5487
|
+
const result = S21.decodeUnknownEither(TaskCompleteSignalSchema)(raw);
|
|
5424
5488
|
if (result._tag === "Right") {
|
|
5425
5489
|
return [result.right];
|
|
5426
5490
|
}
|
|
@@ -5694,11 +5758,50 @@ init_esm_shims();
|
|
|
5694
5758
|
|
|
5695
5759
|
// src/commands/code/orchestrator/loop.ts
|
|
5696
5760
|
init_esm_shims();
|
|
5697
|
-
import { DateTime as DateTime7, Effect as
|
|
5761
|
+
import { DateTime as DateTime7, Effect as Effect19, Option, pipe as pipe3, Ref as Ref7, Stream as Stream8 } from "effect";
|
|
5698
5762
|
|
|
5699
5763
|
// src/commands/code/orchestrator/discovery.ts
|
|
5700
5764
|
init_esm_shims();
|
|
5701
|
-
import { DateTime as DateTime5, Effect as
|
|
5765
|
+
import { DateTime as DateTime5, Effect as Effect17, pipe, Ref as Ref5, Stream as Stream6 } from "effect";
|
|
5766
|
+
|
|
5767
|
+
// src/commands/code/layers/plan/task-generation.ts
|
|
5768
|
+
init_esm_shims();
|
|
5769
|
+
import { mkdir as mkdir8, writeFile as writeFile7 } from "fs/promises";
|
|
5770
|
+
import { join as join9 } from "path";
|
|
5771
|
+
import { Effect as Effect15 } from "effect";
|
|
5772
|
+
var PLANS_DIR6 = ".ferix/plans";
|
|
5773
|
+
function ensureDir7(dirPath) {
|
|
5774
|
+
return Effect15.tryPromise({
|
|
5775
|
+
try: () => mkdir8(dirPath, { recursive: true }),
|
|
5776
|
+
catch: (error) => new PlanStoreError({
|
|
5777
|
+
message: `Failed to create directory: ${dirPath}`,
|
|
5778
|
+
operation: "create",
|
|
5779
|
+
cause: error
|
|
5780
|
+
})
|
|
5781
|
+
}).pipe(Effect15.asVoid);
|
|
5782
|
+
}
|
|
5783
|
+
function getSessionDir6(sessionId) {
|
|
5784
|
+
return join9(process.cwd(), PLANS_DIR6, sessionId);
|
|
5785
|
+
}
|
|
5786
|
+
function getTasksJsonPath(sessionId) {
|
|
5787
|
+
return join9(getSessionDir6(sessionId), "tasks.json");
|
|
5788
|
+
}
|
|
5789
|
+
function writeTasksJson(sessionId, tasksFile) {
|
|
5790
|
+
return Effect15.gen(function* () {
|
|
5791
|
+
const sessionDir = getSessionDir6(sessionId);
|
|
5792
|
+
yield* ensureDir7(sessionDir);
|
|
5793
|
+
const tasksJsonPath = getTasksJsonPath(sessionId);
|
|
5794
|
+
const content = formatTasksJson(tasksFile);
|
|
5795
|
+
yield* Effect15.tryPromise({
|
|
5796
|
+
try: () => writeFile7(tasksJsonPath, content, "utf-8"),
|
|
5797
|
+
catch: (error) => new PlanStoreError({
|
|
5798
|
+
message: `Failed to write tasks.json: ${tasksJsonPath}`,
|
|
5799
|
+
operation: "create",
|
|
5800
|
+
cause: error
|
|
5801
|
+
})
|
|
5802
|
+
});
|
|
5803
|
+
});
|
|
5804
|
+
}
|
|
5702
5805
|
|
|
5703
5806
|
// src/commands/code/orchestrator/event-mapping.ts
|
|
5704
5807
|
init_esm_shims();
|
|
@@ -5881,7 +5984,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
5881
5984
|
|
|
5882
5985
|
// src/commands/code/orchestrator/plan-updates.ts
|
|
5883
5986
|
init_esm_shims();
|
|
5884
|
-
import { DateTime as DateTime4, Effect as
|
|
5987
|
+
import { DateTime as DateTime4, Effect as Effect16, Ref as Ref4 } from "effect";
|
|
5885
5988
|
|
|
5886
5989
|
// src/commands/code/orchestrator/plan-updates/index.ts
|
|
5887
5990
|
init_esm_shims();
|
|
@@ -6203,9 +6306,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
6203
6306
|
tasks: plan.tasks
|
|
6204
6307
|
}) : planStore.update(plan.id, plan);
|
|
6205
6308
|
return storeOp.pipe(
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
(error) =>
|
|
6309
|
+
Effect16.map(() => null),
|
|
6310
|
+
Effect16.catchAll(
|
|
6311
|
+
(error) => Effect16.succeed({
|
|
6209
6312
|
_tag: "PlanUpdateFailed",
|
|
6210
6313
|
operation,
|
|
6211
6314
|
error: error.message,
|
|
@@ -6215,7 +6318,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
6215
6318
|
);
|
|
6216
6319
|
}
|
|
6217
6320
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
6218
|
-
return
|
|
6321
|
+
return Effect16.gen(function* () {
|
|
6219
6322
|
const currentPlan = yield* Ref4.get(currentPlanRef);
|
|
6220
6323
|
const now = yield* DateTime4.now;
|
|
6221
6324
|
const timestamp = DateTime4.formatIso(now);
|
|
@@ -6237,7 +6340,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
6237
6340
|
});
|
|
6238
6341
|
}
|
|
6239
6342
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
6240
|
-
return
|
|
6343
|
+
return Effect16.gen(function* () {
|
|
6241
6344
|
const state = yield* Ref4.get(persistenceStateRef);
|
|
6242
6345
|
if (!(state.dirty && state.pendingOperation)) {
|
|
6243
6346
|
return [];
|
|
@@ -6263,14 +6366,361 @@ function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
|
6263
6366
|
});
|
|
6264
6367
|
}
|
|
6265
6368
|
|
|
6369
|
+
// src/commands/code/orchestrator/prompt.ts
|
|
6370
|
+
init_esm_shims();
|
|
6371
|
+
var DEFAULT_SYSTEM_PROMPT = `You are executing a ralph loop - an iterative AI coding workflow.
|
|
6372
|
+
|
|
6373
|
+
Your output must include structured signals that the orchestrator will parse.
|
|
6374
|
+
These signals MUST appear on their own lines, not inside code blocks.
|
|
6375
|
+
|
|
6376
|
+
## Signal Format
|
|
6377
|
+
|
|
6378
|
+
Use these XML-like tags to communicate structured information:
|
|
6379
|
+
|
|
6380
|
+
### Task Breakdown (discovery phase only)
|
|
6381
|
+
<ferix:tasks>
|
|
6382
|
+
<task id="1">Brief description of first task</task>
|
|
6383
|
+
<task id="2">Brief description of second task</task>
|
|
6384
|
+
</ferix:tasks>
|
|
6385
|
+
|
|
6386
|
+
### Phase Planning
|
|
6387
|
+
<ferix:phases task="1">
|
|
6388
|
+
<phase id="1.1">First phase description</phase>
|
|
6389
|
+
<phase id="1.2">Second phase description</phase>
|
|
6390
|
+
</ferix:phases>
|
|
6391
|
+
|
|
6392
|
+
### Success Criteria
|
|
6393
|
+
<ferix:criteria task="1">
|
|
6394
|
+
<criterion id="1.c1">First criterion</criterion>
|
|
6395
|
+
<criterion id="1.c2">Second criterion</criterion>
|
|
6396
|
+
</ferix:criteria>
|
|
6397
|
+
|
|
6398
|
+
### Phase Lifecycle
|
|
6399
|
+
<ferix:phase-start id="1.1"/>
|
|
6400
|
+
<ferix:phase-done id="1.1"/>
|
|
6401
|
+
<ferix:phase-failed id="1.1">reason</ferix:phase-failed>
|
|
6402
|
+
|
|
6403
|
+
### Criterion Verification
|
|
6404
|
+
<ferix:criterion-passed id="1.c1"/>
|
|
6405
|
+
<ferix:criterion-failed id="1.c1" reason="Explanation"/>
|
|
6406
|
+
|
|
6407
|
+
### Stage Completion
|
|
6408
|
+
<ferix:check-passed/>
|
|
6409
|
+
<ferix:check-failed/>
|
|
6410
|
+
<ferix:review-complete/>
|
|
6411
|
+
<ferix:review-changes-made/>
|
|
6412
|
+
|
|
6413
|
+
### Task Completion
|
|
6414
|
+
<ferix:task-complete id="1">
|
|
6415
|
+
<summary>Brief summary of what was done</summary>
|
|
6416
|
+
<files-modified>file1.ts, file2.ts</files-modified>
|
|
6417
|
+
<files-created>new-file.ts</files-created>
|
|
6418
|
+
</ferix:task-complete>
|
|
6419
|
+
|
|
6420
|
+
### Loop Completion (use ONLY when ALL tasks are done)
|
|
6421
|
+
<ferix:complete>
|
|
6422
|
+
|
|
6423
|
+
\u26A0\uFE0F IMPORTANT: Only emit <ferix:complete> after ALL tasks in the plan are complete.
|
|
6424
|
+
After completing a single task, emit <ferix:task-complete> and continue to the next task.
|
|
6425
|
+
|
|
6426
|
+
IMPORTANT: Always emit signals on their own lines, never inside markdown code blocks.`;
|
|
6427
|
+
var DISCOVERY_SYSTEM_PROMPT = `You are in the DISCOVERY phase of a ralph loop - an iterative AI coding workflow.
|
|
6428
|
+
|
|
6429
|
+
Your goal is to:
|
|
6430
|
+
1. Generate a short, descriptive name for this session
|
|
6431
|
+
2. Analyze the task and break it into SMALL, GRANULAR subtasks
|
|
6432
|
+
|
|
6433
|
+
Your output must include these signals that the orchestrator will parse.
|
|
6434
|
+
These signals MUST appear on their own lines, not inside code blocks.
|
|
6435
|
+
|
|
6436
|
+
## Signal Format
|
|
6437
|
+
|
|
6438
|
+
### Session Name (REQUIRED - emit first)
|
|
6439
|
+
<ferix:session-name>short-descriptive-name</ferix:session-name>
|
|
6440
|
+
|
|
6441
|
+
Guidelines for session name:
|
|
6442
|
+
- Use 2-5 words in kebab-case (lowercase with hyphens)
|
|
6443
|
+
- Describe the main purpose/feature being worked on
|
|
6444
|
+
- Keep it concise but meaningful
|
|
6445
|
+
- Examples: "add-dark-mode", "fix-auth-flow", "refactor-api-endpoints", "update-user-profile-ui"
|
|
6446
|
+
|
|
6447
|
+
### Task Breakdown (REQUIRED)
|
|
6448
|
+
<ferix:tasks>
|
|
6449
|
+
<task id="1">Brief description of first task</task>
|
|
6450
|
+
<task id="2">Brief description of second task</task>
|
|
6451
|
+
</ferix:tasks>
|
|
6452
|
+
|
|
6453
|
+
## Task Granularity Rules (CRITICAL)
|
|
6454
|
+
|
|
6455
|
+
Apply the **"one sentence without 'and'" test**:
|
|
6456
|
+
- If describing a task requires "and" to connect unrelated functions, split it
|
|
6457
|
+
- Good: "Add login endpoint that validates credentials and returns JWT"
|
|
6458
|
+
- Bad: "Handle authentication, user profiles, and billing"
|
|
6459
|
+
|
|
6460
|
+
Each task MUST be:
|
|
6461
|
+
- Small enough to complete in ONE context window
|
|
6462
|
+
- Testable in isolation with automated checks
|
|
6463
|
+
- A single logical unit of work
|
|
6464
|
+
|
|
6465
|
+
For a typical feature, create 5-15 tasks.
|
|
6466
|
+
For complex projects, create 50-200+ discrete tasks.
|
|
6467
|
+
|
|
6468
|
+
## CRITICAL: Task Exclusions
|
|
6469
|
+
|
|
6470
|
+
Do NOT create tasks for any of the following - these are handled automatically by the orchestrator:
|
|
6471
|
+
- Running verification commands (bun lint, bun format, eslint, prettier, etc.)
|
|
6472
|
+
- Running tests (bun test, jest, vitest, etc.)
|
|
6473
|
+
- Running build commands (bun build, tsc, etc.)
|
|
6474
|
+
- Any "Verification", "Testing", or "Testing Plan" sections from PRDs
|
|
6475
|
+
- Final cleanup, validation, or quality check steps
|
|
6476
|
+
- Commands that just run and check output without changing code
|
|
6477
|
+
|
|
6478
|
+
Tasks should ONLY be for implementation work that creates or modifies source code files.
|
|
6479
|
+
Verification commands are run automatically after each task completes - do not create tasks for them.
|
|
6480
|
+
|
|
6481
|
+
IMPORTANT: Always emit signals on their own lines, never inside markdown code blocks.`;
|
|
6482
|
+
var DEFAULT_PLANNING_PROMPT = `## Phase 2: PLANNING
|
|
6483
|
+
|
|
6484
|
+
If no phases are defined for the current task, define them now.
|
|
6485
|
+
Emit a <ferix:phases> block with the execution phases.`;
|
|
6486
|
+
var DEFAULT_EXECUTION_PROMPT = `## Phase 3: EXECUTION
|
|
6487
|
+
|
|
6488
|
+
Execute each phase in order:
|
|
6489
|
+
1. Emit <ferix:phase-start id="X.Y"/>
|
|
6490
|
+
2. Do the work for that phase
|
|
6491
|
+
3. Emit <ferix:phase-done id="X.Y"/> or <ferix:phase-failed id="X.Y">reason</ferix:phase-failed>`;
|
|
6492
|
+
var DEFAULT_CHECK_PROMPT = `## Phase 4: CHECK
|
|
6493
|
+
|
|
6494
|
+
Verify all success criteria are met:
|
|
6495
|
+
- For each criterion, emit <ferix:criterion-passed id="X.cY"/> or <ferix:criterion-failed id="X.cY" reason="..."/>
|
|
6496
|
+
- Then emit <ferix:check-passed/> or <ferix:check-failed/>`;
|
|
6497
|
+
var DEFAULT_REVIEW_PROMPT = `## Phase 6: REVIEW
|
|
6498
|
+
|
|
6499
|
+
Review the code for quality:
|
|
6500
|
+
- Is it clean and well-organized?
|
|
6501
|
+
- Are there any obvious improvements?
|
|
6502
|
+
- If you make changes, emit <ferix:review-changes-made/>
|
|
6503
|
+
- When done, emit <ferix:review-complete/>`;
|
|
6504
|
+
var DEFAULT_COMPLETION_PROMPT = `## Completion
|
|
6505
|
+
|
|
6506
|
+
When you finish the CURRENT task, emit:
|
|
6507
|
+
<ferix:task-complete id="N">
|
|
6508
|
+
<summary>What was accomplished</summary>
|
|
6509
|
+
<files-modified>list of modified files</files-modified>
|
|
6510
|
+
<files-created>list of new files</files-created>
|
|
6511
|
+
</ferix:task-complete>
|
|
6512
|
+
|
|
6513
|
+
After emitting task-complete, CONTINUE working on the next pending task.
|
|
6514
|
+
|
|
6515
|
+
\u26A0\uFE0F ONLY emit <ferix:complete> when ALL tasks in the plan are done.
|
|
6516
|
+
Do NOT emit <ferix:complete> after completing just one task - continue to the next task instead.`;
|
|
6517
|
+
function getPhasePrompt(phase, prompts, defaultPrompt) {
|
|
6518
|
+
return prompts?.phases?.[phase] ?? defaultPrompt;
|
|
6519
|
+
}
|
|
6520
|
+
function buildSystemPrompt(prompts) {
|
|
6521
|
+
return prompts?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
6522
|
+
}
|
|
6523
|
+
function buildVerifyPrompt(verifyCommands, prompts) {
|
|
6524
|
+
const defaultVerifyPrompt = `## Phase 5: VERIFY
|
|
6525
|
+
|
|
6526
|
+
Run these verification commands:
|
|
6527
|
+
${verifyCommands.map((cmd) => `- ${cmd}`).join("\n")}
|
|
6528
|
+
|
|
6529
|
+
If any fail, fix the issues and re-verify.
|
|
6530
|
+
|
|
6531
|
+
IMPORTANT: Once ALL verification commands pass:
|
|
6532
|
+
- Emit <ferix:task-complete id="N"> with a summary of what was done
|
|
6533
|
+
- If this was the last task, also emit <ferix:complete>
|
|
6534
|
+
- Do NOT run verification again after it passes`;
|
|
6535
|
+
return prompts?.phases?.verify ?? defaultVerifyPrompt;
|
|
6536
|
+
}
|
|
6537
|
+
var TASK_STATUS_ICONS = {
|
|
6538
|
+
done: "[x]",
|
|
6539
|
+
in_progress: "[~]",
|
|
6540
|
+
pending: "[ ]",
|
|
6541
|
+
planning: "[~]",
|
|
6542
|
+
failed: "[!]",
|
|
6543
|
+
skipped: "[-]"
|
|
6544
|
+
};
|
|
6545
|
+
function buildTaskListSection(plan) {
|
|
6546
|
+
if (plan.tasks.length === 0) {
|
|
6547
|
+
return "";
|
|
6548
|
+
}
|
|
6549
|
+
const completedCount = plan.tasks.filter((t) => t.status === "done").length;
|
|
6550
|
+
const totalCount = plan.tasks.length;
|
|
6551
|
+
const lines = [
|
|
6552
|
+
"## Task Progress",
|
|
6553
|
+
"",
|
|
6554
|
+
`Status: ${completedCount}/${totalCount} complete`,
|
|
6555
|
+
""
|
|
6556
|
+
];
|
|
6557
|
+
const currentTaskId = plan.tasks.find(
|
|
6558
|
+
(t) => t.status === "in_progress" || t.status === "pending"
|
|
6559
|
+
)?.id;
|
|
6560
|
+
for (const task of plan.tasks) {
|
|
6561
|
+
const icon = TASK_STATUS_ICONS[task.status];
|
|
6562
|
+
const currentMarker = task.id === currentTaskId ? " \u2190 current" : "";
|
|
6563
|
+
lines.push(
|
|
6564
|
+
`- ${icon} Task ${task.id}: ${task.title} (${task.status})${currentMarker}`
|
|
6565
|
+
);
|
|
6566
|
+
}
|
|
6567
|
+
return lines.join("\n");
|
|
6568
|
+
}
|
|
6569
|
+
function buildCurrentTaskSection(plan) {
|
|
6570
|
+
const currentTask = plan.tasks.find(
|
|
6571
|
+
(t) => t.status === "in_progress" || t.status === "pending"
|
|
6572
|
+
);
|
|
6573
|
+
if (!currentTask) {
|
|
6574
|
+
return void 0;
|
|
6575
|
+
}
|
|
6576
|
+
const phasesSection = currentTask.phases.length > 0 ? `### Phases
|
|
6577
|
+
${currentTask.phases.map((p) => `- ${p.id}: ${p.description} (${p.status})`).join("\n")}` : "No phases defined yet. Emit a <ferix:phases> block to define phases.";
|
|
6578
|
+
const criteriaSection = currentTask.criteria.length > 0 ? `### Success Criteria
|
|
6579
|
+
${currentTask.criteria.map((c) => `- ${c.id}: ${c.description} (${c.status})`).join("\n")}` : "";
|
|
6580
|
+
return `## Current Task
|
|
6581
|
+
|
|
6582
|
+
Task ${currentTask.id}: ${currentTask.title}
|
|
6583
|
+
|
|
6584
|
+
${currentTask.description}
|
|
6585
|
+
|
|
6586
|
+
${phasesSection}
|
|
6587
|
+
|
|
6588
|
+
${criteriaSection}`;
|
|
6589
|
+
}
|
|
6590
|
+
function buildDiscoveryPrompt(config) {
|
|
6591
|
+
const sections = [];
|
|
6592
|
+
sections.push(DISCOVERY_SYSTEM_PROMPT);
|
|
6593
|
+
if (config.prompts?.additionalContext) {
|
|
6594
|
+
sections.push(
|
|
6595
|
+
`## Additional Context
|
|
6596
|
+
|
|
6597
|
+
${config.prompts.additionalContext}`
|
|
6598
|
+
);
|
|
6599
|
+
}
|
|
6600
|
+
sections.push(`## Task
|
|
6601
|
+
|
|
6602
|
+
${config.task}`);
|
|
6603
|
+
sections.push(`## Instructions
|
|
6604
|
+
|
|
6605
|
+
Analyze the task above and:
|
|
6606
|
+
|
|
6607
|
+
1. **Generate a session name** (2-5 words, kebab-case)
|
|
6608
|
+
- Should describe the main purpose of this work
|
|
6609
|
+
- Emit: <ferix:session-name>your-descriptive-name</ferix:session-name>
|
|
6610
|
+
|
|
6611
|
+
2. **Break the task into logical subtasks** (2-6 tasks)
|
|
6612
|
+
- Read and understand what needs to be done
|
|
6613
|
+
- Identify the main components or steps
|
|
6614
|
+
- Emit: <ferix:tasks>...</ferix:tasks>
|
|
6615
|
+
|
|
6616
|
+
Each task should be:
|
|
6617
|
+
- Self-contained and independently verifiable
|
|
6618
|
+
- Clear and specific
|
|
6619
|
+
- Ordered logically (dependencies first)
|
|
6620
|
+
- ONLY for code implementation (NOT for running lint/test/build commands)
|
|
6621
|
+
|
|
6622
|
+
REMINDER: Do NOT create tasks for verification steps like "Run lint", "Run tests", "Verify changes", etc.
|
|
6623
|
+
The orchestrator handles verification automatically after each task completes.
|
|
6624
|
+
If the PRD has a "Verification" or "Testing" section, IGNORE it when creating tasks.
|
|
6625
|
+
|
|
6626
|
+
Begin your analysis now.`);
|
|
6627
|
+
return sections.join("\n\n");
|
|
6628
|
+
}
|
|
6629
|
+
function buildPrompt(config, iteration, plan) {
|
|
6630
|
+
const prompts = config.prompts;
|
|
6631
|
+
const sections = [];
|
|
6632
|
+
sections.push(buildSystemPrompt(prompts));
|
|
6633
|
+
if (prompts?.additionalContext) {
|
|
6634
|
+
sections.push(`## Additional Context
|
|
6635
|
+
|
|
6636
|
+
${prompts.additionalContext}`);
|
|
6637
|
+
}
|
|
6638
|
+
sections.push(`## Task
|
|
6639
|
+
|
|
6640
|
+
${config.task}`);
|
|
6641
|
+
if (plan && plan.tasks.length > 0) {
|
|
6642
|
+
const taskListSection = buildTaskListSection(plan);
|
|
6643
|
+
if (taskListSection) {
|
|
6644
|
+
sections.push(taskListSection);
|
|
6645
|
+
}
|
|
6646
|
+
const taskSection = buildCurrentTaskSection(plan);
|
|
6647
|
+
if (taskSection) {
|
|
6648
|
+
sections.push(taskSection);
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
sections.push(getPhasePrompt("planning", prompts, DEFAULT_PLANNING_PROMPT));
|
|
6652
|
+
sections.push(getPhasePrompt("execution", prompts, DEFAULT_EXECUTION_PROMPT));
|
|
6653
|
+
sections.push(getPhasePrompt("check", prompts, DEFAULT_CHECK_PROMPT));
|
|
6654
|
+
if (config.verifyCommands.length > 0 && plan && !areAllTasksComplete(plan)) {
|
|
6655
|
+
const currentTask = plan.tasks.find(
|
|
6656
|
+
(t) => t.status === "in_progress" || t.status === "pending"
|
|
6657
|
+
);
|
|
6658
|
+
if (currentTask) {
|
|
6659
|
+
sections.push(buildVerifyPrompt(config.verifyCommands, prompts));
|
|
6660
|
+
}
|
|
6661
|
+
}
|
|
6662
|
+
sections.push(getPhasePrompt("review", prompts, DEFAULT_REVIEW_PROMPT));
|
|
6663
|
+
sections.push(
|
|
6664
|
+
getPhasePrompt("completion", prompts, DEFAULT_COMPLETION_PROMPT)
|
|
6665
|
+
);
|
|
6666
|
+
sections.push(`---
|
|
6667
|
+
|
|
6668
|
+
Iteration ${iteration} of ${config.maxIterations || "unlimited"}
|
|
6669
|
+
|
|
6670
|
+
Begin.`);
|
|
6671
|
+
return sections.join("\n\n");
|
|
6672
|
+
}
|
|
6673
|
+
function areAllTasksComplete(plan) {
|
|
6674
|
+
if (!plan || plan.tasks.length === 0) {
|
|
6675
|
+
return false;
|
|
6676
|
+
}
|
|
6677
|
+
return plan.tasks.every((t) => t.status === "done" || t.status === "skipped");
|
|
6678
|
+
}
|
|
6679
|
+
function buildSessionState(config, iteration, sessionId, plan, recentProgress = []) {
|
|
6680
|
+
const currentTask = plan?.tasks.find(
|
|
6681
|
+
(t) => t.status === "in_progress" || t.status === "pending"
|
|
6682
|
+
);
|
|
6683
|
+
const taskSummary = plan ? {
|
|
6684
|
+
total: plan.tasks.length,
|
|
6685
|
+
done: plan.tasks.filter((t) => t.status === "done").length,
|
|
6686
|
+
inProgress: plan.tasks.filter((t) => t.status === "in_progress").length,
|
|
6687
|
+
pending: plan.tasks.filter(
|
|
6688
|
+
(t) => t.status === "pending" || t.status === "planning"
|
|
6689
|
+
).length
|
|
6690
|
+
} : { total: 0, done: 0, inProgress: 0, pending: 0 };
|
|
6691
|
+
return {
|
|
6692
|
+
sessionId,
|
|
6693
|
+
originalTask: config.task,
|
|
6694
|
+
iteration,
|
|
6695
|
+
maxIterations: config.maxIterations,
|
|
6696
|
+
taskSummary,
|
|
6697
|
+
currentTask: currentTask ? {
|
|
6698
|
+
id: currentTask.id,
|
|
6699
|
+
title: currentTask.title,
|
|
6700
|
+
description: currentTask.description,
|
|
6701
|
+
phases: currentTask.phases.map((p) => ({
|
|
6702
|
+
id: p.id,
|
|
6703
|
+
description: p.description,
|
|
6704
|
+
status: p.status
|
|
6705
|
+
})),
|
|
6706
|
+
criteria: currentTask.criteria.map((c) => ({
|
|
6707
|
+
id: c.id,
|
|
6708
|
+
description: c.description,
|
|
6709
|
+
status: c.status
|
|
6710
|
+
}))
|
|
6711
|
+
} : null,
|
|
6712
|
+
recentProgress
|
|
6713
|
+
};
|
|
6714
|
+
}
|
|
6715
|
+
|
|
6266
6716
|
// src/commands/code/orchestrator/discovery.ts
|
|
6267
6717
|
function processTextSignals(signalParser, text, context) {
|
|
6268
|
-
return
|
|
6718
|
+
return Effect17.gen(function* () {
|
|
6269
6719
|
const events = [];
|
|
6270
6720
|
const parsedSignals = [];
|
|
6271
6721
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6272
|
-
|
|
6273
|
-
(error) =>
|
|
6722
|
+
Effect17.tapError(
|
|
6723
|
+
(error) => Effect17.logDebug(
|
|
6274
6724
|
"Signal parsing failed, continuing with empty signals",
|
|
6275
6725
|
{
|
|
6276
6726
|
error: String(error),
|
|
@@ -6278,7 +6728,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6278
6728
|
}
|
|
6279
6729
|
)
|
|
6280
6730
|
),
|
|
6281
|
-
|
|
6731
|
+
Effect17.orElseSucceed(() => [])
|
|
6282
6732
|
);
|
|
6283
6733
|
for (const signal of signals) {
|
|
6284
6734
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6288,7 +6738,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6288
6738
|
});
|
|
6289
6739
|
}
|
|
6290
6740
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
6291
|
-
return
|
|
6741
|
+
return Effect17.gen(function* () {
|
|
6292
6742
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6293
6743
|
const events = [domainEvent];
|
|
6294
6744
|
const allSignals = [];
|
|
@@ -6339,7 +6789,7 @@ function planToTasksFile(plan, sessionId, originalTask) {
|
|
|
6339
6789
|
}
|
|
6340
6790
|
function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptStore, progressStore, currentPlanRef, config, sessionId, worktreePath, onSessionName) {
|
|
6341
6791
|
return Stream6.unwrap(
|
|
6342
|
-
|
|
6792
|
+
Effect17.gen(function* () {
|
|
6343
6793
|
const startTimeUtc = yield* DateTime5.now;
|
|
6344
6794
|
const startTime = DateTime5.toEpochMillis(startTimeUtc);
|
|
6345
6795
|
const persistenceStateRef = yield* Ref5.make({
|
|
@@ -6367,7 +6817,7 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6367
6817
|
),
|
|
6368
6818
|
Stream6.flatMap(
|
|
6369
6819
|
(llmEvent) => Stream6.unwrap(
|
|
6370
|
-
|
|
6820
|
+
Effect17.gen(function* () {
|
|
6371
6821
|
const now = yield* DateTime5.now;
|
|
6372
6822
|
const context = {
|
|
6373
6823
|
timestamp: DateTime5.toEpochMillis(now)
|
|
@@ -6407,7 +6857,7 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6407
6857
|
)
|
|
6408
6858
|
);
|
|
6409
6859
|
const completionStream = Stream6.fromEffect(
|
|
6410
|
-
|
|
6860
|
+
Effect17.gen(function* () {
|
|
6411
6861
|
const persistEvents = yield* flushPlanPersistence(
|
|
6412
6862
|
planStore,
|
|
6413
6863
|
currentPlanRef,
|
|
@@ -6416,44 +6866,38 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6416
6866
|
const plan = yield* Ref5.get(currentPlanRef);
|
|
6417
6867
|
const taskCount = plan?.tasks.length ?? 0;
|
|
6418
6868
|
yield* promptStore.copyTemplate(sessionId).pipe(
|
|
6419
|
-
|
|
6420
|
-
(error) =>
|
|
6869
|
+
Effect17.tapError(
|
|
6870
|
+
(error) => Effect17.logDebug("Failed to copy PROMPT.md, continuing", {
|
|
6421
6871
|
error: String(error)
|
|
6422
6872
|
})
|
|
6423
6873
|
),
|
|
6424
|
-
|
|
6874
|
+
Effect17.orElseSucceed(() => void 0)
|
|
6425
6875
|
);
|
|
6426
6876
|
if (plan && plan.tasks.length > 0) {
|
|
6427
|
-
const taskGenModule = yield* Effect16.promise(
|
|
6428
|
-
() => import("./task-generation-YN2MKKY4.js")
|
|
6429
|
-
);
|
|
6430
6877
|
const tasksFile = planToTasksFile(plan, sessionId, config.task);
|
|
6431
|
-
yield*
|
|
6432
|
-
|
|
6433
|
-
(error) =>
|
|
6878
|
+
yield* writeTasksJson(sessionId, tasksFile).pipe(
|
|
6879
|
+
Effect17.tapError(
|
|
6880
|
+
(error) => Effect17.logDebug("Failed to write tasks.json, continuing", {
|
|
6434
6881
|
error: String(error)
|
|
6435
6882
|
})
|
|
6436
6883
|
),
|
|
6437
|
-
|
|
6438
|
-
);
|
|
6439
|
-
const promptModule = yield* Effect16.promise(
|
|
6440
|
-
() => import("./prompt-YXBU2FXF.js")
|
|
6884
|
+
Effect17.orElseSucceed(() => void 0)
|
|
6441
6885
|
);
|
|
6442
6886
|
const recentEntries = yield* progressStore.getRecent(sessionId, 5).pipe(
|
|
6443
|
-
|
|
6444
|
-
(error) =>
|
|
6887
|
+
Effect17.tapError(
|
|
6888
|
+
(error) => Effect17.logDebug(
|
|
6445
6889
|
"Failed to get recent progress, continuing",
|
|
6446
6890
|
{
|
|
6447
6891
|
error: String(error)
|
|
6448
6892
|
}
|
|
6449
6893
|
)
|
|
6450
6894
|
),
|
|
6451
|
-
|
|
6895
|
+
Effect17.orElseSucceed(() => [])
|
|
6452
6896
|
);
|
|
6453
6897
|
const recentProgress = recentEntries.map(
|
|
6454
6898
|
(entry) => `[${entry.action}] Task ${entry.taskId}: ${entry.summary}`
|
|
6455
6899
|
);
|
|
6456
|
-
const initialState =
|
|
6900
|
+
const initialState = buildSessionState(
|
|
6457
6901
|
config,
|
|
6458
6902
|
0,
|
|
6459
6903
|
// iteration 0 = discovery
|
|
@@ -6462,12 +6906,12 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6462
6906
|
recentProgress
|
|
6463
6907
|
);
|
|
6464
6908
|
yield* stateStore.write(sessionId, initialState).pipe(
|
|
6465
|
-
|
|
6466
|
-
(error) =>
|
|
6909
|
+
Effect17.tapError(
|
|
6910
|
+
(error) => Effect17.logDebug("Failed to write STATE.json, continuing", {
|
|
6467
6911
|
error: String(error)
|
|
6468
6912
|
})
|
|
6469
6913
|
),
|
|
6470
|
-
|
|
6914
|
+
Effect17.orElseSucceed(() => void 0)
|
|
6471
6915
|
);
|
|
6472
6916
|
}
|
|
6473
6917
|
const endTimeUtc = yield* DateTime5.now;
|
|
@@ -6477,8 +6921,8 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6477
6921
|
if (capturedName) {
|
|
6478
6922
|
if (onSessionName) {
|
|
6479
6923
|
yield* onSessionName(capturedName).pipe(
|
|
6480
|
-
|
|
6481
|
-
(error) =>
|
|
6924
|
+
Effect17.tapError(
|
|
6925
|
+
(error) => Effect17.logDebug(
|
|
6482
6926
|
"Failed to handle session name, continuing",
|
|
6483
6927
|
{
|
|
6484
6928
|
error: String(error),
|
|
@@ -6486,7 +6930,7 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6486
6930
|
}
|
|
6487
6931
|
)
|
|
6488
6932
|
),
|
|
6489
|
-
|
|
6933
|
+
Effect17.orElseSucceed(() => void 0)
|
|
6490
6934
|
);
|
|
6491
6935
|
}
|
|
6492
6936
|
const sessionNameEvent = {
|
|
@@ -6518,15 +6962,15 @@ function createDiscoveryStream(llm, signalParser, planStore, stateStore, promptS
|
|
|
6518
6962
|
|
|
6519
6963
|
// src/commands/code/orchestrator/iteration.ts
|
|
6520
6964
|
init_esm_shims();
|
|
6521
|
-
import { DateTime as DateTime6, Effect as
|
|
6965
|
+
import { DateTime as DateTime6, Effect as Effect18, pipe as pipe2, Ref as Ref6, Stream as Stream7 } from "effect";
|
|
6522
6966
|
function processTextSignals2(signalParser, text, context) {
|
|
6523
|
-
return
|
|
6967
|
+
return Effect18.gen(function* () {
|
|
6524
6968
|
const events = [];
|
|
6525
6969
|
let completed = false;
|
|
6526
6970
|
const parsedSignals = [];
|
|
6527
6971
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6528
|
-
|
|
6529
|
-
(error) =>
|
|
6972
|
+
Effect18.tapError(
|
|
6973
|
+
(error) => Effect18.logDebug(
|
|
6530
6974
|
"Signal parsing failed, continuing with empty signals",
|
|
6531
6975
|
{
|
|
6532
6976
|
error: String(error),
|
|
@@ -6534,10 +6978,10 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6534
6978
|
}
|
|
6535
6979
|
)
|
|
6536
6980
|
),
|
|
6537
|
-
|
|
6981
|
+
Effect18.orElseSucceed(() => [])
|
|
6538
6982
|
);
|
|
6539
6983
|
if (signals.length > 0) {
|
|
6540
|
-
yield*
|
|
6984
|
+
yield* Effect18.logDebug("Signals parsed", {
|
|
6541
6985
|
signals: signals.map((s) => s._tag),
|
|
6542
6986
|
count: signals.length
|
|
6543
6987
|
});
|
|
@@ -6553,7 +6997,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6553
6997
|
});
|
|
6554
6998
|
}
|
|
6555
6999
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
6556
|
-
return
|
|
7000
|
+
return Effect18.gen(function* () {
|
|
6557
7001
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6558
7002
|
const events = [domainEvent];
|
|
6559
7003
|
let completed = false;
|
|
@@ -6586,7 +7030,7 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
6586
7030
|
}
|
|
6587
7031
|
function createIterationStream(llm, signalParser, planStore, stateStore, progressStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
6588
7032
|
return Stream7.unwrap(
|
|
6589
|
-
|
|
7033
|
+
Effect18.gen(function* () {
|
|
6590
7034
|
const currentPlan = yield* Ref6.get(currentPlanRef);
|
|
6591
7035
|
const persistenceStateRef = yield* Ref6.make({
|
|
6592
7036
|
dirty: false,
|
|
@@ -6596,19 +7040,18 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6596
7040
|
_tag: "IterationStarted",
|
|
6597
7041
|
iteration
|
|
6598
7042
|
};
|
|
6599
|
-
const promptModule = yield* Effect17.promise(() => import("./prompt-YXBU2FXF.js"));
|
|
6600
7043
|
const recentEntries = yield* progressStore.getRecent(sessionId, 5).pipe(
|
|
6601
|
-
|
|
6602
|
-
(error) =>
|
|
7044
|
+
Effect18.tapError(
|
|
7045
|
+
(error) => Effect18.logDebug("Failed to get recent progress, continuing", {
|
|
6603
7046
|
error: String(error)
|
|
6604
7047
|
})
|
|
6605
7048
|
),
|
|
6606
|
-
|
|
7049
|
+
Effect18.orElseSucceed(() => [])
|
|
6607
7050
|
);
|
|
6608
7051
|
const recentProgress = recentEntries.map(
|
|
6609
7052
|
(entry) => `[${entry.action}] Task ${entry.taskId}: ${entry.summary}`
|
|
6610
7053
|
);
|
|
6611
|
-
const state =
|
|
7054
|
+
const state = buildSessionState(
|
|
6612
7055
|
config,
|
|
6613
7056
|
iteration,
|
|
6614
7057
|
sessionId,
|
|
@@ -6616,16 +7059,16 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6616
7059
|
recentProgress
|
|
6617
7060
|
);
|
|
6618
7061
|
yield* stateStore.write(sessionId, state).pipe(
|
|
6619
|
-
|
|
6620
|
-
(error) =>
|
|
7062
|
+
Effect18.tapError(
|
|
7063
|
+
(error) => Effect18.logDebug("Failed to write STATE.json, continuing", {
|
|
6621
7064
|
error: String(error)
|
|
6622
7065
|
})
|
|
6623
7066
|
),
|
|
6624
|
-
|
|
7067
|
+
Effect18.orElseSucceed(() => void 0)
|
|
6625
7068
|
);
|
|
6626
7069
|
const prompt = buildPrompt(config, iteration, currentPlan);
|
|
6627
7070
|
const promptPreview = prompt.slice(0, 200);
|
|
6628
|
-
yield*
|
|
7071
|
+
yield* Effect18.logDebug("Building prompt for iteration", {
|
|
6629
7072
|
iteration,
|
|
6630
7073
|
promptLength: prompt.length,
|
|
6631
7074
|
promptPreview: promptPreview.length < prompt.length ? `${promptPreview}...` : promptPreview
|
|
@@ -6643,7 +7086,7 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6643
7086
|
),
|
|
6644
7087
|
Stream7.flatMap(
|
|
6645
7088
|
(llmEvent) => Stream7.unwrap(
|
|
6646
|
-
|
|
7089
|
+
Effect18.gen(function* () {
|
|
6647
7090
|
const now = yield* DateTime6.now;
|
|
6648
7091
|
const context = {
|
|
6649
7092
|
timestamp: DateTime6.toEpochMillis(now)
|
|
@@ -6665,14 +7108,14 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6665
7108
|
events.push(...planEvents);
|
|
6666
7109
|
}
|
|
6667
7110
|
if (result.completed) {
|
|
6668
|
-
yield*
|
|
7111
|
+
yield* Effect18.logInfo(
|
|
6669
7112
|
"[DEBUG] createIterationStream: LLM emitted completion signal"
|
|
6670
7113
|
);
|
|
6671
7114
|
yield* Ref6.set(loopCompletedRef, true);
|
|
6672
7115
|
}
|
|
6673
7116
|
const updatedPlan = yield* Ref6.get(currentPlanRef);
|
|
6674
7117
|
const allComplete = areAllTasksComplete(updatedPlan);
|
|
6675
|
-
yield*
|
|
7118
|
+
yield* Effect18.logInfo(
|
|
6676
7119
|
"[DEBUG] createIterationStream: Auto-complete check",
|
|
6677
7120
|
{
|
|
6678
7121
|
llmEmittedComplete: result.completed,
|
|
@@ -6684,7 +7127,7 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6684
7127
|
}
|
|
6685
7128
|
);
|
|
6686
7129
|
if (allComplete) {
|
|
6687
|
-
yield*
|
|
7130
|
+
yield* Effect18.logInfo(
|
|
6688
7131
|
"[DEBUG] createIterationStream: All tasks complete - ending loop"
|
|
6689
7132
|
);
|
|
6690
7133
|
yield* Ref6.set(loopCompletedRef, true);
|
|
@@ -6706,7 +7149,7 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6706
7149
|
)
|
|
6707
7150
|
);
|
|
6708
7151
|
const completionStream = Stream7.fromEffect(
|
|
6709
|
-
|
|
7152
|
+
Effect18.gen(function* () {
|
|
6710
7153
|
const persistEvents = yield* flushPlanPersistence(
|
|
6711
7154
|
planStore,
|
|
6712
7155
|
currentPlanRef,
|
|
@@ -6731,7 +7174,7 @@ function createIterationStream(llm, signalParser, planStore, stateStore, progres
|
|
|
6731
7174
|
// src/commands/code/orchestrator/loop.ts
|
|
6732
7175
|
function runLoop(config) {
|
|
6733
7176
|
return Stream8.unwrap(
|
|
6734
|
-
|
|
7177
|
+
Effect19.gen(function* () {
|
|
6735
7178
|
const llm = yield* LLM;
|
|
6736
7179
|
const signalParser = yield* SignalParser;
|
|
6737
7180
|
const sessionStore = yield* SessionStore;
|
|
@@ -6741,7 +7184,7 @@ function runLoop(config) {
|
|
|
6741
7184
|
const stateStore = yield* StateStore;
|
|
6742
7185
|
const promptStore = yield* PromptStore;
|
|
6743
7186
|
const session = yield* sessionStore.create(config.task, config.sessionId).pipe(
|
|
6744
|
-
|
|
7187
|
+
Effect19.mapError(
|
|
6745
7188
|
(e) => new OrchestratorError({
|
|
6746
7189
|
message: `Failed to create session: ${e.message}`,
|
|
6747
7190
|
phase: "setup",
|
|
@@ -6750,15 +7193,15 @@ function runLoop(config) {
|
|
|
6750
7193
|
)
|
|
6751
7194
|
);
|
|
6752
7195
|
const baseBranch = yield* git.getCurrentBranch().pipe(
|
|
6753
|
-
|
|
6754
|
-
(error) =>
|
|
7196
|
+
Effect19.tapError(
|
|
7197
|
+
(error) => Effect19.logDebug("Failed to get current branch", {
|
|
6755
7198
|
error: String(error)
|
|
6756
7199
|
})
|
|
6757
7200
|
),
|
|
6758
|
-
|
|
7201
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6759
7202
|
);
|
|
6760
7203
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
6761
|
-
|
|
7204
|
+
Effect19.mapError(
|
|
6762
7205
|
(e) => new OrchestratorError({
|
|
6763
7206
|
message: `Failed to create worktree: ${e.message}`,
|
|
6764
7207
|
phase: "setup",
|
|
@@ -6773,12 +7216,12 @@ function runLoop(config) {
|
|
|
6773
7216
|
branchName,
|
|
6774
7217
|
baseBranch
|
|
6775
7218
|
}).pipe(
|
|
6776
|
-
|
|
6777
|
-
(error) =>
|
|
7219
|
+
Effect19.tapError(
|
|
7220
|
+
(error) => Effect19.logDebug("Failed to update session with worktree info", {
|
|
6778
7221
|
error: String(error)
|
|
6779
7222
|
})
|
|
6780
7223
|
),
|
|
6781
|
-
|
|
7224
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6782
7225
|
);
|
|
6783
7226
|
const startTimeUtc = yield* DateTime7.now;
|
|
6784
7227
|
const startTime = DateTime7.toEpochMillis(startTimeUtc);
|
|
@@ -6799,7 +7242,7 @@ function runLoop(config) {
|
|
|
6799
7242
|
config,
|
|
6800
7243
|
timestamp: startTime
|
|
6801
7244
|
};
|
|
6802
|
-
const handleSessionName = (displayName) =>
|
|
7245
|
+
const handleSessionName = (displayName) => Effect19.gen(function* () {
|
|
6803
7246
|
const currentSession = yield* Ref7.get(sessionRef);
|
|
6804
7247
|
const newBranchName = yield* git.renameBranch(
|
|
6805
7248
|
session.id,
|
|
@@ -6828,7 +7271,7 @@ function runLoop(config) {
|
|
|
6828
7271
|
);
|
|
6829
7272
|
const iterationsStream = Stream8.unfoldEffect(
|
|
6830
7273
|
1,
|
|
6831
|
-
(iteration) =>
|
|
7274
|
+
(iteration) => Effect19.gen(function* () {
|
|
6832
7275
|
const completed = yield* Ref7.get(loopCompletedRef);
|
|
6833
7276
|
if (completed || iteration > maxIterations) {
|
|
6834
7277
|
return Option.none();
|
|
@@ -6876,8 +7319,8 @@ function runLoop(config) {
|
|
|
6876
7319
|
);
|
|
6877
7320
|
}).pipe(
|
|
6878
7321
|
// Also catch setup errors (e.g., session creation failure)
|
|
6879
|
-
|
|
6880
|
-
(error) =>
|
|
7322
|
+
Effect19.catchAll(
|
|
7323
|
+
(error) => Effect19.succeed(
|
|
6881
7324
|
Stream8.succeed({
|
|
6882
7325
|
_tag: "LoopFailed",
|
|
6883
7326
|
error: {
|
|
@@ -6903,32 +7346,32 @@ Generated by Ferix`;
|
|
|
6903
7346
|
}
|
|
6904
7347
|
function createCompletionStream(sessionStore, git, sessionRef, config, startTime, loopCompletedRef, _worktreePath) {
|
|
6905
7348
|
return Stream8.unwrap(
|
|
6906
|
-
|
|
7349
|
+
Effect19.gen(function* () {
|
|
6907
7350
|
const session = yield* Ref7.get(sessionRef);
|
|
6908
7351
|
const endTimeUtc = yield* DateTime7.now;
|
|
6909
7352
|
const endTime = DateTime7.toEpochMillis(endTimeUtc);
|
|
6910
7353
|
const durationMs = endTime - startTime;
|
|
6911
7354
|
const completed = yield* Ref7.get(loopCompletedRef);
|
|
6912
7355
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
6913
|
-
|
|
6914
|
-
(error) =>
|
|
7356
|
+
Effect19.tapError(
|
|
7357
|
+
(error) => Effect19.logDebug("Final commit failed, continuing", {
|
|
6915
7358
|
sessionId: session.id,
|
|
6916
7359
|
error: String(error)
|
|
6917
7360
|
})
|
|
6918
7361
|
),
|
|
6919
|
-
|
|
7362
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6920
7363
|
);
|
|
6921
7364
|
let branchPushed = false;
|
|
6922
7365
|
if (config.push === true) {
|
|
6923
7366
|
const pushResult = yield* git.pushBranch(session.id).pipe(
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
(error) =>
|
|
7367
|
+
Effect19.map(() => true),
|
|
7368
|
+
Effect19.tapError(
|
|
7369
|
+
(error) => Effect19.logDebug("Push failed, continuing", {
|
|
6927
7370
|
sessionId: session.id,
|
|
6928
7371
|
error: String(error)
|
|
6929
7372
|
})
|
|
6930
7373
|
),
|
|
6931
|
-
|
|
7374
|
+
Effect19.orElseSucceed(() => false)
|
|
6932
7375
|
);
|
|
6933
7376
|
branchPushed = pushResult;
|
|
6934
7377
|
}
|
|
@@ -6937,25 +7380,25 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
6937
7380
|
const title = `feat: ${session.originalTask.slice(0, 50)}`;
|
|
6938
7381
|
const body = buildPRBody(session, config);
|
|
6939
7382
|
const prResult = yield* git.createPR(session.id, title, body, session.baseBranch).pipe(
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
(error) =>
|
|
7383
|
+
Effect19.map((url) => url),
|
|
7384
|
+
Effect19.tapError(
|
|
7385
|
+
(error) => Effect19.logDebug("PR creation failed, continuing", {
|
|
6943
7386
|
sessionId: session.id,
|
|
6944
7387
|
error: String(error)
|
|
6945
7388
|
})
|
|
6946
7389
|
),
|
|
6947
|
-
|
|
7390
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6948
7391
|
);
|
|
6949
7392
|
prUrl = prResult;
|
|
6950
7393
|
}
|
|
6951
7394
|
yield* git.removeWorktreeKeepBranch(session.id).pipe(
|
|
6952
|
-
|
|
6953
|
-
(error) =>
|
|
7395
|
+
Effect19.tapError(
|
|
7396
|
+
(error) => Effect19.logDebug("Worktree cleanup failed, continuing", {
|
|
6954
7397
|
sessionId: session.id,
|
|
6955
7398
|
error: String(error)
|
|
6956
7399
|
})
|
|
6957
7400
|
),
|
|
6958
|
-
|
|
7401
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6959
7402
|
);
|
|
6960
7403
|
const worktreeRemoved = {
|
|
6961
7404
|
_tag: "WorktreeRemoved",
|
|
@@ -6976,13 +7419,13 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
6976
7419
|
status: completed ? "completed" : "paused",
|
|
6977
7420
|
worktreePath: void 0
|
|
6978
7421
|
}).pipe(
|
|
6979
|
-
|
|
6980
|
-
(error) =>
|
|
7422
|
+
Effect19.tapError(
|
|
7423
|
+
(error) => Effect19.logDebug("Session update failed, continuing", {
|
|
6981
7424
|
sessionId: session.id,
|
|
6982
7425
|
error: String(error)
|
|
6983
7426
|
})
|
|
6984
7427
|
),
|
|
6985
|
-
|
|
7428
|
+
Effect19.orElseSucceed(() => void 0)
|
|
6986
7429
|
);
|
|
6987
7430
|
const events = [worktreeRemoved];
|
|
6988
7431
|
if (branchPushed) {
|
|
@@ -7018,7 +7461,7 @@ function run(options) {
|
|
|
7018
7461
|
const sessionId = config.sessionId ?? generateSessionId2();
|
|
7019
7462
|
const configWithSession = { ...config, sessionId };
|
|
7020
7463
|
const events = runLoop(configWithSession);
|
|
7021
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream9.tap((event) =>
|
|
7464
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream9.tap((event) => Effect20.sync(() => onEvent(event)))) : events;
|
|
7022
7465
|
const baseLayers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
7023
7466
|
const loggerLayer = createLoggerLayer(config.debug ?? false, sessionId);
|
|
7024
7467
|
const layers = Layer12.merge(baseLayers, loggerLayer);
|
|
@@ -7031,7 +7474,7 @@ function run(options) {
|
|
|
7031
7474
|
}
|
|
7032
7475
|
function main(config) {
|
|
7033
7476
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
7034
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
7477
|
+
return run({ config, consumer: consumerType }).pipe(Effect20.runPromise);
|
|
7035
7478
|
}
|
|
7036
7479
|
|
|
7037
7480
|
// src/commands/code/services/index.ts
|
|
@@ -7116,14 +7559,14 @@ init_esm_shims();
|
|
|
7116
7559
|
import { access as access5 } from "fs/promises";
|
|
7117
7560
|
import { dirname as dirname4, resolve } from "path";
|
|
7118
7561
|
import { cancel } from "@clack/prompts";
|
|
7119
|
-
import { Effect as
|
|
7562
|
+
import { Effect as Effect25 } from "effect";
|
|
7120
7563
|
import pc19 from "picocolors";
|
|
7121
7564
|
|
|
7122
7565
|
// src/commands/sync/detect-agents.ts
|
|
7123
7566
|
init_esm_shims();
|
|
7124
7567
|
import { access as access3 } from "fs/promises";
|
|
7125
|
-
import { join as
|
|
7126
|
-
import { Effect as
|
|
7568
|
+
import { join as join10 } from "path";
|
|
7569
|
+
import { Effect as Effect21 } from "effect";
|
|
7127
7570
|
var AGENT_DIRECTORIES = {
|
|
7128
7571
|
opencode: [".opencode"],
|
|
7129
7572
|
"claude-code": [".claude"],
|
|
@@ -7136,7 +7579,7 @@ var AGENT_DIRECTORIES = {
|
|
|
7136
7579
|
var SUPPORTED_AGENTS = Object.keys(
|
|
7137
7580
|
AGENT_DIRECTORIES
|
|
7138
7581
|
);
|
|
7139
|
-
var directoryExists2 = (dirPath) =>
|
|
7582
|
+
var directoryExists2 = (dirPath) => Effect21.promise(async () => {
|
|
7140
7583
|
try {
|
|
7141
7584
|
await access3(dirPath);
|
|
7142
7585
|
return true;
|
|
@@ -7146,18 +7589,18 @@ var directoryExists2 = (dirPath) => Effect20.promise(async () => {
|
|
|
7146
7589
|
});
|
|
7147
7590
|
var isAgentPresent = (projectDir, agent) => {
|
|
7148
7591
|
const directories = AGENT_DIRECTORIES[agent];
|
|
7149
|
-
return
|
|
7592
|
+
return Effect21.forEach(
|
|
7150
7593
|
directories,
|
|
7151
|
-
(dir) => directoryExists2(
|
|
7152
|
-
).pipe(
|
|
7594
|
+
(dir) => directoryExists2(join10(projectDir, dir))
|
|
7595
|
+
).pipe(Effect21.map((results) => results.some((exists) => exists)));
|
|
7153
7596
|
};
|
|
7154
|
-
var detectAgents = (projectDir) =>
|
|
7597
|
+
var detectAgents = (projectDir) => Effect21.forEach(
|
|
7155
7598
|
SUPPORTED_AGENTS,
|
|
7156
7599
|
(agent) => isAgentPresent(projectDir, agent).pipe(
|
|
7157
|
-
|
|
7600
|
+
Effect21.map((present) => present ? agent : null)
|
|
7158
7601
|
)
|
|
7159
7602
|
).pipe(
|
|
7160
|
-
|
|
7603
|
+
Effect21.map(
|
|
7161
7604
|
(results) => results.filter((agent) => agent !== null)
|
|
7162
7605
|
)
|
|
7163
7606
|
);
|
|
@@ -7175,24 +7618,24 @@ var components = componentsGeneric();
|
|
|
7175
7618
|
|
|
7176
7619
|
// src/commands/sync/find-skills.ts
|
|
7177
7620
|
import { ConvexHttpClient } from "convex/browser";
|
|
7178
|
-
import { Effect as
|
|
7621
|
+
import { Effect as Effect22, Schema as S23 } from "effect";
|
|
7179
7622
|
|
|
7180
7623
|
// src/commands/sync/errors.ts
|
|
7181
7624
|
init_esm_shims();
|
|
7182
|
-
import { Data } from "effect";
|
|
7183
|
-
var SchemaValidationError = class extends
|
|
7625
|
+
import { Data as Data2 } from "effect";
|
|
7626
|
+
var SchemaValidationError = class extends Data2.TaggedError(
|
|
7184
7627
|
"SchemaValidationError"
|
|
7185
7628
|
) {
|
|
7186
7629
|
};
|
|
7187
|
-
var ConvexError = class extends
|
|
7630
|
+
var ConvexError = class extends Data2.TaggedError("ConvexError") {
|
|
7188
7631
|
};
|
|
7189
|
-
var SkillInstallError = class extends
|
|
7632
|
+
var SkillInstallError = class extends Data2.TaggedError("SkillInstallError") {
|
|
7190
7633
|
};
|
|
7191
7634
|
|
|
7192
7635
|
// src/commands/sync/types.ts
|
|
7193
7636
|
init_esm_shims();
|
|
7194
|
-
import { Schema as
|
|
7195
|
-
var AgentNameSchema =
|
|
7637
|
+
import { Schema as S22 } from "effect";
|
|
7638
|
+
var AgentNameSchema = S22.Literal(
|
|
7196
7639
|
"opencode",
|
|
7197
7640
|
"claude-code",
|
|
7198
7641
|
"cursor",
|
|
@@ -7201,21 +7644,21 @@ var AgentNameSchema = S21.Literal(
|
|
|
7201
7644
|
"openhands",
|
|
7202
7645
|
"windsurf"
|
|
7203
7646
|
);
|
|
7204
|
-
var SkillRepoSchema =
|
|
7205
|
-
owner:
|
|
7206
|
-
repo:
|
|
7207
|
-
githubUrl:
|
|
7208
|
-
});
|
|
7209
|
-
var PackageOrgSchema =
|
|
7210
|
-
packageName:
|
|
7211
|
-
githubOrg:
|
|
7212
|
-
});
|
|
7213
|
-
var PackageOrgsResponseSchema =
|
|
7214
|
-
var SkillReposResponseSchema =
|
|
7215
|
-
var InstallOptionsSchema =
|
|
7216
|
-
dryRun:
|
|
7217
|
-
global:
|
|
7218
|
-
agents:
|
|
7647
|
+
var SkillRepoSchema = S22.Struct({
|
|
7648
|
+
owner: S22.String,
|
|
7649
|
+
repo: S22.String,
|
|
7650
|
+
githubUrl: S22.String
|
|
7651
|
+
});
|
|
7652
|
+
var PackageOrgSchema = S22.Struct({
|
|
7653
|
+
packageName: S22.String,
|
|
7654
|
+
githubOrg: S22.NullOr(S22.String)
|
|
7655
|
+
});
|
|
7656
|
+
var PackageOrgsResponseSchema = S22.Array(PackageOrgSchema);
|
|
7657
|
+
var SkillReposResponseSchema = S22.Array(SkillRepoSchema);
|
|
7658
|
+
var InstallOptionsSchema = S22.Struct({
|
|
7659
|
+
dryRun: S22.optional(S22.Boolean),
|
|
7660
|
+
global: S22.optional(S22.Boolean),
|
|
7661
|
+
agents: S22.optional(S22.Array(AgentNameSchema))
|
|
7219
7662
|
});
|
|
7220
7663
|
var CONVEX_URL_PROD = "https://groovy-mallard-649.convex.cloud";
|
|
7221
7664
|
var CONVEX_URL_DEV = "https://majestic-gnu-964.convex.cloud";
|
|
@@ -7228,8 +7671,8 @@ var NON_NPM_VERSION_PREFIXES = [
|
|
|
7228
7671
|
];
|
|
7229
7672
|
|
|
7230
7673
|
// src/commands/sync/find-skills.ts
|
|
7231
|
-
var findSkillRepos = (orgs, dev) =>
|
|
7232
|
-
const response = yield*
|
|
7674
|
+
var findSkillRepos = (orgs, dev) => Effect22.gen(function* () {
|
|
7675
|
+
const response = yield* Effect22.tryPromise({
|
|
7233
7676
|
try: async () => {
|
|
7234
7677
|
const client = new ConvexHttpClient(getConvexUrl(dev));
|
|
7235
7678
|
return await client.query(api.directories.getByOwners, {
|
|
@@ -7242,10 +7685,10 @@ var findSkillRepos = (orgs, dev) => Effect21.gen(function* () {
|
|
|
7242
7685
|
cause: error
|
|
7243
7686
|
})
|
|
7244
7687
|
});
|
|
7245
|
-
const validated = yield*
|
|
7688
|
+
const validated = yield* S23.decodeUnknown(SkillReposResponseSchema)(
|
|
7246
7689
|
response
|
|
7247
7690
|
).pipe(
|
|
7248
|
-
|
|
7691
|
+
Effect22.mapError(
|
|
7249
7692
|
(error) => new SchemaValidationError({
|
|
7250
7693
|
message: "Invalid response from directories.getByOwners",
|
|
7251
7694
|
context: "findSkillRepos",
|
|
@@ -7259,7 +7702,7 @@ var findSkillRepos = (orgs, dev) => Effect21.gen(function* () {
|
|
|
7259
7702
|
// src/commands/sync/helpers.ts
|
|
7260
7703
|
init_esm_shims();
|
|
7261
7704
|
import { access as access4, readdir as readdir2, readFile as readFile8 } from "fs/promises";
|
|
7262
|
-
import { dirname as dirname3, join as
|
|
7705
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
7263
7706
|
var isNonNpmDependency = (version) => NON_NPM_VERSION_PREFIXES.some((prefix) => version.startsWith(prefix));
|
|
7264
7707
|
var extractDependencies = (pkg) => {
|
|
7265
7708
|
const deps = pkg.dependencies ?? {};
|
|
@@ -7297,15 +7740,15 @@ var GLOB_SUFFIX_PATTERN = /\/\*+$/;
|
|
|
7297
7740
|
var expandGlobPattern = async (rootDir, pattern) => {
|
|
7298
7741
|
if (pattern.endsWith("/*") || pattern.endsWith("/**")) {
|
|
7299
7742
|
const baseDir = pattern.replace(GLOB_SUFFIX_PATTERN, "");
|
|
7300
|
-
const fullPath2 =
|
|
7743
|
+
const fullPath2 = join11(rootDir, baseDir);
|
|
7301
7744
|
try {
|
|
7302
7745
|
const entries = await readdir2(fullPath2, { withFileTypes: true });
|
|
7303
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) =>
|
|
7746
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => join11(fullPath2, entry.name));
|
|
7304
7747
|
} catch {
|
|
7305
7748
|
return [];
|
|
7306
7749
|
}
|
|
7307
7750
|
}
|
|
7308
|
-
const fullPath =
|
|
7751
|
+
const fullPath = join11(rootDir, pattern);
|
|
7309
7752
|
try {
|
|
7310
7753
|
await access4(fullPath);
|
|
7311
7754
|
return [fullPath];
|
|
@@ -7327,7 +7770,7 @@ var discoverPackageJsonFiles = async (rootPath, rootPkg) => {
|
|
|
7327
7770
|
for (const pattern of patterns) {
|
|
7328
7771
|
const dirs = await expandGlobPattern(rootDir, pattern);
|
|
7329
7772
|
for (const dir of dirs) {
|
|
7330
|
-
const pkgPath =
|
|
7773
|
+
const pkgPath = join11(dir, "package.json");
|
|
7331
7774
|
try {
|
|
7332
7775
|
await access4(pkgPath);
|
|
7333
7776
|
packageJsonPaths.push(pkgPath);
|
|
@@ -7342,9 +7785,9 @@ var discoverPackageJsonFiles = async (rootPath, rootPkg) => {
|
|
|
7342
7785
|
init_esm_shims();
|
|
7343
7786
|
import { exec as exec2 } from "child_process";
|
|
7344
7787
|
import { promisify as promisify2 } from "util";
|
|
7345
|
-
import { Effect as
|
|
7788
|
+
import { Effect as Effect23 } from "effect";
|
|
7346
7789
|
var execAsync2 = promisify2(exec2);
|
|
7347
|
-
var installSingleSkill = (repo, options) =>
|
|
7790
|
+
var installSingleSkill = (repo, options) => Effect23.tryPromise({
|
|
7348
7791
|
try: async () => {
|
|
7349
7792
|
const repoId = `${repo.owner}/${repo.repo}`;
|
|
7350
7793
|
const globalFlag = options.global === true ? " --global" : "";
|
|
@@ -7364,9 +7807,9 @@ var installSingleSkill = (repo, options) => Effect22.tryPromise({
|
|
|
7364
7807
|
});
|
|
7365
7808
|
var installSkills = (repos, options = {}) => {
|
|
7366
7809
|
if (options.dryRun === true) {
|
|
7367
|
-
return
|
|
7810
|
+
return Effect23.succeed(repos.map((r) => `${r.owner}/${r.repo}`));
|
|
7368
7811
|
}
|
|
7369
|
-
return
|
|
7812
|
+
return Effect23.forEach(repos, (repo) => installSingleSkill(repo, options), {
|
|
7370
7813
|
concurrency: 1
|
|
7371
7814
|
});
|
|
7372
7815
|
};
|
|
@@ -7374,9 +7817,9 @@ var installSkills = (repos, options = {}) => {
|
|
|
7374
7817
|
// src/commands/sync/resolve-orgs.ts
|
|
7375
7818
|
init_esm_shims();
|
|
7376
7819
|
import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
|
|
7377
|
-
import { Effect as
|
|
7378
|
-
var resolvePackageOrgs = (packageNames, dev) =>
|
|
7379
|
-
const response = yield*
|
|
7820
|
+
import { Effect as Effect24, Schema as S24 } from "effect";
|
|
7821
|
+
var resolvePackageOrgs = (packageNames, dev) => Effect24.gen(function* () {
|
|
7822
|
+
const response = yield* Effect24.tryPromise({
|
|
7380
7823
|
try: async () => {
|
|
7381
7824
|
const client = new ConvexHttpClient2(getConvexUrl(dev));
|
|
7382
7825
|
return await client.action(api.packageOrg.resolve, {
|
|
@@ -7389,10 +7832,10 @@ var resolvePackageOrgs = (packageNames, dev) => Effect23.gen(function* () {
|
|
|
7389
7832
|
cause: error
|
|
7390
7833
|
})
|
|
7391
7834
|
});
|
|
7392
|
-
const validated = yield*
|
|
7835
|
+
const validated = yield* S24.decodeUnknown(PackageOrgsResponseSchema)(
|
|
7393
7836
|
response
|
|
7394
7837
|
).pipe(
|
|
7395
|
-
|
|
7838
|
+
Effect24.mapError(
|
|
7396
7839
|
(error) => new SchemaValidationError({
|
|
7397
7840
|
message: "Invalid response from packageOrg.resolve",
|
|
7398
7841
|
context: "resolvePackageOrgs",
|
|
@@ -7557,7 +8000,7 @@ var extractAllDependencies = async (state) => {
|
|
|
7557
8000
|
var resolveOrganizations = async (state, isDev) => {
|
|
7558
8001
|
state.spinner = createSpinner("Resolving GitHub organizations...").start();
|
|
7559
8002
|
try {
|
|
7560
|
-
const packageOrgs = await
|
|
8003
|
+
const packageOrgs = await Effect25.runPromise(
|
|
7561
8004
|
resolvePackageOrgs(state.dependencies, isDev)
|
|
7562
8005
|
);
|
|
7563
8006
|
state.orgs = Array.from(
|
|
@@ -7582,7 +8025,7 @@ var resolveOrganizations = async (state, isDev) => {
|
|
|
7582
8025
|
var findRepositories = async (state, isDev) => {
|
|
7583
8026
|
state.spinner = createSpinner("Searching for skill repositories...").start();
|
|
7584
8027
|
try {
|
|
7585
|
-
state.skillRepos = await
|
|
8028
|
+
state.skillRepos = await Effect25.runPromise(
|
|
7586
8029
|
findSkillRepos(state.orgs, isDev)
|
|
7587
8030
|
);
|
|
7588
8031
|
if (state.skillRepos.length === 0) {
|
|
@@ -7604,7 +8047,7 @@ var findRepositories = async (state, isDev) => {
|
|
|
7604
8047
|
var detectProjectAgents = async (state, projectDir) => {
|
|
7605
8048
|
state.spinner = createSpinner("Detecting coding agents...").start();
|
|
7606
8049
|
try {
|
|
7607
|
-
state.detectedAgents = await
|
|
8050
|
+
state.detectedAgents = await Effect25.runPromise(detectAgents(projectDir));
|
|
7608
8051
|
if (state.detectedAgents.length === 0) {
|
|
7609
8052
|
state.spinner.info("No coding agents detected");
|
|
7610
8053
|
} else {
|
|
@@ -7691,7 +8134,7 @@ var installRepositories = async (state, reposToInstall, isGlobal, agents) => {
|
|
|
7691
8134
|
`Installing ${pc19.cyan(repo.owner)}${pc19.dim("/")}${pc19.white(repo.repo)}...`
|
|
7692
8135
|
).start();
|
|
7693
8136
|
try {
|
|
7694
|
-
await
|
|
8137
|
+
await Effect25.runPromise(
|
|
7695
8138
|
installSkills([repo], {
|
|
7696
8139
|
dryRun: false,
|
|
7697
8140
|
global: isGlobal,
|