aienvmp 0.1.49 → 0.1.51

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.
@@ -11,20 +11,26 @@ The goal is to help multiple AI agents avoid silently installing or using differ
11
11
 
12
12
  ## Preflight
13
13
 
14
- Before environment-impacting work, run:
14
+ Before environment-impacting work, run the light preflight first:
15
15
 
16
16
  ```bash
17
- npx aienvmp context
17
+ npx aienvmp status --write
18
18
  ```
19
19
 
20
- If the output says `review-required`, do not change global runtimes, package managers, Docker settings, or global packages without asking the user.
20
+ Then read the short handoff:
21
21
 
22
- For machine-readable context, use:
22
+ ```bash
23
+ cat .aienvmp/summary.md
24
+ ```
25
+
26
+ For deeper machine-readable context, use:
23
27
 
24
28
  ```bash
25
29
  npx aienvmp context --json
26
30
  ```
27
31
 
32
+ If the output says `review-required`, do not change global runtimes, package managers, Docker settings, dependencies, lockfiles, or global packages without asking the user.
33
+
28
34
  ## Before Environment Changes
29
35
 
30
36
  Record intent before changing shared environment state:
@@ -38,26 +44,25 @@ Use this for changes such as:
38
44
  - installing or upgrading Node, Python, Docker, package managers, or global CLIs
39
45
  - changing `.nvmrc`, `.python-version`, `mise.toml`, `.tool-versions`, or `.aienvmp/policy.yml`
40
46
  - switching package managers
47
+ - changing dependencies or lockfiles
41
48
  - changing Docker daemon/context assumptions
42
49
 
43
50
  ## After Environment Changes
44
51
 
45
- Refresh the environment map:
52
+ Use the one-command checkpoint after an accepted environment change:
46
53
 
47
54
  ```bash
48
- npx aienvmp sync
55
+ npx aienvmp checkpoint --actor agent:codex --summary "<what changed>" --target "<tool-or-runtime>"
49
56
  ```
50
57
 
51
- Record what changed:
58
+ This records the change, refreshes the env map, writes status/summary/SBOM artifacts, and records a handoff.
52
59
 
53
- ```bash
54
- npx aienvmp record --actor agent:codex --summary "<what changed>" --target "<tool-or-runtime>" --evidence "<command or file>"
55
- ```
56
-
57
- Resolve the original intent if complete:
60
+ If checkpoint is not available, use the explicit fallback:
58
61
 
59
62
  ```bash
60
- npx aienvmp resolve --actor agent:codex --id "<intent-id>"
63
+ npx aienvmp sync
64
+ npx aienvmp record --actor agent:codex --summary "<what changed>" --target "<tool-or-runtime>" --evidence "<command or file>"
65
+ npx aienvmp handoff --record --actor agent:codex
61
66
  ```
62
67
 
63
68
  ## Safety Rules
@@ -66,6 +71,7 @@ npx aienvmp resolve --actor agent:codex --id "<intent-id>"
66
71
  - Treat policy mismatches as review-required.
67
72
  - Do not install, upgrade, downgrade, or remove global software unless the user explicitly asks.
68
73
  - Prefer project-local version files and local environments.
74
+ - Do not switch package managers or rewrite lockfiles only to satisfy a tool preference.
69
75
  - Do not use warnings as permission to interrupt production or shared workspace operations.
70
76
  - Use `npx aienvmp doctor --ci` only in CI or explicit strict-mode automation.
71
77
 
package/BUGFIXES.md CHANGED
@@ -82,6 +82,24 @@ Short record of bugs, fixes, and follow-up checks.
82
82
  - Fix: the Action now has `write-sbom` and the example uploads both native and CycloneDX-lite SBOM artifacts.
83
83
  - Verification: Action tests cover input metadata, SBOM write commands, and example artifact paths.
84
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
+
97
+ ### Agent instructions lagged behind the current protocol
98
+
99
+ - Issue: packaged skill and pointer snippets still emphasized older context/record flows and did not direct agents to `summary.md`.
100
+ - Fix: snippets and the Codex skill now use the current status, summary, context, intent, and checkpoint flow with agent-specific actor examples.
101
+ - Verification: regression tests cover snippet output for AGENTS.md, Claude, and Gemini, and summary/status tests cover the read order and dependency protocol.
102
+
85
103
  ## Template
86
104
 
