gem-skill 0.1.3 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81806de91a59a0b2f9144dedebda65f568d61629064f6182f753229fced49583
4
- data.tar.gz: 444441e8f081192ceb5b6201bc8c2c9428b243729df896a4405db562d435ad7d
3
+ metadata.gz: 3cc3ecc8f770110cc9f8605e149ff2d78c44a388a76add0046c595b01ecbf882
4
+ data.tar.gz: 21ac18bb7bcc6de52ac9987561da30821be843cca322f0b45b8e04ed1a9af3a1
5
5
  SHA512:
6
- metadata.gz: 39ed27eb70e0036d097b01576a78a20a13ed337ddf05abfc5f5a63a0279b65c79d327907cc70fa96d132d7e72ae3d47ce5cdb5a0bb50e30cbfbb441ce428611d
7
- data.tar.gz: 6c0fd5f43ffeac14efe923f9b1742ab469d3e08c50f86dd521c38d45d003800fe45a8447dcb682df0014419d1421d1b2f20eb326d3b6c7f2d3b1d7d0210974bb
6
+ metadata.gz: 4f6fecb42f713486c2abddd7041c6a7294ed4e0cf07affd97457739fe0bd59c8fb080ee80b672817e9a182ff8359896235f2c2c56f16a54eca9528290a3fd07d
7
+ data.tar.gz: acd79346e7b20fcead6bfff93f4f21aa58f81ac203b3fec5616f2f5f55b6d4337c0919cd01a1790159c7e256ec579c14d8e25c2aaa4cd5d85479fea826c4e3fa
data/CHANGELOG.md CHANGED
@@ -5,6 +5,49 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ### Added
9
+ - Bundled `ruby-gem-skills` "router" skill (shipped at the gem root) that teaches assistants how to find cached gem skills in `~/.gem/skills` — a directory neither Claude Code nor Codex scans by default. `gem skill setup` now also copies this skill into each detected assistant's default root (`~/.claude/skills`, `~/.codex/skills`, `~/.agents/skills`), creating none that don't already exist. `Gem::Skill::ROUTER_SKILL_NAME` / `ROUTER_SKILL_DIR` locate the bundled template.
10
+ - `GEMSKIL_MAX_TOKENS` environment variable (default `32767`) — caps the number of output tokens the LLM may generate per skill file. Raise this if generated `SKILL.md` files are being truncated.
11
+ - `--max-tokens TOKENS` CLI flag on `gem skill install`, `bundle skill install`, and `bundle skill refresh` — overrides `GEMSKIL_MAX_TOKENS` for a single invocation. Provider-correct parameter name is selected automatically (`max_completion_tokens` for OpenAI models, `max_tokens` for all others).
12
+ - `GEMSKILL_TEMPERATURE` environment variable (default `0.2`) — sampling temperature for generation. Lower values produce more consistent, less variable output, appropriate for factual reference docs. Applied only to models that support a temperature parameter; reasoning models (e.g. `gpt-5.5`) report `temperature: false` in their RubyLLM metadata and the value is silently skipped for them.
13
+ - `--temperature TEMP` CLI flag on `gem skill install` — overrides `GEMSKILL_TEMPERATURE` for a single invocation.
14
+
15
+ ### Fixed
16
+ - `strip_wrapper_fence` no longer deletes the closing ``` of a skill that legitimately ends with a code block. The trailing wrapper fence is now stripped only when a matching opening wrapper fence (```` ```markdown ```` / ```` ``` ```` at the very start) was present. Previously the final ``` was removed unconditionally, leaving the last code block open at end-of-file — which looked like the LLM truncating its output but was entirely a post-processing bug. Regenerate affected skills with `gem skill install GEM --force`.
17
+
18
+ ### Changed
19
+ - `Generator::MAX_TOKENS` constant reads from `GEMSKIL_MAX_TOKENS` and is passed via `with_params` to every `RubyLLM.chat` call, covering both streaming and non-streaming paths.
20
+ - `Generator#initialize` now accepts `max_tokens:` and `temperature:` keywords so callers can override the token cap and sampling temperature per-instance without touching the environment.
21
+ - `Generator` sets `with_temperature` only when the model's RubyLLM metadata does not mark temperature unsupported, avoiding API errors on reasoning models.
22
+ - `Runner.install_skill` accepts and forwards `max_tokens:` and `temperature:` to `Generator.new`.
23
+ - `README.md` Configuration section now lists all environment variables (gem-skill-specific and provider API keys) in a single reference table.
24
+ - `docs/configuration.md` documents the new `GEMSKIL_MAX_TOKENS` and `GEMSKILL_TEMPERATURE` variables alongside the existing `GEMSKILL_MODEL` entry.
25
+ - `docs/commands/gem-skill.md` and `docs/commands/bundle-skill.md` option tables updated with `--max-tokens` and `--temperature`.
26
+
27
+ ## [0.2.0] - 2026-06-19
28
+
29
+ ### Added
30
+ - `--verify` flag for `gem skill install` and `bundle skill install`/`refresh` — runs a second LLM pass that checks the generated skill's code against the gem's **actual source code** (the source of truth) and corrects mismatched method signatures, default argument values, visibility, return values, and behavioral claims. READMEs and docstrings are frequently stale; this catches it.
31
+ - `gem skill verify GEM_NAME [GEM_NAME ...]` subcommand — verify an already-cached skill in place (never generates; errors if the gem isn't installed or the skill isn't cached).
32
+ - `gem skill list` flags verified versions with a green checkmark (`✓`); unverified versions show no mark. The checkmark is colored only for interactive terminals.
33
+ - `GEMSKILL_PROJECT_DIR` env var (default `.claude/skills`) — controls the project-relative directory `bundle skill` writes symlinks into. Codex users can set it to `.agents` or `.codex` to link skills into a Codex project root.
34
+ - `Gem::Skill::Verifier` — the verification pass. Whether the skill changed is decided by a deterministic diff (not the model's self-report), so the result is trustworthy.
35
+ - `Gem::Skill::Frontmatter` — deterministic, idempotent frontmatter builder shared by the generator and verifier, so a skill always carries valid frontmatter even if the model omits it.
36
+ - `Fetcher#source_code` — concatenates the gem's `lib/**/*.rb` (size-capped) as the ground truth the verifier checks against.
37
+ - `Cache.read_metadata`, `Cache.write_skill`, `Cache.merge_metadata` — support verifying/rewriting a cached skill without clobbering `generated_at`/`model`/`sources`.
38
+ - Verified skills gain a `verification` block in `metadata.json` recording that the skill was checked against the gem's actual source: `verified`, `verified_at`, `model`, and `fixed` (whether the check changed anything). When no installed source is available to check against, it records `verified: false` with a `skipped_reason`.
39
+ - Exit status `2` (`Gem::Skill::EXIT_VERIFY_FIXED`) when `--verify` found and corrected problems, so CI can detect README/source drift. `0` = clean, `1` = error.
40
+
41
+ ### Changed
42
+ - `Runner.install_skill` now accepts `verify:` and returns a `Runner::Result` (`error`, `verify_fixed`) instead of a nil/error-string.
43
+ - Documentation reworded to be assistant-neutral: `SKILL.md` is a shared format read by Claude Code, OpenAI Codex, and other AI coding assistants. Added guidance for pointing non-Claude assistants (e.g. Codex's `~/.codex/skills`, the vendor-neutral `~/.agents/skills`) at the shared cache. `bundle skill` still links into `.claude/skills/` (Claude Code's convention).
44
+
45
+ ### Fixed
46
+ - Generated `SKILL.md` files now include the required YAML frontmatter (`name` + `description`). Without it, the files were never registered/triggered as skills by Claude Code or OpenAI Codex — they were just markdown in a skills folder. `name` is the gem name normalized to hyphen-case (e.g. `ruby_llm` → `ruby-llm`); `description` is a trigger-oriented one-liner derived from the Overview (sanitized for both assistants: single line, no angle brackets). Verified against Claude Code's skill validator and OpenAI's Codex skill spec.
47
+
48
+ ### Note
49
+ - Skills cached before this change lack frontmatter; regenerate them with `gem skill install GEM --force` (or `bundle skill refresh --force`) to add it.
50
+
8
51
  ## [0.1.3] - 2026-06-17
9
52
 
10
53
  ### Added
data/CLAUDE.md ADDED
@@ -0,0 +1,63 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this gem does
6
+
7
+ `gem-skill` generates `SKILL.md` files from Ruby gem documentation using an LLM (via RubyLLM), caches them globally at `~/.gem/skills/<gem_name>/<version>/SKILL.md`, and symlinks them into projects as directory links: `.claude/skills/<gem_name>/ → ~/.gem/skills/<gem_name>/<version>/`. It registers two CLI entry points: `gem skill` (global cache management) and `bundle skill` (project-aware, driven by `Gemfile.lock`), plus a `gem install --with-skill` flag.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ bundle install
13
+ bundle exec rake test # run all tests (default task)
14
+ ruby -Ilib:test test/gem/skill/cache_test.rb # run a single test file
15
+ bundle exec ruby scripts/e2e_test [GEM VERSION MODEL] # live LLM end-to-end test (needs a provider API key)
16
+ bin/dev_install # point Bundler's plugin indexes at this source tree so
17
+ # `bundle skill` runs local code (no build/release needed)
18
+ bin/dev_install --reset # restore to the released gem install
19
+ ```
20
+
21
+ Docs site is MkDocs (`mkdocs.yml`, `docs/`), deployed by `.github/workflows/deploy-github-pages.yml`.
22
+
23
+ ## Architecture
24
+
25
+ ### Entry points
26
+
27
+ - `lib/rubygems_plugin.rb` — loaded automatically by RubyGems; registers `gem skill` (`Gem::Commands::SkillCommand`) AND prepends `--with-skill` onto `gem install`, queuing installed gems and generating their skills in threads via a `Gem.post_install` hook + `at_exit`
28
+ - `plugins.rb` — Bundler plugin entry point; registers `bundle skill` via `Gem::Skill::BundlerPlugin` → `Gem::Skill::BundlerCommand`
29
+
30
+ ### Core pipeline (in call order)
31
+
32
+ 1. **`Lockfile`** (`lib/gem/skill/lockfile.rb`) — parses `Gemfile.lock`; extracts direct-dependency name→version pairs by cross-referencing the `DEPENDENCIES` and `specs:` sections
33
+ 2. **`Fetcher`** (`lib/gem/skill/fetcher.rb`) — collects raw docs for a single gem/version in priority order: (1) local `Gem::Specification` gem_dir, (2) RubyGems API JSON, (3) GitHub raw README. Returns `{metadata:, readme:, changelog:}` with only populated keys
34
+ 3. **`Generator`** (`lib/gem/skill/generator.rb`) — formats fetched sources into a prompt, calls RubyLLM, strips any wrapping code fence, runs `Frontmatter.build`, stores result via `Cache`. Supports streaming via block
35
+ 4. **`Frontmatter`** (`lib/gem/skill/frontmatter.rb`) — deterministically (no LLM) builds/replaces the YAML frontmatter (`name` + `description`) that makes a SKILL.md discoverable by both Claude Code and Codex; enforces both assistants' name/description constraints
36
+ 5. **`Cache`** (`lib/gem/skill/cache.rb`) — reads/writes `~/.gem/skills/<name>/<version>/SKILL.md` and `metadata.json`; `merge_metadata` string-normalizes keys
37
+ 6. **`Verifier`** (`lib/gem/skill/verifier.rb`) — optional second LLM pass (`--verify` or `gem skill verify`) that re-checks a generated skill against the gem's actual installed source and corrects wrong signatures/claims. `changed?` is decided by a deterministic content diff, never the model's self-report; unverifiable (no local source) is recorded in metadata, not an error
38
+ 7. **`Linker`** (`lib/gem/skill/linker.rb`) — creates/updates directory symlinks in the project skills dir pointing into the cache; `prune_dead_links` removes broken symlinks after a refresh
39
+
40
+ **`Runner`** (`lib/gem/skill/runner.rb`) is the shared generate+cache+link+verify orchestration used by both CLI commands; it returns a `Runner::Result` (`Data.define`) and drives a TTY spinner passed in by the caller.
41
+
42
+ ### CLI layer
43
+
44
+ - `lib/gem/skill/cli/gem_command.rb` — `Gem::Commands::SkillCommand`; subcommands: `install`, `verify`, `list`, `purge`, `setup`. Multi-gem installs run concurrently with the `async` gem (`Async::Barrier`). `setup` registers the Bundler plugin and copies the bundled router skill (`ruby-gem-skills/SKILL.md` at repo root) into each detected assistant's global skill root
45
+ - `lib/gem/skill/cli/bundle_command.rb` — `Gem::Skill::BundlerCommand`; subcommands: `install`, `refresh`, `list`. `install` = generate + link all direct lockfile gems; `refresh` = skip gems already linked at the correct version. Parses its own flags (`--force`, `--verify`, `--model`, `--max-tokens`, `--temperature`, `--version`)
46
+
47
+ ### LLM configuration
48
+
49
+ `Gem::Skill.configure_llm!` reads provider API keys from environment variables (see `ENV_KEY_MAP` in `lib/gem/skill.rb`) and configures RubyLLM. Called automatically by both CLI entry points. No-op if already configured.
50
+
51
+ ## Key constants & environment variables
52
+
53
+ - `Cache::ROOT` = `~/.gem/skills` (override: `GEMSKILL_DIR`)
54
+ - `Linker::DEFAULT_PROJECT_DIR` = `.claude/skills` (override: `GEMSKILL_PROJECT_DIR`, re-read on every call — e.g. `.agents` for Codex)
55
+ - `Generator::DEFAULT_MODEL` = `"gpt-5.5"` (override: `GEMSKILL_MODEL` or `--model`)
56
+ - `Generator::MAX_TOKENS` = 32,767 (override: `GEMSKIL_MAX_TOKENS` — note the single-L spelling — or `--max-tokens`)
57
+ - `Generator::DEFAULT_TEMPERATURE` = 0.2 (override: `GEMSKILL_TEMPERATURE` or `--temperature`; skipped for models that reject temperature)
58
+ - `Generator::MAX_SOURCE_CHARS` = 60,000 (README truncation guard)
59
+ - `Gem::Skill::EXIT_VERIFY_FIXED` = 2 — grep-style exit status when `--verify` applied fixes (0 = clean, 1 = error)
60
+
61
+ ## Testing
62
+
63
+ Uses Minitest (`Minitest::TestTask`). Unit tests live in `test/gem/skill/`; shared cache-sandboxing helpers in `test/support/cache_helpers.rb`. `scripts/e2e_test` runs a full live pipeline (fetch → generate → cache → link) against a real LLM and requires at least one provider API key.
data/README.md CHANGED
@@ -5,9 +5,10 @@
5
5
  <tr>
6
6
  <td width="40%"><img src="docs/assets/images/gem-skill.jpg" alt="gem-skill logo" width="100%"></td>
7
7
  <td width="60%">
8
- Generate Claude Code skill files from Ruby gem
9
- documentation and caches them globally so every project that uses a gem
10
- can share the same pre-built knowledge.<br><br>
8
+ Generate <code>SKILL.md</code> files for AI coding assistants
9
+ (Claude Code, OpenAI Codex, and others) from Ruby gem documentation,
10
+ and cache them globally so every project that uses a gem can share the
11
+ same pre-built knowledge.<br><br>
11
12
  <strong><a href="https://madbomber.github.io/gem-skill">Full documentation →</a></strong>
12
13
  </td>
13
14
  </tr>
@@ -16,14 +17,15 @@
16
17
 
17
18
  ## The problem it solves
18
19
 
19
- Every time Claude Code encounters a gem it hasn't seen in the current context, it
20
- re-reads the README, scans examples, and figures out the API. That costs tokens
21
- and time — and the result evaporates when the conversation ends.
20
+ Every time an AI coding assistant encounters a gem it hasn't seen in the current
21
+ context, it re-reads the README, scans examples, and figures out the API. That
22
+ costs tokens and time — and the result evaporates when the conversation ends.
22
23
 
23
24
  `gem-skill` runs that pipeline once, offline, and stores the output as a
24
- `SKILL.md` in `~/.gem/skills`. Projects symlink to the cached version, so Claude
25
- has accurate, version-specific knowledge about each gem without repeating the
26
- ingestion work.
25
+ `SKILL.md` in `~/.gem/skills`. Projects symlink to the cached version, so your
26
+ assistant has accurate, version-specific knowledge about each gem without
27
+ repeating the ingestion work. `SKILL.md` is a shared format — Claude Code,
28
+ OpenAI Codex, and other assistants all read it.
27
29
 
28
30
  ## How the cache is laid out
29
31
 
@@ -38,7 +40,8 @@ ingestion work.
38
40
  └── metadata.json
39
41
  ```
40
42
 
41
- Each project's `.claude/skills/` holds symlinks that point into this cache:
43
+ Each project's `.claude/skills/` directory (Claude Code's convention) holds
44
+ symlinks that point into this cache:
42
45
 
