@deeplake/hivemind 0.7.79 → 0.7.80

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.
@@ -366,38 +366,38 @@ function readQueue() {
366
366
  return { queue: [] };
367
367
  }
368
368
  }
369
- function _isQueuePathInsideHome(path, home) {
370
- const r = resolve(path);
369
+ function _isQueuePathInsideHome(path2, home) {
370
+ const r = resolve(path2);
371
371
  const h = resolve(home);
372
372
  return r.startsWith(h + "/") || r === h;
373
373
  }
374
374
  function writeQueue(q) {
375
- const path = queuePath();
375
+ const path2 = queuePath();
376
376
  const home = resolve(homedir3());
377
- if (!_isQueuePathInsideHome(path, home)) {
378
- throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
377
+ if (!_isQueuePathInsideHome(path2, home)) {
378
+ throw new Error(`notifications-queue write blocked: ${path2} is outside ${home}`);
379
379
  }
380
380
  mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
381
- const tmp = `${path}.${process.pid}.tmp`;
381
+ const tmp = `${path2}.${process.pid}.tmp`;
382
382
  writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
383
- renameSync(tmp, path);
383
+ renameSync(tmp, path2);
384
384
  }
385
385
  async function withQueueLock(fn) {
386
- const path = lockPath();
386
+ const path2 = lockPath();
387
387
  mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
388
388
  let fd = null;
389
389
  for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
390
390
  try {
391
- fd = openSync(path, "wx", 384);
391
+ fd = openSync(path2, "wx", 384);
392
392
  break;
393
393
  } catch (e) {
394
394
  const code = e.code;
395
395
  if (code !== "EEXIST")
396
396
  throw e;
397
397
  try {
398
- const age = Date.now() - statSync(path).mtimeMs;
398
+ const age = Date.now() - statSync(path2).mtimeMs;
399
399
  if (age > LOCK_STALE_MS) {
400
- unlinkSync(path);
400
+ unlinkSync(path2);
401
401
  continue;
402
402
  }
403
403
  } catch {
@@ -418,7 +418,7 @@ async function withQueueLock(fn) {
418
418
  } catch {
419
419
  }
420
420
  try {
421
- unlinkSync(path);
421
+ unlinkSync(path2);
422
422
  } catch {
423
423
  }
424
424
  }
@@ -694,9 +694,9 @@ var DeeplakeApi = class {
694
694
  }
695
695
  }
696
696
  /** Update specific columns on a row by path. */
697
- async updateColumns(path, columns) {
697
+ async updateColumns(path2, columns) {
698
698
  const setClauses = Object.entries(columns).map(([col, val]) => typeof val === "number" ? `${col} = ${val}` : `${col} = '${sqlStr(String(val))}'`).join(", ");
699
- await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path)}'`);
699
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path2)}'`);
700
700
  }
701
701
  // ── Convenience ─────────────────────────────────────────────────────────────
702
702
  /** Create a BM25 search index on a column. */