87
105
  ### Title
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.51
4
+
5
+ - Updated Codex/Claude/Gemini pointer snippets to use the current `status -> summary.md -> context` read order.
6
+ - Added agent-specific actor examples for Codex, Claude, and Gemini snippet output.
7
+ - Added dependency and lockfile changes to the snippet environment-change scope.
8
+ - Updated the packaged Codex skill to prefer `status --write`, `summary.md`, and `checkpoint`.
9
+ - Added `.aienvmp/summary.md` to the shared preflight read order and next-agent hint.
10
+ - Added a Dependency changes section to `summary.md` with read files, intent command, checkpoint command, and package-manager policy.
11
+ - Updated README agent-file guidance and added regression coverage for the AI snippet and summary protocol.
12
+
13
+ ## 0.1.50
14
+
15
+ - Added `aienvmp summary` for a compact Markdown AI/CI handoff view.
16
+ - Added `.aienvmp/summary.md` writing through the default `sync` flow.
17
+ - Added `summary` to preflight artifacts and the stable output contract.
18
+ - Added a GitHub Action `write-summary` input.
19
+ - Appended `.aienvmp/summary.md` to GitHub Step Summary when the Action runs in GitHub Actions.
20
+ - Updated the GitHub Action example and README to include the summary artifact.
21
+ - Fixed UTF-8 BOM JSON parsing so Windows-created `package.json` files are scanned correctly.
22
+ - Added regression tests for summary rendering, sync output, schema metadata, Action Step Summary wiring, and BOM JSON parsing.
23
+
3
24
  ## 0.1.49
4
25
 
5
26
  - Added a `write-sbom` GitHub Action input for explicit SBOM artifact generation.
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.
@@ -63,16 +65,20 @@ AIENV.md # Markdown env map for AI agents
63
65
  `aienvmp` does not replace AGENTS.md, CLAUDE.md, or GEMINI.md. It gives them a live environment source of truth.
64
66
 
65
67
  ```bash
68
+ npx aienvmp snippet codex
66
69
  npx aienvmp snippet agents
67
70
  npx aienvmp snippet claude
68
71
  npx aienvmp snippet gemini
69
72
  ```
70
73
 
74
+ Snippets point each AI to `status`, `summary.md`, `context --json`, intent, and checkpoint without taking over the instruction file.
75
+
71
76
  ## Commands
72
77
 
73
78
  ```bash
74
- aienvmp sync # update env map, status, ledger, dashboard
79
+ aienvmp sync # update env map, status, summary, SBOM, dashboard
75
80
  aienvmp status --write # refresh compact AI status
81
+ aienvmp summary --write # refresh compact Markdown summary
76
82
  aienvmp context --json # AI decision contract
77
83
  aienvmp sbom --json # standalone light SBOM
78
84
  aienvmp sbom --format cyclonedx-lite --json
@@ -87,7 +93,7 @@ aienvmp doctor --strict security|policy|coordination|all
87
93
 
88
94
  ## CI
89
95
 
90
- The GitHub Action writes status, schema, doctor, plan, SBOM, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
96
+ The GitHub Action writes status, summary, schema, doctor, plan, SBOM, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
91
97
 
92
98
  ```yaml
93
99
  - uses: soovwv/aienvmp@main
@@ -95,6 +101,7 @@ The GitHub Action writes status, schema, doctor, plan, SBOM, and dashboard artif
95
101
  write-status: "true"
96
102
  write-plan: "true"
97
103
  write-sbom: "true"
104
+ write-summary: "true"
98
105
  strict: "off"
