@pieai/pro-gov 0.3.9 → 0.3.11

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
@@ -149,8 +149,20 @@ Compound Gate: skipped -> <reason>
149
149
  ```
150
150
 
151
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.
152
+ verify that the host configs are wired. This is a wiring check: it proves the
153
+ project calls `pro-gov host-hook` for Codex, Claude Code, and Antigravity. The
154
+ host behavior contract is covered by PGS package tests against recorded
155
+ per-host Stop/SubagentStop fixtures. Open a fresh AI session after installing or
156
+ changing hooks; old sessions may not reload host configuration.
157
+
158
+ Antigravity hook commands enable `PGS_HOST_HOOK_DEBUG=1` by default in PGS
159
+ starters. The hook writes local diagnostics to `.git/pro-gov-hook-debug/` when
160
+ it is actually invoked, including the input summary, decision, and output JSON.
161
+ This directory lives inside Git's private metadata so it does not dirty the
162
+ worktree. If an Antigravity test does not create a diagnostic file, inspect
163
+ whether the host loaded `.agents/hooks.json` before changing PGS policy.
164
+ `.pro-gov/assets.json` is only an asset-install manifest; its `host` field does
165
+ not control runtime hook dispatch.
154
166
 
155
167
  Ponytail can be installed as an optional complexity adviser. Keep its global mode
156
168
  `off`; test `lite` in one isolated task before considering a stronger mode.
@@ -6,7 +6,7 @@ status: stable
6
6
  canonical: true
7
7
  owner: human
8
8
  created: 2026-05-06
9
- last_reviewed: 2026-06-15
9
+ last_reviewed: 2026-07-02
10
10
  domain: adoption
11
11
  tags:
12
12
  - adoption
@@ -92,8 +92,17 @@ Current package-based method:
92
92
  - copy `starter/lefthook.template.yml` to `lefthook.yml` and
93
93
  `starter/.github/workflows/docs-check.yml` to `.github/workflows/docs-check.yml`
94
94
  when the target project is ready for standard guardrails
95
+ - for an `engineering-runtime` target, install the three host-hook starter files
96
+ or merge their Stop/SubagentStop entries into the target's existing host
97
+ config:
98
+ - `.codex/hooks.json`
99
+ - `.claude/settings.json`
100
+ - `.agents/hooks.json`
95
101
  - run `doc-gov doctor` after wiring guardrails to verify they are actually
96
102
  connected
103
+ - run `pro-gov doctor --strict-hooks` after wiring host hooks to prove the
104
+ target has the expected host-hook configuration for Codex, Claude Code, and
105
+ Antigravity
97
106
  - treat the npm package as the CLI source and the local `docs/governance/`
98
107
  files as the project's checked-in governance contract
99
108
 
@@ -198,6 +207,16 @@ Use Compound Engineering by default only as the post-work Compound Gate:
198
207
  `ce-compound` captures reusable lessons when they exist; otherwise the agent
199
208
  reports a skip reason. Full CE workflows require an explicit user request.
200
209
 
210
+ The host-hook gate is deliberately small. It does not make Compound Engineering
211
+ the main workflow. It only blocks a final completion report that looks like
212
+ finished engineering work but does not say whether the Compound Gate ran or was
213
+ skipped:
214
+
215
+ ```text
216
+ Compound Gate: ran ce-compound -> <path>
217
+ Compound Gate: skipped -> <reason>
218
+ ```
219
+
201
220
  ### Doc-Only
202
221
 
203
222
  Add:
@@ -257,6 +276,7 @@ For the v0.9 structural migration, use
257
276
  Minimum:
258
277
 
259
278
  ```bash
279
+ pnpm pro-gov doctor --strict-hooks
260
280
  pnpm doc-gov check
261
281
  pnpm doc-gov router-check
262
282
  pnpm doc-gov scan --check
@@ -266,6 +286,21 @@ pnpm doc-gov doctor
266
286
  git diff --check
