@modootoday/pmcp 0.14.1 → 0.16.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;
@@ -453,8 +471,6 @@ var init_types = __esm({
453
471
  this.retryable = retryable;
454
472
  this.name = "MailboxError";
455
473
  }
456
- code;
457
- retryable;
458
474
  };
459
475
  }
460
476
  });
@@ -1819,11 +1835,11 @@ function dockerPolicy(value) {
1819
1835
  for (const [runtime2, value2] of entries) {
1820
1836
  if (!Object.hasOwn(runtimeExecutables, runtime2))
1821
1837
  throw new Error("unsupported_docker_runtime");
1822
- const command2 = strings2(value2, "docker_runtime_command");
1823
- const executable = command2[0];
1824
- if (!isAbsolute10(executable) || posix5.normalize(executable) !== executable || command2.some((argument) => /[\r\n\x1b]/.test(argument)))
1838
+ const command3 = strings2(value2, "docker_runtime_command");
1839
+ const executable = command3[0];
1840
+ if (!isAbsolute10(executable) || posix5.normalize(executable) !== executable || command3.some((argument) => /[\r\n\x1b]/.test(argument)))
1825
1841
  throw new Error("invalid_docker_runtime_command");
1826
- parsed[runtime2] = command2;
1842
+ parsed[runtime2] = command3;
1827
1843
  }
1828
1844
  return {
1829
1845
  schemaVersion: 1,
@@ -1920,47 +1936,47 @@ var init_validation2 = __esm({
1920
1936
  });
1921
1937
 
1922
1938
  // src/harness/groups/store.ts
1923
- import { randomUUID as randomUUID4 } from "crypto";
1939
+ import { randomUUID as randomUUID5 } from "crypto";
1924
1940
  import {
1925
- closeSync as closeSync3,
1941
+ closeSync as closeSync4,
1926
1942
  constants as constants3,
1927
1943
  fstatSync as fstatSync2,
1928
- fsyncSync,
1929
- openSync as openSync3,
1930
- readFileSync as readFileSync33,
1931
- realpathSync as realpathSync4,
1932
- renameSync as renameSync3,
1944
+ fsyncSync as fsyncSync2,
1945
+ openSync as openSync4,
1946
+ readFileSync as readFileSync34,
1947
+ realpathSync as realpathSync6,
1948
+ renameSync as renameSync4,
1933
1949
  writeFileSync as writeFileSync9
1934
1950
  } from "fs";
1935
- import { dirname as dirname19, join as join27 } from "path";
1951
+ import { dirname as dirname21, join as join29 } from "path";
1936
1952
  function privateJson(path, value) {
1937
- const temporary = `${path}.${randomUUID4()}.new`;
1938
- const descriptor = openSync3(temporary, "wx", 384);
1953
+ const temporary = `${path}.${randomUUID5()}.new`;
1954
+ const descriptor = openSync4(temporary, "wx", 384);
1939
1955
  try {
1940
1956
  writeFileSync9(descriptor, `${JSON.stringify(value, null, 2)}
1941
1957
  `);
1942
- fsyncSync(descriptor);
1958
+ fsyncSync2(descriptor);
1943
1959
  } finally {
1944
- closeSync3(descriptor);
1960
+ closeSync4(descriptor);
1945
1961
  }
1946
- renameSync3(temporary, path);
1947
- const directory = openSync3(dirname19(path), "r");
1962
+ renameSync4(temporary, path);
1963
+ const directory = openSync4(dirname21(path), "r");
1948
1964
  try {
1949
- fsyncSync(directory);
1965
+ fsyncSync2(directory);
1950
1966
  } finally {
1951
- closeSync3(directory);
1967
+ closeSync4(directory);
1952
1968
  }
1953
1969
  }
1954
1970
  function readPrivateJson2(path) {
1955
- const descriptor = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1971
+ const descriptor = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1956
1972
  try {
1957
1973
  const stat = fstatSync2(descriptor);
1958
1974
  if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 2097152)
1959
1975
  throw new Error("unsafe_state_file");
1960
1976
  if (stat.uid !== process.getuid?.()) throw new Error("foreign_state_file");
1961
- return JSON.parse(readFileSync33(descriptor, "utf8"));
1977
+ return JSON.parse(readFileSync34(descriptor, "utf8"));
1962
1978
  } finally {
1963
- closeSync3(descriptor);
1979
+ closeSync4(descriptor);
1964
1980
  }
1965
1981
  }
1966
1982
  var GroupStore;
@@ -1973,10 +1989,10 @@ var init_store2 = __esm({
1973
1989
  path;
1974
1990
  state;
1975
1991
  constructor(path) {
1976
- this.path = realpathSync4(path);
1992
+ this.path = realpathSync6(path);
1977
1993
  this.state = validateState(readPrivateJson2(path));
1978
1994
  const metadata2 = object8(
1979
- readPrivateJson2(join27(dirname19(this.path), "harness.json"))
1995
+ readPrivateJson2(join29(dirname21(this.path), "harness.json"))
1980
1996
  );
1981
1997
  if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
1982
1998
  throw new Error("foreign_group");
@@ -1997,13 +2013,13 @@ var init_store2 = __esm({
1997
2013
 
1998
2014
  // src/harness/adapters/process/serialized.ts
1999
2015
  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";
2016
+ import { randomUUID as randomUUID6 } from "crypto";
2017
+ import { existsSync as existsSync25, unlinkSync as unlinkSync2 } from "fs";
2018
+ import { dirname as dirname22, join as join30 } from "path";
2003
2019
  function serialized(operation) {
2004
2020
  const store = new GroupStore(text(operation.groupFile, "group_file"));
2005
- const directory = dirname20(store.path);
2006
- const operationFile = join28(directory, `operation-${randomUUID5()}.json`);
2021
+ const directory = dirname22(store.path);
2022
+ const operationFile = join30(directory, `operation-${randomUUID6()}.json`);
2007
2023
  privateJson(operationFile, operation);
2008
2024
  const worker = artifact("worker");
2009
2025
  try {
@@ -2015,7 +2031,7 @@ function serialized(operation) {
2015
2031
  "--no-fork",
2016
2032
  "--conflict-exit-code",
2017
2033
  "73",
2018
- join28(directory, "group-control.lock"),
2034
+ join30(directory, "group-control.lock"),
2019
2035
  process.execPath,
2020
2036
  worker,
2021
2037
  operationFile
@@ -2031,7 +2047,7 @@ function serialized(operation) {
2031
2047
  if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
2032
2048
  return JSON.parse(result.stdout);
2033
2049
  } finally {
2034
- if (existsSync22(operationFile)) unlinkSync2(operationFile);
2050
+ if (existsSync25(operationFile)) unlinkSync2(operationFile);
2035
2051
  }
2036
2052
  }
2037
2053
  var init_serialized = __esm({
@@ -2111,11 +2127,11 @@ var init_command = __esm({
2111
2127
  });
2112
2128
 
2113
2129
  // src/harness/adapters/process/identity.ts
2114
- import { readFileSync as readFileSync34 } from "fs";
2130
+ import { readFileSync as readFileSync35 } from "fs";
2115
2131
  function processIdentity(pid) {
2116
2132
  if (!Number.isSafeInteger(pid) || pid < 1)
2117
2133
  throw new Error("invalid_process_id");
2118
- const stat = readFileSync34(`/proc/${pid}/stat`, "utf8");
2134
+ const stat = readFileSync35(`/proc/${pid}/stat`, "utf8");
2119
2135
  const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
2120
2136
  if (!/^\d+$/.test(fields[19] ?? ""))
2121
2137
  throw new Error("process_identity_unknown");
@@ -2285,7 +2301,6 @@ var init_connection = __esm({
2285
2301
  constructor(records2) {
2286
2302
  this.records = records2;
2287
2303
  }
2288
- records;
2289
2304
  tmux(args, options2 = {}) {
2290
2305
  return command(
2291
2306
  "tmux",
@@ -2345,19 +2360,19 @@ var init_connection = __esm({
2345
2360
  });
2346
2361
 
2347
2362
  // src/harness/adapters/tmux/records.ts
2348
- import { randomUUID as randomUUID6 } from "crypto";
2363
+ import { randomUUID as randomUUID7 } from "crypto";
2349
2364
  import {
2350
- lstatSync as lstatSync5,
2365
+ lstatSync as lstatSync6,
2351
2366
  mkdirSync as mkdirSync9,
2352
2367
  readdirSync as readdirSync10,
2353
- realpathSync as realpathSync5,
2368
+ realpathSync as realpathSync7,
2354
2369
  writeFileSync as writeFileSync10
2355
2370
  } from "fs";
2356
2371
  import { homedir as homedir7 } from "os";
2357
- import { join as join29 } from "path";
2372
+ import { join as join31 } from "path";
2358
2373
  function stateDirectory() {
2359
- return join29(
2360
- process.env.XDG_STATE_HOME ?? join29(homedir7(), ".local/state"),
2374
+ return join31(
2375
+ process.env.XDG_STATE_HOME ?? join31(homedir7(), ".local/state"),
2361
2376
  "pmcp/harness"
2362
2377
  );
2363
2378
  }
@@ -2384,15 +2399,15 @@ var init_records = __esm({
2384
2399
  configPath;
2385
2400
  owner;
2386
2401
  constructor(directory) {
2387
- const stat = lstatSync5(directory);
2402
+ const stat = lstatSync6(directory);
2388
2403
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2389
2404
  throw new Error("unsafe_group_directory");
2390
- this.directory = realpathSync5(directory);
2391
- this.socket = join29(this.directory, "s");
2405
+ this.directory = realpathSync7(directory);
2406
+ this.socket = join31(this.directory, "s");
2392
2407
  if (Buffer.byteLength(this.socket) > 100)
2393
2408
  throw new Error("socket_path_too_long");
2394
- this.metadataPath = join29(this.directory, "harness.json");
2395
- this.configPath = join29(this.directory, "tmux.conf");
2409
+ this.metadataPath = join31(this.directory, "harness.json");
2410
+ this.configPath = join31(this.directory, "tmux.conf");
2396
2411
  this.owner = text(
2397
2412
  object8(readPrivateJson2(this.metadataPath)).owner,
2398
2413
  "owner"
@@ -2402,17 +2417,17 @@ var init_records = __esm({
2402
2417
  static create() {
2403
2418
  const root = stateDirectory();
2404
2419
  mkdirSync9(root, { recursive: true, mode: 448 });
2405
- const directory = join29(root, randomUUID6().slice(0, 16));
2406
- if (Buffer.byteLength(join29(directory, "s")) > 100)
2420
+ const directory = join31(root, randomUUID7().slice(0, 16));
2421
+ if (Buffer.byteLength(join31(directory, "s")) > 100)
2407
2422
  throw new Error("socket_path_too_long");
2408
2423
  mkdirSync9(directory, { mode: 448 });
2409
- mkdirSync9(join29(directory, "sessions"), { mode: 448 });
2410
- privateJson(join29(directory, "harness.json"), {
2411
- owner: randomUUID6(),
2424
+ mkdirSync9(join31(directory, "sessions"), { mode: 448 });
2425
+ privateJson(join31(directory, "harness.json"), {
2426
+ owner: randomUUID7(),
2412
2427
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2413
2428
  });
2414
2429
  writeFileSync10(
2415
- join29(directory, "tmux.conf"),
2430
+ join31(directory, "tmux.conf"),
2416
2431
  [
2417
2432
  "set -g remain-on-exit on",
2418
2433
  "set -g history-limit 2000",
@@ -2428,7 +2443,7 @@ var init_records = __esm({
2428
2443
  }
2429
2444
  path(id2) {
2430
2445
  runtimeUnit(id2);
2431
- return join29(this.directory, "sessions", `${id2}.json`);
2446
+ return join31(this.directory, "sessions", `${id2}.json`);
2432
2447
  }
2433
2448
  read(id2) {
2434
2449
  const record = object8(readPrivateJson2(this.path(id2)));
@@ -2450,7 +2465,7 @@ var init_records = __esm({
2450
2465
  return record;
2451
2466
  }
2452
2467
  all() {
2453
- return readdirSync10(join29(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2468
+ return readdirSync10(join31(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
2454
2469
  }
2455
2470
  save(record) {
2456
2471
  privateJson(this.path(record.id), record);
@@ -2532,8 +2547,8 @@ var init_launch_identity = __esm({
2532
2547
 
2533
2548
  // src/harness/launch/journal.ts
2534
2549
  import { createHash as createHash16 } from "crypto";
2535
- import { existsSync as existsSync23, readdirSync as readdirSync11 } from "fs";
2536
- import { join as join30 } from "path";
2550
+ import { existsSync as existsSync26, readdirSync as readdirSync11 } from "fs";
2551
+ import { join as join32 } from "path";
2537
2552
  function launchJournals(records2) {
2538
2553
  return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
2539
2554
  }
@@ -2541,7 +2556,7 @@ function pendingLaunches(records2) {
2541
2556
  return launchJournals(records2).filter((journal) => {
2542
2557
  const aborted = journal.abortRecord();
2543
2558
  if (aborted) return aborted.state !== "stopped";
2544
- if (!existsSync23(journal.path("committed"))) return true;
2559
+ if (!existsSync26(journal.path("committed"))) return true;
2545
2560
  journal.assertCommitted();
2546
2561
  return false;
2547
2562
  });
@@ -2563,12 +2578,10 @@ var init_journal = __esm({
2563
2578
  this.id = id2;
2564
2579
  runtimeUnit(id2);
2565
2580
  }
2566
- records;
2567
- id;
2568
2581
  path(kind2) {
2569
2582
  if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
2570
2583
  throw new Error("invalid_launch_artifact");
2571
- return join30(this.records.directory, `${this.id}.${kind2}.json`);
2584
+ return join32(this.records.directory, `${this.id}.${kind2}.json`);
2572
2585
  }
2573
2586
  intent() {
2574
2587
  const value = object8(readPrivateJson2(this.path("intent")));
@@ -2597,13 +2610,13 @@ var init_journal = __esm({
2597
2610
  throw new Error("invalid_docker_bridge_digest");
2598
2611
  if (!bridge && creation.bridgeSha256 !== void 0)
2599
2612
  throw new Error("ungranted_docker_bridge_digest");
2600
- const policy = new GroupStore(join30(this.records.directory, "group.json")).state.group.dockerPolicy;
2613
+ const policy = new GroupStore(join32(this.records.directory, "group.json")).state.group.dockerPolicy;
2601
2614
  const profile = selectedProfile(
2602
2615
  policy,
2603
2616
  creation.privateProfile,
2604
2617
  text(value.runtime, "runtime")
2605
2618
  );
2606
- const expectedState = profile?.directory ?? join30(
2619
+ const expectedState = profile?.directory ?? join32(
2607
2620
  this.records.directory,
2608
2621
  "containers",
2609
2622
  text(value.nonce, "nonce"),
@@ -2649,7 +2662,7 @@ var init_journal = __esm({
2649
2662
  }
2650
2663
  assertRegistered() {
2651
2664
  const intent = this.intent();
2652
- const group = new GroupStore(join30(this.records.directory, "group.json")).state.group;
2665
+ const group = new GroupStore(join32(this.records.directory, "group.json")).state.group;
2653
2666
  if (group.id !== intent.groupId || group.generation !== intent.generation)
2654
2667
  throw new Error("launch_group_generation_mismatch");
2655
2668
  if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
@@ -2665,12 +2678,12 @@ var init_journal = __esm({
2665
2678
  throw new Error("launch_record_mismatch");
2666
2679
  }
2667
2680
  authorize() {
2668
- if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
2681
+ if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2669
2682
  this.assertRegistered();
2670
2683
  privateJson(this.path("grant"), this.grant());
2671
2684
  }
2672
2685
  assertGrant() {
2673
- if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
2686
+ if (existsSync26(this.path("aborted"))) throw new Error("launch_was_aborted");
2674
2687
  const grant = object8(readPrivateJson2(this.path("grant")));
2675
2688
  if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
2676
2689
  throw new Error("launch_grant_mismatch");
@@ -2687,7 +2700,7 @@ var init_journal = __esm({
2687
2700
  throw new Error("launch_commit_mismatch");
2688
2701
  }
2689
2702
  abortRecord() {
2690
- if (!existsSync23(this.path("aborted"))) return;
2703
+ if (!existsSync26(this.path("aborted"))) return;
2691
2704
  const value = object8(readPrivateJson2(this.path("aborted")));
2692
2705
  if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
2693
2706
  throw new Error("foreign_launch_abort");
@@ -2734,30 +2747,30 @@ var init_lifecycle = __esm({
2734
2747
  // src/harness/adapters/docker/private-profile.ts
2735
2748
  import { homedir as homedir8 } from "os";
2736
2749
  import {
2737
- closeSync as closeSync4,
2750
+ closeSync as closeSync5,
2738
2751
  constants as constants4,
2739
- existsSync as existsSync24,
2752
+ existsSync as existsSync27,
2740
2753
  fstatSync as fstatSync3,
2741
- lstatSync as lstatSync6,
2742
- openSync as openSync4,
2743
- realpathSync as realpathSync6,
2754
+ lstatSync as lstatSync7,
2755
+ openSync as openSync5,
2756
+ realpathSync as realpathSync8,
2744
2757
  statfsSync
2745
2758
  } from "fs";
2746
- import { join as join31, relative as relative11 } from "path";
2759
+ import { join as join33, relative as relative11 } from "path";
2747
2760
  function overlap(left, right) {
2748
2761
  const scope = relative11(left, right);
2749
2762
  return scope === "" || scope !== ".." && !scope.startsWith("../");
2750
2763
  }
2751
2764
  function profileIdentity(directory, projectRoot) {
2752
- if (directory === "/" || directory === realpathSync6(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync6(directory) !== directory)
2765
+ if (directory === "/" || directory === realpathSync8(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync8(directory) !== directory)
2753
2766
  throw new Error("private_profile_scope_not_admitted");
2754
- const stat = lstatSync6(directory);
2767
+ const stat = lstatSync7(directory);
2755
2768
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2756
2769
  throw new Error("unsafe_private_profile_directory");
2757
2770
  if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
2758
2771
  throw new Error("remote_private_profile_not_admitted");
2759
- const lock = openSync4(
2760
- join31(directory, privateProfileLock),
2772
+ const lock = openSync5(
2773
+ join33(directory, privateProfileLock),
2761
2774
  constants4.O_RDONLY | constants4.O_NOFOLLOW
2762
2775
  );
2763
2776
  try {
@@ -2766,18 +2779,18 @@ function profileIdentity(directory, projectRoot) {
2766
2779
  throw new Error("unsafe_private_profile_lock");
2767
2780
  return `${stat.dev}:${stat.ino}:${value.dev}:${value.ino}`;
2768
2781
  } finally {
2769
- closeSync4(lock);
2782
+ closeSync5(lock);
2770
2783
  }
2771
2784
  }
2772
2785
  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()))
2786
+ const directory = realpathSync8(grant.directory);
2787
+ if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync8(homedir8()))
2775
2788
  throw new Error("private_profile_scope_not_admitted");
2776
- const stat = lstatSync6(directory);
2789
+ const stat = lstatSync7(directory);
2777
2790
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
2778
2791
  throw new Error("unsafe_private_profile_directory");
2779
- const lock = join31(directory, privateProfileLock);
2780
- if (!existsSync24(lock)) closeSync4(openSync4(lock, "wx", 384));
2792
+ const lock = join33(directory, privateProfileLock);
2793
+ if (!existsSync27(lock)) closeSync5(openSync5(lock, "wx", 384));
2781
2794
  const identity = profileIdentity(directory, projectRoot);
2782
2795
  if (grant.identity && grant.identity !== identity)
2783
2796
  throw new Error("private_profile_identity_changed");
@@ -2795,7 +2808,7 @@ function assertProfileAvailable(grant, projectRoot) {
2795
2808
  "--nonblock",
2796
2809
  "--conflict-exit-code",
2797
2810
  "73",
2798
- join31(grant.directory, privateProfileLock),
2811
+ join33(grant.directory, privateProfileLock),
2799
2812
  "true"
2800
2813
  ]);
2801
2814
  } catch (error) {
@@ -2815,7 +2828,7 @@ var init_private_profile = __esm({
2815
2828
 
2816
2829
  // src/harness/adapters/docker/profile.ts
2817
2830
  import { homedir as homedir9 } from "os";
2818
- import { lstatSync as lstatSync7, realpathSync as realpathSync7 } from "fs";
2831
+ import { lstatSync as lstatSync8, realpathSync as realpathSync9 } from "fs";
2819
2832
  import { relative as relative12 } from "path";
2820
2833
  function assertLocalDocker() {
2821
2834
  if (process.env.DOCKER_HOST)
@@ -2832,12 +2845,12 @@ function assertLocalDocker() {
2832
2845
  throw new Error("remote_docker_context_not_admitted");
2833
2846
  }
2834
2847
  function loadDockerPolicy(path, projectRoot, runtimes) {
2835
- if (projectRoot === "/" || projectRoot === realpathSync7(homedir9()))
2848
+ if (projectRoot === "/" || projectRoot === realpathSync9(homedir9()))
2836
2849
  throw new Error("docker_project_root_too_broad");
2837
2850
  const inputPath = text(path, "docker_policy_file");
2838
- if (lstatSync7(inputPath).isSymbolicLink())
2851
+ if (lstatSync8(inputPath).isSymbolicLink())
2839
2852
  throw new Error("symlink_docker_policy_not_admitted");
2840
- const source2 = realpathSync7(inputPath);
2853
+ const source2 = realpathSync9(inputPath);
2841
2854
  const scope = relative12(projectRoot, source2);
2842
2855
  if (scope === ".." || scope.startsWith("../"))
2843
2856
  throw new Error("docker_policy_outside_project");
@@ -2871,16 +2884,16 @@ var init_profile = __esm({
2871
2884
  });
2872
2885
 
2873
2886
  // src/harness/adapters/docker/mounts.ts
2874
- import { lstatSync as lstatSync8, realpathSync as realpathSync8 } from "fs";
2875
- import { join as join32 } from "path";
2887
+ import { lstatSync as lstatSync9, realpathSync as realpathSync10 } from "fs";
2888
+ import { join as join34 } from "path";
2876
2889
  function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
2877
2890
  const state = text(statePath, "container_state_directory");
2878
2891
  const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
2879
2892
  if (profile) assertProfileIdentity(profile, projectRoot);
2880
- const expected = profile?.directory ?? join32(directory, "containers", nonce, "profile");
2881
- if (state !== expected || realpathSync8(state) !== expected)
2893
+ const expected = profile?.directory ?? join34(directory, "containers", nonce, "profile");
2894
+ if (state !== expected || realpathSync10(state) !== expected)
2882
2895
  throw new Error("foreign_container_state_directory");
2883
- const stat = lstatSync8(state);
2896
+ const stat = lstatSync9(state);
2884
2897
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
2885
2898
  throw new Error("unsafe_container_state_directory");
2886
2899
  if (!Array.isArray(actual.Mounts) || actual.Mounts.length !== 2)
@@ -2944,14 +2957,14 @@ function bridgeName(nonce) {
2944
2957
  function containerEntrypoint(policy, runtime2, nonce, privateProfile) {
2945
2958
  const native2 = policy.commands[runtime2];
2946
2959
  if (!native2) throw new Error("docker_runtime_command_not_granted");
2947
- const command2 = policy.egress?.bridgeExecutable ? [
2960
+ const command3 = policy.egress?.bridgeExecutable ? [
2948
2961
  policy.egress.bridgeExecutable,
2949
2962
  `/state/${bridgeName(nonce)}`,
2950
2963
  "--",
2951
2964
  ...native2
2952
2965
  ] : native2;
2953
2966
  const profile = selectedProfile(policy, privateProfile, runtime2);
2954
- if (!profile) return command2;
2967
+ if (!profile) return command3;
2955
2968
  return [
2956
2969
  profile.lockExecutable,
2957
2970
  "--exclusive",
@@ -2960,7 +2973,7 @@ function containerEntrypoint(policy, runtime2, nonce, privateProfile) {
2960
2973
  "--conflict-exit-code",
2961
2974
  "73",
2962
2975
  `/state/${privateProfileLock}`,
2963
- ...command2
2976
+ ...command3
2964
2977
  ];
2965
2978
  }
2966
2979
  var init_entrypoint = __esm({
@@ -2972,17 +2985,17 @@ var init_entrypoint = __esm({
2972
2985
  });
2973
2986
 
2974
2987
  // 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";
2988
+ import { lstatSync as lstatSync10, realpathSync as realpathSync11 } from "fs";
2989
+ import { join as join35, relative as relative13 } from "path";
2977
2990
  function admitResource(path, group, role, directory, memoryMb, runtime2) {
2978
2991
  if (group.profile !== "docker" || !group.dockerPolicy)
2979
2992
  throw new Error("container_requires_docker_profile");
2980
2993
  const { projectRoot } = group;
2981
2994
  assertLocalDocker();
2982
2995
  const inputPath = text(path, "container_file");
2983
- if (lstatSync9(inputPath).isSymbolicLink())
2996
+ if (lstatSync10(inputPath).isSymbolicLink())
2984
2997
  throw new Error("symlink_container_file_not_admitted");
2985
- const source2 = realpathSync9(inputPath);
2998
+ const source2 = realpathSync11(inputPath);
2986
2999
  const scope = relative13(projectRoot, source2);
2987
3000
  if (scope === ".." || scope.startsWith("../"))
2988
3001
  throw new Error("container_file_outside_project");
@@ -3029,7 +3042,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
3029
3042
  )
3030
3043
  ))
3031
3044
  throw new Error("container_runtime_image_not_granted");
3032
- if (config.WorkingDir !== join33("/workspace", relative13(projectRoot, cwd)))
3045
+ if (config.WorkingDir !== join35("/workspace", relative13(projectRoot, cwd)))
3033
3046
  throw new Error("container_cwd_not_granted");
3034
3047
  assertContainerMounts(
3035
3048
  actual,
@@ -3165,20 +3178,20 @@ var init_engine = __esm({
3165
3178
  // src/harness/adapters/docker/bridge-artifact.ts
3166
3179
  import { createHash as createHash17 } from "crypto";
3167
3180
  import {
3168
- closeSync as closeSync5,
3181
+ closeSync as closeSync6,
3169
3182
  constants as constants5,
3170
3183
  fstatSync as fstatSync4,
3171
- fsyncSync as fsyncSync2,
3172
- openSync as openSync5,
3173
- readFileSync as readFileSync35,
3184
+ fsyncSync as fsyncSync3,
3185
+ openSync as openSync6,
3186
+ readFileSync as readFileSync36,
3174
3187
  writeFileSync as writeFileSync11
3175
3188
  } from "fs";
3176
- import { join as join34 } from "path";
3189
+ import { join as join36 } from "path";
3177
3190
  function source() {
3178
3191
  const path = artifact("egress-bridge");
3179
3192
  if (!path.endsWith(".js"))
3180
3193
  throw new Error("docker_bridge_requires_compiled_cli");
3181
- return readFileSync35(path);
3194
+ return readFileSync36(path);
3182
3195
  }
3183
3196
  function digest(value) {
3184
3197
  return createHash17("sha256").update(value).digest("hex");
@@ -3191,30 +3204,30 @@ function prepareBridge(creation) {
3191
3204
  const content = source();
3192
3205
  if (digest(content) !== creation.bridgeSha256)
3193
3206
  throw new Error("docker_bridge_source_changed");
3194
- const descriptor = openSync5(
3195
- join34(creation.state, bridgeName(creation.nonce)),
3207
+ const descriptor = openSync6(
3208
+ join36(creation.state, bridgeName(creation.nonce)),
3196
3209
  "wx",
3197
3210
  384
3198
3211
  );
3199
3212
  try {
3200
3213
  writeFileSync11(descriptor, content);
3201
- fsyncSync2(descriptor);
3214
+ fsyncSync3(descriptor);
3202
3215
  } finally {
3203
- closeSync5(descriptor);
3216
+ closeSync6(descriptor);
3204
3217
  }
3205
3218
  }
3206
3219
  function assertBridge(creation) {
3207
3220
  if (!creation.egress?.bridgeExecutable) return;
3208
- const descriptor = openSync5(
3209
- join34(creation.state, bridgeName(creation.nonce)),
3221
+ const descriptor = openSync6(
3222
+ join36(creation.state, bridgeName(creation.nonce)),
3210
3223
  constants5.O_RDONLY | constants5.O_NOFOLLOW
3211
3224
  );
3212
3225
  try {
3213
3226
  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)
3227
+ if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(readFileSync36(descriptor)) !== creation.bridgeSha256)
3215
3228
  throw new Error("docker_bridge_artifact_changed");
3216
3229
  } finally {
3217
- closeSync5(descriptor);
3230
+ closeSync6(descriptor);
3218
3231
  }
3219
3232
  }
3220
3233
  var init_bridge_artifact = __esm({
@@ -3226,7 +3239,7 @@ var init_bridge_artifact = __esm({
3226
3239
  });
3227
3240
 
3228
3241
  // src/harness/adapters/docker/creation-plan.ts
3229
- import { join as join35, relative as relative14 } from "path";
3242
+ import { join as join37, relative as relative14 } from "path";
3230
3243
  function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
3231
3244
  if (group.profile !== "docker" || !group.dockerPolicy)
3232
3245
  throw new Error("container_requires_docker_profile");
@@ -3243,7 +3256,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3243
3256
  throw new Error("invalid_container_image_environment");
3244
3257
  const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
3245
3258
  if (profile) assertProfileAvailable(profile, group.projectRoot);
3246
- const state = profile?.directory ?? join35(directory, "containers", nonce, "profile");
3259
+ const state = profile?.directory ?? join37(directory, "containers", nonce, "profile");
3247
3260
  return {
3248
3261
  name: containerName(group.owner, id2),
3249
3262
  nonce,
@@ -3260,7 +3273,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
3260
3273
  preparation.privateProfile
3261
3274
  ),
3262
3275
  Cmd: preparation.args.length ? preparation.args : null,
3263
- WorkingDir: join35("/workspace", relative14(group.projectRoot, cwd)),
3276
+ WorkingDir: join37("/workspace", relative14(group.projectRoot, cwd)),
3264
3277
  User: `${process.getuid?.()}:${process.getgid?.()}`,
3265
3278
  Env: [
3266
3279
  ...environment2.filter(
@@ -3359,12 +3372,12 @@ var init_creation_plan = __esm({
3359
3372
 
3360
3373
  // src/harness/adapters/docker/creation.ts
3361
3374
  import { mkdirSync as mkdirSync10 } from "fs";
3362
- import { join as join36 } from "path";
3375
+ import { join as join38 } from "path";
3363
3376
  async function prepareCreation(journal) {
3364
3377
  const intent = journal.intent();
3365
3378
  const creation = intent.dockerCreation;
3366
3379
  if (!creation) throw new Error("container_creation_missing");
3367
- const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3380
+ const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3368
3381
  const profile = selectedProfile(
3369
3382
  group.dockerPolicy,
3370
3383
  creation.privateProfile,
@@ -3378,7 +3391,7 @@ async function prepareCreation(journal) {
3378
3391
  creation.name,
3379
3392
  creation.configuration
3380
3393
  );
3381
- const current = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3394
+ const current = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3382
3395
  if (current.id !== intent.groupId || current.generation !== intent.generation)
3383
3396
  throw new Error("launch_group_generation_mismatch");
3384
3397
  const resource = {
@@ -3446,7 +3459,7 @@ function findCreation(journal) {
3446
3459
  assertResourceOwner(resource, actual);
3447
3460
  assertCreationConfiguration(actual, creation);
3448
3461
  if (!intent.resource) {
3449
- const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
3462
+ const group = new GroupStore(join38(journal.records.directory, "group.json")).state.group;
3450
3463
  return {
3451
3464
  ...admitClaim(
3452
3465
  { ...resource, runtime: intent.runtime, state: creation.state },
@@ -3483,7 +3496,7 @@ var init_creation = __esm({
3483
3496
  });
3484
3497
 
3485
3498
  // src/harness/launch/recovery.ts
3486
- import { existsSync as existsSync25 } from "fs";
3499
+ import { existsSync as existsSync28 } from "fs";
3487
3500
  function abortLaunch(connection, journal) {
3488
3501
  const intent = journal.intent();
3489
3502
  if (!controllerExited(intent.controllerIdentity))
@@ -3500,7 +3513,7 @@ function abortLaunch(connection, journal) {
3500
3513
  inputReplayed: false
3501
3514
  };
3502
3515
  }
3503
- if (!existsSync25(journal.path("proof"))) {
3516
+ if (!existsSync28(journal.path("proof"))) {
3504
3517
  const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
3505
3518
  if (paneUnknown || unitActive(intent.unit))
3506
3519
  throw new Error("launch_identity_unknown");
@@ -3527,7 +3540,7 @@ function abortLaunch(connection, journal) {
3527
3540
  assertProof(connection, proof);
3528
3541
  connection.tmux(["kill-pane", "-t", proof.session.paneId]);
3529
3542
  }
3530
- if (existsSync25(journal.records.path(intent.id)))
3543
+ if (existsSync28(journal.records.path(intent.id)))
3531
3544
  journal.records.save({
3532
3545
  ...proof.session,
3533
3546
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3553,10 +3566,10 @@ var init_recovery = __esm({
3553
3566
  });
3554
3567
 
3555
3568
  // src/harness/adapters/tmux/lifecycle.ts
3556
- import { randomUUID as randomUUID7 } from "crypto";
3557
- import { existsSync as existsSync26 } from "fs";
3569
+ import { randomUUID as randomUUID8 } from "crypto";
3570
+ import { existsSync as existsSync29 } from "fs";
3558
3571
  import { setTimeout as setTimeout2 } from "timers/promises";
3559
- import { join as join37 } from "path";
3572
+ import { join as join39 } from "path";
3560
3573
  async function start(connection, spec) {
3561
3574
  const records2 = connection.records;
3562
3575
  if (spec.budgetSlice !== budgetSlice(records2.owner))
@@ -3566,11 +3579,11 @@ async function start(connection, spec) {
3566
3579
  runtimeUnit(spec.generation);
3567
3580
  if (spec.role !== "main" && spec.role !== "worker")
3568
3581
  throw new Error("invalid_launch_role");
3569
- const id2 = randomUUID7();
3570
- const nonce = randomUUID7();
3582
+ const id2 = randomUUID8();
3583
+ const nonce = randomUUID8();
3571
3584
  const journal = new LaunchJournal(records2, id2);
3572
3585
  const dockerCreation = spec.dockerPreparation ? creationPlan(
3573
- new GroupStore(join37(records2.directory, "group.json")).state.group,
3586
+ new GroupStore(join39(records2.directory, "group.json")).state.group,
3574
3587
  records2.directory,
3575
3588
  id2,
3576
3589
  nonce,
@@ -3630,7 +3643,7 @@ async function start(connection, spec) {
3630
3643
  `exec ${argv.map(quote2).join(" ")}`
3631
3644
  ]);
3632
3645
  const deadline = Date.now() + 5e3;
3633
- while (!existsSync26(journal.path("proof"))) {
3646
+ while (!existsSync29(journal.path("proof"))) {
3634
3647
  if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
3635
3648
  await setTimeout2(25);
3636
3649
  }
@@ -3641,7 +3654,7 @@ async function start(connection, spec) {
3641
3654
  }
3642
3655
  function stop(connection, id2) {
3643
3656
  const journal = new LaunchJournal(connection.records, id2);
3644
- if (existsSync26(journal.path("intent"))) {
3657
+ if (existsSync29(journal.path("intent"))) {
3645
3658
  abortLaunch(connection, journal);
3646
3659
  return;
3647
3660
  }
@@ -3668,7 +3681,7 @@ function cleanup(connection) {
3668
3681
  }
3669
3682
  for (const record of records2) {
3670
3683
  if (record.resource) stopResource(record.resource);
3671
- if (record.stoppedAt || !existsSync26(
3684
+ if (record.stoppedAt || !existsSync29(
3672
3685
  new LaunchJournal(connection.records, record.id).path("intent")
3673
3686
  )) {
3674
3687
  stopUnit(record.unit);
@@ -3698,9 +3711,9 @@ var init_lifecycle2 = __esm({
3698
3711
  });
3699
3712
 
3700
3713
  // 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";
3714
+ import { randomUUID as randomUUID9 } from "crypto";
3715
+ import { closeSync as closeSync7, openSync as openSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
3716
+ import { join as join40 } from "path";
3704
3717
  function validateText(text3) {
3705
3718
  if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
3706
3719
  throw new Error("invalid_text");
@@ -3731,9 +3744,9 @@ function write(connection, id2, text3, submit) {
3731
3744
  validateText(text3);
3732
3745
  const session = connection.inspect(id2);
3733
3746
  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()}`;
3747
+ const lock = join40(connection.records.directory, `${id2}.input.lock`);
3748
+ const descriptor = openSync7(lock, "wx", 384);
3749
+ const buffer = `pmcp-${randomUUID9()}`;
3737
3750
  try {
3738
3751
  writeFileSync12(
3739
3752
  descriptor,
@@ -3773,7 +3786,7 @@ function write(connection, id2, text3, submit) {
3773
3786
  connection.tmux(["delete-buffer", "-b", buffer]);
3774
3787
  } catch {
3775
3788
  }
3776
- closeSync6(descriptor);
3789
+ closeSync7(descriptor);
3777
3790
  unlinkSync3(lock);
3778
3791
  }
3779
3792
  }
@@ -3849,7 +3862,6 @@ var init_backend = __esm({
3849
3862
  this.runDirectory = recordStore.directory;
3850
3863
  this.metadataPath = recordStore.metadataPath;
3851
3864
  }
3852
- recordStore;
3853
3865
  connection;
3854
3866
  owner;
3855
3867
  socket;
@@ -3890,10 +3902,10 @@ var init_backend = __esm({
3890
3902
  });
3891
3903
 
3892
3904
  // src/harness/adapters/systemd/budget.ts
3893
- import { join as join39 } from "path";
3905
+ import { join as join41 } from "path";
3894
3906
  async function budgetOperation(directory, operation) {
3895
3907
  const backend = openBackend(directory);
3896
- const path = join39(directory, "runtime-policy.json");
3908
+ const path = join41(directory, "runtime-policy.json");
3897
3909
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
3898
3910
  const slice = budgetSlice(backend.owner);
3899
3911
  if (operation.action === "configure") {
@@ -3994,7 +4006,7 @@ var init_budget = __esm({
3994
4006
 
3995
4007
  // src/harness/adapters/systemd/configure.ts
3996
4008
  import { execFileSync as execFileSync3 } from "child_process";
3997
- import { join as join40 } from "path";
4009
+ import { join as join42 } from "path";
3998
4010
  function configureBudget(context, input) {
3999
4011
  const { store, backend } = context;
4000
4012
  assertOwnedRecords(backend);
@@ -4016,7 +4028,7 @@ function configureBudget(context, input) {
4016
4028
  throw new Error("capacity_below_active_sessions");
4017
4029
  if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
4018
4030
  throw new Error("budget_below_reserved_memory");
4019
- const path = join40(backend.runDirectory, "runtime-policy.json");
4031
+ const path = join42(backend.runDirectory, "runtime-policy.json");
4020
4032
  const policy = object8(readPrivateJson2(path));
4021
4033
  const metadata2 = object8(readPrivateJson2(backend.metadataPath));
4022
4034
  const slice = budgetSlice(backend.owner);
@@ -4054,11 +4066,11 @@ var init_configure = __esm({
4054
4066
 
4055
4067
  // src/harness/adapters/process/executable.ts
4056
4068
  import { accessSync, constants as constants6, statSync as statSync16 } from "fs";
4057
- import { delimiter, resolve as resolve19 } from "path";
4069
+ import { delimiter, resolve as resolve18 } from "path";
4058
4070
  function locateExecutable(binary, cwd = process.cwd()) {
4059
4071
  for (const directory of (process.env.PATH ?? "").split(delimiter)) {
4060
4072
  if (!directory) continue;
4061
- const path = resolve19(cwd, directory, binary);
4073
+ const path = resolve18(cwd, directory, binary);
4062
4074
  try {
4063
4075
  if (!statSync16(path).isFile()) continue;
4064
4076
  accessSync(path, constants6.X_OK);
@@ -4110,11 +4122,11 @@ var init_admission2 = __esm({
4110
4122
  });
4111
4123
 
4112
4124
  // 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";
4125
+ import { randomUUID as randomUUID10 } from "crypto";
4126
+ import { realpathSync as realpathSync12, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
4127
+ import { join as join43 } from "path";
4116
4128
  async function createGroup(input) {
4117
- const projectRoot = realpathSync10(text(input.projectRoot, "project_root"));
4129
+ const projectRoot = realpathSync12(text(input.projectRoot, "project_root"));
4118
4130
  if (!statSync17(projectRoot).isDirectory())
4119
4131
  throw new Error("invalid_project_root");
4120
4132
  const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
@@ -4134,8 +4146,8 @@ async function createGroup(input) {
4134
4146
  const group = {
4135
4147
  schemaVersion: 1,
4136
4148
  owner: backend.owner,
4137
- id: randomUUID9(),
4138
- generation: randomUUID9(),
4149
+ id: randomUUID10(),
4150
+ generation: randomUUID10(),
4139
4151
  projectRoot,
4140
4152
  allowedRuntimes,
4141
4153
  profile,
@@ -4145,7 +4157,7 @@ async function createGroup(input) {
4145
4157
  sessions: [],
4146
4158
  ...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
4147
4159
  };
4148
- const groupFile = join41(backend.runDirectory, "group.json");
4160
+ const groupFile = join43(backend.runDirectory, "group.json");
4149
4161
  privateJson(groupFile, { group, leases: [], deliveries: [] });
4150
4162
  try {
4151
4163
  await budgetOperation(backend.runDirectory, {
@@ -4158,7 +4170,7 @@ async function createGroup(input) {
4158
4170
  await budgetOperation(backend.runDirectory, { action: "cleanup" });
4159
4171
  unlinkSync4(groupFile);
4160
4172
  } catch (cleanupError) {
4161
- privateJson(join41(backend.runDirectory, "initialization-failure.json"), {
4173
+ privateJson(join43(backend.runDirectory, "initialization-failure.json"), {
4162
4174
  error: String(error),
4163
4175
  cleanupError: String(cleanupError),
4164
4176
  recoveryRequired: true
@@ -4220,16 +4232,16 @@ var init_service2 = __esm({
4220
4232
  });
4221
4233
 
4222
4234
  // src/harness/groups/catalog.ts
4223
- import { existsSync as existsSync27, readdirSync as readdirSync12 } from "fs";
4224
- import { join as join42 } from "path";
4235
+ import { existsSync as existsSync30, readdirSync as readdirSync12 } from "fs";
4236
+ import { join as join44 } from "path";
4225
4237
  function listGroups() {
4226
4238
  const directory = stateDirectory();
4227
- if (!existsSync27(directory)) return { groups: [] };
4239
+ if (!existsSync30(directory)) return { groups: [] };
4228
4240
  const groups2 = [];
4229
4241
  for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
4230
4242
  if (!entry2.isDirectory()) continue;
4231
- const path = join42(directory, entry2.name, "group.json");
4232
- if (!existsSync27(path)) continue;
4243
+ const path = join44(directory, entry2.name, "group.json");
4244
+ if (!existsSync30(path)) continue;
4233
4245
  try {
4234
4246
  const store = new GroupStore(path);
4235
4247
  groups2.push({ groupFile: store.path, group: store.state.group });
@@ -4288,10 +4300,10 @@ var init_service3 = __esm({
4288
4300
  });
4289
4301
 
4290
4302
  // src/harness/application/context.ts
4291
- import { dirname as dirname21 } from "path";
4303
+ import { dirname as dirname23 } from "path";
4292
4304
  function openGroup(path) {
4293
4305
  const store = new GroupStore(path);
4294
- const backend = openBackend(dirname21(store.path));
4306
+ const backend = openBackend(dirname23(store.path));
4295
4307
  if (store.state.group.owner !== backend.owner)
4296
4308
  throw new Error("foreign_group");
4297
4309
  return { store, backend };
@@ -4425,8 +4437,8 @@ var init_watch = __esm({
4425
4437
  });
4426
4438
 
4427
4439
  // src/harness/control/leases.ts
4428
- import { randomUUID as randomUUID10 } from "crypto";
4429
- import { join as join43 } from "path";
4440
+ import { randomUUID as randomUUID11 } from "crypto";
4441
+ import { join as join45 } from "path";
4430
4442
  function resolveLease(context, file2, allowExpired = false) {
4431
4443
  const credential2 = object8(readPrivateJson2(file2));
4432
4444
  const lease = context.store.state.leases.find(
@@ -4458,7 +4470,7 @@ function acquireControl(context, sessionId, controller, mode) {
4458
4470
  if (mode !== "cli" && mode !== "native")
4459
4471
  throw new Error("invalid_control_mode");
4460
4472
  const lease = {
4461
- token: randomUUID10(),
4473
+ token: randomUUID11(),
4462
4474
  sessionId,
4463
4475
  generation: store.state.group.generation,
4464
4476
  controller: id(controller, "controller"),
@@ -4470,7 +4482,7 @@ function acquireControl(context, sessionId, controller, mode) {
4470
4482
  );
4471
4483
  store.state.leases.push(lease);
4472
4484
  store.save();
4473
- const leaseFile = join43(backend.runDirectory, `lease-${randomUUID10()}.json`);
4485
+ const leaseFile = join45(backend.runDirectory, `lease-${randomUUID11()}.json`);
4474
4486
  privateJson(leaseFile, { token: lease.token });
4475
4487
  return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
4476
4488
  }
@@ -4617,18 +4629,18 @@ var init_service5 = __esm({
4617
4629
  });
4618
4630
 
4619
4631
  // 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";
4632
+ import { existsSync as existsSync31, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
4633
+ import { join as join46 } from "path";
4622
4634
  function recoverInputLock(backend, sessionId) {
4623
4635
  backend.inspect(sessionId);
4624
- const path = join44(backend.runDirectory, `${sessionId}.input.lock`);
4625
- if (!existsSync28(path)) return;
4626
- const before = lstatSync10(path);
4636
+ const path = join46(backend.runDirectory, `${sessionId}.input.lock`);
4637
+ if (!existsSync31(path)) return;
4638
+ const before = lstatSync11(path);
4627
4639
  const record = object8(readPrivateJson2(path));
4628
4640
  if (record.id !== sessionId) throw new Error("foreign_writer_lock");
4629
4641
  if (!controllerExited(text(record.controller, "controller_identity")))
4630
4642
  throw new Error("writer_still_alive");
4631
- if (lstatSync10(path).ino !== before.ino)
4643
+ if (lstatSync11(path).ino !== before.ino)
4632
4644
  throw new Error("writer_lock_changed");
4633
4645
  unlinkSync5(path);
4634
4646
  }
@@ -4642,7 +4654,7 @@ var init_recover_lock = __esm({
4642
4654
  });
4643
4655
 
4644
4656
  // src/harness/launch/service.ts
4645
- import { existsSync as existsSync29 } from "fs";
4657
+ import { existsSync as existsSync32 } from "fs";
4646
4658
  function assertLaunchAdmission(records2) {
4647
4659
  for (const journal of launchJournals(records2)) {
4648
4660
  if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
@@ -4667,12 +4679,12 @@ function inspectPendingStarts(context) {
4667
4679
  role: journal.intent().role,
4668
4680
  runtime: journal.intent().runtime,
4669
4681
  controllerExited: controllerExited(journal.intent().controllerIdentity),
4670
- proofPresent: existsSync29(journal.path("proof")),
4671
- recordPresent: existsSync29(journal.records.path(journal.id)),
4682
+ proofPresent: existsSync32(journal.path("proof")),
4683
+ recordPresent: existsSync32(journal.records.path(journal.id)),
4672
4684
  registered: context.store.state.group.sessions.some(
4673
4685
  (session) => session.id === journal.id
4674
4686
  ),
4675
- authorizationPresent: existsSync29(journal.path("grant")),
4687
+ authorizationPresent: existsSync32(journal.path("grant")),
4676
4688
  processingConfirmed: false,
4677
4689
  inputReplayed: false
4678
4690
  }));
@@ -4690,7 +4702,7 @@ var init_service6 = __esm({
4690
4702
  });
4691
4703
 
4692
4704
  // src/harness/sessions/service.ts
4693
- import { realpathSync as realpathSync11 } from "fs";
4705
+ import { realpathSync as realpathSync13 } from "fs";
4694
4706
  import { relative as relative15 } from "path";
4695
4707
  async function startSession(context, input) {
4696
4708
  const { store, backend } = context;
@@ -4724,7 +4736,7 @@ async function startSession(context, input) {
4724
4736
  );
4725
4737
  if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
4726
4738
  }
4727
- const cwd = realpathSync11(text(input.cwd ?? group.projectRoot, "cwd"));
4739
+ const cwd = realpathSync13(text(input.cwd ?? group.projectRoot, "cwd"));
4728
4740
  const scope = relative15(group.projectRoot, cwd);
4729
4741
  if (scope === ".." || scope.startsWith("../"))
4730
4742
  throw new Error("cwd_outside_project");
@@ -4832,7 +4844,7 @@ var init_service7 = __esm({
4832
4844
  });
4833
4845
 
4834
4846
  // src/harness/recovery/service.ts
4835
- import { randomUUID as randomUUID11 } from "crypto";
4847
+ import { randomUUID as randomUUID12 } from "crypto";
4836
4848
  function inspectRecovery(context, includeStarts = false) {
4837
4849
  const pendingStarts = includeStarts ? inspectPendingStarts(context) : [];
4838
4850
  return {
@@ -4915,7 +4927,7 @@ async function replaceMain(context, input) {
4915
4927
  if (observation.alive) throw new Error("main_still_alive");
4916
4928
  previous.stopped = true;
4917
4929
  }
4918
- store.state.group.generation = randomUUID11();
4930
+ store.state.group.generation = randomUUID12();
4919
4931
  store.state.leases = [];
4920
4932
  store.save();
4921
4933
  const result = await startSession(context, { ...input, role: "main" });
@@ -5187,9 +5199,9 @@ var init_invoke = __esm({
5187
5199
  });
5188
5200
 
5189
5201
  // src/tui/adapters/process/runtime.ts
5190
- import { dirname as dirname22, join as join45 } from "path";
5202
+ import { dirname as dirname24, join as join47 } from "path";
5191
5203
  import { fileURLToPath as fileURLToPath4 } from "url";
5192
- import { existsSync as existsSync30, realpathSync as realpathSync12 } from "fs";
5204
+ import { existsSync as existsSync33, realpathSync as realpathSync14 } from "fs";
5193
5205
  function dependencies() {
5194
5206
  const tmux = locateExecutable("tmux");
5195
5207
  const flock = locateExecutable("flock");
@@ -5222,10 +5234,10 @@ function runtime() {
5222
5234
  if (!available.supported || !available.node)
5223
5235
  throw new Error("tui_dependencies_unavailable");
5224
5236
  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 };
5237
+ const directory = dirname24(current);
5238
+ const bundled = join47(directory, "tui-worker.js");
5239
+ if (!existsSync33(bundled)) throw new Error("tui_build_required");
5240
+ return { node: realpathSync14(available.node), worker: bundled };
5229
5241
  }
5230
5242
  var init_runtime2 = __esm({
5231
5243
  "src/tui/adapters/process/runtime.ts"() {
@@ -5236,35 +5248,108 @@ var init_runtime2 = __esm({
5236
5248
  }
5237
5249
  });
5238
5250
 
5251
+ // src/tui/startup/diagnostics.ts
5252
+ function remediation(tools, userManagerAvailable) {
5253
+ const steps = [];
5254
+ if (tools.platform !== "linux")
5255
+ steps.push(
5256
+ "Use a qualified Linux host for the terminal workspace. Other PMCP CLI features do not require this host profile."
5257
+ );
5258
+ const missing = ["tmux", "flock", "systemctl", "systemdRun"].filter(
5259
+ (name) => !tools[name]
5260
+ );
5261
+ if (missing.length)
5262
+ steps.push(
5263
+ `Install the missing tmux, util-linux or systemd tools for your distribution and add them to PATH: ${missing.join(", ")}.`
5264
+ );
5265
+ if (!tools.nodeSupported)
5266
+ steps.push(
5267
+ "Select Node.js 22 or later through your existing version manager and check node --version in this terminal."
5268
+ );
5269
+ if (tools.supported && !userManagerAvailable)
5270
+ steps.push(
5271
+ "Check systemctl --user status in your login session and restore access to its user manager before launching. Doctor does not start or repair services."
5272
+ );
5273
+ return steps;
5274
+ }
5275
+ function inspectTerminalHost() {
5276
+ const tools = dependencies();
5277
+ let userManagerAvailable = false;
5278
+ let reason;
5279
+ try {
5280
+ if (!tools.supported) throw new Error("tui_dependencies_unavailable");
5281
+ assertNativeHost();
5282
+ userManagerAvailable = true;
5283
+ } catch (error) {
5284
+ reason = error instanceof Error ? error.message : "native_host_unavailable";
5285
+ }
5286
+ const runtimes = Object.entries(runtimeExecutables).map(
5287
+ ([id2, executable]) => {
5288
+ const path = locateExecutable(executable);
5289
+ return {
5290
+ id: id2,
5291
+ executable,
5292
+ installed: Boolean(path),
5293
+ ...path ? { path } : {},
5294
+ nativeAcceptance: "not-assessed",
5295
+ authentication: "not-inspected"
5296
+ };
5297
+ }
5298
+ );
5299
+ return {
5300
+ schemaVersion: 1,
5301
+ ok: true,
5302
+ ...tools,
5303
+ supported: tools.supported && userManagerAvailable,
5304
+ userManagerAvailable,
5305
+ ...reason ? { reason } : {},
5306
+ runtimes,
5307
+ qualification: "Prerequisite availability only; native behavior and cgroup delegation require separate qualification.",
5308
+ remediation: remediation(tools, userManagerAvailable),
5309
+ nextCommands: ["pmcp tui plan", "pmcp tui recovery"],
5310
+ providerInvoked: false,
5311
+ authenticationInspected: false
5312
+ };
5313
+ }
5314
+ var init_diagnostics = __esm({
5315
+ "src/tui/startup/diagnostics.ts"() {
5316
+ "use strict";
5317
+ init_runtime2();
5318
+ init_admission2();
5319
+ init_registry();
5320
+ init_executable();
5321
+ }
5322
+ });
5323
+
5239
5324
  // src/tui/state/store.ts
5240
- import { randomUUID as randomUUID12 } from "crypto";
5325
+ import { randomUUID as randomUUID13 } from "crypto";
5241
5326
  import {
5242
- existsSync as existsSync31,
5243
- lstatSync as lstatSync11,
5327
+ existsSync as existsSync34,
5328
+ lstatSync as lstatSync12,
5244
5329
  mkdirSync as mkdirSync11,
5245
- realpathSync as realpathSync13,
5330
+ realpathSync as realpathSync15,
5246
5331
  unlinkSync as unlinkSync6,
5247
5332
  writeFileSync as writeFileSync13
5248
5333
  } from "fs";
5249
5334
  import { homedir as homedir10 } from "os";
5250
- import { dirname as dirname23, join as join46 } from "path";
5335
+ import { dirname as dirname25, join as join48 } from "path";
5251
5336
  function createDirectory() {
5252
- const root = join46(
5253
- process.env.XDG_STATE_HOME ?? join46(homedir10(), ".local/state"),
5337
+ const root = join48(
5338
+ process.env.XDG_STATE_HOME ?? join48(homedir10(), ".local/state"),
5254
5339
  "pmcp/tui"
5255
5340
  );
5256
5341
  mkdirSync11(root, { recursive: true, mode: 448 });
5257
- const directory = join46(realpathSync13(root), randomUUID12().slice(0, 16));
5258
- if (Buffer.byteLength(join46(directory, "s")) > 100)
5342
+ const directory = join48(realpathSync15(root), randomUUID13().slice(0, 16));
5343
+ if (Buffer.byteLength(join48(directory, "s")) > 100)
5259
5344
  throw new Error("socket_path_too_long");
5260
5345
  mkdirSync11(directory, { mode: 448 });
5261
- const owner = randomUUID12();
5262
- privateJson(join46(directory, "owner.json"), {
5346
+ const owner = randomUUID13();
5347
+ privateJson(join48(directory, "owner.json"), {
5263
5348
  owner,
5264
5349
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5265
5350
  });
5266
5351
  writeFileSync13(
5267
- join46(directory, "tmux.conf"),
5352
+ join48(directory, "tmux.conf"),
5268
5353
  [
5269
5354
  "set -g remain-on-exit on",
5270
5355
  "set -g history-limit 2000",
@@ -5279,13 +5364,13 @@ function createDirectory() {
5279
5364
  return { directory, owner };
5280
5365
  }
5281
5366
  function readView(path) {
5282
- const file2 = realpathSync13(path);
5283
- const directory = dirname23(file2);
5284
- const stat = lstatSync11(directory);
5367
+ const file2 = realpathSync15(path);
5368
+ const directory = dirname25(file2);
5369
+ const stat = lstatSync12(directory);
5285
5370
  if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
5286
5371
  throw new Error("unsafe_view_directory");
5287
5372
  const value = object8(readPrivateJson2(file2));
5288
- const metadata2 = object8(readPrivateJson2(join46(directory, "owner.json")));
5373
+ const metadata2 = object8(readPrivateJson2(join48(directory, "owner.json")));
5289
5374
  if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
5290
5375
  throw new Error("foreign_view_owner");
5291
5376
  for (const key of [
@@ -5322,8 +5407,8 @@ function readView(path) {
5322
5407
  throw new Error("invalid_view_identity");
5323
5408
  }
5324
5409
  const view = value;
5325
- const fallback = join46(directory, "callback-failure.json");
5326
- if (existsSync31(fallback)) {
5410
+ const fallback = join48(directory, "callback-failure.json");
5411
+ if (existsSync34(fallback)) {
5327
5412
  const recorded = object8(readPrivateJson2(fallback));
5328
5413
  const failure2 = object8(recorded.failure);
5329
5414
  if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
@@ -5334,8 +5419,8 @@ function readView(path) {
5334
5419
  return { file: file2, view };
5335
5420
  }
5336
5421
  function dismissFailure(file2, view) {
5337
- const fallback = join46(view.directory, "callback-failure.json");
5338
- if (existsSync31(fallback)) unlinkSync6(fallback);
5422
+ const fallback = join48(view.directory, "callback-failure.json");
5423
+ if (existsSync34(fallback)) unlinkSync6(fallback);
5339
5424
  delete view.failure;
5340
5425
  saveView(file2, view);
5341
5426
  }
@@ -5397,7 +5482,7 @@ var init_lock = __esm({
5397
5482
  });
5398
5483
 
5399
5484
  // src/tui/adapters/process/budget.ts
5400
- import { readFileSync as readFileSync36 } from "fs";
5485
+ import { readFileSync as readFileSync37 } from "fs";
5401
5486
  function viewUnit(owner) {
5402
5487
  if (!/^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(owner))
5403
5488
  throw new Error("invalid_view_owner");
@@ -5425,7 +5510,7 @@ function startPresentation(owner, args, env) {
5425
5510
  );
5426
5511
  }
5427
5512
  function assertPresentationBudget(owner, pid) {
5428
- if (!readFileSync36(`/proc/${pid}/cgroup`, "utf8").includes(
5513
+ if (!readFileSync37(`/proc/${pid}/cgroup`, "utf8").includes(
5429
5514
  `/${viewUnit(owner)}
5430
5515
  `
5431
5516
  ))
@@ -5440,7 +5525,7 @@ var init_budget2 = __esm({
5440
5525
 
5441
5526
  // src/tui/adapters/tmux/connection.ts
5442
5527
  import { spawn as spawn3 } from "child_process";
5443
- import { join as join47 } from "path";
5528
+ import { join as join49 } from "path";
5444
5529
  var ViewConnection;
5445
5530
  var init_connection2 = __esm({
5446
5531
  "src/tui/adapters/tmux/connection.ts"() {
@@ -5453,11 +5538,8 @@ var init_connection2 = __esm({
5453
5538
  this.directory = directory;
5454
5539
  this.owner = owner;
5455
5540
  this.identity = identity;
5456
- this.socket = join47(directory, "s");
5541
+ this.socket = join49(directory, "s");
5457
5542
  }
5458
- directory;
5459
- owner;
5460
- identity;
5461
5543
  socket;
5462
5544
  raw(args) {
5463
5545
  const env = { ...process.env };
@@ -5466,14 +5548,14 @@ var init_connection2 = __esm({
5466
5548
  "-S",
5467
5549
  this.socket,
5468
5550
  "-f",
5469
- join47(this.directory, "tmux.conf"),
5551
+ join49(this.directory, "tmux.conf"),
5470
5552
  ...args
5471
5553
  ];
5472
5554
  if (args[0] === "new-session" && !this.identity)
5473
5555
  return startPresentation(this.owner, options2, env).trim();
5474
5556
  return command(
5475
5557
  "tmux",
5476
- ["-S", this.socket, "-f", join47(this.directory, "tmux.conf"), ...args],
5558
+ ["-S", this.socket, "-f", join49(this.directory, "tmux.conf"), ...args],
5477
5559
  { env }
5478
5560
  ).trim();
5479
5561
  }
@@ -5537,7 +5619,7 @@ var init_connection2 = __esm({
5537
5619
  });
5538
5620
 
5539
5621
  // src/tui/application/feedback.ts
5540
- import { join as join48 } from "path";
5622
+ import { join as join50 } from "path";
5541
5623
  function describeFailure(action, error) {
5542
5624
  const message = error instanceof Error ? error.message : "view_action_failed";
5543
5625
  const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
@@ -5574,6 +5656,19 @@ var init_feedback = __esm({
5574
5656
  init_store3();
5575
5657
  init_store2();
5576
5658
  hints = {
5659
+ startup_requires_recovery: "Run pmcp tui recovery for saved ownership and exact inspection commands. No runtime was restarted.",
5660
+ startup_group_paused: "The saved group is paused. Resume it explicitly through the harness CLI before reopening.",
5661
+ startup_main_unavailable: "Inspect the saved main and recovery state. Stop the old group explicitly before using pmcp tui --new.",
5662
+ startup_busy: "Another workspace launch is running. Wait and retry.",
5663
+ startup_runtime_conflict: "The saved main uses another runtime. Stop the old group before creating a new workspace.",
5664
+ workspace_already_running: "Reopen the existing workspace, or stop its exact owned group before using --new.",
5665
+ runtime_executable_unavailable: "Install the selected native CLI on PATH, or choose an installed runtime with --runtime.",
5666
+ view_client_unavailable: "Open the view before using the worker menu, or run pmcp tui start-worker with --view and --runtime.",
5667
+ runtime_not_granted: "Choose a runtime granted by this owned group. Inspect the group before creating another workspace.",
5668
+ runtime_capacity_exceeded: "Stop an exact owned worker before adding another, or use the main session alone.",
5669
+ aggregate_memory_budget_exceeded: "Use fewer sessions or start a worker with an explicit smaller --memory-mb reservation.",
5670
+ group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
5671
+ startup_group_not_ready: "Inspect the group and resume a paused group explicitly before adding a worker.",
5577
5672
  control_connection_lost: "The native writer or lease disappeared. Inspect and release control before reconnecting.",
5578
5673
  control_busy: "Another controller holds the lease. Release it explicitly before retrying.",
5579
5674
  native_writer_active: "A native writer is still attached. Detach that writer before retrying.",
@@ -5594,6 +5689,20 @@ var init_feedback = __esm({
5594
5689
  }
5595
5690
  });
5596
5691
 
5692
+ // src/tui/presentation/target.ts
5693
+ function targetStatus(view) {
5694
+ const role = view.target === view.mainId ? "MAIN" : "WORKER";
5695
+ const phase2 = view.phase === "read-only" ? "OBSERVE" : phaseLabel(view.phase);
5696
+ const input = view.phase === "controlled" ? "INPUT ON" : "INPUT OFF";
5697
+ return `${role} ${view.target.slice(0, 8)} | ${phase2} | ${input}`;
5698
+ }
5699
+ var init_target = __esm({
5700
+ "src/tui/presentation/target.ts"() {
5701
+ "use strict";
5702
+ init_feedback();
5703
+ }
5704
+ });
5705
+
5597
5706
  // src/tui/adapters/tmux/bindings.ts
5598
5707
  function callback(view, file2, action, args = []) {
5599
5708
  return [
@@ -5620,7 +5729,8 @@ function bindKeys(connection, view, file2) {
5620
5729
  ["b", "mailbox"],
5621
5730
  ["d", "detach"],
5622
5731
  ["!", "dismiss"],
5623
- ["g", "management"]
5732
+ ["g", "management"],
5733
+ ["n", "worker-menu"]
5624
5734
  ])
5625
5735
  connection.command([
5626
5736
  "bind-key",
@@ -5641,7 +5751,7 @@ function bindKeys(connection, view, file2) {
5641
5751
  "bind-key",
5642
5752
  "?",
5643
5753
  "display-message",
5644
- "m main; w/1-4 worker; c control; r release; h history; b mailbox; d detach; ! dismiss error; double Ctrl-g forwards native Ctrl-g"
5754
+ "m main; n add worker; w/1-4 worker; c control; r release; h history; b mailbox; d detach; ! dismiss error; double Ctrl-g forwards native Ctrl-g"
5645
5755
  ]);
5646
5756
  connection.command([
5647
5757
  "set-hook",
@@ -5669,7 +5779,7 @@ var init_view = __esm({
5669
5779
  "src/tui/adapters/tmux/view.ts"() {
5670
5780
  "use strict";
5671
5781
  init_command();
5672
- init_feedback();
5782
+ init_target();
5673
5783
  init_connection2();
5674
5784
  init_bindings();
5675
5785
  TmuxView = class {
@@ -5682,8 +5792,6 @@ var init_view = __esm({
5682
5792
  view.serverIdentity
5683
5793
  );
5684
5794
  }
5685
- view;
5686
- stateFile;
5687
5795
  connection;
5688
5796
  shell(argv) {
5689
5797
  return `exec ${argv.map(quote2).join(" ")}`;
@@ -5855,7 +5963,7 @@ var init_view = __esm({
5855
5963
  "set-option",
5856
5964
  "-g",
5857
5965
  "status-right",
5858
- `${view.target === view.mainId ? "main" : "worker"} | ${phaseLabel(view.phase)}${failure2} | Ctrl-g ?`
5966
+ `${targetStatus(view)}${failure2} | Ctrl-g ?`
5859
5967
  ]);
5860
5968
  }
5861
5969
  input(pane, enabled) {
@@ -5878,6 +5986,32 @@ var init_view = __esm({
5878
5986
  clients() {
5879
5987
  return this.connection.command(["list-clients", "-F", "#{client_pid}"]);
5880
5988
  }
5989
+ workerMenu(view) {
5990
+ const pane = view.workerPane ?? view.mainPane;
5991
+ this.connection.assertOwned(pane);
5992
+ const popup = [
5993
+ "tmux",
5994
+ "-S",
5995
+ this.connection.socket,
5996
+ "display-popup",
5997
+ "-E",
5998
+ "-t",
5999
+ pane,
6000
+ "-w",
6001
+ "90%",
6002
+ "-h",
6003
+ "80%",
6004
+ this.shell([
6005
+ view.node,
6006
+ view.worker,
6007
+ "worker-menu",
6008
+ "--view",
6009
+ this.stateFile,
6010
+ "--callback"
6011
+ ])
6012
+ ];
6013
+ this.connection.command(["run-shell", "-b", popup.map(quote2).join(" ")]);
6014
+ }
5881
6015
  copy(pane) {
5882
6016
  this.connection.assertOwned(pane);
5883
6017
  this.connection.command(["copy-mode", "-t", pane]);
@@ -5901,7 +6035,6 @@ var init_common = __esm({
5901
6035
  constructor(view) {
5902
6036
  this.view = view;
5903
6037
  }
5904
- view;
5905
6038
  async call(family, action, input = {}) {
5906
6039
  const group = new GroupStore(this.view.groupFile).state.group;
5907
6040
  if (group.owner !== this.view.groupOwner) throw new Error("foreign_group");
@@ -5935,16 +6068,16 @@ var init_common = __esm({
5935
6068
  });
5936
6069
 
5937
6070
  // src/tui/application/create.ts
5938
- import { join as join49 } from "path";
5939
- import { realpathSync as realpathSync14 } from "fs";
6071
+ import { join as join51 } from "path";
6072
+ import { realpathSync as realpathSync16 } from "fs";
5940
6073
  async function createView(input) {
5941
6074
  const plan2 = planLayout(input.columns, input.rows);
5942
6075
  const helpers = runtime();
5943
6076
  const mailbox = input.mailbox ? {
5944
- config: realpathSync14(input.mailbox.config),
5945
- actorFile: realpathSync14(input.mailbox.actorFile)
6077
+ config: realpathSync16(input.mailbox.config),
6078
+ actorFile: realpathSync16(input.mailbox.actorFile)
5946
6079
  } : void 0;
5947
- const groupFile = realpathSync14(input.groupFile);
6080
+ const groupFile = realpathSync16(input.groupFile);
5948
6081
  const group = new GroupStore(groupFile).state.group;
5949
6082
  const common2 = new CommonHarness({
5950
6083
  groupFile,
@@ -5957,7 +6090,7 @@ async function createView(input) {
5957
6090
  );
5958
6091
  if (!main2) throw new Error("view_requires_live_main");
5959
6092
  const { directory, owner } = createDirectory();
5960
- const file2 = join49(directory, "view.json");
6093
+ const file2 = join51(directory, "view.json");
5961
6094
  const connection = new ViewConnection(directory, owner);
5962
6095
  const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
5963
6096
  const result = connection.raw([
@@ -6185,7 +6318,7 @@ var init_navigation = __esm({
6185
6318
  });
6186
6319
 
6187
6320
  // src/tui/application/history.ts
6188
- import { join as join50 } from "path";
6321
+ import { join as join52 } from "path";
6189
6322
  async function observeHistory(context) {
6190
6323
  const { view, renderer, common: common2, file: file2 } = context;
6191
6324
  await releaseControl(context);
@@ -6193,7 +6326,7 @@ async function observeHistory(context) {
6193
6326
  sessionId: view.target,
6194
6327
  input: { lines: 2e3, maxBytes: 65536 }
6195
6328
  });
6196
- privateJson(join50(view.directory, "history.json"), {
6329
+ privateJson(join52(view.directory, "history.json"), {
6197
6330
  sessionId: view.target,
6198
6331
  content: history.content,
6199
6332
  truncated: history.truncated
@@ -6269,8 +6402,8 @@ var init_mailbox = __esm({
6269
6402
  });
6270
6403
 
6271
6404
  // src/tui/application/lifecycle.ts
6272
- import { existsSync as existsSync32, unlinkSync as unlinkSync7 } from "fs";
6273
- import { join as join51 } from "path";
6405
+ import { existsSync as existsSync35, unlinkSync as unlinkSync7 } from "fs";
6406
+ import { join as join53 } from "path";
6274
6407
  function resizeView({ view, renderer, input }) {
6275
6408
  renderer.resize(view, planLayout(input.columns, input.rows));
6276
6409
  }
@@ -6285,8 +6418,8 @@ async function stopTarget(context) {
6285
6418
  async function closeView(context) {
6286
6419
  await returnToMain(context);
6287
6420
  context.renderer.connection.cleanup();
6288
- const history = join51(context.view.directory, "history.json");
6289
- if (existsSync32(history)) unlinkSync7(history);
6421
+ const history = join53(context.view.directory, "history.json");
6422
+ if (existsSync35(history)) unlinkSync7(history);
6290
6423
  context.view.state = "closed";
6291
6424
  saveView(context.file, context.view);
6292
6425
  }
@@ -6440,6 +6573,11 @@ var init_operations = __esm({
6440
6573
  close: closeView,
6441
6574
  check: checkControl,
6442
6575
  dismiss: ({ view, file: file2 }) => dismissFailure(file2, view),
6576
+ "worker-menu": async (context) => {
6577
+ if (!context.renderer.clients()) throw new Error("view_client_unavailable");
6578
+ await releaseControl(context);
6579
+ context.renderer.workerMenu(context.view);
6580
+ },
6443
6581
  management: async (context) => {
6444
6582
  await releaseControl(context);
6445
6583
  if (context.view.managementWindow) {
@@ -6463,8 +6601,9 @@ __export(invoke_exports2, {
6463
6601
  invokeTui: () => invokeTui
6464
6602
  });
6465
6603
  async function invokeTui(invocation2) {
6466
- if (invocation2.action === "doctor")
6467
- return { schemaVersion: 1, ok: true, ...dependencies() };
6604
+ if (["launch", "plan", "recovery", "start-worker"].includes(invocation2.action))
6605
+ throw new Error("startup_requires_cli_context");
6606
+ if (invocation2.action === "doctor") return inspectTerminalHost();
6468
6607
  if (invocation2.action === "create") return createView(invocation2.create);
6469
6608
  if (invocation2.action === "inspect")
6470
6609
  return operate("inspect", invocation2.viewFile);
@@ -6502,7 +6641,7 @@ function failedInvocation(action, error) {
6502
6641
  var init_invoke2 = __esm({
6503
6642
  "src/tui/application/invoke.ts"() {
6504
6643
  "use strict";
6505
- init_runtime2();
6644
+ init_diagnostics();
6506
6645
  init_lock();
6507
6646
  init_create();
6508
6647
  init_operations();
@@ -6511,6 +6650,543 @@ var init_invoke2 = __esm({
6511
6650
  }
6512
6651
  });
6513
6652
 
6653
+ // src/tui/application/worker-start.ts
6654
+ var worker_start_exports = {};
6655
+ __export(worker_start_exports, {
6656
+ startViewWorker: () => startViewWorker
6657
+ });
6658
+ async function startViewWorker(file2, runtime2, memoryMb) {
6659
+ const { view, common: common2 } = openView(file2);
6660
+ if (view.state !== "open") throw new Error("view_closed");
6661
+ const observed = await common2.snapshot();
6662
+ if (observed.group.state !== "ready") throw new Error("group_not_ready");
6663
+ const main2 = observed.sessions.find(
6664
+ (session) => session.id === view.mainId && session.role === "main"
6665
+ );
6666
+ if (!main2?.alive) throw new Error("startup_main_unavailable");
6667
+ return common2.call("session", "start", {
6668
+ input: { role: "worker", runtime: runtime2, memoryMb: memoryMb ?? main2.memoryMb }
6669
+ });
6670
+ }
6671
+ var init_worker_start = __esm({
6672
+ "src/tui/application/worker-start.ts"() {
6673
+ "use strict";
6674
+ init_context2();
6675
+ }
6676
+ });
6677
+
6678
+ // src/tui/startup/config.ts
6679
+ import { realpathSync as realpathSync17 } from "fs";
6680
+ import { resolve as resolve19 } from "path";
6681
+ function startupSettings(request3) {
6682
+ const path = request3.config ? resolve19(request3.cwd, request3.config) : findConfig(request3.cwd);
6683
+ const config = path ? readConfig(path) : void 0;
6684
+ const source2 = config?.raw.tui;
6685
+ const fail2 = (message) => {
6686
+ throw new ConfigError(path ?? "pmcp.toml", `[tui] ${message}`);
6687
+ };
6688
+ if (source2 !== void 0 && (source2 === null || typeof source2 !== "object" || Array.isArray(source2)))
6689
+ fail2("must be a table");
6690
+ const values = source2 ?? {};
6691
+ const allowed3 = /* @__PURE__ */ new Set([
6692
+ "runtime",
6693
+ "memory_mb",
6694
+ "session_memory_mb",
6695
+ "max_active",
6696
+ "group_file"
6697
+ ]);
6698
+ for (const key of Object.keys(values)) {
6699
+ if (!allowed3.has(key)) fail2(`unknown key ${key}`);
6700
+ }
6701
+ const string = (key) => {
6702
+ const value = values[key];
6703
+ if (value === void 0) return void 0;
6704
+ if (typeof value !== "string" || !value.trim())
6705
+ fail2(`${key} must be a non-empty string`);
6706
+ return value;
6707
+ };
6708
+ const integer2 = (key, fallback, min, max) => {
6709
+ const value = values[key] ?? fallback;
6710
+ if (typeof value !== "number" || !Number.isSafeInteger(value))
6711
+ fail2(`${key} must be an integer`);
6712
+ if (Number(value) < min || Number(value) > max)
6713
+ fail2(`${key} must be between ${min} and ${max}`);
6714
+ return Number(value);
6715
+ };
6716
+ const configuredRuntime = string("runtime");
6717
+ if (configuredRuntime !== void 0 && configuredRuntime !== "auto" && !Object.hasOwn(runtimeExecutables, configuredRuntime))
6718
+ fail2("runtime must be auto or a supported runtime ID");
6719
+ const available = preference.filter(
6720
+ (runtime3) => locateExecutable(runtimeExecutables[runtime3], request3.cwd)
6721
+ );
6722
+ let runtime2 = request3.runtime ?? configuredRuntime ?? "auto";
6723
+ if (runtime2 === "auto") runtime2 = available[0] ?? "codex-cli";
6724
+ if (!Object.hasOwn(runtimeExecutables, runtime2))
6725
+ fail2("runtime must be a supported runtime ID");
6726
+ const memoryMb = integer2("memory_mb", 2048, 128, 4096);
6727
+ const sessionMemoryMb = integer2("session_memory_mb", 768, 32, 1536);
6728
+ if (sessionMemoryMb > memoryMb)
6729
+ fail2("session memory exceeds the group budget");
6730
+ const groupFile = string("group_file");
6731
+ return {
6732
+ projectRoot: realpathSync17(config?.dir ?? request3.cwd),
6733
+ ...config ? { configFile: config.path } : {},
6734
+ runtime: runtime2,
6735
+ allowedRuntimes: [.../* @__PURE__ */ new Set([...available, runtime2])],
6736
+ memoryMb,
6737
+ sessionMemoryMb,
6738
+ maxActive: integer2("max_active", 2, 1, 5),
6739
+ ...groupFile ? { groupFile: resolve19(config?.dir ?? request3.cwd, groupFile) } : {}
6740
+ };
6741
+ }
6742
+ var preference;
6743
+ var init_config3 = __esm({
6744
+ "src/tui/startup/config.ts"() {
6745
+ "use strict";
6746
+ init_config();
6747
+ init_registry();
6748
+ init_executable();
6749
+ preference = [
6750
+ "codex-cli",
6751
+ "gemini-cli",
6752
+ "antigravity",
6753
+ "grok-cli",
6754
+ "claude-code"
6755
+ ];
6756
+ }
6757
+ });
6758
+
6759
+ // src/tui/startup/state.ts
6760
+ import { createHash as createHash19 } from "crypto";
6761
+ import { existsSync as existsSync36, lstatSync as lstatSync13, mkdirSync as mkdirSync12 } from "fs";
6762
+ import { homedir as homedir11 } from "os";
6763
+ import { dirname as dirname26, join as join54 } from "path";
6764
+ function startupFile(projectRoot) {
6765
+ const root = join54(
6766
+ process.env.XDG_STATE_HOME ?? join54(homedir11(), ".local/state"),
6767
+ "pmcp/launcher"
6768
+ );
6769
+ const key = createHash19("sha256").update(projectRoot).digest("hex");
6770
+ return join54(root, `${key}.json`);
6771
+ }
6772
+ function ensureStartupDirectory(file2) {
6773
+ const directory = dirname26(file2);
6774
+ mkdirSync12(directory, { recursive: true, mode: 448 });
6775
+ const stat = lstatSync13(directory);
6776
+ if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
6777
+ throw new Error("unsafe_startup_directory");
6778
+ }
6779
+ function readStartup(file2, projectRoot) {
6780
+ if (!existsSync36(file2)) return null;
6781
+ const value = object8(readPrivateJson2(file2));
6782
+ if (value.schemaVersion !== 1 || value.projectRoot !== projectRoot)
6783
+ throw new Error("foreign_startup_state");
6784
+ if (!["creating", "starting", "view-pending", "ready"].includes(
6785
+ String(value.phase)
6786
+ ))
6787
+ throw new Error("invalid_startup_state");
6788
+ text(value.runtime, "runtime");
6789
+ if (value.phase !== "creating") {
6790
+ text(value.groupFile, "group_file");
6791
+ text(value.generation, "generation");
6792
+ }
6793
+ if (value.phase === "ready") text(value.viewFile, "view_file");
6794
+ return value;
6795
+ }
6796
+ var init_state = __esm({
6797
+ "src/tui/startup/state.ts"() {
6798
+ "use strict";
6799
+ init_store2();
6800
+ init_validation();
6801
+ }
6802
+ });
6803
+
6804
+ // src/tui/startup/worker.ts
6805
+ var worker_exports = {};
6806
+ __export(worker_exports, {
6807
+ startWorkspaceWorker: () => startWorkspaceWorker
6808
+ });
6809
+ async function startWorkspaceWorker(request3, runtime2, memoryMb) {
6810
+ if (!Object.hasOwn(runtimeExecutables, runtime2))
6811
+ throw new Error("unsupported_runtime");
6812
+ const settings2 = startupSettings({
6813
+ cwd: request3.cwd,
6814
+ config: request3.config
6815
+ });
6816
+ const state = readStartup(
6817
+ startupFile(settings2.projectRoot),
6818
+ settings2.projectRoot
6819
+ );
6820
+ if (!state || state.phase !== "ready" || !state.groupFile || !state.generation)
6821
+ throw new Error("startup_requires_recovery");
6822
+ const store = new GroupStore(state.groupFile);
6823
+ const group = store.state.group;
6824
+ if (group.projectRoot !== settings2.projectRoot)
6825
+ throw new Error("startup_project_mismatch");
6826
+ if (group.generation !== state.generation)
6827
+ throw new Error("stale_group_generation");
6828
+ if (group.state === "paused") throw new Error("startup_group_paused");
6829
+ if (group.state !== "ready") throw new Error("startup_group_not_ready");
6830
+ if (group.profile === "native" && !locateExecutable(runtimeExecutables[runtime2], request3.cwd))
6831
+ throw new Error("runtime_executable_unavailable");
6832
+ const observed = await invoke({
6833
+ family: "session",
6834
+ action: "list",
6835
+ groupFile: store.path
6836
+ });
6837
+ if (observed.ok !== true) return observed;
6838
+ const sessions = observed.sessions;
6839
+ if (!sessions.some((session) => session.role === "main" && session.alive))
6840
+ throw new Error("startup_main_unavailable");
6841
+ return invoke({
6842
+ family: "session",
6843
+ action: "start",
6844
+ groupFile: store.path,
6845
+ generation: group.generation,
6846
+ input: {
6847
+ runtime: runtime2,
6848
+ role: "worker",
6849
+ memoryMb: memoryMb ?? settings2.sessionMemoryMb
6850
+ }
6851
+ });
6852
+ }
6853
+ var init_worker = __esm({
6854
+ "src/tui/startup/worker.ts"() {
6855
+ "use strict";
6856
+ init_invoke();
6857
+ init_store2();
6858
+ init_registry();
6859
+ init_executable();
6860
+ init_config3();
6861
+ init_state();
6862
+ }
6863
+ });
6864
+
6865
+ // src/tui/startup/recovery.ts
6866
+ var recovery_exports = {};
6867
+ __export(recovery_exports, {
6868
+ inspectStartupRecovery: () => inspectStartupRecovery
6869
+ });
6870
+ function command2(args) {
6871
+ return ["pmcp", ...args].map(quote2).join(" ");
6872
+ }
6873
+ function inspectStartupRecovery(request3) {
6874
+ const settings2 = startupSettings(request3);
6875
+ const checkpoint = startupFile(settings2.projectRoot);
6876
+ const state = readStartup(checkpoint, settings2.projectRoot);
6877
+ const config = settings2.configFile ? ["--config", settings2.configFile] : [];
6878
+ const common2 = {
6879
+ schemaVersion: 1,
6880
+ ok: true,
6881
+ checkpoint,
6882
+ projectRoot: settings2.projectRoot,
6883
+ providerInvoked: false,
6884
+ runtimeRestarted: false,
6885
+ inputReplayed: false,
6886
+ observedNativeProcesses: false
6887
+ };
6888
+ if (!state)
6889
+ return {
6890
+ ...common2,
6891
+ status: "not-started",
6892
+ hint: "No saved workspace exists. Start explicitly from an interactive terminal.",
6893
+ commands: [command2(["tui", ...config])]
6894
+ };
6895
+ if (!state.groupFile || !state.generation)
6896
+ return {
6897
+ ...common2,
6898
+ status: "manual-inspection",
6899
+ phase: state.phase,
6900
+ hint: "Creation stopped before a group reference was saved. Preserve this checkpoint and inspect owned launch evidence before changing it. No group ownership can be inferred from a matching project path.",
6901
+ commands: []
6902
+ };
6903
+ const store = new GroupStore(state.groupFile);
6904
+ const group = store.state.group;
6905
+ if (group.projectRoot !== settings2.projectRoot)
6906
+ throw new Error("startup_project_mismatch");
6907
+ if (group.generation !== state.generation)
6908
+ throw new Error("stale_group_generation");
6909
+ const target2 = ["--group-file", store.path];
6910
+ const generation = ["--generation", group.generation];
6911
+ const inspect = command2([
6912
+ "harness",
6913
+ "recovery",
6914
+ "inspect",
6915
+ ...target2,
6916
+ "--include-starts"
6917
+ ]);
6918
+ const status = group.state === "ready" ? state.phase : group.state;
6919
+ const commands = [inspect];
6920
+ let hint = "Inspect actual processes and outstanding starts before any recovery action.";
6921
+ if (group.state === "paused") {
6922
+ hint = "The group is paused. Keep it paused until you explicitly decide to resume it.";
6923
+ commands.push(
6924
+ command2(["harness", "group", "resume", ...target2, ...generation])
6925
+ );
6926
+ }
6927
+ if (group.state === "stopped") {
6928
+ hint = "The saved group is stopped. An explicitly confirmed fresh launch can create a replacement.";
6929
+ commands.push(command2(["tui", "--new", ...config]));
6930
+ }
6931
+ if (group.state === "ready" && !["ready", "view-pending"].includes(state.phase)) {
6932
+ hint = "Startup is incomplete. Inspect pending starts; stop the exact owned group before an explicit fresh launch.";
6933
+ commands.push(
6934
+ command2(["harness", "group", "stop", ...target2, ...generation])
6935
+ );
6936
+ }
6937
+ return {
6938
+ ...common2,
6939
+ status,
6940
+ phase: state.phase,
6941
+ groupFile: store.path,
6942
+ generation: group.generation,
6943
+ hint,
6944
+ commands,
6945
+ commandsRequireExplicitAction: true
6946
+ };
6947
+ }
6948
+ var init_recovery2 = __esm({
6949
+ "src/tui/startup/recovery.ts"() {
6950
+ "use strict";
6951
+ init_store2();
6952
+ init_command();
6953
+ init_config3();
6954
+ init_state();
6955
+ }
6956
+ });
6957
+
6958
+ // src/tui/startup/host.ts
6959
+ var host_exports = {};
6960
+ __export(host_exports, {
6961
+ requireTerminal: () => requireTerminal,
6962
+ terminalSupport: () => terminalSupport
6963
+ });
6964
+ function terminalSupport() {
6965
+ try {
6966
+ if (!dependencies().supported)
6967
+ throw new Error("tui_dependencies_unavailable");
6968
+ assertNativeHost();
6969
+ planLayout(process.stdout.columns ?? 0, process.stdout.rows ?? 0);
6970
+ return { supported: true };
6971
+ } catch (error) {
6972
+ return {
6973
+ supported: false,
6974
+ reason: error instanceof Error ? error.message : "terminal_unavailable"
6975
+ };
6976
+ }
6977
+ }
6978
+ function requireTerminal() {
6979
+ if (!interactiveEntry({
6980
+ stdin: process.stdin.isTTY === true,
6981
+ stdout: process.stdout.isTTY === true,
6982
+ term: process.env.TERM,
6983
+ ci: process.env.CI
6984
+ }))
6985
+ throw new Error("terminal_required");
6986
+ const support = terminalSupport();
6987
+ if (!support.supported) throw new Error(support.reason);
6988
+ }
6989
+ var init_host = __esm({
6990
+ "src/tui/startup/host.ts"() {
6991
+ "use strict";
6992
+ init_entry();
6993
+ init_layout();
6994
+ init_admission2();
6995
+ init_runtime2();
6996
+ }
6997
+ });
6998
+
6999
+ // src/tui/startup/lock.ts
7000
+ import { spawnSync as spawnSync6 } from "child_process";
7001
+ import { closeSync as closeSync8, constants as constants7, fstatSync as fstatSync5, openSync as openSync8 } from "fs";
7002
+ function serializedStartup(request3) {
7003
+ const settings2 = startupSettings(request3);
7004
+ const file2 = startupFile(settings2.projectRoot);
7005
+ ensureStartupDirectory(file2);
7006
+ const lock = `${file2}.lock`;
7007
+ const descriptor = openSync8(
7008
+ lock,
7009
+ constants7.O_CREAT | constants7.O_WRONLY | constants7.O_NOFOLLOW,
7010
+ 384
7011
+ );
7012
+ try {
7013
+ const stat = fstatSync5(descriptor);
7014
+ if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
7015
+ throw new Error("unsafe_startup_lock");
7016
+ } finally {
7017
+ closeSync8(descriptor);
7018
+ }
7019
+ const helpers = runtime();
7020
+ const encoded = Buffer.from(JSON.stringify(request3)).toString("base64url");
7021
+ const child = spawnSync6(
7022
+ "flock",
7023
+ [
7024
+ "--wait",
7025
+ "10",
7026
+ "--close",
7027
+ "--conflict-exit-code",
7028
+ "73",
7029
+ lock,
7030
+ helpers.node,
7031
+ helpers.worker,
7032
+ "startup",
7033
+ encoded
7034
+ ],
7035
+ {
7036
+ encoding: "utf8",
7037
+ timeout: 6e4,
7038
+ maxBuffer: 256 * 1024,
7039
+ stdio: ["ignore", "pipe", "pipe"],
7040
+ env: {
7041
+ ...process.env,
7042
+ PMCP_TUI_COLUMNS: String(process.stdout.columns),
7043
+ PMCP_TUI_ROWS: String(process.stdout.rows)
7044
+ }
7045
+ }
7046
+ );
7047
+ if (child.status === 73) throw new Error("startup_busy");
7048
+ if (child.error || !child.stdout.trim())
7049
+ throw new Error("startup_requires_recovery");
7050
+ return JSON.parse(child.stdout);
7051
+ }
7052
+ var init_lock2 = __esm({
7053
+ "src/tui/startup/lock.ts"() {
7054
+ "use strict";
7055
+ init_runtime2();
7056
+ init_state();
7057
+ init_config3();
7058
+ }
7059
+ });
7060
+
7061
+ // src/tui/presentation/runtime-choice.ts
7062
+ import { createInterface as createInterface2 } from "readline/promises";
7063
+ async function chooseRuntime(prompt, choices, preferred) {
7064
+ if (!choices.length) throw new Error("runtime_executable_unavailable");
7065
+ const fallback = Math.max(choices.indexOf(preferred), 0);
7066
+ process.stderr.write(
7067
+ `${choices.map((runtime2, index) => `${index + 1}. ${runtime2}`).join("\n")}
7068
+ `
7069
+ );
7070
+ while (true) {
7071
+ const answer = (await prompt.question(
7072
+ `Runtime [1-${choices.length}, Enter=${fallback + 1}, q=cancel] `
7073
+ )).trim();
7074
+ if (answer === "q") return void 0;
7075
+ if (answer === "") return choices[fallback];
7076
+ if (!/^\d+$/.test(answer)) continue;
7077
+ const index = Number(answer) - 1;
7078
+ if (Number.isSafeInteger(index) && index >= 0 && index < choices.length)
7079
+ return choices[index];
7080
+ }
7081
+ }
7082
+ async function confirmRuntimeStart(prompt, runtime2) {
7083
+ const answer = await prompt.question(
7084
+ `Start ${runtime2}? [Enter=start, q=cancel] `
7085
+ );
7086
+ return answer.trim() === "";
7087
+ }
7088
+ function terminalPrompt() {
7089
+ return createInterface2({ input: process.stdin, output: process.stderr });
7090
+ }
7091
+ var init_runtime_choice = __esm({
7092
+ "src/tui/presentation/runtime-choice.ts"() {
7093
+ "use strict";
7094
+ }
7095
+ });
7096
+
7097
+ // src/tui/startup/entry.ts
7098
+ var entry_exports = {};
7099
+ __export(entry_exports, {
7100
+ launchStartup: () => launchStartup,
7101
+ planStartup: () => planStartup
7102
+ });
7103
+ function planStartup(request3) {
7104
+ const settings2 = startupSettings(request3);
7105
+ const state = readStartup(
7106
+ startupFile(settings2.projectRoot),
7107
+ settings2.projectRoot
7108
+ );
7109
+ return {
7110
+ schemaVersion: 1,
7111
+ ok: true,
7112
+ settings: settings2,
7113
+ savedWorkspace: state,
7114
+ willStartRuntime: !state && !settings2.groupFile,
7115
+ runtimeExecutable: locateExecutable(
7116
+ runtimeExecutables[settings2.runtime],
7117
+ request3.cwd
7118
+ ),
7119
+ providerInvoked: false
7120
+ };
7121
+ }
7122
+ async function launchStartup(request3, confirmed) {
7123
+ requireTerminal();
7124
+ let settings2 = startupSettings(request3);
7125
+ const state = readStartup(
7126
+ startupFile(settings2.projectRoot),
7127
+ settings2.projectRoot
7128
+ );
7129
+ const starting = (!state || request3.fresh) && !settings2.groupFile;
7130
+ if (starting) {
7131
+ if (!confirmed) {
7132
+ const prompt = terminalPrompt();
7133
+ try {
7134
+ let runtime2 = settings2.runtime;
7135
+ process.stderr.write(`PMCP MAIN | ${settings2.projectRoot}
7136
+ `);
7137
+ if (!request3.runtime) {
7138
+ const installed = settings2.allowedRuntimes.filter(
7139
+ (id2) => locateExecutable(runtimeExecutables[id2], request3.cwd)
7140
+ );
7141
+ const selected = await chooseRuntime(prompt, installed, runtime2);
7142
+ if (!selected) return cancelledStart();
7143
+ runtime2 = selected;
7144
+ }
7145
+ if (!await confirmRuntimeStart(prompt, runtime2))
7146
+ return cancelledStart();
7147
+ request3 = { ...request3, runtime: runtime2 };
7148
+ settings2 = startupSettings(request3);
7149
+ } finally {
7150
+ prompt.close();
7151
+ }
7152
+ }
7153
+ if (!locateExecutable(runtimeExecutables[settings2.runtime], request3.cwd))
7154
+ throw new Error("runtime_executable_unavailable");
7155
+ process.stderr.write(
7156
+ `PMCP main: ${settings2.runtime} | ${settings2.projectRoot}
7157
+ `
7158
+ );
7159
+ }
7160
+ const prepared = serializedStartup(request3);
7161
+ if (prepared.ok !== true) return prepared;
7162
+ return invokeTui({
7163
+ action: "open",
7164
+ viewFile: String(prepared.viewFile),
7165
+ input: {}
7166
+ });
7167
+ }
7168
+ function cancelledStart() {
7169
+ return {
7170
+ schemaVersion: 1,
7171
+ ok: true,
7172
+ cancelled: true,
7173
+ providerInvoked: false
7174
+ };
7175
+ }
7176
+ var init_entry2 = __esm({
7177
+ "src/tui/startup/entry.ts"() {
7178
+ "use strict";
7179
+ init_executable();
7180
+ init_registry();
7181
+ init_invoke2();
7182
+ init_config3();
7183
+ init_state();
7184
+ init_host();
7185
+ init_lock2();
7186
+ init_runtime_choice();
7187
+ }
7188
+ });
7189
+
6514
7190
  // src/cli/command.ts
6515
7191
  var ArgumentError = class extends Error {
6516
7192
  constructor(message) {
@@ -6564,15 +7240,15 @@ function one(args, name) {
6564
7240
  function many(args, name) {
6565
7241
  return args.options.get(name) ?? [];
6566
7242
  }
6567
- function printCommandHelp(ui, command2) {
6568
- ui.heading(`pmcp ${command2.name}`);
6569
- ui.line(` ${command2.describe}`);
7243
+ function printCommandHelp(ui, command3) {
7244
+ ui.heading(`pmcp ${command3.name}`);
7245
+ ui.line(` ${command3.describe}`);
6570
7246
  ui.line();
6571
- ui.line(` ${ui.paint(command2.usage, "dim")}`);
6572
- if (command2.options && command2.options.length > 0) {
7247
+ ui.line(` ${ui.paint(command3.usage, "dim")}`);
7248
+ if (command3.options && command3.options.length > 0) {
6573
7249
  ui.line();
6574
7250
  ui.table(
6575
- command2.options.map((option) => [
7251
+ command3.options.map((option) => [
6576
7252
  `--${option.name}${option.placeholder ? ` ${option.placeholder}` : ""}`,
6577
7253
  option.describe
6578
7254
  ])
@@ -6583,9 +7259,14 @@ function printCommandHelp(ui, command2) {
6583
7259
  function printHelp(ui, commands, notImplemented) {
6584
7260
  ui.heading("pmcp \u2014 the skills your installed packages ship");
6585
7261
  ui.line();
6586
- ui.table(commands.map((command2) => [command2.name, command2.describe]));
7262
+ ui.table(commands.map((command3) => [command3.name, command3.describe]));
6587
7263
  ui.line();
6588
- ui.line(" With no command, pmcp runs the MCP server on stdio.");
7264
+ ui.line(
7265
+ " With no command, pmcp opens a terminal workspace on a supported TTY."
7266
+ );
7267
+ ui.line(
7268
+ " Piped launches retain MCP stdio. Use pmcp serve explicitly for MCP."
7269
+ );
6589
7270
  ui.line();
6590
7271
  ui.table([
6591
7272
  ["-q, --quiet", "only errors"],
@@ -6688,6 +7369,9 @@ var Ui = class {
6688
7369
  }
6689
7370
  };
6690
7371
 
7372
+ // src/commands/index.ts
7373
+ init_entry();
7374
+
6691
7375
  // src/intent.ts
6692
7376
  function normaliseIntent(intent) {
6693
7377
  return intent.toLowerCase().replaceAll(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/u).filter(Boolean).join(" ");
@@ -9163,7 +9847,7 @@ function registerMailboxTools(server, service) {
9163
9847
 
9164
9848
  // src/mcp.ts
9165
9849
  var SERVER_NAME = "pmcp";
9166
- var SERVER_VERSION = "0.14.1";
9850
+ var SERVER_VERSION = "0.16.0";
9167
9851
  function registerSkillTools(server, tools) {
9168
9852
  registerCatalogTool(server, tools);
9169
9853
  registerFindTool(server, tools);
@@ -9351,7 +10035,7 @@ async function prepareSearch(catalog, options2) {
9351
10035
  // src/commands/serve.ts
9352
10036
  var serveCommand = {
9353
10037
  name: "serve",
9354
- describe: "Run the MCP server on stdio (the default with no command)",
10038
+ describe: "Run the MCP server on stdio (also the default for piped launches)",
9355
10039
  usage: "pmcp serve [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir>] [--config <file> | --no-config] [--prompts]",
9356
10040
  options: [
9357
10041
  ...CATALOG_OPTIONS,
@@ -9812,6 +10496,10 @@ function installedPackageDirectory(project2, name) {
9812
10496
  for (; ; ) {
9813
10497
  const candidate2 = join11(directory, "node_modules", name);
9814
10498
  if (existsSync10(join11(candidate2, "package.json"))) return candidate2;
10499
+ if (existsSync10(join11(directory, ".git")))
10500
+ throw new Error(
10501
+ "Installed package manifest was not found in this repository"
10502
+ );
9815
10503
  const parent = dirname11(directory);
9816
10504
  if (parent === directory)
9817
10505
  throw new Error("Installed package manifest was not found");
@@ -9829,6 +10517,8 @@ function locate(name, project2) {
9829
10517
  );
9830
10518
  } catch (error) {
9831
10519
  if (!absent(error)) return { name, reason: "unreadable_manifest" };
10520
+ if (existsSync10(join11(directory, ".git")))
10521
+ return { name, reason: "not_installed" };
9832
10522
  const parent = dirname11(directory);
9833
10523
  if (parent === directory) return { name, reason: "not_installed" };
9834
10524
  directory = parent;
@@ -9885,30 +10575,77 @@ function readInstalledDependencies(projectDirectory) {
9885
10575
  }
9886
10576
 
9887
10577
  // 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
10578
  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
10579
+
10580
+ // src/install/manager.ts
10581
+ import { existsSync as existsSync11 } from "fs";
10582
+ import { join as join12, relative as relative6, sep as sep5 } from "path";
10583
+ function installationCommand(context, specs) {
10584
+ if (context.manager === "npm") {
10585
+ const args2 = [
10586
+ "install",
10587
+ "--save-dev",
10588
+ "--save-exact",
10589
+ "--ignore-scripts",
10590
+ "--no-audit",
10591
+ "--no-fund"
10592
+ ];
10593
+ if (context.project !== context.root)
10594
+ args2.push(
10595
+ `--workspace=${relative6(context.root, context.project).split(sep5).join("/")}`
9898
10596
  );
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
- });
10597
+ return { executable: "npm", args: [...args2, ...specs], cwd: context.root };
10598
+ }
10599
+ const args = context.manager === "pnpm" ? ["add", "--save-dev", "--save-exact", "--ignore-scripts"] : ["add", "--dev", "--exact", "--ignore-scripts"];
10600
+ if (context.manager === "pnpm" && context.project === context.root && existsSync11(join12(context.root, "pnpm-workspace.yaml")))
10601
+ args.push("--workspace-root");
10602
+ return {
10603
+ executable: context.manager,
10604
+ args: [...args, ...specs],
10605
+ cwd: context.project
10606
+ };
10607
+ }
10608
+ function integrityArguments(manager, spec) {
10609
+ const verb = manager === "bun" ? "info" : "view";
10610
+ return [verb, spec, "dist.integrity", "--json"];
10611
+ }
10612
+
10613
+ // src/install/context.ts
10614
+ import { createHash as createHash7 } from "crypto";
10615
+ import { existsSync as existsSync13, readFileSync as readFileSync15, realpathSync as realpathSync4 } from "fs";
10616
+ import { dirname as dirname12, join as join14 } from "path";
10617
+
10618
+ // src/install/workspace.ts
10619
+ import { existsSync as existsSync12, readFileSync as readFileSync14, realpathSync as realpathSync3 } from "fs";
10620
+ import { join as join13 } from "path";
10621
+ import { parse as parse3 } from "yaml";
10622
+ function workspaceContains(root, project2) {
10623
+ const file2 = join13(root, "pnpm-workspace.yaml");
10624
+ if (!existsSync12(file2)) return workspacePackageDirs(root).includes(project2);
10625
+ const value = parse3(readFileSync14(file2, "utf8"));
10626
+ if (!value || typeof value !== "object" || Array.isArray(value))
10627
+ throw new Error("invalid pnpm workspace configuration");
10628
+ const patterns = value.packages ?? [];
10629
+ if (!Array.isArray(patterns) || patterns.some((item) => typeof item !== "string"))
10630
+ throw new Error("pnpm workspace packages must be string patterns");
10631
+ const included = /* @__PURE__ */ new Set([root]);
10632
+ const excluded = /* @__PURE__ */ new Set();
10633
+ for (const raw of patterns) {
10634
+ const negated = raw.startsWith("!");
10635
+ const pattern = negated ? raw.slice(1) : raw;
10636
+ if (!pattern || pattern.split("/").includes("..") || pattern.startsWith("/"))
10637
+ throw new Error("workspace patterns must stay inside the workspace");
10638
+ for (const alternative of expandBraces(pattern)) {
10639
+ for (const match of globDirs(root, alternative)) {
10640
+ (negated ? excluded : included).add(realpathSync3(match));
9906
10641
  }
9907
10642
  }
9908
10643
  }
9909
- return matches2;
10644
+ return project2 === root || included.has(project2) && !excluded.has(project2);
9910
10645
  }
9911
- var LOCKS = [
10646
+
10647
+ // src/install/context.ts
10648
+ var locks = [
9912
10649
  "package-lock.json",
9913
10650
  "npm-shrinkwrap.json",
9914
10651
  "bun.lock",
@@ -9921,7 +10658,7 @@ function fingerprint(files) {
9921
10658
  for (const file2 of files) {
9922
10659
  hash2.update(file2).update("\0");
9923
10660
  try {
9924
- hash2.update(readFileSync14(file2));
10661
+ hash2.update(readFileSync15(file2));
9925
10662
  } catch (error) {
9926
10663
  if (error.code !== "ENOENT") throw error;
9927
10664
  hash2.update("absent");
@@ -9932,65 +10669,71 @@ function fingerprint(files) {
9932
10669
  }
9933
10670
  function manifestAt(directory) {
9934
10671
  const value = JSON.parse(
9935
- readFileSync14(join12(directory, "package.json"), "utf8")
10672
+ readFileSync15(join14(directory, "package.json"), "utf8")
9936
10673
  );
9937
10674
  if (!value || typeof value !== "object" || Array.isArray(value))
9938
10675
  throw new Error("invalid project manifest");
9939
10676
  return value;
9940
10677
  }
9941
- function installContext(directory) {
9942
- const project2 = resolve9(directory);
9943
- const manifest2 = manifestAt(project2);
9944
- let root = project2;
10678
+ function managerForLock(file2) {
10679
+ if (file2.startsWith("bun.")) return "bun";
10680
+ if (file2 === "pnpm-lock.yaml") return "pnpm";
10681
+ if (file2 === "package-lock.json" || file2 === "npm-shrinkwrap.json")
10682
+ return "npm";
10683
+ return "unsupported";
10684
+ }
10685
+ function workspaceRoot(project2) {
9945
10686
  let ancestor = project2;
9946
10687
  for (; ; ) {
9947
- if (ancestor !== project2 && existsSync11(join12(ancestor, "package.json"))) {
10688
+ if (ancestor !== project2 && existsSync13(join14(ancestor, "package.json"))) {
9948
10689
  const candidate2 = manifestAt(ancestor);
9949
- if (candidate2.workspaces !== void 0) {
9950
- root = ancestor;
9951
- break;
10690
+ if (candidate2.workspaces !== void 0 || existsSync13(join14(ancestor, "pnpm-workspace.yaml"))) {
10691
+ if (!workspaceContains(ancestor, project2))
10692
+ throw new Error("project is excluded from the ancestor workspace");
10693
+ return ancestor;
9952
10694
  }
9953
- if (LOCKS.some((file2) => existsSync11(join12(ancestor, file2)))) break;
10695
+ if (locks.some((file2) => existsSync13(join14(ancestor, file2))))
10696
+ return project2;
9954
10697
  }
9955
- if (existsSync11(join12(ancestor, ".git")) || dirname12(ancestor) === ancestor)
9956
- break;
10698
+ if (existsSync13(join14(ancestor, ".git")) || dirname12(ancestor) === ancestor)
10699
+ return project2;
9957
10700
  ancestor = dirname12(ancestor);
9958
10701
  }
10702
+ }
10703
+ function installContext(directory) {
10704
+ const project2 = realpathSync4(directory);
10705
+ const manifest2 = manifestAt(project2);
10706
+ const root = workspaceRoot(project2);
9959
10707
  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
- );
10708
+ const lockNames = locks.filter((file2) => existsSync13(join14(root, file2)));
10709
+ const lockManagers = new Set(lockNames.map(managerForLock));
9969
10710
  if (lockManagers.size > 1 || lockManagers.has("unsupported"))
9970
10711
  throw new Error("conflicting or unsupported package manager lockfiles");
9971
10712
  const declared = owner.packageManager?.match(
9972
- /^(npm|bun)@\d+\.\d+\.\d+(?:[-+].+)?$/u
10713
+ /^(npm|bun|pnpm)@\d+\.\d+\.\d+(?:[-+].+)?$/u
9973
10714
  )?.[1];
9974
10715
  if (owner.packageManager !== void 0 && !declared)
9975
- throw new Error("only npm and Bun projects are supported");
10716
+ throw new Error("only npm, Bun and pnpm projects are supported");
9976
10717
  const locked = [...lockManagers][0];
9977
10718
  if (declared && locked && declared !== locked)
9978
10719
  throw new Error("packageManager and lockfile disagree");
9979
10720
  const manager = declared ?? locked;
9980
- if (manager !== "npm" && manager !== "bun")
10721
+ if (manager !== "npm" && manager !== "bun" && manager !== "pnpm")
9981
10722
  throw new Error(
9982
10723
  "declare packageManager or install project dependencies first"
9983
10724
  );
9984
- if (root !== project2 && LOCKS.some((file2) => existsSync11(join12(project2, file2)))) {
10725
+ if (root !== project2 && locks.some((file2) => existsSync13(join14(project2, file2))))
9985
10726
  throw new Error(
9986
10727
  "nested project has its own lockfile; resolve the workspace boundary first"
9987
10728
  );
9988
- }
9989
10729
  const files = [
9990
10730
  .../* @__PURE__ */ new Set([
9991
- join12(root, "package.json"),
9992
- join12(project2, "package.json"),
9993
- ...LOCKS.map((file2) => join12(root, file2))
10731
+ join14(root, "package.json"),
10732
+ join14(project2, "package.json"),
10733
+ join14(root, "pnpm-workspace.yaml"),
10734
+ join14(root, ".npmrc"),
10735
+ join14(project2, ".npmrc"),
10736
+ ...locks.map((file2) => join14(root, file2))
9994
10737
  ])
9995
10738
  ];
9996
10739
  return {
@@ -10002,6 +10745,27 @@ function installContext(directory) {
10002
10745
  fingerprint: fingerprint(files)
10003
10746
  };
10004
10747
  }
10748
+
10749
+ // src/install/plan.ts
10750
+ function matchingSkills(catalog, inventory) {
10751
+ const matches2 = [];
10752
+ for (const dependency of inventory.dependencies) {
10753
+ for (const entry2 of catalog.entries) {
10754
+ const target2 = entry2.targets.find(
10755
+ (target3) => target3.packageName === dependency.name
10756
+ );
10757
+ if (target2 && semver.satisfies(dependency.version, target2.range)) {
10758
+ matches2.push({
10759
+ targetName: dependency.name,
10760
+ targetVersion: dependency.version,
10761
+ verified: target2.verifiedVersions.includes(dependency.version),
10762
+ entry: entry2
10763
+ });
10764
+ }
10765
+ }
10766
+ }
10767
+ return matches2;
10768
+ }
10005
10769
  function planInstall(options2) {
10006
10770
  if (options2.inventory.issues.length)
10007
10771
  throw new Error(
@@ -10066,49 +10830,27 @@ function planInstall(options2) {
10066
10830
  const specs = deliveries.map(
10067
10831
  (entry2) => `${entry2.packageName}@${entry2.version}`
10068
10832
  );
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
10833
  return {
10088
10834
  catalogRevision: catalog.revision,
10089
10835
  context,
10090
10836
  changes: deliveries,
10091
10837
  recalled,
10092
- command: {
10093
- executable: context.manager,
10094
- args,
10095
- cwd: context.manager === "npm" ? context.root : context.project
10096
- }
10838
+ command: installationCommand(context, specs)
10097
10839
  };
10098
10840
  }
10099
10841
 
10100
10842
  // src/commands/remote-options.ts
10101
- import { join as join14, resolve as resolve10 } from "path";
10102
- import { readFileSync as readFileSync17 } from "fs";
10843
+ import { dirname as dirname13, join as join16, resolve as resolve9 } from "path";
10844
+ import { existsSync as existsSync14, readFileSync as readFileSync18 } from "fs";
10103
10845
 
10104
10846
  // src/remote/catalog.ts
10105
- import { readFileSync as readFileSync16, statSync as statSync8 } from "fs";
10847
+ import { readFileSync as readFileSync17, statSync as statSync8 } from "fs";
10106
10848
  import semver2 from "semver";
10107
10849
  import { z as z13 } from "zod";
10108
10850
 
10109
10851
  // 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";
10852
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync16, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
10853
+ import { join as join15 } from "path";
10112
10854
  var MAX_CACHE_BYTES = 8 * 1024 * 1024;
10113
10855
  function memoryCatalogCache() {
10114
10856
  const held = /* @__PURE__ */ new Map();
@@ -10120,11 +10862,11 @@ function memoryCatalogCache() {
10120
10862
  };
10121
10863
  }
10122
10864
  function fileCatalogCache(dir) {
10123
- const path = join13(dir, "catalog-cache.json");
10865
+ const path = join15(dir, "catalog-cache.json");
10124
10866
  return {
10125
10867
  read(origin) {
10126
10868
  try {
10127
- const raw = readFileSync15(path, "utf8");
10869
+ const raw = readFileSync16(path, "utf8");
10128
10870
  if (raw.length > MAX_CACHE_BYTES) return null;
10129
10871
  const held = JSON.parse(raw);
10130
10872
  if (typeof held !== "object" || held === null) return null;
@@ -10247,7 +10989,7 @@ function readCatalogFile(path) {
10247
10989
  const stat = statSync8(path);
10248
10990
  if (!stat.isFile() || stat.size > MAX_CATALOG_BYTES)
10249
10991
  throw new Error("catalog file exceeds the supported size");
10250
- const bytes = readFileSync16(path);
10992
+ const bytes = readFileSync17(path);
10251
10993
  if (bytes.byteLength > MAX_CATALOG_BYTES)
10252
10994
  throw new Error("catalog file exceeds the supported size");
10253
10995
  return parseCatalog(JSON.parse(bytes.toString("utf8")));
@@ -10349,7 +11091,17 @@ function catalogProvider(context) {
10349
11091
  return given ?? (api2 ? "hosted" : "public");
10350
11092
  }
10351
11093
  function projectFrom(context) {
10352
- return resolve10(context.cwd, one(context.args, "project") ?? ".");
11094
+ const explicit = one(context.args, "project");
11095
+ if (explicit) return resolve9(context.cwd, explicit);
11096
+ let directory = resolve9(context.cwd);
11097
+ for (; ; ) {
11098
+ if (existsSync14(join16(directory, "package.json"))) return directory;
11099
+ if (existsSync14(join16(directory, ".git")) || dirname13(directory) === directory)
11100
+ throw new ArgumentError(
11101
+ "No project package.json found; provide --project"
11102
+ );
11103
+ directory = dirname13(directory);
11104
+ }
10353
11105
  }
10354
11106
  function apiOrigin(context) {
10355
11107
  return one(context.args, "api") ?? context.env["PMCP_API_ORIGIN"] ?? DEFAULT_API_ORIGIN;
@@ -10360,11 +11112,11 @@ async function remoteCatalog(context) {
10360
11112
  if (file2) {
10361
11113
  if (one(context.args, "api"))
10362
11114
  throw new Error("choose --catalog or --api, not both");
10363
- return readCatalogFile(resolve10(context.cwd, file2));
11115
+ return readCatalogFile(resolve9(context.cwd, file2));
10364
11116
  }
10365
11117
  if (provider === "public") {
10366
11118
  const catalog = JSON.parse(
10367
- readFileSync17(join14(publicMarketplaceRoot(), "docs/catalog.json"), "utf8")
11119
+ readFileSync18(join16(publicMarketplaceRoot(), "docs/catalog.json"), "utf8")
10368
11120
  );
10369
11121
  return parseCatalog({
10370
11122
  schemaVersion: 1,
@@ -10517,7 +11269,7 @@ function createPackageManagerRunner(userConfig, baseEnv = process.env, verifiedI
10517
11269
  const alreadyVerified = verifiedIntegrity?.get(packageSpec);
10518
11270
  if (alreadyVerified) return alreadyVerified;
10519
11271
  const { manager, project: project2 } = plan2.context;
10520
- const args = manager === "npm" ? ["view", packageSpec, "dist.integrity", "--json"] : ["info", packageSpec, "dist.integrity", "--json"];
11272
+ const args = integrityArguments(manager, packageSpec);
10521
11273
  const result = spawnSync(manager, args, {
10522
11274
  cwd: project2,
10523
11275
  env: environment(userConfig, baseEnv),
@@ -10592,8 +11344,8 @@ function applyInstallPlan(plan2, runner = packageManagerRunner) {
10592
11344
 
10593
11345
  // src/install/verify-content.ts
10594
11346
  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";
11347
+ import { readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
11348
+ import { join as join17, relative as relative7, resolve as resolve10, sep as sep6 } from "path";
10597
11349
  var SKILL = "SKILL.md";
10598
11350
  var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".cache"]);
10599
11351
  function skillFiles2(root) {
@@ -10607,7 +11359,7 @@ function skillFiles2(root) {
10607
11359
  }
10608
11360
  for (const name of entries) {
10609
11361
  if (SKIP.has(name)) continue;
10610
- const path = join15(dir, name);
11362
+ const path = join17(dir, name);
10611
11363
  let stats;
10612
11364
  try {
10613
11365
  stats = statSync9(path);
@@ -10618,7 +11370,7 @@ function skillFiles2(root) {
10618
11370
  else if (name === SKILL)
10619
11371
  found.set(
10620
11372
  relative7(root, path).split(sep6).join("/"),
10621
- readFileSync18(path, "utf8")
11373
+ readFileSync19(path, "utf8")
10622
11374
  );
10623
11375
  }
10624
11376
  };
@@ -10634,11 +11386,11 @@ function contentDigest(files) {
10634
11386
  return `sha256:${hash2.digest("hex")}`;
10635
11387
  }
10636
11388
  function verifyInstalledContent(project2, packageName2, expected) {
10637
- let root = resolve11(project2, "node_modules", ...packageName2.split("/"));
11389
+ let root = resolve10(project2, "node_modules", ...packageName2.split("/"));
10638
11390
  try {
10639
11391
  root = installedPackageDirectory(project2, packageName2);
10640
11392
  } catch {
10641
- root = resolve11(project2, "node_modules", ...packageName2.split("/"));
11393
+ root = resolve10(project2, "node_modules", ...packageName2.split("/"));
10642
11394
  }
10643
11395
  const files = skillFiles2(root);
10644
11396
  const actual = contentDigest(files);
@@ -10652,8 +11404,8 @@ function verifyInstalledContent(project2, packageName2, expected) {
10652
11404
  }
10653
11405
 
10654
11406
  // src/install/public.ts
10655
- import { lstatSync, readFileSync as readFileSync19, readdirSync as readdirSync6 } from "fs";
10656
- import { join as join16 } from "path";
11407
+ import { lstatSync, readFileSync as readFileSync20, readdirSync as readdirSync6 } from "fs";
11408
+ import { join as join18 } from "path";
10657
11409
 
10658
11410
  // src/install/archive.ts
10659
11411
  import { createHash as createHash9 } from "crypto";
@@ -10820,7 +11572,7 @@ function verifyPublicDelivery(project2, delivery) {
10820
11572
  for (const entry2 of readdirSync6(directory, { withFileTypes: true })) {
10821
11573
  const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
10822
11574
  if (entry2.isDirectory()) {
10823
- walk(join16(directory, entry2.name), path);
11575
+ walk(join18(directory, entry2.name), path);
10824
11576
  continue;
10825
11577
  }
10826
11578
  if (!entry2.isFile() || !expected.has(path))
@@ -10831,8 +11583,8 @@ function verifyPublicDelivery(project2, delivery) {
10831
11583
  };
10832
11584
  walk(root);
10833
11585
  for (const [path, bytes] of files) {
10834
- const destination = join16(root, path.slice("package/".length));
10835
- if (!lstatSync(destination).isFile() || !readFileSync19(destination).equals(bytes))
11586
+ const destination = join18(root, path.slice("package/".length));
11587
+ if (!lstatSync(destination).isFile() || !readFileSync20(destination).equals(bytes))
10836
11588
  throw new Error(
10837
11589
  "Installed public package differs from its verified archive; inspect the project before retrying"
10838
11590
  );
@@ -10843,7 +11595,7 @@ function verifyPublicDelivery(project2, delivery) {
10843
11595
  // src/remote/distribution.ts
10844
11596
  import { chmodSync as chmodSync4, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
10845
11597
  import { tmpdir } from "os";
10846
- import { join as join17 } from "path";
11598
+ import { join as join19 } from "path";
10847
11599
  function api(origin, path) {
10848
11600
  const base = new URL(catalogEndpoint(origin));
10849
11601
  base.pathname = path;
@@ -10983,9 +11735,9 @@ async function revokeDistributionCredential(accessToken, id2, origin = DEFAULT_A
10983
11735
  }
10984
11736
  }
10985
11737
  function temporaryRegistryConfig(value) {
10986
- const directory = mkdtempSync(join17(tmpdir(), "pmcp-registry-"));
11738
+ const directory = mkdtempSync(join19(tmpdir(), "pmcp-registry-"));
10987
11739
  chmodSync4(directory, 448);
10988
- const path = join17(directory, ".npmrc");
11740
+ const path = join19(directory, ".npmrc");
10989
11741
  const registry = new URL(value.registry);
10990
11742
  const auth = `//${registry.host}${registry.pathname}:_authToken=${value.token}`;
10991
11743
  writeFileSync3(path, `${value.scope}:registry=${registry.href}
@@ -11238,36 +11990,34 @@ import { homedir as homedir6 } from "os";
11238
11990
  // src/project/apply.ts
11239
11991
  import { createHash as createHash13 } from "crypto";
11240
11992
  import {
11241
- existsSync as existsSync17,
11242
- lstatSync as lstatSync4,
11993
+ existsSync as existsSync20,
11994
+ lstatSync as lstatSync5,
11243
11995
  mkdirSync as mkdirSync7,
11244
- readFileSync as readFileSync24,
11996
+ readFileSync as readFileSync25,
11245
11997
  readdirSync as readdirSync9,
11246
11998
  readlinkSync as readlinkSync2,
11247
- rmSync as rmSync4,
11248
- symlinkSync,
11249
- writeFileSync as writeFileSync5
11999
+ rmSync as rmSync5
11250
12000
  } from "fs";
11251
- import { dirname as dirname16, join as join22, posix as posix2 } from "path";
11252
- import { parse as parse4 } from "smol-toml";
12001
+ import { dirname as dirname18, join as join24, posix as posix2 } from "path";
12002
+ import { parse as parse5 } from "smol-toml";
11253
12003
 
11254
12004
  // src/project/backup.ts
11255
12005
  import { createHash as createHash10 } from "crypto";
11256
12006
  import {
11257
- existsSync as existsSync13,
12007
+ existsSync as existsSync16,
11258
12008
  lstatSync as lstatSync3,
11259
12009
  mkdirSync as mkdirSync6,
11260
12010
  mkdtempSync as mkdtempSync2,
11261
- readFileSync as readFileSync20,
12011
+ readFileSync as readFileSync21,
11262
12012
  writeFileSync as writeFileSync4
11263
12013
  } from "fs";
11264
12014
  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";
12015
+ import { dirname as dirname15, isAbsolute as isAbsolute4, join as join21, relative as relative8, resolve as resolve11 } from "path";
11266
12016
 
11267
12017
  // src/project/package-rules.ts
11268
12018
  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";
12019
+ import { existsSync as existsSync15, lstatSync as lstatSync2, readdirSync as readdirSync7, readlinkSync } from "fs";
12020
+ import { dirname as dirname14, join as join20, posix } from "path";
11271
12021
  var ProjectError = class extends Error {
11272
12022
  constructor(message) {
11273
12023
  super(message);
@@ -11312,11 +12062,11 @@ function linkTarget(abs) {
11312
12062
  }
11313
12063
  function legacyLinks(root) {
11314
12064
  const found = /* @__PURE__ */ new Map();
11315
- const dir = join18(root, RULES_DIR);
11316
- if (!existsSync12(dir)) return found;
12065
+ const dir = join20(root, RULES_DIR);
12066
+ if (!existsSync15(dir)) return found;
11317
12067
  for (const entry2 of readdirSync7(dir).sort()) {
11318
12068
  if (!entry2.endsWith(".md")) continue;
11319
- const target2 = linkTarget(join18(dir, entry2));
12069
+ const target2 = linkTarget(join20(dir, entry2));
11320
12070
  if (target2 === null || !target2.startsWith("../../")) continue;
11321
12071
  found.set(entry2.slice(0, -3), target2.slice("../../".length));
11322
12072
  }
@@ -11336,14 +12086,14 @@ function packageRuleOutputs(root, spec, owned) {
11336
12086
  const legacyByDir = /* @__PURE__ */ new Map();
11337
12087
  for (const [name, target2] of legacy) {
11338
12088
  if (!byTarget.has(target2)) byTarget.set(target2, name);
11339
- const dir = dirname13(target2);
12089
+ const dir = dirname14(target2);
11340
12090
  if (!legacyByDir.has(dir)) legacyByDir.set(dir, name);
11341
12091
  }
11342
12092
  const outputs = [];
11343
12093
  const taken = /* @__PURE__ */ new Map();
11344
12094
  for (const file2 of files) {
11345
12095
  const pkg = file2.slice(0, -suffix.length);
11346
- const name = byTarget.get(file2) ?? legacyByDir.get(dirname13(file2)) ?? deriveName(pkg, spec.strip);
12096
+ const name = byTarget.get(file2) ?? legacyByDir.get(dirname14(file2)) ?? deriveName(pkg, spec.strip);
11347
12097
  const other = taken.get(name);
11348
12098
  if (other !== void 0) {
11349
12099
  throw new ProjectError(
@@ -11362,8 +12112,8 @@ function packageRuleOutputs(root, spec, owned) {
11362
12112
 
11363
12113
  // src/project/backup.ts
11364
12114
  function backupFiles(root, paths) {
11365
- const base = join19(homedir3(), ".cache/pmcp/project-backups");
11366
- const inside = relative8(resolve12(root), resolve12(base));
12115
+ const base = join21(homedir3(), ".cache/pmcp/project-backups");
12116
+ const inside = relative8(resolve11(root), resolve11(base));
11367
12117
  if (!isAbsolute4(inside) && inside !== ".." && !inside.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`)) {
11368
12118
  throw new ProjectError("Project backups must be outside the project");
11369
12119
  }
@@ -11378,12 +12128,12 @@ function backupFiles(root, paths) {
11378
12128
  );
11379
12129
  }
11380
12130
  const snapshots = [...new Set(paths)].map((path) => {
11381
- const absolute2 = resolve12(root, path);
11382
- const within = relative8(resolve12(root), absolute2);
12131
+ const absolute2 = resolve11(root, path);
12132
+ const within = relative8(resolve11(root), absolute2);
11383
12133
  if (isAbsolute4(within) || within === ".." || within.startsWith("../") || within.startsWith("..\\")) {
11384
12134
  throw new ProjectError("Backup paths must stay inside the project");
11385
12135
  }
11386
- if (!existsSync13(absolute2)) {
12136
+ if (!existsSync16(absolute2)) {
11387
12137
  return { path, contents: null };
11388
12138
  }
11389
12139
  const stat = lstatSync3(absolute2);
@@ -11392,15 +12142,15 @@ function backupFiles(root, paths) {
11392
12142
  `Refusing to back up a nonregular config file: ${path}`
11393
12143
  );
11394
12144
  }
11395
- return { path, contents: readFileSync20(absolute2) };
12145
+ return { path, contents: readFileSync21(absolute2) };
11396
12146
  });
11397
- const destination = mkdtempSync2(join19(base, "project-"));
12147
+ const destination = mkdtempSync2(join21(base, "project-"));
11398
12148
  const entries = snapshots.map(({ path, contents }) => {
11399
12149
  if (contents === null) {
11400
12150
  return { path, present: false };
11401
12151
  }
11402
- const output = join19(destination, "files", path);
11403
- mkdirSync6(dirname14(output), { recursive: true, mode: 448 });
12152
+ const output = join21(destination, "files", path);
12153
+ mkdirSync6(dirname15(output), { recursive: true, mode: 448 });
11404
12154
  writeFileSync4(output, contents, { mode: 384 });
11405
12155
  return {
11406
12156
  path,
@@ -11409,11 +12159,11 @@ function backupFiles(root, paths) {
11409
12159
  };
11410
12160
  });
11411
12161
  writeFileSync4(
11412
- join19(destination, "receipt.json"),
12162
+ join21(destination, "receipt.json"),
11413
12163
  JSON.stringify(
11414
12164
  {
11415
12165
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
11416
- projectRoot: resolve12(root),
12166
+ projectRoot: resolve11(root),
11417
12167
  entries
11418
12168
  },
11419
12169
  null,
@@ -11704,34 +12454,34 @@ function mcpOutputs(spec) {
11704
12454
 
11705
12455
  // src/project/hooks.ts
11706
12456
  import { createHash as createHash12 } from "crypto";
11707
- import { existsSync as existsSync15, readFileSync as readFileSync22 } from "fs";
11708
- import { join as join20 } from "path";
12457
+ import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
12458
+ import { join as join22 } from "path";
11709
12459
  import { fileURLToPath as fileURLToPath2 } from "url";
11710
12460
 
11711
12461
  // src/project/hook-adapter.ts
11712
12462
  import { spawnSync as spawnSync2 } from "child_process";
11713
12463
  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";
12464
+ import { existsSync as existsSync17, readFileSync as readFileSync22, realpathSync as realpathSync5, statSync as statSync10 } from "fs";
12465
+ import { dirname as dirname16, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve12 } from "path";
11716
12466
  var denied = () => ({
11717
12467
  decision: "deny",
11718
12468
  reason: "Shell guard failed validation or execution"
11719
12469
  });
11720
- function guardPath(root, command2) {
11721
- const script = command2[0]?.startsWith(".agents/guards/") ? command2[0] : command2[1];
12470
+ function guardPath(root, command3) {
12471
+ const script = command3[0]?.startsWith(".agents/guards/") ? command3[0] : command3[1];
11722
12472
  if (!script?.startsWith(".agents/guards/")) {
11723
12473
  throw new Error("Hook command must execute a script under .agents/guards/");
11724
12474
  }
11725
- const absolute2 = resolve13(root, script);
11726
- const base = resolve13(root, ".agents/guards");
12475
+ const absolute2 = resolve12(root, script);
12476
+ const base = resolve12(root, ".agents/guards");
11727
12477
  const inside = relative10(base, absolute2);
11728
12478
  if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
11729
12479
  throw new Error("Hook guard must stay under .agents/guards/");
11730
12480
  }
11731
- if (!existsSync14(absolute2) || !statSync10(absolute2).isFile()) {
12481
+ if (!existsSync17(absolute2) || !statSync10(absolute2).isFile()) {
11732
12482
  throw new Error("Hook guard script must exist");
11733
12483
  }
11734
- const actual = relative10(base, realpathSync3(absolute2));
12484
+ const actual = relative10(base, realpathSync5(absolute2));
11735
12485
  if (isAbsolute6(actual) || actual === ".." || actual.startsWith("../") || actual.startsWith("..\\")) {
11736
12486
  throw new Error("Hook guard cannot resolve outside .agents/guards/");
11737
12487
  }
@@ -11776,10 +12526,10 @@ function evaluateShellHook(root, policy, tool, payload) {
11776
12526
  if (!shellNames[tool].includes(name)) {
11777
12527
  return { decision: "allow" };
11778
12528
  }
11779
- const command2 = scalar(
12529
+ const command3 = scalar(
11780
12530
  input[tool === "antigravity" ? "CommandLine" : "command"]
11781
12531
  );
11782
- if (command2 === void 0 || command2.length > 65536) {
12532
+ if (command3 === void 0 || command3.length > 65536) {
11783
12533
  return denied();
11784
12534
  }
11785
12535
  const cwd = scalar(input["Cwd"]) ?? scalar(value["cwd"]) ?? root;
@@ -11800,7 +12550,7 @@ function evaluateShellHook(root, policy, tool, payload) {
11800
12550
  input: JSON.stringify({
11801
12551
  runtime: tool,
11802
12552
  event: "before_shell",
11803
- command: command2,
12553
+ command: command3,
11804
12554
  cwd
11805
12555
  }),
11806
12556
  encoding: "utf8",
@@ -11849,8 +12599,8 @@ async function main() {
11849
12599
  if (!file2) {
11850
12600
  throw new Error("Missing hook policy");
11851
12601
  }
11852
- const bytes = readFileSync21(file2);
11853
- const adapter = readFileSync21(process.argv[1]);
12602
+ const bytes = readFileSync22(file2);
12603
+ const adapter = readFileSync22(process.argv[1]);
11854
12604
  const actual = createHash11("sha256").update(adapter).update(bytes).digest("hex");
11855
12605
  if (actual !== integrity2) {
11856
12606
  throw new Error("Hook integrity mismatch");
@@ -11869,8 +12619,8 @@ async function main() {
11869
12619
  }
11870
12620
  chunks.push(bytes2);
11871
12621
  }
11872
- const root = resolve13(dirname15(file2), "../..");
11873
- const guardSha256 = createHash11("sha256").update(readFileSync21(guardPath(root, policy.command))).digest("hex");
12622
+ const root = resolve12(dirname16(file2), "../..");
12623
+ const guardSha256 = createHash11("sha256").update(readFileSync22(guardPath(root, policy.command))).digest("hex");
11874
12624
  if (policy.contractVersion !== 1 || guardSha256 !== policy.guardSha256) {
11875
12625
  throw new Error(
11876
12626
  "Hook guard source changed; reproject and review native trust"
@@ -11923,13 +12673,13 @@ function object4(value) {
11923
12673
  return value;
11924
12674
  }
11925
12675
  function groups(root, file2, key) {
11926
- const absolute2 = join20(root, file2);
11927
- if (!existsSync15(absolute2)) {
12676
+ const absolute2 = join22(root, file2);
12677
+ if (!existsSync18(absolute2)) {
11928
12678
  return [];
11929
12679
  }
11930
12680
  let value;
11931
12681
  try {
11932
- value = JSON.parse(readFileSync22(absolute2, "utf8"));
12682
+ value = JSON.parse(readFileSync23(absolute2, "utf8"));
11933
12683
  } catch {
11934
12684
  throw new ProjectError(
11935
12685
  "Cannot parse native hook configuration; contents are redacted"
@@ -11953,15 +12703,15 @@ function adapterSource() {
11953
12703
  const sourceBuild = fileURLToPath2(
11954
12704
  new URL("../../dist/project/hook-adapter.js", import.meta.url)
11955
12705
  );
11956
- const file2 = existsSync15(published) ? published : sourceBuild;
11957
- if (!existsSync15(file2)) {
12706
+ const file2 = existsSync18(published) ? published : sourceBuild;
12707
+ if (!existsSync18(file2)) {
11958
12708
  throw new ProjectError("Build pmcp before projecting shell hook adapters");
11959
12709
  }
11960
- return readFileSync22(file2, "utf8");
12710
+ return readFileSync23(file2, "utf8");
11961
12711
  }
11962
12712
  function hookOutputs(spec) {
11963
- const lockFile = join20(spec.root, "pmcp.lock");
11964
- const lock = existsSync15(lockFile) ? JSON.parse(readFileSync22(lockFile, "utf8")) : {};
12713
+ const lockFile = join22(spec.root, "pmcp.lock");
12714
+ const lock = existsSync18(lockFile) ? JSON.parse(readFileSync23(lockFile, "utf8")) : {};
11965
12715
  const previous = new Map(
11966
12716
  (lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
11967
12717
  );
@@ -11978,7 +12728,7 @@ function hookOutputs(spec) {
11978
12728
  outputs.push({ kind: "file", path: ADAPTER, content: adapter });
11979
12729
  for (const hook of selected) {
11980
12730
  const policyFile = `${HOOK_DIRECTORY}/${hook.name}.json`;
11981
- const guardSha256 = createHash12("sha256").update(readFileSync22(guardPath(spec.root, hook.command))).digest("hex");
12731
+ const guardSha256 = createHash12("sha256").update(readFileSync23(guardPath(spec.root, hook.command))).digest("hex");
11982
12732
  const policy = `${JSON.stringify({ ...hook, contractVersion: 1, guardSha256 }, null, 2)}
11983
12733
  `;
11984
12734
  outputs.push({ kind: "file", path: policyFile, content: policy });
@@ -11988,11 +12738,11 @@ function hookOutputs(spec) {
11988
12738
  continue;
11989
12739
  }
11990
12740
  const target2 = native[tool];
11991
- const command2 = `d="$PWD"; while [ ! -f "$d/pmcp.toml" ]; do [ "$d" = / ] && exit 1; d=$(dirname "$d"); done; exec node "$d/${ADAPTER}" --pmcp-shell-hook ${tool} "$d/${policyFile}" ${integrity2}`;
12741
+ const command3 = `d="$PWD"; while [ ! -f "$d/pmcp.toml" ]; do [ "$d" = / ] && exit 1; d=$(dirname "$d"); done; exec node "$d/${ADAPTER}" --pmcp-shell-hook ${tool} "$d/${policyFile}" ${integrity2}`;
11992
12742
  const timeout = tool === "gemini" ? hook.timeoutMs + 2e3 : Math.ceil(hook.timeoutMs / 1e3) + 2;
11993
12743
  const value = {
11994
12744
  matcher: target2.matcher,
11995
- hooks: [{ type: "command", command: command2, timeout }]
12745
+ hooks: [{ type: "command", command: command3, timeout }]
11996
12746
  };
11997
12747
  const path = `${target2.file}#${target2.key.join(".")}`;
11998
12748
  const entry2 = desired.get(path) ?? {
@@ -12042,11 +12792,11 @@ function hookOutputs(spec) {
12042
12792
  }
12043
12793
 
12044
12794
  // src/project/frontmatter.ts
12045
- import { parse as parse3 } from "yaml";
12795
+ import { parse as parse4 } from "yaml";
12046
12796
  function splitFrontmatter(source2) {
12047
12797
  const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/u.exec(source2);
12048
12798
  if (!match) return { meta: {}, body: source2 };
12049
- const meta = parse3(match[1] ?? "");
12799
+ const meta = parse4(match[1] ?? "");
12050
12800
  const object9 = meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta : {};
12051
12801
  return { meta: object9, body: match[2] ?? "" };
12052
12802
  }
@@ -12076,28 +12826,28 @@ ${lines2.join("\n")}
12076
12826
  }
12077
12827
 
12078
12828
  // src/project/plan.ts
12079
- import { existsSync as existsSync16, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "fs";
12080
- import { join as join21 } from "path";
12829
+ import { existsSync as existsSync19, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
12830
+ import { join as join23 } from "path";
12081
12831
  var AGENT_FIELDS = {
12082
12832
  claude: ["name", "description", "tools", "model", "effort"],
12083
12833
  gemini: ["name", "description"]
12084
12834
  };
12085
12835
  var SETTINGS = ".claude/settings.json";
12086
12836
  function listDirs(dir) {
12087
- if (!existsSync16(dir)) return [];
12837
+ if (!existsSync19(dir)) return [];
12088
12838
  return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
12089
12839
  }
12090
12840
  var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
12091
12841
  function skillNames(root) {
12092
- const dir = join21(root, ".agents/skills");
12842
+ const dir = join23(root, ".agents/skills");
12093
12843
  return listDirs(dir).filter(
12094
- (name) => existsSync16(join21(dir, name, "SKILL.md"))
12844
+ (name) => existsSync19(join23(dir, name, "SKILL.md"))
12095
12845
  );
12096
12846
  }
12097
12847
  function agentNames(root) {
12098
- const dir = join21(root, ".agents/agents");
12848
+ const dir = join23(root, ".agents/agents");
12099
12849
  return listDirs(dir).filter(
12100
- (name) => existsSync16(join21(dir, name, "agent.md"))
12850
+ (name) => existsSync19(join23(dir, name, "agent.md"))
12101
12851
  );
12102
12852
  }
12103
12853
  function skillOutputs(spec) {
@@ -12131,13 +12881,13 @@ policy:
12131
12881
  }
12132
12882
  function ruleOutputs(spec) {
12133
12883
  if (!spec.tools.has("claude")) return [];
12134
- const dir = join21(spec.root, ".agents/rules");
12135
- if (!existsSync16(dir)) return [];
12884
+ const dir = join23(spec.root, ".agents/rules");
12885
+ if (!existsSync19(dir)) return [];
12136
12886
  const outputs = [];
12137
12887
  for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
12138
12888
  const rel = `.agents/rules/${file2}`;
12139
12889
  const { meta, body } = splitFrontmatter(
12140
- readFileSync23(join21(spec.root, rel), "utf8")
12890
+ readFileSync24(join23(spec.root, rel), "utf8")
12141
12891
  );
12142
12892
  const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
12143
12893
  const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
@@ -12156,7 +12906,7 @@ function agentOutputs(spec) {
12156
12906
  for (const name of agentNames(spec.root)) {
12157
12907
  const rel = `.agents/agents/${name}/agent.md`;
12158
12908
  const { meta, body } = splitFrontmatter(
12159
- readFileSync23(join21(spec.root, rel), "utf8")
12909
+ readFileSync24(join23(spec.root, rel), "utf8")
12160
12910
  );
12161
12911
  const overrides = spec.agents[name] ?? {};
12162
12912
  const note = `<!-- ${MARK} from ${rel} -->
@@ -12225,6 +12975,67 @@ function censusDirs(spec) {
12225
12975
  return dirs;
12226
12976
  }
12227
12977
 
12978
+ // src/project/write.ts
12979
+ import { randomUUID as randomUUID4 } from "crypto";
12980
+ import {
12981
+ closeSync as closeSync3,
12982
+ fchmodSync as fchmodSync2,
12983
+ fsyncSync,
12984
+ lstatSync as lstatSync4,
12985
+ openSync as openSync3,
12986
+ renameSync as renameSync3,
12987
+ rmSync as rmSync4,
12988
+ symlinkSync,
12989
+ writeFileSync as writeFileSync5
12990
+ } from "fs";
12991
+ import { dirname as dirname17 } from "path";
12992
+ function fileMode(path) {
12993
+ try {
12994
+ const stat = lstatSync4(path);
12995
+ if (stat.isFile()) return stat.mode & 511;
12996
+ } catch (error) {
12997
+ if (error.code !== "ENOENT") throw error;
12998
+ }
12999
+ return 438 & ~process.umask();
13000
+ }
13001
+ function syncDirectory(path) {
13002
+ if (process.platform === "win32") return;
13003
+ const descriptor = openSync3(dirname17(path), "r");
13004
+ try {
13005
+ fsyncSync(descriptor);
13006
+ } finally {
13007
+ closeSync3(descriptor);
13008
+ }
13009
+ }
13010
+ function replaceFile(path, content) {
13011
+ const mode = fileMode(path);
13012
+ const temporary = `${path}.${randomUUID4()}.new`;
13013
+ const descriptor = openSync3(temporary, "wx", 384);
13014
+ try {
13015
+ try {
13016
+ writeFileSync5(descriptor, content);
13017
+ fchmodSync2(descriptor, mode);
13018
+ fsyncSync(descriptor);
13019
+ } finally {
13020
+ closeSync3(descriptor);
13021
+ }
13022
+ renameSync3(temporary, path);
13023
+ syncDirectory(path);
13024
+ } finally {
13025
+ rmSync4(temporary, { force: true });
13026
+ }
13027
+ }
13028
+ function replaceLink(path, target2) {
13029
+ const temporary = `${path}.${randomUUID4()}.new`;
13030
+ try {
13031
+ symlinkSync(target2, temporary);
13032
+ renameSync3(temporary, path);
13033
+ syncDirectory(path);
13034
+ } finally {
13035
+ rmSync4(temporary, { force: true });
13036
+ }
13037
+ }
13038
+
12228
13039
  // src/project/apply.ts
12229
13040
  var LOCK_FILE = "pmcp.lock";
12230
13041
  var BEGIN = "# >>> pmcp project (edit pmcp.toml, not this block) >>>";
@@ -12235,24 +13046,24 @@ var PREDECESSORS = [
12235
13046
  ];
12236
13047
  var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
12237
13048
  function readLock(root) {
12238
- const path = join22(root, LOCK_FILE);
12239
- if (!existsSync17(path)) return [];
12240
- const lock = JSON.parse(readFileSync24(path, "utf8"));
13049
+ const path = join24(root, LOCK_FILE);
13050
+ if (!existsSync20(path)) return [];
13051
+ const lock = JSON.parse(readFileSync25(path, "utf8"));
12241
13052
  return lock.outputs ?? [];
12242
13053
  }
12243
13054
  function linkOf(abs) {
12244
13055
  try {
12245
- return lstatSync4(abs).isSymbolicLink() ? readlinkSync2(abs) : null;
13056
+ return lstatSync5(abs).isSymbolicLink() ? readlinkSync2(abs) : null;
12246
13057
  } catch {
12247
13058
  return null;
12248
13059
  }
12249
13060
  }
12250
- var exists = (abs) => existsSync17(abs) || linkOf(abs) !== null;
13061
+ var exists = (abs) => existsSync20(abs) || linkOf(abs) !== null;
12251
13062
  function readJson2(abs) {
12252
- if (!existsSync17(abs)) return {};
13063
+ if (!existsSync20(abs)) return {};
12253
13064
  let value;
12254
13065
  try {
12255
- value = JSON.parse(readFileSync24(abs, "utf8"));
13066
+ value = JSON.parse(readFileSync25(abs, "utf8"));
12256
13067
  } catch {
12257
13068
  throw new ProjectError(
12258
13069
  "Cannot parse native JSON configuration; contents are redacted"
@@ -12304,19 +13115,19 @@ function withoutBlock(text3) {
12304
13115
  return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
12305
13116
  }
12306
13117
  function present(root, output) {
12307
- const abs = join22(root, "file" in output ? output.file : output.path);
13118
+ const abs = join24(root, "file" in output ? output.file : output.path);
12308
13119
  switch (output.kind) {
12309
13120
  case "link":
12310
13121
  return linkOf(abs) === output.target;
12311
13122
  case "file":
12312
- return existsSync17(abs) && linkOf(abs) === null && readFileSync24(abs, "utf8") === output.content;
13123
+ return existsSync20(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
12313
13124
  case "json":
12314
13125
  if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
12315
13126
  return getKey(readJson2(abs), output.key) === void 0;
12316
13127
  }
12317
13128
  return same(getKey(readJson2(abs), output.key), output.value);
12318
13129
  case "block":
12319
- return existsSync17(abs) && readFileSync24(abs, "utf8").includes(block(output.content));
13130
+ return existsSync20(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
12320
13131
  }
12321
13132
  }
12322
13133
  function replaceable(root, output, owned) {
@@ -12324,28 +13135,28 @@ function replaceable(root, output, owned) {
12324
13135
  if (output.key[0] !== "mcpServers" || owned.has(output.path)) {
12325
13136
  return true;
12326
13137
  }
12327
- const existing = getKey(readJson2(join22(root, output.file)), output.key);
13138
+ const existing = getKey(readJson2(join24(root, output.file)), output.key);
12328
13139
  return existing === void 0 || same(existing, output.value);
12329
13140
  }
12330
13141
  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);
13142
+ const absolute2 = join24(root, output.file);
13143
+ const text4 = existsSync20(absolute2) ? readFileSync25(absolute2, "utf8") : "";
13144
+ const outside = parse5(withoutBlock(text4));
13145
+ const proposed = parse5(output.content);
12335
13146
  const existing = objectKeys(outside["mcp_servers"]);
12336
13147
  return !objectKeys(proposed["mcp_servers"]).some(
12337
13148
  (name) => existing.includes(name)
12338
13149
  );
12339
13150
  }
12340
13151
  if (owned.has(output.path)) return true;
12341
- const abs = join22(root, output.path);
13152
+ const abs = join24(root, output.path);
12342
13153
  if (!exists(abs)) return true;
12343
13154
  const target2 = linkOf(abs);
12344
13155
  if (target2 !== null) {
12345
13156
  return output.kind === "link" && target2 === output.target;
12346
13157
  }
12347
- if (lstatSync4(abs).isDirectory()) return false;
12348
- const text3 = readFileSync24(abs, "utf8");
13158
+ if (lstatSync5(abs).isDirectory()) return false;
13159
+ const text3 = readFileSync25(abs, "utf8");
12349
13160
  if (output.kind === "file" && text3 === output.content) return true;
12350
13161
  return PREDECESSORS.some((mark) => text3.includes(mark));
12351
13162
  }
@@ -12359,15 +13170,15 @@ function undeclared(spec, outputs) {
12359
13170
  const declared = new Set(outputs.map((o) => o.path));
12360
13171
  const found = [];
12361
13172
  for (const dir of censusDirs(spec)) {
12362
- const abs = join22(spec.root, dir);
12363
- if (!existsSync17(abs)) continue;
13173
+ const abs = join24(spec.root, dir);
13174
+ if (!existsSync20(abs)) continue;
12364
13175
  for (const entry2 of readdirSync9(abs)) {
12365
13176
  const path = `${dir}/${entry2}`;
12366
13177
  if (!declared.has(path)) found.push(path);
12367
13178
  }
12368
13179
  }
12369
- const rules = join22(spec.root, RULES_DIR);
12370
- if (spec.tools.has("grok") && existsSync17(rules)) {
13180
+ const rules = join24(spec.root, RULES_DIR);
13181
+ if (spec.tools.has("grok") && existsSync20(rules)) {
12371
13182
  for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
12372
13183
  if (entry2.isDirectory()) continue;
12373
13184
  found.push(`${RULES_DIR}/${entry2.name}`);
@@ -12394,9 +13205,9 @@ function deadGlobs(root, outputs) {
12394
13205
  if (rule.kind === "file") {
12395
13206
  text3 = rule.content;
12396
13207
  } 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");
13208
+ const abs = join24(root, posix2.join(posix2.dirname(rule.path), rule.target));
13209
+ if (!existsSync20(abs)) continue;
13210
+ text3 = readFileSync25(abs, "utf8");
12400
13211
  } else {
12401
13212
  continue;
12402
13213
  }
@@ -12446,7 +13257,7 @@ function project(spec, options2) {
12446
13257
  }
12447
13258
  }
12448
13259
  for (const path of jsonPaths) {
12449
- readJson2(join22(spec.root, path));
13260
+ readJson2(join24(spec.root, path));
12450
13261
  }
12451
13262
  if (options2.check) {
12452
13263
  const problems = [
@@ -12508,11 +13319,13 @@ function project(spec, options2) {
12508
13319
  for (const output of outputs) {
12509
13320
  if (output.kind !== "link" && output.kind !== "file") continue;
12510
13321
  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);
13322
+ const abs = join24(spec.root, output.path);
13323
+ mkdirSync7(dirname18(abs), { recursive: true });
13324
+ if (output.kind === "link") {
13325
+ replaceLink(abs, output.target);
13326
+ continue;
13327
+ }
13328
+ replaceFile(abs, output.content);
12516
13329
  }
12517
13330
  let pruned = 0;
12518
13331
  const jsonFiles = /* @__PURE__ */ new Map();
@@ -12534,12 +13347,12 @@ function project(spec, options2) {
12534
13347
  group.drops.push(keyed.key);
12535
13348
  jsonFiles.set(keyed.file, group);
12536
13349
  } else {
12537
- rmSync4(join22(spec.root, entry2.path), { force: true, recursive: true });
13350
+ rmSync5(join24(spec.root, entry2.path), { force: true, recursive: true });
12538
13351
  }
12539
13352
  pruned += 1;
12540
13353
  }
12541
13354
  for (const [file2, { sets, drops }] of jsonFiles) {
12542
- const abs = join22(spec.root, file2);
13355
+ const abs = join24(spec.root, file2);
12543
13356
  const before = readJson2(abs);
12544
13357
  const after = structuredClone(before);
12545
13358
  for (const key of drops) deleteKey(after, key);
@@ -12554,22 +13367,22 @@ function project(spec, options2) {
12554
13367
  }
12555
13368
  }
12556
13369
  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)}
13370
+ if (!existsSync20(abs) && Object.keys(after).length === 0) continue;
13371
+ mkdirSync7(dirname18(abs), { recursive: true });
13372
+ replaceFile(abs, `${JSON.stringify(after, null, 2)}
12560
13373
  `);
12561
13374
  }
12562
13375
  for (const [file2, content] of blockFiles) {
12563
- const abs = join22(spec.root, file2);
12564
- const before = existsSync17(abs) ? readFileSync24(abs, "utf8") : "";
13376
+ const abs = join24(spec.root, file2);
13377
+ const before = existsSync20(abs) ? readFileSync25(abs, "utf8") : "";
12565
13378
  let after = withoutBlock(before);
12566
13379
  if (content !== null) {
12567
13380
  const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
12568
13381
  after = `${after}${sep7}${block(content)}`;
12569
13382
  }
12570
13383
  if (after === before) continue;
12571
- mkdirSync7(dirname16(abs), { recursive: true });
12572
- writeFileSync5(abs, after);
13384
+ mkdirSync7(dirname18(abs), { recursive: true });
13385
+ replaceFile(abs, after);
12573
13386
  }
12574
13387
  if (spec.packageRules) {
12575
13388
  const linkedDirs = new Set(
@@ -12580,15 +13393,15 @@ function project(spec, options2) {
12580
13393
  )
12581
13394
  );
12582
13395
  for (const [name, target2] of legacyLinks(spec.root)) {
12583
- const abs = join22(spec.root, RULES_DIR, `${name}.md`);
12584
- const broken = !existsSync17(abs);
13396
+ const abs = join24(spec.root, RULES_DIR, `${name}.md`);
13397
+ const broken = !existsSync20(abs);
12585
13398
  if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
12586
- rmSync4(abs, { force: true });
13399
+ rmSync5(abs, { force: true });
12587
13400
  pruned += 1;
12588
13401
  }
12589
13402
  }
12590
- writeFileSync5(
12591
- join22(spec.root, LOCK_FILE),
13403
+ replaceFile(
13404
+ join24(spec.root, LOCK_FILE),
12592
13405
  `${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
12593
13406
  `
12594
13407
  );
@@ -12602,11 +13415,11 @@ function project(spec, options2) {
12602
13415
  }
12603
13416
 
12604
13417
  // src/project/mcp-import.ts
12605
- import { readFileSync as readFileSync25, statSync as statSync11, writeFileSync as writeFileSync6 } from "fs";
13418
+ import { readFileSync as readFileSync26, statSync as statSync11, writeFileSync as writeFileSync6 } from "fs";
12606
13419
  import { createHash as createHash14 } from "crypto";
12607
- import { basename as basename6, dirname as dirname17, resolve as resolve14 } from "path";
13420
+ import { basename as basename6, dirname as dirname19, resolve as resolve13 } from "path";
12608
13421
  import { homedir as homedir4 } from "os";
12609
- import { parse as parse5, stringify } from "smol-toml";
13422
+ import { parse as parse6, stringify } from "smol-toml";
12610
13423
 
12611
13424
  // src/project/mcp-spec.ts
12612
13425
  init_config();
@@ -12643,13 +13456,13 @@ function readHooks(path, root, raw) {
12643
13456
  return fail2("unknown shell entry field");
12644
13457
  }
12645
13458
  const name = entry2["name"];
12646
- const command2 = entry2["command"];
13459
+ const command3 = entry2["command"];
12647
13460
  const targets = entry2["targets"];
12648
13461
  const timeoutMs = entry2["timeout_ms"] ?? 15e3;
12649
13462
  if (typeof name !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(name) || names.has(name)) {
12650
13463
  return fail2("shell names must be unique lowercase names");
12651
13464
  }
12652
- if (!Array.isArray(command2) || command2.length === 0 || !command2.every(
13465
+ if (!Array.isArray(command3) || command3.length === 0 || !command3.every(
12653
13466
  (part) => typeof part === "string" && part.length > 0 && !part.includes("\0")
12654
13467
  )) {
12655
13468
  return fail2("command must be a nonempty argument list");
@@ -12663,7 +13476,7 @@ function readHooks(path, root, raw) {
12663
13476
  return fail2("timeout_ms must be an integer from 100 to 60000");
12664
13477
  }
12665
13478
  try {
12666
- guardPath(root, command2);
13479
+ guardPath(root, command3);
12667
13480
  } catch (error) {
12668
13481
  return fail2(
12669
13482
  error instanceof Error ? error.message : "invalid guard path"
@@ -12672,7 +13485,7 @@ function readHooks(path, root, raw) {
12672
13485
  names.add(name);
12673
13486
  return {
12674
13487
  name,
12675
- command: command2,
13488
+ command: command3,
12676
13489
  targets,
12677
13490
  timeoutMs
12678
13491
  };
@@ -12895,13 +13708,13 @@ function readMcp(path, value) {
12895
13708
  fail(path, where, `unknown field ${key}`);
12896
13709
  }
12897
13710
  }
12898
- const command2 = entry2["command"];
13711
+ const command3 = entry2["command"];
12899
13712
  const url = entry2["url"];
12900
- if (command2 === void 0 === (url === void 0)) {
13713
+ if (command3 === void 0 === (url === void 0)) {
12901
13714
  fail(path, where, "provide exactly one of command or url");
12902
13715
  }
12903
13716
  for (const [key, item] of [
12904
- ["command", command2],
13717
+ ["command", command3],
12905
13718
  ["url", url],
12906
13719
  ["cwd", entry2["cwd"]]
12907
13720
  ]) {
@@ -12999,7 +13812,7 @@ function readMcp(path, value) {
12999
13812
  fail(path, where, "mailbox must be a boolean");
13000
13813
  }
13001
13814
  servers[alias] = {
13002
- ...typeof command2 === "string" ? { command: command2 } : { url },
13815
+ ...typeof command3 === "string" ? { command: command3 } : { url },
13003
13816
  args: list(path, `${where} args`, entry2["args"], false),
13004
13817
  env,
13005
13818
  ...envVars.length > 0 ? { envVars } : {},
@@ -13034,11 +13847,11 @@ function sourceServers(source2, root) {
13034
13847
  "Use --source <claude|codex|gemini|grok|antigravity>:<file>"
13035
13848
  );
13036
13849
  }
13037
- const path = resolve14(root, source2.slice(split + 1));
13850
+ const path = resolve13(root, source2.slice(split + 1));
13038
13851
  if (statSync11(path).size > 2 * 1024 * 1024) {
13039
13852
  throw new ProjectError("MCP source exceeds the 2 MiB limit");
13040
13853
  }
13041
- const bytes = readFileSync25(path);
13854
+ const bytes = readFileSync26(path);
13042
13855
  const sha2562 = createHash14("sha256").update(bytes).digest("hex");
13043
13856
  const text3 = bytes.toString("utf8").replace(/^\uFEFF/u, "");
13044
13857
  let parsed;
@@ -13046,7 +13859,7 @@ function sourceServers(source2, root) {
13046
13859
  if (text3.trim() === "") {
13047
13860
  parsed = {};
13048
13861
  } else if (path.endsWith(".toml")) {
13049
- parsed = parse5(text3);
13862
+ parsed = parse6(text3);
13050
13863
  } else {
13051
13864
  parsed = object6(JSON.parse(text3));
13052
13865
  }
@@ -13229,9 +14042,9 @@ function equivalent(alias, left, right) {
13229
14042
  return JSON.stringify(readMcp("import", { [alias]: left })[alias]) === JSON.stringify(readMcp("import", { [alias]: right })[alias]);
13230
14043
  }
13231
14044
  function planMcpImport(config, sources2, names = []) {
13232
- const original = readFileSync25(config, "utf8");
13233
- const root = dirname17(resolve14(config));
13234
- const existing = object6(parse5(original)["mcp"] ?? {});
14045
+ const original = readFileSync26(config, "utf8");
14046
+ const root = dirname19(resolve13(config));
14047
+ const existing = object6(parse6(original)["mcp"] ?? {});
13235
14048
  const additions = {};
13236
14049
  const entries = [];
13237
14050
  const fingerprints = [];
@@ -13280,11 +14093,11 @@ function writeMcpImport(plan2) {
13280
14093
  "Resolve blocked or conflicting MCP entries before importing"
13281
14094
  );
13282
14095
  }
13283
- if (readFileSync25(plan2.config, "utf8") !== plan2.original) {
14096
+ if (readFileSync26(plan2.config, "utf8") !== plan2.original) {
13284
14097
  throw new ProjectError("pmcp.toml changed after planning; plan again");
13285
14098
  }
13286
14099
  for (const source2 of plan2.sources) {
13287
- const current = createHash14("sha256").update(readFileSync25(source2.path)).digest("hex");
14100
+ const current = createHash14("sha256").update(readFileSync26(source2.path)).digest("hex");
13288
14101
  if (current !== source2.sha256) {
13289
14102
  throw new ProjectError("MCP source changed after planning; plan again");
13290
14103
  }
@@ -13293,8 +14106,8 @@ function writeMcpImport(plan2) {
13293
14106
  const after = `${plan2.original.trimEnd()}
13294
14107
 
13295
14108
  ${additions}`;
13296
- parse5(after);
13297
- const backup = backupFiles(dirname17(resolve14(plan2.config)), [
14109
+ parse6(after);
14110
+ const backup = backupFiles(dirname19(resolve13(plan2.config)), [
13298
14111
  basename6(plan2.config)
13299
14112
  ]);
13300
14113
  writeFileSync6(plan2.config, after);
@@ -13303,25 +14116,25 @@ ${additions}`;
13303
14116
 
13304
14117
  // src/project/mcp-env.ts
13305
14118
  import { spawn } from "child_process";
13306
- import { readFileSync as readFileSync26, statSync as statSync12 } from "fs";
14119
+ import { readFileSync as readFileSync27, statSync as statSync12 } from "fs";
13307
14120
  import { homedir as homedir5 } from "os";
13308
- import { resolve as resolve15 } from "path";
13309
- import { parse as parse6 } from "smol-toml";
14121
+ import { resolve as resolve14 } from "path";
14122
+ import { parse as parse7 } from "smol-toml";
13310
14123
  function sourcePath(file2, root) {
13311
14124
  if (file2.startsWith("~/")) {
13312
- return resolve15(homedir5(), file2.slice(2));
14125
+ return resolve14(homedir5(), file2.slice(2));
13313
14126
  }
13314
- return resolve15(root, file2.replaceAll("${PROJECT_ROOT}", root));
14127
+ return resolve14(root, file2.replaceAll("${PROJECT_ROOT}", root));
13315
14128
  }
13316
14129
  function sourceValue(file2, pointer) {
13317
14130
  try {
13318
14131
  if (statSync12(file2).size > 2 * 1024 * 1024) {
13319
14132
  throw new Error("size");
13320
14133
  }
13321
- const text3 = readFileSync26(file2, "utf8").replace(/^\uFEFF/u, "");
14134
+ const text3 = readFileSync27(file2, "utf8").replace(/^\uFEFF/u, "");
13322
14135
  let value;
13323
14136
  if (file2.endsWith(".toml")) {
13324
- value = parse6(text3);
14137
+ value = parse7(text3);
13325
14138
  } else {
13326
14139
  value = JSON.parse(text3);
13327
14140
  }
@@ -13399,11 +14212,11 @@ async function execMcp(spec, alias, env) {
13399
14212
 
13400
14213
  // src/project/doctor.ts
13401
14214
  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";
13405
- var spawnRunner = (command2, args, cwd, env) => {
13406
- const result = spawnSync3(command2, [...args], {
14215
+ import { existsSync as existsSync21, readFileSync as readFileSync28 } from "fs";
14216
+ import { join as join25 } from "path";
14217
+ import { parse as parse8 } from "smol-toml";
14218
+ var spawnRunner = (command3, args, cwd, env) => {
14219
+ const result = spawnSync3(command3, [...args], {
13407
14220
  cwd,
13408
14221
  ...env ? { env: { ...process.env, ...env } } : {},
13409
14222
  encoding: "utf8",
@@ -13477,10 +14290,10 @@ function claude(spec, o) {
13477
14290
  }
13478
14291
  function codex(spec, o) {
13479
14292
  const findings = [];
13480
- const config = join23(o.home, ".codex/config.toml");
14293
+ const config = join25(o.home, ".codex/config.toml");
13481
14294
  let trusted = false;
13482
- if (existsSync18(config)) {
13483
- const projects = parse7(readFileSync27(config, "utf8")).projects ?? {};
14295
+ if (existsSync21(config)) {
14296
+ const projects = parse8(readFileSync28(config, "utf8")).projects ?? {};
13484
14297
  const root = spec.root.replace(/\/$/u, "");
13485
14298
  trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
13486
14299
  }
@@ -13606,8 +14419,8 @@ function grok(spec, o) {
13606
14419
  }
13607
14420
  function antigravity(spec, options2) {
13608
14421
  const foreign = agentNames(spec.root).filter((name) => {
13609
- const text3 = readFileSync27(
13610
- join23(spec.root, ".agents/agents", name, "agent.md"),
14422
+ const text3 = readFileSync28(
14423
+ join25(spec.root, ".agents/agents", name, "agent.md"),
13611
14424
  "utf8"
13612
14425
  );
13613
14426
  return splitFrontmatter(text3).meta["tools"] !== void 0;
@@ -13627,7 +14440,7 @@ function antigravity(spec, options2) {
13627
14440
  if (serversForTool(spec, "antigravity").length > 0) {
13628
14441
  const result = options2.run(
13629
14442
  "agy",
13630
- ["plugin", "validate", join23(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
14443
+ ["plugin", "validate", join25(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
13631
14444
  spec.root
13632
14445
  );
13633
14446
  findings.push(
@@ -13658,9 +14471,9 @@ function doctor(spec, options2) {
13658
14471
  }
13659
14472
 
13660
14473
  // 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";
14474
+ import { existsSync as existsSync22, readFileSync as readFileSync29 } from "fs";
14475
+ import { isAbsolute as isAbsolute7, join as join26, resolve as resolve15 } from "path";
14476
+ import { parse as parse9 } from "smol-toml";
13664
14477
  function object7(value) {
13665
14478
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
13666
14479
  throw new Error("Configuration is not an object");
@@ -13668,11 +14481,11 @@ function object7(value) {
13668
14481
  return value;
13669
14482
  }
13670
14483
  function settings(file2, toml = false) {
13671
- if (!existsSync19(file2)) {
14484
+ if (!existsSync22(file2)) {
13672
14485
  return {};
13673
14486
  }
13674
- const text3 = readFileSync28(file2, "utf8");
13675
- return object7(toml ? parse8(text3) : JSON.parse(text3));
14487
+ const text3 = readFileSync29(file2, "utf8");
14488
+ return object7(toml ? parse9(text3) : JSON.parse(text3));
13676
14489
  }
13677
14490
  function configuredState(value) {
13678
14491
  if (value === true) {
@@ -13684,11 +14497,11 @@ function configuredState(value) {
13684
14497
  return "unknown";
13685
14498
  }
13686
14499
  function located(metadata2, path, pathScope) {
13687
- return { ...metadata2, path, pathScope, exists: existsSync19(path) };
14500
+ return { ...metadata2, path, pathScope, exists: existsSync22(path) };
13688
14501
  }
13689
14502
  function metadata(tool, spec, options2) {
13690
14503
  const env = options2.env ?? {};
13691
- const directory = (name, fallback) => resolve16(spec.root, env[name] ?? join24(options2.home, fallback));
14504
+ const directory = (name, fallback) => resolve15(spec.root, env[name] ?? join26(options2.home, fallback));
13692
14505
  if (tool === "antigravity") {
13693
14506
  return {
13694
14507
  state: "unsupported",
@@ -13712,7 +14525,7 @@ function metadata(tool, spec, options2) {
13712
14525
  state,
13713
14526
  source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
13714
14527
  },
13715
- join24(home2, "memories"),
14528
+ join26(home2, "memories"),
13716
14529
  "store"
13717
14530
  );
13718
14531
  }
@@ -13725,12 +14538,12 @@ function metadata(tool, spec, options2) {
13725
14538
  }
13726
14539
  if (tool === "claude") {
13727
14540
  const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
13728
- const user2 = settings(join24(home2, "settings.json"));
14541
+ const user2 = settings(join26(home2, "settings.json"));
13729
14542
  const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
13730
14543
  const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
13731
14544
  const override = user2["autoMemoryDirectory"];
13732
14545
  if (typeof override === "string") {
13733
- const path = override.startsWith("~/") ? join24(options2.home, override.slice(2)) : override;
14546
+ const path = override.startsWith("~/") ? join26(options2.home, override.slice(2)) : override;
13734
14547
  if (!isAbsolute7(path)) {
13735
14548
  return {
13736
14549
  state: "unknown",
@@ -13743,7 +14556,7 @@ function metadata(tool, spec, options2) {
13743
14556
  if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
13744
14557
  return located(
13745
14558
  { state, source: source2 },
13746
- join24(home2, "projects", projectName, "memory"),
14559
+ join26(home2, "projects", projectName, "memory"),
13747
14560
  "store"
13748
14561
  );
13749
14562
  }
@@ -13752,14 +14565,14 @@ function metadata(tool, spec, options2) {
13752
14565
  state,
13753
14566
  source: `${source2}; native project directory encoding is not guessed`
13754
14567
  },
13755
- join24(home2, "projects"),
14568
+ join26(home2, "projects"),
13756
14569
  "base"
13757
14570
  );
13758
14571
  }
13759
14572
  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);
14573
+ const home2 = join26(options2.home, ".grok");
14574
+ const user2 = settings(join26(home2, "config.toml"), true);
14575
+ const project2 = settings(join26(spec.root, ".grok/config.toml"), true);
13763
14576
  const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
13764
14577
  const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
13765
14578
  const overridden = projectMemory["enabled"] !== void 0;
@@ -13769,12 +14582,12 @@ function metadata(tool, spec, options2) {
13769
14582
  state,
13770
14583
  source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
13771
14584
  },
13772
- join24(home2, "memory-v2/workspaces"),
14585
+ join26(home2, "memory-v2/workspaces"),
13773
14586
  "base"
13774
14587
  );
13775
14588
  }
13776
14589
  const home = directory("GEMINI_CLI_HOME", ".gemini");
13777
- const user = settings(join24(home, "settings.json"));
14590
+ const user = settings(join26(home, "settings.json"));
13778
14591
  const context = user["context"] === void 0 ? {} : object7(user["context"]);
13779
14592
  const names = context["fileName"] ?? "GEMINI.md";
13780
14593
  const contextFileNames = typeof names === "string" ? [names] : names;
@@ -13790,7 +14603,7 @@ function metadata(tool, spec, options2) {
13790
14603
  source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
13791
14604
  contextFileNames
13792
14605
  },
13793
- join24(home, "GEMINI.md"),
14606
+ join26(home, "GEMINI.md"),
13794
14607
  "store"
13795
14608
  );
13796
14609
  }
@@ -13823,14 +14636,14 @@ function memoryFindings(spec, options2) {
13823
14636
  // src/project/edit.ts
13824
14637
  import {
13825
14638
  cpSync,
13826
- existsSync as existsSync20,
14639
+ existsSync as existsSync23,
13827
14640
  mkdirSync as mkdirSync8,
13828
- readFileSync as readFileSync29,
13829
- rmSync as rmSync5,
14641
+ readFileSync as readFileSync30,
14642
+ rmSync as rmSync6,
13830
14643
  statSync as statSync13,
13831
14644
  writeFileSync as writeFileSync7
13832
14645
  } from "fs";
13833
- import { basename as basename7, dirname as dirname18, join as join25 } from "path";
14646
+ import { basename as basename7, dirname as dirname20, join as join27 } from "path";
13834
14647
  var CROSS_TOOL = ["name", "description"];
13835
14648
  var tomlValue2 = (value) => JSON.stringify(value);
13836
14649
  function findEntry(entries, wanted) {
@@ -13854,7 +14667,7 @@ function addEntry(root, entry2) {
13854
14667
  `${entry2.name} is a ${kind2}; only skills and agents are added to .agents/`
13855
14668
  );
13856
14669
  }
13857
- const { meta, body } = splitFrontmatter(readFileSync29(entry2.path, "utf8"));
14670
+ const { meta, body } = splitFrontmatter(readFileSync30(entry2.path, "utf8"));
13858
14671
  const name = basename7(entry2.slug);
13859
14672
  if (!/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
13860
14673
  throw new ProjectError(
@@ -13862,11 +14675,11 @@ function addEntry(root, entry2) {
13862
14675
  );
13863
14676
  }
13864
14677
  if (kind2 === "skill") {
13865
- const dest2 = join25(root, ".agents/skills", name);
13866
- if (existsSync20(dest2))
14678
+ const dest2 = join27(root, ".agents/skills", name);
14679
+ if (existsSync23(dest2))
13867
14680
  throw new ProjectError(`.agents/skills/${name} already exists`);
13868
- const source2 = entry2.root ?? dirname18(entry2.path);
13869
- mkdirSync8(dirname18(dest2), { recursive: true });
14681
+ const source2 = entry2.root ?? dirname20(entry2.path);
14682
+ mkdirSync8(dirname20(dest2), { recursive: true });
13870
14683
  if (statSync13(source2).isDirectory()) {
13871
14684
  cpSync(source2, dest2, { recursive: true });
13872
14685
  } else {
@@ -13878,14 +14691,14 @@ function addEntry(root, entry2) {
13878
14691
  description: meta["description"] ?? entry2.description
13879
14692
  });
13880
14693
  writeFileSync7(
13881
- join25(dest2, "SKILL.md"),
14694
+ join27(dest2, "SKILL.md"),
13882
14695
  `${head}
13883
14696
  ${body.replace(/^\n/u, "")}`
13884
14697
  );
13885
14698
  return { kind: kind2, name, path: `.agents/skills/${name}` };
13886
14699
  }
13887
- const dest = join25(root, ".agents/agents", name);
13888
- if (existsSync20(dest))
14700
+ const dest = join27(root, ".agents/agents", name);
14701
+ if (existsSync23(dest))
13889
14702
  throw new ProjectError(`.agents/agents/${name} already exists`);
13890
14703
  const kept = {};
13891
14704
  const moved = {};
@@ -13895,17 +14708,17 @@ ${body.replace(/^\n/u, "")}`
13895
14708
  }
13896
14709
  mkdirSync8(dest, { recursive: true });
13897
14710
  writeFileSync7(
13898
- join25(dest, "agent.md"),
14711
+ join27(dest, "agent.md"),
13899
14712
  `${renderFrontmatter(kept)}
13900
14713
  ${body.replace(/^\n/u, "")}`
13901
14714
  );
13902
14715
  if (Object.keys(moved).length > 0) {
13903
- const config = join25(root, "pmcp.toml");
14716
+ const config = join27(root, "pmcp.toml");
13904
14717
  const lines2 = [
13905
14718
  `[agents.${name}.claude]`,
13906
14719
  ...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
13907
14720
  ];
13908
- const before = existsSync20(config) ? readFileSync29(config, "utf8") : "";
14721
+ const before = existsSync23(config) ? readFileSync30(config, "utf8") : "";
13909
14722
  const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
13910
14723
  writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
13911
14724
  `);
@@ -13928,15 +14741,15 @@ function withoutTables(toml, name) {
13928
14741
  function removeAsset(root, name) {
13929
14742
  const removed = [];
13930
14743
  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 });
14744
+ if (!existsSync23(join27(root, rel))) continue;
14745
+ rmSync6(join27(root, rel), { recursive: true, force: true });
13933
14746
  removed.push(rel);
13934
14747
  }
13935
14748
  if (removed.length === 0)
13936
14749
  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");
14750
+ const config = join27(root, "pmcp.toml");
14751
+ if (existsSync23(config)) {
14752
+ const before = readFileSync30(config, "utf8");
13940
14753
  const after = withoutTables(before, name);
13941
14754
  if (after !== before) writeFileSync7(config, after);
13942
14755
  }
@@ -14175,16 +14988,16 @@ var doctorCommand = {
14175
14988
  };
14176
14989
 
14177
14990
  // 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";
14991
+ import { existsSync as existsSync24, readFileSync as readFileSync31, statSync as statSync14 } from "fs";
14992
+ import { basename as basename8, join as join28, resolve as resolve16 } from "path";
14180
14993
  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()) {
14994
+ const root = resolve16(dir);
14995
+ const skillMd = basename8(root) === "SKILL.md" ? root : join28(root, "SKILL.md");
14996
+ if (!existsSync24(skillMd) || !statSync14(skillMd).isFile()) {
14184
14997
  return [{ path: root, reason: "no SKILL.md", level: "error" }];
14185
14998
  }
14186
- const front = frontmatterObject(readFileSync30(skillMd, "utf8"));
14187
- const directory = basename8(skillMd === root ? join26(root, "..") : root);
14999
+ const front = frontmatterObject(readFileSync31(skillMd, "utf8"));
15000
+ const directory = basename8(skillMd === root ? join28(root, "..") : root);
14188
15001
  return specProblems(front, directory).map((reason) => ({
14189
15002
  path: skillMd,
14190
15003
  reason,
@@ -14229,8 +15042,8 @@ var validateCommand = {
14229
15042
  };
14230
15043
 
14231
15044
  // src/commands/mailbox.ts
14232
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync8 } from "fs";
14233
- import { resolve as resolve18 } from "path";
15045
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync8 } from "fs";
15046
+ import { resolve as resolve17 } from "path";
14234
15047
  init_config2();
14235
15048
  init_service();
14236
15049
  init_schemas();
@@ -14313,7 +15126,7 @@ var mailboxCommand = {
14313
15126
  }
14314
15127
  if (verb === "init") {
14315
15128
  const config2 = initializeMailboxConfig(
14316
- resolve18(context.cwd, one(context.args, "config") ?? "pmcp.toml"),
15129
+ resolve17(context.cwd, one(context.args, "config") ?? "pmcp.toml"),
14317
15130
  one(context.args, "state-dir")
14318
15131
  );
14319
15132
  const store2 = await MailboxStore.open(config2);
@@ -14338,7 +15151,7 @@ var mailboxCommand = {
14338
15151
  if (verb === "restore") {
14339
15152
  await restoreBackup(required(context, "input"), {
14340
15153
  ...config,
14341
- databasePath: resolve18(context.cwd, required(context, "output"))
15154
+ databasePath: resolve17(context.cwd, required(context, "output"))
14342
15155
  });
14343
15156
  context.ui.data(
14344
15157
  `${JSON.stringify({ ok: true, data: { restored: true } })}
@@ -14412,7 +15225,7 @@ var mailboxCommand = {
14412
15225
  ownsBinding = true;
14413
15226
  }
14414
15227
  const inputFile = one(context.args, "input");
14415
- const input = inputFile ? JSON.parse(readFileSync31(inputFile, "utf8")) : {};
15228
+ const input = inputFile ? JSON.parse(readFileSync32(inputFile, "utf8")) : {};
14416
15229
  const result = await new MailboxService(store, binding).call(
14417
15230
  operation,
14418
15231
  input
@@ -14496,7 +15309,7 @@ var harnessOptions = [
14496
15309
  ];
14497
15310
 
14498
15311
  // src/commands/harness/invocation.ts
14499
- import { readFileSync as readFileSync32, statSync as statSync15 } from "fs";
15312
+ import { readFileSync as readFileSync33, statSync as statSync15 } from "fs";
14500
15313
  init_validation();
14501
15314
  var common = ["group-file", "generation"];
14502
15315
  var options = {
@@ -14536,7 +15349,7 @@ function file(path) {
14536
15349
  const stat = statSync15(path);
14537
15350
  if (!stat.isFile() || stat.size > 65536)
14538
15351
  throw new ArgumentError("Input file exceeds 65536 bytes");
14539
- return readFileSync32(path, "utf8");
15352
+ return readFileSync33(path, "utf8");
14540
15353
  }
14541
15354
  function parseInvocation(context) {
14542
15355
  const [family, action, ...extra] = context.args.positional;
@@ -14642,6 +15455,9 @@ var harnessCommand = {
14642
15455
  }
14643
15456
  };
14644
15457
 
15458
+ // src/commands/tui.ts
15459
+ init_config();
15460
+
14645
15461
  // src/commands/tui/invocation.ts
14646
15462
  var tuiOptions = [
14647
15463
  {
@@ -14657,6 +15473,11 @@ var tuiOptions = [
14657
15473
  },
14658
15474
  { name: "rows", describe: "Terminal rows (12\u20131000)", placeholder: "<count>" },
14659
15475
  { name: "index", describe: "Worker dock index", placeholder: "<count>" },
15476
+ {
15477
+ name: "memory-mb",
15478
+ describe: "Worker memory reservation",
15479
+ placeholder: "<count>"
15480
+ },
14660
15481
  {
14661
15482
  name: "confirm-session",
14662
15483
  describe: "Exact target ID for stop",
@@ -14664,9 +15485,24 @@ var tuiOptions = [
14664
15485
  },
14665
15486
  {
14666
15487
  name: "config",
14667
- describe: "Optional mailbox pmcp.toml",
15488
+ describe: "Project pmcp.toml (or mailbox config for create)",
14668
15489
  placeholder: "<file>"
14669
15490
  },
15491
+ {
15492
+ name: "runtime",
15493
+ describe: "Native runtime ID for main launch or worker start",
15494
+ placeholder: "<runtime>"
15495
+ },
15496
+ {
15497
+ name: "yes",
15498
+ describe: "Confirm the first native runtime start",
15499
+ boolean: true
15500
+ },
15501
+ {
15502
+ name: "new",
15503
+ describe: "Create a workspace after the previous group is stopped",
15504
+ boolean: true
15505
+ },
14670
15506
  {
14671
15507
  name: "actor-file",
14672
15508
  describe: "Optional private mailbox observer credential",
@@ -14674,6 +15510,10 @@ var tuiOptions = [
14674
15510
  }
14675
15511
  ];
14676
15512
  var allowed2 = {
15513
+ launch: ["config", "runtime", "yes", "new"],
15514
+ plan: ["config", "runtime"],
15515
+ recovery: ["config"],
15516
+ "start-worker": ["config", "view", "runtime", "memory-mb"],
14677
15517
  doctor: [],
14678
15518
  create: ["group-file", "columns", "rows", "config", "actor-file"],
14679
15519
  inspect: ["view"],
@@ -14691,10 +15531,12 @@ var allowed2 = {
14691
15531
  close: ["view"],
14692
15532
  dismiss: ["view"],
14693
15533
  management: ["view"],
15534
+ "worker-menu": ["view"],
14694
15535
  check: ["view"]
14695
15536
  };
14696
15537
  function parseTui(args) {
14697
- const [action, ...extra] = args.positional;
15538
+ let [action, ...extra] = args.positional;
15539
+ if (action === void 0) action = one(args, "view") ? "open" : "launch";
14698
15540
  const options2 = action ? allowed2[action] : void 0;
14699
15541
  if (!action || !options2 || extra.length)
14700
15542
  throw new ArgumentError("Provide one supported TUI operation");
@@ -14714,6 +15556,33 @@ function parseTui(args) {
14714
15556
  return Number(value);
14715
15557
  };
14716
15558
  if (action === "doctor") return { action, input: {} };
15559
+ if (action === "start-worker") {
15560
+ const config = one(args, "config");
15561
+ const viewFile = one(args, "view");
15562
+ if (config && viewFile)
15563
+ throw new ArgumentError("Choose --config or --view for worker start");
15564
+ return {
15565
+ action,
15566
+ viewFile,
15567
+ input: {},
15568
+ startup: {
15569
+ config,
15570
+ runtime: required2("runtime"),
15571
+ memoryMb: number("memory-mb")
15572
+ }
15573
+ };
15574
+ }
15575
+ if (action === "launch" || action === "plan" || action === "recovery")
15576
+ return {
15577
+ action,
15578
+ input: {},
15579
+ startup: {
15580
+ config: one(args, "config"),
15581
+ runtime: one(args, "runtime"),
15582
+ fresh: args.flags.has("new"),
15583
+ confirmed: args.flags.has("yes")
15584
+ }
15585
+ };
14717
15586
  if (action === "create") {
14718
15587
  const config = one(args, "config");
14719
15588
  const actorFile = one(args, "actor-file");
@@ -14753,19 +15622,52 @@ function parseTui(args) {
14753
15622
  // src/commands/tui.ts
14754
15623
  var tuiCommand = {
14755
15624
  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>",
15625
+ describe: "Open a native terminal workspace with optional configuration",
15626
+ usage: "pmcp tui [--config <file>] [--runtime <id>] | plan | recovery | doctor | start-worker --runtime <id> | operation --view <file>",
14758
15627
  options: tuiOptions,
14759
15628
  async run(context) {
14760
15629
  const invocation2 = parseTui(context.args);
14761
15630
  const { invokeTui: invokeTui2, failedInvocation: failedInvocation2 } = await Promise.resolve().then(() => (init_invoke2(), invoke_exports2));
14762
15631
  try {
14763
- const receipt = await invokeTui2(invocation2);
14764
- if (invocation2.action !== "open" || receipt.ok !== true)
15632
+ let receipt;
15633
+ if (invocation2.action === "start-worker") {
15634
+ if (invocation2.viewFile) {
15635
+ const { startViewWorker: startViewWorker2 } = await Promise.resolve().then(() => (init_worker_start(), worker_start_exports));
15636
+ receipt = await startViewWorker2(
15637
+ invocation2.viewFile,
15638
+ invocation2.startup.runtime,
15639
+ invocation2.startup?.memoryMb
15640
+ );
15641
+ } else {
15642
+ const { startWorkspaceWorker: startWorkspaceWorker2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
15643
+ receipt = await startWorkspaceWorker2(
15644
+ { config: invocation2.startup?.config, cwd: context.cwd },
15645
+ invocation2.startup.runtime,
15646
+ invocation2.startup?.memoryMb
15647
+ );
15648
+ }
15649
+ } else if (invocation2.action === "recovery") {
15650
+ const { inspectStartupRecovery: inspectStartupRecovery2 } = await Promise.resolve().then(() => (init_recovery2(), recovery_exports));
15651
+ receipt = inspectStartupRecovery2({
15652
+ ...invocation2.startup,
15653
+ cwd: context.cwd
15654
+ });
15655
+ } else if (invocation2.action === "launch" || invocation2.action === "plan") {
15656
+ const { launchStartup: launchStartup2, planStartup: planStartup2 } = await Promise.resolve().then(() => (init_entry2(), entry_exports));
15657
+ const request3 = { ...invocation2.startup, cwd: context.cwd };
15658
+ receipt = invocation2.action === "plan" ? planStartup2(request3) : await launchStartup2(
15659
+ request3,
15660
+ invocation2.startup?.confirmed === true
15661
+ );
15662
+ } else {
15663
+ receipt = await invokeTui2(invocation2);
15664
+ }
15665
+ if (!["open", "launch"].includes(invocation2.action) || receipt.ok !== true)
14765
15666
  context.ui.data(`${JSON.stringify(receipt)}
14766
15667
  `);
14767
15668
  return receipt.ok === true ? 0 : 1;
14768
15669
  } catch (error) {
15670
+ if (error instanceof ConfigError) throw new ArgumentError(error.message);
14769
15671
  context.ui.data(
14770
15672
  `${JSON.stringify(failedInvocation2(invocation2.action, error))}
14771
15673
  `
@@ -14780,14 +15682,14 @@ import { resolve as resolve21 } from "path";
14780
15682
 
14781
15683
  // src/marketplace/export.ts
14782
15684
  import {
14783
- existsSync as existsSync33,
14784
- lstatSync as lstatSync12,
14785
- mkdirSync as mkdirSync12,
14786
- readFileSync as readFileSync37,
15685
+ existsSync as existsSync37,
15686
+ lstatSync as lstatSync14,
15687
+ mkdirSync as mkdirSync13,
15688
+ readFileSync as readFileSync38,
14787
15689
  readdirSync as readdirSync13,
14788
15690
  writeFileSync as writeFileSync14
14789
15691
  } from "fs";
14790
- import { dirname as dirname24, join as join52, resolve as resolve20 } from "path";
15692
+ import { dirname as dirname27, join as join55, resolve as resolve20 } from "path";
14791
15693
  var NATIVE_PLUGIN_RUNTIMES = [
14792
15694
  "claude",
14793
15695
  "codex",
@@ -14812,7 +15714,7 @@ function readResources(directory, prefix, files) {
14812
15714
  for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
14813
15715
  if (entry2.isSymbolicLink())
14814
15716
  throw new Error("Plugin exports cannot contain symlinks");
14815
- const path = join52(directory, entry2.name);
15717
+ const path = join55(directory, entry2.name);
14816
15718
  const relativePath = `${prefix}/${entry2.name}`;
14817
15719
  if (entry2.isDirectory()) {
14818
15720
  if (relativePath.split("/").length > 6)
@@ -14822,10 +15724,10 @@ function readResources(directory, prefix, files) {
14822
15724
  }
14823
15725
  if (!entry2.isFile())
14824
15726
  throw new Error("Plugin resources must be regular files");
14825
- const stats = lstatSync12(path);
15727
+ const stats = lstatSync14(path);
14826
15728
  if (stats.size > 1024 * 1024 || files.size >= 256)
14827
15729
  throw new Error("Plugin resources exceed the export budget");
14828
- files.set(relativePath, readFileSync37(path));
15730
+ files.set(relativePath, readFileSync38(path));
14829
15731
  }
14830
15732
  }
14831
15733
  function pluginExportFiles(root, name, runtime2) {
@@ -14834,7 +15736,7 @@ function pluginExportFiles(root, name, runtime2) {
14834
15736
  const files = /* @__PURE__ */ new Map();
14835
15737
  readResources(containedPath(plugin.directory, "skills"), "skills", files);
14836
15738
  for (const name2 of ["LICENSE", "NOTICE"])
14837
- files.set(name2, readFileSync37(containedPath(root, name2)));
15739
+ files.set(name2, readFileSync38(containedPath(root, name2)));
14838
15740
  const manifests = {
14839
15741
  claude: [".claude-plugin/plugin.json", ".mcp.json"],
14840
15742
  codex: ["plugin.json", "mcp.json"],
@@ -14843,10 +15745,10 @@ function pluginExportFiles(root, name, runtime2) {
14843
15745
  agy: []
14844
15746
  };
14845
15747
  for (const filename of manifests[runtime2]) {
14846
- if (existsSync33(join52(plugin.directory, filename)))
15748
+ if (existsSync37(join55(plugin.directory, filename)))
14847
15749
  files.set(
14848
15750
  filename,
14849
- readFileSync37(containedPath(plugin.directory, filename))
15751
+ readFileSync38(containedPath(plugin.directory, filename))
14850
15752
  );
14851
15753
  }
14852
15754
  if (runtime2 === "agy") {
@@ -14857,11 +15759,11 @@ function pluginExportFiles(root, name, runtime2) {
14857
15759
  `
14858
15760
  )
14859
15761
  );
14860
- const mcp = join52(plugin.directory, ".mcp.json");
14861
- if (existsSync33(mcp))
15762
+ const mcp = join55(plugin.directory, ".mcp.json");
15763
+ if (existsSync37(mcp))
14862
15764
  files.set(
14863
15765
  "mcp_config.json",
14864
- readFileSync37(containedPath(plugin.directory, mcp))
15766
+ readFileSync38(containedPath(plugin.directory, mcp))
14865
15767
  );
14866
15768
  }
14867
15769
  return files;
@@ -14869,9 +15771,9 @@ function pluginExportFiles(root, name, runtime2) {
14869
15771
  function exportPublicPlugin(root, name, runtime2, output) {
14870
15772
  const destination = resolve20(output);
14871
15773
  const files = pluginExportFiles(root, name, runtime2);
14872
- mkdirSync12(dirname24(destination), { recursive: true });
15774
+ mkdirSync13(dirname27(destination), { recursive: true });
14873
15775
  try {
14874
- mkdirSync12(destination);
15776
+ mkdirSync13(destination);
14875
15777
  } catch (error) {
14876
15778
  if (error.code === "EEXIST")
14877
15779
  throw new Error(
@@ -14880,8 +15782,8 @@ function exportPublicPlugin(root, name, runtime2, output) {
14880
15782
  throw error;
14881
15783
  }
14882
15784
  for (const [path, bytes] of files) {
14883
- const target2 = join52(destination, path);
14884
- mkdirSync12(dirname24(target2), { recursive: true });
15785
+ const target2 = join55(destination, path);
15786
+ mkdirSync13(dirname27(target2), { recursive: true });
14885
15787
  writeFileSync14(target2, bytes, { flag: "wx" });
14886
15788
  }
14887
15789
  return { plugin: name, runtime: runtime2, output: destination, files: files.size };
@@ -15002,10 +15904,10 @@ function dispatch(argv, options2 = {}) {
15002
15904
  });
15003
15905
  const [verb, ...rest] = rest0;
15004
15906
  if (verb === void 0) {
15005
- return run(serveCommand, [], { ui, env, cwd });
15907
+ return defaultEntry(options2, { ui, env, cwd });
15006
15908
  }
15007
15909
  if (verb === "--help" || verb === "-h" || verb === "help") {
15008
- const named2 = COMMANDS.find((command3) => command3.name === rest[0]);
15910
+ const named2 = COMMANDS.find((command4) => command4.name === rest[0]);
15009
15911
  if (named2) printCommandHelp(ui, named2);
15010
15912
  else printHelp(ui, COMMANDS, PLANNED);
15011
15913
  return 0;
@@ -15018,8 +15920,8 @@ function dispatch(argv, options2 = {}) {
15018
15920
  if (verb.startsWith("-")) {
15019
15921
  return run(serveCommand, rest0, { ui, env, cwd });
15020
15922
  }
15021
- const command2 = COMMANDS.find((candidate2) => candidate2.name === verb);
15022
- if (command2 === void 0) {
15923
+ const command3 = COMMANDS.find((candidate2) => candidate2.name === verb);
15924
+ if (command3 === void 0) {
15023
15925
  if (PLANNED.includes(verb))
15024
15926
  ui.error(`"${verb}" is designed but not implemented yet`);
15025
15927
  else ui.error(`unknown command "${verb}"`);
@@ -15027,22 +15929,45 @@ function dispatch(argv, options2 = {}) {
15027
15929
  return 2;
15028
15930
  }
15029
15931
  if (rest.includes("--help") || rest.includes("-h")) {
15030
- printCommandHelp(ui, command2);
15932
+ printCommandHelp(ui, command3);
15031
15933
  return 0;
15032
15934
  }
15033
- return run(command2, rest, { ui, env, cwd });
15935
+ return run(command3, rest, { ui, env, cwd });
15936
+ }
15937
+ var VERSION = "0.16.0";
15938
+ async function defaultEntry(options2, context) {
15939
+ const terminal = options2.terminal ?? {
15940
+ stdin: process.stdin.isTTY === true,
15941
+ stdout: process.stdout.isTTY === true
15942
+ };
15943
+ const mode = entryMode({
15944
+ ...terminal,
15945
+ term: context.env.TERM,
15946
+ ci: context.env.CI
15947
+ });
15948
+ if (mode === "server") return run(serveCommand, [], context);
15949
+ if (mode === "terminal") {
15950
+ const { terminalSupport: terminalSupport2 } = await Promise.resolve().then(() => (init_host(), host_exports));
15951
+ const support = terminalSupport2();
15952
+ if (support.supported) return run(tuiCommand, [], context);
15953
+ context.ui.info(`Terminal workspace unavailable: ${support.reason}`);
15954
+ context.ui.line(
15955
+ "Run pmcp tui doctor for prerequisites, or pmcp serve for MCP."
15956
+ );
15957
+ }
15958
+ printHelp(context.ui, COMMANDS, PLANNED);
15959
+ return 0;
15034
15960
  }
15035
- var VERSION = "0.14.1";
15036
- async function run(command2, argv, context) {
15961
+ async function run(command3, argv, context) {
15037
15962
  try {
15038
- return await command2.run({
15963
+ return await command3.run({
15039
15964
  ...context,
15040
- args: parseArgs(argv, command2.options)
15965
+ args: parseArgs(argv, command3.options)
15041
15966
  });
15042
15967
  } catch (error) {
15043
15968
  if (error instanceof ArgumentError) {
15044
15969
  context.ui.error(error.message);
15045
- printCommandHelp(context.ui, command2);
15970
+ printCommandHelp(context.ui, command3);
15046
15971
  return 2;
15047
15972
  }
15048
15973
  context.ui.error(error instanceof Error ? error.message : "command failed");