99
106
  ```
100
107
 
package/action.yml CHANGED
@@ -31,6 +31,10 @@ inputs:
31
31
  description: Write standalone light SBOM and CycloneDX-lite artifacts
32
32
  required: false
33
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"
34
38
 
35
39
  runs:
36
40
  using: composite
@@ -77,6 +81,16 @@ runs:
77
81
  node "$GITHUB_ACTION_PATH/bin/aienvmp.js" sbom --dir "${{ inputs.directory }}" --format cyclonedx-lite --write --quiet
78
82
  fi
79
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
+
80
94
  - name: Doctor
81
95
  shell: bash
82
96
  run: |
@@ -17,6 +17,7 @@ jobs:
17
17
  write-schema: "true"
18
18
  write-doctor-json: "true"
19
19
  write-sbom: "true"
20
+ write-summary: "true"
20
21
  strict: "off" # security, policy, coordination, all, or off
21
22
 
22
23
  - uses: actions/upload-artifact@v4
@@ -27,6 +28,7 @@ jobs:
27
28
  AIENV.md
28
29
  .aienvmp/manifest.json
29
30
  .aienvmp/status.json
31
+ .aienvmp/summary.md
30
32
  .aienvmp/sbom.json
31
33
  .aienvmp/sbom.cdx.json
32
34
  .aienvmp/schema.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aienvmp",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "AI-first environment maps and lightweight runtime SBOMs for shared development machines.",
5
5
  "type": "module",
6
6
  "bin": {
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
@@ -136,11 +140,12 @@ Advanced:
136
140
  aienvmp resolve [--dir .] --actor human:you --id <intent-id> [--status resolved|cancelled]
137
141
  aienvmp record [--dir .] --actor agent:codex --summary "updated .nvmrc" [--target node] [--before 20] [--after 24]
138
142
  aienvmp checkpoint [--dir .] --actor agent:codex --summary "updated dependency" [--target dependency]
139
- aienvmp snippet [agents|claude|gemini] [--write AGENTS.md]
143
+ aienvmp snippet [agents|codex|claude|gemini] [--write AGENTS.md]
140
144
  aienvmp compile [--dir .]
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,118 @@
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
+ const dependencyProtocol = status.dependencyChangeProtocol || {};
52
+ const dependencyCommands = dependencyProtocol.commands || {};
53
+ const dependencyFiles = dependencyFilesFor(status.dependencyReadSet);
54
+
55
+ return [
56
+ "# aienvmp summary",
57
+ "",
58
+ `- state: ${status.state || "unknown"}`,
59
+ `- workspace: ${workspace}`,
60
+ `- warnings: ${valueOrZero(counts.warnings)} / open intents: ${valueOrZero(counts.openIntents)}`,
61
+ `- runtimes: ${valueOrZero(counts.runtimes)} / dependencies: ${valueOrZero(counts.dependencies)} / vulnerabilities: ${valueOrZero(counts.vulnerabilities)}`,
62
+ `- light SBOM risk: ${riskLevel} (${riskScore}) / scanner: ${scanner}`,
63
+ `- next: ${next}`,
64
+ `- AI read first: ${readFirst}, then ${detail}`,
65
+ `- mode: advisory by default; strict is opt-in with ${strict}`,
66
+ "",
67
+ "## AI handoff",
68
+ "",
69
+ `- environment changes: ${status.agentUse?.environmentChanges || "intent-and-review-first"}`,
70
+ `- coordination: ${coordination.next || "No open environment intents."}`,
71
+ `- recent agent activity: ${activity.next || "No environment records need coordination."}`,
72
+ `- conflict targets: ${conflictTargets.length ? conflictTargets.join(", ") : "none"}`,
73
+ `- multi-actor targets: ${multiActorTargets.length ? multiActorTargets.join(", ") : "none"}`,
74
+ "",
75
+ "## SBOM",
76
+ "",
77
+ `- source: ${manifest.lightSbom?.source?.dependencies || "project manifests"}`,
78
+ `- confidence: transitive ${manifest.lightSbom?.confidence?.transitiveDependencies || "not-resolved"}`,
79
+ `- signals: ${riskSignals.length ? riskSignals.join("; ") : "none"}`,
80
+ `- verify: ${sbomRisk.next || "Use a dedicated scanner for security decisions."}`,
81
+ "",
82
+ "## Dependency changes",
83
+ "",
84
+ `- read files: ${dependencyFiles.length ? dependencyFiles.join(", ") : "none detected"}`,
85
+ `- before: ${dependencyCommands.recordIntent || "aienvmp intent --actor agent:id --action planned-change --target dependency"}`,
86
+ `- after: ${dependencyCommands.checkpointAfterChange || "aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency"}`,
87
+ `- package manager policy: ${dependencyProtocol.packageManagerPolicy || "not-detected"}`,
88
+ "",
89
+ "## Artifacts",
90
+ "",
91
+ "- AIENV.md",
92
+ "- .aienvmp/status.json",
93
+ "- .aienvmp/manifest.json",
94
+ "- .aienvmp/sbom.json",
95
+ "- .aienvmp/sbom.cdx.json",
96
+ "- .aienvmp/dashboard.html"
97
+ ].join("\n");
98
+ }
99
+
100
+ function valueOrZero(value) {
101
+ return Number.isFinite(Number(value)) ? Number(value) : 0;
102
+ }
103
+
104
+ function toList(value) {
105
+ return Array.isArray(value) ? value.filter(Boolean) : [];
106
+ }
107
+
108
+ function dependencyFilesFor(readSet = []) {
109
+ const files = [];
110
+ for (const item of toList(readSet).slice(0, 3)) {
111
+ if (item.manifest) files.push(item.manifest);
112
+ for (const lockfile of toList(item.lockfiles).slice(0, 3)) {
113
+ const file = typeof lockfile === "string" ? lockfile : lockfile?.file;
114
+ if (file && !files.includes(file)) files.push(file);
115
+ }
116
+ }
117
+ return files.slice(0, 8);
118
+ }
@@ -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
@@ -66,6 +66,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
66
66
  artifacts: preflightArtifacts(),
67
67
  readOrder: [
68
68
  ".aienvmp/status.json",
69
+ ".aienvmp/summary.md",
69
70
  "AIENV.md",
70
71
  ".aienvmp/manifest.json",
71
72
  ".aienvmp/plan.json",
@@ -195,6 +196,7 @@ function nextAgentHint(state, dependencyReadSet = [], dependencyChangeProtocol =
195
196
  return {
196
197
  handoffCommand: dependencyChangeProtocol.commands?.handoff || "aienvmp handoff --record --actor agent:id",
197
198
  readFirst: ".aienvmp/status.json",
199
+ readSummary: ".aienvmp/summary.md",
198
200
  readNext: "aienvmp context --json",
199
201
  reviewState: state,
200
202
  dependencyFiles,
@@ -351,6 +353,7 @@ function agentQuickstart(reviewRequired) {
351
353
  export function preflightArtifacts() {
352
354
  return {
353
355
  status: ".aienvmp/status.json",
356
+ summary: ".aienvmp/summary.md",
354
357
  envMap: "AIENV.md",
355
358
  manifest: ".aienvmp/manifest.json",
356
359
  sbom: ".aienvmp/sbom.json",
package/src/render.js CHANGED
@@ -114,18 +114,30 @@ function preflightLines(preflight = {}) {
114
114
 
115
115
  export function renderAgentPointer(target = "agents") {
116
116
  const label = target === "claude" ? "Claude" : target === "gemini" ? "Gemini" : "AI agents";
117
+ const actor = target === "claude"
118
+ ? "agent:claude"
119
+ : target === "gemini"
120
+ ? "agent:gemini"
121
+ : target === "codex"
122
+ ? "agent:codex"
123
+ : "agent:id";
117
124
  return `## aienvmp Environment Map
