@modootoday/pmcp 0.14.1 → 0.15.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
@@ -9,6 +9,23 @@ var __export = (target2, all) => {
9
9
  __defProp(target2, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/cli/entry.ts
13
+ function interactiveEntry(terminal) {
14
+ if (!terminal.stdin || !terminal.stdout) return false;
15
+ if (!terminal.term || terminal.term === "dumb") return false;
16
+ return !terminal.ci || terminal.ci === "false" || terminal.ci === "0";
17
+ }
18
+ function entryMode(terminal) {
19
+ if (!terminal.stdin || !terminal.stdout) return "server";
20
+ if (!interactiveEntry(terminal)) return "help";
21
+ return "terminal";
22
+ }
23
+ var init_entry = __esm({
24
+ "src/cli/entry.ts"() {
25
+ "use strict";
26
+ }
27
+ });
28
+
12
29
  // src/storage/sqlite.ts
13
30
  async function openSqlite(path, options2 = {}) {
14
31
  const provider = process.versions.bun ? "bun:sqlite" : "node:sqlite";
@@ -245,6 +262,7 @@ function findConfig(start2) {
245
262
  for (; ; ) {
246
263
  const candidate2 = join4(dir, CONFIG_FILE);
247
264
  if (existsSync4(candidate2)) return candidate2;
265
+ if (existsSync4(join4(dir, ".git"))) return null;
248
266
  const parent = dirname3(dir);
249
267
  if (parent === dir) return null;
250
268
  dir = parent;
@@ -1920,47 +1938,47 @@ var init_validation2 = __esm({
1920
1938
  });
1921
1939
 
1922
1940
  // src/harness/groups/store.ts
1923
- import { randomUUID as randomUUID4 } from "crypto";
1941
+ import { randomUUID as randomUUID5 } from "crypto";
1924
1942
  import {
1925
- closeSync as closeSync3,
1943
+ closeSync as closeSync4,
1926
1944
  constants as constants3,
1927
1945
  fstatSync as fstatSync2,
1928
- fsyncSync,
1929
- openSync as openSync3,
1930
- readFileSync as readFileSync33,
1931
- realpathSync as realpathSync4,
1932
- renameSync as renameSync3,
1946
+ fsyncSync as fsyncSync2,
1947
+ openSync as openSync4,
1948
+ readFileSync as readFileSync34,
1949
+ realpathSync as realpathSync6,
1950
+ renameSync as renameSync4,
1933
1951
  writeFileSync as writeFileSync9
1934
1952
  } from "fs";
1935
- import { dirname as dirname19, join as join27 } from "path";
1953
+ import { dirname as dirname21, join as join29 } from "path";
1936
1954
  function privateJson(path, value) {
1937
- const temporary = `${path}.${randomUUID4()}.new`;
1938
- const descriptor = openSync3(temporary, "wx", 384);
1955
+ const temporary = `${path}.${randomUUID5()}.new`;
1956
+ const descriptor = openSync4(temporary, "wx", 384);
1939
1957
  try {
1940
1958
  writeFileSync9(descriptor, `${JSON.stringify(value, null, 2)}
1941
1959
  `);
1942
- fsyncSync(descriptor);
1960
+ fsyncSync2(descriptor);
1943
1961
  } finally {
1944
- closeSync3(descriptor);
1962
+ closeSync4(descriptor);
1945
1963
  }
1946
- renameSync3(temporary, path);
1947
- const directory = openSync3(dirname19(path), "r");
1964
+ renameSync4(temporary, path);
1965
+ const directory = openSync4(dirname21(path), "r");
1948
1966
  try {
1949
- fsyncSync(directory);
1967
+ fsyncSync2(directory);
1950
1968
  } finally {
1951
- closeSync3(directory);
1969
+ closeSync4(directory);
1952
1970
  }
1953
1971
  }
1954
1972
  function readPrivateJson2(path) {
1955
- const descriptor = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1973
+ const descriptor = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1956
1974
  try {
1957
1975
  const stat = fstatSync2(descriptor);
1958
1976
  if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 2097152)
1959
1977
  throw new Error("unsafe_state_file");
1960
1978
  if (stat.uid !== process.getuid?.()) throw new Error("foreign_state_file");
1961
- return JSON.parse(readFileSync33(descriptor, "utf8"));
1979
+ return JSON.parse(readFileSync34(descriptor, "utf8"));
1962
1980
  } finally {
1963
- closeSync3(descriptor);
1981
+ closeSync4(descriptor);
1964
1982
  }
1965
1983
  }
1966
1984
  var GroupStore;
@@ -1973,10 +1991,10 @@ var init_store2 = __esm({
1973
1991
  path;
1974
1992
  state;
1975
1993
  constructor(path) {
1976
- this.path = realpathSync4(path);
1994
+ this.path = realpathSync6(path);
1977
1995
  this.state = validateState(readPrivateJson2(path));
1978
1996
  const metadata2 = object8(
1979
- readPrivateJson2(join27(dirname19(this.path), "harness.json"))
1997
+ readPrivateJson2(join29(dirname21(this.path), "harness.json"))
1980
1998
  );
1981
1999
  if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
1982
2000
  throw new Error("foreign_group");
@@ -1997,13 +2015,13 @@ var init_store2 = __esm({
1997
2015
 
1998
2016
  // src/harness/adapters/process/serialized.ts
1999
2017
  import { spawnSync as spawnSync4 } from "child_process";
2000
- import { randomUUID as randomUUID5 } from "crypto";
2001
- import { existsSync as existsSync22, unlinkSync as unlinkSync2 } from "fs";
2002
- import { dirname as dirname20, join as join28 } from "path";
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";
2003
2021
  function serialized(operation) {
2004
2022
  const store = new GroupStore(text(operation.groupFile, "group_file"));
2005
- const directory = dirname20(store.path);
2006
- const operationFile = join28(directory, `operation-${randomUUID5()}.json`);
2023
+ const directory = dirname22(store.path);
2024
+ const operationFile = join30(directory, `operation-${randomUUID6()}.json`);
2007
2025
  privateJson(operationFile, operation);
2008
2026
  const worker = artifact("worker");
2009
2027
  try {
@@ -2015,7 +2033,7 @@ function serialized(operation) {
2015
2033
  "--no-fork",
2016
2034
  "--conflict-exit-code",
2017
2035
  "73",
2018
- join28(directory, "group-control.lock"),
2036
+ join30(directory, "group-control.lock"),
2019
2037
  process.execPath,
2020
2038
  worker,
2021
2039
  operationFile
@@ -2031,7 +2049,7 @@ function serialized(operation) {
2031
2049
  if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
2032
2050
  return JSON.parse(result.stdout);
2033
2051
  } finally {
2034
- if (existsSync22(operationFile)) unlinkSync2(operationFile);
2052
+ if (existsSync25(operationFile)) unlinkSync2(operationFile);
2035
2053
  }
2036
2054
  }
2037
2055
  var init_serialized = __esm({
@@ -2111,11 +2129,11 @@ var init_command = __esm({
2111
2129
  });
2112
2130
 
2113
2131
  // src/harness/adapters/process/identity.ts
2114
- import { readFileSync as readFileSync34 } from "fs";
2132
+ import { readFileSync as readFileSync35 } from "fs";
2115
2133
  function processIdentity(pid) {
2116
2134
  if (!Number.isSafeInteger(pid) || pid < 1)
2117
2135
  throw new Error("invalid_process_id");
2118
- const stat = readFileSync34(`/proc/${pid}/stat`, "utf8");
2136
+ const stat = readFileSync35(`/proc/${pid}/stat`, "utf8");
2119
2137
  const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
2120
2138
  if (!/^\d+$/.test(fields[19] ?? ""))
2121
2139
  throw new Error("process_identity_unknown");
@@ -2345,19 +2363,19 @@ var init_connection = __esm({
2345
2363
  });
2346
2364
 
2347
2365
  // src/harness/adapters/tmux/records.ts
2348
- import { randomUUID as randomUUID6 } from "crypto";
2366
+ import { randomUUID as randomUUID7 } from "crypto";
2349
2367
  import {
2350
- lstatSync as lstatSync5,
2368
+ lstatSync as lstatSync6,
2351
2369
  mkdirSync as mkdirSync9,
2352
2370
  readdirSync as readdirSync10,
2353
- realpathSync as realpathSync5,
2371
+ realpathSync as realpathSync7,
2354
2372
  writeFileSync as writeFileSync10
2355
2373
  } from "fs";
2356
2374
  import { homedir as homedir7 } from "os";
2357
- import { join as join29 } from "path";
2375
+ import { join as join31 } from "path";
2358
2376
  function stateDirectory() {
2359
- return join29(
2360
- process.env.XDG_STATE_HOME ?? join29(homedir7(), ".local/state"),
2377
+ return join31(
2378
+ process.env.XDG_STATE_HOME ?? join31(homedir7(), ".local/state"),
2361
2379
  "pmcp/harness"
2362
2380
  );
2363
2381
  }
@@ -2384,15 +2402,15 @@ var init_records = __esm({
2384
2402
  configPath;
2385
2403
  owner;
2386
2404
  constructor(directory) {
2387
- const stat = lstatSync5(directory);
2405
+ const stat = lstatSync6(directory);
2388
2406
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2389
2407
  throw new Error("unsafe_group_directory");
2390
- this.directory = realpathSync5(directory);
2391
- this.socket = join29(this.directory, "s");
2408
+ this.directory = realpathSync7(directory);
2409
+ this.socket = join31(this.directory, "s");
2392
2410
  if (Buffer.byteLength(this.socket) > 100)
2393
2411
  throw new Error("socket_path_too_long");
2394
- this.metadataPath = join29(this.directory, "harness.json");
2395
- this.configPath = join29(this.directory, "tmux.conf");
2412
+ this.metadataPath = join31(this.directory, "harness.json");
2413
+ this.configPath = join31(this.directory, "tmux.conf");
2396
2414
  this.owner = text(
2397
2415
  object8(readPrivateJson2(this.metadataPath)).owner,
2398
2416
  "owner"
@@ -2402,17 +2420,17 @@ var init_records = __esm({
2402
2420
  static create() {
2403
2421
  const root = stateDirectory();
2404
2422
  mkdirSync9(root, { recursive: true, mode: 448 });
2405
- const directory = join29(root, randomUUID6().slice(0, 16));
2406
- if (Buffer.byteLength(join29(directory, "s")) > 100)
2423
+ const directory = join31(root, randomUUID7().slice(0, 16));
2424
+ if (Buffer.byteLength(join31(directory, "s")) > 100)
2407
2425
  throw new Error("socket_path_too_long");
2408
2426
  mkdirSync9(directory, { mode: 448 });
2409
- mkdirSync9(join29(directory, "sessions"), { mode: 448 });
2410
- privateJson(join29(directory, "harness.json"), {
2411
- owner: randomUUID6(),
2427
+ mkdirSync9(join31(directory, "sessions"), { mode: 448 });
2428
+ privateJson(join31(directory, "harness.json"), {
2429
+ owner: randomUUID7(),
2412
2430
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2413
2431
  });
2414
2432
  writeFileSync10(
2415
- join29(directory, "tmux.conf"),
2433
+ join31(directory, "tmux.conf"),
2416
2434
  [
2417
2435
  "set -g remain-on-exit on",
2418
2436
  "set -g history-limit 2000",
@@ -2428,7 +2446,7 @@ var init_records = __esm({
2428
2446
  }
2429
2447
  path(id2) {
2430
2448
  runtimeUnit(id2);
2431
- return join29(this.directory, "sessions", `${id2}.json`);
2449
+ return join31(this.directory, "sessions", `${id2}.json`);
2432
2450
  }
2433
2451
  read(id2) {
2434
2452
  const record = object8(readPrivateJson2(this.path(id2)));
@@ -2450,7 +2468,7 @@ var init_records = __esm({
2450
2468
  return record;
2451
2469
  }
2452
2470
  all() {
2453
- return readdirSync10(join29(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2471
+ return readdirSync10(join31(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2454
2472
  }
2455
2473
  save(record) {
2456
2474
  privateJson(this.path(record.id), record);
@@ -2532,8 +2550,8 @@ var init_launch_identity = __esm({
2532
2550
 
2533
2551
  // src/harness/launch/journal.ts
2534
2552
  import { createHash as createHash16 } from "crypto";
2535
- import { existsSync as existsSync23, readdirSync as readdirSync11 } from "fs";
2536
- import { join as join30 } from "path";
2553
+ import { existsSync as existsSync26, readdirSync as readdirSync11 } from "fs";
2554
+ import { join as join32 } from "path";
2537
2555
  function launchJournals(records2) {
2538
2556
  return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
2539
2557
  }
@@ -2541,7 +2559,7 @@ function pendingLaunches(records2) {
2541
2559
  return launchJournals(records2).filter((journal) => {
2542
2560
  const aborted = journal.abortRecord();
2543
2561
  if (aborted) return aborted.state !== "stopped";
2544
- if (!existsSync23(journal.path("committed"))) return true;
2562
+ if (!existsSync26(journal.path("committed"))) return true;
2545
2563
  journal.assertCommitted();
2546
2564
  return false;
2547
2565
  });
@@ -2568,7 +2586,7 @@ var init_journal = __esm({
2568
2586
  path(kind2) {
2569
2587
  if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
2570
2588
  throw new Error("invalid_launch_artifact");
2571
- return join30(this.records.directory, `${this.id}.${kind2}.json`);
2589
+ return join32(this.records.directory, `${this.id}.${kind2}.json`);
2572
2590
  }
2573
2591
  intent() {
2574
2592
  const value = object8(readPrivateJson2(this.path("intent")));
@@ -2597,13 +2615,13 @@ var init_journal = __esm({
2597
2615
  throw new Error("invalid_docker_bridge_digest");
2598
2616
  if (!bridge && creation.bridgeSha256 !== void 0)
2599
2617
  throw new Error("ungranted_docker_bridge_digest");
2600
- const policy = new GroupStore(join30(this.records.directory, "group.json")).state.group.dockerPolicy;
2618
+ const policy = new GroupStore(join32(this.records.directory, "group.json")).state.group.dockerPolicy;
2601
2619
  const profile = selectedProfile(
2602
2620
  policy,
2603
2621
  creation.privateProfile,
2604
2622
  text(value.runtime, "runtime")
2605
2623
  );
2606
- const expectedState = profile?.directory ?? join30(
2624
+ const expectedState = profile?.directory ?? join32(
2607
2625
  this.records.directory,
2608
2626
  "containers",
2609
2627
  text(value.nonce, "nonce"),
@@ -2649,7 +2667,7 @@ var init_journal = __esm({
2649
2667
  }
2650
2668
  assertRegistered() {
2651
2669
  const intent = this.intent();
2652
- const group = new GroupStore(join30(this.records.directory, "group.json")).state.group;
2670
+ const group = new GroupStore(join32(this.records.directory, "group.json")).state.group;
2653
2671
  if (group.id !== intent.groupId || group.generation !== intent.generation)
2654
2672
  throw new Error("launch_group_generation_mismatch");
2655
2673
  if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
@@ -2665,12 +2683,12 @@ var init_journal = __esm({
2665
2683
  throw new Error("launch_record_mismatch");
2666
2684
  }
2667
2685
  authorize() {
2668
- if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
2686
+ if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2669
2687
  this.assertRegistered();
2670
2688
  privateJson(this.path("grant"), this.grant());
2671
2689
  }
2672
2690
  assertGrant() {
2673
- if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
2691
+ if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2674
2692
  const grant = object8(readPrivateJson2(this.path("grant")));
2675
2693
  if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
2676
2694
  throw new Error("launch_grant_mismatch");
@@ -2687,7 +2705,7 @@ var init_journal = __esm({
2687
2705
  throw new Error("launch_commit_mismatch");
2688
2706
  }
2689
2707
  abortRecord() {
2690
- if (!existsSync23(this.path("aborted"))) return;
2708
+ if (!existsSync26(this.path("aborted"))) return;
2691
2709
  const value = object8(readPrivateJson2(this.path("aborted")));
2692
2710
  if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
2693
2711
  throw new Error("foreign_launch_abort");
@@ -2734,30 +2752,30 @@ var init_lifecycle = __esm({
2734
2752
  // src/harness/adapters/docker/private-profile.ts
2735
2753
  import { homedir as homedir8 } from "os";
2736
2754
  import {
2737
- closeSync as closeSync4,
2755
+ closeSync as closeSync5,
2738
2756
  constants as constants4,
2739
- existsSync as existsSync24,
2757
+ existsSync as existsSync27,
2740
2758
  fstatSync as fstatSync3,
2741
- lstatSync as lstatSync6,
2742
- openSync as openSync4,
2743
- realpathSync as realpathSync6,
2759
+ lstatSync as lstatSync7,
2760
+ openSync as openSync5,
2761
+ realpathSync as realpathSync8,
2744
2762
  statfsSync
2745
2763
  } from "fs";
2746
- import { join as join31, relative as relative11 } from "path";
2764
+ import { join as join33, relative as relative11 } from "path";
2747
2765
  function overlap(left, right) {
2748
2766
  const scope = relative11(left, right);
2749
2767
  return scope === "" || scope !== ".." && !scope.startsWith("../");
2750
2768
  }
2751
2769
  function profileIdentity(directory, projectRoot) {
2752
- if (directory === "/" || directory === realpathSync6(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync6(directory) !== directory)
2770
+ if (directory === "/" || directory === realpathSync8(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync8(directory) !== directory)
2753
2771
  throw new Error("private_profile_scope_not_admitted");
2754
- const stat = lstatSync6(directory);
2772
+ const stat = lstatSync7(directory);
2755
2773
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2756
2774
  throw new Error("unsafe_private_profile_directory");
2757
2775
  if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
2758
2776
  throw new Error("remote_private_profile_not_admitted");
2759
- const lock = openSync4(
2760
- join31(directory, privateProfileLock),
2777
+ const lock = openSync5(
2778
+ join33(directory, privateProfileLock),
2761
2779
  constants4.O_RDONLY | constants4.O_NOFOLLOW
2762
2780
  );
2763
2781
  try {
@@ -2766,18 +2784,18 @@ function profileIdentity(directory, projectRoot) {
2766
2784
  throw new Error("unsafe_private_profile_lock");
2767
2785
  return `${stat.dev}:${stat.ino}:${value.dev}:${value.ino}`;
2768
2786
  } finally {
2769
- closeSync4(lock);
2787
+ closeSync5(lock);
2770
2788
  }
2771
2789
  }
2772
2790
  function captureProfile(grant, projectRoot) {
2773
- const directory = realpathSync6(grant.directory);
2774
- if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync6(homedir8()))
2791
+ const directory = realpathSync8(grant.directory);
2792
+ if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync8(homedir8()))
2775
2793
  throw new Error("private_profile_scope_not_admitted");
2776
- const stat = lstatSync6(directory);
2794
+ const stat = lstatSync7(directory);
2777
2795
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2778
2796
  throw new Error("unsafe_private_profile_directory");
2779
- const lock = join31(directory, privateProfileLock);
2780
- if (!existsSync24(lock)) closeSync4(openSync4(lock, "wx", 384));
2797
+ const lock = join33(directory, privateProfileLock);
2798
+ if (!existsSync27(lock)) closeSync5(openSync5(lock, "wx", 384));
2781
2799
  const identity = profileIdentity(directory, projectRoot);
2782
2800
  if (grant.identity && grant.identity !== identity)
2783
2801
  throw new Error("private_profile_identity_changed");
@@ -2795,7 +2813,7 @@ function assertProfileAvailable(grant, projectRoot) {
2795
2813
  "--nonblock",
2796
2814
  "--conflict-exit-code",
2797
2815
  "73",
2798
- join31(grant.directory, privateProfileLock),
2816
+ join33(grant.directory, privateProfileLock),
2799
2817
  "true"
2800
2818
  ]);
2801
2819
  } catch (error) {
@@ -2815,7 +2833,7 @@ var init_private_profile = __esm({
2815
2833
 
2816
2834
  // src/harness/adapters/docker/profile.ts
2817
2835
  import { homedir as homedir9 } from "os";
2818
- import { lstatSync as lstatSync7, realpathSync as realpathSync7 } from "fs";
2836
+ import { lstatSync as lstatSync8, realpathSync as realpathSync9 } from "fs";
2819
2837
  import { relative as relative12 } from "path";
2820
2838
  function assertLocalDocker() {
2821
2839
  if (process.env.DOCKER_HOST)
@@ -2832,12 +2850,12 @@ function assertLocalDocker() {
2832
2850
  throw new Error("remote_docker_context_not_admitted");
2833
2851
  }
2834
2852
  function loadDockerPolicy(path, projectRoot, runtimes) {
2835
- if (projectRoot === "/" || projectRoot === realpathSync7(homedir9()))
2853
+ if (projectRoot === "/" || projectRoot === realpathSync9(homedir9()))
2836
2854
  throw new Error("docker_project_root_too_broad");
2837
2855
  const inputPath = text(path, "docker_policy_file");
2838
- if (lstatSync7(inputPath).isSymbolicLink())
2856
+ if (lstatSync8(inputPath).isSymbolicLink())
2839
2857
  throw new Error("symlink_docker_policy_not_admitted");
2840
- const source2 = realpathSync7(inputPath);
2858
+ const source2 = realpathSync9(inputPath);
2841
2859
  const scope = relative12(projectRoot, source2);
2842
2860
  if (scope === ".." || scope.startsWith("../"))
2843
2861
  throw new Error("docker_policy_outside_project");
@@ -2871,16 +2889,16 @@ var init_profile = __esm({
2871
2889
  });
2872
2890
 
2873
2891
  // src/harness/adapters/docker/mounts.ts
2874
- import { lstatSync as lstatSync8, realpathSync as realpathSync8 } from "fs";
2875
- import { join as join32 } from "path";
2892
+ import { lstatSync as lstatSync9, realpathSync as realpathSync10 } from "fs";
2893
+ import { join as join34 } from "path";
2876
2894
  function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
2877
2895
  const state = text(statePath, "container_state_directory");
2878
2896
  const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
2879
2897
  if (profile) assertProfileIdentity(profile, projectRoot);
2880
- const expected = profile?.directory ?? join32(directory, "containers", nonce, "profile");
2881
- if (state !== expected || realpathSync8(state) !== expected)
2898
+ const expected = profile?.directory ?? join34(directory, "containers", nonce, "profile");
2899
+ if (state !== expected || realpathSync10(state) !== expected)
2882
2900
  throw new Error("foreign_container_state_directory");
2883
- const stat = lstatSync8(state);
2901
+ const stat = lstatSync9(state);
2884
2902
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2885
2903
  throw new Error("unsafe_container_state_directory");
2886
2904
  if (!Array.isArray(actual.Mounts) || actual.Mounts.length !== 2)
@@ -2972,17 +2990,17 @@ var init_entrypoint = __esm({
2972
2990
  });
2973
2991
 
2974
2992
  // src/harness/adapters/docker/admission.ts
2975
- import { lstatSync as lstatSync9, realpathSync as realpathSync9 } from "fs";
2976
- import { join as join33, relative as relative13 } from "path";
2993
+ import { lstatSync as lstatSync10, realpathSync as realpathSync11 } from "fs";
2994
+ import { join as join35, relative as relative13 } from "path";
2977
2995
  function admitResource(path, group, role, directory, memoryMb, runtime2) {
2978
2996
  if (group.profile !== "docker" || !group.dockerPolicy)
2979
2997
  throw new Error("container_requires_docker_profile");
2980
2998
  const { projectRoot } = group;
2981
2999
  assertLocalDocker();
2982
3000
  const inputPath = text(path, "container_file");
2983
- if (lstatSync9(inputPath).isSymbolicLink())
3001
+ if (lstatSync10(inputPath).isSymbolicLink())
2984
3002
  throw new Error("symlink_container_file_not_admitted");
2985
- const source2 = realpathSync9(inputPath);
3003
+ const source2 = realpathSync11(inputPath);
2986
3004
  const scope = relative13(projectRoot, source2);
2987
3005
  if (scope === ".." || scope.startsWith("../"))
2988
3006
  throw new Error("container_file_outside_project");
@@ -3029,7 +3047,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
3029
3047
  )
3030
3048
  ))
3031
3049
  throw new Error("container_runtime_image_not_granted");
3032
- if (config.WorkingDir !== join33("/workspace", relative13(projectRoot, cwd)))
3050
+ if (config.WorkingDir !== join35("/workspace", relative13(projectRoot, cwd)))
3033
3051
  throw new Error("container_cwd_not_granted");
3034
3052
  assertContainerMounts(
3035
3053
  actual,
@@ -3165,20 +3183,20 @@ var init_engine = __esm({
3165
3183
  // src/harness/adapters/docker/bridge-artifact.ts
3166
3184
  import { createHash as createHash17 } from "crypto";
3167
3185
  import {
3168
- closeSync as closeSync5,
3186
+ closeSync as closeSync6,
3169
3187
  constants as constants5,
3170
3188
  fstatSync as fstatSync4,
3171
- fsyncSync as fsyncSync2,
3172
- openSync as openSync5,
3173
- readFileSync as readFileSync35,
3189
+ fsyncSync as fsyncSync3,
3190
+ openSync as openSync6,
3191
+ readFileSync as readFileSync36,
3174
3192
  writeFileSync as writeFileSync11
3175
3193
  } from "fs";
3176
- import { join as join34 } from "path";
3194
+ import { join as join36 } from "path";
3177
3195
  function source() {
3178
3196
  const path = artifact("egress-bridge");
3179
3197
  if (!path.endsWith(".js"))
3180
3198
  throw new Error("docker_bridge_requires_compiled_cli");
3181
- return readFileSync35(path);
3199
+ return readFileSync36(path);
3182
3200
  }
3183
3201
  function digest(value) {
3184
3202
  return createHash17("sha256").update(value).digest("hex");
@@ -3191,30 +3209,30 @@ function prepareBridge(creation) {
3191
3209
  const content = source();
3192
3210
  if (digest(content) !== creation.bridgeSha256)
3193
3211
  throw new Error("docker_bridge_source_changed");
3194
- const descriptor = openSync5(
3195
- join34(creation.state, bridgeName(creation.nonce)),
3212
+ const descriptor = openSync6(
3213
+ join36(creation.state, bridgeName(creation.nonce)),
3196
3214
  "wx",
3197
3215
  384
3198
3216
  );
3199
3217
  try {
3200
3218
  writeFileSync11(descriptor, content);
3201
- fsyncSync2(descriptor);
3219
+ fsyncSync3(descriptor);
3202
3220
  } finally {
3203
- closeSync5(descriptor);
3221
+ closeSync6(descriptor);
3204
3222
  }
3205
3223
  }
3206
3224
  function assertBridge(creation) {
3207
3225
  if (!creation.egress?.bridgeExecutable) return;
3208
- const descriptor = openSync5(
3209
- join34(creation.state, bridgeName(creation.nonce)),
3226
+ const descriptor = openSync6(
3227
+ join36(creation.state, bridgeName(creation.nonce)),
3210
3228
  constants5.O_RDONLY | constants5.O_NOFOLLOW
3211
3229
  );
3212
3230
  try {
3213
3231
  const stat = fstatSync4(descriptor);
3214
- if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(readFileSync35(descriptor)) !== creation.bridgeSha256)
3232
+ if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(readFileSync36(descriptor)) !== creation.bridgeSha256)
3215
3233
  throw new Error("docker_bridge_artifact_changed");
3216
3234
  } finally {
3217
- closeSync5(descriptor);
3235
+ closeSync6(descriptor);
3218
3236
  }
3219
3237
  }
3220
3238
  var init_bridge_artifact = __esm({
@@ -3226,7 +3244,7 @@ var init_bridge_artifact = __esm({
3226
3244
  });
3227
3245
 
3228
3246
  // src/harness/adapters/docker/creation-plan.ts
3229
- import { join as join35, relative as relative14 } from "path";
3247
+ import { join as join37, relative as relative14 } from "path";
3230
3248
  function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
3231
3249
  if (group.profile !== "docker" || !group.dockerPolicy)
3232
3250
  throw new Error("container_requires_docker_profile");
@@ -3243,7 +3261,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3243
3261
  throw new Error("invalid_container_image_environment");
3244
3262
  const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
3245
3263
  if (profile) assertProfileAvailable(profile, group.projectRoot);
3246
- const state = profile?.directory ?? join35(directory, "containers", nonce, "profile");
3264
+ const state = profile?.directory ?? join37(directory, "containers", nonce, "profile");
3247
3265
  return {
3248
3266
  name: containerName(group.owner, id2),
3249
3267
  nonce,
@@ -3260,7 +3278,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3260
3278
  preparation.privateProfile
3261
3279
  ),
3262
3280
  Cmd: preparation.args.length ? preparation.args : null,
3263
- WorkingDir: join35("/workspace", relative14(group.projectRoot, cwd)),
3281
+ WorkingDir: join37("/workspace", relative14(group.projectRoot, cwd)),
3264
3282
  User: `${process.getuid?.()}:${process.getgid?.()}`,
3265
3283
  Env: [
3266
3284
  ...environment2.filter(
@@ -3359,12 +3377,12 @@ var init_creation_plan = __esm({
3359
3377
 
3360
3378
  // src/harness/adapters/docker/creation.ts
3361
3379
  import { mkdirSync as mkdirSync10 } from "fs";
3362
- import { join as join36 } from "path";
3380
+ import { join as join38 } from "path";
3363
3381
  async function prepareCreation(journal) {
3364
3382
  const intent = journal.intent();
3365
3383
  const creation = intent.dockerCreation;
3366
3384
  if (!creation) throw new Error("container_creation_missing");
3367
- const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3385
+ const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3368
3386
  const profile = selectedProfile(
3369
3387
  group.dockerPolicy,
3370
3388
  creation.privateProfile,
@@ -3378,7 +3396,7 @@ async function prepareCreation(journal) {
3378
3396
  creation.name,
3379
3397
  creation.configuration
3380
3398
  );
3381
- const current = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3399
+ const current = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3382
3400
  if (current.id !== intent.groupId || current.generation !== intent.generation)
3383
3401
  throw new Error("launch_group_generation_mismatch");
3384
3402
  const resource = {
@@ -3446,7 +3464,7 @@ function findCreation(journal) {
3446
3464
  assertResourceOwner(resource, actual);
3447
3465
  assertCreationConfiguration(actual, creation);
3448
3466
  if (!intent.resource) {
3449
- const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3467
+ const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3450
3468
  return {
3451
3469
  ...admitClaim(
3452
3470
  { ...resource, runtime: intent.runtime, state: creation.state },
@@ -3483,7 +3501,7 @@ var init_creation = __esm({
3483
3501
  });
3484
3502
 
3485
3503
  // src/harness/launch/recovery.ts
3486
- import { existsSync as existsSync25 } from "fs";
3504
+ import { existsSync as existsSync28 } from "fs";
3487
3505
  function abortLaunch(connection, journal) {
3488
3506
  const intent = journal.intent();
3489
3507
  if (!controllerExited(intent.controllerIdentity))
@@ -3500,7 +3518,7 @@ function abortLaunch(connection, journal) {
3500
3518
  inputReplayed: false
3501
3519
  };
3502
3520
  }
3503
- if (!existsSync25(journal.path("proof"))) {
3521
+ if (!existsSync28(journal.path("proof"))) {
3504
3522
  const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
3505
3523
  if (paneUnknown || unitActive(intent.unit))
3506
3524
  throw new Error("launch_identity_unknown");
@@ -3527,7 +3545,7 @@ function abortLaunch(connection, journal) {
3527
3545
  assertProof(connection, proof);
3528
3546
  connection.tmux(["kill-pane", "-t", proof.session.paneId]);
3529
3547
  }
3530
- if (existsSync25(journal.records.path(intent.id)))
3548
+ if (existsSync28(journal.records.path(intent.id)))
3531
3549
  journal.records.save({
3532
3550
  ...proof.session,
3533
3551
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3553,10 +3571,10 @@ var init_recovery = __esm({
3553
3571
  });
3554
3572
 
3555
3573
  // src/harness/adapters/tmux/lifecycle.ts
3556
- import { randomUUID as randomUUID7 } from "crypto";
3557
- import { existsSync as existsSync26 } from "fs";
3574
+ import { randomUUID as randomUUID8 } from "crypto";
3575
+ import { existsSync as existsSync29 } from "fs";
3558
3576
  import { setTimeout as setTimeout2 } from "timers/promises";
3559
- import { join as join37 } from "path";
3577
+ import { join as join39 } from "path";
3560
3578
  async function start(connection, spec) {
3561
3579
  const records2 = connection.records;
3562
3580
  if (spec.budgetSlice !== budgetSlice(records2.owner))
@@ -3566,11 +3584,11 @@ async function start(connection, spec) {
3566
3584
  runtimeUnit(spec.generation);
3567
3585
  if (spec.role !== "main" && spec.role !== "worker")
3568
3586
  throw new Error("invalid_launch_role");
3569
- const id2 = randomUUID7();
3570
- const nonce = randomUUID7();
3587
+ const id2 = randomUUID8();
3588
+ const nonce = randomUUID8();
3571
3589
  const journal = new LaunchJournal(records2, id2);
3572
3590
  const dockerCreation = spec.dockerPreparation ? creationPlan(
3573
- new GroupStore(join37(records2.directory, "group.json")).state.group,
3591
+ new GroupStore(join39(records2.directory, "group.json")).state.group,
3574
3592
  records2.directory,
3575
3593
  id2,
3576
3594
  nonce,
@@ -3630,7 +3648,7 @@ async function start(connection, spec) {
3630
3648
  `exec ${argv.map(quote2).join(" ")}`
3631
3649
  ]);
3632
3650
  const deadline = Date.now() + 5e3;
3633
- while (!existsSync26(journal.path("proof"))) {
3651
+ while (!existsSync29(journal.path("proof"))) {
3634
3652
  if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
3635
3653
  await setTimeout2(25);
3636
3654
  }
@@ -3641,7 +3659,7 @@ async function start(connection, spec) {
3641
3659
  }
3642
3660
  function stop(connection, id2) {
3643
3661
  const journal = new LaunchJournal(connection.records, id2);
3644
- if (existsSync26(journal.path("intent"))) {
3662
+ if (existsSync29(journal.path("intent"))) {
3645
3663
  abortLaunch(connection, journal);
3646
3664
  return;
3647
3665
  }
@@ -3668,7 +3686,7 @@ function cleanup(connection) {
3668
3686
  }
3669
3687
  for (const record of records2) {
3670
3688
  if (record.resource) stopResource(record.resource);
3671
- if (record.stoppedAt || !existsSync26(
3689
+ if (record.stoppedAt || !existsSync29(
3672
3690
  new LaunchJournal(connection.records, record.id).path("intent")
3673
3691
  )) {
3674
3692
  stopUnit(record.unit);
@@ -3698,9 +3716,9 @@ var init_lifecycle2 = __esm({
3698
3716
  });
3699
3717
 
3700
3718
  // src/harness/adapters/tmux/input.ts
3701
- import { randomUUID as randomUUID8 } from "crypto";
3702
- import { closeSync as closeSync6, openSync as openSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
3703
- import { join as join38 } from "path";
3719
+ import { randomUUID as randomUUID9 } from "crypto";
3720
+ import { closeSync as closeSync7, openSync as openSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
3721
+ import { join as join40 } from "path";
3704
3722
  function validateText(text3) {
3705
3723
  if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
3706
3724
  throw new Error("invalid_text");
@@ -3731,9 +3749,9 @@ function write(connection, id2, text3, submit) {
3731
3749
  validateText(text3);
3732
3750
  const session = connection.inspect(id2);
3733
3751
  if (!session.alive) throw new Error("runtime_exited");
3734
- const lock = join38(connection.records.directory, `${id2}.input.lock`);
3735
- const descriptor = openSync6(lock, "wx", 384);
3736
- const buffer = `pmcp-${randomUUID8()}`;
3752
+ const lock = join40(connection.records.directory, `${id2}.input.lock`);
3753
+ const descriptor = openSync7(lock, "wx", 384);
3754
+ const buffer = `pmcp-${randomUUID9()}`;
3737
3755
  try {
3738
3756
  writeFileSync12(
3739
3757
  descriptor,
@@ -3773,7 +3791,7 @@ function write(connection, id2, text3, submit) {
3773
3791
  connection.tmux(["delete-buffer", "-b", buffer]);
3774
3792
  } catch {
3775
3793
  }
3776
- closeSync6(descriptor);
3794
+ closeSync7(descriptor);
3777
3795
  unlinkSync3(lock);
3778
3796
  }
3779
3797
  }
@@ -3890,10 +3908,10 @@ var init_backend = __esm({
3890
3908
  });
3891
3909
 
3892
3910
  // src/harness/adapters/systemd/budget.ts
3893
- import { join as join39 } from "path";
3911
+ import { join as join41 } from "path";
3894
3912
  async function budgetOperation(directory, operation) {
3895
3913
  const backend = openBackend(directory);
3896
- const path = join39(directory, "runtime-policy.json");
3914
+ const path = join41(directory, "runtime-policy.json");
3897
3915
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
3898
3916
  const slice = budgetSlice(backend.owner);
3899
3917
  if (operation.action === "configure") {
@@ -3994,7 +4012,7 @@ var init_budget = __esm({
3994
4012
 
3995
4013
  // src/harness/adapters/systemd/configure.ts
3996
4014
  import { execFileSync as execFileSync3 } from "child_process";
3997
- import { join as join40 } from "path";
4015
+ import { join as join42 } from "path";
3998
4016
  function configureBudget(context, input) {
3999
4017
  const { store, backend } = context;
4000
4018
  assertOwnedRecords(backend);
@@ -4016,7 +4034,7 @@ function configureBudget(context, input) {
4016
4034
  throw new Error("capacity_below_active_sessions");
4017
4035
  if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
4018
4036
  throw new Error("budget_below_reserved_memory");
4019
- const path = join40(backend.runDirectory, "runtime-policy.json");
4037
+ const path = join42(backend.runDirectory, "runtime-policy.json");
4020
4038
  const policy = object8(readPrivateJson2(path));
4021
4039
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
4022
4040
  const slice = budgetSlice(backend.owner);
@@ -4054,11 +4072,11 @@ var init_configure = __esm({
4054
4072
 
4055
4073
  // src/harness/adapters/process/executable.ts
4056
4074
  import { accessSync, constants as constants6, statSync as statSync16 } from "fs";
4057
- import { delimiter, resolve as resolve19 } from "path";
4075
+ import { delimiter, resolve as resolve18 } from "path";
4058
4076
  function locateExecutable(binary, cwd = process.cwd()) {
4059
4077
  for (const directory of (process.env.PATH ?? "").split(delimiter)) {
4060
4078
  if (!directory) continue;
4061
- const path = resolve19(cwd, directory, binary);
4079
+ const path = resolve18(cwd, directory, binary);
4062
4080
  try {
4063
4081
  if (!statSync16(path).isFile()) continue;
4064
4082
  accessSync(path, constants6.X_OK);
@@ -4110,11 +4128,11 @@ var init_admission2 = __esm({
4110
4128
  });
4111
4129
 
4112
4130
  // src/harness/groups/service.ts
4113
- import { randomUUID as randomUUID9 } from "crypto";
4114
- import { realpathSync as realpathSync10, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
4115
- import { join as join41 } from "path";
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";
4116
4134
  async function createGroup(input) {
4117
- const projectRoot = realpathSync10(text(input.projectRoot, "project_root"));
4135
+ const projectRoot = realpathSync12(text(input.projectRoot, "project_root"));
4118
4136
  if (!statSync17(projectRoot).isDirectory())
4119
4137
  throw new Error("invalid_project_root");
4120
4138
  const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
@@ -4134,8 +4152,8 @@ async function createGroup(input) {
4134
4152
  const group = {
4135
4153
  schemaVersion: 1,
4136
4154
  owner: backend.owner,
4137
- id: randomUUID9(),
4138
- generation: randomUUID9(),
4155
+ id: randomUUID10(),
4156
+ generation: randomUUID10(),
4139
4157
  projectRoot,
4140
4158
  allowedRuntimes,
4141
4159
  profile,
@@ -4145,7 +4163,7 @@ async function createGroup(input) {
4145
4163
  sessions: [],
4146
4164
  ...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
4147
4165
  };
4148
- const groupFile = join41(backend.runDirectory, "group.json");
4166
+ const groupFile = join43(backend.runDirectory, "group.json");
4149
4167
  privateJson(groupFile, { group, leases: [], deliveries: [] });
4150
4168
  try {
4151
4169
  await budgetOperation(backend.runDirectory, {
@@ -4158,7 +4176,7 @@ async function createGroup(input) {
4158
4176
  await budgetOperation(backend.runDirectory, { action: "cleanup" });
4159
4177
  unlinkSync4(groupFile);
4160
4178
  } catch (cleanupError) {
4161
- privateJson(join41(backend.runDirectory, "initialization-failure.json"), {
4179
+ privateJson(join43(backend.runDirectory, "initialization-failure.json"), {
4162
4180
  error: String(error),
4163
4181
  cleanupError: String(cleanupError),
4164
4182
  recoveryRequired: true
@@ -4220,16 +4238,16 @@ var init_service2 = __esm({
4220
4238
  });
4221
4239
 
4222
4240
  // src/harness/groups/catalog.ts
4223
- import { existsSync as existsSync27, readdirSync as readdirSync12 } from "fs";
4224
- import { join as join42 } from "path";
4241
+ import { existsSync as existsSync30, readdirSync as readdirSync12 } from "fs";
4242
+ import { join as join44 } from "path";
4225
4243
  function listGroups() {
4226
4244
  const directory = stateDirectory();
4227
- if (!existsSync27(directory)) return { groups: [] };
4245
+ if (!existsSync30(directory)) return { groups: [] };
4228
4246
  const groups2 = [];
4229
4247
  for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
4230
4248
  if (!entry2.isDirectory()) continue;
4231
- const path = join42(directory, entry2.name, "group.json");
4232
- if (!existsSync27(path)) continue;
4249
+ const path = join44(directory, entry2.name, "group.json");
4250
+ if (!existsSync30(path)) continue;
4233
4251
  try {
4234
4252
  const store = new GroupStore(path);
4235
4253
  groups2.push({ groupFile: store.path, group: store.state.group });
@@ -4288,10 +4306,10 @@ var init_service3 = __esm({
4288
4306
  });
4289
4307
 
4290
4308
  // src/harness/application/context.ts
4291
- import { dirname as dirname21 } from "path";
4309
+ import { dirname as dirname23 } from "path";
4292
4310
  function openGroup(path) {
4293
4311
  const store = new GroupStore(path);
4294
- const backend = openBackend(dirname21(store.path));
4312
+ const backend = openBackend(dirname23(store.path));
4295
4313
  if (store.state.group.owner !== backend.owner)
4296
4314
  throw new Error("foreign_group");
4297
4315
  return { store, backend };
@@ -4425,8 +4443,8 @@ var init_watch = __esm({
4425
4443
  });
4426
4444
 
4427
4445
  // src/harness/control/leases.ts
4428
- import { randomUUID as randomUUID10 } from "crypto";
4429
- import { join as join43 } from "path";
4446
+ import { randomUUID as randomUUID11 } from "crypto";
4447
+ import { join as join45 } from "path";
4430
4448
  function resolveLease(context, file2, allowExpired = false) {
4431
4449
  const credential2 = object8(readPrivateJson2(file2));
4432
4450
  const lease = context.store.state.leases.find(
@@ -4458,7 +4476,7 @@ function acquireControl(context, sessionId, controller, mode) {
4458
4476
  if (mode !== "cli" && mode !== "native")
4459
4477
  throw new Error("invalid_control_mode");
4460
4478
  const lease = {
4461
- token: randomUUID10(),
4479
+ token: randomUUID11(),
4462
4480
  sessionId,
4463
4481
  generation: store.state.group.generation,
4464
4482
  controller: id(controller, "controller"),
@@ -4470,7 +4488,7 @@ function acquireControl(context, sessionId, controller, mode) {
4470
4488
  );
4471
4489
  store.state.leases.push(lease);
4472
4490
  store.save();
4473
- const leaseFile = join43(backend.runDirectory, `lease-${randomUUID10()}.json`);
4491
+ const leaseFile = join45(backend.runDirectory, `lease-${randomUUID11()}.json`);
4474
4492
  privateJson(leaseFile, { token: lease.token });
4475
4493
  return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
4476
4494
  }
@@ -4617,18 +4635,18 @@ var init_service5 = __esm({
4617
4635
  });
4618
4636
 
4619
4637
  // src/harness/adapters/process/recover-lock.ts
4620
- import { existsSync as existsSync28, lstatSync as lstatSync10, unlinkSync as unlinkSync5 } from "fs";
4621
- import { join as join44 } from "path";
4638
+ import { existsSync as existsSync31, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
4639
+ import { join as join46 } from "path";
4622
4640
  function recoverInputLock(backend, sessionId) {
4623
4641
  backend.inspect(sessionId);
4624
- const path = join44(backend.runDirectory, `${sessionId}.input.lock`);
4625
- if (!existsSync28(path)) return;
4626
- const before = lstatSync10(path);
4642
+ const path = join46(backend.runDirectory, `${sessionId}.input.lock`);
4643
+ if (!existsSync31(path)) return;
4644
+ const before = lstatSync11(path);
4627
4645
  const record = object8(readPrivateJson2(path));
4628
4646
  if (record.id !== sessionId) throw new Error("foreign_writer_lock");
4629
4647
  if (!controllerExited(text(record.controller, "controller_identity")))
4630
4648
  throw new Error("writer_still_alive");
4631
- if (lstatSync10(path).ino !== before.ino)
4649
+ if (lstatSync11(path).ino !== before.ino)
4632
4650
  throw new Error("writer_lock_changed");
4633
4651
  unlinkSync5(path);
4634
4652
  }
@@ -4642,7 +4660,7 @@ var init_recover_lock = __esm({
4642
4660
  });
4643
4661
 
4644
4662
  // src/harness/launch/service.ts
4645
- import { existsSync as existsSync29 } from "fs";
4663
+ import { existsSync as existsSync32 } from "fs";
4646
4664
  function assertLaunchAdmission(records2) {
4647
4665
  for (const journal of launchJournals(records2)) {
4648
4666
  if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
@@ -4667,12 +4685,12 @@ function inspectPendingStarts(context) {
4667
4685
  role: journal.intent().role,
4668
4686
  runtime: journal.intent().runtime,
4669
4687
  controllerExited: controllerExited(journal.intent().controllerIdentity),
4670
- proofPresent: existsSync29(journal.path("proof")),
4671
- recordPresent: existsSync29(journal.records.path(journal.id)),
4688
+ proofPresent: existsSync32(journal.path("proof")),
4689
+ recordPresent: existsSync32(journal.records.path(journal.id)),
4672
4690
  registered: context.store.state.group.sessions.some(
4673
4691
  (session) => session.id === journal.id
4674
4692
  ),
4675
- authorizationPresent: existsSync29(journal.path("grant")),
4693
+ authorizationPresent: existsSync32(journal.path("grant")),
4676
4694
  processingConfirmed: false,
4677
4695
  inputReplayed: false
4678
4696
  }));
@@ -4690,7 +4708,7 @@ var init_service6 = __esm({
4690
4708
  });
4691
4709
 
4692
4710
  // src/harness/sessions/service.ts
4693
- import { realpathSync as realpathSync11 } from "fs";
4711
+ import { realpathSync as realpathSync13 } from "fs";
4694
4712
  import { relative as relative15 } from "path";
4695
4713
  async function startSession(context, input) {
4696
4714
  const { store, backend } = context;
@@ -4724,7 +4742,7 @@ async function startSession(context, input) {
4724
4742
  );
4725
4743
  if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
4726
4744
  }
4727
- const cwd = realpathSync11(text(input.cwd ?? group.projectRoot, "cwd"));
4745
+ const cwd = realpathSync13(text(input.cwd ?? group.projectRoot, "cwd"));
4728
4746
  const scope = relative15(group.projectRoot, cwd);
4729
4747
  if (scope === ".." || scope.startsWith("../"))
4730
4748
  throw new Error("cwd_outside_project");
@@ -4832,7 +4850,7 @@ var init_service7 = __esm({
4832
4850
  });
4833
4851
 
4834
4852
  // src/harness/recovery/service.ts
4835
- import { randomUUID as randomUUID11 } from "crypto";
4853
+ import { randomUUID as randomUUID12 } from "crypto";
4836
4854
  function inspectRecovery(context, includeStarts = false) {
4837
4855
  const pendingStarts = includeStarts ? inspectPendingStarts(context) : [];
4838
4856
  return {
@@ -4915,7 +4933,7 @@ async function replaceMain(context, input) {
4915
4933
  if (observation.alive) throw new Error("main_still_alive");
4916
4934
  previous.stopped = true;
4917
4935
  }
4918
- store.state.group.generation = randomUUID11();
4936
+ store.state.group.generation = randomUUID12();
4919
4937
  store.state.leases = [];
4920
4938
  store.save();
4921
4939
  const result = await startSession(context, { ...input, role: "main" });
@@ -5187,9 +5205,9 @@ var init_invoke = __esm({
5187
5205
  });
5188
5206
 
5189
5207
  // src/tui/adapters/process/runtime.ts
5190
- import { dirname as dirname22, join as join45 } from "path";
5208
+ import { dirname as dirname24, join as join47 } from "path";
5191
5209
  import { fileURLToPath as fileURLToPath4 } from "url";
5192
- import { existsSync as existsSync30, realpathSync as realpathSync12 } from "fs";
5210
+ import { existsSync as existsSync33, realpathSync as realpathSync14 } from "fs";
5193
5211
  function dependencies() {
5194
5212
  const tmux = locateExecutable("tmux");
5195
5213
  const flock = locateExecutable("flock");
@@ -5222,10 +5240,10 @@ function runtime() {
5222
5240
  if (!available.supported || !available.node)
5223
5241
  throw new Error("tui_dependencies_unavailable");
5224
5242
  const current = fileURLToPath4(import.meta.url);
5225
- const directory = dirname22(current);
5226
- const bundled = join45(directory, "tui-worker.js");
5227
- if (!existsSync30(bundled)) throw new Error("tui_build_required");
5228
- return { node: realpathSync12(available.node), worker: bundled };
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 };
5229
5247
  }
5230
5248
  var init_runtime2 = __esm({
5231
5249
  "src/tui/adapters/process/runtime.ts"() {
@@ -5237,34 +5255,34 @@ var init_runtime2 = __esm({
5237
5255
  });
5238
5256
 
5239
5257
  // src/tui/state/store.ts
5240
- import { randomUUID as randomUUID12 } from "crypto";
5258
+ import { randomUUID as randomUUID13 } from "crypto";
5241
5259
  import {
5242
- existsSync as existsSync31,
5243
- lstatSync as lstatSync11,
5260
+ existsSync as existsSync34,
5261
+ lstatSync as lstatSync12,
5244
5262
  mkdirSync as mkdirSync11,
5245
- realpathSync as realpathSync13,
5263
+ realpathSync as realpathSync15,
5246
5264
  unlinkSync as unlinkSync6,
5247
5265
  writeFileSync as writeFileSync13
5248
5266
  } from "fs";
5249
5267
  import { homedir as homedir10 } from "os";
5250
- import { dirname as dirname23, join as join46 } from "path";
5268
+ import { dirname as dirname25, join as join48 } from "path";
5251
5269
  function createDirectory() {
5252
- const root = join46(
5253
- process.env.XDG_STATE_HOME ?? join46(homedir10(), ".local/state"),
5270
+ const root = join48(
5271
+ process.env.XDG_STATE_HOME ?? join48(homedir10(), ".local/state"),
5254
5272
  "pmcp/tui"
5255
5273
  );
5256
5274
  mkdirSync11(root, { recursive: true, mode: 448 });
5257
- const directory = join46(realpathSync13(root), randomUUID12().slice(0, 16));
5258
- if (Buffer.byteLength(join46(directory, "s")) > 100)
5275
+ const directory = join48(realpathSync15(root), randomUUID13().slice(0, 16));
5276
+ if (Buffer.byteLength(join48(directory, "s")) > 100)
5259
5277
  throw new Error("socket_path_too_long");
5260
5278
  mkdirSync11(directory, { mode: 448 });
5261
- const owner = randomUUID12();
5262
- privateJson(join46(directory, "owner.json"), {
5279
+ const owner = randomUUID13();
5280
+ privateJson(join48(directory, "owner.json"), {
5263
5281
  owner,
5264
5282
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5265
5283
  });
5266
5284
  writeFileSync13(
5267
- join46(directory, "tmux.conf"),
5285
+ join48(directory, "tmux.conf"),
5268
5286
  [
5269
5287
  "set -g remain-on-exit on",
5270
5288
  "set -g history-limit 2000",
@@ -5279,13 +5297,13 @@ function createDirectory() {
5279
5297
  return { directory, owner };
5280
5298
  }
5281
5299
  function readView(path) {
5282
- const file2 = realpathSync13(path);
5283
- const directory = dirname23(file2);
5284
- const stat = lstatSync11(directory);
5300
+ const file2 = realpathSync15(path);
5301
+ const directory = dirname25(file2);
5302
+ const stat = lstatSync12(directory);
5285
5303
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
5286
5304
  throw new Error("unsafe_view_directory");
5287
5305
  const value = object8(readPrivateJson2(file2));
5288
- const metadata2 = object8(readPrivateJson2(join46(directory, "owner.json")));
5306
+ const metadata2 = object8(readPrivateJson2(join48(directory, "owner.json")));
5289
5307
  if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
5290
5308
  throw new Error("foreign_view_owner");
5291
5309
  for (const key of [
@@ -5322,8 +5340,8 @@ function readView(path) {
5322
5340
  throw new Error("invalid_view_identity");
5323
5341
  }
5324
5342
  const view = value;
5325
- const fallback = join46(directory, "callback-failure.json");
5326
- if (existsSync31(fallback)) {
5343
+ const fallback = join48(directory, "callback-failure.json");
5344
+ if (existsSync34(fallback)) {
5327
5345
  const recorded = object8(readPrivateJson2(fallback));
5328
5346
  const failure2 = object8(recorded.failure);
5329
5347
  if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
@@ -5334,8 +5352,8 @@ function readView(path) {
5334
5352
  return { file: file2, view };
5335
5353
  }
5336
5354
  function dismissFailure(file2, view) {
5337
- const fallback = join46(view.directory, "callback-failure.json");
5338
- if (existsSync31(fallback)) unlinkSync6(fallback);
5355
+ const fallback = join48(view.directory, "callback-failure.json");
5356
+ if (existsSync34(fallback)) unlinkSync6(fallback);
5339
5357
  delete view.failure;
5340
5358
  saveView(file2, view);
5341
5359
  }
@@ -5397,7 +5415,7 @@ var init_lock = __esm({
5397
5415
  });
5398
5416
 
5399
5417
  // src/tui/adapters/process/budget.ts
5400
- import { readFileSync as readFileSync36 } from "fs";
5418
+ import { readFileSync as readFileSync37 } from "fs";
5401
5419
  function viewUnit(owner) {
5402
5420
  if (!/^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(owner))
5403
5421
  throw new Error("invalid_view_owner");
@@ -5425,7 +5443,7 @@ function startPresentation(owner, args, env) {
5425
5443
  );
5426
5444
  }
5427
5445
  function assertPresentationBudget(owner, pid) {
5428
- if (!readFileSync36(`/proc/${pid}/cgroup`, "utf8").includes(
5446
+ if (!readFileSync37(`/proc/${pid}/cgroup`, "utf8").includes(
5429
5447
  `/${viewUnit(owner)}
5430
5448
  `
5431
5449
  ))
@@ -5440,7 +5458,7 @@ var init_budget2 = __esm({
5440
5458
 
5441
5459
  // src/tui/adapters/tmux/connection.ts
5442
5460
  import { spawn as spawn3 } from "child_process";
5443
- import { join as join47 } from "path";
5461
+ import { join as join49 } from "path";
5444
5462
  var ViewConnection;
5445
5463
  var init_connection2 = __esm({
5446
5464
  "src/tui/adapters/tmux/connection.ts"() {
@@ -5453,7 +5471,7 @@ var init_connection2 = __esm({
5453
5471
  this.directory = directory;
5454
5472
  this.owner = owner;
5455
5473
  this.identity = identity;
5456
- this.socket = join47(directory, "s");
5474
+ this.socket = join49(directory, "s");
5457
5475
  }
5458
5476
  directory;
5459
5477
  owner;
@@ -5466,14 +5484,14 @@ var init_connection2 = __esm({
5466
5484
  "-S",
5467
5485
  this.socket,
5468
5486
  "-f",
5469
- join47(this.directory, "tmux.conf"),
5487
+ join49(this.directory, "tmux.conf"),
5470
5488
  ...args
5471
5489
  ];
5472
5490
  if (args[0] === "new-session" && !this.identity)
5473
5491
  return startPresentation(this.owner, options2, env).trim();
5474
5492
  return command(
5475
5493
  "tmux",
5476
- ["-S", this.socket, "-f", join47(this.directory, "tmux.conf"), ...args],
5494
+ ["-S", this.socket, "-f", join49(this.directory, "tmux.conf"), ...args],
5477
5495
  { env }
5478
5496
  ).trim();
5479
5497
  }
@@ -5537,7 +5555,7 @@ var init_connection2 = __esm({
5537
5555
  });
5538
5556
 
5539
5557
  // src/tui/application/feedback.ts
5540
- import { join as join48 } from "path";
5558
+ import { join as join50 } from "path";
5541
5559
  function describeFailure(action, error) {
5542
5560
  const message = error instanceof Error ? error.message : "view_action_failed";
5543
5561
  const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
@@ -5574,6 +5592,13 @@ var init_feedback = __esm({
5574
5592
  init_store3();
5575
5593
  init_store2();
5576
5594
  hints = {
5595
+ startup_requires_recovery: "Inspect the saved group with pmcp tui plan and pmcp harness recovery inspect. No runtime was restarted.",
5596
+ startup_group_paused: "The saved group is paused. Resume it explicitly through the harness CLI before reopening.",
5597
+ startup_main_unavailable: "Inspect the saved main and recovery state. Stop the old group explicitly before using pmcp tui --new.",
5598
+ startup_busy: "Another workspace launch is running. Wait and retry.",
5599
+ startup_runtime_conflict: "The saved main uses another runtime. Stop the old group before creating a new workspace.",
5600
+ workspace_already_running: "Reopen the existing workspace, or stop its exact owned group before using --new.",
5601
+ runtime_executable_unavailable: "Install the selected native CLI on PATH, or choose an installed runtime with --runtime.",
5577
5602
  control_connection_lost: "The native writer or lease disappeared. Inspect and release control before reconnecting.",
5578
5603
  control_busy: "Another controller holds the lease. Release it explicitly before retrying.",
5579
5604
  native_writer_active: "A native writer is still attached. Detach that writer before retrying.",
@@ -5935,16 +5960,16 @@ var init_common = __esm({
5935
5960
  });
5936
5961
 
5937
5962
  // src/tui/application/create.ts
5938
- import { join as join49 } from "path";
5939
- import { realpathSync as realpathSync14 } from "fs";
5963
+ import { join as join51 } from "path";
5964
+ import { realpathSync as realpathSync16 } from "fs";
5940
5965
  async function createView(input) {
5941
5966
  const plan2 = planLayout(input.columns, input.rows);
5942
5967
  const helpers = runtime();
5943
5968
  const mailbox = input.mailbox ? {
5944
- config: realpathSync14(input.mailbox.config),
5945
- actorFile: realpathSync14(input.mailbox.actorFile)
5969
+ config: realpathSync16(input.mailbox.config),
5970
+ actorFile: realpathSync16(input.mailbox.actorFile)
5946
5971
  } : void 0;
5947
- const groupFile = realpathSync14(input.groupFile);
5972
+ const groupFile = realpathSync16(input.groupFile);
5948
5973
  const group = new GroupStore(groupFile).state.group;
5949
5974
  const common2 = new CommonHarness({
5950
5975
  groupFile,
@@ -5957,7 +5982,7 @@ async function createView(input) {
5957
5982
  );
5958
5983
  if (!main2) throw new Error("view_requires_live_main");
5959
5984
  const { directory, owner } = createDirectory();
5960
- const file2 = join49(directory, "view.json");
5985
+ const file2 = join51(directory, "view.json");
5961
5986
  const connection = new ViewConnection(directory, owner);
5962
5987
  const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
5963
5988
  const result = connection.raw([
@@ -6185,7 +6210,7 @@ var init_navigation = __esm({
6185
6210
  });
6186
6211
 
6187
6212
  // src/tui/application/history.ts
6188
- import { join as join50 } from "path";
6213
+ import { join as join52 } from "path";
6189
6214
  async function observeHistory(context) {
6190
6215
  const { view, renderer, common: common2, file: file2 } = context;
6191
6216
  await releaseControl(context);
@@ -6193,7 +6218,7 @@ async function observeHistory(context) {
6193
6218
  sessionId: view.target,
6194
6219
  input: { lines: 2e3, maxBytes: 65536 }
6195
6220
  });
6196
- privateJson(join50(view.directory, "history.json"), {
6221
+ privateJson(join52(view.directory, "history.json"), {
6197
6222
  sessionId: view.target,
6198
6223
  content: history.content,
6199
6224
  truncated: history.truncated
@@ -6269,8 +6294,8 @@ var init_mailbox = __esm({
6269
6294
  });
6270
6295
 
6271
6296
  // src/tui/application/lifecycle.ts
6272
- import { existsSync as existsSync32, unlinkSync as unlinkSync7 } from "fs";
6273
- import { join as join51 } from "path";
6297
+ import { existsSync as existsSync35, unlinkSync as unlinkSync7 } from "fs";
6298
+ import { join as join53 } from "path";
6274
6299
  function resizeView({ view, renderer, input }) {
6275
6300
  renderer.resize(view, planLayout(input.columns, input.rows));
6276
6301
  }
@@ -6285,8 +6310,8 @@ async function stopTarget(context) {
6285
6310
  async function closeView(context) {
6286
6311
  await returnToMain(context);
6287
6312
  context.renderer.connection.cleanup();
6288
- const history = join51(context.view.directory, "history.json");
6289
- if (existsSync32(history)) unlinkSync7(history);
6313
+ const history = join53(context.view.directory, "history.json");
6314
+ if (existsSync35(history)) unlinkSync7(history);
6290
6315
  context.view.state = "closed";
6291
6316
  saveView(context.file, context.view);
6292
6317
  }
@@ -6463,6 +6488,8 @@ __export(invoke_exports2, {
6463
6488
  invokeTui: () => invokeTui
6464
6489
  });
6465
6490
  async function invokeTui(invocation2) {
6491
+ if (invocation2.action === "launch" || invocation2.action === "plan")
6492
+ throw new Error("startup_requires_cli_context");
6466
6493
  if (invocation2.action === "doctor")
6467
6494
  return { schemaVersion: 1, ok: true, ...dependencies() };
6468
6495
  if (invocation2.action === "create") return createView(invocation2.create);
@@ -6511,6 +6538,319 @@ var init_invoke2 = __esm({
6511
6538
  }
6512
6539
  });
6513
6540
 
6541
+ // src/tui/startup/config.ts
6542
+ import { realpathSync as realpathSync17 } from "fs";
6543
+ import { resolve as resolve19 } from "path";
6544
+ function startupSettings(request3) {
6545
+ const path = request3.config ? resolve19(request3.cwd, request3.config) : findConfig(request3.cwd);
6546
+ const config = path ? readConfig(path) : void 0;
6547
+ const source2 = config?.raw.tui;
6548
+ const fail2 = (message) => {
6549
+ throw new ConfigError(path ?? "pmcp.toml", `[tui] ${message}`);
6550
+ };
6551
+ if (source2 !== void 0 && (source2 === null || typeof source2 !== "object" || Array.isArray(source2)))
6552
+ fail2("must be a table");
6553
+ const values = source2 ?? {};
6554
+ const allowed3 = /* @__PURE__ */ new Set([
6555
+ "runtime",
6556
+ "memory_mb",
6557
+ "session_memory_mb",
6558
+ "max_active",
6559
+ "group_file"
6560
+ ]);
6561
+ for (const key of Object.keys(values)) {
6562
+ if (!allowed3.has(key)) fail2(`unknown key ${key}`);
6563
+ }
6564
+ const string = (key) => {
6565
+ const value = values[key];
6566
+ if (value === void 0) return void 0;
6567
+ if (typeof value !== "string" || !value.trim())
6568
+ fail2(`${key} must be a non-empty string`);
6569
+ return value;
6570
+ };
6571
+ const integer2 = (key, fallback, min, max) => {
6572
+ const value = values[key] ?? fallback;
6573
+ if (typeof value !== "number" || !Number.isSafeInteger(value))
6574
+ fail2(`${key} must be an integer`);
6575
+ if (Number(value) < min || Number(value) > max)
6576
+ fail2(`${key} must be between ${min} and ${max}`);
6577
+ return Number(value);
6578
+ };
6579
+ const configuredRuntime = string("runtime");
6580
+ if (configuredRuntime !== void 0 && configuredRuntime !== "auto" && !Object.hasOwn(runtimeExecutables, configuredRuntime))
6581
+ fail2("runtime must be auto or a supported runtime ID");
6582
+ const available = preference.filter(
6583
+ (runtime3) => locateExecutable(runtimeExecutables[runtime3], request3.cwd)
6584
+ );
6585
+ let runtime2 = request3.runtime ?? configuredRuntime ?? "auto";
6586
+ if (runtime2 === "auto") runtime2 = available[0] ?? "codex-cli";
6587
+ if (!Object.hasOwn(runtimeExecutables, runtime2))
6588
+ fail2("runtime must be a supported runtime ID");
6589
+ const memoryMb = integer2("memory_mb", 2048, 128, 4096);
6590
+ const sessionMemoryMb = integer2("session_memory_mb", 768, 32, 1536);
6591
+ if (sessionMemoryMb > memoryMb)
6592
+ fail2("session memory exceeds the group budget");
6593
+ const groupFile = string("group_file");
6594
+ return {
6595
+ projectRoot: realpathSync17(config?.dir ?? request3.cwd),
6596
+ ...config ? { configFile: config.path } : {},
6597
+ runtime: runtime2,
6598
+ allowedRuntimes: [.../* @__PURE__ */ new Set([...available, runtime2])],
6599
+ memoryMb,
6600
+ sessionMemoryMb,
6601
+ maxActive: integer2("max_active", 2, 1, 5),
6602
+ ...groupFile ? { groupFile: resolve19(config?.dir ?? request3.cwd, groupFile) } : {}
6603
+ };
6604
+ }
6605
+ var preference;
6606
+ var init_config3 = __esm({
6607
+ "src/tui/startup/config.ts"() {
6608
+ "use strict";
6609
+ init_config();
6610
+ init_registry();
6611
+ init_executable();
6612
+ preference = [
6613
+ "codex-cli",
6614
+ "gemini-cli",
6615
+ "antigravity",
6616
+ "grok-cli",
6617
+ "claude-code"
6618
+ ];
6619
+ }
6620
+ });
6621
+
6622
+ // src/tui/startup/state.ts
6623
+ import { createHash as createHash19 } from "crypto";
6624
+ import { existsSync as existsSync36, lstatSync as lstatSync13, mkdirSync as mkdirSync12 } from "fs";
6625
+ import { homedir as homedir11 } from "os";
6626
+ import { dirname as dirname26, join as join54 } from "path";
6627
+ function startupFile(projectRoot) {
6628
+ const root = join54(
6629
+ process.env.XDG_STATE_HOME ?? join54(homedir11(), ".local/state"),
6630
+ "pmcp/launcher"
6631
+ );
6632
+ const key = createHash19("sha256").update(projectRoot).digest("hex");
6633
+ return join54(root, `${key}.json`);
6634
+ }
6635
+ function ensureStartupDirectory(file2) {
6636
+ const directory = dirname26(file2);
6637
+ mkdirSync12(directory, { recursive: true, mode: 448 });
6638
+ const stat = lstatSync13(directory);
6639
+ if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
6640
+ throw new Error("unsafe_startup_directory");
6641
+ }
6642
+ function readStartup(file2, projectRoot) {
6643
+ if (!existsSync36(file2)) return null;
6644
+ const value = object8(readPrivateJson2(file2));
6645
+ if (value.schemaVersion !== 1 || value.projectRoot !== projectRoot)
6646
+ throw new Error("foreign_startup_state");
6647
+ if (!["creating", "starting", "view-pending", "ready"].includes(
6648
+ String(value.phase)
6649
+ ))
6650
+ throw new Error("invalid_startup_state");
6651
+ text(value.runtime, "runtime");
6652
+ if (value.phase !== "creating") {
6653
+ text(value.groupFile, "group_file");
6654
+ text(value.generation, "generation");
6655
+ }
6656
+ if (value.phase === "ready") text(value.viewFile, "view_file");
6657
+ return value;
6658
+ }
6659
+ var init_state = __esm({
6660
+ "src/tui/startup/state.ts"() {
6661
+ "use strict";
6662
+ init_store2();
6663
+ init_validation();
6664
+ }
6665
+ });
6666
+
6667
+ // src/tui/startup/host.ts
6668
+ var host_exports = {};
6669
+ __export(host_exports, {
6670
+ requireTerminal: () => requireTerminal,
6671
+ terminalSupport: () => terminalSupport
6672
+ });
6673
+ function terminalSupport() {
6674
+ try {
6675
+ if (!dependencies().supported)
6676
+ throw new Error("tui_dependencies_unavailable");
6677
+ assertNativeHost();
6678
+ planLayout(process.stdout.columns ?? 0, process.stdout.rows ?? 0);
6679
+ return { supported: true };
6680
+ } catch (error) {
6681
+ return {
6682
+ supported: false,
6683
+ reason: error instanceof Error ? error.message : "terminal_unavailable"
6684
+ };
6685
+ }
6686
+ }
6687
+ function requireTerminal() {
6688
+ if (!interactiveEntry({
6689
+ stdin: process.stdin.isTTY === true,
6690
+ stdout: process.stdout.isTTY === true,
6691
+ term: process.env.TERM,
6692
+ ci: process.env.CI
6693
+ }))
6694
+ throw new Error("terminal_required");
6695
+ const support = terminalSupport();
6696
+ if (!support.supported) throw new Error(support.reason);
6697
+ }
6698
+ var init_host = __esm({
6699
+ "src/tui/startup/host.ts"() {
6700
+ "use strict";
6701
+ init_entry();
6702
+ init_layout();
6703
+ init_admission2();
6704
+ init_runtime2();
6705
+ }
6706
+ });
6707
+
6708
+ // src/tui/startup/lock.ts
6709
+ import { spawnSync as spawnSync6 } from "child_process";
6710
+ import { closeSync as closeSync8, constants as constants7, fstatSync as fstatSync5, openSync as openSync8 } from "fs";
6711
+ function serializedStartup(request3) {
6712
+ const settings2 = startupSettings(request3);
6713
+ const file2 = startupFile(settings2.projectRoot);
6714
+ ensureStartupDirectory(file2);
6715
+ const lock = `${file2}.lock`;
6716
+ const descriptor = openSync8(
6717
+ lock,
6718
+ constants7.O_CREAT | constants7.O_WRONLY | constants7.O_NOFOLLOW,
6719
+ 384
6720
+ );
6721
+ try {
6722
+ const stat = fstatSync5(descriptor);
6723
+ if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
6724
+ throw new Error("unsafe_startup_lock");
6725
+ } finally {
6726
+ closeSync8(descriptor);
6727
+ }
6728
+ const helpers = runtime();
6729
+ const encoded = Buffer.from(JSON.stringify(request3)).toString("base64url");
6730
+ const child = spawnSync6(
6731
+ "flock",
6732
+ [
6733
+ "--wait",
6734
+ "10",
6735
+ "--close",
6736
+ "--conflict-exit-code",
6737
+ "73",
6738
+ lock,
6739
+ helpers.node,
6740
+ helpers.worker,
6741
+ "startup",
6742
+ encoded
6743
+ ],
6744
+ {
6745
+ encoding: "utf8",
6746
+ timeout: 6e4,
6747
+ maxBuffer: 256 * 1024,
6748
+ stdio: ["ignore", "pipe", "pipe"],
6749
+ env: {
6750
+ ...process.env,
6751
+ PMCP_TUI_COLUMNS: String(process.stdout.columns),
6752
+ PMCP_TUI_ROWS: String(process.stdout.rows)
6753
+ }
6754
+ }
6755
+ );
6756
+ if (child.status === 73) throw new Error("startup_busy");
6757
+ if (child.error || !child.stdout.trim())
6758
+ throw new Error("startup_requires_recovery");
6759
+ return JSON.parse(child.stdout);
6760
+ }
6761
+ var init_lock2 = __esm({
6762
+ "src/tui/startup/lock.ts"() {
6763
+ "use strict";
6764
+ init_runtime2();
6765
+ init_state();
6766
+ init_config3();
6767
+ }
6768
+ });
6769
+
6770
+ // src/tui/startup/entry.ts
6771
+ var entry_exports = {};
6772
+ __export(entry_exports, {
6773
+ launchStartup: () => launchStartup,
6774
+ planStartup: () => planStartup
6775
+ });
6776
+ import { createInterface as createInterface2 } from "readline/promises";
6777
+ function planStartup(request3) {
6778
+ const settings2 = startupSettings(request3);
6779
+ const state = readStartup(
6780
+ startupFile(settings2.projectRoot),
6781
+ settings2.projectRoot
6782
+ );
6783
+ return {
6784
+ schemaVersion: 1,
6785
+ ok: true,
6786
+ settings: settings2,
6787
+ savedWorkspace: state,
6788
+ willStartRuntime: !state && !settings2.groupFile,
6789
+ runtimeExecutable: locateExecutable(
6790
+ runtimeExecutables[settings2.runtime],
6791
+ request3.cwd
6792
+ ),
6793
+ providerInvoked: false
6794
+ };
6795
+ }
6796
+ async function launchStartup(request3, confirmed) {
6797
+ requireTerminal();
6798
+ const settings2 = startupSettings(request3);
6799
+ const state = readStartup(
6800
+ startupFile(settings2.projectRoot),
6801
+ settings2.projectRoot
6802
+ );
6803
+ const starting = (!state || request3.fresh) && !settings2.groupFile;
6804
+ if (starting) {
6805
+ if (!locateExecutable(runtimeExecutables[settings2.runtime], request3.cwd))
6806
+ throw new Error("runtime_executable_unavailable");
6807
+ process.stderr.write(
6808
+ `PMCP main: ${settings2.runtime} | ${settings2.projectRoot}
6809
+ `
6810
+ );
6811
+ if (!confirmed && !await confirmStart(settings2.runtime))
6812
+ return {
6813
+ schemaVersion: 1,
6814
+ ok: true,
6815
+ cancelled: true,
6816
+ providerInvoked: false
6817
+ };
6818
+ }
6819
+ const prepared = serializedStartup(request3);
6820
+ if (prepared.ok !== true) return prepared;
6821
+ return invokeTui({
6822
+ action: "open",
6823
+ viewFile: String(prepared.viewFile),
6824
+ input: {}
6825
+ });
6826
+ }
6827
+ async function confirmStart(runtime2) {
6828
+ const prompt = createInterface2({
6829
+ input: process.stdin,
6830
+ output: process.stderr
6831
+ });
6832
+ try {
6833
+ const answer = await prompt.question(
6834
+ `Start ${runtime2}? [Enter=start, q=cancel] `
6835
+ );
6836
+ return answer.trim() === "";
6837
+ } finally {
6838
+ prompt.close();
6839
+ }
6840
+ }
6841
+ var init_entry2 = __esm({
6842
+ "src/tui/startup/entry.ts"() {
6843
+ "use strict";
6844
+ init_executable();
6845
+ init_registry();
6846
+ init_invoke2();
6847
+ init_config3();
6848
+ init_state();
6849
+ init_host();
6850
+ init_lock2();
6851
+ }
6852
+ });
6853
+
6514
6854
  // src/cli/command.ts
6515
6855
  var ArgumentError = class extends Error {
6516
6856
  constructor(message) {
@@ -6585,7 +6925,12 @@ function printHelp(ui, commands, notImplemented) {
6585
6925
  ui.line();
6586
6926
  ui.table(commands.map((command2) => [command2.name, command2.describe]));
6587
6927
  ui.line();
6588
- ui.line(" With no command, pmcp runs the MCP server on stdio.");
6928
+ ui.line(
6929
+ " With no command, pmcp opens a terminal workspace on a supported TTY."
6930
+ );
6931
+ ui.line(
6932
+ " Piped launches retain MCP stdio. Use pmcp serve explicitly for MCP."
6933
+ );
6589
6934
  ui.line();
6590
6935
  ui.table([
6591
6936
  ["-q, --quiet", "only errors"],
@@ -6688,6 +7033,9 @@ var Ui = class {
6688
7033
  }
6689
7034
  };
6690
7035
 
7036
+ // src/commands/index.ts
7037
+ init_entry();
7038
+
6691
7039
  // src/intent.ts
6692
7040
  function normaliseIntent(intent) {
6693
7041
  return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
@@ -9163,7 +9511,7 @@ function registerMailboxTools(server, service) {
9163
9511
 
9164
9512
  // src/mcp.ts
9165
9513
  var SERVER_NAME = "pmcp";
9166
- var SERVER_VERSION = "0.14.1";
9514
+ var SERVER_VERSION = "0.15.0";
9167
9515
  function registerSkillTools(server, tools) {
9168
9516
  registerCatalogTool(server, tools);
9169
9517
  registerFindTool(server, tools);
@@ -9351,7 +9699,7 @@ async function prepareSearch(catalog, options2) {
9351
9699
  // src/commands/serve.ts
9352
9700
  var serveCommand = {
9353
9701
  name: "serve",
9354
- describe: "Run the MCP server on stdio (the default with no command)",
9702
+ describe: "Run the MCP server on stdio (also the default for piped launches)",
9355
9703
  usage: "pmcp serve [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir>] [--config <file> | --no-config] [--prompts]",
9356
9704
  options: [
9357
9705
  ...CATALOG_OPTIONS,
@@ -9812,6 +10160,10 @@ function installedPackageDirectory(project2, name) {
9812
10160
  for (; ; ) {
9813
10161
  const candidate2 = join11(directory, "node_modules", name);
9814
10162
  if (existsSync10(join11(candidate2, "package.json"))) return candidate2;
10163
+ if (existsSync10(join11(directory, ".git")))
10164
+ throw new Error(
10165
+ "Installed package manifest was not found in this repository"
10166
+ );
9815
10167
  const parent = dirname11(directory);
9816
10168
  if (parent === directory)
9817
10169
  throw new Error("Installed package manifest was not found");
@@ -9829,6 +10181,8 @@ function locate(name, project2) {
9829
10181
  );
9830
10182
  } catch (error) {
9831
10183
  if (!absent(error)) return { name, reason: "unreadable_manifest" };
10184
+ if (existsSync10(join11(directory, ".git")))
10185
+ return { name, reason: "not_installed" };
9832
10186
  const parent = dirname11(directory);
9833
10187
  if (parent === directory) return { name, reason: "not_installed" };
9834
10188
  directory = parent;
@@ -9885,30 +10239,77 @@ function readInstalledDependencies(projectDirectory) {
9885
10239
  }
9886
10240
 
9887
10241
  // src/install/plan.ts
9888
- import { createHash as createHash7 } from "crypto";
9889
- import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
9890
- import { dirname as dirname12, join as join12, relative as relative6, resolve as resolve9, sep as sep5 } from "path";
9891
10242
  import semver from "semver";
9892
- function matchingSkills(catalog, inventory) {
9893
- const matches2 = [];
9894
- for (const dependency of inventory.dependencies) {
9895
- for (const entry2 of catalog.entries) {
9896
- const target2 = entry2.targets.find(
9897
- (target3) => target3.packageName === dependency.name
10243
+
10244
+ // src/install/manager.ts
10245
+ import { existsSync as existsSync11 } from "fs";
10246
+ import { join as join12, relative as relative6, sep as sep5 } from "path";
10247
+ function installationCommand(context, specs) {
10248
+ if (context.manager === "npm") {
10249
+ const args2 = [
10250
+ "install",
10251
+ "--save-dev",
10252
+ "--save-exact",
10253
+ "--ignore-scripts",
10254
+ "--no-audit",
10255
+ "--no-fund"
10256
+ ];
10257
+ if (context.project !== context.root)
10258
+ args2.push(
10259
+ `--workspace=${relative6(context.root, context.project).split(sep5).join("/")}`
9898
10260
  );
9899
- if (target2 && semver.satisfies(dependency.version, target2.range)) {
9900
- matches2.push({
9901
- targetName: dependency.name,
9902
- targetVersion: dependency.version,
9903
- verified: target2.verifiedVersions.includes(dependency.version),
9904
- entry: entry2
9905
- });
10261
+ return { executable: "npm", args: [...args2, ...specs], cwd: context.root };
10262
+ }
10263
+ const args = context.manager === "pnpm" ? ["add", "--save-dev", "--save-exact", "--ignore-scripts"] : ["add", "--dev", "--exact", "--ignore-scripts"];
10264
+ if (context.manager === "pnpm" && context.project === context.root && existsSync11(join12(context.root, "pnpm-workspace.yaml")))
10265
+ args.push("--workspace-root");
10266
+ return {
10267
+ executable: context.manager,
10268
+ args: [...args, ...specs],
10269
+ cwd: context.project
10270
+ };
10271
+ }
10272
+ function integrityArguments(manager, spec) {
10273
+ const verb = manager === "bun" ? "info" : "view";
10274
+ return [verb, spec, "dist.integrity", "--json"];
10275
+ }
10276
+
10277
+ // src/install/context.ts
10278
+ import { createHash as createHash7 } from "crypto";
10279
+ import { existsSync as existsSync13, readFileSync as readFileSync15, realpathSync as realpathSync4 } from "fs";
10280
+ import { dirname as dirname12, join as join14 } from "path";
10281
+
10282
+ // src/install/workspace.ts
10283
+ import { existsSync as existsSync12, readFileSync as readFileSync14, realpathSync as realpathSync3 } from "fs";
10284
+ import { join as join13 } from "path";
10285
+ import { parse as parse3 } from "yaml";
10286
+ function workspaceContains(root, project2) {
10287
+ const file2 = join13(root, "pnpm-workspace.yaml");
10288
+ if (!existsSync12(file2)) return workspacePackageDirs(root).includes(project2);
10289
+ const value = parse3(readFileSync14(file2, "utf8"));
10290
+ if (!value || typeof value !== "object" || Array.isArray(value))
10291
+ throw new Error("invalid pnpm workspace configuration");
10292
+ const patterns = value.packages ?? [];
10293
+ if (!Array.isArray(patterns) || patterns.some((item) => typeof item !== "string"))
10294
+ throw new Error("pnpm workspace packages must be string patterns");
10295
+ const included = /* @__PURE__ */ new Set([root]);
10296
+ const excluded = /* @__PURE__ */ new Set();
10297
+ for (const raw of patterns) {
10298
+ const negated = raw.startsWith("!");
10299
+ const pattern = negated ? raw.slice(1) : raw;
10300
+ if (!pattern || pattern.split("/").includes("..") || pattern.startsWith("/"))
10301
+ throw new Error("workspace patterns must stay inside the workspace");
10302
+ for (const alternative of expandBraces(pattern)) {
10303
+ for (const match of globDirs(root, alternative)) {
10304
+ (negated ? excluded : included).add(realpathSync3(match));
9906
10305
  }
9907
10306
  }
9908
10307
  }
9909
- return matches2;
10308
+ return project2 === root || included.has(project2) && !excluded.has(project2);
9910
10309
  }
9911
- var LOCKS = [
10310
+
10311
+ // src/install/context.ts
10312
+ var locks = [
9912
10313
  "package-lock.json",
9913
10314
  "npm-shrinkwrap.json",
9914
10315
  "bun.lock",
@@ -9921,7 +10322,7 @@ function fingerprint(files) {
9921
10322
  for (const file2 of files) {
9922
10323
  hash2.update(file2).update("\0");
9923
10324
  try {
9924
- hash2.update(readFileSync14(file2));
10325
+ hash2.update(readFileSync15(file2));
9925
10326
  } catch (error) {
9926
10327
  if (error.code !== "ENOENT") throw error;
9927
10328
  hash2.update("absent");
@@ -9932,65 +10333,71 @@ function fingerprint(files) {
9932
10333
  }
9933
10334
  function manifestAt(directory) {
9934
10335
  const value = JSON.parse(
9935
- readFileSync14(join12(directory, "package.json"), "utf8")
10336
+ readFileSync15(join14(directory, "package.json"), "utf8")
9936
10337
  );
9937
10338
  if (!value || typeof value !== "object" || Array.isArray(value))
9938
10339
  throw new Error("invalid project manifest");
9939
10340
  return value;
9940
10341
  }
9941
- function installContext(directory) {
9942
- const project2 = resolve9(directory);
9943
- const manifest2 = manifestAt(project2);
9944
- let root = project2;
10342
+ function managerForLock(file2) {
10343
+ if (file2.startsWith("bun.")) return "bun";
10344
+ if (file2 === "pnpm-lock.yaml") return "pnpm";
10345
+ if (file2 === "package-lock.json" || file2 === "npm-shrinkwrap.json")
10346
+ return "npm";
10347
+ return "unsupported";
10348
+ }
10349
+ function workspaceRoot(project2) {
9945
10350
  let ancestor = project2;
9946
10351
  for (; ; ) {
9947
- if (ancestor !== project2 && existsSync11(join12(ancestor, "package.json"))) {
10352
+ if (ancestor !== project2 && existsSync13(join14(ancestor, "package.json"))) {
9948
10353
  const candidate2 = manifestAt(ancestor);
9949
- if (candidate2.workspaces !== void 0) {
9950
- root = ancestor;
9951
- break;
10354
+ if (candidate2.workspaces !== void 0 || existsSync13(join14(ancestor, "pnpm-workspace.yaml"))) {
10355
+ if (!workspaceContains(ancestor, project2))
10356
+ throw new Error("project is excluded from the ancestor workspace");
10357
+ return ancestor;
9952
10358
  }
9953
- if (LOCKS.some((file2) => existsSync11(join12(ancestor, file2)))) break;
10359
+ if (locks.some((file2) => existsSync13(join14(ancestor, file2))))
10360
+ return project2;
9954
10361
  }
9955
- if (existsSync11(join12(ancestor, ".git")) || dirname12(ancestor) === ancestor)
9956
- break;
10362
+ if (existsSync13(join14(ancestor, ".git")) || dirname12(ancestor) === ancestor)
10363
+ return project2;
9957
10364
  ancestor = dirname12(ancestor);
9958
10365
  }
10366
+ }
10367
+ function installContext(directory) {
10368
+ const project2 = realpathSync4(directory);
10369
+ const manifest2 = manifestAt(project2);
10370
+ const root = workspaceRoot(project2);
9959
10371
  const owner = manifestAt(root);
9960
- const lockNames = LOCKS.filter((file2) => existsSync11(join12(root, file2)));
9961
- const lockManagers = new Set(
9962
- lockNames.map((file2) => {
9963
- if (file2.startsWith("bun.")) return "bun";
9964
- if (file2 === "package-lock.json" || file2 === "npm-shrinkwrap.json")
9965
- return "npm";
9966
- return "unsupported";
9967
- })
9968
- );
10372
+ const lockNames = locks.filter((file2) => existsSync13(join14(root, file2)));
10373
+ const lockManagers = new Set(lockNames.map(managerForLock));
9969
10374
  if (lockManagers.size > 1 || lockManagers.has("unsupported"))
9970
10375
  throw new Error("conflicting or unsupported package manager lockfiles");
9971
10376
  const declared = owner.packageManager?.match(
9972
- /^(npm|bun)@\d+\.\d+\.\d+(?:[-+].+)?$/u
10377
+ /^(npm|bun|pnpm)@\d+\.\d+\.\d+(?:[-+].+)?$/u
9973
10378
  )?.[1];
9974
10379
  if (owner.packageManager !== void 0 && !declared)
9975
- throw new Error("only npm and Bun projects are supported");
10380
+ throw new Error("only npm, Bun and pnpm projects are supported");
9976
10381
  const locked = [...lockManagers][0];
9977
10382
  if (declared && locked && declared !== locked)
9978
10383
  throw new Error("packageManager and lockfile disagree");
9979
10384
  const manager = declared ?? locked;
9980
- if (manager !== "npm" && manager !== "bun")
10385
+ if (manager !== "npm" && manager !== "bun" && manager !== "pnpm")
9981
10386
  throw new Error(
9982
10387
  "declare packageManager or install project dependencies first"
9983
10388
  );
9984
- if (root !== project2 && LOCKS.some((file2) => existsSync11(join12(project2, file2)))) {
10389
+ if (root !== project2 && locks.some((file2) => existsSync13(join14(project2, file2))))
9985
10390
  throw new Error(
9986
10391
  "nested project has its own lockfile; resolve the workspace boundary first"
9987
10392
  );
9988
- }
9989
10393
  const files = [
9990
10394
  .../* @__PURE__ */ new Set([
9991
- join12(root, "package.json"),
9992
- join12(project2, "package.json"),
9993
- ...LOCKS.map((file2) => join12(root, file2))
10395
+ join14(root, "package.json"),
10396
+ join14(project2, "package.json"),
10397
+ join14(root, "pnpm-workspace.yaml"),
10398
+ join14(root, ".npmrc"),
10399
+ join14(project2, ".npmrc"),
10400
+ ...locks.map((file2) => join14(root, file2))
9994
10401
  ])
9995
10402
  ];
9996
10403
  return {
@@ -10002,6 +10409,27 @@ function installContext(directory) {
10002
10409
  fingerprint: fingerprint(files)
10003
10410
  };
10004
10411
  }
10412
+
10413
+ // src/install/plan.ts
10414
+ function matchingSkills(catalog, inventory) {
10415
+ const matches2 = [];
10416
+ for (const dependency of inventory.dependencies) {
10417
+ for (const entry2 of catalog.entries) {
10418
+ const target2 = entry2.targets.find(
10419
+ (target3) => target3.packageName === dependency.name
10420
+ );
10421
+ if (target2 && semver.satisfies(dependency.version, target2.range)) {
10422
+ matches2.push({
10423
+ targetName: dependency.name,
10424
+ targetVersion: dependency.version,
10425
+ verified: target2.verifiedVersions.includes(dependency.version),
10426
+ entry: entry2
10427
+ });
10428
+ }
10429
+ }
10430
+ }
10431
+ return matches2;
10432
+ }
10005
10433
  function planInstall(options2) {
10006
10434
  if (options2.inventory.issues.length)
10007
10435
  throw new Error(
@@ -10066,49 +10494,27 @@ function planInstall(options2) {
10066
10494
  const specs = deliveries.map(
10067
10495
  (entry2) => `${entry2.packageName}@${entry2.version}`
10068
10496
  );
10069
- let args;
10070
- if (context.manager === "npm") {
10071
- args = [
10072
- "install",
10073
- "--save-dev",
10074
- "--save-exact",
10075
- "--ignore-scripts",
10076
- "--no-audit",
10077
- "--no-fund"
10078
- ];
10079
- if (context.project !== context.root)
10080
- args.push(
10081
- `--workspace=${relative6(context.root, context.project).split(sep5).join("/")}`
10082
- );
10083
- } else {
10084
- args = ["add", "--dev", "--exact", "--ignore-scripts"];
10085
- }
10086
- args.push(...specs);
10087
10497
  return {
10088
10498
  catalogRevision: catalog.revision,
10089
10499
  context,
10090
10500
  changes: deliveries,
10091
10501
  recalled,
10092
- command: {
10093
- executable: context.manager,
10094
- args,
10095
- cwd: context.manager === "npm" ? context.root : context.project
10096
- }
10502
+ command: installationCommand(context, specs)
10097
10503
  };
10098
10504
  }
10099
10505
 
10100
10506
  // src/commands/remote-options.ts
10101
- import { join as join14, resolve as resolve10 } from "path";
10102
- import { readFileSync as readFileSync17 } from "fs";
10507
+ import { dirname as dirname13, join as join16, resolve as resolve9 } from "path";
10508
+ import { existsSync as existsSync14, readFileSync as readFileSync18 } from "fs";
10103
10509
 
10104
10510
  // src/remote/catalog.ts
10105
- import { readFileSync as readFileSync16, statSync as statSync8 } from "fs";
10511
+ import { readFileSync as readFileSync17, statSync as statSync8 } from "fs";
10106
10512
  import semver2 from "semver";
10107
10513
  import { z as z13 } from "zod";
10108
10514
 
10109
10515
  // src/remote/catalog-cache.ts
10110
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
10111
- import { join as join13 } from "path";
10516
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync16, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
10517
+ import { join as join15 } from "path";
10112
10518
  var MAX_CACHE_BYTES = 8 * 1024 * 1024;
10113
10519
  function memoryCatalogCache() {
10114
10520
  const held = /* @__PURE__ */ new Map();
@@ -10120,11 +10526,11 @@ function memoryCatalogCache() {
10120
10526
  };
10121
10527
  }
10122
10528
  function fileCatalogCache(dir) {
10123
- const path = join13(dir, "catalog-cache.json");
10529
+ const path = join15(dir, "catalog-cache.json");
10124
10530
  return {
10125
10531
  read(origin) {
10126
10532
  try {
10127
- const raw = readFileSync15(path, "utf8");
10533
+ const raw = readFileSync16(path, "utf8");
10128
10534
  if (raw.length > MAX_CACHE_BYTES) return null;
10129
10535
  const held = JSON.parse(raw);
10130
10536
  if (typeof held !== "object" || held === null) return null;
@@ -10247,7 +10653,7 @@ function readCatalogFile(path) {
10247
10653
  const stat = statSync8(path);
10248
10654
  if (!stat.isFile() || stat.size > MAX_CATALOG_BYTES)
10249
10655
  throw new Error("catalog file exceeds the supported size");
10250
- const bytes = readFileSync16(path);
10656
+ const bytes = readFileSync17(path);
10251
10657
  if (bytes.byteLength > MAX_CATALOG_BYTES)
10252
10658
  throw new Error("catalog file exceeds the supported size");
10253
10659
  return parseCatalog(JSON.parse(bytes.toString("utf8")));
@@ -10349,7 +10755,17 @@ function catalogProvider(context) {
10349
10755
  return given ?? (api2 ? "hosted" : "public");
10350
10756
  }
10351
10757
  function projectFrom(context) {
10352
- return resolve10(context.cwd, one(context.args, "project") ?? ".");
10758
+ const explicit = one(context.args, "project");
10759
+ if (explicit) return resolve9(context.cwd, explicit);
10760
+ let directory = resolve9(context.cwd);
10761
+ for (; ; ) {
10762
+ if (existsSync14(join16(directory, "package.json"))) return directory;
10763
+ if (existsSync14(join16(directory, ".git")) || dirname13(directory) === directory)
10764
+ throw new ArgumentError(
10765
+ "No project package.json found; provide --project"
10766
+ );
10767
+ directory = dirname13(directory);
10768
+ }
10353
10769
  }
10354
10770
  function apiOrigin(context) {
10355
10771
  return one(context.args, "api") ?? context.env["PMCP_API_ORIGIN"] ?? DEFAULT_API_ORIGIN;
@@ -10360,11 +10776,11 @@ async function remoteCatalog(context) {
10360
10776
  if (file2) {
10361
10777
  if (one(context.args, "api"))
10362
10778
  throw new Error("choose --catalog or --api, not both");
10363
- return readCatalogFile(resolve10(context.cwd, file2));
10779
+ return readCatalogFile(resolve9(context.cwd, file2));
10364
10780
  }
10365
10781
  if (provider === "public") {
10366
10782
  const catalog = JSON.parse(
10367
- readFileSync17(join14(publicMarketplaceRoot(), "docs/catalog.json"), "utf8")
10783
+ readFileSync18(join16(publicMarketplaceRoot(), "docs/catalog.json"), "utf8")
10368
10784
  );
10369
10785
  return parseCatalog({
10370
10786
  schemaVersion: 1,
@@ -10517,7 +10933,7 @@ function createPackageManagerRunner(userConfig, baseEnv = process.env, verifiedI
10517
10933
  const alreadyVerified = verifiedIntegrity?.get(packageSpec);
10518
10934
  if (alreadyVerified) return alreadyVerified;
10519
10935
  const { manager, project: project2 } = plan2.context;
10520
- const args = manager === "npm" ? ["view", packageSpec, "dist.integrity", "--json"] : ["info", packageSpec, "dist.integrity", "--json"];
10936
+ const args = integrityArguments(manager, packageSpec);
10521
10937
  const result = spawnSync(manager, args, {
10522
10938
  cwd: project2,
10523
10939
  env: environment(userConfig, baseEnv),
@@ -10592,8 +11008,8 @@ function applyInstallPlan(plan2, runner = packageManagerRunner) {
10592
11008
 
10593
11009
  // src/install/verify-content.ts
10594
11010
  import { createHash as createHash8 } from "crypto";
10595
- import { readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
10596
- import { join as join15, relative as relative7, resolve as resolve11, sep as sep6 } from "path";
11011
+ import { readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
11012
+ import { join as join17, relative as relative7, resolve as resolve10, sep as sep6 } from "path";
10597
11013
  var SKILL = "SKILL.md";
10598
11014
  var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".cache"]);
10599
11015
  function skillFiles2(root) {
@@ -10607,7 +11023,7 @@ function skillFiles2(root) {
10607
11023
  }
10608
11024
  for (const name of entries) {
10609
11025
  if (SKIP.has(name)) continue;
10610
- const path = join15(dir, name);
11026
+ const path = join17(dir, name);
10611
11027
  let stats;
10612
11028
  try {
10613
11029
  stats = statSync9(path);
@@ -10618,7 +11034,7 @@ function skillFiles2(root) {
10618
11034
  else if (name === SKILL)
10619
11035
  found.set(
10620
11036
  relative7(root, path).split(sep6).join("/"),
10621
- readFileSync18(path, "utf8")
11037
+ readFileSync19(path, "utf8")
10622
11038
  );
10623
11039
  }
10624
11040
  };
@@ -10634,11 +11050,11 @@ function contentDigest(files) {
10634
11050
  return `sha256:${hash2.digest("hex")}`;
10635
11051
  }
10636
11052
  function verifyInstalledContent(project2, packageName2, expected) {
10637
- let root = resolve11(project2, "node_modules", ...packageName2.split("/"));
11053
+ let root = resolve10(project2, "node_modules", ...packageName2.split("/"));
10638
11054
  try {
10639
11055
  root = installedPackageDirectory(project2, packageName2);
10640
11056
  } catch {
10641
- root = resolve11(project2, "node_modules", ...packageName2.split("/"));
11057
+ root = resolve10(project2, "node_modules", ...packageName2.split("/"));
10642
11058
  }
10643
11059
  const files = skillFiles2(root);
10644
11060
  const actual = contentDigest(files);
@@ -10652,8 +11068,8 @@ function verifyInstalledContent(project2, packageName2, expected) {
10652
11068
  }
10653
11069
 
10654
11070
  // src/install/public.ts
10655
- import { lstatSync, readFileSync as readFileSync19, readdirSync as readdirSync6 } from "fs";
10656
- import { join as join16 } from "path";
11071
+ import { lstatSync, readFileSync as readFileSync20, readdirSync as readdirSync6 } from "fs";
11072
+ import { join as join18 } from "path";
10657
11073
 
10658
11074
  // src/install/archive.ts
10659
11075
  import { createHash as createHash9 } from "crypto";
@@ -10820,7 +11236,7 @@ function verifyPublicDelivery(project2, delivery) {
10820
11236
  for (const entry2 of readdirSync6(directory, { withFileTypes: true })) {
10821
11237
  const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
10822
11238
  if (entry2.isDirectory()) {
10823
- walk(join16(directory, entry2.name), path);
11239
+ walk(join18(directory, entry2.name), path);
10824
11240
  continue;
10825
11241
  }
10826
11242
  if (!entry2.isFile() || !expected.has(path))
@@ -10831,8 +11247,8 @@ function verifyPublicDelivery(project2, delivery) {
10831
11247
  };
10832
11248
  walk(root);
10833
11249
  for (const [path, bytes] of files) {
10834
- const destination = join16(root, path.slice("package/".length));
10835
- if (!lstatSync(destination).isFile() || !readFileSync19(destination).equals(bytes))
11250
+ const destination = join18(root, path.slice("package/".length));
11251
+ if (!lstatSync(destination).isFile() || !readFileSync20(destination).equals(bytes))
10836
11252
  throw new Error(
10837
11253
  "Installed public package differs from its verified archive; inspect the project before retrying"
10838
11254
  );
@@ -10843,7 +11259,7 @@ function verifyPublicDelivery(project2, delivery) {
10843
11259
  // src/remote/distribution.ts
10844
11260
  import { chmodSync as chmodSync4, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
10845
11261
  import { tmpdir } from "os";
10846
- import { join as join17 } from "path";
11262
+ import { join as join19 } from "path";
10847
11263
  function api(origin, path) {
10848
11264
  const base = new URL(catalogEndpoint(origin));
10849
11265
  base.pathname = path;
@@ -10983,9 +11399,9 @@ async function revokeDistributionCredential(accessToken, id2, origin = DEFAULT_A
10983
11399
  }
10984
11400
  }
10985
11401
  function temporaryRegistryConfig(value) {
10986
- const directory = mkdtempSync(join17(tmpdir(), "pmcp-registry-"));
11402
+ const directory = mkdtempSync(join19(tmpdir(), "pmcp-registry-"));
10987
11403
  chmodSync4(directory, 448);
10988
- const path = join17(directory, ".npmrc");
11404
+ const path = join19(directory, ".npmrc");
10989
11405
  const registry = new URL(value.registry);
10990
11406
  const auth = `//${registry.host}${registry.pathname}:_authToken=${value.token}`;
10991
11407
  writeFileSync3(path, `${value.scope}:registry=${registry.href}
@@ -11238,36 +11654,34 @@ import { homedir as homedir6 } from "os";
11238
11654
  // src/project/apply.ts
11239
11655
  import { createHash as createHash13 } from "crypto";
11240
11656
  import {
11241
- existsSync as existsSync17,
11242
- lstatSync as lstatSync4,
11657
+ existsSync as existsSync20,
11658
+ lstatSync as lstatSync5,
11243
11659
  mkdirSync as mkdirSync7,
11244
- readFileSync as readFileSync24,
11660
+ readFileSync as readFileSync25,
11245
11661
  readdirSync as readdirSync9,
11246
11662
  readlinkSync as readlinkSync2,
11247
- rmSync as rmSync4,
11248
- symlinkSync,
11249
- writeFileSync as writeFileSync5
11663
+ rmSync as rmSync5
11250
11664
  } from "fs";
11251
- import { dirname as dirname16, join as join22, posix as posix2 } from "path";
11252
- import { parse as parse4 } from "smol-toml";
11665
+ import { dirname as dirname18, join as join24, posix as posix2 } from "path";
11666
+ import { parse as parse5 } from "smol-toml";
11253
11667
 
11254
11668
  // src/project/backup.ts
11255
11669
  import { createHash as createHash10 } from "crypto";
11256
11670
  import {
11257
- existsSync as existsSync13,
11671
+ existsSync as existsSync16,
11258
11672
  lstatSync as lstatSync3,
11259
11673
  mkdirSync as mkdirSync6,
11260
11674
  mkdtempSync as mkdtempSync2,
11261
- readFileSync as readFileSync20,
11675
+ readFileSync as readFileSync21,
11262
11676
  writeFileSync as writeFileSync4
11263
11677
  } from "fs";
11264
11678
  import { homedir as homedir3 } from "os";
11265
- import { dirname as dirname14, isAbsolute as isAbsolute4, join as join19, relative as relative8, resolve as resolve12 } from "path";
11679
+ import { dirname as dirname15, isAbsolute as isAbsolute4, join as join21, relative as relative8, resolve as resolve11 } from "path";
11266
11680
 
11267
11681
  // src/project/package-rules.ts
11268
11682
  import { execFileSync } from "child_process";
11269
- import { existsSync as existsSync12, lstatSync as lstatSync2, readdirSync as readdirSync7, readlinkSync } from "fs";
11270
- import { dirname as dirname13, join as join18, posix } from "path";
11683
+ import { existsSync as existsSync15, lstatSync as lstatSync2, readdirSync as readdirSync7, readlinkSync } from "fs";
11684
+ import { dirname as dirname14, join as join20, posix } from "path";
11271
11685
  var ProjectError = class extends Error {
11272
11686
  constructor(message) {
11273
11687
  super(message);
@@ -11312,11 +11726,11 @@ function linkTarget(abs) {
11312
11726
  }
11313
11727
  function legacyLinks(root) {
11314
11728
  const found = /* @__PURE__ */ new Map();
11315
- const dir = join18(root, RULES_DIR);
11316
- if (!existsSync12(dir)) return found;
11729
+ const dir = join20(root, RULES_DIR);
11730
+ if (!existsSync15(dir)) return found;
11317
11731
  for (const entry2 of readdirSync7(dir).sort()) {
11318
11732
  if (!entry2.endsWith(".md")) continue;
11319
- const target2 = linkTarget(join18(dir, entry2));
11733
+ const target2 = linkTarget(join20(dir, entry2));
11320
11734
  if (target2 === null || !target2.startsWith("../../")) continue;
11321
11735
  found.set(entry2.slice(0, -3), target2.slice("../../".length));
11322
11736
  }
@@ -11336,14 +11750,14 @@ function packageRuleOutputs(root, spec, owned) {
11336
11750
  const legacyByDir = /* @__PURE__ */ new Map();
11337
11751
  for (const [name, target2] of legacy) {
11338
11752
  if (!byTarget.has(target2)) byTarget.set(target2, name);
11339
- const dir = dirname13(target2);
11753
+ const dir = dirname14(target2);
11340
11754
  if (!legacyByDir.has(dir)) legacyByDir.set(dir, name);
11341
11755
  }
11342
11756
  const outputs = [];
11343
11757
  const taken = /* @__PURE__ */ new Map();
11344
11758
  for (const file2 of files) {
11345
11759
  const pkg = file2.slice(0, -suffix.length);
11346
- const name = byTarget.get(file2) ?? legacyByDir.get(dirname13(file2)) ?? deriveName(pkg, spec.strip);
11760
+ const name = byTarget.get(file2) ?? legacyByDir.get(dirname14(file2)) ?? deriveName(pkg, spec.strip);
11347
11761
  const other = taken.get(name);
11348
11762
  if (other !== void 0) {
11349
11763
  throw new ProjectError(
@@ -11362,8 +11776,8 @@ function packageRuleOutputs(root, spec, owned) {
11362
11776
 
11363
11777
  // src/project/backup.ts
11364
11778
  function backupFiles(root, paths) {
11365
- const base = join19(homedir3(), ".cache/pmcp/project-backups");
11366
- const inside = relative8(resolve12(root), resolve12(base));
11779
+ const base = join21(homedir3(), ".cache/pmcp/project-backups");
11780
+ const inside = relative8(resolve11(root), resolve11(base));
11367
11781
  if (!isAbsolute4(inside) && inside !== ".." && !inside.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`)) {
11368
11782
  throw new ProjectError("Project backups must be outside the project");
11369
11783
  }
@@ -11378,12 +11792,12 @@ function backupFiles(root, paths) {
11378
11792
  );
11379
11793
  }
11380
11794
  const snapshots = [...new Set(paths)].map((path) => {
11381
- const absolute2 = resolve12(root, path);
11382
- const within = relative8(resolve12(root), absolute2);
11795
+ const absolute2 = resolve11(root, path);
11796
+ const within = relative8(resolve11(root), absolute2);
11383
11797
  if (isAbsolute4(within) || within === ".." || within.startsWith("../") || within.startsWith("..\\")) {
11384
11798
  throw new ProjectError("Backup paths must stay inside the project");
11385
11799
  }
11386
- if (!existsSync13(absolute2)) {
11800
+ if (!existsSync16(absolute2)) {
11387
11801
  return { path, contents: null };
11388
11802
  }
11389
11803
  const stat = lstatSync3(absolute2);
@@ -11392,15 +11806,15 @@ function backupFiles(root, paths) {
11392
11806
  `Refusing to back up a nonregular config file: ${path}`
11393
11807
  );
11394
11808
  }
11395
- return { path, contents: readFileSync20(absolute2) };
11809
+ return { path, contents: readFileSync21(absolute2) };
11396
11810
  });
11397
- const destination = mkdtempSync2(join19(base, "project-"));
11811
+ const destination = mkdtempSync2(join21(base, "project-"));
11398
11812
  const entries = snapshots.map(({ path, contents }) => {
11399
11813
  if (contents === null) {
11400
11814
  return { path, present: false };
11401
11815
  }
11402
- const output = join19(destination, "files", path);
11403
- mkdirSync6(dirname14(output), { recursive: true, mode: 448 });
11816
+ const output = join21(destination, "files", path);
11817
+ mkdirSync6(dirname15(output), { recursive: true, mode: 448 });
11404
11818
  writeFileSync4(output, contents, { mode: 384 });
11405
11819
  return {
11406
11820
  path,
@@ -11409,11 +11823,11 @@ function backupFiles(root, paths) {
11409
11823
  };
11410
11824
  });
11411
11825
  writeFileSync4(
11412
- join19(destination, "receipt.json"),
11826
+ join21(destination, "receipt.json"),
11413
11827
  JSON.stringify(
11414
11828
  {
11415
11829
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
11416
- projectRoot: resolve12(root),
11830
+ projectRoot: resolve11(root),
11417
11831
  entries
11418
11832
  },
11419
11833
  null,
@@ -11704,15 +12118,15 @@ function mcpOutputs(spec) {
11704
12118
 
11705
12119
  // src/project/hooks.ts
11706
12120
  import { createHash as createHash12 } from "crypto";
11707
- import { existsSync as existsSync15, readFileSync as readFileSync22 } from "fs";
11708
- import { join as join20 } from "path";
12121
+ import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
12122
+ import { join as join22 } from "path";
11709
12123
  import { fileURLToPath as fileURLToPath2 } from "url";
11710
12124
 
11711
12125
  // src/project/hook-adapter.ts
11712
12126
  import { spawnSync as spawnSync2 } from "child_process";
11713
12127
  import { createHash as createHash11 } from "crypto";
11714
- import { existsSync as existsSync14, readFileSync as readFileSync21, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
11715
- import { dirname as dirname15, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve13 } from "path";
12128
+ import { existsSync as existsSync17, readFileSync as readFileSync22, realpathSync as realpathSync5, statSync as statSync10 } from "fs";
12129
+ import { dirname as dirname16, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve12 } from "path";
11716
12130
  var denied = () => ({
11717
12131
  decision: "deny",
11718
12132
  reason: "Shell guard failed validation or execution"
@@ -11722,16 +12136,16 @@ function guardPath(root, command2) {
11722
12136
  if (!script?.startsWith(".agents/guards/")) {
11723
12137
  throw new Error("Hook command must execute a script under .agents/guards/");
11724
12138
  }
11725
- const absolute2 = resolve13(root, script);
11726
- const base = resolve13(root, ".agents/guards");
12139
+ const absolute2 = resolve12(root, script);
12140
+ const base = resolve12(root, ".agents/guards");
11727
12141
  const inside = relative10(base, absolute2);
11728
12142
  if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
11729
12143
  throw new Error("Hook guard must stay under .agents/guards/");
11730
12144
  }
11731
- if (!existsSync14(absolute2) || !statSync10(absolute2).isFile()) {
12145
+ if (!existsSync17(absolute2) || !statSync10(absolute2).isFile()) {
11732
12146
  throw new Error("Hook guard script must exist");
11733
12147
  }
11734
- const actual = relative10(base, realpathSync3(absolute2));
12148
+ const actual = relative10(base, realpathSync5(absolute2));
11735
12149
  if (isAbsolute6(actual) || actual === ".." || actual.startsWith("../") || actual.startsWith("..\\")) {
11736
12150
  throw new Error("Hook guard cannot resolve outside .agents/guards/");
11737
12151
  }
@@ -11849,8 +12263,8 @@ async function main() {
11849
12263
  if (!file2) {
11850
12264
  throw new Error("Missing hook policy");
11851
12265
  }
11852
- const bytes = readFileSync21(file2);
11853
- const adapter = readFileSync21(process.argv[1]);
12266
+ const bytes = readFileSync22(file2);
12267
+ const adapter = readFileSync22(process.argv[1]);
11854
12268
  const actual = createHash11("sha256").update(adapter).update(bytes).digest("hex");
11855
12269
  if (actual !== integrity2) {
11856
12270
  throw new Error("Hook integrity mismatch");
@@ -11869,8 +12283,8 @@ async function main() {
11869
12283
  }
11870
12284
  chunks.push(bytes2);
11871
12285
  }
11872
- const root = resolve13(dirname15(file2), "../..");
11873
- const guardSha256 = createHash11("sha256").update(readFileSync21(guardPath(root, policy.command))).digest("hex");
12286
+ const root = resolve12(dirname16(file2), "../..");
12287
+ const guardSha256 = createHash11("sha256").update(readFileSync22(guardPath(root, policy.command))).digest("hex");
11874
12288
  if (policy.contractVersion !== 1 || guardSha256 !== policy.guardSha256) {
11875
12289
  throw new Error(
11876
12290
  "Hook guard source changed; reproject and review native trust"
@@ -11923,13 +12337,13 @@ function object4(value) {
11923
12337
  return value;
11924
12338
  }
11925
12339
  function groups(root, file2, key) {
11926
- const absolute2 = join20(root, file2);
11927
- if (!existsSync15(absolute2)) {
12340
+ const absolute2 = join22(root, file2);
12341
+ if (!existsSync18(absolute2)) {
11928
12342
  return [];
11929
12343
  }
11930
12344
  let value;
11931
12345
  try {
11932
- value = JSON.parse(readFileSync22(absolute2, "utf8"));
12346
+ value = JSON.parse(readFileSync23(absolute2, "utf8"));
11933
12347
  } catch {
11934
12348
  throw new ProjectError(
11935
12349
  "Cannot parse native hook configuration; contents are redacted"
@@ -11953,15 +12367,15 @@ function adapterSource() {
11953
12367
  const sourceBuild = fileURLToPath2(
11954
12368
  new URL("../../dist/project/hook-adapter.js", import.meta.url)
11955
12369
  );
11956
- const file2 = existsSync15(published) ? published : sourceBuild;
11957
- if (!existsSync15(file2)) {
12370
+ const file2 = existsSync18(published) ? published : sourceBuild;
12371
+ if (!existsSync18(file2)) {
11958
12372
  throw new ProjectError("Build pmcp before projecting shell hook adapters");
11959
12373
  }
11960
- return readFileSync22(file2, "utf8");
12374
+ return readFileSync23(file2, "utf8");
11961
12375
  }
11962
12376
  function hookOutputs(spec) {
11963
- const lockFile = join20(spec.root, "pmcp.lock");
11964
- const lock = existsSync15(lockFile) ? JSON.parse(readFileSync22(lockFile, "utf8")) : {};
12377
+ const lockFile = join22(spec.root, "pmcp.lock");
12378
+ const lock = existsSync18(lockFile) ? JSON.parse(readFileSync23(lockFile, "utf8")) : {};
11965
12379
  const previous = new Map(
11966
12380
  (lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
11967
12381
  );
@@ -11978,7 +12392,7 @@ function hookOutputs(spec) {
11978
12392
  outputs.push({ kind: "file", path: ADAPTER, content: adapter });
11979
12393
  for (const hook of selected) {
11980
12394
  const policyFile = `${HOOK_DIRECTORY}/${hook.name}.json`;
11981
- const guardSha256 = createHash12("sha256").update(readFileSync22(guardPath(spec.root, hook.command))).digest("hex");
12395
+ const guardSha256 = createHash12("sha256").update(readFileSync23(guardPath(spec.root, hook.command))).digest("hex");
11982
12396
  const policy = `${JSON.stringify({ ...hook, contractVersion: 1, guardSha256 }, null, 2)}
11983
12397
  `;
11984
12398
  outputs.push({ kind: "file", path: policyFile, content: policy });
@@ -12042,11 +12456,11 @@ function hookOutputs(spec) {
12042
12456
  }
12043
12457
 
12044
12458
  // src/project/frontmatter.ts
12045
- import { parse as parse3 } from "yaml";
12459
+ import { parse as parse4 } from "yaml";
12046
12460
  function splitFrontmatter(source2) {
12047
12461
  const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/u.exec(source2);
12048
12462
  if (!match) return { meta: {}, body: source2 };
12049
- const meta = parse3(match[1] ?? "");
12463
+ const meta = parse4(match[1] ?? "");
12050
12464
  const object9 = meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta : {};
12051
12465
  return { meta: object9, body: match[2] ?? "" };
12052
12466
  }
@@ -12076,28 +12490,28 @@ ${lines2.join("\n")}
12076
12490
  }
12077
12491
 
12078
12492
  // src/project/plan.ts
12079
- import { existsSync as existsSync16, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "fs";
12080
- import { join as join21 } from "path";
12493
+ import { existsSync as existsSync19, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
12494
+ import { join as join23 } from "path";
12081
12495
  var AGENT_FIELDS = {
12082
12496
  claude: ["name", "description", "tools", "model", "effort"],
12083
12497
  gemini: ["name", "description"]
12084
12498
  };
12085
12499
  var SETTINGS = ".claude/settings.json";
12086
12500
  function listDirs(dir) {
12087
- if (!existsSync16(dir)) return [];
12501
+ if (!existsSync19(dir)) return [];
12088
12502
  return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
12089
12503
  }
12090
12504
  var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
12091
12505
  function skillNames(root) {
12092
- const dir = join21(root, ".agents/skills");
12506
+ const dir = join23(root, ".agents/skills");
12093
12507
  return listDirs(dir).filter(
12094
- (name) => existsSync16(join21(dir, name, "SKILL.md"))
12508
+ (name) => existsSync19(join23(dir, name, "SKILL.md"))
12095
12509
  );
12096
12510
  }
12097
12511
  function agentNames(root) {
12098
- const dir = join21(root, ".agents/agents");
12512
+ const dir = join23(root, ".agents/agents");
12099
12513
  return listDirs(dir).filter(
12100
- (name) => existsSync16(join21(dir, name, "agent.md"))
12514
+ (name) => existsSync19(join23(dir, name, "agent.md"))
12101
12515
  );
12102
12516
  }
12103
12517
  function skillOutputs(spec) {
@@ -12131,13 +12545,13 @@ policy:
12131
12545
  }
12132
12546
  function ruleOutputs(spec) {
12133
12547
  if (!spec.tools.has("claude")) return [];
12134
- const dir = join21(spec.root, ".agents/rules");
12135
- if (!existsSync16(dir)) return [];
12548
+ const dir = join23(spec.root, ".agents/rules");
12549
+ if (!existsSync19(dir)) return [];
12136
12550
  const outputs = [];
12137
12551
  for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
12138
12552
  const rel = `.agents/rules/${file2}`;
12139
12553
  const { meta, body } = splitFrontmatter(
12140
- readFileSync23(join21(spec.root, rel), "utf8")
12554
+ readFileSync24(join23(spec.root, rel), "utf8")
12141
12555
  );
12142
12556
  const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
12143
12557
  const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
@@ -12156,7 +12570,7 @@ function agentOutputs(spec) {
12156
12570
  for (const name of agentNames(spec.root)) {
12157
12571
  const rel = `.agents/agents/${name}/agent.md`;
12158
12572
  const { meta, body } = splitFrontmatter(
12159
- readFileSync23(join21(spec.root, rel), "utf8")
12573
+ readFileSync24(join23(spec.root, rel), "utf8")
12160
12574
  );
12161
12575
  const overrides = spec.agents[name] ?? {};
12162
12576
  const note = `<!-- ${MARK} from ${rel} -->
@@ -12225,6 +12639,67 @@ function censusDirs(spec) {
12225
12639
  return dirs;
12226
12640
  }
12227
12641
 
12642
+ // src/project/write.ts
12643
+ import { randomUUID as randomUUID4 } from "crypto";
12644
+ import {
12645
+ closeSync as closeSync3,
12646
+ fchmodSync as fchmodSync2,
12647
+ fsyncSync,
12648
+ lstatSync as lstatSync4,
12649
+ openSync as openSync3,
12650
+ renameSync as renameSync3,
12651
+ rmSync as rmSync4,
12652
+ symlinkSync,
12653
+ writeFileSync as writeFileSync5
12654
+ } from "fs";
12655
+ import { dirname as dirname17 } from "path";
12656
+ function fileMode(path) {
12657
+ try {
12658
+ const stat = lstatSync4(path);
12659
+ if (stat.isFile()) return stat.mode & 511;
12660
+ } catch (error) {
12661
+ if (error.code !== "ENOENT") throw error;
12662
+ }
12663
+ return 438 & ~process.umask();
12664
+ }
12665
+ function syncDirectory(path) {
12666
+ if (process.platform === "win32") return;
12667
+ const descriptor = openSync3(dirname17(path), "r");
12668
+ try {
12669
+ fsyncSync(descriptor);
12670
+ } finally {
12671
+ closeSync3(descriptor);
12672
+ }
12673
+ }
12674
+ function replaceFile(path, content) {
12675
+ const mode = fileMode(path);
12676
+ const temporary = `${path}.${randomUUID4()}.new`;
12677
+ const descriptor = openSync3(temporary, "wx", 384);
12678
+ try {
12679
+ try {
12680
+ writeFileSync5(descriptor, content);
12681
+ fchmodSync2(descriptor, mode);
12682
+ fsyncSync(descriptor);
12683
+ } finally {
12684
+ closeSync3(descriptor);
12685
+ }
12686
+ renameSync3(temporary, path);
12687
+ syncDirectory(path);
12688
+ } finally {
12689
+ rmSync4(temporary, { force: true });
12690
+ }
12691
+ }
12692
+ function replaceLink(path, target2) {
12693
+ const temporary = `${path}.${randomUUID4()}.new`;
12694
+ try {
12695
+ symlinkSync(target2, temporary);
12696
+ renameSync3(temporary, path);
12697
+ syncDirectory(path);
12698
+ } finally {
12699
+ rmSync4(temporary, { force: true });
12700
+ }
12701
+ }
12702
+
12228
12703
  // src/project/apply.ts
12229
12704
  var LOCK_FILE = "pmcp.lock";
12230
12705
  var BEGIN = "# >>> pmcp project (edit pmcp.toml, not this block) >>>";
@@ -12235,24 +12710,24 @@ var PREDECESSORS = [
12235
12710
  ];
12236
12711
  var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
12237
12712
  function readLock(root) {
12238
- const path = join22(root, LOCK_FILE);
12239
- if (!existsSync17(path)) return [];
12240
- const lock = JSON.parse(readFileSync24(path, "utf8"));
12713
+ const path = join24(root, LOCK_FILE);
12714
+ if (!existsSync20(path)) return [];
12715
+ const lock = JSON.parse(readFileSync25(path, "utf8"));
12241
12716
  return lock.outputs ?? [];
12242
12717
  }
12243
12718
  function linkOf(abs) {
12244
12719
  try {
12245
- return lstatSync4(abs).isSymbolicLink() ? readlinkSync2(abs) : null;
12720
+ return lstatSync5(abs).isSymbolicLink() ? readlinkSync2(abs) : null;
12246
12721
  } catch {
12247
12722
  return null;
12248
12723
  }
12249
12724
  }
12250
- var exists = (abs) => existsSync17(abs) || linkOf(abs) !== null;
12725
+ var exists = (abs) => existsSync20(abs) || linkOf(abs) !== null;
12251
12726
  function readJson2(abs) {
12252
- if (!existsSync17(abs)) return {};
12727
+ if (!existsSync20(abs)) return {};
12253
12728
  let value;
12254
12729
  try {
12255
- value = JSON.parse(readFileSync24(abs, "utf8"));
12730
+ value = JSON.parse(readFileSync25(abs, "utf8"));
12256
12731
  } catch {
12257
12732
  throw new ProjectError(
12258
12733
  "Cannot parse native JSON configuration; contents are redacted"
@@ -12304,19 +12779,19 @@ function withoutBlock(text3) {
12304
12779
  return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
12305
12780
  }
12306
12781
  function present(root, output) {
12307
- const abs = join22(root, "file" in output ? output.file : output.path);
12782
+ const abs = join24(root, "file" in output ? output.file : output.path);
12308
12783
  switch (output.kind) {
12309
12784
  case "link":
12310
12785
  return linkOf(abs) === output.target;
12311
12786
  case "file":
12312
- return existsSync17(abs) && linkOf(abs) === null && readFileSync24(abs, "utf8") === output.content;
12787
+ return existsSync20(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
12313
12788
  case "json":
12314
12789
  if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
12315
12790
  return getKey(readJson2(abs), output.key) === void 0;
12316
12791
  }
12317
12792
  return same(getKey(readJson2(abs), output.key), output.value);
12318
12793
  case "block":
12319
- return existsSync17(abs) && readFileSync24(abs, "utf8").includes(block(output.content));
12794
+ return existsSync20(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
12320
12795
  }
12321
12796
  }
12322
12797
  function replaceable(root, output, owned) {
@@ -12324,28 +12799,28 @@ function replaceable(root, output, owned) {
12324
12799
  if (output.key[0] !== "mcpServers" || owned.has(output.path)) {
12325
12800
  return true;
12326
12801
  }
12327
- const existing = getKey(readJson2(join22(root, output.file)), output.key);
12802
+ const existing = getKey(readJson2(join24(root, output.file)), output.key);
12328
12803
  return existing === void 0 || same(existing, output.value);
12329
12804
  }
12330
12805
  if (output.kind === "block") {
12331
- const absolute2 = join22(root, output.file);
12332
- const text4 = existsSync17(absolute2) ? readFileSync24(absolute2, "utf8") : "";
12333
- const outside = parse4(withoutBlock(text4));
12334
- const proposed = parse4(output.content);
12806
+ const absolute2 = join24(root, output.file);
12807
+ const text4 = existsSync20(absolute2) ? readFileSync25(absolute2, "utf8") : "";
12808
+ const outside = parse5(withoutBlock(text4));
12809
+ const proposed = parse5(output.content);
12335
12810
  const existing = objectKeys(outside["mcp_servers"]);
12336
12811
  return !objectKeys(proposed["mcp_servers"]).some(
12337
12812
  (name) => existing.includes(name)
12338
12813
  );
12339
12814
  }
12340
12815
  if (owned.has(output.path)) return true;
12341
- const abs = join22(root, output.path);
12816
+ const abs = join24(root, output.path);
12342
12817
  if (!exists(abs)) return true;
12343
12818
  const target2 = linkOf(abs);
12344
12819
  if (target2 !== null) {
12345
12820
  return output.kind === "link" && target2 === output.target;
12346
12821
  }
12347
- if (lstatSync4(abs).isDirectory()) return false;
12348
- const text3 = readFileSync24(abs, "utf8");
12822
+ if (lstatSync5(abs).isDirectory()) return false;
12823
+ const text3 = readFileSync25(abs, "utf8");
12349
12824
  if (output.kind === "file" && text3 === output.content) return true;
12350
12825
  return PREDECESSORS.some((mark) => text3.includes(mark));
12351
12826
  }
@@ -12359,15 +12834,15 @@ function undeclared(spec, outputs) {
12359
12834
  const declared = new Set(outputs.map((o) => o.path));
12360
12835
  const found = [];
12361
12836
  for (const dir of censusDirs(spec)) {
12362
- const abs = join22(spec.root, dir);
12363
- if (!existsSync17(abs)) continue;
12837
+ const abs = join24(spec.root, dir);
12838
+ if (!existsSync20(abs)) continue;
12364
12839
  for (const entry2 of readdirSync9(abs)) {
12365
12840
  const path = `${dir}/${entry2}`;
12366
12841
  if (!declared.has(path)) found.push(path);
12367
12842
  }
12368
12843
  }
12369
- const rules = join22(spec.root, RULES_DIR);
12370
- if (spec.tools.has("grok") && existsSync17(rules)) {
12844
+ const rules = join24(spec.root, RULES_DIR);
12845
+ if (spec.tools.has("grok") && existsSync20(rules)) {
12371
12846
  for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
12372
12847
  if (entry2.isDirectory()) continue;
12373
12848
  found.push(`${RULES_DIR}/${entry2.name}`);
@@ -12394,9 +12869,9 @@ function deadGlobs(root, outputs) {
12394
12869
  if (rule.kind === "file") {
12395
12870
  text3 = rule.content;
12396
12871
  } else if (rule.kind === "link") {
12397
- const abs = join22(root, posix2.join(posix2.dirname(rule.path), rule.target));
12398
- if (!existsSync17(abs)) continue;
12399
- text3 = readFileSync24(abs, "utf8");
12872
+ const abs = join24(root, posix2.join(posix2.dirname(rule.path), rule.target));
12873
+ if (!existsSync20(abs)) continue;
12874
+ text3 = readFileSync25(abs, "utf8");
12400
12875
  } else {
12401
12876
  continue;
12402
12877
  }
@@ -12446,7 +12921,7 @@ function project(spec, options2) {
12446
12921
  }
12447
12922
  }
12448
12923
  for (const path of jsonPaths) {
12449
- readJson2(join22(spec.root, path));
12924
+ readJson2(join24(spec.root, path));
12450
12925
  }
12451
12926
  if (options2.check) {
12452
12927
  const problems = [
@@ -12508,11 +12983,13 @@ function project(spec, options2) {
12508
12983
  for (const output of outputs) {
12509
12984
  if (output.kind !== "link" && output.kind !== "file") continue;
12510
12985
  if (present(spec.root, output)) continue;
12511
- const abs = join22(spec.root, output.path);
12512
- mkdirSync7(dirname16(abs), { recursive: true });
12513
- rmSync4(abs, { force: true, recursive: true });
12514
- if (output.kind === "link") symlinkSync(output.target, abs);
12515
- else writeFileSync5(abs, output.content);
12986
+ const abs = join24(spec.root, output.path);
12987
+ mkdirSync7(dirname18(abs), { recursive: true });
12988
+ if (output.kind === "link") {
12989
+ replaceLink(abs, output.target);
12990
+ continue;
12991
+ }
12992
+ replaceFile(abs, output.content);
12516
12993
  }
12517
12994
  let pruned = 0;
12518
12995
  const jsonFiles = /* @__PURE__ */ new Map();
@@ -12534,12 +13011,12 @@ function project(spec, options2) {
12534
13011
  group.drops.push(keyed.key);
12535
13012
  jsonFiles.set(keyed.file, group);
12536
13013
  } else {
12537
- rmSync4(join22(spec.root, entry2.path), { force: true, recursive: true });
13014
+ rmSync5(join24(spec.root, entry2.path), { force: true, recursive: true });
12538
13015
  }
12539
13016
  pruned += 1;
12540
13017
  }
12541
13018
  for (const [file2, { sets, drops }] of jsonFiles) {
12542
- const abs = join22(spec.root, file2);
13019
+ const abs = join24(spec.root, file2);
12543
13020
  const before = readJson2(abs);
12544
13021
  const after = structuredClone(before);
12545
13022
  for (const key of drops) deleteKey(after, key);
@@ -12554,22 +13031,22 @@ function project(spec, options2) {
12554
13031
  }
12555
13032
  }
12556
13033
  if (same(before, after)) continue;
12557
- if (!existsSync17(abs) && Object.keys(after).length === 0) continue;
12558
- mkdirSync7(dirname16(abs), { recursive: true });
12559
- writeFileSync5(abs, `${JSON.stringify(after, null, 2)}
13034
+ if (!existsSync20(abs) && Object.keys(after).length === 0) continue;
13035
+ mkdirSync7(dirname18(abs), { recursive: true });
13036
+ replaceFile(abs, `${JSON.stringify(after, null, 2)}
12560
13037
  `);
12561
13038
  }
12562
13039
  for (const [file2, content] of blockFiles) {
12563
- const abs = join22(spec.root, file2);
12564
- const before = existsSync17(abs) ? readFileSync24(abs, "utf8") : "";
13040
+ const abs = join24(spec.root, file2);
13041
+ const before = existsSync20(abs) ? readFileSync25(abs, "utf8") : "";
12565
13042
  let after = withoutBlock(before);
12566
13043
  if (content !== null) {
12567
13044
  const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
12568
13045
  after = `${after}${sep7}${block(content)}`;
12569
13046
  }
12570
13047
  if (after === before) continue;
12571
- mkdirSync7(dirname16(abs), { recursive: true });
12572
- writeFileSync5(abs, after);
13048
+ mkdirSync7(dirname18(abs), { recursive: true });
13049
+ replaceFile(abs, after);
12573
13050
  }
12574
13051
  if (spec.packageRules) {
12575
13052
  const linkedDirs = new Set(
@@ -12580,15 +13057,15 @@ function project(spec, options2) {
12580
13057
  )
12581
13058
  );
12582
13059
  for (const [name, target2] of legacyLinks(spec.root)) {
12583
- const abs = join22(spec.root, RULES_DIR, `${name}.md`);
12584
- const broken = !existsSync17(abs);
13060
+ const abs = join24(spec.root, RULES_DIR, `${name}.md`);
13061
+ const broken = !existsSync20(abs);
12585
13062
  if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
12586
- rmSync4(abs, { force: true });
13063
+ rmSync5(abs, { force: true });
12587
13064
  pruned += 1;
12588
13065
  }
12589
13066
  }
12590
- writeFileSync5(
12591
- join22(spec.root, LOCK_FILE),
13067
+ replaceFile(
13068
+ join24(spec.root, LOCK_FILE),
12592
13069
  `${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
12593
13070
  `
12594
13071
  );
@@ -12602,11 +13079,11 @@ function project(spec, options2) {
12602
13079
  }
12603
13080
 
12604
13081
  // src/project/mcp-import.ts
12605
- import { readFileSync as readFileSync25, statSync as statSync11, writeFileSync as writeFileSync6 } from "fs";
13082
+ import { readFileSync as readFileSync26, statSync as statSync11, writeFileSync as writeFileSync6 } from "fs";
12606
13083
  import { createHash as createHash14 } from "crypto";
12607
- import { basename as basename6, dirname as dirname17, resolve as resolve14 } from "path";
13084
+ import { basename as basename6, dirname as dirname19, resolve as resolve13 } from "path";
12608
13085
  import { homedir as homedir4 } from "os";
12609
- import { parse as parse5, stringify } from "smol-toml";
13086
+ import { parse as parse6, stringify } from "smol-toml";
12610
13087
 
12611
13088
  // src/project/mcp-spec.ts
12612
13089
  init_config();
@@ -13034,11 +13511,11 @@ function sourceServers(source2, root) {
13034
13511
  "Use --source <claude|codex|gemini|grok|antigravity>:<file>"
13035
13512
  );
13036
13513
  }
13037
- const path = resolve14(root, source2.slice(split + 1));
13514
+ const path = resolve13(root, source2.slice(split + 1));
13038
13515
  if (statSync11(path).size > 2 * 1024 * 1024) {
13039
13516
  throw new ProjectError("MCP source exceeds the 2 MiB limit");
13040
13517
  }
13041
- const bytes = readFileSync25(path);
13518
+ const bytes = readFileSync26(path);
13042
13519
  const sha2562 = createHash14("sha256").update(bytes).digest("hex");
13043
13520
  const text3 = bytes.toString("utf8").replace(/^\uFEFF/u, "");
13044
13521
  let parsed;
@@ -13046,7 +13523,7 @@ function sourceServers(source2, root) {
13046
13523
  if (text3.trim() === "") {
13047
13524
  parsed = {};
13048
13525
  } else if (path.endsWith(".toml")) {
13049
- parsed = parse5(text3);
13526
+ parsed = parse6(text3);
13050
13527
  } else {
13051
13528
  parsed = object6(JSON.parse(text3));
13052
13529
  }
@@ -13229,9 +13706,9 @@ function equivalent(alias, left, right) {
13229
13706
  return JSON.stringify(readMcp("import", { [alias]: left })[alias]) === JSON.stringify(readMcp("import", { [alias]: right })[alias]);
13230
13707
  }
13231
13708
  function planMcpImport(config, sources2, names = []) {
13232
- const original = readFileSync25(config, "utf8");
13233
- const root = dirname17(resolve14(config));
13234
- const existing = object6(parse5(original)["mcp"] ?? {});
13709
+ const original = readFileSync26(config, "utf8");
13710
+ const root = dirname19(resolve13(config));
13711
+ const existing = object6(parse6(original)["mcp"] ?? {});
13235
13712
  const additions = {};
13236
13713
  const entries = [];
13237
13714
  const fingerprints = [];
@@ -13280,11 +13757,11 @@ function writeMcpImport(plan2) {
13280
13757
  "Resolve blocked or conflicting MCP entries before importing"
13281
13758
  );
13282
13759
  }
13283
- if (readFileSync25(plan2.config, "utf8") !== plan2.original) {
13760
+ if (readFileSync26(plan2.config, "utf8") !== plan2.original) {
13284
13761
  throw new ProjectError("pmcp.toml changed after planning; plan again");
13285
13762
  }
13286
13763
  for (const source2 of plan2.sources) {
13287
- const current = createHash14("sha256").update(readFileSync25(source2.path)).digest("hex");
13764
+ const current = createHash14("sha256").update(readFileSync26(source2.path)).digest("hex");
13288
13765
  if (current !== source2.sha256) {
13289
13766
  throw new ProjectError("MCP source changed after planning; plan again");
13290
13767
  }
@@ -13293,8 +13770,8 @@ function writeMcpImport(plan2) {
13293
13770
  const after = `${plan2.original.trimEnd()}
13294
13771
 
13295
13772
  ${additions}`;
13296
- parse5(after);
13297
- const backup = backupFiles(dirname17(resolve14(plan2.config)), [
13773
+ parse6(after);
13774
+ const backup = backupFiles(dirname19(resolve13(plan2.config)), [
13298
13775
  basename6(plan2.config)
13299
13776
  ]);
13300
13777
  writeFileSync6(plan2.config, after);
@@ -13303,25 +13780,25 @@ ${additions}`;
13303
13780
 
13304
13781
  // src/project/mcp-env.ts
13305
13782
  import { spawn } from "child_process";
13306
- import { readFileSync as readFileSync26, statSync as statSync12 } from "fs";
13783
+ import { readFileSync as readFileSync27, statSync as statSync12 } from "fs";
13307
13784
  import { homedir as homedir5 } from "os";
13308
- import { resolve as resolve15 } from "path";
13309
- import { parse as parse6 } from "smol-toml";
13785
+ import { resolve as resolve14 } from "path";
13786
+ import { parse as parse7 } from "smol-toml";
13310
13787
  function sourcePath(file2, root) {
13311
13788
  if (file2.startsWith("~/")) {
13312
- return resolve15(homedir5(), file2.slice(2));
13789
+ return resolve14(homedir5(), file2.slice(2));
13313
13790
  }
13314
- return resolve15(root, file2.replaceAll("${PROJECT_ROOT}", root));
13791
+ return resolve14(root, file2.replaceAll("${PROJECT_ROOT}", root));
13315
13792
  }
13316
13793
  function sourceValue(file2, pointer) {
13317
13794
  try {
13318
13795
  if (statSync12(file2).size > 2 * 1024 * 1024) {
13319
13796
  throw new Error("size");
13320
13797
  }
13321
- const text3 = readFileSync26(file2, "utf8").replace(/^\uFEFF/u, "");
13798
+ const text3 = readFileSync27(file2, "utf8").replace(/^\uFEFF/u, "");
13322
13799
  let value;
13323
13800
  if (file2.endsWith(".toml")) {
13324
- value = parse6(text3);
13801
+ value = parse7(text3);
13325
13802
  } else {
13326
13803
  value = JSON.parse(text3);
13327
13804
  }
@@ -13399,9 +13876,9 @@ async function execMcp(spec, alias, env) {
13399
13876
 
13400
13877
  // src/project/doctor.ts
13401
13878
  import { spawnSync as spawnSync3 } from "child_process";
13402
- import { existsSync as existsSync18, readFileSync as readFileSync27 } from "fs";
13403
- import { join as join23 } from "path";
13404
- import { parse as parse7 } from "smol-toml";
13879
+ import { existsSync as existsSync21, readFileSync as readFileSync28 } from "fs";
13880
+ import { join as join25 } from "path";
13881
+ import { parse as parse8 } from "smol-toml";
13405
13882
  var spawnRunner = (command2, args, cwd, env) => {
13406
13883
  const result = spawnSync3(command2, [...args], {
13407
13884
  cwd,
@@ -13477,10 +13954,10 @@ function claude(spec, o) {
13477
13954
  }
13478
13955
  function codex(spec, o) {
13479
13956
  const findings = [];
13480
- const config = join23(o.home, ".codex/config.toml");
13957
+ const config = join25(o.home, ".codex/config.toml");
13481
13958
  let trusted = false;
13482
- if (existsSync18(config)) {
13483
- const projects = parse7(readFileSync27(config, "utf8")).projects ?? {};
13959
+ if (existsSync21(config)) {
13960
+ const projects = parse8(readFileSync28(config, "utf8")).projects ?? {};
13484
13961
  const root = spec.root.replace(/\/$/u, "");
13485
13962
  trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
13486
13963
  }
@@ -13606,8 +14083,8 @@ function grok(spec, o) {
13606
14083
  }
13607
14084
  function antigravity(spec, options2) {
13608
14085
  const foreign = agentNames(spec.root).filter((name) => {
13609
- const text3 = readFileSync27(
13610
- join23(spec.root, ".agents/agents", name, "agent.md"),
14086
+ const text3 = readFileSync28(
14087
+ join25(spec.root, ".agents/agents", name, "agent.md"),
13611
14088
  "utf8"
13612
14089
  );
13613
14090
  return splitFrontmatter(text3).meta["tools"] !== void 0;
@@ -13627,7 +14104,7 @@ function antigravity(spec, options2) {
13627
14104
  if (serversForTool(spec, "antigravity").length > 0) {
13628
14105
  const result = options2.run(
13629
14106
  "agy",
13630
- ["plugin", "validate", join23(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14107
+ ["plugin", "validate", join25(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
13631
14108
  spec.root
13632
14109
  );
13633
14110
  findings.push(
@@ -13658,9 +14135,9 @@ function doctor(spec, options2) {
13658
14135
  }
13659
14136
 
13660
14137
  // src/project/memory.ts
13661
- import { existsSync as existsSync19, readFileSync as readFileSync28 } from "fs";
13662
- import { isAbsolute as isAbsolute7, join as join24, resolve as resolve16 } from "path";
13663
- import { parse as parse8 } from "smol-toml";
14138
+ import { existsSync as existsSync22, readFileSync as readFileSync29 } from "fs";
14139
+ import { isAbsolute as isAbsolute7, join as join26, resolve as resolve15 } from "path";
14140
+ import { parse as parse9 } from "smol-toml";
13664
14141
  function object7(value) {
13665
14142
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
13666
14143
  throw new Error("Configuration is not an object");
@@ -13668,11 +14145,11 @@ function object7(value) {
13668
14145
  return value;
13669
14146
  }
13670
14147
  function settings(file2, toml = false) {
13671
- if (!existsSync19(file2)) {
14148
+ if (!existsSync22(file2)) {
13672
14149
  return {};
13673
14150
  }
13674
- const text3 = readFileSync28(file2, "utf8");
13675
- return object7(toml ? parse8(text3) : JSON.parse(text3));
14151
+ const text3 = readFileSync29(file2, "utf8");
14152
+ return object7(toml ? parse9(text3) : JSON.parse(text3));
13676
14153
  }
13677
14154
  function configuredState(value) {
13678
14155
  if (value === true) {
@@ -13684,11 +14161,11 @@ function configuredState(value) {
13684
14161
  return "unknown";
13685
14162
  }
13686
14163
  function located(metadata2, path, pathScope) {
13687
- return { ...metadata2, path, pathScope, exists: existsSync19(path) };
14164
+ return { ...metadata2, path, pathScope, exists: existsSync22(path) };
13688
14165
  }
13689
14166
  function metadata(tool, spec, options2) {
13690
14167
  const env = options2.env ?? {};
13691
- const directory = (name, fallback) => resolve16(spec.root, env[name] ?? join24(options2.home, fallback));
14168
+ const directory = (name, fallback) => resolve15(spec.root, env[name] ?? join26(options2.home, fallback));
13692
14169
  if (tool === "antigravity") {
13693
14170
  return {
13694
14171
  state: "unsupported",
@@ -13712,7 +14189,7 @@ function metadata(tool, spec, options2) {
13712
14189
  state,
13713
14190
  source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
13714
14191
  },
13715
- join24(home2, "memories"),
14192
+ join26(home2, "memories"),
13716
14193
  "store"
13717
14194
  );
13718
14195
  }
@@ -13725,12 +14202,12 @@ function metadata(tool, spec, options2) {
13725
14202
  }
13726
14203
  if (tool === "claude") {
13727
14204
  const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
13728
- const user2 = settings(join24(home2, "settings.json"));
14205
+ const user2 = settings(join26(home2, "settings.json"));
13729
14206
  const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
13730
14207
  const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
13731
14208
  const override = user2["autoMemoryDirectory"];
13732
14209
  if (typeof override === "string") {
13733
- const path = override.startsWith("~/") ? join24(options2.home, override.slice(2)) : override;
14210
+ const path = override.startsWith("~/") ? join26(options2.home, override.slice(2)) : override;
13734
14211
  if (!isAbsolute7(path)) {
13735
14212
  return {
13736
14213
  state: "unknown",
@@ -13743,7 +14220,7 @@ function metadata(tool, spec, options2) {
13743
14220
  if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
13744
14221
  return located(
13745
14222
  { state, source: source2 },
13746
- join24(home2, "projects", projectName, "memory"),
14223
+ join26(home2, "projects", projectName, "memory"),
13747
14224
  "store"
13748
14225
  );
13749
14226
  }
@@ -13752,14 +14229,14 @@ function metadata(tool, spec, options2) {
13752
14229
  state,
13753
14230
  source: `${source2}; native project directory encoding is not guessed`
13754
14231
  },
13755
- join24(home2, "projects"),
14232
+ join26(home2, "projects"),
13756
14233
  "base"
13757
14234
  );
13758
14235
  }
13759
14236
  if (tool === "grok") {
13760
- const home2 = join24(options2.home, ".grok");
13761
- const user2 = settings(join24(home2, "config.toml"), true);
13762
- const project2 = settings(join24(spec.root, ".grok/config.toml"), true);
14237
+ const home2 = join26(options2.home, ".grok");
14238
+ const user2 = settings(join26(home2, "config.toml"), true);
14239
+ const project2 = settings(join26(spec.root, ".grok/config.toml"), true);
13763
14240
  const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
13764
14241
  const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
13765
14242
  const overridden = projectMemory["enabled"] !== void 0;
@@ -13769,12 +14246,12 @@ function metadata(tool, spec, options2) {
13769
14246
  state,
13770
14247
  source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
13771
14248
  },
13772
- join24(home2, "memory-v2/workspaces"),
14249
+ join26(home2, "memory-v2/workspaces"),
13773
14250
  "base"
13774
14251
  );
13775
14252
  }
13776
14253
  const home = directory("GEMINI_CLI_HOME", ".gemini");
13777
- const user = settings(join24(home, "settings.json"));
14254
+ const user = settings(join26(home, "settings.json"));
13778
14255
  const context = user["context"] === void 0 ? {} : object7(user["context"]);
13779
14256
  const names = context["fileName"] ?? "GEMINI.md";
13780
14257
  const contextFileNames = typeof names === "string" ? [names] : names;
@@ -13790,7 +14267,7 @@ function metadata(tool, spec, options2) {
13790
14267
  source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
13791
14268
  contextFileNames
13792
14269
  },
13793
- join24(home, "GEMINI.md"),
14270
+ join26(home, "GEMINI.md"),
13794
14271
  "store"
13795
14272
  );
13796
14273
  }
@@ -13823,14 +14300,14 @@ function memoryFindings(spec, options2) {
13823
14300
  // src/project/edit.ts
13824
14301
  import {
13825
14302
  cpSync,
13826
- existsSync as existsSync20,
14303
+ existsSync as existsSync23,
13827
14304
  mkdirSync as mkdirSync8,
13828
- readFileSync as readFileSync29,
13829
- rmSync as rmSync5,
14305
+ readFileSync as readFileSync30,
14306
+ rmSync as rmSync6,
13830
14307
  statSync as statSync13,
13831
14308
  writeFileSync as writeFileSync7
13832
14309
  } from "fs";
13833
- import { basename as basename7, dirname as dirname18, join as join25 } from "path";
14310
+ import { basename as basename7, dirname as dirname20, join as join27 } from "path";
13834
14311
  var CROSS_TOOL = ["name", "description"];
13835
14312
  var tomlValue2 = (value) => JSON.stringify(value);
13836
14313
  function findEntry(entries, wanted) {
@@ -13854,7 +14331,7 @@ function addEntry(root, entry2) {
13854
14331
  `${entry2.name} is a ${kind2}; only skills and agents are added to .agents/`
13855
14332
  );
13856
14333
  }
13857
- const { meta, body } = splitFrontmatter(readFileSync29(entry2.path, "utf8"));
14334
+ const { meta, body } = splitFrontmatter(readFileSync30(entry2.path, "utf8"));
13858
14335
  const name = basename7(entry2.slug);
13859
14336
  if (!/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
13860
14337
  throw new ProjectError(
@@ -13862,11 +14339,11 @@ function addEntry(root, entry2) {
13862
14339
  );
13863
14340
  }
13864
14341
  if (kind2 === "skill") {
13865
- const dest2 = join25(root, ".agents/skills", name);
13866
- if (existsSync20(dest2))
14342
+ const dest2 = join27(root, ".agents/skills", name);
14343
+ if (existsSync23(dest2))
13867
14344
  throw new ProjectError(`.agents/skills/${name} already exists`);
13868
- const source2 = entry2.root ?? dirname18(entry2.path);
13869
- mkdirSync8(dirname18(dest2), { recursive: true });
14345
+ const source2 = entry2.root ?? dirname20(entry2.path);
14346
+ mkdirSync8(dirname20(dest2), { recursive: true });
13870
14347
  if (statSync13(source2).isDirectory()) {
13871
14348
  cpSync(source2, dest2, { recursive: true });
13872
14349
  } else {
@@ -13878,14 +14355,14 @@ function addEntry(root, entry2) {
13878
14355
  description: meta["description"] ?? entry2.description
13879
14356
  });
13880
14357
  writeFileSync7(
13881
- join25(dest2, "SKILL.md"),
14358
+ join27(dest2, "SKILL.md"),
13882
14359
  `${head}
13883
14360
  ${body.replace(/^\n/u, "")}`
13884
14361
  );
13885
14362
  return { kind: kind2, name, path: `.agents/skills/${name}` };
13886
14363
  }
13887
- const dest = join25(root, ".agents/agents", name);
13888
- if (existsSync20(dest))
14364
+ const dest = join27(root, ".agents/agents", name);
14365
+ if (existsSync23(dest))
13889
14366
  throw new ProjectError(`.agents/agents/${name} already exists`);
13890
14367
  const kept = {};
13891
14368
  const moved = {};
@@ -13895,17 +14372,17 @@ ${body.replace(/^\n/u, "")}`
13895
14372
  }
13896
14373
  mkdirSync8(dest, { recursive: true });
13897
14374
  writeFileSync7(
13898
- join25(dest, "agent.md"),
14375
+ join27(dest, "agent.md"),
13899
14376
  `${renderFrontmatter(kept)}
13900
14377
  ${body.replace(/^\n/u, "")}`
13901
14378
  );
13902
14379
  if (Object.keys(moved).length > 0) {
13903
- const config = join25(root, "pmcp.toml");
14380
+ const config = join27(root, "pmcp.toml");
13904
14381
  const lines2 = [
13905
14382
  `[agents.${name}.claude]`,
13906
14383
  ...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
13907
14384
  ];
13908
- const before = existsSync20(config) ? readFileSync29(config, "utf8") : "";
14385
+ const before = existsSync23(config) ? readFileSync30(config, "utf8") : "";
13909
14386
  const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
13910
14387
  writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
13911
14388
  `);
@@ -13928,15 +14405,15 @@ function withoutTables(toml, name) {
13928
14405
  function removeAsset(root, name) {
13929
14406
  const removed = [];
13930
14407
  for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
13931
- if (!existsSync20(join25(root, rel))) continue;
13932
- rmSync5(join25(root, rel), { recursive: true, force: true });
14408
+ if (!existsSync23(join27(root, rel))) continue;
14409
+ rmSync6(join27(root, rel), { recursive: true, force: true });
13933
14410
  removed.push(rel);
13934
14411
  }
13935
14412
  if (removed.length === 0)
13936
14413
  throw new ProjectError(`no skill or agent named ${name} in .agents/`);
13937
- const config = join25(root, "pmcp.toml");
13938
- if (existsSync20(config)) {
13939
- const before = readFileSync29(config, "utf8");
14414
+ const config = join27(root, "pmcp.toml");
14415
+ if (existsSync23(config)) {
14416
+ const before = readFileSync30(config, "utf8");
13940
14417
  const after = withoutTables(before, name);
13941
14418
  if (after !== before) writeFileSync7(config, after);
13942
14419
  }
@@ -14175,16 +14652,16 @@ var doctorCommand = {
14175
14652
  };
14176
14653
 
14177
14654
  // src/validate.ts
14178
- import { existsSync as existsSync21, readFileSync as readFileSync30, statSync as statSync14 } from "fs";
14179
- import { basename as basename8, join as join26, resolve as resolve17 } from "path";
14655
+ import { existsSync as existsSync24, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
14656
+ import { basename as basename8, join as join28, resolve as resolve16 } from "path";
14180
14657
  function validateSkillDir(dir) {
14181
- const root = resolve17(dir);
14182
- const skillMd = basename8(root) === "SKILL.md" ? root : join26(root, "SKILL.md");
14183
- if (!existsSync21(skillMd) || !statSync14(skillMd).isFile()) {
14658
+ const root = resolve16(dir);
14659
+ const skillMd = basename8(root) === "SKILL.md" ? root : join28(root, "SKILL.md");
14660
+ if (!existsSync24(skillMd) || !statSync14(skillMd).isFile()) {
14184
14661
  return [{ path: root, reason: "no SKILL.md", level: "error" }];
14185
14662
  }
14186
- const front = frontmatterObject(readFileSync30(skillMd, "utf8"));
14187
- const directory = basename8(skillMd === root ? join26(root, "..") : root);
14663
+ const front = frontmatterObject(readFileSync31(skillMd, "utf8"));
14664
+ const directory = basename8(skillMd === root ? join28(root, "..") : root);
14188
14665
  return specProblems(front, directory).map((reason) => ({
14189
14666
  path: skillMd,
14190
14667
  reason,
@@ -14229,8 +14706,8 @@ var validateCommand = {
14229
14706
  };
14230
14707
 
14231
14708
  // src/commands/mailbox.ts
14232
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync8 } from "fs";
14233
- import { resolve as resolve18 } from "path";
14709
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync8 } from "fs";
14710
+ import { resolve as resolve17 } from "path";
14234
14711
  init_config2();
14235
14712
  init_service();
14236
14713
  init_schemas();
@@ -14313,7 +14790,7 @@ var mailboxCommand = {
14313
14790
  }
14314
14791
  if (verb === "init") {
14315
14792
  const config2 = initializeMailboxConfig(
14316
- resolve18(context.cwd, one(context.args, "config") ?? "pmcp.toml"),
14793
+ resolve17(context.cwd, one(context.args, "config") ?? "pmcp.toml"),
14317
14794
  one(context.args, "state-dir")
14318
14795
  );
14319
14796
  const store2 = await MailboxStore.open(config2);
@@ -14338,7 +14815,7 @@ var mailboxCommand = {
14338
14815
  if (verb === "restore") {
14339
14816
  await restoreBackup(required(context, "input"), {
14340
14817
  ...config,
14341
- databasePath: resolve18(context.cwd, required(context, "output"))
14818
+ databasePath: resolve17(context.cwd, required(context, "output"))
14342
14819
  });
14343
14820
  context.ui.data(
14344
14821
  `${JSON.stringify({ ok: true, data: { restored: true } })}
@@ -14412,7 +14889,7 @@ var mailboxCommand = {
14412
14889
  ownsBinding = true;
14413
14890
  }
14414
14891
  const inputFile = one(context.args, "input");
14415
- const input = inputFile ? JSON.parse(readFileSync31(inputFile, "utf8")) : {};
14892
+ const input = inputFile ? JSON.parse(readFileSync32(inputFile, "utf8")) : {};
14416
14893
  const result = await new MailboxService(store, binding).call(
14417
14894
  operation,
14418
14895
  input
@@ -14496,7 +14973,7 @@ var harnessOptions = [
14496
14973
  ];
14497
14974
 
14498
14975
  // src/commands/harness/invocation.ts
14499
- import { readFileSync as readFileSync32, statSync as statSync15 } from "fs";
14976
+ import { readFileSync as readFileSync33, statSync as statSync15 } from "fs";
14500
14977
  init_validation();
14501
14978
  var common = ["group-file", "generation"];
14502
14979
  var options = {
@@ -14536,7 +15013,7 @@ function file(path) {
14536
15013
  const stat = statSync15(path);
14537
15014
  if (!stat.isFile() || stat.size > 65536)
14538
15015
  throw new ArgumentError("Input file exceeds 65536 bytes");
14539
- return readFileSync32(path, "utf8");
15016
+ return readFileSync33(path, "utf8");
14540
15017
  }
14541
15018
  function parseInvocation(context) {
14542
15019
  const [family, action, ...extra] = context.args.positional;
@@ -14642,6 +15119,9 @@ var harnessCommand = {
14642
15119
  }
14643
15120
  };
14644
15121
 
15122
+ // src/commands/tui.ts
15123
+ init_config();
15124
+
14645
15125
  // src/commands/tui/invocation.ts
14646
15126
  var tuiOptions = [
14647
15127
  {
@@ -14664,9 +15144,24 @@ var tuiOptions = [
14664
15144
  },
14665
15145
  {
14666
15146
  name: "config",
14667
- describe: "Optional mailbox pmcp.toml",
15147
+ describe: "Project pmcp.toml (or mailbox config for create)",
14668
15148
  placeholder: "<file>"
14669
15149
  },
15150
+ {
15151
+ name: "runtime",
15152
+ describe: "Main native runtime ID for launch",
15153
+ placeholder: "<runtime>"
15154
+ },
15155
+ {
15156
+ name: "yes",
15157
+ describe: "Confirm the first native runtime start",
15158
+ boolean: true
15159
+ },
15160
+ {
15161
+ name: "new",
15162
+ describe: "Create a workspace after the previous group is stopped",
15163
+ boolean: true
15164
+ },
14670
15165
  {
14671
15166
  name: "actor-file",
14672
15167
  describe: "Optional private mailbox observer credential",
@@ -14674,6 +15169,8 @@ var tuiOptions = [
14674
15169
  }
14675
15170
  ];
14676
15171
  var allowed2 = {
15172
+ launch: ["config", "runtime", "yes", "new"],
15173
+ plan: ["config", "runtime"],
14677
15174
  doctor: [],
14678
15175
  create: ["group-file", "columns", "rows", "config", "actor-file"],
14679
15176
  inspect: ["view"],
@@ -14694,7 +15191,8 @@ var allowed2 = {
14694
15191
  check: ["view"]
14695
15192
  };
14696
15193
  function parseTui(args) {
14697
- const [action, ...extra] = args.positional;
15194
+ let [action, ...extra] = args.positional;
15195
+ if (action === void 0) action = one(args, "view") ? "open" : "launch";
14698
15196
  const options2 = action ? allowed2[action] : void 0;
14699
15197
  if (!action || !options2 || extra.length)
14700
15198
  throw new ArgumentError("Provide one supported TUI operation");
@@ -14714,6 +15212,17 @@ function parseTui(args) {
14714
15212
  return Number(value);
14715
15213
  };
14716
15214
  if (action === "doctor") return { action, input: {} };
15215
+ if (action === "launch" || action === "plan")
15216
+ return {
15217
+ action,
15218
+ input: {},
15219
+ startup: {
15220
+ config: one(args, "config"),
15221
+ runtime: one(args, "runtime"),
15222
+ fresh: args.flags.has("new"),
15223
+ confirmed: args.flags.has("yes")
15224
+ }
15225
+ };
14717
15226
  if (action === "create") {
14718
15227
  const config = one(args, "config");
14719
15228
  const actorFile = one(args, "actor-file");
@@ -14753,19 +15262,30 @@ function parseTui(args) {
14753
15262
  // src/commands/tui.ts
14754
15263
  var tuiCommand = {
14755
15264
  name: "tui",
14756
- describe: "View an owned native harness group with explicit input control",
14757
- usage: "pmcp tui doctor | create --group-file <file> | operation --view <file>",
15265
+ describe: "Open a native terminal workspace with optional configuration",
15266
+ usage: "pmcp tui [--config <file>] [--runtime <id>] | plan | doctor | operation --view <file>",
14758
15267
  options: tuiOptions,
14759
15268
  async run(context) {
14760
15269
  const invocation2 = parseTui(context.args);
14761
15270
  const { invokeTui: invokeTui2, failedInvocation: failedInvocation2 } = await Promise.resolve().then(() => (init_invoke2(), invoke_exports2));
14762
15271
  try {
14763
- const receipt = await invokeTui2(invocation2);
14764
- if (invocation2.action !== "open" || receipt.ok !== true)
15272
+ let receipt;
15273
+ if (invocation2.action === "launch" || invocation2.action === "plan") {
15274
+ const { launchStartup: launchStartup2, planStartup: planStartup2 } = await Promise.resolve().then(() => (init_entry2(), entry_exports));
15275
+ const request3 = { ...invocation2.startup, cwd: context.cwd };
15276
+ receipt = invocation2.action === "plan" ? planStartup2(request3) : await launchStartup2(
15277
+ request3,
15278
+ invocation2.startup?.confirmed === true
15279
+ );
15280
+ } else {
15281
+ receipt = await invokeTui2(invocation2);
15282
+ }
15283
+ if (!["open", "launch"].includes(invocation2.action) || receipt.ok !== true)
14765
15284
  context.ui.data(`${JSON.stringify(receipt)}
14766
15285
  `);
14767
15286
  return receipt.ok === true ? 0 : 1;
14768
15287
  } catch (error) {
15288
+ if (error instanceof ConfigError) throw new ArgumentError(error.message);
14769
15289
  context.ui.data(
14770
15290
  `${JSON.stringify(failedInvocation2(invocation2.action, error))}
14771
15291
  `
@@ -14780,14 +15300,14 @@ import { resolve as resolve21 } from "path";
14780
15300
 
14781
15301
  // src/marketplace/export.ts
14782
15302
  import {
14783
- existsSync as existsSync33,
14784
- lstatSync as lstatSync12,
14785
- mkdirSync as mkdirSync12,
14786
- readFileSync as readFileSync37,
15303
+ existsSync as existsSync37,
15304
+ lstatSync as lstatSync14,
15305
+ mkdirSync as mkdirSync13,
15306
+ readFileSync as readFileSync38,
14787
15307
  readdirSync as readdirSync13,
14788
15308
  writeFileSync as writeFileSync14
14789
15309
  } from "fs";
14790
- import { dirname as dirname24, join as join52, resolve as resolve20 } from "path";
15310
+ import { dirname as dirname27, join as join55, resolve as resolve20 } from "path";
14791
15311
  var NATIVE_PLUGIN_RUNTIMES = [
14792
15312
  "claude",
14793
15313
  "codex",
@@ -14812,7 +15332,7 @@ function readResources(directory, prefix, files) {
14812
15332
  for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
14813
15333
  if (entry2.isSymbolicLink())
14814
15334
  throw new Error("Plugin exports cannot contain symlinks");
14815
- const path = join52(directory, entry2.name);
15335
+ const path = join55(directory, entry2.name);
14816
15336
  const relativePath = `${prefix}/${entry2.name}`;
14817
15337
  if (entry2.isDirectory()) {
14818
15338
  if (relativePath.split("/").length > 6)
@@ -14822,10 +15342,10 @@ function readResources(directory, prefix, files) {
14822
15342
  }
14823
15343
  if (!entry2.isFile())
14824
15344
  throw new Error("Plugin resources must be regular files");
14825
- const stats = lstatSync12(path);
15345
+ const stats = lstatSync14(path);
14826
15346
  if (stats.size > 1024 * 1024 || files.size >= 256)
14827
15347
  throw new Error("Plugin resources exceed the export budget");
14828
- files.set(relativePath, readFileSync37(path));
15348
+ files.set(relativePath, readFileSync38(path));
14829
15349
  }
14830
15350
  }
14831
15351
  function pluginExportFiles(root, name, runtime2) {
@@ -14834,7 +15354,7 @@ function pluginExportFiles(root, name, runtime2) {
14834
15354
  const files = /* @__PURE__ */ new Map();
14835
15355
  readResources(containedPath(plugin.directory, "skills"), "skills", files);
14836
15356
  for (const name2 of ["LICENSE", "NOTICE"])
14837
- files.set(name2, readFileSync37(containedPath(root, name2)));
15357
+ files.set(name2, readFileSync38(containedPath(root, name2)));
14838
15358
  const manifests = {
14839
15359
  claude: [".claude-plugin/plugin.json", ".mcp.json"],
14840
15360
  codex: ["plugin.json", "mcp.json"],
@@ -14843,10 +15363,10 @@ function pluginExportFiles(root, name, runtime2) {
14843
15363
  agy: []
14844
15364
  };
14845
15365
  for (const filename of manifests[runtime2]) {
14846
- if (existsSync33(join52(plugin.directory, filename)))
15366
+ if (existsSync37(join55(plugin.directory, filename)))
14847
15367
  files.set(
14848
15368
  filename,
14849
- readFileSync37(containedPath(plugin.directory, filename))
15369
+ readFileSync38(containedPath(plugin.directory, filename))
14850
15370
  );
14851
15371
  }
14852
15372
  if (runtime2 === "agy") {
@@ -14857,11 +15377,11 @@ function pluginExportFiles(root, name, runtime2) {
14857
15377
  `
14858
15378
  )
14859
15379
  );
14860
- const mcp = join52(plugin.directory, ".mcp.json");
14861
- if (existsSync33(mcp))
15380
+ const mcp = join55(plugin.directory, ".mcp.json");
15381
+ if (existsSync37(mcp))
14862
15382
  files.set(
14863
15383
  "mcp_config.json",
14864
- readFileSync37(containedPath(plugin.directory, mcp))
15384
+ readFileSync38(containedPath(plugin.directory, mcp))
14865
15385
  );
14866
15386
  }
14867
15387
  return files;
@@ -14869,9 +15389,9 @@ function pluginExportFiles(root, name, runtime2) {
14869
15389
  function exportPublicPlugin(root, name, runtime2, output) {
14870
15390
  const destination = resolve20(output);
14871
15391
  const files = pluginExportFiles(root, name, runtime2);
14872
- mkdirSync12(dirname24(destination), { recursive: true });
15392
+ mkdirSync13(dirname27(destination), { recursive: true });
14873
15393
  try {
14874
- mkdirSync12(destination);
15394
+ mkdirSync13(destination);
14875
15395
  } catch (error) {
14876
15396
  if (error.code === "EEXIST")
14877
15397
  throw new Error(
@@ -14880,8 +15400,8 @@ function exportPublicPlugin(root, name, runtime2, output) {
14880
15400
  throw error;
14881
15401
  }
14882
15402
  for (const [path, bytes] of files) {
14883
- const target2 = join52(destination, path);
14884
- mkdirSync12(dirname24(target2), { recursive: true });
15403
+ const target2 = join55(destination, path);
15404
+ mkdirSync13(dirname27(target2), { recursive: true });
14885
15405
  writeFileSync14(target2, bytes, { flag: "wx" });
14886
15406
  }
14887
15407
  return { plugin: name, runtime: runtime2, output: destination, files: files.size };
@@ -15002,7 +15522,7 @@ function dispatch(argv, options2 = {}) {
15002
15522
  });
15003
15523
  const [verb, ...rest] = rest0;
15004
15524
  if (verb === void 0) {
15005
- return run(serveCommand, [], { ui, env, cwd });
15525
+ return defaultEntry(options2, { ui, env, cwd });
15006
15526
  }
15007
15527
  if (verb === "--help" || verb === "-h" || verb === "help") {
15008
15528
  const named2 = COMMANDS.find((command3) => command3.name === rest[0]);
@@ -15032,7 +15552,30 @@ function dispatch(argv, options2 = {}) {
15032
15552
  }
15033
15553
  return run(command2, rest, { ui, env, cwd });
15034
15554
  }
15035
- var VERSION = "0.14.1";
15555
+ var VERSION = "0.15.0";
15556
+ async function defaultEntry(options2, context) {
15557
+ const terminal = options2.terminal ?? {
15558
+ stdin: process.stdin.isTTY === true,
15559
+ stdout: process.stdout.isTTY === true
15560
+ };
15561
+ const mode = entryMode({
15562
+ ...terminal,
15563
+ term: context.env.TERM,
15564
+ ci: context.env.CI
15565
+ });
15566
+ if (mode === "server") return run(serveCommand, [], context);
15567
+ if (mode === "terminal") {
15568
+ const { terminalSupport: terminalSupport2 } = await Promise.resolve().then(() => (init_host(), host_exports));
15569
+ const support = terminalSupport2();
15570
+ if (support.supported) return run(tuiCommand, [], context);
15571
+ context.ui.info(`Terminal workspace unavailable: ${support.reason}`);
15572
+ context.ui.line(
15573
+ "Run pmcp tui doctor for prerequisites, or pmcp serve for MCP."
15574
+ );
15575
+ }
15576
+ printHelp(context.ui, COMMANDS, PLANNED);
15577
+ return 0;
15578
+ }
15036
15579
  async function run(command2, argv, context) {
15037
15580
  try {
15038
15581
  return await command2.run({