ferix-code 0.0.2-beta.23 → 0.0.2-beta.25

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +18 -18
  2. package/dist/index.js +328 -279
  3. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -330,9 +330,9 @@ declare const isContentBlockStart: (u: unknown, overrideOptions?: effect_SchemaA
330
330
  readonly text?: string | undefined;
331
331
  } | {
332
332
  readonly type: "tool_use";
333
+ readonly name: string;
333
334
  readonly input?: unknown;
334
335
  readonly id?: string | undefined;
335
- readonly name: string;
336
336
  };
337
337
  };
338
338
  declare const isContentBlockDelta: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions | number) => u is {
@@ -358,9 +358,9 @@ declare const isAssistantMessage: (u: unknown, overrideOptions?: effect_SchemaAS
358
358
  readonly text: string;
359
359
  } | {
360
360
  readonly type: "tool_use";
361
+ readonly name: string;
361
362
  readonly input: unknown;
362
363
  readonly id?: string | undefined;
363
- readonly name: string;
364
364
  })[];
365
365
  };
366
366
  };
@@ -378,9 +378,9 @@ declare const isInputJsonDelta: (u: unknown, overrideOptions?: effect_SchemaAST.
378
378
  };
379
379
  declare const isToolUseContentBlock: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions | number) => u is {
380
380
  readonly type: "tool_use";
381
+ readonly name: string;
381
382
  readonly input: unknown;
382
383
  readonly id?: string | undefined;
383
- readonly name: string;
384
384
  };
385
385
  declare const isTextContentBlock: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions | number) => u is {
386
386
  readonly type: "text";
@@ -397,9 +397,9 @@ declare const decodeClaudeCliEvent: (u: unknown, overrideOptions?: effect_Schema
397
397
  readonly text?: string | undefined;
398
398
  } | {
399
399
  readonly type: "tool_use";
400
+ readonly name: string;
400
401
  readonly input?: unknown;
401
402
  readonly id?: string | undefined;
402
- readonly name: string;
403
403
  };
404
404
  } | {
405
405
  readonly type: "content_block_delta";
@@ -422,9 +422,9 @@ declare const decodeClaudeCliEvent: (u: unknown, overrideOptions?: effect_Schema
422
422
  readonly text: string;
423
423
  } | {
424
424
  readonly type: "tool_use";
425
+ readonly name: string;
425
426
  readonly input: unknown;
426
427
  readonly id?: string | undefined;
427
- readonly name: string;
428
428
  })[];
429
429
  };
430
430
  } | {
@@ -439,9 +439,9 @@ declare const decodeClaudeCliEventSync: (u: unknown, overrideOptions?: effect_Sc
439
439
  readonly text?: string | undefined;
440
440
  } | {
441
441
  readonly type: "tool_use";
442
+ readonly name: string;
442
443
  readonly input?: unknown;
443
444
  readonly id?: string | undefined;
444
- readonly name: string;
445
445
  };
446
446
  } | {
447
447
  readonly type: "content_block_delta";
@@ -464,9 +464,9 @@ declare const decodeClaudeCliEventSync: (u: unknown, overrideOptions?: effect_Sc
464
464
  readonly text: string;
465
465
  } | {
466
466
  readonly type: "tool_use";
467
+ readonly name: string;
467
468
  readonly input: unknown;
468
469
  readonly id?: string | undefined;
469
- readonly name: string;
470
470
  })[];
471
471
  };
472
472
  } | {
@@ -1604,15 +1604,15 @@ declare const isBashToolInput: (u: unknown, overrideOptions?: effect_SchemaAST.P
1604
1604
  readonly [x: string]: unknown;
1605
1605
  };
1606
1606
  declare const isGlobToolInput: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions | number) => u is {
1607
- readonly pattern: string;
1608
1607
  readonly path?: string | undefined;
1608
+ readonly pattern: string;
1609
1609
  } & {
1610
1610
  readonly [x: string]: unknown;
1611
1611
  };
