feat(agentic): add scan CLI command
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
b20978f8d3
commit
0d05ccac55
2 changed files with 97 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
|||
c.Command("run/orchestrator", core.Command{Description: "Run the queue orchestrator (standalone, no MCP)", Action: s.cmdOrchestrator})
|
||||
c.Command("prep", core.Command{Description: "Prepare a workspace: clone repo, build prompt", Action: s.cmdPrep})
|
||||
c.Command("generate", core.Command{Description: "Generate content from a prompt using the platform content pipeline", Action: s.cmdGenerate})
|
||||
c.Command("scan", core.Command{Description: "Scan Forge repos for actionable issues", Action: s.cmdScan})
|
||||
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("plan-cleanup", core.Command{Description: "Permanently delete archived plans past the retention period", Action: s.cmdPlanCleanup})
|
||||
|
|
@ -205,6 +206,36 @@ func (s *PrepSubsystem) cmdGenerate(options core.Options) core.Result {
|
|||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdScan(options core.Options) core.Result {
|
||||
result := s.handleScan(s.commandContext(), core.NewOptions(
|
||||
core.Option{Key: "org", Value: optionStringValue(options, "org")},
|
||||
core.Option{Key: "labels", Value: optionStringSliceValue(options, "labels")},
|
||||
core.Option{Key: "limit", Value: optionIntValue(options, "limit")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdScan", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(ScanOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdScan", "invalid scan output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "count: %d", output.Count)
|
||||
for _, issue := range output.Issues {
|
||||
if len(issue.Labels) > 0 {
|
||||
core.Print(nil, " %s#%d %s [%s]", issue.Repo, issue.Number, issue.Title, core.Join(",", issue.Labels...))
|
||||
continue
|
||||
}
|
||||
core.Print(nil, " %s#%d %s", issue.Repo, issue.Number, issue.Title)
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdStatus(_ core.Options) core.Result {
|
||||
workspaceRoot := WorkspaceRoot()
|
||||
filesystem := s.Core().Fs()
|
||||
|
|
|
|||
|
|
@ -754,6 +754,71 @@ func TestCommands_CmdGenerate_Good_BriefTemplate(t *testing.T) {
|
|||
assert.Contains(t, output, "content: Template draft")
|
||||
}
|
||||
|
||||
func TestCommands_CmdScan_Good(t *testing.T) {
|
||||
server := mockScanServer(t)
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
forge: forge.NewForge(server.URL, "secret-token"),
|
||||
forgeURL: server.URL,
|
||||
forgeToken: "secret-token",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
output := captureStdout(t, func() {
|
||||
r := s.cmdScan(core.NewOptions(
|
||||
core.Option{Key: "org", Value: "core"},
|
||||
core.Option{Key: "labels", Value: "agentic,bug"},
|
||||
core.Option{Key: "limit", Value: 5},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
|
||||
assert.Contains(t, output, "count:")
|
||||
assert.Contains(t, output, "go-io#10")
|
||||
assert.Contains(t, output, "Add missing tests")
|
||||
}
|
||||
|
||||
func TestCommands_CmdScan_Bad_NoForgeToken(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
s.forgeToken = ""
|
||||
|
||||
r := s.cmdScan(core.NewOptions())
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestCommands_CmdScan_Ugly_EmptyResults(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.URL.Path == "/api/v1/orgs/core/repos":
|
||||
_, _ = w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
||||
{"name": "go-io"},
|
||||
})))
|
||||
default:
|
||||
_, _ = w.Write([]byte(core.JSONMarshalString([]map[string]any{})))
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
s := &PrepSubsystem{
|
||||
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
||||
forge: forge.NewForge(server.URL, "secret-token"),
|
||||
forgeURL: server.URL,
|
||||
forgeToken: "secret-token",
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
output := captureStdout(t, func() {
|
||||
r := s.cmdScan(core.NewOptions(
|
||||
core.Option{Key: "org", Value: "core"},
|
||||
core.Option{Key: "limit", Value: 1},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
|
||||
assert.Contains(t, output, "count: 0")
|
||||
}
|
||||
|
||||
func TestCommands_CmdPlanCreate_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
|
||||
|
|
@ -867,6 +932,7 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
|
|||
assert.Contains(t, cmds, "run/task")
|
||||
assert.Contains(t, cmds, "run/orchestrator")
|
||||
assert.Contains(t, cmds, "prep")
|
||||
assert.Contains(t, cmds, "scan")
|
||||
assert.Contains(t, cmds, "status")
|
||||
assert.Contains(t, cmds, "prompt")
|
||||
assert.Contains(t, cmds, "extract")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue