@fml-inc/fml 0.8.0-rc.2 → 0.8.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
@@ -10,7 +10,7 @@ import {
10
10
  isPidAlive,
11
11
  readObserverStateStatus,
12
12
  removeObserverStateIfOwned
13
- } from "./chunk-WZLLO563.js";
13
+ } from "./chunk-7DWFJENY.js";
14
14
  import {
15
15
  readJsonFile,
16
16
  setHookCaptureDisabled,
@@ -24,7 +24,7 @@ import {
24
24
  isObserverHealth,
25
25
  readHookInboxStatus,
26
26
  readObserverAuthToken
27
- } from "./chunk-56TPYMNK.js";
27
+ } from "./chunk-KBT5RMAU.js";
28
28
  import "./chunk-7V7G7NVF.js";
29
29
  import {
30
30
  claudeConfigDirectory,
@@ -124,7 +124,7 @@ var POLL_INTERVAL_MS = 50;
124
124
  var LOG_ROTATE_BYTES = 10 * 1024 * 1024;
125
125
  var LOG_ROTATE_FILES = 3;
126
126
  function expectedObserverVersion() {
127
- return true ? "0.8.0-rc.2+bed6eaed" : "dev";
127
+ return true ? "0.8.0+246af775" : "dev";
128
128
  }
129
129
  function delay(ms) {
130
130
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1062,9 +1062,9 @@ async function handleDoctor(opts) {
1062
1062
  }
1063
1063
 
1064
1064
  // src/commands/install.ts
1065
- import fs9 from "fs";
1065
+ import fs10 from "fs";
1066
1066
  import os5 from "os";
1067
- import path21 from "path";
1067
+ import path22 from "path";
1068
1068
  import { fileURLToPath } from "url";
1069
1069
 
1070
1070
  // ../../packages/agent-targets/src/install/claude/index.ts
@@ -1305,21 +1305,166 @@ var CLAUDE_PLUGIN_INSTALLER = Object.freeze({
1305
1305
  })
1306
1306
  });
1307
1307
 
1308
- // src/bin-utils.ts
1308
+ // ../../packages/agent-targets/src/install/pi/index.ts
1309
1309
  import fs3 from "fs";
1310
1310
  import path4 from "path";
1311
+ import { randomUUID as randomUUID2 } from "crypto";
1312
+ var FML_PI_EXTENSION_MARKER = "__FML_PI_EXTENSION_V1__";
1313
+ var LEGACY_PI_EXTENSION_MARKER = "__panopticonPiExtensionTest";
1314
+ var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
1315
+ "PANOPTICON_PORT",
1316
+ '"/hooks"',
1317
+ "UserPromptSubmit",
1318
+ "PreToolUse",
1319
+ "PostToolUse",
1320
+ "SessionEnd"
1321
+ ]);
1322
+ function pathApiFor(platform) {
1323
+ return platform === "win32" ? path4.win32 : path4.posix;
1324
+ }
1325
+ function resolvePiAgentDir(context, pathApi) {
1326
+ const configured = context.env.PI_CODING_AGENT_DIR?.trim();
1327
+ if (!configured) {
1328
+ return pathApi.join(context.homeDir, ".pi", "agent");
1329
+ }
1330
+ if (configured === "~") return context.homeDir;
1331
+ if (configured.startsWith("~/") || configured.startsWith("~\\")) {
1332
+ return pathApi.join(context.homeDir, configured.slice(2));
1333
+ }
1334
+ return pathApi.normalize(configured);
1335
+ }
1336
+ function readFileIfPresent(filePath) {
1337
+ try {
1338
+ return fs3.readFileSync(filePath, "utf8");
1339
+ } catch (error) {
1340
+ if (error.code === "ENOENT") return null;
1341
+ throw error;
1342
+ }
1343
+ }
1344
+ function isOwnedExtension(source, legacy = false) {
1345
+ return source.includes(FML_PI_EXTENSION_MARKER) || legacy && (source.includes(LEGACY_PI_EXTENSION_MARKER) || LEGACY_PI_EXTENSION_SIGNATURES.every(
1346
+ (signature) => source.includes(signature)
1347
+ ));
1348
+ }
1349
+ function removeLegacyExtension(filePath) {
1350
+ const source = readFileIfPresent(filePath);
1351
+ if (source === null) return { removed: false, preserved: false };
1352
+ if (!isOwnedExtension(source, true)) {
1353
+ return { removed: false, preserved: true };
1354
+ }
1355
+ fs3.rmSync(filePath);
1356
+ return { removed: true, preserved: false };
1357
+ }
1358
+ function writeExtensionAtomically(filePath, source) {
1359
+ fs3.mkdirSync(path4.dirname(filePath), { recursive: true, mode: 448 });
1360
+ const temporaryPath = path4.join(
1361
+ path4.dirname(filePath),
1362
+ `.${path4.basename(filePath)}.${process.pid}.${randomUUID2()}.tmp`
1363
+ );
1364
+ try {
1365
+ fs3.writeFileSync(temporaryPath, source, { encoding: "utf8", mode: 384 });
1366
+ if (process.platform === "win32" && fs3.existsSync(filePath)) {
1367
+ fs3.rmSync(filePath);
1368
+ }
1369
+ fs3.renameSync(temporaryPath, filePath);
1370
+ } finally {
1371
+ fs3.rmSync(temporaryPath, { force: true });
1372
+ }
1373
+ }
1374
+ var PI_EXTENSION_INSTALLER = Object.freeze({
1375
+ resolvePaths(context) {
1376
+ const pathApi = pathApiFor(context.platform);
1377
+ const extensionsDir = pathApi.join(
1378
+ resolvePiAgentDir(context, pathApi),
1379
+ "extensions"
1380
+ );
1381
+ return {
1382
+ sourcePath: pathApi.join(
1383
+ context.packageRoot,
1384
+ "dist",
1385
+ "pi",
1386
+ "extension.js"
1387
+ ),
1388
+ extensionsDir,
1389
+ destinationPath: pathApi.join(extensionsDir, "fml.js"),
1390
+ legacyDestinationPath: pathApi.join(extensionsDir, "panopticon.js")
1391
+ };
1392
+ },
1393
+ install(paths) {
1394
+ const source = fs3.readFileSync(paths.sourcePath, "utf8");
1395
+ if (!source.includes(FML_PI_EXTENSION_MARKER)) {
1396
+ throw new Error(
1397
+ `Packaged Pi extension is invalid: ${paths.sourcePath} does not contain the FML ownership marker`
1398
+ );
1399
+ }
1400
+ const existing = readFileIfPresent(paths.destinationPath);
1401
+ if (existing !== null && !isOwnedExtension(existing)) {
1402
+ const legacy2 = removeLegacyExtension(paths.legacyDestinationPath);
1403
+ return {
1404
+ status: "preserved",
1405
+ destinationPath: paths.destinationPath,
1406
+ reason: "an extension not managed by FML already exists at this path",
1407
+ legacyRemoved: legacy2.removed,
1408
+ legacyPreserved: legacy2.preserved
1409
+ };
1410
+ }
1411
+ const status = existing === null ? "installed" : existing === source ? "unchanged" : "updated";
1412
+ if (status !== "unchanged") {
1413
+ writeExtensionAtomically(paths.destinationPath, source);
1414
+ }
1415
+ const legacy = removeLegacyExtension(paths.legacyDestinationPath);
1416
+ return {
1417
+ status,
1418
+ destinationPath: paths.destinationPath,
1419
+ legacyRemoved: legacy.removed,
1420
+ legacyPreserved: legacy.preserved
1421
+ };
1422
+ },
1423
+ uninstall(paths) {
1424
+ const existing = readFileIfPresent(paths.destinationPath);
1425
+ const legacy = removeLegacyExtension(paths.legacyDestinationPath);
1426
+ if (existing === null) {
1427
+ return {
1428
+ status: "absent",
1429
+ destinationPath: paths.destinationPath,
1430
+ legacyRemoved: legacy.removed,
1431
+ legacyPreserved: legacy.preserved
1432
+ };
1433
+ }
1434
+ if (!isOwnedExtension(existing)) {
1435
+ return {
1436
+ status: "preserved",
1437
+ destinationPath: paths.destinationPath,
1438
+ reason: "the existing extension is not managed by FML",
1439
+ legacyRemoved: legacy.removed,
1440
+ legacyPreserved: legacy.preserved
1441
+ };
1442
+ }
1443
+ fs3.rmSync(paths.destinationPath);
1444
+ return {
1445
+ status: "removed",
1446
+ destinationPath: paths.destinationPath,
1447
+ legacyRemoved: legacy.removed,
1448
+ legacyPreserved: legacy.preserved
1449
+ };
1450
+ }
1451
+ });
1452
+
1453
+ // src/bin-utils.ts
1454
+ import fs4 from "fs";
1455
+ import path5 from "path";
1311
1456
  import { execFileSync, execSync } from "child_process";
1312
1457
  var IS_WIN = process.platform === "win32";
1313
1458
  var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
