gempilot 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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/skills/using-command-kit/SKILL.md +119 -0
  3. data/.claude/skills/using-command-kit/cli-example.rb +84 -0
  4. data/.claude/skills/using-command-kit/generator-pattern.rb +62 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +281 -0
  7. data/.ruby-version +1 -0
  8. data/CLAUDE.md +45 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +140 -0
  11. data/Rakefile +44 -0
  12. data/data/templates/gem/Gemfile.erb +23 -0
  13. data/data/templates/gem/LICENSE.txt.erb +21 -0
  14. data/data/templates/gem/README.md.erb +25 -0
  15. data/data/templates/gem/Rakefile.erb +36 -0
  16. data/data/templates/gem/bin/console.erb +7 -0
  17. data/data/templates/gem/bin/setup.erb +5 -0
  18. data/data/templates/gem/dotfiles/github/workflows/ci.yml.erb +33 -0
  19. data/data/templates/gem/dotfiles/gitignore +11 -0
  20. data/data/templates/gem/dotfiles/rubocop.yml.erb +209 -0
  21. data/data/templates/gem/dotfiles/ruby-version.erb +1 -0
  22. data/data/templates/gem/exe/gem_name.erb +3 -0
  23. data/data/templates/gem/gemspec.erb +27 -0
  24. data/data/templates/gem/lib/gem_name/version.rb.erb +7 -0
  25. data/data/templates/gem/lib/gem_name.rb.erb +16 -0
  26. data/data/templates/gem/lib/gem_name_extension.rb.erb +20 -0
  27. data/data/templates/gem/rspec.erb +3 -0
  28. data/data/templates/gem/spec/gem_name_spec.rb.erb +5 -0
  29. data/data/templates/gem/spec/spec_helper.rb.erb +10 -0
  30. data/data/templates/gem/spec/zeitwerk_spec.rb.erb +5 -0
  31. data/data/templates/gem/test/gem_name_test.rb.erb +7 -0
  32. data/data/templates/gem/test/test_helper.rb.erb +7 -0
  33. data/data/templates/gem/test/zeitwerk_test.rb.erb +9 -0
  34. data/data/templates/new/.keep +0 -0
  35. data/data/templates/new/command.rb.erb +15 -0
  36. data/docs/command_kit_comparison.md +249 -0
  37. data/docs/command_kit_reference.md +517 -0
  38. data/docs/plans/2026-02-18-gempilot-add-command.md +718 -0
  39. data/docs/superpowers/plans/2026-04-01-rubocop-new-config.md +838 -0
  40. data/docs/superpowers/plans/2026-04-06-dogfood-inflectable.md +659 -0
  41. data/docs/superpowers/plans/2026-04-06-inflection-tests-and-erb-rename.md +166 -0
  42. data/docs/superpowers/plans/2026-04-06-integrate-version-tools.md +162 -0
  43. data/docs/superpowers/plans/2026-04-06-new-readme.md +185 -0
  44. data/docs/version-management-redesign.md +44 -0
  45. data/exe/gempilot +12 -0
  46. data/issues.rec +77 -0
  47. data/lib/core_ext/string/inflection_methods.rb +68 -0
  48. data/lib/core_ext/string/refinements/inflectable.rb +15 -0
  49. data/lib/gempilot/cli/command.rb +17 -0
  50. data/lib/gempilot/cli/commands/bump.rb +49 -0
  51. data/lib/gempilot/cli/commands/console.rb +38 -0
  52. data/lib/gempilot/cli/commands/create.rb +183 -0
  53. data/lib/gempilot/cli/commands/destroy.rb +136 -0
  54. data/lib/gempilot/cli/commands/new.rb +226 -0
  55. data/lib/gempilot/cli/commands/release.rb +40 -0
  56. data/lib/gempilot/cli/gem_builder.rb +105 -0
  57. data/lib/gempilot/cli/gem_context.rb +40 -0
  58. data/lib/gempilot/cli/generator.rb +90 -0
  59. data/lib/gempilot/cli.rb +19 -0
  60. data/lib/gempilot/github_release.rb +30 -0
  61. data/lib/gempilot/project/version.rb +39 -0
  62. data/lib/gempilot/project.rb +111 -0
  63. data/lib/gempilot/strict_shell.rb +18 -0
  64. data/lib/gempilot/version.rb +3 -0
  65. data/lib/gempilot/version_tag.rb +53 -0
  66. data/lib/gempilot/version_task.rb +108 -0
  67. data/lib/gempilot.rb +17 -0
  68. data/notes.md +31 -0
  69. metadata +165 -0
