@pleri/olam-cli 0.1.166 → 0.1.168

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 (67) hide show
  1. package/README.md +4 -2
  2. package/dist/commands/bootstrap.d.ts +6 -0
  3. package/dist/commands/bootstrap.d.ts.map +1 -1
  4. package/dist/commands/bootstrap.js +15 -0
  5. package/dist/commands/bootstrap.js.map +1 -1
  6. package/dist/commands/doctor.js +4 -4
  7. package/dist/commands/doctor.js.map +1 -1
  8. package/dist/commands/init.d.ts +4 -3
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +103 -81
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/commands/memory-service-container.d.ts +8 -0
  13. package/dist/commands/memory-service-container.d.ts.map +1 -1
  14. package/dist/commands/memory-service-container.js +16 -1
  15. package/dist/commands/memory-service-container.js.map +1 -1
  16. package/dist/commands/plans.d.ts +3 -0
  17. package/dist/commands/plans.d.ts.map +1 -0
  18. package/dist/commands/plans.js +211 -0
  19. package/dist/commands/plans.js.map +1 -0
  20. package/dist/commands/setup.d.ts +78 -14
  21. package/dist/commands/setup.d.ts.map +1 -1
  22. package/dist/commands/setup.js +430 -42
  23. package/dist/commands/setup.js.map +1 -1
  24. package/dist/commands/skills-source.d.ts +24 -0
  25. package/dist/commands/skills-source.d.ts.map +1 -1
  26. package/dist/commands/skills-source.js +257 -18
  27. package/dist/commands/skills-source.js.map +1 -1
  28. package/dist/commands/skills.d.ts +21 -0
  29. package/dist/commands/skills.d.ts.map +1 -1
  30. package/dist/commands/skills.js +44 -0
  31. package/dist/commands/skills.js.map +1 -1
  32. package/dist/image-digests.json +8 -7
  33. package/dist/index.js +2494 -1279
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -1
  36. package/dist/lib/bootstrap-kubernetes.js +178 -107
  37. package/dist/lib/bootstrap-kubernetes.js.map +1 -1
  38. package/dist/lib/health-probes.d.ts +16 -0
  39. package/dist/lib/health-probes.d.ts.map +1 -1
  40. package/dist/lib/health-probes.js +49 -0
  41. package/dist/lib/health-probes.js.map +1 -1
  42. package/dist/lib/peripheral-registry.d.ts +9 -3
  43. package/dist/lib/peripheral-registry.d.ts.map +1 -1
  44. package/dist/lib/peripheral-registry.js +4 -4
  45. package/dist/lib/peripheral-registry.js.map +1 -1
  46. package/dist/lib/plans-client.d.ts +69 -0
  47. package/dist/lib/plans-client.d.ts.map +1 -0
  48. package/dist/lib/plans-client.js +137 -0
  49. package/dist/lib/plans-client.js.map +1 -0
  50. package/dist/lib/port-forward.js +1 -1
  51. package/dist/lib/port-forward.js.map +1 -1
  52. package/dist/lib/upgrade-kubernetes.d.ts +1 -1
  53. package/dist/lib/upgrade-kubernetes.d.ts.map +1 -1
  54. package/dist/lib/upgrade-kubernetes.js +35 -21
  55. package/dist/lib/upgrade-kubernetes.js.map +1 -1
  56. package/dist/mcp-server.js +1239 -343
  57. package/hermes-bundle/version.json +1 -1
  58. package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
  59. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
  60. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
  61. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
  62. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
  63. package/host-cp/src/halt-detect.mjs +43 -0
  64. package/host-cp/src/panic-counter.mjs +94 -0
  65. package/host-cp/src/plan-chat-service.mjs +12 -1
  66. package/host-cp/src/server.mjs +75 -0
  67. package/package.json +1 -1