267
287
  ```
268
288
 
289
+ Use `pro-gov doctor --strict-hooks` for engineering-runtime projects. Doc-only
290
+ projects may omit strict host hooks unless they intentionally adopt the
291
+ engineering runtime profile.
292
+
293
+ `pro-gov doctor --strict-hooks` is a wiring check, not a live host simulation.
294
+ It proves the project has hook files that call `pro-gov host-hook`; PGS package
295
+ tests prove the expected Stop/SubagentStop behavior for the supported host
296
+ schemas. After installing or changing hooks, validate from a fresh AI session.
297
+ For Antigravity, a real invocation writes diagnostics under
298
+ `.git/pro-gov-hook-debug/` because the starter hook command sets
299
+ `PGS_HOST_HOOK_DEBUG=1`. Absence of a diagnostic file after a fresh
300
+ Antigravity session usually means the host did not load or run
301
+ `.agents/hooks.json`. The `.pro-gov/assets.json` `host` field records asset
302
+ placement only; it does not control runtime hook dispatch.
303
+
269
304
  Engineering projects should also run their local verification ladder.
270
305
 
271
306
  ## Example: Migrating A Runtime Product
@@ -5,7 +5,7 @@
5
5
  "hooks": [
6
6
  {
7
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",
8
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then PGS_HOST_HOOK_DEBUG=1 ./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 PGS_HOST_HOOK_DEBUG=1 pnpm --silent --filter @pieai/pro-gov dev host-hook --host antigravity --event Stop; else PGS_HOST_HOOK_DEBUG=1 npx --no-install @pieai/pro-gov host-hook --host antigravity --event Stop; fi",
9
9
  "timeout": 30
10
10
  }
11
11
  ]
@@ -16,7 +16,7 @@
16
16
  "hooks": [
17
17
  {
18
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",
19
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && if [ -x ./node_modules/.bin/pro-gov ]; then PGS_HOST_HOOK_DEBUG=1 ./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 PGS_HOST_HOOK_DEBUG=1 pnpm --silent --filter @pieai/pro-gov dev host-hook --host antigravity --event SubagentStop; else PGS_HOST_HOOK_DEBUG=1 npx --no-install @pieai/pro-gov host-hook --host antigravity --event SubagentStop; fi",
20
20
  "timeout": 30
21
21
  }
22
22
  ]
package/dist/cli.js CHANGED
@@ -1628,6 +1628,9 @@ function checkStrictHostHooks(root) {
1628
1628
  if (!content.includes("pro-gov host-hook") || !content.includes(`--host ${entry.host}`)) {
1629
1629
  issues.push(`host-hooks: ${entry.path} does not call pro-gov host-hook for ${entry.host}`);
1630
1630
  }
1631
+ if (entry.host === "antigravity" && !content.includes("PGS_HOST_HOOK_DEBUG=1")) {
1632
+ issues.push(`host-hooks: ${entry.path} does not enable PGS_HOST_HOOK_DEBUG=1 for Antigravity diagnostics`);
1633
+ }
1631
1634
  }
1632
1635
  return issues;
1633
1636
  }
@@ -1636,15 +1639,23 @@ function isEngineeringRuntimeProject(root) {
1636
1639
  }
1637
1640
 
1638
1641
  // src/commands/host-hook.ts
1639
- import { readFileSync as readFileSync9 } from "node:fs";
1642
+ import { spawnSync as spawnSync3 } from "node:child_process";
1643
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
1644
+ import { tmpdir as tmpdir2 } from "node:os";
1645
+ import { basename as basename3, join as join13, resolve as resolve2 } from "node:path";
1640
1646
 
1641
1647
  // src/host-hooks/host-hook-runner.ts
1648
+ import { closeSync, openSync, readFileSync as readFileSync9, readSync, statSync as statSync3 } from "node:fs";
1642
1649
  var gateMarkerPattern = /Compound Gate:\s*(ran ce-compound|skipped)\s*->/i;
1643
1650
  var completionSignalPatterns = [
1644
1651
  /\b(done|completed|implemented|fixed|verified|validated|shipped|pushed|committed)\b/i,
1645
1652
  /\b(tests?|typecheck|build|lint|doctor|pack)\b.*\b(pass|passed|green|succeed|succeeded|ok)\b/i,
1646
1653
  /\b(changed|updated|modified|created|deleted|refactored)\b.*\b(files?|docs?|tests?|hooks?|configs?)\b/i,
1647
- /完成|已完成|修好了|实现了|验证通过|测试通过|已经提交|已经推送|提交并推送/
1654
+ /已完成|完成了|修好了|实现了|验证通过|测试通过|已经提交|已经推送|提交并推送|已提交|已推送/
1655
+ ];
1656
+ var negativeCompletionSignalPatterns = [
1657
+ /\b(not done|not completed|not implemented|not fixed|did not complete|didn't complete|have not completed|haven't completed)\b/i,
1658
+ /没有完成|未完成|还没完成|没有改动|未改动|没有修改|未修改/
1648
1659
  ];
1649
1660
  var compoundGateInstruction = [
1650
1661
  "Before final reporting, pass the PGS Compound Gate.",
@@ -1653,6 +1664,7 @@ var compoundGateInstruction = [
1653
1664
  "If there is no reusable learning, report:",
1654
1665
  "Compound Gate: skipped -> <reason>"
1655
1666
  ].join("\n");
1667
+ var maxTranscriptBytes = 2 * 1024 * 1024;
1656
1668
  function evaluateHostHook(request) {
1657
1669
  if (request.event !== "Stop" && request.event !== "SubagentStop") {
1658
1670
  return { action: "allow" };
@@ -1694,7 +1706,7 @@ function formatHostHookOutput(host, event, decision) {
1694
1706
  }
1695
1707
  if (host === "claude" || host === "claude-code") {
1696
1708
  if (event === "Stop" || event === "SubagentStop") {
1697
- return { ok: false, reason: decision.reason };
1709
+ return { decision: "block", reason: decision.reason };
1698
1710
  }
1699
1711
  if (decision.action === "block") {
1700
1712
  return {
@@ -1723,11 +1735,14 @@ function normalizeStopInput(input) {
1723
1735
  return { lastAssistantMessage: void 0, stopHookActive: false };
1724
1736
  }
1725
1737
  return {
1726
- lastAssistantMessage: findString(input, ["last_assistant_message", "lastAssistantMessage", "message", "text"]),
1738
+ lastAssistantMessage: findString(input, ["last_assistant_message", "lastAssistantMessage", "message", "text"]) ?? findLastAssistantMessageFromTranscript(findString(input, ["agent_transcript_path", "agentTranscriptPath"])) ?? findLastAssistantMessageFromTranscript(findString(input, ["transcript_path", "transcriptPath"])),
1727
1739
  stopHookActive: findBoolean(input, ["stop_hook_active", "stopHookActive"]) ?? false
1728
1740
  };
1729
1741
  }
1730
1742
  function looksLikeCompletedEngineeringWork(message) {
1743
+ if (negativeCompletionSignalPatterns.some((pattern) => pattern.test(message))) {
1744
+ return false;
1745
+ }
1731
1746
  return completionSignalPatterns.some((pattern) => pattern.test(message));
1732
1747
  }
1733
1748
  function findString(input, keys) {
@@ -1748,6 +1763,86 @@ function findBoolean(input, keys) {
1748
1763
  }
1749
1764
  return void 0;
1750
1765
  }
1766
+ function findLastAssistantMessageFromTranscript(path) {
1767
+ if (!path || path.startsWith("~/")) {
1768
+ return void 0;
1769
+ }
1770
+ let content;
1771
+ try {
1772
+ content = readTranscriptTail(path);
1773
+ } catch {
1774
+ return void 0;
1775
+ }
1776
+ const lines = content.split(/\r?\n/);
1777
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
1778
+ const line = lines[index]?.trim();
1779
+ if (!line) {
1780
+ continue;
1781
+ }
1782
+ let record;
1783
+ try {
1784
+ record = JSON.parse(line);
1785
+ } catch {
1786
+ continue;
1787
+ }
1788
+ const message = extractAssistantText(record);
1789
+ if (message) {
1790
+ return message;
1791
+ }
1792
+ }
1793
+ return void 0;
1794
+ }
1795
+ function readTranscriptTail(path) {
1796
+ const size = statSync3(path).size;
1797
+ if (size <= maxTranscriptBytes) {
1798
+ return readFileSync9(path, "utf8");
1799
+ }
1800
+ const fd = openSync(path, "r");
1801
+ try {
1802
+ const buffer = Buffer.allocUnsafe(maxTranscriptBytes);
1803
+ readSync(fd, buffer, 0, maxTranscriptBytes, size - maxTranscriptBytes);
1804
+ return buffer.toString("utf8");
1805
+ } finally {
1806
+ closeSync(fd);
1807
+ }
1808
+ }
1809
+ function extractAssistantText(value) {
1810
+ if (!isRecord(value)) {
1811
+ return void 0;
1812
+ }
1813
+ const nested = value.message;
1814
+ const role = findString(value, ["role", "type"]);
1815
+ if (isRecord(nested)) {
1816
+ const nestedRole = findString(nested, ["role", "type"]);
1817
+ if (nestedRole === "assistant") {
1818
+ return extractTextContent(nested.content);
1819
+ }
1820
+ }
1821
+ if (role === "assistant") {
1822
+ return extractTextContent(value.content) ?? extractTextContent(value.text);
1823
+ }
1824
+ return void 0;
1825
+ }
1826
+ function extractTextContent(value) {
1827
+ if (typeof value === "string" && value.trim().length > 0) {
1828
+ return value;
1829
+ }
1830
+ if (!Array.isArray(value)) {
1831
+ return void 0;
1832
+ }
1833
+ const parts = [];
1834
+ for (const item of value) {
1835
+ if (typeof item === "string") {
1836
+ parts.push(item);
1837
+ continue;
1838
+ }
1839
+ if (isRecord(item) && typeof item.text === "string") {
1840
+ parts.push(item.text);
1841
+ }
1842
+ }
1843
+ const text = parts.join("\n").trim();
1844
+ return text.length > 0 ? text : void 0;
1845
+ }
1751
1846
  function isRecord(value) {
1752
1847
  return typeof value === "object" && value !== null && !Array.isArray(value);
1753
1848
  }
@@ -1761,7 +1856,9 @@ function isHostHookEvent(value) {
1761
1856
  }
1762
1857
 
1763
1858
  // src/commands/host-hook.ts
1764
- function runHostHook(args) {
1859
+ var defaultStdinTimeoutMs = 750;
1860
+ var maxDebugRawInputBytes = 256 * 1024;
1861
+ async function runHostHook(args) {
1765
1862
  const host = readOption(args, "--host");
1766
1863
  const event = readOption(args, "--event");
1767
1864
  if (!isHostHookHost(host)) {
@@ -1772,9 +1869,18 @@ function runHostHook(args) {
1772
1869
  console.error("Expected --event <Stop|SubagentStop|PreToolUse|PostToolUse|UserPromptSubmit>");
1773
1870
  return 1;
1774
1871
  }
1775
- const input = readStdinJson();
1872
+ const rawInput = await readStdinText(defaultStdinTimeoutMs);
1873
+ const input = parseStdinJson(rawInput);
1776
1874
  const decision = evaluateHostHook({ host, event, input });
1777
1875
  const output = formatHostHookOutput(host, event, decision);
1876
+ writeDebugLogIfRequested(args, {
1877
+ decision,
1878
+ event,
1879
+ host,
1880
+ input,
1881
+ output,
1882
+ rawInput
1883
+ });
1778
1884
  console.log(`${JSON.stringify(output)}
