From 7942ba4bda29b9a178cc9eff2e7a27576190b90d Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 02:45:55 +0200 Subject: [PATCH] chore(sf): auto-prompts residual sweep Co-Authored-By: Claude Sonnet 4.6 --- src/resources/extensions/sf/auto-prompts.ts | 5 +- .../extensions/sf/bootstrap/judgment-tools.ts | 70 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/resources/extensions/sf/bootstrap/judgment-tools.ts diff --git a/src/resources/extensions/sf/auto-prompts.ts b/src/resources/extensions/sf/auto-prompts.ts index b27aae8a4..47126e844 100644 --- a/src/resources/extensions/sf/auto-prompts.ts +++ b/src/resources/extensions/sf/auto-prompts.ts @@ -1776,6 +1776,9 @@ export async function buildPlanMilestonePrompt( relMilestoneFile(base, mid, "RESEARCH"), ); const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS")); + const inlinedContextWithFraming = framingBlock + ? `${framingBlock}\n\n${inlinedContext}` + : inlinedContext; return loadPrompt("plan-milestone", { workingDirectory: base, milestoneId: mid, @@ -1786,7 +1789,7 @@ export async function buildPlanMilestonePrompt( researchOutputPath, outputPath: join(base, outputRelPath), secretsOutputPath, - inlinedContext, + inlinedContext: inlinedContextWithFraming, sourceFilePaths: buildSourceFilePaths(base, mid), skillActivation: buildSkillActivationBlock({ base, diff --git a/src/resources/extensions/sf/bootstrap/judgment-tools.ts b/src/resources/extensions/sf/bootstrap/judgment-tools.ts new file mode 100644 index 000000000..137fc0300 --- /dev/null +++ b/src/resources/extensions/sf/bootstrap/judgment-tools.ts @@ -0,0 +1,70 @@ +/** + * Judgment tools — expose sf_log_judgment to the agent in autonomous mode. + * + * The agent is instructed (via the system prompt) to call this tool when + * making non-trivial calls so the user can review reasoning at milestone close. + */ + +import { Type } from "@sinclair/typebox"; +import type { ExtensionAPI } from "@singularity-forge/pi-coding-agent"; + +import { appendJudgment } from "../judgment-log.js"; + +export function registerJudgmentTools(pi: ExtensionAPI): void { + pi.registerTool({ + name: "sf_log_judgment", + label: "Log Judgment", + description: + "Record an agent judgment call for user review at milestone close. " + + "Call this when choosing between alternatives at an ambiguous decision point. " + + "Does NOT delay or block work — pure append-only side-effect.", + promptSnippet: + "Log a judgment call: decision taken, alternatives considered, reasoning, confidence", + promptGuidelines: [ + "Call whenever you choose one approach over plausible alternatives.", + "Set confidence=low when the decision is speculative or you lacked context.", + "Set confidence=high when you had strong evidence for the choice.", + "This call is fire-and-forget — never wait for it or re-read the log mid-task.", + ], + parameters: Type.Object({ + unitId: Type.String({ + description: + "Current unit ID (e.g. M001/S01/T01). Use the active task ID.", + }), + decision: Type.String({ + description: "Short description of the decision taken (1-2 sentences).", + }), + alternatives: Type.Array(Type.String(), { + description: "Alternatives that were considered but not chosen.", + }), + reasoning: Type.String({ + description: + "Why this decision was made over the alternatives (1-3 sentences).", + }), + confidence: Type.Union( + [Type.Literal("low"), Type.Literal("medium"), Type.Literal("high")], + { + description: + "Agent confidence in the decision: low = speculative, medium = reasonable, high = well-evidenced.", + }, + ), + }), + execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => { + appendJudgment(process.cwd(), { + unitId: params.unitId, + decision: params.decision, + alternatives: params.alternatives, + reasoning: params.reasoning, + confidence: params.confidence, + }); + return { + content: [ + { + type: "text" as const, + text: `Judgment logged for unit ${params.unitId}: "${params.decision}" (confidence: ${params.confidence})`, + }, + ], + }; + }, + }); +}