2026-03-17 17:45:04 +00:00
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"os"
"os/exec"
"strings"
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_mirror tool ---
// MirrorInput is the input for agentic_mirror.
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.MirrorInput{Repo: "go-io", DryRun: true, MaxFiles: 50}
2026-03-17 17:45:04 +00:00
type MirrorInput 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
Repo string ` json:"repo,omitempty" ` // Specific repo, or empty for all
DryRun bool ` json:"dry_run,omitempty" ` // Preview without pushing
2026-03-17 17:45:04 +00:00
MaxFiles int ` json:"max_files,omitempty" ` // Max files per PR (default 50, CodeRabbit limit)
}
// MirrorOutput is the output for agentic_mirror.
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.MirrorOutput{Success: true, Count: 1, Synced: []agentic.MirrorSync{{Repo: "go-io"}}}
2026-03-17 17:45:04 +00:00
type MirrorOutput struct {
Success bool ` json:"success" `
Synced [ ] MirrorSync ` json:"synced" `
Skipped [ ] string ` json:"skipped,omitempty" `
Count int ` json:"count" `
}
// MirrorSync records one repo sync.
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
//
// sync := agentic.MirrorSync{Repo: "go-io", CommitsAhead: 3, FilesChanged: 12}
2026-03-17 17:45:04 +00:00
type MirrorSync struct {
Repo string ` json:"repo" `
CommitsAhead int ` json:"commits_ahead" `
FilesChanged int ` json:"files_changed" `
PRURL string ` json:"pr_url,omitempty" `
Pushed bool ` json:"pushed" `
Skipped string ` json:"skipped,omitempty" `
}
func ( s * PrepSubsystem ) registerMirrorTool ( server * mcp . Server ) {
mcp . AddTool ( server , & mcp . Tool {
Name : "agentic_mirror" ,
Description : "Sync Forge repos to GitHub mirrors. Pushes Forge main to GitHub dev branch and creates a PR. Respects file count limits for CodeRabbit review." ,
} , s . mirror )
}
func ( s * PrepSubsystem ) mirror ( ctx context . Context , _ * mcp . CallToolRequest , input MirrorInput ) ( * mcp . CallToolResult , MirrorOutput , error ) {
maxFiles := input . MaxFiles
if maxFiles <= 0 {
maxFiles = 50
}
basePath := s . codePath
if basePath == "" {
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
basePath = core . JoinPath ( core . Env ( "DIR_HOME" ) , "Code" , "core" )
2026-03-17 17:45:04 +00:00
} else {
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 ( basePath , "core" )
2026-03-17 17:45:04 +00:00
}
// Build list of repos to sync
var repos [ ] string
if input . Repo != "" {
repos = [ ] string { input . Repo }
} else {
repos = s . listLocalRepos ( basePath )
}
var synced [ ] MirrorSync
var skipped [ ] string
for _ , repo := range repos {
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-17 17:45:04 +00:00
// Check if github remote exists
if ! hasRemote ( repoDir , "github" ) {
skipped = append ( skipped , repo + ": no github remote" )
continue
}
// Fetch github to get current state
fetchCmd := exec . CommandContext ( ctx , "git" , "fetch" , "github" )
fetchCmd . Dir = repoDir
fetchCmd . Run ( )
2026-03-21 17:57:03 +00:00
// Check how far ahead local default branch is vs github
2026-03-22 03:41:07 +00:00
localBase := DefaultBranch ( repoDir )
2026-03-21 17:57:03 +00:00
ahead := commitsAhead ( repoDir , "github/main" , localBase )
2026-03-17 17:45:04 +00:00
if ahead == 0 {
continue // Already in sync
}
// Count files changed
2026-03-21 17:57:03 +00:00
files := filesChanged ( repoDir , "github/main" , localBase )
2026-03-17 17:45:04 +00:00
sync := MirrorSync {
Repo : repo ,
CommitsAhead : ahead ,
FilesChanged : files ,
}
// Skip if too many files for one PR
if files > maxFiles {
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
sync . Skipped = core . Sprintf ( "%d files exceeds limit of %d" , files , maxFiles )
2026-03-17 17:45:04 +00:00
synced = append ( synced , sync )
continue
}
if input . DryRun {
sync . Skipped = "dry run"
synced = append ( synced , sync )
continue
}
// Ensure dev branch exists on GitHub
ensureDevBranch ( repoDir )
2026-03-21 17:57:03 +00:00
// Push local main to github dev (explicit main, not HEAD)
2026-03-22 03:41:07 +00:00
base := DefaultBranch ( repoDir )
2026-03-21 17:57:03 +00:00
pushCmd := exec . CommandContext ( ctx , "git" , "push" , "github" , base + ":refs/heads/dev" , "--force" )
2026-03-17 17:45:04 +00:00
pushCmd . Dir = repoDir
if err := pushCmd . Run ( ) ; err != nil {
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
sync . Skipped = core . Sprintf ( "push failed: %v" , err )
2026-03-17 17:45:04 +00:00
synced = append ( synced , sync )
continue
}
sync . Pushed = true
// Create PR: dev → main on GitHub
prURL , err := s . createGitHubPR ( ctx , repoDir , repo , ahead , files )
if err != nil {
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
sync . Skipped = core . Sprintf ( "PR creation failed: %v" , err )
2026-03-17 17:45:04 +00:00
} else {
sync . PRURL = prURL
}
synced = append ( synced , sync )
}
return nil , MirrorOutput {
Success : true ,
Synced : synced ,
Skipped : skipped ,
Count : len ( synced ) ,
} , nil
}
// createGitHubPR creates a PR from dev → main using the gh CLI.
func ( s * PrepSubsystem ) createGitHubPR ( ctx context . Context , repoDir , repo string , commits , files int ) ( string , error ) {
// Check if there's already an open PR from dev
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
ghRepo := core . Sprintf ( "%s/%s" , GitHubOrg ( ) , repo )
2026-03-21 16:22:18 +00:00
checkCmd := exec . CommandContext ( ctx , "gh" , "pr" , "list" , "--repo" , ghRepo , "--head" , "dev" , "--state" , "open" , "--json" , "url" , "--limit" , "1" )
2026-03-17 17:45:04 +00:00
checkCmd . Dir = repoDir
out , err := checkCmd . 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
if err == nil && core . Contains ( string ( out ) , "url" ) {
2026-03-17 17:45:04 +00:00
// PR already exists — extract URL
// Format: [{"url":"https://..."}]
url := extractJSONField ( string ( out ) , "url" )
if url != "" {
return url , nil
}
}
// Build PR body
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
body := core . Sprintf ( "## Forge → GitHub Sync\n\n" +
2026-03-17 17:45:04 +00:00
"**Commits:** %d\n" +
"**Files changed:** %d\n\n" +
"Automated sync from Forge (forge.lthn.ai) to GitHub mirror.\n" +
"Review with CodeRabbit before merging.\n\n" +
"---\n" +
"Co-Authored-By: Virgil <virgil@lethean.io>" ,
commits , files )
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
title := core . Sprintf ( "[sync] %s: %d commits, %d files" , repo , commits , files )
2026-03-17 17:45:04 +00:00
prCmd := exec . CommandContext ( ctx , "gh" , "pr" , "create" ,
2026-03-21 16:22:18 +00:00
"--repo" , ghRepo ,
2026-03-17 17:45:04 +00:00
"--head" , "dev" ,
"--base" , "main" ,
"--title" , title ,
"--body" , body ,
)
prCmd . Dir = repoDir
prOut , err := prCmd . CombinedOutput ( )
if err != nil {
2026-03-22 03:41:07 +00:00
return "" , core . E ( "createGitHubPR" , string ( prOut ) , err )
2026-03-17 17:45:04 +00:00
}
// gh pr create outputs the PR URL on the last line
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
lines := core . Split ( core . Trim ( string ( prOut ) ) , "\n" )
2026-03-17 17:45:04 +00:00
if len ( lines ) > 0 {
return lines [ len ( lines ) - 1 ] , nil
}
return "" , nil
}
// ensureDevBranch creates the dev branch on GitHub if it doesn't exist.
func ensureDevBranch ( repoDir string ) {
// Try to push current main as dev — if dev exists this is a no-op (we force-push later)
2026-03-17 19:12:06 +00:00
cmd := exec . Command ( "git" , "push" , "github" , "HEAD:refs/heads/dev" )
2026-03-17 17:45:04 +00:00
cmd . Dir = repoDir
cmd . Run ( ) // Ignore error — branch may already exist
}
// hasRemote checks if a git remote exists.
func hasRemote ( repoDir , name string ) bool {
cmd := exec . Command ( "git" , "remote" , "get-url" , name )
cmd . Dir = repoDir
return cmd . Run ( ) == nil
}
// commitsAhead returns how many commits HEAD is ahead of the ref.
func commitsAhead ( repoDir , base , head string ) int {
cmd := exec . Command ( "git" , "rev-list" , base + ".." + head , "--count" )
cmd . Dir = repoDir
out , err := cmd . Output ( )
if err != nil {
return 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
return parseInt ( string ( out ) )
2026-03-17 17:45:04 +00:00
}
// filesChanged returns the number of files changed between two refs.
func filesChanged ( repoDir , base , head string ) int {
cmd := exec . Command ( "git" , "diff" , "--name-only" , base + ".." + head )
cmd . Dir = repoDir
out , err := cmd . Output ( )
if err != nil {
return 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
lines := core . Split ( core . Trim ( string ( out ) ) , "\n" )
2026-03-17 17:45:04 +00:00
if len ( lines ) == 1 && lines [ 0 ] == "" {
return 0
}
return len ( lines )
}
// listLocalRepos returns repo names that exist as directories in basePath.
func ( s * PrepSubsystem ) listLocalRepos ( 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 repos [ ] string
for _ , e := range entries {
if ! e . IsDir ( ) {
continue
}
// Must have a .git directory
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
if fs . IsDir ( core . JoinPath ( basePath , e . Name ( ) , ".git" ) ) {
2026-03-17 17:45:04 +00:00
repos = append ( repos , e . Name ( ) )
}
}
return repos
}
// extractJSONField extracts a simple string field from JSON array output.
func extractJSONField ( jsonStr , field string ) string {
// Quick and dirty — works for gh CLI output like [{"url":"https://..."}]
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
key := core . Sprintf ( ` "%s":" ` , field )
2026-03-17 17:45:04 +00:00
idx := strings . Index ( jsonStr , key )
if idx < 0 {
return ""
}
start := idx + len ( key )
end := strings . Index ( jsonStr [ start : ] , ` " ` )
if end < 0 {
return ""
}
return jsonStr [ start : start + end ]
}