43
46
  ```
44
47
  your-app/.claude/skills/
@@ -48,6 +51,29 @@ your-app/.claude/skills/
48
51
  Two projects that pin different versions of the same gem each get the right
49
52
  skill; the underlying content is generated once and shared.
50
53
 
54
+ ### Using the cache with other assistants
55
+
56
+ The `~/.gem/skills` cache is assistant-neutral — `SKILL.md` is a shared format.
57
+ By default `bundle skill` links skills into `.claude/skills/`, which Claude Code
58
+ reads automatically. Other assistants discover skills in their own roots; for
59
+ example, OpenAI Codex looks in `~/.codex/skills` and the vendor-neutral
60
+ `~/.agents/skills` (and project-local `.agents/` / `.codex/`).
61
+
62
+ To make `bundle skill` link into a different project directory, set
63
+ `GEMSKILL_PROJECT_DIR`:
64
+
65
+ ```bash
66
+ export GEMSKILL_PROJECT_DIR=".agents" # Codex project root
67
+ bundle skill install # symlinks now land in .agents/
68
+ ```
69
+
70
+ > **Availability ≠ activation.** Claude Code treats every `SKILL.md` under
71
+ > `.claude/skills/` as active automatically. Some assistants (e.g. Codex) only
72
+ > activate a skill if it's in the session's available-skills list or you point
73
+ > at it explicitly — so linking makes a skill *available*, not necessarily
74
+ > *active*. See
75
+ > [Using with other assistants](https://madbomber.github.io/gem-skill/skill-files/#using-with-other-assistants).
76
+
51
77
  ## Installation
52
78
 
53
79
  ```bash
