replicas-engine 0.1.318 → 0.1.320

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 (2) hide show
  1. package/dist/src/index.js +408 -129
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -287,7 +287,7 @@ var WORKSPACE_SIZES = ["small", "large"];
287
287
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
288
288
 
289
289
  // ../shared/src/e2b.ts
290
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-15-v2";
290
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-15-v4";
291
291
 
292
292
  // ../shared/src/runtime-env.ts
293
293
  function parsePosixEnvFile(content) {
@@ -373,6 +373,30 @@ var InFlightMap = class {
373
373
  }
374
374
  };
375
375
 
376
+ // ../shared/src/slash-commands.ts
377
+ var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
378
+ function parseGoalCommand(message) {
379
+ const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
380
+ const value = match?.[1]?.trim();
381
+ if (!value) return null;
382
+ if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
383
+ return { type: "set", objective: value };
384
+ }
385
+ function getGoalCommand(message, goalMode) {
386
+ if (!goalMode) return parseGoalCommand(message);
387
+ const value = message.trim();
388
+ if (!value) return null;
389
+ if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
390
+ return { type: "set", objective: value };
391
+ }
392
+ function getGoalCommandObjectiveValidationError(message, goalMode) {
393
+ const command = getGoalCommand(message, goalMode);
394
+ if (command?.type !== "set") return null;
395
+ const length = command.objective.length;
396
+ if (length <= MAX_CODEX_GOAL_OBJECTIVE_CHARS) return null;
397
+ return `Goal objective must be at most ${MAX_CODEX_GOAL_OBJECTIVE_CHARS} characters (${length} provided). Shorten the /goal objective, or send the extra details as a regular message after setting the goal.`;
398
+ }
399
+
376
400
  // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
377
401
  var SECTION = `### Computer use (Linux desktop control)
378
402
  Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with Xvfb / openbox / x11vnc / noVNC pre-installed and the live noVNC viewer is automatically published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
@@ -1874,30 +1898,6 @@ var DEFAULT_DEFAULT_SKILLS = {
1874
1898
  }
1875
1899
  };
1876
1900
 
1877
- // ../shared/src/prompts.ts
1878
- var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
1879
- function parseGoalCommand(message) {
1880
- const match = message.trim().match(/^\/goal(?:\s+([\s\S]*))?$/i);
1881
- const value = match?.[1]?.trim();
1882
- if (!value) return null;
1883
- if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
1884
- return { type: "set", objective: value };
1885
- }
1886
- function getGoalCommand(message, goalMode) {
1887
- if (!goalMode) return parseGoalCommand(message);
1888
- const value = message.trim();
1889
- if (!value) return null;
1890
- if (/^(clear|reset|unset)$/i.test(value)) return { type: "clear" };
1891
- return { type: "set", objective: value };
1892
- }
1893
- function getGoalCommandObjectiveValidationError(message, goalMode) {
1894
- const command = getGoalCommand(message, goalMode);
1895
- if (command?.type !== "set") return null;
1896
- const length = command.objective.length;
1897
- if (length <= MAX_CODEX_GOAL_OBJECTIVE_CHARS) return null;
1898
- return `Goal objective must be at most ${MAX_CODEX_GOAL_OBJECTIVE_CHARS} characters (${length} provided). Shorten the /goal objective, or send the extra details as a regular message after setting the goal.`;
1899
- }
1900
-
1901
1901
  // ../shared/src/replicas-config.ts
1902
1902
  import { parse as parseYaml } from "yaml";
1903
1903
 
@@ -2663,6 +2663,9 @@ function isGitHubUrl(url) {
2663
2663
  return GITHUB_HOST_RE.test(url);
2664
2664
  }
2665
2665
 
2666
+ // ../shared/src/skill-registry.ts
2667
+ var SKILL_REGISTRY_MANIFEST_VERSION = 1;
2668
+
2666
2669
  // src/utils/exec.ts
2667
2670
  var execAsync = promisify(exec);
2668
2671
  var execFileAsync = promisify(execFile);
@@ -2816,7 +2819,7 @@ var BaseRefreshManager = class {
2816
2819
  this.health.lastErrorMessage = message;
2817
2820
  if (attempt < 3) {
2818
2821
  console.warn(`[${this.managerName}] Initial refresh attempt ${attempt} failed, retrying in 2s...`);
2819
- await new Promise((resolve3) => setTimeout(resolve3, 2e3));
2822
+ await new Promise((resolve4) => setTimeout(resolve4, 2e3));
2820
2823
  } else {
2821
2824
  console.error(`[${this.managerName}] Initial refresh failed after 3 attempts:`, error);
2822
2825
  }
@@ -3735,7 +3738,7 @@ var GitService = class {
3735
3738
  }
3736
3739
  }
3737
3740
  readUntrackedDiff(repoPath, paths) {
3738
- return new Promise((resolve3) => {
3741
+ return new Promise((resolve4) => {
3739
3742
  const child = spawn("git", ["diff", "--", ...paths], {
3740
3743
  cwd: repoPath,
3741
3744
  stdio: ["ignore", "pipe", "pipe"]
@@ -3756,12 +3759,12 @@ var GitService = class {
3756
3759
  });
3757
3760
  child.stderr.on("data", () => {
3758
3761
  });
3759
- child.on("error", () => resolve3(""));
3762
+ child.on("error", () => resolve4(""));
3760
3763
  child.on("close", (code) => {
3761
3764
  if (code === 0 || code === 1 || truncated) {
3762
- resolve3(Buffer.concat(chunks).toString("utf-8"));
3765
+ resolve4(Buffer.concat(chunks).toString("utf-8"));
3763
3766
  } else {
3764
- resolve3("");
3767
+ resolve4("");
3765
3768
  }
3766
3769
  });
3767
3770
  });
@@ -4002,9 +4005,9 @@ var StreamWriter = class {
4002
4005
  return true;
4003
4006
  }
4004
4007
  flush() {
4005
- return new Promise((resolve3) => {
4008
+ return new Promise((resolve4) => {
4006
4009
  if (!this.stream) {
4007
- resolve3();
4010
+ resolve4();
4008
4011
  return;
4009
4012
  }
4010
4013
  if (this.flushTimer) {
@@ -4013,7 +4016,7 @@ var StreamWriter = class {
4013
4016
  }
4014
4017
  const s = this.stream;
4015
4018
  this.stream = null;
4016
- s.end(() => resolve3());
4019
+ s.end(() => resolve4());
4017
4020
  });
4018
4021
  }
4019
4022
  scheduleDropWarning() {
@@ -4494,7 +4497,7 @@ var ReplicasConfigService = class {
4494
4497
  await this.logToFile(`[${params.repoName}] --- Running: ${params.label} ---`);
4495
4498
  emit(`$ ${params.label}
