rails-hyperdrive 0.6.0 → 0.8.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 +4 -4
- data/CHANGELOG.md +305 -1
- data/README.md +44 -13
- data/lib/generators/hyperdrive/install/USAGE +11 -3
- data/lib/generators/hyperdrive/install/install_generator.rb +16 -4
- data/lib/generators/hyperdrive/install_summary.rb +3 -3
- data/lib/generators/hyperdrive/sync/USAGE +7 -5
- data/lib/generators/hyperdrive/sync_runner.rb +20 -10
- data/lib/hyperdrive/skill_tasks.rb +39 -0
- data/lib/rails/commands/hyperdrive/hyperdrive_command.rb +59 -0
- data/lib/rails/hyperdrive/ancestor_locator.rb +10 -13
- data/lib/rails/hyperdrive/artifact_status.rb +16 -7
- data/lib/rails/hyperdrive/auto_install.rb +25 -7
- data/lib/rails/hyperdrive/bundler_artifact_discovery.rb +413 -151
- data/lib/rails/hyperdrive/canonical_skill_render.rb +68 -39
- data/lib/rails/hyperdrive/companion_discovery.rb +4 -5
- data/lib/rails/hyperdrive/gem_manifest.rb +311 -80
- data/lib/rails/hyperdrive/gemspec_locator.rb +38 -0
- data/lib/rails/hyperdrive/install_layout.rb +152 -12
- data/lib/rails/hyperdrive/install_pipeline.rb +142 -78
- data/lib/rails/hyperdrive/install_plan.rb +21 -9
- data/lib/rails/hyperdrive/lock_file.rb +25 -3
- data/lib/rails/hyperdrive/manifest_lint.rb +236 -0
- data/lib/rails/hyperdrive/skill_template.rb +17 -5
- data/lib/rails/hyperdrive/version.rb +1 -1
- metadata +9 -7
- data/lib/rails/hyperdrive/skill_tasks.rb +0 -27
- data/lib/tasks/hyperdrive.rake +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65854e72d10acf60fd5140b816d8ee96bc6f573679dd74e7aeb369cbad9f572a
|
|
4
|
+
data.tar.gz: d86dd2e00fe05e063861b6a408c78dc38809a541cc39fa63a6ab5ca709e88bc3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4a279ebaaf70cf2cd43d050d8d031b0ddc45f0e7790e49954dc4f8c96e1a0733c61c3bb4a1ee365ed076992f29a47f00a40707c964328d11f63b5130cd1ce28
|
|
7
|
+
data.tar.gz: c84c36f0563a52ae2938a998167f2287d68ec586f355794b48c6b79887446fac5cc127f20e0a260613aaeb43f8d9d3187fd198b72a51f3ab7482b20ab5a84f41
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,308 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-08-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **ERB templates for guidelines, agents, and commands.** A companion can ship a
|
|
15
|
+
flat artifact as `<name>.md.erb` directly in its own root
|
|
16
|
+
(`lib/<gem_name>/hyperdrive/guidelines/`, `agents/`, `commands/`, or an
|
|
17
|
+
`agents_dir:`/`commands_dir:` override). It renders at discovery against the
|
|
18
|
+
app's resolved bundle with the same helpers skills use — `gem?`, `any_gem?`,
|
|
19
|
+
`gem_version`, `canonical_render?` — and installs as `<name>.md`: a
|
|
20
|
+
guideline's frontmatter is parsed from the render and then stripped, a
|
|
21
|
+
templated command takes its identity from the rendered stem
|
|
22
|
+
(`analyze.md.erb` → `/analyze`, prefixed by `command_prefix:` as usual), and
|
|
23
|
+
the rendered bytes are what the lock hashes, so a re-render is an ordinary
|
|
24
|
+
upstream delivery through the existing drift, sidecar, and merge machinery.
|
|
25
|
+
There is no template/content pairing for flat kinds and no separate template
|
|
26
|
+
root: Claude Code plugins glob `*.md`, so a template is invisible to them.
|
|
27
|
+
A static `<name>.md` beats a template rendering to the same filename anywhere
|
|
28
|
+
in the kind's roots, always with a warning; a manifest entry may key the
|
|
29
|
+
artifact by either spelling (`reviewer.md.erb` or `reviewer.md`), the shipped
|
|
30
|
+
spelling winning with a warning when both appear, and
|
|
31
|
+
`rake hyperdrive:manifest:check` fails on that ambiguity. A convention-path
|
|
32
|
+
guideline template opts a gem in as a companion, like its static twin.
|
|
33
|
+
Declare a gem-wide `hyperdrive_version: ">= 0.8"` when shipping templated flat
|
|
34
|
+
artifacts: earlier installers never look for `*.md.erb` outside skill
|
|
35
|
+
directories, so the fence is what turns their silence into "upgrade
|
|
36
|
+
rails-hyperdrive to install it".
|
|
37
|
+
- `canonical_render?`, a fourth skill-template ERB helper: `true` in the
|
|
38
|
+
author-side canonical render (`rake hyperdrive:skills:render`/`check`) and
|
|
39
|
+
`false` when rendering into an app, so one template can serve both channels
|
|
40
|
+
where the same content has to read differently — a companion that also ships
|
|
41
|
+
as a Claude Code plugin, say, whose command spellings differ from the
|
|
42
|
+
hyperdrive-installed ones. Unlike the bundle predicates it is deterministic in
|
|
43
|
+
both bindings, so `if`/`else`/`unless` on it is safe. A template using it
|
|
44
|
+
should declare `hyperdrive_version: ">= 0.8"` in its manifest entry (or
|
|
45
|
+
gem-wide), so an older installer reports "upgrade rails-hyperdrive" rather
|
|
46
|
+
than skipping the skill with a template-render failure.
|
|
47
|
+
|
|
48
|
+
## [0.7.0] - 2026-08-26
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- **Two new companion artifact kinds: agents and commands.** A companion gem can
|
|
53
|
+
now ship Claude Code subagents from `agents/*.md` (installed to
|
|
54
|
+
`.claude/agents/<name>.md`) and slash commands from `commands/*.md` (installed
|
|
55
|
+
to `.claude/commands/<name>.md`), alongside its skills and guidelines. Both
|
|
56
|
+
are flat single files that install byte-identical to what the gem ships, and
|
|
57
|
+
both ride the whole existing machine: gating, the `hyperdrive_version:` fence,
|
|
58
|
+
sha-based drift, `--overwrite`/`--sidecar`/`--merge`, `disabled:`, the stale
|
|
59
|
+
sweep, cross-source collision postfixing, and the additive top-up on
|
|
60
|
+
`bundle install`. Agents require `name` + `description` frontmatter like a
|
|
61
|
+
skill; a command's frontmatter is optional and never validated, and its
|
|
62
|
+
identity is its filename stem, so `commands/analyze.md` becomes `/analyze`.
|
|
63
|
+
Neither is wired into `CLAUDE.md` or `index.md` — Claude Code registers them
|
|
64
|
+
by file presence. Installing flat (no per-gem subdirectory) keeps the
|
|
65
|
+
`skills/`/`agents/`/`commands/` sibling geometry a gem ships, so relative
|
|
66
|
+
links between them resolve unchanged after install.
|
|
67
|
+
- Manifest additions in `hyperdrive.yml`: `agents:` and `commands:` gating
|
|
68
|
+
sections keyed by filename, taking the same values `guidelines:` entries do;
|
|
69
|
+
`agents_dir:` and `commands_dir:` to name additional roots (resolved like
|
|
70
|
+
`skills_dir:`); and `command_prefix:`, an optional gem-wide scalar inside
|
|
71
|
+
`commands:` that installs every command of that gem as `<prefix>-<filename>`,
|
|
72
|
+
for a companion that also ships as a Claude Code plugin and wants its
|
|
73
|
+
`/name` namespaced. `rake hyperdrive:manifest:check` lints all of them.
|
|
74
|
+
|
|
75
|
+
- `require "hyperdrive/skill_tasks"` is the require path for the companion-repo
|
|
76
|
+
rake tasks (`hyperdrive:skills:render`, `hyperdrive:skills:check`,
|
|
77
|
+
`hyperdrive:manifest:check`) — framework-neutral, since the tasks run in a
|
|
78
|
+
plain gem repo with no Rails involved.
|
|
79
|
+
- The `hyperdrive_version:` fence now carries a stated guarantee for companion
|
|
80
|
+
authors: it fences on whichever gem implements artifact discovery and install
|
|
81
|
+
— today rails-hyperdrive — and that version numbering is guaranteed
|
|
82
|
+
continuous across any future restructuring of the gem, so a fence like
|
|
83
|
+
`">= 0.8"` keeps its meaning permanently.
|
|
84
|
+
- `rake hyperdrive:manifest:check`, a strict author-side lint of a companion
|
|
85
|
+
gem's `hyperdrive.yml`, on the same companion-repo rake surface as
|
|
86
|
+
`hyperdrive:skills:*` (add `require "hyperdrive/skill_tasks"` to the
|
|
87
|
+
Rakefile; takes the same optional gemspec-path argument). Where the installer
|
|
88
|
+
is permissive so a manifest written for a newer schema never blocks an
|
|
89
|
+
install, the lint fails: unknown keys at every level — the top level,
|
|
90
|
+
`skills:`/`guidelines:` entries, and `conditional:` entries — plus any
|
|
91
|
+
`gem:`/`gems:` or `hyperdrive_version:` value the installer cannot parse, and
|
|
92
|
+
`skills:`/`guidelines:`/`conditional:` keys naming nothing the gem ships. The
|
|
93
|
+
retired `versions:` key and its `version:` near-miss are named pointedly.
|
|
94
|
+
A manifest that lints clean draws no gating warning at install time.
|
|
95
|
+
- A template/content-paired skill's supporting files can be templated too: a
|
|
96
|
+
`*.md.erb` in the template directory renders against the app's bundle and
|
|
97
|
+
installs as `x.md`, so generic skills.sh consumers never copy raw ERB. Within
|
|
98
|
+
a paired skill a template-side file owns its target path — the content
|
|
99
|
+
directory's same-named file never installs, whether the template renders, is
|
|
100
|
+
gated out by `conditional:`, or fails to render.
|
|
101
|
+
`rake hyperdrive:skills:render` now writes each supporting template's
|
|
102
|
+
fail-open canonical face into the paired content directory, and
|
|
103
|
+
`rake hyperdrive:skills:check` byte-gates those faces and fails on any
|
|
104
|
+
`*.md.erb` found under a public skills root. A supporting `*.md.erb` shipped
|
|
105
|
+
under a public skills root still renders, but draws a warning steering it to
|
|
106
|
+
the template directory.
|
|
107
|
+
- `hyperdrive:init --skip-mcp` skips MCP setup entirely: no `rails-hyperdrive`
|
|
108
|
+
entry in `.mcp.json` and no engine mount in `config/routes.rb`. Content
|
|
109
|
+
install, the discover-cache `.gitignore` rule, and the bundler-plugin Gemfile
|
|
110
|
+
directive all still run, and the summary reports `MCP: skipped (--skip-mcp)`
|
|
111
|
+
in place of the mount/server lines. Like `--skip-content`, it only suppresses
|
|
112
|
+
writes — existing MCP configuration is left untouched — and the two flags
|
|
113
|
+
combine.
|
|
114
|
+
- `gems:` is an exact alias of `gem:` at every position the key is read —
|
|
115
|
+
gem-wide defaults, `skills:`/`guidelines:` entries, and per-file
|
|
116
|
+
`conditional:` entries — for every value shape. A map carrying both keys is a
|
|
117
|
+
stylistic slip rather than an error: `gems:` wins, with a warning.
|
|
118
|
+
- Companion-manifest `gem:` gating accepts a map with exactly one of `any:` or
|
|
119
|
+
`all:`, so an artifact can require *every* listed target rather than any one
|
|
120
|
+
of them: `gems: {all: [devise, pundit]}`. `any:` is an explicit spelling of the
|
|
121
|
+
existing any-match, which every bare form (single name, comma-separated
|
|
122
|
+
string, YAML list, `"*"`) keeps by default. The map values take those same
|
|
123
|
+
flat forms, and the form is accepted everywhere `gem:` is — gem-wide defaults,
|
|
124
|
+
`skills:`/`guidelines:` entries, and per-file `conditional:` entries. A `"*"`
|
|
125
|
+
inside `all:` is always satisfied, so it is dropped with a warning; a
|
|
126
|
+
malformed map takes the usual fail-open path (warn, install ungated).
|
|
127
|
+
- Companion manifests can version-fence artifacts against the running
|
|
128
|
+
rails-hyperdrive with a `hyperdrive_version:` requirement, valid gem-wide at
|
|
129
|
+
the manifest top level and per `skills:`/`guidelines:` entry. It is matched
|
|
130
|
+
against the installer's own version rather than the bundle — a constraint
|
|
131
|
+
`gem:` cannot express, since it is an any-match gate across its targets. An
|
|
132
|
+
unsatisfied fence skips the artifact and names the upgrade, both in
|
|
133
|
+
`hyperdrive:init`/`hyperdrive:sync` output and during `bundle install` via the
|
|
134
|
+
bundler plugin.
|
|
135
|
+
|
|
136
|
+
### Changed
|
|
137
|
+
|
|
138
|
+
- **Breaking (installer skew).** `.hyperdrive/lock.yml` is now written at schema
|
|
139
|
+
version 2, so every machine working on an app that has synced with this
|
|
140
|
+
release needs this release too. Older installers ship no schema guard and
|
|
141
|
+
cannot refuse: a 0.6.0 `init`, `sync`, or bundler-plugin top-up run against a
|
|
142
|
+
lock this release wrote silently degrades instead — it never discovers agents
|
|
143
|
+
or commands, so it orphan-warns their lock entries on every run, drops their
|
|
144
|
+
`disabled:` lists, and rewrites the lock back to version 1. Installed files
|
|
145
|
+
are never deleted. From this release on, the read guard (below) makes the
|
|
146
|
+
same skew halt with the upgrade remedy instead of degrading.
|
|
147
|
+
- **Breaking (bundler plugin).** The `bundler-rails-hyperdrive` plugin gem is
|
|
148
|
+
renamed `bundler-hyperdrive` — its directory, gem name, Gemfile directive
|
|
149
|
+
(`plugin "bundler-hyperdrive"`), and release tag namespace
|
|
150
|
+
(`bundler-hyperdrive/vX.Y.Z`). `hyperdrive:init` writes and matches the new
|
|
151
|
+
directive only; an app carrying the old one gets the new line appended and
|
|
152
|
+
should drop the old. Nothing else changes: the `Bundler::Hyperdrive`
|
|
153
|
+
namespace, the hook, and its behavior are untouched.
|
|
154
|
+
- **Breaking (companion gemspecs).** The two directory overrides move out of
|
|
155
|
+
gemspec metadata and into the gem-root manifest as top-level keys:
|
|
156
|
+
`hyperdrive_skills_dir` → `skills_dir:` in `hyperdrive.yml`, and
|
|
157
|
+
`hyperdrive_skill_templates_dir` → `skill_templates_dir:`. The metadata keys
|
|
158
|
+
are no longer read at all — under either spelling, the `rails_`-prefixed one
|
|
159
|
+
0.6.0 shipped included, and with no deprecation or dual-read — so a gem still
|
|
160
|
+
declaring them ships from the default roots, and they no longer count as
|
|
161
|
+
companion opt-in signals (the manifest that now carries them is one).
|
|
162
|
+
Gemspec metadata is left as strictly the pre-install surface rubygems
|
|
163
|
+
serves: `hyperdrive_targets`, `hyperdrive_artifacts`, and the
|
|
164
|
+
`hyperdrive_manifest` bootstrap pointer. Discovery resolves both roots
|
|
165
|
+
fail-open like every other manifest value: a non-string or `..`-containing
|
|
166
|
+
value is warned about and the default roots are used (where the metadata keys
|
|
167
|
+
were ignored silently), and a blank one falls back silently. The
|
|
168
|
+
companion-repo rake tasks stay strict and now raise on a manifest that will
|
|
169
|
+
not parse, rather than rendering with default roots over a file the
|
|
170
|
+
installer cannot read. `rake hyperdrive:manifest:check` validates the two
|
|
171
|
+
new keys.
|
|
172
|
+
- **Breaking (companion gemspecs).** The remaining companion-gem gemspec
|
|
173
|
+
metadata keys drop their `rails_` prefix: `rails_hyperdrive_targets` →
|
|
174
|
+
`hyperdrive_targets`, `rails_hyperdrive_artifacts` →
|
|
175
|
+
`hyperdrive_artifacts`, and `rails_hyperdrive_manifest` →
|
|
176
|
+
`hyperdrive_manifest`. The contract is not Rails-specific, and the keys now
|
|
177
|
+
read that way. The old spellings are no longer read, with no deprecated
|
|
178
|
+
alias: a gem still declaring them is not opted in as a companion, its
|
|
179
|
+
manifest override is ignored, and `hyperdrive:discover` — which now queries
|
|
180
|
+
rubygems for `metadata.hyperdrive_targets:*` — no longer surfaces it.
|
|
181
|
+
Companion gems must update their gemspecs.
|
|
182
|
+
- **Breaking (companion manifests).** Version constraints move onto the gate
|
|
183
|
+
members and the sibling `versions:` key is gone. Wherever `gem:` takes a YAML
|
|
184
|
+
list — the bare list and the `any:`/`all:` values alike — a member is now
|
|
185
|
+
either a bare gem name or a single-pair map carrying that member's own
|
|
186
|
+
requirement: `gems: [railties: ">= 7.0"]`. The requirement is a
|
|
187
|
+
`Gem::Requirement` (comma-separated string or YAML list); a pair value of
|
|
188
|
+
`"*"` or nothing means unconstrained. Scalar and comma-separated string forms
|
|
189
|
+
stay name-only, and a pair value is never target-split, so a compound
|
|
190
|
+
requirement like `">= 4.9, < 6"` is passed whole. A `"*"` used as a pair key
|
|
191
|
+
is meaningless, so it is dropped with a warning in every list context; when it
|
|
192
|
+
was the sole member the gate resolves universal.
|
|
193
|
+
|
|
194
|
+
A `versions:` key remaining anywhere it used to be valid (top level,
|
|
195
|
+
`skills:`/`guidelines:` entries, `conditional:` entries) is warned about and
|
|
196
|
+
ignored: the named targets keep gating, unconstrained. Because requirements
|
|
197
|
+
now travel with the targets, an entry's gate replaces the gem-wide default
|
|
198
|
+
**wholesale** — the per-axis inheritance that let a top-level `versions:`
|
|
199
|
+
apply to an entry naming a different `gem:` is gone. `hyperdrive_version:`
|
|
200
|
+
inheritance is unchanged.
|
|
201
|
+
|
|
202
|
+
No compatibility shim: older rails-hyperdrive releases read a pair member as a
|
|
203
|
+
malformed `gem:` and install the artifact ungated.
|
|
204
|
+
- `.hyperdrive/lock.yml` is now read-guarded against its own schema version. The
|
|
205
|
+
lock is git-tracked and shared across branches that pin different
|
|
206
|
+
rails-hyperdrive versions, so an installer can meet a lock a newer one wrote.
|
|
207
|
+
It previously rewrote the file anyway: unknown top-level keys survive the
|
|
208
|
+
round-trip, but anything a newer schema stores inside the keys it recognizes
|
|
209
|
+
— `disabled:` lists for kinds it does not know, or a reshaped `enabled:` or
|
|
210
|
+
`claude_md` — was silently dropped.
|
|
211
|
+
`hyperdrive:init` and `hyperdrive:sync` now fail with the upgrade remedy
|
|
212
|
+
before any content write (`--dry-run` included, and init's bootstrap steps
|
|
213
|
+
still complete), and `bundle install`'s auto-install prints the same reason
|
|
214
|
+
and installs nothing.
|
|
215
|
+
- An artifact that changes destination — a companion renaming a skill, or a
|
|
216
|
+
cross-source name collision appearing or resolving, flipping between
|
|
217
|
+
`.claude/skills/<name>/` and `.claude/skills/<name>--<source_gem>/` — now has
|
|
218
|
+
its old copy removed, supporting files and emptied directories included,
|
|
219
|
+
instead of being left behind as a byte-duplicate that warns on every sync.
|
|
220
|
+
Removal requires the source gem to still be bundled and to have lost no
|
|
221
|
+
artifact to a discovery skip this run, so a broken companion release or a
|
|
222
|
+
version fence never deletes a good install; a locally-modified copy is always
|
|
223
|
+
warned about and left. The bundler plugin's auto-install removes nothing, as
|
|
224
|
+
before.
|
|
225
|
+
- Orphan warnings no longer claim an artifact is "no longer shipped by" a gem
|
|
226
|
+
that is still in the bundle, in both `hyperdrive:sync` output and the
|
|
227
|
+
"need attention" lines printed during `bundle install`.
|
|
228
|
+
- A `disabled:` entry naming a skill by its postfixed name (`foo--gem_a`) now
|
|
229
|
+
opts that source's artifact out permanently, rather than only while the
|
|
230
|
+
collision that produced the postfix exists.
|
|
231
|
+
- `hyperdrive:init`, `hyperdrive:sync`, and `hyperdrive:discover` are Rails
|
|
232
|
+
commands rather than rake tasks, so their flags now work bare —
|
|
233
|
+
`bin/rails hyperdrive:sync --merge` instead of
|
|
234
|
+
`bin/rails hyperdrive:sync -- --merge` — and
|
|
235
|
+
`bin/rails hyperdrive:sync --help` prints usage. The `-- --flag` form keeps
|
|
236
|
+
working, so existing scripts and docs need no change.
|
|
237
|
+
|
|
238
|
+
### Removed
|
|
239
|
+
|
|
240
|
+
- **BREAKING:** `require "rails/hyperdrive/skill_tasks"`. The companion-repo
|
|
241
|
+
rake tasks are required as `hyperdrive/skill_tasks`; a Rakefile using the old
|
|
242
|
+
spelling raises `LoadError` and needs the one-line change. There is one path,
|
|
243
|
+
under no framework's namespace.
|
|
244
|
+
|
|
245
|
+
- **BREAKING:** the `hyperdrive:*` rake tasks. `bundle exec rake hyperdrive:init`
|
|
246
|
+
(and `:sync` / `:discover`) is no longer available, and the tasks no longer
|
|
247
|
+
appear in `bin/rails -T`. Use `bin/rails hyperdrive:<command>`, which is
|
|
248
|
+
unchanged.
|
|
249
|
+
|
|
250
|
+
### Fixed
|
|
251
|
+
|
|
252
|
+
- A parseable `hyperdrive_version:` fence now survives an unusable `gem:`,
|
|
253
|
+
both per entry and in the gem-wide defaults. The fence is resolved before
|
|
254
|
+
the gate, so fail-open reads "install ungated unless fenced out": a manifest
|
|
255
|
+
written in a value shape an older installer cannot parse is fenced out of
|
|
256
|
+
that installer instead of installing everywhere unconstrained — the exact
|
|
257
|
+
case `hyperdrive_version:` exists to cover. The two gem-wide defaults are
|
|
258
|
+
independent too: an unusable `gem:` default no longer drops a parseable
|
|
259
|
+
fence, and a malformed fence no longer drops a usable `gem:` default; each
|
|
260
|
+
warns for its own axis. An entry whose *own* `hyperdrive_version:` is
|
|
261
|
+
unparsable still installs ungated and unfenced — the gem-wide fence is not
|
|
262
|
+
substituted for a constraint the entry never asked for.
|
|
263
|
+
- A `conditional:` key spelled as a template-backed supporting file's rendered
|
|
264
|
+
face (`references/x.md`) now gates that template, instead of silently
|
|
265
|
+
matching nothing while the file installed unconditionally. Either spelling —
|
|
266
|
+
the shipped `references/x.md.erb` or the face — resolves to the same gate;
|
|
267
|
+
a manifest carrying both for one file draws a warning and the shipped `.erb`
|
|
268
|
+
spelling wins. `rake hyperdrive:manifest:check` agrees with discovery on
|
|
269
|
+
both points, and no longer reports a face-spelled key as naming nothing
|
|
270
|
+
shipped.
|
|
271
|
+
- A `SKILL.md.erb` that fails to render because it reaches for a helper a
|
|
272
|
+
newer rails-hyperdrive added now reports its `hyperdrive_version:` fence
|
|
273
|
+
rather than a bare `ERB render failed (NameError)`, and that line reaches the
|
|
274
|
+
bundler-plugin surface like every other fence warning.
|
|
275
|
+
- An ordinary gate miss — a well-formed `gem:` whose target simply is not
|
|
276
|
+
bundled — no longer marks its source gem as having lost content, so the
|
|
277
|
+
stale-destination sweep keeps converging for companions that gate different
|
|
278
|
+
skills on different stacks. A renamed skill's old directory is now removed
|
|
279
|
+
instead of being orphan-warned indefinitely.
|
|
280
|
+
- `hyperdrive:sync` sweeps a stale `<dest>.new` sidecar when it removes the
|
|
281
|
+
destination that sidecar belonged to, under the same rule as everywhere else
|
|
282
|
+
(machine-pristine → removed; edited → warned about and left). A pristine
|
|
283
|
+
leftover no longer strands itself or keeps an emptied skill directory alive.
|
|
284
|
+
- `rake hyperdrive:manifest:check` fails when a `hyperdrive_manifest` gemspec
|
|
285
|
+
metadata key names a path that is not a file — previously read as "this gem
|
|
286
|
+
ships no manifest" and reported green, while the dangling key still counted
|
|
287
|
+
as companion opt-in and every artifact installed ungated.
|
|
288
|
+
- `rake hyperdrive:skills:render` / `:check` work for a companion that declares
|
|
289
|
+
neither `hyperdrive_skills_dir` nor `hyperdrive_skill_templates_dir`: the
|
|
290
|
+
content root now defaults to top-level `skills/` (matching discovery) rather
|
|
291
|
+
than to the same lib-convention path as the templates root, which made the
|
|
292
|
+
tasks hard-error with "content dir equals template dir".
|
|
293
|
+
- `init`/`sync` no longer count advisory discovery warnings as dropped
|
|
294
|
+
artifacts. Warnings that dropped shipped content — a whole artifact, or one
|
|
295
|
+
supporting file of one — print under `discovery skipped N item(s):` (was
|
|
296
|
+
`discovery skipped N artifact(s):`, which counted every warning and named
|
|
297
|
+
them all artifacts); everything that installed anyway prints under
|
|
298
|
+
`discovery reported M advisory warning(s):`.
|
|
299
|
+
- The bundler-plugin hook now prints discovery advisories during
|
|
300
|
+
`bundle install`, so a retired `versions:` key — content this release
|
|
301
|
+
deliberately installs unconstrained — is no longer silent there. Ordinary
|
|
302
|
+
artifact skips stay with `init`/`sync`.
|
|
303
|
+
- The `installing ungated` warning on a malformed manifest entry that keeps its
|
|
304
|
+
fence now reads `installing ungated unless fenced out`, so it can no longer
|
|
305
|
+
contradict a fence-skip line for the same artifact. Only an unparsable
|
|
306
|
+
`hyperdrive_version:` says `installing ungated and unfenced`.
|
|
307
|
+
- The warning for a manifest that will not parse now names the parser error
|
|
308
|
+
(`ignoring manifest hyperdrive.yml: malformed YAML (<reason>)`), matching what
|
|
309
|
+
`rake hyperdrive:manifest:check` already reported. Behaviour is unchanged:
|
|
310
|
+
gating that cannot be read still resolves to an absent manifest.
|
|
311
|
+
|
|
10
312
|
## [0.6.0] - 2026-08-17
|
|
11
313
|
|
|
12
314
|
### Changed
|
|
@@ -452,7 +754,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
452
754
|
- `hyperdrive:init` generator that installs architecture skills and auto-discovers
|
|
453
755
|
per-gem skills.
|
|
454
756
|
|
|
455
|
-
[Unreleased]: https://github.com/rails-hyperdrive/rails-hyperdrive/compare/v0.
|
|
757
|
+
[Unreleased]: https://github.com/rails-hyperdrive/rails-hyperdrive/compare/v0.8.0...HEAD
|
|
758
|
+
[0.8.0]: https://github.com/rails-hyperdrive/rails-hyperdrive/releases/tag/v0.8.0
|
|
759
|
+
[0.7.0]: https://github.com/rails-hyperdrive/rails-hyperdrive/releases/tag/v0.7.0
|
|
456
760
|
[0.6.0]: https://github.com/rails-hyperdrive/rails-hyperdrive/releases/tag/v0.6.0
|
|
457
761
|
[0.5.0]: https://github.com/rails-hyperdrive/rails-hyperdrive/releases/tag/v0.5.0
|
|
458
762
|
[0.4.0]: https://github.com/rails-hyperdrive/rails-hyperdrive/releases/tag/v0.4.0
|
data/README.md
CHANGED
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
Rails Hyperdrive is a development-only Rails engine for working on Rails apps with AI coding agents. It gives the agent two things it can't get from source alone: live answers from the booted app, and guidance specific to the gems and versions in the bundle.
|
|
14
14
|
|
|
15
15
|
- **Live introspection.** The engine mounts an [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server at `http://localhost:3000/_hyperdrive/mcp` with **8 tools** that answer from the running app itself: eval Ruby, query the DB (read-only), tail logs, list models and routes, jump to source, look up docs, snapshot the stack. The agent asks the router instead of grepping `routes.rb`, and reads the live schema instead of replaying migrations.
|
|
16
|
-
- **Stack-specific knowledge.** `bin/rails hyperdrive:init` discovers **skills** and **
|
|
16
|
+
- **Stack-specific knowledge.** `bin/rails hyperdrive:init` discovers **skills**, **guidelines**, **agents**, and **commands** shipped by companion gems and installs only the ones matching your Gemfile: guidance targeting Sidekiq, for example, lands only if your app bundles Sidekiq, at a version the guidance covers.
|
|
17
17
|
|
|
18
18
|
**rails-hyperdrive is the mechanism; companion gems are the content.** The gem itself ships no skills or guidelines, only the contract and the discovery/install engine. Content reaches your app three ways:
|
|
19
19
|
|
|
20
20
|
- **Native support**: the library gem itself ships a top-level `skills/` directory in the [skills.sh](https://www.skills.sh) layout and opts in as a hyperdrive companion. The preferred route when the maintainer is on board: one gem, one source of truth.
|
|
21
21
|
- **Adopted skill repos**: an existing skills.sh skill repo packaged as a gem, content untouched, gating declared on the side.
|
|
22
|
-
- **Dedicated companion gems**: third-party guidance for a library that ships none itself.
|
|
22
|
+
- **Dedicated companion gems**: third-party guidance for a library that ships none itself.
|
|
23
|
+
|
|
24
|
+
On the last two routes, name the gem `rails-hyperdrive-<name>`: the prefix tells a Gemfile reader exactly what the gem is, though naming plays no part in discovery.
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
@@ -82,22 +84,24 @@ The generated `.mcp.json` points at `http://localhost:3000<mount>/mcp`. If your
|
|
|
82
84
|
|
|
83
85
|
Plus two MCP resources: `hyperdrive://stack-profile` (JSON snapshot of your resolved stack) and `hyperdrive://skills/{name}` (the markdown body of each installed skill). The skill list is enumerated at server boot and the stack snapshot is memoized per process, so a newly installed skill or a changed bundle reaches these two only after a dev-server restart.
|
|
84
86
|
|
|
85
|
-
###
|
|
87
|
+
### Four kinds of knowledge
|
|
86
88
|
|
|
87
|
-
Companion gems ship
|
|
89
|
+
Companion gems ship four artifact kinds, tuned for how agents consume context:
|
|
88
90
|
|
|
89
91
|
- **Skills**: *lazy*. Loaded on demand via Claude Code's native description matcher. Procedural knowledge: "how to write an idempotent Sidekiq job". Installed to `.claude/skills/<name>/SKILL.md`, optionally with supporting files (references, examples, workflows) alongside.
|
|
90
92
|
- **Guidelines**: *eager*. Always in context via a single `@`-include from `CLAUDE.md`. Declarative facts: "this app uses ActionPolicy, not Pundit". Installed to `.claude/hyperdrive/guidelines/<name>.md`.
|
|
93
|
+
- **Agents**: Claude Code subagents, invoked by name or by task match. Installed to `.claude/agents/<name>.md`.
|
|
94
|
+
- **Commands**: Claude Code slash commands. Installed to `.claude/commands/<name>.md`, so a file named `analyze.md` becomes `/analyze`.
|
|
91
95
|
|
|
92
96
|
A companion gem declares in its manifest which gem each artifact targets and at which versions, so what lands in your app is what matches your `Gemfile.lock`, and nothing aimed at a library or version you don't run.
|
|
93
97
|
|
|
94
|
-
With no companion gems, `hyperdrive:init` sets up just the server plumbing (`.mcp.json`, the engine mount, the lockfile) and puts **nothing** into your agent's context window.
|
|
98
|
+
With no companion gems, `hyperdrive:init` sets up just the server plumbing (`.mcp.json`, the engine mount, the lockfile) and puts **nothing** into your agent's context window. The two halves are independently skippable: `--skip-mcp` installs content but no MCP plumbing, `--skip-content` the reverse. Neither removes anything already in place.
|
|
95
99
|
|
|
96
100
|
---
|
|
97
101
|
|
|
98
102
|
## Staying in sync
|
|
99
103
|
|
|
100
|
-
**After `bundle install`: automatic.** `hyperdrive:init` registers the [`bundler-
|
|
104
|
+
**After `bundle install`: automatic.** `hyperdrive:init` registers the [`bundler-hyperdrive`](bundler-hyperdrive/) Bundler plugin in your Gemfile. From then on, adding a companion gem lands its artifacts on that very `bundle install`, with no extra command to run. The plugin is additive only (it never touches an existing file); version bumps and orphaned artifacts are only reported, with a pointer to `hyperdrive:sync`.
|
|
101
105
|
|
|
102
106
|
**`bin/rails hyperdrive:sync`: on demand.** Run it any time (e.g. after `bundle update`) to refresh installed content to the current bundle. It touches no bootstrap artifact and leaves locally modified files untouched (skip + warn). When you *have* edited an installed file and its gem ships a new version, three mutually exclusive flags reconcile the two:
|
|
103
107
|
|
|
@@ -127,6 +131,8 @@ CLAUDE.md # user-owned; ONE injected line: @.claude
|
|
|
127
131
|
.claude/skills/<name>/
|
|
128
132
|
SKILL.md # companion-shipped, installed verbatim (frontmatter included)
|
|
129
133
|
<supporting files> # optional extras (references/, examples/, …), installed as shipped (*.md.erb rendered)
|
|
134
|
+
.claude/agents/<name>.md # companion-shipped subagent, installed verbatim
|
|
135
|
+
.claude/commands/<name>.md # companion-shipped slash command, installed verbatim
|
|
130
136
|
.hyperdrive/lock.yml # git-tracked manifest (source gem, version, content hash)
|
|
131
137
|
```
|
|
132
138
|
|
|
@@ -148,6 +154,10 @@ disabled:
|
|
|
148
154
|
- sidekiq-idempotency
|
|
149
155
|
guidelines:
|
|
150
156
|
- jobs-sidekiq
|
|
157
|
+
agents:
|
|
158
|
+
- sidekiq-reviewer
|
|
159
|
+
commands:
|
|
160
|
+
- analyze
|
|
151
161
|
```
|
|
152
162
|
|
|
153
163
|
A disabled artifact is never installed, and one already on disk is removed on the next `hyperdrive:init` or `hyperdrive:sync`, but **only if you haven't edited it**. A locally modified file is reported and left alone, for you to delete when you're ready. Disabling a skill removes its shipped supporting files under the same per-file rule; files you created yourself in the skill directory survive and keep the directory alive. Disabling a guideline also drops its line from `index.md`, so it leaves eager context along with the file.
|
|
@@ -181,10 +191,14 @@ Ship markdown, declare what it targets, publish. That's the whole contract:
|
|
|
181
191
|
|
|
182
192
|
```
|
|
183
193
|
skills/<name>/SKILL.md # skill (dir-per-skill, may ship supporting files)
|
|
194
|
+
agents/<name>.md # agent (flat file)
|
|
195
|
+
commands/<name>.md # command (flat file)
|
|
184
196
|
lib/<gem_name>/hyperdrive/guidelines/<name>.md # guideline (flat file)
|
|
185
197
|
```
|
|
186
198
|
|
|
187
|
-
|
|
199
|
+
`<gem_name>` is your gem's name exactly as published, dashes and all — `rails-hyperdrive-sidekiq` ships guidelines under `lib/rails-hyperdrive-sidekiq/hyperdrive/guidelines/`, not `lib/rails/hyperdrive/sidekiq/`.
|
|
200
|
+
|
|
201
|
+
Top-level `skills/`, `agents/`, and `commands/` are the tool-agnostic face of your gem — the same sibling layout `.claude/` has, so relative links between them keep resolving once installed. They are readable by skills.sh and plain git-clone consumers as well as hyperdrive, and are scanned once your gem has opted in (below). `lib/<gem_name>/hyperdrive/` is the hyperdrive-specific root: guidelines, and ERB skill templates (`SKILL.md.erb`, which must stay out of `skills/` so raw ERB never reaches generic consumers). Plain skills shipped under it remain scanned as well. Guidelines, agents, and commands may ship as `<name>.md.erb` in their own roots — rendered against the app's bundle at discovery and installed as `<name>.md`, with a static file of the same name taking precedence.
|
|
188
202
|
|
|
189
203
|
Frontmatter is pure skills.sh: only `name` and `description` are read, so a skill repo's content integrates without modification:
|
|
190
204
|
|
|
@@ -195,25 +209,42 @@ description: Background job conventions for Sidekiq.
|
|
|
195
209
|
---
|
|
196
210
|
```
|
|
197
211
|
|
|
198
|
-
|
|
212
|
+
Agents follow the same contract. Commands are the exception: their frontmatter is optional and never validated, and their identity is the filename stem.
|
|
213
|
+
|
|
214
|
+
Gating (which bundles an artifact installs into) lives in a `hyperdrive.yml` manifest at the gem root (or at the path named by a `hyperdrive_manifest` gemspec metadata key), never in the content. Every key is optional; no manifest (or an empty one) means everything installs universally:
|
|
199
215
|
|
|
200
216
|
```yaml
|
|
201
|
-
|
|
202
|
-
|
|
217
|
+
gems: # gem-wide default: TARGET gem(s), resolved in the bundle ("gem:" is an alias)
|
|
218
|
+
- sidekiq: ">= 7.0, < 9.0" # a list member is a bare name, or a name: requirement pair
|
|
203
219
|
skills: # per-skill overrides, keyed by skill dir relative to its skills root
|
|
204
220
|
jobs-sidekiq:
|
|
205
|
-
|
|
221
|
+
gems:
|
|
222
|
+
- sidekiq: ">= 8.0"
|
|
223
|
+
hyperdrive_version: ">= 0.7" # require a minimum rails-hyperdrive for this artifact
|
|
206
224
|
guidelines: # per-guideline overrides, keyed by filename
|
|
207
225
|
jobs.md:
|
|
208
226
|
gem: sidekiq
|
|
227
|
+
agents: # per-agent overrides, keyed by filename
|
|
228
|
+
sidekiq-reviewer.md:
|
|
229
|
+
gem: sidekiq
|
|
230
|
+
commands: # per-command overrides, keyed by filename
|
|
231
|
+
command_prefix: sidekiq # optional; every command installs as <prefix>-<filename>
|
|
232
|
+
analyze.md:
|
|
233
|
+
gem: sidekiq
|
|
209
234
|
```
|
|
210
235
|
|
|
211
|
-
|
|
236
|
+
A gate naming several targets can be written as a map with one of `any:`/`all:` — `gems: {any: [sidekiq, solid_queue]}` installs when either is bundled, `gems: {all: [devise, pundit]}` only when both are. A bare list is shorthand for `any:`, and an entry's gate replaces the gem-wide default wholesale.
|
|
237
|
+
|
|
238
|
+
`hyperdrive_version:` is valid at the top level too, and is matched against the running rails-hyperdrive rather than the bundle — the sanctioned way to fence content that needs a newer installer. It fences on whichever gem implements artifact discovery and install — today rails-hyperdrive — and that version numbering is guaranteed continuous across any future restructuring of the gem, so a fence like `">= 0.8"` keeps its meaning permanently.
|
|
239
|
+
|
|
240
|
+
Shipping a `hyperdrive.yml` (or declaring `hyperdrive_manifest`) opts your gem in as a companion. Also declare your targets in gemspec metadata. That opts your gem in too, and it is the pre-install targeting signal (how `hyperdrive:discover` suggests you before anyone installs you):
|
|
212
241
|
|
|
213
242
|
```ruby
|
|
214
|
-
spec.metadata["
|
|
243
|
+
spec.metadata["hyperdrive_targets"] = "sidekiq"
|
|
215
244
|
```
|
|
216
245
|
|
|
246
|
+
Companion repos get author-side CI checks by adding `require "hyperdrive/skill_tasks"` to the `Rakefile`: `rake hyperdrive:skills:check` keeps generated skill content in step with its templates, and `rake hyperdrive:manifest:check` lints `hyperdrive.yml` strictly.
|
|
247
|
+
|
|
217
248
|
[docs/COMPANION_GEMS.md](docs/COMPANION_GEMS.md) has the full contract: multi-target artifacts, multi-file skills, per-file gem gating, ERB-templated content, the template/content paired layout that also serves `npx skills` and git-clone consumers, and the collision and dedup rules.
|
|
218
249
|
|
|
219
250
|
---
|
|
@@ -3,14 +3,21 @@ Description:
|
|
|
3
3
|
- adds the rails-hyperdrive server to .mcp.json (Claude Code config),
|
|
4
4
|
merging into any servers already configured there
|
|
5
5
|
- mounts Rails::Hyperdrive::Engine at /_hyperdrive in config/routes.rb (idempotent)
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
- registers the bundler-hyperdrive plugin in the Gemfile, so newly
|
|
7
|
+
bundled companion gems install their artifacts on bundle install
|
|
8
|
+
- adds a .gitignore rule for the hyperdrive:discover cache
|
|
9
|
+
- discovers companion-gem skills, guidelines, agents, and commands in the
|
|
10
|
+
bundle and installs them (skills to .claude/skills/, guidelines to
|
|
11
|
+
.claude/hyperdrive/guidelines/, agents to .claude/agents/, commands to
|
|
12
|
+
.claude/commands/)
|
|
9
13
|
- maintains the .claude/hyperdrive/index.md aggregator and injects one
|
|
10
14
|
@-include line into CLAUDE.md — both only while a companion gem ships
|
|
11
15
|
a guideline, and both removed when the last one goes
|
|
12
16
|
- tracks everything in .hyperdrive/lock.yml
|
|
13
17
|
|
|
18
|
+
The .mcp.json entry and the engine mount are skippable with --skip-mcp;
|
|
19
|
+
existing MCP configuration is left untouched either way.
|
|
20
|
+
|
|
14
21
|
Re-running re-syncs content and leaves locally-modified files untouched
|
|
15
22
|
(skip + warn). Routine content refresh is bin/rails hyperdrive:sync
|
|
16
23
|
(see its --merge, --sidecar, and --overwrite flags for reconciling
|
|
@@ -20,4 +27,5 @@ Examples:
|
|
|
20
27
|
bin/rails hyperdrive:init
|
|
21
28
|
bin/rails hyperdrive:init --mount-at /admin/hyperdrive
|
|
22
29
|
bin/rails hyperdrive:init --skip-content
|
|
30
|
+
bin/rails hyperdrive:init --skip-mcp
|
|
23
31
|
bin/rails hyperdrive:init --dry-run
|
|
@@ -20,14 +20,15 @@ module Rails
|
|
|
20
20
|
MCP_SERVER_KEY = "rails-hyperdrive".freeze
|
|
21
21
|
|
|
22
22
|
GEMFILE = "Gemfile".freeze
|
|
23
|
-
BUNDLER_PLUGIN = "bundler-
|
|
23
|
+
BUNDLER_PLUGIN = "bundler-hyperdrive".freeze
|
|
24
24
|
|
|
25
25
|
# No templates are rendered; source_root exists so Rails resolves the
|
|
26
26
|
# sibling USAGE file for `--help`.
|
|
27
27
|
source_root File.expand_path("templates", __dir__)
|
|
28
28
|
|
|
29
29
|
class_option :mount_at, type: :string, default: DEFAULT_MOUNT_AT, desc: "Engine mount path."
|
|
30
|
-
class_option :skip_content, type: :boolean, default: false, desc: "Skip all .claude content, CLAUDE.md, and the lockfile;
|
|
30
|
+
class_option :skip_content, type: :boolean, default: false, desc: "Skip all .claude content, CLAUDE.md, and the lockfile; leave the .mcp.json, .gitignore, Gemfile, and mount steps."
|
|
31
|
+
class_option :skip_mcp, type: :boolean, default: false, desc: "Skip MCP setup entirely; write no .mcp.json entry and no engine mount."
|
|
31
32
|
class_option :dry_run, type: :boolean, default: false, desc: "Show what would change; write nothing."
|
|
32
33
|
|
|
33
34
|
def verify_environment
|
|
@@ -41,6 +42,8 @@ module Rails
|
|
|
41
42
|
# The write is forced: Thor's conflict prompt would otherwise block the
|
|
42
43
|
# run waiting on stdin.
|
|
43
44
|
def write_mcp_json
|
|
45
|
+
return if options[:skip_mcp]
|
|
46
|
+
|
|
44
47
|
existing = mcp_json_on_disk
|
|
45
48
|
document = existing ? parse_mcp_json(existing) : {}
|
|
46
49
|
return if document.nil?
|
|
@@ -80,6 +83,8 @@ module Rails
|
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
def mount_engine
|
|
86
|
+
return if options[:skip_mcp]
|
|
87
|
+
|
|
83
88
|
routes_file = "config/routes.rb"
|
|
84
89
|
unless File.exist?(::Rails.root.join(routes_file))
|
|
85
90
|
say_status :skip, "no #{routes_file} found; skipping engine mount", :yellow
|
|
@@ -107,9 +112,16 @@ module Rails
|
|
|
107
112
|
def print_summary
|
|
108
113
|
say ""
|
|
109
114
|
say_status :done, "hyperdrive initialized", :green
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
if options[:skip_mcp]
|
|
116
|
+
say " MCP: skipped (--skip-mcp)"
|
|
117
|
+
else
|
|
118
|
+
say " Mount: #{mount_path} (in config/routes.rb)"
|
|
119
|
+
say " Server: #{::Rails::Hyperdrive::McpServer::TOOLS.size} MCP tools at http://localhost:3000#{mount_path}/mcp"
|
|
120
|
+
end
|
|
112
121
|
runner.summary_lines.each { |line| say line } unless options[:skip_content]
|
|
122
|
+
# Every next step is about reaching the MCP endpoint.
|
|
123
|
+
return if options[:skip_mcp]
|
|
124
|
+
|
|
113
125
|
say ""
|
|
114
126
|
say " Next steps:"
|
|
115
127
|
say " 1. bin/rails server"
|
|
@@ -4,8 +4,8 @@ module Rails
|
|
|
4
4
|
module Generators
|
|
5
5
|
module Hyperdrive
|
|
6
6
|
module InstallSummary
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
KIND_ORDER = ::Rails::Hyperdrive::InstallLayout.content_kinds.map(&:lock_kind).freeze
|
|
8
|
+
KIND_WIDTH = KIND_ORDER.map(&:length).max
|
|
9
9
|
|
|
10
10
|
module_function
|
|
11
11
|
|
|
@@ -38,7 +38,7 @@ module Rails
|
|
|
38
38
|
|
|
39
39
|
def installed_counts(entries)
|
|
40
40
|
counts = entries.group_by { |e| e.kind.to_s }.transform_values(&:size)
|
|
41
|
-
"Installed #{quantify(counts[
|
|
41
|
+
"Installed #{KIND_ORDER.map { |kind| quantify(counts[kind].to_i, kind) }.join(", ")}"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def group_by_source(entries)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Description:
|
|
2
2
|
Syncs Rails Hyperdrive content to match the current bundle (run after
|
|
3
3
|
`bundle update` or after adding a companion gem):
|
|
4
|
-
- discovers companion-gem skills
|
|
5
|
-
them (skills to .claude/skills/, guidelines to
|
|
6
|
-
.claude/hyperdrive/guidelines/
|
|
4
|
+
- discovers companion-gem skills, guidelines, agents, and commands in the
|
|
5
|
+
bundle and installs them (skills to .claude/skills/, guidelines to
|
|
6
|
+
.claude/hyperdrive/guidelines/, agents to .claude/agents/, commands to
|
|
7
|
+
.claude/commands/)
|
|
7
8
|
- maintains the .claude/hyperdrive/index.md aggregator and the single
|
|
8
9
|
@-include line in CLAUDE.md — both only while a companion gem ships a
|
|
9
10
|
guideline, and both removed when the last one goes
|
|
@@ -24,8 +25,9 @@ Description:
|
|
|
24
25
|
upstream wholesale. Either way the next sync is quiet: the lock already
|
|
25
26
|
records the delivered upstream, so the same version is never re-offered.
|
|
26
27
|
|
|
27
|
-
Touches no bootstrap artifact: .mcp.json, the engine mount, the
|
|
28
|
-
|
|
28
|
+
Touches no bootstrap artifact: .mcp.json, the engine mount, the Gemfile
|
|
29
|
+
plugin directive, and .gitignore belong to hyperdrive:init and are left
|
|
30
|
+
alone.
|
|
29
31
|
|
|
30
32
|
Examples:
|
|
31
33
|
bin/rails hyperdrive:sync
|