@modootoday/pmcp 0.16.0 → 0.17.0

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/cli.js CHANGED
@@ -471,6 +471,8 @@ var init_types = __esm({
471
471
  this.retryable = retryable;
472
472
  this.name = "MailboxError";
473
473
  }
474
+ code;
475
+ retryable;
474
476
  };
475
477
  }
476
478
  });
@@ -1944,11 +1946,11 @@ import {
1944
1946
  fsyncSync as fsyncSync2,
1945
1947
  openSync as openSync4,
1946
1948
  readFileSync as readFileSync34,
1947
- realpathSync as realpathSync6,
1949
+ realpathSync as realpathSync7,
1948
1950
  renameSync as renameSync4,
1949
1951
  writeFileSync as writeFileSync9
1950
1952
  } from "fs";
1951
- import { dirname as dirname21, join as join29 } from "path";
1953
+ import { dirname as dirname21, join as join31 } from "path";
1952
1954
  function privateJson(path, value) {
1953
1955
  const temporary = `${path}.${randomUUID5()}.new`;
1954
1956
  const descriptor = openSync4(temporary, "wx", 384);
@@ -1989,10 +1991,10 @@ var init_store2 = __esm({
1989
1991
  path;
1990
1992
  state;
1991
1993
  constructor(path) {
1992
- this.path = realpathSync6(path);
1994
+ this.path = realpathSync7(path);
1993
1995
  this.state = validateState(readPrivateJson2(path));
1994
1996
  const metadata2 = object8(
1995
- readPrivateJson2(join29(dirname21(this.path), "harness.json"))
1997
+ readPrivateJson2(join31(dirname21(this.path), "harness.json"))
1996
1998
  );
1997
1999
  if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
1998
2000
  throw new Error("foreign_group");
@@ -2014,12 +2016,12 @@ var init_store2 = __esm({
2014
2016
  // src/harness/adapters/process/serialized.ts
2015
2017
  import { spawnSync as spawnSync4 } from "child_process";
2016
2018
  import { randomUUID as randomUUID6 } from "crypto";
2017
- import { existsSync as existsSync25, unlinkSync as unlinkSync2 } from "fs";
2018
- import { dirname as dirname22, join as join30 } from "path";
2019
+ import { existsSync as existsSync26, unlinkSync as unlinkSync2 } from "fs";
2020
+ import { dirname as dirname22, join as join32 } from "path";
2019
2021
  function serialized(operation) {
2020
2022
  const store = new GroupStore(text(operation.groupFile, "group_file"));
2021
2023
  const directory = dirname22(store.path);
2022
- const operationFile = join30(directory, `operation-${randomUUID6()}.json`);
2024
+ const operationFile = join32(directory, `operation-${randomUUID6()}.json`);
2023
2025
  privateJson(operationFile, operation);
2024
2026
  const worker = artifact("worker");
2025
2027
  try {
@@ -2031,7 +2033,7 @@ function serialized(operation) {
2031
2033
  "--no-fork",
2032
2034
  "--conflict-exit-code",
2033
2035
  "73",
2034
- join30(directory, "group-control.lock"),
2036
+ join32(directory, "group-control.lock"),
2035
2037
  process.execPath,
2036
2038
  worker,
2037
2039
  operationFile
@@ -2047,7 +2049,7 @@ function serialized(operation) {
2047
2049
  if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
2048
2050
  return JSON.parse(result.stdout);
2049
2051
  } finally {
2050
- if (existsSync25(operationFile)) unlinkSync2(operationFile);
2052
+ if (existsSync26(operationFile)) unlinkSync2(operationFile);
2051
2053
  }
2052
2054
  }
2053
2055
  var init_serialized = __esm({
@@ -2301,6 +2303,7 @@ var init_connection = __esm({
2301
2303
  constructor(records2) {
2302
2304
  this.records = records2;
2303
2305
  }
2306
+ records;
2304
2307
  tmux(args, options2 = {}) {
2305
2308
  return command(
2306
2309
  "tmux",
@@ -2365,14 +2368,14 @@ import {
2365
2368
  lstatSync as lstatSync6,
2366
2369
  mkdirSync as mkdirSync9,
2367
2370
  readdirSync as readdirSync10,
2368
- realpathSync as realpathSync7,
2371
+ realpathSync as realpathSync8,
2369
2372
  writeFileSync as writeFileSync10
2370
2373
  } from "fs";
2371
2374
  import { homedir as homedir7 } from "os";
2372
- import { join as join31 } from "path";
2375
+ import { join as join33 } from "path";
2373
2376
  function stateDirectory() {
2374
- return join31(
2375
- process.env.XDG_STATE_HOME ?? join31(homedir7(), ".local/state"),
2377
+ return join33(
2378
+ process.env.XDG_STATE_HOME ?? join33(homedir7(), ".local/state"),
2376
2379
  "pmcp/harness"
2377
2380
  );
2378
2381
  }
@@ -2402,12 +2405,12 @@ var init_records = __esm({
2402
2405
  const stat = lstatSync6(directory);
2403
2406
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2404
2407
  throw new Error("unsafe_group_directory");
2405
- this.directory = realpathSync7(directory);
2406
- this.socket = join31(this.directory, "s");
2408
+ this.directory = realpathSync8(directory);
2409
+ this.socket = join33(this.directory, "s");
2407
2410
  if (Buffer.byteLength(this.socket) > 100)
2408
2411
  throw new Error("socket_path_too_long");
2409
- this.metadataPath = join31(this.directory, "harness.json");
2410
- this.configPath = join31(this.directory, "tmux.conf");
2412
+ this.metadataPath = join33(this.directory, "harness.json");
2413
+ this.configPath = join33(this.directory, "tmux.conf");
2411
2414
  this.owner = text(
2412
2415
  object8(readPrivateJson2(this.metadataPath)).owner,
2413
2416
  "owner"
@@ -2417,17 +2420,17 @@ var init_records = __esm({
2417
2420
  static create() {
2418
2421
  const root = stateDirectory();
2419
2422
  mkdirSync9(root, { recursive: true, mode: 448 });
2420
- const directory = join31(root, randomUUID7().slice(0, 16));
2421
- if (Buffer.byteLength(join31(directory, "s")) > 100)
2423
+ const directory = join33(root, randomUUID7().slice(0, 16));
2424
+ if (Buffer.byteLength(join33(directory, "s")) > 100)
2422
2425
  throw new Error("socket_path_too_long");
2423
2426
  mkdirSync9(directory, { mode: 448 });
2424
- mkdirSync9(join31(directory, "sessions"), { mode: 448 });
2425
- privateJson(join31(directory, "harness.json"), {
2427
+ mkdirSync9(join33(directory, "sessions"), { mode: 448 });
2428
+ privateJson(join33(directory, "harness.json"), {
2426
2429
  owner: randomUUID7(),
2427
2430
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2428
2431
  });
2429
2432
  writeFileSync10(
2430
- join31(directory, "tmux.conf"),
2433
+ join33(directory, "tmux.conf"),
2431
2434
  [
2432
2435
  "set -g remain-on-exit on",
2433
2436
  "set -g history-limit 2000",
@@ -2443,7 +2446,7 @@ var init_records = __esm({
2443
2446
  }
2444
2447
  path(id2) {
2445
2448
  runtimeUnit(id2);
2446
- return join31(this.directory, "sessions", `${id2}.json`);
2449
+ return join33(this.directory, "sessions", `${id2}.json`);
2447
2450
  }
2448
2451
  read(id2) {
2449
2452
  const record = object8(readPrivateJson2(this.path(id2)));
@@ -2465,7 +2468,7 @@ var init_records = __esm({
2465
2468
  return record;
2466
2469
  }
2467
2470
  all() {
2468
- return readdirSync10(join31(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2471
+ return readdirSync10(join33(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2469
2472
  }
2470
2473
  save(record) {
2471
2474
  privateJson(this.path(record.id), record);
@@ -2547,8 +2550,8 @@ var init_launch_identity = __esm({
2547
2550
 
2548
2551
  // src/harness/launch/journal.ts
2549
2552
  import { createHash as createHash16 } from "crypto";
2550
- import { existsSync as existsSync26, readdirSync as readdirSync11 } from "fs";
2551
- import { join as join32 } from "path";
2553
+ import { existsSync as existsSync27, readdirSync as readdirSync11 } from "fs";
2554
+ import { join as join34 } from "path";
2552
2555
  function launchJournals(records2) {
2553
2556
  return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
2554
2557
  }
@@ -2556,7 +2559,7 @@ function pendingLaunches(records2) {
2556
2559
  return launchJournals(records2).filter((journal) => {
2557
2560
  const aborted = journal.abortRecord();
2558
2561
  if (aborted) return aborted.state !== "stopped";
2559
- if (!existsSync26(journal.path("committed"))) return true;
2562
+ if (!existsSync27(journal.path("committed"))) return true;
2560
2563
  journal.assertCommitted();
2561
2564
  return false;
2562
2565
  });
@@ -2578,10 +2581,12 @@ var init_journal = __esm({
2578
2581
  this.id = id2;
2579
2582
  runtimeUnit(id2);
2580
2583
  }
2584
+ records;
2585
+ id;
2581
2586
  path(kind2) {
2582
2587
  if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
2583
2588
  throw new Error("invalid_launch_artifact");
2584
- return join32(this.records.directory, `${this.id}.${kind2}.json`);
2589
+ return join34(this.records.directory, `${this.id}.${kind2}.json`);
2585
2590
  }
2586
2591
  intent() {
2587
2592
  const value = object8(readPrivateJson2(this.path("intent")));
@@ -2610,13 +2615,13 @@ var init_journal = __esm({
2610
2615
  throw new Error("invalid_docker_bridge_digest");
2611
2616
  if (!bridge && creation.bridgeSha256 !== void 0)
2612
2617
  throw new Error("ungranted_docker_bridge_digest");
2613
- const policy = new GroupStore(join32(this.records.directory, "group.json")).state.group.dockerPolicy;
2618
+ const policy = new GroupStore(join34(this.records.directory, "group.json")).state.group.dockerPolicy;
2614
2619
  const profile = selectedProfile(
2615
2620
  policy,
2616
2621
  creation.privateProfile,
2617
2622
  text(value.runtime, "runtime")
2618
2623
  );
2619
- const expectedState = profile?.directory ?? join32(
2624
+ const expectedState = profile?.directory ?? join34(
2620
2625
  this.records.directory,
2621
2626
  "containers",
2622
2627
  text(value.nonce, "nonce"),
@@ -2662,7 +2667,7 @@ var init_journal = __esm({
2662
2667
  }
2663
2668
  assertRegistered() {
2664
2669
  const intent = this.intent();
2665
- const group = new GroupStore(join32(this.records.directory, "group.json")).state.group;
2670
+ const group = new GroupStore(join34(this.records.directory, "group.json")).state.group;
2666
2671
  if (group.id !== intent.groupId || group.generation !== intent.generation)
2667
2672
  throw new Error("launch_group_generation_mismatch");
2668
2673
  if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
@@ -2678,12 +2683,12 @@ var init_journal = __esm({
2678
2683
  throw new Error("launch_record_mismatch");
2679
2684
  }
2680
2685
  authorize() {
2681
- if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2686
+ if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
2682
2687
  this.assertRegistered();
2683
2688
  privateJson(this.path("grant"), this.grant());
2684
2689
  }
2685
2690
  assertGrant() {
2686
- if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2691
+ if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
2687
2692
  const grant = object8(readPrivateJson2(this.path("grant")));
2688
2693
  if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
2689
2694
  throw new Error("launch_grant_mismatch");
@@ -2700,7 +2705,7 @@ var init_journal = __esm({
2700
2705
  throw new Error("launch_commit_mismatch");
2701
2706
  }
2702
2707
  abortRecord() {
2703
- if (!existsSync26(this.path("aborted"))) return;
2708
+ if (!existsSync27(this.path("aborted"))) return;
2704
2709
  const value = object8(readPrivateJson2(this.path("aborted")));
2705
2710
  if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
2706
2711
  throw new Error("foreign_launch_abort");
@@ -2749,20 +2754,20 @@ import { homedir as homedir8 } from "os";
2749
2754
  import {
2750
2755
  closeSync as closeSync5,
2751
2756
  constants as constants4,
2752
- existsSync as existsSync27,
2757
+ existsSync as existsSync28,
2753
2758
  fstatSync as fstatSync3,
2754
2759
  lstatSync as lstatSync7,
2755
2760
  openSync as openSync5,
2756
- realpathSync as realpathSync8,
2761
+ realpathSync as realpathSync9,
2757
2762
  statfsSync
2758
2763
  } from "fs";
2759
- import { join as join33, relative as relative11 } from "path";
2764
+ import { join as join35, relative as relative11 } from "path";
2760
2765
  function overlap(left, right) {
2761
2766
  const scope = relative11(left, right);
2762
2767
  return scope === "" || scope !== ".." && !scope.startsWith("../");
2763
2768
  }
2764
2769
  function profileIdentity(directory, projectRoot) {
2765
- if (directory === "/" || directory === realpathSync8(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync8(directory) !== directory)
2770
+ if (directory === "/" || directory === realpathSync9(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync9(directory) !== directory)
2766
2771
  throw new Error("private_profile_scope_not_admitted");
2767
2772
  const stat = lstatSync7(directory);
2768
2773
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
@@ -2770,7 +2775,7 @@ function profileIdentity(directory, projectRoot) {
2770
2775
  if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
2771
2776
  throw new Error("remote_private_profile_not_admitted");
2772
2777
  const lock = openSync5(
2773
- join33(directory, privateProfileLock),
2778
+ join35(directory, privateProfileLock),
2774
2779
  constants4.O_RDONLY | constants4.O_NOFOLLOW
2775
2780
  );
2776
2781
  try {
@@ -2783,14 +2788,14 @@ function profileIdentity(directory, projectRoot) {
2783
2788
  }
2784
2789
  }
2785
2790
  function captureProfile(grant, projectRoot) {
2786
- const directory = realpathSync8(grant.directory);
2787
- if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync8(homedir8()))
2791
+ const directory = realpathSync9(grant.directory);
2792
+ if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync9(homedir8()))
2788
2793
  throw new Error("private_profile_scope_not_admitted");
2789
2794
  const stat = lstatSync7(directory);
2790
2795
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2791
2796
  throw new Error("unsafe_private_profile_directory");
2792
- const lock = join33(directory, privateProfileLock);
2793
- if (!existsSync27(lock)) closeSync5(openSync5(lock, "wx", 384));
2797
+ const lock = join35(directory, privateProfileLock);
2798
+ if (!existsSync28(lock)) closeSync5(openSync5(lock, "wx", 384));
2794
2799
  const identity = profileIdentity(directory, projectRoot);
2795
2800
  if (grant.identity && grant.identity !== identity)
2796
2801
  throw new Error("private_profile_identity_changed");
@@ -2808,7 +2813,7 @@ function assertProfileAvailable(grant, projectRoot) {
2808
2813
  "--nonblock",
2809
2814
  "--conflict-exit-code",
2810
2815
  "73",
2811
- join33(grant.directory, privateProfileLock),
2816
+ join35(grant.directory, privateProfileLock),
2812
2817
  "true"
2813
2818
  ]);
2814
2819
  } catch (error) {
@@ -2828,7 +2833,7 @@ var init_private_profile = __esm({
2828
2833
 
2829
2834
  // src/harness/adapters/docker/profile.ts
2830
2835
  import { homedir as homedir9 } from "os";
2831
- import { lstatSync as lstatSync8, realpathSync as realpathSync9 } from "fs";
2836
+ import { lstatSync as lstatSync8, realpathSync as realpathSync10 } from "fs";
2832
2837
  import { relative as relative12 } from "path";
2833
2838
  function assertLocalDocker() {
2834
2839
  if (process.env.DOCKER_HOST)
@@ -2845,12 +2850,12 @@ function assertLocalDocker() {
2845
2850
  throw new Error("remote_docker_context_not_admitted");
2846
2851
  }
2847
2852
  function loadDockerPolicy(path, projectRoot, runtimes) {
2848
- if (projectRoot === "/" || projectRoot === realpathSync9(homedir9()))
2853
+ if (projectRoot === "/" || projectRoot === realpathSync10(homedir9()))
2849
2854
  throw new Error("docker_project_root_too_broad");
2850
2855
  const inputPath = text(path, "docker_policy_file");
2851
2856
  if (lstatSync8(inputPath).isSymbolicLink())
2852
2857
  throw new Error("symlink_docker_policy_not_admitted");
2853
- const source2 = realpathSync9(inputPath);
2858
+ const source2 = realpathSync10(inputPath);
2854
2859
  const scope = relative12(projectRoot, source2);
2855
2860
  if (scope === ".." || scope.startsWith("../"))
2856
2861
  throw new Error("docker_policy_outside_project");
@@ -2884,14 +2889,14 @@ var init_profile = __esm({
2884
2889
  });
2885
2890
 
2886
2891
  // src/harness/adapters/docker/mounts.ts
2887
- import { lstatSync as lstatSync9, realpathSync as realpathSync10 } from "fs";
2888
- import { join as join34 } from "path";
2892
+ import { lstatSync as lstatSync9, realpathSync as realpathSync11 } from "fs";
2893
+ import { join as join36 } from "path";
2889
2894
  function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
2890
2895
  const state = text(statePath, "container_state_directory");
2891
2896
  const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
2892
2897
  if (profile) assertProfileIdentity(profile, projectRoot);
2893
- const expected = profile?.directory ?? join34(directory, "containers", nonce, "profile");
2894
- if (state !== expected || realpathSync10(state) !== expected)
2898
+ const expected = profile?.directory ?? join36(directory, "containers", nonce, "profile");
2899
+ if (state !== expected || realpathSync11(state) !== expected)
2895
2900
  throw new Error("foreign_container_state_directory");
2896
2901
  const stat = lstatSync9(state);
2897
2902
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
@@ -2985,8 +2990,8 @@ var init_entrypoint = __esm({
2985
2990
  });
2986
2991
 
2987
2992
  // src/harness/adapters/docker/admission.ts
2988
- import { lstatSync as lstatSync10, realpathSync as realpathSync11 } from "fs";
2989
- import { join as join35, relative as relative13 } from "path";
2993
+ import { lstatSync as lstatSync10, realpathSync as realpathSync12 } from "fs";
2994
+ import { join as join37, relative as relative13 } from "path";
2990
2995
  function admitResource(path, group, role, directory, memoryMb, runtime2) {
2991
2996
  if (group.profile !== "docker" || !group.dockerPolicy)
2992
2997
  throw new Error("container_requires_docker_profile");
@@ -2995,7 +3000,7 @@ function admitResource(path, group, role, directory, memoryMb, runtime2) {
2995
3000
  const inputPath = text(path, "container_file");
2996
3001
  if (lstatSync10(inputPath).isSymbolicLink())
2997
3002
  throw new Error("symlink_container_file_not_admitted");
2998
- const source2 = realpathSync11(inputPath);
3003
+ const source2 = realpathSync12(inputPath);
2999
3004
  const scope = relative13(projectRoot, source2);
3000
3005
  if (scope === ".." || scope.startsWith("../"))
3001
3006
  throw new Error("container_file_outside_project");
@@ -3042,7 +3047,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
3042
3047
  )
3043
3048
  ))
3044
3049
  throw new Error("container_runtime_image_not_granted");
3045
- if (config.WorkingDir !== join35("/workspace", relative13(projectRoot, cwd)))
3050
+ if (config.WorkingDir !== join37("/workspace", relative13(projectRoot, cwd)))
3046
3051
  throw new Error("container_cwd_not_granted");
3047
3052
  assertContainerMounts(
3048
3053
  actual,
@@ -3186,7 +3191,7 @@ import {
3186
3191
  readFileSync as readFileSync36,
3187
3192
  writeFileSync as writeFileSync11
3188
3193
  } from "fs";
3189
- import { join as join36 } from "path";
3194
+ import { join as join38 } from "path";
3190
3195
  function source() {
3191
3196
  const path = artifact("egress-bridge");
3192
3197
  if (!path.endsWith(".js"))
@@ -3205,7 +3210,7 @@ function prepareBridge(creation) {
3205
3210
  if (digest(content) !== creation.bridgeSha256)
3206
3211
  throw new Error("docker_bridge_source_changed");
3207
3212
  const descriptor = openSync6(
3208
- join36(creation.state, bridgeName(creation.nonce)),
3213
+ join38(creation.state, bridgeName(creation.nonce)),
3209
3214
  "wx",
3210
3215
  384
3211
3216
  );
@@ -3219,7 +3224,7 @@ function prepareBridge(creation) {
3219
3224
  function assertBridge(creation) {
3220
3225
  if (!creation.egress?.bridgeExecutable) return;
3221
3226
  const descriptor = openSync6(
3222
- join36(creation.state, bridgeName(creation.nonce)),
3227
+ join38(creation.state, bridgeName(creation.nonce)),
3223
3228
  constants5.O_RDONLY | constants5.O_NOFOLLOW
3224
3229
  );
3225
3230
  try {
@@ -3239,7 +3244,7 @@ var init_bridge_artifact = __esm({
3239
3244
  });
3240
3245
 
3241
3246
  // src/harness/adapters/docker/creation-plan.ts
3242
- import { join as join37, relative as relative14 } from "path";
3247
+ import { join as join39, relative as relative14 } from "path";
3243
3248
  function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
3244
3249
  if (group.profile !== "docker" || !group.dockerPolicy)
3245
3250
  throw new Error("container_requires_docker_profile");
@@ -3256,7 +3261,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3256
3261
  throw new Error("invalid_container_image_environment");
3257
3262
  const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
3258
3263
  if (profile) assertProfileAvailable(profile, group.projectRoot);
3259
- const state = profile?.directory ?? join37(directory, "containers", nonce, "profile");
3264
+ const state = profile?.directory ?? join39(directory, "containers", nonce, "profile");
3260
3265
  return {
3261
3266
  name: containerName(group.owner, id2),
3262
3267
  nonce,
@@ -3273,7 +3278,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3273
3278
  preparation.privateProfile
3274
3279
  ),
3275
3280
  Cmd: preparation.args.length ? preparation.args : null,
3276
- WorkingDir: join37("/workspace", relative14(group.projectRoot, cwd)),
3281
+ WorkingDir: join39("/workspace", relative14(group.projectRoot, cwd)),
3277
3282
  User: `${process.getuid?.()}:${process.getgid?.()}`,
3278
3283
  Env: [
3279
3284
  ...environment2.filter(
@@ -3372,12 +3377,12 @@ var init_creation_plan = __esm({
3372
3377
 
3373
3378
  // src/harness/adapters/docker/creation.ts
3374
3379
  import { mkdirSync as mkdirSync10 } from "fs";
3375
- import { join as join38 } from "path";
3380
+ import { join as join40 } from "path";
3376
3381
  async function prepareCreation(journal) {
3377
3382
  const intent = journal.intent();
3378
3383
  const creation = intent.dockerCreation;
3379
3384
  if (!creation) throw new Error("container_creation_missing");
3380
- const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3385
+ const group = new GroupStore(join40(journal.records.directory, "group.json")).state.group;
3381
3386
  const profile = selectedProfile(
3382
3387
  group.dockerPolicy,
3383
3388
  creation.privateProfile,
@@ -3391,7 +3396,7 @@ async function prepareCreation(journal) {
3391
3396
  creation.name,
3392
3397
  creation.configuration
3393
3398
  );
3394
- const current = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3399
+ const current = new GroupStore(join40(journal.records.directory, "group.json")).state.group;
3395
3400
  if (current.id !== intent.groupId || current.generation !== intent.generation)
3396
3401
  throw new Error("launch_group_generation_mismatch");
3397
3402
  const resource = {
@@ -3459,7 +3464,7 @@ function findCreation(journal) {
3459
3464
  assertResourceOwner(resource, actual);
3460
3465
  assertCreationConfiguration(actual, creation);
3461
3466
  if (!intent.resource) {
3462
- const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3467
+ const group = new GroupStore(join40(journal.records.directory, "group.json")).state.group;
3463
3468
  return {
3464
3469
  ...admitClaim(
3465
3470
  { ...resource, runtime: intent.runtime, state: creation.state },
@@ -3496,7 +3501,7 @@ var init_creation = __esm({
3496
3501
  });
3497
3502
 
3498
3503
  // src/harness/launch/recovery.ts
3499
- import { existsSync as existsSync28 } from "fs";
3504
+ import { existsSync as existsSync29 } from "fs";
3500
3505
  function abortLaunch(connection, journal) {
3501
3506
  const intent = journal.intent();
3502
3507
  if (!controllerExited(intent.controllerIdentity))
@@ -3513,7 +3518,7 @@ function abortLaunch(connection, journal) {
3513
3518
  inputReplayed: false
3514
3519
  };
3515
3520
  }
3516
- if (!existsSync28(journal.path("proof"))) {
3521
+ if (!existsSync29(journal.path("proof"))) {
3517
3522
  const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
3518
3523
  if (paneUnknown || unitActive(intent.unit))
3519
3524
  throw new Error("launch_identity_unknown");
@@ -3540,7 +3545,7 @@ function abortLaunch(connection, journal) {
3540
3545
  assertProof(connection, proof);
3541
3546
  connection.tmux(["kill-pane", "-t", proof.session.paneId]);
3542
3547
  }
3543
- if (existsSync28(journal.records.path(intent.id)))
3548
+ if (existsSync29(journal.records.path(intent.id)))
3544
3549
  journal.records.save({
3545
3550
  ...proof.session,
3546
3551
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3567,9 +3572,9 @@ var init_recovery = __esm({
3567
3572
 
3568
3573
  // src/harness/adapters/tmux/lifecycle.ts
3569
3574
  import { randomUUID as randomUUID8 } from "crypto";
3570
- import { existsSync as existsSync29 } from "fs";
3575
+ import { existsSync as existsSync30 } from "fs";
3571
3576
  import { setTimeout as setTimeout2 } from "timers/promises";
3572
- import { join as join39 } from "path";
3577
+ import { join as join41 } from "path";
3573
3578
  async function start(connection, spec) {
3574
3579
  const records2 = connection.records;
3575
3580
  if (spec.budgetSlice !== budgetSlice(records2.owner))
@@ -3583,7 +3588,7 @@ async function start(connection, spec) {
3583
3588
  const nonce = randomUUID8();
3584
3589
  const journal = new LaunchJournal(records2, id2);
3585
3590
  const dockerCreation = spec.dockerPreparation ? creationPlan(
3586
- new GroupStore(join39(records2.directory, "group.json")).state.group,
3591
+ new GroupStore(join41(records2.directory, "group.json")).state.group,
3587
3592
  records2.directory,
3588
3593
  id2,
3589
3594
  nonce,
@@ -3643,7 +3648,7 @@ async function start(connection, spec) {
3643
3648
  `exec ${argv.map(quote2).join(" ")}`
3644
3649
  ]);
3645
3650
  const deadline = Date.now() + 5e3;
3646
- while (!existsSync29(journal.path("proof"))) {
3651
+ while (!existsSync30(journal.path("proof"))) {
3647
3652
  if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
3648
3653
  await setTimeout2(25);
3649
3654
  }
@@ -3654,7 +3659,7 @@ async function start(connection, spec) {
3654
3659
  }
3655
3660
  function stop(connection, id2) {
3656
3661
  const journal = new LaunchJournal(connection.records, id2);
3657
- if (existsSync29(journal.path("intent"))) {
3662
+ if (existsSync30(journal.path("intent"))) {
3658
3663
  abortLaunch(connection, journal);
3659
3664
  return;
3660
3665
  }
@@ -3681,7 +3686,7 @@ function cleanup(connection) {
3681
3686
  }
3682
3687
  for (const record of records2) {
3683
3688
  if (record.resource) stopResource(record.resource);
3684
- if (record.stoppedAt || !existsSync29(
3689
+ if (record.stoppedAt || !existsSync30(
3685
3690
  new LaunchJournal(connection.records, record.id).path("intent")
3686
3691
  )) {
3687
3692
  stopUnit(record.unit);
@@ -3713,7 +3718,7 @@ var init_lifecycle2 = __esm({
3713
3718
  // src/harness/adapters/tmux/input.ts
3714
3719
  import { randomUUID as randomUUID9 } from "crypto";
3715
3720
  import { closeSync as closeSync7, openSync as openSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
3716
- import { join as join40 } from "path";
3721
+ import { join as join42 } from "path";
3717
3722
  function validateText(text3) {
3718
3723
  if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
3719
3724
  throw new Error("invalid_text");
@@ -3744,7 +3749,7 @@ function write(connection, id2, text3, submit) {
3744
3749
  validateText(text3);
3745
3750
  const session = connection.inspect(id2);
3746
3751
  if (!session.alive) throw new Error("runtime_exited");
3747
- const lock = join40(connection.records.directory, `${id2}.input.lock`);
3752
+ const lock = join42(connection.records.directory, `${id2}.input.lock`);
3748
3753
  const descriptor = openSync7(lock, "wx", 384);
3749
3754
  const buffer = `pmcp-${randomUUID9()}`;
3750
3755
  try {
@@ -3862,6 +3867,7 @@ var init_backend = __esm({
3862
3867
  this.runDirectory = recordStore.directory;
3863
3868
  this.metadataPath = recordStore.metadataPath;
3864
3869
  }
3870
+ recordStore;
3865
3871
  connection;
3866
3872
  owner;
3867
3873
  socket;
@@ -3902,10 +3908,10 @@ var init_backend = __esm({
3902
3908
  });
3903
3909
 
3904
3910
  // src/harness/adapters/systemd/budget.ts
3905
- import { join as join41 } from "path";
3911
+ import { join as join43 } from "path";
3906
3912
  async function budgetOperation(directory, operation) {
3907
3913
  const backend = openBackend(directory);
3908
- const path = join41(directory, "runtime-policy.json");
3914
+ const path = join43(directory, "runtime-policy.json");
3909
3915
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
3910
3916
  const slice = budgetSlice(backend.owner);
3911
3917
  if (operation.action === "configure") {
@@ -4006,7 +4012,7 @@ var init_budget = __esm({
4006
4012
 
4007
4013
  // src/harness/adapters/systemd/configure.ts
4008
4014
  import { execFileSync as execFileSync3 } from "child_process";
4009
- import { join as join42 } from "path";
4015
+ import { join as join44 } from "path";
4010
4016
  function configureBudget(context, input) {
4011
4017
  const { store, backend } = context;
4012
4018
  assertOwnedRecords(backend);
@@ -4028,7 +4034,7 @@ function configureBudget(context, input) {
4028
4034
  throw new Error("capacity_below_active_sessions");
4029
4035
  if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
4030
4036
  throw new Error("budget_below_reserved_memory");
4031
- const path = join42(backend.runDirectory, "runtime-policy.json");
4037
+ const path = join44(backend.runDirectory, "runtime-policy.json");
4032
4038
  const policy = object8(readPrivateJson2(path));
4033
4039
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
4034
4040
  const slice = budgetSlice(backend.owner);
@@ -4123,10 +4129,10 @@ var init_admission2 = __esm({
4123
4129
 
4124
4130
  // src/harness/groups/service.ts
4125
4131
  import { randomUUID as randomUUID10 } from "crypto";
4126
- import { realpathSync as realpathSync12, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
4127
- import { join as join43 } from "path";
4132
+ import { realpathSync as realpathSync13, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
4133
+ import { join as join45 } from "path";
4128
4134
  async function createGroup(input) {
4129
- const projectRoot = realpathSync12(text(input.projectRoot, "project_root"));
4135
+ const projectRoot = realpathSync13(text(input.projectRoot, "project_root"));
4130
4136
  if (!statSync17(projectRoot).isDirectory())
4131
4137
  throw new Error("invalid_project_root");
4132
4138
  const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
@@ -4157,7 +4163,7 @@ async function createGroup(input) {
4157
4163
  sessions: [],
4158
4164
  ...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
4159
4165
  };
4160
- const groupFile = join43(backend.runDirectory, "group.json");
4166
+ const groupFile = join45(backend.runDirectory, "group.json");
4161
4167
  privateJson(groupFile, { group, leases: [], deliveries: [] });
4162
4168
  try {
4163
4169
  await budgetOperation(backend.runDirectory, {
@@ -4170,7 +4176,7 @@ async function createGroup(input) {
4170
4176
  await budgetOperation(backend.runDirectory, { action: "cleanup" });
4171
4177
  unlinkSync4(groupFile);
4172
4178
  } catch (cleanupError) {
4173
- privateJson(join43(backend.runDirectory, "initialization-failure.json"), {
4179
+ privateJson(join45(backend.runDirectory, "initialization-failure.json"), {
4174
4180
  error: String(error),
4175
4181
  cleanupError: String(cleanupError),
4176
4182
  recoveryRequired: true
@@ -4232,16 +4238,16 @@ var init_service2 = __esm({
4232
4238
  });
4233
4239
 
4234
4240
  // src/harness/groups/catalog.ts
4235
- import { existsSync as existsSync30, readdirSync as readdirSync12 } from "fs";
4236
- import { join as join44 } from "path";
4241
+ import { existsSync as existsSync31, readdirSync as readdirSync12 } from "fs";
4242
+ import { join as join46 } from "path";
4237
4243
  function listGroups() {
4238
4244
  const directory = stateDirectory();
4239
- if (!existsSync30(directory)) return { groups: [] };
4245
+ if (!existsSync31(directory)) return { groups: [] };
4240
4246
  const groups2 = [];
4241
4247
  for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
4242
4248
  if (!entry2.isDirectory()) continue;
4243
- const path = join44(directory, entry2.name, "group.json");
4244
- if (!existsSync30(path)) continue;
4249
+ const path = join46(directory, entry2.name, "group.json");
4250
+ if (!existsSync31(path)) continue;
4245
4251
  try {
4246
4252
  const store = new GroupStore(path);
4247
4253
  groups2.push({ groupFile: store.path, group: store.state.group });
@@ -4438,7 +4444,7 @@ var init_watch = __esm({
4438
4444
 
4439
4445
  // src/harness/control/leases.ts
4440
4446
  import { randomUUID as randomUUID11 } from "crypto";
4441
- import { join as join45 } from "path";
4447
+ import { join as join47 } from "path";
4442
4448
  function resolveLease(context, file2, allowExpired = false) {
4443
4449
  const credential2 = object8(readPrivateJson2(file2));
4444
4450
  const lease = context.store.state.leases.find(
@@ -4482,7 +4488,7 @@ function acquireControl(context, sessionId, controller, mode) {
4482
4488
  );
4483
4489
  store.state.leases.push(lease);
4484
4490
  store.save();
4485
- const leaseFile = join45(backend.runDirectory, `lease-${randomUUID11()}.json`);
4491
+ const leaseFile = join47(backend.runDirectory, `lease-${randomUUID11()}.json`);
4486
4492
  privateJson(leaseFile, { token: lease.token });
4487
4493
  return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
4488
4494
  }
@@ -4629,12 +4635,12 @@ var init_service5 = __esm({
4629
4635
  });
4630
4636
 
4631
4637
  // src/harness/adapters/process/recover-lock.ts
4632
- import { existsSync as existsSync31, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
4633
- import { join as join46 } from "path";
4638
+ import { existsSync as existsSync32, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
4639
+ import { join as join48 } from "path";
4634
4640
  function recoverInputLock(backend, sessionId) {
4635
4641
  backend.inspect(sessionId);
4636
- const path = join46(backend.runDirectory, `${sessionId}.input.lock`);
4637
- if (!existsSync31(path)) return;
4642
+ const path = join48(backend.runDirectory, `${sessionId}.input.lock`);
4643
+ if (!existsSync32(path)) return;
4638
4644
  const before = lstatSync11(path);
4639
4645
  const record = object8(readPrivateJson2(path));
4640
4646
  if (record.id !== sessionId) throw new Error("foreign_writer_lock");
@@ -4654,7 +4660,7 @@ var init_recover_lock = __esm({
4654
4660
  });
4655
4661
 
4656
4662
  // src/harness/launch/service.ts
4657
- import { existsSync as existsSync32 } from "fs";
4663
+ import { existsSync as existsSync33 } from "fs";
4658
4664
  function assertLaunchAdmission(records2) {
4659
4665
  for (const journal of launchJournals(records2)) {
4660
4666
  if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
@@ -4679,12 +4685,12 @@ function inspectPendingStarts(context) {
4679
4685
  role: journal.intent().role,
4680
4686
  runtime: journal.intent().runtime,
4681
4687
  controllerExited: controllerExited(journal.intent().controllerIdentity),
4682
- proofPresent: existsSync32(journal.path("proof")),
4683
- recordPresent: existsSync32(journal.records.path(journal.id)),
4688
+ proofPresent: existsSync33(journal.path("proof")),
4689
+ recordPresent: existsSync33(journal.records.path(journal.id)),
4684
4690
  registered: context.store.state.group.sessions.some(
4685
4691
  (session) => session.id === journal.id
4686
4692
  ),
4687
- authorizationPresent: existsSync32(journal.path("grant")),
4693
+ authorizationPresent: existsSync33(journal.path("grant")),
4688
4694
  processingConfirmed: false,
4689
4695
  inputReplayed: false
4690
4696
  }));
@@ -4702,7 +4708,7 @@ var init_service6 = __esm({
4702
4708
  });
4703
4709
 
4704
4710
  // src/harness/sessions/service.ts
4705
- import { realpathSync as realpathSync13 } from "fs";
4711
+ import { realpathSync as realpathSync14 } from "fs";
4706
4712
  import { relative as relative15 } from "path";
4707
4713
  async function startSession(context, input) {
4708
4714
  const { store, backend } = context;
@@ -4736,7 +4742,7 @@ async function startSession(context, input) {
4736
4742
  );
4737
4743
  if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
4738
4744
  }
4739
- const cwd = realpathSync13(text(input.cwd ?? group.projectRoot, "cwd"));
4745
+ const cwd = realpathSync14(text(input.cwd ?? group.projectRoot, "cwd"));
4740
4746
  const scope = relative15(group.projectRoot, cwd);
4741
4747
  if (scope === ".." || scope.startsWith("../"))
4742
4748
  throw new Error("cwd_outside_project");
@@ -5199,9 +5205,9 @@ var init_invoke = __esm({
5199
5205
  });
5200
5206
 
5201
5207
  // src/tui/adapters/process/runtime.ts
5202
- import { dirname as dirname24, join as join47 } from "path";
5208
+ import { dirname as dirname24, join as join49 } from "path";
5203
5209
  import { fileURLToPath as fileURLToPath4 } from "url";
5204
- import { existsSync as existsSync33, realpathSync as realpathSync14 } from "fs";
5210
+ import { existsSync as existsSync34, realpathSync as realpathSync15 } from "fs";
5205
5211
  function dependencies() {
5206
5212
  const tmux = locateExecutable("tmux");
5207
5213
  const flock = locateExecutable("flock");
@@ -5235,9 +5241,9 @@ function runtime() {
5235
5241
  throw new Error("tui_dependencies_unavailable");
5236
5242
  const current = fileURLToPath4(import.meta.url);
5237
5243
  const directory = dirname24(current);
5238
- const bundled = join47(directory, "tui-worker.js");
5239
- if (!existsSync33(bundled)) throw new Error("tui_build_required");
5240
- return { node: realpathSync14(available.node), worker: bundled };
5244
+ const bundled = join49(directory, "tui-worker.js");
5245
+ if (!existsSync34(bundled)) throw new Error("tui_build_required");
5246
+ return { node: realpathSync15(available.node), worker: bundled };
5241
5247
  }
5242
5248
  var init_runtime2 = __esm({
5243
5249
  "src/tui/adapters/process/runtime.ts"() {
@@ -5324,32 +5330,32 @@ var init_diagnostics = __esm({
5324
5330
  // src/tui/state/store.ts
5325
5331
  import { randomUUID as randomUUID13 } from "crypto";
5326
5332
  import {
5327
- existsSync as existsSync34,
5333
+ existsSync as existsSync35,
5328
5334
  lstatSync as lstatSync12,
5329
5335
  mkdirSync as mkdirSync11,
5330
- realpathSync as realpathSync15,
5336
+ realpathSync as realpathSync16,
5331
5337
  unlinkSync as unlinkSync6,
5332
5338
  writeFileSync as writeFileSync13
5333
5339
  } from "fs";
5334
5340
  import { homedir as homedir10 } from "os";
5335
- import { dirname as dirname25, join as join48 } from "path";
5341
+ import { dirname as dirname25, join as join50 } from "path";
5336
5342
  function createDirectory() {
5337
- const root = join48(
5338
- process.env.XDG_STATE_HOME ?? join48(homedir10(), ".local/state"),
5343
+ const root = join50(
5344
+ process.env.XDG_STATE_HOME ?? join50(homedir10(), ".local/state"),
5339
5345
  "pmcp/tui"
5340
5346
  );
5341
5347
  mkdirSync11(root, { recursive: true, mode: 448 });
5342
- const directory = join48(realpathSync15(root), randomUUID13().slice(0, 16));
5343
- if (Buffer.byteLength(join48(directory, "s")) > 100)
5348
+ const directory = join50(realpathSync16(root), randomUUID13().slice(0, 16));
5349
+ if (Buffer.byteLength(join50(directory, "s")) > 100)
5344
5350
  throw new Error("socket_path_too_long");
5345
5351
  mkdirSync11(directory, { mode: 448 });
5346
5352
  const owner = randomUUID13();
5347
- privateJson(join48(directory, "owner.json"), {
5353
+ privateJson(join50(directory, "owner.json"), {
5348
5354
  owner,
5349
5355
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5350
5356
  });
5351
5357
  writeFileSync13(
5352
- join48(directory, "tmux.conf"),
5358
+ join50(directory, "tmux.conf"),
5353
5359
  [
5354
5360
  "set -g remain-on-exit on",
5355
5361
  "set -g history-limit 2000",
@@ -5364,13 +5370,13 @@ function createDirectory() {
5364
5370
  return { directory, owner };
5365
5371
  }
5366
5372
  function readView(path) {
5367
- const file2 = realpathSync15(path);
5373
+ const file2 = realpathSync16(path);
5368
5374
  const directory = dirname25(file2);
5369
5375
  const stat = lstatSync12(directory);
5370
5376
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
5371
5377
  throw new Error("unsafe_view_directory");
5372
5378
  const value = object8(readPrivateJson2(file2));
5373
- const metadata2 = object8(readPrivateJson2(join48(directory, "owner.json")));
5379
+ const metadata2 = object8(readPrivateJson2(join50(directory, "owner.json")));
5374
5380
  if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
5375
5381
  throw new Error("foreign_view_owner");
5376
5382
  for (const key of [
@@ -5407,8 +5413,8 @@ function readView(path) {
5407
5413
  throw new Error("invalid_view_identity");
5408
5414
  }
5409
5415
  const view = value;
5410
- const fallback = join48(directory, "callback-failure.json");
5411
- if (existsSync34(fallback)) {
5416
+ const fallback = join50(directory, "callback-failure.json");
5417
+ if (existsSync35(fallback)) {
5412
5418
  const recorded = object8(readPrivateJson2(fallback));
5413
5419
  const failure2 = object8(recorded.failure);
5414
5420
  if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
@@ -5419,8 +5425,8 @@ function readView(path) {
5419
5425
  return { file: file2, view };
5420
5426
  }
5421
5427
  function dismissFailure(file2, view) {
5422
- const fallback = join48(view.directory, "callback-failure.json");
5423
- if (existsSync34(fallback)) unlinkSync6(fallback);
5428
+ const fallback = join50(view.directory, "callback-failure.json");
5429
+ if (existsSync35(fallback)) unlinkSync6(fallback);
5424
5430
  delete view.failure;
5425
5431
  saveView(file2, view);
5426
5432
  }
@@ -5525,7 +5531,7 @@ var init_budget2 = __esm({
5525
5531
 
5526
5532
  // src/tui/adapters/tmux/connection.ts
5527
5533
  import { spawn as spawn3 } from "child_process";
5528
- import { join as join49 } from "path";
5534
+ import { join as join51 } from "path";
5529
5535
  var ViewConnection;
5530
5536
  var init_connection2 = __esm({
5531
5537
  "src/tui/adapters/tmux/connection.ts"() {
@@ -5538,8 +5544,11 @@ var init_connection2 = __esm({
5538
5544
  this.directory = directory;
5539
5545
  this.owner = owner;
5540
5546
  this.identity = identity;
5541
- this.socket = join49(directory, "s");
5547
+ this.socket = join51(directory, "s");
5542
5548
  }
5549
+ directory;
5550
+ owner;
5551
+ identity;
5543
5552
  socket;
5544
5553
  raw(args) {
5545
5554
  const env = { ...process.env };
@@ -5548,14 +5557,14 @@ var init_connection2 = __esm({
5548
5557
  "-S",
5549
5558
  this.socket,
5550
5559
  "-f",
5551
- join49(this.directory, "tmux.conf"),
5560
+ join51(this.directory, "tmux.conf"),
5552
5561
  ...args
5553
5562
  ];
5554
5563
  if (args[0] === "new-session" && !this.identity)
5555
5564
  return startPresentation(this.owner, options2, env).trim();
5556
5565
  return command(
5557
5566
  "tmux",
5558
- ["-S", this.socket, "-f", join49(this.directory, "tmux.conf"), ...args],
5567
+ ["-S", this.socket, "-f", join51(this.directory, "tmux.conf"), ...args],
5559
5568
  { env }
5560
5569
  ).trim();
5561
5570
  }
@@ -5619,7 +5628,7 @@ var init_connection2 = __esm({
5619
5628
  });
5620
5629
 
5621
5630
  // src/tui/application/feedback.ts
5622
- import { join as join50 } from "path";
5631
+ import { join as join52 } from "path";
5623
5632
  function describeFailure(action, error) {
5624
5633
  const message = error instanceof Error ? error.message : "view_action_failed";
5625
5634
  const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
@@ -5792,6 +5801,8 @@ var init_view = __esm({
5792
5801
  view.serverIdentity
5793
5802
  );
5794
5803
  }
5804
+ view;
5805
+ stateFile;
5795
5806
  connection;
5796
5807
  shell(argv) {
5797
5808
  return `exec ${argv.map(quote2).join(" ")}`;
@@ -6035,6 +6046,7 @@ var init_common = __esm({
6035
6046
  constructor(view) {
6036
6047
  this.view = view;
6037
6048
  }
6049
+ view;
6038
6050
  async call(family, action, input = {}) {
6039
6051
  const group = new GroupStore(this.view.groupFile).state.group;
6040
6052
  if (group.owner !== this.view.groupOwner) throw new Error("foreign_group");
@@ -6068,16 +6080,16 @@ var init_common = __esm({
6068
6080
  });
6069
6081
 
6070
6082
  // src/tui/application/create.ts
6071
- import { join as join51 } from "path";
6072
- import { realpathSync as realpathSync16 } from "fs";
6083
+ import { join as join53 } from "path";
6084
+ import { realpathSync as realpathSync17 } from "fs";
6073
6085
  async function createView(input) {
6074
6086
  const plan2 = planLayout(input.columns, input.rows);
6075
6087
  const helpers = runtime();
6076
6088
  const mailbox = input.mailbox ? {
6077
- config: realpathSync16(input.mailbox.config),
6078
- actorFile: realpathSync16(input.mailbox.actorFile)
6089
+ config: realpathSync17(input.mailbox.config),
6090
+ actorFile: realpathSync17(input.mailbox.actorFile)
6079
6091
  } : void 0;
6080
- const groupFile = realpathSync16(input.groupFile);
6092
+ const groupFile = realpathSync17(input.groupFile);
6081
6093
  const group = new GroupStore(groupFile).state.group;
6082
6094
  const common2 = new CommonHarness({
6083
6095
  groupFile,
@@ -6090,7 +6102,7 @@ async function createView(input) {
6090
6102
  );
6091
6103
  if (!main2) throw new Error("view_requires_live_main");
6092
6104
  const { directory, owner } = createDirectory();
6093
- const file2 = join51(directory, "view.json");
6105
+ const file2 = join53(directory, "view.json");
6094
6106
  const connection = new ViewConnection(directory, owner);
6095
6107
  const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
6096
6108
  const result = connection.raw([
@@ -6318,7 +6330,7 @@ var init_navigation = __esm({
6318
6330
  });
6319
6331
 
6320
6332
  // src/tui/application/history.ts
6321
- import { join as join52 } from "path";
6333
+ import { join as join54 } from "path";
6322
6334
  async function observeHistory(context) {
6323
6335
  const { view, renderer, common: common2, file: file2 } = context;
6324
6336
  await releaseControl(context);
@@ -6326,7 +6338,7 @@ async function observeHistory(context) {
6326
6338
  sessionId: view.target,
6327
6339
  input: { lines: 2e3, maxBytes: 65536 }
6328
6340
  });
6329
- privateJson(join52(view.directory, "history.json"), {
6341
+ privateJson(join54(view.directory, "history.json"), {
6330
6342
  sessionId: view.target,
6331
6343
  content: history.content,
6332
6344
  truncated: history.truncated
@@ -6402,8 +6414,8 @@ var init_mailbox = __esm({
6402
6414
  });
6403
6415
 
6404
6416
  // src/tui/application/lifecycle.ts
6405
- import { existsSync as existsSync35, unlinkSync as unlinkSync7 } from "fs";
6406
- import { join as join53 } from "path";
6417
+ import { existsSync as existsSync36, unlinkSync as unlinkSync7 } from "fs";
6418
+ import { join as join55 } from "path";
6407
6419
  function resizeView({ view, renderer, input }) {
6408
6420
  renderer.resize(view, planLayout(input.columns, input.rows));
6409
6421
  }
@@ -6418,8 +6430,8 @@ async function stopTarget(context) {
6418
6430
  async function closeView(context) {
6419
6431
  await returnToMain(context);
6420
6432
  context.renderer.connection.cleanup();
6421
- const history = join53(context.view.directory, "history.json");
6422
- if (existsSync35(history)) unlinkSync7(history);
6433
+ const history = join55(context.view.directory, "history.json");
6434
+ if (existsSync36(history)) unlinkSync7(history);
6423
6435
  context.view.state = "closed";
6424
6436
  saveView(context.file, context.view);
6425
6437
  }
@@ -6676,7 +6688,7 @@ var init_worker_start = __esm({
6676
6688
  });
6677
6689
 
6678
6690
  // src/tui/startup/config.ts
6679
- import { realpathSync as realpathSync17 } from "fs";
6691
+ import { realpathSync as realpathSync18 } from "fs";
6680
6692
  import { resolve as resolve19 } from "path";
6681
6693
  function startupSettings(request3) {
6682
6694
  const path = request3.config ? resolve19(request3.cwd, request3.config) : findConfig(request3.cwd);
@@ -6729,7 +6741,7 @@ function startupSettings(request3) {
6729
6741
  fail2("session memory exceeds the group budget");
6730
6742
  const groupFile = string("group_file");
6731
6743
  return {
6732
- projectRoot: realpathSync17(config?.dir ?? request3.cwd),
6744
+ projectRoot: realpathSync18(config?.dir ?? request3.cwd),
6733
6745
  ...config ? { configFile: config.path } : {},
6734
6746
  runtime: runtime2,
6735
6747
  allowedRuntimes: [.../* @__PURE__ */ new Set([...available, runtime2])],
@@ -6758,16 +6770,16 @@ var init_config3 = __esm({
6758
6770
 
6759
6771
  // src/tui/startup/state.ts
6760
6772
  import { createHash as createHash19 } from "crypto";
6761
- import { existsSync as existsSync36, lstatSync as lstatSync13, mkdirSync as mkdirSync12 } from "fs";
6773
+ import { existsSync as existsSync37, lstatSync as lstatSync13, mkdirSync as mkdirSync12 } from "fs";
6762
6774
  import { homedir as homedir11 } from "os";
6763
- import { dirname as dirname26, join as join54 } from "path";
6775
+ import { dirname as dirname26, join as join56 } from "path";
6764
6776
  function startupFile(projectRoot) {
6765
- const root = join54(
6766
- process.env.XDG_STATE_HOME ?? join54(homedir11(), ".local/state"),
6777
+ const root = join56(
6778
+ process.env.XDG_STATE_HOME ?? join56(homedir11(), ".local/state"),
6767
6779
  "pmcp/launcher"
6768
6780
  );
6769
6781
  const key = createHash19("sha256").update(projectRoot).digest("hex");
6770
- return join54(root, `${key}.json`);
6782
+ return join56(root, `${key}.json`);
6771
6783
  }
6772
6784
  function ensureStartupDirectory(file2) {
6773
6785
  const directory = dirname26(file2);
@@ -6777,7 +6789,7 @@ function ensureStartupDirectory(file2) {
6777
6789
  throw new Error("unsafe_startup_directory");
6778
6790
  }
6779
6791
  function readStartup(file2, projectRoot) {
6780
- if (!existsSync36(file2)) return null;
6792
+ if (!existsSync37(file2)) return null;
6781
6793
  const value = object8(readPrivateJson2(file2));
6782
6794
  if (value.schemaVersion !== 1 || value.projectRoot !== projectRoot)
6783
6795
  throw new Error("foreign_startup_state");
@@ -7374,7 +7386,7 @@ init_entry();
7374
7386
 
7375
7387
  // src/intent.ts
7376
7388
  function normaliseIntent(intent) {
7377
- return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
7389
+ return intent.normalize("NFC").toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
7378
7390
  }
7379
7391
 
7380
7392
  // src/cache.ts
@@ -9847,7 +9859,7 @@ function registerMailboxTools(server, service) {
9847
9859
 
9848
9860
  // src/mcp.ts
9849
9861
  var SERVER_NAME = "pmcp";
9850
- var SERVER_VERSION = "0.16.0";
9862
+ var SERVER_VERSION = "0.17.0";
9851
9863
  function registerSkillTools(server, tools) {
9852
9864
  registerCatalogTool(server, tools);
9853
9865
  registerFindTool(server, tools);
@@ -11990,7 +12002,7 @@ import { homedir as homedir6 } from "os";
11990
12002
  // src/project/apply.ts
11991
12003
  import { createHash as createHash13 } from "crypto";
11992
12004
  import {
11993
- existsSync as existsSync20,
12005
+ existsSync as existsSync21,
11994
12006
  lstatSync as lstatSync5,
11995
12007
  mkdirSync as mkdirSync7,
11996
12008
  readFileSync as readFileSync25,
@@ -11998,7 +12010,7 @@ import {
11998
12010
  readlinkSync as readlinkSync2,
11999
12011
  rmSync as rmSync5
12000
12012
  } from "fs";
12001
- import { dirname as dirname18, join as join24, posix as posix2 } from "path";
12013
+ import { dirname as dirname18, join as join25, posix as posix2 } from "path";
12002
12014
  import { parse as parse5 } from "smol-toml";
12003
12015
 
12004
12016
  // src/project/backup.ts
@@ -12190,6 +12202,56 @@ function splitKeyed(path) {
12190
12202
 
12191
12203
  // src/project/mcp.ts
12192
12204
  import { isAbsolute as isAbsolute5, relative as relative9 } from "path";
12205
+
12206
+ // src/project/adapters/opencode.ts
12207
+ import { existsSync as existsSync17 } from "fs";
12208
+ import { join as join22 } from "path";
12209
+ var OPENCODE_CONFIG = "opencode.json";
12210
+ function checkOpenCodeConfig(root) {
12211
+ if (existsSync17(join22(root, "opencode.jsonc"))) {
12212
+ throw new ProjectError(
12213
+ "OpenCode JSONC projection is not supported; reconcile opencode.jsonc before projecting opencode.json"
12214
+ );
12215
+ }
12216
+ }
12217
+ function opencodeMcpEntry(server, started) {
12218
+ if (server.tools !== void 0) {
12219
+ throw new ProjectError(
12220
+ "OpenCode per-server tool filtering is not qualified; limit this server's targets"
12221
+ );
12222
+ }
12223
+ const timeout = server.native?.opencode;
12224
+ if (server.url !== void 0) {
12225
+ const headers = { ...server.headers };
12226
+ for (const [key, name] of Object.entries(server.headerEnv ?? {})) {
12227
+ headers[key] = `{env:${name}}`;
12228
+ }
12229
+ if (server.bearerTokenEnvVar) {
12230
+ headers["Authorization"] = `Bearer {env:${server.bearerTokenEnvVar}}`;
12231
+ }
12232
+ return {
12233
+ type: "remote",
12234
+ url: server.url,
12235
+ enabled: true,
12236
+ ...Object.keys(headers).length > 0 ? { headers } : {},
12237
+ ...timeout
12238
+ };
12239
+ }
12240
+ if (!started) {
12241
+ throw new ProjectError(
12242
+ "OpenCode local MCP projection requires a launch specification"
12243
+ );
12244
+ }
12245
+ return {
12246
+ type: "local",
12247
+ command: [started.command, ...started.args],
12248
+ enabled: true,
12249
+ ...Object.keys(started.env).length > 0 ? { environment: { ...started.env } } : {},
12250
+ ...timeout
12251
+ };
12252
+ }
12253
+
12254
+ // src/project/mcp.ts
12193
12255
  var runtimeByTool = {
12194
12256
  claude: "claude-code",
12195
12257
  codex: "codex-cli",
@@ -12197,6 +12259,15 @@ var runtimeByTool = {
12197
12259
  grok: "grok-cli",
12198
12260
  antigravity: "antigravity"
12199
12261
  };
12262
+ function mailboxRuntime(tool) {
12263
+ const runtime2 = runtimeByTool[tool];
12264
+ if (!runtime2) {
12265
+ throw new ProjectError(
12266
+ "OpenCode mailbox runtime binding is not qualified; limit this server's targets"
12267
+ );
12268
+ }
12269
+ return runtime2;
12270
+ }
12200
12271
  var ROOT_VAR = "${PROJECT_ROOT}";
12201
12272
  var ANTIGRAVITY_MCP_PLUGIN = ".agents/plugins/pmcp-mcp";
12202
12273
  var VARIABLE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/gu;
@@ -12332,18 +12403,19 @@ function httpEntry(server, tool) {
12332
12403
  function nativeEntry(spec, alias, server, tool) {
12333
12404
  let result;
12334
12405
  if (server.url !== void 0) {
12406
+ if (tool === "opencode") return opencodeMcpEntry(server);
12335
12407
  result = httpEntry(server, tool);
12336
12408
  } else {
12337
12409
  const env = { ...server.env };
12338
12410
  if (server.mailbox) {
12339
- env["PMCP_MAILBOX_RUNTIME"] = runtimeByTool[tool];
12411
+ env["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
12340
12412
  }
12341
12413
  let started;
12342
12414
  if (Object.keys(server.envFrom ?? {}).length > 0) {
12343
12415
  const entry2 = relative9(spec.root, process.argv[1] ?? "");
12344
12416
  const driverEnv = {};
12345
12417
  if (server.mailbox) {
12346
- driverEnv["PMCP_MAILBOX_RUNTIME"] = runtimeByTool[tool];
12418
+ driverEnv["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
12347
12419
  }
12348
12420
  if (!isAbsolute5(entry2) && !entry2.startsWith("..")) {
12349
12421
  started = launch({
@@ -12361,6 +12433,7 @@ function nativeEntry(spec, alias, server, tool) {
12361
12433
  } else {
12362
12434
  started = launch({ ...server, env });
12363
12435
  }
12436
+ if (tool === "opencode") return opencodeMcpEntry(server, started);
12364
12437
  result = { command: started.command, args: [...started.args] };
12365
12438
  if (Object.keys(started.env).length > 0) {
12366
12439
  result["env"] = { ...started.env };
@@ -12417,9 +12490,11 @@ function mcpOutputs(spec) {
12417
12490
  const jsonFiles = {
12418
12491
  claude: ".mcp.json",
12419
12492
  gemini: ".gemini/settings.json",
12420
- antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json`
12493
+ antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json`,
12494
+ opencode: OPENCODE_CONFIG
12421
12495
  };
12422
12496
  for (const tool of spec.tools) {
12497
+ if (tool === "opencode") checkOpenCodeConfig(spec.root);
12423
12498
  const servers = serversForTool(spec, tool);
12424
12499
  if (servers.length === 0) {
12425
12500
  continue;
@@ -12438,7 +12513,7 @@ function mcpOutputs(spec) {
12438
12513
  outputs.push(
12439
12514
  jsonOutput(
12440
12515
  file2,
12441
- ["mcpServers", alias],
12516
+ [tool === "opencode" ? "mcp" : "mcpServers", alias],
12442
12517
  nativeEntry(spec, alias, server, tool)
12443
12518
  )
12444
12519
  );
@@ -12454,14 +12529,14 @@ function mcpOutputs(spec) {
12454
12529
 
12455
12530
  // src/project/hooks.ts
12456
12531
  import { createHash as createHash12 } from "crypto";
12457
- import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
12458
- import { join as join22 } from "path";
12532
+ import { existsSync as existsSync19, readFileSync as readFileSync23 } from "fs";
12533
+ import { join as join23 } from "path";
12459
12534
  import { fileURLToPath as fileURLToPath2 } from "url";
12460
12535
 
12461
12536
  // src/project/hook-adapter.ts
12462
12537
  import { spawnSync as spawnSync2 } from "child_process";
12463
12538
  import { createHash as createHash11 } from "crypto";
12464
- import { existsSync as existsSync17, readFileSync as readFileSync22, realpathSync as realpathSync5, statSync as statSync10 } from "fs";
12539
+ import { existsSync as existsSync18, readFileSync as readFileSync22, realpathSync as realpathSync5, statSync as statSync10 } from "fs";
12465
12540
  import { dirname as dirname16, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve12 } from "path";
12466
12541
  var denied = () => ({
12467
12542
  decision: "deny",
@@ -12478,7 +12553,7 @@ function guardPath(root, command3) {
12478
12553
  if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
12479
12554
  throw new Error("Hook guard must stay under .agents/guards/");
12480
12555
  }
12481
- if (!existsSync17(absolute2) || !statSync10(absolute2).isFile()) {
12556
+ if (!existsSync18(absolute2) || !statSync10(absolute2).isFile()) {
12482
12557
  throw new Error("Hook guard script must exist");
12483
12558
  }
12484
12559
  const actual = relative10(base, realpathSync5(absolute2));
@@ -12673,8 +12748,8 @@ function object4(value) {
12673
12748
  return value;
12674
12749
  }
12675
12750
  function groups(root, file2, key) {
12676
- const absolute2 = join22(root, file2);
12677
- if (!existsSync18(absolute2)) {
12751
+ const absolute2 = join23(root, file2);
12752
+ if (!existsSync19(absolute2)) {
12678
12753
  return [];
12679
12754
  }
12680
12755
  let value;
@@ -12703,15 +12778,15 @@ function adapterSource() {
12703
12778
  const sourceBuild = fileURLToPath2(
12704
12779
  new URL("../../dist/project/hook-adapter.js", import.meta.url)
12705
12780
  );
12706
- const file2 = existsSync18(published) ? published : sourceBuild;
12707
- if (!existsSync18(file2)) {
12781
+ const file2 = existsSync19(published) ? published : sourceBuild;
12782
+ if (!existsSync19(file2)) {
12708
12783
  throw new ProjectError("Build pmcp before projecting shell hook adapters");
12709
12784
  }
12710
12785
  return readFileSync23(file2, "utf8");
12711
12786
  }
12712
12787
  function hookOutputs(spec) {
12713
- const lockFile = join22(spec.root, "pmcp.lock");
12714
- const lock = existsSync18(lockFile) ? JSON.parse(readFileSync23(lockFile, "utf8")) : {};
12788
+ const lockFile = join23(spec.root, "pmcp.lock");
12789
+ const lock = existsSync19(lockFile) ? JSON.parse(readFileSync23(lockFile, "utf8")) : {};
12715
12790
  const previous = new Map(
12716
12791
  (lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
12717
12792
  );
@@ -12826,33 +12901,44 @@ ${lines2.join("\n")}
12826
12901
  }
12827
12902
 
12828
12903
  // src/project/plan.ts
12829
- import { existsSync as existsSync19, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
12830
- import { join as join23 } from "path";
12904
+ import { existsSync as existsSync20, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
12905
+ import { join as join24 } from "path";
12906
+
12907
+ // src/project/adapters/opencode-skills.ts
12908
+ function opencodeSkillOutputs(spec, names) {
12909
+ if (!spec.tools.has("opencode")) return [];
12910
+ return names.filter((name) => spec.skills[name]?.invocation === "user").map(
12911
+ (name) => jsonOutput(OPENCODE_CONFIG, ["permission", "skill", name], "deny")
12912
+ );
12913
+ }
12914
+
12915
+ // src/project/plan.ts
12831
12916
  var AGENT_FIELDS = {
12832
12917
  claude: ["name", "description", "tools", "model", "effort"],
12833
12918
  gemini: ["name", "description"]
12834
12919
  };
12835
12920
  var SETTINGS = ".claude/settings.json";
12836
12921
  function listDirs(dir) {
12837
- if (!existsSync19(dir)) return [];
12922
+ if (!existsSync20(dir)) return [];
12838
12923
  return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
12839
12924
  }
12840
12925
  var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
12841
12926
  function skillNames(root) {
12842
- const dir = join23(root, ".agents/skills");
12927
+ const dir = join24(root, ".agents/skills");
12843
12928
  return listDirs(dir).filter(
12844
- (name) => existsSync19(join23(dir, name, "SKILL.md"))
12929
+ (name) => existsSync20(join24(dir, name, "SKILL.md"))
12845
12930
  );
12846
12931
  }
12847
12932
  function agentNames(root) {
12848
- const dir = join23(root, ".agents/agents");
12933
+ const dir = join24(root, ".agents/agents");
12849
12934
  return listDirs(dir).filter(
12850
- (name) => existsSync19(join23(dir, name, "agent.md"))
12935
+ (name) => existsSync20(join24(dir, name, "agent.md"))
12851
12936
  );
12852
12937
  }
12853
12938
  function skillOutputs(spec) {
12854
- const outputs = [];
12855
- for (const name of skillNames(spec.root)) {
12939
+ const names = skillNames(spec.root);
12940
+ const outputs = opencodeSkillOutputs(spec, names);
12941
+ for (const name of names) {
12856
12942
  const target2 = `../../.agents/skills/${name}`;
12857
12943
  if (spec.tools.has("claude")) {
12858
12944
  outputs.push({ kind: "link", path: `.claude/skills/${name}`, target: target2 });
@@ -12881,13 +12967,13 @@ policy:
12881
12967
  }
12882
12968
  function ruleOutputs(spec) {
12883
12969
  if (!spec.tools.has("claude")) return [];
12884
- const dir = join23(spec.root, ".agents/rules");
12885
- if (!existsSync19(dir)) return [];
12970
+ const dir = join24(spec.root, ".agents/rules");
12971
+ if (!existsSync20(dir)) return [];
12886
12972
  const outputs = [];
12887
12973
  for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
12888
12974
  const rel = `.agents/rules/${file2}`;
12889
12975
  const { meta, body } = splitFrontmatter(
12890
- readFileSync24(join23(spec.root, rel), "utf8")
12976
+ readFileSync24(join24(spec.root, rel), "utf8")
12891
12977
  );
12892
12978
  const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
12893
12979
  const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
@@ -12906,7 +12992,7 @@ function agentOutputs(spec) {
12906
12992
  for (const name of agentNames(spec.root)) {
12907
12993
  const rel = `.agents/agents/${name}/agent.md`;
12908
12994
  const { meta, body } = splitFrontmatter(
12909
- readFileSync24(join23(spec.root, rel), "utf8")
12995
+ readFileSync24(join24(spec.root, rel), "utf8")
12910
12996
  );
12911
12997
  const overrides = spec.agents[name] ?? {};
12912
12998
  const note = `<!-- ${MARK} from ${rel} -->
@@ -13046,8 +13132,8 @@ var PREDECESSORS = [
13046
13132
  ];
13047
13133
  var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
13048
13134
  function readLock(root) {
13049
- const path = join24(root, LOCK_FILE);
13050
- if (!existsSync20(path)) return [];
13135
+ const path = join25(root, LOCK_FILE);
13136
+ if (!existsSync21(path)) return [];
13051
13137
  const lock = JSON.parse(readFileSync25(path, "utf8"));
13052
13138
  return lock.outputs ?? [];
13053
13139
  }
@@ -13058,9 +13144,9 @@ function linkOf(abs) {
13058
13144
  return null;
13059
13145
  }
13060
13146
  }
13061
- var exists = (abs) => existsSync20(abs) || linkOf(abs) !== null;
13147
+ var exists = (abs) => existsSync21(abs) || linkOf(abs) !== null;
13062
13148
  function readJson2(abs) {
13063
- if (!existsSync20(abs)) return {};
13149
+ if (!existsSync21(abs)) return {};
13064
13150
  let value;
13065
13151
  try {
13066
13152
  value = JSON.parse(readFileSync25(abs, "utf8"));
@@ -13115,32 +13201,44 @@ function withoutBlock(text3) {
13115
13201
  return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
13116
13202
  }
13117
13203
  function present(root, output) {
13118
- const abs = join24(root, "file" in output ? output.file : output.path);
13204
+ const abs = join25(root, "file" in output ? output.file : output.path);
13119
13205
  switch (output.kind) {
13120
13206
  case "link":
13121
13207
  return linkOf(abs) === output.target;
13122
13208
  case "file":
13123
- return existsSync20(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
13209
+ return existsSync21(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
13124
13210
  case "json":
13125
13211
  if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
13126
13212
  return getKey(readJson2(abs), output.key) === void 0;
13127
13213
  }
13128
13214
  return same(getKey(readJson2(abs), output.key), output.value);
13129
13215
  case "block":
13130
- return existsSync20(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
13216
+ return existsSync21(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
13131
13217
  }
13132
13218
  }
13133
13219
  function replaceable(root, output, owned) {
13134
13220
  if (output.kind === "json") {
13135
- if (output.key[0] !== "mcpServers" || owned.has(output.path)) {
13221
+ if (output.file === "opencode.json") {
13222
+ let parent = readJson2(join25(root, output.file));
13223
+ for (const part of output.key.slice(0, -1)) {
13224
+ if (parent === void 0) break;
13225
+ if (parent === null || typeof parent !== "object" || Array.isArray(parent))
13226
+ return false;
13227
+ parent = parent[part];
13228
+ }
13229
+ if (parent !== void 0 && (parent === null || typeof parent !== "object" || Array.isArray(parent)))
13230
+ return false;
13231
+ }
13232
+ const guarded2 = output.key[0] === "mcpServers" || output.file === "opencode.json";
13233
+ if (!guarded2 || owned.has(output.path)) {
13136
13234
  return true;
13137
13235
  }
13138
- const existing = getKey(readJson2(join24(root, output.file)), output.key);
13236
+ const existing = getKey(readJson2(join25(root, output.file)), output.key);
13139
13237
  return existing === void 0 || same(existing, output.value);
13140
13238
  }
13141
13239
  if (output.kind === "block") {
13142
- const absolute2 = join24(root, output.file);
13143
- const text4 = existsSync20(absolute2) ? readFileSync25(absolute2, "utf8") : "";
13240
+ const absolute2 = join25(root, output.file);
13241
+ const text4 = existsSync21(absolute2) ? readFileSync25(absolute2, "utf8") : "";
13144
13242
  const outside = parse5(withoutBlock(text4));
13145
13243
  const proposed = parse5(output.content);
13146
13244
  const existing = objectKeys(outside["mcp_servers"]);
@@ -13149,7 +13247,7 @@ function replaceable(root, output, owned) {
13149
13247
  );
13150
13248
  }
13151
13249
  if (owned.has(output.path)) return true;
13152
- const abs = join24(root, output.path);
13250
+ const abs = join25(root, output.path);
13153
13251
  if (!exists(abs)) return true;
13154
13252
  const target2 = linkOf(abs);
13155
13253
  if (target2 !== null) {
@@ -13170,15 +13268,15 @@ function undeclared(spec, outputs) {
13170
13268
  const declared = new Set(outputs.map((o) => o.path));
13171
13269
  const found = [];
13172
13270
  for (const dir of censusDirs(spec)) {
13173
- const abs = join24(spec.root, dir);
13174
- if (!existsSync20(abs)) continue;
13271
+ const abs = join25(spec.root, dir);
13272
+ if (!existsSync21(abs)) continue;
13175
13273
  for (const entry2 of readdirSync9(abs)) {
13176
13274
  const path = `${dir}/${entry2}`;
13177
13275
  if (!declared.has(path)) found.push(path);
13178
13276
  }
13179
13277
  }
13180
- const rules = join24(spec.root, RULES_DIR);
13181
- if (spec.tools.has("grok") && existsSync20(rules)) {
13278
+ const rules = join25(spec.root, RULES_DIR);
13279
+ if (spec.tools.has("grok") && existsSync21(rules)) {
13182
13280
  for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
13183
13281
  if (entry2.isDirectory()) continue;
13184
13282
  found.push(`${RULES_DIR}/${entry2.name}`);
@@ -13205,8 +13303,8 @@ function deadGlobs(root, outputs) {
13205
13303
  if (rule.kind === "file") {
13206
13304
  text3 = rule.content;
13207
13305
  } else if (rule.kind === "link") {
13208
- const abs = join24(root, posix2.join(posix2.dirname(rule.path), rule.target));
13209
- if (!existsSync20(abs)) continue;
13306
+ const abs = join25(root, posix2.join(posix2.dirname(rule.path), rule.target));
13307
+ if (!existsSync21(abs)) continue;
13210
13308
  text3 = readFileSync25(abs, "utf8");
13211
13309
  } else {
13212
13310
  continue;
@@ -13257,7 +13355,7 @@ function project(spec, options2) {
13257
13355
  }
13258
13356
  }
13259
13357
  for (const path of jsonPaths) {
13260
- readJson2(join24(spec.root, path));
13358
+ readJson2(join25(spec.root, path));
13261
13359
  }
13262
13360
  if (options2.check) {
13263
13361
  const problems = [
@@ -13278,6 +13376,7 @@ function project(spec, options2) {
13278
13376
  return { code: 3, problems: refused, outputs: outputs.length, pruned: 0 };
13279
13377
  }
13280
13378
  const mcpPaths = [
13379
+ "opencode.json",
13281
13380
  ".mcp.json",
13282
13381
  ".gemini/settings.json",
13283
13382
  ".agents/mcp_config.json",
@@ -13319,7 +13418,7 @@ function project(spec, options2) {
13319
13418
  for (const output of outputs) {
13320
13419
  if (output.kind !== "link" && output.kind !== "file") continue;
13321
13420
  if (present(spec.root, output)) continue;
13322
- const abs = join24(spec.root, output.path);
13421
+ const abs = join25(spec.root, output.path);
13323
13422
  mkdirSync7(dirname18(abs), { recursive: true });
13324
13423
  if (output.kind === "link") {
13325
13424
  replaceLink(abs, output.target);
@@ -13347,12 +13446,12 @@ function project(spec, options2) {
13347
13446
  group.drops.push(keyed.key);
13348
13447
  jsonFiles.set(keyed.file, group);
13349
13448
  } else {
13350
- rmSync5(join24(spec.root, entry2.path), { force: true, recursive: true });
13449
+ rmSync5(join25(spec.root, entry2.path), { force: true, recursive: true });
13351
13450
  }
13352
13451
  pruned += 1;
13353
13452
  }
13354
13453
  for (const [file2, { sets, drops }] of jsonFiles) {
13355
- const abs = join24(spec.root, file2);
13454
+ const abs = join25(spec.root, file2);
13356
13455
  const before = readJson2(abs);
13357
13456
  const after = structuredClone(before);
13358
13457
  for (const key of drops) deleteKey(after, key);
@@ -13367,14 +13466,14 @@ function project(spec, options2) {
13367
13466
  }
13368
13467
  }
13369
13468
  if (same(before, after)) continue;
13370
- if (!existsSync20(abs) && Object.keys(after).length === 0) continue;
13469
+ if (!existsSync21(abs) && Object.keys(after).length === 0) continue;
13371
13470
  mkdirSync7(dirname18(abs), { recursive: true });
13372
13471
  replaceFile(abs, `${JSON.stringify(after, null, 2)}
13373
13472
  `);
13374
13473
  }
13375
13474
  for (const [file2, content] of blockFiles) {
13376
- const abs = join24(spec.root, file2);
13377
- const before = existsSync20(abs) ? readFileSync25(abs, "utf8") : "";
13475
+ const abs = join25(spec.root, file2);
13476
+ const before = existsSync21(abs) ? readFileSync25(abs, "utf8") : "";
13378
13477
  let after = withoutBlock(before);
13379
13478
  if (content !== null) {
13380
13479
  const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
@@ -13393,15 +13492,15 @@ function project(spec, options2) {
13393
13492
  )
13394
13493
  );
13395
13494
  for (const [name, target2] of legacyLinks(spec.root)) {
13396
- const abs = join24(spec.root, RULES_DIR, `${name}.md`);
13397
- const broken = !existsSync20(abs);
13495
+ const abs = join25(spec.root, RULES_DIR, `${name}.md`);
13496
+ const broken = !existsSync21(abs);
13398
13497
  if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
13399
13498
  rmSync5(abs, { force: true });
13400
13499
  pruned += 1;
13401
13500
  }
13402
13501
  }
13403
13502
  replaceFile(
13404
- join24(spec.root, LOCK_FILE),
13503
+ join25(spec.root, LOCK_FILE),
13405
13504
  `${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
13406
13505
  `
13407
13506
  );
@@ -13498,7 +13597,8 @@ var TOOLS = [
13498
13597
  "codex",
13499
13598
  "gemini",
13500
13599
  "grok",
13501
- "antigravity"
13600
+ "antigravity",
13601
+ "opencode"
13502
13602
  ];
13503
13603
  var NAME = /^[a-z0-9][a-z0-9-]*$/u;
13504
13604
  function table(path, where, value) {
@@ -13654,7 +13754,8 @@ function nativeOptions(path, where, value) {
13654
13754
  codex: ["startup_timeout_sec", "tool_timeout_sec"],
13655
13755
  gemini: ["timeout"],
13656
13756
  grok: ["startup_timeout_sec", "tool_timeout_sec"],
13657
- antigravity: []
13757
+ antigravity: [],
13758
+ opencode: ["timeout"]
13658
13759
  };
13659
13760
  const result = {};
13660
13761
  for (const [tool, raw] of Object.entries(
@@ -13848,6 +13949,11 @@ function sourceServers(source2, root) {
13848
13949
  );
13849
13950
  }
13850
13951
  const path = resolve13(root, source2.slice(split + 1));
13952
+ if (tool === "opencode") {
13953
+ throw new ProjectError(
13954
+ "OpenCode MCP import is not qualified; define the server in pmcp.toml"
13955
+ );
13956
+ }
13851
13957
  if (statSync11(path).size > 2 * 1024 * 1024) {
13852
13958
  throw new ProjectError("MCP source exceeds the 2 MiB limit");
13853
13959
  }
@@ -14212,9 +14318,68 @@ async function execMcp(spec, alias, env) {
14212
14318
 
14213
14319
  // src/project/doctor.ts
14214
14320
  import { spawnSync as spawnSync3 } from "child_process";
14215
- import { existsSync as existsSync21, readFileSync as readFileSync28 } from "fs";
14216
- import { join as join25 } from "path";
14321
+ import { existsSync as existsSync22, readFileSync as readFileSync28 } from "fs";
14322
+ import { join as join27 } from "path";
14217
14323
  import { parse as parse8 } from "smol-toml";
14324
+
14325
+ // src/project/adapters/opencode-doctor.ts
14326
+ import { realpathSync as realpathSync6 } from "fs";
14327
+ import { join as join26 } from "path";
14328
+ function connected(out, alias) {
14329
+ const ansi = new RegExp(
14330
+ `${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`,
14331
+ "gu"
14332
+ );
14333
+ const pattern = new RegExp(
14334
+ `^[^a-zA-Z0-9]*${alias}\\s+connected(?:\\s|$)`,
14335
+ "u"
14336
+ );
14337
+ return out.replace(ansi, "").split("\n").some((line) => pattern.test(line.trim()));
14338
+ }
14339
+ function projectSkill(out, root, name) {
14340
+ try {
14341
+ const entries = JSON.parse(out);
14342
+ if (!Array.isArray(entries)) return false;
14343
+ const expected = realpathSync6(
14344
+ join26(root, ".agents/skills", name, "SKILL.md")
14345
+ );
14346
+ return entries.some(
14347
+ (entry2) => entry2?.name === name && typeof entry2.location === "string" && realpathSync6(entry2.location) === expected
14348
+ );
14349
+ } catch {
14350
+ return false;
14351
+ }
14352
+ }
14353
+ function opencodeDoctor(spec, options2, listed2) {
14354
+ const findings = [];
14355
+ const names = skillNames(spec.root);
14356
+ if (names.length > 0) {
14357
+ findings.push(
14358
+ listed2(
14359
+ "opencode",
14360
+ "project skills discovered",
14361
+ options2.run("opencode", ["debug", "skill"], spec.root, options2.env),
14362
+ names,
14363
+ (out, name) => projectSkill(out, spec.root, name)
14364
+ )
14365
+ );
14366
+ }
14367
+ const aliases = serversForTool(spec, "opencode").map(([alias]) => alias);
14368
+ if (aliases.length > 0) {
14369
+ findings.push(
14370
+ listed2(
14371
+ "opencode",
14372
+ "mcp servers connected",
14373
+ options2.run("opencode", ["mcp", "list"], spec.root, options2.env),
14374
+ aliases,
14375
+ connected
14376
+ )
14377
+ );
14378
+ }
14379
+ return findings;
14380
+ }
14381
+
14382
+ // src/project/doctor.ts
14218
14383
  var spawnRunner = (command3, args, cwd, env) => {
14219
14384
  const result = spawnSync3(command3, [...args], {
14220
14385
  cwd,
@@ -14290,9 +14455,9 @@ function claude(spec, o) {
14290
14455
  }
14291
14456
  function codex(spec, o) {
14292
14457
  const findings = [];
14293
- const config = join25(o.home, ".codex/config.toml");
14458
+ const config = join27(o.home, ".codex/config.toml");
14294
14459
  let trusted = false;
14295
- if (existsSync21(config)) {
14460
+ if (existsSync22(config)) {
14296
14461
  const projects = parse8(readFileSync28(config, "utf8")).projects ?? {};
14297
14462
  const root = spec.root.replace(/\/$/u, "");
14298
14463
  trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
@@ -14420,7 +14585,7 @@ function grok(spec, o) {
14420
14585
  function antigravity(spec, options2) {
14421
14586
  const foreign = agentNames(spec.root).filter((name) => {
14422
14587
  const text3 = readFileSync28(
14423
- join25(spec.root, ".agents/agents", name, "agent.md"),
14588
+ join27(spec.root, ".agents/agents", name, "agent.md"),
14424
14589
  "utf8"
14425
14590
  );
14426
14591
  return splitFrontmatter(text3).meta["tools"] !== void 0;
@@ -14440,7 +14605,7 @@ function antigravity(spec, options2) {
14440
14605
  if (serversForTool(spec, "antigravity").length > 0) {
14441
14606
  const result = options2.run(
14442
14607
  "agy",
14443
- ["plugin", "validate", join25(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14608
+ ["plugin", "validate", join27(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14444
14609
  spec.root
14445
14610
  );
14446
14611
  findings.push(
@@ -14464,6 +14629,8 @@ function doctor(spec, options2) {
14464
14629
  if (wanted("codex")) findings.push(...codex(spec, options2));
14465
14630
  if (wanted("gemini")) findings.push(...gemini(spec, options2));
14466
14631
  if (wanted("grok")) findings.push(...grok(spec, options2));
14632
+ if (wanted("opencode"))
14633
+ findings.push(...opencodeDoctor(spec, options2, listed));
14467
14634
  if (wanted("antigravity")) {
14468
14635
  findings.push(...antigravity(spec, options2));
14469
14636
  }
@@ -14471,8 +14638,8 @@ function doctor(spec, options2) {
14471
14638
  }
14472
14639
 
14473
14640
  // src/project/memory.ts
14474
- import { existsSync as existsSync22, readFileSync as readFileSync29 } from "fs";
14475
- import { isAbsolute as isAbsolute7, join as join26, resolve as resolve15 } from "path";
14641
+ import { existsSync as existsSync23, readFileSync as readFileSync29 } from "fs";
14642
+ import { isAbsolute as isAbsolute7, join as join28, resolve as resolve15 } from "path";
14476
14643
  import { parse as parse9 } from "smol-toml";
14477
14644
  function object7(value) {
14478
14645
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
@@ -14481,7 +14648,7 @@ function object7(value) {
14481
14648
  return value;
14482
14649
  }
14483
14650
  function settings(file2, toml = false) {
14484
- if (!existsSync22(file2)) {
14651
+ if (!existsSync23(file2)) {
14485
14652
  return {};
14486
14653
  }
14487
14654
  const text3 = readFileSync29(file2, "utf8");
@@ -14497,11 +14664,17 @@ function configuredState(value) {
14497
14664
  return "unknown";
14498
14665
  }
14499
14666
  function located(metadata2, path, pathScope) {
14500
- return { ...metadata2, path, pathScope, exists: existsSync22(path) };
14667
+ return { ...metadata2, path, pathScope, exists: existsSync23(path) };
14501
14668
  }
14502
14669
  function metadata(tool, spec, options2) {
14503
14670
  const env = options2.env ?? {};
14504
- const directory = (name, fallback) => resolve15(spec.root, env[name] ?? join26(options2.home, fallback));
14671
+ const directory = (name, fallback) => resolve15(spec.root, env[name] ?? join28(options2.home, fallback));
14672
+ if (tool === "opencode") {
14673
+ return {
14674
+ state: "unsupported",
14675
+ source: "OpenCode native memory metadata is not qualified"
14676
+ };
14677
+ }
14505
14678
  if (tool === "antigravity") {
14506
14679
  return {
14507
14680
  state: "unsupported",
@@ -14525,7 +14698,7 @@ function metadata(tool, spec, options2) {
14525
14698
  state,
14526
14699
  source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
14527
14700
  },
14528
- join26(home2, "memories"),
14701
+ join28(home2, "memories"),
14529
14702
  "store"
14530
14703
  );
14531
14704
  }
@@ -14538,12 +14711,12 @@ function metadata(tool, spec, options2) {
14538
14711
  }
14539
14712
  if (tool === "claude") {
14540
14713
  const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
14541
- const user2 = settings(join26(home2, "settings.json"));
14714
+ const user2 = settings(join28(home2, "settings.json"));
14542
14715
  const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
14543
14716
  const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
14544
14717
  const override = user2["autoMemoryDirectory"];
14545
14718
  if (typeof override === "string") {
14546
- const path = override.startsWith("~/") ? join26(options2.home, override.slice(2)) : override;
14719
+ const path = override.startsWith("~/") ? join28(options2.home, override.slice(2)) : override;
14547
14720
  if (!isAbsolute7(path)) {
14548
14721
  return {
14549
14722
  state: "unknown",
@@ -14556,7 +14729,7 @@ function metadata(tool, spec, options2) {
14556
14729
  if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
14557
14730
  return located(
14558
14731
  { state, source: source2 },
14559
- join26(home2, "projects", projectName, "memory"),
14732
+ join28(home2, "projects", projectName, "memory"),
14560
14733
  "store"
14561
14734
  );
14562
14735
  }
@@ -14565,14 +14738,14 @@ function metadata(tool, spec, options2) {
14565
14738
  state,
14566
14739
  source: `${source2}; native project directory encoding is not guessed`
14567
14740
  },
14568
- join26(home2, "projects"),
14741
+ join28(home2, "projects"),
14569
14742
  "base"
14570
14743
  );
14571
14744
  }
14572
14745
  if (tool === "grok") {
14573
- const home2 = join26(options2.home, ".grok");
14574
- const user2 = settings(join26(home2, "config.toml"), true);
14575
- const project2 = settings(join26(spec.root, ".grok/config.toml"), true);
14746
+ const home2 = join28(options2.home, ".grok");
14747
+ const user2 = settings(join28(home2, "config.toml"), true);
14748
+ const project2 = settings(join28(spec.root, ".grok/config.toml"), true);
14576
14749
  const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
14577
14750
  const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
14578
14751
  const overridden = projectMemory["enabled"] !== void 0;
@@ -14582,12 +14755,12 @@ function metadata(tool, spec, options2) {
14582
14755
  state,
14583
14756
  source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
14584
14757
  },
14585
- join26(home2, "memory-v2/workspaces"),
14758
+ join28(home2, "memory-v2/workspaces"),
14586
14759
  "base"
14587
14760
  );
14588
14761
  }
14589
14762
  const home = directory("GEMINI_CLI_HOME", ".gemini");
14590
- const user = settings(join26(home, "settings.json"));
14763
+ const user = settings(join28(home, "settings.json"));
14591
14764
  const context = user["context"] === void 0 ? {} : object7(user["context"]);
14592
14765
  const names = context["fileName"] ?? "GEMINI.md";
14593
14766
  const contextFileNames = typeof names === "string" ? [names] : names;
@@ -14603,7 +14776,7 @@ function metadata(tool, spec, options2) {
14603
14776
  source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
14604
14777
  contextFileNames
14605
14778
  },
14606
- join26(home, "GEMINI.md"),
14779
+ join28(home, "GEMINI.md"),
14607
14780
  "store"
14608
14781
  );
14609
14782
  }
@@ -14636,14 +14809,14 @@ function memoryFindings(spec, options2) {
14636
14809
  // src/project/edit.ts
14637
14810
  import {
14638
14811
  cpSync,
14639
- existsSync as existsSync23,
14812
+ existsSync as existsSync24,
14640
14813
  mkdirSync as mkdirSync8,
14641
14814
  readFileSync as readFileSync30,
14642
14815
  rmSync as rmSync6,
14643
14816
  statSync as statSync13,
14644
14817
  writeFileSync as writeFileSync7
14645
14818
  } from "fs";
14646
- import { basename as basename7, dirname as dirname20, join as join27 } from "path";
14819
+ import { basename as basename7, dirname as dirname20, join as join29 } from "path";
14647
14820
  var CROSS_TOOL = ["name", "description"];
14648
14821
  var tomlValue2 = (value) => JSON.stringify(value);
14649
14822
  function findEntry(entries, wanted) {
@@ -14675,8 +14848,8 @@ function addEntry(root, entry2) {
14675
14848
  );
14676
14849
  }
14677
14850
  if (kind2 === "skill") {
14678
- const dest2 = join27(root, ".agents/skills", name);
14679
- if (existsSync23(dest2))
14851
+ const dest2 = join29(root, ".agents/skills", name);
14852
+ if (existsSync24(dest2))
14680
14853
  throw new ProjectError(`.agents/skills/${name} already exists`);
14681
14854
  const source2 = entry2.root ?? dirname20(entry2.path);
14682
14855
  mkdirSync8(dirname20(dest2), { recursive: true });
@@ -14691,14 +14864,14 @@ function addEntry(root, entry2) {
14691
14864
  description: meta["description"] ?? entry2.description
14692
14865
  });
14693
14866
  writeFileSync7(
14694
- join27(dest2, "SKILL.md"),
14867
+ join29(dest2, "SKILL.md"),
14695
14868
  `${head}
14696
14869
  ${body.replace(/^\n/u, "")}`
14697
14870
  );
14698
14871
  return { kind: kind2, name, path: `.agents/skills/${name}` };
14699
14872
  }
14700
- const dest = join27(root, ".agents/agents", name);
14701
- if (existsSync23(dest))
14873
+ const dest = join29(root, ".agents/agents", name);
14874
+ if (existsSync24(dest))
14702
14875
  throw new ProjectError(`.agents/agents/${name} already exists`);
14703
14876
  const kept = {};
14704
14877
  const moved = {};
@@ -14708,17 +14881,17 @@ ${body.replace(/^\n/u, "")}`
14708
14881
  }
14709
14882
  mkdirSync8(dest, { recursive: true });
14710
14883
  writeFileSync7(
14711
- join27(dest, "agent.md"),
14884
+ join29(dest, "agent.md"),
14712
14885
  `${renderFrontmatter(kept)}
14713
14886
  ${body.replace(/^\n/u, "")}`
14714
14887
  );
14715
14888
  if (Object.keys(moved).length > 0) {
14716
- const config = join27(root, "pmcp.toml");
14889
+ const config = join29(root, "pmcp.toml");
14717
14890
  const lines2 = [
14718
14891
  `[agents.${name}.claude]`,
14719
14892
  ...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
14720
14893
  ];
14721
- const before = existsSync23(config) ? readFileSync30(config, "utf8") : "";
14894
+ const before = existsSync24(config) ? readFileSync30(config, "utf8") : "";
14722
14895
  const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
14723
14896
  writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
14724
14897
  `);
@@ -14741,14 +14914,14 @@ function withoutTables(toml, name) {
14741
14914
  function removeAsset(root, name) {
14742
14915
  const removed = [];
14743
14916
  for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
14744
- if (!existsSync23(join27(root, rel))) continue;
14745
- rmSync6(join27(root, rel), { recursive: true, force: true });
14917
+ if (!existsSync24(join29(root, rel))) continue;
14918
+ rmSync6(join29(root, rel), { recursive: true, force: true });
14746
14919
  removed.push(rel);
14747
14920
  }
14748
14921
  if (removed.length === 0)
14749
14922
  throw new ProjectError(`no skill or agent named ${name} in .agents/`);
14750
- const config = join27(root, "pmcp.toml");
14751
- if (existsSync23(config)) {
14923
+ const config = join29(root, "pmcp.toml");
14924
+ if (existsSync24(config)) {
14752
14925
  const before = readFileSync30(config, "utf8");
14753
14926
  const after = withoutTables(before, name);
14754
14927
  if (after !== before) writeFileSync7(config, after);
@@ -14988,16 +15161,16 @@ var doctorCommand = {
14988
15161
  };
14989
15162
 
14990
15163
  // src/validate.ts
14991
- import { existsSync as existsSync24, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
14992
- import { basename as basename8, join as join28, resolve as resolve16 } from "path";
15164
+ import { existsSync as existsSync25, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
15165
+ import { basename as basename8, join as join30, resolve as resolve16 } from "path";
14993
15166
  function validateSkillDir(dir) {
14994
15167
  const root = resolve16(dir);
14995
- const skillMd = basename8(root) === "SKILL.md" ? root : join28(root, "SKILL.md");
14996
- if (!existsSync24(skillMd) || !statSync14(skillMd).isFile()) {
15168
+ const skillMd = basename8(root) === "SKILL.md" ? root : join30(root, "SKILL.md");
15169
+ if (!existsSync25(skillMd) || !statSync14(skillMd).isFile()) {
14997
15170
  return [{ path: root, reason: "no SKILL.md", level: "error" }];
14998
15171
  }
14999
15172
  const front = frontmatterObject(readFileSync31(skillMd, "utf8"));
15000
- const directory = basename8(skillMd === root ? join28(root, "..") : root);
15173
+ const directory = basename8(skillMd === root ? join30(root, "..") : root);
15001
15174
  return specProblems(front, directory).map((reason) => ({
15002
15175
  path: skillMd,
15003
15176
  reason,
@@ -15682,14 +15855,14 @@ import { resolve as resolve21 } from "path";
15682
15855
 
15683
15856
  // src/marketplace/export.ts
15684
15857
  import {
15685
- existsSync as existsSync37,
15858
+ existsSync as existsSync38,
15686
15859
  lstatSync as lstatSync14,
15687
15860
  mkdirSync as mkdirSync13,
15688
15861
  readFileSync as readFileSync38,
15689
15862
  readdirSync as readdirSync13,
15690
15863
  writeFileSync as writeFileSync14
15691
15864
  } from "fs";
15692
- import { dirname as dirname27, join as join55, resolve as resolve20 } from "path";
15865
+ import { dirname as dirname27, join as join57, resolve as resolve20 } from "path";
15693
15866
  var NATIVE_PLUGIN_RUNTIMES = [
15694
15867
  "claude",
15695
15868
  "codex",
@@ -15714,7 +15887,7 @@ function readResources(directory, prefix, files) {
15714
15887
  for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
15715
15888
  if (entry2.isSymbolicLink())
15716
15889
  throw new Error("Plugin exports cannot contain symlinks");
15717
- const path = join55(directory, entry2.name);
15890
+ const path = join57(directory, entry2.name);
15718
15891
  const relativePath = `${prefix}/${entry2.name}`;
15719
15892
  if (entry2.isDirectory()) {
15720
15893
  if (relativePath.split("/").length > 6)
@@ -15745,7 +15918,7 @@ function pluginExportFiles(root, name, runtime2) {
15745
15918
  agy: []
15746
15919
  };
15747
15920
  for (const filename of manifests[runtime2]) {
15748
- if (existsSync37(join55(plugin.directory, filename)))
15921
+ if (existsSync38(join57(plugin.directory, filename)))
15749
15922
  files.set(
15750
15923
  filename,
15751
15924
  readFileSync38(containedPath(plugin.directory, filename))
@@ -15759,8 +15932,8 @@ function pluginExportFiles(root, name, runtime2) {
15759
15932
  `
15760
15933
  )
15761
15934
  );
15762
- const mcp = join55(plugin.directory, ".mcp.json");
15763
- if (existsSync37(mcp))
15935
+ const mcp = join57(plugin.directory, ".mcp.json");
15936
+ if (existsSync38(mcp))
15764
15937
  files.set(
15765
15938
  "mcp_config.json",
15766
15939
  readFileSync38(containedPath(plugin.directory, mcp))
@@ -15782,7 +15955,7 @@ function exportPublicPlugin(root, name, runtime2, output) {
15782
15955
  throw error;
15783
15956
  }
15784
15957
  for (const [path, bytes] of files) {
15785
- const target2 = join55(destination, path);
15958
+ const target2 = join57(destination, path);
15786
15959
  mkdirSync13(dirname27(target2), { recursive: true });
15787
15960
  writeFileSync14(target2, bytes, { flag: "wx" });
15788
15961
  }
@@ -15934,7 +16107,7 @@ function dispatch(argv, options2 = {}) {
15934
16107
  }
15935
16108
  return run(command3, rest, { ui, env, cwd });
15936
16109
  }
15937
- var VERSION = "0.16.0";
16110
+ var VERSION = "0.17.0";
15938
16111
  async function defaultEntry(options2, context) {
15939
16112
  const terminal = options2.terminal ?? {
15940
16113
  stdin: process.stdin.isTTY === true,