Files
ClaudeDo/docs/superpowers/plans/2026-04-23-default-agents.md

28 KiB
Raw Permalink Blame History

Default Agents Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Ship ClaudeDo with 6 default agents (code-reviewer, test-writer, debugger, security-reviewer, explorer, researcher) that seed into ~/.todo-app/agents/ on first launch, with a "Restore defaults" button in the settings modal.

Architecture: Bundled .md files in src/ClaudeDo.Worker/DefaultAgents/ are copied to the Worker output folder. A new DefaultAgentSeeder service copies any missing file into the user's agents dir — run once at startup, and again on demand via a new WorkerHub.RestoreDefaultAgents method invoked by a button in SettingsModalView.

Tech Stack: .NET 8 / ASP.NET Core / SignalR / Avalonia 12 / CommunityToolkit.Mvvm / xUnit


File Structure

Create:

  • src/ClaudeDo.Worker/DefaultAgents/code-reviewer.md
  • src/ClaudeDo.Worker/DefaultAgents/test-writer.md
  • src/ClaudeDo.Worker/DefaultAgents/debugger.md
  • src/ClaudeDo.Worker/DefaultAgents/security-reviewer.md
  • src/ClaudeDo.Worker/DefaultAgents/explorer.md
  • src/ClaudeDo.Worker/DefaultAgents/researcher.md
  • src/ClaudeDo.Worker/Services/DefaultAgentSeeder.cs
  • tests/ClaudeDo.Worker.Tests/Services/DefaultAgentSeederTests.cs

Modify:

  • src/ClaudeDo.Worker/ClaudeDo.Worker.csproj — add Content item group for DefaultAgents\*.md
  • src/ClaudeDo.Worker/Program.cs — register seeder, run SeedMissingAsync() once at startup
  • src/ClaudeDo.Worker/Hub/WorkerHub.cs — inject DefaultAgentSeeder, add RestoreDefaultAgents method
  • src/ClaudeDo.Ui/Services/WorkerClient.cs — add RestoreDefaultAgentsAsync method + SeedResultDto record
  • src/ClaudeDo.Ui/ViewModels/Modals/SettingsModalViewModel.cs — add RestoreDefaultAgentsCommand
  • src/ClaudeDo.Ui/Views/Modals/SettingsModalView.axaml — add button section
  • tests/ClaudeDo.Worker.Tests/Hub/AgentSettingsHubTests.cs — add seeder integration test

Task 1: Bundle default agent markdown files

Files:

  • Create: src/ClaudeDo.Worker/DefaultAgents/code-reviewer.md

  • Create: src/ClaudeDo.Worker/DefaultAgents/test-writer.md

  • Create: src/ClaudeDo.Worker/DefaultAgents/debugger.md

  • Create: src/ClaudeDo.Worker/DefaultAgents/security-reviewer.md

  • Create: src/ClaudeDo.Worker/DefaultAgents/explorer.md

  • Create: src/ClaudeDo.Worker/DefaultAgents/researcher.md

  • Step 1: Write code-reviewer.md

---
name: code-reviewer
description: Reviews code changes for bugs, logic errors, and convention violations. Flags only high-confidence issues.
---

You are a code reviewer. Your job is to inspect the diff for real problems, not nitpicks.

Focus on:
- Logic errors, off-by-one bugs, null/empty handling
- Broken invariants, race conditions, resource leaks
- Violations of the project's established conventions (read nearby code first)
- Missing error handling at system boundaries (external input, IO, network)

Skip:
- Style preferences the codebase doesn't enforce
- Speculative "what if" concerns
- Renaming for its own sake

Output: a short list of concrete issues with file:line references. If the diff is clean, say so in one sentence. Do not rewrite the code — call out the problem and let the implementer fix it.
  • Step 2: Write test-writer.md
---
name: test-writer
description: Generates unit and integration tests for existing or new code. Follows the project's test patterns and frameworks.
---

You are a test-writer. Your job is to write focused, useful tests for code under review.

