Skip to content

feat: add Sprout persona pack symlink management - #84

Merged
wpfleger96 merged 7 commits into
mainfrom
wpfleger/sprout-pack-symlinks
Jun 4, 2026
Merged

feat: add Sprout persona pack symlink management#84
wpfleger96 merged 7 commits into
mainfrom
wpfleger/sprout-pack-symlinks

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Adds a SproutTool that 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.json at runtime rather than hardcoded, so any pack definition dropped into config/sprout/ works without code changes. _is_specialized_path now accepts the target owner and short-circuits for non-Agent (Tool) instances to avoid the /agents/ path filter collision.

  • SproutTool reads id from config_dir/sprout/.plugin/plugin.json, gracefully returns no symlinks if the manifest is missing or malformed
  • _is_specialized_path uses isinstance(target_owner, Agent) guard to prevent Tool symlink paths from being filtered out
  • needs_cache overridden to False since Sprout config is symlink-only (no file rendering)

Related: sprout#852 (full pack CRUD in the Sprout UI)

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
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.
wpfleger96 and others added 2 commits June 4, 2026 16:40
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.
@wpfleger96
wpfleger96 merged commit fb43a64 into main Jun 4, 2026
9 checks passed
@wpfleger96
wpfleger96 deleted the wpfleger/sprout-pack-symlinks branch June 4, 2026 21:14
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.

1 participant