aienvmp 0.1.14 → 0.1.15

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.15
4
+
5
+ - Added dashboard links for written AI plan artifacts.
6
+ - Added `write-plan` support to the GitHub Action so CI can emit read-only plan outputs.
7
+ - Updated the GitHub Action example to show the sync, plan, and doctor flow.
8
+
3
9
  ## 0.1.14
4
10
 
5
11
  - Added `aienvmp plan` for read-only AI environment action plans.
package/README.md CHANGED
@@ -36,6 +36,8 @@ AIENV.md
36
36
  .aienvmp/manifest.json
37
37
  .aienvmp/intents.jsonl
38
38
  .aienvmp/timeline.jsonl
39
+ .aienvmp/plan.json
40
+ .aienvmp/plan.md
39
41
  .aienvmp/dashboard.html
40
42
  ```
41
43
 
package/action.yml CHANGED
@@ -11,6 +11,10 @@ inputs:
11
11
  description: Fail when doctor reports warnings
12
12
  required: false
13
13
  default: "false"
14
+ write-plan:
15
+ description: Write read-only AI plan artifacts
16
+ required: false
17
+ default: "true"
14
18
 
15
19
  runs:
16
20
  using: composite
@@ -19,6 +23,13 @@ runs:
19
23
  shell: bash
20
24
  run: node "$GITHUB_ACTION_PATH/bin/aienvmp.js" sync --dir "${{ inputs.directory }}" --quiet
21
25
 
26
+ - name: Write AI plan
27
+ shell: bash
28
+ run: |
29
+ if [ "${{ inputs.write-plan }}" = "true" ]; then
30
+ node "$GITHUB_ACTION_PATH/bin/aienvmp.js" plan --dir "${{ inputs.directory }}" --write --json
31
+ fi
32
+
22
33
  - name: Doctor
23
34
  shell: bash
24
35
  run: |
@@ -13,4 +13,5 @@ jobs:
13
13
  - uses: soovwv/aienvmp@main
14
14
  with:
15
15
  directory: "."
16
+ write-plan: "true"
16
17
  strict: "false"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aienvmp",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "AI-first environment maps and lightweight runtime SBOMs for shared development machines.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,9 +2,9 @@ import fs from "node:fs/promises";
2
2
  import { execFile } from "node:child_process";
3
3
  import path from "node:path";
4
4
  import { diagnose } from "../doctor.js";
5
- import { readJson } from "../fsutil.js";
5
+ import { exists, readJson } from "../fsutil.js";
6
6
  import { openIntents, readJsonl, readTimeline } from "../timeline.js";
7
- import { dashboardPath, intentsPath, manifestPath, timelinePath, workspaceDir } from "../paths.js";
7
+ import { dashboardPath, intentsPath, manifestPath, planJsonPath, planMdPath, timelinePath, workspaceDir } from "../paths.js";
8
8
  import { renderDashboard } from "../render.js";
9
9
  import { loadPolicy, policyWarnings } from "../policy.js";
10
10
  import { recommendedActions } from "../actions.js";
@@ -17,7 +17,8 @@ export async function dashWorkspace(args) {
17
17
  const intents = openIntents(await readJsonl(intentsPath(dir)));
18
18
  const policy = await loadPolicy(dir);
19
19
  const warnings = [...diagnose(manifest, { timeline, intents }), ...policyWarnings(manifest, policy)];
20
- const html = renderDashboard({ ...manifest, recommendedActions: recommendedActions(manifest, { warnings, intents }) }, timeline, warnings, intents, policy);
20
+ const planArtifacts = await detectedPlanArtifacts(dir);
21
+ const html = renderDashboard({ ...manifest, recommendedActions: recommendedActions(manifest, { warnings, intents }), planArtifacts }, timeline, warnings, intents, policy);
21
22
  const out = dashboardPath(dir);
22
23
  await fs.mkdir(path.dirname(out), { recursive: true });
23
24
  await fs.writeFile(out, html, "utf8");
@@ -26,6 +27,15 @@ export async function dashWorkspace(args) {
26
27
  return { dashboard: out };
27
28
  }
28
29
 
30
+ async function detectedPlanArtifacts(dir) {
31
+ const json = planJsonPath(dir);
32
+ const markdown = planMdPath(dir);
33
+ return {
34
+ json: await exists(json) ? ".aienvmp/plan.json" : "",
35
+ markdown: await exists(markdown) ? ".aienvmp/plan.md" : ""
36
+ };
37
+ }
38
+
29
39
  function openFile(file) {
30
40
  if (process.platform === "win32") {
31
41
  execFile("cmd", ["/c", "start", "", file], { windowsHide: true });
package/src/render.js CHANGED
@@ -252,7 +252,7 @@ const sec=manifest.security||{};
252
252
  const secSummary=sec.summary||{total:0,critical:0,high:0,moderate:0,low:0,info:0};
253
253
  const secPackages=sec.topPackages||[];
254
254
  const securityFix=p=>p.fixVersions?.length?\`fix \${p.fixVersions.slice(0,3).join(', ')}\`:(p.fixAvailable?'fix available':'review required');
255
- const securityRefs=p=>p.advisories?.length?\` · \${p.advisories.map(a=>a.id||a.title).filter(Boolean).slice(0,2).join(', ')}\`:'';
255
+ const securityRefs=p=>p.advisories?.length?\` - \${p.advisories.map(a=>a.id||a.title).filter(Boolean).slice(0,2).join(', ')}\`:'';
256
256
  const securityHtml=sec.enabled?\`<table><tr><th>Total</th><td><code>\${esc(secSummary.total||0)}</code></td></tr><tr><th>Critical</th><td><code>\${esc(secSummary.critical||0)}</code></td></tr><tr><th>High</th><td><code>\${esc(secSummary.high||0)}</code></td></tr><tr><th>Moderate</th><td><code>\${esc(secSummary.moderate||0)}</code></td></tr><tr><th>Low</th><td><code>\${esc(secSummary.low||0)}</code></td></tr></table>\${secPackages.length?'<div class="timeline">'+secPackages.slice(0,5).map(p=>\`<div class="event"><time>\${esc(p.severity)}</time><div><b>\${esc(p.name)}</b> \${esc(securityFix(p))}\${esc(securityRefs(p))}</div></div>\`).join('')+'</div>':'<div class="okline" style="margin-top:10px">No vulnerable packages reported.</div>'}\`:'<div class="okline">Security scan is off. Run <code>aienvmp sync --security</code> for read-only vulnerability summary.</div>';
257
257
  const change=c=>c.type==='changed'?\`\${c.scope} \${c.key}: \${c.before} -> \${c.after}\`:\`\${c.scope} \${c.key}: \${c.type} \${c.after||c.before}\`;
258
258
  const timelineLabel=t=>t.change?change(t.change):(t.summary||t.action||t.type||'recorded change');
@@ -264,6 +264,8 @@ const intentsHtml=intents.length?'<div class="timeline">'+intents.slice(-6).reve
264
264
  const policyHtml=entries(policy).length?\`<table>\${rows(policy)}</table>\`:'<div class="okline">No explicit version policy set.</div>';
265
265
  const actions=manifest.recommendedActions||[];
266
266
  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>';
267
+ const plan=manifest.planArtifacts||{};
268
+ 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>';
267
269
  const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
268
270
  const reviewRequired=warnings.length>0||intents.length>0;
269
271
  const recentChanges=timeline.slice(-8).length;
@@ -305,6 +307,8 @@ document.getElementById('app').innerHTML=\`
305
307
  <aside>
306
308
  \${card('Recommended Actions','<span class="pill">'+actions.length+' actions</span>',actionsHtml)}
307
309
  <div style="height:14px"></div>
310
+ \${card('AI Plan Artifacts',plan.markdown||plan.json?'<span class="pill">written</span>':'<span class="pill off">not written</span>',planHtml)}
311
+ <div style="height:14px"></div>
308
312
  \${card('Environment Health',warnings.length?'<span class="pill warn">attention</span>':'<span class="pill">clear</span>',warnHtml)}
309
313
  <div style="height:14px"></div>
310
314
  \${card('Version Policy','<span class="pill">'+entries(policy).length+' rules</span>',policyHtml)}