@@ -78,12 +104,22 @@ export GEMINI_API_KEY="..." # or Gemini
78
104
 
79
105
  ## Configuration
80
106
 
81
- Two environment variables control `gem-skill`'s behaviour:
107
+ All environment variables and their defaults:
82
108
 
83
109
  | Variable | Default | Description |
84
110
  |---|---|---|
85
- | `GEMSKILL_DIR` | `~/.gem/skills` | Root directory for the skill cache |
111
+ | `GEMSKILL_DIR` | `~/.gem/skills` | Root directory for the global skill cache |
112
+ | `GEMSKILL_PROJECT_DIR` | `.claude/skills` | Project-relative directory where `bundle skill` writes symlinks |
86
113
  | `GEMSKILL_MODEL` | `gpt-5.5` | LLM model used when generating skills |
114
+ | `GEMSKIL_MAX_TOKENS` | `32767` | Maximum output tokens per generated skill file |
115
+ | `GEMSKILL_TEMPERATURE` | `0.2` | Sampling temperature (skipped for reasoning models that reject it) |
116
+ | `ANTHROPIC_API_KEY` | _(none)_ | API key for Anthropic / Claude models |
117
+ | `OPENAI_API_KEY` | _(none)_ | API key for OpenAI / GPT models |
118
+ | `GEMINI_API_KEY` | _(none)_ | API key for Google Gemini models |
119
+ | `MISTRAL_API_KEY` | _(none)_ | API key for Mistral models |
120
+ | `DEEPSEEK_API_KEY` | _(none)_ | API key for DeepSeek models |
121
+ | `OPENROUTER_API_KEY` | _(none)_ | API key for OpenRouter |
122
+ | `XAI_API_KEY` | _(none)_ | API key for xAI (Grok) models |
87
123
 
