pair-mode 0.3.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +66 -9
  4. package/assets/syntax/clojure.yaml +36 -0
  5. package/assets/syntax/cmake.yaml +41 -0
  6. package/assets/syntax/crystal.yaml +71 -0
  7. package/assets/syntax/csharp.yaml +51 -0
  8. package/assets/syntax/dart.yaml +45 -0
  9. package/assets/syntax/elm.yaml +38 -0
  10. package/assets/syntax/erb.yaml +42 -0
  11. package/assets/syntax/erlang.yaml +45 -0
  12. package/assets/syntax/fsharp.yaml +48 -0
  13. package/assets/syntax/graphql.yaml +47 -0
  14. package/assets/syntax/groovy.yaml +111 -0
  15. package/assets/syntax/haml.yaml +16 -0
  16. package/assets/syntax/haskell.yaml +52 -0
  17. package/assets/syntax/ini.yaml +23 -0
  18. package/assets/syntax/java.yaml +36 -0
  19. package/assets/syntax/julia.yaml +56 -0
  20. package/assets/syntax/kotlin.yaml +65 -0
  21. package/assets/syntax/makefile.yaml +37 -0
  22. package/assets/syntax/nginx.yaml +22 -0
  23. package/assets/syntax/nim.yaml +27 -0
  24. package/assets/syntax/nix.yaml +32 -0
  25. package/assets/syntax/objc.yaml +60 -0
  26. package/assets/syntax/ocaml.yaml +43 -0
  27. package/assets/syntax/perl.yaml +58 -0
  28. package/assets/syntax/php.yaml +60 -0
  29. package/assets/syntax/r.yaml +30 -0
  30. package/assets/syntax/scala.yaml +32 -0
  31. package/assets/syntax/svelte.yaml +27 -0
  32. package/assets/syntax/swift.yaml +102 -0
  33. package/assets/syntax/vue.yaml +63 -0
  34. package/assets/syntax/xml.yaml +37 -0
  35. package/assets/syntax/zig.yaml +52 -0
  36. package/dist/claude-code.js +461 -149
  37. package/dist/cli.js +1490 -476
  38. package/dist/codex.js +461 -149
  39. package/dist/opencode.js +453 -142
  40. package/dist/pair-tui.js +172 -26
  41. package/dist/pi.js +508 -155
  42. package/package.json +4 -4
  43. package/skills/toggle/SKILL.md +24 -0
  44. package/commands/pair.md +0 -18
  45. package/skills/pair/SKILL.md +0 -20