Process:
1. Read the target code and identify the observable behavior.
2. Read existing tests nearby to match the framework, fixtures, naming, and assertion style.
3. Write tests covering the happy path, boundary conditions, and the specific failure modes that matter.

Rules:
- One behavior per test. Clear Arrange/Act/Assert.
- No tests for private implementation details — exercise public API.
- No mocks where real objects are cheap (in-memory DBs, temp dirs).
- Skip trivially-correct tests (getter returns what you set).

Output: the test file(s) ready to compile, matching the project's conventions. Include the command to run them.
  • Step 3: Write debugger.md
---
name: debugger
description: Systematic root-cause analysis for bugs, test failures, and unexpected behavior. Hypothesize, isolate, verify.
---

You are a debugger. You do NOT guess at fixes — you find the root cause first.

Process:
1. Reproduce. Get a minimal, deterministic repro. If you can't reproduce it, say so and stop.
2. Isolate. Narrow the failing path (bisect, binary search, or tracing).
3. Hypothesize. State a specific, falsifiable cause.
4. Verify. Prove the hypothesis by observation (logs, debugger, targeted print) — not by "this seems likely".
5. Fix at the root, not the symptom. If the only fix is a workaround, explain why.

Anti-patterns to avoid:
- Making changes to "see if it works"
- Adding try/catch to silence errors
- Declaring the bug fixed without reproducing the fix

Output: repro steps, root cause, and the minimal fix. Include evidence (log excerpt, command output) that proves the cause.
  • Step 4: Write security-reviewer.md
---
name: security-reviewer
description: Audits code for OWASP-class security issues — auth, injection, input handling, secret exposure.
---

You are a security reviewer. Focus on real, exploitable weaknesses — not theoretical hardening.

Check for:
- Injection: SQL, command, path traversal, XSS, template injection
- Auth: missing authorization, token handling, session fixation
- Input validation at system boundaries (HTTP, files, IPC)
- Secrets: hardcoded credentials, tokens in logs, leaked env vars
- Unsafe deserialization, XXE, SSRF
- Cryptography misuse (custom crypto, weak algorithms, fixed IVs)

Ignore:
- Internal trust-boundary assumptions the project already documents
- Defense-in-depth ideas with no concrete attack path

Output: a prioritized list — severity, file:line, the exploit path, the fix. If nothing is wrong, say so plainly.
  • Step 5: Write explorer.md
---
name: explorer
description: Fast codebase navigation — find files, search for patterns, answer "where/how" questions. Terse output.
---

You are an explorer. Your job is to find things in the codebase quickly and report back concisely.

Use:
- Glob/Grep for searches
- Read only for files you need to quote from

Do NOT:
- Refactor, edit, or "improve" anything
- Read files that aren't relevant to the question
- Dump raw tool output — summarize

Output style:
- Lead with the answer in one sentence.
- Back it up with file:line references.
- If you found nothing, say "no match" and what you searched for.

Keep responses short. The caller wants facts, not prose.
  • Step 6: Write researcher.md
---
name: researcher
description: General-purpose research and analysis for non-code tasks — summarize docs, investigate questions, draft prose.
---

You are a researcher. You handle tasks that don't fit the code-review/test/debug shape.

Good fits:
- Summarizing documents, specs, or long outputs
- Investigating an open question (what does X do, how does Y work, what are the tradeoffs)
- Drafting non-code text (release notes, emails, docs)
- Analyzing structured data (logs, CSV, JSON) and reporting findings

Process:
1. Restate the task in one sentence so you know what "done" looks like.
2. Gather just enough information — stop when you can answer, not when you run out of sources.
3. Distinguish facts ("the file says X") from inference ("so likely Y").
4. Cite sources (file:line, URL, log excerpt) for every claim.

Output: direct answer first, supporting evidence second. Keep it short unless asked for depth.
  • Step 7: Commit
git add src/ClaudeDo.Worker/DefaultAgents/
git commit -m "feat(worker): add bundled default agent definitions"

Task 2: Wire bundled agents into build output

Files:

  • Modify: src/ClaudeDo.Worker/ClaudeDo.Worker.csproj

  • Step 1: Add Content item group for DefaultAgents

