From 3022f05fb87f73f262686276a459f2dbd98258ab Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 22 Mar 2026 09:08:45 +0000 Subject: [PATCH] refactor(agentic): route file I/O through core.Fs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace raw os.* file operations with Core Fs equivalents: - os.Stat → fs.Exists/fs.IsFile/fs.IsDir (resume, pr, plan, mirror, prep) - os.ReadDir → fs.List (queue, status, plan, mirror, review_queue) - os.Remove → fs.Delete (dispatch) - os.OpenFile(append) → fs.Append (events, review_queue) - strings.Replace → core.Replace (scan) Eliminates os import from resume.go, pr.go. Eliminates strings import from scan.go. Trades os for io in events.go. Co-Authored-By: Virgil --- pkg/agentic/dispatch.go | 2 +- pkg/agentic/events.go | 11 ++++++----- pkg/agentic/mirror.go | 7 ++++--- pkg/agentic/plan.go | 9 +++++---- pkg/agentic/pr.go | 3 +-- pkg/agentic/prep.go | 36 ++++++++++++++++++++++-------------- pkg/agentic/queue.go | 10 ++++++---- pkg/agentic/resume.go | 3 +-- pkg/agentic/review_queue.go | 16 ++++++++++------ pkg/agentic/scan.go | 3 +-- pkg/agentic/status.go | 7 ++++--- 11 files changed, 61 insertions(+), 46 deletions(-) diff --git a/pkg/agentic/dispatch.go b/pkg/agentic/dispatch.go index ef98bc5..0fd2e47 100644 --- a/pkg/agentic/dispatch.go +++ b/pkg/agentic/dispatch.go @@ -125,7 +125,7 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir, srcDir string) (int, st // Clean up stale BLOCKED.md from previous runs so it doesn't // prevent this run from completing - os.Remove(core.JoinPath(srcDir, "BLOCKED.md")) + fs.Delete(core.JoinPath(srcDir, "BLOCKED.md")) proc, err := process.StartWithOptions(context.Background(), process.RunOptions{ Command: command, diff --git a/pkg/agentic/events.go b/pkg/agentic/events.go index 4b0a66f..be02918 100644 --- a/pkg/agentic/events.go +++ b/pkg/agentic/events.go @@ -4,7 +4,7 @@ package agentic import ( "encoding/json" - "os" + "io" "time" core "dappco.re/go/core" @@ -42,10 +42,11 @@ func emitCompletionEvent(agent, workspace, status string) { } // Append to events log - f, err := os.OpenFile(eventsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { + r := fs.Append(eventsFile) + if !r.OK { return } - defer f.Close() - f.Write(append(data, '\n')) + wc := r.Value.(io.WriteCloser) + defer wc.Close() + wc.Write(append(data, '\n')) } diff --git a/pkg/agentic/mirror.go b/pkg/agentic/mirror.go index 934da95..bb57d74 100644 --- a/pkg/agentic/mirror.go +++ b/pkg/agentic/mirror.go @@ -246,17 +246,18 @@ func filesChanged(repoDir, base, head string) int { // listLocalRepos returns repo names that exist as directories in basePath. func (s *PrepSubsystem) listLocalRepos(basePath string) []string { - entries, err := os.ReadDir(basePath) - if err != nil { + r := fs.List(basePath) + if !r.OK { return nil } + entries := r.Value.([]os.DirEntry) var repos []string for _, e := range entries { if !e.IsDir() { continue } // Must have a .git directory - if _, err := os.Stat(core.JoinPath(basePath, e.Name(), ".git")); err == nil { + if fs.IsDir(core.JoinPath(basePath, e.Name(), ".git")) { repos = append(repos, e.Name()) } } diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index 2e447fb..d2fdde8 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -279,7 +279,7 @@ func (s *PrepSubsystem) planDelete(_ context.Context, _ *mcp.CallToolRequest, in } path := planPath(PlansRoot(), input.ID) - if _, err := os.Stat(path); err != nil { + if !fs.Exists(path) { return nil, PlanDeleteOutput{}, core.E("planDelete", "plan not found: "+input.ID, nil) } @@ -301,10 +301,11 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu return nil, PlanListOutput{}, core.E("planList", "failed to access plans directory", err) } - entries, err := os.ReadDir(dir) - if err != nil { - return nil, PlanListOutput{}, core.E("planList", "failed to read plans directory", err) + r := fs.List(dir) + if !r.OK { + return nil, PlanListOutput{}, nil } + entries := r.Value.([]os.DirEntry) var plans []Plan for _, entry := range entries { diff --git a/pkg/agentic/pr.go b/pkg/agentic/pr.go index a4f8d5e..89f2310 100644 --- a/pkg/agentic/pr.go +++ b/pkg/agentic/pr.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "net/http" - "os" "os/exec" core "dappco.re/go/core" @@ -58,7 +57,7 @@ func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, in wsDir := core.JoinPath(WorkspaceRoot(), input.Workspace) srcDir := core.JoinPath(wsDir, "src") - if _, err := os.Stat(srcDir); err != nil { + if !fs.IsDir(srcDir) { return nil, CreatePROutput{}, core.E("createPR", "workspace not found: "+input.Workspace, nil) } diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index 7596c26..c8dc8f8 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -250,12 +250,20 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques wsTmpl = "review" } - promptContent, _ := lib.Prompt(input.Template) + promptContent := "" + if r := lib.Prompt(input.Template); r.OK { + promptContent = r.Value.(string) + } personaContent := "" if input.Persona != "" { - personaContent, _ = lib.Persona(input.Persona) + if r := lib.Persona(input.Persona); r.OK { + personaContent = r.Value.(string) + } + } + flowContent := "" + if r := lib.Flow(detectLanguage(repoPath)); r.OK { + flowContent = r.Value.(string) } - flowContent, _ := lib.Flow(detectLanguage(repoPath)) wsData := &lib.WorkspaceData{ Repo: input.Repo, @@ -319,13 +327,13 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques // --- Prompt templates --- func (s *PrepSubsystem) writePromptTemplate(template, wsDir string) { - prompt, err := lib.Template(template) - if err != nil { - // Fallback to default template - prompt, _ = lib.Template("default") - if prompt == "" { - prompt = "Read TODO.md and complete the task. Work in src/.\n" - } + r := lib.Template(template) + if !r.OK { + r = lib.Template("default") + } + prompt := "Read TODO.md and complete the task. Work in src/.\n" + if r.OK { + prompt = r.Value.(string) } fs.Write(core.JoinPath(wsDir, "src", "PROMPT.md"), prompt) @@ -337,12 +345,12 @@ func (s *PrepSubsystem) writePromptTemplate(template, wsDir string) { // and writes PLAN.md into the workspace src/ directory. func (s *PrepSubsystem) writePlanFromTemplate(templateSlug string, variables map[string]string, task string, wsDir string) { // Load template from embedded prompts package - data, err := lib.Template(templateSlug) - if err != nil { + r := lib.Template(templateSlug) + if !r.OK { return // Template not found, skip silently } - content := data + content := r.Value.(string) // Substitute variables ({{variable_name}} → value) for key, value := range variables { @@ -649,7 +657,7 @@ func detectLanguage(repoPath string) string { {"Dockerfile", "docker"}, } for _, c := range checks { - if _, err := os.Stat(core.JoinPath(repoPath, c.file)); err == nil { + if fs.IsFile(core.JoinPath(repoPath, c.file)) { return c.lang } } diff --git a/pkg/agentic/queue.go b/pkg/agentic/queue.go index e5034ff..78a6be5 100644 --- a/pkg/agentic/queue.go +++ b/pkg/agentic/queue.go @@ -119,10 +119,11 @@ func (s *PrepSubsystem) delayForAgent(agent string) time.Duration { func (s *PrepSubsystem) countRunningByAgent(agent string) int { wsRoot := WorkspaceRoot() - entries, err := os.ReadDir(wsRoot) - if err != nil { + r := fs.List(wsRoot) + if !r.OK { return 0 } + entries := r.Value.([]os.DirEntry) count := 0 for _, entry := range entries { @@ -171,10 +172,11 @@ func (s *PrepSubsystem) drainQueue() { wsRoot := WorkspaceRoot() - entries, err := os.ReadDir(wsRoot) - if err != nil { + r := fs.List(wsRoot) + if !r.OK { return } + entries := r.Value.([]os.DirEntry) for _, entry := range entries { if !entry.IsDir() { diff --git a/pkg/agentic/resume.go b/pkg/agentic/resume.go index 0f45f1a..34195f1 100644 --- a/pkg/agentic/resume.go +++ b/pkg/agentic/resume.go @@ -4,7 +4,6 @@ package agentic import ( "context" - "os" core "dappco.re/go/core" "github.com/modelcontextprotocol/go-sdk/mcp" @@ -48,7 +47,7 @@ func (s *PrepSubsystem) resume(ctx context.Context, _ *mcp.CallToolRequest, inpu srcDir := core.JoinPath(wsDir, "src") // Verify workspace exists - if _, err := os.Stat(srcDir); err != nil { + if !fs.IsDir(srcDir) { return nil, ResumeOutput{}, core.E("resume", "workspace not found: "+input.Workspace, nil) } diff --git a/pkg/agentic/review_queue.go b/pkg/agentic/review_queue.go index 7e47232..c834092 100644 --- a/pkg/agentic/review_queue.go +++ b/pkg/agentic/review_queue.go @@ -5,6 +5,7 @@ package agentic import ( "context" "encoding/json" + "io" "os" "os/exec" "regexp" @@ -134,10 +135,11 @@ func (s *PrepSubsystem) reviewQueue(ctx context.Context, _ *mcp.CallToolRequest, // findReviewCandidates returns repos that are ahead of GitHub main. func (s *PrepSubsystem) findReviewCandidates(basePath string) []string { - entries, err := os.ReadDir(basePath) - if err != nil { + r := fs.List(basePath) + if !r.OK { return nil } + entries := r.Value.([]os.DirEntry) var candidates []string for _, e := range entries { @@ -361,11 +363,13 @@ func (s *PrepSubsystem) storeReviewOutput(repoDir, repo, reviewer, output string jsonLine, _ := json.Marshal(entry) jsonlPath := core.JoinPath(dataDir, "reviews.jsonl") - f, err := os.OpenFile(jsonlPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err == nil { - defer f.Close() - f.Write(append(jsonLine, '\n')) + r := fs.Append(jsonlPath) + if !r.OK { + return } + wc := r.Value.(io.WriteCloser) + defer wc.Close() + wc.Write(append(jsonLine, '\n')) } // saveRateLimitState persists rate limit info for cross-run awareness. diff --git a/pkg/agentic/scan.go b/pkg/agentic/scan.go index 107e0b6..91c3ee8 100644 --- a/pkg/agentic/scan.go +++ b/pkg/agentic/scan.go @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "net/http" - "strings" core "dappco.re/go/core" "github.com/modelcontextprotocol/go-sdk/mcp" @@ -197,7 +196,7 @@ func (s *PrepSubsystem) listRepoIssues(ctx context.Context, org, repo, label str Title: issue.Title, Labels: labels, Assignee: assignee, - URL: strings.Replace(issue.HTMLURL, "https://forge.lthn.ai", s.forgeURL, 1), + URL: core.Replace(issue.HTMLURL, "https://forge.lthn.ai", s.forgeURL), }) } diff --git a/pkg/agentic/status.go b/pkg/agentic/status.go index 7ea8fbf..061cbee 100644 --- a/pkg/agentic/status.go +++ b/pkg/agentic/status.go @@ -113,10 +113,11 @@ func (s *PrepSubsystem) registerStatusTool(server *mcp.Server) { func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, input StatusInput) (*mcp.CallToolResult, StatusOutput, error) { wsRoot := WorkspaceRoot() - entries, err := os.ReadDir(wsRoot) - if err != nil { - return nil, StatusOutput{}, core.E("status", "no workspaces found", err) + r := fs.List(wsRoot) + if !r.OK { + return nil, StatusOutput{}, core.E("status", "no workspaces found", nil) } + entries := r.Value.([]os.DirEntry) var workspaces []WorkspaceInfo