2026-03-17 17:45:04 +00:00
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"encoding/json"
refactor(agentic): route file I/O through core.Fs
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 <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
"io"
2026-03-17 17:45:04 +00:00
"os"
"os/exec"
"regexp"
"time"
2026-03-22 03:41:07 +00:00
core "dappco.re/go/core"
2026-03-17 17:45:04 +00:00
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// --- agentic_review_queue tool ---
// ReviewQueueInput controls the review queue runner.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
// input := agentic.ReviewQueueInput{Reviewer: "coderabbit", Limit: 4, DryRun: true}
2026-03-17 17:45:04 +00:00
type ReviewQueueInput struct {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
Limit int ` json:"limit,omitempty" ` // Max PRs to process this run (default: 4)
Reviewer string ` json:"reviewer,omitempty" ` // "coderabbit" (default), "codex", or "both"
DryRun bool ` json:"dry_run,omitempty" ` // Preview without acting
LocalOnly bool ` json:"local_only,omitempty" ` // Run review locally, don't touch GitHub
2026-03-17 17:45:04 +00:00
}
// ReviewQueueOutput reports what happened.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
// out := agentic.ReviewQueueOutput{Success: true, Processed: []agentic.ReviewResult{{Repo: "go-io", Verdict: "clean"}}}
2026-03-17 17:45:04 +00:00
type ReviewQueueOutput struct {
Success bool ` json:"success" `
Processed [ ] ReviewResult ` json:"processed" `
Skipped [ ] string ` json:"skipped,omitempty" `
RateLimit * RateLimitInfo ` json:"rate_limit,omitempty" `
}
// ReviewResult is the outcome of reviewing one repo.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
// result := agentic.ReviewResult{Repo: "go-io", Verdict: "findings", Findings: 3, Action: "fix_dispatched"}
2026-03-17 17:45:04 +00:00
type ReviewResult struct {
Repo string ` json:"repo" `
Verdict string ` json:"verdict" ` // clean, findings, rate_limited, error
Findings int ` json:"findings" ` // Number of findings (0 = clean)
Action string ` json:"action" ` // merged, fix_dispatched, skipped, waiting
Detail string ` json:"detail,omitempty" `
}
// RateLimitInfo tracks CodeRabbit rate limit state.
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
//
// limit := agentic.RateLimitInfo{Limited: true, Message: "retry after 2026-03-22T06:00:00Z"}
2026-03-17 17:45:04 +00:00
type RateLimitInfo struct {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
Limited bool ` json:"limited" `
RetryAt time . Time ` json:"retry_at,omitempty" `
Message string ` json:"message,omitempty" `
2026-03-17 17:45:04 +00:00
}
func ( s * PrepSubsystem ) registerReviewQueueTool ( server * mcp . Server ) {
mcp . AddTool ( server , & mcp . Tool {
Name : "agentic_review_queue" ,
Description : "Process the CodeRabbit review queue. Runs local CodeRabbit review on repos, auto-merges clean ones on GitHub, dispatches fix agents for findings. Respects rate limits." ,
} , s . reviewQueue )
}
func ( s * PrepSubsystem ) reviewQueue ( ctx context . Context , _ * mcp . CallToolRequest , input ReviewQueueInput ) ( * mcp . CallToolResult , ReviewQueueOutput , error ) {
limit := input . Limit
if limit <= 0 {
limit = 4
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
basePath := core . JoinPath ( s . codePath , "core" )
2026-03-17 17:45:04 +00:00
// Find repos with draft PRs (ahead of GitHub)
candidates := s . findReviewCandidates ( basePath )
if len ( candidates ) == 0 {
return nil , ReviewQueueOutput {
Success : true ,
Processed : nil ,
} , nil
}
var processed [ ] ReviewResult
var skipped [ ] string
var rateInfo * RateLimitInfo
for _ , repo := range candidates {
if len ( processed ) >= limit {
skipped = append ( skipped , repo + " (limit reached)" )
continue
}
// Check rate limit from previous run
if rateInfo != nil && rateInfo . Limited && time . Now ( ) . Before ( rateInfo . RetryAt ) {
skipped = append ( skipped , repo + " (rate limited)" )
continue
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
repoDir := core . JoinPath ( basePath , repo )
2026-03-21 16:05:59 +00:00
reviewer := input . Reviewer
if reviewer == "" {
reviewer = "coderabbit"
}
result := s . reviewRepo ( ctx , repoDir , repo , reviewer , input . DryRun , input . LocalOnly )
2026-03-17 17:45:04 +00:00
// Parse rate limit from result
if result . Verdict == "rate_limited" {
retryAfter := parseRetryAfter ( result . Detail )
rateInfo = & RateLimitInfo {
Limited : true ,
RetryAt : time . Now ( ) . Add ( retryAfter ) ,
Message : result . Detail ,
}
// Don't count rate-limited as processed — save the slot
skipped = append ( skipped , repo + " (rate limited: " + retryAfter . String ( ) + ")" )
continue
}
processed = append ( processed , result )
}
// Save rate limit state for next run
if rateInfo != nil {
s . saveRateLimitState ( rateInfo )
}
return nil , ReviewQueueOutput {
Success : true ,
Processed : processed ,
Skipped : skipped ,
RateLimit : rateInfo ,
} , nil
}
// findReviewCandidates returns repos that are ahead of GitHub main.
func ( s * PrepSubsystem ) findReviewCandidates ( basePath string ) [ ] string {
refactor(agentic): route file I/O through core.Fs
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 <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
r := fs . List ( basePath )
if ! r . OK {
2026-03-17 17:45:04 +00:00
return nil
}
refactor(agentic): route file I/O through core.Fs
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 <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
entries := r . Value . ( [ ] os . DirEntry )
2026-03-17 17:45:04 +00:00
var candidates [ ] string
for _ , e := range entries {
if ! e . IsDir ( ) {
continue
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
repoDir := core . JoinPath ( basePath , e . Name ( ) )
2026-03-17 17:45:04 +00:00
if ! hasRemote ( repoDir , "github" ) {
continue
}
ahead := commitsAhead ( repoDir , "github/main" , "HEAD" )
if ahead > 0 {
candidates = append ( candidates , e . Name ( ) )
}
}
return candidates
}
// reviewRepo runs CodeRabbit on a single repo and takes action.
2026-03-21 16:05:59 +00:00
func ( s * PrepSubsystem ) reviewRepo ( ctx context . Context , repoDir , repo , reviewer string , dryRun , localOnly bool ) ReviewResult {
2026-03-17 17:45:04 +00:00
result := ReviewResult { Repo : repo }
// Check saved rate limit
if rl := s . loadRateLimitState ( ) ; rl != nil && rl . Limited && time . Now ( ) . Before ( rl . RetryAt ) {
result . Verdict = "rate_limited"
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
result . Detail = core . Sprintf ( "retry after %s" , rl . RetryAt . Format ( time . RFC3339 ) )
2026-03-17 17:45:04 +00:00
return result
}
2026-03-21 16:05:59 +00:00
// Run reviewer CLI locally — use the reviewer passed from reviewQueue
if reviewer == "" {
reviewer = "coderabbit"
}
2026-03-17 17:45:04 +00:00
cmd := s . buildReviewCommand ( ctx , repoDir , reviewer )
out , err := cmd . CombinedOutput ( )
output := string ( out )
// Parse rate limit (both reviewers use similar patterns)
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if core . Contains ( output , "Rate limit exceeded" ) || core . Contains ( output , "rate limit" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "rate_limited"
result . Detail = output
return result
}
// Parse error
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if err != nil && ! core . Contains ( output , "No findings" ) && ! core . Contains ( output , "no issues" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "error"
result . Detail = output
return result
}
// Store raw output for training data
s . storeReviewOutput ( repoDir , repo , reviewer , output )
// Parse verdict
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if core . Contains ( output , "No findings" ) || core . Contains ( output , "no issues" ) || core . Contains ( output , "LGTM" ) {
2026-03-17 17:45:04 +00:00
result . Verdict = "clean"
result . Findings = 0
if dryRun {
result . Action = "skipped (dry run)"
return result
}
if localOnly {
result . Action = "clean (local only)"
return result
}
// Push to GitHub and mark PR ready / merge
if err := s . pushAndMerge ( ctx , repoDir , repo ) ; err != nil {
result . Action = "push failed: " + err . Error ( )
} else {
result . Action = "merged"
}
} else {
// Has findings — count them and dispatch fix agent
result . Verdict = "findings"
result . Findings = countFindings ( output )
result . Detail = truncate ( output , 500 )
if dryRun {
result . Action = "skipped (dry run)"
return result
}
// Save findings for agent dispatch
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
findingsFile := core . JoinPath ( repoDir , ".core" , "coderabbit-findings.txt" )
2026-03-22 03:41:07 +00:00
fs . Write ( findingsFile , output )
2026-03-17 17:45:04 +00:00
// Dispatch fix agent with the findings
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
task := core . Sprintf ( "Fix CodeRabbit findings. The review output is in .core/coderabbit-findings.txt. " +
2026-03-17 17:45:04 +00:00
"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 ) )
2026-03-21 17:25:23 +00:00
if err := s . dispatchFixFromQueue ( ctx , repo , task ) ; err != nil {
result . Action = "fix_dispatch_failed"
result . Detail = err . Error ( )
} else {
result . Action = "fix_dispatched"
}
2026-03-17 17:45:04 +00:00
}
return result
}
// pushAndMerge pushes to GitHub dev and merges the PR.
func ( s * PrepSubsystem ) pushAndMerge ( ctx context . Context , repoDir , repo string ) error {
// Push to dev
pushCmd := exec . CommandContext ( ctx , "git" , "push" , "github" , "HEAD:refs/heads/dev" , "--force" )
pushCmd . Dir = repoDir
if out , err := pushCmd . CombinedOutput ( ) ; err != nil {
2026-03-22 03:41:07 +00:00
return core . E ( "pushAndMerge" , "push failed: " + string ( out ) , err )
2026-03-17 17:45:04 +00:00
}
// Mark PR ready if draft
2026-03-17 19:27:44 +00:00
readyCmd := exec . CommandContext ( ctx , "gh" , "pr" , "ready" , "--repo" , GitHubOrg ( ) + "/" + repo )
2026-03-17 17:45:04 +00:00
readyCmd . Dir = repoDir
readyCmd . Run ( ) // Ignore error — might already be ready
// Try to merge
2026-03-17 19:19:04 +00:00
mergeCmd := exec . CommandContext ( ctx , "gh" , "pr" , "merge" , "--merge" , "--delete-branch" )
2026-03-17 17:45:04 +00:00
mergeCmd . Dir = repoDir
if out , err := mergeCmd . CombinedOutput ( ) ; err != nil {
2026-03-22 03:41:07 +00:00
return core . E ( "pushAndMerge" , "merge failed: " + string ( out ) , err )
2026-03-17 17:45:04 +00:00
}
return nil
}
// dispatchFixFromQueue dispatches an opus agent to fix CodeRabbit findings.
2026-03-21 17:25:23 +00:00
func ( s * PrepSubsystem ) dispatchFixFromQueue ( ctx context . Context , repo , task string ) error {
2026-03-17 17:45:04 +00:00
// Use the dispatch system — creates workspace, spawns agent
input := DispatchInput {
Repo : repo ,
Task : task ,
Agent : "claude:opus" ,
}
2026-03-21 17:25:23 +00:00
_ , out , err := s . dispatch ( ctx , nil , input )
if err != nil {
return err
}
if ! out . Success {
2026-03-22 03:41:07 +00:00
return core . E ( "dispatchFixFromQueue" , "dispatch failed for " + repo , nil )
2026-03-21 17:25:23 +00:00
}
return nil
2026-03-17 17:45:04 +00:00
}
// countFindings estimates the number of findings in CodeRabbit output.
func countFindings ( output string ) int {
// Count lines that look like findings
count := 0
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
for _ , line := range core . Split ( output , "\n" ) {
trimmed := core . Trim ( line )
if core . HasPrefix ( trimmed , "- " ) || core . HasPrefix ( trimmed , "* " ) ||
core . Contains ( trimmed , "Issue:" ) || core . Contains ( trimmed , "Finding:" ) ||
core . Contains ( trimmed , "⚠" ) || core . Contains ( trimmed , "❌" ) {
2026-03-17 17:45:04 +00:00
count ++
}
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if count == 0 && ! core . Contains ( output , "No findings" ) {
2026-03-17 17:45:04 +00:00
count = 1 // At least one finding if not clean
}
return count
}
// parseRetryAfter extracts the retry duration from a rate limit message.
// Example: "please try after 4 minutes and 56 seconds"
func parseRetryAfter ( message string ) time . Duration {
re := regexp . MustCompile ( ` (\d+)\s*minutes?\s*(?:and\s*)?(\d+)?\s*seconds? ` )
matches := re . FindStringSubmatch ( message )
if len ( matches ) >= 2 {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
mins := parseInt ( matches [ 1 ] )
2026-03-17 17:45:04 +00:00
secs := 0
if len ( matches ) >= 3 && matches [ 2 ] != "" {
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
secs = parseInt ( matches [ 2 ] )
2026-03-17 17:45:04 +00:00
}
return time . Duration ( mins ) * time . Minute + time . Duration ( secs ) * time . Second
}
// Default: 5 minutes
return 5 * time . Minute
}
// buildReviewCommand creates the CLI command for the chosen reviewer.
func ( s * PrepSubsystem ) buildReviewCommand ( ctx context . Context , repoDir , reviewer string ) * exec . Cmd {
switch reviewer {
case "codex" :
2026-03-21 16:36:26 +00:00
cmd := exec . CommandContext ( ctx , "codex" , "review" , "--base" , "github/main" )
cmd . Dir = repoDir
return cmd
2026-03-17 17:45:04 +00:00
default : // coderabbit
return exec . CommandContext ( ctx , "coderabbit" , "review" , "--plain" ,
"--base" , "github/main" , "--config" , "CLAUDE.md" , "--cwd" , repoDir )
}
}
// storeReviewOutput saves raw review output for training data collection.
func ( s * PrepSubsystem ) storeReviewOutput ( repoDir , repo , reviewer , output string ) {
refactor(agentic): adopt core.Env() + core.Path() across package
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 10:15:15 +00:00
dataDir := core . JoinPath ( core . Env ( "DIR_HOME" ) , ".core" , "training" , "reviews" )
2026-03-22 03:41:07 +00:00
fs . EnsureDir ( dataDir )
2026-03-17 17:45:04 +00:00
timestamp := time . Now ( ) . Format ( "2006-01-02T15-04-05" )
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
filename := core . Sprintf ( "%s_%s_%s.txt" , repo , reviewer , timestamp )
2026-03-17 17:45:04 +00:00
// Write raw output
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
fs . Write ( core . JoinPath ( dataDir , filename ) , output )
2026-03-17 17:45:04 +00:00
// Append to JSONL for structured training
entry := map [ string ] string {
"repo" : repo ,
"reviewer" : reviewer ,
"timestamp" : time . Now ( ) . Format ( time . RFC3339 ) ,
"output" : output ,
"verdict" : "clean" ,
}
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
if ! core . Contains ( output , "No findings" ) && ! core . Contains ( output , "no issues" ) {
2026-03-17 17:45:04 +00:00
entry [ "verdict" ] = "findings"
}
jsonLine , _ := json . Marshal ( entry )
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
jsonlPath := core . JoinPath ( dataDir , "reviews.jsonl" )
refactor(agentic): route file I/O through core.Fs
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 <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
r := fs . Append ( jsonlPath )
if ! r . OK {
return
2026-03-17 19:27:44 +00:00
}
refactor(agentic): route file I/O through core.Fs
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 <virgil@lethean.io>
2026-03-22 09:08:45 +00:00
wc := r . Value . ( io . WriteCloser )
defer wc . Close ( )
wc . Write ( append ( jsonLine , '\n' ) )
2026-03-17 17:45:04 +00:00
}
// saveRateLimitState persists rate limit info for cross-run awareness.
func ( s * PrepSubsystem ) saveRateLimitState ( info * RateLimitInfo ) {
refactor(agentic): adopt core.Env() + core.Path() across package
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 10:15:15 +00:00
path := core . JoinPath ( core . Env ( "DIR_HOME" ) , ".core" , "coderabbit-ratelimit.json" )
2026-03-17 17:45:04 +00:00
data , _ := json . Marshal ( info )
2026-03-22 03:41:07 +00:00
fs . Write ( path , string ( data ) )
2026-03-17 17:45:04 +00:00
}
// loadRateLimitState reads persisted rate limit info.
func ( s * PrepSubsystem ) loadRateLimitState ( ) * RateLimitInfo {
refactor(agentic): adopt core.Env() + core.Path() across package
Replace all os.UserHomeDir/os.Getenv/os.Hostname with core.Env().
Replace all filepath.Base/Dir/Glob/IsAbs with core.PathBase/PathDir/
PathGlob/PathIsAbs.
10 files migrated: paths, prep, review_queue, remote, dispatch,
ingest, mirror, plan, verify, watch.
Imports eliminated: 5x os, 7x filepath. All file I/O and path
construction now routes through Core primitives.
Bumps dappco.re/go/core to v0.6.0.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 10:15:15 +00:00
path := core . JoinPath ( core . Env ( "DIR_HOME" ) , ".core" , "coderabbit-ratelimit.json" )
2026-03-22 03:41:07 +00:00
r := fs . Read ( path )
if ! r . OK {
2026-03-17 17:45:04 +00:00
return nil
}
var info RateLimitInfo
2026-03-22 03:41:07 +00:00
if json . Unmarshal ( [ ] byte ( r . Value . ( string ) ) , & info ) != nil {
2026-03-17 17:45:04 +00:00
return nil
}
return & info
}