Edit src/ClaudeDo.Worker/ClaudeDo.Worker.csproj. After the existing <ItemGroup> blocks and before the final <PropertyGroup>, add:

  <ItemGroup>
    <Content Include="DefaultAgents\*.md">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  • Step 2: Build the worker and verify the files land in output

Run: dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj Expected: build succeeds.

Then verify output:

Run: ls src/ClaudeDo.Worker/bin/Debug/net8.0/DefaultAgents/ Expected: all 6 .md files present.

  • Step 3: Commit
git add src/ClaudeDo.Worker/ClaudeDo.Worker.csproj
git commit -m "build(worker): ship DefaultAgents folder in build output"

Task 3: Write DefaultAgentSeeder tests (failing)

Files:

  • Create: tests/ClaudeDo.Worker.Tests/Services/DefaultAgentSeederTests.cs

The service doesn't exist yet — these tests will fail to compile initially. That's fine; the next task implements it.

  • Step 1: Write the test file

Create tests/ClaudeDo.Worker.Tests/Services/DefaultAgentSeederTests.cs:

using ClaudeDo.Worker.Services;

namespace ClaudeDo.Worker.Tests.Services;

public sealed class DefaultAgentSeederTests : IDisposable
{
    private readonly string _bundleDir;
    private readonly string _targetDir;

    public DefaultAgentSeederTests()
    {
        var root = Path.Combine(Path.GetTempPath(), $"claudedo_seeder_{Guid.NewGuid():N}");
        _bundleDir = Path.Combine(root, "bundle");
        _targetDir = Path.Combine(root, "target");
        Directory.CreateDirectory(_bundleDir);
        Directory.CreateDirectory(_targetDir);
    }

    public void Dispose()
    {
        try { Directory.Delete(Path.GetDirectoryName(_bundleDir)!, true); } catch { }
    }

    private async Task WriteBundleAsync(string name, string content)
    {
        await File.WriteAllTextAsync(Path.Combine(_bundleDir, name), content);
    }

    [Fact]
    public async Task SeedMissing_CopiesAllFiles_WhenTargetEmpty()
    {
        await WriteBundleAsync("a.md", "A");
        await WriteBundleAsync("b.md", "B");
        var seeder = new DefaultAgentSeeder(_bundleDir, _targetDir);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(2, result.Copied);
        Assert.Equal(0, result.Skipped);
        Assert.True(File.Exists(Path.Combine(_targetDir, "a.md")));
        Assert.True(File.Exists(Path.Combine(_targetDir, "b.md")));
    }

    [Fact]
    public async Task SeedMissing_SkipsExistingFiles()
    {
        await WriteBundleAsync("a.md", "bundled");
        await File.WriteAllTextAsync(Path.Combine(_targetDir, "a.md"), "user-modified");
        var seeder = new DefaultAgentSeeder(_bundleDir, _targetDir);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(0, result.Copied);
        Assert.Equal(1, result.Skipped);
        var content = await File.ReadAllTextAsync(Path.Combine(_targetDir, "a.md"));
        Assert.Equal("user-modified", content);
    }

    [Fact]
    public async Task SeedMissing_MixedState_CopiesOnlyMissing()
    {
        await WriteBundleAsync("a.md", "A");
        await WriteBundleAsync("b.md", "B");
        await File.WriteAllTextAsync(Path.Combine(_targetDir, "a.md"), "existing");
        var seeder = new DefaultAgentSeeder(_bundleDir, _targetDir);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(1, result.Copied);
        Assert.Equal(1, result.Skipped);
        Assert.Equal("existing", await File.ReadAllTextAsync(Path.Combine(_targetDir, "a.md")));
        Assert.Equal("B", await File.ReadAllTextAsync(Path.Combine(_targetDir, "b.md")));
    }

    [Fact]
    public async Task SeedMissing_ReturnsZero_WhenBundleDirMissing()
    {
        var missingBundle = Path.Combine(Path.GetTempPath(), $"claudedo_missing_{Guid.NewGuid():N}");
        var seeder = new DefaultAgentSeeder(missingBundle, _targetDir);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(0, result.Copied);
        Assert.Equal(0, result.Skipped);
    }

