@pieai/pro-gov 0.3.7 → 0.3.8
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 +23 -9
- package/assets/docs/reference/adoption/adoption-playbook.md +29 -9
- package/assets/docs/reference/adoption/project-relationship.md +14 -15
- package/assets/docs/reference/adoption/recommended-agent-tooling.md +23 -1
- package/assets/integrations/compound-engineering.md +124 -0
- package/assets/integrations/ponytail.md +52 -0
- package/assets/integrations/superpowers.md +21 -8
- package/assets/profiles/doc-only/manifest.yml +2 -0
- package/assets/profiles/doc-only/profile.md +4 -4
- package/assets/profiles/engineering-runtime/manifest.yml +2 -0
- package/assets/profiles/engineering-runtime/profile.md +6 -4
- package/assets/starter/AGENTS.template.md +2 -1
- package/assets/starter/docs/governance/boundary.md +7 -0
- package/assets/starter/docs/governance/ssot-v0.9.md +12 -0
- package/assets/starter/docs/reference/documentation-map.md +5 -0
- package/cli-guide.md +34 -6
- package/dist/cli.js +473 -38
- package/package.json +2 -2
- package/assets/docs/reference/adoption/public-release-checklist.md +0 -153
- package/assets/docs/reference/adoption/site-publication-brief.md +0 -94
package/dist/cli.js
CHANGED
|
@@ -1601,6 +1601,10 @@ function resolveDocGovDependencyCli() {
|
|
|
1601
1601
|
}
|
|
1602
1602
|
}
|
|
1603
1603
|
|
|
1604
|
+
// src/commands/init.ts
|
|
1605
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1606
|
+
import { basename as basename3, dirname as dirname7, join as join13 } from "node:path";
|
|
1607
|
+
|
|
1604
1608
|
// src/commands/shared.ts
|
|
1605
1609
|
function planStarterFiles(profile) {
|
|
1606
1610
|
return listAssets().flatMap((asset) => {
|
|
@@ -1611,11 +1615,21 @@ function planStarterFiles(profile) {
|
|
|
1611
1615
|
{
|
|
1612
1616
|
sourcePath: asset.path,
|
|
1613
1617
|
targetPath,
|
|
1614
|
-
absoluteSourcePath: asset.absolutePath
|
|
1618
|
+
absoluteSourcePath: asset.absolutePath,
|
|
1619
|
+
ownership: classifyOwnership(targetPath)
|
|
1615
1620
|
}
|
|
1616
1621
|
];
|
|
1617
1622
|
}).sort((a, b) => a.targetPath.localeCompare(b.targetPath));
|
|
1618
1623
|
}
|
|
1624
|
+
function classifyOwnership(targetPath) {
|
|
1625
|
+
if (targetPath === "lefthook.yml" || targetPath === ".github/workflows/docs-check.yml") {
|
|
1626
|
+
return "optional-guardrail";
|
|
1627
|
+
}
|
|
1628
|
+
if (targetPath === "AGENTS.md" || targetPath === "CLAUDE.md" || targetPath === "docs/policy/best-practice-for-this-project.md" || targetPath === "docs/reference/documentation-map.md" || targetPath === "docs/reference/execution/current-work.md") {
|
|
1629
|
+
return "project-local-seed";
|
|
1630
|
+
}
|
|
1631
|
+
return "shared";
|
|
1632
|
+
}
|
|
1619
1633
|
function isOtherProfileRouting(targetPath, profile) {
|
|
1620
1634
|
return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v0.9.md`;
|
|
1621
1635
|
}
|
|
@@ -1631,8 +1645,9 @@ function starterTargetPath(sourcePath) {
|
|
|
1631
1645
|
function runInit(args) {
|
|
1632
1646
|
const profile = readFlag(args, "--profile");
|
|
1633
1647
|
const dryRun = args.includes("--dry-run");
|
|
1634
|
-
|
|
1635
|
-
|
|
1648
|
+
const apply = args.includes("--apply");
|
|
1649
|
+
if (dryRun === apply) {
|
|
1650
|
+
console.error("pro-gov init requires exactly one of --dry-run or --apply.");
|
|
1636
1651
|
return 1;
|
|
1637
1652
|
}
|
|
1638
1653
|
if (!profile) {
|
|
@@ -1643,15 +1658,50 @@ function runInit(args) {
|
|
|
1643
1658
|
console.error(`Invalid profile: ${profile}`);
|
|
1644
1659
|
return 1;
|
|
1645
1660
|
}
|
|
1661
|
+
const files = planStarterFiles(profile).filter((file) => file.ownership !== "optional-guardrail");
|
|
1662
|
+
if (apply) return applyStarterFiles(files, profile);
|
|
1646
1663
|
console.log("pro-gov init DRY RUN");
|
|
1647
1664
|
console.log(`profile: ${profile}`);
|
|
1648
1665
|
console.log("");
|
|
1649
1666
|
console.log("Planned starter files:");
|
|
1650
|
-
for (const file of
|
|
1667
|
+
for (const file of files) {
|
|
1651
1668
|
console.log(` ${file.targetPath} <- ${file.sourcePath}`);
|
|
1652
1669
|
}
|
|
1653
1670
|
return 0;
|
|
1654
1671
|
}
|
|
1672
|
+
function applyStarterFiles(files, profile) {
|
|
1673
|
+
const root = process.cwd();
|
|
1674
|
+
const conflicts = files.filter((file) => existsSync13(join13(root, file.targetPath)));
|
|
1675
|
+
if (conflicts.length > 0) {
|
|
1676
|
+
console.error("pro-gov init is refusing to overwrite existing project files:");
|
|
1677
|
+
for (const file of conflicts) console.error(` ${file.targetPath}`);
|
|
1678
|
+
console.error("No files were written. Use --dry-run and migrate existing files deliberately.");
|
|
1679
|
+
return 1;
|
|
1680
|
+
}
|
|
1681
|
+
for (const file of files) {
|
|
1682
|
+
const targetPath = join13(root, file.targetPath);
|
|
1683
|
+
mkdirSync4(dirname7(targetPath), { recursive: true });
|
|
1684
|
+
const source = readFileSync8(file.absoluteSourcePath);
|
|
1685
|
+
const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
|
|
1686
|
+
writeFileSync3(targetPath, content);
|
|
1687
|
+
}
|
|
1688
|
+
console.log("pro-gov init APPLIED");
|
|
1689
|
+
console.log(`profile: ${profile}`);
|
|
1690
|
+
console.log(`created-files: ${files.length}`);
|
|
1691
|
+
console.log("Existing project files were not overwritten.");
|
|
1692
|
+
console.log("Next: customize project-local policy/current-work, run doc-gov scan, then run doc-gov doctor.");
|
|
1693
|
+
return 0;
|
|
1694
|
+
}
|
|
1695
|
+
function renderAgentsTemplate(template, projectName, profile) {
|
|
1696
|
+
const selectedRoute = `docs/governance/agents-routing/${profile}-v0.9.md`;
|
|
1697
|
+
return template.replace("# PROJECT_NAME AI Router", `# ${projectName} AI Router`).replace(
|
|
1698
|
+
/6\. The selected agents routing file:\n - `docs\/governance\/agents-routing\/engineering-runtime-v0\.9\.md`, or\n - `docs\/governance\/agents-routing\/doc-only-v0\.9\.md`/,
|
|
1699
|
+
`6. The selected agents routing file: \`${selectedRoute}\``
|
|
1700
|
+
).replace(
|
|
1701
|
+
"- Name this project's adopted profile: `engineering-runtime` or `doc-only`.",
|
|
1702
|
+
`- This project adopts the \`${profile}\` profile.`
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1655
1705
|
function readFlag(args, flag) {
|
|
1656
1706
|
const index = args.indexOf(flag);
|
|
1657
1707
|
if (index === -1) return null;
|
|
@@ -1661,8 +1711,282 @@ function readFlag(args, flag) {
|
|
|
1661
1711
|
}
|
|
1662
1712
|
|
|
1663
1713
|
// src/commands/lens.ts
|
|
1664
|
-
import { mkdirSync as
|
|
1665
|
-
import { dirname as
|
|
1714
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1715
|
+
import { dirname as dirname9 } from "node:path";
|
|
1716
|
+
|
|
1717
|
+
// src/lens/audit.ts
|
|
1718
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1719
|
+
import { basename as basename4, dirname as dirname8, join as join14 } from "node:path";
|
|
1720
|
+
var REQUIRED_ARTIFACTS = [
|
|
1721
|
+
"manifest.md",
|
|
1722
|
+
"raw/project-lens/architecture-lens.md",
|
|
1723
|
+
"raw/project-lens/truth-surface-audit.md",
|
|
1724
|
+
"raw/project-lens/technology-strategy.md",
|
|
1725
|
+
"raw/ponytail/ponytail-audit.md",
|
|
1726
|
+
"raw/ponytail/ponytail-debt.md",
|
|
1727
|
+
"raw/ponytail/ponytail-gain.md",
|
|
1728
|
+
"raw/target/target-state.md",
|
|
1729
|
+
"raw/target/commands.md",
|
|
1730
|
+
"raw/target/sources.md",
|
|
1731
|
+
"synthesis/decision-index.md",
|
|
1732
|
+
"synthesis/handoff-for-implementation-ai.md"
|
|
1733
|
+
];
|
|
1734
|
+
function createProjectLensAuditPackage(targetDir, auditDir) {
|
|
1735
|
+
const contract = {
|
|
1736
|
+
version: 1,
|
|
1737
|
+
target: {
|
|
1738
|
+
path: targetDir,
|
|
1739
|
+
name: basename4(targetDir) || "target"
|
|
1740
|
+
},
|
|
1741
|
+
requiredArtifacts: [...REQUIRED_ARTIFACTS]
|
|
1742
|
+
};
|
|
1743
|
+
mkdirSync5(auditDir, { recursive: true });
|
|
1744
|
+
writeJson(join14(auditDir, "audit.contract.json"), contract);
|
|
1745
|
+
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
1746
|
+
writeTemplate(join14(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
|
|
1747
|
+
}
|
|
1748
|
+
return contract;
|
|
1749
|
+
}
|
|
1750
|
+
function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
1751
|
+
const contractPath = join14(auditDir, "audit.contract.json");
|
|
1752
|
+
if (!existsSync14(contractPath)) {
|
|
1753
|
+
return {
|
|
1754
|
+
ok: false,
|
|
1755
|
+
auditDir,
|
|
1756
|
+
issues: [
|
|
1757
|
+
{
|
|
1758
|
+
type: "missing-contract",
|
|
1759
|
+
path: "audit.contract.json"
|
|
1760
|
+
}
|
|
1761
|
+
]
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
let contract;
|
|
1765
|
+
try {
|
|
1766
|
+
contract = JSON.parse(readFileSync9(contractPath, "utf8"));
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
return {
|
|
1769
|
+
ok: false,
|
|
1770
|
+
auditDir,
|
|
1771
|
+
issues: [
|
|
1772
|
+
{
|
|
1773
|
+
type: "invalid-contract",
|
|
1774
|
+
path: "audit.contract.json",
|
|
1775
|
+
message: error instanceof Error ? error.message : "Invalid JSON"
|
|
1776
|
+
}
|
|
1777
|
+
]
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
const issues = [];
|
|
1781
|
+
if (contract.version !== 1 || !Array.isArray(contract.requiredArtifacts)) {
|
|
1782
|
+
issues.push({
|
|
1783
|
+
type: "invalid-contract",
|
|
1784
|
+
path: "audit.contract.json",
|
|
1785
|
+
message: "Expected version 1 and requiredArtifacts array"
|
|
1786
|
+
});
|
|
1787
|
+
} else {
|
|
1788
|
+
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
1789
|
+
if (!contract.requiredArtifacts.includes(artifactPath)) {
|
|
1790
|
+
issues.push({
|
|
1791
|
+
type: "invalid-contract",
|
|
1792
|
+
path: "audit.contract.json",
|
|
1793
|
+
message: `Missing required artifact in contract: ${artifactPath}`
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
1799
|
+
const absolutePath = join14(auditDir, artifactPath);
|
|
1800
|
+
if (!existsSync14(absolutePath)) {
|
|
1801
|
+
issues.push({ type: "missing-required-artifact", path: artifactPath });
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
const content = readFileSync9(absolutePath, "utf8");
|
|
1805
|
+
if (isPendingArtifact(content)) {
|
|
1806
|
+
issues.push({ type: "artifact-not-complete", path: artifactPath });
|
|
1807
|
+
} else if (hasTemplateBody(content)) {
|
|
1808
|
+
issues.push({ type: "artifact-template-not-replaced", path: artifactPath });
|
|
1809
|
+
} else {
|
|
1810
|
+
issues.push(...checkArtifactGuardrails(artifactPath, content, options.mode));
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return {
|
|
1814
|
+
ok: issues.length === 0,
|
|
1815
|
+
auditDir,
|
|
1816
|
+
issues
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
function writeJson(path, value) {
|
|
1820
|
+
mkdirSync5(dirname8(path), { recursive: true });
|
|
1821
|
+
writeFileSync4(path, `${JSON.stringify(value, null, 2)}
|
|
1822
|
+
`);
|
|
1823
|
+
}
|
|
1824
|
+
function writeTemplate(path, content) {
|
|
1825
|
+
mkdirSync5(dirname8(path), { recursive: true });
|
|
1826
|
+
writeFileSync4(path, content);
|
|
1827
|
+
}
|
|
1828
|
+
function renderArtifactTemplate(artifactPath, contract) {
|
|
1829
|
+
const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
|
|
1830
|
+
const producer = artifactProducer(artifactPath);
|
|
1831
|
+
return `${[
|
|
1832
|
+
"---",
|
|
1833
|
+
"status: pending",
|
|
1834
|
+
`producer: ${producer}`,
|
|
1835
|
+
`target: ${contract.target.path}`,
|
|
1836
|
+
`artifact: ${artifactPath}`,
|
|
1837
|
+
"---",
|
|
1838
|
+
"",
|
|
1839
|
+
`# ${title}`,
|
|
1840
|
+
"",
|
|
1841
|
+
"Replace this template with the raw audit output for this producer.",
|
|
1842
|
+
...renderArtifactGuardrailHint(artifactPath),
|
|
1843
|
+
"",
|
|
1844
|
+
"Required completion marker:",
|
|
1845
|
+
"",
|
|
1846
|
+
"```text",
|
|
1847
|
+
"status: complete",
|
|
1848
|
+
"```"
|
|
1849
|
+
].join("\n")}
|
|
1850
|
+
`;
|
|
1851
|
+
}
|
|
1852
|
+
function renderArtifactGuardrailHint(artifactPath) {
|
|
1853
|
+
const rules = REQUIRED_METHOD_RECORDS[artifactPath];
|
|
1854
|
+
if (!rules) return [];
|
|
1855
|
+
const lines = [
|
|
1856
|
+
"",
|
|
1857
|
+
"Required audit method records. Keep these labels at the start of their own lines:",
|
|
1858
|
+
"",
|
|
1859
|
+
...rules.map((rule) => `${rule.label} <replace with evidence>`)
|
|
1860
|
+
];
|
|
1861
|
+
if (artifactPath === "manifest.md") {
|
|
1862
|
+
lines.push(
|
|
1863
|
+
"",
|
|
1864
|
+
"For --mode fresh, also keep these labels at the start of their own lines:",
|
|
1865
|
+
"",
|
|
1866
|
+
"Audit run mode: <replace with fresh>",
|
|
1867
|
+
"Current session id: <replace with current Codex thread id, or unknown plus reason>",
|
|
1868
|
+
"Fresh run evidence: <replace with current-run raw pass and subagent evidence>",
|
|
1869
|
+
"",
|
|
1870
|
+
"For --mode reuse, use these labels instead of the fresh-mode labels:",
|
|
1871
|
+
"",
|
|
1872
|
+
"Audit run mode: <replace with reuse>",
|
|
1873
|
+
"Reuse source audit: <replace with reused audit package path>",
|
|
1874
|
+
"Reuse justification: <replace with same target commit, clean status, and check result>",
|
|
1875
|
+
"No new subagents were run: <replace with true and explanation>"
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
1878
|
+
return lines;
|
|
1879
|
+
}
|
|
1880
|
+
function artifactProducer(artifactPath) {
|
|
1881
|
+
if (artifactPath.startsWith("raw/project-lens/")) return "project-lens";
|
|
1882
|
+
if (artifactPath.startsWith("raw/ponytail/")) return "ponytail";
|
|
1883
|
+
if (artifactPath.startsWith("raw/target/")) return "target-evidence";
|
|
1884
|
+
if (artifactPath.startsWith("synthesis/")) return "synthesis";
|
|
1885
|
+
return "audit";
|
|
1886
|
+
}
|
|
1887
|
+
function isPendingArtifact(content) {
|
|
1888
|
+
const frontmatter = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
1889
|
+
if (!frontmatter) return true;
|
|
1890
|
+
return !/^status:\s*complete\s*$/m.test(frontmatter[1]);
|
|
1891
|
+
}
|
|
1892
|
+
function hasTemplateBody(content) {
|
|
1893
|
+
return content.includes("Replace this template with the raw audit output for this producer.");
|
|
1894
|
+
}
|
|
1895
|
+
var REQUIRED_METHOD_RECORDS = {
|
|
1896
|
+
"manifest.md": [
|
|
1897
|
+
{
|
|
1898
|
+
label: "Read-only boundary:",
|
|
1899
|
+
match: /^Read-only boundary:/im
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
label: "Agent execution record:",
|
|
1903
|
+
match: /^Agent execution record:/im
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
label: "Subagent trace:",
|
|
1907
|
+
match: /^Subagent trace:/im
|
|
1908
|
+
}
|
|
1909
|
+
],
|
|
1910
|
+
"raw/target/commands.md": [
|
|
1911
|
+
{
|
|
1912
|
+
label: "Project Lens method source:",
|
|
1913
|
+
match: /^Project Lens method source:/im
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
label: "Ponytail method source:",
|
|
1917
|
+
match: /^Ponytail method source:/im
|
|
1918
|
+
}
|
|
1919
|
+
],
|
|
1920
|
+
"synthesis/decision-index.md": [
|
|
1921
|
+
{
|
|
1922
|
+
label: "Target repository final status:",
|
|
1923
|
+
match: /^Target repository final status:/im
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
label: "Audit package final status:",
|
|
1927
|
+
match: /^Audit package final status:/im
|
|
1928
|
+
}
|
|
1929
|
+
]
|
|
1930
|
+
};
|
|
1931
|
+
var RUN_MODE_METHOD_RECORDS = {
|
|
1932
|
+
fresh: [
|
|
1933
|
+
{
|
|
1934
|
+
label: "Audit run mode: fresh",
|
|
1935
|
+
match: /^Audit run mode:\s*fresh\b/im
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
label: "Current session id:",
|
|
1939
|
+
match: /^Current session id:/im
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
label: "Fresh run evidence:",
|
|
1943
|
+
match: /^Fresh run evidence:/im
|
|
1944
|
+
}
|
|
1945
|
+
],
|
|
1946
|
+
reuse: [
|
|
1947
|
+
{
|
|
1948
|
+
label: "Audit run mode: reuse",
|
|
1949
|
+
match: /^Audit run mode:\s*reuse\b/im
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
label: "Reuse source audit:",
|
|
1953
|
+
match: /^Reuse source audit:/im
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
label: "Reuse justification:",
|
|
1957
|
+
match: /^Reuse justification:/im
|
|
1958
|
+
},
|
|
1959
|
+
{
|
|
1960
|
+
label: "No new subagents were run:",
|
|
1961
|
+
match: /^No new subagents were run:/im
|
|
1962
|
+
}
|
|
1963
|
+
]
|
|
1964
|
+
};
|
|
1965
|
+
function checkArtifactGuardrails(artifactPath, content, mode) {
|
|
1966
|
+
const rules = [
|
|
1967
|
+
...REQUIRED_METHOD_RECORDS[artifactPath] ?? [],
|
|
1968
|
+
...artifactPath === "manifest.md" && mode ? RUN_MODE_METHOD_RECORDS[mode] : []
|
|
1969
|
+
];
|
|
1970
|
+
if (!rules) return [];
|
|
1971
|
+
return rules.filter((rule) => !hasCompletedMethodRecord(rule, content)).map((rule) => ({
|
|
1972
|
+
type: "audit-method-not-recorded",
|
|
1973
|
+
path: artifactPath,
|
|
1974
|
+
message: `Missing required audit method record: ${rule.label}`
|
|
1975
|
+
}));
|
|
1976
|
+
}
|
|
1977
|
+
function hasCompletedMethodRecord(rule, content) {
|
|
1978
|
+
return content.split(/\r?\n/).some((line) => rule.match.test(line) && !line.includes("<replace with"));
|
|
1979
|
+
}
|
|
1980
|
+
function formatProjectLensAuditCheckText(result) {
|
|
1981
|
+
if (result.ok) return `audit package ok: ${result.auditDir}`;
|
|
1982
|
+
return [
|
|
1983
|
+
`audit package failed: ${result.auditDir}`,
|
|
1984
|
+
...result.issues.map((issue) => {
|
|
1985
|
+
const message = issue.message ? `: ${issue.message}` : "";
|
|
1986
|
+
return `- ${issue.type}: ${issue.path}${message}`;
|
|
1987
|
+
})
|
|
1988
|
+
].join("\n");
|
|
1989
|
+
}
|
|
1666
1990
|
|
|
1667
1991
|
// src/lens/report.ts
|
|
1668
1992
|
function formatProjectLensInspection(report) {
|
|
@@ -1737,8 +2061,8 @@ function bulletList(values) {
|
|
|
1737
2061
|
|
|
1738
2062
|
// src/lens/scan.ts
|
|
1739
2063
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1740
|
-
import { existsSync as
|
|
1741
|
-
import { join as
|
|
2064
|
+
import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
|
|
2065
|
+
import { join as join15, relative as relative5 } from "node:path";
|
|
1742
2066
|
var ignoredDirectories = /* @__PURE__ */ new Set([
|
|
1743
2067
|
".git",
|
|
1744
2068
|
".next",
|
|
@@ -1755,24 +2079,24 @@ function scanProjectLensTarget(targetDir, options = {}) {
|
|
|
1755
2079
|
return {
|
|
1756
2080
|
targetDir,
|
|
1757
2081
|
aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter(
|
|
1758
|
-
(file) =>
|
|
2082
|
+
(file) => existsSync15(join15(targetDir, file))
|
|
1759
2083
|
),
|
|
1760
2084
|
aiConfigFiles: [],
|
|
1761
2085
|
packageJson,
|
|
1762
2086
|
docs: {
|
|
1763
|
-
hasDocsDirectory:
|
|
2087
|
+
hasDocsDirectory: existsSync15(join15(targetDir, "docs")),
|
|
1764
2088
|
markdownFileCount: markdownFiles.length,
|
|
1765
2089
|
governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
|
|
1766
2090
|
},
|
|
1767
2091
|
git: readGitState(targetDir),
|
|
1768
|
-
largeFiles: files.map((file) => ({ path: file, bytes: statSync3(
|
|
2092
|
+
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)
|
|
1769
2093
|
};
|
|
1770
2094
|
}
|
|
1771
2095
|
function readPackageJson(targetDir) {
|
|
1772
|
-
const packageJsonPath =
|
|
1773
|
-
if (!
|
|
2096
|
+
const packageJsonPath = join15(targetDir, "package.json");
|
|
2097
|
+
if (!existsSync15(packageJsonPath)) return void 0;
|
|
1774
2098
|
try {
|
|
1775
|
-
const packageJson = JSON.parse(
|
|
2099
|
+
const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf8"));
|
|
1776
2100
|
return {
|
|
1777
2101
|
scripts: Object.keys(packageJson.scripts ?? {}).sort(),
|
|
1778
2102
|
dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
|
|
@@ -1807,13 +2131,13 @@ function listProjectFiles(targetDir) {
|
|
|
1807
2131
|
return files.sort();
|
|
1808
2132
|
}
|
|
1809
2133
|
function collectFiles2(rootDir, currentDir, files) {
|
|
1810
|
-
if (!
|
|
2134
|
+
if (!existsSync15(currentDir)) return;
|
|
1811
2135
|
for (const entry of readdirSync6(currentDir, { withFileTypes: true })) {
|
|
1812
2136
|
if (entry.isDirectory()) {
|
|
1813
2137
|
if (ignoredDirectories.has(entry.name)) continue;
|
|
1814
|
-
collectFiles2(rootDir,
|
|
2138
|
+
collectFiles2(rootDir, join15(currentDir, entry.name), files);
|
|
1815
2139
|
} else if (entry.isFile()) {
|
|
1816
|
-
files.push(toUnixPath4(relative5(rootDir,
|
|
2140
|
+
files.push(toUnixPath4(relative5(rootDir, join15(currentDir, entry.name))));
|
|
1817
2141
|
}
|
|
1818
2142
|
}
|
|
1819
2143
|
}
|
|
@@ -1830,6 +2154,9 @@ function runLens(args) {
|
|
|
1830
2154
|
if (subcommand2 === "report") {
|
|
1831
2155
|
return runLensReport(rest);
|
|
1832
2156
|
}
|
|
2157
|
+
if (subcommand2 === "audit") {
|
|
2158
|
+
return runLensAudit(rest);
|
|
2159
|
+
}
|
|
1833
2160
|
printUsage2();
|
|
1834
2161
|
return 1;
|
|
1835
2162
|
}
|
|
@@ -1862,11 +2189,52 @@ function runLensReport(args) {
|
|
|
1862
2189
|
}
|
|
1863
2190
|
const report = scanProjectLensTarget(options.value.targetDir);
|
|
1864
2191
|
const markdown = renderProjectLensMarkdownReport(report);
|
|
1865
|
-
|
|
1866
|
-
|
|
2192
|
+
mkdirSync6(dirname9(options.value.outPath), { recursive: true });
|
|
2193
|
+
writeFileSync5(options.value.outPath, markdown);
|
|
1867
2194
|
console.log(`report: ${options.value.outPath}`);
|
|
1868
2195
|
return 0;
|
|
1869
2196
|
}
|
|
2197
|
+
function runLensAudit(args) {
|
|
2198
|
+
const [auditSubcommand, ...rest] = args;
|
|
2199
|
+
if (auditSubcommand === "init") {
|
|
2200
|
+
const options = parseLensOptions(rest, "audit init");
|
|
2201
|
+
if (!options.ok) {
|
|
2202
|
+
console.error(options.error);
|
|
2203
|
+
printUsage2();
|
|
2204
|
+
return 1;
|
|
2205
|
+
}
|
|
2206
|
+
if (!options.value.outPath) {
|
|
2207
|
+
console.error("Expected --out <path>");
|
|
2208
|
+
printUsage2();
|
|
2209
|
+
return 1;
|
|
2210
|
+
}
|
|
2211
|
+
createProjectLensAuditPackage(options.value.targetDir, options.value.outPath);
|
|
2212
|
+
console.log(`audit: ${options.value.outPath}`);
|
|
2213
|
+
return 0;
|
|
2214
|
+
}
|
|
2215
|
+
if (auditSubcommand === "check") {
|
|
2216
|
+
const options = parseLensOptions(rest, "audit check");
|
|
2217
|
+
if (!options.ok) {
|
|
2218
|
+
console.error(options.error);
|
|
2219
|
+
printUsage2();
|
|
2220
|
+
return 1;
|
|
2221
|
+
}
|
|
2222
|
+
if (!options.value.auditDir) {
|
|
2223
|
+
console.error("Expected --dir <path>");
|
|
2224
|
+
printUsage2();
|
|
2225
|
+
return 1;
|
|
2226
|
+
}
|
|
2227
|
+
const result = checkProjectLensAuditPackage(options.value.auditDir, { mode: options.value.auditMode });
|
|
2228
|
+
if (options.value.json || options.value.format === "json") {
|
|
2229
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2230
|
+
} else {
|
|
2231
|
+
console.log(formatProjectLensAuditCheckText(result));
|
|
2232
|
+
}
|
|
2233
|
+
return result.ok ? 0 : 1;
|
|
2234
|
+
}
|
|
2235
|
+
printUsage2();
|
|
2236
|
+
return 1;
|
|
2237
|
+
}
|
|
1870
2238
|
function parseLensOptions(args, subcommand2) {
|
|
1871
2239
|
const options = {
|
|
1872
2240
|
targetDir: process.cwd(),
|
|
@@ -1895,6 +2263,21 @@ function parseLensOptions(args, subcommand2) {
|
|
|
1895
2263
|
if (!outPath) return { ok: false, error: "Expected --out <path>" };
|
|
1896
2264
|
options.outPath = outPath;
|
|
1897
2265
|
index += 1;
|
|
2266
|
+
} else if (arg === "--dir") {
|
|
2267
|
+
const auditDir = args[index + 1];
|
|
2268
|
+
if (!auditDir) return { ok: false, error: "Expected --dir <path>" };
|
|
2269
|
+
options.auditDir = auditDir;
|
|
2270
|
+
index += 1;
|
|
2271
|
+
} else if (arg === "--mode") {
|
|
2272
|
+
if (subcommand2 !== "audit check") {
|
|
2273
|
+
return { ok: false, error: `Unknown lens ${subcommand2} option: ${arg}` };
|
|
2274
|
+
}
|
|
2275
|
+
const auditMode = args[index + 1];
|
|
2276
|
+
if (auditMode !== "fresh" && auditMode !== "reuse") {
|
|
2277
|
+
return { ok: false, error: "Expected --mode fresh|reuse" };
|
|
2278
|
+
}
|
|
2279
|
+
options.auditMode = auditMode;
|
|
2280
|
+
index += 1;
|
|
1898
2281
|
} else {
|
|
1899
2282
|
return { ok: false, error: `Unknown lens ${subcommand2} option: ${arg}` };
|
|
1900
2283
|
}
|
|
@@ -1905,18 +2288,21 @@ function printUsage2() {
|
|
|
1905
2288
|
console.error("Usage: pro-gov lens scan [--target <path>] [--json]");
|
|
1906
2289
|
console.error("Usage: pro-gov lens inspect [--target <path>] [--format text|json]");
|
|
1907
2290
|
console.error("Usage: pro-gov lens report --target <path> --out <path>");
|
|
2291
|
+
console.error("Usage: pro-gov lens audit init --target <path> --out <path>");
|
|
2292
|
+
console.error("Usage: pro-gov lens audit check --dir <path> [--mode fresh|reuse] [--json]");
|
|
1908
2293
|
}
|
|
1909
2294
|
|
|
1910
2295
|
// src/commands/portfolio.ts
|
|
1911
|
-
import { existsSync as
|
|
1912
|
-
import { join as
|
|
2296
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
2297
|
+
import { join as join16 } from "node:path";
|
|
1913
2298
|
|
|
1914
2299
|
// src/portfolio/manifest.ts
|
|
1915
|
-
import { existsSync as
|
|
2300
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
|
|
2301
|
+
import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve2 } from "node:path";
|
|
1916
2302
|
function loadPortfolioManifest(configPath) {
|
|
1917
2303
|
let parsed;
|
|
1918
2304
|
try {
|
|
1919
|
-
parsed = JSON.parse(
|
|
2305
|
+
parsed = JSON.parse(readFileSync11(configPath, "utf8"));
|
|
1920
2306
|
} catch (error) {
|
|
1921
2307
|
return {
|
|
1922
2308
|
configPath,
|
|
@@ -1928,13 +2314,29 @@ function loadPortfolioManifest(configPath) {
|
|
|
1928
2314
|
]
|
|
1929
2315
|
};
|
|
1930
2316
|
}
|
|
1931
|
-
const
|
|
2317
|
+
const normalized = resolveManifestPaths(parsed, dirname10(resolve2(configPath)));
|
|
2318
|
+
const issues = validatePortfolioManifest(normalized);
|
|
1932
2319
|
return {
|
|
1933
2320
|
configPath,
|
|
1934
|
-
manifest: issues.length === 0 ?
|
|
2321
|
+
manifest: issues.length === 0 ? normalized : void 0,
|
|
1935
2322
|
issues
|
|
1936
2323
|
};
|
|
1937
2324
|
}
|
|
2325
|
+
function resolveManifestPaths(value, configDir) {
|
|
2326
|
+
if (!isRecord(value)) return value;
|
|
2327
|
+
const resolveEndpoint = (endpoint) => {
|
|
2328
|
+
if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
|
|
2329
|
+
return endpoint;
|
|
2330
|
+
}
|
|
2331
|
+
return { ...endpoint, path: resolve2(configDir, endpoint.path) };
|
|
2332
|
+
};
|
|
2333
|
+
return {
|
|
2334
|
+
...value,
|
|
2335
|
+
controlPlane: resolveEndpoint(value.controlPlane),
|
|
2336
|
+
executionEngine: resolveEndpoint(value.executionEngine),
|
|
2337
|
+
targets: Array.isArray(value.targets) ? value.targets.map(resolveEndpoint) : value.targets
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
1938
2340
|
function validatePortfolioManifest(value) {
|
|
1939
2341
|
const issues = [];
|
|
1940
2342
|
if (!isRecord(value)) {
|
|
@@ -2054,7 +2456,7 @@ function validateEndpoint(value, field, issues) {
|
|
|
2054
2456
|
});
|
|
2055
2457
|
return;
|
|
2056
2458
|
}
|
|
2057
|
-
if (!
|
|
2459
|
+
if (!existsSync16(value.path)) {
|
|
2058
2460
|
issues.push({
|
|
2059
2461
|
type: "missing-path",
|
|
2060
2462
|
id: typeof value.id === "string" ? value.id : void 0,
|
|
@@ -2329,8 +2731,8 @@ function isHost2(value) {
|
|
|
2329
2731
|
return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
|
|
2330
2732
|
}
|
|
2331
2733
|
function findPortfolioAgentAssetsDir(manifest) {
|
|
2332
|
-
const agentAssetsDir = manifest?.executionEngine?.path ?
|
|
2333
|
-
return agentAssetsDir &&
|
|
2734
|
+
const agentAssetsDir = manifest?.executionEngine?.path ? join16(manifest.executionEngine.path, "agent-assets") : void 0;
|
|
2735
|
+
return agentAssetsDir && existsSync17(join16(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
|
|
2334
2736
|
}
|
|
2335
2737
|
function printUsage3() {
|
|
2336
2738
|
console.error("Usage:");
|
|
@@ -2340,24 +2742,44 @@ function printUsage3() {
|
|
|
2340
2742
|
}
|
|
2341
2743
|
|
|
2342
2744
|
// src/commands/sync.ts
|
|
2343
|
-
import { existsSync as
|
|
2344
|
-
import { join as
|
|
2745
|
+
import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
|
|
2746
|
+
import { join as join17 } from "node:path";
|
|
2345
2747
|
function runSync(args) {
|
|
2346
2748
|
if (!args.includes("--check")) {
|
|
2347
|
-
console.error("pro-gov sync
|
|
2749
|
+
console.error("pro-gov sync is read-only and requires --check.");
|
|
2750
|
+
return 1;
|
|
2751
|
+
}
|
|
2752
|
+
const requestedProfile = readFlag2(args, "--profile");
|
|
2753
|
+
let profile;
|
|
2754
|
+
if (requestedProfile) {
|
|
2755
|
+
if (!isValidProfile(requestedProfile)) {
|
|
2756
|
+
console.error(`Invalid profile: ${requestedProfile}`);
|
|
2757
|
+
return 1;
|
|
2758
|
+
}
|
|
2759
|
+
profile = requestedProfile;
|
|
2760
|
+
} else {
|
|
2761
|
+
profile = inferInstalledProfile(process.cwd());
|
|
2762
|
+
}
|
|
2763
|
+
if (!profile) {
|
|
2764
|
+
console.error(
|
|
2765
|
+
"Cannot infer one installed profile. Pass --profile <engineering-runtime|doc-only>."
|
|
2766
|
+
);
|
|
2348
2767
|
return 1;
|
|
2349
2768
|
}
|
|
2350
2769
|
let differences = 0;
|
|
2351
2770
|
console.log("pro-gov sync check");
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2771
|
+
console.log(`profile: ${profile}`);
|
|
2772
|
+
for (const file of planStarterFiles(profile)) {
|
|
2773
|
+
const targetPath = join17(process.cwd(), file.targetPath);
|
|
2774
|
+
if (!existsSync18(targetPath)) {
|
|
2775
|
+
if (file.ownership === "optional-guardrail") continue;
|
|
2355
2776
|
console.log(`missing: ${file.targetPath}`);
|
|
2356
2777
|
differences += 1;
|
|
2357
2778
|
continue;
|
|
2358
2779
|
}
|
|
2359
|
-
|
|
2360
|
-
const
|
|
2780
|
+
if (file.ownership === "project-local-seed") continue;
|
|
2781
|
+
const source = readFileSync12(file.absoluteSourcePath, "utf8");
|
|
2782
|
+
const target = readFileSync12(targetPath, "utf8");
|
|
2361
2783
|
if (source !== target) {
|
|
2362
2784
|
console.log(`different: ${file.targetPath}`);
|
|
2363
2785
|
differences += 1;
|
|
@@ -2370,6 +2792,17 @@ function runSync(args) {
|
|
|
2370
2792
|
console.log("sync check passed: starter files match packaged assets.");
|
|
2371
2793
|
return 0;
|
|
2372
2794
|
}
|
|
2795
|
+
function inferInstalledProfile(root) {
|
|
2796
|
+
const installed = ["engineering-runtime", "doc-only"].filter(
|
|
2797
|
+
(profile) => existsSync18(join17(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
|
|
2798
|
+
);
|
|
2799
|
+
return installed.length === 1 ? installed[0] : void 0;
|
|
2800
|
+
}
|
|
2801
|
+
function readFlag2(args, flag) {
|
|
2802
|
+
const index = args.indexOf(flag);
|
|
2803
|
+
const value = index >= 0 ? args[index + 1] : void 0;
|
|
2804
|
+
return value && !value.startsWith("--") ? value : void 0;
|
|
2805
|
+
}
|
|
2373
2806
|
|
|
2374
2807
|
// src/cli.ts
|
|
2375
2808
|
var COMMANDS = [
|
|
@@ -2387,8 +2820,10 @@ var COMMANDS = [
|
|
|
2387
2820
|
"lens scan [--target <path>] [--json]",
|
|
2388
2821
|
"lens inspect [--target <path>] [--format text|json]",
|
|
2389
2822
|
"lens report --target <path> --out <path>",
|
|
2390
|
-
"init --
|
|
2391
|
-
"
|
|
2823
|
+
"lens audit init --target <path> --out <path>",
|
|
2824
|
+
"lens audit check --dir <path> [--json]",
|
|
2825
|
+
"init --profile <engineering-runtime|doc-only> <--dry-run|--apply>",
|
|
2826
|
+
"sync --check [--profile <engineering-runtime|doc-only>]",
|
|
2392
2827
|
"doctor"
|
|
2393
2828
|
];
|
|
2394
2829
|
var [command, subcommand] = process.argv.slice(2);
|