ferix-code 0.0.2-beta.25 → 0.0.2-beta.26
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 +1 -1
- package/dist/index.js +341 -159
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -775,7 +775,7 @@ declare const decodeLoopConfig: (u: unknown, overrideOptions?: effect_SchemaAST.
|
|
|
775
775
|
} | undefined;
|
|
776
776
|
readonly additionalContext?: string | undefined;
|
|
777
777
|
} | undefined;
|
|
778
|
-
readonly provider?: "claude" | "
|
|
778
|
+
readonly provider?: "claude" | "opencode" | "cursor" | undefined;
|
|
779
779
|
}, effect_ParseResult.ParseError, never>;
|
|
780
780
|
|
|
781
781
|
/**
|
package/dist/index.js
CHANGED
|
@@ -70,18 +70,28 @@ var init_registry = __esm({
|
|
|
70
70
|
|
|
71
71
|
// src/index.ts
|
|
72
72
|
init_esm_shims();
|
|
73
|
-
import { access as
|
|
74
|
-
import { dirname as dirname2, join as
|
|
73
|
+
import { access as access4, readdir as readdir2, readFile as readFile7 } from "fs/promises";
|
|
74
|
+
import { dirname as dirname2, join as join8, resolve } from "path";
|
|
75
|
+
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
|
75
76
|
|
|
76
77
|
// ../sync/dist/index.js
|
|
77
78
|
init_esm_shims();
|
|
78
79
|
|
|
79
|
-
// ../sync/dist/chunk-
|
|
80
|
+
// ../sync/dist/chunk-5E4WZI4D.js
|
|
80
81
|
init_esm_shims();
|
|
81
82
|
|
|
82
|
-
// ../sync/dist/chunk-
|
|
83
|
+
// ../sync/dist/chunk-BNNEGPUU.js
|
|
83
84
|
init_esm_shims();
|
|
84
85
|
import { Schema as S } from "effect";
|
|
86
|
+
var AgentNameSchema = S.Literal(
|
|
87
|
+
"opencode",
|
|
88
|
+
"claude-code",
|
|
89
|
+
"cursor",
|
|
90
|
+
"cline",
|
|
91
|
+
"codex",
|
|
92
|
+
"openhands",
|
|
93
|
+
"windsurf"
|
|
94
|
+
);
|
|
85
95
|
var PackageNameSchema = S.String.pipe(
|
|
86
96
|
S.pattern(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/),
|
|
87
97
|
S.brand("PackageName")
|
|
@@ -132,7 +142,8 @@ var SyncOptionsSchema = S.Struct({
|
|
|
132
142
|
});
|
|
133
143
|
var InstallOptionsSchema = S.Struct({
|
|
134
144
|
dryRun: S.optional(S.Boolean),
|
|
135
|
-
global: S.optional(S.Boolean)
|
|
145
|
+
global: S.optional(S.Boolean),
|
|
146
|
+
agents: S.optional(S.Array(AgentNameSchema))
|
|
136
147
|
});
|
|
137
148
|
var SyncResultSchema = S.Struct({
|
|
138
149
|
dependencies: S.Array(S.String),
|
|
@@ -6082,7 +6093,7 @@ var wrapper_default = import_websocket.default;
|
|
|
6082
6093
|
var nodeWebSocket = wrapper_default;
|
|
6083
6094
|
setDefaultWebSocketConstructor(nodeWebSocket);
|
|
6084
6095
|
|
|
6085
|
-
// ../sync/dist/chunk-
|
|
6096
|
+
// ../sync/dist/chunk-5E4WZI4D.js
|
|
6086
6097
|
import { Effect, Schema as S2 } from "effect";
|
|
6087
6098
|
var findSkillRepos = (orgs, dev) => Effect.gen(function* () {
|
|
6088
6099
|
const response = yield* Effect.tryPromise({
|
|
@@ -6112,7 +6123,7 @@ var findSkillRepos = (orgs, dev) => Effect.gen(function* () {
|
|
|
6112
6123
|
return validated;
|
|
6113
6124
|
});
|
|
6114
6125
|
|
|
6115
|
-
// ../sync/dist/chunk-
|
|
6126
|
+
// ../sync/dist/chunk-EDGIQ4IK.js
|
|
6116
6127
|
init_esm_shims();
|
|
6117
6128
|
import { exec } from "child_process";
|
|
6118
6129
|
import { promisify } from "util";
|
|
@@ -6122,7 +6133,8 @@ var installSingleSkill = (repo, options) => Effect2.tryPromise({
|
|
|
6122
6133
|
try: async () => {
|
|
6123
6134
|
const repoId = `${repo.owner}/${repo.repo}`;
|
|
6124
6135
|
const globalFlag = options.global === true ? " --global" : "";
|
|
6125
|
-
const
|
|
6136
|
+
const agentFlag = options.agents && options.agents.length > 0 ? ` --agent ${options.agents.join(" ")}` : "";
|
|
6137
|
+
const command = `npx skills add ${repoId}${globalFlag}${agentFlag} --yes`;
|
|
6126
6138
|
await execAsync(command);
|
|
6127
6139
|
return repoId;
|
|
6128
6140
|
},
|
|
@@ -6144,7 +6156,7 @@ var installSkills = (repos, options = {}) => {
|
|
|
6144
6156
|
});
|
|
6145
6157
|
};
|
|
6146
6158
|
|
|
6147
|
-
// ../sync/dist/chunk-
|
|
6159
|
+
// ../sync/dist/chunk-C6TYE4HI.js
|
|
6148
6160
|
init_esm_shims();
|
|
6149
6161
|
import { Effect as Effect3, Schema as S3 } from "effect";
|
|
6150
6162
|
var resolvePackageOrgs = (packageNames, dev) => Effect3.gen(function* () {
|
|
@@ -6176,18 +6188,60 @@ var resolvePackageOrgs = (packageNames, dev) => Effect3.gen(function* () {
|
|
|
6176
6188
|
});
|
|
6177
6189
|
|
|
6178
6190
|
// ../sync/dist/index.js
|
|
6179
|
-
import { Effect as
|
|
6191
|
+
import { Effect as Effect22, Schema as S4 } from "effect";
|
|
6192
|
+
import { access } from "fs/promises";
|
|
6193
|
+
import { join } from "path";
|
|
6194
|
+
import { Effect as Effect4 } from "effect";
|
|
6195
|
+
var AGENT_DIRECTORIES = {
|
|
6196
|
+
opencode: [".opencode"],
|
|
6197
|
+
"claude-code": [".claude"],
|
|
6198
|
+
cursor: [".cursor"],
|
|
6199
|
+
cline: [".cline"],
|
|
6200
|
+
codex: [".codex"],
|
|
6201
|
+
openhands: [".openhands"],
|
|
6202
|
+
windsurf: [".windsurf"]
|
|
6203
|
+
};
|
|
6204
|
+
var SUPPORTED_AGENTS = Object.keys(
|
|
6205
|
+
AGENT_DIRECTORIES
|
|
6206
|
+
);
|
|
6207
|
+
var directoryExists = (dirPath) => Effect4.tryPromise({
|
|
6208
|
+
try: async () => {
|
|
6209
|
+
await access(dirPath);
|
|
6210
|
+
return true;
|
|
6211
|
+
},
|
|
6212
|
+
catch: () => false
|
|
6213
|
+
}).pipe(Effect4.catchAll(() => Effect4.succeed(false)));
|
|
6214
|
+
var isAgentPresent = (projectDir, agent) => {
|
|
6215
|
+
const directories = AGENT_DIRECTORIES[agent];
|
|
6216
|
+
return Effect4.forEach(
|
|
6217
|
+
directories,
|
|
6218
|
+
(dir) => directoryExists(join(projectDir, dir))
|
|
6219
|
+
).pipe(Effect4.map((results) => results.some((exists) => exists)));
|
|
6220
|
+
};
|
|
6221
|
+
var detectAgents = (projectDir) => Effect4.forEach(
|
|
6222
|
+
SUPPORTED_AGENTS,
|
|
6223
|
+
(agent) => isAgentPresent(projectDir, agent).pipe(
|
|
6224
|
+
Effect4.map((present) => present ? agent : null)
|
|
6225
|
+
)
|
|
6226
|
+
).pipe(
|
|
6227
|
+
Effect4.map(
|
|
6228
|
+
(results) => results.filter((agent) => agent !== null)
|
|
6229
|
+
)
|
|
6230
|
+
);
|
|
6231
|
+
var validateAgentNames = (agents) => agents.filter(
|
|
6232
|
+
(agent) => !SUPPORTED_AGENTS.includes(agent)
|
|
6233
|
+
);
|
|
6180
6234
|
|
|
6181
6235
|
// src/index.ts
|
|
6182
6236
|
import { Command } from "commander";
|
|
6183
|
-
import { Effect as
|
|
6237
|
+
import { Effect as Effect30 } from "effect";
|
|
6184
6238
|
import ora from "ora";
|
|
6185
6239
|
import pc17 from "picocolors";
|
|
6186
6240
|
|
|
6187
6241
|
// package.json
|
|
6188
6242
|
var package_default = {
|
|
6189
6243
|
name: "ferix-code",
|
|
6190
|
-
version: "0.0.2-beta.
|
|
6244
|
+
version: "0.0.2-beta.26",
|
|
6191
6245
|
description: "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
6192
6246
|
type: "module",
|
|
6193
6247
|
bin: {
|
|
@@ -6206,6 +6260,7 @@ var package_default = {
|
|
|
6206
6260
|
bump: "npm version prerelease --preid=beta --workspaces=false && bun run build && bun publish --tag beta"
|
|
6207
6261
|
},
|
|
6208
6262
|
dependencies: {
|
|
6263
|
+
"@clack/prompts": "catalog:",
|
|
6209
6264
|
commander: "^14.0.0",
|
|
6210
6265
|
effect: "^3.19.15",
|
|
6211
6266
|
"human-id": "^4.1.3",
|
|
@@ -6236,7 +6291,7 @@ var package_default = {
|
|
|
6236
6291
|
|
|
6237
6292
|
// src/program.ts
|
|
6238
6293
|
init_esm_shims();
|
|
6239
|
-
import { Effect as
|
|
6294
|
+
import { Effect as Effect29, Stream as Stream10 } from "effect";
|
|
6240
6295
|
|
|
6241
6296
|
// src/consumers/index.ts
|
|
6242
6297
|
init_esm_shims();
|
|
@@ -8690,14 +8745,14 @@ import { Layer as Layer14 } from "effect";
|
|
|
8690
8745
|
init_esm_shims();
|
|
8691
8746
|
import { exec as exec2 } from "child_process";
|
|
8692
8747
|
import {
|
|
8693
|
-
access,
|
|
8748
|
+
access as access2,
|
|
8694
8749
|
appendFile,
|
|
8695
8750
|
copyFile,
|
|
8696
8751
|
mkdir,
|
|
8697
8752
|
readFile,
|
|
8698
8753
|
rm
|
|
8699
8754
|
} from "fs/promises";
|
|
8700
|
-
import { dirname, join } from "path";
|
|
8755
|
+
import { dirname, join as join2 } from "path";
|
|
8701
8756
|
import { promisify as promisify2 } from "util";
|
|
8702
8757
|
import { Effect as Effect8, Layer } from "effect";
|
|
8703
8758
|
|
|
@@ -8743,7 +8798,7 @@ var WORKTREES_DIR = ".ferix/worktrees";
|
|
|
8743
8798
|
var GITDIR_REGEX = /^gitdir:\s*(.+)$/m;
|
|
8744
8799
|
var BRANCH_PREFIX = "ferix";
|
|
8745
8800
|
function getWorktreeDir(sessionId) {
|
|
8746
|
-
return
|
|
8801
|
+
return join2(process.cwd(), WORKTREES_DIR, sessionId);
|
|
8747
8802
|
}
|
|
8748
8803
|
function getBranchName(sessionId) {
|
|
8749
8804
|
return `${BRANCH_PREFIX}/${sessionId}`;
|
|
@@ -8764,10 +8819,10 @@ function gitExec(command, cwd) {
|
|
|
8764
8819
|
}
|
|
8765
8820
|
});
|
|
8766
8821
|
}
|
|
8767
|
-
function
|
|
8822
|
+
function directoryExists2(dirPath) {
|
|
8768
8823
|
return Effect8.tryPromise({
|
|
8769
8824
|
try: async () => {
|
|
8770
|
-
await
|
|
8825
|
+
await access2(dirPath);
|
|
8771
8826
|
return true;
|
|
8772
8827
|
},
|
|
8773
8828
|
catch: () => new Error("Directory does not exist")
|
|
@@ -8807,8 +8862,8 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
8807
8862
|
return;
|
|
8808
8863
|
}
|
|
8809
8864
|
for (const file of untrackedFiles) {
|
|
8810
|
-
const srcPath =
|
|
8811
|
-
const destPath =
|
|
8865
|
+
const srcPath = join2(process.cwd(), file);
|
|
8866
|
+
const destPath = join2(worktreeDir, file);
|
|
8812
8867
|
yield* Effect8.tryPromise({
|
|
8813
8868
|
try: async () => {
|
|
8814
8869
|
await mkdir(dirname(destPath), { recursive: true });
|
|
@@ -8822,7 +8877,7 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
8822
8877
|
}
|
|
8823
8878
|
yield* Effect8.tryPromise({
|
|
8824
8879
|
try: async () => {
|
|
8825
|
-
const gitFilePath =
|
|
8880
|
+
const gitFilePath = join2(worktreeDir, ".git");
|
|
8826
8881
|
const gitFileContent = await readFile(gitFilePath, "utf-8");
|
|
8827
8882
|
const gitDirMatch = gitFileContent.match(GITDIR_REGEX);
|
|
8828
8883
|
const gitDirPath = gitDirMatch?.[1]?.trim();
|
|
@@ -8830,7 +8885,7 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
8830
8885
|
return;
|
|
8831
8886
|
}
|
|
8832
8887
|
const gitDir = gitDirPath;
|
|
8833
|
-
const excludePath =
|
|
8888
|
+
const excludePath = join2(gitDir, "info", "exclude");
|
|
8834
8889
|
await mkdir(dirname(excludePath), { recursive: true });
|
|
8835
8890
|
const excludeContent = "\n# Untracked files copied from main worktree (auto-generated by ferix)\n" + untrackedFiles.join("\n") + "\n";
|
|
8836
8891
|
await appendFile(excludePath, excludeContent);
|
|
@@ -8846,7 +8901,7 @@ var make = {
|
|
|
8846
8901
|
createWorktree: (sessionId, baseBranch) => Effect8.gen(function* () {
|
|
8847
8902
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
8848
8903
|
const branchName = getBranchName(sessionId);
|
|
8849
|
-
const worktreesBase =
|
|
8904
|
+
const worktreesBase = join2(process.cwd(), WORKTREES_DIR);
|
|
8850
8905
|
yield* Effect8.tryPromise({
|
|
8851
8906
|
try: () => mkdir(worktreesBase, { recursive: true }),
|
|
8852
8907
|
catch: (error) => new GitError({
|
|
@@ -8855,7 +8910,7 @@ var make = {
|
|
|
8855
8910
|
cause: error
|
|
8856
8911
|
})
|
|
8857
8912
|
});
|
|
8858
|
-
const exists = yield*
|
|
8913
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
8859
8914
|
if (exists) {
|
|
8860
8915
|
return worktreeDir;
|
|
8861
8916
|
}
|
|
@@ -8876,7 +8931,7 @@ var make = {
|
|
|
8876
8931
|
removeWorktree: (sessionId) => Effect8.gen(function* () {
|
|
8877
8932
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
8878
8933
|
const branchName = getBranchName(sessionId);
|
|
8879
|
-
const exists = yield*
|
|
8934
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
8880
8935
|
if (!exists) {
|
|
8881
8936
|
return;
|
|
8882
8937
|
}
|
|
@@ -8909,7 +8964,7 @@ var make = {
|
|
|
8909
8964
|
}),
|
|
8910
8965
|
removeWorktreeKeepBranch: (sessionId) => Effect8.gen(function* () {
|
|
8911
8966
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
8912
|
-
const exists = yield*
|
|
8967
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
8913
8968
|
if (!exists) {
|
|
8914
8969
|
return;
|
|
8915
8970
|
}
|
|
@@ -8939,12 +8994,12 @@ var make = {
|
|
|
8939
8994
|
}),
|
|
8940
8995
|
getWorktreePath: (sessionId) => Effect8.gen(function* () {
|
|
8941
8996
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
8942
|
-
const exists = yield*
|
|
8997
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
8943
8998
|
return exists ? worktreeDir : void 0;
|
|
8944
8999
|
}),
|
|
8945
9000
|
commitChanges: (sessionId, message) => Effect8.gen(function* () {
|
|
8946
9001
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
8947
|
-
const exists = yield*
|
|
9002
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
8948
9003
|
if (!exists) {
|
|
8949
9004
|
return yield* Effect8.fail(
|
|
8950
9005
|
new GitError({
|
|
@@ -9000,7 +9055,7 @@ var make = {
|
|
|
9000
9055
|
}),
|
|
9001
9056
|
pushBranch: (sessionId) => Effect8.gen(function* () {
|
|
9002
9057
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9003
|
-
const exists = yield*
|
|
9058
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9004
9059
|
if (!exists) {
|
|
9005
9060
|
return yield* Effect8.fail(
|
|
9006
9061
|
new GitError({
|
|
@@ -9021,7 +9076,7 @@ var make = {
|
|
|
9021
9076
|
}),
|
|
9022
9077
|
createPR: (sessionId, title, body, baseBranch) => Effect8.gen(function* () {
|
|
9023
9078
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9024
|
-
const exists = yield*
|
|
9079
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9025
9080
|
if (!exists) {
|
|
9026
9081
|
return yield* Effect8.fail(
|
|
9027
9082
|
new GitError({
|
|
@@ -9059,7 +9114,7 @@ var make = {
|
|
|
9059
9114
|
renameBranch: (sessionId, displayName) => Effect8.gen(function* () {
|
|
9060
9115
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9061
9116
|
const oldBranchName = getBranchName(sessionId);
|
|
9062
|
-
const exists = yield*
|
|
9117
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9063
9118
|
if (!exists) {
|
|
9064
9119
|
return yield* Effect8.fail(
|
|
9065
9120
|
new GitError({
|
|
@@ -9223,7 +9278,7 @@ var MemoryGit = {
|
|
|
9223
9278
|
// src/layers/guardrails/file-system.ts
|
|
9224
9279
|
init_esm_shims();
|
|
9225
9280
|
import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
|
|
9226
|
-
import { join as
|
|
9281
|
+
import { join as join3 } from "path";
|
|
9227
9282
|
import { DateTime, Effect as Effect10, Layer as Layer3 } from "effect";
|
|
9228
9283
|
|
|
9229
9284
|
// src/domain/index.ts
|
|
@@ -10245,10 +10300,10 @@ function ensureDir(dirPath) {
|
|
|
10245
10300
|
}).pipe(Effect10.asVoid);
|
|
10246
10301
|
}
|
|
10247
10302
|
function getSessionDir(sessionId) {
|
|
10248
|
-
return
|
|
10303
|
+
return join3(process.cwd(), PLANS_DIR, sessionId);
|
|
10249
10304
|
}
|
|
10250
10305
|
function getGuardrailsPath(sessionId) {
|
|
10251
|
-
return
|
|
10306
|
+
return join3(getSessionDir(sessionId), "guardrails.json");
|
|
10252
10307
|
}
|
|
10253
10308
|
function serializeGuardrails(guardrails) {
|
|
10254
10309
|
return JSON.stringify(guardrails, null, 2);
|
|
@@ -10894,8 +10949,8 @@ function createProviderLayer2(name) {
|
|
|
10894
10949
|
|
|
10895
10950
|
// src/layers/plan/file-system.ts
|
|
10896
10951
|
init_esm_shims();
|
|
10897
|
-
import { access as
|
|
10898
|
-
import { join as
|
|
10952
|
+
import { access as access3, mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
10953
|
+
import { join as join4 } from "path";
|
|
10899
10954
|
import { Effect as Effect16, Layer as Layer7 } from "effect";
|
|
10900
10955
|
|
|
10901
10956
|
// src/services/plan-store.ts
|
|
@@ -10917,10 +10972,10 @@ function ensureDir2(dirPath) {
|
|
|
10917
10972
|
}).pipe(Effect16.asVoid);
|
|
10918
10973
|
}
|
|
10919
10974
|
function getSessionDir2(sessionId) {
|
|
10920
|
-
return
|
|
10975
|
+
return join4(process.cwd(), PLANS_DIR2, sessionId);
|
|
10921
10976
|
}
|
|
10922
10977
|
function getPlanPath(sessionId, planId) {
|
|
10923
|
-
return
|
|
10978
|
+
return join4(getSessionDir2(sessionId), `${planId}.json`);
|
|
10924
10979
|
}
|
|
10925
10980
|
function generatePlanId(taskNumber) {
|
|
10926
10981
|
return PlanId(`task-${taskNumber}`);
|
|
@@ -10999,7 +11054,7 @@ var make3 = {
|
|
|
10999
11054
|
}
|
|
11000
11055
|
const sessionDirs = yield* Effect16.tryPromise({
|
|
11001
11056
|
try: async () => {
|
|
11002
|
-
const plansDir =
|
|
11057
|
+
const plansDir = join4(process.cwd(), PLANS_DIR2);
|
|
11003
11058
|
const dirs = await readdir(plansDir);
|
|
11004
11059
|
return dirs;
|
|
11005
11060
|
},
|
|
@@ -11013,7 +11068,7 @@ var make3 = {
|
|
|
11013
11068
|
const planPath = getPlanPath(sid, planId);
|
|
11014
11069
|
const exists = yield* Effect16.tryPromise({
|
|
11015
11070
|
try: async () => {
|
|
11016
|
-
await
|
|
11071
|
+
await access3(planPath);
|
|
11017
11072
|
return true;
|
|
11018
11073
|
},
|
|
11019
11074
|
catch: () => new PlanStoreError({
|
|
@@ -11165,7 +11220,7 @@ var MemoryPlan = {
|
|
|
11165
11220
|
// src/layers/progress/file-system.ts
|
|
11166
11221
|
init_esm_shims();
|
|
11167
11222
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
11168
|
-
import { join as
|
|
11223
|
+
import { join as join5 } from "path";
|
|
11169
11224
|
import { DateTime as DateTime3, Effect as Effect18, Layer as Layer9 } from "effect";
|
|
11170
11225
|
|
|
11171
11226
|
// src/services/progress-store.ts
|
|
@@ -11187,10 +11242,10 @@ function ensureDir3(dirPath) {
|
|
|
11187
11242
|
}).pipe(Effect18.asVoid);
|
|
11188
11243
|
}
|
|
11189
11244
|
function getSessionDir3(sessionId) {
|
|
11190
|
-
return
|
|
11245
|
+
return join5(process.cwd(), PLANS_DIR3, sessionId);
|
|
11191
11246
|
}
|
|
11192
11247
|
function getProgressPath(sessionId) {
|
|
11193
|
-
return
|
|
11248
|
+
return join5(getSessionDir3(sessionId), "progress.json");
|
|
11194
11249
|
}
|
|
11195
11250
|
function serializeProgress(progress) {
|
|
11196
11251
|
return JSON.stringify(progress, null, 2);
|
|
@@ -11369,7 +11424,7 @@ var MemoryProgress = {
|
|
|
11369
11424
|
// src/layers/session/file-system.ts
|
|
11370
11425
|
init_esm_shims();
|
|
11371
11426
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
11372
|
-
import { join as
|
|
11427
|
+
import { join as join6 } from "path";
|
|
11373
11428
|
import { DateTime as DateTime5, Effect as Effect20, Layer as Layer11 } from "effect";
|
|
11374
11429
|
import { humanId } from "human-id";
|
|
11375
11430
|
|
|
@@ -11396,7 +11451,7 @@ function ensureDir4(dirPath) {
|
|
|
11396
11451
|
}).pipe(Effect20.asVoid);
|
|
11397
11452
|
}
|
|
11398
11453
|
function getSessionPath(sessionId) {
|
|
11399
|
-
return
|
|
11454
|
+
return join6(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
|
|
11400
11455
|
}
|
|
11401
11456
|
function serializeSession(session) {
|
|
11402
11457
|
return JSON.stringify(session, null, 2);
|
|
@@ -11425,7 +11480,7 @@ function deserializeSession(json) {
|
|
|
11425
11480
|
}
|
|
11426
11481
|
var make5 = {
|
|
11427
11482
|
create: (originalTask) => Effect20.gen(function* () {
|
|
11428
|
-
const sessionsDir =
|
|
11483
|
+
const sessionsDir = join6(process.cwd(), SESSIONS_DIR);
|
|
11429
11484
|
yield* ensureDir4(sessionsDir);
|
|
11430
11485
|
const now = yield* DateTime5.now;
|
|
11431
11486
|
const timestampMs = DateTime5.toEpochMillis(now);
|
|
@@ -11544,7 +11599,7 @@ var MemorySession = {
|
|
|
11544
11599
|
|
|
11545
11600
|
// src/layers/signal/ferix-parser.ts
|
|
11546
11601
|
init_esm_shims();
|
|
11547
|
-
import { Effect as
|
|
11602
|
+
import { Effect as Effect23, Layer as Layer13, Ref as Ref8 } from "effect";
|
|
11548
11603
|
|
|
11549
11604
|
// src/services/signal-parser.ts
|
|
11550
11605
|
init_esm_shims();
|
|
@@ -12059,10 +12114,10 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
12059
12114
|
// src/layers/signal/ferix-parser.ts
|
|
12060
12115
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
12061
12116
|
function createAccumulatorImpl() {
|
|
12062
|
-
return
|
|
12117
|
+
return Effect23.gen(function* () {
|
|
12063
12118
|
const chunksRef = yield* Ref8.make([]);
|
|
12064
12119
|
const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
|
|
12065
|
-
const feed = (text) =>
|
|
12120
|
+
const feed = (text) => Effect23.gen(function* () {
|
|
12066
12121
|
const chunks = yield* Ref8.get(chunksRef);
|
|
12067
12122
|
chunks.push(text);
|
|
12068
12123
|
const buffer = chunks.join("");
|
|
@@ -12090,7 +12145,7 @@ function createAccumulatorImpl() {
|
|
|
12090
12145
|
yield* Ref8.set(emittedRef, emitted);
|
|
12091
12146
|
return newSignals;
|
|
12092
12147
|
});
|
|
12093
|
-
const flush = () =>
|
|
12148
|
+
const flush = () => Effect23.gen(function* () {
|
|
12094
12149
|
const chunks = yield* Ref8.get(chunksRef);
|
|
12095
12150
|
const buffer = chunks.join("");
|
|
12096
12151
|
yield* Ref8.set(chunksRef, []);
|
|
@@ -12106,7 +12161,7 @@ function createAccumulatorImpl() {
|
|
|
12106
12161
|
});
|
|
12107
12162
|
}
|
|
12108
12163
|
var make6 = {
|
|
12109
|
-
parse: (text) =>
|
|
12164
|
+
parse: (text) => Effect23.succeed(signalSpecRegistry.parseAll(text)),
|
|
12110
12165
|
createAccumulator: createAccumulatorImpl
|
|
12111
12166
|
};
|
|
12112
12167
|
var Live15 = Layer13.succeed(SignalParser, make6);
|
|
@@ -12162,41 +12217,41 @@ init_esm_shims();
|
|
|
12162
12217
|
|
|
12163
12218
|
// src/orchestrator/loop.ts
|
|
12164
12219
|
init_esm_shims();
|
|
12165
|
-
import { DateTime as DateTime10, Effect as
|
|
12220
|
+
import { DateTime as DateTime10, Effect as Effect28, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
|
|
12166
12221
|
|
|
12167
12222
|
// src/orchestrator/discovery.ts
|
|
12168
12223
|
init_esm_shims();
|
|
12169
|
-
import { DateTime as DateTime8, Effect as
|
|
12224
|
+
import { DateTime as DateTime8, Effect as Effect26, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
|
|
12170
12225
|
|
|
12171
12226
|
// src/layers/plan/task-generation.ts
|
|
12172
12227
|
init_esm_shims();
|
|
12173
12228
|
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
12174
|
-
import { join as
|
|
12175
|
-
import { Effect as
|
|
12229
|
+
import { join as join7 } from "path";
|
|
12230
|
+
import { Effect as Effect24 } from "effect";
|
|
12176
12231
|
var PLANS_DIR4 = ".ferix/plans";
|
|
12177
12232
|
function ensureDir5(dirPath) {
|
|
12178
|
-
return
|
|
12233
|
+
return Effect24.tryPromise({
|
|
12179
12234
|
try: () => mkdir6(dirPath, { recursive: true }),
|
|
12180
12235
|
catch: (error) => new PlanStoreError({
|
|
12181
12236
|
message: `Failed to create directory: ${dirPath}`,
|
|
12182
12237
|
operation: "create",
|
|
12183
12238
|
cause: error
|
|
12184
12239
|
})
|
|
12185
|
-
}).pipe(
|
|
12240
|
+
}).pipe(Effect24.asVoid);
|
|
12186
12241
|
}
|
|
12187
12242
|
function getSessionDir4(sessionId) {
|
|
12188
|
-
return
|
|
12243
|
+
return join7(process.cwd(), PLANS_DIR4, sessionId);
|
|
12189
12244
|
}
|
|
12190
12245
|
function getTasksMdPath(sessionId) {
|
|
12191
|
-
return
|
|
12246
|
+
return join7(getSessionDir4(sessionId), "tasks.md");
|
|
12192
12247
|
}
|
|
12193
12248
|
function writeTasksMd(sessionId, tasks) {
|
|
12194
|
-
return
|
|
12249
|
+
return Effect24.gen(function* () {
|
|
12195
12250
|
const sessionDir = getSessionDir4(sessionId);
|
|
12196
12251
|
yield* ensureDir5(sessionDir);
|
|
12197
12252
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
12198
12253
|
const content = formatTasksMd(tasks);
|
|
12199
|
-
yield*
|
|
12254
|
+
yield* Effect24.tryPromise({
|
|
12200
12255
|
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
12201
12256
|
catch: (error) => new PlanStoreError({
|
|
12202
12257
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
@@ -12388,7 +12443,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
12388
12443
|
|
|
12389
12444
|
// src/orchestrator/plan-updates.ts
|
|
12390
12445
|
init_esm_shims();
|
|
12391
|
-
import { DateTime as DateTime7, Effect as
|
|
12446
|
+
import { DateTime as DateTime7, Effect as Effect25, Ref as Ref9 } from "effect";
|
|
12392
12447
|
|
|
12393
12448
|
// src/orchestrator/plan-updates/index.ts
|
|
12394
12449
|
init_esm_shims();
|
|
@@ -12710,9 +12765,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
12710
12765
|
tasks: plan.tasks
|
|
12711
12766
|
}) : planStore.update(plan.id, plan);
|
|
12712
12767
|
return storeOp.pipe(
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
(error) =>
|
|
12768
|
+
Effect25.map(() => null),
|
|
12769
|
+
Effect25.catchAll(
|
|
12770
|
+
(error) => Effect25.succeed({
|
|
12716
12771
|
_tag: "PlanUpdateFailed",
|
|
12717
12772
|
operation,
|
|
12718
12773
|
error: error.message,
|
|
@@ -12722,7 +12777,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
12722
12777
|
);
|
|
12723
12778
|
}
|
|
12724
12779
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
12725
|
-
return
|
|
12780
|
+
return Effect25.gen(function* () {
|
|
12726
12781
|
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
12727
12782
|
const now = yield* DateTime7.now;
|
|
12728
12783
|
const timestamp = DateTime7.formatIso(now);
|
|
@@ -12744,7 +12799,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
12744
12799
|
});
|
|
12745
12800
|
}
|
|
12746
12801
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
12747
|
-
return
|
|
12802
|
+
return Effect25.gen(function* () {
|
|
12748
12803
|
const state = yield* Ref9.get(persistenceStateRef);
|
|
12749
12804
|
if (!(state.dirty && state.pendingOperation)) {
|
|
12750
12805
|
return [];
|
|
@@ -13068,12 +13123,12 @@ function areAllTasksComplete(plan) {
|
|
|
13068
13123
|
|
|
13069
13124
|
// src/orchestrator/discovery.ts
|
|
13070
13125
|
function processTextSignals(signalParser, text, context) {
|
|
13071
|
-
return
|
|
13126
|
+
return Effect26.gen(function* () {
|
|
13072
13127
|
const events = [];
|
|
13073
13128
|
const parsedSignals = [];
|
|
13074
13129
|
const signals = yield* signalParser.parse(text).pipe(
|
|
13075
|
-
|
|
13076
|
-
(error) =>
|
|
13130
|
+
Effect26.tapError(
|
|
13131
|
+
(error) => Effect26.logDebug(
|
|
13077
13132
|
"Signal parsing failed, continuing with empty signals",
|
|
13078
13133
|
{
|
|
13079
13134
|
error: String(error),
|
|
@@ -13081,7 +13136,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
13081
13136
|
}
|
|
13082
13137
|
)
|
|
13083
13138
|
),
|
|
13084
|
-
|
|
13139
|
+
Effect26.orElseSucceed(() => [])
|
|
13085
13140
|
);
|
|
13086
13141
|
for (const signal of signals) {
|
|
13087
13142
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -13091,7 +13146,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
13091
13146
|
});
|
|
13092
13147
|
}
|
|
13093
13148
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
13094
|
-
return
|
|
13149
|
+
return Effect26.gen(function* () {
|
|
13095
13150
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
13096
13151
|
const events = [domainEvent];
|
|
13097
13152
|
const allSignals = [];
|
|
@@ -13136,7 +13191,7 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
13136
13191
|
}
|
|
13137
13192
|
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath, onSessionName) {
|
|
13138
13193
|
return Stream7.unwrap(
|
|
13139
|
-
|
|
13194
|
+
Effect26.gen(function* () {
|
|
13140
13195
|
const startTimeUtc = yield* DateTime8.now;
|
|
13141
13196
|
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
13142
13197
|
const persistenceStateRef = yield* Ref10.make({
|
|
@@ -13164,7 +13219,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13164
13219
|
),
|
|
13165
13220
|
Stream7.flatMap(
|
|
13166
13221
|
(llmEvent) => Stream7.unwrap(
|
|
13167
|
-
|
|
13222
|
+
Effect26.gen(function* () {
|
|
13168
13223
|
const now = yield* DateTime8.now;
|
|
13169
13224
|
const context = {
|
|
13170
13225
|
timestamp: DateTime8.toEpochMillis(now)
|
|
@@ -13204,7 +13259,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13204
13259
|
)
|
|
13205
13260
|
);
|
|
13206
13261
|
const completionStream = Stream7.fromEffect(
|
|
13207
|
-
|
|
13262
|
+
Effect26.gen(function* () {
|
|
13208
13263
|
const persistEvents = yield* flushPlanPersistence(
|
|
13209
13264
|
planStore,
|
|
13210
13265
|
currentPlanRef,
|
|
@@ -13215,12 +13270,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13215
13270
|
if (plan && plan.tasks.length > 0) {
|
|
13216
13271
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
13217
13272
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
13218
|
-
|
|
13219
|
-
(error) =>
|
|
13273
|
+
Effect26.tapError(
|
|
13274
|
+
(error) => Effect26.logDebug("Failed to write tasks.md, continuing", {
|
|
13220
13275
|
error: String(error)
|
|
13221
13276
|
})
|
|
13222
13277
|
),
|
|
13223
|
-
|
|
13278
|
+
Effect26.orElseSucceed(() => void 0)
|
|
13224
13279
|
);
|
|
13225
13280
|
}
|
|
13226
13281
|
const endTimeUtc = yield* DateTime8.now;
|
|
@@ -13230,8 +13285,8 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13230
13285
|
if (capturedName) {
|
|
13231
13286
|
if (onSessionName) {
|
|
13232
13287
|
yield* onSessionName(capturedName).pipe(
|
|
13233
|
-
|
|
13234
|
-
(error) =>
|
|
13288
|
+
Effect26.tapError(
|
|
13289
|
+
(error) => Effect26.logDebug(
|
|
13235
13290
|
"Failed to handle session name, continuing",
|
|
13236
13291
|
{
|
|
13237
13292
|
error: String(error),
|
|
@@ -13239,7 +13294,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13239
13294
|
}
|
|
13240
13295
|
)
|
|
13241
13296
|
),
|
|
13242
|
-
|
|
13297
|
+
Effect26.orElseSucceed(() => void 0)
|
|
13243
13298
|
);
|
|
13244
13299
|
}
|
|
13245
13300
|
const sessionNameEvent = {
|
|
@@ -13271,15 +13326,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13271
13326
|
|
|
13272
13327
|
// src/orchestrator/iteration.ts
|
|
13273
13328
|
init_esm_shims();
|
|
13274
|
-
import { DateTime as DateTime9, Effect as
|
|
13329
|
+
import { DateTime as DateTime9, Effect as Effect27, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
|
|
13275
13330
|
function processTextSignals2(signalParser, text, context) {
|
|
13276
|
-
return
|
|
13331
|
+
return Effect27.gen(function* () {
|
|
13277
13332
|
const events = [];
|
|
13278
13333
|
let completed = false;
|
|
13279
13334
|
const parsedSignals = [];
|
|
13280
13335
|
const signals = yield* signalParser.parse(text).pipe(
|
|
13281
|
-
|
|
13282
|
-
(error) =>
|
|
13336
|
+
Effect27.tapError(
|
|
13337
|
+
(error) => Effect27.logDebug(
|
|
13283
13338
|
"Signal parsing failed, continuing with empty signals",
|
|
13284
13339
|
{
|
|
13285
13340
|
error: String(error),
|
|
@@ -13287,7 +13342,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
13287
13342
|
}
|
|
13288
13343
|
)
|
|
13289
13344
|
),
|
|
13290
|
-
|
|
13345
|
+
Effect27.orElseSucceed(() => [])
|
|
13291
13346
|
);
|
|
13292
13347
|
for (const signal of signals) {
|
|
13293
13348
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -13300,7 +13355,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
13300
13355
|
});
|
|
13301
13356
|
}
|
|
13302
13357
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
13303
|
-
return
|
|
13358
|
+
return Effect27.gen(function* () {
|
|
13304
13359
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
13305
13360
|
const events = [domainEvent];
|
|
13306
13361
|
let completed = false;
|
|
@@ -13333,7 +13388,7 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
13333
13388
|
}
|
|
13334
13389
|
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
13335
13390
|
return Stream8.unwrap(
|
|
13336
|
-
|
|
13391
|
+
Effect27.gen(function* () {
|
|
13337
13392
|
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
13338
13393
|
const persistenceStateRef = yield* Ref11.make({
|
|
13339
13394
|
dirty: false,
|
|
@@ -13356,7 +13411,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13356
13411
|
),
|
|
13357
13412
|
Stream8.flatMap(
|
|
13358
13413
|
(llmEvent) => Stream8.unwrap(
|
|
13359
|
-
|
|
13414
|
+
Effect27.gen(function* () {
|
|
13360
13415
|
const now = yield* DateTime9.now;
|
|
13361
13416
|
const context = {
|
|
13362
13417
|
timestamp: DateTime9.toEpochMillis(now)
|
|
@@ -13378,14 +13433,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13378
13433
|
events.push(...planEvents);
|
|
13379
13434
|
}
|
|
13380
13435
|
if (result.completed) {
|
|
13381
|
-
yield*
|
|
13436
|
+
yield* Effect27.logInfo(
|
|
13382
13437
|
"[DEBUG] createIterationStream: LLM emitted completion signal"
|
|
13383
13438
|
);
|
|
13384
13439
|
yield* Ref11.set(loopCompletedRef, true);
|
|
13385
13440
|
}
|
|
13386
13441
|
const updatedPlan = yield* Ref11.get(currentPlanRef);
|
|
13387
13442
|
const allComplete = areAllTasksComplete(updatedPlan);
|
|
13388
|
-
yield*
|
|
13443
|
+
yield* Effect27.logInfo(
|
|
13389
13444
|
"[DEBUG] createIterationStream: Auto-complete check",
|
|
13390
13445
|
{
|
|
13391
13446
|
llmEmittedComplete: result.completed,
|
|
@@ -13397,7 +13452,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13397
13452
|
}
|
|
13398
13453
|
);
|
|
13399
13454
|
if (allComplete) {
|
|
13400
|
-
yield*
|
|
13455
|
+
yield* Effect27.logInfo(
|
|
13401
13456
|
"[DEBUG] createIterationStream: All tasks complete - ending loop"
|
|
13402
13457
|
);
|
|
13403
13458
|
yield* Ref11.set(loopCompletedRef, true);
|
|
@@ -13419,7 +13474,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13419
13474
|
)
|
|
13420
13475
|
);
|
|
13421
13476
|
const completionStream = Stream8.fromEffect(
|
|
13422
|
-
|
|
13477
|
+
Effect27.gen(function* () {
|
|
13423
13478
|
const persistEvents = yield* flushPlanPersistence(
|
|
13424
13479
|
planStore,
|
|
13425
13480
|
currentPlanRef,
|
|
@@ -13444,14 +13499,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13444
13499
|
// src/orchestrator/loop.ts
|
|
13445
13500
|
function runLoop(config) {
|
|
13446
13501
|
return Stream9.unwrap(
|
|
13447
|
-
|
|
13502
|
+
Effect28.gen(function* () {
|
|
13448
13503
|
const llm = yield* LLM;
|
|
13449
13504
|
const signalParser = yield* SignalParser;
|
|
13450
13505
|
const sessionStore = yield* SessionStore;
|
|
13451
13506
|
const planStore = yield* PlanStore;
|
|
13452
13507
|
const git = yield* Git;
|
|
13453
13508
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
13454
|
-
|
|
13509
|
+
Effect28.mapError(
|
|
13455
13510
|
(e) => new OrchestratorError({
|
|
13456
13511
|
message: `Failed to create session: ${e.message}`,
|
|
13457
13512
|
phase: "setup",
|
|
@@ -13460,15 +13515,15 @@ function runLoop(config) {
|
|
|
13460
13515
|
)
|
|
13461
13516
|
);
|
|
13462
13517
|
const baseBranch = yield* git.getCurrentBranch().pipe(
|
|
13463
|
-
|
|
13464
|
-
(error) =>
|
|
13518
|
+
Effect28.tapError(
|
|
13519
|
+
(error) => Effect28.logDebug("Failed to get current branch", {
|
|
13465
13520
|
error: String(error)
|
|
13466
13521
|
})
|
|
13467
13522
|
),
|
|
13468
|
-
|
|
13523
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13469
13524
|
);
|
|
13470
13525
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
13471
|
-
|
|
13526
|
+
Effect28.mapError(
|
|
13472
13527
|
(e) => new OrchestratorError({
|
|
13473
13528
|
message: `Failed to create worktree: ${e.message}`,
|
|
13474
13529
|
phase: "setup",
|
|
@@ -13483,12 +13538,12 @@ function runLoop(config) {
|
|
|
13483
13538
|
branchName,
|
|
13484
13539
|
baseBranch
|
|
13485
13540
|
}).pipe(
|
|
13486
|
-
|
|
13487
|
-
(error) =>
|
|
13541
|
+
Effect28.tapError(
|
|
13542
|
+
(error) => Effect28.logDebug("Failed to update session with worktree info", {
|
|
13488
13543
|
error: String(error)
|
|
13489
13544
|
})
|
|
13490
13545
|
),
|
|
13491
|
-
|
|
13546
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13492
13547
|
);
|
|
13493
13548
|
const startTimeUtc = yield* DateTime10.now;
|
|
13494
13549
|
const startTime = DateTime10.toEpochMillis(startTimeUtc);
|
|
@@ -13509,7 +13564,7 @@ function runLoop(config) {
|
|
|
13509
13564
|
config,
|
|
13510
13565
|
timestamp: startTime
|
|
13511
13566
|
};
|
|
13512
|
-
const handleSessionName = (displayName) =>
|
|
13567
|
+
const handleSessionName = (displayName) => Effect28.gen(function* () {
|
|
13513
13568
|
const currentSession = yield* Ref12.get(sessionRef);
|
|
13514
13569
|
const newBranchName = yield* git.renameBranch(
|
|
13515
13570
|
session.id,
|
|
@@ -13535,7 +13590,7 @@ function runLoop(config) {
|
|
|
13535
13590
|
);
|
|
13536
13591
|
const iterationsStream = Stream9.unfoldEffect(
|
|
13537
13592
|
1,
|
|
13538
|
-
(iteration) =>
|
|
13593
|
+
(iteration) => Effect28.gen(function* () {
|
|
13539
13594
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
13540
13595
|
if (completed || iteration > maxIterations) {
|
|
13541
13596
|
return Option.none();
|
|
@@ -13581,8 +13636,8 @@ function runLoop(config) {
|
|
|
13581
13636
|
);
|
|
13582
13637
|
}).pipe(
|
|
13583
13638
|
// Also catch setup errors (e.g., session creation failure)
|
|
13584
|
-
|
|
13585
|
-
(error) =>
|
|
13639
|
+
Effect28.catchAll(
|
|
13640
|
+
(error) => Effect28.succeed(
|
|
13586
13641
|
Stream9.succeed({
|
|
13587
13642
|
_tag: "LoopFailed",
|
|
13588
13643
|
error: {
|
|
@@ -13608,32 +13663,32 @@ Generated by Ferix`;
|
|
|
13608
13663
|
}
|
|
13609
13664
|
function createCompletionStream(sessionStore, git, sessionRef, config, startTime, loopCompletedRef, _worktreePath) {
|
|
13610
13665
|
return Stream9.unwrap(
|
|
13611
|
-
|
|
13666
|
+
Effect28.gen(function* () {
|
|
13612
13667
|
const session = yield* Ref12.get(sessionRef);
|
|
13613
13668
|
const endTimeUtc = yield* DateTime10.now;
|
|
13614
13669
|
const endTime = DateTime10.toEpochMillis(endTimeUtc);
|
|
13615
13670
|
const durationMs = endTime - startTime;
|
|
13616
13671
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
13617
13672
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
13618
|
-
|
|
13619
|
-
(error) =>
|
|
13673
|
+
Effect28.tapError(
|
|
13674
|
+
(error) => Effect28.logDebug("Final commit failed, continuing", {
|
|
13620
13675
|
sessionId: session.id,
|
|
13621
13676
|
error: String(error)
|
|
13622
13677
|
})
|
|
13623
13678
|
),
|
|
13624
|
-
|
|
13679
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13625
13680
|
);
|
|
13626
13681
|
let branchPushed = false;
|
|
13627
13682
|
if (config.push === true) {
|
|
13628
13683
|
const pushResult = yield* git.pushBranch(session.id).pipe(
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
(error) =>
|
|
13684
|
+
Effect28.map(() => true),
|
|
13685
|
+
Effect28.tapError(
|
|
13686
|
+
(error) => Effect28.logDebug("Push failed, continuing", {
|
|
13632
13687
|
sessionId: session.id,
|
|
13633
13688
|
error: String(error)
|
|
13634
13689
|
})
|
|
13635
13690
|
),
|
|
13636
|
-
|
|
13691
|
+
Effect28.orElseSucceed(() => false)
|
|
13637
13692
|
);
|
|
13638
13693
|
branchPushed = pushResult;
|
|
13639
13694
|
}
|
|
@@ -13642,25 +13697,25 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13642
13697
|
const title = `feat: ${session.originalTask.slice(0, 50)}`;
|
|
13643
13698
|
const body = buildPRBody(session, config);
|
|
13644
13699
|
const prResult = yield* git.createPR(session.id, title, body, session.baseBranch).pipe(
|
|
13645
|
-
|
|
13646
|
-
|
|
13647
|
-
(error) =>
|
|
13700
|
+
Effect28.map((url) => url),
|
|
13701
|
+
Effect28.tapError(
|
|
13702
|
+
(error) => Effect28.logDebug("PR creation failed, continuing", {
|
|
13648
13703
|
sessionId: session.id,
|
|
13649
13704
|
error: String(error)
|
|
13650
13705
|
})
|
|
13651
13706
|
),
|
|
13652
|
-
|
|
13707
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13653
13708
|
);
|
|
13654
13709
|
prUrl = prResult;
|
|
13655
13710
|
}
|
|
13656
13711
|
yield* git.removeWorktreeKeepBranch(session.id).pipe(
|
|
13657
|
-
|
|
13658
|
-
(error) =>
|
|
13712
|
+
Effect28.tapError(
|
|
13713
|
+
(error) => Effect28.logDebug("Worktree cleanup failed, continuing", {
|
|
13659
13714
|
sessionId: session.id,
|
|
13660
13715
|
error: String(error)
|
|
13661
13716
|
})
|
|
13662
13717
|
),
|
|
13663
|
-
|
|
13718
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13664
13719
|
);
|
|
13665
13720
|
const worktreeRemoved = {
|
|
13666
13721
|
_tag: "WorktreeRemoved",
|
|
@@ -13681,13 +13736,13 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13681
13736
|
status: completed ? "completed" : "paused",
|
|
13682
13737
|
worktreePath: void 0
|
|
13683
13738
|
}).pipe(
|
|
13684
|
-
|
|
13685
|
-
(error) =>
|
|
13739
|
+
Effect28.tapError(
|
|
13740
|
+
(error) => Effect28.logDebug("Session update failed, continuing", {
|
|
13686
13741
|
sessionId: session.id,
|
|
13687
13742
|
error: String(error)
|
|
13688
13743
|
})
|
|
13689
13744
|
),
|
|
13690
|
-
|
|
13745
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13691
13746
|
);
|
|
13692
13747
|
const events = [worktreeRemoved];
|
|
13693
13748
|
if (branchPushed) {
|
|
@@ -13717,7 +13772,7 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13717
13772
|
function run(options) {
|
|
13718
13773
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
13719
13774
|
const events = runLoop(config);
|
|
13720
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
13775
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect29.sync(() => onEvent(event)))) : events;
|
|
13721
13776
|
const layers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
13722
13777
|
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
13723
13778
|
if (consumerType === "none") {
|
|
@@ -13729,7 +13784,7 @@ function run(options) {
|
|
|
13729
13784
|
function runTest(options, mockEvents) {
|
|
13730
13785
|
const { config, onEvent } = options;
|
|
13731
13786
|
const events = runLoop(config);
|
|
13732
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
13787
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect29.sync(() => onEvent(event)))) : events;
|
|
13733
13788
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
13734
13789
|
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
13735
13790
|
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
@@ -13737,11 +13792,11 @@ function runTest(options, mockEvents) {
|
|
|
13737
13792
|
function collectEvents(config, mockEvents) {
|
|
13738
13793
|
const events = runLoop(config);
|
|
13739
13794
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
13740
|
-
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(
|
|
13795
|
+
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect29.map((chunk) => Array.from(chunk)));
|
|
13741
13796
|
}
|
|
13742
13797
|
function main(config) {
|
|
13743
13798
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
13744
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
13799
|
+
return run({ config, consumer: consumerType }).pipe(Effect29.runPromise);
|
|
13745
13800
|
}
|
|
13746
13801
|
|
|
13747
13802
|
// src/services/index.ts
|
|
@@ -13805,17 +13860,17 @@ var GLOB_SUFFIX_PATTERN = /\/\*+$/;
|
|
|
13805
13860
|
var expandGlobPattern = async (rootDir, pattern) => {
|
|
13806
13861
|
if (pattern.endsWith("/*") || pattern.endsWith("/**")) {
|
|
13807
13862
|
const baseDir = pattern.replace(GLOB_SUFFIX_PATTERN, "");
|
|
13808
|
-
const fullPath2 =
|
|
13863
|
+
const fullPath2 = join8(rootDir, baseDir);
|
|
13809
13864
|
try {
|
|
13810
13865
|
const entries = await readdir2(fullPath2, { withFileTypes: true });
|
|
13811
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) =>
|
|
13866
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => join8(fullPath2, entry.name));
|
|
13812
13867
|
} catch {
|
|
13813
13868
|
return [];
|
|
13814
13869
|
}
|
|
13815
13870
|
}
|
|
13816
|
-
const fullPath =
|
|
13871
|
+
const fullPath = join8(rootDir, pattern);
|
|
13817
13872
|
try {
|
|
13818
|
-
await
|
|
13873
|
+
await access4(fullPath);
|
|
13819
13874
|
return [fullPath];
|
|
13820
13875
|
} catch {
|
|
13821
13876
|
return [];
|
|
@@ -13835,9 +13890,9 @@ var discoverPackageJsonFiles = async (rootPath, rootPkg) => {
|
|
|
13835
13890
|
for (const pattern of patterns) {
|
|
13836
13891
|
const dirs = await expandGlobPattern(rootDir, pattern);
|
|
13837
13892
|
for (const dir of dirs) {
|
|
13838
|
-
const pkgPath =
|
|
13893
|
+
const pkgPath = join8(dir, "package.json");
|
|
13839
13894
|
try {
|
|
13840
|
-
await
|
|
13895
|
+
await access4(pkgPath);
|
|
13841
13896
|
packageJsonPaths.push(pkgPath);
|
|
13842
13897
|
} catch {
|
|
13843
13898
|
}
|
|
@@ -13884,7 +13939,7 @@ var createSpinner = (text) => {
|
|
|
13884
13939
|
var discoverPackages = async (state, packagePath) => {
|
|
13885
13940
|
state.spinner = createSpinner("Scanning for package.json files...").start();
|
|
13886
13941
|
const absolutePath = resolve(packagePath);
|
|
13887
|
-
await
|
|
13942
|
+
await access4(absolutePath);
|
|
13888
13943
|
const rootPkg = await readPackageJsonFile(absolutePath);
|
|
13889
13944
|
state.packageJsonPaths = await discoverPackageJsonFiles(
|
|
13890
13945
|
absolutePath,
|
|
@@ -13917,7 +13972,7 @@ var extractAllDependencies = async (state) => {
|
|
|
13917
13972
|
};
|
|
13918
13973
|
var resolveOrganizations = async (state, isDev) => {
|
|
13919
13974
|
state.spinner = createSpinner("Resolving GitHub organizations...").start();
|
|
13920
|
-
const packageOrgs = await
|
|
13975
|
+
const packageOrgs = await Effect30.runPromise(
|
|
13921
13976
|
resolvePackageOrgs(state.dependencies, isDev)
|
|
13922
13977
|
);
|
|
13923
13978
|
state.orgs = Array.from(
|
|
@@ -13937,7 +13992,7 @@ var resolveOrganizations = async (state, isDev) => {
|
|
|
13937
13992
|
};
|
|
13938
13993
|
var findRepositories = async (state, isDev) => {
|
|
13939
13994
|
state.spinner = createSpinner("Searching for skill repositories...").start();
|
|
13940
|
-
state.skillRepos = await
|
|
13995
|
+
state.skillRepos = await Effect30.runPromise(findSkillRepos(state.orgs, isDev));
|
|
13941
13996
|
if (state.skillRepos.length === 0) {
|
|
13942
13997
|
state.spinner.warn("No skill repositories found");
|
|
13943
13998
|
printHint(
|
|
@@ -13950,29 +14005,57 @@ var findRepositories = async (state, isDev) => {
|
|
|
13950
14005
|
);
|
|
13951
14006
|
return true;
|
|
13952
14007
|
};
|
|
13953
|
-
var
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
for (let i = 0; i < skillRepos.length; i++) {
|
|
13957
|
-
const repo = skillRepos[i];
|
|
14008
|
+
var selectRepositories = async (skillRepos) => {
|
|
14009
|
+
if (skillRepos.length === 1) {
|
|
14010
|
+
const repo = skillRepos[0];
|
|
13958
14011
|
if (repo) {
|
|
13959
|
-
|
|
14012
|
+
printHint(
|
|
14013
|
+
`Only 1 repository found, auto-selecting: ${pc17.cyan(`${repo.owner}/${repo.repo}`)}`
|
|
14014
|
+
);
|
|
13960
14015
|
}
|
|
14016
|
+
return { type: "selected", repos: skillRepos };
|
|
13961
14017
|
}
|
|
14018
|
+
if (!process.stdin.isTTY) {
|
|
14019
|
+
printHint(
|
|
14020
|
+
`Non-interactive terminal detected, selecting all ${skillRepos.length} repositories.`
|
|
14021
|
+
);
|
|
14022
|
+
printHint(`Use ${pc17.cyan("--yes")} flag to skip this message in CI/CD.`);
|
|
14023
|
+
return { type: "selected", repos: skillRepos };
|
|
14024
|
+
}
|
|
14025
|
+
console.log();
|
|
14026
|
+
const selected = await multiselect({
|
|
14027
|
+
message: "Select repositories to install (space to toggle, a to toggle all)",
|
|
14028
|
+
options: skillRepos.map((repo) => ({
|
|
14029
|
+
value: repo,
|
|
14030
|
+
label: `${repo.owner}/${repo.repo}`
|
|
14031
|
+
})),
|
|
14032
|
+
required: false
|
|
14033
|
+
});
|
|
14034
|
+
if (isCancel(selected)) {
|
|
14035
|
+
return { type: "cancelled" };
|
|
14036
|
+
}
|
|
14037
|
+
if (selected.length === 0) {
|
|
14038
|
+
return { type: "none" };
|
|
14039
|
+
}
|
|
14040
|
+
return { type: "selected", repos: selected };
|
|
13962
14041
|
};
|
|
13963
|
-
var installRepositories = async (state, isGlobal) => {
|
|
14042
|
+
var installRepositories = async (state, reposToInstall, isGlobal, agents) => {
|
|
13964
14043
|
printSection("Installing");
|
|
13965
14044
|
console.log();
|
|
13966
14045
|
const installed = [];
|
|
13967
14046
|
const failed = [];
|
|
13968
|
-
for (const repo of
|
|
14047
|
+
for (const repo of reposToInstall) {
|
|
13969
14048
|
const repoId = `${repo.owner}/${repo.repo}`;
|
|
13970
14049
|
state.spinner = createSpinner(
|
|
13971
14050
|
`Installing ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}...`
|
|
13972
14051
|
).start();
|
|
13973
14052
|
try {
|
|
13974
|
-
await
|
|
13975
|
-
installSkills([repo], {
|
|
14053
|
+
await Effect30.runPromise(
|
|
14054
|
+
installSkills([repo], {
|
|
14055
|
+
dryRun: false,
|
|
14056
|
+
global: isGlobal,
|
|
14057
|
+
agents: [...agents]
|
|
14058
|
+
})
|
|
13976
14059
|
);
|
|
13977
14060
|
state.spinner.succeed(
|
|
13978
14061
|
`Installed ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}`
|
|
@@ -13987,7 +14070,7 @@ var installRepositories = async (state, isGlobal) => {
|
|
|
13987
14070
|
}
|
|
13988
14071
|
return { installed, failed };
|
|
13989
14072
|
};
|
|
13990
|
-
var displaySummary = (installed, failed, isGlobal) => {
|
|
14073
|
+
var displaySummary = (installed, failed, isGlobal, agents) => {
|
|
13991
14074
|
printSection("Summary");
|
|
13992
14075
|
console.log();
|
|
13993
14076
|
if (installed.length > 0) {
|
|
@@ -14001,11 +14084,91 @@ var displaySummary = (installed, failed, isGlobal) => {
|
|
|
14001
14084
|
);
|
|
14002
14085
|
}
|
|
14003
14086
|
const installLocation = isGlobal ? "globally" : "in project";
|
|
14004
|
-
|
|
14087
|
+
const agentList = agents.length > 0 ? agents.join(", ") : "all agents";
|
|
14088
|
+
printHint(`Skills installed ${installLocation} to: ${pc17.cyan(agentList)}`);
|
|
14089
|
+
console.log();
|
|
14090
|
+
};
|
|
14091
|
+
var detectProjectAgents = async (state, projectDir) => {
|
|
14092
|
+
state.spinner = createSpinner("Detecting coding agents...").start();
|
|
14093
|
+
state.detectedAgents = await Effect30.runPromise(detectAgents(projectDir));
|
|
14094
|
+
if (state.detectedAgents.length === 0) {
|
|
14095
|
+
state.spinner.warn("No coding agents detected");
|
|
14096
|
+
printHint(
|
|
14097
|
+
`No agent directories found (${pc17.dim(".cursor/, .claude/, .opencode/, etc.")})`
|
|
14098
|
+
);
|
|
14099
|
+
printHint(
|
|
14100
|
+
`Use ${pc17.cyan("--agents")} to specify agents manually, e.g. ${pc17.cyan("--agents cursor claude-code")}`
|
|
14101
|
+
);
|
|
14102
|
+
return false;
|
|
14103
|
+
}
|
|
14104
|
+
state.spinner.succeed(
|
|
14105
|
+
`Detected agents: ${pc17.cyan(state.detectedAgents.join(", "))}`
|
|
14106
|
+
);
|
|
14107
|
+
return true;
|
|
14108
|
+
};
|
|
14109
|
+
var determineAgents = async (state, packagePath, manualAgents) => {
|
|
14110
|
+
if (manualAgents && manualAgents.length > 0) {
|
|
14111
|
+
const invalidAgents = validateAgentNames(manualAgents);
|
|
14112
|
+
if (invalidAgents.length > 0) {
|
|
14113
|
+
printError(`Invalid agent names: ${invalidAgents.join(", ")}`);
|
|
14114
|
+
printHint(`Supported agents: ${SUPPORTED_AGENTS.join(", ")}`);
|
|
14115
|
+
console.log();
|
|
14116
|
+
return null;
|
|
14117
|
+
}
|
|
14118
|
+
printSuccess(`Using specified agents: ${pc17.cyan(manualAgents.join(", "))}`);
|
|
14119
|
+
return manualAgents;
|
|
14120
|
+
}
|
|
14121
|
+
const projectDir = dirname2(resolve(packagePath));
|
|
14122
|
+
const hasAgents = await detectProjectAgents(state, projectDir);
|
|
14123
|
+
return hasAgents ? state.detectedAgents : null;
|
|
14124
|
+
};
|
|
14125
|
+
var handleRepoSelection = async (skillRepos, skipSelection) => {
|
|
14126
|
+
if (skipSelection) {
|
|
14127
|
+
printSuccess(
|
|
14128
|
+
`Auto-selecting all ${skillRepos.length} repositories (--yes)`
|
|
14129
|
+
);
|
|
14130
|
+
return skillRepos;
|
|
14131
|
+
}
|
|
14132
|
+
const selection = await selectRepositories(skillRepos);
|
|
14133
|
+
if (selection.type === "cancelled") {
|
|
14134
|
+
cancel("Operation cancelled.");
|
|
14135
|
+
process.exit(0);
|
|
14136
|
+
}
|
|
14137
|
+
if (selection.type === "none") {
|
|
14138
|
+
console.log();
|
|
14139
|
+
printHint("No repositories selected. Skipping installation.");
|
|
14140
|
+
console.log();
|
|
14141
|
+
return null;
|
|
14142
|
+
}
|
|
14143
|
+
printSuccess(`Selected ${selection.repos.length} repositories`);
|
|
14144
|
+
return selection.repos;
|
|
14145
|
+
};
|
|
14146
|
+
var displayDryRunSummary = (reposToInstall, agentsToUse) => {
|
|
14147
|
+
console.log();
|
|
14148
|
+
printSection("Would install");
|
|
14149
|
+
console.log();
|
|
14150
|
+
for (let i = 0; i < reposToInstall.length; i++) {
|
|
14151
|
+
const repo = reposToInstall[i];
|
|
14152
|
+
if (repo) {
|
|
14153
|
+
printRepo(repo.owner, repo.repo, i);
|
|
14154
|
+
}
|
|
14155
|
+
}
|
|
14156
|
+
console.log();
|
|
14157
|
+
printHint(
|
|
14158
|
+
`Would install to: ${pc17.cyan(agentsToUse.join(", ") || "all agents")}`
|
|
14159
|
+
);
|
|
14160
|
+
printHint(`Run without ${pc17.cyan("--dry-run")} to install these skills.`);
|
|
14005
14161
|
console.log();
|
|
14006
14162
|
};
|
|
14007
14163
|
var runSyncCommand = async (options) => {
|
|
14008
|
-
const {
|
|
14164
|
+
const {
|
|
14165
|
+
path: packagePath,
|
|
14166
|
+
dryRun,
|
|
14167
|
+
global: isGlobal,
|
|
14168
|
+
dev: isDev,
|
|
14169
|
+
agents: manualAgents,
|
|
14170
|
+
yes: skipSelection
|
|
14171
|
+
} = options;
|
|
14009
14172
|
printHeader(
|
|
14010
14173
|
"Ferix Sync",
|
|
14011
14174
|
isDev ? pc17.yellow("Development Mode") : "Discovering skills from your dependencies"
|
|
@@ -14015,9 +14178,14 @@ var runSyncCommand = async (options) => {
|
|
|
14015
14178
|
packageJsonPaths: [],
|
|
14016
14179
|
dependencies: [],
|
|
14017
14180
|
orgs: [],
|
|
14018
|
-
skillRepos: []
|
|
14181
|
+
skillRepos: [],
|
|
14182
|
+
detectedAgents: []
|
|
14019
14183
|
};
|
|
14020
14184
|
await discoverPackages(state, packagePath);
|
|
14185
|
+
const agentsToUse = await determineAgents(state, packagePath, manualAgents);
|
|
14186
|
+
if (!agentsToUse) {
|
|
14187
|
+
return;
|
|
14188
|
+
}
|
|
14021
14189
|
const hasDeps = await extractAllDependencies(state);
|
|
14022
14190
|
if (!hasDeps) {
|
|
14023
14191
|
return;
|
|
@@ -14030,23 +14198,37 @@ var runSyncCommand = async (options) => {
|
|
|
14030
14198
|
if (!hasRepos) {
|
|
14031
14199
|
return;
|
|
14032
14200
|
}
|
|
14033
|
-
|
|
14201
|
+
const reposToInstall = await handleRepoSelection(
|
|
14202
|
+
state.skillRepos,
|
|
14203
|
+
skipSelection
|
|
14204
|
+
);
|
|
14205
|
+
if (!reposToInstall) {
|
|
14206
|
+
return;
|
|
14207
|
+
}
|
|
14034
14208
|
if (dryRun) {
|
|
14035
|
-
|
|
14036
|
-
printHint(`Run without ${pc17.cyan("--dry-run")} to install these skills.`);
|
|
14037
|
-
console.log();
|
|
14209
|
+
displayDryRunSummary(reposToInstall, agentsToUse);
|
|
14038
14210
|
return;
|
|
14039
14211
|
}
|
|
14040
|
-
const { installed, failed } = await installRepositories(
|
|
14041
|
-
|
|
14212
|
+
const { installed, failed } = await installRepositories(
|
|
14213
|
+
state,
|
|
14214
|
+
reposToInstall,
|
|
14215
|
+
isGlobal,
|
|
14216
|
+
agentsToUse
|
|
14217
|
+
);
|
|
14218
|
+
displaySummary(installed, failed, isGlobal, agentsToUse);
|
|
14042
14219
|
};
|
|
14043
|
-
program.command("sync").description("Discover and install skills based on your dependencies").option("-p, --path <path>", "Path to package.json", "./package.json").option("-n, --dry-run", "List skills without installing").option("-g, --global", "Install globally instead of project-level").option("-d, --dev", "Use development server instead of production").
|
|
14220
|
+
program.command("sync").description("Discover and install skills based on your dependencies").option("-p, --path <path>", "Path to package.json", "./package.json").option("-n, --dry-run", "List skills without installing").option("-g, --global", "Install globally instead of project-level").option("-d, --dev", "Use development server instead of production").option(
|
|
14221
|
+
"-a, --agents <agents...>",
|
|
14222
|
+
"Specify agents to install to (auto-detects if not provided)"
|
|
14223
|
+
).option("-y, --yes", "Skip selection prompt, install all repositories").action(async (options) => {
|
|
14044
14224
|
try {
|
|
14045
14225
|
await runSyncCommand({
|
|
14046
14226
|
path: options.path,
|
|
14047
14227
|
dryRun: options.dryRun ?? false,
|
|
14048
14228
|
global: options.global ?? false,
|
|
14049
|
-
dev: options.dev ?? false
|
|
14229
|
+
dev: options.dev ?? false,
|
|
14230
|
+
agents: options.agents,
|
|
14231
|
+
yes: options.yes ?? false
|
|
14050
14232
|
});
|
|
14051
14233
|
} catch (error) {
|
|
14052
14234
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ferix-code",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
3
|
+
"version": "0.0.2-beta.26",
|
|
4
4
|
"description": "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"bump": "npm version prerelease --preid=beta --workspaces=false && bun run build && bun publish --tag beta"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"@clack/prompts": "^0.10.0",
|
|
22
23
|
"commander": "^14.0.0",
|
|
23
24
|
"effect": "^3.19.15",
|
|
24
25
|
"human-id": "^4.1.3",
|