Skip to content

flow init: install SKILL.md to Codex / Cursor / Gemini skill dirs#45

Open
vishnukv-facets wants to merge 3 commits into
mainfrom
feat/multi-agent-skill-install
Open

flow init: install SKILL.md to Codex / Cursor / Gemini skill dirs#45
vishnukv-facets wants to merge 3 commits into
mainfrom
feat/multi-agent-skill-install

Conversation

@vishnukv-facets
Copy link
Copy Markdown
Contributor

Summary

flow init / flow skill install / flow skill update / flow skill uninstall / maybeAutoUpgradeSkill now operate over a list of per-agent install targets instead of the single hard-coded ~/.claude/skills/flow/. Claude stays always-on; Codex, Cursor, and Gemini are auto-discovered when their parent home dirs exist on disk.

Gemini specifically installs to ~/.agents/skills/flow/ (not ~/.gemini/skills/) because Gemini CLI scans both the shared ~/.agents/skills/ root and its per-agent dir — writing to both would double-load the skill.

Why

The shipped SKILL.md already documents Codex behavior, but the binary only writes to ~/.claude/. Other agents support the same SKILL.md format via their own skills/ dirs and the shared ~/.agents/skills/ root. Filling those dirs is the minimum change required for those IDEs to discover and load the flow skill — no flow do runtime changes, no DB schema work, no agent-dispatch logic.

Implementation

  • skillTarget separates presenceSubdir (existence check) from installSubdir (where SKILL.md goes). Identical for Claude/Codex/Cursor; Gemini's presence dir is ~/.gemini/ but install dir is ~/.agents/.
  • skillTargets() discovers live targets at runtime. Claude is always included (preserves legacy behavior); others are auto-discovered. We never eagerly create ~/.<agent>/ for agents the user doesn't have.
  • Per-agent VERSION sidecars (~/.<install-dir>/skills/flow/VERSION) so each agent upgrades independently.
  • Install is batch-or-nothing: if any target SKILL.md already exists, plain install refuses without --force. One mental model, no partial-install states.
  • installSessionStartHook stays Claude-only — other agents have their own hook mechanisms (or none), out of scope here.
  • Codex 1024-byte description limit: paired change trims the embedded SKILL.md's description: frontmatter block so Codex will actually load the skill. TestSkillFrontmatterDescriptionFitsCodexLimit pins the invariant.

What's intentionally out of scope

  • flow do --agent <codex|opencode|...> runtime dispatch — partially built on unmerged facets/codex-support branch, separate work.
  • session_provider DB column.
  • Agent-specific hook installation beyond Claude's SessionStart.
  • Opencode support — its ~/.opencode/ layout (node bin tree) doesn't match the global-skills-dir pattern; revisit if opencode adopts one.

Test plan

  • make test passes (all packages, including 10 new tests in internal/app/skill_test.go):
    • TestSkillTargetsClaudeAlwaysPresent — Claude always in the list
    • TestSkillTargetsDiscoversInstalledAgents — Codex/Cursor/Gemini auto-discovered when their presence dirs exist
    • TestSkillInstallWritesAllAgentTargets — install lands in all 4 dirs; Gemini lands at ~/.agents/, not ~/.gemini/skills/
    • TestSkillInstallSkipsAbsentAgents~/.cursor, ~/.gemini, ~/.agents NOT auto-created when their presence dirs are missing
    • TestGeminiInstallsToAgentsRoot — Gemini redirect in isolation
    • TestSkillInstallRefusesIfAnyTargetExists — batch-or-nothing safety
    • TestSkillUpdateOverwritesAllAgentsupdate refreshes everywhere
    • TestSkillUninstallRemovesAllAgents — uninstall sweeps all agents
    • TestMaybeAutoUpgradeRefreshesAllAgents — version mismatch refreshes every target
    • TestSkillFrontmatterDescriptionFitsCodexLimit — Codex 1024-byte description limit
  • End-to-end smoke against fake \$HOME:
    • All 4 agent dirs present → SKILL.md lands at 4 correct locations (Gemini at ~/.agents/)
    • Re-run is idempotent
    • Only ~/.codex/ present → install to Claude + Codex; ~/.cursor, ~/.gemini, ~/.agents NOT created
    • Only ~/.gemini/ present → install to Claude + ~/.agents/; ~/.gemini/skills/ stays empty
  • Reviewer sanity check: confirm the install matches your local agent layout assumptions (Claude always-on, others discover-only).