1314
1459
  function resolveBin(name) {
1315
- const pathDirs = (process.env.PATH ?? "").split(path4.delimiter).filter(Boolean);
1316
- const hasExt = path4.extname(name) !== "";
1460
+ const pathDirs = (process.env.PATH ?? "").split(path5.delimiter).filter(Boolean);
1461
+ const hasExt = path5.extname(name) !== "";
1317
1462
  const exts = !IS_WIN || hasExt ? [""] : (process.env.PATHEXT ?? DEFAULT_PATHEXT).split(";").filter(Boolean);
1318
1463
  for (const dir of pathDirs) {
1319
1464
  for (const ext of exts) {
1320
- const candidate = path4.join(dir, name + ext);
1465
+ const candidate = path5.join(dir, name + ext);
1321
1466
  try {
1322
- if (fs3.statSync(candidate).isFile()) return candidate;
1467
+ if (fs4.statSync(candidate).isFile()) return candidate;
1323
1468
  } catch {
1324
1469
  }
1325
1470
  }
@@ -1351,18 +1496,18 @@ function execBinSync(bin, args, opts = {}) {
1351
1496
  }
1352
1497
 
1353
1498
  // src/managed-directories.ts
1354
- import fs4 from "fs";
1499
+ import fs5 from "fs";
1355
1500
  import os from "os";
1356
- import path5 from "path";
1501
+ import path6 from "path";
1357
1502
  var FML_MANAGED_DIRECTORY_MARKER = ".fml-managed-root";
1358
1503
  var FML_MANAGED_DIRECTORY_MARKER_CONTENT = "fml-managed-directory-v1\n";
1359
1504
  function comparisonPathApi(platform, targetPaths) {
1360
1505
  if (platform === "win32" && targetPaths.some(
1361
1506
  (targetPath) => /^[A-Za-z]:[\\/]/u.test(targetPath) || targetPath.startsWith("\\\\") || targetPath.startsWith("//")
1362
1507
  )) {
1363
- return path5.win32;
1508
+ return path6.win32;
1364
1509
  }
1365
- return path5;
1510
+ return path6;
1366
1511
  }
1367
1512
  function resolveComparisonPath(targetPath, platform) {
1368
1513
  return comparisonPathApi(platform, [targetPath]).resolve(targetPath);
@@ -1435,7 +1580,7 @@ function configuredManagedRoots(platform = process.platform) {
1435
1580
  );
1436
1581
  }
1437
1582
  function managedDirectoryMarker(directory) {
1438
- return path5.join(directory, FML_MANAGED_DIRECTORY_MARKER);
1583
+ return path6.join(directory, FML_MANAGED_DIRECTORY_MARKER);
1439
1584
  }
1440
1585
  function ownershipStatus(stats, platform) {
1441
1586
  if (platform === "win32") return "unverifiable";
@@ -1446,8 +1591,8 @@ function ownershipStatus(stats, platform) {
1446
1591
  function hasManagedDirectoryMarkerContents(directory) {
1447
1592
  const marker = managedDirectoryMarker(directory);
1448
1593
  try {
1449
- const stats = fs4.lstatSync(marker);
1450
- return stats.isFile() && fs4.readFileSync(marker, "utf8") === FML_MANAGED_DIRECTORY_MARKER_CONTENT;
1594
+ const stats = fs5.lstatSync(marker);
1595
+ return stats.isFile() && fs5.readFileSync(marker, "utf8") === FML_MANAGED_DIRECTORY_MARKER_CONTENT;
1451
1596
  } catch {
1452
1597
  return false;
1453
1598
  }
@@ -1455,11 +1600,11 @@ function hasManagedDirectoryMarkerContents(directory) {
1455
1600
  function hasManagedDirectoryMarker(directory, platform = process.platform) {
1456
1601
  const marker = managedDirectoryMarker(directory);
1457
1602
  try {
1458
- const stats = fs4.lstatSync(marker);
1603
+ const stats = fs5.lstatSync(marker);
1459
1604
  if (!stats.isFile() || ownershipStatus(stats, platform) !== "owned") {
1460
1605
  return false;
1461
1606
  }
1462
- return fs4.readFileSync(marker, "utf8") === FML_MANAGED_DIRECTORY_MARKER_CONTENT;
1607
+ return fs5.readFileSync(marker, "utf8") === FML_MANAGED_DIRECTORY_MARKER_CONTENT;
1463
1608
  } catch {
1464
1609
  return false;
1465
1610
  }
@@ -1467,7 +1612,7 @@ function hasManagedDirectoryMarker(directory, platform = process.platform) {
1467
1612
  function writeManagedDirectoryMarker(directory, platform) {
1468
1613
  const marker = managedDirectoryMarker(directory);
1469
1614
  try {
1470
- fs4.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
1615
+ fs5.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
1471
1616
  encoding: "utf8",
1472
1617
  flag: "wx",
1473
1618
  mode: 384
@@ -1492,23 +1637,23 @@ function prepareManagedDirectory(root, platform = process.platform) {
1492
1637
  let created = false;
1493
1638
  let existingStats = null;
1494
1639
  try {
1495
- existingStats = fs4.lstatSync(root.directory);
1640
+ existingStats = fs5.lstatSync(root.directory);
1496
1641
  } catch (error) {
1497
1642
  if (error.code !== "ENOENT") throw error;
1498
1643
  existed = false;
1499
1644
  }
1500
1645
  if (!existed) {
1501
- fs4.mkdirSync(path5.dirname(root.directory), {
1646
+ fs5.mkdirSync(path6.dirname(root.directory), {
1502
1647
  recursive: true,
1503
1648
  mode: 448
1504
1649
  });
1505
1650
  try {
1506
- fs4.mkdirSync(root.directory, { mode: 448 });
1651
+ fs5.mkdirSync(root.directory, { mode: 448 });
1507
1652
  created = true;
1508
1653
  } catch (error) {
1509
1654
  if (error.code !== "EEXIST") throw error;
1510
1655
  existed = true;
1511
- existingStats = fs4.lstatSync(root.directory);
1656
+ existingStats = fs5.lstatSync(root.directory);
1512
1657
  }
1513
1658
  }
1514
1659
  if (hasSymlinkedParentBelowTrustedAnchor(root.directory, platform)) {
@@ -1518,7 +1663,7 @@ function prepareManagedDirectory(root, platform = process.platform) {
1518
1663
  markerCreated: false
1519
1664
  };
1520
1665
  }
1521
- const directoryStats = existingStats ?? fs4.lstatSync(root.directory);
1666
+ const directoryStats = existingStats ?? fs5.lstatSync(root.directory);
1522
1667
  const ownership = ownershipStatus(directoryStats, platform);
1523
1668
  const recognizedWindowsMarker = platform === "win32" && hasManagedDirectoryMarkerContents(root.directory);
1524
1669
  if (directoryStats.isSymbolicLink() || !directoryStats.isDirectory() || ownership === "foreign" || !root.nativeDefault && ownership !== "owned" && !(created && ownership === "unverifiable") && !recognizedWindowsMarker) {
@@ -1542,7 +1687,7 @@ function prepareManagedDirectory(root, platform = process.platform) {
1542
1687
  markerCreated: false
1543
1688
  };
1544
1689
  }
1545
- const mayClaim = fs4.readdirSync(root.directory).length === 0;
1690
+ const mayClaim = fs5.readdirSync(root.directory).length === 0;
1546
1691
  if (!mayClaim) {
1547
1692
  return {
1548
1693
  directory: root.directory,
@@ -1601,7 +1746,7 @@ function hasSymlinkedParentBelowTrustedAnchor(targetPath, platform = process.pla
1601
1746
  for (const component of relativeParent.split(pathApi.sep)) {
1602
1747
  current = pathApi.join(current, component);
1603
1748
  try {
1604
- if (fs4.lstatSync(current).isSymbolicLink()) return true;
1749
+ if (fs5.lstatSync(current).isSymbolicLink()) return true;
1605
1750
  } catch (error) {
1606
1751
  if (error.code === "ENOENT") return false;
1607
1752
  return true;
@@ -1626,7 +1771,7 @@ function unsafePurgeRoots(roots, platform = process.platform) {
1626
1771
  }
1627
1772
  let stats;
1628
1773
  try {
1629
- stats = fs4.lstatSync(root.directory);
1774
+ stats = fs5.lstatSync(root.directory);
1630
1775
  } catch (error) {
1631
1776
  if (error.code === "ENOENT") continue;
1632
1777
  unsafe.push({
@@ -1675,14 +1820,14 @@ function unsafePurgeRoots(roots, platform = process.platform) {
1675
1820
  }
1676
1821
 
1677
1822
  // src/commands/hook-config.ts
1678
- import fs5 from "fs";
1823
+ import fs6 from "fs";
1679
1824
  import os2 from "os";
1680
- import path10 from "path";
1825
+ import path11 from "path";
1681
1826
 
1682
1827
  // ../../packages/agent-targets/src/install/hooks/codex.ts
1683
1828
  import { createHash } from "crypto";
1684
1829
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
1685
- import path7 from "path";
1830
+ import path8 from "path";
1686
1831
  import { parse as parse2 } from "smol-toml";
1687
1832
 
1688
1833
  // ../../packages/agent-targets/src/toml.ts
@@ -1692,7 +1837,7 @@ function asRecord2(value) {
1692
1837
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1693
1838
  }
1694
1839
  function singlePath(value) {
1695
- const path29 = [];
1840
+ const path30 = [];
1696
1841
  let current = value;
1697
1842
  while (true) {
1698
1843
  if (Array.isArray(current) && current.length === 1) {
@@ -1700,13 +1845,13 @@ function singlePath(value) {
1700
1845
  continue;
1701
1846
  }
1702
1847
  const record = asRecord2(current);
1703
- if (!record) return path29;
1848
+ if (!record) return path30;
1704
1849
  const keys = Object.keys(record);
1705
- if (keys.length === 0) return path29;
1850
+ if (keys.length === 0) return path30;
1706
1851
  if (keys.length !== 1) throw new Error("ambiguous TOML statement path");
1707
1852
  const key = keys[0];
1708
1853
  if (key === void 0) throw new Error("missing TOML statement key");
1709
- path29.push(key);
1854
+ path30.push(key);
1710
1855
  current = record[key];
1711
1856
  }
1712
1857
  }
@@ -1788,8 +1933,8 @@ function scanStatements(source) {
1788
1933
  }
1789
1934
  return statements;
1790
1935
  }
1791
- function startsWithPath(path29, prefix) {
1792
- return prefix.every((part, index) => path29[index] === part);
1936
+ function startsWithPath(path30, prefix) {
1937
+ return prefix.every((part, index) => path30[index] === part);
1793
1938
  }
1794
1939
  function pathsEqual(left, right) {
1795
1940
  return left.length === right.length && left.every((part, index) => part === right[index]);
@@ -1815,9 +1960,9 @@ function appendToml(source, addition) {
1815
1960
  function hasOwn(record, key) {
1816
1961
  return Object.prototype.hasOwnProperty.call(record, key);
1817
1962
  }
1818
- function updatedValueAtPath(updated, path29) {
1963
+ function updatedValueAtPath(updated, path30) {
1819
1964
  let current = updated;
1820
- for (const part of path29) {
1965
+ for (const part of path30) {
1821
1966
  const record = asRecord2(current);
1822
1967
  if (!record || !hasOwn(record, part)) return { exists: false };
1823
1968
  current = record[part];
@@ -1831,22 +1976,22 @@ function normalizedPaths(updates) {
1831
1976
  throw new Error("TOML update path cannot be empty");
1832
1977
  }
1833
1978
  if (paths.some(
1834
- (path29) => pathsEqual(path29, update.path) || startsWithPath(update.path, path29)
1979
+ (path30) => pathsEqual(path30, update.path) || startsWithPath(update.path, path30)
1835
1980
  )) {
1836
1981
  continue;
1837
1982
  }
1838
1983
  for (let index = paths.length - 1; index >= 0; index -= 1) {
1839
- const path29 = paths[index];
1840
- if (path29 && startsWithPath(path29, update.path)) paths.splice(index, 1);
1984
+ const path30 = paths[index];
1985
+ if (path30 && startsWithPath(path30, update.path)) paths.splice(index, 1);
1841
1986
  }
1842
1987
  paths.push([...update.path]);
1843
1988
  }
1844
1989
  return paths;
1845
1990
  }
1846
- function valueDocument(path29, value) {
1991
+ function valueDocument(path30, value) {
1847
1992
  let nested = value;
1848
- for (let index = path29.length - 1; index >= 0; index -= 1) {
1849
- const part = path29[index];
1993
+ for (let index = path30.length - 1; index >= 0; index -= 1) {
1994
+ const part = path30[index];
1850
1995
  if (part === void 0) throw new Error("TOML update path is invalid");
1851
1996
  nested = { [part]: nested };
1852
1997
  }
@@ -1865,12 +2010,12 @@ function insertAfterStatement(source, statement, addition) {
1865
2010
  const separator = before.endsWith(eol) ? "" : eol;
1866
2011
  return `${before}${separator}${normalized}${eol}${source.slice(statement.end)}`;
1867
2012
  }
1868
- function insertUpdatedValue(source, path29, value) {
1869
- const key = path29.at(-1);
2013
+ function insertUpdatedValue(source, path30, value) {
2014
+ const key = path30.at(-1);
1870
2015
  if (key === void 0) throw new Error("TOML update path cannot be empty");
1871
2016
  const assignment = localAssignment(key, value);
1872
2017
  if (assignment !== null) {
1873
- const parent = path29.slice(0, -1);
2018
+ const parent = path30.slice(0, -1);
1874
2019
  const parentTables = scanStatements(source).filter(
1875
2020
  (statement) => statement.isTable && pathsEqual(statement.path, parent)
1876
2021
  );
@@ -1884,17 +2029,17 @@ function insertUpdatedValue(source, path29, value) {
1884
2029
  return insertAfterStatement(source, parentTable, assignment);
1885
2030
  }
1886
2031
  }
1887
- return appendToml(source, stringify(valueDocument(path29, value)));
2032
+ return appendToml(source, stringify(valueDocument(path30, value)));
1888
2033
  }
1889
2034
  function updateTomlPaths(source, updated, updates) {
1890
2035
  const paths = normalizedPaths(updates);
1891
2036
  if (paths.length === 0) return source;
1892
2037
  const stripped = removeStatements(source, scanStatements(source), paths);
1893
2038
  let result = stripped;
1894
- for (const path29 of paths) {
1895
- const next = updatedValueAtPath(updated, path29);
2039
+ for (const path30 of paths) {
2040
+ const next = updatedValueAtPath(updated, path30);
1896
2041
  if (!next.exists) continue;
1897
- result = insertUpdatedValue(result, path29, next.value);
2042
+ result = insertUpdatedValue(result, path30, next.value);
1898
2043
  }
1899
2044
  let reparsed;
1900
2045
  try {
@@ -1913,7 +2058,7 @@ function updateTomlPaths(source, updated, updates) {
1913
2058
  }
1914
2059
 
1915
2060
  // ../../packages/agent-targets/src/install/hooks/shared.ts
1916
- import path6 from "path";
2061
+ import path7 from "path";
1917
2062
  function asRecord3(value) {
1918
2063
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1919
2064
  }
@@ -1973,7 +2118,7 @@ function changedMutation(snapshot, after) {
1973
2118
  return before === after ? null : { path: snapshot.path, before, after };
1974
2119
  }
1975
2120
  function fmlHookCommand(pluginRoot, target2) {
1976
- const handlerPath = path6.join(pluginRoot, "bin", "hook-handler");
2121
+ const handlerPath = path7.join(pluginRoot, "bin", "hook-handler");
1977
2122
  const encodedPath = Buffer.from(handlerPath, "utf8").toString("base64url");
1978
2123
  return `node -e "${FML_HOOK_LOADER}" ${encodedPath} ${target2}`;
1979
2124
  }
@@ -2086,11 +2231,11 @@ function uniquePaths(paths) {
2086
2231
  function codexPaths(context) {
2087
2232
  const configuredHome = context.env.CODEX_HOME;
2088
2233
  const trimmedHome = typeof configuredHome === "string" ? configuredHome.trim() : "";
2089
- const dir = path7.resolve(
2090
- trimmedHome.length > 0 ? trimmedHome : path7.join(context.homeDir, ".codex")
2234
+ const dir = path8.resolve(
2235
+ trimmedHome.length > 0 ? trimmedHome : path8.join(context.homeDir, ".codex")
2091
2236
  );
2092
- const configPath4 = path7.join(dir, "config.toml");
2093
- const hooksPath = path7.join(dir, "hooks.json");
2237
+ const configPath4 = path8.join(dir, "config.toml");
2238
+ const hooksPath = path8.join(dir, "hooks.json");
2094
2239
  return {
2095
2240
  configPath: configPath4,
2096
2241
  hooksPath,
@@ -2522,7 +2667,7 @@ function codexConfigUpdatePaths(original, updated) {
2522
2667
  }
2523
2668
  }
2524
2669
  }
2525
- return paths.map((path29) => ({ path: path29 }));
2670
+ return paths.map((path30) => ({ path: path30 }));
2526
2671
  }
2527
2672
  function updateCodexConfigToml(source, original, updated) {
2528
2673
  return updateTomlPaths(
@@ -2972,7 +3117,7 @@ var CODEX_HOOK_INSTALLER = Object.freeze({
2972
3117
 
2973
3118
  // ../../packages/agent-targets/src/install/hooks/gemini.ts
2974
3119
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
2975
- import path8 from "path";
3120
+ import path9 from "path";
2976
3121
 
2977
3122
  // ../../packages/agent-targets/src/mcp/jsonc.ts
2978
3123
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
@@ -3028,7 +3173,7 @@ function longestCommonArraySubsequence(original, updated) {
3028
3173
  }
3029
3174
  return matches;
3030
3175
  }
3031
- function arrayDiff(original, updated, path29) {
3176
+ function arrayDiff(original, updated, path30) {
3032
3177
  const updates = [];
3033
3178
  const matches = longestCommonArraySubsequence(original, updated);
3034
3179
  let originalStart = 0;
@@ -3046,19 +3191,19 @@ function arrayDiff(original, updated, path29) {
3046
3191
  ...jsoncUpdatesForObjectDiff(
3047
3192
  original[originalStart + offset],
3048
3193
  updated[updatedStart + offset],
3049
- [...path29, currentIndex + offset]
3194
+ [...path30, currentIndex + offset]
3050
3195
  )
3051
3196
  );
3052
3197
  }
3053
3198
  for (let offset = pairedCount; offset < originalCount; offset += 1) {
3054
3199
  updates.push({
3055
- path: [...path29, currentIndex + pairedCount],
3200
+ path: [...path30, currentIndex + pairedCount],
3056
3201
  value: void 0
3057
3202
  });
3058
3203
  }
3059
3204
  for (let offset = pairedCount; offset < updatedCount; offset += 1) {
3060
3205
  updates.push({
3061
- path: [...path29, currentIndex + offset],
3206
+ path: [...path30, currentIndex + offset],
3062
3207
  value: updated[updatedStart + offset],
3063
3208
  isArrayInsertion: true
3064
3209
  });
@@ -3071,21 +3216,21 @@ function arrayDiff(original, updated, path29) {
3071
3216
  }
3072
3217
  return updates;
3073
3218
  }
3074
- function jsoncUpdatesForObjectDiff(original, updated, path29 = []) {
3219
+ function jsoncUpdatesForObjectDiff(original, updated, path30 = []) {
3075
3220
  if (isDeepStrictEqual3(original, updated)) return [];
3076
3221
  if (Array.isArray(original) && Array.isArray(updated)) {
3077
- return arrayDiff(original, updated, path29);
3222
+ return arrayDiff(original, updated, path30);
3078
3223
  }
3079
3224
  const originalObject = asObject(original);
3080
3225
  const updatedObject = asObject(updated);
3081
- if (!originalObject || !updatedObject) return [{ path: path29, value: updated }];
3226
+ if (!originalObject || !updatedObject) return [{ path: path30, value: updated }];
3082
3227
  const keys = /* @__PURE__ */ new Set([
3083
3228
  ...Object.keys(originalObject),
3084
3229
  ...Object.keys(updatedObject)
3085
3230
  ]);
3086
3231
  return [...keys].flatMap(
3087
3232
  (key) => jsoncUpdatesForObjectDiff(originalObject[key], updatedObject[key], [
3088
- ...path29,
3233
+ ...path30,
3089
3234
  key
3090
3235
  ])
3091
3236
  );
@@ -3133,7 +3278,7 @@ var TARGET_ID3 = TARGET3.id;
3133
3278
  var DISPLAY_NAME3 = TARGET3.displayName;
3134
3279
  var MANAGED_HOOK_NAME = "fml-capture";
3135
3280
  function settingsPath(context) {
3136
- return path8.join(geminiConfigDirectory(context), "settings.json");
3281
+ return path9.join(geminiConfigDirectory(context), "settings.json");
3137
3282
  }
3138
3283
  function readGeminiSettings(snapshot) {
3139
3284
  const failure = snapshotError(snapshot);
@@ -3366,15 +3511,15 @@ var GEMINI_HOOK_INSTALLER = Object.freeze({
3366
3511
  });
3367
3512
 
3368
3513
  // ../../packages/agent-targets/src/install/hooks/grok.ts
3369
- import path9 from "path";
3514
+ import path10 from "path";
3370
3515
  var TARGET4 = agentTarget("grok");
3371
3516
  var TARGET_ID4 = TARGET4.id;
3372
3517
  var DISPLAY_NAME4 = TARGET4.displayName;
3373
3518
  var HOOK_TIMEOUT_SECONDS2 = 10;
3374
3519
  function grokHooksPath(context) {
3375
3520
  const configuredHome = context.env.GROK_HOME?.trim();
3376
- const grokHome = configuredHome ? path9.resolve(configuredHome) : path9.join(context.homeDir, ".grok");
3377
- return path9.join(grokHome, "hooks", "fml.json");
3521
+ const grokHome = configuredHome ? path10.resolve(configuredHome) : path10.join(context.homeDir, ".grok");
3522
+ return path10.join(grokHome, "hooks", "fml.json");
3378
3523
  }
3379
3524
  function planInstall3(context) {
3380
3525
  const configPath4 = grokHooksPath(context);
@@ -3493,14 +3638,14 @@ var HOOK_REGISTRATION_CAPABILITIES = Object.freeze([
3493
3638
  ]);
3494
3639
 
3495
3640
  // src/commands/hook-config.ts
3496
- var DEFAULT_CODEX_HOOK_OWNERSHIP_STATE_PATH = path10.join(
3641
+ var DEFAULT_CODEX_HOOK_OWNERSHIP_STATE_PATH = path11.join(
3497
3642
  FML_DATA_DIR,
3498
3643
  "codex-hook-install.json"
3499
3644
  );
3500
3645
  function readHookFile(filePath) {
3501
3646
  let stat;
3502
3647
  try {
3503
- stat = fs5.lstatSync(filePath);
3648
+ stat = fs6.lstatSync(filePath);
3504
3649
  } catch (error) {
3505
3650
  if (error.code === "ENOENT") {
3506
3651
  return { path: filePath, kind: "missing" };
@@ -3529,7 +3674,7 @@ function readHookFile(filePath) {
3529
3674
  return {
3530
3675
  path: filePath,
3531
3676
  kind: "file",
3532
- contents: fs5.readFileSync(filePath, "utf8")
3677
+ contents: fs6.readFileSync(filePath, "utf8")
3533
3678
  };
3534
3679
  } catch (error) {
3535
3680
  return {
@@ -3541,17 +3686,17 @@ function readHookFile(filePath) {
3541
3686
  }
3542
3687
  function canonicalizePath(filePath) {
3543
3688
  try {
3544
- return fs5.realpathSync(filePath);
3689
+ return fs6.realpathSync(filePath);
3545
3690
  } catch {
3546
3691
  const missingSegments = [];
3547
3692
  let ancestor = filePath;
3548
3693
  while (true) {
3549
- const parent = path10.dirname(ancestor);
3694
+ const parent = path11.dirname(ancestor);
3550
3695
  if (parent === ancestor) return filePath;
3551
- missingSegments.unshift(path10.basename(ancestor));
3696
+ missingSegments.unshift(path11.basename(ancestor));
3552
3697
  ancestor = parent;
3553
3698
  try {
3554
- return path10.join(fs5.realpathSync(ancestor), ...missingSegments);
3699
+ return path11.join(fs6.realpathSync(ancestor), ...missingSegments);
3555
3700
  } catch {
3556
3701
  }
3557
3702
  }
@@ -3570,7 +3715,7 @@ function assertMutationPrecondition(mutation) {
3570
3715
  }
3571
3716
  function applyMutation(mutation) {
3572
3717
  if (mutation.after === null) {
3573
- fs5.rmSync(mutation.path);
3718
+ fs6.rmSync(mutation.path);
3574
3719
  } else {
3575
3720
  writeTextFileAtomic(mutation.path, mutation.after);
3576
3721
  }
@@ -3583,7 +3728,7 @@ function rollbackMutation(mutation) {
3583
3728
  };
3584
3729
  assertMutationPrecondition(inverse);
3585
3730
  if (inverse.after === null) {
3586
- fs5.rmSync(inverse.path);
3731
+ fs6.rmSync(inverse.path);
3587
3732
  } else {
3588
3733
  writeTextFileAtomic(inverse.path, inverse.after);
3589
3734
  }
@@ -3651,7 +3796,7 @@ function applyPlan(plan, warn, onFailure) {
3651
3796
  };
3652
3797
  }
3653
3798
  function plannerContext(packageRoot, options) {
3654
- const ownershipStatePath = options.ownershipStatePath ?? (options.homeDir ? path10.join(options.homeDir, ".fml", "codex-hook-install.json") : DEFAULT_CODEX_HOOK_OWNERSHIP_STATE_PATH);
3799
+ const ownershipStatePath = options.ownershipStatePath ?? (options.homeDir ? path11.join(options.homeDir, ".fml", "codex-hook-install.json") : DEFAULT_CODEX_HOOK_OWNERSHIP_STATE_PATH);
3655
3800
  return {
3656
3801
  packageRoot,
3657
3802
  homeDir: options.homeDir ?? os2.homedir(),
@@ -3682,10 +3827,10 @@ function removeFmlHooks(options = {}) {
3682
3827
  }
3683
3828
 
3684
3829
  // src/commands/marketplace-link.ts
3685
- import fs6 from "fs";
3830
+ import fs7 from "fs";
3686
3831
  function existingPathKind(linkPath) {
3687
3832
  try {
3688
- return fs6.lstatSync(linkPath).isSymbolicLink() ? "symlink" : "other";
3833
+ return fs7.lstatSync(linkPath).isSymbolicLink() ? "symlink" : "other";
3689
3834
  } catch (error) {
3690
3835
  if (error.code === "ENOENT") return "missing";
3691
3836
  throw error;
@@ -3694,15 +3839,15 @@ function existingPathKind(linkPath) {
3694
3839
  function replaceMarketplaceLink(linkPath, targetPath, type) {
3695
3840
  const existing = existingPathKind(linkPath);
3696
3841
  if (existing === "other") return "preserved";
3697
- if (existing === "symlink") fs6.unlinkSync(linkPath);
3698
- fs6.symlinkSync(targetPath, linkPath, type);
3842
+ if (existing === "symlink") fs7.unlinkSync(linkPath);
3843
+ fs7.symlinkSync(targetPath, linkPath, type);
3699
3844
  return existing === "symlink" ? "replaced" : "created";
3700
3845
  }
3701
3846
  function removeMarketplaceLink(linkPath) {
3702
3847
  const existing = existingPathKind(linkPath);
3703
3848
  if (existing === "missing") return "missing";
3704
3849
  if (existing === "other") return "preserved";
3705
- fs6.unlinkSync(linkPath);
3850
+ fs7.unlinkSync(linkPath);
3706
3851
  return "removed";
3707
3852
  }
3708
3853
 
@@ -3797,10 +3942,10 @@ function configRecord(value) {
3797
3942
  }
3798
3943
 
3799
3944
  // ../../packages/agent-targets/src/mcp/targets/claude-desktop.ts
3800
- import path12 from "path";
3945
+ import path13 from "path";
3801
3946
 
3802
3947
  // ../../packages/agent-targets/src/mcp/capability.ts
3803
- import path11 from "path";
3948
+ import path12 from "path";
3804
3949
  import { parse as parse4 } from "smol-toml";
3805
3950
 
3806
3951
  // ../../packages/agent-targets/src/mcp/toml.ts
@@ -3906,7 +4051,7 @@ function changedConfigPlan(target2, current, before, data) {
3906
4051
  function generatedServer(target2, context) {
3907
4052
  return fmlMcpServerConfig(
3908
4053
  target2.command,
3909
- path11.join(context.packageRoot, "bin", "mcp-server")
4054
+ path12.join(context.packageRoot, "bin", "mcp-server")
3910
4055
  );
3911
4056
  }
3912
4057
  function planInstall4(target2, context) {
@@ -3986,7 +4131,7 @@ var CLAUDE_DESKTOP_DIRECT_MCP = createDirectMcpCapability({
3986
4131
  configKey: "mcpServers",
3987
4132
  configPath: ({ env, homeDir, platform }) => {
3988
4133
  if (platform === "darwin") {
3989
- return path12.join(
4134
+ return path13.join(
3990
4135
  homeDir,
3991
4136
  "Library",
3992
4137
  "Application Support",
@@ -3995,14 +4140,14 @@ var CLAUDE_DESKTOP_DIRECT_MCP = createDirectMcpCapability({
3995
4140
  );
3996
4141
  }
3997
4142
  if (platform === "win32") {
3998
- return path12.join(
3999
- env.APPDATA ?? path12.join(homeDir, "AppData", "Roaming"),
4143
+ return path13.join(
4144
+ env.APPDATA ?? path13.join(homeDir, "AppData", "Roaming"),
4000
4145
  "Claude",
4001
4146
  "claude_desktop_config.json"
4002
4147
  );
4003
4148
  }
4004
- return path12.join(
4005
- env.XDG_CONFIG_HOME ?? path12.join(homeDir, ".config"),
4149
+ return path13.join(
4150
+ env.XDG_CONFIG_HOME ?? path13.join(homeDir, ".config"),
4006
4151
  "Claude",
4007
4152
  "claude_desktop_config.json"
4008
4153
  );
@@ -4011,7 +4156,7 @@ var CLAUDE_DESKTOP_DIRECT_MCP = createDirectMcpCapability({
4011
4156
  });
4012
4157
 
4013
4158
  // ../../packages/agent-targets/src/mcp/targets/codex.ts
4014
- import path13 from "path";
4159
+ import path14 from "path";
4015
4160
  var TARGET6 = agentTarget("codex");
4016
4161
  var CODEX_DIRECT_MCP = createDirectMcpCapability({
4017
4162
  targetId: TARGET6.id,
@@ -4020,26 +4165,26 @@ var CODEX_DIRECT_MCP = createDirectMcpCapability({
4020
4165
  configKey: "mcp_servers",
4021
4166
  configPath: ({ env, homeDir }) => {
4022
4167
  const configuredHome = env.CODEX_HOME;
4023
- const codexHome = typeof configuredHome === "string" && configuredHome.length > 0 ? path13.resolve(configuredHome) : path13.join(homeDir, ".codex");
4024
- return path13.join(codexHome, "config.toml");
4168
+ const codexHome = typeof configuredHome === "string" && configuredHome.length > 0 ? path14.resolve(configuredHome) : path14.join(homeDir, ".codex");
4169
+ return path14.join(codexHome, "config.toml");
4025
4170
  },
4026
4171
  command: () => "node"
4027
4172
  });
4028
4173
 
4029
4174
  // ../../packages/agent-targets/src/mcp/targets/gemini.ts
4030
- import path14 from "path";
4175
+ import path15 from "path";
4031
4176
  var TARGET7 = agentTarget("gemini");
4032
4177
  var GEMINI_DIRECT_MCP = createDirectMcpCapability({
4033
4178
  targetId: TARGET7.id,
4034
4179
  displayName: TARGET7.displayName,
4035
4180
  configFormat: "json",
4036
4181
  configKey: "mcpServers",
4037
- configPath: (context) => path14.join(geminiConfigDirectory(context), "settings.json"),
4182
+ configPath: (context) => path15.join(geminiConfigDirectory(context), "settings.json"),
4038
4183
  command: () => "node"
4039
4184
  });
4040
4185
 
4041
4186
  // ../../packages/agent-targets/src/mcp/targets/grok.ts
4042
- import path15 from "path";
4187
+ import path16 from "path";
4043
4188
  var TARGET8 = agentTarget("grok");
4044
4189
  var GROK_DIRECT_MCP = createDirectMcpCapability({
4045
4190
  targetId: TARGET8.id,
@@ -4048,8 +4193,8 @@ var GROK_DIRECT_MCP = createDirectMcpCapability({
4048
4193
  configKey: "mcp_servers",
4049
4194
  configPath: ({ env, homeDir }) => {
4050
4195
  const configuredHome = env.GROK_HOME;
4051
- const grokHome = typeof configuredHome === "string" && configuredHome.length > 0 ? path15.resolve(configuredHome) : path15.join(homeDir, ".grok");
4052
- return path15.join(grokHome, "config.toml");
4196
+ const grokHome = typeof configuredHome === "string" && configuredHome.length > 0 ? path16.resolve(configuredHome) : path16.join(homeDir, ".grok");
4197
+ return path16.join(grokHome, "config.toml");
4053
4198
  },
4054
4199
  command: () => "node"
4055
4200
  });
@@ -4063,7 +4208,7 @@ var DIRECT_MCP_CAPABILITIES = Object.freeze([
4063
4208
  ]);
4064
4209
 
4065
4210
  // src/commands/mcp-config.ts
4066
- import fs7 from "fs";
4211
+ import fs8 from "fs";
4067
4212
  import os3 from "os";
4068
4213
  function resolutionContext(options) {
4069
4214
  return {
@@ -4076,7 +4221,7 @@ function resolutionContext(options) {
4076
4221
  function readMcpFile(filePath) {
4077
4222
  let stat;
4078
4223
  try {
4079
- stat = fs7.lstatSync(filePath);
4224
+ stat = fs8.lstatSync(filePath);
4080
4225
  } catch (error) {
4081
4226
  if (error.code === "ENOENT") {
4082
4227
  return { path: filePath, kind: "missing" };
@@ -4105,7 +4250,7 @@ function readMcpFile(filePath) {
4105
4250
  return {
4106
4251
  path: filePath,
4107
4252
  kind: "file",
4108
- contents: fs7.readFileSync(filePath, "utf8")
4253
+ contents: fs8.readFileSync(filePath, "utf8")
4109
4254
  };
4110
4255
  } catch (error) {
4111
4256
  return {
@@ -4141,14 +4286,14 @@ function preflightMutation(mutation) {
4141
4286
  }
4142
4287
  function writeMutation(mutation) {
4143
4288
  if (mutation.after === null) {
4144
- fs7.rmSync(mutation.path, { force: true });
4289
+ fs8.rmSync(mutation.path, { force: true });
4145
4290
  return;
4146
4291
  }
4147
4292
  writeTextFileAtomic(mutation.path, mutation.after);
4148
4293
  }
4149
4294
  function rollbackMutation2(mutation) {
4150
4295
  if (mutation.before === null) {
4151
- fs7.rmSync(mutation.path, { force: true });
4296
+ fs8.rmSync(mutation.path, { force: true });
4152
4297
  return;
4153
4298
  }
4154
4299
  writeTextFileAtomic(mutation.path, mutation.before);
@@ -4238,13 +4383,13 @@ function removeFmlDirectMcp(target2 = "all", options = {}) {
4238
4383
  }
4239
4384
 
4240
4385
  // src/commands/agent-skills.ts
4241
- import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
4242
- import fs8 from "fs";
4386
+ import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
4387
+ import fs9 from "fs";
4243
4388
  import os4 from "os";
4244
- import path20 from "path";
4389
+ import path21 from "path";
4245
4390
 
4246
4391
  // ../../packages/agent-targets/src/install/surfaces/capability.ts
4247
- import path16 from "path";
4392
+ import path17 from "path";
4248
4393
  var SOURCE_PATH_SEGMENTS = ["agent-assets", "fml", "SKILL.md"];
4249
4394
  var DESTINATION_PATH_SEGMENTS = ["skills", "fml", "SKILL.md"];
4250
4395
  function createAgentSkillCapability(definition) {
@@ -4257,8 +4402,8 @@ function createAgentSkillCapability(definition) {
4257
4402
  resolve: (context) => Object.freeze({
4258
4403
  targetId: definition.targetId,
4259
4404
  displayName: definition.displayName,
4260
- sourcePath: path16.join(context.packageRoot, ...SOURCE_PATH_SEGMENTS),
4261
- destinationPath: path16.join(
4405
+ sourcePath: path17.join(context.packageRoot, ...SOURCE_PATH_SEGMENTS),
4406
+ destinationPath: path17.join(
4262
4407
  definition.destinationRoot(context),
4263
4408
  ...DESTINATION_PATH_SEGMENTS
4264
4409
  ),
@@ -4281,7 +4426,7 @@ var CLAUDE_AGENT_SKILL = createAgentSkillCapability({
4281
4426
  });
4282
4427
 
4283
4428
  // ../../packages/agent-targets/src/install/surfaces/targets/codex.ts
4284
- import path17 from "path";
4429
+ import path18 from "path";
4285
4430
  var TARGET10 = agentTarget("codex");
4286
4431
  var CODEX_AGENT_SKILL = createAgentSkillCapability({
4287
4432
  targetId: TARGET10.id,
@@ -4289,11 +4434,11 @@ var CODEX_AGENT_SKILL = createAgentSkillCapability({
4289
4434
  invocation: "$fml",
4290
4435
  deliveryMode: "instruction-read",
4291
4436
  argumentMode: "user-input",
4292
- destinationRoot: ({ homeDir }) => path17.join(homeDir, ".agents")
4437
+ destinationRoot: ({ homeDir }) => path18.join(homeDir, ".agents")
4293
4438
  });
4294
4439
 
4295
4440
  // ../../packages/agent-targets/src/install/surfaces/targets/gemini.ts
4296
- import path18 from "path";
4441
+ import path19 from "path";
4297
4442
  var TARGET11 = agentTarget("gemini");
4298
4443
  var GEMINI_AGENT_SKILL = createAgentSkillCapability({
4299
4444
  targetId: TARGET11.id,
@@ -4303,13 +4448,13 @@ var GEMINI_AGENT_SKILL = createAgentSkillCapability({
4303
4448
  argumentMode: "post-submit-prompt",
4304
4449
  destinationRoot: ({ env, homeDir }) => {
4305
4450
  const configuredHome = env.GEMINI_CLI_HOME?.trim();
4306
- const homeRoot = configuredHome ? path18.resolve(configuredHome) : homeDir;
4307
- return path18.join(homeRoot, ".agents");
4451
+ const homeRoot = configuredHome ? path19.resolve(configuredHome) : homeDir;
4452
+ return path19.join(homeRoot, ".agents");
4308
4453
  }
4309
4454
  });
4310
4455
 
4311
4456
  // ../../packages/agent-targets/src/install/surfaces/targets/grok.ts
4312
- import path19 from "path";
4457
+ import path20 from "path";
4313
4458
  var TARGET12 = agentTarget("grok");
4314
4459
  var GROK_AGENT_SKILL = createAgentSkillCapability({
4315
4460
  targetId: TARGET12.id,
@@ -4317,7 +4462,7 @@ var GROK_AGENT_SKILL = createAgentSkillCapability({
4317
4462
  invocation: "/fml",
4318
4463
  deliveryMode: "prompt-expansion",
4319
4464
  argumentMode: "template-or-append",
4320
- destinationRoot: ({ homeDir }) => path19.join(homeDir, ".agents")
4465
+ destinationRoot: ({ homeDir }) => path20.join(homeDir, ".agents")
4321
4466
  });
4322
4467
 
4323
4468
  // ../../packages/agent-targets/src/install/surfaces/registry.ts
@@ -4331,7 +4476,7 @@ var AGENT_SKILL_CAPABILITIES = Object.freeze([
4331
4476
  // src/commands/agent-skills.ts
4332
4477
  var FML_AGENT_SKILL_MARKER = "<!-- fml-managed-agent-surface:v1 -->";
4333
4478
  var AGENT_SKILL_STATE_SCHEMA_VERSION = 1;
4334
- var DEFAULT_STATE_PATH = path20.join(FML_DATA_DIR, "agent-skills.json");
4479
+ var DEFAULT_STATE_PATH = path21.join(FML_DATA_DIR, "agent-skills.json");
4335
4480
  function emptyState() {
4336
4481
  return Object.freeze({
4337
4482
  schemaVersion: AGENT_SKILL_STATE_SCHEMA_VERSION,
@@ -4344,7 +4489,7 @@ function sha256(contents) {
4344
4489
  function readSkillFile(filePath) {
4345
4490
  let stat;
4346
4491
  try {
4347
- stat = fs8.lstatSync(filePath);
4492
+ stat = fs9.lstatSync(filePath);
4348
4493
  } catch (error) {
4349
4494
  if (error.code === "ENOENT") {
4350
4495
  return { kind: "missing" };
@@ -4367,7 +4512,7 @@ function readSkillFile(filePath) {
4367
4512
  };
4368
4513
  }
4369
4514
  try {
4370
- const contents = fs8.readFileSync(filePath, "utf8");
4515
+ const contents = fs9.readFileSync(filePath, "utf8");
4371
4516
  return { kind: "file", contents, sha256: sha256(contents) };
4372
4517
  } catch (error) {
4373
4518
  return {
@@ -4436,7 +4581,7 @@ function parseState(source, statePath) {
4436
4581
  }
4437
4582
  entries.push(
4438
4583
  Object.freeze({
4439
- path: path20.resolve(entry.path),
4584
+ path: path21.resolve(entry.path),
4440
4585
  sha256: entry.sha256,
4441
4586
  targetIds: Object.freeze([...entry.targetIds])
4442
4587
  })
@@ -4462,7 +4607,7 @@ function loadState(statePath) {
4462
4607
  }
4463
4608
  function serializeState(entries) {
4464
4609
  const sorted = [...entries].map((entry) => ({
4465
- path: path20.resolve(entry.path),
4610
+ path: path21.resolve(entry.path),
4466
4611
  sha256: entry.sha256,
4467
4612
  targetIds: [...entry.targetIds].sort()
4468
4613
  })).sort((left, right) => left.path.localeCompare(right.path));
@@ -4501,7 +4646,7 @@ function resolvedGroups(pluginRoot, options) {
4501
4646
  const groups = /* @__PURE__ */ new Map();
4502
4647
  for (const capability of AGENT_SKILL_CAPABILITIES) {
4503
4648
  const resolved = capability.resolve(context);
4504
- const destinationPath = path20.resolve(resolved.destinationPath);
4649
+ const destinationPath = path21.resolve(resolved.destinationPath);
4505
4650
  const key = process.platform === "win32" ? destinationPath.toLowerCase() : destinationPath;
4506
4651
  const current = groups.get(key) ?? [];
4507
4652
  current.push({ ...resolved, destinationPath });
@@ -4538,10 +4683,10 @@ function resultFor(group, status, reason) {
4538
4683
  });
4539
4684
  }
4540
4685
  function destinationDirectoryIssue(destinationPath, requireEmpty) {
4541
- const skillDir = path20.dirname(destinationPath);
4686
+ const skillDir = path21.dirname(destinationPath);
4542
4687
  let stat;
4543
4688
  try {
4544
- stat = fs8.lstatSync(skillDir);
4689
+ stat = fs9.lstatSync(skillDir);
4545
4690
  } catch (error) {
4546
4691
  if (error.code === "ENOENT") return null;
4547
4692
  return error instanceof Error ? error.message : String(error);
@@ -4549,7 +4694,7 @@ function destinationDirectoryIssue(destinationPath, requireEmpty) {
4549
4694
  if (stat.isSymbolicLink()) return "skill directory is a symbolic link";
4550
4695
  if (!stat.isDirectory()) return "skill directory is not a directory";
4551
4696
  try {
4552
- if (!requireEmpty || fs8.readdirSync(skillDir).length === 0) return null;
4697
+ if (!requireEmpty || fs9.readdirSync(skillDir).length === 0) return null;
4553
4698
  return "skill directory contains unmanaged files";
4554
4699
  } catch (error) {
4555
4700
  return error instanceof Error ? error.message : String(error);
@@ -4566,9 +4711,9 @@ function rollbackSkillWrite(destinationPath, before) {
4566
4711
  writeTextFileAtomic(destinationPath, before.contents);
4567
4712
  return;
4568
4713
  }
4569
- fs8.rmSync(destinationPath, { force: true });
4714
+ fs9.rmSync(destinationPath, { force: true });
4570
4715
  try {
4571
- fs8.rmdirSync(path20.dirname(destinationPath));
4716
+ fs9.rmdirSync(path21.dirname(destinationPath));
4572
4717
  } catch {
4573
4718
  }
4574
4719
  }
@@ -4611,12 +4756,12 @@ function installManagedAgentSkills(pluginRoot, options = {}) {
4611
4756
  const loaded = loadState(statePath);
4612
4757
  let stateSource = loaded.source;
4613
4758
  const entries = new Map(
4614
- loaded.state.entries.map((entry) => [path20.resolve(entry.path), entry])
4759
+ loaded.state.entries.map((entry) => [path21.resolve(entry.path), entry])
4615
4760
  );
4616
4761
  const results = [];
4617
4762
  for (const group of resolvedGroups(pluginRoot, options)) {
4618
4763
  const source = sourceSkill(group.sourcePath);
4619
- const destinationPath = path20.resolve(group.destinationPath);
4764
+ const destinationPath = path21.resolve(group.destinationPath);
4620
4765
  const current = readSkillFile(destinationPath);
4621
4766
  const previous = entries.get(destinationPath);
4622
4767
  const nextEntry = stateEntryFor(
@@ -4701,11 +4846,11 @@ function removeSkillFile(args) {
4701
4846
  `Agent skill changed before ${args.destinationPath} could be removed.`
4702
4847
  );
4703
4848
  }
4704
- const backupPath = path20.join(
4705
- path20.dirname(args.destinationPath),
4706
- `.${path20.basename(args.destinationPath)}.${process.pid}.${randomUUID2()}.bak`
4849
+ const backupPath = path21.join(
4850
+ path21.dirname(args.destinationPath),
4851
+ `.${path21.basename(args.destinationPath)}.${process.pid}.${randomUUID3()}.bak`
4707
4852
  );
4708
- fs8.renameSync(args.destinationPath, backupPath);
4853
+ fs9.renameSync(args.destinationPath, backupPath);
4709
4854
  let stateSource;
4710
4855
  try {
4711
4856
  stateSource = writeState(
@@ -4715,7 +4860,7 @@ function removeSkillFile(args) {
4715
4860
  );
4716
4861
  } catch (error) {
4717
4862
  try {
4718
- fs8.renameSync(backupPath, args.destinationPath);
4863
+ fs9.renameSync(backupPath, args.destinationPath);
4719
4864
  } catch (rollbackError) {
4720
4865
  const message = error instanceof Error ? error.message : String(error);
4721
4866
  const rollbackMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
@@ -4727,12 +4872,12 @@ function removeSkillFile(args) {
4727
4872
  }
4728
4873
  let warning;
4729
4874
  try {
4730
- fs8.rmSync(backupPath, { force: true });
4875
+ fs9.rmSync(backupPath, { force: true });
4731
4876
  } catch (error) {
4732
4877
  warning = `agent skill removal was committed, but backup cleanup failed at ${backupPath}: ${error instanceof Error ? error.message : String(error)}`;
4733
4878
  }
4734
4879
  try {
4735
- fs8.rmdirSync(path20.dirname(args.destinationPath));
4880
+ fs9.rmdirSync(path21.dirname(args.destinationPath));
4736
4881
  } catch {
4737
4882
  }
4738
4883
  return { stateSource, ...warning ? { warning } : {} };
@@ -4742,16 +4887,16 @@ function removeManagedAgentSkills(pluginRoot, options = {}) {
4742
4887
  const loaded = loadState(statePath);
4743
4888
  let stateSource = loaded.source;
4744
4889
  const entries = new Map(
4745
- loaded.state.entries.map((entry) => [path20.resolve(entry.path), entry])
4890
+ loaded.state.entries.map((entry) => [path21.resolve(entry.path), entry])
4746
4891
  );
4747
4892
  const candidates = /* @__PURE__ */ new Map();
4748
4893
  const currentGroups = resolvedGroups(pluginRoot, options);
4749
- const packagedSourcePath = currentGroups[0]?.sourcePath ?? path20.join(pluginRoot, "agent-assets", "fml", "SKILL.md");
4894
+ const packagedSourcePath = currentGroups[0]?.sourcePath ?? path21.join(pluginRoot, "agent-assets", "fml", "SKILL.md");
4750
4895
  for (const group of currentGroups) {
4751
- candidates.set(path20.resolve(group.destinationPath), group);
4896
+ candidates.set(path21.resolve(group.destinationPath), group);
4752
4897
  }
4753
4898
  for (const entry of entries.values()) {
4754
- const destinationPath = path20.resolve(entry.path);
4899
+ const destinationPath = path21.resolve(entry.path);
4755
4900
  if (candidates.has(destinationPath)) continue;
4756
4901
  const targets = entry.targetIds.filter(isAgentSkillTargetId).map(targetForId);
4757
4902
  if (targets.length === 0) continue;
@@ -4766,7 +4911,7 @@ function removeManagedAgentSkills(pluginRoot, options = {}) {
4766
4911
  for (const group of [...candidates.values()].sort(
4767
4912
  (left, right) => left.destinationPath.localeCompare(right.destinationPath)
4768
4913
  )) {
4769
- const destinationPath = path20.resolve(group.destinationPath);
4914
+ const destinationPath = path21.resolve(group.destinationPath);
4770
4915
  const current = readSkillFile(destinationPath);
4771
4916
  const previous = entries.get(destinationPath);
4772
4917
  if (current.kind === "missing") {
@@ -4824,13 +4969,13 @@ function removeManagedAgentSkills(pluginRoot, options = {}) {
4824
4969
  }
4825
4970
 
4826
4971
  // src/commands/install.ts
4827
- function resolvePluginRoot(startDir = path21.dirname(fileURLToPath(import.meta.url))) {
4972
+ function resolvePluginRoot(startDir = path22.dirname(fileURLToPath(import.meta.url))) {
4828
4973
  let directory = startDir;
4829
4974
  while (true) {
4830
- if (fs9.existsSync(path21.join(directory, "package.json")) && fs9.existsSync(path21.join(directory, "bin"))) {
4975
+ if (fs10.existsSync(path22.join(directory, "package.json")) && fs10.existsSync(path22.join(directory, "bin"))) {
4831
4976
  return directory;
4832
4977
  }
4833
- const parent = path21.dirname(directory);
4978
+ const parent = path22.dirname(directory);
4834
4979
  if (parent === directory) {
4835
4980
  throw new Error(
4836
4981
  `Could not locate the FML package root: no ancestor of ${startDir} contains both package.json and bin/. Reinstall @fml-inc/fml and retry.`
@@ -4845,7 +4990,7 @@ function getPluginRoot() {
4845
4990
  function readJsonFileForUpdate(filePath) {
4846
4991
  let source;
4847
4992
  try {
4848
- source = fs9.readFileSync(filePath, "utf8");
4993
+ source = fs10.readFileSync(filePath, "utf8");
4849
4994
  } catch (error) {
4850
4995
  if (error.code === "ENOENT") {
4851
4996
  return { ok: true, exists: false, data: {} };
@@ -4909,9 +5054,9 @@ async function handleInstall(opts = {}) {
4909
5054
  FML_LOG_DIR,
4910
5055
  FML_OBSERVER_DIR
4911
5056
  ])) {
4912
- fs9.mkdirSync(directory, { recursive: true, mode: 448 });
5057
+ fs10.mkdirSync(directory, { recursive: true, mode: 448 });
4913
5058
  }
4914
- initializeSyncSettings(path21.join(FML_OBSERVER_DIR, "sync.json"));
5059
+ initializeSyncSettings(path22.join(FML_OBSERVER_DIR, "sync.json"));
4915
5060
  let skillReady = false;
4916
5061
  try {
4917
5062
  const skillResults = installManagedAgentSkills(pluginRoot);
@@ -4930,7 +5075,35 @@ async function handleInstall(opts = {}) {
4930
5075
  `Warning: agent-native skill setup could not be completed: ${message}`
4931
5076
  );
4932
5077
  }
4933
- const packageJson = readJsonFile(path21.join(pluginRoot, "package.json"));
5078
+ let piExtensionReady = false;
5079
+ try {
5080
+ const piPaths = PI_EXTENSION_INSTALLER.resolvePaths({
5081
+ env: process.env,
5082
+ homeDir: os5.homedir(),
5083
+ packageRoot: pluginRoot,
5084
+ platform: process.platform
5085
+ });
5086
+ const piResult = PI_EXTENSION_INSTALLER.install(piPaths);
5087
+ piExtensionReady = piResult.status !== "preserved";
5088
+ if (piResult.status === "preserved") {
5089
+ console.log(
5090
+ ` warn: preserved ${piResult.destinationPath} for Pi: ${piResult.reason}`
5091
+ );
5092
+ }
5093
+ if (piResult.legacyRemoved) {
5094
+ console.log(" Removed the legacy Panopticon Pi extension");
5095
+ } else if (piResult.legacyPreserved) {
5096
+ console.log(
5097
+ ` warn: preserved unrecognized Pi extension ${piPaths.legacyDestinationPath}; inspect and remove it if it belongs to Panopticon`
5098
+ );
5099
+ }
5100
+ } catch (error) {
5101
+ const message = error instanceof Error ? error.message : String(error);
5102
+ console.log(
5103
+ ` warn: Pi extension setup could not be completed: ${message}`
5104
+ );
5105
+ }
5106
+ const packageJson = readJsonFile(path22.join(pluginRoot, "package.json"));
4934
5107
  const generatedManifest = readJsonFile(paths.pluginManifestPath);
4935
5108
  const version = typeof generatedManifest?.version === "string" ? generatedManifest.version : typeof packageJson?.version === "string" ? packageJson.version : "0.0.0-dev";
4936
5109
  writeJsonFile(
@@ -4947,7 +5120,7 @@ async function handleInstall(opts = {}) {
4947
5120
  `Warning: preserved unreadable marketplace config ${paths.marketplaceManifestPath}: ${marketplaceResult.error}`
4948
5121
  );
4949
5122
  } else {
4950
- fs9.mkdirSync(paths.marketplaceDir, { recursive: true, mode: 448 });
5123
+ fs10.mkdirSync(paths.marketplaceDir, { recursive: true, mode: 448 });
4951
5124
  const linkUpdate = replaceMarketplaceLink(
4952
5125
  paths.marketplaceLinkPath,
4953
5126
  pluginRoot,
@@ -5027,9 +5200,9 @@ async function handleInstall(opts = {}) {
5027
5200
  }
5028
5201
  });
5029
5202
  }
5030
- if (!skillReady || !marketplaceReady || !settingsReady || !hooksReady) {
5203
+ if (!skillReady || !piExtensionReady || !marketplaceReady || !settingsReady || !hooksReady) {
5031
5204
  throw new Error(
5032
- "FML install is incomplete: repair the preserved agent skill, hook configuration, Claude plugin path, or configuration reported above, then run `fml install` again."
5205
+ "FML install is incomplete: repair the preserved agent skill, Pi extension, hook configuration, Claude plugin path, or configuration reported above, then run `fml install` again."
5033
5206
  );
5034
5207
  }
5035
5208
  try {
@@ -5056,12 +5229,12 @@ async function handleInstall(opts = {}) {
5056
5229
  import { statSync } from "fs";
5057
5230
 
5058
5231
  // src/commands/otel-config.ts
5059
- import fs11 from "fs";
5232
+ import fs12 from "fs";
5060
5233
  import os6 from "os";
5061
- import path26 from "path";
5234
+ import path27 from "path";
5062
5235
 
5063
5236
  // ../../packages/agent-targets/src/install/otel/claude.ts
5064
- import path22 from "path";
5237
+ import path23 from "path";
5065
5238
 
5066
5239
  // ../../packages/agent-targets/src/install/otel/shared.ts
5067
5240
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
@@ -5198,9 +5371,9 @@ var MANAGED_MARKER = "FML_OTEL_MANAGED";
5198
5371
  var CLAUDE_OTEL_SOURCE_LIMITATION = "Claude Code may truncate inline raw API bodies and tool content at 60 KiB and redact extended-thinking content before export";
5199
5372
  function configPath(context) {
5200
5373
  if (context.configPathOverride) {
5201
- return path22.resolve(context.configPathOverride);
5374
+ return path23.resolve(context.configPathOverride);
5202
5375
  }
5203
- return path22.join(
5376
+ return path23.join(
5204
5377
  claudeConfigDirectory({ env: context.env, homeDir: context.homeDir }),
5205
5378
  "settings.json"
5206
5379
  );
@@ -5371,7 +5544,7 @@ var CLAUDE_OTEL_INSTALLER = Object.freeze({
5371
5544
 
5372
5545
  // ../../packages/agent-targets/src/install/otel/codex.ts
5373
5546
  import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
5374
- import path23 from "path";
5547
+ import path24 from "path";
5375
5548
  import { parse as parse5 } from "smol-toml";
5376
5549
  var TARGET14 = agentTarget("codex");
5377
5550
  var TARGET_ID6 = TARGET14.id;
@@ -5380,13 +5553,13 @@ var AUTHENTICATION2 = "bearer-header";
5380
5553
  var CODEX_OTEL_SOURCE_LIMITATION = "Codex OTel has no documented raw API body or full tool-content export switch; tool-result content is an output snippet";
5381
5554
  function configPath2(context) {
5382
5555
  if (context.configPathOverride) {
5383
- return path23.resolve(context.configPathOverride);
5556
+ return path24.resolve(context.configPathOverride);
5384
5557
  }
5385
5558
  const configuredHome = context.env.CODEX_HOME;
5386
- const codexHome = path23.resolve(
5387
- typeof configuredHome === "string" && configuredHome.trim().length > 0 ? configuredHome : path23.join(context.homeDir, ".codex")
5559
+ const codexHome = path24.resolve(
5560
+ typeof configuredHome === "string" && configuredHome.trim().length > 0 ? configuredHome : path24.join(context.homeDir, ".codex")
5388
5561
  );
5389
- return path23.join(codexHome, "config.toml");
5562
+ return path24.join(codexHome, "config.toml");
5390
5563
  }
5391
5564
  function readToml(snapshot) {
5392
5565
  const failure = snapshotError2(snapshot);
@@ -5606,7 +5779,7 @@ var CODEX_OTEL_INSTALLER = Object.freeze({
5606
5779
  });
5607
5780
 
5608
5781
  // ../../packages/agent-targets/src/install/otel/gemini.ts
5609
- import path24 from "path";
5782
+ import path25 from "path";
5610
5783
  var TARGET15 = agentTarget("gemini");
5611
5784
  var TARGET_ID7 = TARGET15.id;
5612
5785
  var DISPLAY_NAME7 = TARGET15.displayName;
@@ -5614,9 +5787,9 @@ var AUTHENTICATION3 = "path-token";
5614
5787
  var GEMINI_OTEL_SOURCE_LIMITATION = "Gemini CLI has no documented OTLP header setting; FML uses an observer-local tokenized loopback path";
5615
5788
  function configPath3(context) {
5616
5789
  if (context.configPathOverride) {
5617
- return path24.resolve(context.configPathOverride);
5790
+ return path25.resolve(context.configPathOverride);
5618
5791
  }
5619
- return path24.join(geminiConfigDirectory(context), "settings.json");
5792
+ return path25.join(geminiConfigDirectory(context), "settings.json");
5620
5793
  }
5621
5794
  function managedTelemetry(context) {
5622
5795
  const destination = resolveDestination(context.observer);
@@ -5770,8 +5943,8 @@ var OTEL_INSTALLER_CAPABILITIES = Object.freeze([
5770
5943
  ]);
5771
5944
 
5772
5945
  // src/commands/otel-config-state.ts
5773
- import fs10 from "fs";
5774
- import path25 from "path";
5946
+ import fs11 from "fs";
5947
+ import path26 from "path";
5775
5948
  var OTEL_CONFIG_STATE_SCHEMA_VERSION = 1;
5776
5949
  function emptyState2() {
5777
5950
  return Object.freeze({
@@ -5785,12 +5958,12 @@ function invalidState2(statePath, reason) {
5785
5958
  );
5786
5959
  }
5787
5960
  function enforceOwnerOnlyMode(statePath) {
5788
- if (process.platform !== "win32") fs10.chmodSync(statePath, 384);
5961
+ if (process.platform !== "win32") fs11.chmodSync(statePath, 384);
5789
5962
  }
5790
5963
  function readStateFile(statePath) {
5791
5964
  let stat;
5792
5965
  try {
5793
- stat = fs10.lstatSync(statePath);
5966
+ stat = fs11.lstatSync(statePath);
5794
5967
  } catch (error) {
5795
5968
  if (error.code === "ENOENT") {
5796
5969
  return { kind: "missing" };
@@ -5808,7 +5981,7 @@ function readStateFile(statePath) {
5808
5981
  }
5809
5982
  try {
5810
5983
  enforceOwnerOnlyMode(statePath);
5811
- return { kind: "file", contents: fs10.readFileSync(statePath, "utf8") };
5984
+ return { kind: "file", contents: fs11.readFileSync(statePath, "utf8") };
5812
5985
  } catch (error) {
5813
5986
  return {
5814
5987
  kind: "invalid",
@@ -5817,7 +5990,7 @@ function readStateFile(statePath) {
5817
5990
  }
5818
5991
  }
5819
5992
  function otelConfigStateEntryKey(targetId, configPath4) {
5820
- const resolved = path25.resolve(configPath4);
5993
+ const resolved = path26.resolve(configPath4);
5821
5994
  const pathKey = process.platform === "win32" ? resolved.toLowerCase() : resolved;
5822
5995
  return `${targetId}\0${pathKey}`;
5823
5996
  }
@@ -5853,7 +6026,7 @@ function parseState2(source, statePath) {
5853
6026
  if (typeof observerRecord.baseUrl !== "string" || observerRecord.baseUrl.length === 0 || typeof observerRecord.authToken !== "string" || observerRecord.authToken.length === 0) {
5854
6027
  throw invalidState2(statePath, "contains an invalid observer destination");
5855
6028
  }
5856
- const resolvedPath = path25.resolve(entry.path);
6029
+ const resolvedPath = path26.resolve(entry.path);
5857
6030
  const key = otelConfigStateEntryKey(entry.targetId, resolvedPath);
5858
6031
  if (keys.has(key)) {
5859
6032
  throw invalidState2(statePath, "contains duplicate entries");
@@ -5891,7 +6064,7 @@ function loadFmlOtelConfigState(statePath) {
5891
6064
  function serializeState2(entries) {
5892
6065
  const sorted = [...entries].map((entry) => ({
5893
6066
  targetId: entry.targetId,
5894
- path: path25.resolve(entry.path),
6067
+ path: path26.resolve(entry.path),
5895
6068
  observer: {
5896
6069
  baseUrl: entry.observer.baseUrl,
5897
6070
  authToken: entry.observer.authToken
@@ -5928,7 +6101,7 @@ function isKnownOtelTargetId(targetId) {
5928
6101
  }
5929
6102
 
5930
6103
  // src/commands/otel-config.ts
5931
- var DEFAULT_OTEL_CONFIG_STATE_PATH = path26.join(
6104
+ var DEFAULT_OTEL_CONFIG_STATE_PATH = path27.join(
5932
6105
  FML_DATA_DIR,
5933
6106
  "otel-config.json"
5934
6107
  );
@@ -5937,7 +6110,7 @@ function readLocalObserverOtelDestination(options) {
5937
6110
  if (!authToken) return null;
5938
6111
  try {
5939
6112
  const parsed = JSON.parse(
5940
- fs11.readFileSync(options.endpointFile, "utf8")
6113
+ fs12.readFileSync(options.endpointFile, "utf8")
5941
6114
  );
5942
6115
  const endpoint = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
5943
6116
  const host = endpoint?.host;
@@ -5954,7 +6127,7 @@ function readLocalObserverOtelDestination(options) {
5954
6127
  function readOtelFile(filePath) {
5955
6128
  let stat;
5956
6129
  try {
5957
- stat = fs11.lstatSync(filePath);
6130
+ stat = fs12.lstatSync(filePath);
5958
6131
  } catch (error) {
5959
6132
  if (error.code === "ENOENT") {
5960
6133
  return { path: filePath, kind: "missing" };
@@ -5983,7 +6156,7 @@ function readOtelFile(filePath) {
5983
6156
  return {
5984
6157
  path: filePath,
5985
6158
  kind: "file",
5986
- contents: fs11.readFileSync(filePath, "utf8")
6159
+ contents: fs12.readFileSync(filePath, "utf8")
5987
6160
  };
5988
6161
  } catch (error) {
5989
6162
  return {
@@ -6005,7 +6178,7 @@ function assertMutationPrecondition2(mutation) {
6005
6178
  throw new Error(`configuration changed while planning ${mutation.path}`);
6006
6179
  }
6007
6180
  function applyMutation2(mutation) {
6008
- if (mutation.after === null) fs11.rmSync(mutation.path);
6181
+ if (mutation.after === null) fs12.rmSync(mutation.path);
6009
6182
  else writeTextFileAtomic(mutation.path, mutation.after);
6010
6183
  }
6011
6184
  function enforceOwnerOnlyConfigPermissions(filePath) {
@@ -6018,7 +6191,7 @@ function enforceOwnerOnlyConfigPermissions(filePath) {
6018
6191
  );
6019
6192
  }
6020
6193
  try {
6021
- fs11.chmodSync(filePath, 384);
6194
+ fs12.chmodSync(filePath, 384);
6022
6195
  } catch (error) {
6023
6196
  const detail = error instanceof Error ? error.message : String(error);
6024
6197
  throw new Error(
@@ -6113,7 +6286,7 @@ function plannerContext3(observer, options, configPathOverride, configOwnershipR
6113
6286
  observer,
6114
6287
  homeDir: options.homeDir ?? os6.homedir(),
6115
6288
  env: options.env ?? process.env,
6116
- ...configPathOverride ? { configPathOverride: path26.resolve(configPathOverride) } : {},
6289
+ ...configPathOverride ? { configPathOverride: path27.resolve(configPathOverride) } : {},
6117
6290
  ...configOwnershipRecorded ? { configOwnershipRecorded: true } : {},
6118
6291
  readFile: readOtelFile
6119
6292
  };
@@ -6156,7 +6329,7 @@ function commitStateEntry(state, key, next) {
6156
6329
  function stateEntry(plan, observer) {
6157
6330
  return Object.freeze({
6158
6331
  targetId: plan.targetId,
6159
- path: path26.resolve(plan.configPath),
6332
+ path: path27.resolve(plan.configPath),
6160
6333
  observer: Object.freeze({
6161
6334
  baseUrl: observer.baseUrl,
6162
6335
  authToken: observer.authToken
@@ -6714,8 +6887,8 @@ async function canOpenBrowser() {
6714
6887
  if (process.env.CODESPACES) return false;
6715
6888
  if (process.env.REMOTE_CONTAINERS) return false;
6716
6889
  try {
6717
- const fs13 = await import("fs");
6718
- if (fs13.existsSync("/.dockerenv")) return false;
6890
+ const fs14 = await import("fs");
6891
+ if (fs14.existsSync("/.dockerenv")) return false;
6719
6892
  } catch {
6720
6893
  }
6721
6894
  return true;
@@ -7274,9 +7447,9 @@ async function handleSetup(dependencies = defaultDependencies) {
7274
7447
  }
7275
7448
 
7276
7449
  // src/commands/uninstall.ts
7277
- import fs12 from "fs";
7450
+ import fs13 from "fs";
7278
7451
  import os7 from "os";
7279
- import path27 from "path";
7452
+ import path28 from "path";
7280
7453
  import readline from "readline/promises";
7281
7454
  var PURGE_WARNING = "This deletes FML credentials for ALL environments, logs, sync state, and every pending best-effort OTLP envelope.";
7282
7455
  function tryDisableHookCapture() {
@@ -7292,19 +7465,19 @@ function tryDisableHookCapture() {
7292
7465
  }
7293
7466
  }
7294
7467
  function resolvePreservedPaths(preservedFile) {
7295
- const logicalPath = path27.resolve(preservedFile);
7468
+ const logicalPath = path28.resolve(preservedFile);
7296
7469
  try {
7297
- const canonicalPath = fs12.realpathSync(preservedFile);
7470
+ const canonicalPath = fs13.realpathSync(preservedFile);
7298
7471
  return pathsAreSame(canonicalPath, logicalPath) ? [logicalPath] : [logicalPath, canonicalPath];
7299
7472
  } catch {
7300
7473
  return [logicalPath];
7301
7474
  }
7302
7475
  }
7303
7476
  function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot = false) {
7304
- const resolvedDirectory = path27.resolve(directory);
7477
+ const resolvedDirectory = path28.resolve(directory);
7305
7478
  let directoryStats;
7306
7479
  try {
7307
- directoryStats = fs12.lstatSync(directory);
7480
+ directoryStats = fs13.lstatSync(directory);
7308
7481
  } catch (error) {
7309
7482
  if (error.code === "ENOENT") return true;
7310
7483
  throw error;
@@ -7315,14 +7488,14 @@ function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot
7315
7488
  (preservedPath) => pathContains(resolvedDirectory, preservedPath)
7316
7489
  );
7317
7490
  if (!containsLogicalMarker) {
7318
- fs12.rmSync(directory, { force: true });
7491
+ fs13.rmSync(directory, { force: true });
7319
7492
  return true;
7320
7493
  }
7321
7494
  return false;
7322
7495
  }
7323
7496
  const directoryPaths = [resolvedDirectory];
7324
7497
  try {
7325
- const canonicalDirectory = fs12.realpathSync(directory);
7498
+ const canonicalDirectory = fs13.realpathSync(directory);
7326
7499
  if (!pathsAreSame(canonicalDirectory, resolvedDirectory)) {
7327
7500
  directoryPaths.push(canonicalDirectory);
7328
7501
  }
@@ -7334,26 +7507,26 @@ function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot
7334
7507
  )
7335
7508
  );
7336
7509
  if (!containsPreservedPath) {
7337
- fs12.rmSync(directory, { recursive: true, force: true });
7510
+ fs13.rmSync(directory, { recursive: true, force: true });
7338
7511
  return true;
7339
7512
  }
7340
7513
  const isPreservedPath = directoryPaths.some(
7341
7514
  (directoryPath) => preservedPaths.some(
7342
- (preservedPath) => pathsAreSame(directoryPath, path27.resolve(preservedPath))
7515
+ (preservedPath) => pathsAreSame(directoryPath, path28.resolve(preservedPath))
7343
7516
  )
7344
7517
  );
7345
7518
  if (isPreservedPath) return true;
7346
7519
  if (!directoryStats.isDirectory()) return false;
7347
7520
  let entries;
7348
7521
  try {
7349
- entries = fs12.readdirSync(directory);
7522
+ entries = fs13.readdirSync(directory);
7350
7523
  } catch (error) {
7351
7524
  if (error.code === "ENOENT") return true;
7352
7525
  throw error;
7353
7526
  }
7354
7527
  let complete = true;
7355
7528
  for (const entry of entries) {
7356
- const entryPath = path27.join(directory, entry);
7529
+ const entryPath = path28.join(directory, entry);
7357
7530
  if (!purgeDirectoryPreservingPaths(entryPath, preservedPaths, false)) {
7358
7531
  complete = false;
7359
7532
  }
@@ -7372,13 +7545,13 @@ function purgeDirectoryPreservingFiles(directory, preservedFiles) {
7372
7545
  }
7373
7546
  function finishCustomRootPurge(directory) {
7374
7547
  const marker = managedDirectoryMarker(directory);
7375
- fs12.rmSync(marker, { force: true });
7548
+ fs13.rmSync(marker, { force: true });
7376
7549
  try {
7377
- fs12.rmdirSync(directory);
7550
+ fs13.rmdirSync(directory);
7378
7551
  } catch (error) {
7379
7552
  if (error.code === "ENOENT") return;
7380
7553
  try {
7381
- fs12.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
7554
+ fs13.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
7382
7555
  encoding: "utf8",
7383
7556
  flag: "wx",
7384
7557
  mode: 384
@@ -7476,7 +7649,7 @@ async function handleUninstall(opts) {
7476
7649
  return;
7477
7650
  }
7478
7651
  for (const root of purgeRoots) {
7479
- if (!fs12.existsSync(root.directory) && pathContains(root.directory, FML_OBSERVER_DIR)) {
7652
+ if (!fs13.existsSync(root.directory) && pathContains(root.directory, FML_OBSERVER_DIR)) {
7480
7653
  prepareManagedDirectory(root);
7481
7654
  }
7482
7655
  }
@@ -7552,6 +7725,38 @@ async function handleUninstall(opts) {
7552
7725
  );
7553
7726
  }
7554
7727
  let integrationsRemoved = true;
7728
+ try {
7729
+ const piPaths = PI_EXTENSION_INSTALLER.resolvePaths({
7730
+ env: process.env,
7731
+ homeDir: os7.homedir(),
7732
+ packageRoot: resolvePackageRoot(),
7733
+ platform: process.platform
7734
+ });
7735
+ const piResult = PI_EXTENSION_INSTALLER.uninstall(piPaths);
7736
+ if (piResult.status === "preserved") {
7737
+ integrationsRemoved = false;
7738
+ process.exitCode = 1;
7739
+ console.warn(
7740
+ `Warning: Preserved ${piResult.destinationPath} for Pi: ${piResult.reason}`
7741
+ );
7742
+ }
7743
+ if (piResult.legacyRemoved) {
7744
+ console.log("Removed the legacy Panopticon Pi extension");
7745
+ } else if (piResult.legacyPreserved) {
7746
+ integrationsRemoved = false;
7747
+ process.exitCode = 1;
7748
+ console.warn(
7749
+ `Warning: Preserved unrecognized Pi extension ${piPaths.legacyDestinationPath}; inspect and remove it if it belongs to Panopticon`
7750
+ );
7751
+ }
7752
+ } catch (error) {
7753
+ integrationsRemoved = false;
7754
+ process.exitCode = 1;
7755
+ const message = error instanceof Error ? error.message : String(error);
7756
+ console.warn(
7757
+ `Warning: Pi extension cleanup could not be completed: ${message}`
7758
+ );
7759
+ }
7555
7760
  if (removeMarketplaceLink(paths.marketplaceLinkPath) === "preserved") {
7556
7761
  integrationsRemoved = false;
7557
7762
  console.warn(
@@ -7568,7 +7773,7 @@ async function handleUninstall(opts) {
7568
7773
  )
7569
7774
  );
7570
7775
  }
7571
- fs12.rmSync(paths.pluginCacheDir, { recursive: true, force: true });
7776
+ fs13.rmSync(paths.pluginCacheDir, { recursive: true, force: true });
7572
7777
  const reportIntegrationWarning = (message) => {
7573
7778
  integrationsRemoved = false;
7574
7779
  console.warn(message.replace(/^warn:/u, "Warning:"));
@@ -7690,7 +7895,7 @@ async function handleUninstall(opts) {
7690
7895
  // src/commands/update.ts
7691
7896
  var SEMVER_PATTERN = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
7692
7897
  function getCurrentVersion() {
7693
- return true ? "0.8.0-rc.2+bed6eaed" : "unknown";
7898
+ return true ? "0.8.0+246af775" : "unknown";
7694
7899
  }
7695
7900
  function resolveUpdateChannel(version) {
7696
7901
  const match = SEMVER_PATTERN.exec(version);
@@ -8723,8 +8928,8 @@ async function handleConfigDetail(opts) {
8723
8928
 
8724
8929
  // src/commands/tools.ts
8725
8930
  import { readFileSync } from "fs";
8726
- import path28 from "path";
8727
- var pluginVersion = true ? "0.8.0-rc.2+bed6eaed" : void 0;
8931
+ import path29 from "path";
8932
+ var pluginVersion = true ? "0.8.0+246af775" : void 0;
8728
8933
  async function getApi() {
8729
8934
  const api = await getAuthenticatedClient();
8730
8935
  if (!api) {
@@ -8831,7 +9036,7 @@ async function handleToolsCall(name, opts) {
8831
9036
  }
8832
9037
  const raw = opts.file ? (() => {
8833
9038
  try {
8834
- return readFileSync(path28.resolve(opts.file), "utf8");
9039
+ return readFileSync(path29.resolve(opts.file), "utf8");
8835
9040
  } catch (err) {
8836
9041
  const msg = err instanceof Error ? err.message : String(err);
8837
9042
  console.error(`Could not read args file: ${msg}`);
@@ -8881,7 +9086,7 @@ if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.
8881
9086
  printBanner();
8882
9087
  }
8883
9088
  var program = new Command().name("fml").description("FML CLI and agent tools").version(
8884
- true ? "0.8.0-rc.2+bed6eaed" : "dev"
9089
+ true ? "0.8.0+246af775" : "dev"
8885
9090
  );
8886
9091
  function formatCommandInventory(cmd, prefix = "") {
8887
9092
  const name = prefix ? `${prefix} ${cmd.name()}` : cmd.name();