b9b6503315
* reorder prompt sections: task-first with dynamic TOC put the actual task at the top of the prompt for primacy, add a dynamic table of contents, and push system/runtime metadata to the end. new section order: TOC → YOUR TASK → PROCEDURE → EVENT CONTEXT → SYSTEM → LEARNINGS → RUNTIME Made-with: Cursor * enforce clean working tree: continue session if agent leaves uncommitted changes after each agent run, check `git status --porcelain`. if dirty, resume the same session with instructions to commit on a new branch, push, and open a PR. retries up to 3 times before giving up. - claude code: capture session_id from result event, use --resume <id> - opencode: use --continue to resume the last session - remove --no-session-persistence from claude (needed for --resume) - update Task mode to clarify branch/push/PR is the default finalize step Made-with: Cursor * log full prompt in collapsible group for debugging Made-with: Cursor * fix: format tool refs in buildCommitPrompt via formatMcpToolRef * enforce clean git status: general instructions, stop hook, and Task mode Made-with: Cursor * fix: rename stale titleBody references after body leak fix Made-with: Cursor --------- Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
import { execFileSync } from "node:child_process";
|
|
import type { AgentId } from "../external.ts";
|
|
import { log } from "../utils/cli.ts";
|
|
import type { ResolvedInstructions } from "../utils/instructions.ts";
|
|
import type { ResolvedPayload } from "../utils/payload.ts";
|
|
import type { TodoTracker } from "../utils/todoTracking.ts";
|
|
|
|
// maximum number of stderr lines to keep in the rolling buffer during agent execution
|
|
export const MAX_STDERR_LINES = 20;
|
|
|
|
// ── post-run commit enforcement ─────────────────────────────────────────────────
|
|
|
|
export const MAX_COMMIT_RETRIES = 3;
|
|
|
|
export function getGitStatus(): string {
|
|
try {
|
|
return execFileSync("git", ["status", "--porcelain"], {
|
|
encoding: "utf-8",
|
|
timeout: 10_000,
|
|
}).trim();
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
export function buildCommitPrompt(_agentId: AgentId, status: string): string {
|
|
return [
|
|
`UNCOMMITTED CHANGES — the working tree is dirty. push all changes to a pull request (new or existing). \`git status\` must be clean before you finish.`,
|
|
"",
|
|
"```",
|
|
status,
|
|
"```",
|
|
].join("\n");
|
|
}
|
|
|
|
/**
|
|
* token/cost usage data from a single agent run
|
|
*/
|
|
export interface AgentUsage {
|
|
agent: string;
|
|
inputTokens: number;
|
|
outputTokens: number;
|
|
cacheReadTokens?: number | undefined;
|
|
cacheWriteTokens?: number | undefined;
|
|
costUsd?: number | undefined;
|
|
}
|
|
|
|
/**
|
|
* Result returned by agent execution
|
|
*/
|
|
export interface AgentResult {
|
|
success: boolean;
|
|
output?: string | undefined;
|
|
error?: string | undefined;
|
|
metadata?: Record<string, unknown>;
|
|
usage?: AgentUsage | undefined;
|
|
}
|
|
|
|
/**
|
|
* Minimal context passed to agent.run()
|
|
*/
|
|
export interface AgentRunContext {
|
|
payload: ResolvedPayload;
|
|
resolvedModel?: string | undefined;
|
|
mcpServerUrl: string;
|
|
tmpdir: string;
|
|
instructions: ResolvedInstructions;
|
|
todoTracker?: TodoTracker | undefined;
|
|
}
|
|
|
|
export interface Agent {
|
|
name: AgentId;
|
|
install: (token?: string) => Promise<string>;
|
|
run: (ctx: AgentRunContext) => Promise<AgentResult>;
|
|
}
|
|
|
|
export const agent = (input: Agent): Agent => {
|
|
return {
|
|
...input,
|
|
run: async (ctx: AgentRunContext): Promise<AgentResult> => {
|
|
log.debug(`» payload: ${JSON.stringify(ctx.payload, null, 2)}`);
|
|
return input.run(ctx);
|
|
},
|
|
};
|
|
};
|