feat: add Sprout persona pack symlink management - #84
Merged
Conversation
The Sprout persona pack living in src/ai_rules/config/sprout/ had no install logic — the pack only reached Sprout via manual UI import, which copies files and breaks the single-source-of-truth managed by ai-rules. Adds a SproutTool target that symlinks the pack directory into both the production (xyz.block.sprout.app) and dev (xyz.block.sprout.app.dev) Sprout Application Support paths on macOS. Worktree dev builds inherit automatically via Sprout's own shared-agent-data symlink mechanism, so no additional targets are needed. Also fixes _is_specialized_path() in ConfigComponent to gate on isinstance(target_owner, Agent) rather than substring-matching path strings — the old check would have silently filtered Sprout's pack paths (which contain /agents/) from the config install pipeline.
* feat: add agents_md profile field for AGENTS.md content appending The base AGENTS.md is shared across all agents and profiles with no way to add profile-specific context (e.g., work VPN hints, team tooling). New `agents_md` field on profiles accumulates through the inheritance chain (parent first, child appends) and is merged with the base file at install time, written atomically to `~/.ai-agent-rules/cache/shared/AGENTS.md`, and symlinked from `~/AGENTS.md`. All other agents resolve transitively through `~/AGENTS.md` via `@` includes so no per-agent changes are needed. Also fixes CodexAgent, which was the only agent pointing its AGENTS.md symlink directly at the base file instead of using `@~/AGENTS.md` like every other agent. * fix: resolve CI failures from agents_md feature Mypy flagged `get_merged_agents_md_path()` returning `Path | None` in the `symlinks` property — assert non-None when `needs_agents_md_cache` is true (the two conditions are logically equivalent). E2E fixture was missing `codex/AGENTS.md` in the config dir, same gap already fixed in the unit conftest. * refactor: extract shared format_unified_diff and wire up AGENTS.md cache diff The Rich-formatted unified diff logic was duplicated verbatim between get_content_diff() and get_cache_diff(). Extract into a single format_unified_diff() helper in symlinks.py, refactor all three callers (symlinks, targets, MCP), and wire up AgentsMdComponent to show actual content diffs instead of just "stale." MCP diffs now get the same green/red coloring as all other components. Also adds agents_md content to the work profile for VPN/uv hints. * fix: address crossfire review feedback for agents_md PR - Fix MCP format_diff argument order (installed→expected, not reversed) and restore dropped server name header in diff output - Fix ctx.console → console in MCP status to respect output buffering - Extract get_expected_agents_md_content() on SharedAgent, eliminating 3x duplicated merge logic and handling empty base AGENTS.md correctly - Simplify is_agents_md_cache_stale() to pure content comparison, removing mtime checks with parent-profile blind spot - Auto-include SharedAgent in filtered installs (--agents codex) since all agents depend on ~/AGENTS.md via @-includes - Add agents_md to README profile key documentation - Fix ruff format-check CI failure
wpfleger96
marked this pull request as ready for review
June 4, 2026 18:56
The pack ID was hardcoded as SPROUT_PACK_ID in platform.py, coupling ai-rules to a specific pack. Now SproutTool reads the id field from .plugin/plugin.json at runtime, so any pack definition dropped into config/sprout/ works without code changes. Gracefully returns no symlinks if the manifest is missing or malformed.
Avatars are already base64-encoded inline in the persona .persona.md frontmatter. The raw PNGs in avatars/ were unreferenced by any pack file and added 756 KB of dead weight.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
SproutToolthat symlinks the bundled persona pack into both production (xyz.block.sprout.app) and dev (xyz.block.sprout.app.dev) macOS Application Support directories so pack personas appear in Sprout without manual import.The pack ID is read from
.plugin/plugin.jsonat runtime rather than hardcoded, so any pack definition dropped intoconfig/sprout/works without code changes._is_specialized_pathnow accepts the target owner and short-circuits for non-Agent (Tool) instances to avoid the/agents/path filter collision.SproutToolreadsidfromconfig_dir/sprout/.plugin/plugin.json, gracefully returns no symlinks if the manifest is missing or malformed_is_specialized_pathusesisinstance(target_owner, Agent)guard to prevent Tool symlink paths from being filtered outneeds_cacheoverridden toFalsesince Sprout config is symlink-only (no file rendering)Related: sprout#852 (full pack CRUD in the Sprout UI)