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,7 @@
1
+ ENV["MT_NO_PLUGINS"] = "1" unless ENV["RM_INFO"]
2
+
3
+ require "minitest/autorun"
4
+ require "minitest/reporters"
5
+ Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)]
6
+
7
+ require "<%= @require_path %>"
@@ -0,0 +1,9 @@
1
+ require "test_helper"
2
+
3
+ class ZeitwerkTest < Minitest::Test
4
+ def test_eager_loading
5
+ <%= @module_name %>::LOADER.eager_load(force: true)
6
+
7
+ assert_kind_of Zeitwerk::Loader, <%= @module_name %>::LOADER
8
+ end
9
+ end
File without changes
@@ -0,0 +1,15 @@
1
+ require_relative "../command"
2
+
3
+ module <%= @gem_module %>
4
+ class CLI
5
+ module Commands
6
+ class <%= @command_name %> < Command
7
+ description "TODO: describe the <%= @command_file_name %> command"
8
+
9
+ def run
10
+ puts "TODO: implement <%= @command_file_name %>"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,249 @@
1
+ # Gempilot vs Ronin: CommandKit Usage Comparison
2
+
3
+ ## How Gempilot Uses CommandKit
4
+
5
+ ### Current Usage (after restructuring)
6
+
7
+ | CommandKit Module | Used? | Where |
8
+ |---|---|---|
9
+ | `CommandKit::Commands` | Yes | `Gempilot::CLI` - top-level command dispatcher |
10
+ | `CommandKit::Commands::AutoLoad` | Yes | `Gempilot::CLI` - auto-discovers commands from `cli/commands/` |
11
+ | `CommandKit::Options::Version` | Yes | `Gempilot::CLI` - `--version` flag |
12
+ | `CommandKit::Command` | Yes | `Gempilot::CLI::Command` - base class for all commands |
13
+ | `CommandKit::Colors` | Yes | `Gempilot::CLI::Command` and `Gempilot::CLI::Generator` |
14
+ | `CommandKit::FileUtils` | Yes | `Gempilot::CLI::Generator` - `erb()` method |
15
+ | `CommandKit::Options` | Yes | `Commands::New` - all option definitions (implicit via Command) |
16
+ | `CommandKit::Arguments` | Yes | `Commands::New` - `gem_name` argument (implicit via Command) |
17
+ | `CommandKit::Inflector` | **No** | Hand-rolled `inflect_module` in `Commands::New` instead |
18
+ | `CommandKit::Interactive` | **No** | Not included in base Command |
19
+ | `CommandKit::Description` | Yes | `Commands::New` - `description "Create a new gem"` |
20
+ | `CommandKit::Usage` | Yes | `Commands::New` - `usage "[options] GEM_NAME"` |
21
+ | `CommandKit::Printing` | Yes | Implicit via Command |
22
+ | `CommandKit::Printing::Indent` | **No** | |
23
+ | `CommandKit::Printing::Fields` | **No** | |
24
+ | `CommandKit::Printing::Lists` | **No** | |
25
+ | `CommandKit::Printing::Tables` | **No** | |
26
+ | `CommandKit::Terminal` | **No** | |
27
+ | `CommandKit::Pager` | **No** | |
28
+ | `CommandKit::OS` | **No** | |
29
+ | `CommandKit::XDG` | **No** | |
30
+ | `CommandKit::BugReport` | **No** | |
31
+ | `CommandKit::Help::Man` | **No** | |
32
+ | `CommandKit::Edit` | **No** | |
33
+ | `CommandKit::Examples` | **No** | No examples defined on any command |
34
+ | `CommandKit::ExceptionHandler` | Yes | Implicit via Command |
35
+
36
+ ### What Gempilot Hand-Rolls Instead of Using CommandKit
37
+
38
+ 1. **Inflector** - `Commands::New#inflect_module` (line 113-115) does:
39
+ ```ruby
40
+ def inflect_module(name)
41
+ name.split(/[-_]/).map(&:capitalize).join
42
+ end
43
+ ```
44
+ Should use `CommandKit::Inflector.camelize(name)` which handles the same transformation plus edge cases (e.g., `/` to `::` for nested modules).
45
+
46
+ 2. **Generator module** - `Gempilot::CLI::Generator` reimplements ronin-core's Generator pattern. This is intentional (own implementation, no ronin-core dependency), but it duplicates logic that `CommandKit::FileUtils` already partly provides (`erb` method). The Generator adds `print_action`, `mkdir`, `cp`, `sh`, `touch`, `chmod` wrappers with colored output.
47
+
48
+ 3. **Shell execution** - `Gempilot::Commands#sh` uses a custom `Runner` class with PTY/Open3 pipeline for command execution. The Generator module has its own simpler `sh` that calls `system()`. Two different shell execution paths exist.
49
+
50
+ ---
51
+
52
+ ## How Ronin Uses CommandKit (Reference Implementation)
53
+
54
+ ### Module Usage
55
+
56
+ | CommandKit Module | Used? | Where |
57
+ |---|---|---|
58
+ | `CommandKit::Commands` | Yes | `Ronin::CLI` |
59
+ | `CommandKit::Commands::AutoLoad` | Yes | `Ronin::CLI`, `Commands::New`, and many others |
60
+ | `CommandKit::Options::Version` | Yes | `Ronin::CLI` |
61
+ | `CommandKit::Command` | Yes (via ronin-core) | `Core::CLI::Command` base class |
62
+ | `CommandKit::Colors` | Yes | `Core::CLI::Generator`, syntax highlighting |
63
+ | `CommandKit::FileUtils` | Yes | `Core::CLI::Generator` |
64
+ | `CommandKit::Options` | Yes | Every command |
65
+ | `CommandKit::Arguments` | Yes | Every command |
66
+ | `CommandKit::Interactive` | **No** | Not used in ronin itself |
67
+ | `CommandKit::Description` | Yes | Every command |
68
+ | `CommandKit::Usage` | Yes | Every command |
69
+ | `CommandKit::Examples` | Yes | Most commands have examples |
70
+ | `CommandKit::Printing` | Yes | Implicit via Command |
71
+ | `CommandKit::Printing::Indent` | Yes | Output formatting |
72
+ | `CommandKit::Printing::Tables` | Yes | Database query output |
73
+ | `CommandKit::Terminal` | Yes | Width-aware output |
74
+ | `CommandKit::Pager` | Yes | Long output paging |
75
+ | `CommandKit::OS` | Yes | OS-specific behavior |
76
+ | `CommandKit::BugReport` | Yes | `Core::CLI::Command` base class |
77
+ | `CommandKit::Help::Man` | Yes | Every command has `man_page` |
78
+ | `CommandKit::Inflector` | Yes | Used internally for command name derivation |
79
+
80
+ ### Ronin Patterns Worth Adopting
81
+
82
+ 1. **Every command has `examples`** - Ronin defines usage examples on almost every command:
83
+ ```ruby
84
+ examples [
85
+ 'project foo',
86
+ 'script foo.rb'
87
+ ]
88
+ ```
89
+
90
+ 2. **`bug_report_url` on base command** - Set once, inherited everywhere:
91
+ ```ruby
92
+ class Command < Core::CLI::Command
93
+ bug_report_url 'https://github.com/ronin-rb/ronin/issues/new'
94
+ end
95
+ ```
96
+
97
+ 3. **Man pages** - Every command declares `man_page 'ronin-command.1'` for full documentation.
98
+
99
+ 4. **Nested AutoLoad** - The `new` command is itself a Commands container with AutoLoad:
100
+ ```ruby
101
+ class New < Command
102
+ include CommandKit::Commands::AutoLoad.new(
103
+ dir: "#{__dir__}/new",
104
+ namespace: "#{self}"
105
+ )
106
+ end
107
+ ```
108
+ This allows `ronin new project`, `ronin new script`, etc. Each subcommand is a separate file in `commands/new/`.
109
+
110
+ 5. **Core::CLI::Generator** builds on CommandKit::FileUtils - It adds `template_dir`, `print_action`, and wrappers for `mkdir`, `cp`, `erb`, `chmod`, `sh`. Gempilot's Generator follows this pattern.
111
+
112
+ 6. **Consistent use of Inflector** - Ronin never hand-rolls string case conversion.
113
+
114
+ ---
115
+
116
+ ## Side-by-Side: CLI Entry Points
117
+
118
+ ### Gempilot
119
+ ```ruby
120
+ # lib/gempilot/cli.rb
121
+ class Gempilot::CLI
122
+ include CommandKit::Commands
123
+ include CommandKit::Commands::AutoLoad.new(
124
+ dir: "#{__dir__}/cli/commands",
125
+ namespace: "#{self}::Commands"
126
+ )
127
+ include CommandKit::Options::Version
128
+
129
+ command_name "gempilot"
130
+ version Gempilot::VERSION
131
+ end
132
+ ```
133
+
134
+ ### Ronin
135
+ ```ruby
136
+ # lib/ronin/cli.rb
137
+ class Ronin::CLI
138
+ include CommandKit::Commands
139
+ include CommandKit::Commands::AutoLoad.new(
140
+ dir: "#{__dir__}/cli/commands",
141
+ namespace: "#{self}::Commands"
142
+ )
143
+ include CommandKit::Options::Version
144
+ include Core::CLI::Help::Banner
145
+
146
+ command_name 'ronin'
147
+ version Ronin::VERSION
148
+
149
+ command_aliases['enc'] = 'encode'
150
+ command_aliases['dec'] = 'decode'
151
+ end
152
+ ```
153
+
154
+ **Verdict:** Nearly identical. Gempilot follows the pattern correctly. Ronin adds `command_aliases` and a help banner, both nice touches.
155
+
156
+ ---
157
+
158
+ ## Side-by-Side: Generator Commands
159
+
160
+ ### Gempilot - `Commands::New`
161
+ ```ruby
162
+ class New < Command
163
+ include Generator
164
+
165
+ template_dir File.join(Gempilot::ROOT, "data", "templates", "gem")
166
+
167
+ option :summary, value: { type: String }, desc: "Gem summary"
168
+ option :test, value: { type: { "minitest" => :minitest, "rspec" => :rspec }, default: :minitest }, desc: "Test framework"
169
+ argument :gem_name, required: true, desc: "Name of the gem"
170
+
171
+ def run(gem_name)
172
+ @gem_name = gem_name
173
+ @module_name = inflect_module(gem_name)
174
+ mkdir gem_name
175
+ erb "gemspec.erb", "#{gem_name}/#{gem_name}.gemspec"
176
+ # ...
177
+ end
178
+ end
179
+ ```
180
+
181
+ ### Ronin - `Commands::New::Project`
182
+ ```ruby
183
+ class Project < Command
184
+ include Core::CLI::Generator
185
+
186
+ template_dir File.join(ROOT, 'data', 'templates', 'project')
187
+
188
+ option :git, desc: 'Initializes a git repo'
189
+ option :ruby_version, value: { type: String, usage: 'VERSION', default: RUBY_VERSION }, desc: '...'
190
+ argument :path, required: true, desc: 'The directory to create'
191
+
192
+ description 'Creates a new Ruby project directory'
193
+ man_page 'ronin-new-project.1'
194
+
195
+ def run(path)
196
+ @project_name = File.basename(path)
197
+ @ruby_version = options[:ruby_version]
198
+ mkdir path
199
+ erb 'Gemfile.erb', File.join(path, 'Gemfile')
200
+ # ...
201
+ end
202
+ end
203
+ ```
204
+
205
+ **Verdict:** Same pattern. Gempilot's version is correct. Ronin adds `man_page` declaration and uses `usage:` on option values for better help output.
206
+
207
+ ---
208
+
209
+ ## Grading: Gempilot's Idiomatic Use of CommandKit
210
+
211
+ ### Grade: **B+**
212
+
213
+ Gempilot's restructured codebase follows the command_kit patterns well. The CLI entry, base command, AutoLoad, option/argument definitions, and Generator module all match the Ronin reference closely.
214
+
215
+ ### What Earns the Grade
216
+
217
+ **Done right:**
218
+ - CLI with `Commands` + `AutoLoad` - textbook implementation
219
+ - Base `Command` class with `Colors` - follows the pattern
220
+ - Generator module following ronin-core's design
221
+ - Option definitions with `value:` Hash, proper types
222
+ - Argument definitions with `required: true`
223
+ - `template_dir` class method pattern
224
+ - `description` and `usage` on commands
225
+ - ERB templates with instance variable binding
226
+ - Executable entry point: thin `CLI.start`
227
+
228
+ **Holding it back from A:**
229
+
230
+ 1. **`CommandKit::Inflector` not used** - Hand-rolled `inflect_module` instead of `Inflector.camelize`. This is specifically called out in CLAUDE.md issue #2.
231
+
232
+ 2. **No `CommandKit::Interactive`** - CLAUDE.md issue #1 asks for interactive prompting when arguments are missing. `CommandKit::Interactive` provides `ask`, `ask_yes_or_no`, `ask_multiple_choice` - exactly what's needed.
233
+
234
+ 3. **No `examples` on any command** - Ronin defines examples on every command. Gempilot defines none. Easy win for help output.
235
+
236
+ 4. **No `bug_report_url`** - Low effort, high value for user experience.
237
+
238
+ 5. **Two shell execution paths** - `Generator#sh` (uses `system`) and `Commands#sh` / `Runner` (uses Open3 pipeline with PTY). Should converge.
239
+
240
+ 6. **`CommandMapper` integration unclear** - `CommandMappers::Bundle` is a 1091-line DSL that's not wired into any command yet. The `Commands::New#run` calls `sh 'bundle', 'install'` directly instead of using the typed DSL.
241
+
242
+ ### What Would Make It an A
243
+
244
+ 1. Replace `inflect_module` with `CommandKit::Inflector.camelize`
245
+ 2. Include `CommandKit::Interactive` in base Command, use it for missing-argument prompting
246
+ 3. Add `examples` to `Commands::New`
247
+ 4. Add `bug_report_url` to base Command
248
+ 5. Wire `CommandMappers::Bundle` into the `new` command for `bundle install` / `bundle config` operations
249
+ 6. Add `CommandKit::Printing::Indent` for structured output during generation