mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
bb6a65ac76
* fix(plugin): consolidate system prompt injections into single array element The plugin previously pushes planning prompts and improvement contexts as separate elements in the output.system array. This change appends them to output.system[0] with newline separators instead. This keeps all system instructions within a single message block to prevent potential parsing or formatting issues when the agent processes the context. * refactor(opencode-plugin): extract composeSystemPrompt helper to centralize system prompt assembly and add unit tests * style(opencode-plugin): remove extra newline before plan submission reminder heading * fix(opencode-plugin): store composed prompt result before clearing system array to prevent data loss Previously, `output.system` was cleared with `length = 0` before being passed into `composeSystemPrompt`, causing the function to compose from an empty array instead of the original system content. The fix stores the composition result in a variable first, then pushes it after clearing. Additionally, add `.trim()` in `stripConflictingPlanModeRules` to normalize whitespace before filtering empty entries, and include a test case for empty string collapse behavior. * refactor(plan-mode.ts): move string trimming from stripConflictingPlanModeRules to composeSystemPrompt for centralized whitespace handling * test(plan-mode): add test case for trimming trailing newlines in composeSystemPrompt
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
// ── Permission helpers ────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Normalize an `edit` permission value before merging additional rules.
|
|
*
|
|
* OpenCode's zod transform converts legacy `tools: { edit: false }` to
|
|
* `permission.edit = "deny"` (a plain string) before any plugin sees the
|
|
* config. Spreading a string in JS produces char-index keys:
|
|
* `{ ..."deny" }` → `{ "0": "d", "1": "e", "2": "n", "3": "y" }`
|
|
* which corrupt the permission ruleset and cause zod validation failures.
|
|
*
|
|
* This function converts a string action to `{ "*": action }` (equivalent
|
|
* wildcard object) so the caller can safely spread it and add overrides.
|
|
*/
|
|
export function normalizeEditPermission(
|
|
edit: string | Record<string, string> | undefined,
|
|
): Record<string, string> {
|
|
if (typeof edit === "string") {
|
|
return { "*": edit };
|
|
}
|
|
return edit ?? {};
|
|
}
|
|
|
|
// ── Prompt stripping ──────────────────────────────────────────────────────
|
|
|
|
function shouldStripPlanModeLine(line: string): boolean {
|
|
const normalized = line.trim().toLowerCase();
|
|
return normalized.includes("strictly forbidden: any file edits")
|
|
|| normalized.includes("your plan at ")
|
|
|| normalized.includes("plan file already exists at ")
|
|
|| normalized.includes(".opencode/plans/")
|
|
|| normalized.includes("plan_exit")
|
|
|| (normalized.includes("agent's conversation") && normalized.includes("not on disk"));
|
|
}
|
|
|
|
function cleanupSystemEntry(entry: string): string {
|
|
return entry
|
|
.split("\n")
|
|
.map((line) => line.replace(/[ \t]+$/g, ""))
|
|
.join("\n")
|
|
.replace(/\n{3,}/g, "\n\n")
|
|
.trim();
|
|
}
|
|
|
|
export function stripConflictingPlanModeRules(systemEntries: string[]): string[] {
|
|
return systemEntries
|
|
.map((entry) =>
|
|
cleanupSystemEntry(
|
|
entry
|
|
.split("\n")
|
|
.filter((line) => !shouldStripPlanModeLine(line))
|
|
.join("\n"),
|
|
),
|
|
)
|
|
.filter(Boolean);
|
|
}
|
|
|
|
export function composeSystemPrompt(system: string[], additions: string[]): string[] {
|
|
return [system.concat(additions).map((s) => s.trim()).filter(Boolean).join("\n\n")];
|
|
}
|