@pieai/pro-gov 0.5.0 → 0.5.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.
package/dist/cli.js
CHANGED
|
@@ -2903,7 +2903,7 @@ function validatePortfolioManifest(value) {
|
|
|
2903
2903
|
continue;
|
|
2904
2904
|
}
|
|
2905
2905
|
validateEndpoint(target, "targets", issues, technologyCatalog);
|
|
2906
|
-
validateAllowedFields(target, "target", ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions"], issues);
|
|
2906
|
+
validateAllowedFields(target, "target", ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions", "environmentPolicy"], issues);
|
|
2907
2907
|
if (typeof target.id === "string") {
|
|
2908
2908
|
if (seenTargetIds.has(target.id)) {
|
|
2909
2909
|
issues.push({
|
|
@@ -2962,7 +2962,7 @@ function validateEndpoint(value, field, issues, technologyCatalog) {
|
|
|
2962
2962
|
validateAllowedFields(
|
|
2963
2963
|
value,
|
|
2964
2964
|
field,
|
|
2965
|
-
field === "targets" ? ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions"] : ["id", "path", "projectType", "capabilities", "boundary", "technologyExceptions"],
|
|
2965
|
+
field === "targets" ? ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions", "environmentPolicy"] : ["id", "path", "projectType", "capabilities", "boundary", "technologyExceptions", "environmentPolicy"],
|
|
2966
2966
|
issues
|
|
2967
2967
|
);
|
|
2968
2968
|
if (typeof value.id !== "string" || value.id.length === 0) {
|
|
@@ -3080,6 +3080,14 @@ function isRepositoryRelativePath(value) {
|
|
|
3080
3080
|
const segments = value.replaceAll("\\", "/").split("/");
|
|
3081
3081
|
return !segments.includes("..");
|
|
3082
3082
|
}
|
|
3083
|
+
function isExactRepositoryRelativePath(value) {
|
|
3084
|
+
if (value.length === 0) return false;
|
|
3085
|
+
if (isAbsolute3(value)) return false;
|
|
3086
|
+
if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\") || value.startsWith("//")) return false;
|
|
3087
|
+
if (value.includes("\\")) return false;
|
|
3088
|
+
const segments = value.split("/");
|
|
3089
|
+
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
3090
|
+
}
|
|
3083
3091
|
function validateRepositoryGovernance(value, field, catalog, issues) {
|
|
3084
3092
|
const id = typeof value.id === "string" ? value.id : void 0;
|
|
3085
3093
|
if (value.projectType !== void 0 && (typeof value.projectType !== "string" || !catalog.projectTypes.has(value.projectType))) {
|
|
@@ -3113,6 +3121,53 @@ function validateRepositoryGovernance(value, field, catalog, issues) {
|
|
|
3113
3121
|
if (typeof exception.reason !== "string" || exception.reason.length === 0) issues.push({ type: "invalid-field", id, field: `${field}.technologyExceptions.reason`, message: "Technology exception reason must be non-empty." });
|
|
3114
3122
|
}
|
|
3115
3123
|
}
|
|
3124
|
+
validateEnvironmentPolicy(value.environmentPolicy, field, id, issues);
|
|
3125
|
+
}
|
|
3126
|
+
function validateEnvironmentPolicy(value, field, id, issues) {
|
|
3127
|
+
if (value === void 0) return;
|
|
3128
|
+
if (!isRecord(value)) {
|
|
3129
|
+
issues.push({ type: "invalid-field", id, field: `${field}.environmentPolicy`, message: "Repository environmentPolicy must be an object." });
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
validateAllowedFields(value, "environmentPolicy", ["localOnly"], issues);
|
|
3133
|
+
if (value.localOnly === void 0) return;
|
|
3134
|
+
if (!Array.isArray(value.localOnly)) {
|
|
3135
|
+
issues.push({ type: "invalid-field", id, field: `${field}.environmentPolicy.localOnly`, message: "environmentPolicy.localOnly must be an array." });
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
3139
|
+
for (const entry of value.localOnly) {
|
|
3140
|
+
if (!isRecord(entry)) {
|
|
3141
|
+
issues.push({ type: "invalid-field", id, field: `${field}.environmentPolicy.localOnly`, message: "environmentPolicy.localOnly entry must be an object." });
|
|
3142
|
+
continue;
|
|
3143
|
+
}
|
|
3144
|
+
validateAllowedFields(entry, "environmentPolicy.localOnly", ["path", "reason"], issues);
|
|
3145
|
+
if (typeof entry.path !== "string" || !isExactRepositoryRelativePath(entry.path)) {
|
|
3146
|
+
issues.push({
|
|
3147
|
+
type: "invalid-field",
|
|
3148
|
+
id,
|
|
3149
|
+
field: `${field}.environmentPolicy.localOnly.path`,
|
|
3150
|
+
message: `environmentPolicy.localOnly path must be an exact repository-relative path without absolute, backslash, empty, '.', or '..' segments: ${String(entry.path)}`
|
|
3151
|
+
});
|
|
3152
|
+
} else if (seenPaths.has(entry.path)) {
|
|
3153
|
+
issues.push({
|
|
3154
|
+
type: "invalid-field",
|
|
3155
|
+
id,
|
|
3156
|
+
field: `${field}.environmentPolicy.localOnly.path`,
|
|
3157
|
+
message: `Duplicate environmentPolicy.localOnly path: ${entry.path}`
|
|
3158
|
+
});
|
|
3159
|
+
} else {
|
|
3160
|
+
seenPaths.add(entry.path);
|
|
3161
|
+
}
|
|
3162
|
+
if (typeof entry.reason !== "string" || entry.reason.trim().length === 0) {
|
|
3163
|
+
issues.push({
|
|
3164
|
+
type: "invalid-field",
|
|
3165
|
+
id,
|
|
3166
|
+
field: `${field}.environmentPolicy.localOnly.reason`,
|
|
3167
|
+
message: "environmentPolicy.localOnly reason must be a non-empty string."
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3116
3171
|
}
|
|
3117
3172
|
function validateHostTooling(value, issues) {
|
|
3118
3173
|
if (value === void 0) return;
|
|
@@ -3600,7 +3655,7 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
|
|
|
3600
3655
|
claudeCode: jsonObjectKeys(join20(root, ".claude/settings.json"), "mcpServers"),
|
|
3601
3656
|
codex: tomlMcpNames(join20(root, ".codex/config.toml"))
|
|
3602
3657
|
};
|
|
3603
|
-
const secrets = inspectRepositorySecrets(root, endpoint.id, secretsRoot, git.isRepository);
|
|
3658
|
+
const secrets = inspectRepositorySecrets(root, endpoint.id, secretsRoot, git.isRepository, endpoint.environmentPolicy);
|
|
3604
3659
|
const projectModel = inspectProjectModel(root, endpoint, technologyGovernance);
|
|
3605
3660
|
const recommendations = [];
|
|
3606
3661
|
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");
|
|
@@ -3622,9 +3677,10 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
|
|
|
3622
3677
|
if (skills.claudeCompatibility === "dangling-symlink") recommendations.push("`.claude/skills` \u662F\u65AD\u5F00\u7684\u94FE\u63A5\u3002");
|
|
3623
3678
|
if (hasWorkflowReminderHooks(hooks)) recommendations.push("\u53D1\u73B0 Stop/SubagentStop hook\uFF1B\u786E\u8BA4\u5B83\u662F\u5426\u4ECD\u6709\u9879\u76EE\u4E13\u5C5E\u7528\u9014\uFF0C\u5E76\u79FB\u9664\u9000\u4F11\u7684 PGS \u5DE5\u4F5C\u6D41\u63D0\u9192\u3002");
|
|
3624
3679
|
const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
|
|
3680
|
+
const envNeedsCentralReview = liveEnv.filter((file) => !file.localOnly);
|
|
3625
3681
|
if (liveEnv.some((file) => file.tracked)) recommendations.push("\u53D1\u73B0\u88AB Git \u8DDF\u8E2A\u7684\u771F\u5B9E\u73AF\u5883\u6587\u4EF6\uFF1B\u5E94\u7ACB\u5373\u786E\u8BA4\u5176\u4E2D\u662F\u5426\u5305\u542B\u51ED\u636E\u5E76\u8FC1\u51FA\u4ED3\u5E93\u3002");
|
|
3626
|
-
if (
|
|
3627
|
-
else if (
|
|
3682
|
+
if (envNeedsCentralReview.length > 0 && secrets.centralDirectory === "absent") recommendations.push("\u4ED3\u5E93\u6709\u672C\u5730\u73AF\u5883\u6587\u4EF6\uFF0C\u4F46\u4E2D\u592E `.secrets` \u4E2D\u6CA1\u6709\u5BF9\u5E94\u76EE\u5F55\uFF1B\u786E\u8BA4\u662F\u5426\u9700\u8981\u7EB3\u5165\u5206\u5C42\u7BA1\u7406\u3002");
|
|
3683
|
+
else if (envNeedsCentralReview.some((file) => !file.centralized)) recommendations.push("\u53D1\u73B0\u771F\u5B9E\u73AF\u5883\u6587\u4EF6\u5C1A\u672A\u8FDE\u63A5\u5230\u672C\u9879\u76EE\u7684\u4E2D\u592E `.secrets` \u76EE\u5F55\uFF1B\u533A\u5206\u53EF\u4E22\u5F03\u751F\u6210\u7269\u4E0E\u672C\u5730\u4E3B\u6765\u6E90\uFF0C\u9700\u4FDD\u7559\u7684\u6765\u6E90\u5E94\u96C6\u4E2D\u540E\u518D\u63A5\u56DE\u9879\u76EE\u3002");
|
|
3628
3684
|
if (hasUnsafeCentralSecretPermissions(secrets)) recommendations.push("\u4E2D\u592E\u5BC6\u94A5\u76EE\u5F55\u6216\u6587\u4EF6\u6743\u9650\u8FC7\u5BBD\uFF1B\u76EE\u5F55\u5E94\u4E3A 0700\uFF0C\u914D\u7F6E\u6587\u4EF6\u5E94\u4E3A 0600\u3002");
|
|
3629
3685
|
if (!docs.packages.aligned) recommendations.push(`PGS \u5305\u7248\u672C\u672A\u4E0E\u6267\u884C\u5F15\u64CE ${docs.packages.expected ?? "\u672A\u77E5\u7248\u672C"} \u5BF9\u9F50\uFF1B\u53D1\u5E03\u4E0A\u6E38\u540E\u518D\u540C\u6B65\u76EE\u6807\u4ED3\u5E93\u3002`);
|
|
3630
3686
|
if (role === "target" && !docs.manifest) recommendations.push("\u7F3A\u5C11 docs/governance/MANIFEST.yml\uFF1B\u6587\u6863\u6E05\u5355\u65E0\u6CD5\u8BC1\u660E\u5DF2\u540C\u6B65\u3002");
|
|
@@ -3655,7 +3711,7 @@ function deriveStatus(entries, git, hooks, skills, secrets, projectModel, techno
|
|
|
3655
3711
|
const missingBaseline = projectModel.baseline.some((technology) => !technology.detected && !hasBaselineException(projectModel, technology.id));
|
|
3656
3712
|
const missingSelected = projectModel.optionalCapabilities.some((technology) => technology.selected && !technology.detected);
|
|
3657
3713
|
const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
|
|
3658
|
-
const secretMaterializationNeedsReview = liveEnv.some((file) => !file.centralized);
|
|
3714
|
+
const secretMaterializationNeedsReview = liveEnv.some((file) => !file.centralized && !file.localOnly);
|
|
3659
3715
|
if (entries.agents !== "pgs-router" || entries.claude !== "agents-symlink" || hasWorkflowReminderHooks(hooks) || skills.claudeCompatibility === "duplicate-directory" || skills.claudeCompatibility === "dangling-symlink" || git.branches.length > 1 || git.worktrees.length > 1 || git.dirtyPaths.length > 0 || (git.ahead ?? 0) > 0 || secretMaterializationNeedsReview || technologyGovernanceConfigured && !projectModel.projectType || missingBaseline || missingSelected) return "attention";
|
|
3660
3716
|
return "healthy";
|
|
3661
3717
|
}
|
|
@@ -3816,17 +3872,25 @@ function inspectClaudeSkillRoot(root) {
|
|
|
3816
3872
|
}
|
|
3817
3873
|
return stat.isDirectory() ? "duplicate-directory" : "other";
|
|
3818
3874
|
}
|
|
3819
|
-
function inspectRepositorySecrets(root, id, secretsRoot, isRepository) {
|
|
3875
|
+
function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
|
|
3820
3876
|
const centralPath = join20(secretsRoot, id);
|
|
3821
3877
|
const centralRealPath = safeRealpath(centralPath);
|
|
3822
|
-
const
|
|
3823
|
-
path,
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3878
|
+
const localOnlyReasons = new Map(
|
|
3879
|
+
(environmentPolicy?.localOnly ?? []).map((entry) => [entry.path, entry.reason])
|
|
3880
|
+
);
|
|
3881
|
+
const envFiles = collectEnvironmentFiles(root).map((path) => {
|
|
3882
|
+
const localOnlyReason = localOnlyReasons.get(path);
|
|
3883
|
+
return {
|
|
3884
|
+
path,
|
|
3885
|
+
tracked: isRepository ? gitTracks(root, path) : false,
|
|
3886
|
+
template: isEnvironmentTemplate(path),
|
|
3887
|
+
fixture: isEnvironmentFixture(path),
|
|
3888
|
+
symlink: lstatSync6(join20(root, path)).isSymbolicLink(),
|
|
3889
|
+
centralized: pointsInside(join20(root, path), centralRealPath),
|
|
3890
|
+
localOnly: localOnlyReason !== void 0,
|
|
3891
|
+
...localOnlyReason !== void 0 ? { localOnlyReason } : {}
|
|
3892
|
+
};
|
|
3893
|
+
});
|
|
3830
3894
|
return {
|
|
3831
3895
|
centralDirectory: existsSync21(centralPath) ? "present" : "absent",
|
|
3832
3896
|
centralMode: existsSync21(centralPath) ? modeString(statSync4(centralPath).mode) : void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pieai/pro-gov",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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.5.
|
|
38
|
+
"@pieai/doc-gov": "^0.5.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@pieai/swimmer-ui-kit": "1.0.1",
|