@@ -445,8 +445,8 @@ var init_parseUtil = __esm({
445
445
  init_errors();
446
446
  init_en();
447
447
  makeIssue = (params) => {
448
- const { data, path: path49, errorMaps, issueData } = params;
449
- const fullPath = [...path49, ...issueData.path || []];
448
+ const { data, path: path52, errorMaps, issueData } = params;
449
+ const fullPath = [...path52, ...issueData.path || []];
450
450
  const fullIssue = {
451
451
  ...issueData,
452
452
  path: fullPath
@@ -754,11 +754,11 @@ var init_types = __esm({
754
754
  init_parseUtil();
755
755
  init_util();
756
756
  ParseInputLazyPath = class {
757
- constructor(parent, value, path49, key) {
757
+ constructor(parent, value, path52, key) {
758
758
  this._cachedPath = [];
759
759
  this.parent = parent;
760
760
  this.data = value;
761
- this._path = path49;
761
+ this._path = path52;
762
762
  this._key = key;
763
763
  }
764
764
  get path() {
@@ -7332,8 +7332,8 @@ var require_utils = __commonJS({
7332
7332
  }
7333
7333
  return ind;
7334
7334
  }
7335
- function removeDotSegments(path49) {
7336
- let input = path49;
7335
+ function removeDotSegments(path52) {
7336
+ let input = path52;
7337
7337
  const output = [];
7338
7338
  let nextSlash = -1;
7339
7339
  let len = 0;
@@ -7532,8 +7532,8 @@ var require_schemes = __commonJS({
7532
7532
  wsComponent.secure = void 0;
7533
7533
  }
7534
7534
  if (wsComponent.resourceName) {
7535
- const [path49, query] = wsComponent.resourceName.split("?");
7536
- wsComponent.path = path49 && path49 !== "/" ? path49 : void 0;
7535
+ const [path52, query] = wsComponent.resourceName.split("?");
7536
+ wsComponent.path = path52 && path52 !== "/" ? path52 : void 0;
7537
7537
  wsComponent.query = query;
7538
7538
  wsComponent.resourceName = void 0;
7539
7539
  }
@@ -10895,12 +10895,12 @@ var require_dist = __commonJS({
10895
10895
  throw new Error(`Unknown format "${name}"`);
10896
10896
  return f;
10897
10897
  };
10898
- function addFormats(ajv, list, fs49, exportName) {
10898
+ function addFormats(ajv, list, fs51, exportName) {
10899
10899
  var _a3;
10900
10900
  var _b;
10901
10901
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
10902
10902
  for (const f of list)
10903
- ajv.addFormat(f, fs49[f]);
10903
+ ajv.addFormat(f, fs51[f]);
10904
10904
  }
10905
10905
  module.exports = exports = formatsPlugin;
10906
10906
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -11008,7 +11008,7 @@ import YAML from "yaml";
11008
11008
  function bootstrapStepCmd(entry) {
11009
11009
  return typeof entry === "string" ? entry : entry.cmd;
11010
11010
  }
11011
- function refineForbiddenKeys(value, path49, ctx, rejectSource) {
11011
+ function refineForbiddenKeys(value, path52, ctx, rejectSource) {
11012
11012
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
11013
11013
  return;
11014
11014
  }
@@ -11016,12 +11016,12 @@ function refineForbiddenKeys(value, path49, ctx, rejectSource) {
11016
11016
  if (FORBIDDEN_KEYS.has(key)) {
11017
11017
  ctx.addIssue({
11018
11018
  code: external_exports.ZodIssueCode.custom,
11019
- path: [...path49, key],
11019
+ path: [...path52, key],
11020
11020
  message: `forbidden key "${key}" (prototype-pollution surface)`
11021
11021
  });
11022
11022
  continue;
11023
11023
  }
11024
- if (rejectSource && path49.length === 0 && key === "source") {
11024
+ if (rejectSource && path52.length === 0 && key === "source") {
11025
11025
  ctx.addIssue({
11026
11026
  code: external_exports.ZodIssueCode.custom,
11027
11027
  path: ["source"],
@@ -11029,21 +11029,21 @@ function refineForbiddenKeys(value, path49, ctx, rejectSource) {
11029
11029
  });
11030
11030
  continue;
11031
11031
  }
11032
- refineForbiddenKeys(value[key], [...path49, key], ctx, false);
11032
+ refineForbiddenKeys(value[key], [...path52, key], ctx, false);
11033
11033
  }
11034
11034
  }
11035
- function rejectForbiddenKeys(value, path49, rejectSource) {
11035
+ function rejectForbiddenKeys(value, path52, rejectSource) {
11036
11036
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
11037
11037
  return;
11038
11038
  }
11039
11039
  for (const key of Object.keys(value)) {
11040
11040
  if (FORBIDDEN_KEYS.has(key)) {
11041
- throw new Error(`[manifest] ${path49}: forbidden key "${key}" (prototype-pollution surface)`);
11041
+ throw new Error(`[manifest] ${path52}: forbidden key "${key}" (prototype-pollution surface)`);
11042
11042
  }
11043
11043
  if (rejectSource && key === "source") {
11044
- throw new Error(`[manifest] ${path49}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
11044
+ throw new Error(`[manifest] ${path52}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
11045
11045
  }
11046
- rejectForbiddenKeys(value[key], `${path49}.${key}`, false);
11046
+ rejectForbiddenKeys(value[key], `${path52}.${key}`, false);
11047
11047
  }
11048
11048
  }
11049
11049
  function unknownTopLevelKeys(parsed) {
@@ -11894,16 +11894,42 @@ var init_loader = __esm({
11894
11894
  }
11895
11895
  });
11896
11896
 
11897
+ // ../core/dist/skill-sources/source-config-schema.js
11898
+ var PREFIX_PATTERN, SourceConfigSchema, SourceConfigSnapshotSchema;
11899
+ var init_source_config_schema = __esm({
11900
+ "../core/dist/skill-sources/source-config-schema.js"() {
11901
+ "use strict";
11902
+ init_v3();
11903
+ PREFIX_PATTERN = /^[a-z0-9][a-z0-9_-]{0,38}$/;
11904
+ SourceConfigSchema = external_exports.object({
11905
+ /**
11906
+ * Source's RECOMMENDED rename prefix. Operators inherit this when their
11907
+ * `~/.olam/config.json` has no explicit `prefix` override. Same regex
11908
+ * shape as operator-side `SkillSourceSchema.prefix`.
11909
+ */
11910
+ prefix: external_exports.string().regex(PREFIX_PATTERN, "source-config prefix must be ASCII lowercase + digits + dash/underscore (1-39 chars, no leading/trailing dash)").optional(),
11911
+ /**
11912
+ * Source's RECOMMENDED prefix scope. Same enum as operator-side
11913
+ * `SkillSourceSchema.prefixScope`. Operators inherit this when their
11914
+ * `~/.olam/config.json` has no explicit `prefixScope` override.
11915
+ */
11916
+ prefixScope: external_exports.array(external_exports.enum(["skill", "agent"])).optional()
11917
+ }).strict();
11918
+ SourceConfigSnapshotSchema = SourceConfigSchema;
11919
+ }
11920
+ });
11921
+
11897
11922
  // ../core/dist/skill-sources/schema.js
11898
- var SKILL_SOURCE_ID_LENGTH, NAME_PATTERN, URL_PATTERN, PREFIX_PATTERN, SkillSourceSchema;
11923
+ var SKILL_SOURCE_ID_LENGTH, NAME_PATTERN, URL_PATTERN, PREFIX_PATTERN2, SkillSourceSchema;
11899
11924
  var init_schema3 = __esm({
11900
11925
  "../core/dist/skill-sources/schema.js"() {
11901
11926
  "use strict";
11902
11927
  init_v3();
11928
+ init_source_config_schema();
11903
11929
  SKILL_SOURCE_ID_LENGTH = 12;
11904
11930
  NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
11905
11931
  URL_PATTERN = /^(?:https?:\/\/|git@|ssh:\/\/|file:\/\/|\/).+/;
11906
- PREFIX_PATTERN = /^[a-z0-9][a-z0-9_-]{0,38}$/;
11932
+ PREFIX_PATTERN2 = /^[a-z0-9][a-z0-9_-]{0,38}$/;
11907
11933
  SkillSourceSchema = external_exports.object({
11908
11934
  id: external_exports.string().length(SKILL_SOURCE_ID_LENGTH, `skill-source id must be exactly ${SKILL_SOURCE_ID_LENGTH} chars (sha256-of-url prefix)`).regex(/^[a-f0-9]+$/, "skill-source id must be lowercase hex"),
11909
11935
  name: external_exports.string().regex(NAME_PATTERN, "skill-source name must be ASCII lowercase + digits + dash (1-64 chars, no leading/trailing dash)"),
@@ -11920,7 +11946,29 @@ var init_schema3 = __esm({
11920
11946
  * use canonical names; `olam flywheel migrate-overlays --push` enforces
11921
11947
  * this via the reverse validator.
11922
11948
  */
11923
- prefix: external_exports.string().regex(PREFIX_PATTERN, "skill-source prefix must be ASCII lowercase + digits + dash/underscore (1-39 chars, no leading/trailing dash)").optional()
11949
+ prefix: external_exports.string().regex(PREFIX_PATTERN2, "skill-source prefix must be ASCII lowercase + digits + dash/underscore (1-39 chars, no leading/trailing dash)").optional(),
11950
+ /**
11951
+ * Which artifact kinds get renamed when `prefix` is set. Optional; defaults
11952
+ * to `['skill', 'agent']` for back-compat with the original Phase A semantic
11953
+ * (Decision 6 in docs/decisions/019-skill-prefix-rules.md). Operators who
11954
+ * want to rebrand ONLY skills (keeping `@architect` etc. canonical) set
11955
+ * `['skill']`; ones who want only agents set `['agent']`.
11956
+ *
11957
+ * Empty array `[]` = same as omitting `prefix` entirely (no renaming).
11958
+ *
11959
+ * NOT applicable when `prefix` is undefined.
11960
+ */
11961
+ prefixScope: external_exports.array(external_exports.enum(["skill", "agent"])).optional(),
11962
+ /**
11963
+ * Snapshot of the LAST source-config.yaml content this operator saw +
11964
+ * consented to (ADR-021). Trust-gate state: when the live source-config
11965
+ * differs from this snapshot, sync halts with a consent prompt instead
11966
+ * of silently rebranding. Snapshot is updated on accept; persists across
11967
+ * syncs. When undefined: operator has never seen a source-config from this
11968
+ * source (first-sync flow applies). Field shape mirrors SourceConfigSchema
11969
+ * for round-trip simplicity.
11970
+ */
11971
+ lastSeenSourcePrefix: SourceConfigSnapshotSchema.optional()
11924
11972
  });
11925
11973
  }
11926
11974
  });
@@ -12449,7 +12497,9 @@ var init_trust_audit_log = __esm({
12449
12497
  "meta-hook-stripped",
12450
12498
  "setup-skill-source-picked",
12451
12499
  "setup-project-sweep-completed",
12452
- "prefix-collision"
12500
+ "prefix-collision",
12501
+ "source-prefix-adopted",
12502
+ "source-prefix-changed"
12453
12503
  ]);
12454
12504
  TrustMethodSchema = external_exports.enum(["flag", "interactive", "auto-reject-tty", "none"]);
12455
12505
  TrustAuditEntrySchema = external_exports.object({
@@ -12563,48 +12613,54 @@ function updateSkillSource(id, patch) {
12563
12613
  }
12564
12614
  }
12565
12615
  if (patch.prefix !== void 0 && patch.prefix !== null) {
12566
- if (!PREFIX_PATTERN2.test(patch.prefix)) {
12616
+ if (!PREFIX_PATTERN3.test(patch.prefix)) {
12567
12617
  throw new Error(`prefix must match /^[a-z0-9][a-z0-9_-]{0,38}$/ \u2014 got "${patch.prefix}"`);
12568
12618
  }
12569
12619
  }
12570
12620
  const existing = config2.skillSources[idx];
12571
- let prefixUpdate = {};
12621
+ let working = { ...existing };
12572
12622
  if (patch.prefix === null) {
12573
- const { prefix: _dropped, ...rest } = existing;
12574
- void _dropped;
12575
- const updated2 = {
12576
- ...rest,
12577
- ...patch.name !== void 0 ? { name: patch.name } : {},
12578
- ...patch.branch !== void 0 ? { branch: patch.branch } : {},
12579
- ...patch.lastPulledSha !== void 0 ? { lastPulledSha: patch.lastPulledSha } : {}
12580
- };
12581
- const next2 = [...config2.skillSources];
12582
- next2[idx] = updated2;
12583
- writeGlobalConfig({ ...config2, skillSources: next2 });
12584
- return updated2;
12623
+ const { prefix: _p, ...rest } = working;
12624
+ void _p;
12625
+ working = rest;
12585
12626
  } else if (patch.prefix !== void 0) {
12586
- prefixUpdate = { prefix: patch.prefix };
12627
+ working = { ...working, prefix: patch.prefix };
12628
+ }
12629
+ if (patch.prefixScope === null) {
12630
+ const { prefixScope: _s, ...rest } = working;
12631
+ void _s;
12632
+ working = rest;
12633
+ } else if (patch.prefixScope !== void 0) {
12634
+ working = { ...working, prefixScope: [...patch.prefixScope] };
12635
+ }
12636
+ if (patch.lastSeenSourcePrefix === null) {
12637
+ const { lastSeenSourcePrefix: _l, ...rest } = working;
12638
+ void _l;
12639
+ working = rest;
12640
+ } else if (patch.lastSeenSourcePrefix !== void 0) {
12641
+ const validated = SourceConfigSnapshotSchema.parse(patch.lastSeenSourcePrefix);
12642
+ working = { ...working, lastSeenSourcePrefix: validated };
12587
12643
  }
12588
12644
  const updated = {
12589
- ...existing,
12645
+ ...working,
12590
12646
  ...patch.name !== void 0 ? { name: patch.name } : {},
12591
12647
  ...patch.branch !== void 0 ? { branch: patch.branch } : {},
12592
- ...patch.lastPulledSha !== void 0 ? { lastPulledSha: patch.lastPulledSha } : {},
12593
- ...prefixUpdate
12648
+ ...patch.lastPulledSha !== void 0 ? { lastPulledSha: patch.lastPulledSha } : {}
12594
12649
  };
12595
12650
  const next = [...config2.skillSources];
12596
12651
  next[idx] = updated;
12597
12652
  writeGlobalConfig({ ...config2, skillSources: next });
12598
12653
  return updated;
12599
12654
  }
12600
- var PREFIX_PATTERN2;
12655
+ var PREFIX_PATTERN3;
12601
12656
  var init_store2 = __esm({
12602
12657
  "../core/dist/skill-sources/store.js"() {
12603
12658
  "use strict";
12604
12659
  init_store();
12605
12660
  init_schema3();
12606
12661
  init_trust_audit_log();
12607
- PREFIX_PATTERN2 = /^[a-z0-9][a-z0-9_-]{0,38}$/;
12662
+ init_source_config_schema();
12663
+ PREFIX_PATTERN3 = /^[a-z0-9][a-z0-9_-]{0,38}$/;
12608
12664
  }
12609
12665
  });
12610
12666
 
@@ -13404,7 +13460,7 @@ function detectCollisions(artifacts) {
13404
13460
  }
13405
13461
  return { winners, collisions };
13406
13462
  }
13407
- function cleanManagedSymlinks(claude, installedOlamVersion, overlayReferences) {
13463
+ function cleanManagedSymlinks(claude, installedOlamVersion, overlayReferences, expectedAgentWinnerNames) {
13408
13464
  const shadowBackups = [];
13409
13465
  for (const bucket of BUCKETS) {
13410
13466
  const dir = path21.join(claude, bucket);
@@ -13438,8 +13494,11 @@ function cleanManagedSymlinks(claude, installedOlamVersion, overlayReferences) {
13438
13494
  }
13439
13495
  fs20.unlinkSync(p);
13440
13496
  } else if (bucket === "agents" && stat.isFile() && !name.includes(".shadow-backup-")) {
13441
- const backup = shadowBackup(p);
13442
- shadowBackups.push(backup);
13497
+ const hasWinner = expectedAgentWinnerNames !== void 0 ? expectedAgentWinnerNames.has(name) : true;
13498
+ if (hasWinner) {
13499
+ const backup = shadowBackup(p);
13500
+ shadowBackups.push(backup);
13501
+ }
13443
13502
  }
13444
13503
  } catch {
13445
13504
  }
@@ -13479,8 +13538,9 @@ function deployArtifacts(artifacts, opts) {
13479
13538
  for (const bucket of BUCKETS) {
13480
13539
  fs20.mkdirSync(path21.join(claude, bucket), { recursive: true });
13481
13540
  }
13482
- const sweepShadowBackups = cleanManagedSymlinks(claude, opts?.installedOlamVersion, opts?.overlayReferences);
13483
13541
  const { winners, collisions } = detectCollisions(artifacts);
13542
+ const expectedAgentWinnerNames = new Set(winners.filter((a) => a.kind === "agent").map((a) => a.deployBasename));
13543
+ const sweepShadowBackups = cleanManagedSymlinks(claude, opts?.installedOlamVersion, opts?.overlayReferences, expectedAgentWinnerNames);
13484
13544
  const result = { linked: 0, shadowBackups: [...sweepShadowBackups], collisions };
13485
13545
  for (const artifact of winners) {
13486
13546
  const bucket = bucketFor(artifact.kind);
@@ -14945,15 +15005,18 @@ import * as fs30 from "node:fs";
14945
15005
  import * as path29 from "node:path";
14946
15006
  function buildSourcePrefixMap(sources) {
14947
15007
  const byId = /* @__PURE__ */ new Map();
15008
+ const scopeById = /* @__PURE__ */ new Map();
14948
15009
  const prefixes = /* @__PURE__ */ new Set();
14949
15010
  for (const s of sources) {
14950
15011
  if (s.prefix !== void 0 && s.prefix.length > 0) {
14951
15012
  byId.set(s.id, s.prefix);
15013
+ scopeById.set(s.id, s.prefixScope ?? DEFAULT_SCOPE);
14952
15014
  prefixes.add(s.prefix);
14953
15015
  }
14954
15016
  }
14955
15017
  return {
14956
15018
  get: (sourceId) => byId.get(sourceId),
15019
+ getScope: (sourceId) => scopeById.get(sourceId) ?? DEFAULT_SCOPE,
14957
15020
  registeredPrefixes: Array.from(prefixes)
14958
15021
  };
14959
15022
  }
@@ -14965,6 +15028,9 @@ function applyPrefixRewrites(baseArtifacts, sourceMap, claudeDir2, dryRun) {
14965
15028
  const prefix = sourceMap.get(artifact.sourceId);
14966
15029
  if (prefix === void 0)
14967
15030
  continue;
15031
+ const scope = sourceMap.getScope(artifact.sourceId);
15032
+ if (!scope.includes(artifact.kind))
15033
+ continue;
14968
15034
  const canonical = artifact.deployBasename;
14969
15035
  const otherPrefixes = sourceMap.registeredPrefixes.filter((p) => p !== prefix);
14970
15036
  const renamed = applyPrefix(canonical, prefix, otherPrefixes);
@@ -15031,11 +15097,104 @@ function detectPrefixCollisions(sources) {
15031
15097
  }
15032
15098
  return collisions;
15033
15099
  }
15100
+ var DEFAULT_SCOPE;
15034
15101
  var init_prefix_deploy = __esm({
15035
15102
  "../core/dist/skill-sync/prefix-deploy.js"() {
15036
15103
  "use strict";
15037
15104
  init_prefix_rules();
15038
15105
  init_managed_merge();
15106
+ DEFAULT_SCOPE = ["skill", "agent"];
15107
+ }
15108
+ });
15109
+
15110
+ // ../core/dist/skill-sync/resolve-source-config.js
15111
+ import { readFileSync as readFileSync29, existsSync as existsSync31 } from "node:fs";
15112
+ import { join as join31 } from "node:path";
15113
+ import { parse as parseYaml4 } from "yaml";
15114
+ function sourceConfigPath(clonePath) {
15115
+ return join31(clonePath, "shared", "source-config.yaml");
15116
+ }
15117
+ function readSourceConfig(clonePath, sourceId) {
15118
+ const path52 = sourceConfigPath(clonePath);
15119
+ if (!existsSync31(path52))
15120
+ return void 0;
15121
+ let raw;
15122
+ try {
15123
+ raw = readFileSync29(path52, "utf-8");
15124
+ } catch (err) {
15125
+ emitMalformedWarning(sourceId, `read failed: ${errToMsg(err)}`);
15126
+ return void 0;
15127
+ }
15128
+ let parsed;
15129
+ try {
15130
+ parsed = parseYaml4(raw);
15131
+ } catch (err) {
15132
+ emitMalformedWarning(sourceId, `YAML parse failed: ${errToMsg(err)}`);
15133
+ return void 0;
15134
+ }
15135
+ const candidate = parsed === null || parsed === void 0 ? {} : parsed;
15136
+ const result = SourceConfigSchema.safeParse(candidate);
15137
+ if (!result.success) {
15138
+ emitMalformedWarning(sourceId, `schema validation failed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
15139
+ return void 0;
15140
+ }
15141
+ return result.data;
15142
+ }
15143
+ function emitMalformedWarning(sourceId, reason) {
15144
+ const id = sourceId ?? "<unknown source>";
15145
+ process.stderr.write(`[source-config:malformed] ${id}: ${reason}
15146
+ `);
15147
+ }
15148
+ function errToMsg(err) {
15149
+ return err instanceof Error ? err.message : String(err);
15150
+ }
15151
+ function resolveEffectivePrefix(operatorSource, sourceConfig) {
15152
+ const opPrefix = operatorSource.prefix;
15153
+ const opScope = operatorSource.prefixScope;
15154
+ const srcPrefix = sourceConfig?.prefix;
15155
+ const srcScope = sourceConfig?.prefixScope;
15156
+ const effectivePrefix = opPrefix ?? srcPrefix;
15157
+ const effectiveScope = opScope ?? srcScope;
15158
+ const operatorHasAny = opPrefix !== void 0 || opScope !== void 0 && opScope.length > 0;
15159
+ const sourceHasAny = srcPrefix !== void 0 || srcScope !== void 0 && srcScope.length > 0;
15160
+ const origin = operatorHasAny ? "operator" : sourceHasAny ? "source" : "none";
15161
+ const result = { origin };
15162
+ if (effectivePrefix !== void 0)
15163
+ result.prefix = effectivePrefix;
15164
+ if (effectiveScope !== void 0)
15165
+ result.prefixScope = effectiveScope;
15166
+ return result;
15167
+ }
15168
+ function sourceConfigsEqual(a, b) {
15169
+ const normalize = (c) => {
15170
+ if (c === void 0)
15171
+ return {};
15172
+ const out = {};
15173
+ if (c.prefix !== void 0)
15174
+ out.prefix = c.prefix;
15175
+ if (c.prefixScope !== void 0 && c.prefixScope.length > 0) {
15176
+ out.prefixScope = [...c.prefixScope];
15177
+ }
15178
+ return out;
15179
+ };
15180
+ const na = normalize(a);
15181
+ const nb = normalize(b);
15182
+ if (na.prefix !== nb.prefix)
15183
+ return false;
15184
+ const sa = na.prefixScope ?? [];
15185
+ const sb = nb.prefixScope ?? [];
15186
+ if (sa.length !== sb.length)
15187
+ return false;
15188
+ for (let i = 0; i < sa.length; i++) {
15189
+ if (sa[i] !== sb[i])
15190
+ return false;
15191
+ }
15192
+ return true;
15193
+ }
15194
+ var init_resolve_source_config = __esm({
15195
+ "../core/dist/skill-sync/resolve-source-config.js"() {
15196
+ "use strict";
15197
+ init_source_config_schema();
15039
15198
  }
15040
15199
  });
15041
15200
 
@@ -15099,8 +15258,95 @@ async function syncSkills(opts = {}) {
15099
15258
  const memberOverlaysEnabled = process.env["OLAM_MEMBER_OVERLAYS"] === "1";
15100
15259
  const baseArtifacts = projectFilteredArtifacts.filter((a) => a.kind !== "overlay");
15101
15260
  const overlayArtifacts = memberOverlaysEnabled ? projectFilteredArtifacts.filter((a) => a.kind === "overlay") : [];
15102
- const sourcePrefixMap = buildSourcePrefixMap(sources);
15103
- const hasPrefixRewrites = sources.some((s) => s.prefix !== void 0 && s.prefix.length > 0);
15261
+ const effectiveSources = sources.map((s) => {
15262
+ const clonePath = skillSourceClonePath(s.id);
15263
+ const sourceConfig = fs31.existsSync(clonePath) ? readSourceConfig(clonePath, s.id) : void 0;
15264
+ const eff = resolveEffectivePrefix(s, sourceConfig);
15265
+ const entry = {
15266
+ id: s.id,
15267
+ origin: eff.origin,
15268
+ sourceConfig
15269
+ };
15270
+ if (eff.prefix !== void 0)
15271
+ entry.prefix = eff.prefix;
15272
+ if (eff.prefixScope !== void 0)
15273
+ entry.prefixScope = eff.prefixScope;
15274
+ return entry;
15275
+ });
15276
+ const sourcePrefixGateOutcomes = [];
15277
+ for (const eff of effectiveSources) {
15278
+ if (eff.sourceConfig === void 0) {
15279
+ sourcePrefixGateOutcomes.push({ sourceId: eff.id, outcome: "no-source-config" });
15280
+ continue;
15281
+ }
15282
+ if (eff.origin === "operator") {
15283
+ sourcePrefixGateOutcomes.push({ sourceId: eff.id, outcome: "no-change" });
15284
+ continue;
15285
+ }
15286
+ const operatorSource = sources.find((s) => s.id === eff.id);
15287
+ const lastSeen = operatorSource?.lastSeenSourcePrefix;
15288
+ const sameAsLastSeen = sourceConfigsEqual(lastSeen, eff.sourceConfig);
15289
+ if (sameAsLastSeen) {
15290
+ sourcePrefixGateOutcomes.push({ sourceId: eff.id, outcome: "no-change" });
15291
+ continue;
15292
+ }
15293
+ const kind = lastSeen === void 0 ? "first-time" : "changed";
15294
+ const flagGrant = kind === "first-time" && opts.autoAcceptSourcePrefix === true || kind === "changed" && opts.acceptSourcePrefixChanges === true;
15295
+ let consented = flagGrant;
15296
+ if (!consented && opts.sourcePrefixPrompt !== void 0) {
15297
+ try {
15298
+ const promptInput = {
15299
+ sourceName: operatorSource?.name ?? eff.id,
15300
+ sourceId: eff.id,
15301
+ newConfig: eff.sourceConfig,
15302
+ kind
15303
+ };
15304
+ if (lastSeen !== void 0)
15305
+ promptInput.priorConfig = lastSeen;
15306
+ consented = await opts.sourcePrefixPrompt(promptInput);
15307
+ } catch {
15308
+ consented = false;
15309
+ }
15310
+ }
15311
+ if (consented) {
15312
+ if (!opts.dryRun) {
15313
+ try {
15314
+ updateSkillSource(eff.id, { lastSeenSourcePrefix: eff.sourceConfig });
15315
+ appendTrustAudit({
15316
+ gitUrl: `internal:source-prefix-${kind === "first-time" ? "adopted" : "changed"}`,
15317
+ action: kind === "first-time" ? "source-prefix-adopted" : "source-prefix-changed",
15318
+ trustMethod: flagGrant ? "flag" : "interactive",
15319
+ sourceId: eff.id,
15320
+ note: kind === "first-time" ? `snapshot=${JSON.stringify(eff.sourceConfig)}` : `prior=${JSON.stringify(lastSeen ?? {})}; new=${JSON.stringify(eff.sourceConfig)}`
15321
+ });
15322
+ } catch {
15323
+ }
15324
+ }
15325
+ sourcePrefixGateOutcomes.push({
15326
+ sourceId: eff.id,
15327
+ outcome: kind === "first-time" ? "adopted" : "changed"
15328
+ });
15329
+ } else {
15330
+ delete eff.prefix;
15331
+ delete eff.prefixScope;
15332
+ eff.origin = "none";
15333
+ const isNonTty = opts.sourcePrefixPrompt === void 0;
15334
+ sourcePrefixGateOutcomes.push({
15335
+ sourceId: eff.id,
15336
+ outcome: isNonTty ? "skipped-non-tty" : "rejected"
15337
+ });
15338
+ if (isNonTty) {
15339
+ process.stderr.write(`[source-prefix:skipped] ${operatorSource?.name ?? eff.id}: source-config has prefix-suggestion '${eff.sourceConfig.prefix ?? "<empty>"}' but no consent (non-TTY without --auto-accept-source-prefix). Skipping for this sync; canonical names deployed.
15340
+ `);
15341
+ }
15342
+ }
15343
+ }
15344
+ const sourcePrefixMap = buildSourcePrefixMap(effectiveSources);
15345
+ const hasPrefixRewrites = effectiveSources.some((s) => s.prefix !== void 0 && s.prefix.length > 0);
15346
+ const effectivePrefixOrigins = {};
15347
+ for (const s of effectiveSources) {
15348
+ effectivePrefixOrigins[s.id] = s.origin;
15349
+ }
15104
15350
  const willMutateManagedDir = !opts.dryRun && (memberOverlaysEnabled && overlayArtifacts.length > 0 || hasPrefixRewrites);
15105
15351
  if (willMutateManagedDir) {
15106
15352
  const claude = claudeDir();
@@ -15149,7 +15395,7 @@ async function syncSkills(opts = {}) {
15149
15395
  const permissionFiles = projectFilteredArtifacts.filter((a) => a.kind === "permission").map((a) => a.sourcePath);
15150
15396
  const claudeDirForRewrites = claudeDir();
15151
15397
  const prefixRewriteResult = applyPrefixRewrites(baseArtifacts, sourcePrefixMap, claudeDirForRewrites, opts.dryRun === true);
15152
- const prefixCollisions = detectPrefixCollisions(sources);
15398
+ const prefixCollisions = detectPrefixCollisions(effectiveSources);
15153
15399
  if (!opts.dryRun) {
15154
15400
  for (const collision of prefixCollisions) {
15155
15401
  try {
@@ -15176,7 +15422,9 @@ async function syncSkills(opts = {}) {
15176
15422
  metaHooks: void 0,
15177
15423
  perSource,
15178
15424
  prefixRewrites: prefixRewriteResult.entries,
15179
- prefixCollisions
15425
+ prefixCollisions,
15426
+ effectivePrefixOrigins,
15427
+ sourcePrefixGateOutcomes
15180
15428
  };
15181
15429
  if (opts.dryRun) {
15182
15430
  summary.collisions = detectCollisions(baseArtifacts).collisions;
@@ -15308,6 +15556,8 @@ var init_engine = __esm({
15308
15556
  init_markdown_merger();
15309
15557
  init_managed_merge();
15310
15558
  init_prefix_deploy();
15559
+ init_resolve_source_config();
15560
+ init_store2();
15311
15561
  }
15312
15562
  });
15313
15563
 
@@ -15820,6 +16070,8 @@ __export(skill_sources_exports, {
15820
16070
  SkillOverrideSchema: () => SkillOverrideSchema,
15821
16071
  SkillSourceGitError: () => SkillSourceGitError,
15822
16072
  SkillSourceSchema: () => SkillSourceSchema,
16073
+ SourceConfigSchema: () => SourceConfigSchema,
16074
+ SourceConfigSnapshotSchema: () => SourceConfigSnapshotSchema,
15823
16075
  TrustActionSchema: () => TrustActionSchema,
15824
16076
  TrustAuditEntrySchema: () => TrustAuditEntrySchema,
15825
16077
  TrustMethodSchema: () => TrustMethodSchema,
@@ -15859,12 +16111,14 @@ __export(skill_sources_exports, {
15859
16111
  pullSkillSource: () => pullSkillSource,
15860
16112
  readLatestMigrationSnapshot: () => readLatestMigrationSnapshot,
15861
16113
  readMigrationSnapshotFromPath: () => readMigrationSnapshotFromPath,
16114
+ readSourceConfig: () => readSourceConfig,
15862
16115
  readTrustAuditLog: () => readTrustAuditLog,
15863
16116
  redactUrl: () => redactUrl2,
15864
16117
  removeSkillSource: () => removeSkillSource,
15865
16118
  removeSkillSourceClone: () => removeSkillSourceClone,
15866
16119
  reorderSkillSource: () => reorderSkillSource,
15867
16120
  resolveAtlasUser: () => resolveAtlasUser,
16121
+ resolveEffectivePrefix: () => resolveEffectivePrefix,
15868
16122
  resolveSourceArtifacts: () => resolveSourceArtifacts,
15869
16123
  resolveSubscriptions: () => resolveSubscriptions,
15870
16124
  restoreCloneToBranchHead: () => restoreCloneToBranchHead,
@@ -15875,6 +16129,8 @@ __export(skill_sources_exports, {
15875
16129
  skillSourcesAuditLogPath: () => skillSourcesAuditLogPath,
15876
16130
  skillSourcesRootDir: () => skillSourcesRootDir,
15877
16131
  skillsHookSettingsPathFor: () => settingsPathFor,
16132
+ sourceConfigPath: () => sourceConfigPath,
16133
+ sourceConfigsEqual: () => sourceConfigsEqual,
15878
16134
  syncSkills: () => syncSkills,
15879
16135
  uninstallSkillsHookFromFile: () => uninstallSkillsHookFromFile,
15880
16136
  updateSkillSource: () => updateSkillSource,
@@ -15891,6 +16147,8 @@ var init_skill_sources = __esm({
15891
16147
  init_hook_install();
15892
16148
  init_migration_snapshot();
15893
16149
  init_engine();
16150
+ init_resolve_source_config();
16151
+ init_source_config_schema();
15894
16152
  init_file_lock();
15895
16153
  init_schema5();
15896
16154
  init_per_project_override();
@@ -15917,6 +16175,9 @@ var init_global_config = __esm({
15917
16175
  }
15918
16176
  });
15919
16177
 
16178
+ // ../mcp-server/src/redirect-stdout-to-stderr.ts
16179
+ console.log = console.error;
16180
+
15920
16181
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
15921
16182
  import process3 from "node:process";
15922
16183
 
@@ -16160,10 +16421,10 @@ function mergeDefs(...defs) {
16160
16421
  function cloneDef(schema) {
16161
16422
  return mergeDefs(schema._zod.def);
16162
16423
  }
16163
- function getElementAtPath(obj, path49) {
16164
- if (!path49)
16424
+ function getElementAtPath(obj, path52) {
16425
+ if (!path52)
16165
16426
  return obj;
16166
- return path49.reduce((acc, key) => acc?.[key], obj);
16427
+ return path52.reduce((acc, key) => acc?.[key], obj);
16167
16428
  }
16168
16429
  function promiseAllObject(promisesObj) {
16169
16430
  const keys = Object.keys(promisesObj);
@@ -16572,11 +16833,11 @@ function explicitlyAborted(x, startIndex = 0) {
16572
16833
  }
16573
16834
  return false;
16574
16835
  }
16575
- function prefixIssues(path49, issues) {
16836
+ function prefixIssues(path52, issues) {
16576
16837
  return issues.map((iss) => {
16577
16838
  var _a3;
16578
16839
  (_a3 = iss).path ?? (_a3.path = []);
16579
- iss.path.unshift(path49);
16840
+ iss.path.unshift(path52);
16580
16841
  return iss;
16581
16842
  });
16582
16843
  }
@@ -16723,16 +16984,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
16723
16984
  }
16724
16985
  function formatError(error2, mapper = (issue2) => issue2.message) {
16725
16986
  const fieldErrors = { _errors: [] };
16726
- const processError = (error3, path49 = []) => {
16987
+ const processError = (error3, path52 = []) => {
16727
16988
  for (const issue2 of error3.issues) {
16728
16989
  if (issue2.code === "invalid_union" && issue2.errors.length) {
16729
- issue2.errors.map((issues) => processError({ issues }, [...path49, ...issue2.path]));
16990
+ issue2.errors.map((issues) => processError({ issues }, [...path52, ...issue2.path]));
16730
16991
  } else if (issue2.code === "invalid_key") {
16731
- processError({ issues: issue2.issues }, [...path49, ...issue2.path]);
16992
+ processError({ issues: issue2.issues }, [...path52, ...issue2.path]);
16732
16993
  } else if (issue2.code === "invalid_element") {
16733
- processError({ issues: issue2.issues }, [...path49, ...issue2.path]);
16994
+ processError({ issues: issue2.issues }, [...path52, ...issue2.path]);
16734
16995
  } else {
16735
- const fullpath = [...path49, ...issue2.path];
16996
+ const fullpath = [...path52, ...issue2.path];
16736
16997
  if (fullpath.length === 0) {
16737
16998
  fieldErrors._errors.push(mapper(issue2));
16738
16999
  } else {
@@ -26155,6 +26416,228 @@ var McpZodTypeKind;
26155
26416
  McpZodTypeKind2["Completable"] = "McpCompletable";
26156
26417
  })(McpZodTypeKind || (McpZodTypeKind = {}));
26157
26418
 
26419
+ // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/uriTemplate.js
26420
+ var MAX_TEMPLATE_LENGTH = 1e6;
26421
+ var MAX_VARIABLE_LENGTH = 1e6;
26422
+ var MAX_TEMPLATE_EXPRESSIONS = 1e4;
26423
+ var MAX_REGEX_LENGTH = 1e6;
26424
+ var UriTemplate = class _UriTemplate {
26425
+ /**
26426
+ * Returns true if the given string contains any URI template expressions.
26427
+ * A template expression is a sequence of characters enclosed in curly braces,
26428
+ * like {foo} or {?bar}.
26429
+ */
26430
+ static isTemplate(str) {
26431
+ return /\{[^}\s]+\}/.test(str);
26432
+ }
26433
+ static validateLength(str, max, context) {
26434
+ if (str.length > max) {
26435
+ throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
26436
+ }
26437
+ }
26438
+ get variableNames() {
26439
+ return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names);
26440
+ }
26441
+ constructor(template) {
26442
+ _UriTemplate.validateLength(template, MAX_TEMPLATE_LENGTH, "Template");
26443
+ this.template = template;
26444
+ this.parts = this.parse(template);
26445
+ }
26446
+ toString() {
26447
+ return this.template;
26448
+ }
26449
+ parse(template) {
26450
+ const parts = [];
26451
+ let currentText = "";
26452
+ let i = 0;
26453
+ let expressionCount = 0;
26454
+ while (i < template.length) {
26455
+ if (template[i] === "{") {
26456
+ if (currentText) {
26457
+ parts.push(currentText);
26458
+ currentText = "";
26459
+ }
26460
+ const end = template.indexOf("}", i);
26461
+ if (end === -1)
26462
+ throw new Error("Unclosed template expression");
26463
+ expressionCount++;
26464
+ if (expressionCount > MAX_TEMPLATE_EXPRESSIONS) {
26465
+ throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
26466
+ }
26467
+ const expr = template.slice(i + 1, end);
26468
+ const operator = this.getOperator(expr);
26469
+ const exploded = expr.includes("*");
26470
+ const names = this.getNames(expr);
26471
+ const name = names[0];
26472
+ for (const name2 of names) {
26473
+ _UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name");
26474
+ }
26475
+ parts.push({ name, operator, names, exploded });
26476
+ i = end + 1;
26477
+ } else {
26478
+ currentText += template[i];
26479
+ i++;
26480
+ }
26481
+ }
26482
+ if (currentText) {
26483
+ parts.push(currentText);
26484
+ }
26485
+ return parts;
26486
+ }
26487
+ getOperator(expr) {
26488
+ const operators = ["+", "#", ".", "/", "?", "&"];
26489
+ return operators.find((op) => expr.startsWith(op)) || "";
26490
+ }
26491
+ getNames(expr) {
26492
+ const operator = this.getOperator(expr);
26493
+ return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0);
26494
+ }
26495
+ encodeValue(value, operator) {
26496
+ _UriTemplate.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value");
26497
+ if (operator === "+" || operator === "#") {
26498
+ return encodeURI(value);
26499
+ }
26500
+ return encodeURIComponent(value);
26501
+ }
26502
+ expandPart(part, variables) {
26503
+ if (part.operator === "?" || part.operator === "&") {
26504
+ const pairs = part.names.map((name) => {
26505
+ const value2 = variables[name];
26506
+ if (value2 === void 0)
26507
+ return "";
26508
+ const encoded2 = Array.isArray(value2) ? value2.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value2.toString(), part.operator);
26509
+ return `${name}=${encoded2}`;
26510
+ }).filter((pair) => pair.length > 0);
26511
+ if (pairs.length === 0)
26512
+ return "";
26513
+ const separator = part.operator === "?" ? "?" : "&";
26514
+ return separator + pairs.join("&");
26515
+ }
26516
+ if (part.names.length > 1) {
26517
+ const values2 = part.names.map((name) => variables[name]).filter((v) => v !== void 0);
26518
+ if (values2.length === 0)
26519
+ return "";
26520
+ return values2.map((v) => Array.isArray(v) ? v[0] : v).join(",");
26521
+ }
26522
+ const value = variables[part.name];
26523
+ if (value === void 0)
26524
+ return "";
26525
+ const values = Array.isArray(value) ? value : [value];
26526
+ const encoded = values.map((v) => this.encodeValue(v, part.operator));
26527
+ switch (part.operator) {
26528
+ case "":
26529
+ return encoded.join(",");
26530
+ case "+":
26531
+ return encoded.join(",");
26532
+ case "#":
26533
+ return "#" + encoded.join(",");
26534
+ case ".":
26535
+ return "." + encoded.join(".");
26536
+ case "/":
26537
+ return "/" + encoded.join("/");
26538
+ default:
26539
+ return encoded.join(",");
26540
+ }
26541
+ }
26542
+ expand(variables) {
26543
+ let result = "";
26544
+ let hasQueryParam = false;
26545
+ for (const part of this.parts) {
26546
+ if (typeof part === "string") {
26547
+ result += part;
26548
+ continue;
26549
+ }
26550
+ const expanded = this.expandPart(part, variables);
26551
+ if (!expanded)
26552
+ continue;
26553
+ if ((part.operator === "?" || part.operator === "&") && hasQueryParam) {
26554
+ result += expanded.replace("?", "&");
26555
+ } else {
26556
+ result += expanded;
26557
+ }
26558
+ if (part.operator === "?" || part.operator === "&") {
26559
+ hasQueryParam = true;
26560
+ }
26561
+ }
26562
+ return result;
26563
+ }
26564
+ escapeRegExp(str) {
26565
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26566
+ }
26567
+ partToRegExp(part) {
26568
+ const patterns = [];
26569
+ for (const name2 of part.names) {
26570
+ _UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name");
26571
+ }
26572
+ if (part.operator === "?" || part.operator === "&") {
26573
+ for (let i = 0; i < part.names.length; i++) {
26574
+ const name2 = part.names[i];
26575
+ const prefix = i === 0 ? "\\" + part.operator : "&";
26576
+ patterns.push({
26577
+ pattern: prefix + this.escapeRegExp(name2) + "=([^&]+)",
26578
+ name: name2
26579
+ });
26580
+ }
26581
+ return patterns;
26582
+ }
26583
+ let pattern;
26584
+ const name = part.name;
26585
+ switch (part.operator) {
26586
+ case "":
26587
+ pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)";
26588
+ break;
26589
+ case "+":
26590
+ case "#":
26591
+ pattern = "(.+)";
26592
+ break;
26593
+ case ".":
26594
+ pattern = "\\.([^/,]+)";
26595
+ break;
26596
+ case "/":
26597
+ pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)");
26598
+ break;
26599
+ default:
26600
+ pattern = "([^/]+)";
26601
+ }
26602
+ patterns.push({ pattern, name });
26603
+ return patterns;
26604
+ }
26605
+ match(uri) {
26606
+ _UriTemplate.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI");
26607
+ let pattern = "^";
26608
+ const names = [];
26609
+ for (const part of this.parts) {
26610
+ if (typeof part === "string") {
26611
+ pattern += this.escapeRegExp(part);
26612
+ } else {
26613
+ const patterns = this.partToRegExp(part);
26614
+ for (const { pattern: partPattern, name } of patterns) {
26615
+ pattern += partPattern;
26616
+ names.push({ name, exploded: part.exploded });
26617
+ }
26618
+ }
26619
+ }
26620
+ pattern += "$";
26621
+ _UriTemplate.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
26622
+ const regex = new RegExp(pattern);
26623
+ const match = uri.match(regex);
26624
+ if (!match)
26625
+ return null;
26626
+ const result = {};
26627
+ for (let i = 0; i < names.length; i++) {
26628
+ const { name, exploded } = names[i];
26629
+ const value = match[i + 1];
26630
+ const cleanName = name.replace("*", "");
26631
+ if (exploded && value.includes(",")) {
26632
+ result[cleanName] = value.split(",");
26633
+ } else {
26634
+ result[cleanName] = value;
26635
+ }
26636
+ }
26637
+ return result;
26638
+ }
26639
+ };
26640
+
26158
26641
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
26159
26642
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
26160
26643
  function validateToolName(name) {
@@ -26944,6 +27427,30 @@ var McpServer = class {
26944
27427
  }
26945
27428
  }
26946
27429
  };
27430
+ var ResourceTemplate = class {
27431
+ constructor(uriTemplate, _callbacks) {
27432
+ this._callbacks = _callbacks;
27433
+ this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
27434
+ }
27435
+ /**
27436
+ * Gets the URI template pattern.
27437
+ */
27438
+ get uriTemplate() {
27439
+ return this._uriTemplate;
27440
+ }
27441
+ /**
27442
+ * Gets the list callback, if one was provided.
27443
+ */
27444
+ get listCallback() {
27445
+ return this._callbacks.list;
27446
+ }
27447
+ /**
27448
+ * Gets the callback for completing a specific URI template variable, if one was provided.
27449
+ */
27450
+ completeCallback(variable) {
27451
+ return this._callbacks.complete?.[variable];
27452
+ }
27453
+ };
26947
27454
  var EMPTY_OBJECT_JSON_SCHEMA = {
26948
27455
  type: "object",
26949
27456
  properties: {}
@@ -27268,7 +27775,7 @@ function readSecretIfExists() {
27268
27775
 
27269
27776
  // ../core/dist/auth/client.js
27270
27777
  var DEFAULT_BASE_URL = "http://127.0.0.1:9999";
27271
- var DEFAULT_TIMEOUT_MS = 3e3;
27778
+ var DEFAULT_TIMEOUT_MS = 1e4;
27272
27779
  var RETRY_COUNT = 2;
27273
27780
  var RETRY_BACKOFF_MS = 250;
27274
27781
  var AuthClient = class {
@@ -27371,8 +27878,8 @@ var AuthClient = class {
27371
27878
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
27372
27879
  }
27373
27880
  }
27374
- async request(method, path49, body, attempt = 0) {
27375
- const url2 = `${this.baseUrl}${path49}`;
27881
+ async request(method, path52, body, attempt = 0) {
27882
+ const url2 = `${this.baseUrl}${path52}`;
27376
27883
  const controller = new AbortController();
27377
27884
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
27378
27885
  const headers = {};
@@ -27390,7 +27897,7 @@ var AuthClient = class {
27390
27897
  } catch (err) {
27391
27898
  if (attempt < RETRY_COUNT && isTransient(err)) {
27392
27899
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
27393
- return this.request(method, path49, body, attempt + 1);
27900
+ return this.request(method, path52, body, attempt + 1);
27394
27901
  }
27395
27902
  throw err;
27396
27903
  } finally {
@@ -28244,12 +28751,12 @@ function register3(server, _ctx, _initError) {
28244
28751
  registry2.close();
28245
28752
  }
28246
28753
  try {
28247
- const { default: fs49 } = await import("node:fs");
28248
- const { default: os28 } = await import("node:os");
28249
- const { default: path49 } = await import("node:path");
28250
- const tokenPath = path49.join(os28.homedir(), ".olam", "host-cp.token");
28251
- if (fs49.existsSync(tokenPath)) {
28252
- const token = fs49.readFileSync(tokenPath, "utf-8").trim();
28754
+ const { default: fs51 } = await import("node:fs");
28755
+ const { default: os31 } = await import("node:os");
28756
+ const { default: path52 } = await import("node:path");
28757
+ const tokenPath = path52.join(os31.homedir(), ".olam", "host-cp.token");
28758
+ if (fs51.existsSync(tokenPath)) {
28759
+ const token = fs51.readFileSync(tokenPath, "utf-8").trim();
28253
28760
  await fetch("http://127.0.0.1:19000/api/admin/world-pr", {
28254
28761
  method: "POST",
28255
28762
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
@@ -28669,10 +29176,10 @@ function extractMcpConfig(claudeJsonPath) {
28669
29176
  }
28670
29177
  return { mcpServers, secrets };
28671
29178
  }
28672
- function readOptional(path49) {
28673
- if (!existsSync6(path49)) return null;
29179
+ function readOptional(path52) {
29180
+ if (!existsSync6(path52)) return null;
28674
29181
  try {
28675
- return readFileSync5(path49, "utf8");
29182
+ return readFileSync5(path52, "utf8");
28676
29183
  } catch {
28677
29184
  return null;
28678
29185
  }
@@ -30118,8 +30625,8 @@ var CloudflareProvider = class extends ComputeProvider {
30118
30625
  // -----------------------------------------------------------------------
30119
30626
  // Internal fetch helper
30120
30627
  // -----------------------------------------------------------------------
30121
- async request(path49, method, body) {
30122
- const url2 = `${this.config.workerUrl}${path49}`;
30628
+ async request(path52, method, body) {
30629
+ const url2 = `${this.config.workerUrl}${path52}`;
30123
30630
  const bearer = await this.config.mintToken();
30124
30631
  const headers = {
30125
30632
  Authorization: `Bearer ${bearer}`
@@ -32570,10 +33077,10 @@ async function writeManifest(args) {
32570
33077
  capturedAt: args.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
32571
33078
  shots: entries
32572
33079
  };
32573
- const path49 = join13(args.outDir, "manifest.json");
32574
- await writeFile(path49, `${JSON.stringify(manifest, null, 2)}
33080
+ const path52 = join13(args.outDir, "manifest.json");
33081
+ await writeFile(path52, `${JSON.stringify(manifest, null, 2)}
32575
33082
  `, "utf8");
32576
- return { path: path49, manifest };
33083
+ return { path: path52, manifest };
32577
33084
  }
32578
33085
 
32579
33086
  // ../mcp-server/src/tools/_capture/proxy.ts
@@ -32827,9 +33334,9 @@ async function startProxy(opts) {
32827
33334
  const liveCompiled = verified.allowedPaths.map(compileGlob);
32828
33335
  const target = parseRequestTarget(req);
32829
33336
  if (!target) return httpReject(400, "invalid_target");
32830
- const path49 = target.pathname;
32831
- if (!liveCompiled.some((re) => re.test(path49))) {
32832
- return httpReject(403, "outside_allow_list", { path: path49 });
33337
+ const path52 = target.pathname;
33338
+ if (!liveCompiled.some((re) => re.test(path52))) {
33339
+ return httpReject(403, "outside_allow_list", { path: path52 });
32833
33340
  }
32834
33341
  const headerWorld = req.headers[WORLD_ASSERT_HEADER];
32835
33342
  const headerWorldStr = typeof headerWorld === "string" ? headerWorld : Array.isArray(headerWorld) && headerWorld.length > 0 ? headerWorld[0] : void 0;
@@ -33608,14 +34115,14 @@ async function runShot(browser, shot, outDir, format, jpegQuality, allowEval, as
33608
34115
  await page.waitForTimeout(shot.afterLoadMs);
33609
34116
  }
33610
34117
  const ext = format === "jpeg" ? "jpg" : "png";
33611
- const path49 = join14(outDir, `${shot.name}.${ext}`);
34118
+ const path52 = join14(outDir, `${shot.name}.${ext}`);
33612
34119
  await page.screenshot({
33613
- path: path49,
34120
+ path: path52,
33614
34121
  type: format,
33615
34122
  ...format === "jpeg" ? { quality: jpegQuality } : {},
33616
34123
  fullPage: false
33617
34124
  });
33618
- return { name: shot.name, path: path49, urlRedacted: redactUrl(shot.url), viewport };
34125
+ return { name: shot.name, path: path52, urlRedacted: redactUrl(shot.url), viewport };
33619
34126
  } finally {
33620
34127
  await context.close();
33621
34128
  }
@@ -34059,12 +34566,12 @@ function openUrl(url2) {
34059
34566
  var HOST_CP_URL = "http://127.0.0.1:19000";
34060
34567
  async function readHostCpToken2() {
34061
34568
  try {
34062
- const { default: fs49 } = await import("node:fs");
34063
- const { default: os28 } = await import("node:os");
34064
- const { default: path49 } = await import("node:path");
34065
- const tp = path49.join(os28.homedir(), ".olam", "host-cp.token");
34066
- if (!fs49.existsSync(tp)) return { token: null };
34067
- return { token: fs49.readFileSync(tp, "utf-8").trim() };
34569
+ const { default: fs51 } = await import("node:fs");
34570
+ const { default: os31 } = await import("node:os");
34571
+ const { default: path52 } = await import("node:path");
34572
+ const tp = path52.join(os31.homedir(), ".olam", "host-cp.token");
34573
+ if (!fs51.existsSync(tp)) return { token: null };
34574
+ return { token: fs51.readFileSync(tp, "utf-8").trim() };
34068
34575
  } catch {
34069
34576
  return { token: null };
34070
34577
  }
@@ -34324,9 +34831,9 @@ function register22(server, _ctx, _initError) {
34324
34831
  description: external_exports.string().optional().describe("Optional human-readable description."),
34325
34832
  defaultBranch: external_exports.string().optional().describe("Default branch name (e.g. main).")
34326
34833
  },
34327
- async ({ name, path: path49, description, defaultBranch }) => {
34834
+ async ({ name, path: path52, description, defaultBranch }) => {
34328
34835
  try {
34329
- const entry = addRepo({ name, path: path49, description, defaultBranch });
34836
+ const entry = addRepo({ name, path: path52, description, defaultBranch });
34330
34837
  return {
34331
34838
  content: [{
34332
34839
  type: "text",
@@ -34367,9 +34874,9 @@ function register22(server, _ctx, _initError) {
34367
34874
  description: external_exports.string().optional().describe("New description."),
34368
34875
  defaultBranch: external_exports.string().optional().describe("New default branch.")
34369
34876
  },
34370
- async ({ name, path: path49, description, defaultBranch }) => {
34877
+ async ({ name, path: path52, description, defaultBranch }) => {
34371
34878
  try {
34372
- const entry = updateRepo(name, { path: path49, description, defaultBranch });
34879
+ const entry = updateRepo(name, { path: path52, description, defaultBranch });
34373
34880
  return {
34374
34881
  content: [{
34375
34882
  type: "text",
@@ -34941,10 +35448,196 @@ function register26(server, _ctx, _initError) {
34941
35448
  );
34942
35449
  }
34943
35450
 
35451
+ // ../mcp-server/src/tools/skills-search.ts
35452
+ var skills_search_exports = {};
35453
+ __export(skills_search_exports, {
35454
+ register: () => register27
35455
+ });
35456
+ init_v3();
35457
+ import * as path36 from "node:path";
35458
+ import * as os22 from "node:os";
35459
+
35460
+ // ../mcp-server/src/lib/skills-index.mjs
35461
+ import { createRequire as createRequire4 } from "node:module";
35462
+ import * as fs36 from "node:fs";
35463
+ import * as path35 from "node:path";
35464
+ import * as os21 from "node:os";
35465
+ var VECTOR_DIM = 256;
35466
+ var SCHEMA_VERSION3 = "1";
35467
+ var SCHEMA_KEY = "skills_index_schema_version";
35468
+ var _require4 = createRequire4(import.meta.url);
35469
+ var _Database3 = null;
35470
+ function getDatabase3() {
35471
+ if (_Database3 === null) {
35472
+ _Database3 = _require4("better-sqlite3");
35473
+ }
35474
+ return _Database3;
35475
+ }
35476
+ var STOPWORDS = /* @__PURE__ */ new Set([
35477
+ "a",
35478
+ "an",
35479
+ "and",
35480
+ "the",
35481
+ "of",
35482
+ "to",
35483
+ "in",
35484
+ "on",
35485
+ "for",
35486
+ "with",
35487
+ "by",
35488
+ "is",
35489
+ "are",
35490
+ "be",
35491
+ "or",
35492
+ "as",
35493
+ "at",
35494
+ "it",
35495
+ "this",
35496
+ "that",
35497
+ "from",
35498
+ "use",
35499
+ "used",
35500
+ "when",
35501
+ "if",
35502
+ "via"
35503
+ ]);
35504
+ function fnv1a32(str) {
35505
+ let h = 2166136261;
35506
+ for (let i = 0; i < str.length; i++) {
35507
+ h ^= str.charCodeAt(i);
35508
+ h = Math.imul(h, 16777619);
35509
+ }
35510
+ return h >>> 0;
35511
+ }
35512
+ function tokenize(text) {
35513
+ if (!text) return [];
35514
+ const tokens = [];
35515
+ for (const raw of text.toLowerCase().split(/[^a-z0-9]+/)) {
35516
+ if (raw.length < 2) continue;
35517
+ if (STOPWORDS.has(raw)) continue;
35518
+ tokens.push(raw);
35519
+ }
35520
+ return tokens;
35521
+ }
35522
+ function embed(text) {
35523
+ const vec = new Float32Array(VECTOR_DIM);
35524
+ const tokens = tokenize(text);
35525
+ if (tokens.length === 0) return vec;
35526
+ for (const tok of tokens) {
35527
+ const bucket = fnv1a32(tok) % VECTOR_DIM;
35528
+ vec[bucket] += 1;
35529
+ }
35530
+ let mag = 0;
35531
+ for (let i = 0; i < VECTOR_DIM; i++) mag += vec[i] * vec[i];
35532
+ mag = Math.sqrt(mag);
35533
+ if (mag > 0) {
35534
+ for (let i = 0; i < VECTOR_DIM; i++) vec[i] /= mag;
35535
+ }
35536
+ return vec;
35537
+ }
35538
+ function cosine(a, b) {
35539
+ let dot = 0;
35540
+ for (let i = 0; i < VECTOR_DIM; i++) dot += a[i] * b[i];
35541
+ return dot;
35542
+ }
35543
+ function bufferToVector(buf) {
35544
+ const ab = new ArrayBuffer(buf.byteLength);
35545
+ Buffer.from(ab).set(buf);
35546
+ const vec = new Float32Array(ab);
35547
+ if (vec.length !== VECTOR_DIM) {
35548
+ throw new Error(`vector length mismatch: expected ${VECTOR_DIM}, got ${vec.length}`);
35549
+ }
35550
+ return vec;
35551
+ }
35552
+ function openIndex(dbPath) {
35553
+ if (!fs36.existsSync(dbPath)) {
35554
+ throw new Error(
35555
+ `skills index not found at ${dbPath}. Run \`node scripts/skills-index-build.mjs --out ${dbPath}\` first.`
35556
+ );
35557
+ }
35558
+ const Database = getDatabase3();
35559
+ const db = new Database(dbPath, { readonly: true, fileMustExist: true });
35560
+ let row;
35561
+ try {
35562
+ row = db.prepare("SELECT value FROM meta WHERE key = ?").get(SCHEMA_KEY);
35563
+ } catch (e) {
35564
+ db.close();
35565
+ throw new Error(`skills index at ${dbPath} is not a valid skills DB: ${e.message}`);
35566
+ }
35567
+ if (!row || row.value !== SCHEMA_VERSION3) {
35568
+ db.close();
35569
+ throw new Error(
35570
+ `skills index at ${dbPath} has schema version ${row ? row.value : "(none)"}, expected ${SCHEMA_VERSION3}. Rebuild it.`
35571
+ );
35572
+ }
35573
+ return db;
35574
+ }
35575
+ function searchIndex(dbPath, query, k = 5) {
35576
+ const db = openIndex(dbPath);
35577
+ try {
35578
+ const qvec = embed(query);
35579
+ let qmag = 0;
35580
+ for (let i = 0; i < VECTOR_DIM; i++) qmag += qvec[i] * qvec[i];
35581
+ if (qmag === 0) return [];
35582
+ const rows = db.prepare("SELECT name, description, vector FROM skills").all();
35583
+ const scored = rows.map((r) => {
35584
+ const v = bufferToVector(r.vector);
35585
+ const score = cosine(qvec, v);
35586
+ const snippet = (r.description || "").slice(0, 200);
35587
+ return { name: r.name, score, snippet };
35588
+ });
35589
+ scored.sort((a, b) => b.score - a.score);
35590
+ return scored.slice(0, Math.max(1, k));
35591
+ } finally {
35592
+ db.close();
35593
+ }
35594
+ }
35595
+ var DEFAULT_DB_PATH = path35.join(os21.homedir(), ".olam", "skills.vec.db");
35596
+ var DEFAULT_SOURCE_DIR = path35.join(os21.homedir(), ".claude", "skills");
35597
+
35598
+ // ../mcp-server/src/tools/skills-search.ts
35599
+ function defaultDbPath() {
35600
+ const override = process.env["SKILLS_INDEX_PATH"];
35601
+ if (override) return override;
35602
+ return path36.join(os22.homedir(), ".olam", "skills.vec.db");
35603
+ }
35604
+ function asMessage9(err) {
35605
+ return err instanceof Error ? err.message : String(err);
35606
+ }
35607
+ function ok3(data) {
35608
+ return {
35609
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
35610
+ };
35611
+ }
35612
+ function fail3(err) {
35613
+ return {
35614
+ content: [{ type: "text", text: asMessage9(err) }],
35615
+ isError: true
35616
+ };
35617
+ }
35618
+ function register27(server, _ctx, _initError) {
35619
+ server.tool(
35620
+ "olam_skills_search",
35621
+ "Semantic search over the local Claude skills index. Returns top-k skills ranked by cosine similarity against a deterministic bag-of-words embedding. Requires `scripts/skills-index-build.mjs` to have been run; set `SKILLS_INDEX_PATH` to override the index location (defaults to ~/.olam/skills.vec.db).",
35622
+ {
35623
+ query: external_exports.string().min(1).describe('Natural-language description of the kind of skill you want (e.g. "stage and commit my changes" or "roll out a new release").'),
35624
+ k: external_exports.number().int().min(1).max(50).optional().describe("Number of hits to return (default 5, max 50).")
35625
+ },
35626
+ async ({ query, k }) => {
35627
+ try {
35628
+ const hits = searchIndex(defaultDbPath(), query, k ?? 5);
35629
+ return ok3({ query, k: k ?? 5, count: hits.length, hits });
35630
+ } catch (err) {
35631
+ return fail3(err);
35632
+ }
35633
+ }
35634
+ );
35635
+ }
35636
+
34944
35637
  // ../mcp-server/src/tools/kg-classify.ts
34945
35638
  var kg_classify_exports = {};
34946
35639
  __export(kg_classify_exports, {
34947
- register: () => register27
35640
+ register: () => register28
34948
35641
  });
34949
35642
  init_v3();
34950
35643
 
@@ -34957,8 +35650,8 @@ function port() {
34957
35650
  const n = Number.parseInt(env, 10);
34958
35651
  return Number.isFinite(n) && n > 0 ? n : KG_SERVICE_PORT_DEFAULT;
34959
35652
  }
34960
- function url(path49) {
34961
- return `http://127.0.0.1:${port()}${path49}`;
35653
+ function url(path52) {
35654
+ return `http://127.0.0.1:${port()}${path52}`;
34962
35655
  }
34963
35656
  function kgServiceHealthUrl() {
34964
35657
  return url("/health");
@@ -35019,7 +35712,7 @@ async function status(opts = {}) {
35019
35712
  }
35020
35713
 
35021
35714
  // ../mcp-server/src/tools/kg-classify.ts
35022
- function register27(server, _ctx, _initError) {
35715
+ function register28(server, _ctx, _initError) {
35023
35716
  server.tool(
35024
35717
  "olam_kg_classify",
35025
35718
  "Route a question to kg | grep | both via the 4-layer classifier (kg-service container). Returns route + layer + reason + matched exemplar. Requires olam-kg-service running (see `olam services up`).",
@@ -35067,7 +35760,7 @@ function register27(server, _ctx, _initError) {
35067
35760
  // ../mcp-server/src/tools/kg-doctor.ts
35068
35761
  var kg_doctor_exports = {};
35069
35762
  __export(kg_doctor_exports, {
35070
- register: () => register28
35763
+ register: () => register29
35071
35764
  });
35072
35765
  async function runProbes() {
35073
35766
  const results = [];
@@ -35117,7 +35810,7 @@ async function runProbes() {
35117
35810
  }
35118
35811
  return results;
35119
35812
  }
35120
- function register28(server, _ctx, _initError) {
35813
+ function register29(server, _ctx, _initError) {
35121
35814
  server.tool(
35122
35815
  "olam_kg_doctor",
35123
35816
  "Diagnostic report for olam-kg-service over HTTP: /health readiness, workspace inventory, and /classify round-trip. Returns a JSON `probes` array. If the container isn't running, all probes fail with a hint to run `olam services up`.",
@@ -35153,13 +35846,13 @@ function register28(server, _ctx, _initError) {
35153
35846
  // ../mcp-server/src/tools/kg-install-hook.ts
35154
35847
  var kg_install_hook_exports = {};
35155
35848
  __export(kg_install_hook_exports, {
35156
- register: () => register29
35849
+ register: () => register30
35157
35850
  });
35158
35851
  init_v3();
35159
35852
  init_merge_settings();
35160
- import * as fs36 from "node:fs";
35161
- import * as path35 from "node:path";
35162
- import * as os21 from "node:os";
35853
+ import * as fs37 from "node:fs";
35854
+ import * as path37 from "node:path";
35855
+ import * as os23 from "node:os";
35163
35856
 
35164
35857
  // ../core/dist/kg/hook-template.js
35165
35858
  var KG_HOOK_SENTINEL = "kg-service-v2-classifier-hook";
@@ -35209,12 +35902,12 @@ function buildHookMatcherEntry(opts) {
35209
35902
  // ../mcp-server/src/tools/kg-install-hook.ts
35210
35903
  function settingsPathFor2(scope, projectPath) {
35211
35904
  if (scope === "user") {
35212
- return path35.join(os21.homedir(), ".claude", "settings.json");
35905
+ return path37.join(os23.homedir(), ".claude", "settings.json");
35213
35906
  }
35214
35907
  const root = projectPath ?? process.cwd();
35215
- return path35.join(root, ".claude", "settings.json");
35908
+ return path37.join(root, ".claude", "settings.json");
35216
35909
  }
35217
- function register29(server, _ctx, _initError) {
35910
+ function register30(server, _ctx, _initError) {
35218
35911
  server.tool(
35219
35912
  "olam_kg_install_hook",
35220
35913
  "Install the kg-service PreToolUse hook into .claude/settings.json. Idempotent (sentinel-detected). Default scope=project (writes <cwd>/.claude/settings.json); scope=user writes ~/.claude/settings.json.",
@@ -35226,12 +35919,12 @@ function register29(server, _ctx, _initError) {
35226
35919
  const scope = params.scope === "user" ? "user" : "project";
35227
35920
  const filePath = settingsPathFor2(scope, params.projectPath);
35228
35921
  try {
35229
- fs36.mkdirSync(path35.dirname(filePath), { recursive: true });
35922
+ fs37.mkdirSync(path37.dirname(filePath), { recursive: true });
35230
35923
  let backupPath = null;
35231
- if (fs36.existsSync(filePath)) {
35924
+ if (fs37.existsSync(filePath)) {
35232
35925
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
35233
35926
  backupPath = `${filePath}.olam-bak.${ts}`;
35234
- fs36.copyFileSync(filePath, backupPath);
35927
+ fs37.copyFileSync(filePath, backupPath);
35235
35928
  }
35236
35929
  const result = mergeHomeSettingsJson(filePath, {
35237
35930
  ensureHook: {
@@ -35242,7 +35935,7 @@ function register29(server, _ctx, _initError) {
35242
35935
  });
35243
35936
  if (result.status === "already-present" && backupPath) {
35244
35937
  try {
35245
- fs36.unlinkSync(backupPath);
35938
+ fs37.unlinkSync(backupPath);
35246
35939
  } catch {
35247
35940
  }
35248
35941
  }
@@ -35283,18 +35976,18 @@ function register29(server, _ctx, _initError) {
35283
35976
  // ../mcp-server/src/tools/kg-uninstall-hook.ts
35284
35977
  var kg_uninstall_hook_exports = {};
35285
35978
  __export(kg_uninstall_hook_exports, {
35286
- register: () => register30
35979
+ register: () => register31
35287
35980
  });
35288
35981
  init_v3();
35289
- import * as fs37 from "node:fs";
35290
- import * as path36 from "node:path";
35291
- import * as os22 from "node:os";
35982
+ import * as fs38 from "node:fs";
35983
+ import * as path38 from "node:path";
35984
+ import * as os24 from "node:os";
35292
35985
  function settingsPathFor3(scope, projectPath) {
35293
35986
  if (scope === "user") {
35294
- return path36.join(os22.homedir(), ".claude", "settings.json");
35987
+ return path38.join(os24.homedir(), ".claude", "settings.json");
35295
35988
  }
35296
35989
  const root = projectPath ?? process.cwd();
35297
- return path36.join(root, ".claude", "settings.json");
35990
+ return path38.join(root, ".claude", "settings.json");
35298
35991
  }
35299
35992
  function dropSentinel(matchers) {
35300
35993
  let changed = false;
@@ -35316,7 +36009,7 @@ function dropSentinel(matchers) {
35316
36009
  }
35317
36010
  return { matchers: out, changed };
35318
36011
  }
35319
- function register30(server, _ctx, _initError) {
36012
+ function register31(server, _ctx, _initError) {
35320
36013
  server.tool(
35321
36014
  "olam_kg_uninstall_hook",
35322
36015
  "Remove the kg-service PreToolUse hook from .claude/settings.json. Sentinel-matched: only the olam entry is removed; other PreToolUse hooks are preserved.",
@@ -35328,7 +36021,7 @@ function register30(server, _ctx, _initError) {
35328
36021
  const scope = params.scope === "user" ? "user" : "project";
35329
36022
  const filePath = settingsPathFor3(scope, params.projectPath);
35330
36023
  try {
35331
- if (!fs37.existsSync(filePath)) {
36024
+ if (!fs38.existsSync(filePath)) {
35332
36025
  return {
35333
36026
  content: [
35334
36027
  {
@@ -35342,7 +36035,7 @@ function register30(server, _ctx, _initError) {
35342
36035
  ]
35343
36036
  };
35344
36037
  }
35345
- const raw = fs37.readFileSync(filePath, "utf-8");
36038
+ const raw = fs38.readFileSync(filePath, "utf-8");
35346
36039
  const settings = raw.trim() ? JSON.parse(raw) : {};
35347
36040
  const preToolUse = settings.hooks?.PreToolUse;
35348
36041
  if (!Array.isArray(preToolUse) || preToolUse.length === 0) {
@@ -35377,7 +36070,7 @@ function register30(server, _ctx, _initError) {
35377
36070
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
35378
36071
  const backupPath = `${filePath}.olam-bak.${ts}`;
35379
36072
  try {
35380
- fs37.copyFileSync(filePath, backupPath);
36073
+ fs38.copyFileSync(filePath, backupPath);
35381
36074
  } catch {
35382
36075
  }
35383
36076
  const next = {
@@ -35389,7 +36082,7 @@ function register30(server, _ctx, _initError) {
35389
36082
  if (otherStages.length === 0) delete next.hooks;
35390
36083
  else delete next.hooks.PreToolUse;
35391
36084
  }
35392
- fs37.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
36085
+ fs38.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
35393
36086
  return {
35394
36087
  content: [
35395
36088
  {
@@ -35451,6 +36144,7 @@ var toolModules = [
35451
36144
  runbook_exports,
35452
36145
  skill_source_exports,
35453
36146
  skills_exports,
36147
+ skills_search_exports,
35454
36148
  kg_classify_exports,
35455
36149
  kg_doctor_exports,
35456
36150
  kg_install_hook_exports,
@@ -35462,6 +36156,207 @@ function registerAllTools(server, ctx, initError) {
35462
36156
  }
35463
36157
  }
35464
36158
 
36159
+ // ../mcp-server/src/resources/chunks.ts
36160
+ import fs39 from "node:fs";
36161
+ import os25 from "node:os";
36162
+ import path39 from "node:path";
36163
+ var DEFAULT_HOST_CP_URL = "http://127.0.0.1:19000";
36164
+ var DEFAULT_PLAN_CHAT_SECRET_PATH = path39.join(
36165
+ os25.homedir(),
36166
+ ".olam",
36167
+ "plan-chat-secret"
36168
+ );
36169
+ var URI_SCHEME = "olam:";
36170
+ var URI_HOST = "worlds";
36171
+ var WORLD_ID_RE2 = /^[a-z0-9][a-z0-9-_.]{0,62}$/i;
36172
+ var SESSION_ID_RE = /^[a-zA-Z0-9_\-.]{1,128}$/;
36173
+ var LIMIT_RE = /^[1-9][0-9]{0,5}$/;
36174
+ function loadBearer(bearerOpt, secretPath) {
36175
+ if (typeof bearerOpt === "string" && bearerOpt.length > 0) return bearerOpt;
36176
+ const envBearer = process.env.OLAM_PLAN_CHAT_BEARER?.trim();
36177
+ if (envBearer && envBearer.length > 0) return envBearer;
36178
+ try {
36179
+ const onDisk = fs39.readFileSync(secretPath, "utf8").trim();
36180
+ return onDisk.length > 0 ? onDisk : null;
36181
+ } catch (err) {
36182
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
36183
+ return null;
36184
+ }
36185
+ return null;
36186
+ }
36187
+ }
36188
+ function parseChunksUri(uri) {
36189
+ let parsed;
36190
+ try {
36191
+ parsed = new URL(uri);
36192
+ } catch {
36193
+ return null;
36194
+ }
36195
+ if (parsed.protocol !== URI_SCHEME) return null;
36196
+ if (parsed.host !== URI_HOST) return null;
36197
+ const segments = parsed.pathname.split("/").filter((s) => s.length > 0);
36198
+ if (segments.length !== 2) return null;
36199
+ const [rawWorldId, kind] = segments;
36200
+ if (rawWorldId === void 0 || kind !== "chunks") return null;
36201
+ const worldId = decodeURIComponent(rawWorldId);
36202
+ if (!WORLD_ID_RE2.test(worldId)) return null;
36203
+ const sessionIdRaw = parsed.searchParams.get("sessionId");
36204
+ const limitRaw = parsed.searchParams.get("limit");
36205
+ let sessionId;
36206
+ if (sessionIdRaw !== null) {
36207
+ if (!SESSION_ID_RE.test(sessionIdRaw)) return null;
36208
+ sessionId = sessionIdRaw;
36209
+ }
36210
+ let limit;
36211
+ if (limitRaw !== null) {
36212
+ if (!LIMIT_RE.test(limitRaw)) return null;
36213
+ limit = Number.parseInt(limitRaw, 10);
36214
+ }
36215
+ return { worldId, sessionId, limit };
36216
+ }
36217
+ function buildUpstreamUrl(hostCpUrl, parts) {
36218
+ const upstream = new URL(hostCpUrl);
36219
+ upstream.pathname = "/api/plan-chat/v1/shape";
36220
+ upstream.searchParams.set("table", "chunks");
36221
+ upstream.searchParams.set("world_id", parts.worldId);
36222
+ if (parts.sessionId) {
36223
+ upstream.searchParams.set("session_id", parts.sessionId);
36224
+ }
36225
+ if (typeof parts.limit === "number") {
36226
+ upstream.searchParams.set("limit", String(parts.limit));
36227
+ }
36228
+ upstream.searchParams.set("offset", "-1");
36229
+ return upstream;
36230
+ }
36231
+ function createChunksResource(deps = {}) {
36232
+ const hostCpUrl = deps.hostCpUrl ?? process.env.OLAM_HOST_CP_URL?.trim() ?? DEFAULT_HOST_CP_URL;
36233
+ const fetchImpl = deps.fetchImpl ?? fetch;
36234
+ const secretPath = deps.secretPath ?? DEFAULT_PLAN_CHAT_SECRET_PATH;
36235
+ const resolveBearer = () => loadBearer(deps.bearer, secretPath);
36236
+ return {
36237
+ name: "olam-world-chunks",
36238
+ // RFC 6570 form with required `sessionId` in the template (the SDK's
36239
+ // matcher treats every listed var as required, so listing both
36240
+ // `sessionId` AND `limit` would reject URIs that only pass sessionId).
36241
+ // The optional `limit` param is parsed by `parseChunksUri` from the
36242
+ // concrete URL instead — clients pass `?sessionId=...&limit=...` and
36243
+ // both fields land in `ChunksUriParts`.
36244
+ uriTemplate: "olam://worlds/{worldId}/chunks{?sessionId}",
36245
+ description: "Per-world agent reasoning chunks (the chunks substrate). Append `?sessionId=<id>` to scope to a single planning session \u2014 Electric SQL requires it server-side. Optional `&limit=<n>` caps the returned row count. Returns the Electric shape snapshot as JSON.",
36246
+ mimeType: "application/json",
36247
+ parseUri: parseChunksUri,
36248
+ async read(uri) {
36249
+ const parts = parseChunksUri(uri);
36250
+ if (!parts) {
36251
+ return {
36252
+ ok: false,
36253
+ code: "invalid_uri",
36254
+ message: `Invalid chunks URI: ${uri}. Expected \`olam://worlds/{worldId}/chunks?sessionId=<id>\`.`
36255
+ };
36256
+ }
36257
+ if (!parts.sessionId) {
36258
+ return {
36259
+ ok: false,
36260
+ code: "missing_session_id",
36261
+ message: "sessionId query param is required. Electric SQL enforces per-(world_id,session_id) scope at the proxy layer."
36262
+ };
36263
+ }
36264
+ const bearer = resolveBearer();
36265
+ if (!bearer) {
36266
+ return {
36267
+ ok: false,
36268
+ code: "missing_bearer",
36269
+ message: "No plan-chat bearer available. Set OLAM_PLAN_CHAT_BEARER or ensure ~/.olam/plan-chat-secret exists."
36270
+ };
36271
+ }
36272
+ const upstream = buildUpstreamUrl(hostCpUrl, parts);
36273
+ let res;
36274
+ try {
36275
+ res = await fetchImpl(upstream.toString(), {
36276
+ method: "GET",
36277
+ headers: {
36278
+ authorization: `Bearer ${bearer}`,
36279
+ accept: "application/json"
36280
+ }
36281
+ });
36282
+ } catch (err) {
36283
+ return {
36284
+ ok: false,
36285
+ code: "upstream_unavailable",
36286
+ message: `host-cp /api/plan-chat/v1/shape unreachable: ${err instanceof Error ? err.message : String(err)}`,
36287
+ detail: { upstream: upstream.toString() }
36288
+ };
36289
+ }
36290
+ if (!res.ok) {
36291
+ let body = "";
36292
+ try {
36293
+ body = await res.text();
36294
+ } catch {
36295
+ }
36296
+ return {
36297
+ ok: false,
36298
+ code: "upstream_error",
36299
+ message: `host-cp returned ${res.status} ${res.statusText} for chunks fetch`,
36300
+ detail: { upstream: upstream.toString(), body: body.slice(0, 512) }
36301
+ };
36302
+ }
36303
+ let payload;
36304
+ try {
36305
+ payload = await res.json();
36306
+ } catch (err) {
36307
+ return {
36308
+ ok: false,
36309
+ code: "upstream_error",
36310
+ message: `host-cp returned non-JSON body for chunks fetch: ${err instanceof Error ? err.message : String(err)}`,
36311
+ detail: { upstream: upstream.toString() }
36312
+ };
36313
+ }
36314
+ return {
36315
+ ok: true,
36316
+ uri,
36317
+ mimeType: "application/json",
36318
+ payload
36319
+ };
36320
+ }
36321
+ };
36322
+ }
36323
+
36324
+ // ../mcp-server/src/resources/index.ts
36325
+ function registerAllResources(server, deps = {}) {
36326
+ const chunks = deps.chunks ?? createChunksResource();
36327
+ server.registerResource(
36328
+ chunks.name,
36329
+ new ResourceTemplate(chunks.uriTemplate, {
36330
+ list: void 0
36331
+ }),
36332
+ {
36333
+ description: chunks.description,
36334
+ mimeType: chunks.mimeType
36335
+ },
36336
+ async (uri) => {
36337
+ const result = await chunks.read(uri.toString());
36338
+ if (!result.ok) {
36339
+ const err = new Error(result.message);
36340
+ err.code = result.code;
36341
+ throw err;
36342
+ }
36343
+ return {
36344
+ contents: [
36345
+ {
36346
+ uri: result.uri,
36347
+ mimeType: result.mimeType,
36348
+ text: JSON.stringify(result.payload)
36349
+ }
36350
+ ]
36351
+ };
36352
+ }
36353
+ );
36354
+ logger.info("Olam MCP resources registered", {
36355
+ resources: [chunks.name],
36356
+ uriTemplate: chunks.uriTemplate
36357
+ });
36358
+ }
36359
+
35465
36360
  // ../mcp-server/src/server.ts
35466
36361
  var SERVER_NAME = "olam";
35467
36362
  var SERVER_VERSION = "0.1.0";
@@ -35477,6 +36372,7 @@ function createServer4(ctx, initError) {
35477
36372
  { instructions: SERVER_INSTRUCTIONS }
35478
36373
  );
35479
36374
  registerAllTools(server, ctx, initError);
36375
+ registerAllResources(server);
35480
36376
  logger.info("Olam MCP server created", {
35481
36377
  name: SERVER_NAME,
35482
36378
  version: SERVER_VERSION,
@@ -35486,8 +36382,8 @@ function createServer4(ctx, initError) {
35486
36382
  }
35487
36383
 
35488
36384
  // ../mcp-server/src/utils/native-probe.ts
35489
- import { createRequire as createRequire4 } from "node:module";
35490
- var PROBE_REQUIRE = createRequire4(import.meta.url);
36385
+ import { createRequire as createRequire5 } from "node:module";
36386
+ var PROBE_REQUIRE = createRequire5(import.meta.url);
35491
36387
  function runtimeModuleVersion() {
35492
36388
  return Number.parseInt(process.versions.modules, 10);
35493
36389
  }
@@ -35604,9 +36500,9 @@ init_loader();
35604
36500
  // ../core/dist/world/manager.js
35605
36501
  import * as crypto8 from "node:crypto";
35606
36502
  import { execSync as execSync5, spawnSync as spawnSync4 } from "node:child_process";
35607
- import * as fs46 from "node:fs";
35608
- import * as os26 from "node:os";
35609
- import * as path46 from "node:path";
36503
+ import * as fs48 from "node:fs";
36504
+ import * as os29 from "node:os";
36505
+ import * as path49 from "node:path";
35610
36506
 
35611
36507
  // ../core/dist/world/state.js
35612
36508
  var VALID_TRANSITIONS = {
@@ -35702,8 +36598,8 @@ function resolveDevboxImage(config2, tag) {
35702
36598
 
35703
36599
  // ../core/dist/world/worktree.js
35704
36600
  import { execFileSync as execFileSync4 } from "node:child_process";
35705
- import * as fs38 from "node:fs";
35706
- import * as path37 from "node:path";
36601
+ import * as fs40 from "node:fs";
36602
+ import * as path40 from "node:path";
35707
36603
  function resolveGitDir(repo) {
35708
36604
  if (repo.path) {
35709
36605
  return repo.path;
@@ -35713,11 +36609,11 @@ function resolveGitDir(repo) {
35713
36609
  async function createWorktrees(repos, worldId, workspacePath, branch) {
35714
36610
  const created = [];
35715
36611
  for (const repo of repos) {
35716
- const worktreePath = path37.join(workspacePath, repo.name);
36612
+ const worktreePath = path40.join(workspacePath, repo.name);
35717
36613
  const gitDir = resolveGitDir(repo);
35718
36614
  const branchName = branch || `olam/${worldId}`;
35719
36615
  try {
35720
- fs38.mkdirSync(path37.dirname(worktreePath), { recursive: true });
36616
+ fs40.mkdirSync(path40.dirname(worktreePath), { recursive: true });
35721
36617
  execFileSync4("git", ["worktree", "add", worktreePath, "-b", branchName], {
35722
36618
  cwd: gitDir,
35723
36619
  stdio: "pipe"
@@ -35750,7 +36646,7 @@ async function createWorktrees(repos, worldId, workspacePath, branch) {
35750
36646
  }
35751
36647
  async function removeWorktrees(repos, workspacePath) {
35752
36648
  for (const repo of repos) {
35753
- const worktreePath = path37.join(workspacePath, repo.name);
36649
+ const worktreePath = path40.join(workspacePath, repo.name);
35754
36650
  let gitDir;
35755
36651
  try {
35756
36652
  gitDir = resolveGitDir(repo);
@@ -35825,12 +36721,12 @@ function removeBranch(repo, branch) {
35825
36721
 
35826
36722
  // ../core/dist/world/kg-overlay.js
35827
36723
  import { execFileSync as execFileSync5 } from "node:child_process";
35828
- import * as fs39 from "node:fs";
35829
- import * as path38 from "node:path";
36724
+ import * as fs41 from "node:fs";
36725
+ import * as path41 from "node:path";
35830
36726
 
35831
36727
  // ../core/dist/kg/storage-paths.js
35832
- import { homedir as homedir22 } from "node:os";
35833
- import { join as join38, resolve as resolve9 } from "node:path";
36728
+ import { homedir as homedir24 } from "node:os";
36729
+ import { join as join41, resolve as resolve9 } from "node:path";
35834
36730
 
35835
36731
  // ../core/dist/world/workspace-name.js
35836
36732
  var InvalidWorkspaceNameError = class extends Error {
@@ -35851,25 +36747,25 @@ function validateWorkspaceName(name) {
35851
36747
 
35852
36748
  // ../core/dist/kg/storage-paths.js
35853
36749
  function olamHome() {
35854
- return process.env.OLAM_HOME ?? join38(homedir22(), ".olam");
36750
+ return process.env.OLAM_HOME ?? join41(homedir24(), ".olam");
35855
36751
  }
35856
36752
  function kgRoot() {
35857
- return join38(olamHome(), "kg");
36753
+ return join41(olamHome(), "kg");
35858
36754
  }
35859
36755
  function worldsRoot() {
35860
- return join38(olamHome(), "worlds");
36756
+ return join41(olamHome(), "worlds");
35861
36757
  }
35862
- function assertWithinPrefix(path49, prefix, label) {
35863
- if (!path49.startsWith(prefix + "/")) {
35864
- throw new Error(`${label} escape: ${path49} not under ${prefix}/`);
36758
+ function assertWithinPrefix(path52, prefix, label) {
36759
+ if (!path52.startsWith(prefix + "/")) {
36760
+ throw new Error(`${label} escape: ${path52} not under ${prefix}/`);
35865
36761
  }
35866
36762
  }
35867
36763
  function kgPristinePath(workspace) {
35868
36764
  validateWorkspaceName(workspace);
35869
36765
  const root = kgRoot();
35870
- const path49 = resolve9(join38(root, workspace));
35871
- assertWithinPrefix(path49, root, "kgPristinePath");
35872
- return path49;
36766
+ const path52 = resolve9(join41(root, workspace));
36767
+ assertWithinPrefix(path52, root, "kgPristinePath");
36768
+ return path52;
35873
36769
  }
35874
36770
  var KG_PATHS_INTERNALS = Object.freeze({
35875
36771
  olamHome,
@@ -35885,10 +36781,10 @@ var KgOverlayError = class extends Error {
35885
36781
  }
35886
36782
  };
35887
36783
  function ensureGitignoreEntry(worldClonePath) {
35888
- const gitignorePath = path38.join(worldClonePath, ".gitignore");
35889
- if (!fs39.existsSync(gitignorePath))
36784
+ const gitignorePath = path41.join(worldClonePath, ".gitignore");
36785
+ if (!fs41.existsSync(gitignorePath))
35890
36786
  return "no-gitignore";
35891
- const content = fs39.readFileSync(gitignorePath, "utf-8");
36787
+ const content = fs41.readFileSync(gitignorePath, "utf-8");
35892
36788
  const lines = content.split("\n").map((l) => l.trim());
35893
36789
  const recognised = /* @__PURE__ */ new Set([
35894
36790
  "graphify-out",
@@ -35903,24 +36799,24 @@ function ensureGitignoreEntry(worldClonePath) {
35903
36799
  const eol = content.includes("\r\n") ? "\r\n" : "\n";
35904
36800
  const needsLeadingNewline = content.length > 0 && !content.endsWith(eol);
35905
36801
  const block = `${needsLeadingNewline ? eol : ""}${eol}# olam-kg-service: per-world KG overlay (Phase B1)${eol}graphify-out/${eol}`;
35906
- fs39.appendFileSync(gitignorePath, block, "utf-8");
36802
+ fs41.appendFileSync(gitignorePath, block, "utf-8");
35907
36803
  return "appended";
35908
36804
  }
35909
36805
  function createWorldOverlay(opts) {
35910
36806
  const pristineRoot = kgPristinePath(opts.workspace);
35911
- const pristinePath = path38.join(pristineRoot, "graphify-out");
35912
- if (!fs39.existsSync(pristinePath)) {
36807
+ const pristinePath = path41.join(pristineRoot, "graphify-out");
36808
+ if (!fs41.existsSync(pristinePath)) {
35913
36809
  throw new KgOverlayError(`Pristine KG for workspace ${JSON.stringify(opts.workspace)} not found at ${pristinePath}. Run \`olam kg build ${opts.workspace}\` first.`);
35914
36810
  }
35915
- if (!path38.isAbsolute(opts.worldClonePath)) {
36811
+ if (!path41.isAbsolute(opts.worldClonePath)) {
35916
36812
  throw new KgOverlayError(`worldClonePath must be absolute (got ${opts.worldClonePath})`);
35917
36813
  }
35918
- if (!fs39.existsSync(opts.worldClonePath)) {
36814
+ if (!fs41.existsSync(opts.worldClonePath)) {
35919
36815
  throw new KgOverlayError(`worldClonePath does not exist: ${opts.worldClonePath}. Create the clone before reflinking.`);
35920
36816
  }
35921
- const overlayPath = path38.join(opts.worldClonePath, "graphify-out");
35922
- if (fs39.existsSync(overlayPath)) {
35923
- fs39.rmSync(overlayPath, { recursive: true, force: true });
36817
+ const overlayPath = path41.join(opts.worldClonePath, "graphify-out");
36818
+ if (fs41.existsSync(overlayPath)) {
36819
+ fs41.rmSync(overlayPath, { recursive: true, force: true });
35924
36820
  }
35925
36821
  const useReflink = process.platform === "darwin";
35926
36822
  let strategy;
@@ -35938,7 +36834,7 @@ function createWorldOverlay(opts) {
35938
36834
  } else {
35939
36835
  strategy = "cp-r";
35940
36836
  }
35941
- if (strategy === "cp-r" || !fs39.existsSync(overlayPath)) {
36837
+ if (strategy === "cp-r" || !fs41.existsSync(overlayPath)) {
35942
36838
  try {
35943
36839
  execFileSync5("cp", ["-r", pristinePath, opts.worldClonePath], {
35944
36840
  stdio: ["ignore", "ignore", "pipe"]
@@ -35950,7 +36846,7 @@ function createWorldOverlay(opts) {
35950
36846
  throw new KgOverlayError(`cp -r failed: ${msg}${reflinkMsg}`);
35951
36847
  }
35952
36848
  }
35953
- if (!fs39.existsSync(overlayPath)) {
36849
+ if (!fs41.existsSync(overlayPath)) {
35954
36850
  throw new KgOverlayError(`Overlay creation produced no ${overlayPath} after cp \u2014 filesystem returned without error?`);
35955
36851
  }
35956
36852
  const gitignoreAction = ensureGitignoreEntry(opts.worldClonePath);
@@ -35964,12 +36860,12 @@ function createWorldOverlay(opts) {
35964
36860
 
35965
36861
  // ../core/dist/world/baseline-diff.js
35966
36862
  import { execFileSync as execFileSync6 } from "node:child_process";
35967
- import * as fs40 from "node:fs";
35968
- import * as os23 from "node:os";
35969
- import * as path39 from "node:path";
36863
+ import * as fs42 from "node:fs";
36864
+ import * as os26 from "node:os";
36865
+ import * as path42 from "node:path";
35970
36866
  var DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
35971
- function expandHome2(p, homedir28) {
35972
- return p.replace(/^~(?=$|\/|\\)/, homedir28());
36867
+ function expandHome2(p, homedir30) {
36868
+ return p.replace(/^~(?=$|\/|\\)/, homedir30());
35973
36869
  }
35974
36870
  function sanitizeRepoFilename(name) {
35975
36871
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -35992,10 +36888,10 @@ ${stderr}`;
35992
36888
  }
35993
36889
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
35994
36890
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync6(cmd, args, opts));
35995
- const homedir28 = deps.homedir ?? (() => os23.homedir());
35996
- const baselineDir = path39.join(workspacePath, ".olam", "baseline");
36891
+ const homedir30 = deps.homedir ?? (() => os26.homedir());
36892
+ const baselineDir = path42.join(workspacePath, ".olam", "baseline");
35997
36893
  try {
35998
- fs40.mkdirSync(baselineDir, { recursive: true });
36894
+ fs42.mkdirSync(baselineDir, { recursive: true });
35999
36895
  } catch (err) {
36000
36896
  const msg = err instanceof Error ? err.message : String(err);
36001
36897
  console.warn(`[baseline-diff] mkdir ${baselineDir} failed: ${msg}; reaper will see no baseline at all`);
@@ -36007,9 +36903,9 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
36007
36903
  if (!repo.path)
36008
36904
  continue;
36009
36905
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
36010
- const outPath = path39.join(baselineDir, filename);
36011
- const repoPath = expandHome2(repo.path, homedir28);
36012
- if (!fs40.existsSync(repoPath)) {
36906
+ const outPath = path42.join(baselineDir, filename);
36907
+ const repoPath = expandHome2(repo.path, homedir30);
36908
+ if (!fs42.existsSync(repoPath)) {
36013
36909
  writeBaselineFile(outPath, `# repo: ${repo.name}
36014
36910
  # (skipped: path ${repoPath} does not exist)
36015
36911
  `);
@@ -36076,7 +36972,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
36076
36972
  }
36077
36973
  function writeBaselineFile(outPath, content) {
36078
36974
  try {
36079
- fs40.writeFileSync(outPath, content);
36975
+ fs42.writeFileSync(outPath, content);
36080
36976
  } catch (err) {
36081
36977
  const msg = err instanceof Error ? err.message : String(err);
36082
36978
  console.warn(`[baseline-diff] write to ${outPath} failed: ${msg}`);
@@ -36084,8 +36980,8 @@ function writeBaselineFile(outPath, content) {
36084
36980
  }
36085
36981
  function stripWorktreeEdits(repos, workspacePath) {
36086
36982
  for (const repo of repos) {
36087
- const worktreePath = path39.join(workspacePath, repo.name);
36088
- if (!fs40.existsSync(worktreePath))
36983
+ const worktreePath = path42.join(workspacePath, repo.name);
36984
+ if (!fs42.existsSync(worktreePath))
36089
36985
  continue;
36090
36986
  try {
36091
36987
  execFileSync6("git", ["checkout", "--", "."], {
@@ -36144,21 +37040,21 @@ function extractStderr(err) {
36144
37040
  }
36145
37041
  function carryUncommittedEdits(repos, workspacePath, deps = {}) {
36146
37042
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync6(cmd, args, opts));
36147
- const homedir28 = deps.homedir ?? (() => os23.homedir());
36148
- const existsSync46 = deps.existsSync ?? ((p) => fs40.existsSync(p));
36149
- const copyFileSync9 = deps.copyFileSync ?? ((src, dest) => fs40.copyFileSync(src, dest));
36150
- const mkdirSync29 = deps.mkdirSync ?? ((dirPath, opts) => {
36151
- fs40.mkdirSync(dirPath, opts);
37043
+ const homedir30 = deps.homedir ?? (() => os26.homedir());
37044
+ const existsSync48 = deps.existsSync ?? ((p) => fs42.existsSync(p));
37045
+ const copyFileSync9 = deps.copyFileSync ?? ((src, dest) => fs42.copyFileSync(src, dest));
37046
+ const mkdirSync30 = deps.mkdirSync ?? ((dirPath, opts) => {
37047
+ fs42.mkdirSync(dirPath, opts);
36152
37048
  });
36153
37049
  const plans = [];
36154
37050
  for (const repo of repos) {
36155
37051
  if (!repo.path)
36156
37052
  continue;
36157
- const repoPath = expandHome2(repo.path, homedir28);
36158
- const worktreePath = path39.join(workspacePath, repo.name);
36159
- if (!existsSync46(repoPath))
37053
+ const repoPath = expandHome2(repo.path, homedir30);
37054
+ const worktreePath = path42.join(workspacePath, repo.name);
37055
+ if (!existsSync48(repoPath))
36160
37056
  continue;
36161
- if (!existsSync46(worktreePath)) {
37057
+ if (!existsSync48(worktreePath)) {
36162
37058
  console.warn(`[carry] ${repo.name}: world worktree ${worktreePath} missing; skipping carry for this repo`);
36163
37059
  continue;
36164
37060
  }
@@ -36216,12 +37112,12 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
36216
37112
  }
36217
37113
  }
36218
37114
  for (const rel of plan.diff.untracked) {
36219
- const src = path39.join(plan.repoPath, rel);
36220
- const dest = path39.join(plan.worktreePath, rel);
36221
- if (!existsSync46(src))
37115
+ const src = path42.join(plan.repoPath, rel);
37116
+ const dest = path42.join(plan.worktreePath, rel);
37117
+ if (!existsSync48(src))
36222
37118
  continue;
36223
37119
  try {
36224
- mkdirSync29(path39.dirname(dest), { recursive: true });
37120
+ mkdirSync30(path42.dirname(dest), { recursive: true });
36225
37121
  copyFileSync9(src, dest);
36226
37122
  } catch (err) {
36227
37123
  const msg = err instanceof Error ? err.message : String(err);
@@ -36247,8 +37143,8 @@ function formatBaselineSummary(result) {
36247
37143
  }
36248
37144
 
36249
37145
  // ../core/dist/world/context-injection.js
36250
- import * as fs41 from "node:fs";
36251
- import * as path40 from "node:path";
37146
+ import * as fs43 from "node:fs";
37147
+ import * as path43 from "node:path";
36252
37148
 
36253
37149
  // ../core/dist/world/templates/_generated.js
36254
37150
  var GH_PR_CREATE = '# Creating PRs from inside an Olam world\n\n## The problem\n\nCalling `gh pr create` directly inside an Olam container **hangs forever** on a\nTTY confirmation prompt that the agent cannot answer. This is a known, reproduced issue.\n\n## The fix\n\nAlways use this exact pattern:\n\n```bash\necho y | timeout 30 gh pr create \\\n --base main \\\n --head <branch> \\\n --title "<title>" \\\n --body-file /tmp/pr-body.md\n```\n\n## Why each part matters\n\n- **`echo y |`** \u2014 answers the "Submit?" confirmation prompt up front so `gh` never pauses.\n- **`timeout 30`** \u2014 hard deadline. If `gh` hangs anyway, the command exits non-zero so the\n agent can react instead of stalling the world indefinitely.\n- **`--body-file /tmp/pr-body.md`** \u2014 write the PR body to a temp file first. Avoids\n shell-escaping bugs that occur with `--body "..."` for long or multi-line bodies.\n- **`--head` and `--base` explicitly** \u2014 removes all interactive prompts; `gh` has nothing to ask.\n\n## Troubleshooting\n\n**"no commits" or "branch not found" error:**\n\n```bash\ngit push -u origin <branch>\n# then retry:\necho y | timeout 30 gh pr create --base main --head <branch> --title "..." --body-file /tmp/pr-body.md\n```\n\n**Timeout exceeded (command exits 124):**\n\n- Check if the branch was pushed: `git log origin/<branch> --oneline -1`\n- Check GitHub CLI auth: `gh auth status`\n- Retry once; if it hangs again, open the PR via the GitHub web UI.\n';
@@ -36258,10 +37154,10 @@ var WORLD_CLAUDE_MD = '# Olam World: {{worldName}}\n\n{{taskBlock}}\n\n## Enviro
36258
37154
  // ../core/dist/world/context-injection.js
36259
37155
  function injectWorldContext(opts) {
36260
37156
  const { world } = opts;
36261
- const claudeDir2 = path40.join(world.workspacePath, ".claude");
36262
- fs41.mkdirSync(claudeDir2, { recursive: true });
37157
+ const claudeDir2 = path43.join(world.workspacePath, ".claude");
37158
+ fs43.mkdirSync(claudeDir2, { recursive: true });
36263
37159
  const content = WORLD_CLAUDE_MD.replace("{{worldName}}", world.name).replace("{{worldId}}", world.id).replace("{{branch}}", world.branch).replace("{{taskBlock}}", buildTaskBlock(opts)).replace("{{reposList}}", buildReposList(world)).replace("{{servicesLine}}", buildServicesLine(opts.services)).replace("{{pleriPlaneLine}}", buildPleriPlaneLine(opts.pleriPlaneUrl)).replace("{{planFileBlock}}", buildPlanFileBlock(world)).replace("{{extraContextBlock}}", buildExtraContextBlock(opts.claudeMdExtra));
36264
- fs41.writeFileSync(path40.join(claudeDir2, "CLAUDE.md"), content);
37160
+ fs43.writeFileSync(path43.join(claudeDir2, "CLAUDE.md"), content);
36265
37161
  writeOlamDocs(world.workspacePath);
36266
37162
  }
36267
37163
  function buildTaskBlock(opts) {
@@ -36335,10 +37231,10 @@ function buildExtraContextBlock(extra) {
36335
37231
  ${extra}`;
36336
37232
  }
36337
37233
  function writeOlamDocs(workspacePath) {
36338
- const docsDir = path40.join(workspacePath, ".olam", "docs");
36339
- fs41.mkdirSync(docsDir, { recursive: true });
36340
- fs41.writeFileSync(path40.join(docsDir, "gh-pr-create.md"), GH_PR_CREATE);
36341
- fs41.writeFileSync(path40.join(docsDir, "lane-orchestration.md"), LANE_ORCHESTRATION);
37234
+ const docsDir = path43.join(workspacePath, ".olam", "docs");
37235
+ fs43.mkdirSync(docsDir, { recursive: true });
37236
+ fs43.writeFileSync(path43.join(docsDir, "gh-pr-create.md"), GH_PR_CREATE);
37237
+ fs43.writeFileSync(path43.join(docsDir, "lane-orchestration.md"), LANE_ORCHESTRATION);
36342
37238
  }
36343
37239
  function formatTaskSource(ctx) {
36344
37240
  if (ctx.source === "linear" && ctx.ticketId) {
@@ -36352,9 +37248,9 @@ function formatTaskSource(ctx) {
36352
37248
  function hasPlanFile(world) {
36353
37249
  if (world.repos.length === 0)
36354
37250
  return false;
36355
- const plansDir = path40.join(world.workspacePath, world.repos[0], "docs", "plans");
37251
+ const plansDir = path43.join(world.workspacePath, world.repos[0], "docs", "plans");
36356
37252
  try {
36357
- return fs41.existsSync(plansDir) && fs41.readdirSync(plansDir).length > 0;
37253
+ return fs43.existsSync(plansDir) && fs43.readdirSync(plansDir).length > 0;
36358
37254
  } catch {
36359
37255
  return false;
36360
37256
  }
@@ -36926,25 +37822,25 @@ init_repo_manifest();
36926
37822
 
36927
37823
  // ../core/dist/world/snapshot.js
36928
37824
  import * as crypto7 from "node:crypto";
36929
- import * as fs42 from "node:fs";
36930
- import * as os24 from "node:os";
36931
- import * as path41 from "node:path";
37825
+ import * as fs44 from "node:fs";
37826
+ import * as os27 from "node:os";
37827
+ import * as path44 from "node:path";
36932
37828
  import { execFileSync as execFileSync7, spawn as spawn2 } from "node:child_process";
36933
37829
  import { gunzipSync } from "node:zlib";
36934
37830
  function snapshotsDir() {
36935
- return process.env["OLAM_SNAPSHOTS_DIR"] ?? path41.join(os24.homedir(), ".olam", "snapshots");
37831
+ return process.env["OLAM_SNAPSHOTS_DIR"] ?? path44.join(os27.homedir(), ".olam", "snapshots");
36936
37832
  }
36937
37833
  function snapshotKindDirByWorkspace(workspace, arch, kind) {
36938
- return path41.join(snapshotsDir(), "by-workspace", workspace, arch, kind);
37834
+ return path44.join(snapshotsDir(), "by-workspace", workspace, arch, kind);
36939
37835
  }
36940
37836
  function cleanupLegacyByWorldDir(worldId) {
36941
- const legacyDir = path41.join(snapshotsDir(), worldId);
37837
+ const legacyDir = path44.join(snapshotsDir(), worldId);
36942
37838
  if (worldId === "by-workspace")
36943
37839
  return;
36944
- if (!fs42.existsSync(legacyDir))
37840
+ if (!fs44.existsSync(legacyDir))
36945
37841
  return;
36946
37842
  try {
36947
- fs42.rmSync(legacyDir, { recursive: true, force: true });
37843
+ fs44.rmSync(legacyDir, { recursive: true, force: true });
36948
37844
  } catch {
36949
37845
  }
36950
37846
  }
@@ -36963,11 +37859,11 @@ function hashBuffers(entries) {
36963
37859
  return hash.digest("hex").slice(0, 12);
36964
37860
  }
36965
37861
  function computeGemsFingerprint(repoDir, imageDigest) {
36966
- const lockfile = path41.join(repoDir, "Gemfile.lock");
36967
- if (!fs42.existsSync(lockfile))
37862
+ const lockfile = path44.join(repoDir, "Gemfile.lock");
37863
+ if (!fs44.existsSync(lockfile))
36968
37864
  return null;
36969
37865
  const entries = [
36970
- { path: "Gemfile.lock", content: fs42.readFileSync(lockfile) }
37866
+ { path: "Gemfile.lock", content: fs44.readFileSync(lockfile) }
36971
37867
  ];
36972
37868
  if (imageDigest) {
36973
37869
  entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
@@ -36977,10 +37873,10 @@ function computeGemsFingerprint(repoDir, imageDigest) {
36977
37873
  function computeNodeFingerprint(repoDir, imageDigest) {
36978
37874
  const candidates = ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"];
36979
37875
  for (const name of candidates) {
36980
- const lockfile = path41.join(repoDir, name);
36981
- if (fs42.existsSync(lockfile)) {
37876
+ const lockfile = path44.join(repoDir, name);
37877
+ if (fs44.existsSync(lockfile)) {
36982
37878
  const entries = [
36983
- { path: name, content: fs42.readFileSync(lockfile) }
37879
+ { path: name, content: fs44.readFileSync(lockfile) }
36984
37880
  ];
36985
37881
  if (imageDigest) {
36986
37882
  entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
@@ -36999,18 +37895,18 @@ function unpackTarballAtomic(srcPath, destDir) {
36999
37895
  detail: validation.detail ?? `unsafe entry: ${validation.unsafePath}`
37000
37896
  };
37001
37897
  }
37002
- const parent = path41.dirname(destDir);
37003
- fs42.mkdirSync(parent, { recursive: true });
37898
+ const parent = path44.dirname(destDir);
37899
+ fs44.mkdirSync(parent, { recursive: true });
37004
37900
  const tmpSuffix = `.tmp-${process.pid}-${crypto7.randomBytes(4).toString("hex")}`;
37005
37901
  const tmpDir = `${destDir}${tmpSuffix}`;
37006
37902
  try {
37007
- fs42.mkdirSync(tmpDir, { recursive: true });
37903
+ fs44.mkdirSync(tmpDir, { recursive: true });
37008
37904
  execFileSync7("tar", ["-xzf", srcPath, "-C", tmpDir], { stdio: "pipe" });
37009
- fs42.renameSync(tmpDir, destDir);
37905
+ fs44.renameSync(tmpDir, destDir);
37010
37906
  return { ok: true, entryCount: validation.entries.length };
37011
37907
  } catch (err) {
37012
37908
  try {
37013
- fs42.rmSync(tmpDir, { recursive: true, force: true });
37909
+ fs44.rmSync(tmpDir, { recursive: true, force: true });
37014
37910
  } catch {
37015
37911
  }
37016
37912
  return {
@@ -37021,12 +37917,12 @@ function unpackTarballAtomic(srcPath, destDir) {
37021
37917
  }
37022
37918
  }
37023
37919
  function resolvesWithin(base, target) {
37024
- const resolved = path41.resolve(base, target);
37025
- const baseResolved = path41.resolve(base);
37026
- const rel = path41.relative(baseResolved, resolved);
37920
+ const resolved = path44.resolve(base, target);
37921
+ const baseResolved = path44.resolve(base);
37922
+ const rel = path44.relative(baseResolved, resolved);
37027
37923
  if (rel === "")
37028
37924
  return true;
37029
- return !rel.startsWith("..") && !path41.isAbsolute(rel);
37925
+ return !rel.startsWith("..") && !path44.isAbsolute(rel);
37030
37926
  }
37031
37927
  var TYPE_CHAR_TO_TYPE = {
37032
37928
  "-": "file",
@@ -37076,7 +37972,7 @@ function parseTarListLine(line) {
37076
37972
  function validateHardlinksBinary(tarPath, targetDir) {
37077
37973
  let raw;
37078
37974
  try {
37079
- raw = gunzipSync(fs42.readFileSync(tarPath));
37975
+ raw = gunzipSync(fs44.readFileSync(tarPath));
37080
37976
  } catch {
37081
37977
  return null;
37082
37978
  }
@@ -37091,7 +37987,7 @@ function validateHardlinksBinary(tarPath, targetDir) {
37091
37987
  const name = block.subarray(0, nameNull >= 0 && nameNull <= 99 ? nameNull : 100).toString("utf-8");
37092
37988
  const linkNull = block.indexOf(0, 157);
37093
37989
  const linkname = block.subarray(157, linkNull >= 157 && linkNull <= 256 ? linkNull : 257).toString("utf-8");
37094
- if (linkname && (path41.isAbsolute(linkname) || !resolvesWithin(targetDir, linkname))) {
37990
+ if (linkname && (path44.isAbsolute(linkname) || !resolvesWithin(targetDir, linkname))) {
37095
37991
  return {
37096
37992
  valid: false,
37097
37993
  reason: "hardlink-escape",
@@ -37129,7 +38025,7 @@ function enumerateAndValidateTarballEntries(tarPath, targetDir) {
37129
38025
  const entry = parseTarListLine(line);
37130
38026
  if (!entry)
37131
38027
  continue;
37132
- if (path41.isAbsolute(entry.name) || !resolvesWithin(targetDir, entry.name)) {
38028
+ if (path44.isAbsolute(entry.name) || !resolvesWithin(targetDir, entry.name)) {
37133
38029
  return {
37134
38030
  valid: false,
37135
38031
  reason: "path-traversal",
@@ -37137,8 +38033,8 @@ function enumerateAndValidateTarballEntries(tarPath, targetDir) {
37137
38033
  };
37138
38034
  }
37139
38035
  if (entry.type === "symlink" && entry.linkname !== void 0) {
37140
- const symlinkParent = path41.join(targetDir, path41.dirname(entry.name));
37141
- if (path41.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, path41.join(path41.dirname(entry.name), entry.linkname))) {
38036
+ const symlinkParent = path44.join(targetDir, path44.dirname(entry.name));
38037
+ if (path44.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, path44.join(path44.dirname(entry.name), entry.linkname))) {
37142
38038
  return {
37143
38039
  valid: false,
37144
38040
  reason: "symlink-escape",
@@ -37148,7 +38044,7 @@ function enumerateAndValidateTarballEntries(tarPath, targetDir) {
37148
38044
  }
37149
38045
  }
37150
38046
  if (entry.type === "hardlink" && entry.linkname !== void 0) {
37151
- if (path41.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, entry.linkname)) {
38047
+ if (path44.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, entry.linkname)) {
37152
38048
  return {
37153
38049
  valid: false,
37154
38050
  reason: "hardlink-escape",
@@ -37181,8 +38077,8 @@ function restoreSnapshotsForRepos(input) {
37181
38077
  }
37182
38078
  const archDir = snapshotKindDirByWorkspace(input.workspace, input.arch, kind);
37183
38079
  const tarFilename = `${repo.name}-${input.arch}-${fingerprint}.tar.gz`;
37184
- const tarPath = path41.join(archDir, tarFilename);
37185
- if (!fs42.existsSync(tarPath)) {
38080
+ const tarPath = path44.join(archDir, tarFilename);
38081
+ if (!fs44.existsSync(tarPath)) {
37186
38082
  outcomes.push({ repo: repo.name, kind, outcome: "miss", reason: "no-tarball", fingerprint });
37187
38083
  continue;
37188
38084
  }
@@ -37197,9 +38093,9 @@ function restoreSnapshotsForRepos(input) {
37197
38093
  });
37198
38094
  continue;
37199
38095
  }
37200
- const targetDir = path41.join(repo.worktreeDir, targetSubpath);
38096
+ const targetDir = path44.join(repo.worktreeDir, targetSubpath);
37201
38097
  try {
37202
- fs42.rmSync(targetDir, { recursive: true, force: true });
38098
+ fs44.rmSync(targetDir, { recursive: true, force: true });
37203
38099
  } catch {
37204
38100
  }
37205
38101
  const result = unpackTarballAtomic(tarPath, targetDir);
@@ -37212,8 +38108,8 @@ function restoreSnapshotsForRepos(input) {
37212
38108
  fingerprint
37213
38109
  });
37214
38110
  try {
37215
- fs42.rmSync(tarPath, { force: true });
37216
- fs42.rmSync(manifestPath(tarPath), { force: true });
38111
+ fs44.rmSync(tarPath, { force: true });
38112
+ fs44.rmSync(manifestPath(tarPath), { force: true });
37217
38113
  } catch {
37218
38114
  }
37219
38115
  continue;
@@ -37229,10 +38125,10 @@ function restoreSnapshotsForRepos(input) {
37229
38125
  }
37230
38126
  function readManifest(tarPath) {
37231
38127
  const mPath = manifestPath(tarPath);
37232
- if (!fs42.existsSync(mPath))
38128
+ if (!fs44.existsSync(mPath))
37233
38129
  return null;
37234
38130
  try {
37235
- return JSON.parse(fs42.readFileSync(mPath, "utf-8"));
38131
+ return JSON.parse(fs44.readFileSync(mPath, "utf-8"));
37236
38132
  } catch {
37237
38133
  return null;
37238
38134
  }
@@ -37247,17 +38143,17 @@ function isPidAlive(pid) {
37247
38143
  }
37248
38144
  }
37249
38145
  function evictOldSnapshotsWithFlock(maxBytes, dir = snapshotsDir()) {
37250
- fs42.mkdirSync(dir, { recursive: true });
37251
- const lockPath = path41.join(dir, EVICT_LOCK_FILENAME);
38146
+ fs44.mkdirSync(dir, { recursive: true });
38147
+ const lockPath = path44.join(dir, EVICT_LOCK_FILENAME);
37252
38148
  let fd;
37253
38149
  try {
37254
- fd = fs42.openSync(lockPath, fs42.constants.O_WRONLY | fs42.constants.O_CREAT | fs42.constants.O_EXCL, 384);
38150
+ fd = fs44.openSync(lockPath, fs44.constants.O_WRONLY | fs44.constants.O_CREAT | fs44.constants.O_EXCL, 384);
37255
38151
  } catch (err) {
37256
38152
  if (err.code !== "EEXIST")
37257
38153
  return 0;
37258
38154
  let holderPid = null;
37259
38155
  try {
37260
- holderPid = parseInt(fs42.readFileSync(lockPath, "utf-8").trim(), 10);
38156
+ holderPid = parseInt(fs44.readFileSync(lockPath, "utf-8").trim(), 10);
37261
38157
  } catch {
37262
38158
  holderPid = null;
37263
38159
  }
@@ -37265,23 +38161,23 @@ function evictOldSnapshotsWithFlock(maxBytes, dir = snapshotsDir()) {
37265
38161
  return 0;
37266
38162
  }
37267
38163
  try {
37268
- fs42.unlinkSync(lockPath);
37269
- fd = fs42.openSync(lockPath, fs42.constants.O_WRONLY | fs42.constants.O_CREAT | fs42.constants.O_EXCL, 384);
38164
+ fs44.unlinkSync(lockPath);
38165
+ fd = fs44.openSync(lockPath, fs44.constants.O_WRONLY | fs44.constants.O_CREAT | fs44.constants.O_EXCL, 384);
37270
38166
  } catch {
37271
38167
  return 0;
37272
38168
  }
37273
38169
  }
37274
38170
  try {
37275
- fs42.writeSync(fd, `${process.pid}
38171
+ fs44.writeSync(fd, `${process.pid}
37276
38172
  `);
37277
38173
  } finally {
37278
- fs42.closeSync(fd);
38174
+ fs44.closeSync(fd);
37279
38175
  }
37280
38176
  try {
37281
38177
  return evictOldSnapshots(maxBytes, dir);
37282
38178
  } finally {
37283
38179
  try {
37284
- fs42.unlinkSync(lockPath);
38180
+ fs44.unlinkSync(lockPath);
37285
38181
  } catch {
37286
38182
  }
37287
38183
  }
@@ -37314,16 +38210,16 @@ function spawnAutoCapture(worldId, olamBin = "olam") {
37314
38210
  }
37315
38211
  }
37316
38212
  function evictOldSnapshots(maxBytes, dir = snapshotsDir()) {
37317
- if (!fs42.existsSync(dir))
38213
+ if (!fs44.existsSync(dir))
37318
38214
  return 0;
37319
38215
  const allTars = [];
37320
38216
  const walk = (d) => {
37321
- for (const entry of fs42.readdirSync(d, { withFileTypes: true })) {
37322
- const full = path41.join(d, entry.name);
38217
+ for (const entry of fs44.readdirSync(d, { withFileTypes: true })) {
38218
+ const full = path44.join(d, entry.name);
37323
38219
  if (entry.isDirectory()) {
37324
38220
  walk(full);
37325
38221
  } else if (entry.name.endsWith(".tar.gz")) {
37326
- const stat = fs42.statSync(full);
38222
+ const stat = fs44.statSync(full);
37327
38223
  allTars.push({ path: full, size: stat.size, mtime: stat.mtimeMs });
37328
38224
  }
37329
38225
  }
@@ -37338,8 +38234,8 @@ function evictOldSnapshots(maxBytes, dir = snapshotsDir()) {
37338
38234
  for (const tar of allTars) {
37339
38235
  if (remaining <= maxBytes)
37340
38236
  break;
37341
- fs42.rmSync(tar.path, { force: true });
37342
- fs42.rmSync(manifestPath(tar.path), { force: true });
38237
+ fs44.rmSync(tar.path, { force: true });
38238
+ fs44.rmSync(manifestPath(tar.path), { force: true });
37343
38239
  freed += tar.size;
37344
38240
  remaining -= tar.size;
37345
38241
  }
@@ -37456,14 +38352,14 @@ function gcloudAvailable(execFn = defaultExecFn) {
37456
38352
 
37457
38353
  // ../core/dist/world/olam-yaml.js
37458
38354
  init_repo_manifest();
37459
- import * as path42 from "node:path";
38355
+ import * as path45 from "node:path";
37460
38356
  import YAML2 from "yaml";
37461
38357
  function enrichReposWithManifests(repos, workspacePath) {
37462
38358
  return repos.map((repo) => {
37463
38359
  if (repo.manifest !== void 0 && repo.manifest !== null) {
37464
38360
  return repo;
37465
38361
  }
37466
- const repoDir = path42.join(workspacePath, repo.name);
38362
+ const repoDir = path45.join(workspacePath, repo.name);
37467
38363
  let manifest = null;
37468
38364
  try {
37469
38365
  manifest = loadRepoManifest(repoDir);
@@ -37478,16 +38374,16 @@ function enrichReposWithManifests(repos, workspacePath) {
37478
38374
  }
37479
38375
 
37480
38376
  // ../core/dist/policies/loader.js
37481
- import * as fs43 from "node:fs";
37482
- import * as path43 from "node:path";
37483
- import { parse as parseYaml4 } from "yaml";
38377
+ import * as fs45 from "node:fs";
38378
+ import * as path46 from "node:path";
38379
+ import { parse as parseYaml5 } from "yaml";
37484
38380
  function parseFrontmatter2(content) {
37485
38381
  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
37486
38382
  if (!match)
37487
38383
  return null;
37488
38384
  const [, yamlText = "", body = ""] = match;
37489
38385
  try {
37490
- const frontmatter = parseYaml4(yamlText);
38386
+ const frontmatter = parseYaml5(yamlText);
37491
38387
  return { frontmatter, body };
37492
38388
  } catch {
37493
38389
  return null;
@@ -37499,20 +38395,20 @@ function toStringArray(v) {
37499
38395
  return v.filter((x) => typeof x === "string");
37500
38396
  }
37501
38397
  function loadPolicies(workspaceRoot) {
37502
- const policiesDir = path43.join(workspaceRoot, ".olam", "policies");
37503
- if (!fs43.existsSync(policiesDir))
38398
+ const policiesDir = path46.join(workspaceRoot, ".olam", "policies");
38399
+ if (!fs45.existsSync(policiesDir))
37504
38400
  return [];
37505
38401
  let files;
37506
38402
  try {
37507
- files = fs43.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
38403
+ files = fs45.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
37508
38404
  } catch {
37509
38405
  return [];
37510
38406
  }
37511
38407
  const policies = [];
37512
38408
  for (const file of files) {
37513
- const filePath = path43.join(policiesDir, file);
38409
+ const filePath = path46.join(policiesDir, file);
37514
38410
  try {
37515
- const content = fs43.readFileSync(filePath, "utf8");
38411
+ const content = fs45.readFileSync(filePath, "utf8");
37516
38412
  const parsed = parseFrontmatter2(content);
37517
38413
  if (!parsed) {
37518
38414
  console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
@@ -37659,12 +38555,12 @@ init_store();
37659
38555
  init_bridge();
37660
38556
 
37661
38557
  // ../core/dist/global-config/runbook-resolver.js
37662
- import * as fs44 from "node:fs";
37663
- import * as os25 from "node:os";
37664
- import * as path44 from "node:path";
38558
+ import * as fs46 from "node:fs";
38559
+ import * as os28 from "node:os";
38560
+ import * as path47 from "node:path";
37665
38561
  function expandTilde(p) {
37666
38562
  if (p === "~" || p.startsWith("~/")) {
37667
- return path44.join(os25.homedir(), p.slice(1));
38563
+ return path47.join(os28.homedir(), p.slice(1));
37668
38564
  }
37669
38565
  return p;
37670
38566
  }
@@ -37676,7 +38572,7 @@ function resolveRunbookToWorldParams(runbook, repoRegistry) {
37676
38572
  throw new Error(`repo "${repoName}" is referenced by runbook "${runbook.name}" but is not in the registry. Run "olam repos add ${repoName} --path <path>" to register it.`);
37677
38573
  }
37678
38574
  const resolvedPath = expandTilde(entry.path);
37679
- if (!fs44.existsSync(resolvedPath)) {
38575
+ if (!fs46.existsSync(resolvedPath)) {
37680
38576
  throw new Error(`repo "${repoName}" path "${resolvedPath}" no longer exists. Run "olam repos update ${repoName} --path <new-path>" to fix.`);
37681
38577
  }
37682
38578
  }
@@ -37692,19 +38588,19 @@ function resolveRunbookToWorldParams(runbook, repoRegistry) {
37692
38588
  init_port_validator();
37693
38589
 
37694
38590
  // ../core/dist/world/bootstrap-hooks.js
37695
- import * as fs45 from "node:fs";
37696
- import * as path45 from "node:path";
38591
+ import * as fs47 from "node:fs";
38592
+ import * as path48 from "node:path";
37697
38593
  function runFixtureCopySeeds(seeds, workspacePath) {
37698
38594
  if (!seeds)
37699
38595
  return;
37700
38596
  for (const seed of seeds) {
37701
38597
  if (seed.type !== "fixture-copy")
37702
38598
  continue;
37703
- const srcAbs = path45.resolve(workspacePath, seed.repo, seed.src);
37704
- const destAbs = path45.resolve(workspacePath, seed.repo, seed.dest);
37705
- const destDir = path45.dirname(destAbs);
37706
- fs45.mkdirSync(destDir, { recursive: true });
37707
- fs45.cpSync(srcAbs, destAbs, { recursive: true, force: true });
38599
+ const srcAbs = path48.resolve(workspacePath, seed.repo, seed.src);
38600
+ const destAbs = path48.resolve(workspacePath, seed.repo, seed.dest);
38601
+ const destDir = path48.dirname(destAbs);
38602
+ fs47.mkdirSync(destDir, { recursive: true });
38603
+ fs47.cpSync(srcAbs, destAbs, { recursive: true, force: true });
37708
38604
  }
37709
38605
  }
37710
38606
  async function runSeedHooks(seeds, containerName, servicePortMap, exec) {
@@ -38340,7 +39236,7 @@ ${detail}`);
38340
39236
  runbookSeeds = resolved.seeds;
38341
39237
  }
38342
39238
  const worldId = generateWorldId();
38343
- const workspacePath = path46.join(os26.homedir(), ".olam", "worlds", worldId);
39239
+ const workspacePath = path49.join(os29.homedir(), ".olam", "worlds", worldId);
38344
39240
  const portOffset = this.registry.getNextPortOffset();
38345
39241
  const branch = opts.branchName ?? `olam/${worldId}`;
38346
39242
  const repos = this.resolveReposWithWorkspace(opts);
@@ -38421,38 +39317,38 @@ ${detail}`);
38421
39317
  for (const repo of repos) {
38422
39318
  if (!repo.path)
38423
39319
  continue;
38424
- const sourceRoot = repo.path.replace(/^~/, os26.homedir());
38425
- const worktreeRoot = path46.join(workspacePath, repo.name);
38426
- if (!fs46.existsSync(sourceRoot) || !fs46.existsSync(worktreeRoot))
39320
+ const sourceRoot = repo.path.replace(/^~/, os29.homedir());
39321
+ const worktreeRoot = path49.join(workspacePath, repo.name);
39322
+ if (!fs48.existsSync(sourceRoot) || !fs48.existsSync(worktreeRoot))
38427
39323
  continue;
38428
39324
  let copied = 0;
38429
39325
  for (const pattern of RUNTIME_FILE_PATTERNS) {
38430
39326
  const matches2 = [];
38431
39327
  if (pattern.includes("*")) {
38432
- const [dir, glob] = [path46.dirname(pattern), path46.basename(pattern)];
38433
- const sourceDir = path46.join(sourceRoot, dir);
38434
- if (fs46.existsSync(sourceDir)) {
39328
+ const [dir, glob] = [path49.dirname(pattern), path49.basename(pattern)];
39329
+ const sourceDir = path49.join(sourceRoot, dir);
39330
+ if (fs48.existsSync(sourceDir)) {
38435
39331
  const ext = glob.replace(/^\*+/, "");
38436
39332
  try {
38437
- for (const entry of fs46.readdirSync(sourceDir)) {
39333
+ for (const entry of fs48.readdirSync(sourceDir)) {
38438
39334
  if (ext === "" || entry.endsWith(ext))
38439
- matches2.push(path46.join(dir, entry));
39335
+ matches2.push(path49.join(dir, entry));
38440
39336
  }
38441
39337
  } catch {
38442
39338
  }
38443
39339
  }
38444
- } else if (fs46.existsSync(path46.join(sourceRoot, pattern))) {
39340
+ } else if (fs48.existsSync(path49.join(sourceRoot, pattern))) {
38445
39341
  matches2.push(pattern);
38446
39342
  }
38447
39343
  for (const rel of matches2) {
38448
- const src = path46.join(sourceRoot, rel);
38449
- const dst = path46.join(worktreeRoot, rel);
39344
+ const src = path49.join(sourceRoot, rel);
39345
+ const dst = path49.join(worktreeRoot, rel);
38450
39346
  try {
38451
- const st = fs46.statSync(src);
39347
+ const st = fs48.statSync(src);
38452
39348
  if (!st.isFile())
38453
39349
  continue;
38454
- fs46.mkdirSync(path46.dirname(dst), { recursive: true });
38455
- fs46.copyFileSync(src, dst);
39350
+ fs48.mkdirSync(path49.dirname(dst), { recursive: true });
39351
+ fs48.copyFileSync(src, dst);
38456
39352
  copied++;
38457
39353
  } catch {
38458
39354
  }
@@ -38538,7 +39434,7 @@ ${detail}`);
38538
39434
  }
38539
39435
  const overlayAttachments = [];
38540
39436
  for (const repo of repos) {
38541
- const worldClonePath = path46.join(workspacePath, repo.name);
39437
+ const worldClonePath = path49.join(workspacePath, repo.name);
38542
39438
  try {
38543
39439
  const result = createWorldOverlay({
38544
39440
  workspace: repo.name,
@@ -38593,7 +39489,7 @@ ${detail}`);
38593
39489
  try {
38594
39490
  const hostExec = makeHostExecFn();
38595
39491
  for (const repo of repos) {
38596
- const repoDir = path46.join(workspacePath, repo.name);
39492
+ const repoDir = path49.join(workspacePath, repo.name);
38597
39493
  if (repo.stack && Object.keys(repo.stack).length > 0) {
38598
39494
  preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
38599
39495
  } else {
@@ -38637,10 +39533,10 @@ ${detail}`);
38637
39533
  const worldEnv = {};
38638
39534
  if (opts.task)
38639
39535
  worldEnv.OLAM_TASK = opts.task;
38640
- const r2CredsPath = path46.join(os26.homedir(), ".olam", "r2-credentials.json");
38641
- if (fs46.existsSync(r2CredsPath)) {
39536
+ const r2CredsPath = path49.join(os29.homedir(), ".olam", "r2-credentials.json");
39537
+ if (fs48.existsSync(r2CredsPath)) {
38642
39538
  try {
38643
- const r2Raw = fs46.readFileSync(r2CredsPath, "utf-8").trim();
39539
+ const r2Raw = fs48.readFileSync(r2CredsPath, "utf-8").trim();
38644
39540
  if (r2Raw.length > 0) {
38645
39541
  const r2 = JSON.parse(r2Raw);
38646
39542
  if (typeof r2.account_id === "string")
@@ -38657,10 +39553,10 @@ ${detail}`);
38657
39553
  } catch {
38658
39554
  }
38659
39555
  }
38660
- const keysYamlPath = path46.join(os26.homedir(), ".olam", "keys.yaml");
38661
- if (fs46.existsSync(keysYamlPath)) {
39556
+ const keysYamlPath = path49.join(os29.homedir(), ".olam", "keys.yaml");
39557
+ if (fs48.existsSync(keysYamlPath)) {
38662
39558
  try {
38663
- const keysRaw = fs46.readFileSync(keysYamlPath, "utf-8").trim();
39559
+ const keysRaw = fs48.readFileSync(keysYamlPath, "utf-8").trim();
38664
39560
  if (keysRaw.length > 0) {
38665
39561
  const parsed = YAML3.parse(keysRaw);
38666
39562
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
@@ -38719,10 +39615,10 @@ ${detail}`);
38719
39615
  worldEnv[k] = v;
38720
39616
  }
38721
39617
  for (const { repoName, relativePath, content } of fileWrites) {
38722
- const absPath = path46.join(workspacePath, repoName, relativePath);
39618
+ const absPath = path49.join(workspacePath, repoName, relativePath);
38723
39619
  try {
38724
- fs46.mkdirSync(path46.dirname(absPath), { recursive: true });
38725
- fs46.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
39620
+ fs48.mkdirSync(path49.dirname(absPath), { recursive: true });
39621
+ fs48.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
38726
39622
  mode: 384
38727
39623
  });
38728
39624
  console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
@@ -38899,7 +39795,7 @@ ${detail}`);
38899
39795
  imageDigest: void 0,
38900
39796
  repos: enrichedRepos.map((r) => ({
38901
39797
  name: r.name,
38902
- worktreeDir: path46.join(workspacePath, r.name)
39798
+ worktreeDir: path49.join(workspacePath, r.name)
38903
39799
  }))
38904
39800
  });
38905
39801
  for (const out of restoreResult.outcomes) {
@@ -39005,7 +39901,7 @@ ${detail}`);
39005
39901
  }
39006
39902
  if (opts.task) {
39007
39903
  const allPolicies = repos.flatMap((repo) => {
39008
- const repoWorktree = path46.join(workspacePath, repo.name);
39904
+ const repoWorktree = path49.join(workspacePath, repo.name);
39009
39905
  try {
39010
39906
  return loadPolicies(repoWorktree);
39011
39907
  } catch (err) {
@@ -39018,8 +39914,8 @@ ${detail}`);
39018
39914
  try {
39019
39915
  execSync5(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
39020
39916
  for (const repo of repos) {
39021
- const policiesDir = path46.join(workspacePath, repo.name, ".olam", "policies");
39022
- if (fs46.existsSync(policiesDir)) {
39917
+ const policiesDir = path49.join(workspacePath, repo.name, ".olam", "policies");
39918
+ if (fs48.existsSync(policiesDir)) {
39023
39919
  execSync5(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
39024
39920
  }
39025
39921
  }
@@ -39127,8 +40023,8 @@ ${detail}`);
39127
40023
  } catch {
39128
40024
  }
39129
40025
  try {
39130
- fs46.rmSync(world.workspacePath, { recursive: true, force: true });
39131
- if (fs46.existsSync(world.workspacePath)) {
40026
+ fs48.rmSync(world.workspacePath, { recursive: true, force: true });
40027
+ if (fs48.existsSync(world.workspacePath)) {
39132
40028
  console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
39133
40029
  }
39134
40030
  } catch (err) {
@@ -39237,14 +40133,14 @@ ${detail}`);
39237
40133
  }).filter((r) => r !== void 0);
39238
40134
  }
39239
40135
  transportPlanFile(planFilePath, workspacePath, repoNames) {
39240
- const planContent = fs46.readFileSync(planFilePath, "utf-8");
39241
- const planFileName = path46.basename(planFilePath);
40136
+ const planContent = fs48.readFileSync(planFilePath, "utf-8");
40137
+ const planFileName = path49.basename(planFilePath);
39242
40138
  const targetRepo = repoNames[0];
39243
40139
  if (!targetRepo)
39244
40140
  return;
39245
- const plansDir = path46.join(workspacePath, targetRepo, "docs", "plans");
39246
- fs46.mkdirSync(plansDir, { recursive: true });
39247
- fs46.writeFileSync(path46.join(plansDir, planFileName), planContent);
40141
+ const plansDir = path49.join(workspacePath, targetRepo, "docs", "plans");
40142
+ fs48.mkdirSync(plansDir, { recursive: true });
40143
+ fs48.writeFileSync(path49.join(plansDir, planFileName), planContent);
39248
40144
  }
39249
40145
  resolveServices(repos) {
39250
40146
  const services = [];
@@ -39678,8 +40574,8 @@ import * as http2 from "node:http";
39678
40574
 
39679
40575
  // ../core/dist/dashboard/server.js
39680
40576
  import * as http from "node:http";
39681
- import * as fs47 from "node:fs";
39682
- import * as path47 from "node:path";
40577
+ import * as fs49 from "node:fs";
40578
+ import * as path50 from "node:path";
39683
40579
  import { fileURLToPath as fileURLToPath3 } from "node:url";
39684
40580
 
39685
40581
  // ../core/dist/dashboard/serialize.js
@@ -40014,7 +40910,7 @@ function notFound(res) {
40014
40910
  }
40015
40911
  function openThoughtStore(workspacePath) {
40016
40912
  const dbPath = getWorldDbPath(workspacePath);
40017
- if (!fs47.existsSync(dbPath))
40913
+ if (!fs49.existsSync(dbPath))
40018
40914
  return null;
40019
40915
  return new ThoughtLocalStore(dbPath);
40020
40916
  }
@@ -40185,13 +41081,13 @@ function findSessionInWorld(registry2, sessionId) {
40185
41081
  }
40186
41082
  function createDashboardServer(opts) {
40187
41083
  const { port: port2, registry: registry2 } = opts;
40188
- const thisDir = path47.dirname(fileURLToPath3(import.meta.url));
40189
- const defaultPublicDir = path47.resolve(thisDir, "../../../control-plane/public");
41084
+ const thisDir = path50.dirname(fileURLToPath3(import.meta.url));
41085
+ const defaultPublicDir = path50.resolve(thisDir, "../../../control-plane/public");
40190
41086
  const publicDir = opts.publicDir ?? defaultPublicDir;
40191
- let hasPublicDir = fs47.existsSync(publicDir);
41087
+ let hasPublicDir = fs49.existsSync(publicDir);
40192
41088
  const server = http.createServer((req, res) => {
40193
41089
  if (!hasPublicDir) {
40194
- hasPublicDir = fs47.existsSync(publicDir);
41090
+ hasPublicDir = fs49.existsSync(publicDir);
40195
41091
  }
40196
41092
  const host = req.headers.host ?? `localhost:${port2}`;
40197
41093
  const url2 = new URL(req.url ?? "/", `http://${host}`);
@@ -40465,22 +41361,22 @@ function createDashboardServer(opts) {
40465
41361
  res.end(`<html><body style="font-family:system-ui;padding:2rem"><h1>Olam Dashboard</h1><p>The React app has not been built yet.</p><p>Run <code>npm run build:app</code> in <code>packages/control-plane</code> to build it.</p><p>API routes are available at <code>/api/*</code>.</p></body></html>`);
40466
41362
  return;
40467
41363
  }
40468
- let filePath = path47.join(publicDir, pathname === "/" ? "index.html" : pathname);
41364
+ let filePath = path50.join(publicDir, pathname === "/" ? "index.html" : pathname);
40469
41365
  if (!filePath.startsWith(publicDir)) {
40470
41366
  notFound(res);
40471
41367
  return;
40472
41368
  }
40473
- if (fs47.existsSync(filePath) && fs47.statSync(filePath).isFile()) {
40474
- const ext = path47.extname(filePath);
41369
+ if (fs49.existsSync(filePath) && fs49.statSync(filePath).isFile()) {
41370
+ const ext = path50.extname(filePath);
40475
41371
  const contentType = MIME[ext] ?? "application/octet-stream";
40476
41372
  res.writeHead(200, { "Content-Type": contentType });
40477
- fs47.createReadStream(filePath).pipe(res);
41373
+ fs49.createReadStream(filePath).pipe(res);
40478
41374
  return;
40479
41375
  }
40480
- filePath = path47.join(publicDir, "index.html");
40481
- if (fs47.existsSync(filePath)) {
41376
+ filePath = path50.join(publicDir, "index.html");
41377
+ if (fs49.existsSync(filePath)) {
40482
41378
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
40483
- fs47.createReadStream(filePath).pipe(res);
41379
+ fs49.createReadStream(filePath).pipe(res);
40484
41380
  return;
40485
41381
  }
40486
41382
  notFound(res);
@@ -40490,17 +41386,17 @@ function createDashboardServer(opts) {
40490
41386
  }
40491
41387
 
40492
41388
  // ../core/dist/dashboard/state.js
40493
- import * as fs48 from "node:fs";
40494
- import * as os27 from "node:os";
40495
- import * as path48 from "node:path";
40496
- var STATE_PATH = path48.join(os27.homedir(), ".olam", "dashboard.json");
41389
+ import * as fs50 from "node:fs";
41390
+ import * as os30 from "node:os";
41391
+ import * as path51 from "node:path";
41392
+ var STATE_PATH = path51.join(os30.homedir(), ".olam", "dashboard.json");
40497
41393
  function saveDashboardState(state) {
40498
- fs48.mkdirSync(path48.dirname(STATE_PATH), { recursive: true });
40499
- fs48.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
41394
+ fs50.mkdirSync(path51.dirname(STATE_PATH), { recursive: true });
41395
+ fs50.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
40500
41396
  }
40501
41397
  function loadDashboardState() {
40502
41398
  try {
40503
- const raw = fs48.readFileSync(STATE_PATH, "utf-8");
41399
+ const raw = fs50.readFileSync(STATE_PATH, "utf-8");
40504
41400
  return JSON.parse(raw);
40505
41401
  } catch {
40506
41402
  return null;
@@ -40508,7 +41404,7 @@ function loadDashboardState() {
40508
41404
  }
40509
41405
  function clearDashboardState() {
40510
41406
  try {
40511
- fs48.unlinkSync(STATE_PATH);
41407
+ fs50.unlinkSync(STATE_PATH);
40512
41408
  } catch {
40513
41409
  }
40514
41410
  }
@@ -40788,8 +41684,8 @@ var PleriClient = class {
40788
41684
  };
40789
41685
 
40790
41686
  // ../mcp-server/src/env-loader.ts
40791
- import { readFileSync as readFileSync37, existsSync as existsSync45, statSync as statSync14 } from "node:fs";
40792
- import { join as join49, dirname as dirname28, resolve as resolve13 } from "node:path";
41687
+ import { readFileSync as readFileSync39, existsSync as existsSync47, statSync as statSync14 } from "node:fs";
41688
+ import { join as join52, dirname as dirname29, resolve as resolve13 } from "node:path";
40793
41689
  var PROJECT_MARKERS = [
40794
41690
  ".olam/config.yaml",
40795
41691
  ".olam/config.yml",
@@ -40801,26 +41697,26 @@ function findProjectRoot2(startDir) {
40801
41697
  const root = resolve13("/");
40802
41698
  while (true) {
40803
41699
  for (const marker of PROJECT_MARKERS) {
40804
- if (existsSync45(join49(dir, marker))) return dir;
41700
+ if (existsSync47(join52(dir, marker))) return dir;
40805
41701
  }
40806
- const pkg = join49(dir, "package.json");
40807
- if (existsSync45(pkg)) {
41702
+ const pkg = join52(dir, "package.json");
41703
+ if (existsSync47(pkg)) {
40808
41704
  try {
40809
- const json = JSON.parse(readFileSync37(pkg, "utf8"));
41705
+ const json = JSON.parse(readFileSync39(pkg, "utf8"));
40810
41706
  const isOlamWorkspace = typeof json.name === "string" && json.name.startsWith("@olam/");
40811
41707
  const hasOlamDep = json.dependencies && Object.keys(json.dependencies).some((k) => k.startsWith("@olam/")) || json.devDependencies && Object.keys(json.devDependencies).some((k) => k.startsWith("@olam/"));
40812
41708
  if (isOlamWorkspace || hasOlamDep) return dir;
40813
41709
  } catch {
40814
41710
  }
40815
41711
  }
40816
- const parent = dirname28(dir);
41712
+ const parent = dirname29(dir);
40817
41713
  if (parent === dir || parent === root) return null;
40818
41714
  dir = parent;
40819
41715
  }
40820
41716
  }
40821
- function parseEnvFile(path49) {
41717
+ function parseEnvFile(path52) {
40822
41718
  const out = {};
40823
- const raw = readFileSync37(path49, "utf8");
41719
+ const raw = readFileSync39(path52, "utf8");
40824
41720
  for (const line of raw.split(/\r?\n/)) {
40825
41721
  const trimmed = line.trim();
40826
41722
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -40843,8 +41739,8 @@ function loadProjectEnv(startDir = process.cwd()) {
40843
41739
  const filesRead = [];
40844
41740
  const merged = {};
40845
41741
  for (const name of [".env", ".env.local"]) {
40846
- const p = join49(root, name);
40847
- if (existsSync45(p) && statSync14(p).isFile()) {
41742
+ const p = join52(root, name);
41743
+ if (existsSync47(p) && statSync14(p).isFile()) {
40848
41744
  Object.assign(merged, parseEnvFile(p));
40849
41745
  filesRead.push(p);
40850
41746
  }
@@ -40880,12 +41776,12 @@ async function main() {
40880
41776
  maxDailyUsd: config2.cost.max_daily_usd,
40881
41777
  warningThreshold: config2.cost.warning_threshold
40882
41778
  });
40883
- const WORLD_ID_RE2 = /^[a-z0-9-]+$/;
41779
+ const WORLD_ID_RE3 = /^[a-z0-9-]+$/;
40884
41780
  const callerWorldIdRaw = (process.env.OLAM_CALLER_WORLD_ID ?? "").trim();
40885
41781
  let callerWorldId;
40886
41782
  if (callerWorldIdRaw.length === 0) {
40887
41783
  callerWorldId = void 0;
40888
- } else if (WORLD_ID_RE2.test(callerWorldIdRaw)) {
41784
+ } else if (WORLD_ID_RE3.test(callerWorldIdRaw)) {
40889
41785
  callerWorldId = callerWorldIdRaw;
40890
41786
  } else {
40891
41787
  logger.warn(