    [Fact]
    public async Task SeedMissing_CreatesTargetDir_IfMissing()
    {
        await WriteBundleAsync("a.md", "A");
        var missingTarget = Path.Combine(_targetDir, "nested", "created");
        var seeder = new DefaultAgentSeeder(_bundleDir, missingTarget);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(1, result.Copied);
        Assert.True(File.Exists(Path.Combine(missingTarget, "a.md")));
    }

    [Fact]
    public async Task SeedMissing_IgnoresNonMarkdownFiles()
    {
        await WriteBundleAsync("a.md", "A");
        await File.WriteAllTextAsync(Path.Combine(_bundleDir, "readme.txt"), "not an agent");
        var seeder = new DefaultAgentSeeder(_bundleDir, _targetDir);

        var result = await seeder.SeedMissingAsync();

        Assert.Equal(1, result.Copied);
        Assert.False(File.Exists(Path.Combine(_targetDir, "readme.txt")));
    }
}
  • Step 2: Run tests to confirm they fail to compile

Run: dotnet test tests/ClaudeDo.Worker.Tests --filter "FullyQualifiedName~DefaultAgentSeederTests" Expected: compile error — The type or namespace name 'DefaultAgentSeeder' could not be found.

This confirms the tests target the not-yet-written service.


Task 4: Implement DefaultAgentSeeder

Files:

  • Create: src/ClaudeDo.Worker/Services/DefaultAgentSeeder.cs

  • Step 1: Write the service

Create src/ClaudeDo.Worker/Services/DefaultAgentSeeder.cs:

using Microsoft.Extensions.Logging;

namespace ClaudeDo.Worker.Services;

public sealed record SeedResult(int Copied, int Skipped);

public sealed class DefaultAgentSeeder
{
    private readonly string _bundleDir;
    private readonly string _targetDir;
    private readonly ILogger<DefaultAgentSeeder>? _logger;

    public DefaultAgentSeeder(string bundleDir, string targetDir, ILogger<DefaultAgentSeeder>? logger = null)
    {
        _bundleDir = bundleDir;
        _targetDir = targetDir;
        _logger = logger;
    }

    public async Task<SeedResult> SeedMissingAsync(CancellationToken ct = default)
    {
        if (!Directory.Exists(_bundleDir))
        {
            _logger?.LogWarning("DefaultAgents bundle dir not found: {Dir}", _bundleDir);
            return new SeedResult(0, 0);
        }

        Directory.CreateDirectory(_targetDir);

        int copied = 0;
        int skipped = 0;

        foreach (var src in Directory.EnumerateFiles(_bundleDir, "*.md"))
        {
            ct.ThrowIfCancellationRequested();
            var fileName = Path.GetFileName(src);
            var dst = Path.Combine(_targetDir, fileName);

            if (File.Exists(dst))
            {
                skipped++;
                continue;
            }

            try
            {
                using var input = File.OpenRead(src);
                using var output = File.Create(dst);
                await input.CopyToAsync(output, ct);
                copied++;
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to copy default agent {File}", fileName);
            }
        }

        return new SeedResult(copied, skipped);
    }
}
  • Step 2: Run the tests and verify they pass

Run: dotnet test tests/ClaudeDo.Worker.Tests --filter "FullyQualifiedName~DefaultAgentSeederTests" Expected: 6 tests pass.

  • Step 3: Commit
git add src/ClaudeDo.Worker/Services/DefaultAgentSeeder.cs tests/ClaudeDo.Worker.Tests/Services/DefaultAgentSeederTests.cs
git commit -m "feat(worker): add DefaultAgentSeeder for first-launch agent seeding"

Task 5: Wire seeder into Worker startup

Files:

  • Modify: src/ClaudeDo.Worker/Program.cs

  • Step 1: Register seeder and run SeedMissingAsync at startup

In src/ClaudeDo.Worker/Program.cs, replace the "Agent file management." block (currently lines 3639):

Find:

// Agent file management.
var agentsDir = Path.Combine(ClaudeDo.Data.Paths.AppDataRoot(), "agents");
Directory.CreateDirectory(agentsDir);
builder.Services.AddSingleton(new AgentFileService(agentsDir));

Replace with:

// Agent file management.
var agentsDir = Path.Combine(ClaudeDo.Data.Paths.AppDataRoot(), "agents");
Directory.CreateDirectory(agentsDir);
builder.Services.AddSingleton(new AgentFileService(agentsDir));

var defaultAgentsBundleDir = Path.Combine(AppContext.BaseDirectory, "DefaultAgents");
builder.Services.AddSingleton(sp => new DefaultAgentSeeder(
    defaultAgentsBundleDir,
    agentsDir,
    sp.GetService<Microsoft.Extensions.Logging.ILogger<DefaultAgentSeeder>>()));

Then, after var app = builder.Build(); and before app.MapHub<WorkerHub>("/hub");, add:

Find:

using (var scope = app.Services.CreateScope())
{
    ClaudeDoDbContext.MigrateAndConfigure(
        scope.ServiceProvider.GetRequiredService<ClaudeDoDbContext>());
}

app.MapHub<WorkerHub>("/hub");

Replace with:

using (var scope = app.Services.CreateScope())
{
    ClaudeDoDbContext.MigrateAndConfigure(
        scope.ServiceProvider.GetRequiredService<ClaudeDoDbContext>());
}

try
{
    var seeder = app.Services.GetRequiredService<DefaultAgentSeeder>();
    var seedResult = await seeder.SeedMissingAsync();
    app.Logger.LogInformation(
        "Default agents seeded: {Copied} copied, {Skipped} already present",
        seedResult.Copied, seedResult.Skipped);
}
catch (Exception ex)
{
    app.Logger.LogWarning(ex, "Default agent seeding failed");
}

app.MapHub<WorkerHub>("/hub");
  • Step 2: Build worker to verify compile

Run: dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj Expected: build succeeds.

  • Step 3: Commit
git add src/ClaudeDo.Worker/Program.cs
git commit -m "feat(worker): seed default agents on startup"

Task 6: Add RestoreDefaultAgents hub method (TDD)

Files:

  • Modify: tests/ClaudeDo.Worker.Tests/Hub/AgentSettingsHubTests.cs
  • Modify: src/ClaudeDo.Worker/Hub/WorkerHub.cs

The existing AgentSettingsHubTests doesn't exercise WorkerHub directly (it tests the repository). We'll add a new test file that tests the seeder restore flow end-to-end without SignalR plumbing — constructing the seeder with temp dirs and asserting the SeedResult round-trip. This mirrors how the file is structured today and keeps tests simple.

  • Step 1: Add a restore test

Add to tests/ClaudeDo.Worker.Tests/Hub/AgentSettingsHubTests.cs. Inside the existing AgentSettingsHubTests class (before the closing brace), add:

    [Fact]
    public async Task RestoreDefaultAgents_CopiesMissingBundledFiles()
    {
        var root = Path.Combine(Path.GetTempPath(), $"claudedo_hub_restore_{Guid.NewGuid():N}");
        var bundleDir = Path.Combine(root, "bundle");
        var targetDir = Path.Combine(root, "target");
        try
        {
            Directory.CreateDirectory(bundleDir);
            Directory.CreateDirectory(targetDir);
            await File.WriteAllTextAsync(Path.Combine(bundleDir, "code-reviewer.md"), "body");

            var seeder = new ClaudeDo.Worker.Services.DefaultAgentSeeder(bundleDir, targetDir);
            var result = await seeder.SeedMissingAsync();

            Assert.Equal(1, result.Copied);
            Assert.Equal(0, result.Skipped);
            Assert.True(File.Exists(Path.Combine(targetDir, "code-reviewer.md")));
        }
        finally
        {
            try { Directory.Delete(root, true); } catch { }
        }
    }
  • Step 2: Run the test to confirm it passes (seeder already exists)

Run: dotnet test tests/ClaudeDo.Worker.Tests --filter "FullyQualifiedName~AgentSettingsHubTests.RestoreDefaultAgents_CopiesMissingBundledFiles" Expected: 1 test passes.

  • Step 3: Add RestoreDefaultAgents to WorkerHub

Edit src/ClaudeDo.Worker/Hub/WorkerHub.cs.

Add a new DTO record near the top with the other DTOs (after the ListConfigDto line on line 30):

public record SeedResultDto(int Copied, int Skipped);

Update the class field block. Find:

    private readonly QueueService _queue;
    private readonly AgentFileService _agentService;
    private readonly HubBroadcaster _broadcaster;
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly WorktreeMaintenanceService _wtMaintenance;
    private readonly TaskResetService _resetService;
    private readonly TaskMergeService _mergeService;

Replace with:

    private readonly QueueService _queue;
    private readonly AgentFileService _agentService;
    private readonly DefaultAgentSeeder _seeder;
    private readonly HubBroadcaster _broadcaster;
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly WorktreeMaintenanceService _wtMaintenance;
    private readonly TaskResetService _resetService;
    private readonly TaskMergeService _mergeService;

Update the constructor. Find:

    public WorkerHub(
        QueueService queue,
        AgentFileService agentService,
        HubBroadcaster broadcaster,
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        WorktreeMaintenanceService wtMaintenance,
        TaskResetService resetService,
        TaskMergeService mergeService)
    {
        _queue = queue;
        _agentService = agentService;
        _broadcaster = broadcaster;
        _dbFactory = dbFactory;
        _wtMaintenance = wtMaintenance;
        _resetService = resetService;
        _mergeService = mergeService;
    }

Replace with:

    public WorkerHub(
        QueueService queue,
        AgentFileService agentService,
        DefaultAgentSeeder seeder,
        HubBroadcaster broadcaster,
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        WorktreeMaintenanceService wtMaintenance,
        TaskResetService resetService,
        TaskMergeService mergeService)
    {
        _queue = queue;
        _agentService = agentService;
        _seeder = seeder;
        _broadcaster = broadcaster;
        _dbFactory = dbFactory;
        _wtMaintenance = wtMaintenance;
        _resetService = resetService;
        _mergeService = mergeService;
    }

Then add the hub method. After the existing RefreshAgents method (currently line 126):

    public async Task<SeedResultDto> RestoreDefaultAgents()
    {
        var result = await _seeder.SeedMissingAsync();
        return new SeedResultDto(result.Copied, result.Skipped);
    }
  • Step 4: Build worker to verify compile

Run: dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj Expected: build succeeds.

  • Step 5: Run all Worker tests to confirm no regressions

Run: dotnet test tests/ClaudeDo.Worker.Tests Expected: all tests pass.

  • Step 6: Commit
git add src/ClaudeDo.Worker/Hub/WorkerHub.cs tests/ClaudeDo.Worker.Tests/Hub/AgentSettingsHubTests.cs
git commit -m "feat(worker): expose RestoreDefaultAgents hub method"

Task 7: Add RestoreDefaultAgentsAsync to WorkerClient

Files:

  • Modify: src/ClaudeDo.Ui/Services/WorkerClient.cs

  • Step 1: Add the DTO record

At the bottom of src/ClaudeDo.Ui/Services/WorkerClient.cs, alongside the other public record declarations (after ListConfigDto, currently line 350), add:

public sealed record SeedResultDto(int Copied, int Skipped);
  • Step 2: Add the client method

In the WorkerClient class, after the RefreshAgentsAsync method (currently line 232), add:

    public async Task<SeedResultDto?> RestoreDefaultAgentsAsync()
    {
        try
        {
            return await _hub.InvokeAsync<SeedResultDto>("RestoreDefaultAgents");
        }
        catch
        {
            return null;
        }
    }
  • Step 3: Build UI to verify compile

Run: dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj Expected: build succeeds.

  • Step 4: Commit
