ac6d8eb3be
* feat: resolve @prompt_name saved-prompt references in workflow step prompts
Workflow step prompts, the GitHub review-watch task prompt, and the CI
auto-fix prompt now resolve "@name" references to saved custom prompts
(Settings > Prompts) at invocation time, appending the referenced
content as hidden context while keeping the mention visible - matching
how chat messages and message_task already resolve @mentions. Editing
a saved prompt now propagates to every workflow step that references
it without needing to update each step individually.
Adds a Monaco "@" completion provider to the step-prompt and
review-watch editors, alongside the existing "{{placeholder}}"
provider.
* fix: address PR review findings on prompt-reference expansion
- Make AppendReferenceExpansions idempotent: a prompt that already
contains a previously-appended expansion block is returned unchanged,
since the block's own "### @name" headers would otherwise be
re-scanned as fresh mentions on a second call.
- Skip "@name" expansion entirely for CLI-passthrough sessions: their
prompts are written raw to a PTY with no <kandev-system> stripping,
so the hidden block would otherwise leak into the terminal verbatim.
* fix: fail safe on passthrough lookup error in startTask
A session-lookup error while resolving isPassthrough for the Kandev
MCP wrap and prompt-reference expansion previously defaulted to
false (non-passthrough), risking a hidden <kandev-system> block
leaking into a real passthrough session's PTY on a transient error.
Extracted resolveIsPassthroughForLaunch, which now fails safe by
defaulting to true (skip injection) and logs a warning.
* fix: scope expansion idempotency marker to system blocks
The idempotency guard added in a prior commit checked for the
expansion marker anywhere in the prompt, which could falsely
suppress a legitimate first-time expansion if caller-controlled
text happened to contain that literal phrase. Add
sysprompt.HasSystemMarker, mirroring the existing HasKandevContext
pattern, so the marker is only recognized inside a formatter-
produced <kandev-system> block. Also strengthens a passthrough test
assertion that didn't actually distinguish the snapshot path from
the fail-safe default.
* fix: match exact expansion-block prefix in idempotency guard
The prior scoping fix accepted the marker anywhere inside any
<kandev-system> block, which could still false-positive against an
unrelated system block (e.g. a plan-mode prefix or MCP context
wrap) that happens to mention the marker phrase without it being
the block's own content. Check for the exact literal prefix this
package's formatter produces instead: the opening tag immediately
followed by the marker, with nothing in between.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { createPromptMentionCompletionProvider } from "./script-editor-completions";
|
|
import type { PromptReference } from "@/lib/prompts/expand-prompt-references";
|
|
import type { editor, languages, Position } from "monaco-editor";
|
|
|
|
const monacoStub = {
|
|
languages: {
|
|
CompletionItemKind: { Reference: 17 },
|
|
},
|
|
} as unknown as typeof import("monaco-editor");
|
|
|
|
function modelForLine(line: string): editor.ITextModel {
|
|
return {
|
|
getLineContent: () => line,
|
|
} as unknown as editor.ITextModel;
|
|
}
|
|
|
|
function complete(
|
|
prompts: PromptReference[],
|
|
line: string,
|
|
column: number,
|
|
): languages.CompletionList {
|
|
const provider = createPromptMentionCompletionProvider(monacoStub, prompts);
|
|
const result = provider.provideCompletionItems(
|
|
modelForLine(line),
|
|
{ lineNumber: 1, column } as unknown as Position,
|
|
{} as languages.CompletionContext,
|
|
{} as import("monaco-editor").CancellationToken,
|
|
);
|
|
return result as languages.CompletionList;
|
|
}
|
|
|
|
const prompts: PromptReference[] = [
|
|
{ id: "1", name: "review", content: "Review this code carefully for bugs." },
|
|
{ id: "2", name: "release-notes", content: "Write release notes." },
|
|
];
|
|
|
|
describe("createPromptMentionCompletionProvider", () => {
|
|
it("has @ as its only trigger character", () => {
|
|
const provider = createPromptMentionCompletionProvider(monacoStub, prompts);
|
|
expect(provider.triggerCharacters).toEqual(["@"]);
|
|
});
|
|
|
|
it("suggests prompts when @ is at the start of a line", () => {
|
|
const { suggestions } = complete(prompts, "@", 2);
|
|
expect(suggestions.map((s) => (s.label as { label: string }).label ?? s.label)).toEqual([
|
|
"@review",
|
|
"@release-notes",
|
|
]);
|
|
});
|
|
|
|
it("suggests prompts when @ follows whitespace", () => {
|
|
const { suggestions } = complete(prompts, "please @re", 11);
|
|
expect(suggestions).toHaveLength(2);
|
|
expect(suggestions[0].insertText).toBe("review");
|
|
});
|
|
|
|
it("does not suggest when @ is mid-word", () => {
|
|
const { suggestions } = complete(prompts, "foo@bar", 8);
|
|
expect(suggestions).toHaveLength(0);
|
|
});
|
|
|
|
it("does not suggest when there is no @ before the cursor", () => {
|
|
const { suggestions } = complete(prompts, "hello", 6);
|
|
expect(suggestions).toHaveLength(0);
|
|
});
|
|
|
|
it("returns no suggestions when prompts list is empty", () => {
|
|
const { suggestions } = complete([], "@", 2);
|
|
expect(suggestions).toHaveLength(0);
|
|
});
|
|
|
|
it("includes a content preview in detail/documentation", () => {
|
|
const { suggestions } = complete(prompts, "@", 2);
|
|
expect(suggestions[0].detail).toBe("Review this code carefully for bugs.");
|
|
});
|
|
});
|