package/dist/pi.js CHANGED
@@ -7363,11 +7363,72 @@ var require_dist = __commonJS({
7363
7363
  // src/core/state/state.ts
7364
7364
  import { createHash } from "node:crypto";
7365
7365
  import { homedir } from "node:os";
7366
- import { join, dirname, basename } from "node:path";
7367
- import { existsSync, realpathSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
7366
+ import { join as join2, dirname, basename } from "node:path";
7367
+ import { existsSync, realpathSync, mkdirSync, writeFileSync, unlinkSync as unlinkSync2 } from "node:fs";
7368
+
7369
+ // src/helpers/isRecord.ts
7370
+ function isRecord(value) {
7371
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7372
+ }
7373
+
7374
+ // src/helpers/readFileOrEmpty.ts
7375
+ import { readFileSync } from "node:fs";
7376
+ function readFileOrEmpty(path) {
7377
+ try {
7378
+ return readFileSync(path, "utf-8");
7379
+ } catch {
7380
+ return "";
7381
+ }
7382
+ }
7383
+
7384
+ // src/helpers/removeQuietly.ts
7385
+ import { unlinkSync } from "node:fs";
7386
+ function removeQuietly(path) {
7387
+ try {
7388
+ unlinkSync(path);
7389
+ } catch {
7390
+ }
7391
+ }
7392
+
7393
+ // src/helpers/resolvesOnPath.ts
7394
+ import { spawnSync } from "node:child_process";
7395
+ var defaultResolvesOnPath = (command) => {
7396
+ const result = spawnSync("which", [command], { stdio: "ignore" });
7397
+ return result.status === 0;
7398
+ };
7399
+
7400
+ // src/helpers/resultFilePath.ts
7401
+ import { randomBytes } from "node:crypto";
7402
+ import { tmpdir } from "node:os";
7403
+ import { join } from "node:path";
7404
+ var NAME_BYTES = 6;
7405
+ function resultFilePath() {
7406
+ const name = `pair-result-${randomBytes(NAME_BYTES).toString("hex")}.json`;
7407
+ return join(tmpdir(), name);
7408
+ }
7409
+
7410
+ // src/helpers/spawn.ts
7411
+ import { spawnSync as spawnSync2 } from "node:child_process";
7412
+ var defaultSpawn = (command, args) => {
7413
+ const result = spawnSync2(command, args, { stdio: ["ignore", "ignore", "pipe"] });
7414
+ const stderr = result.stderr ? result.stderr.toString("utf-8") : result.error?.message ?? "";
7415
+ return { status: result.status, stderr };
7416
+ };
7417
+
7418
+ // src/helpers/splitLines.ts
7419
+ function splitLines(text) {
7420
+ const lines = text.split("\n");
7421
+ const last = lines.at(-1);
7422
+ if (last === "") {
7423
+ lines.pop();
7424
+ }
7425
+ return lines;
7426
+ }
7427
+
7428
+ // src/core/state/state.ts
7368
7429
  function stateDir() {
7369
- const base = process.env["XDG_STATE_HOME"] || join(homedir(), ".local", "state");
7370
- return join(base, "pair-mode");
7430
+ const base = process.env["XDG_STATE_HOME"] || join2(homedir(), ".local", "state");
7431
+ return join2(base, "pair-mode");
7371
7432
  }
7372
7433
  function realpathLenient(path) {
7373
7434
  try {
@@ -7377,7 +7438,7 @@ function realpathLenient(path) {
7377
7438
  if (parent === path) {
7378
7439
  return path;
7379
7440
  }
7380
- return join(realpathLenient(parent), basename(path));
7441
+ return join2(realpathLenient(parent), basename(path));
7381
7442
  }
7382
7443
  }
7383
7444
  var DIGEST_LENGTH = 16;
@@ -7386,16 +7447,23 @@ function digestFor(directory) {
7386
7447
  return createHash("sha1").update(real).digest("hex").slice(0, DIGEST_LENGTH);
7387
7448
  }
7388
7449
  function flagPath(directory) {
7389
- return join(stateDir(), `${digestFor(directory)}.on`);
7450
+ return join2(stateDir(), `${digestFor(directory)}.on`);
7390
7451
  }
7391
7452
  function sessionsDir() {
7392
- return join(stateDir(), "sessions");
7453
+ return join2(stateDir(), "sessions");
7454
+ }
7455
+ var OWNER_ONLY_DIR = 448;
7456
+ var OWNER_ONLY_FILE = 384;
7457
+ function makeSessionsDir() {
7458
+ const path = sessionsDir();
7459
+ mkdirSync(path, { recursive: true, mode: OWNER_ONLY_DIR });
7460
+ return path;
7393
7461
  }
7394
7462
  function sessionSocketPath(directory) {
7395
- return join(sessionsDir(), `${digestFor(directory)}.sock`);
7463
+ return join2(sessionsDir(), `${digestFor(directory)}.sock`);
7396
7464
  }
7397
7465
  function sessionUrlPath(directory) {
7398
- return join(sessionsDir(), `${digestFor(directory)}.url`);
7466
+ return join2(sessionsDir(), `${digestFor(directory)}.url`);
7399
7467
  }
7400
7468
  function findSessionSocket(filePath) {
7401
7469
  let current = dirname(realpathLenient(filePath));
@@ -7411,7 +7479,13 @@ function findSessionSocket(filePath) {
7411
7479
  current = parent;
7412
7480
  }
7413
7481
  }
7414
- function isEnabled(filePath) {
7482
+ function sessionFlagState(key) {
7483
+ if (existsSync(sessionKeyOptOutPath(key))) {
7484
+ return "off";
7485
+ }
7486
+ return existsSync(sessionKeyFlagPath(key)) ? "on" : "unset";
7487
+ }
7488
+ function directoryEnabled(filePath) {
7415
7489
  let current = dirname(realpathLenient(filePath));
7416
7490
  while (true) {
7417
7491
  if (existsSync(flagPath(current))) {
@@ -7424,6 +7498,29 @@ function isEnabled(filePath) {
7424
7498
  current = parent;
7425
7499
  }
7426
7500
  }
7501
+ function isEnabled(filePath, key) {
7502
+ if (key !== void 0) {
7503
+ const state = sessionFlagState(key);
7504
+ if (state !== "unset") {
7505
+ return state === "on";
7506
+ }
7507
+ }
7508
+ return directoryEnabled(filePath);
7509
+ }
7510
+ function enableSession(key) {
7511
+ const path = sessionKeyFlagPath(key);
7512
+ makeSessionsDir();
7513
+ removeQuietly(sessionKeyOptOutPath(key));
7514
+ writeFileSync(path, "", { mode: OWNER_ONLY_FILE });
7515
+ return path;
7516
+ }
7517
+ function optOutSession(key) {
7518
+ const path = sessionKeyOptOutPath(key);
7519
+ makeSessionsDir();
7520
+ removeQuietly(sessionKeyFlagPath(key));
7521
+ writeFileSync(path, "", { mode: OWNER_ONLY_FILE });
7522
+ return path;
7523
+ }
7427
7524
  function enable(directory) {
7428
7525
  const path = flagPath(directory);
7429
7526
  mkdirSync(dirname(path), { recursive: true });
@@ -7435,13 +7532,34 @@ function disable(directory) {
7435
7532
  if (!existsSync(path)) {
7436
7533
  return false;
7437
7534
  }
7438
- unlinkSync(path);
7535
+ unlinkSync2(path);
7439
7536
  return true;
7440
7537
  }
7441
-
7442
- // src/helpers/isRecord.ts
7443
- function isRecord(value) {
7444
- return typeof value === "object" && value !== null && !Array.isArray(value);
7538
+ var SESSION_KEY_LENGTH = 8;
7539
+ function sessionKey(agentSessionId) {
7540
+ const digest = createHash("sha1").update(agentSessionId).digest("hex");
7541
+ return `s-${digest.slice(0, SESSION_KEY_LENGTH)}`;
7542
+ }
7543
+ function watchUrlPath(directory, key) {
7544
+ return key ? sessionKeyUrlPath(key) : sessionUrlPath(directory);
7545
+ }
7546
+ function keyFor(agentSessionId) {
7547
+ return agentSessionId === void 0 || agentSessionId === "" ? void 0 : sessionKey(agentSessionId);
7548
+ }
7549
+ function sessionKeyPath(key, extension) {
7550
+ return join2(sessionsDir(), `${key}${extension}`);
7551
+ }
7552
+ function sessionKeySocketPath(key) {
7553
+ return sessionKeyPath(key, ".sock");
7554
+ }
7555
+ function sessionKeyFlagPath(key) {
7556
+ return sessionKeyPath(key, ".on");
7557
+ }
7558
+ function sessionKeyOptOutPath(key) {
7559
+ return sessionKeyPath(key, ".off");
7560
+ }
7561
+ function sessionKeyUrlPath(key) {
7562
+ return sessionKeyPath(key, ".url");
7445
7563
  }
7446
7564
 
7447
7565
  // src/core/simulate/simulate.ts
@@ -7488,7 +7606,7 @@ function applyEdit(text, edit) {
7488
7606
  }
7489
7607
  return replaceFirst(text, old, next);
7490
7608
  }
7491
- function simulate(tool, input, readFile) {
7609
+ function simulate(tool, input, readFile, sessionId) {
7492
7610
  if (!isRecord(input)) {
7493
7611
  return null;
7494
7612
  }
@@ -7500,7 +7618,7 @@ function simulate(tool, input, readFile) {
7500
7618
  const content = input["content"];
7501
7619
  const after = typeof content === "string" ? content : "";
7502
7620
  const before = readFile(filePath);
7503
- return { tool, filePath, before, after };
7621
+ return { tool, filePath, before, after, sessionId };
7504
7622
  }
7505
7623
  if (tool === "Edit" || tool === "MultiEdit") {
7506
7624
  const editsRaw = input["edits"];
@@ -7515,71 +7633,19 @@ function simulate(tool, input, readFile) {
7515
7633
  if (after === null) {
7516
7634
  return null;
7517
7635
  }
7518
- return { tool, filePath, before, after };
7636
+ return { tool, filePath, before, after, sessionId };
7519
7637
  }
7520
7638
  return null;
7521
7639
  }
7522
7640
 
7523
7641
  // src/cli/toggle.ts
7524
7642
  import { existsSync as existsSync2, readFileSync as readFileSync2, unlinkSync as unlinkSync3 } from "node:fs";
7525
-
7526
- // src/helpers/readFileOrEmpty.ts
7527
- import { readFileSync } from "node:fs";
7528
- function readFileOrEmpty(path) {
7529
- try {
7530
- return readFileSync(path, "utf-8");
7531
- } catch {
7532
- return "";
7533
- }
7534
- }
7535
-
7536
- // src/helpers/removeQuietly.ts
7537
- import { unlinkSync as unlinkSync2 } from "node:fs";
7538
- function removeQuietly(path) {
7539
- try {
7540
- unlinkSync2(path);
7541
- } catch {
7542
- }
7643
+ import { join as join3 } from "node:path";
7644
+ function statusProbe(directory) {
7645
+ return join3(directory, ".pair-mode-status-probe");
7543
7646
  }
7544
-
7545
- // src/helpers/resolvesOnPath.ts
7546
- import { spawnSync } from "node:child_process";
7547
- var defaultResolvesOnPath = (command) => {
7548
- const result = spawnSync("which", [command], { stdio: "ignore" });
7549
- return result.status === 0;
7550
- };
7551
-
7552
- // src/helpers/resultFilePath.ts
7553
- import { randomBytes } from "node:crypto";
7554
- import { tmpdir } from "node:os";
7555
- import { join as join2 } from "node:path";
7556
- var NAME_BYTES = 6;
7557
- function resultFilePath() {
7558
- const name = `pair-result-${randomBytes(NAME_BYTES).toString("hex")}.json`;
7559
- return join2(tmpdir(), name);
7560
- }
7561
-
7562
- // src/helpers/spawn.ts
7563
- import { spawnSync as spawnSync2 } from "node:child_process";
7564
- var defaultSpawn = (command, args) => {
7565
- const result = spawnSync2(command, args, { stdio: ["ignore", "ignore", "pipe"] });
7566
- const stderr = result.stderr ? result.stderr.toString("utf-8") : result.error?.message ?? "";
7567
- return { status: result.status, stderr };
7568
- };
7569
-
7570
- // src/helpers/splitLines.ts
7571
- function splitLines(text) {
7572
- const lines = text.split("\n");
7573
- const last = lines.at(-1);
7574
- if (last === "") {
7575
- lines.pop();
7576
- }
7577
- return lines;
7578
- }
7579
-
7580
- // src/cli/toggle.ts
7581
- function readLink(directory) {
7582
- const path = sessionUrlPath(directory);
7647
+ function readLink(directory, key) {
7648
+ const path = watchUrlPath(directory, key);
7583
7649
  if (!existsSync2(path)) {
7584
7650
  return null;
7585
7651
  }
@@ -7593,8 +7659,8 @@ function readLink(directory) {
7593
7659
  }
7594
7660
  return null;
7595
7661
  }
7596
- function stopLink(directory) {
7597
- const link = readLink(directory);
7662
+ function stopLink(directory, key) {
7663
+ const link = readLink(directory, key);
7598
7664
  if (link === null) {
7599
7665
  return false;
7600
7666
  }
@@ -7603,24 +7669,37 @@ function stopLink(directory) {
7603
7669
  } catch {
7604
7670
  }
7605
7671
  try {
7606
- unlinkSync3(sessionUrlPath(directory));
7672
+ unlinkSync3(watchUrlPath(directory, key));
7607
7673
  } catch {
7608
7674
  }
7609
7675
  return true;
7610
7676
  }
7611
- function pairOn(directory) {
7612
- enable(directory);
7613
- return `pair mode ON for ${directory}`;
7677
+ function onHeadline(directory, key) {
7678
+ return key ? `pair mode ON \xB7 ${key} \xB7 ${directory}` : `pair mode ON for ${directory}`;
7614
7679
  }
7615
- function pairOff(directory) {
7680
+ function pairOn(directory, key) {
7681
+ if (key) {
7682
+ enableSession(key);
7683
+ } else {
7684
+ enable(directory);
7685
+ }
7686
+ return onHeadline(directory, key);
7687
+ }
7688
+ function pairOff(directory, key) {
7689
+ if (key) {
7690
+ optOutSession(key);
7691
+ const stopped2 = stopLink(directory, key);
7692
+ return stopped2 ? `pair mode OFF \xB7 ${key} (web watcher stopped)` : `pair mode OFF \xB7 ${key}`;
7693
+ }
7616
7694
  disable(directory);
7617
7695
  const stopped = stopLink(directory);
7618
7696
  return stopped ? `pair mode OFF for ${directory} (web watcher stopped)` : `pair mode OFF for ${directory}`;
7619
7697
  }
7620
- function pairStatus(directory) {
7621
- const on = existsSync2(flagPath(directory));
7622
- const link = readLink(directory);
7623
- const state = `pair mode ${on ? "ON" : "OFF"} for ${directory}`;
7698
+ function pairStatus(directory, key) {
7699
+ const on = isEnabled(statusProbe(directory), key);
7700
+ const link = readLink(directory, key);
7701
+ const scope = key ? `${key} \xB7 ${directory}` : directory;
7702
+ const state = `pair mode ${on ? "ON" : "OFF"} for ${scope}`;
7624
7703
  return link === null ? state : `${state}
7625
7704
  ${link.url}`;
7626
7705
  }
@@ -8049,7 +8128,7 @@ function parseNoteResult(text) {
8049
8128
  import { randomBytes as randomBytes3 } from "node:crypto";
8050
8129
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, rmSync } from "node:fs";
8051
8130
  import { tmpdir as tmpdir2 } from "node:os";
8052
- import { join as join8 } from "node:path";
8131
+ import { join as join9 } from "node:path";
8053
8132
 
8054
8133
  // src/core/render/render.ts
8055
8134
  var HEADER_LEAD = [
@@ -8089,56 +8168,299 @@ function renderInline(input) {
8089
8168
  // src/editors/micro.ts
8090
8169
  var import_yaml = __toESM(require_dist(), 1);
8091
8170
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
8092
- import { join as join4 } from "node:path";
8171
+ import { join as join5 } from "node:path";
8093
8172
 
8094
8173
  // src/editors/languages.ts
8095
- import { extname } from "node:path";
8096
- var LANGS = {
8174
+ import { openSync, readSync, closeSync } from "node:fs";
8175
+ import { basename as basename2, extname } from "node:path";
8176
+ var EXTENSIONS = {
8097
8177
  ".go": "go",
8098
8178
  ".rb": "ruby",
8099
8179
  ".rake": "ruby",
8180
+ ".gemspec": "ruby",
8181
+ ".ru": "ruby",
8182
+ ".erb": "erb",
8183
+ ".haml": "haml",
8100
8184
  ".ts": "typescript",
8101
- ".tsx": "typescript",
8185
+ ".mts": "typescript",
8186
+ ".cts": "typescript",
8187
+ ".tsx": "tsx",
8102
8188
  ".js": "javascript",
8103
- ".jsx": "javascript",
8104
8189
  ".mjs": "javascript",
8105
- ".py": "python3",
8190
+ ".cjs": "javascript",
8191
+ ".jsx": "jsx",
8192
+ ".vue": "vue",
8193
+ ".svelte": "svelte",
8194
+ ".astro": "astro",
8195
+ ".py": "python",
8196
+ ".pyi": "python",
8106
8197
  ".ex": "elixir",
8107
8198
  ".exs": "elixir",
8199
+ ".heex": "elixir",
8200
+ ".erl": "erlang",
8201
+ ".hrl": "erlang",
8108
8202
  ".rs": "rust",
8109
- ".sh": "sh",
8110
- ".bash": "sh",
8111
- ".fish": "fish",
8203
+ ".sh": "shellscript",
8204
+ ".bash": "shellscript",
8205
+ ".ksh": "shellscript",
8112
8206
  ".zsh": "zsh",
8207
+ ".fish": "fish",
8208
+ ".ps1": "powershell",
8209
+ ".psm1": "powershell",
8210
+ ".bat": "bat",
8211
+ ".cmd": "bat",
8113
8212
  ".sql": "sql",
8114
8213
  ".json": "json",
8214
+ ".jsonc": "jsonc",
8215
+ ".json5": "json5",
8115
8216
  ".tf": "terraform",
8217
+ ".tfvars": "terraform",
8218
+ ".hcl": "hcl",
8116
8219
  ".proto": "proto",
8117
- ".dockerfile": "dockerfile",
8220
+ ".dockerfile": "docker",
8118
8221
  ".toml": "toml",
8119
8222
  ".yaml": "yaml",
8120
8223
  ".yml": "yaml",
8224
+ ".ini": "ini",
8225
+ ".cfg": "ini",
8226
+ ".properties": "properties",
8227
+ ".env": "dotenv",
8121
8228
  ".md": "markdown",
8229
+ ".markdown": "markdown",
8230
+ ".mdx": "mdx",
8122
8231
  ".css": "css",
8232
+ ".scss": "scss",
8233
+ ".sass": "sass",
8234
+ ".less": "less",
8235
+ ".styl": "stylus",
8123
8236
  ".html": "html",
8124
- ".erb": "html",
8237
+ ".htm": "html",
8238
+ ".xml": "xml",
8239
+ ".xsl": "xml",
8240
+ ".svg": "xml",
8125
8241
  ".lua": "lua",
8126
8242
  ".c": "c",
8127
- ".h": "c"
8243
+ ".h": "c",
8244
+ ".cc": "cpp",
8245
+ ".cpp": "cpp",
8246
+ ".cxx": "cpp",
8247
+ ".hpp": "cpp",
8248
+ ".hh": "cpp",
8249
+ ".cs": "csharp",
8250
+ ".java": "java",
8251
+ ".kt": "kotlin",
8252
+ ".kts": "kotlin",
8253
+ ".swift": "swift",
8254
+ ".m": "objective-c",
8255
+ ".mm": "objective-c",
8256
+ ".php": "php",
8257
+ ".pl": "perl",
8258
+ ".pm": "perl",
8259
+ ".scala": "scala",
8260
+ ".sc": "scala",
8261
+ ".clj": "clojure",
8262
+ ".cljs": "clojure",
8263
+ ".cljc": "clojure",
8264
+ ".hs": "haskell",
8265
+ ".nix": "nix",
8266
+ ".r": "r",
8267
+ ".jl": "julia",
8268
+ ".zig": "zig",
8269
+ ".nim": "nim",
8270
+ ".dart": "dart",
8271
+ ".groovy": "groovy",
8272
+ ".gradle": "groovy",
8273
+ ".cr": "crystal",
8274
+ ".ml": "ocaml",
8275
+ ".mli": "ocaml",
8276
+ ".fs": "fsharp",
8277
+ ".fsx": "fsharp",
8278
+ ".elm": "elm",
8279
+ ".graphql": "graphql",
8280
+ ".gql": "graphql",
8281
+ ".prisma": "prisma",
8282
+ ".sol": "solidity",
8283
+ ".vim": "viml",
8284
+ ".diff": "diff",
8285
+ ".patch": "diff",
8286
+ ".tex": "latex",
8287
+ ".wgsl": "wgsl",
8288
+ ".glsl": "glsl",
8289
+ ".cue": "cue"
8290
+ };
8291
+ var FILENAMES = {
8292
+ gemfile: "ruby",
8293
+ rakefile: "ruby",
8294
+ capfile: "ruby",
8295
+ vagrantfile: "ruby",
8296
+ guardfile: "ruby",
8297
+ podfile: "ruby",
8298
+ fastfile: "ruby",
8299
+ appfile: "ruby",
8300
+ brewfile: "ruby",
8301
+ "config.ru": "ruby",
8302
+ dockerfile: "docker",
8303
+ containerfile: "docker",
8304
+ makefile: "make",
8305
+ gnumakefile: "make",
8306
+ "cmakelists.txt": "cmake",
8307
+ "cargo.lock": "toml",
8308
+ "gemfile.lock": "toml",
8309
+ ".babelrc": "json",
8310
+ ".eslintrc": "json",
8311
+ ".prettierrc": "json",
8312
+ ".env": "dotenv",
8313
+ ".gitconfig": "ini",
8314
+ ".editorconfig": "ini",
8315
+ "nginx.conf": "nginx",
8316
+ "pnpm-workspace.yaml": "yaml"
8128
8317
  };
8129
- function syntaxName(sourcePath) {
8130
- const ext = extname(sourcePath).toLowerCase();
8131
- return LANGS[ext] ?? null;
8318
+ var SHEBANGS = [
8319
+ { pattern: /^#!.*\/(env\s+)?ruby(\s|$)/, id: "ruby" },
8320
+ { pattern: /^#!.*\/(env\s+)?python[\d.]*(\s|$)/, id: "python" },
8321
+ { pattern: /^#!.*\/(env\s+)?(node|bun|deno)(\s|$)/, id: "javascript" },
8322
+ { pattern: /^#!.*\/(env\s+)?(bash|sh|ksh|dash)(\s|$)/, id: "shellscript" },
8323
+ { pattern: /^#!.*\/(env\s+)?zsh(\s|$)/, id: "zsh" },
8324
+ { pattern: /^#!.*\/(env\s+)?fish(\s|$)/, id: "fish" },
8325
+ { pattern: /^#!.*\/(env\s+)?perl(\s|$)/, id: "perl" },
8326
+ { pattern: /^#!.*\/(env\s+)?php(\s|$)/, id: "php" },
8327
+ { pattern: /^#!.*\/(env\s+)?(elixir|iex)(\s|$)/, id: "elixir" },
8328
+ { pattern: /^#!.*\/(env\s+)?lua(\s|$)/, id: "lua" },
8329
+ { pattern: /^#!.*\/(env\s+)?Rscript(\s|$)/, id: "r" },
8330
+ { pattern: /^#!.*\/(env\s+)?pwsh(\s|$)/, id: "powershell" }
8331
+ ];
8332
+ var FIRST_LINE_BYTES = 256;
8333
+ function firstLine(sourcePath) {
8334
+ let handle = null;
8335
+ try {
8336
+ handle = openSync(sourcePath, "r");
8337
+ const buffer = Buffer.alloc(FIRST_LINE_BYTES);
8338
+ const read = readSync(handle, buffer, 0, FIRST_LINE_BYTES, 0);
8339
+ const text = buffer.toString("utf-8", 0, read);
8340
+ const newline = text.indexOf("\n");
8341
+ return newline === -1 ? text : text.slice(0, newline);
8342
+ } catch {
8343
+ return null;
8344
+ } finally {
8345
+ if (handle !== null) {
8346
+ try {
8347
+ closeSync(handle);
8348
+ } catch {
8349
+ }
8350
+ }
8351
+ }
8352
+ }
8353
+ function shebangLanguage(sourcePath) {
8354
+ const line = firstLine(sourcePath);
8355
+ if (line === null || !line.startsWith("#!")) {
8356
+ return null;
8357
+ }
8358
+ return SHEBANGS.find((rule) => rule.pattern.test(line))?.id ?? null;
8359
+ }
8360
+ function detectLanguage(sourcePath) {
8361
+ const name = basename2(sourcePath).toLowerCase();
8362
+ const ext = extname(name);
8363
+ if (ext !== "" && EXTENSIONS[ext] !== void 0) {
8364
+ return EXTENSIONS[ext];
8365
+ }
8366
+ if (FILENAMES[name] !== void 0) {
8367
+ return FILENAMES[name];
8368
+ }
8369
+ return shebangLanguage(sourcePath);
8370
+ }
8371
+ var MICRO_SYNTAX = {
8372
+ c: "c",
8373
+ clojure: "clojure",
8374
+ cmake: "cmake",
8375
+ crystal: "crystal",
8376
+ csharp: "csharp",
8377
+ css: "css",
8378
+ dart: "dart",
8379
+ docker: "dockerfile",
8380
+ elixir: "elixir",
8381
+ elm: "elm",
8382
+ erb: "erb",
8383
+ erlang: "erlang",
8384
+ fish: "fish",
8385
+ fsharp: "fsharp",
8386
+ go: "go",
8387
+ graphql: "graphql",
8388
+ groovy: "groovy",
8389
+ haml: "haml",
8390
+ haskell: "haskell",
8391
+ html: "html",
8392
+ ini: "ini",
8393
+ java: "java",
8394
+ javascript: "javascript",
8395
+ json: "json",
8396
+ julia: "julia",
8397
+ kotlin: "kotlin",
8398
+ lua: "lua",
8399
+ make: "makefile",
8400
+ markdown: "markdown",
8401
+ nginx: "nginx",
8402
+ nim: "nim",
8403
+ nix: "nix",
8404
+ "objective-c": "objc",
8405
+ ocaml: "ocaml",
8406
+ perl: "perl",
8407
+ php: "php",
8408
+ proto: "proto",
8409
+ python: "python3",
8410
+ r: "r",
8411
+ ruby: "ruby",
8412
+ rust: "rust",
8413
+ scala: "scala",
8414
+ shellscript: "sh",
8415
+ sql: "sql",
8416
+ svelte: "svelte",
8417
+ swift: "swift",
8418
+ terraform: "terraform",
8419
+ toml: "toml",
8420
+ typescript: "typescript",
8421
+ vue: "vue",
8422
+ xml: "xml",
8423
+ yaml: "yaml",
8424
+ zig: "zig",
8425
+ zsh: "zsh"
8426
+ };
8427
+ var VIM_FILETYPES = {
8428
+ shellscript: "sh",
8429
+ docker: "dockerfile",
8430
+ csharp: "cs",
8431
+ "objective-c": "objc",
8432
+ bat: "dosbatch",
8433
+ viml: "vim",
8434
+ mdx: "markdown",
8435
+ jsx: "javascriptreact",
8436
+ tsx: "typescriptreact",
8437
+ dotenv: "sh",
8438
+ properties: "jproperties",
8439
+ stylus: "stylus"
8440
+ };
8441
+ function microSyntaxName(sourcePath) {
8442
+ const id = detectLanguage(sourcePath);
8443
+ if (id === null) {
8444
+ return null;
8445
+ }
8446
+ return MICRO_SYNTAX[id] ?? null;
8447
+ }
8448
+ function vimFiletype(sourcePath) {
8449
+ const id = detectLanguage(sourcePath);
8450
+ if (id === null) {
8451
+ return null;
8452
+ }
8453
+ return VIM_FILETYPES[id] ?? id;
8132
8454
  }
8133
8455
 
8134
8456
  // src/editors/syntax-cache.ts
8135
8457
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
8136
- import { dirname as dirname2, join as join3 } from "node:path";
8458
+ import { dirname as dirname2, join as join4 } from "node:path";
8137
8459
  import { fileURLToPath } from "node:url";
8138
8460
  function defaultAssetsDir() {
8139
8461
  let dir = dirname2(fileURLToPath(import.meta.url));
8140
8462
  while (true) {
8141
- const candidate = join3(dir, "assets", "syntax");
8463
+ const candidate = join4(dir, "assets", "syntax");
8142
8464
  if (existsSync3(candidate)) {
8143
8465
  return candidate;
8144
8466
  }
@@ -8153,7 +8475,7 @@ function syntaxSource(lang, assetsDir = defaultAssetsDir()) {
8153
8475
  if (assetsDir === null) {
8154
8476
  return null;
8155
8477
  }
8156
- const path = join3(assetsDir, `${lang}.yaml`);
8478
+ const path = join4(assetsDir, `${lang}.yaml`);
8157
8479
  if (!existsSync3(path)) {
8158
8480
  return null;
8159
8481
  }
@@ -8190,7 +8512,7 @@ function bandRules() {
8190
8512
  ];
8191
8513
  }
8192
8514
  function writeColorScheme(configDir, theme) {
8193
- const dir = join4(configDir, "colorschemes");
8515
+ const dir = join5(configDir, "colorschemes");
8194
8516
  mkdirSync2(dir, { recursive: true });
8195
8517
  const text = `include "monokai"
8196
8518
 
@@ -8198,7 +8520,7 @@ color-link pairadd "#d7ffd7,${theme.add}"
8198
8520
  color-link pairdel "#ffd7d7,${theme.del}"
8199
8521
  color-link pairskip "#6a6a6a,${theme.fold}"
8200
8522
  `;
8201
- writeFileSync2(join4(dir, "pair.micro"), text, "utf-8");
8523
+ writeFileSync2(join5(dir, "pair.micro"), text, "utf-8");
8202
8524
  }
8203
8525
  function syntaxText(lang, source) {
8204
8526
  const parsed = (0, import_yaml.parse)(source);
@@ -8214,7 +8536,7 @@ function syntaxText(lang, source) {
8214
8536
  return (0, import_yaml.stringify)(file, { lineWidth: 0 });
8215
8537
  }
8216
8538
  function writeSyntax(configDir, sourcePath) {
8217
- const lang = syntaxName(sourcePath);
8539
+ const lang = microSyntaxName(sourcePath);
8218
8540
  if (lang === null) {
8219
8541
  return;
8220
8542
  }
@@ -8226,9 +8548,9 @@ function writeSyntax(configDir, sourcePath) {
8226
8548
  if (text === null) {
8227
8549
  return;
8228
8550
  }
8229
- const dir = join4(configDir, "syntax");
8551
+ const dir = join5(configDir, "syntax");
8230
8552
  mkdirSync2(dir, { recursive: true });
8231
- writeFileSync2(join4(dir, `pair-${lang}.yaml`), text, "utf-8");
8553
+ writeFileSync2(join5(dir, `pair-${lang}.yaml`), text, "utf-8");
8232
8554
  }
8233
8555
  function createMicroEditor(resolvesOnPath = defaultResolvesOnPath) {
8234
8556
  return {
@@ -8241,7 +8563,7 @@ function createMicroEditor(resolvesOnPath = defaultResolvesOnPath) {
8241
8563
  return ["# F3 moves between panes. Ctrl+W or F2 sends and closes."];
8242
8564
  },
8243
8565
  bufferSuffix(sourcePath) {
8244
- const lang = syntaxName(sourcePath);
8566
+ const lang = microSyntaxName(sourcePath);
8245
8567
  if (lang === null) {
8246
8568
  return ".diff";
8247
8569
  }
@@ -8250,12 +8572,12 @@ function createMicroEditor(resolvesOnPath = defaultResolvesOnPath) {
8250
8572
  prepare(context) {
8251
8573
  mkdirSync2(context.configDir, { recursive: true });
8252
8574
  writeFileSync2(
8253
- join4(context.configDir, "bindings.json"),
8575
+ join5(context.configDir, "bindings.json"),
8254
8576
  JSON.stringify(MICRO_BINDINGS, null, 2),
8255
8577
  "utf-8"
8256
8578
  );
8257
8579
  writeFileSync2(
8258
- join4(context.configDir, "settings.json"),
8580
+ join5(context.configDir, "settings.json"),
8259
8581
  JSON.stringify(MICRO_SETTINGS, null, 2),
8260
8582
  "utf-8"
8261
8583
  );
@@ -8279,16 +8601,6 @@ function isHexColor(value) {
8279
8601
  }
8280
8602
 
8281
8603
  // src/editors/vim.ts
8282
- var VIM_FILETYPE_OVERRIDES = {
8283
- python3: "python"
8284
- };
8285
- function vimFiletype(sourcePath) {
8286
- const lang = syntaxName(sourcePath);
8287
- if (lang === null) {
8288
- return null;
8289
- }
8290
- return VIM_FILETYPE_OVERRIDES[lang] ?? lang;
8291
- }
8292
8604
  function safeThemeColor(value) {
8293
8605
  if (!isHexColor(value)) {
8294
8606
  throw new Error(`invalid theme colour for vim highlight: ${value}`);
@@ -8343,7 +8655,7 @@ function vimEditor(name, resolvesOnPath = defaultResolvesOnPath) {
8343
8655
 
8344
8656
  // src/editors/nano.ts
8345
8657
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
8346
- import { join as join5 } from "node:path";
8658
+ import { join as join6 } from "node:path";
8347
8659
  function safeThemeColor2(value) {
8348
8660
  if (!isHexColor(value)) {
8349
8661
  throw new Error(`invalid theme colour for nano rcfile: ${value}`);
@@ -8355,7 +8667,7 @@ function writeNanorc(configDir, theme) {
8355
8667
  color ,${safeThemeColor2(theme.del)} "^\u258C\u258C-"
8356
8668
  color ,${safeThemeColor2(theme.fold)} "^\u22EF"
8357
8669
  `;
8358
- const path = join5(configDir, "pair.nanorc");
8670
+ const path = join6(configDir, "pair.nanorc");
8359
8671
  writeFileSync3(path, text, "utf-8");
8360
8672
  return path;
8361
8673
  }
@@ -8385,12 +8697,12 @@ function createNanoEditor(resolvesOnPath = defaultResolvesOnPath) {
8385
8697
 
8386
8698
  // src/editors/pair.ts
8387
8699
  import { existsSync as existsSync5 } from "node:fs";
8388
- import { dirname as dirname4, extname as extname3, join as join7 } from "node:path";
8700
+ import { dirname as dirname4, extname as extname3, join as join8 } from "node:path";
8389
8701
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8390
8702
 
8391
8703
  // src/core/config/config.ts
8392
8704
  import { homedir as homedir2 } from "node:os";
8393
- import { join as join6, dirname as dirname3 } from "node:path";
8705
+ import { join as join7, dirname as dirname3 } from "node:path";
8394
8706
  import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
8395
8707
  var EDITOR_NAMES = ["auto", "pair", "micro", "nvim", "vim", "nano"];
8396
8708
  var MULTIPLEXER_NAMES = ["auto", "zellij", "tmux", "none"];
@@ -8417,8 +8729,8 @@ var DEFAULT_CONFIG = {
8417
8729
  syntax: true
8418
8730
  };
8419
8731
  function configPath(homeDir) {
8420
- const base = process.env["XDG_CONFIG_HOME"] || join6(homeDir ?? homedir2(), ".config");
8421
- return join6(base, "pair-mode", "config.json");
8732
+ const base = process.env["XDG_CONFIG_HOME"] || join7(homeDir ?? homedir2(), ".config");
8733
+ return join7(base, "pair-mode", "config.json");
8422
8734
  }
8423
8735
  function isEditorName(value) {
8424
8736
  return typeof value === "string" && EDITOR_NAMES.includes(value);
@@ -8725,7 +9037,7 @@ function paintLayout(layout) {
8725
9037
  // src/editors/pair.ts
8726
9038
  var defaultResolveTuiEntry = () => {
8727
9039
  const here = dirname4(fileURLToPath2(import.meta.url));
8728
- return join7(here, "pair-tui.js");
9040
+ return join8(here, "pair-tui.js");
8729
9041
  };
8730
9042
  var defaultCheckBundleExists = existsSync5;
8731
9043
  function createPairEditor(resolveTuiEntry = defaultResolveTuiEntry, checkBundleExists = defaultCheckBundleExists) {
@@ -8891,9 +9203,9 @@ function createTmuxMultiplexer(spawn = defaultSpawn, resolvesOnPath = defaultRes
8891
9203
  }
8892
9204
 
8893
9205
  // src/multiplexers/tty.ts
8894
- import { openSync, closeSync } from "node:fs";
9206
+ import { openSync as openSync2, closeSync as closeSync2 } from "node:fs";
8895
9207
  import { spawnSync as spawnSync3 } from "node:child_process";
8896
- var defaultOpen = () => openSync("/dev/tty", "r+");
9208
+ var defaultOpen = () => openSync2("/dev/tty", "r+");
8897
9209
  var defaultRunner = (command, args, fd) => {
8898
9210
  const result = spawnSync3(command, args, { stdio: [fd, fd, fd] });
8899
9211
  const detail = result.status === 0 ? "" : String(result.error?.message ?? result.stderr ?? "");
@@ -8901,7 +9213,7 @@ var defaultRunner = (command, args, fd) => {
8901
9213
  };
8902
9214
  function closeQuietly(fd) {
8903
9215
  try {
8904
- closeSync(fd);
9216
+ closeSync2(fd);
8905
9217
  } catch {
8906
9218
  }
8907
9219
  }
@@ -8965,7 +9277,7 @@ function splitCommand(value) {
8965
9277
  }
8966
9278
  function tempFile(prefix, suffix, content) {
8967
9279
  const name = `${prefix}${randomBytes3(NAME_BYTES2).toString("hex")}${suffix}`;
8968
- const path = join8(tmpdir2(), name);
9280
+ const path = join9(tmpdir2(), name);
8969
9281
  writeFileSync5(path, content, "utf-8");
8970
9282
  return path;
8971
9283
  }
@@ -8976,7 +9288,7 @@ function removeTreeQuietly(path) {
8976
9288
  }
8977
9289
  }
8978
9290
  function reviewConfigDir() {
8979
- return join8(stateDir(), "editor", randomBytes3(NAME_BYTES2).toString("hex"));
9291
+ return join9(stateDir(), "editor", randomBytes3(NAME_BYTES2).toString("hex"));
8980
9292
  }
8981
9293
  function withEnvPrefix(argv, env) {
8982
9294
  const assignments = Object.entries(env).map(([key, value]) => `${key}=${value}`);
@@ -9056,6 +9368,7 @@ function createPaneTransport(deps = {}) {
9056
9368
 
9057
9369
  // src/transports/session/client.ts
9058
9370
  import { createConnection } from "node:net";
9371
+ import { existsSync as existsSync6 } from "node:fs";
9059
9372
 
9060
9373
  // src/transports/session/wire.ts
9061
9374
  var CLIENT_KINDS = ["tui", "web"];
@@ -9070,11 +9383,11 @@ function createLineReader() {
9070
9383
  return parts.filter((line) => line.trim() !== "");
9071
9384
  };
9072
9385
  }
9073
- function isString(value) {
9386
+ function isString2(value) {
9074
9387
  return typeof value === "string";
9075
9388
  }
9076
9389
  function isClientKind(value) {
9077
- return isString(value) && CLIENT_KINDS.includes(value);
9390
+ return isString2(value) && CLIENT_KINDS.includes(value);
9078
9391
  }
9079
9392
  function isQuestion(value) {
9080
9393
  if (!isRecord(value)) {
@@ -9082,16 +9395,16 @@ function isQuestion(value) {
9082
9395
  }
9083
9396
  const line = value["line"];
9084
9397
  const lineOk = line === null || typeof line === "number";
9085
- return lineOk && isString(value["code"]) && isString(value["text"]);
9398
+ return lineOk && isString2(value["code"]) && isString2(value["text"]);
9086
9399
  }
9087
9400
  function isQuestionList(value) {
9088
9401
  return Array.isArray(value) && value.every(isQuestion);
9089
9402
  }
9090
9403
  function toSubmit(raw) {
9091
- if (!isString(raw["tool"]) || !isString(raw["path"])) {
9404
+ if (!isString2(raw["tool"]) || !isString2(raw["path"])) {
9092
9405
  return null;
9093
9406
  }
9094
- if (!isString(raw["before"]) || !isString(raw["after"])) {
9407
+ if (!isString2(raw["before"]) || !isString2(raw["after"])) {
9095
9408
  return null;
9096
9409
  }
9097
9410
  return {
@@ -9107,7 +9420,7 @@ function toAttach(raw) {
9107
9420
  }
9108
9421
  function toReview(raw) {
9109
9422
  const submit = toSubmit({ ...raw, type: "submit" });
9110
- if (submit === null || !isString(raw["id"])) {
9423
+ if (submit === null || !isString2(raw["id"])) {
9111
9424
  return null;
9112
9425
  }
9113
9426
  return {
@@ -9120,13 +9433,31 @@ function toReview(raw) {
9120
9433
  };
9121
9434
  }
9122
9435
  function toVerdict(raw) {
9123
- if (!isString(raw["id"]) || !isQuestionList(raw["questions"])) {
9436
+ if (!isString2(raw["id"]) || !isQuestionList(raw["questions"])) {
9124
9437
  return null;
9125
9438
  }
9126
9439
  return { type: "verdict", id: raw["id"], questions: raw["questions"] };
9127
9440
  }
9128
9441
  function toCancel(raw) {
9129
- return isString(raw["id"]) ? { type: "cancel", id: raw["id"] } : null;
9442
+ return isString2(raw["id"]) ? { type: "cancel", id: raw["id"] } : null;
9443
+ }
9444
+ function isNumber(value) {
9445
+ return typeof value === "number" && Number.isFinite(value);
9446
+ }
9447
+ function toState(raw) {
9448
+ const lastAttachAt = raw["lastAttachAt"];
9449
+ if (lastAttachAt !== null && !isString2(lastAttachAt)) {
9450
+ return null;
9451
+ }
9452
+ if (!isNumber(raw["clientCount"]) || !isNumber(raw["waitingDepth"])) {
9453
+ return null;
9454
+ }
9455
+ return {
9456
+ type: "state",
9457
+ clientCount: raw["clientCount"],
9458
+ waitingDepth: raw["waitingDepth"],
9459
+ lastAttachAt
9460
+ };
9130
9461
  }
9131
9462
  function decodeLine(line) {
9132
9463
  let parsed;
@@ -9154,11 +9485,18 @@ function decodeLine(line) {
9154
9485
  if (type === "cancel") {
9155
9486
  return toCancel(parsed);
9156
9487
  }
9488
+ if (type === "status") {
9489
+ return { type: "status" };
9490
+ }
9491
+ if (type === "state") {
9492
+ return toState(parsed);
9493
+ }
9157
9494
  return null;
9158
9495
  }
9159
9496
 
9160
9497
  // src/transports/session/client.ts
9161
9498
  var MS_PER_SECOND = 1e3;
9499
+ var NO_WATCHER = "no pair-mode watcher attached";
9162
9500
  function failOpen(detail) {
9163
9501
  return { reviewed: false, detail };
9164
9502
  }
@@ -9191,7 +9529,7 @@ function requestReview(request, options) {
9191
9529
  let lastError = "";
9192
9530
  socket.on("error", (error) => {
9193
9531
  if (isConnectionRefused(error)) {
9194
- settle(failOpen("no pair-mode watcher attached"));
9532
+ settle(failOpen(NO_WATCHER));
9195
9533
  return;
9196
9534
  }
9197
9535
  lastError = error instanceof Error ? error.message : String(error);
@@ -9221,15 +9559,27 @@ function requestReview(request, options) {
9221
9559
  });
9222
9560
  });
9223
9561
  }
9224
- function reviewInSession(request, config, socketPath) {
9225
- const path = socketPath ?? findSessionSocket(request.filePath);
9226
- if (path === null) {
9227
- return Promise.resolve(failOpen("no pair-mode watcher attached"));
9562
+ async function reviewInSession(request, config, socketPath) {
9563
+ const timeoutMs = config.session.timeout * MS_PER_SECOND;
9564
+ if (socketPath !== void 0) {
9565
+ return await requestReview(request, { socketPath, timeoutMs });
9228
9566
  }
9229
- return requestReview(request, {
9230
- socketPath: path,
9231
- timeoutMs: config.session.timeout * MS_PER_SECOND
9232
- });
9567
+ const key = keyFor(request.sessionId);
9568
+ const sessionPath = key === void 0 ? null : sessionKeySocketPath(key);
9569
+ if (sessionPath !== null && existsSync6(sessionPath)) {
9570
+ const outcome = await requestReview(request, { socketPath: sessionPath, timeoutMs });
9571
+ if (outcome.reviewed) {
9572
+ return outcome;
9573
+ }
9574
+ if (outcome.detail !== NO_WATCHER) {
9575
+ return outcome;
9576
+ }
9577
+ }
9578
+ const directoryPath = findSessionSocket(request.filePath);
9579
+ if (directoryPath === null) {
9580
+ return failOpen(NO_WATCHER);
9581
+ }
9582
+ return await requestReview(request, { socketPath: directoryPath, timeoutMs });
9233
9583
  }
9234
9584
  function createSessionTransport(socketPath) {
9235
9585
  return {
@@ -9250,7 +9600,8 @@ async function runPair(request, config, deps = {}) {
9250
9600
  if (request.before === request.after) {
9251
9601
  return { decision: "allow", reviewed: false };
9252
9602
  }
9253
- if (!isEnabled(request.filePath)) {
9603
+ const key = keyFor(request.sessionId);
9604
+ if (!isEnabled(request.filePath, key)) {
9254
9605
  return { decision: "allow", reviewed: false };
9255
9606
  }
9256
9607
  const transport = deps.transport ?? resolveTransport(config, deps);
@@ -9308,10 +9659,12 @@ async function handleToolCall(event, runPair2 = runPair) {
9308
9659
  if (call === null) {
9309
9660
  return { block: false };
9310
9661
  }
9311
- if (!isEnabled(call.filePath)) {
9662
+ const sessionId = event.sessionId === "" ? void 0 : event.sessionId;
9663
+ const key = keyFor(sessionId);
9664
+ if (!isEnabled(call.filePath, key)) {
9312
9665
  return { block: false };
9313
9666
  }
9314
- const request = simulate(call.tool, call.input, readFileOrEmpty);
9667
+ const request = simulate(call.tool, call.input, readFileOrEmpty, sessionId);
9315
9668
  if (request === null) {
9316
9669
  return { block: false };
9317
9670
  }