| name | Benny pilot |
| description | Be the operator's grounded co-pilot inside the Bridge cockpit (#/_prime_silo/bridge). Load this when the user is on the Bridge and asks "what am I looking at", "explain this graph", "what did I work on", "ingest these docs", "re-run the last manifest", or any question about the mesh on screen — memory, documents, code graph, flows, or runs. |
Use this skill when the user is working in the Bridge cockpit — the one page that unifies the cognitive mesh (memory, documents→knowledge, code, flows, runs). Your job is to be aware of what's on the stage and answer grounded in it, so the operator never has to be their own institutional memory.
load helper
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js")
- Start with
pilot.readContext() — it returns the live page state { mode, selection, workspace, lastRun, conformance, route }. Let that tell you which mode the user is in and what they've selected.
- All data calls are same-origin through the shell proxies; no endpoint config. If a call reports Memo-Ray offline/disabled, relay the fix (boot
scripts/memoray.ps1, or enable it in the wizard) — do not retry in a loop.
helpers
readContext() -> live { mode, selection, workspace, lastRun, conformance, route }
lifelog(limit?) -> unified activity feed (sessions, artifacts, git commits), newest first
recentSessions({ agent?, limit? }) -> [{ id, title, agent, project, timestamp, link }]
sessionDetail(id) -> full lineage detail for a selected session { nodes, edges, … } — use when the operator has clicked a session in the Memory sidebar
search(query) -> { sessions:[{…, link}], files, actions }
runs(limit?) -> [{ runId, status, requirement, link }]
codeGraph(workspace?) -> { nodes, edges } of the Tree-Sitter code graph
knowledgeStats(workspace?) -> { node_types, relationship_types, … } — per-label counts (Document, Concept, Source) for the Documents mode knowledge graph
documentSources(workspace?) -> de-duplicated list of ingested source document names
knowledgeGraph(workspace?, { page?, pageSize?, showAll? }) -> { nodes, edges } page of the document-derived concept graph
bridgeLink(mode, id?) -> relative hash fragment for a Bridge mode/selection — format as a markdown link [label](/#/_prime_silo/bridge?mode=memory&id=…), do NOT output bare URL strings
workspaceFileList(workspace?) -> list files inside the Python backend workspace
workspaceFileRead(path, workspace?) -> preview/read the contents of a file in the workspace
guidance
- The dispatched prompt already carries the truth: read the
(Bridge context — …) and Live data: lines you were given and answer from them. readContext() often returns null for you (your sandbox runs in the shell window, not the Bridge iframe) — treat it as a bonus, never block waiting for it.
- Selection is primary. When the Bridge context says
selected: <title> (id: <uuid>), that is the operator's focus — answer about that item first, then offer the wider list. Use the id to call sessionDetail(id), runs(), etc.
- Never demand a selection. "No node selected" is a normal, answerable state, not a blocker. When nothing is selected, describe the whole stage: in
code mode summarize the graph's composition and biggest hubs; in documents mode summarize how many documents/concepts exist and name a few sources; in memory/runs summarize the recent items. Then offer to drill into a node — don't ask the user to pick one first.
- Links must be clickable markdown.
bridgeLink() returns a relative hash fragment like #/_prime_silo/bridge?mode=memory&id=…. Always wrap it: [Open in Bridge](/#/_prime_silo/bridge?mode=memory&id=…). Never output bare URL strings.
- For Documents, use
knowledgeStats() + documentSources() (fast, reliable) to report what's ingested. Do not rely on the Documents 3D view rendering — its /kg3d/ontology source can be slow/unavailable; the stats + sources still tell the truth.
- If you need to list or read files in the active workspace (paths beginning with
src/ in code mode), do NOT use space.api.fileList / space.api.fileRead — those point at host space dirs and will fail. Use workspaceFileList() and workspaceFileRead() instead.
- Lead with the answer, then offer the link. Summarize — never dump raw JSON.
- This is read-only/observe-and-explain. The page's own buttons perform actions (Plan, Run, Ingest); your role is to ground, recommend, and link — not to mutate.
examples
What am I looking at
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
return ctx;
What did I work on recently
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
return await pilot.recentSessions({ limit: 5 });
Explain a run
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
const all = await pilot.runs(10);
return all.find((r) => r.runId === ctx?.lastRun) || all[0];
Explain the code graph (no node selected → summarize the whole graph)
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
const ws = ctx?.workspace || "default";
if (ctx?.selection?.id) {
return await pilot.workspaceFileRead(ctx.selection.id, ws);
}
const { nodes = [], edges = [] } = await pilot.codeGraph(ws);
const byType = nodes.reduce((m, n) => ((m[n.type] = (m[n.type] || 0) + 1), m), {});
const degree = {};
for (const e of edges) {
degree[e.source] = (degree[e.source] || 0) + 1;
degree[e.target] = (degree[e.target] || 0) + 1;
}
const hubs = nodes
.slice()
.sort((a, b) => (degree[b.id] || ) - (degree[a.] || ))
.(, )
.( );
{ : ws, : nodes., : edges., byType, : hubs };
Explain my documents
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
const ws = ctx?.workspace || "default";
const stats = await pilot.knowledgeStats(ws);
const sources = await pilot.documentSources(ws);
return {
documents: stats?.node_types?.Document || stats?.node_types?.Source || 0,
concepts: stats?.node_types?.Concept || 0,
sources: sources.slice(0, 10)
};
Explain the selected Memory session (or list recent sessions if none selected)
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
const selId = ctx?.selection?.id;
if (selId) {
const detail = await pilot.sessionDetail(selId);
const sessions = await pilot.recentSessions({ limit: 5 });
const meta = sessions.find((s) => s.id === selId) || {};
return {
selected: { id: selId, title: meta.title, agent: meta.agent, project: meta.project },
detail,
link: pilot.bridgeLink("memory", selId)
};
}
const sessions = await pilot.recentSessions({ limit: 10 });
return sessions;
Explain the Governance V2 view (active run/session, risk, steps, navigator state)
const pilot = await import("/mod/_prime_silo/memoray_client/ext/skills/benny-pilot/benny-pilot.js");
const ctx = pilot.readContext();
const pc = ctx?.pageContext;
const gov = pc?.governance || {};
const activeId = gov.activeRunId;
if (!activeId) {
const all = await pilot.runs(10);
return {
message:
`Governance V2 Navigator: ${gov.summary?.totalVisible ?? all.length} items visible ` +
`(filter: ${gov.filter || "all"}, sort: ${gov.sort || "weight"}, ` +
`${gov.summary?.totalFailures ?? "?"} failures). No run selected — click one to focus.`,
runs: all
};
}
const isSession = gov.activeType === "session";
let detail;
if (isSession) {
detail = await pilot.sessionDetail(activeId);
} else {
const all = pilot.();
detail = all.( r. === activeId) || ;
}
{
activeId,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
: gov.,
detail,
: pilot.(, activeId)
};