1779
1885
  `);
1780
1886
  return 0;
@@ -1784,8 +1890,7 @@ function readOption(args, name) {
1784
1890
  if (index < 0) return void 0;
1785
1891
  return args[index + 1];
1786
1892
  }
1787
- function readStdinJson() {
1788
- const raw = readFileSync9(0, "utf8").trim();
1893
+ function parseStdinJson(raw) {
1789
1894
  if (!raw) return {};
1790
1895
  try {
1791
1896
  return JSON.parse(raw);
@@ -1793,10 +1898,106 @@ function readStdinJson() {
1793
1898
  return {};
1794
1899
  }
1795
1900
  }
1901
+ function readStdinText(timeoutMs) {
1902
+ if (process.stdin.isTTY) {
1903
+ return Promise.resolve("");
1904
+ }
1905
+ process.stdin.setEncoding("utf8");
1906
+ return new Promise((resolveText) => {
1907
+ let settled = false;
1908
+ let content = "";
1909
+ const settle = () => {
1910
+ if (settled) return;
1911
+ settled = true;
1912
+ clearTimeout(timer);
1913
+ process.stdin.off("data", onData);
1914
+ process.stdin.off("end", settle);
1915
+ process.stdin.off("error", settle);
1916
+ process.stdin.pause();
1917
+ resolveText(content.trim());
1918
+ };
1919
+ const onData = (chunk) => {
1920
+ content += chunk.toString();
1921
+ };
1922
+ const timer = setTimeout(settle, timeoutMs);
1923
+ timer.unref();
1924
+ process.stdin.on("data", onData);
1925
+ process.stdin.on("end", settle);
1926
+ process.stdin.on("error", settle);
1927
+ process.stdin.resume();
1928
+ });
1929
+ }
1930
+ function writeDebugLogIfRequested(args, record) {
1931
+ const explicitPath = readOption(args, "--debug-log");
1932
+ const enabled = explicitPath !== void 0 || process.env.PGS_HOST_HOOK_DEBUG === "1";
1933
+ if (!enabled) return;
1934
+ const debugDir = explicitPath && explicitPath.trim().length > 0 ? explicitPath : defaultDebugDir();
1935
+ try {
1936
+ mkdirSync4(debugDir, { recursive: true });
1937
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1938
+ const fileName = `${timestamp}-${process.pid}-${record.host}-${record.event}.json`;
1939
+ writeFileSync3(
1940
+ join13(debugDir, fileName),
1941
+ `${JSON.stringify(
1942
+ {
1943
+ schemaVersion: 1,
1944
+ cwd: process.cwd(),
1945
+ event: record.event,
1946
+ host: record.host,
1947
+ nodeVersion: process.version,
1948
+ packageVersion: readPackageVersion(),
1949
+ rawInput: truncateDebugRawInput(record.rawInput),
1950
+ input: record.input,
1951
+ decision: record.decision,
1952
+ output: record.output
1953
+ },
1954
+ null,
1955
+ 2
1956
+ )}
1957
+ `,
1958
+ "utf8"
1959
+ );
1960
+ } catch {
1961
+ }
1962
+ }
1963
+ function defaultDebugDir() {
1964
+ const gitPath = spawnSync3("git", ["rev-parse", "--git-path", "pro-gov-hook-debug"], {
1965
+ encoding: "utf8",
1966
+ stdio: ["ignore", "pipe", "ignore"]
1967
+ });
1968
+ const value = gitPath.status === 0 ? gitPath.stdout.trim() : "";
1969
+ if (value) {
1970
+ return resolve2(process.cwd(), value);
1971
+ }
1972
+ return join13(tmpdir2(), "pro-gov-hook-debug", basename3(process.cwd()));
1973
+ }
1974
+ function readPackageVersion() {
1975
+ const packageJsonPath = resolvePackageJsonPath();
1976
+ if (!packageJsonPath) return void 0;
1977
+ try {
1978
+ const parsed = JSON.parse(readFileSync10(packageJsonPath, "utf8"));
1979
+ return typeof parsed.version === "string" ? parsed.version : void 0;
1980
+ } catch {
1981
+ return void 0;
1982
+ }
1983
+ }
1984
+ function resolvePackageJsonPath() {
1985
+ const candidates = [
1986
+ resolve2(process.cwd(), "packages/pro-gov/package.json"),
1987
+ resolve2(process.cwd(), "node_modules/@pieai/pro-gov/package.json")
1988
+ ];
1989
+ return candidates.find((candidate) => existsSync13(candidate));
1990
+ }
1991
+ function truncateDebugRawInput(value) {
1992
+ const buffer = Buffer.from(value);
1993
+ if (buffer.byteLength <= maxDebugRawInputBytes) return value;
1994
+ return `${buffer.subarray(0, maxDebugRawInputBytes).toString("utf8")}
1995
+ [truncated]`;
1996
+ }
1796
1997
 
1797
1998
  // src/commands/init.ts
1798
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
1799
- import { basename as basename3, dirname as dirname7, join as join13 } from "node:path";
1999
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
2000
+ import { basename as basename4, dirname as dirname7, join as join14 } from "node:path";
1800
2001
 
1801
2002
  // src/commands/shared.ts
1802
2003
  function planStarterFiles(profile) {
@@ -1868,7 +2069,7 @@ function runInit(args) {
1868
2069
  }
1869
2070
  function applyStarterFiles(files, profile) {
1870
2071
  const root = process.cwd();
1871
- const conflicts = files.filter((file) => existsSync13(join13(root, file.targetPath)));
2072
+ const conflicts = files.filter((file) => existsSync14(join14(root, file.targetPath)));
1872
2073
  if (conflicts.length > 0) {
1873
2074
  console.error("pro-gov init is refusing to overwrite existing project files:");
1874
2075
  for (const file of conflicts) console.error(` ${file.targetPath}`);
@@ -1876,11 +2077,11 @@ function applyStarterFiles(files, profile) {
1876
2077
  return 1;
1877
2078
  }
1878
2079
  for (const file of files) {
1879
- const targetPath = join13(root, file.targetPath);
1880
- mkdirSync4(dirname7(targetPath), { recursive: true });
1881
- const source = readFileSync10(file.absoluteSourcePath);
1882
- const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
1883
- writeFileSync3(targetPath, content);
2080
+ const targetPath = join14(root, file.targetPath);
2081
+ mkdirSync5(dirname7(targetPath), { recursive: true });
2082
+ const source = readFileSync11(file.absoluteSourcePath);
2083
+ const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename4(root), profile) : source;
2084
+ writeFileSync4(targetPath, content);
1884
2085
  }
1885
2086
  console.log("pro-gov init APPLIED");
1886
2087
  console.log(`profile: ${profile}`);
@@ -1908,12 +2109,12 @@ function readFlag(args, flag) {
1908
2109
  }
1909
2110
 
1910
2111
  // src/commands/lens.ts
1911
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2112
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
1912
2113
  import { dirname as dirname9 } from "node:path";
1913
2114
 
1914
2115
  // src/lens/audit.ts
1915
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
1916
- import { basename as basename4, dirname as dirname8, join as join14 } from "node:path";
2116
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "node:fs";
2117
+ import { basename as basename5, dirname as dirname8, join as join15 } from "node:path";
1917
2118
  var REQUIRED_ARTIFACTS = [
1918
2119
  "manifest.md",
1919
2120
  "raw/project-lens/architecture-lens.md",
@@ -1933,20 +2134,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
1933
2134
  version: 1,
1934
2135
  target: {
1935
2136
  path: targetDir,
1936
- name: basename4(targetDir) || "target"
2137
+ name: basename5(targetDir) || "target"
1937
2138
  },
1938
2139
  requiredArtifacts: [...REQUIRED_ARTIFACTS]
1939
2140
  };
1940
- mkdirSync5(auditDir, { recursive: true });
1941
- writeJson(join14(auditDir, "audit.contract.json"), contract);
2141
+ mkdirSync6(auditDir, { recursive: true });
2142
+ writeJson(join15(auditDir, "audit.contract.json"), contract);
1942
2143
  for (const artifactPath of REQUIRED_ARTIFACTS) {
1943
- writeTemplate(join14(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
2144
+ writeTemplate(join15(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
1944
2145
  }
1945
2146
  return contract;
1946
2147
  }
1947
2148
  function checkProjectLensAuditPackage(auditDir, options = {}) {
1948
- const contractPath = join14(auditDir, "audit.contract.json");
1949
- if (!existsSync14(contractPath)) {
2149
+ const contractPath = join15(auditDir, "audit.contract.json");
2150
+ if (!existsSync15(contractPath)) {
1950
2151
  return {
1951
2152
  ok: false,
1952
2153
  auditDir,
@@ -1960,7 +2161,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
1960
2161
  }
1961
2162
  let contract;
1962
2163
  try {
1963
- contract = JSON.parse(readFileSync11(contractPath, "utf8"));
2164
+ contract = JSON.parse(readFileSync12(contractPath, "utf8"));
1964
2165
  } catch (error) {
1965
2166
  return {
1966
2167
  ok: false,
@@ -1993,12 +2194,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
1993
2194
  }
1994
2195
  }
1995
2196
  for (const artifactPath of REQUIRED_ARTIFACTS) {
1996
- const absolutePath = join14(auditDir, artifactPath);
1997
- if (!existsSync14(absolutePath)) {
2197
+ const absolutePath = join15(auditDir, artifactPath);
2198
+ if (!existsSync15(absolutePath)) {
1998
2199
  issues.push({ type: "missing-required-artifact", path: artifactPath });
1999
2200
  continue;
2000
2201
  }
2001
- const content = readFileSync11(absolutePath, "utf8");
2202
+ const content = readFileSync12(absolutePath, "utf8");
2002
2203
  if (isPendingArtifact(content)) {
2003
2204
  issues.push({ type: "artifact-not-complete", path: artifactPath });
2004
2205
  } else if (hasTemplateBody(content)) {
@@ -2014,13 +2215,13 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
2014
2215
  };
2015
2216
  }
2016
2217
  function writeJson(path, value) {
2017
- mkdirSync5(dirname8(path), { recursive: true });
2018
- writeFileSync4(path, `${JSON.stringify(value, null, 2)}
2218
+ mkdirSync6(dirname8(path), { recursive: true });
2219
+ writeFileSync5(path, `${JSON.stringify(value, null, 2)}
2019
2220
  `);
