@evoclock/pi-agentic-driver 0.4.3
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/LICENSE +736 -0
- package/PROVENANCE.md +69 -0
- package/README.md +325 -0
- package/config/herdr-worker-repositories.v1.json +9 -0
- package/extensions/aidr.ts +5 -0
- package/extensions/code-phage.js +144 -0
- package/extensions/herdr-communication.ts +7 -0
- package/extensions/herdr-lifecycle.ts +7 -0
- package/extensions/linux-microvm.ts +10 -0
- package/lib/adapters/diff-scope.mjs +148 -0
- package/lib/adapters/evidence.mjs +151 -0
- package/lib/adapters/narrative.mjs +171 -0
- package/lib/adapters/review-feedback.mjs +77 -0
- package/lib/adapters/visualization.mjs +176 -0
- package/lib/code-phage-core.mjs +882 -0
- package/lib/python_ast_metrics.py +378 -0
- package/lib/typescript_ast_metrics.mjs +441 -0
- package/package.json +50 -0
- package/scripts/aidr_writing_review.js +468 -0
- package/scripts/enforcement/herdr_communication_pi.js +1198 -0
- package/scripts/enforcement/herdr_lifecycle_pi.js +902 -0
- package/scripts/enforcement/linux_microvm_cutover_pi.js +328 -0
- package/scripts/enforcement/linux_microvm_remote_fixture.sh +366 -0
- package/scripts/enforcement/native_tui_context.js +11 -0
- package/templates/AGENTS.md +72 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
// visualization adapter — optional bounded visual payloads for code-phage.
|
|
5
|
+
//
|
|
6
|
+
// Concept provenance: sideshow v0.13.0 (MIT, modem-dev/sideshow, commit
|
|
7
|
+
// 81b65eb) demonstrates data-first visual review surfaces: small JSON,
|
|
8
|
+
// Mermaid, and diagram payloads that illustrate findings, updated in
|
|
9
|
+
// place rather than duplicated, behind an explicit human decision.
|
|
10
|
+
// Adapted here without sideshow's server, viewer, storage, or any
|
|
11
|
+
// network surface: this adapter is a pure function that emits bounded
|
|
12
|
+
// diagram source text. It never uploads files, never syncs traces, and
|
|
13
|
+
// never contacts a remote endpoint. Opt-in is required at every call.
|
|
14
|
+
//
|
|
15
|
+
// The palette is the first-party thermall-power-station theme from
|
|
16
|
+
// evoclock/thermall (sage / rust / peach / cream on warm dark greys).
|
|
17
|
+
export const VISUALIZATION_SCHEMA = "agentic-driver.code-phage-visualization.v1";
|
|
18
|
+
|
|
19
|
+
export const THERMALL_POWER_STATION = Object.freeze({
|
|
20
|
+
name: "thermall-power-station",
|
|
21
|
+
background: "#1a1816",
|
|
22
|
+
surface: "#383431",
|
|
23
|
+
panel: "#4a4540",
|
|
24
|
+
boost: "#5c564f",
|
|
25
|
+
sage: "#79c39e",
|
|
26
|
+
rust: "#e77843",
|
|
27
|
+
peach: "#ee9b69",
|
|
28
|
+
cream: "#ead1b5",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const MAX_NODES = 48;
|
|
32
|
+
const MAX_LABEL_CHARS = 64;
|
|
33
|
+
const MAX_EDGES = 96;
|
|
34
|
+
|
|
35
|
+
function truncateLabel(value) {
|
|
36
|
+
const text = String(value ?? "");
|
|
37
|
+
return text.length > MAX_LABEL_CHARS ? `${text.slice(0, MAX_LABEL_CHARS - 1)}…` : text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sanitizeLine(value) {
|
|
41
|
+
// Keep diagram source single-line safe for Mermaid and D2.
|
|
42
|
+
return String(value ?? "").replaceAll('"', "'").replaceAll(/[\r\n]+/g, " ").trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function classifyNode(entry, anchor) {
|
|
46
|
+
if (anchor && Object.values(anchor.overCeiling || {}).some(Boolean)) return "over-ceiling";
|
|
47
|
+
if (anchor) return "touched";
|
|
48
|
+
return "file";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function nodeColor(kind) {
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case "over-ceiling": return THERMALL_POWER_STATION.rust;
|
|
54
|
+
case "touched": return THERMALL_POWER_STATION.sage;
|
|
55
|
+
default: return THERMALL_POWER_STATION.peach;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// One bounded graph of changed files and their touched callables.
|
|
60
|
+
function buildGraph(result) {
|
|
61
|
+
const nodes = [];
|
|
62
|
+
const edges = [];
|
|
63
|
+
const changedCallables = Array.isArray(result?.changedCallables) ? result.changedCallables : [];
|
|
64
|
+
const seenFileNodes = new Set();
|
|
65
|
+
for (const entry of changedCallables) {
|
|
66
|
+
if (!entry || typeof entry.path !== "string" || !entry.path) continue;
|
|
67
|
+
const fileNode = `f:${entry.path}`;
|
|
68
|
+
if (nodes.length >= MAX_NODES) break;
|
|
69
|
+
// Duplicate file entries share one file node so downstream ID maps stay unique.
|
|
70
|
+
const isNewFileNode = !seenFileNodes.has(fileNode);
|
|
71
|
+
if (isNewFileNode) {
|
|
72
|
+
seenFileNodes.add(fileNode);
|
|
73
|
+
nodes.push({ id: fileNode, kind: "file", label: truncateLabel(entry.path), color: nodeColor("file") });
|
|
74
|
+
}
|
|
75
|
+
const anchors = Array.isArray(entry.evidence?.anchors) ? entry.evidence.anchors : [];
|
|
76
|
+
for (const anchor of anchors) {
|
|
77
|
+
if (nodes.length >= MAX_NODES) break;
|
|
78
|
+
const kind = classifyNode(entry, anchor);
|
|
79
|
+
const id = `c:${entry.path}#${anchor.name}#${nodes.length}`;
|
|
80
|
+
nodes.push({ id, kind, label: truncateLabel(anchor.name), color: nodeColor(kind) });
|
|
81
|
+
if (edges.length < MAX_EDGES) edges.push({ from: fileNode, to: id });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { nodes, edges };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function safeId(id, used) {
|
|
88
|
+
let base = "n" + String(id).replaceAll(/[^a-zA-Z0-9]/g, "_");
|
|
89
|
+
let candidate = base;
|
|
90
|
+
let suffix = 2;
|
|
91
|
+
while (used.has(candidate)) candidate = `${base}_${suffix++}`;
|
|
92
|
+
used.add(candidate);
|
|
93
|
+
return candidate;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Mermaid source: portable Markdown fallback rendered by GitHub/GitLab and
|
|
97
|
+
// most Markdown viewers. Class assignment is per node kind, not global.
|
|
98
|
+
export function buildMermaid(result) {
|
|
99
|
+
const { nodes, edges } = buildGraph(result);
|
|
100
|
+
const used = new Set();
|
|
101
|
+
const mermaidId = new Map(nodes.map((node) => [node.id, safeId(node.id, used)]));
|
|
102
|
+
const lines = ["graph TD"];
|
|
103
|
+
for (const node of nodes) {
|
|
104
|
+
lines.push(` ${mermaidId.get(node.id)}["${sanitizeLine(node.label)}"]`);
|
|
105
|
+
}
|
|
106
|
+
for (const edge of edges) {
|
|
107
|
+
lines.push(` ${mermaidId.get(edge.from)} --> ${mermaidId.get(edge.to)}`);
|
|
108
|
+
}
|
|
109
|
+
lines.push(` classDef overCeiling stroke:${THERMALL_POWER_STATION.rust},color:${THERMALL_POWER_STATION.cream}`);
|
|
110
|
+
lines.push(` classDef touched stroke:${THERMALL_POWER_STATION.sage},color:${THERMALL_POWER_STATION.cream}`);
|
|
111
|
+
lines.push(` classDef file stroke:${THERMALL_POWER_STATION.peach},color:${THERMALL_POWER_STATION.cream}`);
|
|
112
|
+
for (const kind of ["over-ceiling", "touched", "file"]) {
|
|
113
|
+
const ids = nodes.filter((n) => n.kind === kind).map((n) => mermaidId.get(n.id));
|
|
114
|
+
if (ids.length) lines.push(` class ${ids.join(",")} ${kind === "over-ceiling" ? "overCeiling" : kind}`);
|
|
115
|
+
}
|
|
116
|
+
return { schema: VISUALIZATION_SCHEMA, format: "mermaid", theme: THERMALL_POWER_STATION.name, source: lines.join("\n"), nodeCount: nodes.length, edgeCount: edges.length };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// D2 source: preferred for high-quality browser rendering (e.g. an
|
|
120
|
+
// optional Sideshow surface). Styling mirrors thermall-power-station.
|
|
121
|
+
export function buildD2(result) {
|
|
122
|
+
const { nodes, edges } = buildGraph(result);
|
|
123
|
+
const lines = [`direction: down`, `style.fill: "${THERMALL_POWER_STATION.background}"`];
|
|
124
|
+
lines.push("classes: {");
|
|
125
|
+
lines.push(` overCeiling: { style: { fill: "${THERMALL_POWER_STATION.surface}"; stroke: "${THERMALL_POWER_STATION.rust}"; stroke-width: 4; border-radius: 12; font-color: "${THERMALL_POWER_STATION.cream}"; bold: true } }`);
|
|
126
|
+
lines.push(` touched: { style: { fill: "${THERMALL_POWER_STATION.surface}"; stroke: "${THERMALL_POWER_STATION.sage}"; stroke-width: 4; border-radius: 12; font-color: "${THERMALL_POWER_STATION.cream}"; bold: true } }`);
|
|
127
|
+
lines.push(` file: { style: { fill: "${THERMALL_POWER_STATION.panel}"; stroke: "${THERMALL_POWER_STATION.peach}"; stroke-width: 4; border-radius: 12; font-color: "${THERMALL_POWER_STATION.cream}"; bold: true } }`);
|
|
128
|
+
lines.push("}");
|
|
129
|
+
for (const node of nodes) {
|
|
130
|
+
lines.push(`"${sanitizeLine(node.id)}": "${sanitizeLine(node.label)}" { class: ${node.kind === "over-ceiling" ? "overCeiling" : node.kind === "touched" ? "touched" : "file"} }`);
|
|
131
|
+
}
|
|
132
|
+
for (const edge of edges) {
|
|
133
|
+
lines.push(`"${sanitizeLine(edge.from)}" -> "${sanitizeLine(edge.to)}": { style.stroke: "${THERMALL_POWER_STATION.boost}"; style.stroke-width: 3 }`);
|
|
134
|
+
}
|
|
135
|
+
return { schema: VISUALIZATION_SCHEMA, format: "d2", theme: THERMALL_POWER_STATION.name, source: lines.join("\n"), nodeCount: nodes.length, edgeCount: edges.length };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Compact JSON payload: the data-first surface for optional viewers.
|
|
139
|
+
// Fails closed to an empty payload on malformed input.
|
|
140
|
+
export function buildVisualJson(result) {
|
|
141
|
+
const { nodes, edges } = buildGraph(result);
|
|
142
|
+
return {
|
|
143
|
+
schema: VISUALIZATION_SCHEMA,
|
|
144
|
+
format: "json",
|
|
145
|
+
theme: THERMALL_POWER_STATION.name,
|
|
146
|
+
nodes,
|
|
147
|
+
edges,
|
|
148
|
+
nodeCount: nodes.length,
|
|
149
|
+
edgeCount: edges.length,
|
|
150
|
+
counts: {
|
|
151
|
+
changedFiles: result?.changedScope?.changedFiles ?? 0,
|
|
152
|
+
touchedCallables: result?.changedScope?.touchedCallables ?? 0,
|
|
153
|
+
reviewFeedback: result?.summary?.reviewFeedback ?? { AMEND: 0, CONSULT: 0 },
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Single opt-in entry point. `options.enabled` must be exactly true:
|
|
159
|
+
// visualization is never implicit, mirroring the sideshow lesson that
|
|
160
|
+
// surfaces and trace sync must not activate without a human decision.
|
|
161
|
+
export function buildVisualization(result, options = {}) {
|
|
162
|
+
if (options.enabled !== true) {
|
|
163
|
+
return { schema: VISUALIZATION_SCHEMA, enabled: false, reason: "visualization is opt-in; pass { enabled: true } to build a payload." };
|
|
164
|
+
}
|
|
165
|
+
const format = options.format || "json";
|
|
166
|
+
const builders = { json: buildVisualJson, mermaid: buildMermaid, d2: buildD2 };
|
|
167
|
+
const builder = builders[format];
|
|
168
|
+
if (!builder) {
|
|
169
|
+
return { schema: VISUALIZATION_SCHEMA, enabled: false, reason: `unknown format ${format}; use json, mermaid, or d2.` };
|
|
170
|
+
}
|
|
171
|
+
return { enabled: true, noNetwork: true, noFileWrites: true, noTraceUpload: true, ...builder(result) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function safeIdForD2(id) {
|
|
175
|
+
return String(id).replaceAll(/[^a-zA-Z0-9]/g, "_");
|
|
176
|
+
}
|