gem-skill 0.1.3 → 0.2.0

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: c425b88d925bd6ef993c1ea04581ffd0e457bebc9137cccf68cf3aacd6e00fde
4
+ data.tar.gz: ba39b45df96bc6f76c6b2ffa950a3a2cafbba4b7c0b2bfd94ecbd9cd4b38cd06
5
5
  SHA512:
6
- metadata.gz: 39ed27eb70e0036d097b01576a78a20a13ed337ddf05abfc5f5a63a0279b65c79d327907cc70fa96d132d7e72ae3d47ce5cdb5a0bb50e30cbfbb441ce428611d
7
- data.tar.gz: 6c0fd5f43ffeac14efe923f9b1742ab469d3e08c50f86dd521c38d45d003800fe45a8447dcb682df0014419d1421d1b2f20eb326d3b6c7f2d3b1d7d0210974bb
6
+ metadata.gz: 7b4558c9312730d8d7218951591985d868db5bea5f4282f52702ad01feb5ec381c3cfe5af56cb9b50cfa5c45b58e5dbf54bbefee99d6c36d033598383977c35e
7
+ data.tar.gz: 7464f9cd590e6899ad7a4d2b5a464c42c054ce4287502625349f5cb438e3c66e53e4b56ab7291f8730404f9f9f9f4636a7412cf736a40f40e6dce9a94a5ff048
data/CHANGELOG.md CHANGED
@@ -5,6 +5,33 @@ 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
+
11
+ ## [0.2.0] - 2026-06-19
12
+
13
+ ### Added
14
+ - `--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.
15
+ - `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).
16
+ - `gem skill list` flags verified versions with a green checkmark (`✓`); unverified versions show no mark. The checkmark is colored only for interactive terminals.
17
+ - `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.
18
+ - `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.
19
+ - `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.
20
+ - `Fetcher#source_code` — concatenates the gem's `lib/**/*.rb` (size-capped) as the ground truth the verifier checks against.
21
+ - `Cache.read_metadata`, `Cache.write_skill`, `Cache.merge_metadata` — support verifying/rewriting a cached skill without clobbering `generated_at`/`model`/`sources`.
22
+ - 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`.
23
+ - 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.
24
+
25
+ ### Changed
26
+ - `Runner.install_skill` now accepts `verify:` and returns a `Runner::Result` (`error`, `verify_fixed`) instead of a nil/error-string.
27
+ - 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).
28
+
29
+ ### Fixed
30
+ - 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.
31
+
32
+ ### Note
33
+ - Skills cached before this change lack frontmatter; regenerate them with `gem skill install GEM --force` (or `bundle skill refresh --force`) to add it.
34
+
8
35
  ## [0.1.3] - 2026-06-17
9
36
 
10
37
  ### Added
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
@@ -82,7 +108,8 @@ Two environment variables control `gem-skill`'s behaviour:
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 |
87
114
 
88
115
  ```bash
@@ -91,6 +118,9 @@ export GEMSKILL_DIR="/Volumes/shared/gem-skills"
91
118
 
92
119
  # Switch the default model to Claude
93
120
  export GEMSKILL_MODEL="claude-sonnet-4-6"
121
+
122
+ # Codex users: link skills into a Codex project root instead of .claude/skills
123
+ export GEMSKILL_PROJECT_DIR=".agents" # or ".codex"
94
124
  ```
95
125
 
96
126
  The `--model` flag on any command overrides `GEMSKILL_MODEL` for that
@@ -113,7 +143,10 @@ gem skill install chunker-ruby --force
113
143
  # Use a different model
114
144
  gem skill install chunker-ruby --model claude-haiku-4-5
115
145
 
116
- # Show everything in the cache
146
+ # Verify an already-cached skill against the gem's source (fixes mismatches)
147
+ gem skill verify chunker-ruby
148
+
149
+ # Show everything in the cache (verified versions are flagged with a ✓)
117
150
  gem skill list
118
151
 
119
152
  # Remove a specific cached version
@@ -130,13 +163,21 @@ If a gem isn't installed locally, `gem skill install` will install it first.
130
163
 
131
164
  ### `gem skill setup`
132
165
 
133
- Run once after `gem install gem-skill` to enable `bundle skill` globally:
166
+ Run once after `gem install gem-skill`:
134
167
 
135
168
  ```bash
136
169
  gem skill setup