88
124
  ```bash
89
125
  # Store skills on a shared drive accessible to all projects
@@ -91,6 +127,12 @@ export GEMSKILL_DIR="/Volumes/shared/gem-skills"
91
127
 
92
128
  # Switch the default model to Claude
93
129
  export GEMSKILL_MODEL="claude-sonnet-4-6"
130
+
131
+ # Codex users: link skills into a Codex project root instead of .claude/skills
132
+ export GEMSKILL_PROJECT_DIR=".agents" # or ".codex"
133
+
134
+ # Raise the output token cap if generated skill files are being truncated
135
+ export GEMSKIL_MAX_TOKENS=65536
94
136
  ```
95
137
 
96
138
  The `--model` flag on any command overrides `GEMSKILL_MODEL` for that
@@ -113,7 +155,10 @@ gem skill install chunker-ruby --force
113
155
  # Use a different model
114
156
  gem skill install chunker-ruby --model claude-haiku-4-5
115
157
 
116
- # Show everything in the cache
158
+ # Verify an already-cached skill against the gem's source (fixes mismatches)
159
+ gem skill verify chunker-ruby
160
+
161
+ # Show everything in the cache (verified versions are flagged with a ✓)
117
162
  gem skill list
118
163
 
119
164
  # Remove a specific cached version
