feat(prompts): add editable system/planning/agent prompt files

Introduces ~/.todo-app/prompts/{system,planning,agent}.md as the canonical
location for prompt content. The settings modal exposes "Open in editor"
shortcuts for each, and TaskRunner merges system.md (always) and agent.md
(for "agent"-tagged tasks) into the effective system prompt alongside the
existing global/list/task layers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-25 10:10:50 +02:00
parent 6c54759aa0
commit 7f96ae9508
4 changed files with 124 additions and 7 deletions

View File

@@ -404,14 +404,22 @@ public sealed class TaskRunner
TaskEntity task, ListConfigEntity? listConfig, string? resumeSessionId, CancellationToken ct)
{
AppSettingsEntity global;
bool isAgentTask;
using (var ctx = _dbFactory.CreateDbContext())
{
var settingsRepo = new AppSettingsRepository(ctx);
global = await settingsRepo.GetAsync(ct);
var taskRepo = new TaskRepository(ctx);
var tags = await taskRepo.GetEffectiveTagsAsync(task.Id, ct);
isAgentTask = tags.Any(t => string.Equals(t.Name, "agent", StringComparison.OrdinalIgnoreCase));
}
var systemFile = PromptFiles.ReadOrNull(PromptKind.System);
var agentFile = isAgentTask ? PromptFiles.ReadOrNull(PromptKind.Agent) : null;
var instructions = MergeInstructions(
global.DefaultClaudeInstructions, listConfig?.SystemPrompt, task.SystemPrompt);
systemFile, global.DefaultClaudeInstructions, listConfig?.SystemPrompt, task.SystemPrompt, agentFile);
return new ClaudeRunConfig(
Model: task.Model ?? listConfig?.Model ?? global.DefaultModel,
@@ -422,12 +430,11 @@ public sealed class TaskRunner
PermissionMode: global.DefaultPermissionMode);
}
public static string MergeInstructions(string? global, string? list, string? task)
public static string MergeInstructions(params string?[] parts)
{
var parts = new List<string>(3);
if (!string.IsNullOrWhiteSpace(global)) parts.Add(global.Trim());
if (!string.IsNullOrWhiteSpace(list)) parts.Add(list.Trim());
if (!string.IsNullOrWhiteSpace(task)) parts.Add(task.Trim());
return string.Join("\n\n", parts);
var trimmed = parts
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => p!.Trim());
return string.Join("\n\n", trimmed);
}
}