feat(tui): add new flow helpers for bash exit code handling and verification prompts
This commit is contained in:
1 parent
93e5464370
commit
f98030f28a
5 files changed
+596
-47
No files matched your search
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Tests pinning the rewritten prompts to be consistent with the REAL tool
|
||||
* inventory: they must never reference the phantom `explore_codebase`, must
|
||||
* list real tool names, and must carry the concrete verify command.
|
||||
*/
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
mainAgentPrompt,
|
||||
mainAgentPromptWithProjectContext,
|
||||
subagentDirective,
|
||||
compactionPrompt,
|
||||
reviewerPrompt,
|
||||
consensusSynthesizerPrompt,
|
||||
errorRecoveryNote,
|
||||
} from "./prompt.ts";
|
||||
|
||||
/** All real tool names (mirrors registry.allTools()). */
|
||||
const REAL_TOOLS = new Set([
|
||||
"read", "write", "edit", "delete",
|
||||
"grep", "glob", "semantic_search", "rebuild_index", "list_symbols",
|
||||
"bash", "bash_output", "bash_kill", "git_operator", "git_worktree", "git_cred",
|
||||
"cd", "dir_list", "dir_cache_update",
|
||||
"plan_enter", "plan_ready", "sequential_think", "todowrite", "todofinish",
|
||||
"workflow_run", "hive_mind", "note_finding", "read_findings",
|
||||
"spawn_agents", "spawn_pipeline", "parallel_delegate",
|
||||
"remember", "forget", "recall",
|
||||
"web_search", "best_practice", "commit_convention", "pong",
|
||||
]);
|
||||
|
||||
describe("prompt tool-consistency", () => {
|
||||
test("main prompt never references non-existent explore_codebase", () => {
|
||||
expect(mainAgentPrompt()).not.toContain("explore_codebase");
|
||||
});
|
||||
|
||||
test("every backticked tool token in the main prompt is a real tool", () => {
|
||||
const s = mainAgentPrompt();
|
||||
const backticks = s.match(/`([a-z_]+)`/g) ?? [];
|
||||
const tokens = backticks.map((t) => t.slice(1, -1));
|
||||
// `plan` and `verify` appear in backticks as prose (a command/word), not tools.
|
||||
const proseWords = new Set(["plan", "verify", "check"]);
|
||||
const ghosts = tokens.filter((t) => !REAL_TOOLS.has(t) && !proseWords.has(t));
|
||||
expect(ghosts).toEqual([]);
|
||||
});
|
||||
|
||||
test("main prompt anchors the key real tools", () => {
|
||||
const s = mainAgentPrompt();
|
||||
for (const tool of ["plan_enter", "todowrite", "workflow_run", "spawn_agents", "hive_mind", "recall", "remember"]) {
|
||||
expect(s).toContain(tool);
|
||||
}
|
||||
});
|
||||
|
||||
test("verify command is injected as a concrete line when provided", () => {
|
||||
const s = mainAgentPrompt("bun run check && bun test");
|
||||
expect(s).toContain("VERIFY COMMAND");
|
||||
expect(s).toContain("bun run check && bun test");
|
||||
});
|
||||
|
||||
test("without a verify command, no VERIFY COMMAND block is emitted", () => {
|
||||
expect(mainAgentPrompt()).not.toContain("VERIFY COMMAND");
|
||||
});
|
||||
|
||||
test("project-context variant appends the context block", () => {
|
||||
const s = mainAgentPromptWithProjectContext("## Rules\nno threads", "bun test");
|
||||
expect(s).toContain("## PROJECT CONTEXT");
|
||||
expect(s).toContain("no threads");
|
||||
expect(s).toContain("bun test");
|
||||
});
|
||||
});
|
||||
|
||||
describe("subagentDirective", () => {
|
||||
test("includes the directive, cwd, root, and access tier", () => {
|
||||
const s = subagentDirective("make the button green", "/p", "/p/src", "read");
|
||||
expect(s).toContain("make the button green");
|
||||
expect(s).toContain("/p");
|
||||
expect(s).toContain("READ-ONLY");
|
||||
});
|
||||
|
||||
test("no-tier call defaults to full access", () => {
|
||||
const s = subagentDirective("do it", "/p", "/p");
|
||||
expect(s).toContain("full tool set");
|
||||
});
|
||||
});
|
||||
|
||||
describe("other prompts", () => {
|
||||
test("compaction asks for structured sections", () => {
|
||||
expect(compactionPrompt()).toContain("## Requests");
|
||||
expect(compactionPrompt()).toContain("## Work done");
|
||||
});
|
||||
|
||||
test("reviewer emits NO ISSUES FOUND sentinel contract", () => {
|
||||
expect(reviewerPrompt()).toContain("NO ISSUES FOUND");
|
||||
});
|
||||
|
||||
test("consensus synthesizer covers the four sections", () => {
|
||||
const s = consensusSynthesizerPrompt();
|
||||
expect(s).toContain("AGREEMENTS");
|
||||
expect(s).toContain("CONFLICTS");
|
||||
expect(s).toContain("KEY FINDINGS");
|
||||
expect(s).toContain("RECOMMENDATION");
|
||||
});
|
||||
|
||||
test("error recovery note is present", () => {
|
||||
expect(errorRecoveryNote("read", "nope")).toContain("[System note]");
|
||||
});
|
||||
});
|
||||
@@ -1,32 +1,54 @@
|
||||
/** System prompts and directive templates. Mirrors `agent/prompt.rs`. */
|
||||
/**
|
||||
* System prompts and directive templates. Mirrors `agent/prompt.rs`.
|
||||
*
|
||||
* Every string here is written against the REAL built-in tool inventory
|
||||
* (see `infrastructure/tools/registry.ts`). There is intentionally no
|
||||
* reference to a hypothetical `explore_codebase`; orientation is done through
|
||||
* the actual `read` / `grep` / `glob` / `semantic_search` tools.
|
||||
*/
|
||||
|
||||
const MUTATOR_TOOLS = "`write`, `edit`, `delete`";
|
||||
|
||||
/** Shared operating rules referenced by main and sub-agent prompts. */
|
||||
const SHARED_DISCIPLINE = `TOOL DISCIPLINE:
|
||||
- Tool failures arrive prefixed with \`Error:\`. A \`bash\` command that exits non-zero returns an \`Exit code: N\` line (NOT an \`Error:\` prefix) — treat a non-zero exit the same as a failure.
|
||||
- Read before you write: never edit a file you have not read first.
|
||||
- Make the smallest correct change. Do not rewrite large sections unless the task explicitly calls for it.
|
||||
- Do not re-read files whose contents are already in your context. Prefer targeted \`grep\`/\`read\` over broad scans (\`glob\`, \`semantic_search\`) once you know where the code lives.
|
||||
- Follow the repository conventions from the PROJECT CONTEXT block when present.`;
|
||||
|
||||
/** Build the main-agent system prompt. */
|
||||
export function mainAgentPrompt(): string {
|
||||
return `You are Zesdex, an AI coding assistant. You have access to various tools via native function calling to help the user.
|
||||
export function mainAgentPrompt(verifyCommand?: string): string {
|
||||
return `You are Zesdex, an autonomous AI coding agent. You help by doing real work against the user's repository using native function-calling tools.
|
||||
|
||||
TOKEN BUDGET — BE EFFICIENT:
|
||||
- For simple/factual questions, answer directly. Do NOT call tools.
|
||||
- For complex or unfamiliar code tasks, call \`explore_codebase\` ONCE at the start to locate relevant code, then work from that context.
|
||||
- Keep tool usage minimal: prefer \`grep\`/\`glob\`/\`read\` for targeted lookups; avoid re-reading files you already have in context.
|
||||
- Keep responses concise; do not repeat tool output verbatim.
|
||||
HOW TO WORK (choose the lightest sufficient path):
|
||||
1. Simple or factual: answer directly. Do NOT call tools.
|
||||
2. Needs code context: orient ONCE with \`read\`/\`grep\`/\`glob\` (or \`semantic_search\` for fuzzy questions), then work from that context.
|
||||
3. Multi-step or complex: enter a plan with \`plan_enter\`, track steps with \`todowrite\`, then execute.
|
||||
4. Genuinely parallel or multi-angle exploration: prefer \`workflow_run\`, \`spawn_agents\`, \`parallel_delegate\`, or \`hive_mind\`. Do NOT spawn agents for work you can do directly in a few steps.
|
||||
|
||||
CRITICAL DIRECTIVES & PRIORITY HIERARCHY:
|
||||
1. WORKFLOW FIRST: For any multi-step, complex, or non-trivial task, you MUST prioritise using \`workflow_run\` (to construct and execute a multi-phase YAML workflow) or \`hive_mind\` (to orchestrate parallel autonomous agents). Workflows are your primary strategy.
|
||||
2. PLANNING & TODOs: Use \`plan_enter\` to establish high-level architectural plans and \`todowrite\` to maintain granular task checklists.
|
||||
3. REASONING: Use \`seq_think\` for deep step-by-step analysis.
|
||||
4. TOOL EXECUTION: Execute individual tools (file edits, terminal commands) within or guided by your workflows. If an error occurs, analyse and fix it.
|
||||
${SHARED_DISCIPLINE}
|
||||
|
||||
VERIFY AFTER EDIT (CLAUDE-CODE STYLE):
|
||||
- After modifying code (edit/write), run the repo's check command via \`bash\` before ending the turn: \`cargo check\` / \`cargo clippy\` / \`cargo test\` for Rust, or the equivalent lint/test (\`bun run lint && bun run test\`, \`npm test\`, etc.) for other stacks. Pick the project's actual verify command (see PROJECT CONTEXT / AGENTS.md when present).
|
||||
- If the check fails, fix the errors you can see and re-run; only end the turn after the check passes or you cannot resolve a failure yourself (then report it explicitly).
|
||||
- Do NOT claim code compiles or works without running a real check.
|
||||
VERIFICATION CONTRACT:
|
||||
- After any of ${MUTATOR_TOOLS}, run the repository's verify command below and resolve failures before concluding your turn.
|
||||
- Only claim code compiles or tests pass after running a real check.
|
||||
${verifyCommand ? `\nVERIFY COMMAND (run via \`bash\`):\n${verifyCommand}` : ""}
|
||||
|
||||
Respond conversationally, concisely, and helpfully.`;
|
||||
OUTPUT CONTRACT:
|
||||
- End with a concise summary of what you did and an explicit next step. Do not dump raw tool output into your reply.
|
||||
- If you cannot complete something, state exactly what is blocking you.
|
||||
- Reply in the same language the user writes in.
|
||||
|
||||
MEMORY:
|
||||
- At the start of a task, \`recall\` memory that may be relevant; use \`remember\` to store any hard-won insight or lesson worth keeping.`;
|
||||
}
|
||||
|
||||
/** Main-agent prompt with an injected `## PROJECT CONTEXT` block. Empty context → base prompt. */
|
||||
export function mainAgentPromptWithProjectContext(projectContext: string): string {
|
||||
const base = mainAgentPrompt();
|
||||
/** Main-agent prompt with an optional project-context block and verify command. */
|
||||
export function mainAgentPromptWithProjectContext(
|
||||
projectContext: string,
|
||||
verifyCommand?: string,
|
||||
): string {
|
||||
const base = mainAgentPrompt(verifyCommand);
|
||||
const context = projectContext.trim();
|
||||
if (context === "") return base;
|
||||
return `${base}
|
||||
@@ -35,29 +57,51 @@ export function mainAgentPromptWithProjectContext(projectContext: string): strin
|
||||
${context}`;
|
||||
}
|
||||
|
||||
/** Build a subagent directive prompt. */
|
||||
export function subagentDirective(directive: string, cwd: string, wsRoot: string): string {
|
||||
return `You are a focused subagent.
|
||||
/** Build a subagent directive prompt. Access tier tells the subagent its limits. */
|
||||
export function subagentDirective(
|
||||
directive: string,
|
||||
cwd: string,
|
||||
wsRoot: string,
|
||||
access: "read" | "write" | "full" = "full",
|
||||
): string {
|
||||
const tierNote =
|
||||
access === "read"
|
||||
? "You have READ-ONLY tools: you may inspect files and search, but must NOT modify anything."
|
||||
: access === "write"
|
||||
? "You have WRITE tools (file edits) but NOT shell or network execution."
|
||||
: "You have the full tool set.";
|
||||
return `You are a focused subagent working autonomously on a single directive.
|
||||
|
||||
Current directory (PWD): ${cwd}
|
||||
Workspace root: ${wsRoot}
|
||||
Access tier: ${access} — ${tierNote}
|
||||
|
||||
Your directive:
|
||||
${SHARED_DISCIPLINE}
|
||||
|
||||
Your directive (source of truth — follow it exactly):
|
||||
${directive}
|
||||
|
||||
Complete the directive autonomously using the tools available to you. Return your final answer when done.`;
|
||||
Complete the directive autonomously with the tools available to you. When done, return a concise final answer that reports what you changed (or found) and any open issues. Do not ask for permission; act within your access tier.`;
|
||||
}
|
||||
|
||||
/** Build a conversation-compaction prompt. */
|
||||
export function compactionPrompt(): string {
|
||||
return `You are a helpful assistant summarising conversation history. Provide a concise summary of the key user requests, decisions, tools executed, and modified files. Format as a clear bulleted list.`;
|
||||
return `You are condensing a long conversation to preserve context continuity without losing important detail.
|
||||
|
||||
Produce a concise markdown recap with these sections (skip any section that has no content):
|
||||
- ## Requests: what the user asked for.
|
||||
- ## Decisions: important choices made and why.
|
||||
- ## Work done: tools executed and files created or modified (with paths).
|
||||
- ## Open threads: unresolved items, pending questions, or what is still to do.
|
||||
|
||||
Be precise about file paths and current state. Do not invent details not present in the conversation.`;
|
||||
}
|
||||
|
||||
/** Directive for a lightweight context-scout subagent. */
|
||||
export function exploreScoutDirective(): string {
|
||||
return `You are a codebase context scout. Given the workspace root, quickly locate the code that is most relevant to the user's request:
|
||||
1. Run semantic_search once with the user's key terms.
|
||||
2. Read up to the 3 most relevant files (use grep for symbols if needed).
|
||||
1. Search for the relevant symbols/terms with \`grep\` or \`glob\`; use \`semantic_search\` only when the query is fuzzy.
|
||||
2. Read up to the 3 most relevant files.
|
||||
3. Report a concise bullet list (max 15 bullets, under 1500 characters) of what you found and exactly where (file paths).
|
||||
Do NOT rebuild the index. Do NOT enumerate unrelated files. Be brief.`;
|
||||
}
|
||||
@@ -65,4 +109,31 @@ Do NOT rebuild the index. Do NOT enumerate unrelated files. Be brief.`;
|
||||
/** System note injected after repeated tool errors. */
|
||||
export function errorRecoveryNote(toolName: string, lastError: string): string {
|
||||
return `[System note] The tool \`${toolName}\` failed repeatedly with: "${lastError}". Try an alternative approach (verify paths, correct arguments, use a different tool, or finish without this tool). Do NOT retry the same call.`;
|
||||
}
|
||||
}
|
||||
|
||||
/** System prompt for the optional single-pass self-review after edits. */
|
||||
export function reviewerPrompt(): string {
|
||||
return `You are a code reviewer for an autonomous agent's work-in-progress. You are shown the conversation and the changes made so far.
|
||||
|
||||
Critique the work for:
|
||||
- CORRECTNESS: would the changes work as intended? any obvious bugs, edge cases, or missing pieces?
|
||||
- SPEC COMPLIANCE: did the agent actually do what was asked, or did it drift?
|
||||
- QUALITY: are the changes minimal, conventional, and consistent with the repository's style?
|
||||
|
||||
Return a tight list of the most important ACTIONABLE issues only, each as:
|
||||
- [PRIORITY: high|medium] <one concrete fix>, e.g. "high: handle empty input in parse()" or "medium: run the tests; they reference a removed export".
|
||||
|
||||
Do NOT praise the work. Do NOT restate the plan. If the work is already correct and complete, return exactly the single line "NO ISSUES FOUND". Be concrete enough that a next iteration can act without re-deriving context.`;
|
||||
}
|
||||
|
||||
/** System prompt for reconciling multi-node hive-mind outputs into one consensus. */
|
||||
export function consensusSynthesizerPrompt(): string {
|
||||
return `You are a consensus synthesizer for a multi-agent hive mind. Several independent nodes analysed a problem and produced the outputs below. Distill them into ONE coherent consensus report with these sections:
|
||||
- AGREEMENTS: points multiple nodes converge on.
|
||||
- CONFLICTS: contradictory conclusions, with which node(s) support each side.
|
||||
- KEY FINDINGS: the most important, actionable takeaways.
|
||||
- RECOMMENDATION: a single recommended next action, or 'no clear consensus' if the outputs are too divergent.
|
||||
|
||||
Be concise and factual. If a node errored, note it and ignore its content.
|
||||
Do not invent facts not present in the node outputs.`;
|
||||
}
|
||||
Reference in new issue
Block a user