Closes #43

🤖 Generated with Claude Code

vishnukv-facets and others added 2 commits May 13, 2026 17:06
…w init

`flow init`, `flow skill install`, `flow skill update`, `flow skill
uninstall`, and `maybeAutoUpgradeSkill` now operate over a list of
per-agent install targets instead of the single hard-coded
`~/.claude/skills/flow/`. Claude stays always-on; Codex, Cursor, and
Gemini are auto-discovered by checking whether their `~/.<agent>/`
parent dir exists. SessionStart hook installation stays Claude-only.

Why: the installed flow skill already documents Codex behavior, but
the binary only writes to ~/.claude/. Filling Codex/Cursor/Gemini skill
dirs is the minimum change required for those agents to discover and
load the flow skill — no `flow do` runtime changes, no DB schema work,
no agent dispatch. Auto-discover means we never pollute home dirs for
agents the user doesn't have installed.

Implementation:
- `skillTargetCandidates` enumerates the four known agents (Claude,
  Codex, Cursor, Gemini); `skillTargets()` filters to those whose
  parent dir is present.
- Per-agent VERSION sidecars at `~/.<agent>/skills/flow/VERSION` let
  auto-upgrade refresh each agent independently.
- Install is batch-or-nothing: if any target SKILL.md already exists,
  plain `install` refuses without `--force` — one mental model, no
  partially-installed states.
- Uninstall iterates the candidate list (not the live target set) so
  we still clean up if an agent's home dir was removed between install
  and uninstall.

Tests: 8 new tests in skill_test.go cover discovery, multi-target
install, absent-agent skipping, batch-or-nothing refusal, update
overwrite, uninstall sweep, and auto-upgrade refresh.

Closes #43

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… to Codex limit

Gemini CLI scans both `~/.gemini/skills/` and the shared
`~/.agents/skills/` root, so installing into both double-loads the
skill. Redirect Gemini's install target to `~/.agents/skills/flow/`
only. Presence detection still keys on `~/.gemini/` — we only install
to the shared root when the user actually has Gemini installed.

Implementation:
- skillTarget gains separate `presenceSubdir` (existence check) and
  `installSubdir` (where SKILL.md goes). For every agent except
  Gemini these are the same. Gemini's install lands at `.agents/`.
- New `resolvedSkillTarget` carries absolute paths after $HOME is
  resolved so call sites don't recompute.
- Uninstall iterates candidates using the install path, so the
  Gemini cleanup happens under `~/.agents/skills/flow/` not `~/.gemini/`.

Codex frontmatter limit: a paired change in this commit trims the
embedded SKILL.md's `description:` block to fit Codex's 1024-byte
limit. `TestSkillFrontmatterDescriptionFitsCodexLimit` pins the
invariant — both the trim and the test are needed for Codex to load
the skill at all.

Tests:
- TestGeminiInstallsToAgentsRoot: presence-only Gemini lands at
  ~/.agents/, never under ~/.gemini/skills/.
- TestSkillInstallWritesAllAgentTargets updated to assert
  Gemini install path = ~/.agents/skills/flow/SKILL.md and the
  negative property that ~/.gemini/skills/ stays empty.
- TestSkillInstallSkipsAbsentAgents: ~/.agents is NOT auto-created
  when Gemini isn't present.
- Update/Uninstall/AutoUpgrade tests use the right install dirs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vishnukv-facets vishnukv-facets requested a review from rr0hit May 14, 2026 06:10
@anshulsao
Copy link
Copy Markdown
Contributor

One design pushback before going deeper — the agent-detection logic and the auto-upgrade behavior compose into a papercut worth fixing in this PR.

Current behavior:

  • Non-Claude agents are detected via ~/.<agent>/ presence at install time
  • maybeAutoUpgradeSkill skips any target whose SKILL.md isn't already on disk (treats absence as "user opted out")

