@pieai/pro-gov 0.3.8 → 0.3.9

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 CHANGED
@@ -139,6 +139,19 @@ engineering work. The default PGS pattern is a Compound Gate: run
139
139
  skipped. Full CE workflows such as `ce-plan`, `ce-work`, and `lfg` remain
140
140
  explicit user choices.
141
141
 
142
+ Engineering-runtime starters include cross-host Stop hooks for Codex,
143
+ Claude Code, and Antigravity. Those hooks call `pro-gov host-hook` and require
144
+ the final report to include either:
145
+
146
+ ```text
147
+ Compound Gate: ran ce-compound -> <path>
148
+ Compound Gate: skipped -> <reason>
149
+ ```
150
+
151
+ Use `pro-gov doctor --strict-hooks` after syncing an engineering project to
152
+ verify that the host configs are wired. Open a fresh AI session after installing
153
+ or changing hooks; old sessions may not reload host configuration.
154
+
142
155
  Ponytail can be installed as an optional complexity adviser. Keep its global mode
143
156
  `off`; test `lite` in one isolated task before considering a stronger mode.
144
157
  Ponytail must not remove requested scope, tests, safety, accessibility, or proof.
@@ -39,3 +39,9 @@ project shape. `pro-gov init --apply` can install it into a fresh target, but
39
39
  refuses all writes when any destination already exists. `sync --check` compares
40
40
  shared core files while leaving project-local router, policy, and current-work
41
41
  content under the target project's ownership.
42
+
43
+ Engineering-runtime starters also include Codex, Claude Code, and Antigravity
44
+ Stop hooks that call `pro-gov host-hook`. Use `pro-gov doctor --strict-hooks`
45
+ after sync to confirm the exit gate is wired. The hook enforces the Compound
46
+ Gate final-report marker; it does not replace verification, tests, or
47
+ `ce-compound` itself.
@@ -0,0 +1,26 @@
1
+ {
2
+ "pgs-compound-gate": {
3
+ "Stop": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host antigravity --event Stop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host antigravity --event Stop; else npx --no-install @pieai/pro-gov host-hook --host antigravity --event Stop; fi",
9
+ "timeout": 30
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "SubagentStop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host antigravity --event SubagentStop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host antigravity --event SubagentStop; else npx --no-install @pieai/pro-gov host-hook --host antigravity --event SubagentStop; fi",
20
+ "timeout": 30
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "hooks": {
3
+ "Stop": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "cd \"${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel)}\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host claude-code --event Stop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host claude-code --event Stop; else npx --no-install @pieai/pro-gov host-hook --host claude-code --event Stop; fi",
9
+ "timeout": 30
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "SubagentStop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "cd \"${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel)}\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host claude-code --event SubagentStop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host claude-code --event SubagentStop; else npx --no-install @pieai/pro-gov host-hook --host claude-code --event SubagentStop; fi",
20
+ "timeout": 30
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "hooks": {
3
+ "Stop": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host codex --event Stop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host codex --event Stop; else npx --no-install @pieai/pro-gov host-hook --host codex --event Stop; fi",
9
+ "timeout": 30,
10
+ "statusMessage": "Checking PGS Compound Gate"
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "SubagentStop": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then ./node_modules/.bin/pro-gov host-hook --host codex --event SubagentStop; elif [ -f packages/pro-gov/src/cli.ts ] && command -v pnpm >/dev/null 2>&1; then pnpm --silent --filter @pieai/pro-gov dev host-hook --host codex --event SubagentStop; else npx --no-install @pieai/pro-gov host-hook --host codex --event SubagentStop; fi",
21
+ "timeout": 30,
22
+ "statusMessage": "Checking PGS subagent Compound Gate"
23
+ }
24
+ ]
25
+ }
26
+ ]
27
+ }
28
+ }
@@ -39,6 +39,7 @@
39
39
  - Point to the chosen agents-routing file from `docs/governance/agents-routing/`.
40
40
  - Engineering projects may use Superpowers, Directed Development, GStack, or other external workflows only inside the selected lane; do not copy upstream integration guides into the target project by default.
41
41
  - For engineering projects, Superpowers is the default engineering workflow. Compound Engineering is used by default only as the post-work Compound Gate through `ce-compound`; full CE workflows require an explicit user request.
