@pleri/olam-cli 0.1.208 → 0.1.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -535,8 +535,8 @@ var init_parseUtil = __esm({
535
535
  init_errors();
536
536
  init_en();
537
537
  makeIssue = (params) => {
538
- const { data, path: path121, errorMaps, issueData } = params;
539
- const fullPath = [...path121, ...issueData.path || []];
538
+ const { data, path: path122, errorMaps, issueData } = params;
539
+ const fullPath = [...path122, ...issueData.path || []];
540
540
  const fullIssue = {
541
541
  ...issueData,
542
542
  path: fullPath
@@ -844,11 +844,11 @@ var init_types = __esm({
844
844
  init_parseUtil();
845
845
  init_util();
846
846
  ParseInputLazyPath = class {
847
- constructor(parent, value, path121, key) {
847
+ constructor(parent, value, path122, key) {
848
848
  this._cachedPath = [];
849
849
  this.parent = parent;
850
850
  this.data = value;
851
- this._path = path121;
851
+ this._path = path122;
852
852
  this._key = key;
853
853
  }
854
854
  get path() {
@@ -4336,7 +4336,7 @@ import YAML from "yaml";
4336
4336
  function bootstrapStepCmd(entry) {
4337
4337
  return typeof entry === "string" ? entry : entry.cmd;
4338
4338
  }
4339
- function refineForbiddenKeys(value, path121, ctx, rejectSource) {
4339
+ function refineForbiddenKeys(value, path122, ctx, rejectSource) {
4340
4340
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4341
4341
  return;
4342
4342
  }
@@ -4344,12 +4344,12 @@ function refineForbiddenKeys(value, path121, ctx, rejectSource) {
4344
4344
  if (FORBIDDEN_KEYS.has(key)) {
4345
4345
  ctx.addIssue({
4346
4346
  code: external_exports.ZodIssueCode.custom,
4347
- path: [...path121, key],
4347
+ path: [...path122, key],
4348
4348
  message: `forbidden key "${key}" (prototype-pollution surface)`
4349
4349
  });
4350
4350
  continue;
4351
4351
  }
4352
- if (rejectSource && path121.length === 0 && key === "source") {
4352
+ if (rejectSource && path122.length === 0 && key === "source") {
4353
4353
  ctx.addIssue({
4354
4354
  code: external_exports.ZodIssueCode.custom,
4355
4355
  path: ["source"],
@@ -4357,21 +4357,21 @@ function refineForbiddenKeys(value, path121, ctx, rejectSource) {
4357
4357
  });
4358
4358
  continue;
4359
4359
  }
4360
- refineForbiddenKeys(value[key], [...path121, key], ctx, false);
4360
+ refineForbiddenKeys(value[key], [...path122, key], ctx, false);
4361
4361
  }
4362
4362
  }
4363
- function rejectForbiddenKeys(value, path121, rejectSource) {
4363
+ function rejectForbiddenKeys(value, path122, rejectSource) {
4364
4364
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4365
4365
  return;
4366
4366
  }
4367
4367
  for (const key of Object.keys(value)) {
4368
4368
  if (FORBIDDEN_KEYS.has(key)) {
4369
- throw new Error(`[manifest] ${path121}: forbidden key "${key}" (prototype-pollution surface)`);
4369
+ throw new Error(`[manifest] ${path122}: forbidden key "${key}" (prototype-pollution surface)`);
4370
4370
  }
4371
4371
  if (rejectSource && key === "source") {
4372
- throw new Error(`[manifest] ${path121}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4372
+ throw new Error(`[manifest] ${path122}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4373
4373
  }
4374
- rejectForbiddenKeys(value[key], `${path121}.${key}`, false);
4374
+ rejectForbiddenKeys(value[key], `${path122}.${key}`, false);
4375
4375
  }
4376
4376
  }
4377
4377
  function unknownTopLevelKeys(parsed) {
@@ -5645,16 +5645,16 @@ function isValidConfig(value) {
5645
5645
  if (typeof v.install_id !== "string" || v.install_id.length === 0) return false;
5646
5646
  return true;
5647
5647
  }
5648
- function atomicWriteJSON(path121, value, stderr = process.stderr) {
5648
+ function atomicWriteJSON(path122, value, stderr = process.stderr) {
5649
5649
  ensureStateDir();
5650
- const dir = dirname3(path121);
5650
+ const dir = dirname3(path122);
5651
5651
  if (!existsSync6(dir)) {
5652
5652
  mkdirSync2(dir, { recursive: true });
5653
5653
  }
5654
- const tmp = `${path121}.tmp.${process.pid}`;
5654
+ const tmp = `${path122}.tmp.${process.pid}`;
5655
5655
  try {
5656
5656
  writeFileSync2(tmp, JSON.stringify(value, null, 2) + "\n", { encoding: "utf8" });
5657
- renameSync2(tmp, path121);
5657
+ renameSync2(tmp, path122);
5658
5658
  } catch (err) {
5659
5659
  if (existsSync6(tmp)) {
5660
5660
  try {
@@ -5669,16 +5669,16 @@ function atomicWriteJSON(path121, value, stderr = process.stderr) {
5669
5669
  throw err;
5670
5670
  }
5671
5671
  }
5672
- function atomicWriteYaml(path121, value, stderr = process.stderr) {
5672
+ function atomicWriteYaml(path122, value, stderr = process.stderr) {
5673
5673
  ensureStateDir();
5674
- const dir = dirname3(path121);
5674
+ const dir = dirname3(path122);
5675
5675
  if (!existsSync6(dir)) {
5676
5676
  mkdirSync2(dir, { recursive: true });
5677
5677
  }
5678
- const tmp = `${path121}.tmp.${process.pid}`;
5678
+ const tmp = `${path122}.tmp.${process.pid}`;
5679
5679
  try {
5680
5680
  writeFileSync2(tmp, stringifyYaml3(value), { encoding: "utf8" });
5681
- renameSync2(tmp, path121);
5681
+ renameSync2(tmp, path122);
5682
5682
  } catch (err) {
5683
5683
  if (existsSync6(tmp)) {
5684
5684
  try {
@@ -5693,11 +5693,11 @@ function atomicWriteYaml(path121, value, stderr = process.stderr) {
5693
5693
  throw err;
5694
5694
  }
5695
5695
  }
5696
- function atomicWrite(path121, value, stderr = process.stderr) {
5697
- if (isYamlPath(path121)) {
5698
- atomicWriteYaml(path121, value, stderr);
5696
+ function atomicWrite(path122, value, stderr = process.stderr) {
5697
+ if (isYamlPath(path122)) {
5698
+ atomicWriteYaml(path122, value, stderr);
5699
5699
  } else {
5700
- atomicWriteJSON(path121, value, stderr);
5700
+ atomicWriteJSON(path122, value, stderr);
5701
5701
  }
5702
5702
  }
5703
5703
  function readConfig(opts = {}) {
@@ -6006,8 +6006,8 @@ var init_client = __esm({
6006
6006
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
6007
6007
  }
6008
6008
  }
6009
- async request(method, path121, body, attempt = 0) {
6010
- const url2 = `${this.baseUrl}${path121}`;
6009
+ async request(method, path122, body, attempt = 0) {
6010
+ const url2 = `${this.baseUrl}${path122}`;
6011
6011
  const controller = new AbortController();
6012
6012
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
6013
6013
  const headers = {};
@@ -6025,7 +6025,7 @@ var init_client = __esm({
6025
6025
  } catch (err) {
6026
6026
  if (attempt < RETRY_COUNT && isTransient(err)) {
6027
6027
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
6028
- return this.request(method, path121, body, attempt + 1);
6028
+ return this.request(method, path122, body, attempt + 1);
6029
6029
  }
6030
6030
  throw err;
6031
6031
  } finally {
@@ -7903,8 +7903,8 @@ var init_provider3 = __esm({
7903
7903
  // -----------------------------------------------------------------------
7904
7904
  // Internal fetch helper
7905
7905
  // -----------------------------------------------------------------------
7906
- async request(path121, method, body) {
7907
- const url2 = `${this.config.workerUrl}${path121}`;
7906
+ async request(path122, method, body) {
7907
+ const url2 = `${this.config.workerUrl}${path122}`;
7908
7908
  const bearer = await this.config.mintToken();
7909
7909
  const headers = {
7910
7910
  Authorization: `Bearer ${bearer}`
@@ -9740,17 +9740,17 @@ function kgRoot() {
9740
9740
  function worldsRoot() {
9741
9741
  return join19(olamHome3(), "worlds");
9742
9742
  }
9743
- function assertWithinPrefix(path121, prefix, label) {
9744
- if (!path121.startsWith(prefix + "/")) {
9745
- throw new Error(`${label} escape: ${path121} not under ${prefix}/`);
9743
+ function assertWithinPrefix(path122, prefix, label) {
9744
+ if (!path122.startsWith(prefix + "/")) {
9745
+ throw new Error(`${label} escape: ${path122} not under ${prefix}/`);
9746
9746
  }
9747
9747
  }
9748
9748
  function kgPristinePath(workspace) {
9749
9749
  validateWorkspaceName(workspace);
9750
9750
  const root = kgRoot();
9751
- const path121 = resolve6(join19(root, workspace));
9752
- assertWithinPrefix(path121, root, "kgPristinePath");
9753
- return path121;
9751
+ const path122 = resolve6(join19(root, workspace));
9752
+ assertWithinPrefix(path122, root, "kgPristinePath");
9753
+ return path122;
9754
9754
  }
9755
9755
  var KG_PATHS_INTERNALS;
9756
9756
  var init_storage_paths = __esm({
@@ -9865,8 +9865,8 @@ import { execFileSync as execFileSync4 } from "node:child_process";
9865
9865
  import * as fs17 from "node:fs";
9866
9866
  import * as os10 from "node:os";
9867
9867
  import * as path19 from "node:path";
9868
- function expandHome2(p, homedir89) {
9869
- return p.replace(/^~(?=$|\/|\\)/, homedir89());
9868
+ function expandHome2(p, homedir90) {
9869
+ return p.replace(/^~(?=$|\/|\\)/, homedir90());
9870
9870
  }
9871
9871
  function sanitizeRepoFilename(name) {
9872
9872
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -9889,7 +9889,7 @@ ${stderr}`;
9889
9889
  }
9890
9890
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
9891
9891
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync4(cmd, args, opts));
9892
- const homedir89 = deps.homedir ?? (() => os10.homedir());
9892
+ const homedir90 = deps.homedir ?? (() => os10.homedir());
9893
9893
  const baselineDir = path19.join(workspacePath, ".olam", "baseline");
9894
9894
  try {
9895
9895
  fs17.mkdirSync(baselineDir, { recursive: true });
@@ -9905,7 +9905,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
9905
9905
  continue;
9906
9906
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
9907
9907
  const outPath = path19.join(baselineDir, filename);
9908
- const repoPath = expandHome2(repo.path, homedir89);
9908
+ const repoPath = expandHome2(repo.path, homedir90);
9909
9909
  if (!fs17.existsSync(repoPath)) {
9910
9910
  writeBaselineFile(outPath, `# repo: ${repo.name}
9911
9911
  # (skipped: path ${repoPath} does not exist)
@@ -10025,8 +10025,8 @@ function extractStderr(err) {
10025
10025
  }
10026
10026
  function carryUncommittedEdits(repos, workspacePath, deps = {}) {
10027
10027
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync4(cmd, args, opts));
10028
- const homedir89 = deps.homedir ?? (() => os10.homedir());
10029
- const existsSync136 = deps.existsSync ?? ((p) => fs17.existsSync(p));
10028
+ const homedir90 = deps.homedir ?? (() => os10.homedir());
10029
+ const existsSync137 = deps.existsSync ?? ((p) => fs17.existsSync(p));
10030
10030
  const copyFileSync20 = deps.copyFileSync ?? ((src, dest) => fs17.copyFileSync(src, dest));
10031
10031
  const mkdirSync81 = deps.mkdirSync ?? ((dirPath, opts) => {
10032
10032
  fs17.mkdirSync(dirPath, opts);
@@ -10035,11 +10035,11 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
10035
10035
  for (const repo of repos) {
10036
10036
  if (!repo.path)
10037
10037
  continue;
10038
- const repoPath = expandHome2(repo.path, homedir89);
10038
+ const repoPath = expandHome2(repo.path, homedir90);
10039
10039
  const worktreePath = path19.join(workspacePath, repo.name);
10040
- if (!existsSync136(repoPath))
10040
+ if (!existsSync137(repoPath))
10041
10041
  continue;
10042
- if (!existsSync136(worktreePath)) {
10042
+ if (!existsSync137(worktreePath)) {
10043
10043
  console.warn(`[carry] ${repo.name}: world worktree ${worktreePath} missing; skipping carry for this repo`);
10044
10044
  continue;
10045
10045
  }
@@ -10099,7 +10099,7 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
10099
10099
  for (const rel of plan.diff.untracked) {
10100
10100
  const src = path19.join(plan.repoPath, rel);
10101
10101
  const dest = path19.join(plan.worktreePath, rel);
10102
- if (!existsSync136(src))
10102
+ if (!existsSync137(src))
10103
10103
  continue;
10104
10104
  try {
10105
10105
  mkdirSync81(path19.dirname(dest), { recursive: true });
@@ -16708,10 +16708,10 @@ async function readHostCpToken2() {
16708
16708
  if (!fs29.existsSync(tp)) return null;
16709
16709
  return fs29.readFileSync(tp, "utf-8").trim();
16710
16710
  }
16711
- async function callHostCpProxy(method, worldId, path121, body) {
16711
+ async function callHostCpProxy(method, worldId, path122, body) {
16712
16712
  const token = await readHostCpToken2();
16713
16713
  if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
16714
- const url2 = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path121}`;
16714
+ const url2 = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path122}`;
16715
16715
  try {
16716
16716
  const headers = {
16717
16717
  Authorization: `Bearer ${token}`
@@ -17132,57 +17132,57 @@ import { randomBytes as randomBytes8 } from "node:crypto";
17132
17132
  function generateSecret() {
17133
17133
  return randomBytes8(SECRET_LEN_BYTES).toString("hex");
17134
17134
  }
17135
- function writeSecretAtPath(path121, value) {
17136
- mkdirSync23(dirname23(path121), { recursive: true, mode: 448 });
17137
- const tmp = `${path121}.tmp.${process.pid}`;
17135
+ function writeSecretAtPath(path122, value) {
17136
+ mkdirSync23(dirname23(path122), { recursive: true, mode: 448 });
17137
+ const tmp = `${path122}.tmp.${process.pid}`;
17138
17138
  writeFileSync18(tmp, value, { mode: 384 });
17139
17139
  chmodSync8(tmp, 384);
17140
- renameSync7(tmp, path121);
17140
+ renameSync7(tmp, path122);
17141
17141
  }
17142
- function readSecretAtPathOrNull(path121) {
17143
- if (!existsSync34(path121)) return null;
17144
- const mode = statSync9(path121).mode & 511;
17142
+ function readSecretAtPathOrNull(path122) {
17143
+ if (!existsSync34(path122)) return null;
17144
+ const mode = statSync9(path122).mode & 511;
17145
17145
  if (mode !== 384) {
17146
17146
  process.stderr.write(
17147
- `warn: ${path121} has mode 0${mode.toString(8)}; expected 0600. Run 'olam memory secret rotate' to regenerate.
17147
+ `warn: ${path122} has mode 0${mode.toString(8)}; expected 0600. Run 'olam memory secret rotate' to regenerate.
17148
17148
  `
17149
17149
  );
17150
17150
  }
17151
- return readFileSync28(path121, "utf8").trim();
17151
+ return readFileSync28(path122, "utf8").trim();
17152
17152
  }
17153
- function readSecretAtPath(path121) {
17154
- const v = readSecretAtPathOrNull(path121);
17153
+ function readSecretAtPath(path122) {
17154
+ const v = readSecretAtPathOrNull(path122);
17155
17155
  if (v === null) {
17156
17156
  throw new Error(
17157
- `Secret not found at ${path121}. Run 'olam memory start' to generate it.`
17157
+ `Secret not found at ${path122}. Run 'olam memory start' to generate it.`
17158
17158
  );
17159
17159
  }
17160
17160
  return v;
17161
17161
  }
17162
- function ensureMemorySecret(path121 = MEMORY_SECRET_PATH) {
17163
- const existing = readSecretAtPathOrNull(path121);
17162
+ function ensureMemorySecret(path122 = MEMORY_SECRET_PATH) {
17163
+ const existing = readSecretAtPathOrNull(path122);
17164
17164
  if (existing) return existing;
17165
17165
  const fresh = generateSecret();
17166
- const writePath = path121 === MEMORY_SECRET_PATH ? MEMORY_SECRET_CANONICAL_PATH : path121;
17166
+ const writePath = path122 === MEMORY_SECRET_PATH ? MEMORY_SECRET_CANONICAL_PATH : path122;
17167
17167
  writeSecretAtPath(writePath, fresh);
17168
17168
  return fresh;
17169
17169
  }
17170
- function readMemorySecretOrNull(path121 = MEMORY_SECRET_PATH) {
17171
- return readSecretAtPathOrNull(path121);
17170
+ function readMemorySecretOrNull(path122 = MEMORY_SECRET_PATH) {
17171
+ return readSecretAtPathOrNull(path122);
17172
17172
  }
17173
- function readMemorySecret(path121 = MEMORY_SECRET_PATH) {
17174
- return readSecretAtPath(path121);
17173
+ function readMemorySecret(path122 = MEMORY_SECRET_PATH) {
17174
+ return readSecretAtPath(path122);
17175
17175
  }
17176
- function rotateMemorySecret(path121 = MEMORY_SECRET_CANONICAL_PATH) {
17176
+ function rotateMemorySecret(path122 = MEMORY_SECRET_CANONICAL_PATH) {
17177
17177
  const fresh = generateSecret();
17178
- writeSecretAtPath(path121, fresh);
17178
+ writeSecretAtPath(path122, fresh);
17179
17179
  return fresh;
17180
17180
  }
17181
- function hasMemorySecret(path121 = MEMORY_SECRET_PATH) {
17182
- return existsSync34(path121);
17181
+ function hasMemorySecret(path122 = MEMORY_SECRET_PATH) {
17182
+ return existsSync34(path122);
17183
17183
  }
17184
- function writeCloudMemorySecret(value, path121 = CLOUD_MEMORY_SECRET_CANONICAL_PATH) {
17185
- writeSecretAtPath(path121, value);
17184
+ function writeCloudMemorySecret(value, path122 = CLOUD_MEMORY_SECRET_CANONICAL_PATH) {
17185
+ writeSecretAtPath(path122, value);
17186
17186
  }
17187
17187
  var MEMORY_SECRET_PATH, CLOUD_MEMORY_SECRET_PATH, MEMORY_SECRET_CANONICAL_PATH, CLOUD_MEMORY_SECRET_CANONICAL_PATH, SECRET_LEN_BYTES;
17188
17188
  var init_memory_secret = __esm({
@@ -17260,31 +17260,31 @@ __export(from_manifest_exports, {
17260
17260
  provenanceLine: () => provenanceLine
17261
17261
  });
17262
17262
  import { readFileSync as readFileSync37 } from "node:fs";
17263
- function parseForkManifest(path121) {
17263
+ function parseForkManifest(path122) {
17264
17264
  let raw;
17265
17265
  try {
17266
- raw = readFileSync37(path121, "utf8");
17266
+ raw = readFileSync37(path122, "utf8");
17267
17267
  } catch (err) {
17268
17268
  const reason = err?.code === "ENOENT" ? "file does not exist" : err.message;
17269
- throw new Error(`--from-manifest: cannot read ${path121} (${reason})`);
17269
+ throw new Error(`--from-manifest: cannot read ${path122} (${reason})`);
17270
17270
  }
17271
17271
  let json;
17272
17272
  try {
17273
17273
  json = JSON.parse(raw);
17274
17274
  } catch (err) {
17275
- throw new Error(`--from-manifest: ${path121} is not valid JSON (${err.message})`);
17275
+ throw new Error(`--from-manifest: ${path122} is not valid JSON (${err.message})`);
17276
17276
  }
17277
17277
  if (!isPlainObject2(json)) {
17278
- throw new Error(`--from-manifest: ${path121} root must be a JSON object`);
17278
+ throw new Error(`--from-manifest: ${path122} root must be a JSON object`);
17279
17279
  }
17280
17280
  const originalWorldId = json.originalWorldId;
17281
17281
  if (typeof originalWorldId !== "string" || originalWorldId.trim().length === 0) {
17282
- throw new Error(`--from-manifest: ${path121} missing required string "originalWorldId"`);
17282
+ throw new Error(`--from-manifest: ${path122} missing required string "originalWorldId"`);
17283
17283
  }
17284
17284
  const newPrompt = json.newPrompt;
17285
17285
  if (typeof newPrompt !== "string" || newPrompt.trim().length === 0) {
17286
17286
  throw new Error(
17287
- `--from-manifest: ${path121} has no "newPrompt" \u2014 re-run trace-replay with --with-prompt "<alternate dispatch>"`
17287
+ `--from-manifest: ${path122} has no "newPrompt" \u2014 re-run trace-replay with --with-prompt "<alternate dispatch>"`
17288
17288
  );
17289
17289
  }
17290
17290
  const fp = isPlainObject2(json.forkPoint) ? json.forkPoint : {};
@@ -17295,7 +17295,7 @@ function parseForkManifest(path121) {
17295
17295
  originalWorldId: originalWorldId.trim(),
17296
17296
  forkPoint: { resolvedPhase, resolvedSpanId, resolvedAt },
17297
17297
  newPrompt,
17298
- manifestPath: path121
17298
+ manifestPath: path122
17299
17299
  };
17300
17300
  }
17301
17301
  function defaultNameFromManifest(manifest) {
@@ -17390,10 +17390,10 @@ function legacySecretPath(home = homedir27()) {
17390
17390
  return join53(home, ".olam", "secrets", "plan-agent-secret");
17391
17391
  }
17392
17392
  function readRemoteConnection() {
17393
- for (const path121 of [planChatConnectionPath(), legacyConnectionPath()]) {
17393
+ for (const path122 of [planChatConnectionPath(), legacyConnectionPath()]) {
17394
17394
  try {
17395
- if (!existsSync45(path121)) continue;
17396
- const parsed = JSON.parse(readFileSync38(path121, "utf8"));
17395
+ if (!existsSync45(path122)) continue;
17396
+ const parsed = JSON.parse(readFileSync38(path122, "utf8"));
17397
17397
  if (typeof parsed.url !== "string" || parsed.url.length === 0) continue;
17398
17398
  return {
17399
17399
  url: parsed.url,
@@ -17407,12 +17407,12 @@ function readRemoteConnection() {
17407
17407
  return null;
17408
17408
  }
17409
17409
  function writeRemoteConnection(input2) {
17410
- const path121 = planChatConnectionPath();
17411
- mkdirSync29(dirname30(path121), { recursive: true, mode: 448 });
17410
+ const path122 = planChatConnectionPath();
17411
+ mkdirSync29(dirname30(path122), { recursive: true, mode: 448 });
17412
17412
  const payload = { url: input2.url, connectedAt: input2.connectedAt };
17413
17413
  if (input2.password) payload["password"] = input2.password;
17414
17414
  if (input2.bearer) payload["bearer"] = input2.bearer;
17415
- writeSecretAtPath(path121, JSON.stringify(payload, null, 2) + "\n");
17415
+ writeSecretAtPath(path122, JSON.stringify(payload, null, 2) + "\n");
17416
17416
  }
17417
17417
  function resolveRemoteUrl2() {
17418
17418
  const env = process.env["OLAM_CLOUD_URL"];
@@ -17726,9 +17726,9 @@ function memoryConnectionPath() {
17726
17726
  }
17727
17727
  function readMemoryConnection() {
17728
17728
  try {
17729
- const path121 = memoryConnectionPath();
17730
- if (!existsSync63(path121)) return null;
17731
- const parsed = JSON.parse(readFileSync55(path121, "utf8"));
17729
+ const path122 = memoryConnectionPath();
17730
+ if (!existsSync63(path122)) return null;
17731
+ const parsed = JSON.parse(readFileSync55(path122, "utf8"));
17732
17732
  if (typeof parsed.url !== "string" || parsed.url.length === 0) return null;
17733
17733
  return { url: parsed.url, connectedAt: parsed.connectedAt ?? "" };
17734
17734
  } catch {
@@ -17736,9 +17736,9 @@ function readMemoryConnection() {
17736
17736
  }
17737
17737
  }
17738
17738
  function writeMemoryConnection(url2, connectedAt) {
17739
- const path121 = memoryConnectionPath();
17740
- mkdirSync39(dirname36(path121), { recursive: true, mode: 448 });
17741
- writeSecretAtPath(path121, JSON.stringify({ url: url2, connectedAt }, null, 2) + "\n");
17739
+ const path122 = memoryConnectionPath();
17740
+ mkdirSync39(dirname36(path122), { recursive: true, mode: 448 });
17741
+ writeSecretAtPath(path122, JSON.stringify({ url: url2, connectedAt }, null, 2) + "\n");
17742
17742
  }
17743
17743
  var init_memory_connection = __esm({
17744
17744
  "src/lib/memory-connection.ts"() {
@@ -19407,16 +19407,25 @@ function settingsBackupDir() {
19407
19407
  return override;
19408
19408
  return path73.join(os38.homedir(), ".olam", "state", "settings-backups");
19409
19409
  }
19410
+ function entryKey(entry) {
19411
+ const cmds = commandsInEntry(entry);
19412
+ const cmdKey = cmds.length > 0 ? cmds[0] : "";
19413
+ return `${entry.matcher ?? ""}\0${cmdKey}`;
19414
+ }
19410
19415
  function dedupeByMatcher(entries) {
19411
19416
  const map = /* @__PURE__ */ new Map();
19412
19417
  for (const e of entries) {
19413
- const cmds = commandsInEntry(e);
19414
- const cmdKey = cmds.length > 0 ? cmds[0] : "";
19415
- const key = `${e.matcher ?? ""}\0${cmdKey}`;
19416
- map.set(key, e);
19418
+ map.set(entryKey(e), e);
19417
19419
  }
19418
19420
  return [...map.values()];
19419
19421
  }
19422
+ function reclaimLegacyDuplicates(preserved, olamFinal) {
19423
+ if (olamFinal.length === 0)
19424
+ return { kept: preserved, reclaimedCount: 0 };
19425
+ const olamKeys = new Set(olamFinal.map(entryKey));
19426
+ const kept = preserved.filter((e) => !olamKeys.has(entryKey(e)));
19427
+ return { kept, reclaimedCount: preserved.length - kept.length };
19428
+ }
19420
19429
  function commandsInEntry(entry) {
19421
19430
  const cmds = [];
19422
19431
  const direct = entry["command"];
@@ -19517,6 +19526,7 @@ function mergeSettings(input2) {
19517
19526
  ]);
19518
19527
  let hooksAdded = 0;
19519
19528
  let dualWriteDeduped = 0;
19529
+ let legacyReclaimed = 0;
19520
19530
  const dualWriteDroppedCommands = [];
19521
19531
  for (const cat of categories) {
19522
19532
  const existing = Array.isArray(existingHooks[cat]) ? existingHooks[cat] : [];
@@ -19526,7 +19536,9 @@ function mergeSettings(input2) {
19526
19536
  const { kept: olamFinal, droppedCount, droppedCommands } = applyDualWriteDedup(olamDeduped, preserved);
19527
19537
  dualWriteDeduped += droppedCount;
19528
19538
  dualWriteDroppedCommands.push(...droppedCommands);
19529
- mergedHooks[cat] = [...preserved, ...olamFinal];
19539
+ const { kept: preservedFinal, reclaimedCount } = reclaimLegacyDuplicates(preserved, olamFinal);
19540
+ legacyReclaimed += reclaimedCount;
19541
+ mergedHooks[cat] = [...preservedFinal, ...olamFinal];
19530
19542
  hooksAdded += olamFinal.length;
19531
19543
  }
19532
19544
  const permSet = /* @__PURE__ */ new Set();
@@ -19556,7 +19568,14 @@ function mergeSettings(input2) {
19556
19568
  const tmp = `${settingsPath}.tmp-${process.pid}`;
19557
19569
  fs75.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", { mode: 420 });
19558
19570
  fs75.renameSync(tmp, settingsPath);
19559
- return { backupPath, hooksAdded, permissionsCount: permSet.size, dualWriteDeduped, dualWriteDroppedCommands };
19571
+ return {
19572
+ backupPath,
19573
+ hooksAdded,
19574
+ permissionsCount: permSet.size,
19575
+ dualWriteDeduped,
19576
+ dualWriteDroppedCommands,
19577
+ legacyReclaimed
19578
+ };
19560
19579
  }
19561
19580
  var OLAM_SKILLS_MARKER, BACKUP_RETENTION, DUAL_WRITE_DEDUP_RULES;
19562
19581
  var init_settings_merger = __esm({
@@ -21013,12 +21032,12 @@ function sourceConfigPath(clonePath) {
21013
21032
  return join91(clonePath, "shared", "source-config.yaml");
21014
21033
  }
21015
21034
  function readSourceConfig(clonePath, sourceId) {
21016
- const path121 = sourceConfigPath(clonePath);
21017
- if (!existsSync88(path121))
21035
+ const path122 = sourceConfigPath(clonePath);
21036
+ if (!existsSync88(path122))
21018
21037
  return void 0;
21019
21038
  let raw;
21020
21039
  try {
21021
- raw = readFileSync82(path121, "utf-8");
21040
+ raw = readFileSync82(path122, "utf-8");
21022
21041
  } catch (err) {
21023
21042
  emitMalformedWarning(sourceId, `read failed: ${errToMsg(err)}`);
21024
21043
  return void 0;
@@ -22744,10 +22763,16 @@ var init_kg_eager = __esm({
22744
22763
  // ../core/dist/kg/hook-template.js
22745
22764
  var hook_template_exports = {};
22746
22765
  __export(hook_template_exports, {
22766
+ KG_HOOK_PROMPT_SENTINEL: () => KG_HOOK_PROMPT_SENTINEL,
22767
+ KG_HOOK_PROMPT_SENTINEL_PREFIX: () => KG_HOOK_PROMPT_SENTINEL_PREFIX,
22747
22768
  KG_HOOK_SENTINEL: () => KG_HOOK_SENTINEL,
22748
22769
  KG_HOOK_SENTINEL_PREFIX: () => KG_HOOK_SENTINEL_PREFIX,
22770
+ PROMPT_TERM_EXTRACT_PY: () => PROMPT_TERM_EXTRACT_PY,
22771
+ TERM_EXTRACT_PY: () => TERM_EXTRACT_PY,
22749
22772
  buildHookCommand: () => buildHookCommand,
22750
- buildHookMatcherEntry: () => buildHookMatcherEntry
22773
+ buildHookMatcherEntry: () => buildHookMatcherEntry,
22774
+ buildPromptHookCommand: () => buildPromptHookCommand,
22775
+ buildPromptHookMatcherEntry: () => buildPromptHookMatcherEntry
22751
22776
  });
22752
22777
  function defaultUrl(flavor) {
22753
22778
  if (flavor === "host")
@@ -22763,20 +22788,26 @@ function buildHookCommand(opts) {
22763
22788
  try:
22764
22789
  d=json.loads(sys.stdin.read())
22765
22790
  route=d.get("route","")
22791
+ qnodes=[]
22792
+ try:
22793
+ qd=json.loads(os.environ.get("KG_QUERY_NODES","") or "{}")
22794
+ if isinstance(qd,dict) and qd.get("ok") and isinstance(qd.get("nodes"),list):
22795
+ qnodes=qd["nodes"]
22796
+ except Exception: qnodes=[]
22797
+ real_n=len(qnodes)
22766
22798
  if route:
22767
22799
  if route in ("kg","both"):
22768
22800
  label=d.get("top_match") or d.get("reason","")
22769
22801
  layer=d.get("layer","?")
22770
- nodes=d.get("nodes_matched",0)
22802
+ nodes=real_n if real_n else d.get("nodes_matched",0)
22771
22803
  saved=(d.get("savings") or {}).get("saved_tokens_est",0)
22772
22804
  saved_k=round(saved/1000)
22773
22805
  sys.stderr.write(f"\\x1b[32m\\u29bf\\u29bf\\x1b[0m KG hit \\u2713 {label} \\u2192 L{layer}/{route} \\u00b7 {nodes} nodes \\u00b7 \\x1b[32m~{saved_k}k tokens saved\\x1b[0m\\n")
22774
- else:
22806
+ elif route=="grep":
22775
22807
  sys.stderr.write("\\U0001f50d Grep used\\n")
22776
- if route and route != "grep":
22808
+ if route in ("kg","both"):
22777
22809
  label=d.get("top_match") or d.get("reason","")
22778
22810
  layer=d.get("layer","?")
22779
- nodes=d.get("nodes_matched",0)
22780
22811
  saved=(d.get("savings") or {}).get("saved_tokens_est",0)
22781
22812
  saved_k=round(saved/1000)
22782
22813
  q=os.environ.get("KG_QUERY","")[:60]
@@ -22786,20 +22817,44 @@ try:
22786
22817
  rel=any(k in ql for k in ("call","caller","import","uses","used","reference","who "))
22787
22818
  g="olam kg mirror graph \\"" + sym + "\\" --workspace <ws>"
22788
22819
  hint=g + (" --relates" if rel else "") + " (where X is defined; add --relates for what calls/imports it; --repo <name> to browse a repo)"
22789
- print(json.dumps({"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":f"[kg-classifier L{layer}|{route}] {label[:140]}\\n\\u21b3 graph: {hint}"}}))
22820
+ if qnodes:
22821
+ lines=[]
22822
+ for n in qnodes[:5]:
22823
+ s=str(n.get("symbol") or n.get("label") or "?")
22824
+ f=str(n.get("file") or "")
22825
+ loc=str(n.get("loc") or "")
22826
+ lines.append(f" {s} {f}:{loc}" if loc else f" {s} {f}")
22827
+ body="[kg-classifier L"+str(layer)+"|"+route+"] "+label[:140]+"\\n\\u21b3 nodes ("+str(real_n)+"):\\n"+"\\n".join(lines)+"\\n\\u21b3 graph: "+hint
22828
+ else:
22829
+ body="[kg-classifier L"+str(layer)+"|"+route+"] "+label[:140]+"\\n\\u21b3 graph: "+hint
22830
+ print(json.dumps({"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":body}}))
22790
22831
  except Exception: pass' 2>/dev/null`;
22791
22832
  const isCloud = opts.flavor === "cloud-sandbox";
22792
22833
  const resolvedUrl = isCloud ? '"${OLAM_KG_PROXY_URL:-}/v1/classify"' : url2;
22793
22834
  const authHeader = isCloud ? `-H "Authorization: Bearer \${OLAM_KG_PROXY_BEARER:-}"` : "";
22794
22835
  const cloudGuard = isCloud ? `if [ -z "\${OLAM_KG_PROXY_URL:-}" ] || [ -z "\${OLAM_KG_PROXY_BEARER:-}" ]; then exit 0; fi; ` : "";
22795
- const wsField = isCloud ? `,\\"workspace\\":\\"\${OLAM_KG_PROXY_WORKSPACE:-}\\"` : "";
22796
- const curlPost = `RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' ${authHeader} -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"${wsField}}" ${resolvedUrl} 2>/dev/null)`;
22797
- const hostRemoteFallback = opts.flavor === "host" ? `; if [ -z "$RESP" ] && [ -r "$HOME/.olam/kg-proxy-url" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then KG_ORIGIN=$(sed 's|\\(https*://[^/]*\\).*|\\1|' "$HOME/.olam/kg-proxy-url" 2>/dev/null); RESP=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"}" "$KG_ORIGIN/v1/classify" 2>/dev/null); fi` : "";
22836
+ const isHostOrWorld = opts.flavor === "host" || opts.flavor === "world";
22837
+ const wsResolve = isHostOrWorld ? `KG_WS="\${OLAM_KG_WORKSPACE:-$(cat "\${OLAM_HOME:-$HOME/.olam}/kg-default-workspace" 2>/dev/null || echo atlas-one)}"; ` : "";
22838
+ const wsField = isCloud ? `,\\"workspace\\":\\"\${OLAM_KG_PROXY_WORKSPACE:-}\\"` : `,\\"workspace\\":\\"$KG_WS\\"`;
22839
+ const termExtract = TERM_EXTRACT_PY;
22840
+ const qTermLocal = `$(echo \\"$KG_TERM\\" | head -c 200 | tr '\\"' ' ')`;
22841
+ const setTerm = `KG_TERM=$(echo "$CMD" | ${termExtract}); `;
22842
+ const curlPost = `${wsResolve}${setTerm}RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' ${authHeader} -d "{\\"q\\":\\"${qTermLocal}\\"${wsField}}" ${resolvedUrl} 2>/dev/null)`;
22843
+ const hostRemoteFallback = opts.flavor === "host" ? `; if [ -r "$HOME/.olam/kg-proxy-url" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then KG_ORIGIN=$(sed 's|\\(https*://[^/]*\\).*|\\1|' "$HOME/.olam/kg-proxy-url" 2>/dev/null); if [ -z "$RESP" ]; then RESP=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" -d "{\\"q\\":\\"${qTermLocal}\\"${wsField}}" "$KG_ORIGIN/v1/classify" 2>/dev/null); fi; fi` : "";
22844
+ const routeIsKg = `echo "$RESP" | grep -Eq '"route"[[:space:]]*:[[:space:]]*"(kg|both)"'`;
22845
+ const queryBody = `-d "{\\"q\\":\\"${qTermLocal}\\",\\"limit\\":5${wsField}}"`;
22846
+ let queryFollowup = "";
22847
+ if (opts.flavor === "host") {
22848
+ queryFollowup = `; KG_QUERY_NODES=""; if [ -n "$RESP" ] && ${routeIsKg} && [ -n "$KG_ORIGIN" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then KG_QUERY_NODES=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" ${queryBody} "$KG_ORIGIN/v1/query" 2>/dev/null); fi`;
22849
+ } else if (isCloud) {
22850
+ queryFollowup = `; KG_QUERY_NODES=""; if [ -n "$RESP" ] && ${routeIsKg}; then KG_QUERY_NODES=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' ${authHeader} ${queryBody} "\${OLAM_KG_PROXY_URL:-}/v1/query" 2>/dev/null); fi`;
22851
+ }
22852
+ const emitLine = `export KG_QUERY="$(echo \\"$CMD\\" | head -c 60 | tr '\\"' ' ')"; export KG_QUERY_NODES="\${KG_QUERY_NODES:-}"; echo "$RESP" | ${emitContext}`;
22798
22853
  return [
22799
22854
  `KG_SENTINEL=${KG_HOOK_SENTINEL}`,
22800
22855
  `CMD=$(${extractCmd})`,
22801
- `case "$CMD" in grep\\ *|grep|git\\ grep\\ *|rg\\ *|ripgrep\\ *|find\\ *|fd\\ *|ack\\ *|ag\\ *) ${cloudGuard}${curlPost}${hostRemoteFallback}`,
22802
- `KG_QUERY="$(echo \\"$CMD\\" | head -c 60 | tr '\\"' ' ')" echo "$RESP" | ${emitContext}`,
22856
+ `case "$CMD" in grep\\ *|grep|git\\ grep\\ *|rg\\ *|ripgrep\\ *|find\\ *|fd\\ *|ack\\ *|ag\\ *) ${cloudGuard}${curlPost}${hostRemoteFallback}${queryFollowup}`,
22857
+ emitLine,
22803
22858
  `;; esac`
22804
22859
  ].join("; ");
22805
22860
  }
@@ -22814,12 +22869,112 @@ function buildHookMatcherEntry(opts) {
22814
22869
  ]
22815
22870
  };
22816
22871
  }
22817
- var KG_HOOK_SENTINEL, KG_HOOK_SENTINEL_PREFIX;
22872
+ function buildPromptHookCommand(opts) {
22873
+ const url2 = opts.url ?? defaultUrl(opts.flavor);
22874
+ const isCloud = opts.flavor === "cloud-sandbox";
22875
+ const isHostOrWorld = opts.flavor === "host" || opts.flavor === "world";
22876
+ const extractCmd = `python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('prompt',''))" 2>/dev/null`;
22877
+ const resolvedUrl = isCloud ? '"${OLAM_KG_PROXY_URL:-}/v1/classify"' : url2;
22878
+ const authHeader = isCloud ? `-H "Authorization: Bearer \${OLAM_KG_PROXY_BEARER:-}"` : "";
22879
+ const cloudGuard = isCloud ? `if [ -z "\${OLAM_KG_PROXY_URL:-}" ] || [ -z "\${OLAM_KG_PROXY_BEARER:-}" ]; then exit 0; fi; ` : "";
22880
+ const wsResolve = isHostOrWorld ? `KG_WS="\${OLAM_KG_WORKSPACE:-$(cat "\${OLAM_HOME:-$HOME/.olam}/kg-default-workspace" 2>/dev/null || echo atlas-one)}"; ` : "";
22881
+ const wsField = isCloud ? `,\\"workspace\\":\\"\${OLAM_KG_PROXY_WORKSPACE:-}\\"` : `,\\"workspace\\":\\"$KG_WS\\"`;
22882
+ const classifyPost = `${wsResolve}RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' ${authHeader} -d "{\\"q\\":\\"$(echo \\"$PROMPT\\" | head -c 200 | tr '\\"' ' ')\\"${wsField}}" ${resolvedUrl} 2>/dev/null)`;
22883
+ const hostRemoteFallback = opts.flavor === "host" ? `; if [ -r "$HOME/.olam/kg-proxy-url" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then KG_ORIGIN=$(sed 's|\\(https*://[^/]*\\).*|\\1|' "$HOME/.olam/kg-proxy-url" 2>/dev/null); if [ -z "$RESP" ]; then RESP=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" -d "{\\"q\\":\\"$(echo \\"$PROMPT\\" | head -c 200 | tr '\\"' ' ')\\"${wsField}}" "$KG_ORIGIN/v1/classify" 2>/dev/null); fi; fi` : "";
22884
+ const setTerm = `KG_TERM=$(echo "$PROMPT" | ${PROMPT_TERM_EXTRACT_PY}); `;
22885
+ const qTerm = `$(echo \\"$KG_TERM\\" | head -c 200 | tr '\\"' ' ')`;
22886
+ const routeIsKg = `echo "$RESP" | grep -Eq '"route"[[:space:]]*:[[:space:]]*"(kg|both)"'`;
22887
+ const queryBody = `-d "{\\"q\\":\\"${qTerm}\\",\\"limit\\":5${wsField}}"`;
22888
+ let queryFollowup = "";
22889
+ if (opts.flavor === "host") {
22890
+ queryFollowup = `; KG_QUERY_NODES=""; if [ -n "$RESP" ] && ${routeIsKg} && [ -n "$KG_ORIGIN" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then ${setTerm}KG_QUERY_NODES=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" ${queryBody} "$KG_ORIGIN/v1/query" 2>/dev/null); fi`;
22891
+ } else if (isCloud) {
22892
+ queryFollowup = `; KG_QUERY_NODES=""; if [ -n "$RESP" ] && ${routeIsKg}; then ${setTerm}KG_QUERY_NODES=$(curl -s --max-time 3 -X POST -H 'Content-Type: application/json' ${authHeader} ${queryBody} "\${OLAM_KG_PROXY_URL:-}/v1/query" 2>/dev/null); fi`;
22893
+ }
22894
+ const emitContext = `python3 -c 'import json,sys,os
22895
+ try:
22896
+ d=json.loads(sys.stdin.read())
22897
+ route=d.get("route","")
22898
+ if route not in ("kg","both"): sys.exit(0)
22899
+ qnodes=[]
22900
+ try:
22901
+ qd=json.loads(os.environ.get("KG_QUERY_NODES","") or "{}")
22902
+ if isinstance(qd,dict) and qd.get("ok") and isinstance(qd.get("nodes"),list):
22903
+ qnodes=qd["nodes"]
22904
+ except Exception: qnodes=[]
22905
+ layer=d.get("layer","?")
22906
+ cands=d.get("candidate_symbols") or []
22907
+ sym=(cands[0] if cands else "") or "<symbol>"
22908
+ q=os.environ.get("KG_QUERY","")[:60]
22909
+ ql=q.lower()
22910
+ rel=any(k in ql for k in ("call","caller","import","uses","used","reference","who "))
22911
+ g="olam kg mirror graph \\"" + sym + "\\" --workspace <ws>"
22912
+ hint=g + (" --relates" if rel else "") + " (where X is defined; add --relates for what calls/imports it; --repo <name> to browse a repo)"
22913
+ if qnodes:
22914
+ lines=[]
22915
+ for n in qnodes[:5]:
22916
+ s=str(n.get("symbol") or n.get("label") or "?")
22917
+ f=str(n.get("file") or "")
22918
+ loc=str(n.get("loc") or "")
22919
+ lines.append(f" {s} {f}:{loc}" if loc else f" {s} {f}")
22920
+ body="[kg L"+str(layer)+"|"+route+"] this looks symbol-shaped \u2014 the KG already has:\\n\\u21b3 nodes ("+str(len(qnodes))+"):\\n"+"\\n".join(lines)+"\\n\\u21b3 graph: "+hint
22921
+ else:
22922
+ body="[kg L"+str(layer)+"|"+route+"] this looks symbol-shaped \u2014 prefer the KG over grep.\\n\\u21b3 graph: "+hint
22923
+ print(json.dumps({"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":body}}))
22924
+ except Exception: pass' 2>/dev/null`;
22925
+ const emitLine = `export KG_QUERY="$(echo \\"$PROMPT\\" | head -c 60 | tr '\\"' ' ')"; export KG_QUERY_NODES="\${KG_QUERY_NODES:-}"; echo "$RESP" | ${emitContext}`;
22926
+ return [
22927
+ `KG_SENTINEL=${KG_HOOK_PROMPT_SENTINEL}`,
22928
+ `PROMPT=$(${extractCmd})`,
22929
+ // No search-tool gate: classify every non-empty prompt. Empty prompt → no-op.
22930
+ `if [ -n "$PROMPT" ]; then ${cloudGuard}${classifyPost}${hostRemoteFallback}${queryFollowup}; ${emitLine}; fi`
22931
+ ].join("; ");
22932
+ }
22933
+ function buildPromptHookMatcherEntry(opts) {
22934
+ return {
22935
+ hooks: [
22936
+ {
22937
+ type: "command",
22938
+ command: buildPromptHookCommand(opts)
22939
+ }
22940
+ ]
22941
+ };
22942
+ }
22943
+ var KG_HOOK_SENTINEL, KG_HOOK_SENTINEL_PREFIX, KG_HOOK_PROMPT_SENTINEL, KG_HOOK_PROMPT_SENTINEL_PREFIX, TERM_EXTRACT_PY, PROMPT_TERM_EXTRACT_PY;
22818
22944
  var init_hook_template2 = __esm({
22819
22945
  "../core/dist/kg/hook-template.js"() {
22820
22946
  "use strict";
22821
- KG_HOOK_SENTINEL = "kg-service-v3-classifier-hook";
22947
+ KG_HOOK_SENTINEL = "kg-service-v4-classifier-hook";
22822
22948
  KG_HOOK_SENTINEL_PREFIX = "kg-service-v";
22949
+ KG_HOOK_PROMPT_SENTINEL = "kg-service-prompt-v4-classifier-hook";
22950
+ KG_HOOK_PROMPT_SENTINEL_PREFIX = "kg-service-prompt-v";
22951
+ TERM_EXTRACT_PY = `python3 -c 'import sys
22952
+ cmd=sys.stdin.read().strip()
22953
+ toks=cmd.split()
22954
+ TOOLS=("grep","rg","ripgrep","find","fd","ack","ag")
22955
+ DIRS=("src","lib","app","test","spec","packages")
22956
+ QUOTES=chr(34)+chr(39)+chr(96)
22957
+ i=0
22958
+ if toks and toks[0]=="git" and len(toks)>1 and toks[1]=="grep": i=2
22959
+ elif toks and toks[0] in TOOLS: i=1
22960
+ term=""
22961
+ for t in toks[i:]:
22962
+ if t.startswith("-"): continue
22963
+ s=t.strip(QUOTES)
22964
+ if not s: continue
22965
+ if "/" in s or s.endswith("/") or s in DIRS or s in (".",".."): continue
22966
+ term=s; break
22967
+ print(term if term else cmd)' 2>/dev/null`;
22968
+ PROMPT_TERM_EXTRACT_PY = `python3 -c 'import sys,re
22969
+ p=sys.stdin.read().strip()
22970
+ Q=chr(34); B=chr(96); A=chr(39)
22971
+ pats=[B+"([^"+B+"]{3,})"+B, Q+"([^"+Q+"]{3,})"+Q, A+"([^"+A+"]{3,})"+A, "\\\\b[A-Za-z_$][\\\\w$]*(?:\\\\.[A-Za-z_$][\\\\w$]*)+\\\\b", "\\\\b[a-zA-Z_$][\\\\w$]*[A-Z][\\\\w$]*\\\\b", "\\\\b[a-z_][a-z0-9_]*_[a-z0-9_]+\\\\b"]
22972
+ term=""
22973
+ for pat in pats:
22974
+ m=re.search(pat,p)
22975
+ if m:
22976
+ term=(m.group(1) if m.groups() else m.group(0)).strip(); break
22977
+ print(term if term else p)' 2>/dev/null`;
22823
22978
  }
22824
22979
  });
22825
22980
 
@@ -23383,8 +23538,8 @@ function olamDir(deps) {
23383
23538
  return join104(deps.home ?? homedir56(), ".olam");
23384
23539
  }
23385
23540
  function resolveLocalSecretValue(obj, deps = {}) {
23386
- const path121 = join104(olamDir(deps), obj.local);
23387
- const direct = readSecretAtPathOrNull(path121);
23541
+ const path122 = join104(olamDir(deps), obj.local);
23542
+ const direct = readSecretAtPathOrNull(path122);
23388
23543
  if (direct) return direct;
23389
23544
  if (obj.object === "plan-chat-secret") {
23390
23545
  const conn = readRemoteConnection();
@@ -23696,9 +23851,9 @@ var init_machine_schema = __esm({
23696
23851
  init_cli_version();
23697
23852
  init_output();
23698
23853
  init_exit_codes();
23699
- import { existsSync as existsSync135 } from "node:fs";
23700
- import { homedir as homedir88 } from "node:os";
23701
- import { join as join143 } from "node:path";
23854
+ import { existsSync as existsSync136 } from "node:fs";
23855
+ import { homedir as homedir89 } from "node:os";
23856
+ import { join as join144 } from "node:path";
23702
23857
  import { Command } from "commander";
23703
23858
 
23704
23859
  // src/commands/init.ts
@@ -24254,9 +24409,9 @@ var UnknownArchetypeError = class extends Error {
24254
24409
  };
24255
24410
  var ArchetypeCycleError = class extends Error {
24256
24411
  path;
24257
- constructor(path121) {
24258
- super(`Archetype inheritance cycle detected: ${path121.join(" \u2192 ")} \u2192 ${path121[0] ?? "?"}`);
24259
- this.path = path121;
24412
+ constructor(path122) {
24413
+ super(`Archetype inheritance cycle detected: ${path122.join(" \u2192 ")} \u2192 ${path122[0] ?? "?"}`);
24414
+ this.path = path122;
24260
24415
  this.name = "ArchetypeCycleError";
24261
24416
  }
24262
24417
  };
@@ -25606,8 +25761,8 @@ async function installObservability(opts) {
25606
25761
  }
25607
25762
  const outcomes = [];
25608
25763
  for (const script of OBSERVABILITY_SCRIPTS) {
25609
- const path121 = join37(observabilityDir, script);
25610
- if (!existsSync31(path121)) {
25764
+ const path122 = join37(observabilityDir, script);
25765
+ if (!existsSync31(path122)) {
25611
25766
  outcomes.push({ script, state: "missing" });
25612
25767
  continue;
25613
25768
  }
@@ -25619,7 +25774,7 @@ async function installObservability(opts) {
25619
25774
  }
25620
25775
  }
25621
25776
  const runResult = await runObservabilityScript(
25622
- path121,
25777
+ path122,
25623
25778
  script,
25624
25779
  scriptEnv,
25625
25780
  opts.verbose
@@ -27291,8 +27446,8 @@ function locateMkcert(deps) {
27291
27446
  const spawn14 = deps.spawnImpl ?? defaultSpawn;
27292
27447
  const which = spawn14("which", ["mkcert"]);
27293
27448
  if (which.status === 0) {
27294
- const path121 = which.stdout.trim();
27295
- if (path121.length > 0) return path121;
27449
+ const path122 = which.stdout.trim();
27450
+ if (path122.length > 0) return path122;
27296
27451
  }
27297
27452
  const brewPath = "/opt/homebrew/bin/mkcert";
27298
27453
  const exists = spawn14("test", ["-x", brewPath]);
@@ -28622,12 +28777,12 @@ function appendAuditEntry(entry, auditLogPath, writeFileSyncImpl) {
28622
28777
  async function runManifestRefresh(manifestsDir, acceptRegression, deps = {}, peripheral) {
28623
28778
  const auditLogPath = deps.auditLogPath ?? MANIFEST_REFRESH_AUDIT_LOG;
28624
28779
  const readdirSync37 = deps.readdirSync ?? fs35.readdirSync;
28625
- const readFileSync125 = deps.readFileSync ?? fs35.readFileSync;
28780
+ const readFileSync126 = deps.readFileSync ?? fs35.readFileSync;
28626
28781
  const writeFileSyncImpl = deps.writeFileSync ?? fs35.writeFileSync;
28627
- const existsSync136 = deps.existsSync ?? fs35.existsSync;
28782
+ const existsSync137 = deps.existsSync ?? fs35.existsSync;
28628
28783
  const now = deps.now ? deps.now() : /* @__PURE__ */ new Date();
28629
28784
  const targetDir = peripheral ? path36.join(manifestsDir, peripheral) : manifestsDir;
28630
- if (!existsSync136(targetDir)) {
28785
+ if (!existsSync137(targetDir)) {
28631
28786
  return {
28632
28787
  ok: false,
28633
28788
  message: peripheral ? `peripheral manifests directory not found: ${targetDir}` : `manifests directory not found: ${targetDir}`
@@ -28648,7 +28803,7 @@ async function runManifestRefresh(manifestsDir, acceptRegression, deps = {}, per
28648
28803
  const filePath = path36.join(targetDir, file);
28649
28804
  let content;
28650
28805
  try {
28651
- content = readFileSync125(filePath, "utf8");
28806
+ content = readFileSync126(filePath, "utf8");
28652
28807
  } catch {
28653
28808
  continue;
28654
28809
  }
@@ -28907,11 +29062,11 @@ async function checkSecretPreCondition(context, deps) {
28907
29062
  }
28908
29063
  async function applyConfigMapSubstitution(context, manifestsDir, deps) {
28909
29064
  const wrap = deps.kubectlWrapImpl ?? kubectlWrap;
28910
- const readFileSync125 = deps.readFileSyncImpl ?? fs36.readFileSync;
29065
+ const readFileSync126 = deps.readFileSyncImpl ?? fs36.readFileSync;
28911
29066
  const configMapPath = path37.join(manifestsDir, "30-configmap.yaml");
28912
29067
  let rawYaml;
28913
29068
  try {
28914
- rawYaml = readFileSync125(configMapPath, "utf8");
29069
+ rawYaml = readFileSync126(configMapPath, "utf8");
28915
29070
  } catch (err) {
28916
29071
  return `Failed to read ConfigMap at ${configMapPath}: ${err instanceof Error ? err.message : String(err)}`;
28917
29072
  }
@@ -30205,10 +30360,10 @@ function emptyMigrationState() {
30205
30360
  return { version: 1, migrated: {} };
30206
30361
  }
30207
30362
  function readMigrationState(statePath, deps) {
30208
- const existsSync136 = deps.existsSync ?? fs40.existsSync;
30209
- const readFileSync125 = deps.readFileSync ?? ((p) => fs40.readFileSync(p, "utf-8"));
30210
- if (!existsSync136(statePath)) return emptyMigrationState();
30211
- const raw = readFileSync125(statePath);
30363
+ const existsSync137 = deps.existsSync ?? fs40.existsSync;
30364
+ const readFileSync126 = deps.readFileSync ?? ((p) => fs40.readFileSync(p, "utf-8"));
30365
+ if (!existsSync137(statePath)) return emptyMigrationState();
30366
+ const raw = readFileSync126(statePath);
30212
30367
  let parsed;
30213
30368
  try {
30214
30369
  parsed = JSON.parse(raw);
@@ -30225,17 +30380,17 @@ function readMigrationState(statePath, deps) {
30225
30380
  return parsed;
30226
30381
  }
30227
30382
  function writeMigrationStateAtomic(statePath, state, deps) {
30228
- const writeFileSync76 = deps.writeFileSync ?? ((p, d) => fs40.writeFileSync(p, d, "utf-8"));
30383
+ const writeFileSync77 = deps.writeFileSync ?? ((p, d) => fs40.writeFileSync(p, d, "utf-8"));
30229
30384
  const renameSync23 = deps.renameSync ?? fs40.renameSync;
30230
30385
  const tmpPath = `${statePath}.tmp`;
30231
- writeFileSync76(tmpPath, JSON.stringify(state, null, 2) + "\n");
30386
+ writeFileSync77(tmpPath, JSON.stringify(state, null, 2) + "\n");
30232
30387
  renameSync23(tmpPath, statePath);
30233
30388
  }
30234
30389
  function readLocalAccounts(accountsPath, deps) {
30235
- const existsSync136 = deps.existsSync ?? fs40.existsSync;
30236
- const readFileSync125 = deps.readFileSync ?? ((p) => fs40.readFileSync(p, "utf-8"));
30237
- if (!existsSync136(accountsPath)) return null;
30238
- const raw = readFileSync125(accountsPath);
30390
+ const existsSync137 = deps.existsSync ?? fs40.existsSync;
30391
+ const readFileSync126 = deps.readFileSync ?? ((p) => fs40.readFileSync(p, "utf-8"));
30392
+ if (!existsSync137(accountsPath)) return null;
30393
+ const raw = readFileSync126(accountsPath);
30239
30394
  let parsed;
30240
30395
  try {
30241
30396
  parsed = JSON.parse(raw);
@@ -33877,9 +34032,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
33877
34032
  "These source files have changed since the image was built; the",
33878
34033
  "changes will NOT take effect in fresh worlds until you rebuild:"
33879
34034
  ];
33880
- for (const { path: path121, mtimeMs } of result.newerSources) {
34035
+ for (const { path: path122, mtimeMs } of result.newerSources) {
33881
34036
  const when = new Date(mtimeMs).toISOString();
33882
- lines.push(` \u2022 ${path121} (modified ${when})`);
34037
+ lines.push(` \u2022 ${path122} (modified ${when})`);
33883
34038
  }
33884
34039
  lines.push("");
33885
34040
  lines.push("Rebuild with:");
@@ -34259,15 +34414,15 @@ var AGENTMEMORY_LOCAL_URL = "http://host.docker.internal:3111";
34259
34414
  var HOST_CP_URL = "http://127.0.0.1:19000";
34260
34415
  async function readHostCpTokenForCreate() {
34261
34416
  try {
34262
- const { default: fs121 } = await import("node:fs");
34263
- const { default: os66 } = await import("node:os");
34264
- const { default: path121 } = await import("node:path");
34265
- const tp = path121.join(
34266
- process.env.OLAM_HOME ?? path121.join(os66.homedir(), ".olam"),
34417
+ const { default: fs122 } = await import("node:fs");
34418
+ const { default: os67 } = await import("node:os");
34419
+ const { default: path122 } = await import("node:path");
34420
+ const tp = path122.join(
34421
+ process.env.OLAM_HOME ?? path122.join(os67.homedir(), ".olam"),
34267
34422
  "host-cp.token"
34268
34423
  );
34269
- if (!fs121.existsSync(tp)) return null;
34270
- return fs121.readFileSync(tp, "utf-8").trim();
34424
+ if (!fs122.existsSync(tp)) return null;
34425
+ return fs122.readFileSync(tp, "utf-8").trim();
34271
34426
  } catch {
34272
34427
  return null;
34273
34428
  }
@@ -34767,12 +34922,12 @@ function defaultNameFromPrompt(prompt) {
34767
34922
  }
34768
34923
  async function readHostCpToken3() {
34769
34924
  try {
34770
- const { default: fs121 } = await import("node:fs");
34771
- const { default: os66 } = await import("node:os");
34772
- const { default: path121 } = await import("node:path");
34773
- const tp = path121.join(os66.homedir(), ".olam", "host-cp.token");
34774
- if (!fs121.existsSync(tp)) return null;
34775
- const raw = fs121.readFileSync(tp, "utf-8").trim();
34925
+ const { default: fs122 } = await import("node:fs");
34926
+ const { default: os67 } = await import("node:os");
34927
+ const { default: path122 } = await import("node:path");
34928
+ const tp = path122.join(os67.homedir(), ".olam", "host-cp.token");
34929
+ if (!fs122.existsSync(tp)) return null;
34930
+ const raw = fs122.readFileSync(tp, "utf-8").trim();
34776
34931
  return raw.length > 0 ? raw : null;
34777
34932
  } catch {
34778
34933
  return null;
@@ -36853,13 +37008,13 @@ var PlansClient = class {
36853
37008
  const credentials = Buffer.from(`operator:${this.cfg.bearer}`).toString("base64");
36854
37009
  return `Basic ${credentials}`;
36855
37010
  }
36856
- async request(path121, init) {
37011
+ async request(path122, init) {
36857
37012
  const headers = {
36858
37013
  Authorization: this.authHeader()
36859
37014
  };
36860
37015
  if (init.body) headers["content-type"] = "application/json";
36861
37016
  if (init.adminSecret) headers["X-Admin-Secret"] = init.adminSecret;
36862
- return fetch(`${this.cfg.cloudUrl}${path121}`, {
37017
+ return fetch(`${this.cfg.cloudUrl}${path122}`, {
36863
37018
  method: init.method,
36864
37019
  headers,
36865
37020
  body: init.body ? JSON.stringify(init.body) : void 0
@@ -39410,11 +39565,11 @@ function zodIssueToError(issue, doc, lineCounter) {
39410
39565
  suggestion: deriveSuggestion(issue)
39411
39566
  };
39412
39567
  }
39413
- function formatJsonPath(path121) {
39414
- if (path121.length === 0)
39568
+ function formatJsonPath(path122) {
39569
+ if (path122.length === 0)
39415
39570
  return "<root>";
39416
39571
  let out = "";
39417
- for (const seg of path121) {
39572
+ for (const seg of path122) {
39418
39573
  if (typeof seg === "number") {
39419
39574
  out += `[${seg}]`;
39420
39575
  } else {
@@ -39423,11 +39578,11 @@ function formatJsonPath(path121) {
39423
39578
  }
39424
39579
  return out;
39425
39580
  }
39426
- function resolveYamlLocation(path121, doc, lineCounter) {
39581
+ function resolveYamlLocation(path122, doc, lineCounter) {
39427
39582
  let bestLine = 0;
39428
39583
  let bestColumn = 0;
39429
- for (let depth = path121.length; depth >= 0; depth -= 1) {
39430
- const segment = path121.slice(0, depth);
39584
+ for (let depth = path122.length; depth >= 0; depth -= 1) {
39585
+ const segment = path122.slice(0, depth);
39431
39586
  try {
39432
39587
  const node = doc.getIn(segment, true);
39433
39588
  if (node && typeof node === "object" && "range" in node) {
@@ -39645,11 +39800,11 @@ function topoSort(nodes) {
39645
39800
  }
39646
39801
  function traceCycle(start, byId) {
39647
39802
  const seen = /* @__PURE__ */ new Set();
39648
- const path121 = [];
39803
+ const path122 = [];
39649
39804
  let current = start;
39650
39805
  while (current && !seen.has(current)) {
39651
39806
  seen.add(current);
39652
- path121.push(current);
39807
+ path122.push(current);
39653
39808
  const node = byId.get(current);
39654
39809
  const next = node?.dependsOn[0];
39655
39810
  if (next === void 0)
@@ -39657,10 +39812,10 @@ function traceCycle(start, byId) {
39657
39812
  current = next;
39658
39813
  }
39659
39814
  if (current && seen.has(current)) {
39660
- const idx = path121.indexOf(current);
39661
- return [...path121.slice(idx), current];
39815
+ const idx = path122.indexOf(current);
39816
+ return [...path122.slice(idx), current];
39662
39817
  }
39663
- return path121;
39818
+ return path122;
39664
39819
  }
39665
39820
 
39666
39821
  // ../core/dist/executor/types.js
@@ -44546,8 +44701,8 @@ function checkWorld(container, hostShas, dockerExec) {
44546
44701
  const m = shaLine.match(/^([a-f0-9]{64})\s+(.+)$/);
44547
44702
  if (!m) continue;
44548
44703
  const sha = m[1];
44549
- const path121 = m[2];
44550
- const filename = path121?.split("/").pop();
44704
+ const path122 = m[2];
44705
+ const filename = path122?.split("/").pop();
44551
44706
  if (!filename) continue;
44552
44707
  const mtimeSecs = parseInt(mtimeLine.trim(), 10);
44553
44708
  if (Number.isNaN(mtimeSecs) || !sha) continue;
@@ -45665,9 +45820,9 @@ var TasksClient = class {
45665
45820
  this.olamNodeId = opts.olamNodeId;
45666
45821
  this.sessionId = opts.sessionId;
45667
45822
  }
45668
- async call(method, path121, scopes, body, query) {
45823
+ async call(method, path122, scopes, body, query) {
45669
45824
  const qs = query ? "?" + new URLSearchParams(Object.entries(query).filter(([, v]) => v !== void 0)).toString() : "";
45670
- const url2 = `${this.baseUrl}${path121}${qs}`;
45825
+ const url2 = `${this.baseUrl}${path122}${qs}`;
45671
45826
  const res = await fetch(url2, {
45672
45827
  method,
45673
45828
  headers: {
@@ -45718,8 +45873,8 @@ function parseFrontmatter2(raw) {
45718
45873
  }
45719
45874
  return { frontmatter: fm, body };
45720
45875
  }
45721
- function parseTracker(path121) {
45722
- const raw = readFileSync60(path121, "utf8");
45876
+ function parseTracker(path122) {
45877
+ const raw = readFileSync60(path122, "utf8");
45723
45878
  const { frontmatter, body } = parseFrontmatter2(raw);
45724
45879
  const lines = body.split("\n");
45725
45880
  const tasks = [];
@@ -45956,8 +46111,8 @@ function registerTasks(program2) {
45956
46111
  `);
45957
46112
  }
45958
46113
  });
45959
- tasks.command("sync-tracker <path>").description("Parse /10x:commit-plan tracker.md + upsert each task via POST /api/tasks (B2.4; dual-emit pattern, olam-side per cross-ownership decoupling)").option("--node-id <uuid>", "Olam node ID").option("--session-id <uuid>", "Session ID").option("--dry-run", "Parse + print task records; do not POST").option("--json", "Output raw JSON envelope").action(async (path121, opts) => {
45960
- const parsed = parseTracker(path121);
46114
+ tasks.command("sync-tracker <path>").description("Parse /10x:commit-plan tracker.md + upsert each task via POST /api/tasks (B2.4; dual-emit pattern, olam-side per cross-ownership decoupling)").option("--node-id <uuid>", "Olam node ID").option("--session-id <uuid>", "Session ID").option("--dry-run", "Parse + print task records; do not POST").option("--json", "Output raw JSON envelope").action(async (path122, opts) => {
46115
+ const parsed = parseTracker(path122);
45961
46116
  if (opts.dryRun) {
45962
46117
  process.stdout.write(JSON.stringify(parsed, null, 2) + "\n");
45963
46118
  return;
@@ -47520,7 +47675,7 @@ function registerSetup(program2) {
47520
47675
  const hasCloudArg = Boolean(
47521
47676
  rawOpts.skillsSource || rawOpts.memoryUrl || rawOpts.remoteMemoryUrl || rawOpts.kgUrl || rawOpts.remoteKgUrl || rawOpts.remoteUrl || rawOpts.remoteDispatchUrl || rawOpts.credentials
47522
47677
  );
47523
- let path121 = resolveSetupPath2(
47678
+ let path122 = resolveSetupPath2(
47524
47679
  {
47525
47680
  localMode: rawOpts.localMode,
47526
47681
  cloudFirst: rawOpts.cloudFirst || hasCloudArg,
@@ -47530,7 +47685,7 @@ function registerSetup(program2) {
47530
47685
  },
47531
47686
  isTty
47532
47687
  );
47533
- if (path121 === "prompt") {
47688
+ if (path122 === "prompt") {
47534
47689
  const { select: select2 } = await import("@inquirer/prompts");
47535
47690
  const choice = await select2({
47536
47691
  message: "How do you want to set up olam?",
@@ -47540,9 +47695,9 @@ function registerSetup(program2) {
47540
47695
  ],
47541
47696
  default: "cloud"
47542
47697
  });
47543
- path121 = choice === "local" ? "local" : "cloud";
47698
+ path122 = choice === "local" ? "local" : "cloud";
47544
47699
  }
47545
- if (path121 === "cloud") {
47700
+ if (path122 === "cloud") {
47546
47701
  const credentials = rawOpts.credentials === "gcp" ? "gcp" : rawOpts.credentials === "prompt" ? "prompt" : void 0;
47547
47702
  if (rawOpts.credentials !== void 0 && credentials === void 0) {
47548
47703
  process.stderr.write(`[olam setup] invalid --credentials "${rawOpts.credentials}" (expected: gcp | prompt)
@@ -49786,7 +49941,10 @@ var HOOK_BASENAMES = [
49786
49941
  "agentmemory-recall-trigger.mjs",
49787
49942
  "agentmemory-classify-queue.mjs",
49788
49943
  "agentmemory-reflect-cite.mjs",
49789
- "recall-log.mjs"
49944
+ "recall-log.mjs",
49945
+ // Shared dependency module imported by recall-trigger, classify-queue, and
49946
+ // agentmemory-save. Copied (not registered) — sibling of its importers.
49947
+ "agent-memory-gate.mjs"
49790
49948
  ];
49791
49949
  var SOURCE_ANCHOR = "agentmemory-session-recall.js";
49792
49950
  function resolveSourceDir(here3) {
@@ -49908,9 +50066,19 @@ function installHooks(opts = {}) {
49908
50066
  }
49909
50067
  });
49910
50068
  settings.push(`kg-hook (PreToolUse): ${describeMerge(kgResult.status)}`);
50069
+ const kgPromptResult = mergeHomeSettingsJson(settingsPath, {
50070
+ ensureHook: {
50071
+ stage: "UserPromptSubmit",
50072
+ sentinel: KG_HOOK_PROMPT_SENTINEL,
50073
+ staleSentinelPrefix: KG_HOOK_PROMPT_SENTINEL_PREFIX,
50074
+ entry: buildPromptHookMatcherEntry({ flavor: "host" })
50075
+ }
50076
+ });
50077
+ settings.push(`kg-hook (UserPromptSubmit): ${describeMerge(kgPromptResult.status)}`);
49911
50078
  } else {
49912
50079
  settings.push("save-hook (PostToolUse): dry-run");
49913
50080
  settings.push("kg-hook (PreToolUse): dry-run");
50081
+ settings.push("kg-hook (UserPromptSubmit): dry-run");
49914
50082
  }
49915
50083
  return { filesWritten, filesReplaced, filesUnchanged, backups, settings };
49916
50084
  }
@@ -50558,6 +50726,9 @@ function printSyncSummary(summary2, dryRun, verbose = false) {
50558
50726
  console.log(` ${pc38.dim("(dropped)")} ${cmd.slice(0, 120)}${cmd.length > 120 ? "\u2026" : ""}`);
50559
50727
  }
50560
50728
  }
50729
+ if (summary2.merge.legacyReclaimed > 0) {
50730
+ console.log(` ${pc38.dim(`legacy duplicates reclaimed: ${summary2.merge.legacyReclaimed} (pre-marker hook copies collapsed; each olam hook now fires once)`)}`);
50731
+ }
50561
50732
  }
50562
50733
  if (summary2.metaHooks) {
50563
50734
  const mh = summary2.metaHooks;
@@ -53827,7 +53998,7 @@ function resolveBundlePath(candidates2 = BUNDLE_PATH_CANDIDATES, exists = exists
53827
53998
  var MISSING_BUNDLE_REMEDY = "olam mcp server bundle missing. Searched: " + BUNDLE_PATH_CANDIDATES.join(", ") + ". For local dev, run: node packages/cli/scripts/bundle-mcp-server.mjs. A fresh `npm install -g @pleri/olam-cli@latest` should always include the bundle (see prepublishOnly in packages/cli/package.json); file an issue if it does not.";
53828
53999
  function registerMcpServe(cmd) {
53829
54000
  cmd.command("serve").description(
53830
- "Run the olam MCP server (stdio transport). Claude Code wires this via `claude mcp add olam --scope user -- npx -y @pleri/olam-cli mcp serve`."
54001
+ "Run the olam MCP server (stdio transport). Claude Code wires this via `claude mcp add olam --scope user -- npx -y @pleri/olam-cli@latest mcp serve`."
53831
54002
  ).action(async () => {
53832
54003
  const bundlePath = resolveBundlePath();
53833
54004
  if (bundlePath === null) {
@@ -53836,6 +54007,8 @@ function registerMcpServe(cmd) {
53836
54007
  return;
53837
54008
  }
53838
54009
  await import(bundlePath);
54010
+ await new Promise(() => {
54011
+ });
53839
54012
  });
53840
54013
  }
53841
54014
 
@@ -53843,8 +54016,9 @@ function registerMcpServe(cmd) {
53843
54016
  init_output();
53844
54017
  init_install_shared();
53845
54018
  var NPM_PACKAGE_NAME2 = "@pleri/olam-cli";
54019
+ var NPM_PACKAGE_SPEC = `${NPM_PACKAGE_NAME2}@latest`;
53846
54020
  function buildClaudeMcpAddArgs2(scope, useGlobal) {
53847
- const target = useGlobal ? { cmd: "olam", args: ["mcp", "serve"] } : { cmd: "npx", args: ["-y", NPM_PACKAGE_NAME2, "mcp", "serve"] };
54021
+ const target = useGlobal ? { cmd: "olam", args: ["mcp", "serve"] } : { cmd: "npx", args: ["-y", NPM_PACKAGE_SPEC, "mcp", "serve"] };
53848
54022
  return {
53849
54023
  command: "claude",
53850
54024
  args: ["mcp", "add", "olam", "--scope", scope, "--", target.cmd, ...target.args]
@@ -53954,8 +54128,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
53954
54128
  function authHeaders() {
53955
54129
  return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
53956
54130
  }
53957
- async function apiFetch(path121, init = {}) {
53958
- const res = await fetch(`${BASE_URL}${path121}`, {
54131
+ async function apiFetch(path122, init = {}) {
54132
+ const res = await fetch(`${BASE_URL}${path122}`, {
53959
54133
  ...init,
53960
54134
  headers: {
53961
54135
  "Content-Type": "application/json",
@@ -54972,18 +55146,29 @@ import { existsSync as existsSync121 } from "node:fs";
54972
55146
  // ../core/dist/memory/gate.js
54973
55147
  init_olam_paths();
54974
55148
  import { readFileSync as readFileSync108 } from "node:fs";
54975
- var TRUTHY_ENV = /* @__PURE__ */ new Set(["1", "true", "on"]);
54976
- var TRUTHY_FILE = /* @__PURE__ */ new Set(["true", "1"]);
54977
- function isAgentMemoryEnabled() {
55149
+ var ENV_MODE = /* @__PURE__ */ new Map([
55150
+ ["readonly", "readonly"],
55151
+ ["editor", "editor"],
55152
+ ["1", "readonly"],
55153
+ ["true", "readonly"],
55154
+ ["on", "readonly"]
55155
+ ]);
55156
+ var FILE_MODE = /* @__PURE__ */ new Map([
55157
+ ["readonly", "readonly"],
55158
+ ["editor", "editor"],
55159
+ ["true", "readonly"],
55160
+ ["1", "readonly"]
55161
+ ]);
55162
+ function agentMemoryMode() {
54978
55163
  const envVal = process.env["OLAM_AGENT_MEMORY"];
54979
55164
  if (envVal !== void 0 && envVal.length > 0) {
54980
- return TRUTHY_ENV.has(envVal.trim().toLowerCase());
55165
+ return ENV_MODE.get(envVal.trim().toLowerCase()) ?? "off";
54981
55166
  }
54982
55167
  try {
54983
55168
  const raw = readFileSync108(agentMemoryEnabledPath(), "utf8").trim().toLowerCase();
54984
- return TRUTHY_FILE.has(raw);
55169
+ return FILE_MODE.get(raw) ?? "off";
54985
55170
  } catch {
54986
- return false;
55171
+ return "off";
54987
55172
  }
54988
55173
  }
54989
55174
 
@@ -55037,18 +55222,18 @@ async function runMemoryStatus(opts = {}) {
55037
55222
  printInfo("livez", s.livez);
55038
55223
  printInfo("secret", s.secretSet ? "~/.olam/memory-secret (set)" : "(missing)");
55039
55224
  printInfo("port", `${s.port}`);
55040
- const gateEnabled = isAgentMemoryEnabled();
55225
+ const mode = agentMemoryMode();
55041
55226
  const envVal = process.env["OLAM_AGENT_MEMORY"];
55042
55227
  const envActive = envVal !== void 0 && envVal.length > 0;
55043
- if (gateEnabled) {
55044
- printInfo(
55045
- "agent-memory",
55046
- envActive ? `ENABLED (OLAM_AGENT_MEMORY=${envVal} env override)` : `ENABLED (flag file: ${agentMemoryEnabledPath()})`
55047
- );
55228
+ const source = envActive ? `OLAM_AGENT_MEMORY=${envVal} env override` : `flag file: ${agentMemoryEnabledPath()}`;
55229
+ if (mode === "editor") {
55230
+ printInfo("agent-memory", `EDITOR \u2014 recall + capture (${source})`);
55231
+ } else if (mode === "readonly") {
55232
+ printInfo("agent-memory", `READONLY \u2014 recall only, no capture (${source})`);
55048
55233
  } else {
55049
55234
  printInfo(
55050
55235
  "agent-memory",
55051
- envActive ? `DISABLED (OLAM_AGENT_MEMORY=${envVal} env override)` : `DISABLED \u2014 enable with \`olam memory enable\``
55236
+ envActive ? `OFF (${source})` : `OFF \u2014 enable with \`olam memory enable\` (--editor for capture)`
55052
55237
  );
55053
55238
  }
55054
55239
  if (s.legacyPidfilePresent) {
@@ -55194,27 +55379,34 @@ init_olam_paths();
55194
55379
  import { mkdirSync as mkdirSync73, writeFileSync as writeFileSync66 } from "node:fs";
55195
55380
  import { dirname as dirname69 } from "node:path";
55196
55381
  init_output();
55197
- function writeGateFlag(enabled) {
55382
+ function writeGateFlag(value) {
55198
55383
  const flagPath = agentMemoryEnabledPath();
55199
55384
  mkdirSync73(dirname69(flagPath), { recursive: true });
55200
- writeFileSync66(flagPath, enabled ? "TRUE\n" : "FALSE\n", { mode: 420 });
55385
+ writeFileSync66(flagPath, `${value}
55386
+ `, { mode: 420 });
55201
55387
  }
55202
55388
  function registerMemoryEnable(parent) {
55203
- parent.command("enable").description("Enable agent-memory recall and capture (writes TRUE to the gate flag file)").action(() => {
55204
- writeGateFlag(true);
55205
- const flagPath = agentMemoryEnabledPath();
55206
- printSuccess(`agent-memory enabled`);
55207
- printInfo("flag file", flagPath);
55208
- printInfo("gate", isAgentMemoryEnabled() ? "ENABLED" : "ENABLED (re-read confirms)");
55389
+ parent.command("enable").description(
55390
+ "Enable agent-memory. Default READONLY (recall only); pass --editor for recall + capture."
55391
+ ).option("--editor", "Grant capture (write) access \u2014 fully agentic mode (EDITOR)").option("--readonly", "Recall only, no capture (READONLY) \u2014 the default").action((opts) => {
55392
+ if (opts.editor && opts.readonly) {
55393
+ printInfo("gate", "pass only one of --editor / --readonly");
55394
+ process.exitCode = 1;
55395
+ return;
55396
+ }
55397
+ const value = opts.editor ? "EDITOR" : "READONLY";
55398
+ writeGateFlag(value);
55399
+ printSuccess(`agent-memory enabled (${value})`);
55400
+ printInfo("flag file", agentMemoryEnabledPath());
55401
+ printInfo("mode", agentMemoryMode());
55209
55402
  });
55210
55403
  }
55211
55404
  function registerMemoryDisable(parent) {
55212
55405
  parent.command("disable").description("Disable agent-memory recall and capture (writes FALSE to the gate flag file)").action(() => {
55213
- writeGateFlag(false);
55214
- const flagPath = agentMemoryEnabledPath();
55406
+ writeGateFlag("FALSE");
55215
55407
  printSuccess(`agent-memory disabled`);
55216
- printInfo("flag file", flagPath);
55217
- printInfo("gate", isAgentMemoryEnabled() ? "DISABLED (re-read confirms)" : "DISABLED");
55408
+ printInfo("flag file", agentMemoryEnabledPath());
55409
+ printInfo("mode", agentMemoryMode());
55218
55410
  });
55219
55411
  }
55220
55412
 
@@ -55579,13 +55771,13 @@ function resolveMemoryServiceDir() {
55579
55771
  );
55580
55772
  }
55581
55773
  function resolveLocalBridgeScript(serviceDir) {
55582
- const path121 = join126(serviceDir, "scripts", "local-bridge-server.mjs");
55583
- if (!existsSync123(path121)) {
55774
+ const path122 = join126(serviceDir, "scripts", "local-bridge-server.mjs");
55775
+ if (!existsSync123(path122)) {
55584
55776
  throw new Error(
55585
- `Could not find local-bridge-server.mjs at ${path121}. Verify packages/memory-service ships the scripts/ directory.`
55777
+ `Could not find local-bridge-server.mjs at ${path122}. Verify packages/memory-service ships the scripts/ directory.`
55586
55778
  );
55587
55779
  }
55588
- return path121;
55780
+ return path122;
55589
55781
  }
55590
55782
  function validateBridgeOpts(opts) {
55591
55783
  const local = opts.local ?? true;
@@ -56444,18 +56636,179 @@ function registerInboxInstallHookCommand(inbox) {
56444
56636
  });
56445
56637
  }
56446
56638
 
56639
+ // src/commands/inbox-toggle.ts
56640
+ init_merge_settings();
56641
+ import * as fs112 from "node:fs";
56642
+ import * as os60 from "node:os";
56643
+ import * as path112 from "node:path";
56644
+ init_output();
56645
+ var INBOX_ENV_KEY = "OLAM_QUESTION_INBOX";
56646
+ var OPT_OUT_VALUE = "0";
56647
+ function isOptOutValue(v) {
56648
+ const s = (v ?? "").toLowerCase();
56649
+ return s === "0" || s === "false" || s === "off" || s === "no";
56650
+ }
56651
+ function resolveScope(opts) {
56652
+ return opts.scope === "project" ? "project" : "user";
56653
+ }
56654
+ function localBrokerBasePresent() {
56655
+ return fs112.existsSync(path112.join(os60.homedir(), ".olam", "anthropic-base-url"));
56656
+ }
56657
+ function readPersistedOptOut(filePath) {
56658
+ if (!fs112.existsSync(filePath)) return void 0;
56659
+ const raw = fs112.readFileSync(filePath, "utf-8");
56660
+ if (!raw.trim()) return void 0;
56661
+ let settings;
56662
+ try {
56663
+ settings = JSON.parse(raw);
56664
+ } catch {
56665
+ return void 0;
56666
+ }
56667
+ const env = settings["env"];
56668
+ if (!env || typeof env !== "object") return void 0;
56669
+ const val = env[INBOX_ENV_KEY];
56670
+ return typeof val === "string" ? val : void 0;
56671
+ }
56672
+ function doDisable(scope) {
56673
+ const filePath = settingsPathFor3(scope);
56674
+ let result;
56675
+ try {
56676
+ result = mergeHomeSettingsJson(filePath, { env: { [INBOX_ENV_KEY]: OPT_OUT_VALUE } });
56677
+ } catch (err) {
56678
+ printError(
56679
+ `could not update ${filePath}: ${err instanceof Error ? err.message : String(err)}`
56680
+ );
56681
+ process.exitCode = 1;
56682
+ return;
56683
+ }
56684
+ if (result.status === "installed") {
56685
+ printSuccess(`question inbox DISABLED (${scope} scope)`);
56686
+ } else {
56687
+ printInfo("inbox", `already disabled (${scope} scope)`);
56688
+ }
56689
+ printInfo("settings", filePath);
56690
+ printInfo("effect", "new Claude Code sessions answer AskUserQuestion in the terminal");
56691
+ printInfo("re-enable", `olam inbox enable --scope ${scope}`);
56692
+ }
56693
+ function doEnable(scope) {
56694
+ const filePath = settingsPathFor3(scope);
56695
+ if (!fs112.existsSync(filePath)) {
56696
+ printInfo("inbox", `no settings at ${filePath} \u2014 already at default (ON for local)`);
56697
+ return;
56698
+ }
56699
+ const raw = fs112.readFileSync(filePath, "utf-8");
56700
+ if (!raw.trim()) {
56701
+ printInfo("inbox", `settings empty at ${filePath} \u2014 already at default (ON for local)`);
56702
+ return;
56703
+ }
56704
+ let settings;
56705
+ try {
56706
+ settings = JSON.parse(raw);
56707
+ } catch {
56708
+ printError(`could not parse ${filePath} \u2014 no changes made`);
56709
+ process.exitCode = 1;
56710
+ return;
56711
+ }
56712
+ const env = settings["env"];
56713
+ if (!env || typeof env !== "object" || !(INBOX_ENV_KEY in env)) {
56714
+ printInfo("inbox", `not disabled at this scope (${scope}) \u2014 nothing to enable`);
56715
+ return;
56716
+ }
56717
+ const nextEnv = { ...env };
56718
+ delete nextEnv[INBOX_ENV_KEY];
56719
+ const updated = { ...settings, env: nextEnv };
56720
+ const backupPath = backup2(filePath);
56721
+ try {
56722
+ fs112.writeFileSync(filePath, JSON.stringify(updated, null, 2) + "\n", { mode: 420 });
56723
+ } catch (err) {
56724
+ printError(
56725
+ `could not write ${filePath}: ${err instanceof Error ? err.message : String(err)}`
56726
+ );
56727
+ if (backupPath) printInfo("backup", `settings.json preserved at ${backupPath}`);
56728
+ process.exitCode = 1;
56729
+ return;
56730
+ }
56731
+ printSuccess(`question inbox ENABLED (${scope} scope) \u2014 default-ON restored`);
56732
+ printInfo("settings", filePath);
56733
+ if (backupPath) printInfo("backup", backupPath);
56734
+ if (!localBrokerBasePresent()) {
56735
+ printInfo(
56736
+ "note",
56737
+ "no ~/.olam/anthropic-base-url found \u2014 inbox stays inactive until cloud auth is set up (olam auth issue-anthropic-token)"
56738
+ );
56739
+ }
56740
+ }
56741
+ function doStatus(scope) {
56742
+ const filePath = settingsPathFor3(scope);
56743
+ const persisted = readPersistedOptOut(filePath);
56744
+ const brokerUrl = process.env.OLAM_QUESTION_BROKER_URL;
56745
+ const runtimeOptOut = process.env[INBOX_ENV_KEY];
56746
+ const basePresent = localBrokerBasePresent();
56747
+ printInfo("scope", `${scope} (${filePath})`);
56748
+ printInfo(
56749
+ `${INBOX_ENV_KEY} (persisted)`,
56750
+ persisted === void 0 ? "(unset)" : JSON.stringify(persisted)
56751
+ );
56752
+ if (runtimeOptOut !== void 0) {
56753
+ printInfo(`${INBOX_ENV_KEY} (current env)`, JSON.stringify(runtimeOptOut));
56754
+ }
56755
+ printInfo("~/.olam/anthropic-base-url", basePresent ? "present" : "absent");
56756
+ if (brokerUrl) printInfo("OLAM_QUESTION_BROKER_URL", "set (forces inbox ON)");
56757
+ let on;
56758
+ let reason;
56759
+ if (brokerUrl) {
56760
+ on = true;
56761
+ reason = "OLAM_QUESTION_BROKER_URL is set (explicit/CF override)";
56762
+ } else if (isOptOutValue(persisted)) {
56763
+ on = false;
56764
+ reason = `${INBOX_ENV_KEY}=${JSON.stringify(persisted)} opts out`;
56765
+ } else if (basePresent) {
56766
+ on = true;
56767
+ reason = "default-ON (anthropic-base-url present, not opted out)";
56768
+ } else {
56769
+ on = false;
56770
+ reason = "no broker base (~/.olam/anthropic-base-url absent) \u2192 passthrough";
56771
+ }
56772
+ if (on) {
56773
+ printInfo("inbox interception", `ON \u2014 ${reason}`);
56774
+ } else {
56775
+ printSuccess(`inbox interception: OFF \u2014 ${reason}`);
56776
+ }
56777
+ }
56778
+ function scopeOption(cmd) {
56779
+ return cmd.option(
56780
+ "--scope <scope>",
56781
+ "user (default; ~/.claude/settings.json) or project (<cwd>/.claude/settings.json)",
56782
+ "user"
56783
+ );
56784
+ }
56785
+ function registerInboxToggleCommands(inbox) {
56786
+ scopeOption(
56787
+ inbox.command("disable").description(
56788
+ "Stop routing AskUserQuestion to the mobile inbox; answer in the terminal instead"
56789
+ )
56790
+ ).action((opts) => doDisable(resolveScope(opts)));
56791
+ scopeOption(
56792
+ inbox.command("enable").description("Re-enable inbox routing for AskUserQuestion (restore default-ON)")
56793
+ ).action((opts) => doEnable(resolveScope(opts)));
56794
+ scopeOption(
56795
+ inbox.command("status").description("Show whether AskUserQuestion is routed to the inbox in this scope")
56796
+ ).action((opts) => doStatus(resolveScope(opts)));
56797
+ }
56798
+
56447
56799
  // src/commands/inbox/index.ts
56448
56800
  function registerInbox(program2) {
56449
56801
  const inbox = program2.command("inbox").description("Mobile question-inbox operations (route AskUserQuestion to phone)");
56450
56802
  registerInboxInstallHookCommand(inbox);
56803
+ registerInboxToggleCommands(inbox);
56451
56804
  }
56452
56805
 
56453
56806
  // src/commands/kg-build.ts
56454
56807
  init_storage_paths();
56455
56808
  init_workspace_name();
56456
- import * as fs117 from "node:fs";
56457
- import * as os63 from "node:os";
56458
- import * as path117 from "node:path";
56809
+ import * as fs118 from "node:fs";
56810
+ import * as os64 from "node:os";
56811
+ import * as path118 from "node:path";
56459
56812
 
56460
56813
  // ../core/dist/kg/kg-service-client.js
56461
56814
  var KG_SERVICE_PORT_DEFAULT = 9997;
@@ -56466,8 +56819,8 @@ function port() {
56466
56819
  const n = Number.parseInt(env, 10);
56467
56820
  return Number.isFinite(n) && n > 0 ? n : KG_SERVICE_PORT_DEFAULT;
56468
56821
  }
56469
- function url(path121) {
56470
- return `http://127.0.0.1:${port()}${path121}`;
56822
+ function url(path122) {
56823
+ return `http://127.0.0.1:${port()}${path122}`;
56471
56824
  }
56472
56825
  function kgServiceHealthUrl() {
56473
56826
  return url("/health");
@@ -56547,39 +56900,39 @@ init_storage_paths();
56547
56900
  init_workspace_name();
56548
56901
  init_kg_caps();
56549
56902
  init_output();
56550
- import fs112 from "node:fs";
56551
- import { homedir as homedir75 } from "node:os";
56552
- import path112 from "node:path";
56903
+ import fs113 from "node:fs";
56904
+ import { homedir as homedir76 } from "node:os";
56905
+ import path113 from "node:path";
56553
56906
  function olamHome6() {
56554
- return process.env.OLAM_HOME ?? path112.join(homedir75(), ".olam");
56907
+ return process.env.OLAM_HOME ?? path113.join(homedir76(), ".olam");
56555
56908
  }
56556
56909
  function kgRoot2() {
56557
- return path112.join(olamHome6(), "kg");
56910
+ return path113.join(olamHome6(), "kg");
56558
56911
  }
56559
56912
  function worldsRoot2() {
56560
- return path112.join(olamHome6(), "worlds");
56913
+ return path113.join(olamHome6(), "worlds");
56561
56914
  }
56562
56915
  function dirSizeBytes2(dir) {
56563
- if (!fs112.existsSync(dir)) return 0;
56916
+ if (!fs113.existsSync(dir)) return 0;
56564
56917
  let total = 0;
56565
56918
  const stack = [dir];
56566
56919
  while (stack.length > 0) {
56567
56920
  const cur = stack.pop();
56568
56921
  let entries;
56569
56922
  try {
56570
- entries = fs112.readdirSync(cur, { withFileTypes: true });
56923
+ entries = fs113.readdirSync(cur, { withFileTypes: true });
56571
56924
  } catch {
56572
56925
  continue;
56573
56926
  }
56574
56927
  for (const entry of entries) {
56575
- const full = path112.join(cur, entry.name);
56928
+ const full = path113.join(cur, entry.name);
56576
56929
  if (entry.isSymbolicLink()) continue;
56577
56930
  if (entry.isDirectory()) {
56578
56931
  stack.push(full);
56579
56932
  continue;
56580
56933
  }
56581
56934
  try {
56582
- total += fs112.statSync(full).size;
56935
+ total += fs113.statSync(full).size;
56583
56936
  } catch {
56584
56937
  }
56585
56938
  }
@@ -56593,10 +56946,10 @@ function formatBytes5(n) {
56593
56946
  return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
56594
56947
  }
56595
56948
  function readFreshness(workspace) {
56596
- const file = path112.join(kgPristinePath(workspace), "freshness.json");
56597
- if (!fs112.existsSync(file)) return null;
56949
+ const file = path113.join(kgPristinePath(workspace), "freshness.json");
56950
+ if (!fs113.existsSync(file)) return null;
56598
56951
  try {
56599
- const raw = JSON.parse(fs112.readFileSync(file, "utf-8"));
56952
+ const raw = JSON.parse(fs113.readFileSync(file, "utf-8"));
56600
56953
  if (raw && typeof raw === "object") return raw;
56601
56954
  return null;
56602
56955
  } catch {
@@ -56604,10 +56957,10 @@ function readFreshness(workspace) {
56604
56957
  }
56605
56958
  }
56606
56959
  function readOverlayNodeCount(graphifyOutDir) {
56607
- const graphPath = path112.join(graphifyOutDir, "graph.json");
56608
- if (!fs112.existsSync(graphPath)) return null;
56960
+ const graphPath = path113.join(graphifyOutDir, "graph.json");
56961
+ if (!fs113.existsSync(graphPath)) return null;
56609
56962
  try {
56610
- const raw = JSON.parse(fs112.readFileSync(graphPath, "utf-8"));
56963
+ const raw = JSON.parse(fs113.readFileSync(graphPath, "utf-8"));
56611
56964
  if (raw && typeof raw === "object") {
56612
56965
  const nodes = raw.nodes;
56613
56966
  if (Array.isArray(nodes)) return nodes.length;
@@ -56619,28 +56972,28 @@ function readOverlayNodeCount(graphifyOutDir) {
56619
56972
  }
56620
56973
  function listOverlays() {
56621
56974
  const root = worldsRoot2();
56622
- if (!fs112.existsSync(root)) return [];
56975
+ if (!fs113.existsSync(root)) return [];
56623
56976
  const records = [];
56624
56977
  let worldDirs;
56625
56978
  try {
56626
- worldDirs = fs112.readdirSync(root, { withFileTypes: true });
56979
+ worldDirs = fs113.readdirSync(root, { withFileTypes: true });
56627
56980
  } catch {
56628
56981
  return [];
56629
56982
  }
56630
56983
  for (const worldEntry of worldDirs) {
56631
56984
  if (!worldEntry.isDirectory()) continue;
56632
56985
  const worldId = worldEntry.name;
56633
- const worldDir = path112.join(root, worldId);
56986
+ const worldDir = path113.join(root, worldId);
56634
56987
  let cloneDirs;
56635
56988
  try {
56636
- cloneDirs = fs112.readdirSync(worldDir, { withFileTypes: true });
56989
+ cloneDirs = fs113.readdirSync(worldDir, { withFileTypes: true });
56637
56990
  } catch {
56638
56991
  continue;
56639
56992
  }
56640
56993
  for (const cloneEntry of cloneDirs) {
56641
56994
  if (!cloneEntry.isDirectory()) continue;
56642
- const graphifyOut = path112.join(worldDir, cloneEntry.name, "graphify-out");
56643
- if (!fs112.existsSync(graphifyOut)) continue;
56995
+ const graphifyOut = path113.join(worldDir, cloneEntry.name, "graphify-out");
56996
+ if (!fs113.existsSync(graphifyOut)) continue;
56644
56997
  records.push({
56645
56998
  world_id: worldId,
56646
56999
  clone_dir: cloneEntry.name,
@@ -56654,11 +57007,11 @@ function listOverlays() {
56654
57007
  }
56655
57008
  function listPristines(overlays) {
56656
57009
  const root = kgRoot2();
56657
- if (!fs112.existsSync(root)) return [];
57010
+ if (!fs113.existsSync(root)) return [];
56658
57011
  const records = [];
56659
57012
  let entries;
56660
57013
  try {
56661
- entries = fs112.readdirSync(root, { withFileTypes: true });
57014
+ entries = fs113.readdirSync(root, { withFileTypes: true });
56662
57015
  } catch {
56663
57016
  return [];
56664
57017
  }
@@ -56671,7 +57024,7 @@ function listPristines(overlays) {
56671
57024
  continue;
56672
57025
  }
56673
57026
  const fresh = readFreshness(workspace);
56674
- const graphifyOut = path112.join(kgPristinePath(workspace), "graphify-out");
57027
+ const graphifyOut = path113.join(kgPristinePath(workspace), "graphify-out");
56675
57028
  const size = dirSizeBytes2(graphifyOut);
56676
57029
  const worldCount = overlays.filter((o) => o.clone_dir === workspace).length;
56677
57030
  records.push({
@@ -56807,10 +57160,10 @@ init_storage_paths();
56807
57160
  init_workspace_name();
56808
57161
  init_output();
56809
57162
  import { spawn as spawn13 } from "node:child_process";
56810
- import fs113 from "node:fs";
56811
- import path113 from "node:path";
57163
+ import fs114 from "node:fs";
57164
+ import path114 from "node:path";
56812
57165
  function pidFilePath2(workspace) {
56813
- return path113.join(kgPristinePath(workspace), ".watch.pid");
57166
+ return path114.join(kgPristinePath(workspace), ".watch.pid");
56814
57167
  }
56815
57168
  function isPidAlive3(pid) {
56816
57169
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -56825,39 +57178,39 @@ function isPidAlive3(pid) {
56825
57178
  }
56826
57179
  function readAndClassifyPid(workspace) {
56827
57180
  const file = pidFilePath2(workspace);
56828
- if (!fs113.existsSync(file)) return { status: "no-pidfile", pid: null };
57181
+ if (!fs114.existsSync(file)) return { status: "no-pidfile", pid: null };
56829
57182
  let pid;
56830
57183
  try {
56831
- const raw = fs113.readFileSync(file, "utf-8").trim();
57184
+ const raw = fs114.readFileSync(file, "utf-8").trim();
56832
57185
  pid = Number.parseInt(raw, 10);
56833
57186
  } catch {
56834
- fs113.rmSync(file, { force: true });
57187
+ fs114.rmSync(file, { force: true });
56835
57188
  return { status: "stale-reclaimed", pid: null };
56836
57189
  }
56837
57190
  if (!Number.isInteger(pid) || pid <= 0) {
56838
- fs113.rmSync(file, { force: true });
57191
+ fs114.rmSync(file, { force: true });
56839
57192
  return { status: "stale-reclaimed", pid: null };
56840
57193
  }
56841
57194
  if (isPidAlive3(pid)) return { status: "active", pid };
56842
- fs113.rmSync(file, { force: true });
57195
+ fs114.rmSync(file, { force: true });
56843
57196
  return { status: "stale-reclaimed", pid: null };
56844
57197
  }
56845
57198
  function writePidFile2(workspace, pid) {
56846
57199
  const file = pidFilePath2(workspace);
56847
- const dir = path113.dirname(file);
56848
- fs113.mkdirSync(dir, { recursive: true });
56849
- fs113.writeFileSync(file, String(pid), { encoding: "utf-8" });
57200
+ const dir = path114.dirname(file);
57201
+ fs114.mkdirSync(dir, { recursive: true });
57202
+ fs114.writeFileSync(file, String(pid), { encoding: "utf-8" });
56850
57203
  }
56851
57204
  function removePidFile(workspace) {
56852
57205
  const file = pidFilePath2(workspace);
56853
57206
  try {
56854
- fs113.rmSync(file, { force: true });
57207
+ fs114.rmSync(file, { force: true });
56855
57208
  } catch {
56856
57209
  }
56857
57210
  }
56858
57211
  async function runKgWatch(workspaceArg, opts, deps = {}) {
56859
57212
  const cwd = deps.cwd ?? opts.cwd ?? process.cwd();
56860
- const name = workspaceArg ?? path113.basename(cwd).toLowerCase();
57213
+ const name = workspaceArg ?? path114.basename(cwd).toLowerCase();
56861
57214
  try {
56862
57215
  validateWorkspaceName(name);
56863
57216
  } catch (err) {
@@ -56865,7 +57218,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
56865
57218
  return { exitCode: 1, pidWritten: false };
56866
57219
  }
56867
57220
  const pristinePath = kgPristinePath(name);
56868
- const graphPath = path113.join(pristinePath, "graphify-out", "graph.json");
57221
+ const graphPath = path114.join(pristinePath, "graphify-out", "graph.json");
56869
57222
  const pidState = readAndClassifyPid(name);
56870
57223
  if (pidState.status === "active") {
56871
57224
  printError(
@@ -57178,33 +57531,33 @@ function registerKgDoctorCommand(kg) {
57178
57531
  init_merge_settings();
57179
57532
  init_hook_template2();
57180
57533
  init_output();
57181
- import * as fs114 from "node:fs";
57182
- import * as path114 from "node:path";
57183
- import * as os60 from "node:os";
57534
+ import * as fs115 from "node:fs";
57535
+ import * as path115 from "node:path";
57536
+ import * as os61 from "node:os";
57184
57537
  import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
57185
57538
  function settingsPathFor4(scope) {
57186
57539
  if (scope === "user") {
57187
- return path114.join(os60.homedir(), ".claude", "settings.json");
57540
+ return path115.join(os61.homedir(), ".claude", "settings.json");
57188
57541
  }
57189
- return path114.join(process.cwd(), ".claude", "settings.json");
57542
+ return path115.join(process.cwd(), ".claude", "settings.json");
57190
57543
  }
57191
57544
  function backup3(filePath) {
57192
- if (!fs114.existsSync(filePath)) return null;
57545
+ if (!fs115.existsSync(filePath)) return null;
57193
57546
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
57194
57547
  const backupPath = `${filePath}.olam-bak.${ts}`;
57195
- fs114.copyFileSync(filePath, backupPath);
57548
+ fs115.copyFileSync(filePath, backupPath);
57196
57549
  return backupPath;
57197
57550
  }
57198
57551
  var HERMES_HOOK_MATCHERS = ["terminal", "bash", "shell", "search_files", "grep", "ripgrep"];
57199
57552
  function hermesConfigPath2() {
57200
- return path114.join(os60.homedir(), ".hermes", "config.yaml");
57553
+ return path115.join(os61.homedir(), ".hermes", "config.yaml");
57201
57554
  }
57202
57555
  function hermesHooksDir() {
57203
- return path114.join(os60.homedir(), ".hermes", "hooks");
57556
+ return path115.join(os61.homedir(), ".hermes", "hooks");
57204
57557
  }
57205
57558
  function patchHermesConfigForHook(action) {
57206
57559
  const configPath = hermesConfigPath2();
57207
- const raw = fs114.readFileSync(configPath, "utf-8");
57560
+ const raw = fs115.readFileSync(configPath, "utf-8");
57208
57561
  const config = yamlParse3(raw);
57209
57562
  const hooks = config["hooks"] ?? {};
57210
57563
  const preToolCall = Array.isArray(hooks["pre_tool_call"]) ? hooks["pre_tool_call"] : [];
@@ -57213,7 +57566,7 @@ function patchHermesConfigForHook(action) {
57213
57566
  (e) => typeof e["command"] === "string" && e["command"].includes(HERMES_KG_HOOK_SENTINEL)
57214
57567
  );
57215
57568
  if (alreadyPresent) return "already-present";
57216
- const hookScriptPath = path114.join(hermesHooksDir(), "kg-first.sh");
57569
+ const hookScriptPath = path115.join(hermesHooksDir(), "kg-first.sh");
57217
57570
  const entry = {
57218
57571
  matcher: HERMES_HOOK_MATCHERS.join("|"),
57219
57572
  command: `${hookScriptPath} # ${HERMES_KG_HOOK_SENTINEL}`
@@ -57223,7 +57576,7 @@ function patchHermesConfigForHook(action) {
57223
57576
  hooks: { ...hooks, pre_tool_call: [...preToolCall, entry] },
57224
57577
  hooks_auto_accept: true
57225
57578
  };
57226
- fs114.writeFileSync(configPath, yamlStringify4(updated2), "utf-8");
57579
+ fs115.writeFileSync(configPath, yamlStringify4(updated2), "utf-8");
57227
57580
  return "patched";
57228
57581
  }
57229
57582
  const filtered = preToolCall.filter(
@@ -57234,12 +57587,12 @@ function patchHermesConfigForHook(action) {
57234
57587
  ...config,
57235
57588
  hooks: { ...hooks, pre_tool_call: filtered }
57236
57589
  };
57237
- fs114.writeFileSync(configPath, yamlStringify4(updated), "utf-8");
57590
+ fs115.writeFileSync(configPath, yamlStringify4(updated), "utf-8");
57238
57591
  return "patched";
57239
57592
  }
57240
57593
  function doInstallForHermes() {
57241
57594
  const configPath = hermesConfigPath2();
57242
- if (!fs114.existsSync(configPath)) {
57595
+ if (!fs115.existsSync(configPath)) {
57243
57596
  printError(`~/.hermes/config.yaml not found at ${configPath}`);
57244
57597
  printInfo("remedy", "Install Hermes first, then re-run `olam kg install-hook --for hermes`");
57245
57598
  process.exitCode = 1;
@@ -57267,17 +57620,17 @@ function doInstallForHermes() {
57267
57620
  function doUninstallForHermes() {
57268
57621
  const configPath = hermesConfigPath2();
57269
57622
  const hooksDir = hermesHooksDir();
57270
- const hookScriptPath = path114.join(hooksDir, "kg-first.sh");
57623
+ const hookScriptPath = path115.join(hooksDir, "kg-first.sh");
57271
57624
  let scriptRemoved = false;
57272
- if (fs114.existsSync(hookScriptPath)) {
57273
- const content = fs114.readFileSync(hookScriptPath, "utf-8");
57625
+ if (fs115.existsSync(hookScriptPath)) {
57626
+ const content = fs115.readFileSync(hookScriptPath, "utf-8");
57274
57627
  if (content.includes(HERMES_KG_HOOK_SENTINEL)) {
57275
- fs114.unlinkSync(hookScriptPath);
57628
+ fs115.unlinkSync(hookScriptPath);
57276
57629
  scriptRemoved = true;
57277
57630
  }
57278
57631
  }
57279
57632
  let configPatched = false;
57280
- if (fs114.existsSync(configPath)) {
57633
+ if (fs115.existsSync(configPath)) {
57281
57634
  const result = patchHermesConfigForHook("uninstall");
57282
57635
  configPatched = result === "patched";
57283
57636
  }
@@ -57302,9 +57655,9 @@ function registerKgInstallHookCommand(kg) {
57302
57655
  const scope = opts.scope === "user" ? "user" : "project";
57303
57656
  const filePath = settingsPathFor4(scope);
57304
57657
  try {
57305
- fs114.mkdirSync(path114.dirname(filePath), { recursive: true });
57658
+ fs115.mkdirSync(path115.dirname(filePath), { recursive: true });
57306
57659
  } catch (err) {
57307
- printError(`could not create ${path114.dirname(filePath)}: ${err instanceof Error ? err.message : String(err)}`);
57660
+ printError(`could not create ${path115.dirname(filePath)}: ${err instanceof Error ? err.message : String(err)}`);
57308
57661
  process.exitCode = 1;
57309
57662
  return;
57310
57663
  }
@@ -57318,6 +57671,14 @@ function registerKgInstallHookCommand(kg) {
57318
57671
  entry: buildHookMatcherEntry({ flavor: "host" })
57319
57672
  }
57320
57673
  });
57674
+ mergeHomeSettingsJson(filePath, {
57675
+ ensureHook: {
57676
+ stage: "UserPromptSubmit",
57677
+ sentinel: KG_HOOK_PROMPT_SENTINEL,
57678
+ staleSentinelPrefix: KG_HOOK_PROMPT_SENTINEL_PREFIX,
57679
+ entry: buildPromptHookMatcherEntry({ flavor: "host" })
57680
+ }
57681
+ });
57321
57682
  switch (result.status) {
57322
57683
  case "installed":
57323
57684
  printSuccess(`kg-service hook installed (${scope} scope)`);
@@ -57329,7 +57690,7 @@ function registerKgInstallHookCommand(kg) {
57329
57690
  printInfo("kg-service hook", `already installed at ${filePath}`);
57330
57691
  if (backupPath) {
57331
57692
  try {
57332
- fs114.unlinkSync(backupPath);
57693
+ fs115.unlinkSync(backupPath);
57333
57694
  } catch {
57334
57695
  }
57335
57696
  }
@@ -57349,22 +57710,22 @@ function registerKgInstallHookCommand(kg) {
57349
57710
  // src/commands/kg-uninstall-hook.ts
57350
57711
  init_hook_template2();
57351
57712
  init_output();
57352
- import * as fs115 from "node:fs";
57353
- import * as path115 from "node:path";
57354
- import * as os61 from "node:os";
57713
+ import * as fs116 from "node:fs";
57714
+ import * as path116 from "node:path";
57715
+ import * as os62 from "node:os";
57355
57716
  function settingsPathFor5(scope) {
57356
57717
  if (scope === "user") {
57357
- return path115.join(os61.homedir(), ".claude", "settings.json");
57718
+ return path116.join(os62.homedir(), ".claude", "settings.json");
57358
57719
  }
57359
- return path115.join(process.cwd(), ".claude", "settings.json");
57720
+ return path116.join(process.cwd(), ".claude", "settings.json");
57360
57721
  }
57361
- function dropSentinel(matchers) {
57722
+ function dropSentinel(matchers, sentinelPrefix) {
57362
57723
  let changed = false;
57363
57724
  const out = [];
57364
57725
  for (const matcher of matchers) {
57365
57726
  const innerHooks = matcher.hooks ?? [];
57366
57727
  const keptInner = innerHooks.filter((h) => {
57367
- if (typeof h.command === "string" && h.command.includes(KG_HOOK_SENTINEL_PREFIX)) {
57728
+ if (typeof h.command === "string" && h.command.includes(sentinelPrefix)) {
57368
57729
  changed = true;
57369
57730
  return false;
57370
57731
  }
@@ -57386,13 +57747,13 @@ function registerKgUninstallHookCommand(kg) {
57386
57747
  kg.command("uninstall-hook").description("Remove kg-service PreToolUse hook from .claude/settings.json (sentinel-matched; surgical)").option("--scope <scope>", "project (default) or user", "project").action((opts) => {
57387
57748
  const scope = opts.scope === "user" ? "user" : "project";
57388
57749
  const filePath = settingsPathFor5(scope);
57389
- if (!fs115.existsSync(filePath)) {
57750
+ if (!fs116.existsSync(filePath)) {
57390
57751
  printInfo("kg-service hook", `no settings.json at ${filePath} \u2014 nothing to remove`);
57391
57752
  return;
57392
57753
  }
57393
57754
  let settings;
57394
57755
  try {
57395
- const raw = fs115.readFileSync(filePath, "utf-8");
57756
+ const raw = fs116.readFileSync(filePath, "utf-8");
57396
57757
  settings = raw.trim() ? JSON.parse(raw) : {};
57397
57758
  } catch (err) {
57398
57759
  printError(`could not parse ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -57400,38 +57761,36 @@ function registerKgUninstallHookCommand(kg) {
57400
57761
  return;
57401
57762
  }
57402
57763
  const preToolUse = settings.hooks?.PreToolUse;
57403
- if (!Array.isArray(preToolUse) || preToolUse.length === 0) {
57404
- printInfo("kg-service hook", `no PreToolUse hooks at ${filePath} \u2014 nothing to remove`);
57764
+ const promptSubmit = settings.hooks?.UserPromptSubmit;
57765
+ const hasPre = Array.isArray(preToolUse) && preToolUse.length > 0;
57766
+ const hasPrompt = Array.isArray(promptSubmit) && promptSubmit.length > 0;
57767
+ if (!hasPre && !hasPrompt) {
57768
+ printInfo("kg-service hook", `no kg hooks at ${filePath} \u2014 nothing to remove`);
57405
57769
  return;
57406
57770
  }
57407
- const { matchers, changed } = dropSentinel(preToolUse);
57408
- if (!changed) {
57771
+ const preResult = hasPre ? dropSentinel(preToolUse, KG_HOOK_SENTINEL_PREFIX) : { matchers: [], changed: false };
57772
+ const promptResult = hasPrompt ? dropSentinel(promptSubmit, KG_HOOK_PROMPT_SENTINEL_PREFIX) : { matchers: [], changed: false };
57773
+ if (!preResult.changed && !promptResult.changed) {
57409
57774
  printInfo("kg-service hook", `not found in ${filePath} \u2014 already uninstalled`);
57410
57775
  return;
57411
57776
  }
57412
57777
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
57413
57778
  const backupPath = `${filePath}.olam-bak.${ts}`;
57414
57779
  try {
57415
- fs115.copyFileSync(filePath, backupPath);
57780
+ fs116.copyFileSync(filePath, backupPath);
57416
57781
  } catch {
57417
57782
  }
57418
- const next = {
57419
- ...settings,
57420
- hooks: {
57421
- ...settings.hooks,
57422
- PreToolUse: matchers
57423
- }
57424
- };
57425
- if (matchers.length === 0) {
57426
- const otherStages = Object.keys(next.hooks ?? {}).filter((k) => k !== "PreToolUse");
57427
- if (otherStages.length === 0) {
57428
- delete next.hooks;
57429
- } else {
57430
- delete next.hooks.PreToolUse;
57431
- }
57783
+ const nextHooks = { ...settings.hooks };
57784
+ if (hasPre) nextHooks.PreToolUse = preResult.matchers;
57785
+ if (hasPrompt) nextHooks.UserPromptSubmit = promptResult.matchers;
57786
+ if (hasPre && preResult.matchers.length === 0) delete nextHooks.PreToolUse;
57787
+ if (hasPrompt && promptResult.matchers.length === 0) delete nextHooks.UserPromptSubmit;
57788
+ const next = { ...settings, hooks: nextHooks };
57789
+ if (Object.keys(nextHooks).length === 0) {
57790
+ delete next.hooks;
57432
57791
  }
57433
57792
  try {
57434
- fs115.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
57793
+ fs116.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
57435
57794
  printSuccess(`kg-service hook removed from ${filePath}`);
57436
57795
  printInfo("backup", backupPath);
57437
57796
  } catch (err) {
@@ -57504,15 +57863,15 @@ function registerKgSavingsCommand(kg) {
57504
57863
 
57505
57864
  // src/commands/kg-mirror.ts
57506
57865
  init_output();
57507
- import * as fs116 from "node:fs";
57508
- import * as os62 from "node:os";
57509
- import * as path116 from "node:path";
57866
+ import * as fs117 from "node:fs";
57867
+ import * as os63 from "node:os";
57868
+ import * as path117 from "node:path";
57510
57869
  function readEnvOrFile(envVar, fileName) {
57511
57870
  const fromEnv = process.env[envVar];
57512
57871
  if (fromEnv && fromEnv.length > 0) return fromEnv.trim();
57513
57872
  try {
57514
- const file = path116.join(os62.homedir(), ".olam", fileName);
57515
- const content = fs116.readFileSync(file, "utf-8").trim();
57873
+ const file = path117.join(os63.homedir(), ".olam", fileName);
57874
+ const content = fs117.readFileSync(file, "utf-8").trim();
57516
57875
  if (content.length > 0) return content;
57517
57876
  } catch {
57518
57877
  }
@@ -57774,14 +58133,14 @@ async function runKgMirrorGraph(symbol, options = {}) {
57774
58133
  function guessGitUrl(workspace) {
57775
58134
  try {
57776
58135
  const { spawnSync: spawnSync36 } = __require("node:child_process");
57777
- const cwd = path116.resolve(process.cwd());
58136
+ const cwd = path117.resolve(process.cwd());
57778
58137
  const candidates2 = [
57779
58138
  cwd,
57780
- path116.join(path116.dirname(cwd), workspace),
57781
- path116.join(os62.homedir(), "Projects", workspace)
58139
+ path117.join(path117.dirname(cwd), workspace),
58140
+ path117.join(os63.homedir(), "Projects", workspace)
57782
58141
  ];
57783
58142
  for (const dir of candidates2) {
57784
- if (!fs116.existsSync(path116.join(dir, ".git"))) continue;
58143
+ if (!fs117.existsSync(path117.join(dir, ".git"))) continue;
57785
58144
  const r = spawnSync36("git", ["-C", dir, "remote", "get-url", "origin"], {
57786
58145
  encoding: "utf-8"
57787
58146
  });
@@ -57819,12 +58178,12 @@ function registerKgMirrorCommand(kg) {
57819
58178
  // src/commands/kg-connect.ts
57820
58179
  init_memory_secret();
57821
58180
  init_connect_url();
57822
- import { existsSync as existsSync129, readFileSync as readFileSync115 } from "node:fs";
57823
- import { homedir as homedir79 } from "node:os";
57824
- import { join as join133 } from "node:path";
58181
+ import { existsSync as existsSync130, readFileSync as readFileSync116 } from "node:fs";
58182
+ import { homedir as homedir80 } from "node:os";
58183
+ import { join as join134 } from "node:path";
57825
58184
  init_output();
57826
58185
  function olamFile(name) {
57827
- return join133(homedir79(), ".olam", name);
58186
+ return join134(homedir80(), ".olam", name);
57828
58187
  }
57829
58188
  function safeUrl3(url2) {
57830
58189
  try {
@@ -57845,7 +58204,7 @@ function registerKgConnectCommand(kg) {
57845
58204
  return;
57846
58205
  }
57847
58206
  const urlPath = olamFile("kg-proxy-url");
57848
- const previous = existsSync129(urlPath) ? readFileSync115(urlPath, "utf8").trim() : "";
58207
+ const previous = existsSync130(urlPath) ? readFileSync116(urlPath, "utf8").trim() : "";
57849
58208
  writeSecretAtPath(urlPath, classifierUrl);
57850
58209
  if (opts.bearer) writeSecretAtPath(olamFile("kg-proxy-bearer"), opts.bearer);
57851
58210
  if (opts.builderUrl) {
@@ -57880,8 +58239,8 @@ function registerKgConnectCommand(kg) {
57880
58239
 
57881
58240
  // src/commands/kg-build.ts
57882
58241
  function readQueueFromDisk(queuePath) {
57883
- if (!fs117.existsSync(queuePath)) return [];
57884
- const raw = fs117.readFileSync(queuePath, "utf-8");
58242
+ if (!fs118.existsSync(queuePath)) return [];
58243
+ const raw = fs118.readFileSync(queuePath, "utf-8");
57885
58244
  const entries = [];
57886
58245
  for (const line of raw.split("\n")) {
57887
58246
  const t = line.trim();
@@ -57894,14 +58253,14 @@ function readQueueFromDisk(queuePath) {
57894
58253
  return entries;
57895
58254
  }
57896
58255
  function writeQueueToDisk(queuePath, entries) {
57897
- fs117.mkdirSync(path117.dirname(queuePath), { recursive: true });
58256
+ fs118.mkdirSync(path118.dirname(queuePath), { recursive: true });
57898
58257
  const content = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length > 0 ? "\n" : "");
57899
- fs117.writeFileSync(queuePath, content, "utf-8");
58258
+ fs118.writeFileSync(queuePath, content, "utf-8");
57900
58259
  }
57901
58260
  async function runKgBuildPending(opts = {}) {
57902
58261
  const queuePath = opts.queuePath ?? (() => {
57903
- const stateDir2 = process.env["OLAM_STATE_DIR"] ?? path117.join(os63.homedir(), ".olam", "state");
57904
- return path117.join(stateDir2, "kg-pending.jsonl");
58262
+ const stateDir2 = process.env["OLAM_STATE_DIR"] ?? path118.join(os64.homedir(), ".olam", "state");
58263
+ return path118.join(stateDir2, "kg-pending.jsonl");
57905
58264
  })();
57906
58265
  const readQueue2 = opts.readQueueFn ?? readQueueFromDisk;
57907
58266
  const writeQueue2 = opts.writeQueueFn ?? writeQueueToDisk;
@@ -57917,7 +58276,7 @@ async function runKgBuildPending(opts = {}) {
57917
58276
  const remaining = [];
57918
58277
  for (const entry of deduped) {
57919
58278
  const repoPath = entry.path;
57920
- const workspaceName = path117.basename(repoPath).toLowerCase();
58279
+ const workspaceName = path118.basename(repoPath).toLowerCase();
57921
58280
  printInfo("kg build --pending", `building ${repoPath} (workspace=${workspaceName})`);
57922
58281
  let containerPath;
57923
58282
  try {
@@ -57955,20 +58314,20 @@ async function runKgBuildPending(opts = {}) {
57955
58314
  }
57956
58315
  function resolveWorkspace(arg) {
57957
58316
  const cwd = process.cwd();
57958
- const name = arg ?? path117.basename(cwd).toLowerCase();
58317
+ const name = arg ?? path118.basename(cwd).toLowerCase();
57959
58318
  validateWorkspaceName(name);
57960
58319
  return { name, sourcePath: cwd };
57961
58320
  }
57962
58321
  function toContainerPath(hostPath) {
57963
- const home = os63.homedir();
57964
- const resolved = path117.resolve(hostPath);
57965
- if (!resolved.startsWith(home + path117.sep) && resolved !== home) {
58322
+ const home = os64.homedir();
58323
+ const resolved = path118.resolve(hostPath);
58324
+ if (!resolved.startsWith(home + path118.sep) && resolved !== home) {
57966
58325
  throw new Error(
57967
58326
  `source path "${resolved}" is not under $HOME (${home}). kg-service can only build repos that live under your home dir (it bind-mounts $HOME:/host-home:ro at start). Move the repo or set OLAM_HOME if you need a different root.`
57968
58327
  );
57969
58328
  }
57970
- const rel = path117.relative(home, resolved);
57971
- return rel === "" ? "/host-home" : path117.posix.join("/host-home", rel.split(path117.sep).join("/"));
58329
+ const rel = path118.relative(home, resolved);
58330
+ return rel === "" ? "/host-home" : path118.posix.join("/host-home", rel.split(path118.sep).join("/"));
57972
58331
  }
57973
58332
  async function runKgBuild(workspaceArg, options = {}) {
57974
58333
  let workspace;
@@ -57986,7 +58345,7 @@ async function runKgBuild(workspaceArg, options = {}) {
57986
58345
  return { exitCode: 2 };
57987
58346
  }
57988
58347
  const outDir = kgPristinePath(workspace.name);
57989
- fs117.mkdirSync(outDir, { recursive: true });
58348
+ fs118.mkdirSync(outDir, { recursive: true });
57990
58349
  const human = !options.json;
57991
58350
  if (human) {
57992
58351
  printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
@@ -58019,12 +58378,12 @@ async function runKgBuild(workspaceArg, options = {}) {
58019
58378
  workspace: workspace.name,
58020
58379
  graphify_path: "container"
58021
58380
  };
58022
- fs117.writeFileSync(
58023
- path117.join(outDir, "freshness.json"),
58381
+ fs118.writeFileSync(
58382
+ path118.join(outDir, "freshness.json"),
58024
58383
  JSON.stringify(freshness, null, 2) + "\n",
58025
58384
  "utf-8"
58026
58385
  );
58027
- const finalOut = path117.join(outDir, "graphify-out");
58386
+ const finalOut = path118.join(outDir, "graphify-out");
58028
58387
  if (options.json) {
58029
58388
  process.stdout.write(JSON.stringify(freshness) + "\n");
58030
58389
  } else {
@@ -58068,12 +58427,12 @@ function registerKg(program2) {
58068
58427
  // src/commands/flywheel/emit-breadcrumb.ts
58069
58428
  init_file_lock();
58070
58429
  import { mkdirSync as mkdirSync77, appendFileSync as appendFileSync6 } from "node:fs";
58071
- import { homedir as homedir81 } from "node:os";
58072
- import { dirname as dirname74, join as join135 } from "node:path";
58430
+ import { homedir as homedir82 } from "node:os";
58431
+ import { dirname as dirname74, join as join136 } from "node:path";
58073
58432
  import { randomUUID as randomUUID4 } from "node:crypto";
58074
58433
  var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info", "warn"]);
58075
58434
  var PROMPT_FEEDING_FIELDS = ["extracted_pattern", "severity", "affected_persona", "proposed_edit"];
58076
- var BREADCRUMBS_BASE = join135(homedir81(), ".local", "share", "claude", "breadcrumbs");
58435
+ var BREADCRUMBS_BASE = join136(homedir82(), ".local", "share", "claude", "breadcrumbs");
58077
58436
  var LOCK_FILENAME = ".flywheel-emit.lock";
58078
58437
  function buildRecord(opts) {
58079
58438
  const rec = {
@@ -58120,7 +58479,7 @@ function validatePromptFeeding(rec) {
58120
58479
  }
58121
58480
  function destPath(projectSlug) {
58122
58481
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
58123
- return join135(BREADCRUMBS_BASE, projectSlug, `${today}.jsonl`);
58482
+ return join136(BREADCRUMBS_BASE, projectSlug, `${today}.jsonl`);
58124
58483
  }
58125
58484
  async function emitBreadcrumb(opts) {
58126
58485
  if (!VALID_SEVERITIES.has(opts.severity)) {
@@ -58139,14 +58498,14 @@ async function emitBreadcrumb(opts) {
58139
58498
  );
58140
58499
  process.exit(2);
58141
58500
  }
58142
- const path121 = destPath(rec.project_slug);
58143
- const lockDir = dirname74(path121);
58501
+ const path122 = destPath(rec.project_slug);
58502
+ const lockDir = dirname74(path122);
58144
58503
  mkdirSync77(lockDir, { recursive: true });
58145
58504
  const line = JSON.stringify(rec) + "\n";
58146
58505
  await withFileLock(
58147
58506
  lockDir,
58148
58507
  () => {
58149
- appendFileSync6(path121, line, "utf8");
58508
+ appendFileSync6(path122, line, "utf8");
58150
58509
  },
58151
58510
  {
58152
58511
  lockFilename: LOCK_FILENAME,
@@ -58162,7 +58521,7 @@ async function emitBreadcrumb(opts) {
58162
58521
  acquireTimeoutMs: 2e4
58163
58522
  }
58164
58523
  );
58165
- process.stdout.write(`[K7-emit] ${path121}: ${rec.extracted_pattern} (${rec.severity})
58524
+ process.stdout.write(`[K7-emit] ${path122}: ${rec.extracted_pattern} (${rec.severity})
58166
58525
  `);
58167
58526
  }
58168
58527
  function registerFlywheelEmitBreadcrumb(parent) {
@@ -58227,7 +58586,7 @@ function registerFlywheelK5Score(parent) {
58227
58586
  }
58228
58587
 
58229
58588
  // src/commands/flywheel/k5-validate.ts
58230
- import { readFileSync as readFileSync117, statSync as statSync33 } from "node:fs";
58589
+ import { readFileSync as readFileSync118, statSync as statSync33 } from "node:fs";
58231
58590
  import { parse as parseYAML } from "yaml";
58232
58591
  var K5_DIMS = ["direction", "approach", "open_questions", "constraints", "reuse"];
58233
58592
  var MAX_PLAN_BYTES = 1048576;
@@ -58285,9 +58644,9 @@ function validateK5ScoredAt(scoredAt) {
58285
58644
  }
58286
58645
  return null;
58287
58646
  }
58288
- function validatePlan(path121) {
58647
+ function validatePlan(path122) {
58289
58648
  const emptyJson = (status2, errors2) => ({
58290
- path: path121,
58649
+ path: path122,
58291
58650
  status: status2,
58292
58651
  errors: errors2,
58293
58652
  k5_scores: null,
@@ -58298,33 +58657,33 @@ function validatePlan(path121) {
58298
58657
  });
58299
58658
  let stat;
58300
58659
  try {
58301
- stat = statSync33(path121);
58660
+ stat = statSync33(path122);
58302
58661
  } catch (err) {
58303
- const m = `FAIL cannot stat ${path121}: ${err instanceof Error ? err.message : "unknown"}`;
58662
+ const m = `FAIL cannot stat ${path122}: ${err instanceof Error ? err.message : "unknown"}`;
58304
58663
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58305
58664
  }
58306
58665
  if (stat.size > MAX_PLAN_BYTES) {
58307
- const m = `FAIL ${path121}: plan file exceeds 1MB (${stat.size} bytes); refusing to parse`;
58666
+ const m = `FAIL ${path122}: plan file exceeds 1MB (${stat.size} bytes); refusing to parse`;
58308
58667
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58309
58668
  }
58310
58669
  let text;
58311
58670
  try {
58312
- text = readFileSync117(path121, "utf8");
58671
+ text = readFileSync118(path122, "utf8");
58313
58672
  } catch (err) {
58314
- const m = `FAIL cannot read ${path121}: ${err instanceof Error ? err.message : "unknown"}`;
58673
+ const m = `FAIL cannot read ${path122}: ${err instanceof Error ? err.message : "unknown"}`;
58315
58674
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58316
58675
  }
58317
58676
  if (!text.replace(/^/, "").startsWith("---")) {
58318
- const m = `FAIL ${path121}: no YAML frontmatter (missing opening --- marker)`;
58677
+ const m = `FAIL ${path122}: no YAML frontmatter (missing opening --- marker)`;
58319
58678
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58320
58679
  }
58321
58680
  if (!text.includes("\n---", 3)) {
58322
- const m = `FAIL ${path121}: frontmatter block never closed (missing closing --- marker)`;
58681
+ const m = `FAIL ${path122}: frontmatter block never closed (missing closing --- marker)`;
58323
58682
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58324
58683
  }
58325
58684
  const fm = extractFrontmatter(text);
58326
58685
  if (fm === null) {
58327
- const m = `FAIL ${path121}: frontmatter could not be parsed as YAML`;
58686
+ const m = `FAIL ${path122}: frontmatter could not be parsed as YAML`;
58328
58687
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58329
58688
  }
58330
58689
  const hasScores = "k5_scores" in fm;
@@ -58334,7 +58693,7 @@ function validatePlan(path121) {
58334
58693
  if (!hasScores && !hasBoost && !hasComposite && !hasScoredAt) {
58335
58694
  return {
58336
58695
  ok: true,
58337
- message: `PASS ${path121}: k5_scores absent (acceptable \u2014 legacy plan)`,
58696
+ message: `PASS ${path122}: k5_scores absent (acceptable \u2014 legacy plan)`,
58338
58697
  json: emptyJson("absent", [])
58339
58698
  };
58340
58699
  }
@@ -58358,12 +58717,12 @@ function validatePlan(path121) {
58358
58717
  if (errors.length > 0) {
58359
58718
  return {
58360
58719
  ok: false,
58361
- message: `FAIL ${path121}: ${errors.join("; ")}`,
58720
+ message: `FAIL ${path122}: ${errors.join("; ")}`,
58362
58721
  json: emptyJson("fail", errors)
58363
58722
  };
58364
58723
  }
58365
58724
  const json = emptyJson("pass", []);
58366
- const lines = [`PASS ${path121}: k5_scores valid`];
58725
+ const lines = [`PASS ${path122}: k5_scores valid`];
58367
58726
  if (hasScores && typeof fm.k5_scores === "object" && fm.k5_scores !== null) {
58368
58727
  const scoresObj = fm.k5_scores;
58369
58728
  const parsed = {};
@@ -58433,7 +58792,7 @@ function registerFlywheelK5Validate(parent) {
58433
58792
  }
58434
58793
 
58435
58794
  // src/commands/flywheel/k10-measure.ts
58436
- import { readFileSync as readFileSync118 } from "node:fs";
58795
+ import { readFileSync as readFileSync119 } from "node:fs";
58437
58796
 
58438
58797
  // ../core/dist/lib/k10-budget.js
58439
58798
  var K10_TOKEN_CAP = 5500;
@@ -58490,7 +58849,7 @@ function registerFlywheelK10Measure(parent) {
58490
58849
  parent.command("k10-measure").description("Measure K10 token budget for upstream + optional overlay; emit PASS/REJECT verdict").requiredOption("--upstream <path>", "path to upstream file (e.g. persona prompt)").option("--overlay <path>", "path to overlay file (omit if none \u2014 PASS without enforcement)").option("--json", "emit verdict as JSON instead of human-readable").action((opts) => {
58491
58850
  let upstreamText;
58492
58851
  try {
58493
- upstreamText = readFileSync118(opts.upstream, "utf8");
58852
+ upstreamText = readFileSync119(opts.upstream, "utf8");
58494
58853
  } catch (err) {
58495
58854
  process.stderr.write(
58496
58855
  `[k10-measure-error] cannot read upstream ${opts.upstream}: ${err instanceof Error ? err.message : "unknown"}
@@ -58502,7 +58861,7 @@ function registerFlywheelK10Measure(parent) {
58502
58861
  let overlayTokens = null;
58503
58862
  if (opts.overlay !== void 0) {
58504
58863
  try {
58505
- const overlayText = readFileSync118(opts.overlay, "utf8");
58864
+ const overlayText = readFileSync119(opts.overlay, "utf8");
58506
58865
  overlayTokens = tokensFromText(overlayText);
58507
58866
  } catch (err) {
58508
58867
  process.stderr.write(
@@ -58536,13 +58895,13 @@ function registerFlywheelK10Measure(parent) {
58536
58895
  }
58537
58896
 
58538
58897
  // src/commands/flywheel/check-persona-skeleton.ts
58539
- import { existsSync as existsSync131, readFileSync as readFileSync119, statSync as statSync34 } from "node:fs";
58540
- import { homedir as homedir82 } from "node:os";
58541
- import { basename as basename14, join as join136 } from "node:path";
58898
+ import { existsSync as existsSync132, readFileSync as readFileSync120, statSync as statSync34 } from "node:fs";
58899
+ import { homedir as homedir83 } from "node:os";
58900
+ import { basename as basename14, join as join137 } from "node:path";
58542
58901
  import { parse as parseYAML2 } from "yaml";
58543
58902
  var CHARS_PER_TOKEN3 = 4;
58544
58903
  var K10_TOKEN_CAP2 = 6e3;
58545
- var AGENTS_DIR = join136(homedir82(), ".claude", "agents");
58904
+ var AGENTS_DIR = join137(homedir83(), ".claude", "agents");
58546
58905
  var REQUIRED_FRONTMATTER_KEYS = ["name", "description", "allowed-tools"];
58547
58906
  var REQUIRED_SECTIONS = [
58548
58907
  "## Role",
@@ -58582,10 +58941,10 @@ function countNamedPatterns(sectionText) {
58582
58941
  return Math.max(h3Count, bulletCount);
58583
58942
  }
58584
58943
  function checkFile(filepath) {
58585
- if (!existsSync131(filepath) || !statSync34(filepath).isFile()) {
58944
+ if (!existsSync132(filepath) || !statSync34(filepath).isFile()) {
58586
58945
  return { passed: false, failures: [`file not found: ${filepath}`], tokens: 0, mergedSkipped: false };
58587
58946
  }
58588
- const text = readFileSync119(filepath, "utf8");
58947
+ const text = readFileSync120(filepath, "utf8");
58589
58948
  const { fm, body } = parseFile(text);
58590
58949
  const failures = [];
58591
58950
  for (const key of REQUIRED_FRONTMATTER_KEYS) {
@@ -58598,8 +58957,8 @@ function checkFile(filepath) {
58598
58957
  const agentName = typeof fm.name === "string" ? fm.name : "";
58599
58958
  let mergedSkipped = false;
58600
58959
  if (!isNewAgent && agentName !== "") {
58601
- const upstreamPath = join136(AGENTS_DIR, `${agentName}.md`);
58602
- if (existsSync131(upstreamPath)) {
58960
+ const upstreamPath = join137(AGENTS_DIR, `${agentName}.md`);
58961
+ if (existsSync132(upstreamPath)) {
58603
58962
  mergedSkipped = true;
58604
58963
  }
58605
58964
  }
@@ -58651,7 +59010,7 @@ Results: ${passCount} passed, ${failCount} failed
58651
59010
  }
58652
59011
 
58653
59012
  // src/commands/flywheel/diversity-check.ts
58654
- import { readFileSync as readFileSync120 } from "node:fs";
59013
+ import { readFileSync as readFileSync121 } from "node:fs";
58655
59014
  import { basename as basename15 } from "node:path";
58656
59015
  import { globSync as globSync2 } from "node:fs";
58657
59016
 
@@ -58761,7 +59120,7 @@ function registerFlywheelDiversityCheck(parent) {
58761
59120
  const personas = /* @__PURE__ */ new Map();
58762
59121
  for (const filepath of files) {
58763
59122
  try {
58764
- const body = readFileSync120(filepath, "utf8");
59123
+ const body = readFileSync121(filepath, "utf8");
58765
59124
  if (body.trim().length > 0) {
58766
59125
  personas.set(basename15(filepath, ".md"), body);
58767
59126
  }
@@ -58792,9 +59151,9 @@ ${formatRedivergencePrompt(persona_a, persona_b, score, void 0, void 0, threshol
58792
59151
  }
58793
59152
 
58794
59153
  // src/commands/flywheel/ping.ts
58795
- import { mkdirSync as mkdirSync78, writeFileSync as writeFileSync73 } from "node:fs";
58796
- import { homedir as homedir83 } from "node:os";
58797
- import { dirname as dirname75, join as join137 } from "node:path";
59154
+ import { mkdirSync as mkdirSync78, writeFileSync as writeFileSync74 } from "node:fs";
59155
+ import { homedir as homedir84 } from "node:os";
59156
+ import { dirname as dirname75, join as join138 } from "node:path";
58798
59157
  var COLD_START_BUDGET_GOOD_MS = 200;
58799
59158
  var COLD_START_BUDGET_FAIR_MS = 500;
58800
59159
  function classifyColdStart(coldStartMs) {
@@ -58810,9 +59169,9 @@ function readOlamVersion() {
58810
59169
  }
58811
59170
  }
58812
59171
  function writeBaseline(record) {
58813
- const baselinePath = join137(homedir83(), ".local", "share", "olam", "flywheel-baseline.json");
59172
+ const baselinePath = join138(homedir84(), ".local", "share", "olam", "flywheel-baseline.json");
58814
59173
  mkdirSync78(dirname75(baselinePath), { recursive: true });
58815
- writeFileSync73(baselinePath, JSON.stringify(record, null, 2) + "\n", "utf8");
59174
+ writeFileSync74(baselinePath, JSON.stringify(record, null, 2) + "\n", "utf8");
58816
59175
  return baselinePath;
58817
59176
  }
58818
59177
  function registerFlywheelPing(parent) {
@@ -58847,30 +59206,30 @@ function registerFlywheelPing(parent) {
58847
59206
  }
58848
59207
 
58849
59208
  // src/commands/flywheel/session-start.ts
58850
- import { readFileSync as readFileSync121, statSync as statSync35 } from "node:fs";
58851
- import { homedir as homedir84 } from "node:os";
58852
- import { join as join138 } from "node:path";
59209
+ import { readFileSync as readFileSync122, statSync as statSync35 } from "node:fs";
59210
+ import { homedir as homedir85 } from "node:os";
59211
+ import { join as join139 } from "node:path";
58853
59212
  var SESSIONSTART_HOOK_SENTINEL = "olam-sessionstart-context-hook-v1";
58854
59213
  var MAX_WAKE_BRIEF_BYTES = 8 * 1024;
58855
59214
  var MAX_ACTIVE_WORLD_BYTES = 4 * 1024;
58856
59215
  var MAX_CONFIG_BYTES = 8 * 1024;
58857
- function tryReadCapped(path121, maxBytes) {
59216
+ function tryReadCapped(path122, maxBytes) {
58858
59217
  try {
58859
- const stat = statSync35(path121);
59218
+ const stat = statSync35(path122);
58860
59219
  if (!stat.isFile() || stat.size === 0) return null;
58861
59220
  if (stat.size > maxBytes) {
58862
59221
  process.stderr.write(
58863
- `[${SESSIONSTART_HOOK_SENTINEL}] skipping ${path121}: ${stat.size} bytes > ${maxBytes} cap
59222
+ `[${SESSIONSTART_HOOK_SENTINEL}] skipping ${path122}: ${stat.size} bytes > ${maxBytes} cap
58864
59223
  `
58865
59224
  );
58866
59225
  return null;
58867
59226
  }
58868
- return readFileSync121(path121, "utf-8");
59227
+ return readFileSync122(path122, "utf-8");
58869
59228
  } catch (err) {
58870
59229
  const code = err?.code;
58871
59230
  if (code === "ENOENT") return null;
58872
59231
  process.stderr.write(
58873
- `[${SESSIONSTART_HOOK_SENTINEL}] read failed for ${path121}: ${err?.message ?? err}
59232
+ `[${SESSIONSTART_HOOK_SENTINEL}] read failed for ${path122}: ${err?.message ?? err}
58874
59233
  `
58875
59234
  );
58876
59235
  return null;
@@ -58890,15 +59249,15 @@ function tryParseJson(raw, label) {
58890
59249
  }
58891
59250
  function buildContextBlock(olamHome7) {
58892
59251
  const config = tryParseJson(
58893
- tryReadCapped(join138(olamHome7, "config.json"), MAX_CONFIG_BYTES),
59252
+ tryReadCapped(join139(olamHome7, "config.json"), MAX_CONFIG_BYTES),
58894
59253
  "config.json"
58895
59254
  );
58896
59255
  const activeWorld = tryParseJson(
58897
- tryReadCapped(join138(olamHome7, "state", "active-world.json"), MAX_ACTIVE_WORLD_BYTES),
59256
+ tryReadCapped(join139(olamHome7, "state", "active-world.json"), MAX_ACTIVE_WORLD_BYTES),
58898
59257
  "active-world.json"
58899
59258
  );
58900
59259
  const wakeBrief = tryReadCapped(
58901
- join138(olamHome7, "state", "wake-brief.md"),
59260
+ join139(olamHome7, "state", "wake-brief.md"),
58902
59261
  MAX_WAKE_BRIEF_BYTES
58903
59262
  );
58904
59263
  const sections = [];
@@ -58950,7 +59309,7 @@ function registerFlywheelSessionStart(parent) {
58950
59309
  parent.command("session-start").description(
58951
59310
  "Emit operator-state context for the SessionStart hook (4th defense layer). Reads ~/.olam/state/* and writes the additionalContext JSON to stdout. Fail-soft: always exits 0."
58952
59311
  ).action(() => {
58953
- const olamHome7 = process.env.OLAM_HOME ?? join138(homedir84(), ".olam");
59312
+ const olamHome7 = process.env.OLAM_HOME ?? join139(homedir85(), ".olam");
58954
59313
  emitSessionStartContext(olamHome7);
58955
59314
  process.exit(0);
58956
59315
  });
@@ -58959,9 +59318,9 @@ function registerFlywheelSessionStart(parent) {
58959
59318
  // src/commands/flywheel/install-sessionstart-hook.ts
58960
59319
  init_merge_settings();
58961
59320
  init_output();
58962
- import { existsSync as existsSync132, copyFileSync as copyFileSync19, mkdirSync as mkdirSync79, readFileSync as readFileSync122, unlinkSync as unlinkSync26, writeFileSync as writeFileSync74 } from "node:fs";
58963
- import { homedir as homedir85 } from "node:os";
58964
- import { dirname as dirname76, join as join139 } from "node:path";
59321
+ import { existsSync as existsSync133, copyFileSync as copyFileSync19, mkdirSync as mkdirSync79, readFileSync as readFileSync123, unlinkSync as unlinkSync26, writeFileSync as writeFileSync75 } from "node:fs";
59322
+ import { homedir as homedir86 } from "node:os";
59323
+ import { dirname as dirname76, join as join140 } from "node:path";
58965
59324
  var SESSIONSTART_HOOK_STAGE = "SessionStart";
58966
59325
  var SESSIONSTART_HOOK_TIMEOUT_MS = 5e3;
58967
59326
  var NOOP_GUARD = "command -v olam >/dev/null 2>&1 || exit 0;";
@@ -58978,11 +59337,11 @@ function buildSessionStartHookEntry() {
58978
59337
  };
58979
59338
  }
58980
59339
  function settingsPathFor6(scope, cwd) {
58981
- if (scope === "user") return join139(homedir85(), ".claude", "settings.json");
58982
- return join139(cwd ?? process.cwd(), ".claude", "settings.json");
59340
+ if (scope === "user") return join140(homedir86(), ".claude", "settings.json");
59341
+ return join140(cwd ?? process.cwd(), ".claude", "settings.json");
58983
59342
  }
58984
59343
  function backup4(filePath) {
58985
- if (!existsSync132(filePath)) return null;
59344
+ if (!existsSync133(filePath)) return null;
58986
59345
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
58987
59346
  const backupPath = `${filePath}.olam-bak.${ts}`;
58988
59347
  copyFileSync19(filePath, backupPath);
@@ -59008,8 +59367,8 @@ function installSessionStartHook(filePath) {
59008
59367
  return { status: result.status, filePath, backupPath };
59009
59368
  }
59010
59369
  function uninstallSessionStartHook(filePath) {
59011
- if (!existsSync132(filePath)) return { status: "no-settings", filePath };
59012
- const raw = readFileSync122(filePath, "utf-8");
59370
+ if (!existsSync133(filePath)) return { status: "no-settings", filePath };
59371
+ const raw = readFileSync123(filePath, "utf-8");
59013
59372
  const settings = raw.trim() ? JSON.parse(raw) : {};
59014
59373
  const matchers = settings.hooks?.SessionStart;
59015
59374
  if (!Array.isArray(matchers) || matchers.length === 0) {
@@ -59049,7 +59408,7 @@ function uninstallSessionStartHook(filePath) {
59049
59408
  delete next.hooks.SessionStart;
59050
59409
  }
59051
59410
  }
59052
- writeFileSync74(filePath, JSON.stringify(next, null, 2) + "\n");
59411
+ writeFileSync75(filePath, JSON.stringify(next, null, 2) + "\n");
59053
59412
  return { status: "removed", filePath };
59054
59413
  }
59055
59414
  function registerFlywheelInstallSessionStartHook(parent) {
@@ -59377,14 +59736,14 @@ init_manager();
59377
59736
  init_context();
59378
59737
  init_output();
59379
59738
  import { spawnSync as defaultSpawnSync } from "node:child_process";
59380
- import * as fs118 from "node:fs";
59381
- import * as os64 from "node:os";
59382
- import * as path118 from "node:path";
59739
+ import * as fs119 from "node:fs";
59740
+ import * as os65 from "node:os";
59741
+ import * as path119 from "node:path";
59383
59742
  function devboxContainerName(worldId) {
59384
59743
  return `olam-${worldId}-devbox`;
59385
59744
  }
59386
59745
  function olamHomeDir() {
59387
- return process.env["OLAM_HOME"] ?? path118.join(os64.homedir(), ".olam");
59746
+ return process.env["OLAM_HOME"] ?? path119.join(os65.homedir(), ".olam");
59388
59747
  }
59389
59748
  function defaultRestartContainer(name, spawn14 = defaultSpawnSync) {
59390
59749
  const r = spawn14("docker", ["restart", "--time", "30", name], {
@@ -59397,8 +59756,8 @@ function defaultRestartContainer(name, spawn14 = defaultSpawnSync) {
59397
59756
  };
59398
59757
  }
59399
59758
  function defaultAppendAuditLog(homeDir, line) {
59400
- fs118.mkdirSync(homeDir, { recursive: true });
59401
- fs118.appendFileSync(path118.join(homeDir, "usage.log"), line.endsWith("\n") ? line : line + "\n", {
59759
+ fs119.mkdirSync(homeDir, { recursive: true });
59760
+ fs119.appendFileSync(path119.join(homeDir, "usage.log"), line.endsWith("\n") ? line : line + "\n", {
59402
59761
  encoding: "utf-8"
59403
59762
  });
59404
59763
  }
@@ -59443,19 +59802,19 @@ async function doRekey(worldId, deps) {
59443
59802
  );
59444
59803
  const rotatedAt = deps.now().toISOString();
59445
59804
  const homeDir = deps.olamHomeDir();
59446
- const worldDir = path118.join(homeDir, "worlds", worldId);
59447
- fs118.mkdirSync(worldDir, { recursive: true });
59448
- const credentialsPath = path118.join(worldDir, "credentials.json");
59805
+ const worldDir = path119.join(homeDir, "worlds", worldId);
59806
+ fs119.mkdirSync(worldDir, { recursive: true });
59807
+ const credentialsPath = path119.join(worldDir, "credentials.json");
59449
59808
  const payload = {
59450
59809
  worldRoleName,
59451
59810
  password,
59452
59811
  rotatedAt
59453
59812
  };
59454
- fs118.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
59813
+ fs119.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
59455
59814
  encoding: "utf-8",
59456
59815
  mode: 384
59457
59816
  });
59458
- fs118.chmodSync(credentialsPath, 384);
59817
+ fs119.chmodSync(credentialsPath, 384);
59459
59818
  const restart = deps.restartContainer(devboxContainerName(worldId));
59460
59819
  deps.appendAuditLog(`${rotatedAt} ${worldId} rekey`);
59461
59820
  if (!restart.ok) {
@@ -59521,9 +59880,9 @@ function registerRekey(program2) {
59521
59880
  // src/commands/yolo.ts
59522
59881
  init_output();
59523
59882
  import * as childProcess2 from "node:child_process";
59524
- import * as fs119 from "node:fs";
59525
- import * as os65 from "node:os";
59526
- import * as path119 from "node:path";
59883
+ import * as fs120 from "node:fs";
59884
+ import * as os66 from "node:os";
59885
+ import * as path120 from "node:path";
59527
59886
  import pc51 from "picocolors";
59528
59887
  var YOLO_BOOT_DELAY_MS = 8e3;
59529
59888
  var CAPTURE_DELAY_MS = 4e3;
@@ -59551,7 +59910,7 @@ function defaultSleeper(ms) {
59551
59910
  function detectTmuxBinary(runner = defaultRunner) {
59552
59911
  for (const p of TMUX_PROBE_PATHS) {
59553
59912
  if (TMUX_SHIM_MARKERS.some((m) => p.includes(m))) continue;
59554
- if (fs119.existsSync(p)) return p;
59913
+ if (fs120.existsSync(p)) return p;
59555
59914
  }
59556
59915
  const result = runner("which", ["tmux"]);
59557
59916
  if (result.status === 0 && result.stdout) {
@@ -59565,17 +59924,17 @@ function resolveWorktreeRoot(runner = defaultRunner, cwd = process.cwd()) {
59565
59924
  if (process.env["OLAM_TMUX_YOLO_ROOT"]) {
59566
59925
  return process.env["OLAM_TMUX_YOLO_ROOT"];
59567
59926
  }
59568
- const rootFile = path119.join(os65.homedir(), ".olam-tmux-yolo-root");
59569
- if (fs119.existsSync(rootFile)) {
59570
- const line = fs119.readFileSync(rootFile, "utf-8").trim();
59927
+ const rootFile = path120.join(os66.homedir(), ".olam-tmux-yolo-root");
59928
+ if (fs120.existsSync(rootFile)) {
59929
+ const line = fs120.readFileSync(rootFile, "utf-8").trim();
59571
59930
  if (line) return line;
59572
59931
  }
59573
59932
  const gitResult = runner("git", ["rev-parse", "--show-toplevel"], { cwd });
59574
59933
  if (gitResult.status !== 0 || !gitResult.stdout) {
59575
- return path119.join(cwd, "..", path119.basename(cwd) + "-wt");
59934
+ return path120.join(cwd, "..", path120.basename(cwd) + "-wt");
59576
59935
  }
59577
59936
  const repoRoot = gitResult.stdout.trim();
59578
- return path119.join(path119.dirname(repoRoot), path119.basename(repoRoot) + "-wt");
59937
+ return path120.join(path120.dirname(repoRoot), path120.basename(repoRoot) + "-wt");
59579
59938
  }
59580
59939
  function resolveYoloCommand(runner = defaultRunner) {
59581
59940
  const result = runner("bash", ["-lc", "command -v yolo"]);
@@ -59601,7 +59960,7 @@ async function spawnYolo(opts) {
59601
59960
  );
59602
59961
  }
59603
59962
  try {
59604
- fs119.accessSync(opts.promptFile, fs119.constants.R_OK);
59963
+ fs120.accessSync(opts.promptFile, fs120.constants.R_OK);
59605
59964
  } catch {
59606
59965
  throw new Error(
59607
59966
  `Prompt file not found or not readable: ${opts.promptFile}
@@ -59609,7 +59968,7 @@ Create the file with your task description and try again.`
59609
59968
  );
59610
59969
  }
59611
59970
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59612
- const worktreePath = path119.join(worktreeRoot, opts.name);
59971
+ const worktreePath = path120.join(worktreeRoot, opts.name);
59613
59972
  const branchCheck = runner("git", ["show-ref", "--verify", "--quiet", `refs/heads/${opts.branch}`], { cwd });
59614
59973
  if (branchCheck.status === 0) {
59615
59974
  throw new Error(
@@ -59617,7 +59976,7 @@ Create the file with your task description and try again.`
59617
59976
  Run: olam yolo --cleanup ${opts.name} (or use --force to bypass the merged check)`
59618
59977
  );
59619
59978
  }
59620
- if (fs119.existsSync(worktreePath)) {
59979
+ if (fs120.existsSync(worktreePath)) {
59621
59980
  throw new Error(
59622
59981
  `Worktree directory already exists: ${worktreePath}
59623
59982
  Run: olam yolo --cleanup ${opts.name} to remove it first.`
@@ -59682,10 +60041,10 @@ function listYoloWindows(opts = {}) {
59682
60041
  const windowNames = listResult.stdout.split("\n").map((n) => n.trim()).filter(Boolean);
59683
60042
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59684
60043
  return windowNames.map((name) => {
59685
- const worktreePath = path119.join(worktreeRoot, name);
60044
+ const worktreePath = path120.join(worktreeRoot, name);
59686
60045
  return {
59687
60046
  name,
59688
- worktreePath: fs119.existsSync(worktreePath) ? worktreePath : void 0
60047
+ worktreePath: fs120.existsSync(worktreePath) ? worktreePath : void 0
59689
60048
  };
59690
60049
  });
59691
60050
  }
@@ -59694,7 +60053,7 @@ async function cleanupYolo(opts) {
59694
60053
  const cwd = opts.cwd ?? process.cwd();
59695
60054
  const mainBranch = opts.mainBranch ?? "main";
59696
60055
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59697
- const worktreePath = path119.join(worktreeRoot, opts.name);
60056
+ const worktreePath = path120.join(worktreeRoot, opts.name);
59698
60057
  const worktreeList = runner("git", ["worktree", "list", "--porcelain"], { cwd });
59699
60058
  let branch;
59700
60059
  if (worktreeList.status === 0) {
@@ -59819,18 +60178,18 @@ function registerYolo(program2) {
59819
60178
  }
59820
60179
 
59821
60180
  // src/pleri-config.ts
59822
- import * as fs120 from "node:fs";
59823
- import * as path120 from "node:path";
60181
+ import * as fs121 from "node:fs";
60182
+ import * as path121 from "node:path";
59824
60183
  function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
59825
60184
  if (process.env.PLERI_BASE_URL) {
59826
60185
  return true;
59827
60186
  }
59828
- const configPath = path120.join(configDir, "config.yaml");
59829
- if (!fs120.existsSync(configPath)) {
60187
+ const configPath = path121.join(configDir, "config.yaml");
60188
+ if (!fs121.existsSync(configPath)) {
59830
60189
  return false;
59831
60190
  }
59832
60191
  try {
59833
- const contents = fs120.readFileSync(configPath, "utf8");
60192
+ const contents = fs121.readFileSync(configPath, "utf8");
59834
60193
  return /^[^#\n]*\bpleri:/m.test(contents);
59835
60194
  } catch {
59836
60195
  return false;
@@ -59907,7 +60266,7 @@ var HELP_GROUPS = [
59907
60266
  // crystallize is conditionally hidden (requires Pleri); kept here so it
59908
60267
  // lands in the right group when Pleri IS configured.
59909
60268
  title: "Dev tools",
59910
- names: ["begin", "completion", "crystallize", "hermes", "lanes", "policy-check", "pr", "stop", "yolo"]
60269
+ names: ["begin", "completion", "crystallize", "hermes", "inbox", "lanes", "policy-check", "pr", "stop", "yolo"]
59911
60270
  },
59912
60271
  {
59913
60272
  // Alphabetical within group. `config` lives in 'Cloud setup' above.
@@ -60129,7 +60488,7 @@ try {
60129
60488
  }
60130
60489
  }
60131
60490
  void scheduleUpgradeCheck(cliVersion);
60132
- if (process.argv.length <= 2 && !existsSync135(join143(homedir88(), ".olam", "config.json"))) {
60491
+ if (process.argv.length <= 2 && !existsSync136(join144(homedir89(), ".olam", "config.json"))) {
60133
60492
  process.stdout.write("No olam config found yet.\n");
60134
60493
  process.stdout.write("Get started: olam setup\n\n");
60135
60494
  }