Skip to content

Commit c05a7b7

Browse files
committed
Structure system prompt into composable sections (#5)
* Split system prompt into composable sections * Test prompt section composition
1 parent d38b3c9 commit c05a7b7

2 files changed

Lines changed: 138 additions & 17 deletions

File tree

‎src/prompts.ts‎

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,86 @@
1-
export function buildSystemPrompt(): string {
1+
const defaultAgentTools = [
2+
"read_file",
3+
"write_file",
4+
"edit_file",
5+
"run_shell",
6+
"search_files",
7+
"grep",
8+
"list_dir",
9+
"submit_plan",
10+
"submit_output",
11+
];
12+
13+
const defaultChatTools = [
14+
"read_file",
15+
"write_file",
16+
"edit_file",
17+
"run_shell",
18+
"search_files",
19+
"grep",
20+
"list_dir",
21+
];
22+
23+
const joinSections = (sections: string[]) => sections.join("\n\n");
24+
25+
export function buildAgentRole(): string {
26+
return "You are an autonomous coding agent operating inside an event-driven loop.";
27+
}
28+
29+
export function buildToolCallDiscipline(): string {
230
return [
3-
"You are an autonomous coding agent operating inside an event-driven loop.",
4-
"",
5-
"Rules:",
31+
"Tool-call discipline:",
632
"1. Every turn must produce at least one tool_call. Conversational text without tool_calls is not progress.",
733
"2. Do not explain what you will do before doing it. Just call the tool.",
8-
"4. You MUST call submit_output when the task is fully complete. No other action signals completion.",
9-
"5. If tests are failing, you MUST NOT submit. Fix the tests first.",
10-
"6. Do not re-read a file you already read. The tool will return an error if you try.",
11-
"7. Never write large files in a single write_file call. If a file exceeds ~200 lines, write it in sections using run_shell (printf or cat heredoc) or break the work into edit_file calls on an existing scaffold.",
12-
"",
13-
"Available tools: read_file, write_file, edit_file, run_shell, search_files, grep, list_dir, submit_plan, submit_output.",
14-
"",
15-
"When calling submit_output, include a brief summary of what was done.",
1634
].join("\n");
1735
}
1836

19-
export function buildChatSystemPrompt(): string {
37+
export function buildSubmitRules(): string {
2038
return [
39+
"Submit and completion rules:",
40+
"1. You MUST call submit_output when the task is fully complete. No other action signals completion.",
41+
"2. If tests are failing, you MUST NOT submit. Fix the tests first.",
42+
"3. When calling submit_output, include a brief summary of what was done.",
43+
].join("\n");
44+
}
45+
46+
export function buildBudgetRules(): string {
47+
return [
48+
"Budget and read limits:",
49+
"1. Do not re-read a file you already read. The tool will return an error if you try.",
50+
"2. Never write large files in a single write_file call. If a file exceeds ~200 lines, write it in sections using run_shell (printf or cat heredoc) or break the work into edit_file calls on an existing scaffold.",
51+
].join("\n");
52+
}
53+
54+
export function buildPlanRules(): string {
55+
return [
56+
"Plan requirements:",
57+
"1. Submit a plan before making changes when the task requires multiple steps.",
58+
"2. Keep work aligned with the submitted plan and update it if the approach changes.",
59+
].join("\n");
60+
}
61+
62+
export function buildAvailableTools(tools = defaultAgentTools): string {
63+
return `Available tools: ${tools.join(", ")}.`;
64+
}
65+
66+
export function buildSystemPrompt(tools = defaultAgentTools): string {
67+
return joinSections([
68+
buildAgentRole(),
69+
buildToolCallDiscipline(),
70+
buildSubmitRules(),
71+
buildBudgetRules(),
72+
buildPlanRules(),
73+
buildAvailableTools(tools),
74+
]);
75+
}
76+
77+
export function buildChatSystemPrompt(): string {
78+
return joinSections([
2179
"You are a helpful coding assistant. You can answer questions, write code, and use tools when needed.",
22-
"",
23-
"Available tools: read_file, write_file, edit_file, run_shell, search_files, grep, list_dir.",
24-
"",
80+
buildToolCallDiscipline(),
81+
buildBudgetRules(),
82+
buildPlanRules(),
83+
buildAvailableTools(defaultChatTools),
2584
"When the user asks you to do something, use the appropriate tools. When you are done, reply with a summary.",
26-
].join("\n");
85+
]);
2786
}

‎tests/unit/prompts.test.ts‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { expect, test } from "bun:test";
2+
3+
import {
4+
buildAgentRole,
5+
buildAvailableTools,
6+
buildBudgetRules,
7+
buildChatSystemPrompt,
8+
buildPlanRules,
9+
buildSubmitRules,
10+
buildSystemPrompt,
11+
buildToolCallDiscipline,
12+
} from "../../src/prompts.js";
13+
14+
const sections = [
15+
buildAgentRole,
16+
buildToolCallDiscipline,
17+
buildSubmitRules,
18+
buildBudgetRules,
19+
buildPlanRules,
20+
buildAvailableTools,
21+
];
22+
23+
test("prompt sections are non-empty", () => {
24+
for (const buildSection of sections) {
25+
expect(buildSection().trim().length).toBeGreaterThan(0);
26+
}
27+
});
28+
29+
test("system prompt includes every section", () => {
30+
const prompt = buildSystemPrompt();
31+
32+
expect(prompt).toContain(buildAgentRole());
33+
expect(prompt).toContain(buildToolCallDiscipline());
34+
expect(prompt).toContain(buildSubmitRules());
35+
expect(prompt).toContain(buildBudgetRules());
36+
expect(prompt).toContain(buildPlanRules());
37+
expect(prompt).toContain(buildAvailableTools());
38+
});
39+
40+
test("chat system prompt excludes submit rules", () => {
41+
const prompt = buildChatSystemPrompt();
42+
43+
expect(prompt).not.toContain(buildSubmitRules());
44+
expect(prompt).not.toContain("submit_output");
45+
});
46+
47+
test("full system prompt contains provided tool names", () => {
48+
const prompt = buildSystemPrompt(["first_tool", "second_tool"]);
49+
50+
expect(prompt).toContain("first_tool");
51+
expect(prompt).toContain("second_tool");
52+
});
53+
54+
test("system prompt preserves core agent instructions", () => {
55+
const prompt = buildSystemPrompt();
56+
57+
expect(prompt).toContain("autonomous coding agent");
58+
expect(prompt).toContain("Every turn must produce at least one tool_call");
59+
expect(prompt).toContain("Do not re-read a file you already read");
60+
expect(prompt).toContain("MUST call submit_output");
61+
expect(prompt).toContain("If tests are failing, you MUST NOT submit");
62+
});

0 commit comments

Comments
 (0)