feat(attachments): inject reference files into the run + clean up files on delete
TaskRunner appends attached files (absolute paths) to the run prompt as the read-only Reference files section. Task and list deletes now remove the on-disk attachment dir eagerly, and a startup AttachmentOrphanRecovery sweep drops any attachments/<taskId>/ whose task no longer exists (covers list cascade and planning-discard paths).
This commit is contained in:
@@ -9,6 +9,18 @@ public sealed class AttachmentStore
|
||||
public AttachmentStore(string? root = null)
|
||||
=> _root = root ?? Paths.Expand("~/.todo-app/attachments");
|
||||
|
||||
public string Root => _root;
|
||||
|
||||
public IReadOnlyList<string> EnumerateTaskIds()
|
||||
{
|
||||
if (!Directory.Exists(_root)) return Array.Empty<string>();
|
||||
return Directory.GetDirectories(_root)
|
||||
.Select(Path.GetFileName)
|
||||
.Where(n => n is not null)
|
||||
.Select(n => n!)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public string TaskDir(string taskId)
|
||||
=> Path.Combine(_root, taskId);
|
||||
|
||||
|
||||
@@ -6,8 +6,13 @@ namespace ClaudeDo.Data.Repositories;
|
||||
public sealed class ListRepository
|
||||
{
|
||||
private readonly ClaudeDoDbContext _context;
|
||||
private readonly AttachmentStore _attachments;
|
||||
|
||||
public ListRepository(ClaudeDoDbContext context) => _context = context;
|
||||
public ListRepository(ClaudeDoDbContext context, AttachmentStore? attachments = null)
|
||||
{
|
||||
_context = context;
|
||||
_attachments = attachments ?? new AttachmentStore();
|
||||
}
|
||||
|
||||
public async Task AddAsync(ListEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
@@ -23,7 +28,13 @@ public sealed class ListRepository
|
||||
|
||||
public async Task DeleteAsync(string listId, CancellationToken ct = default)
|
||||
{
|
||||
var taskIds = await _context.Tasks
|
||||
.Where(t => t.ListId == listId)
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(ct);
|
||||
await _context.Lists.Where(l => l.Id == listId).ExecuteDeleteAsync(ct);
|
||||
foreach (var id in taskIds)
|
||||
_attachments.DeleteTaskDir(id);
|
||||
}
|
||||
|
||||
public async Task<ListEntity?> GetByIdAsync(string listId, CancellationToken ct = default)
|
||||
|
||||
@@ -7,8 +7,13 @@ namespace ClaudeDo.Data.Repositories;
|
||||
public sealed class TaskRepository
|
||||
{
|
||||
private readonly ClaudeDoDbContext _context;
|
||||
private readonly AttachmentStore _attachments;
|
||||
|
||||
public TaskRepository(ClaudeDoDbContext context) => _context = context;
|
||||
public TaskRepository(ClaudeDoDbContext context, AttachmentStore? attachments = null)
|
||||
{
|
||||
_context = context;
|
||||
_attachments = attachments ?? new AttachmentStore();
|
||||
}
|
||||
|
||||
#region CRUD
|
||||
|
||||
@@ -37,6 +42,7 @@ public sealed class TaskRepository
|
||||
public async Task DeleteAsync(string taskId, CancellationToken ct = default)
|
||||
{
|
||||
await _context.Tasks.Where(t => t.Id == taskId).ExecuteDeleteAsync(ct);
|
||||
_attachments.DeleteTaskDir(taskId);
|
||||
}
|
||||
|
||||
public async Task<TaskEntity?> GetByIdAsync(string taskId, CancellationToken ct = default)
|
||||
|
||||
Reference in New Issue
Block a user