aienvmp 0.1.37 → 0.1.38

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.38
4
+
5
+ - Added a 10-second AI quickstart flow to the shared preflight contract and status output.
6
+ - Added preflight intent target recommendations so agents can record runtime, package manager, dependency, Docker, or coordination changes consistently.
7
+ - Surfaced AI intent target recommendations in the dashboard so humans can review the same target guidance.
8
+ - Added the same quickstart and intent target guidance to `AIENV.md` so Markdown-first agents receive the current preflight.
9
+ - Aligned AGENTS/Claude/Gemini pointer snippets with the same status-first, target-aware AI flow.
10
+
3
11
  ## 0.1.37
4
12
 
5
13
  - Added `lightSbom` to the manifest as an AI-ready package and vulnerability summary.
package/README.md CHANGED
@@ -22,6 +22,8 @@ npx aienvmp plan
22
22
  npx aienvmp handoff
23
23
  ```
24
24
 
25
+ 10-second AI flow: `aienvmp status --write` -> `aienvmp context --json` -> intent before environment changes.
26
+
25
27
  Optional deeper read-only checks:
26
28
 
27
29
  ```bash
@@ -53,9 +55,13 @@ AIENV.md
53
55
  .aienvmp/dashboard.html # includes dependencies, plan, remediation, and environment cards
