@pieai/pro-gov 0.3.12 → 0.3.14
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 +5 -0
- package/assets/docs/reference/adoption/adoption-playbook.md +10 -0
- package/assets/docs/reference/adoption/recommended-agent-tooling.md +10 -0
- package/assets/integrations/compound-engineering.md +23 -4
- package/assets/integrations/superpowers.md +15 -9
- package/assets/profiles/engineering-runtime/profile.md +5 -0
- package/assets/starter/.github/workflows/docs-check.yml +3 -3
- package/assets/starter/AGENTS.template.md +4 -0
- package/cli-guide.md +8 -0
- package/dist/cli.js +300 -74
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -148,6 +148,11 @@ engineering work. The default PGS pattern is a Compound Gate: run
|
|
|
148
148
|
skipped. Full CE workflows such as `ce-plan`, `ce-work`, and `lfg` remain
|
|
149
149
|
explicit user choices.
|
|
150
150
|
|
|
151
|
+
PGS Learning Recall is the pre-work companion to that post-work gate. Before
|
|
152
|
+
non-trivial implementation, debugging, release, architecture, or portfolio-sync
|
|
153
|
+
work, run `pro-gov learn recall --query "<task summary>"` and read relevant
|
|
154
|
+
`docs/solutions/**` or `CONCEPTS.md` hits before changing files.
|
|
155
|
+
|
|
151
156
|
Engineering-runtime starters include cross-host Stop hooks for Codex,
|
|
152
157
|
Claude Code, and Antigravity. Those hooks call `pro-gov host-hook` and require
|
|
153
158
|
the final report to include either:
|
|
@@ -211,6 +211,16 @@ Use Compound Engineering by default only as the post-work Compound Gate:
|
|
|
211
211
|
`ce-compound` captures reusable lessons when they exist; otherwise the agent
|
|
212
212
|
reports a skip reason. Full CE workflows require an explicit user request.
|
|
213
213
|
|
|
214
|
+
Use PGS Learning Recall before non-trivial work:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pro-gov learn recall --query "<task summary>"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
This reads the project's existing `docs/solutions/**` and `CONCEPTS.md` records
|
|
221
|
+
so prior lessons can shape the work without making Compound Engineering the
|
|
222
|
+
default execution workflow.
|
|
223
|
+
|
|
214
224
|
The host-hook gate is deliberately small. It does not make Compound Engineering
|
|
215
225
|
the main workflow. It only blocks a final completion report that looks like
|
|
216
226
|
finished engineering work but does not say whether the Compound Gate ran or was
|
|
@@ -78,6 +78,15 @@ Superpowers completes verified work
|
|
|
78
78
|
-> run ce-compound or report a skip reason
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
PGS Learning Recall is the matching pre-work step:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pro-gov learn recall --query "<task summary>"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It searches `docs/solutions/**` and `CONCEPTS.md` so agents can reuse prior
|
|
88
|
+
lessons without making Compound Engineering the default execution workflow.
|
|
89
|
+
|
|
81
90
|
Do not make routine tasks choose between Superpowers and Compound Engineering as
|
|
82
91
|
competing default engineering processes. Use CE's full `ce-plan`, `ce-work`, or
|
|
83
92
|
`lfg` flow only when the user explicitly asks for it.
|
|
@@ -107,6 +116,7 @@ For an engineering/runtime project:
|
|
|
107
116
|
```text
|
|
108
117
|
install PGS packages
|
|
109
118
|
-> keep Superpowers available for engineering workflows
|
|
119
|
+
-> run pro-gov learn recall before non-trivial work
|
|
110
120
|
-> use Compound Engineering's ce-compound as the post-work learning tail
|
|
111
121
|
-> install Ponytail but keep global mode off
|
|
112
122
|
-> activate Ponytail only when a bounded simplicity review would help
|
|
@@ -11,6 +11,7 @@ Think of an AI project as a workshop:
|
|
|
11
11
|
- Superpowers is the default cooking process for engineering work.
|
|
12
12
|
- Compound Engineering's `ce-compound` is the recipe notebook written after a
|
|
13
13
|
non-trivial dish is proven to work.
|
|
14
|
+
- `pro-gov learn recall` is the recipe-card search before the next dish starts.
|
|
14
15
|
- Ponytail is the cost and complexity adviser.
|
|
15
16
|
|
|
16
17
|
The default path is one main process with one learning tail. Do not make every
|
|
@@ -25,12 +26,30 @@ capture loop by default, not as a second default execution engine.
|
|
|
25
26
|
Use this order:
|
|
26
27
|
|
|
27
28
|
1. PGS routes the task and selects the project lane.
|
|
28
|
-
2.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
2. For non-trivial engineering work, run Learning Recall against the task
|
|
30
|
+
summary and read relevant prior lessons.
|
|
31
|
+
3. Superpowers handles the normal engineering workflow when that lane needs one.
|
|
32
|
+
4. Ponytail may run as an explicit complexity review when useful.
|
|
33
|
+
5. The agent must pass the Compound Gate before final reporting.
|
|
34
|
+
6. Doc Gov validates governed documentation and ignores CE-owned external
|
|
32
35
|
artifacts.
|
|
33
36
|
|
|
37
|
+
## Learning Recall
|
|
38
|
+
|
|
39
|
+
CE writes the reusable lesson. PGS recalls it.
|
|
40
|
+
|
|
41
|
+
Before non-trivial implementation, debugging, release, architecture, or
|
|
42
|
+
portfolio-sync work, run:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pro-gov learn recall --query "<task summary>"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Read the relevant hits before changing files. A no-hit result is acceptable;
|
|
49
|
+
do not scan every `docs/solutions/**` file by hand. The recall command is a
|
|
50
|
+
small search surface over `docs/solutions/**` and `CONCEPTS.md`, not a second
|
|
51
|
+
workflow engine, vector database, or CE replacement.
|
|
52
|
+
|
|
34
53
|
## Compound Gate
|
|
35
54
|
|
|
36
55
|
Every completed non-trivial engineering task must pass the Compound Gate:
|
|
@@ -8,13 +8,15 @@ Think of an AI project as a building site:
|
|
|
8
8
|
|
|
9
9
|
- PGS is the traffic desk and inspection station.
|
|
10
10
|
- Superpowers is the construction process.
|
|
11
|
+
- `pro-gov learn recall` is the pre-work lookup for reusable lessons.
|
|
11
12
|
- Compound Engineering's `ce-compound` is the knowledge-capture tail.
|
|
12
13
|
- Ponytail is an optional cost and complexity adviser.
|
|
13
14
|
|
|
14
15
|
PGS chooses the lane. Superpowers makes sure the work follows the right
|
|
15
|
-
engineering process.
|
|
16
|
-
|
|
17
|
-
cancel the process or the final
|
|
16
|
+
engineering process. Learning Recall checks prior lessons before work starts.
|
|
17
|
+
Compound Engineering records reusable lessons after verified work. Ponytail may
|
|
18
|
+
suggest a leaner implementation, but it cannot cancel the process or the final
|
|
19
|
+
inspection.
|
|
18
20
|
|
|
19
21
|
## Boundary
|
|
20
22
|
|
|
@@ -32,6 +34,7 @@ Project Governance System owns:
|
|
|
32
34
|
- documentation lifecycle
|
|
33
35
|
- agents routing
|
|
34
36
|
- current work index conventions
|
|
37
|
+
- pre-work learning recall through `pro-gov learn recall`
|
|
35
38
|
- the governed location and boundary for externally sourced AI evidence rules
|
|
36
39
|
|
|
37
40
|
Ponytail, when explicitly enabled for a task, may advise on:
|
|
@@ -59,17 +62,20 @@ Durable outputs should map back to the project's doc-gov layers:
|
|
|
59
62
|
|
|
60
63
|
## Execution Order
|
|
61
64
|
|
|
62
|
-
Agents routing classifies first.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
Agents routing classifies first. For non-trivial work, Learning Recall checks
|
|
66
|
+
prior `docs/solutions/**` and `CONCEPTS.md` records before files change.
|
|
67
|
+
Superpowers executes inside the selected lane. Optional Ponytail advice may run
|
|
68
|
+
after those responsibilities are known. It may make the implementation leaner,
|
|
69
|
+
but it must not remove explicit requirements or reduce how correctness is
|
|
70
|
+
proven. The Compound Gate runs near the end, before final reporting, to decide
|
|
71
|
+
whether verified work produced reusable learning.
|
|
67
72
|
|
|
68
73
|
```mermaid
|
|
69
74
|
flowchart TD
|
|
70
75
|
A["Task arrives"] --> B["Read project router and current work"]
|
|
71
76
|
B --> C["Classify profile and lane"]
|
|
72
|
-
C -->
|
|
77
|
+
C --> R["Run Learning Recall for non-trivial work"]
|
|
78
|
+
R --> D{"Does this lane need a Superpowers workflow?"}
|
|
73
79
|
D -- "yes" --> E["Use the matching Superpowers skill"]
|
|
74
80
|
D -- "no" --> F["Use the local non-Superpowers lane rules"]
|
|
75
81
|
E --> G{"Would an explicit simplicity review help?"}
|
|
@@ -45,3 +45,8 @@ Stop hooks that call `pro-gov host-hook`. Use `pro-gov doctor --strict-hooks`
|
|
|
45
45
|
after sync to confirm the exit gate is wired. The hook enforces the Compound
|
|
46
46
|
Gate final-report marker; it does not replace verification, tests, or
|
|
47
47
|
`ce-compound` itself.
|
|
48
|
+
|
|
49
|
+
For non-trivial engineering work, run `pro-gov learn recall --query "<task
|
|
50
|
+
summary>"` before changing files so existing `docs/solutions/**` and
|
|
51
|
+
`CONCEPTS.md` lessons can shape the work without enabling a second default
|
|
52
|
+
workflow.
|
|
@@ -29,13 +29,13 @@ jobs:
|
|
|
29
29
|
name: doc-gov
|
|
30
30
|
runs-on: ubuntu-latest
|
|
31
31
|
steps:
|
|
32
|
-
- uses: actions/checkout@
|
|
32
|
+
- uses: actions/checkout@v7
|
|
33
33
|
|
|
34
|
-
- uses: pnpm/action-setup@
|
|
34
|
+
- uses: pnpm/action-setup@v6
|
|
35
35
|
with:
|
|
36
36
|
version: 11.9.0
|
|
37
37
|
|
|
38
|
-
- uses: actions/setup-node@
|
|
38
|
+
- uses: actions/setup-node@v6
|
|
39
39
|
with:
|
|
40
40
|
node-version: "24"
|
|
41
41
|
cache: pnpm
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
- `docs/governance/agents-routing/engineering-runtime-v0.9.md`, or
|
|
19
19
|
- `docs/governance/agents-routing/doc-only-v0.9.md`
|
|
20
20
|
7. `docs/reference/execution/current-work.md`
|
|
21
|
+
8. Before non-trivial implementation, debugging, release, architecture, or
|
|
22
|
+
migration work, run `pro-gov learn recall --query "<task summary>"` and read
|
|
23
|
+
any relevant prior-learning hits before changing files.
|
|
21
24
|
|
|
22
25
|
## Governance
|
|
23
26
|
|
|
@@ -39,6 +42,7 @@
|
|
|
39
42
|
- Point to the chosen agents-routing file from `docs/governance/agents-routing/`.
|
|
40
43
|
- Engineering projects may use Superpowers, Directed Development, GStack, or other external workflows only inside the selected lane; do not copy upstream integration guides into the target project by default.
|
|
41
44
|
- For engineering projects, Superpowers is the default engineering workflow. Compound Engineering is used by default only as the post-work Compound Gate through `ce-compound`; full CE workflows require an explicit user request.
|
|
45
|
+
- For engineering projects, PGS Learning Recall is the pre-work companion to that post-work gate: recall existing lessons first, then do the work, then decide whether a new lesson should be compounded.
|
|
42
46
|
- Engineering projects should have PGS Stop hooks wired for Codex, Claude Code, and Antigravity. Before final reporting after completed engineering work, report `Compound Gate: ran ce-compound -> <path>` or `Compound Gate: skipped -> <reason>`.
|
|
43
47
|
- Doc-only projects should say that Superpowers TDD and Directed Development are not enabled by default.
|
|
44
48
|
- External workflow systems such as Superpowers or GStack run inside the lane selected by this router. They must not replace this project router.
|
package/cli-guide.md
CHANGED
|
@@ -20,6 +20,7 @@ pro-gov portfolio check --config /path/to/portfolio.json
|
|
|
20
20
|
pro-gov portfolio plan --config /path/to/portfolio.json --target web-app --json
|
|
21
21
|
pro-gov portfolio assets-check --config /path/to/portfolio.json --json
|
|
22
22
|
pro-gov portfolio doctor --config /path/to/portfolio.json --json
|
|
23
|
+
pro-gov learn recall --query "release downstream sync" --target .
|
|
23
24
|
pro-gov lens inspect --target .
|
|
24
25
|
pro-gov lens report --target . --out .pro-gov/lens-report.md
|
|
25
26
|
pro-gov lens audit init --target /path/to/project --out audits/project/2026-07-01
|
|
@@ -42,6 +43,13 @@ but checks project-local seeds such as `AGENTS.md`, project policy, and current
|
|
|
42
43
|
work for presence only. It infers the installed profile when exactly one route
|
|
43
44
|
exists; `--profile` resolves an empty or temporarily ambiguous target.
|
|
44
45
|
|
|
46
|
+
`learn recall` is the pre-work companion to Compound Engineering's post-work
|
|
47
|
+
`ce-compound` learning records. It searches `docs/solutions/**` and
|
|
48
|
+
`CONCEPTS.md` in the target repository and returns the most relevant prior
|
|
49
|
+
lessons for the current task. Use it before non-trivial implementation,
|
|
50
|
+
debugging, release, architecture, or portfolio-sync work. It is read-only and
|
|
51
|
+
does not require a full local PGS checkout.
|
|
52
|
+
|
|
45
53
|
Full upstream-checkout commands:
|
|
46
54
|
|
|
47
55
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -2204,13 +2204,230 @@ function readFlag(args, flag) {
|
|
|
2204
2204
|
return value;
|
|
2205
2205
|
}
|
|
2206
2206
|
|
|
2207
|
+
// src/learning/recall.ts
|
|
2208
|
+
import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync12 } from "node:fs";
|
|
2209
|
+
import { basename as basename5, join as join15, relative as relative5 } from "node:path";
|
|
2210
|
+
function recallLearnings(root, options) {
|
|
2211
|
+
const query = options.query.trim();
|
|
2212
|
+
const terms = tokenize(query);
|
|
2213
|
+
const limit = Math.max(1, options.limit ?? 5);
|
|
2214
|
+
if (terms.length === 0) {
|
|
2215
|
+
return { query, hits: [] };
|
|
2216
|
+
}
|
|
2217
|
+
const records = loadLearningRecords(root);
|
|
2218
|
+
const hits = records.map((record) => {
|
|
2219
|
+
const score = scoreRecord(record, terms);
|
|
2220
|
+
return {
|
|
2221
|
+
relativePath: record.relativePath,
|
|
2222
|
+
title: record.title,
|
|
2223
|
+
score,
|
|
2224
|
+
summary: summarizeRecord(record, terms)
|
|
2225
|
+
};
|
|
2226
|
+
}).filter((hit) => hit.score > 0).sort((a, b) => b.score - a.score || a.relativePath.localeCompare(b.relativePath)).slice(0, limit);
|
|
2227
|
+
return { query, hits };
|
|
2228
|
+
}
|
|
2229
|
+
function loadLearningRecords(root) {
|
|
2230
|
+
const records = [];
|
|
2231
|
+
const solutionsDir = join15(root, "docs/solutions");
|
|
2232
|
+
if (existsSync15(solutionsDir)) {
|
|
2233
|
+
for (const path of listMarkdownFiles(solutionsDir)) {
|
|
2234
|
+
records.push(readLearningRecord(root, path));
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
const conceptsPath = join15(root, "CONCEPTS.md");
|
|
2238
|
+
if (existsSync15(conceptsPath)) {
|
|
2239
|
+
records.push(readLearningRecord(root, conceptsPath));
|
|
2240
|
+
}
|
|
2241
|
+
return records;
|
|
2242
|
+
}
|
|
2243
|
+
function listMarkdownFiles(dir) {
|
|
2244
|
+
const files = [];
|
|
2245
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
2246
|
+
const absolutePath = join15(dir, entry.name);
|
|
2247
|
+
if (entry.isDirectory()) {
|
|
2248
|
+
files.push(...listMarkdownFiles(absolutePath));
|
|
2249
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2250
|
+
files.push(absolutePath);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return files.sort();
|
|
2254
|
+
}
|
|
2255
|
+
function readLearningRecord(root, absolutePath) {
|
|
2256
|
+
const content = readFileSync12(absolutePath, "utf8");
|
|
2257
|
+
const parsed = splitFrontmatter(content);
|
|
2258
|
+
const body = parsed.body;
|
|
2259
|
+
return {
|
|
2260
|
+
relativePath: normalizePath(relative5(root, absolutePath)),
|
|
2261
|
+
title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
|
|
2262
|
+
metadata: parsed.frontmatter,
|
|
2263
|
+
body
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
function splitFrontmatter(content) {
|
|
2267
|
+
if (!content.startsWith("---\n")) {
|
|
2268
|
+
return { frontmatter: "", body: content };
|
|
2269
|
+
}
|
|
2270
|
+
const end = content.indexOf("\n---", 4);
|
|
2271
|
+
if (end === -1) {
|
|
2272
|
+
return { frontmatter: "", body: content };
|
|
2273
|
+
}
|
|
2274
|
+
return {
|
|
2275
|
+
frontmatter: content.slice(4, end).trim(),
|
|
2276
|
+
body: content.slice(end + "\n---".length).trim()
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
function findTitle(frontmatter, body) {
|
|
2280
|
+
const titleLine = frontmatter.split(/\r?\n/).find((line) => line.trim().toLowerCase().startsWith("title:"));
|
|
2281
|
+
if (titleLine) {
|
|
2282
|
+
return titleLine.slice(titleLine.indexOf(":") + 1).trim().replace(/^["']|["']$/g, "");
|
|
2283
|
+
}
|
|
2284
|
+
const heading = body.split(/\r?\n/).find((line) => line.startsWith("# "));
|
|
2285
|
+
return heading ? heading.slice(2).trim() : void 0;
|
|
2286
|
+
}
|
|
2287
|
+
function titleFromPath(path) {
|
|
2288
|
+
return basename5(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2289
|
+
}
|
|
2290
|
+
function scoreRecord(record, terms) {
|
|
2291
|
+
const title = record.title.toLowerCase();
|
|
2292
|
+
const metadata = record.metadata.toLowerCase();
|
|
2293
|
+
const path = record.relativePath.toLowerCase();
|
|
2294
|
+
const body = record.body.toLowerCase();
|
|
2295
|
+
let score = 0;
|
|
2296
|
+
for (const term of terms) {
|
|
2297
|
+
if (title.includes(term)) score += 6;
|
|
2298
|
+
if (metadata.includes(term)) score += 4;
|
|
2299
|
+
if (path.includes(term)) score += 2;
|
|
2300
|
+
if (body.includes(term)) score += 1;
|
|
2301
|
+
}
|
|
2302
|
+
return score;
|
|
2303
|
+
}
|
|
2304
|
+
function summarizeRecord(record, terms) {
|
|
2305
|
+
const lines = record.body.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
2306
|
+
const best = lines.map((line, index) => ({
|
|
2307
|
+
line,
|
|
2308
|
+
index,
|
|
2309
|
+
score: countTermMatches(line, terms) + (line.startsWith("#") ? 0.5 : 0)
|
|
2310
|
+
})).filter((candidate) => candidate.score > 0).sort((a, b) => b.score - a.score || a.index - b.index)[0];
|
|
2311
|
+
if (!best) {
|
|
2312
|
+
return truncate(cleanMarkdownLine(lines[0] ?? record.title), 180);
|
|
2313
|
+
}
|
|
2314
|
+
const cleaned = cleanMarkdownLine(best.line);
|
|
2315
|
+
if (!best.line.startsWith("#")) {
|
|
2316
|
+
return truncate(cleaned, 180);
|
|
2317
|
+
}
|
|
2318
|
+
const nextLine = lines.slice(best.index + 1).find((line) => !line.startsWith("#"));
|
|
2319
|
+
return truncate([cleaned, nextLine].filter(Boolean).join(" - "), 180);
|
|
2320
|
+
}
|
|
2321
|
+
function tokenize(input) {
|
|
2322
|
+
return [...new Set(input.toLowerCase().match(/[a-z0-9\u4e00-\u9fff]+/g) ?? [])].filter((term) => term.length > 1);
|
|
2323
|
+
}
|
|
2324
|
+
function truncate(input, maxLength) {
|
|
2325
|
+
return input.length <= maxLength ? input : `${input.slice(0, maxLength - 1)}\u2026`;
|
|
2326
|
+
}
|
|
2327
|
+
function normalizePath(path) {
|
|
2328
|
+
return path.split("\\").join("/");
|
|
2329
|
+
}
|
|
2330
|
+
function countTermMatches(input, terms) {
|
|
2331
|
+
const lower = input.toLowerCase();
|
|
2332
|
+
return terms.filter((term) => lower.includes(term)).length;
|
|
2333
|
+
}
|
|
2334
|
+
function cleanMarkdownLine(input) {
|
|
2335
|
+
return input.replace(/^#+\s*/, "").trim();
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
// src/commands/learn.ts
|
|
2339
|
+
function runLearn(args) {
|
|
2340
|
+
const [subcommand2, ...rest] = args;
|
|
2341
|
+
if (subcommand2 === "recall") return runLearnRecall(rest);
|
|
2342
|
+
printUsage2();
|
|
2343
|
+
return 1;
|
|
2344
|
+
}
|
|
2345
|
+
function runLearnRecall(args) {
|
|
2346
|
+
const options = parseRecallOptions(args);
|
|
2347
|
+
if (!options.ok) {
|
|
2348
|
+
console.error(options.error);
|
|
2349
|
+
printUsage2();
|
|
2350
|
+
return 1;
|
|
2351
|
+
}
|
|
2352
|
+
const result = recallLearnings(options.value.targetDir, {
|
|
2353
|
+
query: options.value.query,
|
|
2354
|
+
limit: options.value.limit
|
|
2355
|
+
});
|
|
2356
|
+
if (options.value.json) {
|
|
2357
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2358
|
+
return 0;
|
|
2359
|
+
}
|
|
2360
|
+
console.log(`Learning Recall: ${result.hits.length} hit${result.hits.length === 1 ? "" : "s"}`);
|
|
2361
|
+
for (const hit of result.hits) {
|
|
2362
|
+
console.log(`${hit.relativePath} ${hit.score} ${hit.title}`);
|
|
2363
|
+
if (hit.summary) console.log(` ${hit.summary}`);
|
|
2364
|
+
}
|
|
2365
|
+
if (result.hits.length === 0) {
|
|
2366
|
+
console.log("No relevant learning records found.");
|
|
2367
|
+
}
|
|
2368
|
+
return 0;
|
|
2369
|
+
}
|
|
2370
|
+
function parseRecallOptions(args) {
|
|
2371
|
+
let targetDir = process.cwd();
|
|
2372
|
+
let query = "";
|
|
2373
|
+
let limit = 5;
|
|
2374
|
+
let json = false;
|
|
2375
|
+
const positional = [];
|
|
2376
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
2377
|
+
const arg = args[index];
|
|
2378
|
+
if (arg === "--target") {
|
|
2379
|
+
const value = readFlagValue(args, index, "--target");
|
|
2380
|
+
if (!value.ok) return value;
|
|
2381
|
+
targetDir = value.value;
|
|
2382
|
+
index += 1;
|
|
2383
|
+
} else if (arg === "--query") {
|
|
2384
|
+
const value = readFlagValue(args, index, "--query");
|
|
2385
|
+
if (!value.ok) return value;
|
|
2386
|
+
query = value.value;
|
|
2387
|
+
index += 1;
|
|
2388
|
+
} else if (arg === "--limit") {
|
|
2389
|
+
const parsed = readFlagValue(args, index, "--limit");
|
|
2390
|
+
if (!parsed.ok) return parsed;
|
|
2391
|
+
const value = Number.parseInt(parsed.value, 10);
|
|
2392
|
+
if (!Number.isFinite(value) || value < 1) return { ok: false, error: "--limit must be a positive integer" };
|
|
2393
|
+
limit = value;
|
|
2394
|
+
index += 1;
|
|
2395
|
+
} else if (arg === "--json") {
|
|
2396
|
+
json = true;
|
|
2397
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
2398
|
+
return { ok: false, error: "Usage requested" };
|
|
2399
|
+
} else if (arg.startsWith("--")) {
|
|
2400
|
+
return { ok: false, error: `Unknown option: ${arg}` };
|
|
2401
|
+
} else {
|
|
2402
|
+
positional.push(arg);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
if (!query && positional.length > 0) {
|
|
2406
|
+
query = positional.join(" ");
|
|
2407
|
+
}
|
|
2408
|
+
if (!query.trim()) {
|
|
2409
|
+
return { ok: false, error: "Missing required --query <text>" };
|
|
2410
|
+
}
|
|
2411
|
+
return { ok: true, value: { targetDir, query, limit, json } };
|
|
2412
|
+
}
|
|
2413
|
+
function readFlagValue(args, index, flag) {
|
|
2414
|
+
const value = args[index + 1];
|
|
2415
|
+
if (!value || value.startsWith("--")) {
|
|
2416
|
+
return { ok: false, error: `Missing value for ${flag}` };
|
|
2417
|
+
}
|
|
2418
|
+
return { ok: true, value };
|
|
2419
|
+
}
|
|
2420
|
+
function printUsage2() {
|
|
2421
|
+
console.error("Usage: pro-gov learn recall --query <text> [--target <path>] [--limit <n>] [--json]");
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2207
2424
|
// src/commands/lens.ts
|
|
2208
2425
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2209
2426
|
import { dirname as dirname9 } from "node:path";
|
|
2210
2427
|
|
|
2211
2428
|
// src/lens/audit.ts
|
|
2212
|
-
import { existsSync as
|
|
2213
|
-
import { basename as
|
|
2429
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2430
|
+
import { basename as basename6, dirname as dirname8, join as join16 } from "node:path";
|
|
2214
2431
|
var REQUIRED_ARTIFACTS = [
|
|
2215
2432
|
"manifest.md",
|
|
2216
2433
|
"raw/project-lens/architecture-lens.md",
|
|
@@ -2230,20 +2447,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
|
|
|
2230
2447
|
version: 1,
|
|
2231
2448
|
target: {
|
|
2232
2449
|
path: targetDir,
|
|
2233
|
-
name:
|
|
2450
|
+
name: basename6(targetDir) || "target"
|
|
2234
2451
|
},
|
|
2235
2452
|
requiredArtifacts: [...REQUIRED_ARTIFACTS]
|
|
2236
2453
|
};
|
|
2237
2454
|
mkdirSync6(auditDir, { recursive: true });
|
|
2238
|
-
writeJson(
|
|
2455
|
+
writeJson(join16(auditDir, "audit.contract.json"), contract);
|
|
2239
2456
|
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
2240
|
-
writeTemplate(
|
|
2457
|
+
writeTemplate(join16(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
|
|
2241
2458
|
}
|
|
2242
2459
|
return contract;
|
|
2243
2460
|
}
|
|
2244
2461
|
function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
2245
|
-
const contractPath =
|
|
2246
|
-
if (!
|
|
2462
|
+
const contractPath = join16(auditDir, "audit.contract.json");
|
|
2463
|
+
if (!existsSync16(contractPath)) {
|
|
2247
2464
|
return {
|
|
2248
2465
|
ok: false,
|
|
2249
2466
|
auditDir,
|
|
@@ -2257,7 +2474,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
|
2257
2474
|
}
|
|
2258
2475
|
let contract;
|
|
2259
2476
|
try {
|
|
2260
|
-
contract = JSON.parse(
|
|
2477
|
+
contract = JSON.parse(readFileSync13(contractPath, "utf8"));
|
|
2261
2478
|
} catch (error) {
|
|
2262
2479
|
return {
|
|
2263
2480
|
ok: false,
|
|
@@ -2290,12 +2507,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
|
|
|
2290
2507
|
}
|
|
2291
2508
|
}
|
|
2292
2509
|
for (const artifactPath of REQUIRED_ARTIFACTS) {
|
|
2293
|
-
const absolutePath =
|
|
2294
|
-
if (!
|
|
2510
|
+
const absolutePath = join16(auditDir, artifactPath);
|
|
2511
|
+
if (!existsSync16(absolutePath)) {
|
|
2295
2512
|
issues.push({ type: "missing-required-artifact", path: artifactPath });
|
|
2296
2513
|
continue;
|
|
2297
2514
|
}
|
|
2298
|
-
const content =
|
|
2515
|
+
const content = readFileSync13(absolutePath, "utf8");
|
|
2299
2516
|
if (isPendingArtifact(content)) {
|
|
2300
2517
|
issues.push({ type: "artifact-not-complete", path: artifactPath });
|
|
2301
2518
|
} else if (hasTemplateBody(content)) {
|
|
@@ -2555,8 +2772,8 @@ function bulletList(values) {
|
|
|
2555
2772
|
|
|
2556
2773
|
// src/lens/scan.ts
|
|
2557
2774
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2558
|
-
import { existsSync as
|
|
2559
|
-
import { join as
|
|
2775
|
+
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync4 } from "node:fs";
|
|
2776
|
+
import { join as join17, relative as relative6 } from "node:path";
|
|
2560
2777
|
var ignoredDirectories = /* @__PURE__ */ new Set([
|
|
2561
2778
|
".git",
|
|
2562
2779
|
".next",
|
|
@@ -2573,24 +2790,24 @@ function scanProjectLensTarget(targetDir, options = {}) {
|
|
|
2573
2790
|
return {
|
|
2574
2791
|
targetDir,
|
|
2575
2792
|
aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter(
|
|
2576
|
-
(file) =>
|
|
2793
|
+
(file) => existsSync17(join17(targetDir, file))
|
|
2577
2794
|
),
|
|
2578
2795
|
aiConfigFiles: [],
|
|
2579
2796
|
packageJson,
|
|
2580
2797
|
docs: {
|
|
2581
|
-
hasDocsDirectory:
|
|
2798
|
+
hasDocsDirectory: existsSync17(join17(targetDir, "docs")),
|
|
2582
2799
|
markdownFileCount: markdownFiles.length,
|
|
2583
2800
|
governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
|
|
2584
2801
|
},
|
|
2585
2802
|
git: readGitState(targetDir),
|
|
2586
|
-
largeFiles: files.map((file) => ({ path: file, bytes: statSync4(
|
|
2803
|
+
largeFiles: files.map((file) => ({ path: file, bytes: statSync4(join17(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
|
|
2587
2804
|
};
|
|
2588
2805
|
}
|
|
2589
2806
|
function readPackageJson(targetDir) {
|
|
2590
|
-
const packageJsonPath =
|
|
2591
|
-
if (!
|
|
2807
|
+
const packageJsonPath = join17(targetDir, "package.json");
|
|
2808
|
+
if (!existsSync17(packageJsonPath)) return void 0;
|
|
2592
2809
|
try {
|
|
2593
|
-
const packageJson = JSON.parse(
|
|
2810
|
+
const packageJson = JSON.parse(readFileSync14(packageJsonPath, "utf8"));
|
|
2594
2811
|
return {
|
|
2595
2812
|
scripts: Object.keys(packageJson.scripts ?? {}).sort(),
|
|
2596
2813
|
dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
|
|
@@ -2625,13 +2842,13 @@ function listProjectFiles(targetDir) {
|
|
|
2625
2842
|
return files.sort();
|
|
2626
2843
|
}
|
|
2627
2844
|
function collectFiles2(rootDir, currentDir, files) {
|
|
2628
|
-
if (!
|
|
2629
|
-
for (const entry of
|
|
2845
|
+
if (!existsSync17(currentDir)) return;
|
|
2846
|
+
for (const entry of readdirSync7(currentDir, { withFileTypes: true })) {
|
|
2630
2847
|
if (entry.isDirectory()) {
|
|
2631
2848
|
if (ignoredDirectories.has(entry.name)) continue;
|
|
2632
|
-
collectFiles2(rootDir,
|
|
2849
|
+
collectFiles2(rootDir, join17(currentDir, entry.name), files);
|
|
2633
2850
|
} else if (entry.isFile()) {
|
|
2634
|
-
files.push(toUnixPath4(
|
|
2851
|
+
files.push(toUnixPath4(relative6(rootDir, join17(currentDir, entry.name))));
|
|
2635
2852
|
}
|
|
2636
2853
|
}
|
|
2637
2854
|
}
|
|
@@ -2651,14 +2868,14 @@ function runLens(args) {
|
|
|
2651
2868
|
if (subcommand2 === "audit") {
|
|
2652
2869
|
return runLensAudit(rest);
|
|
2653
2870
|
}
|
|
2654
|
-
|
|
2871
|
+
printUsage3();
|
|
2655
2872
|
return 1;
|
|
2656
2873
|
}
|
|
2657
2874
|
function runLensInspect(args, subcommand2) {
|
|
2658
2875
|
const options = parseLensOptions(args, subcommand2);
|
|
2659
2876
|
if (!options.ok) {
|
|
2660
2877
|
console.error(options.error);
|
|
2661
|
-
|
|
2878
|
+
printUsage3();
|
|
2662
2879
|
return 1;
|
|
2663
2880
|
}
|
|
2664
2881
|
const report = scanProjectLensTarget(options.value.targetDir);
|
|
@@ -2673,12 +2890,12 @@ function runLensReport(args) {
|
|
|
2673
2890
|
const options = parseLensOptions(args, "report");
|
|
2674
2891
|
if (!options.ok) {
|
|
2675
2892
|
console.error(options.error);
|
|
2676
|
-
|
|
2893
|
+
printUsage3();
|
|
2677
2894
|
return 1;
|
|
2678
2895
|
}
|
|
2679
2896
|
if (!options.value.outPath) {
|
|
2680
2897
|
console.error("Expected --out <path>");
|
|
2681
|
-
|
|
2898
|
+
printUsage3();
|
|
2682
2899
|
return 1;
|
|
2683
2900
|
}
|
|
2684
2901
|
const report = scanProjectLensTarget(options.value.targetDir);
|
|
@@ -2694,12 +2911,12 @@ function runLensAudit(args) {
|
|
|
2694
2911
|
const options = parseLensOptions(rest, "audit init");
|
|
2695
2912
|
if (!options.ok) {
|
|
2696
2913
|
console.error(options.error);
|
|
2697
|
-
|
|
2914
|
+
printUsage3();
|
|
2698
2915
|
return 1;
|
|
2699
2916
|
}
|
|
2700
2917
|
if (!options.value.outPath) {
|
|
2701
2918
|
console.error("Expected --out <path>");
|
|
2702
|
-
|
|
2919
|
+
printUsage3();
|
|
2703
2920
|
return 1;
|
|
2704
2921
|
}
|
|
2705
2922
|
createProjectLensAuditPackage(options.value.targetDir, options.value.outPath);
|
|
@@ -2710,12 +2927,12 @@ function runLensAudit(args) {
|
|
|
2710
2927
|
const options = parseLensOptions(rest, "audit check");
|
|
2711
2928
|
if (!options.ok) {
|
|
2712
2929
|
console.error(options.error);
|
|
2713
|
-
|
|
2930
|
+
printUsage3();
|
|
2714
2931
|
return 1;
|
|
2715
2932
|
}
|
|
2716
2933
|
if (!options.value.auditDir) {
|
|
2717
2934
|
console.error("Expected --dir <path>");
|
|
2718
|
-
|
|
2935
|
+
printUsage3();
|
|
2719
2936
|
return 1;
|
|
2720
2937
|
}
|
|
2721
2938
|
const result = checkProjectLensAuditPackage(options.value.auditDir, { mode: options.value.auditMode });
|
|
@@ -2726,7 +2943,7 @@ function runLensAudit(args) {
|
|
|
2726
2943
|
}
|
|
2727
2944
|
return result.ok ? 0 : 1;
|
|
2728
2945
|
}
|
|
2729
|
-
|
|
2946
|
+
printUsage3();
|
|
2730
2947
|
return 1;
|
|
2731
2948
|
}
|
|
2732
2949
|
function parseLensOptions(args, subcommand2) {
|
|
@@ -2778,7 +2995,7 @@ function parseLensOptions(args, subcommand2) {
|
|
|
2778
2995
|
}
|
|
2779
2996
|
return { ok: true, value: options };
|
|
2780
2997
|
}
|
|
2781
|
-
function
|
|
2998
|
+
function printUsage3() {
|
|
2782
2999
|
console.error("Usage: pro-gov lens scan [--target <path>] [--json]");
|
|
2783
3000
|
console.error("Usage: pro-gov lens inspect [--target <path>] [--format text|json]");
|
|
2784
3001
|
console.error("Usage: pro-gov lens report --target <path> --out <path>");
|
|
@@ -2787,16 +3004,16 @@ function printUsage2() {
|
|
|
2787
3004
|
}
|
|
2788
3005
|
|
|
2789
3006
|
// src/commands/portfolio.ts
|
|
2790
|
-
import { existsSync as
|
|
2791
|
-
import { join as
|
|
3007
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
3008
|
+
import { join as join20 } from "node:path";
|
|
2792
3009
|
|
|
2793
3010
|
// src/portfolio/manifest.ts
|
|
2794
|
-
import { existsSync as
|
|
3011
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
|
|
2795
3012
|
import { dirname as dirname10, isAbsolute as isAbsolute3, resolve as resolve4 } from "node:path";
|
|
2796
3013
|
function loadPortfolioManifest(configPath) {
|
|
2797
3014
|
let parsed;
|
|
2798
3015
|
try {
|
|
2799
|
-
parsed = JSON.parse(
|
|
3016
|
+
parsed = JSON.parse(readFileSync15(configPath, "utf8"));
|
|
2800
3017
|
} catch (error) {
|
|
2801
3018
|
return {
|
|
2802
3019
|
configPath,
|
|
@@ -2956,7 +3173,7 @@ function validateEndpoint(value, field, issues) {
|
|
|
2956
3173
|
});
|
|
2957
3174
|
return;
|
|
2958
3175
|
}
|
|
2959
|
-
if (!
|
|
3176
|
+
if (!existsSync18(value.path)) {
|
|
2960
3177
|
issues.push({
|
|
2961
3178
|
type: "missing-path",
|
|
2962
3179
|
id: typeof value.id === "string" ? value.id : void 0,
|
|
@@ -3027,9 +3244,9 @@ function isRecord2(value) {
|
|
|
3027
3244
|
|
|
3028
3245
|
// src/portfolio/doctor.ts
|
|
3029
3246
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
3030
|
-
import { existsSync as
|
|
3247
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "node:fs";
|
|
3031
3248
|
import { createRequire as createRequire2 } from "node:module";
|
|
3032
|
-
import { dirname as dirname11, join as
|
|
3249
|
+
import { dirname as dirname11, join as join19 } from "node:path";
|
|
3033
3250
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
3034
3251
|
|
|
3035
3252
|
// src/host-tooling/inventory.ts
|
|
@@ -3120,13 +3337,13 @@ function isRecord3(value) {
|
|
|
3120
3337
|
}
|
|
3121
3338
|
|
|
3122
3339
|
// src/portfolio/asset-state.ts
|
|
3123
|
-
import { existsSync as
|
|
3124
|
-
import { join as
|
|
3340
|
+
import { existsSync as existsSync19, lstatSync as lstatSync5, readFileSync as readFileSync16 } from "node:fs";
|
|
3341
|
+
import { join as join18 } from "node:path";
|
|
3125
3342
|
function comparePortfolioAssetState(options) {
|
|
3126
3343
|
const expectedManifest = readPlanDocument(options.expectedPlan, ".pro-gov/assets.json");
|
|
3127
3344
|
const expectedLock = readPlanDocument(options.expectedPlan, ".pro-gov/assets.lock.json");
|
|
3128
|
-
const currentManifest = readJsonFile(
|
|
3129
|
-
const currentLock = readJsonFile(
|
|
3345
|
+
const currentManifest = readJsonFile(join18(options.targetDir, ".pro-gov/assets.json"));
|
|
3346
|
+
const currentLock = readJsonFile(join18(options.targetDir, ".pro-gov/assets.lock.json"));
|
|
3130
3347
|
const issues = [];
|
|
3131
3348
|
if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
|
|
3132
3349
|
issues.push({
|
|
@@ -3149,7 +3366,7 @@ function comparePortfolioAssetState(options) {
|
|
|
3149
3366
|
const expectedTargets = new Set((expectedLock?.assets ?? []).map((entry) => entry.targetPath));
|
|
3150
3367
|
for (const entry of currentLock?.assets ?? []) {
|
|
3151
3368
|
if (expectedTargets.has(entry.targetPath)) continue;
|
|
3152
|
-
const targetAbsolutePath =
|
|
3369
|
+
const targetAbsolutePath = join18(options.targetDir, entry.targetPath);
|
|
3153
3370
|
if (!pathIsSymlink(targetAbsolutePath)) continue;
|
|
3154
3371
|
issues.push({
|
|
3155
3372
|
type: "orphaned-managed-symlink",
|
|
@@ -3171,9 +3388,9 @@ function readPlanDocument(plan, targetPath) {
|
|
|
3171
3388
|
}
|
|
3172
3389
|
}
|
|
3173
3390
|
function readJsonFile(path) {
|
|
3174
|
-
if (!
|
|
3391
|
+
if (!existsSync19(path)) return void 0;
|
|
3175
3392
|
try {
|
|
3176
|
-
return JSON.parse(
|
|
3393
|
+
return JSON.parse(readFileSync16(path, "utf8"));
|
|
3177
3394
|
} catch {
|
|
3178
3395
|
return void 0;
|
|
3179
3396
|
}
|
|
@@ -3222,11 +3439,11 @@ function inspectPortfolio(options) {
|
|
|
3222
3439
|
function inspectTarget(options) {
|
|
3223
3440
|
const { target } = options;
|
|
3224
3441
|
const issues = [];
|
|
3225
|
-
const packageJson = readJson2(
|
|
3442
|
+
const packageJson = readJson2(join19(target.path, "package.json"));
|
|
3226
3443
|
const packages = {};
|
|
3227
3444
|
for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
|
|
3228
3445
|
const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
|
|
3229
|
-
const installedPackage = readJson2(
|
|
3446
|
+
const installedPackage = readJson2(join19(target.path, "node_modules", packageName, "package.json"));
|
|
3230
3447
|
const installed = installedPackage?.version;
|
|
3231
3448
|
const expected = options.expectedPackageVersions[packageName];
|
|
3232
3449
|
packages[packageName] = { declared, installed, expected };
|
|
@@ -3272,7 +3489,7 @@ function inspectTarget(options) {
|
|
|
3272
3489
|
registry: options.registry,
|
|
3273
3490
|
bundles: options.bundles,
|
|
3274
3491
|
bundleIds: target.assetBundles ?? [],
|
|
3275
|
-
host: "codex"
|
|
3492
|
+
host: readTargetAssetHost(target.path) ?? "codex"
|
|
3276
3493
|
});
|
|
3277
3494
|
issues.push(...comparePortfolioAssetState({ targetDir: target.path, expectedPlan }).issues);
|
|
3278
3495
|
} catch (error) {
|
|
@@ -3280,7 +3497,7 @@ function inspectTarget(options) {
|
|
|
3280
3497
|
type: "asset-lock-drift",
|
|
3281
3498
|
message: error instanceof Error ? error.message : String(error)
|
|
3282
3499
|
});
|
|
3283
|
-
if (!
|
|
3500
|
+
if (!existsSync20(join19(target.path, ".pro-gov/assets.json"))) {
|
|
3284
3501
|
issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
|
|
3285
3502
|
}
|
|
3286
3503
|
}
|
|
@@ -3294,9 +3511,16 @@ function inspectTarget(options) {
|
|
|
3294
3511
|
issues: deduplicateIssues(issues)
|
|
3295
3512
|
};
|
|
3296
3513
|
}
|
|
3514
|
+
function readTargetAssetHost(targetDir) {
|
|
3515
|
+
const lockfile = readJson2(join19(targetDir, ".pro-gov/assets.lock.json"));
|
|
3516
|
+
return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
|
|
3517
|
+
}
|
|
3518
|
+
function isAssetRegistryHost(value) {
|
|
3519
|
+
return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
|
|
3520
|
+
}
|
|
3297
3521
|
function runTargetChecks(target) {
|
|
3298
|
-
const proGovCli =
|
|
3299
|
-
const docGovCli =
|
|
3522
|
+
const proGovCli = join19(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
|
|
3523
|
+
const docGovCli = join19(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
|
|
3300
3524
|
const commands = [
|
|
3301
3525
|
{
|
|
3302
3526
|
name: "pro-gov doctor",
|
|
@@ -3307,7 +3531,7 @@ function runTargetChecks(target) {
|
|
|
3307
3531
|
{ name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
|
|
3308
3532
|
];
|
|
3309
3533
|
return commands.map((command2) => {
|
|
3310
|
-
if (!
|
|
3534
|
+
if (!existsSync20(command2.cli)) return { name: command2.name, status: null };
|
|
3311
3535
|
const result = spawnSync6(process.execPath, [command2.cli, ...command2.args], {
|
|
3312
3536
|
cwd: target.path,
|
|
3313
3537
|
encoding: "utf8",
|
|
@@ -3348,16 +3572,16 @@ function getExpectedPackageVersions() {
|
|
|
3348
3572
|
function findOwnPackageJson() {
|
|
3349
3573
|
let current = dirname11(fileURLToPath3(import.meta.url));
|
|
3350
3574
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
3351
|
-
const candidate =
|
|
3352
|
-
if (
|
|
3575
|
+
const candidate = join19(current, "package.json");
|
|
3576
|
+
if (existsSync20(candidate)) return candidate;
|
|
3353
3577
|
current = dirname11(current);
|
|
3354
3578
|
}
|
|
3355
3579
|
return "";
|
|
3356
3580
|
}
|
|
3357
3581
|
function readJson2(path) {
|
|
3358
|
-
if (!path || !
|
|
3582
|
+
if (!path || !existsSync20(path)) return void 0;
|
|
3359
3583
|
try {
|
|
3360
|
-
return JSON.parse(
|
|
3584
|
+
return JSON.parse(readFileSync17(path, "utf8"));
|
|
3361
3585
|
} catch {
|
|
3362
3586
|
return void 0;
|
|
3363
3587
|
}
|
|
@@ -3379,14 +3603,14 @@ function runPortfolio(args) {
|
|
|
3379
3603
|
if (subcommand2 === "plan") return runPortfolioPlan(rest);
|
|
3380
3604
|
if (subcommand2 === "assets-check") return runPortfolioAssetsCheck(rest);
|
|
3381
3605
|
if (subcommand2 === "doctor") return runPortfolioDoctor(rest);
|
|
3382
|
-
|
|
3606
|
+
printUsage4();
|
|
3383
3607
|
return 1;
|
|
3384
3608
|
}
|
|
3385
3609
|
function runPortfolioDoctor(args) {
|
|
3386
3610
|
const options = parsePortfolioOptions(args);
|
|
3387
3611
|
if (!options.ok) {
|
|
3388
3612
|
console.error(options.error);
|
|
3389
|
-
|
|
3613
|
+
printUsage4();
|
|
3390
3614
|
return 1;
|
|
3391
3615
|
}
|
|
3392
3616
|
const loaded = loadPortfolioManifest(options.value.configPath);
|
|
@@ -3436,7 +3660,7 @@ function runPortfolioCheck(args) {
|
|
|
3436
3660
|
const options = parsePortfolioOptions(args);
|
|
3437
3661
|
if (!options.ok) {
|
|
3438
3662
|
console.error(options.error);
|
|
3439
|
-
|
|
3663
|
+
printUsage4();
|
|
3440
3664
|
return 1;
|
|
3441
3665
|
}
|
|
3442
3666
|
const loaded = loadPortfolioManifest(options.value.configPath);
|
|
@@ -3468,7 +3692,7 @@ function runPortfolioPlan(args) {
|
|
|
3468
3692
|
const options = parsePortfolioOptions(args);
|
|
3469
3693
|
if (!options.ok) {
|
|
3470
3694
|
console.error(options.error);
|
|
3471
|
-
|
|
3695
|
+
printUsage4();
|
|
3472
3696
|
return 1;
|
|
3473
3697
|
}
|
|
3474
3698
|
const loaded = loadPortfolioManifest(options.value.configPath);
|
|
@@ -3542,7 +3766,7 @@ function runPortfolioAssetsCheck(args) {
|
|
|
3542
3766
|
const options = parsePortfolioOptions(args);
|
|
3543
3767
|
if (!options.ok) {
|
|
3544
3768
|
console.error(options.error);
|
|
3545
|
-
|
|
3769
|
+
printUsage4();
|
|
3546
3770
|
return 1;
|
|
3547
3771
|
}
|
|
3548
3772
|
const loaded = loadPortfolioManifest(options.value.configPath);
|
|
@@ -3658,7 +3882,7 @@ function parsePortfolioOptions(args) {
|
|
|
3658
3882
|
index += 1;
|
|
3659
3883
|
} else if (arg === "--host") {
|
|
3660
3884
|
const host = args[index + 1];
|
|
3661
|
-
if (!isHost2(host)) return { ok: false, error: "Expected --host codex|claude-code" };
|
|
3885
|
+
if (!isHost2(host)) return { ok: false, error: "Expected --host codex|claude-code|gemini-cli|antigravity" };
|
|
3662
3886
|
options.host = host;
|
|
3663
3887
|
index += 1;
|
|
3664
3888
|
} else if (arg === "--json") {
|
|
@@ -3674,10 +3898,10 @@ function isHost2(value) {
|
|
|
3674
3898
|
return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
|
|
3675
3899
|
}
|
|
3676
3900
|
function findPortfolioAgentAssetsDir(manifest) {
|
|
3677
|
-
const agentAssetsDir = manifest?.executionEngine?.path ?
|
|
3678
|
-
return agentAssetsDir &&
|
|
3901
|
+
const agentAssetsDir = manifest?.executionEngine?.path ? join20(manifest.executionEngine.path, "agent-assets") : void 0;
|
|
3902
|
+
return agentAssetsDir && existsSync21(join20(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
|
|
3679
3903
|
}
|
|
3680
|
-
function
|
|
3904
|
+
function printUsage4() {
|
|
3681
3905
|
console.error("Usage:");
|
|
3682
3906
|
console.error(" pro-gov portfolio check --config <path> [--json]");
|
|
3683
3907
|
console.error(" pro-gov portfolio plan --config <path> [--target <id|all>] [--host codex|claude-code|gemini-cli|antigravity] [--json]");
|
|
@@ -3686,8 +3910,8 @@ function printUsage3() {
|
|
|
3686
3910
|
}
|
|
3687
3911
|
|
|
3688
3912
|
// src/commands/sync.ts
|
|
3689
|
-
import { existsSync as
|
|
3690
|
-
import { join as
|
|
3913
|
+
import { existsSync as existsSync22, readFileSync as readFileSync18 } from "node:fs";
|
|
3914
|
+
import { join as join21 } from "node:path";
|
|
3691
3915
|
function runSync(args) {
|
|
3692
3916
|
if (!args.includes("--check")) {
|
|
3693
3917
|
console.error("pro-gov sync is read-only and requires --check.");
|
|
@@ -3714,16 +3938,16 @@ function runSync(args) {
|
|
|
3714
3938
|
console.log("pro-gov sync check");
|
|
3715
3939
|
console.log(`profile: ${profile}`);
|
|
3716
3940
|
for (const file of planStarterFiles(profile)) {
|
|
3717
|
-
const targetPath =
|
|
3718
|
-
if (!
|
|
3941
|
+
const targetPath = join21(process.cwd(), file.targetPath);
|
|
3942
|
+
if (!existsSync22(targetPath)) {
|
|
3719
3943
|
if (file.ownership === "optional-guardrail") continue;
|
|
3720
3944
|
console.log(`missing: ${file.targetPath}`);
|
|
3721
3945
|
differences += 1;
|
|
3722
3946
|
continue;
|
|
3723
3947
|
}
|
|
3724
3948
|
if (file.ownership === "project-local-seed") continue;
|
|
3725
|
-
const source =
|
|
3726
|
-
const target =
|
|
3949
|
+
const source = readFileSync18(file.absoluteSourcePath, "utf8");
|
|
3950
|
+
const target = readFileSync18(targetPath, "utf8");
|
|
3727
3951
|
if (source !== target) {
|
|
3728
3952
|
console.log(`different: ${file.targetPath}`);
|
|
3729
3953
|
differences += 1;
|
|
@@ -3738,7 +3962,7 @@ function runSync(args) {
|
|
|
3738
3962
|
}
|
|
3739
3963
|
function inferInstalledProfile(root) {
|
|
3740
3964
|
const installed = ["engineering-runtime", "doc-only"].filter(
|
|
3741
|
-
(profile) =>
|
|
3965
|
+
(profile) => existsSync22(join21(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
|
|
3742
3966
|
);
|
|
3743
3967
|
return installed.length === 1 ? installed[0] : void 0;
|
|
3744
3968
|
}
|
|
@@ -3762,6 +3986,7 @@ var COMMANDS = [
|
|
|
3762
3986
|
"portfolio plan --config <path> [--target <id|all>] [--json]",
|
|
3763
3987
|
"portfolio assets-check --config <path> [--target <id|all>] [--json]",
|
|
3764
3988
|
"portfolio doctor --config <path> [--target <id|all>] [--json]",
|
|
3989
|
+
"learn recall --query <text> [--target <path>] [--limit <n>] [--json]",
|
|
3765
3990
|
"lens scan [--target <path>] [--json]",
|
|
3766
3991
|
"lens inspect [--target <path>] [--format text|json]",
|
|
3767
3992
|
"lens report --target <path> --out <path>",
|
|
@@ -3780,6 +4005,7 @@ async function main() {
|
|
|
3780
4005
|
return command ? 0 : 1;
|
|
3781
4006
|
}
|
|
3782
4007
|
if (command === "assets") return runAssets(process.argv.slice(3));
|
|
4008
|
+
if (command === "learn") return runLearn(process.argv.slice(3));
|
|
3783
4009
|
if (command === "lens") return runLens(process.argv.slice(3));
|
|
3784
4010
|
if (command === "portfolio") return runPortfolio(process.argv.slice(3));
|
|
3785
4011
|
if (command === "host-hook") return runHostHook(process.argv.slice(3));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pieai/pro-gov",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Project-level distribution kit for Project Governance System.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@pieai/doc-gov": "^0.3.
|
|
38
|
+
"@pieai/doc-gov": "^0.3.14"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "24.13.2",
|