@pieai/pro-gov 0.9.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.
Files changed (2) hide show
  1. package/dist/cli.js +132 -65
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -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",
@@ -427,6 +427,13 @@ function validateAssetRegistry(registry, options = {}) {
427
427
  path: asset.sourcePath,
428
428
  message: `Asset source path does not exist: ${asset.sourcePath}`
429
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
+ });
430
437
  } else if (asset.kind === "skill") {
431
438
  if (asset.sourceKind === "local-pack") {
432
439
  if (!isLocalSkillPack(sourceAbsolutePath)) {
@@ -483,6 +490,16 @@ function isSafeRegistrySourcePath(sourcePath) {
483
490
  function normalizeRegistrySourcePath(sourcePath) {
484
491
  return posix.normalize(sourcePath.replaceAll("\\", "/"));
485
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
+ }
486
503
  function pathExistsEvenIfDanglingSymlink(path) {
487
504
  try {
488
505
  lstatSync(path);
@@ -544,7 +561,7 @@ function hashAgentAssetContent(asset, agentAssetsDir) {
544
561
  function hashAssetPathContent(sourceAbsolutePath) {
545
562
  const hash = createHash2("sha256");
546
563
  for (const filePath of listFiles3(sourceAbsolutePath)) {
547
- const relativePath = toUnixPath3(relative3(sourceAbsolutePath, filePath));
564
+ const relativePath = toUnixPath3(relative4(sourceAbsolutePath, filePath));
548
565
  hash.update(relativePath);
549
566
  hash.update("\0");
550
567
  hash.update(readFileSync3(filePath));
@@ -712,15 +729,15 @@ import {
712
729
  lstatSync as lstatSync3,
713
730
  mkdirSync as mkdirSync2,
714
731
  readlinkSync as readlinkSync2,
715
- realpathSync as realpathSync2,
732
+ realpathSync as realpathSync3,
716
733
  symlinkSync,
717
734
  unlinkSync,
718
735
  writeFileSync
719
736
  } from "node:fs";
720
- 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";
721
738
 
722
739
  // src/asset-targets/install-plan.ts
723
- import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync, statSync as statSync3 } from "node:fs";
740
+ import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
724
741
  import { basename, dirname as dirname3, join as join7, resolve } from "node:path";
725
742
  function createAssetInstallPlan(options) {
726
743
  const placement = options.placement ?? "registry";
@@ -872,7 +889,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
872
889
  if (managedEntry && managedEntry.delivery === "snapshot") {
873
890
  throw new Error(`Refusing to replace managed snapshot with a symlink: ${targetPath}`);
874
891
  }
875
- if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync(targetAbsolutePath) === realpathSync(sourcePath)) {
892
+ if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
876
893
  return {
877
894
  type: "adopt-existing-symlink",
878
895
  assetId: asset.id,
@@ -913,7 +930,7 @@ function createSnapshotAction(options) {
913
930
  }
914
931
  const stats = lstatSync2(options.targetAbsolutePath);
915
932
  if (stats.isSymbolicLink()) {
916
- if (existsSync7(options.targetAbsolutePath) && realpathSync(options.targetAbsolutePath) === realpathSync(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
933
+ if (existsSync7(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
917
934
  return {
918
935
  type: "migrate-symlink-to-snapshot",
919
936
  assetId: options.asset.id,
@@ -1031,7 +1048,7 @@ function createLegacyClaudeAdoptions(options) {
1031
1048
  const targetAbsolutePath = join7(options.targetDir, targetPath);
1032
1049
  const compatibilityRootPath = join7(options.targetDir, ".claude/skills");
1033
1050
  const canonicalRootPath = join7(options.targetDir, ".agents/skills");
1034
- 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)) {
1035
1052
  throw new Error(
1036
1053
  `Legacy Claude compatibility root is not the exact canonical alias for ${entry.id}.`
1037
1054
  );
@@ -1044,7 +1061,7 @@ function createLegacyClaudeAdoptions(options) {
1044
1061
  const legacyAbsolutePath = join7(options.targetDir, entry.targetPath);
1045
1062
  const legacyStat = lstatSync2(legacyAbsolutePath);
1046
1063
  const expectedSourcePath = join7(options.agentAssetsDir, entry.sourcePath);
1047
- 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)) {
1048
1065
  throw new Error(`Legacy Claude skill target cannot be safely adopted: ${entry.targetPath}`);
1049
1066
  }
1050
1067
  const action = {
@@ -1175,7 +1192,7 @@ function applyAction(targetDir, action) {
1175
1192
  }
1176
1193
  mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1177
1194
  const sourceAbsolutePath = resolve2(action.sourcePath);
1178
- const symlinkTarget = relative4(realpathSync2(dirname4(targetAbsolutePath)), realpathSync2(sourceAbsolutePath)) || ".";
1195
+ const symlinkTarget = relative5(realpathSync3(dirname4(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
1179
1196
  if (action.type === "symlink") {
1180
1197
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1181
1198
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
@@ -1199,7 +1216,7 @@ function validateExistingSymlink(targetDir, action) {
1199
1216
  throw new Error(`Refusing unsafe existing symlink adoption: ${action.targetPath}`);
1200
1217
  }
1201
1218
  const targetAbsolutePath = join8(targetDir, action.targetPath);
1202
- if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync2(targetAbsolutePath) !== realpathSync2(action.sourcePath)) {
1219
+ if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
1203
1220
  throw new Error(`Existing skill symlink changed before apply: ${action.targetPath}`);
1204
1221
  }
1205
1222
  }
@@ -1226,7 +1243,7 @@ function validateSnapshotAction(targetDir, action) {
1226
1243
  return;
1227
1244
  }
1228
1245
  if (action.type === "migrate-symlink-to-snapshot") {
1229
- if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync3(targetAbsolutePath).isSymbolicLink() || !existsSync8(targetAbsolutePath) || realpathSync2(targetAbsolutePath) !== realpathSync2(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
1246
+ if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync3(targetAbsolutePath).isSymbolicLink() || !existsSync8(targetAbsolutePath) || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
1230
1247
  throw new Error(`Snapshot symlink changed before apply: ${action.targetPath}`);
1231
1248
  }
1232
1249
  return;
@@ -1272,14 +1289,14 @@ function validateAdoptedSymlink(targetDir, action) {
1272
1289
  const compatibilityRootPath = join8(targetDir, action.compatibilityRootPath);
1273
1290
  const targetAbsolutePath = join8(targetDir, action.targetPath);
1274
1291
  const legacyAbsolutePath = join8(targetDir, action.legacyTargetPath);
1275
- 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)) {
1276
1293
  throw new Error(
1277
1294
  `Legacy Claude compatibility alias changed before apply: ${action.compatibilityRootPath}`
1278
1295
  );
1279
1296
  }
1280
1297
  const targetStat = lstatSync3(targetAbsolutePath);
1281
1298
  const legacyStat = lstatSync3(legacyAbsolutePath);
1282
- 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)) {
1283
1300
  throw new Error(`Legacy Claude skill target changed before apply: ${action.targetPath}`);
1284
1301
  }
1285
1302
  }
@@ -1312,7 +1329,7 @@ function pathExistsEvenIfDanglingSymlink3(path) {
1312
1329
  }
1313
1330
 
1314
1331
  // src/asset-targets/check.ts
1315
- 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";
1316
1333
  import { join as join9 } from "node:path";
1317
1334
  function checkInstalledAssets(options) {
1318
1335
  const lockfilePath = join9(options.targetDir, ".pro-gov/assets.lock.json");
@@ -1469,6 +1486,18 @@ function checkInstalledAssets(options) {
1469
1486
  });
1470
1487
  continue;
1471
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
+ }
1472
1501
  const currentSourceHash = hashAgentAssetContent(asset, options.agentAssetsDir);
1473
1502
  if ((delivery === "snapshot" || targetHashMatchesLock) && currentSourceHash !== entry.contentHash) {
1474
1503
  issues.push({
@@ -2222,25 +2251,26 @@ function runPackageDoctor(_args, dependencies = {}) {
2222
2251
  function runDoctor(args) {
2223
2252
  return runPackageDoctor(args);
2224
2253
  }
2225
- function checkDocGov() {
2226
- const fromPath = spawnSync2("doc-gov", ["--help"], {
2254
+ function checkDocGov(options = {}) {
2255
+ const run = options.run ?? ((command2, args) => spawnSync2(command2, args, {
2227
2256
  encoding: "utf8",
2228
2257
  stdio: "ignore"
2229
- });
2230
- if (!fromPath.error && fromPath.status === 0) {
2231
- return { ok: true, message: "doc-gov: available on PATH" };
2232
- }
2233
- const dependencyCli = resolveDocGovDependencyCli();
2258
+ }));
2259
+ const dependencyCli = (options.resolveDependencyCli ?? resolveDocGovDependencyCli)();
2234
2260
  if (!dependencyCli) {
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
+ }
2235
2268
  return {
2236
2269
  ok: false,
2237
- message: "doc-gov: not found; install @pieai/doc-gov beside @pieai/pro-gov for validation."
2270
+ message: "doc-gov: not found; install the required @pieai/doc-gov package dependency beside @pieai/pro-gov."
2238
2271
  };
2239
2272
  }
2240
- const fromDependency = spawnSync2(process.execPath, [dependencyCli, "--help"], {
2241
- encoding: "utf8",
2242
- stdio: "ignore"
2243
- });
2273
+ const fromDependency = run(process.execPath, [dependencyCli, "--help"]);
2244
2274
  if (!fromDependency.error && fromDependency.status === 0) {
2245
2275
  return { ok: true, message: "doc-gov: available via package dependency" };
2246
2276
  }
@@ -2416,7 +2446,7 @@ import {
2416
2446
  writeFileSync as writeFileSync4
2417
2447
  } from "node:fs";
2418
2448
  import { homedir } from "node:os";
2419
- 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";
2420
2450
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2421
2451
  var ROOT_DEFINITIONS = [
2422
2452
  {
@@ -2857,7 +2887,7 @@ function countImmediateEntries(path) {
2857
2887
  }
2858
2888
  function displayPath(path, homePath) {
2859
2889
  const absolute = resolve3(path);
2860
- const withinHome = relative5(homePath, absolute);
2890
+ const withinHome = relative6(homePath, absolute);
2861
2891
  if (withinHome === "") return "~";
2862
2892
  if (!withinHome.startsWith("..")) return `~/${withinHome}`;
2863
2893
  return absolute;
@@ -3983,7 +4013,7 @@ function printUsage2() {
3983
4013
 
3984
4014
  // src/learning/recall.ts
3985
4015
  import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
3986
- 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";
3987
4017
  function recallLearnings(root, options) {
3988
4018
  const query = options.query.trim();
3989
4019
  const terms = tokenize(query);
@@ -4038,7 +4068,7 @@ function readLearningRecord(root, absolutePath) {
4038
4068
  const parsed = splitFrontmatter(content);
4039
4069
  const body = parsed.body;
4040
4070
  return {
4041
- relativePath: normalizePath(relative6(root, absolutePath)),
4071
+ relativePath: normalizePath(relative7(root, absolutePath)),
4042
4072
  title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
4043
4073
  metadata: parsed.frontmatter,
4044
4074
  body
@@ -4120,7 +4150,7 @@ function cleanMarkdownLine(input) {
4120
4150
 
4121
4151
  // src/learning/capture.ts
4122
4152
  import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
4123
- 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";
4124
4154
  function captureLearning(root, options) {
4125
4155
  const title = options.title.trim();
4126
4156
  const summary = options.summary.trim();
@@ -4134,7 +4164,7 @@ function captureLearning(root, options) {
4134
4164
  const idSlug = basename4(path, ".md");
4135
4165
  writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
4136
4166
  return {
4137
- relativePath: normalizePath2(relative7(root, path)),
4167
+ relativePath: normalizePath2(relative8(root, path)),
4138
4168
  title,
4139
4169
  captureMode: "pgs-native"
4140
4170
  };
@@ -4758,7 +4788,7 @@ import { homedir as homedir3 } from "node:os";
4758
4788
  import { join as join22 } from "node:path";
4759
4789
 
4760
4790
  // src/host-ssot.ts
4761
- 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";
4762
4792
  import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, resolve as resolve6 } from "node:path";
4763
4793
  function inspectProjectHostSsot(root) {
4764
4794
  const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
@@ -4808,7 +4838,7 @@ function inspectCanonicalPath(root, path) {
4808
4838
  if (!stat) return { path, status: "missing" };
4809
4839
  if (stat.isSymbolicLink()) {
4810
4840
  try {
4811
- realpathSync3(absolutePath);
4841
+ realpathSync5(absolutePath);
4812
4842
  return { path, status: "symlink" };
4813
4843
  } catch {
4814
4844
  return { path, status: "dangling-symlink" };
@@ -4842,7 +4872,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4842
4872
  }
4843
4873
  let targetMatches = false;
4844
4874
  try {
4845
- targetMatches = realpathSync3(absolutePath) === realpathSync3(expectedPath);
4875
+ targetMatches = realpathSync5(absolutePath) === realpathSync5(expectedPath);
4846
4876
  } catch {
4847
4877
  return { ...base, rawTarget, resolvedTarget, status: "dangling-symlink" };
4848
4878
  }
@@ -5036,7 +5066,7 @@ function readPackageJson(path) {
5036
5066
  // src/repository-files.ts
5037
5067
  import { spawnSync as spawnSync3 } from "node:child_process";
5038
5068
  import { existsSync as existsSync20, readdirSync as readdirSync10 } from "node:fs";
5039
- import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative8 } from "node:path";
5069
+ import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative9 } from "node:path";
5040
5070
  var gitMaxBufferBytes = 64 * 1024 * 1024;
5041
5071
  var RepositoryFileDiscoveryError = class extends Error {
5042
5072
  constructor(message) {
@@ -5104,7 +5134,7 @@ function discoverFilesystemFiles(root, options) {
5104
5134
  continue;
5105
5135
  }
5106
5136
  if (!entry.isFile()) continue;
5107
- const relativePath = normalizeRepositoryRelativePath(relative8(root, absolutePath));
5137
+ const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
5108
5138
  if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
5109
5139
  files.add(relativePath);
5110
5140
  }
@@ -5954,15 +5984,15 @@ import { homedir as homedir5 } from "node:os";
5954
5984
  import { dirname as dirname17, join as join33, resolve as resolve9 } from "node:path";
5955
5985
 
5956
5986
  // src/portfolio/ai-health/entries.ts
5957
- import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync5 } from "node:fs";
5987
+ import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync7 } from "node:fs";
5958
5988
  import { join as join26 } from "node:path";
5959
5989
 
5960
5990
  // src/portfolio/ai-health/shared.ts
5961
5991
  import { execFileSync as execFileSync2 } from "node:child_process";
5962
- import { existsSync as existsSync25, lstatSync as lstatSync10, readFileSync as readFileSync17, readdirSync as readdirSync11, realpathSync as realpathSync4, statSync as statSync7 } 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";
5963
5993
  function safeRealpath(path) {
5964
5994
  try {
5965
- return realpathSync4(path);
5995
+ return realpathSync6(path);
5966
5996
  } catch {
5967
5997
  return void 0;
5968
5998
  }
@@ -6074,7 +6104,7 @@ function inspectEntries(root) {
6074
6104
  const info = lstatSync11(claudePath);
6075
6105
  if (info.isSymbolicLink()) {
6076
6106
  try {
6077
- claude = realpathSync5(claudePath) === realpathSync5(agentsPath) ? "agents-symlink" : "custom";
6107
+ claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
6078
6108
  } catch {
6079
6109
  claude = "dangling-symlink";
6080
6110
  }
@@ -6107,7 +6137,7 @@ function inspectAgentLinks(root) {
6107
6137
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6108
6138
  if (stat.isSymbolicLink()) {
6109
6139
  try {
6110
- realpathSync5(path);
6140
+ realpathSync7(path);
6111
6141
  } catch {
6112
6142
  kind = "dangling-symlink";
6113
6143
  }
@@ -6126,7 +6156,7 @@ function inspectOptionalEntry(root, filename, agentsPath) {
6126
6156
  const info = lstatSync11(path);
6127
6157
  if (info.isSymbolicLink()) {
6128
6158
  try {
6129
- return realpathSync5(path) === realpathSync5(agentsPath) ? "agents-symlink" : "custom";
6159
+ return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
6130
6160
  } catch {
6131
6161
  return "dangling-symlink";
6132
6162
  }
@@ -6257,12 +6287,12 @@ function inspectGit2(root) {
6257
6287
  // src/portfolio/ai-health/hosts.ts
6258
6288
  import { execFileSync as execFileSync5 } from "node:child_process";
6259
6289
  import { existsSync as existsSync28 } from "node:fs";
6260
- import { join as join28, resolve as resolve8, sep as sep2 } from "node:path";
6290
+ import { join as join28, resolve as resolve8, sep as sep3 } from "node:path";
6261
6291
 
6262
6292
  // src/portfolio/ai-health/devspace.ts
6263
6293
  import { execFileSync as execFileSync4 } from "node:child_process";
6264
6294
  import { existsSync as existsSync27, statSync as statSync8 } from "node:fs";
6265
- import { join as join27, relative as relative9, resolve as resolve7, sep } from "node:path";
6295
+ import { join as join27, relative as relative10, resolve as resolve7, sep as sep2 } from "node:path";
6266
6296
  function inspectDevSpaceHealth(options) {
6267
6297
  const run = options.run ?? runDevSpaceCommand;
6268
6298
  const configDirectory = join27(options.homeDir, ".devspace");
@@ -6368,8 +6398,8 @@ function isLoopbackHost(host) {
6368
6398
  return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
6369
6399
  }
6370
6400
  function isPathInside(path, root) {
6371
- const fromRoot = relative9(resolve7(root), resolve7(path));
6372
- return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
6401
+ const fromRoot = relative10(resolve7(root), resolve7(path));
6402
+ return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
6373
6403
  }
6374
6404
 
6375
6405
  // src/portfolio/ai-health/hosts.ts
@@ -6498,7 +6528,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6498
6528
  if (!isRecord3(item) || !isRecord3(item.source)) continue;
6499
6529
  const type = item.source.type;
6500
6530
  const path = item.source.path;
6501
- if (typeof path === "string" && path.includes(sep2 + ".grok" + sep2 + "bundled" + sep2))
6531
+ if (typeof path === "string" && path.includes(sep3 + ".grok" + sep3 + "bundled" + sep3))
6502
6532
  skillCounts.bundled += 1;
6503
6533
  else if (type === "project") skillCounts.project += 1;
6504
6534
  else if (type === "plugin") skillCounts.plugin += 1;
@@ -6537,19 +6567,19 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
6537
6567
  const resolvedRoot = safeRealpath(root) ?? resolve8(root);
6538
6568
  if (resolvedSource === join28(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
6539
6569
  return "project-shared";
6540
- if (resolvedSource.startsWith(resolvedRoot + sep2)) return "project";
6570
+ if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
6541
6571
  if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6542
6572
  if (localClaudeNames.has(name)) return "project-local";
6543
6573
  if (userClaudeNames.has(name)) return "user";
6544
6574
  }
6545
- if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep2)) return "user";
6575
+ if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
6546
6576
  if (sourceType === "project") return "project";
6547
6577
  return "unknown";
6548
6578
  }
6549
6579
 
6550
6580
  // src/portfolio/ai-health/secrets.ts
6551
6581
  import { existsSync as existsSync29, lstatSync as lstatSync12, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
6552
- import { join as join29, relative as relative10, sep as sep3 } from "node:path";
6582
+ import { join as join29, relative as relative11, sep as sep4 } from "node:path";
6553
6583
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
6554
6584
  const centralPath = join29(secretsRoot, id);
6555
6585
  const centralRealPath = safeRealpath(centralPath);
@@ -6600,7 +6630,7 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
6600
6630
  if (!SKIP_ENV_DIRECTORIES.has(entry.name))
6601
6631
  found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
6602
6632
  } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
6603
- found.push(relative10(root, join29(current, entry.name)));
6633
+ found.push(relative11(root, join29(current, entry.name)));
6604
6634
  }
6605
6635
  }
6606
6636
  } catch {
@@ -6615,7 +6645,7 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
6615
6645
  for (const entry of readdirSync12(current, { withFileTypes: true })) {
6616
6646
  const path = join29(current, entry.name);
6617
6647
  if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
6618
- else found.push({ path: relative10(root, path), mode: modeString(lstatSync12(path).mode) });
6648
+ else found.push({ path: relative11(root, path), mode: modeString(lstatSync12(path).mode) });
6619
6649
  }
6620
6650
  } catch {
6621
6651
  return found;
@@ -6642,8 +6672,8 @@ function pointsInside(path, expectedRoot) {
6642
6672
  if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
6643
6673
  const target = safeRealpath(path);
6644
6674
  if (!target) return false;
6645
- const fromRoot = relative10(expectedRoot, target);
6646
- return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep3}`);
6675
+ const fromRoot = relative11(expectedRoot, target);
6676
+ return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
6647
6677
  }
6648
6678
  function hasUnsafeCentralSecretPermissions(secrets) {
6649
6679
  return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
@@ -6653,7 +6683,7 @@ function inspectSecretsRoot(path) {
6653
6683
  }
6654
6684
 
6655
6685
  // src/portfolio/ai-health/skills.ts
6656
- import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync6 } from "node:fs";
6686
+ import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync8 } from "node:fs";
6657
6687
  import { join as join30 } from "node:path";
6658
6688
  function countAutomaticSkillsNeedingReview(skills) {
6659
6689
  return skills.automatic.filter(
@@ -6741,7 +6771,7 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6741
6771
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6742
6772
  let realPath;
6743
6773
  try {
6744
- realPath = realpathSync6(path);
6774
+ realPath = realpathSync8(path);
6745
6775
  } catch {
6746
6776
  if (stat.isSymbolicLink()) kind = "dangling-symlink";
6747
6777
  }
@@ -6802,8 +6832,8 @@ function inspectClaudeSkillRoot(root) {
6802
6832
  const stat = lstatSync13(path);
6803
6833
  if (stat.isSymbolicLink()) {
6804
6834
  try {
6805
- const target = realpathSync6(path);
6806
- return target === realpathSync6(join30(root, ".agents/skills")) ? "shared-root" : "other";
6835
+ const target = realpathSync8(path);
6836
+ return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
6807
6837
  } catch {
6808
6838
  return "dangling-symlink";
6809
6839
  }
@@ -7568,8 +7598,12 @@ function runPortfolioDoctor(args) {
7568
7598
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7569
7599
  return 1;
7570
7600
  }
7601
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7602
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7603
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7604
+ }
7571
7605
  const loadedAssets = loadAgentAssetRegistry({
7572
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7606
+ agentAssetsDir
7573
7607
  });
7574
7608
  if (loadedAssets.issues.length > 0) {
7575
7609
  for (const issue of loadedAssets.issues) console.error(`${issue.type}: ${issue.message}`);
@@ -7670,8 +7704,12 @@ function runPortfolioPlan(args) {
7670
7704
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7671
7705
  return 1;
7672
7706
  }
7707
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7708
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7709
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7710
+ }
7673
7711
  const loadedAssets = loadAgentAssetRegistry({
7674
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7712
+ agentAssetsDir
7675
7713
  });
7676
7714
  if (loadedAssets.issues.length > 0) {
7677
7715
  for (const issue of loadedAssets.issues) {
@@ -7760,8 +7798,12 @@ function runPortfolioAssetsCheck(args) {
7760
7798
  console.error(`Unknown portfolio target: ${options.value.targetId}`);
7761
7799
  return 1;
7762
7800
  }
7801
+ const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
7802
+ if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
7803
+ return reportMissingPortfolioRegistry(loaded, options.value.json);
7804
+ }
7763
7805
  const loadedAssets = loadAgentAssetRegistry({
7764
- agentAssetsDir: findPortfolioAgentAssetsDir(loaded.manifest)
7806
+ agentAssetsDir
7765
7807
  });
7766
7808
  if (loadedAssets.issues.length > 0) {
7767
7809
  if (options.value.json) {
@@ -7874,6 +7916,31 @@ function findPortfolioAgentAssetsDir(manifest) {
7874
7916
  const agentAssetsDir = manifest?.executionEngine?.path ? join34(manifest.executionEngine.path, "agent-assets") : void 0;
7875
7917
  return agentAssetsDir && existsSync33(join34(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
7876
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;
7943
+ }
7877
7944
  function printUsage5() {
7878
7945
  console.error("Usage:");
7879
7946
  console.error(" pro-gov portfolio check --config <path> [--json]");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pieai/pro-gov",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Project-level distribution kit for Project Governance System.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "@pieai/doc-gov": "^0.9.0"
38
+ "@pieai/doc-gov": "^0.9.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@pieai/swimmer-ui-kit": "1.3.2",