aienvmp 0.1.60 → 0.1.62
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 +6 -0
- package/CHANGELOG.md +18 -0
- package/README.md +2 -1
- package/action.yml +32 -0
- package/package.json +1 -1
- package/src/commands/context.js +1 -0
- package/src/commands/status.js +1 -0
- package/src/commands/summary.js +3 -0
- package/src/contract.js +3 -2
- package/src/preflight.js +53 -0
- package/src/render.js +4 -0
package/BUGFIXES.md
CHANGED
|
@@ -4,6 +4,12 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
4
4
|
|
|
5
5
|
## 2026-07-08
|
|
6
6
|
|
|
7
|
+
### GitHub Action hid strict-plan guidance inside artifacts
|
|
8
|
+
|
|
9
|
+
- Issue: the Action produced `doctor.json` and `summary.md`, but the recommended local/CI strict commands were not visible as a dedicated Step Summary block.
|
|
10
|
+
- Fix: the Action now appends an `aienvmp strict plan` section sourced from advisory doctor metadata, using a temporary payload when `write-doctor-json` is disabled.
|
|
11
|
+
- Verification: Action regression tests check the strict-plan block, `strictPlan` source, CI command, and temporary-file fallback.
|
|
12
|
+
|
|
7
13
|
### CLI version was hardcoded
|
|
8
14
|
|
|
9
15
|
- Issue: `npm run smoke` printed `0.1.0` after the package version changed.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.62
|
|
4
|
+
|
|
5
|
+
- Added a compact `collaboration` block to the shared preflight/status contract for multi-agent environment coordination.
|
|
6
|
+
- Exposed `collaboration` at the root of `context --json` so AI agents can read status, active targets, and the next command without digging through multiple sections.
|
|
7
|
+
- Added the collaboration status and rule to `.aienvmp/summary.md`.
|
|
8
|
+
- Added a short collaboration line to plain `aienvmp status` output.
|
|
9
|
+
- Added an `AI Collaboration` dashboard card with status, active targets, env-change rule, and next command.
|
|
10
|
+
- Documented the collaboration block in the stable schema contract and README.
|
|
11
|
+
- Added regression tests for status, context, summary, dashboard, and schema surfaces.
|
|
12
|
+
|
|
13
|
+
## 0.1.61
|
|
14
|
+
|
|
15
|
+
- Added an `aienvmp strict plan` block to the GitHub Action Step Summary.
|
|
16
|
+
- Reused advisory `doctor --json` data so CI can show local and strict commands without forcing failure.
|
|
17
|
+
- Preserved `write-doctor-json: false` by using a temporary advisory doctor payload when only the summary needs strict guidance.
|
|
18
|
+
- Documented the Action Step Summary strict-plan surface in the README.
|
|
19
|
+
- Added regression coverage for the Action strict-plan summary contract.
|
|
20
|
+
|
|
3
21
|
## 0.1.60
|
|
4
22
|
|
|
5
23
|
- Added `enforcement.strictPlan` to help AI agents and CI choose the narrowest explicit strict scope.
|
package/README.md
CHANGED
|
@@ -51,6 +51,7 @@ AIENV.md # Markdown env map for AI agents
|
|
|
51
51
|
- `summary.md` is the short CI/AI handoff view, with `AI readiness`, signals, and next action first.
|
|
52
52
|
- `status.json.nextAgent` tells the next AI what to read and whether to review first.
|
|
53
53
|
- `aiReadiness` gives a one-field ready/review signal for AI continuation.
|
|
54
|
+
- `collaboration` gives the shortest multi-agent env-change status, targets, and next command.
|
|
54
55
|
- `dependencyReadSet` lists manifests and lockfiles before package or security changes.
|
|
55
56
|
- `sbomRisk` gives AI a compact light-SBOM risk level, signals, and next command.
|
|
56
57
|
- `sbom.json.aiDependencyReview` connects SBOM risk, scanner confidence, and safe dependency-change steps.
|
|
@@ -98,7 +99,7 @@ aienvmp doctor --strict security|policy|coordination|all
|
|
|
98
99
|
## CI
|
|
99
100
|
|
|
100
101
|
The GitHub Action writes status, summary, schema, doctor, plan, SBOM, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
|
|
101
|
-
|
|
102
|
+
The Step Summary includes an `aienvmp strict plan` block so humans and AI agents can pick the narrowest CI strict scope without parsing the full artifacts.
|
|
102
103
|
|
|
103
104
|
```yaml
|
|
104
105
|
- uses: soovwv/aienvmp@main
|
package/action.yml
CHANGED
|
@@ -91,6 +91,38 @@ runs:
|
|
|
91
91
|
fi
|
|
92
92
|
fi
|
|
93
93
|
|
|
94
|
+
- name: Append strict plan summary
|
|
95
|
+
shell: bash
|
|
96
|
+
run: |
|
|
97
|
+
if [ "${{ inputs.write-summary }}" = "true" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
98
|
+
mkdir -p "${{ inputs.directory }}/.aienvmp"
|
|
99
|
+
plan_source="${{ inputs.directory }}/.aienvmp/doctor.json"
|
|
100
|
+
if [ ! -f "$plan_source" ]; then
|
|
101
|
+
plan_source="$(mktemp)"
|
|
102
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" doctor --dir "${{ inputs.directory }}" --json > "$plan_source"
|
|
103
|
+
fi
|
|
104
|
+
node - "$plan_source" "$GITHUB_STEP_SUMMARY" <<'NODE'
|
|
105
|
+
const fs = require("node:fs");
|
|
106
|
+
|
|
107
|
+
const [doctorPath, summaryPath] = process.argv.slice(2);
|
|
108
|
+
const doctor = JSON.parse(fs.readFileSync(doctorPath, "utf8"));
|
|
109
|
+
const plan = doctor.enforcement?.strictPlan || {};
|
|
110
|
+
const local = plan.localDefault || "aienvmp doctor --json";
|
|
111
|
+
const ci = plan.ciCommand || "aienvmp doctor --strict all --json";
|
|
112
|
+
const rule = plan.rule || "Strict failure is explicit only.";
|
|
113
|
+
|
|
114
|
+
fs.appendFileSync(summaryPath, [
|
|
115
|
+
"",
|
|
116
|
+
"## aienvmp strict plan",
|
|
117
|
+
"",
|
|
118
|
+
`- local: \`${local}\``,
|
|
119
|
+
`- ci: \`${ci}\``,
|
|
120
|
+
`- rule: ${rule}`,
|
|
121
|
+
""
|
|
122
|
+
].join("\n"));
|
|
123
|
+
NODE
|
|
124
|
+
fi
|
|
125
|
+
|
|
94
126
|
- name: Doctor
|
|
95
127
|
shell: bash
|
|
96
128
|
run: |
|
package/package.json
CHANGED
package/src/commands/context.js
CHANGED
|
@@ -27,6 +27,7 @@ export async function contextWorkspace(args) {
|
|
|
27
27
|
status: warnings.length ? "review-required" : "clear",
|
|
28
28
|
preflight,
|
|
29
29
|
aiReadiness: preflight.aiReadiness,
|
|
30
|
+
collaboration: preflight.collaboration,
|
|
30
31
|
coordination: preflight.coordination,
|
|
31
32
|
agentPointers: preflight.agentPointers,
|
|
32
33
|
sbomRisk: preflight.sbomRisk,
|
package/src/commands/status.js
CHANGED
|
@@ -23,6 +23,7 @@ export async function statusWorkspace(args) {
|
|
|
23
23
|
} else if (!args.quiet) {
|
|
24
24
|
console.log(`${output.state}: ${output.summary}`);
|
|
25
25
|
console.log(`ai-readiness: ${output.aiReadiness?.level || "unknown"} - ${output.aiReadiness?.next || "Run aienvmp context --json for details."}`);
|
|
26
|
+
console.log(`collaboration: ${output.collaboration?.status || "unknown"} - ${output.collaboration?.nextCommand || "aienvmp status --json"}`);
|
|
26
27
|
console.log(`next: ${output.nextCommand}`);
|
|
27
28
|
console.log(`ai: ${output.quickstart.readFirst} -> ${output.quickstart.detailCommand}`);
|
|
28
29
|
console.log(`intent: ${output.intentTargets[0]?.command || output.commands.recordIntent}`);
|
package/src/commands/summary.js
CHANGED
|
@@ -37,6 +37,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
37
37
|
const sbomRisk = status.sbomRisk || manifest.lightSbom?.riskSummary || {};
|
|
38
38
|
const coordination = status.coordination || {};
|
|
39
39
|
const activity = status.agentActivity || {};
|
|
40
|
+
const collaboration = status.collaboration || {};
|
|
40
41
|
const workspace = manifest.workspace?.root || manifest.workspace?.name || ".";
|
|
41
42
|
const next = status.nextCommand || status.decision?.nextCommand || "aienvmp status";
|
|
42
43
|
const readFirst = status.nextAgent?.readFirst || ".aienvmp/status.json";
|
|
@@ -64,6 +65,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
64
65
|
`- AI readiness: ${aiReadiness.level || "unknown"}`,
|
|
65
66
|
`- AI signals: ${aiSignals.length ? aiSignals.join("; ") : "none"}`,
|
|
66
67
|
`- AI next: ${aiNext}`,
|
|
68
|
+
`- AI collaboration: ${collaboration.status || "unknown"} / ${toList(collaboration.activeTargets).join(", ") || "none"} / ${collaboration.nextCommand || "aienvmp status --json"}`,
|
|
67
69
|
`- AI safe local work: ${toList(aiReadiness.safeProjectLocalActions)[0] || "read artifacts and avoid environment changes until reviewed"}`,
|
|
68
70
|
`- AI read first: ${readFirst}, then ${detail}`,
|
|
69
71
|
`- mode: advisory by default; strict is opt-in with ${strict}`,
|
|
@@ -79,6 +81,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
79
81
|
"## AI handoff",
|
|
80
82
|
"",
|
|
81
83
|
`- environment changes: ${status.agentUse?.environmentChanges || "intent-and-review-first"}`,
|
|
84
|
+
`- collaboration rule: ${collaboration.rule || "Record intent before shared environment changes."}`,
|
|
82
85
|
`- coordination: ${coordination.next || "No open environment intents."}`,
|
|
83
86
|
`- recent agent activity: ${activity.next || "No environment records need coordination."}`,
|
|
84
87
|
`- conflict targets: ${conflictTargets.length ? conflictTargets.join(", ") : "none"}`,
|
package/src/contract.js
CHANGED
|
@@ -4,7 +4,7 @@ export function preflightContract() {
|
|
|
4
4
|
version: 1,
|
|
5
5
|
stability: "additive",
|
|
6
6
|
requiredFields: ["schemaVersion", "state", "decision", "quickstart", "commands", "artifacts"],
|
|
7
|
-
aiEntryFields: ["state", "summary", "aiReadiness", "nextAgent", "coordination", "agentActivity", "agentPointers", "sbomRisk", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "aiReadiness", "collaboration", "nextAgent", "coordination", "agentActivity", "agentPointers", "sbomRisk", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
8
8
|
rule: "Consumers should ignore unknown fields and treat missing optional fields as unavailable."
|
|
9
9
|
};
|
|
10
10
|
}
|
|
@@ -29,7 +29,7 @@ export function schemaContract() {
|
|
|
29
29
|
},
|
|
30
30
|
context: {
|
|
31
31
|
command: "aienvmp context --json",
|
|
32
|
-
rootFields: ["status", "preflight", "aiReadiness", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
32
|
+
rootFields: ["status", "preflight", "aiReadiness", "collaboration", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
33
33
|
},
|
|
34
34
|
handoff: {
|
|
35
35
|
command: "aienvmp handoff --json",
|
|
@@ -56,6 +56,7 @@ export function schemaContract() {
|
|
|
56
56
|
consumerRule: "Ignore unknown fields. Do not require optional fields unless listed in requiredFields.",
|
|
57
57
|
localBehavior: "read-only; this command does not scan, install, update, or lock anything.",
|
|
58
58
|
aiReadinessRule: "When aiReadiness.level is review, project-local code work may still continue if aiReadiness.projectLocalWork is allowed; environment changes should follow intent/review guidance.",
|
|
59
|
+
collaborationRule: "Use collaboration.status, activeTargets, and nextCommand as the shortest multi-agent environment coordination hint.",
|
|
59
60
|
strictPlanRule: "Use enforcement.strictPlan or preflight.enforcementProfile.strictPlan to choose the narrowest explicit strict scope for CI."
|
|
60
61
|
}
|
|
61
62
|
};
|
package/src/preflight.js
CHANGED
|
@@ -19,6 +19,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
19
19
|
const sbomRisk = manifest.lightSbom?.riskSummary || {};
|
|
20
20
|
const agentPointers = agentPointerSummary(manifest.agentFiles);
|
|
21
21
|
const aiReadiness = aiReadinessSummary({ state, decision, coordination, agentActivity, agentPointers, sbomRisk, followUps });
|
|
22
|
+
const collaboration = collaborationSummary({ state, decision, coordination, agentActivity, followUps, aiReadiness });
|
|
22
23
|
return {
|
|
23
24
|
schemaVersion: 1,
|
|
24
25
|
contract: preflightContract(),
|
|
@@ -61,6 +62,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
61
62
|
nextAgent: nextAgentHint(state, dependencyReadSet, dependencyChangeProtocol),
|
|
62
63
|
coordination,
|
|
63
64
|
agentActivity,
|
|
65
|
+
collaboration,
|
|
64
66
|
agentPointers,
|
|
65
67
|
aiReadiness,
|
|
66
68
|
sbomRisk,
|
|
@@ -92,6 +94,45 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
92
94
|
};
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
function collaborationSummary({ state, decision, coordination, agentActivity, followUps, aiReadiness }) {
|
|
98
|
+
const conflictTargets = coordination?.conflictTargets || [];
|
|
99
|
+
const multiActorTargets = agentActivity?.multiActorTargets || [];
|
|
100
|
+
const followUpTargets = (followUps || []).map((item) => item.target || "environment");
|
|
101
|
+
const activeTargets = unique([...conflictTargets, ...multiActorTargets, ...followUpTargets]);
|
|
102
|
+
const reviewSignals = [];
|
|
103
|
+
if (conflictTargets.length) reviewSignals.push("open intent conflict");
|
|
104
|
+
if (multiActorTargets.length) reviewSignals.push("multi-agent environment record");
|
|
105
|
+
if ((followUps || []).length) reviewSignals.push("pending post-change follow-up");
|
|
106
|
+
if (state !== "clear") reviewSignals.push("preflight review required");
|
|
107
|
+
|
|
108
|
+
const nextCommand = firstFollowUpCommand(followUps)
|
|
109
|
+
|| (multiActorTargets.length ? "aienvmp handoff --record --actor agent:id" : "")
|
|
110
|
+
|| (conflictTargets.length ? "aienvmp plan --write" : "")
|
|
111
|
+
|| (decision?.canChangeEnvironmentWithoutReview
|
|
112
|
+
? "aienvmp intent --actor agent:id --action planned-change --target environment"
|
|
113
|
+
: "aienvmp context --json");
|
|
114
|
+
|
|
115
|
+
const status = reviewSignals.length ? "review-before-env-change" : "clear";
|
|
116
|
+
return {
|
|
117
|
+
status,
|
|
118
|
+
mode: "advisory",
|
|
119
|
+
activeTargets,
|
|
120
|
+
reviewSignals,
|
|
121
|
+
projectLocalWork: aiReadiness?.projectLocalWork || (decision?.canContinueProjectLocalWork ? "allowed" : "review-first"),
|
|
122
|
+
environmentChanges: reviewSignals.length ? "intent-review-handoff-first" : "intent-first",
|
|
123
|
+
nextCommand,
|
|
124
|
+
rule: reviewSignals.length
|
|
125
|
+
? "Do not install, remove, upgrade, downgrade, or switch shared environment tools until the listed collaboration signals are reviewed."
|
|
126
|
+
: "Multiple AI agents may continue project-local work; record intent before shared environment changes.",
|
|
127
|
+
commands: {
|
|
128
|
+
read: "aienvmp status --json",
|
|
129
|
+
plan: "aienvmp plan --write",
|
|
130
|
+
handoff: "aienvmp handoff --record --actor agent:id",
|
|
131
|
+
checkpoint: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment"
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
95
136
|
function aiReadinessSummary({ state, decision, coordination, agentActivity, agentPointers, sbomRisk, followUps }) {
|
|
96
137
|
const blockers = [];
|
|
97
138
|
const review = [];
|
|
@@ -429,6 +470,18 @@ function normalizeTarget(target = "") {
|
|
|
429
470
|
return normalized;
|
|
430
471
|
}
|
|
431
472
|
|
|
473
|
+
function unique(items = []) {
|
|
474
|
+
return [...new Set(items.map((item) => normalizeTarget(item)).filter(Boolean))];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function firstFollowUpCommand(followUps = []) {
|
|
478
|
+
for (const item of followUps || []) {
|
|
479
|
+
const command = item.commands?.find(Boolean);
|
|
480
|
+
if (command) return command;
|
|
481
|
+
}
|
|
482
|
+
return "";
|
|
483
|
+
}
|
|
484
|
+
|
|
432
485
|
function agentQuickstart(reviewRequired) {
|
|
433
486
|
return {
|
|
434
487
|
label: "10-second AI flow",
|
package/src/render.js
CHANGED
|
@@ -484,6 +484,8 @@ const followUpsHtml=followUps.length?'<div class="timeline">'+followUps.slice(0,
|
|
|
484
484
|
const agentActivity=manifest.preflight?.agentActivity||{};
|
|
485
485
|
const activityTargets=agentActivity.targets||[];
|
|
486
486
|
const activityHtml=activityTargets.length?'<div class="timeline">'+activityTargets.slice(0,5).map(a=>\`<div class="event"><time>\${esc(a.target||'env')}</time><div><b>\${esc((a.actors||[]).join(', ')||'unknown')}</b> \${esc(a.count||0)} record(s) \${a.multiActor?'<code>multi-agent</code>':'<code>single-agent</code>'}\${a.latestSummary?\`<div class="path">\${esc(a.latestSummary)}</div>\`:''}</div></div>\`).join('')+'</div><div class="path">'+esc(agentActivity.next||'Review activity before environment changes.')+'</div>':'<div class="okline">No recorded environment activity needs coordination.</div>';
|
|
487
|
+
const collaboration=manifest.preflight?.collaboration||{};
|
|
488
|
+
const collaborationHtml=\`<table><tr><th>Status</th><td><code>\${esc(collaboration.status||'unknown')}</code> \${esc(collaboration.mode||'advisory')}</td></tr><tr><th>Targets</th><td>\${esc((collaboration.activeTargets||[]).join(', ')||'none')}</td></tr><tr><th>Project work</th><td><code>\${esc(collaboration.projectLocalWork||'allowed')}</code></td></tr><tr><th>Env changes</th><td><code>\${esc(collaboration.environmentChanges||'intent-first')}</code></td></tr><tr><th>Next</th><td><code>\${esc(collaboration.nextCommand||'aienvmp status --json')}</code></td></tr></table><div class="timeline">\${(collaboration.reviewSignals||[]).slice(0,4).map(signal=>\`<div class="event"><time>review</time><div>\${esc(signal)}</div></div>\`).join('')}</div><div class="path">\${esc(collaboration.rule||'Record intent before shared environment changes.')}</div>\`;
|
|
487
489
|
const dependencyReadSet=manifest.preflight?.dependencyReadSet||[];
|
|
488
490
|
const dependencyReadSetHtml=dependencyReadSet.length?'<div class="timeline">'+dependencyReadSet.slice(0,5).map(d=>\`<div class="event"><time>\${esc(d.ecosystem||'deps')}</time><div><b>\${esc(d.manifest||'dependency files')}</b> <code>\${esc(d.manager||'unknown')}</code><div class="path">\${esc([d.manifest,...(d.lockfiles||[])].filter(Boolean).join(', '))}</div>\${d.riskPackages?.length?\`<div class="path">risk: \${esc(d.riskPackages.join(', '))}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No dependency files detected.</div>';
|
|
489
491
|
const dependencyProtocol=manifest.preflight?.dependencyChangeProtocol||{};
|
|
@@ -546,6 +548,8 @@ document.getElementById('app').innerHTML=\`
|
|
|
546
548
|
<div style="height:14px"></div>
|
|
547
549
|
\${card('Agent Activity',agentActivity.multiActorTargets?.length?'<span class="pill warn">'+agentActivity.multiActorTargets.length+' shared</span>':'<span class="pill">clear</span>',activityHtml)}
|
|
548
550
|
<div style="height:14px"></div>
|
|
551
|
+
\${card('AI Collaboration',collaboration.status==='clear'?'<span class="pill">clear</span>':'<span class="pill warn">review</span>',collaborationHtml)}
|
|
552
|
+
<div style="height:14px"></div>
|
|
549
553
|
\${card('AI Contract','<span class="pill">'+(contract.stability||'additive')+'</span>',contractHtml)}
|
|
550
554
|
<div style="height:14px"></div>
|
|
551
555
|
\${card('Dependency Read Set','<span class="pill">'+dependencyReadSet.length+' files</span>',dependencyReadSetHtml)}
|