Update Pi extension for context injection · Entire
Update Pi extension for context injection
9ea2071→main·
dipree·4w ago·1 file·+40 added/-5
Sessions
ee09419b55ceView transcript
?\ Investigate CLI Injection at Session StartPi·GPT-5.5·3 steps
Changes
1
.pi/extensions/entire
- Mindex.ts+40/-5
13 unmodified lines
14
15
16
17
17
18
19
20
21
22
23
24
25
23
24
26
27
28
29
30
31
29
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
32 unmodified lines
91
92
93
72
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
13 unmodified lines
const ENTIRE_CMD = "entire";
let pendingSkillEvents: Array<{ skill_name: string; invocation: string; timestamp: string }> = [];
function fireHook(hookName: string, data: Record<string, unknown>): Promise<void> {
// fireHook pipes data to `entire hooks pi <hookName>` and resolves with the
// hook's stdout (empty string on any failure). Most hooks ignore the return;
// before_agent_start uses it to apply a model-context injection.
function fireHook(hookName: string, data: Record<string, unknown>): Promise<string> {
return new Promise((resolve) => {
try {
const child = execFile(
"sh",
["-c", `${ENTIRE_CMD} hooks pi ${hookName}`],
{ timeout: 10000, windowsHide: true },
() => resolve(),
{ timeout: 10000, windowsHide: true, maxBuffer: 4 * 1024 * 1024 },
(_err, stdout) => resolve(typeof stdout === "string" ? stdout : ""),
);
child.stdin?.end(JSON.stringify(data));
} catch {
// best effort — never block the agent on a hook failure
resolve();
resolve("");
}
});
}
// parseInjectedContext scans a hook's stdout for Entire's injection envelope
// ({"inject_context":"..."}) and returns the text to inject, or null.
function parseInjectedContext(stdout: string): string | null {
if (!stdout) return null;
for (const line of stdout.split("\n")) {
const trimmed = line.trim();
if (!trimmed.startsWith("{")) continue;
try {
const parsed = JSON.parse(trimmed) as { inject_context?: unknown };
if (typeof parsed.inject_context === "string" && parsed.inject_context.length > 0) {
return parsed.inject_context;
}
} catch {
// not our envelope — ignore
}
}
return null;
}
function parseSkillInvocation(text: string): { skill_name: string; invocation: string } | null {
const match = text.trimStart().match(/^\/skill:([a-z0-9][a-z0-9-]{0,63})(?:\s|$)/);
if (!match) return null;
32 unmodified lines
pi.on("before_agent_start", async (event, ctx) => {
const skillEvents = pendingSkillEvents;
pendingSkillEvents = [];
await fireHook("before_agent_start", {
const stdout = await fireHook("before_agent_start", {
type: "before_agent_start",
cwd: ctx.cwd,
session_file: ctx.sessionManager.getSessionFile(),
prompt: event.prompt,
skill_events: skillEvents,
});
const injected = parseInjectedContext(stdout);
if (injected) {
// Inject a hidden, persistent message so the model learns about Entire.
// display:false keeps it out of the user-facing transcript while still
// sending it to the LLM. Entire emits this at most once per session.
return {
message: {
customType: "entire-context",
content: injected,
display: false,
},
};
}
});
pi.on("agent_end", async (_event, ctx) => {