aienvmp 0.1.17 → 0.1.19
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 +12 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/commands/dash.js +13 -1
- package/src/commands/plan.js +76 -0
- package/src/render.js +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.19
|
|
4
|
+
|
|
5
|
+
- Added runtime, package manager, Docker, and coordination environment steps to `aienvmp plan`.
|
|
6
|
+
- Kept environment plans read-only and ask-first for global changes.
|
|
7
|
+
- Improved AI guidance for resolving version drift and multi-agent coordination warnings.
|
|
8
|
+
|
|
9
|
+
## 0.1.18
|
|
10
|
+
|
|
11
|
+
- Added a dashboard remediation steps card backed by `.aienvmp/plan.json`.
|
|
12
|
+
- Kept dashboard remediation details bounded to package, severity, fix version, and advisory references.
|
|
13
|
+
- Improved human visibility for AI-generated read-only security plans.
|
|
14
|
+
|
|
3
15
|
## 0.1.17
|
|
4
16
|
|
|
5
17
|
- Added bounded security remediation steps to `aienvmp plan`.
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ AIENV.md
|
|
|
38
38
|
.aienvmp/timeline.jsonl
|
|
39
39
|
.aienvmp/plan.json
|
|
40
40
|
.aienvmp/plan.md
|
|
41
|
-
.aienvmp/dashboard.html
|
|
41
|
+
.aienvmp/dashboard.html # includes plan and remediation cards
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Trust states are machine-readable: `observed`, `planned`, `changed`, `review`, `verified`, `stale`.
|
|
@@ -59,7 +59,7 @@ npx aienvmp snippet agents
|
|
|
59
59
|
aienvmp sync # update env map, light SBOM, ledger, dashboard
|
|
60
60
|
aienvmp context # AI preflight brief
|
|
61
61
|
aienvmp context --json # machine-readable AI decision context + recommended actions
|
|
62
|
-
aienvmp plan # read-only AI action plan
|
|
62
|
+
aienvmp plan # read-only AI action plan for drift and remediation
|
|
63
63
|
aienvmp handoff # next-agent handoff summary + recommended actions
|
|
64
64
|
aienvmp intent # record a planned env change
|
|
65
65
|
aienvmp record # record what changed
|
package/package.json
CHANGED
package/src/commands/dash.js
CHANGED
|
@@ -18,7 +18,8 @@ export async function dashWorkspace(args) {
|
|
|
18
18
|
const policy = await loadPolicy(dir);
|
|
19
19
|
const warnings = [...diagnose(manifest, { timeline, intents }), ...policyWarnings(manifest, policy)];
|
|
20
20
|
const planArtifacts = await detectedPlanArtifacts(dir);
|
|
21
|
-
const
|
|
21
|
+
const planRemediation = await detectedPlanRemediation(dir);
|
|
22
|
+
const html = renderDashboard({ ...manifest, recommendedActions: recommendedActions(manifest, { warnings, intents }), planArtifacts, planRemediation }, timeline, warnings, intents, policy);
|
|
22
23
|
const out = dashboardPath(dir);
|
|
23
24
|
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
24
25
|
await fs.writeFile(out, html, "utf8");
|
|
@@ -36,6 +37,17 @@ async function detectedPlanArtifacts(dir) {
|
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
async function detectedPlanRemediation(dir) {
|
|
41
|
+
const plan = await readJson(planJsonPath(dir), {});
|
|
42
|
+
return (plan.remediationSteps || []).slice(0, 5).map((item) => ({
|
|
43
|
+
package: item.package || "unknown",
|
|
44
|
+
severity: item.severity || "unknown",
|
|
45
|
+
fixVersions: Array.isArray(item.fixVersions) ? item.fixVersions.slice(0, 3) : [],
|
|
46
|
+
fixAvailable: item.fixAvailable === true,
|
|
47
|
+
advisories: Array.isArray(item.advisories) ? item.advisories.map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 2) : []
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
function openFile(file) {
|
|
40
52
|
if (process.platform === "win32") {
|
|
41
53
|
execFile("cmd", ["/c", "start", "", file], { windowsHide: true });
|
package/src/commands/plan.js
CHANGED
|
@@ -57,6 +57,7 @@ export function buildPlan(manifest, warnings = [], intents = [], policy = {}) {
|
|
|
57
57
|
topPackages: manifest.security?.topPackages || []
|
|
58
58
|
},
|
|
59
59
|
remediationSteps: remediationSteps(manifest.security),
|
|
60
|
+
environmentSteps: environmentSteps(warnings),
|
|
60
61
|
notes: [
|
|
61
62
|
"This plan is read-only.",
|
|
62
63
|
"Ask the user before global runtime, package manager, Docker, or global package changes.",
|
|
@@ -65,6 +66,81 @@ export function buildPlan(manifest, warnings = [], intents = [], policy = {}) {
|
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
function environmentSteps(warnings = []) {
|
|
70
|
+
return warnings
|
|
71
|
+
.filter((warning) => warning.code !== "security-vulnerabilities")
|
|
72
|
+
.slice(0, 8)
|
|
73
|
+
.map((warning) => ({
|
|
74
|
+
code: warning.code,
|
|
75
|
+
category: environmentCategory(warning.code),
|
|
76
|
+
summary: warning.message,
|
|
77
|
+
steps: environmentStepLines(warning.code)
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function environmentCategory(code = "") {
|
|
82
|
+
if (["conflicting-open-intents", "stale-open-intent", "handoff-stale"].includes(code)) return "coordination";
|
|
83
|
+
if (code.includes("docker")) return "container";
|
|
84
|
+
if (code.includes("lockfile") || code.includes("package-manager")) return "package-manager";
|
|
85
|
+
if (code.includes("node") || code.includes("python") || code.includes("version") || code.includes("runtime")) return "runtime";
|
|
86
|
+
return "environment";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function environmentStepLines(code = "") {
|
|
90
|
+
if (code === "node-version-mismatch") return [
|
|
91
|
+
"Read .nvmrc and the detected Node version before changing tools.",
|
|
92
|
+
"Prefer project-local version managers such as nvm, mise, or asdf.",
|
|
93
|
+
"Ask before changing global Node or npm installations.",
|
|
94
|
+
"Run aienvmp sync and record the change after alignment."
|
|
95
|
+
];
|
|
96
|
+
if (code === "python-version-mismatch") return [
|
|
97
|
+
"Read .python-version and the detected Python version before changing tools.",
|
|
98
|
+
"Prefer project-local virtual environments or version managers.",
|
|
99
|
+
"Ask before changing global Python installations.",
|
|
100
|
+
"Run aienvmp sync and record the change after alignment."
|
|
101
|
+
];
|
|
102
|
+
if (code === "mixed-node-lockfiles" || code === "package-manager-policy-mismatch") return [
|
|
103
|
+
"Identify the intended package manager from project policy and lockfiles.",
|
|
104
|
+
"Do not delete lockfiles without user approval.",
|
|
105
|
+
"Use one package manager for dependency changes.",
|
|
106
|
+
"Record the chosen package manager policy if it changes."
|
|
107
|
+
];
|
|
108
|
+
if (code === "python-missing") return [
|
|
109
|
+
"Confirm whether the Python project is active.",
|
|
110
|
+
"Prefer project-local Python setup before global installation.",
|
|
111
|
+
"Ask before installing a global Python runtime.",
|
|
112
|
+
"Run aienvmp sync after setup."
|
|
113
|
+
];
|
|
114
|
+
if (code === "docker-missing") return [
|
|
115
|
+
"Confirm whether Docker is required for the current task.",
|
|
116
|
+
"Do not change Docker daemon or context without user approval.",
|
|
117
|
+
"Document fallback commands if Docker is unavailable.",
|
|
118
|
+
"Run aienvmp sync after Docker availability changes."
|
|
119
|
+
];
|
|
120
|
+
if (code === "manifest-stale") return [
|
|
121
|
+
"Run aienvmp sync before environment changes.",
|
|
122
|
+
"Review context again after refresh."
|
|
123
|
+
];
|
|
124
|
+
if (code === "conflicting-open-intents") return [
|
|
125
|
+
"Review open intents before changing the environment.",
|
|
126
|
+
"Coordinate with the user or other agent.",
|
|
127
|
+
"Resolve stale or superseded intents before proceeding."
|
|
128
|
+
];
|
|
129
|
+
if (code === "stale-open-intent") return [
|
|
130
|
+
"Confirm whether the old intent is still valid.",
|
|
131
|
+
"Resolve or renew the intent before changing the environment."
|
|
132
|
+
];
|
|
133
|
+
if (code === "handoff-stale") return [
|
|
134
|
+
"Run aienvmp handoff --record --actor agent:id before the next AI continues.",
|
|
135
|
+
"Review recent changes before new environment work."
|
|
136
|
+
];
|
|
137
|
+
return [
|
|
138
|
+
"Review the warning before changing environment state.",
|
|
139
|
+
"Ask before global environment changes.",
|
|
140
|
+
"Run aienvmp sync after any accepted change."
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
68
144
|
function remediationSteps(security = {}) {
|
|
69
145
|
if (!security.enabled) return [];
|
|
70
146
|
return (security.topPackages || []).slice(0, 8).map((pkg) => {
|
package/src/render.js
CHANGED
|
@@ -185,6 +185,9 @@ export function renderPlan(plan) {
|
|
|
185
185
|
"Remediation steps:",
|
|
186
186
|
...(plan.remediationSteps?.length ? plan.remediationSteps.slice(0, 5).flatMap(remediationLines) : ["- none"]),
|
|
187
187
|
"",
|
|
188
|
+
"Environment steps:",
|
|
189
|
+
...(plan.environmentSteps?.length ? plan.environmentSteps.slice(0, 5).flatMap(environmentLines) : ["- none"]),
|
|
190
|
+
"",
|
|
188
191
|
"Warnings:",
|
|
189
192
|
...(plan.warnings.length ? plan.warnings.map((warning) => `- [${warning.code}] ${warning.message}`) : ["- none"]),
|
|
190
193
|
""
|
|
@@ -192,6 +195,13 @@ export function renderPlan(plan) {
|
|
|
192
195
|
return lines.join("\n");
|
|
193
196
|
}
|
|
194
197
|
|
|
198
|
+
function environmentLines(item) {
|
|
199
|
+
return [
|
|
200
|
+
`- ${item.category}: ${item.summary}`,
|
|
201
|
+
...item.steps.slice(0, 4).map((step) => ` - ${step}`)
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
|
|
195
205
|
function remediationLines(item) {
|
|
196
206
|
const fix = item.fixVersions?.length ? `fix ${item.fixVersions.join(", ")}` : item.fixAvailable ? "fix available" : "review required";
|
|
197
207
|
const advisories = (item.advisories || []).map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 2);
|
|
@@ -278,6 +288,10 @@ const actions=manifest.recommendedActions||[];
|
|
|
278
288
|
const actionsHtml=actions.length?'<div class="timeline">'+actions.slice(0,6).map(a=>\`<div class="event"><time>\${esc(a.priority)}</time><div><b>\${esc(a.category)}</b> \${esc(a.summary)}\${a.command?\`<div class="path">\${esc(a.command)}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No recommended actions. Continue project-local work.</div>';
|
|
279
289
|
const plan=manifest.planArtifacts||{};
|
|
280
290
|
const planHtml=plan.markdown||plan.json?\`<table><tr><th>Markdown</th><td>\${plan.markdown?'<a href="plan.md">plan.md</a>':'not written'}</td></tr><tr><th>JSON</th><td>\${plan.json?'<a href="plan.json">plan.json</a>':'not written'}</td></tr></table>\`:'<div class="okline">No plan artifacts yet. Run <code>aienvmp plan --write</code>.</div>';
|
|
291
|
+
const remediation=manifest.planRemediation||[];
|
|
292
|
+
const remediationFix=r=>r.fixVersions?.length?\`fix \${r.fixVersions.join(', ')}\`:(r.fixAvailable?'fix available':'review required');
|
|
293
|
+
const remediationRefs=r=>r.advisories?.length?\` - \${r.advisories.join(', ')}\`:'';
|
|
294
|
+
const remediationHtml=remediation.length?'<div class="timeline">'+remediation.map(r=>\`<div class="event"><time>\${esc(r.severity)}</time><div><b>\${esc(r.package)}</b> \${esc(remediationFix(r))}\${esc(remediationRefs(r))}</div></div>\`).join('')+'</div>':'<div class="okline">No remediation steps in the current plan.</div>';
|
|
281
295
|
const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
|
|
282
296
|
const reviewRequired=warnings.length>0||intents.length>0;
|
|
283
297
|
const recentChanges=timeline.slice(-8).length;
|
|
@@ -321,6 +335,8 @@ document.getElementById('app').innerHTML=\`
|
|
|
321
335
|
<div style="height:14px"></div>
|
|
322
336
|
\${card('AI Plan Artifacts',plan.markdown||plan.json?'<span class="pill">written</span>':'<span class="pill off">not written</span>',planHtml)}
|
|
323
337
|
<div style="height:14px"></div>
|
|
338
|
+
\${card('Remediation Steps',remediation.length?'<span class="pill warn">'+remediation.length+' items</span>':'<span class="pill off">none</span>',remediationHtml)}
|
|
339
|
+
<div style="height:14px"></div>
|
|
324
340
|
\${card('Environment Health',warnings.length?'<span class="pill warn">attention</span>':'<span class="pill">clear</span>',warnHtml)}
|
|
325
341
|
<div style="height:14px"></div>
|
|
326
342
|
\${card('Version Policy','<span class="pill">'+entries(policy).length+' rules</span>',policyHtml)}
|