feat: move workspace + process commands into services — main.go 98 lines

- ProcessRegister: proper factory in pkg/agentic
- Workspace commands (list/clean/dispatch): moved to agentic.registerWorkspaceCommands
- workspace.go deleted from cmd/
- main.go: 98 lines, just core.New + app commands + c.Run()

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-25 00:03:22 +00:00
parent 4b8628007d
commit 72387dde91
4 changed files with 39 additions and 30 deletions

View file

@ -4,7 +4,6 @@ import (
"os"
"dappco.re/go/core"
"dappco.re/go/core/process"
"dappco.re/go/agent/pkg/agentic"
"dappco.re/go/agent/pkg/brain"
@ -15,16 +14,7 @@ import (
func main() {
c := core.New(
core.WithOption("name", "core-agent"),
core.WithService(func(c *core.Core) core.Result {
svc, err := process.NewService(process.Options{})(c)
if err != nil {
return core.Result{Value: err, OK: false}
}
if procSvc, ok := svc.(*process.Service); ok {
_ = process.SetDefault(procSvc)
}
return core.Result{Value: svc, OK: true}
}),
core.WithService(agentic.ProcessRegister),
core.WithService(agentic.Register),
core.WithService(monitor.Register),
core.WithService(brain.Register),
@ -99,9 +89,8 @@ func main() {
},
})
// Forge + Workspace CLI commands (in separate files)
// Forge CLI commands (in forge.go — @TODO move to forge service)
registerForgeCommands(c)
registerWorkspaceCommands(c)
// registerFlowCommands(c) — on feat/flow-system branch
// Run: ServiceStartup → Cli → ServiceShutdown → os.Exit if error

View file

@ -1,22 +1,23 @@
// SPDX-License-Identifier: EUPL-1.2
package main
// Workspace CLI commands registered by the agentic service during OnStartup.
package agentic
import (
"os"
"dappco.re/go/core"
"dappco.re/go/agent/pkg/agentic"
core "dappco.re/go/core"
)
func registerWorkspaceCommands(c *core.Core) {
// registerWorkspaceCommands adds workspace management commands.
func (s *PrepSubsystem) registerWorkspaceCommands() {
c := s.core
// workspace/list — show all workspaces with status
c.Command("workspace/list", core.Command{
Description: "List all agent workspaces with status",
Action: func(opts core.Options) core.Result {
wsRoot := agentic.WorkspaceRoot()
wsRoot := WorkspaceRoot()
fsys := c.Fs()
r := fsys.List(wsRoot)
@ -33,7 +34,6 @@ func registerWorkspaceCommands(c *core.Core) {
}
statusFile := core.JoinPath(wsRoot, e.Name(), "status.json")
if sr := fsys.Read(statusFile); sr.OK {
// Quick parse for status field
content := sr.Value.(string)
status := extractField(content, "status")
repo := extractField(content, "repo")
@ -49,11 +49,10 @@ func registerWorkspaceCommands(c *core.Core) {
},
})
// workspace/clean — remove stale workspaces
c.Command("workspace/clean", core.Command{
Description: "Remove completed/failed/blocked workspaces",
Action: func(opts core.Options) core.Result {
wsRoot := agentic.WorkspaceRoot()
wsRoot := WorkspaceRoot()
fsys := c.Fs()
filter := opts.String("_arg")
if filter == "" {
@ -115,16 +114,14 @@ func registerWorkspaceCommands(c *core.Core) {
},
})
// workspace/dispatch — dispatch an agent (CLI wrapper for MCP tool)
c.Command("workspace/dispatch", core.Command{
Description: "Dispatch an agent to work on a repo task",
Action: func(opts core.Options) core.Result {
repo := opts.String("_arg")
if repo == "" {
core.Print(nil, "usage: core-agent workspace/dispatch <repo> --task=\"...\" --issue=N|--pr=N|--branch=X [--agent=codex]")
core.Print(nil, "usage: core-agent workspace dispatch <repo> --task=\"...\" --issue=N|--pr=N|--branch=X [--agent=codex]")
return core.Result{OK: false}
}
core.Print(nil, "dispatch via CLI not yet wired — use MCP agentic_dispatch tool")
core.Print(nil, "repo: %s, task: %s", repo, opts.String("task"))
return core.Result{OK: true}
@ -133,9 +130,7 @@ func registerWorkspaceCommands(c *core.Core) {
}
// extractField does a quick JSON field extraction without full unmarshal.
// Looks for "field":"value" pattern. Good enough for status.json.
func extractField(jsonStr, field string) string {
// Match both "field":"value" and "field": "value"
needle := core.Concat("\"", field, "\"")
idx := -1
for i := 0; i <= len(jsonStr)-len(needle); i++ {
@ -147,14 +142,13 @@ func extractField(jsonStr, field string) string {
if idx < 0 {
return ""
}
// Skip : and whitespace to find opening quote
for idx < len(jsonStr) && (jsonStr[idx] == ':' || jsonStr[idx] == ' ' || jsonStr[idx] == '\t') {
idx++
}
if idx >= len(jsonStr) || jsonStr[idx] != '"' {
return ""
}
idx++ // skip opening quote
idx++
end := idx
for end < len(jsonStr) && jsonStr[end] != '"' {
end++

View file

@ -91,6 +91,7 @@ func (s *PrepSubsystem) SetCore(c *core.Core) {
func (s *PrepSubsystem) OnStartup(ctx context.Context) error {
s.StartRunner()
s.registerCommands(ctx)
s.registerWorkspaceCommands()
return nil
}

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
core "dappco.re/go/core"
"dappco.re/go/core/process"
)
// ProcessRegister is the service factory for the process management service.
// Wraps core/process for the v0.3.3→v0.4 factory pattern.
//
// core.New(
// core.WithService(agentic.ProcessRegister),
// )
func ProcessRegister(c *core.Core) core.Result {
svc, err := process.NewService(process.Options{})(c)
if err != nil {
return core.Result{Value: err, OK: false}
}
if procSvc, ok := svc.(*process.Service); ok {
_ = process.SetDefault(procSvc)
}
return core.Result{Value: svc, OK: true}
}