2020
2221
  }
2021
2222
  function writeTemplate(path, content) {
2022
- mkdirSync5(dirname8(path), { recursive: true });
2023
- writeFileSync4(path, content);
2223
+ mkdirSync6(dirname8(path), { recursive: true });
2224
+ writeFileSync5(path, content);
2024
2225
  }
2025
2226
  function renderArtifactTemplate(artifactPath, contract) {
2026
2227
  const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
@@ -2257,9 +2458,9 @@ function bulletList(values) {
2257
2458
  }
2258
2459
 
2259
2460
  // src/lens/scan.ts
2260
- import { spawnSync as spawnSync3 } from "node:child_process";
2261
- import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
2262
- import { join as join15, relative as relative5 } from "node:path";
2461
+ import { spawnSync as spawnSync4 } from "node:child_process";
2462
+ import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
2463
+ import { join as join16, relative as relative5 } from "node:path";
2263
2464
  var ignoredDirectories = /* @__PURE__ */ new Set([
2264
2465
  ".git",
2265
2466
  ".next",
@@ -2276,24 +2477,24 @@ function scanProjectLensTarget(targetDir, options = {}) {
2276
2477
  return {
2277
2478
  targetDir,
2278
2479
  aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter(
2279
- (file) => existsSync15(join15(targetDir, file))
2480
+ (file) => existsSync16(join16(targetDir, file))
2280
2481
  ),
2281
2482
  aiConfigFiles: [],
2282
2483
  packageJson,
2283
2484
  docs: {
2284
- hasDocsDirectory: existsSync15(join15(targetDir, "docs")),
2485
+ hasDocsDirectory: existsSync16(join16(targetDir, "docs")),
2285
2486
  markdownFileCount: markdownFiles.length,
2286
2487
  governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
2287
2488
  },
2288
2489
  git: readGitState(targetDir),
2289
- largeFiles: files.map((file) => ({ path: file, bytes: statSync3(join15(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
2490
+ largeFiles: files.map((file) => ({ path: file, bytes: statSync4(join16(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
2290
2491
  };
2291
2492
  }
2292
2493
  function readPackageJson(targetDir) {
2293
- const packageJsonPath = join15(targetDir, "package.json");
2294
- if (!existsSync15(packageJsonPath)) return void 0;
2494
+ const packageJsonPath = join16(targetDir, "package.json");
2495
+ if (!existsSync16(packageJsonPath)) return void 0;
2295
2496
  try {
2296
- const packageJson = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
2497
+ const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
2297
2498
  return {
2298
2499
  scripts: Object.keys(packageJson.scripts ?? {}).sort(),
2299
2500
  dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
@@ -2316,7 +2517,7 @@ function readGitState(targetDir) {
2316
2517
  };
2317
2518
  }
2318
2519
  function runGit(targetDir, args) {
2319
- const result = spawnSync3("git", ["-C", targetDir, ...args], {
2520
+ const result = spawnSync4("git", ["-C", targetDir, ...args], {
2320
2521
  encoding: "utf8"
2321
2522
  });
2322
2523
  if (result.status !== 0) return { ok: false };
@@ -2328,13 +2529,13 @@ function listProjectFiles(targetDir) {
2328
2529
  return files.sort();
2329
2530
  }
2330
2531
  function collectFiles2(rootDir, currentDir, files) {
2331
- if (!existsSync15(currentDir)) return;
2532
+ if (!existsSync16(currentDir)) return;
2332
2533
  for (const entry of readdirSync6(currentDir, { withFileTypes: true })) {
2333
2534
  if (entry.isDirectory()) {
2334
2535
  if (ignoredDirectories.has(entry.name)) continue;
2335
- collectFiles2(rootDir, join15(currentDir, entry.name), files);
2536
+ collectFiles2(rootDir, join16(currentDir, entry.name), files);
2336
2537
  } else if (entry.isFile()) {
2337
- files.push(toUnixPath4(relative5(rootDir, join15(currentDir, entry.name))));
2538
+ files.push(toUnixPath4(relative5(rootDir, join16(currentDir, entry.name))));
2338
2539
  }
2339
2540
  }
2340
2541
  }
@@ -2386,8 +2587,8 @@ function runLensReport(args) {
2386
2587
  }
2387
2588
  const report = scanProjectLensTarget(options.value.targetDir);
2388
2589
  const markdown = renderProjectLensMarkdownReport(report);
2389
- mkdirSync6(dirname9(options.value.outPath), { recursive: true });
2390
- writeFileSync5(options.value.outPath, markdown);
2590
+ mkdirSync7(dirname9(options.value.outPath), { recursive: true });
2591
+ writeFileSync6(options.value.outPath, markdown);
2391
2592
  console.log(`report: ${options.value.outPath}`);
2392
2593
  return 0;
2393
2594
  }
@@ -2490,16 +2691,16 @@ function printUsage2() {
2490
2691
  }
2491
2692
 
2492
2693
  // src/commands/portfolio.ts
2493
- import { existsSync as existsSync17 } from "node:fs";
2494
- import { join as join16 } from "node:path";
2694
+ import { existsSync as existsSync18 } from "node:fs";
2695
+ import { join as join17 } from "node:path";
2495
2696
 
2496
2697
  // src/portfolio/manifest.ts
2497
- import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
2498
- import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve2 } from "node:path";
2698
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
2699
+ import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
2499
2700
  function loadPortfolioManifest(configPath) {
2500
2701
  let parsed;
2501
2702
  try {
2502
- parsed = JSON.parse(readFileSync13(configPath, "utf8"));
2703
+ parsed = JSON.parse(readFileSync14(configPath, "utf8"));
2503
2704
  } catch (error) {
2504
2705
  return {
2505
2706
  configPath,
@@ -2511,7 +2712,7 @@ function loadPortfolioManifest(configPath) {
2511
2712
  ]
2512
2713
  };
2513
2714
  }
2514
- const normalized = resolveManifestPaths(parsed, dirname10(resolve2(configPath)));
2715
+ const normalized = resolveManifestPaths(parsed, dirname10(resolve3(configPath)));
2515
2716
  const issues = validatePortfolioManifest(normalized);
2516
2717
  return {
2517
2718
  configPath,
@@ -2525,7 +2726,7 @@ function resolveManifestPaths(value, configDir) {
2525
2726
  if (!isRecord2(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
2526
2727
  return endpoint;
2527
2728
  }
2528
- return { ...endpoint, path: resolve2(configDir, endpoint.path) };
2729
+ return { ...endpoint, path: resolve3(configDir, endpoint.path) };
2529
2730
  };
2530
2731
  return {
2531
2732
  ...value,
@@ -2653,7 +2854,7 @@ function validateEndpoint(value, field, issues) {
2653
2854
  });
2654
2855
  return;
2655
2856
  }
2656
- if (!existsSync16(value.path)) {
2857
+ if (!existsSync17(value.path)) {
2657
2858
  issues.push({
2658
2859
  type: "missing-path",
2659
2860
  id: typeof value.id === "string" ? value.id : void 0,
@@ -2928,8 +3129,8 @@ function isHost2(value) {
2928
3129
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
2929
3130
  }
2930
3131
  function findPortfolioAgentAssetsDir(manifest) {
2931
- const agentAssetsDir = manifest?.executionEngine?.path ? join16(manifest.executionEngine.path, "agent-assets") : void 0;
2932
- return agentAssetsDir && existsSync17(join16(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
3132
+ const agentAssetsDir = manifest?.executionEngine?.path ? join17(manifest.executionEngine.path, "agent-assets") : void 0;
3133
+ return agentAssetsDir && existsSync18(join17(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
2933
3134
  }
2934
3135
  function printUsage3() {
2935
3136
  console.error("Usage:");
@@ -2939,8 +3140,8 @@ function printUsage3() {
2939
3140
  }
2940
3141
 
2941
3142
  // src/commands/sync.ts
2942
- import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
2943
- import { join as join17 } from "node:path";
3143
+ import { existsSync as existsSync19, readFileSync as readFileSync15 } from "node:fs";
3144
+ import { join as join18 } from "node:path";
2944
3145
  function runSync(args) {
2945
3146
  if (!args.includes("--check")) {
2946
3147
  console.error("pro-gov sync is read-only and requires --check.");
@@ -2967,16 +3168,16 @@ function runSync(args) {
2967
3168
  console.log("pro-gov sync check");
2968
3169
  console.log(`profile: ${profile}`);
2969
3170
  for (const file of planStarterFiles(profile)) {
2970
- const targetPath = join17(process.cwd(), file.targetPath);
2971
- if (!existsSync18(targetPath)) {
3171
+ const targetPath = join18(process.cwd(), file.targetPath);
3172
+ if (!existsSync19(targetPath)) {
2972
3173
  if (file.ownership === "optional-guardrail") continue;
2973
3174
  console.log(`missing: ${file.targetPath}`);
2974
3175
  differences += 1;
2975
3176
  continue;
2976
3177
  }
2977
3178
  if (file.ownership === "project-local-seed") continue;
2978
- const source = readFileSync14(file.absoluteSourcePath, "utf8");
2979
- const target = readFileSync14(targetPath, "utf8");
3179
+ const source = readFileSync15(file.absoluteSourcePath, "utf8");
3180
+ const target = readFileSync15(targetPath, "utf8");
2980
3181
  if (source !== target) {
2981
3182
  console.log(`different: ${file.targetPath}`);
2982
3183
  differences += 1;
@@ -2991,7 +3192,7 @@ function runSync(args) {
2991
3192
  }
2992
3193
  function inferInstalledProfile(root) {
2993
3194
  const installed = ["engineering-runtime", "doc-only"].filter(
2994
- (profile) => existsSync18(join17(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
3195
+ (profile) => existsSync19(join18(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
2995
3196
  );
2996
3197
  return installed.length === 1 ? installed[0] : void 0;
2997
3198
  }
@@ -3025,27 +3226,22 @@ var COMMANDS = [
3025
3226
  "doctor"
3026
3227
  ];
3027
3228
  var [command, subcommand] = process.argv.slice(2);
3028
- if (!command || command === "--help" || command === "-h") {
3029
- printHelp();
3030
- process.exitCode = command ? 0 : 1;
3031
- } else if (command === "assets") {
3032
- process.exitCode = runAssets(process.argv.slice(3));
3033
- } else if (command === "lens") {
3034
- process.exitCode = runLens(process.argv.slice(3));
3035
- } else if (command === "portfolio") {
3036
- process.exitCode = runPortfolio(process.argv.slice(3));
3037
- } else if (command === "host-hook") {
3038
- process.exitCode = runHostHook(process.argv.slice(3));
3039
- } else if (command === "init") {
3040
- process.exitCode = runInit(process.argv.slice(3));
3041
- } else if (command === "sync") {
3042
- process.exitCode = runSync(process.argv.slice(3));
3043
- } else if (command === "doctor") {
3044
- process.exitCode = runDoctor(process.argv.slice(3));
3045
- } else {
3229
+ process.exitCode = await main();
3230
+ async function main() {
3231
+ if (!command || command === "--help" || command === "-h") {
3232
+ printHelp();
3233
+ return command ? 0 : 1;
3234
+ }
3235
+ if (command === "assets") return runAssets(process.argv.slice(3));
3236
+ if (command === "lens") return runLens(process.argv.slice(3));
3237
+ if (command === "portfolio") return runPortfolio(process.argv.slice(3));
3238
+ if (command === "host-hook") return runHostHook(process.argv.slice(3));
3239
+ if (command === "init") return runInit(process.argv.slice(3));
3240
+ if (command === "sync") return runSync(process.argv.slice(3));
3241
+ if (command === "doctor") return runDoctor(process.argv.slice(3));
3046
3242
  console.error(`Unknown command: ${[command, subcommand].filter(Boolean).join(" ")}`);
3047
3243
  printHelp();
3048
- process.exitCode = 1;
3244
+ return 1;
3049
3245
  }
3050
3246
  function printHelp() {
3051
3247
  console.log("pro-gov \u2014 project-level distribution kit for Project Governance System");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pieai/pro-gov",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
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.9"
38
+ "@pieai/doc-gov": "^0.3.11"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "24.13.2",