@pieai/pro-gov 0.5.2 → 0.5.4

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
@@ -3585,25 +3585,35 @@ import {
3585
3585
  statSync as statSync4,
3586
3586
  writeFileSync as writeFileSync7
3587
3587
  } from "node:fs";
3588
+ import { homedir } from "node:os";
3588
3589
  import { dirname as dirname12, join as join20, relative as relative8, resolve as resolve4, sep } from "node:path";
3589
3590
  import { fileURLToPath as fileURLToPath4 } from "node:url";
3590
3591
  function inspectPortfolioAiHealth(options) {
3591
3592
  const endpoints = collectEndpoints(options.manifest);
3592
3593
  const secretsRoot = options.secretsRoot ?? join20(dirname12(options.manifest.controlPlane?.path ?? endpoints[0]?.endpoint.path ?? process.cwd()), ".secrets");
3593
- const homeDir = options.homeDir ?? process.env.HOME ?? "";
3594
+ const homeDir = options.homeDir ?? process.env.HOME ?? homedir();
3595
+ const grokVersion = commandVersion("grok");
3594
3596
  const executionEngineRoot = options.manifest.executionEngine?.path;
3595
3597
  const skillRegistry = inspectSkillRegistry(executionEngineRoot);
3596
3598
  const expectedPackageVersion = packageVersion(join20(executionEngineRoot ?? "", "packages/pro-gov/package.json"));
3597
- const repositories = endpoints.map(({ endpoint, role }) => inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion, options.manifest.technologyGovernance));
3599
+ const repositories = endpoints.map(({ endpoint, role }) => inspectRepository(
3600
+ endpoint,
3601
+ role,
3602
+ secretsRoot,
3603
+ homeDir,
3604
+ expectedPackageVersion,
3605
+ options.manifest.technologyGovernance,
3606
+ grokVersion
3607
+ ));
3598
3608
  const summary = { healthy: 0, attention: 0, unhealthy: 0 };
3599
3609
  for (const repository of repositories) summary[repository.status] += 1;
3600
3610
  return {
3601
- schemaVersion: 1,
3611
+ schemaVersion: 2,
3602
3612
  portfolioId: options.manifest.portfolioId,
3603
3613
  generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3604
- privacy: "Names, paths, counts, and configuration structure only. Secret values, environment values, MCP commands, arguments, and MCP environment maps are never collected.",
3614
+ privacy: "Names, paths, counts, scopes, and configuration origins only. Secret values, environment values, MCP commands, arguments, URLs, headers, and MCP environment maps are never retained or rendered.",
3605
3615
  secretsRoot: inspectSecretsRoot(secretsRoot),
3606
- userMcp: inspectUserMcp(homeDir),
3616
+ hostEnvironment: inspectHostEnvironment(homeDir, grokVersion),
3607
3617
  skillRegistry,
3608
3618
  technologyGovernance: {
3609
3619
  strategySource: options.manifest.technologyGovernance?.strategySource,
@@ -3643,17 +3653,21 @@ function collectEndpoints(manifest) {
3643
3653
  return true;
3644
3654
  });
3645
3655
  }
3646
- function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion, technologyGovernance) {
3656
+ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackageVersion, technologyGovernance, grokVersion) {
3647
3657
  const root = endpoint.path;
3648
3658
  const git = inspectGit2(root);
3649
3659
  const entries = inspectEntries(root);
3650
- const skills = inspectSkills(root);
3660
+ const grokInspection = inspectGrokProject(root, homeDir, grokVersion);
3661
+ const skills = inspectSkills(root, grokInspection);
3651
3662
  const hooks = inspectHooks(root);
3652
3663
  const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
3653
3664
  const mcp = {
3654
- root: jsonObjectKeys(join20(root, ".mcp.json"), "mcpServers"),
3655
- claudeCode: jsonObjectKeys(join20(root, ".claude/settings.json"), "mcpServers"),
3656
- codex: tomlMcpNames(join20(root, ".codex/config.toml"))
3665
+ codexProject: tomlMcpNames(join20(root, ".codex/config.toml")),
3666
+ claudeCodeProjectShared: jsonObjectKeys(join20(root, ".mcp.json"), "mcpServers"),
3667
+ claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
3668
+ grokProject: tomlMcpNames(join20(root, ".grok/config.toml")),
3669
+ grokEffective: grokInspection.effectiveMcp,
3670
+ grokInspection: grokInspection.inspection
3657
3671
  };
3658
3672
  const secrets = inspectRepositorySecrets(root, endpoint.id, secretsRoot, git.isRepository, endpoint.environmentPolicy);
3659
3673
  const projectModel = inspectProjectModel(root, endpoint, technologyGovernance);
@@ -3694,7 +3708,7 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
3694
3708
  role,
3695
3709
  path: root,
3696
3710
  profile: "profile" in endpoint ? endpoint.profile : void 0,
3697
- status: deriveStatus(entries, git, hooks, skills, secrets, projectModel, Boolean(technologyGovernance)),
3711
+ status: deriveStatus(role, entries, git, hooks, skills, secrets, docs, projectModel, Boolean(technologyGovernance)),
3698
3712
  recommendations,
3699
3713
  git,
3700
3714
  entries,
@@ -3706,13 +3720,15 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
3706
3720
  projectModel
3707
3721
  };
3708
3722
  }
3709
- function deriveStatus(entries, git, hooks, skills, secrets, projectModel, technologyGovernanceConfigured) {
3723
+ function deriveStatus(role, entries, git, hooks, skills, secrets, docs, projectModel, technologyGovernanceConfigured) {
3710
3724
  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";
3711
3725
  const missingBaseline = projectModel.baseline.some((technology) => !technology.detected && !hasBaselineException(projectModel, technology.id));
3712
3726
  const missingSelected = projectModel.optionalCapabilities.some((technology) => technology.selected && !technology.detected);
3713
3727
  const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
3714
3728
  const secretMaterializationNeedsReview = liveEnv.some((file) => !file.centralized && !file.localOnly);
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";
3729
+ const packageVersionNeedsReview = docs.packages.expected !== void 0 && !docs.packages.aligned;
3730
+ const targetManifestMissing = role === "target" && !docs.manifest;
3731
+ 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 || packageVersionNeedsReview || targetManifestMissing) return "attention";
3716
3732
  return "healthy";
3717
3733
  }
3718
3734
  function inspectProjectModel(root, endpoint, governance) {
@@ -3819,7 +3835,7 @@ function inspectOptionalEntry(root, filename, agentsPath) {
3819
3835
  const content = safeRead(path);
3820
3836
  return /AGENTS\.md/.test(content) && content.length < 2e3 ? "thin-adapter" : "custom";
3821
3837
  }
3822
- function inspectSkills(root) {
3838
+ function inspectSkills(root, grokInspection) {
3823
3839
  const lock = readJson3(join20(root, ".pro-gov/assets.lock.json"));
3824
3840
  const managed = /* @__PURE__ */ new Set();
3825
3841
  const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
@@ -3855,6 +3871,12 @@ function inspectSkills(root) {
3855
3871
  installed: canonical.filter((item) => item.managed && item.kind !== "dangling-symlink").length,
3856
3872
  discoverable: canonical.filter((item) => item.kind !== "dangling-symlink").length,
3857
3873
  runtime: "unobservable"
3874
+ },
3875
+ hosts: {
3876
+ codexProject: canonical.filter((item) => item.kind !== "dangling-symlink").length,
3877
+ claudeCodeProject: inspectSkillRoot(join20(root, ".claude/skills")).names.length,
3878
+ grokNativeProject: inspectSkillRoot(join20(root, ".grok/skills")).names.length,
3879
+ grokEffective: grokInspection.skills
3858
3880
  }
3859
3881
  };
3860
3882
  }
@@ -3965,13 +3987,147 @@ function hasUnsafeCentralSecretPermissions(secrets) {
3965
3987
  function inspectSecretsRoot(path) {
3966
3988
  return existsSync21(path) ? { path, exists: true, mode: modeString(statSync4(path).mode) } : { path, exists: false };
3967
3989
  }
3968
- function inspectUserMcp(homeDir) {
3969
- if (!homeDir) return { codex: [], claudeCode: [] };
3990
+ function inspectHostEnvironment(homeDir, grokVersion) {
3991
+ const codexConfig = join20(homeDir, ".codex/config.toml");
3992
+ const claudeConfig = join20(homeDir, ".claude.json");
3993
+ const grokConfig = join20(homeDir, ".grok/config.toml");
3970
3994
  return {
3971
- codex: tomlMcpNames(join20(homeDir, ".codex/config.toml")),
3972
- claudeCode: jsonObjectKeys(join20(homeDir, ".claude/settings.json"), "mcpServers")
3995
+ mcp: {
3996
+ codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
3997
+ claudeCodeUser: { path: claudeConfig, names: jsonObjectKeys(claudeConfig, "mcpServers") },
3998
+ grokUser: { path: grokConfig, names: tomlMcpNames(grokConfig) }
3999
+ },
4000
+ skills: {
4001
+ codexUser: inspectSkillRoot(join20(homeDir, ".agents/skills")),
4002
+ claudeCodeUser: inspectSkillRoot(join20(homeDir, ".claude/skills")),
4003
+ grokUser: inspectSkillRoot(join20(homeDir, ".grok/skills")),
4004
+ grokAgentsCompatibility: inspectSkillRoot(join20(homeDir, ".agents/skills")),
4005
+ grokClaudeCompatibility: inspectSkillRoot(join20(homeDir, ".claude/skills"))
4006
+ },
4007
+ grok: {
4008
+ available: grokVersion !== void 0,
4009
+ ...grokVersion ? { version: grokVersion } : {},
4010
+ inspectionCommand: "grok inspect --json"
4011
+ }
3973
4012
  };
3974
4013
  }
4014
+ function inspectSkillRoot(path) {
4015
+ const exists = pathLexists(path) && safeIsDirectory(path);
4016
+ const names = exists ? safeReadDir(path).filter((name) => !name.startsWith(".") && existsSync21(join20(path, name, "SKILL.md"))) : [];
4017
+ return { path, exists, names };
4018
+ }
4019
+ function claudeProjectLocalMcpNames(homeDir, root) {
4020
+ if (!homeDir) return [];
4021
+ const value = readJson3(join20(homeDir, ".claude.json"));
4022
+ if (!isRecord3(value) || !isRecord3(value.projects)) return [];
4023
+ const candidates = new Set([resolve4(root), safeRealpath(root)].filter((path) => Boolean(path)));
4024
+ const names = /* @__PURE__ */ new Set();
4025
+ for (const [path, project] of Object.entries(value.projects)) {
4026
+ const projectPaths = [resolve4(path), safeRealpath(path)].filter((candidate) => Boolean(candidate));
4027
+ if (!projectPaths.some((candidate) => candidates.has(candidate)) || !isRecord3(project) || !isRecord3(project.mcpServers)) continue;
4028
+ for (const name of Object.keys(project.mcpServers)) names.add(name);
4029
+ }
4030
+ return [...names].sort();
4031
+ }
4032
+ function commandVersion(command2) {
4033
+ try {
4034
+ const output = execFileSync(command2, ["version"], {
4035
+ encoding: "utf8",
4036
+ stdio: ["ignore", "pipe", "ignore"],
4037
+ timeout: 3e3
4038
+ });
4039
+ return output.match(/\b\d+\.\d+\.\d+\b/)?.[0];
4040
+ } catch {
4041
+ return void 0;
4042
+ }
4043
+ }
4044
+ function inspectGrokProject(root, homeDir, grokVersion) {
4045
+ const empty = (inspection) => ({
4046
+ inspection,
4047
+ effectiveMcp: [],
4048
+ skills: { inspection, total: 0, project: 0, userOrCompatible: 0, plugin: 0, bundled: 0 },
4049
+ claudeCompatibility: "unknown",
4050
+ cursorCompatibility: "unknown"
4051
+ });
4052
+ if (!grokVersion) return empty("unavailable");
4053
+ try {
4054
+ const value = JSON.parse(execFileSync("grok", ["inspect", "--json"], {
4055
+ cwd: root,
4056
+ encoding: "utf8",
4057
+ env: { ...process.env, HOME: homeDir, GROK_HOME: join20(homeDir, ".grok") },
4058
+ maxBuffer: 10 * 1024 * 1024,
4059
+ stdio: ["ignore", "pipe", "ignore"],
4060
+ timeout: 8e3
4061
+ }));
4062
+ if (!isRecord3(value)) return empty("failed");
4063
+ const userClaudeNames = new Set(jsonObjectKeys(join20(homeDir, ".claude.json"), "mcpServers"));
4064
+ const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
4065
+ const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
4066
+ if (!isRecord3(item) || typeof item.name !== "string") return [];
4067
+ const source = isRecord3(item.source) ? item.source : {};
4068
+ const sourceType = typeof source.type === "string" ? source.type : "unknown";
4069
+ const sourcePath = typeof source.path === "string" ? source.path : void 0;
4070
+ const vendor = typeof item.vendor === "string" ? item.vendor : sourceType;
4071
+ return [{
4072
+ name: item.name,
4073
+ vendor,
4074
+ scope: inferGrokMcpScope(item.name, sourceType, sourcePath, root, homeDir, userClaudeNames, localClaudeNames),
4075
+ sourceType,
4076
+ ...sourcePath ? { sourcePath } : {}
4077
+ }];
4078
+ }) : [];
4079
+ const skillCounts = { project: 0, userOrCompatible: 0, plugin: 0, bundled: 0 };
4080
+ if (Array.isArray(value.skills)) {
4081
+ for (const item of value.skills) {
4082
+ if (!isRecord3(item) || !isRecord3(item.source)) continue;
4083
+ const type = item.source.type;
4084
+ const path = item.source.path;
4085
+ if (typeof path === "string" && path.includes(sep + ".grok" + sep + "bundled" + sep)) skillCounts.bundled += 1;
4086
+ else if (type === "project") skillCounts.project += 1;
4087
+ else if (type === "plugin") skillCounts.plugin += 1;
4088
+ else skillCounts.userOrCompatible += 1;
4089
+ }
4090
+ }
4091
+ const cells = isRecord3(value.externalCompat) && Array.isArray(value.externalCompat.cells) ? value.externalCompat.cells : [];
4092
+ return {
4093
+ inspection: "ok",
4094
+ effectiveMcp,
4095
+ skills: {
4096
+ inspection: "ok",
4097
+ total: skillCounts.project + skillCounts.userOrCompatible + skillCounts.plugin + skillCounts.bundled,
4098
+ ...skillCounts
4099
+ },
4100
+ claudeCompatibility: compatibilityEnabled(cells, "claude"),
4101
+ cursorCompatibility: compatibilityEnabled(cells, "cursor")
4102
+ };
4103
+ } catch {
4104
+ return empty("failed");
4105
+ }
4106
+ }
4107
+ function compatibilityEnabled(cells, vendor) {
4108
+ const matching = cells.filter((cell) => isRecord3(cell) && cell.vendor === vendor);
4109
+ if (matching.length === 0) return "unknown";
4110
+ return matching.some((cell) => isRecord3(cell) && cell.enabled === true);
4111
+ }
4112
+ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClaudeNames, localClaudeNames) {
4113
+ if (!sourcePath) {
4114
+ if (sourceType === "project") return "project";
4115
+ if (localClaudeNames.has(name)) return "project-local";
4116
+ if (userClaudeNames.has(name)) return "user";
4117
+ return "unknown";
4118
+ }
4119
+ const resolvedSource = safeRealpath(sourcePath) ?? resolve4(sourcePath);
4120
+ const resolvedRoot = safeRealpath(root) ?? resolve4(root);
4121
+ if (resolvedSource === join20(resolvedRoot, ".mcp.json")) return "project-shared";
4122
+ if (resolvedSource.startsWith(resolvedRoot + sep)) return "project";
4123
+ if (homeDir && resolvedSource === join20(resolve4(homeDir), ".claude.json")) {
4124
+ if (localClaudeNames.has(name)) return "project-local";
4125
+ if (userClaudeNames.has(name)) return "user";
4126
+ }
4127
+ if (homeDir && resolvedSource.startsWith(resolve4(homeDir) + sep)) return "user";
4128
+ if (sourceType === "project") return "project";
4129
+ return "unknown";
4130
+ }
3975
4131
  var HOOK_EVENT_NAMES = /* @__PURE__ */ new Set([
3976
4132
  "PreToolUse",
3977
4133
  "PostToolUse",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pieai/pro-gov",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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.2"
38
+ "@pieai/doc-gov": "^0.5.4"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@pieai/swimmer-ui-kit": "1.0.1",