skill-creator 2.1.0 → 2.2.0

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 (62) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +13 -0
  3. package/dist/cli.js +5373 -3653
  4. package/dist/cli.js.map +4 -4
  5. package/dist/daemon.js +2373 -828
  6. package/dist/daemon.js.map +4 -4
  7. package/dist/package.json +1 -1
  8. package/dist/webui/_app/immutable/assets/0.CT0Z92yp.css +2 -0
  9. package/dist/webui/_app/immutable/assets/ProviderView.BKgtsfqs.css +1 -0
  10. package/dist/webui/_app/immutable/chunks/0k4S-8Dm.js +1 -0
  11. package/dist/webui/_app/immutable/chunks/{NO3Cfsmo.js → BD8KMBgQ.js} +1 -1
  12. package/dist/webui/_app/immutable/chunks/{Dbn_qXWm.js → BLKqTz4e.js} +1 -1
  13. package/dist/webui/_app/immutable/chunks/BTdzDCtA.js +1 -0
  14. package/dist/webui/_app/immutable/chunks/{Bs27XW8_.js → BXwUnloU.js} +1 -1
  15. package/dist/webui/_app/immutable/chunks/BZOx0Qre.js +1 -0
  16. package/dist/webui/_app/immutable/chunks/BfKHhAfw.js +1 -0
  17. package/dist/webui/_app/immutable/chunks/Bm81ovyU.js +8 -0
  18. package/dist/webui/_app/immutable/chunks/{BKjuWHgW.js → BpM7GQdD.js} +3 -3
  19. package/dist/webui/_app/immutable/chunks/{CeR4do8T.js → BpPdtmVY.js} +1 -1
  20. package/dist/webui/_app/immutable/chunks/{BEPs9Z9s.js → C4d-joyd.js} +1 -1
  21. package/dist/webui/_app/immutable/chunks/{K56ab8zF.js → CRrxCnau.js} +1 -1
  22. package/dist/webui/_app/immutable/chunks/CUkgSGd5.js +1 -0
  23. package/dist/webui/_app/immutable/chunks/{hrRIAQd7.js → CX1CHr5A.js} +1 -1
  24. package/dist/webui/_app/immutable/chunks/CaRy05kX.js +1 -0
  25. package/dist/webui/_app/immutable/chunks/{UeEccMjN.js → CmWHiR19.js} +1 -1
  26. package/dist/webui/_app/immutable/chunks/CokLx9SK.js +3 -0
  27. package/dist/webui/_app/immutable/chunks/D-GGMKF5.js +1 -0
  28. package/dist/webui/_app/immutable/chunks/D00gXCrF.js +2 -0
  29. package/dist/webui/_app/immutable/chunks/{BIfnT_mK.js → DAvb_BTz.js} +1 -1
  30. package/dist/webui/_app/immutable/chunks/{CY3MARMs.js → DYphEAOV.js} +1 -1
  31. package/dist/webui/_app/immutable/chunks/{0umLG1IN.js → DlRJa6Rp.js} +1 -1
  32. package/dist/webui/_app/immutable/chunks/{D-V0xBeS.js → DsOfMPqr.js} +1 -1
  33. package/dist/webui/_app/immutable/chunks/DxUS3G9A.js +1 -0
  34. package/dist/webui/_app/immutable/chunks/{CQD9uzpA.js → LKyxHDTu.js} +1 -1
  35. package/dist/webui/_app/immutable/chunks/{Dl8eQ129.js → WSgbVOcX.js} +1 -1
  36. package/dist/webui/_app/immutable/chunks/{Bos6D7ir.js → b00Wice3.js} +2 -2
  37. package/dist/webui/_app/immutable/chunks/{Ckw1quWt.js → n3HFLfXO.js} +1 -1
  38. package/dist/webui/_app/immutable/chunks/{B0yogbIO.js → xHmEZGe9.js} +1 -1
  39. package/dist/webui/_app/immutable/chunks/{D8Der6oM.js → y5ROg0gY.js} +1 -1
  40. package/dist/webui/_app/immutable/entry/{app.CkIQoKd-.js → app.wmUVPoAL.js} +2 -2
  41. package/dist/webui/_app/immutable/entry/start.DeqHj237.js +1 -0
  42. package/dist/webui/_app/immutable/nodes/0.B1MRgs82.js +234 -0
  43. package/dist/webui/_app/immutable/nodes/{1.DxOotYUD.js → 1.DmFrzXB8.js} +1 -1
  44. package/dist/webui/_app/immutable/nodes/{2.BhwY9m4H.js → 2.C5_mWJeB.js} +1 -1
  45. package/dist/webui/_app/version.json +1 -1
  46. package/dist/webui/index.html +27 -27
  47. package/package.json +7 -4
  48. package/dist/webui/_app/immutable/assets/0.BfQ9GZ7H.css +0 -2
  49. package/dist/webui/_app/immutable/assets/ProviderView.DEH95hqa.css +0 -1
  50. package/dist/webui/_app/immutable/chunks/9b6xLvUw.js +0 -1
  51. package/dist/webui/_app/immutable/chunks/BDe0K-12.js +0 -1
  52. package/dist/webui/_app/immutable/chunks/BkEnd9_Z.js +0 -1
  53. package/dist/webui/_app/immutable/chunks/CIFG41pU.js +0 -1
  54. package/dist/webui/_app/immutable/chunks/CNPsST_i.js +0 -1
  55. package/dist/webui/_app/immutable/chunks/D-ihBChi.js +0 -1
  56. package/dist/webui/_app/immutable/chunks/D7i1h2eW.js +0 -2
  57. package/dist/webui/_app/immutable/chunks/DFDHkP9N.js +0 -1
  58. package/dist/webui/_app/immutable/chunks/DFdxGYRu.js +0 -8
  59. package/dist/webui/_app/immutable/chunks/QU8DIUJz.js +0 -3
  60. package/dist/webui/_app/immutable/chunks/UsOmL8ol.js +0 -1
  61. package/dist/webui/_app/immutable/entry/start.DG-jtr9O.js +0 -1
  62. package/dist/webui/_app/immutable/nodes/0.DY8kli0K.js +0 -234
