feat(agentic): add mirror command

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 19:11:02 +00:00
parent 8fab46dcdc
commit e6593913f8
2 changed files with 48 additions and 0 deletions

View file

@ -28,6 +28,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
c.Command("generate", core.Command{Description: "Generate content from a prompt using the platform content pipeline", Action: s.cmdGenerate})
c.Command("complete", core.Command{Description: "Run the completion pipeline (QA → PR → Verify → Ingest → Poke)", Action: s.cmdComplete})
c.Command("scan", core.Command{Description: "Scan Forge repos for actionable issues", Action: s.cmdScan})
c.Command("mirror", core.Command{Description: "Mirror Forge repos to GitHub", Action: s.cmdMirror})
c.Command("brain/ingest", core.Command{Description: "Bulk ingest memories into OpenBrain", Action: s.cmdBrainIngest})
c.Command("brain/seed-memory", core.Command{Description: "Import markdown memories into OpenBrain from a project memory directory", Action: s.cmdBrainSeedMemory})
c.Command("brain/list", core.Command{Description: "List memories in OpenBrain", Action: s.cmdBrainList})
@ -313,6 +314,42 @@ func (s *PrepSubsystem) cmdScan(options core.Options) core.Result {
return core.Result{Value: output, OK: true}
}
func (s *PrepSubsystem) cmdMirror(options core.Options) core.Result {
result := s.handleMirror(s.commandContext(), core.NewOptions(
core.Option{Key: "repo", Value: optionStringValue(options, "repo", "_arg")},
core.Option{Key: "dry_run", Value: optionBoolValue(options, "dry_run", "dry-run")},
core.Option{Key: "max_files", Value: optionIntValue(options, "max_files", "max-files")},
))
if !result.OK {
err := commandResultError("agentic.cmdMirror", result)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
output, ok := result.Value.(MirrorOutput)
if !ok {
err := core.E("agentic.cmdMirror", "invalid mirror output", nil)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
core.Print(nil, "count: %d", output.Count)
for _, item := range output.Synced {
core.Print(nil, " %s commits=%d files=%d", item.Repo, item.CommitsAhead, item.FilesChanged)
if item.PRURL != "" {
core.Print(nil, " pr: %s", item.PRURL)
}
if item.Skipped != "" {
core.Print(nil, " %s", item.Skipped)
}
}
for _, skipped := range output.Skipped {
core.Print(nil, "skipped: %s", skipped)
}
return core.Result{Value: output, OK: true}
}
func (s *PrepSubsystem) cmdBrainList(options core.Options) core.Result {
result := s.Core().Action("brain.list").Run(s.commandContext(), core.NewOptions(
core.Option{Key: "project", Value: optionStringValue(options, "project")},

View file

@ -1272,6 +1272,7 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
assert.Contains(t, cmds, "prep")
assert.Contains(t, cmds, "complete")
assert.Contains(t, cmds, "scan")
assert.Contains(t, cmds, "mirror")
assert.Contains(t, cmds, "status")
assert.Contains(t, cmds, "prompt")
assert.Contains(t, cmds, "extract")
@ -1298,6 +1299,16 @@ func TestCommands_CmdPRManage_Good_NoCandidates(t *testing.T) {
assert.True(t, r.OK)
}
func TestCommands_CmdMirror_Good_NoRepos(t *testing.T) {
s, _ := testPrepWithCore(t, nil)
output := captureStdout(t, func() {
r := s.cmdMirror(core.NewOptions())
assert.True(t, r.OK)
})
assert.Contains(t, output, "count: 0")
}
// --- CmdExtract Bad/Ugly ---
func TestCommands_CmdExtract_Bad_TargetDirAlreadyHasFiles(t *testing.T) {