Skip to content

feat(desktop): two-layer agent prompts + dynamic AGENTS.md + MCP gating + harness-agnostic skill path#584

Merged
wpfleger96 merged 18 commits into
mainfrom
wpfleger/agent-personas
May 26, 2026
Merged

feat(desktop): two-layer agent prompts + dynamic AGENTS.md + MCP gating + harness-agnostic skill path#584
wpfleger96 merged 18 commits into
mainfrom
wpfleger/agent-personas

Conversation

@wpfleger96
Copy link
Copy Markdown
Collaborator

@wpfleger96 wpfleger96 commented May 14, 2026

Summary

Four changes that give managed agents reliable, CLI-first Sprout platform awareness and fix MCP tool injection for non-sprout-agent harnesses.

1. Two-layer prompt architecture

Adds a shared [Base] prompt layer injected by the ACP harness on every turn — Sprout CLI reference, communication patterns, startup recovery, and workspace context. Every agent gets platform orientation without duplicating it across personas.

  • base_prompt.md compiled in via include_str!(), prepended as [Base] before [System] (persona) on all turns including heartbeats and initial messages
  • FormatPromptArgs struct replaces positional params on format_prompt()
  • SPROUT_ACP_NO_BASE_PROMPT disables it; SPROUT_ACP_BASE_PROMPT_FILE overrides the compiled-in default
  • Solo/Kit/Scout built-in personas: Sprout-specific bits removed (now in base layer), model/provider reverted to None

2. Dynamic AGENTS.md regeneration

~/.sprout/AGENTS.md now dynamically reflects workspace state. A managed section between <!-- BEGIN/END SPROUT MANAGED --> markers is regenerated whenever agents, personas, or workspace config change.

  • regenerate_nest_context() renders an active agents table into the managed section
  • try_regenerate_nest() hooks in 10 Tauri command functions — all outside lock scope
  • Marker-based merge preserves user edits outside the managed section
  • Atomic writes via tempfile::NamedTempFile; app-startup hook for first-launch currency

3. CLI-first agent prompts + versioned nest file refresh

Completes the MCP-to-CLI migration for all agent-facing content compiled into the binary. Adds a version-gated refresh mechanism so existing installs receive template updates on upgrade.

  • Stripped all MCP function-call syntax from base_prompt.md, nest_agents.md, context hints in queue.rs, and heartbeat prompt in lib.rs — agents now exclusively reference sprout CLI commands
  • nest_agents.md trimmed from 80 to 55 lines: removed sections now covered by base_prompt.md and SKILL.md
  • NEST_AGENTS_VERSION / NEST_SKILL_VERSION constants gate a startup refresh in ensure_nest_at — stale static content is atomically overwritten when the binary ships newer templates
  • Managed section and user content below it are preserved across refreshes

4. MCP server gating + harness-agnostic skill path

Fixes MCP tool injection so only providers that explicitly declare an MCP server get one. Moves the skill file to the open-standard .agents/skills/ path with data-driven symlinks for all known harnesses.

  • Providers with mcp_command: None (goose, Claude Code, Codex) no longer get sprout-mcp-server injected — empty string propagates through the creation/spawn/ACP chain to produce zero MCP servers
  • Only sprout-agent (mcp_command: Some("sprout-dev-mcp")) retains MCP tools
  • SKILL.md moved from .claude/skills/sprout-cli/ to .agents/skills/sprout-cli/ (canonical path); relative symlinks created for each known harness (.claude/skills/, .goose/skills/, .codex/skills/) via KnownAcpProvider.skill_dir
  • Migration logic preserves user-edited SKILL.md content when converting from the old real directory
  • Removed duplicate @mention formatting guideline from nest_agents.md (already in base_prompt.md)
  • Version constants bumped to 3 to trigger refresh on existing installs

@wpfleger96 wpfleger96 requested a review from wesbillman as a code owner May 14, 2026 18:53
Comment thread crates/sprout-acp/src/base_prompt.md Outdated
Comment thread desktop/src-tauri/src/managed_agents/personas.rs Outdated
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch 2 times, most recently from 23b4eb0 to 035d4c0 Compare May 15, 2026 19:43
wpfleger96 added a commit that referenced this pull request May 15, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 changed the title feat: two-layer prompt architecture for managed agents feat(desktop): two-layer agent prompt architecture + dynamic AGENTS.md regeneration May 15, 2026
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch 5 times, most recently from 350aa8a to 95515fd Compare May 16, 2026 03:26
wpfleger96 added a commit that referenced this pull request May 18, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 95515fd to 8505a9e Compare May 18, 2026 19:34
wpfleger96 added a commit that referenced this pull request May 18, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 8505a9e to 00c1a38 Compare May 18, 2026 22:37
wpfleger96 added a commit that referenced this pull request May 18, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 00c1a38 to 58a3ace Compare May 18, 2026 23:13
wpfleger96 added a commit that referenced this pull request May 18, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 58a3ace to 6db3ab4 Compare May 18, 2026 23:43
wpfleger96 added a commit that referenced this pull request May 18, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 6db3ab4 to bd11938 Compare May 18, 2026 23:48
@wpfleger96 wpfleger96 marked this pull request as draft May 19, 2026 21:31
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from bd11938 to 682d8fe Compare May 19, 2026 21:49
wpfleger96 added a commit that referenced this pull request May 19, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
wpfleger96 added a commit that referenced this pull request May 20, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch 2 times, most recently from 76c6997 to 42c04f0 Compare May 20, 2026 20:58
wpfleger96 added a commit that referenced this pull request May 20, 2026
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
wpfleger96 added 14 commits May 26, 2026 17:34
BackendKind and RespondTo are only used in test helper constructors.
Fixes identified by crossfire review (Codex + Gemini) and plan author:

- Use tempfile::NamedTempFile for atomic writes instead of deterministic
  .tmp path that races under concurrent regeneration triggers
- Enforce ordered BEGIN/END marker search with line-start anchoring to
  prevent inverted slicing when markers are out of order or mid-line
- Strip orphan BEGIN markers before appending new managed section
- Escape pipe and newline characters in agent/persona names to prevent
  Markdown table corruption
- Rename "Role" column to "Persona" (display_name is a name, not a role)
- Move regenerate_nest_context calls outside lock scope in all mutation
  hooks to reduce lock hold time and eliminate future deadlock risk
- Add 7 adversarial unit tests: marker ordering, orphan cleanup,
  duplicates, code-block false positives, pipe/newline escaping,
  idempotency
nest.rs grew to 770 lines with regenerate_nest_context, marker
helpers, and 19 unit tests. The 500-line default is too tight for
this file — override to 800 following the established pattern.
…uplication

base_prompt.md (injected every turn via [Base]) already has a comprehensive
CLI reference table covering all 11 command groups. The CLI_QUICK_REFERENCE
constant in nest.rs was a less complete duplicate (4 commands). AGENTS.md's
dynamic section now focuses on what's unique to it: active agents and
workspace info.

Also bumps personas.rs file size override from 900 to 950 to accommodate
the merge_personas inequality checks added in the review-fix commit.
Review surfaced 10 findings across 5 independent sources (3 Claude
specialists + Codex + Gemini). Key changes:

- Fix incorrect CLI syntax in base_prompt.md (missing --channel flag,
  positional arg that should be named) and stale nest_agents.md reference
- Apply line-start validation to END_MARKER in find_managed_markers,
  matching the BEGIN_MARKER contract documented in the function comment
- Extract prepend_base_prompt helper to deduplicate [Base] injection
  across format_prompt, dispatch_heartbeat, and initial_message paths
- Move base_prompt_file read from main.rs panic to Config::from_cli
  with proper ConfigError propagation and 1 MB size guard
- Extract try_regenerate_nest helper to consolidate 11 identical
  error-handling blocks across command files
- Sanitize relay_url before AGENTS.md injection (strip CR/LF)
- Fix greedy newline strip in strip_orphan_begin_marker
- Add doc comments for PromptContext.base_prompt lifetime constraint
rustfmt fixes after rebase onto main (extra blank lines in lib.rs and
nest.rs). Bump nest.rs file size override from 800 to 960 to account
for skill file init code merged from #613.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Agent-facing prompts (base prompt, context hints, heartbeat, AGENTS.md
template) still referenced MCP function-call syntax despite the CLI
migration. Additionally, existing users never received template updates
because nest files used write-once semantics.

Strip all MCP references from the four agent-facing sources compiled
into the binary. Add version-gated refresh to ensure_nest_at so
AGENTS.md static content (above managed markers) and SKILL.md are
updated on binary upgrades while preserving the dynamic agent table.



# Conflicts:
#	desktop/scripts/check-file-sizes.mjs
upsert_managed_section always wrote via NamedTempFile even when the
new content was identical, bumping mtime on every app launch. Add an
early return when content matches to make the function truly
idempotent at the filesystem level.
…c skill path

Agents whose provider declares mcp_command: None (goose, Claude Code,
Codex) were getting sprout-mcp-server injected via the ACP session,
giving them callable MCP tools that override text-based CLI instructions.
Reinterpret None as "no MCP needed" and gate at creation time (empty
string stored) and spawn time (empty env var -> empty vec from
build_mcp_servers). Only sprout-agent retains its sprout-dev-mcp.

Move SKILL.md from .claude/skills/sprout-cli/ to the open-standard
.agents/skills/sprout-cli/ with a relative symlink for Claude Code
compat. Migrate pre-existing real directories, preserving user-edited
content. Remove duplicate @mention guideline from nest AGENTS.md (already
in base_prompt.md). Bump version constants to 3 to trigger refresh.
Only Claude Code got a symlink for skill discovery; Goose, Codex, and
future harnesses were ignored. Add a skill_dir field to
KnownAcpProvider so each harness declares its own skill path, then
loop over all providers to create relative symlinks from each
harness dir to the canonical .agents/skills/sprout-cli.

Moves the real SKILL.md to .agents/skills/sprout-cli (agent-neutral
canonical path) with symlinks at .goose/skills/, .claude/skills/,
and .codex/skills/.



# Conflicts:
#	desktop/src-tauri/src/managed_agents/discovery.rs
Doc comment on SPROUT_CLI_SKILL_MD still said "Claude Code skill" and
the ensure_nest_at docstring used .claude/skills as an inline example
rather than describing the generic provider pattern.
ensure_skill_symlinks_skip_real_directory assumed no migration path;
refresh_skill_md_if_stale converts real dirs at .claude/skills/sprout-cli
to symlinks, so the test was updated to match the actual merged behavior.
Bump nest.rs file-size override to accommodate new provider symlink tests.
Fixes cargo fmt violation at nest.rs:165 — extra blank line between
refresh_skill_md_if_stale and the permissions comment block.
@wpfleger96 wpfleger96 force-pushed the wpfleger/agent-personas branch from 726139e to 7f39683 Compare May 26, 2026 21:36
@wpfleger96 wpfleger96 marked this pull request as ready for review May 26, 2026 21:41
@wpfleger96 wpfleger96 requested a review from a team as a code owner May 26, 2026 21:41
@wpfleger96 wpfleger96 merged commit b13db17 into main May 26, 2026
16 checks passed
@wpfleger96 wpfleger96 deleted the wpfleger/agent-personas branch May 26, 2026 21:46
wpfleger96 added a commit that referenced this pull request May 26, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 26, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 26, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 26, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 27, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 27, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 27, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request May 27, 2026
Existing agents created before #584 have mcp_command = "sprout-mcp-server"
stored in managed-agents.json. At spawn, this registers the full 49-tool
messaging MCP server — goose prefers live tools over CLI instructions.

On every launch, reconcile_provider_mcp_commands() reads the JSON, looks
up each agent_command in the discovery table, and patches mcp_command to
the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp"
for sprout-agent). Unknown/custom agents are left untouched.

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants