diff --git a/src/ClaudeDo.Data/PromptFiles.cs b/src/ClaudeDo.Data/PromptFiles.cs
index d1a98cb..4d8d113 100644
--- a/src/ClaudeDo.Data/PromptFiles.cs
+++ b/src/ClaudeDo.Data/PromptFiles.cs
@@ -1,6 +1,8 @@
+using System.Text;
+
namespace ClaudeDo.Data;
-public enum PromptKind { System, Planning, Agent }
+public enum PromptKind { System, Planning, PlanningInitial, Retry, DailyPrep, WeeklyReport }
public static class PromptFiles
{
@@ -9,8 +11,11 @@ public static class PromptFiles
public static string PathFor(PromptKind kind) => kind switch
{
PromptKind.System => Path.Combine(Root, "system.md"),
- PromptKind.Planning => Path.Combine(Root, "planning.md"),
- PromptKind.Agent => Path.Combine(Root, "agent.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"),
_ => throw new ArgumentOutOfRangeException(nameof(kind))
};
@@ -30,29 +35,148 @@ public static class PromptFiles
return string.IsNullOrEmpty(content) ? null : content;
}
- private static string DefaultFor(PromptKind kind) => kind switch
+ /// File content if present and non-empty, otherwise the bundled default.
+ public static string ReadOrDefault(PromptKind kind) => ReadOrNull(kind) ?? DefaultFor(kind);
+
+ /// Render a prompt: read file-or-default, then substitute named tokens.
+ public static string Render(PromptKind kind, IReadOnlyDictionary values)
+ => RenderTemplate(ReadOrDefault(kind), values);
+
+ /// Replace only the given {name} tokens; any other braces pass through untouched.
+ public static string RenderTemplate(string template, IReadOnlyDictionary values)
{
- PromptKind.System =>
- "# System Prompt\n\n" +
- "Baseline instructions appended to every task run.\n" +
- "Edit this file to inject project-wide rules (style, conventions, hard constraints).\n",
- PromptKind.Planning =>
- "You are a planning assistant for ClaudeDo.\n" +
- "Your role is to help break down a task into smaller, actionable subtasks.\n" +
- "Your final goal WILL ALWAYS be the creation of Subtasks.\n\n" +
- "ALWAYS invoke the `superpowers:brainstorming` skill via the Skill tool at the\n" +
- "start of every planning session, and follow its process end-to-end. It guides\n" +
- "you through clarifying questions, approach exploration, and design approval\n" +
- "BEFORE any subtasks are created. Do not create child tasks until the user has\n" +
- "approved a design.\n\n" +
- "NEVER change files yourself.\n\n" +
- "ALWAYS use the available MCP tools (mcp__claudedo__*) to create child tasks once\n" +
- "the design is approved. When you are done planning, finalize the session.\n\n" +
- "Be concise and focused. Each subtask should be independently executable.\n",
- PromptKind.Agent =>
- "# Agent Prompt\n\n" +
- "Appended to the system prompt for tasks tagged \"agent\" (auto-queued runs).\n" +
- "Use this for autonomous-execution rules that don't apply to manual runs.\n",
+ 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,
_ => ""
};
+
+ 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.
+
+ ## 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 with no human watching. There is no one to answer mid-task
+ questions, so never stop to ask — make the most reasonable decision, note the
+ assumption, and continue.
+
+ ## 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:
+
+ 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 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.
+ """;
+
+ 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 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.
+ """;
}
diff --git a/tests/ClaudeDo.Data.Tests/PromptFilesTests.cs b/tests/ClaudeDo.Data.Tests/PromptFilesTests.cs
new file mode 100644
index 0000000..6f00b50
--- /dev/null
+++ b/tests/ClaudeDo.Data.Tests/PromptFilesTests.cs
@@ -0,0 +1,46 @@
+using ClaudeDo.Data;
+
+namespace ClaudeDo.Data.Tests;
+
+public class PromptFilesTests
+{
+ [Fact]
+ public void RenderTemplate_replaces_known_tokens()
+ {
+ var outp = PromptFiles.RenderTemplate(
+ "Plan for {date}, cap {maxTasks}.",
+ new Dictionary { ["date"] = "2026-06-04", ["maxTasks"] = "5" });
+ Assert.Equal("Plan for 2026-06-04, cap 5.", outp);
+ }
+
+ [Fact]
+ public void RenderTemplate_leaves_unknown_braces_intact()
+ {
+ var outp = PromptFiles.RenderTemplate(
+ "## {Wochentag}, {dd.MM.yyyy} — {start}",
+ new Dictionary { ["start"] = "01.06.2026" });
+ Assert.Equal("## {Wochentag}, {dd.MM.yyyy} — 01.06.2026", outp);
+ }
+
+ [Fact]
+ public void DefaultFor_system_mentions_blocked_marker_and_scope()
+ {
+ var d = PromptFiles.DefaultFor(PromptKind.System);
+ Assert.Contains("CLAUDEDO_BLOCKED:", d);
+ Assert.Contains("unattended", d, StringComparison.OrdinalIgnoreCase);
+ }
+
+ [Fact]
+ public void DefaultFor_planning_initial_has_title_and_description_tokens()
+ {
+ var d = PromptFiles.DefaultFor(PromptKind.PlanningInitial);
+ Assert.Contains("{title}", d);
+ Assert.Contains("{description}", d);
+ }
+
+ [Fact]
+ public void PathFor_planning_is_planning_system_file()
+ {
+ Assert.EndsWith("planning-system.md", PromptFiles.PathFor(PromptKind.Planning));
+ }
+}