aienvmp 0.1.67 → 0.1.68
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 +13 -0
- package/README.md +2 -1
- package/action.yml +30 -0
- package/examples/github-action.yml +1 -1
- package/package.json +1 -1
- package/src/commands/context.js +10 -0
- package/src/commands/doctor.js +9 -0
- package/src/commands/handoff.js +1 -0
- package/src/contract.js +4 -3
- package/src/preflight.js +3 -1
- package/src/render.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.68
|
|
6
|
+
|
|
7
|
+
- Added an `aienvmp AI loop` block to the GitHub Action Step Summary using `schema.aiLoop`.
|
|
8
|
+
- Kept the Action summary advisory by default while showing the same loop documented in README and `schema --json`.
|
|
9
|
+
- Updated the GitHub Action example and README CI notes to mention Step Summary strict-plan and AI-loop blocks.
|
|
10
|
+
- Added `doctor --json` `nextSafeCommand` so AI agents can pick one advisory next command without parsing every warning.
|
|
11
|
+
- Aligned the dashboard next-command fallback with `maintenanceLoop.nextCommand` so the visual surface follows the same advisory next-step chain.
|
|
12
|
+
- Added `context --json` `nextSafeCommand` so AI agents get the same one-step advisory next command in the preflight payload.
|
|
13
|
+
- Added `handoff --json` `nextSafeCommand` so the next AI can resume from one root-level advisory command.
|
|
14
|
+
- Added `status --json` and `.aienvmp/status.json` `nextSafeCommand` as a stable alias for the preflight next command.
|
|
15
|
+
|
|
3
16
|
## 0.1.67
|
|
4
17
|
|
|
5
18
|
- Added a dashboard `First read` strip with status, first file, review targets, and local operation mode.
|
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ AIENV.md # Markdown env map for AI agents
|
|
|
77
77
|
- `sbomRisk` and `sbomReview` connect light SBOM risk to safe dependency-change steps.
|
|
78
78
|
- `collaboration`, `coordination`, and `agentActivity` show multi-agent conflicts and shared targets.
|
|
79
79
|
- `strictDecision` separates local warn-only checks from optional CI strict gates.
|
|
80
|
+
- `status --json`, `context --json`, `handoff --json`, and `doctor --json` include `nextSafeCommand` for one advisory next step.
|
|
80
81
|
- `schema --json` prints the stable machine-readable contract without scanning.
|
|
81
82
|
|
|
82
83
|
## Agent Files
|
|
@@ -118,7 +119,7 @@ aienvmp doctor --strict security|policy|coordination|all
|
|
|
118
119
|
## CI
|
|
119
120
|
|
|
120
121
|
The GitHub Action writes status, summary, schema, doctor, plan, SBOM, and dashboard artifacts. `strict: "off"` reports warnings without failing the job.
|
|
121
|
-
The Step Summary includes
|
|
122
|
+
The Step Summary includes `aienvmp strict plan` and `aienvmp AI loop` blocks so humans and AI agents can choose CI gates and continue the same workflow without parsing full artifacts.
|
|
122
123
|
|
|
123
124
|
```yaml
|
|
124
125
|
- uses: soovwv/aienvmp@main
|
package/action.yml
CHANGED
|
@@ -123,6 +123,36 @@ runs:
|
|
|
123
123
|
NODE
|
|
124
124
|
fi
|
|
125
125
|
|
|
126
|
+
- name: Append AI loop summary
|
|
127
|
+
shell: bash
|
|
128
|
+
run: |
|
|
129
|
+
if [ "${{ inputs.write-summary }}" = "true" ] && [ -n "$GITHUB_STEP_SUMMARY" ]; then
|
|
130
|
+
mkdir -p "${{ inputs.directory }}/.aienvmp"
|
|
131
|
+
schema_source="${{ inputs.directory }}/.aienvmp/schema.json"
|
|
132
|
+
if [ ! -f "$schema_source" ]; then
|
|
133
|
+
schema_source="$(mktemp)"
|
|
134
|
+
node "$GITHUB_ACTION_PATH/bin/aienvmp.js" schema --json > "$schema_source"
|
|
135
|
+
fi
|
|
136
|
+
node - "$schema_source" "$GITHUB_STEP_SUMMARY" <<'NODE'
|
|
137
|
+
const fs = require("node:fs");
|
|
138
|
+
|
|
139
|
+
const [schemaPath, summaryPath] = process.argv.slice(2);
|
|
140
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
|
141
|
+
const loop = schema.aiLoop || {};
|
|
142
|
+
const steps = (loop.steps || []).slice(0, 6).map((item) => item.command).join(" -> ");
|
|
143
|
+
|
|
144
|
+
fs.appendFileSync(summaryPath, [
|
|
145
|
+
"",
|
|
146
|
+
"## aienvmp AI loop",
|
|
147
|
+
"",
|
|
148
|
+
`- mode: ${loop.localMode || "warn-only"}`,
|
|
149
|
+
`- steps: \`${steps || "aienvmp sync -> aienvmp status -> aienvmp handoff"}\``,
|
|
150
|
+
`- rule: ${loop.strictRule || "Local checks are warn-only; strict gates are explicit."}`,
|
|
151
|
+
""
|
|
152
|
+
].join("\n"));
|
|
153
|
+
NODE
|
|
154
|
+
fi
|
|
155
|
+
|
|
126
156
|
- name: Doctor
|
|
127
157
|
shell: bash
|
|
128
158
|
run: |
|
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
write-schema: "true"
|
|
18
18
|
write-doctor-json: "true"
|
|
19
19
|
write-sbom: "true"
|
|
20
|
-
write-summary: "true"
|
|
20
|
+
write-summary: "true" # appends summary, strict plan, and AI loop to GitHub Step Summary
|
|
21
21
|
strict: "off" # security, policy, coordination, all, or off
|
|
22
22
|
|
|
23
23
|
- uses: actions/upload-artifact@v4
|
package/package.json
CHANGED
package/src/commands/context.js
CHANGED
|
@@ -22,9 +22,11 @@ export async function contextWorkspace(args) {
|
|
|
22
22
|
const actions = recommendedActions(manifest, { warnings, intents });
|
|
23
23
|
const stepSummary = compactStepSummary(buildPlan(manifest, warnings, intents, policy));
|
|
24
24
|
const preflight = buildPreflight(manifest, warnings, intents, timeline);
|
|
25
|
+
const nextSafeCommand = contextNextSafeCommand(actions, warnings, preflight);
|
|
25
26
|
if (args.json) {
|
|
26
27
|
console.log(JSON.stringify({
|
|
27
28
|
status: warnings.length ? "review-required" : "clear",
|
|
29
|
+
nextSafeCommand,
|
|
28
30
|
preflight,
|
|
29
31
|
aiReadiness: preflight.aiReadiness,
|
|
30
32
|
collaboration: preflight.collaboration,
|
|
@@ -59,6 +61,14 @@ export async function contextWorkspace(args) {
|
|
|
59
61
|
console.log(renderContext({ ...manifest, preflight }, timeline, warnings, intents, policy, actions));
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
function contextNextSafeCommand(actions = [], warnings = [], preflight = {}) {
|
|
65
|
+
const actionCommand = actions.find((item) => item.command)?.command;
|
|
66
|
+
return preflight.nextCommand
|
|
67
|
+
|| preflight.maintenanceLoop?.nextCommand
|
|
68
|
+
|| actionCommand
|
|
69
|
+
|| (warnings.length ? "aienvmp plan --write" : "aienvmp status --json");
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
function lightSbomSummary(lightSbom = {}) {
|
|
63
73
|
return {
|
|
64
74
|
mode: lightSbom.mode || "light-sbom",
|
package/src/commands/doctor.js
CHANGED
|
@@ -20,6 +20,7 @@ export async function doctorWorkspace(args) {
|
|
|
20
20
|
const actions = recommendedActions(manifest, { warnings, intents });
|
|
21
21
|
const preflight = buildPreflight(manifest, warnings, intents, timeline);
|
|
22
22
|
const strict = strictResult(warnings, args);
|
|
23
|
+
const nextSafeCommand = doctorNextSafeCommand(actions, warnings);
|
|
23
24
|
const exitBehavior = {
|
|
24
25
|
mode: strict.enabled ? "strict" : "advisory",
|
|
25
26
|
willSetFailureExitCode: strict.fail,
|
|
@@ -37,6 +38,7 @@ export async function doctorWorkspace(args) {
|
|
|
37
38
|
aiReadiness: preflight.aiReadiness,
|
|
38
39
|
agentPointers: preflight.agentPointers,
|
|
39
40
|
openIntentCount: intents.length,
|
|
41
|
+
nextSafeCommand,
|
|
40
42
|
warnings,
|
|
41
43
|
recommendedActions: actions,
|
|
42
44
|
enforcement: enforcementAdvice(warnings),
|
|
@@ -63,3 +65,10 @@ export async function doctorWorkspace(args) {
|
|
|
63
65
|
process.exitCode = 1;
|
|
64
66
|
}
|
|
65
67
|
}
|
|
68
|
+
|
|
69
|
+
function doctorNextSafeCommand(actions = [], warnings = []) {
|
|
70
|
+
const command = actions.find((item) => item.command)?.command;
|
|
71
|
+
if (command) return command;
|
|
72
|
+
if (warnings.length) return "aienvmp plan --write";
|
|
73
|
+
return "aienvmp status --json";
|
|
74
|
+
}
|
package/src/commands/handoff.js
CHANGED
|
@@ -49,6 +49,7 @@ export function buildHandoff(manifest, timeline = [], warnings = [], intents = [
|
|
|
49
49
|
const preflight = buildPreflight(manifest, warnings, intents, timeline);
|
|
50
50
|
return {
|
|
51
51
|
status: reviewRequired ? "review-required" : "clear",
|
|
52
|
+
nextSafeCommand: preflight.nextCommand || preflight.maintenanceLoop?.nextCommand || "aienvmp status --json",
|
|
52
53
|
trust: manifest.trust || {},
|
|
53
54
|
schemaVersion: manifest.schemaVersion || 1,
|
|
54
55
|
preflight,
|
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", "collaboration", "maintenanceLoop", "nextAgent", "coordination", "agentActivity", "agentPointers", "sbomRisk", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "nextSafeCommand", "aiReadiness", "collaboration", "maintenanceLoop", "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
|
}
|
|
@@ -32,6 +32,7 @@ export function schemaContract() {
|
|
|
32
32
|
status: {
|
|
33
33
|
file: ".aienvmp/status.json",
|
|
34
34
|
command: "aienvmp status --json",
|
|
35
|
+
rootFields: ["state", "nextCommand", "nextSafeCommand", "decision", "counts", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "sbomRisk"],
|
|
35
36
|
contract: preflightContract()
|
|
36
37
|
},
|
|
37
38
|
summary: {
|
|
@@ -43,11 +44,11 @@ export function schemaContract() {
|
|
|
43
44
|
},
|
|
44
45
|
context: {
|
|
45
46
|
command: "aienvmp context --json",
|
|
46
|
-
rootFields: ["status", "preflight", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
47
|
+
rootFields: ["status", "nextSafeCommand", "preflight", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
47
48
|
},
|
|
48
49
|
handoff: {
|
|
49
50
|
command: "aienvmp handoff --json",
|
|
50
|
-
rootFields: ["status", "decision", "preflight", "continuation", "coordination", "dependencyHandoff", "openIntents", "warnings", "recommendedActions", "recentChanges"]
|
|
51
|
+
rootFields: ["status", "nextSafeCommand", "decision", "preflight", "continuation", "coordination", "dependencyHandoff", "openIntents", "warnings", "recommendedActions", "recentChanges"]
|
|
51
52
|
},
|
|
52
53
|
manifest: {
|
|
53
54
|
file: ".aienvmp/manifest.json",
|
package/src/preflight.js
CHANGED
|
@@ -30,6 +30,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
30
30
|
dependencyReadSet,
|
|
31
31
|
collaboration
|
|
32
32
|
});
|
|
33
|
+
const nextCommand = maintenanceLoop.nextCommand || topAction?.command || decision.nextCommand;
|
|
33
34
|
return {
|
|
34
35
|
schemaVersion: 1,
|
|
35
36
|
contract: preflightContract(),
|
|
@@ -102,7 +103,8 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
102
103
|
recordIntent: intentTargets[0]?.command || "aienvmp intent --actor agent:id --action planned-change"
|
|
103
104
|
},
|
|
104
105
|
topAction,
|
|
105
|
-
nextCommand
|
|
106
|
+
nextCommand,
|
|
107
|
+
nextSafeCommand: nextCommand
|
|
106
108
|
};
|
|
107
109
|
}
|
|
108
110
|
|
package/src/render.js
CHANGED
|
@@ -528,6 +528,7 @@ const activityTargets=agentActivity.targets||[];
|
|
|
528
528
|
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>';
|
|
529
529
|
const collaboration=manifest.preflight?.collaboration||{};
|
|
530
530
|
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>\`;
|
|
531
|
+
const maintenanceLoop=manifest.preflight?.maintenanceLoop||{};
|
|
531
532
|
const dependencyReadSet=manifest.preflight?.dependencyReadSet||[];
|
|
532
533
|
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>';
|
|
533
534
|
const dependencyProtocol=manifest.preflight?.dependencyChangeProtocol||{};
|
|
@@ -552,8 +553,8 @@ const sbomRiskValue=riskSummary.level||'unknown';
|
|
|
552
553
|
const sbomRiskClass=['urgent','high','medium'].includes(sbomRiskValue)?'review':'ready';
|
|
553
554
|
const sbomRiskScore=riskSummary.score!==undefined?' ('+riskSummary.score+')':'';
|
|
554
555
|
const sbomRiskNext=riskSummary.next||aiDependencyReview.beforeDependencyChange?.[0]||'Run aienvmp sbom --json for dependency context.';
|
|
555
|
-
const nextCommand=manifest.preflight?.nextCommand||topAction.command||collaboration.nextCommand||'aienvmp status --json';
|
|
556
|
-
const nextReason=topAction.summary||collaboration.rule||riskSummary.next||'Read status/context before changing shared environment state.';
|
|
556
|
+
const nextCommand=manifest.preflight?.nextCommand||maintenanceLoop.nextCommand||topAction.command||collaboration.nextCommand||'aienvmp status --json';
|
|
557
|
+
const nextReason=topAction.summary||maintenanceLoop.rule||collaboration.rule||riskSummary.next||'Read status/context before changing shared environment state.';
|
|
557
558
|
const coordination=manifest.preflight?.coordination||{};
|
|
558
559
|
const conflictTargets=coordination.conflictTargets||[];
|
|
559
560
|
const handoffFiles=nextAgent.dependencyFiles?.length?nextAgent.dependencyFiles:(dependencyReadSet[0]?[dependencyReadSet[0].manifest,...(dependencyReadSet[0].lockfiles||[])].filter(Boolean):[]);
|