@pieai/pro-gov 0.7.2 → 0.7.3
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/README.md +9 -10
- package/assets/docs/reference/adoption/adoption-playbook.md +8 -8
- package/assets/docs/reference/adoption/migration-v1.0.md +3 -3
- package/assets/docs/reference/adoption/recommended-agent-tooling.md +16 -5
- package/assets/integrations/.gitkeep +1 -0
- package/assets/portfolio-dashboard/app.js +5 -5
- package/assets/profiles/engineering-runtime/manifest.yml +0 -2
- package/assets/profiles/engineering-runtime/profile.md +5 -4
- package/assets/public-agent-assets/registry.json +1 -1
- package/assets/starter/.github/workflows/docs-check.yml +11 -1
- package/assets/starter/docs/governance/agents-routing/engineering-runtime-v1.1.md +19 -1
- package/assets/starter/docs/governance/boundary.md +3 -4
- package/assets/starter/docs/governance/doc-agent-rules.md +2 -2
- package/assets/starter/docs/governance/doc-types.md +2 -2
- package/assets/starter/docs/governance/ssot-v1.1.md +3 -5
- package/assets/starter/docs/governance/templates/adr.md +24 -2
- package/assets/starter/docs/reference/documentation-map.md +1 -1
- package/dist/cli.js +381 -85
- package/package.json +2 -2
- package/assets/integrations/mattpocock-skills.md +0 -42
package/dist/cli.js
CHANGED
|
@@ -577,10 +577,10 @@ function createAssetInstallPlan(options) {
|
|
|
577
577
|
const placement = options.placement ?? "registry";
|
|
578
578
|
const assetsById = new Map(options.registry.assets.map((asset) => [asset.id, asset]));
|
|
579
579
|
const bundlesById = new Map(options.bundles.map((bundle) => [bundle.id, bundle]));
|
|
580
|
-
const assetIds =
|
|
580
|
+
const assetIds = resolveAssetIds(options.bundleIds, options.assetIds ?? [], bundlesById);
|
|
581
581
|
const assets = assetIds.map((assetId) => {
|
|
582
582
|
const asset = assetsById.get(assetId);
|
|
583
|
-
if (!asset) throw new Error(`Unknown asset id in
|
|
583
|
+
if (!asset) throw new Error(`Unknown asset id in install plan: ${assetId}`);
|
|
584
584
|
if (asset.kind === "skill" && !asset.hosts.includes(options.host)) {
|
|
585
585
|
throw new Error(`Asset ${assetId} does not support host ${options.host}`);
|
|
586
586
|
}
|
|
@@ -661,8 +661,8 @@ function createAssetInstallPlan(options) {
|
|
|
661
661
|
]
|
|
662
662
|
};
|
|
663
663
|
}
|
|
664
|
-
function
|
|
665
|
-
const ids =
|
|
664
|
+
function resolveAssetIds(bundleIds, explicitAssetIds, bundlesById) {
|
|
665
|
+
const ids = new Set(explicitAssetIds);
|
|
666
666
|
for (const bundleId of bundleIds) {
|
|
667
667
|
const bundle = bundlesById.get(bundleId);
|
|
668
668
|
if (!bundle) throw new Error(`Unknown bundle id: ${bundleId}`);
|
|
@@ -1437,6 +1437,7 @@ function runAssetsPlan(args) {
|
|
|
1437
1437
|
registry: loaded.registry,
|
|
1438
1438
|
bundles: loadAgentAssetBundles(loaded.agentAssetsDir),
|
|
1439
1439
|
bundleIds: options.value.bundleIds,
|
|
1440
|
+
assetIds: options.value.assetIds,
|
|
1440
1441
|
host: options.value.host,
|
|
1441
1442
|
placement: options.value.placement
|
|
1442
1443
|
});
|
|
@@ -1519,6 +1520,7 @@ function parsePlanOptions(args) {
|
|
|
1519
1520
|
targetDir: process.cwd(),
|
|
1520
1521
|
json: false,
|
|
1521
1522
|
bundleIds: [],
|
|
1523
|
+
assetIds: [],
|
|
1522
1524
|
host: "codex"
|
|
1523
1525
|
};
|
|
1524
1526
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -1533,6 +1535,11 @@ function parsePlanOptions(args) {
|
|
|
1533
1535
|
if (!bundleId) return { ok: false, error: "Expected --bundle <bundle-id>" };
|
|
1534
1536
|
options.bundleIds.push(bundleId);
|
|
1535
1537
|
index += 1;
|
|
1538
|
+
} else if (arg === "--asset") {
|
|
1539
|
+
const assetId = args[index + 1];
|
|
1540
|
+
if (!assetId) return { ok: false, error: "Expected --asset <asset-id>" };
|
|
1541
|
+
options.assetIds.push(assetId);
|
|
1542
|
+
index += 1;
|
|
1536
1543
|
} else if (arg === "--host") {
|
|
1537
1544
|
const host = args[index + 1];
|
|
1538
1545
|
if (!isHost(host)) {
|
|
@@ -1564,8 +1571,8 @@ function parsePlanOptions(args) {
|
|
|
1564
1571
|
return { ok: false, error: `Unknown assets plan option: ${arg}` };
|
|
1565
1572
|
}
|
|
1566
1573
|
}
|
|
1567
|
-
if (options.bundleIds.length === 0) {
|
|
1568
|
-
return { ok: false, error: "Expected at least one --bundle <bundle-id>" };
|
|
1574
|
+
if (options.bundleIds.length === 0 && options.assetIds.length === 0) {
|
|
1575
|
+
return { ok: false, error: "Expected at least one --bundle <bundle-id> or --asset <asset-id>" };
|
|
1569
1576
|
}
|
|
1570
1577
|
return { ok: true, value: options };
|
|
1571
1578
|
}
|
|
@@ -1711,7 +1718,7 @@ function printUsage() {
|
|
|
1711
1718
|
console.error(" pro-gov assets list [--registry] [--json] [--visibility public|private|third-party|all]");
|
|
1712
1719
|
console.error(" pro-gov assets discover [--target <path>] [--json]");
|
|
1713
1720
|
console.error(" pro-gov assets recommend [--target <path>] [--json]");
|
|
1714
|
-
console.error(" pro-gov assets plan --bundle <bundle-id> [--
|
|
1721
|
+
console.error(" pro-gov assets plan [--bundle <bundle-id>] [--asset <asset-id>] [--target <path>] [--host codex|claude-code|gemini-cli|antigravity] [--placement auto|manual] [--out <path>] [--json]");
|
|
1715
1722
|
console.error(" pro-gov assets apply --plan <path>");
|
|
1716
1723
|
console.error(" pro-gov assets check [--target <path>] [--json]");
|
|
1717
1724
|
console.error(" pro-gov assets public-check [--public-root <path>] [--private-root <path>] [--json]");
|
|
@@ -2817,13 +2824,30 @@ import { homedir } from "node:os";
|
|
|
2817
2824
|
import { join as join18 } from "node:path";
|
|
2818
2825
|
var DEFAULT_CACHE_THRESHOLD_BYTES = 1e9;
|
|
2819
2826
|
var MAX_CACHE_ENTRIES = 2e4;
|
|
2820
|
-
function
|
|
2821
|
-
const legacyDirectories = inspectLegacyDirectories(root);
|
|
2827
|
+
function inspectHostRedundancy(options = {}) {
|
|
2822
2828
|
const homeDir = options.homeDir ?? homedir();
|
|
2823
|
-
const cachePaths = getPlaywrightCachePaths(
|
|
2824
|
-
|
|
2829
|
+
const cachePaths = getPlaywrightCachePaths(
|
|
2830
|
+
homeDir,
|
|
2831
|
+
options.playwrightBrowsersPath ?? process.env.PLAYWRIGHT_BROWSERS_PATH
|
|
2832
|
+
);
|
|
2833
|
+
const playwrightCaches = cachePaths.map((path) => inspectPlaywrightCache(path, options.cache));
|
|
2825
2834
|
const cacheThresholdBytes = options.cacheThresholdBytes ?? DEFAULT_CACHE_THRESHOLD_BYTES;
|
|
2826
|
-
const status =
|
|
2835
|
+
const status = playwrightCaches.some(
|
|
2836
|
+
(cache) => cache.exists && (cache.bytes >= cacheThresholdBytes || cache.truncated)
|
|
2837
|
+
) ? "attention" : "clean";
|
|
2838
|
+
return {
|
|
2839
|
+
status,
|
|
2840
|
+
playwrightCaches,
|
|
2841
|
+
recommendations: status === "attention" ? [
|
|
2842
|
+
"\u53D1\u73B0\u5927\u578B\u6216\u672A\u5B8C\u6574\u8BA1\u6570\u7684 Playwright \u6D4F\u89C8\u5668\u7F13\u5B58\uFF1B\u786E\u8BA4\u6CA1\u6709\u6D4B\u8BD5\u4EFB\u52A1\u5360\u7528\u540E\uFF0C\u6309\u5BBF\u4E3B\u7EA7\u7F13\u5B58\u7EDF\u4E00\u6E05\u7406\u3002"
|
|
2843
|
+
] : []
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
function inspectProjectRedundancy(root, options = {}) {
|
|
2847
|
+
const legacyDirectories = inspectLegacyDirectories(root);
|
|
2848
|
+
const hostRedundancy = options.includePlaywrightCaches === false ? void 0 : inspectHostRedundancy(options);
|
|
2849
|
+
const playwrightCaches = hostRedundancy?.playwrightCaches ?? [];
|
|
2850
|
+
const status = legacyDirectories.length > 0 || hostRedundancy?.status === "attention" ? "attention" : "clean";
|
|
2827
2851
|
return { status, legacyDirectories, playwrightCaches };
|
|
2828
2852
|
}
|
|
2829
2853
|
function inspectLegacyDirectories(root) {
|
|
@@ -2831,13 +2855,15 @@ function inspectLegacyDirectories(root) {
|
|
|
2831
2855
|
const path = join18(root, relativePath);
|
|
2832
2856
|
if (!existsSync16(path)) return [];
|
|
2833
2857
|
const stats = collectDirectoryStats(path);
|
|
2834
|
-
return [
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2858
|
+
return [
|
|
2859
|
+
{
|
|
2860
|
+
path: relativePath,
|
|
2861
|
+
kind: "legacy-ai-directory",
|
|
2862
|
+
fileCount: stats.fileCount,
|
|
2863
|
+
bytes: stats.bytes,
|
|
2864
|
+
reason: "\u9879\u76EE\u7EA7\u65E7 AI \u5BBF\u4E3B\u76EE\u5F55\uFF1B\u5E94\u4E0E .agents/skills \u7684 SSOT \u9010\u9879\u6BD4\u5BF9\u540E\u518D\u51B3\u5B9A\u662F\u5426\u8FC1\u79FB\uFF0C\u626B\u63CF\u5668\u4E0D\u81EA\u52A8\u5220\u9664\u3002"
|
|
2865
|
+
}
|
|
2866
|
+
];
|
|
2841
2867
|
}
|
|
2842
2868
|
function getPlaywrightCachePaths(homeDir, configuredPath) {
|
|
2843
2869
|
const candidates = [
|
|
@@ -2848,18 +2874,40 @@ function getPlaywrightCachePaths(homeDir, configuredPath) {
|
|
|
2848
2874
|
].filter((path) => Boolean(path));
|
|
2849
2875
|
return [...new Set(candidates)];
|
|
2850
2876
|
}
|
|
2851
|
-
function inspectPlaywrightCache(path) {
|
|
2877
|
+
function inspectPlaywrightCache(path, cache) {
|
|
2878
|
+
const cached = cache?.get(path);
|
|
2879
|
+
if (cached) return cached;
|
|
2852
2880
|
if (!existsSync16(path)) {
|
|
2853
|
-
|
|
2881
|
+
const missing = {
|
|
2882
|
+
path,
|
|
2883
|
+
exists: false,
|
|
2884
|
+
fileCount: 0,
|
|
2885
|
+
bytes: 0,
|
|
2886
|
+
revisionCount: 0,
|
|
2887
|
+
truncated: false
|
|
2888
|
+
};
|
|
2889
|
+
cache?.set(path, missing);
|
|
2890
|
+
return missing;
|
|
2854
2891
|
}
|
|
2855
2892
|
const stats = collectDirectoryStats(path);
|
|
2856
2893
|
let revisionCount = 0;
|
|
2857
2894
|
try {
|
|
2858
|
-
revisionCount = readdirSync7(path, { withFileTypes: true }).filter(
|
|
2895
|
+
revisionCount = readdirSync7(path, { withFileTypes: true }).filter(
|
|
2896
|
+
(entry) => entry.isDirectory()
|
|
2897
|
+
).length;
|
|
2859
2898
|
} catch {
|
|
2860
2899
|
revisionCount = 0;
|
|
2861
2900
|
}
|
|
2862
|
-
|
|
2901
|
+
const evidence = {
|
|
2902
|
+
path,
|
|
2903
|
+
exists: true,
|
|
2904
|
+
fileCount: stats.fileCount,
|
|
2905
|
+
bytes: stats.bytes,
|
|
2906
|
+
revisionCount,
|
|
2907
|
+
truncated: stats.truncated
|
|
2908
|
+
};
|
|
2909
|
+
cache?.set(path, evidence);
|
|
2910
|
+
return evidence;
|
|
2863
2911
|
}
|
|
2864
2912
|
function collectDirectoryStats(root) {
|
|
2865
2913
|
let fileCount = 0;
|
|
@@ -3397,7 +3445,7 @@ function validateTechnologyGovernance(value, issues) {
|
|
|
3397
3445
|
issues.push({ type: "invalid-field", field: "technologyGovernance", message: "Portfolio technologyGovernance must be an object." });
|
|
3398
3446
|
return { technologies, projectTypes };
|
|
3399
3447
|
}
|
|
3400
|
-
validateAllowedFields(value, "technologyGovernance", ["strategySource", "versionPolicy", "technologies", "projectTypes"], issues);
|
|
3448
|
+
validateAllowedFields(value, "technologyGovernance", ["strategySource", "versionPolicy", "exclusiveOwnership", "technologies", "projectTypes"], issues);
|
|
3401
3449
|
if (value.strategySource !== void 0 && (typeof value.strategySource !== "string" || value.strategySource.length === 0)) {
|
|
3402
3450
|
issues.push({ type: "invalid-field", field: "technologyGovernance.strategySource", message: "Technology strategySource must be a non-empty string." });
|
|
3403
3451
|
}
|
|
@@ -3460,9 +3508,65 @@ function validateTechnologyGovernance(value, issues) {
|
|
|
3460
3508
|
if (!technologies.has(technology)) issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes", message: `Project type ${projectType.id} references unknown technology: ${technology}` });
|
|
3461
3509
|
}
|
|
3462
3510
|
}
|
|
3511
|
+
validateExclusiveOwnership(value.exclusiveOwnership, projectTypes, issues);
|
|
3463
3512
|
validateVersionPolicy(value.versionPolicy, projectTypes, issues);
|
|
3464
3513
|
return { technologies, projectTypes };
|
|
3465
3514
|
}
|
|
3515
|
+
function validateExclusiveOwnership(value, projectTypes, issues) {
|
|
3516
|
+
if (value === void 0) return;
|
|
3517
|
+
const field = "technologyGovernance.exclusiveOwnership";
|
|
3518
|
+
if (!Array.isArray(value)) {
|
|
3519
|
+
issues.push({ type: "invalid-field", field, message: "Technology exclusiveOwnership must be an array." });
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
3523
|
+
for (const rule of value) {
|
|
3524
|
+
if (!isRecord(rule)) {
|
|
3525
|
+
issues.push({ type: "invalid-field", field, message: "Exclusive ownership rule must be an object." });
|
|
3526
|
+
continue;
|
|
3527
|
+
}
|
|
3528
|
+
validateAllowedFields(rule, "exclusiveOwnership", ["id", "label", "allowedProjectTypes", "paths"], issues);
|
|
3529
|
+
const id = typeof rule.id === "string" ? rule.id : "";
|
|
3530
|
+
if (!id || seenIds.has(id)) {
|
|
3531
|
+
issues.push({ type: "invalid-field", field: `${field}.id`, message: `Exclusive ownership rule id must be non-empty and unique: ${String(rule.id)}` });
|
|
3532
|
+
} else {
|
|
3533
|
+
seenIds.add(id);
|
|
3534
|
+
}
|
|
3535
|
+
if (typeof rule.label !== "string" || rule.label.trim().length === 0) {
|
|
3536
|
+
issues.push({ type: "invalid-field", field: `${field}.label`, message: "Exclusive ownership rule label must be a non-empty string." });
|
|
3537
|
+
}
|
|
3538
|
+
validateOptionalStringArray(rule.allowedProjectTypes, id, `${field}.allowedProjectTypes`, issues);
|
|
3539
|
+
if (!Array.isArray(rule.allowedProjectTypes) || rule.allowedProjectTypes.length === 0) {
|
|
3540
|
+
issues.push({ type: "invalid-field", field: `${field}.allowedProjectTypes`, message: "Exclusive ownership rule must allow at least one project type." });
|
|
3541
|
+
} else {
|
|
3542
|
+
for (const projectType of rule.allowedProjectTypes) {
|
|
3543
|
+
if (typeof projectType === "string" && !projectTypes.has(projectType)) {
|
|
3544
|
+
issues.push({ type: "invalid-field", field: `${field}.allowedProjectTypes`, message: `Exclusive ownership rule references unknown project type: ${projectType}` });
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
validateOptionalStringArray(rule.paths, id, `${field}.paths`, issues);
|
|
3549
|
+
if (!Array.isArray(rule.paths) || rule.paths.length === 0) {
|
|
3550
|
+
issues.push({ type: "invalid-field", field: `${field}.paths`, message: "Exclusive ownership rule must declare at least one repository-relative path." });
|
|
3551
|
+
} else {
|
|
3552
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
3553
|
+
for (const path of rule.paths) {
|
|
3554
|
+
if (typeof path !== "string") continue;
|
|
3555
|
+
if (!isExactRepositoryRelativePath(path)) {
|
|
3556
|
+
issues.push({
|
|
3557
|
+
type: "invalid-field",
|
|
3558
|
+
field: `${field}.paths`,
|
|
3559
|
+
message: `Exclusive ownership path must be an exact repository-relative path: ${path}`
|
|
3560
|
+
});
|
|
3561
|
+
} else if (seenPaths.has(path)) {
|
|
3562
|
+
issues.push({ type: "invalid-field", field: `${field}.paths`, message: `Duplicate exclusive ownership path: ${path}` });
|
|
3563
|
+
} else {
|
|
3564
|
+
seenPaths.add(path);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3466
3570
|
function validateVersionPolicy(value, projectTypes, issues) {
|
|
3467
3571
|
if (value === void 0) return;
|
|
3468
3572
|
if (!isRecord(value)) {
|
|
@@ -4039,9 +4143,12 @@ function collectPackageManifests(root) {
|
|
|
4039
4143
|
"build",
|
|
4040
4144
|
"coverage",
|
|
4041
4145
|
"dist",
|
|
4146
|
+
"examples",
|
|
4147
|
+
"fixtures",
|
|
4042
4148
|
"node_modules",
|
|
4043
4149
|
"out",
|
|
4044
4150
|
"target",
|
|
4151
|
+
"templates",
|
|
4045
4152
|
"tmp",
|
|
4046
4153
|
".cache",
|
|
4047
4154
|
".pnpm-store",
|
|
@@ -4289,6 +4396,18 @@ import { homedir as homedir4 } from "node:os";
|
|
|
4289
4396
|
import { dirname as dirname14, join as join24, relative as relative8, resolve as resolve5, sep } from "node:path";
|
|
4290
4397
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
4291
4398
|
var CURRENT_ROUTER_VERSION = "1.1";
|
|
4399
|
+
var MCP_DISCOVERY_PATHS = {
|
|
4400
|
+
project: {
|
|
4401
|
+
codex: ".codex/config.toml",
|
|
4402
|
+
claudeCodeShared: ".mcp.json",
|
|
4403
|
+
grok: ".grok/config.toml"
|
|
4404
|
+
},
|
|
4405
|
+
user: {
|
|
4406
|
+
codex: ".codex/config.toml",
|
|
4407
|
+
claudeCode: ".claude.json",
|
|
4408
|
+
grok: ".grok/config.toml"
|
|
4409
|
+
}
|
|
4410
|
+
};
|
|
4292
4411
|
function inspectPortfolioAiHealth(options) {
|
|
4293
4412
|
const allEndpoints = collectEndpoints(options.manifest);
|
|
4294
4413
|
const endpoints = options.targetId && options.targetId !== "all" ? allEndpoints.filter(({ endpoint }) => endpoint.id === options.targetId) : allEndpoints;
|
|
@@ -4305,6 +4424,7 @@ function inspectPortfolioAiHealth(options) {
|
|
|
4305
4424
|
const grokVersion = commandVersion("grok");
|
|
4306
4425
|
const executionEngineRoot = options.manifest.executionEngine?.path;
|
|
4307
4426
|
const skillRegistry = inspectSkillRegistry(executionEngineRoot);
|
|
4427
|
+
const userSkills = inspectUserSkillEvidence(join24(homeDir, ".agents/skills"));
|
|
4308
4428
|
const expectedPackageVersion = packageVersion(
|
|
4309
4429
|
join24(executionEngineRoot ?? "", "packages/pro-gov/package.json")
|
|
4310
4430
|
);
|
|
@@ -4316,13 +4436,15 @@ function inspectPortfolioAiHealth(options) {
|
|
|
4316
4436
|
homeDir,
|
|
4317
4437
|
expectedPackageVersion,
|
|
4318
4438
|
options.manifest.technologyGovernance,
|
|
4319
|
-
grokVersion
|
|
4439
|
+
grokVersion,
|
|
4440
|
+
skillRegistry.skills,
|
|
4441
|
+
userSkills
|
|
4320
4442
|
)
|
|
4321
4443
|
);
|
|
4322
4444
|
const summary = { healthy: 0, attention: 0, unhealthy: 0 };
|
|
4323
4445
|
for (const repository of repositories) summary[repository.status] += 1;
|
|
4324
4446
|
return {
|
|
4325
|
-
schemaVersion:
|
|
4447
|
+
schemaVersion: 6,
|
|
4326
4448
|
portfolioId: options.manifest.portfolioId,
|
|
4327
4449
|
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4328
4450
|
coverage: {
|
|
@@ -4339,7 +4461,7 @@ function inspectPortfolioAiHealth(options) {
|
|
|
4339
4461
|
allEndpoints.map(({ endpoint }) => endpoint.path),
|
|
4340
4462
|
options.manifest.specialistChecks?.devspace
|
|
4341
4463
|
),
|
|
4342
|
-
skillRegistry,
|
|
4464
|
+
skillRegistry: skillRegistry.health,
|
|
4343
4465
|
technologyGovernance: {
|
|
4344
4466
|
strategySource: options.manifest.technologyGovernance?.strategySource,
|
|
4345
4467
|
versionPolicy: options.manifest.technologyGovernance?.versionPolicy,
|
|
@@ -4424,20 +4546,23 @@ function collectEndpoints(manifest) {
|
|
|
4424
4546
|
return true;
|
|
4425
4547
|
});
|
|
4426
4548
|
}
|
|
4427
|
-
function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackageVersion, technologyGovernance, grokVersion) {
|
|
4549
|
+
function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackageVersion, technologyGovernance, grokVersion, registeredSkills, userSkills) {
|
|
4428
4550
|
const root = endpoint.path;
|
|
4429
4551
|
const git = inspectGit2(root);
|
|
4430
4552
|
const entries = inspectEntries(root);
|
|
4431
4553
|
const grokInspection = inspectGrokProject(root, homeDir, grokVersion);
|
|
4432
|
-
const skills = inspectSkills(root, grokInspection);
|
|
4554
|
+
const skills = inspectSkills(root, grokInspection, registeredSkills, userSkills);
|
|
4433
4555
|
const hostSsot = inspectProjectHostSsot(root);
|
|
4434
4556
|
const hooks = inspectHooks(root);
|
|
4435
4557
|
const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
|
|
4436
4558
|
const mcp = {
|
|
4437
|
-
codexProject: tomlMcpNames(join24(root,
|
|
4438
|
-
claudeCodeProjectShared: jsonObjectKeys(
|
|
4559
|
+
codexProject: tomlMcpNames(join24(root, MCP_DISCOVERY_PATHS.project.codex)),
|
|
4560
|
+
claudeCodeProjectShared: jsonObjectKeys(
|
|
4561
|
+
join24(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
|
|
4562
|
+
"mcpServers"
|
|
4563
|
+
),
|
|
4439
4564
|
claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
|
|
4440
|
-
grokProject: tomlMcpNames(join24(root,
|
|
4565
|
+
grokProject: tomlMcpNames(join24(root, MCP_DISCOVERY_PATHS.project.grok)),
|
|
4441
4566
|
grokEffective: grokInspection.effectiveMcp,
|
|
4442
4567
|
grokInspection: grokInspection.inspection
|
|
4443
4568
|
};
|
|
@@ -4449,13 +4574,21 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
|
|
|
4449
4574
|
endpoint.environmentPolicy
|
|
4450
4575
|
);
|
|
4451
4576
|
const projectModel = inspectProjectModel(root, endpoint, technologyGovernance);
|
|
4577
|
+
const exclusiveOwnershipViolations = inspectExclusiveOwnership(
|
|
4578
|
+
root,
|
|
4579
|
+
endpoint,
|
|
4580
|
+
technologyGovernance
|
|
4581
|
+
);
|
|
4452
4582
|
const versions = inspectVersionPolicy(
|
|
4453
4583
|
root,
|
|
4454
4584
|
technologyGovernance?.versionPolicy,
|
|
4455
4585
|
endpoint.projectType
|
|
4456
4586
|
);
|
|
4457
4587
|
const verification = inspectProjectVerification(root);
|
|
4458
|
-
const redundancy = inspectProjectRedundancy(root, {
|
|
4588
|
+
const redundancy = inspectProjectRedundancy(root, {
|
|
4589
|
+
homeDir,
|
|
4590
|
+
includePlaywrightCaches: false
|
|
4591
|
+
});
|
|
4459
4592
|
const recommendations = [];
|
|
4460
4593
|
if (!git.isRepository) recommendations.push("\u8BE5\u8DEF\u5F84\u4E0D\u662F Git \u4ED3\u5E93\uFF1B\u786E\u8BA4\u6E05\u5355\u8DEF\u5F84\u662F\u5426\u6B63\u786E\u3002");
|
|
4461
4594
|
if (git.unmergedBranches.length > 0)
|
|
@@ -4495,8 +4628,32 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
|
|
|
4495
4628
|
recommendations.push("GEMINI.md \u662F\u72EC\u7ACB\u5165\u53E3\uFF1B\u82E5\u6CA1\u6709 Gemini \u4E13\u5C5E\u5DEE\u5F02\uFF0C\u5EFA\u8BAE\u94FE\u63A5\u5230 AGENTS.md\u3002");
|
|
4496
4629
|
if (entries.gemini === "dangling-symlink")
|
|
4497
4630
|
recommendations.push("GEMINI.md \u662F\u65AD\u5F00\u7684\u94FE\u63A5\uFF1B\u9700\u8981\u91CD\u65B0\u6307\u5411 AGENTS.md\u3002");
|
|
4498
|
-
if (skills.
|
|
4499
|
-
recommendations.push("`.agents/skills` \u4E2D\u5B58\u5728\u65AD\u5F00\u7684\u6280\u80FD\u94FE\u63A5\u3002");
|
|
4631
|
+
if ([...skills.automatic, ...skills.manual].some((skill) => skill.kind === "dangling-symlink"))
|
|
4632
|
+
recommendations.push("`.agents/skills` \u6216 `.agents/manual-skills` \u4E2D\u5B58\u5728\u65AD\u5F00\u7684\u6280\u80FD\u94FE\u63A5\u3002");
|
|
4633
|
+
if (skills.invalidEntries.length > 0)
|
|
4634
|
+
recommendations.push(
|
|
4635
|
+
`\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`
|
|
4636
|
+
);
|
|
4637
|
+
if (skills.userDuplicates.length > 0)
|
|
4638
|
+
recommendations.push(
|
|
4639
|
+
`\u9879\u76EE\u6280\u80FD\u4E0E\u7528\u6237\u7EA7\u6280\u80FD\u91CD\u590D\uFF1A${skills.userDuplicates.join("\u3001")}\uFF1B\u4F18\u5148\u4FDD\u7559\u7528\u6237\u7EA7\u5355\u4E00\u5165\u53E3\u3002`
|
|
4640
|
+
);
|
|
4641
|
+
if (skills.crossPlacementDuplicates.length > 0)
|
|
4642
|
+
recommendations.push(
|
|
4643
|
+
`\u540C\u540D\u6280\u80FD\u540C\u65F6\u51FA\u73B0\u5728 auto \u4E0E manual\uFF1A${skills.crossPlacementDuplicates.join("\u3001")}\uFF1B\u53EA\u80FD\u4FDD\u7559\u4E00\u4E2A\u671F\u671B\u4F4D\u7F6E\u3002`
|
|
4644
|
+
);
|
|
4645
|
+
if (skills.placementDrift.length > 0)
|
|
4646
|
+
recommendations.push(
|
|
4647
|
+
`\u6709 ${skills.placementDrift.length} \u4E2A\u6280\u80FD\u672A\u653E\u5728\u6CE8\u518C\u8868\u89C4\u5B9A\u7684\u4F4D\u7F6E\uFF1A${skills.placementDrift.map((item) => `${item.name} \u5E94\u4E3A ${item.reason === "scope" ? "\u7528\u6237\u7EA7" : `\u9879\u76EE ${item.expectedPlacement}`}`).join("\u3001")}\u3002`
|
|
4648
|
+
);
|
|
4649
|
+
if (skills.automatic.length > skills.automaticReviewThreshold)
|
|
4650
|
+
recommendations.push(
|
|
4651
|
+
`\u81EA\u52A8\u53D1\u73B0\u6280\u80FD\u6709 ${skills.automatic.length} \u4E2A\uFF0C\u8D85\u8FC7 ${skills.automaticReviewThreshold} \u4E2A\u590D\u6838\u7EBF\uFF1B\u9010\u9879\u786E\u8BA4\u662F\u5426\u9AD8\u9891\u4E14\u5141\u8BB8\u4E3B\u52A8\u89E6\u53D1\u3002`
|
|
4652
|
+
);
|
|
4653
|
+
if (skills.unregistered.length > 0)
|
|
4654
|
+
recommendations.push(
|
|
4655
|
+
`\u6709 ${skills.unregistered.length} \u4E2A\u6280\u80FD\u65E0\u6CD5\u6620\u5C04\u5230 PGS \u6CE8\u518C\u6E90\uFF1B\u786E\u8BA4\u5B83\u4EEC\u662F\u5408\u7406\u7684\u9879\u76EE\u672C\u5730\u6280\u80FD\u8FD8\u662F\u5F85\u767B\u8BB0\u8D44\u4EA7\u3002`
|
|
4656
|
+
);
|
|
4500
4657
|
if (skills.claudeCompatibility === "duplicate-directory")
|
|
4501
4658
|
recommendations.push(
|
|
4502
4659
|
"`.claude/skills` \u662F\u72EC\u7ACB\u526F\u672C\uFF1B\u5EFA\u8BAE\u94FE\u63A5\u5230 `.agents/skills`\uFF0C\u907F\u514D\u53CC\u4EFD\u6280\u80FD\u6F02\u79FB\u3002"
|
|
@@ -4545,6 +4702,11 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
|
|
|
4545
4702
|
recommendations.push("\u7F3A\u5C11 docs/governance/MANIFEST.yml\uFF1B\u6587\u6863\u6E05\u5355\u65E0\u6CD5\u8BC1\u660E\u5DF2\u540C\u6B65\u3002");
|
|
4546
4703
|
if (technologyGovernance && !projectModel.projectType)
|
|
4547
4704
|
recommendations.push("\u672A\u58F0\u660E\u9879\u76EE\u7C7B\u578B\uFF1B\u65E0\u6CD5\u628A\u5B9E\u9645\u6280\u672F\u4E0E\u4EA7\u54C1\u7EBF\u57FA\u7EBF\u8FDB\u884C\u6BD4\u8F83\u3002");
|
|
4705
|
+
for (const violation of exclusiveOwnershipViolations) {
|
|
4706
|
+
recommendations.push(
|
|
4707
|
+
`\u4ED3\u5E93\u8D8A\u754C\u62E5\u6709\u201C${violation.rule.label}\u201D\uFF1A${violation.paths.join("\u3001")}\uFF1B\u4EC5\u5141\u8BB8\u9879\u76EE\u7C7B\u578B ${violation.rule.allowedProjectTypes.join("\u3001")} \u6301\u6709\u3002`
|
|
4708
|
+
);
|
|
4709
|
+
}
|
|
4548
4710
|
const missingBaseline = projectModel.baseline.filter(
|
|
4549
4711
|
(technology) => !technology.detected && !hasBaselineException(projectModel, technology.id)
|
|
4550
4712
|
);
|
|
@@ -4569,7 +4731,7 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
|
|
|
4569
4731
|
);
|
|
4570
4732
|
if (redundancy.status === "attention")
|
|
4571
4733
|
recommendations.push(
|
|
4572
|
-
"\u53D1\u73B0\u65E7 AI \u76EE\u5F55\
|
|
4734
|
+
"\u53D1\u73B0\u9879\u76EE\u7EA7\u65E7 AI \u76EE\u5F55\uFF1B\u4EC5\u63D0\u4F9B\u8BC1\u636E\uFF0C\u4E0E .agents/skills \u7684 SSOT \u6BD4\u5BF9\u540E\u518D\u7531\u4EBA\u5DE5\u8FC1\u79FB\u6216\u6E05\u7406\u3002"
|
|
4573
4735
|
);
|
|
4574
4736
|
return {
|
|
4575
4737
|
id: endpoint.id,
|
|
@@ -4586,6 +4748,7 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
|
|
|
4586
4748
|
secrets,
|
|
4587
4749
|
docs,
|
|
4588
4750
|
projectModel,
|
|
4751
|
+
exclusiveOwnershipViolations,
|
|
4589
4752
|
Boolean(technologyGovernance),
|
|
4590
4753
|
versions,
|
|
4591
4754
|
verification,
|
|
@@ -4733,8 +4896,8 @@ function hasUsableTechnologyFile(path) {
|
|
|
4733
4896
|
return false;
|
|
4734
4897
|
}
|
|
4735
4898
|
}
|
|
4736
|
-
function deriveStatus(role, entries, git, hooks, skills, hostSsot, secrets, docs, projectModel, technologyGovernanceConfigured, versions, verification, redundancy) {
|
|
4737
|
-
if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || skills.
|
|
4899
|
+
function deriveStatus(role, entries, git, hooks, skills, hostSsot, secrets, docs, projectModel, exclusiveOwnershipViolations, technologyGovernanceConfigured, versions, verification, redundancy) {
|
|
4900
|
+
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))
|
|
4738
4901
|
return "unhealthy";
|
|
4739
4902
|
const missingBaseline = projectModel.baseline.some(
|
|
4740
4903
|
(technology) => !technology.detected && !hasBaselineException(projectModel, technology.id)
|
|
@@ -4749,10 +4912,18 @@ function deriveStatus(role, entries, git, hooks, skills, hostSsot, secrets, docs
|
|
|
4749
4912
|
const packageVersionNeedsReview = docs.packages.expected !== void 0 && !docs.packages.aligned;
|
|
4750
4913
|
const routerVersionNeedsReview = !docs.routerAligned;
|
|
4751
4914
|
const targetManifestMissing = role === "target" && !docs.manifest;
|
|
4752
|
-
if (entries.agents !== "pgs-router" || entries.claude !== "agents-symlink" || hasWorkflowReminderHooks(hooks) || skills.claudeCompatibility === "duplicate-directory" || skills.claudeCompatibility === "dangling-symlink" || !hostSsot.compliant || git.branches.length > 1 || git.worktrees.length > 1 || git.dirtyPaths.length > 0 || (git.ahead ?? 0) > 0 || secretMaterializationNeedsReview || technologyGovernanceConfigured && !projectModel.projectType || missingBaseline || missingSelected || packageVersionNeedsReview || routerVersionNeedsReview || targetManifestMissing || versions.status === "attention" || verification.status === "attention" || redundancy.legacyDirectories.length > 0)
|
|
4915
|
+
if (entries.agents !== "pgs-router" || entries.claude !== "agents-symlink" || hasWorkflowReminderHooks(hooks) || skills.claudeCompatibility === "duplicate-directory" || skills.claudeCompatibility === "dangling-symlink" || skills.invalidEntries.length > 0 || skills.userDuplicates.length > 0 || skills.crossPlacementDuplicates.length > 0 || skills.placementDrift.length > 0 || skills.automatic.length > skills.automaticReviewThreshold || !hostSsot.compliant || git.branches.length > 1 || git.worktrees.length > 1 || git.dirtyPaths.length > 0 || (git.ahead ?? 0) > 0 || secretMaterializationNeedsReview || technologyGovernanceConfigured && !projectModel.projectType || missingBaseline || missingSelected || packageVersionNeedsReview || routerVersionNeedsReview || targetManifestMissing || versions.status === "attention" || verification.status === "attention" || redundancy.legacyDirectories.length > 0)
|
|
4753
4916
|
return "attention";
|
|
4754
4917
|
return "healthy";
|
|
4755
4918
|
}
|
|
4919
|
+
function inspectExclusiveOwnership(root, endpoint, governance) {
|
|
4920
|
+
const projectType = endpoint.projectType;
|
|
4921
|
+
return (governance?.exclusiveOwnership ?? []).flatMap((rule) => {
|
|
4922
|
+
if (projectType && rule.allowedProjectTypes.includes(projectType)) return [];
|
|
4923
|
+
const paths = rule.paths.filter((path) => existsSync23(join24(root, path)));
|
|
4924
|
+
return paths.length > 0 ? [{ rule, paths }] : [];
|
|
4925
|
+
});
|
|
4926
|
+
}
|
|
4756
4927
|
function inspectProjectModel(root, endpoint, governance) {
|
|
4757
4928
|
const projectType = governance?.projectTypes.find(
|
|
4758
4929
|
(candidate) => candidate.id === endpoint.projectType
|
|
@@ -4880,51 +5051,142 @@ function inspectOptionalEntry(root, filename, agentsPath) {
|
|
|
4880
5051
|
const content = safeRead(path);
|
|
4881
5052
|
return /AGENTS\.md/.test(content) && content.length < 2e3 ? "thin-adapter" : "custom";
|
|
4882
5053
|
}
|
|
4883
|
-
function inspectSkills(root, grokInspection) {
|
|
5054
|
+
function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
|
|
4884
5055
|
const lock = readJson4(join24(root, ".pro-gov/assets.lock.json"));
|
|
5056
|
+
const assetManifest = readJson4(join24(root, ".pro-gov/assets.json"));
|
|
4885
5057
|
const managed = /* @__PURE__ */ new Set();
|
|
4886
5058
|
const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
|
|
4887
5059
|
if (isRecord3(lock) && Array.isArray(lock.assets)) {
|
|
4888
5060
|
for (const asset of lock.assets) {
|
|
4889
5061
|
if (!isRecord3(asset) || typeof asset.targetPath !== "string") continue;
|
|
4890
|
-
const match = asset.targetPath.match(/^\.agents\/skills\/([^/]+)$/);
|
|
4891
|
-
if (match) managed.add(match[1]);
|
|
5062
|
+
const match = asset.targetPath.match(/^\.agents\/(skills|manual-skills)\/([^/]+)$/);
|
|
5063
|
+
if (match) managed.add(`${match[1]}:${match[2]}`);
|
|
4892
5064
|
}
|
|
4893
5065
|
}
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
-
const
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
const
|
|
5066
|
+
const inspectPlacement = (placement) => {
|
|
5067
|
+
const directory = placement === "auto" ? "skills" : "manual-skills";
|
|
5068
|
+
const skillRoot = join24(root, ".agents", directory);
|
|
5069
|
+
if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
|
|
5070
|
+
return safeReadDir(skillRoot).filter((name) => !name.startsWith(".")).map((name) => inspectSkillItem(skillRoot, directory, name, managed, registeredSkills));
|
|
5071
|
+
};
|
|
5072
|
+
const automatic = inspectPlacement("auto");
|
|
5073
|
+
const manual = inspectPlacement("manual");
|
|
5074
|
+
const invalidEntries = [
|
|
5075
|
+
...inspectInvalidSkillEntries(root, "skills"),
|
|
5076
|
+
...inspectInvalidSkillEntries(root, "manual-skills")
|
|
5077
|
+
];
|
|
5078
|
+
const userDuplicates = [...automatic, ...manual].filter((item) => skillDuplicatesUser(item, root, userSkills)).map((item) => item.name).filter((name, index, names) => names.indexOf(name) === index).sort();
|
|
5079
|
+
const automaticNames = new Set(automatic.map((item) => item.name));
|
|
5080
|
+
const crossPlacementDuplicates = manual.map((item) => item.name).filter((name) => automaticNames.has(name)).sort();
|
|
5081
|
+
const placementDrift = [
|
|
5082
|
+
...automatic.flatMap((item) => skillPlacementDrift(item, "auto")),
|
|
5083
|
+
...manual.flatMap((item) => skillPlacementDrift(item, "manual"))
|
|
5084
|
+
];
|
|
5085
|
+
const unregistered = [
|
|
5086
|
+
...automatic.flatMap(
|
|
5087
|
+
(item) => item.registryId || item.classification ? [] : [{ name: item.name, placement: "auto" }]
|
|
5088
|
+
),
|
|
5089
|
+
...manual.flatMap(
|
|
5090
|
+
(item) => item.registryId || item.classification ? [] : [{ name: item.name, placement: "manual" }]
|
|
5091
|
+
)
|
|
5092
|
+
];
|
|
5093
|
+
const lockedSkills = isRecord3(lock) && Array.isArray(lock.assets) ? lock.assets.filter(
|
|
5094
|
+
(asset) => isRecord3(asset) && typeof asset.targetPath === "string" && /^\.agents\/(skills|manual-skills)\/[^/]+$/.test(asset.targetPath)
|
|
5095
|
+
) : [];
|
|
5096
|
+
const desiredAssetIds = new Set(
|
|
5097
|
+
isRecord3(assetManifest) ? stringArray(assetManifest.assetIds) : []
|
|
5098
|
+
);
|
|
5099
|
+
const desired = registeredSkills.filter(
|
|
5100
|
+
(skill) => desiredAssetIds.has(skill.id) && skill.defaultScope === "project"
|
|
5101
|
+
).length;
|
|
5102
|
+
const allInstalled = [...automatic, ...manual];
|
|
4909
5103
|
return {
|
|
4910
|
-
|
|
5104
|
+
automatic,
|
|
5105
|
+
manual,
|
|
5106
|
+
invalidEntries,
|
|
5107
|
+
userDuplicates,
|
|
5108
|
+
crossPlacementDuplicates,
|
|
5109
|
+
placementDrift,
|
|
5110
|
+
unregistered,
|
|
5111
|
+
automaticReviewThreshold: 10,
|
|
4911
5112
|
claudeCompatibility: inspectClaudeSkillRoot(root),
|
|
4912
5113
|
bundleIds,
|
|
4913
5114
|
lifecycle: {
|
|
4914
|
-
desired
|
|
4915
|
-
locked,
|
|
4916
|
-
installed:
|
|
4917
|
-
discoverable:
|
|
5115
|
+
desired,
|
|
5116
|
+
locked: lockedSkills.length,
|
|
5117
|
+
installed: allInstalled.filter((item) => item.managed && item.kind !== "dangling-symlink").length,
|
|
5118
|
+
discoverable: automatic.filter((item) => item.kind !== "dangling-symlink").length,
|
|
4918
5119
|
runtime: "unobservable"
|
|
4919
5120
|
},
|
|
4920
5121
|
hosts: {
|
|
4921
|
-
codexProject:
|
|
5122
|
+
codexProject: automatic.filter((item) => item.kind !== "dangling-symlink").length,
|
|
4922
5123
|
claudeCodeProject: inspectSkillRoot(join24(root, ".claude/skills")).names.length,
|
|
4923
5124
|
grokNativeProject: inspectSkillRoot(join24(root, ".grok/skills")).names.length,
|
|
4924
5125
|
grokEffective: grokInspection.skills
|
|
4925
5126
|
}
|
|
4926
5127
|
};
|
|
4927
5128
|
}
|
|
5129
|
+
function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills) {
|
|
5130
|
+
const path = join24(skillRoot, name);
|
|
5131
|
+
const stat = lstatSync8(path);
|
|
5132
|
+
let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
|
|
5133
|
+
let realPath;
|
|
5134
|
+
try {
|
|
5135
|
+
realPath = realpathSync4(path);
|
|
5136
|
+
} catch {
|
|
5137
|
+
if (stat.isSymbolicLink()) kind = "dangling-symlink";
|
|
5138
|
+
}
|
|
5139
|
+
const registered = realPath ? registeredSkills.find((skill) => skill.sourceRealPath === realPath) : void 0;
|
|
5140
|
+
const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync23(join24(path, "SKILL.md")) ? "project-local" : void 0;
|
|
5141
|
+
return {
|
|
5142
|
+
name,
|
|
5143
|
+
kind,
|
|
5144
|
+
managed: managed.has(`${directory}:${name}`),
|
|
5145
|
+
classification,
|
|
5146
|
+
registryId: registered?.id,
|
|
5147
|
+
expectedPlacement: registered?.defaultPlacement,
|
|
5148
|
+
expectedScope: registered?.defaultScope
|
|
5149
|
+
};
|
|
5150
|
+
}
|
|
5151
|
+
function isPluginPack(path) {
|
|
5152
|
+
const skillsRoot = join24(path, "skills");
|
|
5153
|
+
return existsSync23(join24(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync23(join24(skillsRoot, name, "SKILL.md")));
|
|
5154
|
+
}
|
|
5155
|
+
function inspectInvalidSkillEntries(root, directory) {
|
|
5156
|
+
const skillRoot = join24(root, ".agents", directory);
|
|
5157
|
+
if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
|
|
5158
|
+
return safeReadDir(skillRoot).flatMap((name) => {
|
|
5159
|
+
if (name === ".gitkeep") return [];
|
|
5160
|
+
if (name === ".DS_Store")
|
|
5161
|
+
return [{ path: `.agents/${directory}/${name}`, reason: "metadata-junk" }];
|
|
5162
|
+
if (name.startsWith("."))
|
|
5163
|
+
return [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }];
|
|
5164
|
+
const path = join24(skillRoot, name);
|
|
5165
|
+
return !lstatSync8(path).isDirectory() && !lstatSync8(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
|
|
5166
|
+
});
|
|
5167
|
+
}
|
|
5168
|
+
function skillDuplicatesUser(item, root, userSkills) {
|
|
5169
|
+
if (userSkills.names.has(item.name)) return true;
|
|
5170
|
+
const automatic = join24(root, ".agents/skills", item.name);
|
|
5171
|
+
const manual = join24(root, ".agents/manual-skills", item.name);
|
|
5172
|
+
const realPath = safeRealpath(pathLexists(automatic) ? automatic : manual);
|
|
5173
|
+
return realPath ? userSkills.realPaths.has(realPath) : false;
|
|
5174
|
+
}
|
|
5175
|
+
function skillPlacementDrift(item, actualPlacement) {
|
|
5176
|
+
if (!item.registryId || !item.expectedPlacement) return [];
|
|
5177
|
+
if (item.expectedScope === "user" || item.expectedPlacement !== actualPlacement)
|
|
5178
|
+
return [
|
|
5179
|
+
{
|
|
5180
|
+
name: item.name,
|
|
5181
|
+
registryId: item.registryId,
|
|
5182
|
+
actualPlacement,
|
|
5183
|
+
expectedPlacement: item.expectedPlacement,
|
|
5184
|
+
expectedScope: item.expectedScope ?? "project",
|
|
5185
|
+
reason: item.expectedScope === "user" ? "scope" : "placement"
|
|
5186
|
+
}
|
|
5187
|
+
];
|
|
5188
|
+
return [];
|
|
5189
|
+
}
|
|
4928
5190
|
function inspectClaudeSkillRoot(root) {
|
|
4929
5191
|
const path = join24(root, ".claude/skills");
|
|
4930
5192
|
if (!pathLexists(path)) return "missing";
|
|
@@ -5048,9 +5310,9 @@ function inspectSecretsRoot(path) {
|
|
|
5048
5310
|
return existsSync23(path) ? { path, exists: true, mode: modeString(statSync5(path).mode) } : { path, exists: false };
|
|
5049
5311
|
}
|
|
5050
5312
|
function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceSettings) {
|
|
5051
|
-
const codexConfig = join24(homeDir,
|
|
5052
|
-
const claudeConfig = join24(homeDir,
|
|
5053
|
-
const grokConfig = join24(homeDir,
|
|
5313
|
+
const codexConfig = join24(homeDir, MCP_DISCOVERY_PATHS.user.codex);
|
|
5314
|
+
const claudeConfig = join24(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
|
|
5315
|
+
const grokConfig = join24(homeDir, MCP_DISCOVERY_PATHS.user.grok);
|
|
5054
5316
|
const hostEnvironment = {
|
|
5055
5317
|
mcp: {
|
|
5056
5318
|
codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
|
|
@@ -5069,7 +5331,8 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
|
|
|
5069
5331
|
available: grokVersion !== void 0,
|
|
5070
5332
|
...grokVersion ? { version: grokVersion } : {},
|
|
5071
5333
|
inspectionCommand: "grok inspect --json"
|
|
5072
|
-
}
|
|
5334
|
+
},
|
|
5335
|
+
redundancy: inspectHostRedundancy({ homeDir })
|
|
5073
5336
|
};
|
|
5074
5337
|
if (devspaceSettings) {
|
|
5075
5338
|
hostEnvironment.devspace = inspectDevSpaceHealth({
|
|
@@ -5197,7 +5460,7 @@ function inspectSkillRoot(path) {
|
|
|
5197
5460
|
}
|
|
5198
5461
|
function claudeProjectLocalMcpNames(homeDir, root) {
|
|
5199
5462
|
if (!homeDir) return [];
|
|
5200
|
-
const value = readJson4(join24(homeDir,
|
|
5463
|
+
const value = readJson4(join24(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
|
|
5201
5464
|
if (!isRecord3(value) || !isRecord3(value.projects)) return [];
|
|
5202
5465
|
const candidates = new Set(
|
|
5203
5466
|
[resolve5(root), safeRealpath(root)].filter((path) => Boolean(path))
|
|
@@ -5246,7 +5509,9 @@ function inspectGrokProject(root, homeDir, grokVersion) {
|
|
|
5246
5509
|
})
|
|
5247
5510
|
);
|
|
5248
5511
|
if (!isRecord3(value)) return empty("failed");
|
|
5249
|
-
const userClaudeNames = new Set(
|
|
5512
|
+
const userClaudeNames = new Set(
|
|
5513
|
+
jsonObjectKeys(join24(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
|
|
5514
|
+
);
|
|
5250
5515
|
const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
|
|
5251
5516
|
const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
|
|
5252
5517
|
if (!isRecord3(item) || typeof item.name !== "string") return [];
|
|
@@ -5315,9 +5580,10 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
|
|
|
5315
5580
|
}
|
|
5316
5581
|
const resolvedSource = safeRealpath(sourcePath) ?? resolve5(sourcePath);
|
|
5317
5582
|
const resolvedRoot = safeRealpath(root) ?? resolve5(root);
|
|
5318
|
-
if (resolvedSource === join24(resolvedRoot,
|
|
5583
|
+
if (resolvedSource === join24(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
|
|
5584
|
+
return "project-shared";
|
|
5319
5585
|
if (resolvedSource.startsWith(resolvedRoot + sep)) return "project";
|
|
5320
|
-
if (homeDir && resolvedSource === join24(resolve5(homeDir),
|
|
5586
|
+
if (homeDir && resolvedSource === join24(resolve5(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
|
|
5321
5587
|
if (localClaudeNames.has(name)) return "project-local";
|
|
5322
5588
|
if (userClaudeNames.has(name)) return "user";
|
|
5323
5589
|
}
|
|
@@ -5406,7 +5672,11 @@ function recordOrEmpty(value) {
|
|
|
5406
5672
|
return isRecord3(value) ? value : {};
|
|
5407
5673
|
}
|
|
5408
5674
|
function inspectSkillRegistry(executionEngineRoot) {
|
|
5409
|
-
if (!executionEngineRoot)
|
|
5675
|
+
if (!executionEngineRoot)
|
|
5676
|
+
return {
|
|
5677
|
+
health: { source: 0, registered: 0, bundled: 0, bundles: 0 },
|
|
5678
|
+
skills: []
|
|
5679
|
+
};
|
|
5410
5680
|
const agentAssetsRoot = join24(executionEngineRoot, "agent-assets");
|
|
5411
5681
|
const registry = readJson4(join24(agentAssetsRoot, "registry.json"));
|
|
5412
5682
|
const assets = isRecord3(registry) && Array.isArray(registry.assets) ? registry.assets : [];
|
|
@@ -5428,14 +5698,40 @@ function inspectSkillRegistry(executionEngineRoot) {
|
|
|
5428
5698
|
0
|
|
5429
5699
|
);
|
|
5430
5700
|
return {
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5701
|
+
health: {
|
|
5702
|
+
source,
|
|
5703
|
+
registered: registeredSkills.length,
|
|
5704
|
+
bundled: registeredSkills.filter(
|
|
5705
|
+
(asset) => isRecord3(asset) && typeof asset.id === "string" && bundledIds.has(asset.id)
|
|
5706
|
+
).length,
|
|
5707
|
+
bundles: bundleFiles.length
|
|
5708
|
+
},
|
|
5709
|
+
skills: registeredSkills.flatMap((asset) => {
|
|
5710
|
+
if (!isRecord3(asset) || typeof asset.id !== "string" || typeof asset.sourcePath !== "string" || asset.defaultPlacement !== "auto" && asset.defaultPlacement !== "manual")
|
|
5711
|
+
return [];
|
|
5712
|
+
return [
|
|
5713
|
+
{
|
|
5714
|
+
id: asset.id,
|
|
5715
|
+
sourceRealPath: safeRealpath(join24(agentAssetsRoot, asset.sourcePath)),
|
|
5716
|
+
defaultPlacement: asset.defaultPlacement,
|
|
5717
|
+
defaultScope: asset.defaultScope === "user" ? "user" : "project"
|
|
5718
|
+
}
|
|
5719
|
+
];
|
|
5720
|
+
})
|
|
5437
5721
|
};
|
|
5438
5722
|
}
|
|
5723
|
+
function inspectUserSkillEvidence(root) {
|
|
5724
|
+
const names = /* @__PURE__ */ new Set();
|
|
5725
|
+
const realPaths = /* @__PURE__ */ new Set();
|
|
5726
|
+
if (!pathLexists(root) || !safeIsDirectory(root)) return { names, realPaths };
|
|
5727
|
+
for (const name of safeReadDir(root)) {
|
|
5728
|
+
if (name.startsWith(".")) continue;
|
|
5729
|
+
names.add(name);
|
|
5730
|
+
const realPath = safeRealpath(join24(root, name));
|
|
5731
|
+
if (realPath) realPaths.add(realPath);
|
|
5732
|
+
}
|
|
5733
|
+
return { names, realPaths };
|
|
5734
|
+
}
|
|
5439
5735
|
function jsonObjectKeys(path, key) {
|
|
5440
5736
|
const value = readJson4(path);
|
|
5441
5737
|
if (!isRecord3(value) || !isRecord3(value[key])) return [];
|
|
@@ -5561,7 +5857,12 @@ function runPortfolioAiHealth(args) {
|
|
|
5561
5857
|
return 1;
|
|
5562
5858
|
}
|
|
5563
5859
|
const targetId = options.value.targetId && options.value.targetId !== "all" ? options.value.targetId : void 0;
|
|
5564
|
-
|
|
5860
|
+
const allRepositoryIds = [
|
|
5861
|
+
loaded.manifest.controlPlane?.id,
|
|
5862
|
+
loaded.manifest.executionEngine?.id,
|
|
5863
|
+
...loaded.manifest.targets.map((target) => target.id)
|
|
5864
|
+
].filter((id) => Boolean(id));
|
|
5865
|
+
if (targetId && !allRepositoryIds.includes(targetId)) {
|
|
5565
5866
|
console.error(`Unknown portfolio target: ${targetId}`);
|
|
5566
5867
|
return 1;
|
|
5567
5868
|
}
|
|
@@ -5570,11 +5871,6 @@ function runPortfolioAiHealth(args) {
|
|
|
5570
5871
|
secretsRoot: options.value.secretsRoot,
|
|
5571
5872
|
targetId
|
|
5572
5873
|
});
|
|
5573
|
-
const allRepositoryIds = [
|
|
5574
|
-
loaded.manifest.controlPlane?.id,
|
|
5575
|
-
loaded.manifest.executionEngine?.id,
|
|
5576
|
-
...loaded.manifest.targets.map((target) => target.id)
|
|
5577
|
-
].filter((id) => Boolean(id));
|
|
5578
5874
|
const existing = targetId ? readExistingAiHealthReport(options.value.outDir, loaded.manifest.portfolioId) : void 0;
|
|
5579
5875
|
const report = targetId ? mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) : latest;
|
|
5580
5876
|
const written = writePortfolioAiHealthReport(report, options.value.outDir);
|
|
@@ -6040,7 +6336,7 @@ var COMMANDS = [
|
|
|
6040
6336
|
"assets list [--json] [--visibility public|private|third-party|all]",
|
|
6041
6337
|
"assets discover [--target <path>] [--json]",
|
|
6042
6338
|
"assets recommend [--target <path>] [--json]",
|
|
6043
|
-
"assets plan --bundle <bundle-id> [--target <path>] [--json]",
|
|
6339
|
+
"assets plan [--bundle <bundle-id>] [--asset <asset-id>] [--target <path>] [--json]",
|
|
6044
6340
|
"assets apply --plan <path>",
|
|
6045
6341
|
"assets check [--target <path>] [--strict-registry] [--json]",
|
|
6046
6342
|
"assets public-check [--public-root <path>] [--private-root <path>] [--json]",
|