137
170
  ```
138
171
 
139
- This registers gem-skill as a Bundler plugin so `bundle skill` works in every project.
172
+ This does two things:
173
+
174
+ 1. Registers gem-skill as a Bundler plugin so `bundle skill` works in every project.
175
+ 2. Installs the **`ruby-gem-skills`** router skill into the default skill root of
176
+ each detected assistant (`~/.claude/skills`, `~/.codex/skills`,
177
+ `~/.agents/skills`). Cached gem skills live in `~/.gem/skills`, which
178
+ assistants don't scan by default — this small always-on skill teaches Claude
179
+ Code and Codex how to find and load a gem's `SKILL.md` on demand. Re-run
180
+ `gem skill setup` after upgrading gem-skill to refresh it.
140
181
 
141
182
  ### `gem install --with-skill`
142
183
 
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,6 +44,7 @@ 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`) |
46
49
  | `--version`, `-v` | Print the installed gem-skill version and exit |
47
50
 
@@ -66,7 +69,10 @@ All gems are processed concurrently:
66
69
  ✓ ruby_llm 1.16.0 done
67
70
  ```
68
71
 
69
- After completion, each skill is symlinked into `.claude/skills/`:
72
+ After completion, each skill is symlinked into the project skill directory
73
+ (`.claude/skills/` by default; set
74
+ [`GEMSKILL_PROJECT_DIR`](../configuration.md#gemskill_project_dir) to change it,
75
+ e.g. `.agents` for Codex):
70
76
 
71
77
  ```
72
78
  your-project/.claude/skills/
@@ -75,7 +81,8 @@ your-project/.claude/skills/
75
81
  └── zeitwerk → ~/.gem/skills/zeitwerk/2.8.2/
76
82
  ```
77
83
 
78
- Claude Code automatically reads `SKILL.md` from each linked directory.
84
+ The assistant automatically reads `SKILL.md` from each linked directory (Claude
85
+ Code reads `.claude/skills/`; other assistants use their own roots).
79
86
 
80
87
  ---
81
88
 
@@ -18,6 +18,7 @@ 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`) |
22
23
  | `--version`, `-v` | Print the installed gem-skill version and exit |
23
24
 
@@ -32,11 +33,66 @@ gem skill install faraday zeitwerk dry-validation
32
33
 
33
34
  # Force regeneration with a specific model
34
35
  gem skill install rails --force --model claude-opus-4-8
36
+
37
+ # Generate, then verify the result against the gem's source code
38
+ gem skill install tty-spinner --verify
35
39
  ```
36
40
 
37
41
  If a gem is not installed locally, gem-skill will install it automatically
38
42
  before generating the skill.
39
43
 
44
+ ### Verifying against source (`--verify`)
45
+
46
+ The generation pass synthesizes a skill from a gem's README, changelog, and
47
+ examples. That prose is sometimes stale or wrong about exact method signatures,
48
+ default argument values, and behavior. `--verify` adds a second pass that checks
49
+ the generated skill against the gem's **actual installed source code** (the only
50
+ source of truth) and rewrites anything the source contradicts.
51
+
52
+ Verification requires the gem to be installed locally (it reads `lib/**/*.rb`).
53
+ If no source is available, the skill is left untouched.
54
+
55
+ **Exit status** (so CI can detect README/source drift):
56
+
57
+ | Code | Meaning |
58
+ |------|---------|
59
+ | `0` | Success — skill was clean, or `--verify` not used |
60
+ | `1` | Error |
61
+ | `2` | `--verify` found and corrected problems |
62
+
63
+ Either way, `metadata.json` gains a `verification` block recording that the gem's
64
+ actual source was consulted, which files were examined, and — when fixes were
65
+ applied — an array of structured, issue-ready corrections (affected symbol,
66
+ source location, what the skill said vs. the truth, and the proving source
67
+ snippet). See [Cache layout](cache.md#metadatajson) for the full schema.
68
+
69
+ A verified skill is flagged with a green checkmark in
70
+ [`gem skill list`](#gem-skill-list).
71
+
72
+ ---
73
+
74
+ ### `gem skill verify`
75
+
76
+ Verify an **already-cached** skill against the gem's source, in place, without
77
+ regenerating it.
78
+
79
+ ```bash
80
+ gem skill verify GEM_NAME [GEM_NAME ...]
81
+ ```
82
+
83
+ This runs the same source-truth check as `--verify`, but never generates: the
84
+ gem must be installed (verification reads its source) and the skill must already
85
+ be cached. It errors if either is missing rather than generating a new skill.
86
+
87
+ ```bash
88
+ # Verify the cached tty-spinner skill against its installed source
89
+ gem skill verify tty-spinner
90
+ ```
91
+
92
+ Exit status matches `--verify`: `0` clean, `1` error, `2` when corrections were
93
+ applied. Verified versions are flagged with a green checkmark in
94
+ [`gem skill list`](#gem-skill-list).
95
+
40
96
  ---
41
97
 
42
98
  ### `gem skill --version`
@@ -63,20 +119,35 @@ All gems are processed concurrently — you'll see a live spinner per gem:
63
119
 
64
120
  ### `gem skill setup`
65
121
 
66
- Register gem-skill as a Bundler plugin (run once after `gem install gem-skill`).
122
+ Run once after `gem install gem-skill`.
67
123
 
68
124
  ```bash