git add src/ClaudeDo.Ui/Services/WorkerClient.cs
git commit -m "feat(ui): add RestoreDefaultAgentsAsync to WorkerClient"

Task 8: Add restore button to Settings modal

Files:

  • Modify: src/ClaudeDo.Ui/ViewModels/Modals/SettingsModalViewModel.cs

  • Modify: src/ClaudeDo.Ui/Views/Modals/SettingsModalView.axaml

  • Step 1: Add the command to the viewmodel

In src/ClaudeDo.Ui/ViewModels/Modals/SettingsModalViewModel.cs, add a new command method. Place it after the existing ConfirmResetAll method (currently ending line 162), before the OpenPath method:

    [RelayCommand]
    private async Task RestoreDefaultAgents()
    {
        IsBusy = true;
        StatusMessage = "";
        try
        {
            var result = await _worker.RestoreDefaultAgentsAsync();
            if (result is null)
                StatusMessage = "Worker offline.";
            else if (result.Copied == 0 && result.Skipped == 0)
                StatusMessage = "No default agents bundled.";
            else if (result.Copied == 0)
                StatusMessage = "All default agents already present.";
            else
                StatusMessage = $"Restored {result.Copied} default agent(s).";

            await _worker.RefreshAgentsAsync();
        }
        catch (Exception ex)
        {
            StatusMessage = $"Restore failed: {ex.Message}";
        }
        finally { IsBusy = false; }
    }
  • Step 2: Add a button to the settings view

Edit src/ClaudeDo.Ui/Views/Modals/SettingsModalView.axaml. Add a new section after the WORKTREES StackPanel block (which ends with </StackPanel> around line 185) and before the ABOUT section (<!-- ABOUT --> around line 187).

Find:

          </StackPanel>

          <!-- ABOUT -->

Replace with:

          </StackPanel>

          <!-- AGENTS -->
          <StackPanel Spacing="0">
            <TextBlock Classes="section-label" Text="AGENTS"/>
            <Border Classes="section">
              <StackPanel Spacing="8">
                <TextBlock Text="Restore bundled default agents (code-reviewer, test-writer, debugger, security-reviewer, explorer, researcher). Existing files are not overwritten."
                           FontSize="11"
                           TextWrapping="Wrap"
                           Foreground="{DynamicResource TextDimBrush}"/>
                <Button Content="Restore default agents"
                        Command="{Binding RestoreDefaultAgentsCommand}"
                        IsEnabled="{Binding !IsBusy}"
                        HorizontalAlignment="Left"/>
              </StackPanel>
            </Border>
          </StackPanel>

          <!-- ABOUT -->
  • Step 3: Build UI to verify compile

Run: dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj Expected: build succeeds.

  • Step 4: Manual smoke test

Start the worker and the UI. Open Settings (3-dots next to username). The AGENTS section should appear with a "Restore default agents" button.

  1. With ~/.todo-app/agents/ empty (delete any existing .md files first, back them up if needed): click the button. Status should read "Restored N default agent(s)." The files should appear in the folder.
  2. Click again. Status should read "All default agents already present."
  3. Modify one of the restored files. Click restore. The modified file content should be preserved.

If any step fails, stop and fix before committing.

  • Step 5: Commit
git add src/ClaudeDo.Ui/ViewModels/Modals/SettingsModalViewModel.cs src/ClaudeDo.Ui/Views/Modals/SettingsModalView.axaml
git commit -m "feat(ui): add Restore default agents button to Settings modal"

Verification

At the end, run the full test suite and build all projects:

dotnet build src/ClaudeDo.Worker/ClaudeDo.Worker.csproj
dotnet build src/ClaudeDo.Ui/ClaudeDo.Ui.csproj
dotnet build src/ClaudeDo.Data/ClaudeDo.Data.csproj
dotnet test tests/ClaudeDo.Worker.Tests

Expected: all builds succeed, all tests pass.

Additionally, start the Worker once with an empty ~/.todo-app/agents/ folder and confirm the log line:

Default agents seeded: 6 copied, 0 already present

Then confirm ~/.todo-app/agents/ contains all 6 markdown files.