aienvmp 0.1.40 → 0.1.41
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/CHANGELOG.md +9 -0
- package/README.md +4 -0
- package/package.json +1 -1
- package/src/cli.js +5 -1
- package/src/commands/context.js +4 -0
- package/src/commands/handoff.js +1 -0
- package/src/commands/schema.js +16 -0
- package/src/contract.js +42 -0
- package/src/dependencies.js +19 -1
- package/src/preflight.js +2 -0
- package/src/render.js +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.41
|
|
4
|
+
|
|
5
|
+
- Added preflight contract metadata so AI and CI consumers can rely on stable entry fields while ignoring additive changes.
|
|
6
|
+
- Added `aienvmp schema --json` to print the stable AI-readable output contract without scanning a workspace.
|
|
7
|
+
- Exposed coordination summaries at the root of `context --json` and `handoff --json`, with a human-readable handoff section.
|
|
8
|
+
- Added light SBOM source, confidence, and limitation hints so agents know what the lightweight snapshot does and does not prove.
|
|
9
|
+
- Updated the README with the schema command and light SBOM verification boundary while keeping the quick-start compact.
|
|
10
|
+
- Added an AI Contract dashboard card so humans can review the same stable fields that agents consume.
|
|
11
|
+
|
|
3
12
|
## 0.1.40
|
|
4
13
|
|
|
5
14
|
- Added dependency handoff summaries so the next AI receives dependency read-set and protocol guidance directly in `handoff`.
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ It helps Codex, Claude, Gemini, and humans avoid silent runtime, package manager
|
|
|
16
16
|
npx aienvmp sync
|
|
17
17
|
npx aienvmp status
|
|
18
18
|
npx aienvmp context --json
|
|
19
|
+
npx aienvmp schema --json
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
Before environment changes:
|
|
@@ -43,10 +44,12 @@ AIENV.md # Markdown env map for AI agents
|
|
|
43
44
|
## AI Contract
|
|
44
45
|
|
|
45
46
|
- `status`, `context`, `plan`, and `handoff` share one preflight contract.
|
|
47
|
+
- `schema --json` prints the stable AI-readable output contract without scanning.
|
|
46
48
|
- `status.json.nextAgent` tells the next AI what to read and whether to review first.
|
|
47
49
|
- `dependencyReadSet` lists manifests and lockfiles before package or security changes.
|
|
48
50
|
- `coordination.conflictTargets` shows where multiple agents are planning changes.
|
|
49
51
|
- `handoff` carries dependency read-set and protocol guidance for the next AI.
|
|
52
|
+
- Light SBOM includes source/confidence hints; verify security claims with dedicated scanners.
|
|
50
53
|
- Everything is advisory by default; strict failure is opt-in with `doctor --strict`.
|
|
51
54
|
|
|
52
55
|
## Agent Files
|
|
@@ -65,6 +68,7 @@ npx aienvmp snippet gemini
|
|
|
65
68
|
aienvmp sync # update env map, status, ledger, dashboard
|
|
66
69
|
aienvmp status --write # refresh compact AI status
|
|
67
70
|
aienvmp context --json # AI decision contract
|
|
71
|
+
aienvmp schema --json # stable output contract for AI/CI consumers
|
|
68
72
|
aienvmp plan --write # read-only action plan
|
|
69
73
|
aienvmp handoff --record # next-agent summary
|
|
70
74
|
aienvmp intent # record planned env change
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { snippetWorkspace } from "./commands/snippet.js";
|
|
|
13
13
|
import { handoffWorkspace } from "./commands/handoff.js";
|
|
14
14
|
import { planWorkspace } from "./commands/plan.js";
|
|
15
15
|
import { statusWorkspace } from "./commands/status.js";
|
|
16
|
+
import { schemaWorkspace } from "./commands/schema.js";
|
|
16
17
|
import { readFileSync } from "node:fs";
|
|
17
18
|
|
|
18
19
|
const commands = new Map([
|
|
@@ -30,7 +31,8 @@ const commands = new Map([
|
|
|
30
31
|
["snippet", snippetWorkspace],
|
|
31
32
|
["handoff", handoffWorkspace],
|
|
32
33
|
["plan", planWorkspace],
|
|
33
|
-
["status", statusWorkspace]
|
|
34
|
+
["status", statusWorkspace],
|
|
35
|
+
["schema", schemaWorkspace]
|
|
34
36
|
]);
|
|
35
37
|
|
|
36
38
|
const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
@@ -107,6 +109,7 @@ Usage:
|
|
|
107
109
|
aienvmp status [--dir .] [--json] [--write] [--quiet]
|
|
108
110
|
aienvmp handoff [--dir .] [--json] [--record --actor agent:id]
|
|
109
111
|
aienvmp plan [--dir .] [--json] [--write]
|
|
112
|
+
aienvmp schema [--json]
|
|
110
113
|
|
|
111
114
|
Common:
|
|
112
115
|
aienvmp sync update AIENV.md, manifest, status, ledger, intents, and dashboard
|
|
@@ -114,6 +117,7 @@ Common:
|
|
|
114
117
|
aienvmp context print the AI preflight brief
|
|
115
118
|
aienvmp handoff print the next-agent handoff summary
|
|
116
119
|
aienvmp plan print a read-only AI environment action plan
|
|
120
|
+
aienvmp schema print the stable AI-readable output contract
|
|
117
121
|
aienvmp snippet print an AGENTS.md pointer snippet
|
|
118
122
|
aienvmp dash regenerate/open the lightweight dashboard
|
|
119
123
|
|
package/src/commands/context.js
CHANGED
|
@@ -26,6 +26,7 @@ export async function contextWorkspace(args) {
|
|
|
26
26
|
console.log(JSON.stringify({
|
|
27
27
|
status: warnings.length ? "review-required" : "clear",
|
|
28
28
|
preflight,
|
|
29
|
+
coordination: preflight.coordination,
|
|
29
30
|
decision,
|
|
30
31
|
enforcement: enforcementAdvice(warnings),
|
|
31
32
|
recommendedActions: actions,
|
|
@@ -73,6 +74,9 @@ function lightSbomSummary(lightSbom = {}) {
|
|
|
73
74
|
guidance: "No lockfile policy detected."
|
|
74
75
|
},
|
|
75
76
|
dependencyChangeHints: (lightSbom.dependencyChangeHints || []).slice(0, 8),
|
|
77
|
+
source: lightSbom.source || {},
|
|
78
|
+
confidence: lightSbom.confidence || {},
|
|
79
|
+
limitations: lightSbom.limitations || [],
|
|
76
80
|
aiUse: lightSbom.aiUse || {}
|
|
77
81
|
};
|
|
78
82
|
}
|
package/src/commands/handoff.js
CHANGED
|
@@ -62,6 +62,7 @@ export function buildHandoff(manifest, timeline = [], warnings = [], intents = [
|
|
|
62
62
|
inventory: inventorySummary(manifest.inventory),
|
|
63
63
|
security: securitySummary(manifest.security),
|
|
64
64
|
dependencyHandoff: dependencyHandoffSummary(preflight),
|
|
65
|
+
coordination: preflight.coordination,
|
|
65
66
|
policy: {
|
|
66
67
|
node: policy.node || "not set",
|
|
67
68
|
python: policy.python || "not set",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { schemaContract } from "../contract.js";
|
|
2
|
+
|
|
3
|
+
export async function schemaWorkspace(args = {}) {
|
|
4
|
+
const schema = schemaContract();
|
|
5
|
+
if (args.json) {
|
|
6
|
+
console.log(JSON.stringify(schema, null, 2));
|
|
7
|
+
} else {
|
|
8
|
+
console.log("aienvmp contract");
|
|
9
|
+
console.log(`schema: ${schema.name} v${schema.schemaVersion}`);
|
|
10
|
+
console.log(`status: ${schema.outputs.status.command}`);
|
|
11
|
+
console.log(`context: ${schema.outputs.context.command}`);
|
|
12
|
+
console.log(`handoff: ${schema.outputs.handoff.command}`);
|
|
13
|
+
console.log(`rule: ${schema.compatibility.consumerRule}`);
|
|
14
|
+
}
|
|
15
|
+
return schema;
|
|
16
|
+
}
|
package/src/contract.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function preflightContract() {
|
|
2
|
+
return {
|
|
3
|
+
name: "aienvmp-preflight",
|
|
4
|
+
version: 1,
|
|
5
|
+
stability: "additive",
|
|
6
|
+
requiredFields: ["schemaVersion", "state", "decision", "quickstart", "commands", "artifacts"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "nextAgent", "coordination", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
8
|
+
rule: "Consumers should ignore unknown fields and treat missing optional fields as unavailable."
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function schemaContract() {
|
|
13
|
+
return {
|
|
14
|
+
schemaVersion: 1,
|
|
15
|
+
name: "aienvmp-contract",
|
|
16
|
+
purpose: "Stable AI-readable contract for aienvmp outputs.",
|
|
17
|
+
outputs: {
|
|
18
|
+
status: {
|
|
19
|
+
file: ".aienvmp/status.json",
|
|
20
|
+
command: "aienvmp status --json",
|
|
21
|
+
contract: preflightContract()
|
|
22
|
+
},
|
|
23
|
+
context: {
|
|
24
|
+
command: "aienvmp context --json",
|
|
25
|
+
rootFields: ["status", "preflight", "coordination", "decision", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
26
|
+
},
|
|
27
|
+
handoff: {
|
|
28
|
+
command: "aienvmp handoff --json",
|
|
29
|
+
rootFields: ["status", "decision", "preflight", "coordination", "dependencyHandoff", "openIntents", "warnings", "recommendedActions", "recentChanges"]
|
|
30
|
+
},
|
|
31
|
+
manifest: {
|
|
32
|
+
file: ".aienvmp/manifest.json",
|
|
33
|
+
rootFields: ["schemaVersion", "workspace", "runtimes", "packageManagers", "dependencySnapshot", "lightSbom", "security", "trust"]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
compatibility: {
|
|
37
|
+
stability: "additive",
|
|
38
|
+
consumerRule: "Ignore unknown fields. Do not require optional fields unless listed in requiredFields.",
|
|
39
|
+
localBehavior: "read-only; this command does not scan, install, update, or lock anything."
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/dependencies.js
CHANGED
|
@@ -79,6 +79,23 @@ export function buildLightSbom(snapshot = {}, security = {}) {
|
|
|
79
79
|
schemaVersion: 1,
|
|
80
80
|
mode: "light-sbom",
|
|
81
81
|
note: "AI-ready package and vulnerability summary from read-only project files and optional scanners.",
|
|
82
|
+
source: {
|
|
83
|
+
dependencies: "project manifests",
|
|
84
|
+
lockfiles: "file presence only",
|
|
85
|
+
vulnerabilities: security.enabled ? "optional scanner summary" : "not scanned",
|
|
86
|
+
resolver: "not run"
|
|
87
|
+
},
|
|
88
|
+
confidence: {
|
|
89
|
+
directDependencies: "high",
|
|
90
|
+
lockfileManager: snapshot.lockfiles?.length ? "medium" : "none",
|
|
91
|
+
transitiveDependencies: "not-resolved",
|
|
92
|
+
vulnerabilities: security.enabled ? "scanner-provided" : "not-scanned"
|
|
93
|
+
},
|
|
94
|
+
limitations: [
|
|
95
|
+
"Does not install packages.",
|
|
96
|
+
"Does not resolve full transitive dependency graphs.",
|
|
97
|
+
"Does not replace CycloneDX, SPDX, Syft, Trivy, npm audit, or pip-audit outputs."
|
|
98
|
+
],
|
|
82
99
|
summary: {
|
|
83
100
|
ecosystems: countBy(packages, "ecosystem"),
|
|
84
101
|
managers: countBy(packages, "manager"),
|
|
@@ -96,7 +113,8 @@ export function buildLightSbom(snapshot = {}, security = {}) {
|
|
|
96
113
|
aiUse: {
|
|
97
114
|
beforeDependencyChanges: "Read lightSbom.summary and lightSbom.topRisk before changing dependencies.",
|
|
98
115
|
securityMode: security.enabled ? "scanner-summary" : "scanner-off",
|
|
99
|
-
dependencySource: "project manifests only; no install or resolver is run"
|
|
116
|
+
dependencySource: "project manifests only; no install or resolver is run",
|
|
117
|
+
trustRule: "Use lightSbom as a fast AI planning map; verify with dedicated scanners before security claims."
|
|
100
118
|
}
|
|
101
119
|
};
|
|
102
120
|
}
|
package/src/preflight.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recommendedActions } from "./actions.js";
|
|
2
2
|
import { aiDecision } from "./decision.js";
|
|
3
3
|
import { enforcementAdvice } from "./enforcement.js";
|
|
4
|
+
import { preflightContract } from "./contract.js";
|
|
4
5
|
|
|
5
6
|
export function buildPreflight(manifest = {}, warnings = [], intents = []) {
|
|
6
7
|
const decision = aiDecision(warnings, intents);
|
|
@@ -14,6 +15,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
|
|
|
14
15
|
const coordination = coordinationSummary(intents);
|
|
15
16
|
return {
|
|
16
17
|
schemaVersion: 1,
|
|
18
|
+
contract: preflightContract(),
|
|
17
19
|
state,
|
|
18
20
|
summary: state === "clear"
|
|
19
21
|
? "Project-local work can continue. Record intent before environment changes."
|
package/src/render.js
CHANGED
|
@@ -199,6 +199,9 @@ export function renderHandoff(handoff) {
|
|
|
199
199
|
"Open intents:",
|
|
200
200
|
...(handoff.openIntents.length ? handoff.openIntents.map((i) => `- ${i.actor}: ${i.action}${i.target ? ` (${i.target})` : ""}`) : ["- none"]),
|
|
201
201
|
"",
|
|
202
|
+
"Coordination:",
|
|
203
|
+
...coordinationHandoffLines(handoff.coordination),
|
|
204
|
+
"",
|
|
202
205
|
"Warnings:",
|
|
203
206
|
...(handoff.warnings.length ? handoff.warnings.map((w) => `- ${w.message}`) : ["- none"]),
|
|
204
207
|
"",
|
|
@@ -220,6 +223,12 @@ export function renderHandoff(handoff) {
|
|
|
220
223
|
return lines.join("\n");
|
|
221
224
|
}
|
|
222
225
|
|
|
226
|
+
function coordinationHandoffLines(coordination = {}) {
|
|
227
|
+
const conflicts = coordination.conflictTargets || [];
|
|
228
|
+
if (conflicts.length) return [`- Conflicts: ${conflicts.join(", ")}`, `- Next: ${coordination.next || "review open intents"}`];
|
|
229
|
+
return [`- Open intents: ${coordination.openIntentCount || 0}`, `- Next: ${coordination.next || "no open environment intents"}`];
|
|
230
|
+
}
|
|
231
|
+
|
|
223
232
|
function dependencyHandoffLines(dependencyHandoff = {}) {
|
|
224
233
|
const readSet = dependencyHandoff.readSet || [];
|
|
225
234
|
const protocol = dependencyHandoff.protocol || {};
|
|
@@ -406,6 +415,8 @@ const ciReadinessHtml=ciReadiness.length?'<table>'+ciReadiness.map(s=>\`<tr><th>
|
|
|
406
415
|
const enforcementProfile=manifest.preflight?.enforcementProfile||{};
|
|
407
416
|
const strictCommands=enforcementProfile.strictCommands||[];
|
|
408
417
|
const enforcementHtml=\`<table><tr><th>Default</th><td><code>\${esc(enforcementProfile.defaultMode||'advisory')}</code></td></tr><tr><th>Local</th><td>\${esc(enforcementProfile.localOperation||'non-blocking')}</td></tr><tr><th>Strict</th><td>\${esc(enforcementProfile.strictUse||'CI or explicit checks only')}</td></tr><tr><th>Recommended</th><td><code>\${esc(enforcementProfile.recommendedStrictCommand||'aienvmp doctor --strict all')}</code></td></tr></table><div class="timeline">\${strictCommands.slice(0,4).map(cmd=>\`<div class="event"><time>CI</time><div><code>\${esc(cmd)}</code></div></div>\`).join('')}</div><div class="path">\${esc(enforcementProfile.reason||'Warnings stay advisory unless strict mode is requested.')}</div>\`;
|
|
418
|
+
const contract=manifest.preflight?.contract||{};
|
|
419
|
+
const contractHtml=contract.name?\`<table><tr><th>Name</th><td><code>\${esc(contract.name)}</code></td></tr><tr><th>Version</th><td><code>\${esc(contract.version||1)}</code></td></tr><tr><th>Stability</th><td><code>\${esc(contract.stability||'additive')}</code></td></tr><tr><th>AI fields</th><td>\${esc((contract.aiEntryFields||[]).join(', ')||'none')}</td></tr></table><div class="path">\${esc(contract.rule||'Ignore unknown fields.')}</div>\`:'<div class="okline">Run <code>aienvmp status --write</code> to include AI contract metadata.</div>';
|
|
409
420
|
const intentTargets=manifest.preflight?.intentTargets||[];
|
|
410
421
|
const intentTargetsHtml=intentTargets.length?'<div class="timeline">'+intentTargets.slice(0,5).map(t=>\`<div class="event"><time>\${esc(t.target)}</time><div><b>\${esc(t.target)}</b> \${esc(t.reason||'Record this target before environment changes.')}\${t.command?\`<div class="path">\${esc(t.command)}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No specific target recommendation. Use <code>aienvmp intent --actor agent:id --action planned-change</code>.</div>';
|
|
411
422
|
const dependencyReadSet=manifest.preflight?.dependencyReadSet||[];
|
|
@@ -462,6 +473,8 @@ document.getElementById('app').innerHTML=\`
|
|
|
462
473
|
<div style="height:14px"></div>
|
|
463
474
|
\${card('AI Intent Targets','<span class="pill">'+intentTargets.length+' targets</span>',intentTargetsHtml)}
|
|
464
475
|
<div style="height:14px"></div>
|
|
476
|
+
\${card('AI Contract','<span class="pill">'+(contract.stability||'additive')+'</span>',contractHtml)}
|
|
477
|
+
<div style="height:14px"></div>
|
|
465
478
|
\${card('Dependency Read Set','<span class="pill">'+dependencyReadSet.length+' files</span>',dependencyReadSetHtml)}
|
|
466
479
|
<div style="height:14px"></div>
|
|
467
480
|
\${card('Dependency Protocol','<span class="pill">'+(dependencyProtocol.mode||'advisory')+'</span>',dependencyProtocolHtml)}
|
|
@@ -569,6 +582,10 @@ function lightSbomLines(lightSbom = {}) {
|
|
|
569
582
|
`- Transitive or unmatched vulnerable packages: ${summary.transitiveOrUnmatchedVulnerablePackages || 0}`,
|
|
570
583
|
`- Lockfiles: ${(summary.lockfiles || []).map((item) => item.file).join(", ") || "none"}`
|
|
571
584
|
];
|
|
585
|
+
if (lightSbom.source || lightSbom.confidence) {
|
|
586
|
+
lines.push(`- Source: ${lightSbom.source?.dependencies || "project manifests"}; vulnerabilities: ${lightSbom.source?.vulnerabilities || "not scanned"}`);
|
|
587
|
+
lines.push(`- Confidence: direct ${lightSbom.confidence?.directDependencies || "unknown"}; transitive ${lightSbom.confidence?.transitiveDependencies || "unknown"}`);
|
|
588
|
+
}
|
|
572
589
|
if (lightSbom.packageManagerPolicy) {
|
|
573
590
|
lines.push(`- Package manager policy: ${lightSbom.packageManagerPolicy.status}`);
|
|
574
591
|
lines.push(`- Package manager guidance: ${lightSbom.packageManagerPolicy.guidance}`);
|