@@ -1128,8 +1128,8 @@ function formatToolCall(obj) {
1128
1128
  input: ${formatToolInput(obj?.tool_input)}
1129
1129
  response: ${formatToolResponse(obj?.tool_response, obj?.tool_input, obj?.tool_name)}`;
1130
1130
  }
1131
- function normalizeContent(path, raw) {
1132
- if (!path.includes("/sessions/"))
1131
+ function normalizeContent(path2, raw) {
1132
+ if (!path2.includes("/sessions/"))
1133
1133
  return raw;
1134
1134
  if (!raw || raw[0] !== "{")
1135
1135
  return raw;
@@ -1856,13 +1856,13 @@ var _migrated = false;
1856
1856
  function readUserConfig() {
1857
1857
  if (_cache !== null)
1858
1858
  return _cache;
1859
- const path = _configPath();
1860
- if (!existsSync4(path)) {
1859
+ const path2 = _configPath();
1860
+ if (!existsSync4(path2)) {
1861
1861
  _cache = {};
1862
1862
  return _cache;
1863
1863
  }
1864
1864
  try {
1865
- const raw = readFileSync6(path, "utf-8");
1865
+ const raw = readFileSync6(path2, "utf-8");
1866
1866
  const parsed = JSON.parse(raw);
1867
1867
  _cache = isPlainObject(parsed) ? parsed : {};
1868
1868
  } catch {
@@ -1873,13 +1873,13 @@ function readUserConfig() {
1873
1873
  function writeUserConfig(patch) {
1874
1874
  const current = readUserConfig();
1875
1875
  const merged = deepMerge(current, patch);
1876
- const path = _configPath();
1877
- const dir = dirname(path);
1876
+ const path2 = _configPath();
1877
+ const dir = dirname(path2);
1878
1878
  if (!existsSync4(dir))
1879
1879
  mkdirSync4(dir, { recursive: true });
1880
- const tmp = `${path}.tmp.${process.pid}`;
1880
+ const tmp = `${path2}.tmp.${process.pid}`;
1881
1881
  writeFileSync4(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1882
- renameSync2(tmp, path);
1882
+ renameSync2(tmp, path2);
1883
1883
  _cache = merged;
1884
1884
  return merged;
1885
1885
  }
@@ -2350,18 +2350,18 @@ function lastBuildPath(baseDir, worktreeId) {
2350
2350
  return join11(baseDir, ".last-build.json");
2351
2351
  }
2352
2352
  function readLastBuild(baseDir, worktreeId) {
2353
- let path = lastBuildPath(baseDir, worktreeId);
2354
- if (!existsSync5(path)) {
2353
+ let path2 = lastBuildPath(baseDir, worktreeId);
2354
+ if (!existsSync5(path2)) {
2355
2355
  if (worktreeId === void 0)
2356
2356
  return null;
2357
2357
  const legacy = lastBuildPath(baseDir, void 0);
2358
2358
  if (!existsSync5(legacy))
2359
2359
  return null;
2360
- path = legacy;
2360
+ path2 = legacy;
2361
2361
  }
2362
2362
  let raw;
2363
2363
  try {
2364
- raw = readFileSync7(path, "utf8");
2364
+ raw = readFileSync7(path2, "utf8");
2365
2365
  } catch {
2366
2366
  return null;
2367
2367
  }
@@ -2970,18 +2970,18 @@ function workTreeIdFor(cwd) {
2970
2970
  return createHash3("sha256").update(cwd).digest("hex").slice(0, 16);
2971
2971
  }
2972
2972
  function handleGraphVfs(subpath, cwd) {
2973
- const path = subpath.replace(/^\/+/, "");
2974
- if (path === "" || path === "/") {
2973
+ const path2 = subpath.replace(/^\/+/, "");
2974
+ if (path2 === "" || path2 === "/") {
2975
2975
  return { kind: "ok", body: dirListing() };
2976
2976
  }
2977
- if (path === "index.md" || path === "index") {
2977
+ if (path2 === "index.md" || path2 === "index") {
2978
2978
  return loadSnapshotOrError(cwd, (snap, baseDir) => ({
2979
2979
  kind: "ok",
2980
2980
  body: renderIndex(snap, baseDir, cwd)
2981
2981
  }));
2982
2982
  }
2983
- if (path.startsWith("find/")) {
2984
- const pattern = path.slice("find/".length);
2983
+ if (path2.startsWith("find/")) {
2984
+ const pattern = path2.slice("find/".length);
2985
2985
  if (pattern === "") {
2986
2986
  return { kind: "not-found", message: "find/ requires a pattern: cat memory/graph/find/<keyword>" };
2987
2987
  }
@@ -2990,8 +2990,8 @@ function handleGraphVfs(subpath, cwd) {
2990
2990
  body: renderFind(snap, pattern, baseDir, workTreeIdFor(cwd))
2991
2991
  }));
2992
2992
  }
2993
- if (path.startsWith("show/")) {
2994
- const key = path.slice("show/".length);
2993
+ if (path2.startsWith("show/")) {
2994
+ const key = path2.slice("show/".length);
2995
2995
  if (key === "") {
2996
2996
  return { kind: "not-found", message: "show/ requires a handle or pattern" };
2997
2997
  }
@@ -3000,8 +3000,8 @@ function handleGraphVfs(subpath, cwd) {
3000
3000
  body: renderShow(snap, key, baseDir, workTreeIdFor(cwd))
3001
3001
  }));
3002
3002
  }
3003
- if (path.startsWith("query/")) {
3004
- const pattern = path.slice("query/".length);
3003
+ if (path2.startsWith("query/")) {
3004
+ const pattern = path2.slice("query/".length);
3005
3005
  if (pattern === "") {
3006
3006
  return { kind: "not-found", message: "query/ requires a pattern: cat memory/graph/query/<keyword>" };
3007
3007
  }
@@ -3010,28 +3010,28 @@ function handleGraphVfs(subpath, cwd) {
3010
3010
  body: renderQuery(snap, pattern, baseDir, workTreeIdFor(cwd))
3011
3011
  }));
3012
3012
  }
3013
- if (path.startsWith("impact/")) {
3014
- const pattern = path.slice("impact/".length);
3013
+ if (path2.startsWith("impact/")) {
3014
+ const pattern = path2.slice("impact/".length);
3015
3015
  if (pattern === "") {
3016
3016
  return { kind: "not-found", message: "impact/ requires a pattern: cat memory/graph/impact/<symbol>" };
3017
3017
  }
3018
3018
  return loadSnapshotOrError(cwd, (snap) => ({ kind: "ok", body: renderImpact(snap, pattern) }));
3019
3019
  }
3020
- if (path.startsWith("neighborhood/")) {
3021
- const file = path.slice("neighborhood/".length);
3020
+ if (path2.startsWith("neighborhood/")) {
3021
+ const file = path2.slice("neighborhood/".length);
3022
3022
  if (file === "") {
3023
3023
  return { kind: "not-found", message: "neighborhood/ requires a file path: cat memory/graph/neighborhood/<file>" };
3024
3024
  }
3025
3025
  return loadSnapshotOrError(cwd, (snap) => ({ kind: "ok", body: renderNeighborhood(snap, file) }));
3026
3026
  }
3027
- if (path === "layers" || path === "layers/" || path === "layers/index.md") {
3027
+ if (path2 === "layers" || path2 === "layers/" || path2 === "layers/index.md") {
3028
3028
  return loadSnapshotOrError(cwd, (snap) => ({ kind: "ok", body: renderLayers(snap) }));
3029
3029
  }
3030
- if (path === "tour" || path === "tour/" || path === "tour/index.md") {
3030
+ if (path2 === "tour" || path2 === "tour/" || path2 === "tour/index.md") {
3031
3031
  return loadSnapshotOrError(cwd, (snap) => ({ kind: "ok", body: renderTour(snap) }));
3032
3032
  }
3033
- if (path.startsWith("path/")) {
3034
- const rest = path.slice("path/".length);
3033
+ if (path2.startsWith("path/")) {
3034
+ const rest = path2.slice("path/".length);
3035
3035
  const slash = rest.indexOf("/");
3036
3036
  if (slash <= 0 || slash === rest.length - 1) {
3037
3037
  return { kind: "not-found", message: "path/ needs two patterns: cat memory/graph/path/<from>/<to> (each a symbol-name substring, no slash)" };
@@ -3042,7 +3042,7 @@ function handleGraphVfs(subpath, cwd) {
3042
3042
  }
3043
3043
  return {
3044
3044
  kind: "not-found",
3045
- message: `Unknown endpoint: graph/${path}
3045
+ message: `Unknown endpoint: graph/${path2}
3046
3046
  Available: index.md, find/<pattern>, query/<pattern>, show/<handle-or-pattern>, impact/<pattern>, neighborhood/<file>, layers, tour, path/<from>/<to>`
3047
3047
  };