69
125
  gem skill setup
70
126
  ```
71
127
 
72
- This enables `bundle skill` in any project on the machine. See
73
- [Installation](../installation.md) for details.
128
+ It does two things:
129
+
130
+ 1. **Registers gem-skill as a Bundler plugin** so `bundle skill` works in any
131
+ project on the machine.
132
+ 2. **Installs the `ruby-gem-skills` router skill** into the default skill root of
133
+ each detected assistant — `~/.claude/skills` (Claude Code), `~/.codex/skills`
134
+ and `~/.agents/skills` (Codex). A root is only written if its assistant home
135
+ (`~/.claude`, `~/.codex`, `~/.agents`) already exists.
136
+
137
+ The router skill matters because cached gem skills live in `~/.gem/skills`, a
138
+ directory assistants don't scan by default. This small always-on skill triggers
139
+ when you work with a Ruby gem and tells the assistant how to find and read that
140
+ gem's cached `SKILL.md` (resolving the version from `Gemfile.lock` or the
141
+ installed gem). Re-run `gem skill setup` after upgrading gem-skill to refresh the
142
+ copy. See [Installation](../installation.md) for details.
74
143
 
75
144
  ---
76
145
 
77
146
  ### `gem skill list`
78
147
 
79
- Show all skills currently in the global cache.
148
+ Show all skills currently in the global cache. A green checkmark (`✓`) appears
149
+ next to any version whose skill has been verified against the gem's source (via
150
+ `--verify` or `gem skill verify`); unverified versions show no mark.
80
151
 
81
152
  ```bash
82
153
  gem skill list
@@ -88,12 +159,18 @@ gem skill list
88
159
  Cached skills in /Users/you/.gem/skills:
89
160
 
90
161
  debug_me 1.1.0
91
- faraday 2.12.0, 2.14.3
92
- zeitwerk 2.8.2
162
+ faraday 2.12.0, 2.14.3
163
+ zeitwerk 2.8.2
93
164
 
94
165
  3 gem(s), 4 version(s) total.
95
166
  ```
96
167
 
168
+ Here `faraday 2.14.3` and `zeitwerk 2.8.2` have verified skills, while
169
+ `faraday 2.12.0` and `debug_me 1.1.0` have not been verified. The checkmark is
170
+ shown in green when the output is an interactive terminal, and as a plain `✓`
171
+ when piped or redirected. "Verified" means the skill was checked against the
172
+ gem's actual source — see [`gem skill verify`](#gem-skill-verify).
173
+
97
174
  ---
98
175
 
99
176
  ### `gem skill purge`
@@ -37,6 +37,43 @@ 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
+
40
77
  ### `GEMSKILL_MODEL`
41
78
 
42
79
  Controls which LLM model is used when generating skills.
data/docs/how-it-works.md CHANGED
@@ -16,6 +16,9 @@ Gemfile.lock / gem name
16
16
 
17
17
  Cache write to ~/.gem/skills/<gem>/<version>/
18
18
 
19
+ Verifier (optional, --verify) check the skill's code against the
20
+ gem's actual source; correct mismatches in place
21
+
19
22
  Linker symlink .claude/skills/<gem> → cache dir
20
23
  ```
21
24
 
@@ -78,27 +81,71 @@ Manages the global skill cache. Structure:
78
81
  └── <gem_name>/
79
82
  └── <version>/
80
83
  ├── SKILL.md