4496
4499
  `);
4497
- return new Promise((resolve3) => {
4500
+ return new Promise((resolve4) => {
4498
4501
  const proc = spawn2("bash", ["-lc", params.command], {
4499
4502
  cwd: params.cwd,
4500
4503
  env: process.env,
@@ -4505,7 +4508,7 @@ var ReplicasConfigService = class {
4505
4508
  const finish = (result) => {
4506
4509
  if (settled) return;
4507
4510
  settled = true;
4508
- resolve3(result);
4511
+ resolve4(result);
4509
4512
  };
4510
4513
  const timer = setTimeout(() => {
4511
4514
  timedOut = true;
@@ -4931,7 +4934,7 @@ async function registerDesktopPreview() {
4931
4934
  if (await attemptRegistration()) {
4932
4935
  return;
4933
4936
  }
4934
- await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS));
4937
+ await new Promise((resolve4) => setTimeout(resolve4, RETRY_DELAY_MS));
4935
4938
  }
4936
4939
  console.error(
4937
4940
  `[DesktopPreview] Gave up registering port ${DESKTOP_NOVNC_PORT} after ${REGISTRATION_TIMEOUT_MS}ms`
@@ -4940,9 +4943,9 @@ async function registerDesktopPreview() {
4940
4943
 
4941
4944
  // src/services/chat/chat-service.ts
4942
4945
  import { existsSync as existsSync7 } from "fs";
4943
- import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile12, rename as rename2, rm } from "fs/promises";
4946
+ import { appendFile as appendFile4, copyFile, mkdir as mkdir11, readFile as readFile13, rename as rename2, rm } from "fs/promises";
4944
4947
  import { homedir as homedir14 } from "os";
4945
- import { join as join16 } from "path";
4948
+ import { join as join17 } from "path";
4946
4949
  import { randomUUID as randomUUID5 } from "crypto";
4947
4950
 
4948
4951
  // src/managers/claude-manager.ts
@@ -4950,7 +4953,7 @@ import {
4950
4953
  query
4951
4954
  } from "@anthropic-ai/claude-agent-sdk";
4952
4955
  import { randomUUID as randomUUID4 } from "crypto";
4953
- import { join as join13 } from "path";
4956
+ import { join as join14 } from "path";
4954
4957
  import { mkdir as mkdir10, appendFile as appendFile2 } from "fs/promises";
4955
4958
  import { homedir as homedir11 } from "os";
4956
4959
 
@@ -5800,6 +5803,248 @@ function extractToolCommand(input) {
5800
5803
  return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
5801
5804
  }
5802
5805
 
5806
+ // src/services/skill-registry-service.ts
5807
+ import { readFile as readFile9, readdir as readdir3, stat as stat2 } from "fs/promises";
5808
+ import { dirname as dirname3, isAbsolute, join as join13, relative, resolve } from "path";
5809
+ var REGISTRY_ROOT_DIR = ".replicas/skill-registries";
5810
+ var REGISTRY_MANIFEST = "manifest.json";
5811
+ async function getSkillRegistryInventory(homeDir) {
5812
+ const registryRoot = getRegistryRoot(homeDir);
5813
+ const manifest = await readManifest(homeDir);
5814
+ if (!manifest) {
5815
+ return emptyInventory(registryRoot);
5816
+ }
5817
+ const registries = [];
5818
+ for (const registry of manifest.registries) {
5819
+ if (!isPathInside(registryRoot, registry.checkoutPath)) {
5820
+ console.warn(`[SkillRegistry] Ignoring registry outside root: ${registry.checkoutPath}`);
5821
+ continue;
5822
+ }
5823
+ if (!await directoryExists(registry.checkoutPath)) {
5824
+ console.warn(`[SkillRegistry] Ignoring missing registry checkout: ${registry.checkoutPath}`);
5825
+ continue;
5826
+ }
5827
+ const contents = await scanRegistry(registry.checkoutPath);
5828
+ registries.push({ ...registry, ...contents });
5829
+ }
5830
+ return {
5831
+ registryRoot,
5832
+ registries,
5833
+ claudePluginRoots: uniqueFlat(registries, (registry) => registry.claudePluginRoots),
5834
+ standaloneSkills: uniqueStandaloneSkills(registries.flatMap((registry) => registry.standaloneSkills)),
5835
+ codexMarketplaceCwds: uniqueFlat(registries, (registry) => registry.codexMarketplaceCwds)
5836
+ };
5837
+ }
5838
+ async function buildClaudeRegistryConfig(homeDir) {
5839
+ const inventory = await getSkillRegistryInventory(homeDir);
5840
+ return {
5841
+ plugins: inventory.claudePluginRoots.map((path5) => ({ type: "local", path: path5 })),
5842
+ enableAllSkills: inventory.claudePluginRoots.length > 0 || inventory.standaloneSkills.length > 0
5843
+ };
5844
+ }
5845
+ async function applyCodexSkillRegistries(params) {
5846
+ const inventory = await getSkillRegistryInventory(params.homeDir);
5847
+ if (inventory.registries.length === 0) return;
5848
+ await installCodexRegistryPlugins(params.client, inventory);
5849
+ }
5850
+ async function scanRegistry(registryDir) {
5851
+ const claudePluginRoots = await findClaudePluginRoots(registryDir);
5852
+ const skillCollections = await findSkillCollections(registryDir);
5853
+ const topLevelSkills = await findTopLevelSkills(registryDir);
5854
+ const codexMarketplaceCwds = await hasCodexMarketplace(registryDir) ? [registryDir] : [];
5855
+ return {
5856
+ claudePluginRoots,
5857
+ standaloneSkills: uniqueStandaloneSkills([
5858
+ ...skillCollections.flatMap((collection) => collection.skillDirs.map((skillDir) => ({
5859
+ source: collection.source,
5860
+ skillDir
5861
+ }))),
5862
+ ...topLevelSkills
5863
+ ]),
5864
+ codexMarketplaceCwds
5865
+ };
5866
+ }
5867
+ async function findSkillCollections(registryDir) {
5868
+ const candidateRoots = [
5869
+ { source: "skills", root: join13(registryDir, "skills") },
5870
+ { source: "agents", root: join13(registryDir, ".agents", "skills") },
5871
+ { source: "codex", root: join13(registryDir, ".codex", "skills") },
5872
+ { source: "claude", root: join13(registryDir, ".claude", "skills") }
5873
+ ];
5874
+ const collections = [];
5875
+ for (const { source, root } of candidateRoots) {
5876
+ const skillDirs = await findSkillDirsInRoot(root);
5877
+ if (skillDirs.length > 0) {
5878
+ collections.push({ source, root, skillDirs });
5879
+ }
5880
+ }
5881
+ return collections;
5882
+ }
5883
+ async function findSkillDirsInRoot(root) {
5884
+ const skillDirs = [];
5885
+ for (const dirent of await safeReadDir(root)) {
5886
+ const skillDir = join13(root, dirent.name);
5887
+ if (!await isDirectoryDirent(dirent, skillDir)) continue;
5888
+ if (await fileExists(join13(skillDir, "SKILL.md"))) {
5889
+ skillDirs.push(skillDir);
5890
+ }
5891
+ }
5892
+ return skillDirs;
5893
+ }
5894
+ async function findTopLevelSkills(registryDir) {
5895
+ const skills = [];
5896
+ for (const dirent of await safeReadDir(registryDir)) {
5897
+ if (dirent.name.startsWith(".") || dirent.name === "skills" || dirent.name === "plugins") continue;
5898
+ const skillDir = join13(registryDir, dirent.name);
5899
+ if (!await isDirectoryDirent(dirent, skillDir)) continue;
5900
+ if (await fileExists(join13(skillDir, "SKILL.md"))) {
5901
+ skills.push({ source: "root", skillDir });
5902
+ }
5903
+ }
5904
+ return skills;
5905
+ }
5906
+ async function findClaudePluginRoots(registryDir) {
5907
+ const roots = [];
5908
+ async function walk(dir) {
5909
+ for (const dirent of await safeReadDir(dir)) {
5910
+ if (dirent.name === ".git") continue;
5911
+ const child = join13(dir, dirent.name);
5912
+ if (!await isDirectoryDirent(dirent, child)) continue;
5913
+ if (dirent.name === ".claude-plugin") {
5914
+ if (await fileExists(join13(child, "plugin.json"))) {
5915
+ roots.push(dirname3(child));
5916
+ }
5917
+ continue;
5918
+ }
5919
+ await walk(child);
5920
+ }
5921
+ }
5922
+ await walk(registryDir);
5923
+ return uniqueStrings(roots);
5924
+ }
5925
+ async function hasCodexMarketplace(registryDir) {
5926
+ return await fileExists(join13(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join13(registryDir, ".codex", "plugins", "marketplace.json"));
5927
+ }
5928
+ async function installCodexRegistryPlugins(client, inventory) {
5929
+ const cwds = inventory.codexMarketplaceCwds;
5930
+ if (cwds.length === 0) return;
5931
+ try {
5932
+ const response = await client.request("plugin/list", { cwds });
5933
+ for (const marketplace of response.marketplaces) {
5934
+ if (!marketplace.path || !isPathInside(inventory.registryRoot, marketplace.path)) continue;
5935
+ for (const plugin of marketplace.plugins) {
5936
+ if (plugin.installed || plugin.installPolicy === "NOT_AVAILABLE" || plugin.availability !== "AVAILABLE") {
5937
+ continue;
5938
+ }
5939
+ try {
5940
+ await client.request("plugin/install", {
5941
+ marketplacePath: marketplace.path,
5942
+ pluginName: plugin.name
5943
+ });
5944
+ } catch (error) {
5945
+ console.warn(`[SkillRegistry] Failed to install Codex registry plugin ${plugin.name}:`, error);
5946
+ }
5947
+ }
5948
+ }
5949
+ } catch (error) {
5950
+ console.warn("[SkillRegistry] Failed to list Codex registry plugins:", error);
5951
+ }
5952
+ }
5953
+ async function readManifest(homeDir) {
5954
+ try {
5955
+ const raw = await readFile9(getManifestPath(homeDir), "utf8");
5956
+ const parsed = JSON.parse(raw);
5957
+ if (!isManifest(parsed)) {
5958
+ console.warn("[SkillRegistry] Ignoring invalid skill registry manifest.");
5959
+ return null;
5960
+ }
5961
+ return parsed;
5962
+ } catch (error) {
5963
+ if (isNotFoundError(error)) return null;
5964
+ throw error;
5965
+ }
5966
+ }
5967
+ function isManifest(value) {
5968
+ if (typeof value !== "object" || value === null) return false;
5969
+ return "version" in value && value.version === SKILL_REGISTRY_MANIFEST_VERSION && "registries" in value && Array.isArray(value.registries) && value.registries.every(isSkillRegistryEntry);
5970
+ }
5971
+ function isSkillRegistryEntry(value) {
5972
+ if (typeof value !== "object" || value === null) return false;
5973
+ return "index" in value && typeof value.index === "number" && "name" in value && typeof value.name === "string" && "url" in value && typeof value.url === "string" && "checkoutPath" in value && typeof value.checkoutPath === "string";
5974
+ }
5975
+ function getRegistryRoot(homeDir) {
5976
+ return join13(homeDir, REGISTRY_ROOT_DIR);
5977
+ }
5978
+ function getManifestPath(homeDir) {
5979
+ return join13(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
5980
+ }
5981
+ function emptyInventory(registryRoot) {
5982
+ return {
5983
+ registryRoot,
5984
+ registries: [],
5985
+ claudePluginRoots: [],
5986
+ standaloneSkills: [],
5987
+ codexMarketplaceCwds: []
5988
+ };
5989
+ }
5990
+ function uniqueFlat(items, getValues) {
5991
+ return uniqueStrings(items.flatMap(getValues));
5992
+ }
5993
+ function uniqueStrings(values) {
5994
+ return Array.from(new Set(values));
5995
+ }
5996
+ function uniqueStandaloneSkills(skills) {
5997
+ const seen = /* @__PURE__ */ new Set();
5998
+ const unique = [];
5999
+ for (const skill of skills) {
6000
+ const key = `${skill.source}\0${skill.skillDir}`;
6001
+ if (seen.has(key)) continue;
6002
+ seen.add(key);
6003
+ unique.push(skill);
6004
+ }
6005
+ return unique;
6006
+ }
6007
+ async function safeReadDir(path5) {
6008
+ try {
6009
+ return await readdir3(path5, { withFileTypes: true });
6010
+ } catch (error) {
6011
+ if (isNotFoundError(error)) return [];
6012
+ throw error;
6013
+ }
6014
+ }
6015
+ async function isDirectoryDirent(dirent, path5) {
6016
+ if (dirent.isDirectory()) return true;
6017
+ if (!dirent.isSymbolicLink()) return false;
6018
+ return directoryExists(path5);
6019
+ }
6020
+ async function directoryExists(path5) {
6021
+ try {
6022
+ const pathStat = await stat2(path5);
6023
+ return pathStat.isDirectory();
6024
+ } catch (error) {
6025
+ if (isNotFoundError(error)) return false;
6026
+ throw error;
6027
+ }
6028
+ }
6029
+ async function fileExists(path5) {
6030
+ try {
6031
+ const pathStat = await stat2(path5);
6032
+ return pathStat.isFile();
6033
+ } catch (error) {
6034
+ if (isNotFoundError(error)) return false;
6035
+ throw error;
6036
+ }
6037
+ }
6038
+ function isPathInside(parentPath, childPath) {
6039
+ const parent = resolve(parentPath);
6040
+ const child = resolve(childPath);
6041
+ const childRelativePath = relative(parent, child);
6042
+ return childRelativePath === "" || !childRelativePath.startsWith("..") && !isAbsolute(childRelativePath);
6043
+ }
6044
+ function isNotFoundError(error) {
6045
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6046
+ }
6047
+
5803
6048
  // src/managers/claude-manager.ts
5804
6049
  var PromptStream = class {
5805
6050
  queue = [];
@@ -5832,8 +6077,8 @@ var PromptStream = class {
5832
6077
  if (this.closed) {
5833
6078
  return Promise.resolve({ value: void 0, done: true });
5834
6079
  }
5835
- return new Promise((resolve3) => {
5836
- this.waiters.push(resolve3);
6080
+ return new Promise((resolve4) => {
6081
+ this.waiters.push(resolve4);
5837
6082
  });
5838
6083
  }
5839
6084
  };
@@ -5997,7 +6242,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5997
6242
  authRetrying = false;
5998
6243
  constructor(options) {
5999
6244
  super(options);
6000
- this.historyFile = options.historyFilePath ?? join13(homedir11(), ".replicas", "claude", "history.jsonl");
6245
+ this.historyFile = options.historyFilePath ?? join14(homedir11(), ".replicas", "claude", "history.jsonl");
6001
6246
  this.systemPromptOverride = options.systemPromptOverride;
6002
6247
  this.toolsOverride = options.tools;
6003
6248
  this.mcpServersConfig = options.mcpServers;
@@ -6071,14 +6316,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6071
6316
  const requestId = randomUUID4();
6072
6317
  const toolUseId = options.toolUseID;
6073
6318
  const { options: requestOptions, questions: requestQuestions } = handler.getRequest(input);
6074
- const result = await new Promise((resolve3) => {
6319
+ const result = await new Promise((resolve4) => {
6075
6320
  this.pendingToolInputs.set(requestId, {
6076
6321
  requestId,
6077
6322
  toolUseId,
6078
6323
  toolName,
6079
6324
  input,
6080
6325
  handler,
6081
- resolve: resolve3
6326
+ resolve: resolve4
6082
6327
  });
6083
6328
  const onAbort = () => {
6084
6329
  const pending = this.pendingToolInputs.get(requestId);
@@ -6275,8 +6520,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6275
6520
  this.pendingInterrupt = false;
6276
6521
  return;
6277
6522
  }
6278
- await new Promise((resolve3, reject) => {
6279
- this.pendingTurn = { resolve: resolve3, reject };
6523
+ await new Promise((resolve4, reject) => {
6524
+ this.pendingTurn = { resolve: resolve4, reject };
6280
6525
  promptStream.push(userMessage);
6281
6526
  });
6282
6527
  }
@@ -6351,6 +6596,15 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6351
6596
  ...useDefaultToolPolicy ? ALWAYS_DISALLOWED_TOOLS : [],
6352
6597
  ...interactiveAllowed ? [] : INTERACTIVE_TOOL_NAMES
6353
6598
  ];
6599
+ let registryPlugins = [];
6600
+ let enableRegistrySkills = false;
6601
+ try {
6602
+ const registryConfig = await buildClaudeRegistryConfig(ENGINE_ENV.HOME_DIR);
6603
+ registryPlugins = registryConfig.plugins;
6604
+ enableRegistrySkills = registryConfig.enableAllSkills;
6605
+ } catch (error) {
6606
+ console.warn("[ClaudeManager] Failed to load skill registry config:", error);
6607
+ }
6354
6608
  const promptStream = new PromptStream();
6355
6609
  const response = query({
6356
6610
  prompt: promptStream,
@@ -6366,6 +6620,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6366
6620
  settingSources: ["user", "project", "local"],
6367
6621
  systemPrompt,
6368
6622
  ...this.mcpServersConfig ? { mcpServers: this.mcpServersConfig } : {},
6623
+ ...registryPlugins.length > 0 ? { plugins: registryPlugins } : {},
6624
+ ...enableRegistrySkills ? { skills: "all" } : {},
6369
6625
  env: queryEnv,
6370
6626
  model: resolvedModel,
6371
6627
  includePartialMessages: true,
@@ -6648,7 +6904,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6648
6904
  }
6649
6905
  }
6650
6906
  async initialize() {
6651
- const historyDir = join13(homedir11(), ".replicas", "claude");
6907
+ const historyDir = join14(homedir11(), ".replicas", "claude");
6652
6908
  await mkdir10(historyDir, { recursive: true });
6653
6909
  if (this.initialSessionId) {
6654
6910
  this.sessionId = this.initialSessionId;
@@ -6731,13 +6987,13 @@ var AspClient = class {
6731
6987
  }
6732
6988
  const id = this.nextId;
6733
6989
  this.nextId += 1;
6734
- const promise = new Promise((resolve3, reject) => {
6990
+ const promise = new Promise((resolve4, reject) => {
6735
6991
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
6736
6992
  const timer = timeoutMs > 0 ? setTimeout(() => {
6737
6993
  this.pending.delete(id);
6738
6994
  reject(new Error(`ASP request timed out for ${method}`));
6739
6995
  }, timeoutMs) : null;
6740
- this.pending.set(id, { resolve: resolve3, reject, method, timer });
6996
+ this.pending.set(id, { resolve: resolve4, reject, method, timer });
6741
6997
  });
6742
6998
  this.write({ method, id, params });
6743
6999
  return promise;
@@ -6887,7 +7143,7 @@ var AspClient = class {
6887
7143
  // src/managers/codex-asp/app-server-process.ts
6888
7144
  var DEFAULT_CODEX_BINARY = "codex";
6889
7145
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
6890
- var ENGINE_PACKAGE_VERSION = "0.1.318";
7146
+ var ENGINE_PACKAGE_VERSION = "0.1.320";
6891
7147
  var INITIALIZE_METHOD = "initialize";
6892
7148
  var INITIALIZED_NOTIFICATION = "initialized";
6893
7149
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -6992,13 +7248,13 @@ var AppServerProcess = class {
6992
7248
  if (!child || child.killed) {
6993
7249
  return;
6994
7250
  }
6995
- await new Promise((resolve3) => {
7251
+ await new Promise((resolve4) => {
6996
7252
  const timer = setTimeout(() => {
6997
7253
  child.kill("SIGKILL");
6998
7254
  }, 2e3);
6999
7255
  child.once("exit", () => {
7000
7256
  clearTimeout(timer);
7001
- resolve3();
7257
+ resolve4();
7002
7258
  });
7003
7259
  child.kill("SIGTERM");
7004
7260
  });
@@ -7022,8 +7278,8 @@ var AppServerProcess = class {
7022
7278
  }
7023
7279
  this.shuttingDown = true;
7024
7280
  child.kill("SIGKILL");
7025
- await new Promise((resolve3) => {
7026
- child.once("exit", () => resolve3());
7281
+ await new Promise((resolve4) => {
7282
+ child.once("exit", () => resolve4());
7027
7283
  });
7028
7284
  }
7029
7285
  };
@@ -7690,7 +7946,7 @@ var TranscriptUpdateCoalescer = class {
7690
7946
  };
7691
7947
 
7692
7948
  // src/managers/codex-asp/codex-history-file.ts
7693
- import { appendFile as appendFile3, readFile as readFile9 } from "fs/promises";
7949
+ import { appendFile as appendFile3, readFile as readFile10 } from "fs/promises";
7694
7950
  var CodexHistoryFile = class {
7695
7951
  constructor(filePath) {
7696
7952
  this.filePath = filePath;
@@ -7708,7 +7964,7 @@ var CodexHistoryFile = class {
7708
7964
  }
7709
7965
  async load() {
7710
7966
  try {
7711
- const content = await readFile9(this.filePath, "utf-8");
7967
+ const content = await readFile10(this.filePath, "utf-8");
7712
7968
  return parseAgentEventJsonlWithCodexAspTranscript(content);
7713
7969
  } catch (error) {
7714
7970
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
@@ -7734,6 +7990,7 @@ var CodexAspManager = class extends CodingAgentManager {
7734
7990
  transcriptUpdateCoalescer = new TranscriptUpdateCoalescer((threadId) => {
7735
7991
  this.flushTranscriptUpdated(threadId);
7736
7992
  });
7993
+ skillRegistriesApplied = false;
7737
7994
  constructor(options) {
7738
7995
  super(options);
7739
7996
  this.historyFile = options.historyFilePath ? new CodexHistoryFile(options.historyFilePath) : null;
@@ -7919,6 +8176,7 @@ var CodexAspManager = class extends CodingAgentManager {
7919
8176
  }
7920
8177
  }
7921
8178
  async ensureThread(host, request, developerInstructions) {
8179
+ await this.applySkillRegistries(host);
7922
8180
  const existingThreadId = this.currentThreadId;
7923
8181
  if (existingThreadId) {
7924
8182
  if (!this.threadAttached) {
@@ -7943,6 +8201,18 @@ var CodexAspManager = class extends CodingAgentManager {
7943
8201
  await this.onSaveSessionId(this.currentThreadId);
7944
8202
  return threadId;
7945
8203
  }
8204
+ async applySkillRegistries(host) {
8205
+ if (this.skillRegistriesApplied) return;
8206
+ try {
8207
+ await applyCodexSkillRegistries({
8208
+ client: host.client,
8209
+ homeDir: ENGINE_ENV.HOME_DIR
8210
+ });
8211
+ this.skillRegistriesApplied = true;
8212
+ } catch (error) {
8213
+ console.warn("[CodexAspManager] Failed to apply skill registries:", error);
8214
+ }
8215
+ }
7946
8216
  async runTurn(host, threadId, request, developerInstructions) {
7947
8217
  const { params, tempImagePaths } = await buildTurnStartParams(threadId, request, developerInstructions);
7948
8218
  return this.observeTurn(host, threadId, request, async () => {
@@ -7965,8 +8235,8 @@ var CodexAspManager = class extends CodingAgentManager {
7965
8235
  }
7966
8236
  async observeTurn(host, threadId, request, startTurn) {
7967
8237
  let resolveCompleted;
7968
- const completed = new Promise((resolve3) => {
7969
- resolveCompleted = resolve3;
8238
+ const completed = new Promise((resolve4) => {
8239
+ resolveCompleted = resolve4;
7970
8240
  });
7971
8241
  let rejectDisposed = () => {
7972
8242
  };
@@ -8166,7 +8436,9 @@ var CodexAspManager = class extends CodingAgentManager {
8166
8436
  serverRequest.params.itemId,
8167
8437
  serverRequest.params.command ?? "",
8168
8438
  result.reason,
8169
- serverRequest.params.startedAtMs
8439
+ serverRequest.params.startedAtMs,
8440
+ serverRequest.params.cwd,
8441
+ serverRequest.params.commandActions
8170
8442
  );
8171
8443
  host.client.respond(requestId, { decision: "decline" });
8172
8444
  return;
@@ -8203,7 +8475,9 @@ var CodexAspManager = class extends CodingAgentManager {
8203
8475
  serverRequest.params.callId,
8204
8476
  command,
8205
8477
  result.reason,
8206
- Date.now()
8478
+ Date.now(),
8479
+ serverRequest.params.cwd,
8480
+ [{ type: "unknown", command }]
8207
8481
  );
8208
8482
  host.client.respond(requestId, { decision: "denied" });
8209
8483
  return;
@@ -8260,16 +8534,21 @@ var CodexAspManager = class extends CodingAgentManager {
8260
8534
  nextTranscriptSequence() {
8261
8535
  return this.codexAspSequence++;
8262
8536
  }
8263
- recordBlockedCommand(threadId, turnId, itemId, command, reason, startedAtMs) {
8537
+ recordBlockedCommand(threadId, turnId, itemId, command, reason, startedAtMs, cwd, commandActions) {
8264
8538
  const output = reason?.includes("Blocked by organization policy") ? reason : `Blocked by organization policy: ${reason || "agents may not merge pull requests."}`;
8265
8539
  const timestamp = timestampFromMilliseconds(startedAtMs);
8266
8540
  const blockedItem = {
8267
8541
  type: "commandExecution",
8268
8542
  id: itemId,
8269
8543
  command,
8544
+ cwd: cwd ?? this.workingDirectory,
8545
+ processId: null,
8546
+ source: "agent",
8547
+ commandActions: commandActions ?? [{ type: "unknown", command }],
8270
8548
  aggregatedOutput: output,
8271
8549
  exitCode: null,
8272
- status: "declined"
8550
+ status: "declined",
8551
+ durationMs: null
8273
8552
  };
8274
8553
  this.upsertTranscriptItem(threadId, turnId, blockedItem, timestamp, "failed", "completed");
8275
8554
  this.emitTranscriptUpdated(threadId, { immediate: true });
@@ -9191,12 +9470,12 @@ var KeepAliveService = class _KeepAliveService {
9191
9470
  var keepAliveService = new KeepAliveService();
9192
9471
 
9193
9472
  // src/services/canvas-service.ts
9194
- import { readdir as readdir3, readFile as readFile10, stat as stat2 } from "fs/promises";
9473
+ import { readdir as readdir4, readFile as readFile11, stat as stat3 } from "fs/promises";
9195
9474
  import { homedir as homedir12 } from "os";
9196
- import { join as join14 } from "path";
9475
+ import { join as join15 } from "path";
9197
9476
  var CANVAS_DIRECTORIES = [
9198
- join14(homedir12(), ".claude", "plans"),
9199
- join14(homedir12(), ".replicas", "canvas")
9477
+ join15(homedir12(), ".claude", "plans"),
9478
+ join15(homedir12(), ".replicas", "canvas")
9200
9479
  ];
9201
9480
  var CanvasService = class {
9202
9481
  async listItems() {
@@ -9204,7 +9483,7 @@ var CanvasService = class {
9204
9483
  for (const directory of CANVAS_DIRECTORIES) {
9205
9484
  let entries;
9206
9485
  try {
9207
- entries = await readdir3(directory, { withFileTypes: true });
9486
+ entries = await readdir4(directory, { withFileTypes: true });
9208
9487
  } catch {
9209
9488
  continue;
9210
9489
  }
@@ -9215,7 +9494,7 @@ var CanvasService = class {
9215
9494
  const { kind } = classifyCanvasFilename(entry.name);
9216
9495
  let sizeBytes = 0;
9217
9496
  try {
9218
- const s = await stat2(join14(directory, entry.name));
9497
+ const s = await stat3(join15(directory, entry.name));
9219
9498
  sizeBytes = s.size;
9220
9499
  } catch {
9221
9500
  continue;
@@ -9230,11 +9509,11 @@ var CanvasService = class {
9230
9509
  if (!safe) return null;
9231
9510
  const { kind, mimeType } = classifyCanvasFilename(safe);
9232
9511
  for (const directory of CANVAS_DIRECTORIES) {
9233
- const filePath = join14(directory, safe);
9512
+ const filePath = join15(directory, safe);
9234
9513
  let sizeBytes = 0;
9235
9514
  let updatedAt = "";
9236
9515
  try {
9237
- const s = await stat2(filePath);
9516
+ const s = await stat3(filePath);
9238
9517
  sizeBytes = s.size;
9239
9518
  updatedAt = s.mtime.toISOString();
9240
9519
  } catch {
@@ -9251,7 +9530,7 @@ var CanvasService = class {
9251
9530
  };
9252
9531
  }
9253
9532
  try {
9254
- const bytes = await readFile10(filePath);
9533
+ const bytes = await readFile11(filePath);
9255
9534
  return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
9256
9535
  } catch {
9257
9536
  continue;
@@ -9366,14 +9645,14 @@ async function reconcileCanvasItems(filenames) {
9366
9645
  }
9367
9646
 
9368
9647
  // src/services/upload-chat-transcripts.ts
9369
- import { readdir as readdir4, readFile as readFile11 } from "fs/promises";
9370
- import { basename, join as join15 } from "path";
9648
+ import { readdir as readdir5, readFile as readFile12 } from "fs/promises";
9649
+ import { basename, join as join16 } from "path";
9371
9650
  import { homedir as homedir13 } from "os";
9372
- var ENGINE_DIR2 = join15(homedir13(), ".replicas", "engine");
9651
+ var ENGINE_DIR2 = join16(homedir13(), ".replicas", "engine");
9373
9652
  var HISTORY_DIRS = [
9374
- join15(ENGINE_DIR2, "claude-histories"),
9375
- join15(ENGINE_DIR2, "relay-histories"),
9376
- join15(ENGINE_DIR2, "codex-histories")
9653
+ join16(ENGINE_DIR2, "claude-histories"),
9654
+ join16(ENGINE_DIR2, "relay-histories"),
9655
+ join16(ENGINE_DIR2, "codex-histories")
9377
9656
  ];
9378
9657
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9379
9658
  let flushed = 0;
@@ -9382,7 +9661,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9382
9661
  for (const dir of HISTORY_DIRS) {
9383
9662
  let entries;
9384
9663
  try {
9385
- entries = await readdir4(dir);
9664
+ entries = await readdir5(dir);
9386
9665
  } catch {
9387
9666
  continue;
9388
9667
  }
@@ -9390,7 +9669,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9390
9669
  if (!entry.endsWith(".jsonl")) continue;
9391
9670
  const chatId = basename(entry, ".jsonl");
9392
9671
  tasks.push(
9393
- uploadChatTranscript(chatId, join15(dir, entry), chatsById.get(chatId)).then(() => {
9672
+ uploadChatTranscript(chatId, join16(dir, entry), chatsById.get(chatId)).then(() => {
9394
9673
  flushed++;
9395
9674
  }).catch((err) => {
9396
9675
  failed++;
@@ -9403,7 +9682,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
9403
9682
  return { flushed, failed };
9404
9683
  }
9405
9684
  async function uploadChatTranscript(chatId, filePath, chat) {
9406
- const bytes = await readFile11(filePath);
9685
+ const bytes = await readFile12(filePath);
9407
9686
  if (bytes.byteLength === 0) return;
9408
9687
  const form = new FormData();
9409
9688
  form.append("chat_id", chatId);
@@ -9460,18 +9739,18 @@ var DuplicateDefaultChatError = class extends Error {
9460
9739
  };
9461
9740
 
9462
9741
  // src/services/chat/chat-service.ts
9463
- var ENGINE_DIR3 = join16(homedir14(), ".replicas", "engine");
9464
- var CHATS_FILE = join16(ENGINE_DIR3, "chats.json");
9465
- var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR3, "claude-histories");
9466
- var RELAY_HISTORY_DIR = join16(ENGINE_DIR3, "relay-histories");
9467
- var CODEX_HISTORY_DIR = join16(ENGINE_DIR3, "codex-histories");
9742
+ var ENGINE_DIR3 = join17(homedir14(), ".replicas", "engine");
9743
+ var CHATS_FILE = join17(ENGINE_DIR3, "chats.json");
9744
+ var CLAUDE_HISTORY_DIR = join17(ENGINE_DIR3, "claude-histories");
9745
+ var RELAY_HISTORY_DIR = join17(ENGINE_DIR3, "relay-histories");
9746
+ var CODEX_HISTORY_DIR = join17(ENGINE_DIR3, "codex-histories");
9468
9747
  var HISTORY_DIR_BY_PROVIDER = {
9469
9748
  claude: CLAUDE_HISTORY_DIR,
9470
9749
  relay: RELAY_HISTORY_DIR,
9471
9750
  codex: CODEX_HISTORY_DIR
9472
9751
  };
9473
- var CHAT_SENDERS_DIR = join16(ENGINE_DIR3, "chat-senders");
9474
- var CODEX_AUTH_PATH2 = join16(homedir14(), ".codex", "auth.json");
9752
+ var CHAT_SENDERS_DIR = join17(ENGINE_DIR3, "chat-senders");
9753
+ var CODEX_AUTH_PATH2 = join17(homedir14(), ".codex", "auth.json");
9475
9754
  var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
9476
9755
  function isChatMessageSender(value) {
9477
9756
  if (!isRecord4(value)) return false;
@@ -9669,7 +9948,7 @@ var ChatService = class {
9669
9948
  };
9670
9949
  }
9671
9950
  senderFilePath(chatId) {
9672
- return join16(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
9951
+ return join17(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
9673
9952
  }
9674
9953
  async appendSender(chatId, sender) {
9675
9954
  try {
@@ -9680,7 +9959,7 @@ var ChatService = class {
9680
9959
  }
9681
9960
  async readSenders(chatId) {
9682
9961
  try {
9683
- const content = await readFile12(this.senderFilePath(chatId), "utf-8");
9962
+ const content = await readFile13(this.senderFilePath(chatId), "utf-8");
9684
9963
  const lines = content.split("\n").filter((line) => line.trim().length > 0);
9685
9964
  const senders = [];
9686
9965
  for (const line of lines) {
@@ -9825,7 +10104,7 @@ var ChatService = class {
9825
10104
  return descendants;
9826
10105
  }
9827
10106
  async deleteHistoryFile(persisted) {
9828
- await rm(join16(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
10107
+ await rm(join17(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`), { force: true });
9829
10108
  await rm(this.senderFilePath(persisted.id), { force: true });
