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 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" | "cursor" | "opencode" | undefined;
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 access3, readdir as readdir2, readFile as readFile7 } from "fs/promises";
74
- import { dirname as dirname2, join as join7, resolve } from "path";
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-XQ7UIC5I.js
80
+ // ../sync/dist/chunk-5E4WZI4D.js
80
81
  init_esm_shims();
81
82
 
82
- // ../sync/dist/chunk-MSWOW6ZJ.js
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-XQ7UIC5I.js
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-FXKCLMLB.js
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 command = `npx skills add ${repoId}${globalFlag} --yes`;
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-BAZKO52B.js
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 Effect4, Schema as S4 } from "effect";
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 Effect29 } from "effect";
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.25",
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 Effect28, Stream as Stream10 } from "effect";
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 join(process.cwd(), WORKTREES_DIR, sessionId);
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 directoryExists(dirPath) {
8822
+ function directoryExists2(dirPath) {
8768
8823
  return Effect8.tryPromise({
8769
8824
  try: async () => {
8770
- await access(dirPath);
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 = join(process.cwd(), file);
8811
- const destPath = join(worktreeDir, file);
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 = join(worktreeDir, ".git");
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 = join(gitDir, "info", "exclude");
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 = join(process.cwd(), WORKTREES_DIR);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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* directoryExists(worktreeDir);
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 join2 } from "path";
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 join2(process.cwd(), PLANS_DIR, sessionId);
10303
+ return join3(process.cwd(), PLANS_DIR, sessionId);
10249
10304
  }
10250
10305
  function getGuardrailsPath(sessionId) {
10251
- return join2(getSessionDir(sessionId), "guardrails.json");
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 access2, mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
10898
- import { join as join3 } from "path";
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 join3(process.cwd(), PLANS_DIR2, sessionId);
10975
+ return join4(process.cwd(), PLANS_DIR2, sessionId);
10921
10976
  }
10922
10977
  function getPlanPath(sessionId, planId) {
10923
- return join3(getSessionDir2(sessionId), `${planId}.json`);
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 = join3(process.cwd(), PLANS_DIR2);
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 access2(planPath);
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 join4 } from "path";
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 join4(process.cwd(), PLANS_DIR3, sessionId);
11245
+ return join5(process.cwd(), PLANS_DIR3, sessionId);
11191
11246
  }
11192
11247
  function getProgressPath(sessionId) {
11193
- return join4(getSessionDir3(sessionId), "progress.json");
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 join5 } from "path";
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 join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
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 = join5(process.cwd(), SESSIONS_DIR);
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 Effect22, Layer as Layer13, Ref as Ref8 } from "effect";
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 Effect22.gen(function* () {
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) => Effect22.gen(function* () {
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 = () => Effect22.gen(function* () {
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) => Effect22.succeed(signalSpecRegistry.parseAll(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 Effect27, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
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 Effect25, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
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 join6 } from "path";
12175
- import { Effect as Effect23 } from "effect";
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 Effect23.tryPromise({
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(Effect23.asVoid);
12240
+ }).pipe(Effect24.asVoid);
12186
12241
  }
12187
12242
  function getSessionDir4(sessionId) {
12188
- return join6(process.cwd(), PLANS_DIR4, sessionId);
12243
+ return join7(process.cwd(), PLANS_DIR4, sessionId);
12189
12244
  }
12190
12245
  function getTasksMdPath(sessionId) {
12191
- return join6(getSessionDir4(sessionId), "tasks.md");
12246
+ return join7(getSessionDir4(sessionId), "tasks.md");
12192
12247
  }
12193
12248
  function writeTasksMd(sessionId, tasks) {
12194
- return Effect23.gen(function* () {
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* Effect23.tryPromise({
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 Effect24, Ref as Ref9 } from "effect";
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
- Effect24.map(() => null),
12714
- Effect24.catchAll(
12715
- (error) => Effect24.succeed({
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 Effect24.gen(function* () {
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 Effect24.gen(function* () {
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 Effect25.gen(function* () {
13126
+ return Effect26.gen(function* () {
13072
13127
  const events = [];
13073
13128
  const parsedSignals = [];
13074
13129
  const signals = yield* signalParser.parse(text).pipe(
13075
- Effect25.tapError(
13076
- (error) => Effect25.logDebug(
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
- Effect25.orElseSucceed(() => [])
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 Effect25.gen(function* () {
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
- Effect25.gen(function* () {
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
- Effect25.gen(function* () {
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
- Effect25.gen(function* () {
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
- Effect25.tapError(
13219
- (error) => Effect25.logDebug("Failed to write tasks.md, continuing", {
13273
+ Effect26.tapError(
13274
+ (error) => Effect26.logDebug("Failed to write tasks.md, continuing", {
13220
13275
  error: String(error)
13221
13276
  })
13222
13277
  ),
13223
- Effect25.orElseSucceed(() => void 0)
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
- Effect25.tapError(
13234
- (error) => Effect25.logDebug(
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
- Effect25.orElseSucceed(() => void 0)
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 Effect26, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
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 Effect26.gen(function* () {
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
- Effect26.tapError(
13282
- (error) => Effect26.logDebug(
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
- Effect26.orElseSucceed(() => [])
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 Effect26.gen(function* () {
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
- Effect26.gen(function* () {
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
- Effect26.gen(function* () {
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* Effect26.logInfo(
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* Effect26.logInfo(
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* Effect26.logInfo(
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
- Effect26.gen(function* () {
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
- Effect27.gen(function* () {
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
- Effect27.mapError(
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
- Effect27.tapError(
13464
- (error) => Effect27.logDebug("Failed to get current branch", {
13518
+ Effect28.tapError(
13519
+ (error) => Effect28.logDebug("Failed to get current branch", {
13465
13520
  error: String(error)
13466
13521
  })
13467
13522
  ),
13468
- Effect27.orElseSucceed(() => void 0)
13523
+ Effect28.orElseSucceed(() => void 0)
13469
13524
  );
13470
13525
  const worktreePath = yield* git.createWorktree(session.id).pipe(
13471
- Effect27.mapError(
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
- Effect27.tapError(
13487
- (error) => Effect27.logDebug("Failed to update session with worktree info", {
13541
+ Effect28.tapError(
13542
+ (error) => Effect28.logDebug("Failed to update session with worktree info", {
13488
13543
  error: String(error)
13489
13544
  })
13490
13545
  ),
13491
- Effect27.orElseSucceed(() => void 0)
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) => Effect27.gen(function* () {
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) => Effect27.gen(function* () {
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
- Effect27.catchAll(
13585
- (error) => Effect27.succeed(
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
- Effect27.gen(function* () {
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
- Effect27.tapError(
13619
- (error) => Effect27.logDebug("Final commit failed, continuing", {
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
- Effect27.orElseSucceed(() => void 0)
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
- Effect27.map(() => true),
13630
- Effect27.tapError(
13631
- (error) => Effect27.logDebug("Push failed, continuing", {
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
- Effect27.orElseSucceed(() => false)
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
- Effect27.map((url) => url),
13646
- Effect27.tapError(
13647
- (error) => Effect27.logDebug("PR creation failed, continuing", {
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
- Effect27.orElseSucceed(() => void 0)
13707
+ Effect28.orElseSucceed(() => void 0)
13653
13708
  );
13654
13709
  prUrl = prResult;
13655
13710
  }
13656
13711
  yield* git.removeWorktreeKeepBranch(session.id).pipe(
13657
- Effect27.tapError(
13658
- (error) => Effect27.logDebug("Worktree cleanup failed, continuing", {
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
- Effect27.orElseSucceed(() => void 0)
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
- Effect27.tapError(
13685
- (error) => Effect27.logDebug("Session update failed, continuing", {
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
- Effect27.orElseSucceed(() => void 0)
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) => Effect28.sync(() => onEvent(event)))) : events;
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) => Effect28.sync(() => onEvent(event)))) : events;
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(Effect28.map((chunk) => Array.from(chunk)));
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(Effect28.runPromise);
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 = join7(rootDir, baseDir);
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) => join7(fullPath2, entry.name));
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 = join7(rootDir, pattern);
13871
+ const fullPath = join8(rootDir, pattern);
13817
13872
  try {
13818
- await access3(fullPath);
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 = join7(dir, "package.json");
13893
+ const pkgPath = join8(dir, "package.json");
13839
13894
  try {
13840
- await access3(pkgPath);
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 access3(absolutePath);
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 Effect29.runPromise(
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 Effect29.runPromise(findSkillRepos(state.orgs, isDev));
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 displayRepositories = (skillRepos) => {
13954
- printSection("Repositories");
13955
- console.log();
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
- printRepo(repo.owner, repo.repo, i);
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 state.skillRepos) {
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 Effect29.runPromise(
13975
- installSkills([repo], { dryRun: false, global: isGlobal })
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
- printHint(`Skills installed ${installLocation}.`);
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 { path: packagePath, dryRun, global: isGlobal, dev: isDev } = options;
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
- displayRepositories(state.skillRepos);
14201
+ const reposToInstall = await handleRepoSelection(
14202
+ state.skillRepos,
14203
+ skipSelection
14204
+ );
14205
+ if (!reposToInstall) {
14206
+ return;
14207
+ }
14034
14208
  if (dryRun) {
14035
- console.log();
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(state, isGlobal);
14041
- displaySummary(installed, failed, isGlobal);
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").action(async (options) => {
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.25",
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",