1612
1612
  declare const isGrepToolInput: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions | number) => u is {
1613
1613
  readonly type?: string | undefined;
1614
- readonly pattern: string;
1615
1614
  readonly path?: string | undefined;
1615
+ readonly pattern: string;
1616
1616
  readonly glob?: string | undefined;
1617
1617
  readonly output_mode?: "content" | "files_with_matches" | "count" | undefined;
1618
1618
  } & {
@@ -1728,8 +1728,8 @@ interface ValidatedToolUseEvent {
1728
1728
  readonly input: AnyToolInput;
1729
1729
  }
1730
1730
  declare const decodeLLMEvent: (u: unknown, overrideOptions?: effect_SchemaAST.ParseOptions) => effect_Effect.Effect<{
1731
- readonly _tag: "Text";
1732
1731
  readonly text: string;
1732
+ readonly _tag: "Text";
1733
1733
  } | {
1734
1734
  readonly _tag: "ToolStart";
1735
1735
  readonly tool: string;
@@ -1903,17 +1903,17 @@ declare const decodePlan: (u: unknown, overrideOptions?: effect_SchemaAST.ParseO
1903
1903
  readonly context?: string | undefined;
1904
1904
  readonly tasks: readonly {
1905
1905
  readonly phases: readonly {
1906
- readonly status: "pending" | "in_progress" | "done" | "failed";
1906
+ readonly status: "failed" | "pending" | "in_progress" | "done";
1907
1907
  readonly id: string;
1908
1908
  readonly description: string;
1909
1909
  }[];
1910
- readonly status: "planning" | "pending" | "in_progress" | "done" | "failed" | "skipped";
1910
+ readonly status: "planning" | "failed" | "pending" | "in_progress" | "done" | "skipped";
1911
1911
  readonly attempts: number;
1912
1912
  readonly id: string;
1913
1913
  readonly description: string;
1914
1914
  readonly title: string;
1915
1915
  readonly criteria: readonly {
1916
- readonly status: "pending" | "failed" | "passed";
1916
+ readonly status: "failed" | "pending" | "passed";
1917
1917
  readonly id: string;
1918
1918
  readonly description: string;
1919
1919
  readonly failureReason?: string | undefined;
@@ -1929,17 +1929,17 @@ declare const decodePlanData: (u: unknown, overrideOptions?: effect_SchemaAST.Pa
1929
1929
  readonly context?: string | undefined;
1930
1930
  readonly tasks: readonly {
1931
1931
  readonly phases: readonly {
1932
- readonly status: "pending" | "in_progress" | "done" | "failed";
1932
+ readonly status: "failed" | "pending" | "in_progress" | "done";
1933
1933
  readonly id: string;
1934
1934
  readonly description: string;
1935
1935
  }[];
1936
- readonly status: "planning" | "pending" | "in_progress" | "done" | "failed" | "skipped";
1936
+ readonly status: "planning" | "failed" | "pending" | "in_progress" | "done" | "skipped";
1937
1937
  readonly attempts: number;
1938
1938
  readonly id: string;
1939
1939
  readonly description: string;
1940
1940
  readonly title: string;
1941
1941
  readonly criteria: readonly {
1942
- readonly status: "pending" | "failed" | "passed";
1942
+ readonly status: "failed" | "pending" | "passed";
1943
1943
  readonly id: string;
1944
1944
  readonly description: string;
1945
1945
  readonly failureReason?: string | undefined;
@@ -2382,8 +2382,8 @@ declare const decodeSignal: (u: unknown, overrideOptions?: effect_SchemaAST.Pars
2382
2382
  } | {
2383
2383
  readonly _tag: "LoopComplete";
2384
2384
  } | {
2385
- readonly _tag: "SessionNameDefined";
2386
2385
  readonly name: string;
2386
+ readonly _tag: "SessionNameDefined";
2387
2387
  } | {
2388
2388
  readonly _tag: "Learning";
2389
2389
  readonly content: string;
@@ -2449,8 +2449,8 @@ declare const decodeSignalSync: (u: unknown, overrideOptions?: effect_SchemaAST.
2449
2449
  } | {
2450
2450
  readonly _tag: "LoopComplete";
2451
2451
  } | {
2452
- readonly _tag: "SessionNameDefined";
2453
2452
  readonly name: string;
2453
+ readonly _tag: "SessionNameDefined";
2454
2454
  } | {
2455
2455
  readonly _tag: "Learning";
2456
2456
  readonly content: string;
package/dist/index.js CHANGED
@@ -70,6 +70,8 @@ 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
75
 
74
76
  // ../sync/dist/index.js
75
77
  init_esm_shims();
@@ -149,12 +151,6 @@ var decodeSkillReposResponse = S.decodeUnknown(
149
151
  var CONVEX_URL_PROD = "https://groovy-mallard-649.convex.cloud";
150
152
  var CONVEX_URL_DEV = "https://majestic-gnu-964.convex.cloud";
151
153
  var getConvexUrl = (dev) => dev ? CONVEX_URL_DEV : CONVEX_URL_PROD;
152
- var NON_NPM_VERSION_PREFIXES = [
153
- "workspace:",
154
- "file:",
155
- "link:",
156
- "portal:"
157
- ];
158
154
 
159
155
  // ../sync/dist/chunk-3LE2T6D2.js
160
156
  init_esm_shims();
@@ -6116,7 +6112,7 @@ var findSkillRepos = (orgs, dev) => Effect.gen(function* () {
6116
6112
  return validated;
6117
6113
  });
6118
6114
 
6119
- // ../sync/dist/chunk-3CFQKJND.js
6115
+ // ../sync/dist/chunk-FXKCLMLB.js
6120
6116
  init_esm_shims();
6121
6117
  import { exec } from "child_process";
6122
6118
  import { promisify } from "util";
@@ -6126,7 +6122,7 @@ var installSingleSkill = (repo, options) => Effect2.tryPromise({
6126
6122
  try: async () => {
6127
6123
  const repoId = `${repo.owner}/${repo.repo}`;
6128
6124
  const globalFlag = options.global === true ? " --global" : "";
6129
- const command = `npx skills add ${repoId}${globalFlag}`;
6125
+ const command = `npx skills add ${repoId}${globalFlag} --yes`;
6130
6126
  await execAsync(command);
6131
6127
  return repoId;
6132
6128
  },
@@ -6180,202 +6176,18 @@ var resolvePackageOrgs = (packageNames, dev) => Effect3.gen(function* () {
6180
6176
  });
6181
6177
 
6182
6178
  // ../sync/dist/index.js
6183
- import { access, readdir, readFile } from "fs/promises";
6184
- import { dirname, join, resolve } from "path";
6185
6179
  import { Effect as Effect4, Schema as S4 } from "effect";
6186
- var validateFileExists = (filePath) => Effect4.tryPromise({
6187
- try: async () => {
6188
- const absolutePath = resolve(filePath);
6189
- await access(absolutePath);
6190
- return absolutePath;
6191
- },
6192
- catch: () => new PackageJsonError({
6193
- message: `File not found: ${filePath}`,
6194
- path: filePath
6195
- })
6196
- });
6197
- var readFileContent = (absolutePath) => Effect4.tryPromise({
6198
- try: async () => await readFile(absolutePath, "utf-8"),
6199
- catch: (error) => new PackageJsonError({
6200
- message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
6201
- path: absolutePath,
6202
- cause: error
6203
- })
6204
- });
6205
- var parseJson = (content, path2) => Effect4.try({
6206
- try: () => JSON.parse(content),
6207
- catch: (error) => new PackageJsonError({
6208
- message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
6209
- path: path2,
6210
- cause: error
6211
- })
6212
- });
6213
- var validatePackageJson = (json, path2) => S4.decodeUnknown(PackageJsonSchema)(json).pipe(
6214
- Effect4.mapError(
6215
- (error) => new SchemaValidationError({
6216
- message: "Invalid package.json structure",
6217
- context: path2,
6218
- cause: error
6219
- })
6220
- )
6221
- );
6222
- var isNonNpmDependency = (version2) => NON_NPM_VERSION_PREFIXES.some((prefix) => version2.startsWith(prefix));
6223
- var extractDependencies = (pkg) => {
6224
- const deps = pkg.dependencies ?? {};
6225
- const devDeps = pkg.devDependencies ?? {};
6226
- const allDeps = /* @__PURE__ */ new Map();
6227
- for (const [name, version2] of Object.entries(deps)) {
6228
- if (!isNonNpmDependency(version2)) {
6229
- allDeps.set(name, version2);
6230
- }
6231
- }
6232
- for (const [name, version2] of Object.entries(devDeps)) {
6233
- if (!(allDeps.has(name) || isNonNpmDependency(version2))) {
6234
- allDeps.set(name, version2);
6235
- }
6236
- }
6237
- return Array.from(allDeps.keys());
6238
- };
6239
- var emptyResult = (dependencies = [], orgs = [], packageJsonCount = 1) => ({
6240
- dependencies: [...dependencies],
6241
- orgs: [...orgs],
6242
- skillRepos: [],
6243
- installed: [],
6244
- packageJsonCount
6245
- });
6246
- var toMutableSkillRepos = (repos) => repos.map((r) => ({ owner: r.owner, repo: r.repo, githubUrl: r.githubUrl }));
6247
- var extractWorkspacePatterns = (pkg) => {
6248
- const workspaces = pkg.workspaces;
6249
- if (!workspaces) {
6250
- return [];
6251
- }
6252
- if (Array.isArray(workspaces)) {
6253
- return workspaces.filter((w) => typeof w === "string");
6254
- }
6255
- if (typeof workspaces === "object" && "packages" in workspaces) {
6256
- const packages = workspaces.packages;
6257
- if (Array.isArray(packages)) {
6258
- return packages.filter((w) => typeof w === "string");
6259
- }
6260
- }
6261
- return [];
6262
- };
6263
- var expandGlobPattern = async (rootDir, pattern) => {
6264
- if (pattern.endsWith("/*")) {
6265
- const baseDir = pattern.slice(0, -2);
6266
- const fullPath2 = join(rootDir, baseDir);
6267
- try {
6268
- const entries = await readdir(fullPath2, { withFileTypes: true });
6269
- return entries.filter((entry) => entry.isDirectory()).map((entry) => join(fullPath2, entry.name));
6270
- } catch {
6271
- return [];
6272
- }
6273
- }
6274
- if (pattern.endsWith("/**")) {
6275
- const baseDir = pattern.slice(0, -3);
6276
- const fullPath2 = join(rootDir, baseDir);
6277
- try {
6278
- const entries = await readdir(fullPath2, { withFileTypes: true });
6279
- return entries.filter((entry) => entry.isDirectory()).map((entry) => join(fullPath2, entry.name));
6280
- } catch {
6281
- return [];
6282
- }
6283
- }
6284
- const fullPath = join(rootDir, pattern);
6285
- try {
6286
- await access(fullPath);
6287
- return [fullPath];
6288
- } catch {
6289
- return [];
6290
- }
6291
- };
6292
- var discoverPackageJsonFiles = (rootPath, rootPkg) => Effect4.tryPromise({
6293
- try: async () => {
6294
- const rootDir = dirname(rootPath);
6295
- const patterns = extractWorkspacePatterns(rootPkg);
6296
- if (patterns.length === 0) {
6297
- return [rootPath];
6298
- }
6299
- const packageJsonPaths = [rootPath];
6300
- for (const pattern of patterns) {
6301
- const dirs = await expandGlobPattern(rootDir, pattern);
6302
- for (const dir of dirs) {
6303
- const pkgPath = join(dir, "package.json");
6304
- try {
6305
- await access(pkgPath);
6306
- packageJsonPaths.push(pkgPath);
6307
- } catch {
6308
- }
6309
- }
6310
- }
6311
- return packageJsonPaths;
6312
- },
6313
- catch: (error) => new PackageJsonError({
6314
- message: `Failed to discover workspace packages: ${error instanceof Error ? error.message : String(error)}`,
6315
- path: rootPath,
6316
- cause: error
6317
- })
6318
- });
6319
- var readPackageJson = (filePath) => Effect4.gen(function* () {
6320
- const content = yield* readFileContent(filePath);
6321
- const json = yield* parseJson(content, filePath);
6322
- return yield* validatePackageJson(json, filePath);
6323
- });
6324
- var sync = (packageJsonPath, options = {}) => Effect4.gen(function* () {
6325
- const absolutePath = yield* validateFileExists(packageJsonPath);
6326
- const rootPkg = yield* readPackageJson(absolutePath);
6327
- const packageJsonPaths = yield* discoverPackageJsonFiles(
6328
- absolutePath,
6329
- rootPkg
6330
- );
6331
- const packageJsonCount = packageJsonPaths.length;
6332
- const allDependencies = /* @__PURE__ */ new Set();
6333
- for (const pkgPath of packageJsonPaths) {
6334
- const pkg = yield* readPackageJson(pkgPath);
6335
- const deps = extractDependencies(pkg);
6336
- for (const dep of deps) {
6337
- allDependencies.add(dep);
6338
- }
6339
- }
6340
- const dependencies = Array.from(allDependencies);
6341
- if (dependencies.length === 0) {
6342
- return emptyResult([], [], packageJsonCount);
6343
- }
6344
- const packageOrgs = yield* resolvePackageOrgs(dependencies, options.dev);
6345
- const orgs = Array.from(
6346
- new Set(
6347
- packageOrgs.map((p) => p.githubOrg).filter((org) => org !== null)
6348
- )
6349
- );
6350
- if (orgs.length === 0) {
6351
- return emptyResult([...dependencies], [], packageJsonCount);
6352
- }
6353
- const skillRepos = yield* findSkillRepos(orgs, options.dev);
6354
- if (skillRepos.length === 0) {
6355
- return emptyResult([...dependencies], orgs, packageJsonCount);
6356
- }
6357
- const installed = yield* installSkills(skillRepos, {
6358
- dryRun: options.dryRun,
6359
- global: options.global
6360
- });
6361
- return {
6362
- dependencies: [...dependencies],
6363
- orgs,
6364
- skillRepos: toMutableSkillRepos(skillRepos),
6365
- installed: [...installed],
6366
- packageJsonCount
6367
- };
6368
- });
6369
6180
 
6370
6181
  // src/index.ts
6371
6182
  import { Command } from "commander";
6372
6183
  import { Effect as Effect29 } from "effect";
6184
+ import ora from "ora";
6373
6185
  import pc17 from "picocolors";
6374
6186
 
6375
6187
  // package.json
6376
6188
  var package_default = {
6377
6189
  name: "ferix-code",
6378
- version: "0.0.2-beta.23",
6190
+ version: "0.0.2-beta.25",
6379
6191
  description: "Composable RALPH loops for AI coding agents - v2 with Effect",
6380
6192
  type: "module",
6381
6193
  bin: {
@@ -6397,6 +6209,7 @@ var package_default = {
6397
6209
  commander: "^14.0.0",
6398
6210
  effect: "^3.19.15",
6399
6211
  "human-id": "^4.1.3",
6212
+ ora: "^9.1.0",
6400
6213
  picocolors: "^1.1.1"
6401
6214
  },
6402
6215
  devDependencies: {
@@ -8877,14 +8690,14 @@ import { Layer as Layer14 } from "effect";
8877
8690
  init_esm_shims();
8878
8691
  import { exec as exec2 } from "child_process";
8879
8692
  import {
8880
- access as access2,
8693
+ access,
8881
8694
  appendFile,
8882
8695
  copyFile,
8883
8696
  mkdir,
8884
- readFile as readFile2,
8697
+ readFile,
8885
8698
  rm
8886
8699
  } from "fs/promises";
8887
- import { dirname as dirname2, join as join2 } from "path";
8700
+ import { dirname, join } from "path";
8888
8701
  import { promisify as promisify2 } from "util";
8889
8702
  import { Effect as Effect8, Layer } from "effect";
8890
8703
 
@@ -8930,7 +8743,7 @@ var WORKTREES_DIR = ".ferix/worktrees";
8930
8743
  var GITDIR_REGEX = /^gitdir:\s*(.+)$/m;
8931
8744
  var BRANCH_PREFIX = "ferix";
8932
8745
  function getWorktreeDir(sessionId) {
8933
- return join2(process.cwd(), WORKTREES_DIR, sessionId);
8746
+ return join(process.cwd(), WORKTREES_DIR, sessionId);
8934
8747
  }
8935
8748
  function getBranchName(sessionId) {
8936
8749
  return `${BRANCH_PREFIX}/${sessionId}`;
@@ -8954,7 +8767,7 @@ function gitExec(command, cwd) {
8954
8767
  function directoryExists(dirPath) {
8955
8768
  return Effect8.tryPromise({
8956
8769
  try: async () => {
8957
- await access2(dirPath);
8770
+ await access(dirPath);
8958
8771
  return true;
8959
8772
  },
8960
8773
  catch: () => new Error("Directory does not exist")
@@ -8994,11 +8807,11 @@ function copyUntrackedFiles(worktreeDir) {
8994
8807
  return;
8995
8808
  }
8996
8809
  for (const file of untrackedFiles) {
8997
- const srcPath = join2(process.cwd(), file);
8998
- const destPath = join2(worktreeDir, file);
8810
+ const srcPath = join(process.cwd(), file);
8811
+ const destPath = join(worktreeDir, file);
8999
8812
  yield* Effect8.tryPromise({
9000
8813
  try: async () => {
9001
- await mkdir(dirname2(destPath), { recursive: true });
8814
+ await mkdir(dirname(destPath), { recursive: true });
9002
8815
  await copyFile(srcPath, destPath);
9003
8816
  },
9004
8817
  catch: () => new GitError({
@@ -9009,16 +8822,16 @@ function copyUntrackedFiles(worktreeDir) {
9009
8822
  }
9010
8823
  yield* Effect8.tryPromise({
9011
8824
  try: async () => {
9012
- const gitFilePath = join2(worktreeDir, ".git");
9013
- const gitFileContent = await readFile2(gitFilePath, "utf-8");
8825
+ const gitFilePath = join(worktreeDir, ".git");
8826
+ const gitFileContent = await readFile(gitFilePath, "utf-8");
9014
8827
  const gitDirMatch = gitFileContent.match(GITDIR_REGEX);
9015
8828
  const gitDirPath = gitDirMatch?.[1]?.trim();
9016
8829
  if (!gitDirPath) {
9017
8830
  return;
9018
8831
  }
9019
8832
  const gitDir = gitDirPath;
9020
- const excludePath = join2(gitDir, "info", "exclude");
9021
- await mkdir(dirname2(excludePath), { recursive: true });
8833
+ const excludePath = join(gitDir, "info", "exclude");
8834
+ await mkdir(dirname(excludePath), { recursive: true });
9022
8835
  const excludeContent = "\n# Untracked files copied from main worktree (auto-generated by ferix)\n" + untrackedFiles.join("\n") + "\n";
9023
8836
  await appendFile(excludePath, excludeContent);
9024
8837
  },
@@ -9033,7 +8846,7 @@ var make = {
9033
8846
  createWorktree: (sessionId, baseBranch) => Effect8.gen(function* () {
9034
8847
  const worktreeDir = getWorktreeDir(sessionId);
9035
8848
  const branchName = getBranchName(sessionId);
9036
- const worktreesBase = join2(process.cwd(), WORKTREES_DIR);
8849
+ const worktreesBase = join(process.cwd(), WORKTREES_DIR);
9037
8850
  yield* Effect8.tryPromise({
9038
8851
  try: () => mkdir(worktreesBase, { recursive: true }),
9039
8852
  catch: (error) => new GitError({
@@ -9409,8 +9222,8 @@ var MemoryGit = {
9409
9222
 
9410
9223
  // src/layers/guardrails/file-system.ts
9411
9224
  init_esm_shims();
9412
- import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
9413
- import { join as join3 } from "path";
9225
+ import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
9226
+ import { join as join2 } from "path";
9414
9227
  import { DateTime, Effect as Effect10, Layer as Layer3 } from "effect";
9415
9228
 
9416
9229
  // src/domain/index.ts
@@ -10432,10 +10245,10 @@ function ensureDir(dirPath) {
10432
10245
  }).pipe(Effect10.asVoid);
10433
10246
  }
10434
10247
  function getSessionDir(sessionId) {
10435
- return join3(process.cwd(), PLANS_DIR, sessionId);
10248
+ return join2(process.cwd(), PLANS_DIR, sessionId);
10436
10249
  }
10437
10250
  function getGuardrailsPath(sessionId) {
10438
- return join3(getSessionDir(sessionId), "guardrails.json");
10251
+ return join2(getSessionDir(sessionId), "guardrails.json");
10439
10252
  }
10440
10253
  function serializeGuardrails(guardrails) {
10441
10254
  return JSON.stringify(guardrails, null, 2);
@@ -10477,7 +10290,7 @@ var make2 = {
10477
10290
  const existing = yield* Effect10.tryPromise({
10478
10291
  try: async () => {
10479
10292
  try {
10480
- const content = await readFile3(guardrailsPath, "utf-8");
10293
+ const content = await readFile2(guardrailsPath, "utf-8");
10481
10294
  return content;
10482
10295
  } catch {
10483
10296
  return null;
@@ -10526,7 +10339,7 @@ var make2 = {
10526
10339
  const content = yield* Effect10.tryPromise({
10527
10340
  try: async () => {
10528
10341
  try {
10529
- return await readFile3(guardrailsPath, "utf-8");
10342
+ return await readFile2(guardrailsPath, "utf-8");
10530
10343
  } catch {
10531
10344
  return null;
10532
10345
  }
@@ -11081,8 +10894,8 @@ function createProviderLayer2(name) {
11081
10894
 
11082
10895
  // src/layers/plan/file-system.ts
11083
10896
  init_esm_shims();
11084
- import { access as access3, mkdir as mkdir3, readdir as readdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
11085
- import { join as join4 } from "path";
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";
11086
10899
  import { Effect as Effect16, Layer as Layer7 } from "effect";
11087
10900
 
11088
10901
  // src/services/plan-store.ts
@@ -11104,10 +10917,10 @@ function ensureDir2(dirPath) {
11104
10917
  }).pipe(Effect16.asVoid);
11105
10918
  }
11106
10919
  function getSessionDir2(sessionId) {
11107
- return join4(process.cwd(), PLANS_DIR2, sessionId);
10920
+ return join3(process.cwd(), PLANS_DIR2, sessionId);
11108
10921
  }
11109
10922
  function getPlanPath(sessionId, planId) {
11110
- return join4(getSessionDir2(sessionId), `${planId}.json`);
10923
+ return join3(getSessionDir2(sessionId), `${planId}.json`);
11111
10924
  }
11112
10925
  function generatePlanId(taskNumber) {
11113
10926
  return PlanId(`task-${taskNumber}`);
@@ -11147,7 +10960,7 @@ var make3 = {
11147
10960
  const existingPlans = yield* Effect16.tryPromise({
11148
10961
  try: async () => {
11149
10962
  try {
11150
- const files = await readdir2(sessionDir);
10963
+ const files = await readdir(sessionDir);
11151
10964
  return files.filter((f) => f.endsWith(".json")).length;
11152
10965
  } catch {
11153
10966
  return 0;
@@ -11175,7 +10988,7 @@ var make3 = {
11175
10988
  if (sessionId) {
11176
10989
  const planPath = getPlanPath(sessionId, planId);
11177
10990
  const content = yield* Effect16.tryPromise({
11178
- try: () => readFile4(planPath, "utf-8"),
10991
+ try: () => readFile3(planPath, "utf-8"),
11179
10992
  catch: (error) => new PlanStoreError({
11180
10993
  message: `Failed to read plan file: ${planPath}`,
11181
10994
  operation: "load",
@@ -11186,8 +10999,8 @@ var make3 = {
11186
10999
  }
11187
11000
  const sessionDirs = yield* Effect16.tryPromise({
11188
11001
  try: async () => {
11189
- const plansDir = join4(process.cwd(), PLANS_DIR2);
11190
- const dirs = await readdir2(plansDir);
11002
+ const plansDir = join3(process.cwd(), PLANS_DIR2);
11003
+ const dirs = await readdir(plansDir);
11191
11004
  return dirs;
11192
11005
  },
11193
11006
  catch: (error) => new PlanStoreError({
@@ -11200,7 +11013,7 @@ var make3 = {
11200
11013
  const planPath = getPlanPath(sid, planId);
11201
11014
  const exists = yield* Effect16.tryPromise({
11202
11015
  try: async () => {
11203
- await access3(planPath);
11016
+ await access2(planPath);
11204
11017
  return true;
11205
11018
  },
11206
11019
  catch: () => new PlanStoreError({
@@ -11210,7 +11023,7 @@ var make3 = {
11210
11023
  }).pipe(Effect16.orElseSucceed(() => false));
11211
11024
  if (exists) {
11212
11025
  const content = yield* Effect16.tryPromise({
11213
- try: () => readFile4(planPath, "utf-8"),
11026
+ try: () => readFile3(planPath, "utf-8"),
11214
11027
  catch: (error) => new PlanStoreError({
11215
11028
  message: `Failed to read plan file: ${planPath}`,
11216
11029
  operation: "load",
@@ -11243,7 +11056,7 @@ var make3 = {
11243
11056
  const files = yield* Effect16.tryPromise({
11244
11057
  try: async () => {
11245
11058
  try {
11246
- return await readdir2(sessionDir);
11059
+ return await readdir(sessionDir);
11247
11060
  } catch {
11248
11061
  return [];
11249
11062
  }
@@ -11351,8 +11164,8 @@ var MemoryPlan = {
11351
11164
 
11352
11165
  // src/layers/progress/file-system.ts
11353
11166
  init_esm_shims();
11354
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
11355
- import { join as join5 } from "path";
11167
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
11168
+ import { join as join4 } from "path";
11356
11169
  import { DateTime as DateTime3, Effect as Effect18, Layer as Layer9 } from "effect";
11357
11170
 
11358
11171
  // src/services/progress-store.ts
@@ -11374,10 +11187,10 @@ function ensureDir3(dirPath) {
11374
11187
  }).pipe(Effect18.asVoid);
11375
11188
  }
11376
11189
  function getSessionDir3(sessionId) {
11377
- return join5(process.cwd(), PLANS_DIR3, sessionId);
11190
+ return join4(process.cwd(), PLANS_DIR3, sessionId);
11378
11191
  }
11379
11192
  function getProgressPath(sessionId) {
11380
- return join5(getSessionDir3(sessionId), "progress.json");
11193
+ return join4(getSessionDir3(sessionId), "progress.json");
11381
11194
  }
11382
11195
  function serializeProgress(progress) {
11383
11196
  return JSON.stringify(progress, null, 2);
@@ -11419,7 +11232,7 @@ var make4 = {
11419
11232
  const existing = yield* Effect18.tryPromise({
11420
11233
  try: async () => {
11421
11234
  try {
11422
- const content = await readFile5(progressPath, "utf-8");
11235
+ const content = await readFile4(progressPath, "utf-8");
11423
11236
  return content;
11424
11237
  } catch {
11425
11238
  return null;
@@ -11464,7 +11277,7 @@ var make4 = {
11464
11277
  const content = yield* Effect18.tryPromise({
11465
11278
  try: async () => {
11466
11279
  try {
11467
- return await readFile5(progressPath, "utf-8");
11280
+ return await readFile4(progressPath, "utf-8");
11468
11281
  } catch {
11469
11282
  return null;
11470
11283
  }
@@ -11555,8 +11368,8 @@ var MemoryProgress = {
11555
11368
 
11556
11369
  // src/layers/session/file-system.ts
11557
11370
  init_esm_shims();
11558
- import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
11559
- import { join as join6 } from "path";
11371
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
11372
+ import { join as join5 } from "path";
11560
11373
  import { DateTime as DateTime5, Effect as Effect20, Layer as Layer11 } from "effect";
11561
11374
  import { humanId } from "human-id";
11562
11375
 
@@ -11583,7 +11396,7 @@ function ensureDir4(dirPath) {
11583
11396
  }).pipe(Effect20.asVoid);
11584
11397
  }
11585
11398
  function getSessionPath(sessionId) {
11586
- return join6(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
11399
+ return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
11587
11400
  }
11588
11401
  function serializeSession(session) {
11589
11402
  return JSON.stringify(session, null, 2);
@@ -11612,7 +11425,7 @@ function deserializeSession(json) {
11612
11425
  }
11613
11426
  var make5 = {
11614
11427
  create: (originalTask) => Effect20.gen(function* () {
11615
- const sessionsDir = join6(process.cwd(), SESSIONS_DIR);
11428
+ const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
11616
11429
  yield* ensureDir4(sessionsDir);
11617
11430
  const now = yield* DateTime5.now;
11618
11431
  const timestampMs = DateTime5.toEpochMillis(now);
@@ -11638,7 +11451,7 @@ var make5 = {
11638
11451
  get: (sessionId) => Effect20.gen(function* () {
11639
11452
  const sessionPath = getSessionPath(sessionId);
11640
11453
  const content = yield* Effect20.tryPromise({
11641
- try: () => readFile6(sessionPath, "utf-8"),
11454
+ try: () => readFile5(sessionPath, "utf-8"),
11642
11455
  catch: (error) => new SessionStoreError({
11643
11456
  message: `Failed to read session file: ${sessionPath}`,
11644
11457
  operation: "get",
@@ -12357,8 +12170,8 @@ import { DateTime as DateTime8, Effect as Effect25, pipe, Ref as Ref10, Stream a
12357
12170
 
12358
12171
  // src/layers/plan/task-generation.ts
12359
12172
  init_esm_shims();
12360
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
12361
- import { join as join7 } from "path";
12173
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
12174
+ import { join as join6 } from "path";
12362
12175
  import { Effect as Effect23 } from "effect";
12363
12176
  var PLANS_DIR4 = ".ferix/plans";
12364
12177
  function ensureDir5(dirPath) {
@@ -12372,10 +12185,10 @@ function ensureDir5(dirPath) {
12372
12185
  }).pipe(Effect23.asVoid);
12373
12186
  }
12374
12187
  function getSessionDir4(sessionId) {
12375
- return join7(process.cwd(), PLANS_DIR4, sessionId);
12188
+ return join6(process.cwd(), PLANS_DIR4, sessionId);
12376
12189
  }
12377
12190
  function getTasksMdPath(sessionId) {
12378
- return join7(getSessionDir4(sessionId), "tasks.md");
12191
+ return join6(getSessionDir4(sessionId), "tasks.md");
12379
12192
  }
12380
12193
  function writeTasksMd(sessionId, tasks) {
12381
12194
  return Effect23.gen(function* () {
@@ -13954,55 +13767,291 @@ program.command("run", { isDefault: true }).argument("<task>", "Task description
13954
13767
  process.exit(1);
13955
13768
  }
13956
13769
  });
13957
- 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) => {
13770
+ var NON_NPM_VERSION_PREFIXES2 = ["workspace:", "file:", "link:", "portal:"];
13771
+ var isNonNpmDependency = (version2) => NON_NPM_VERSION_PREFIXES2.some((prefix) => version2.startsWith(prefix));
13772
+ var extractDependencies = (pkg) => {
13773
+ const deps = pkg.dependencies ?? {};
13774
+ const devDeps = pkg.devDependencies ?? {};
13775
+ const allDeps = /* @__PURE__ */ new Map();
13776
+ for (const [name, version2] of Object.entries(deps)) {
13777
+ if (!isNonNpmDependency(version2)) {
13778
+ allDeps.set(name, version2);
13779
+ }
13780
+ }
13781
+ for (const [name, version2] of Object.entries(devDeps)) {
13782
+ if (!(allDeps.has(name) || isNonNpmDependency(version2))) {
13783
+ allDeps.set(name, version2);
13784
+ }
13785
+ }
13786
+ return Array.from(allDeps.keys());
13787
+ };
13788
+ var extractWorkspacePatterns = (pkg) => {
13789
+ const workspaces = pkg.workspaces;
13790
+ if (!workspaces) {
13791
+ return [];
13792
+ }
13793
+ if (Array.isArray(workspaces)) {
13794
+ return workspaces.filter((w) => typeof w === "string");
13795
+ }
13796
+ if (typeof workspaces === "object" && "packages" in workspaces) {
13797
+ const packages = workspaces.packages;
13798
+ if (Array.isArray(packages)) {
13799
+ return packages.filter((w) => typeof w === "string");
13800
+ }
13801
+ }
13802
+ return [];
13803
+ };
13804
+ var GLOB_SUFFIX_PATTERN = /\/\*+$/;
13805
+ var expandGlobPattern = async (rootDir, pattern) => {
13806
+ if (pattern.endsWith("/*") || pattern.endsWith("/**")) {
13807
+ const baseDir = pattern.replace(GLOB_SUFFIX_PATTERN, "");
13808
+ const fullPath2 = join7(rootDir, baseDir);
13809
+ try {
13810
+ const entries = await readdir2(fullPath2, { withFileTypes: true });
13811
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => join7(fullPath2, entry.name));
13812
+ } catch {
13813
+ return [];
13814
+ }
13815
+ }
13816
+ const fullPath = join7(rootDir, pattern);
13958
13817
  try {
13959
- console.log("\nScanning for package.json files...");
13960
- const result = await Effect29.runPromise(
13961
- sync(options.path, {
13962
- dryRun: options.dryRun ?? false,
13963
- global: options.global ?? false,
13964
- dev: options.dev ?? false
13965
- })
13966
- );
13967
- if (options.dev) {
13968
- console.log(pc17.yellow("Using development server"));
13818
+ await access3(fullPath);
13819
+ return [fullPath];
13820
+ } catch {
13821
+ return [];
13822
+ }
13823
+ };
13824
+ var readPackageJsonFile = async (filePath) => {
13825
+ const content = await readFile7(filePath, "utf-8");
13826
+ return JSON.parse(content);
13827
+ };
13828
+ var discoverPackageJsonFiles = async (rootPath, rootPkg) => {
13829
+ const rootDir = dirname2(rootPath);
13830
+ const patterns = extractWorkspacePatterns(rootPkg);
13831
+ if (patterns.length === 0) {
13832
+ return [rootPath];
13833
+ }
13834
+ const packageJsonPaths = [rootPath];
13835
+ for (const pattern of patterns) {
13836
+ const dirs = await expandGlobPattern(rootDir, pattern);
13837
+ for (const dir of dirs) {
13838
+ const pkgPath = join7(dir, "package.json");
13839
+ try {
13840
+ await access3(pkgPath);
13841
+ packageJsonPaths.push(pkgPath);
13842
+ } catch {
13843
+ }
13969
13844
  }
13970
- const packageMsg = result.packageJsonCount > 1 ? `Found ${pc17.bold(String(result.packageJsonCount))} package.json files (monorepo)` : "Found 1 package.json file";
13971
- console.log(packageMsg);
13972
- console.log(
13973
- `Found ${pc17.bold(String(result.dependencies.length))} dependencies (deduplicated)`
13974
- );
13975
- console.log(
13976
- `Resolved ${pc17.bold(String(result.orgs.length))} unique GitHub organizations`
13977
- );
13978
- if (result.skillRepos.length === 0) {
13979
- console.log(
13980
- `
13981
- ${pc17.yellow("No skill repositories found for your dependencies.")}`
13982
- );
13983
- return;
13845
+ }
13846
+ return packageJsonPaths;
13847
+ };
13848
+ var SYMBOLS = {
13849
+ success: pc17.green("\u2713"),
13850
+ error: pc17.red("\u2717")
13851
+ };
13852
+ var printHeader = (title, subtitle) => {
13853
+ console.log();
13854
+ console.log(pc17.bold(pc17.cyan(` ${title}`)));
13855
+ if (subtitle) {
13856
+ console.log(pc17.dim(` ${subtitle}`));
13857
+ }
13858
+ console.log();
13859
+ };
13860
+ var printSection = (title) => {
13861
+ console.log();
13862
+ console.log(pc17.bold(` ${title}`));
13863
+ };
13864
+ var printRepo = (owner, repo, index) => {
13865
+ const num = pc17.dim(`${String(index + 1).padStart(2, " ")}.`);
13866
+ console.log(` ${num} ${pc17.cyan(owner)}${pc17.dim("/")}${pc17.white(repo)}`);
13867
+ };
13868
+ var printSuccess = (message) => {
13869
+ console.log(` ${SYMBOLS.success} ${pc17.green(message)}`);
13870
+ };
13871
+ var printError = (message) => {
13872
+ console.log(` ${SYMBOLS.error} ${pc17.red(message)}`);
13873
+ };
13874
+ var printHint = (message) => {
13875
+ console.log(pc17.dim(` ${message}`));
13876
+ };
13877
+ var createSpinner = (text) => {
13878
+ return ora({
13879
+ text,
13880
+ prefixText: " ",
13881
+ spinner: "dots"
13882
+ });
13883
+ };
13884
+ var discoverPackages = async (state, packagePath) => {
13885
+ state.spinner = createSpinner("Scanning for package.json files...").start();
13886
+ const absolutePath = resolve(packagePath);
13887
+ await access3(absolutePath);
13888
+ const rootPkg = await readPackageJsonFile(absolutePath);
13889
+ state.packageJsonPaths = await discoverPackageJsonFiles(
13890
+ absolutePath,
13891
+ rootPkg
13892
+ );
13893
+ const isMonorepo = state.packageJsonPaths.length > 1;
13894
+ state.spinner.succeed(
13895
+ isMonorepo ? `Found ${pc17.bold(String(state.packageJsonPaths.length))} package.json files ${pc17.dim("(monorepo)")}` : "Found package.json"
13896
+ );
13897
+ return true;
13898
+ };
13899
+ var extractAllDependencies = async (state) => {
13900
+ state.spinner = createSpinner("Extracting dependencies...").start();
13901
+ const allDependencies = /* @__PURE__ */ new Set();
13902
+ for (const pkgPath of state.packageJsonPaths) {
13903
+ const pkg = await readPackageJsonFile(pkgPath);
13904
+ for (const dep of extractDependencies(pkg)) {
13905
+ allDependencies.add(dep);
13984
13906
  }
13985
- console.log(
13986
- `
13987
- Found ${pc17.bold(String(result.skillRepos.length))} skill repositories:
13988
- `
13907
+ }
13908
+ state.dependencies = Array.from(allDependencies);
13909
+ if (state.dependencies.length === 0) {
13910
+ state.spinner.warn("No dependencies found");
13911
+ return false;
13912
+ }
13913
+ state.spinner.succeed(
13914
+ `Found ${pc17.bold(String(state.dependencies.length))} unique dependencies`
13915
+ );
13916
+ return true;
13917
+ };
13918
+ var resolveOrganizations = async (state, isDev) => {
13919
+ state.spinner = createSpinner("Resolving GitHub organizations...").start();
13920
+ const packageOrgs = await Effect29.runPromise(
13921
+ resolvePackageOrgs(state.dependencies, isDev)
13922
+ );
13923
+ state.orgs = Array.from(
13924
+ new Set(
13925
+ packageOrgs.map((p) => p.githubOrg).filter((org) => org !== null)
13926
+ )
13927
+ );
13928
+ if (state.orgs.length === 0) {
13929
+ state.spinner.warn("No GitHub organizations found");
13930
+ printHint("Your dependencies don't have linked GitHub organizations.");
13931
+ return false;
13932
+ }
13933
+ state.spinner.succeed(
13934
+ `Resolved ${pc17.bold(String(state.orgs.length))} GitHub organizations`
13935
+ );
13936
+ return true;
13937
+ };
13938
+ var findRepositories = async (state, isDev) => {
13939
+ state.spinner = createSpinner("Searching for skill repositories...").start();
13940
+ state.skillRepos = await Effect29.runPromise(findSkillRepos(state.orgs, isDev));
13941
+ if (state.skillRepos.length === 0) {
13942
+ state.spinner.warn("No skill repositories found");
13943
+ printHint(
13944
+ "None of your dependency organizations have published skills yet."
13989
13945
  );
13990
- for (const repo of result.skillRepos) {
13991
- console.log(` ${pc17.green("\u2022")} ${repo.owner}/${repo.repo}`);
13946
+ return false;
13947
+ }
13948
+ state.spinner.succeed(
13949
+ `Found ${pc17.bold(String(state.skillRepos.length))} skill ${state.skillRepos.length === 1 ? "repository" : "repositories"}`
13950
+ );
13951
+ return true;
13952
+ };
13953
+ var displayRepositories = (skillRepos) => {
13954
+ printSection("Repositories");
13955
+ console.log();
13956
+ for (let i = 0; i < skillRepos.length; i++) {
13957
+ const repo = skillRepos[i];
13958
+ if (repo) {
13959
+ printRepo(repo.owner, repo.repo, i);
13992
13960
  }
13993
- if (options.dryRun) {
13994
- console.log(
13995
- `
13996
- ${pc17.dim("Run without --dry-run to install these skills.")}`
13961
+ }
13962
+ };
13963
+ var installRepositories = async (state, isGlobal) => {
13964
+ printSection("Installing");
13965
+ console.log();
13966
+ const installed = [];
13967
+ const failed = [];
13968
+ for (const repo of state.skillRepos) {
13969
+ const repoId = `${repo.owner}/${repo.repo}`;
13970
+ state.spinner = createSpinner(
13971
+ `Installing ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}...`
13972
+ ).start();
13973
+ try {
13974
+ await Effect29.runPromise(
13975
+ installSkills([repo], { dryRun: false, global: isGlobal })
13997
13976
  );
13998
- } else {
13999
- console.log(
14000
- `
14001
- ${pc17.green("\u2713")} Installed ${pc17.bold(String(result.installed.length))} skill repositories`
13977
+ state.spinner.succeed(
13978
+ `Installed ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}`
14002
13979
  );
13980
+ installed.push(repoId);
13981
+ } catch {
13982
+ state.spinner.fail(
13983
+ `Failed to install ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}`
13984
+ );
13985
+ failed.push(repoId);
14003
13986
  }
13987
+ }
13988
+ return { installed, failed };
13989
+ };
13990
+ var displaySummary = (installed, failed, isGlobal) => {
13991
+ printSection("Summary");
13992
+ console.log();
13993
+ if (installed.length > 0) {
13994
+ printSuccess(
13995
+ `${installed.length} skill ${installed.length === 1 ? "repository" : "repositories"} installed successfully`
13996
+ );
13997
+ }
13998
+ if (failed.length > 0) {
13999
+ printError(
14000
+ `${failed.length} ${failed.length === 1 ? "installation" : "installations"} failed`
14001
+ );
14002
+ }
14003
+ const installLocation = isGlobal ? "globally" : "in project";
14004
+ printHint(`Skills installed ${installLocation}.`);
14005
+ console.log();
14006
+ };
14007
+ var runSyncCommand = async (options) => {
14008
+ const { path: packagePath, dryRun, global: isGlobal, dev: isDev } = options;
14009
+ printHeader(
14010
+ "Ferix Sync",
14011
+ isDev ? pc17.yellow("Development Mode") : "Discovering skills from your dependencies"
14012
+ );
14013
+ const state = {
14014
+ spinner: void 0,
14015
+ packageJsonPaths: [],
14016
+ dependencies: [],
14017
+ orgs: [],
14018
+ skillRepos: []
14019
+ };
14020
+ await discoverPackages(state, packagePath);
14021
+ const hasDeps = await extractAllDependencies(state);
14022
+ if (!hasDeps) {
14023
+ return;
14024
+ }
14025
+ const hasOrgs = await resolveOrganizations(state, isDev);
14026
+ if (!hasOrgs) {
14027
+ return;
14028
+ }
14029
+ const hasRepos = await findRepositories(state, isDev);
14030
+ if (!hasRepos) {
14031
+ return;
14032
+ }
14033
+ displayRepositories(state.skillRepos);
14034
+ if (dryRun) {
14035
+ console.log();
14036
+ printHint(`Run without ${pc17.cyan("--dry-run")} to install these skills.`);
14037
+ console.log();
14038
+ return;
14039
+ }
14040
+ const { installed, failed } = await installRepositories(state, isGlobal);
14041
+ displaySummary(installed, failed, isGlobal);
14042
+ };
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) => {
14044
+ try {
14045
+ await runSyncCommand({
14046
+ path: options.path,
14047
+ dryRun: options.dryRun ?? false,
14048
+ global: options.global ?? false,
14049
+ dev: options.dev ?? false
14050
+ });
14004
14051
  } catch (error) {
14005
- console.error(pc17.red("Error:"), error);
14052
+ console.log();
14053
+ printError(error instanceof Error ? error.message : String(error));
14054
+ console.log();
14006
14055
  process.exit(1);
14007
14056
  }
14008
14057
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ferix-code",
3
- "version": "0.0.2-beta.23",
3
+ "version": "0.0.2-beta.25",
4
4
  "description": "Composable RALPH loops for AI coding agents - v2 with Effect",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,7 @@
22
22
  "commander": "^14.0.0",
23
23
  "effect": "^3.19.15",
24
24
  "human-id": "^4.1.3",
25
+ "ora": "^9.1.0",
25
26
  "picocolors": "^1.1.1"
26
27
  },
27
28
  "devDependencies": {