@pieai/pro-gov 0.8.0 → 0.9.1

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
@@ -8,8 +8,8 @@ var packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
8
  var sourceRoot = join(packageRoot, "..", "..");
9
9
  var packagedAssetsRoot = join(packageRoot, "assets");
10
10
  var assetRoots = ["starter", "profiles", "integrations", "docs/reference/adoption"];
11
- function listAssets() {
12
- const root = existsSync(join(sourceRoot, "starter")) ? sourceRoot : packagedAssetsRoot;
11
+ function listAssets(source = "auto") {
12
+ const root = source === "packaged" ? packagedAssetsRoot : source === "source" ? sourceRoot : existsSync(join(sourceRoot, "starter")) ? sourceRoot : packagedAssetsRoot;
13
13
  return assetRoots.flatMap((assetRoot) => {
14
14
  const absoluteRoot = join(root, assetRoot);
15
15
  if (!existsSync(absoluteRoot)) return [];
@@ -211,12 +211,12 @@ function toUnixPath2(path) {
211
211
  // src/asset-registry/loader.ts
212
212
  import { createHash as createHash2 } from "node:crypto";
213
213
  import { existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
214
- import { dirname as dirname2, join as join5, relative as relative3 } from "node:path";
214
+ import { dirname as dirname2, join as join5, relative as relative4 } from "node:path";
215
215
  import { fileURLToPath as fileURLToPath2 } from "node:url";
216
216
 
217
217
  // src/asset-registry/registry.ts
218
- import { existsSync as existsSync4, lstatSync, readdirSync as readdirSync4 } from "node:fs";
219
- import { isAbsolute, join as join4, posix } from "node:path";
218
+ import { existsSync as existsSync4, lstatSync, readdirSync as readdirSync4, realpathSync } from "node:fs";
219
+ import { isAbsolute, join as join4, posix, relative as relative3, sep } from "node:path";
220
220
  var supportedFamilies = /* @__PURE__ */ new Set([
221
221
  "pie-skills",
222
222
  "npx-skills",
@@ -247,6 +247,7 @@ var supportedVisibilities = /* @__PURE__ */ new Set([
247
247
  var supportedSourceKinds = /* @__PURE__ */ new Set(["local", "local-pack", "npx"]);
248
248
  var supportedSkillPlacements = /* @__PURE__ */ new Set(["auto", "manual"]);
249
249
  var supportedSkillScopes = /* @__PURE__ */ new Set(["project", "user"]);
250
+ var supportedDeliveries = /* @__PURE__ */ new Set(["symlink", "snapshot"]);
250
251
  var supportedHosts = /* @__PURE__ */ new Set([
251
252
  "codex",
252
253
  "claude-code",
@@ -254,12 +255,35 @@ var supportedHosts = /* @__PURE__ */ new Set([
254
255
  "antigravity"
255
256
  ]);
256
257
  var skillInstallNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
258
+ var safeProjectTargetFilenamePattern = /^[^./\\][^/\\]*$/;
257
259
  function assetSkillInstallName(asset) {
258
260
  return asset.installName ?? posix.basename(asset.sourcePath);
259
261
  }
262
+ function isValidAssetProjectTargetPath(kind, projectTargetPath) {
263
+ if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute(projectTargetPath) || projectTargetPath.includes("\\")) {
264
+ return false;
265
+ }
266
+ const segments = projectTargetPath.split("/");
267
+ if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
268
+ return false;
269
+ }
270
+ const filename = segments.at(-1);
271
+ if (!filename || !safeProjectTargetFilenamePattern.test(filename)) return false;
272
+ if (kind === "rule") {
273
+ return segments.length === 4 && segments[0] === "docs" && segments[1] === "policy" && segments[2] === "shared-rules" && filename.endsWith(".md") || segments.length === 4 && segments[0] === ".pro-gov" && segments[1] === "agent-assets" && segments[2] === "rules";
274
+ }
275
+ if (kind === "command") {
276
+ return segments.length === 4 && segments[0] === ".pro-gov" && segments[1] === "agent-assets" && segments[2] === "commands";
277
+ }
278
+ return false;
279
+ }
280
+ function isValidSnapshotProjectTargetPath(projectTargetPath) {
281
+ return typeof projectTargetPath === "string" && projectTargetPath.startsWith("docs/policy/shared-rules/") && isValidAssetProjectTargetPath("rule", projectTargetPath);
282
+ }
260
283
  function validateAssetRegistry(registry, options = {}) {
261
284
  const issues = [];
262
285
  const seenIds = /* @__PURE__ */ new Set();
286
+ const projectTargetOwners = /* @__PURE__ */ new Map();
263
287
  for (const asset of registry.assets) {
264
288
  if (seenIds.has(asset.id)) {
265
289
  issues.push({
@@ -311,6 +335,21 @@ function validateAssetRegistry(registry, options = {}) {
311
335
  message: `Unsupported asset source kind: ${asset.sourceKind}`
312
336
  });
313
337
  }
338
+ if (asset.delivery !== void 0 && !supportedDeliveries.has(asset.delivery)) {
339
+ issues.push({
340
+ type: "unsupported-enum",
341
+ id: asset.id,
342
+ message: `Unsupported asset delivery: ${asset.delivery}`
343
+ });
344
+ }
345
+ if (asset.delivery === "snapshot" && (asset.kind !== "rule" || !isValidSnapshotProjectTargetPath(asset.projectTargetPath))) {
346
+ issues.push({
347
+ type: "invalid-snapshot-delivery",
348
+ id: asset.id,
349
+ path: asset.projectTargetPath,
350
+ message: `Snapshot delivery is only allowed for rule assets targeting docs/policy/shared-rules/<name>.md: ${asset.id}`
351
+ });
352
+ }
314
353
  for (const host of asset.hosts) {
315
354
  if (!supportedHosts.has(host)) {
316
355
  issues.push({
@@ -349,6 +388,26 @@ function validateAssetRegistry(registry, options = {}) {
349
388
  message: `Asset source path escapes agent-assets: ${asset.sourcePath}`
350
389
  });
351
390
  }
391
+ if (asset.projectTargetPath !== void 0 && !isValidAssetProjectTargetPath(asset.kind, asset.projectTargetPath)) {
392
+ issues.push({
393
+ type: "invalid-project-target-path",
394
+ id: asset.id,
395
+ path: asset.projectTargetPath,
396
+ message: `Asset project target path is not allowed for ${asset.kind}: ${asset.projectTargetPath}`
397
+ });
398
+ } else if (asset.projectTargetPath !== void 0) {
399
+ const existingOwner = projectTargetOwners.get(asset.projectTargetPath);
400
+ if (existingOwner !== void 0 && existingOwner !== asset.id) {
401
+ issues.push({
402
+ type: "duplicate-project-target-path",
403
+ id: asset.id,
404
+ path: asset.projectTargetPath,
405
+ message: `Project target path is already owned by ${existingOwner}: ${asset.projectTargetPath}`
406
+ });
407
+ } else {
408
+ projectTargetOwners.set(asset.projectTargetPath, asset.id);
409
+ }
410
+ }
352
411
  if (asset.visibility !== "public" && asset.publishable) {
353
412
  issues.push({
354
413
  type: "non-public-publishable",
@@ -368,6 +427,13 @@ function validateAssetRegistry(registry, options = {}) {
368
427
  path: asset.sourcePath,
369
428
  message: `Asset source path does not exist: ${asset.sourcePath}`
370
429
  });
430
+ } else if (!isWithinAgentAssetsDir(options.agentAssetsDir, sourceAbsolutePath)) {
431
+ issues.push({
432
+ type: "source-target-escape",
433
+ id: asset.id,
434
+ path: asset.sourcePath,
435
+ message: `Asset source resolves outside agent-assets: ${asset.sourcePath}`
436
+ });
371
437
  } else if (asset.kind === "skill") {
372
438
  if (asset.sourceKind === "local-pack") {
373
439
  if (!isLocalSkillPack(sourceAbsolutePath)) {
@@ -424,6 +490,16 @@ function isSafeRegistrySourcePath(sourcePath) {
424
490
  function normalizeRegistrySourcePath(sourcePath) {
425
491
  return posix.normalize(sourcePath.replaceAll("\\", "/"));
426
492
  }
493
+ function isWithinAgentAssetsDir(agentAssetsDir, sourceAbsolutePath) {
494
+ try {
495
+ const agentAssetsRealPath = realpathSync(agentAssetsDir);
496
+ const sourceRealPath = realpathSync(sourceAbsolutePath);
497
+ const relativePath = relative3(agentAssetsRealPath, sourceRealPath);
498
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
499
+ } catch {
500
+ return false;
501
+ }
502
+ }
427
503
  function pathExistsEvenIfDanglingSymlink(path) {
428
504
  try {
429
505
  lstatSync(path);
@@ -434,6 +510,23 @@ function pathExistsEvenIfDanglingSymlink(path) {
434
510
  }
435
511
 
436
512
  // src/asset-registry/loader.ts
513
+ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
514
+ const selectedIds = new Set(selectedAssetIds);
515
+ const selectedAssets = registry.assets.filter((asset) => selectedIds.has(asset.id));
516
+ const canonicalRegistry = canonicalizeValue({
517
+ schemaVersion: registry.schemaVersion,
518
+ assets: [...selectedAssets].sort(
519
+ (left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0
520
+ )
521
+ });
522
+ const hash = createHash2("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
523
+ return {
524
+ schema: "agent-assets-registry",
525
+ version: registry.schemaVersion,
526
+ hash: `sha256:${hash}`,
527
+ assetCount: selectedAssets.length
528
+ };
529
+ }
437
530
  function loadAgentAssetRegistry(options = {}) {
438
531
  const agentAssetsDir = options.agentAssetsDir ?? findDefaultAgentAssetsDir();
439
532
  const registryPath = join5(agentAssetsDir, "registry.json");
@@ -458,6 +551,7 @@ function createAgentAssetLockEntries(registry, agentAssetsDir, assetIds) {
458
551
  return registry.assets.filter((asset) => !wantedIds || wantedIds.has(asset.id)).map((asset) => ({
459
552
  id: asset.id,
460
553
  sourcePath: asset.sourcePath,
554
+ delivery: asset.delivery ?? "symlink",
461
555
  contentHash: hashAgentAssetContent(asset, agentAssetsDir)
462
556
  })).sort((a, b) => a.id.localeCompare(b.id));
463
557
  }
@@ -467,7 +561,7 @@ function hashAgentAssetContent(asset, agentAssetsDir) {
467
561
  function hashAssetPathContent(sourceAbsolutePath) {
468
562
  const hash = createHash2("sha256");
469
563
  for (const filePath of listFiles3(sourceAbsolutePath)) {
470
- const relativePath = toUnixPath3(relative3(sourceAbsolutePath, filePath));
564
+ const relativePath = toUnixPath3(relative4(sourceAbsolutePath, filePath));
471
565
  hash.update(relativePath);
472
566
  hash.update("\0");
473
567
  hash.update(readFileSync3(filePath));
@@ -475,6 +569,15 @@ function hashAssetPathContent(sourceAbsolutePath) {
475
569
  }
476
570
  return `sha256:${hash.digest("hex")}`;
477
571
  }
572
+ function canonicalizeValue(value) {
573
+ if (Array.isArray(value)) return value.map((item) => canonicalizeValue(item));
574
+ if (value !== null && typeof value === "object") {
575
+ return Object.fromEntries(
576
+ Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, item]) => [key, canonicalizeValue(item)])
577
+ );
578
+ }
579
+ return value;
580
+ }
478
581
  function findDefaultAgentAssetsDir() {
479
582
  const packageRoot2 = findPackageRoot(dirname2(fileURLToPath2(import.meta.url)));
480
583
  const repoRoot = join5(packageRoot2, "..", "..");
@@ -620,20 +723,21 @@ function resolveSafePath(root, sourcePath) {
620
723
  }
621
724
 
622
725
  // src/asset-targets/apply.ts
726
+ import { createHash as createHash3 } from "node:crypto";
623
727
  import {
624
728
  existsSync as existsSync8,
625
729
  lstatSync as lstatSync3,
626
730
  mkdirSync as mkdirSync2,
627
731
  readlinkSync as readlinkSync2,
628
- realpathSync as realpathSync2,
732
+ realpathSync as realpathSync3,
629
733
  symlinkSync,
630
734
  unlinkSync,
631
735
  writeFileSync
632
736
  } from "node:fs";
633
- import { dirname as dirname4, join as join8, relative as relative4, resolve as resolve2 } from "node:path";
737
+ import { dirname as dirname4, join as join8, relative as relative5, resolve as resolve2 } from "node:path";
634
738
 
635
739
  // src/asset-targets/install-plan.ts
636
- import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync } from "node:fs";
740
+ import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
637
741
  import { basename, dirname as dirname3, join as join7, resolve } from "node:path";
638
742
  function createAssetInstallPlan(options) {
639
743
  const placement = options.placement ?? "registry";
@@ -653,9 +757,9 @@ function createAssetInstallPlan(options) {
653
757
  options.agentAssetsDir,
654
758
  assetIds
655
759
  );
760
+ const registryProvenance = createAgentAssetRegistryProvenance(options.registry, assetIds);
656
761
  const managedLock = readManagedLock(options.targetDir);
657
762
  const managedEntries = managedLock.entries;
658
- const managedTargets = new Set(managedEntries.map((entry) => entry.targetPath));
659
763
  const legacyAdoptions = createLegacyClaudeAdoptions({
660
764
  targetDir: options.targetDir,
661
765
  agentAssetsDir: options.agentAssetsDir,
@@ -671,7 +775,7 @@ function createAssetInstallPlan(options) {
671
775
  options.targetDir,
672
776
  options.host,
673
777
  placement,
674
- managedTargets
778
+ managedEntries
675
779
  )
676
780
  );
677
781
  const manifest = {
@@ -686,6 +790,7 @@ function createAssetInstallPlan(options) {
686
790
  host: options.host,
687
791
  placement,
688
792
  bundleIds: [...options.bundleIds],
793
+ registryProvenance,
689
794
  assets: lockEntries.map((entry) => {
690
795
  const action = assetActions.find(
691
796
  (candidate) => "assetId" in candidate && candidate.assetId === entry.id
@@ -747,7 +852,7 @@ function resolveAssetIds(bundleIds, explicitAssetIds, bundlesById) {
747
852
  }
748
853
  return [...ids].sort();
749
854
  }
750
- function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, managedTargets) {
855
+ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, managedEntries) {
751
856
  if (asset.kind === "skill" && asset.defaultScope === "user") {
752
857
  throw new Error(
753
858
  `User-scoped asset ${asset.id} must be linked at the user level, not installed into a project target.`
@@ -757,9 +862,23 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
757
862
  const targetPath = resolveHostTargetPath(asset, host, placement);
758
863
  const targetAbsolutePath = join7(targetDir, targetPath);
759
864
  const targetExists = pathExistsEvenIfDanglingSymlink2(targetAbsolutePath);
865
+ const managedEntry = managedEntries.find(
866
+ (entry) => entry.id === asset.id && entry.targetPath === targetPath
867
+ );
868
+ if ((asset.delivery ?? "symlink") === "snapshot") {
869
+ return createSnapshotAction({
870
+ asset,
871
+ agentAssetsDir,
872
+ sourcePath,
873
+ targetPath,
874
+ targetAbsolutePath,
875
+ targetExists,
876
+ managedEntry
877
+ });
878
+ }
760
879
  if (targetExists) {
761
880
  const stats = lstatSync2(targetAbsolutePath);
762
- if (stats.isSymbolicLink() && managedTargets.has(targetPath)) {
881
+ if (stats.isSymbolicLink() && managedEntry && (managedEntry.delivery ?? "symlink") === "symlink") {
763
882
  return {
764
883
  type: "update-symlink",
765
884
  assetId: asset.id,
@@ -767,7 +886,10 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
767
886
  targetPath
768
887
  };
769
888
  }
770
- if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync(targetAbsolutePath) === realpathSync(sourcePath)) {
889
+ if (managedEntry && managedEntry.delivery === "snapshot") {
890
+ throw new Error(`Refusing to replace managed snapshot with a symlink: ${targetPath}`);
891
+ }
892
+ if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
771
893
  return {
772
894
  type: "adopt-existing-symlink",
773
895
  assetId: asset.id,
@@ -784,7 +906,78 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
784
906
  targetPath
785
907
  };
786
908
  }
909
+ function createSnapshotAction(options) {
910
+ if (options.asset.kind !== "rule" || !isValidSnapshotProjectTargetPath(options.asset.projectTargetPath)) {
911
+ throw new Error(
912
+ `Snapshot delivery requires a rule target under docs/policy/shared-rules/: ${options.asset.id}`
913
+ );
914
+ }
915
+ if (!statSync3(options.sourcePath).isFile()) {
916
+ throw new Error(`Snapshot source must be a regular file: ${options.asset.sourcePath}`);
917
+ }
918
+ const content = readFileSync4(options.sourcePath);
919
+ const contentBase64 = content.toString("base64");
920
+ const contentHash = hashAgentAssetContent(options.asset, options.agentAssetsDir);
921
+ const managedDelivery = options.managedEntry?.delivery ?? "symlink";
922
+ if (!options.targetExists) {
923
+ return {
924
+ type: "snapshot",
925
+ assetId: options.asset.id,
926
+ targetPath: options.targetPath,
927
+ contentBase64,
928
+ contentHash
929
+ };
930
+ }
931
+ const stats = lstatSync2(options.targetAbsolutePath);
932
+ if (stats.isSymbolicLink()) {
933
+ if (existsSync7(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
934
+ return {
935
+ type: "migrate-symlink-to-snapshot",
936
+ assetId: options.asset.id,
937
+ sourcePath: options.sourcePath,
938
+ targetPath: options.targetPath,
939
+ contentBase64,
940
+ contentHash
941
+ };
942
+ }
943
+ throw new Error(`Refusing to overwrite unmanaged target: ${options.targetPath}`);
944
+ }
945
+ if (!stats.isFile()) {
946
+ throw new Error(`Refusing to overwrite unmanaged target: ${options.targetPath}`);
947
+ }
948
+ const currentTargetHash = hashAssetPathContent(options.targetAbsolutePath);
949
+ if (currentTargetHash === contentHash) {
950
+ return {
951
+ type: "adopt-snapshot",
952
+ assetId: options.asset.id,
953
+ targetPath: options.targetPath,
954
+ contentHash
955
+ };
956
+ }
957
+ if (managedDelivery === "snapshot" && options.managedEntry?.contentHash) {
958
+ if (currentTargetHash !== options.managedEntry.contentHash) {
959
+ throw new Error(`Refusing to overwrite locally drifted snapshot: ${options.targetPath}`);
960
+ }
961
+ return {
962
+ type: "update-snapshot",
963
+ assetId: options.asset.id,
964
+ targetPath: options.targetPath,
965
+ contentBase64,
966
+ contentHash,
967
+ expectedContentHash: options.managedEntry.contentHash
968
+ };
969
+ }
970
+ throw new Error(`Refusing to overwrite unmanaged target: ${options.targetPath}`);
971
+ }
787
972
  function resolveHostTargetPath(asset, _host, placement) {
973
+ if (asset.projectTargetPath !== void 0) {
974
+ if (!isValidAssetProjectTargetPath(asset.kind, asset.projectTargetPath)) {
975
+ throw new Error(
976
+ `Invalid project target path for asset ${asset.id}: ${asset.projectTargetPath}`
977
+ );
978
+ }
979
+ return asset.projectTargetPath;
980
+ }
788
981
  if (asset.kind === "skill") {
789
982
  const effectivePlacement = resolveSkillPlacement(asset, placement);
790
983
  if (effectivePlacement === "manual") {
@@ -818,7 +1011,7 @@ function readManagedLock(targetDir) {
818
1011
  return {
819
1012
  host: typeof lockfile.host === "string" ? lockfile.host : void 0,
820
1013
  entries: (lockfile.assets ?? []).filter(
821
- (entry) => typeof entry.id === "string" && typeof entry.sourcePath === "string" && typeof entry.targetPath === "string"
1014
+ (entry) => typeof entry.id === "string" && typeof entry.sourcePath === "string" && typeof entry.targetPath === "string" && (entry.delivery === void 0 || entry.delivery === "symlink" || entry.delivery === "snapshot") && (entry.contentHash === void 0 || typeof entry.contentHash === "string")
822
1015
  )
823
1016
  };
824
1017
  } catch {
@@ -855,7 +1048,7 @@ function createLegacyClaudeAdoptions(options) {
855
1048
  const targetAbsolutePath = join7(options.targetDir, targetPath);
856
1049
  const compatibilityRootPath = join7(options.targetDir, ".claude/skills");
857
1050
  const canonicalRootPath = join7(options.targetDir, ".agents/skills");
858
- if (!lstatSync2(canonicalRootPath).isDirectory() || !lstatSync2(compatibilityRootPath).isSymbolicLink() || readlinkSync(compatibilityRootPath) !== "../.agents/skills" || realpathSync(compatibilityRootPath) !== realpathSync(canonicalRootPath)) {
1051
+ if (!lstatSync2(canonicalRootPath).isDirectory() || !lstatSync2(compatibilityRootPath).isSymbolicLink() || readlinkSync(compatibilityRootPath) !== "../.agents/skills" || realpathSync2(compatibilityRootPath) !== realpathSync2(canonicalRootPath)) {
859
1052
  throw new Error(
860
1053
  `Legacy Claude compatibility root is not the exact canonical alias for ${entry.id}.`
861
1054
  );
@@ -868,7 +1061,7 @@ function createLegacyClaudeAdoptions(options) {
868
1061
  const legacyAbsolutePath = join7(options.targetDir, entry.targetPath);
869
1062
  const legacyStat = lstatSync2(legacyAbsolutePath);
870
1063
  const expectedSourcePath = join7(options.agentAssetsDir, entry.sourcePath);
871
- if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || realpathSync(targetAbsolutePath) !== realpathSync(expectedSourcePath)) {
1064
+ if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || realpathSync2(targetAbsolutePath) !== realpathSync2(expectedSourcePath)) {
872
1065
  throw new Error(`Legacy Claude skill target cannot be safely adopted: ${entry.targetPath}`);
873
1066
  }
874
1067
  const action = {
@@ -900,6 +1093,29 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
900
1093
  const targetAbsolutePath = join7(targetDir, entry.targetPath);
901
1094
  if (!pathExistsEvenIfDanglingSymlink2(targetAbsolutePath)) continue;
902
1095
  const stats = lstatSync2(targetAbsolutePath);
1096
+ if ((entry.delivery ?? "symlink") === "snapshot") {
1097
+ if (!isValidSnapshotProjectTargetPath(entry.targetPath)) {
1098
+ throw new Error(
1099
+ `Refusing to remove snapshot outside live shared rules: ${entry.targetPath}`
1100
+ );
1101
+ }
1102
+ if (!stats.isFile() || !entry.contentHash) {
1103
+ throw new Error(
1104
+ `Refusing to remove managed snapshot without its locked file hash: ${entry.targetPath}`
1105
+ );
1106
+ }
1107
+ const currentTargetHash = hashAssetPathContent(targetAbsolutePath);
1108
+ if (currentTargetHash !== entry.contentHash) {
1109
+ throw new Error(`Refusing to remove locally drifted managed snapshot: ${entry.targetPath}`);
1110
+ }
1111
+ actions.push({
1112
+ type: "remove-snapshot",
1113
+ assetId: entry.id,
1114
+ targetPath: entry.targetPath,
1115
+ expectedContentHash: entry.contentHash
1116
+ });
1117
+ continue;
1118
+ }
903
1119
  if (!stats.isSymbolicLink()) {
904
1120
  throw new Error(
905
1121
  `Refusing to remove path that is no longer a managed symlink: ${entry.targetPath}`
@@ -922,12 +1138,12 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
922
1138
  return actions.sort((a, b) => a.targetPath.localeCompare(b.targetPath));
923
1139
  }
924
1140
  function isManagedAssetTargetPath(path) {
925
- return /^(?:\.agents\/(?:skills|manual-skills)|\.pro-gov\/agent-assets\/(?:rules|commands))\/[^/.][^/]*$/.test(
1141
+ return /^(?:(?:\.agents\/(?:skills|manual-skills)|\.pro-gov\/agent-assets\/(?:rules|commands))\/[^./\\][^/\\]*|docs\/policy\/shared-rules\/[^./\\][^/\\]*\.md)$/.test(
926
1142
  path
927
1143
  );
928
1144
  }
929
1145
  function isLegacyClaudeSkillTargetPath(path) {
930
- return /^\.claude\/skills\/[^/.][^/]*$/.test(path);
1146
+ return /^\.claude\/skills\/[^./\\][^/\\]*$/.test(path);
931
1147
  }
932
1148
  function pathExistsEvenIfDanglingSymlink2(path) {
933
1149
  try {
@@ -944,6 +1160,9 @@ function applyAssetInstallPlan(plan) {
944
1160
  for (const action of plan.actions) {
945
1161
  if (action.type === "adopt-symlink") validateAdoptedSymlink(plan.targetDir, action);
946
1162
  if (action.type === "adopt-existing-symlink") validateExistingSymlink(plan.targetDir, action);
1163
+ if (action.type === "snapshot" || action.type === "update-snapshot" || action.type === "adopt-snapshot" || action.type === "migrate-symlink-to-snapshot" || action.type === "remove-snapshot") {
1164
+ validateSnapshotAction(plan.targetDir, action);
1165
+ }
947
1166
  }
948
1167
  for (const action of plan.actions) {
949
1168
  applyAction(plan.targetDir, action);
@@ -958,6 +1177,10 @@ function applyAction(targetDir, action) {
958
1177
  return;
959
1178
  }
960
1179
  if (action.type === "adopt-symlink" || action.type === "adopt-existing-symlink") return;
1180
+ if (action.type === "snapshot" || action.type === "update-snapshot" || action.type === "adopt-snapshot" || action.type === "migrate-symlink-to-snapshot" || action.type === "remove-snapshot") {
1181
+ applySnapshotAction(targetDir, action);
1182
+ return;
1183
+ }
961
1184
  if (action.type === "create-dir") {
962
1185
  mkdirSync2(targetAbsolutePath, { recursive: true });
963
1186
  return;
@@ -969,7 +1192,7 @@ function applyAction(targetDir, action) {
969
1192
  }
970
1193
  mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
971
1194
  const sourceAbsolutePath = resolve2(action.sourcePath);
972
- const symlinkTarget = relative4(realpathSync2(dirname4(targetAbsolutePath)), realpathSync2(sourceAbsolutePath)) || ".";
1195
+ const symlinkTarget = relative5(realpathSync3(dirname4(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
973
1196
  if (action.type === "symlink") {
974
1197
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
975
1198
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
@@ -993,10 +1216,71 @@ function validateExistingSymlink(targetDir, action) {
993
1216
  throw new Error(`Refusing unsafe existing symlink adoption: ${action.targetPath}`);
994
1217
  }
995
1218
  const targetAbsolutePath = join8(targetDir, action.targetPath);
996
- if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync2(targetAbsolutePath) !== realpathSync2(action.sourcePath)) {
1219
+ if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
997
1220
  throw new Error(`Existing skill symlink changed before apply: ${action.targetPath}`);
998
1221
  }
999
1222
  }
1223
+ function validateSnapshotAction(targetDir, action) {
1224
+ if (!isValidSnapshotProjectTargetPath(action.targetPath)) {
1225
+ throw new Error(`Refusing unsafe snapshot target: ${action.targetPath}`);
1226
+ }
1227
+ if ("contentBase64" in action && hashSnapshotBytes(Buffer.from(action.contentBase64, "base64")) !== action.contentHash) {
1228
+ throw new Error(`Snapshot content hash is invalid: ${action.targetPath}`);
1229
+ }
1230
+ const targetAbsolutePath = join8(targetDir, action.targetPath);
1231
+ if (action.type === "snapshot") {
1232
+ if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1233
+ throw new Error(`Snapshot target changed before apply: ${action.targetPath}`);
1234
+ }
1235
+ return;
1236
+ }
1237
+ if (action.type === "update-snapshot") {
1238
+ assertRegularSnapshotHash(targetAbsolutePath, action.expectedContentHash, action.targetPath);
1239
+ return;
1240
+ }
1241
+ if (action.type === "adopt-snapshot") {
1242
+ assertRegularSnapshotHash(targetAbsolutePath, action.contentHash, action.targetPath);
1243
+ return;
1244
+ }
1245
+ if (action.type === "migrate-symlink-to-snapshot") {
1246
+ if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync3(targetAbsolutePath).isSymbolicLink() || !existsSync8(targetAbsolutePath) || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
1247
+ throw new Error(`Snapshot symlink changed before apply: ${action.targetPath}`);
1248
+ }
1249
+ return;
1250
+ }
1251
+ assertRegularSnapshotHash(targetAbsolutePath, action.expectedContentHash, action.targetPath);
1252
+ }
1253
+ function applySnapshotAction(targetDir, action) {
1254
+ const targetAbsolutePath = join8(targetDir, action.targetPath);
1255
+ if (action.type === "adopt-snapshot") return;
1256
+ if (action.type === "remove-snapshot") {
1257
+ unlinkSync(targetAbsolutePath);
1258
+ return;
1259
+ }
1260
+ if (action.type === "migrate-symlink-to-snapshot") unlinkSync(targetAbsolutePath);
1261
+ mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1262
+ if (action.type === "snapshot" && pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1263
+ throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1264
+ }
1265
+ writeFileSync(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
1266
+ }
1267
+ function assertRegularSnapshotHash(targetAbsolutePath, expectedHash, targetPath) {
1268
+ if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1269
+ throw new Error(`Snapshot target changed before apply: ${targetPath}`);
1270
+ }
1271
+ const stats = lstatSync3(targetAbsolutePath);
1272
+ if (!stats.isFile() || hashAssetPathContent(targetAbsolutePath) !== expectedHash) {
1273
+ throw new Error(`Snapshot target hash changed before apply: ${targetPath}`);
1274
+ }
1275
+ }
1276
+ function hashSnapshotBytes(content) {
1277
+ const hash = createHash3("sha256");
1278
+ hash.update("");
1279
+ hash.update("\0");
1280
+ hash.update(content);
1281
+ hash.update("\0");
1282
+ return `sha256:${hash.digest("hex")}`;
1283
+ }
1000
1284
  function validateAdoptedSymlink(targetDir, action) {
1001
1285
  if (!isManagedAssetTargetPath(action.targetPath) || !isLegacyClaudeSkillTargetPath(action.legacyTargetPath) || action.compatibilityRootPath !== ".claude/skills" || action.expectedCompatibilityRawTarget !== "../.agents/skills") {
1002
1286
  throw new Error(`Refusing unsafe legacy Claude adoption: ${action.legacyTargetPath}`);
@@ -1005,14 +1289,14 @@ function validateAdoptedSymlink(targetDir, action) {
1005
1289
  const compatibilityRootPath = join8(targetDir, action.compatibilityRootPath);
1006
1290
  const targetAbsolutePath = join8(targetDir, action.targetPath);
1007
1291
  const legacyAbsolutePath = join8(targetDir, action.legacyTargetPath);
1008
- if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || readlinkSync2(compatibilityRootPath) !== action.expectedCompatibilityRawTarget || realpathSync2(compatibilityRootPath) !== realpathSync2(canonicalRootPath)) {
1292
+ if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || readlinkSync2(compatibilityRootPath) !== action.expectedCompatibilityRawTarget || realpathSync3(compatibilityRootPath) !== realpathSync3(canonicalRootPath)) {
1009
1293
  throw new Error(
1010
1294
  `Legacy Claude compatibility alias changed before apply: ${action.compatibilityRootPath}`
1011
1295
  );
1012
1296
  }
1013
1297
  const targetStat = lstatSync3(targetAbsolutePath);
1014
1298
  const legacyStat = lstatSync3(legacyAbsolutePath);
1015
- if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || targetStat.dev !== action.expectedDevice || targetStat.ino !== action.expectedInode || realpathSync2(targetAbsolutePath) !== realpathSync2(action.sourcePath)) {
1299
+ if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || targetStat.dev !== action.expectedDevice || targetStat.ino !== action.expectedInode || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
1016
1300
  throw new Error(`Legacy Claude skill target changed before apply: ${action.targetPath}`);
1017
1301
  }
1018
1302
  }
@@ -1045,7 +1329,7 @@ function pathExistsEvenIfDanglingSymlink3(path) {
1045
1329
  }
1046
1330
 
1047
1331
  // src/asset-targets/check.ts
1048
- import { existsSync as existsSync9, lstatSync as lstatSync4, readFileSync as readFileSync5 } from "node:fs";
1332
+ import { existsSync as existsSync9, lstatSync as lstatSync4, readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
1049
1333
  import { join as join9 } from "node:path";
1050
1334
  function checkInstalledAssets(options) {
1051
1335
  const lockfilePath = join9(options.targetDir, ".pro-gov/assets.lock.json");
@@ -1064,10 +1348,54 @@ function checkInstalledAssets(options) {
1064
1348
  const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
1065
1349
  const issues = [];
1066
1350
  const strictRegistry = options.strictRegistry ?? false;
1351
+ const selectedAssetIds = (lockfile.assets ?? []).map((entry) => entry.id);
1352
+ const hasRegistryProvenance2 = Object.prototype.hasOwnProperty.call(
1353
+ lockfile,
1354
+ "registryProvenance"
1355
+ );
1356
+ const registryProvenanceMismatch = strictRegistry && hasRegistryProvenance2 && !matchesRegistryProvenance(
1357
+ lockfile.registryProvenance,
1358
+ createAgentAssetRegistryProvenance(options.registry, selectedAssetIds)
1359
+ );
1360
+ if (registryProvenanceMismatch) {
1361
+ issues.push({
1362
+ type: "registry-provenance-mismatch",
1363
+ message: "Asset lock registry provenance does not match the registry available to this checker; registry-dependent checks were skipped."
1364
+ });
1365
+ }
1067
1366
  for (const entry of lockfile.assets ?? []) {
1367
+ if (!isManagedAssetTargetPath(entry.targetPath) && !isLegacyClaudeSkillTargetPath(entry.targetPath)) {
1368
+ issues.push({
1369
+ type: "unsafe-target-path",
1370
+ id: entry.id,
1371
+ targetPath: entry.targetPath,
1372
+ message: `Managed asset target is outside supported roots: ${entry.targetPath}`
1373
+ });
1374
+ continue;
1375
+ }
1068
1376
  const asset = registryById.get(entry.id);
1069
1377
  const targetAbsolutePath = join9(options.targetDir, entry.targetPath);
1070
- if (!asset && strictRegistry) {
1378
+ const delivery = entry.delivery ?? "symlink";
1379
+ const portableDeferredSkill = delivery === "symlink" && !strictRegistry && isProjectSkillTarget(entry.targetPath);
1380
+ if (delivery !== "symlink" && delivery !== "snapshot") {
1381
+ issues.push({
1382
+ type: "unsupported-delivery",
1383
+ id: entry.id,
1384
+ targetPath: entry.targetPath,
1385
+ message: `Managed asset delivery is unsupported: ${delivery}`
1386
+ });
1387
+ continue;
1388
+ }
1389
+ if (delivery === "snapshot" && !isValidSnapshotProjectTargetPath(entry.targetPath)) {
1390
+ issues.push({
1391
+ type: "unsafe-target-path",
1392
+ id: entry.id,
1393
+ targetPath: entry.targetPath,
1394
+ message: `Managed snapshot target is outside live shared rules: ${entry.targetPath}`
1395
+ });
1396
+ continue;
1397
+ }
1398
+ if (!asset && strictRegistry && !registryProvenanceMismatch) {
1071
1399
  issues.push({
1072
1400
  type: "unknown-asset",
1073
1401
  id: entry.id,
@@ -1075,7 +1403,7 @@ function checkInstalledAssets(options) {
1075
1403
  message: `Lockfile references unknown asset: ${entry.id}`
1076
1404
  });
1077
1405
  }
1078
- if (asset?.kind === "skill" && asset.defaultScope === "user") {
1406
+ if (!registryProvenanceMismatch && asset?.kind === "skill" && asset.defaultScope === "user") {
1079
1407
  issues.push({
1080
1408
  type: "user-scoped-asset-in-project-lock",
1081
1409
  id: entry.id,
@@ -1083,7 +1411,7 @@ function checkInstalledAssets(options) {
1083
1411
  message: `User-scoped skill is still locked into this project; move it to the user skill roots: ${entry.id}`
1084
1412
  });
1085
1413
  }
1086
- if (asset) {
1414
+ if (asset && !registryProvenanceMismatch) {
1087
1415
  const hostFolderIssue = checkHostFolder(
1088
1416
  lockfile.host,
1089
1417
  asset.kind,
@@ -1099,6 +1427,7 @@ function checkInstalledAssets(options) {
1099
1427
  }
1100
1428
  }
1101
1429
  if (!pathExistsEvenIfDanglingSymlink4(targetAbsolutePath)) {
1430
+ if (portableDeferredSkill) continue;
1102
1431
  issues.push({
1103
1432
  type: "missing-target",
1104
1433
  id: entry.id,
@@ -1108,7 +1437,16 @@ function checkInstalledAssets(options) {
1108
1437
  continue;
1109
1438
  }
1110
1439
  const targetStats = lstatSync4(targetAbsolutePath);
1111
- if (!targetStats.isSymbolicLink()) {
1440
+ if (delivery === "snapshot" && !targetStats.isFile()) {
1441
+ issues.push({
1442
+ type: "snapshot-not-regular-file",
1443
+ id: entry.id,
1444
+ targetPath: entry.targetPath,
1445
+ message: `Managed snapshot is not a regular file: ${entry.targetPath}`
1446
+ });
1447
+ continue;
1448
+ }
1449
+ if (delivery === "symlink" && !targetStats.isSymbolicLink()) {
1112
1450
  issues.push({
1113
1451
  type: "unmanaged-conflict",
1114
1452
  id: entry.id,
@@ -1117,7 +1455,8 @@ function checkInstalledAssets(options) {
1117
1455
  });
1118
1456
  continue;
1119
1457
  }
1120
- if (!existsSync9(targetAbsolutePath)) {
1458
+ if (delivery === "symlink" && !existsSync9(targetAbsolutePath)) {
1459
+ if (portableDeferredSkill) continue;
1121
1460
  issues.push({
1122
1461
  type: "dangling-symlink",
1123
1462
  id: entry.id,
@@ -1136,7 +1475,7 @@ function checkInstalledAssets(options) {
1136
1475
  message: `Managed asset hash drifted: ${entry.id}`
1137
1476
  });
1138
1477
  }
1139
- if (!asset || !strictRegistry) continue;
1478
+ if (!asset || !strictRegistry || registryProvenanceMismatch) continue;
1140
1479
  const sourceAbsolutePath = join9(options.agentAssetsDir, asset.sourcePath);
1141
1480
  if (!existsSync9(sourceAbsolutePath)) {
1142
1481
  issues.push({
@@ -1147,8 +1486,20 @@ function checkInstalledAssets(options) {
1147
1486
  });
1148
1487
  continue;
1149
1488
  }
1489
+ if (delivery === "symlink") {
1490
+ const expectedSource = realpathSync4(sourceAbsolutePath);
1491
+ const actualSource = realpathSync4(targetAbsolutePath);
1492
+ if (expectedSource !== actualSource) {
1493
+ issues.push({
1494
+ type: "symlink-source-drift",
1495
+ id: entry.id,
1496
+ targetPath: entry.targetPath,
1497
+ message: `Managed symlink does not resolve to its registered source: ${entry.targetPath}`
1498
+ });
1499
+ }
1500
+ }
1150
1501
  const currentSourceHash = hashAgentAssetContent(asset, options.agentAssetsDir);
1151
- if (targetHashMatchesLock && currentSourceHash !== entry.contentHash) {
1502
+ if ((delivery === "snapshot" || targetHashMatchesLock) && currentSourceHash !== entry.contentHash) {
1152
1503
  issues.push({
1153
1504
  type: "hash-drift",
1154
1505
  id: entry.id,
@@ -1157,9 +1508,16 @@ function checkInstalledAssets(options) {
1157
1508
  });
1158
1509
  }
1159
1510
  }
1160
- issues.push(...checkDuplicateSkillPlacements(options.targetDir, options.registry));
1511
+ if (!registryProvenanceMismatch) {
1512
+ issues.push(...checkDuplicateSkillPlacements(options.targetDir, options.registry));
1513
+ }
1161
1514
  return { targetDir: options.targetDir, issues };
1162
1515
  }
1516
+ function matchesRegistryProvenance(value, expected) {
1517
+ if (value === null || typeof value !== "object") return false;
1518
+ const provenance = value;
1519
+ return provenance.schema === expected.schema && provenance.version === expected.version && provenance.hash === expected.hash && provenance.assetCount === expected.assetCount;
1520
+ }
1163
1521
  function checkRegistryPlacement(lockfile, asset, targetPath) {
1164
1522
  if (lockfile.placement !== "registry") return void 0;
1165
1523
  if (asset.kind !== "skill") return void 0;
@@ -1235,6 +1593,9 @@ function pathExistsEvenIfDanglingSymlink4(path) {
1235
1593
  return false;
1236
1594
  }
1237
1595
  }
1596
+ function isProjectSkillTarget(targetPath) {
1597
+ return targetPath.startsWith(".agents/skills/") || targetPath.startsWith(".agents/manual-skills/");
1598
+ }
1238
1599
 
1239
1600
  // src/asset-targets/recommend.ts
1240
1601
  import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync6 } from "node:fs";
@@ -1301,13 +1662,6 @@ function recommendBundlesForTarget(targetDir) {
1301
1662
  reasons: signals.frontendSignals.map((signal) => `frontend dependency: ${signal}`)
1302
1663
  });
1303
1664
  }
1304
- if (signals.researchSignals.length > 0) {
1305
- recommendations.push({
1306
- bundleId: "research-docs",
1307
- confidence: "high",
1308
- reasons: signals.researchSignals
1309
- });
1310
- }
1311
1665
  if (signals.writingSignals.length > 0) {
1312
1666
  recommendations.push({
1313
1667
  bundleId: "novel-writing",
@@ -1877,11 +2231,12 @@ var REQUIRED_ASSETS = [
1877
2231
  "profiles/doc-only/profile.md",
1878
2232
  "docs/reference/adoption/migration-v1.1.md"
1879
2233
  ];
1880
- function runDoctor(_args) {
1881
- const assets = listAssets();
2234
+ function runPackageDoctor(_args, dependencies = {}) {
2235
+ const assets = dependencies.assets ?? listAssets("packaged");
1882
2236
  const assetPaths = new Set(assets.map((asset) => asset.path));
1883
2237
  const missing = REQUIRED_ASSETS.filter((assetPath) => !assetPaths.has(assetPath));
1884
- console.log("pro-gov doctor");
2238
+ const docGov = dependencies.docGov ?? checkDocGov();
2239
+ console.log("pro-gov package-doctor");
1885
2240
  console.log(`assets: ${assets.length}`);
1886
2241
  if (missing.length > 0) {
1887
2242
  for (const assetPath of missing) {
@@ -1890,29 +2245,39 @@ function runDoctor(_args) {
1890
2245
  } else {
1891
2246
  console.log("assets: required project-governance assets found");
1892
2247
  }
1893
- console.log(checkDocGov());
1894
- return missing.length > 0 ? 1 : 0;
2248
+ console.log(docGov.message);
2249
+ return missing.length > 0 || !docGov.ok ? 1 : 0;
2250
+ }
2251
+ function runDoctor(args) {
2252
+ return runPackageDoctor(args);
1895
2253
  }
1896
- function checkDocGov() {
1897
- const fromPath = spawnSync2("doc-gov", ["--help"], {
2254
+ function checkDocGov(options = {}) {
2255
+ const run = options.run ?? ((command2, args) => spawnSync2(command2, args, {
1898
2256
  encoding: "utf8",
1899
2257
  stdio: "ignore"
1900
- });
1901
- if (!fromPath.error && fromPath.status === 0) {
1902
- return "doc-gov: available on PATH";
1903
- }
1904
- const dependencyCli = resolveDocGovDependencyCli();
2258
+ }));
2259
+ const dependencyCli = (options.resolveDependencyCli ?? resolveDocGovDependencyCli)();
1905
2260
  if (!dependencyCli) {
1906
- return "doc-gov: not found; install @pieai/doc-gov beside @pieai/pro-gov for validation.";
2261
+ const fromPath = run("doc-gov", ["--help"]);
2262
+ if (!fromPath.error && fromPath.status === 0) {
2263
+ return {
2264
+ ok: false,
2265
+ message: "doc-gov: found only on PATH; the required local @pieai/doc-gov package dependency is unavailable."
2266
+ };
2267
+ }
2268
+ return {
2269
+ ok: false,
2270
+ message: "doc-gov: not found; install the required @pieai/doc-gov package dependency beside @pieai/pro-gov."
2271
+ };
1907
2272
  }
1908
- const fromDependency = spawnSync2(process.execPath, [dependencyCli, "--help"], {
1909
- encoding: "utf8",
1910
- stdio: "ignore"
1911
- });
2273
+ const fromDependency = run(process.execPath, [dependencyCli, "--help"]);
1912
2274
  if (!fromDependency.error && fromDependency.status === 0) {
1913
- return "doc-gov: available via package dependency";
2275
+ return { ok: true, message: "doc-gov: available via package dependency" };
1914
2276
  }
1915
- return `doc-gov: dependency found but returned status ${fromDependency.status ?? "unknown"}`;
2277
+ return {
2278
+ ok: false,
2279
+ message: `doc-gov: dependency found but returned status ${fromDependency.status ?? "unknown"}`
2280
+ };
1916
2281
  }
1917
2282
  function resolveDocGovDependencyCli() {
1918
2283
  try {
@@ -2077,11 +2442,11 @@ import {
2077
2442
  lstatSync as lstatSync6,
2078
2443
  mkdirSync as mkdirSync5,
2079
2444
  readdirSync as readdirSync7,
2080
- statSync as statSync3,
2445
+ statSync as statSync4,
2081
2446
  writeFileSync as writeFileSync4
2082
2447
  } from "node:fs";
2083
2448
  import { homedir } from "node:os";
2084
- import { dirname as dirname8, join as join14, relative as relative5, resolve as resolve3 } from "node:path";
2449
+ import { dirname as dirname8, join as join14, relative as relative6, resolve as resolve3 } from "node:path";
2085
2450
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2086
2451
  var ROOT_DEFINITIONS = [
2087
2452
  {
@@ -2504,7 +2869,7 @@ function fallbackMeasure(root) {
2504
2869
  if (entry.isDirectory()) pending.push(path);
2505
2870
  else if (entry.isFile()) {
2506
2871
  try {
2507
- bytes += statSync3(path).size;
2872
+ bytes += statSync4(path).size;
2508
2873
  } catch {
2509
2874
  }
2510
2875
  }
@@ -2522,7 +2887,7 @@ function countImmediateEntries(path) {
2522
2887
  }
2523
2888
  function displayPath(path, homePath) {
2524
2889
  const absolute = resolve3(path);
2525
- const withinHome = relative5(homePath, absolute);
2890
+ const withinHome = relative6(homePath, absolute);
2526
2891
  if (withinHome === "") return "~";
2527
2892
  if (!withinHome.startsWith("..")) return `~/${withinHome}`;
2528
2893
  return absolute;
@@ -3648,7 +4013,7 @@ function printUsage2() {
3648
4013
 
3649
4014
  // src/learning/recall.ts
3650
4015
  import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
3651
- import { basename as basename3, join as join15, relative as relative6 } from "node:path";
4016
+ import { basename as basename3, join as join15, relative as relative7 } from "node:path";
3652
4017
  function recallLearnings(root, options) {
3653
4018
  const query = options.query.trim();
3654
4019
  const terms = tokenize(query);
@@ -3703,7 +4068,7 @@ function readLearningRecord(root, absolutePath) {
3703
4068
  const parsed = splitFrontmatter(content);
3704
4069
  const body = parsed.body;
3705
4070
  return {
3706
- relativePath: normalizePath(relative6(root, absolutePath)),
4071
+ relativePath: normalizePath(relative7(root, absolutePath)),
3707
4072
  title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
3708
4073
  metadata: parsed.frontmatter,
3709
4074
  body
@@ -3785,7 +4150,7 @@ function cleanMarkdownLine(input) {
3785
4150
 
3786
4151
  // src/learning/capture.ts
3787
4152
  import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
3788
- import { basename as basename4, join as join16, relative as relative7 } from "node:path";
4153
+ import { basename as basename4, join as join16, relative as relative8 } from "node:path";
3789
4154
  function captureLearning(root, options) {
3790
4155
  const title = options.title.trim();
3791
4156
  const summary = options.summary.trim();
@@ -3799,7 +4164,7 @@ function captureLearning(root, options) {
3799
4164
  const idSlug = basename4(path, ".md");
3800
4165
  writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
3801
4166
  return {
3802
- relativePath: normalizePath2(relative7(root, path)),
4167
+ relativePath: normalizePath2(relative8(root, path)),
3803
4168
  title,
3804
4169
  captureMode: "pgs-native"
3805
4170
  };
@@ -4417,13 +4782,13 @@ function formatLink(link) {
4417
4782
  }
4418
4783
 
4419
4784
  // src/lens/scan.ts
4420
- import { spawnSync as spawnSync3 } from "node:child_process";
4421
- import { existsSync as existsSync20, readdirSync as readdirSync10, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
4785
+ import { spawnSync as spawnSync4 } from "node:child_process";
4786
+ import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync6 } from "node:fs";
4422
4787
  import { homedir as homedir3 } from "node:os";
4423
- import { join as join21, relative as relative8 } from "node:path";
4788
+ import { join as join22 } from "node:path";
4424
4789
 
4425
4790
  // src/host-ssot.ts
4426
- import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as realpathSync3 } from "node:fs";
4791
+ import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
4427
4792
  import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, resolve as resolve6 } from "node:path";
4428
4793
  function inspectProjectHostSsot(root) {
4429
4794
  const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
@@ -4473,7 +4838,7 @@ function inspectCanonicalPath(root, path) {
4473
4838
  if (!stat) return { path, status: "missing" };
4474
4839
  if (stat.isSymbolicLink()) {
4475
4840
  try {
4476
- realpathSync3(absolutePath);
4841
+ realpathSync5(absolutePath);
4477
4842
  return { path, status: "symlink" };
4478
4843
  } catch {
4479
4844
  return { path, status: "dangling-symlink" };
@@ -4507,7 +4872,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4507
4872
  }
4508
4873
  let targetMatches = false;
4509
4874
  try {
4510
- targetMatches = realpathSync3(absolutePath) === realpathSync3(expectedPath);
4875
+ targetMatches = realpathSync5(absolutePath) === realpathSync5(expectedPath);
4511
4876
  } catch {
4512
4877
  return { ...base, rawTarget, resolvedTarget, status: "dangling-symlink" };
4513
4878
  }
@@ -4542,7 +4907,7 @@ function safeLstat2(path) {
4542
4907
  }
4543
4908
 
4544
4909
  // src/portfolio/redundancy.ts
4545
- import { existsSync as existsSync18, readdirSync as readdirSync9, statSync as statSync4 } from "node:fs";
4910
+ import { existsSync as existsSync18, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
4546
4911
  import { homedir as homedir2 } from "node:os";
4547
4912
  import { join as join19 } from "node:path";
4548
4913
  var DEFAULT_CACHE_THRESHOLD_BYTES = 1e9;
@@ -4657,7 +5022,7 @@ function collectDirectoryStats(root) {
4657
5022
  } else if (entry.isFile()) {
4658
5023
  fileCount += 1;
4659
5024
  try {
4660
- bytes += statSync4(path).size;
5025
+ bytes += statSync5(path).size;
4661
5026
  } catch {
4662
5027
  }
4663
5028
  }
@@ -4698,6 +5063,111 @@ function readPackageJson(path) {
4698
5063
  }
4699
5064
  }
4700
5065
 
5066
+ // src/repository-files.ts
5067
+ import { spawnSync as spawnSync3 } from "node:child_process";
5068
+ import { existsSync as existsSync20, readdirSync as readdirSync10 } from "node:fs";
5069
+ import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative9 } from "node:path";
5070
+ var gitMaxBufferBytes = 64 * 1024 * 1024;
5071
+ var RepositoryFileDiscoveryError = class extends Error {
5072
+ constructor(message) {
5073
+ super(message);
5074
+ this.name = "RepositoryFileDiscoveryError";
5075
+ }
5076
+ };
5077
+ function discoverRepositoryFiles(root, options = {}) {
5078
+ const probe = runGit(root, ["rev-parse", "--is-inside-work-tree"]);
5079
+ if (!probe.ok) {
5080
+ if (!probe.notRepository || existsSync20(join21(root, ".git"))) {
5081
+ throw new RepositoryFileDiscoveryError(probe.message);
5082
+ }
5083
+ return {
5084
+ source: "filesystem",
5085
+ files: discoverFilesystemFiles(root, options)
5086
+ };
5087
+ }
5088
+ if (probe.stdout.trim() !== "true") {
5089
+ throw new RepositoryFileDiscoveryError(
5090
+ "Git repository discovery failed: target is a Git repository without a worktree."
5091
+ );
5092
+ }
5093
+ const gitFiles = runGit(root, [
5094
+ "ls-files",
5095
+ "--cached",
5096
+ "--others",
5097
+ "--exclude-standard",
5098
+ "-z",
5099
+ ...options.gitPathspecs?.length ? ["--", ...options.gitPathspecs] : []
5100
+ ]);
5101
+ if (!gitFiles.ok) throw new RepositoryFileDiscoveryError(gitFiles.message);
5102
+ const files = /* @__PURE__ */ new Set();
5103
+ for (const path of gitFiles.stdout.split("\0")) {
5104
+ if (!path) continue;
5105
+ const normalized = normalizeRepositoryRelativePath(path);
5106
+ if (!isSafeRepositoryRelativePath(normalized)) {
5107
+ throw new RepositoryFileDiscoveryError(
5108
+ `Git returned a path outside the repository boundary: ${path}`
5109
+ );
5110
+ }
5111
+ if (existsSync20(join21(root, normalized))) files.add(normalized);
5112
+ }
5113
+ return { source: "git", files: [...files].sort() };
5114
+ }
5115
+ function normalizeRepositoryRelativePath(path) {
5116
+ return posix3.normalize(path.replaceAll("\\", "/").replace(/^\.\/+/, ""));
5117
+ }
5118
+ function discoverFilesystemFiles(root, options) {
5119
+ const files = /* @__PURE__ */ new Set();
5120
+ const maxDepth = options.fallbackMaxDepth ?? Number.POSITIVE_INFINITY;
5121
+ const ignoredDirectories2 = options.fallbackIgnoredDirectories ?? /* @__PURE__ */ new Set();
5122
+ const visit = (directory, depth) => {
5123
+ if (depth > maxDepth || !existsSync20(directory)) return;
5124
+ let entries;
5125
+ try {
5126
+ entries = readdirSync10(directory, { withFileTypes: true });
5127
+ } catch {
5128
+ return;
5129
+ }
5130
+ for (const entry of entries) {
5131
+ const absolutePath = join21(directory, entry.name);
5132
+ if (entry.isDirectory()) {
5133
+ if (!ignoredDirectories2.has(entry.name)) visit(absolutePath, depth + 1);
5134
+ continue;
5135
+ }
5136
+ if (!entry.isFile()) continue;
5137
+ const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
5138
+ if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
5139
+ files.add(relativePath);
5140
+ }
5141
+ }
5142
+ };
5143
+ visit(root, 0);
5144
+ return [...files].sort();
5145
+ }
5146
+ function isSafeRepositoryRelativePath(path) {
5147
+ return path !== "" && path !== "." && !isAbsolute5(path) && !/^[a-zA-Z]:\//.test(path) && path !== ".." && !path.startsWith("../");
5148
+ }
5149
+ function runGit(root, args) {
5150
+ const result = spawnSync3("git", ["-C", root, ...args], {
5151
+ encoding: "utf8",
5152
+ maxBuffer: gitMaxBufferBytes,
5153
+ env: { ...process.env, LANG: "C", LC_ALL: "C" }
5154
+ });
5155
+ if (result.error) {
5156
+ return {
5157
+ ok: false,
5158
+ notRepository: false,
5159
+ message: `Git repository discovery failed: ${result.error.message}`
5160
+ };
5161
+ }
5162
+ if (result.status === 0) return { ok: true, stdout: result.stdout };
5163
+ const stderr = result.stderr.trim();
5164
+ return {
5165
+ ok: false,
5166
+ notRepository: /not a git repository/i.test(stderr),
5167
+ message: `Git repository discovery failed (${result.status ?? "unknown status"}): ${stderr || "no diagnostic output"}`
5168
+ };
5169
+ }
5170
+
4701
5171
  // src/lens/scan.ts
4702
5172
  var ignoredDirectories = /* @__PURE__ */ new Set([".git", ".next", ".turbo", "dist", "node_modules", "coverage"]);
4703
5173
  function scanProjectLensTarget(targetDir, options = {}) {
@@ -4714,7 +5184,7 @@ function scanProjectLensTarget(targetDir, options = {}) {
4714
5184
  includedFileCount: files.length,
4715
5185
  excludedFileCount: candidateFiles.length - files.length
4716
5186
  },
4717
- aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync20(join21(targetDir, file))),
5187
+ aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync21(join22(targetDir, file))),
4718
5188
  aiConfigFiles: [],
4719
5189
  hostSsot: inspectProjectHostSsot(targetDir),
4720
5190
  userHostSsot: inspectUserSkillsSsot(options.homeDir ?? process.env.HOME ?? homedir3()),
@@ -4725,17 +5195,17 @@ function scanProjectLensTarget(targetDir, options = {}) {
4725
5195
  }),
4726
5196
  packageJson,
4727
5197
  docs: {
4728
- hasDocsDirectory: existsSync20(join21(targetDir, "docs")),
5198
+ hasDocsDirectory: existsSync21(join22(targetDir, "docs")),
4729
5199
  markdownFileCount: markdownFiles.length,
4730
5200
  governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
4731
5201
  },
4732
5202
  git: readGitState(targetDir),
4733
- largeFiles: files.map((file) => ({ path: file, bytes: statSync5(join21(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
5203
+ largeFiles: files.map((file) => ({ path: file, bytes: statSync6(join22(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
4734
5204
  };
4735
5205
  }
4736
5206
  function readPackageJson2(targetDir) {
4737
- const packageJsonPath = join21(targetDir, "package.json");
4738
- if (!existsSync20(packageJsonPath)) return void 0;
5207
+ const packageJsonPath = join22(targetDir, "package.json");
5208
+ if (!existsSync21(packageJsonPath)) return void 0;
4739
5209
  try {
4740
5210
  const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
4741
5211
  return {
@@ -4748,10 +5218,10 @@ function readPackageJson2(targetDir) {
4748
5218
  }
4749
5219
  }
4750
5220
  function readGitState(targetDir) {
4751
- const branch = runGit(targetDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
5221
+ const branch = runGit2(targetDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
4752
5222
  if (!branch.ok) return { available: false };
4753
- const head = runGit(targetDir, ["log", "-1", "--format=%H %s"]);
4754
- const status = runGit(targetDir, ["status", "-sb"]);
5223
+ const head = runGit2(targetDir, ["log", "-1", "--format=%H %s"]);
5224
+ const status = runGit2(targetDir, ["status", "-sb"]);
4755
5225
  return {
4756
5226
  available: true,
4757
5227
  branch: branch.stdout,
@@ -4759,27 +5229,18 @@ function readGitState(targetDir) {
4759
5229
  statusShort: status.ok ? status.stdout : void 0
4760
5230
  };
4761
5231
  }
4762
- function runGit(targetDir, args) {
4763
- const result = spawnSync3("git", ["-C", targetDir, ...args], {
4764
- encoding: "utf8"
5232
+ function runGit2(targetDir, args) {
5233
+ const result = spawnSync4("git", ["-C", targetDir, ...args], {
5234
+ encoding: "utf8",
5235
+ maxBuffer: 64 * 1024 * 1024
4765
5236
  });
4766
5237
  if (result.status !== 0) return { ok: false };
4767
5238
  return { ok: true, stdout: result.stdout.trim() };
4768
5239
  }
4769
5240
  function listProjectFiles(targetDir) {
4770
- const gitFiles = runGit(targetDir, [
4771
- "ls-files",
4772
- "--cached",
4773
- "--others",
4774
- "--exclude-standard",
4775
- "-z"
4776
- ]);
4777
- if (gitFiles.ok) {
4778
- return gitFiles.stdout.split("\0").filter(Boolean).map(toUnixPath4).filter((file) => existsSync20(join21(targetDir, file))).sort();
4779
- }
4780
- const files = [];
4781
- collectFiles2(targetDir, targetDir, files);
4782
- return files.sort();
5241
+ return discoverRepositoryFiles(targetDir, {
5242
+ fallbackIgnoredDirectories: ignoredDirectories
5243
+ }).files;
4783
5244
  }
4784
5245
  var excludedEvidencePrefixes = [
4785
5246
  ".agents/manual-skills/",
@@ -4792,20 +5253,6 @@ var excludedEvidencePrefixes = [
4792
5253
  function isFirstPartyEvidenceFile(file) {
4793
5254
  return !excludedEvidencePrefixes.some((prefix) => file.startsWith(prefix));
4794
5255
  }
4795
- function collectFiles2(rootDir, currentDir, files) {
4796
- if (!existsSync20(currentDir)) return;
4797
- for (const entry of readdirSync10(currentDir, { withFileTypes: true })) {
4798
- if (entry.isDirectory()) {
4799
- if (ignoredDirectories.has(entry.name)) continue;
4800
- collectFiles2(rootDir, join21(currentDir, entry.name), files);
4801
- } else if (entry.isFile()) {
4802
- files.push(toUnixPath4(relative8(rootDir, join21(currentDir, entry.name))));
4803
- }
4804
- }
4805
- }
4806
- function toUnixPath4(path) {
4807
- return path.replaceAll("\\", "/");
4808
- }
4809
5256
 
4810
5257
  // src/commands/lens.ts
4811
5258
  function runLens(args) {
@@ -4957,19 +5404,19 @@ function printUsage4() {
4957
5404
  }
4958
5405
 
4959
5406
  // src/commands/portfolio.ts
4960
- import { existsSync as existsSync32, readFileSync as readFileSync18 } from "node:fs";
4961
- import { join as join33 } from "node:path";
5407
+ import { existsSync as existsSync33, readFileSync as readFileSync18 } from "node:fs";
5408
+ import { join as join34 } from "node:path";
4962
5409
 
4963
5410
  // src/portfolio/doctor.ts
4964
- import { spawnSync as spawnSync6 } from "node:child_process";
4965
- import { existsSync as existsSync23, readFileSync as readFileSync16 } from "node:fs";
5411
+ import { spawnSync as spawnSync7 } from "node:child_process";
5412
+ import { existsSync as existsSync24, readFileSync as readFileSync16 } from "node:fs";
4966
5413
  import { createRequire as createRequire2 } from "node:module";
4967
5414
  import { homedir as homedir4 } from "node:os";
4968
- import { dirname as dirname15, join as join24 } from "node:path";
5415
+ import { dirname as dirname15, join as join25 } from "node:path";
4969
5416
  import { fileURLToPath as fileURLToPath4 } from "node:url";
4970
5417
 
4971
5418
  // src/host-tooling/inventory.ts
4972
- import { spawnSync as spawnSync4 } from "node:child_process";
5419
+ import { spawnSync as spawnSync5 } from "node:child_process";
4973
5420
  function inspectHostTooling(requirements, runner = defaultRunner2) {
4974
5421
  const hosts = [];
4975
5422
  const issues = [];
@@ -5043,7 +5490,7 @@ function parseHostPlugins(host, value) {
5043
5490
  });
5044
5491
  }
5045
5492
  function defaultRunner2({ command: command2 }) {
5046
- const result = spawnSync4(command2[0] ?? "", command2.slice(1), {
5493
+ const result = spawnSync5(command2[0] ?? "", command2.slice(1), {
5047
5494
  encoding: "utf8",
5048
5495
  timeout: 1e4
5049
5496
  });
@@ -5058,8 +5505,8 @@ function isRecord2(value) {
5058
5505
  }
5059
5506
 
5060
5507
  // src/portfolio/asset-state.ts
5061
- import { existsSync as existsSync21, lstatSync as lstatSync8, readFileSync as readFileSync14 } from "node:fs";
5062
- import { join as join22 } from "node:path";
5508
+ import { existsSync as existsSync22, lstatSync as lstatSync8, readFileSync as readFileSync14 } from "node:fs";
5509
+ import { join as join23 } from "node:path";
5063
5510
  function comparePortfolioAssetState(options) {
5064
5511
  const expectedManifest = readPlanDocument(
5065
5512
  options.expectedPlan,
@@ -5070,10 +5517,10 @@ function comparePortfolioAssetState(options) {
5070
5517
  ".pro-gov/assets.lock.json"
5071
5518
  );
5072
5519
  const currentManifest = readJsonFile(
5073
- join22(options.targetDir, ".pro-gov/assets.json")
5520
+ join23(options.targetDir, ".pro-gov/assets.json")
5074
5521
  );
5075
5522
  const currentLock = readJsonFile(
5076
- join22(options.targetDir, ".pro-gov/assets.lock.json")
5523
+ join23(options.targetDir, ".pro-gov/assets.lock.json")
5077
5524
  );
5078
5525
  const issues = [];
5079
5526
  if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
@@ -5101,7 +5548,7 @@ function comparePortfolioAssetState(options) {
5101
5548
  (action) => action.type === "adopt-symlink" && action.assetId === entry.id && action.legacyTargetPath === entry.targetPath
5102
5549
  ))
5103
5550
  continue;
5104
- const targetAbsolutePath = join22(options.targetDir, entry.targetPath);
5551
+ const targetAbsolutePath = join23(options.targetDir, entry.targetPath);
5105
5552
  if (!pathIsSymlink(targetAbsolutePath)) continue;
5106
5553
  issues.push({
5107
5554
  type: "orphaned-managed-symlink",
@@ -5123,7 +5570,7 @@ function readPlanDocument(plan, targetPath) {
5123
5570
  }
5124
5571
  }
5125
5572
  function readJsonFile(path) {
5126
- if (!existsSync21(path)) return void 0;
5573
+ if (!existsSync22(path)) return void 0;
5127
5574
  try {
5128
5575
  return JSON.parse(readFileSync14(path, "utf8"));
5129
5576
  } catch {
@@ -5134,7 +5581,12 @@ function sameStrings(left, right) {
5134
5581
  return JSON.stringify([...left ?? []].sort()) === JSON.stringify([...right ?? []].sort());
5135
5582
  }
5136
5583
  function sameLock(left, right) {
5137
- return JSON.stringify(normalizeLock(left)) === JSON.stringify(normalizeLock(right));
5584
+ if (JSON.stringify(normalizeLock(left)) !== JSON.stringify(normalizeLock(right))) return false;
5585
+ if (!hasRegistryProvenance(left) || !hasRegistryProvenance(right)) return true;
5586
+ return JSON.stringify(left.registryProvenance) === JSON.stringify(right.registryProvenance);
5587
+ }
5588
+ function hasRegistryProvenance(lock) {
5589
+ return lock !== void 0 && Object.prototype.hasOwnProperty.call(lock, "registryProvenance");
5138
5590
  }
5139
5591
  function normalizeLock(lock) {
5140
5592
  return {
@@ -5153,9 +5605,9 @@ function pathIsSymlink(path) {
5153
5605
  }
5154
5606
 
5155
5607
  // src/portfolio/version-policy.ts
5156
- import { spawnSync as spawnSync5 } from "node:child_process";
5157
- import { existsSync as existsSync22, readFileSync as readFileSync15, readdirSync as readdirSync11 } from "node:fs";
5158
- import { dirname as dirname14, join as join23 } from "node:path";
5608
+ import { spawnSync as spawnSync6 } from "node:child_process";
5609
+ import { existsSync as existsSync23, lstatSync as lstatSync9, readFileSync as readFileSync15 } from "node:fs";
5610
+ import { dirname as dirname14, join as join24 } from "node:path";
5159
5611
  function inspectVersionPolicy(root, policy, projectType) {
5160
5612
  if (!policy) return { status: "compliant", packages: [], runtimes: [], attentionCount: 0 };
5161
5613
  const packageManifests = collectPackageManifests(root);
@@ -5245,7 +5697,7 @@ function inspectRuntime(expectedName, expectedVersion) {
5245
5697
  function readRuntimeVersion(name) {
5246
5698
  if (name === "node") return process.versions.node;
5247
5699
  if (name !== "deno") return void 0;
5248
- const result = spawnSync5("deno", ["--version"], { encoding: "utf8" });
5700
+ const result = spawnSync6("deno", ["--version"], { encoding: "utf8" });
5249
5701
  if (result.status !== 0) return void 0;
5250
5702
  return /^deno\s+(\d+\.\d+\.\d+)/m.exec(result.stdout)?.[1];
5251
5703
  }
@@ -5264,7 +5716,7 @@ function findDeclaredVersion(packageJson, name) {
5264
5716
  function readInstalledVersion(root, name, fromDirectory = root) {
5265
5717
  let current = fromDirectory;
5266
5718
  while (true) {
5267
- const packageJson = readJson2(join23(current, "node_modules", name, "package.json"));
5719
+ const packageJson = readJson2(join24(current, "node_modules", name, "package.json"));
5268
5720
  if (typeof packageJson?.version === "string") return packageJson.version;
5269
5721
  if (current === root) return void 0;
5270
5722
  const parent = dirname14(current);
@@ -5273,7 +5725,6 @@ function readInstalledVersion(root, name, fromDirectory = root) {
5273
5725
  }
5274
5726
  }
5275
5727
  function collectPackageManifests(root) {
5276
- const manifests = [];
5277
5728
  const ignored = /* @__PURE__ */ new Set([
5278
5729
  ".git",
5279
5730
  ".next",
@@ -5297,37 +5748,36 @@ function collectPackageManifests(root) {
5297
5748
  ".pnpm-store",
5298
5749
  ".tmp-repos"
5299
5750
  ]);
5300
- const visit = (directory, depth) => {
5301
- if (depth > 6) return;
5302
- let entries;
5751
+ const manifests = /* @__PURE__ */ new Map();
5752
+ const addManifest = (relativePath) => {
5753
+ const directorySegments = relativePath.split("/").slice(0, -1);
5754
+ if (relativePath.split("/").at(-1) !== "package.json" || directorySegments.length > 6 || directorySegments.some((segment) => ignored.has(segment))) {
5755
+ return;
5756
+ }
5757
+ const path = join24(root, relativePath);
5303
5758
  try {
5304
- entries = readdirSync11(directory, { withFileTypes: true });
5759
+ if (!lstatSync9(path).isFile()) return;
5305
5760
  } catch {
5306
5761
  return;
5307
5762
  }
5308
- for (const entry of entries) {
5309
- const path = join23(directory, entry.name);
5310
- if (entry.isFile() && entry.name === "package.json") {
5311
- const packageJson = readJson2(path);
5312
- if (packageJson)
5313
- manifests.push({
5314
- path: path.slice(root.length + 1) || "package.json",
5315
- directory,
5316
- packageJson
5317
- });
5318
- } else if (entry.isDirectory() && !ignored.has(entry.name)) {
5319
- visit(path, depth + 1);
5320
- }
5321
- }
5763
+ const packageJson = readJson2(path);
5764
+ if (!packageJson) return;
5765
+ manifests.set(relativePath, { path: relativePath, directory: dirname14(path), packageJson });
5322
5766
  };
5323
- visit(root, 0);
5324
- return manifests.sort((a, b) => a.path.localeCompare(b.path));
5767
+ const discovery = discoverRepositoryFiles(root, {
5768
+ gitPathspecs: ["package.json", ":(glob)**/package.json"],
5769
+ fallbackIgnoredDirectories: ignored,
5770
+ fallbackMaxDepth: 6,
5771
+ fallbackIncludeFile: (path) => path.split("/").at(-1) === "package.json"
5772
+ });
5773
+ for (const relativePath of discovery.files) addManifest(relativePath);
5774
+ return [...manifests.values()].sort((a, b) => a.path.localeCompare(b.path));
5325
5775
  }
5326
5776
  function unique(values) {
5327
5777
  return [...new Set(values)];
5328
5778
  }
5329
5779
  function readJson2(path) {
5330
- if (!existsSync22(path)) return void 0;
5780
+ if (!existsSync23(path)) return void 0;
5331
5781
  try {
5332
5782
  return JSON.parse(readFileSync15(path, "utf8"));
5333
5783
  } catch {
@@ -5362,12 +5812,12 @@ function inspectTarget(options) {
5362
5812
  const { target } = options;
5363
5813
  const hostSsot = inspectProjectHostSsot(target.path);
5364
5814
  const issues = [];
5365
- const packageJson = readJson3(join24(target.path, "package.json"));
5815
+ const packageJson = readJson3(join25(target.path, "package.json"));
5366
5816
  const packages = {};
5367
5817
  for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
5368
5818
  const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
5369
5819
  const installedPackage = readJson3(
5370
- join24(target.path, "node_modules", packageName, "package.json")
5820
+ join25(target.path, "node_modules", packageName, "package.json")
5371
5821
  );
5372
5822
  const installed = installedPackage?.version;
5373
5823
  const expected = options.expectedPackageVersions[packageName];
@@ -5427,7 +5877,7 @@ function inspectTarget(options) {
5427
5877
  type: "asset-lock-drift",
5428
5878
  message: error instanceof Error ? error.message : String(error)
5429
5879
  });
5430
- if (!existsSync23(join24(target.path, ".pro-gov/assets.json"))) {
5880
+ if (!existsSync24(join25(target.path, ".pro-gov/assets.json"))) {
5431
5881
  issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
5432
5882
  }
5433
5883
  }
@@ -5445,15 +5895,15 @@ function inspectTarget(options) {
5445
5895
  };
5446
5896
  }
5447
5897
  function readTargetAssetHost(targetDir) {
5448
- const lockfile = readJson3(join24(targetDir, ".pro-gov/assets.lock.json"));
5898
+ const lockfile = readJson3(join25(targetDir, ".pro-gov/assets.lock.json"));
5449
5899
  return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
5450
5900
  }
5451
5901
  function isAssetRegistryHost(value) {
5452
5902
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
5453
5903
  }
5454
5904
  function runTargetChecks(target) {
5455
- const proGovCli = join24(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
5456
- const docGovCli = join24(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
5905
+ const proGovCli = join25(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
5906
+ const docGovCli = join25(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
5457
5907
  const commands = [
5458
5908
  {
5459
5909
  name: "pro-gov doctor",
@@ -5464,8 +5914,8 @@ function runTargetChecks(target) {
5464
5914
  { name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
5465
5915
  ];
5466
5916
  return commands.map((command2) => {
5467
- if (!existsSync23(command2.cli)) return { name: command2.name, status: null };
5468
- const result = spawnSync6(process.execPath, [command2.cli, ...command2.args], {
5917
+ if (!existsSync24(command2.cli)) return { name: command2.name, status: null };
5918
+ const result = spawnSync7(process.execPath, [command2.cli, ...command2.args], {
5469
5919
  cwd: target.path,
5470
5920
  encoding: "utf8",
5471
5921
  timeout: 3e4
@@ -5474,13 +5924,13 @@ function runTargetChecks(target) {
5474
5924
  });
5475
5925
  }
5476
5926
  function inspectGit(path) {
5477
- const inside = spawnSync6("git", ["rev-parse", "--is-inside-work-tree"], {
5927
+ const inside = spawnSync7("git", ["rev-parse", "--is-inside-work-tree"], {
5478
5928
  cwd: path,
5479
5929
  encoding: "utf8"
5480
5930
  });
5481
5931
  if (inside.status !== 0) return { isRepository: false, dirty: false };
5482
- const status = spawnSync6("git", ["status", "--porcelain"], { cwd: path, encoding: "utf8" });
5483
- const branch = spawnSync6("git", ["branch", "--show-current"], { cwd: path, encoding: "utf8" });
5932
+ const status = spawnSync7("git", ["status", "--porcelain"], { cwd: path, encoding: "utf8" });
5933
+ const branch = spawnSync7("git", ["branch", "--show-current"], { cwd: path, encoding: "utf8" });
5484
5934
  return {
5485
5935
  isRepository: true,
5486
5936
  dirty: status.stdout.trim().length > 0,
@@ -5505,14 +5955,14 @@ function getExpectedPackageVersions() {
5505
5955
  function findOwnPackageJson() {
5506
5956
  let current = dirname15(fileURLToPath4(import.meta.url));
5507
5957
  for (let depth = 0; depth < 5; depth += 1) {
5508
- const candidate = join24(current, "package.json");
5509
- if (existsSync23(candidate)) return candidate;
5958
+ const candidate = join25(current, "package.json");
5959
+ if (existsSync24(candidate)) return candidate;
5510
5960
  current = dirname15(current);
5511
5961
  }
5512
5962
  return "";
5513
5963
  }
5514
5964
  function readJson3(path) {
5515
- if (!path || !existsSync23(path)) return void 0;
5965
+ if (!path || !existsSync24(path)) return void 0;
5516
5966
  try {
5517
5967
  return JSON.parse(readFileSync16(path, "utf8"));
5518
5968
  } catch {
@@ -5531,18 +5981,18 @@ function deduplicateIssues(issues) {
5531
5981
 
5532
5982
  // src/portfolio/ai-health/index.ts
5533
5983
  import { homedir as homedir5 } from "node:os";
5534
- import { dirname as dirname17, join as join32, resolve as resolve9 } from "node:path";
5984
+ import { dirname as dirname17, join as join33, resolve as resolve9 } from "node:path";
5535
5985
 
5536
5986
  // src/portfolio/ai-health/entries.ts
5537
- import { existsSync as existsSync25, lstatSync as lstatSync10, realpathSync as realpathSync5 } from "node:fs";
5538
- import { join as join25 } from "node:path";
5987
+ import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync7 } from "node:fs";
5988
+ import { join as join26 } from "node:path";
5539
5989
 
5540
5990
  // src/portfolio/ai-health/shared.ts
5541
5991
  import { execFileSync as execFileSync2 } from "node:child_process";
5542
- import { existsSync as existsSync24, lstatSync as lstatSync9, readFileSync as readFileSync17, readdirSync as readdirSync12, realpathSync as realpathSync4, statSync as statSync6 } from "node:fs";
5992
+ import { existsSync as existsSync25, lstatSync as lstatSync10, readFileSync as readFileSync17, readdirSync as readdirSync11, realpathSync as realpathSync6, statSync as statSync7 } from "node:fs";
5543
5993
  function safeRealpath(path) {
5544
5994
  try {
5545
- return realpathSync4(path);
5995
+ return realpathSync6(path);
5546
5996
  } catch {
5547
5997
  return void 0;
5548
5998
  }
@@ -5569,7 +6019,7 @@ function jsonObjectKeys(path, key) {
5569
6019
  return Object.keys(value[key]).sort();
5570
6020
  }
5571
6021
  function tomlMcpNames(path) {
5572
- if (!existsSync24(path)) return [];
6022
+ if (!existsSync25(path)) return [];
5573
6023
  const names = /* @__PURE__ */ new Set();
5574
6024
  for (const line of safeRead(path).split(/\r?\n/)) {
5575
6025
  const match = line.match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([^.\]]+))\]\s*$/);
@@ -5594,21 +6044,21 @@ function safeRead(path) {
5594
6044
  }
5595
6045
  function safeReadDir(path) {
5596
6046
  try {
5597
- return readdirSync12(path).sort();
6047
+ return readdirSync11(path).sort();
5598
6048
  } catch {
5599
6049
  return [];
5600
6050
  }
5601
6051
  }
5602
6052
  function safeIsDirectory(path) {
5603
6053
  try {
5604
- return statSync6(path).isDirectory();
6054
+ return statSync7(path).isDirectory();
5605
6055
  } catch {
5606
6056
  return false;
5607
6057
  }
5608
6058
  }
5609
6059
  function pathLexists(path) {
5610
6060
  try {
5611
- lstatSync9(path);
6061
+ lstatSync10(path);
5612
6062
  return true;
5613
6063
  } catch {
5614
6064
  return false;
@@ -5646,15 +6096,15 @@ function hasWorkflowReminderHooks(hooks) {
5646
6096
  );
5647
6097
  }
5648
6098
  function inspectEntries(root) {
5649
- const agentsPath = join25(root, "AGENTS.md");
5650
- const agents = !existsSync25(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
5651
- const claudePath = join25(root, "CLAUDE.md");
6099
+ const agentsPath = join26(root, "AGENTS.md");
6100
+ const agents = !existsSync26(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
6101
+ const claudePath = join26(root, "CLAUDE.md");
5652
6102
  let claude = "missing";
5653
6103
  if (pathLexists(claudePath)) {
5654
- const info = lstatSync10(claudePath);
6104
+ const info = lstatSync11(claudePath);
5655
6105
  if (info.isSymbolicLink()) {
5656
6106
  try {
5657
- claude = realpathSync5(claudePath) === realpathSync5(agentsPath) ? "agents-symlink" : "custom";
6107
+ claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
5658
6108
  } catch {
5659
6109
  claude = "dangling-symlink";
5660
6110
  }
@@ -5665,13 +6115,48 @@ function inspectEntries(root) {
5665
6115
  }
5666
6116
  return { agents, claude, gemini: inspectOptionalEntry(root, "GEMINI.md", agentsPath) };
5667
6117
  }
6118
+ var AGENT_LINK_ROOTS = [".agents/workflows", ".agents/commands", ".claude/commands"];
6119
+ function inspectAgentLinks(root) {
6120
+ const entries = AGENT_LINK_ROOTS.flatMap((directory) => {
6121
+ const directoryPath = join26(root, directory);
6122
+ if (!pathLexists(directoryPath)) return [];
6123
+ try {
6124
+ if (!lstatSync11(directoryPath).isDirectory()) return [];
6125
+ } catch {
6126
+ return [];
6127
+ }
6128
+ return safeReadDir(directoryPath).filter((name) => !name.startsWith(".")).flatMap((name) => {
6129
+ const relativePath = `${directory}/${name}`;
6130
+ const path = join26(root, relativePath);
6131
+ let stat;
6132
+ try {
6133
+ stat = lstatSync11(path);
6134
+ } catch {
6135
+ return [];
6136
+ }
6137
+ let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6138
+ if (stat.isSymbolicLink()) {
6139
+ try {
6140
+ realpathSync7(path);
6141
+ } catch {
6142
+ kind = "dangling-symlink";
6143
+ }
6144
+ }
6145
+ return [{ path: relativePath, kind, tracked: gitTracks(root, relativePath) }];
6146
+ });
6147
+ });
6148
+ return {
6149
+ entries,
6150
+ trackedDangling: entries.filter((entry) => entry.tracked && entry.kind === "dangling-symlink").map((entry) => entry.path)
6151
+ };
6152
+ }
5668
6153
  function inspectOptionalEntry(root, filename, agentsPath) {
5669
- const path = join25(root, filename);
6154
+ const path = join26(root, filename);
5670
6155
  if (!pathLexists(path)) return "missing";
5671
- const info = lstatSync10(path);
6156
+ const info = lstatSync11(path);
5672
6157
  if (info.isSymbolicLink()) {
5673
6158
  try {
5674
- return realpathSync5(path) === realpathSync5(agentsPath) ? "agents-symlink" : "custom";
6159
+ return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
5675
6160
  } catch {
5676
6161
  return "dangling-symlink";
5677
6162
  }
@@ -5706,7 +6191,7 @@ function inspectHooks(root) {
5706
6191
  { host: "codex", path: ".codex/hooks.json" }
5707
6192
  ];
5708
6193
  return configs.map((config) => {
5709
- const value = readJson4(join25(root, config.path));
6194
+ const value = readJson4(join26(root, config.path));
5710
6195
  const counts = /* @__PURE__ */ new Map();
5711
6196
  collectHookEvents(value, counts);
5712
6197
  return {
@@ -5727,18 +6212,18 @@ function collectHookEvents(value, counts) {
5727
6212
  }
5728
6213
  }
5729
6214
  function inspectDocs(root, expected) {
5730
- const packageJson = readJson4(join25(root, "package.json"));
6215
+ const packageJson = readJson4(join26(root, "package.json"));
5731
6216
  const dependencies = isRecord3(packageJson) ? { ...recordOrEmpty(packageJson.dependencies), ...recordOrEmpty(packageJson.devDependencies) } : {};
5732
6217
  const docGov = dependencyVersion(dependencies["@pieai/doc-gov"]);
5733
6218
  const proGov = dependencyVersion(dependencies["@pieai/pro-gov"]);
5734
- const routerMatch = safeRead(join25(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
6219
+ const routerMatch = safeRead(join26(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
5735
6220
  const declared = [docGov, proGov].filter((value) => Boolean(value));
5736
6221
  return {
5737
6222
  routerVersion: routerMatch?.[1],
5738
6223
  expectedRouterVersion: CURRENT_ROUTER_VERSION,
5739
6224
  routerAligned: routerMatch?.[1] === CURRENT_ROUTER_VERSION,
5740
- manifest: existsSync25(join25(root, "docs/governance/MANIFEST.yml")),
5741
- currentWork: existsSync25(join25(root, "docs/reference/execution/current-work.md")),
6225
+ manifest: existsSync26(join26(root, "docs/governance/MANIFEST.yml")),
6226
+ currentWork: existsSync26(join26(root, "docs/reference/execution/current-work.md")),
5742
6227
  packages: {
5743
6228
  expected,
5744
6229
  docGov,
@@ -5801,18 +6286,18 @@ function inspectGit2(root) {
5801
6286
 
5802
6287
  // src/portfolio/ai-health/hosts.ts
5803
6288
  import { execFileSync as execFileSync5 } from "node:child_process";
5804
- import { existsSync as existsSync27 } from "node:fs";
5805
- import { join as join27, resolve as resolve8, sep as sep2 } from "node:path";
6289
+ import { existsSync as existsSync28 } from "node:fs";
6290
+ import { join as join28, resolve as resolve8, sep as sep3 } from "node:path";
5806
6291
 
5807
6292
  // src/portfolio/ai-health/devspace.ts
5808
6293
  import { execFileSync as execFileSync4 } from "node:child_process";
5809
- import { existsSync as existsSync26, statSync as statSync7 } from "node:fs";
5810
- import { join as join26, relative as relative9, resolve as resolve7, sep } from "node:path";
6294
+ import { existsSync as existsSync27, statSync as statSync8 } from "node:fs";
6295
+ import { join as join27, relative as relative10, resolve as resolve7, sep as sep2 } from "node:path";
5811
6296
  function inspectDevSpaceHealth(options) {
5812
6297
  const run = options.run ?? runDevSpaceCommand;
5813
- const configDirectory = join26(options.homeDir, ".devspace");
5814
- const configPath = join26(configDirectory, "config.json");
5815
- const authPath = join26(configDirectory, "auth.json");
6298
+ const configDirectory = join27(options.homeDir, ".devspace");
6299
+ const configPath = join27(configDirectory, "config.json");
6300
+ const authPath = join27(configDirectory, "auth.json");
5816
6301
  const installedResult = run("devspace", ["--version"], 3e3);
5817
6302
  const installedVersion = installedResult.ok ? installedResult.stdout.match(/\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/)?.[0] : void 0;
5818
6303
  const latestResult = run(
@@ -5835,9 +6320,9 @@ function inspectDevSpaceHealth(options) {
5835
6320
  (repositoryPath) => allowedRoots.some((root) => isPathInside(repositoryPath, root))
5836
6321
  ) ? "complete" : "partial";
5837
6322
  const bind = configExists && typeof configValue.host === "string" ? isLoopbackHost(configValue.host) ? "loopback" : "non-loopback" : "unknown";
5838
- const directoryMode = existsSync26(configDirectory) ? modeString(statSync7(configDirectory).mode) : void 0;
5839
- const fileMode = existsSync26(configPath) ? modeString(statSync7(configPath).mode) : void 0;
5840
- const authMode = existsSync26(authPath) ? modeString(statSync7(authPath).mode) : void 0;
6323
+ const directoryMode = existsSync27(configDirectory) ? modeString(statSync8(configDirectory).mode) : void 0;
6324
+ const fileMode = existsSync27(configPath) ? modeString(statSync8(configPath).mode) : void 0;
6325
+ const authMode = existsSync27(authPath) ? modeString(statSync8(authPath).mode) : void 0;
5841
6326
  const update = installedVersion && latestVersion ? installedVersion === latestVersion ? "current" : "available" : "unknown";
5842
6327
  const recommendations = [];
5843
6328
  let status = "healthy";
@@ -5851,7 +6336,7 @@ function inspectDevSpaceHealth(options) {
5851
6336
  };
5852
6337
  if (!installedResult.ok) unhealthy("\u672C\u673A\u672A\u53D1\u73B0 DevSpace\uFF1B\u65E0\u6CD5\u4F7F\u7528\u5BBF\u4E3B\u5DE5\u4F5C\u533A\u670D\u52A1\u3002");
5853
6338
  if (!configExists) unhealthy("\u7F3A\u5C11 ~/.devspace/config.json\u3002");
5854
- if (!existsSync26(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
6339
+ if (!existsSync27(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
5855
6340
  if (directoryMode && directoryMode !== "700")
5856
6341
  unhealthy(`~/.devspace \u76EE\u5F55\u6743\u9650\u4E3A ${directoryMode}\uFF0C\u5E94\u6536\u7D27\u4E3A 700\u3002`);
5857
6342
  if (fileMode && fileMode !== "600") unhealthy(`DevSpace \u914D\u7F6E\u6587\u4EF6\u6743\u9650\u4E3A ${fileMode}\uFF0C\u5E94\u4E3A 600\u3002`);
@@ -5884,7 +6369,7 @@ function inspectDevSpaceHealth(options) {
5884
6369
  exists: configExists,
5885
6370
  ...directoryMode ? { directoryMode } : {},
5886
6371
  ...fileMode ? { fileMode } : {},
5887
- authExists: existsSync26(authPath),
6372
+ authExists: existsSync27(authPath),
5888
6373
  ...authMode ? { authMode } : {},
5889
6374
  bind,
5890
6375
  portValid: configExists && typeof configValue.port === "number" && Number.isInteger(configValue.port) && configValue.port > 0 && configValue.port <= 65535,
@@ -5913,8 +6398,8 @@ function isLoopbackHost(host) {
5913
6398
  return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
5914
6399
  }
5915
6400
  function isPathInside(path, root) {
5916
- const fromRoot = relative9(resolve7(root), resolve7(path));
5917
- return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
6401
+ const fromRoot = relative10(resolve7(root), resolve7(path));
6402
+ return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
5918
6403
  }
5919
6404
 
5920
6405
  // src/portfolio/ai-health/hosts.ts
@@ -5931,9 +6416,9 @@ var MCP_DISCOVERY_PATHS = {
5931
6416
  }
5932
6417
  };
5933
6418
  function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceSettings) {
5934
- const codexConfig = join27(homeDir, MCP_DISCOVERY_PATHS.user.codex);
5935
- const claudeConfig = join27(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
5936
- const grokConfig = join27(homeDir, MCP_DISCOVERY_PATHS.user.grok);
6419
+ const codexConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.codex);
6420
+ const claudeConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
6421
+ const grokConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.grok);
5937
6422
  const hostEnvironment = {
5938
6423
  mcp: {
5939
6424
  codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
@@ -5941,11 +6426,11 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
5941
6426
  grokUser: { path: grokConfig, names: tomlMcpNames(grokConfig) }
5942
6427
  },
5943
6428
  skills: {
5944
- codexUser: inspectSkillRoot(join27(homeDir, ".agents/skills")),
5945
- claudeCodeUser: inspectSkillRoot(join27(homeDir, ".claude/skills")),
5946
- grokUser: inspectSkillRoot(join27(homeDir, ".grok/skills")),
5947
- grokAgentsCompatibility: inspectSkillRoot(join27(homeDir, ".agents/skills")),
5948
- grokClaudeCompatibility: inspectSkillRoot(join27(homeDir, ".claude/skills")),
6429
+ codexUser: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6430
+ claudeCodeUser: inspectSkillRoot(join28(homeDir, ".claude/skills")),
6431
+ grokUser: inspectSkillRoot(join28(homeDir, ".grok/skills")),
6432
+ grokAgentsCompatibility: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6433
+ grokClaudeCompatibility: inspectSkillRoot(join28(homeDir, ".claude/skills")),
5949
6434
  ssot: inspectUserSkillsSsot(homeDir)
5950
6435
  },
5951
6436
  grok: {
@@ -5966,13 +6451,13 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
5966
6451
  function inspectSkillRoot(path) {
5967
6452
  const exists = pathLexists(path) && safeIsDirectory(path);
5968
6453
  const names = exists ? safeReadDir(path).filter(
5969
- (name) => !name.startsWith(".") && existsSync27(join27(path, name, "SKILL.md"))
6454
+ (name) => !name.startsWith(".") && existsSync28(join28(path, name, "SKILL.md"))
5970
6455
  ) : [];
5971
6456
  return { path, exists, names };
5972
6457
  }
5973
6458
  function claudeProjectLocalMcpNames(homeDir, root) {
5974
6459
  if (!homeDir) return [];
5975
- const value = readJson4(join27(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
6460
+ const value = readJson4(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
5976
6461
  if (!isRecord3(value) || !isRecord3(value.projects)) return [];
5977
6462
  const candidates = new Set(
5978
6463
  [resolve8(root), safeRealpath(root)].filter((path) => Boolean(path))
@@ -6002,7 +6487,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6002
6487
  execFileSync5("grok", ["inspect", "--json"], {
6003
6488
  cwd: root,
6004
6489
  encoding: "utf8",
6005
- env: { ...process.env, HOME: homeDir, GROK_HOME: join27(homeDir, ".grok") },
6490
+ env: { ...process.env, HOME: homeDir, GROK_HOME: join28(homeDir, ".grok") },
6006
6491
  maxBuffer: 10 * 1024 * 1024,
6007
6492
  stdio: ["ignore", "pipe", "ignore"],
6008
6493
  timeout: 8e3
@@ -6010,7 +6495,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6010
6495
  );
6011
6496
  if (!isRecord3(value)) return empty("failed");
6012
6497
  const userClaudeNames = new Set(
6013
- jsonObjectKeys(join27(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
6498
+ jsonObjectKeys(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
6014
6499
  );
6015
6500
  const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
6016
6501
  const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
@@ -6043,7 +6528,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6043
6528
  if (!isRecord3(item) || !isRecord3(item.source)) continue;
6044
6529
  const type = item.source.type;
6045
6530
  const path = item.source.path;
6046
- if (typeof path === "string" && path.includes(sep2 + ".grok" + sep2 + "bundled" + sep2))
6531
+ if (typeof path === "string" && path.includes(sep3 + ".grok" + sep3 + "bundled" + sep3))
6047
6532
  skillCounts.bundled += 1;
6048
6533
  else if (type === "project") skillCounts.project += 1;
6049
6534
  else if (type === "plugin") skillCounts.plugin += 1;
@@ -6080,23 +6565,23 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
6080
6565
  }
6081
6566
  const resolvedSource = safeRealpath(sourcePath) ?? resolve8(sourcePath);
6082
6567
  const resolvedRoot = safeRealpath(root) ?? resolve8(root);
6083
- if (resolvedSource === join27(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
6568
+ if (resolvedSource === join28(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
6084
6569
  return "project-shared";
6085
- if (resolvedSource.startsWith(resolvedRoot + sep2)) return "project";
6086
- if (homeDir && resolvedSource === join27(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6570
+ if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
6571
+ if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6087
6572
  if (localClaudeNames.has(name)) return "project-local";
6088
6573
  if (userClaudeNames.has(name)) return "user";
6089
6574
  }
6090
- if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep2)) return "user";
6575
+ if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
6091
6576
  if (sourceType === "project") return "project";
6092
6577
  return "unknown";
6093
6578
  }
6094
6579
 
6095
6580
  // src/portfolio/ai-health/secrets.ts
6096
- import { existsSync as existsSync28, lstatSync as lstatSync11, readdirSync as readdirSync13, statSync as statSync8 } from "node:fs";
6097
- import { join as join28, relative as relative10, sep as sep3 } from "node:path";
6581
+ import { existsSync as existsSync29, lstatSync as lstatSync12, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
6582
+ import { join as join29, relative as relative11, sep as sep4 } from "node:path";
6098
6583
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
6099
- const centralPath = join28(secretsRoot, id);
6584
+ const centralPath = join29(secretsRoot, id);
6100
6585
  const centralRealPath = safeRealpath(centralPath);
6101
6586
  const localOnlyReasons = new Map(
6102
6587
  (environmentPolicy?.localOnly ?? []).map((entry) => [entry.path, entry.reason])
@@ -6108,16 +6593,16 @@ function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environme
6108
6593
  tracked: isRepository ? gitTracks(root, path) : false,
6109
6594
  template: isEnvironmentTemplate(path),
6110
6595
  fixture: isEnvironmentFixture(path),
6111
- symlink: lstatSync11(join28(root, path)).isSymbolicLink(),
6112
- centralized: pointsInside(join28(root, path), centralRealPath),
6596
+ symlink: lstatSync12(join29(root, path)).isSymbolicLink(),
6597
+ centralized: pointsInside(join29(root, path), centralRealPath),
6113
6598
  localOnly: localOnlyReason !== void 0,
6114
6599
  ...localOnlyReason !== void 0 ? { localOnlyReason } : {}
6115
6600
  };
6116
6601
  });
6117
6602
  return {
6118
- centralDirectory: existsSync28(centralPath) ? "present" : "absent",
6119
- centralMode: existsSync28(centralPath) ? modeString(statSync8(centralPath).mode) : void 0,
6120
- centralFiles: existsSync28(centralPath) ? collectCentralSecretFiles(centralPath) : [],
6603
+ centralDirectory: existsSync29(centralPath) ? "present" : "absent",
6604
+ centralMode: existsSync29(centralPath) ? modeString(statSync9(centralPath).mode) : void 0,
6605
+ centralFiles: existsSync29(centralPath) ? collectCentralSecretFiles(centralPath) : [],
6121
6606
  repositoryEnvFiles: envFiles
6122
6607
  };
6123
6608
  }
@@ -6140,12 +6625,12 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
6140
6625
  if (depth > 5) return [];
6141
6626
  const found = [];
6142
6627
  try {
6143
- for (const entry of readdirSync13(current, { withFileTypes: true })) {
6628
+ for (const entry of readdirSync12(current, { withFileTypes: true })) {
6144
6629
  if (entry.isDirectory()) {
6145
6630
  if (!SKIP_ENV_DIRECTORIES.has(entry.name))
6146
- found.push(...collectEnvironmentFiles(root, join28(current, entry.name), depth + 1));
6631
+ found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
6147
6632
  } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
6148
- found.push(relative10(root, join28(current, entry.name)));
6633
+ found.push(relative11(root, join29(current, entry.name)));
6149
6634
  }
6150
6635
  }
6151
6636
  } catch {
@@ -6157,10 +6642,10 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
6157
6642
  if (depth > 3) return [];
6158
6643
  const found = [];
6159
6644
  try {
6160
- for (const entry of readdirSync13(current, { withFileTypes: true })) {
6161
- const path = join28(current, entry.name);
6645
+ for (const entry of readdirSync12(current, { withFileTypes: true })) {
6646
+ const path = join29(current, entry.name);
6162
6647
  if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
6163
- else found.push({ path: relative10(root, path), mode: modeString(lstatSync11(path).mode) });
6648
+ else found.push({ path: relative11(root, path), mode: modeString(lstatSync12(path).mode) });
6164
6649
  }
6165
6650
  } catch {
6166
6651
  return found;
@@ -6184,30 +6669,30 @@ function isEnvironmentFixture(path) {
6184
6669
  return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
6185
6670
  }
6186
6671
  function pointsInside(path, expectedRoot) {
6187
- if (!expectedRoot || !lstatSync11(path).isSymbolicLink()) return false;
6672
+ if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
6188
6673
  const target = safeRealpath(path);
6189
6674
  if (!target) return false;
6190
- const fromRoot = relative10(expectedRoot, target);
6191
- return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep3}`);
6675
+ const fromRoot = relative11(expectedRoot, target);
6676
+ return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
6192
6677
  }
6193
6678
  function hasUnsafeCentralSecretPermissions(secrets) {
6194
6679
  return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
6195
6680
  }
6196
6681
  function inspectSecretsRoot(path) {
6197
- return existsSync28(path) ? { path, exists: true, mode: modeString(statSync8(path).mode) } : { path, exists: false };
6682
+ return existsSync29(path) ? { path, exists: true, mode: modeString(statSync9(path).mode) } : { path, exists: false };
6198
6683
  }
6199
6684
 
6200
6685
  // src/portfolio/ai-health/skills.ts
6201
- import { existsSync as existsSync29, lstatSync as lstatSync12, realpathSync as realpathSync6 } from "node:fs";
6202
- import { join as join29 } from "node:path";
6686
+ import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync8 } from "node:fs";
6687
+ import { join as join30 } from "node:path";
6203
6688
  function countAutomaticSkillsNeedingReview(skills) {
6204
6689
  return skills.automatic.filter(
6205
6690
  (item) => !item.managed || !item.registryId || item.expectedPlacement !== "auto" || item.expectedScope === "user"
6206
6691
  ).length;
6207
6692
  }
6208
6693
  function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6209
- const lock = readJson4(join29(root, ".pro-gov/assets.lock.json"));
6210
- const assetManifest = readJson4(join29(root, ".pro-gov/assets.json"));
6694
+ const lock = readJson4(join30(root, ".pro-gov/assets.lock.json"));
6695
+ const assetManifest = readJson4(join30(root, ".pro-gov/assets.json"));
6211
6696
  const managed = /* @__PURE__ */ new Set();
6212
6697
  const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
6213
6698
  if (isRecord3(lock) && Array.isArray(lock.assets)) {
@@ -6219,7 +6704,7 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6219
6704
  }
6220
6705
  const inspectPlacement = (placement) => {
6221
6706
  const directory = placement === "auto" ? "skills" : "manual-skills";
6222
- const skillRoot = join29(root, ".agents", directory);
6707
+ const skillRoot = join30(root, ".agents", directory);
6223
6708
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6224
6709
  return safeReadDir(skillRoot).filter((name) => !name.startsWith(".")).map((name) => inspectSkillItem(skillRoot, directory, name, managed, registeredSkills));
6225
6710
  };
@@ -6274,24 +6759,24 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6274
6759
  },
6275
6760
  hosts: {
6276
6761
  codexProject: automatic.filter((item) => item.kind !== "dangling-symlink").length,
6277
- claudeCodeProject: inspectSkillRoot(join29(root, ".claude/skills")).names.length,
6278
- grokNativeProject: inspectSkillRoot(join29(root, ".grok/skills")).names.length,
6762
+ claudeCodeProject: inspectSkillRoot(join30(root, ".claude/skills")).names.length,
6763
+ grokNativeProject: inspectSkillRoot(join30(root, ".grok/skills")).names.length,
6279
6764
  grokEffective: grokInspection.skills
6280
6765
  }
6281
6766
  };
6282
6767
  }
6283
6768
  function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills) {
6284
- const path = join29(skillRoot, name);
6285
- const stat = lstatSync12(path);
6769
+ const path = join30(skillRoot, name);
6770
+ const stat = lstatSync13(path);
6286
6771
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6287
6772
  let realPath;
6288
6773
  try {
6289
- realPath = realpathSync6(path);
6774
+ realPath = realpathSync8(path);
6290
6775
  } catch {
6291
6776
  if (stat.isSymbolicLink()) kind = "dangling-symlink";
6292
6777
  }
6293
6778
  const registered = realPath ? registeredSkills.find((skill) => skill.sourceRealPath === realPath) : void 0;
6294
- const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync29(join29(path, "SKILL.md")) ? "project-local" : void 0;
6779
+ const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync30(join30(path, "SKILL.md")) ? "project-local" : void 0;
6295
6780
  return {
6296
6781
  name,
6297
6782
  kind,
@@ -6303,11 +6788,11 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6303
6788
  };
6304
6789
  }
6305
6790
  function isPluginPack(path) {
6306
- const skillsRoot = join29(path, "skills");
6307
- return existsSync29(join29(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync29(join29(skillsRoot, name, "SKILL.md")));
6791
+ const skillsRoot = join30(path, "skills");
6792
+ return existsSync30(join30(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync30(join30(skillsRoot, name, "SKILL.md")));
6308
6793
  }
6309
6794
  function inspectInvalidSkillEntries(root, directory) {
6310
- const skillRoot = join29(root, ".agents", directory);
6795
+ const skillRoot = join30(root, ".agents", directory);
6311
6796
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6312
6797
  return safeReadDir(skillRoot).flatMap((name) => {
6313
6798
  if (name === ".gitkeep") return [];
@@ -6315,14 +6800,14 @@ function inspectInvalidSkillEntries(root, directory) {
6315
6800
  return [{ path: `.agents/${directory}/${name}`, reason: "metadata-junk" }];
6316
6801
  if (name.startsWith("."))
6317
6802
  return [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }];
6318
- const path = join29(skillRoot, name);
6319
- return !lstatSync12(path).isDirectory() && !lstatSync12(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
6803
+ const path = join30(skillRoot, name);
6804
+ return !lstatSync13(path).isDirectory() && !lstatSync13(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
6320
6805
  });
6321
6806
  }
6322
6807
  function skillDuplicatesUser(item, root, userSkills) {
6323
6808
  if (userSkills.names.has(item.name)) return true;
6324
- const automatic = join29(root, ".agents/skills", item.name);
6325
- const manual = join29(root, ".agents/manual-skills", item.name);
6809
+ const automatic = join30(root, ".agents/skills", item.name);
6810
+ const manual = join30(root, ".agents/manual-skills", item.name);
6326
6811
  const realPath = safeRealpath(pathLexists(automatic) ? automatic : manual);
6327
6812
  return realPath ? userSkills.realPaths.has(realPath) : false;
6328
6813
  }
@@ -6342,13 +6827,13 @@ function skillPlacementDrift(item, actualPlacement) {
6342
6827
  return [];
6343
6828
  }
6344
6829
  function inspectClaudeSkillRoot(root) {
6345
- const path = join29(root, ".claude/skills");
6830
+ const path = join30(root, ".claude/skills");
6346
6831
  if (!pathLexists(path)) return "missing";
6347
- const stat = lstatSync12(path);
6832
+ const stat = lstatSync13(path);
6348
6833
  if (stat.isSymbolicLink()) {
6349
6834
  try {
6350
- const target = realpathSync6(path);
6351
- return target === realpathSync6(join29(root, ".agents/skills")) ? "shared-root" : "other";
6835
+ const target = realpathSync8(path);
6836
+ return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
6352
6837
  } catch {
6353
6838
  return "dangling-symlink";
6354
6839
  }
@@ -6361,27 +6846,27 @@ function inspectSkillRegistry(executionEngineRoot) {
6361
6846
  health: { source: 0, registered: 0, bundled: 0, bundles: 0 },
6362
6847
  skills: []
6363
6848
  };
6364
- const agentAssetsRoot = join29(executionEngineRoot, "agent-assets");
6365
- const registry = readJson4(join29(agentAssetsRoot, "registry.json"));
6849
+ const agentAssetsRoot = join30(executionEngineRoot, "agent-assets");
6850
+ const registry = readJson4(join30(agentAssetsRoot, "registry.json"));
6366
6851
  const assets = isRecord3(registry) && Array.isArray(registry.assets) ? registry.assets : [];
6367
6852
  const registeredSkills = assets.filter((asset) => isRecord3(asset) && asset.kind === "skill");
6368
- const bundleRoot = join29(agentAssetsRoot, "bundles");
6853
+ const bundleRoot = join30(agentAssetsRoot, "bundles");
6369
6854
  const bundleFiles = safeReadDir(bundleRoot).filter((file) => file.endsWith(".json"));
6370
6855
  const bundledIds = /* @__PURE__ */ new Set();
6371
6856
  for (const file of bundleFiles) {
6372
- const bundle = readJson4(join29(bundleRoot, file));
6857
+ const bundle = readJson4(join30(bundleRoot, file));
6373
6858
  if (!isRecord3(bundle) || !Array.isArray(bundle.assets)) continue;
6374
6859
  for (const id of bundle.assets) if (typeof id === "string") bundledIds.add(id);
6375
6860
  }
6376
6861
  const sourceRoots = [
6377
- join29(agentAssetsRoot, "skills/pie-skills"),
6378
- join29(agentAssetsRoot, "skills/npx-skills/.agents/skills")
6862
+ join30(agentAssetsRoot, "skills/pie-skills"),
6863
+ join30(agentAssetsRoot, "skills/npx-skills/.agents/skills")
6379
6864
  ];
6380
6865
  const source = sourceRoots.reduce(
6381
- (count, root) => count + safeReadDir(root).filter((name) => existsSync29(join29(root, name, "SKILL.md"))).length,
6866
+ (count, root) => count + safeReadDir(root).filter((name) => existsSync30(join30(root, name, "SKILL.md"))).length,
6382
6867
  0
6383
6868
  ) + registeredSkills.filter(
6384
- (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join29(agentAssetsRoot, asset.sourcePath))
6869
+ (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join30(agentAssetsRoot, asset.sourcePath))
6385
6870
  ).length;
6386
6871
  return {
6387
6872
  health: {
@@ -6398,7 +6883,7 @@ function inspectSkillRegistry(executionEngineRoot) {
6398
6883
  return [
6399
6884
  {
6400
6885
  id: asset.id,
6401
- sourceRealPath: safeRealpath(join29(agentAssetsRoot, asset.sourcePath)),
6886
+ sourceRealPath: safeRealpath(join30(agentAssetsRoot, asset.sourcePath)),
6402
6887
  defaultPlacement: asset.defaultPlacement,
6403
6888
  defaultScope: asset.defaultScope === "user" ? "user" : "project"
6404
6889
  }
@@ -6413,21 +6898,27 @@ function inspectUserSkillEvidence(root) {
6413
6898
  for (const name of safeReadDir(root)) {
6414
6899
  if (name.startsWith(".")) continue;
6415
6900
  names.add(name);
6416
- const realPath = safeRealpath(join29(root, name));
6901
+ const realPath = safeRealpath(join30(root, name));
6417
6902
  if (realPath) realPaths.add(realPath);
6418
6903
  }
6419
6904
  return { names, realPaths };
6420
6905
  }
6421
6906
 
6422
6907
  // src/portfolio/ai-health/technology.ts
6423
- import { existsSync as existsSync30, readdirSync as readdirSync14, statSync as statSync9 } from "node:fs";
6424
- import { join as join30 } from "node:path";
6908
+ import { existsSync as existsSync31, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
6909
+ import { join as join31 } from "node:path";
6425
6910
  function buildTechnologyMatrix(governance, repositories) {
6426
- if (!governance) return [];
6911
+ if (!governance || governance.technologies.length === 0) return [];
6427
6912
  const policy = governance.versionPolicy;
6913
+ const packageManifestsByRepository = /* @__PURE__ */ new Map();
6914
+ for (const repository of repositories) {
6915
+ if (!packageManifestsByRepository.has(repository.path)) {
6916
+ packageManifestsByRepository.set(repository.path, collectPackageManifests(repository.path));
6917
+ }
6918
+ }
6428
6919
  return governance.technologies.map((technology) => {
6429
6920
  const projects = repositories.flatMap((repository) => {
6430
- const packageManifests = collectPackageManifests(repository.path);
6921
+ const packageManifests = packageManifestsByRepository.get(repository.path) ?? [];
6431
6922
  const packageSignals = (technology.packages ?? []).map((name) => {
6432
6923
  const requirement = policy?.packages.find((item) => item.name === name);
6433
6924
  return packageManifests.map((manifest) => {
@@ -6442,8 +6933,8 @@ function buildTechnologyMatrix(governance, repositories) {
6442
6933
  }).filter((item) => item !== void 0);
6443
6934
  }).flat();
6444
6935
  const fileSignal = (technology.files ?? []).some(
6445
- (path) => hasUsableTechnologyFile(join30(repository.path, path)) || packageManifests.some(
6446
- (manifest) => hasUsableTechnologyFile(join30(manifest.directory, path))
6936
+ (path) => hasUsableTechnologyFile(join31(repository.path, path)) || packageManifests.some(
6937
+ (manifest) => hasUsableTechnologyFile(join31(manifest.directory, path))
6447
6938
  )
6448
6939
  );
6449
6940
  const modelSignal = [
@@ -6534,14 +7025,14 @@ function buildTechnologyMatrix(governance, repositories) {
6534
7025
  }).filter((technology) => technology.projectCount > 0);
6535
7026
  }
6536
7027
  function hasUsableTechnologyFile(path) {
6537
- if (!existsSync30(path)) return false;
7028
+ if (!existsSync31(path)) return false;
6538
7029
  try {
6539
- const info = statSync9(path);
7030
+ const info = statSync10(path);
6540
7031
  if (info.isFile()) return true;
6541
7032
  if (!info.isDirectory()) return false;
6542
- return readdirSync14(path, { withFileTypes: true }).some((entry) => {
7033
+ return readdirSync13(path, { withFileTypes: true }).some((entry) => {
6543
7034
  if (entry.name.startsWith(".")) return false;
6544
- const child = join30(path, entry.name);
7035
+ const child = join31(path, entry.name);
6545
7036
  if (entry.isDirectory()) return hasUsableTechnologyFile(child);
6546
7037
  return entry.name.toLowerCase() !== "readme.md";
6547
7038
  });
@@ -6553,7 +7044,7 @@ function inspectExclusiveOwnership(root, endpoint, governance) {
6553
7044
  const projectType = endpoint.projectType;
6554
7045
  return (governance?.exclusiveOwnership ?? []).flatMap((rule) => {
6555
7046
  if (projectType && rule.allowedProjectTypes.includes(projectType)) return [];
6556
- const paths = rule.paths.filter((path) => existsSync30(join30(root, path)));
7047
+ const paths = rule.paths.filter((path) => existsSync31(join31(root, path)));
6557
7048
  return paths.length > 0 ? [{ rule, paths }] : [];
6558
7049
  });
6559
7050
  }
@@ -6568,7 +7059,7 @@ function inspectProjectModel(root, endpoint, governance) {
6568
7059
  const detection = (id) => {
6569
7060
  const technology = technologyById.get(id);
6570
7061
  const packageMatch = technology?.packages?.some((name) => packages.has(name)) ?? false;
6571
- const fileMatch = technology?.files?.some((path) => existsSync30(join30(root, path))) ?? false;
7062
+ const fileMatch = technology?.files?.some((path) => existsSync31(join31(root, path))) ?? false;
6572
7063
  return { id, label: technology?.label ?? id, detected: packageMatch || fileMatch };
6573
7064
  };
6574
7065
  const selected = new Set(endpoint.capabilities ?? []);
@@ -6607,8 +7098,8 @@ function collectPackageNames(root) {
6607
7098
  }
6608
7099
 
6609
7100
  // src/portfolio/ai-health/report.ts
6610
- import { cpSync as cpSync3, existsSync as existsSync31, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
6611
- import { dirname as dirname16, join as join31 } from "node:path";
7101
+ import { cpSync as cpSync3, existsSync as existsSync32, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
7102
+ import { dirname as dirname16, join as join32 } from "node:path";
6612
7103
  import { fileURLToPath as fileURLToPath5 } from "node:url";
6613
7104
  function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
6614
7105
  const repositoriesById = /* @__PURE__ */ new Map();
@@ -6652,16 +7143,16 @@ function writePortfolioAiHealthReport(report, outDir) {
6652
7143
  mkdirSync10(outDir, { recursive: true });
6653
7144
  const dashboardAssets = findDashboardAssets();
6654
7145
  for (const file of ["index.html", "app.js", "app.css"]) {
6655
- const source = join31(dashboardAssets, file);
6656
- if (!existsSync31(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
6657
- cpSync3(source, join31(outDir, file));
7146
+ const source = join32(dashboardAssets, file);
7147
+ if (!existsSync32(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
7148
+ cpSync3(source, join32(outDir, file));
6658
7149
  }
6659
- const jsonPath = join31(outDir, "portfolio-ai-health.json");
6660
- const htmlPath = join31(outDir, "index.html");
7150
+ const jsonPath = join32(outDir, "portfolio-ai-health.json");
7151
+ const htmlPath = join32(outDir, "index.html");
6661
7152
  writeFileSync9(jsonPath, `${JSON.stringify(report, null, 2)}
6662
7153
  `);
6663
7154
  writeFileSync9(
6664
- join31(outDir, "data.js"),
7155
+ join32(outDir, "data.js"),
6665
7156
  `window.__PORTFOLIO_AI_HEALTH__ = ${safeJavaScriptJson2(report)};
6666
7157
  `
6667
7158
  );
@@ -6671,14 +7162,14 @@ function findDashboardAssets() {
6671
7162
  const packageRoot2 = dirname16(dirname16(fileURLToPath5(import.meta.url)));
6672
7163
  const candidates = [
6673
7164
  process.env.PGS_DASHBOARD_ASSETS_DIR,
6674
- join31(packageRoot2, ".dashboard-build"),
6675
- join31(packageRoot2, "assets/portfolio-dashboard"),
6676
- join31(process.cwd(), ".dashboard-build"),
6677
- join31(process.cwd(), "assets/portfolio-dashboard"),
6678
- join31(process.cwd(), "packages/pro-gov/.dashboard-build"),
6679
- join31(process.cwd(), "packages/pro-gov/assets/portfolio-dashboard")
7165
+ join32(packageRoot2, ".dashboard-build"),
7166
+ join32(packageRoot2, "assets/portfolio-dashboard"),
7167
+ join32(process.cwd(), ".dashboard-build"),
7168
+ join32(process.cwd(), "assets/portfolio-dashboard"),
7169
+ join32(process.cwd(), "packages/pro-gov/.dashboard-build"),
7170
+ join32(process.cwd(), "packages/pro-gov/assets/portfolio-dashboard")
6680
7171
  ].filter((value) => Boolean(value));
6681
- const match = candidates.find((path) => existsSync31(join31(path, "index.html")));
7172
+ const match = candidates.find((path) => existsSync32(join32(path, "index.html")));
6682
7173
  if (!match)
6683
7174
  throw new Error(
6684
7175
  "Portfolio dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -6696,7 +7187,7 @@ function inspectPortfolioAiHealth(options) {
6696
7187
  if (options.targetId && options.targetId !== "all" && endpoints.length === 0) {
6697
7188
  throw new Error(`Unknown portfolio target: ${options.targetId}`);
6698
7189
  }
6699
- const secretsRoot = options.secretsRoot ?? join32(
7190
+ const secretsRoot = options.secretsRoot ?? join33(
6700
7191
  dirname17(
6701
7192
  options.manifest.controlPlane?.path ?? allEndpoints[0]?.endpoint.path ?? process.cwd()
6702
7193
  ),
@@ -6706,9 +7197,9 @@ function inspectPortfolioAiHealth(options) {
6706
7197
  const grokVersion = commandVersion("grok");
6707
7198
  const executionEngineRoot = options.manifest.executionEngine?.path;
6708
7199
  const skillRegistry = inspectSkillRegistry(executionEngineRoot);
6709
- const userSkills = inspectUserSkillEvidence(join32(homeDir, ".agents/skills"));
7200
+ const userSkills = inspectUserSkillEvidence(join33(homeDir, ".agents/skills"));
6710
7201
  const expectedPackageVersion = packageVersion(
6711
- join32(executionEngineRoot ?? "", "packages/pro-gov/package.json")
7202
+ join33(executionEngineRoot ?? "", "packages/pro-gov/package.json")
6712
7203
  );
6713
7204
  const repositories = endpoints.map(
6714
7205
  ({ endpoint, role }) => inspectRepository(
@@ -6726,7 +7217,7 @@ function inspectPortfolioAiHealth(options) {
6726
7217
  const summary = { healthy: 0, attention: 0, unhealthy: 0 };
6727
7218
  for (const repository of repositories) summary[repository.status] += 1;
6728
7219
  return {
6729
- schemaVersion: 7,
7220
+ schemaVersion: 8,
6730
7221
  portfolioId: options.manifest.portfolioId,
6731
7222
  generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
6732
7223
  coverage: {
@@ -6775,19 +7266,20 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
6775
7266
  const root = endpoint.path;
6776
7267
  const git = inspectGit2(root);
6777
7268
  const entries = inspectEntries(root);
7269
+ const agentLinks = inspectAgentLinks(root);
6778
7270
  const grokInspection = inspectGrokProject(root, homeDir, grokVersion);
6779
7271
  const skills = inspectSkills(root, grokInspection, registeredSkills, userSkills);
6780
7272
  const hostSsot = inspectProjectHostSsot(root);
6781
7273
  const hooks = inspectHooks(root);
6782
7274
  const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
6783
7275
  const mcp = {
6784
- codexProject: tomlMcpNames(join32(root, MCP_DISCOVERY_PATHS.project.codex)),
7276
+ codexProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.codex)),
6785
7277
  claudeCodeProjectShared: jsonObjectKeys(
6786
- join32(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
7278
+ join33(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
6787
7279
  "mcpServers"
6788
7280
  ),
6789
7281
  claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
6790
- grokProject: tomlMcpNames(join32(root, MCP_DISCOVERY_PATHS.project.grok)),
7282
+ grokProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.grok)),
6791
7283
  grokEffective: grokInspection.effectiveMcp,
6792
7284
  grokInspection: grokInspection.inspection
6793
7285
  };
@@ -6855,6 +7347,10 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
6855
7347
  recommendations.push("GEMINI.md \u662F\u65AD\u5F00\u7684\u94FE\u63A5\uFF1B\u9700\u8981\u91CD\u65B0\u6307\u5411 AGENTS.md\u3002");
6856
7348
  if ([...skills.automatic, ...skills.manual].some((skill) => skill.kind === "dangling-symlink"))
6857
7349
  recommendations.push("`.agents/skills` \u6216 `.agents/manual-skills` \u4E2D\u5B58\u5728\u65AD\u5F00\u7684\u6280\u80FD\u94FE\u63A5\u3002");
7350
+ if (agentLinks.trackedDangling.length > 0)
7351
+ recommendations.push(
7352
+ `\u53D1\u73B0 ${agentLinks.trackedDangling.length} \u4E2A\u53D7 Git \u8DDF\u8E2A\u7684 agent workflow/command \u65AD\u5F00\u94FE\u63A5\uFF1A${agentLinks.trackedDangling.join("\u3001")}\uFF1B\u786E\u8BA4\u662F\u5426\u5E94\u5220\u9664\u65E7\u5165\u53E3\u6216\u6062\u590D canonical \u76EE\u6807\u3002`
7353
+ );
6858
7354
  if (skills.invalidEntries.length > 0)
6859
7355
  recommendations.push(
6860
7356
  `\u6280\u80FD\u76EE\u5F55\u4E2D\u6709 ${skills.invalidEntries.length} \u4E2A\u5783\u573E\u6216\u975E\u6280\u80FD\u6587\u4EF6\uFF1A${skills.invalidEntries.map((entry) => entry.path).join("\u3001")}\u3002`
@@ -6967,6 +7463,7 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
6967
7463
  status: deriveStatus(
6968
7464
  role,
6969
7465
  entries,
7466
+ agentLinks,
6970
7467
  git,
6971
7468
  hooks,
6972
7469
  skills,
@@ -6983,6 +7480,7 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
6983
7480
  recommendations,
6984
7481
  git,
6985
7482
  entries,
7483
+ agentLinks,
6986
7484
  hooks,
6987
7485
  mcp,
6988
7486
  skills,
@@ -6995,8 +7493,8 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
6995
7493
  redundancy
6996
7494
  };
6997
7495
  }
6998
- function deriveStatus(role, entries, git, hooks, skills, hostSsot, secrets, docs, projectModel, exclusiveOwnershipViolations, technologyGovernanceConfigured, versions, verification, redundancy) {
6999
- if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || [...skills.automatic, ...skills.manual].some((item) => item.kind === "dangling-symlink") || exclusiveOwnershipViolations.length > 0 || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template && !file.fixture) || hasUnsafeCentralSecretPermissions(secrets))
7496
+ function deriveStatus(role, entries, agentLinks, git, hooks, skills, hostSsot, secrets, docs, projectModel, exclusiveOwnershipViolations, technologyGovernanceConfigured, versions, verification, redundancy) {
7497
+ if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || agentLinks.trackedDangling.length > 0 || [...skills.automatic, ...skills.manual].some((item) => item.kind === "dangling-symlink") || exclusiveOwnershipViolations.length > 0 || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template && !file.fixture) || hasUnsafeCentralSecretPermissions(secrets))
7000
7498
  return "unhealthy";
7001
7499
  const missingBaseline = projectModel.baseline.some(
7002
7500
  (technology) => !technology.detected && !hasBaselineException(projectModel, technology.id)
@@ -7100,8 +7598,12 @@ function runPortfolioDoctor(args) {
7100
7598
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7101
7599
  return 1;
7102
7600
  }
7601
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7602
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7603
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7604
+ }
7103
7605
  const loadedAssets = loadAgentAssetRegistry({
7104
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7606
+ agentAssetsDir
7105
7607
  });
7106
7608
  if (loadedAssets.issues.length > 0) {
7107
7609
  for (const issue of loadedAssets.issues) console.error(`${issue.type}: ${issue.message}`);
@@ -7202,8 +7704,12 @@ function runPortfolioPlan(args) {
7202
7704
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7203
7705
  return 1;
7204
7706
  }
7707
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7708
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7709
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7710
+ }
7205
7711
  const loadedAssets = loadAgentAssetRegistry({
7206
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7712
+ agentAssetsDir
7207
7713
  });
7208
7714
  if (loadedAssets.issues.length > 0) {
7209
7715
  for (const issue of loadedAssets.issues) {
@@ -7292,8 +7798,12 @@ function runPortfolioAssetsCheck(args) {
7292
7798
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7293
7799
  return 1;
7294
7800
  }
7801
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7802
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7803
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7804
+ }
7295
7805
  const loadedAssets = loadAgentAssetRegistry({
7296
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7806
+ agentAssetsDir
7297
7807
  });
7298
7808
  if (loadedAssets.issues.length > 0) {
7299
7809
  if (options.value.json) {
@@ -7403,8 +7913,33 @@ function isHost2(value) {
7403
7913
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
7404
7914
  }
7405
7915
  function findPortfolioAgentAssetsDir(manifest) {
7406
- const agentAssetsDir = manifest?.executionEngine?.path ? join33(manifest.executionEngine.path, "agent-assets") : void 0;
7407
- return agentAssetsDir && existsSync32(join33(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
7916
+ const agentAssetsDir = manifest?.executionEngine?.path ? join34(manifest.executionEngine.path, "agent-assets") : void 0;
7917
+ return agentAssetsDir && existsSync33(join34(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
7918
+ }
7919
+ function reportMissingPortfolioRegistry(loaded, json) {
7920
+ const expectedPath = loaded.manifest?.executionEngine?.path ? join34(loaded.manifest.executionEngine.path, "agent-assets/registry.json") : "executionEngine.path/agent-assets/registry.json";
7921
+ const issue = {
7922
+ type: "missing-control-plane-registry",
7923
+ message: `Portfolio control-plane registry is required at ${expectedPath}; refusing the package fallback registry.`
7924
+ };
7925
+ if (json) {
7926
+ console.log(
7927
+ JSON.stringify(
7928
+ {
7929
+ ok: false,
7930
+ configPath: loaded.configPath,
7931
+ portfolioId: loaded.manifest?.portfolioId,
7932
+ issues: [issue],
7933
+ targets: []
7934
+ },
7935
+ null,
7936
+ 2
7937
+ )
7938
+ );
7939
+ } else {
7940
+ console.error(`${issue.type}: ${issue.message}`);
7941
+ }
7942
+ return 1;
7408
7943
  }
7409
7944
  function printUsage5() {
7410
7945
  console.error("Usage:");
@@ -7420,8 +7955,8 @@ function printUsage5() {
7420
7955
  }
7421
7956
  function readExistingAiHealthReport(outDir, portfolioId) {
7422
7957
  if (!outDir) return void 0;
7423
- const path = join33(outDir, "portfolio-ai-health.json");
7424
- if (!existsSync32(path)) return void 0;
7958
+ const path = join34(outDir, "portfolio-ai-health.json");
7959
+ if (!existsSync33(path)) return void 0;
7425
7960
  try {
7426
7961
  const value = JSON.parse(readFileSync18(path, "utf8"));
7427
7962
  if (!value || typeof value !== "object" || value.portfolioId !== portfolioId || !Array.isArray(value.repositories))
@@ -7433,8 +7968,8 @@ function readExistingAiHealthReport(outDir, portfolioId) {
7433
7968
  }
7434
7969
 
7435
7970
  // src/commands/sync.ts
7436
- import { existsSync as existsSync33, lstatSync as lstatSync13, readFileSync as readFileSync19, readlinkSync as readlinkSync4 } from "node:fs";
7437
- import { join as join34 } from "node:path";
7971
+ import { existsSync as existsSync34, lstatSync as lstatSync14, readFileSync as readFileSync19, readlinkSync as readlinkSync4 } from "node:fs";
7972
+ import { join as join35 } from "node:path";
7438
7973
  function runSync(args) {
7439
7974
  const check = args.includes("--check");
7440
7975
  if (!check) {
@@ -7462,7 +7997,7 @@ function runSync(args) {
7462
7997
  console.log("pro-gov sync check");
7463
7998
  console.log(`profile: ${profile}`);
7464
7999
  for (const file of planStarterFiles(profile)) {
7465
- const targetPath = join34(process.cwd(), file.targetPath);
8000
+ const targetPath = join35(process.cwd(), file.targetPath);
7466
8001
  const stat = safeLstat3(targetPath);
7467
8002
  if (!stat) {
7468
8003
  if (file.ownership === "optional-guardrail") continue;
@@ -7521,13 +8056,13 @@ function normalizeMarkdownTableCell(cell) {
7521
8056
  }
7522
8057
  function inferInstalledProfile(root) {
7523
8058
  const installed = ["engineering-runtime", "doc-only"].filter(
7524
- (profile) => existsSync33(join34(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
8059
+ (profile) => existsSync34(join35(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
7525
8060
  );
7526
8061
  return installed.length === 1 ? installed[0] : void 0;
7527
8062
  }
7528
8063
  function safeLstat3(path) {
7529
8064
  try {
7530
- return lstatSync13(path);
8065
+ return lstatSync14(path);
7531
8066
  } catch {
7532
8067
  return void 0;
7533
8068
  }
@@ -7565,7 +8100,8 @@ var COMMANDS = [
7565
8100
  "lens audit check --dir <path> [--json]",
7566
8101
  "init --profile <engineering-runtime|doc-only> <--dry-run|--apply>",
7567
8102
  "sync --check [--profile <engineering-runtime|doc-only>]",
7568
- "doctor"
8103
+ "package-doctor",
8104
+ "doctor (legacy alias for package-doctor)"
7569
8105
  ];
7570
8106
  var [command, subcommand] = process.argv.slice(2);
7571
8107
  process.exitCode = await main();
@@ -7581,6 +8117,7 @@ async function main() {
7581
8117
  if (command === "host-lens") return runHostLens(process.argv.slice(3));
7582
8118
  if (command === "init") return runInit(process.argv.slice(3));
7583
8119
  if (command === "sync") return runSync(process.argv.slice(3));
8120
+ if (command === "package-doctor") return runPackageDoctor(process.argv.slice(3));
7584
8121
  if (command === "doctor") return runDoctor(process.argv.slice(3));
7585
8122
  console.error(`Unknown command: ${[command, subcommand].filter(Boolean).join(" ")}`);
7586
8123
  printHelp();