Skip to content

install-paca-skills.sh: non-lossy Agent Skills distribution - #454

Merged
pikann merged 4 commits into
Paca-AI:masterfrom
gorlix:453-non-lossy-skill-distribution
Sep 3, 2026
Merged

install-paca-skills.sh: non-lossy Agent Skills distribution#454
pikann merged 4 commits into
Paca-AI:masterfrom
gorlix:453-non-lossy-skill-distribution

Conversation

@gorlix

@gorlix gorlix commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

install-paca-skills.sh now 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 .md command 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 to jq -j.

Implements point 1 of #453, alongside the separate com.gorlix.project-skills plugin (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

  • Other (a behavior change to an existing script + new CI coverage — none of the listed categories quite fit; flagging this template may be tuned for doc-only contributions)

Details

  • CLAUDE_DIR/GEMINI_DIR now point at ~/.claude/skills / ~/.gemini/skills (was .../commands).
  • install_one_skill(): Claude/Gemini/Cursor branches now cp the raw SKILL.md verbatim into a per-skill directory; only the AGENTS.md branch still calls strip_frontmatter.
  • Removed the now-unused toml_basic_string helper.
  • docs/guides/install-skills.md: updated the platform table and Uninstall section for the new directory-per-skill layout.
  • .github/workflows/scripts-pr-ci.yml: added scripts/install-paca-skills.sh to the shellcheck target list (fixed one pre-existing SC2016 info-level note this surfaced, unrelated to this change's logic), and added a new install-paca-skills-smoke job.

Testing

  • shellcheck --shell=bash scripts/install-paca-skills.sh — clean.
  • New install-paca-skills-smoke CI job: runs the real script (not a stubbed curl) against a local fixture HTTP server serving fixed GET /api/v1/skills / GET /api/v1/plugins JSON, then asserts byte-exact SKILL.md content for every native target plus AGENTS.md's stripped section.
  • Manually verified against a real docker compose -f deploy/docker-compose.dev.yml instance: ran the script for real with an isolated $HOME and project dir, then diffed and sha256sum-compared every installed file against the live GET /api/v1/skills response — byte-identical across Claude Code, Gemini CLI, and Cursor targets.

Checklist

  • The change is focused and scoped.
  • Related documentation is updated.
  • New structure or direction is explained clearly.
  • I avoided unnecessary detail or premature abstraction.

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>
@gorlix

gorlix commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Marking this draft while I verify the Gemini side beyond docs. The official Gemini CLI docs describe native .gemini/skills/<name>/SKILL.md discovery, but on the one real machine I could test against, the installed tool is actually Google's Antigravity IDE (documented as Gemini CLI's successor), and a newly-written skill folder there didn't show up as expected. Could be a restart/reload quirk, could be a real gap between Antigravity's actual behavior and what the docs describe — still narrowing it down. Will either confirm it works or add a safety-net fallback (keep writing the old TOML alongside the new SKILL.md folder for the Gemini target) before marking this ready again.

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>
@gorlix

gorlix commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Update: confirmed on the real Antigravity IDE install (restarted the app too, ruling out caching) that the native ~/.gemini/skills/ folder alone did not surface the skill. Rather than assume the docs are simply wrong, I restored the previous ~/.gemini/commands/<name>.toml write alongside the new native folder — so this is now additive for Gemini rather than a cutover. Nobody loses the behavior that worked before this branch; whoever's tooling does read the native folder gets that too. Updated the smoke-test CI job to assert both are written.

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.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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.md copies replace lossy per-platform reshapinginstall_one_skill now mkdir -ps a per-skill directory and cps the raw SKILL.md for Claude Code (~/.claude/skills/), Gemini CLI (~/.gemini/skills/), and Cursor (<project>/.cursor/skills/); only AGENTS.md still strips frontmatter. The unused toml_basic_string helper was removed.
  • jq -rjq -j for the bundled-skill fetch — stops jq appending a trailing newline to content that already ends in one, keeping the copied SKILL.md files byte-identical to what the API serves.
  • docs/guides/install-skills.md — platform table and Uninstall section updated to the new <name>/SKILL.md layout.
  • .github/workflows/scripts-pr-ci.yml — shellcheck coverage extended to install-paca-skills.sh, plus a new install-paca-skills-smoke job 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?

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread scripts/install-paca-skills.sh Outdated
Comment thread docs/guides/install-skills.md Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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 39d43e9 brings back toml_basic_string, GEMINI_LEGACY_DIR="${HOME}/.gemini/commands", and the description/prompt = ''' TOML write (still guarded by ''' detection: a body containing ''' skips the legacy command while the native folder is still written), plus the matching mkdir, platform-menu, summary-echo, and Uninstall-docs updates.
  • CI smoke assertions extended — the install-paca-skills-smoke job now also requires ~/.gemini/commands/<name>.toml to exist with description = and prompt = ''' 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.

Pullfrog  | Fix it ➔View workflow run | Using 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>
@gorlix

gorlix commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

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 antigravity.google's own docs and inspected a real Antigravity install directly: its skills come from installed plugins at ~/.gemini/config/plugins/<plugin-name>/, each a plugin.json manifest (name/version/description/author — no explicit skill list) plus a skills/ subfolder that gets scanned automatically. Found Google's own bundled "science" plugin in exactly this shape, confirming the format.

This PR now writes a synthetic paca plugin in that same shape (~/.gemini/config/plugins/paca/{plugin.json,installed_version.json,skills/<name>/SKILL.md}). Verified on the same real Antigravity install that was negative before: all 12 bundled skills now show up in its skills panel and its /pac slash-command search — confirmed again after a full app restart, via the actual script (not a manual file copy).

Gemini CLI's own documented ~/.gemini/skills/ path and the pre-existing ~/.gemini/commands/*.toml write are both kept unconditionally alongside this — harmless if unread, and the classic terminal gemini tool (distinct from Antigravity) still wasn't available to test directly.

Marking ready for review.

@gorlix
gorlix marked this pull request as ready for review September 2, 2026 15:09

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — the Antigravity plugin mechanism is genuinely sound (officially documented, and the plugin.json shape field-for-field matches Google's own science-skills plugin). 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 synthetic plugin.json + installed_version.json for a paca plugin under ~/.gemini/config/plugins/paca/ once per run, then copies each skill verbatim into skills/<name>/SKILL.md; the ~/.gemini/skills/ write (classic Gemini CLI only) and the ~/.gemini/commands/<name>.toml legacy fallback remain as hedged, clearly-labeled fallbacks.
  • CI smoke job extended for the Antigravity target — asserts plugin.json name, installed_version.json existence, and byte-exact SKILL.md under 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 -j comment, 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)

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread scripts/install-paca-skills.sh Outdated
Comment thread scripts/install-paca-skills.sh Outdated
Comment thread scripts/install-paca-skills.sh
Comment thread docs/guides/install-skills.md Outdated
- 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 pikann left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you for the contribution! 🚀

@pikann
pikann merged commit 7d099bb into Paca-AI:master Sep 3, 2026
5 checks passed
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