diff --git a/.core/reference/docs/RFC.md b/.core/reference/docs/RFC.md index 457ef22..90ef3b8 100644 --- a/.core/reference/docs/RFC.md +++ b/.core/reference/docs/RFC.md @@ -434,6 +434,7 @@ func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string) ## Changelog +- 2026-03-29: cmd/core-agent no longer rewrites `os.Args` before startup. The binary-owned commands now use named handlers, keeping the entrypoint on Core CLI primitives instead of repo-local argument mutation. - 2026-03-26: WIP — net/http consolidated to transport.go (ONE file). net/url + io/fs eliminated. RFC-025 updated with 3 new quality gates (net/http, net/url, io/fs). 1:1 test + example test coverage. Array[T].Deduplicate replaces custom helpers. Remaining: remove dead `client` field from test literals, brain/provider.go Gin handler. - 2026-03-25: Quality gates pass. Zero disallowed imports (all 10). encoding/json→Core JSON. path/filepath→Core Path. os→Core Env/Fs. io→Core ReadAll/WriteAll. go-process fully Result-native. ServiceRuntime on all subsystems. 22 named Actions + Task pipeline. ChannelNotifier→IPC. Reference docs synced. - 2026-03-25: Initial spec — written with full core/go v0.8.0 domain context. diff --git a/cmd/core-agent/commands.go b/cmd/core-agent/commands.go index 2fe1eb5..6a7f475 100644 --- a/cmd/core-agent/commands.go +++ b/cmd/core-agent/commands.go @@ -1,31 +1,13 @@ +// SPDX-License-Identifier: EUPL-1.2 + package main import ( - "os" - "dappco.re/go/core" ) -// applyLogLevel scans os.Args for --quiet/-q/--debug before Core starts. -// Must run before c.Run() so ServiceStartup logs respect the level. -// -// core-agent --quiet version → errors only -// core-agent --debug mcp → full debug output -// core-agent status → default (info) -func applyLogLevel() { - var cleaned []string - cleaned = append(cleaned, os.Args[0]) - for _, arg := range os.Args[1:] { - switch arg { - case "--quiet", "-q": - core.SetLevel(core.LevelError) - case "--debug", "-d": - core.SetLevel(core.LevelDebug) - default: - cleaned = append(cleaned, arg) - } - } - os.Args = cleaned +type appCommandSet struct { + core *core.Core } // registerAppCommands adds app-level CLI commands (version, check, env). @@ -35,61 +17,69 @@ func applyLogLevel() { // core-agent check — health check // core-agent env — environment variables func registerAppCommands(c *core.Core) { + commands := appCommandSet{core: c} + c.Command("version", core.Command{ Description: "Print version and build info", - Action: func(opts core.Options) core.Result { - core.Print(nil, "core-agent %s", c.App().Version) - core.Print(nil, " go: %s", core.Env("GO")) - core.Print(nil, " os: %s/%s", core.Env("OS"), core.Env("ARCH")) - core.Print(nil, " home: %s", core.Env("DIR_HOME")) - core.Print(nil, " hostname: %s", core.Env("HOSTNAME")) - core.Print(nil, " pid: %s", core.Env("PID")) - core.Print(nil, " channel: %s", updateChannel()) - return core.Result{OK: true} - }, + Action: commands.version, }) c.Command("check", core.Command{ Description: "Verify workspace, deps, and config", - Action: func(opts core.Options) core.Result { - fs := c.Fs() - core.Print(nil, "core-agent %s health check", c.App().Version) - core.Print(nil, "") - core.Print(nil, " binary: core-agent") - - agentsPath := core.Path("Code", ".core", "agents.yaml") - if fs.IsFile(agentsPath) { - core.Print(nil, " agents: %s (ok)", agentsPath) - } else { - core.Print(nil, " agents: %s (MISSING)", agentsPath) - } - - wsRoot := core.Path("Code", ".core", "workspace") - if fs.IsDir(wsRoot) { - entries := core.PathGlob(core.JoinPath(wsRoot, "*")) - core.Print(nil, " workspace: %s (%d entries)", wsRoot, len(entries)) - } else { - core.Print(nil, " workspace: %s (MISSING)", wsRoot) - } - - core.Print(nil, " services: %d registered", len(c.Services())) - core.Print(nil, " actions: %d registered", len(c.Actions())) - core.Print(nil, " commands: %d registered", len(c.Commands())) - core.Print(nil, " env keys: %d loaded", len(core.EnvKeys())) - core.Print(nil, "") - core.Print(nil, "ok") - return core.Result{OK: true} - }, + Action: commands.check, }) c.Command("env", core.Command{ Description: "Show all core.Env() keys and values", - Action: func(opts core.Options) core.Result { - keys := core.EnvKeys() - for _, k := range keys { - core.Print(nil, " %-15s %s", k, core.Env(k)) - } - return core.Result{OK: true} - }, + Action: commands.env, }) } + +func (commands appCommandSet) version(_ core.Options) core.Result { + core.Print(nil, "core-agent %s", commands.core.App().Version) + core.Print(nil, " go: %s", core.Env("GO")) + core.Print(nil, " os: %s/%s", core.Env("OS"), core.Env("ARCH")) + core.Print(nil, " home: %s", core.Env("DIR_HOME")) + core.Print(nil, " hostname: %s", core.Env("HOSTNAME")) + core.Print(nil, " pid: %s", core.Env("PID")) + core.Print(nil, " channel: %s", updateChannel()) + return core.Result{OK: true} +} + +func (commands appCommandSet) check(_ core.Options) core.Result { + fs := commands.core.Fs() + core.Print(nil, "core-agent %s health check", commands.core.App().Version) + core.Print(nil, "") + core.Print(nil, " binary: core-agent") + + agentsPath := core.Path("Code", ".core", "agents.yaml") + if fs.IsFile(agentsPath) { + core.Print(nil, " agents: %s (ok)", agentsPath) + } else { + core.Print(nil, " agents: %s (MISSING)", agentsPath) + } + + wsRoot := core.Path("Code", ".core", "workspace") + if fs.IsDir(wsRoot) { + entries := core.PathGlob(core.JoinPath(wsRoot, "*")) + core.Print(nil, " workspace: %s (%d entries)", wsRoot, len(entries)) + } else { + core.Print(nil, " workspace: %s (MISSING)", wsRoot) + } + + core.Print(nil, " services: %d registered", len(commands.core.Services())) + core.Print(nil, " actions: %d registered", len(commands.core.Actions())) + core.Print(nil, " commands: %d registered", len(commands.core.Commands())) + core.Print(nil, " env keys: %d loaded", len(core.EnvKeys())) + core.Print(nil, "") + core.Print(nil, "ok") + return core.Result{OK: true} +} + +func (commands appCommandSet) env(_ core.Options) core.Result { + keys := core.EnvKeys() + for _, key := range keys { + core.Print(nil, " %-15s %s", key, core.Env(key)) + } + return core.Result{OK: true} +} diff --git a/cmd/core-agent/commands_test.go b/cmd/core-agent/commands_test.go index 3b96bfc..274874b 100644 --- a/cmd/core-agent/commands_test.go +++ b/cmd/core-agent/commands_test.go @@ -3,7 +3,6 @@ package main import ( - "os" "testing" "dappco.re/go/core" @@ -19,44 +18,6 @@ func newTestCore(t *testing.T) *core.Core { return c } -// --- applyLogLevel --- - -func TestCommands_ApplyLogLevel_Good(t *testing.T) { - original := os.Args - defer func() { os.Args = original }() - - os.Args = []string{"core-agent", "--quiet", "version"} - applyLogLevel() - assert.Equal(t, []string{"core-agent", "version"}, os.Args) -} - -func TestCommands_ApplyLogLevel_Good_Debug(t *testing.T) { - original := os.Args - defer func() { os.Args = original }() - - os.Args = []string{"core-agent", "-d", "check"} - applyLogLevel() - assert.Equal(t, []string{"core-agent", "check"}, os.Args) -} - -func TestCommands_ApplyLogLevel_Bad_NoFlag(t *testing.T) { - original := os.Args - defer func() { os.Args = original }() - - os.Args = []string{"core-agent", "status"} - applyLogLevel() - assert.Equal(t, []string{"core-agent", "status"}, os.Args) -} - -func TestCommands_ApplyLogLevel_Ugly_FlagAfterCommand(t *testing.T) { - original := os.Args - defer func() { os.Args = original }() - - os.Args = []string{"core-agent", "version", "-q"} - applyLogLevel() - assert.Equal(t, []string{"core-agent", "version"}, os.Args) -} - // --- registerAppCommands --- func TestCommands_RegisterAppCommands_Good(t *testing.T) { diff --git a/cmd/core-agent/main.go b/cmd/core-agent/main.go index 6eca564..6dbb69a 100644 --- a/cmd/core-agent/main.go +++ b/cmd/core-agent/main.go @@ -12,10 +12,6 @@ import ( ) func main() { - // Set log level early — before ServiceStartup to suppress startup noise. - // --quiet/-q reduces to errors only, --debug shows everything. - applyLogLevel() - c := core.New( core.WithOption("name", "core-agent"), core.WithService(agentic.ProcessRegister), diff --git a/docs/RFC.md b/docs/RFC.md index 8e8f23e..efa8239 100644 --- a/docs/RFC.md +++ b/docs/RFC.md @@ -434,6 +434,7 @@ func (s *PrepSubsystem) gitCmd(ctx context.Context, dir string, args ...string) ## Changelog +- 2026-03-29: cmd/core-agent no longer rewrites `os.Args` before startup. The binary-owned commands now use named handlers, keeping the entrypoint on Core CLI primitives instead of repo-local argument mutation. - 2026-03-29: brain/provider.go no longer imports net/http for Gin handlers. Handler responses now use named status constants and shared response helpers. HTTP remains intentionally centralised in pkg/agentic/transport.go. - 2026-03-26: WIP — net/http consolidated to transport.go (ONE file). net/url + io/fs eliminated. RFC-025 updated with 3 new quality gates (net/http, net/url, io/fs). 1:1 test + example test coverage. Array[T].Deduplicate replaces custom helpers. - 2026-03-25: Quality gates pass. Zero disallowed imports (all 10). encoding/json→Core JSON. path/filepath→Core Path. os→Core Env/Fs. io→Core ReadAll/WriteAll. go-process fully Result-native. ServiceRuntime on all subsystems. 22 named Actions + Task pipeline. ChannelNotifier→IPC. Reference docs synced.