@pieai/pro-gov 0.4.3 → 0.4.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/README.md +16 -58
- package/assets/docs/reference/adoption/adoption-playbook.md +16 -68
- package/assets/docs/reference/adoption/agent-workflow-methodology-comparison.md +10 -2
- package/assets/docs/reference/adoption/recommended-agent-tooling.md +21 -118
- package/assets/integrations/compound-engineering.md +8 -161
- package/assets/integrations/directed-development.md +4 -6
- package/assets/integrations/mattpocock-skills.md +27 -130
- package/assets/integrations/ponytail.md +6 -158
- package/assets/integrations/superpowers.md +9 -104
- package/assets/profiles/doc-only/manifest.yml +1 -4
- package/assets/profiles/doc-only/profile.md +1 -1
- package/assets/profiles/engineering-runtime/manifest.yml +1 -4
- package/assets/profiles/engineering-runtime/profile.md +3 -13
- package/assets/starter/AGENTS.template.md +5 -7
- package/assets/starter/docs/governance/agents-routing/doc-only-v0.9.md +2 -2
- package/assets/starter/docs/governance/agents-routing/engineering-runtime-v0.9.md +5 -4
- package/assets/starter/docs/governance/boundary.md +4 -7
- package/assets/starter/docs/governance/ssot-v0.9.md +3 -4
- package/assets/starter/docs/reference/documentation-map.md +4 -4
- package/cli-guide.md +16 -40
- package/dist/cli.js +134 -707
- package/package.json +2 -2
- package/assets/starter/.agents/hooks.json +0 -26
- package/assets/starter/.claude/settings.json +0 -26
- package/assets/starter/.codex/hooks.json +0 -28
package/dist/cli.js
CHANGED
|
@@ -1638,7 +1638,7 @@ function printNpxUsage() {
|
|
|
1638
1638
|
|
|
1639
1639
|
// src/commands/doctor.ts
|
|
1640
1640
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1641
|
-
import { existsSync as existsSync12
|
|
1641
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
1642
1642
|
import { createRequire } from "node:module";
|
|
1643
1643
|
import { dirname as dirname6, join as join12 } from "node:path";
|
|
1644
1644
|
var REQUIRED_ASSETS = [
|
|
@@ -1650,11 +1650,9 @@ var REQUIRED_ASSETS = [
|
|
|
1650
1650
|
"profiles/doc-only/profile.md"
|
|
1651
1651
|
];
|
|
1652
1652
|
function runDoctor(_args) {
|
|
1653
|
-
const strictHooks = _args.includes("--strict-hooks");
|
|
1654
1653
|
const assets = listAssets();
|
|
1655
1654
|
const assetPaths = new Set(assets.map((asset) => asset.path));
|
|
1656
1655
|
const missing = REQUIRED_ASSETS.filter((assetPath) => !assetPaths.has(assetPath));
|
|
1657
|
-
const hookIssues = strictHooks ? checkStrictHostHooks(process.cwd()) : [];
|
|
1658
1656
|
console.log("pro-gov doctor");
|
|
1659
1657
|
console.log(`assets: ${assets.length}`);
|
|
1660
1658
|
if (missing.length > 0) {
|
|
@@ -1665,13 +1663,7 @@ function runDoctor(_args) {
|
|
|
1665
1663
|
console.log("assets: required project-governance assets found");
|
|
1666
1664
|
}
|
|
1667
1665
|
console.log(checkDocGov());
|
|
1668
|
-
|
|
1669
|
-
console.error(issue);
|
|
1670
|
-
}
|
|
1671
|
-
if (strictHooks && hookIssues.length === 0) {
|
|
1672
|
-
console.log("host-hooks: PGS Compound Gate hooks wired");
|
|
1673
|
-
}
|
|
1674
|
-
return missing.length > 0 || hookIssues.length > 0 ? 1 : 0;
|
|
1666
|
+
return missing.length > 0 ? 1 : 0;
|
|
1675
1667
|
}
|
|
1676
1668
|
function checkDocGov() {
|
|
1677
1669
|
const fromPath = spawnSync2("doc-gov", ["--help"], {
|
|
@@ -1704,424 +1696,10 @@ function resolveDocGovDependencyCli() {
|
|
|
1704
1696
|
return null;
|
|
1705
1697
|
}
|
|
1706
1698
|
}
|
|
1707
|
-
function checkStrictHostHooks(root) {
|
|
1708
|
-
if (!isEngineeringRuntimeProject(root)) {
|
|
1709
|
-
return [];
|
|
1710
|
-
}
|
|
1711
|
-
const expected = [
|
|
1712
|
-
{ path: ".codex/hooks.json", host: "codex" },
|
|
1713
|
-
{ path: ".claude/settings.json", host: "claude-code" },
|
|
1714
|
-
{ path: ".agents/hooks.json", host: "antigravity" }
|
|
1715
|
-
];
|
|
1716
|
-
const issues = [];
|
|
1717
|
-
for (const entry of expected) {
|
|
1718
|
-
const absolutePath = join12(root, entry.path);
|
|
1719
|
-
if (!existsSync12(absolutePath)) {
|
|
1720
|
-
issues.push(`host-hooks: missing ${entry.path}`);
|
|
1721
|
-
continue;
|
|
1722
|
-
}
|
|
1723
|
-
const content = readFileSync8(absolutePath, "utf8");
|
|
1724
|
-
if (!content.includes("pro-gov host-hook") || !content.includes(`--host ${entry.host}`)) {
|
|
1725
|
-
issues.push(`host-hooks: ${entry.path} does not call pro-gov host-hook for ${entry.host}`);
|
|
1726
|
-
}
|
|
1727
|
-
if (entry.host === "antigravity" && !content.includes("PGS_HOST_HOOK_DEBUG=1")) {
|
|
1728
|
-
issues.push(`host-hooks: ${entry.path} does not enable PGS_HOST_HOOK_DEBUG=1 for Antigravity diagnostics`);
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
return issues;
|
|
1732
|
-
}
|
|
1733
|
-
function isEngineeringRuntimeProject(root) {
|
|
1734
|
-
return existsSync12(join12(root, "docs/governance/agents-routing/engineering-runtime-v0.9.md"));
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
// src/commands/host-hook.ts
|
|
1738
|
-
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1739
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1740
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
1741
|
-
import { basename as basename3, join as join13, resolve as resolve3 } from "node:path";
|
|
1742
|
-
|
|
1743
|
-
// src/host-hooks/host-hook-runner.ts
|
|
1744
|
-
import { closeSync, openSync, readFileSync as readFileSync9, readSync, statSync as statSync3 } from "node:fs";
|
|
1745
|
-
var gateMarkerPattern = /Compound Gate:\s*(ran ce-compound|ran fallback capture|skipped)\s*->/i;
|
|
1746
|
-
var completionSignalPatterns = [
|
|
1747
|
-
/\b(done|completed|implemented|fixed|verified|validated|shipped|pushed|committed)\b/i,
|
|
1748
|
-
/\b(tests?|typecheck|build|lint|doctor|pack)\b.*\b(pass|passed|green|succeed|succeeded|ok)\b/i,
|
|
1749
|
-
/\b(changed|updated|modified|created|deleted|refactored)\b.*\b(files?|docs?|tests?|hooks?|configs?)\b/i,
|
|
1750
|
-
/已完成|完成了|修好了|实现了|验证通过|测试通过|已经提交|已经推送|提交并推送|已提交|已推送/
|
|
1751
|
-
];
|
|
1752
|
-
var negativeCompletionSignalPatterns = [
|
|
1753
|
-
/\b(not done|not completed|not implemented|not fixed|did not complete|didn't complete|have not completed|haven't completed)\b/i,
|
|
1754
|
-
/没有完成|未完成|还没完成|没有改动|未改动|没有修改|未修改/
|
|
1755
|
-
];
|
|
1756
|
-
var compoundGateInstruction = [
|
|
1757
|
-
"Before final reporting, pass the PGS Compound Gate.",
|
|
1758
|
-
"If this completed work produced reusable learning, run compound-engineering:ce-compound and report:",
|
|
1759
|
-
"Compound Gate: ran ce-compound -> <path>",
|
|
1760
|
-
"If the Compound Engineering plugin is unavailable in this host, use PGS fallback capture:",
|
|
1761
|
-
'pro-gov learn capture --title "<learning title>" --summary "<reusable lesson>"',
|
|
1762
|
-
"Compound Gate: ran fallback capture -> <path>",
|
|
1763
|
-
"If there is no reusable learning, report:",
|
|
1764
|
-
"Compound Gate: skipped -> <reason>"
|
|
1765
|
-
].join("\n");
|
|
1766
|
-
var liteCompoundGateInstruction = [
|
|
1767
|
-
"Pass the PGS Compound Gate before final reporting.",
|
|
1768
|
-
"Report one line:",
|
|
1769
|
-
"Compound Gate: ran ce-compound -> <path>",
|
|
1770
|
-
"Compound Gate: ran fallback capture -> <path>",
|
|
1771
|
-
"Compound Gate: skipped -> <reason>"
|
|
1772
|
-
].join("\n");
|
|
1773
|
-
var maxTranscriptBytes = 2 * 1024 * 1024;
|
|
1774
|
-
function evaluateHostHook(request) {
|
|
1775
|
-
const mode = request.compoundGateMode ?? "off";
|
|
1776
|
-
if (mode === "off") {
|
|
1777
|
-
return { action: "allow" };
|
|
1778
|
-
}
|
|
1779
|
-
if (request.event !== "Stop" && request.event !== "SubagentStop") {
|
|
1780
|
-
return { action: "allow" };
|
|
1781
|
-
}
|
|
1782
|
-
const input = normalizeStopInput(request.input);
|
|
1783
|
-
if (input.stopHookActive) {
|
|
1784
|
-
return { action: "allow" };
|
|
1785
|
-
}
|
|
1786
|
-
if (!input.lastAssistantMessage) {
|
|
1787
|
-
return { action: "allow" };
|
|
1788
|
-
}
|
|
1789
|
-
if (gateMarkerPattern.test(input.lastAssistantMessage)) {
|
|
1790
|
-
return { action: "allow" };
|
|
1791
|
-
}
|
|
1792
|
-
if (!looksLikeCompletedEngineeringWork(input.lastAssistantMessage)) {
|
|
1793
|
-
return { action: "allow" };
|
|
1794
|
-
}
|
|
1795
|
-
return { action: "continue", reason: compoundGateInstructionForMode(mode) };
|
|
1796
|
-
}
|
|
1797
|
-
function compoundGateInstructionForMode(mode) {
|
|
1798
|
-
return mode === "lite" ? liteCompoundGateInstruction : compoundGateInstruction;
|
|
1799
|
-
}
|
|
1800
|
-
function formatHostHookOutput(host, event, decision) {
|
|
1801
|
-
if (decision.action === "allow") {
|
|
1802
|
-
return {};
|
|
1803
|
-
}
|
|
1804
|
-
if (host === "codex") {
|
|
1805
|
-
if (event === "Stop" || event === "SubagentStop") {
|
|
1806
|
-
return { decision: "block", reason: decision.reason };
|
|
1807
|
-
}
|
|
1808
|
-
if (decision.action === "block") {
|
|
1809
|
-
return {
|
|
1810
|
-
hookSpecificOutput: {
|
|
1811
|
-
hookEventName: event,
|
|
1812
|
-
permissionDecision: "deny",
|
|
1813
|
-
permissionDecisionReason: decision.reason
|
|
1814
|
-
},
|
|
1815
|
-
systemMessage: decision.reason
|
|
1816
|
-
};
|
|
1817
|
-
}
|
|
1818
|
-
return { systemMessage: decision.reason };
|
|
1819
|
-
}
|
|
1820
|
-
if (host === "claude" || host === "claude-code") {
|
|
1821
|
-
if (event === "Stop" || event === "SubagentStop") {
|
|
1822
|
-
return { decision: "block", reason: decision.reason };
|
|
1823
|
-
}
|
|
1824
|
-
if (decision.action === "block") {
|
|
1825
|
-
return {
|
|
1826
|
-
hookSpecificOutput: {
|
|
1827
|
-
hookEventName: event,
|
|
1828
|
-
permissionDecision: "deny",
|
|
1829
|
-
permissionDecisionReason: decision.reason
|
|
1830
|
-
}
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
return { systemMessage: decision.reason };
|
|
1834
|
-
}
|
|
1835
|
-
if (host === "antigravity") {
|
|
1836
|
-
if (event === "Stop" || event === "SubagentStop") {
|
|
1837
|
-
return { decision: "continue", reason: decision.reason };
|
|
1838
|
-
}
|
|
1839
|
-
if (decision.action === "block") {
|
|
1840
|
-
return { decision: "deny", reason: decision.reason };
|
|
1841
|
-
}
|
|
1842
|
-
return { decision: "allow", reason: decision.reason };
|
|
1843
|
-
}
|
|
1844
|
-
return {};
|
|
1845
|
-
}
|
|
1846
|
-
function normalizeStopInput(input) {
|
|
1847
|
-
if (!isRecord(input)) {
|
|
1848
|
-
return { lastAssistantMessage: void 0, stopHookActive: false };
|
|
1849
|
-
}
|
|
1850
|
-
return {
|
|
1851
|
-
lastAssistantMessage: findString(input, ["last_assistant_message", "lastAssistantMessage", "message", "text"]) ?? findLastAssistantMessageFromTranscript(findString(input, ["agent_transcript_path", "agentTranscriptPath"])) ?? findLastAssistantMessageFromTranscript(findString(input, ["transcript_path", "transcriptPath"])),
|
|
1852
|
-
stopHookActive: findBoolean(input, ["stop_hook_active", "stopHookActive"]) ?? false
|
|
1853
|
-
};
|
|
1854
|
-
}
|
|
1855
|
-
function looksLikeCompletedEngineeringWork(message) {
|
|
1856
|
-
if (negativeCompletionSignalPatterns.some((pattern) => pattern.test(message))) {
|
|
1857
|
-
return false;
|
|
1858
|
-
}
|
|
1859
|
-
return completionSignalPatterns.some((pattern) => pattern.test(message));
|
|
1860
|
-
}
|
|
1861
|
-
function findString(input, keys) {
|
|
1862
|
-
for (const key of keys) {
|
|
1863
|
-
const value = input[key];
|
|
1864
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
1865
|
-
return value;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
return void 0;
|
|
1869
|
-
}
|
|
1870
|
-
function findBoolean(input, keys) {
|
|
1871
|
-
for (const key of keys) {
|
|
1872
|
-
const value = input[key];
|
|
1873
|
-
if (typeof value === "boolean") {
|
|
1874
|
-
return value;
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
return void 0;
|
|
1878
|
-
}
|
|
1879
|
-
function findLastAssistantMessageFromTranscript(path) {
|
|
1880
|
-
if (!path || path.startsWith("~/")) {
|
|
1881
|
-
return void 0;
|
|
1882
|
-
}
|
|
1883
|
-
let content;
|
|
1884
|
-
try {
|
|
1885
|
-
content = readTranscriptTail(path);
|
|
1886
|
-
} catch {
|
|
1887
|
-
return void 0;
|
|
1888
|
-
}
|
|
1889
|
-
const lines = content.split(/\r?\n/);
|
|
1890
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1891
|
-
const line = lines[index]?.trim();
|
|
1892
|
-
if (!line) {
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
let record;
|
|
1896
|
-
try {
|
|
1897
|
-
record = JSON.parse(line);
|
|
1898
|
-
} catch {
|
|
1899
|
-
continue;
|
|
1900
|
-
}
|
|
1901
|
-
const message = extractAssistantText(record);
|
|
1902
|
-
if (message) {
|
|
1903
|
-
return message;
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
return void 0;
|
|
1907
|
-
}
|
|
1908
|
-
function readTranscriptTail(path) {
|
|
1909
|
-
const size = statSync3(path).size;
|
|
1910
|
-
if (size <= maxTranscriptBytes) {
|
|
1911
|
-
return readFileSync9(path, "utf8");
|
|
1912
|
-
}
|
|
1913
|
-
const fd = openSync(path, "r");
|
|
1914
|
-
try {
|
|
1915
|
-
const buffer = Buffer.allocUnsafe(maxTranscriptBytes);
|
|
1916
|
-
readSync(fd, buffer, 0, maxTranscriptBytes, size - maxTranscriptBytes);
|
|
1917
|
-
return buffer.toString("utf8");
|
|
1918
|
-
} finally {
|
|
1919
|
-
closeSync(fd);
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
function extractAssistantText(value) {
|
|
1923
|
-
if (!isRecord(value)) {
|
|
1924
|
-
return void 0;
|
|
1925
|
-
}
|
|
1926
|
-
const nested = value.message;
|
|
1927
|
-
const role = findString(value, ["role", "type"]);
|
|
1928
|
-
if (isRecord(nested)) {
|
|
1929
|
-
const nestedRole = findString(nested, ["role", "type"]);
|
|
1930
|
-
if (nestedRole === "assistant") {
|
|
1931
|
-
return extractTextContent(nested.content);
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
if (role === "assistant") {
|
|
1935
|
-
return extractTextContent(value.content) ?? extractTextContent(value.text);
|
|
1936
|
-
}
|
|
1937
|
-
return void 0;
|
|
1938
|
-
}
|
|
1939
|
-
function extractTextContent(value) {
|
|
1940
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
1941
|
-
return value;
|
|
1942
|
-
}
|
|
1943
|
-
if (!Array.isArray(value)) {
|
|
1944
|
-
return void 0;
|
|
1945
|
-
}
|
|
1946
|
-
const parts = [];
|
|
1947
|
-
for (const item of value) {
|
|
1948
|
-
if (typeof item === "string") {
|
|
1949
|
-
parts.push(item);
|
|
1950
|
-
continue;
|
|
1951
|
-
}
|
|
1952
|
-
if (isRecord(item) && typeof item.text === "string") {
|
|
1953
|
-
parts.push(item.text);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
const text = parts.join("\n").trim();
|
|
1957
|
-
return text.length > 0 ? text : void 0;
|
|
1958
|
-
}
|
|
1959
|
-
function isRecord(value) {
|
|
1960
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
// src/host-hooks/types.ts
|
|
1964
|
-
function isHostHookHost(value) {
|
|
1965
|
-
return value === "antigravity" || value === "claude" || value === "claude-code" || value === "codex";
|
|
1966
|
-
}
|
|
1967
|
-
function isHostHookEvent(value) {
|
|
1968
|
-
return value === "PostToolUse" || value === "PreToolUse" || value === "Stop" || value === "SubagentStop" || value === "UserPromptSubmit";
|
|
1969
|
-
}
|
|
1970
|
-
function isCompoundGateMode(value) {
|
|
1971
|
-
return value === "off" || value === "lite" || value === "strict";
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
// src/commands/host-hook.ts
|
|
1975
|
-
var defaultStdinTimeoutMs = 750;
|
|
1976
|
-
var maxDebugRawInputBytes = 256 * 1024;
|
|
1977
|
-
async function runHostHook(args) {
|
|
1978
|
-
const host = readOption(args, "--host");
|
|
1979
|
-
const event = readOption(args, "--event");
|
|
1980
|
-
const compoundGateMode = readCompoundGateMode(args);
|
|
1981
|
-
if (!isHostHookHost(host)) {
|
|
1982
|
-
console.error("Expected --host <codex|claude-code|antigravity>");
|
|
1983
|
-
return 1;
|
|
1984
|
-
}
|
|
1985
|
-
if (!isHostHookEvent(event)) {
|
|
1986
|
-
console.error("Expected --event <Stop|SubagentStop|PreToolUse|PostToolUse|UserPromptSubmit>");
|
|
1987
|
-
return 1;
|
|
1988
|
-
}
|
|
1989
|
-
const rawInput = await readStdinText(defaultStdinTimeoutMs);
|
|
1990
|
-
const input = parseStdinJson(rawInput);
|
|
1991
|
-
const decision = evaluateHostHook({ compoundGateMode, host, event, input });
|
|
1992
|
-
const output = formatHostHookOutput(host, event, decision);
|
|
1993
|
-
writeDebugLogIfRequested(args, {
|
|
1994
|
-
compoundGateMode,
|
|
1995
|
-
decision,
|
|
1996
|
-
event,
|
|
1997
|
-
host,
|
|
1998
|
-
input,
|
|
1999
|
-
output,
|
|
2000
|
-
rawInput
|
|
2001
|
-
});
|
|
2002
|
-
console.log(`${JSON.stringify(output)}
|
|
2003
|
-
`);
|
|
2004
|
-
return 0;
|
|
2005
|
-
}
|
|
2006
|
-
function readCompoundGateMode(args) {
|
|
2007
|
-
const value = readOption(args, "--compound-gate-mode") ?? process.env.PGS_COMPOUND_GATE_MODE ?? "off";
|
|
2008
|
-
if (isCompoundGateMode(value)) return value;
|
|
2009
|
-
return "off";
|
|
2010
|
-
}
|
|
2011
|
-
function readOption(args, name) {
|
|
2012
|
-
const index = args.indexOf(name);
|
|
2013
|
-
if (index < 0) return void 0;
|
|
2014
|
-
return args[index + 1];
|
|
2015
|
-
}
|
|
2016
|
-
function parseStdinJson(raw) {
|
|
2017
|
-
if (!raw) return {};
|
|
2018
|
-
try {
|
|
2019
|
-
return JSON.parse(raw);
|
|
2020
|
-
} catch {
|
|
2021
|
-
return {};
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
function readStdinText(timeoutMs) {
|
|
2025
|
-
if (process.stdin.isTTY) {
|
|
2026
|
-
return Promise.resolve("");
|
|
2027
|
-
}
|
|
2028
|
-
process.stdin.setEncoding("utf8");
|
|
2029
|
-
return new Promise((resolveText) => {
|
|
2030
|
-
let settled = false;
|
|
2031
|
-
let content = "";
|
|
2032
|
-
const settle = () => {
|
|
2033
|
-
if (settled) return;
|
|
2034
|
-
settled = true;
|
|
2035
|
-
clearTimeout(timer);
|
|
2036
|
-
process.stdin.off("data", onData);
|
|
2037
|
-
process.stdin.off("end", settle);
|
|
2038
|
-
process.stdin.off("error", settle);
|
|
2039
|
-
process.stdin.pause();
|
|
2040
|
-
resolveText(content.trim());
|
|
2041
|
-
};
|
|
2042
|
-
const onData = (chunk) => {
|
|
2043
|
-
content += chunk.toString();
|
|
2044
|
-
};
|
|
2045
|
-
const timer = setTimeout(settle, timeoutMs);
|
|
2046
|
-
timer.unref();
|
|
2047
|
-
process.stdin.on("data", onData);
|
|
2048
|
-
process.stdin.on("end", settle);
|
|
2049
|
-
process.stdin.on("error", settle);
|
|
2050
|
-
process.stdin.resume();
|
|
2051
|
-
});
|
|
2052
|
-
}
|
|
2053
|
-
function writeDebugLogIfRequested(args, record) {
|
|
2054
|
-
const explicitPath = readOption(args, "--debug-log");
|
|
2055
|
-
const enabled = explicitPath !== void 0 || process.env.PGS_HOST_HOOK_DEBUG === "1";
|
|
2056
|
-
if (!enabled) return;
|
|
2057
|
-
const debugDir = explicitPath && explicitPath.trim().length > 0 ? explicitPath : defaultDebugDir();
|
|
2058
|
-
try {
|
|
2059
|
-
mkdirSync4(debugDir, { recursive: true });
|
|
2060
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2061
|
-
const fileName = `${timestamp}-${process.pid}-${record.host}-${record.event}.json`;
|
|
2062
|
-
writeFileSync3(
|
|
2063
|
-
join13(debugDir, fileName),
|
|
2064
|
-
`${JSON.stringify(
|
|
2065
|
-
{
|
|
2066
|
-
schemaVersion: 1,
|
|
2067
|
-
cwd: process.cwd(),
|
|
2068
|
-
compoundGateMode: record.compoundGateMode,
|
|
2069
|
-
event: record.event,
|
|
2070
|
-
host: record.host,
|
|
2071
|
-
nodeVersion: process.version,
|
|
2072
|
-
packageVersion: readPackageVersion(),
|
|
2073
|
-
rawInput: truncateDebugRawInput(record.rawInput),
|
|
2074
|
-
input: record.input,
|
|
2075
|
-
decision: record.decision,
|
|
2076
|
-
output: record.output
|
|
2077
|
-
},
|
|
2078
|
-
null,
|
|
2079
|
-
2
|
|
2080
|
-
)}
|
|
2081
|
-
`,
|
|
2082
|
-
"utf8"
|
|
2083
|
-
);
|
|
2084
|
-
} catch {
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
function defaultDebugDir() {
|
|
2088
|
-
const gitPath = spawnSync3("git", ["rev-parse", "--git-path", "pro-gov-hook-debug"], {
|
|
2089
|
-
encoding: "utf8",
|
|
2090
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
2091
|
-
});
|
|
2092
|
-
const value = gitPath.status === 0 ? gitPath.stdout.trim() : "";
|
|
2093
|
-
if (value) {
|
|
2094
|
-
return resolve3(process.cwd(), value);
|
|
2095
|
-
}
|
|
2096
|
-
return join13(tmpdir2(), "pro-gov-hook-debug", basename3(process.cwd()));
|
|
2097
|
-
}
|
|
2098
|
-
function readPackageVersion() {
|
|
2099
|
-
const packageJsonPath = resolvePackageJsonPath();
|
|
2100
|
-
if (!packageJsonPath) return void 0;
|
|
2101
|
-
try {
|
|
2102
|
-
const parsed = JSON.parse(readFileSync10(packageJsonPath, "utf8"));
|
|
2103
|
-
return typeof parsed.version === "string" ? parsed.version : void 0;
|
|
2104
|
-
} catch {
|
|
2105
|
-
return void 0;
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
function resolvePackageJsonPath() {
|
|
2109
|
-
const candidates = [
|
|
2110
|
-
resolve3(process.cwd(), "packages/pro-gov/package.json"),
|
|
2111
|
-
resolve3(process.cwd(), "node_modules/@pieai/pro-gov/package.json")
|
|
2112
|
-
];
|
|
2113
|
-
return candidates.find((candidate) => existsSync13(candidate));
|
|
2114
|
-
}
|
|
2115
|
-
function truncateDebugRawInput(value) {
|
|
2116
|
-
const buffer = Buffer.from(value);
|
|
2117
|
-
if (buffer.byteLength <= maxDebugRawInputBytes) return value;
|
|
2118
|
-
return `${buffer.subarray(0, maxDebugRawInputBytes).toString("utf8")}
|
|
2119
|
-
[truncated]`;
|
|
2120
|
-
}
|
|
2121
1699
|
|
|
2122
1700
|
// src/commands/init.ts
|
|
2123
|
-
import { existsSync as
|
|
2124
|
-
import { basename as
|
|
1701
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1702
|
+
import { basename as basename3, dirname as dirname7, join as join13 } from "node:path";
|
|
2125
1703
|
|
|
2126
1704
|
// src/commands/shared.ts
|
|
2127
1705
|
function planStarterFiles(profile) {
|
|
@@ -2129,7 +1707,6 @@ function planStarterFiles(profile) {
|
|
|
2129
1707
|
const targetPath = starterTargetPath(asset.path);
|
|
2130
1708
|
if (!targetPath) return [];
|
|
2131
1709
|
if (profile && isOtherProfileRouting(targetPath, profile)) return [];
|
|
2132
|
-
if (profile && isEngineeringOnlyGuardrail(targetPath) && profile !== "engineering-runtime") return [];
|
|
2133
1710
|
return [
|
|
2134
1711
|
{
|
|
2135
1712
|
sourcePath: asset.path,
|
|
@@ -2152,12 +1729,6 @@ function classifyOwnership(targetPath) {
|
|
|
2152
1729
|
function isOtherProfileRouting(targetPath, profile) {
|
|
2153
1730
|
return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v0.9.md`;
|
|
2154
1731
|
}
|
|
2155
|
-
function isHostHookTargetPath(targetPath) {
|
|
2156
|
-
return targetPath === ".codex/hooks.json" || targetPath === ".claude/settings.json" || targetPath === ".agents/hooks.json";
|
|
2157
|
-
}
|
|
2158
|
-
function isEngineeringOnlyGuardrail(targetPath) {
|
|
2159
|
-
return isHostHookTargetPath(targetPath);
|
|
2160
|
-
}
|
|
2161
1732
|
function starterTargetPath(sourcePath) {
|
|
2162
1733
|
if (sourcePath === "starter/AGENTS.template.md") return "AGENTS.md";
|
|
2163
1734
|
if (sourcePath === "starter/CLAUDE.template.md") return "CLAUDE.md";
|
|
@@ -2196,7 +1767,7 @@ function runInit(args) {
|
|
|
2196
1767
|
}
|
|
2197
1768
|
function applyStarterFiles(files, profile) {
|
|
2198
1769
|
const root = process.cwd();
|
|
2199
|
-
const conflicts = files.filter((file) =>
|
|
1770
|
+
const conflicts = files.filter((file) => existsSync13(join13(root, file.targetPath)));
|
|
2200
1771
|
if (conflicts.length > 0) {
|
|
2201
1772
|
console.error("pro-gov init is refusing to overwrite existing project files:");
|
|
2202
1773
|
for (const file of conflicts) console.error(` ${file.targetPath}`);
|
|
@@ -2204,11 +1775,11 @@ function applyStarterFiles(files, profile) {
|
|
|
2204
1775
|
return 1;
|
|
2205
1776
|
}
|
|
2206
1777
|
for (const file of files) {
|
|
2207
|
-
const targetPath =
|
|
2208
|
-
|
|
2209
|
-
const source =
|
|
2210
|
-
const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"),
|
|
2211
|
-
|
|
1778
|
+
const targetPath = join13(root, file.targetPath);
|
|
1779
|
+
mkdirSync4(dirname7(targetPath), { recursive: true });
|
|
1780
|
+
const source = readFileSync8(file.absoluteSourcePath);
|
|
1781
|
+
const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
|
|
1782
|
+
writeFileSync3(targetPath, content);
|
|
2212
1783
|
}
|
|
2213
1784
|
console.log("pro-gov init APPLIED");
|
|
2214
1785
|
console.log(`profile: ${profile}`);
|
|
@@ -2236,8 +1807,8 @@ function readFlag(args, flag) {
|
|
|
2236
1807
|
}
|
|
2237
1808
|
|
|
2238
1809
|
// src/learning/recall.ts
|
|
2239
|
-
import { existsSync as
|
|
2240
|
-
import { basename as
|
|
1810
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "node:fs";
|
|
1811
|
+
import { basename as basename4, join as join14, relative as relative5 } from "node:path";
|
|
2241
1812
|
function recallLearnings(root, options) {
|
|
2242
1813
|
const query = options.query.trim();
|
|
2243
1814
|
const terms = tokenize(query);
|
|
@@ -2259,14 +1830,14 @@ function recallLearnings(root, options) {
|
|
|
2259
1830
|
}
|
|
2260
1831
|
function loadLearningRecords(root) {
|
|
2261
1832
|
const records = [];
|
|
2262
|
-
const solutionsDir =
|
|
2263
|
-
if (
|
|
1833
|
+
const solutionsDir = join14(root, "docs/solutions");
|
|
1834
|
+
if (existsSync14(solutionsDir)) {
|
|
2264
1835
|
for (const path of listMarkdownFiles(solutionsDir)) {
|
|
2265
1836
|
records.push(readLearningRecord(root, path));
|
|
2266
1837
|
}
|
|
2267
1838
|
}
|
|
2268
|
-
const conceptsPath =
|
|
2269
|
-
if (
|
|
1839
|
+
const conceptsPath = join14(root, "CONCEPTS.md");
|
|
1840
|
+
if (existsSync14(conceptsPath)) {
|
|
2270
1841
|
records.push(readLearningRecord(root, conceptsPath));
|
|
2271
1842
|
}
|
|
2272
1843
|
return records;
|
|
@@ -2274,7 +1845,7 @@ function loadLearningRecords(root) {
|
|
|
2274
1845
|
function listMarkdownFiles(dir) {
|
|
2275
1846
|
const files = [];
|
|
2276
1847
|
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
2277
|
-
const absolutePath =
|
|
1848
|
+
const absolutePath = join14(dir, entry.name);
|
|
2278
1849
|
if (entry.isDirectory()) {
|
|
2279
1850
|
files.push(...listMarkdownFiles(absolutePath));
|
|
2280
1851
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -2284,7 +1855,7 @@ function listMarkdownFiles(dir) {
|
|
|
2284
1855
|
return files.sort();
|
|
2285
1856
|
}
|
|
2286
1857
|
function readLearningRecord(root, absolutePath) {
|
|
2287
|
-
const content =
|
|
1858
|
+
const content = readFileSync9(absolutePath, "utf8");
|
|
2288
1859
|
const parsed = splitFrontmatter(content);
|
|
2289
1860
|
const body = parsed.body;
|
|
2290
1861
|
return {
|
|
@@ -2316,7 +1887,7 @@ function findTitle(frontmatter, body) {
|
|
|
2316
1887
|
return heading ? heading.slice(2).trim() : void 0;
|
|
2317
1888
|
}
|
|
2318
1889
|
function titleFromPath(path) {
|
|
2319
|
-
return
|
|
1890
|
+
return basename4(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2320
1891
|
}
|
|
2321
1892
|
function scoreRecord(record, terms) {
|
|
2322
1893
|
const title = record.title.toLowerCase();
|
|
@@ -2367,92 +1938,55 @@ function cleanMarkdownLine(input) {
|
|
|
2367
1938
|
}
|
|
2368
1939
|
|
|
2369
1940
|
// src/learning/capture.ts
|
|
2370
|
-
import { existsSync as
|
|
2371
|
-
import { join as
|
|
2372
|
-
function
|
|
1941
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1942
|
+
import { join as join15, relative as relative6 } from "node:path";
|
|
1943
|
+
function captureLearning(root, options) {
|
|
2373
1944
|
const title = options.title.trim();
|
|
2374
1945
|
const summary = options.summary.trim();
|
|
2375
1946
|
if (!title) throw new Error("title is required");
|
|
2376
1947
|
if (!summary) throw new Error("summary is required");
|
|
2377
1948
|
const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
|
|
2378
|
-
const moduleName = options.module?.trim() || "PGS
|
|
2379
|
-
const dir =
|
|
2380
|
-
|
|
2381
|
-
const
|
|
2382
|
-
|
|
2383
|
-
const content = renderFallbackLearning({
|
|
2384
|
-
title,
|
|
2385
|
-
summary,
|
|
2386
|
-
category,
|
|
2387
|
-
moduleName
|
|
2388
|
-
});
|
|
2389
|
-
writeFileSync5(path, content);
|
|
1949
|
+
const moduleName = options.module?.trim() || "PGS learning capture";
|
|
1950
|
+
const dir = join15(root, "docs/solutions", category);
|
|
1951
|
+
mkdirSync5(dir, { recursive: true });
|
|
1952
|
+
const path = uniquePath(dir, slugify(title) || "learning");
|
|
1953
|
+
writeFileSync4(path, renderLearning({ title, summary, category, moduleName }));
|
|
2390
1954
|
return {
|
|
2391
1955
|
relativePath: normalizePath2(relative6(root, path)),
|
|
2392
1956
|
title,
|
|
2393
|
-
captureMode: "pgs-
|
|
1957
|
+
captureMode: "pgs-native"
|
|
2394
1958
|
};
|
|
2395
1959
|
}
|
|
2396
|
-
function
|
|
2397
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2398
|
-
const problemType = options.category === "workflow-issues" ? "workflow_issue" : "knowledge";
|
|
1960
|
+
function renderLearning(options) {
|
|
2399
1961
|
return [
|
|
2400
1962
|
"---",
|
|
2401
1963
|
`title: ${yamlString(options.title)}`,
|
|
2402
|
-
`date: ${
|
|
1964
|
+
`date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
|
|
2403
1965
|
`category: ${options.category}`,
|
|
2404
1966
|
`module: ${yamlString(options.moduleName)}`,
|
|
2405
|
-
|
|
2406
|
-
"
|
|
2407
|
-
"severity: medium",
|
|
2408
|
-
"capture_mode: pgs-fallback",
|
|
2409
|
-
"applies_when:",
|
|
2410
|
-
' - "The Compound Engineering plugin or ce-compound skill is unavailable in the current host"',
|
|
2411
|
-
' - "Completed work produced reusable learning that should be recallable later"',
|
|
2412
|
-
"tags: [compound-gate, fallback-capture, learning-recall]",
|
|
1967
|
+
"capture_mode: pgs-native",
|
|
1968
|
+
"tags: [learning-recall]",
|
|
2413
1969
|
"---",
|
|
2414
1970
|
"",
|
|
2415
1971
|
`# ${options.title}`,
|
|
2416
1972
|
"",
|
|
2417
|
-
"## Context",
|
|
2418
|
-
"",
|
|
2419
|
-
"This record was captured with the PGS fallback capture path because the full",
|
|
2420
|
-
"Compound Engineering `ce-compound` workflow was unavailable in the current",
|
|
2421
|
-
"host. Prefer `ce-compound` when the plugin is installed; use this fallback",
|
|
2422
|
-
"only to avoid losing reusable learning.",
|
|
2423
|
-
"",
|
|
2424
1973
|
"## Guidance",
|
|
2425
1974
|
"",
|
|
2426
1975
|
options.summary,
|
|
2427
1976
|
"",
|
|
2428
|
-
"##
|
|
2429
|
-
"",
|
|
2430
|
-
"A Compound Gate that cannot write a learning record turns reusable experience",
|
|
2431
|
-
"into a final-report sentence that future agents cannot reliably find. The",
|
|
2432
|
-
"fallback keeps the learning searchable by `pro-gov learn recall` without",
|
|
2433
|
-
"copying or replacing the Compound Engineering workflow.",
|
|
2434
|
-
"",
|
|
2435
|
-
"## When to Apply",
|
|
1977
|
+
"## Applies When",
|
|
2436
1978
|
"",
|
|
2437
1979
|
"- The work is complete and verified.",
|
|
2438
|
-
"- The lesson is reusable
|
|
2439
|
-
"- `compound-engineering:ce-compound` is not available in the current host.",
|
|
2440
|
-
"- The learning is not already covered by an existing `docs/solutions/**` record.",
|
|
2441
|
-
"",
|
|
2442
|
-
"## Examples",
|
|
2443
|
-
"",
|
|
2444
|
-
"```text",
|
|
2445
|
-
"Compound Gate: ran fallback capture -> docs/solutions/<category>/<file>.md",
|
|
2446
|
-
"```",
|
|
1980
|
+
"- The lesson is non-obvious, reusable, and not already documented.",
|
|
2447
1981
|
""
|
|
2448
1982
|
].join("\n");
|
|
2449
1983
|
}
|
|
2450
1984
|
function uniquePath(dir, slug) {
|
|
2451
1985
|
let index = 1;
|
|
2452
|
-
let candidate =
|
|
2453
|
-
while (
|
|
1986
|
+
let candidate = join15(dir, `${slug}.md`);
|
|
1987
|
+
while (existsSync15(candidate)) {
|
|
2454
1988
|
index += 1;
|
|
2455
|
-
candidate =
|
|
1989
|
+
candidate = join15(dir, `${slug}-${index}.md`);
|
|
2456
1990
|
}
|
|
2457
1991
|
return candidate;
|
|
2458
1992
|
}
|
|
@@ -2508,7 +2042,7 @@ function runLearnCapture(args) {
|
|
|
2508
2042
|
}
|
|
2509
2043
|
let result;
|
|
2510
2044
|
try {
|
|
2511
|
-
result =
|
|
2045
|
+
result = captureLearning(options.value.targetDir, {
|
|
2512
2046
|
title: options.value.title,
|
|
2513
2047
|
summary: options.value.summary,
|
|
2514
2048
|
category: options.value.category,
|
|
@@ -2629,20 +2163,18 @@ function printUsage2() {
|
|
|
2629
2163
|
}
|
|
2630
2164
|
|
|
2631
2165
|
// src/commands/lens.ts
|
|
2632
|
-
import { mkdirSync as
|
|
2166
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2633
2167
|
import { dirname as dirname9 } from "node:path";
|
|
2634
2168
|
|
|
2635
2169
|
// src/lens/audit.ts
|
|
2636
|
-
import { existsSync as
|
|
2637
|
-
import { basename as
|
|
2170
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2171
|
+
import { basename as basename5, dirname as dirname8, join as join16 } from "node:path";
|
|
2638
2172
|
var REQUIRED_ARTIFACTS = [
|
|
2639
2173
|
"manifest.md",
|
|
2640
2174
|
"raw/project-lens/architecture-lens.md",
|
|
2641
2175
|
"raw/project-lens/truth-surface-audit.md",
|
|
2642
2176
|
"raw/project-lens/technology-strategy.md",
|
|
2643
|
-
"raw/
|
|
2644
|
-
"raw/ponytail/ponytail-debt.md",
|
|
2645
|
-
"raw/ponytail/ponytail-gain.md",
|
|
2177
|
+
"raw/project-lens/simplicity-review.md",
|
|
2646
2178
|
"raw/target/target-state.md",
|
|
2647
2179
|
"raw/target/commands.md",
|
|
2648
2180
|
"raw/target/sources.md",
|
|
@@ -2654,20 +2186,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
|
|
|
2654
2186
|
version: 1,
|
|
2655
2187
|
target: {
|
|
2656
2188
|
path: targetDir,
|
|
2657
|
-
name:
|
|
2189
|
+
name: basename5(targetDir) || "target"
|
|
2658
2190
|
},
|
|
2659
2191
|
requiredArtifacts: [...REQUIRED_ARTIFACTS]
|
|
2660
2192
|
};
|
|
2661
|
-
|
|
2662
|
-
writeJson(
|
|
2193
|
+
mkdirSync6(auditDir, { recursive: true });
|
|
2194
|
+
writeJson(join16(auditDir, "audit.contract.json"), contract);
|
|
2663
2195
|
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
2664
|
-
writeTemplate(
|
|
2196
|
+
writeTemplate(join16(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
|
|
2665
2197
|
}
|
|
2666
2198
|
return contract;
|
|
2667
2199
|
}
|
|
2668
2200
|
function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
2669
|
-
const contractPath =
|
|
2670
|
-
if (!
|
|
2201
|
+
const contractPath = join16(auditDir, "audit.contract.json");
|
|
2202
|
+
if (!existsSync16(contractPath)) {
|
|
2671
2203
|
return {
|
|
2672
2204
|
ok: false,
|
|
2673
2205
|
auditDir,
|
|
@@ -2681,7 +2213,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
|
2681
2213
|
}
|
|
2682
2214
|
let contract;
|
|
2683
2215
|
try {
|
|
2684
|
-
contract = JSON.parse(
|
|
2216
|
+
contract = JSON.parse(readFileSync10(contractPath, "utf8"));
|
|
2685
2217
|
} catch (error) {
|
|
2686
2218
|
return {
|
|
2687
2219
|
ok: false,
|
|
@@ -2714,12 +2246,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
|
2714
2246
|
}
|
|
2715
2247
|
}
|
|
2716
2248
|
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
2717
|
-
const absolutePath =
|
|
2718
|
-
if (!
|
|
2249
|
+
const absolutePath = join16(auditDir, artifactPath);
|
|
2250
|
+
if (!existsSync16(absolutePath)) {
|
|
2719
2251
|
issues.push({ type: "missing-required-artifact", path: artifactPath });
|
|
2720
2252
|
continue;
|
|
2721
2253
|
}
|
|
2722
|
-
const content =
|
|
2254
|
+
const content = readFileSync10(absolutePath, "utf8");
|
|
2723
2255
|
if (isPendingArtifact(content)) {
|
|
2724
2256
|
issues.push({ type: "artifact-not-complete", path: artifactPath });
|
|
2725
2257
|
} else if (hasTemplateBody(content)) {
|
|
@@ -2735,13 +2267,13 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
|
2735
2267
|
};
|
|
2736
2268
|
}
|
|
2737
2269
|
function writeJson(path, value) {
|
|
2738
|
-
|
|
2739
|
-
|
|
2270
|
+
mkdirSync6(dirname8(path), { recursive: true });
|
|
2271
|
+
writeFileSync5(path, `${JSON.stringify(value, null, 2)}
|
|
2740
2272
|
`);
|
|
2741
2273
|
}
|
|
2742
2274
|
function writeTemplate(path, content) {
|
|
2743
|
-
|
|
2744
|
-
|
|
2275
|
+
mkdirSync6(dirname8(path), { recursive: true });
|
|
2276
|
+
writeFileSync5(path, content);
|
|
2745
2277
|
}
|
|
2746
2278
|
function renderArtifactTemplate(artifactPath, contract) {
|
|
2747
2279
|
const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
|
|
@@ -2797,7 +2329,6 @@ function renderArtifactGuardrailHint(artifactPath) {
|
|
|
2797
2329
|
}
|
|
2798
2330
|
function artifactProducer(artifactPath) {
|
|
2799
2331
|
if (artifactPath.startsWith("raw/project-lens/")) return "project-lens";
|
|
2800
|
-
if (artifactPath.startsWith("raw/ponytail/")) return "ponytail";
|
|
2801
2332
|
if (artifactPath.startsWith("raw/target/")) return "target-evidence";
|
|
2802
2333
|
if (artifactPath.startsWith("synthesis/")) return "synthesis";
|
|
2803
2334
|
return "audit";
|
|
@@ -2831,8 +2362,8 @@ var REQUIRED_METHOD_RECORDS = {
|
|
|
2831
2362
|
match: /^Project Lens method source:/im
|
|
2832
2363
|
},
|
|
2833
2364
|
{
|
|
2834
|
-
label: "
|
|
2835
|
-
match: /^
|
|
2365
|
+
label: "Simplicity review method source:",
|
|
2366
|
+
match: /^Simplicity review method source:/im
|
|
2836
2367
|
}
|
|
2837
2368
|
],
|
|
2838
2369
|
"synthesis/decision-index.md": [
|
|
@@ -2978,9 +2509,9 @@ function bulletList(values) {
|
|
|
2978
2509
|
}
|
|
2979
2510
|
|
|
2980
2511
|
// src/lens/scan.ts
|
|
2981
|
-
import { spawnSync as
|
|
2982
|
-
import { existsSync as
|
|
2983
|
-
import { join as
|
|
2512
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2513
|
+
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
|
|
2514
|
+
import { join as join17, relative as relative7 } from "node:path";
|
|
2984
2515
|
var ignoredDirectories = /* @__PURE__ */ new Set([
|
|
2985
2516
|
".git",
|
|
2986
2517
|
".next",
|
|
@@ -2997,24 +2528,24 @@ function scanProjectLensTarget(targetDir, options = {}) {
|
|
|
2997
2528
|
return {
|
|
2998
2529
|
targetDir,
|
|
2999
2530
|
aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter(
|
|
3000
|
-
(file) =>
|
|
2531
|
+
(file) => existsSync17(join17(targetDir, file))
|
|
3001
2532
|
),
|
|
3002
2533
|
aiConfigFiles: [],
|
|
3003
2534
|
packageJson,
|
|
3004
2535
|
docs: {
|
|
3005
|
-
hasDocsDirectory:
|
|
2536
|
+
hasDocsDirectory: existsSync17(join17(targetDir, "docs")),
|
|
3006
2537
|
markdownFileCount: markdownFiles.length,
|
|
3007
2538
|
governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
|
|
3008
2539
|
},
|
|
3009
2540
|
git: readGitState(targetDir),
|
|
3010
|
-
largeFiles: files.map((file) => ({ path: file, bytes:
|
|
2541
|
+
largeFiles: files.map((file) => ({ path: file, bytes: statSync3(join17(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
|
|
3011
2542
|
};
|
|
3012
2543
|
}
|
|
3013
2544
|
function readPackageJson(targetDir) {
|
|
3014
|
-
const packageJsonPath =
|
|
3015
|
-
if (!
|
|
2545
|
+
const packageJsonPath = join17(targetDir, "package.json");
|
|
2546
|
+
if (!existsSync17(packageJsonPath)) return void 0;
|
|
3016
2547
|
try {
|
|
3017
|
-
const packageJson = JSON.parse(
|
|
2548
|
+
const packageJson = JSON.parse(readFileSync11(packageJsonPath, "utf8"));
|
|
3018
2549
|
return {
|
|
3019
2550
|
scripts: Object.keys(packageJson.scripts ?? {}).sort(),
|
|
3020
2551
|
dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
|
|
@@ -3037,7 +2568,7 @@ function readGitState(targetDir) {
|
|
|
3037
2568
|
};
|
|
3038
2569
|
}
|
|
3039
2570
|
function runGit(targetDir, args) {
|
|
3040
|
-
const result =
|
|
2571
|
+
const result = spawnSync3("git", ["-C", targetDir, ...args], {
|
|
3041
2572
|
encoding: "utf8"
|
|
3042
2573
|
});
|
|
3043
2574
|
if (result.status !== 0) return { ok: false };
|
|
@@ -3049,13 +2580,13 @@ function listProjectFiles(targetDir) {
|
|
|
3049
2580
|
return files.sort();
|
|
3050
2581
|
}
|
|
3051
2582
|
function collectFiles2(rootDir, currentDir, files) {
|
|
3052
|
-
if (!
|
|
2583
|
+
if (!existsSync17(currentDir)) return;
|
|
3053
2584
|
for (const entry of readdirSync7(currentDir, { withFileTypes: true })) {
|
|
3054
2585
|
if (entry.isDirectory()) {
|
|
3055
2586
|
if (ignoredDirectories.has(entry.name)) continue;
|
|
3056
|
-
collectFiles2(rootDir,
|
|
2587
|
+
collectFiles2(rootDir, join17(currentDir, entry.name), files);
|
|
3057
2588
|
} else if (entry.isFile()) {
|
|
3058
|
-
files.push(toUnixPath4(relative7(rootDir,
|
|
2589
|
+
files.push(toUnixPath4(relative7(rootDir, join17(currentDir, entry.name))));
|
|
3059
2590
|
}
|
|
3060
2591
|
}
|
|
3061
2592
|
}
|
|
@@ -3107,8 +2638,8 @@ function runLensReport(args) {
|
|
|
3107
2638
|
}
|
|
3108
2639
|
const report = scanProjectLensTarget(options.value.targetDir);
|
|
3109
2640
|
const markdown = renderProjectLensMarkdownReport(report);
|
|
3110
|
-
|
|
3111
|
-
|
|
2641
|
+
mkdirSync7(dirname9(options.value.outPath), { recursive: true });
|
|
2642
|
+
writeFileSync6(options.value.outPath, markdown);
|
|
3112
2643
|
console.log(`report: ${options.value.outPath}`);
|
|
3113
2644
|
return 0;
|
|
3114
2645
|
}
|
|
@@ -3211,16 +2742,16 @@ function printUsage3() {
|
|
|
3211
2742
|
}
|
|
3212
2743
|
|
|
3213
2744
|
// src/commands/portfolio.ts
|
|
3214
|
-
import { existsSync as
|
|
3215
|
-
import { join as
|
|
2745
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
2746
|
+
import { join as join20 } from "node:path";
|
|
3216
2747
|
|
|
3217
2748
|
// src/portfolio/manifest.ts
|
|
3218
|
-
import { existsSync as
|
|
3219
|
-
import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as
|
|
2749
|
+
import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
|
|
2750
|
+
import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
|
|
3220
2751
|
function loadPortfolioManifest(configPath) {
|
|
3221
2752
|
let parsed;
|
|
3222
2753
|
try {
|
|
3223
|
-
parsed = JSON.parse(
|
|
2754
|
+
parsed = JSON.parse(readFileSync12(configPath, "utf8"));
|
|
3224
2755
|
} catch (error) {
|
|
3225
2756
|
return {
|
|
3226
2757
|
configPath,
|
|
@@ -3232,7 +2763,7 @@ function loadPortfolioManifest(configPath) {
|
|
|
3232
2763
|
]
|
|
3233
2764
|
};
|
|
3234
2765
|
}
|
|
3235
|
-
const normalized = resolveManifestPaths(parsed, dirname10(
|
|
2766
|
+
const normalized = resolveManifestPaths(parsed, dirname10(resolve3(configPath)));
|
|
3236
2767
|
const issues = validatePortfolioManifest(normalized);
|
|
3237
2768
|
return {
|
|
3238
2769
|
configPath,
|
|
@@ -3241,12 +2772,12 @@ function loadPortfolioManifest(configPath) {
|
|
|
3241
2772
|
};
|
|
3242
2773
|
}
|
|
3243
2774
|
function resolveManifestPaths(value, configDir) {
|
|
3244
|
-
if (!
|
|
2775
|
+
if (!isRecord(value)) return value;
|
|
3245
2776
|
const resolveEndpoint = (endpoint) => {
|
|
3246
|
-
if (!
|
|
2777
|
+
if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
|
|
3247
2778
|
return endpoint;
|
|
3248
2779
|
}
|
|
3249
|
-
return { ...endpoint, path:
|
|
2780
|
+
return { ...endpoint, path: resolve3(configDir, endpoint.path) };
|
|
3250
2781
|
};
|
|
3251
2782
|
return {
|
|
3252
2783
|
...value,
|
|
@@ -3257,7 +2788,7 @@ function resolveManifestPaths(value, configDir) {
|
|
|
3257
2788
|
}
|
|
3258
2789
|
function validatePortfolioManifest(value) {
|
|
3259
2790
|
const issues = [];
|
|
3260
|
-
if (!
|
|
2791
|
+
if (!isRecord(value)) {
|
|
3261
2792
|
return [
|
|
3262
2793
|
{
|
|
3263
2794
|
type: "invalid-field",
|
|
@@ -3299,7 +2830,7 @@ function validatePortfolioManifest(value) {
|
|
|
3299
2830
|
}
|
|
3300
2831
|
const seenTargetIds = /* @__PURE__ */ new Set();
|
|
3301
2832
|
for (const target of value.targets) {
|
|
3302
|
-
if (!
|
|
2833
|
+
if (!isRecord(target)) {
|
|
3303
2834
|
issues.push({
|
|
3304
2835
|
type: "invalid-field",
|
|
3305
2836
|
field: "targets",
|
|
@@ -3356,7 +2887,7 @@ function validateAllowedFields(value, location, allowedFields, issues) {
|
|
|
3356
2887
|
}
|
|
3357
2888
|
function validateEndpoint(value, field, issues) {
|
|
3358
2889
|
if (value === void 0) return;
|
|
3359
|
-
if (!
|
|
2890
|
+
if (!isRecord(value)) {
|
|
3360
2891
|
issues.push({
|
|
3361
2892
|
type: "invalid-field",
|
|
3362
2893
|
field,
|
|
@@ -3380,7 +2911,7 @@ function validateEndpoint(value, field, issues) {
|
|
|
3380
2911
|
});
|
|
3381
2912
|
return;
|
|
3382
2913
|
}
|
|
3383
|
-
if (!
|
|
2914
|
+
if (!existsSync18(value.path)) {
|
|
3384
2915
|
issues.push({
|
|
3385
2916
|
type: "missing-path",
|
|
3386
2917
|
id: typeof value.id === "string" ? value.id : void 0,
|
|
@@ -3412,7 +2943,7 @@ function validateHostTooling(value, issues) {
|
|
|
3412
2943
|
}
|
|
3413
2944
|
const seenHosts = /* @__PURE__ */ new Set();
|
|
3414
2945
|
for (const entry of value) {
|
|
3415
|
-
if (!
|
|
2946
|
+
if (!isRecord(entry)) {
|
|
3416
2947
|
issues.push({
|
|
3417
2948
|
type: "invalid-field",
|
|
3418
2949
|
field: "hostTooling",
|
|
@@ -3445,19 +2976,19 @@ function validateHostTooling(value, issues) {
|
|
|
3445
2976
|
}
|
|
3446
2977
|
}
|
|
3447
2978
|
}
|
|
3448
|
-
function
|
|
2979
|
+
function isRecord(value) {
|
|
3449
2980
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3450
2981
|
}
|
|
3451
2982
|
|
|
3452
2983
|
// src/portfolio/doctor.ts
|
|
3453
|
-
import { spawnSync as
|
|
3454
|
-
import { existsSync as
|
|
2984
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
2985
|
+
import { existsSync as existsSync20, readFileSync as readFileSync14 } from "node:fs";
|
|
3455
2986
|
import { createRequire as createRequire2 } from "node:module";
|
|
3456
|
-
import { dirname as dirname11, join as
|
|
2987
|
+
import { dirname as dirname11, join as join19 } from "node:path";
|
|
3457
2988
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
3458
2989
|
|
|
3459
2990
|
// src/host-tooling/inventory.ts
|
|
3460
|
-
import { spawnSync as
|
|
2991
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
3461
2992
|
function inspectHostTooling(requirements, runner = defaultRunner2) {
|
|
3462
2993
|
const hosts = [];
|
|
3463
2994
|
const issues = [];
|
|
@@ -3515,10 +3046,10 @@ function inspectHostTooling(requirements, runner = defaultRunner2) {
|
|
|
3515
3046
|
return { hosts, issues };
|
|
3516
3047
|
}
|
|
3517
3048
|
function parseHostPlugins(host, value) {
|
|
3518
|
-
const entries = host === "codex" ?
|
|
3049
|
+
const entries = host === "codex" ? isRecord2(value) && Array.isArray(value.installed) ? value.installed : void 0 : Array.isArray(value) ? value : void 0;
|
|
3519
3050
|
if (!entries) throw new Error("expected a plugin array");
|
|
3520
3051
|
return entries.flatMap((entry) => {
|
|
3521
|
-
if (!
|
|
3052
|
+
if (!isRecord2(entry)) return [];
|
|
3522
3053
|
const id = host === "codex" ? entry.pluginId : entry.id;
|
|
3523
3054
|
if (typeof id !== "string") return [];
|
|
3524
3055
|
return [{
|
|
@@ -3529,7 +3060,7 @@ function parseHostPlugins(host, value) {
|
|
|
3529
3060
|
});
|
|
3530
3061
|
}
|
|
3531
3062
|
function defaultRunner2({ command: command2 }) {
|
|
3532
|
-
const result =
|
|
3063
|
+
const result = spawnSync4(command2[0] ?? "", command2.slice(1), {
|
|
3533
3064
|
encoding: "utf8",
|
|
3534
3065
|
timeout: 1e4
|
|
3535
3066
|
});
|
|
@@ -3539,18 +3070,18 @@ function defaultRunner2({ command: command2 }) {
|
|
|
3539
3070
|
stderr: result.stderr || result.error?.message || ""
|
|
3540
3071
|
};
|
|
3541
3072
|
}
|
|
3542
|
-
function
|
|
3073
|
+
function isRecord2(value) {
|
|
3543
3074
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3544
3075
|
}
|
|
3545
3076
|
|
|
3546
3077
|
// src/portfolio/asset-state.ts
|
|
3547
|
-
import { existsSync as
|
|
3548
|
-
import { join as
|
|
3078
|
+
import { existsSync as existsSync19, lstatSync as lstatSync5, readFileSync as readFileSync13 } from "node:fs";
|
|
3079
|
+
import { join as join18 } from "node:path";
|
|
3549
3080
|
function comparePortfolioAssetState(options) {
|
|
3550
3081
|
const expectedManifest = readPlanDocument(options.expectedPlan, ".pro-gov/assets.json");
|
|
3551
3082
|
const expectedLock = readPlanDocument(options.expectedPlan, ".pro-gov/assets.lock.json");
|
|
3552
|
-
const currentManifest = readJsonFile(
|
|
3553
|
-
const currentLock = readJsonFile(
|
|
3083
|
+
const currentManifest = readJsonFile(join18(options.targetDir, ".pro-gov/assets.json"));
|
|
3084
|
+
const currentLock = readJsonFile(join18(options.targetDir, ".pro-gov/assets.lock.json"));
|
|
3554
3085
|
const issues = [];
|
|
3555
3086
|
if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
|
|
3556
3087
|
issues.push({
|
|
@@ -3573,7 +3104,7 @@ function comparePortfolioAssetState(options) {
|
|
|
3573
3104
|
const expectedTargets = new Set((expectedLock?.assets ?? []).map((entry) => entry.targetPath));
|
|
3574
3105
|
for (const entry of currentLock?.assets ?? []) {
|
|
3575
3106
|
if (expectedTargets.has(entry.targetPath)) continue;
|
|
3576
|
-
const targetAbsolutePath =
|
|
3107
|
+
const targetAbsolutePath = join18(options.targetDir, entry.targetPath);
|
|
3577
3108
|
if (!pathIsSymlink(targetAbsolutePath)) continue;
|
|
3578
3109
|
issues.push({
|
|
3579
3110
|
type: "orphaned-managed-symlink",
|
|
@@ -3595,9 +3126,9 @@ function readPlanDocument(plan, targetPath) {
|
|
|
3595
3126
|
}
|
|
3596
3127
|
}
|
|
3597
3128
|
function readJsonFile(path) {
|
|
3598
|
-
if (!
|
|
3129
|
+
if (!existsSync19(path)) return void 0;
|
|
3599
3130
|
try {
|
|
3600
|
-
return JSON.parse(
|
|
3131
|
+
return JSON.parse(readFileSync13(path, "utf8"));
|
|
3601
3132
|
} catch {
|
|
3602
3133
|
return void 0;
|
|
3603
3134
|
}
|
|
@@ -3646,11 +3177,11 @@ function inspectPortfolio(options) {
|
|
|
3646
3177
|
function inspectTarget(options) {
|
|
3647
3178
|
const { target } = options;
|
|
3648
3179
|
const issues = [];
|
|
3649
|
-
const packageJson = readJson2(
|
|
3180
|
+
const packageJson = readJson2(join19(target.path, "package.json"));
|
|
3650
3181
|
const packages = {};
|
|
3651
3182
|
for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
|
|
3652
3183
|
const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
|
|
3653
|
-
const installedPackage = readJson2(
|
|
3184
|
+
const installedPackage = readJson2(join19(target.path, "node_modules", packageName, "package.json"));
|
|
3654
3185
|
const installed = installedPackage?.version;
|
|
3655
3186
|
const expected = options.expectedPackageVersions[packageName];
|
|
3656
3187
|
packages[packageName] = { declared, installed, expected };
|
|
@@ -3704,7 +3235,7 @@ function inspectTarget(options) {
|
|
|
3704
3235
|
type: "asset-lock-drift",
|
|
3705
3236
|
message: error instanceof Error ? error.message : String(error)
|
|
3706
3237
|
});
|
|
3707
|
-
if (!
|
|
3238
|
+
if (!existsSync20(join19(target.path, ".pro-gov/assets.json"))) {
|
|
3708
3239
|
issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
|
|
3709
3240
|
}
|
|
3710
3241
|
}
|
|
@@ -3719,27 +3250,27 @@ function inspectTarget(options) {
|
|
|
3719
3250
|
};
|
|
3720
3251
|
}
|
|
3721
3252
|
function readTargetAssetHost(targetDir) {
|
|
3722
|
-
const lockfile = readJson2(
|
|
3253
|
+
const lockfile = readJson2(join19(targetDir, ".pro-gov/assets.lock.json"));
|
|
3723
3254
|
return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
|
|
3724
3255
|
}
|
|
3725
3256
|
function isAssetRegistryHost(value) {
|
|
3726
3257
|
return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
|
|
3727
3258
|
}
|
|
3728
3259
|
function runTargetChecks(target) {
|
|
3729
|
-
const proGovCli =
|
|
3730
|
-
const docGovCli =
|
|
3260
|
+
const proGovCli = join19(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
|
|
3261
|
+
const docGovCli = join19(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
|
|
3731
3262
|
const commands = [
|
|
3732
3263
|
{
|
|
3733
3264
|
name: "pro-gov doctor",
|
|
3734
3265
|
cli: proGovCli,
|
|
3735
|
-
args:
|
|
3266
|
+
args: ["doctor"]
|
|
3736
3267
|
},
|
|
3737
3268
|
{ name: "doc-gov router-check", cli: docGovCli, args: ["router-check"] },
|
|
3738
3269
|
{ name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
|
|
3739
3270
|
];
|
|
3740
3271
|
return commands.map((command2) => {
|
|
3741
|
-
if (!
|
|
3742
|
-
const result =
|
|
3272
|
+
if (!existsSync20(command2.cli)) return { name: command2.name, status: null };
|
|
3273
|
+
const result = spawnSync5(process.execPath, [command2.cli, ...command2.args], {
|
|
3743
3274
|
cwd: target.path,
|
|
3744
3275
|
encoding: "utf8",
|
|
3745
3276
|
timeout: 3e4
|
|
@@ -3748,13 +3279,13 @@ function runTargetChecks(target) {
|
|
|
3748
3279
|
});
|
|
3749
3280
|
}
|
|
3750
3281
|
function inspectGit(path) {
|
|
3751
|
-
const inside =
|
|
3282
|
+
const inside = spawnSync5("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
3752
3283
|
cwd: path,
|
|
3753
3284
|
encoding: "utf8"
|
|
3754
3285
|
});
|
|
3755
3286
|
if (inside.status !== 0) return { isRepository: false, dirty: false };
|
|
3756
|
-
const status =
|
|
3757
|
-
const branch =
|
|
3287
|
+
const status = spawnSync5("git", ["status", "--porcelain"], { cwd: path, encoding: "utf8" });
|
|
3288
|
+
const branch = spawnSync5("git", ["branch", "--show-current"], { cwd: path, encoding: "utf8" });
|
|
3758
3289
|
return {
|
|
3759
3290
|
isRepository: true,
|
|
3760
3291
|
dirty: status.stdout.trim().length > 0,
|
|
@@ -3779,16 +3310,16 @@ function getExpectedPackageVersions() {
|
|
|
3779
3310
|
function findOwnPackageJson() {
|
|
3780
3311
|
let current = dirname11(fileURLToPath3(import.meta.url));
|
|
3781
3312
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
3782
|
-
const candidate =
|
|
3783
|
-
if (
|
|
3313
|
+
const candidate = join19(current, "package.json");
|
|
3314
|
+
if (existsSync20(candidate)) return candidate;
|
|
3784
3315
|
current = dirname11(current);
|
|
3785
3316
|
}
|
|
3786
3317
|
return "";
|
|
3787
3318
|
}
|
|
3788
3319
|
function readJson2(path) {
|
|
3789
|
-
if (!path || !
|
|
3320
|
+
if (!path || !existsSync20(path)) return void 0;
|
|
3790
3321
|
try {
|
|
3791
|
-
return JSON.parse(
|
|
3322
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
3792
3323
|
} catch {
|
|
3793
3324
|
return void 0;
|
|
3794
3325
|
}
|
|
@@ -4105,8 +3636,8 @@ function isHost2(value) {
|
|
|
4105
3636
|
return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
|
|
4106
3637
|
}
|
|
4107
3638
|
function findPortfolioAgentAssetsDir(manifest) {
|
|
4108
|
-
const agentAssetsDir = manifest?.executionEngine?.path ?
|
|
4109
|
-
return agentAssetsDir &&
|
|
3639
|
+
const agentAssetsDir = manifest?.executionEngine?.path ? join20(manifest.executionEngine.path, "agent-assets") : void 0;
|
|
3640
|
+
return agentAssetsDir && existsSync21(join20(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
|
|
4110
3641
|
}
|
|
4111
3642
|
function printUsage4() {
|
|
4112
3643
|
console.error("Usage:");
|
|
@@ -4117,69 +3648,12 @@ function printUsage4() {
|
|
|
4117
3648
|
}
|
|
4118
3649
|
|
|
4119
3650
|
// src/commands/sync.ts
|
|
4120
|
-
import { existsSync as
|
|
4121
|
-
import {
|
|
4122
|
-
import { isDeepStrictEqual } from "node:util";
|
|
4123
|
-
|
|
4124
|
-
// src/host-hooks/config-merge.ts
|
|
4125
|
-
var MANAGED_EVENTS = ["Stop", "SubagentStop"];
|
|
4126
|
-
function mergeHostHookConfig(targetPath, current, template) {
|
|
4127
|
-
const currentRecord = asRecord(current);
|
|
4128
|
-
const templateRecord = asRecord(template);
|
|
4129
|
-
if (targetPath === ".agents/hooks.json") {
|
|
4130
|
-
return {
|
|
4131
|
-
...currentRecord,
|
|
4132
|
-
"pgs-compound-gate": templateRecord["pgs-compound-gate"]
|
|
4133
|
-
};
|
|
4134
|
-
}
|
|
4135
|
-
const host = targetPath === ".codex/hooks.json" ? "codex" : "claude-code";
|
|
4136
|
-
const currentHooks = asRecord(currentRecord.hooks);
|
|
4137
|
-
const templateHooks = asRecord(templateRecord.hooks);
|
|
4138
|
-
const mergedHooks = { ...currentHooks };
|
|
4139
|
-
for (const event of MANAGED_EVENTS) {
|
|
4140
|
-
const currentEntries = asArray(currentHooks[event]);
|
|
4141
|
-
const templateEntries = asArray(templateHooks[event]);
|
|
4142
|
-
mergedHooks[event] = replaceManagedEntries(currentEntries, templateEntries, host, event);
|
|
4143
|
-
}
|
|
4144
|
-
return {
|
|
4145
|
-
...currentRecord,
|
|
4146
|
-
hooks: mergedHooks
|
|
4147
|
-
};
|
|
4148
|
-
}
|
|
4149
|
-
function replaceManagedEntries(currentEntries, templateEntries, host, event) {
|
|
4150
|
-
const result = [];
|
|
4151
|
-
let insertedTemplate = false;
|
|
4152
|
-
for (const entry of currentEntries) {
|
|
4153
|
-
if (isManagedEntry(entry, host, event)) {
|
|
4154
|
-
if (!insertedTemplate) {
|
|
4155
|
-
result.push(...templateEntries);
|
|
4156
|
-
insertedTemplate = true;
|
|
4157
|
-
}
|
|
4158
|
-
continue;
|
|
4159
|
-
}
|
|
4160
|
-
result.push(entry);
|
|
4161
|
-
}
|
|
4162
|
-
if (!insertedTemplate) result.push(...templateEntries);
|
|
4163
|
-
return result;
|
|
4164
|
-
}
|
|
4165
|
-
function isManagedEntry(entry, host, event) {
|
|
4166
|
-
const serialized = JSON.stringify(entry);
|
|
4167
|
-
return serialized.includes("pro-gov host-hook") && serialized.includes(`--host ${host}`) && serialized.includes(`--event ${event}`);
|
|
4168
|
-
}
|
|
4169
|
-
function asRecord(value) {
|
|
4170
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
4171
|
-
return value;
|
|
4172
|
-
}
|
|
4173
|
-
function asArray(value) {
|
|
4174
|
-
return Array.isArray(value) ? value : [];
|
|
4175
|
-
}
|
|
4176
|
-
|
|
4177
|
-
// src/commands/sync.ts
|
|
3651
|
+
import { existsSync as existsSync22, readFileSync as readFileSync15 } from "node:fs";
|
|
3652
|
+
import { join as join21 } from "node:path";
|
|
4178
3653
|
function runSync(args) {
|
|
4179
3654
|
const check = args.includes("--check");
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
console.error("pro-gov sync requires exactly one of --check or --apply-host-hooks.");
|
|
3655
|
+
if (!check) {
|
|
3656
|
+
console.error("pro-gov sync requires --check.");
|
|
4183
3657
|
return 1;
|
|
4184
3658
|
}
|
|
4185
3659
|
const requestedProfile = readFlag2(args, "--profile");
|
|
@@ -4199,13 +3673,12 @@ function runSync(args) {
|
|
|
4199
3673
|
);
|
|
4200
3674
|
return 1;
|
|
4201
3675
|
}
|
|
4202
|
-
if (applyHostHooks) return applyHostHookConfigs(profile);
|
|
4203
3676
|
let differences = 0;
|
|
4204
3677
|
console.log("pro-gov sync check");
|
|
4205
3678
|
console.log(`profile: ${profile}`);
|
|
4206
3679
|
for (const file of planStarterFiles(profile)) {
|
|
4207
|
-
const targetPath =
|
|
4208
|
-
if (!
|
|
3680
|
+
const targetPath = join21(process.cwd(), file.targetPath);
|
|
3681
|
+
if (!existsSync22(targetPath)) {
|
|
4209
3682
|
if (file.ownership === "optional-guardrail") continue;
|
|
4210
3683
|
console.log(`missing: ${file.targetPath}`);
|
|
4211
3684
|
differences += 1;
|
|
@@ -4213,8 +3686,8 @@ function runSync(args) {
|
|
|
4213
3686
|
}
|
|
4214
3687
|
if (file.ownership === "optional-guardrail") continue;
|
|
4215
3688
|
if (file.ownership === "project-local-seed") continue;
|
|
4216
|
-
const source =
|
|
4217
|
-
const target =
|
|
3689
|
+
const source = readFileSync15(file.absoluteSourcePath, "utf8");
|
|
3690
|
+
const target = readFileSync15(targetPath, "utf8");
|
|
4218
3691
|
if (!matchesExpectedContent(file.targetPath, source, target)) {
|
|
4219
3692
|
console.log(`different: ${file.targetPath}`);
|
|
4220
3693
|
differences += 1;
|
|
@@ -4227,52 +3700,11 @@ function runSync(args) {
|
|
|
4227
3700
|
console.log("sync check passed: starter files match packaged assets.");
|
|
4228
3701
|
return 0;
|
|
4229
3702
|
}
|
|
4230
|
-
function applyHostHookConfigs(profile) {
|
|
4231
|
-
if (profile !== "engineering-runtime") {
|
|
4232
|
-
console.error("Host hooks are installed only for the engineering-runtime profile.");
|
|
4233
|
-
return 1;
|
|
4234
|
-
}
|
|
4235
|
-
const root = process.cwd();
|
|
4236
|
-
const updates = [];
|
|
4237
|
-
try {
|
|
4238
|
-
for (const file of planStarterFiles(profile)) {
|
|
4239
|
-
if (!isHostHookTargetPath(file.targetPath)) continue;
|
|
4240
|
-
const path = join22(root, file.targetPath);
|
|
4241
|
-
const template = JSON.parse(readFileSync18(file.absoluteSourcePath, "utf8"));
|
|
4242
|
-
const targetExists = existsSync23(path);
|
|
4243
|
-
const current = targetExists ? JSON.parse(readFileSync18(path, "utf8")) : {};
|
|
4244
|
-
const merged = mergeHostHookConfig(file.targetPath, current, template);
|
|
4245
|
-
if (targetExists && isDeepStrictEqual(current, merged)) continue;
|
|
4246
|
-
updates.push({ path, content: `${JSON.stringify(merged, null, 2)}
|
|
4247
|
-
` });
|
|
4248
|
-
}
|
|
4249
|
-
} catch (error) {
|
|
4250
|
-
console.error(`Cannot merge host hook configuration: ${errorMessage(error)}`);
|
|
4251
|
-
console.error("No host hook files were written.");
|
|
4252
|
-
return 1;
|
|
4253
|
-
}
|
|
4254
|
-
for (const update of updates) {
|
|
4255
|
-
mkdirSync9(dirname12(update.path), { recursive: true });
|
|
4256
|
-
writeFileSync8(update.path, update.content);
|
|
4257
|
-
}
|
|
4258
|
-
console.log(`host hooks applied: ${updates.length}`);
|
|
4259
|
-
console.log("Project-owned host config keys and non-PGS hook entries were preserved.");
|
|
4260
|
-
return 0;
|
|
4261
|
-
}
|
|
4262
3703
|
function matchesExpectedContent(targetPath, source, target) {
|
|
4263
|
-
if (
|
|
4264
|
-
|
|
4265
|
-
return normalizeMarkdownTablePadding(source) === normalizeMarkdownTablePadding(target);
|
|
4266
|
-
}
|
|
4267
|
-
return source === target;
|
|
4268
|
-
}
|
|
4269
|
-
try {
|
|
4270
|
-
const current = JSON.parse(target);
|
|
4271
|
-
const template = JSON.parse(source);
|
|
4272
|
-
return isDeepStrictEqual(current, mergeHostHookConfig(targetPath, current, template));
|
|
4273
|
-
} catch {
|
|
4274
|
-
return false;
|
|
3704
|
+
if (targetPath.endsWith(".md")) {
|
|
3705
|
+
return normalizeMarkdownTablePadding(source) === normalizeMarkdownTablePadding(target);
|
|
4275
3706
|
}
|
|
3707
|
+
return source === target;
|
|
4276
3708
|
}
|
|
4277
3709
|
function normalizeMarkdownTablePadding(content) {
|
|
4278
3710
|
return content.split("\n").map((line) => {
|
|
@@ -4289,7 +3721,7 @@ function normalizeMarkdownTableCell(cell) {
|
|
|
4289
3721
|
}
|
|
4290
3722
|
function inferInstalledProfile(root) {
|
|
4291
3723
|
const installed = ["engineering-runtime", "doc-only"].filter(
|
|
4292
|
-
(profile) =>
|
|
3724
|
+
(profile) => existsSync22(join21(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
|
|
4293
3725
|
);
|
|
4294
3726
|
return installed.length === 1 ? installed[0] : void 0;
|
|
4295
3727
|
}
|
|
@@ -4298,9 +3730,6 @@ function readFlag2(args, flag) {
|
|
|
4298
3730
|
const value = index >= 0 ? args[index + 1] : void 0;
|
|
4299
3731
|
return value && !value.startsWith("--") ? value : void 0;
|
|
4300
3732
|
}
|
|
4301
|
-
function errorMessage(error) {
|
|
4302
|
-
return error instanceof Error ? error.message : String(error);
|
|
4303
|
-
}
|
|
4304
3733
|
|
|
4305
3734
|
// src/cli.ts
|
|
4306
3735
|
var COMMANDS = [
|
|
@@ -4323,9 +3752,8 @@ var COMMANDS = [
|
|
|
4323
3752
|
"lens report --target <path> --out <path>",
|
|
4324
3753
|
"lens audit init --target <path> --out <path>",
|
|
4325
3754
|
"lens audit check --dir <path> [--json]",
|
|
4326
|
-
"host-hook --host <codex|claude-code|antigravity> --event <Stop|SubagentStop|...> [--compound-gate-mode off|lite|strict]",
|
|
4327
3755
|
"init --profile <engineering-runtime|doc-only> <--dry-run|--apply>",
|
|
4328
|
-
"sync
|
|
3756
|
+
"sync --check [--profile <engineering-runtime|doc-only>]",
|
|
4329
3757
|
"doctor"
|
|
4330
3758
|
];
|
|
4331
3759
|
var [command, subcommand] = process.argv.slice(2);
|
|
@@ -4339,7 +3767,6 @@ async function main() {
|
|
|
4339
3767
|
if (command === "learn") return runLearn(process.argv.slice(3));
|
|
4340
3768
|
if (command === "lens") return runLens(process.argv.slice(3));
|
|
4341
3769
|
if (command === "portfolio") return runPortfolio(process.argv.slice(3));
|
|
4342
|
-
if (command === "host-hook") return runHostHook(process.argv.slice(3));
|
|
4343
3770
|
if (command === "init") return runInit(process.argv.slice(3));
|
|
4344
3771
|
if (command === "sync") return runSync(process.argv.slice(3));
|
|
4345
3772
|
if (command === "doctor") return runDoctor(process.argv.slice(3));
|