@@ -130,13 +175,21 @@ If a gem isn't installed locally, `gem skill install` will install it first.
130
175
 
131
176
  ### `gem skill setup`
132
177
 
133
- Run once after `gem install gem-skill` to enable `bundle skill` globally:
178
+ Run once after `gem install gem-skill`:
134
179
 
135
180
  ```bash
136
181
  gem skill setup
137
182
  ```
138
183
 
139
- This registers gem-skill as a Bundler plugin so `bundle skill` works in every project.
184
+ This does two things:
185
+
186
+ 1. Registers gem-skill as a Bundler plugin so `bundle skill` works in every project.
187
+ 2. Installs the **`ruby-gem-skills`** router skill into the default skill root of
188
+ each detected assistant (`~/.claude/skills`, `~/.codex/skills`,
189
+ `~/.agents/skills`). Cached gem skills live in `~/.gem/skills`, which
190
+ assistants don't scan by default — this small always-on skill teaches Claude
191
+ Code and Codex how to find and load a gem's `SKILL.md` on demand. Re-run
192
+ `gem skill setup` after upgrading gem-skill to refresh it.
140
193
 
141
194
  ### `gem install --with-skill`
142
195
 
data/docs/cache.md CHANGED
@@ -36,8 +36,9 @@ projects pinning different versions of the same gem each get the correct skill.
36
36
 
37
37
  ### `SKILL.md`
38
38
 
39
- The generated skill file. Contains structured documentation tailored for
40
- Claude Code. See [Skill Files](skill-files.md) for the format.
39
+ The generated skill file. Contains structured documentation for AI coding
40
+ assistants (Claude Code, OpenAI Codex, and others). See
41
+ [Skill Files](skill-files.md) for the format.
41
42
 
42
43
  ### `metadata.json`
43
44
 
@@ -53,6 +54,52 @@ Stores provenance information:
53
54
  }
54
55
  ```
55
56
 
57
+ When a skill is generated with `--verify`, a `verification` block is added that
58
+ records that the gem's actual source code was consulted, exactly which files were
59
+ examined, and the issue-ready corrections that resulted:
60
+
61
+ ```json
62
+ {
63
+ "gem_name": "tty-spinner",
64
+ "version": "0.9.3",
65
+ "model": "gpt-5.5",
66
+ "generated_at": "2026-06-17T10:23:45Z",
67
+ "sources": ["metadata", "readme", "changelog"],
68
+ "verification": {
69
+ "verified": true,
70
+ "verified_at": "2026-06-19T14:02:11Z",
71
+ "model": "gpt-5.5",
72
+ "used_source_code": true,
73
+ "source": {
74
+ "files": ["lib/tty/spinner.rb", "lib/tty/spinner/multi.rb", "lib/tty/spinner/formats.rb"],
75
+ "file_count": 3,
76
+ "chars": 26452,
77
+ "truncated": false
78
+ },
79
+ "fixed": true,
80
+ "change_count": 1,
81
+ "changes": [
82
+ {
83
+ "category": "default_value",
84
+ "symbol": "TTY::Spinner#stop",
85
+ "skill_section": "Core API",
86
+ "source_location": "lib/tty/spinner.rb:387",
87
+ "was": "stop(message = nil)",
88
+ "now": "stop(message = '')",
89
+ "detail": "Default argument is an empty string, not nil; the README implied nil.",
90
+ "source_evidence": "def stop(stop_message = '')"
91
+ }
92
+ ]
93
+ }
94
+ }
95
+ ```
96
+
97
+ Each entry in `changes` is detailed enough to open a documentation issue against
98
+ the gem: it names the affected symbol, where the skill was wrong, what it claimed
99
+ versus the truth, and the source snippet that proves it. When source isn't
100
+ available locally, `verification` instead records `"verified": false`,
101
+ `"used_source_code": false`, and a `"skipped_reason"`.
102
+
56
103
  ## Cache commands
57
104
 
58
105
  ```bash
