ferix-code 0.0.2-beta.24 → 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 +19 -19
- package/dist/index.js +576 -345
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -70,16 +70,28 @@ var init_registry = __esm({
|
|
|
70
70
|
|
|
71
71
|
// src/index.ts
|
|
72
72
|
init_esm_shims();
|
|
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";
|
|
73
76
|
|
|
74
77
|
// ../sync/dist/index.js
|
|
75
78
|
init_esm_shims();
|
|
76
79
|
|
|
77
|
-
// ../sync/dist/chunk-
|
|
80
|
+
// ../sync/dist/chunk-5E4WZI4D.js
|
|
78
81
|
init_esm_shims();
|
|
79
82
|
|
|
80
|
-
// ../sync/dist/chunk-
|
|
83
|
+
// ../sync/dist/chunk-BNNEGPUU.js
|
|
81
84
|
init_esm_shims();
|
|
82
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
|
+
);
|
|
83
95
|
var PackageNameSchema = S.String.pipe(
|
|
84
96
|
S.pattern(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/),
|
|
85
97
|
S.brand("PackageName")
|
|
@@ -130,7 +142,8 @@ var SyncOptionsSchema = S.Struct({
|
|
|
130
142
|
});
|
|
131
143
|
var InstallOptionsSchema = S.Struct({
|
|
132
144
|
dryRun: S.optional(S.Boolean),
|
|
133
|
-
global: S.optional(S.Boolean)
|
|
145
|
+
global: S.optional(S.Boolean),
|
|
146
|
+
agents: S.optional(S.Array(AgentNameSchema))
|
|
134
147
|
});
|
|
135
148
|
var SyncResultSchema = S.Struct({
|
|
136
149
|
dependencies: S.Array(S.String),
|
|
@@ -149,12 +162,6 @@ var decodeSkillReposResponse = S.decodeUnknown(
|
|
|
149
162
|
var CONVEX_URL_PROD = "https://groovy-mallard-649.convex.cloud";
|
|
150
163
|
var CONVEX_URL_DEV = "https://majestic-gnu-964.convex.cloud";
|
|
151
164
|
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
165
|
|
|
159
166
|
// ../sync/dist/chunk-3LE2T6D2.js
|
|
160
167
|
init_esm_shims();
|
|
@@ -6086,7 +6093,7 @@ var wrapper_default = import_websocket.default;
|
|
|
6086
6093
|
var nodeWebSocket = wrapper_default;
|
|
6087
6094
|
setDefaultWebSocketConstructor(nodeWebSocket);
|
|
6088
6095
|
|
|
6089
|
-
// ../sync/dist/chunk-
|
|
6096
|
+
// ../sync/dist/chunk-5E4WZI4D.js
|
|
6090
6097
|
import { Effect, Schema as S2 } from "effect";
|
|
6091
6098
|
var findSkillRepos = (orgs, dev) => Effect.gen(function* () {
|
|
6092
6099
|
const response = yield* Effect.tryPromise({
|
|
@@ -6116,7 +6123,7 @@ var findSkillRepos = (orgs, dev) => Effect.gen(function* () {
|
|
|
6116
6123
|
return validated;
|
|
6117
6124
|
});
|
|
6118
6125
|
|
|
6119
|
-
// ../sync/dist/chunk-
|
|
6126
|
+
// ../sync/dist/chunk-EDGIQ4IK.js
|
|
6120
6127
|
init_esm_shims();
|
|
6121
6128
|
import { exec } from "child_process";
|
|
6122
6129
|
import { promisify } from "util";
|
|
@@ -6126,7 +6133,8 @@ var installSingleSkill = (repo, options) => Effect2.tryPromise({
|
|
|
6126
6133
|
try: async () => {
|
|
6127
6134
|
const repoId = `${repo.owner}/${repo.repo}`;
|
|
6128
6135
|
const globalFlag = options.global === true ? " --global" : "";
|
|
6129
|
-
const
|
|
6136
|
+
const agentFlag = options.agents && options.agents.length > 0 ? ` --agent ${options.agents.join(" ")}` : "";
|
|
6137
|
+
const command = `npx skills add ${repoId}${globalFlag}${agentFlag} --yes`;
|
|
6130
6138
|
await execAsync(command);
|
|
6131
6139
|
return repoId;
|
|
6132
6140
|
},
|
|
@@ -6148,7 +6156,7 @@ var installSkills = (repos, options = {}) => {
|
|
|
6148
6156
|
});
|
|
6149
6157
|
};
|
|
6150
6158
|
|
|
6151
|
-
// ../sync/dist/chunk-
|
|
6159
|
+
// ../sync/dist/chunk-C6TYE4HI.js
|
|
6152
6160
|
init_esm_shims();
|
|
6153
6161
|
import { Effect as Effect3, Schema as S3 } from "effect";
|
|
6154
6162
|
var resolvePackageOrgs = (packageNames, dev) => Effect3.gen(function* () {
|
|
@@ -6180,202 +6188,60 @@ var resolvePackageOrgs = (packageNames, dev) => Effect3.gen(function* () {
|
|
|
6180
6188
|
});
|
|
6181
6189
|
|
|
6182
6190
|
// ../sync/dist/index.js
|
|
6183
|
-
import {
|
|
6184
|
-
import {
|
|
6185
|
-
import {
|
|
6186
|
-
|
|
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({
|
|
6187
6208
|
try: async () => {
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
return absolutePath;
|
|
6209
|
+
await access(dirPath);
|
|
6210
|
+
return true;
|
|
6191
6211
|
},
|
|
6192
|
-
catch: () =>
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
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
|
-
})
|
|
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)
|
|
6220
6229
|
)
|
|
6221
6230
|
);
|
|
6222
|
-
var
|
|
6223
|
-
|
|
6224
|
-
|
|
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
|
-
});
|
|
6231
|
+
var validateAgentNames = (agents) => agents.filter(
|
|
6232
|
+
(agent) => !SUPPORTED_AGENTS.includes(agent)
|
|
6233
|
+
);
|
|
6369
6234
|
|
|
6370
6235
|
// src/index.ts
|
|
6371
6236
|
import { Command } from "commander";
|
|
6372
|
-
import { Effect as
|
|
6237
|
+
import { Effect as Effect30 } from "effect";
|
|
6238
|
+
import ora from "ora";
|
|
6373
6239
|
import pc17 from "picocolors";
|
|
6374
6240
|
|
|
6375
6241
|
// package.json
|
|
6376
6242
|
var package_default = {
|
|
6377
6243
|
name: "ferix-code",
|
|
6378
|
-
version: "0.0.2-beta.
|
|
6244
|
+
version: "0.0.2-beta.26",
|
|
6379
6245
|
description: "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
6380
6246
|
type: "module",
|
|
6381
6247
|
bin: {
|
|
@@ -6394,9 +6260,11 @@ var package_default = {
|
|
|
6394
6260
|
bump: "npm version prerelease --preid=beta --workspaces=false && bun run build && bun publish --tag beta"
|
|
6395
6261
|
},
|
|
6396
6262
|
dependencies: {
|
|
6263
|
+
"@clack/prompts": "catalog:",
|
|
6397
6264
|
commander: "^14.0.0",
|
|
6398
6265
|
effect: "^3.19.15",
|
|
6399
6266
|
"human-id": "^4.1.3",
|
|
6267
|
+
ora: "^9.1.0",
|
|
6400
6268
|
picocolors: "^1.1.1"
|
|
6401
6269
|
},
|
|
6402
6270
|
devDependencies: {
|
|
@@ -6423,7 +6291,7 @@ var package_default = {
|
|
|
6423
6291
|
|
|
6424
6292
|
// src/program.ts
|
|
6425
6293
|
init_esm_shims();
|
|
6426
|
-
import { Effect as
|
|
6294
|
+
import { Effect as Effect29, Stream as Stream10 } from "effect";
|
|
6427
6295
|
|
|
6428
6296
|
// src/consumers/index.ts
|
|
6429
6297
|
init_esm_shims();
|
|
@@ -8881,10 +8749,10 @@ import {
|
|
|
8881
8749
|
appendFile,
|
|
8882
8750
|
copyFile,
|
|
8883
8751
|
mkdir,
|
|
8884
|
-
readFile
|
|
8752
|
+
readFile,
|
|
8885
8753
|
rm
|
|
8886
8754
|
} from "fs/promises";
|
|
8887
|
-
import { dirname
|
|
8755
|
+
import { dirname, join as join2 } from "path";
|
|
8888
8756
|
import { promisify as promisify2 } from "util";
|
|
8889
8757
|
import { Effect as Effect8, Layer } from "effect";
|
|
8890
8758
|
|
|
@@ -8951,7 +8819,7 @@ function gitExec(command, cwd) {
|
|
|
8951
8819
|
}
|
|
8952
8820
|
});
|
|
8953
8821
|
}
|
|
8954
|
-
function
|
|
8822
|
+
function directoryExists2(dirPath) {
|
|
8955
8823
|
return Effect8.tryPromise({
|
|
8956
8824
|
try: async () => {
|
|
8957
8825
|
await access2(dirPath);
|
|
@@ -8998,7 +8866,7 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
8998
8866
|
const destPath = join2(worktreeDir, file);
|
|
8999
8867
|
yield* Effect8.tryPromise({
|
|
9000
8868
|
try: async () => {
|
|
9001
|
-
await mkdir(
|
|
8869
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
9002
8870
|
await copyFile(srcPath, destPath);
|
|
9003
8871
|
},
|
|
9004
8872
|
catch: () => new GitError({
|
|
@@ -9010,7 +8878,7 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
9010
8878
|
yield* Effect8.tryPromise({
|
|
9011
8879
|
try: async () => {
|
|
9012
8880
|
const gitFilePath = join2(worktreeDir, ".git");
|
|
9013
|
-
const gitFileContent = await
|
|
8881
|
+
const gitFileContent = await readFile(gitFilePath, "utf-8");
|
|
9014
8882
|
const gitDirMatch = gitFileContent.match(GITDIR_REGEX);
|
|
9015
8883
|
const gitDirPath = gitDirMatch?.[1]?.trim();
|
|
9016
8884
|
if (!gitDirPath) {
|
|
@@ -9018,7 +8886,7 @@ function copyUntrackedFiles(worktreeDir) {
|
|
|
9018
8886
|
}
|
|
9019
8887
|
const gitDir = gitDirPath;
|
|
9020
8888
|
const excludePath = join2(gitDir, "info", "exclude");
|
|
9021
|
-
await mkdir(
|
|
8889
|
+
await mkdir(dirname(excludePath), { recursive: true });
|
|
9022
8890
|
const excludeContent = "\n# Untracked files copied from main worktree (auto-generated by ferix)\n" + untrackedFiles.join("\n") + "\n";
|
|
9023
8891
|
await appendFile(excludePath, excludeContent);
|
|
9024
8892
|
},
|
|
@@ -9042,7 +8910,7 @@ var make = {
|
|
|
9042
8910
|
cause: error
|
|
9043
8911
|
})
|
|
9044
8912
|
});
|
|
9045
|
-
const exists = yield*
|
|
8913
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9046
8914
|
if (exists) {
|
|
9047
8915
|
return worktreeDir;
|
|
9048
8916
|
}
|
|
@@ -9063,7 +8931,7 @@ var make = {
|
|
|
9063
8931
|
removeWorktree: (sessionId) => Effect8.gen(function* () {
|
|
9064
8932
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9065
8933
|
const branchName = getBranchName(sessionId);
|
|
9066
|
-
const exists = yield*
|
|
8934
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9067
8935
|
if (!exists) {
|
|
9068
8936
|
return;
|
|
9069
8937
|
}
|
|
@@ -9096,7 +8964,7 @@ var make = {
|
|
|
9096
8964
|
}),
|
|
9097
8965
|
removeWorktreeKeepBranch: (sessionId) => Effect8.gen(function* () {
|
|
9098
8966
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9099
|
-
const exists = yield*
|
|
8967
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9100
8968
|
if (!exists) {
|
|
9101
8969
|
return;
|
|
9102
8970
|
}
|
|
@@ -9126,12 +8994,12 @@ var make = {
|
|
|
9126
8994
|
}),
|
|
9127
8995
|
getWorktreePath: (sessionId) => Effect8.gen(function* () {
|
|
9128
8996
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9129
|
-
const exists = yield*
|
|
8997
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9130
8998
|
return exists ? worktreeDir : void 0;
|
|
9131
8999
|
}),
|
|
9132
9000
|
commitChanges: (sessionId, message) => Effect8.gen(function* () {
|
|
9133
9001
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9134
|
-
const exists = yield*
|
|
9002
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9135
9003
|
if (!exists) {
|
|
9136
9004
|
return yield* Effect8.fail(
|
|
9137
9005
|
new GitError({
|
|
@@ -9187,7 +9055,7 @@ var make = {
|
|
|
9187
9055
|
}),
|
|
9188
9056
|
pushBranch: (sessionId) => Effect8.gen(function* () {
|
|
9189
9057
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9190
|
-
const exists = yield*
|
|
9058
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9191
9059
|
if (!exists) {
|
|
9192
9060
|
return yield* Effect8.fail(
|
|
9193
9061
|
new GitError({
|
|
@@ -9208,7 +9076,7 @@ var make = {
|
|
|
9208
9076
|
}),
|
|
9209
9077
|
createPR: (sessionId, title, body, baseBranch) => Effect8.gen(function* () {
|
|
9210
9078
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9211
|
-
const exists = yield*
|
|
9079
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9212
9080
|
if (!exists) {
|
|
9213
9081
|
return yield* Effect8.fail(
|
|
9214
9082
|
new GitError({
|
|
@@ -9246,7 +9114,7 @@ var make = {
|
|
|
9246
9114
|
renameBranch: (sessionId, displayName) => Effect8.gen(function* () {
|
|
9247
9115
|
const worktreeDir = getWorktreeDir(sessionId);
|
|
9248
9116
|
const oldBranchName = getBranchName(sessionId);
|
|
9249
|
-
const exists = yield*
|
|
9117
|
+
const exists = yield* directoryExists2(worktreeDir);
|
|
9250
9118
|
if (!exists) {
|
|
9251
9119
|
return yield* Effect8.fail(
|
|
9252
9120
|
new GitError({
|
|
@@ -9409,7 +9277,7 @@ var MemoryGit = {
|
|
|
9409
9277
|
|
|
9410
9278
|
// src/layers/guardrails/file-system.ts
|
|
9411
9279
|
init_esm_shims();
|
|
9412
|
-
import { mkdir as mkdir2, readFile as
|
|
9280
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
|
|
9413
9281
|
import { join as join3 } from "path";
|
|
9414
9282
|
import { DateTime, Effect as Effect10, Layer as Layer3 } from "effect";
|
|
9415
9283
|
|
|
@@ -10477,7 +10345,7 @@ var make2 = {
|
|
|
10477
10345
|
const existing = yield* Effect10.tryPromise({
|
|
10478
10346
|
try: async () => {
|
|
10479
10347
|
try {
|
|
10480
|
-
const content = await
|
|
10348
|
+
const content = await readFile2(guardrailsPath, "utf-8");
|
|
10481
10349
|
return content;
|
|
10482
10350
|
} catch {
|
|
10483
10351
|
return null;
|
|
@@ -10526,7 +10394,7 @@ var make2 = {
|
|
|
10526
10394
|
const content = yield* Effect10.tryPromise({
|
|
10527
10395
|
try: async () => {
|
|
10528
10396
|
try {
|
|
10529
|
-
return await
|
|
10397
|
+
return await readFile2(guardrailsPath, "utf-8");
|
|
10530
10398
|
} catch {
|
|
10531
10399
|
return null;
|
|
10532
10400
|
}
|
|
@@ -11081,7 +10949,7 @@ function createProviderLayer2(name) {
|
|
|
11081
10949
|
|
|
11082
10950
|
// src/layers/plan/file-system.ts
|
|
11083
10951
|
init_esm_shims();
|
|
11084
|
-
import { access as access3, mkdir as mkdir3, readdir
|
|
10952
|
+
import { access as access3, mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
11085
10953
|
import { join as join4 } from "path";
|
|
11086
10954
|
import { Effect as Effect16, Layer as Layer7 } from "effect";
|
|
11087
10955
|
|
|
@@ -11147,7 +11015,7 @@ var make3 = {
|
|
|
11147
11015
|
const existingPlans = yield* Effect16.tryPromise({
|
|
11148
11016
|
try: async () => {
|
|
11149
11017
|
try {
|
|
11150
|
-
const files = await
|
|
11018
|
+
const files = await readdir(sessionDir);
|
|
11151
11019
|
return files.filter((f) => f.endsWith(".json")).length;
|
|
11152
11020
|
} catch {
|
|
11153
11021
|
return 0;
|
|
@@ -11175,7 +11043,7 @@ var make3 = {
|
|
|
11175
11043
|
if (sessionId) {
|
|
11176
11044
|
const planPath = getPlanPath(sessionId, planId);
|
|
11177
11045
|
const content = yield* Effect16.tryPromise({
|
|
11178
|
-
try: () =>
|
|
11046
|
+
try: () => readFile3(planPath, "utf-8"),
|
|
11179
11047
|
catch: (error) => new PlanStoreError({
|
|
11180
11048
|
message: `Failed to read plan file: ${planPath}`,
|
|
11181
11049
|
operation: "load",
|
|
@@ -11187,7 +11055,7 @@ var make3 = {
|
|
|
11187
11055
|
const sessionDirs = yield* Effect16.tryPromise({
|
|
11188
11056
|
try: async () => {
|
|
11189
11057
|
const plansDir = join4(process.cwd(), PLANS_DIR2);
|
|
11190
|
-
const dirs = await
|
|
11058
|
+
const dirs = await readdir(plansDir);
|
|
11191
11059
|
return dirs;
|
|
11192
11060
|
},
|
|
11193
11061
|
catch: (error) => new PlanStoreError({
|
|
@@ -11210,7 +11078,7 @@ var make3 = {
|
|
|
11210
11078
|
}).pipe(Effect16.orElseSucceed(() => false));
|
|
11211
11079
|
if (exists) {
|
|
11212
11080
|
const content = yield* Effect16.tryPromise({
|
|
11213
|
-
try: () =>
|
|
11081
|
+
try: () => readFile3(planPath, "utf-8"),
|
|
11214
11082
|
catch: (error) => new PlanStoreError({
|
|
11215
11083
|
message: `Failed to read plan file: ${planPath}`,
|
|
11216
11084
|
operation: "load",
|
|
@@ -11243,7 +11111,7 @@ var make3 = {
|
|
|
11243
11111
|
const files = yield* Effect16.tryPromise({
|
|
11244
11112
|
try: async () => {
|
|
11245
11113
|
try {
|
|
11246
|
-
return await
|
|
11114
|
+
return await readdir(sessionDir);
|
|
11247
11115
|
} catch {
|
|
11248
11116
|
return [];
|
|
11249
11117
|
}
|
|
@@ -11351,7 +11219,7 @@ var MemoryPlan = {
|
|
|
11351
11219
|
|
|
11352
11220
|
// src/layers/progress/file-system.ts
|
|
11353
11221
|
init_esm_shims();
|
|
11354
|
-
import { mkdir as mkdir4, readFile as
|
|
11222
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
11355
11223
|
import { join as join5 } from "path";
|
|
11356
11224
|
import { DateTime as DateTime3, Effect as Effect18, Layer as Layer9 } from "effect";
|
|
11357
11225
|
|
|
@@ -11419,7 +11287,7 @@ var make4 = {
|
|
|
11419
11287
|
const existing = yield* Effect18.tryPromise({
|
|
11420
11288
|
try: async () => {
|
|
11421
11289
|
try {
|
|
11422
|
-
const content = await
|
|
11290
|
+
const content = await readFile4(progressPath, "utf-8");
|
|
11423
11291
|
return content;
|
|
11424
11292
|
} catch {
|
|
11425
11293
|
return null;
|
|
@@ -11464,7 +11332,7 @@ var make4 = {
|
|
|
11464
11332
|
const content = yield* Effect18.tryPromise({
|
|
11465
11333
|
try: async () => {
|
|
11466
11334
|
try {
|
|
11467
|
-
return await
|
|
11335
|
+
return await readFile4(progressPath, "utf-8");
|
|
11468
11336
|
} catch {
|
|
11469
11337
|
return null;
|
|
11470
11338
|
}
|
|
@@ -11555,7 +11423,7 @@ var MemoryProgress = {
|
|
|
11555
11423
|
|
|
11556
11424
|
// src/layers/session/file-system.ts
|
|
11557
11425
|
init_esm_shims();
|
|
11558
|
-
import { mkdir as mkdir5, readFile as
|
|
11426
|
+
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
11559
11427
|
import { join as join6 } from "path";
|
|
11560
11428
|
import { DateTime as DateTime5, Effect as Effect20, Layer as Layer11 } from "effect";
|
|
11561
11429
|
import { humanId } from "human-id";
|
|
@@ -11638,7 +11506,7 @@ var make5 = {
|
|
|
11638
11506
|
get: (sessionId) => Effect20.gen(function* () {
|
|
11639
11507
|
const sessionPath = getSessionPath(sessionId);
|
|
11640
11508
|
const content = yield* Effect20.tryPromise({
|
|
11641
|
-
try: () =>
|
|
11509
|
+
try: () => readFile5(sessionPath, "utf-8"),
|
|
11642
11510
|
catch: (error) => new SessionStoreError({
|
|
11643
11511
|
message: `Failed to read session file: ${sessionPath}`,
|
|
11644
11512
|
operation: "get",
|
|
@@ -11731,7 +11599,7 @@ var MemorySession = {
|
|
|
11731
11599
|
|
|
11732
11600
|
// src/layers/signal/ferix-parser.ts
|
|
11733
11601
|
init_esm_shims();
|
|
11734
|
-
import { Effect as
|
|
11602
|
+
import { Effect as Effect23, Layer as Layer13, Ref as Ref8 } from "effect";
|
|
11735
11603
|
|
|
11736
11604
|
// src/services/signal-parser.ts
|
|
11737
11605
|
init_esm_shims();
|
|
@@ -12246,10 +12114,10 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
12246
12114
|
// src/layers/signal/ferix-parser.ts
|
|
12247
12115
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
12248
12116
|
function createAccumulatorImpl() {
|
|
12249
|
-
return
|
|
12117
|
+
return Effect23.gen(function* () {
|
|
12250
12118
|
const chunksRef = yield* Ref8.make([]);
|
|
12251
12119
|
const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
|
|
12252
|
-
const feed = (text) =>
|
|
12120
|
+
const feed = (text) => Effect23.gen(function* () {
|
|
12253
12121
|
const chunks = yield* Ref8.get(chunksRef);
|
|
12254
12122
|
chunks.push(text);
|
|
12255
12123
|
const buffer = chunks.join("");
|
|
@@ -12277,7 +12145,7 @@ function createAccumulatorImpl() {
|
|
|
12277
12145
|
yield* Ref8.set(emittedRef, emitted);
|
|
12278
12146
|
return newSignals;
|
|
12279
12147
|
});
|
|
12280
|
-
const flush = () =>
|
|
12148
|
+
const flush = () => Effect23.gen(function* () {
|
|
12281
12149
|
const chunks = yield* Ref8.get(chunksRef);
|
|
12282
12150
|
const buffer = chunks.join("");
|
|
12283
12151
|
yield* Ref8.set(chunksRef, []);
|
|
@@ -12293,7 +12161,7 @@ function createAccumulatorImpl() {
|
|
|
12293
12161
|
});
|
|
12294
12162
|
}
|
|
12295
12163
|
var make6 = {
|
|
12296
|
-
parse: (text) =>
|
|
12164
|
+
parse: (text) => Effect23.succeed(signalSpecRegistry.parseAll(text)),
|
|
12297
12165
|
createAccumulator: createAccumulatorImpl
|
|
12298
12166
|
};
|
|
12299
12167
|
var Live15 = Layer13.succeed(SignalParser, make6);
|
|
@@ -12349,27 +12217,27 @@ init_esm_shims();
|
|
|
12349
12217
|
|
|
12350
12218
|
// src/orchestrator/loop.ts
|
|
12351
12219
|
init_esm_shims();
|
|
12352
|
-
import { DateTime as DateTime10, Effect as
|
|
12220
|
+
import { DateTime as DateTime10, Effect as Effect28, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
|
|
12353
12221
|
|
|
12354
12222
|
// src/orchestrator/discovery.ts
|
|
12355
12223
|
init_esm_shims();
|
|
12356
|
-
import { DateTime as DateTime8, Effect as
|
|
12224
|
+
import { DateTime as DateTime8, Effect as Effect26, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
|
|
12357
12225
|
|
|
12358
12226
|
// src/layers/plan/task-generation.ts
|
|
12359
12227
|
init_esm_shims();
|
|
12360
|
-
import { mkdir as mkdir6, readFile as
|
|
12228
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
12361
12229
|
import { join as join7 } from "path";
|
|
12362
|
-
import { Effect as
|
|
12230
|
+
import { Effect as Effect24 } from "effect";
|
|
12363
12231
|
var PLANS_DIR4 = ".ferix/plans";
|
|
12364
12232
|
function ensureDir5(dirPath) {
|
|
12365
|
-
return
|
|
12233
|
+
return Effect24.tryPromise({
|
|
12366
12234
|
try: () => mkdir6(dirPath, { recursive: true }),
|
|
12367
12235
|
catch: (error) => new PlanStoreError({
|
|
12368
12236
|
message: `Failed to create directory: ${dirPath}`,
|
|
12369
12237
|
operation: "create",
|
|
12370
12238
|
cause: error
|
|
12371
12239
|
})
|
|
12372
|
-
}).pipe(
|
|
12240
|
+
}).pipe(Effect24.asVoid);
|
|
12373
12241
|
}
|
|
12374
12242
|
function getSessionDir4(sessionId) {
|
|
12375
12243
|
return join7(process.cwd(), PLANS_DIR4, sessionId);
|
|
@@ -12378,12 +12246,12 @@ function getTasksMdPath(sessionId) {
|
|
|
12378
12246
|
return join7(getSessionDir4(sessionId), "tasks.md");
|
|
12379
12247
|
}
|
|
12380
12248
|
function writeTasksMd(sessionId, tasks) {
|
|
12381
|
-
return
|
|
12249
|
+
return Effect24.gen(function* () {
|
|
12382
12250
|
const sessionDir = getSessionDir4(sessionId);
|
|
12383
12251
|
yield* ensureDir5(sessionDir);
|
|
12384
12252
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
12385
12253
|
const content = formatTasksMd(tasks);
|
|
12386
|
-
yield*
|
|
12254
|
+
yield* Effect24.tryPromise({
|
|
12387
12255
|
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
12388
12256
|
catch: (error) => new PlanStoreError({
|
|
12389
12257
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
@@ -12575,7 +12443,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
12575
12443
|
|
|
12576
12444
|
// src/orchestrator/plan-updates.ts
|
|
12577
12445
|
init_esm_shims();
|
|
12578
|
-
import { DateTime as DateTime7, Effect as
|
|
12446
|
+
import { DateTime as DateTime7, Effect as Effect25, Ref as Ref9 } from "effect";
|
|
12579
12447
|
|
|
12580
12448
|
// src/orchestrator/plan-updates/index.ts
|
|
12581
12449
|
init_esm_shims();
|
|
@@ -12897,9 +12765,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
12897
12765
|
tasks: plan.tasks
|
|
12898
12766
|
}) : planStore.update(plan.id, plan);
|
|
12899
12767
|
return storeOp.pipe(
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
(error) =>
|
|
12768
|
+
Effect25.map(() => null),
|
|
12769
|
+
Effect25.catchAll(
|
|
12770
|
+
(error) => Effect25.succeed({
|
|
12903
12771
|
_tag: "PlanUpdateFailed",
|
|
12904
12772
|
operation,
|
|
12905
12773
|
error: error.message,
|
|
@@ -12909,7 +12777,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
12909
12777
|
);
|
|
12910
12778
|
}
|
|
12911
12779
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
12912
|
-
return
|
|
12780
|
+
return Effect25.gen(function* () {
|
|
12913
12781
|
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
12914
12782
|
const now = yield* DateTime7.now;
|
|
12915
12783
|
const timestamp = DateTime7.formatIso(now);
|
|
@@ -12931,7 +12799,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
12931
12799
|
});
|
|
12932
12800
|
}
|
|
12933
12801
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
12934
|
-
return
|
|
12802
|
+
return Effect25.gen(function* () {
|
|
12935
12803
|
const state = yield* Ref9.get(persistenceStateRef);
|
|
12936
12804
|
if (!(state.dirty && state.pendingOperation)) {
|
|
12937
12805
|
return [];
|
|
@@ -13255,12 +13123,12 @@ function areAllTasksComplete(plan) {
|
|
|
13255
13123
|
|
|
13256
13124
|
// src/orchestrator/discovery.ts
|
|
13257
13125
|
function processTextSignals(signalParser, text, context) {
|
|
13258
|
-
return
|
|
13126
|
+
return Effect26.gen(function* () {
|
|
13259
13127
|
const events = [];
|
|
13260
13128
|
const parsedSignals = [];
|
|
13261
13129
|
const signals = yield* signalParser.parse(text).pipe(
|
|
13262
|
-
|
|
13263
|
-
(error) =>
|
|
13130
|
+
Effect26.tapError(
|
|
13131
|
+
(error) => Effect26.logDebug(
|
|
13264
13132
|
"Signal parsing failed, continuing with empty signals",
|
|
13265
13133
|
{
|
|
13266
13134
|
error: String(error),
|
|
@@ -13268,7 +13136,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
13268
13136
|
}
|
|
13269
13137
|
)
|
|
13270
13138
|
),
|
|
13271
|
-
|
|
13139
|
+
Effect26.orElseSucceed(() => [])
|
|
13272
13140
|
);
|
|
13273
13141
|
for (const signal of signals) {
|
|
13274
13142
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -13278,7 +13146,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
13278
13146
|
});
|
|
13279
13147
|
}
|
|
13280
13148
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
13281
|
-
return
|
|
13149
|
+
return Effect26.gen(function* () {
|
|
13282
13150
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
13283
13151
|
const events = [domainEvent];
|
|
13284
13152
|
const allSignals = [];
|
|
@@ -13323,7 +13191,7 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
13323
13191
|
}
|
|
13324
13192
|
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath, onSessionName) {
|
|
13325
13193
|
return Stream7.unwrap(
|
|
13326
|
-
|
|
13194
|
+
Effect26.gen(function* () {
|
|
13327
13195
|
const startTimeUtc = yield* DateTime8.now;
|
|
13328
13196
|
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
13329
13197
|
const persistenceStateRef = yield* Ref10.make({
|
|
@@ -13351,7 +13219,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13351
13219
|
),
|
|
13352
13220
|
Stream7.flatMap(
|
|
13353
13221
|
(llmEvent) => Stream7.unwrap(
|
|
13354
|
-
|
|
13222
|
+
Effect26.gen(function* () {
|
|
13355
13223
|
const now = yield* DateTime8.now;
|
|
13356
13224
|
const context = {
|
|
13357
13225
|
timestamp: DateTime8.toEpochMillis(now)
|
|
@@ -13391,7 +13259,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13391
13259
|
)
|
|
13392
13260
|
);
|
|
13393
13261
|
const completionStream = Stream7.fromEffect(
|
|
13394
|
-
|
|
13262
|
+
Effect26.gen(function* () {
|
|
13395
13263
|
const persistEvents = yield* flushPlanPersistence(
|
|
13396
13264
|
planStore,
|
|
13397
13265
|
currentPlanRef,
|
|
@@ -13402,12 +13270,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13402
13270
|
if (plan && plan.tasks.length > 0) {
|
|
13403
13271
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
13404
13272
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
13405
|
-
|
|
13406
|
-
(error) =>
|
|
13273
|
+
Effect26.tapError(
|
|
13274
|
+
(error) => Effect26.logDebug("Failed to write tasks.md, continuing", {
|
|
13407
13275
|
error: String(error)
|
|
13408
13276
|
})
|
|
13409
13277
|
),
|
|
13410
|
-
|
|
13278
|
+
Effect26.orElseSucceed(() => void 0)
|
|
13411
13279
|
);
|
|
13412
13280
|
}
|
|
13413
13281
|
const endTimeUtc = yield* DateTime8.now;
|
|
@@ -13417,8 +13285,8 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13417
13285
|
if (capturedName) {
|
|
13418
13286
|
if (onSessionName) {
|
|
13419
13287
|
yield* onSessionName(capturedName).pipe(
|
|
13420
|
-
|
|
13421
|
-
(error) =>
|
|
13288
|
+
Effect26.tapError(
|
|
13289
|
+
(error) => Effect26.logDebug(
|
|
13422
13290
|
"Failed to handle session name, continuing",
|
|
13423
13291
|
{
|
|
13424
13292
|
error: String(error),
|
|
@@ -13426,7 +13294,7 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13426
13294
|
}
|
|
13427
13295
|
)
|
|
13428
13296
|
),
|
|
13429
|
-
|
|
13297
|
+
Effect26.orElseSucceed(() => void 0)
|
|
13430
13298
|
);
|
|
13431
13299
|
}
|
|
13432
13300
|
const sessionNameEvent = {
|
|
@@ -13458,15 +13326,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
13458
13326
|
|
|
13459
13327
|
// src/orchestrator/iteration.ts
|
|
13460
13328
|
init_esm_shims();
|
|
13461
|
-
import { DateTime as DateTime9, Effect as
|
|
13329
|
+
import { DateTime as DateTime9, Effect as Effect27, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
|
|
13462
13330
|
function processTextSignals2(signalParser, text, context) {
|
|
13463
|
-
return
|
|
13331
|
+
return Effect27.gen(function* () {
|
|
13464
13332
|
const events = [];
|
|
13465
13333
|
let completed = false;
|
|
13466
13334
|
const parsedSignals = [];
|
|
13467
13335
|
const signals = yield* signalParser.parse(text).pipe(
|
|
13468
|
-
|
|
13469
|
-
(error) =>
|
|
13336
|
+
Effect27.tapError(
|
|
13337
|
+
(error) => Effect27.logDebug(
|
|
13470
13338
|
"Signal parsing failed, continuing with empty signals",
|
|
13471
13339
|
{
|
|
13472
13340
|
error: String(error),
|
|
@@ -13474,7 +13342,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
13474
13342
|
}
|
|
13475
13343
|
)
|
|
13476
13344
|
),
|
|
13477
|
-
|
|
13345
|
+
Effect27.orElseSucceed(() => [])
|
|
13478
13346
|
);
|
|
13479
13347
|
for (const signal of signals) {
|
|
13480
13348
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -13487,7 +13355,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
13487
13355
|
});
|
|
13488
13356
|
}
|
|
13489
13357
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
13490
|
-
return
|
|
13358
|
+
return Effect27.gen(function* () {
|
|
13491
13359
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
13492
13360
|
const events = [domainEvent];
|
|
13493
13361
|
let completed = false;
|
|
@@ -13520,7 +13388,7 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
13520
13388
|
}
|
|
13521
13389
|
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
13522
13390
|
return Stream8.unwrap(
|
|
13523
|
-
|
|
13391
|
+
Effect27.gen(function* () {
|
|
13524
13392
|
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
13525
13393
|
const persistenceStateRef = yield* Ref11.make({
|
|
13526
13394
|
dirty: false,
|
|
@@ -13543,7 +13411,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13543
13411
|
),
|
|
13544
13412
|
Stream8.flatMap(
|
|
13545
13413
|
(llmEvent) => Stream8.unwrap(
|
|
13546
|
-
|
|
13414
|
+
Effect27.gen(function* () {
|
|
13547
13415
|
const now = yield* DateTime9.now;
|
|
13548
13416
|
const context = {
|
|
13549
13417
|
timestamp: DateTime9.toEpochMillis(now)
|
|
@@ -13565,14 +13433,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13565
13433
|
events.push(...planEvents);
|
|
13566
13434
|
}
|
|
13567
13435
|
if (result.completed) {
|
|
13568
|
-
yield*
|
|
13436
|
+
yield* Effect27.logInfo(
|
|
13569
13437
|
"[DEBUG] createIterationStream: LLM emitted completion signal"
|
|
13570
13438
|
);
|
|
13571
13439
|
yield* Ref11.set(loopCompletedRef, true);
|
|
13572
13440
|
}
|
|
13573
13441
|
const updatedPlan = yield* Ref11.get(currentPlanRef);
|
|
13574
13442
|
const allComplete = areAllTasksComplete(updatedPlan);
|
|
13575
|
-
yield*
|
|
13443
|
+
yield* Effect27.logInfo(
|
|
13576
13444
|
"[DEBUG] createIterationStream: Auto-complete check",
|
|
13577
13445
|
{
|
|
13578
13446
|
llmEmittedComplete: result.completed,
|
|
@@ -13584,7 +13452,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13584
13452
|
}
|
|
13585
13453
|
);
|
|
13586
13454
|
if (allComplete) {
|
|
13587
|
-
yield*
|
|
13455
|
+
yield* Effect27.logInfo(
|
|
13588
13456
|
"[DEBUG] createIterationStream: All tasks complete - ending loop"
|
|
13589
13457
|
);
|
|
13590
13458
|
yield* Ref11.set(loopCompletedRef, true);
|
|
@@ -13606,7 +13474,7 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13606
13474
|
)
|
|
13607
13475
|
);
|
|
13608
13476
|
const completionStream = Stream8.fromEffect(
|
|
13609
|
-
|
|
13477
|
+
Effect27.gen(function* () {
|
|
13610
13478
|
const persistEvents = yield* flushPlanPersistence(
|
|
13611
13479
|
planStore,
|
|
13612
13480
|
currentPlanRef,
|
|
@@ -13631,14 +13499,14 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
13631
13499
|
// src/orchestrator/loop.ts
|
|
13632
13500
|
function runLoop(config) {
|
|
13633
13501
|
return Stream9.unwrap(
|
|
13634
|
-
|
|
13502
|
+
Effect28.gen(function* () {
|
|
13635
13503
|
const llm = yield* LLM;
|
|
13636
13504
|
const signalParser = yield* SignalParser;
|
|
13637
13505
|
const sessionStore = yield* SessionStore;
|
|
13638
13506
|
const planStore = yield* PlanStore;
|
|
13639
13507
|
const git = yield* Git;
|
|
13640
13508
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
13641
|
-
|
|
13509
|
+
Effect28.mapError(
|
|
13642
13510
|
(e) => new OrchestratorError({
|
|
13643
13511
|
message: `Failed to create session: ${e.message}`,
|
|
13644
13512
|
phase: "setup",
|
|
@@ -13647,15 +13515,15 @@ function runLoop(config) {
|
|
|
13647
13515
|
)
|
|
13648
13516
|
);
|
|
13649
13517
|
const baseBranch = yield* git.getCurrentBranch().pipe(
|
|
13650
|
-
|
|
13651
|
-
(error) =>
|
|
13518
|
+
Effect28.tapError(
|
|
13519
|
+
(error) => Effect28.logDebug("Failed to get current branch", {
|
|
13652
13520
|
error: String(error)
|
|
13653
13521
|
})
|
|
13654
13522
|
),
|
|
13655
|
-
|
|
13523
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13656
13524
|
);
|
|
13657
13525
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
13658
|
-
|
|
13526
|
+
Effect28.mapError(
|
|
13659
13527
|
(e) => new OrchestratorError({
|
|
13660
13528
|
message: `Failed to create worktree: ${e.message}`,
|
|
13661
13529
|
phase: "setup",
|
|
@@ -13670,12 +13538,12 @@ function runLoop(config) {
|
|
|
13670
13538
|
branchName,
|
|
13671
13539
|
baseBranch
|
|
13672
13540
|
}).pipe(
|
|
13673
|
-
|
|
13674
|
-
(error) =>
|
|
13541
|
+
Effect28.tapError(
|
|
13542
|
+
(error) => Effect28.logDebug("Failed to update session with worktree info", {
|
|
13675
13543
|
error: String(error)
|
|
13676
13544
|
})
|
|
13677
13545
|
),
|
|
13678
|
-
|
|
13546
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13679
13547
|
);
|
|
13680
13548
|
const startTimeUtc = yield* DateTime10.now;
|
|
13681
13549
|
const startTime = DateTime10.toEpochMillis(startTimeUtc);
|
|
@@ -13696,7 +13564,7 @@ function runLoop(config) {
|
|
|
13696
13564
|
config,
|
|
13697
13565
|
timestamp: startTime
|
|
13698
13566
|
};
|
|
13699
|
-
const handleSessionName = (displayName) =>
|
|
13567
|
+
const handleSessionName = (displayName) => Effect28.gen(function* () {
|
|
13700
13568
|
const currentSession = yield* Ref12.get(sessionRef);
|
|
13701
13569
|
const newBranchName = yield* git.renameBranch(
|
|
13702
13570
|
session.id,
|
|
@@ -13722,7 +13590,7 @@ function runLoop(config) {
|
|
|
13722
13590
|
);
|
|
13723
13591
|
const iterationsStream = Stream9.unfoldEffect(
|
|
13724
13592
|
1,
|
|
13725
|
-
(iteration) =>
|
|
13593
|
+
(iteration) => Effect28.gen(function* () {
|
|
13726
13594
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
13727
13595
|
if (completed || iteration > maxIterations) {
|
|
13728
13596
|
return Option.none();
|
|
@@ -13768,8 +13636,8 @@ function runLoop(config) {
|
|
|
13768
13636
|
);
|
|
13769
13637
|
}).pipe(
|
|
13770
13638
|
// Also catch setup errors (e.g., session creation failure)
|
|
13771
|
-
|
|
13772
|
-
(error) =>
|
|
13639
|
+
Effect28.catchAll(
|
|
13640
|
+
(error) => Effect28.succeed(
|
|
13773
13641
|
Stream9.succeed({
|
|
13774
13642
|
_tag: "LoopFailed",
|
|
13775
13643
|
error: {
|
|
@@ -13795,32 +13663,32 @@ Generated by Ferix`;
|
|
|
13795
13663
|
}
|
|
13796
13664
|
function createCompletionStream(sessionStore, git, sessionRef, config, startTime, loopCompletedRef, _worktreePath) {
|
|
13797
13665
|
return Stream9.unwrap(
|
|
13798
|
-
|
|
13666
|
+
Effect28.gen(function* () {
|
|
13799
13667
|
const session = yield* Ref12.get(sessionRef);
|
|
13800
13668
|
const endTimeUtc = yield* DateTime10.now;
|
|
13801
13669
|
const endTime = DateTime10.toEpochMillis(endTimeUtc);
|
|
13802
13670
|
const durationMs = endTime - startTime;
|
|
13803
13671
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
13804
13672
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
13805
|
-
|
|
13806
|
-
(error) =>
|
|
13673
|
+
Effect28.tapError(
|
|
13674
|
+
(error) => Effect28.logDebug("Final commit failed, continuing", {
|
|
13807
13675
|
sessionId: session.id,
|
|
13808
13676
|
error: String(error)
|
|
13809
13677
|
})
|
|
13810
13678
|
),
|
|
13811
|
-
|
|
13679
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13812
13680
|
);
|
|
13813
13681
|
let branchPushed = false;
|
|
13814
13682
|
if (config.push === true) {
|
|
13815
13683
|
const pushResult = yield* git.pushBranch(session.id).pipe(
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
(error) =>
|
|
13684
|
+
Effect28.map(() => true),
|
|
13685
|
+
Effect28.tapError(
|
|
13686
|
+
(error) => Effect28.logDebug("Push failed, continuing", {
|
|
13819
13687
|
sessionId: session.id,
|
|
13820
13688
|
error: String(error)
|
|
13821
13689
|
})
|
|
13822
13690
|
),
|
|
13823
|
-
|
|
13691
|
+
Effect28.orElseSucceed(() => false)
|
|
13824
13692
|
);
|
|
13825
13693
|
branchPushed = pushResult;
|
|
13826
13694
|
}
|
|
@@ -13829,25 +13697,25 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13829
13697
|
const title = `feat: ${session.originalTask.slice(0, 50)}`;
|
|
13830
13698
|
const body = buildPRBody(session, config);
|
|
13831
13699
|
const prResult = yield* git.createPR(session.id, title, body, session.baseBranch).pipe(
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
(error) =>
|
|
13700
|
+
Effect28.map((url) => url),
|
|
13701
|
+
Effect28.tapError(
|
|
13702
|
+
(error) => Effect28.logDebug("PR creation failed, continuing", {
|
|
13835
13703
|
sessionId: session.id,
|
|
13836
13704
|
error: String(error)
|
|
13837
13705
|
})
|
|
13838
13706
|
),
|
|
13839
|
-
|
|
13707
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13840
13708
|
);
|
|
13841
13709
|
prUrl = prResult;
|
|
13842
13710
|
}
|
|
13843
13711
|
yield* git.removeWorktreeKeepBranch(session.id).pipe(
|
|
13844
|
-
|
|
13845
|
-
(error) =>
|
|
13712
|
+
Effect28.tapError(
|
|
13713
|
+
(error) => Effect28.logDebug("Worktree cleanup failed, continuing", {
|
|
13846
13714
|
sessionId: session.id,
|
|
13847
13715
|
error: String(error)
|
|
13848
13716
|
})
|
|
13849
13717
|
),
|
|
13850
|
-
|
|
13718
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13851
13719
|
);
|
|
13852
13720
|
const worktreeRemoved = {
|
|
13853
13721
|
_tag: "WorktreeRemoved",
|
|
@@ -13868,13 +13736,13 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13868
13736
|
status: completed ? "completed" : "paused",
|
|
13869
13737
|
worktreePath: void 0
|
|
13870
13738
|
}).pipe(
|
|
13871
|
-
|
|
13872
|
-
(error) =>
|
|
13739
|
+
Effect28.tapError(
|
|
13740
|
+
(error) => Effect28.logDebug("Session update failed, continuing", {
|
|
13873
13741
|
sessionId: session.id,
|
|
13874
13742
|
error: String(error)
|
|
13875
13743
|
})
|
|
13876
13744
|
),
|
|
13877
|
-
|
|
13745
|
+
Effect28.orElseSucceed(() => void 0)
|
|
13878
13746
|
);
|
|
13879
13747
|
const events = [worktreeRemoved];
|
|
13880
13748
|
if (branchPushed) {
|
|
@@ -13904,7 +13772,7 @@ function createCompletionStream(sessionStore, git, sessionRef, config, startTime
|
|
|
13904
13772
|
function run(options) {
|
|
13905
13773
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
13906
13774
|
const events = runLoop(config);
|
|
13907
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
13775
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect29.sync(() => onEvent(event)))) : events;
|
|
13908
13776
|
const layers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
13909
13777
|
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
13910
13778
|
if (consumerType === "none") {
|
|
@@ -13916,7 +13784,7 @@ function run(options) {
|
|
|
13916
13784
|
function runTest(options, mockEvents) {
|
|
13917
13785
|
const { config, onEvent } = options;
|
|
13918
13786
|
const events = runLoop(config);
|
|
13919
|
-
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) =>
|
|
13787
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect29.sync(() => onEvent(event)))) : events;
|
|
13920
13788
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
13921
13789
|
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
13922
13790
|
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
@@ -13924,11 +13792,11 @@ function runTest(options, mockEvents) {
|
|
|
13924
13792
|
function collectEvents(config, mockEvents) {
|
|
13925
13793
|
const events = runLoop(config);
|
|
13926
13794
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
13927
|
-
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(
|
|
13795
|
+
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect29.map((chunk) => Array.from(chunk)));
|
|
13928
13796
|
}
|
|
13929
13797
|
function main(config) {
|
|
13930
13798
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
13931
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
13799
|
+
return run({ config, consumer: consumerType }).pipe(Effect29.runPromise);
|
|
13932
13800
|
}
|
|
13933
13801
|
|
|
13934
13802
|
// src/services/index.ts
|
|
@@ -13954,55 +13822,418 @@ program.command("run", { isDefault: true }).argument("<task>", "Task description
|
|
|
13954
13822
|
process.exit(1);
|
|
13955
13823
|
}
|
|
13956
13824
|
});
|
|
13957
|
-
|
|
13825
|
+
var NON_NPM_VERSION_PREFIXES2 = ["workspace:", "file:", "link:", "portal:"];
|
|
13826
|
+
var isNonNpmDependency = (version2) => NON_NPM_VERSION_PREFIXES2.some((prefix) => version2.startsWith(prefix));
|
|
13827
|
+
var extractDependencies = (pkg) => {
|
|
13828
|
+
const deps = pkg.dependencies ?? {};
|
|
13829
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
13830
|
+
const allDeps = /* @__PURE__ */ new Map();
|
|
13831
|
+
for (const [name, version2] of Object.entries(deps)) {
|
|
13832
|
+
if (!isNonNpmDependency(version2)) {
|
|
13833
|
+
allDeps.set(name, version2);
|
|
13834
|
+
}
|
|
13835
|
+
}
|
|
13836
|
+
for (const [name, version2] of Object.entries(devDeps)) {
|
|
13837
|
+
if (!(allDeps.has(name) || isNonNpmDependency(version2))) {
|
|
13838
|
+
allDeps.set(name, version2);
|
|
13839
|
+
}
|
|
13840
|
+
}
|
|
13841
|
+
return Array.from(allDeps.keys());
|
|
13842
|
+
};
|
|
13843
|
+
var extractWorkspacePatterns = (pkg) => {
|
|
13844
|
+
const workspaces = pkg.workspaces;
|
|
13845
|
+
if (!workspaces) {
|
|
13846
|
+
return [];
|
|
13847
|
+
}
|
|
13848
|
+
if (Array.isArray(workspaces)) {
|
|
13849
|
+
return workspaces.filter((w) => typeof w === "string");
|
|
13850
|
+
}
|
|
13851
|
+
if (typeof workspaces === "object" && "packages" in workspaces) {
|
|
13852
|
+
const packages = workspaces.packages;
|
|
13853
|
+
if (Array.isArray(packages)) {
|
|
13854
|
+
return packages.filter((w) => typeof w === "string");
|
|
13855
|
+
}
|
|
13856
|
+
}
|
|
13857
|
+
return [];
|
|
13858
|
+
};
|
|
13859
|
+
var GLOB_SUFFIX_PATTERN = /\/\*+$/;
|
|
13860
|
+
var expandGlobPattern = async (rootDir, pattern) => {
|
|
13861
|
+
if (pattern.endsWith("/*") || pattern.endsWith("/**")) {
|
|
13862
|
+
const baseDir = pattern.replace(GLOB_SUFFIX_PATTERN, "");
|
|
13863
|
+
const fullPath2 = join8(rootDir, baseDir);
|
|
13864
|
+
try {
|
|
13865
|
+
const entries = await readdir2(fullPath2, { withFileTypes: true });
|
|
13866
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => join8(fullPath2, entry.name));
|
|
13867
|
+
} catch {
|
|
13868
|
+
return [];
|
|
13869
|
+
}
|
|
13870
|
+
}
|
|
13871
|
+
const fullPath = join8(rootDir, pattern);
|
|
13958
13872
|
try {
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13873
|
+
await access4(fullPath);
|
|
13874
|
+
return [fullPath];
|
|
13875
|
+
} catch {
|
|
13876
|
+
return [];
|
|
13877
|
+
}
|
|
13878
|
+
};
|
|
13879
|
+
var readPackageJsonFile = async (filePath) => {
|
|
13880
|
+
const content = await readFile7(filePath, "utf-8");
|
|
13881
|
+
return JSON.parse(content);
|
|
13882
|
+
};
|
|
13883
|
+
var discoverPackageJsonFiles = async (rootPath, rootPkg) => {
|
|
13884
|
+
const rootDir = dirname2(rootPath);
|
|
13885
|
+
const patterns = extractWorkspacePatterns(rootPkg);
|
|
13886
|
+
if (patterns.length === 0) {
|
|
13887
|
+
return [rootPath];
|
|
13888
|
+
}
|
|
13889
|
+
const packageJsonPaths = [rootPath];
|
|
13890
|
+
for (const pattern of patterns) {
|
|
13891
|
+
const dirs = await expandGlobPattern(rootDir, pattern);
|
|
13892
|
+
for (const dir of dirs) {
|
|
13893
|
+
const pkgPath = join8(dir, "package.json");
|
|
13894
|
+
try {
|
|
13895
|
+
await access4(pkgPath);
|
|
13896
|
+
packageJsonPaths.push(pkgPath);
|
|
13897
|
+
} catch {
|
|
13898
|
+
}
|
|
13969
13899
|
}
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
13900
|
+
}
|
|
13901
|
+
return packageJsonPaths;
|
|
13902
|
+
};
|
|
13903
|
+
var SYMBOLS = {
|
|
13904
|
+
success: pc17.green("\u2713"),
|
|
13905
|
+
error: pc17.red("\u2717")
|
|
13906
|
+
};
|
|
13907
|
+
var printHeader = (title, subtitle) => {
|
|
13908
|
+
console.log();
|
|
13909
|
+
console.log(pc17.bold(pc17.cyan(` ${title}`)));
|
|
13910
|
+
if (subtitle) {
|
|
13911
|
+
console.log(pc17.dim(` ${subtitle}`));
|
|
13912
|
+
}
|
|
13913
|
+
console.log();
|
|
13914
|
+
};
|
|
13915
|
+
var printSection = (title) => {
|
|
13916
|
+
console.log();
|
|
13917
|
+
console.log(pc17.bold(` ${title}`));
|
|
13918
|
+
};
|
|
13919
|
+
var printRepo = (owner, repo, index) => {
|
|
13920
|
+
const num = pc17.dim(`${String(index + 1).padStart(2, " ")}.`);
|
|
13921
|
+
console.log(` ${num} ${pc17.cyan(owner)}${pc17.dim("/")}${pc17.white(repo)}`);
|
|
13922
|
+
};
|
|
13923
|
+
var printSuccess = (message) => {
|
|
13924
|
+
console.log(` ${SYMBOLS.success} ${pc17.green(message)}`);
|
|
13925
|
+
};
|
|
13926
|
+
var printError = (message) => {
|
|
13927
|
+
console.log(` ${SYMBOLS.error} ${pc17.red(message)}`);
|
|
13928
|
+
};
|
|
13929
|
+
var printHint = (message) => {
|
|
13930
|
+
console.log(pc17.dim(` ${message}`));
|
|
13931
|
+
};
|
|
13932
|
+
var createSpinner = (text) => {
|
|
13933
|
+
return ora({
|
|
13934
|
+
text,
|
|
13935
|
+
prefixText: " ",
|
|
13936
|
+
spinner: "dots"
|
|
13937
|
+
});
|
|
13938
|
+
};
|
|
13939
|
+
var discoverPackages = async (state, packagePath) => {
|
|
13940
|
+
state.spinner = createSpinner("Scanning for package.json files...").start();
|
|
13941
|
+
const absolutePath = resolve(packagePath);
|
|
13942
|
+
await access4(absolutePath);
|
|
13943
|
+
const rootPkg = await readPackageJsonFile(absolutePath);
|
|
13944
|
+
state.packageJsonPaths = await discoverPackageJsonFiles(
|
|
13945
|
+
absolutePath,
|
|
13946
|
+
rootPkg
|
|
13947
|
+
);
|
|
13948
|
+
const isMonorepo = state.packageJsonPaths.length > 1;
|
|
13949
|
+
state.spinner.succeed(
|
|
13950
|
+
isMonorepo ? `Found ${pc17.bold(String(state.packageJsonPaths.length))} package.json files ${pc17.dim("(monorepo)")}` : "Found package.json"
|
|
13951
|
+
);
|
|
13952
|
+
return true;
|
|
13953
|
+
};
|
|
13954
|
+
var extractAllDependencies = async (state) => {
|
|
13955
|
+
state.spinner = createSpinner("Extracting dependencies...").start();
|
|
13956
|
+
const allDependencies = /* @__PURE__ */ new Set();
|
|
13957
|
+
for (const pkgPath of state.packageJsonPaths) {
|
|
13958
|
+
const pkg = await readPackageJsonFile(pkgPath);
|
|
13959
|
+
for (const dep of extractDependencies(pkg)) {
|
|
13960
|
+
allDependencies.add(dep);
|
|
13961
|
+
}
|
|
13962
|
+
}
|
|
13963
|
+
state.dependencies = Array.from(allDependencies);
|
|
13964
|
+
if (state.dependencies.length === 0) {
|
|
13965
|
+
state.spinner.warn("No dependencies found");
|
|
13966
|
+
return false;
|
|
13967
|
+
}
|
|
13968
|
+
state.spinner.succeed(
|
|
13969
|
+
`Found ${pc17.bold(String(state.dependencies.length))} unique dependencies`
|
|
13970
|
+
);
|
|
13971
|
+
return true;
|
|
13972
|
+
};
|
|
13973
|
+
var resolveOrganizations = async (state, isDev) => {
|
|
13974
|
+
state.spinner = createSpinner("Resolving GitHub organizations...").start();
|
|
13975
|
+
const packageOrgs = await Effect30.runPromise(
|
|
13976
|
+
resolvePackageOrgs(state.dependencies, isDev)
|
|
13977
|
+
);
|
|
13978
|
+
state.orgs = Array.from(
|
|
13979
|
+
new Set(
|
|
13980
|
+
packageOrgs.map((p) => p.githubOrg).filter((org) => org !== null)
|
|
13981
|
+
)
|
|
13982
|
+
);
|
|
13983
|
+
if (state.orgs.length === 0) {
|
|
13984
|
+
state.spinner.warn("No GitHub organizations found");
|
|
13985
|
+
printHint("Your dependencies don't have linked GitHub organizations.");
|
|
13986
|
+
return false;
|
|
13987
|
+
}
|
|
13988
|
+
state.spinner.succeed(
|
|
13989
|
+
`Resolved ${pc17.bold(String(state.orgs.length))} GitHub organizations`
|
|
13990
|
+
);
|
|
13991
|
+
return true;
|
|
13992
|
+
};
|
|
13993
|
+
var findRepositories = async (state, isDev) => {
|
|
13994
|
+
state.spinner = createSpinner("Searching for skill repositories...").start();
|
|
13995
|
+
state.skillRepos = await Effect30.runPromise(findSkillRepos(state.orgs, isDev));
|
|
13996
|
+
if (state.skillRepos.length === 0) {
|
|
13997
|
+
state.spinner.warn("No skill repositories found");
|
|
13998
|
+
printHint(
|
|
13999
|
+
"None of your dependency organizations have published skills yet."
|
|
13977
14000
|
);
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
${pc17.
|
|
14001
|
+
return false;
|
|
14002
|
+
}
|
|
14003
|
+
state.spinner.succeed(
|
|
14004
|
+
`Found ${pc17.bold(String(state.skillRepos.length))} skill ${state.skillRepos.length === 1 ? "repository" : "repositories"}`
|
|
14005
|
+
);
|
|
14006
|
+
return true;
|
|
14007
|
+
};
|
|
14008
|
+
var selectRepositories = async (skillRepos) => {
|
|
14009
|
+
if (skillRepos.length === 1) {
|
|
14010
|
+
const repo = skillRepos[0];
|
|
14011
|
+
if (repo) {
|
|
14012
|
+
printHint(
|
|
14013
|
+
`Only 1 repository found, auto-selecting: ${pc17.cyan(`${repo.owner}/${repo.repo}`)}`
|
|
13982
14014
|
);
|
|
13983
|
-
return;
|
|
13984
14015
|
}
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
14016
|
+
return { type: "selected", repos: skillRepos };
|
|
14017
|
+
}
|
|
14018
|
+
if (!process.stdin.isTTY) {
|
|
14019
|
+
printHint(
|
|
14020
|
+
`Non-interactive terminal detected, selecting all ${skillRepos.length} repositories.`
|
|
13989
14021
|
);
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
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 };
|
|
14041
|
+
};
|
|
14042
|
+
var installRepositories = async (state, reposToInstall, isGlobal, agents) => {
|
|
14043
|
+
printSection("Installing");
|
|
14044
|
+
console.log();
|
|
14045
|
+
const installed = [];
|
|
14046
|
+
const failed = [];
|
|
14047
|
+
for (const repo of reposToInstall) {
|
|
14048
|
+
const repoId = `${repo.owner}/${repo.repo}`;
|
|
14049
|
+
state.spinner = createSpinner(
|
|
14050
|
+
`Installing ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}...`
|
|
14051
|
+
).start();
|
|
14052
|
+
try {
|
|
14053
|
+
await Effect30.runPromise(
|
|
14054
|
+
installSkills([repo], {
|
|
14055
|
+
dryRun: false,
|
|
14056
|
+
global: isGlobal,
|
|
14057
|
+
agents: [...agents]
|
|
14058
|
+
})
|
|
13997
14059
|
);
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
|
|
14060
|
+
state.spinner.succeed(
|
|
14061
|
+
`Installed ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}`
|
|
14062
|
+
);
|
|
14063
|
+
installed.push(repoId);
|
|
14064
|
+
} catch {
|
|
14065
|
+
state.spinner.fail(
|
|
14066
|
+
`Failed to install ${pc17.cyan(repo.owner)}${pc17.dim("/")}${pc17.white(repo.repo)}`
|
|
14002
14067
|
);
|
|
14068
|
+
failed.push(repoId);
|
|
14003
14069
|
}
|
|
14070
|
+
}
|
|
14071
|
+
return { installed, failed };
|
|
14072
|
+
};
|
|
14073
|
+
var displaySummary = (installed, failed, isGlobal, agents) => {
|
|
14074
|
+
printSection("Summary");
|
|
14075
|
+
console.log();
|
|
14076
|
+
if (installed.length > 0) {
|
|
14077
|
+
printSuccess(
|
|
14078
|
+
`${installed.length} skill ${installed.length === 1 ? "repository" : "repositories"} installed successfully`
|
|
14079
|
+
);
|
|
14080
|
+
}
|
|
14081
|
+
if (failed.length > 0) {
|
|
14082
|
+
printError(
|
|
14083
|
+
`${failed.length} ${failed.length === 1 ? "installation" : "installations"} failed`
|
|
14084
|
+
);
|
|
14085
|
+
}
|
|
14086
|
+
const installLocation = isGlobal ? "globally" : "in project";
|
|
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.`);
|
|
14161
|
+
console.log();
|
|
14162
|
+
};
|
|
14163
|
+
var runSyncCommand = async (options) => {
|
|
14164
|
+
const {
|
|
14165
|
+
path: packagePath,
|
|
14166
|
+
dryRun,
|
|
14167
|
+
global: isGlobal,
|
|
14168
|
+
dev: isDev,
|
|
14169
|
+
agents: manualAgents,
|
|
14170
|
+
yes: skipSelection
|
|
14171
|
+
} = options;
|
|
14172
|
+
printHeader(
|
|
14173
|
+
"Ferix Sync",
|
|
14174
|
+
isDev ? pc17.yellow("Development Mode") : "Discovering skills from your dependencies"
|
|
14175
|
+
);
|
|
14176
|
+
const state = {
|
|
14177
|
+
spinner: void 0,
|
|
14178
|
+
packageJsonPaths: [],
|
|
14179
|
+
dependencies: [],
|
|
14180
|
+
orgs: [],
|
|
14181
|
+
skillRepos: [],
|
|
14182
|
+
detectedAgents: []
|
|
14183
|
+
};
|
|
14184
|
+
await discoverPackages(state, packagePath);
|
|
14185
|
+
const agentsToUse = await determineAgents(state, packagePath, manualAgents);
|
|
14186
|
+
if (!agentsToUse) {
|
|
14187
|
+
return;
|
|
14188
|
+
}
|
|
14189
|
+
const hasDeps = await extractAllDependencies(state);
|
|
14190
|
+
if (!hasDeps) {
|
|
14191
|
+
return;
|
|
14192
|
+
}
|
|
14193
|
+
const hasOrgs = await resolveOrganizations(state, isDev);
|
|
14194
|
+
if (!hasOrgs) {
|
|
14195
|
+
return;
|
|
14196
|
+
}
|
|
14197
|
+
const hasRepos = await findRepositories(state, isDev);
|
|
14198
|
+
if (!hasRepos) {
|
|
14199
|
+
return;
|
|
14200
|
+
}
|
|
14201
|
+
const reposToInstall = await handleRepoSelection(
|
|
14202
|
+
state.skillRepos,
|
|
14203
|
+
skipSelection
|
|
14204
|
+
);
|
|
14205
|
+
if (!reposToInstall) {
|
|
14206
|
+
return;
|
|
14207
|
+
}
|
|
14208
|
+
if (dryRun) {
|
|
14209
|
+
displayDryRunSummary(reposToInstall, agentsToUse);
|
|
14210
|
+
return;
|
|
14211
|
+
}
|
|
14212
|
+
const { installed, failed } = await installRepositories(
|
|
14213
|
+
state,
|
|
14214
|
+
reposToInstall,
|
|
14215
|
+
isGlobal,
|
|
14216
|
+
agentsToUse
|
|
14217
|
+
);
|
|
14218
|
+
displaySummary(installed, failed, isGlobal, agentsToUse);
|
|
14219
|
+
};
|
|
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) => {
|
|
14224
|
+
try {
|
|
14225
|
+
await runSyncCommand({
|
|
14226
|
+
path: options.path,
|
|
14227
|
+
dryRun: options.dryRun ?? false,
|
|
14228
|
+
global: options.global ?? false,
|
|
14229
|
+
dev: options.dev ?? false,
|
|
14230
|
+
agents: options.agents,
|
|
14231
|
+
yes: options.yes ?? false
|
|
14232
|
+
});
|
|
14004
14233
|
} catch (error) {
|
|
14005
|
-
console.
|
|
14234
|
+
console.log();
|
|
14235
|
+
printError(error instanceof Error ? error.message : String(error));
|
|
14236
|
+
console.log();
|
|
14006
14237
|
process.exit(1);
|
|
14007
14238
|
}
|
|
14008
14239
|
});
|