@pleri/olam-cli 0.1.209 → 0.1.211

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) {
@@ -53845,8 +54016,9 @@ function registerMcpServe(cmd) {
53845
54016
  init_output();
53846
54017
  init_install_shared();
53847
54018
  var NPM_PACKAGE_NAME2 = "@pleri/olam-cli";
54019
+ var NPM_PACKAGE_SPEC = `${NPM_PACKAGE_NAME2}@latest`;
53848
54020
  function buildClaudeMcpAddArgs2(scope, useGlobal) {
53849
- 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"] };
53850
54022
  return {
53851
54023
  command: "claude",
53852
54024
  args: ["mcp", "add", "olam", "--scope", scope, "--", target.cmd, ...target.args]
@@ -53956,8 +54128,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
53956
54128
  function authHeaders() {
53957
54129
  return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
53958
54130
  }
53959
- async function apiFetch(path121, init = {}) {
53960
- const res = await fetch(`${BASE_URL}${path121}`, {
54131
+ async function apiFetch(path122, init = {}) {
54132
+ const res = await fetch(`${BASE_URL}${path122}`, {
53961
54133
  ...init,
53962
54134
  headers: {
53963
54135
  "Content-Type": "application/json",
@@ -54974,18 +55146,29 @@ import { existsSync as existsSync121 } from "node:fs";
54974
55146
  // ../core/dist/memory/gate.js
54975
55147
  init_olam_paths();
54976
55148
  import { readFileSync as readFileSync108 } from "node:fs";
54977
- var TRUTHY_ENV = /* @__PURE__ */ new Set(["1", "true", "on"]);
54978
- var TRUTHY_FILE = /* @__PURE__ */ new Set(["true", "1"]);
54979
- 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() {
54980
55163
  const envVal = process.env["OLAM_AGENT_MEMORY"];
54981
55164
  if (envVal !== void 0 && envVal.length > 0) {
54982
- return TRUTHY_ENV.has(envVal.trim().toLowerCase());
55165
+ return ENV_MODE.get(envVal.trim().toLowerCase()) ?? "off";
54983
55166
  }
54984
55167
  try {
54985
55168
  const raw = readFileSync108(agentMemoryEnabledPath(), "utf8").trim().toLowerCase();
54986
- return TRUTHY_FILE.has(raw);
55169
+ return FILE_MODE.get(raw) ?? "off";
54987
55170
  } catch {
54988
- return false;
55171
+ return "off";
54989
55172
  }
54990
55173
  }
54991
55174
 
@@ -55039,18 +55222,18 @@ async function runMemoryStatus(opts = {}) {
55039
55222
  printInfo("livez", s.livez);
55040
55223
  printInfo("secret", s.secretSet ? "~/.olam/memory-secret (set)" : "(missing)");
55041
55224
  printInfo("port", `${s.port}`);
55042
- const gateEnabled = isAgentMemoryEnabled();
55225
+ const mode = agentMemoryMode();
55043
55226
  const envVal = process.env["OLAM_AGENT_MEMORY"];
55044
55227
  const envActive = envVal !== void 0 && envVal.length > 0;
55045
- if (gateEnabled) {
55046
- printInfo(
55047
- "agent-memory",
55048
- envActive ? `ENABLED (OLAM_AGENT_MEMORY=${envVal} env override)` : `ENABLED (flag file: ${agentMemoryEnabledPath()})`
55049
- );
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})`);
55050
55233
  } else {
55051
55234
  printInfo(
55052
55235
  "agent-memory",
55053
- 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)`
55054
55237
  );
55055
55238
  }
55056
55239
  if (s.legacyPidfilePresent) {
@@ -55196,27 +55379,34 @@ init_olam_paths();
55196
55379
  import { mkdirSync as mkdirSync73, writeFileSync as writeFileSync66 } from "node:fs";
55197
55380
  import { dirname as dirname69 } from "node:path";
55198
55381
  init_output();
55199
- function writeGateFlag(enabled) {
55382
+ function writeGateFlag(value) {
55200
55383
  const flagPath = agentMemoryEnabledPath();
55201
55384
  mkdirSync73(dirname69(flagPath), { recursive: true });
55202
- writeFileSync66(flagPath, enabled ? "TRUE\n" : "FALSE\n", { mode: 420 });
55385
+ writeFileSync66(flagPath, `${value}
55386
+ `, { mode: 420 });
55203
55387
  }
55204
55388
  function registerMemoryEnable(parent) {
55205
- parent.command("enable").description("Enable agent-memory recall and capture (writes TRUE to the gate flag file)").action(() => {
55206
- writeGateFlag(true);
55207
- const flagPath = agentMemoryEnabledPath();
55208
- printSuccess(`agent-memory enabled`);
55209
- printInfo("flag file", flagPath);
55210
- 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());
55211
55402
  });
55212
55403
  }
55213
55404
  function registerMemoryDisable(parent) {
55214
55405
  parent.command("disable").description("Disable agent-memory recall and capture (writes FALSE to the gate flag file)").action(() => {
55215
- writeGateFlag(false);
55216
- const flagPath = agentMemoryEnabledPath();
55406
+ writeGateFlag("FALSE");
55217
55407
  printSuccess(`agent-memory disabled`);
55218
- printInfo("flag file", flagPath);
55219
- printInfo("gate", isAgentMemoryEnabled() ? "DISABLED (re-read confirms)" : "DISABLED");
55408
+ printInfo("flag file", agentMemoryEnabledPath());
55409
+ printInfo("mode", agentMemoryMode());
55220
55410
  });
55221
55411
  }
55222
55412
 
@@ -55581,13 +55771,13 @@ function resolveMemoryServiceDir() {
55581
55771
  );
55582
55772
  }
55583
55773
  function resolveLocalBridgeScript(serviceDir) {
55584
- const path121 = join126(serviceDir, "scripts", "local-bridge-server.mjs");
55585
- if (!existsSync123(path121)) {
55774
+ const path122 = join126(serviceDir, "scripts", "local-bridge-server.mjs");
55775
+ if (!existsSync123(path122)) {
55586
55776
  throw new Error(
55587
- `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.`
55588
55778
  );
55589
55779
  }
55590
- return path121;
55780
+ return path122;
55591
55781
  }
55592
55782
  function validateBridgeOpts(opts) {
55593
55783
  const local = opts.local ?? true;
@@ -56446,18 +56636,179 @@ function registerInboxInstallHookCommand(inbox) {
56446
56636
  });
56447
56637
  }
56448
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
+
56449
56799
  // src/commands/inbox/index.ts
56450
56800
  function registerInbox(program2) {
56451
56801
  const inbox = program2.command("inbox").description("Mobile question-inbox operations (route AskUserQuestion to phone)");
56452
56802
  registerInboxInstallHookCommand(inbox);
56803
+ registerInboxToggleCommands(inbox);
56453
56804
  }
56454
56805
 
56455
56806
  // src/commands/kg-build.ts
56456
56807
  init_storage_paths();
56457
56808
  init_workspace_name();
56458
- import * as fs117 from "node:fs";
56459
- import * as os63 from "node:os";
56460
- 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";
56461
56812
 
56462
56813
  // ../core/dist/kg/kg-service-client.js
56463
56814
  var KG_SERVICE_PORT_DEFAULT = 9997;
@@ -56468,8 +56819,8 @@ function port() {
56468
56819
  const n = Number.parseInt(env, 10);
56469
56820
  return Number.isFinite(n) && n > 0 ? n : KG_SERVICE_PORT_DEFAULT;
56470
56821
  }
56471
- function url(path121) {
56472
- return `http://127.0.0.1:${port()}${path121}`;
56822
+ function url(path122) {
56823
+ return `http://127.0.0.1:${port()}${path122}`;
56473
56824
  }
56474
56825
  function kgServiceHealthUrl() {
56475
56826
  return url("/health");
@@ -56549,39 +56900,39 @@ init_storage_paths();
56549
56900
  init_workspace_name();
56550
56901
  init_kg_caps();
56551
56902
  init_output();
56552
- import fs112 from "node:fs";
56553
- import { homedir as homedir75 } from "node:os";
56554
- 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";
56555
56906
  function olamHome6() {
56556
- return process.env.OLAM_HOME ?? path112.join(homedir75(), ".olam");
56907
+ return process.env.OLAM_HOME ?? path113.join(homedir76(), ".olam");
56557
56908
  }
56558
56909
  function kgRoot2() {
56559
- return path112.join(olamHome6(), "kg");
56910
+ return path113.join(olamHome6(), "kg");
56560
56911
  }
56561
56912
  function worldsRoot2() {
56562
- return path112.join(olamHome6(), "worlds");
56913
+ return path113.join(olamHome6(), "worlds");
56563
56914
  }
56564
56915
  function dirSizeBytes2(dir) {
56565
- if (!fs112.existsSync(dir)) return 0;
56916
+ if (!fs113.existsSync(dir)) return 0;
56566
56917
  let total = 0;
56567
56918
  const stack = [dir];
56568
56919
  while (stack.length > 0) {
56569
56920
  const cur = stack.pop();
56570
56921
  let entries;
56571
56922
  try {
56572
- entries = fs112.readdirSync(cur, { withFileTypes: true });
56923
+ entries = fs113.readdirSync(cur, { withFileTypes: true });
56573
56924
  } catch {
56574
56925
  continue;
56575
56926
  }
56576
56927
  for (const entry of entries) {
56577
- const full = path112.join(cur, entry.name);
56928
+ const full = path113.join(cur, entry.name);
56578
56929
  if (entry.isSymbolicLink()) continue;
56579
56930
  if (entry.isDirectory()) {
56580
56931
  stack.push(full);
56581
56932
  continue;
56582
56933
  }
56583
56934
  try {
56584
- total += fs112.statSync(full).size;
56935
+ total += fs113.statSync(full).size;
56585
56936
  } catch {
56586
56937
  }
56587
56938
  }
@@ -56595,10 +56946,10 @@ function formatBytes5(n) {
56595
56946
  return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
56596
56947
  }
56597
56948
  function readFreshness(workspace) {
56598
- const file = path112.join(kgPristinePath(workspace), "freshness.json");
56599
- if (!fs112.existsSync(file)) return null;
56949
+ const file = path113.join(kgPristinePath(workspace), "freshness.json");
56950
+ if (!fs113.existsSync(file)) return null;
56600
56951
  try {
56601
- const raw = JSON.parse(fs112.readFileSync(file, "utf-8"));
56952
+ const raw = JSON.parse(fs113.readFileSync(file, "utf-8"));
56602
56953
  if (raw && typeof raw === "object") return raw;
56603
56954
  return null;
56604
56955
  } catch {
@@ -56606,10 +56957,10 @@ function readFreshness(workspace) {
56606
56957
  }
56607
56958
  }
56608
56959
  function readOverlayNodeCount(graphifyOutDir) {
56609
- const graphPath = path112.join(graphifyOutDir, "graph.json");
56610
- if (!fs112.existsSync(graphPath)) return null;
56960
+ const graphPath = path113.join(graphifyOutDir, "graph.json");
56961
+ if (!fs113.existsSync(graphPath)) return null;
56611
56962
  try {
56612
- const raw = JSON.parse(fs112.readFileSync(graphPath, "utf-8"));
56963
+ const raw = JSON.parse(fs113.readFileSync(graphPath, "utf-8"));
56613
56964
  if (raw && typeof raw === "object") {
56614
56965
  const nodes = raw.nodes;
56615
56966
  if (Array.isArray(nodes)) return nodes.length;
@@ -56621,28 +56972,28 @@ function readOverlayNodeCount(graphifyOutDir) {
56621
56972
  }
56622
56973
  function listOverlays() {
56623
56974
  const root = worldsRoot2();
56624
- if (!fs112.existsSync(root)) return [];
56975
+ if (!fs113.existsSync(root)) return [];
56625
56976
  const records = [];
56626
56977
  let worldDirs;
56627
56978
  try {
56628
- worldDirs = fs112.readdirSync(root, { withFileTypes: true });
56979
+ worldDirs = fs113.readdirSync(root, { withFileTypes: true });
56629
56980
  } catch {
56630
56981
  return [];
56631
56982
  }
56632
56983
  for (const worldEntry of worldDirs) {
56633
56984
  if (!worldEntry.isDirectory()) continue;
56634
56985
  const worldId = worldEntry.name;
56635
- const worldDir = path112.join(root, worldId);
56986
+ const worldDir = path113.join(root, worldId);
56636
56987
  let cloneDirs;
56637
56988
  try {
56638
- cloneDirs = fs112.readdirSync(worldDir, { withFileTypes: true });
56989
+ cloneDirs = fs113.readdirSync(worldDir, { withFileTypes: true });
56639
56990
  } catch {
56640
56991
  continue;
56641
56992
  }
56642
56993
  for (const cloneEntry of cloneDirs) {
56643
56994
  if (!cloneEntry.isDirectory()) continue;
56644
- const graphifyOut = path112.join(worldDir, cloneEntry.name, "graphify-out");
56645
- if (!fs112.existsSync(graphifyOut)) continue;
56995
+ const graphifyOut = path113.join(worldDir, cloneEntry.name, "graphify-out");
56996
+ if (!fs113.existsSync(graphifyOut)) continue;
56646
56997
  records.push({
56647
56998
  world_id: worldId,
56648
56999
  clone_dir: cloneEntry.name,
@@ -56656,11 +57007,11 @@ function listOverlays() {
56656
57007
  }
56657
57008
  function listPristines(overlays) {
56658
57009
  const root = kgRoot2();
56659
- if (!fs112.existsSync(root)) return [];
57010
+ if (!fs113.existsSync(root)) return [];
56660
57011
  const records = [];
56661
57012
  let entries;
56662
57013
  try {
56663
- entries = fs112.readdirSync(root, { withFileTypes: true });
57014
+ entries = fs113.readdirSync(root, { withFileTypes: true });
56664
57015
  } catch {
56665
57016
  return [];
56666
57017
  }
@@ -56673,7 +57024,7 @@ function listPristines(overlays) {
56673
57024
  continue;
56674
57025
  }
56675
57026
  const fresh = readFreshness(workspace);
56676
- const graphifyOut = path112.join(kgPristinePath(workspace), "graphify-out");
57027
+ const graphifyOut = path113.join(kgPristinePath(workspace), "graphify-out");
56677
57028
  const size = dirSizeBytes2(graphifyOut);
56678
57029
  const worldCount = overlays.filter((o) => o.clone_dir === workspace).length;
56679
57030
  records.push({
@@ -56809,10 +57160,10 @@ init_storage_paths();
56809
57160
  init_workspace_name();
56810
57161
  init_output();
56811
57162
  import { spawn as spawn13 } from "node:child_process";
56812
- import fs113 from "node:fs";
56813
- import path113 from "node:path";
57163
+ import fs114 from "node:fs";
57164
+ import path114 from "node:path";
56814
57165
  function pidFilePath2(workspace) {
56815
- return path113.join(kgPristinePath(workspace), ".watch.pid");
57166
+ return path114.join(kgPristinePath(workspace), ".watch.pid");
56816
57167
  }
56817
57168
  function isPidAlive3(pid) {
56818
57169
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -56827,39 +57178,39 @@ function isPidAlive3(pid) {
56827
57178
  }
56828
57179
  function readAndClassifyPid(workspace) {
56829
57180
  const file = pidFilePath2(workspace);
56830
- if (!fs113.existsSync(file)) return { status: "no-pidfile", pid: null };
57181
+ if (!fs114.existsSync(file)) return { status: "no-pidfile", pid: null };
56831
57182
  let pid;
56832
57183
  try {
56833
- const raw = fs113.readFileSync(file, "utf-8").trim();
57184
+ const raw = fs114.readFileSync(file, "utf-8").trim();
56834
57185
  pid = Number.parseInt(raw, 10);
56835
57186
  } catch {
56836
- fs113.rmSync(file, { force: true });
57187
+ fs114.rmSync(file, { force: true });
56837
57188
  return { status: "stale-reclaimed", pid: null };
56838
57189
  }
56839
57190
  if (!Number.isInteger(pid) || pid <= 0) {
56840
- fs113.rmSync(file, { force: true });
57191
+ fs114.rmSync(file, { force: true });
56841
57192
  return { status: "stale-reclaimed", pid: null };
56842
57193
  }
56843
57194
  if (isPidAlive3(pid)) return { status: "active", pid };
56844
- fs113.rmSync(file, { force: true });
57195
+ fs114.rmSync(file, { force: true });
56845
57196
  return { status: "stale-reclaimed", pid: null };
56846
57197
  }
56847
57198
  function writePidFile2(workspace, pid) {
56848
57199
  const file = pidFilePath2(workspace);
56849
- const dir = path113.dirname(file);
56850
- fs113.mkdirSync(dir, { recursive: true });
56851
- 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" });
56852
57203
  }
56853
57204
  function removePidFile(workspace) {
56854
57205
  const file = pidFilePath2(workspace);
56855
57206
  try {
56856
- fs113.rmSync(file, { force: true });
57207
+ fs114.rmSync(file, { force: true });
56857
57208
  } catch {
56858
57209
  }
56859
57210
  }
56860
57211
  async function runKgWatch(workspaceArg, opts, deps = {}) {
56861
57212
  const cwd = deps.cwd ?? opts.cwd ?? process.cwd();
56862
- const name = workspaceArg ?? path113.basename(cwd).toLowerCase();
57213
+ const name = workspaceArg ?? path114.basename(cwd).toLowerCase();
56863
57214
  try {
56864
57215
  validateWorkspaceName(name);
56865
57216
  } catch (err) {
@@ -56867,7 +57218,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
56867
57218
  return { exitCode: 1, pidWritten: false };
56868
57219
  }
56869
57220
  const pristinePath = kgPristinePath(name);
56870
- const graphPath = path113.join(pristinePath, "graphify-out", "graph.json");
57221
+ const graphPath = path114.join(pristinePath, "graphify-out", "graph.json");
56871
57222
  const pidState = readAndClassifyPid(name);
56872
57223
  if (pidState.status === "active") {
56873
57224
  printError(
@@ -57180,33 +57531,33 @@ function registerKgDoctorCommand(kg) {
57180
57531
  init_merge_settings();
57181
57532
  init_hook_template2();
57182
57533
  init_output();
57183
- import * as fs114 from "node:fs";
57184
- import * as path114 from "node:path";
57185
- 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";
57186
57537
  import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
57187
57538
  function settingsPathFor4(scope) {
57188
57539
  if (scope === "user") {
57189
- return path114.join(os60.homedir(), ".claude", "settings.json");
57540
+ return path115.join(os61.homedir(), ".claude", "settings.json");
57190
57541
  }
57191
- return path114.join(process.cwd(), ".claude", "settings.json");
57542
+ return path115.join(process.cwd(), ".claude", "settings.json");
57192
57543
  }
57193
57544
  function backup3(filePath) {
57194
- if (!fs114.existsSync(filePath)) return null;
57545
+ if (!fs115.existsSync(filePath)) return null;
57195
57546
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
57196
57547
  const backupPath = `${filePath}.olam-bak.${ts}`;
57197
- fs114.copyFileSync(filePath, backupPath);
57548
+ fs115.copyFileSync(filePath, backupPath);
57198
57549
  return backupPath;
57199
57550
  }
57200
57551
  var HERMES_HOOK_MATCHERS = ["terminal", "bash", "shell", "search_files", "grep", "ripgrep"];
57201
57552
  function hermesConfigPath2() {
57202
- return path114.join(os60.homedir(), ".hermes", "config.yaml");
57553
+ return path115.join(os61.homedir(), ".hermes", "config.yaml");
57203
57554
  }
57204
57555
  function hermesHooksDir() {
57205
- return path114.join(os60.homedir(), ".hermes", "hooks");
57556
+ return path115.join(os61.homedir(), ".hermes", "hooks");
57206
57557
  }
57207
57558
  function patchHermesConfigForHook(action) {
57208
57559
  const configPath = hermesConfigPath2();
57209
- const raw = fs114.readFileSync(configPath, "utf-8");
57560
+ const raw = fs115.readFileSync(configPath, "utf-8");
57210
57561
  const config = yamlParse3(raw);
57211
57562
  const hooks = config["hooks"] ?? {};
57212
57563
  const preToolCall = Array.isArray(hooks["pre_tool_call"]) ? hooks["pre_tool_call"] : [];
@@ -57215,7 +57566,7 @@ function patchHermesConfigForHook(action) {
57215
57566
  (e) => typeof e["command"] === "string" && e["command"].includes(HERMES_KG_HOOK_SENTINEL)
57216
57567
  );
57217
57568
  if (alreadyPresent) return "already-present";
57218
- const hookScriptPath = path114.join(hermesHooksDir(), "kg-first.sh");
57569
+ const hookScriptPath = path115.join(hermesHooksDir(), "kg-first.sh");
57219
57570
  const entry = {
57220
57571
  matcher: HERMES_HOOK_MATCHERS.join("|"),
57221
57572
  command: `${hookScriptPath} # ${HERMES_KG_HOOK_SENTINEL}`
@@ -57225,7 +57576,7 @@ function patchHermesConfigForHook(action) {
57225
57576
  hooks: { ...hooks, pre_tool_call: [...preToolCall, entry] },
57226
57577
  hooks_auto_accept: true
57227
57578
  };
57228
- fs114.writeFileSync(configPath, yamlStringify4(updated2), "utf-8");
57579
+ fs115.writeFileSync(configPath, yamlStringify4(updated2), "utf-8");
57229
57580
  return "patched";
57230
57581
  }
57231
57582
  const filtered = preToolCall.filter(
@@ -57236,12 +57587,12 @@ function patchHermesConfigForHook(action) {
57236
57587
  ...config,
57237
57588
  hooks: { ...hooks, pre_tool_call: filtered }
57238
57589
  };
57239
- fs114.writeFileSync(configPath, yamlStringify4(updated), "utf-8");
57590
+ fs115.writeFileSync(configPath, yamlStringify4(updated), "utf-8");
57240
57591
  return "patched";
57241
57592
  }
57242
57593
  function doInstallForHermes() {
57243
57594
  const configPath = hermesConfigPath2();
57244
- if (!fs114.existsSync(configPath)) {
57595
+ if (!fs115.existsSync(configPath)) {
57245
57596
  printError(`~/.hermes/config.yaml not found at ${configPath}`);
57246
57597
  printInfo("remedy", "Install Hermes first, then re-run `olam kg install-hook --for hermes`");
57247
57598
  process.exitCode = 1;
@@ -57269,17 +57620,17 @@ function doInstallForHermes() {
57269
57620
  function doUninstallForHermes() {
57270
57621
  const configPath = hermesConfigPath2();
57271
57622
  const hooksDir = hermesHooksDir();
57272
- const hookScriptPath = path114.join(hooksDir, "kg-first.sh");
57623
+ const hookScriptPath = path115.join(hooksDir, "kg-first.sh");
57273
57624
  let scriptRemoved = false;
57274
- if (fs114.existsSync(hookScriptPath)) {
57275
- const content = fs114.readFileSync(hookScriptPath, "utf-8");
57625
+ if (fs115.existsSync(hookScriptPath)) {
57626
+ const content = fs115.readFileSync(hookScriptPath, "utf-8");
57276
57627
  if (content.includes(HERMES_KG_HOOK_SENTINEL)) {
57277
- fs114.unlinkSync(hookScriptPath);
57628
+ fs115.unlinkSync(hookScriptPath);
57278
57629
  scriptRemoved = true;
57279
57630
  }
57280
57631
  }
57281
57632
  let configPatched = false;
57282
- if (fs114.existsSync(configPath)) {
57633
+ if (fs115.existsSync(configPath)) {
57283
57634
  const result = patchHermesConfigForHook("uninstall");
57284
57635
  configPatched = result === "patched";
57285
57636
  }
@@ -57304,9 +57655,9 @@ function registerKgInstallHookCommand(kg) {
57304
57655
  const scope = opts.scope === "user" ? "user" : "project";
57305
57656
  const filePath = settingsPathFor4(scope);
57306
57657
  try {
57307
- fs114.mkdirSync(path114.dirname(filePath), { recursive: true });
57658
+ fs115.mkdirSync(path115.dirname(filePath), { recursive: true });
57308
57659
  } catch (err) {
57309
- 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)}`);
57310
57661
  process.exitCode = 1;
57311
57662
  return;
57312
57663
  }
@@ -57320,6 +57671,14 @@ function registerKgInstallHookCommand(kg) {
57320
57671
  entry: buildHookMatcherEntry({ flavor: "host" })
57321
57672
  }
57322
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
+ });
57323
57682
  switch (result.status) {
57324
57683
  case "installed":
57325
57684
  printSuccess(`kg-service hook installed (${scope} scope)`);
@@ -57331,7 +57690,7 @@ function registerKgInstallHookCommand(kg) {
57331
57690
  printInfo("kg-service hook", `already installed at ${filePath}`);
57332
57691
  if (backupPath) {
57333
57692
  try {
57334
- fs114.unlinkSync(backupPath);
57693
+ fs115.unlinkSync(backupPath);
57335
57694
  } catch {
57336
57695
  }
57337
57696
  }
@@ -57351,22 +57710,22 @@ function registerKgInstallHookCommand(kg) {
57351
57710
  // src/commands/kg-uninstall-hook.ts
57352
57711
  init_hook_template2();
57353
57712
  init_output();
57354
- import * as fs115 from "node:fs";
57355
- import * as path115 from "node:path";
57356
- 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";
57357
57716
  function settingsPathFor5(scope) {
57358
57717
  if (scope === "user") {
57359
- return path115.join(os61.homedir(), ".claude", "settings.json");
57718
+ return path116.join(os62.homedir(), ".claude", "settings.json");
57360
57719
  }
57361
- return path115.join(process.cwd(), ".claude", "settings.json");
57720
+ return path116.join(process.cwd(), ".claude", "settings.json");
57362
57721
  }
57363
- function dropSentinel(matchers) {
57722
+ function dropSentinel(matchers, sentinelPrefix) {
57364
57723
  let changed = false;
57365
57724
  const out = [];
57366
57725
  for (const matcher of matchers) {
57367
57726
  const innerHooks = matcher.hooks ?? [];
57368
57727
  const keptInner = innerHooks.filter((h) => {
57369
- if (typeof h.command === "string" && h.command.includes(KG_HOOK_SENTINEL_PREFIX)) {
57728
+ if (typeof h.command === "string" && h.command.includes(sentinelPrefix)) {
57370
57729
  changed = true;
57371
57730
  return false;
57372
57731
  }
@@ -57388,13 +57747,13 @@ function registerKgUninstallHookCommand(kg) {
57388
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) => {
57389
57748
  const scope = opts.scope === "user" ? "user" : "project";
57390
57749
  const filePath = settingsPathFor5(scope);
57391
- if (!fs115.existsSync(filePath)) {
57750
+ if (!fs116.existsSync(filePath)) {
57392
57751
  printInfo("kg-service hook", `no settings.json at ${filePath} \u2014 nothing to remove`);
57393
57752
  return;
57394
57753
  }
57395
57754
  let settings;
57396
57755
  try {
57397
- const raw = fs115.readFileSync(filePath, "utf-8");
57756
+ const raw = fs116.readFileSync(filePath, "utf-8");
57398
57757
  settings = raw.trim() ? JSON.parse(raw) : {};
57399
57758
  } catch (err) {
57400
57759
  printError(`could not parse ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
@@ -57402,38 +57761,36 @@ function registerKgUninstallHookCommand(kg) {
57402
57761
  return;
57403
57762
  }
57404
57763
  const preToolUse = settings.hooks?.PreToolUse;
57405
- if (!Array.isArray(preToolUse) || preToolUse.length === 0) {
57406
- 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`);
57407
57769
  return;
57408
57770
  }
57409
- const { matchers, changed } = dropSentinel(preToolUse);
57410
- 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) {
57411
57774
  printInfo("kg-service hook", `not found in ${filePath} \u2014 already uninstalled`);
57412
57775
  return;
57413
57776
  }
57414
57777
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
57415
57778
  const backupPath = `${filePath}.olam-bak.${ts}`;
57416
57779
  try {
57417
- fs115.copyFileSync(filePath, backupPath);
57780
+ fs116.copyFileSync(filePath, backupPath);
57418
57781
  } catch {
57419
57782
  }
57420
- const next = {
57421
- ...settings,
57422
- hooks: {
57423
- ...settings.hooks,
57424
- PreToolUse: matchers
57425
- }
57426
- };
57427
- if (matchers.length === 0) {
57428
- const otherStages = Object.keys(next.hooks ?? {}).filter((k) => k !== "PreToolUse");
57429
- if (otherStages.length === 0) {
57430
- delete next.hooks;
57431
- } else {
57432
- delete next.hooks.PreToolUse;
57433
- }
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;
57434
57791
  }
57435
57792
  try {
57436
- fs115.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
57793
+ fs116.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
57437
57794
  printSuccess(`kg-service hook removed from ${filePath}`);
57438
57795
  printInfo("backup", backupPath);
57439
57796
  } catch (err) {
@@ -57506,15 +57863,15 @@ function registerKgSavingsCommand(kg) {
57506
57863
 
57507
57864
  // src/commands/kg-mirror.ts
57508
57865
  init_output();
57509
- import * as fs116 from "node:fs";
57510
- import * as os62 from "node:os";
57511
- 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";
57512
57869
  function readEnvOrFile(envVar, fileName) {
57513
57870
  const fromEnv = process.env[envVar];
57514
57871
  if (fromEnv && fromEnv.length > 0) return fromEnv.trim();
57515
57872
  try {
57516
- const file = path116.join(os62.homedir(), ".olam", fileName);
57517
- 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();
57518
57875
  if (content.length > 0) return content;
57519
57876
  } catch {
57520
57877
  }
@@ -57776,14 +58133,14 @@ async function runKgMirrorGraph(symbol, options = {}) {
57776
58133
  function guessGitUrl(workspace) {
57777
58134
  try {
57778
58135
  const { spawnSync: spawnSync36 } = __require("node:child_process");
57779
- const cwd = path116.resolve(process.cwd());
58136
+ const cwd = path117.resolve(process.cwd());
57780
58137
  const candidates2 = [
57781
58138
  cwd,
57782
- path116.join(path116.dirname(cwd), workspace),
57783
- path116.join(os62.homedir(), "Projects", workspace)
58139
+ path117.join(path117.dirname(cwd), workspace),
58140
+ path117.join(os63.homedir(), "Projects", workspace)
57784
58141
  ];
57785
58142
  for (const dir of candidates2) {
57786
- if (!fs116.existsSync(path116.join(dir, ".git"))) continue;
58143
+ if (!fs117.existsSync(path117.join(dir, ".git"))) continue;
57787
58144
  const r = spawnSync36("git", ["-C", dir, "remote", "get-url", "origin"], {
57788
58145
  encoding: "utf-8"
57789
58146
  });
@@ -57821,12 +58178,12 @@ function registerKgMirrorCommand(kg) {
57821
58178
  // src/commands/kg-connect.ts
57822
58179
  init_memory_secret();
57823
58180
  init_connect_url();
57824
- import { existsSync as existsSync129, readFileSync as readFileSync115 } from "node:fs";
57825
- import { homedir as homedir79 } from "node:os";
57826
- 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";
57827
58184
  init_output();
57828
58185
  function olamFile(name) {
57829
- return join133(homedir79(), ".olam", name);
58186
+ return join134(homedir80(), ".olam", name);
57830
58187
  }
57831
58188
  function safeUrl3(url2) {
57832
58189
  try {
@@ -57847,7 +58204,7 @@ function registerKgConnectCommand(kg) {
57847
58204
  return;
57848
58205
  }
57849
58206
  const urlPath = olamFile("kg-proxy-url");
57850
- const previous = existsSync129(urlPath) ? readFileSync115(urlPath, "utf8").trim() : "";
58207
+ const previous = existsSync130(urlPath) ? readFileSync116(urlPath, "utf8").trim() : "";
57851
58208
  writeSecretAtPath(urlPath, classifierUrl);
57852
58209
  if (opts.bearer) writeSecretAtPath(olamFile("kg-proxy-bearer"), opts.bearer);
57853
58210
  if (opts.builderUrl) {
@@ -57882,8 +58239,8 @@ function registerKgConnectCommand(kg) {
57882
58239
 
57883
58240
  // src/commands/kg-build.ts
57884
58241
  function readQueueFromDisk(queuePath) {
57885
- if (!fs117.existsSync(queuePath)) return [];
57886
- const raw = fs117.readFileSync(queuePath, "utf-8");
58242
+ if (!fs118.existsSync(queuePath)) return [];
58243
+ const raw = fs118.readFileSync(queuePath, "utf-8");
57887
58244
  const entries = [];
57888
58245
  for (const line of raw.split("\n")) {
57889
58246
  const t = line.trim();
@@ -57896,14 +58253,14 @@ function readQueueFromDisk(queuePath) {
57896
58253
  return entries;
57897
58254
  }
57898
58255
  function writeQueueToDisk(queuePath, entries) {
57899
- fs117.mkdirSync(path117.dirname(queuePath), { recursive: true });
58256
+ fs118.mkdirSync(path118.dirname(queuePath), { recursive: true });
57900
58257
  const content = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length > 0 ? "\n" : "");
57901
- fs117.writeFileSync(queuePath, content, "utf-8");
58258
+ fs118.writeFileSync(queuePath, content, "utf-8");
57902
58259
  }
57903
58260
  async function runKgBuildPending(opts = {}) {
57904
58261
  const queuePath = opts.queuePath ?? (() => {
57905
- const stateDir2 = process.env["OLAM_STATE_DIR"] ?? path117.join(os63.homedir(), ".olam", "state");
57906
- 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");
57907
58264
  })();
57908
58265
  const readQueue2 = opts.readQueueFn ?? readQueueFromDisk;
57909
58266
  const writeQueue2 = opts.writeQueueFn ?? writeQueueToDisk;
@@ -57919,7 +58276,7 @@ async function runKgBuildPending(opts = {}) {
57919
58276
  const remaining = [];
57920
58277
  for (const entry of deduped) {
57921
58278
  const repoPath = entry.path;
57922
- const workspaceName = path117.basename(repoPath).toLowerCase();
58279
+ const workspaceName = path118.basename(repoPath).toLowerCase();
57923
58280
  printInfo("kg build --pending", `building ${repoPath} (workspace=${workspaceName})`);
57924
58281
  let containerPath;
57925
58282
  try {
@@ -57957,20 +58314,20 @@ async function runKgBuildPending(opts = {}) {
57957
58314
  }
57958
58315
  function resolveWorkspace(arg) {
57959
58316
  const cwd = process.cwd();
57960
- const name = arg ?? path117.basename(cwd).toLowerCase();
58317
+ const name = arg ?? path118.basename(cwd).toLowerCase();
57961
58318
  validateWorkspaceName(name);
57962
58319
  return { name, sourcePath: cwd };
57963
58320
  }
57964
58321
  function toContainerPath(hostPath) {
57965
- const home = os63.homedir();
57966
- const resolved = path117.resolve(hostPath);
57967
- 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) {
57968
58325
  throw new Error(
57969
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.`
57970
58327
  );
57971
58328
  }
57972
- const rel = path117.relative(home, resolved);
57973
- 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("/"));
57974
58331
  }
57975
58332
  async function runKgBuild(workspaceArg, options = {}) {
57976
58333
  let workspace;
@@ -57988,7 +58345,7 @@ async function runKgBuild(workspaceArg, options = {}) {
57988
58345
  return { exitCode: 2 };
57989
58346
  }
57990
58347
  const outDir = kgPristinePath(workspace.name);
57991
- fs117.mkdirSync(outDir, { recursive: true });
58348
+ fs118.mkdirSync(outDir, { recursive: true });
57992
58349
  const human = !options.json;
57993
58350
  if (human) {
57994
58351
  printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
@@ -58021,12 +58378,12 @@ async function runKgBuild(workspaceArg, options = {}) {
58021
58378
  workspace: workspace.name,
58022
58379
  graphify_path: "container"
58023
58380
  };
58024
- fs117.writeFileSync(
58025
- path117.join(outDir, "freshness.json"),
58381
+ fs118.writeFileSync(
58382
+ path118.join(outDir, "freshness.json"),
58026
58383
  JSON.stringify(freshness, null, 2) + "\n",
58027
58384
  "utf-8"
58028
58385
  );
58029
- const finalOut = path117.join(outDir, "graphify-out");
58386
+ const finalOut = path118.join(outDir, "graphify-out");
58030
58387
  if (options.json) {
58031
58388
  process.stdout.write(JSON.stringify(freshness) + "\n");
58032
58389
  } else {
@@ -58070,12 +58427,12 @@ function registerKg(program2) {
58070
58427
  // src/commands/flywheel/emit-breadcrumb.ts
58071
58428
  init_file_lock();
58072
58429
  import { mkdirSync as mkdirSync77, appendFileSync as appendFileSync6 } from "node:fs";
58073
- import { homedir as homedir81 } from "node:os";
58074
- 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";
58075
58432
  import { randomUUID as randomUUID4 } from "node:crypto";
58076
58433
  var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info", "warn"]);
58077
58434
  var PROMPT_FEEDING_FIELDS = ["extracted_pattern", "severity", "affected_persona", "proposed_edit"];
58078
- var BREADCRUMBS_BASE = join135(homedir81(), ".local", "share", "claude", "breadcrumbs");
58435
+ var BREADCRUMBS_BASE = join136(homedir82(), ".local", "share", "claude", "breadcrumbs");
58079
58436
  var LOCK_FILENAME = ".flywheel-emit.lock";
58080
58437
  function buildRecord(opts) {
58081
58438
  const rec = {
@@ -58122,7 +58479,7 @@ function validatePromptFeeding(rec) {
58122
58479
  }
58123
58480
  function destPath(projectSlug) {
58124
58481
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
58125
- return join135(BREADCRUMBS_BASE, projectSlug, `${today}.jsonl`);
58482
+ return join136(BREADCRUMBS_BASE, projectSlug, `${today}.jsonl`);
58126
58483
  }
58127
58484
  async function emitBreadcrumb(opts) {
58128
58485
  if (!VALID_SEVERITIES.has(opts.severity)) {
@@ -58141,14 +58498,14 @@ async function emitBreadcrumb(opts) {
58141
58498
  );
58142
58499
  process.exit(2);
58143
58500
  }
58144
- const path121 = destPath(rec.project_slug);
58145
- const lockDir = dirname74(path121);
58501
+ const path122 = destPath(rec.project_slug);
58502
+ const lockDir = dirname74(path122);
58146
58503
  mkdirSync77(lockDir, { recursive: true });
58147
58504
  const line = JSON.stringify(rec) + "\n";
58148
58505
  await withFileLock(
58149
58506
  lockDir,
58150
58507
  () => {
58151
- appendFileSync6(path121, line, "utf8");
58508
+ appendFileSync6(path122, line, "utf8");
58152
58509
  },
58153
58510
  {
58154
58511
  lockFilename: LOCK_FILENAME,
@@ -58164,7 +58521,7 @@ async function emitBreadcrumb(opts) {
58164
58521
  acquireTimeoutMs: 2e4
58165
58522
  }
58166
58523
  );
58167
- process.stdout.write(`[K7-emit] ${path121}: ${rec.extracted_pattern} (${rec.severity})
58524
+ process.stdout.write(`[K7-emit] ${path122}: ${rec.extracted_pattern} (${rec.severity})
58168
58525
  `);
58169
58526
  }
58170
58527
  function registerFlywheelEmitBreadcrumb(parent) {
@@ -58229,7 +58586,7 @@ function registerFlywheelK5Score(parent) {
58229
58586
  }
58230
58587
 
58231
58588
  // src/commands/flywheel/k5-validate.ts
58232
- import { readFileSync as readFileSync117, statSync as statSync33 } from "node:fs";
58589
+ import { readFileSync as readFileSync118, statSync as statSync33 } from "node:fs";
58233
58590
  import { parse as parseYAML } from "yaml";
58234
58591
  var K5_DIMS = ["direction", "approach", "open_questions", "constraints", "reuse"];
58235
58592
  var MAX_PLAN_BYTES = 1048576;
@@ -58287,9 +58644,9 @@ function validateK5ScoredAt(scoredAt) {
58287
58644
  }
58288
58645
  return null;
58289
58646
  }
58290
- function validatePlan(path121) {
58647
+ function validatePlan(path122) {
58291
58648
  const emptyJson = (status2, errors2) => ({
58292
- path: path121,
58649
+ path: path122,
58293
58650
  status: status2,
58294
58651
  errors: errors2,
58295
58652
  k5_scores: null,
@@ -58300,33 +58657,33 @@ function validatePlan(path121) {
58300
58657
  });
58301
58658
  let stat;
58302
58659
  try {
58303
- stat = statSync33(path121);
58660
+ stat = statSync33(path122);
58304
58661
  } catch (err) {
58305
- 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"}`;
58306
58663
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58307
58664
  }
58308
58665
  if (stat.size > MAX_PLAN_BYTES) {
58309
- 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`;
58310
58667
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58311
58668
  }
58312
58669
  let text;
58313
58670
  try {
58314
- text = readFileSync117(path121, "utf8");
58671
+ text = readFileSync118(path122, "utf8");
58315
58672
  } catch (err) {
58316
- 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"}`;
58317
58674
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58318
58675
  }
58319
58676
  if (!text.replace(/^/, "").startsWith("---")) {
58320
- const m = `FAIL ${path121}: no YAML frontmatter (missing opening --- marker)`;
58677
+ const m = `FAIL ${path122}: no YAML frontmatter (missing opening --- marker)`;
58321
58678
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58322
58679
  }
58323
58680
  if (!text.includes("\n---", 3)) {
58324
- const m = `FAIL ${path121}: frontmatter block never closed (missing closing --- marker)`;
58681
+ const m = `FAIL ${path122}: frontmatter block never closed (missing closing --- marker)`;
58325
58682
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58326
58683
  }
58327
58684
  const fm = extractFrontmatter(text);
58328
58685
  if (fm === null) {
58329
- const m = `FAIL ${path121}: frontmatter could not be parsed as YAML`;
58686
+ const m = `FAIL ${path122}: frontmatter could not be parsed as YAML`;
58330
58687
  return { ok: false, message: m, json: emptyJson("error", [m]) };
58331
58688
  }
58332
58689
  const hasScores = "k5_scores" in fm;
@@ -58336,7 +58693,7 @@ function validatePlan(path121) {
58336
58693
  if (!hasScores && !hasBoost && !hasComposite && !hasScoredAt) {
58337
58694
  return {
58338
58695
  ok: true,
58339
- message: `PASS ${path121}: k5_scores absent (acceptable \u2014 legacy plan)`,
58696
+ message: `PASS ${path122}: k5_scores absent (acceptable \u2014 legacy plan)`,
58340
58697
  json: emptyJson("absent", [])
58341
58698
  };
58342
58699
  }
@@ -58360,12 +58717,12 @@ function validatePlan(path121) {
58360
58717
  if (errors.length > 0) {
58361
58718
  return {
58362
58719
  ok: false,
58363
- message: `FAIL ${path121}: ${errors.join("; ")}`,
58720
+ message: `FAIL ${path122}: ${errors.join("; ")}`,
58364
58721
  json: emptyJson("fail", errors)
58365
58722
  };
58366
58723
  }
58367
58724
  const json = emptyJson("pass", []);
58368
- const lines = [`PASS ${path121}: k5_scores valid`];
58725
+ const lines = [`PASS ${path122}: k5_scores valid`];
58369
58726
  if (hasScores && typeof fm.k5_scores === "object" && fm.k5_scores !== null) {
58370
58727
  const scoresObj = fm.k5_scores;
58371
58728
  const parsed = {};
@@ -58435,7 +58792,7 @@ function registerFlywheelK5Validate(parent) {
58435
58792
  }
58436
58793
 
58437
58794
  // src/commands/flywheel/k10-measure.ts
58438
- import { readFileSync as readFileSync118 } from "node:fs";
58795
+ import { readFileSync as readFileSync119 } from "node:fs";
58439
58796
 
58440
58797
  // ../core/dist/lib/k10-budget.js
58441
58798
  var K10_TOKEN_CAP = 5500;
@@ -58492,7 +58849,7 @@ function registerFlywheelK10Measure(parent) {
58492
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) => {
58493
58850
  let upstreamText;
58494
58851
  try {
58495
- upstreamText = readFileSync118(opts.upstream, "utf8");
58852
+ upstreamText = readFileSync119(opts.upstream, "utf8");
58496
58853
  } catch (err) {
58497
58854
  process.stderr.write(
58498
58855
  `[k10-measure-error] cannot read upstream ${opts.upstream}: ${err instanceof Error ? err.message : "unknown"}
@@ -58504,7 +58861,7 @@ function registerFlywheelK10Measure(parent) {
58504
58861
  let overlayTokens = null;
58505
58862
  if (opts.overlay !== void 0) {
58506
58863
  try {
58507
- const overlayText = readFileSync118(opts.overlay, "utf8");
58864
+ const overlayText = readFileSync119(opts.overlay, "utf8");
58508
58865
  overlayTokens = tokensFromText(overlayText);
58509
58866
  } catch (err) {
58510
58867
  process.stderr.write(
@@ -58538,13 +58895,13 @@ function registerFlywheelK10Measure(parent) {
58538
58895
  }
58539
58896
 
58540
58897
  // src/commands/flywheel/check-persona-skeleton.ts
58541
- import { existsSync as existsSync131, readFileSync as readFileSync119, statSync as statSync34 } from "node:fs";
58542
- import { homedir as homedir82 } from "node:os";
58543
- 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";
58544
58901
  import { parse as parseYAML2 } from "yaml";
58545
58902
  var CHARS_PER_TOKEN3 = 4;
58546
58903
  var K10_TOKEN_CAP2 = 6e3;
58547
- var AGENTS_DIR = join136(homedir82(), ".claude", "agents");
58904
+ var AGENTS_DIR = join137(homedir83(), ".claude", "agents");
58548
58905
  var REQUIRED_FRONTMATTER_KEYS = ["name", "description", "allowed-tools"];
58549
58906
  var REQUIRED_SECTIONS = [
58550
58907
  "## Role",
@@ -58584,10 +58941,10 @@ function countNamedPatterns(sectionText) {
58584
58941
  return Math.max(h3Count, bulletCount);
58585
58942
  }
58586
58943
  function checkFile(filepath) {
58587
- if (!existsSync131(filepath) || !statSync34(filepath).isFile()) {
58944
+ if (!existsSync132(filepath) || !statSync34(filepath).isFile()) {
58588
58945
  return { passed: false, failures: [`file not found: ${filepath}`], tokens: 0, mergedSkipped: false };
58589
58946
  }
58590
- const text = readFileSync119(filepath, "utf8");
58947
+ const text = readFileSync120(filepath, "utf8");
58591
58948
  const { fm, body } = parseFile(text);
58592
58949
  const failures = [];
58593
58950
  for (const key of REQUIRED_FRONTMATTER_KEYS) {
@@ -58600,8 +58957,8 @@ function checkFile(filepath) {
58600
58957
  const agentName = typeof fm.name === "string" ? fm.name : "";
58601
58958
  let mergedSkipped = false;
58602
58959
  if (!isNewAgent && agentName !== "") {
58603
- const upstreamPath = join136(AGENTS_DIR, `${agentName}.md`);
58604
- if (existsSync131(upstreamPath)) {
58960
+ const upstreamPath = join137(AGENTS_DIR, `${agentName}.md`);
58961
+ if (existsSync132(upstreamPath)) {
58605
58962
  mergedSkipped = true;
58606
58963
  }
58607
58964
  }
@@ -58653,7 +59010,7 @@ Results: ${passCount} passed, ${failCount} failed
58653
59010
  }
58654
59011
 
58655
59012
  // src/commands/flywheel/diversity-check.ts
58656
- import { readFileSync as readFileSync120 } from "node:fs";
59013
+ import { readFileSync as readFileSync121 } from "node:fs";
58657
59014
  import { basename as basename15 } from "node:path";
58658
59015
  import { globSync as globSync2 } from "node:fs";
58659
59016
 
@@ -58763,7 +59120,7 @@ function registerFlywheelDiversityCheck(parent) {
58763
59120
  const personas = /* @__PURE__ */ new Map();
58764
59121
  for (const filepath of files) {
58765
59122
  try {
58766
- const body = readFileSync120(filepath, "utf8");
59123
+ const body = readFileSync121(filepath, "utf8");
58767
59124
  if (body.trim().length > 0) {
58768
59125
  personas.set(basename15(filepath, ".md"), body);
58769
59126
  }
@@ -58794,9 +59151,9 @@ ${formatRedivergencePrompt(persona_a, persona_b, score, void 0, void 0, threshol
58794
59151
  }
58795
59152
 
58796
59153
  // src/commands/flywheel/ping.ts
58797
- import { mkdirSync as mkdirSync78, writeFileSync as writeFileSync73 } from "node:fs";
58798
- import { homedir as homedir83 } from "node:os";
58799
- 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";
58800
59157
  var COLD_START_BUDGET_GOOD_MS = 200;
58801
59158
  var COLD_START_BUDGET_FAIR_MS = 500;
58802
59159
  function classifyColdStart(coldStartMs) {
@@ -58812,9 +59169,9 @@ function readOlamVersion() {
58812
59169
  }
58813
59170
  }
58814
59171
  function writeBaseline(record) {
58815
- const baselinePath = join137(homedir83(), ".local", "share", "olam", "flywheel-baseline.json");
59172
+ const baselinePath = join138(homedir84(), ".local", "share", "olam", "flywheel-baseline.json");
58816
59173
  mkdirSync78(dirname75(baselinePath), { recursive: true });
58817
- writeFileSync73(baselinePath, JSON.stringify(record, null, 2) + "\n", "utf8");
59174
+ writeFileSync74(baselinePath, JSON.stringify(record, null, 2) + "\n", "utf8");
58818
59175
  return baselinePath;
58819
59176
  }
58820
59177
  function registerFlywheelPing(parent) {
@@ -58849,30 +59206,30 @@ function registerFlywheelPing(parent) {
58849
59206
  }
58850
59207
 
58851
59208
  // src/commands/flywheel/session-start.ts
58852
- import { readFileSync as readFileSync121, statSync as statSync35 } from "node:fs";
58853
- import { homedir as homedir84 } from "node:os";
58854
- 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";
58855
59212
  var SESSIONSTART_HOOK_SENTINEL = "olam-sessionstart-context-hook-v1";
58856
59213
  var MAX_WAKE_BRIEF_BYTES = 8 * 1024;
58857
59214
  var MAX_ACTIVE_WORLD_BYTES = 4 * 1024;
58858
59215
  var MAX_CONFIG_BYTES = 8 * 1024;
58859
- function tryReadCapped(path121, maxBytes) {
59216
+ function tryReadCapped(path122, maxBytes) {
58860
59217
  try {
58861
- const stat = statSync35(path121);
59218
+ const stat = statSync35(path122);
58862
59219
  if (!stat.isFile() || stat.size === 0) return null;
58863
59220
  if (stat.size > maxBytes) {
58864
59221
  process.stderr.write(
58865
- `[${SESSIONSTART_HOOK_SENTINEL}] skipping ${path121}: ${stat.size} bytes > ${maxBytes} cap
59222
+ `[${SESSIONSTART_HOOK_SENTINEL}] skipping ${path122}: ${stat.size} bytes > ${maxBytes} cap
58866
59223
  `
58867
59224
  );
58868
59225
  return null;
58869
59226
  }
58870
- return readFileSync121(path121, "utf-8");
59227
+ return readFileSync122(path122, "utf-8");
58871
59228
  } catch (err) {
58872
59229
  const code = err?.code;
58873
59230
  if (code === "ENOENT") return null;
58874
59231
  process.stderr.write(
58875
- `[${SESSIONSTART_HOOK_SENTINEL}] read failed for ${path121}: ${err?.message ?? err}
59232
+ `[${SESSIONSTART_HOOK_SENTINEL}] read failed for ${path122}: ${err?.message ?? err}
58876
59233
  `
58877
59234
  );
58878
59235
  return null;
@@ -58892,15 +59249,15 @@ function tryParseJson(raw, label) {
58892
59249
  }
58893
59250
  function buildContextBlock(olamHome7) {
58894
59251
  const config = tryParseJson(
58895
- tryReadCapped(join138(olamHome7, "config.json"), MAX_CONFIG_BYTES),
59252
+ tryReadCapped(join139(olamHome7, "config.json"), MAX_CONFIG_BYTES),
58896
59253
  "config.json"
58897
59254
  );
58898
59255
  const activeWorld = tryParseJson(
58899
- tryReadCapped(join138(olamHome7, "state", "active-world.json"), MAX_ACTIVE_WORLD_BYTES),
59256
+ tryReadCapped(join139(olamHome7, "state", "active-world.json"), MAX_ACTIVE_WORLD_BYTES),
58900
59257
  "active-world.json"
58901
59258
  );
58902
59259
  const wakeBrief = tryReadCapped(
58903
- join138(olamHome7, "state", "wake-brief.md"),
59260
+ join139(olamHome7, "state", "wake-brief.md"),
58904
59261
  MAX_WAKE_BRIEF_BYTES
58905
59262
  );
58906
59263
  const sections = [];
@@ -58952,7 +59309,7 @@ function registerFlywheelSessionStart(parent) {
58952
59309
  parent.command("session-start").description(
58953
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."
58954
59311
  ).action(() => {
58955
- const olamHome7 = process.env.OLAM_HOME ?? join138(homedir84(), ".olam");
59312
+ const olamHome7 = process.env.OLAM_HOME ?? join139(homedir85(), ".olam");
58956
59313
  emitSessionStartContext(olamHome7);
58957
59314
  process.exit(0);
58958
59315
  });
@@ -58961,9 +59318,9 @@ function registerFlywheelSessionStart(parent) {
58961
59318
  // src/commands/flywheel/install-sessionstart-hook.ts
58962
59319
  init_merge_settings();
58963
59320
  init_output();
58964
- import { existsSync as existsSync132, copyFileSync as copyFileSync19, mkdirSync as mkdirSync79, readFileSync as readFileSync122, unlinkSync as unlinkSync26, writeFileSync as writeFileSync74 } from "node:fs";
58965
- import { homedir as homedir85 } from "node:os";
58966
- 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";
58967
59324
  var SESSIONSTART_HOOK_STAGE = "SessionStart";
58968
59325
  var SESSIONSTART_HOOK_TIMEOUT_MS = 5e3;
58969
59326
  var NOOP_GUARD = "command -v olam >/dev/null 2>&1 || exit 0;";
@@ -58980,11 +59337,11 @@ function buildSessionStartHookEntry() {
58980
59337
  };
58981
59338
  }
58982
59339
  function settingsPathFor6(scope, cwd) {
58983
- if (scope === "user") return join139(homedir85(), ".claude", "settings.json");
58984
- 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");
58985
59342
  }
58986
59343
  function backup4(filePath) {
58987
- if (!existsSync132(filePath)) return null;
59344
+ if (!existsSync133(filePath)) return null;
58988
59345
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
58989
59346
  const backupPath = `${filePath}.olam-bak.${ts}`;
58990
59347
  copyFileSync19(filePath, backupPath);
@@ -59010,8 +59367,8 @@ function installSessionStartHook(filePath) {
59010
59367
  return { status: result.status, filePath, backupPath };
59011
59368
  }
59012
59369
  function uninstallSessionStartHook(filePath) {
59013
- if (!existsSync132(filePath)) return { status: "no-settings", filePath };
59014
- const raw = readFileSync122(filePath, "utf-8");
59370
+ if (!existsSync133(filePath)) return { status: "no-settings", filePath };
59371
+ const raw = readFileSync123(filePath, "utf-8");
59015
59372
  const settings = raw.trim() ? JSON.parse(raw) : {};
59016
59373
  const matchers = settings.hooks?.SessionStart;
59017
59374
  if (!Array.isArray(matchers) || matchers.length === 0) {
@@ -59051,7 +59408,7 @@ function uninstallSessionStartHook(filePath) {
59051
59408
  delete next.hooks.SessionStart;
59052
59409
  }
59053
59410
  }
59054
- writeFileSync74(filePath, JSON.stringify(next, null, 2) + "\n");
59411
+ writeFileSync75(filePath, JSON.stringify(next, null, 2) + "\n");
59055
59412
  return { status: "removed", filePath };
59056
59413
  }
59057
59414
  function registerFlywheelInstallSessionStartHook(parent) {
@@ -59379,14 +59736,14 @@ init_manager();
59379
59736
  init_context();
59380
59737
  init_output();
59381
59738
  import { spawnSync as defaultSpawnSync } from "node:child_process";
59382
- import * as fs118 from "node:fs";
59383
- import * as os64 from "node:os";
59384
- 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";
59385
59742
  function devboxContainerName(worldId) {
59386
59743
  return `olam-${worldId}-devbox`;
59387
59744
  }
59388
59745
  function olamHomeDir() {
59389
- return process.env["OLAM_HOME"] ?? path118.join(os64.homedir(), ".olam");
59746
+ return process.env["OLAM_HOME"] ?? path119.join(os65.homedir(), ".olam");
59390
59747
  }
59391
59748
  function defaultRestartContainer(name, spawn14 = defaultSpawnSync) {
59392
59749
  const r = spawn14("docker", ["restart", "--time", "30", name], {
@@ -59399,8 +59756,8 @@ function defaultRestartContainer(name, spawn14 = defaultSpawnSync) {
59399
59756
  };
59400
59757
  }
59401
59758
  function defaultAppendAuditLog(homeDir, line) {
59402
- fs118.mkdirSync(homeDir, { recursive: true });
59403
- 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", {
59404
59761
  encoding: "utf-8"
59405
59762
  });
59406
59763
  }
@@ -59445,19 +59802,19 @@ async function doRekey(worldId, deps) {
59445
59802
  );
59446
59803
  const rotatedAt = deps.now().toISOString();
59447
59804
  const homeDir = deps.olamHomeDir();
59448
- const worldDir = path118.join(homeDir, "worlds", worldId);
59449
- fs118.mkdirSync(worldDir, { recursive: true });
59450
- 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");
59451
59808
  const payload = {
59452
59809
  worldRoleName,
59453
59810
  password,
59454
59811
  rotatedAt
59455
59812
  };
59456
- fs118.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
59813
+ fs119.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
59457
59814
  encoding: "utf-8",
59458
59815
  mode: 384
59459
59816
  });
59460
- fs118.chmodSync(credentialsPath, 384);
59817
+ fs119.chmodSync(credentialsPath, 384);
59461
59818
  const restart = deps.restartContainer(devboxContainerName(worldId));
59462
59819
  deps.appendAuditLog(`${rotatedAt} ${worldId} rekey`);
59463
59820
  if (!restart.ok) {
@@ -59523,9 +59880,9 @@ function registerRekey(program2) {
59523
59880
  // src/commands/yolo.ts
59524
59881
  init_output();
59525
59882
  import * as childProcess2 from "node:child_process";
59526
- import * as fs119 from "node:fs";
59527
- import * as os65 from "node:os";
59528
- 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";
59529
59886
  import pc51 from "picocolors";
59530
59887
  var YOLO_BOOT_DELAY_MS = 8e3;
59531
59888
  var CAPTURE_DELAY_MS = 4e3;
@@ -59553,7 +59910,7 @@ function defaultSleeper(ms) {
59553
59910
  function detectTmuxBinary(runner = defaultRunner) {
59554
59911
  for (const p of TMUX_PROBE_PATHS) {
59555
59912
  if (TMUX_SHIM_MARKERS.some((m) => p.includes(m))) continue;
59556
- if (fs119.existsSync(p)) return p;
59913
+ if (fs120.existsSync(p)) return p;
59557
59914
  }
59558
59915
  const result = runner("which", ["tmux"]);
59559
59916
  if (result.status === 0 && result.stdout) {
@@ -59567,17 +59924,17 @@ function resolveWorktreeRoot(runner = defaultRunner, cwd = process.cwd()) {
59567
59924
  if (process.env["OLAM_TMUX_YOLO_ROOT"]) {
59568
59925
  return process.env["OLAM_TMUX_YOLO_ROOT"];
59569
59926
  }
59570
- const rootFile = path119.join(os65.homedir(), ".olam-tmux-yolo-root");
59571
- if (fs119.existsSync(rootFile)) {
59572
- 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();
59573
59930
  if (line) return line;
59574
59931
  }
59575
59932
  const gitResult = runner("git", ["rev-parse", "--show-toplevel"], { cwd });
59576
59933
  if (gitResult.status !== 0 || !gitResult.stdout) {
59577
- return path119.join(cwd, "..", path119.basename(cwd) + "-wt");
59934
+ return path120.join(cwd, "..", path120.basename(cwd) + "-wt");
59578
59935
  }
59579
59936
  const repoRoot = gitResult.stdout.trim();
59580
- return path119.join(path119.dirname(repoRoot), path119.basename(repoRoot) + "-wt");
59937
+ return path120.join(path120.dirname(repoRoot), path120.basename(repoRoot) + "-wt");
59581
59938
  }
59582
59939
  function resolveYoloCommand(runner = defaultRunner) {
59583
59940
  const result = runner("bash", ["-lc", "command -v yolo"]);
@@ -59603,7 +59960,7 @@ async function spawnYolo(opts) {
59603
59960
  );
59604
59961
  }
59605
59962
  try {
59606
- fs119.accessSync(opts.promptFile, fs119.constants.R_OK);
59963
+ fs120.accessSync(opts.promptFile, fs120.constants.R_OK);
59607
59964
  } catch {
59608
59965
  throw new Error(
59609
59966
  `Prompt file not found or not readable: ${opts.promptFile}
@@ -59611,7 +59968,7 @@ Create the file with your task description and try again.`
59611
59968
  );
59612
59969
  }
59613
59970
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59614
- const worktreePath = path119.join(worktreeRoot, opts.name);
59971
+ const worktreePath = path120.join(worktreeRoot, opts.name);
59615
59972
  const branchCheck = runner("git", ["show-ref", "--verify", "--quiet", `refs/heads/${opts.branch}`], { cwd });
59616
59973
  if (branchCheck.status === 0) {
59617
59974
  throw new Error(
@@ -59619,7 +59976,7 @@ Create the file with your task description and try again.`
59619
59976
  Run: olam yolo --cleanup ${opts.name} (or use --force to bypass the merged check)`
59620
59977
  );
59621
59978
  }
59622
- if (fs119.existsSync(worktreePath)) {
59979
+ if (fs120.existsSync(worktreePath)) {
59623
59980
  throw new Error(
59624
59981
  `Worktree directory already exists: ${worktreePath}
59625
59982
  Run: olam yolo --cleanup ${opts.name} to remove it first.`
@@ -59684,10 +60041,10 @@ function listYoloWindows(opts = {}) {
59684
60041
  const windowNames = listResult.stdout.split("\n").map((n) => n.trim()).filter(Boolean);
59685
60042
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59686
60043
  return windowNames.map((name) => {
59687
- const worktreePath = path119.join(worktreeRoot, name);
60044
+ const worktreePath = path120.join(worktreeRoot, name);
59688
60045
  return {
59689
60046
  name,
59690
- worktreePath: fs119.existsSync(worktreePath) ? worktreePath : void 0
60047
+ worktreePath: fs120.existsSync(worktreePath) ? worktreePath : void 0
59691
60048
  };
59692
60049
  });
59693
60050
  }
@@ -59696,7 +60053,7 @@ async function cleanupYolo(opts) {
59696
60053
  const cwd = opts.cwd ?? process.cwd();
59697
60054
  const mainBranch = opts.mainBranch ?? "main";
59698
60055
  const worktreeRoot = resolveWorktreeRoot(runner, cwd);
59699
- const worktreePath = path119.join(worktreeRoot, opts.name);
60056
+ const worktreePath = path120.join(worktreeRoot, opts.name);
59700
60057
  const worktreeList = runner("git", ["worktree", "list", "--porcelain"], { cwd });
59701
60058
  let branch;
59702
60059
  if (worktreeList.status === 0) {
@@ -59821,18 +60178,18 @@ function registerYolo(program2) {
59821
60178
  }
59822
60179
 
59823
60180
  // src/pleri-config.ts
59824
- import * as fs120 from "node:fs";
59825
- import * as path120 from "node:path";
60181
+ import * as fs121 from "node:fs";
60182
+ import * as path121 from "node:path";
59826
60183
  function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
59827
60184
  if (process.env.PLERI_BASE_URL) {
59828
60185
  return true;
59829
60186
  }
59830
- const configPath = path120.join(configDir, "config.yaml");
59831
- if (!fs120.existsSync(configPath)) {
60187
+ const configPath = path121.join(configDir, "config.yaml");
60188
+ if (!fs121.existsSync(configPath)) {
59832
60189
  return false;
59833
60190
  }
59834
60191
  try {
59835
- const contents = fs120.readFileSync(configPath, "utf8");
60192
+ const contents = fs121.readFileSync(configPath, "utf8");
59836
60193
  return /^[^#\n]*\bpleri:/m.test(contents);
59837
60194
  } catch {
59838
60195
  return false;
@@ -59909,7 +60266,7 @@ var HELP_GROUPS = [
59909
60266
  // crystallize is conditionally hidden (requires Pleri); kept here so it
59910
60267
  // lands in the right group when Pleri IS configured.
59911
60268
  title: "Dev tools",
59912
- names: ["begin", "completion", "crystallize", "hermes", "lanes", "policy-check", "pr", "stop", "yolo"]
60269
+ names: ["begin", "completion", "crystallize", "hermes", "inbox", "lanes", "policy-check", "pr", "stop", "yolo"]
59913
60270
  },
59914
60271
  {
59915
60272
  // Alphabetical within group. `config` lives in 'Cloud setup' above.
@@ -60131,7 +60488,7 @@ try {
60131
60488
  }
60132
60489
  }
60133
60490
  void scheduleUpgradeCheck(cliVersion);
60134
- if (process.argv.length <= 2 && !existsSync135(join143(homedir88(), ".olam", "config.json"))) {
60491
+ if (process.argv.length <= 2 && !existsSync136(join144(homedir89(), ".olam", "config.json"))) {
60135
60492
  process.stdout.write("No olam config found yet.\n");
60136
60493
  process.stdout.write("Get started: olam setup\n\n");
60137
60494
  }