install-paca-skills.sh: non-lossy Agent Skills distribution - #454
Conversation
Claude Code, Gemini CLI, and Cursor now all natively read the agentskills.io SKILL.md folder format (verified against each tool's own current official docs, not assumed) -- so install-paca-skills.sh now writes each skill verbatim, frontmatter and all, into that native folder for all three, instead of stripping frontmatter into a flat .md/.toml command file: - Claude Code -> ~/.claude/skills/<name>/SKILL.md (was ~/.claude/commands/<name>.md) - Gemini CLI -> ~/.gemini/skills/<name>/SKILL.md (was ~/.gemini/commands/<name>.toml) - Cursor -> <project>/.cursor/skills/<name>/SKILL.md (was .cursor/commands/<name>.md) AGENTS.md is unchanged: it's a single shared file, not a per-skill directory, so it still strips frontmatter and re-shapes each skill into a plain markdown section. Also fixes a latent fidelity bug this change surfaced: the bundled-skill fetch used `jq -r '.content'`, which appends its own trailing newline regardless of whether the source string already ends in one -- every previous target reshaped or stripped the body anyway so the extra blank line was invisible, but it broke byte-for-byte verbatim copying for the new targets above. Switched to `jq -j` (join-output, no added newline). Verified against a live instance: output now matches the raw API response byte-for-byte (confirmed via diff and sha256sum across all three targets). Implements point 1 of Paca-AI#453, alongside the separate com.gorlix.project-skills plugin (point 3, gorlix/paca-plugin-project-skills) which implements point 2/3. Testing: - shellcheck clean (fixed one pre-existing SC2016 info-level note in a line this change didn't otherwise touch, now that this script is linted for the first time -- see scripts-pr-ci.yml). - New install-paca-skills-smoke CI job: runs the real script against a local fixture HTTP server (not a stubbed curl) and asserts byte-exact SKILL.md content for every native target, plus AGENTS.md's frontmatter-stripped section. - Manually verified against a real docker-compose.dev.yml instance: ran the script for real, diffed and sha256-compared every installed file against the live GET /api/v1/skills response. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Marking this draft while I verify the Gemini side beyond docs. The official Gemini CLI docs describe native |
Live-tested this branch's Gemini change on a real machine and found the docs weren't the whole story: the installed tool there is Google's Antigravity IDE, documented as Gemini CLI's successor, and a skill written only to the native ~/.gemini/skills/<name>/SKILL.md folder did not show up as an available skill -- confirmed both before and after restarting the app, ruling out a simple caching/reload issue. Rather than trust "the docs say it works" over an actual negative result, this restores the pre-existing ~/.gemini/commands/<name>.toml write (frontmatter stripped, re-shaped into Gemini's TOML command format -- exactly what this script did before this branch) alongside the new native folder, instead of replacing it. Whichever mechanism a given Gemini installation actually reads, the skill is available either way; nobody loses functionality they had before this branch. Real terminal Gemini CLI itself wasn't available to test against here, so this stays a belt-and-suspenders fallback rather than a conclusion that the native path is broken -- it may well work correctly there. The point is not shipping a hard cutover for a target this couldn't be verified against end-to-end. Updates: install-paca-skills.sh (GEMINI_LEGACY_DIR + restored TOML generation in install_one_skill), docs/guides/install-skills.md (table + uninstall section), scripts-pr-ci.yml (install-paca-skills-smoke now also asserts the legacy .toml is written). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Update: confirmed on the real Antigravity IDE install (restarted the app too, ruling out caching) that the native Still draft — want to see a real terminal Gemini CLI (not just Antigravity) confirm the native path before calling this fully settled, and haven't independently re-verified Claude Code/Cursor on a live install yet either. |
There was a problem hiding this comment.
ℹ️ No critical issues — the change is sound and the CI byte-exactness assertion genuinely guards the regression it targets. Two minor nits inline, plus stale-doc and legacy-cleanup notes in the body.
Reviewed changes
- Verbatim
SKILL.mdcopies replace lossy per-platform reshaping —install_one_skillnowmkdir -ps a per-skill directory andcps the rawSKILL.mdfor Claude Code (~/.claude/skills/), Gemini CLI (~/.gemini/skills/), and Cursor (<project>/.cursor/skills/); only AGENTS.md still strips frontmatter. The unusedtoml_basic_stringhelper was removed. jq -r→jq -jfor the bundled-skill fetch — stopsjqappending a trailing newline to content that already ends in one, keeping the copiedSKILL.mdfiles byte-identical to what the API serves.docs/guides/install-skills.md— platform table and Uninstall section updated to the new<name>/SKILL.mdlayout..github/workflows/scripts-pr-ci.yml— shellcheck coverage extended toinstall-paca-skills.sh, plus a newinstall-paca-skills-smokejob that runs the real script against a local fixture HTTP server and asserts byte-exact output for every native target and the frontmatter-stripped AGENTS.md section.
I verified the disposition-changing claims directly: the three native target locations were confirmed against the current official docs for each tool (~/.claude/skills/<name>/SKILL.md, ~/.gemini/skills/<name>/SKILL.md, <project>/.cursor/skills/<name>/SKILL.md, all accepting plain agentskills.io frontmatter), the script stays shellcheck-clean, and I replayed the smoke test locally — it passes on this branch and fails exactly against the old jq -r behavior (want + "\n"), so the test genuinely guards the lossy-bug class this PR fixes.
⚠️ Stale install-path references outside the diff
README.md:343 still describes the layout this PR removes (~/.claude/commands/, ~/.gemini/commands/, .cursor/commands/), and apps/web/src/components/projects/agents/acp-bridge-setup.tsx:87 has a comment naming ~/.claude/commands, ~/.gemini/commands. The README is the front door for the curl | bash install path this change revamps, so it now actively contradicts the updated guide.
Technical details
# Stale install-path references
## Affected sites
- README.md:343 — lists the removed `~/.claude/commands/`, `~/.gemini/commands/`, `.cursor/commands/` layouts
- apps/web/src/components/projects/agents/acp-bridge-setup.tsx:87 — comment names `~/.claude/commands, ~/.gemini/commands`
## Required outcome
- Both should describe the new Agent Skills layouts (`~/.claude/skills`, `~/.gemini/skills`, `.cursor/skills`), matching docs/guides/install-skills.md.
## Suggested approach (optional)
- README.md:343 already links users to docs/guides/install-skills.md, so it could drop the inline path list and defer to the guide; the tsx comment is a one-line path swap.ℹ️ Legacy on-disk files from the previous script are left behind
Users who already ran the old installer still have ~/.claude/commands/paca*.md, ~/.gemini/commands/paca*.toml, and <project>/.cursor/commands/paca*.md. The new installer writes the new layout alongside without removing the old format, and the updated Uninstall docs only remove the new paths — so existing users end up with duplicated (and now-stale) slash-command surface, and "uninstall" misses the legacy files entirely. Whether the installer should best-effort clean the old format or the docs should note a one-time rm is a scoping call for the author.
Technical details
# Legacy-format cleanup on upgrade
## Affected sites
- scripts/install-paca-skills.sh — `install_one_skill()` writes only the new `<name>/SKILL.md` layout
- docs/guides/install-skills.md "Uninstall" — removes only new-format paths
## Required outcome
- Re-running the installer over an old install should not leave duplicated legacy commands; uninstall should remove everything the installer ever wrote.
## Open questions for the human
- Add best-effort legacy cleanup (e.g. `rm -f ~/.claude/commands/paca-* ~/.gemini/commands/paca-* <project>/.cursor/commands/paca-*`) when writing the new format, or document the one-time removal instead?DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No new issues in the delta commit — the legacy fallback restore is sound and its CI assertions pass locally. The items from the prior review (stale README/tsx install-path refs, legacy-cleanup question, two inline nits) remain open.
Reviewed changes
- Gemini CLI legacy TOML restored as a fallback alongside the native folder — commit
39d43e9brings backtoml_basic_string,GEMINI_LEGACY_DIR="${HOME}/.gemini/commands", and thedescription/prompt = '''TOML write (still guarded by'''detection: a body containing'''skips the legacy command while the native folder is still written), plus the matchingmkdir, platform-menu, summary-echo, and Uninstall-docs updates. - CI smoke assertions extended — the
install-paca-skills-smokejob now also requires~/.gemini/commands/<name>.tomlto exist withdescription =andprompt = '''lines for both fixtures. - Docs and header comment updated — the Gemini CLI row in the platform table and script header now document the native + legacy dual-write and the Antigravity rationale.
I replayed the smoke test against the new head: native-folder byte-exactness still holds, both fixtures' legacy .toml files are written with the correct description = "..." / prompt = '''...''' shape, and all new assertions pass. The dual-write is a deliberate, well-documented tradeoff (belt-and-suspenders for the Gemini→Antigravity transition per the author's live testing), and the ''' guard prevents the one case that could produce malformed TOML from silently being written. Nothing new to flag.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
The previous commit's fallback (keep the legacy TOML alongside the new
native folder) was a hedge against not knowing why Antigravity wasn't
picking up ~/.gemini/skills/. Investigated further instead of settling
for the hedge, and found the actual mechanism by reading
antigravity.google's own docs plus inspecting a real installation:
Antigravity's real skills come from *installed plugins* --
~/.gemini/config/plugins/<plugin-name>/, each with a plugin.json
manifest (name/version/description/author -- no skill list) and a
skills/ subfolder scanned automatically. Confirmed by finding Google's
own bundled "science" plugin in exactly that shape on the test machine.
Replicated that shape for a synthetic "paca" plugin
(~/.gemini/config/plugins/paca/{plugin.json,installed_version.json,skills/}),
written once per run for the manifest and once per skill for the
skills/ subfolder. Verified end-to-end on the real Antigravity install
that was previously negative: all 12 bundled skills now show up in its
skills panel and in the /pac slash-command search, confirmed again
after a full app restart.
Gemini CLI's own documented ~/.gemini/skills/ folder and the legacy
~/.gemini/commands/ TOML write are both kept unconditionally alongside
this -- neither hurts, and the classic terminal Gemini CLI tool (as
opposed to Antigravity) was never available to test, so this doesn't
drop a path that might work there.
Updated the install-paca-skills-smoke CI job to assert plugin.json,
installed_version.json, and byte-exact skill content under the new
path, and updated docs/guides/install-skills.md accordingly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Update: found the real mechanism, and it's verified end-to-end now. Kept digging instead of settling for the fallback-only approach in the last update. Read This PR now writes a synthetic Gemini CLI's own documented Marking ready for review. |
There was a problem hiding this comment.
ℹ️ No critical issues — the Antigravity plugin mechanism is genuinely sound (officially documented, and the
plugin.jsonshape field-for-field matches Google's ownscience-skillsplugin). Nits and open items from the prior review are still open, and the delta adds one doc-accuracy wrinkle worth a look.
Reviewed changes
- Antigravity plugin mechanism added as the verified Gemini target (
a539dd7) — writes a syntheticplugin.json+installed_version.jsonfor apacaplugin under~/.gemini/config/plugins/paca/once per run, then copies each skill verbatim intoskills/<name>/SKILL.md; the~/.gemini/skills/write (classic Gemini CLI only) and the~/.gemini/commands/<name>.tomllegacy fallback remain as hedged, clearly-labeled fallbacks. - CI smoke job extended for the Antigravity target — asserts
plugin.jsonname,installed_version.jsonexistence, and byte-exactSKILL.mdunder the plugin's skills subfolder; I replayed the full smoke test locally against the new head and the assertions genuinely fail if the plugin write regresses. - Docs and header updated — platform table, Uninstall section, and script header now document the three-writes structure with the Antigravity rationale.
I verified the load-bearing third-party claims directly: Antigravity's plugin discovery from ~/.gemini/config/plugins/<name>/ with auto-scanned skills/ is documented at antigravity.google/docs/plugins, and the plugin.json fields written match Google's own google-deepmind/science-skills package. Two caveats the author should reconcile (details below): installed_version.json has no Google documentation, and Antigravity's actual docs-backed global skills folder is ~/.gemini/config/skills/ — the folder the script writes (~/.gemini/skills/) is the classic-Gemini-CLI path, not Antigravity's.
⚠️ Stale install-path references outside the diff remain
The two spots flagged in the prior review are still untouched, and the README is now more stale than before — it still describes the removed ~/.claude/commands/ / ~/.gemini/commands/ / .cursor/commands/ layouts (README.md:343) and even omits Gemini's name entirely on the new list. The tsx comment (apps/web/src/components/projects/agents/acp-bridge-setup.tsx:87) still names ~/.claude/commands, ~/.gemini/commands. This PR changes the directory contract; both should be brought in sync with docs/guides/install-skills.md before this is the story the front door tells.
Technical details
# Stale install-path references
## Affected sites
- README.md:343 — describes the removed `~/.claude/commands/`, `~/.gemini/commands/`, `.cursor/commands/` layouts and names only Claude/Gemini/Cursor on the Gemini row
- apps/web/src/components/projects/agents/acp-bridge-setup.tsx:87 — comment names `~/.claude/commands, ~/.gemini/commands`
## Required outcome
- Both should reflect the new layouts (`~/.claude/skills`, `~/.gemini/config/plugins/paca`, `~/.gemini/skills`, `.cursor/skills`), or inherit from docs/guides/install-skills.md (README already links it).ℹ️ Antigravity has a documented global skills folder the script skips
The header calls ~/.gemini/skills/<name>/SKILL.md "the one path directly backed by that product's own documentation" — true for the classic Gemini CLI (geminicli.com), but not for Antigravity: its documented global skills location is ~/.gemini/config/skills/<folder>/ (antigravity.google/docs/skills), which the script never writes. Not a bug — the plugin path is the verified one — but if a docs-backed second Antigravity path is wanted, ~/.gemini/config/skills/ is the target to add, not ~/.gemini/skills/. Related open question: Antigravity actively reconciles managed plugins (deleting one triggers re-download), so a future registry-installed plugin that takes the paca name could silently clobber this synthetic one — low probability, but worth a sentence in the header or docs.
Technical details
# Antigravity global skills dir + plugin reconciliation
## Affected sites
- scripts/install-paca-skills.sh header + install_one_skill() — only `~/.gemini/skills/` is written as the "docs-backed" native folder
- docs/guides/install-skills.md platform table — same omission
## Open questions for the human
- Antigravity 2.0/IDE reads global skills from `~/.gemini/config/skills/`; classic Gemini CLI reads `~/.gemini/skills/`. The two products are converging (Antigravity is documented as Gemini CLI's successor); is a fourth write to `~/.gemini/config/skills/` in scope, or is the plugin mechanism + CLI fallback enough?
- If skills-only content is fine as a "plugin", note in the header that a future real `paca` plugin from a registry would overwrite/re-download over this directory.ℹ️ Legacy on-disk files from the previous script are still left behind
The prior review's open question stands: users who ran the old installer still have ~/.claude/commands/paca-*.md and <project>/.cursor/commands/paca-*.md on disk, and re-running this script writes the new layout alongside without removing them. The updated Uninstall docs do now cover the Gemini legacy .toml and the Antigravity plugin dir, but not the Claude/Cursor commands/ remnants. Existing users get duplicated slash-command surface either way.
Technical details
# Legacy-format cleanup on upgrade
## Affected sites
- scripts/install-paca-skills.sh — install_one_skill() writes only the new `<name>/SKILL.md` layouts
- docs/guides/install-skills.md "Uninstall" — removes new-format paths + gemini legacy toml, not `~/.claude/commands/paca-*` / `.cursor/commands/paca-*`
## Open questions for the human
- Add best-effort legacy cleanup (e.g. `rm -f ~/.claude/commands/paca-* .cursor/commands/paca-*`) when writing the new format, or document a one-time manual removal for existing installs?ℹ️ Nitpicks
- The script header now lists the Claude Code / Cursor / AGENTS.md targets twice (once at the top, then again verbatim after the Gemini "THREE writes" block) — an editing artifact from this commit's restructure; the second list reads as a continuation of the Gemini note. (anchored inline)
- The "every non-lossy... no," false start is still in the
jq -jcomment, and the trailing line still names only "Claude Code / Gemini CLI / Cursor" though there's a fourth verbatim target now. (anchored inline) - The "Cursor has no global commands directory" parenthetical in the platform table was flagged last review and is still present — Cursor's current docs list
~/.cursor/skills/and~/.agents/skills/as user-level (global) directories, so the stated justification is inaccurate even if per-project install remains the design choice. (anchored inline)
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
- Remove a leftover mid-sentence self-correction ("every non-lossy...
no, every target...") in two places, and fix the trailing platform
list next to it to name all four verbatim-write targets (it still
said "Claude Code / Gemini CLI / Cursor", missing the Antigravity
plugin path added since).
- Collapse the header's duplicated Claude Code/Cursor/AGENTS.md list
(appeared once before the Gemini section, verbatim again right
after it) into a single list, with the Gemini detail following.
- Fix "Cursor has no global commands directory" (script header +
docs/guides/install-skills.md): Cursor does support a global
~/.cursor/skills/ — the installer stays project-scoped by choice,
not because no global option exists.
- Soften the installed_version.json claim: nothing in Google's docs or
the bundled science plugin backs its existence — it's presumably
app-written bookkeeping, harmless to write and useful as a concrete
file for the CI smoke test to assert on, but not part of the
verified discovery mechanism (plugin.json + skills/ is).
Verified: shellcheck clean, no other diff.
pikann
left a comment
There was a problem hiding this comment.
LGTM! Thank you for the contribution! 🚀

Summary
install-paca-skills.shnow writes each skill verbatim (frontmatter and all) into Claude Code's, Gemini CLI's, and Cursor's own native Agent Skills folder format (<name>/SKILL.md, the agentskills.io standard), instead of stripping frontmatter into a flat.mdcommand file or re-shaping into Gemini's TOML format. Verified directly against each tool's own current official docs that all three now support this natively — Claude Code and Cursor already did; Gemini CLI added it after this assistant's training cutoff, so I didn't trust prior knowledge and re-checked live. AGENTS.md is unchanged (single shared file, not a per-skill directory, so it still strips frontmatter into a plain section).Also fixes a latent fidelity bug this change surfaced: the bundled-skill fetch used
jq -r '.content', which appends its own trailing newline regardless of whether the source string already ends in one. Every previous target reshaped or stripped the body anyway, so the extra blank line was invisible — it isn't once the goal is byte-for-byte verbatim copying. Switched tojq -j.Implements point 1 of #453, alongside the separate
com.gorlix.project-skillsplugin (repo, marketplace PR) which implements point 2/3 of the same issue, per the maintainer's go-ahead there. Relates to #453 — this PR alone doesn't close it (point 3's plugin lives in a separate repo).Type of Change
Details
CLAUDE_DIR/GEMINI_DIRnow point at~/.claude/skills/~/.gemini/skills(was.../commands).install_one_skill(): Claude/Gemini/Cursor branches nowcpthe rawSKILL.mdverbatim into a per-skill directory; only the AGENTS.md branch still callsstrip_frontmatter.toml_basic_stringhelper.docs/guides/install-skills.md: updated the platform table andUninstallsection for the new directory-per-skill layout..github/workflows/scripts-pr-ci.yml: addedscripts/install-paca-skills.shto the shellcheck target list (fixed one pre-existing SC2016 info-level note this surfaced, unrelated to this change's logic), and added a newinstall-paca-skills-smokejob.Testing
shellcheck --shell=bash scripts/install-paca-skills.sh— clean.install-paca-skills-smokeCI job: runs the real script (not a stubbedcurl) against a local fixture HTTP server serving fixedGET /api/v1/skills/GET /api/v1/pluginsJSON, then asserts byte-exactSKILL.mdcontent for every native target plus AGENTS.md's stripped section.docker compose -f deploy/docker-compose.dev.ymlinstance: ran the script for real with an isolated$HOMEand project dir, then diffed andsha256sum-compared every installed file against the liveGET /api/v1/skillsresponse — byte-identical across Claude Code, Gemini CLI, and Cursor targets.Checklist