aienvmp 0.1.48 → 0.1.50
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/BUGFIXES.md +18 -0
- package/CHANGELOG.md +21 -0
- package/README.md +7 -2
- package/action.yml +26 -0
- package/examples/github-action.yml +5 -0
- package/package.json +1 -1
- package/src/cli.js +7 -2
- package/src/commands/summary.js +96 -0
- package/src/commands/sync.js +4 -1
- package/src/contract.js +6 -0
- package/src/fsutil.js +5 -1
- package/src/paths.js +4 -0
- package/src/preflight.js +1 -0
package/BUGFIXES.md
CHANGED
|
@@ -76,6 +76,24 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
76
76
|
- Fix: `aienvmp sbom --format cyclonedx-lite` and `.aienvmp/sbom.cdx.json` now export project-manifest packages in a CycloneDX-compatible shape with explicit limitations.
|
|
77
77
|
- Verification: tests cover component mapping, vulnerability hints, sync output, schema metadata, and dashboard links.
|
|
78
78
|
|
|
79
|
+
### CI example did not surface SBOM artifacts clearly
|
|
80
|
+
|
|
81
|
+
- Issue: sync could generate SBOM artifacts, but the GitHub Action and example upload list did not make them explicit.
|
|
82
|
+
- Fix: the Action now has `write-sbom` and the example uploads both native and CycloneDX-lite SBOM artifacts.
|
|
83
|
+
- Verification: Action tests cover input metadata, SBOM write commands, and example artifact paths.
|
|
84
|
+
|
|
85
|
+
### CI and AI review still needed a short handoff view
|
|
86
|
+
|
|
87
|
+
- Issue: `status.json` was AI-friendly, but GitHub Actions and quick human review still required opening JSON artifacts or the dashboard.
|
|
88
|
+
- Fix: `aienvmp summary` now writes `.aienvmp/summary.md`, `sync` creates it by default, and the GitHub Action appends it to `GITHUB_STEP_SUMMARY`.
|
|
89
|
+
- Verification: tests cover summary rendering, sync artifact creation, schema metadata, Action wiring, and example upload paths.
|
|
90
|
+
|
|
91
|
+
### Windows-created JSON files could be missed
|
|
92
|
+
|
|
93
|
+
- Issue: UTF-8 BOM JSON files created by some Windows tools could fail JSON parsing and make dependency snapshots look empty.
|
|
94
|
+
- Fix: `readJson` now strips a leading UTF-8 BOM before parsing.
|
|
95
|
+
- Verification: regression tests cover BOM JSON parsing and Windows smoke testing confirmed dependency counts in `summary.md`.
|
|
96
|
+
|
|
79
97
|
## Template
|
|
80
98
|
|
|
81
99
|
### Title
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.50
|
|
4
|
+
|
|
5
|
+
- Added `aienvmp summary` for a compact Markdown AI/CI handoff view.
|
|
6
|
+
- Added `.aienvmp/summary.md` writing through the default `sync` flow.
|
|
7
|
+
- Added `summary` to preflight artifacts and the stable output contract.
|
|
8
|
+
- Added a GitHub Action `write-summary` input.
|
|
9
|
+
- Appended `.aienvmp/summary.md` to GitHub Step Summary when the Action runs in GitHub Actions.
|
|
10
|
+
- Updated the GitHub Action example and README to include the summary artifact.
|
|
11
|
+
- Fixed UTF-8 BOM JSON parsing so Windows-created `package.json` files are scanned correctly.
|
|
12
|
+
- Added regression tests for summary rendering, sync output, schema metadata, Action Step Summary wiring, and BOM JSON parsing.
|
|
13
|
+
|
|
14
|
+
## 0.1.49
|
|
15
|
+
|
|
16
|
+
- Added a `write-sbom` GitHub Action input for explicit SBOM artifact generation.
|
|
17
|
+
- Added Action steps for writing `.aienvmp/sbom.json` and `.aienvmp/sbom.cdx.json`.
|
|
18
|
+
- Updated the GitHub Action example to enable SBOM writing.
|
|
19
|
+
- Added native light SBOM artifact upload paths to the example workflow.
|
|
20
|
+
- Added CycloneDX-lite artifact upload paths to the example workflow.
|
|
21
|
+
- Updated README CI usage to mention SBOM artifacts.
|
|
22
|
+
- Added regression coverage for Action SBOM inputs, commands, and example upload paths.
|
|
23
|
+
|
|
3
24
|
## 0.1.48
|
|
4
25
|
|
|
5
26
|
- Added `aienvmp sbom --format cyclonedx-lite` for a lightweight CycloneDX-compatible export.
|
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Warnings are advisory by default. Use `doctor --strict <scope>` only when you wa
|
|
|
34
34
|
```text
|
|
35
35
|
AIENV.md # Markdown env map for AI agents
|
|
36
36
|
.aienvmp/status.json # first AI read: clear/review, next command, nextAgent hint
|
|
37
|
+
.aienvmp/summary.md # compact AI/CI summary
|
|
37
38
|
.aienvmp/manifest.json # runtime map + light SBOM
|
|
38
39
|
.aienvmp/sbom.json # standalone AI-readable light SBOM
|
|
39
40
|
.aienvmp/sbom.cdx.json # CycloneDX-lite export from project manifests
|
|
@@ -47,6 +48,7 @@ AIENV.md # Markdown env map for AI agents
|
|
|
47
48
|
|
|
48
49
|
- `status`, `context`, `plan`, and `handoff` share one preflight contract.
|
|
49
50
|
- `schema --json` prints the stable AI-readable output contract without scanning.
|
|
51
|
+
- `summary.md` is the short CI/AI handoff view.
|
|
50
52
|
- `status.json.nextAgent` tells the next AI what to read and whether to review first.
|
|
51
53
|
- `dependencyReadSet` lists manifests and lockfiles before package or security changes.
|
|
52
54
|
- `sbomRisk` gives AI a compact light-SBOM risk level, signals, and next command.
|
|
@@ -71,8 +73,9 @@ npx aienvmp snippet gemini
|
|
|
71
73
|
## Commands
|
|
72
74
|
|
|
73
75
|
```bash
|
|
74
|
-
aienvmp sync # update env map, status,
|
|
76
|
+
aienvmp sync # update env map, status, summary, SBOM, dashboard
|
|
75
77
|
aienvmp status --write # refresh compact AI status
|
|
78
|
+
aienvmp summary --write # refresh compact Markdown summary
|
|
76
79
|
aienvmp context --json # AI decision contract
|
|
77
80
|
aienvmp sbom --json # standalone light SBOM
|
|
78
81
|
aienvmp sbom --format cyclonedx-lite --json
|
|
@@ -87,13 +90,15 @@ aienvmp doctor --strict security|policy|coordination|all
|
|
|
87
90
|
|
|
88
91
|
## CI
|
|
89
92
|
|
|
90
|
-
The GitHub Action writes status, schema, doctor, plan, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
|
|
93
|
+
The GitHub Action writes status, summary, schema, doctor, plan, SBOM, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
|
|
91
94
|
|
|
92
95
|
```yaml
|
|
93
96
|
- uses: soovwv/aienvmp@main
|
|
94
97
|
with:
|
|
95
98
|
write-status: "true"
|
|
96
99
|
write-plan: "true"
|
|
100
|
+
write-sbom: "true"
|
|
101
|
+
write-summary: "true"
|
|
97
102
|
strict: "off"
|
|
98
103
|
```
|
|
99
104
|
|
package/action.yml
CHANGED
|
@@ -27,6 +27,14 @@ inputs:
|
|
|
27
27
|
description: Write doctor JSON with advisory/strict exit behavior
|
|
28
28
|
required: false
|
|
29
29
|
default: "true"
|
|
30
|
+
write-sbom:
|
|
31
|
+
description: Write standalone light SBOM and CycloneDX-lite artifacts
|
|
32
|
+
required: false
|
|
33
|
+
default: "true"
|
|
34
|
+
write-summary:
|
|
35
|
+
description: Write compact Markdown summary and append it to GitHub Step Summary
|
|
36
|
+
required: false
|
|
37
|
+
default: "true"
|
|
30
38
|
|
|
31
39
|
runs:
|
|
32
40
|
using: composite
|
|
@@ -65,6 +73,24 @@ runs:
|
|
|
65
73
|
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" doctor --dir "${{ inputs.directory }}" --json > "${{ inputs.directory }}/.aienvmp/doctor.json"
|
|
66
74
|
fi
|
|
67
75
|
|
|
76
|
+
- name: Write SBOM artifacts
|
|
77
|
+
shell: bash
|
|
78
|
+
run: |
|
|
79
|
+
if [ "${{ inputs.write-sbom }}" = "true" ]; then
|
|
80
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" sbom --dir "${{ inputs.directory }}" --write --quiet
|
|
81
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" sbom --dir "${{ inputs.directory }}" --format cyclonedx-lite --write --quiet
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
- name: Write AI summary
|
|
85
|
+
shell: bash
|
|
86
|
+
run: |
|
|
87
|
+
if [ "${{ inputs.write-summary }}" = "true" ]; then
|
|
88
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" summary --dir "${{ inputs.directory }}" --write --quiet
|
|
89
|
+
if [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
90
|
+
cat "${{ inputs.directory }}/.aienvmp/summary.md" >> "$GITHUB_STEP_SUMMARY"
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
|
|
68
94
|
- name: Doctor
|
|
69
95
|
shell: bash
|
|
70
96
|
run: |
|
|
@@ -16,6 +16,8 @@ jobs:
|
|
|
16
16
|
write-plan: "true"
|
|
17
17
|
write-schema: "true"
|
|
18
18
|
write-doctor-json: "true"
|
|
19
|
+
write-sbom: "true"
|
|
20
|
+
write-summary: "true"
|
|
19
21
|
strict: "off" # security, policy, coordination, all, or off
|
|
20
22
|
|
|
21
23
|
- uses: actions/upload-artifact@v4
|
|
@@ -26,6 +28,9 @@ jobs:
|
|
|
26
28
|
AIENV.md
|
|
27
29
|
.aienvmp/manifest.json
|
|
28
30
|
.aienvmp/status.json
|
|
31
|
+
.aienvmp/summary.md
|
|
32
|
+
.aienvmp/sbom.json
|
|
33
|
+
.aienvmp/sbom.cdx.json
|
|
29
34
|
.aienvmp/schema.json
|
|
30
35
|
.aienvmp/doctor.json
|
|
31
36
|
.aienvmp/plan.json
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { statusWorkspace } from "./commands/status.js";
|
|
|
16
16
|
import { schemaWorkspace } from "./commands/schema.js";
|
|
17
17
|
import { checkpointWorkspace } from "./commands/checkpoint.js";
|
|
18
18
|
import { sbomWorkspace } from "./commands/sbom.js";
|
|
19
|
+
import { summaryWorkspace } from "./commands/summary.js";
|
|
19
20
|
import { readFileSync } from "node:fs";
|
|
20
21
|
|
|
21
22
|
const commands = new Map([
|
|
@@ -36,7 +37,8 @@ const commands = new Map([
|
|
|
36
37
|
["status", statusWorkspace],
|
|
37
38
|
["schema", schemaWorkspace],
|
|
38
39
|
["checkpoint", checkpointWorkspace],
|
|
39
|
-
["sbom", sbomWorkspace]
|
|
40
|
+
["sbom", sbomWorkspace],
|
|
41
|
+
["summary", summaryWorkspace]
|
|
40
42
|
]);
|
|
41
43
|
|
|
42
44
|
const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
@@ -115,16 +117,18 @@ Usage:
|
|
|
115
117
|
aienvmp checkpoint [--dir .] --actor agent:id --summary "what changed" [--target dependency] [--json]
|
|
116
118
|
aienvmp plan [--dir .] [--json] [--write]
|
|
117
119
|
aienvmp sbom [--dir .] [--json] [--write]
|
|
120
|
+
aienvmp summary [--dir .] [--write]
|
|
118
121
|
aienvmp schema [--json]
|
|
119
122
|
|
|
120
123
|
Common:
|
|
121
|
-
aienvmp sync update AIENV.md, manifest, status, ledger, intents, and dashboard
|
|
124
|
+
aienvmp sync update AIENV.md, manifest, status, summary, SBOM, ledger, intents, and dashboard
|
|
122
125
|
aienvmp status print one simple environment decision; --write saves .aienvmp/status.json
|
|
123
126
|
aienvmp context print the AI preflight brief
|
|
124
127
|
aienvmp handoff print the next-agent handoff summary
|
|
125
128
|
aienvmp checkpoint record, sync, status, and handoff after an env change
|
|
126
129
|
aienvmp plan print a read-only AI environment action plan
|
|
127
130
|
aienvmp sbom print/write standalone light SBOM artifact
|
|
131
|
+
aienvmp summary print/write a compact Markdown summary for AI and CI
|
|
128
132
|
aienvmp schema print the stable AI-readable output contract
|
|
129
133
|
aienvmp snippet print an AGENTS.md pointer snippet
|
|
130
134
|
aienvmp dash regenerate/open the lightweight dashboard
|
|
@@ -141,6 +145,7 @@ Advanced:
|
|
|
141
145
|
aienvmp diff [--dir .]
|
|
142
146
|
aienvmp doctor [--dir .] [--json] [--ci] [--strict security|policy|coordination|all]
|
|
143
147
|
aienvmp sbom [--dir .] [--json] [--write]
|
|
148
|
+
aienvmp summary [--dir .] [--write]
|
|
144
149
|
aienvmp dash [--dir .] [--open]
|
|
145
150
|
`);
|
|
146
151
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readJson } from "../fsutil.js";
|
|
4
|
+
import { manifestPath, statusJsonPath, summaryMdPath, workspaceDir } from "../paths.js";
|
|
5
|
+
import { statusWorkspace } from "./status.js";
|
|
6
|
+
|
|
7
|
+
export async function summaryWorkspace(args) {
|
|
8
|
+
const dir = workspaceDir(args);
|
|
9
|
+
let status = await readJson(statusJsonPath(dir));
|
|
10
|
+
if (!status) {
|
|
11
|
+
status = await statusWorkspace({ ...args, dir, json: false, write: true, quiet: true });
|
|
12
|
+
}
|
|
13
|
+
const manifest = await readJson(manifestPath(dir), {});
|
|
14
|
+
const markdown = renderSummary(status, manifest);
|
|
15
|
+
const artifact = args.write ? await writeSummaryArtifact(dir, markdown) : "";
|
|
16
|
+
const output = { artifact, summary: markdown, state: status.state || "unknown", sbomRisk: status.sbomRisk || {}, nextCommand: status.nextCommand || "" };
|
|
17
|
+
|
|
18
|
+
if (args.json) {
|
|
19
|
+
console.log(JSON.stringify(output, null, 2));
|
|
20
|
+
} else if (!args.quiet) {
|
|
21
|
+
console.log(markdown.trimEnd());
|
|
22
|
+
if (artifact) console.log(`\nsummary: ${artifact}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return output;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function writeSummaryArtifact(dir, markdown) {
|
|
29
|
+
const out = summaryMdPath(dir);
|
|
30
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
31
|
+
await fs.writeFile(out, `${markdown.trimEnd()}\n`, "utf8");
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function renderSummary(status = {}, manifest = {}) {
|
|
36
|
+
const counts = status.counts || {};
|
|
37
|
+
const sbomRisk = status.sbomRisk || manifest.lightSbom?.riskSummary || {};
|
|
38
|
+
const coordination = status.coordination || {};
|
|
39
|
+
const activity = status.agentActivity || {};
|
|
40
|
+
const workspace = manifest.workspace?.root || manifest.workspace?.name || ".";
|
|
41
|
+
const next = status.nextCommand || status.decision?.nextCommand || "aienvmp status";
|
|
42
|
+
const readFirst = status.nextAgent?.readFirst || ".aienvmp/status.json";
|
|
43
|
+
const detail = status.quickstart?.detailCommand || "aienvmp context --json";
|
|
44
|
+
const strict = status.enforcement?.recommendedCommand || "aienvmp doctor --strict all";
|
|
45
|
+
const riskLevel = sbomRisk.level || "unknown";
|
|
46
|
+
const riskScore = valueOrZero(sbomRisk.score);
|
|
47
|
+
const scanner = sbomRisk.scanner || manifest.lightSbom?.source?.scanner || "not run";
|
|
48
|
+
const riskSignals = toList(sbomRisk.signals).slice(0, 3);
|
|
49
|
+
const conflictTargets = toList(coordination.conflictTargets);
|
|
50
|
+
const multiActorTargets = toList(activity.multiActorTargets);
|
|
51
|
+
|
|
52
|
+
return [
|
|
53
|
+
"# aienvmp summary",
|
|
54
|
+
"",
|
|
55
|
+
`- state: ${status.state || "unknown"}`,
|
|
56
|
+
`- workspace: ${workspace}`,
|
|
57
|
+
`- warnings: ${valueOrZero(counts.warnings)} / open intents: ${valueOrZero(counts.openIntents)}`,
|
|
58
|
+
`- runtimes: ${valueOrZero(counts.runtimes)} / dependencies: ${valueOrZero(counts.dependencies)} / vulnerabilities: ${valueOrZero(counts.vulnerabilities)}`,
|
|
59
|
+
`- light SBOM risk: ${riskLevel} (${riskScore}) / scanner: ${scanner}`,
|
|
60
|
+
`- next: ${next}`,
|
|
61
|
+
`- AI read first: ${readFirst}, then ${detail}`,
|
|
62
|
+
`- mode: advisory by default; strict is opt-in with ${strict}`,
|
|
63
|
+
"",
|
|
64
|
+
"## AI handoff",
|
|
65
|
+
"",
|
|
66
|
+
`- environment changes: ${status.agentUse?.environmentChanges || "intent-and-review-first"}`,
|
|
67
|
+
`- coordination: ${coordination.next || "No open environment intents."}`,
|
|
68
|
+
`- recent agent activity: ${activity.next || "No environment records need coordination."}`,
|
|
69
|
+
`- conflict targets: ${conflictTargets.length ? conflictTargets.join(", ") : "none"}`,
|
|
70
|
+
`- multi-actor targets: ${multiActorTargets.length ? multiActorTargets.join(", ") : "none"}`,
|
|
71
|
+
"",
|
|
72
|
+
"## SBOM",
|
|
73
|
+
"",
|
|
74
|
+
`- source: ${manifest.lightSbom?.source?.dependencies || "project manifests"}`,
|
|
75
|
+
`- confidence: transitive ${manifest.lightSbom?.confidence?.transitiveDependencies || "not-resolved"}`,
|
|
76
|
+
`- signals: ${riskSignals.length ? riskSignals.join("; ") : "none"}`,
|
|
77
|
+
`- verify: ${sbomRisk.next || "Use a dedicated scanner for security decisions."}`,
|
|
78
|
+
"",
|
|
79
|
+
"## Artifacts",
|
|
80
|
+
"",
|
|
81
|
+
"- AIENV.md",
|
|
82
|
+
"- .aienvmp/status.json",
|
|
83
|
+
"- .aienvmp/manifest.json",
|
|
84
|
+
"- .aienvmp/sbom.json",
|
|
85
|
+
"- .aienvmp/sbom.cdx.json",
|
|
86
|
+
"- .aienvmp/dashboard.html"
|
|
87
|
+
].join("\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function valueOrZero(value) {
|
|
91
|
+
return Number.isFinite(Number(value)) ? Number(value) : 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function toList(value) {
|
|
95
|
+
return Array.isArray(value) ? value.filter(Boolean) : [];
|
|
96
|
+
}
|
package/src/commands/sync.js
CHANGED
|
@@ -4,6 +4,7 @@ import { compileWorkspace } from "./compile.js";
|
|
|
4
4
|
import { dashWorkspace } from "./dash.js";
|
|
5
5
|
import { statusWorkspace } from "./status.js";
|
|
6
6
|
import { sbomWorkspace } from "./sbom.js";
|
|
7
|
+
import { summaryWorkspace } from "./summary.js";
|
|
7
8
|
|
|
8
9
|
export async function syncWorkspace(args) {
|
|
9
10
|
const quiet = args.quiet || args.json;
|
|
@@ -16,6 +17,7 @@ export async function syncWorkspace(args) {
|
|
|
16
17
|
const status = await statusWorkspace({ ...next, json: false, write: true, quiet: true });
|
|
17
18
|
const sbom = await sbomWorkspace({ ...next, json: false, write: true, quiet: true });
|
|
18
19
|
const cyclonedx = await sbomWorkspace({ ...next, json: false, write: true, quiet: true, format: "cyclonedx-lite" });
|
|
20
|
+
const summary = await summaryWorkspace({ ...next, json: false, write: true, quiet: true });
|
|
19
21
|
|
|
20
22
|
const result = {
|
|
21
23
|
status: "ok",
|
|
@@ -26,6 +28,7 @@ export async function syncWorkspace(args) {
|
|
|
26
28
|
status: status.artifact,
|
|
27
29
|
sbom: sbom.artifact,
|
|
28
30
|
cyclonedx: cyclonedx.artifact,
|
|
31
|
+
summary: summary.artifact,
|
|
29
32
|
dashboard: dashboard.dashboard
|
|
30
33
|
},
|
|
31
34
|
changes: scanned.changes,
|
|
@@ -35,7 +38,7 @@ export async function syncWorkspace(args) {
|
|
|
35
38
|
if (args.json) {
|
|
36
39
|
console.log(JSON.stringify(result, null, 2));
|
|
37
40
|
} else if (!quiet) {
|
|
38
|
-
console.log("sync complete: AIENV.md, manifest, status, ledger, intents, and dashboard are up to date");
|
|
41
|
+
console.log("sync complete: AIENV.md, manifest, status, SBOM, summary, ledger, intents, and dashboard are up to date");
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
return result;
|
package/src/contract.js
CHANGED
|
@@ -20,6 +20,12 @@ export function schemaContract() {
|
|
|
20
20
|
command: "aienvmp status --json",
|
|
21
21
|
contract: preflightContract()
|
|
22
22
|
},
|
|
23
|
+
summary: {
|
|
24
|
+
file: ".aienvmp/summary.md",
|
|
25
|
+
command: "aienvmp summary --write",
|
|
26
|
+
format: "markdown",
|
|
27
|
+
purpose: "Compact AI and CI step summary for quick review."
|
|
28
|
+
},
|
|
23
29
|
context: {
|
|
24
30
|
command: "aienvmp context --json",
|
|
25
31
|
rootFields: ["status", "preflight", "coordination", "decision", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
package/src/fsutil.js
CHANGED
|
@@ -12,12 +12,16 @@ export async function exists(file) {
|
|
|
12
12
|
|
|
13
13
|
export async function readJson(file, fallback = null) {
|
|
14
14
|
try {
|
|
15
|
-
return JSON.parse(await fs.readFile(file, "utf8"));
|
|
15
|
+
return JSON.parse(stripBom(await fs.readFile(file, "utf8")));
|
|
16
16
|
} catch {
|
|
17
17
|
return fallback;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function stripBom(value) {
|
|
22
|
+
return String(value).replace(/^\uFEFF/, "");
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
export async function writeJson(file, data) {
|
|
22
26
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
23
27
|
await fs.writeFile(file, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
package/src/paths.js
CHANGED
|
@@ -48,6 +48,10 @@ export function planMdPath(dir) {
|
|
|
48
48
|
return path.join(stateDir(dir), "plan.md");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export function summaryMdPath(dir) {
|
|
52
|
+
return path.join(stateDir(dir), "summary.md");
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
export function aiEnvPath(dir) {
|
|
52
56
|
return path.join(dir, "AIENV.md");
|
|
53
57
|
}
|
package/src/preflight.js
CHANGED
|
@@ -351,6 +351,7 @@ function agentQuickstart(reviewRequired) {
|
|
|
351
351
|
export function preflightArtifacts() {
|
|
352
352
|
return {
|
|
353
353
|
status: ".aienvmp/status.json",
|
|
354
|
+
summary: ".aienvmp/summary.md",
|
|
354
355
|
envMap: "AIENV.md",
|
|
355
356
|
manifest: ".aienvmp/manifest.json",
|
|
356
357
|
sbom: ".aienvmp/sbom.json",
|