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.
|
`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
|
### 7.4 Protocol path and strict bypass
|
||||||
|
|
||||||
The **protocol path** is present when outgoing context contains:
|
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}`;
|
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) {
|
export default function (pi: ExtensionAPI) {
|
||||||
let lastRootPairMtimes: import("./src/index.js").RootPairMtimes = {};
|
let lastRootPairMtimes: import("./src/index.js").RootPairMtimes = {};
|
||||||
pi.registerTool({
|
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) => {
|
pi.on("before_agent_start", async (_event, _ctx) => {
|
||||||
const config = loadConfig(_ctx.cwd);
|
const config = loadConfig(_ctx.cwd);
|
||||||
const mapFiles = findPiMapFiles(_ctx.cwd);
|
const mapFiles = findPiMapFiles(_ctx.cwd);
|
||||||
@@ -366,9 +387,10 @@ export default function (pi: ExtensionAPI) {
|
|||||||
if (!modeAllowsPreInitHint(config.promptInjectionMode)) {
|
if (!modeAllowsPreInitHint(config.promptInjectionMode)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
if (hintAlreadyInContext(_ctx)) return {};
|
||||||
return {
|
return {
|
||||||
message: {
|
message: {
|
||||||
customType: "pi-project-map-hint",
|
customType: HINT_CUSTOM_TYPE,
|
||||||
content: buildPreInitHint(),
|
content: buildPreInitHint(),
|
||||||
display: true,
|
display: true,
|
||||||
},
|
},
|
||||||
@@ -378,9 +400,10 @@ export default function (pi: ExtensionAPI) {
|
|||||||
// Slice 4: advisory mode shows a visible lightweight reminder after init.
|
// Slice 4: advisory mode shows a visible lightweight reminder after init.
|
||||||
// No root-pair preload, no per-turn reinjection.
|
// No root-pair preload, no per-turn reinjection.
|
||||||
if (config.promptInjectionMode === "advisory") {
|
if (config.promptInjectionMode === "advisory") {
|
||||||
|
if (hintAlreadyInContext(_ctx)) return {};
|
||||||
return {
|
return {
|
||||||
message: {
|
message: {
|
||||||
customType: "pi-project-map-hint",
|
customType: HINT_CUSTOM_TYPE,
|
||||||
content: buildAdvisoryReminder(),
|
content: buildAdvisoryReminder(),
|
||||||
display: true,
|
display: true,
|
||||||
},
|
},
|
||||||
@@ -414,12 +437,14 @@ export default function (pi: ExtensionAPI) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hintAlreadyInContext(_ctx)) return {};
|
||||||
|
|
||||||
// Slice 2: post-init root-pair preload + budgeted expansion
|
// Slice 2: post-init root-pair preload + budgeted expansion
|
||||||
const contextWindow = discoverContextWindow(_ctx);
|
const contextWindow = discoverContextWindow(_ctx);
|
||||||
const payload = buildInjectionPayload(_ctx.cwd, config, contextWindow);
|
const payload = buildInjectionPayload(_ctx.cwd, config, contextWindow);
|
||||||
return {
|
return {
|
||||||
message: {
|
message: {
|
||||||
customType: "pi-project-map-hint",
|
customType: HINT_CUSTOM_TYPE,
|
||||||
content: payload.content,
|
content: payload.content,
|
||||||
display: payload.display,
|
display: payload.display,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ describe("pi-extension", () => {
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
ui: { notify: mockNotify },
|
ui: { notify: mockNotify },
|
||||||
|
sessionManager: {
|
||||||
|
buildSessionContext: vi.fn(() => ({ messages: [] })),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockPi = {
|
const mockPi = {
|
||||||
@@ -505,6 +508,53 @@ describe("pi-extension", () => {
|
|||||||
expect(result.message.content).toContain("Updated");
|
expect(result.message.content).toContain("Updated");
|
||||||
expect(result.message.content).toContain("TestIndex");
|
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", () => {
|
describe("context event", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user