aienvmp 0.1.68 → 0.1.69
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 +10 -0
- package/README.md +4 -0
- package/package.json +1 -1
- package/src/commands/context.js +1 -0
- package/src/commands/handoff.js +1 -0
- package/src/commands/plan.js +4 -1
- package/src/commands/sbom.js +35 -4
- package/src/commands/summary.js +7 -4
- package/src/contract.js +10 -5
- package/src/preflight.js +17 -0
- package/src/render.js +28 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.69
|
|
6
|
+
|
|
7
|
+
- Added `aiBootstrap` to the shared preflight surfaces so AI agents can read the shortest first-read, next-command, and local-mode hint.
|
|
8
|
+
- Mirrored `aiBootstrap` in the dashboard first-read area so humans see the same AI entry hint.
|
|
9
|
+
- Added the same bootstrap hint to `summary.md` so Markdown-first AI agents can start from the compact artifact.
|
|
10
|
+
- Added the bootstrap hint to `AIENV.md` so the main environment map matches the JSON, summary, and dashboard entry point.
|
|
11
|
+
- Added `aiBootstrap` and `nextSafeCommand` to plan JSON and the same bootstrap hint to `plan.md`.
|
|
12
|
+
- Added `aiBootstrap` and `nextSafeCommand` to the standalone light SBOM artifact so dependency review starts from the same AI loop.
|
|
13
|
+
- Added `aienvmp:aiBootstrap:*` properties to CycloneDX-lite output so external SBOM consumers can find the same next-step hint.
|
|
14
|
+
|
|
5
15
|
## 0.1.68
|
|
6
16
|
|
|
7
17
|
- Added an `aienvmp AI loop` block to the GitHub Action Step Summary using `schema.aiLoop`.
|
package/README.md
CHANGED
|
@@ -73,11 +73,15 @@ AIENV.md # Markdown env map for AI agents
|
|
|
73
73
|
## AI Contract
|
|
74
74
|
|
|
75
75
|
- `status`, `context`, `plan`, and `handoff` share one additive preflight contract.
|
|
76
|
+
- `aiBootstrap` gives AI the shortest read-first, next-command, and local-mode hint.
|
|
77
|
+
- `AIENV.md`, `summary.md`, and `plan.md` start with the same bootstrap hint for Markdown-first agents.
|
|
78
|
+
- `sbom.json` and CycloneDX-lite properties include the same bootstrap hint for dependency and security review loops.
|
|
76
79
|
- `maintenanceLoop` gives AI the recurring env-management loop.
|
|
77
80
|
- `sbomRisk` and `sbomReview` connect light SBOM risk to safe dependency-change steps.
|
|
78
81
|
- `collaboration`, `coordination`, and `agentActivity` show multi-agent conflicts and shared targets.
|
|
79
82
|
- `strictDecision` separates local warn-only checks from optional CI strict gates.
|
|
80
83
|
- `status --json`, `context --json`, `handoff --json`, and `doctor --json` include `nextSafeCommand` for one advisory next step.
|
|
84
|
+
- The dashboard mirrors `aiBootstrap` so humans and AI see the same first-read and next-command hint.
|
|
81
85
|
- `schema --json` prints the stable machine-readable contract without scanning.
|
|
82
86
|
|
|
83
87
|
## Agent Files
|
package/package.json
CHANGED
package/src/commands/context.js
CHANGED
|
@@ -27,6 +27,7 @@ export async function contextWorkspace(args) {
|
|
|
27
27
|
console.log(JSON.stringify({
|
|
28
28
|
status: warnings.length ? "review-required" : "clear",
|
|
29
29
|
nextSafeCommand,
|
|
30
|
+
aiBootstrap: preflight.aiBootstrap,
|
|
30
31
|
preflight,
|
|
31
32
|
aiReadiness: preflight.aiReadiness,
|
|
32
33
|
collaboration: preflight.collaboration,
|
package/src/commands/handoff.js
CHANGED
|
@@ -50,6 +50,7 @@ export function buildHandoff(manifest, timeline = [], warnings = [], intents = [
|
|
|
50
50
|
return {
|
|
51
51
|
status: reviewRequired ? "review-required" : "clear",
|
|
52
52
|
nextSafeCommand: preflight.nextCommand || preflight.maintenanceLoop?.nextCommand || "aienvmp status --json",
|
|
53
|
+
aiBootstrap: preflight.aiBootstrap,
|
|
53
54
|
trust: manifest.trust || {},
|
|
54
55
|
schemaVersion: manifest.schemaVersion || 1,
|
|
55
56
|
preflight,
|
package/src/commands/plan.js
CHANGED
|
@@ -36,11 +36,14 @@ export async function planWorkspace(args) {
|
|
|
36
36
|
export function buildPlan(manifest, warnings = [], intents = [], policy = {}, timeline = []) {
|
|
37
37
|
const actions = recommendedActions(manifest, { warnings, intents });
|
|
38
38
|
const status = warnings.length || intents.length ? "review-required" : "clear";
|
|
39
|
+
const preflight = buildPreflight(manifest, warnings, intents, timeline);
|
|
39
40
|
return {
|
|
40
41
|
schemaVersion: 1,
|
|
41
42
|
generatedAt: new Date().toISOString(),
|
|
42
43
|
status,
|
|
43
|
-
|
|
44
|
+
aiBootstrap: preflight.aiBootstrap,
|
|
45
|
+
nextSafeCommand: preflight.nextSafeCommand || preflight.nextCommand,
|
|
46
|
+
preflight,
|
|
44
47
|
workspace: manifest.workspace || {},
|
|
45
48
|
trust: manifest.trust || {},
|
|
46
49
|
decision: aiDecision(warnings, intents),
|
package/src/commands/sbom.js
CHANGED
|
@@ -22,6 +22,11 @@ export async function sbomWorkspace(args = {}) {
|
|
|
22
22
|
|
|
23
23
|
export function buildSbomArtifact(manifest = {}) {
|
|
24
24
|
const lightSbom = manifest.lightSbom || {};
|
|
25
|
+
const dependencyReview = lightSbom.aiDependencyReview || aiDependencyReview(lightSbom);
|
|
26
|
+
const nextSafeCommand = dependencyReview.beforeDependencyChange?.[0]
|
|
27
|
+
|| lightSbom.riskSummary?.commands?.[0]
|
|
28
|
+
|| "aienvmp context --json";
|
|
29
|
+
const aiBootstrap = sbomBootstrap(nextSafeCommand, dependencyReview);
|
|
25
30
|
return {
|
|
26
31
|
schemaVersion: 1,
|
|
27
32
|
schemaName: "aienvmp.light-sbom",
|
|
@@ -36,16 +41,31 @@ export function buildSbomArtifact(manifest = {}) {
|
|
|
36
41
|
topRisk: (lightSbom.topRisk || []).slice(0, 20),
|
|
37
42
|
packageManagerPolicy: lightSbom.packageManagerPolicy || {},
|
|
38
43
|
dependencyChangeHints: (lightSbom.dependencyChangeHints || []).slice(0, 20),
|
|
39
|
-
|
|
44
|
+
aiBootstrap,
|
|
45
|
+
nextSafeCommand,
|
|
46
|
+
aiDependencyReview: dependencyReview,
|
|
40
47
|
aiUse: {
|
|
41
48
|
purpose: "Standalone AI-readable light SBOM artifact.",
|
|
42
49
|
readBefore: "Dependency changes, vulnerability remediation, release review, or shared AI handoff.",
|
|
43
|
-
nextCommand:
|
|
50
|
+
nextCommand: nextSafeCommand,
|
|
44
51
|
rule: "Use as a lightweight planning map; verify security claims with dedicated scanners."
|
|
45
52
|
}
|
|
46
53
|
};
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
function sbomBootstrap(nextSafeCommand, review = {}) {
|
|
57
|
+
return {
|
|
58
|
+
purpose: "Shortest AI entry point for dependency and SBOM review.",
|
|
59
|
+
readFirst: ".aienvmp/sbom.json",
|
|
60
|
+
detailCommand: "aienvmp context --json",
|
|
61
|
+
nextSafeCommand,
|
|
62
|
+
localMode: "advisory",
|
|
63
|
+
projectLocalWork: "allowed",
|
|
64
|
+
environmentChanges: review.status === "review" ? "review-first" : "intent-first",
|
|
65
|
+
rule: review.rule || "Read SBOM risk first; record intent before dependency or lockfile changes."
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
49
69
|
function aiDependencyReview(lightSbom = {}) {
|
|
50
70
|
const risk = lightSbom.riskSummary || {};
|
|
51
71
|
const hints = lightSbom.dependencyChangeHints || [];
|
|
@@ -97,6 +117,11 @@ export function buildCycloneDxLite(manifest = {}) {
|
|
|
97
117
|
const snapshot = manifest.dependencySnapshot || {};
|
|
98
118
|
const packages = snapshot.packages || [];
|
|
99
119
|
const lightSbom = manifest.lightSbom || {};
|
|
120
|
+
const dependencyReview = lightSbom.aiDependencyReview || aiDependencyReview(lightSbom);
|
|
121
|
+
const nextSafeCommand = dependencyReview.beforeDependencyChange?.[0]
|
|
122
|
+
|| lightSbom.riskSummary?.commands?.[0]
|
|
123
|
+
|| "aienvmp context --json";
|
|
124
|
+
const aiBootstrap = sbomBootstrap(nextSafeCommand, dependencyReview);
|
|
100
125
|
return {
|
|
101
126
|
bomFormat: "CycloneDX",
|
|
102
127
|
specVersion: "1.6",
|
|
@@ -121,14 +146,20 @@ export function buildCycloneDxLite(manifest = {}) {
|
|
|
121
146
|
{ name: "aienvmp:source", value: lightSbom.source?.dependencies || "project manifests" },
|
|
122
147
|
{ name: "aienvmp:confidence:transitiveDependencies", value: lightSbom.confidence?.transitiveDependencies || "not-resolved" },
|
|
123
148
|
{ name: "aienvmp:risk:level", value: lightSbom.riskSummary?.level || "clear" },
|
|
124
|
-
{ name: "aienvmp:risk:score", value: String(lightSbom.riskSummary?.score || 0) }
|
|
149
|
+
{ name: "aienvmp:risk:score", value: String(lightSbom.riskSummary?.score || 0) },
|
|
150
|
+
{ name: "aienvmp:aiBootstrap:readFirst", value: aiBootstrap.readFirst },
|
|
151
|
+
{ name: "aienvmp:aiBootstrap:detailCommand", value: aiBootstrap.detailCommand },
|
|
152
|
+
{ name: "aienvmp:aiBootstrap:nextSafeCommand", value: aiBootstrap.nextSafeCommand },
|
|
153
|
+
{ name: "aienvmp:aiBootstrap:localMode", value: aiBootstrap.localMode },
|
|
154
|
+
{ name: "aienvmp:aiBootstrap:environmentChanges", value: aiBootstrap.environmentChanges }
|
|
125
155
|
]
|
|
126
156
|
},
|
|
127
157
|
components: packages.slice(0, 200).map(cycloneComponent),
|
|
128
158
|
vulnerabilities: (lightSbom.topRisk || []).slice(0, 50).map(cycloneVulnerability),
|
|
129
159
|
properties: [
|
|
130
160
|
{ name: "aienvmp:limitation", value: "Light SBOM from project manifests only; no install or dependency resolver was run." },
|
|
131
|
-
{ name: "aienvmp:verifyWith", value: "CycloneDX, Syft, Trivy, npm audit, pip-audit, or another dedicated scanner before security claims." }
|
|
161
|
+
{ name: "aienvmp:verifyWith", value: "CycloneDX, Syft, Trivy, npm audit, pip-audit, or another dedicated scanner before security claims." },
|
|
162
|
+
{ name: "aienvmp:aiBootstrap:rule", value: aiBootstrap.rule }
|
|
132
163
|
]
|
|
133
164
|
};
|
|
134
165
|
}
|
package/src/commands/summary.js
CHANGED
|
@@ -40,10 +40,11 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
40
40
|
const collaboration = status.collaboration || {};
|
|
41
41
|
const maintenanceLoop = status.maintenanceLoop || {};
|
|
42
42
|
const sbomReview = maintenanceLoop.sbomReview || {};
|
|
43
|
+
const aiBootstrap = status.aiBootstrap || {};
|
|
43
44
|
const workspace = manifest.workspace?.root || manifest.workspace?.name || ".";
|
|
44
|
-
const next = status.nextCommand || status.decision?.nextCommand || "aienvmp status";
|
|
45
|
-
const readFirst = status.nextAgent?.readFirst || ".aienvmp/status.json";
|
|
46
|
-
const detail = status.quickstart?.detailCommand || "aienvmp context --json";
|
|
45
|
+
const next = aiBootstrap.nextSafeCommand || status.nextSafeCommand || status.nextCommand || status.decision?.nextCommand || "aienvmp status";
|
|
46
|
+
const readFirst = aiBootstrap.readFirst || status.nextAgent?.readFirst || ".aienvmp/status.json";
|
|
47
|
+
const detail = aiBootstrap.detailCommand || status.quickstart?.detailCommand || "aienvmp context --json";
|
|
47
48
|
const strict = status.enforcement?.recommendedCommand || "aienvmp doctor --strict all";
|
|
48
49
|
const riskLevel = sbomRisk.level || "unknown";
|
|
49
50
|
const riskScore = valueOrZero(sbomRisk.score);
|
|
@@ -67,11 +68,13 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
67
68
|
"",
|
|
68
69
|
`- AI readiness: ${aiReadiness.level || "unknown"}`,
|
|
69
70
|
`- AI signals: ${aiSignals.length ? aiSignals.join("; ") : "none"}`,
|
|
70
|
-
`- AI
|
|
71
|
+
`- AI bootstrap: ${aiBootstrap.projectLocalWork || "allowed"} / ${aiBootstrap.environmentChanges || status.agentUse?.environmentChanges || "intent-first"} / ${aiBootstrap.localMode || "advisory"}`,
|
|
72
|
+
`- AI next: ${next} (${aiNext})`,
|
|
71
73
|
`- AI collaboration: ${collaboration.status || "unknown"} / ${toList(collaboration.activeTargets).join(", ") || "none"} / ${collaboration.nextCommand || "aienvmp status --json"}`,
|
|
72
74
|
`- AI maintenance loop: ${maintenanceLoop.nextCommand || next}`,
|
|
73
75
|
`- AI safe local work: ${toList(aiReadiness.safeProjectLocalActions)[0] || "read artifacts and avoid environment changes until reviewed"}`,
|
|
74
76
|
`- AI read first: ${readFirst}, then ${detail}`,
|
|
77
|
+
`- AI bootstrap rule: ${aiBootstrap.rule || "Read status first, use context for details, and keep local checks advisory."}`,
|
|
75
78
|
`- mode: advisory by default; strict is opt-in with ${strict}`,
|
|
76
79
|
`- local check: ${strictDecision.localCommand || "aienvmp doctor --json"} (${strictDecision.local || "warn-only"})`,
|
|
77
80
|
`- CI strict: ${strictPlan.ciCommand || `${strict} --json`}`,
|
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", "nextSafeCommand", "aiReadiness", "collaboration", "maintenanceLoop", "nextAgent", "coordination", "agentActivity", "agentPointers", "sbomRisk", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "aiBootstrap", "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,7 +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
|
+
rootFields: ["state", "aiBootstrap", "nextCommand", "nextSafeCommand", "decision", "counts", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "sbomRisk"],
|
|
36
36
|
contract: preflightContract()
|
|
37
37
|
},
|
|
38
38
|
summary: {
|
|
@@ -42,13 +42,18 @@ export function schemaContract() {
|
|
|
42
42
|
purpose: "Compact AI and CI step summary for quick review.",
|
|
43
43
|
startsWith: ["AI readiness", "AI signals", "AI next"]
|
|
44
44
|
},
|
|
45
|
+
plan: {
|
|
46
|
+
file: ".aienvmp/plan.json",
|
|
47
|
+
command: "aienvmp plan --json",
|
|
48
|
+
rootFields: ["schemaVersion", "status", "aiBootstrap", "nextSafeCommand", "preflight", "decision", "enforcement", "recommendedActions", "reviewGates", "remediationSteps", "environmentSteps"]
|
|
49
|
+
},
|
|
45
50
|
context: {
|
|
46
51
|
command: "aienvmp context --json",
|
|
47
|
-
rootFields: ["status", "nextSafeCommand", "preflight", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
52
|
+
rootFields: ["status", "aiBootstrap", "nextSafeCommand", "preflight", "aiReadiness", "collaboration", "maintenanceLoop", "coordination", "agentPointers", "decision", "enforcement", "recommendedActions", "workspace", "dependencySnapshot", "lightSbom", "warnings"]
|
|
48
53
|
},
|
|
49
54
|
handoff: {
|
|
50
55
|
command: "aienvmp handoff --json",
|
|
51
|
-
rootFields: ["status", "nextSafeCommand", "decision", "preflight", "continuation", "coordination", "dependencyHandoff", "openIntents", "warnings", "recommendedActions", "recentChanges"]
|
|
56
|
+
rootFields: ["status", "aiBootstrap", "nextSafeCommand", "decision", "preflight", "continuation", "coordination", "dependencyHandoff", "openIntents", "warnings", "recommendedActions", "recentChanges"]
|
|
52
57
|
},
|
|
53
58
|
manifest: {
|
|
54
59
|
file: ".aienvmp/manifest.json",
|
|
@@ -57,7 +62,7 @@ export function schemaContract() {
|
|
|
57
62
|
sbom: {
|
|
58
63
|
file: ".aienvmp/sbom.json",
|
|
59
64
|
command: "aienvmp sbom --json",
|
|
60
|
-
rootFields: ["schemaVersion", "schemaName", "workspace", "summary", "riskSummary", "topRisk", "packageManagerPolicy", "dependencyChangeHints", "aiDependencyReview"],
|
|
65
|
+
rootFields: ["schemaVersion", "schemaName", "workspace", "aiBootstrap", "nextSafeCommand", "summary", "riskSummary", "topRisk", "packageManagerPolicy", "dependencyChangeHints", "aiDependencyReview"],
|
|
61
66
|
aiDependencyReviewFields: ["status", "statusReason", "securityConfidence", "readFirst", "reviewTargets", "beforeDependencyChange", "afterDependencyChange", "rule"]
|
|
62
67
|
},
|
|
63
68
|
cyclonedxLite: {
|
package/src/preflight.js
CHANGED
|
@@ -31,6 +31,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
31
31
|
collaboration
|
|
32
32
|
});
|
|
33
33
|
const nextCommand = maintenanceLoop.nextCommand || topAction?.command || decision.nextCommand;
|
|
34
|
+
const aiBootstrap = aiBootstrapSummary({ state, decision, nextCommand });
|
|
34
35
|
return {
|
|
35
36
|
schemaVersion: 1,
|
|
36
37
|
contract: preflightContract(),
|
|
@@ -70,6 +71,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
70
71
|
projectLocalWork: decision.canContinueProjectLocalWork ? "allowed" : "review-first",
|
|
71
72
|
environmentChanges: decision.canChangeEnvironmentWithoutReview ? "allowed" : "intent-and-review-first"
|
|
72
73
|
},
|
|
74
|
+
aiBootstrap,
|
|
73
75
|
quickstart: agentQuickstart(decision.reviewRequired),
|
|
74
76
|
nextAgent: nextAgentHint(state, dependencyReadSet, dependencyChangeProtocol),
|
|
75
77
|
coordination,
|
|
@@ -108,6 +110,21 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
function aiBootstrapSummary({ state, decision, nextCommand }) {
|
|
114
|
+
return {
|
|
115
|
+
purpose: "Shortest AI entry point for this workspace environment.",
|
|
116
|
+
readFirst: ".aienvmp/status.json",
|
|
117
|
+
detailCommand: "aienvmp context --json",
|
|
118
|
+
nextSafeCommand: nextCommand || "aienvmp status --json",
|
|
119
|
+
localMode: "advisory",
|
|
120
|
+
projectLocalWork: decision.canContinueProjectLocalWork ? "allowed" : "review-first",
|
|
121
|
+
environmentChanges: decision.canChangeEnvironmentWithoutReview ? "intent-first" : "review-first",
|
|
122
|
+
rule: state === "clear"
|
|
123
|
+
? "Continue project-local work; record intent before shared environment changes."
|
|
124
|
+
: "Review context before shared environment changes; local checks remain non-blocking."
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
111
128
|
function maintenanceLoopSummary({ state, decision, topAction, followUps, sbomRisk, aiDependencyReview = {}, dependencyReadSet, collaboration }) {
|
|
112
129
|
const followUpCommand = firstFollowUpCommand(followUps);
|
|
113
130
|
const dependencyAware = dependencyReadSet.length > 0;
|
package/src/render.js
CHANGED
|
@@ -70,10 +70,17 @@ export function renderAIEnv(manifest, timeline = [], warnings = [], intents = []
|
|
|
70
70
|
|
|
71
71
|
function preflightLines(preflight = {}) {
|
|
72
72
|
const quickstart = preflight.quickstart;
|
|
73
|
+
const aiBootstrap = preflight.aiBootstrap || {};
|
|
73
74
|
const targets = preflight.intentTargets || [];
|
|
74
75
|
const maintenanceLoop = preflight.maintenanceLoop || {};
|
|
75
76
|
const lines = ["## 10-Second AI Flow", ""];
|
|
76
|
-
if (
|
|
77
|
+
if (aiBootstrap.nextSafeCommand || aiBootstrap.readFirst) {
|
|
78
|
+
lines.push(`- AI bootstrap: ${aiBootstrap.projectLocalWork || "allowed"} / ${aiBootstrap.environmentChanges || "intent-first"} / ${aiBootstrap.localMode || "advisory"}`);
|
|
79
|
+
lines.push(`- Next safe command: \`${aiBootstrap.nextSafeCommand || preflight.nextSafeCommand || preflight.nextCommand || "aienvmp status --json"}\``);
|
|
80
|
+
lines.push(`- Read first: \`${aiBootstrap.readFirst || ".aienvmp/status.json"}\``);
|
|
81
|
+
lines.push(`- Detail: \`${aiBootstrap.detailCommand || "aienvmp context --json"}\``);
|
|
82
|
+
lines.push(`- Rule: ${aiBootstrap.rule || "Read status first, use context for details, and keep local checks advisory."}`);
|
|
83
|
+
} else if (quickstart) {
|
|
77
84
|
lines.push(`- Read first: \`${quickstart.readFirst}\``);
|
|
78
85
|
lines.push(`- Detail: \`${quickstart.detailCommand}\``);
|
|
79
86
|
lines.push(`- Before env change: \`${quickstart.beforeEnvironmentChange}\``);
|
|
@@ -288,10 +295,16 @@ function dependencyHandoffLines(dependencyHandoff = {}) {
|
|
|
288
295
|
}
|
|
289
296
|
|
|
290
297
|
export function renderPlan(plan) {
|
|
298
|
+
const aiBootstrap = plan.aiBootstrap || plan.preflight?.aiBootstrap || {};
|
|
299
|
+
const nextSafeCommand = plan.nextSafeCommand || aiBootstrap.nextSafeCommand || plan.preflight?.nextSafeCommand || plan.preflight?.nextCommand || "aienvmp status --json";
|
|
291
300
|
const lines = [
|
|
292
301
|
"# AI Environment Plan",
|
|
293
302
|
"",
|
|
294
303
|
`Status: ${plan.status}`,
|
|
304
|
+
`AI bootstrap: ${aiBootstrap.projectLocalWork || "allowed"} / ${aiBootstrap.environmentChanges || "intent-first"} / ${aiBootstrap.localMode || "advisory"}`,
|
|
305
|
+
`Next safe command: ${nextSafeCommand}`,
|
|
306
|
+
`Read first: ${aiBootstrap.readFirst || ".aienvmp/status.json"} -> ${aiBootstrap.detailCommand || "aienvmp context --json"}`,
|
|
307
|
+
`Bootstrap rule: ${aiBootstrap.rule || "Read status first, use context for details, and keep local checks advisory."}`,
|
|
295
308
|
`Decision: ${plan.decision?.mode || plan.status}`,
|
|
296
309
|
`Enforcement: ${plan.enforcement?.mode || "advisory-by-default"} (${plan.enforcement?.localBehavior || "non-blocking"})`,
|
|
297
310
|
`Generated: ${plan.generatedAt}`,
|
|
@@ -413,7 +426,7 @@ h1,h2,h3,p{margin:0}h1{font-size:clamp(28px,4vw,46px);line-height:1.02;margin-to
|
|
|
413
426
|
.nextbar b{color:var(--green);font-size:12px;text-transform:uppercase;letter-spacing:.08em}
|
|
414
427
|
.nextbar code{display:inline-block;max-width:100%;white-space:normal;overflow-wrap:anywhere}
|
|
415
428
|
.nextbar span{color:var(--muted);font-size:12px;overflow-wrap:anywhere}
|
|
416
|
-
.brief{display:grid;grid-template-columns:1fr
|
|
429
|
+
.brief{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;margin:0 0 14px}
|
|
417
430
|
.brief-item{border:1px solid var(--line2);background:rgba(9,19,16,.9);border-radius:8px;padding:10px;min-width:0}
|
|
418
431
|
.brief-k{color:var(--muted);font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.08em}
|
|
419
432
|
.brief-v{margin-top:5px;font-size:13px;font-weight:700;color:var(--text);overflow-wrap:anywhere}
|
|
@@ -541,6 +554,7 @@ const nextAction=reviewRequired?'Review before environment changes':'Proceed wit
|
|
|
541
554
|
const auditItem=(key,value,hint,klass='')=>\`<div class="audit-item \${klass}"><div class="audit-k">\${key}</div><div class="audit-v">\${value}</div><div class="audit-hint">\${hint}</div></div>\`;
|
|
542
555
|
const controlCard=(label,value,next,klass='')=>\`<div class="control-card \${klass}"><div class="control-label">\${label}</div><div class="control-value">\${esc(value)}</div><div class="control-next">\${esc(next)}</div></div>\`;
|
|
543
556
|
const driftLabel=warnings.length?'detected':'none';
|
|
557
|
+
const aiBootstrap=manifest.preflight?.aiBootstrap||{};
|
|
544
558
|
const nextAgent=manifest.preflight?.nextAgent||{};
|
|
545
559
|
const aiReadiness=manifest.preflight?.aiReadiness||{};
|
|
546
560
|
const aiReadinessSignals=(aiReadiness.signals||[]).slice(0,3);
|
|
@@ -553,17 +567,18 @@ const sbomRiskValue=riskSummary.level||'unknown';
|
|
|
553
567
|
const sbomRiskClass=['urgent','high','medium'].includes(sbomRiskValue)?'review':'ready';
|
|
554
568
|
const sbomRiskScore=riskSummary.score!==undefined?' ('+riskSummary.score+')':'';
|
|
555
569
|
const sbomRiskNext=riskSummary.next||aiDependencyReview.beforeDependencyChange?.[0]||'Run aienvmp sbom --json for dependency context.';
|
|
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.';
|
|
570
|
+
const nextCommand=aiBootstrap.nextSafeCommand||manifest.preflight?.nextSafeCommand||manifest.preflight?.nextCommand||maintenanceLoop.nextCommand||topAction.command||collaboration.nextCommand||'aienvmp status --json';
|
|
571
|
+
const nextReason=topAction.summary||aiBootstrap.rule||maintenanceLoop.rule||collaboration.rule||riskSummary.next||'Read status/context before changing shared environment state.';
|
|
558
572
|
const coordination=manifest.preflight?.coordination||{};
|
|
559
573
|
const conflictTargets=coordination.conflictTargets||[];
|
|
560
574
|
const handoffFiles=nextAgent.dependencyFiles?.length?nextAgent.dependencyFiles:(dependencyReadSet[0]?[dependencyReadSet[0].manifest,...(dependencyReadSet[0].lockfiles||[])].filter(Boolean):[]);
|
|
561
575
|
const handoffNext=nextAgent.rule||(reviewRequired?'Review warnings and open intents':'Continue project-local work');
|
|
562
|
-
const firstRead=nextAgent.readFirst||'.aienvmp/status.json';
|
|
576
|
+
const firstRead=aiBootstrap.readFirst||nextAgent.readFirst||'.aienvmp/status.json';
|
|
563
577
|
const reviewTargets=[...new Set([...(conflictTargets||[]),...(collaboration.activeTargets||[]),...(riskSummary.reviewTargets||[])].filter(Boolean))];
|
|
564
|
-
const safeMode=enforcementProfile.gate?.localDefault||enforcementProfile.localOperation||'warn-only';
|
|
578
|
+
const safeMode=aiBootstrap.localMode||enforcementProfile.gate?.localDefault||enforcementProfile.localOperation||'warn-only';
|
|
579
|
+
const bootstrapState=[aiBootstrap.projectLocalWork||'allowed',aiBootstrap.environmentChanges||'intent-first'].join(' / ');
|
|
565
580
|
const briefItem=(key,value)=>\`<div class="brief-item"><div class="brief-k">\${key}</div><div class="brief-v">\${esc(value)}</div></div>\`;
|
|
566
|
-
const handoffHtml=\`<table><tr><th>Status</th><td>\${reviewRequired?'review-required':'clear'}</td></tr><tr><th>Trust</th><td><code>\${esc(trustState)}</code></td></tr><tr><th>Read first</th><td><code>\${esc(
|
|
581
|
+
const handoffHtml=\`<table><tr><th>Status</th><td>\${reviewRequired?'review-required':'clear'}</td></tr><tr><th>Trust</th><td><code>\${esc(trustState)}</code></td></tr><tr><th>Read first</th><td><code>\${esc(firstRead)}</code></td></tr><tr><th>Dependency files</th><td>\${handoffFiles.length?'<code>'+esc(handoffFiles.join(', '))+'</code>':'none'}</td></tr><tr><th>Conflicts</th><td>\${conflictTargets.length?'<code>'+esc(conflictTargets.join(', '))+'</code>':'none'}</td></tr><tr><th>Next</th><td>\${esc(handoffNext)}</td></tr></table>\`;
|
|
567
582
|
document.getElementById('app').innerHTML=\`
|
|
568
583
|
<header>
|
|
569
584
|
<div>
|
|
@@ -584,6 +599,7 @@ document.getElementById('app').innerHTML=\`
|
|
|
584
599
|
<span>\${esc(nextReason)}</span>
|
|
585
600
|
</section>
|
|
586
601
|
<section class="brief" aria-label="First read">
|
|
602
|
+
\${briefItem('AI bootstrap',bootstrapState)}
|
|
587
603
|
\${briefItem('Status',reviewRequired?'review required':'clear')}
|
|
588
604
|
\${briefItem('Read first',firstRead)}
|
|
589
605
|
\${briefItem('Review targets',reviewTargets.length?reviewTargets.slice(0,4).join(', '):'none')}
|
|
@@ -688,9 +704,13 @@ function formatTimeline(item) {
|
|
|
688
704
|
}
|
|
689
705
|
|
|
690
706
|
function contextLines(manifest, warnings, intents) {
|
|
707
|
+
const preflight = manifest.preflight || {};
|
|
708
|
+
const aiBootstrap = preflight.aiBootstrap || {};
|
|
691
709
|
return [
|
|
692
710
|
`- Status: ${warnings.length ? "review-required" : "clear"}`,
|
|
693
|
-
`- Next: ${warnings.length ? "review warnings before environment changes" : "continue with project-local work"}`,
|
|
711
|
+
`- Next: ${aiBootstrap.nextSafeCommand || preflight.nextSafeCommand || preflight.nextCommand || (warnings.length ? "review warnings before environment changes" : "continue with project-local work")}`,
|
|
712
|
+
`- Bootstrap: ${aiBootstrap.projectLocalWork || "allowed"} / ${aiBootstrap.environmentChanges || "intent-first"} / ${aiBootstrap.localMode || "advisory"}`,
|
|
713
|
+
`- Read first: ${aiBootstrap.readFirst || ".aienvmp/status.json"} -> ${aiBootstrap.detailCommand || "aienvmp context --json"}`,
|
|
694
714
|
`- Node: ${manifest.runtimes.node || "not detected"}`,
|
|
695
715
|
`- Python: ${manifest.runtimes.python || manifest.runtimes.python3 || "not detected"}`,
|
|
696
716
|
`- Docker: ${manifest.containers.docker ? "available" : "not detected"}`,
|