118
125
 
119
126
  ${label} should use \`aienvmp\` as the workspace environment source of truth.
120
127
 
121
- Before changing runtimes, package managers, Docker settings, global packages, or environment policy:
128
+ Fast read order:
122
129
 
123
130
  1. Run \`aienvmp status --write\`.
124
- 2. Run \`aienvmp context --json\` for details.
125
- 3. Read \`AIENV.md\`.
126
- 4. If status or context says \`review-required\`, ask the user before changing the environment.
127
- 5. Record planned environment changes with the recommended target, for example \`aienvmp intent --actor agent:id --action planned-change --target dependency\`.
128
- 6. After environment changes, run \`aienvmp checkpoint --actor agent:id --summary what-changed --target environment\`.
131
+ 2. Read \`.aienvmp/summary.md\` for the short handoff.
132
+ 3. Run \`aienvmp context --json\` for details.
133
+ 4. Read \`AIENV.md\` when Markdown context is easier.
134
+
135
+ Before changing runtimes, package managers, Docker settings, global packages, dependencies, lockfiles, or environment policy:
136
+
137
+ 1. If status or context says \`review-required\`, ask the user before changing the environment.
138
+ 2. Record planned environment changes with the recommended target, for example \`aienvmp intent --actor ${actor} --action planned-change --target dependency\`.
139
+ 3. Prefer project-local version files and local environments.
140
+ 4. After accepted environment changes, run \`aienvmp checkpoint --actor ${actor} --summary what-changed --target environment\`.
129
141
 
130
142
  \`aienvmp\` does not replace this instruction file. It provides the live env map, lightweight runtime SBOM, intent log, timeline, and dashboard.`;
131
143
  }