@@ -0,0 +1,659 @@
1
+ # Dogfood String::Inflectable Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Replace `CommandKit::Inflector` with gempilot's own `String::Inflectable` refinement, and wire the template generator to copy all version management domain objects into generated gems.
6
+
7
+ **Architecture:** Create `String::InflectionMethods` (class methods) and `String::Inflectable` (refinement) under `lib/core_ext/`. Switch all 6 call sites from `CommandKit::Inflector.camelize/underscore` to the refinement. Copy the same files into the template directory. Update `gem_builder.rb` to copy all rakelib support files + core_ext into generated gems. Rewrite `version.rake.erb` as thin domain-object wiring.
8
+
9
+ **Tech Stack:** Ruby refinements, Zeitwerk, ERB templates
10
+
11
+ ---
12
+
13
+ ## File Structure
14
+
15
+ ### New in gempilot
16
+ | Path | Responsibility |
17
+ |------|----------------|
18
+ | `lib/core_ext/string/inflection_methods.rb` | `dasherize`, `underscore`, `camelize` as module methods |
19
+ | `lib/core_ext/string/refinements/inflectable.rb` | Refinement wrapping InflectionMethods onto String |
20
+
21
+ ### New in templates
22
+ | Path | Responsibility |
23
+ |------|----------------|
24
+ | `data/templates/gem/lib/core_ext/string/inflection_methods.rb` | Same — shipped to generated gems |
25
+ | `data/templates/gem/lib/core_ext/string/refinements/inflectable.rb` | Same — shipped to generated gems |
26
+ | `data/templates/gem/rakelib/project.rb` | Project introspection for generated gems |
27
+
28
+ ### Modify
29
+ | Path | Change |
30
+ |------|--------|
31
+ | `lib/gempilot.rb:7` | Add `LOADER.ignore` for core_ext |
32
+ | `rakelib/project.rb:1-3,36` | Switch to refinement |
33
+ | `lib/gempilot/cli/gem_context.rb:1,19,27` | Switch to refinement |
34
+ | `lib/gempilot/cli/commands/create.rb:4,102` | Switch to refinement |
35
+ | `lib/gempilot/cli/commands/new.rb:4,120-121` | Switch to refinement |
36
+ | `lib/gempilot/cli/commands/destroy.rb:3,95` | Switch to refinement |
37
+ | `lib/gempilot/cli/gem_builder.rb:49-52` | Copy all rakelib + core_ext files |
38
+ | `data/templates/gem/rakelib/version.rake.erb` | Rewrite to use domain objects |
39
+ | `data/templates/gem/lib/gem_name.rb.erb:9` | Add `LOADER.ignore` for core_ext |
40
+ | `data/templates/gem/Gemfile.erb` | Add `warning` gem |
41
+
42
+ ---
43
+
44
+ ### Task 1: Create inflection files in gempilot
45
+
46
+ **Files:**
47
+ - Create: `lib/core_ext/string/inflection_methods.rb`
48
+ - Create: `lib/core_ext/string/refinements/inflectable.rb`
49
+ - Modify: `lib/gempilot.rb`
50
+
51
+ - [ ] **Step 1: Create `lib/core_ext/string/inflection_methods.rb`**
52
+
53
+ ```ruby
54
+ require "strscan"
55
+
56
+ module String::InflectionMethods
57
+ def dasherize(name) = name.to_s.tr("_", "-")
58
+
59
+ def underscore(name)
60
+ scanner = StringScanner.new(name.to_s)
61
+ new_string = String.new
62
+ until scanner.eos?
63
+ if (separator = scanner.scan(/[_-]+/))
64
+ new_string << ("_" * separator.length)
65
+ else
66
+ if (capitalized = scanner.scan(/[A-Z][a-z\d]+/))
67
+ new_string << capitalized
68
+ elsif (uppercase = scanner.scan(/[A-Z][A-Z\d]*(?=[A-Z_-]|$)/))
69
+ new_string << uppercase
70
+ elsif (lowercase = scanner.scan(/[a-z][a-z\d]*/))
71
+ new_string << lowercase
72
+ else
73
+ raise(ArgumentError, "cannot convert string to underscored: #{scanner.string.inspect}")
74
+ end
75
+ if (separator = scanner.scan(/[_-]+/))
76
+ new_string << ("_" * separator.length)
77
+ elsif !scanner.eos?
78
+ new_string << "_"
79
+ end
80
+ end
81
+ end
82
+ new_string.downcase!
83
+ new_string
84
+ end
85
+
86
+ def camelize(name)
87
+ scanner = StringScanner.new(name.to_s)
88
+ new_string = String.new
89
+ until scanner.eos?
90
+ if (word = scanner.scan(/[A-Za-z\d]+/))
91
+ word.capitalize!
92
+ new_string << word
93
+ elsif (numbers = scanner.scan(/[_-]\d+/))
94
+ new_string << "_#{numbers[1..]}"
95
+ elsif scanner.scan(/[_-]+/)
96
+ elsif scanner.scan(%r{/})
97
+ new_string << "::"
98
+ else
99
+ raise(ArgumentError, "cannot convert string to CamelCase: #{scanner.string.inspect}")
100
+ end
101
+ end
102
+ new_string
103
+ end
104
+ end
105
+ ```
106
+
107
+ - [ ] **Step 2: Create `lib/core_ext/string/refinements/inflectable.rb`**
108
+
109
+ ```ruby
110
+ require_relative "../inflection_methods"
111
+
112
+ module String::Inflectable
113
+ refine String.singleton_class do
114
+ import_methods String::InflectionMethods
115
+ end
116
+
117
+ refine String do
118
+ def dasherize
119
+ self.class.dasherize(self)
120
+ end
121
+
122
+ def underscore
123
+ self.class.underscore(self)
124
+ end
125
+
126
+ def camelize
127
+ self.class.camelize(self)
128
+ end
129
+ end
130
+ end
131
+ ```
132
+
133
+ - [ ] **Step 3: Add `LOADER.ignore` in `lib/gempilot.rb`**
134
+
135
+ In `lib/gempilot.rb`, add the ignore line after `LOADER = Zeitwerk::Loader.for_gem`:
136
+
137
+ ```ruby
138
+ module Gempilot
139
+ LOADER = Zeitwerk::Loader.for_gem
140
+ LOADER.inflector.inflect("cli" => "CLI")
141
+ LOADER.ignore("#{__dir__}/core_ext")
142
+ LOADER.setup
143
+ ```
144
+
145
+ - [ ] **Step 4: Verify the refinement works**
146
+
147
+ Run: `ruby -e 'require_relative "lib/core_ext/string/refinements/inflectable"; using String::Inflectable; puts "my_gem".camelize'`
148
+ Expected: `MyGem`
149
+
150
+ - [ ] **Step 5: Verify Zeitwerk still loads cleanly**
151
+
152
+ Run: `bundle exec ruby -e 'require "gempilot"; Gempilot::LOADER.eager_load(force: true); puts "OK"'`
153
+ Expected: `OK`
154
+
155
+ - [ ] **Step 6: Commit**
156
+
157
+ ```bash
158
+ git add lib/core_ext/ lib/gempilot.rb
159
+ git commit -m "Add String::Inflectable refinement for inflection"
160
+ ```
161
+
162
+ ---
163
+
164
+ ### Task 2: Switch gempilot from CommandKit::Inflector to String::Inflectable
165
+
166
+ **Files:**
167
+ - Modify: `rakelib/project.rb`
168
+ - Modify: `lib/gempilot/cli/gem_context.rb`
169
+ - Modify: `lib/gempilot/cli/commands/create.rb`
170
+ - Modify: `lib/gempilot/cli/commands/new.rb`
171
+ - Modify: `lib/gempilot/cli/commands/destroy.rb`
172
+
173
+ - [ ] **Step 1: Switch `rakelib/project.rb`**
174
+
175
+ Replace line 3 (`require "command_kit/inflector"`) with:
176
+ ```ruby
177
+ require_relative "../lib/core_ext/string/refinements/inflectable"
178
+ ```
179
+
180
+ Add `using String::Inflectable` inside the class body (after `class Project`).
181
+
182
+ Replace line 36 (`Object.const_get(CommandKit::Inflector.camelize(name))`) with:
183
+ ```ruby
184
+ Object.const_get(name.camelize)
185
+ ```
186
+
187
+ - [ ] **Step 2: Switch `lib/gempilot/cli/gem_context.rb`**
188
+
189
+ Replace line 1 (`require "command_kit/inflector"`) with:
190
+ ```ruby
191
+ require_relative "../../core_ext/string/refinements/inflectable"
192
+ ```
193
+
194
+ Add `using String::Inflectable` as the first line inside `module GemContext`.
195
+
196
+ Replace line 19 (`CommandKit::Inflector.camelize(@require_path)`) with:
197
+ ```ruby
198
+ @require_path.camelize
199
+ ```
200
+
201
+ Replace line 27 (`CommandKit::Inflector.underscore(p)`) with:
202
+ ```ruby
203
+ p.underscore
204
+ ```
205
+
206
+ - [ ] **Step 3: Switch `lib/gempilot/cli/commands/create.rb`**
207
+
208
+ Replace line 4 (`require "command_kit/inflector"`) with:
209
+ ```ruby
210
+ require_relative "../../../core_ext/string/refinements/inflectable"
211
+ ```
212
+
213
+ Add `using String::Inflectable` inside the `Create` class body.
214
+
215
+ Replace line 102 (`CommandKit::Inflector.camelize(@require_path)`) with:
216
+ ```ruby
217
+ @require_path.camelize
218
+ ```
219
+
220
+ - [ ] **Step 4: Switch `lib/gempilot/cli/commands/new.rb`**
221
+
222
+ Replace line 4 (`require "command_kit/inflector"`) with:
223
+ ```ruby
224
+ require_relative "../../../core_ext/string/refinements/inflectable"
225
+ ```
226
+
227
+ Add `using String::Inflectable` inside the `New` class body.
228
+
229
+ Replace line 120 (`CommandKit::Inflector.underscore(name)`) with:
230
+ ```ruby
231
+ name.underscore
232
+ ```
233
+
234
+ Replace line 121 (`CommandKit::Inflector.camelize(name)`) with:
235
+ ```ruby
236
+ name.camelize
237
+ ```
238
+
239
+ - [ ] **Step 5: Switch `lib/gempilot/cli/commands/destroy.rb`**
240
+
241
+ Replace line 3 (`require "command_kit/inflector"`) with:
242
+ ```ruby
243
+ require_relative "../../../core_ext/string/refinements/inflectable"
244
+ ```
245
+
246
+ Add `using String::Inflectable` inside the `Destroy` class body.
247
+
248
+ Replace line 95 (`CommandKit::Inflector.underscore(name)`) with:
249
+ ```ruby
250
+ name.underscore
251
+ ```
252
+
253
+ - [ ] **Step 6: Run full test suite**
254
+
255
+ Run: `bundle exec rake test spec`
256
+ Expected: All pass
257
+
258
+ - [ ] **Step 7: Run rubocop**
259
+
260
+ Run: `bundle exec rubocop`
261
+ Expected: No offenses
262
+
263
+ - [ ] **Step 8: Commit**
264
+
265
+ ```bash
266
+ git add rakelib/project.rb lib/gempilot/cli/gem_context.rb lib/gempilot/cli/commands/create.rb lib/gempilot/cli/commands/new.rb lib/gempilot/cli/commands/destroy.rb
267
+ git commit -m "Switch from CommandKit::Inflector to String::Inflectable"
268
+ ```
269
+
270
+ ---
271
+
272
+ ### Task 3: Add inflection + project templates for generated gems
273
+
274
+ **Files:**
275
+ - Create: `data/templates/gem/lib/core_ext/string/inflection_methods.rb`
276
+ - Create: `data/templates/gem/lib/core_ext/string/refinements/inflectable.rb`
277
+ - Create: `data/templates/gem/rakelib/project.rb`
278
+
279
+ - [ ] **Step 1: Copy inflection files to template directory**
280
+
281
+ ```bash
282
+ mkdir -p data/templates/gem/lib/core_ext/string/refinements
283
+ cp lib/core_ext/string/inflection_methods.rb data/templates/gem/lib/core_ext/string/inflection_methods.rb
284
+ cp lib/core_ext/string/refinements/inflectable.rb data/templates/gem/lib/core_ext/string/refinements/inflectable.rb
285
+ ```
286
+
287
+ - [ ] **Step 2: Create `data/templates/gem/rakelib/project.rb`**
288
+
289
+ This is gempilot's own `rakelib/project.rb` but with the require path adjusted (rakelib is at gem root, so `../lib/` is correct — same as gempilot's own path):
290
+
291
+ ```ruby
292
+ require "pathname"
293
+ require "warning"
294
+ require_relative "../lib/core_ext/string/refinements/inflectable"
295
+ require_relative "project_version"
296
+
297
+ # Introspects a gem project directory to discover its name, module,
298
+ # and version. Used by rake tasks to drive version lifecycle operations.
299
+ class Project
300
+ class ProjectIntrospectionError < StandardError; end
301
+
302
+ REDEFINITION_WARNING = /previous definition of VERSION was here/
303
+ REINITIALIZATION_WARNING = /already initialized constant [^\s]+::VERSION/
304
+ private_constant :REDEFINITION_WARNING, :REINITIALIZATION_WARNING
305
+
306
+ using String::Inflectable
307
+
308
+ attr_reader :root
309
+
310
+ def initialize(root = __dir__)
311
+ @root = Pathname(root)
312
+ @verifications = Set.new
313
+ end
314
+
315
+ def lib
316
+ root.join("lib")
317
+ .tap { verify_existence! it }
318
+ end
319
+
320
+ def lib_project
321
+ @lib_project ||= fetch_lib_project
322
+ end
323
+
324
+ def name
325
+ lib_project.basename.to_s
326
+ end
327
+
328
+ def klass
329
+ Object.const_get(name.camelize)
330
+ end
331
+
332
+ def version
333
+ @version ||= fetch_version
334
+ end
335
+
336
+ def refresh_version!
337
+ @version = fetch_version
338
+ end
339
+
340
+ def increment_version
341
+ version.next_version
342
+ end
343
+
344
+ def version_tag = version.tag
345
+
346
+ def version_value = version.value
347
+
348
+ def write_version!(old_version, new_version)
349
+ with_version_file do |f|
350
+ source = f.read
351
+
352
+ unless source.match?(Regexp.escape(old_version.value))
353
+ abort "Expected to find #{old_version.value} in #{f.path} but did not"
354
+ end
355
+
356
+ f.rewind
357
+ f.write source.gsub(old_version.value, new_version.value)
358
+ end
359
+ end
360
+
361
+ private
362
+
363
+ def with_version_file
364
+ version.path.open(File::RDWR, 0o644) do |f|
365
+ f.flock File::LOCK_EX
366
+ yield f
367
+ f.truncate(f.pos)
368
+ end
369
+ end
370
+
371
+ def fetch_lib_project
372
+ files = lib.glob("*.rb")
373
+ dirs = files.map { it.sub_ext("") }.select(&:directory?)
374
+ case dirs.count
375
+ in 0 then raise ProjectIntrospectionError, "Could not identify project dir"
376
+ in (2..)
377
+ msg = "Found more than one possible project name:\n - #{dirs.join("\n - ")}"
378
+ raise ProjectIntrospectionError, msg
379
+ in 1 then dirs.first
380
+ end
381
+ end
382
+
383
+ def fetch_version
384
+ Warning.ignore(REDEFINITION_WARNING)
385
+ Warning.ignore(REINITIALIZATION_WARNING)
386
+ path = lib_project
387
+ .join("version.rb")
388
+ .tap { verify_existence! it }
389
+ .tap { load it }
390
+
391
+ value = klass.const_get(:VERSION)
392
+
393
+ Version.new(path:, value:)
394
+ end
395
+
396
+ def verify_existence!(path)
397
+ return true if @verifications.member? path
398
+
399
+ raise ProjectIntrospectionError, "Expected #{path} to exist but does not" unless path.exist?
400
+
401
+ @verifications.add path
402
+ end
403
+ end
404
+ ```
405
+
406
+ - [ ] **Step 3: Commit**
407
+
408
+ ```bash
409
+ git add data/templates/gem/lib/core_ext/ data/templates/gem/rakelib/project.rb
410
+ git commit -m "Add inflection and Project templates for generated gems"
411
+ ```
412
+
413
+ ---
414
+
415
+ ### Task 4: Rewrite version.rake.erb to use domain objects
416
+
417
+ **Files:**
418
+ - Rewrite: `data/templates/gem/rakelib/version.rake.erb`
419
+
420
+ - [ ] **Step 1: Rewrite `data/templates/gem/rakelib/version.rake.erb`**
421
+
422
+ Replace the entire file with:
423
+
424
+ ```ruby
425
+ require_relative "project"
426
+ require_relative "version_tag"
427
+ require_relative "github_release"
428
+
429
+ root_path = File.expand_path("../", __dir__)
430
+ project = Project.new(root_path)
431
+
432
+ namespace :version do
433
+ desc "Display the current version"
434
+ task(:current) { puts "Current version: #{project.version_value}" }
435
+
436
+ desc "Bump the patch version"
437
+ task :bump do
438
+ old_version = project.version
439
+ new_version = project.increment_version
440
+ project.write_version!(old_version, new_version)
441
+ project.refresh_version!
442
+ puts "Version bumped from #{old_version.value} to #{project.version_value}"
443
+ end
444
+
445
+ desc "Commit the version change"
446
+ task(:commit) { VersionTag.new(project.version).create }
447
+
448
+ desc "Tag the current version"
449
+ task(:tag) { VersionTag.new(project.version).tag }
450
+
451
+ desc "Untag the current version"
452
+ task(:untag) { VersionTag.new(project.version).untag }
453
+
454
+ desc "Reset the last version bump commit"
455
+ task :reset do
456
+ VersionTag.new(project.version).reset
457
+ project.refresh_version!
458
+ end
459
+
460
+ desc "Revert the last version bump commit"
461
+ task :revert do
462
+ VersionTag.new(project.version).revert
463
+ project.refresh_version!
464
+ end
465
+
466
+ desc "Bump version, commit, and tag"
467
+ task release: ["version:bump", "version:commit", "version:tag"]
468
+
469
+ desc "Untag and reset version"
470
+ task unrelease: ["version:untag", "version:reset"]
471
+
472
+ namespace :github do
473
+ desc "Create a GitHub release for the current version"
474
+ task(:release) { GithubRelease.new(project.version_tag).create }
475
+
476
+ desc "Delete the GitHub release for the current version"
477
+ task(:unrelease) { GithubRelease.new(project.version_tag).destroy }
478
+
479
+ desc "List GitHub releases"
480
+ task(:list) { GithubRelease.new(project.version_tag).list }
481
+ end
482
+ end
483
+ ```
484
+
485
+ Note: This file no longer needs ERB — it has zero interpolations. Rename it from `.erb` to plain `.rake` if desired, but the generator's `erb` method handles non-ERB content fine (ERB passes through plain text unchanged). Keeping the `.erb` extension avoids changing the `erb` call in `gem_builder.rb`.
486
+
487
+ - [ ] **Step 2: Commit**
488
+
489
+ ```bash
490
+ git add data/templates/gem/rakelib/version.rake.erb
491
+ git commit -m "Rewrite version.rake.erb to use domain objects"
492
+ ```
493
+
494
+ ---
495
+
496
+ ### Task 5: Update gem_builder.rb to copy all support files
497
+
498
+ **Files:**
499
+ - Modify: `lib/gempilot/cli/gem_builder.rb:49-52`
500
+
501
+ - [ ] **Step 1: Expand `render_version_rake` and add `render_core_ext`**
502
+
503
+ Replace the `render_version_rake` method (lines 49-52) with:
504
+
505
+ ```ruby
506
+ def render_version_rake
507
+ mkdir "#{@gem_name}/rakelib"
508
+ erb "rakelib/version.rake.erb", "#{@gem_name}/rakelib/version.rake"
509
+ cp "rakelib/project.rb", "#{@gem_name}/rakelib/project.rb"
510
+ cp "rakelib/project_version.rb", "#{@gem_name}/rakelib/project_version.rb"
511
+ cp "rakelib/version_tag.rb", "#{@gem_name}/rakelib/version_tag.rb"
512
+ cp "rakelib/github_release.rb", "#{@gem_name}/rakelib/github_release.rb"
513
+ cp "rakelib/strict_shell.rb", "#{@gem_name}/rakelib/strict_shell.rb"
514
+ end
515
+ ```
516
+
517
+ Add a new method and call it from `render_core_templates` (add the call after line 46, `render_version_rake`):
518
+
519
+ ```ruby
520
+ def render_core_ext
521
+ mkdir "#{@gem_name}/lib/core_ext"
522
+ mkdir "#{@gem_name}/lib/core_ext/string"
523
+ mkdir "#{@gem_name}/lib/core_ext/string/refinements"
524
+ cp "lib/core_ext/string/inflection_methods.rb",
525
+ "#{@gem_name}/lib/core_ext/string/inflection_methods.rb"
526
+ cp "lib/core_ext/string/refinements/inflectable.rb",
527
+ "#{@gem_name}/lib/core_ext/string/refinements/inflectable.rb"
528
+ end
529
+ ```
530
+
531
+ Add the call in `render_core_templates` after `render_version_rake`:
532
+
533
+ ```ruby
534
+ render_version_rake
535
+ render_core_ext
536
+ ```
537
+
538
+ - [ ] **Step 2: Run tests**
539
+
540
+ Run: `bundle exec rake test spec`
541
+ Expected: All pass
542
+
543
+ - [ ] **Step 3: Commit**
544
+
545
+ ```bash
546
+ git add lib/gempilot/cli/gem_builder.rb
547
+ git commit -m "Wire generator to copy rakelib support files and core_ext"
548
+ ```
549
+
550
+ ---
551
+
552
+ ### Task 6: Update generated gem Zeitwerk config and Gemfile
553
+
554
+ **Files:**
555
+ - Modify: `data/templates/gem/lib/gem_name.rb.erb`
556
+ - Modify: `data/templates/gem/Gemfile.erb`
557
+
558
+ - [ ] **Step 1: Add `LOADER.ignore` to `data/templates/gem/lib/gem_name.rb.erb`**
559
+
560
+ Change the non-hyphenated branch from:
561
+ ```erb
562
+ module <%= @module_name %>
563
+ LOADER = Zeitwerk::Loader.for_gem
564
+ LOADER.setup
565
+ ```
566
+
567
+ To:
568
+ ```erb
569
+ module <%= @module_name %>
570
+ LOADER = Zeitwerk::Loader.for_gem
571
+ LOADER.ignore("#{__dir__}/core_ext")
572
+ LOADER.setup
573
+ ```
574
+
575
+ - [ ] **Step 2: Add `warning` gem to `data/templates/gem/Gemfile.erb`**
576
+
577
+ Add `gem "warning"` after the rubocop gems (before the closing of the file). Insert after the last `<% end -%>` on line 22:
578
+
579
+ ```erb
580
+ <% if @test_framework == :rspec -%>
581
+ gem "rubocop-rspec"
582
+ <% end -%>
583
+ gem "warning"
584
+ ```
585
+
586
+ - [ ] **Step 3: Commit**
587
+
588
+ ```bash
589
+ git add data/templates/gem/lib/gem_name.rb.erb data/templates/gem/Gemfile.erb
590
+ git commit -m "Add Zeitwerk core_ext ignore and warning gem to generated gems"
591
+ ```
592
+
593
+ ---
594
+
595
+ ### Task 7: Integration test
596
+
597
+ - [ ] **Step 1: Generate a test gem**
598
+
599
+ ```bash
600
+ cd /var/tmp
601
+ bundle exec ruby -I /workspace/gempilot/lib /workspace/gempilot/exe/gempilot create test_gem --test minitest --no-exe --no-git --summary "Test" --author "Test" --email "test@test.com"
602
+ ```
603
+
604
+ - [ ] **Step 2: Verify generated files exist**
605
+
606
+ ```bash
607
+ ls test_gem/rakelib/
608
+ ```
609
+
610
+ Expected: `github_release.rb project.rb project_version.rb strict_shell.rb version.rake version_tag.rb`
611
+
612
+ ```bash
613
+ ls test_gem/lib/core_ext/string/refinements/
614
+ ```
615
+
616
+ Expected: `inflectable.rb`
617
+
618
+ - [ ] **Step 3: Verify rake tasks work**
619
+
620
+ ```bash
621
+ cd test_gem && bundle install && bundle exec rake -T version
622
+ ```
623
+
624
+ Expected: Lists 12 version tasks
625
+
626
+ ```bash
627
+ bundle exec rake version:current
628
+ ```
629
+
630
+ Expected: `Current version: 0.1.0`
631
+
632
+ - [ ] **Step 4: Verify full suite passes**
633
+
634
+ ```bash
635
+ bundle exec rake
636
+ ```
637
+
638
+ Expected: Tests + rubocop pass
639
+
640
+ - [ ] **Step 5: Clean up**
641
+
642
+ ```bash
643
+ cd /workspace/gempilot
644
+ rm -rf /var/tmp/test_gem
645
+ ```
646
+
647
+ - [ ] **Step 6: Run gempilot's own full suite**
648
+
649
+ Run: `bundle exec rake test spec`
650
+ Expected: All pass
651
+
652
+ Run: `bundle exec rubocop`
653
+ Expected: No offenses
654
+
655
+ - [ ] **Step 7: Commit any fixes needed**
656
+
657
+ ```bash
658
+ git add -A && git commit -m "Fix integration issues" # only if fixes were needed
659
+ ```