diff --git a/cmd/core-agent/commands.go b/cmd/core-agent/commands.go index 07153c1..07ba367 100644 --- a/cmd/core-agent/commands.go +++ b/cmd/core-agent/commands.go @@ -14,10 +14,8 @@ type applicationCommandSet struct { core *core.Core } -// startupArgs applies early log flags, then returns args for c.Cli().Run(). -// -// args := startupArgs() -// _ = c.Cli().Run(args...) +// args := startupArgs() +// _ = c.Cli().Run(args...) func startupArgs() []string { previous := flag.CommandLine commandLine := flag.NewFlagSet("core-agent", flag.ContinueOnError) @@ -48,10 +46,8 @@ func startupArgs() []string { return applyLogLevel(commandLine.Args()) } -// applyLogLevel strips log-level flags from args and applies the level in-order. -// -// args := applyLogLevel([]string{"version", "-q"}) -// args := applyLogLevel([]string{"--debug", "mcp"}) +// args := applyLogLevel([]string{"version", "-q"}) +// args := applyLogLevel([]string{"--debug", "mcp"}) func applyLogLevel(args []string) []string { var cleaned []string for _, arg := range args { diff --git a/cmd/core-agent/mcp_service.go b/cmd/core-agent/mcp_service.go index ecf38f0..c225875 100644 --- a/cmd/core-agent/mcp_service.go +++ b/cmd/core-agent/mcp_service.go @@ -10,10 +10,8 @@ import ( "forge.lthn.ai/core/mcp/pkg/mcp" ) -// registerMCPService builds the MCP service from registered AX subsystems. -// -// c := core.New(core.WithService(registerMCPService)) -// _, ok := core.ServiceFor[*mcp.Service](c, "mcp") +// c := core.New(core.WithService(registerMCPService)) +// _, ok := core.ServiceFor[*mcp.Service](c, "mcp") func registerMCPService(c *core.Core) core.Result { if c == nil { return core.Result{Value: core.E("main.registerMCPService", "core is required", nil), OK: false} diff --git a/cmd/core-agent/update.go b/cmd/core-agent/update.go index 56388ee..5f6a114 100644 --- a/cmd/core-agent/update.go +++ b/cmd/core-agent/update.go @@ -4,10 +4,8 @@ package main import agentpkg "dappco.re/go/agent" -// updateChannel maps the build version to the release channel. -// -// agentpkg.Version = "0.15.0" -// updateChannel() // "stable" +// agentpkg.Version = "0.15.0" +// updateChannel() // "stable" func updateChannel() string { switch { case agentpkg.Version == "" || agentpkg.Version == "dev": diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index ba89911..c6dfa2c 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -1,7 +1,6 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package agentic provides MCP tools for agent orchestration. -// Prepares workspaces and dispatches subagents. +// core.New(core.WithService(agentic.Register)) package agentic import ( @@ -18,15 +17,10 @@ import ( "gopkg.in/yaml.v3" ) -// AgentOptions configures the agentic service. -// -// options := agentic.AgentOptions{} +// options := agentic.AgentOptions{} type AgentOptions struct{} -// PrepSubsystem provides agentic MCP tools for workspace orchestration. -// Agent lifecycle events are broadcast via s.Core().ACTION(messages.AgentCompleted{}). -// -// core.New(core.WithService(agentic.Register)) +// core.New(core.WithService(agentic.Register)) type PrepSubsystem struct { *core.ServiceRuntime[AgentOptions] forge *forge.Forge @@ -47,10 +41,8 @@ type PrepSubsystem struct { var _ coremcp.Subsystem = (*PrepSubsystem)(nil) -// NewPrep creates an agentic subsystem. -// -// subsystem := agentic.NewPrep() -// subsystem.SetCompletionNotifier(monitor) +// subsystem := agentic.NewPrep() +// subsystem.SetCompletionNotifier(monitor) func NewPrep() *PrepSubsystem { home := HomeDir() @@ -81,18 +73,13 @@ func NewPrep() *PrepSubsystem { } } -// Use core.New(core.WithService(agentic.Register)) for new code. -// -// prep.SetCore(c) +// prep.SetCore(c) func (s *PrepSubsystem) SetCore(c *core.Core) { s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{}) } -// OnStartup implements core.Startable — registers named Actions, starts the queue runner, -// and registers CLI commands. The Action registry IS the capability map. -// -// c.Action("agentic.dispatch").Run(ctx, options) -// c.Actions() // ["agentic.dispatch", "agentic.prep", "agentic.status", ...] +// c.Action("agentic.dispatch").Run(ctx, options) +// c.Actions() // ["agentic.dispatch", "agentic.prep", "agentic.status", ...] func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { c := s.Core() @@ -214,10 +201,8 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { // registerCommands is in commands.go -// OnShutdown implements core.Stoppable and freezes the queue. -// -// subsystem := agentic.NewPrep() -// _ = subsystem.OnShutdown(context.Background()) +// subsystem := agentic.NewPrep() +// _ = subsystem.OnShutdown(context.Background()) func (s *PrepSubsystem) OnShutdown(ctx context.Context) core.Result { s.frozen = true return core.Result{OK: true} @@ -243,20 +228,16 @@ func (s *PrepSubsystem) hydrateWorkspaces() { } } -// TrackWorkspace registers or updates a workspace in the in-memory Registry. -// -// s.TrackWorkspace("core/go-io/task-5", st) +// s.TrackWorkspace("core/go-io/task-5", st) func (s *PrepSubsystem) TrackWorkspace(name string, st *WorkspaceStatus) { if s.workspaces != nil { s.workspaces.Set(name, st) } } -// Workspaces returns the workspace Registry for cross-cutting queries. -// -// s.Workspaces().Names() // all workspace names -// s.Workspaces().List("core/*") // org-scoped workspaces -// s.Workspaces().Each(func(name string, st *WorkspaceStatus) { ... }) +// s.Workspaces().Names() // all workspace names +// s.Workspaces().List("core/*") // org-scoped workspaces +// s.Workspaces().Each(func(name string, st *WorkspaceStatus) { ... }) func (s *PrepSubsystem) Workspaces() *core.Registry[*WorkspaceStatus] { return s.workspaces } @@ -268,17 +249,13 @@ func envOr(key, fallback string) string { return fallback } -// Name identifies the MCP subsystem. -// -// subsystem := agentic.NewPrep() -// name := subsystem.Name() -// _ = name // "agentic" +// subsystem := agentic.NewPrep() +// name := subsystem.Name() +// _ = name // "agentic" func (s *PrepSubsystem) Name() string { return "agentic" } -// RegisterTools publishes the agentic MCP tools on the server. -// -// subsystem := agentic.NewPrep() -// subsystem.RegisterTools(server) +// subsystem := agentic.NewPrep() +// subsystem.RegisterTools(server) func (s *PrepSubsystem) RegisterTools(server *mcp.Server) { mcp.AddTool(server, &mcp.Tool{ Name: "agentic_prep_workspace", @@ -306,18 +283,13 @@ func (s *PrepSubsystem) RegisterTools(server *mcp.Server) { s.registerWatchTool(server) } -// Shutdown satisfies mcp.SubsystemWithShutdown for clean server teardown. -// -// subsystem := agentic.NewPrep() -// _ = subsystem.Shutdown(context.Background()) +// subsystem := agentic.NewPrep() +// _ = subsystem.Shutdown(context.Background()) func (s *PrepSubsystem) Shutdown(_ context.Context) error { return nil } // --- Input/Output types --- -// PrepInput is the input for agentic_prep_workspace. -// One of Issue, PR, Branch, or Tag is required. -// -// input := agentic.PrepInput{Repo: "go-io", Issue: 15, Task: "Migrate to Core primitives"} +// input := agentic.PrepInput{Repo: "go-io", Issue: 15, Task: "Migrate to Core primitives"} type PrepInput struct { Repo string `json:"repo"` // required: e.g. "go-io" Org string `json:"org,omitempty"` // default "core" @@ -334,9 +306,7 @@ type PrepInput struct { DryRun bool `json:"dry_run,omitempty"` // preview without executing } -// PrepOutput is the output for agentic_prep_workspace. -// -// out := agentic.PrepOutput{Success: true, WorkspaceDir: ".core/workspace/core/go-io/task-15"} +// out := agentic.PrepOutput{Success: true, WorkspaceDir: ".core/workspace/core/go-io/task-15"} type PrepOutput struct { Success bool `json:"success"` WorkspaceDir string `json:"workspace_dir"` @@ -348,10 +318,8 @@ type PrepOutput struct { Resumed bool `json:"resumed"` } -// workspaceDir resolves the workspace path from the input identifier. -// -// dir := workspaceDir("core", "go-io", PrepInput{Issue: 15}) -// // → ".core/workspace/core/go-io/task-15" +// dir := workspaceDir("core", "go-io", PrepInput{Issue: 15}) +// dir == ".core/workspace/core/go-io/task-15" func workspaceDir(org, repo string, input PrepInput) (string, error) { r := workspaceDirResult(org, repo, input) if !r.OK { @@ -368,10 +336,8 @@ func workspaceDir(org, repo string, input PrepInput) (string, error) { return workspaceDir, nil } -// workspaceDirResult resolves the workspace path and returns core.Result. -// -// r := workspaceDirResult("core", "go-io", PrepInput{Issue: 15}) -// if r.OK { workspaceDir := r.Value.(string) } +// r := workspaceDirResult("core", "go-io", PrepInput{Issue: 15}) +// if r.OK { workspaceDir := r.Value.(string) } func workspaceDirResult(org, repo string, input PrepInput) core.Result { orgName := core.ValidateName(org) if !orgName.OK { diff --git a/pkg/agentic/queue.go b/pkg/agentic/queue.go index e44bd20..48f2688 100644 --- a/pkg/agentic/queue.go +++ b/pkg/agentic/queue.go @@ -10,18 +10,14 @@ import ( "gopkg.in/yaml.v3" ) -// DispatchConfig controls agent dispatch behaviour. -// -// config := agentic.DispatchConfig{DefaultAgent: "claude", DefaultTemplate: "coding"} +// config := agentic.DispatchConfig{DefaultAgent: "claude", DefaultTemplate: "coding"} type DispatchConfig struct { DefaultAgent string `yaml:"default_agent"` DefaultTemplate string `yaml:"default_template"` WorkspaceRoot string `yaml:"workspace_root"` } -// RateConfig controls pacing between task dispatches. -// -// rate := agentic.RateConfig{ResetUTC: "06:00", SustainedDelay: 120, BurstWindow: 2, BurstDelay: 15} +// rate := agentic.RateConfig{ResetUTC: "06:00", SustainedDelay: 120, BurstWindow: 2, BurstDelay: 15} type RateConfig struct { ResetUTC string `yaml:"reset_utc"` // Daily quota reset time (UTC), e.g. "06:00" DailyLimit int `yaml:"daily_limit"` // Max requests per day (0 = unknown) @@ -31,13 +27,12 @@ type RateConfig struct { BurstDelay int `yaml:"burst_delay"` // Delay during burst window } -// ConcurrencyLimit supports both flat (int) and nested (map with total + per-model) formats. +// claude: 1 → Total=1, Models=nil +// codex: → Total=2, Models={"gpt-5.4": 1, "gpt-5.3-codex-spark": 1} // -// claude: 1 → Total=1, Models=nil -// codex: → Total=2, Models={"gpt-5.4": 1, "gpt-5.3-codex-spark": 1} -// total: 2 -// gpt-5.4: 1 -// gpt-5.3-codex-spark: 1 +// total: 2 +// gpt-5.4: 1 +// gpt-5.3-codex-spark: 1 type ConcurrencyLimit struct { Total int Models map[string]int @@ -65,9 +60,7 @@ func (c *ConcurrencyLimit) UnmarshalYAML(value *yaml.Node) error { return nil } -// AgentsConfig is the root of config/agents.yaml. -// -// config := agentic.AgentsConfig{Version: 1, Dispatch: agentic.DispatchConfig{DefaultAgent: "claude"}} +// config := agentic.AgentsConfig{Version: 1, Dispatch: agentic.DispatchConfig{DefaultAgent: "claude"}} type AgentsConfig struct { Version int `yaml:"version"` Dispatch DispatchConfig `yaml:"dispatch"` @@ -235,9 +228,7 @@ func baseAgent(agent string) string { return core.SplitN(agent, ":", 2)[0] } -// canDispatchAgent checks both pool-level and per-model concurrency limits. -// -// codex: {total: 2, models: {gpt-5.4: 1}} → max 2 codex total, max 1 gpt-5.4 +// codex: {total: 2, models: {gpt-5.4: 1}} → max 2 codex total, max 1 gpt-5.4 func (s *PrepSubsystem) canDispatchAgent(agent string) bool { var concurrency map[string]ConcurrencyLimit if s.ServiceRuntime != nil { diff --git a/pkg/agentic/status.go b/pkg/agentic/status.go index 440332e..4e9bef9 100644 --- a/pkg/agentic/status.go +++ b/pkg/agentic/status.go @@ -10,14 +10,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" ) -// statusPath := agentic.WorkspaceStatusPath("/srv/.core/workspace/core/go-io/task-5") -// blockedPath := agentic.WorkspaceBlockedPath("/srv/.core/workspace/core/go-io/task-5") -// logs := agentic.WorkspaceLogFiles("/srv/.core/workspace/core/go-io/task-5") - -// WorkspaceStatus represents the current state of an agent workspace. -// -// result := ReadStatusResult(workspaceDir) -// if result.OK && result.Value.(*WorkspaceStatus).Status == "completed" { autoCreatePR(workspaceDir) } +// result := ReadStatusResult(workspaceDir) +// if result.OK && result.Value.(*WorkspaceStatus).Status == "completed" { autoCreatePR(workspaceDir) } type WorkspaceStatus struct { Status string `json:"status"` // running, completed, blocked, failed Agent string `json:"agent"` // gemini, claude, codex @@ -35,12 +29,9 @@ type WorkspaceStatus struct { PRURL string `json:"pr_url,omitempty"` // pull request URL (after PR created) } -// WorkspaceQuery is the QUERY type for workspace state lookups. -// Returns the workspace Registry via c.QUERY(agentic.WorkspaceQuery{}). -// -// r := c.QUERY(agentic.WorkspaceQuery{}) -// if r.OK { reg := r.Value.(*core.Registry[*WorkspaceStatus]) } -// r := c.QUERY(agentic.WorkspaceQuery{Name: "core/go-io/task-5"}) +// r := c.QUERY(agentic.WorkspaceQuery{}) +// if r.OK { reg := r.Value.(*core.Registry[*WorkspaceStatus]) } +// r := c.QUERY(agentic.WorkspaceQuery{Name: "core/go-io/task-5"}) type WorkspaceQuery struct { Name string // specific workspace (empty = all) Status string // filter by status (empty = all) @@ -58,10 +49,8 @@ func writeStatus(workspaceDir string, status *WorkspaceStatus) error { return nil } -// writeStatusResult writes status.json and returns core.Result. -// -// result := writeStatusResult("/srv/core/workspace/core/go-io/task-5", &WorkspaceStatus{Status: "running"}) -// if result.OK { return } +// result := writeStatusResult("/srv/core/workspace/core/go-io/task-5", &WorkspaceStatus{Status: "running"}) +// if result.OK { return } func writeStatusResult(workspaceDir string, status *WorkspaceStatus) core.Result { if status == nil { return core.Result{Value: core.E("writeStatus", "status is required", nil), OK: false} @@ -80,10 +69,8 @@ func writeStatusResult(workspaceDir string, status *WorkspaceStatus) core.Result return core.Result{OK: true} } -// ReadStatusResult parses status.json and returns a WorkspaceStatus pointer. -// -// result := ReadStatusResult("/path/to/workspace") -// if result.OK { workspaceStatus := result.Value.(*WorkspaceStatus) } +// result := ReadStatusResult("/path/to/workspace") +// if result.OK { workspaceStatus := result.Value.(*WorkspaceStatus) } func ReadStatusResult(workspaceDir string) core.Result { r := fs.Read(WorkspaceStatusPath(workspaceDir)) if !r.OK { @@ -104,10 +91,8 @@ func ReadStatusResult(workspaceDir string) core.Result { return core.Result{Value: &s, OK: true} } -// workspaceStatusValue extracts a WorkspaceStatus from a Result. -// -// result := ReadStatusResult("/path/to/workspace") -// workspaceStatus, ok := workspaceStatusValue(result) +// result := ReadStatusResult("/path/to/workspace") +// workspaceStatus, ok := workspaceStatusValue(result) func workspaceStatusValue(result core.Result) (*WorkspaceStatus, bool) { workspaceStatus, ok := result.Value.(*WorkspaceStatus) if !ok || workspaceStatus == nil { @@ -118,19 +103,14 @@ func workspaceStatusValue(result core.Result) (*WorkspaceStatus, bool) { // --- agentic_status tool --- -// StatusInput is the input for agentic_status. -// -// input := agentic.StatusInput{Workspace: "core/go-io/task-42", Limit: 50} +// input := agentic.StatusInput{Workspace: "core/go-io/task-42", Limit: 50} type StatusInput struct { Workspace string `json:"workspace,omitempty"` // specific workspace name, or empty for all Limit int `json:"limit,omitempty"` // max results (default 100) Status string `json:"status,omitempty"` // filter: running, completed, failed, blocked } -// StatusOutput is the output for agentic_status. -// Returns stats by default. Only blocked workspaces are listed (they need attention). -// -// out := agentic.StatusOutput{Total: 42, Running: 3, Queued: 10, Completed: 25} +// out := agentic.StatusOutput{Total: 42, Running: 3, Queued: 10, Completed: 25} type StatusOutput struct { Total int `json:"total"` Running int `json:"running"` @@ -140,9 +120,7 @@ type StatusOutput struct { Blocked []BlockedInfo `json:"blocked,omitempty"` } -// BlockedInfo shows a workspace that needs human input. -// -// info := agentic.BlockedInfo{Name: "core/go-io/task-4", Repo: "go-io", Question: "Which API version?"} +// info := agentic.BlockedInfo{Name: "core/go-io/task-4", Repo: "go-io", Question: "Which API version?"} type BlockedInfo struct { Name string `json:"name"` Repo string `json:"repo"` diff --git a/pkg/brain/brain.go b/pkg/brain/brain.go index bc70662..e52705d 100644 --- a/pkg/brain/brain.go +++ b/pkg/brain/brain.go @@ -1,9 +1,7 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package brain gives MCP and HTTP services the same OpenBrain capability map. -// -// subsystem := brain.New(nil) -// core.Println(subsystem.Name()) +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) // "brain" package brain import ( @@ -15,9 +13,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" ) -// fs provides unrestricted filesystem access for shared brain credentials. +// keyPath := core.JoinPath(home, ".claude", "brain.key") // -// keyPath := core.JoinPath(home, ".claude", "brain.key") // if readResult := fs.Read(keyPath); readResult.OK { // apiKey = core.Trim(readResult.Value.(string)) // } @@ -27,40 +24,31 @@ func stringField(values map[string]any, key string) string { return core.Sprint(values[key]) } -// errBridgeNotAvailable is returned when a tool requires the Laravel bridge -// but it has not been initialised (headless mode). +// core.E("brain", "bridge not available", nil) var errBridgeNotAvailable = core.E("brain", "bridge not available", nil) -// Subsystem routes `brain_*` MCP tools through the shared IDE bridge. -// -// subsystem := brain.New(nil) -// core.Println(subsystem.Name()) // "brain" +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) // "brain" type Subsystem struct { bridge *ide.Bridge } -// New builds the bridge-backed OpenBrain subsystem used by MCP. -// -// subsystem := brain.New(nil) -// core.Println(subsystem.Name()) +// subsystem := brain.New(nil) +// core.Println(subsystem.Name()) func New(bridge *ide.Bridge) *Subsystem { return &Subsystem{bridge: bridge} } -// name := subsystem.Name() // "brain" +// name := subsystem.Name() // "brain" func (s *Subsystem) Name() string { return "brain" } -// RegisterTools publishes the bridge-backed brain tools on an MCP server. -// -// subsystem := brain.New(nil) -// subsystem.RegisterTools(server) +// subsystem := brain.New(nil) +// subsystem.RegisterTools(server) func (s *Subsystem) RegisterTools(server *mcp.Server) { s.registerBrainTools(server) } -// Shutdown satisfies the MCP subsystem lifecycle without extra cleanup. -// -// _ = subsystem.Shutdown(context.Background()) +// _ = subsystem.Shutdown(context.Background()) func (s *Subsystem) Shutdown(_ context.Context) error { return nil } diff --git a/pkg/brain/direct.go b/pkg/brain/direct.go index dbeb466..19f29ca 100644 --- a/pkg/brain/direct.go +++ b/pkg/brain/direct.go @@ -12,10 +12,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" ) -// DirectSubsystem talks to OpenBrain over HTTP without the IDE bridge. -// -// subsystem := brain.NewDirect() -// core.Println(subsystem.Name()) // "brain" +// subsystem := brain.NewDirect() +// core.Println(subsystem.Name()) // "brain" type DirectSubsystem struct { apiURL string apiKey string @@ -23,10 +21,8 @@ type DirectSubsystem struct { var _ coremcp.Subsystem = (*DirectSubsystem)(nil) -// NewDirect builds the HTTP-backed OpenBrain subsystem. -// -// subsystem := brain.NewDirect() -// core.Println(subsystem.Name()) +// subsystem := brain.NewDirect() +// core.Println(subsystem.Name()) func NewDirect() *DirectSubsystem { apiURL := core.Env("CORE_BRAIN_URL") if apiURL == "" { @@ -56,13 +52,11 @@ func NewDirect() *DirectSubsystem { } } -// name := subsystem.Name() // "brain" +// name := subsystem.Name() // "brain" func (s *DirectSubsystem) Name() string { return "brain" } -// RegisterTools publishes the direct `brain_*` and `agent_*` tools on an MCP server. -// -// subsystem := brain.NewDirect() -// subsystem.RegisterTools(server) +// subsystem := brain.NewDirect() +// subsystem.RegisterTools(server) func (s *DirectSubsystem) RegisterTools(server *mcp.Server) { mcp.AddTool(server, &mcp.Tool{ Name: "brain_remember", @@ -83,9 +77,7 @@ func (s *DirectSubsystem) RegisterTools(server *mcp.Server) { s.RegisterMessagingTools(server) } -// Shutdown satisfies the MCP subsystem lifecycle without extra cleanup. -// -// _ = subsystem.Shutdown(context.Background()) +// _ = subsystem.Shutdown(context.Background()) func (s *DirectSubsystem) Shutdown(_ context.Context) error { return nil } func brainKeyPath(home string) string { diff --git a/pkg/brain/messaging.go b/pkg/brain/messaging.go index 176c838..7df400a 100644 --- a/pkg/brain/messaging.go +++ b/pkg/brain/messaging.go @@ -10,10 +10,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" ) -// RegisterMessagingTools adds direct agent messaging tools to an MCP server. -// -// subsystem := brain.NewDirect() -// subsystem.RegisterMessagingTools(server) +// subsystem := brain.NewDirect() +// subsystem.RegisterMessagingTools(server) func (s *DirectSubsystem) RegisterMessagingTools(server *mcp.Server) { mcp.AddTool(server, &mcp.Tool{ Name: "agent_send", @@ -33,34 +31,26 @@ func (s *DirectSubsystem) RegisterMessagingTools(server *mcp.Server) { // Input/Output types -// SendInput is the payload for `agent_send`. -// -// brain.SendInput{To: "charon", Subject: "status update", Content: "deploy complete"} +// brain.SendInput{To: "charon", Subject: "status update", Content: "deploy complete"} type SendInput struct { To string `json:"to"` Content string `json:"content"` Subject string `json:"subject,omitempty"` } -// SendOutput reports the stored direct message. -// -// brain.SendOutput{Success: true, ID: 42, To: "charon"} +// brain.SendOutput{Success: true, ID: 42, To: "charon"} type SendOutput struct { Success bool `json:"success"` ID int `json:"id"` To string `json:"to"` } -// InboxInput selects which inbox `agent_inbox` should read. -// -// brain.InboxInput{Agent: "cladius"} +// brain.InboxInput{Agent: "cladius"} type InboxInput struct { Agent string `json:"agent,omitempty"` } -// MessageItem is one inbox or conversation entry. -// -// brain.MessageItem{ID: 7, From: "cladius", To: "charon", Content: "all green"} +// brain.MessageItem{ID: 7, From: "cladius", To: "charon", Content: "all green"} type MessageItem struct { ID int `json:"id"` From string `json:"from"` @@ -71,24 +61,18 @@ type MessageItem struct { CreatedAt string `json:"created_at"` } -// InboxOutput returns the latest direct messages for one agent. -// -// brain.InboxOutput{Success: true, Messages: []brain.MessageItem{{ID: 1, From: "charon", To: "cladius"}}} +// brain.InboxOutput{Success: true, Messages: []brain.MessageItem{{ID: 1, From: "charon", To: "cladius"}}} type InboxOutput struct { Success bool `json:"success"` Messages []MessageItem `json:"messages"` } -// ConversationInput selects the thread `agent_conversation` should load. -// -// brain.ConversationInput{Agent: "charon"} +// brain.ConversationInput{Agent: "charon"} type ConversationInput struct { Agent string `json:"agent"` } -// ConversationOutput returns a direct message thread with another agent. -// -// brain.ConversationOutput{Success: true, Messages: []brain.MessageItem{{ID: 10, From: "cladius", To: "charon"}}} +// brain.ConversationOutput{Success: true, Messages: []brain.MessageItem{{ID: 10, From: "cladius", To: "charon"}}} type ConversationOutput struct { Success bool `json:"success"` Messages []MessageItem `json:"messages"` diff --git a/pkg/brain/provider.go b/pkg/brain/provider.go index b5de871..3b4d1b8 100644 --- a/pkg/brain/provider.go +++ b/pkg/brain/provider.go @@ -33,10 +33,8 @@ const ( statusServiceUnavailable = 503 ) -// NewProvider builds the HTTP provider around the IDE bridge and WS hub. -// -// p := brain.NewProvider(bridge, hub) -// core.Println(p.BasePath()) +// p := brain.NewProvider(bridge, hub) +// core.Println(p.BasePath()) func NewProvider(bridge *ide.Bridge, hub *ws.Hub) *BrainProvider { return &BrainProvider{ bridge: bridge, @@ -47,15 +45,11 @@ func NewProvider(bridge *ide.Bridge, hub *ws.Hub) *BrainProvider { // name := p.Name() // "brain" func (p *BrainProvider) Name() string { return "brain" } -// BasePath shows where the provider mounts its routes. -// -// base := p.BasePath() // "/api/brain" +// base := p.BasePath() // "/api/brain" func (p *BrainProvider) BasePath() string { return "/api/brain" } -// Channels lists the WS events emitted after brain actions complete. -// -// channels := p.Channels() -// core.Println(channels[0]) // "brain.remember.complete" +// channels := p.Channels() +// core.Println(channels[0]) // "brain.remember.complete" func (p *BrainProvider) Channels() []string { return []string{ "brain.remember.complete", @@ -64,10 +58,8 @@ func (p *BrainProvider) Channels() []string { } } -// Element describes the browser component that renders the brain panel. -// -// spec := p.Element() -// core.Println(spec.Tag) // "core-brain-panel" +// spec := p.Element() +// core.Println(spec.Tag) // "core-brain-panel" func (p *BrainProvider) Element() provider.ElementSpec { return provider.ElementSpec{ Tag: "core-brain-panel", @@ -75,9 +67,7 @@ func (p *BrainProvider) Element() provider.ElementSpec { } } -// RegisterRoutes mounts the provider handlers onto a router group. -// -// p.RegisterRoutes(router.Group("/api/brain")) +// p.RegisterRoutes(router.Group("/api/brain")) func (p *BrainProvider) RegisterRoutes(rg *gin.RouterGroup) { rg.POST("/remember", p.remember) rg.POST("/recall", p.recall) @@ -86,10 +76,8 @@ func (p *BrainProvider) RegisterRoutes(rg *gin.RouterGroup) { rg.GET("/status", p.status) } -// Describe returns the route contract used by API discovery and docs. -// -// routes := p.Describe() -// core.Println(routes[0].Path) // "/remember" +// routes := p.Describe() +// core.Println(routes[0].Path) // "/remember" func (p *BrainProvider) Describe() []api.RouteDescription { return []api.RouteDescription{ { diff --git a/pkg/brain/tools.go b/pkg/brain/tools.go index c693b4d..6999c02 100644 --- a/pkg/brain/tools.go +++ b/pkg/brain/tools.go @@ -13,10 +13,6 @@ import ( // -- Input/Output types ------------------------------------------------------- -// RememberInput is the input for brain_remember. -// -// Usage example: -// // input := brain.RememberInput{ // Content: "Use core.Env for system paths.", // Type: "convention", @@ -31,10 +27,6 @@ type RememberInput struct { ExpiresIn int `json:"expires_in,omitempty"` } -// RememberOutput is the output for brain_remember. -// -// Usage example: -// // output := brain.RememberOutput{ // Success: true, // MemoryID: "mem_123", @@ -45,10 +37,6 @@ type RememberOutput struct { Timestamp time.Time `json:"timestamp"` } -// RecallInput is the input for brain_recall. -// -// Usage example: -// // input := brain.RecallInput{ // Query: "core.Env conventions", // TopK: 5, @@ -59,10 +47,6 @@ type RecallInput struct { Filter RecallFilter `json:"filter,omitempty"` } -// RecallFilter holds optional filter criteria for brain_recall. -// -// Usage example: -// // filter := brain.RecallFilter{ // Project: "agent", // Type: "convention", @@ -74,10 +58,6 @@ type RecallFilter struct { MinConfidence float64 `json:"min_confidence,omitempty"` } -// RecallOutput is the output for brain_recall. -// -// Usage example: -// // output := brain.RecallOutput{ // Success: true, // Count: 1, @@ -88,10 +68,6 @@ type RecallOutput struct { Memories []Memory `json:"memories"` } -// Memory is a single memory entry returned by recall or list. -// -// Usage example: -// // memory := brain.Memory{ // ID: "mem_123", // Type: "convention", @@ -111,10 +87,6 @@ type Memory struct { UpdatedAt string `json:"updated_at"` } -// ForgetInput is the input for brain_forget. -// -// Usage example: -// // input := brain.ForgetInput{ // ID: "mem_123", // Reason: "superseded", @@ -124,10 +96,6 @@ type ForgetInput struct { Reason string `json:"reason,omitempty"` } -// ForgetOutput is the output for brain_forget. -// -// Usage example: -// // output := brain.ForgetOutput{ // Success: true, // Forgotten: "mem_123", @@ -138,10 +106,6 @@ type ForgetOutput struct { Timestamp time.Time `json:"timestamp"` } -// ListInput is the input for brain_list. -// -// Usage example: -// // input := brain.ListInput{ // Project: "agent", // Limit: 20, @@ -153,10 +117,6 @@ type ListInput struct { Limit int `json:"limit,omitempty"` } -// ListOutput is the output for brain_list. -// -// Usage example: -// // output := brain.ListOutput{ // Success: true, // Count: 2, diff --git a/pkg/messages/messages.go b/pkg/messages/messages.go index 8fd6c52..6805d16 100644 --- a/pkg/messages/messages.go +++ b/pkg/messages/messages.go @@ -1,26 +1,18 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package messages defines IPC message types for inter-service communication -// within core-agent. Services emit these via c.ACTION() and handle them via -// c.RegisterAction(). No service imports another — they share only these types. -// -// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Status: "completed"}) +// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Status: "completed"}) package messages // --- Agent Lifecycle --- -// AgentStarted is broadcast when a subagent process is spawned. -// -// c.ACTION(messages.AgentStarted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5"}) +// c.ACTION(messages.AgentStarted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5"}) type AgentStarted struct { Agent string Repo string Workspace string } -// AgentCompleted is broadcast when a subagent process exits. -// -// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed"}) +// c.ACTION(messages.AgentCompleted{Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed"}) type AgentCompleted struct { Agent string Repo string @@ -30,9 +22,7 @@ type AgentCompleted struct { // --- QA & PR Pipeline --- -// QAResult is broadcast after QA runs on a completed workspace. -// -// c.ACTION(messages.QAResult{Workspace: "core/go-io/task-5", Repo: "go-io", Passed: true}) +// c.ACTION(messages.QAResult{Workspace: "core/go-io/task-5", Repo: "go-io", Passed: true}) type QAResult struct { Workspace string Repo string @@ -40,9 +30,7 @@ type QAResult struct { Output string } -// PRCreated is broadcast after a PR is auto-created on Forge. -// -// c.ACTION(messages.PRCreated{Repo: "go-io", Branch: "agent/fix-tests", PRURL: "https://...", PRNum: 12}) +// c.ACTION(messages.PRCreated{Repo: "go-io", Branch: "agent/fix-tests", PRURL: "https://...", PRNum: 12}) type PRCreated struct { Repo string Branch string @@ -50,18 +38,14 @@ type PRCreated struct { PRNum int } -// PRMerged is broadcast after a PR is auto-verified and merged. -// -// c.ACTION(messages.PRMerged{Repo: "go-io", PRURL: "https://...", PRNum: 12}) +// c.ACTION(messages.PRMerged{Repo: "go-io", PRURL: "https://...", PRNum: 12}) type PRMerged struct { Repo string PRURL string PRNum int } -// PRNeedsReview is broadcast when auto-merge fails and human attention is needed. -// -// c.ACTION(messages.PRNeedsReview{Repo: "go-io", PRNum: 12, Reason: "merge conflict"}) +// c.ACTION(messages.PRNeedsReview{Repo: "go-io", PRNum: 12, Reason: "merge conflict"}) type PRNeedsReview struct { Repo string PRURL string @@ -71,31 +55,22 @@ type PRNeedsReview struct { // --- Queue --- -// QueueDrained is broadcast when running=0 and queued=0 (genuinely empty). -// -// c.ACTION(messages.QueueDrained{Completed: 3}) +// c.ACTION(messages.QueueDrained{Completed: 3}) type QueueDrained struct { Completed int } -// PokeQueue signals the runner to drain the queue immediately. -// -// c.ACTION(messages.PokeQueue{}) +// c.ACTION(messages.PokeQueue{}) type PokeQueue struct{} -// SpawnQueued is sent by the runner to request agentic spawn a queued workspace. -// Runner gates (frozen + concurrency), agentic owns the actual process spawn. -// -// c.ACTION(messages.SpawnQueued{Workspace: "core/go-io/task-5", Agent: "codex", Task: "review"}) +// c.ACTION(messages.SpawnQueued{Workspace: "core/go-io/task-5", Agent: "codex", Task: "review"}) type SpawnQueued struct { Workspace string Agent string Task string } -// RateLimitDetected is broadcast when fast failures trigger agent pool backoff. -// -// c.ACTION(messages.RateLimitDetected{Pool: "codex", Duration: "30m"}) +// c.ACTION(messages.RateLimitDetected{Pool: "codex", Duration: "30m"}) type RateLimitDetected struct { Pool string Duration string @@ -103,27 +78,21 @@ type RateLimitDetected struct { // --- Monitor Events --- -// HarvestComplete is broadcast when a workspace branch is ready for review. -// -// c.ACTION(messages.HarvestComplete{Repo: "go-io", Branch: "agent/fix-tests", Files: 5}) +// c.ACTION(messages.HarvestComplete{Repo: "go-io", Branch: "agent/fix-tests", Files: 5}) type HarvestComplete struct { Repo string Branch string Files int } -// HarvestRejected is broadcast when a workspace fails safety checks (binaries, size). -// -// c.ACTION(messages.HarvestRejected{Repo: "go-io", Branch: "agent/fix-tests", Reason: "binary detected"}) +// c.ACTION(messages.HarvestRejected{Repo: "go-io", Branch: "agent/fix-tests", Reason: "binary detected"}) type HarvestRejected struct { Repo string Branch string Reason string } -// InboxMessage is broadcast when a new inter-agent message arrives. -// -// c.ACTION(messages.InboxMessage{From: "charon", Subject: "status", Content: "all green"}) +// c.ACTION(messages.InboxMessage{From: "charon", Subject: "status", Content: "all green"}) type InboxMessage struct { From string Subject string diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index f61fb6c..a34dd82 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -1,9 +1,7 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package monitor keeps workspace state and inbox status visible to MCP clients. -// -// service := monitor.New(monitor.Options{Interval: 30 * time.Second}) -// service.RegisterTools(server) +// service := monitor.New(monitor.Options{Interval: 30 * time.Second}) +// service.RegisterTools(server) package monitor import ( diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 4608f58..5f7bc25 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -1,9 +1,7 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package runner owns agent dispatch and workspace lifecycle. -// -// service := runner.New() -// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) +// service := runner.New() +// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) package runner import ( @@ -16,17 +14,11 @@ import ( core "dappco.re/go/core" ) -// Options configures the runner service. -// -// options := runner.Options{} +// options := runner.Options{} type Options struct{} -// Service is the agent dispatch runner. -// Manages concurrency limits, queue drain, workspace lifecycle, and frozen state. -// All dispatch requests — MCP tool, CLI, or IPC — go through this service. -// -// service := runner.New() -// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) +// service := runner.New() +// service.TrackWorkspace("core/go-io/task-5", &runner.WorkspaceStatus{Status: "running", Agent: "codex"}) type Service struct { *core.ServiceRuntime[Options] dispatchMu sync.Mutex @@ -73,13 +65,13 @@ func Register(coreApp *core.Core) core.Result { return core.Result{Value: service, OK: true} } -// OnStartup registers Actions and starts the queue runner. +// c.Action("runner.dispatch").Run(ctx, core.NewOptions( // -// c.Action("runner.dispatch").Run(ctx, core.NewOptions( -// core.Option{Key: "repo", Value: "go-io"}, -// core.Option{Key: "agent", Value: "codex"}, -// )) -// c.Action("runner.status").Run(ctx, core.NewOptions()) +// core.Option{Key: "repo", Value: "go-io"}, +// core.Option{Key: "agent", Value: "codex"}, +// +// )) +// c.Action("runner.status").Run(ctx, core.NewOptions()) func (s *Service) OnStartup(ctx context.Context) core.Result { coreApp := s.Core() @@ -99,9 +91,8 @@ func (s *Service) OnStartup(ctx context.Context) core.Result { return core.Result{OK: true} } -// OnShutdown freezes the queue. +// result := service.OnShutdown(context.Background()) // -// result := service.OnShutdown(context.Background()) // if result.OK { // core.Println(service.IsFrozen()) // } @@ -110,9 +101,8 @@ func (s *Service) OnShutdown(_ context.Context) core.Result { return core.Result{OK: true} } -// HandleIPCEvents applies runner side-effects for IPC messages. +// service.HandleIPCEvents(c, messages.PokeQueue{}) // -// service.HandleIPCEvents(c, messages.PokeQueue{}) // service.HandleIPCEvents(c, messages.AgentCompleted{ // Agent: "codex", Repo: "go-io", Workspace: "core/go-io/task-5", Status: "completed", // }) @@ -190,16 +180,12 @@ func (s *Service) HandleIPCEvents(coreApp *core.Core, msg core.Message) core.Res return core.Result{OK: true} } -// IsFrozen returns whether dispatch is currently frozen. -// -// if s.IsFrozen() { return "queue is frozen" } +// if s.IsFrozen() { return "queue is frozen" } func (s *Service) IsFrozen() bool { return s.frozen } -// Poke signals the runner to check the queue immediately. -// -// s.Poke() +// s.Poke() func (s *Service) Poke() { if s.pokeCh == nil { return @@ -210,11 +196,8 @@ func (s *Service) Poke() { } } -// TrackWorkspace registers or updates a workspace in the in-memory Registry. -// Accepts the runner projection directly and the agentic projection from IPC. -// -// s.TrackWorkspace("core/go-io/task-5", &WorkspaceStatus{Status: "running", Agent: "codex"}) -// s.TrackWorkspace("core/go-io/task-5", &agentic.WorkspaceStatus{Status: "running", Agent: "codex"}) +// s.TrackWorkspace("core/go-io/task-5", &WorkspaceStatus{Status: "running", Agent: "codex"}) +// s.TrackWorkspace("core/go-io/task-5", &agentic.WorkspaceStatus{Status: "running", Agent: "codex"}) func (s *Service) TrackWorkspace(name string, status any) { if s.workspaces == nil { return @@ -240,17 +223,13 @@ func (s *Service) TrackWorkspace(name string, status any) { s.workspaces.Delete(core.Concat("pending/", workspaceStatus.Repo)) } -// Workspaces returns the workspace Registry. -// -// s.Workspaces().Each(func(name string, workspaceStatus *WorkspaceStatus) { ... }) +// s.Workspaces().Each(func(name string, workspaceStatus *WorkspaceStatus) { ... }) func (s *Service) Workspaces() *core.Registry[*WorkspaceStatus] { return s.workspaces } -// handleWorkspaceQuery answers workspace state queries from Core QUERY calls. -// -// result := c.QUERY(runner.WorkspaceQuery{Name: "core/go-io/task-42"}) -// result := c.QUERY(runner.WorkspaceQuery{Status: "running"}) +// result := c.QUERY(runner.WorkspaceQuery{Name: "core/go-io/task-42"}) +// result := c.QUERY(runner.WorkspaceQuery{Status: "running"}) func (s *Service) handleWorkspaceQuery(_ *core.Core, query core.Query) core.Result { workspaceQuery, ok := query.(WorkspaceQuery) if !ok { diff --git a/pkg/setup/config.go b/pkg/setup/config.go index df7b4d3..f103703 100644 --- a/pkg/setup/config.go +++ b/pkg/setup/config.go @@ -7,9 +7,7 @@ import ( "gopkg.in/yaml.v3" ) -// ConfigData supplies values when setup renders workspace templates. -// -// data := setup.ConfigData{Name: "agent", Type: "go", Repository: "core/agent"} +// data := setup.ConfigData{Name: "agent", Type: "go", Repository: "core/agent"} type ConfigData struct { Name string Description string @@ -22,17 +20,13 @@ type ConfigData struct { Env map[string]string } -// Target describes one build target. -// -// target := setup.Target{OS: "linux", Arch: "amd64"} +// target := setup.Target{OS: "linux", Arch: "amd64"} type Target struct { OS string Arch string } -// Command defines one named command in generated config. -// -// command := setup.Command{Name: "unit", Run: "go test ./..."} +// command := setup.Command{Name: "unit", Run: "go test ./..."} type Command struct { Name string Run string @@ -48,10 +42,8 @@ type configValue struct { Value any } -// GenerateBuildConfig renders `build.yaml` content for a detected repo type. -// -// r := setup.GenerateBuildConfig("/srv/repos/agent", setup.TypeGo) -// if r.OK { content := r.Value.(string) } +// r := setup.GenerateBuildConfig("/srv/repos/agent", setup.TypeGo) +// if r.OK { content := r.Value.(string) } func GenerateBuildConfig(path string, projectType ProjectType) core.Result { name := core.PathBase(path) sections := []configSection{ @@ -95,10 +87,8 @@ func GenerateBuildConfig(path string, projectType ProjectType) core.Result { return renderConfig(core.Concat(name, " build configuration"), sections) } -// GenerateTestConfig renders `test.yaml` content for a detected repo type. -// -// r := setup.GenerateTestConfig(setup.TypeGo) -// if r.OK { content := r.Value.(string) } +// r := setup.GenerateTestConfig(setup.TypeGo) +// if r.OK { content := r.Value.(string) } func GenerateTestConfig(projectType ProjectType) core.Result { var sections []configSection diff --git a/pkg/setup/detect.go b/pkg/setup/detect.go index a7b8060..ac11d19 100644 --- a/pkg/setup/detect.go +++ b/pkg/setup/detect.go @@ -1,18 +1,14 @@ // SPDX-License-Identifier: EUPL-1.2 -// Package setup provisions `.core/` files and workspace scaffolds for a repo. -// -// service := core.ServiceFor[*setup.Service](core.New(core.WithService(setup.Register)), "setup") +// service := core.ServiceFor[*setup.Service](core.New(core.WithService(setup.Register)), "setup") package setup import ( core "dappco.re/go/core" ) -// ProjectType records what setup detected in a repository path. -// -// projectType := setup.Detect("/srv/repos/agent") -// if projectType == setup.TypeGo { /* generate Go defaults */ } +// projectType := setup.Detect("/srv/repos/agent") +// if projectType == setup.TypeGo { /* generate Go defaults */ } type ProjectType string const ( @@ -23,12 +19,10 @@ const ( TypeUnknown ProjectType = "unknown" ) -// fs provides unrestricted filesystem access for setup operations. +// fs := (&core.Fs{}).NewUnrestricted() var fs = (&core.Fs{}).NewUnrestricted() -// Detect inspects a repository path and returns the primary project type. -// -// projectType := setup.Detect("./repo") +// projectType := setup.Detect("./repo") func Detect(path string) ProjectType { base := absolutePath(path) checks := []struct { @@ -48,9 +42,7 @@ func Detect(path string) ProjectType { return TypeUnknown } -// DetectAll returns every detected project type for a polyglot repository. -// -// types := setup.DetectAll("./repo") +// types := setup.DetectAll("./repo") func DetectAll(path string) []ProjectType { base := absolutePath(path) var projectTypes []ProjectType diff --git a/pkg/setup/service.go b/pkg/setup/service.go index 2516d94..0f4d194 100644 --- a/pkg/setup/service.go +++ b/pkg/setup/service.go @@ -26,16 +26,12 @@ func Register(c *core.Core) core.Result { return core.Result{Value: service, OK: true} } -// OnStartup keeps the setup service ready for Core startup hooks. -// -// result := service.OnStartup(context.Background()) +// result := service.OnStartup(context.Background()) func (s *Service) OnStartup(ctx context.Context) core.Result { return core.Result{OK: true} } -// DetectGitRemote reads `origin` and returns `owner/repo` when available. -// -// remote := service.DetectGitRemote("/srv/repos/agent") +// remote := service.DetectGitRemote("/srv/repos/agent") func (s *Service) DetectGitRemote(path string) string { result := s.Core().Process().RunIn(context.Background(), path, "git", "remote", "get-url", "origin") if !result.OK { diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go index 66691a7..cd09981 100644 --- a/pkg/setup/setup.go +++ b/pkg/setup/setup.go @@ -7,10 +7,8 @@ import ( core "dappco.re/go/core" ) -// Options controls one setup run. -// -// result := service.Run(setup.Options{Path: ".", Template: "auto", Force: true}) -// if !result.OK { core.Print(nil, "%v", result.Value) } +// result := service.Run(setup.Options{Path: ".", Template: "auto", Force: true}) +// if !result.OK { core.Print(nil, "%v", result.Value) } type Options struct { Path string // Target directory (default: cwd) DryRun bool // Preview only, don't write @@ -18,10 +16,8 @@ type Options struct { Template string // Workspace template or compatibility alias (default, review, security, agent, go, php, gui, auto) } -// Run generates `.core/` files and optional workspace scaffolding for a repo. -// -// result := service.Run(setup.Options{Path: ".", Template: "auto"}) -// core.Println(result.OK) +// result := service.Run(setup.Options{Path: ".", Template: "auto"}) +// core.Println(result.OK) func (s *Service) Run(options Options) core.Result { if options.Path == "" { options.Path = core.Env("DIR_CWD")