diff --git a/pkg/agentic/dispatch.go b/pkg/agentic/dispatch.go index 6469e2c..ac443d3 100644 --- a/pkg/agentic/dispatch.go +++ b/pkg/agentic/dispatch.go @@ -551,7 +551,6 @@ func (s *PrepSubsystem) dispatch(ctx context.Context, callRequest *mcp.CallToolR Runs: 1, } writeStatusResult(workspaceDir, workspaceStatus) - // Track in runner's registry (runner owns workspace state) if s.ServiceRuntime != nil { if runnerSvc, ok := core.ServiceFor[workspaceTracker](s.Core(), "runner"); ok { runnerSvc.TrackWorkspace(WorkspaceName(workspaceDir), workspaceStatus) diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index 1293e7f..d2482fe 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -296,7 +296,6 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu continue } - // Apply filters if input.Status != "" && plan.Status != input.Status { continue } diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index dc31a0a..ef600f4 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -257,19 +257,19 @@ func (s *PrepSubsystem) Shutdown(_ context.Context) error { return nil } // input := agentic.PrepInput{Repo: "go-io", Issue: 15, Task: "Migrate to Core primitives"} type PrepInput struct { - Repo string `json:"repo"` // required: e.g. "go-io" - Org string `json:"org,omitempty"` // default "core" - Task string `json:"task,omitempty"` // task description - Agent string `json:"agent,omitempty"` // agent type - Issue int `json:"issue,omitempty"` // Forge issue → workspace: task-{num}/ - PR int `json:"pr,omitempty"` // PR number → workspace: pr-{num}/ - Branch string `json:"branch,omitempty"` // branch → workspace: {branch}/ - Tag string `json:"tag,omitempty"` // tag → workspace: {tag}/ (immutable) - Template string `json:"template,omitempty"` // prompt template slug - PlanTemplate string `json:"plan_template,omitempty"` // plan template slug - Variables map[string]string `json:"variables,omitempty"` // template variable substitution - Persona string `json:"persona,omitempty"` // persona slug - DryRun bool `json:"dry_run,omitempty"` // preview without executing + Repo string `json:"repo"` + Org string `json:"org,omitempty"` + Task string `json:"task,omitempty"` + Agent string `json:"agent,omitempty"` + Issue int `json:"issue,omitempty"` + PR int `json:"pr,omitempty"` + Branch string `json:"branch,omitempty"` + Tag string `json:"tag,omitempty"` + Template string `json:"template,omitempty"` + PlanTemplate string `json:"plan_template,omitempty"` + Variables map[string]string `json:"variables,omitempty"` + Persona string `json:"persona,omitempty"` + DryRun bool `json:"dry_run,omitempty"` } // out := agentic.PrepOutput{Success: true, WorkspaceDir: ".core/workspace/core/go-io/task-15"} @@ -343,7 +343,6 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques input.Template = "coding" } - // Resolve workspace directory from identifier workspaceResult := workspaceDirResult(input.Org, input.Repo, input) if !workspaceResult.OK { err, _ := workspaceResult.Value.(error) @@ -361,23 +360,17 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques metaDir := workspaceMetaDir(workspaceDir) out := PrepOutput{WorkspaceDir: workspaceDir, RepoDir: repoDir} - // Source repo path — org and repo were validated by workspaceDirResult. repoPath := core.JoinPath(s.codePath, input.Org, input.Repo) process := s.Core().Process() - // Ensure meta directory exists if r := fs.EnsureDir(metaDir); !r.OK { return nil, PrepOutput{}, core.E("prep", "failed to create meta dir", nil) } - // Check for resume: if repo/ already has .git, pull latest instead of re-cloning resumed := fs.IsDir(core.JoinPath(repoDir, ".git")) out.Resumed = resumed if resumed { - // Preserve the current branch on resume. Pull it only if it exists on - // origin; otherwise refresh the default branch refs without abandoning the - // workspace branch. r := process.RunIn(ctx, repoDir, "git", "rev-parse", "--abbrev-ref", "HEAD") currentBranch := "" if r.OK { @@ -397,7 +390,6 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } } - // Extract default workspace template (go.work etc.) if result := lib.ExtractWorkspace("default", workspaceDir, &lib.WorkspaceData{ Repo: input.Repo, Branch: "", @@ -411,12 +403,10 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } if !resumed { - // Clone repo into repo/ if r := process.RunIn(ctx, ".", "git", "clone", repoPath, repoDir); !r.OK { return nil, PrepOutput{}, core.E("prep", core.Concat("git clone failed for ", input.Repo), nil) } - // Create feature branch taskSlug := sanitiseBranchSlug(input.Task, 40) if taskSlug == "" { if input.Issue > 0 { @@ -434,15 +424,12 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } out.Branch = branchName } else { - // Resume: read branch from existing checkout r := process.RunIn(ctx, repoDir, "git", "rev-parse", "--abbrev-ref", "HEAD") if r.OK { out.Branch = core.Trim(r.Value.(string)) } } - // Overwrite CODEX.md with language-specific version if needed. - // The default template is Go-focused. PHP repos get CODEX-PHP.md instead. lang := detectLanguage(repoPath) if lang == "php" { if r := lib.WorkspaceFile("default", "CODEX-PHP.md.tmpl"); r.OK { @@ -451,13 +438,8 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } } - // Clone workspace dependencies — Core modules needed to build the repo. - // Reads go.mod, finds dappco.re/go/core/* imports, clones from Forge, - // and updates go.work so the agent can build inside the workspace. s.cloneWorkspaceDeps(ctx, workspaceDir, repoDir, input.Org) - // Clone ecosystem docs into .core/reference/ so agents have full documentation. - // The docs site (core.help) has architecture guides, specs, and API references. docsDir := core.JoinPath(workspaceDir, ".core", "reference", "docs") if !fs.IsDir(docsDir) { docsRepo := core.JoinPath(s.codePath, input.Org, "docs") @@ -466,11 +448,8 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } } - // Copy RFC specs from plans repo into workspace specs/ folder. - // Maps repo name to plans directory: go-io → core/go/io/, go-process → core/go/process/, etc. s.copyRepoSpecs(workspaceDir, input.Repo) - // Build the rich prompt with all context out.Prompt, out.Memories, out.Consumers = s.buildPrompt(ctx, input, out.Branch, repoPath) out.Success = true @@ -482,27 +461,22 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques func (s *PrepSubsystem) copyRepoSpecs(workspaceDir, repo string) { fs := (&core.Fs{}).NewUnrestricted() - // Plans repo base — look for it relative to codePath plansBase := core.JoinPath(s.codePath, "host-uk", "core", "plans") if !fs.IsDir(plansBase) { return } - // Map repo name to plans directory var specDir string switch { case core.HasPrefix(repo, "go-"): - // go-io → core/go/io, go-process → core/go/process pkg := core.TrimPrefix(repo, "go-") specDir = core.JoinPath(plansBase, "core", "go", pkg) case core.HasPrefix(repo, "core-"): - // core-bio → core/php/bio, core-social → core/php/social mod := core.TrimPrefix(repo, "core-") specDir = core.JoinPath(plansBase, "core", "php", mod) case repo == "go": specDir = core.JoinPath(plansBase, "core", "go") default: - // agent → core/agent, mcp → core/mcp, cli → core/go/cli, ide → core/ide specDir = core.JoinPath(plansBase, "core", repo) } @@ -510,8 +484,6 @@ func (s *PrepSubsystem) copyRepoSpecs(workspaceDir, repo string) { return } - // Glob RFC*.md at each depth level (root, 1 deep, 2 deep, 3 deep). - // Preserves subdirectory structure: specDir/pkg/nested/RFC.md → specs/pkg/nested/RFC.md specsDir := core.JoinPath(workspaceDir, "specs") fs.EnsureDir(specsDir) @@ -550,18 +522,15 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch memories := 0 consumers := 0 - // Task b.WriteString("TASK: ") b.WriteString(input.Task) b.WriteString("\n\n") - // Repo info b.WriteString(core.Sprintf("REPO: %s/%s on branch %s\n", input.Org, input.Repo, branch)) b.WriteString(core.Sprintf("LANGUAGE: %s\n", detectLanguage(repoPath))) b.WriteString(core.Sprintf("BUILD: %s\n", detectBuildCmd(repoPath))) b.WriteString(core.Sprintf("TEST: %s\n\n", detectTestCmd(repoPath))) - // Persona if input.Persona != "" { if r := lib.Persona(input.Persona); r.OK { b.WriteString("PERSONA:\n") @@ -570,14 +539,12 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch } } - // Flow if r := lib.Flow(detectLanguage(repoPath)); r.OK { b.WriteString("WORKFLOW:\n") b.WriteString(r.Value.(string)) b.WriteString("\n\n") } - // Issue body if input.Issue > 0 { if body := s.getIssueBody(ctx, input.Org, input.Repo, input.Issue); body != "" { b.WriteString("ISSUE:\n") @@ -586,7 +553,6 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch } } - // Brain recall if recall, count := s.brainRecall(ctx, input.Repo); recall != "" { b.WriteString("CONTEXT (from OpenBrain):\n") b.WriteString(recall) @@ -594,7 +560,6 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch memories = count } - // Consumers if list, count := s.findConsumersList(input.Repo); list != "" { b.WriteString("CONSUMERS (modules that import this repo):\n") b.WriteString(list) @@ -602,14 +567,12 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch consumers = count } - // Recent git log if log := s.getGitLog(repoPath); log != "" { b.WriteString("RECENT CHANGES:\n```\n") b.WriteString(log) b.WriteString("```\n\n") } - // Plan template if input.PlanTemplate != "" { if plan := s.renderPlan(input.PlanTemplate, input.Variables, input.Task); plan != "" { b.WriteString("PLAN:\n") @@ -618,7 +581,6 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch } } - // Constraints b.WriteString("CONSTRAINTS:\n") b.WriteString("- Read CODEX.md for coding conventions (if it exists)\n") b.WriteString("- Read CLAUDE.md for project-specific instructions (if it exists)\n") diff --git a/pkg/agentic/review_queue.go b/pkg/agentic/review_queue.go index 4b821c2..2e5fef5 100644 --- a/pkg/agentic/review_queue.go +++ b/pkg/agentic/review_queue.go @@ -210,11 +210,9 @@ func (s *PrepSubsystem) reviewRepo(ctx context.Context, repoDir, repo, reviewer return result } - // Save findings for agent dispatch findingsFile := core.JoinPath(repoDir, ".core", "coderabbit-findings.txt") fs.Write(findingsFile, output) - // Dispatch fix agent with the findings task := core.Sprintf( "Fix CodeRabbit findings. The review output is in .core/coderabbit-findings.txt. Read it, verify each finding against the code, fix what's valid. Run tests. Commit: fix(coderabbit): address review findings\n\nFindings summary (%d issues):\n%s", result.Findings, truncate(output, 1500)) @@ -302,7 +300,7 @@ func (s *PrepSubsystem) buildReviewCommand(repoDir, reviewer string) (string, [] switch reviewer { case "codex": return "codex", []string{"review", "--base", "github/main"} - default: // coderabbit + default: return "coderabbit", []string{"review", "--plain", "--base", "github/main", "--config", "CLAUDE.md", "--cwd", repoDir} } } @@ -315,10 +313,8 @@ func (s *PrepSubsystem) storeReviewOutput(repoDir, repo, reviewer, output string timestamp := time.Now().Format("2006-01-02T15-04-05") filename := core.Sprintf("%s_%s_%s.txt", repo, reviewer, timestamp) - // Write raw output fs.Write(core.JoinPath(dataDir, filename), output) - // Append to JSONL for structured training entry := map[string]string{ "repo": repo, "reviewer": reviewer, diff --git a/pkg/lib/lib.go b/pkg/lib/lib.go index c75a831..6748775 100644 --- a/pkg/lib/lib.go +++ b/pkg/lib/lib.go @@ -341,12 +341,10 @@ func listNamesRecursive(mount, dir string) []string { subPath := core.JoinPath(mount, relPath) - // Try as directory — recurse if it has contents if childNames := data.ListNames(subPath); childNames.OK { slugs = append(slugs, listNamesRecursive(mount, relPath)...) } - // Always add the slug — ListNames includes both files and dirs slugs = append(slugs, relPath) } return slugs diff --git a/pkg/runner/queue.go b/pkg/runner/queue.go index 03e80ba..38e99ad 100644 --- a/pkg/runner/queue.go +++ b/pkg/runner/queue.go @@ -228,13 +228,9 @@ func (s *Service) drainOne() bool { continue } - // Ask agentic to spawn — runner owns the gate, - // agentic owns the actual process launch. - // Workspace name is relative path from workspace root (e.g. "core/go-ai/dev") workspaceName := agentic.WorkspaceName(workspaceDir) core.Info("drainOne: found queued workspace", "workspace", workspaceName, "agent", workspaceStatus.Agent) - // Spawn directly — agentic is a Core service, use ServiceFor to get it if s.ServiceRuntime == nil { continue } @@ -258,7 +254,6 @@ func (s *Service) drainOne() bool { continue } - // Only mark running AFTER successful spawn workspaceStatus.Status = "running" workspaceStatus.PID = pid workspaceStatus.Runs++