Combined effect: if a user runs flow init with only Claude, then later installs Codex CLI, the flow skill never lands in ~/.codex/skills/flow/ until they manually run flow skill install --force / flow skill update. The PR's "agents discover SKILL.md when they boot" promise only holds at the instant of the first install — every subsequent agent install hits a silent gap.

Proposal: drop detection entirely. Install all four locations always, on both init and auto-upgrade.

init / upgrade tick
        │
        ▼
for each of [claude, codex, cursor, gemini]:
    write SKILL.md + per-target VERSION sidecar

Why this is fine:

  • SKILL.md is a few KB × 4 — disk cost is nothing.
  • Eliminates the "I installed Codex last week, where's flow?" failure mode.
  • Auto-upgrade becomes trivially correct: every agent always carries the current version, no opt-out heuristic needed.
  • Simpler code: plain loop, no presenceSubdir / installSubdir split, no skillTargets() discovery conditional.

The one real cost is pre-creating ~/.agents/ (Gemini's shared multi-agent skills root) for users who don't have Gemini installed. Two ways to handle it:

  1. Accept it. ~/.agents/ is content-scanned by other tools, not presence-scanned, so an empty-ish flow/ subtree is harmless.
  2. Drop a ~/.agents/.flow-managed marker so external scanners can disambiguate "flow created this" from "the user has Gemini."

Net trade: install + auto-upgrade are seamless, the discovery code disappears, and the post-install-agent gap closes. Worth flipping before merge IMO — happy to discuss if there's a constraint I'm missing.

…nstall opt-out

maybeAutoUpgradeSkill used to treat a missing SKILL.md as "user opted
out" and never reinstall — fine when Claude was the only target, but
under multi-agent it meant installing Codex (or any other CLI) after
flow init left ~/.codex/skills/flow/ empty until the user manually ran
flow skill install --force. Anshul flagged this on PR #45.

Treat missing SKILL.md as "needs install" instead. The legitimate
opt-out case is now expressed by a ~/.flow/.skill-uninstalled marker
that flow skill uninstall drops on success and flow skill install /
flow init clear. A fresh-binary safety gate (flowEngagementDetected)
keeps the first run of an unconfigured binary from silently creating
SKILL.md before the user runs flow init.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vishnukv-facets
Copy link
Copy Markdown
Contributor Author

Good catch — gap is real and worth fixing. Pushed 1ac5a44.

Took the diagnosis but not the full prescription. Going wider on the trigger (treat missing SKILL.md as "install it"), keeping presence-based discovery for the targets. Reasoning: pre-creating ~/.codex/ / ~/.cursor/ / ~/.gemini/ / ~/.agents/ for users who don't have those agents is a small filesystem-pollution footgun — some CLIs use ~/.<self>/ existence as a "first run" signal, and the ~/.agents/.flow-managed marker you proposed for Gemini is itself a tell that even the simpler design felt like it needed an apology.

What changed in maybeAutoUpgradeSkill:

  1. SKILL.md missing for a detected target → install fresh (was: skip as opt-out). Closes the Codex-installed-after-flow init gap.
  2. Legitimate opt-out is now a real signal, not an inference from filesystem state: ~/.flow/.skill-uninstalled marker, set by flow skill uninstall (gated on anyRemoved so no-op uninstalls stay no-ops), cleared by flow skill install / flow init. Auto-upgrade bails on marker before reading anything else.
  3. Fresh-binary safety gate (flowEngagementDetected): without a flow.db or an existing SKILL.md somewhere, auto-upgrade is a no-op. Prevents flow list on a virgin binary from silently materializing ~/.claude/skills/flow/SKILL.md alongside the "not initialized" error.

Net effect: install Codex three weeks after flow init → next flow command picks it up automatically; flow skill uninstall stays sticky across binary upgrades; freshly-downloaded binary touches zero files until the user does something explicit.

Tests: 4 new (TestMaybeAutoUpgradeInstallsNewlyDetectedAgent, TestMaybeAutoUpgradeRespectsUninstallMarker, TestSkillInstallClearsUninstallMarker, TestSkillUninstallNoopDoesNotSetMarker), 322 passing.

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.

flow init: install SKILL.md to Codex / Cursor / Gemini skill dirs, not only ~/.claude/

2 participants