42
+ - Engineering projects should have PGS Stop hooks wired for Codex, Claude Code, and Antigravity. Before final reporting after completed engineering work, report `Compound Gate: ran ce-compound -> <path>` or `Compound Gate: skipped -> <reason>`.
42
43
  - Doc-only projects should say that Superpowers TDD and Directed Development are not enabled by default.
43
44
  - External workflow systems such as Superpowers or GStack run inside the lane selected by this router. They must not replace this project router.
44
45
 
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ var assetRoots = [
14
14
  "docs/reference/adoption"
15
15
  ];
16
16
  function listAssets() {
17
- const root = existsSync(packagedAssetsRoot) ? packagedAssetsRoot : sourceRoot;
17
+ const root = existsSync(join(sourceRoot, "starter")) ? sourceRoot : packagedAssetsRoot;
18
18
  return assetRoots.flatMap((assetRoot) => {
19
19
  const absoluteRoot = join(root, assetRoot);
20
20
  if (!existsSync(absoluteRoot)) return [];
@@ -1542,7 +1542,7 @@ function printNpxUsage() {
1542
1542
 
1543
1543
  // src/commands/doctor.ts
1544
1544
  import { spawnSync as spawnSync2 } from "node:child_process";
1545
- import { existsSync as existsSync12 } from "node:fs";
1545
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
1546
1546
  import { createRequire } from "node:module";
1547
1547
  import { dirname as dirname6, join as join12 } from "node:path";
1548
1548
  var REQUIRED_ASSETS = [
@@ -1554,9 +1554,11 @@ var REQUIRED_ASSETS = [
1554
1554
  "profiles/doc-only/profile.md"
1555
1555
  ];
1556
1556
  function runDoctor(_args) {
1557
+ const strictHooks = _args.includes("--strict-hooks");
1557
1558
  const assets = listAssets();
1558
1559
  const assetPaths = new Set(assets.map((asset) => asset.path));
1559
1560
  const missing = REQUIRED_ASSETS.filter((assetPath) => !assetPaths.has(assetPath));
1561
+ const hookIssues = strictHooks ? checkStrictHostHooks(process.cwd()) : [];
1560
1562
  console.log("pro-gov doctor");
1561
1563
  console.log(`assets: ${assets.length}`);
1562
1564
  if (missing.length > 0) {
@@ -1567,7 +1569,13 @@ function runDoctor(_args) {
1567
1569
  console.log("assets: required project-governance assets found");
1568
1570
  }
1569
1571
  console.log(checkDocGov());
1570
- return missing.length > 0 ? 1 : 0;
1572
+ for (const issue of hookIssues) {
1573
+ console.error(issue);
1574
+ }
1575
+ if (strictHooks && hookIssues.length === 0) {
1576
+ console.log("host-hooks: PGS Compound Gate hooks wired");
1577
+ }
1578
+ return missing.length > 0 || hookIssues.length > 0 ? 1 : 0;
1571
1579
  }
1572
1580
  function checkDocGov() {
1573
1581
  const fromPath = spawnSync2("doc-gov", ["--help"], {
@@ -1600,9 +1608,194 @@ function resolveDocGovDependencyCli() {
1600
1608
  return null;
1601
1609
  }
1602
1610
  }
1611
+ function checkStrictHostHooks(root) {
1612
+ if (!isEngineeringRuntimeProject(root)) {
1613
+ return [];
1614
+ }
1615
+ const expected = [
1616
+ { path: ".codex/hooks.json", host: "codex" },
1617
+ { path: ".claude/settings.json", host: "claude-code" },
1618
+ { path: ".agents/hooks.json", host: "antigravity" }
1619
+ ];
1620
+ const issues = [];
1621
+ for (const entry of expected) {
1622
+ const absolutePath = join12(root, entry.path);
1623
+ if (!existsSync12(absolutePath)) {
1624
+ issues.push(`host-hooks: missing ${entry.path}`);
1625
+ continue;
1626
+ }
1627
+ const content = readFileSync8(absolutePath, "utf8");
1628
+ if (!content.includes("pro-gov host-hook") || !content.includes(`--host ${entry.host}`)) {
1629
+ issues.push(`host-hooks: ${entry.path} does not call pro-gov host-hook for ${entry.host}`);
1630
+ }
1631
+ }
1632
+ return issues;
1633
+ }
1634
+ function isEngineeringRuntimeProject(root) {
1635
+ return existsSync12(join12(root, "docs/governance/agents-routing/engineering-runtime-v0.9.md"));
1636
+ }
1637
+
1638
+ // src/commands/host-hook.ts
1639
+ import { readFileSync as readFileSync9 } from "node:fs";
1640
+
1641
+ // src/host-hooks/host-hook-runner.ts
1642
+ var gateMarkerPattern = /Compound Gate:\s*(ran ce-compound|skipped)\s*->/i;
1643
+ var completionSignalPatterns = [
1644
+ /\b(done|completed|implemented|fixed|verified|validated|shipped|pushed|committed)\b/i,
1645
+ /\b(tests?|typecheck|build|lint|doctor|pack)\b.*\b(pass|passed|green|succeed|succeeded|ok)\b/i,
1646
+ /\b(changed|updated|modified|created|deleted|refactored)\b.*\b(files?|docs?|tests?|hooks?|configs?)\b/i,
1647
+ /完成|已完成|修好了|实现了|验证通过|测试通过|已经提交|已经推送|提交并推送/
1648
+ ];
1649
+ var compoundGateInstruction = [
1650
+ "Before final reporting, pass the PGS Compound Gate.",
1651
+ "If this completed work produced reusable learning, run compound-engineering:ce-compound and report:",
1652
+ "Compound Gate: ran ce-compound -> <path>",
1653
+ "If there is no reusable learning, report:",
1654
+ "Compound Gate: skipped -> <reason>"
1655
+ ].join("\n");
1656
+ function evaluateHostHook(request) {
1657
+ if (request.event !== "Stop" && request.event !== "SubagentStop") {
1658
+ return { action: "allow" };
1659
+ }
1660
+ const input = normalizeStopInput(request.input);
1661
+ if (input.stopHookActive) {
1662
+ return { action: "allow" };
1663
+ }
1664
+ if (!input.lastAssistantMessage) {
1665
+ return { action: "allow" };
1666
+ }
1667
+ if (gateMarkerPattern.test(input.lastAssistantMessage)) {
1668
+ return { action: "allow" };
1669
+ }
1670
+ if (!looksLikeCompletedEngineeringWork(input.lastAssistantMessage)) {
1671
+ return { action: "allow" };
1672
+ }
1673
+ return { action: "continue", reason: compoundGateInstruction };
1674
+ }
1675
+ function formatHostHookOutput(host, event, decision) {
1676
+ if (decision.action === "allow") {
1677
+ return {};
1678
+ }
1679
+ if (host === "codex") {
1680
+ if (event === "Stop" || event === "SubagentStop") {
1681
+ return { decision: "block", reason: decision.reason };
1682
+ }
1683
+ if (decision.action === "block") {
1684
+ return {
1685
+ hookSpecificOutput: {
1686
+ hookEventName: event,
1687
+ permissionDecision: "deny",
1688
+ permissionDecisionReason: decision.reason
1689
+ },
1690
+ systemMessage: decision.reason
1691
+ };
1692
+ }
1693
+ return { systemMessage: decision.reason };
1694
+ }
1695
+ if (host === "claude" || host === "claude-code") {
1696
+ if (event === "Stop" || event === "SubagentStop") {
1697
+ return { ok: false, reason: decision.reason };
1698
+ }
1699
+ if (decision.action === "block") {
1700
+ return {
1701
+ hookSpecificOutput: {
1702
+ hookEventName: event,
1703
+ permissionDecision: "deny",
1704
+ permissionDecisionReason: decision.reason
1705
+ }
1706
+ };
1707
+ }
1708
+ return { systemMessage: decision.reason };
1709
+ }
1710
+ if (host === "antigravity") {
1711
+ if (event === "Stop" || event === "SubagentStop") {
1712
+ return { decision: "continue", reason: decision.reason };
1713
+ }
1714
+ if (decision.action === "block") {
1715
+ return { decision: "deny", reason: decision.reason };
1716
+ }
1717
+ return { decision: "allow", reason: decision.reason };
1718
+ }
1719
+ return {};
1720
+ }
1721
+ function normalizeStopInput(input) {
1722
+ if (!isRecord(input)) {
1723
+ return { lastAssistantMessage: void 0, stopHookActive: false };
1724
+ }
1725
+ return {
1726
+ lastAssistantMessage: findString(input, ["last_assistant_message", "lastAssistantMessage", "message", "text"]),
1727
+ stopHookActive: findBoolean(input, ["stop_hook_active", "stopHookActive"]) ?? false
1728
+ };
1729
+ }
1730
+ function looksLikeCompletedEngineeringWork(message) {
1731
+ return completionSignalPatterns.some((pattern) => pattern.test(message));
1732
+ }
1733
+ function findString(input, keys) {
1734
+ for (const key of keys) {
1735
+ const value = input[key];
1736
+ if (typeof value === "string" && value.trim().length > 0) {
1737
+ return value;
1738
+ }
1739
+ }
1740
+ return void 0;
1741
+ }
1742
+ function findBoolean(input, keys) {
1743
+ for (const key of keys) {
1744
+ const value = input[key];
1745
+ if (typeof value === "boolean") {
1746
+ return value;
1747
+ }
1748
+ }
1749
+ return void 0;
1750
+ }
1751
+ function isRecord(value) {
1752
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1753
+ }
1754
+
1755
+ // src/host-hooks/types.ts
1756
+ function isHostHookHost(value) {
1757
+ return value === "antigravity" || value === "claude" || value === "claude-code" || value === "codex";
1758
+ }
1759
+ function isHostHookEvent(value) {
1760
+ return value === "PostToolUse" || value === "PreToolUse" || value === "Stop" || value === "SubagentStop" || value === "UserPromptSubmit";
1761
+ }
1762
+
1763
+ // src/commands/host-hook.ts
1764
+ function runHostHook(args) {
1765
+ const host = readOption(args, "--host");
1766
+ const event = readOption(args, "--event");
1767
+ if (!isHostHookHost(host)) {
1768
+ console.error("Expected --host <codex|claude-code|antigravity>");
1769
+ return 1;
1770
+ }
1771
+ if (!isHostHookEvent(event)) {
1772
+ console.error("Expected --event <Stop|SubagentStop|PreToolUse|PostToolUse|UserPromptSubmit>");
1773
+ return 1;
1774
+ }
1775
+ const input = readStdinJson();
1776
+ const decision = evaluateHostHook({ host, event, input });
1777
+ const output = formatHostHookOutput(host, event, decision);
1778
+ console.log(`${JSON.stringify(output)}
1779
+ `);
1780
+ return 0;
1781
+ }
1782
+ function readOption(args, name) {
1783
+ const index = args.indexOf(name);
1784
+ if (index < 0) return void 0;
1785
+ return args[index + 1];
1786
+ }
1787
+ function readStdinJson() {
1788
+ const raw = readFileSync9(0, "utf8").trim();
1789
+ if (!raw) return {};
1790
+ try {
1791
+ return JSON.parse(raw);
1792
+ } catch {
1793
+ return {};
1794
+ }
1795
+ }
1603
1796
 
1604
1797
  // src/commands/init.ts
1605
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
1798
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
1606
1799
  import { basename as basename3, dirname as dirname7, join as join13 } from "node:path";
1607
1800
 
1608
1801
  // src/commands/shared.ts
@@ -1611,6 +1804,7 @@ function planStarterFiles(profile) {
1611
1804
  const targetPath = starterTargetPath(asset.path);
1612
1805
  if (!targetPath) return [];
1613
1806
  if (profile && isOtherProfileRouting(targetPath, profile)) return [];
1807
+ if (profile && isEngineeringOnlyGuardrail(targetPath) && profile !== "engineering-runtime") return [];
1614
1808
  return [
1615
1809
  {
1616
1810
  sourcePath: asset.path,
@@ -1633,6 +1827,9 @@ function classifyOwnership(targetPath) {
1633
1827
  function isOtherProfileRouting(targetPath, profile) {
1634
1828
  return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v0.9.md`;
1635
1829
  }
1830
+ function isEngineeringOnlyGuardrail(targetPath) {
1831
+ return targetPath === ".codex/hooks.json" || targetPath === ".claude/settings.json" || targetPath === ".agents/hooks.json";
1832
+ }
1636
1833
  function starterTargetPath(sourcePath) {
1637
1834
  if (sourcePath === "starter/AGENTS.template.md") return "AGENTS.md";
1638
1835
  if (sourcePath === "starter/CLAUDE.template.md") return "CLAUDE.md";
@@ -1681,7 +1878,7 @@ function applyStarterFiles(files, profile) {
1681
1878
  for (const file of files) {
1682
1879
  const targetPath = join13(root, file.targetPath);
1683
1880
  mkdirSync4(dirname7(targetPath), { recursive: true });
1684
- const source = readFileSync8(file.absoluteSourcePath);
1881
+ const source = readFileSync10(file.absoluteSourcePath);
1685
1882
  const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
1686
1883
  writeFileSync3(targetPath, content);
1687
1884
  }
@@ -1715,7 +1912,7 @@ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:f
1715
1912
  import { dirname as dirname9 } from "node:path";
1716
1913
 
1717
1914
  // src/lens/audit.ts
1718
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
1915
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
1719
1916
  import { basename as basename4, dirname as dirname8, join as join14 } from "node:path";
1720
1917
  var REQUIRED_ARTIFACTS = [
1721
1918
  "manifest.md",
@@ -1763,7 +1960,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
1763
1960
  }
1764
1961
  let contract;
1765
1962
  try {
1766
- contract = JSON.parse(readFileSync9(contractPath, "utf8"));
1963
+ contract = JSON.parse(readFileSync11(contractPath, "utf8"));
1767
1964
  } catch (error) {
1768
1965
  return {
1769
1966
  ok: false,
@@ -1801,7 +1998,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
1801
1998
  issues.push({ type: "missing-required-artifact", path: artifactPath });
1802
1999
  continue;
1803
2000
  }
1804
- const content = readFileSync9(absolutePath, "utf8");
2001
+ const content = readFileSync11(absolutePath, "utf8");
1805
2002
  if (isPendingArtifact(content)) {
1806
2003
  issues.push({ type: "artifact-not-complete", path: artifactPath });
1807
2004
  } else if (hasTemplateBody(content)) {
@@ -2061,7 +2258,7 @@ function bulletList(values) {
2061
2258
 
2062
2259
  // src/lens/scan.ts
2063
2260
  import { spawnSync as spawnSync3 } from "node:child_process";
2064
- import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
2261
+ import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
2065
2262
  import { join as join15, relative as relative5 } from "node:path";
2066
2263
  var ignoredDirectories = /* @__PURE__ */ new Set([
2067
2264
  ".git",
@@ -2096,7 +2293,7 @@ function readPackageJson(targetDir) {
2096
2293
  const packageJsonPath = join15(targetDir, "package.json");
2097
2294
  if (!existsSync15(packageJsonPath)) return void 0;
2098
2295
  try {
2099
- const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf8"));
2296
+ const packageJson = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
2100
2297
  return {
2101
2298
  scripts: Object.keys(packageJson.scripts ?? {}).sort(),
2102
2299
  dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
@@ -2297,12 +2494,12 @@ import { existsSync as existsSync17 } from "node:fs";
2297
2494
  import { join as join16 } from "node:path";
2298
2495
 
2299
2496
  // src/portfolio/manifest.ts
2300
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
2497
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
2301
2498
  import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve2 } from "node:path";
2302
2499
  function loadPortfolioManifest(configPath) {
2303
2500
  let parsed;
2304
2501
  try {
2305
- parsed = JSON.parse(readFileSync11(configPath, "utf8"));
2502
+ parsed = JSON.parse(readFileSync13(configPath, "utf8"));
2306
2503
  } catch (error) {
2307
2504
  return {
2308
2505
  configPath,
@@ -2323,9 +2520,9 @@ function loadPortfolioManifest(configPath) {
2323
2520
  };
2324
2521
  }
2325
2522
  function resolveManifestPaths(value, configDir) {
2326
- if (!isRecord(value)) return value;
2523
+ if (!isRecord2(value)) return value;
2327
2524
  const resolveEndpoint = (endpoint) => {
2328
- if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
2525
+ if (!isRecord2(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
2329
2526
  return endpoint;
2330
2527
  }
2331
2528
  return { ...endpoint, path: resolve2(configDir, endpoint.path) };
@@ -2339,7 +2536,7 @@ function resolveManifestPaths(value, configDir) {
2339
2536
  }
2340
2537
  function validatePortfolioManifest(value) {
2341
2538
  const issues = [];
2342
- if (!isRecord(value)) {
2539
+ if (!isRecord2(value)) {
2343
2540
  return [
2344
2541
  {
2345
2542
  type: "invalid-field",
@@ -2375,7 +2572,7 @@ function validatePortfolioManifest(value) {
2375
2572
  }
2376
2573
  const seenTargetIds = /* @__PURE__ */ new Set();
2377
2574
  for (const target of value.targets) {
2378
- if (!isRecord(target)) {
2575
+ if (!isRecord2(target)) {
2379
2576
  issues.push({
2380
2577
  type: "invalid-field",
2381
2578
  field: "targets",
@@ -2432,7 +2629,7 @@ function validateAllowedFields(value, location, allowedFields, issues) {
2432
2629
  }
2433
2630
  function validateEndpoint(value, field, issues) {
2434
2631
  if (value === void 0) return;
2435
- if (!isRecord(value)) {
2632
+ if (!isRecord2(value)) {
2436
2633
  issues.push({
2437
2634
  type: "invalid-field",
2438
2635
  field,
@@ -2476,7 +2673,7 @@ function validateOptionalStringArray(value, id, field, issues) {
2476
2673
  });
2477
2674
  }
2478
2675
  }
2479
- function isRecord(value) {
2676
+ function isRecord2(value) {
2480
2677
  return typeof value === "object" && value !== null && !Array.isArray(value);
2481
2678
  }
2482
2679
 
@@ -2742,7 +2939,7 @@ function printUsage3() {
2742
2939
  }
2743
2940
 
2744
2941
  // src/commands/sync.ts
2745
- import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
2942
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
2746
2943
  import { join as join17 } from "node:path";
2747
2944
  function runSync(args) {
2748
2945
  if (!args.includes("--check")) {
@@ -2778,8 +2975,8 @@ function runSync(args) {
2778
2975
  continue;
2779
2976
  }
2780
2977
  if (file.ownership === "project-local-seed") continue;
2781
- const source = readFileSync12(file.absoluteSourcePath, "utf8");
2782
- const target = readFileSync12(targetPath, "utf8");
2978
+ const source = readFileSync14(file.absoluteSourcePath, "utf8");
2979
+ const target = readFileSync14(targetPath, "utf8");
2783
2980
  if (source !== target) {
2784
2981
  console.log(`different: ${file.targetPath}`);
2785
2982
  differences += 1;
@@ -2822,6 +3019,7 @@ var COMMANDS = [
2822
3019
  "lens report --target <path> --out <path>",
2823
3020
  "lens audit init --target <path> --out <path>",
2824
3021
  "lens audit check --dir <path> [--json]",
3022
+ "host-hook --host <codex|claude-code|antigravity> --event <Stop|SubagentStop|...>",
2825
3023
  "init --profile <engineering-runtime|doc-only> <--dry-run|--apply>",
2826
3024
  "sync --check [--profile <engineering-runtime|doc-only>]",
2827
3025
  "doctor"
@@ -2836,6 +3034,8 @@ if (!command || command === "--help" || command === "-h") {
2836
3034
  process.exitCode = runLens(process.argv.slice(3));
2837
3035
  } else if (command === "portfolio") {
2838
3036
  process.exitCode = runPortfolio(process.argv.slice(3));
3037
+ } else if (command === "host-hook") {
3038
+ process.exitCode = runHostHook(process.argv.slice(3));
2839
3039
  } else if (command === "init") {
2840
3040
  process.exitCode = runInit(process.argv.slice(3));
2841
3041
  } else if (command === "sync") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pieai/pro-gov",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
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.3.8"
38
+ "@pieai/doc-gov": "^0.3.9"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "24.13.2",