9830
10109
  }
9831
10110
  async getChatHistory(chatId) {
@@ -9896,7 +10175,7 @@ var ChatService = class {
9896
10175
  if (persisted.provider === "claude") {
9897
10176
  provider = new ClaudeManager({
9898
10177
  workingDirectory: this.workingDirectory,
9899
- historyFilePath: join16(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
10178
+ historyFilePath: join17(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
9900
10179
  initialSessionId: persisted.providerSessionId,
9901
10180
  onSaveSessionId: saveSession,
9902
10181
  onTurnComplete: onProviderTurnComplete,
@@ -9905,7 +10184,7 @@ var ChatService = class {
9905
10184
  } else if (persisted.provider === "relay") {
9906
10185
  provider = new RelayManager({
9907
10186
  workingDirectory: this.workingDirectory,
9908
- historyFilePath: join16(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
10187
+ historyFilePath: join17(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
9909
10188
  initialSessionId: persisted.providerSessionId,
9910
10189
  onSaveSessionId: saveSession,
9911
10190
  onTurnComplete: onProviderTurnComplete,
@@ -9916,7 +10195,7 @@ var ChatService = class {
9916
10195
  } else {
9917
10196
  provider = new CodexAspManager({
9918
10197
  workingDirectory: this.workingDirectory,
9919
- historyFilePath: join16(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
10198
+ historyFilePath: join17(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
9920
10199
  initialSessionId: persisted.providerSessionId,
9921
10200
  onSaveSessionId: saveSession,
9922
10201
  onTurnComplete: onProviderTurnComplete,
@@ -10054,7 +10333,7 @@ var ChatService = class {
10054
10333
  });
10055
10334
  uploadChatTranscript(
10056
10335
  chatId,
10057
- join16(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
10336
+ join17(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
10058
10337
  this.toSummary(chat)
10059
10338
  ).catch((err) => {
10060
10339
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -10069,7 +10348,7 @@ var ChatService = class {
10069
10348
  }
10070
10349
  async loadChats() {
10071
10350
  try {
10072
- const content = await readFile12(CHATS_FILE, "utf-8");
10351
+ const content = await readFile13(CHATS_FILE, "utf-8");
10073
10352
  return parsePersistedChatsContent(content);
10074
10353
  } catch (error) {
10075
10354
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -10084,7 +10363,7 @@ var ChatService = class {
10084
10363
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
10085
10364
  }
10086
10365
  try {
10087
- const backupContent = await readFile12(CHATS_BACKUP_FILE, "utf-8");
10366
+ const backupContent = await readFile13(CHATS_BACKUP_FILE, "utf-8");
10088
10367
  return parsePersistedChatsContent(backupContent);
10089
10368
  } catch (backupError) {
10090
10369
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -10169,8 +10448,8 @@ var ChatService = class {
10169
10448
 
10170
10449
  // src/services/repo-file-service.ts
10171
10450
  import { execFile as execFile2 } from "child_process";
10172
- import { readFile as readFile13, realpath, stat as stat3 } from "fs/promises";
10173
- import { join as join17, resolve, extname } from "path";
10451
+ import { readFile as readFile14, realpath, stat as stat4 } from "fs/promises";
10452
+ import { join as join18, resolve as resolve2, extname } from "path";
10174
10453
  var CACHE_TTL_MS = 3e4;
10175
10454
  var SEARCH_TIMEOUT_MS = 15e3;
10176
10455
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -10240,7 +10519,7 @@ function scoreMatch(query2, filePath) {
10240
10519
  return 0;
10241
10520
  }
10242
10521
  function execGitAsync(args, cwd, timeoutMs) {
10243
- return new Promise((resolve3, reject) => {
10522
+ return new Promise((resolve4, reject) => {
10244
10523
  const child = execFile2("git", args, {
10245
10524
  cwd,
10246
10525
  encoding: "utf-8",
@@ -10250,7 +10529,7 @@ function execGitAsync(args, cwd, timeoutMs) {
10250
10529
  if (error) {
10251
10530
  reject(error);
10252
10531
  } else {
10253
- resolve3(stdout);
10532
+ resolve4(stdout);
10254
10533
  }
10255
10534
  });
10256
10535
  child.stdin?.end();
@@ -10330,11 +10609,11 @@ var RepoFileService = class {
10330
10609
  const repo = repos.find((r) => r.name === repoName);
10331
10610
  if (!repo) return null;
10332
10611
  try {
10333
- const fullPath = await realpath(resolve(join17(repo.path, filePath)));
10612
+ const fullPath = await realpath(resolve2(join18(repo.path, filePath)));
10334
10613
  const repoRoot = await realpath(repo.path);
10335
10614
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
10336
10615
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
10337
- const fileStat = await stat3(fullPath);
10616
+ const fileStat = await stat4(fullPath);
10338
10617
  if (!fileStat.isFile()) return null;
10339
10618
  const sizeBytes = fileStat.size;
10340
10619
  if (isBinaryExtension(filePath)) {
@@ -10359,7 +10638,7 @@ var RepoFileService = class {
10359
10638
  tooLarge: true
10360
10639
  };
10361
10640
  }
10362
- const content = await readFile13(fullPath, "utf-8");
10641
+ const content = await readFile14(fullPath, "utf-8");
10363
10642
  return {
10364
10643
  repoName,
10365
10644
  path: filePath,
@@ -10437,21 +10716,21 @@ var RepoFileService = class {
10437
10716
  // src/v1-routes.ts
10438
10717
  import { Hono } from "hono";
10439
10718
  import { z as z2 } from "zod";
10440
- import { readdir as readdir6, stat as stat4, readFile as readFile16 } from "fs/promises";
10441
- import { join as join20, resolve as resolve2 } from "path";
10719
+ import { readdir as readdir7, stat as stat5, readFile as readFile17 } from "fs/promises";
10720
+ import { join as join21, resolve as resolve3 } from "path";
10442
10721
 
10443
10722
  // src/services/warm-hooks-service.ts
10444
10723
  import { spawn as spawn4 } from "child_process";
10445
- import { readFile as readFile15 } from "fs/promises";
10724
+ import { readFile as readFile16 } from "fs/promises";
10446
10725
  import { existsSync as existsSync8 } from "fs";
10447
- import { join as join19 } from "path";
10726
+ import { join as join20 } from "path";
10448
10727
 
10449
10728
  // src/services/warm-hook-logs-service.ts
10450
- import { mkdir as mkdir12, readFile as readFile14, writeFile as writeFile6, readdir as readdir5, appendFile as appendFile5, unlink as unlink3 } from "fs/promises";
10729
+ import { mkdir as mkdir12, readFile as readFile15, writeFile as writeFile6, readdir as readdir6, appendFile as appendFile5, unlink as unlink3 } from "fs/promises";
10451
10730
  import { homedir as homedir15 } from "os";
10452
- import { join as join18 } from "path";
10453
- var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
10454
- var CURRENT_RUN_LOG = join18(LOGS_DIR2, "current-run.log");
10731
+ import { join as join19 } from "path";
10732
+ var LOGS_DIR2 = join19(homedir15(), ".replicas", "warm-hook-logs");
10733
+ var CURRENT_RUN_LOG = join19(LOGS_DIR2, "current-run.log");
10455
10734
  var GLOBAL_FILENAME = "global.json";
10456
10735
  function withPreview2(stored) {
10457
10736
  const preview = buildHookOutputPreview(stored.output);
@@ -10468,7 +10747,7 @@ var WarmHookLogsService = class {
10468
10747
  hookName: "organization",
10469
10748
  ...entry
10470
10749
  };
10471
- await writeFile6(join18(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
10750
+ await writeFile6(join19(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
10472
10751
  `, "utf-8");
10473
10752
  }
10474
10753
  async saveEnvironmentHookLog(entry) {
@@ -10478,7 +10757,7 @@ var WarmHookLogsService = class {
10478
10757
  hookName: "environment",
10479
10758
  ...entry
10480
10759
  };
10481
- await writeFile6(join18(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
10760
+ await writeFile6(join19(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
10482
10761
  `, "utf-8");
10483
10762
  }
10484
10763
  async saveRepoHookLog(repoName, entry) {
@@ -10488,13 +10767,13 @@ var WarmHookLogsService = class {
10488
10767
  hookName: repoName,
10489
10768
  ...entry
10490
10769
  };
10491
- await writeFile6(join18(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
10770
+ await writeFile6(join19(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
10492
10771
  `, "utf-8");
10493
10772
  }
10494
10773
  async getAllLogs() {
10495
10774
  let files;
10496
10775
  try {
10497
- files = await readdir5(LOGS_DIR2);
10776
+ files = await readdir6(LOGS_DIR2);
10498
10777
  } catch (err) {
10499
10778
  if (err.code === "ENOENT") {
10500
10779
  return [];
@@ -10507,7 +10786,7 @@ var WarmHookLogsService = class {
10507
10786
  continue;
10508
10787
  }
10509
10788
  try {
10510
- const raw = await readFile14(join18(LOGS_DIR2, file), "utf-8");
10789
+ const raw = await readFile15(join19(LOGS_DIR2, file), "utf-8");
10511
10790
  const stored = JSON.parse(raw);
10512
10791
  logs.push(withPreview2(stored));
10513
10792
  } catch {
@@ -10536,7 +10815,7 @@ var WarmHookLogsService = class {
10536
10815
  }
10537
10816
  async getCurrentRunLog() {
10538
10817
  try {
10539
- return await readFile14(CURRENT_RUN_LOG, "utf-8");
10818
+ return await readFile15(CURRENT_RUN_LOG, "utf-8");
10540
10819
  } catch (err) {
10541
10820
  if (err.code === "ENOENT") return null;
10542
10821
  throw err;
@@ -10545,7 +10824,7 @@ var WarmHookLogsService = class {
10545
10824
  async getFullOutput(hookType, hookName) {
10546
10825
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
10547
10826
  try {
10548
- const raw = await readFile14(join18(LOGS_DIR2, filename), "utf-8");
10827
+ const raw = await readFile15(join19(LOGS_DIR2, filename), "utf-8");
10549
10828
  const stored = JSON.parse(raw);
10550
10829
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
10551
10830
  return null;
@@ -10564,12 +10843,12 @@ var warmHookLogsService = new WarmHookLogsService();
10564
10843
  // src/services/warm-hooks-service.ts
10565
10844
  async function readRepoWarmHook(repoPath) {
10566
10845
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
10567
- const configPath = join19(repoPath, filename);
10846
+ const configPath = join20(repoPath, filename);
10568
10847
  if (!existsSync8(configPath)) {
10569
10848
  continue;
10570
10849
  }
10571
10850
  try {
10572
- const raw = await readFile15(configPath, "utf-8");
10851
+ const raw = await readFile16(configPath, "utf-8");
10573
10852
  const config = parseReplicasConfigString(raw, filename);
10574
10853
  if (!config.warmHook) {
10575
10854
  return null;
@@ -10615,7 +10894,7 @@ async function executeHookScriptStreaming(params) {
10615
10894
  let bufferExceeded = false;
10616
10895
  params.onChunk(`$ ${params.label}
10617
10896
  `);
10618
- return new Promise((resolve3) => {
10897
+ return new Promise((resolve4) => {
10619
10898
  const proc = spawn4("bash", ["-lc", params.content], {
10620
10899
  cwd: params.cwd,
10621
10900
  env: process.env,
@@ -10648,7 +10927,7 @@ async function executeHookScriptStreaming(params) {
10648
10927
  proc.on("close", (code) => {
10649
10928
  clearTimeout(timer);
10650
10929
  const output = [`$ ${params.label}`, ...outputParts].join("");
10651
- resolve3({
10930
+ resolve4({
10652
10931
  exitCode: bufferExceeded ? 1 : code ?? 1,
10653
10932
  output,
10654
10933
  timedOut: killed && !bufferExceeded
@@ -10658,7 +10937,7 @@ async function executeHookScriptStreaming(params) {
10658
10937
  clearTimeout(timer);
10659
10938
  const output = [`$ ${params.label}
10660
10939
  `, ...outputParts, err.message].join("");
10661
- resolve3({
10940
+ resolve4({
10662
10941
  exitCode: 1,
10663
10942
  output,
10664
10943
  timedOut: false
@@ -11514,12 +11793,12 @@ function createV1Routes(deps) {
11514
11793
  });
11515
11794
  app2.get("/logs", async (c) => {
11516
11795
  try {
11517
- const files = await readdir6(LOG_DIR).catch(() => []);
11796
+ const files = await readdir7(LOG_DIR).catch(() => []);
11518
11797
  const logFiles = files.filter((f) => f.endsWith(".log"));
11519
11798
  const sessions = await Promise.all(
11520
11799
  logFiles.map(async (filename) => {
11521
- const filePath = join20(LOG_DIR, filename);
11522
- const fileStat = await stat4(filePath);
11800
+ const filePath = join21(LOG_DIR, filename);
11801
+ const fileStat = await stat5(filePath);
11523
11802
  const sessionId = filename.replace(/\.log$/, "");
11524
11803
  return {
11525
11804
  sessionId,
@@ -11547,15 +11826,15 @@ function createV1Routes(deps) {
11547
11826
  if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes("..")) {
11548
11827
  return c.json(jsonError("Invalid session ID"), 400);
11549
11828
  }
11550
- const filePath = resolve2(LOG_DIR, `${sessionId}.log`);
11551
- if (!filePath.startsWith(resolve2(LOG_DIR))) {
11829
+ const filePath = resolve3(LOG_DIR, `${sessionId}.log`);
11830
+ if (!filePath.startsWith(resolve3(LOG_DIR))) {
11552
11831
  return c.json(jsonError("Invalid session ID"), 400);
11553
11832
  }
11554
11833
  const offset = parseInt(c.req.query("offset") || "0", 10);
11555
11834
  const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
11556
11835
  let content;
11557
11836
  try {
11558
- content = await readFile16(filePath, "utf-8");
11837
+ content = await readFile17(filePath, "utf-8");
11559
11838
  } catch {
11560
11839
  return c.json(jsonError("Log session not found"), 404);
11561
11840
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.318",
3
+ "version": "0.1.320",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",