81
- └── metadata.json (gem, version, model, generated_at, sources)
84
+ └── metadata.json (gem, version, model, generated_at, sources,
85
+ and after --verify: a "verification" block with
86
+ source provenance + structured changes)
82
87
  ```
83
88
 
84
89
  `Cache::ROOT` is set once at load time from `GEMSKILL_DIR` (default: `~/.gem/skills`).
85
90
 
91
+ `read_metadata` / `write_skill` / `merge_metadata` let the verifier rewrite a
92
+ cached skill and annotate its metadata without clobbering the original
93
+ `generated_at`, `model`, or `sources`.
94
+
95
+ ### Verifier
96
+
97
+ `lib/gem/skill/verifier.rb`
98
+
99
+ Optional second pass, enabled by `--verify`. Generation synthesizes prose
100
+ sources (README, changelog, examples) which are frequently stale or wrong about
101
+ exact signatures. The verifier re-checks the generated skill against the gem's
102
+ **actual source code** — the only source of truth — and corrects mismatched
103
+ method signatures, default argument values, visibility, return values, and
104
+ behavioral claims.
105
+
106
+ ```ruby
107
+ Verifier.new(gem_name, version, model:).verify(skill_content)
108
+ # => Result(content:, changes:, changed:, verifiable:, source:, model:)
109
+ ```
110
+
111
+ Ground truth comes from `Fetcher#source_code` (the gem's `lib/**/*.rb`), and
112
+ `Fetcher#source_manifest` records which files were examined. Whether the skill
113
+ actually changed is decided by a **deterministic diff** of the content before and
114
+ after — not by trusting the model's self-report — so the exit code is reliable.
115
+ If no installed source is available, `verifiable` is false and the skill is left
116
+ untouched.
117
+
118
+ Each correction in `changes` is a structured, issue-ready Hash
119
+ (`category`, `symbol`, `skill_section`, `source_location`, `was`, `now`,
120
+ `detail`, `source_evidence`) — detailed enough to file a documentation bug
121
+ against the gem. The Runner writes these, plus source provenance, into the
122
+ `verification` block of `metadata.json`.
123
+
86
124
  ### Linker
87
125
 
88
126
  `lib/gem/skill/linker.rb`
89
127
 
90
- Creates and manages directory symlinks in `.claude/skills/` inside a project:
128
+ Creates and manages directory symlinks in the project's skill directory
129
+ (default `.claude/skills/`, Claude Code's convention):
91
130
 
92
131
  ```
93
- .claude/skills/<gem_name> → ~/.gem/skills/<gem_name>/<version>/
132
+ <project_dir>/<gem_name> → ~/.gem/skills/<gem_name>/<version>/
94
133
  ```
95
134
 
96
- Symlinks point to the **version directory**, not directly to `SKILL.md`.
97
- Claude Code discovers `SKILL.md` by reading inside the linked directory.
135
+ The directory is `Linker.project_dir`, read from `GEMSKILL_PROJECT_DIR` each call
136
+ (default `.claude/skills`). Codex users set it to `.agents` or `.codex` so
137
+ `bundle skill` links into a Codex root instead. Symlinks point to the **version
138
+ directory**, not directly to `SKILL.md`; the assistant discovers `SKILL.md` by
139
+ reading inside the linked directory.
98
140
 
99
141
  `Linker.prune_dead_links` removes any symlink whose target no longer exists
100
142
  in the cache (e.g. after `gem skill purge`).
101
143
 
144
+ The cache itself is assistant-neutral. `SKILL.md` is a shared format; other
145
+ assistants read it from their own roots. Note that linking only makes a skill
146
+ *available* — some assistants (e.g. Codex) require it to be in the available-skills
147
+ list or referenced explicitly before it's *active*.
148
+
102
149
  ### Runner
103
150
 
104
151
  `lib/gem/skill/runner.rb`
@@ -107,12 +154,14 @@ Shared core used by both CLI commands. Drives one gem through the
107
154
  cache-check → generate → link sequence:
108
155
 
109
156
  ```ruby
110
- Runner.install_skill(gem_name, version, spinner, force:, model:)
111
- # Returns nil on success, error message string on failure
157
+ Runner.install_skill(gem_name, version, spinner, force:, model:, verify:)
158
+ # => Runner::Result(error:, verify_fixed:, change_count:)
112
159
  ```
113
160
 
114
- Returns the error message rather than raising, so the caller (the concurrent
115
- fiber) can record it without killing other in-flight fibers.
161
+ Captures errors into the result rather than raising, so the caller (the
162
+ concurrent fiber) can record them without killing other in-flight fibers. When
163
+ `verify:` is set, the result's `verify_fixed` lets the CLI aggregate across all
164
+ gems and exit `2` (`EXIT_VERIFY_FIXED`) if any skill was corrected.
116
165
 
117
166
  ---
118
167