@@ -81,7 +128,10 @@ Skills generated on one machine are immediately available on others.
81
128
 
82
129
  ## Project symlinks
83
130
 
84
- Projects don't store skills locally — they hold symlinks into the global cache:
131
+ Projects don't store skills locally — they hold symlinks into the global cache.
132
+ `bundle skill` writes these into the directory named by
133
+ [`GEMSKILL_PROJECT_DIR`](configuration.md#gemskill_project_dir), which defaults
134
+ to `.claude/skills/` (Claude Code's convention):
85
135
 
86
136
  ```
87
137
  your-project/.claude/skills/
@@ -89,12 +139,39 @@ your-project/.claude/skills/
89
139
  └── zeitwerk → ~/.gem/skills/zeitwerk/2.8.2/
90
140
  ```
91
141
 
92
- Each symlink points to the **version directory**. Claude Code reads `SKILL.md`
93
- from inside the linked directory.
142
+ Each symlink points to the **version directory**, and the assistant reads
143
+ `SKILL.md` from inside the linked directory.
94
144
 
95
145
  `bundle skill refresh` updates symlinks when versions change after `bundle update`.
96
146
  `bundle skill list` shows the status of all current symlinks.
97
147
 
148
+ ### Other assistants
149
+
150
+ `SKILL.md` is a shared format and the cache is assistant-neutral. Assistants
151
+ other than Claude Code look in their own skill roots — for example, OpenAI Codex
152
+ uses `~/.codex/skills` and the vendor-neutral `~/.agents/skills` globally, or
153
+ project-local `.agents/` / `.codex/`.
154
+
155
+ For project links, set `GEMSKILL_PROJECT_DIR` so `bundle skill` writes straight
156
+ into the right directory:
157
+
158
+ ```bash
159
+ export GEMSKILL_PROJECT_DIR=".agents"
160
+ bundle skill install
161
+ ```
162
+
163
+ For a global, cross-project link, symlink a cached version directory into the
164
+ assistant's global root:
165
+
166
+ ```bash
167
+ ln -s ~/.gem/skills/faraday/2.14.3 ~/.agents/skills/faraday
168
+ ```
169
+
170
+ Note that linking only makes a skill *available*; some assistants (e.g. Codex)
171
+ won't *activate* it unless it's in the session's available-skills list or you
172
+ reference it explicitly. See
173
+ [Using with other assistants](skill-files.md#using-with-other-assistants).
174
+
98
175
  ## Regenerating skills
99
176
 
100
177
  Skills do not auto-expire. Regenerate explicitly when you want updated content:
@@ -2,7 +2,9 @@
2
2
 
3
3
  The `bundle skill` command is project-aware: it reads `Gemfile.lock` to
4
4
  determine which gems and versions are in use, generates skills for all of them,
5
- and links the results into `.claude/skills/` in the project root.
5
+ and links the results into `.claude/skills/` (Claude Code's skill directory) in
6
+ the project root. The generated `SKILL.md` files are a shared format that other
7
+ assistants read too — see [Using with other assistants](../skill-files.md#using-with-other-assistants).
6
8
 
7
9
  ## Global options
8
10
 
@@ -42,7 +44,9 @@ bundle skill install [OPTIONS]
42
44
  | Flag | Description |
43
45
  |------|-------------|
44
46
  | `--force` | Regenerate even if skills are already cached |
47
+ | `--verify` | Verify generated skills against each gem's source and fix mismatches (exit `2` if any fixes applied) |
45
48
  | `--model MODEL` | LLM model to use (overrides `GEMSKILL_MODEL`) |
49
+ | `--max-tokens TOKENS` | Max output tokens per skill (overrides `GEMSKIL_MAX_TOKENS`; default: 32767) |
46
50
  | `--version`, `-v` | Print the installed gem-skill version and exit |
47
51
 
48
52
  **Example:**
@@ -66,7 +70,10 @@ All gems are processed concurrently:
66
70
  ✓ ruby_llm 1.16.0 done
67
71
  ```
68
72
 
69
- After completion, each skill is symlinked into `.claude/skills/`:
73
+ After completion, each skill is symlinked into the project skill directory
74
+ (`.claude/skills/` by default; set
75
+ [`GEMSKILL_PROJECT_DIR`](../configuration.md#gemskill_project_dir) to change it,
76
+ e.g. `.agents` for Codex):
70
77
 
71
78
  ```
72
79
  your-project/.claude/skills/
@@ -75,7 +82,8 @@ your-project/.claude/skills/
75
82
  └── zeitwerk → ~/.gem/skills/zeitwerk/2.8.2/
76
83
  ```
77
84
 
78
- Claude Code automatically reads `SKILL.md` from each linked directory.
85
+ The assistant automatically reads `SKILL.md` from each linked directory (Claude
86
+ Code reads `.claude/skills/`; other assistants use their own roots).
79
87
 
80
88
  ---
81
89
 
@@ -91,6 +99,7 @@ bundle skill refresh [OPTIONS]
91
99
  |------|-------------|
92
100
  | `--force` | Regenerate all skills, even those already at the correct version |
93
101
  | `--model MODEL` | LLM model to use |
102
+ | `--max-tokens TOKENS` | Max output tokens per skill (overrides `GEMSKIL_MAX_TOKENS`; default: 32767) |
94
103
 
95
104
  `refresh` skips gems that are already linked at the correct version (shows
96
105
  `up to date`), regenerates gems whose version changed, and removes dead symlinks
@@ -18,7 +18,10 @@ gem skill install GEM_NAME [GEM_NAME ...]
18
18
  | Flag | Description |
19
19
  |------|-------------|
20
20
  | `--force`, `-f` | Regenerate even if a skill is already cached |
21
+ | `--verify` | After generating, verify the skill's code against the gem's actual source and fix mismatches |
21
22
  | `--model MODEL`, `-m MODEL` | LLM model to use (overrides `GEMSKILL_MODEL`) |
23
+ | `--max-tokens TOKENS` | Max output tokens for this run (overrides `GEMSKIL_MAX_TOKENS`; default: 32767) |
24
+ | `--temperature TEMP` | Sampling temperature, skipped by reasoning models (overrides `GEMSKILL_TEMPERATURE`; default: 0.2) |
22
25
  | `--version`, `-v` | Print the installed gem-skill version and exit |
23
26
 
24
27
  **Examples:**
@@ -32,11 +35,72 @@ gem skill install faraday zeitwerk dry-validation
32
35
 
33
36
  # Force regeneration with a specific model
34
37
  gem skill install rails --force --model claude-opus-4-8
38
+
39
+ # Generate, then verify the result against the gem's source code
40
+ gem skill install tty-spinner --verify
41
+
42
+ # Raise the token cap to avoid truncated output
43
+ gem skill install rails --max-tokens 65536
44
+
45
+ # Lower the temperature for more reproducible output (temperature-capable models)
46
+ gem skill install faraday --model claude-sonnet-4-6 --temperature 0.0
35
47
  ```
36
48
 
37
49
  If a gem is not installed locally, gem-skill will install it automatically
38
50
  before generating the skill.
39
51
 
52
+ ### Verifying against source (`--verify`)
53
+
54
+ The generation pass synthesizes a skill from a gem's README, changelog, and
55
+ examples. That prose is sometimes stale or wrong about exact method signatures,
56
+ default argument values, and behavior. `--verify` adds a second pass that checks
57
+ the generated skill against the gem's **actual installed source code** (the only
58
+ source of truth) and rewrites anything the source contradicts.
59
+
60
+ Verification requires the gem to be installed locally (it reads `lib/**/*.rb`).
61
+ If no source is available, the skill is left untouched.
62
+
63
+ **Exit status** (so CI can detect README/source drift):
64
+
65
+ | Code | Meaning |
66
+ |------|---------|
67
+ | `0` | Success — skill was clean, or `--verify` not used |
68
+ | `1` | Error |
69
+ | `2` | `--verify` found and corrected problems |
70
+
71
+ Either way, `metadata.json` gains a `verification` block recording that the gem's
72
+ actual source was consulted, which files were examined, and — when fixes were
73
+ applied — an array of structured, issue-ready corrections (affected symbol,
74
+ source location, what the skill said vs. the truth, and the proving source
75
+ snippet). See [Cache layout](cache.md#metadatajson) for the full schema.
76
+
77
+ A verified skill is flagged with a green checkmark in
78
+ [`gem skill list`](#gem-skill-list).
79
+
80
+ ---
81
+
82
+ ### `gem skill verify`
83
+
84
+ Verify an **already-cached** skill against the gem's source, in place, without
85
+ regenerating it.
86
+
87
+ ```bash
88
+ gem skill verify GEM_NAME [GEM_NAME ...]
89
+ ```
90
+
91
+ This runs the same source-truth check as `--verify`, but never generates: the
92
+ gem must be installed (verification reads its source) and the skill must already
93
+ be cached. It errors if either is missing rather than generating a new skill.
94
+
95
+ ```bash
96
+ # Verify the cached tty-spinner skill against its installed source
97
+ gem skill verify tty-spinner
98
+ ```
99
+
100
+ Exit status matches `--verify`: `0` clean, `1` error, `2` when corrections were
101
+ applied. Verified versions are flagged with a green checkmark in
102
+ [`gem skill list`](#gem-skill-list).
103
+
40
104
  ---
41
105
 
42
106
  ### `gem skill --version`
@@ -63,20 +127,35 @@ All gems are processed concurrently — you'll see a live spinner per gem:
63
127
 
64
128
  ### `gem skill setup`
65
129
 
66
- Register gem-skill as a Bundler plugin (run once after `gem install gem-skill`).
130
+ Run once after `gem install gem-skill`.
67
131
 
68
132
  ```bash
69
133
  gem skill setup
70
134
  ```
71
135
 
72
- This enables `bundle skill` in any project on the machine. See
73
- [Installation](../installation.md) for details.
136
+ It does two things:
137
+
138
+ 1. **Registers gem-skill as a Bundler plugin** so `bundle skill` works in any
139
+ project on the machine.
140
+ 2. **Installs the `ruby-gem-skills` router skill** into the default skill root of
141
+ each detected assistant — `~/.claude/skills` (Claude Code), `~/.codex/skills`
142
+ and `~/.agents/skills` (Codex). A root is only written if its assistant home
143
+ (`~/.claude`, `~/.codex`, `~/.agents`) already exists.
144
+
145
+ The router skill matters because cached gem skills live in `~/.gem/skills`, a
146
+ directory assistants don't scan by default. This small always-on skill triggers
147
+ when you work with a Ruby gem and tells the assistant how to find and read that
148
+ gem's cached `SKILL.md` (resolving the version from `Gemfile.lock` or the
149
+ installed gem). Re-run `gem skill setup` after upgrading gem-skill to refresh the
150
+ copy. See [Installation](../installation.md) for details.
74
151
 
75
152
  ---
76
153
 
77
154
  ### `gem skill list`
78
155
 
79
- Show all skills currently in the global cache.
156
+ Show all skills currently in the global cache. A green checkmark (`✓`) appears
157
+ next to any version whose skill has been verified against the gem's source (via
158
+ `--verify` or `gem skill verify`); unverified versions show no mark.
80
159
 
81
160
  ```bash
82
161
  gem skill list
@@ -88,12 +167,18 @@ gem skill list
88
167
  Cached skills in /Users/you/.gem/skills:
89
168
 
90
169
  debug_me 1.1.0
91
- faraday 2.12.0, 2.14.3
92
- zeitwerk 2.8.2
170
+ faraday 2.12.0, 2.14.3 ✓
171
+ zeitwerk 2.8.2 ✓
93
172
 
94
173
  3 gem(s), 4 version(s) total.
95
174
  ```
96
175
 
176
+ Here `faraday 2.14.3` and `zeitwerk 2.8.2` have verified skills, while
177
+ `faraday 2.12.0` and `debug_me 1.1.0` have not been verified. The checkmark is
178
+ shown in green when the output is an interactive terminal, and as a plain `✓`
179
+ when piped or redirected. "Verified" means the skill was checked against the
180
+ gem's actual source — see [`gem skill verify`](#gem-skill-verify).
181
+
97
182
  ---
98
183
 
99
184
  ### `gem skill purge`
@@ -37,6 +37,67 @@ Controls where generated skills are cached.
37
37
  Useful for sharing a skill cache across machines via a network drive, or for
38
38
  keeping skills in a non-standard location.
39
39
 
40
+ ### `GEMSKILL_PROJECT_DIR`
41
+
42
+ The project-relative directory where `bundle skill` writes its symlinks into the
43
+ cache. Change it to match whichever assistant you use.
44
+
45
+ | | |
46
+ |---|---|
47
+ | **Default** | `.claude/skills` (Claude Code) |
48
+ | **Example** | `export GEMSKILL_PROJECT_DIR=".agents"` |
49
+
50
+ `SKILL.md` is a shared format, but each assistant looks for skills in its own
51
+ project directory:
52
+
53
+ | Assistant | Suggested `GEMSKILL_PROJECT_DIR` |
54
+ |--------------|----------------------------------|
55
+ | Claude Code | `.claude/skills` (default) |
56
+ | OpenAI Codex | `.agents` or `.codex` |
57
+
58
+ ```bash
59
+ # Claude Code (default — no need to set anything)
60
+ bundle skill install
61
+
62
+ # OpenAI Codex — link into a Codex project root instead
63
+ export GEMSKILL_PROJECT_DIR=".agents"
64
+ bundle skill install # symlinks now land in .agents/
65
+ ```
66
+
67
+ A blank or unset value falls back to the `.claude/skills` default.
68
+
69
+ !!! note "Availability is not activation"
70
+ Setting `GEMSKILL_PROJECT_DIR` controls *where the symlinks are written*. It
71
+ does not change how an assistant decides a skill is active. Claude Code
72
+ activates every `SKILL.md` under `.claude/skills/` automatically; other
73
+ assistants (e.g. Codex) may require the skill to be in the session's
74
+ available-skills list or referenced explicitly. See
75
+ [Using with other assistants](skill-files.md#using-with-other-assistants).
76
+
77
+ ### `GEMSKIL_MAX_TOKENS`
78
+
79
+ Controls the maximum number of output tokens the LLM may generate for each skill file.
80
+ Increase this if generated `SKILL.md` files are being truncated.
81
+
82
+ | | |
83
+ |---|---|
84
+ | **Default** | `32767` |
85
+ | **Example** | `export GEMSKIL_MAX_TOKENS=65536` |
86
+
87
+ ### `GEMSKILL_TEMPERATURE`
88
+
89
+ Sampling temperature for generation. Lower values produce more consistent,
90
+ deterministic output — appropriate for factual reference documentation.
91
+
92
+ | | |
93
+ |---|---|
94
+ | **Default** | `0.2` |
95
+ | **Example** | `export GEMSKILL_TEMPERATURE=0.0` |
96
+
97
+ Only applied to models that support a temperature parameter. Reasoning models
98
+ (e.g. `gpt-5.5`) reject it, so the value is silently skipped for them — switch to
99
+ a temperature-supporting model (Claude, Gemini, older GPT) to benefit.
100
+
40
101
  ### `GEMSKILL_MODEL`
41
102
 
42
103
  Controls which LLM model is used when generating skills.