Compare commits
2 Commits
fb302a033e
...
5f1c107667
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f1c107667 | |||
| 6bc7be4c21 |
@@ -267,6 +267,8 @@ Payload fallback scanning is handled inside `context`-level decision logic; ther
|
||||
|
||||
`detectEditIntent()` and `detectArchitectureSensitiveReasoning()` provide heuristic fallback for generic turns.
|
||||
|
||||
In addition to marker-based deduplication, `before_agent_start` scans the active session context via `ctx.sessionManager.buildSessionContext()` for an existing `pi-project-map-hint` custom message. If one is already present in the current branch, the handler skips injection entirely. This prevents duplicate visible hints in advisory/pre-init modes and duplicate hidden hints in strong/strict modes when the session context already contains the guidance. The hint is automatically re-injected after compaction or `/tree` navigation removes it from the active path.
|
||||
|
||||
### 7.4 Protocol path and strict bypass
|
||||
|
||||
The **protocol path** is present when outgoing context contains:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Pi skill prompt hook
|
||||
// Injected into every prompt to remind the agent to maintain .pi-map.md files
|
||||
|
||||
export const MAINTENANCE_INSTRUCTION = `
|
||||
If you modify any source file, run \`project-map:patch <file-path>\` to update the analysis.
|
||||
If you suspect staleness, run \`project-map:validate\`.
|
||||
`;
|
||||
|
||||
export function injectPrompt(originalPrompt: string): string {
|
||||
return `${originalPrompt}\n\n---\n${MAINTENANCE_INSTRUCTION}`;
|
||||
}
|
||||
+29
-4
@@ -81,6 +81,25 @@ function renderProgressBar(
|
||||
return `[${bar}] ${completed}/${total}${file}`;
|
||||
}
|
||||
|
||||
const HINT_CUSTOM_TYPE = "pi-project-map-hint";
|
||||
|
||||
function hintAlreadyInContext(ctx: any): boolean {
|
||||
const manager = ctx?.sessionManager;
|
||||
if (!manager || typeof manager.buildSessionContext !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { messages } = manager.buildSessionContext();
|
||||
if (!Array.isArray(messages)) return false;
|
||||
|
||||
return messages.some(
|
||||
(m: any) =>
|
||||
m &&
|
||||
m.role === "custom" &&
|
||||
m.customType === HINT_CUSTOM_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
let lastRootPairMtimes: import("./src/index.js").RootPairMtimes = {};
|
||||
pi.registerTool({
|
||||
@@ -351,7 +370,9 @@ export default function (pi: ExtensionAPI) {
|
||||
}
|
||||
});
|
||||
|
||||
// Inject maintenance instructions before agent starts
|
||||
// Inject maintenance instructions before agent starts, but only once
|
||||
// within the current branch of context. Re-inject after compaction or
|
||||
// tree navigation removes the hint from the active path.
|
||||
pi.on("before_agent_start", async (_event, _ctx) => {
|
||||
const config = loadConfig(_ctx.cwd);
|
||||
const mapFiles = findPiMapFiles(_ctx.cwd);
|
||||
@@ -366,9 +387,10 @@ export default function (pi: ExtensionAPI) {
|
||||
if (!modeAllowsPreInitHint(config.promptInjectionMode)) {
|
||||
return {};
|
||||
}
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: buildPreInitHint(),
|
||||
display: true,
|
||||
},
|
||||
@@ -378,9 +400,10 @@ export default function (pi: ExtensionAPI) {
|
||||
// Slice 4: advisory mode shows a visible lightweight reminder after init.
|
||||
// No root-pair preload, no per-turn reinjection.
|
||||
if (config.promptInjectionMode === "advisory") {
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: buildAdvisoryReminder(),
|
||||
display: true,
|
||||
},
|
||||
@@ -414,12 +437,14 @@ export default function (pi: ExtensionAPI) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
|
||||
// Slice 2: post-init root-pair preload + budgeted expansion
|
||||
const contextWindow = discoverContextWindow(_ctx);
|
||||
const payload = buildInjectionPayload(_ctx.cwd, config, contextWindow);
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: payload.content,
|
||||
display: payload.display,
|
||||
},
|
||||
|
||||
@@ -70,6 +70,9 @@ describe("pi-extension", () => {
|
||||
})),
|
||||
},
|
||||
ui: { notify: mockNotify },
|
||||
sessionManager: {
|
||||
buildSessionContext: vi.fn(() => ({ messages: [] })),
|
||||
},
|
||||
};
|
||||
|
||||
const mockPi = {
|
||||
@@ -505,6 +508,53 @@ describe("pi-extension", () => {
|
||||
expect(result.message.content).toContain("Updated");
|
||||
expect(result.message.content).toContain("TestIndex");
|
||||
});
|
||||
|
||||
it("skips injection when hint is already present in session context", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## role\nTest\n");
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ promptInjectionMode: "advisory" }),
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
mockCtx.sessionManager.buildSessionContext = vi.fn(() => ({
|
||||
messages: [
|
||||
{
|
||||
role: "custom",
|
||||
customType: "pi-project-map-hint",
|
||||
content: "existing hint",
|
||||
display: false,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it("injects hint again when session context does not contain it", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## role\nTest\n");
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ promptInjectionMode: "advisory" }),
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
mockCtx.sessionManager.buildSessionContext = vi.fn(() => ({
|
||||
messages: [
|
||||
{ role: "user", content: "hello" },
|
||||
{ role: "assistant", content: [{ type: "text", text: "hi" }] },
|
||||
],
|
||||
}));
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
|
||||
expect(result).toHaveProperty("message");
|
||||
expect(result.message.customType).toBe("pi-project-map-hint");
|
||||
});
|
||||
});
|
||||
|
||||
describe("context event", () => {
|
||||
|
||||
Reference in New Issue
Block a user