Context
Netclaw currently stores sub-agent definitions as pairs of ~/.netclaw/agents/<name>.json + <name>.md files, parsed by FileSubAgentDefinitionLoader. Claude Code, OpenCode, and the public SKILL.md format that Netclaw already adopted for skills (#150) all use a single markdown file per unit with YAML frontmatter. Sub-agents are the only artifact in Netclaw's install that still uses a two-file JSON+MD pattern.
Two consequences:
- Sub-agents authored for Claude Code or OpenCode can't drop into
~/.netclaw/agents/ as-is. Users with an existing local-ai-preferences/agents/*.md library would be directly portable if Netclaw adopted the same shape.
- Authoring a new Netclaw sub-agent requires keeping two files in sync.
Additionally, the current spawn_agent tool lets the frontline LLM pass only a free-form task string — there's no way to specialize a sub-agent for a single invocation (focus on this competitor, use this workspace, limit scope to that repo) without authoring a whole new agent file.
And there's no way to scope a sub-agent to a specific project/workspace. Every agent is global.
Zero-migration observation: ~/.netclaw/agents/ on a fresh install holds only the three stock defaults seeded by netclaw init. There is no user-authored content to preserve. The format change is free.
Proposal
1. Single-file markdown format with YAML frontmatter
Replace the JSON+MD sidecar with one .md file per agent:
---
name: research-assistant
description: >
Use when the user needs deep web research, multiple sources, or citation.
Spawn with a focused task. Returns a cited summary, not a transcript.
tools: [web_search, web_fetch, file_read, attach_file]
modelRole: Compaction
timeoutSeconds: 120
visibility: user-facing
emitStructuredFindings: false
---
You are a research assistant. Your job is to help the user by searching
the web, gathering information from multiple sources, and synthesizing
findings into clear, well-organized summaries.
## Guidelines
...
Frontmatter fields map 1:1 to the existing SubAgentProfile record at src/Netclaw.Configuration/SubAgentProfile.cs — no new runtime types.
2. Folder discovery and project overlay
Scanner walks ~/.netclaw/agents/*.md at daemon startup and also <workspace>/.netclaw/agents/*.md when a session has a workspace. Project-scoped agents layer on top of home-dir agents (by name). The home-dir scan is authoritative when no workspace is active.
This lets repos ship sub-agents the same way they ship .github/workflows/*.yml and pairs cleanly with #595 (session CWD + workspace context).
3. Enriched [available-subagents] context layer
Today ToolIndexUpdater.UpdateSubAgentDiscovery() at src/Netclaw.Daemon/Mcp/ToolIndexUpdater.cs:95-117 emits:
- research-assistant: Deep web research with search and citation (timeout: 120s)
Enriched form includes the full description, tool list, and an example call using the new Context parameter so the frontline LLM sees both what each agent does and how to specialize it per call.
4. Optional Context parameter on spawn_agent
Extend SpawnAgentTool.Params at src/Netclaw.Actors/SubAgents/SpawnAgentTool.cs:22-26 with one optional field:
public record Params(
string Agent,
string Task,
string? Context = null);
The tool description on the Context field teaches the frontline LLM when to use it ("workspace details, the user's broader goal, facts the subagent would otherwise have to rediscover — do not duplicate the agent's built-in instructions").
Context is prefixed onto the subagent's first user message, not injected into the system prompt. The agent's identity (body of the .md file) stays verbatim from disk for reproducibility. If Context is null the message is just the Task — identical to today's behavior, so backward compatible.
Initial subagent history becomes:
[system] <frontmatter body verbatim>
[user] Context:
<Context, if provided>
Task:
<Task>
No length cap. The runtime decides if a context is too large — forcing a cap in policy would be premature.
5. Regenerate the three init-wizard seeds
IdentityStepViewModel.cs:307+ currently writes three JSON+MD pairs. Rewrite to emit three .md files in the new format.
6. Fail loudly on invalid files
Malformed frontmatter, unknown tool names (anything not in the ToolRegistry and not in SubAgentToolPolicy.GetAllowedUserFacingTools()), duplicate name across home+project scans — all raise a loud warning at scan time and the agent is skipped. No silent degradation. Per CLAUDE.md "no silent fallbacks" rule.
Security posture preserved
SubAgentToolPolicy.IsAllowedForUserFacing(toolName) still filters the tools: array at spawn time. User-facing agents stay restricted to the 4-tool allowlist (web_search, web_fetch, file_read, attach_file). If users drop in a Claude Code agent declaring tools: [Task, Bash, Read], those names either get translated (future work) or fail loudly with an error telling the user which tools they need to rename.
Critical files
src/Netclaw.Configuration/FileSubAgentDefinitionLoader.cs — rewrite for MD+frontmatter parsing (replace JSON reader with a YAML frontmatter extractor)
src/Netclaw.Configuration/SubAgentProfile.cs — existing shape is already right; only the file loader changes
src/Netclaw.Configuration/SubAgentDefinitionRegistry.cs — add project-overlay scan path
src/Netclaw.Configuration/SubAgentDiscoveryContextLayer.cs — unchanged (registry feeds it)
src/Netclaw.Daemon/Mcp/ToolIndexUpdater.cs:95-117 — enrich UpdateSubAgentDiscovery() with description + tools + example
src/Netclaw.Actors/SubAgents/SpawnAgentTool.cs:22-26 — add Context param with descriptive tool metadata
src/Netclaw.Actors/SubAgents/SubAgentActor.cs:109-110 — compose initial user message with optional Context prefix
src/Netclaw.Cli/Tui/Wizard/Steps/IdentityStepViewModel.cs:307+ — seed defaults in new format
src/Netclaw.Configuration/SubAgentToolPolicy.cs — unchanged; still the allowlist gate
Tests
- Parser: frontmatter extraction, type coercion, missing required fields → fail loud
- Scanner: home-only, home + project overlay, duplicate name across scopes → fail loud
- Tool allowlist: unknown names → loud warning, agent skipped
- Context injection:
null → original user-message shape preserved; populated → Context: prefix added
SubAgentSpawner integration: full spawn with Context propagates to SubAgentActor._history
- Seed parity: the three regenerated defaults parse and spawn identically to their JSON predecessors
Out of scope for this issue
Tracked in follow-on issues or existing open items:
Related
Context
Netclaw currently stores sub-agent definitions as pairs of
~/.netclaw/agents/<name>.json+<name>.mdfiles, parsed byFileSubAgentDefinitionLoader. Claude Code, OpenCode, and the publicSKILL.mdformat that Netclaw already adopted for skills (#150) all use a single markdown file per unit with YAML frontmatter. Sub-agents are the only artifact in Netclaw's install that still uses a two-file JSON+MD pattern.Two consequences:
~/.netclaw/agents/as-is. Users with an existinglocal-ai-preferences/agents/*.mdlibrary would be directly portable if Netclaw adopted the same shape.Additionally, the current
spawn_agenttool lets the frontline LLM pass only a free-formtaskstring — there's no way to specialize a sub-agent for a single invocation (focus on this competitor, use this workspace, limit scope to that repo) without authoring a whole new agent file.And there's no way to scope a sub-agent to a specific project/workspace. Every agent is global.
Zero-migration observation:
~/.netclaw/agents/on a fresh install holds only the three stock defaults seeded bynetclaw init. There is no user-authored content to preserve. The format change is free.Proposal
1. Single-file markdown format with YAML frontmatter
Replace the JSON+MD sidecar with one
.mdfile per agent:Frontmatter fields map 1:1 to the existing
SubAgentProfilerecord atsrc/Netclaw.Configuration/SubAgentProfile.cs— no new runtime types.2. Folder discovery and project overlay
Scanner walks
~/.netclaw/agents/*.mdat daemon startup and also<workspace>/.netclaw/agents/*.mdwhen a session has a workspace. Project-scoped agents layer on top of home-dir agents (byname). The home-dir scan is authoritative when no workspace is active.This lets repos ship sub-agents the same way they ship
.github/workflows/*.ymland pairs cleanly with #595 (session CWD + workspace context).3. Enriched
[available-subagents]context layerToday
ToolIndexUpdater.UpdateSubAgentDiscovery()atsrc/Netclaw.Daemon/Mcp/ToolIndexUpdater.cs:95-117emits:Enriched form includes the full description, tool list, and an example call using the new Context parameter so the frontline LLM sees both what each agent does and how to specialize it per call.
4. Optional
Contextparameter onspawn_agentExtend
SpawnAgentTool.Paramsatsrc/Netclaw.Actors/SubAgents/SpawnAgentTool.cs:22-26with one optional field:The tool description on the
Contextfield teaches the frontline LLM when to use it ("workspace details, the user's broader goal, facts the subagent would otherwise have to rediscover — do not duplicate the agent's built-in instructions").Context is prefixed onto the subagent's first user message, not injected into the system prompt. The agent's identity (body of the
.mdfile) stays verbatim from disk for reproducibility. IfContextis null the message is just the Task — identical to today's behavior, so backward compatible.Initial subagent history becomes:
No length cap. The runtime decides if a context is too large — forcing a cap in policy would be premature.
5. Regenerate the three init-wizard seeds
IdentityStepViewModel.cs:307+currently writes three JSON+MD pairs. Rewrite to emit three.mdfiles in the new format.6. Fail loudly on invalid files
Malformed frontmatter, unknown tool names (anything not in the
ToolRegistryand not inSubAgentToolPolicy.GetAllowedUserFacingTools()), duplicatenameacross home+project scans — all raise a loud warning at scan time and the agent is skipped. No silent degradation. Per CLAUDE.md "no silent fallbacks" rule.Security posture preserved
SubAgentToolPolicy.IsAllowedForUserFacing(toolName)still filters thetools:array at spawn time. User-facing agents stay restricted to the 4-tool allowlist (web_search,web_fetch,file_read,attach_file). If users drop in a Claude Code agent declaringtools: [Task, Bash, Read], those names either get translated (future work) or fail loudly with an error telling the user which tools they need to rename.Critical files
src/Netclaw.Configuration/FileSubAgentDefinitionLoader.cs— rewrite for MD+frontmatter parsing (replace JSON reader with a YAML frontmatter extractor)src/Netclaw.Configuration/SubAgentProfile.cs— existing shape is already right; only the file loader changessrc/Netclaw.Configuration/SubAgentDefinitionRegistry.cs— add project-overlay scan pathsrc/Netclaw.Configuration/SubAgentDiscoveryContextLayer.cs— unchanged (registry feeds it)src/Netclaw.Daemon/Mcp/ToolIndexUpdater.cs:95-117— enrichUpdateSubAgentDiscovery()with description + tools + examplesrc/Netclaw.Actors/SubAgents/SpawnAgentTool.cs:22-26— addContextparam with descriptive tool metadatasrc/Netclaw.Actors/SubAgents/SubAgentActor.cs:109-110— compose initial user message with optional Context prefixsrc/Netclaw.Cli/Tui/Wizard/Steps/IdentityStepViewModel.cs:307+— seed defaults in new formatsrc/Netclaw.Configuration/SubAgentToolPolicy.cs— unchanged; still the allowlist gateTests
null→ original user-message shape preserved; populated →Context:prefix addedSubAgentSpawnerintegration: full spawn with Context propagates toSubAgentActor._historyOut of scope for this issue
Tracked in follow-on issues or existing open items:
modelfield in frontmatter — blocked on multi-model provider architecture (filed as follow-on in this session)Task → spawn_agent,Bash → shell_execute,Read → file_readetc.)WorkingContext— tracked in SubAgentActor: tool file paths don't contribute to parent session WorkingContext #600AGENTS.md— tracked in Expand subagent delegation guidance #522Related
SKILL.mdformat; same pattern applied here