Problem Statement
WildGecu currently supports a single agent identity per installation. Users who want multiple specialized agents — a research agent, a coding assistant, a daily planner — must run entirely separate WildGecu instances with different --home directories. There is no way to define, manage, or address multiple named agents from a single installation, and no way for the main agent to delegate to a persistent agent with its own expertise and memory.
Solution
Introduce static subagents — persistent, named agents that live under the main agent's home directory. Each static agent has its own identity (SOUL.md), memory (MEMORY.md), configuration (config.yaml), and cron jobs. They are managed via CLI commands and can be addressed by name from both the user (via --agent flag) and the main agent (via delegate_to_agent and create_agent tools).
Static agents are independent peers of the main agent: they have their own personality, model preference, and scheduled tasks, but they share the same daemon process and global configuration (providers, API keys).
User Stories
- As a user, I want to create a named agent with its own identity (e.g.,
wildgecu agent add researcher), so that I can have specialized agents for different domains.
- As a user, I want to list all my static agents (
wildgecu agent list), so that I can see what agents are available.
- As a user, I want to remove a static agent (
wildgecu agent remove researcher), so that I can clean up agents I no longer need.
- As a user, I want to chat with a specific agent (
wildgecu chat --agent=researcher), so that I get responses from the right identity and memory.
- As a user, I want to use code mode with a specific agent (
wildgecu code --agent=researcher), so that a specialized coding agent works on my repo.
- As a user, I want each agent to have its own
config.yaml specifying its preferred model, so that I can assign cheaper models to simple agents and expensive models to complex ones.
- As a user, I want each agent to have its own SOUL.md created through an interactive bootstrap interview, so that each agent has a distinct personality and expertise.
- As a user, I want each agent to maintain its own MEMORY.md, so that memory from one agent doesn't leak into another.
- As a user, I want to add cron jobs to specific agents (
wildgecu cron add --agent=researcher), so that scheduled tasks run with the right agent identity.
- As a user, I want to list cron jobs for a specific agent (
wildgecu cron ls --agent=researcher), so that I can manage each agent's schedule independently.
- As an agent (main), I want to delegate a task to a static agent by name (
delegate_to_agent), so that I can leverage a specialist's identity and memory for the task.
- As an agent (main), I want to create a new static agent during conversation (
create_agent), so that the user can ask me to set up a new specialist without leaving the chat.
- As a developer, I want static agents to reuse the existing
Home abstraction, so that SOUL/MEMORY/crons/skills work identically for main and sub agents.
- As a developer, I want one daemon to serve all agents, so that there's no process management overhead per agent.
- As a developer, I want the daemon to load all agents' cron jobs at startup, so that all scheduled work runs from a single scheduler.
- As a static agent, I want to run my cron jobs with my own SOUL and model, so that my scheduled outputs reflect my identity.
- As a static agent, I want my own memory curation after each session, so that I build up context relevant to my domain.
Implementation Decisions
Directory structure
Each static agent lives at ~/.wildgecu/agents/<name>/ with a mirrored structure:
~/.wildgecu/
├── SOUL.md # main agent identity
├── MEMORY.md # main agent memory
├── wildgecu.yaml # global config (providers, API keys)
├── crons/ # main agent cron jobs
├── cron-results/ # main agent cron results
├── agents/
│ ├── researcher/
│ │ ├── config.yaml # agent-specific config (model)
│ │ ├── SOUL.md # agent identity
│ │ ├── MEMORY.md # agent memory
│ │ ├── crons/ # agent cron jobs
│ │ └── cron-results/ # agent cron results
│ └── planner/
│ ├── config.yaml
│ ├── SOUL.md
│ ├── MEMORY.md
│ ├── crons/
│ └── cron-results/
Agent config.yaml
Minimal for v1:
model: gemini/gemini-2.0-flash
The model field is a provider/model reference (or alias) resolved against the global wildgecu.yaml providers map. If omitted, the agent inherits the global default_model.
Home abstraction extension
The existing Home type gets new methods:
AgentsDir() string — returns the path to the agents/ directory
Agent(name string) (*Home, error) — returns a Home instance rooted at agents/<name>/, creating the directory if needed
ListAgents() ([]string, error) — lists all agent names by scanning agents/
This means a static agent's Home gives access to .Soul(), .Memory(), .CronsDir(), .CronResultsDir() etc. — all scoped to that agent's directory. No new abstraction needed.
Agent config parsing
A new config type for parsing config.yaml:
- Lives alongside agent setup code (e.g.,
pkg/agent/agentcfg/ or directly in pkg/agent/)
- Parses the YAML file and resolves the model reference against the global config
- Returns a model string that the provider Container can use
CLI commands
New command group: wildgecu agent
wildgecu agent add <name> — Creates the agent directory, runs a bootstrap interview (reusing the existing bootstrap mechanism from cmd/init.go), writes SOUL.md and config.yaml. Asks for model preference during setup.
wildgecu agent list — Scans agents/ and prints each agent's name, model, and whether it has a SOUL.
wildgecu agent remove <name> — Deletes the agent's directory after confirmation.
Modified commands with --agent flag:
wildgecu chat --agent=<name> — Connects to the daemon and creates a session scoped to the named agent's identity and model.
wildgecu code --agent=<name> — Same, but in code mode.
wildgecu cron add --agent=<name> — Adds a cron job under the named agent's crons/ directory.
wildgecu cron ls --agent=<name> — Lists cron jobs for the named agent.
wildgecu cron rm --agent=<name> <job> — Removes a cron job from the named agent.
When --agent is omitted, all commands operate on the main agent (backward compatible).
Daemon changes
The daemon currently initializes one SessionManager for the main agent. With static agents:
- At startup, the daemon scans
~/.wildgecu/agents/ to discover all static agents.
- Cron scheduling: For each agent, it loads their
crons/ directory and creates cron jobs using the agent's own provider (resolved from their config.yaml model + global providers map). All jobs run in one gocron.Scheduler.
- Session creation: The daemon's IPC protocol (ChatRequest) gains an
Agent field. When a client sends session.create with an agent name, the daemon creates a session using that agent's Home, SOUL, and model — calling agent.Prepare() with the agent-specific config.
- Reload: On daemon restart, the agent list and all cron jobs are reloaded.
delegate_to_agent tool
A new tool available to the main agent (and other static agents):
- Input:
agent_name (required, string), prompt (required, string)
- Looks up the static agent by name from
~/.wildgecu/agents/<name>/
- Loads the agent's SOUL, MEMORY, and config
- Creates a provider from the agent's model config
- Runs a full agent loop with the agent's system prompt and tools
- Returns the final text response to the caller
- The delegated agent's conversation is NOT saved to its memory (it's a one-shot delegation, not a full session)
create_agent tool
A new tool available to the main agent:
- Input:
name (required, string), soul (required, string), model (optional, string)
- Creates the directory structure at
agents/<name>/
- Writes the provided soul content to
SOUL.md
- Writes
config.yaml with the model (or default)
- Returns confirmation to the caller
- The main agent crafts the SOUL content based on conversation with the user (no bootstrap interview — the main agent IS the interviewer)
Module changes
- Modified:
pkg/home/home.go — Add AgentsDir(), Agent(name), ListAgents() methods
- New:
pkg/agent/agentcfg.go (or similar) — Agent config.yaml parsing
- New:
cmd/agent.go — wildgecu agent command group
- New:
cmd/agent_add.go — Bootstrap interview for new agents
- New:
cmd/agent_list.go — List agents
- New:
cmd/agent_remove.go — Remove agents
- Modified:
cmd/chat.go — Add --agent flag, pass agent name to daemon
- Modified:
cmd/code.go — Add --agent flag
- Modified:
cmd/cron.go — Add --agent flag
- Modified:
cmd/cron_add.go — Add --agent flag
- Modified:
pkg/daemon/daemon.go — Scan agents at startup, multi-agent cron loading
- Modified:
pkg/daemon/sessions.go — Support sessions scoped to specific agents
- Modified:
pkg/daemon/chat_handler.go — Handle Agent field in ChatRequest
- Modified:
pkg/agent/tools/subagent.go — Add delegate_to_agent and create_agent tools
Testing Decisions
Good tests verify external behavior through public interfaces. Use mock providers and temporary directories, not real LLM APIs.
Modules to test
-
pkg/home/home.go — Agent directory methods: Test AgentsDir(), Agent(name), ListAgents() with temp directories. Verify directory creation, listing, and that sub-Home accessors (Soul, Memory, CronsDir) return correct paths. Prior art: TestNew, TestIdentityFiles, TestDirectoryAccessors in pkg/home/home_test.go.
-
Agent config parsing: Test YAML parsing, model resolution, and defaults. Prior art: config tests in x/config/load_test.go.
-
delegate_to_agent tool: Test with a mock provider and a temp agent directory containing a SOUL and config. Verify that the agent's identity is loaded and the prompt is forwarded correctly. Prior art: tool tests in pkg/agent/tools/.
-
create_agent tool: Test that it creates the correct directory structure and files. Verify config.yaml and SOUL.md contents. No mock provider needed — pure filesystem test.
-
Daemon multi-agent cron loading: Test that the scheduler discovers and loads cron jobs from multiple agent directories. Prior art: pkg/cron/scheduler_test.go.
What makes a good test here
- Test public interfaces, not internal helpers
- Use
t.TempDir() for filesystem operations
- Use subtests (
t.Run) per the project's CLAUDE.md convention
- Mock the Provider interface for tool tests
- Verify the contract: correct file paths, correct SOUL loading, correct model resolution
Out of Scope
- Telegram per subagent — Telegram stays bound to the main agent only
- Hot-reload of agents — Agents are discovered on daemon startup/restart only
- Workspace-scoped agents — Agents in project
.wildgecu/ directories
- Tool restrictions in config.yaml — All agents get the same tool set for v1
- Agent-to-agent direct communication — Agents communicate only through the parent's delegation tools
- Documentation updates — Will be done as a follow-up after implementation
Further Notes
- The
--agent flag follows the same pattern as the existing --model flag: it's a global flag on the root command, available to all subcommands.
- The
delegate_to_agent tool intentionally does NOT persist memory for the delegated session. This keeps delegation fast and side-effect-free. If users want persistent conversations with a subagent, they use wildgecu chat --agent=<name>.
- The
create_agent tool lets the main agent act as an interviewer — the user says "create me a research agent" and the main agent crafts an appropriate SOUL without running the standard bootstrap flow.
- Agent names should be validated (alphanumeric + hyphens, no spaces, no path traversal).
Problem Statement
WildGecu currently supports a single agent identity per installation. Users who want multiple specialized agents — a research agent, a coding assistant, a daily planner — must run entirely separate WildGecu instances with different
--homedirectories. There is no way to define, manage, or address multiple named agents from a single installation, and no way for the main agent to delegate to a persistent agent with its own expertise and memory.Solution
Introduce static subagents — persistent, named agents that live under the main agent's home directory. Each static agent has its own identity (SOUL.md), memory (MEMORY.md), configuration (config.yaml), and cron jobs. They are managed via CLI commands and can be addressed by name from both the user (via
--agentflag) and the main agent (viadelegate_to_agentandcreate_agenttools).Static agents are independent peers of the main agent: they have their own personality, model preference, and scheduled tasks, but they share the same daemon process and global configuration (providers, API keys).
User Stories
wildgecu agent add researcher), so that I can have specialized agents for different domains.wildgecu agent list), so that I can see what agents are available.wildgecu agent remove researcher), so that I can clean up agents I no longer need.wildgecu chat --agent=researcher), so that I get responses from the right identity and memory.wildgecu code --agent=researcher), so that a specialized coding agent works on my repo.config.yamlspecifying its preferred model, so that I can assign cheaper models to simple agents and expensive models to complex ones.wildgecu cron add --agent=researcher), so that scheduled tasks run with the right agent identity.wildgecu cron ls --agent=researcher), so that I can manage each agent's schedule independently.delegate_to_agent), so that I can leverage a specialist's identity and memory for the task.create_agent), so that the user can ask me to set up a new specialist without leaving the chat.Homeabstraction, so that SOUL/MEMORY/crons/skills work identically for main and sub agents.Implementation Decisions
Directory structure
Each static agent lives at
~/.wildgecu/agents/<name>/with a mirrored structure:Agent config.yaml
Minimal for v1:
The
modelfield is a provider/model reference (or alias) resolved against the globalwildgecu.yamlproviders map. If omitted, the agent inherits the globaldefault_model.Home abstraction extension
The existing
Hometype gets new methods:AgentsDir() string— returns the path to theagents/directoryAgent(name string) (*Home, error)— returns aHomeinstance rooted atagents/<name>/, creating the directory if neededListAgents() ([]string, error)— lists all agent names by scanningagents/This means a static agent's
Homegives access to.Soul(),.Memory(),.CronsDir(),.CronResultsDir()etc. — all scoped to that agent's directory. No new abstraction needed.Agent config parsing
A new config type for parsing
config.yaml:pkg/agent/agentcfg/or directly inpkg/agent/)CLI commands
New command group:
wildgecu agentwildgecu agent add <name>— Creates the agent directory, runs a bootstrap interview (reusing the existing bootstrap mechanism fromcmd/init.go), writesSOUL.mdandconfig.yaml. Asks for model preference during setup.wildgecu agent list— Scansagents/and prints each agent's name, model, and whether it has a SOUL.wildgecu agent remove <name>— Deletes the agent's directory after confirmation.Modified commands with
--agentflag:wildgecu chat --agent=<name>— Connects to the daemon and creates a session scoped to the named agent's identity and model.wildgecu code --agent=<name>— Same, but in code mode.wildgecu cron add --agent=<name>— Adds a cron job under the named agent'scrons/directory.wildgecu cron ls --agent=<name>— Lists cron jobs for the named agent.wildgecu cron rm --agent=<name> <job>— Removes a cron job from the named agent.When
--agentis omitted, all commands operate on the main agent (backward compatible).Daemon changes
The daemon currently initializes one
SessionManagerfor the main agent. With static agents:~/.wildgecu/agents/to discover all static agents.crons/directory and creates cron jobs using the agent's own provider (resolved from theirconfig.yamlmodel + global providers map). All jobs run in onegocron.Scheduler.Agentfield. When a client sendssession.createwith an agent name, the daemon creates a session using that agent'sHome, SOUL, and model — callingagent.Prepare()with the agent-specific config.delegate_to_agenttoolA new tool available to the main agent (and other static agents):
agent_name(required, string),prompt(required, string)~/.wildgecu/agents/<name>/create_agenttoolA new tool available to the main agent:
name(required, string),soul(required, string),model(optional, string)agents/<name>/SOUL.mdconfig.yamlwith the model (or default)Module changes
pkg/home/home.go— AddAgentsDir(),Agent(name),ListAgents()methodspkg/agent/agentcfg.go(or similar) — Agent config.yaml parsingcmd/agent.go—wildgecu agentcommand groupcmd/agent_add.go— Bootstrap interview for new agentscmd/agent_list.go— List agentscmd/agent_remove.go— Remove agentscmd/chat.go— Add--agentflag, pass agent name to daemoncmd/code.go— Add--agentflagcmd/cron.go— Add--agentflagcmd/cron_add.go— Add--agentflagpkg/daemon/daemon.go— Scan agents at startup, multi-agent cron loadingpkg/daemon/sessions.go— Support sessions scoped to specific agentspkg/daemon/chat_handler.go— HandleAgentfield in ChatRequestpkg/agent/tools/subagent.go— Adddelegate_to_agentandcreate_agenttoolsTesting Decisions
Good tests verify external behavior through public interfaces. Use mock providers and temporary directories, not real LLM APIs.
Modules to test
pkg/home/home.go— Agent directory methods: TestAgentsDir(),Agent(name),ListAgents()with temp directories. Verify directory creation, listing, and that sub-Home accessors (Soul, Memory, CronsDir) return correct paths. Prior art:TestNew,TestIdentityFiles,TestDirectoryAccessorsinpkg/home/home_test.go.Agent config parsing: Test YAML parsing, model resolution, and defaults. Prior art: config tests in
x/config/load_test.go.delegate_to_agenttool: Test with a mock provider and a temp agent directory containing a SOUL and config. Verify that the agent's identity is loaded and the prompt is forwarded correctly. Prior art: tool tests inpkg/agent/tools/.create_agenttool: Test that it creates the correct directory structure and files. Verify config.yaml and SOUL.md contents. No mock provider needed — pure filesystem test.Daemon multi-agent cron loading: Test that the scheduler discovers and loads cron jobs from multiple agent directories. Prior art:
pkg/cron/scheduler_test.go.What makes a good test here
t.TempDir()for filesystem operationst.Run) per the project's CLAUDE.md conventionOut of Scope
.wildgecu/directoriesFurther Notes
--agentflag follows the same pattern as the existing--modelflag: it's a global flag on the root command, available to all subcommands.delegate_to_agenttool intentionally does NOT persist memory for the delegated session. This keeps delegation fast and side-effect-free. If users want persistent conversations with a subagent, they usewildgecu chat --agent=<name>.create_agenttool lets the main agent act as an interviewer — the user says "create me a research agent" and the main agent crafts an appropriate SOUL without running the standard bootstrap flow.