@modootoday/pmcp 0.16.1 → 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
@@ -1946,11 +1946,11 @@ import {
1946
1946
  fsyncSync as fsyncSync2,
1947
1947
  openSync as openSync4,
1948
1948
  readFileSync as readFileSync34,
1949
- realpathSync as realpathSync6,
1949
+ realpathSync as realpathSync7,
1950
1950
  renameSync as renameSync4,
1951
1951
  writeFileSync as writeFileSync9
1952
1952
  } from "fs";
1953
- import { dirname as dirname21, join as join29 } from "path";
1953
+ import { dirname as dirname21, join as join31 } from "path";
1954
1954
  function privateJson(path, value) {
1955
1955
  const temporary = `${path}.${randomUUID5()}.new`;
1956
1956
  const descriptor = openSync4(temporary, "wx", 384);
@@ -1991,10 +1991,10 @@ var init_store2 = __esm({
1991
1991
  path;
1992
1992
  state;
1993
1993
  constructor(path) {
1994
- this.path = realpathSync6(path);
1994
+ this.path = realpathSync7(path);
1995
1995
  this.state = validateState(readPrivateJson2(path));
1996
1996
  const metadata2 = object8(
1997
- readPrivateJson2(join29(dirname21(this.path), "harness.json"))
1997
+ readPrivateJson2(join31(dirname21(this.path), "harness.json"))
1998
1998
  );
1999
1999
  if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
2000
2000
  throw new Error("foreign_group");
@@ -2016,12 +2016,12 @@ var init_store2 = __esm({
2016
2016
  // src/harness/adapters/process/serialized.ts
2017
2017
  import { spawnSync as spawnSync4 } from "child_process";
2018
2018
  import { randomUUID as randomUUID6 } from "crypto";
2019
- import { existsSync as existsSync25, unlinkSync as unlinkSync2 } from "fs";
2020
- 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";
2021
2021
  function serialized(operation) {
2022
2022
  const store = new GroupStore(text(operation.groupFile, "group_file"));
2023
2023
  const directory = dirname22(store.path);
2024
- const operationFile = join30(directory, `operation-${randomUUID6()}.json`);
2024
+ const operationFile = join32(directory, `operation-${randomUUID6()}.json`);
2025
2025
  privateJson(operationFile, operation);
2026
2026
  const worker = artifact("worker");
2027
2027
  try {
@@ -2033,7 +2033,7 @@ function serialized(operation) {
2033
2033
  "--no-fork",
2034
2034
  "--conflict-exit-code",
2035
2035
  "73",
2036
- join30(directory, "group-control.lock"),
2036
+ join32(directory, "group-control.lock"),
2037
2037
  process.execPath,
2038
2038
  worker,
2039
2039
  operationFile
@@ -2049,7 +2049,7 @@ function serialized(operation) {
2049
2049
  if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
2050
2050
  return JSON.parse(result.stdout);
2051
2051
  } finally {
2052
- if (existsSync25(operationFile)) unlinkSync2(operationFile);
2052
+ if (existsSync26(operationFile)) unlinkSync2(operationFile);
2053
2053
  }
2054
2054
  }
2055
2055
  var init_serialized = __esm({
@@ -2368,14 +2368,14 @@ import {
2368
2368
  lstatSync as lstatSync6,
2369
2369
  mkdirSync as mkdirSync9,
2370
2370
  readdirSync as readdirSync10,
2371
- realpathSync as realpathSync7,
2371
+ realpathSync as realpathSync8,
2372
2372
  writeFileSync as writeFileSync10
2373
2373
  } from "fs";
2374
2374
  import { homedir as homedir7 } from "os";
2375
- import { join as join31 } from "path";
2375
+ import { join as join33 } from "path";
2376
2376
  function stateDirectory() {
2377
- return join31(
2378
- process.env.XDG_STATE_HOME ?? join31(homedir7(), ".local/state"),
2377
+ return join33(
2378
+ process.env.XDG_STATE_HOME ?? join33(homedir7(), ".local/state"),
2379
2379
  "pmcp/harness"
2380
2380
  );
2381
2381
  }
@@ -2405,12 +2405,12 @@ var init_records = __esm({
2405
2405
  const stat = lstatSync6(directory);
2406
2406
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2407
2407
  throw new Error("unsafe_group_directory");
2408
- this.directory = realpathSync7(directory);
2409
- this.socket = join31(this.directory, "s");
2408
+ this.directory = realpathSync8(directory);
2409
+ this.socket = join33(this.directory, "s");
2410
2410
  if (Buffer.byteLength(this.socket) > 100)
2411
2411
  throw new Error("socket_path_too_long");
2412
- this.metadataPath = join31(this.directory, "harness.json");
2413
- this.configPath = join31(this.directory, "tmux.conf");
2412
+ this.metadataPath = join33(this.directory, "harness.json");
2413
+ this.configPath = join33(this.directory, "tmux.conf");
2414
2414
  this.owner = text(
2415
2415
  object8(readPrivateJson2(this.metadataPath)).owner,
2416
2416
  "owner"
@@ -2420,17 +2420,17 @@ var init_records = __esm({
2420
2420
  static create() {
2421
2421
  const root = stateDirectory();
2422
2422
  mkdirSync9(root, { recursive: true, mode: 448 });
2423
- const directory = join31(root, randomUUID7().slice(0, 16));
2424
- if (Buffer.byteLength(join31(directory, "s")) > 100)
2423
+ const directory = join33(root, randomUUID7().slice(0, 16));
2424
+ if (Buffer.byteLength(join33(directory, "s")) > 100)
2425
2425
  throw new Error("socket_path_too_long");
2426
2426
  mkdirSync9(directory, { mode: 448 });
2427
- mkdirSync9(join31(directory, "sessions"), { mode: 448 });
2428
- privateJson(join31(directory, "harness.json"), {
2427
+ mkdirSync9(join33(directory, "sessions"), { mode: 448 });
2428
+ privateJson(join33(directory, "harness.json"), {
2429
2429
  owner: randomUUID7(),
2430
2430
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2431
2431
  });
2432
2432
  writeFileSync10(
2433
- join31(directory, "tmux.conf"),
2433
+ join33(directory, "tmux.conf"),
2434
2434
  [
2435
2435
  "set -g remain-on-exit on",
2436
2436
  "set -g history-limit 2000",
@@ -2446,7 +2446,7 @@ var init_records = __esm({
2446
2446
  }
2447
2447
  path(id2) {
2448
2448
  runtimeUnit(id2);
2449
- return join31(this.directory, "sessions", `${id2}.json`);
2449
+ return join33(this.directory, "sessions", `${id2}.json`);
2450
2450
  }
2451
2451
  read(id2) {
2452
2452
  const record = object8(readPrivateJson2(this.path(id2)));
@@ -2468,7 +2468,7 @@ var init_records = __esm({
2468
2468
  return record;
2469
2469
  }
2470
2470
  all() {
2471
- 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)));
2472
2472
  }
2473
2473
  save(record) {
2474
2474
  privateJson(this.path(record.id), record);
@@ -2550,8 +2550,8 @@ var init_launch_identity = __esm({
2550
2550
 
2551
2551
  // src/harness/launch/journal.ts
2552
2552
  import { createHash as createHash16 } from "crypto";
2553
- import { existsSync as existsSync26, readdirSync as readdirSync11 } from "fs";
2554
- import { join as join32 } from "path";
2553
+ import { existsSync as existsSync27, readdirSync as readdirSync11 } from "fs";
2554
+ import { join as join34 } from "path";
2555
2555
  function launchJournals(records2) {
2556
2556
  return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
2557
2557
  }
@@ -2559,7 +2559,7 @@ function pendingLaunches(records2) {
2559
2559
  return launchJournals(records2).filter((journal) => {
2560
2560
  const aborted = journal.abortRecord();
2561
2561
  if (aborted) return aborted.state !== "stopped";
2562
- if (!existsSync26(journal.path("committed"))) return true;
2562
+ if (!existsSync27(journal.path("committed"))) return true;
2563
2563
  journal.assertCommitted();
2564
2564
  return false;
2565
2565
  });
@@ -2586,7 +2586,7 @@ var init_journal = __esm({
2586
2586
  path(kind2) {
2587
2587
  if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
2588
2588
  throw new Error("invalid_launch_artifact");
2589
- return join32(this.records.directory, `${this.id}.${kind2}.json`);
2589
+ return join34(this.records.directory, `${this.id}.${kind2}.json`);
2590
2590
  }
2591
2591
  intent() {
2592
2592
  const value = object8(readPrivateJson2(this.path("intent")));
@@ -2615,13 +2615,13 @@ var init_journal = __esm({
2615
2615
  throw new Error("invalid_docker_bridge_digest");
2616
2616
  if (!bridge && creation.bridgeSha256 !== void 0)
2617
2617
  throw new Error("ungranted_docker_bridge_digest");
2618
- 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;
2619
2619
  const profile = selectedProfile(
2620
2620
  policy,
2621
2621
  creation.privateProfile,
2622
2622
  text(value.runtime, "runtime")
2623
2623
  );
2624
- const expectedState = profile?.directory ?? join32(
2624
+ const expectedState = profile?.directory ?? join34(
2625
2625
  this.records.directory,
2626
2626
  "containers",
2627
2627
  text(value.nonce, "nonce"),
@@ -2667,7 +2667,7 @@ var init_journal = __esm({
2667
2667
  }
2668
2668
  assertRegistered() {
2669
2669
  const intent = this.intent();
2670
- 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;
2671
2671
  if (group.id !== intent.groupId || group.generation !== intent.generation)
2672
2672
  throw new Error("launch_group_generation_mismatch");
2673
2673
  if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
@@ -2683,12 +2683,12 @@ var init_journal = __esm({
2683
2683
  throw new Error("launch_record_mismatch");
2684
2684
  }
2685
2685
  authorize() {
2686
- if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2686
+ if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
2687
2687
  this.assertRegistered();
2688
2688
  privateJson(this.path("grant"), this.grant());
2689
2689
  }
2690
2690
  assertGrant() {
2691
- if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2691
+ if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
2692
2692
  const grant = object8(readPrivateJson2(this.path("grant")));
2693
2693
  if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
2694
2694
  throw new Error("launch_grant_mismatch");
@@ -2705,7 +2705,7 @@ var init_journal = __esm({
2705
2705
  throw new Error("launch_commit_mismatch");
2706
2706
  }
2707
2707
  abortRecord() {
2708
- if (!existsSync26(this.path("aborted"))) return;
2708
+ if (!existsSync27(this.path("aborted"))) return;
2709
2709
  const value = object8(readPrivateJson2(this.path("aborted")));
2710
2710
  if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
2711
2711
  throw new Error("foreign_launch_abort");
@@ -2754,20 +2754,20 @@ import { homedir as homedir8 } from "os";
2754
2754
  import {
2755
2755
  closeSync as closeSync5,
2756
2756
  constants as constants4,
2757
- existsSync as existsSync27,
2757
+ existsSync as existsSync28,
2758
2758
  fstatSync as fstatSync3,
2759
2759
  lstatSync as lstatSync7,
2760
2760
  openSync as openSync5,
2761
- realpathSync as realpathSync8,
2761
+ realpathSync as realpathSync9,
2762
2762
  statfsSync
2763
2763
  } from "fs";
2764
- import { join as join33, relative as relative11 } from "path";
2764
+ import { join as join35, relative as relative11 } from "path";
2765
2765
  function overlap(left, right) {
2766
2766
  const scope = relative11(left, right);
2767
2767
  return scope === "" || scope !== ".." && !scope.startsWith("../");
2768
2768
  }
2769
2769
  function profileIdentity(directory, projectRoot) {
2770
- 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)
2771
2771
  throw new Error("private_profile_scope_not_admitted");
2772
2772
  const stat = lstatSync7(directory);
2773
2773
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
@@ -2775,7 +2775,7 @@ function profileIdentity(directory, projectRoot) {
2775
2775
  if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
2776
2776
  throw new Error("remote_private_profile_not_admitted");
2777
2777
  const lock = openSync5(
2778
- join33(directory, privateProfileLock),
2778
+ join35(directory, privateProfileLock),
2779
2779
  constants4.O_RDONLY | constants4.O_NOFOLLOW
2780
2780
  );
2781
2781
  try {
@@ -2788,14 +2788,14 @@ function profileIdentity(directory, projectRoot) {
2788
2788
  }
2789
2789
  }
2790
2790
  function captureProfile(grant, projectRoot) {
2791
- const directory = realpathSync8(grant.directory);
2792
- 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()))
2793
2793
  throw new Error("private_profile_scope_not_admitted");
2794
2794
  const stat = lstatSync7(directory);
2795
2795
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2796
2796
  throw new Error("unsafe_private_profile_directory");
2797
- const lock = join33(directory, privateProfileLock);
2798
- if (!existsSync27(lock)) closeSync5(openSync5(lock, "wx", 384));
2797
+ const lock = join35(directory, privateProfileLock);
2798
+ if (!existsSync28(lock)) closeSync5(openSync5(lock, "wx", 384));
2799
2799
  const identity = profileIdentity(directory, projectRoot);
2800
2800
  if (grant.identity && grant.identity !== identity)
2801
2801
  throw new Error("private_profile_identity_changed");
@@ -2813,7 +2813,7 @@ function assertProfileAvailable(grant, projectRoot) {
2813
2813
  "--nonblock",
2814
2814
  "--conflict-exit-code",
2815
2815
  "73",
2816
- join33(grant.directory, privateProfileLock),
2816
+ join35(grant.directory, privateProfileLock),
2817
2817
  "true"
2818
2818
  ]);
2819
2819
  } catch (error) {
@@ -2833,7 +2833,7 @@ var init_private_profile = __esm({
2833
2833
 
2834
2834
  // src/harness/adapters/docker/profile.ts
2835
2835
  import { homedir as homedir9 } from "os";
2836
- import { lstatSync as lstatSync8, realpathSync as realpathSync9 } from "fs";
2836
+ import { lstatSync as lstatSync8, realpathSync as realpathSync10 } from "fs";
2837
2837
  import { relative as relative12 } from "path";
2838
2838
  function assertLocalDocker() {
2839
2839
  if (process.env.DOCKER_HOST)
@@ -2850,12 +2850,12 @@ function assertLocalDocker() {
2850
2850
  throw new Error("remote_docker_context_not_admitted");
2851
2851
  }
2852
2852
  function loadDockerPolicy(path, projectRoot, runtimes) {
2853
- if (projectRoot === "/" || projectRoot === realpathSync9(homedir9()))
2853
+ if (projectRoot === "/" || projectRoot === realpathSync10(homedir9()))
2854
2854
  throw new Error("docker_project_root_too_broad");
2855
2855
  const inputPath = text(path, "docker_policy_file");
2856
2856
  if (lstatSync8(inputPath).isSymbolicLink())
2857
2857
  throw new Error("symlink_docker_policy_not_admitted");
2858
- const source2 = realpathSync9(inputPath);
2858
+ const source2 = realpathSync10(inputPath);
2859
2859
  const scope = relative12(projectRoot, source2);
2860
2860
  if (scope === ".." || scope.startsWith("../"))
2861
2861
  throw new Error("docker_policy_outside_project");
@@ -2889,14 +2889,14 @@ var init_profile = __esm({
2889
2889
  });
2890
2890
 
2891
2891
  // src/harness/adapters/docker/mounts.ts
2892
- import { lstatSync as lstatSync9, realpathSync as realpathSync10 } from "fs";
2893
- import { join as join34 } from "path";
2892
+ import { lstatSync as lstatSync9, realpathSync as realpathSync11 } from "fs";
2893
+ import { join as join36 } from "path";
2894
2894
  function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
2895
2895
  const state = text(statePath, "container_state_directory");
2896
2896
  const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
2897
2897
  if (profile) assertProfileIdentity(profile, projectRoot);
2898
- const expected = profile?.directory ?? join34(directory, "containers", nonce, "profile");
2899
- if (state !== expected || realpathSync10(state) !== expected)
2898
+ const expected = profile?.directory ?? join36(directory, "containers", nonce, "profile");
2899
+ if (state !== expected || realpathSync11(state) !== expected)
2900
2900
  throw new Error("foreign_container_state_directory");
2901
2901
  const stat = lstatSync9(state);
2902
2902
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
@@ -2990,8 +2990,8 @@ var init_entrypoint = __esm({
2990
2990
  });
2991
2991
 
2992
2992
  // src/harness/adapters/docker/admission.ts
2993
- import { lstatSync as lstatSync10, realpathSync as realpathSync11 } from "fs";
2994
- 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";
2995
2995
  function admitResource(path, group, role, directory, memoryMb, runtime2) {
2996
2996
  if (group.profile !== "docker" || !group.dockerPolicy)
2997
2997
  throw new Error("container_requires_docker_profile");
@@ -3000,7 +3000,7 @@ function admitResource(path, group, role, directory, memoryMb, runtime2) {
3000
3000
  const inputPath = text(path, "container_file");
3001
3001
  if (lstatSync10(inputPath).isSymbolicLink())
3002
3002
  throw new Error("symlink_container_file_not_admitted");
3003
- const source2 = realpathSync11(inputPath);
3003
+ const source2 = realpathSync12(inputPath);
3004
3004
  const scope = relative13(projectRoot, source2);
3005
3005
  if (scope === ".." || scope.startsWith("../"))
3006
3006
  throw new Error("container_file_outside_project");
@@ -3047,7 +3047,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
3047
3047
  )
3048
3048
  ))
3049
3049
  throw new Error("container_runtime_image_not_granted");
3050
- if (config.WorkingDir !== join35("/workspace", relative13(projectRoot, cwd)))
3050
+ if (config.WorkingDir !== join37("/workspace", relative13(projectRoot, cwd)))
3051
3051
  throw new Error("container_cwd_not_granted");
3052
3052
  assertContainerMounts(
3053
3053
  actual,
@@ -3191,7 +3191,7 @@ import {
3191
3191
  readFileSync as readFileSync36,
3192
3192
  writeFileSync as writeFileSync11
3193
3193
  } from "fs";
3194
- import { join as join36 } from "path";
3194
+ import { join as join38 } from "path";
3195
3195
  function source() {
3196
3196
  const path = artifact("egress-bridge");
3197
3197
  if (!path.endsWith(".js"))
@@ -3210,7 +3210,7 @@ function prepareBridge(creation) {
3210
3210
  if (digest(content) !== creation.bridgeSha256)
3211
3211
  throw new Error("docker_bridge_source_changed");
3212
3212
  const descriptor = openSync6(
3213
- join36(creation.state, bridgeName(creation.nonce)),
3213
+ join38(creation.state, bridgeName(creation.nonce)),
3214
3214
  "wx",
3215
3215
  384
3216
3216
  );
@@ -3224,7 +3224,7 @@ function prepareBridge(creation) {
3224
3224
  function assertBridge(creation) {
3225
3225
  if (!creation.egress?.bridgeExecutable) return;
3226
3226
  const descriptor = openSync6(
3227
- join36(creation.state, bridgeName(creation.nonce)),
3227
+ join38(creation.state, bridgeName(creation.nonce)),
3228
3228
  constants5.O_RDONLY | constants5.O_NOFOLLOW
3229
3229
  );
3230
3230
  try {
@@ -3244,7 +3244,7 @@ var init_bridge_artifact = __esm({
3244
3244
  });
3245
3245
 
3246
3246
  // src/harness/adapters/docker/creation-plan.ts
3247
- import { join as join37, relative as relative14 } from "path";
3247
+ import { join as join39, relative as relative14 } from "path";
3248
3248
  function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
3249
3249
  if (group.profile !== "docker" || !group.dockerPolicy)
3250
3250
  throw new Error("container_requires_docker_profile");
@@ -3261,7 +3261,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3261
3261
  throw new Error("invalid_container_image_environment");
3262
3262
  const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
3263
3263
  if (profile) assertProfileAvailable(profile, group.projectRoot);
3264
- const state = profile?.directory ?? join37(directory, "containers", nonce, "profile");
3264
+ const state = profile?.directory ?? join39(directory, "containers", nonce, "profile");
3265
3265
  return {
3266
3266
  name: containerName(group.owner, id2),
3267
3267
  nonce,
@@ -3278,7 +3278,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3278
3278
  preparation.privateProfile
3279
3279
  ),
3280
3280
  Cmd: preparation.args.length ? preparation.args : null,
3281
- WorkingDir: join37("/workspace", relative14(group.projectRoot, cwd)),
3281
+ WorkingDir: join39("/workspace", relative14(group.projectRoot, cwd)),
3282
3282
  User: `${process.getuid?.()}:${process.getgid?.()}`,
3283
3283
  Env: [
3284
3284
  ...environment2.filter(
@@ -3377,12 +3377,12 @@ var init_creation_plan = __esm({
3377
3377
 
3378
3378
  // src/harness/adapters/docker/creation.ts
3379
3379
  import { mkdirSync as mkdirSync10 } from "fs";
3380
- import { join as join38 } from "path";
3380
+ import { join as join40 } from "path";
3381
3381
  async function prepareCreation(journal) {
3382
3382
  const intent = journal.intent();
3383
3383
  const creation = intent.dockerCreation;
3384
3384
  if (!creation) throw new Error("container_creation_missing");
3385
- 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;
3386
3386
  const profile = selectedProfile(
3387
3387
  group.dockerPolicy,
3388
3388
  creation.privateProfile,
@@ -3396,7 +3396,7 @@ async function prepareCreation(journal) {
3396
3396
  creation.name,
3397
3397
  creation.configuration
3398
3398
  );
3399
- 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;
3400
3400
  if (current.id !== intent.groupId || current.generation !== intent.generation)
3401
3401
  throw new Error("launch_group_generation_mismatch");
3402
3402
  const resource = {
@@ -3464,7 +3464,7 @@ function findCreation(journal) {
3464
3464
  assertResourceOwner(resource, actual);
3465
3465
  assertCreationConfiguration(actual, creation);
3466
3466
  if (!intent.resource) {
3467
- 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;
3468
3468
  return {
3469
3469
  ...admitClaim(
3470
3470
  { ...resource, runtime: intent.runtime, state: creation.state },
@@ -3501,7 +3501,7 @@ var init_creation = __esm({
3501
3501
  });
3502
3502
 
3503
3503
  // src/harness/launch/recovery.ts
3504
- import { existsSync as existsSync28 } from "fs";
3504
+ import { existsSync as existsSync29 } from "fs";
3505
3505
  function abortLaunch(connection, journal) {
3506
3506
  const intent = journal.intent();
3507
3507
  if (!controllerExited(intent.controllerIdentity))
@@ -3518,7 +3518,7 @@ function abortLaunch(connection, journal) {
3518
3518
  inputReplayed: false
3519
3519
  };
3520
3520
  }
3521
- if (!existsSync28(journal.path("proof"))) {
3521
+ if (!existsSync29(journal.path("proof"))) {
3522
3522
  const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
3523
3523
  if (paneUnknown || unitActive(intent.unit))
3524
3524
  throw new Error("launch_identity_unknown");
@@ -3545,7 +3545,7 @@ function abortLaunch(connection, journal) {
3545
3545
  assertProof(connection, proof);
3546
3546
  connection.tmux(["kill-pane", "-t", proof.session.paneId]);
3547
3547
  }
3548
- if (existsSync28(journal.records.path(intent.id)))
3548
+ if (existsSync29(journal.records.path(intent.id)))
3549
3549
  journal.records.save({
3550
3550
  ...proof.session,
3551
3551
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3572,9 +3572,9 @@ var init_recovery = __esm({
3572
3572
 
3573
3573
  // src/harness/adapters/tmux/lifecycle.ts
3574
3574
  import { randomUUID as randomUUID8 } from "crypto";
3575
- import { existsSync as existsSync29 } from "fs";
3575
+ import { existsSync as existsSync30 } from "fs";
3576
3576
  import { setTimeout as setTimeout2 } from "timers/promises";
3577
- import { join as join39 } from "path";
3577
+ import { join as join41 } from "path";
3578
3578
  async function start(connection, spec) {
3579
3579
  const records2 = connection.records;
3580
3580
  if (spec.budgetSlice !== budgetSlice(records2.owner))
@@ -3588,7 +3588,7 @@ async function start(connection, spec) {
3588
3588
  const nonce = randomUUID8();
3589
3589
  const journal = new LaunchJournal(records2, id2);
3590
3590
  const dockerCreation = spec.dockerPreparation ? creationPlan(
3591
- new GroupStore(join39(records2.directory, "group.json")).state.group,
3591
+ new GroupStore(join41(records2.directory, "group.json")).state.group,
3592
3592
  records2.directory,
3593
3593
  id2,
3594
3594
  nonce,
@@ -3648,7 +3648,7 @@ async function start(connection, spec) {
3648
3648
  `exec ${argv.map(quote2).join(" ")}`
3649
3649
  ]);
3650
3650
  const deadline = Date.now() + 5e3;
3651
- while (!existsSync29(journal.path("proof"))) {
3651
+ while (!existsSync30(journal.path("proof"))) {
3652
3652
  if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
3653
3653
  await setTimeout2(25);
3654
3654
  }
@@ -3659,7 +3659,7 @@ async function start(connection, spec) {
3659
3659
  }
3660
3660
  function stop(connection, id2) {
3661
3661
  const journal = new LaunchJournal(connection.records, id2);
3662
- if (existsSync29(journal.path("intent"))) {
3662
+ if (existsSync30(journal.path("intent"))) {
3663
3663
  abortLaunch(connection, journal);
3664
3664
  return;
3665
3665
  }
@@ -3686,7 +3686,7 @@ function cleanup(connection) {
3686
3686
  }
3687
3687
  for (const record of records2) {
3688
3688
  if (record.resource) stopResource(record.resource);
3689
- if (record.stoppedAt || !existsSync29(
3689
+ if (record.stoppedAt || !existsSync30(
3690
3690
  new LaunchJournal(connection.records, record.id).path("intent")
3691
3691
  )) {
3692
3692
  stopUnit(record.unit);
@@ -3718,7 +3718,7 @@ var init_lifecycle2 = __esm({
3718
3718
  // src/harness/adapters/tmux/input.ts
3719
3719
  import { randomUUID as randomUUID9 } from "crypto";
3720
3720
  import { closeSync as closeSync7, openSync as openSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
3721
- import { join as join40 } from "path";
3721
+ import { join as join42 } from "path";
3722
3722
  function validateText(text3) {
3723
3723
  if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
3724
3724
  throw new Error("invalid_text");
@@ -3749,7 +3749,7 @@ function write(connection, id2, text3, submit) {
3749
3749
  validateText(text3);
3750
3750
  const session = connection.inspect(id2);
3751
3751
  if (!session.alive) throw new Error("runtime_exited");
3752
- const lock = join40(connection.records.directory, `${id2}.input.lock`);
3752
+ const lock = join42(connection.records.directory, `${id2}.input.lock`);
3753
3753
  const descriptor = openSync7(lock, "wx", 384);
3754
3754
  const buffer = `pmcp-${randomUUID9()}`;
3755
3755
  try {
@@ -3908,10 +3908,10 @@ var init_backend = __esm({
3908
3908
  });
3909
3909
 
3910
3910
  // src/harness/adapters/systemd/budget.ts
3911
- import { join as join41 } from "path";
3911
+ import { join as join43 } from "path";
3912
3912
  async function budgetOperation(directory, operation) {
3913
3913
  const backend = openBackend(directory);
3914
- const path = join41(directory, "runtime-policy.json");
3914
+ const path = join43(directory, "runtime-policy.json");
3915
3915
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
3916
3916
  const slice = budgetSlice(backend.owner);
3917
3917
  if (operation.action === "configure") {
@@ -4012,7 +4012,7 @@ var init_budget = __esm({
4012
4012
 
4013
4013
  // src/harness/adapters/systemd/configure.ts
4014
4014
  import { execFileSync as execFileSync3 } from "child_process";
4015
- import { join as join42 } from "path";
4015
+ import { join as join44 } from "path";
4016
4016
  function configureBudget(context, input) {
4017
4017
  const { store, backend } = context;
4018
4018
  assertOwnedRecords(backend);
@@ -4034,7 +4034,7 @@ function configureBudget(context, input) {
4034
4034
  throw new Error("capacity_below_active_sessions");
4035
4035
  if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
4036
4036
  throw new Error("budget_below_reserved_memory");
4037
- const path = join42(backend.runDirectory, "runtime-policy.json");
4037
+ const path = join44(backend.runDirectory, "runtime-policy.json");
4038
4038
  const policy = object8(readPrivateJson2(path));
4039
4039
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
4040
4040
  const slice = budgetSlice(backend.owner);
@@ -4129,10 +4129,10 @@ var init_admission2 = __esm({
4129
4129
 
4130
4130
  // src/harness/groups/service.ts
4131
4131
  import { randomUUID as randomUUID10 } from "crypto";
4132
- import { realpathSync as realpathSync12, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
4133
- 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";
4134
4134
  async function createGroup(input) {
4135
- const projectRoot = realpathSync12(text(input.projectRoot, "project_root"));
4135
+ const projectRoot = realpathSync13(text(input.projectRoot, "project_root"));
4136
4136
  if (!statSync17(projectRoot).isDirectory())
4137
4137
  throw new Error("invalid_project_root");
4138
4138
  const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
@@ -4163,7 +4163,7 @@ async function createGroup(input) {
4163
4163
  sessions: [],
4164
4164
  ...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
4165
4165
  };
4166
- const groupFile = join43(backend.runDirectory, "group.json");
4166
+ const groupFile = join45(backend.runDirectory, "group.json");
4167
4167
  privateJson(groupFile, { group, leases: [], deliveries: [] });
4168
4168
  try {
4169
4169
  await budgetOperation(backend.runDirectory, {
@@ -4176,7 +4176,7 @@ async function createGroup(input) {
4176
4176
  await budgetOperation(backend.runDirectory, { action: "cleanup" });
4177
4177
  unlinkSync4(groupFile);
4178
4178
  } catch (cleanupError) {
4179
- privateJson(join43(backend.runDirectory, "initialization-failure.json"), {
4179
+ privateJson(join45(backend.runDirectory, "initialization-failure.json"), {
4180
4180
  error: String(error),
4181
4181
  cleanupError: String(cleanupError),
4182
4182
  recoveryRequired: true
@@ -4238,16 +4238,16 @@ var init_service2 = __esm({
4238
4238
  });
4239
4239
 
4240
4240
  // src/harness/groups/catalog.ts
4241
- import { existsSync as existsSync30, readdirSync as readdirSync12 } from "fs";
4242
- import { join as join44 } from "path";
4241
+ import { existsSync as existsSync31, readdirSync as readdirSync12 } from "fs";
4242
+ import { join as join46 } from "path";
4243
4243
  function listGroups() {
4244
4244
  const directory = stateDirectory();
4245
- if (!existsSync30(directory)) return { groups: [] };
4245
+ if (!existsSync31(directory)) return { groups: [] };
4246
4246
  const groups2 = [];
4247
4247
  for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
4248
4248
  if (!entry2.isDirectory()) continue;
4249
- const path = join44(directory, entry2.name, "group.json");
4250
- if (!existsSync30(path)) continue;
4249
+ const path = join46(directory, entry2.name, "group.json");
4250
+ if (!existsSync31(path)) continue;
4251
4251
  try {
4252
4252
  const store = new GroupStore(path);
4253
4253
  groups2.push({ groupFile: store.path, group: store.state.group });
@@ -4444,7 +4444,7 @@ var init_watch = __esm({
4444
4444
 
4445
4445
  // src/harness/control/leases.ts
4446
4446
  import { randomUUID as randomUUID11 } from "crypto";
4447
- import { join as join45 } from "path";
4447
+ import { join as join47 } from "path";
4448
4448
  function resolveLease(context, file2, allowExpired = false) {
4449
4449
  const credential2 = object8(readPrivateJson2(file2));
4450
4450
  const lease = context.store.state.leases.find(
@@ -4488,7 +4488,7 @@ function acquireControl(context, sessionId, controller, mode) {
4488
4488
  );
4489
4489
  store.state.leases.push(lease);
4490
4490
  store.save();
4491
- const leaseFile = join45(backend.runDirectory, `lease-${randomUUID11()}.json`);
4491
+ const leaseFile = join47(backend.runDirectory, `lease-${randomUUID11()}.json`);
4492
4492
  privateJson(leaseFile, { token: lease.token });
4493
4493
  return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
4494
4494
  }
@@ -4635,12 +4635,12 @@ var init_service5 = __esm({
4635
4635
  });
4636
4636
 
4637
4637
  // src/harness/adapters/process/recover-lock.ts
4638
- import { existsSync as existsSync31, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
4639
- 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";
4640
4640
  function recoverInputLock(backend, sessionId) {
4641
4641
  backend.inspect(sessionId);
4642
- const path = join46(backend.runDirectory, `${sessionId}.input.lock`);
4643
- if (!existsSync31(path)) return;
4642
+ const path = join48(backend.runDirectory, `${sessionId}.input.lock`);
4643
+ if (!existsSync32(path)) return;
4644
4644
  const before = lstatSync11(path);
4645
4645
  const record = object8(readPrivateJson2(path));
4646
4646
  if (record.id !== sessionId) throw new Error("foreign_writer_lock");
@@ -4660,7 +4660,7 @@ var init_recover_lock = __esm({
4660
4660
  });
4661
4661
 
4662
4662
  // src/harness/launch/service.ts
4663
- import { existsSync as existsSync32 } from "fs";
4663
+ import { existsSync as existsSync33 } from "fs";
4664
4664
  function assertLaunchAdmission(records2) {
4665
4665
  for (const journal of launchJournals(records2)) {
4666
4666
  if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
@@ -4685,12 +4685,12 @@ function inspectPendingStarts(context) {
4685
4685
  role: journal.intent().role,
4686
4686
  runtime: journal.intent().runtime,
4687
4687
  controllerExited: controllerExited(journal.intent().controllerIdentity),
4688
- proofPresent: existsSync32(journal.path("proof")),
4689
- recordPresent: existsSync32(journal.records.path(journal.id)),
4688
+ proofPresent: existsSync33(journal.path("proof")),
4689
+ recordPresent: existsSync33(journal.records.path(journal.id)),
4690
4690
  registered: context.store.state.group.sessions.some(
4691
4691
  (session) => session.id === journal.id
4692
4692
  ),
4693
- authorizationPresent: existsSync32(journal.path("grant")),
4693
+ authorizationPresent: existsSync33(journal.path("grant")),
4694
4694
  processingConfirmed: false,
4695
4695
  inputReplayed: false
4696
4696
  }));
@@ -4708,7 +4708,7 @@ var init_service6 = __esm({
4708
4708
  });
4709
4709
 
4710
4710
  // src/harness/sessions/service.ts
4711
- import { realpathSync as realpathSync13 } from "fs";
4711
+ import { realpathSync as realpathSync14 } from "fs";
4712
4712
  import { relative as relative15 } from "path";
4713
4713
  async function startSession(context, input) {
4714
4714
  const { store, backend } = context;
@@ -4742,7 +4742,7 @@ async function startSession(context, input) {
4742
4742
  );
4743
4743
  if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
4744
4744
  }
4745
- const cwd = realpathSync13(text(input.cwd ?? group.projectRoot, "cwd"));
4745
+ const cwd = realpathSync14(text(input.cwd ?? group.projectRoot, "cwd"));
4746
4746
  const scope = relative15(group.projectRoot, cwd);
4747
4747
  if (scope === ".." || scope.startsWith("../"))
4748
4748
  throw new Error("cwd_outside_project");
@@ -5205,9 +5205,9 @@ var init_invoke = __esm({
5205
5205
  });
5206
5206
 
5207
5207
  // src/tui/adapters/process/runtime.ts
5208
- import { dirname as dirname24, join as join47 } from "path";
5208
+ import { dirname as dirname24, join as join49 } from "path";
5209
5209
  import { fileURLToPath as fileURLToPath4 } from "url";
5210
- import { existsSync as existsSync33, realpathSync as realpathSync14 } from "fs";
5210
+ import { existsSync as existsSync34, realpathSync as realpathSync15 } from "fs";
5211
5211
  function dependencies() {
5212
5212
  const tmux = locateExecutable("tmux");
5213
5213
  const flock = locateExecutable("flock");
@@ -5241,9 +5241,9 @@ function runtime() {
5241
5241
  throw new Error("tui_dependencies_unavailable");
5242
5242
  const current = fileURLToPath4(import.meta.url);
5243
5243
  const directory = dirname24(current);
5244
- const bundled = join47(directory, "tui-worker.js");
5245
- if (!existsSync33(bundled)) throw new Error("tui_build_required");
5246
- 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 };
5247
5247
  }
5248
5248
  var init_runtime2 = __esm({
5249
5249
  "src/tui/adapters/process/runtime.ts"() {
@@ -5330,32 +5330,32 @@ var init_diagnostics = __esm({
5330
5330
  // src/tui/state/store.ts
5331
5331
  import { randomUUID as randomUUID13 } from "crypto";
5332
5332
  import {
5333
- existsSync as existsSync34,
5333
+ existsSync as existsSync35,
5334
5334
  lstatSync as lstatSync12,
5335
5335
  mkdirSync as mkdirSync11,
5336
- realpathSync as realpathSync15,
5336
+ realpathSync as realpathSync16,
5337
5337
  unlinkSync as unlinkSync6,
5338
5338
  writeFileSync as writeFileSync13
5339
5339
  } from "fs";
5340
5340
  import { homedir as homedir10 } from "os";
5341
- import { dirname as dirname25, join as join48 } from "path";
5341
+ import { dirname as dirname25, join as join50 } from "path";
5342
5342
  function createDirectory() {
5343
- const root = join48(
5344
- process.env.XDG_STATE_HOME ?? join48(homedir10(), ".local/state"),
5343
+ const root = join50(
5344
+ process.env.XDG_STATE_HOME ?? join50(homedir10(), ".local/state"),
5345
5345
  "pmcp/tui"
5346
5346
  );
5347
5347
  mkdirSync11(root, { recursive: true, mode: 448 });
5348
- const directory = join48(realpathSync15(root), randomUUID13().slice(0, 16));
5349
- 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)
5350
5350
  throw new Error("socket_path_too_long");
5351
5351
  mkdirSync11(directory, { mode: 448 });
5352
5352
  const owner = randomUUID13();
5353
- privateJson(join48(directory, "owner.json"), {
5353
+ privateJson(join50(directory, "owner.json"), {
5354
5354
  owner,
5355
5355
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5356
5356
  });
5357
5357
  writeFileSync13(
5358
- join48(directory, "tmux.conf"),
5358
+ join50(directory, "tmux.conf"),
5359
5359
  [
5360
5360
  "set -g remain-on-exit on",
5361
5361
  "set -g history-limit 2000",
@@ -5370,13 +5370,13 @@ function createDirectory() {
5370
5370
  return { directory, owner };
5371
5371
  }
5372
5372
  function readView(path) {
5373
- const file2 = realpathSync15(path);
5373
+ const file2 = realpathSync16(path);
5374
5374
  const directory = dirname25(file2);
5375
5375
  const stat = lstatSync12(directory);
5376
5376
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
5377
5377
  throw new Error("unsafe_view_directory");
5378
5378
  const value = object8(readPrivateJson2(file2));
5379
- const metadata2 = object8(readPrivateJson2(join48(directory, "owner.json")));
5379
+ const metadata2 = object8(readPrivateJson2(join50(directory, "owner.json")));
5380
5380
  if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
5381
5381
  throw new Error("foreign_view_owner");
5382
5382
  for (const key of [
@@ -5413,8 +5413,8 @@ function readView(path) {
5413
5413
  throw new Error("invalid_view_identity");
5414
5414
  }
5415
5415
  const view = value;
5416
- const fallback = join48(directory, "callback-failure.json");
5417
- if (existsSync34(fallback)) {
5416
+ const fallback = join50(directory, "callback-failure.json");
5417
+ if (existsSync35(fallback)) {
5418
5418
  const recorded = object8(readPrivateJson2(fallback));
5419
5419
  const failure2 = object8(recorded.failure);
5420
5420
  if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
@@ -5425,8 +5425,8 @@ function readView(path) {
5425
5425
  return { file: file2, view };
5426
5426
  }
5427
5427
  function dismissFailure(file2, view) {
5428
- const fallback = join48(view.directory, "callback-failure.json");
5429
- if (existsSync34(fallback)) unlinkSync6(fallback);
5428
+ const fallback = join50(view.directory, "callback-failure.json");
5429
+ if (existsSync35(fallback)) unlinkSync6(fallback);
5430
5430
  delete view.failure;
5431
5431
  saveView(file2, view);
5432
5432
  }
@@ -5531,7 +5531,7 @@ var init_budget2 = __esm({
5531
5531
 
5532
5532
  // src/tui/adapters/tmux/connection.ts
5533
5533
  import { spawn as spawn3 } from "child_process";
5534
- import { join as join49 } from "path";
5534
+ import { join as join51 } from "path";
5535
5535
  var ViewConnection;
5536
5536
  var init_connection2 = __esm({
5537
5537
  "src/tui/adapters/tmux/connection.ts"() {
@@ -5544,7 +5544,7 @@ var init_connection2 = __esm({
5544
5544
  this.directory = directory;
5545
5545
  this.owner = owner;
5546
5546
  this.identity = identity;
5547
- this.socket = join49(directory, "s");
5547
+ this.socket = join51(directory, "s");
5548
5548
  }
5549
5549
  directory;
5550
5550
  owner;
@@ -5557,14 +5557,14 @@ var init_connection2 = __esm({
5557
5557
  "-S",
5558
5558
  this.socket,
5559
5559
  "-f",
5560
- join49(this.directory, "tmux.conf"),
5560
+ join51(this.directory, "tmux.conf"),
5561
5561
  ...args
5562
5562
  ];
5563
5563
  if (args[0] === "new-session" && !this.identity)
5564
5564
  return startPresentation(this.owner, options2, env).trim();
5565
5565
  return command(
5566
5566
  "tmux",
5567
- ["-S", this.socket, "-f", join49(this.directory, "tmux.conf"), ...args],
5567
+ ["-S", this.socket, "-f", join51(this.directory, "tmux.conf"), ...args],
5568
5568
  { env }
5569
5569
  ).trim();
5570
5570
  }
@@ -5628,7 +5628,7 @@ var init_connection2 = __esm({
5628
5628
  });
5629
5629
 
5630
5630
  // src/tui/application/feedback.ts
5631
- import { join as join50 } from "path";
5631
+ import { join as join52 } from "path";
5632
5632
  function describeFailure(action, error) {
5633
5633
  const message = error instanceof Error ? error.message : "view_action_failed";
5634
5634
  const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
@@ -6080,16 +6080,16 @@ var init_common = __esm({
6080
6080
  });
6081
6081
 
6082
6082
  // src/tui/application/create.ts
6083
- import { join as join51 } from "path";
6084
- import { realpathSync as realpathSync16 } from "fs";
6083
+ import { join as join53 } from "path";
6084
+ import { realpathSync as realpathSync17 } from "fs";
6085
6085
  async function createView(input) {
6086
6086
  const plan2 = planLayout(input.columns, input.rows);
6087
6087
  const helpers = runtime();
6088
6088
  const mailbox = input.mailbox ? {
6089
- config: realpathSync16(input.mailbox.config),
6090
- actorFile: realpathSync16(input.mailbox.actorFile)
6089
+ config: realpathSync17(input.mailbox.config),
6090
+ actorFile: realpathSync17(input.mailbox.actorFile)
6091
6091
  } : void 0;
6092
- const groupFile = realpathSync16(input.groupFile);
6092
+ const groupFile = realpathSync17(input.groupFile);
6093
6093
  const group = new GroupStore(groupFile).state.group;
6094
6094
  const common2 = new CommonHarness({
6095
6095
  groupFile,
@@ -6102,7 +6102,7 @@ async function createView(input) {
6102
6102
  );
6103
6103
  if (!main2) throw new Error("view_requires_live_main");
6104
6104
  const { directory, owner } = createDirectory();
6105
- const file2 = join51(directory, "view.json");
6105
+ const file2 = join53(directory, "view.json");
6106
6106
  const connection = new ViewConnection(directory, owner);
6107
6107
  const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
6108
6108
  const result = connection.raw([
@@ -6330,7 +6330,7 @@ var init_navigation = __esm({
6330
6330
  });
6331
6331
 
6332
6332
  // src/tui/application/history.ts
6333
- import { join as join52 } from "path";
6333
+ import { join as join54 } from "path";
6334
6334
  async function observeHistory(context) {
6335
6335
  const { view, renderer, common: common2, file: file2 } = context;
6336
6336
  await releaseControl(context);
@@ -6338,7 +6338,7 @@ async function observeHistory(context) {
6338
6338
  sessionId: view.target,
6339
6339
  input: { lines: 2e3, maxBytes: 65536 }
6340
6340
  });
6341
- privateJson(join52(view.directory, "history.json"), {
6341
+ privateJson(join54(view.directory, "history.json"), {
6342
6342
  sessionId: view.target,
6343
6343
  content: history.content,
6344
6344
  truncated: history.truncated
@@ -6414,8 +6414,8 @@ var init_mailbox = __esm({
6414
6414
  });
6415
6415
 
6416
6416
  // src/tui/application/lifecycle.ts
6417
- import { existsSync as existsSync35, unlinkSync as unlinkSync7 } from "fs";
6418
- import { join as join53 } from "path";
6417
+ import { existsSync as existsSync36, unlinkSync as unlinkSync7 } from "fs";
6418
+ import { join as join55 } from "path";
6419
6419
  function resizeView({ view, renderer, input }) {
6420
6420
  renderer.resize(view, planLayout(input.columns, input.rows));
6421
6421
  }
@@ -6430,8 +6430,8 @@ async function stopTarget(context) {
6430
6430
  async function closeView(context) {
6431
6431
  await returnToMain(context);
6432
6432
  context.renderer.connection.cleanup();
6433
- const history = join53(context.view.directory, "history.json");
6434
- if (existsSync35(history)) unlinkSync7(history);
6433
+ const history = join55(context.view.directory, "history.json");
6434
+ if (existsSync36(history)) unlinkSync7(history);
6435
6435
  context.view.state = "closed";
6436
6436
  saveView(context.file, context.view);
6437
6437
  }
@@ -6688,7 +6688,7 @@ var init_worker_start = __esm({
6688
6688
  });
6689
6689
 
6690
6690
  // src/tui/startup/config.ts
6691
- import { realpathSync as realpathSync17 } from "fs";
6691
+ import { realpathSync as realpathSync18 } from "fs";
6692
6692
  import { resolve as resolve19 } from "path";
6693
6693
  function startupSettings(request3) {
6694
6694
  const path = request3.config ? resolve19(request3.cwd, request3.config) : findConfig(request3.cwd);
@@ -6741,7 +6741,7 @@ function startupSettings(request3) {
6741
6741
  fail2("session memory exceeds the group budget");
6742
6742
  const groupFile = string("group_file");
6743
6743
  return {
6744
- projectRoot: realpathSync17(config?.dir ?? request3.cwd),
6744
+ projectRoot: realpathSync18(config?.dir ?? request3.cwd),
6745
6745
  ...config ? { configFile: config.path } : {},
6746
6746
  runtime: runtime2,
6747
6747
  allowedRuntimes: [.../* @__PURE__ */ new Set([...available, runtime2])],
@@ -6770,16 +6770,16 @@ var init_config3 = __esm({
6770
6770
 
6771
6771
  // src/tui/startup/state.ts
6772
6772
  import { createHash as createHash19 } from "crypto";
6773
- 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";
6774
6774
  import { homedir as homedir11 } from "os";
6775
- import { dirname as dirname26, join as join54 } from "path";
6775
+ import { dirname as dirname26, join as join56 } from "path";
6776
6776
  function startupFile(projectRoot) {
6777
- const root = join54(
6778
- process.env.XDG_STATE_HOME ?? join54(homedir11(), ".local/state"),
6777
+ const root = join56(
6778
+ process.env.XDG_STATE_HOME ?? join56(homedir11(), ".local/state"),
6779
6779
  "pmcp/launcher"
6780
6780
  );
6781
6781
  const key = createHash19("sha256").update(projectRoot).digest("hex");
6782
- return join54(root, `${key}.json`);
6782
+ return join56(root, `${key}.json`);
6783
6783
  }
6784
6784
  function ensureStartupDirectory(file2) {
6785
6785
  const directory = dirname26(file2);
@@ -6789,7 +6789,7 @@ function ensureStartupDirectory(file2) {
6789
6789
  throw new Error("unsafe_startup_directory");
6790
6790
  }
6791
6791
  function readStartup(file2, projectRoot) {
6792
- if (!existsSync36(file2)) return null;
6792
+ if (!existsSync37(file2)) return null;
6793
6793
  const value = object8(readPrivateJson2(file2));
6794
6794
  if (value.schemaVersion !== 1 || value.projectRoot !== projectRoot)
6795
6795
  throw new Error("foreign_startup_state");
@@ -9859,7 +9859,7 @@ function registerMailboxTools(server, service) {
9859
9859
 
9860
9860
  // src/mcp.ts
9861
9861
  var SERVER_NAME = "pmcp";
9862
- var SERVER_VERSION = "0.16.1";
9862
+ var SERVER_VERSION = "0.17.0";
9863
9863
  function registerSkillTools(server, tools) {
9864
9864
  registerCatalogTool(server, tools);
9865
9865
  registerFindTool(server, tools);
@@ -12002,7 +12002,7 @@ import { homedir as homedir6 } from "os";
12002
12002
  // src/project/apply.ts
12003
12003
  import { createHash as createHash13 } from "crypto";
12004
12004
  import {
12005
- existsSync as existsSync20,
12005
+ existsSync as existsSync21,
12006
12006
  lstatSync as lstatSync5,
12007
12007
  mkdirSync as mkdirSync7,
12008
12008
  readFileSync as readFileSync25,
@@ -12010,7 +12010,7 @@ import {
12010
12010
  readlinkSync as readlinkSync2,
12011
12011
  rmSync as rmSync5
12012
12012
  } from "fs";
12013
- 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";
12014
12014
  import { parse as parse5 } from "smol-toml";
12015
12015
 
12016
12016
  // src/project/backup.ts
@@ -12202,6 +12202,56 @@ function splitKeyed(path) {
12202
12202
 
12203
12203
  // src/project/mcp.ts
12204
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
12205
12255
  var runtimeByTool = {
12206
12256
  claude: "claude-code",
12207
12257
  codex: "codex-cli",
@@ -12209,6 +12259,15 @@ var runtimeByTool = {
12209
12259
  grok: "grok-cli",
12210
12260
  antigravity: "antigravity"
12211
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
+ }
12212
12271
  var ROOT_VAR = "${PROJECT_ROOT}";
12213
12272
  var ANTIGRAVITY_MCP_PLUGIN = ".agents/plugins/pmcp-mcp";
12214
12273
  var VARIABLE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/gu;
@@ -12344,18 +12403,19 @@ function httpEntry(server, tool) {
12344
12403
  function nativeEntry(spec, alias, server, tool) {
12345
12404
  let result;
12346
12405
  if (server.url !== void 0) {
12406
+ if (tool === "opencode") return opencodeMcpEntry(server);
12347
12407
  result = httpEntry(server, tool);
12348
12408
  } else {
12349
12409
  const env = { ...server.env };
12350
12410
  if (server.mailbox) {
12351
- env["PMCP_MAILBOX_RUNTIME"] = runtimeByTool[tool];
12411
+ env["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
12352
12412
  }
12353
12413
  let started;
12354
12414
  if (Object.keys(server.envFrom ?? {}).length > 0) {
12355
12415
  const entry2 = relative9(spec.root, process.argv[1] ?? "");
12356
12416
  const driverEnv = {};
12357
12417
  if (server.mailbox) {
12358
- driverEnv["PMCP_MAILBOX_RUNTIME"] = runtimeByTool[tool];
12418
+ driverEnv["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
12359
12419
  }
12360
12420
  if (!isAbsolute5(entry2) && !entry2.startsWith("..")) {
12361
12421
  started = launch({
@@ -12373,6 +12433,7 @@ function nativeEntry(spec, alias, server, tool) {
12373
12433
  } else {
12374
12434
  started = launch({ ...server, env });
12375
12435
  }
12436
+ if (tool === "opencode") return opencodeMcpEntry(server, started);
12376
12437
  result = { command: started.command, args: [...started.args] };
12377
12438
  if (Object.keys(started.env).length > 0) {
12378
12439
  result["env"] = { ...started.env };
@@ -12429,9 +12490,11 @@ function mcpOutputs(spec) {
12429
12490
  const jsonFiles = {
12430
12491
  claude: ".mcp.json",
12431
12492
  gemini: ".gemini/settings.json",
12432
- antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json`
12493
+ antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json`,
12494
+ opencode: OPENCODE_CONFIG
12433
12495
  };
12434
12496
  for (const tool of spec.tools) {
12497
+ if (tool === "opencode") checkOpenCodeConfig(spec.root);
12435
12498
  const servers = serversForTool(spec, tool);
12436
12499
  if (servers.length === 0) {
12437
12500
  continue;
@@ -12450,7 +12513,7 @@ function mcpOutputs(spec) {
12450
12513
  outputs.push(
12451
12514
  jsonOutput(
12452
12515
  file2,
12453
- ["mcpServers", alias],
12516
+ [tool === "opencode" ? "mcp" : "mcpServers", alias],
12454
12517
  nativeEntry(spec, alias, server, tool)
12455
12518
  )
12456
12519
  );
@@ -12466,14 +12529,14 @@ function mcpOutputs(spec) {
12466
12529
 
12467
12530
  // src/project/hooks.ts
12468
12531
  import { createHash as createHash12 } from "crypto";
12469
- import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
12470
- import { join as join22 } from "path";
12532
+ import { existsSync as existsSync19, readFileSync as readFileSync23 } from "fs";
12533
+ import { join as join23 } from "path";
12471
12534
  import { fileURLToPath as fileURLToPath2 } from "url";
12472
12535
 
12473
12536
  // src/project/hook-adapter.ts
12474
12537
  import { spawnSync as spawnSync2 } from "child_process";
12475
12538
  import { createHash as createHash11 } from "crypto";
12476
- 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";
12477
12540
  import { dirname as dirname16, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve12 } from "path";
12478
12541
  var denied = () => ({
12479
12542
  decision: "deny",
@@ -12490,7 +12553,7 @@ function guardPath(root, command3) {
12490
12553
  if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
12491
12554
  throw new Error("Hook guard must stay under .agents/guards/");
12492
12555
  }
12493
- if (!existsSync17(absolute2) || !statSync10(absolute2).isFile()) {
12556
+ if (!existsSync18(absolute2) || !statSync10(absolute2).isFile()) {
12494
12557
  throw new Error("Hook guard script must exist");
12495
12558
  }
12496
12559
  const actual = relative10(base, realpathSync5(absolute2));
@@ -12685,8 +12748,8 @@ function object4(value) {
12685
12748
  return value;
12686
12749
  }
12687
12750
  function groups(root, file2, key) {
12688
- const absolute2 = join22(root, file2);
12689
- if (!existsSync18(absolute2)) {
12751
+ const absolute2 = join23(root, file2);
12752
+ if (!existsSync19(absolute2)) {
12690
12753
  return [];
12691
12754
  }
12692
12755
  let value;
@@ -12715,15 +12778,15 @@ function adapterSource() {
12715
12778
  const sourceBuild = fileURLToPath2(
12716
12779
  new URL("../../dist/project/hook-adapter.js", import.meta.url)
12717
12780
  );
12718
- const file2 = existsSync18(published) ? published : sourceBuild;
12719
- if (!existsSync18(file2)) {
12781
+ const file2 = existsSync19(published) ? published : sourceBuild;
12782
+ if (!existsSync19(file2)) {
12720
12783
  throw new ProjectError("Build pmcp before projecting shell hook adapters");
12721
12784
  }
12722
12785
  return readFileSync23(file2, "utf8");
12723
12786
  }
12724
12787
  function hookOutputs(spec) {
12725
- const lockFile = join22(spec.root, "pmcp.lock");
12726
- 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")) : {};
12727
12790
  const previous = new Map(
12728
12791
  (lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
12729
12792
  );
@@ -12838,33 +12901,44 @@ ${lines2.join("\n")}
12838
12901
  }
12839
12902
 
12840
12903
  // src/project/plan.ts
12841
- import { existsSync as existsSync19, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
12842
- 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
12843
12916
  var AGENT_FIELDS = {
12844
12917
  claude: ["name", "description", "tools", "model", "effort"],
12845
12918
  gemini: ["name", "description"]
12846
12919
  };
12847
12920
  var SETTINGS = ".claude/settings.json";
12848
12921
  function listDirs(dir) {
12849
- if (!existsSync19(dir)) return [];
12922
+ if (!existsSync20(dir)) return [];
12850
12923
  return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
12851
12924
  }
12852
12925
  var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
12853
12926
  function skillNames(root) {
12854
- const dir = join23(root, ".agents/skills");
12927
+ const dir = join24(root, ".agents/skills");
12855
12928
  return listDirs(dir).filter(
12856
- (name) => existsSync19(join23(dir, name, "SKILL.md"))
12929
+ (name) => existsSync20(join24(dir, name, "SKILL.md"))
12857
12930
  );
12858
12931
  }
12859
12932
  function agentNames(root) {
12860
- const dir = join23(root, ".agents/agents");
12933
+ const dir = join24(root, ".agents/agents");
12861
12934
  return listDirs(dir).filter(
12862
- (name) => existsSync19(join23(dir, name, "agent.md"))
12935
+ (name) => existsSync20(join24(dir, name, "agent.md"))
12863
12936
  );
12864
12937
  }
12865
12938
  function skillOutputs(spec) {
12866
- const outputs = [];
12867
- 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) {
12868
12942
  const target2 = `../../.agents/skills/${name}`;
12869
12943
  if (spec.tools.has("claude")) {
12870
12944
  outputs.push({ kind: "link", path: `.claude/skills/${name}`, target: target2 });
@@ -12893,13 +12967,13 @@ policy:
12893
12967
  }
12894
12968
  function ruleOutputs(spec) {
12895
12969
  if (!spec.tools.has("claude")) return [];
12896
- const dir = join23(spec.root, ".agents/rules");
12897
- if (!existsSync19(dir)) return [];
12970
+ const dir = join24(spec.root, ".agents/rules");
12971
+ if (!existsSync20(dir)) return [];
12898
12972
  const outputs = [];
12899
12973
  for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
12900
12974
  const rel = `.agents/rules/${file2}`;
12901
12975
  const { meta, body } = splitFrontmatter(
12902
- readFileSync24(join23(spec.root, rel), "utf8")
12976
+ readFileSync24(join24(spec.root, rel), "utf8")
12903
12977
  );
12904
12978
  const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
12905
12979
  const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
@@ -12918,7 +12992,7 @@ function agentOutputs(spec) {
12918
12992
  for (const name of agentNames(spec.root)) {
12919
12993
  const rel = `.agents/agents/${name}/agent.md`;
12920
12994
  const { meta, body } = splitFrontmatter(
12921
- readFileSync24(join23(spec.root, rel), "utf8")
12995
+ readFileSync24(join24(spec.root, rel), "utf8")
12922
12996
  );
12923
12997
  const overrides = spec.agents[name] ?? {};
12924
12998
  const note = `<!-- ${MARK} from ${rel} -->
@@ -13058,8 +13132,8 @@ var PREDECESSORS = [
13058
13132
  ];
13059
13133
  var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
13060
13134
  function readLock(root) {
13061
- const path = join24(root, LOCK_FILE);
13062
- if (!existsSync20(path)) return [];
13135
+ const path = join25(root, LOCK_FILE);
13136
+ if (!existsSync21(path)) return [];
13063
13137
  const lock = JSON.parse(readFileSync25(path, "utf8"));
13064
13138
  return lock.outputs ?? [];
13065
13139
  }
@@ -13070,9 +13144,9 @@ function linkOf(abs) {
13070
13144
  return null;
13071
13145
  }
13072
13146
  }
13073
- var exists = (abs) => existsSync20(abs) || linkOf(abs) !== null;
13147
+ var exists = (abs) => existsSync21(abs) || linkOf(abs) !== null;
13074
13148
  function readJson2(abs) {
13075
- if (!existsSync20(abs)) return {};
13149
+ if (!existsSync21(abs)) return {};
13076
13150
  let value;
13077
13151
  try {
13078
13152
  value = JSON.parse(readFileSync25(abs, "utf8"));
@@ -13127,32 +13201,44 @@ function withoutBlock(text3) {
13127
13201
  return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
13128
13202
  }
13129
13203
  function present(root, output) {
13130
- const abs = join24(root, "file" in output ? output.file : output.path);
13204
+ const abs = join25(root, "file" in output ? output.file : output.path);
13131
13205
  switch (output.kind) {
13132
13206
  case "link":
13133
13207
  return linkOf(abs) === output.target;
13134
13208
  case "file":
13135
- return existsSync20(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
13209
+ return existsSync21(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
13136
13210
  case "json":
13137
13211
  if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
13138
13212
  return getKey(readJson2(abs), output.key) === void 0;
13139
13213
  }
13140
13214
  return same(getKey(readJson2(abs), output.key), output.value);
13141
13215
  case "block":
13142
- return existsSync20(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
13216
+ return existsSync21(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
13143
13217
  }
13144
13218
  }
13145
13219
  function replaceable(root, output, owned) {
13146
13220
  if (output.kind === "json") {
13147
- 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)) {
13148
13234
  return true;
13149
13235
  }
13150
- const existing = getKey(readJson2(join24(root, output.file)), output.key);
13236
+ const existing = getKey(readJson2(join25(root, output.file)), output.key);
13151
13237
  return existing === void 0 || same(existing, output.value);
13152
13238
  }
13153
13239
  if (output.kind === "block") {
13154
- const absolute2 = join24(root, output.file);
13155
- const text4 = existsSync20(absolute2) ? readFileSync25(absolute2, "utf8") : "";
13240
+ const absolute2 = join25(root, output.file);
13241
+ const text4 = existsSync21(absolute2) ? readFileSync25(absolute2, "utf8") : "";
13156
13242
  const outside = parse5(withoutBlock(text4));
13157
13243
  const proposed = parse5(output.content);
13158
13244
  const existing = objectKeys(outside["mcp_servers"]);
@@ -13161,7 +13247,7 @@ function replaceable(root, output, owned) {
13161
13247
  );
13162
13248
  }
13163
13249
  if (owned.has(output.path)) return true;
13164
- const abs = join24(root, output.path);
13250
+ const abs = join25(root, output.path);
13165
13251
  if (!exists(abs)) return true;
13166
13252
  const target2 = linkOf(abs);
13167
13253
  if (target2 !== null) {
@@ -13182,15 +13268,15 @@ function undeclared(spec, outputs) {
13182
13268
  const declared = new Set(outputs.map((o) => o.path));
13183
13269
  const found = [];
13184
13270
  for (const dir of censusDirs(spec)) {
13185
- const abs = join24(spec.root, dir);
13186
- if (!existsSync20(abs)) continue;
13271
+ const abs = join25(spec.root, dir);
13272
+ if (!existsSync21(abs)) continue;
13187
13273
  for (const entry2 of readdirSync9(abs)) {
13188
13274
  const path = `${dir}/${entry2}`;
13189
13275
  if (!declared.has(path)) found.push(path);
13190
13276
  }
13191
13277
  }
13192
- const rules = join24(spec.root, RULES_DIR);
13193
- if (spec.tools.has("grok") && existsSync20(rules)) {
13278
+ const rules = join25(spec.root, RULES_DIR);
13279
+ if (spec.tools.has("grok") && existsSync21(rules)) {
13194
13280
  for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
13195
13281
  if (entry2.isDirectory()) continue;
13196
13282
  found.push(`${RULES_DIR}/${entry2.name}`);
@@ -13217,8 +13303,8 @@ function deadGlobs(root, outputs) {
13217
13303
  if (rule.kind === "file") {
13218
13304
  text3 = rule.content;
13219
13305
  } else if (rule.kind === "link") {
13220
- const abs = join24(root, posix2.join(posix2.dirname(rule.path), rule.target));
13221
- if (!existsSync20(abs)) continue;
13306
+ const abs = join25(root, posix2.join(posix2.dirname(rule.path), rule.target));
13307
+ if (!existsSync21(abs)) continue;
13222
13308
  text3 = readFileSync25(abs, "utf8");
13223
13309
  } else {
13224
13310
  continue;
@@ -13269,7 +13355,7 @@ function project(spec, options2) {
13269
13355
  }
13270
13356
  }
13271
13357
  for (const path of jsonPaths) {
13272
- readJson2(join24(spec.root, path));
13358
+ readJson2(join25(spec.root, path));
13273
13359
  }
13274
13360
  if (options2.check) {
13275
13361
  const problems = [
@@ -13290,6 +13376,7 @@ function project(spec, options2) {
13290
13376
  return { code: 3, problems: refused, outputs: outputs.length, pruned: 0 };
13291
13377
  }
13292
13378
  const mcpPaths = [
13379
+ "opencode.json",
13293
13380
  ".mcp.json",
13294
13381
  ".gemini/settings.json",
13295
13382
  ".agents/mcp_config.json",
@@ -13331,7 +13418,7 @@ function project(spec, options2) {
13331
13418
  for (const output of outputs) {
13332
13419
  if (output.kind !== "link" && output.kind !== "file") continue;
13333
13420
  if (present(spec.root, output)) continue;
13334
- const abs = join24(spec.root, output.path);
13421
+ const abs = join25(spec.root, output.path);
13335
13422
  mkdirSync7(dirname18(abs), { recursive: true });
13336
13423
  if (output.kind === "link") {
13337
13424
  replaceLink(abs, output.target);
@@ -13359,12 +13446,12 @@ function project(spec, options2) {
13359
13446
  group.drops.push(keyed.key);
13360
13447
  jsonFiles.set(keyed.file, group);
13361
13448
  } else {
13362
- rmSync5(join24(spec.root, entry2.path), { force: true, recursive: true });
13449
+ rmSync5(join25(spec.root, entry2.path), { force: true, recursive: true });
13363
13450
  }
13364
13451
  pruned += 1;
13365
13452
  }
13366
13453
  for (const [file2, { sets, drops }] of jsonFiles) {
13367
- const abs = join24(spec.root, file2);
13454
+ const abs = join25(spec.root, file2);
13368
13455
  const before = readJson2(abs);
13369
13456
  const after = structuredClone(before);
13370
13457
  for (const key of drops) deleteKey(after, key);
@@ -13379,14 +13466,14 @@ function project(spec, options2) {
13379
13466
  }
13380
13467
  }
13381
13468
  if (same(before, after)) continue;
13382
- if (!existsSync20(abs) && Object.keys(after).length === 0) continue;
13469
+ if (!existsSync21(abs) && Object.keys(after).length === 0) continue;
13383
13470
  mkdirSync7(dirname18(abs), { recursive: true });
13384
13471
  replaceFile(abs, `${JSON.stringify(after, null, 2)}
13385
13472
  `);
13386
13473
  }
13387
13474
  for (const [file2, content] of blockFiles) {
13388
- const abs = join24(spec.root, file2);
13389
- const before = existsSync20(abs) ? readFileSync25(abs, "utf8") : "";
13475
+ const abs = join25(spec.root, file2);
13476
+ const before = existsSync21(abs) ? readFileSync25(abs, "utf8") : "";
13390
13477
  let after = withoutBlock(before);
13391
13478
  if (content !== null) {
13392
13479
  const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
@@ -13405,15 +13492,15 @@ function project(spec, options2) {
13405
13492
  )
13406
13493
  );
13407
13494
  for (const [name, target2] of legacyLinks(spec.root)) {
13408
- const abs = join24(spec.root, RULES_DIR, `${name}.md`);
13409
- const broken = !existsSync20(abs);
13495
+ const abs = join25(spec.root, RULES_DIR, `${name}.md`);
13496
+ const broken = !existsSync21(abs);
13410
13497
  if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
13411
13498
  rmSync5(abs, { force: true });
13412
13499
  pruned += 1;
13413
13500
  }
13414
13501
  }
13415
13502
  replaceFile(
13416
- join24(spec.root, LOCK_FILE),
13503
+ join25(spec.root, LOCK_FILE),
13417
13504
  `${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
13418
13505
  `
13419
13506
  );
@@ -13510,7 +13597,8 @@ var TOOLS = [
13510
13597
  "codex",
13511
13598
  "gemini",
13512
13599
  "grok",
13513
- "antigravity"
13600
+ "antigravity",
13601
+ "opencode"
13514
13602
  ];
13515
13603
  var NAME = /^[a-z0-9][a-z0-9-]*$/u;
13516
13604
  function table(path, where, value) {
@@ -13666,7 +13754,8 @@ function nativeOptions(path, where, value) {
13666
13754
  codex: ["startup_timeout_sec", "tool_timeout_sec"],
13667
13755
  gemini: ["timeout"],
13668
13756
  grok: ["startup_timeout_sec", "tool_timeout_sec"],
13669
- antigravity: []
13757
+ antigravity: [],
13758
+ opencode: ["timeout"]
13670
13759
  };
13671
13760
  const result = {};
13672
13761
  for (const [tool, raw] of Object.entries(
@@ -13860,6 +13949,11 @@ function sourceServers(source2, root) {
13860
13949
  );
13861
13950
  }
13862
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
+ }
13863
13957
  if (statSync11(path).size > 2 * 1024 * 1024) {
13864
13958
  throw new ProjectError("MCP source exceeds the 2 MiB limit");
13865
13959
  }
@@ -14224,9 +14318,68 @@ async function execMcp(spec, alias, env) {
14224
14318
 
14225
14319
  // src/project/doctor.ts
14226
14320
  import { spawnSync as spawnSync3 } from "child_process";
14227
- import { existsSync as existsSync21, readFileSync as readFileSync28 } from "fs";
14228
- import { join as join25 } from "path";
14321
+ import { existsSync as existsSync22, readFileSync as readFileSync28 } from "fs";
14322
+ import { join as join27 } from "path";
14229
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
14230
14383
  var spawnRunner = (command3, args, cwd, env) => {
14231
14384
  const result = spawnSync3(command3, [...args], {
14232
14385
  cwd,
@@ -14302,9 +14455,9 @@ function claude(spec, o) {
14302
14455
  }
14303
14456
  function codex(spec, o) {
14304
14457
  const findings = [];
14305
- const config = join25(o.home, ".codex/config.toml");
14458
+ const config = join27(o.home, ".codex/config.toml");
14306
14459
  let trusted = false;
14307
- if (existsSync21(config)) {
14460
+ if (existsSync22(config)) {
14308
14461
  const projects = parse8(readFileSync28(config, "utf8")).projects ?? {};
14309
14462
  const root = spec.root.replace(/\/$/u, "");
14310
14463
  trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
@@ -14432,7 +14585,7 @@ function grok(spec, o) {
14432
14585
  function antigravity(spec, options2) {
14433
14586
  const foreign = agentNames(spec.root).filter((name) => {
14434
14587
  const text3 = readFileSync28(
14435
- join25(spec.root, ".agents/agents", name, "agent.md"),
14588
+ join27(spec.root, ".agents/agents", name, "agent.md"),
14436
14589
  "utf8"
14437
14590
  );
14438
14591
  return splitFrontmatter(text3).meta["tools"] !== void 0;
@@ -14452,7 +14605,7 @@ function antigravity(spec, options2) {
14452
14605
  if (serversForTool(spec, "antigravity").length > 0) {
14453
14606
  const result = options2.run(
14454
14607
  "agy",
14455
- ["plugin", "validate", join25(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14608
+ ["plugin", "validate", join27(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14456
14609
  spec.root
14457
14610
  );
14458
14611
  findings.push(
@@ -14476,6 +14629,8 @@ function doctor(spec, options2) {
14476
14629
  if (wanted("codex")) findings.push(...codex(spec, options2));
14477
14630
  if (wanted("gemini")) findings.push(...gemini(spec, options2));
14478
14631
  if (wanted("grok")) findings.push(...grok(spec, options2));
14632
+ if (wanted("opencode"))
14633
+ findings.push(...opencodeDoctor(spec, options2, listed));
14479
14634
  if (wanted("antigravity")) {
14480
14635
  findings.push(...antigravity(spec, options2));
14481
14636
  }
@@ -14483,8 +14638,8 @@ function doctor(spec, options2) {
14483
14638
  }
14484
14639
 
14485
14640
  // src/project/memory.ts
14486
- import { existsSync as existsSync22, readFileSync as readFileSync29 } from "fs";
14487
- 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";
14488
14643
  import { parse as parse9 } from "smol-toml";
14489
14644
  function object7(value) {
14490
14645
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
@@ -14493,7 +14648,7 @@ function object7(value) {
14493
14648
  return value;
14494
14649
  }
14495
14650
  function settings(file2, toml = false) {
14496
- if (!existsSync22(file2)) {
14651
+ if (!existsSync23(file2)) {
14497
14652
  return {};
14498
14653
  }
14499
14654
  const text3 = readFileSync29(file2, "utf8");
@@ -14509,11 +14664,17 @@ function configuredState(value) {
14509
14664
  return "unknown";
14510
14665
  }
14511
14666
  function located(metadata2, path, pathScope) {
14512
- return { ...metadata2, path, pathScope, exists: existsSync22(path) };
14667
+ return { ...metadata2, path, pathScope, exists: existsSync23(path) };
14513
14668
  }
14514
14669
  function metadata(tool, spec, options2) {
14515
14670
  const env = options2.env ?? {};
14516
- 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
+ }
14517
14678
  if (tool === "antigravity") {
14518
14679
  return {
14519
14680
  state: "unsupported",
@@ -14537,7 +14698,7 @@ function metadata(tool, spec, options2) {
14537
14698
  state,
14538
14699
  source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
14539
14700
  },
14540
- join26(home2, "memories"),
14701
+ join28(home2, "memories"),
14541
14702
  "store"
14542
14703
  );
14543
14704
  }
@@ -14550,12 +14711,12 @@ function metadata(tool, spec, options2) {
14550
14711
  }
14551
14712
  if (tool === "claude") {
14552
14713
  const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
14553
- const user2 = settings(join26(home2, "settings.json"));
14714
+ const user2 = settings(join28(home2, "settings.json"));
14554
14715
  const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
14555
14716
  const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
14556
14717
  const override = user2["autoMemoryDirectory"];
14557
14718
  if (typeof override === "string") {
14558
- const path = override.startsWith("~/") ? join26(options2.home, override.slice(2)) : override;
14719
+ const path = override.startsWith("~/") ? join28(options2.home, override.slice(2)) : override;
14559
14720
  if (!isAbsolute7(path)) {
14560
14721
  return {
14561
14722
  state: "unknown",
@@ -14568,7 +14729,7 @@ function metadata(tool, spec, options2) {
14568
14729
  if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
14569
14730
  return located(
14570
14731
  { state, source: source2 },
14571
- join26(home2, "projects", projectName, "memory"),
14732
+ join28(home2, "projects", projectName, "memory"),
14572
14733
  "store"
14573
14734
  );
14574
14735
  }
@@ -14577,14 +14738,14 @@ function metadata(tool, spec, options2) {
14577
14738
  state,
14578
14739
  source: `${source2}; native project directory encoding is not guessed`
14579
14740
  },
14580
- join26(home2, "projects"),
14741
+ join28(home2, "projects"),
14581
14742
  "base"
14582
14743
  );
14583
14744
  }
14584
14745
  if (tool === "grok") {
14585
- const home2 = join26(options2.home, ".grok");
14586
- const user2 = settings(join26(home2, "config.toml"), true);
14587
- 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);
14588
14749
  const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
14589
14750
  const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
14590
14751
  const overridden = projectMemory["enabled"] !== void 0;
@@ -14594,12 +14755,12 @@ function metadata(tool, spec, options2) {
14594
14755
  state,
14595
14756
  source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
14596
14757
  },
14597
- join26(home2, "memory-v2/workspaces"),
14758
+ join28(home2, "memory-v2/workspaces"),
14598
14759
  "base"
14599
14760
  );
14600
14761
  }
14601
14762
  const home = directory("GEMINI_CLI_HOME", ".gemini");
14602
- const user = settings(join26(home, "settings.json"));
14763
+ const user = settings(join28(home, "settings.json"));
14603
14764
  const context = user["context"] === void 0 ? {} : object7(user["context"]);
14604
14765
  const names = context["fileName"] ?? "GEMINI.md";
14605
14766
  const contextFileNames = typeof names === "string" ? [names] : names;
@@ -14615,7 +14776,7 @@ function metadata(tool, spec, options2) {
14615
14776
  source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
14616
14777
  contextFileNames
14617
14778
  },
14618
- join26(home, "GEMINI.md"),
14779
+ join28(home, "GEMINI.md"),
14619
14780
  "store"
14620
14781
  );
14621
14782
  }
@@ -14648,14 +14809,14 @@ function memoryFindings(spec, options2) {
14648
14809
  // src/project/edit.ts
14649
14810
  import {
14650
14811
  cpSync,
14651
- existsSync as existsSync23,
14812
+ existsSync as existsSync24,
14652
14813
  mkdirSync as mkdirSync8,
14653
14814
  readFileSync as readFileSync30,
14654
14815
  rmSync as rmSync6,
14655
14816
  statSync as statSync13,
14656
14817
  writeFileSync as writeFileSync7
14657
14818
  } from "fs";
14658
- 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";
14659
14820
  var CROSS_TOOL = ["name", "description"];
14660
14821
  var tomlValue2 = (value) => JSON.stringify(value);
14661
14822
  function findEntry(entries, wanted) {
@@ -14687,8 +14848,8 @@ function addEntry(root, entry2) {
14687
14848
  );
14688
14849
  }
14689
14850
  if (kind2 === "skill") {
14690
- const dest2 = join27(root, ".agents/skills", name);
14691
- if (existsSync23(dest2))
14851
+ const dest2 = join29(root, ".agents/skills", name);
14852
+ if (existsSync24(dest2))
14692
14853
  throw new ProjectError(`.agents/skills/${name} already exists`);
14693
14854
  const source2 = entry2.root ?? dirname20(entry2.path);
14694
14855
  mkdirSync8(dirname20(dest2), { recursive: true });
@@ -14703,14 +14864,14 @@ function addEntry(root, entry2) {
14703
14864
  description: meta["description"] ?? entry2.description
14704
14865
  });
14705
14866
  writeFileSync7(
14706
- join27(dest2, "SKILL.md"),
14867
+ join29(dest2, "SKILL.md"),
14707
14868
  `${head}
14708
14869
  ${body.replace(/^\n/u, "")}`
14709
14870
  );
14710
14871
  return { kind: kind2, name, path: `.agents/skills/${name}` };
14711
14872
  }
14712
- const dest = join27(root, ".agents/agents", name);
14713
- if (existsSync23(dest))
14873
+ const dest = join29(root, ".agents/agents", name);
14874
+ if (existsSync24(dest))
14714
14875
  throw new ProjectError(`.agents/agents/${name} already exists`);
14715
14876
  const kept = {};
14716
14877
  const moved = {};
@@ -14720,17 +14881,17 @@ ${body.replace(/^\n/u, "")}`
14720
14881
  }
14721
14882
  mkdirSync8(dest, { recursive: true });
14722
14883
  writeFileSync7(
14723
- join27(dest, "agent.md"),
14884
+ join29(dest, "agent.md"),
14724
14885
  `${renderFrontmatter(kept)}
14725
14886
  ${body.replace(/^\n/u, "")}`
14726
14887
  );
14727
14888
  if (Object.keys(moved).length > 0) {
14728
- const config = join27(root, "pmcp.toml");
14889
+ const config = join29(root, "pmcp.toml");
14729
14890
  const lines2 = [
14730
14891
  `[agents.${name}.claude]`,
14731
14892
  ...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
14732
14893
  ];
14733
- const before = existsSync23(config) ? readFileSync30(config, "utf8") : "";
14894
+ const before = existsSync24(config) ? readFileSync30(config, "utf8") : "";
14734
14895
  const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
14735
14896
  writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
14736
14897
  `);
@@ -14753,14 +14914,14 @@ function withoutTables(toml, name) {
14753
14914
  function removeAsset(root, name) {
14754
14915
  const removed = [];
14755
14916
  for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
14756
- if (!existsSync23(join27(root, rel))) continue;
14757
- rmSync6(join27(root, rel), { recursive: true, force: true });
14917
+ if (!existsSync24(join29(root, rel))) continue;
14918
+ rmSync6(join29(root, rel), { recursive: true, force: true });
14758
14919
  removed.push(rel);
14759
14920
  }
14760
14921
  if (removed.length === 0)
14761
14922
  throw new ProjectError(`no skill or agent named ${name} in .agents/`);
14762
- const config = join27(root, "pmcp.toml");
14763
- if (existsSync23(config)) {
14923
+ const config = join29(root, "pmcp.toml");
14924
+ if (existsSync24(config)) {
14764
14925
  const before = readFileSync30(config, "utf8");
14765
14926
  const after = withoutTables(before, name);
14766
14927
  if (after !== before) writeFileSync7(config, after);
@@ -15000,16 +15161,16 @@ var doctorCommand = {
15000
15161
  };
15001
15162
 
15002
15163
  // src/validate.ts
15003
- import { existsSync as existsSync24, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
15004
- 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";
15005
15166
  function validateSkillDir(dir) {
15006
15167
  const root = resolve16(dir);
15007
- const skillMd = basename8(root) === "SKILL.md" ? root : join28(root, "SKILL.md");
15008
- 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()) {
15009
15170
  return [{ path: root, reason: "no SKILL.md", level: "error" }];
15010
15171
  }
15011
15172
  const front = frontmatterObject(readFileSync31(skillMd, "utf8"));
15012
- const directory = basename8(skillMd === root ? join28(root, "..") : root);
15173
+ const directory = basename8(skillMd === root ? join30(root, "..") : root);
15013
15174
  return specProblems(front, directory).map((reason) => ({
15014
15175
  path: skillMd,
15015
15176
  reason,
@@ -15694,14 +15855,14 @@ import { resolve as resolve21 } from "path";
15694
15855
 
15695
15856
  // src/marketplace/export.ts
15696
15857
  import {
15697
- existsSync as existsSync37,
15858
+ existsSync as existsSync38,
15698
15859
  lstatSync as lstatSync14,
15699
15860
  mkdirSync as mkdirSync13,
15700
15861
  readFileSync as readFileSync38,
15701
15862
  readdirSync as readdirSync13,
15702
15863
  writeFileSync as writeFileSync14
15703
15864
  } from "fs";
15704
- 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";
15705
15866
  var NATIVE_PLUGIN_RUNTIMES = [
15706
15867
  "claude",
15707
15868
  "codex",
@@ -15726,7 +15887,7 @@ function readResources(directory, prefix, files) {
15726
15887
  for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
15727
15888
  if (entry2.isSymbolicLink())
15728
15889
  throw new Error("Plugin exports cannot contain symlinks");
15729
- const path = join55(directory, entry2.name);
15890
+ const path = join57(directory, entry2.name);
15730
15891
  const relativePath = `${prefix}/${entry2.name}`;
15731
15892
  if (entry2.isDirectory()) {
15732
15893
  if (relativePath.split("/").length > 6)
@@ -15757,7 +15918,7 @@ function pluginExportFiles(root, name, runtime2) {
15757
15918
  agy: []
15758
15919
  };
15759
15920
  for (const filename of manifests[runtime2]) {
15760
- if (existsSync37(join55(plugin.directory, filename)))
15921
+ if (existsSync38(join57(plugin.directory, filename)))
15761
15922
  files.set(
15762
15923
  filename,
15763
15924
  readFileSync38(containedPath(plugin.directory, filename))
@@ -15771,8 +15932,8 @@ function pluginExportFiles(root, name, runtime2) {
15771
15932
  `
15772
15933
  )
15773
15934
  );
15774
- const mcp = join55(plugin.directory, ".mcp.json");
15775
- if (existsSync37(mcp))
15935
+ const mcp = join57(plugin.directory, ".mcp.json");
15936
+ if (existsSync38(mcp))
15776
15937
  files.set(
15777
15938
  "mcp_config.json",
15778
15939
  readFileSync38(containedPath(plugin.directory, mcp))
@@ -15794,7 +15955,7 @@ function exportPublicPlugin(root, name, runtime2, output) {
15794
15955
  throw error;
15795
15956
  }
15796
15957
  for (const [path, bytes] of files) {
15797
- const target2 = join55(destination, path);
15958
+ const target2 = join57(destination, path);
15798
15959
  mkdirSync13(dirname27(target2), { recursive: true });
15799
15960
  writeFileSync14(target2, bytes, { flag: "wx" });
15800
15961
  }
@@ -15946,7 +16107,7 @@ function dispatch(argv, options2 = {}) {
15946
16107
  }
15947
16108
  return run(command3, rest, { ui, env, cwd });
15948
16109
  }
15949
- var VERSION = "0.16.1";
16110
+ var VERSION = "0.17.0";
15950
16111
  async function defaultEntry(options2, context) {
15951
16112
  const terminal = options2.terminal ?? {
15952
16113
  stdin: process.stdin.isTTY === true,