feat: add base prompt template

This commit is contained in:
2026-06-11 14:17:42 +01:00
parent 58fe45b1b3
commit 4132aa4552
2 changed files with 305 additions and 1 deletions
+74 -1
View File
@@ -1 +1,74 @@
// Base prompt template. // Base system prompt builder for ChatGPT advisory use.
/**
* Build a base system prompt for ChatGPT advisory use.
*
* @param {{ question?: string, context?: string, constraints?: string[], projectSummary?: string, taskSummary?: string }} [input]
* @returns {string} The complete base prompt as a single string.
*/
export function buildBasePrompt(input) {
const lines = [
"You are ChatGPT acting as a second-opinion assistant for Claude Code.",
"Claude Code is the primary coding agent and is currently backed by a local Ollama coding model.",
"You are not responsible for editing files, running commands, deploying, committing code, or making autonomous decisions.",
"Your role:",
"- Review plans",
"- Review code snippets",
"- Analyse architecture",
"- Debug issues",
"- Identify risks",
"- Suggest safer implementation approaches",
"- Break work into smaller steps",
"Rules:",
"- Be concise and actionable.",
"- Use only the context provided.",
"- Do not assume access to the full repository.",
"- Do not request secrets.",
"- Do not recommend sending entire repositories.",
"- Prefer small reviewable changes.",
"- Prefer simple architecture.",
"- Prefer local/self-hosted options where practical.",
"- Call out uncertainty clearly.",
"- Avoid over-engineering.",
'- Do not pretend to have executed code or tests.',
];
const question = input?.question;
if (question) {
lines.push("");
lines.push("---");
lines.push("Your task:");
lines.push("");
lines.push(`Question: ${question}`);
if (input?.context) {
lines.push("");
lines.push(`Context: ${input.context}`);
}
if (input?.constraints && input.constraints.length > 0) {
lines.push("");
lines.push("Constraints:");
for (const c of input.constraints) {
lines.push(`- ${c}`);
}
}
if (input?.projectSummary) {
lines.push("");
lines.push(`Project summary: ${input.projectSummary}`);
}
if (input?.taskSummary) {
lines.push("");
lines.push(`Task summary: ${input.taskSummary}`);
}
}
return lines.join("\n");
}
+231
View File
@@ -0,0 +1,231 @@
import { describe, it, expect } from "vitest";
import { buildBasePrompt } from "../../src/prompts/base.js";
describe("buildBasePrompt", () => {
// --- Basic output contract ---
it("returns a non-empty string when called with no arguments", () => {
const result = buildBasePrompt();
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
it("returns a non-empty string when called with an empty object", () => {
const result = buildBasePrompt({});
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
it("is idempotent — same output on repeated calls with no input", () => {
const a = buildBasePrompt();
const b = buildBasePrompt();
expect(a).toBe(b);
});
// --- Role concepts ---
it("mentions ChatGPT as a second-opinion assistant for Claude Code", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("second-opinion");
expect(prompt).toContain("Claude Code");
});
it("states ChatGPT should not modify files", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("files");
});
it("contains the concept of not running commands", () => {
const prompt = buildBasePrompt();
expect(prompt).toMatch(/commands?\s*(?:,|\.)/);
});
it("contains the concept of not deploying", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("deploying");
});
it("contains the concept of not making autonomous decisions", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("decisions");
});
// --- Reviewer / planner / debugger / architecture advisor / risk checker concepts ---
it("includes reviewer role (review plans or code)", () => {
const prompt = buildBasePrompt();
expect(prompt.toLowerCase()).toContain("review");
expect(prompt.toLowerCase()).toContain("code");
});
it("includes debugger role (debug issues)", () => {
const prompt = buildBasePrompt();
expect(prompt).toMatch(/debug\s+issues?/i);
});
it("includes architecture advisor role (analyse architecture)", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("architecture");
});
it("includes risk checker role (identify risks)", () => {
const prompt = buildBasePrompt();
expect(prompt).toMatch(/risks?/);
});
// --- Project preferences ---
it("reflects preference for simple architecture", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("simple");
expect(prompt).toContain("architecture");
});
it("reflects preference for small changes", () => {
const prompt = buildBasePrompt();
expect(prompt).toMatch(/small/i);
});
it("reflects local-first approach (prefer local options)", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("local");
});
it("reflects security-conscious stance (do not request secrets)", () => {
const prompt = buildBasePrompt();
expect(prompt).toContain("secrets");
});
// --- "Your task" section gating ---
it("does NOT emit 'Your task' section when question is absent", () => {
const result = buildBasePrompt({ context: "some context" });
expect(result).not.toContain("Your task");
});
it("does NOT emit 'Your task' section when question is an empty string", () => {
const result = buildBasePrompt({ question: "" });
expect(result).not.toContain("Your task");
});
it("emits 'Your task' section when question is present and non-empty", () => {
const result = buildBasePrompt({ question: "How should I handle auth?" });
expect(result).toContain("Your task");
});
it("emits 'Your task' only when question is provided, ignoring other fields alone", () => {
const result = buildBasePrompt({
context: "some context",
constraints: ["must be safe"],
projectSummary: "a project",
taskSummary: "a task",
});
expect(result).not.toContain("Your task");
});
// --- Input fields appended correctly (requires question) ---
it("appends Question when question is provided", () => {
const result = buildBasePrompt({ question: "What should I do?" });
expect(result).toContain("Question: What should I do?");
});
it("appends Context when context is provided alongside question", () => {
const result = buildBasePrompt({
question: "Is this OK?",
context: "This is the relevant code.",
});
expect(result).toContain("Context: This is the relevant code.");
});
it("does NOT append Context section when context is empty alongside question", () => {
const result = buildBasePrompt({ question: "Is this OK?", context: "" });
expect(result).not.toContain("Context:");
});
it("appends Constraints list when constraints are provided alongside question", () => {
const result = buildBasePrompt({
question: "Review my plan",
constraints: ["must be safe", "must be fast"],
});
expect(result).toContain("Constraints:");
expect(result).toContain("- must be safe");
expect(result).toContain("- must be fast");
});
it("does NOT append Constraints section when constraints array is empty", () => {
const result = buildBasePrompt({ question: "Review my plan", constraints: [] });
expect(result).not.toContain("Constraints:");
});
it("appends Project summary when projectSummary is provided alongside question", () => {
const result = buildBasePrompt({
question: "What should I do?",
projectSummary: "A small Node.js API.",
});
expect(result).toContain("Project summary: A small Node.js API.");
});
it("does NOT append Project summary when projectSummary is empty", () => {
const result = buildBasePrompt({ question: "What should I do?", projectSummary: "" });
expect(result).not.toContain("Project summary:");
});
it("appends Task summary when taskSummary is provided alongside question", () => {
const result = buildBasePrompt({
question: "What should I do?",
taskSummary: "Adding rate limiting.",
});
expect(result).toContain("Task summary: Adding rate limiting.");
});
it("does NOT append Task summary when taskSummary is empty", () => {
const result = buildBasePrompt({ question: "What should I do?", taskSummary: "" });
expect(result).not.toContain("Task summary:");
});
// --- No tool-specific content ---
it("does NOT contain tool-specific prompt identifiers", () => {
const prompt = buildBasePrompt();
expect(prompt).not.toContain("review-plan");
expect(prompt).not.toContain("review_code");
expect(prompt).not.toContain("debug-issue");
expect(prompt).not.toContain("architecture-review");
});
// --- Full input test ---
it("includes all sections when all fields are provided", () => {
const result = buildBasePrompt({
question: "Is this approach correct?",
context: "Current handler catches all errors.",
constraints: ["no stack traces", "no raw data"],
projectSummary: "Private VPC REST API.",
taskSummary: "Adding rate limiting.",
});
expect(result).toContain("Your task");
expect(result).toContain("Question: Is this approach correct?");
expect(result).toContain("Context: Current handler catches all errors.");
expect(result).toContain("Constraints:");
expect(result).toContain("- no stack traces");
expect(result).toContain("- no raw data");
expect(result).toContain("Project summary: Private VPC REST API.");
expect(result).toContain("Task summary: Adding rate limiting.");
});
it("returns the base prompt (without task section) when question is undefined but other fields exist", () => {
const result = buildBasePrompt({
context: "some context",
constraints: ["rule 1"],
projectSummary: "a summary",
taskSummary: "a task",
});
expect(result).not.toContain("Your task");
expect(result).not.toContain("Question:");
expect(result).not.toContain("Context: some context");
expect(result).not.toContain("Constraints:");
});
});