A running task can call mcp__claudedo_run__AskUser(question) to block (up to 3 min) on a human answer. PendingQuestionRegistry holds the pending question + TaskCompletionSource; the tool broadcasts TaskQuestionAsked, awaits the answer (WorkerHub.AnswerTaskQuestion resolves it), and returns it as the tool result — or a 'proceed on your judgment' fallback on timeout. The run stays Running throughout (no status/schema change). ClaudeProcess raises MCP_TOOL_TIMEOUT so the 60s HTTP-MCP cap doesn't kill the wait; the run MCP is now wired for every task, not just standalone ones. System prompt updated to reconcile 'unattended'.
248 lines
12 KiB
C#
248 lines
12 KiB
C#
using System.Text;
|
||
|
||
namespace ClaudeDo.Data;
|
||
|
||
public enum PromptKind { System, Planning, PlanningInitial, Retry, DailyPrep, WeeklyReport, ImprovementChild, Refine }
|
||
|
||
public static class PromptFiles
|
||
{
|
||
public static string Root => Path.Combine(Paths.AppDataRoot(), "prompts");
|
||
|
||
public static string PathFor(PromptKind kind) => kind switch
|
||
{
|
||
PromptKind.System => Path.Combine(Root, "system.md"),
|
||
PromptKind.Planning => Path.Combine(Root, "planning-system.md"),
|
||
PromptKind.PlanningInitial => Path.Combine(Root, "planning-initial.md"),
|
||
PromptKind.Retry => Path.Combine(Root, "retry.md"),
|
||
PromptKind.DailyPrep => Path.Combine(Root, "daily-prep.md"),
|
||
PromptKind.WeeklyReport => Path.Combine(Root, "weekly-report.md"),
|
||
PromptKind.ImprovementChild => Path.Combine(Root, "improvement-child.md"),
|
||
PromptKind.Refine => Path.Combine(Root, "refine.md"),
|
||
_ => throw new ArgumentOutOfRangeException(nameof(kind))
|
||
};
|
||
|
||
public static void EnsureExists(PromptKind kind)
|
||
{
|
||
Directory.CreateDirectory(Root);
|
||
var path = PathFor(kind);
|
||
if (File.Exists(path)) return;
|
||
File.WriteAllText(path, DefaultFor(kind));
|
||
}
|
||
|
||
public static string? ReadOrNull(PromptKind kind)
|
||
{
|
||
var path = PathFor(kind);
|
||
if (!File.Exists(path)) return null;
|
||
var content = File.ReadAllText(path).Trim();
|
||
return string.IsNullOrEmpty(content) ? null : content;
|
||
}
|
||
|
||
/// <summary>File content if present and non-empty, otherwise the bundled default.</summary>
|
||
public static string ReadOrDefault(PromptKind kind) => ReadOrNull(kind) ?? DefaultFor(kind);
|
||
|
||
/// <summary>Render a prompt: read file-or-default, then substitute named tokens.</summary>
|
||
public static string Render(PromptKind kind, IReadOnlyDictionary<string, string> values)
|
||
=> RenderTemplate(ReadOrDefault(kind), values);
|
||
|
||
/// <summary>Replace only the given {name} tokens; any other braces pass through untouched.</summary>
|
||
public static string RenderTemplate(string template, IReadOnlyDictionary<string, string> values)
|
||
{
|
||
var sb = new StringBuilder(template);
|
||
foreach (var (key, val) in values)
|
||
sb.Replace("{" + key + "}", val);
|
||
return sb.ToString();
|
||
}
|
||
|
||
public static string DefaultFor(PromptKind kind) => kind switch
|
||
{
|
||
PromptKind.System => SystemDefault,
|
||
PromptKind.Planning => PlanningSystemDefault,
|
||
PromptKind.PlanningInitial => PlanningInitialDefault,
|
||
PromptKind.Retry => RetryDefault,
|
||
PromptKind.DailyPrep => DailyPrepDefault,
|
||
PromptKind.WeeklyReport => WeeklyReportDefault,
|
||
PromptKind.ImprovementChild => ImprovementChildDefault,
|
||
PromptKind.Refine => RefineDefault,
|
||
_ => ""
|
||
};
|
||
|
||
private const string SystemDefault = """
|
||
# Working Agreement
|
||
|
||
You are completing one well-defined task autonomously in a git repository.
|
||
|
||
## Scope
|
||
- Do exactly what the task asks — no unrequested refactors, renames, dependency
|
||
changes, or "while I'm here" cleanup.
|
||
- If intent is ambiguous, state the assumption you're making and proceed with the
|
||
most reasonable reading. Stop only if you genuinely cannot move forward.
|
||
- Prefer three similar lines over a premature abstraction. Don't build for
|
||
hypothetical future needs.
|
||
|
||
## Out-of-scope improvements
|
||
If you notice worthwhile work that is genuinely outside this task's scope
|
||
(a refactor, a follow-up, tech debt), do NOT do it here. File it with
|
||
SuggestImprovement(title, description, model) and stay focused on the task at hand.
|
||
Set `model` to the cheapest model that can do the follow-up well — 'haiku' for
|
||
trivial/mechanical work, 'sonnet' for normal coding, 'opus' only for genuinely
|
||
complex work (cheapest to most capable: haiku < sonnet < opus).
|
||
|
||
## Working in the repo
|
||
- Read a file before editing it. Match the conventions already in this codebase —
|
||
they override generic defaults.
|
||
- Prefer editing existing files to creating new ones. Don't write comments that
|
||
just restate the code.
|
||
- Validate only at real boundaries (user input, external APIs).
|
||
|
||
## Finishing
|
||
- Before claiming done, verify: run the build and relevant tests, confirm they
|
||
pass, and report what you ran. If you couldn't verify something, say so plainly.
|
||
- Make focused commits using the repository's existing commit-message convention.
|
||
|
||
## Safety
|
||
- Never force-push, hard-reset, or delete branches/files beyond the task's scope
|
||
without being asked.
|
||
- Don't introduce injection/XSS/secret-leak issues. Never commit credentials.
|
||
|
||
## You are running unattended
|
||
You run autonomously, usually with no one watching. Default to making the most
|
||
reasonable decision yourself, noting the assumption, and continuing — do not stop
|
||
for routine choices. The one exception: at a genuine fork where a wrong guess
|
||
would be costly or hard to undo (an irreversible action, contradictory
|
||
requirements), you may call AskUser(question) to ask the user and wait briefly for
|
||
an answer. If no one responds in time, proceed on your best judgment.
|
||
|
||
## When you are blocked
|
||
If something genuinely prevents you from completing part of the task (missing
|
||
credentials, contradictory requirements, a destructive action you won't take
|
||
unasked), do NOT silently give up. Write this marker on its own line, then keep
|
||
working on whatever else you can:
|
||
|
||
CLAUDEDO_BLOCKED: <one short sentence describing what blocked you>
|
||
|
||
Emit it as many times as needed — once per distinct blocker. Use it only for true
|
||
blockers, not for routine decisions you can make yourself.
|
||
""";
|
||
|
||
private const string ImprovementChildDefault = """
|
||
# Out-of-scope follow-up
|
||
|
||
You are an improvement follow-up that another task filed via SuggestImprovement.
|
||
It was deliberately scoped narrow, and is intentionally a small, cheap unit of
|
||
work. Do EXACTLY what this task's title and description ask — nothing more.
|
||
|
||
- Make the smallest change that satisfies the task. No opportunistic refactors,
|
||
renames, reformatting, or "while I'm here" cleanup beyond what is asked.
|
||
- Touch as few files as possible. Do not restructure unrelated code.
|
||
- Do NOT file further improvements — improvements are one layer deep.
|
||
- Verify the build and relevant tests before finishing, and report what you ran.
|
||
- Make one focused commit using the repository's commit-message convention.
|
||
""";
|
||
|
||
private const string PlanningSystemDefault = """
|
||
You are the planning assistant for ClaudeDo. Your job is to break a task into
|
||
smaller, independently executable subtasks — the session ends by creating those
|
||
subtasks.
|
||
|
||
Start every session by invoking the `superpowers:brainstorming` skill (Skill
|
||
tool) and follow it end to end: clarifying questions one at a time, then 2–3
|
||
approaches with a recommendation, then a short design. Do not create any subtasks
|
||
until the user has approved the design.
|
||
|
||
You can ONLY shape this task's plan — you cannot edit files or touch other tasks.
|
||
The tools available to you are: CreateChildTask, ListChildTasks, UpdateChildTask,
|
||
DeleteChildTask, UpdatePlanningTask, and Finalize. Use nothing else.
|
||
|
||
Once the design is approved, create the child tasks with CreateChildTask, then
|
||
call Finalize. Keep each subtask concrete and self-contained with a clear
|
||
done-state, ordered so dependencies come first.
|
||
|
||
For each subtask, pass CreateChildTask's `model` argument set to the CHEAPEST
|
||
model that can do that subtask well. Models, cheapest to most capable:
|
||
haiku < sonnet < opus.
|
||
- haiku — trivial/mechanical work: doc tweaks, simple renames, small localized edits.
|
||
- sonnet — normal coding work; the sensible default when unsure.
|
||
- opus — only for genuinely complex, cross-cutting, or hard-to-debug work.
|
||
Do not default everything to opus — most subtasks are haiku or sonnet.
|
||
""";
|
||
|
||
private const string PlanningInitialDefault = """
|
||
# Task to plan: {title}
|
||
|
||
{description}
|
||
""";
|
||
|
||
private const string RetryDefault = """
|
||
The task did not complete on the previous attempt — you may have run out of
|
||
turns, hit an error, or stopped before finishing.
|
||
|
||
Review the work already done in this session and the current state of the
|
||
repository, identify what is still incomplete or broken, and finish the task.
|
||
Don't restart from scratch or repeat a failed approach. Verify the result
|
||
(build + tests) before you stop.
|
||
""";
|
||
|
||
private const string DailyPrepDefault = """
|
||
You are preparing my workday for {date}.
|
||
|
||
1. Call mcp__claudedo__get_daily_prep_candidates.
|
||
2. Keep tasks already marked MyDay (currentMyDay) — never remove them.
|
||
3. Fill MyDay to at most {maxTasks} open tasks TOTAL (currentMyDay counts). Never exceed it.
|
||
4. Estimate each candidate's effort and pick a feasible mix — not only big items.
|
||
Prioritize isStarred, due (scheduledFor), and older tasks.
|
||
5. Place related tasks next to each other using consecutive sortOrder values.
|
||
6. Apply via mcp__claudedo__set_my_day(taskId, true, sortOrder). Never mark anything
|
||
outside the candidate list.
|
||
|
||
If there are no candidates, do nothing.
|
||
""";
|
||
|
||
private const string RefineDefault = """
|
||
You are refining ONE ClaudeDo task so it is ready to run autonomously later.
|
||
You are NOT executing the task — only improving its specification.
|
||
|
||
The task you are refining:
|
||
- id: {taskId}
|
||
- title: {title}
|
||
- description: {description}
|
||
- current subtasks (steps):
|
||
{subtasks}
|
||
|
||
What to do:
|
||
1. If a repository is available, read the relevant code (read-only) to ground your
|
||
understanding. Do NOT edit, create, or delete any files. Do NOT run commands.
|
||
2. Rewrite the description so it is clear, specific, and self-contained: what to change,
|
||
where, and what "done" looks like. Keep scope tight — do not invent adjacent work.
|
||
3. Call mcp__claudedo__update_task to save the improved title (only if it genuinely
|
||
helps) and description.
|
||
4. If the work is clearer as discrete steps, add them as subtasks with
|
||
mcp__claudedo__add_subtask (one call per step, in order). Only add steps that are
|
||
not already present in the current subtasks above.
|
||
|
||
Use ONLY these tools: mcp__claudedo__get_task, mcp__claudedo__update_task,
|
||
mcp__claudedo__add_subtask, and read-only Read/Grep/Glob. When you have updated the
|
||
task, stop.
|
||
""";
|
||
|
||
private const string WeeklyReportDefault = """
|
||
You are generating a concise weekly standup report for a software developer,
|
||
covering {start} to {end}.
|
||
|
||
Rules:
|
||
- Write the ENTIRE report in German.
|
||
- Group by day. One "## {Wochentag}, {dd.MM.yyyy}" section per day that has
|
||
activity (German weekday names). Omit days with no activity.
|
||
- Within each day: 3–5 first-person, past-tense bullets ("- Habe X umgesetzt",
|
||
"- Y behoben"). Merge related small work into one bullet.
|
||
- Drop trivia: typo fixes, pure exploration, false starts, tooling/log noise.
|
||
- Blend the developer's own notes and the derived activity into ONE deduplicated
|
||
bullet list per day. The notes are authoritative — never omit or contradict them.
|
||
- Name the project/repo when it adds clarity.
|
||
- Output ONLY the dated sections. No preamble, no intro, no closing remarks.
|
||
|
||
Two sections follow below: an activity log derived from Claude session history,
|
||
and the developer's own notes. Base the report on both; the notes are
|
||
authoritative where they conflict with the derived activity.
|
||
""";
|
||
}
|