@pieai/pro-gov 0.4.8 → 0.5.0
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 +2 -2
- package/assets/docs/reference/adoption/adoption-playbook.md +8 -8
- package/assets/docs/reference/adoption/migration-v1.0.md +73 -0
- package/assets/integrations/mattpocock-skills.md +4 -2
- package/assets/portfolio-dashboard/app.css +1 -1
- package/assets/portfolio-dashboard/app.js +6 -6
- package/assets/profiles/doc-only/manifest.yml +1 -1
- package/assets/profiles/doc-only/profile.md +2 -2
- package/assets/profiles/engineering-runtime/manifest.yml +1 -1
- package/assets/profiles/engineering-runtime/profile.md +3 -3
- package/assets/starter/AGENTS.template.md +2 -2
- package/assets/starter/docs/governance/agents-routing/{doc-only-v0.9.md → doc-only-v1.0.md} +4 -4
- package/assets/starter/docs/governance/agents-routing/{engineering-runtime-v0.9.md → engineering-runtime-v1.0.md} +4 -4
- package/assets/starter/docs/governance/boundary.md +6 -4
- package/assets/starter/docs/governance/doc-agent-rules.md +3 -7
- package/assets/starter/docs/governance/doc-types.md +2 -6
- package/assets/starter/docs/governance/{ssot-v0.9.md → ssot-v1.0.md} +12 -9
- package/assets/starter/docs/governance/templates/adr.md +2 -22
- package/assets/starter/docs/reference/documentation-map.md +7 -7
- package/cli-guide.md +2 -2
- package/dist/cli.js +274 -41
- package/package.json +2 -2
- package/assets/starter/docs/decisions/.gitkeep +0 -1
package/dist/cli.js
CHANGED
|
@@ -1654,9 +1654,9 @@ import { createRequire } from "node:module";
|
|
|
1654
1654
|
import { dirname as dirname6, join as join12 } from "node:path";
|
|
1655
1655
|
var REQUIRED_ASSETS = [
|
|
1656
1656
|
"starter/AGENTS.template.md",
|
|
1657
|
-
"starter/docs/governance/ssot-
|
|
1658
|
-
"starter/docs/governance/agents-routing/engineering-runtime-
|
|
1659
|
-
"starter/docs/governance/agents-routing/doc-only-
|
|
1657
|
+
"starter/docs/governance/ssot-v1.0.md",
|
|
1658
|
+
"starter/docs/governance/agents-routing/engineering-runtime-v1.0.md",
|
|
1659
|
+
"starter/docs/governance/agents-routing/doc-only-v1.0.md",
|
|
1660
1660
|
"profiles/engineering-runtime/profile.md",
|
|
1661
1661
|
"profiles/doc-only/profile.md"
|
|
1662
1662
|
];
|
|
@@ -1738,7 +1738,7 @@ function classifyOwnership(targetPath) {
|
|
|
1738
1738
|
return "shared";
|
|
1739
1739
|
}
|
|
1740
1740
|
function isOtherProfileRouting(targetPath, profile) {
|
|
1741
|
-
return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-
|
|
1741
|
+
return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v1.0.md`;
|
|
1742
1742
|
}
|
|
1743
1743
|
function starterTargetPath(sourcePath) {
|
|
1744
1744
|
if (sourcePath === "starter/AGENTS.template.md") return "AGENTS.md";
|
|
@@ -1800,7 +1800,7 @@ function applyStarterFiles(files, profile) {
|
|
|
1800
1800
|
return 0;
|
|
1801
1801
|
}
|
|
1802
1802
|
function renderAgentsTemplate(template, projectName, profile) {
|
|
1803
|
-
const selectedRoute = `docs/governance/agents-routing/${profile}-
|
|
1803
|
+
const selectedRoute = `docs/governance/agents-routing/${profile}-v1.0.md`;
|
|
1804
1804
|
return template.replace("# PROJECT_NAME AI Router", `# ${projectName} AI Router`).replace("`PROFILE_NAME`", `\`${profile}\``).replace("`PROFILE_ROUTE`", `\`${selectedRoute}\``).replace(/\n{3,}/g, "\n\n");
|
|
1805
1805
|
}
|
|
1806
1806
|
function readFlag(args, flag) {
|
|
@@ -1834,18 +1834,22 @@ function recallLearnings(root, options) {
|
|
|
1834
1834
|
return { query, hits };
|
|
1835
1835
|
}
|
|
1836
1836
|
function loadLearningRecords(root) {
|
|
1837
|
-
const
|
|
1838
|
-
const
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1837
|
+
const recordsByTitle = /* @__PURE__ */ new Map();
|
|
1838
|
+
for (const relativeDir of ["docs/reference/learnings", "docs/solutions"]) {
|
|
1839
|
+
const learningDir = join14(root, relativeDir);
|
|
1840
|
+
if (!existsSync14(learningDir)) continue;
|
|
1841
|
+
for (const path of listMarkdownFiles(learningDir)) {
|
|
1842
|
+
const record = readLearningRecord(root, path);
|
|
1843
|
+
const key = record.title.trim().toLowerCase();
|
|
1844
|
+
if (!recordsByTitle.has(key)) recordsByTitle.set(key, record);
|
|
1842
1845
|
}
|
|
1843
1846
|
}
|
|
1844
1847
|
const conceptsPath = join14(root, "CONCEPTS.md");
|
|
1845
1848
|
if (existsSync14(conceptsPath)) {
|
|
1846
|
-
|
|
1849
|
+
const record = readLearningRecord(root, conceptsPath);
|
|
1850
|
+
recordsByTitle.set(`concepts:${record.title.toLowerCase()}`, record);
|
|
1847
1851
|
}
|
|
1848
|
-
return
|
|
1852
|
+
return [...recordsByTitle.values()];
|
|
1849
1853
|
}
|
|
1850
1854
|
function listMarkdownFiles(dir) {
|
|
1851
1855
|
const files = [];
|
|
@@ -1944,7 +1948,7 @@ function cleanMarkdownLine(input) {
|
|
|
1944
1948
|
|
|
1945
1949
|
// src/learning/capture.ts
|
|
1946
1950
|
import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1947
|
-
import { join as join15, relative as relative6 } from "node:path";
|
|
1951
|
+
import { basename as basename5, join as join15, relative as relative6 } from "node:path";
|
|
1948
1952
|
function captureLearning(root, options) {
|
|
1949
1953
|
const title = options.title.trim();
|
|
1950
1954
|
const summary = options.summary.trim();
|
|
@@ -1952,10 +1956,11 @@ function captureLearning(root, options) {
|
|
|
1952
1956
|
if (!summary) throw new Error("summary is required");
|
|
1953
1957
|
const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
|
|
1954
1958
|
const moduleName = options.module?.trim() || "PGS learning capture";
|
|
1955
|
-
const dir = join15(root, "docs/
|
|
1959
|
+
const dir = join15(root, "docs/reference/learnings", category);
|
|
1956
1960
|
mkdirSync5(dir, { recursive: true });
|
|
1957
1961
|
const path = uniquePath(dir, slugify(title) || "learning");
|
|
1958
|
-
|
|
1962
|
+
const idSlug = basename5(path, ".md");
|
|
1963
|
+
writeFileSync4(path, renderLearning({ title, summary, category, moduleName, idSlug }));
|
|
1959
1964
|
return {
|
|
1960
1965
|
relativePath: normalizePath2(relative6(root, path)),
|
|
1961
1966
|
title,
|
|
@@ -1965,12 +1970,23 @@ function captureLearning(root, options) {
|
|
|
1965
1970
|
function renderLearning(options) {
|
|
1966
1971
|
return [
|
|
1967
1972
|
"---",
|
|
1973
|
+
`id: REF-LEARNING-${upperSlug(options.category)}-${upperSlug(options.idSlug)}`,
|
|
1968
1974
|
`title: ${yamlString(options.title)}`,
|
|
1969
|
-
|
|
1975
|
+
"type: reference",
|
|
1976
|
+
"status: stable",
|
|
1977
|
+
"canonical: true",
|
|
1978
|
+
"owner: ai-assisted",
|
|
1979
|
+
`created: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
|
|
1980
|
+
`last_reviewed: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
|
|
1981
|
+
"domain: learning",
|
|
1982
|
+
"tags:",
|
|
1983
|
+
" - learning-recall",
|
|
1984
|
+
` - ${options.category}`,
|
|
1985
|
+
"pinned: false",
|
|
1986
|
+
"related: []",
|
|
1970
1987
|
`category: ${options.category}`,
|
|
1971
1988
|
`module: ${yamlString(options.moduleName)}`,
|
|
1972
1989
|
"capture_mode: pgs-native",
|
|
1973
|
-
"tags: [learning-recall]",
|
|
1974
1990
|
"---",
|
|
1975
1991
|
"",
|
|
1976
1992
|
`# ${options.title}`,
|
|
@@ -2004,6 +2020,9 @@ function yamlString(input) {
|
|
|
2004
2020
|
function normalizePath2(path) {
|
|
2005
2021
|
return path.split("\\").join("/");
|
|
2006
2022
|
}
|
|
2023
|
+
function upperSlug(input) {
|
|
2024
|
+
return slugify(input).toUpperCase() || "LEARNING";
|
|
2025
|
+
}
|
|
2007
2026
|
|
|
2008
2027
|
// src/commands/learn.ts
|
|
2009
2028
|
function runLearn(args) {
|
|
@@ -2173,7 +2192,7 @@ import { dirname as dirname9 } from "node:path";
|
|
|
2173
2192
|
|
|
2174
2193
|
// src/lens/audit.ts
|
|
2175
2194
|
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2176
|
-
import { basename as
|
|
2195
|
+
import { basename as basename6, dirname as dirname8, join as join16 } from "node:path";
|
|
2177
2196
|
var REQUIRED_ARTIFACTS = [
|
|
2178
2197
|
"manifest.md",
|
|
2179
2198
|
"raw/project-lens/architecture-lens.md",
|
|
@@ -2191,7 +2210,7 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
|
|
|
2191
2210
|
version: 1,
|
|
2192
2211
|
target: {
|
|
2193
2212
|
path: targetDir,
|
|
2194
|
-
name:
|
|
2213
|
+
name: basename6(targetDir) || "target"
|
|
2195
2214
|
},
|
|
2196
2215
|
requiredArtifacts: [...REQUIRED_ARTIFACTS]
|
|
2197
2216
|
};
|
|
@@ -2824,6 +2843,7 @@ function resolveManifestPaths(value, configDir) {
|
|
|
2824
2843
|
};
|
|
2825
2844
|
return {
|
|
2826
2845
|
...value,
|
|
2846
|
+
technologyGovernance: isRecord(value.technologyGovernance) && typeof value.technologyGovernance.strategySource === "string" && !isAbsolute3(value.technologyGovernance.strategySource) ? { ...value.technologyGovernance, strategySource: resolve3(configDir, value.technologyGovernance.strategySource) } : value.technologyGovernance,
|
|
2827
2847
|
controlPlane: resolveEndpoint(value.controlPlane),
|
|
2828
2848
|
executionEngine: resolveEndpoint(value.executionEngine),
|
|
2829
2849
|
targets: Array.isArray(value.targets) ? value.targets.map(resolveEndpoint) : value.targets
|
|
@@ -2857,11 +2877,12 @@ function validatePortfolioManifest(value) {
|
|
|
2857
2877
|
validateAllowedFields(
|
|
2858
2878
|
value,
|
|
2859
2879
|
"root",
|
|
2860
|
-
["schemaVersion", "portfolioId", "controlPlane", "executionEngine", "hostTooling", "targets"],
|
|
2880
|
+
["schemaVersion", "portfolioId", "technologyGovernance", "controlPlane", "executionEngine", "hostTooling", "targets"],
|
|
2861
2881
|
issues
|
|
2862
2882
|
);
|
|
2863
|
-
|
|
2864
|
-
validateEndpoint(value.
|
|
2883
|
+
const technologyCatalog = validateTechnologyGovernance(value.technologyGovernance, issues);
|
|
2884
|
+
validateEndpoint(value.controlPlane, "controlPlane", issues, technologyCatalog);
|
|
2885
|
+
validateEndpoint(value.executionEngine, "executionEngine", issues, technologyCatalog);
|
|
2865
2886
|
validateHostTooling(value.hostTooling, issues);
|
|
2866
2887
|
if (!Array.isArray(value.targets)) {
|
|
2867
2888
|
issues.push({
|
|
@@ -2881,8 +2902,8 @@ function validatePortfolioManifest(value) {
|
|
|
2881
2902
|
});
|
|
2882
2903
|
continue;
|
|
2883
2904
|
}
|
|
2884
|
-
validateEndpoint(target, "targets", issues);
|
|
2885
|
-
validateAllowedFields(target, "target", ["id", "path", "profile", "assetBundles"], issues);
|
|
2905
|
+
validateEndpoint(target, "targets", issues, technologyCatalog);
|
|
2906
|
+
validateAllowedFields(target, "target", ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions"], issues);
|
|
2886
2907
|
if (typeof target.id === "string") {
|
|
2887
2908
|
if (seenTargetIds.has(target.id)) {
|
|
2888
2909
|
issues.push({
|
|
@@ -2928,7 +2949,7 @@ function validateAllowedFields(value, location, allowedFields, issues) {
|
|
|
2928
2949
|
});
|
|
2929
2950
|
}
|
|
2930
2951
|
}
|
|
2931
|
-
function validateEndpoint(value, field, issues) {
|
|
2952
|
+
function validateEndpoint(value, field, issues, technologyCatalog) {
|
|
2932
2953
|
if (value === void 0) return;
|
|
2933
2954
|
if (!isRecord(value)) {
|
|
2934
2955
|
issues.push({
|
|
@@ -2938,6 +2959,12 @@ function validateEndpoint(value, field, issues) {
|
|
|
2938
2959
|
});
|
|
2939
2960
|
return;
|
|
2940
2961
|
}
|
|
2962
|
+
validateAllowedFields(
|
|
2963
|
+
value,
|
|
2964
|
+
field,
|
|
2965
|
+
field === "targets" ? ["id", "path", "profile", "assetBundles", "projectType", "capabilities", "boundary", "technologyExceptions"] : ["id", "path", "projectType", "capabilities", "boundary", "technologyExceptions"],
|
|
2966
|
+
issues
|
|
2967
|
+
);
|
|
2941
2968
|
if (typeof value.id !== "string" || value.id.length === 0) {
|
|
2942
2969
|
issues.push({
|
|
2943
2970
|
type: "invalid-field",
|
|
@@ -2962,6 +2989,7 @@ function validateEndpoint(value, field, issues) {
|
|
|
2962
2989
|
message: `Portfolio ${field} path does not exist: ${value.path}`
|
|
2963
2990
|
});
|
|
2964
2991
|
}
|
|
2992
|
+
validateRepositoryGovernance(value, field, technologyCatalog, issues);
|
|
2965
2993
|
}
|
|
2966
2994
|
function validateOptionalStringArray(value, id, field, issues) {
|
|
2967
2995
|
if (value === void 0) return;
|
|
@@ -2974,6 +3002,118 @@ function validateOptionalStringArray(value, id, field, issues) {
|
|
|
2974
3002
|
});
|
|
2975
3003
|
}
|
|
2976
3004
|
}
|
|
3005
|
+
function validateTechnologyGovernance(value, issues) {
|
|
3006
|
+
const technologies = /* @__PURE__ */ new Set();
|
|
3007
|
+
const projectTypes = /* @__PURE__ */ new Map();
|
|
3008
|
+
if (value === void 0) return { technologies, projectTypes };
|
|
3009
|
+
if (!isRecord(value)) {
|
|
3010
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance", message: "Portfolio technologyGovernance must be an object." });
|
|
3011
|
+
return { technologies, projectTypes };
|
|
3012
|
+
}
|
|
3013
|
+
validateAllowedFields(value, "technologyGovernance", ["strategySource", "technologies", "projectTypes"], issues);
|
|
3014
|
+
if (value.strategySource !== void 0 && (typeof value.strategySource !== "string" || value.strategySource.length === 0)) {
|
|
3015
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.strategySource", message: "Technology strategySource must be a non-empty string." });
|
|
3016
|
+
}
|
|
3017
|
+
if (!Array.isArray(value.technologies)) {
|
|
3018
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.technologies", message: "Technology definitions must be an array." });
|
|
3019
|
+
} else {
|
|
3020
|
+
for (const technology of value.technologies) {
|
|
3021
|
+
if (!isRecord(technology)) {
|
|
3022
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.technologies", message: "Technology definition must be an object." });
|
|
3023
|
+
continue;
|
|
3024
|
+
}
|
|
3025
|
+
validateAllowedFields(technology, "technology", ["id", "label", "packages", "files"], issues);
|
|
3026
|
+
if (typeof technology.id !== "string" || technology.id.length === 0 || technologies.has(technology.id)) {
|
|
3027
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.technologies.id", message: `Technology id must be non-empty and unique: ${String(technology.id)}` });
|
|
3028
|
+
} else technologies.add(technology.id);
|
|
3029
|
+
if (typeof technology.label !== "string" || technology.label.length === 0) {
|
|
3030
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.technologies.label", message: "Technology label must be a non-empty string." });
|
|
3031
|
+
}
|
|
3032
|
+
validateOptionalStringArray(technology.packages, technology.id, "technology.packages", issues);
|
|
3033
|
+
validateOptionalStringArray(technology.files, technology.id, "technology.files", issues);
|
|
3034
|
+
if (Array.isArray(technology.files)) {
|
|
3035
|
+
for (const file of technology.files) {
|
|
3036
|
+
if (typeof file === "string" && !isRepositoryRelativePath(file)) {
|
|
3037
|
+
issues.push({
|
|
3038
|
+
type: "invalid-field",
|
|
3039
|
+
id: typeof technology.id === "string" ? technology.id : void 0,
|
|
3040
|
+
field: "technology.files",
|
|
3041
|
+
message: `Technology file signal must stay inside the repository: ${file}`
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (!Array.isArray(value.projectTypes)) {
|
|
3049
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes", message: "Project type definitions must be an array." });
|
|
3050
|
+
} else {
|
|
3051
|
+
for (const projectType of value.projectTypes) {
|
|
3052
|
+
if (!isRecord(projectType)) {
|
|
3053
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes", message: "Project type definition must be an object." });
|
|
3054
|
+
continue;
|
|
3055
|
+
}
|
|
3056
|
+
validateAllowedFields(projectType, "projectType", ["id", "title", "summary", "baseline", "optionalCapabilities"], issues);
|
|
3057
|
+
const id = typeof projectType.id === "string" ? projectType.id : "";
|
|
3058
|
+
if (!id || projectTypes.has(id)) {
|
|
3059
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes.id", message: `Project type id must be non-empty and unique: ${String(projectType.id)}` });
|
|
3060
|
+
}
|
|
3061
|
+
if (typeof projectType.title !== "string" || typeof projectType.summary !== "string") {
|
|
3062
|
+
issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes", message: `Project type ${id || "(unknown)"} requires title and summary.` });
|
|
3063
|
+
}
|
|
3064
|
+
validateOptionalStringArray(projectType.baseline, id, "projectType.baseline", issues);
|
|
3065
|
+
validateOptionalStringArray(projectType.optionalCapabilities, id, "projectType.optionalCapabilities", issues);
|
|
3066
|
+
if (id && typeof projectType.title === "string" && typeof projectType.summary === "string" && Array.isArray(projectType.baseline) && Array.isArray(projectType.optionalCapabilities)) {
|
|
3067
|
+
projectTypes.set(id, projectType);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
for (const projectType of projectTypes.values()) {
|
|
3072
|
+
for (const technology of [...projectType.baseline, ...projectType.optionalCapabilities]) {
|
|
3073
|
+
if (!technologies.has(technology)) issues.push({ type: "invalid-field", field: "technologyGovernance.projectTypes", message: `Project type ${projectType.id} references unknown technology: ${technology}` });
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return { technologies, projectTypes };
|
|
3077
|
+
}
|
|
3078
|
+
function isRepositoryRelativePath(value) {
|
|
3079
|
+
if (value.length === 0 || isAbsolute3(value)) return false;
|
|
3080
|
+
const segments = value.replaceAll("\\", "/").split("/");
|
|
3081
|
+
return !segments.includes("..");
|
|
3082
|
+
}
|
|
3083
|
+
function validateRepositoryGovernance(value, field, catalog, issues) {
|
|
3084
|
+
const id = typeof value.id === "string" ? value.id : void 0;
|
|
3085
|
+
if (value.projectType !== void 0 && (typeof value.projectType !== "string" || !catalog.projectTypes.has(value.projectType))) {
|
|
3086
|
+
issues.push({ type: "invalid-field", id, field: `${field}.projectType`, message: `Repository projectType is not defined: ${String(value.projectType)}` });
|
|
3087
|
+
}
|
|
3088
|
+
validateOptionalStringArray(value.capabilities, id, "capabilities", issues);
|
|
3089
|
+
if (Array.isArray(value.capabilities) && typeof value.projectType === "string") {
|
|
3090
|
+
const allowed = new Set(catalog.projectTypes.get(value.projectType)?.optionalCapabilities ?? []);
|
|
3091
|
+
for (const capability of value.capabilities) {
|
|
3092
|
+
if (typeof capability === "string" && !allowed.has(capability)) issues.push({ type: "invalid-field", id, field: `${field}.capabilities`, message: `Capability ${capability} is not optional for project type ${value.projectType}.` });
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
if (value.boundary !== void 0) {
|
|
3096
|
+
if (!isRecord(value.boundary)) issues.push({ type: "invalid-field", id, field: `${field}.boundary`, message: "Repository boundary must be an object." });
|
|
3097
|
+
else {
|
|
3098
|
+
validateAllowedFields(value.boundary, "boundary", ["owns", "doesNotOwn"], issues);
|
|
3099
|
+
validateOptionalStringArray(value.boundary.owns, id, "boundary.owns", issues);
|
|
3100
|
+
validateOptionalStringArray(value.boundary.doesNotOwn, id, "boundary.doesNotOwn", issues);
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
if (value.technologyExceptions !== void 0) {
|
|
3104
|
+
if (!Array.isArray(value.technologyExceptions)) issues.push({ type: "invalid-field", id, field: `${field}.technologyExceptions`, message: "Technology exceptions must be an array." });
|
|
3105
|
+
else for (const exception of value.technologyExceptions) {
|
|
3106
|
+
if (!isRecord(exception)) {
|
|
3107
|
+
issues.push({ type: "invalid-field", id, field: `${field}.technologyExceptions`, message: "Technology exception must be an object." });
|
|
3108
|
+
continue;
|
|
3109
|
+
}
|
|
3110
|
+
validateAllowedFields(exception, "technologyException", ["technology", "classification", "reason"], issues);
|
|
3111
|
+
if (typeof exception.technology !== "string" || !catalog.technologies.has(exception.technology)) issues.push({ type: "invalid-field", id, field: `${field}.technologyExceptions.technology`, message: `Technology exception references unknown technology: ${String(exception.technology)}` });
|
|
3112
|
+
if (!["acceptable-exception", "observe", "scheduled-migration", "urgent-drift", "bad-standard"].includes(String(exception.classification))) issues.push({ type: "invalid-field", id, field: `${field}.technologyExceptions.classification`, message: `Unsupported technology exception classification: ${String(exception.classification)}` });
|
|
3113
|
+
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
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
2977
3117
|
function validateHostTooling(value, issues) {
|
|
2978
3118
|
if (value === void 0) return;
|
|
2979
3119
|
if (!Array.isArray(value)) {
|
|
@@ -3390,7 +3530,7 @@ import {
|
|
|
3390
3530
|
statSync as statSync4,
|
|
3391
3531
|
writeFileSync as writeFileSync7
|
|
3392
3532
|
} from "node:fs";
|
|
3393
|
-
import { dirname as dirname12, join as join20, relative as relative8, resolve as resolve4 } from "node:path";
|
|
3533
|
+
import { dirname as dirname12, join as join20, relative as relative8, resolve as resolve4, sep } from "node:path";
|
|
3394
3534
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
3395
3535
|
function inspectPortfolioAiHealth(options) {
|
|
3396
3536
|
const endpoints = collectEndpoints(options.manifest);
|
|
@@ -3399,7 +3539,7 @@ function inspectPortfolioAiHealth(options) {
|
|
|
3399
3539
|
const executionEngineRoot = options.manifest.executionEngine?.path;
|
|
3400
3540
|
const skillRegistry = inspectSkillRegistry(executionEngineRoot);
|
|
3401
3541
|
const expectedPackageVersion = packageVersion(join20(executionEngineRoot ?? "", "packages/pro-gov/package.json"));
|
|
3402
|
-
const repositories = endpoints.map(({ endpoint, role }) => inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion));
|
|
3542
|
+
const repositories = endpoints.map(({ endpoint, role }) => inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion, options.manifest.technologyGovernance));
|
|
3403
3543
|
const summary = { healthy: 0, attention: 0, unhealthy: 0 };
|
|
3404
3544
|
for (const repository of repositories) summary[repository.status] += 1;
|
|
3405
3545
|
return {
|
|
@@ -3410,6 +3550,11 @@ function inspectPortfolioAiHealth(options) {
|
|
|
3410
3550
|
secretsRoot: inspectSecretsRoot(secretsRoot),
|
|
3411
3551
|
userMcp: inspectUserMcp(homeDir),
|
|
3412
3552
|
skillRegistry,
|
|
3553
|
+
technologyGovernance: {
|
|
3554
|
+
strategySource: options.manifest.technologyGovernance?.strategySource,
|
|
3555
|
+
projectTypes: options.manifest.technologyGovernance?.projectTypes.length ?? 0,
|
|
3556
|
+
technologies: options.manifest.technologyGovernance?.technologies.length ?? 0
|
|
3557
|
+
},
|
|
3413
3558
|
summary,
|
|
3414
3559
|
repositories
|
|
3415
3560
|
};
|
|
@@ -3443,19 +3588,20 @@ function collectEndpoints(manifest) {
|
|
|
3443
3588
|
return true;
|
|
3444
3589
|
});
|
|
3445
3590
|
}
|
|
3446
|
-
function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion) {
|
|
3591
|
+
function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion, technologyGovernance) {
|
|
3447
3592
|
const root = endpoint.path;
|
|
3448
3593
|
const git = inspectGit2(root);
|
|
3449
3594
|
const entries = inspectEntries(root);
|
|
3450
3595
|
const skills = inspectSkills(root);
|
|
3451
3596
|
const hooks = inspectHooks(root);
|
|
3452
|
-
const docs = inspectDocs(root, expectedPackageVersion);
|
|
3597
|
+
const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
|
|
3453
3598
|
const mcp = {
|
|
3454
3599
|
root: jsonObjectKeys(join20(root, ".mcp.json"), "mcpServers"),
|
|
3455
3600
|
claudeCode: jsonObjectKeys(join20(root, ".claude/settings.json"), "mcpServers"),
|
|
3456
3601
|
codex: tomlMcpNames(join20(root, ".codex/config.toml"))
|
|
3457
3602
|
};
|
|
3458
3603
|
const secrets = inspectRepositorySecrets(root, endpoint.id, secretsRoot, git.isRepository);
|
|
3604
|
+
const projectModel = inspectProjectModel(root, endpoint, technologyGovernance);
|
|
3459
3605
|
const recommendations = [];
|
|
3460
3606
|
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");
|
|
3461
3607
|
if (git.unmergedBranches.length > 0) recommendations.push(`\u6709 ${git.unmergedBranches.length} \u6761\u5206\u652F\u5C1A\u672A\u5408\u5165\u5F53\u524D HEAD\uFF1A${git.unmergedBranches.join(", ")}\u3002`);
|
|
@@ -3475,17 +3621,24 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion)
|
|
|
3475
3621
|
if (skills.claudeCompatibility === "duplicate-directory") recommendations.push("`.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");
|
|
3476
3622
|
if (skills.claudeCompatibility === "dangling-symlink") recommendations.push("`.claude/skills` \u662F\u65AD\u5F00\u7684\u94FE\u63A5\u3002");
|
|
3477
3623
|
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");
|
|
3478
|
-
const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template);
|
|
3624
|
+
const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
|
|
3479
3625
|
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");
|
|
3480
|
-
|
|
3626
|
+
if (liveEnv.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");
|
|
3627
|
+
else if (liveEnv.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
|
+
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");
|
|
3481
3629
|
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`);
|
|
3482
3630
|
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");
|
|
3631
|
+
if (technologyGovernance && !projectModel.projectType) 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");
|
|
3632
|
+
const missingBaseline = projectModel.baseline.filter((technology) => !technology.detected && !hasBaselineException(projectModel, technology.id));
|
|
3633
|
+
if (missingBaseline.length > 0) recommendations.push(`\u6280\u672F\u57FA\u7EBF\u7F3A\u5C11\u53EF\u9A8C\u8BC1\u4FE1\u53F7\uFF1A${missingBaseline.map((technology) => technology.label).join("\u3001")}\u3002`);
|
|
3634
|
+
const missingSelected = projectModel.optionalCapabilities.filter((technology) => technology.selected && !technology.detected);
|
|
3635
|
+
if (missingSelected.length > 0) recommendations.push(`\u5DF2\u9009\u80FD\u529B\u7F3A\u5C11\u53EF\u9A8C\u8BC1\u4FE1\u53F7\uFF1A${missingSelected.map((technology) => technology.label).join("\u3001")}\u3002`);
|
|
3483
3636
|
return {
|
|
3484
3637
|
id: endpoint.id,
|
|
3485
3638
|
role,
|
|
3486
3639
|
path: root,
|
|
3487
3640
|
profile: "profile" in endpoint ? endpoint.profile : void 0,
|
|
3488
|
-
status: deriveStatus(entries, git, hooks, skills, secrets),
|
|
3641
|
+
status: deriveStatus(entries, git, hooks, skills, secrets, projectModel, Boolean(technologyGovernance)),
|
|
3489
3642
|
recommendations,
|
|
3490
3643
|
git,
|
|
3491
3644
|
entries,
|
|
@@ -3493,14 +3646,62 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion)
|
|
|
3493
3646
|
mcp,
|
|
3494
3647
|
skills,
|
|
3495
3648
|
secrets,
|
|
3496
|
-
docs
|
|
3649
|
+
docs,
|
|
3650
|
+
projectModel
|
|
3497
3651
|
};
|
|
3498
3652
|
}
|
|
3499
|
-
function deriveStatus(entries, git, hooks, skills, secrets) {
|
|
3500
|
-
if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || skills.canonical.some((item) => item.kind === "dangling-symlink") || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template)) return "unhealthy";
|
|
3501
|
-
|
|
3653
|
+
function deriveStatus(entries, git, hooks, skills, secrets, projectModel, technologyGovernanceConfigured) {
|
|
3654
|
+
if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || skills.canonical.some((item) => item.kind === "dangling-symlink") || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template && !file.fixture) || hasUnsafeCentralSecretPermissions(secrets)) return "unhealthy";
|
|
3655
|
+
const missingBaseline = projectModel.baseline.some((technology) => !technology.detected && !hasBaselineException(projectModel, technology.id));
|
|
3656
|
+
const missingSelected = projectModel.optionalCapabilities.some((technology) => technology.selected && !technology.detected);
|
|
3657
|
+
const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
|
|
3658
|
+
const secretMaterializationNeedsReview = liveEnv.some((file) => !file.centralized);
|
|
3659
|
+
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";
|
|
3502
3660
|
return "healthy";
|
|
3503
3661
|
}
|
|
3662
|
+
function inspectProjectModel(root, endpoint, governance) {
|
|
3663
|
+
const projectType = governance?.projectTypes.find((candidate) => candidate.id === endpoint.projectType);
|
|
3664
|
+
const packages = collectPackageNames(root);
|
|
3665
|
+
const technologyById = new Map((governance?.technologies ?? []).map((technology) => [technology.id, technology]));
|
|
3666
|
+
const detection = (id) => {
|
|
3667
|
+
const technology = technologyById.get(id);
|
|
3668
|
+
const packageMatch = technology?.packages?.some((name) => packages.has(name)) ?? false;
|
|
3669
|
+
const fileMatch = technology?.files?.some((path) => existsSync21(join20(root, path))) ?? false;
|
|
3670
|
+
return { id, label: technology?.label ?? id, detected: packageMatch || fileMatch };
|
|
3671
|
+
};
|
|
3672
|
+
const selected = new Set(endpoint.capabilities ?? []);
|
|
3673
|
+
return {
|
|
3674
|
+
projectType: projectType ? { id: projectType.id, title: projectType.title, summary: projectType.summary } : void 0,
|
|
3675
|
+
boundary: endpoint.boundary,
|
|
3676
|
+
baseline: (projectType?.baseline ?? []).map(detection),
|
|
3677
|
+
optionalCapabilities: (projectType?.optionalCapabilities ?? []).map((id) => ({ ...detection(id), selected: selected.has(id) })),
|
|
3678
|
+
exceptions: endpoint.technologyExceptions ?? []
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
function hasBaselineException(projectModel, technologyId) {
|
|
3682
|
+
return projectModel.exceptions.some(
|
|
3683
|
+
(exception) => exception.technology === technologyId && exception.classification !== "urgent-drift"
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
function collectPackageNames(root) {
|
|
3687
|
+
const names = /* @__PURE__ */ new Set();
|
|
3688
|
+
let files = [];
|
|
3689
|
+
try {
|
|
3690
|
+
files = splitLines(execFileSync("git", ["ls-files", "*package.json"], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }));
|
|
3691
|
+
} catch {
|
|
3692
|
+
if (existsSync21(join20(root, "package.json"))) files = ["package.json"];
|
|
3693
|
+
}
|
|
3694
|
+
for (const file of files) {
|
|
3695
|
+
const packageJson = readJson3(join20(root, file));
|
|
3696
|
+
if (!isRecord3(packageJson)) continue;
|
|
3697
|
+
for (const section of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
3698
|
+
const dependencies = packageJson[section];
|
|
3699
|
+
if (!isRecord3(dependencies)) continue;
|
|
3700
|
+
for (const name of Object.keys(dependencies)) names.add(name);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
return names;
|
|
3704
|
+
}
|
|
3504
3705
|
function hasWorkflowReminderHooks(hooks) {
|
|
3505
3706
|
return hooks.some((hook) => hook.events.some((event) => event.name === "Stop" || event.name === "SubagentStop"));
|
|
3506
3707
|
}
|
|
@@ -3617,11 +3818,14 @@ function inspectClaudeSkillRoot(root) {
|
|
|
3617
3818
|
}
|
|
3618
3819
|
function inspectRepositorySecrets(root, id, secretsRoot, isRepository) {
|
|
3619
3820
|
const centralPath = join20(secretsRoot, id);
|
|
3821
|
+
const centralRealPath = safeRealpath(centralPath);
|
|
3620
3822
|
const envFiles = collectEnvironmentFiles(root).map((path) => ({
|
|
3621
3823
|
path,
|
|
3622
3824
|
tracked: isRepository ? gitTracks(root, path) : false,
|
|
3623
|
-
template:
|
|
3624
|
-
|
|
3825
|
+
template: isEnvironmentTemplate(path),
|
|
3826
|
+
fixture: isEnvironmentFixture(path),
|
|
3827
|
+
symlink: lstatSync6(join20(root, path)).isSymbolicLink(),
|
|
3828
|
+
centralized: pointsInside(join20(root, path), centralRealPath)
|
|
3625
3829
|
}));
|
|
3626
3830
|
return {
|
|
3627
3831
|
centralDirectory: existsSync21(centralPath) ? "present" : "absent",
|
|
@@ -3630,7 +3834,7 @@ function inspectRepositorySecrets(root, id, secretsRoot, isRepository) {
|
|
|
3630
3834
|
repositoryEnvFiles: envFiles
|
|
3631
3835
|
};
|
|
3632
3836
|
}
|
|
3633
|
-
var SKIP_ENV_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".next", ".nuxt", ".output", ".turbo", ".worktrees", "build", "coverage", "dist", "node_modules", "out", "target"]);
|
|
3837
|
+
var SKIP_ENV_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".next", ".nuxt", ".output", ".turbo", ".vercel", ".worktrees", "build", "coverage", "dist", "node_modules", "out", "target"]);
|
|
3634
3838
|
function collectEnvironmentFiles(root, current = root, depth = 0) {
|
|
3635
3839
|
if (depth > 5) return [];
|
|
3636
3840
|
const found = [];
|
|
@@ -3638,7 +3842,7 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
|
|
|
3638
3842
|
for (const entry of readdirSync8(current, { withFileTypes: true })) {
|
|
3639
3843
|
if (entry.isDirectory()) {
|
|
3640
3844
|
if (!SKIP_ENV_DIRECTORIES.has(entry.name)) found.push(...collectEnvironmentFiles(root, join20(current, entry.name), depth + 1));
|
|
3641
|
-
} else if (entry.name
|
|
3845
|
+
} else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
|
|
3642
3846
|
found.push(relative8(root, join20(current, entry.name)));
|
|
3643
3847
|
}
|
|
3644
3848
|
}
|
|
@@ -3665,6 +3869,35 @@ function basenameOnly(path) {
|
|
|
3665
3869
|
const parts = path.split(/[\\/]/);
|
|
3666
3870
|
return parts[parts.length - 1] ?? path;
|
|
3667
3871
|
}
|
|
3872
|
+
function isEnvironmentFilename(name) {
|
|
3873
|
+
return name === ".env" || name.startsWith(".env.") || name.endsWith(".env");
|
|
3874
|
+
}
|
|
3875
|
+
function isProviderGeneratedEnvironmentFile(name) {
|
|
3876
|
+
return name.endsWith(".pulled");
|
|
3877
|
+
}
|
|
3878
|
+
function isEnvironmentTemplate(path) {
|
|
3879
|
+
return /(^|[._-])(example|sample|template|defaults?)([._-]|$)/i.test(basenameOnly(path));
|
|
3880
|
+
}
|
|
3881
|
+
function isEnvironmentFixture(path) {
|
|
3882
|
+
return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
|
|
3883
|
+
}
|
|
3884
|
+
function safeRealpath(path) {
|
|
3885
|
+
try {
|
|
3886
|
+
return realpathSync2(path);
|
|
3887
|
+
} catch {
|
|
3888
|
+
return void 0;
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
function pointsInside(path, expectedRoot) {
|
|
3892
|
+
if (!expectedRoot || !lstatSync6(path).isSymbolicLink()) return false;
|
|
3893
|
+
const target = safeRealpath(path);
|
|
3894
|
+
if (!target) return false;
|
|
3895
|
+
const fromRoot = relative8(expectedRoot, target);
|
|
3896
|
+
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
|
|
3897
|
+
}
|
|
3898
|
+
function hasUnsafeCentralSecretPermissions(secrets) {
|
|
3899
|
+
return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
|
|
3900
|
+
}
|
|
3668
3901
|
function inspectSecretsRoot(path) {
|
|
3669
3902
|
return existsSync21(path) ? { path, exists: true, mode: modeString(statSync4(path).mode) } : { path, exists: false };
|
|
3670
3903
|
}
|
|
@@ -4293,7 +4526,7 @@ function normalizeMarkdownTableCell(cell) {
|
|
|
4293
4526
|
}
|
|
4294
4527
|
function inferInstalledProfile(root) {
|
|
4295
4528
|
const installed = ["engineering-runtime", "doc-only"].filter(
|
|
4296
|
-
(profile) => existsSync23(join22(root, `docs/governance/agents-routing/${profile}-
|
|
4529
|
+
(profile) => existsSync23(join22(root, `docs/governance/agents-routing/${profile}-v1.0.md`))
|
|
4297
4530
|
);
|
|
4298
4531
|
return installed.length === 1 ? installed[0] : void 0;
|
|
4299
4532
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pieai/pro-gov",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
38
|
+
"@pieai/doc-gov": "^0.5.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@pieai/swimmer-ui-kit": "1.0.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|