aienvmp 0.1.51 → 0.1.53
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 +12 -0
- package/CHANGELOG.md +20 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/actions.js +20 -0
- package/src/commands/context.js +1 -0
- package/src/commands/doctor.js +2 -0
- package/src/commands/summary.js +7 -0
- package/src/contract.js +2 -2
- package/src/manifest.js +22 -1
- package/src/preflight.js +55 -0
- package/src/render.js +6 -2
package/BUGFIXES.md
CHANGED
|
@@ -100,6 +100,18 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
100
100
|
- Fix: snippets and the Codex skill now use the current status, summary, context, intent, and checkpoint flow with agent-specific actor examples.
|
|
101
101
|
- Verification: regression tests cover snippet output for AGENTS.md, Claude, and Gemini, and summary/status tests cover the read order and dependency protocol.
|
|
102
102
|
|
|
103
|
+
### AI pointer files were only detected as booleans
|
|
104
|
+
|
|
105
|
+
- Issue: AI and humans could see that an instruction file existed, but not whether it actually contained the aienvmp pointer.
|
|
106
|
+
- Fix: `agentFiles` now records path, existence, pointer marker state, role, and install command; recommended actions suggest installing a pointer without failing local work.
|
|
107
|
+
- Verification: tests cover sync pointer scanning, recommended action output, and dashboard status labels.
|
|
108
|
+
|
|
109
|
+
### AI consumers had to inspect the full manifest for pointer state
|
|
110
|
+
|
|
111
|
+
- Issue: `status`, `context`, and `doctor` did not expose a compact pointer summary even after manifest scanning detected it.
|
|
112
|
+
- Fix: `agentPointers` now appears in status/preflight, context JSON, doctor JSON, and summary markdown.
|
|
113
|
+
- Verification: regression tests cover status, context, doctor, schema, and summary outputs.
|
|
114
|
+
|
|
103
115
|
## Template
|
|
104
116
|
|
|
105
117
|
### Title
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.53
|
|
4
|
+
|
|
5
|
+
- Added `agentPointers` to the shared AI preflight/status contract.
|
|
6
|
+
- Added `agentPointers` to `context --json` at the root for quick AI access.
|
|
7
|
+
- Added `agentPointers` to `doctor --json` without making missing pointers a blocking warning.
|
|
8
|
+
- Added agent pointer status to `.aienvmp/summary.md`.
|
|
9
|
+
- Added `agentPointers` to the stable schema contract metadata.
|
|
10
|
+
- Preserved legacy boolean `agentFiles` compatibility in preflight summaries.
|
|
11
|
+
- Added regression tests for status, context, doctor, schema, and summary pointer outputs.
|
|
12
|
+
|
|
13
|
+
## 0.1.52
|
|
14
|
+
|
|
15
|
+
- Expanded `manifest.agentFiles` from booleans to lightweight agent instruction metadata.
|
|
16
|
+
- Detected whether AGENTS.md, CLAUDE.md, and GEMINI.md contain the aienvmp pointer marker.
|
|
17
|
+
- Added install commands and roles to agent instruction metadata.
|
|
18
|
+
- Added a low-priority recommended action when no AI instruction pointer is installed.
|
|
19
|
+
- Updated the dashboard Agent Pointers card to distinguish installed pointers, missing pointers, and missing files.
|
|
20
|
+
- Updated README guidance to mention pointer detection and non-blocking doctor recommendations.
|
|
21
|
+
- Added regression tests for pointer scanning, pointer recommendations, dashboard rendering, and sync output.
|
|
22
|
+
|
|
3
23
|
## 0.1.51
|
|
4
24
|
|
|
5
25
|
- Updated Codex/Claude/Gemini pointer snippets to use the current `status -> summary.md -> context` read order.
|
package/README.md
CHANGED
|
@@ -72,6 +72,8 @@ npx aienvmp snippet gemini
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Snippets point each AI to `status`, `summary.md`, `context --json`, intent, and checkpoint without taking over the instruction file.
|
|
75
|
+
`sync` records whether those pointers are installed, and `doctor` can recommend installing one without blocking local work.
|
|
76
|
+
`status`, `context --json`, and `doctor --json` expose the same `agentPointers` summary for AI consumers.
|
|
75
77
|
|
|
76
78
|
## Commands
|
|
77
79
|
|
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -21,6 +21,7 @@ export function recommendedActions(manifest = {}, context = {}) {
|
|
|
21
21
|
|
|
22
22
|
actions.push(...securityActions(manifest.security));
|
|
23
23
|
actions.push(...sbomRiskActions(manifest.lightSbom?.riskSummary));
|
|
24
|
+
actions.push(...agentPointerActions(manifest.agentFiles));
|
|
24
25
|
|
|
25
26
|
if (hasRuntimePolicyWarning(warnings)) {
|
|
26
27
|
actions.push(action("review-version-policy", "medium", "runtime", "Detected runtime or package manager policy drift. Prefer project-local version files and ask before global changes."));
|
|
@@ -74,6 +75,25 @@ function securityActions(security = {}) {
|
|
|
74
75
|
)];
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
function agentPointerActions(agentFiles = {}) {
|
|
79
|
+
const known = Object.entries(agentFiles || {}).filter(([name]) => ["agents", "claude", "gemini"].includes(name));
|
|
80
|
+
if (!known.length) return [];
|
|
81
|
+
if (known.some(([, item]) => hasPointer(item))) return [];
|
|
82
|
+
const first = known.find(([, item]) => item?.installCommand) || known[0];
|
|
83
|
+
return [action(
|
|
84
|
+
"install-agent-pointer",
|
|
85
|
+
"low",
|
|
86
|
+
"agent-instructions",
|
|
87
|
+
"Install an aienvmp pointer in an agent instruction file so future AI agents discover the env map before changing runtimes or dependencies.",
|
|
88
|
+
first?.[1]?.installCommand || "aienvmp snippet codex --write"
|
|
89
|
+
)];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function hasPointer(item) {
|
|
93
|
+
if (typeof item === "boolean") return item;
|
|
94
|
+
return item?.hasAienvmpPointer === true;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
function action(id, priority, category, summary, command = "") {
|
|
78
98
|
return { id, priority, category, summary, command };
|
|
79
99
|
}
|
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
|
coordination: preflight.coordination,
|
|
30
|
+
agentPointers: preflight.agentPointers,
|
|
30
31
|
sbomRisk: preflight.sbomRisk,
|
|
31
32
|
followUps: preflight.followUps,
|
|
32
33
|
decision,
|
package/src/commands/doctor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { intentsPath, manifestPath, timelinePath, workspaceDir } from "../paths.
|
|
|
5
5
|
import { loadPolicy, policyWarnings } from "../policy.js";
|
|
6
6
|
import { recommendedActions } from "../actions.js";
|
|
7
7
|
import { enforcementAdvice, strictResult } from "../enforcement.js";
|
|
8
|
+
import { agentPointerSummary } from "../preflight.js";
|
|
8
9
|
|
|
9
10
|
export { strictResult } from "../enforcement.js";
|
|
10
11
|
|
|
@@ -32,6 +33,7 @@ export async function doctorWorkspace(args) {
|
|
|
32
33
|
exitBehavior,
|
|
33
34
|
trust: manifest.trust || {},
|
|
34
35
|
policy,
|
|
36
|
+
agentPointers: agentPointerSummary(manifest.agentFiles),
|
|
35
37
|
openIntentCount: intents.length,
|
|
36
38
|
warnings,
|
|
37
39
|
recommendedActions: actions,
|
package/src/commands/summary.js
CHANGED
|
@@ -51,6 +51,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
51
51
|
const dependencyProtocol = status.dependencyChangeProtocol || {};
|
|
52
52
|
const dependencyCommands = dependencyProtocol.commands || {};
|
|
53
53
|
const dependencyFiles = dependencyFilesFor(status.dependencyReadSet);
|
|
54
|
+
const agentPointers = status.agentPointers || {};
|
|
54
55
|
|
|
55
56
|
return [
|
|
56
57
|
"# aienvmp summary",
|
|
@@ -86,6 +87,12 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
86
87
|
`- after: ${dependencyCommands.checkpointAfterChange || "aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency"}`,
|
|
87
88
|
`- package manager policy: ${dependencyProtocol.packageManagerPolicy || "not-detected"}`,
|
|
88
89
|
"",
|
|
90
|
+
"## Agent pointers",
|
|
91
|
+
"",
|
|
92
|
+
`- installed: ${toList(agentPointers.installed).join(", ") || "none"}`,
|
|
93
|
+
`- missing: ${toList(agentPointers.missing).join(", ") || "none"}`,
|
|
94
|
+
`- next: ${agentPointers.next || "Run aienvmp snippet codex --write if AI agents need instruction-file discovery."}`,
|
|
95
|
+
"",
|
|
89
96
|
"## Artifacts",
|
|
90
97
|
"",
|
|
91
98
|
"- AIENV.md",
|
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", "nextAgent", "coordination", "agentActivity", "sbomRisk", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "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
|
}
|
|
@@ -28,7 +28,7 @@ export function schemaContract() {
|
|
|
28
28
|
},
|
|
29
29
|
context: {
|
|
30
30
|
command: "aienvmp context --json",
|
|
31
|
-
rootFields: ["status", "preflight", "coordination", "decision", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
31
|
+
rootFields: ["status", "preflight", "coordination", "agentPointers", "decision", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
32
32
|
},
|
|
33
33
|
handoff: {
|
|
34
34
|
command: "aienvmp handoff --json",
|
package/src/manifest.js
CHANGED
|
@@ -205,11 +205,32 @@ async function scanAgentFiles(dir) {
|
|
|
205
205
|
};
|
|
206
206
|
const out = {};
|
|
207
207
|
for (const [name, rel] of Object.entries(files)) {
|
|
208
|
-
|
|
208
|
+
const full = path.join(dir, rel);
|
|
209
|
+
const fileExists = await exists(full);
|
|
210
|
+
const content = fileExists ? await fs.readFile(full, "utf8") : "";
|
|
211
|
+
out[name] = {
|
|
212
|
+
path: rel,
|
|
213
|
+
exists: fileExists,
|
|
214
|
+
hasAienvmpPointer: content.includes("<!-- aienvmp:begin -->") && content.includes("<!-- aienvmp:end -->"),
|
|
215
|
+
installCommand: snippetCommand(name),
|
|
216
|
+
role: agentRole(name)
|
|
217
|
+
};
|
|
209
218
|
}
|
|
210
219
|
return out;
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
function snippetCommand(name) {
|
|
223
|
+
if (name === "agents") return "aienvmp snippet codex --write";
|
|
224
|
+
if (["claude", "gemini"].includes(name)) return `aienvmp snippet ${name} --write`;
|
|
225
|
+
return "";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function agentRole(name) {
|
|
229
|
+
if (name === "agents") return "codex";
|
|
230
|
+
if (["claude", "gemini"].includes(name)) return name;
|
|
231
|
+
return "external";
|
|
232
|
+
}
|
|
233
|
+
|
|
213
234
|
function compact(obj) {
|
|
214
235
|
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value));
|
|
215
236
|
}
|
package/src/preflight.js
CHANGED
|
@@ -17,6 +17,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
17
17
|
const followUps = pendingFollowUps(timeline);
|
|
18
18
|
const agentActivity = agentActivitySummary(timeline);
|
|
19
19
|
const sbomRisk = manifest.lightSbom?.riskSummary || {};
|
|
20
|
+
const agentPointers = agentPointerSummary(manifest.agentFiles);
|
|
20
21
|
return {
|
|
21
22
|
schemaVersion: 1,
|
|
22
23
|
contract: preflightContract(),
|
|
@@ -58,6 +59,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
58
59
|
nextAgent: nextAgentHint(state, dependencyReadSet, dependencyChangeProtocol),
|
|
59
60
|
coordination,
|
|
60
61
|
agentActivity,
|
|
62
|
+
agentPointers,
|
|
61
63
|
sbomRisk,
|
|
62
64
|
followUps,
|
|
63
65
|
intentTargets,
|
|
@@ -123,6 +125,59 @@ function agentActivitySummary(timeline = []) {
|
|
|
123
125
|
};
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
export function agentPointerSummary(agentFiles = {}) {
|
|
129
|
+
const entries = Object.entries(agentFiles || {}).filter(([name]) => ["agents", "claude", "gemini"].includes(name));
|
|
130
|
+
const targets = entries.map(([name, item]) => {
|
|
131
|
+
const normalized = normalizeAgentFile(item);
|
|
132
|
+
return {
|
|
133
|
+
name,
|
|
134
|
+
role: normalized.role || (name === "agents" ? "codex" : name),
|
|
135
|
+
file: normalized.path || defaultAgentFile(name),
|
|
136
|
+
exists: normalized.exists,
|
|
137
|
+
hasPointer: normalized.hasAienvmpPointer,
|
|
138
|
+
installCommand: normalized.installCommand || defaultInstallCommand(name)
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const installed = targets.filter((item) => item.hasPointer);
|
|
142
|
+
const missing = targets.filter((item) => !item.hasPointer);
|
|
143
|
+
return {
|
|
144
|
+
installedCount: installed.length,
|
|
145
|
+
missingCount: missing.length,
|
|
146
|
+
installed: installed.map((item) => item.role),
|
|
147
|
+
missing: missing.map((item) => item.role),
|
|
148
|
+
targets,
|
|
149
|
+
next: missing.length
|
|
150
|
+
? `Install a pointer with ${missing[0].installCommand} if this workspace uses that AI.`
|
|
151
|
+
: "Agent instruction pointers are installed for detected AI instruction files.",
|
|
152
|
+
mode: "advisory"
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeAgentFile(item) {
|
|
157
|
+
if (typeof item === "boolean") {
|
|
158
|
+
return { exists: item, hasAienvmpPointer: item };
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
path: item?.path || "",
|
|
162
|
+
exists: item?.exists === true,
|
|
163
|
+
hasAienvmpPointer: item?.hasAienvmpPointer === true,
|
|
164
|
+
installCommand: item?.installCommand || "",
|
|
165
|
+
role: item?.role || ""
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function defaultAgentFile(name) {
|
|
170
|
+
if (name === "claude") return "CLAUDE.md";
|
|
171
|
+
if (name === "gemini") return "GEMINI.md";
|
|
172
|
+
return "AGENTS.md";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function defaultInstallCommand(name) {
|
|
176
|
+
if (name === "claude") return "aienvmp snippet claude --write";
|
|
177
|
+
if (name === "gemini") return "aienvmp snippet gemini --write";
|
|
178
|
+
return "aienvmp snippet codex --write";
|
|
179
|
+
}
|
|
180
|
+
|
|
126
181
|
function coordinationSummary(intents = []) {
|
|
127
182
|
const byTarget = new Map();
|
|
128
183
|
for (const intent of intents) {
|
package/src/render.js
CHANGED
|
@@ -445,7 +445,11 @@ const securityHtml=sec.enabled?\`<table><tr><th>Total</th><td><code>\${esc(secSu
|
|
|
445
445
|
const change=c=>c.type==='changed'?\`\${c.scope} \${c.key}: \${c.before} -> \${c.after}\`:\`\${c.scope} \${c.key}: \${c.type} \${c.after||c.before}\`;
|
|
446
446
|
const timelineLabel=t=>t.change?change(t.change):(t.summary||t.action||t.type||'recorded change');
|
|
447
447
|
const agentNames={agents:'Codex',claude:'Claude',gemini:'Gemini'};
|
|
448
|
-
const
|
|
448
|
+
const agentInfo=v=>typeof v==='object'&&v? v : {exists:!!v,hasAienvmpPointer:!!v,path:''};
|
|
449
|
+
const agentHasPointer=v=>agentInfo(v).hasAienvmpPointer===true;
|
|
450
|
+
const agentStatus=v=>agentHasPointer(v)?'aienvmp pointer installed':(agentInfo(v).exists?'file detected, pointer missing':'not detected');
|
|
451
|
+
const agentCards=Object.entries(agentNames).map(([key,label])=>\`<div class="agent"><strong>\${label}</strong><span>\${esc(agentStatus(manifest.agentFiles?.[key]))}</span>\${agentInfo(manifest.agentFiles?.[key]).installCommand?\`<span class="path">\${esc(agentInfo(manifest.agentFiles?.[key]).installCommand)}</span>\`:''}</div>\`).join('');
|
|
452
|
+
const agentPointerCount=entries(manifest.agentFiles).filter(([,v])=>agentHasPointer(v)).length;
|
|
449
453
|
const warnHtml=warnings.length?'<div class="warnings">'+warnings.map(w=>\`<div class="warning">\${esc(w.message)}</div>\`).join('')+'</div>':'<div class="okline">No blocking environment warnings detected.</div>';
|
|
450
454
|
const timelineHtml=timeline.length?'<div class="timeline">'+timeline.slice(-8).reverse().map(t=>\`<div class="event"><time>\${esc(t.at.replace('T',' ').slice(0,16))}</time><div><b>\${esc(t.actor||'system')}</b> \${esc(timelineLabel(t))}</div></div>\`).join('')+'</div>':'<div class="okline">No previous environment changes recorded.</div>';
|
|
451
455
|
const intentsHtml=intents.length?'<div class="timeline">'+intents.slice(-6).reverse().map(i=>\`<div class="event"><time>\${esc(i.at.replace('T',' ').slice(0,16))}</time><div><b>\${esc(i.actor)}</b> plans \${esc(i.action)}</div></div>\`).join('')+'</div>':'<div class="okline">No pending agent intents recorded.</div>';
|
|
@@ -561,7 +565,7 @@ document.getElementById('app').innerHTML=\`
|
|
|
561
565
|
<div style="height:14px"></div>
|
|
562
566
|
\${card('AI Handoff',reviewRequired?'<span class="pill warn">review</span>':'<span class="pill">ready</span>',handoffHtml)}
|
|
563
567
|
<div style="height:14px"></div>
|
|
564
|
-
\${card('Agent Pointers','<span class="pill">'+
|
|
568
|
+
\${card('Agent Pointers','<span class="pill">'+agentPointerCount+' installed</span>','<div class="agents">'+agentCards+'</div>')}
|
|
565
569
|
<div style="height:14px"></div>
|
|
566
570
|
\${card('Snapshot','',\`<table><tr><th>OS</th><td>\${esc(manifest.os.platform)} \${esc(manifest.os.release)} \${esc(manifest.os.arch)}</td></tr><tr><th>Shell</th><td>\${esc(manifest.os.shell||'unknown')}</td></tr><tr><th>Workspace</th><td><div class="path">\${esc(manifest.workspace.path)}</div></td></tr></table>\`)}
|
|
567
571
|
</aside>
|