@pieai/pro-gov 0.9.0 → 0.9.2
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.
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"notes": "Public rule for choosing evidence lanes and requiring fresh verification before completion.",
|
|
26
26
|
"promotion": {
|
|
27
27
|
"privateSourcePath": "rules/pie-rules/ai-in-the-loop.md",
|
|
28
|
-
"privateSourceHash": "sha256:
|
|
28
|
+
"privateSourceHash": "sha256:344eba8c3b1f3130ed33e4b6a4511a332e2ca477e6a40ad7d4c73006197f5445",
|
|
29
29
|
"publicHash": "sha256:911702baf6ea1d724e41ea1876d23d80f4de8dfaaadbf1399a40c982c810224c",
|
|
30
30
|
"sanitized": true,
|
|
31
|
-
"lastReviewed": "2026-08-
|
|
32
|
-
"reviewNotes": "Reviewed after the private source gained governed front matter and normalized formatting. The public copy intentionally keeps the portable evidence-loop method without the private Grok compatibility lane or project governance metadata."
|
|
31
|
+
"lastReviewed": "2026-08-20",
|
|
32
|
+
"reviewNotes": "Reviewed after the private source gained governed front matter and normalized formatting. The public copy intentionally keeps the portable evidence-loop method without the private Grok compatibility lane or project governance metadata. Re-reviewed 2026-08-20: the private source moved by Markdown table padding only, with no wording change, so the sanitized public copy is unaffected and only the private hash is re-pinned."
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Seeded by `pro-gov init`. This file is project-local after seeding — add
|
|
2
|
+
# your own build outputs and generated paths freely.
|
|
3
|
+
|
|
4
|
+
# Dependencies and build output
|
|
5
|
+
node_modules
|
|
6
|
+
dist
|
|
7
|
+
build
|
|
8
|
+
coverage
|
|
9
|
+
out
|
|
10
|
+
.next
|
|
11
|
+
.turbo
|
|
12
|
+
.tmp
|
|
13
|
+
|
|
14
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
15
|
+
# PGS-managed content — DO NOT REFORMAT. Keep these entries.
|
|
16
|
+
#
|
|
17
|
+
# `pro-gov sync --check` byte-compares docs/governance against the packaged
|
|
18
|
+
# starter, and `pro-gov portfolio assets-check` byte-compares the shared-rule
|
|
19
|
+
# snapshots. A formatter that only pads Markdown table cells is enough to fail
|
|
20
|
+
# both, and the guardrails cannot tell cosmetic padding from a real local edit
|
|
21
|
+
# — re-applying the asset then refuses with asset-lock-drift.
|
|
22
|
+
#
|
|
23
|
+
# If you need one of these files changed, change it upstream in Project
|
|
24
|
+
# Governance System and re-sync; do not restyle it here.
|
|
25
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
26
|
+
docs/governance
|
|
27
|
+
docs/policy/shared-rules
|
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
|
|
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(
|
|
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
|
|
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
|
|
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) &&
|
|
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) &&
|
|
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" ||
|
|
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 ||
|
|
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 =
|
|
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() ||
|
|
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) ||
|
|
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 ||
|
|
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 ||
|
|
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
|
|
2254
|
+
function checkDocGov(options = {}) {
|
|
2255
|
+
const run = options.run ?? ((command2, args) => spawnSync2(command2, args, {
|
|
2227
2256
|
encoding: "utf8",
|
|
2228
2257
|
stdio: "ignore"
|
|
2229
|
-
});
|
|
2230
|
-
|
|
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
|
|
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 =
|
|
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
|
}
|
|
@@ -2298,7 +2328,10 @@ function classifyOwnership(targetPath) {
|
|
|
2298
2328
|
if (targetPath === "lefthook.yml" || targetPath === ".github/workflows/docs-check.yml") {
|
|
2299
2329
|
return "optional-guardrail";
|
|
2300
2330
|
}
|
|
2301
|
-
if (targetPath === "AGENTS.md" ||
|
|
2331
|
+
if (targetPath === "AGENTS.md" || // Seeded so a new project starts with the managed paths protected from its
|
|
2332
|
+
// formatter, then owned locally — every project adds its own build outputs,
|
|
2333
|
+
// so this must not be byte-compared by `sync --check`.
|
|
2334
|
+
targetPath === ".oxfmtignore" || targetPath === "docs/policy/best-practice-for-this-project.md" || targetPath === "docs/reference/documentation-map.md" || targetPath === "docs/reference/execution/current-work.md") {
|
|
2302
2335
|
return "project-local-seed";
|
|
2303
2336
|
}
|
|
2304
2337
|
return "shared";
|
|
@@ -2416,7 +2449,7 @@ import {
|
|
|
2416
2449
|
writeFileSync as writeFileSync4
|
|
2417
2450
|
} from "node:fs";
|
|
2418
2451
|
import { homedir } from "node:os";
|
|
2419
|
-
import { dirname as dirname8, join as join14, relative as
|
|
2452
|
+
import { dirname as dirname8, join as join14, relative as relative6, resolve as resolve3 } from "node:path";
|
|
2420
2453
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2421
2454
|
var ROOT_DEFINITIONS = [
|
|
2422
2455
|
{
|
|
@@ -2563,7 +2596,11 @@ function inspectAgentBrowser(options) {
|
|
|
2563
2596
|
const output = execFileSync(command2, ["--version"], {
|
|
2564
2597
|
encoding: "utf8",
|
|
2565
2598
|
stdio: ["ignore", "pipe", "ignore"],
|
|
2566
|
-
|
|
2599
|
+
// Host scans can run alongside the full portfolio test matrix. Keep the
|
|
2600
|
+
// probe bounded, but leave enough headroom for a busy Node process to
|
|
2601
|
+
// spawn the pinned CLI instead of misclassifying a slow start as an
|
|
2602
|
+
// unparseable version.
|
|
2603
|
+
timeout: 1e4
|
|
2567
2604
|
}).trim();
|
|
2568
2605
|
const installedVersion = output.match(/(?:^|\s)(\d+\.\d+\.\d+)(?:\s|$)/)?.[1];
|
|
2569
2606
|
if (!installedVersion) {
|
|
@@ -2857,7 +2894,7 @@ function countImmediateEntries(path) {
|
|
|
2857
2894
|
}
|
|
2858
2895
|
function displayPath(path, homePath) {
|
|
2859
2896
|
const absolute = resolve3(path);
|
|
2860
|
-
const withinHome =
|
|
2897
|
+
const withinHome = relative6(homePath, absolute);
|
|
2861
2898
|
if (withinHome === "") return "~";
|
|
2862
2899
|
if (!withinHome.startsWith("..")) return `~/${withinHome}`;
|
|
2863
2900
|
return absolute;
|
|
@@ -3983,7 +4020,7 @@ function printUsage2() {
|
|
|
3983
4020
|
|
|
3984
4021
|
// src/learning/recall.ts
|
|
3985
4022
|
import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
|
|
3986
|
-
import { basename as basename3, join as join15, relative as
|
|
4023
|
+
import { basename as basename3, join as join15, relative as relative7 } from "node:path";
|
|
3987
4024
|
function recallLearnings(root, options) {
|
|
3988
4025
|
const query = options.query.trim();
|
|
3989
4026
|
const terms = tokenize(query);
|
|
@@ -4038,7 +4075,7 @@ function readLearningRecord(root, absolutePath) {
|
|
|
4038
4075
|
const parsed = splitFrontmatter(content);
|
|
4039
4076
|
const body = parsed.body;
|
|
4040
4077
|
return {
|
|
4041
|
-
relativePath: normalizePath(
|
|
4078
|
+
relativePath: normalizePath(relative7(root, absolutePath)),
|
|
4042
4079
|
title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
|
|
4043
4080
|
metadata: parsed.frontmatter,
|
|
4044
4081
|
body
|
|
@@ -4120,7 +4157,7 @@ function cleanMarkdownLine(input) {
|
|
|
4120
4157
|
|
|
4121
4158
|
// src/learning/capture.ts
|
|
4122
4159
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
4123
|
-
import { basename as basename4, join as join16, relative as
|
|
4160
|
+
import { basename as basename4, join as join16, relative as relative8 } from "node:path";
|
|
4124
4161
|
function captureLearning(root, options) {
|
|
4125
4162
|
const title = options.title.trim();
|
|
4126
4163
|
const summary = options.summary.trim();
|
|
@@ -4134,7 +4171,7 @@ function captureLearning(root, options) {
|
|
|
4134
4171
|
const idSlug = basename4(path, ".md");
|
|
4135
4172
|
writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
|
|
4136
4173
|
return {
|
|
4137
|
-
relativePath: normalizePath2(
|
|
4174
|
+
relativePath: normalizePath2(relative8(root, path)),
|
|
4138
4175
|
title,
|
|
4139
4176
|
captureMode: "pgs-native"
|
|
4140
4177
|
};
|
|
@@ -4758,7 +4795,7 @@ import { homedir as homedir3 } from "node:os";
|
|
|
4758
4795
|
import { join as join22 } from "node:path";
|
|
4759
4796
|
|
|
4760
4797
|
// src/host-ssot.ts
|
|
4761
|
-
import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as
|
|
4798
|
+
import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
|
|
4762
4799
|
import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, resolve as resolve6 } from "node:path";
|
|
4763
4800
|
function inspectProjectHostSsot(root) {
|
|
4764
4801
|
const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
|
|
@@ -4808,7 +4845,7 @@ function inspectCanonicalPath(root, path) {
|
|
|
4808
4845
|
if (!stat) return { path, status: "missing" };
|
|
4809
4846
|
if (stat.isSymbolicLink()) {
|
|
4810
4847
|
try {
|
|
4811
|
-
|
|
4848
|
+
realpathSync5(absolutePath);
|
|
4812
4849
|
return { path, status: "symlink" };
|
|
4813
4850
|
} catch {
|
|
4814
4851
|
return { path, status: "dangling-symlink" };
|
|
@@ -4842,7 +4879,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
|
|
|
4842
4879
|
}
|
|
4843
4880
|
let targetMatches = false;
|
|
4844
4881
|
try {
|
|
4845
|
-
targetMatches =
|
|
4882
|
+
targetMatches = realpathSync5(absolutePath) === realpathSync5(expectedPath);
|
|
4846
4883
|
} catch {
|
|
4847
4884
|
return { ...base, rawTarget, resolvedTarget, status: "dangling-symlink" };
|
|
4848
4885
|
}
|
|
@@ -5036,7 +5073,7 @@ function readPackageJson(path) {
|
|
|
5036
5073
|
// src/repository-files.ts
|
|
5037
5074
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
5038
5075
|
import { existsSync as existsSync20, readdirSync as readdirSync10 } from "node:fs";
|
|
5039
|
-
import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as
|
|
5076
|
+
import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative9 } from "node:path";
|
|
5040
5077
|
var gitMaxBufferBytes = 64 * 1024 * 1024;
|
|
5041
5078
|
var RepositoryFileDiscoveryError = class extends Error {
|
|
5042
5079
|
constructor(message) {
|
|
@@ -5104,7 +5141,7 @@ function discoverFilesystemFiles(root, options) {
|
|
|
5104
5141
|
continue;
|
|
5105
5142
|
}
|
|
5106
5143
|
if (!entry.isFile()) continue;
|
|
5107
|
-
const relativePath = normalizeRepositoryRelativePath(
|
|
5144
|
+
const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
|
|
5108
5145
|
if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
|
|
5109
5146
|
files.add(relativePath);
|
|
5110
5147
|
}
|
|
@@ -5954,15 +5991,15 @@ import { homedir as homedir5 } from "node:os";
|
|
|
5954
5991
|
import { dirname as dirname17, join as join33, resolve as resolve9 } from "node:path";
|
|
5955
5992
|
|
|
5956
5993
|
// src/portfolio/ai-health/entries.ts
|
|
5957
|
-
import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as
|
|
5994
|
+
import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync7 } from "node:fs";
|
|
5958
5995
|
import { join as join26 } from "node:path";
|
|
5959
5996
|
|
|
5960
5997
|
// src/portfolio/ai-health/shared.ts
|
|
5961
5998
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
5962
|
-
import { existsSync as existsSync25, lstatSync as lstatSync10, readFileSync as readFileSync17, readdirSync as readdirSync11, realpathSync as
|
|
5999
|
+
import { existsSync as existsSync25, lstatSync as lstatSync10, readFileSync as readFileSync17, readdirSync as readdirSync11, realpathSync as realpathSync6, statSync as statSync7 } from "node:fs";
|
|
5963
6000
|
function safeRealpath(path) {
|
|
5964
6001
|
try {
|
|
5965
|
-
return
|
|
6002
|
+
return realpathSync6(path);
|
|
5966
6003
|
} catch {
|
|
5967
6004
|
return void 0;
|
|
5968
6005
|
}
|
|
@@ -6074,7 +6111,7 @@ function inspectEntries(root) {
|
|
|
6074
6111
|
const info = lstatSync11(claudePath);
|
|
6075
6112
|
if (info.isSymbolicLink()) {
|
|
6076
6113
|
try {
|
|
6077
|
-
claude =
|
|
6114
|
+
claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
|
|
6078
6115
|
} catch {
|
|
6079
6116
|
claude = "dangling-symlink";
|
|
6080
6117
|
}
|
|
@@ -6107,7 +6144,7 @@ function inspectAgentLinks(root) {
|
|
|
6107
6144
|
let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
|
|
6108
6145
|
if (stat.isSymbolicLink()) {
|
|
6109
6146
|
try {
|
|
6110
|
-
|
|
6147
|
+
realpathSync7(path);
|
|
6111
6148
|
} catch {
|
|
6112
6149
|
kind = "dangling-symlink";
|
|
6113
6150
|
}
|
|
@@ -6126,7 +6163,7 @@ function inspectOptionalEntry(root, filename, agentsPath) {
|
|
|
6126
6163
|
const info = lstatSync11(path);
|
|
6127
6164
|
if (info.isSymbolicLink()) {
|
|
6128
6165
|
try {
|
|
6129
|
-
return
|
|
6166
|
+
return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
|
|
6130
6167
|
} catch {
|
|
6131
6168
|
return "dangling-symlink";
|
|
6132
6169
|
}
|
|
@@ -6257,12 +6294,12 @@ function inspectGit2(root) {
|
|
|
6257
6294
|
// src/portfolio/ai-health/hosts.ts
|
|
6258
6295
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
6259
6296
|
import { existsSync as existsSync28 } from "node:fs";
|
|
6260
|
-
import { join as join28, resolve as resolve8, sep as
|
|
6297
|
+
import { join as join28, resolve as resolve8, sep as sep3 } from "node:path";
|
|
6261
6298
|
|
|
6262
6299
|
// src/portfolio/ai-health/devspace.ts
|
|
6263
6300
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
6264
6301
|
import { existsSync as existsSync27, statSync as statSync8 } from "node:fs";
|
|
6265
|
-
import { join as join27, relative as
|
|
6302
|
+
import { join as join27, relative as relative10, resolve as resolve7, sep as sep2 } from "node:path";
|
|
6266
6303
|
function inspectDevSpaceHealth(options) {
|
|
6267
6304
|
const run = options.run ?? runDevSpaceCommand;
|
|
6268
6305
|
const configDirectory = join27(options.homeDir, ".devspace");
|
|
@@ -6368,8 +6405,8 @@ function isLoopbackHost(host) {
|
|
|
6368
6405
|
return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
|
|
6369
6406
|
}
|
|
6370
6407
|
function isPathInside(path, root) {
|
|
6371
|
-
const fromRoot =
|
|
6372
|
-
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${
|
|
6408
|
+
const fromRoot = relative10(resolve7(root), resolve7(path));
|
|
6409
|
+
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
|
|
6373
6410
|
}
|
|
6374
6411
|
|
|
6375
6412
|
// src/portfolio/ai-health/hosts.ts
|
|
@@ -6498,7 +6535,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
|
|
|
6498
6535
|
if (!isRecord3(item) || !isRecord3(item.source)) continue;
|
|
6499
6536
|
const type = item.source.type;
|
|
6500
6537
|
const path = item.source.path;
|
|
6501
|
-
if (typeof path === "string" && path.includes(
|
|
6538
|
+
if (typeof path === "string" && path.includes(sep3 + ".grok" + sep3 + "bundled" + sep3))
|
|
6502
6539
|
skillCounts.bundled += 1;
|
|
6503
6540
|
else if (type === "project") skillCounts.project += 1;
|
|
6504
6541
|
else if (type === "plugin") skillCounts.plugin += 1;
|
|
@@ -6537,19 +6574,19 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
|
|
|
6537
6574
|
const resolvedRoot = safeRealpath(root) ?? resolve8(root);
|
|
6538
6575
|
if (resolvedSource === join28(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
|
|
6539
6576
|
return "project-shared";
|
|
6540
|
-
if (resolvedSource.startsWith(resolvedRoot +
|
|
6577
|
+
if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
|
|
6541
6578
|
if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
|
|
6542
6579
|
if (localClaudeNames.has(name)) return "project-local";
|
|
6543
6580
|
if (userClaudeNames.has(name)) return "user";
|
|
6544
6581
|
}
|
|
6545
|
-
if (homeDir && resolvedSource.startsWith(resolve8(homeDir) +
|
|
6582
|
+
if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
|
|
6546
6583
|
if (sourceType === "project") return "project";
|
|
6547
6584
|
return "unknown";
|
|
6548
6585
|
}
|
|
6549
6586
|
|
|
6550
6587
|
// src/portfolio/ai-health/secrets.ts
|
|
6551
6588
|
import { existsSync as existsSync29, lstatSync as lstatSync12, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
|
|
6552
|
-
import { join as join29, relative as
|
|
6589
|
+
import { join as join29, relative as relative11, sep as sep4 } from "node:path";
|
|
6553
6590
|
function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
|
|
6554
6591
|
const centralPath = join29(secretsRoot, id);
|
|
6555
6592
|
const centralRealPath = safeRealpath(centralPath);
|
|
@@ -6600,7 +6637,7 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
|
|
|
6600
6637
|
if (!SKIP_ENV_DIRECTORIES.has(entry.name))
|
|
6601
6638
|
found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
|
|
6602
6639
|
} else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
|
|
6603
|
-
found.push(
|
|
6640
|
+
found.push(relative11(root, join29(current, entry.name)));
|
|
6604
6641
|
}
|
|
6605
6642
|
}
|
|
6606
6643
|
} catch {
|
|
@@ -6615,7 +6652,7 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
|
|
|
6615
6652
|
for (const entry of readdirSync12(current, { withFileTypes: true })) {
|
|
6616
6653
|
const path = join29(current, entry.name);
|
|
6617
6654
|
if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
|
|
6618
|
-
else found.push({ path:
|
|
6655
|
+
else found.push({ path: relative11(root, path), mode: modeString(lstatSync12(path).mode) });
|
|
6619
6656
|
}
|
|
6620
6657
|
} catch {
|
|
6621
6658
|
return found;
|
|
@@ -6642,8 +6679,8 @@ function pointsInside(path, expectedRoot) {
|
|
|
6642
6679
|
if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
|
|
6643
6680
|
const target = safeRealpath(path);
|
|
6644
6681
|
if (!target) return false;
|
|
6645
|
-
const fromRoot =
|
|
6646
|
-
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${
|
|
6682
|
+
const fromRoot = relative11(expectedRoot, target);
|
|
6683
|
+
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
|
|
6647
6684
|
}
|
|
6648
6685
|
function hasUnsafeCentralSecretPermissions(secrets) {
|
|
6649
6686
|
return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
|
|
@@ -6653,7 +6690,7 @@ function inspectSecretsRoot(path) {
|
|
|
6653
6690
|
}
|
|
6654
6691
|
|
|
6655
6692
|
// src/portfolio/ai-health/skills.ts
|
|
6656
|
-
import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as
|
|
6693
|
+
import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync8 } from "node:fs";
|
|
6657
6694
|
import { join as join30 } from "node:path";
|
|
6658
6695
|
function countAutomaticSkillsNeedingReview(skills) {
|
|
6659
6696
|
return skills.automatic.filter(
|
|
@@ -6741,7 +6778,7 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
|
|
|
6741
6778
|
let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
|
|
6742
6779
|
let realPath;
|
|
6743
6780
|
try {
|
|
6744
|
-
realPath =
|
|
6781
|
+
realPath = realpathSync8(path);
|
|
6745
6782
|
} catch {
|
|
6746
6783
|
if (stat.isSymbolicLink()) kind = "dangling-symlink";
|
|
6747
6784
|
}
|
|
@@ -6802,8 +6839,8 @@ function inspectClaudeSkillRoot(root) {
|
|
|
6802
6839
|
const stat = lstatSync13(path);
|
|
6803
6840
|
if (stat.isSymbolicLink()) {
|
|
6804
6841
|
try {
|
|
6805
|
-
const target =
|
|
6806
|
-
return target ===
|
|
6842
|
+
const target = realpathSync8(path);
|
|
6843
|
+
return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
|
|
6807
6844
|
} catch {
|
|
6808
6845
|
return "dangling-symlink";
|
|
6809
6846
|
}
|
|
@@ -7568,8 +7605,12 @@ function runPortfolioDoctor(args) {
|
|
|
7568
7605
|
console.error(`Unknown portfolio target: ${options.value.targetId}`);
|
|
7569
7606
|
return 1;
|
|
7570
7607
|
}
|
|
7608
|
+
const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
|
|
7609
|
+
if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
|
|
7610
|
+
return reportMissingPortfolioRegistry(loaded, options.value.json);
|
|
7611
|
+
}
|
|
7571
7612
|
const loadedAssets = loadAgentAssetRegistry({
|
|
7572
|
-
agentAssetsDir
|
|
7613
|
+
agentAssetsDir
|
|
7573
7614
|
});
|
|
7574
7615
|
if (loadedAssets.issues.length > 0) {
|
|
7575
7616
|
for (const issue of loadedAssets.issues) console.error(`${issue.type}: ${issue.message}`);
|
|
@@ -7670,8 +7711,12 @@ function runPortfolioPlan(args) {
|
|
|
7670
7711
|
console.error(`Unknown portfolio target: ${options.value.targetId}`);
|
|
7671
7712
|
return 1;
|
|
7672
7713
|
}
|
|
7714
|
+
const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
|
|
7715
|
+
if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
|
|
7716
|
+
return reportMissingPortfolioRegistry(loaded, options.value.json);
|
|
7717
|
+
}
|
|
7673
7718
|
const loadedAssets = loadAgentAssetRegistry({
|
|
7674
|
-
agentAssetsDir
|
|
7719
|
+
agentAssetsDir
|
|
7675
7720
|
});
|
|
7676
7721
|
if (loadedAssets.issues.length > 0) {
|
|
7677
7722
|
for (const issue of loadedAssets.issues) {
|
|
@@ -7760,8 +7805,12 @@ function runPortfolioAssetsCheck(args) {
|
|
|
7760
7805
|
console.error(`Unknown portfolio target: ${options.value.targetId}`);
|
|
7761
7806
|
return 1;
|
|
7762
7807
|
}
|
|
7808
|
+
const agentAssetsDir = findPortfolioAgentAssetsDir(loaded.manifest);
|
|
7809
|
+
if (loaded.manifest.executionEngine?.path && !agentAssetsDir) {
|
|
7810
|
+
return reportMissingPortfolioRegistry(loaded, options.value.json);
|
|
7811
|
+
}
|
|
7763
7812
|
const loadedAssets = loadAgentAssetRegistry({
|
|
7764
|
-
agentAssetsDir
|
|
7813
|
+
agentAssetsDir
|
|
7765
7814
|
});
|
|
7766
7815
|
if (loadedAssets.issues.length > 0) {
|
|
7767
7816
|
if (options.value.json) {
|
|
@@ -7874,6 +7923,31 @@ function findPortfolioAgentAssetsDir(manifest) {
|
|
|
7874
7923
|
const agentAssetsDir = manifest?.executionEngine?.path ? join34(manifest.executionEngine.path, "agent-assets") : void 0;
|
|
7875
7924
|
return agentAssetsDir && existsSync33(join34(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
|
|
7876
7925
|
}
|
|
7926
|
+
function reportMissingPortfolioRegistry(loaded, json) {
|
|
7927
|
+
const expectedPath = loaded.manifest?.executionEngine?.path ? join34(loaded.manifest.executionEngine.path, "agent-assets/registry.json") : "executionEngine.path/agent-assets/registry.json";
|
|
7928
|
+
const issue = {
|
|
7929
|
+
type: "missing-control-plane-registry",
|
|
7930
|
+
message: `Portfolio control-plane registry is required at ${expectedPath}; refusing the package fallback registry.`
|
|
7931
|
+
};
|
|
7932
|
+
if (json) {
|
|
7933
|
+
console.log(
|
|
7934
|
+
JSON.stringify(
|
|
7935
|
+
{
|
|
7936
|
+
ok: false,
|
|
7937
|
+
configPath: loaded.configPath,
|
|
7938
|
+
portfolioId: loaded.manifest?.portfolioId,
|
|
7939
|
+
issues: [issue],
|
|
7940
|
+
targets: []
|
|
7941
|
+
},
|
|
7942
|
+
null,
|
|
7943
|
+
2
|
|
7944
|
+
)
|
|
7945
|
+
);
|
|
7946
|
+
} else {
|
|
7947
|
+
console.error(`${issue.type}: ${issue.message}`);
|
|
7948
|
+
}
|
|
7949
|
+
return 1;
|
|
7950
|
+
}
|
|
7877
7951
|
function printUsage5() {
|
|
7878
7952
|
console.error("Usage:");
|
|
7879
7953
|
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.
|
|
3
|
+
"version": "0.9.2",
|
|
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.
|
|
38
|
+
"@pieai/doc-gov": "^0.9.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@pieai/swimmer-ui-kit": "1.3.2",
|