3048
3048
  }
@@ -3441,22 +3441,22 @@ function handlesPath(baseDir, worktreeId) {
3441
3441
  return join14(baseDir, "worktrees", worktreeId, ".find-handles.json");
3442
3442
  }
3443
3443
  function saveHandles(baseDir, worktreeId, ids, pattern) {
3444
- const path = handlesPath(baseDir, worktreeId);
3444
+ const path2 = handlesPath(baseDir, worktreeId);
3445
3445
  const payload = { pattern, ts: Date.now(), ids };
3446
3446
  try {
3447
- mkdirSync8(dirname6(path), { recursive: true });
3448
- const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
3447
+ mkdirSync8(dirname6(path2), { recursive: true });
3448
+ const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
3449
3449
  writeFileSync7(tmp, JSON.stringify(payload));
3450
- renameSync5(tmp, path);
3450
+ renameSync5(tmp, path2);
3451
3451
  } catch {
3452
3452
  }
3453
3453
  }
3454
3454
  function loadHandles(baseDir, worktreeId) {
3455
- const path = handlesPath(baseDir, worktreeId);
3456
- if (!existsSync7(path))
3455
+ const path2 = handlesPath(baseDir, worktreeId);
3456
+ if (!existsSync7(path2))
3457
3457
  return null;
3458
3458
  try {
3459
- const parsed = JSON.parse(readFileSync9(path, "utf8"));
3459
+ const parsed = JSON.parse(readFileSync9(path2, "utf8"));
3460
3460
  if (parsed === null || typeof parsed !== "object")
3461
3461
  return null;
3462
3462
  const o = parsed;
@@ -3546,10 +3546,230 @@ function tryGraphRead(rewrittenCommand, cwd) {
3546
3546
  return result.kind === "ok" ? result.body : `(${result.kind}) ${result.message}`;
3547
3547
  }
3548
3548
 
3549
+ // dist/src/skillify/skillopt-trigger.js
3550
+ import { spawn as spawn2 } from "node:child_process";
3551
+ import fs from "node:fs";
3552
+ import path from "node:path";
3553
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
3554
+
3555
+ // dist/src/skillify/state-dir.js
3556
+ import { homedir as homedir10 } from "node:os";
3557
+ import { join as join15 } from "node:path";
3558
+ function getStateDir() {
3559
+ const override = process.env.HIVEMIND_STATE_DIR?.trim();
3560
+ return override && override.length > 0 ? override : join15(homedir10(), ".deeplake", "state", "skillify");
3561
+ }
3562
+
3563
+ // dist/src/skillify/skill-invocations.js
3564
+ function pathToSkillRef(s) {
3565
+ if (typeof s !== "string")
3566
+ return null;
3567
+ const m = s.match(/\/skills\/(?:[^/\s"'`]+\/)*([^/\s"'`]+)\/SKILL\.md/);
3568
+ return m ? m[1] : null;
3569
+ }
3570
+ function splitOrgSkill(skill) {
3571
+ if (skill.includes(":"))
3572
+ return null;
3573
+ if (skill.includes("/") || skill.includes("\\") || skill.includes(".."))
3574
+ return null;
3575
+ const i = skill.lastIndexOf("--");
3576
+ if (i <= 0 || i + 2 >= skill.length)
3577
+ return null;
3578
+ return { name: skill.slice(0, i), author: skill.slice(i + 2) };
3579
+ }
3580
+
3581
+ // dist/src/skillify/manifest.js
3582
+ import { existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync7, unlinkSync as unlinkSync4, writeFileSync as writeFileSync8 } from "node:fs";
3583
+ import { dirname as dirname8, join as join17 } from "node:path";
3584
+
3585
+ // dist/src/skillify/legacy-migration.js
3586
+ import { existsSync as existsSync8, renameSync as renameSync6 } from "node:fs";
3587
+ import { dirname as dirname7, join as join16 } from "node:path";
3588
+ var dlog = (msg) => log("skillify-migrate", msg);
3589
+ var attempted = false;
3590
+ function migrateLegacyStateDir() {
3591
+ if (process.env.HIVEMIND_STATE_DIR?.trim())
3592
+ return;
3593
+ if (attempted)
3594
+ return;
3595
+ attempted = true;
3596
+ const current = getStateDir();
3597
+ const legacy = join16(dirname7(current), "skilify");
3598
+ if (!existsSync8(legacy))
3599
+ return;
3600
+ if (existsSync8(current))
3601
+ return;
3602
+ try {
3603
+ renameSync6(legacy, current);
3604
+ dlog(`migrated ${legacy} -> ${current}`);
3605
+ } catch (err) {
3606
+ const code = err.code;
3607
+ if (code === "EXDEV" || code === "EPERM" || code === "ENOENT" || code === "EEXIST" || code === "ENOTEMPTY") {
3608
+ dlog(`migration skipped (${code}); legacy dir left as-is or another process handled it`);
3609
+ return;
3610
+ }
3611
+ throw err;
3612
+ }
3613
+ }
3614
+
3615
+ // dist/src/skillify/manifest.js
3616
+ function emptyManifest() {
3617
+ return { version: 1, entries: [] };
3618
+ }
3619
+ function manifestPath() {
3620
+ return join17(getStateDir(), "pulled.json");
3621
+ }
3622
+ function loadManifest(path2 = manifestPath()) {
3623
+ migrateLegacyStateDir();
3624
+ if (!existsSync9(path2))
3625
+ return emptyManifest();
3626
+ let raw;
3627
+ try {
3628
+ raw = readFileSync10(path2, "utf-8");
3629
+ } catch {
3630
+ return emptyManifest();
3631
+ }
3632
+ try {
3633
+ const parsed = JSON.parse(raw);
3634
+ if (!parsed || typeof parsed !== "object")
3635
+ return emptyManifest();
3636
+ if (parsed.version !== 1 || !Array.isArray(parsed.entries))
3637
+ return emptyManifest();
3638
+ const entries = [];
3639
+ for (const e of parsed.entries) {
3640
+ if (!e || typeof e !== "object")
3641
+ continue;
3642
+ if (typeof e.dirName !== "string" || !e.dirName)
3643
+ continue;
3644
+ if (e.dirName.includes("/") || e.dirName.includes("\\") || e.dirName.includes(".."))
3645
+ continue;
3646
+ if (typeof e.name !== "string" || !e.name)
3647
+ continue;
3648
+ if (typeof e.author !== "string")
3649
+ continue;
3650
+ if (typeof e.installRoot !== "string" || !e.installRoot)
3651
+ continue;
3652
+ if (e.install !== "global" && e.install !== "project")
3653
+ continue;
3654
+ const symlinks = Array.isArray(e.symlinks) ? e.symlinks.filter((p) => typeof p === "string" && p.length > 0 && (p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p)) && // absolute (POSIX or Windows)
3655
+ !p.includes("..")) : [];
3656
+ entries.push({
3657
+ dirName: e.dirName,
3658
+ name: e.name,
3659
+ author: e.author,
3660
+ projectKey: typeof e.projectKey === "string" ? e.projectKey : "",
3661
+ remoteVersion: typeof e.remoteVersion === "number" ? e.remoteVersion : 1,
3662
+ install: e.install,
3663
+ installRoot: e.installRoot,
3664
+ pulledAt: typeof e.pulledAt === "string" ? e.pulledAt : (/* @__PURE__ */ new Date()).toISOString(),
3665
+ symlinks
3666
+ });
3667
+ }
3668
+ return { version: 1, entries };
3669
+ } catch {
3670
+ return emptyManifest();
3671
+ }
3672
+ }
3673
+
3674
+ // dist/src/skillify/skillopt-env.js
3675
+ var SKILLOPT_ENV = {
3676
+ /** User-set kill switch: "1" disables the whole trigger. */
3677
+ DISABLED: "HIVEMIND_SKILLOPT_DISABLED",
3678
+ /** Recursion guard the trigger sets on the spawned worker so the worker can't re-arm. */
3679
+ WORKER: "HIVEMIND_SKILLOPT_WORKER",
3680
+ /** Worker inputs, handed trigger → worker via the child env. */
3681
+ SESSION: "HIVEMIND_SKILLOPT_SESSION",
3682
+ SKILL: "HIVEMIND_SKILLOPT_SKILL",
3683
+ REACTION: "HIVEMIND_SKILLOPT_REACTION",
3684
+ TOOL_USE_ID: "HIVEMIND_SKILLOPT_TOOL_USE_ID",
3685
+ /** Which agent's CLI runs the judge/proposer (claude_code/codex/hermes/cursor/pi). */
3686
+ AGENT: "HIVEMIND_SKILLOPT_AGENT",
3687
+ /** K-message judgment-window size override. */
3688
+ JUDGE_WINDOW: "HIVEMIND_SKILLOPT_JUDGE_WINDOW"
3689
+ };
3690
+
3691
+ // dist/src/skillify/skillopt-trigger.js
3692
+ var DEFAULT_JUDGE_WINDOW = 3;
3693
+ function judgeWindow(env = process.env) {
3694
+ const n = Number(env[SKILLOPT_ENV.JUDGE_WINDOW]);
3695
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_JUDGE_WINDOW;
3696
+ }
3697
+ function defaultIsOrgSkill(skillRef) {
3698
+ try {
3699
+ return loadManifest().entries.some((e) => e.dirName === skillRef);
3700
+ } catch {
3701
+ return false;
3702
+ }
3703
+ }
3704
+ function pendingFile(sessionId) {
3705
+ const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 200);
3706
+ return path.join(getStateDir(), "skillopt", "pending", `${safe}.json`);
3707
+ }
3708
+ var fileStore = {
3709
+ load(sessionId) {
3710
+ try {
3711
+ return JSON.parse(fs.readFileSync(pendingFile(sessionId), "utf8"));
3712
+ } catch {
3713
+ return null;
3714
+ }
3715
+ },
3716
+ save(sessionId, p) {
3717
+ try {
3718
+ const f = pendingFile(sessionId);
3719
+ if (p === null) {
3720
+ try {
3721
+ fs.unlinkSync(f);
3722
+ } catch {
3723
+ }
3724
+ return;
3725
+ }
3726
+ fs.mkdirSync(path.dirname(f), { recursive: true });
3727
+ const tmp = `${f}.${process.pid}.tmp`;
3728
+ fs.writeFileSync(tmp, JSON.stringify(p));
3729
+ fs.renameSync(tmp, f);
3730
+ } catch {
3731
+ }
3732
+ }
3733
+ };
3734
+ function markSkillPending(sessionId, skillRef, toolUseId, deps = {}) {
3735
+ if (!sessionId || !skillRef)
3736
+ return false;
3737
+ if (!splitOrgSkill(skillRef))
3738
+ return false;
3739
+ if (!(deps.isOrgSkill ?? defaultIsOrgSkill)(skillRef))
3740
+ return false;
3741
+ (deps.store ?? fileStore).save(sessionId, { skill: skillRef, budget: judgeWindow(deps.env ?? process.env), toolUseId });
3742
+ return true;
3743
+ }
3744
+
3745
+ // dist/src/hooks/shared/skillopt-hook.js
3746
+ function skillRefFromSkillFileRead(toolName, toolInput) {
3747
+ if (/^read$/i.test(toolName))
3748
+ return pathToSkillRef(toolInput?.path);
3749
+ return pathToSkillRef(toolInput?.command);
3750
+ }
3751
+ function armSkillOptOnSkillUse(sessionId, toolName, toolInput, toolUseId) {
3752
+ try {
3753
+ if (process.env[SKILLOPT_ENV.DISABLED] === "1")
3754
+ return;
3755
+ let ref = null;
3756
+ if (toolName === "Skill") {
3757
+ const s = toolInput?.skill;
3758
+ ref = typeof s === "string" ? s : null;
3759
+ } else {
3760
+ ref = skillRefFromSkillFileRead(toolName, toolInput);
3761
+ }
3762
+ if (ref)
3763
+ markSkillPending(sessionId, ref, toolUseId);
3764
+ } catch {
3765
+ }
3766
+ }
3767
+
3549
3768
  // dist/src/hooks/hermes/pre-tool-use.js
3550
3769
  var log5 = (msg) => log("hermes-pre-tool-use", msg);
3551
3770
  async function main() {
3552
3771
  const input = await readStdin();
3772
+ armSkillOptOnSkillUse(input.session_id ?? "", input.tool_name ?? "", input.tool_input);
3553
3773
  if (input.tool_name !== "terminal")
3554
3774
  return;
3555
3775
  const ti = input.tool_input;