package/dist/daemon.js CHANGED
@@ -1783,28 +1783,28 @@ var init_create_prompt_CIJV_96B = __esm({
1783
1783
  b = b > 0.04045 ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
1784
1784
  const x = r * 0.4124 + g * 0.3576 + b * 0.1805;
1785
1785
  const y = r * 0.2126 + g * 0.7152 + b * 0.0722;
1786
- const z27 = r * 0.0193 + g * 0.1192 + b * 0.9505;
1786
+ const z31 = r * 0.0193 + g * 0.1192 + b * 0.9505;
1787
1787
  return [
1788
1788
  x * 100,
1789
1789
  y * 100,
1790
- z27 * 100
1790
+ z31 * 100
1791
1791
  ];
1792
1792
  };
1793
1793
  convert$1.rgb.lab = function(rgb) {
1794
1794
  const xyz = convert$1.rgb.xyz(rgb);
1795
1795
  let x = xyz[0];
1796
1796
  let y = xyz[1];
1797
- let z27 = xyz[2];
1797
+ let z31 = xyz[2];
1798
1798
  x /= 95.047;
1799
1799
  y /= 100;
1800
- z27 /= 108.883;
1800
+ z31 /= 108.883;
1801
1801
  x = x > 8856e-6 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
1802
1802
  y = y > 8856e-6 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
1803
- z27 = z27 > 8856e-6 ? z27 ** (1 / 3) : 7.787 * z27 + 16 / 116;
1803
+ z31 = z31 > 8856e-6 ? z31 ** (1 / 3) : 7.787 * z31 + 16 / 116;
1804
1804
  return [
1805
1805
  116 * y - 16,
1806
1806
  500 * (x - y),
1807
- 200 * (y - z27)
1807
+ 200 * (y - z31)
1808
1808
  ];
1809
1809
  };
1810
1810
  convert$1.hsl.rgb = function(hsl) {
@@ -2001,13 +2001,13 @@ var init_create_prompt_CIJV_96B = __esm({
2001
2001
  convert$1.xyz.rgb = function(xyz) {
2002
2002
  const x = xyz[0] / 100;
2003
2003
  const y = xyz[1] / 100;
2004
- const z27 = xyz[2] / 100;
2004
+ const z31 = xyz[2] / 100;
2005
2005
  let r;
2006
2006
  let g;
2007
2007
  let b;
2008
- r = x * 3.2406 + y * -1.5372 + z27 * -0.4986;
2009
- g = x * -0.9689 + y * 1.8758 + z27 * 0.0415;
2010
- b = x * 0.0557 + y * -0.204 + z27 * 1.057;
2008
+ r = x * 3.2406 + y * -1.5372 + z31 * -0.4986;
2009
+ g = x * -0.9689 + y * 1.8758 + z31 * 0.0415;
2010
+ b = x * 0.0557 + y * -0.204 + z31 * 1.057;
2011
2011
  r = r > 31308e-7 ? 1.055 * r ** (1 / 2.4) - 0.055 : r * 12.92;
2012
2012
  g = g > 31308e-7 ? 1.055 * g ** (1 / 2.4) - 0.055 : g * 12.92;
2013
2013
  b = b > 31308e-7 ? 1.055 * b ** (1 / 2.4) - 0.055 : b * 12.92;
@@ -2023,17 +2023,17 @@ var init_create_prompt_CIJV_96B = __esm({
2023
2023
  convert$1.xyz.lab = function(xyz) {
2024
2024
  let x = xyz[0];
2025
2025
  let y = xyz[1];
2026
- let z27 = xyz[2];
2026
+ let z31 = xyz[2];
2027
2027
  x /= 95.047;
2028
2028
  y /= 100;
2029
- z27 /= 108.883;
2029
+ z31 /= 108.883;
2030
2030
  x = x > 8856e-6 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
2031
2031
  y = y > 8856e-6 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
2032
- z27 = z27 > 8856e-6 ? z27 ** (1 / 3) : 7.787 * z27 + 16 / 116;
2032
+ z31 = z31 > 8856e-6 ? z31 ** (1 / 3) : 7.787 * z31 + 16 / 116;
2033
2033
  return [
2034
2034
  116 * y - 16,
2035
2035
  500 * (x - y),
2036
- 200 * (y - z27)
2036
+ 200 * (y - z31)
2037
2037
  ];
2038
2038
  };
2039
2039
  convert$1.lab.xyz = function(lab) {
@@ -2042,23 +2042,23 @@ var init_create_prompt_CIJV_96B = __esm({
2042
2042
  const b = lab[2];
2043
2043
  let x;
2044
2044
  let y;
2045
- let z27;
2045
+ let z31;
2046
2046
  y = (l + 16) / 116;
2047
2047
  x = a / 500 + y;
2048
- z27 = y - b / 200;
2048
+ z31 = y - b / 200;
2049
2049
  const y2 = y ** 3;
2050
2050
  const x2 = x ** 3;
2051
- const z28 = z27 ** 3;
2051
+ const z210 = z31 ** 3;
2052
2052
  y = y2 > 8856e-6 ? y2 : (y - 16 / 116) / 7.787;
2053
2053
  x = x2 > 8856e-6 ? x2 : (x - 16 / 116) / 7.787;
2054
- z27 = z28 > 8856e-6 ? z28 : (z27 - 16 / 116) / 7.787;
2054
+ z31 = z210 > 8856e-6 ? z210 : (z31 - 16 / 116) / 7.787;
2055
2055
  x *= 95.047;
2056
2056
  y *= 100;
2057
- z27 *= 108.883;
2057
+ z31 *= 108.883;
2058
2058
  return [
2059
2059
  x,
2060
2060
  y,
2061
- z27
2061
+ z31
2062
2062
  ];
2063
2063
  };
2064
2064
  convert$1.lab.lch = function(lab) {
@@ -2407,15 +2407,15 @@ var init_create_prompt_CIJV_96B = __esm({
2407
2407
  };
2408
2408
  }
2409
2409
  function wrapConversion(toModel, graph) {
2410
- const path33 = [graph[toModel].parent, toModel];
2410
+ const path39 = [graph[toModel].parent, toModel];
2411
2411
  let fn = conversions$1[graph[toModel].parent][toModel];
2412
2412
  let cur = graph[toModel].parent;
2413
2413
  while (graph[cur].parent) {
2414
- path33.unshift(graph[cur].parent);
2414
+ path39.unshift(graph[cur].parent);
2415
2415
  fn = link(conversions$1[graph[cur].parent][cur], fn);
2416
2416
  cur = graph[cur].parent;
2417
2417
  }
2418
- fn.conversion = path33;
2418
+ fn.conversion = path39;
2419
2419
  return fn;
2420
2420
  }
2421
2421
  module2.exports = function(fromModel) {
@@ -3978,18 +3978,18 @@ function parseSkillFile(filePath) {
3978
3978
  const issues = [];
3979
3979
  const suggestions$1 = [];
3980
3980
  for (const issue of validation$1.error.errors) {
3981
- const path33 = issue.path.join(".") || "frontmatter";
3981
+ const path39 = issue.path.join(".") || "frontmatter";
3982
3982
  if (issue.code === "invalid_type" && issue.message === "Required") {
3983
- issues.push(`Missing required field '${path33}' in ${filePath}`);
3984
- suggestions$1.push(`Add '${path33}: <value>' to the YAML frontmatter.`);
3983
+ issues.push(`Missing required field '${path39}' in ${filePath}`);
3984
+ suggestions$1.push(`Add '${path39}: <value>' to the YAML frontmatter.`);
3985
3985
  continue;
3986
3986
  }
3987
3987
  if (issue.code === "too_small") {
3988
- issues.push(`${path33}: ${issue.message}`);
3989
- suggestions$1.push(`Provide a non-empty value for '${path33}'.`);
3988
+ issues.push(`${path39}: ${issue.message}`);
3989
+ suggestions$1.push(`Provide a non-empty value for '${path39}'.`);
3990
3990
  continue;
3991
3991
  }
3992
- issues.push(`${path33}: ${issue.message}`);
3992
+ issues.push(`${path39}: ${issue.message}`);
3993
3993
  }
3994
3994
  if (suggestions$1.length === 0) suggestions$1.push("Ensure SKILL.md frontmatter includes non-empty 'name' and 'description' fields.");
3995
3995
  throw new ValidationError$2(filePath, issues, suggestions$1);
@@ -4103,9 +4103,9 @@ function builtInAgentRoots(baseDir, location, sourceKind) {
4103
4103
  sourcePriority: sourcePriority$1(sourceKind)
4104
4104
  }));
4105
4105
  }
4106
- function sharedRoot(path33, location, sourceKind) {
4106
+ function sharedRoot(path39, location, sourceKind) {
4107
4107
  return {
4108
- path: path33,
4108
+ path: path39,
4109
4109
  provider: "agents",
4110
4110
  location,
4111
4111
  sourceKind,
@@ -4113,9 +4113,9 @@ function sharedRoot(path33, location, sourceKind) {
4113
4113
  };
4114
4114
  }
4115
4115
  function legacySharedRoot(baseDir, location, sourceKind) {
4116
- const path33 = join(baseDir, ".agent", "skills");
4117
- if (!existsSync(path33)) return [];
4118
- return [sharedRoot(path33, location, sourceKind)];
4116
+ const path39 = join(baseDir, ".agent", "skills");
4117
+ if (!existsSync(path39)) return [];
4118
+ return [sharedRoot(path39, location, sourceKind)];
4119
4119
  }
4120
4120
  function uniqueByPath(entries) {
4121
4121
  const seen = /* @__PURE__ */ new Set();
@@ -5360,8 +5360,8 @@ function buildPreview$1(options2, selected) {
5360
5360
  function promptMultiSelect(options2) {
5361
5361
  return promptImpl(options2);
5362
5362
  }
5363
- function normalizeRepoUrl(origin, path33) {
5364
- let cleanPath = path33.replace(/\/+$/, "");
5363
+ function normalizeRepoUrl(origin, path39) {
5364
+ let cleanPath = path39.replace(/\/+$/, "");
5365
5365
  if (cleanPath.endsWith(".git")) cleanPath = cleanPath.slice(0, -4);
5366
5366
  return `${origin}${cleanPath}.git`;
5367
5367
  }
@@ -5635,17 +5635,17 @@ function installSkillDir(skillDir, targetRoot, force = false) {
5635
5635
  status: alreadyExists ? "overwritten" : "installed"
5636
5636
  };
5637
5637
  }
5638
- function buildSourceArgs(input, branch, path33, repo) {
5638
+ function buildSourceArgs(input, branch, path39, repo) {
5639
5639
  return {
5640
5640
  source: repo ?? input,
5641
5641
  ...branch ? { branch } : {},
5642
- ...path33 ? { path: path33 } : {}
5642
+ ...path39 ? { path: path39 } : {}
5643
5643
  };
5644
5644
  }
5645
- function formatSourceLabel(input, branch, path33, repo) {
5645
+ function formatSourceLabel(input, branch, path39, repo) {
5646
5646
  const pieces = [repo ?? input];
5647
5647
  if (branch) pieces.push(`--branch=${branch}`);
5648
- if (path33) pieces.push(`--path=${path33}`);
5648
+ if (path39) pieces.push(`--path=${path39}`);
5649
5649
  return pieces.join(" ");
5650
5650
  }
5651
5651
  function defaultMode(source, explicit) {
@@ -5805,8 +5805,8 @@ async function selectSkills(targetDirs, argv$1, cmdBuilder) {
5805
5805
  };
5806
5806
  const source = cmdBuilder.getArg("source")[0] ?? argv$1.source;
5807
5807
  const branch = cmdBuilder.getArg("branch")[0];
5808
- const path33 = cmdBuilder.getArg("path")[0];
5809
- const sourceLabel = formatSourceLabel(source, branch, path33);
5808
+ const path39 = cmdBuilder.getArg("path")[0];
5809
+ const sourceLabel = formatSourceLabel(source, branch, path39);
5810
5810
  const listing = formatSkillListing(entries, sourceLabel);
5811
5811
  const selectors = parseSelectors(argv$1);
5812
5812
  if (argv$1.all) return {
@@ -6979,8 +6979,8 @@ var init_workflow_install_KvZzb83I = __esm({
6979
6979
  en_default = errorMap;
6980
6980
  overrideErrorMap = en_default;
6981
6981
  makeIssue = (params) => {
6982
- const { data, path: path33, errorMaps, issueData } = params;
6983
- const fullPath = [...path33, ...issueData.path || []];
6982
+ const { data, path: path39, errorMaps, issueData } = params;
6983
+ const fullPath = [...path39, ...issueData.path || []];
6984
6984
  const fullIssue = {
6985
6985
  ...issueData,
6986
6986
  path: fullPath
@@ -7070,11 +7070,11 @@ var init_workflow_install_KvZzb83I = __esm({
7070
7070
  errorUtil$1.toString = (message) => typeof message === "string" ? message : message?.message;
7071
7071
  })(errorUtil || (errorUtil = {}));
7072
7072
  ParseInputLazyPath = class {
7073
- constructor(parent, value, path33, key$1) {
7073
+ constructor(parent, value, path39, key$1) {
7074
7074
  this._cachedPath = [];
7075
7075
  this.parent = parent;
7076
7076
  this.data = value;
7077
- this._path = path33;
7077
+ this._path = path39;
7078
7078
  this._key = key$1;
7079
7079
  }
7080
7080
  get path() {
@@ -18022,7 +18022,7 @@ var init_workflow_install_KvZzb83I = __esm({
18022
18022
  };
18023
18023
  }) });
18024
18024
  require_gray_matter = /* @__PURE__ */ __commonJS2({ "node_modules/.pnpm/gray-matter@4.0.3/node_modules/gray-matter/index.js": ((exports2, module2) => {
18025
- const fs31 = __require2("fs");
18025
+ const fs38 = __require2("fs");
18026
18026
  const sections = require_section_matter();
18027
18027
  const defaults = require_defaults$1();
18028
18028
  const stringify$1 = require_stringify();
@@ -18093,7 +18093,7 @@ var init_workflow_install_KvZzb83I = __esm({
18093
18093
  return stringify$1(file, data, options2);
18094
18094
  };
18095
18095
  matter$1.read = function(filepath, options2) {
18096
- const file = matter$1(fs31.readFileSync(filepath, "utf8"), options2);
18096
+ const file = matter$1(fs38.readFileSync(filepath, "utf8"), options2);
18097
18097
  file.path = filepath;
18098
18098
  return file;
18099
18099
  };
@@ -23320,8 +23320,8 @@ var init_workflow_install_KvZzb83I = __esm({
23320
23320
  for (let i$4 = 0; i$4 < str$12.length; i$4++) if (str$12[i$4] === token) ind++;
23321
23321
  return ind;
23322
23322
  }
23323
- function removeDotSegments$1(path33) {
23324
- let input = path33;
23323
+ function removeDotSegments$1(path39) {
23324
+ let input = path39;
23325
23325
  const output = [];
23326
23326
  let nextSlash = -1;
23327
23327
  let len = 0;
@@ -23474,8 +23474,8 @@ var init_workflow_install_KvZzb83I = __esm({
23474
23474
  wsComponent.secure = void 0;
23475
23475
  }
23476
23476
  if (wsComponent.resourceName) {
23477
- const [path33, query] = wsComponent.resourceName.split("?");
23478
- wsComponent.path = path33 && path33 !== "/" ? path33 : void 0;
23477
+ const [path39, query] = wsComponent.resourceName.split("?");
23478
+ wsComponent.path = path39 && path39 !== "/" ? path39 : void 0;
23479
23479
  wsComponent.query = query;
23480
23480
  wsComponent.resourceName = void 0;
23481
23481
  }
@@ -36772,32 +36772,32 @@ If the user names a skill, or a task clearly matches a skill that is not already
36772
36772
  });
36773
36773
 
36774
36774
  // src/daemon/dsh-profile-support.ts
36775
- import { createRequire as createRequire4 } from "node:module";
36776
- import fs16 from "node:fs";
36777
- import path18 from "node:path";
36775
+ import { createRequire as createRequire5 } from "node:module";
36776
+ import fs25 from "node:fs";
36777
+ import path27 from "node:path";
36778
36778
  function ensureDirLink(link, target) {
36779
- fs16.mkdirSync(path18.dirname(link), { recursive: true });
36780
- if (fs16.existsSync(link) && fs16.realpathSync(link) === target) return;
36781
- fs16.rmSync(link, { recursive: true, force: true });
36782
- fs16.symlinkSync(target, link, "dir");
36779
+ fs25.mkdirSync(path27.dirname(link), { recursive: true });
36780
+ if (fs25.existsSync(link) && fs25.realpathSync(link) === target) return;
36781
+ fs25.rmSync(link, { recursive: true, force: true });
36782
+ fs25.symlinkSync(target, link, "dir");
36783
36783
  }
36784
36784
  function completeTransitiveMirror(home) {
36785
- const mirrorRoot = path18.join(home, "profiles", "node_modules");
36786
- if (!fs16.existsSync(mirrorRoot)) return;
36785
+ const mirrorRoot = path27.join(home, "profiles", "node_modules");
36786
+ if (!fs25.existsSync(mirrorRoot)) return;
36787
36787
  const manifestOf = (dir) => {
36788
36788
  try {
36789
- return JSON.parse(fs16.readFileSync(path18.join(dir, "package.json"), "utf8"));
36789
+ return JSON.parse(fs25.readFileSync(path27.join(dir, "package.json"), "utf8"));
36790
36790
  } catch {
36791
36791
  return null;
36792
36792
  }
36793
36793
  };
36794
36794
  const listPackages = () => {
36795
36795
  const names = [];
36796
- for (const scope of fs16.readdirSync(mirrorRoot)) {
36796
+ for (const scope of fs25.readdirSync(mirrorRoot)) {
36797
36797
  if (scope.startsWith(".") || scope === ".bin") continue;
36798
- const scopeDir = path18.join(mirrorRoot, scope);
36798
+ const scopeDir = path27.join(mirrorRoot, scope);
36799
36799
  if (scope.startsWith("@")) {
36800
- for (const name of fs16.readdirSync(scopeDir)) names.push(`${scope}/${name}`);
36800
+ for (const name of fs25.readdirSync(scopeDir)) names.push(`${scope}/${name}`);
36801
36801
  } else {
36802
36802
  names.push(scope);
36803
36803
  }
@@ -36807,18 +36807,18 @@ function completeTransitiveMirror(home) {
36807
36807
  for (let round = 0; round < 8; round += 1) {
36808
36808
  let linked = 0;
36809
36809
  for (const name of listPackages()) {
36810
- const pkgDir = path18.join(mirrorRoot, name);
36810
+ const pkgDir = path27.join(mirrorRoot, name);
36811
36811
  const manifest = manifestOf(pkgDir);
36812
36812
  const deps = { ...manifest?.dependencies };
36813
36813
  if (!deps) continue;
36814
36814
  for (const dep of Object.keys(deps)) {
36815
- const depLink = path18.join(mirrorRoot, dep);
36816
- if (fs16.existsSync(depLink)) continue;
36815
+ const depLink = path27.join(mirrorRoot, dep);
36816
+ if (fs25.existsSync(depLink)) continue;
36817
36817
  try {
36818
- const resolved = createRequire4(path18.join(realPkgDir(pkgDir), "package.json")).resolve(
36818
+ const resolved = createRequire5(path27.join(realPkgDir(pkgDir), "package.json")).resolve(
36819
36819
  `${dep}/package.json`
36820
36820
  );
36821
- ensureDirLink(depLink, path18.dirname(resolved));
36821
+ ensureDirLink(depLink, path27.dirname(resolved));
36822
36822
  linked += 1;
36823
36823
  } catch {
36824
36824
  }
@@ -36829,7 +36829,7 @@ function completeTransitiveMirror(home) {
36829
36829
  }
36830
36830
  function realPkgDir(pkgDir) {
36831
36831
  try {
36832
- return fs16.realpathSync(pkgDir);
36832
+ return fs25.realpathSync(pkgDir);
36833
36833
  } catch {
36834
36834
  return pkgDir;
36835
36835
  }
@@ -36994,8 +36994,8 @@ __export(dsh_kernel_exports, {
36994
36994
  KERNEL_AGENT_TOOL_ALLOWLIST: () => KERNEL_AGENT_TOOL_ALLOWLIST,
36995
36995
  bootDshKernel: () => bootDshKernel
36996
36996
  });
36997
- import fs17 from "node:fs";
36998
- import path19 from "node:path";
36997
+ import fs26 from "node:fs";
36998
+ import path28 from "node:path";
36999
36999
  import { fileURLToPath as fileURLToPath2 } from "node:url";
37000
37000
  import {
37001
37001
  boot,
@@ -37021,15 +37021,15 @@ async function bootDshKernel(options2) {
37021
37021
  ""
37022
37022
  );
37023
37023
  const llmYaml = localLlmRouteYaml();
37024
- fs17.writeFileSync(path19.join(profileDir, "cordis.patch.yml"), disableYaml + llmYaml, "utf8");
37025
- const installAnchor = path19.join(repoRoot, "package.json");
37024
+ fs26.writeFileSync(path28.join(profileDir, "cordis.patch.yml"), disableYaml + llmYaml, "utf8");
37025
+ const installAnchor = path28.join(repoRoot, "package.json");
37026
37026
  const profile = loadProfile("skill-creator", "kernel", installAnchor, options2.home);
37027
37027
  await healProfilesModuleFallback({ installAnchor, profile, home: options2.home });
37028
37028
  completeTransitiveMirror(options2.home);
37029
- const presetDir = path19.join(options2.home, ".agent-presets", "skill-creator");
37030
- fs17.mkdirSync(presetDir, { recursive: true });
37031
- fs17.writeFileSync(
37032
- path19.join(presetDir, "preset.yml"),
37029
+ const presetDir = path28.join(options2.home, ".agent-presets", "skill-creator");
37030
+ fs26.mkdirSync(presetDir, { recursive: true });
37031
+ fs26.writeFileSync(
37032
+ path28.join(presetDir, "preset.yml"),
37033
37033
  [
37034
37034
  "name: Skill Creator Agent",
37035
37035
  "description: \u4EA7\u54C1\u4F1A\u8BDD\u9884\u8BBE\u2014\u2014Manager \u80FD\u529B\u9762 + ask-user\uFF0C\u65E0\u901A\u7528 fs/shell \u5DE5\u5177\u3002",
@@ -37038,8 +37038,8 @@ async function bootDshKernel(options2) {
37038
37038
  ].join("\n"),
37039
37039
  "utf8"
37040
37040
  );
37041
- fs17.writeFileSync(
37042
- path19.join(presetDir, "agent.cordis.yml"),
37041
+ fs26.writeFileSync(
37042
+ path28.join(presetDir, "agent.cordis.yml"),
37043
37043
  [
37044
37044
  "# skill-creator \u4EA7\u54C1 preset\uFF1A\u7531 skill-creator kernel boot \u5199\u5165\uFF08user root\uFF09\u3002",
37045
37045
  "- id: persona",
@@ -37056,7 +37056,7 @@ async function bootDshKernel(options2) {
37056
37056
  ].join("\n"),
37057
37057
  "utf8"
37058
37058
  );
37059
- const configPath = path19.join(profileDir, "cordis.yml");
37059
+ const configPath = path28.join(profileDir, "cordis.yml");
37060
37060
  const mcpRow = options2.mcp ? [
37061
37061
  "- id: mcp-skill-creator\n",
37062
37062
  " name: '@deepseek-ai/dsh-mcp-client'\n",
@@ -37067,7 +37067,7 @@ async function bootDshKernel(options2) {
37067
37067
  " headers:\n",
37068
37068
  " Authorization: !!js '`Bearer ${process.env.SKILL_CREATOR_MCP_TOKEN}`'\n"
37069
37069
  ].join("") : "";
37070
- fs17.writeFileSync(
37070
+ fs26.writeFileSync(
37071
37071
  configPath,
37072
37072
  [
37073
37073
  "- id: agent-presets\n",
@@ -37164,8 +37164,8 @@ var init_dsh_kernel = __esm({
37164
37164
  init_dsh_profile_support();
37165
37165
  init_agent_roles2();
37166
37166
  modulePath = fileURLToPath2(import.meta.url);
37167
- sourceMode = path19.basename(modulePath) === "dsh-kernel.ts";
37168
- repoRoot = sourceMode ? path19.resolve(path19.dirname(modulePath), "../../..") : path19.resolve(path19.dirname(modulePath), "..");
37167
+ sourceMode = path28.basename(modulePath) === "dsh-kernel.ts";
37168
+ repoRoot = sourceMode ? path28.resolve(path28.dirname(modulePath), "../../..") : path28.resolve(path28.dirname(modulePath), "..");
37169
37169
  KERNEL_DISABLED_TOOL_ROWS = [
37170
37170
  "tool-pwsh",
37171
37171
  "tool-fs",
@@ -37196,8 +37196,8 @@ function resolveDaemonAppLaunch(daemonModuleUrl, runtimeExecutable = process.exe
37196
37196
  }
37197
37197
 
37198
37198
  // src/daemon/index.ts
37199
- import fs29 from "node:fs";
37200
- import path31 from "node:path";
37199
+ import fs36 from "node:fs";
37200
+ import path37 from "node:path";
37201
37201
  import { fileURLToPath as fileURLToPath5 } from "node:url";
37202
37202
  import { randomBytes as randomBytes15 } from "node:crypto";
37203
37203
 
@@ -38199,7 +38199,7 @@ var DshSessionStreamsInputSchema = z7.object({
38199
38199
 
38200
38200
  // src/shared/rpc-contract.ts
38201
38201
  import { oc } from "@orpc/contract";
38202
- import { z as z14 } from "zod";
38202
+ import { z as z15 } from "zod";
38203
38203
 
38204
38204
  // src/shared/contracts/acp.ts
38205
38205
  import { z as z8 } from "zod";
@@ -39684,49 +39684,142 @@ var ApplyUpdateResultSchema = z13.object({
39684
39684
  results: z13.array(ApplyUpdateResultEntrySchema)
39685
39685
  });
39686
39686
 
39687
+ // src/shared/contracts/search.ts
39688
+ import { z as z14 } from "zod";
39689
+ var SkillInstallationSchema = z14.object({
39690
+ /** 入口原始绝对路径(Agent 环境视角,可能是指向 canonical 目录的 symlink)。 */
39691
+ path: z14.string().min(1),
39692
+ /** 该 root 所属 Workspace(Global 为 `~`,Imported 为 ws_*)。 */
39693
+ workspaceId: WorkspaceIdSchema,
39694
+ /** 该 root 的 Provider。 */
39695
+ providerId: ProviderIdSchema
39696
+ }).strict();
39697
+ var SkillSearchDocumentSchema = z14.object({
39698
+ /** 与 skills.list 一致的稳定 sk_ id(canonical path digest)。 */
39699
+ id: SkillIdSchema,
39700
+ /** frontmatter name;无效时回退目录名(见 invalidFrontmatter)。 */
39701
+ name: z14.string(),
39702
+ /** frontmatter description;无效时置空。 */
39703
+ description: z14.string(),
39704
+ /** frontmatter keywords(string|string[] 收窄后的字符串数组)。 */
39705
+ keywords: z14.array(z14.string()),
39706
+ /** frontmatter triggers(同上收窄)。 */
39707
+ triggers: z14.array(z14.string()),
39708
+ /** body 的 #/##/###/#### 标题拼接(截 30 条)。 */
39709
+ headings: z14.string(),
39710
+ /** markdown-to-search-text 正文(剥 fence 围栏行,截 12k chars)。 */
39711
+ body: z14.string(),
39712
+ /** realpath 真相。 */
39713
+ canonicalPath: z14.string().min(1),
39714
+ /** 同一 canonical 目录的全部入口(未来 RPC/GUI 可直接组装 WorkspaceProviderTarget)。 */
39715
+ installations: z14.array(SkillInstallationSchema).min(1),
39716
+ /** 实际被索引 SKILL.md 原始字节的 SHA-256 hex(64 字符,无前缀)。 */
39717
+ contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
39718
+ /** 仅 .SKILL.md 在场(技能被禁用)。 */
39719
+ disabled: z14.boolean(),
39720
+ /** SKILL.md 与 .SKILL.md 并存(内容源取 SKILL.md)。 */
39721
+ conflict: z14.boolean(),
39722
+ /** frontmatter 未过 name/description schema(文档仍入索引)。 */
39723
+ invalidFrontmatter: z14.boolean()
39724
+ }).strict();
39725
+ var SkillSearchDuplicateSchema = z14.object({
39726
+ id: SkillIdSchema,
39727
+ canonicalPath: z14.string().min(1)
39728
+ }).strict();
39729
+ var SkillDuplicateMemberSchema = z14.object({
39730
+ id: SkillIdSchema,
39731
+ name: z14.string(),
39732
+ canonicalPath: z14.string().min(1),
39733
+ installations: z14.array(SkillInstallationSchema).min(1),
39734
+ disabled: z14.boolean(),
39735
+ conflict: z14.boolean()
39736
+ }).strict();
39737
+ var SkillDuplicateGroupSchema = z14.object({
39738
+ contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
39739
+ members: z14.array(SkillDuplicateMemberSchema).min(2)
39740
+ }).strict();
39741
+ var SkillSearchResultSchema = z14.object({
39742
+ id: SkillIdSchema,
39743
+ name: z14.string(),
39744
+ description: z14.string(),
39745
+ canonicalPath: z14.string().min(1),
39746
+ installations: z14.array(SkillInstallationSchema).min(1),
39747
+ contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
39748
+ disabled: z14.boolean(),
39749
+ conflict: z14.boolean(),
39750
+ /** final = 0.7×(bm25/(bm25+8)) + 0.3×rerank。 */
39751
+ score: z14.number(),
39752
+ /** 同 contentHash 组内被折叠的其余成员(同序)。 */
39753
+ duplicates: z14.array(SkillSearchDuplicateSchema)
39754
+ }).strict();
39755
+ var SkillSearchOptionsSchema = z14.object({
39756
+ limit: z14.number().int().min(1).max(50).default(10)
39757
+ }).strict();
39758
+
39687
39759
  // src/shared/rpc-contract.ts
39688
- var WorkspaceProviderReadInputSchema = z14.object({
39760
+ var WorkspaceProviderReadInputSchema = z15.object({
39689
39761
  ...WorkspaceProviderTargetSchema.shape,
39690
- includeDisabled: z14.boolean().optional()
39762
+ includeDisabled: z15.boolean().optional()
39691
39763
  });
39692
39764
  var SkillsListInputSchema = WorkspaceProviderReadInputSchema;
39693
39765
  var SkillsInfoInputSchema = WorkspaceProviderReadInputSchema.extend({
39694
39766
  skillId: SkillIdSchema
39695
39767
  });
39696
- var SkillsToggleInputSchema = z14.object({
39768
+ var SkillsToggleInputSchema = z15.object({
39697
39769
  ...WorkspaceProviderTargetSchema.shape,
39698
- skillIds: z14.array(SkillIdSchema).min(1),
39699
- mode: z14.enum(["enable", "disable"])
39770
+ skillIds: z15.array(SkillIdSchema).min(1),
39771
+ mode: z15.enum(["enable", "disable"])
39700
39772
  });
39701
- var WorkspaceAddInputSchema = z14.object({
39702
- path: z14.string().trim().min(1),
39703
- label: z14.string().trim().min(1).optional()
39773
+ var WorkspaceAddInputSchema = z15.object({
39774
+ path: z15.string().trim().min(1),
39775
+ label: z15.string().trim().min(1).optional()
39704
39776
  });
39705
- var WorkspaceRemoveInputSchema = z14.object({ id: ImportedWorkspaceIdSchema });
39706
- var WorkspaceSetActiveInputSchema = z14.object({ id: WorkspaceIdSchema });
39707
- var CreatorRemoveInputSchema = z14.object({
39777
+ var WorkspaceRemoveInputSchema = z15.object({ id: ImportedWorkspaceIdSchema });
39778
+ var WorkspaceSetActiveInputSchema = z15.object({ id: WorkspaceIdSchema });
39779
+ var CreatorRemoveInputSchema = z15.object({
39708
39780
  ...WorkspaceProviderTargetSchema.shape,
39709
39781
  skillId: SkillIdSchema,
39710
- expectedRevision: z14.string().regex(/^sha256:[a-f0-9]{64}$/)
39782
+ expectedRevision: z15.string().regex(/^sha256:[a-f0-9]{64}$/)
39711
39783
  });
39712
- var RepositoryScanInputSchema = z14.object({
39713
- source: z14.string().trim().min(1),
39714
- ref: z14.string().trim().min(1).optional()
39784
+ var SkillsSearchInputSchema = z15.object({
39785
+ query: z15.string().min(1),
39786
+ limit: SkillSearchOptionsSchema.shape.limit
39787
+ }).strict();
39788
+ var RepositoryScanInputSchema = z15.object({
39789
+ source: z15.string().trim().min(1),
39790
+ ref: z15.string().trim().min(1).optional()
39715
39791
  });
39716
- var RepositoryPreviewInputSchema = z14.object({
39792
+ var RepositoryPreviewInputSchema = z15.object({
39717
39793
  sessionId: RepositorySessionIdSchema,
39718
39794
  skillId: RemoteSkillIdSchema
39719
39795
  });
39720
39796
  var rpcContract = oc.errors(RpcErrorDefinitions).router({
39721
39797
  skills: {
39722
39798
  /** Discover skills within one explicit Workspace Provider. */
39723
- list: oc.input(SkillsListInputSchema).output(z14.object({ skills: z14.array(SkillMetadataSchema) })),
39799
+ list: oc.input(SkillsListInputSchema).output(z15.object({ skills: z15.array(SkillMetadataSchema) })),
39724
39800
  /** Read one Workspace Provider-scoped skill document. */
39725
39801
  info: oc.input(SkillsInfoInputSchema).output(SkillInfoSchema),
39726
39802
  /** Enable or disable selected opaque skill IDs. */
39727
39803
  toggle: oc.input(SkillsToggleInputSchema).output(ToggleSummarySchema),
39728
39804
  /** Validate one Workspace Provider-scoped skill. */
39729
39805
  validate: oc.input(SkillsInfoInputSchema).output(ValidateResultSchema),
39806
+ /**
39807
+ * Search local skills across all workspaces (daemon 长驻检索:canonical 去重 +
39808
+ * BM25 + 冻结 rerank;返回稳定 sk_ id 与 installations 作用域三元组)。
39809
+ */
39810
+ search: oc.input(SkillsSearchInputSchema).output(z15.object({ results: z15.array(SkillSearchResultSchema) })),
39811
+ /**
39812
+ * 检索内容配置面:以系统默认编辑器打开 server-owned 的 search-config.toml
39813
+ * (排除目录清单;路径由 daemon 派生,不接受调用方输入)。
39814
+ */
39815
+ searchConfig: {
39816
+ open: oc.input(z15.object({})).output(z15.object({ opened: z15.boolean() }))
39817
+ },
39818
+ /**
39819
+ * 内容重复组(索引 contentHash 分组 >1;无查询维度的全量重复清单,
39820
+ * 排序冻结可重放)。
39821
+ */
39822
+ duplicates: oc.input(z15.object({})).output(z15.object({ groups: z15.array(SkillDuplicateGroupSchema) })),
39730
39823
  update: {
39731
39824
  /** Compare skills-CLI lock hashes against upstream and report outdated skills. */
39732
39825
  check: oc.input(UpdateCheckInputSchema).output(UpdateCheckResultSchema),
@@ -39736,13 +39829,18 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39736
39829
  },
39737
39830
  workspace: {
39738
39831
  /** List Global and imported Workspaces with fresh Provider counts. */
39739
- list: oc.input(z14.object({})).output(z14.object({ workspaces: z14.array(WorkspaceSchema) })),
39832
+ list: oc.input(z15.object({})).output(z15.object({ workspaces: z15.array(WorkspaceSchema) })),
39740
39833
  /** Import a canonical directory workspace. */
39741
- add: oc.input(WorkspaceAddInputSchema).output(z14.object({ workspace: WorkspaceSchema })),
39834
+ add: oc.input(WorkspaceAddInputSchema).output(z15.object({ workspace: WorkspaceSchema })),
39742
39835
  /** Remove an imported workspace registration. */
39743
- remove: oc.input(WorkspaceRemoveInputSchema).output(z14.object({ activeId: WorkspaceIdSchema })),
39836
+ remove: oc.input(WorkspaceRemoveInputSchema).output(z15.object({ activeId: WorkspaceIdSchema })),
39744
39837
  /** Select the active workspace. */
39745
- setActive: oc.input(WorkspaceSetActiveInputSchema).output(z14.object({ activeId: WorkspaceIdSchema }))
39838
+ setActive: oc.input(WorkspaceSetActiveInputSchema).output(z15.object({ activeId: WorkspaceIdSchema })),
39839
+ /**
39840
+ * 原生目录选择器(ext-dialog;tray 挂载后可用)。用户取消 → path:null;
39841
+ * 平台不支持 / headless → supported:false(WebUI 以此隐藏 Browse 入口)。
39842
+ */
39843
+ pickDirectory: oc.input(z15.object({})).output(z15.object({ supported: z15.boolean(), path: z15.string().nullable() }))
39746
39844
  },
39747
39845
  creator: {
39748
39846
  /** Create or revision-check and update a skill. */
@@ -39750,7 +39848,7 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39750
39848
  /** Load an editable skill document. */
39751
39849
  load: oc.input(SkillsInfoInputSchema).output(SkillDocumentSchema),
39752
39850
  /** Revision-check and delete one skill. */
39753
- remove: oc.input(CreatorRemoveInputSchema).output(z14.object({ removed: z14.literal(true) })),
39851
+ remove: oc.input(CreatorRemoveInputSchema).output(z15.object({ removed: z15.literal(true) })),
39754
39852
  /** Read revision history for one skill (change 5: change log sub-view). */
39755
39853
  revisions: oc.input(CreatorRevisionsInputSchema).output(CreatorRevisionsResultSchema)
39756
39854
  },
@@ -39763,29 +39861,29 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39763
39861
  install: oc.input(RepositoryInstallInputSchema).output(InstallResultSchema),
39764
39862
  sources: {
39765
39863
  /** List curated (built-in) and user-persisted sources for the Discover grid. */
39766
- list: oc.input(z14.object({})).output(
39767
- z14.object({
39768
- builtIn: z14.array(
39769
- z14.object({
39770
- id: z14.string(),
39771
- label: z14.string(),
39772
- gitUrl: z14.string(),
39773
- description: z14.string(),
39774
- homepage: z14.string().optional()
39864
+ list: oc.input(z15.object({})).output(
39865
+ z15.object({
39866
+ builtIn: z15.array(
39867
+ z15.object({
39868
+ id: z15.string(),
39869
+ label: z15.string(),
39870
+ gitUrl: z15.string(),
39871
+ description: z15.string(),
39872
+ homepage: z15.string().optional()
39775
39873
  })
39776
39874
  ),
39777
- user: z14.array(UserSourceSchema)
39875
+ user: z15.array(UserSourceSchema)
39778
39876
  })
39779
39877
  ),
39780
39878
  /** Persist a new user source (https git URL only). */
39781
- add: oc.input(AddUserSourceInputSchema).output(z14.object({ source: UserSourceSchema })),
39879
+ add: oc.input(AddUserSourceInputSchema).output(z15.object({ source: UserSourceSchema })),
39782
39880
  /** Remove one user-persisted source; built-in ids are rejected. */
39783
- remove: oc.input(RemoveUserSourceInputSchema).output(z14.object({ removed: z14.literal(true) }))
39881
+ remove: oc.input(RemoveUserSourceInputSchema).output(z15.object({ removed: z15.literal(true) }))
39784
39882
  }
39785
39883
  },
39786
39884
  daemon: {
39787
39885
  /** Read the live daemon and tray status. */
39788
- status: oc.input(z14.object({})).output(DaemonStatusSchema)
39886
+ status: oc.input(z15.object({})).output(DaemonStatusSchema)
39789
39887
  },
39790
39888
  skillIntelligence: {
39791
39889
  /** Read-only multi-skill analysis locked to observed revisions. */
@@ -39793,7 +39891,7 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39793
39891
  /** Store a Manager-owned proposal draft; never mutates Providers. */
39794
39892
  propose: oc.input(ProposeInputSchema).output(ProposeResultSchema),
39795
39893
  /** List pending proposal drafts (newest first). */
39796
- list: oc.input(z14.object({})).output(ListProposalsResultSchema),
39894
+ list: oc.input(z15.object({})).output(ListProposalsResultSchema),
39797
39895
  /** Delete one proposal draft. */
39798
39896
  reject: oc.input(RejectProposalInputSchema).output(RejectProposalResultSchema),
39799
39897
  /** Apply one proposal through existing revision-safe mutations. */
@@ -39801,9 +39899,9 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39801
39899
  },
39802
39900
  steward: {
39803
39901
  /** Probe every registered backend (typed unavailable; no auto fallback). */
39804
- backends: oc.input(z14.object({})).output(StewardBackendsResultSchema),
39902
+ backends: oc.input(z15.object({})).output(StewardBackendsResultSchema),
39805
39903
  /** List steward runs (newest first, bounded). */
39806
- list: oc.input(z14.object({})).output(StewardListResultSchema),
39904
+ list: oc.input(z15.object({})).output(StewardListResultSchema),
39807
39905
  /** Start a steward run on an explicit backend and Imported Workspace.Provider. */
39808
39906
  start: oc.input(StewardStartInputSchema).output(StewardStartResultSchema),
39809
39907
  /** Poll normalized run events after a seq cursor. */
@@ -39838,16 +39936,16 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39838
39936
  },
39839
39937
  /** MCP mutation proposal 审批链(task 4.4;Manager authority 的决定面)。 */
39840
39938
  proposals: {
39841
- list: oc.input(z14.object({})).output(z14.object({ proposals: z14.array(AgentMcpProposalViewSchema) })),
39842
- approve: oc.input(AgentProposalDecisionInputSchema).output(z14.object({ proposal: AgentMcpProposalViewSchema })),
39843
- reject: oc.input(AgentProposalDecisionInputSchema).output(z14.object({ proposal: AgentMcpProposalViewSchema }))
39939
+ list: oc.input(z15.object({})).output(z15.object({ proposals: z15.array(AgentMcpProposalViewSchema) })),
39940
+ approve: oc.input(AgentProposalDecisionInputSchema).output(z15.object({ proposal: AgentMcpProposalViewSchema })),
39941
+ reject: oc.input(AgentProposalDecisionInputSchema).output(z15.object({ proposal: AgentMcpProposalViewSchema }))
39844
39942
  },
39845
39943
  /** 面板会话:内核 agent 会话的生命周期投影(task 2.2;旧 dsh.* 收敛并入)。 */
39846
39944
  sessions: {
39847
39945
  /** 列出内核 live 会话(含非面板会话的 disposed 投影)。 */
39848
- list: oc.input(z14.object({})).output(z14.object({ sessions: z14.array(AgentSessionSummarySchema) })),
39946
+ list: oc.input(z15.object({})).output(z15.object({ sessions: z15.array(AgentSessionSummarySchema) })),
39849
39947
  /** 脱敏 run/session 帧查询(原 dsh.sessions.streams 平移;steward 投影消费)。 */
39850
- streams: oc.input(AgentSessionsStreamsInputSchema).output(z14.object({ frames: z14.array(DshSessionStreamFrameSchema) })),
39948
+ streams: oc.input(AgentSessionsStreamsInputSchema).output(z15.object({ frames: z15.array(DshSessionStreamFrameSchema) })),
39851
39949
  /**
39852
39950
  * 清理面板会话转录(R14-C):按保留天数 / 全量 / 显式 ID 删除产品转录层
39853
39951
  * (sessions/YYYY/MM/DD),running 会话跳过;不触碰 $DSH_HOME 内核日志。
@@ -39890,16 +39988,16 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39890
39988
  /** 单文件预览:图片缩略(jSquash)/ 文本头 / 二进制名投影。 */
39891
39989
  preview: oc.input(AgentFilesPreviewInputSchema).output(AgentFilesPreviewResultSchema),
39892
39990
  /** 原生文件选择(R18 @xmorse/rfd):mode 预置 filter;空 = 取消。 */
39893
- pickFiles: oc.input(z14.object({ mode: z14.enum(["image", "file"]) })).output(z14.object({ paths: z14.array(z14.string().min(1)) }))
39991
+ pickFiles: oc.input(z15.object({ mode: z15.enum(["image", "file"]) })).output(z15.object({ paths: z15.array(z15.string().min(1)) }))
39894
39992
  },
39895
39993
  /** pi-ai 装配目录(models.dev 镜像)投影:全量 provider 画廊(filter 在 UI)。 */
39896
39994
  models: {
39897
- catalog: oc.input(z14.object({})).output(z14.object({ providers: z14.array(ModelProviderCatalogEntrySchema) }))
39995
+ catalog: oc.input(z15.object({})).output(z15.object({ providers: z15.array(ModelProviderCatalogEntrySchema) }))
39898
39996
  },
39899
39997
  /** model/preset/permission/approval 投影(原 dsh.settings 平移)。 */
39900
39998
  settings: {
39901
39999
  /** 当前 settings 投影 + provider 凭据(apiKey 按 R16 用户裁决回显)。 */
39902
- get: oc.input(z14.object({})).output(DshStewardSettingsViewSchema),
40000
+ get: oc.input(z15.object({})).output(DshStewardSettingsViewSchema),
39903
40001
  /** 应用补丁;revision 只在真实变更时 +1;类型化 rejected 见契约 union。 */
39904
40002
  update: oc.input(DshSettingsUpdateSchema).output(DshSettingsUpdateResultSchema),
39905
40003
  /**
@@ -39924,13 +40022,13 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
39924
40022
  /** List ACP-capable agents installed on this machine (daemon-lifetime cached). */
39925
40023
  agents: {
39926
40024
  /** Probe known PATH binaries and return their availability projection. */
39927
- list: oc.input(z14.object({})).output(z14.object({ agents: z14.array(AcpAgentInfoSchema) }))
40025
+ list: oc.input(z15.object({})).output(z15.object({ agents: z15.array(AcpAgentInfoSchema) }))
39928
40026
  },
39929
40027
  session: {
39930
40028
  /** Spawn the selected agent subprocess and register a daemon-owned session. */
39931
40029
  open: oc.input(AcpSessionOpenInputSchema).output(AcpSessionOpenResultSchema),
39932
40030
  /** Terminate the agent subprocess for one session (idempotent). */
39933
- close: oc.input(AcpSessionCloseInputSchema).output(z14.object({ closed: z14.literal(true) }))
40031
+ close: oc.input(AcpSessionCloseInputSchema).output(z15.object({ closed: z15.literal(true) }))
39934
40032
  }
39935
40033
  }
39936
40034
  });
@@ -41070,6 +41168,7 @@ async function save(workspaces, skills, input, revisionLog, revisionLogLimit) {
41070
41168
  const content = matter.stringify(input.body, frontmatter);
41071
41169
  atomicWriteUtf8(targetFile, content.endsWith("\n") ? content : `${content}
41072
41170
  `);
41171
+ skills.invalidateDiscovery(target);
41073
41172
  const skillId = input.mode === "create" ? (await skills.list(target, true)).find(
41074
41173
  (skill) => skill.path === fs4.realpathSync(skillDirectory)
41075
41174
  )?.id : input.skillId;
@@ -41122,6 +41221,7 @@ async function remove(workspaces, skills, target, skillId, expectedRevision) {
41122
41221
  throw new DomainError("CONFLICT", "This skill changed on disk. Reload it before deleting.");
41123
41222
  }
41124
41223
  fs4.rmSync(skill.path, { recursive: true, force: false });
41224
+ skills.invalidateDiscovery(target);
41125
41225
  }
41126
41226
  function writableDirectory(workspaces, target) {
41127
41227
  const scope = workspaces.resolveWritable(target);
@@ -41150,28 +41250,28 @@ init_workflow_install_KvZzb83I();
41150
41250
  // src/daemon/repository-service.ts
41151
41251
  import { execa as execa2 } from "execa";
41152
41252
  import matter2 from "gray-matter";
41153
- import { z as z15, ZodError as ZodError3 } from "zod";
41253
+ import { z as z16, ZodError as ZodError3 } from "zod";
41154
41254
  var CLONE_TIMEOUT_MS = 6e4;
41155
41255
  var MAX_SESSIONS = 6;
41156
- var InstallerResultEntrySchema = z15.object({
41157
- skill: z15.string(),
41158
- destination: z15.string(),
41159
- path: z15.string(),
41160
- status: z15.enum(["installed", "skipped", "overwritten", "failed"]),
41161
- error: z15.string().optional()
41256
+ var InstallerResultEntrySchema = z16.object({
41257
+ skill: z16.string(),
41258
+ destination: z16.string(),
41259
+ path: z16.string(),
41260
+ status: z16.enum(["installed", "skipped", "overwritten", "failed"]),
41261
+ error: z16.string().optional()
41162
41262
  });
41163
- var InstallerSummarySchema = z15.object({
41164
- results: z15.array(InstallerResultEntrySchema),
41165
- installed: z15.number().int().nonnegative(),
41166
- skipped: z15.number().int().nonnegative(),
41167
- overwritten: z15.number().int().nonnegative(),
41168
- failed: z15.number().int().nonnegative()
41263
+ var InstallerSummarySchema = z16.object({
41264
+ results: z16.array(InstallerResultEntrySchema),
41265
+ installed: z16.number().int().nonnegative(),
41266
+ skipped: z16.number().int().nonnegative(),
41267
+ overwritten: z16.number().int().nonnegative(),
41268
+ failed: z16.number().int().nonnegative()
41169
41269
  });
41170
- var InstallerPreviewSchema = z15.object({
41171
- dryRun: z15.literal(true),
41172
- skills: z15.array(z15.object({ name: z15.string(), description: z15.string() })),
41173
- destinations: z15.array(z15.object({ path: z15.string(), exists: z15.boolean() })),
41174
- totalInstalls: z15.number().int().nonnegative()
41270
+ var InstallerPreviewSchema = z16.object({
41271
+ dryRun: z16.literal(true),
41272
+ skills: z16.array(z16.object({ name: z16.string(), description: z16.string() })),
41273
+ destinations: z16.array(z16.object({ path: z16.string(), exists: z16.boolean() })),
41274
+ totalInstalls: z16.number().int().nonnegative()
41175
41275
  });
41176
41276
  function createRepositoryService(workspaces, skills, options2 = {}) {
41177
41277
  const sessions = /* @__PURE__ */ new Map();
@@ -41457,6 +41557,7 @@ function assertExpectedInstallerEntry(target, entry) {
41457
41557
  }
41458
41558
  }
41459
41559
  async function installedSkillId(target, skills) {
41560
+ skills.invalidateDiscovery(target.target);
41460
41561
  const canonicalPath = canonicalDirectory(target.expectedPath);
41461
41562
  if (canonicalPath !== target.expectedPath) {
41462
41563
  throw new Error("Installed skill path must not resolve through a symbolic link.");
@@ -41762,16 +41863,16 @@ function formatZodError(error) {
41762
41863
 
41763
41864
  // src/daemon/skills-cli-probe.ts
41764
41865
  import { execa as execa3 } from "execa";
41765
- import { z as z16 } from "zod";
41766
- var SkillsCliListEntrySchema = z16.object({
41866
+ import { z as z17 } from "zod";
41867
+ var SkillsCliListEntrySchema = z17.object({
41767
41868
  /** 技能名。 */
41768
- name: z16.string(),
41869
+ name: z17.string(),
41769
41870
  /** 技能的规范路径(canonicalPath)。 */
41770
- path: z16.string().min(1),
41871
+ path: z17.string().min(1),
41771
41872
  /** 作用域;CLI 输出为 `project` 或 `global`,额外值被丢弃。 */
41772
- scope: z16.union([z16.literal("project"), z16.literal("global")]).optional()
41873
+ scope: z17.union([z17.literal("project"), z17.literal("global")]).optional()
41773
41874
  });
41774
- var SkillsCliListEnvelopeSchema = z16.array(z16.unknown());
41875
+ var SkillsCliListEnvelopeSchema = z17.array(z17.unknown());
41775
41876
  var DEFAULT_TIMEOUT_MS2 = 15e3;
41776
41877
  async function runNpxSkillsList() {
41777
41878
  return execa3("npx", ["skills", "list", "--json"], {
@@ -41798,6 +41899,7 @@ function parseSkillsCliList(stdout) {
41798
41899
  function createSkillsCliProbe(options2 = {}) {
41799
41900
  const run = options2.run ?? runNpxSkillsList;
41800
41901
  let cached2 = null;
41902
+ let inflight = null;
41801
41903
  const probeOnce = async () => {
41802
41904
  try {
41803
41905
  const { stdout } = await run();
@@ -41807,10 +41909,26 @@ function createSkillsCliProbe(options2 = {}) {
41807
41909
  }
41808
41910
  };
41809
41911
  return {
41810
- /** 返回缓存的探测映射;首次调用 shell out,后续命中缓存。 */
41912
+ /** 返回缓存的探测映射;首次调用 shell out(在途共享),后续命中缓存。 */
41811
41913
  async probe() {
41812
41914
  if (cached2) return cached2;
41813
- cached2 = await probeOnce();
41915
+ if (inflight) return inflight;
41916
+ inflight = probeOnce().then((result) => {
41917
+ cached2 = result;
41918
+ return result;
41919
+ });
41920
+ try {
41921
+ return await inflight;
41922
+ } finally {
41923
+ inflight = null;
41924
+ }
41925
+ },
41926
+ /**
41927
+ * 同步快照(perf-firstscreen B-5):缓存命中返回映射,未就绪返回 null——
41928
+ * 调用方(skills.list)不阻塞等待 npx(冷启动 0-15s);daemon boot 后
41929
+ * 台预热 probe,就绪后的下一次投影自然补全 provenance。
41930
+ */
41931
+ peek() {
41814
41932
  return cached2;
41815
41933
  },
41816
41934
  /** 失效缓存;指定路径时仅移除对应条目,否则清空全部。 */
@@ -41821,7 +41939,7 @@ function createSkillsCliProbe(options2 = {}) {
41821
41939
  return;
41822
41940
  }
41823
41941
  const next = new Map(cached2);
41824
- for (const path33 of paths) next.delete(path33);
41942
+ for (const path39 of paths) next.delete(path39);
41825
41943
  cached2 = next;
41826
41944
  }
41827
41945
  };
@@ -41838,68 +41956,68 @@ import path9 from "node:path";
41838
41956
  import { execa as execa4 } from "execa";
41839
41957
 
41840
41958
  // src/shared/contracts/skills-lock.ts
41841
- import { z as z17 } from "zod";
41842
- var SkillLockEntrySchema = z17.object({
41959
+ import { z as z18 } from "zod";
41960
+ var SkillLockEntrySchema = z18.object({
41843
41961
  /** 规范化源标识(如 `owner/repo`、`mintlify/bun.com`)。 */
41844
- source: z17.string(),
41962
+ source: z18.string(),
41845
41963
  /** 源类型(`github`、`mintlify`、`huggingface`、`local` 等)。 */
41846
- sourceType: z17.string(),
41964
+ sourceType: z18.string(),
41847
41965
  /** 安装时使用的原始 URL,升级时用于重新拉取。 */
41848
- sourceUrl: z17.string(),
41966
+ sourceUrl: z18.string(),
41849
41967
  /** 安装时使用的分支或 tag ref。 */
41850
- ref: z17.string().optional(),
41968
+ ref: z18.string().optional(),
41851
41969
  /** 源仓库内的子路径(可选)。 */
41852
- skillPath: z17.string().optional(),
41970
+ skillPath: z18.string().optional(),
41853
41971
  /**
41854
41972
  * 技能整个目录的 GitHub tree SHA;任一文件变更即变化。
41855
41973
  * 由 telemetry server 通过 GitHub Trees API 取得。
41856
41974
  */
41857
- skillFolderHash: z17.string(),
41975
+ skillFolderHash: z18.string(),
41858
41976
  /** 首次安装的 ISO 时间戳。 */
41859
- installedAt: z17.string(),
41977
+ installedAt: z18.string(),
41860
41978
  /** 最近一次更新的 ISO 时间戳。 */
41861
- updatedAt: z17.string(),
41979
+ updatedAt: z18.string(),
41862
41980
  /** 所属插件名(可选)。 */
41863
- pluginName: z17.string().optional()
41981
+ pluginName: z18.string().optional()
41864
41982
  });
41865
- var DismissedPromptsSchema = z17.object({ findSkillsPrompt: z17.boolean().optional() }).optional();
41866
- var SkillLockFileSchema = z17.object({
41983
+ var DismissedPromptsSchema = z18.object({ findSkillsPrompt: z18.boolean().optional() }).optional();
41984
+ var SkillLockFileSchema = z18.object({
41867
41985
  /** schema 版本;当前固定为 3。 */
41868
- version: z17.literal(3),
41986
+ version: z18.literal(3),
41869
41987
  /** 技能名 → 记录的映射。 */
41870
- skills: z17.record(z17.string(), SkillLockEntrySchema),
41988
+ skills: z18.record(z18.string(), SkillLockEntrySchema),
41871
41989
  /** 已忽略的提示集合(可选)。 */
41872
41990
  dismissed: DismissedPromptsSchema,
41873
41991
  /** 最近一次选中的 agents(可选)。 */
41874
- lastSelectedAgents: z17.array(z17.string()).optional()
41992
+ lastSelectedAgents: z18.array(z18.string()).optional()
41875
41993
  });
41876
- var LocalSkillLockEntrySchema = z17.object({
41994
+ var LocalSkillLockEntrySchema = z18.object({
41877
41995
  /** 技能来源(npm 包名、owner/repo、本地路径等)。 */
41878
- source: z17.string(),
41996
+ source: z18.string(),
41879
41997
  /** 当 source 被规范化时,保留原始远端 URL(可选)。 */
41880
- sourceUrl: z17.string().optional(),
41998
+ sourceUrl: z18.string().optional(),
41881
41999
  /** 安装时使用的分支或 tag ref(可选)。 */
41882
- ref: z17.string().optional(),
42000
+ ref: z18.string().optional(),
41883
42001
  /** 源类型(`github`、`node_modules`、`local` 等)。 */
41884
- sourceType: z17.string(),
42002
+ sourceType: z18.string(),
41885
42003
  /**
41886
42004
  * 源仓库内 SKILL.md 的路径(如 `skills/pdf/SKILL.md`)。
41887
42005
  * 升级时按它定向重装;非仓库源可缺省。
41888
42006
  */
41889
- skillPath: z17.string().optional(),
42007
+ skillPath: z18.string().optional(),
41890
42008
  /**
41891
42009
  * 由技能目录所有文件计算的 SHA-256。
41892
42010
  * 与全局锁的 GitHub tree SHA 不同。
41893
42011
  */
41894
- computedHash: z17.string(),
42012
+ computedHash: z18.string(),
41895
42013
  /** Eve subagent 目标(可选),daemon 不消费。 */
41896
- subagents: z17.array(z17.string()).optional()
42014
+ subagents: z18.array(z18.string()).optional()
41897
42015
  });
41898
- var LocalSkillLockFileSchema = z17.object({
42016
+ var LocalSkillLockFileSchema = z18.object({
41899
42017
  /** schema 版本;当前固定为 1。 */
41900
- version: z17.literal(1),
42018
+ version: z18.literal(1),
41901
42019
  /** 技能名 → 记录的映射。 */
41902
- skills: z17.record(z17.string(), LocalSkillLockEntrySchema)
42020
+ skills: z18.record(z18.string(), LocalSkillLockEntrySchema)
41903
42021
  });
41904
42022
  function parseGlobalSkillLock(value) {
41905
42023
  const parsed = SkillLockFileSchema.safeParse(value);
@@ -42356,6 +42474,1544 @@ function safeParseJsonValue(source) {
42356
42474
  }
42357
42475
  }
42358
42476
 
42477
+ // src/daemon/skill-search/service.ts
42478
+ import fs16 from "node:fs";
42479
+ import path18 from "node:path";
42480
+
42481
+ // src/daemon/workspace-registry/persistence.ts
42482
+ import fs8 from "node:fs";
42483
+ import path11 from "node:path";
42484
+
42485
+ // src/daemon/workspace-registry/state.ts
42486
+ import path10 from "node:path";
42487
+ import { z as z19 } from "zod";
42488
+ var StoredWorkspaceSchema = z19.object({
42489
+ id: ImportedWorkspaceIdSchema,
42490
+ label: z19.string().trim().min(1),
42491
+ path: z19.string().min(1).refine(path10.isAbsolute, "Workspace path must be absolute.").refine((value) => path10.normalize(value) === value, "Workspace path must be normalized.")
42492
+ }).strict();
42493
+ var WorkspaceRegistryStateSchema = z19.object({
42494
+ schemaVersion: z19.literal(2),
42495
+ activeId: WorkspaceIdSchema,
42496
+ workspaces: z19.array(StoredWorkspaceSchema)
42497
+ }).strict().superRefine((state, context) => {
42498
+ const ids = /* @__PURE__ */ new Set();
42499
+ const paths = /* @__PURE__ */ new Set();
42500
+ for (const [index, workspace] of state.workspaces.entries()) {
42501
+ if (workspace.id !== opaquePathId("ws", workspace.path)) {
42502
+ context.addIssue({
42503
+ code: "custom",
42504
+ message: "Workspace ID does not match its path.",
42505
+ path: ["workspaces", index, "id"]
42506
+ });
42507
+ }
42508
+ if (ids.has(workspace.id)) {
42509
+ context.addIssue({
42510
+ code: "custom",
42511
+ message: `Duplicate Workspace ID: ${workspace.id}`,
42512
+ path: ["workspaces", index, "id"]
42513
+ });
42514
+ }
42515
+ if (paths.has(workspace.path)) {
42516
+ context.addIssue({
42517
+ code: "custom",
42518
+ message: `Duplicate Workspace path: ${workspace.path}`,
42519
+ path: ["workspaces", index, "path"]
42520
+ });
42521
+ }
42522
+ ids.add(workspace.id);
42523
+ paths.add(workspace.path);
42524
+ }
42525
+ if (state.activeId !== GLOBAL_WORKSPACE_ID && !ids.has(state.activeId)) {
42526
+ context.addIssue({
42527
+ code: "custom",
42528
+ message: `Active Workspace is not registered: ${state.activeId}`,
42529
+ path: ["activeId"]
42530
+ });
42531
+ }
42532
+ });
42533
+ function emptyRegistryState() {
42534
+ return { schemaVersion: 2, activeId: GLOBAL_WORKSPACE_ID, workspaces: [] };
42535
+ }
42536
+ function importWorkspace(state, entry) {
42537
+ const existing = state.workspaces.find((workspace) => workspace.path === entry.path);
42538
+ if (existing) return { state, entry: existing, changed: false };
42539
+ const collision = state.workspaces.find((workspace) => workspace.id === entry.id);
42540
+ if (collision) {
42541
+ throw new Error(`Workspace ID collision between ${collision.path} and ${entry.path}`);
42542
+ }
42543
+ return {
42544
+ state: {
42545
+ ...state,
42546
+ activeId: entry.id,
42547
+ workspaces: [...state.workspaces, entry]
42548
+ },
42549
+ entry,
42550
+ changed: true
42551
+ };
42552
+ }
42553
+ function forgetWorkspace(state, id) {
42554
+ if (!state.workspaces.some((workspace) => workspace.id === id)) return null;
42555
+ return {
42556
+ ...state,
42557
+ activeId: state.activeId === id ? GLOBAL_WORKSPACE_ID : state.activeId,
42558
+ workspaces: state.workspaces.filter((workspace) => workspace.id !== id)
42559
+ };
42560
+ }
42561
+ function activateWorkspace(state, id) {
42562
+ if (id !== GLOBAL_WORKSPACE_ID && !state.workspaces.some((workspace) => workspace.id === id)) {
42563
+ return null;
42564
+ }
42565
+ if (state.activeId === id) return state;
42566
+ return { ...state, activeId: id };
42567
+ }
42568
+
42569
+ // src/daemon/workspace-registry/persistence.ts
42570
+ var WORKSPACES_FILE = "workspaces.json";
42571
+ function createWorkspaceRegistryPersistence() {
42572
+ const file = path11.join(appDir(), WORKSPACES_FILE);
42573
+ return {
42574
+ load: () => loadState2(file),
42575
+ commit: (state) => commitState2(file, state)
42576
+ };
42577
+ }
42578
+ function loadState2(file) {
42579
+ if (!fs8.existsSync(file)) return emptyRegistryState();
42580
+ let source;
42581
+ try {
42582
+ source = fs8.readFileSync(file, "utf8");
42583
+ } catch (error) {
42584
+ throw new Error(
42585
+ `Cannot read workspace registry ${file}: ${error instanceof Error ? error.message : String(error)}`
42586
+ );
42587
+ }
42588
+ return safeParseJson(source, WorkspaceRegistryStateSchema) ?? emptyRegistryState();
42589
+ }
42590
+ function commitState2(file, state) {
42591
+ const validated = WorkspaceRegistryStateSchema.parse(state);
42592
+ fs8.mkdirSync(path11.dirname(file), { recursive: true, mode: 448 });
42593
+ atomicWriteUtf8(file, `${JSON.stringify(validated, null, 2)}
42594
+ `);
42595
+ }
42596
+
42597
+ // src/daemon/provider-roots.ts
42598
+ import fs9 from "node:fs";
42599
+ import os5 from "node:os";
42600
+ import path12 from "node:path";
42601
+ function requireProvider(providerId) {
42602
+ const provider = providerCatalogEntry(providerId);
42603
+ if (!provider) throw new DomainError("NOT_FOUND", `Provider not found: ${providerId}`);
42604
+ return provider;
42605
+ }
42606
+ function globalProviderRoot(provider) {
42607
+ if (provider.globalPath === null) return null;
42608
+ const home = os5.homedir();
42609
+ const override = globalHomeOverride(provider.id);
42610
+ if (override) return path12.join(override, "skills");
42611
+ if (provider.id === "openclaw") return openClawRoot(home);
42612
+ if (provider.globalPath.startsWith(".config/")) {
42613
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || path12.join(home, ".config");
42614
+ return path12.join(configHome, provider.globalPath.slice(".config/".length));
42615
+ }
42616
+ return path12.join(home, provider.globalPath);
42617
+ }
42618
+ function importedProviderRoot(workspaceRoot, provider) {
42619
+ const root = path12.resolve(workspaceRoot, provider.workspacePath);
42620
+ const relative2 = path12.relative(workspaceRoot, root);
42621
+ if (relative2.startsWith("..") || path12.isAbsolute(relative2)) {
42622
+ throw new Error(`Provider root escaped Workspace: ${provider.id}`);
42623
+ }
42624
+ return root;
42625
+ }
42626
+ function providerRootAvailable(root) {
42627
+ if (!root) return false;
42628
+ try {
42629
+ return fs9.statSync(root).isDirectory();
42630
+ } catch {
42631
+ return false;
42632
+ }
42633
+ }
42634
+ function globalHomeOverride(providerId) {
42635
+ const variable = providerId === "codex" ? "CODEX_HOME" : providerId === "claude-code" ? "CLAUDE_CONFIG_DIR" : providerId === "mistral-vibe" ? "VIBE_HOME" : providerId === "hermes-agent" ? "HERMES_HOME" : providerId === "autohand-code" ? "AUTOHAND_HOME" : providerId === "grok" ? "GROK_HOME" : null;
42636
+ const value = variable ? process.env[variable]?.trim() : void 0;
42637
+ return value || null;
42638
+ }
42639
+ function openClawRoot(home) {
42640
+ for (const directory of [".openclaw", ".clawdbot", ".moltbot"]) {
42641
+ if (providerRootAvailable(path12.join(home, directory)))
42642
+ return path12.join(home, directory, "skills");
42643
+ }
42644
+ return path12.join(home, ".openclaw", "skills");
42645
+ }
42646
+
42647
+ // src/daemon/skill-search/canonicalize.ts
42648
+ import fs11 from "node:fs";
42649
+ import path14 from "node:path";
42650
+
42651
+ // src/daemon/skill-search/content-files.ts
42652
+ import fs10 from "node:fs";
42653
+ import path13 from "node:path";
42654
+ var BUILTIN_EXCLUDED_DIRS = [
42655
+ "node_modules",
42656
+ "build",
42657
+ "dist",
42658
+ "target",
42659
+ "__pycache__",
42660
+ "tmp",
42661
+ "logs"
42662
+ ];
42663
+ var MAX_DEPTH = 4;
42664
+ var MAX_FILES = 32;
42665
+ var IDENTITY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", ".skill.md"]);
42666
+ function collectExtraMarkdownFiles(canonicalPath, excludedDirs = /* @__PURE__ */ new Set()) {
42667
+ const effective = /* @__PURE__ */ new Set([...BUILTIN_EXCLUDED_DIRS, ...excludedDirs]);
42668
+ const files = [];
42669
+ collectDirectory(canonicalPath, 0, effective, files);
42670
+ files.sort((left, right) => compareString(left.path, right.path));
42671
+ return files.slice(0, MAX_FILES);
42672
+ }
42673
+ function collectDirectory(directory, depth, excludedDirs, files) {
42674
+ if (depth > MAX_DEPTH || files.length >= MAX_FILES) return;
42675
+ let dirents;
42676
+ try {
42677
+ dirents = fs10.readdirSync(directory, { withFileTypes: true });
42678
+ } catch {
42679
+ return;
42680
+ }
42681
+ dirents.sort((left, right) => compareString(left.name, right.name));
42682
+ for (const dirent of dirents) {
42683
+ if (files.length >= MAX_FILES) return;
42684
+ const entryPath = path13.join(directory, dirent.name);
42685
+ if (dirent.isDirectory()) {
42686
+ if (dirent.name.startsWith(".") || excludedDirs.has(dirent.name)) continue;
42687
+ let descentStat;
42688
+ try {
42689
+ descentStat = fs10.lstatSync(entryPath);
42690
+ } catch {
42691
+ continue;
42692
+ }
42693
+ if (!descentStat.isDirectory() || descentStat.isSymbolicLink()) continue;
42694
+ collectDirectory(entryPath, depth + 1, excludedDirs, files);
42695
+ continue;
42696
+ }
42697
+ if (!dirent.isFile()) continue;
42698
+ if (!dirent.name.toLowerCase().endsWith(".md")) continue;
42699
+ if (IDENTITY_FILE_NAMES.has(dirent.name.toLowerCase())) continue;
42700
+ if (!isRegularFile(entryPath)) continue;
42701
+ let stat2;
42702
+ try {
42703
+ stat2 = fs10.statSync(entryPath);
42704
+ } catch {
42705
+ continue;
42706
+ }
42707
+ if (!stat2.isFile()) continue;
42708
+ files.push({
42709
+ path: entryPath,
42710
+ mtimeMs: stat2.mtimeMs,
42711
+ size: stat2.size,
42712
+ ino: stat2.ino,
42713
+ ctimeMs: stat2.ctimeMs
42714
+ });
42715
+ }
42716
+ }
42717
+ function isRegularFile(file) {
42718
+ try {
42719
+ return fs10.lstatSync(file).isFile();
42720
+ } catch {
42721
+ return false;
42722
+ }
42723
+ }
42724
+ function compareString(left, right) {
42725
+ return left < right ? -1 : left > right ? 1 : 0;
42726
+ }
42727
+
42728
+ // src/daemon/skill-search/canonicalize.ts
42729
+ function canonicalizeCandidates(entries, excludedDirs = /* @__PURE__ */ new Set()) {
42730
+ const groups = /* @__PURE__ */ new Map();
42731
+ for (const entry of entries) {
42732
+ let canonicalPath;
42733
+ try {
42734
+ canonicalPath = fs11.realpathSync(entry.path);
42735
+ } catch {
42736
+ continue;
42737
+ }
42738
+ const installation = {
42739
+ path: entry.path,
42740
+ workspaceId: entry.workspaceId,
42741
+ providerId: entry.providerId
42742
+ };
42743
+ const group = groups.get(canonicalPath) ?? { installations: [] };
42744
+ const exists = group.installations.some(
42745
+ (candidate) => candidate.path === installation.path && candidate.workspaceId === installation.workspaceId && candidate.providerId === installation.providerId
42746
+ );
42747
+ if (!exists) group.installations.push(installation);
42748
+ groups.set(canonicalPath, group);
42749
+ }
42750
+ const scans = [];
42751
+ for (const [canonicalPath, group] of groups) {
42752
+ const source = selectSourceFile(canonicalPath);
42753
+ if (!source) continue;
42754
+ let sourceStat;
42755
+ try {
42756
+ sourceStat = fs11.statSync(source.sourcePath);
42757
+ } catch {
42758
+ continue;
42759
+ }
42760
+ const extras = collectExtraMarkdownFiles(canonicalPath, excludedDirs);
42761
+ const files = [
42762
+ {
42763
+ path: source.sourcePath,
42764
+ mtimeMs: sourceStat.mtimeMs,
42765
+ size: sourceStat.size,
42766
+ ino: sourceStat.ino,
42767
+ ctimeMs: sourceStat.ctimeMs
42768
+ },
42769
+ ...extras
42770
+ ].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
42771
+ scans.push({
42772
+ id: SkillIdSchema.parse(opaquePathId("sk", canonicalPath)),
42773
+ canonicalPath,
42774
+ installations: group.installations.sort(
42775
+ (left, right) => compareString2(left.path, right.path)
42776
+ ),
42777
+ sourceFile: source.sourceFile,
42778
+ sourcePath: source.sourcePath,
42779
+ disabled: source.disabled,
42780
+ conflict: source.conflict,
42781
+ files
42782
+ });
42783
+ }
42784
+ scans.sort((left, right) => compareString2(left.canonicalPath, right.canonicalPath));
42785
+ return scans;
42786
+ }
42787
+ function selectSourceFile(canonicalPath) {
42788
+ const enabled = path14.join(canonicalPath, "SKILL.md");
42789
+ const disabledFile = path14.join(canonicalPath, ".SKILL.md");
42790
+ const hasEnabled = isRegularFile2(enabled);
42791
+ const hasDisabled = isRegularFile2(disabledFile);
42792
+ if (hasEnabled) {
42793
+ return { sourceFile: "SKILL.md", sourcePath: enabled, disabled: false, conflict: hasDisabled };
42794
+ }
42795
+ if (hasDisabled) {
42796
+ return { sourceFile: ".SKILL.md", sourcePath: disabledFile, disabled: true, conflict: false };
42797
+ }
42798
+ return null;
42799
+ }
42800
+ function compareString2(left, right) {
42801
+ return left < right ? -1 : left > right ? 1 : 0;
42802
+ }
42803
+ function isRegularFile2(file) {
42804
+ try {
42805
+ return fs11.lstatSync(file).isFile();
42806
+ } catch {
42807
+ return false;
42808
+ }
42809
+ }
42810
+
42811
+ // src/daemon/skill-search/config.ts
42812
+ import { createHash as createHash8 } from "node:crypto";
42813
+ import fs13 from "node:fs";
42814
+ import path16 from "node:path";
42815
+ import { parse as parseToml } from "smol-toml";
42816
+ import { z as z22 } from "zod";
42817
+
42818
+ // src/daemon/skill-search/index.ts
42819
+ import { createHash as createHash7 } from "node:crypto";
42820
+ import { createRequire as createRequire4 } from "node:module";
42821
+ import fs12 from "node:fs";
42822
+ import path15 from "node:path";
42823
+ import MiniSearch from "minisearch";
42824
+ import { z as z21 } from "zod";
42825
+
42826
+ // src/daemon/skill-search/parser.ts
42827
+ import { createHash as createHash6 } from "node:crypto";
42828
+ import matter3 from "gray-matter";
42829
+ import { z as z20 } from "zod";
42830
+ var PARSER_VERSION = "matter-mdset-v2";
42831
+ var HEADINGS_LIMIT = 30;
42832
+ var BODY_LIMIT = 12e3;
42833
+ var EXTRA_FILE_LIMIT = 6e3;
42834
+ var EXTRA_TOTAL_LIMIT = 12e3;
42835
+ var FENCE_MARKER_RE = /^[ \t]*(?:```|~~~)/;
42836
+ var HEADING_RE = /^#{1,4}[ \t]+(.+)$/gm;
42837
+ var FrontmatterShapeSchema = z20.object({
42838
+ name: z20.string().min(1),
42839
+ description: z20.string().min(1)
42840
+ });
42841
+ function parseSkillDocumentSet(sourceRaw, extras, directoryName) {
42842
+ return finalizeDocument(splitFrontmatter(sourceRaw), sourceRaw, extras, directoryName);
42843
+ }
42844
+ function finalizeDocument(split, sourceRaw, extras, directoryName) {
42845
+ const hash = createHash6("sha256").update(sourceRaw);
42846
+ for (const extra of extras) hash.update(extra.raw);
42847
+ const headingLines = [];
42848
+ const bodyLines = [];
42849
+ let inFence = false;
42850
+ for (const line of split.content.split("\n")) {
42851
+ if (FENCE_MARKER_RE.test(line)) {
42852
+ inFence = !inFence;
42853
+ continue;
42854
+ }
42855
+ bodyLines.push(line);
42856
+ if (!inFence) headingLines.push(line);
42857
+ }
42858
+ const headings = collectHeadings(headingLines.join("\n"));
42859
+ const skillBody = bodyLines.join("\n").slice(0, BODY_LIMIT);
42860
+ let extraBudget = EXTRA_TOTAL_LIMIT;
42861
+ const extraParts = [];
42862
+ for (const extra of extras) {
42863
+ if (extraBudget <= 0) break;
42864
+ const text = stripFences(extra.raw.toString("utf8")).trim().slice(0, EXTRA_FILE_LIMIT);
42865
+ if (!text) continue;
42866
+ const bounded = text.slice(0, extraBudget);
42867
+ extraBudget -= bounded.length;
42868
+ extraParts.push(bounded);
42869
+ }
42870
+ const body = extraParts.length > 0 ? `${skillBody}
42871
+ ${extraParts.join("\n")}` : skillBody;
42872
+ const valid = safeParseExternal(FrontmatterShapeSchema, split.frontmatter);
42873
+ const frontmatterRecord = split.frontmatter !== null && typeof split.frontmatter === "object" ? split.frontmatter : {};
42874
+ return {
42875
+ name: valid ? valid.name : directoryName,
42876
+ description: valid ? valid.description : "",
42877
+ keywords: narrowStringList(frontmatterRecord.keywords),
42878
+ triggers: narrowStringList(frontmatterRecord.triggers),
42879
+ headings,
42880
+ body,
42881
+ contentHash: hash.digest("hex"),
42882
+ invalidFrontmatter: valid === null
42883
+ };
42884
+ }
42885
+ function splitFrontmatter(raw) {
42886
+ const text = raw.toString("utf8");
42887
+ try {
42888
+ const parsed = matter3(text);
42889
+ return { frontmatter: parsed.data ?? {}, content: parsed.content ?? "" };
42890
+ } catch {
42891
+ return { frontmatter: {}, content: text };
42892
+ }
42893
+ }
42894
+ function stripFences(text) {
42895
+ const lines = [];
42896
+ let inFence = false;
42897
+ for (const line of text.split("\n")) {
42898
+ if (FENCE_MARKER_RE.test(line)) {
42899
+ inFence = !inFence;
42900
+ continue;
42901
+ }
42902
+ lines.push(line);
42903
+ }
42904
+ return lines.join("\n");
42905
+ }
42906
+ function narrowStringList(value) {
42907
+ if (typeof value === "string") return [value];
42908
+ if (!Array.isArray(value)) return [];
42909
+ return value.filter((entry) => typeof entry === "string");
42910
+ }
42911
+ function collectHeadings(content) {
42912
+ const headings = [];
42913
+ for (const match of content.matchAll(HEADING_RE)) {
42914
+ headings.push(match[1].trim());
42915
+ if (headings.length >= HEADINGS_LIMIT) break;
42916
+ }
42917
+ return headings.join("\n");
42918
+ }
42919
+
42920
+ // src/daemon/skill-search/ranking.ts
42921
+ var RANKING_VERSION = "rerank-2026-09-18-v2";
42922
+ var TOP_CANDIDATES = 40;
42923
+ var RERANK_WEIGHTS = {
42924
+ exactName: 0.9,
42925
+ namePrefix: 0.5,
42926
+ queryInName: 0.4,
42927
+ keywordExact: 0.3,
42928
+ descCoverage: 0.2
42929
+ };
42930
+ var RERANK_CAP = 1;
42931
+ var MIX = { bm25: 0.7, rerank: 0.3 };
42932
+ var NORM_K = 8;
42933
+ function rankResults(candidates, query, limit, tokenize) {
42934
+ const normalizedQuery = query.trim().toLowerCase();
42935
+ const queryTokens = [...new Set(tokenize(query))];
42936
+ const rerankCache = /* @__PURE__ */ new Map();
42937
+ const groups = /* @__PURE__ */ new Map();
42938
+ for (const candidate of candidates) {
42939
+ const group = groups.get(candidate.contentHash) ?? [];
42940
+ group.push(candidate);
42941
+ groups.set(candidate.contentHash, group);
42942
+ }
42943
+ const orderedGroups = [...groups.values()].map((group) => group.slice().sort(byGroupOrder));
42944
+ const pool = orderedGroups.map((group) => group[0]).sort(byGroupOrder).slice(0, TOP_CANDIDATES);
42945
+ const scored = pool.map((candidate) => {
42946
+ const rerank = rerankScore(candidate, normalizedQuery, queryTokens, tokenize, rerankCache);
42947
+ return {
42948
+ candidate,
42949
+ final: stableFinal(
42950
+ MIX.bm25 * (candidate.bm25 / (candidate.bm25 + NORM_K)) + MIX.rerank * rerank
42951
+ )
42952
+ };
42953
+ });
42954
+ scored.sort(byFrozenTieBreak);
42955
+ return scored.slice(0, limit).map(({ candidate, final }) => {
42956
+ const group = groups.get(candidate.contentHash) ?? [candidate];
42957
+ const members = group.filter((member) => member !== candidate).sort(byGroupOrder);
42958
+ return {
42959
+ id: candidate.id,
42960
+ name: candidate.name,
42961
+ description: candidate.description,
42962
+ canonicalPath: candidate.canonicalPath,
42963
+ installations: mergeInstallations(candidate, members),
42964
+ contentHash: candidate.contentHash,
42965
+ disabled: candidate.disabled,
42966
+ conflict: candidate.conflict,
42967
+ score: final,
42968
+ duplicates: members.map((member) => ({
42969
+ id: member.id,
42970
+ canonicalPath: member.canonicalPath
42971
+ }))
42972
+ };
42973
+ });
42974
+ }
42975
+ function byGroupOrder(left, right) {
42976
+ if (left.bm25 !== right.bm25) return right.bm25 - left.bm25;
42977
+ return left.canonicalPath < right.canonicalPath ? -1 : left.canonicalPath > right.canonicalPath ? 1 : 0;
42978
+ }
42979
+ function mergeInstallations(primary, members) {
42980
+ const seen = /* @__PURE__ */ new Set();
42981
+ const merged = [];
42982
+ for (const installation of [primary, ...members].flatMap(
42983
+ (candidate) => candidate.installations
42984
+ )) {
42985
+ const key = `${installation.path}|${installation.workspaceId}|${installation.providerId}`;
42986
+ if (seen.has(key)) continue;
42987
+ seen.add(key);
42988
+ merged.push(installation);
42989
+ }
42990
+ return merged;
42991
+ }
42992
+ function stableFinal(raw) {
42993
+ return Number(raw.toPrecision(12));
42994
+ }
42995
+ function byFrozenTieBreak(left, right) {
42996
+ if (left.final !== right.final) return right.final - left.final;
42997
+ if (left.candidate.name !== right.candidate.name) {
42998
+ return left.candidate.name < right.candidate.name ? -1 : 1;
42999
+ }
43000
+ return left.candidate.canonicalPath < right.candidate.canonicalPath ? -1 : left.candidate.canonicalPath > right.candidate.canonicalPath ? 1 : 0;
43001
+ }
43002
+ function rerankScore(candidate, normalizedQuery, queryTokens, tokenize, descriptionTokenCache) {
43003
+ if (normalizedQuery === "") return 0;
43004
+ const lowerName = candidate.name.toLowerCase();
43005
+ let score = 0;
43006
+ if (lowerName === normalizedQuery) score += RERANK_WEIGHTS.exactName;
43007
+ if (lowerName.startsWith(normalizedQuery)) score += RERANK_WEIGHTS.namePrefix;
43008
+ if (lowerName.includes(normalizedQuery)) score += RERANK_WEIGHTS.queryInName;
43009
+ if (candidate.keywords.some((keyword) => keyword.toLowerCase() === normalizedQuery)) {
43010
+ score += RERANK_WEIGHTS.keywordExact;
43011
+ }
43012
+ if (queryTokens.length > 0) {
43013
+ let tokens2 = descriptionTokenCache.get(candidate.description);
43014
+ if (!tokens2) {
43015
+ tokens2 = new Set(tokenize(candidate.description));
43016
+ descriptionTokenCache.set(candidate.description, tokens2);
43017
+ }
43018
+ const covered = queryTokens.filter((token) => tokens2.has(token)).length;
43019
+ score += RERANK_WEIGHTS.descCoverage * (covered / queryTokens.length);
43020
+ }
43021
+ return Math.min(score, RERANK_CAP);
43022
+ }
43023
+
43024
+ // src/daemon/skill-search/tokenizer.ts
43025
+ var TOKENIZER_VERSION = "segmenter-bigram-v1";
43026
+ var CJK_SCRIPTS_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
43027
+ var CJK_RUN_SOURCE = "[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+";
43028
+ var LATIN_RUN_SOURCE = "[@A-Za-z0-9:/._-]+";
43029
+ var RUN_RE = new RegExp(`${CJK_RUN_SOURCE}|${LATIN_RUN_SOURCE}|.`, "gu");
43030
+ var CJK_RUN_START_RE = new RegExp(`^${CJK_RUN_SOURCE}`, "u");
43031
+ var CAMEL_RE = new RegExp("\\p{Lu}+(?=\\p{Lu}[\\p{Ll}\\d])|\\p{Lu}[\\p{Ll}\\d]*|\\p{Lu}+|\\p{Ll}+|\\d+", "gu");
43032
+ function probeSegmenter() {
43033
+ if (typeof Intl.Segmenter === "undefined") return null;
43034
+ let segmenter2;
43035
+ try {
43036
+ segmenter2 = new Intl.Segmenter("zh", { granularity: "word" });
43037
+ } catch {
43038
+ return null;
43039
+ }
43040
+ const words = [...segmenter2.segment("\u7EC4\u4EF6\u8BBE\u8BA1")].filter((segment) => segment.isWordLike).map((segment) => segment.segment);
43041
+ if (words.length === 0) return null;
43042
+ if (words.every((word) => [...word].length === 1)) return null;
43043
+ return segmenter2;
43044
+ }
43045
+ function createSkillTokenizer() {
43046
+ const segmenter2 = probeSegmenter();
43047
+ function segmentCjk(text) {
43048
+ if (!segmenter2) return [...text];
43049
+ return [...segmenter2.segment(text)].filter((segment) => segment.isWordLike).map((segment) => segment.segment);
43050
+ }
43051
+ return { tokenize };
43052
+ function tokenize(text) {
43053
+ const normalized = text.normalize("NFKC");
43054
+ const out = [];
43055
+ for (const match of normalized.matchAll(RUN_RE)) {
43056
+ const run = match[0];
43057
+ if (CJK_RUN_START_RE.test(run)) {
43058
+ emitCjkRun(segmentCjk(run), out);
43059
+ } else if (/^[@A-Za-z0-9]/.test(run)) {
43060
+ splitLatinIdent(run, out);
43061
+ }
43062
+ }
43063
+ const seen = /* @__PURE__ */ new Set();
43064
+ const tokens2 = [];
43065
+ for (const token of out) {
43066
+ const lowered = token.toLowerCase();
43067
+ if (seen.has(lowered)) continue;
43068
+ seen.add(lowered);
43069
+ tokens2.push(lowered);
43070
+ }
43071
+ return tokens2.filter(
43072
+ (token) => CJK_SCRIPTS_RE.test(token) ? [...token].length >= 1 : token.length >= 2 || /\d/.test(token)
43073
+ );
43074
+ }
43075
+ }
43076
+ function emitCjkRun(words, out) {
43077
+ let i = 0;
43078
+ while (i < words.length) {
43079
+ const word = words[i];
43080
+ if ([...word].length >= 2) {
43081
+ out.push(word);
43082
+ i += 1;
43083
+ continue;
43084
+ }
43085
+ let j = i;
43086
+ while (j < words.length && [...words[j]].length === 1) j += 1;
43087
+ const singles = words.slice(i, j);
43088
+ if (singles.length === 1) {
43089
+ out.push(singles[0]);
43090
+ } else {
43091
+ for (let k = 0; k + 1 < singles.length; k++) {
43092
+ out.push(singles[k] + singles[k + 1]);
43093
+ }
43094
+ }
43095
+ i = j;
43096
+ }
43097
+ }
43098
+ function emitKebab(chunk, out) {
43099
+ const atoms = chunk.split(/[^A-Za-z0-9]+/).filter((atom) => atom.length > 0);
43100
+ for (const atom of atoms) {
43101
+ const words = atom.match(CAMEL_RE) ?? [atom];
43102
+ out.push(...words);
43103
+ if (words.length > 1) out.push(atom.toLowerCase());
43104
+ }
43105
+ if (atoms.length > 1) out.push(atoms.join("-").toLowerCase());
43106
+ }
43107
+ function splitLatinIdent(run, out) {
43108
+ const parts = run.split("/");
43109
+ if (parts.length >= 2) {
43110
+ const last = parts[parts.length - 1];
43111
+ const owner = parts[parts.length - 2];
43112
+ if (/^@?[a-z0-9][a-z0-9._-]*$/i.test(owner) && /^[a-z0-9][a-z0-9._-]*$/i.test(last)) {
43113
+ const scope = owner.replace(/^@/, "");
43114
+ out.push(scope, last, `${scope}/${last}`);
43115
+ }
43116
+ for (const part of parts) emitKebab(part, out);
43117
+ return;
43118
+ }
43119
+ emitKebab(run, out);
43120
+ }
43121
+
43122
+ // src/daemon/skill-search/index.ts
43123
+ var SCHEMA_VERSION = 3;
43124
+ var ENGINE_NAME = "minisearch";
43125
+ var SEARCH_INDEX_FILE = "search-index.json";
43126
+ var SEARCH_FIELDS = ["name", "description", "keywords", "triggers", "headings", "body"];
43127
+ var FIELD_BOOST = {
43128
+ name: 10,
43129
+ description: 6,
43130
+ keywords: 5,
43131
+ triggers: 5,
43132
+ headings: 3,
43133
+ body: 1
43134
+ };
43135
+ var STORE_FIELDS = [
43136
+ "name",
43137
+ "description",
43138
+ "keywords",
43139
+ "canonicalPath",
43140
+ "installations",
43141
+ "contentHash",
43142
+ "disabled",
43143
+ "conflict",
43144
+ "invalidFrontmatter"
43145
+ ];
43146
+ var SEARCH_PREFIX = true;
43147
+ var SEARCH_FUZZY = 0.2;
43148
+ var REBUILD_DISCARD_RATIO = 0.2;
43149
+ var ENGINE_CONFIG_DIGEST = createHash7("sha256").update(
43150
+ JSON.stringify({
43151
+ fields: [...SEARCH_FIELDS],
43152
+ boost: FIELD_BOOST,
43153
+ prefix: SEARCH_PREFIX,
43154
+ fuzzy: SEARCH_FUZZY,
43155
+ processTerm: "identity"
43156
+ })
43157
+ ).digest("hex").slice(0, 16);
43158
+ var SkillSearchIndexError = class extends DomainError {
43159
+ constructor(message, options2) {
43160
+ super("UNAVAILABLE", message, options2);
43161
+ this.name = "SkillSearchIndexError";
43162
+ }
43163
+ };
43164
+ var FileIdentitySchema = z21.object({
43165
+ path: z21.string().min(1),
43166
+ mtimeMs: z21.number(),
43167
+ size: z21.number(),
43168
+ ino: z21.number(),
43169
+ ctimeMs: z21.number()
43170
+ }).strict();
43171
+ var StatEnvelopeSchema = z21.object({
43172
+ canonicalPath: z21.string().min(1),
43173
+ files: z21.array(FileIdentitySchema).min(1),
43174
+ installations: z21.array(SkillInstallationSchema).min(1),
43175
+ contentHash: z21.string().regex(/^[a-f0-9]{64}$/),
43176
+ disabled: z21.boolean(),
43177
+ conflict: z21.boolean(),
43178
+ invalidFrontmatter: z21.boolean()
43179
+ }).strict();
43180
+ var IndexEnvelopeSchema = z21.object({
43181
+ schemaVersion: z21.literal(3),
43182
+ tokenizerVersion: z21.string(),
43183
+ parserVersion: z21.string(),
43184
+ rankingVersion: z21.string(),
43185
+ engine: z21.object({ name: z21.string(), version: z21.string(), configDigest: z21.string() }).strict(),
43186
+ /** 生效排除配置的冻结摘要(配置变更触发全量重建)。 */
43187
+ searchConfigDigest: z21.string().min(1),
43188
+ /** 载荷完整性摘要:sha256(JSON.stringify({index, stats})),写入时计算、加载时重算。 */
43189
+ payloadDigest: z21.string().regex(/^[a-f0-9]{64}$/),
43190
+ index: z21.unknown(),
43191
+ stats: z21.record(SkillIdSchema, StatEnvelopeSchema)
43192
+ }).strict();
43193
+ var StoredProjectionSchema = z21.object({
43194
+ name: z21.string(),
43195
+ description: z21.string(),
43196
+ keywords: z21.array(z21.string()),
43197
+ canonicalPath: z21.string().min(1),
43198
+ installations: z21.array(SkillInstallationSchema).min(1),
43199
+ contentHash: z21.string().regex(/^[a-f0-9]{64}$/),
43200
+ disabled: z21.boolean(),
43201
+ conflict: z21.boolean(),
43202
+ invalidFrontmatter: z21.boolean()
43203
+ });
43204
+ function createSearchMiniSearch(tokenizer) {
43205
+ return new MiniSearch(miniSearchOptions(tokenizer));
43206
+ }
43207
+ function searchMiniSearchInstance(miniSearch, query) {
43208
+ const results = miniSearch.search(query, {
43209
+ boost: FIELD_BOOST,
43210
+ prefix: SEARCH_PREFIX,
43211
+ fuzzy: SEARCH_FUZZY
43212
+ });
43213
+ const candidates = [];
43214
+ for (const result of results) {
43215
+ const id = SkillIdSchema.safeParse(result.id);
43216
+ if (!id.success) continue;
43217
+ const stored = miniSearch.getStoredFields(result.id);
43218
+ const projection = stored ? safeParseExternal(StoredProjectionSchema, stored) : null;
43219
+ if (!projection) continue;
43220
+ candidates.push({
43221
+ id: id.data,
43222
+ bm25: result.score,
43223
+ ...projection
43224
+ });
43225
+ }
43226
+ return candidates;
43227
+ }
43228
+ function miniSearchOptions(tokenizer) {
43229
+ return {
43230
+ fields: [...SEARCH_FIELDS],
43231
+ storeFields: [...STORE_FIELDS],
43232
+ tokenize: (text) => tokenizer.tokenize(text),
43233
+ processTerm: (term) => term,
43234
+ stringifyField: (value) => Array.isArray(value) ? value.join(" ") : String(value),
43235
+ autoVacuum: false
43236
+ };
43237
+ }
43238
+ function createSkillSearchIndex(tokenizer = createSkillTokenizer(), getSearchConfigDigest = () => UNCONFIGURED_CONFIG_DIGEST) {
43239
+ return new MiniSearchSkillSearchIndex(tokenizer, getSearchConfigDigest);
43240
+ }
43241
+ var UNCONFIGURED_CONFIG_DIGEST = createHash7("sha256").digest("hex");
43242
+ var MiniSearchSkillSearchIndex = class {
43243
+ constructor(tokenizer, getSearchConfigDigest) {
43244
+ this.tokenizer = tokenizer;
43245
+ this.getSearchConfigDigest = getSearchConfigDigest;
43246
+ this.miniSearch = createSearchMiniSearch(tokenizer);
43247
+ }
43248
+ tokenizer;
43249
+ getSearchConfigDigest;
43250
+ miniSearch;
43251
+ stats = /* @__PURE__ */ new Map();
43252
+ loaded = false;
43253
+ freshen(scans, readDocument) {
43254
+ if (!this.loaded) {
43255
+ const load3 = this.loadFromDisk();
43256
+ this.loaded = true;
43257
+ if (load3.kind !== "ok") {
43258
+ return this.rebuild(scans, readDocument, load3.reason);
43259
+ }
43260
+ this.miniSearch = load3.miniSearch;
43261
+ this.stats = load3.stats;
43262
+ }
43263
+ const scanById = new Map(scans.map((scan2) => [scan2.id, scan2]));
43264
+ const indexedIds = new Set(this.stats.keys());
43265
+ const removed = [...indexedIds].filter((id) => !scanById.has(id));
43266
+ const changed = [];
43267
+ for (const scan2 of scans) {
43268
+ const stat2 = this.stats.get(scan2.id);
43269
+ if (!stat2 || !statIsCurrent(stat2, scan2)) changed.push(scan2);
43270
+ }
43271
+ const discardOperations = removed.length + changed.filter((scan2) => indexedIds.has(scan2.id)).length;
43272
+ const dirtyRatio = indexedIds.size === 0 ? 1 : discardOperations / indexedIds.size;
43273
+ if (dirtyRatio > REBUILD_DISCARD_RATIO) {
43274
+ return this.rebuild(scans, readDocument, "dirty-ratio");
43275
+ }
43276
+ if (removed.length === 0 && changed.length === 0) {
43277
+ return { mode: "fresh", documents: this.stats.size, parsed: 0, discarded: 0 };
43278
+ }
43279
+ const documents = changed.map((scan2) => ({ scan: scan2, document: readDocument(scan2) }));
43280
+ const snapshotIndex = this.miniSearch.toJSON();
43281
+ const snapshotStats = new Map(this.stats);
43282
+ let parsed = 0;
43283
+ try {
43284
+ for (const id of removed) {
43285
+ this.discardIndexed(id);
43286
+ this.stats.delete(id);
43287
+ }
43288
+ for (const { scan: scan2, document: document2 } of documents) {
43289
+ if (indexedIds.has(scan2.id)) this.discardIndexed(scan2.id);
43290
+ this.miniSearch.add(document2);
43291
+ this.stats.set(scan2.id, statEntry(scan2, document2));
43292
+ parsed += 1;
43293
+ }
43294
+ this.save();
43295
+ } catch (error) {
43296
+ this.restoreSnapshot(snapshotIndex, snapshotStats);
43297
+ if (error instanceof SkillSearchIndexError) this.invalidateAfterSaveFailure();
43298
+ throw error;
43299
+ }
43300
+ return {
43301
+ mode: "incremental",
43302
+ documents: this.stats.size,
43303
+ parsed,
43304
+ discarded: discardOperations
43305
+ };
43306
+ }
43307
+ search(query) {
43308
+ return searchMiniSearchInstance(this.miniSearch, query);
43309
+ }
43310
+ duplicates() {
43311
+ const byHash = /* @__PURE__ */ new Map();
43312
+ for (const [id, stat2] of this.stats) {
43313
+ const group = byHash.get(stat2.contentHash) ?? [];
43314
+ group.push({ id, stat: stat2 });
43315
+ byHash.set(stat2.contentHash, group);
43316
+ }
43317
+ const groups = [];
43318
+ for (const [contentHash, entries] of byHash) {
43319
+ if (entries.length < 2) continue;
43320
+ entries.sort(
43321
+ (left, right) => compareCanonicalPath(left.stat.canonicalPath, right.stat.canonicalPath)
43322
+ );
43323
+ const members = [];
43324
+ for (const { id, stat: stat2 } of entries) {
43325
+ const stored = this.miniSearch.getStoredFields(id);
43326
+ const name = typeof stored?.name === "string" ? stored.name : null;
43327
+ if (name === null) continue;
43328
+ members.push({
43329
+ id,
43330
+ name,
43331
+ canonicalPath: stat2.canonicalPath,
43332
+ installations: stat2.installations,
43333
+ disabled: stat2.disabled,
43334
+ conflict: stat2.conflict
43335
+ });
43336
+ }
43337
+ if (members.length >= 2) groups.push({ contentHash, members });
43338
+ }
43339
+ groups.sort(
43340
+ (left, right) => compareCanonicalPath(left.members[0].canonicalPath, right.members[0].canonicalPath)
43341
+ );
43342
+ return groups;
43343
+ }
43344
+ documentCount() {
43345
+ return this.miniSearch.documentCount;
43346
+ }
43347
+ rebuild(scans, readDocument, reason) {
43348
+ const discarded = this.stats.size;
43349
+ const miniSearch = createSearchMiniSearch(this.tokenizer);
43350
+ const stats = /* @__PURE__ */ new Map();
43351
+ for (const scan2 of scans) {
43352
+ const document2 = readDocument(scan2);
43353
+ miniSearch.add(document2);
43354
+ stats.set(scan2.id, statEntry(scan2, document2));
43355
+ }
43356
+ const envelopeIndex = miniSearch.toJSON();
43357
+ const envelopeStats = Object.fromEntries(stats);
43358
+ try {
43359
+ this.persist(envelopeIndex, envelopeStats);
43360
+ } catch (error) {
43361
+ this.invalidateAfterSaveFailure();
43362
+ throw error;
43363
+ }
43364
+ this.miniSearch = miniSearch;
43365
+ this.stats = stats;
43366
+ return {
43367
+ mode: "rebuilt",
43368
+ documents: stats.size,
43369
+ parsed: scans.length,
43370
+ discarded,
43371
+ rebuildReason: reason
43372
+ };
43373
+ }
43374
+ /** 落盘失败后清空不可持久化的内存快照,下一次调用必须重新读取磁盘真相。 */
43375
+ invalidateAfterSaveFailure() {
43376
+ this.loaded = false;
43377
+ this.miniSearch = createSearchMiniSearch(this.tokenizer);
43378
+ this.stats = /* @__PURE__ */ new Map();
43379
+ }
43380
+ /** 增量变更阶段失败后恢复本轮开始前的完整成员状态(索引原子一致性)。 */
43381
+ restoreSnapshot(snapshotIndex, snapshotStats) {
43382
+ try {
43383
+ this.miniSearch = MiniSearch.loadJSON(
43384
+ JSON.stringify(snapshotIndex),
43385
+ miniSearchOptions(this.tokenizer)
43386
+ );
43387
+ this.stats = snapshotStats;
43388
+ } catch {
43389
+ this.invalidateAfterSaveFailure();
43390
+ }
43391
+ }
43392
+ discardIndexed(id) {
43393
+ if (this.miniSearch.has(id)) this.miniSearch.discard(id);
43394
+ }
43395
+ loadFromDisk() {
43396
+ const file = path15.join(appDir(), SEARCH_INDEX_FILE);
43397
+ let source;
43398
+ try {
43399
+ source = fs12.readFileSync(file, "utf8");
43400
+ } catch (error) {
43401
+ if (error instanceof Error && error.code === "ENOENT") {
43402
+ return { kind: "empty", reason: "missing" };
43403
+ }
43404
+ throw new SkillSearchIndexError(
43405
+ `Cannot read the skill search index ${file}: ${error instanceof Error ? error.message : String(error)}`,
43406
+ { cause: error }
43407
+ );
43408
+ }
43409
+ const envelope = safeParseJson(source, IndexEnvelopeSchema);
43410
+ if (!envelope) return { kind: "empty", reason: "corrupt" };
43411
+ if (payloadDigest(envelope.index, envelope.stats) !== envelope.payloadDigest) {
43412
+ return { kind: "empty", reason: "corrupt" };
43413
+ }
43414
+ if (envelope.tokenizerVersion !== TOKENIZER_VERSION || envelope.parserVersion !== PARSER_VERSION || envelope.rankingVersion !== RANKING_VERSION || envelope.engine.name !== ENGINE_NAME || envelope.engine.version !== minisearchRuntimeVersion() || envelope.engine.configDigest !== ENGINE_CONFIG_DIGEST || envelope.searchConfigDigest !== this.getSearchConfigDigest()) {
43415
+ return { kind: "empty", reason: "incompatible" };
43416
+ }
43417
+ try {
43418
+ const serialized = safeParseExternal(
43419
+ z21.object({
43420
+ documentIds: z21.record(z21.string(), z21.string()),
43421
+ storedFields: z21.record(z21.string(), z21.unknown())
43422
+ }).passthrough(),
43423
+ envelope.index
43424
+ );
43425
+ if (!serialized) return { kind: "empty", reason: "corrupt" };
43426
+ const shortIds = Object.keys(serialized.documentIds);
43427
+ const activeIdValues = Object.values(serialized.documentIds);
43428
+ const activeIds = new Set(activeIdValues);
43429
+ const statIds = new Set(Object.keys(envelope.stats));
43430
+ const storedFieldIds = Object.keys(serialized.storedFields);
43431
+ if (activeIds.size !== activeIdValues.length || activeIds.size !== statIds.size || activeIdValues.some((id) => !statIds.has(id)) || storedFieldIds.length !== shortIds.length || shortIds.some((shortId) => !Object.hasOwn(serialized.storedFields, shortId))) {
43432
+ return { kind: "empty", reason: "corrupt" };
43433
+ }
43434
+ const miniSearch = MiniSearch.loadJSON(
43435
+ JSON.stringify(envelope.index),
43436
+ miniSearchOptions(this.tokenizer)
43437
+ );
43438
+ for (const [id, stat2] of Object.entries(envelope.stats)) {
43439
+ if (!miniSearch.has(id)) return { kind: "empty", reason: "corrupt" };
43440
+ const stored = miniSearch.getStoredFields(id);
43441
+ const projection = stored ? safeParseExternal(StoredProjectionSchema, stored) : null;
43442
+ if (!projection || projection.canonicalPath !== stat2.canonicalPath || projection.contentHash !== stat2.contentHash || projection.disabled !== stat2.disabled || projection.conflict !== stat2.conflict || projection.invalidFrontmatter !== stat2.invalidFrontmatter || JSON.stringify(projection.installations) !== JSON.stringify(stat2.installations)) {
43443
+ return { kind: "empty", reason: "corrupt" };
43444
+ }
43445
+ }
43446
+ return { kind: "ok", miniSearch, stats: new Map(Object.entries(envelope.stats)) };
43447
+ } catch {
43448
+ return { kind: "empty", reason: "corrupt" };
43449
+ }
43450
+ }
43451
+ save() {
43452
+ this.persist(this.miniSearch.toJSON(), Object.fromEntries(this.stats));
43453
+ }
43454
+ /** 组装五版本信封并原子落盘;载荷摘要随写计算。 */
43455
+ persist(index, stats) {
43456
+ const file = path15.join(appDir(), SEARCH_INDEX_FILE);
43457
+ const envelope = {
43458
+ schemaVersion: SCHEMA_VERSION,
43459
+ tokenizerVersion: TOKENIZER_VERSION,
43460
+ parserVersion: PARSER_VERSION,
43461
+ rankingVersion: RANKING_VERSION,
43462
+ engine: {
43463
+ name: ENGINE_NAME,
43464
+ version: minisearchRuntimeVersion(),
43465
+ configDigest: ENGINE_CONFIG_DIGEST
43466
+ },
43467
+ searchConfigDigest: this.getSearchConfigDigest(),
43468
+ payloadDigest: payloadDigest(index, stats),
43469
+ index,
43470
+ stats
43471
+ };
43472
+ try {
43473
+ atomicWriteUtf8(file, JSON.stringify(envelope));
43474
+ } catch (error) {
43475
+ throw new SkillSearchIndexError(
43476
+ `Cannot persist the skill search index ${file}: ${error instanceof Error ? error.message : String(error)}`,
43477
+ { cause: error }
43478
+ );
43479
+ }
43480
+ }
43481
+ };
43482
+ function compareCanonicalPath(left, right) {
43483
+ return left < right ? -1 : left > right ? 1 : 0;
43484
+ }
43485
+ function payloadDigest(index, stats) {
43486
+ return createHash7("sha256").update(JSON.stringify({ index, stats })).digest("hex");
43487
+ }
43488
+ function statIsCurrent(stat2, scan2) {
43489
+ if (stat2.canonicalPath !== scan2.canonicalPath || stat2.disabled !== scan2.disabled || stat2.conflict !== scan2.conflict || stat2.files.length !== scan2.files.length) {
43490
+ return false;
43491
+ }
43492
+ for (let index = 0; index < stat2.files.length; index += 1) {
43493
+ const indexed = stat2.files[index];
43494
+ const candidate = scan2.files[index];
43495
+ if (indexed.path !== candidate.path || indexed.mtimeMs !== candidate.mtimeMs || indexed.size !== candidate.size || indexed.ino !== candidate.ino || indexed.ctimeMs !== candidate.ctimeMs) {
43496
+ return false;
43497
+ }
43498
+ }
43499
+ return stat2.installations.length === scan2.installations.length && stat2.installations.every((installation, index) => {
43500
+ const candidate = scan2.installations[index];
43501
+ return installation.path === candidate.path && installation.workspaceId === candidate.workspaceId && installation.providerId === candidate.providerId;
43502
+ });
43503
+ }
43504
+ function statEntry(scan2, document2) {
43505
+ return {
43506
+ canonicalPath: scan2.canonicalPath,
43507
+ files: scan2.files,
43508
+ installations: document2.installations,
43509
+ contentHash: document2.contentHash,
43510
+ disabled: document2.disabled,
43511
+ conflict: document2.conflict,
43512
+ invalidFrontmatter: document2.invalidFrontmatter
43513
+ };
43514
+ }
43515
+ var cachedRuntimeVersion = null;
43516
+ function minisearchRuntimeVersion() {
43517
+ if (cachedRuntimeVersion) return cachedRuntimeVersion;
43518
+ try {
43519
+ const require2 = createRequire4(import.meta.url);
43520
+ const entry = require2.resolve("minisearch");
43521
+ let directory = path15.dirname(entry);
43522
+ for (let hop = 0; hop < 4; hop += 1) {
43523
+ const packageFile = path15.join(directory, "package.json");
43524
+ if (fs12.existsSync(packageFile)) {
43525
+ const version = safeParseJson(
43526
+ fs12.readFileSync(packageFile, "utf8"),
43527
+ z21.object({ version: z21.string() })
43528
+ )?.version;
43529
+ cachedRuntimeVersion = version ?? "unresolved";
43530
+ return cachedRuntimeVersion;
43531
+ }
43532
+ directory = path15.dirname(directory);
43533
+ }
43534
+ cachedRuntimeVersion = "unresolved";
43535
+ } catch {
43536
+ cachedRuntimeVersion = "unresolved";
43537
+ }
43538
+ return cachedRuntimeVersion;
43539
+ }
43540
+
43541
+ // src/daemon/skill-search/config.ts
43542
+ var SEARCH_CONFIG_FILE = "search-config.toml";
43543
+ var TEMPLATE_EXCLUDE_DIRS = [
43544
+ ".cargo",
43545
+ ".cache",
43546
+ ".npm",
43547
+ ".pnpm-store",
43548
+ ".bun",
43549
+ ".rustup",
43550
+ ".local"
43551
+ ];
43552
+ var SearchConfigSchema = z22.object({
43553
+ excludeDirs: z22.array(z22.string().min(1)).default([])
43554
+ }).strict();
43555
+ function searchConfigPath() {
43556
+ return path16.join(appDir(), SEARCH_CONFIG_FILE);
43557
+ }
43558
+ function searchConfigTemplate() {
43559
+ return `# Skill search content configuration (server-owned).
43560
+ # \u6280\u80FD\u6B63\u6587\u7D22\u5F15\u4F1A\u6536\u96C6\u6280\u80FD\u76EE\u5F55\u5185\u7684\u5176\u4F59 *.md\uFF1B\u4E0B\u5217\u76EE\u5F55\u540D\u4E0D\u4F1A\u8FDB\u5165\u6536\u96C6
43561
+ # \uFF08\u5728\u5185\u7F6E\u6E05\u5355\u4E4B\u4E0A\u8FFD\u52A0\uFF0C\u53EA\u80FD\u589E\u52A0\u6392\u9664\uFF1B\u4EE5 "." \u5F00\u5934\u7684\u76EE\u5F55\u59CB\u7EC8\u5168\u8DF3\uFF0C
43562
+ # \u5185\u7F6E\u6E05\u5355\u4E0D\u53EF\u79FB\u9664\uFF09\u3002\u4FEE\u6539\u540E\u4E0B\u4E00\u6B21\u7D22\u5F15\u7EF4\u62A4\u4F1A\u81EA\u52A8\u5168\u91CF\u91CD\u5EFA\u3002
43563
+ excludeDirs = [
43564
+ ${TEMPLATE_EXCLUDE_DIRS.map((dir) => `"${dir}"`).join(",\n ")},
43565
+ ]
43566
+ `;
43567
+ }
43568
+ function loadSkillSearchConfig() {
43569
+ const file = searchConfigPath();
43570
+ let source;
43571
+ try {
43572
+ source = fs13.readFileSync(file, "utf8");
43573
+ } catch (error) {
43574
+ const code = error instanceof Error ? error.code : void 0;
43575
+ if (code === "ENOENT") {
43576
+ writeTemplate(file);
43577
+ return effectiveConfig(TEMPLATE_EXCLUDE_DIRS);
43578
+ }
43579
+ throw new SkillSearchIndexError(
43580
+ `Cannot read the skill search config ${file}: ${error instanceof Error ? error.message : String(error)}`,
43581
+ { cause: error }
43582
+ );
43583
+ }
43584
+ let raw;
43585
+ try {
43586
+ raw = parseToml(source);
43587
+ } catch {
43588
+ return effectiveConfig([]);
43589
+ }
43590
+ const parsed = safeParseExternal(SearchConfigSchema, raw);
43591
+ return effectiveConfig(parsed ? parsed.excludeDirs : []);
43592
+ }
43593
+ function effectiveConfig(extraDirs) {
43594
+ const merged = [.../* @__PURE__ */ new Set([...BUILTIN_EXCLUDED_DIRS, ...extraDirs])].sort();
43595
+ return { excludedDirs: merged, configDigest: digestOf(merged) };
43596
+ }
43597
+ function digestOf(dirs) {
43598
+ return createHash8("sha256").update(dirs.join("\n")).digest("hex");
43599
+ }
43600
+ function writeTemplate(file) {
43601
+ try {
43602
+ atomicWriteUtf8(file, searchConfigTemplate());
43603
+ } catch (error) {
43604
+ throw new SkillSearchIndexError(
43605
+ `Cannot write the skill search config template ${file}: ${error instanceof Error ? error.message : String(error)}`,
43606
+ { cause: error }
43607
+ );
43608
+ }
43609
+ }
43610
+
43611
+ // src/daemon/skill-search/scanner.ts
43612
+ import fs14 from "node:fs";
43613
+ import path17 from "node:path";
43614
+ var MAX_DEPTH2 = 2;
43615
+ var SKILL_FILE_NAMES = ["SKILL.md", ".SKILL.md"];
43616
+ function scanSkillRoots(roots) {
43617
+ const entries = [];
43618
+ for (const root of roots) {
43619
+ scanDirectory(root, root.rootPath, 1, entries);
43620
+ }
43621
+ entries.sort(
43622
+ (left, right) => left.path === right.path ? compareString3(left.workspaceId, right.workspaceId) || compareString3(left.providerId, right.providerId) : compareString3(left.path, right.path)
43623
+ );
43624
+ return entries;
43625
+ }
43626
+ function compareString3(left, right) {
43627
+ return left < right ? -1 : left > right ? 1 : 0;
43628
+ }
43629
+ function scanDirectory(root, directory, depth, entries) {
43630
+ let dirents;
43631
+ try {
43632
+ dirents = fs14.readdirSync(directory, { withFileTypes: true });
43633
+ } catch {
43634
+ return;
43635
+ }
43636
+ dirents.sort((left, right) => compareString3(left.name, right.name));
43637
+ for (const dirent of dirents) {
43638
+ if (dirent.name.startsWith(".") || dirent.name === "node_modules") continue;
43639
+ if (!dirent.isDirectory() && !dirent.isSymbolicLink()) continue;
43640
+ if (depth > 1 && !dirent.isDirectory()) continue;
43641
+ const entryPath = path17.join(directory, dirent.name);
43642
+ let stat2;
43643
+ try {
43644
+ stat2 = fs14.statSync(entryPath);
43645
+ } catch {
43646
+ continue;
43647
+ }
43648
+ if (!stat2.isDirectory()) continue;
43649
+ if (hasSkillFile(entryPath)) {
43650
+ entries.push({ path: entryPath, workspaceId: root.workspaceId, providerId: root.providerId });
43651
+ }
43652
+ if (dirent.isDirectory() && depth < MAX_DEPTH2) {
43653
+ scanDirectory(root, entryPath, depth + 1, entries);
43654
+ }
43655
+ }
43656
+ }
43657
+ function hasSkillFile(entryPath) {
43658
+ return SKILL_FILE_NAMES.some((name) => isRegularFile3(path17.join(entryPath, name)));
43659
+ }
43660
+ function isRegularFile3(file) {
43661
+ try {
43662
+ return fs14.lstatSync(file).isFile();
43663
+ } catch {
43664
+ return false;
43665
+ }
43666
+ }
43667
+
43668
+ // src/daemon/skill-search/watcher.ts
43669
+ import fs15 from "node:fs";
43670
+ var FLUSH_DEBOUNCE_MS = 300;
43671
+ var SYNCHRONOUS_FLUSH_DOCUMENT_LIMIT = 2e4;
43672
+ var fsWatchFactory = (directory) => {
43673
+ const watcher = fs15.watch(directory, { recursive: true, persistent: false });
43674
+ return {
43675
+ close: () => watcher.close(),
43676
+ onEvent: (callback) => watcher.on("change", callback),
43677
+ onError: (callback) => watcher.on("error", callback)
43678
+ };
43679
+ };
43680
+ function createSkillSearchWatcher(options2) {
43681
+ const watchFactory = options2.watch ?? fsWatchFactory;
43682
+ const watched = /* @__PURE__ */ new Map();
43683
+ let dirty = false;
43684
+ let disposed = false;
43685
+ let flushTimer = null;
43686
+ function markDirty() {
43687
+ if (disposed) return;
43688
+ dirty = true;
43689
+ if (flushTimer !== null) return;
43690
+ flushTimer = setTimeout(() => {
43691
+ flushTimer = null;
43692
+ if (disposed || !dirty) return;
43693
+ if (options2.documentCount() > SYNCHRONOUS_FLUSH_DOCUMENT_LIMIT) return;
43694
+ try {
43695
+ options2.onFlush();
43696
+ dirty = false;
43697
+ } catch {
43698
+ }
43699
+ }, FLUSH_DEBOUNCE_MS);
43700
+ flushTimer.unref?.();
43701
+ }
43702
+ return {
43703
+ reconcile(watchDirs) {
43704
+ if (disposed) return;
43705
+ const desired = new Set(watchDirs);
43706
+ for (const [dir, entry] of watched) {
43707
+ if (!desired.has(dir)) {
43708
+ watched.delete(dir);
43709
+ closeQuietly(entry);
43710
+ }
43711
+ }
43712
+ for (const dir of desired) {
43713
+ if (watched.has(dir)) continue;
43714
+ try {
43715
+ const handle = watchFactory(dir);
43716
+ handle.onEvent(() => markDirty());
43717
+ handle.onError(() => {
43718
+ const entry = watched.get(dir);
43719
+ if (entry) {
43720
+ watched.delete(dir);
43721
+ closeQuietly(entry);
43722
+ }
43723
+ markDirty();
43724
+ });
43725
+ watched.set(dir, { handle, closed: false });
43726
+ } catch {
43727
+ }
43728
+ }
43729
+ },
43730
+ isClean(watchDirs) {
43731
+ if (dirty) return false;
43732
+ return watchDirs.every((dir) => watched.has(dir));
43733
+ },
43734
+ dispose() {
43735
+ disposed = true;
43736
+ if (flushTimer !== null) {
43737
+ clearTimeout(flushTimer);
43738
+ flushTimer = null;
43739
+ }
43740
+ for (const entry of watched.values()) closeQuietly(entry);
43741
+ watched.clear();
43742
+ dirty = false;
43743
+ },
43744
+ watchedCount() {
43745
+ return watched.size;
43746
+ }
43747
+ };
43748
+ }
43749
+ function closeQuietly(entry) {
43750
+ if (entry.closed) return;
43751
+ entry.closed = true;
43752
+ try {
43753
+ entry.handle.close();
43754
+ } catch {
43755
+ }
43756
+ }
43757
+
43758
+ // src/daemon/skill-search/service.ts
43759
+ var SkillSearchDocumentReadError = class extends DomainError {
43760
+ constructor(message, options2) {
43761
+ super("UNAVAILABLE", message, options2);
43762
+ this.name = "SkillSearchDocumentReadError";
43763
+ }
43764
+ };
43765
+ function createSkillSearchService() {
43766
+ return createSkillSearchEngine(defaultResolveRoots(createWorkspaceRegistryPersistence()));
43767
+ }
43768
+ function createSkillSearchEngine(resolveRoots) {
43769
+ const tokenizer = createSkillTokenizer();
43770
+ let config = null;
43771
+ const index = createSkillSearchIndex(tokenizer, () => config?.configDigest ?? "");
43772
+ try {
43773
+ config = loadSkillSearchConfig();
43774
+ } catch (error) {
43775
+ console.warn(
43776
+ `[skill-search] config priming failed: ${error instanceof Error ? error.message : String(error)}`
43777
+ );
43778
+ }
43779
+ let cachedWatchDirs = [];
43780
+ let cachedRootsKey = null;
43781
+ function maintain() {
43782
+ const activeConfig = loadSkillSearchConfig();
43783
+ config = activeConfig;
43784
+ const roots = resolveRoots();
43785
+ const candidates = scanSkillRoots(roots);
43786
+ const excluded = new Set(activeConfig.excludedDirs);
43787
+ const scans = canonicalizeCandidates(candidates, excluded);
43788
+ index.freshen(scans, readSkillSearchDocument);
43789
+ cachedWatchDirs = canonicalWatchDirs(roots);
43790
+ cachedRootsKey = rootsKeyOf(roots);
43791
+ watcher.reconcile(cachedWatchDirs);
43792
+ }
43793
+ const watcher = createSkillSearchWatcher({
43794
+ onFlush: () => maintain(),
43795
+ documentCount: () => index.documentCount()
43796
+ });
43797
+ function ensureMaintained() {
43798
+ const roots = resolveRoots();
43799
+ const rootsChanged = cachedRootsKey !== rootsKeyOf(roots);
43800
+ const configChanged = config === null || loadSkillSearchConfig().configDigest !== config.configDigest;
43801
+ if (rootsChanged || configChanged || !watcher.isClean(cachedWatchDirs)) {
43802
+ maintain();
43803
+ }
43804
+ }
43805
+ return {
43806
+ /** 检索本地 skills;query 为空字符串时返回空结果(CLI 层负责把空 query 判为用法错误)。 */
43807
+ search: async (query, searchOptions) => {
43808
+ const parsedOptions = SkillSearchOptionsSchema.parse(searchOptions ?? {});
43809
+ if (query.trim() === "") return [];
43810
+ ensureMaintained();
43811
+ const hits = index.search(query);
43812
+ return rankResults(hits, query, parsedOptions.limit, (text) => tokenizer.tokenize(text));
43813
+ },
43814
+ /**
43815
+ * 内容重复组投影(与 search 共用维护门:clean + 配置未变时零扫描直读内存)。
43816
+ */
43817
+ duplicates: async () => {
43818
+ ensureMaintained();
43819
+ return index.duplicates().map((group) => ({
43820
+ contentHash: group.contentHash,
43821
+ members: group.members.map((member) => ({
43822
+ ...member,
43823
+ id: SkillIdSchema.parse(member.id)
43824
+ }))
43825
+ }));
43826
+ },
43827
+ /** watcher 回收(daemon stop coordinator 接线;幂等)。 */
43828
+ dispose: () => {
43829
+ watcher.dispose();
43830
+ }
43831
+ };
43832
+ }
43833
+ function canonicalWatchDirs(roots) {
43834
+ const dirs = /* @__PURE__ */ new Set();
43835
+ for (const root of roots) {
43836
+ try {
43837
+ dirs.add(fs16.realpathSync(root.rootPath));
43838
+ } catch {
43839
+ }
43840
+ }
43841
+ return [...dirs].sort();
43842
+ }
43843
+ function rootsKeyOf(roots) {
43844
+ return roots.map((root) => `${root.workspaceId}:${root.providerId}:${root.rootPath}`).sort().join("|");
43845
+ }
43846
+ function readSkillSearchDocument(scan2) {
43847
+ const sourceIdentity = scan2.files.find((file) => file.path === scan2.sourcePath);
43848
+ if (!sourceIdentity) {
43849
+ throw new SkillSearchDocumentReadError(
43850
+ `The scanned skill identity source is missing from the file set: ${scan2.sourcePath}`
43851
+ );
43852
+ }
43853
+ const extras = [];
43854
+ for (const file of scan2.files) {
43855
+ if (file.path === scan2.sourcePath) continue;
43856
+ extras.push({ path: file.path, raw: readFileWithIdentity(file.path, file) });
43857
+ }
43858
+ const sourceRaw = readFileWithIdentity(scan2.sourcePath, sourceIdentity);
43859
+ const parsed = parseSkillDocumentSet(sourceRaw, extras, path18.basename(scan2.canonicalPath));
43860
+ return {
43861
+ id: scan2.id,
43862
+ name: parsed.name,
43863
+ description: parsed.description,
43864
+ keywords: parsed.keywords,
43865
+ triggers: parsed.triggers,
43866
+ headings: parsed.headings,
43867
+ body: parsed.body,
43868
+ canonicalPath: scan2.canonicalPath,
43869
+ installations: scan2.installations,
43870
+ contentHash: parsed.contentHash,
43871
+ disabled: scan2.disabled,
43872
+ conflict: scan2.conflict,
43873
+ invalidFrontmatter: parsed.invalidFrontmatter
43874
+ };
43875
+ }
43876
+ function readFileWithIdentity(filePath, identity2) {
43877
+ let fd = null;
43878
+ try {
43879
+ try {
43880
+ const noFollow = process.platform === "win32" ? 0 : fs16.constants.O_NOFOLLOW;
43881
+ fd = fs16.openSync(filePath, fs16.constants.O_RDONLY | noFollow);
43882
+ } catch (error) {
43883
+ throw new SkillSearchDocumentReadError(
43884
+ `Cannot open the scanned skill file ${filePath} without following a symlink.`,
43885
+ { cause: error }
43886
+ );
43887
+ }
43888
+ const descriptorStat = fs16.fstatSync(fd);
43889
+ if (!descriptorStat.isFile() || descriptorStat.ino !== identity2.ino || descriptorStat.size !== identity2.size) {
43890
+ throw new SkillSearchDocumentReadError(
43891
+ `The scanned skill file identity changed before read: ${filePath}`
43892
+ );
43893
+ }
43894
+ return fs16.readFileSync(fd);
43895
+ } catch (error) {
43896
+ if (error instanceof SkillSearchDocumentReadError) throw error;
43897
+ throw new SkillSearchDocumentReadError(`Cannot read the scanned skill file ${filePath}.`, {
43898
+ cause: error
43899
+ });
43900
+ } finally {
43901
+ if (fd !== null) fs16.closeSync(fd);
43902
+ }
43903
+ }
43904
+ function defaultResolveRoots(stateSource) {
43905
+ return () => {
43906
+ const roots = [];
43907
+ for (const provider of PROVIDER_CATALOG) {
43908
+ const rootPath = globalProviderRoot(provider);
43909
+ if (rootPath === null) continue;
43910
+ roots.push({
43911
+ rootPath,
43912
+ workspaceId: GLOBAL_WORKSPACE_ID,
43913
+ providerId: ProviderIdSchema.parse(provider.id)
43914
+ });
43915
+ }
43916
+ const state = stateSource.load();
43917
+ for (const workspace of state.workspaces) {
43918
+ for (const provider of PROVIDER_CATALOG) {
43919
+ roots.push({
43920
+ rootPath: importedProviderRoot(workspace.path, provider),
43921
+ workspaceId: workspace.id,
43922
+ providerId: ProviderIdSchema.parse(provider.id)
43923
+ });
43924
+ }
43925
+ }
43926
+ return roots;
43927
+ };
43928
+ }
43929
+
43930
+ // src/daemon/search-config-opener.ts
43931
+ import { spawn as spawn5 } from "node:child_process";
43932
+ function platformOpenFile(filePath) {
43933
+ const platform2 = process.platform;
43934
+ if (platform2 === "win32") {
43935
+ return spawnDetached({ file: "cmd", args: ["/c", "start", "", filePath] });
43936
+ }
43937
+ const command = platform2 === "darwin" ? { file: "open", args: [filePath] } : { file: "xdg-open", args: [filePath] };
43938
+ return spawnDetached(command, { checkExitCode: true });
43939
+ }
43940
+ function spawnDetached(command, options2 = {}) {
43941
+ const label = `${command.file} ${command.args.join(" ")}`;
43942
+ return new Promise((resolve2, reject) => {
43943
+ let child;
43944
+ try {
43945
+ child = spawn5(command.file, command.args, { detached: true, stdio: "ignore" });
43946
+ } catch (error) {
43947
+ reject(openerError(label, error));
43948
+ return;
43949
+ }
43950
+ child.on("error", (error) => reject(openerError(label, error)));
43951
+ child.unref();
43952
+ if (!options2.checkExitCode) {
43953
+ resolve2();
43954
+ return;
43955
+ }
43956
+ child.on("exit", (code) => {
43957
+ if (code !== null && code !== 0) {
43958
+ reject(
43959
+ new DomainError(
43960
+ "UNAVAILABLE",
43961
+ `Cannot open the search config with ${label}: exited with code ${code} (no default application?)`
43962
+ )
43963
+ );
43964
+ } else {
43965
+ resolve2();
43966
+ }
43967
+ });
43968
+ });
43969
+ }
43970
+ function openerError(label, cause) {
43971
+ return new DomainError(
43972
+ "UNAVAILABLE",
43973
+ `Cannot open the search config with ${label}: ${cause instanceof Error ? cause.message : String(cause)}`,
43974
+ { cause }
43975
+ );
43976
+ }
43977
+
43978
+ // src/daemon/dialog-service.ts
43979
+ async function attachExtDialog(tray) {
43980
+ const ext = await import("@opentray/ext-dialog");
43981
+ return ext.attachDialog(tray);
43982
+ }
43983
+ function createDialogService(options2 = {}) {
43984
+ const attachDialog = options2.attachDialog ?? attachExtDialog;
43985
+ let picker = null;
43986
+ return {
43987
+ attach(tray) {
43988
+ if (picker !== null) return;
43989
+ try {
43990
+ void Promise.resolve(attachDialog(tray)).then((attached) => {
43991
+ picker = attached;
43992
+ }).catch(() => void 0);
43993
+ } catch {
43994
+ }
43995
+ },
43996
+ async pickDirectory() {
43997
+ if (picker === null) return { supported: false, path: null };
43998
+ try {
43999
+ return { supported: true, path: await picker.pickDirectory() };
44000
+ } catch (error) {
44001
+ const code = error.code;
44002
+ if (code === "dialog_platform_unsupported") {
44003
+ return { supported: false, path: null };
44004
+ }
44005
+ throw new DomainError(
44006
+ "UNAVAILABLE",
44007
+ "The native directory picker could not be presented.",
44008
+ { cause: error }
44009
+ );
44010
+ }
44011
+ }
44012
+ };
44013
+ }
44014
+
42359
44015
  // src/daemon/skill-intelligence-service.ts
42360
44016
  import { randomBytes as randomBytes5 } from "node:crypto";
42361
44017
 
@@ -42621,7 +44277,7 @@ function analyzeDocuments(documents) {
42621
44277
  }
42622
44278
  }
42623
44279
  const sharedPaths = left.referencedPaths.filter(
42624
- (path33) => right.referencedPaths.includes(path33)
44280
+ (path39) => right.referencedPaths.includes(path39)
42625
44281
  );
42626
44282
  if (sharedPaths.length > 0) {
42627
44283
  const id = findingId("shared-resource-path", [left.skillId, right.skillId]);
@@ -42635,9 +44291,9 @@ function analyzeDocuments(documents) {
42635
44291
  [left.skillId]: left.revision,
42636
44292
  [right.skillId]: right.revision
42637
44293
  },
42638
- evidence: sharedPaths.slice(0, 3).map((path33) => ({
44294
+ evidence: sharedPaths.slice(0, 3).map((path39) => ({
42639
44295
  label: "shared path",
42640
- snippet: path33
44296
+ snippet: path39
42641
44297
  }))
42642
44298
  });
42643
44299
  addEdge("shared-resource", [left.skillId, right.skillId], id);
@@ -43015,30 +44671,30 @@ function createSkillIntelligenceService(skills, creator) {
43015
44671
  }
43016
44672
 
43017
44673
  // src/daemon/skill-service.ts
43018
- import fs8 from "node:fs";
43019
- import path10 from "node:path";
43020
- import { z as z18 } from "zod";
44674
+ import fs17 from "node:fs";
44675
+ import path19 from "node:path";
44676
+ import { z as z23 } from "zod";
43021
44677
  function metadataId(skillPath) {
43022
44678
  return SkillIdSchema.parse(opaquePathId("sk", canonicalDirectory(skillPath)));
43023
44679
  }
43024
- var CcskiSkillMetadataSchema = z18.object({
43025
- name: z18.string(),
43026
- description: z18.string(),
43027
- disabled: z18.boolean().optional(),
43028
- provider: z18.string(),
44680
+ var CcskiSkillMetadataSchema = z23.object({
44681
+ name: z23.string(),
44682
+ description: z23.string(),
44683
+ disabled: z23.boolean().optional(),
44684
+ provider: z23.string(),
43029
44685
  location: SkillLocationSchema,
43030
- sourcePriority: z18.number().optional(),
43031
- sourceKind: z18.string().optional(),
43032
- path: z18.string().min(1),
43033
- hasReferences: z18.boolean(),
43034
- hasScripts: z18.boolean(),
43035
- hasAssets: z18.boolean(),
44686
+ sourcePriority: z23.number().optional(),
44687
+ sourceKind: z23.string().optional(),
44688
+ path: z23.string().min(1),
44689
+ hasReferences: z23.boolean(),
44690
+ hasScripts: z23.boolean(),
44691
+ hasAssets: z23.boolean(),
43036
44692
  pluginInfo: PluginInfoSchema.optional()
43037
44693
  });
43038
- var CcskiValidateResultSchema = z18.object({
43039
- success: z18.boolean(),
43040
- errors: z18.array(z18.string()),
43041
- warnings: z18.array(z18.string())
44694
+ var CcskiValidateResultSchema = z23.object({
44695
+ success: z23.boolean(),
44696
+ errors: z23.array(z23.string()),
44697
+ warnings: z23.array(z23.string())
43042
44698
  });
43043
44699
  function projectProvenance(skillPath, probeMap) {
43044
44700
  if (!probeMap) return { installedVia: "unknown", updatable: false };
@@ -43055,7 +44711,7 @@ function projectMetadata(source, probeMap) {
43055
44711
  id: metadataId(canonicalPath),
43056
44712
  name: skill.name,
43057
44713
  description: skill.description,
43058
- directoryName: path10.basename(canonicalPath),
44714
+ directoryName: path19.basename(canonicalPath),
43059
44715
  disabled: skill.disabled ?? false,
43060
44716
  provider: skill.provider,
43061
44717
  location: skill.location,
@@ -43077,19 +44733,41 @@ function createSkillService(workspaces, options2 = {}) {
43077
44733
  const discoverSkills2 = options2.discoverSkills ?? listSkills;
43078
44734
  const validateSkill2 = options2.validateSkill ?? validateSkill;
43079
44735
  const skillsCliProbe = options2.skillsCliProbe;
44736
+ const discoveryInflight = /* @__PURE__ */ new Map();
44737
+ const discoveryKey = (target, includeDisabled) => `${target.workspaceId}:${target.providerId}:${includeDisabled}`;
44738
+ const cachedList = (target, includeDisabled = true) => {
44739
+ const key = discoveryKey(target, includeDisabled);
44740
+ const existing = discoveryInflight.get(key);
44741
+ if (existing) return existing;
44742
+ const promise = list(workspaces, discoverSkills2, target, includeDisabled, skillsCliProbe);
44743
+ discoveryInflight.set(key, promise);
44744
+ const forget = () => discoveryInflight.delete(key);
44745
+ promise.then(forget, forget);
44746
+ return promise;
44747
+ };
44748
+ const invalidateTarget = (target) => {
44749
+ discoveryInflight.delete(discoveryKey(target, true));
44750
+ discoveryInflight.delete(discoveryKey(target, false));
44751
+ };
43080
44752
  return {
43081
- list: (target, includeDisabled = true) => list(workspaces, discoverSkills2, target, includeDisabled, skillsCliProbe),
43082
- resolve: (target, skillId) => resolveSkill2(workspaces, discoverSkills2, target, skillId, skillsCliProbe),
44753
+ list: (target, includeDisabled = true) => cachedList(target, includeDisabled),
44754
+ resolve: (target, skillId) => resolveSkill2(cachedList, target, skillId),
43083
44755
  skillFile,
43084
- info: (target, skillId) => info2(workspaces, discoverSkills2, target, skillId, skillsCliProbe),
43085
- toggle: (target, skillIds, mode) => toggle(workspaces, discoverSkills2, target, skillIds, mode, skillsCliProbe),
43086
- validate: (target, skillId) => validate2(workspaces, discoverSkills2, validateSkill2, target, skillId, skillsCliProbe)
44756
+ info: (target, skillId) => info2(cachedList, target, skillId),
44757
+ toggle: (target, skillIds, mode) => toggle(cachedList, target, skillIds, mode).finally(() => invalidateTarget(target)),
44758
+ validate: (target, skillId) => validate2(cachedList, validateSkill2, workspaces, target, skillId),
44759
+ /**
44760
+ * 丢弃该 target 的 discovery 缓存(写事务用):apply/rollback 等绕过
44761
+ * 本 service 直接写盘后,验证读必须看到新磁盘态(perf B-6 的 TTL 缓存
44762
+ * 不知道外部写入)。
44763
+ */
44764
+ invalidateDiscovery: invalidateTarget
43087
44765
  };
43088
44766
  }
43089
44767
  async function list(workspaces, discoverSkills2, target, includeDisabled = true, skillsCliProbe) {
43090
44768
  const scope = workspaces.resolve(target, includeDisabled);
43091
44769
  const skills = await discoverSkills2(scope.options);
43092
- const probeMap = skillsCliProbe ? await skillsCliProbe.probe() : null;
44770
+ const probeMap = skillsCliProbe?.peek() ?? null;
43093
44771
  const byId = /* @__PURE__ */ new Map();
43094
44772
  for (const skill of skills) {
43095
44773
  const projected = projectMetadata(skill, probeMap);
@@ -43099,27 +44777,25 @@ async function list(workspaces, discoverSkills2, target, includeDisabled = true,
43099
44777
  }
43100
44778
  return [...byId.values()].sort((left, right) => left.name.localeCompare(right.name));
43101
44779
  }
43102
- async function resolveSkill2(workspaces, discoverSkills2, target, skillId, skillsCliProbe) {
43103
- const skill = (await list(workspaces, discoverSkills2, target, true, skillsCliProbe)).find(
43104
- (candidate) => candidate.id === skillId
43105
- );
44780
+ async function resolveSkill2(loadList, target, skillId) {
44781
+ const skill = (await loadList(target, true)).find((candidate) => candidate.id === skillId);
43106
44782
  if (!skill)
43107
44783
  throw new DomainError("NOT_FOUND", `Skill not found in Workspace Provider: ${skillId}`);
43108
44784
  return skill;
43109
44785
  }
43110
44786
  function skillFile(skill) {
43111
44787
  const filename = skill.disabled ? ".SKILL.md" : "SKILL.md";
43112
- const file = path10.join(skill.path, filename);
44788
+ const file = path19.join(skill.path, filename);
43113
44789
  assertPathInside(skill.path, file);
43114
- if (!fs8.existsSync(file) || !fs8.statSync(file).isFile()) {
44790
+ if (!fs17.existsSync(file) || !fs17.statSync(file).isFile()) {
43115
44791
  throw new DomainError("UNAVAILABLE", `Skill document is unavailable: ${skill.name}`);
43116
44792
  }
43117
44793
  return file;
43118
44794
  }
43119
- async function info2(workspaces, discoverSkills2, target, skillId, skillsCliProbe) {
43120
- const skill = await resolveSkill2(workspaces, discoverSkills2, target, skillId, skillsCliProbe);
44795
+ async function info2(loadList, target, skillId) {
44796
+ const skill = await resolveSkill2(loadList, target, skillId);
43121
44797
  const file = skillFile(skill);
43122
- const content = fs8.readFileSync(file, "utf8");
44798
+ const content = fs17.readFileSync(file, "utf8");
43123
44799
  return {
43124
44800
  ...skill,
43125
44801
  size: Buffer.byteLength(content),
@@ -43127,13 +44803,8 @@ async function info2(workspaces, discoverSkills2, target, skillId, skillsCliProb
43127
44803
  revision: contentRevision(content)
43128
44804
  };
43129
44805
  }
43130
- async function toggle(workspaces, discoverSkills2, target, skillIds, mode, skillsCliProbe) {
43131
- const discovered = new Map(
43132
- (await list(workspaces, discoverSkills2, target, true, skillsCliProbe)).map((skill) => [
43133
- skill.id,
43134
- skill
43135
- ])
43136
- );
44806
+ async function toggle(loadList, target, skillIds, mode) {
44807
+ const discovered = new Map((await loadList(target, true)).map((skill) => [skill.id, skill]));
43137
44808
  const results = [];
43138
44809
  for (const skillId of skillIds) {
43139
44810
  const skill = discovered.get(skillId);
@@ -43146,21 +44817,21 @@ async function toggle(workspaces, discoverSkills2, target, skillIds, mode, skill
43146
44817
  results.push({ skillId, name: skill.name, status: "skipped" });
43147
44818
  continue;
43148
44819
  }
43149
- const source = path10.join(skill.path, wantsDisabled ? "SKILL.md" : ".SKILL.md");
43150
- const destination = path10.join(skill.path, wantsDisabled ? ".SKILL.md" : "SKILL.md");
44820
+ const source = path19.join(skill.path, wantsDisabled ? "SKILL.md" : ".SKILL.md");
44821
+ const destination = path19.join(skill.path, wantsDisabled ? ".SKILL.md" : "SKILL.md");
43151
44822
  try {
43152
44823
  assertPathInside(skill.path, source);
43153
44824
  assertPathInside(skill.path, destination);
43154
- if (fs8.existsSync(destination)) {
44825
+ if (fs17.existsSync(destination)) {
43155
44826
  results.push({
43156
44827
  skillId,
43157
44828
  name: skill.name,
43158
44829
  status: "conflict",
43159
- error: `Both ${path10.basename(source)} and ${path10.basename(destination)} exist.`
44830
+ error: `Both ${path19.basename(source)} and ${path19.basename(destination)} exist.`
43160
44831
  });
43161
44832
  continue;
43162
44833
  }
43163
- fs8.renameSync(source, destination);
44834
+ fs17.renameSync(source, destination);
43164
44835
  results.push({ skillId, name: skill.name, status: wantsDisabled ? "disabled" : "enabled" });
43165
44836
  } catch {
43166
44837
  results.push({
@@ -43182,10 +44853,8 @@ async function toggle(workspaces, discoverSkills2, target, skillIds, mode, skill
43182
44853
  failed: results.filter((result) => result.status === "failed").length
43183
44854
  };
43184
44855
  }
43185
- async function validate2(workspaces, discoverSkills2, validateSkill2, target, skillId, skillsCliProbe) {
43186
- const skill = (await list(workspaces, discoverSkills2, target, true, skillsCliProbe)).find(
43187
- (candidate) => candidate.id === skillId
43188
- );
44856
+ async function validate2(loadList, validateSkill2, workspaces, target, skillId) {
44857
+ const skill = (await loadList(target, true)).find((candidate) => candidate.id === skillId);
43189
44858
  if (!skill)
43190
44859
  throw new DomainError("NOT_FOUND", `Skill not found in Workspace Provider: ${skillId}`);
43191
44860
  const result = safeParseExternal(
@@ -43214,9 +44883,9 @@ async function validate2(workspaces, discoverSkills2, validateSkill2, target, sk
43214
44883
  }
43215
44884
 
43216
44885
  // src/daemon/steward-service.ts
43217
- import { promises as fs9 } from "node:fs";
44886
+ import { promises as fs18 } from "node:fs";
43218
44887
  import { randomBytes as randomBytes6 } from "node:crypto";
43219
- import path11 from "node:path";
44888
+ import path20 from "node:path";
43220
44889
 
43221
44890
  // src/daemon/steward/harness-adapter.ts
43222
44891
  var HarnessProcessLostError = class extends Error {
@@ -43261,7 +44930,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
43261
44930
  );
43262
44931
  const runs = /* @__PURE__ */ new Map();
43263
44932
  let disposed = false;
43264
- const executionParent = options2.executionRootParent ?? path11.join(runDir(), "steward");
44933
+ const executionParent = options2.executionRootParent ?? path20.join(runDir(), "steward");
43265
44934
  const appendEvent = (entry, event) => {
43266
44935
  const seq = entry.events.length > 0 ? entry.events[entry.events.length - 1].seq + 1 : 1;
43267
44936
  entry.events.push({ ...event, seq, at: (/* @__PURE__ */ new Date()).toISOString(), runId: entry.run.runId });
@@ -43290,7 +44959,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
43290
44959
  };
43291
44960
  const cleanupExecutionRoot = async (root) => {
43292
44961
  try {
43293
- await fs9.rm(root, { recursive: true, force: true });
44962
+ await fs18.rm(root, { recursive: true, force: true });
43294
44963
  } catch {
43295
44964
  }
43296
44965
  };
@@ -43362,8 +45031,8 @@ function createStewardService(workspaces, skills, intelligence, options2) {
43362
45031
  if (selected.length === 0) {
43363
45032
  throw new DomainError("INVALID_OPERATION", "No skills selected for the steward run.");
43364
45033
  }
43365
- await fs9.mkdir(executionParent, { recursive: true });
43366
- const executionRoot = await fs9.mkdtemp(path11.join(executionParent, "run-"));
45034
+ await fs18.mkdir(executionParent, { recursive: true });
45035
+ const executionRoot = await fs18.mkdtemp(path20.join(executionParent, "run-"));
43367
45036
  const runId = brandRunId(`sr_${randomBytes6(12).toString("hex")}`);
43368
45037
  const run = {
43369
45038
  runId,
@@ -43417,9 +45086,9 @@ function createStewardService(workspaces, skills, intelligence, options2) {
43417
45086
  }
43418
45087
  for (const skill of selected) {
43419
45088
  const info3 = await skillsService.info(run.target, skill.id);
43420
- const destination = path11.join(entry.run.executionRoot, "skills", skill.directoryName);
43421
- await fs9.mkdir(destination, { recursive: true });
43422
- await fs9.writeFile(path11.join(destination, "SKILL.md"), info3.content, "utf8");
45089
+ const destination = path20.join(entry.run.executionRoot, "skills", skill.directoryName);
45090
+ await fs18.mkdir(destination, { recursive: true });
45091
+ await fs18.writeFile(path20.join(destination, "SKILL.md"), info3.content, "utf8");
43423
45092
  }
43424
45093
  const findingSnapshots = analyzed.report.findings.map(
43425
45094
  (finding) => ({
@@ -43505,7 +45174,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
43505
45174
  directoryName: skill.directoryName,
43506
45175
  revision,
43507
45176
  disabled: skill.disabled,
43508
- documentPath: path11.join("skills", skill.directoryName, "SKILL.md")
45177
+ documentPath: path20.join("skills", skill.directoryName, "SKILL.md")
43509
45178
  };
43510
45179
  }),
43511
45180
  findings: findingSnapshots,
@@ -43734,23 +45403,23 @@ var STEWARD_PROMPT_VERSION = "1.0.0";
43734
45403
  var STEWARD_TOOL_VERSION = "1.0.0";
43735
45404
 
43736
45405
  // src/daemon/steward/context-snapshot.ts
43737
- import { createHash as createHash6, randomBytes as randomBytes7 } from "node:crypto";
43738
- import { promises as fs10 } from "node:fs";
43739
- import path12 from "node:path";
45406
+ import { createHash as createHash9, randomBytes as randomBytes7 } from "node:crypto";
45407
+ import { promises as fs19 } from "node:fs";
45408
+ import path21 from "node:path";
43740
45409
  function stewardStoreDir() {
43741
- return path12.join(homeDir(), "steward-store");
45410
+ return path21.join(homeDir(), "steward-store");
43742
45411
  }
43743
45412
  function snapshotsDir() {
43744
- return path12.join(stewardStoreDir(), "snapshots");
45413
+ return path21.join(stewardStoreDir(), "snapshots");
43745
45414
  }
43746
45415
  async function walkRegularFiles(directory) {
43747
45416
  const found = [];
43748
- const entries = await fs10.readdir(directory, { withFileTypes: true });
45417
+ const entries = await fs19.readdir(directory, { withFileTypes: true });
43749
45418
  for (const entry of entries) {
43750
45419
  if (entry.name === ".SKILL.md" || entry.name.startsWith(".tmp-")) continue;
43751
- const absolute = path12.join(directory, entry.name);
45420
+ const absolute = path21.join(directory, entry.name);
43752
45421
  if (entry.isDirectory()) {
43753
- found.push(...(await walkRegularFiles(absolute)).map((rel) => path12.join(entry.name, rel)));
45422
+ found.push(...(await walkRegularFiles(absolute)).map((rel) => path21.join(entry.name, rel)));
43754
45423
  continue;
43755
45424
  }
43756
45425
  if (entry.isFile()) {
@@ -43761,8 +45430,8 @@ async function walkRegularFiles(directory) {
43761
45430
  return found;
43762
45431
  }
43763
45432
  async function sha256File(absolute) {
43764
- const bytes = await fs10.readFile(absolute);
43765
- return { hash: createHash6("sha256").update(bytes).digest("hex"), byteSize: bytes.byteLength };
45433
+ const bytes = await fs19.readFile(absolute);
45434
+ return { hash: createHash9("sha256").update(bytes).digest("hex"), byteSize: bytes.byteLength };
43766
45435
  }
43767
45436
  async function buildContextSnapshot(skills, input) {
43768
45437
  const scopeKind = input.target.workspaceId === GLOBAL_WORKSPACE_ID ? "global" : "imported";
@@ -43825,7 +45494,7 @@ async function buildContextSnapshot(skills, input) {
43825
45494
  try {
43826
45495
  const relFiles = await walkRegularFiles(skill.path);
43827
45496
  for (const rel of relFiles) {
43828
- const { hash, byteSize: resourceBytes } = await sha256File(path12.join(skill.path, rel));
45497
+ const { hash, byteSize: resourceBytes } = await sha256File(path21.join(skill.path, rel));
43829
45498
  resources.push({
43830
45499
  skillId: skill.id,
43831
45500
  relPath: rel,
@@ -43867,8 +45536,8 @@ async function buildContextSnapshot(skills, input) {
43867
45536
  }
43868
45537
  const snapshot = parsedSnapshot.data;
43869
45538
  try {
43870
- await fs10.mkdir(snapshotsDir(), { recursive: true });
43871
- const file = path12.join(snapshotsDir(), `${snapshotId}.json`);
45539
+ await fs19.mkdir(snapshotsDir(), { recursive: true });
45540
+ const file = path21.join(snapshotsDir(), `${snapshotId}.json`);
43872
45541
  atomicWriteUtf8(file, JSON.stringify(snapshot, null, 2));
43873
45542
  } catch (error) {
43874
45543
  throw new DomainError(
@@ -43880,15 +45549,15 @@ async function buildContextSnapshot(skills, input) {
43880
45549
  }
43881
45550
 
43882
45551
  // src/daemon/steward/audit-store.ts
43883
- import { promises as fs11 } from "node:fs";
43884
- import path13 from "node:path";
45552
+ import { promises as fs20 } from "node:fs";
45553
+ import path22 from "node:path";
43885
45554
  function storeFile(name) {
43886
- return path13.join(stewardStoreDir(), `${name}.jsonl`);
45555
+ return path22.join(stewardStoreDir(), `${name}.jsonl`);
43887
45556
  }
43888
45557
  async function appendLine(file, value) {
43889
45558
  try {
43890
- await fs11.mkdir(path13.dirname(file), { recursive: true });
43891
- await fs11.appendFile(file, `${JSON.stringify(value)}
45559
+ await fs20.mkdir(path22.dirname(file), { recursive: true });
45560
+ await fs20.appendFile(file, `${JSON.stringify(value)}
43892
45561
  `, "utf8");
43893
45562
  } catch (error) {
43894
45563
  throw new DomainError(
@@ -43900,7 +45569,7 @@ async function appendLine(file, value) {
43900
45569
  async function readLines(file, parse2) {
43901
45570
  let raw;
43902
45571
  try {
43903
- raw = await fs11.readFile(file, "utf8");
45572
+ raw = await fs20.readFile(file, "utf8");
43904
45573
  } catch (error) {
43905
45574
  if (error.code === "ENOENT") return [];
43906
45575
  throw new DomainError(
@@ -43959,21 +45628,21 @@ function createStewardAuditStore() {
43959
45628
  }
43960
45629
 
43961
45630
  // src/daemon/steward/approval-service.ts
43962
- import { createHash as createHash8, randomBytes as randomBytes10 } from "node:crypto";
43963
- import path17 from "node:path";
45631
+ import { createHash as createHash11, randomBytes as randomBytes10 } from "node:crypto";
45632
+ import path26 from "node:path";
43964
45633
 
43965
45634
  // src/daemon/steward/apply-transaction.ts
43966
45635
  import { randomBytes as randomBytes9 } from "node:crypto";
43967
- import { promises as fs15 } from "node:fs";
43968
- import path16 from "node:path";
45636
+ import { promises as fs24 } from "node:fs";
45637
+ import path25 from "node:path";
43969
45638
 
43970
45639
  // src/daemon/steward/journal-schema.ts
43971
- import { promises as fs13 } from "node:fs";
43972
- import path14 from "node:path";
43973
- import { z as z19 } from "zod";
45640
+ import { promises as fs22 } from "node:fs";
45641
+ import path23 from "node:path";
45642
+ import { z as z24 } from "zod";
43974
45643
 
43975
45644
  // src/daemon/steward/dir-identity.ts
43976
- import { promises as fs12 } from "node:fs";
45645
+ import { promises as fs21 } from "node:fs";
43977
45646
  function darwinSystemAlias(dir) {
43978
45647
  if (process.platform !== "darwin") return null;
43979
45648
  for (const root of ["/var", "/tmp", "/etc"]) {
@@ -43982,14 +45651,14 @@ function darwinSystemAlias(dir) {
43982
45651
  return null;
43983
45652
  }
43984
45653
  async function assertCanonicalDirectory(dir) {
43985
- const stat2 = await fs12.lstat(dir).catch(() => null);
45654
+ const stat2 = await fs21.lstat(dir).catch(() => null);
43986
45655
  if (stat2 === null || !stat2.isDirectory() || stat2.isSymbolicLink()) {
43987
45656
  throw new DomainError(
43988
45657
  "INVALID_OPERATION",
43989
45658
  `manager root is not a real directory (symlinked or missing): ${dir}`
43990
45659
  );
43991
45660
  }
43992
- const real = await fs12.realpath(dir);
45661
+ const real = await fs21.realpath(dir);
43993
45662
  const darwinAlias = darwinSystemAlias(dir);
43994
45663
  if (real !== dir && real !== darwinAlias) {
43995
45664
  throw new DomainError(
@@ -44000,7 +45669,7 @@ async function assertCanonicalDirectory(dir) {
44000
45669
  return { dev: stat2.dev, ino: stat2.ino };
44001
45670
  }
44002
45671
  async function verifyDirIdentity(dir, identity2) {
44003
- const stat2 = await fs12.lstat(dir).catch(() => null);
45672
+ const stat2 = await fs21.lstat(dir).catch(() => null);
44004
45673
  if (stat2 === null || !stat2.isDirectory() || stat2.dev !== identity2.dev || stat2.ino !== identity2.ino) {
44005
45674
  throw new DomainError(
44006
45675
  "UNAVAILABLE",
@@ -44029,20 +45698,20 @@ async function anchorManagerDirectory(dir) {
44029
45698
  }
44030
45699
 
44031
45700
  // src/daemon/steward/journal-schema.ts
44032
- var BackupRefSchema = z19.string().regex(/^\d+-[0-9a-f]{12}\.bin$/, {
45701
+ var BackupRefSchema = z24.string().regex(/^\d+-[0-9a-f]{12}\.bin$/, {
44033
45702
  message: "Manager backup reference must match `<seq>-<sha12>.bin`."
44034
45703
  });
44035
- var Sha256HexSchema = z19.string().regex(/^[a-f0-9]{64}$/);
44036
- var BackupManifestLineSchema = z19.strictObject({
45704
+ var Sha256HexSchema = z24.string().regex(/^[a-f0-9]{64}$/);
45705
+ var BackupManifestLineSchema = z24.strictObject({
44037
45706
  ref: BackupRefSchema,
44038
- seq: z19.number().int().positive(),
45707
+ seq: z24.number().int().positive(),
44039
45708
  from: RelPathSchema,
44040
45709
  sha256: Sha256HexSchema,
44041
- byteSize: z19.number().int().nonnegative().max(64 * 1024 * 1024, "backup byteSize exceeds the manager ceiling")
45710
+ byteSize: z24.number().int().nonnegative().max(64 * 1024 * 1024, "backup byteSize exceeds the manager ceiling")
44042
45711
  });
44043
- var ResourceDetailSchema = z19.strictObject({
44044
- kind: z19.literal("resource"),
44045
- strategy: z19.enum(["copy", "move", "reference"]),
45712
+ var ResourceDetailSchema = z24.strictObject({
45713
+ kind: z24.literal("resource"),
45714
+ strategy: z24.enum(["copy", "move", "reference"]),
44046
45715
  from: RelPathSchema,
44047
45716
  to: RelPathSchema,
44048
45717
  /** Codex R10 P1-2/P1-7:apply 时记录的落盘字节 sha256(回滚删除前逐项校验)。 */
@@ -44066,81 +45735,81 @@ var ResourceDetailSchema = z19.strictObject({
44066
45735
  });
44067
45736
  }
44068
45737
  });
44069
- var JournalEntrySchema = z19.discriminatedUnion("step", [
44070
- z19.strictObject({
44071
- seq: z19.number().int().positive(),
44072
- step: z19.literal("precheck"),
44073
- detail: z19.strictObject({
44074
- kind: z19.literal("precheck"),
44075
- targets: z19.array(SkillDirectoryNameSchema).min(1)
45738
+ var JournalEntrySchema = z24.discriminatedUnion("step", [
45739
+ z24.strictObject({
45740
+ seq: z24.number().int().positive(),
45741
+ step: z24.literal("precheck"),
45742
+ detail: z24.strictObject({
45743
+ kind: z24.literal("precheck"),
45744
+ targets: z24.array(SkillDirectoryNameSchema).min(1)
44076
45745
  })
44077
45746
  }),
44078
- z19.strictObject({
44079
- seq: z19.number().int().positive(),
44080
- step: z19.literal("edit"),
44081
- detail: z19.strictObject({
44082
- kind: z19.literal("content"),
45747
+ z24.strictObject({
45748
+ seq: z24.number().int().positive(),
45749
+ step: z24.literal("edit"),
45750
+ detail: z24.strictObject({
45751
+ kind: z24.literal("content"),
44083
45752
  skillId: SkillIdSchema,
44084
45753
  beforeRevision: ContentRevisionSchema
44085
45754
  })
44086
45755
  }),
44087
- z19.strictObject({
44088
- seq: z19.number().int().positive(),
44089
- step: z19.literal("disable"),
44090
- detail: z19.strictObject({
44091
- kind: z19.literal("enablement"),
45756
+ z24.strictObject({
45757
+ seq: z24.number().int().positive(),
45758
+ step: z24.literal("disable"),
45759
+ detail: z24.strictObject({
45760
+ kind: z24.literal("enablement"),
44092
45761
  skillId: SkillIdSchema,
44093
45762
  /** Codex R10 P1-3:apply 捕获的启停前态——undo 只恢复捕获状态。 */
44094
- wasDisabled: z19.boolean(),
45763
+ wasDisabled: z24.boolean(),
44095
45764
  revision: ContentRevisionSchema.optional()
44096
45765
  })
44097
45766
  }),
44098
- z19.strictObject({
44099
- seq: z19.number().int().positive(),
44100
- step: z19.literal("enable"),
44101
- detail: z19.strictObject({
44102
- kind: z19.literal("enablement"),
45767
+ z24.strictObject({
45768
+ seq: z24.number().int().positive(),
45769
+ step: z24.literal("enable"),
45770
+ detail: z24.strictObject({
45771
+ kind: z24.literal("enablement"),
44103
45772
  skillId: SkillIdSchema,
44104
45773
  /** Codex R10 P1-3:apply 捕获的启停前态——undo 只恢复捕获状态。 */
44105
- wasEnabled: z19.boolean(),
45774
+ wasEnabled: z24.boolean(),
44106
45775
  revision: ContentRevisionSchema.optional()
44107
45776
  })
44108
45777
  }),
44109
- z19.strictObject({
44110
- seq: z19.number().int().positive(),
44111
- step: z19.literal("create-target"),
44112
- detail: z19.strictObject({
44113
- kind: z19.literal("content"),
45778
+ z24.strictObject({
45779
+ seq: z24.number().int().positive(),
45780
+ step: z24.literal("create-target"),
45781
+ detail: z24.strictObject({
45782
+ kind: z24.literal("content"),
44114
45783
  directoryName: SkillDirectoryNameSchema,
44115
45784
  /** Codex R10 P1-2:创建后 SKILL.md 的 revision(sha256:…,回滚删除前校验)。 */
44116
45785
  revision: ContentRevisionSchema
44117
45786
  })
44118
45787
  }),
44119
- z19.strictObject({
44120
- seq: z19.number().int().positive(),
44121
- step: z19.literal("resource"),
45788
+ z24.strictObject({
45789
+ seq: z24.number().int().positive(),
45790
+ step: z24.literal("resource"),
44122
45791
  detail: ResourceDetailSchema
44123
45792
  }),
44124
- z19.strictObject({
44125
- seq: z19.number().int().positive(),
44126
- step: z19.literal("commit"),
44127
- detail: z19.strictObject({
44128
- kind: z19.literal("commit"),
45793
+ z24.strictObject({
45794
+ seq: z24.number().int().positive(),
45795
+ step: z24.literal("commit"),
45796
+ detail: z24.strictObject({
45797
+ kind: z24.literal("commit"),
44129
45798
  /** 操作 ID(journal 文件名派生);回放侧按与期望 operationId 全等比对。 */
44130
- proposalId: z19.string().min(1).max(200),
44131
- status: z19.literal("applied"),
44132
- mutationCount: z19.number().int().nonnegative()
45799
+ proposalId: z24.string().min(1).max(200),
45800
+ status: z24.literal("applied"),
45801
+ mutationCount: z24.number().int().nonnegative()
44133
45802
  })
44134
45803
  })
44135
45804
  ]);
44136
45805
  async function readJournal(journalPath) {
44137
- const parentIdentity = await anchorManagerDirectory(path14.dirname(journalPath));
45806
+ const parentIdentity = await anchorManagerDirectory(path23.dirname(journalPath));
44138
45807
  let raw;
44139
45808
  let leafIdentity;
44140
45809
  try {
44141
- const handle = await fs13.open(
45810
+ const handle = await fs22.open(
44142
45811
  journalPath,
44143
- fs13.constants.O_RDONLY | fs13.constants.O_NOFOLLOW
45812
+ fs22.constants.O_RDONLY | fs22.constants.O_NOFOLLOW
44144
45813
  );
44145
45814
  try {
44146
45815
  const stat2 = await handle.stat();
@@ -44166,14 +45835,14 @@ async function readJournal(journalPath) {
44166
45835
  `Journal file unreadable (${code}); recovery required: ${journalPath}`
44167
45836
  );
44168
45837
  }
44169
- const after = await fs13.lstat(journalPath).catch(() => null);
45838
+ const after = await fs22.lstat(journalPath).catch(() => null);
44170
45839
  if (after === null || !after.isFile() || after.dev !== leafIdentity.dev || after.ino !== leafIdentity.ino) {
44171
45840
  throw new DomainError(
44172
45841
  "UNAVAILABLE",
44173
45842
  `Journal leaf identity drifted after read; recovery required: ${journalPath}`
44174
45843
  );
44175
45844
  }
44176
- await verifyDirIdentity(path14.dirname(journalPath), parentIdentity);
45845
+ await verifyDirIdentity(path23.dirname(journalPath), parentIdentity);
44177
45846
  const entries = [];
44178
45847
  const lines = raw.split("\n");
44179
45848
  for (let index = 0; index < lines.length; index += 1) {
@@ -44329,29 +45998,29 @@ function assertCommittedJournal(entries, expected) {
44329
45998
  }
44330
45999
 
44331
46000
  // src/daemon/steward/fs-authority.ts
44332
- import { createHash as createHash7, randomBytes as randomBytes8 } from "node:crypto";
44333
- import { promises as fs14 } from "node:fs";
44334
- import path15 from "node:path";
46001
+ import { createHash as createHash10, randomBytes as randomBytes8 } from "node:crypto";
46002
+ import { promises as fs23 } from "node:fs";
46003
+ import path24 from "node:path";
44335
46004
  var assertRealRoot = assertCanonicalDirectory;
44336
- var O_NOFOLLOW = fs14.constants.O_NOFOLLOW ?? 0;
46005
+ var O_NOFOLLOW = fs23.constants.O_NOFOLLOW ?? 0;
44337
46006
  function sha256Hex(bytes) {
44338
- return createHash7("sha256").update(bytes).digest("hex");
46007
+ return createHash10("sha256").update(bytes).digest("hex");
44339
46008
  }
44340
46009
  async function assertNoSymlinkAncestors(to, root) {
44341
- const realRoot = await fs14.realpath(root);
44342
- const parent = path15.dirname(to);
44343
- const realParent = await fs14.realpath(parent);
44344
- const lexicalParent = path15.join(realRoot, path15.relative(root, parent));
46010
+ const realRoot = await fs23.realpath(root);
46011
+ const parent = path24.dirname(to);
46012
+ const realParent = await fs23.realpath(parent);
46013
+ const lexicalParent = path24.join(realRoot, path24.relative(root, parent));
44345
46014
  if (realParent !== lexicalParent) {
44346
46015
  throw new DomainError(
44347
46016
  "INVALID_OPERATION",
44348
- `Resource target path contains a symlink ancestor: ${path15.relative(root, to)}`
46017
+ `Resource target path contains a symlink ancestor: ${path24.relative(root, to)}`
44349
46018
  );
44350
46019
  }
44351
46020
  }
44352
46021
  async function syncDir(dir) {
44353
46022
  try {
44354
- const handle = await fs14.open(dir, fs14.constants.O_RDONLY | fs14.constants.O_DIRECTORY);
46023
+ const handle = await fs23.open(dir, fs23.constants.O_RDONLY | fs23.constants.O_DIRECTORY);
44355
46024
  try {
44356
46025
  await handle.sync();
44357
46026
  } finally {
@@ -44367,7 +46036,7 @@ async function syncDir(dir) {
44367
46036
  async function captureLeafIdentity(leaf, label, expectedByteSize) {
44368
46037
  let stat2;
44369
46038
  try {
44370
- stat2 = await fs14.lstat(leaf);
46039
+ stat2 = await fs23.lstat(leaf);
44371
46040
  } catch {
44372
46041
  throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
44373
46042
  }
@@ -44385,8 +46054,8 @@ async function captureLeafIdentity(leaf, label, expectedByteSize) {
44385
46054
  async function readResourceBytesStrict(from, root, expectedByteSize, label) {
44386
46055
  assertPathInside(root, from);
44387
46056
  const rootIdentity = await assertRealRoot(root);
44388
- const realRoot = await fs14.realpath(root);
44389
- const lexicalPosition = path15.join(realRoot, path15.relative(root, from));
46057
+ const realRoot = await fs23.realpath(root);
46058
+ const lexicalPosition = path24.join(realRoot, path24.relative(root, from));
44390
46059
  const realFrom = await realpathOrNotFound(from, label);
44391
46060
  if (realFrom !== lexicalPosition) {
44392
46061
  throw new DomainError(
@@ -44397,7 +46066,7 @@ async function readResourceBytesStrict(from, root, expectedByteSize, label) {
44397
46066
  const identity2 = await captureLeafIdentity(from, label, expectedByteSize);
44398
46067
  let handle;
44399
46068
  try {
44400
- handle = await fs14.open(from, fs14.constants.O_RDONLY | O_NOFOLLOW);
46069
+ handle = await fs23.open(from, fs23.constants.O_RDONLY | O_NOFOLLOW);
44401
46070
  } catch {
44402
46071
  throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
44403
46072
  }
@@ -44435,7 +46104,7 @@ async function readResourceBytesStrict(from, root, expectedByteSize, label) {
44435
46104
  }
44436
46105
  async function realpathOrNotFound(target, label) {
44437
46106
  try {
44438
- return await fs14.realpath(target);
46107
+ return await fs23.realpath(target);
44439
46108
  } catch {
44440
46109
  throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
44441
46110
  }
@@ -44443,13 +46112,13 @@ async function realpathOrNotFound(target, label) {
44443
46112
  async function writeFileExclusiveVerified(to, root, bytes, label) {
44444
46113
  assertPathInside(root, to);
44445
46114
  const rootIdentity = await assertRealRoot(root);
44446
- const realRoot = await fs14.realpath(root);
44447
- const lexicalPosition = path15.join(realRoot, path15.relative(root, to));
46115
+ const realRoot = await fs23.realpath(root);
46116
+ const lexicalPosition = path24.join(realRoot, path24.relative(root, to));
44448
46117
  await assertNoSymlinkAncestors(to, root);
44449
- const parentIdentity = await assertCanonicalDirectory(path15.dirname(to));
46118
+ const parentIdentity = await assertCanonicalDirectory(path24.dirname(to));
44450
46119
  let handle;
44451
46120
  try {
44452
- handle = await fs14.open(to, "wx");
46121
+ handle = await fs23.open(to, "wx");
44453
46122
  } catch (error) {
44454
46123
  throw new DomainError(
44455
46124
  "INVALID_OPERATION",
@@ -44463,14 +46132,14 @@ async function writeFileExclusiveVerified(to, root, bytes, label) {
44463
46132
  throw new DomainError("INVALID_OPERATION", `Resource target is not a regular file: ${label}`);
44464
46133
  }
44465
46134
  identity2 = { dev: stat2.dev, ino: stat2.ino };
44466
- const realTo = await fs14.realpath(to);
46135
+ const realTo = await fs23.realpath(to);
44467
46136
  if (realTo !== lexicalPosition) {
44468
46137
  throw new DomainError(
44469
46138
  "UNAVAILABLE",
44470
46139
  `Resource target escaped the root before any bytes were written (path race; at most an empty placeholder remains at ${realTo}); recovery required: ${label}`
44471
46140
  );
44472
46141
  }
44473
- const atPosition = await fs14.lstat(to);
46142
+ const atPosition = await fs23.lstat(to);
44474
46143
  if (atPosition.dev !== identity2.dev || atPosition.ino !== identity2.ino) {
44475
46144
  throw new DomainError(
44476
46145
  "UNAVAILABLE",
@@ -44478,20 +46147,20 @@ async function writeFileExclusiveVerified(to, root, bytes, label) {
44478
46147
  );
44479
46148
  }
44480
46149
  await verifyDirIdentity(root, rootIdentity);
44481
- await verifyDirIdentity(path15.dirname(to), parentIdentity);
46150
+ await verifyDirIdentity(path24.dirname(to), parentIdentity);
44482
46151
  await handle.writeFile(bytes);
44483
46152
  await handle.sync();
44484
46153
  } finally {
44485
46154
  await handle.close().catch(() => void 0);
44486
46155
  }
44487
- const realToAfter = await fs14.realpath(to);
46156
+ const realToAfter = await fs23.realpath(to);
44488
46157
  if (realToAfter !== lexicalPosition) {
44489
46158
  throw new DomainError(
44490
46159
  "UNAVAILABLE",
44491
46160
  `Resource target escaped the provider root after write (path race); external path untouched, recovery required: ${label}`
44492
46161
  );
44493
46162
  }
44494
- const after = await fs14.lstat(to);
46163
+ const after = await fs23.lstat(to);
44495
46164
  if (after.dev !== identity2.dev || after.ino !== identity2.ino) {
44496
46165
  throw new DomainError(
44497
46166
  "UNAVAILABLE",
@@ -44506,7 +46175,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
44506
46175
  await assertNoSymlinkAncestors(to, root);
44507
46176
  let stat2 = null;
44508
46177
  try {
44509
- stat2 = await fs14.lstat(to);
46178
+ stat2 = await fs23.lstat(to);
44510
46179
  } catch {
44511
46180
  stat2 = null;
44512
46181
  }
@@ -44514,7 +46183,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
44514
46183
  if (!stat2.isFile()) {
44515
46184
  throw new Error(`move restore target exists but is not a regular file: ${label}`);
44516
46185
  }
44517
- const handle = await fs14.open(to, fs14.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
46186
+ const handle = await fs23.open(to, fs23.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
44518
46187
  if (handle === null) {
44519
46188
  throw new Error(`move restore target cannot be opened without following symlinks: ${label}`);
44520
46189
  }
@@ -44530,7 +46199,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
44530
46199
  } finally {
44531
46200
  await handle.close().catch(() => void 0);
44532
46201
  }
44533
- const after = await fs14.lstat(to);
46202
+ const after = await fs23.lstat(to);
44534
46203
  if (after.dev !== stat2.dev || after.ino !== stat2.ino) {
44535
46204
  throw new Error(`move restore target identity drifted after read: ${label}`);
44536
46205
  }
@@ -44554,7 +46223,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
44554
46223
  `Resource source is hardlinked (nlink=${identity2.nlink}); exclusive-inode policy refuses to move it: ${label}`
44555
46224
  );
44556
46225
  }
44557
- const handle = await fs14.open(leaf, fs14.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
46226
+ const handle = await fs23.open(leaf, fs23.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
44558
46227
  if (handle === null) {
44559
46228
  throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
44560
46229
  }
@@ -44568,13 +46237,13 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
44568
46237
  }
44569
46238
  const quarantineRoot = await prepareBackupRoot(quarantineJournalPath);
44570
46239
  const quarantineIdentity = await assertCanonicalDirectory(quarantineRoot);
44571
- const tombstone = path15.join(
46240
+ const tombstone = path24.join(
44572
46241
  quarantineRoot,
44573
- `removed-${randomBytes8(8).toString("hex")}-${path15.basename(leaf)}`
46242
+ `removed-${randomBytes8(8).toString("hex")}-${path24.basename(leaf)}`
44574
46243
  );
44575
46244
  try {
44576
46245
  await verifyDirIdentity(root, rootIdentity);
44577
- await fs14.rename(leaf, tombstone);
46246
+ await fs23.rename(leaf, tombstone);
44578
46247
  } catch (error) {
44579
46248
  if (error.code === "EXDEV") {
44580
46249
  throw new DomainError(
@@ -44584,7 +46253,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
44584
46253
  }
44585
46254
  throw error;
44586
46255
  }
44587
- const stillExists = await fs14.lstat(leaf).then(
46256
+ const stillExists = await fs23.lstat(leaf).then(
44588
46257
  () => true,
44589
46258
  () => false
44590
46259
  );
@@ -44594,7 +46263,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
44594
46263
  `Resource source path survived quarantine rename (path race); recovery required: ${label}`
44595
46264
  );
44596
46265
  }
44597
- const tomb = await fs14.lstat(tombstone).catch(() => null);
46266
+ const tomb = await fs23.lstat(tombstone).catch(() => null);
44598
46267
  if (tomb === null || !tomb.isFile() || tomb.dev !== identity2.dev || tomb.ino !== identity2.ino) {
44599
46268
  throw new DomainError(
44600
46269
  "UNAVAILABLE",
@@ -44617,14 +46286,14 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
44617
46286
  }
44618
46287
  async function prepareBackupRoot(journalPath) {
44619
46288
  const backupDir = `${journalPath}.backups`;
44620
- await fs14.mkdir(path15.dirname(journalPath), 448).catch(() => void 0);
44621
- await anchorManagerDirectory(path15.dirname(journalPath)).catch(() => {
46289
+ await fs23.mkdir(path24.dirname(journalPath), 448).catch(() => void 0);
46290
+ await anchorManagerDirectory(path24.dirname(journalPath)).catch(() => {
44622
46291
  throw new DomainError(
44623
46292
  "UNAVAILABLE",
44624
- `journal directory is not canonical (or was replaced during this daemon lifetime); recovery required: ${path15.dirname(journalPath)}`
46293
+ `journal directory is not canonical (or was replaced during this daemon lifetime); recovery required: ${path24.dirname(journalPath)}`
44625
46294
  );
44626
46295
  });
44627
- await fs14.mkdir(backupDir, 448).catch(() => void 0);
46296
+ await fs23.mkdir(backupDir, 448).catch(() => void 0);
44628
46297
  await anchorManagerDirectory(backupDir).catch(() => {
44629
46298
  throw new DomainError(
44630
46299
  "UNAVAILABLE",
@@ -44638,24 +46307,24 @@ async function writeBackupWithManifest(options2) {
44638
46307
  const parsedRef = BackupRefSchema.parse(ref);
44639
46308
  const root = await prepareBackupRoot(options2.journalPath);
44640
46309
  const rootIdentity = await assertCanonicalDirectory(root);
44641
- await writeFileExclusiveVerified(path15.join(root, parsedRef), root, bytes, `backup:${parsedRef}`);
46310
+ await writeFileExclusiveVerified(path24.join(root, parsedRef), root, bytes, `backup:${parsedRef}`);
44642
46311
  await syncDir(root);
44643
46312
  await verifyDirIdentity(root, rootIdentity);
44644
- const manifestHandle = await fs14.open(
44645
- path15.join(root, "manifest.jsonl"),
44646
- fs14.constants.O_WRONLY | fs14.constants.O_APPEND | fs14.constants.O_CREAT | O_NOFOLLOW,
46313
+ const manifestHandle = await fs23.open(
46314
+ path24.join(root, "manifest.jsonl"),
46315
+ fs23.constants.O_WRONLY | fs23.constants.O_APPEND | fs23.constants.O_CREAT | O_NOFOLLOW,
44647
46316
  384
44648
46317
  );
44649
46318
  await verifyDirIdentity(root, rootIdentity);
44650
46319
  const manifestStat = await manifestHandle.stat();
44651
46320
  if (!manifestStat.isFile()) {
44652
46321
  await manifestHandle.close().catch(() => void 0);
44653
- throw new Error(`backup manifest is not a regular file: ${path15.join(root, "manifest.jsonl")}`);
46322
+ throw new Error(`backup manifest is not a regular file: ${path24.join(root, "manifest.jsonl")}`);
44654
46323
  }
44655
46324
  if (manifestStat.nlink !== 1) {
44656
46325
  await manifestHandle.close().catch(() => void 0);
44657
46326
  throw new Error(
44658
- `backup manifest is hardlinked (nlink=${manifestStat.nlink}); manager refuses to append: ${path15.join(root, "manifest.jsonl")}`
46327
+ `backup manifest is hardlinked (nlink=${manifestStat.nlink}); manager refuses to append: ${path24.join(root, "manifest.jsonl")}`
44659
46328
  );
44660
46329
  }
44661
46330
  try {
@@ -44671,7 +46340,7 @@ async function writeBackupWithManifest(options2) {
44671
46340
  await manifestHandle.sync();
44672
46341
  if ((await manifestHandle.stat()).nlink !== 1) {
44673
46342
  throw new Error(
44674
- `backup manifest gained a hardlink during append; recovery required: ${path15.join(root, "manifest.jsonl")}`
46343
+ `backup manifest gained a hardlink during append; recovery required: ${path24.join(root, "manifest.jsonl")}`
44675
46344
  );
44676
46345
  }
44677
46346
  } finally {
@@ -44682,10 +46351,10 @@ async function writeBackupWithManifest(options2) {
44682
46351
  }
44683
46352
  async function readBackupManifest(journalPath) {
44684
46353
  const root = await prepareBackupRoot(journalPath);
44685
- const manifestPath = path15.join(root, "manifest.jsonl");
46354
+ const manifestPath = path24.join(root, "manifest.jsonl");
44686
46355
  let raw;
44687
46356
  try {
44688
- const handle = await fs14.open(manifestPath, fs14.constants.O_RDONLY | O_NOFOLLOW);
46357
+ const handle = await fs23.open(manifestPath, fs23.constants.O_RDONLY | O_NOFOLLOW);
44689
46358
  try {
44690
46359
  if (!(await handle.stat()).isFile()) {
44691
46360
  throw new Error(`backup manifest is not a regular file: ${manifestPath}`);
@@ -44776,18 +46445,18 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
44776
46445
  let seq = 0;
44777
46446
  const movedRestore = /* @__PURE__ */ new Map();
44778
46447
  let journalWriter = null;
44779
- const operationId = path16.basename(deps.journalPath, ".jsonl");
46448
+ const operationId = path25.basename(deps.journalPath, ".jsonl");
44780
46449
  const recordStep = async (step, detail) => {
44781
46450
  if (journalWriter === null) {
44782
- await fs15.mkdir(path16.dirname(deps.journalPath), { recursive: true });
44783
- await assertCanonicalDirectory(path16.dirname(deps.journalPath));
44784
- const journalDirIdentity = await anchorManagerDirectory(path16.dirname(deps.journalPath));
44785
- journalWriter = await fs15.open(
46451
+ await fs24.mkdir(path25.dirname(deps.journalPath), { recursive: true });
46452
+ await assertCanonicalDirectory(path25.dirname(deps.journalPath));
46453
+ const journalDirIdentity = await anchorManagerDirectory(path25.dirname(deps.journalPath));
46454
+ journalWriter = await fs24.open(
44786
46455
  deps.journalPath,
44787
- fs15.constants.O_WRONLY | fs15.constants.O_CREAT | fs15.constants.O_EXCL | fs15.constants.O_NOFOLLOW,
46456
+ fs24.constants.O_WRONLY | fs24.constants.O_CREAT | fs24.constants.O_EXCL | fs24.constants.O_NOFOLLOW,
44788
46457
  384
44789
46458
  );
44790
- await verifyDirIdentity(path16.dirname(deps.journalPath), journalDirIdentity);
46459
+ await verifyDirIdentity(path25.dirname(deps.journalPath), journalDirIdentity);
44791
46460
  }
44792
46461
  seq += 1;
44793
46462
  const entry = { seq, step, detail };
@@ -44797,6 +46466,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
44797
46466
  await journalWriter.sync();
44798
46467
  };
44799
46468
  const root = deps.workspaces.resolveWritable(snapshot.target).directory;
46469
+ deps.skills.invalidateDiscovery(snapshot.target);
44800
46470
  const compensate = async (failure) => {
44801
46471
  const targetTrees = buildTargetTrees(journal);
44802
46472
  for (const entry of [...journal].reverse()) {
@@ -44819,6 +46489,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
44819
46489
  };
44820
46490
  }
44821
46491
  }
46492
+ deps.skills.invalidateDiscovery(snapshot.target);
44822
46493
  return { status: "compensated", mutations, failure };
44823
46494
  };
44824
46495
  try {
@@ -44940,7 +46611,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
44940
46611
  }
44941
46612
  const targets = proposal.patch.kind === "split" ? proposal.patch.targets : [proposal.patch.target];
44942
46613
  for (const target of targets) {
44943
- const destination = path16.join(root, target.directoryName);
46614
+ const destination = path25.join(root, target.directoryName);
44944
46615
  if (await pathExists(destination)) {
44945
46616
  throw new DomainError(
44946
46617
  "CONFLICT",
@@ -44985,12 +46656,12 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
44985
46656
  `Resource mapping source missing from snapshot: ${mapping.sourceSkillId}`
44986
46657
  );
44987
46658
  }
44988
- const from = path16.join(root, mappingSource.directoryName, mapping.sourcePath);
44989
- const to = path16.join(root, target.directoryName, mapping.targetPath);
46659
+ const from = path25.join(root, mappingSource.directoryName, mapping.sourcePath);
46660
+ const to = path25.join(root, target.directoryName, mapping.targetPath);
44990
46661
  assertPathInside(root, from);
44991
46662
  assertPathInside(root, to);
44992
- assertPathInside(path16.join(root, target.directoryName), to);
44993
- if (path16.basename(to).toLowerCase() === "skill.md") {
46663
+ assertPathInside(path25.join(root, target.directoryName), to);
46664
+ if (path25.basename(to).toLowerCase() === "skill.md") {
44994
46665
  throw new DomainError(
44995
46666
  "INVALID_OPERATION",
44996
46667
  `Resource targetPath must not target the skill document: ${target.directoryName}/${mapping.targetPath}`
@@ -45055,7 +46726,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
45055
46726
  sha256: sha256Hex(writtenBytes),
45056
46727
  ...backupRef === void 0 ? {} : { backupRef, sourceSha256: liveHash }
45057
46728
  });
45058
- await fs15.mkdir(path16.dirname(to), { recursive: true });
46729
+ await fs24.mkdir(path25.dirname(to), { recursive: true });
45059
46730
  await assertNoSymlinkAncestors(to, root);
45060
46731
  if (mapping.strategy === "reference") {
45061
46732
  await writeFileExclusiveVerified(
@@ -45099,6 +46770,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
45099
46770
  break;
45100
46771
  }
45101
46772
  }
46773
+ deps.skills.invalidateDiscovery(snapshot.target);
45102
46774
  await recordStep("commit", {
45103
46775
  kind: "commit",
45104
46776
  proposalId: operationId,
@@ -45137,7 +46809,7 @@ async function toggleWithPostcondition(deps, target, skillId, mode) {
45137
46809
  }
45138
46810
  async function pathExists(target) {
45139
46811
  try {
45140
- await fs15.stat(target);
46812
+ await fs24.stat(target);
45141
46813
  return true;
45142
46814
  } catch {
45143
46815
  return false;
@@ -45157,7 +46829,7 @@ async function undoJournalSteps(entries, context) {
45157
46829
  checked.push(parsed.data);
45158
46830
  }
45159
46831
  assertCommittedJournal(checked, {
45160
- proposalId: path16.basename(context.backupJournalPath ?? context.deps.journalPath, ".jsonl"),
46832
+ proposalId: path25.basename(context.backupJournalPath ?? context.deps.journalPath, ".jsonl"),
45161
46833
  proposal: context.proposal,
45162
46834
  snapshot: context.snapshot
45163
46835
  });
@@ -45202,7 +46874,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45202
46874
  assertPathInside(root, dir);
45203
46875
  let stat2;
45204
46876
  try {
45205
- stat2 = await fs15.lstat(dir);
46877
+ stat2 = await fs24.lstat(dir);
45206
46878
  } catch {
45207
46879
  return;
45208
46880
  }
@@ -45213,8 +46885,8 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45213
46885
  `Created target is no longer a directory (external drift): ${directoryName}`
45214
46886
  );
45215
46887
  }
45216
- const realDir = await fs15.realpath(dir);
45217
- const lexicalDir = path16.join(await fs15.realpath(root), path16.relative(root, dir));
46888
+ const realDir = await fs24.realpath(dir);
46889
+ const lexicalDir = path25.join(await fs24.realpath(root), path25.relative(root, dir));
45218
46890
  if (realDir !== lexicalDir) {
45219
46891
  throw new DomainError(
45220
46892
  "INVALID_OPERATION",
@@ -45231,7 +46903,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45231
46903
  );
45232
46904
  }
45233
46905
  for (const [file, expectedSha] of expected) {
45234
- const content = await fs15.readFile(path16.join(dir, file));
46906
+ const content = await fs24.readFile(path25.join(dir, file));
45235
46907
  const actualSha = sha256Hex(content);
45236
46908
  const wanted = expectedSha.startsWith("sha256:") ? expectedSha.slice("sha256:".length) : expectedSha;
45237
46909
  if (actualSha !== wanted) {
@@ -45244,13 +46916,13 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45244
46916
  const identity2 = { dev: stat2.dev, ino: stat2.ino };
45245
46917
  const quarantineRoot = await prepareBackupRoot(quarantineJournalPath);
45246
46918
  const quarantineIdentity = await assertCanonicalDirectory(quarantineRoot);
45247
- const tombstone = path16.join(
46919
+ const tombstone = path25.join(
45248
46920
  quarantineRoot,
45249
- `removed-dir-${randomBytes9(8).toString("hex")}-${path16.basename(dir)}`
46921
+ `removed-dir-${randomBytes9(8).toString("hex")}-${path25.basename(dir)}`
45250
46922
  );
45251
46923
  try {
45252
46924
  await verifyDirIdentity(root, rootIdentity);
45253
- await fs15.rename(dir, tombstone);
46925
+ await fs24.rename(dir, tombstone);
45254
46926
  } catch (error) {
45255
46927
  if (error.code === "EXDEV") {
45256
46928
  throw new DomainError(
@@ -45260,7 +46932,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45260
46932
  }
45261
46933
  throw error;
45262
46934
  }
45263
- const stillExists = await fs15.lstat(dir).then(
46935
+ const stillExists = await fs24.lstat(dir).then(
45264
46936
  () => true,
45265
46937
  () => false
45266
46938
  );
@@ -45270,7 +46942,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45270
46942
  `Created target survived quarantine rename (path race); recovery required: ${directoryName}`
45271
46943
  );
45272
46944
  }
45273
- const tomb = await fs15.lstat(tombstone).catch(() => null);
46945
+ const tomb = await fs24.lstat(tombstone).catch(() => null);
45274
46946
  if (tomb === null || !tomb.isDirectory() || tomb.dev !== identity2.dev || tomb.ino !== identity2.ino) {
45275
46947
  throw new DomainError(
45276
46948
  "UNAVAILABLE",
@@ -45283,11 +46955,11 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
45283
46955
  }
45284
46956
  async function walkFiles(dir, prefix) {
45285
46957
  const files = /* @__PURE__ */ new Map();
45286
- const entries = await fs15.readdir(dir, { withFileTypes: true });
46958
+ const entries = await fs24.readdir(dir, { withFileTypes: true });
45287
46959
  for (const entry of entries) {
45288
46960
  const rel = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
45289
46961
  if (entry.isDirectory()) {
45290
- for (const [f] of await walkFiles(path16.join(dir, entry.name), rel)) files.set(f, "");
46962
+ for (const [f] of await walkFiles(path25.join(dir, entry.name), rel)) files.set(f, "");
45291
46963
  } else if (entry.isFile()) {
45292
46964
  files.set(rel, "");
45293
46965
  } else {
@@ -45373,7 +47045,7 @@ async function undoStep(entry, context) {
45373
47045
  );
45374
47046
  }
45375
47047
  await removeCreatedDirectory(
45376
- path16.join(root, directoryName),
47048
+ path25.join(root, directoryName),
45377
47049
  root,
45378
47050
  directoryName,
45379
47051
  context.targetTrees.get(directoryName),
@@ -45384,11 +47056,11 @@ async function undoStep(entry, context) {
45384
47056
  case "resource": {
45385
47057
  const { from: fromRel, to, strategy } = entry.detail;
45386
47058
  if (strategy === "move") {
45387
- const from = path16.join(root, fromRel);
47059
+ const from = path25.join(root, fromRel);
45388
47060
  assertPathInside(root, from);
45389
47061
  const moved = context.movedRestore?.get(entry.seq);
45390
47062
  if (moved) {
45391
- await fs15.mkdir(path16.dirname(moved.from), { recursive: true });
47063
+ await fs24.mkdir(path25.dirname(moved.from), { recursive: true });
45392
47064
  await restoreResourceBytesStrict(moved.from, root, moved.bytes, fromRel);
45393
47065
  } else {
45394
47066
  const { backupRef, sourceSha256 } = entry.detail;
@@ -45407,7 +47079,7 @@ async function undoStep(entry, context) {
45407
47079
  sha256: sourceSha256
45408
47080
  });
45409
47081
  const backupRoot = await prepareBackupRoot(backupJournalPath);
45410
- const backupPath = path16.join(backupRoot, backupRef);
47082
+ const backupPath = path25.join(backupRoot, backupRef);
45411
47083
  let backup;
45412
47084
  try {
45413
47085
  backup = await readResourceBytesStrict(
@@ -45426,7 +47098,7 @@ async function undoStep(entry, context) {
45426
47098
  `move rollback backup hash mismatch for step ${entry.seq}: ${backupRef}`
45427
47099
  );
45428
47100
  }
45429
- await fs15.mkdir(path16.dirname(from), { recursive: true });
47101
+ await fs24.mkdir(path25.dirname(from), { recursive: true });
45430
47102
  await restoreResourceBytesStrict(from, root, backup, fromRel);
45431
47103
  }
45432
47104
  }
@@ -45438,7 +47110,7 @@ async function undoStep(entry, context) {
45438
47110
  );
45439
47111
  }
45440
47112
  await removeCreatedDirectory(
45441
- path16.join(root, directoryName),
47113
+ path25.join(root, directoryName),
45442
47114
  root,
45443
47115
  directoryName,
45444
47116
  context.targetTrees.get(directoryName),
@@ -45469,17 +47141,17 @@ function coerceYamlScalar(raw) {
45469
47141
  return raw;
45470
47142
  }
45471
47143
  async function scanUnfinishedJournals(storeDir) {
45472
- const journalDir = path16.join(storeDir, "journal");
47144
+ const journalDir = path25.join(storeDir, "journal");
45473
47145
  let files;
45474
47146
  try {
45475
- files = await fs15.readdir(journalDir);
47147
+ files = await fs24.readdir(journalDir);
45476
47148
  } catch {
45477
47149
  return [];
45478
47150
  }
45479
47151
  const unfinished = [];
45480
47152
  for (const file of files) {
45481
47153
  if (!file.endsWith(".jsonl") || file.endsWith("-rollback.jsonl")) continue;
45482
- const journalPath = path16.join(journalDir, file);
47154
+ const journalPath = path25.join(journalDir, file);
45483
47155
  const proposalId = file.replace(/\.jsonl$/, "");
45484
47156
  let entries;
45485
47157
  try {
@@ -45504,7 +47176,7 @@ async function scanUnfinishedJournals(storeDir) {
45504
47176
  // src/daemon/steward/approval-service.ts
45505
47177
  function fingerprintOf(proposal) {
45506
47178
  const canonical = JSON.stringify(proposal.patch, Object.keys(proposal.patch).sort());
45507
- return `sha256:${createHash8("sha256").update(canonical).digest("hex")}`;
47179
+ return `sha256:${createHash11("sha256").update(canonical).digest("hex")}`;
45508
47180
  }
45509
47181
  function createStewardApprovalService(deps) {
45510
47182
  const proposals = /* @__PURE__ */ new Map();
@@ -45574,7 +47246,7 @@ function createStewardApprovalService(deps) {
45574
47246
  const targets = entry.proposal.patch.kind === "split" ? entry.proposal.patch.targets.map((target) => target.directoryName) : [entry.proposal.patch.target.directoryName];
45575
47247
  let conflict = false;
45576
47248
  for (const name of targets) {
45577
- const exists = await (await import("node:fs/promises")).stat(path17.join(root, name)).then(() => true).catch(() => false);
47249
+ const exists = await (await import("node:fs/promises")).stat(path26.join(root, name)).then(() => true).catch(() => false);
45578
47250
  if (exists) {
45579
47251
  conflict = true;
45580
47252
  checks.push({
@@ -45659,7 +47331,7 @@ function createStewardApprovalService(deps) {
45659
47331
  skills: deps.skills,
45660
47332
  creator: deps.creator,
45661
47333
  store: deps.store,
45662
- journalPath: path17.join(stewardStoreDir(), "journal", `${proposalId}.jsonl`)
47334
+ journalPath: path26.join(stewardStoreDir(), "journal", `${proposalId}.jsonl`)
45663
47335
  });
45664
47336
  const audit = {
45665
47337
  id: StewardAuditIdSchema.parse(`aud_${randomBytes10(8).toString("hex")}`),
@@ -45691,7 +47363,7 @@ function createStewardApprovalService(deps) {
45691
47363
  const entry = requireProposal(audit.proposalId);
45692
47364
  const patch = entry.proposal.patch;
45693
47365
  if (patch.kind === "disable") {
45694
- const journalPathForProbe = path17.join(
47366
+ const journalPathForProbe = path26.join(
45695
47367
  stewardStoreDir(),
45696
47368
  "journal",
45697
47369
  `${audit.proposalId}.jsonl`
@@ -45871,7 +47543,7 @@ function createStewardApprovalService(deps) {
45871
47543
  if (fingerprintOf(entry.proposal) !== grant.fingerprint) {
45872
47544
  throw new DomainError("CONFLICT", "Patch fingerprint drifted since the rollback grant.");
45873
47545
  }
45874
- const journalPath = path17.join(stewardStoreDir(), "journal", `${audit.proposalId}.jsonl`);
47546
+ const journalPath = path26.join(stewardStoreDir(), "journal", `${audit.proposalId}.jsonl`);
45875
47547
  const root = deps.workspaces.resolveWritable(entry.snapshot.target).directory;
45876
47548
  const mutations = [];
45877
47549
  try {
@@ -45889,7 +47561,7 @@ function createStewardApprovalService(deps) {
45889
47561
  skills: deps.skills,
45890
47562
  creator: deps.creator,
45891
47563
  store: deps.store,
45892
- journalPath: path17.join(stewardStoreDir(), "journal", `${auditId}-rollback.jsonl`)
47564
+ journalPath: path26.join(stewardStoreDir(), "journal", `${auditId}-rollback.jsonl`)
45893
47565
  },
45894
47566
  root,
45895
47567
  mutations,
@@ -46005,7 +47677,7 @@ function createCapabilityRegistry(definitions) {
46005
47677
  }
46006
47678
 
46007
47679
  // src/daemon/steward/capabilities.ts
46008
- import { z as z20 } from "zod";
47680
+ import { z as z25 } from "zod";
46009
47681
  function ok(value) {
46010
47682
  return { kind: "ok", value };
46011
47683
  }
@@ -46029,11 +47701,11 @@ function parseAuditIdInput(input) {
46029
47701
  const value = input.auditId;
46030
47702
  return typeof value === "string" && value.length > 0 ? value : null;
46031
47703
  }
46032
- var listInputSchema = z20.unknown();
46033
- var skillIdInputSchema = z20.object({ skillId: SkillIdSchema });
46034
- var proposeInputSchema = z20.object({ proposal: SkillProposalSchema });
46035
- var proposalIdInputSchema = z20.object({ proposalId: StewardProposalIdSchema });
46036
- var auditIdInputSchema = z20.object({ auditId: z20.string().min(1) });
47704
+ var listInputSchema = z25.unknown();
47705
+ var skillIdInputSchema = z25.object({ skillId: SkillIdSchema });
47706
+ var proposeInputSchema = z25.object({ proposal: SkillProposalSchema });
47707
+ var proposalIdInputSchema = z25.object({ proposalId: StewardProposalIdSchema });
47708
+ var auditIdInputSchema = z25.object({ auditId: z25.string().min(1) });
46037
47709
  function createStewardCapabilities(deps) {
46038
47710
  const { snapshot, proposals, validate: validate3, apply, rollback } = deps;
46039
47711
  return [
@@ -46711,7 +48383,7 @@ function createSkillStewardPipelineService(deps) {
46711
48383
 
46712
48384
  // src/daemon/steward/dsh-settings.ts
46713
48385
  import { promises as fsp } from "node:fs";
46714
- import path20 from "node:path";
48386
+ import path29 from "node:path";
46715
48387
  import { normalizeApiKey } from "@deepseek-ai/dsh-llm";
46716
48388
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
46717
48389
 
@@ -46950,7 +48622,7 @@ function dshBridgeModelEntry(model) {
46950
48622
  }
46951
48623
  async function syncDshModelRoutes(routes) {
46952
48624
  const dshHome = resolveDefaultDshHome();
46953
- const file = path20.join(dshHome, "settings.yaml");
48625
+ const file = path29.join(dshHome, "settings.yaml");
46954
48626
  let doc = {};
46955
48627
  try {
46956
48628
  const raw = await fsp.readFile(file, "utf8");
@@ -46983,7 +48655,7 @@ async function syncDshModelRoutes(routes) {
46983
48655
  async function syncDshRouteCredential(provider, apiKey) {
46984
48656
  const ref = dshRouteApiKeyEnv(provider);
46985
48657
  const dshHome = resolveDefaultDshHome();
46986
- const file = path20.join(dshHome, ".credentials.yaml");
48658
+ const file = path29.join(dshHome, ".credentials.yaml");
46987
48659
  let doc = {};
46988
48660
  try {
46989
48661
  const parsed = parseYaml(await fsp.readFile(file, "utf8"));
@@ -47015,10 +48687,10 @@ async function syncDshRouteCredential(provider, apiKey) {
47015
48687
  }
47016
48688
  }
47017
48689
  function settingsFile() {
47018
- return path20.join(homeDir(), "steward-store", "dsh-settings.json");
48690
+ return path29.join(homeDir(), "steward-store", "dsh-settings.json");
47019
48691
  }
47020
48692
  function credentialsFile() {
47021
- return path20.join(homeDir(), "steward-store", "dsh-credentials.json");
48693
+ return path29.join(homeDir(), "steward-store", "dsh-credentials.json");
47022
48694
  }
47023
48695
  async function readPrivateJson(file) {
47024
48696
  let raw;
@@ -47290,7 +48962,7 @@ function createDshSettingsService(options2 = {}) {
47290
48962
  // src/daemon/kernel/agent-sessions.ts
47291
48963
  import { randomUUID as randomUUID2 } from "node:crypto";
47292
48964
  import { createUserMessage as createUserMessage2 } from "@deepseek-ai/dsh-llm";
47293
- import { z as z22 } from "zod";
48965
+ import { z as z27 } from "zod";
47294
48966
  init_agent_roles();
47295
48967
  init_dsh_kernel();
47296
48968
 
@@ -47384,6 +49056,8 @@ var AGENT_MODES = {
47384
49056
  tools: [
47385
49057
  "workspace_list",
47386
49058
  "skills_list",
49059
+ "skills_search",
49060
+ "skills_duplicates",
47387
49061
  "skills_info",
47388
49062
  "skills_validate",
47389
49063
  "creator_load",
@@ -47397,6 +49071,8 @@ var AGENT_MODES = {
47397
49071
  tools: [
47398
49072
  "workspace_list",
47399
49073
  "skills_list",
49074
+ "skills_search",
49075
+ "skills_duplicates",
47400
49076
  "skills_info",
47401
49077
  "skills_validate",
47402
49078
  "skills_toggle",
@@ -47412,6 +49088,8 @@ var AGENT_MODES = {
47412
49088
  tools: [
47413
49089
  "workspace_list",
47414
49090
  "skills_list",
49091
+ "skills_search",
49092
+ "skills_duplicates",
47415
49093
  "skills_info",
47416
49094
  "skills_validate",
47417
49095
  "repository_scan",
@@ -47587,11 +49265,11 @@ async function runBootSessionCleanup(deps) {
47587
49265
 
47588
49266
  // src/daemon/kernel/agent-queue.ts
47589
49267
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
47590
- import { z as z21 } from "zod";
47591
- var InboxMessageSchema = z21.object({
47592
- id: z21.string().min(1),
47593
- source: z21.unknown().optional(),
47594
- content: z21.array(z21.object({ type: z21.string().min(1), text: z21.string().optional() }).passthrough()).min(1)
49268
+ import { z as z26 } from "zod";
49269
+ var InboxMessageSchema = z26.object({
49270
+ id: z26.string().min(1),
49271
+ source: z26.unknown().optional(),
49272
+ content: z26.array(z26.object({ type: z26.string().min(1), text: z26.string().optional() }).passthrough()).min(1)
47595
49273
  }).passthrough();
47596
49274
  function parseInboxMessage(raw) {
47597
49275
  const checked = InboxMessageSchema.safeParse(raw);
@@ -47633,8 +49311,8 @@ function rebuildEditedMessage(original, text) {
47633
49311
  }
47634
49312
 
47635
49313
  // src/daemon/kernel/session-transcripts.ts
47636
- import fs18 from "node:fs";
47637
- import path21 from "node:path";
49314
+ import fs27 from "node:fs";
49315
+ import path30 from "node:path";
47638
49316
  function parseMeta(raw) {
47639
49317
  if (typeof raw !== "object" || raw === null) return null;
47640
49318
  const meta = raw;
@@ -47661,22 +49339,22 @@ function createSessionTranscripts(rootDir) {
47661
49339
  let hydrated = false;
47662
49340
  function sessionDir(meta) {
47663
49341
  const [y, m, d] = dateSegments(meta.createdAt);
47664
- return path21.join(rootDir, y, m, d, meta.sessionId);
49342
+ return path30.join(rootDir, y, m, d, meta.sessionId);
47665
49343
  }
47666
49344
  function hydrate() {
47667
49345
  if (hydrated) return;
47668
49346
  hydrated = true;
47669
49347
  let years = [];
47670
49348
  try {
47671
- years = fs18.readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^\d{4}$/.test(entry.name)).map((entry) => entry.name);
49349
+ years = fs27.readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^\d{4}$/.test(entry.name)).map((entry) => entry.name);
47672
49350
  } catch {
47673
49351
  return;
47674
49352
  }
47675
49353
  for (const year of years) {
47676
- for (const month of safeDirs(path21.join(rootDir, year))) {
47677
- for (const day of safeDirs(path21.join(rootDir, year, month))) {
47678
- for (const sid of safeDirs(path21.join(rootDir, year, month, day))) {
47679
- if (!dirs.has(sid)) dirs.set(sid, path21.join(rootDir, year, month, day, sid));
49354
+ for (const month of safeDirs(path30.join(rootDir, year))) {
49355
+ for (const day of safeDirs(path30.join(rootDir, year, month))) {
49356
+ for (const sid of safeDirs(path30.join(rootDir, year, month, day))) {
49357
+ if (!dirs.has(sid)) dirs.set(sid, path30.join(rootDir, year, month, day, sid));
47680
49358
  }
47681
49359
  }
47682
49360
  }
@@ -47684,35 +49362,35 @@ function createSessionTranscripts(rootDir) {
47684
49362
  }
47685
49363
  function safeDirs(dir) {
47686
49364
  try {
47687
- return fs18.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
49365
+ return fs27.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
47688
49366
  } catch {
47689
49367
  return [];
47690
49368
  }
47691
49369
  }
47692
49370
  function pruneEmptyDateDirs() {
47693
49371
  for (const year of safeDirs(rootDir)) {
47694
- const yearDir = path21.join(rootDir, year);
49372
+ const yearDir = path30.join(rootDir, year);
47695
49373
  for (const month of safeDirs(yearDir)) {
47696
- const monthDir = path21.join(yearDir, month);
49374
+ const monthDir = path30.join(yearDir, month);
47697
49375
  for (const day of safeDirs(monthDir)) {
47698
- const dayDir = path21.join(monthDir, day);
49376
+ const dayDir = path30.join(monthDir, day);
47699
49377
  if (safeDirs(dayDir).length === 0) {
47700
49378
  try {
47701
- fs18.rmdirSync(dayDir);
49379
+ fs27.rmdirSync(dayDir);
47702
49380
  } catch {
47703
49381
  }
47704
49382
  }
47705
49383
  }
47706
49384
  if (safeDirs(monthDir).length === 0) {
47707
49385
  try {
47708
- fs18.rmdirSync(monthDir);
49386
+ fs27.rmdirSync(monthDir);
47709
49387
  } catch {
47710
49388
  }
47711
49389
  }
47712
49390
  }
47713
49391
  if (safeDirs(yearDir).length === 0) {
47714
49392
  try {
47715
- fs18.rmdirSync(yearDir);
49393
+ fs27.rmdirSync(yearDir);
47716
49394
  } catch {
47717
49395
  }
47718
49396
  }
@@ -47722,12 +49400,12 @@ function createSessionTranscripts(rootDir) {
47722
49400
  recordStart(meta) {
47723
49401
  try {
47724
49402
  const dir = sessionDir(meta);
47725
- fs18.mkdirSync(dir, { recursive: true, mode: 448 });
47726
- const target = path21.join(dir, "meta.json");
49403
+ fs27.mkdirSync(dir, { recursive: true, mode: 448 });
49404
+ const target = path30.join(dir, "meta.json");
47727
49405
  const tmp = `${target}.tmp`;
47728
- fs18.writeFileSync(tmp, `${JSON.stringify(meta)}
49406
+ fs27.writeFileSync(tmp, `${JSON.stringify(meta)}
47729
49407
  `, { mode: 384 });
47730
- fs18.renameSync(tmp, target);
49408
+ fs27.renameSync(tmp, target);
47731
49409
  dirs.set(meta.sessionId, dir);
47732
49410
  } catch (error) {
47733
49411
  console.error(`[session-transcripts] recordStart failed for ${meta.sessionId}:`, error);
@@ -47737,7 +49415,7 @@ function createSessionTranscripts(rootDir) {
47737
49415
  try {
47738
49416
  const dir = dirs.get(sessionId);
47739
49417
  if (dir === void 0) return;
47740
- fs18.appendFileSync(path21.join(dir, "frames.jsonl"), `${JSON.stringify(frame)}
49418
+ fs27.appendFileSync(path30.join(dir, "frames.jsonl"), `${JSON.stringify(frame)}
47741
49419
  `, {
47742
49420
  mode: 384
47743
49421
  });
@@ -47749,14 +49427,14 @@ function createSessionTranscripts(rootDir) {
47749
49427
  const dir = dirs.get(sessionId);
47750
49428
  if (dir === void 0) return false;
47751
49429
  try {
47752
- const meta = parseMeta(JSON.parse(fs18.readFileSync(path21.join(dir, "meta.json"), "utf8")));
49430
+ const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
47753
49431
  if (meta === null || meta.mode === mode) return meta !== null;
47754
49432
  const next = { ...meta, mode };
47755
- const target = path21.join(dir, "meta.json");
49433
+ const target = path30.join(dir, "meta.json");
47756
49434
  const tmp = `${target}.tmp`;
47757
- fs18.writeFileSync(tmp, `${JSON.stringify(next)}
49435
+ fs27.writeFileSync(tmp, `${JSON.stringify(next)}
47758
49436
  `, { mode: 384 });
47759
- fs18.renameSync(tmp, target);
49437
+ fs27.renameSync(tmp, target);
47760
49438
  return true;
47761
49439
  } catch (error) {
47762
49440
  console.error(`[session-transcripts] updateMode failed for ${sessionId}:`, error);
@@ -47769,14 +49447,14 @@ function createSessionTranscripts(rootDir) {
47769
49447
  const dir = dirs.get(sessionId);
47770
49448
  if (dir === void 0) return false;
47771
49449
  try {
47772
- const meta = parseMeta(JSON.parse(fs18.readFileSync(path21.join(dir, "meta.json"), "utf8")));
49450
+ const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
47773
49451
  if (meta === null || meta.title === trimmed) return meta !== null;
47774
49452
  const next = { ...meta, title: trimmed };
47775
- const target = path21.join(dir, "meta.json");
49453
+ const target = path30.join(dir, "meta.json");
47776
49454
  const tmp = `${target}.tmp`;
47777
- fs18.writeFileSync(tmp, `${JSON.stringify(next)}
49455
+ fs27.writeFileSync(tmp, `${JSON.stringify(next)}
47778
49456
  `, { mode: 384 });
47779
- fs18.renameSync(tmp, target);
49457
+ fs27.renameSync(tmp, target);
47780
49458
  return true;
47781
49459
  } catch (error) {
47782
49460
  console.error(`[session-transcripts] updateTitle failed for ${sessionId}:`, error);
@@ -47788,7 +49466,7 @@ function createSessionTranscripts(rootDir) {
47788
49466
  const metas = [];
47789
49467
  for (const dir of dirs.values()) {
47790
49468
  try {
47791
- const meta = parseMeta(JSON.parse(fs18.readFileSync(path21.join(dir, "meta.json"), "utf8")));
49469
+ const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
47792
49470
  if (meta !== null) metas.push(meta);
47793
49471
  } catch {
47794
49472
  }
@@ -47802,7 +49480,7 @@ function createSessionTranscripts(rootDir) {
47802
49480
  if (dir === void 0) return [];
47803
49481
  let lines = [];
47804
49482
  try {
47805
- lines = fs18.readFileSync(path21.join(dir, "frames.jsonl"), "utf8").split("\n");
49483
+ lines = fs27.readFileSync(path30.join(dir, "frames.jsonl"), "utf8").split("\n");
47806
49484
  } catch {
47807
49485
  return [];
47808
49486
  }
@@ -47822,7 +49500,7 @@ function createSessionTranscripts(rootDir) {
47822
49500
  hydrate();
47823
49501
  const buckets = /* @__PURE__ */ new Map();
47824
49502
  for (const [sessionId, dir] of dirs) {
47825
- const segments = path21.relative(rootDir, dir).split(path21.sep);
49503
+ const segments = path30.relative(rootDir, dir).split(path30.sep);
47826
49504
  if (segments.length !== 4) continue;
47827
49505
  const [year, month, day] = segments;
47828
49506
  if (!/^\d{4}$/.test(year) || !/^\d{2}$/.test(month) || !/^\d{2}$/.test(day)) continue;
@@ -47840,7 +49518,7 @@ function createSessionTranscripts(rootDir) {
47840
49518
  const dir = dirs.get(sessionId);
47841
49519
  if (dir === void 0) return false;
47842
49520
  try {
47843
- fs18.rmSync(dir, { recursive: true, force: true });
49521
+ fs27.rmSync(dir, { recursive: true, force: true });
47844
49522
  } catch (error) {
47845
49523
  console.error(`[session-transcripts] remove failed for ${sessionId}:`, error);
47846
49524
  return false;
@@ -47863,35 +49541,35 @@ function summaryOfMeta(meta) {
47863
49541
  }
47864
49542
 
47865
49543
  // src/daemon/kernel/agent-sessions.ts
47866
- var AgentChunkEventSchema = z22.object({
47867
- chunk: z22.object({
47868
- type: z22.string().min(1),
47869
- text: z22.string().optional(),
47870
- id: z22.string().optional(),
47871
- name: z22.string().optional(),
47872
- argumentsDelta: z22.string().optional()
49544
+ var AgentChunkEventSchema = z27.object({
49545
+ chunk: z27.object({
49546
+ type: z27.string().min(1),
49547
+ text: z27.string().optional(),
49548
+ id: z27.string().optional(),
49549
+ name: z27.string().optional(),
49550
+ argumentsDelta: z27.string().optional()
47873
49551
  })
47874
49552
  });
47875
- var TodoWriteEventSchema = z22.object({
47876
- todos: z22.array(
47877
- z22.object({
47878
- content: z22.string(),
47879
- status: z22.string().min(1)
49553
+ var TodoWriteEventSchema = z27.object({
49554
+ todos: z27.array(
49555
+ z27.object({
49556
+ content: z27.string(),
49557
+ status: z27.string().min(1)
47880
49558
  })
47881
49559
  )
47882
49560
  });
47883
- var MessageSourceSchema = z22.object({
47884
- kind: z22.string().optional(),
47885
- callId: z22.string().optional()
49561
+ var MessageSourceSchema = z27.object({
49562
+ kind: z27.string().optional(),
49563
+ callId: z27.string().optional()
47886
49564
  }).passthrough();
47887
- var MessageBlockSchema = z22.object({
47888
- type: z22.string().min(1),
47889
- text: z22.string().optional(),
47890
- name: z22.string().optional()
49565
+ var MessageBlockSchema = z27.object({
49566
+ type: z27.string().min(1),
49567
+ text: z27.string().optional(),
49568
+ name: z27.string().optional()
47891
49569
  }).passthrough();
47892
- var MessageShapeSchema = z22.object({
49570
+ var MessageShapeSchema = z27.object({
47893
49571
  source: MessageSourceSchema.optional(),
47894
- content: z22.array(MessageBlockSchema).min(1)
49572
+ content: z27.array(MessageBlockSchema).min(1)
47895
49573
  }).passthrough();
47896
49574
  function messageEnvelopeOf(raw) {
47897
49575
  if (typeof raw === "object" && raw !== null && "message" in raw) {
@@ -47900,29 +49578,29 @@ function messageEnvelopeOf(raw) {
47900
49578
  }
47901
49579
  return raw;
47902
49580
  }
47903
- var MessageEventSchema = z22.preprocess(messageEnvelopeOf, MessageShapeSchema);
47904
- var TurnEndEventSchema = z22.object({
47905
- reason: z22.object({ kind: z22.string().optional() }).passthrough().optional()
49581
+ var MessageEventSchema = z27.preprocess(messageEnvelopeOf, MessageShapeSchema);
49582
+ var TurnEndEventSchema = z27.object({
49583
+ reason: z27.object({ kind: z27.string().optional() }).passthrough().optional()
47906
49584
  }).passthrough();
47907
- var SessionTitleEventSchema = z22.object({
47908
- title: z22.string().optional()
49585
+ var SessionTitleEventSchema = z27.object({
49586
+ title: z27.string().optional()
47909
49587
  }).passthrough();
47910
- var ToolCallEventSchema = z22.object({
47911
- callId: z22.string().min(1),
47912
- name: z22.string().min(1),
47913
- arguments: z22.string()
49588
+ var ToolCallEventSchema = z27.object({
49589
+ callId: z27.string().min(1),
49590
+ name: z27.string().min(1),
49591
+ arguments: z27.string()
47914
49592
  }).passthrough();
47915
- var ToolResultEventSchema = z22.object({
47916
- message: z22.object({
47917
- source: z22.object({ kind: z22.literal("tool"), callId: z22.string().min(1) }).passthrough(),
47918
- content: z22.tuple([
47919
- z22.object({
47920
- type: z22.literal("tool-result"),
47921
- toolCallId: z22.string().min(1),
47922
- content: z22.array(
47923
- z22.object({
47924
- type: z22.string().min(1),
47925
- text: z22.string().optional()
49593
+ var ToolResultEventSchema = z27.object({
49594
+ message: z27.object({
49595
+ source: z27.object({ kind: z27.literal("tool"), callId: z27.string().min(1) }).passthrough(),
49596
+ content: z27.tuple([
49597
+ z27.object({
49598
+ type: z27.literal("tool-result"),
49599
+ toolCallId: z27.string().min(1),
49600
+ content: z27.array(
49601
+ z27.object({
49602
+ type: z27.string().min(1),
49603
+ text: z27.string().optional()
47926
49604
  }).passthrough()
47927
49605
  ).min(1).refine(
47928
49606
  (parts) => parts.some(
@@ -47934,15 +49612,15 @@ var ToolResultEventSchema = z22.object({
47934
49612
  ])
47935
49613
  }).passthrough()
47936
49614
  });
47937
- var TurnStartEventSchema = z22.record(z22.string(), z22.unknown());
47938
- var AgentStatusEventSchema = z22.record(z22.string(), z22.unknown());
47939
- var SubagentCatalogEventSchema = z22.object({
47940
- childId: z22.string().min(1),
47941
- childCreatedAt: z22.number().optional(),
47942
- mode: z22.enum(["one-shot", "continuable"]),
47943
- label: z22.string().optional()
49615
+ var TurnStartEventSchema = z27.record(z27.string(), z27.unknown());
49616
+ var AgentStatusEventSchema = z27.record(z27.string(), z27.unknown());
49617
+ var SubagentCatalogEventSchema = z27.object({
49618
+ childId: z27.string().min(1),
49619
+ childCreatedAt: z27.number().optional(),
49620
+ mode: z27.enum(["one-shot", "continuable"]),
49621
+ label: z27.string().optional()
47944
49622
  });
47945
- var UsageEnvelopeSchema = z22.object({ usage: z22.record(z22.string(), z22.unknown()).optional() }).passthrough();
49623
+ var UsageEnvelopeSchema = z27.object({ usage: z27.record(z27.string(), z27.unknown()).optional() }).passthrough();
47946
49624
  var DROPPED_EVENT_LOG_MAX = 200;
47947
49625
  function logDroppedEvent(sessionId, type, data) {
47948
49626
  let detail;
@@ -48996,9 +50674,9 @@ function productToolDenyList(globalNames, mode) {
48996
50674
  }
48997
50675
 
48998
50676
  // src/daemon/model-catalog.ts
48999
- import fs19 from "node:fs";
49000
- import path22 from "node:path";
49001
- import { createRequire as createRequire5 } from "node:module";
50677
+ import fs28 from "node:fs";
50678
+ import path31 from "node:path";
50679
+ import { createRequire as createRequire6 } from "node:module";
49002
50680
 
49003
50681
  // src/shared/provider-icons.generated.ts
49004
50682
  var PROVIDER_ICONS = {
@@ -49065,13 +50743,13 @@ function labelOf(provider) {
49065
50743
  return cn ? `${pretty} \xB7 CN` : pretty;
49066
50744
  }
49067
50745
  function resolveCatalogDataDir() {
49068
- const require2 = createRequire5(import.meta.url);
50746
+ const require2 = createRequire6(import.meta.url);
49069
50747
  const basePkg = require2.resolve("@deepseek-ai/dsh-base/package.json");
49070
- const baseNm = path22.dirname(path22.dirname(path22.dirname(basePkg)));
49071
- const hostReal = fs19.realpathSync(path22.join(baseNm, "@deepseek-ai/dsh-llm-pi-ai"));
49072
- const piAi = path22.join(path22.dirname(hostReal), "../@earendil-works/pi-ai");
49073
- const dataDir = path22.join(piAi, "dist", "providers", "data");
49074
- if (!fs19.existsSync(dataDir)) {
50748
+ const baseNm = path31.dirname(path31.dirname(path31.dirname(basePkg)));
50749
+ const hostReal = fs28.realpathSync(path31.join(baseNm, "@deepseek-ai/dsh-llm-pi-ai"));
50750
+ const piAi = path31.join(path31.dirname(hostReal), "../@earendil-works/pi-ai");
50751
+ const dataDir = path31.join(piAi, "dist", "providers", "data");
50752
+ if (!fs28.existsSync(dataDir)) {
49075
50753
  throw new DomainError("UNAVAILABLE", `pi-ai catalog data not found at ${dataDir}`);
49076
50754
  }
49077
50755
  return dataDir;
@@ -49112,13 +50790,13 @@ function listModelProviders() {
49112
50790
  if (cached !== null) return cached;
49113
50791
  const dataDir = resolveCatalogDataDir();
49114
50792
  const entries = [];
49115
- for (const file of fs19.readdirSync(dataDir)) {
50793
+ for (const file of fs28.readdirSync(dataDir)) {
49116
50794
  if (!file.endsWith(".json")) continue;
49117
50795
  const provider = file.slice(0, -".json".length);
49118
50796
  if (EXCLUDED_PROVIDERS.has(provider)) continue;
49119
50797
  let parsed;
49120
50798
  try {
49121
- parsed = JSON.parse(fs19.readFileSync(path22.join(dataDir, file), "utf8"));
50799
+ parsed = JSON.parse(fs28.readFileSync(path31.join(dataDir, file), "utf8"));
49122
50800
  } catch {
49123
50801
  continue;
49124
50802
  }
@@ -49173,7 +50851,7 @@ function createModelCatalogService() {
49173
50851
  }
49174
50852
 
49175
50853
  // src/daemon/capability/domain-capabilities.ts
49176
- import { z as z23 } from "zod";
50854
+ import { z as z28 } from "zod";
49177
50855
  function toResult(error) {
49178
50856
  if (error instanceof DomainError) {
49179
50857
  return { kind: "failed", code: error.code, message: error.message };
@@ -49194,7 +50872,7 @@ async function invoke(action) {
49194
50872
  return toResult(error);
49195
50873
  }
49196
50874
  }
49197
- var none = z23.object({});
50875
+ var none = z28.object({});
49198
50876
  function createDomainCapabilities(domain) {
49199
50877
  return [
49200
50878
  {
@@ -49272,6 +50950,41 @@ function createDomainCapabilities(domain) {
49272
50950
  return domain.skills.validate(parsed, parsed.skillId);
49273
50951
  })
49274
50952
  },
50953
+ {
50954
+ name: "skills.search",
50955
+ description: "Search installed local skills across all workspaces; returns stable skill ids with workspace/provider scope.",
50956
+ authority: "readonly",
50957
+ input: SkillsSearchInputSchema,
50958
+ handler: (input) => invoke(async () => {
50959
+ const parsed = SkillsSearchInputSchema.parse(input);
50960
+ return {
50961
+ results: await domain.skillSearch.search(parsed.query, {
50962
+ limit: parsed.limit ?? 10
50963
+ })
50964
+ };
50965
+ })
50966
+ },
50967
+ {
50968
+ name: "skills.duplicates",
50969
+ description: "List content-duplicate skill groups from the search index (contentHash groups with more than one canonical entry).",
50970
+ authority: "readonly",
50971
+ input: z28.object({}).strict(),
50972
+ handler: (input) => invoke(async () => {
50973
+ z28.object({}).strict().parse(input);
50974
+ return { groups: await domain.skillSearch.duplicates() };
50975
+ })
50976
+ },
50977
+ {
50978
+ name: "skills.search_config.open",
50979
+ description: "Open the server-owned skill search config file (search-config.toml) with the system default editor.",
50980
+ authority: "readonly",
50981
+ input: z28.object({}).strict(),
50982
+ handler: (input) => invoke(async () => {
50983
+ z28.object({}).strict().parse(input);
50984
+ await domain.searchConfigOpener(searchConfigPath());
50985
+ return { opened: true };
50986
+ })
50987
+ },
49275
50988
  {
49276
50989
  name: "skills.update.check",
49277
50990
  description: "Compare skills-CLI lock hashes against upstream (read-only).",
@@ -49598,7 +51311,7 @@ function createMcpProposalStore(registry) {
49598
51311
  }
49599
51312
 
49600
51313
  // src/daemon/steward/jsonrpc-stdio.ts
49601
- import { spawn as spawn5 } from "node:child_process";
51314
+ import { spawn as spawn6 } from "node:child_process";
49602
51315
  function parseFrame2(raw) {
49603
51316
  let value;
49604
51317
  try {
@@ -49797,7 +51510,7 @@ var StdioJsonRpcClient = class {
49797
51510
  }
49798
51511
  };
49799
51512
  function defaultStdioSpawn(command, args, options2) {
49800
- return spawn5(command, args, { cwd: options2.cwd, stdio: ["pipe", "pipe", "pipe"] });
51513
+ return spawn6(command, args, { cwd: options2.cwd, stdio: ["pipe", "pipe", "pipe"] });
49801
51514
  }
49802
51515
  function parseCommandSpec(spec) {
49803
51516
  const trimmed = spec.trim();
@@ -50146,182 +51859,19 @@ function createFixtureHarnessAdapter(options2 = {}) {
50146
51859
  }
50147
51860
 
50148
51861
  // src/daemon/workspace-registry/index.ts
50149
- import fs22 from "node:fs";
50150
- import path27 from "node:path";
50151
-
50152
- // src/daemon/provider-roots.ts
50153
- import fs20 from "node:fs";
50154
- import os5 from "node:os";
50155
- import path23 from "node:path";
50156
- function requireProvider(providerId) {
50157
- const provider = providerCatalogEntry(providerId);
50158
- if (!provider) throw new DomainError("NOT_FOUND", `Provider not found: ${providerId}`);
50159
- return provider;
50160
- }
50161
- function globalProviderRoot(provider) {
50162
- if (provider.globalPath === null) return null;
50163
- const home = os5.homedir();
50164
- const override = globalHomeOverride(provider.id);
50165
- if (override) return path23.join(override, "skills");
50166
- if (provider.id === "openclaw") return openClawRoot(home);
50167
- if (provider.globalPath.startsWith(".config/")) {
50168
- const configHome = process.env.XDG_CONFIG_HOME?.trim() || path23.join(home, ".config");
50169
- return path23.join(configHome, provider.globalPath.slice(".config/".length));
50170
- }
50171
- return path23.join(home, provider.globalPath);
50172
- }
50173
- function importedProviderRoot(workspaceRoot, provider) {
50174
- const root = path23.resolve(workspaceRoot, provider.workspacePath);
50175
- const relative2 = path23.relative(workspaceRoot, root);
50176
- if (relative2.startsWith("..") || path23.isAbsolute(relative2)) {
50177
- throw new Error(`Provider root escaped Workspace: ${provider.id}`);
50178
- }
50179
- return root;
50180
- }
50181
- function providerRootAvailable(root) {
50182
- if (!root) return false;
50183
- try {
50184
- return fs20.statSync(root).isDirectory();
50185
- } catch {
50186
- return false;
50187
- }
50188
- }
50189
- function globalHomeOverride(providerId) {
50190
- const variable = providerId === "codex" ? "CODEX_HOME" : providerId === "claude-code" ? "CLAUDE_CONFIG_DIR" : providerId === "mistral-vibe" ? "VIBE_HOME" : providerId === "hermes-agent" ? "HERMES_HOME" : providerId === "autohand-code" ? "AUTOHAND_HOME" : providerId === "grok" ? "GROK_HOME" : null;
50191
- const value = variable ? process.env[variable]?.trim() : void 0;
50192
- return value || null;
50193
- }
50194
- function openClawRoot(home) {
50195
- for (const directory of [".openclaw", ".clawdbot", ".moltbot"]) {
50196
- if (providerRootAvailable(path23.join(home, directory)))
50197
- return path23.join(home, directory, "skills");
50198
- }
50199
- return path23.join(home, ".openclaw", "skills");
50200
- }
50201
-
50202
- // src/daemon/workspace-registry/persistence.ts
50203
- import fs21 from "node:fs";
50204
- import path25 from "node:path";
50205
-
50206
- // src/daemon/workspace-registry/state.ts
50207
- import path24 from "node:path";
50208
- import { z as z24 } from "zod";
50209
- var StoredWorkspaceSchema = z24.object({
50210
- id: ImportedWorkspaceIdSchema,
50211
- label: z24.string().trim().min(1),
50212
- path: z24.string().min(1).refine(path24.isAbsolute, "Workspace path must be absolute.").refine((value) => path24.normalize(value) === value, "Workspace path must be normalized.")
50213
- }).strict();
50214
- var WorkspaceRegistryStateSchema = z24.object({
50215
- schemaVersion: z24.literal(2),
50216
- activeId: WorkspaceIdSchema,
50217
- workspaces: z24.array(StoredWorkspaceSchema)
50218
- }).strict().superRefine((state, context) => {
50219
- const ids = /* @__PURE__ */ new Set();
50220
- const paths = /* @__PURE__ */ new Set();
50221
- for (const [index, workspace] of state.workspaces.entries()) {
50222
- if (workspace.id !== opaquePathId("ws", workspace.path)) {
50223
- context.addIssue({
50224
- code: "custom",
50225
- message: "Workspace ID does not match its path.",
50226
- path: ["workspaces", index, "id"]
50227
- });
50228
- }
50229
- if (ids.has(workspace.id)) {
50230
- context.addIssue({
50231
- code: "custom",
50232
- message: `Duplicate Workspace ID: ${workspace.id}`,
50233
- path: ["workspaces", index, "id"]
50234
- });
50235
- }
50236
- if (paths.has(workspace.path)) {
50237
- context.addIssue({
50238
- code: "custom",
50239
- message: `Duplicate Workspace path: ${workspace.path}`,
50240
- path: ["workspaces", index, "path"]
50241
- });
50242
- }
50243
- ids.add(workspace.id);
50244
- paths.add(workspace.path);
50245
- }
50246
- if (state.activeId !== GLOBAL_WORKSPACE_ID && !ids.has(state.activeId)) {
50247
- context.addIssue({
50248
- code: "custom",
50249
- message: `Active Workspace is not registered: ${state.activeId}`,
50250
- path: ["activeId"]
50251
- });
50252
- }
50253
- });
50254
- function emptyRegistryState() {
50255
- return { schemaVersion: 2, activeId: GLOBAL_WORKSPACE_ID, workspaces: [] };
50256
- }
50257
- function importWorkspace(state, entry) {
50258
- const existing = state.workspaces.find((workspace) => workspace.path === entry.path);
50259
- if (existing) return { state, entry: existing, changed: false };
50260
- const collision = state.workspaces.find((workspace) => workspace.id === entry.id);
50261
- if (collision) {
50262
- throw new Error(`Workspace ID collision between ${collision.path} and ${entry.path}`);
50263
- }
50264
- return {
50265
- state: {
50266
- ...state,
50267
- activeId: entry.id,
50268
- workspaces: [...state.workspaces, entry]
50269
- },
50270
- entry,
50271
- changed: true
50272
- };
50273
- }
50274
- function forgetWorkspace(state, id) {
50275
- if (!state.workspaces.some((workspace) => workspace.id === id)) return null;
50276
- return {
50277
- ...state,
50278
- activeId: state.activeId === id ? GLOBAL_WORKSPACE_ID : state.activeId,
50279
- workspaces: state.workspaces.filter((workspace) => workspace.id !== id)
50280
- };
50281
- }
50282
- function activateWorkspace(state, id) {
50283
- if (id !== GLOBAL_WORKSPACE_ID && !state.workspaces.some((workspace) => workspace.id === id)) {
50284
- return null;
50285
- }
50286
- if (state.activeId === id) return state;
50287
- return { ...state, activeId: id };
50288
- }
50289
-
50290
- // src/daemon/workspace-registry/persistence.ts
50291
- var WORKSPACES_FILE = "workspaces.json";
50292
- function createWorkspaceRegistryPersistence() {
50293
- const file = path25.join(appDir(), WORKSPACES_FILE);
50294
- return {
50295
- load: () => loadState2(file),
50296
- commit: (state) => commitState2(file, state)
50297
- };
50298
- }
50299
- function loadState2(file) {
50300
- if (!fs21.existsSync(file)) return emptyRegistryState();
50301
- let source;
50302
- try {
50303
- source = fs21.readFileSync(file, "utf8");
50304
- } catch (error) {
50305
- throw new Error(
50306
- `Cannot read workspace registry ${file}: ${error instanceof Error ? error.message : String(error)}`
50307
- );
50308
- }
50309
- return safeParseJson(source, WorkspaceRegistryStateSchema) ?? emptyRegistryState();
50310
- }
50311
- function commitState2(file, state) {
50312
- const validated = WorkspaceRegistryStateSchema.parse(state);
50313
- fs21.mkdirSync(path25.dirname(file), { recursive: true, mode: 448 });
50314
- atomicWriteUtf8(file, `${JSON.stringify(validated, null, 2)}
50315
- `);
50316
- }
51862
+ import fs29 from "node:fs";
51863
+ import path33 from "node:path";
50317
51864
 
50318
51865
  // src/daemon/workspace-registry/projection.ts
50319
- import path26 from "node:path";
51866
+ import os6 from "node:os";
51867
+ import path32 from "node:path";
50320
51868
  function providerCountKey(workspaceId, providerId) {
50321
51869
  return `${workspaceId}:${providerId}`;
50322
51870
  }
50323
- async function countWorkspaceSnapshot(state, countSkills) {
50324
- const tasks = [];
51871
+ var SKIP_PLUGINS_PATH = path32.join(os6.tmpdir(), `.skill-creator-skip-plugins-${process.pid}`);
51872
+ async function scanWorkspaceSnapshot(state, listSkills2) {
51873
+ const counts = /* @__PURE__ */ new Map();
51874
+ const skillKeys = /* @__PURE__ */ new Map();
50325
51875
  const workspaces = [
50326
51876
  { id: GLOBAL_WORKSPACE_ID, directory: null, global: true },
50327
51877
  ...state.workspaces.map((workspace) => ({
@@ -50330,56 +51880,33 @@ async function countWorkspaceSnapshot(state, countSkills) {
50330
51880
  global: false
50331
51881
  }))
50332
51882
  ];
51883
+ const tasks = [];
50333
51884
  for (const workspace of workspaces) {
51885
+ const keySet = /* @__PURE__ */ new Set();
51886
+ skillKeys.set(workspace.id, keySet);
50334
51887
  for (const provider of PROVIDER_CATALOG) {
50335
51888
  const root = workspace.global ? globalProviderRoot(provider) : workspace.directory ? importedProviderRoot(workspace.directory, provider) : null;
50336
51889
  const providerId = provider.id;
50337
- const count = root && providerRootAvailable(root) ? countSkills({
51890
+ const scan2 = root && providerRootAvailable(root) ? listSkills2({
50338
51891
  customDirs: [root],
50339
51892
  customProvider: providerId,
50340
51893
  scanDefaultDirs: false,
50341
- all: true
50342
- }) : Promise.resolve(0);
51894
+ all: true,
51895
+ ...provider.id === "claude-code" ? {} : { claudePluginsFile: SKIP_PLUGINS_PATH, claudePluginsRoot: SKIP_PLUGINS_PATH }
51896
+ }) : Promise.resolve([]);
50343
51897
  tasks.push(
50344
- count.then((value) => [providerCountKey(workspace.id, providerId), value])
51898
+ scan2.then((entries) => {
51899
+ counts.set(providerCountKey(workspace.id, providerId), entries.length);
51900
+ const canonicalRoot = root === null ? "" : safeCanonical(root) ?? root;
51901
+ for (const entry of entries) {
51902
+ keySet.add(`${canonicalRoot}${path32.sep}${entry.directoryName}`);
51903
+ }
51904
+ })
50345
51905
  );
50346
51906
  }
50347
51907
  }
50348
- return new Map(await Promise.all(tasks));
50349
- }
50350
- async function collectWorkspaceSkillKeys(state, listSkills2) {
50351
- const tasks = [];
50352
- const workspaces = [
50353
- { id: GLOBAL_WORKSPACE_ID, directory: null, global: true },
50354
- ...state.workspaces.map((workspace) => ({
50355
- id: workspace.id,
50356
- directory: availableDirectory(workspace.path),
50357
- global: false
50358
- }))
50359
- ];
50360
- for (const workspace of workspaces) {
50361
- const keySet = /* @__PURE__ */ new Set();
50362
- tasks.push(listWorkspaceKeys(workspace, listSkills2, keySet));
50363
- }
50364
- return new Map(await Promise.all(tasks));
50365
- }
50366
- async function listWorkspaceKeys(workspace, listSkills2, keySet) {
50367
- for (const provider of PROVIDER_CATALOG) {
50368
- const root = workspace.global ? globalProviderRoot(provider) : workspace.directory ? importedProviderRoot(workspace.directory, provider) : null;
50369
- if (!root || !providerRootAvailable(root)) continue;
50370
- const providerId = provider.id;
50371
- const entries = await listSkills2({
50372
- customDirs: [root],
50373
- customProvider: providerId,
50374
- scanDefaultDirs: false,
50375
- all: true
50376
- });
50377
- const canonicalRoot = safeCanonical(root) ?? root;
50378
- for (const entry of entries) {
50379
- keySet.add(`${canonicalRoot}${path26.sep}${entry.directoryName}`);
50380
- }
50381
- }
50382
- return [workspace.id, keySet];
51908
+ await Promise.all(tasks);
51909
+ return { counts, skillKeys };
50383
51910
  }
50384
51911
  function projectWorkspaceSnapshot(state, counts, skillKeys) {
50385
51912
  const global3 = projectGlobalWorkspace(state.activeId, counts, skillKeys);
@@ -50461,9 +51988,7 @@ function createWorkspaceRegistry(options2 = {}) {
50461
51988
  const persistence = createWorkspaceRegistryPersistence();
50462
51989
  let state = persistence.load();
50463
51990
  let revision = 0;
50464
- const usingDefaultCounter = options2.countSkills === void 0;
50465
- const countSkills = options2.countSkills ?? countCcskiSkills;
50466
- const listSkills2 = options2.listSkills ?? (usingDefaultCounter ? listCcskiSkills : void 0);
51991
+ const listSkills2 = options2.listSkills ?? listCcskiSkills;
50467
51992
  const commit = (next) => {
50468
51993
  if (next === state) return;
50469
51994
  persistence.commit(next);
@@ -50476,9 +52001,7 @@ function createWorkspaceRegistry(options2 = {}) {
50476
52001
  const observedRevision = revision;
50477
52002
  const snapshot = state;
50478
52003
  try {
50479
- const counts = await countWorkspaceSnapshot(snapshot, countSkills);
50480
- if (revision !== observedRevision) continue;
50481
- const skillKeys = listSkills2 ? await collectWorkspaceSkillKeys(snapshot, listSkills2) : void 0;
52004
+ const { counts, skillKeys } = await scanWorkspaceSnapshot(snapshot, listSkills2);
50482
52005
  if (revision !== observedRevision) continue;
50483
52006
  return projectWorkspaceSnapshot(snapshot, counts, skillKeys);
50484
52007
  } catch (error) {
@@ -50487,17 +52010,17 @@ function createWorkspaceRegistry(options2 = {}) {
50487
52010
  }
50488
52011
  },
50489
52012
  import(directory, label) {
50490
- const resolvedPath = path27.resolve(directory);
50491
- if (!fs22.existsSync(resolvedPath)) {
52013
+ const resolvedPath = path33.resolve(directory);
52014
+ if (!fs29.existsSync(resolvedPath)) {
50492
52015
  throw new DomainError("NOT_FOUND", "Workspace directory was not found.");
50493
52016
  }
50494
- if (!fs22.statSync(resolvedPath).isDirectory()) {
52017
+ if (!fs29.statSync(resolvedPath).isDirectory()) {
50495
52018
  throw new DomainError("INVALID_OPERATION", "Workspace path must be a directory.");
50496
52019
  }
50497
52020
  const canonicalPath = canonicalDirectory(resolvedPath);
50498
52021
  const entry = {
50499
52022
  id: ImportedWorkspaceIdSchema.parse(opaquePathId("ws", canonicalPath)),
50500
- label: label?.trim() || path27.basename(canonicalPath) || canonicalPath,
52023
+ label: label?.trim() || path33.basename(canonicalPath) || canonicalPath,
50501
52024
  path: canonicalPath
50502
52025
  };
50503
52026
  const result = importWorkspace(state, entry);
@@ -50555,12 +52078,9 @@ function createWorkspaceRegistry(options2 = {}) {
50555
52078
  }
50556
52079
  };
50557
52080
  }
50558
- async function countCcskiSkills(options2) {
50559
- return (await listSkills(options2)).length;
50560
- }
50561
52081
  async function listCcskiSkills(options2) {
50562
52082
  const skills = await listSkills(options2);
50563
- return skills.map((skill) => ({ directoryName: path27.basename(skill.path) }));
52083
+ return skills.map((skill) => ({ directoryName: path33.basename(skill.path) }));
50564
52084
  }
50565
52085
  function providerScope(target, workspaceKind, workspaceLabel, directory, includeDisabled) {
50566
52086
  return {
@@ -50592,6 +52112,7 @@ function createDaemonDomain(workspaces = createWorkspaceRegistry(), options2 = {
50592
52112
  const modelCatalog = createModelCatalogService();
50593
52113
  const agentFiles = createAgentFilesService();
50594
52114
  const skillsCliProbe = options2.skillsCliProbe ?? createSkillsCliProbe();
52115
+ void skillsCliProbe.probe().catch(() => void 0);
50595
52116
  const skills = createSkillService(workspaces, { skillsCliProbe });
50596
52117
  const agentSessions = createAgentSessionsService({
50597
52118
  kernel: () => kernelHostRef.handle,
@@ -50627,6 +52148,8 @@ ${info3.content.slice(0, 2e5)}`
50627
52148
  const repository = createRepositoryService(workspaces, skills);
50628
52149
  const creator = createCreatorService(workspaces, skills);
50629
52150
  const skillIntelligence = createSkillIntelligenceService(skills, creator);
52151
+ const searchConfigOpener = options2.searchConfigOpener ?? platformOpenFile;
52152
+ const dialog = options2.dialog ?? createDialogService();
50630
52153
  const domain = {
50631
52154
  workspaces,
50632
52155
  skills,
@@ -50635,6 +52158,9 @@ ${info3.content.slice(0, 2e5)}`
50635
52158
  sourceRegistry: createSourceRegistry(),
50636
52159
  skillsCliProbe,
50637
52160
  skillsUpdate: createSkillsUpdateService(workspaces, skills, skillsCliProbe, repository),
52161
+ skillSearch: createSkillSearchService(),
52162
+ searchConfigOpener,
52163
+ dialog,
50638
52164
  acpBridge: createAcpBridgeService(workspaces),
50639
52165
  skillIntelligence,
50640
52166
  steward: createStewardService(workspaces, skills, skillIntelligence, {
@@ -50666,7 +52192,7 @@ ${info3.content.slice(0, 2e5)}`
50666
52192
  }
50667
52193
 
50668
52194
  // src/daemon/ipc-server.ts
50669
- import fs24 from "node:fs";
52195
+ import fs31 from "node:fs";
50670
52196
  import net2 from "node:net";
50671
52197
 
50672
52198
  // src/shared/socket-liveness.ts
@@ -50693,10 +52219,10 @@ function socketAcceptsConnections(socketPathname, timeoutMs = DEFAULT_SOCKET_PRO
50693
52219
  }
50694
52220
 
50695
52221
  // src/daemon/log.ts
50696
- import fs23 from "node:fs";
52222
+ import fs30 from "node:fs";
50697
52223
  function log(message) {
50698
52224
  try {
50699
- fs23.appendFileSync(daemonLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
52225
+ fs30.appendFileSync(daemonLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
50700
52226
  `);
50701
52227
  } catch {
50702
52228
  }
@@ -50723,8 +52249,8 @@ var IpcServer = class {
50723
52249
  /** 获取单实例 socket,仅清理已确认失活的 Unix 路径。 */
50724
52250
  async start() {
50725
52251
  const directory = runDir();
50726
- fs24.mkdirSync(directory, { recursive: true, mode: 448 });
50727
- if (process.platform !== "win32") fs24.chmodSync(directory, 448);
52252
+ fs31.mkdirSync(directory, { recursive: true, mode: 448 });
52253
+ if (process.platform !== "win32") fs31.chmodSync(directory, 448);
50728
52254
  const server = await this.bindSocket(true);
50729
52255
  if (!server) return false;
50730
52256
  this.server = server;
@@ -50775,7 +52301,7 @@ var IpcServer = class {
50775
52301
  }
50776
52302
  );
50777
52303
  if (result.server) {
50778
- if (process.platform !== "win32") fs24.chmodSync(socket, 384);
52304
+ if (process.platform !== "win32") fs31.chmodSync(socket, 384);
50779
52305
  log(`ipc listening on ${socket}`);
50780
52306
  return result.server;
50781
52307
  }
@@ -50785,7 +52311,7 @@ var IpcServer = class {
50785
52311
  }
50786
52312
  if (!allowStaleCleanup || await socketAcceptsConnections(socket)) return null;
50787
52313
  try {
50788
- fs24.unlinkSync(socket);
52314
+ fs31.unlinkSync(socket);
50789
52315
  } catch (error) {
50790
52316
  const code = error instanceof Error && "code" in error ? error.code : void 0;
50791
52317
  if (code !== "ENOENT") return null;
@@ -50866,9 +52392,9 @@ var IpcServer = class {
50866
52392
  };
50867
52393
 
50868
52394
  // src/daemon/web-server.ts
50869
- import { existsSync as existsSync2, promises as fs25 } from "node:fs";
52395
+ import { existsSync as existsSync2, promises as fs32 } from "node:fs";
50870
52396
  import http from "node:http";
50871
- import path28 from "node:path";
52397
+ import path34 from "node:path";
50872
52398
  import { RPCHandler } from "@orpc/server/ws";
50873
52399
  import { WebSocketServer } from "ws";
50874
52400
 
@@ -50903,6 +52429,21 @@ function createRpcRouter(deps) {
50903
52429
  validate: rpc.skills.validate.handler(
50904
52430
  async ({ input }) => domain.skills.validate(input, input.skillId)
50905
52431
  ),
52432
+ // skill-search-integration C1:daemon 长驻检索单例(索引 IO 故障经错误边界透传)。
52433
+ search: rpc.skills.search.handler(async ({ input }) => ({
52434
+ results: await domain.skillSearch.search(input.query, { limit: input.limit ?? 10 })
52435
+ })),
52436
+ // search-robustness R3:在系统默认编辑器打开 server-owned search-config.toml。
52437
+ searchConfig: {
52438
+ open: rpc.skills.searchConfig.open.handler(async () => {
52439
+ await domain.searchConfigOpener(searchConfigPath());
52440
+ return { opened: true };
52441
+ })
52442
+ },
52443
+ // search-duplicates P1:内容重复组投影(索引事实,排序冻结)。
52444
+ duplicates: rpc.skills.duplicates.handler(async () => ({
52445
+ groups: await domain.skillSearch.duplicates()
52446
+ })),
50906
52447
  update: {
50907
52448
  check: rpc.skills.update.check.handler(async ({ input }) => {
50908
52449
  const discovered = await domain.skills.list(input, true);
@@ -50925,7 +52466,8 @@ function createRpcRouter(deps) {
50925
52466
  })),
50926
52467
  setActive: rpc.workspace.setActive.handler(({ input }) => ({
50927
52468
  activeId: domain.workspaces.activate(input.id)
50928
- }))
52469
+ })),
52470
+ pickDirectory: rpc.workspace.pickDirectory.handler(async () => domain.dialog.pickDirectory())
50929
52471
  },
50930
52472
  creator: {
50931
52473
  save: rpc.creator.save.handler(({ input }) => domain.creator.save(input)),
@@ -51439,19 +52981,19 @@ var WebServer = class {
51439
52981
  async serveStatic(response, pathname) {
51440
52982
  let relativePath = decodeURIComponent(pathname.slice(1));
51441
52983
  if (!relativePath || relativePath.endsWith("/")) relativePath += "index.html";
51442
- const root = path28.resolve(this.options.webuiDir);
51443
- let file = path28.resolve(root, relativePath);
51444
- if (path28.relative(root, file).startsWith("..")) {
52984
+ const root = path34.resolve(this.options.webuiDir);
52985
+ let file = path34.resolve(root, relativePath);
52986
+ if (path34.relative(root, file).startsWith("..")) {
51445
52987
  response.writeHead(403).end();
51446
52988
  return;
51447
52989
  }
51448
- if (!existsSync2(file)) file = path28.join(root, "index.html");
52990
+ if (!existsSync2(file)) file = path34.join(root, "index.html");
51449
52991
  if (!existsSync2(file)) {
51450
52992
  response.writeHead(404).end("not found");
51451
52993
  return;
51452
52994
  }
51453
- const extension = path28.extname(file);
51454
- const data = await fs25.readFile(file);
52995
+ const extension = path34.extname(file);
52996
+ const data = await fs32.readFile(file);
51455
52997
  response.writeHead(200, {
51456
52998
  "content-type": MIME[extension] ?? "application/octet-stream",
51457
52999
  "cache-control": extension === ".html" ? "no-cache" : "public, max-age=3600"
@@ -51461,7 +53003,7 @@ var WebServer = class {
51461
53003
  };
51462
53004
 
51463
53005
  // src/daemon/steward/dsh-session-binder.ts
51464
- import fs26 from "node:fs/promises";
53006
+ import fs33 from "node:fs/promises";
51465
53007
  import {
51466
53008
  createAssistantMessage,
51467
53009
  createToolResultMessage,
@@ -51501,7 +53043,7 @@ function createDshSessionBinder(options2) {
51501
53043
  if (!host) return { ok: false, failure: { kind: "HOST_UNAVAILABLE" } };
51502
53044
  const surface = narrowHostSurface(host.ctx);
51503
53045
  if ("kind" in surface) return { ok: false, failure: surface };
51504
- const canonical = await fs26.realpath(input.workspaceDir);
53046
+ const canonical = await fs33.realpath(input.workspaceDir);
51505
53047
  const workspace = await surface.workspaceRegistry.resolveByPath(canonical) ?? await surface.workspaceRegistry.create(
51506
53048
  canonical,
51507
53049
  input.workspaceTitle ?? canonical.split("/").pop() ?? canonical
@@ -51643,7 +53185,7 @@ function summarizeToolResult(call) {
51643
53185
 
51644
53186
  // src/daemon/mcp/skill-creator-mcp.ts
51645
53187
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
51646
- import { z as z25 } from "zod";
53188
+ import { z as z29 } from "zod";
51647
53189
  function mcpToolName(capabilityName) {
51648
53190
  return capabilityName.replace(/\./g, "_");
51649
53191
  }
@@ -51708,7 +53250,7 @@ function createSkillCreatorMcpServer(deps) {
51708
53250
  proposeName,
51709
53251
  {
51710
53252
  description: `Propose: ${descriptor.description} A human approves it in the Skill Creator UI before execution.`,
51711
- inputSchema: z25.object(shape2)
53253
+ inputSchema: z29.object(shape2)
51712
53254
  },
51713
53255
  async (args) => proposeResult(deps.proposals.create(descriptor.name, args))
51714
53256
  );
@@ -51730,7 +53272,7 @@ function createSkillCreatorMcpServer(deps) {
51730
53272
  if (shape) {
51731
53273
  server.registerTool(
51732
53274
  toolName,
51733
- { description: descriptor.description, inputSchema: z25.object(shape) },
53275
+ { description: descriptor.description, inputSchema: z29.object(shape) },
51734
53276
  async (args) => withCard(
51735
53277
  descriptor.name,
51736
53278
  await deps.capabilities.call(descriptor.name, args, MCP_PRINCIPAL),
@@ -51785,13 +53327,13 @@ function createSkillCreatorMcpServer(deps) {
51785
53327
  }
51786
53328
 
51787
53329
  // src/daemon/tray-host.ts
51788
- import fs28 from "node:fs";
51789
- import path30 from "node:path";
53330
+ import fs35 from "node:fs";
53331
+ import path36 from "node:path";
51790
53332
  import { fileURLToPath as fileURLToPath4 } from "node:url";
51791
53333
 
51792
53334
  // src/daemon/app-icon.ts
51793
- import fs27 from "node:fs";
51794
- import path29 from "node:path";
53335
+ import fs34 from "node:fs";
53336
+ import path35 from "node:path";
51795
53337
  import { fileURLToPath as fileURLToPath3 } from "node:url";
51796
53338
  var LINUX_ICON_SIZES = [16, 32, 48, 64, 128, 256, 512];
51797
53339
  function resolveAppIcon(webuiDir, platform2 = process.platform) {
@@ -51840,7 +53382,7 @@ function resolveAppIcon(webuiDir, platform2 = process.platform) {
51840
53382
  if (platform2 === "linux") {
51841
53383
  const assets = LINUX_ICON_SIZES.flatMap((size) => {
51842
53384
  const iconPath = resolvePackagedIconPath(
51843
- path29.join("linux", `${size}x${size}`, "app-icon.png"),
53385
+ path35.join("linux", `${size}x${size}`, "app-icon.png"),
51844
53386
  webuiDir
51845
53387
  );
51846
53388
  return iconPath === null ? [] : [
@@ -51867,31 +53409,31 @@ function resolveNativeVariantPair(lightFileName, darkFileName, webuiDir) {
51867
53409
  return [lightPath, darkPath];
51868
53410
  }
51869
53411
  function resolveNativeAppIconPath(fileName, webuiDir) {
51870
- const here = path29.dirname(fileURLToPath3(import.meta.url));
53412
+ const here = path35.dirname(fileURLToPath3(import.meta.url));
51871
53413
  const candidates = [
51872
- webuiDir ? path29.join(webuiDir, "icons", "app-icon", fileName) : null,
51873
- path29.join(here, "webui", "icons", "app-icon", fileName),
51874
- path29.join(here, "..", "resources", "app-icon", fileName),
51875
- path29.join(here, "..", "..", "resources", "app-icon", fileName),
51876
- path29.join(process.cwd(), "resources", "app-icon", fileName)
53414
+ webuiDir ? path35.join(webuiDir, "icons", "app-icon", fileName) : null,
53415
+ path35.join(here, "webui", "icons", "app-icon", fileName),
53416
+ path35.join(here, "..", "resources", "app-icon", fileName),
53417
+ path35.join(here, "..", "..", "resources", "app-icon", fileName),
53418
+ path35.join(process.cwd(), "resources", "app-icon", fileName)
51877
53419
  ];
51878
53420
  return firstExistingFile(candidates);
51879
53421
  }
51880
53422
  function resolvePackagedIconPath(fileName, webuiDir) {
51881
- const here = path29.dirname(fileURLToPath3(import.meta.url));
53423
+ const here = path35.dirname(fileURLToPath3(import.meta.url));
51882
53424
  const candidates = [
51883
- webuiDir ? path29.join(webuiDir, "icons", fileName) : null,
51884
- path29.join(here, "webui", "icons", fileName),
51885
- path29.join(here, "..", "..", "webui", "build", "icons", fileName),
51886
- path29.join(here, "..", "..", "webui", "static", "icons", fileName),
51887
- path29.join(process.cwd(), "webui", "build", "icons", fileName),
51888
- path29.join(process.cwd(), "webui", "static", "icons", fileName)
53425
+ webuiDir ? path35.join(webuiDir, "icons", fileName) : null,
53426
+ path35.join(here, "webui", "icons", fileName),
53427
+ path35.join(here, "..", "..", "webui", "build", "icons", fileName),
53428
+ path35.join(here, "..", "..", "webui", "static", "icons", fileName),
53429
+ path35.join(process.cwd(), "webui", "build", "icons", fileName),
53430
+ path35.join(process.cwd(), "webui", "static", "icons", fileName)
51889
53431
  ];
51890
53432
  return firstExistingFile(candidates);
51891
53433
  }
51892
53434
  function firstExistingFile(candidates) {
51893
53435
  for (const candidate of candidates) {
51894
- if (candidate && fs27.existsSync(candidate)) return candidate;
53436
+ if (candidate && fs34.existsSync(candidate)) return candidate;
51895
53437
  }
51896
53438
  return null;
51897
53439
  }
@@ -52264,17 +53806,17 @@ function resolveTrayIconPath(webuiDir) {
52264
53806
  return resolvePackagedIconPath2("monochrome-mini.png", webuiDir);
52265
53807
  }
52266
53808
  function resolvePackagedIconPath2(fileName, webuiDir) {
52267
- const here = path30.dirname(fileURLToPath4(import.meta.url));
53809
+ const here = path36.dirname(fileURLToPath4(import.meta.url));
52268
53810
  const candidates = [
52269
- webuiDir ? path30.join(webuiDir, "icons", fileName) : null,
52270
- path30.join(here, "webui", "icons", fileName),
52271
- path30.join(here, "..", "..", "webui", "build", "icons", fileName),
52272
- path30.join(here, "..", "..", "webui", "static", "icons", fileName),
52273
- path30.join(process.cwd(), "webui", "build", "icons", fileName),
52274
- path30.join(process.cwd(), "webui", "static", "icons", fileName)
53811
+ webuiDir ? path36.join(webuiDir, "icons", fileName) : null,
53812
+ path36.join(here, "webui", "icons", fileName),
53813
+ path36.join(here, "..", "..", "webui", "build", "icons", fileName),
53814
+ path36.join(here, "..", "..", "webui", "static", "icons", fileName),
53815
+ path36.join(process.cwd(), "webui", "build", "icons", fileName),
53816
+ path36.join(process.cwd(), "webui", "static", "icons", fileName)
52275
53817
  ];
52276
53818
  for (const candidate of candidates) {
52277
- if (candidate && fs28.existsSync(candidate)) return candidate;
53819
+ if (candidate && fs35.existsSync(candidate)) return candidate;
52278
53820
  }
52279
53821
  return null;
52280
53822
  }
@@ -52362,7 +53904,7 @@ async function safeCall(label, operation) {
52362
53904
  }
52363
53905
 
52364
53906
  // src/daemon/index.ts
52365
- var __dirname = path31.dirname(fileURLToPath5(import.meta.url));
53907
+ var __dirname = path37.dirname(fileURLToPath5(import.meta.url));
52366
53908
  var SHUTDOWN_GRACE_MS = 1e3;
52367
53909
  var SHUTDOWN_TASK_TIMEOUT_MS = 2e3;
52368
53910
  async function settleTeardown(label, action) {
@@ -52391,20 +53933,20 @@ async function settleTeardown(label, action) {
52391
53933
  }
52392
53934
  }
52393
53935
  function resolveWebuiDir(override) {
52394
- if (override) return path31.resolve(override);
53936
+ if (override) return path37.resolve(override);
52395
53937
  const candidates = [
52396
- path31.join(__dirname, "webui"),
53938
+ path37.join(__dirname, "webui"),
52397
53939
  // dist/webui (bundled)
52398
- path31.join(__dirname, "..", "..", "webui", "static"),
53940
+ path37.join(__dirname, "..", "..", "webui", "static"),
52399
53941
  // source dev: Vite static assets
52400
- path31.join(__dirname, "..", "..", "webui", "build"),
53942
+ path37.join(__dirname, "..", "..", "webui", "build"),
52401
53943
  // source production build
52402
- path31.join(process.cwd(), "dist", "webui"),
52403
- path31.join(process.cwd(), "webui", "static"),
52404
- path31.join(process.cwd(), "webui", "build")
53944
+ path37.join(process.cwd(), "dist", "webui"),
53945
+ path37.join(process.cwd(), "webui", "static"),
53946
+ path37.join(process.cwd(), "webui", "build")
52405
53947
  ];
52406
53948
  for (const c of candidates) {
52407
- if (fs29.existsSync(c)) return c;
53949
+ if (fs36.existsSync(c)) return c;
52408
53950
  }
52409
53951
  return candidates[0];
52410
53952
  }
@@ -52529,7 +54071,9 @@ async function bootDaemon(opts) {
52529
54071
  // ACP 子进程池:有界杀光所有 agent 子进程,避免孤儿进程泄漏。
52530
54072
  settleTeardown("acp bridge", () => domain.acpBridge.dispose()),
52531
54073
  // steward:取消活动 run、回收隔离 execution root、dispose backend adapter。
52532
- settleTeardown("steward service", () => domain.steward.dispose())
54074
+ settleTeardown("steward service", () => domain.steward.dispose()),
54075
+ // skill search:关闭 provider root watchers(persistent:false 之外的显式回收)。
54076
+ settleTeardown("skill search", () => domain.skillSearch.dispose())
52533
54077
  ];
52534
54078
  await Promise.allSettled(tasks);
52535
54079
  } finally {
@@ -52589,6 +54133,7 @@ async function bootDaemon(opts) {
52589
54133
  await Promise.allSettled([settleTeardown("late tray host", async () => host?.destroy())]);
52590
54134
  } else {
52591
54135
  handlesRef.trayHost = host;
54136
+ if (result.tray) domain.dialog.attach(result.tray);
52592
54137
  status.tray = result.window ? "mounted" : webMode && result.tray ? "web" : "headless";
52593
54138
  if (result.failure) {
52594
54139
  status.trayError = `[${result.failure.kind}@${result.failure.stage}] ${result.failure.cause instanceof Error ? result.failure.cause.message : String(result.failure.cause)}`;
@@ -52611,21 +54156,21 @@ async function bootDaemon(opts) {
52611
54156
  }
52612
54157
 
52613
54158
  // src/shared/package-version.ts
52614
- import fs30 from "node:fs";
52615
- import path32 from "node:path";
54159
+ import fs37 from "node:fs";
54160
+ import path38 from "node:path";
52616
54161
  import { fileURLToPath as fileURLToPath6 } from "node:url";
52617
- import { z as z26 } from "zod";
52618
- var PackageVersionSchema = z26.object({ version: z26.string().min(1) });
54162
+ import { z as z30 } from "zod";
54163
+ var PackageVersionSchema = z30.object({ version: z30.string().min(1) });
52619
54164
  function readNearestPackageVersion(moduleUrl) {
52620
- const here = path32.dirname(fileURLToPath6(moduleUrl));
54165
+ const here = path38.dirname(fileURLToPath6(moduleUrl));
52621
54166
  const candidates = [
52622
- path32.join(here, "package.json"),
52623
- path32.join(here, "..", "package.json"),
52624
- path32.join(here, "..", "..", "package.json")
54167
+ path38.join(here, "package.json"),
54168
+ path38.join(here, "..", "package.json"),
54169
+ path38.join(here, "..", "..", "package.json")
52625
54170
  ];
52626
54171
  for (const candidate of candidates) {
52627
54172
  try {
52628
- const pkg = safeParseJson(fs30.readFileSync(candidate, "utf8"), PackageVersionSchema);
54173
+ const pkg = safeParseJson(fs37.readFileSync(candidate, "utf8"), PackageVersionSchema);
52629
54174
  if (pkg) return pkg.version;
52630
54175
  } catch {
52631
54176
  }