54
56
  ```
55
57
 
58
+ `AIENV.md` includes the 10-second AI flow and recommended intent targets for Markdown-first agents.
59
+
56
60
  Trust states are machine-readable: `observed`, `planned`, `changed`, `review`, `verified`, `stale`.
57
61
  `status.json` also lists AI read order, artifact paths, and safe commands.
58
62
  `status`, `context`, `plan`, and `handoff` share the same AI preflight contract.
63
+ Preflight also recommends the intent target, so agents do not guess between runtime, package manager, dependency, Docker, or coordination changes.
64
+ The dashboard shows the same intent target guidance for human review.
59
65
 
60
66
  AI agents can observe, plan, and record. Only a human or CI should mark environment facts as verified.
61
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aienvmp",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "AI-first environment maps and lightweight runtime SBOMs for shared development machines.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,6 +5,7 @@ import { openIntents, readJsonl, readTimeline } from "../timeline.js";
5
5
  import { aiEnvPath, intentsPath, manifestPath, timelinePath, workspaceDir } from "../paths.js";
6
6
  import { renderAIEnv } from "../render.js";
7
7
  import { loadPolicy, policyWarnings } from "../policy.js";
8
+ import { buildPreflight } from "../preflight.js";
8
9
 
9
10
  export async function compileWorkspace(args) {
10
11
  const dir = workspaceDir(args);
@@ -14,7 +15,10 @@ export async function compileWorkspace(args) {
14
15
  const intents = openIntents(await readJsonl(intentsPath(dir)));
15
16
  const policy = await loadPolicy(dir);
16
17
  const warnings = [...diagnose(manifest, { timeline, intents }), ...policyWarnings(manifest, policy)];
17
- const rendered = renderAIEnv(manifest, timeline, warnings, intents, policy);
18
+ const rendered = renderAIEnv({
19
+ ...manifest,
20
+ preflight: buildPreflight(manifest, warnings, intents)
21
+ }, timeline, warnings, intents, policy);
18
22
  await fs.writeFile(aiEnvPath(dir), rendered, "utf8");
19
23
  if (!args.quiet) {
20
24
  console.log(`compiled ${aiEnvPath(dir)}`);
@@ -23,6 +23,8 @@ export async function statusWorkspace(args) {
23
23
  } else if (!args.quiet) {
24
24
  console.log(`${output.state}: ${output.summary}`);
25
25
  console.log(`next: ${output.nextCommand}`);
26
+ console.log(`ai: ${output.quickstart.readFirst} -> ${output.quickstart.detailCommand}`);
27
+ console.log(`intent: ${output.intentTargets[0]?.command || output.commands.recordIntent}`);
26
28
  console.log(`strict: ${output.enforcement.recommendedCommand}`);
27
29
  if (artifact) console.log(`status: ${artifact}`);
28
30
  }
package/src/preflight.js CHANGED
@@ -8,6 +8,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
8
8
  const actions = recommendedActions(manifest, { warnings, intents });
9
9
  const state = decision.reviewRequired ? "review-required" : "clear";
10
10
  const topAction = actions[0] || null;
11
+ const intentTargets = recommendedIntentTargets(manifest, warnings, intents);
11
12
  return {
12
13
  schemaVersion: 1,
13
14
  state,
@@ -43,6 +44,8 @@ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
43
44
  projectLocalWork: decision.canContinueProjectLocalWork ? "allowed" : "review-first",
44
45
  environmentChanges: decision.canChangeEnvironmentWithoutReview ? "allowed" : "intent-and-review-first"
45
46
  },
47
+ quickstart: agentQuickstart(decision.reviewRequired),
48
+ intentTargets,
46
49
  artifacts: preflightArtifacts(),
47
50
  readOrder: [
48
51
  ".aienvmp/status.json",
@@ -58,13 +61,94 @@ export function buildPreflight(manifest = {}, warnings = [], intents = []) {
58
61
  context: "aienvmp context --json",
59
62
  plan: "aienvmp plan --write",
60
63
  handoff: "aienvmp handoff --record --actor agent:id",
61
- recordIntent: "aienvmp intent --actor agent:id --action planned-change"
64
+ recordIntent: intentTargets[0]?.command || "aienvmp intent --actor agent:id --action planned-change"
62
65
  },
63
66
  topAction,
64
67
  nextCommand: topAction?.command || decision.nextCommand
65
68
  };
66
69
  }
67
70
 
71
+ function recommendedIntentTargets(manifest = {}, warnings = [], intents = []) {
72
+ const targets = [];
73
+ for (const warning of warnings) {
74
+ addTarget(targets, targetFromWarning(warning), warning.message || warning.code, warning.code);
75
+ }
76
+ for (const intent of intents) {
77
+ addTarget(targets, intent.target || targetFromText(intent.action), `Open intent from ${intent.actor || "unknown"}.`, "open-intent");
78
+ }
79
+ const pmPolicy = manifest.lightSbom?.packageManagerPolicy;
80
+ if (pmPolicy?.status === "review-required") {
81
+ addTarget(targets, "package-manager", pmPolicy.guidance, "package-manager-policy");
82
+ }
83
+ if (Number(manifest.security?.summary?.total || 0) > 0) {
84
+ addTarget(targets, "dependency", "Security findings are dependency-related; record dependency intent before remediation.", "security");
85
+ }
86
+ if (Number(manifest.dependencySnapshot?.summary?.packages || 0) > 0) {
87
+ addTarget(targets, "dependency", "Dependency manifests detected; use this target before package changes.", "dependency-snapshot");
88
+ }
89
+ if (!targets.length) {
90
+ addTarget(targets, "environment", "Default target when the change affects runtime, dependency, container, or global tool state.", "default");
91
+ }
92
+ return targets.slice(0, 5).map((item) => ({
93
+ ...item,
94
+ command: `aienvmp intent --actor agent:id --action planned-change --target ${item.target}`
95
+ }));
96
+ }
97
+
98
+ function addTarget(targets, target, reason, source) {
99
+ const normalized = normalizeTarget(target);
100
+ if (!normalized) return;
101
+ const existing = targets.find((item) => item.target === normalized);
102
+ if (existing) {
103
+ if (source && !existing.sources.includes(source)) existing.sources.push(source);
104
+ return;
105
+ }
106
+ targets.push({ target: normalized, reason: reason || "Environment change target.", sources: source ? [source] : [] });
107
+ }
108
+
109
+ function targetFromWarning(warning = {}) {
110
+ if (warning.target) return warning.target;
111
+ const code = warning.code || "";
112
+ if (code.includes("node")) return "node";
113
+ if (code.includes("python")) return "python";
114
+ if (code.includes("docker")) return "docker";
115
+ if (code.includes("lockfile") || code.includes("package-manager")) return "package-manager";
116
+ if (code.includes("security")) return "dependency";
117
+ if (code.includes("intent") || code.includes("handoff")) return "coordination";
118
+ return targetFromText(`${warning.message || ""} ${code}`);
119
+ }
120
+
121
+ function targetFromText(text = "") {
122
+ const normalized = String(text).toLowerCase();
123
+ for (const target of ["node", "python", "docker", "package-manager", "dependency", "npm", "pnpm", "yarn", "uv", "pip", "pipx"]) {
124
+ if (normalized.includes(target)) return target;
125
+ }
126
+ if (normalized.includes("package manager") || normalized.includes("lockfile")) return "package-manager";
127
+ if (normalized.includes("vulnerab") || normalized.includes("package")) return "dependency";
128
+ return "";
129
+ }
130
+
131
+ function normalizeTarget(target = "") {
132
+ const normalized = String(target).trim().toLowerCase();
133
+ if (["npm", "pnpm", "yarn"].includes(normalized)) return "package-manager";
134
+ if (["pip", "pipx", "uv"].includes(normalized)) return "python";
135
+ return normalized;
136
+ }
137
+
138
+ function agentQuickstart(reviewRequired) {
139
+ return {
140
+ label: "10-second AI flow",
141
+ readFirst: "aienvmp status --write",
142
+ detailCommand: "aienvmp context --json",
143
+ beforeEnvironmentChange: "aienvmp intent --actor agent:id --action planned-change --target <runtime|package-manager|docker|dependency>",
144
+ afterEnvironmentChange: "aienvmp sync && aienvmp record --actor agent:id --summary what-changed",
145
+ handoff: "aienvmp handoff --record --actor agent:id",
146
+ rule: reviewRequired
147
+ ? "Review warnings or open intents before environment changes; project-local work may continue."
148
+ : "Continue project-local work; record intent before environment changes."
149
+ };
150
+ }
151
+
68
152
  export function preflightArtifacts() {
69
153
  return {
70
154
  status: ".aienvmp/status.json",
package/src/render.js CHANGED
@@ -16,6 +16,7 @@ export function renderAIEnv(manifest, timeline = [], warnings = [], intents = []
16
16
  lines.push("5. After environment changes, run `aienvmp sync`.");
17
17
  lines.push("6. Record what changed with `aienvmp record --actor agent:id --summary what-changed`.");
18
18
  lines.push("7. At handoff, run `aienvmp handoff --record --actor agent:id`.", "");
19
+ lines.push(...preflightLines(manifest.preflight), "");
19
20
  lines.push("## Current Policy", "");
20
21
  lines.push(...policyLines(policy));
21
22
  lines.push("- Enforcement: non-blocking by default; warnings require review but do not lock the machine.");
@@ -69,6 +70,31 @@ export function renderAIEnv(manifest, timeline = [], warnings = [], intents = []
69
70
  return lines.join("\n");
70
71
  }
71
72
 
73
+ function preflightLines(preflight = {}) {
74
+ const quickstart = preflight.quickstart;
75
+ const targets = preflight.intentTargets || [];
76
+ const lines = ["## 10-Second AI Flow", ""];
77
+ if (quickstart) {
78
+ lines.push(`- Read first: \`${quickstart.readFirst}\``);
79
+ lines.push(`- Detail: \`${quickstart.detailCommand}\``);
80
+ lines.push(`- Before env change: \`${quickstart.beforeEnvironmentChange}\``);
81
+ lines.push(`- After env change: \`${quickstart.afterEnvironmentChange}\``);
82
+ lines.push(`- Handoff: \`${quickstart.handoff}\``);
83
+ lines.push(`- Rule: ${quickstart.rule}`);
84
+ } else {
85
+ lines.push("- Run `aienvmp status --write`, then `aienvmp context --json` before environment changes.");
86
+ }
87
+ lines.push("", "## Recommended Intent Targets", "");
88
+ if (targets.length) {
89
+ for (const target of targets.slice(0, 5)) {
90
+ lines.push(`- ${target.target}: \`${target.command}\` - ${target.reason}`);
91
+ }
92
+ } else {
93
+ lines.push("- environment: `aienvmp intent --actor agent:id --action planned-change --target environment`");
94
+ }
95
+ return lines;
96
+ }
97
+
72
98
  export function renderAgentPointer(target = "agents") {
73
99
  const label = target === "claude" ? "Claude" : target === "gemini" ? "Gemini" : "AI agents";
74
100
  return `## aienvmp Environment Map
@@ -77,12 +103,12 @@ ${label} should use \`aienvmp\` as the workspace environment source of truth.
77
103
 
78
104
  Before changing runtimes, package managers, Docker settings, global packages, or environment policy:
79
105
 
80
- 1. Run \`aienvmp context\`.
81
- 2. Read \`AIENV.md\`.
82
- 3. If the context says \`review-required\`, ask the user before changing the environment.
83
- 4. Record planned environment changes with \`aienvmp intent\`.
84
- 5. After environment changes, run \`aienvmp sync\`.
85
- 6. Record what changed with \`aienvmp record\`.
106
+ 1. Run \`aienvmp status --write\`.
107
+ 2. Run \`aienvmp context --json\` for details.
108
+ 3. Read \`AIENV.md\`.
109
+ 4. If status or context says \`review-required\`, ask the user before changing the environment.
110
+ 5. Record planned environment changes with the recommended target, for example \`aienvmp intent --actor agent:id --action planned-change --target dependency\`.
111
+ 6. After environment changes, run \`aienvmp sync\` and \`aienvmp record --actor agent:id --summary what-changed\`.
86
112
  7. At handoff, run \`aienvmp handoff --record --actor agent:id\`.
87
113
 
88
114
  \`aienvmp\` does not replace this instruction file. It provides the live env map, lightweight runtime SBOM, intent log, timeline, and dashboard.`;
@@ -326,6 +352,8 @@ const ciReadinessHtml=ciReadiness.length?'<table>'+ciReadiness.map(s=>\`<tr><th>
326
352
  const enforcementProfile=manifest.preflight?.enforcementProfile||{};
327
353
  const strictCommands=enforcementProfile.strictCommands||[];
328
354
  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>\`;
355
+ const intentTargets=manifest.preflight?.intentTargets||[];
356
+ 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>';
329
357
  const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
330
358
  const reviewRequired=warnings.length>0||intents.length>0;
331
359
  const recentChanges=timeline.slice(-8).length;
@@ -369,6 +397,8 @@ document.getElementById('app').innerHTML=\`
369
397
  <aside>
370
398
  \${card('Recommended Actions','<span class="pill">'+actions.length+' actions</span>',actionsHtml)}
371
399
  <div style="height:14px"></div>
400
+ \${card('AI Intent Targets','<span class="pill">'+intentTargets.length+' targets</span>',intentTargetsHtml)}
401
+ <div style="height:14px"></div>
372
402
  \${card('AI Plan Artifacts',plan.markdown||plan.json?'<span class="pill">written</span>':'<span class="pill off">not written</span>',planHtml)}
373
403
  <div style="height:14px"></div>
374
404
  \${card('Remediation Steps',remediation.length?'<span class="pill warn">'+remediation.length+' items</span>':'<span class="pill off">none</span>',remediationHtml)}