gempilot 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CLAUDE.md +3 -2
- data/Rakefile +7 -8
- data/data/templates/gem/Gemfile.erb +1 -0
- data/docs/superpowers/plans/2026-06-09-address-review-critiques.md +679 -0
- data/issues.rec +76 -10
- data/lib/core_ext/string/inflection_methods.rb +3 -1
- data/lib/gempilot/cli/commands/create.rb +12 -0
- data/lib/gempilot/cli/commands/destroy.rb +9 -23
- data/lib/gempilot/cli/commands/new.rb +19 -36
- data/lib/gempilot/cli/gem_context.rb +9 -13
- data/lib/gempilot/gem_constant.rb +61 -0
- data/lib/gempilot/version.rb +1 -1
- data/lib/gempilot/version_tag.rb +6 -2
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48657265841f0000324b7cfca38d98ed1a45a59e7906810660a8e9e047042286
|
|
4
|
+
data.tar.gz: 9a084c85bfeec3ea094da9e7b7b245a0e30a050631db84acf8d434e88470b948
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 677fa36cce5bf93834debebf366296289b5fe69baf1ecb325d2169aa03140e928373a36052d4e4accfff37aebdfdb5562bbdda09244c5e6e15b1f51b982397c9
|
|
7
|
+
data.tar.gz: 8afb56703f50f6f42c56b7049167c498656576c8c4e5061cdd11883b9d32efe0ea7906203391dc809d4197c222f654010439fd38ead128ce9783446dafa50e54
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.1
|
data/CLAUDE.md
CHANGED
|
@@ -20,6 +20,7 @@ A CLI tool for creating and managing Ruby gems, built on CommandKit.
|
|
|
20
20
|
- Uses CommandKit::Inflector for name inflection
|
|
21
21
|
- Generator module (`lib/gempilot/cli/generator.rb`) provides template rendering via ERB
|
|
22
22
|
- GemContext module (`lib/gempilot/cli/gem_context.rb`) shared by new, destroy, release, console
|
|
23
|
+
- `GemConstant` value object (`lib/gempilot/gem_constant.rb`) owns constant→namespace/path resolution for `new`/`destroy`; constants are rooted at the gem module by construction
|
|
23
24
|
- CommandKit::Commands::AutoLoad maps filenames in `commands/` to command names
|
|
24
25
|
|
|
25
26
|
### Testing
|
|
@@ -34,7 +35,7 @@ A CLI tool for creating and managing Ruby gems, built on CommandKit.
|
|
|
34
35
|
- RuboCop with framework-specific plugins
|
|
35
36
|
- GitHub Actions CI workflow (`.github/workflows/ci.yml`)
|
|
36
37
|
- `git ls-files`-based gemspec with glob fallback for non-git repos
|
|
37
|
-
- Version
|
|
38
|
+
- Version lifecycle rake tasks installed via `Gempilot::VersionTask.new` (a `Rake::TaskLib`): `version:current/bump/commit/tag/untag/reset/revert`, composite `version:release`/`version:unrelease`, and `version:github:release/unrelease/list`
|
|
38
39
|
|
|
39
40
|
### Notes
|
|
40
41
|
- AutoLoad uses block form in `cli.rb` with explicit `summary:` per command (lazy loading means descriptions aren't available at help time without this)
|
|
@@ -42,4 +43,4 @@ A CLI tool for creating and managing Ruby gems, built on CommandKit.
|
|
|
42
43
|
|
|
43
44
|
## ISSUES
|
|
44
45
|
|
|
45
|
-
1. RESOLVED — `exe/gempilot` had unconditional `ENV["BUNDLE_GEMFILE"]`
|
|
46
|
+
1. RESOLVED — `exe/gempilot` previously had an unconditional `ENV["BUNDLE_GEMFILE"]` + `require "bundler/setup"`, breaking `gem install` usage. It now guards both behind `if File.exist?(gemfile)` (the Ronin pattern), so installed gems skip Bundler while in-repo runs still use it.
|
data/Rakefile
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
|
-
Bundler::GemHelper.install_tasks name: "gempilot"
|
|
3
2
|
|
|
4
3
|
require "minitest/test_task"
|
|
5
4
|
Minitest::TestTask.create
|
|
@@ -7,7 +6,7 @@ Minitest::TestTask.create
|
|
|
7
6
|
require "rspec/core/rake_task"
|
|
8
7
|
RSpec::Core::RakeTask.new(:spec)
|
|
9
8
|
|
|
10
|
-
require_relative
|
|
9
|
+
require_relative "lib/gempilot"
|
|
11
10
|
|
|
12
11
|
namespace :spec do
|
|
13
12
|
desc "Prints the specification suite in documentation format and exits"
|
|
@@ -30,12 +29,12 @@ end
|
|
|
30
29
|
require "rubocop/rake_task"
|
|
31
30
|
RuboCop::RakeTask.new do |t|
|
|
32
31
|
t.patterns = [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
"lib/*.rb",
|
|
33
|
+
"lib/**/*.rb",
|
|
34
|
+
"spec/*.rb",
|
|
35
|
+
"spec/**/*.rb",
|
|
36
|
+
"test/*.rb",
|
|
37
|
+
"test/**/*.rb",
|
|
39
38
|
]
|
|
40
39
|
end
|
|
41
40
|
|
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
# Address Code-Review Critiques 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:** Retire a class of namespace/path bugs and the `new`/`destroy` duplication by introducing a `GemConstant` value object, plus small polish to the version tasks, framework detection, and stale docs.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Extract the duplicated "constant within a gem" logic (qualify, namespaces, lib/test paths) out of `new.rb`, `destroy.rb`, and `gem_context.rb` into one immutable `Gempilot::GemConstant` (a `Data` subclass), mirroring the existing `Project::Version` value object. Commands build a `GemConstant` via a shared `GemContext#gem_constant` factory and read paths off it. Smaller, independent fixes round out the other critiques.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Ruby 4.0, CommandKit, Zeitwerk, RSpec 3.13 + Minitest, RuboCop (+ rubocop-rspec).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Environment note
|
|
14
|
+
|
|
15
|
+
`bundle exec` is broken on this box (vendored gems are macOS-built; see memory `gempilot-test-running-env`). Run tools directly against system gems:
|
|
16
|
+
- RSpec: `rspec <path>` (full suite: `rspec`)
|
|
17
|
+
- Minitest: `ruby -Itest -Ilib -e 'Dir["test/**/*_test.rb"].each { |f| require File.expand_path(f) }'`
|
|
18
|
+
- RuboCop: `rubocop <paths>`
|
|
19
|
+
|
|
20
|
+
In a normal environment these are `bundle exec rspec` / `bundle exec rake test` / `bundle exec rake rubocop`.
|
|
21
|
+
|
|
22
|
+
## File structure
|
|
23
|
+
|
|
24
|
+
| File | Responsibility | Change |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `lib/gempilot/gem_constant.rb` | Value object: a class/module constant within a gem (qualify + paths) | **Create** |
|
|
27
|
+
| `spec/gempilot/gem_constant_spec.rb` | Unit spec for `GemConstant` | **Create** |
|
|
28
|
+
| `lib/gempilot/cli/gem_context.rb` | Shared command context; `gem_constant` factory + framework detection | Modify |
|
|
29
|
+
| `lib/gempilot/cli/commands/new.rb` | `new` command, now delegating constant logic to `GemConstant` | Modify |
|
|
30
|
+
| `lib/gempilot/cli/commands/destroy.rb` | `destroy` command, same delegation | Modify |
|
|
31
|
+
| `lib/gempilot/version_tag.rb` | Version git ops: consistent error + named constant | Modify |
|
|
32
|
+
| `spec/gempilot/version_tag_spec.rb` | Update the non-bump assertion expectation | Modify |
|
|
33
|
+
| `spec/gempilot/cli/commands/new_namespace_spec.rb` | Document unified non-interactive rooting | Modify |
|
|
34
|
+
| `CLAUDE.md` | Fix stale architecture/feature notes | Modify |
|
|
35
|
+
|
|
36
|
+
**Design decision (documented in `GemConstant`):** every class/module constant is rooted at the gem's module *by construction* — bare input is prefixed, input already starting with the gem's first segment is left as-is. This makes interactive and non-interactive behavior consistent and removes the half-enforced `validate_gem_root!` (no test depends on its rejection). Rooting matches on the first segment only, preserving extension-gem support (a `My::Gem` gem accepts any `My::…`).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Part A — Version task polish
|
|
41
|
+
|
|
42
|
+
### Task A1: Make the non-bump guard `raise` instead of `abort`
|
|
43
|
+
|
|
44
|
+
**Files:**
|
|
45
|
+
- Modify: `lib/gempilot/version_tag.rb:50`
|
|
46
|
+
- Test: `spec/gempilot/version_tag_spec.rb:109-118`
|
|
47
|
+
|
|
48
|
+
- [ ] **Step 1: Update the spec to expect a `RuntimeError`** (consistent with `#create`'s dirty-staging error)
|
|
49
|
+
|
|
50
|
+
In `spec/gempilot/version_tag_spec.rb`, replace the block at lines 109-118 with:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
describe "#tag when last commit is not a version bump" do
|
|
54
|
+
before do
|
|
55
|
+
File.write("README.md", "init")
|
|
56
|
+
system("git add README.md && git commit -m 'Not a version bump' --quiet")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "raises an error" do
|
|
60
|
+
expect { version_tag.tag }.to raise_error(RuntimeError, /does not appear to be a version bump/)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- [ ] **Step 2: Run the spec, verify it fails**
|
|
66
|
+
|
|
67
|
+
Run: `rspec spec/gempilot/version_tag_spec.rb -e "not a version bump"`
|
|
68
|
+
Expected: FAIL — `expected RuntimeError, got #<SystemExit: exit>` (code still calls `abort`).
|
|
69
|
+
|
|
70
|
+
- [ ] **Step 3: Change `abort` to `raise`**
|
|
71
|
+
|
|
72
|
+
In `lib/gempilot/version_tag.rb`, in `assert_last_commit_is_bump!` (line 50), change:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
abort "Last commit does not appear to be a version bump." unless message.start_with?("Bump version to ")
|
|
76
|
+
```
|
|
77
|
+
to:
|
|
78
|
+
```ruby
|
|
79
|
+
raise "Last commit does not appear to be a version bump." unless message.start_with?("Bump version to ")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- [ ] **Step 4: Run the spec, verify it passes**
|
|
83
|
+
|
|
84
|
+
Run: `rspec spec/gempilot/version_tag_spec.rb`
|
|
85
|
+
Expected: PASS (all examples).
|
|
86
|
+
|
|
87
|
+
- [ ] **Step 5: Commit**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git add lib/gempilot/version_tag.rb spec/gempilot/version_tag_spec.rb
|
|
91
|
+
git commit -m "Raise instead of abort in version bump guard"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Task A2: Extract the bump-message prefix into a named constant
|
|
95
|
+
|
|
96
|
+
**Files:**
|
|
97
|
+
- Modify: `lib/gempilot/version_tag.rb`
|
|
98
|
+
|
|
99
|
+
- [ ] **Step 1: Confirm current behavior is covered, run the spec**
|
|
100
|
+
|
|
101
|
+
Run: `rspec spec/gempilot/version_tag_spec.rb`
|
|
102
|
+
Expected: PASS. (`#create` asserts the commit message; the guard test asserts the prefix check — both cover this refactor.)
|
|
103
|
+
|
|
104
|
+
- [ ] **Step 2: Add the constant and use it in both places**
|
|
105
|
+
|
|
106
|
+
In `lib/gempilot/version_tag.rb`, add the constant just below `include StrictShell`:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
include StrictShell
|
|
110
|
+
|
|
111
|
+
## Commit-message prefix written for a version bump; the guard below
|
|
112
|
+
## matches on it, so the two must stay in sync.
|
|
113
|
+
BUMP_MESSAGE_PREFIX = "Bump version to ".freeze
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
In `#create`, change the commit line to:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
sh "git", "commit", "-m", "#{BUMP_MESSAGE_PREFIX}#{version.value}"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
In `assert_last_commit_is_bump!`, change the guard to:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
raise "Last commit does not appear to be a version bump." unless message.start_with?(BUMP_MESSAGE_PREFIX)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- [ ] **Step 3: Run the spec, verify still green**
|
|
129
|
+
|
|
130
|
+
Run: `rspec spec/gempilot/version_tag_spec.rb`
|
|
131
|
+
Expected: PASS (commit message is byte-identical: `"Bump version to <value>"`).
|
|
132
|
+
|
|
133
|
+
- [ ] **Step 4: Lint and commit**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
rubocop lib/gempilot/version_tag.rb
|
|
137
|
+
git add lib/gempilot/version_tag.rb
|
|
138
|
+
git commit -m "Name the version bump commit-message prefix"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Part B — `GemConstant` value object + de-duplicate new/destroy
|
|
144
|
+
|
|
145
|
+
### Task B1: Create the `GemConstant` value object
|
|
146
|
+
|
|
147
|
+
**Files:**
|
|
148
|
+
- Create: `lib/gempilot/gem_constant.rb`
|
|
149
|
+
- Test: `spec/gempilot/gem_constant_spec.rb`
|
|
150
|
+
|
|
151
|
+
- [ ] **Step 1: Write the unit spec**
|
|
152
|
+
|
|
153
|
+
Create `spec/gempilot/gem_constant_spec.rb`:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
require "spec_helper"
|
|
157
|
+
|
|
158
|
+
RSpec.describe Gempilot::GemConstant do
|
|
159
|
+
def constant(input, gem_module: "MyGem", require_path: "my_gem")
|
|
160
|
+
described_class.new(input: input, gem_module: gem_module, require_path: require_path)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe "#qualified" do
|
|
164
|
+
it "prepends the gem module to a bare suffix" do
|
|
165
|
+
expect(constant("Services::Auth").qualified).to eq("MyGem::Services::Auth")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "leaves an already-rooted constant unchanged" do
|
|
169
|
+
expect(constant("MyGem::Services::Auth").qualified).to eq("MyGem::Services::Auth")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "matches on the root segment for multi-segment modules" do
|
|
173
|
+
c = constant("My::Widget", gem_module: "My::Gem", require_path: "my/gem")
|
|
174
|
+
expect(c.qualified).to eq("My::Widget")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "prepends the full module to a bare suffix under a multi-segment module" do
|
|
178
|
+
c = constant("Widget", gem_module: "My::Gem", require_path: "my/gem")
|
|
179
|
+
expect(c.qualified).to eq("My::Gem::Widget")
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
describe "#namespaces / #name" do
|
|
184
|
+
it "splits the qualified constant", :aggregate_failures do
|
|
185
|
+
c = constant("Services::Auth")
|
|
186
|
+
expect(c.namespaces).to eq(%w[MyGem Services])
|
|
187
|
+
expect(c.name).to eq("Auth")
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe "#lib_path" do
|
|
192
|
+
it "builds the underscored source path" do
|
|
193
|
+
expect(constant("Services::Auth").lib_path).to eq("lib/my_gem/services/auth.rb")
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe "#test_path" do
|
|
198
|
+
it "builds the rspec path" do
|
|
199
|
+
expect(constant("Services::Auth").test_path(:rspec)).to eq("spec/my_gem/services/auth_spec.rb")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "builds the minitest path" do
|
|
203
|
+
expect(constant("Services::Auth").test_path(:minitest)).to eq("test/my_gem/services/auth_test.rb")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it "does not duplicate segments for multi-segment (hyphenated) modules" do
|
|
207
|
+
c = constant("Widget", gem_module: "My::Gem", require_path: "my/gem")
|
|
208
|
+
expect(c.test_path(:minitest)).to eq("test/my/gem/widget_test.rb")
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- [ ] **Step 2: Run the spec, verify it fails**
|
|
215
|
+
|
|
216
|
+
Run: `rspec spec/gempilot/gem_constant_spec.rb`
|
|
217
|
+
Expected: FAIL — `uninitialized constant Gempilot::GemConstant`.
|
|
218
|
+
|
|
219
|
+
- [ ] **Step 3: Create the value object**
|
|
220
|
+
|
|
221
|
+
Create `lib/gempilot/gem_constant.rb`:
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
module Gempilot
|
|
225
|
+
## A class or module constant resolved within a gem's namespace.
|
|
226
|
+
##
|
|
227
|
+
## Wraps raw user input (a bare suffix like +Services::Auth+ or a
|
|
228
|
+
## fully-qualified +MyGem::Services::Auth+) together with the gem's module
|
|
229
|
+
## and require path, and derives the qualified constant, file paths, and
|
|
230
|
+
## namespace pieces from a single parse.
|
|
231
|
+
##
|
|
232
|
+
## Every constant is rooted at the gem's module: bare input is prefixed with
|
|
233
|
+
## it, while input already starting with the gem's root segment is left as
|
|
234
|
+
## is. Rooting is matched on the first segment only, so an extension gem
|
|
235
|
+
## whose module is +My::Gem+ accepts any +My::...+ constant.
|
|
236
|
+
class GemConstant < Data.define(:input, :gem_module, :require_path)
|
|
237
|
+
using String::Inflectable
|
|
238
|
+
|
|
239
|
+
## The fully-qualified constant, rooted at the gem module.
|
|
240
|
+
def qualified
|
|
241
|
+
input.start_with?("#{root_segment}::") ? input : "#{gem_module}::#{input}"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
## Namespace segments preceding the final constant name.
|
|
245
|
+
def namespaces
|
|
246
|
+
parts[0...-1]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
## The final class or module name.
|
|
250
|
+
def name
|
|
251
|
+
parts.last
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
## Path to the constant's source file, e.g. +lib/my_gem/services/auth.rb+.
|
|
255
|
+
def lib_path
|
|
256
|
+
"#{File.join("lib", *path_segments)}.rb"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
## Path to the constant's test file for +framework+ (+:rspec+ or
|
|
260
|
+
## +:minitest+); correct for multi-segment (hyphenated) gem modules.
|
|
261
|
+
def test_path(framework)
|
|
262
|
+
rest = path_segments.drop(require_path.split("/").length)
|
|
263
|
+
if framework == :rspec
|
|
264
|
+
"#{File.join("spec", require_path, *rest)}_spec.rb"
|
|
265
|
+
else
|
|
266
|
+
"#{File.join("test", require_path, *rest)}_test.rb"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
private
|
|
271
|
+
|
|
272
|
+
def root_segment
|
|
273
|
+
gem_module.split("::").first
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def parts
|
|
277
|
+
qualified.split("::")
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def path_segments
|
|
281
|
+
parts.map(&:underscore)
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- [ ] **Step 4: Run the spec, verify it passes**
|
|
288
|
+
|
|
289
|
+
Run: `rspec spec/gempilot/gem_constant_spec.rb`
|
|
290
|
+
Expected: PASS (all examples). If you see `undefined method 'underscore'`, confirm `String::Inflectable` is loaded at boot (it is for `new.rb`); the file-top `using` activates it for the whole file.
|
|
291
|
+
|
|
292
|
+
- [ ] **Step 5: Lint and commit**
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
rubocop lib/gempilot/gem_constant.rb spec/gempilot/gem_constant_spec.rb
|
|
296
|
+
git add lib/gempilot/gem_constant.rb spec/gempilot/gem_constant_spec.rb
|
|
297
|
+
git commit -m "Add GemConstant value object for gem-rooted constants"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Task B2: Add a `gem_constant` factory to `GemContext`
|
|
301
|
+
|
|
302
|
+
**Files:**
|
|
303
|
+
- Modify: `lib/gempilot/cli/gem_context.rb`
|
|
304
|
+
|
|
305
|
+
- [ ] **Step 1: Add the factory method** (additive; `parse_constant`/`validate_gem_root!` stay for now so nothing breaks)
|
|
306
|
+
|
|
307
|
+
In `lib/gempilot/cli/gem_context.rb`, add this method inside `module GemContext` (after `detect_gem_context`):
|
|
308
|
+
|
|
309
|
+
```ruby
|
|
310
|
+
def gem_constant(input)
|
|
311
|
+
GemConstant.new(input: input, gem_module: @gem_module, require_path: @require_path)
|
|
312
|
+
end
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
- [ ] **Step 2: Verify nothing is broken**
|
|
316
|
+
|
|
317
|
+
Run: `rspec`
|
|
318
|
+
Expected: PASS (additive change; existing suite unaffected).
|
|
319
|
+
|
|
320
|
+
- [ ] **Step 3: Commit**
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
git add lib/gempilot/cli/gem_context.rb
|
|
324
|
+
git commit -m "Add GemContext#gem_constant factory"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Task B3: Refactor `new` to use `GemConstant`
|
|
328
|
+
|
|
329
|
+
**Files:**
|
|
330
|
+
- Modify: `lib/gempilot/cli/commands/new.rb`
|
|
331
|
+
- Test: `spec/gempilot/cli/commands/new_namespace_spec.rb`
|
|
332
|
+
|
|
333
|
+
- [ ] **Step 1: Add a spec for unified non-interactive rooting** (documents the now-consistent behavior: a bare name passed as an argument is rooted, instead of being rejected)
|
|
334
|
+
|
|
335
|
+
In `spec/gempilot/cli/commands/new_namespace_spec.rb`, add this context inside the top-level `describe` (the file already has the tmpdir `around` + `include FileUtils` + `my_gem` gemspec fixture):
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
describe "non-interactive rooting" do
|
|
339
|
+
before { mkdir_p("lib/my_gem") }
|
|
340
|
+
|
|
341
|
+
it "roots a bare class name under the gem module" do
|
|
342
|
+
command.main(["class", "Services::Auth"])
|
|
343
|
+
|
|
344
|
+
expect(File).to exist("lib/my_gem/services/auth.rb")
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- [ ] **Step 2: Run it, verify it fails**
|
|
350
|
+
|
|
351
|
+
Run: `rspec spec/gempilot/cli/commands/new_namespace_spec.rb -e "roots a bare class name"`
|
|
352
|
+
Expected: FAIL — current code calls `validate_gem_root!("Services")` and exits, so the file is not created (`SystemExit`).
|
|
353
|
+
|
|
354
|
+
- [ ] **Step 3: Refactor `new.rb`**
|
|
355
|
+
|
|
356
|
+
In `lib/gempilot/cli/commands/new.rb`:
|
|
357
|
+
|
|
358
|
+
Replace `prompt_for_path` (it no longer prepends — `GemConstant#qualified` owns that):
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
def prompt_for_path(type)
|
|
362
|
+
ask(colors.green(type), required: true)
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Replace `dispatch_add` so class/module build a `GemConstant`:
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
def dispatch_add(type, path)
|
|
370
|
+
case type
|
|
371
|
+
when "class" then add_class(gem_constant(path))
|
|
372
|
+
when "module" then add_module(gem_constant(path))
|
|
373
|
+
when "command" then add_command(path)
|
|
374
|
+
else
|
|
375
|
+
puts colors.red("Unknown type '#{type}'. Use class, module, or command.")
|
|
376
|
+
exit 1
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Delete `prepare_constant` and `class_test_path` entirely. Replace `add_class`, `add_module`, and `add_test_file` with:
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
def add_class(constant)
|
|
385
|
+
print_adding_banner("class", constant.qualified)
|
|
386
|
+
ensure_directory(File.dirname(constant.lib_path))
|
|
387
|
+
source = build_nested_source(constant.namespaces, "class", constant.name)
|
|
388
|
+
create_file(constant.lib_path, source)
|
|
389
|
+
add_test_file(constant)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def add_module(constant)
|
|
393
|
+
print_adding_banner("module", constant.qualified)
|
|
394
|
+
ensure_directory(File.dirname(constant.lib_path))
|
|
395
|
+
source = build_nested_source(constant.namespaces, "module", constant.name)
|
|
396
|
+
create_file(constant.lib_path, source)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def add_test_file(constant)
|
|
400
|
+
test_path = constant.test_path(@test_framework)
|
|
401
|
+
ensure_directory(File.dirname(test_path))
|
|
402
|
+
content = if @test_framework == :rspec
|
|
403
|
+
rspec_class_content(constant.namespaces, constant.name)
|
|
404
|
+
else
|
|
405
|
+
minitest_class_content(constant.namespaces, constant.name)
|
|
406
|
+
end
|
|
407
|
+
create_file(test_path, content)
|
|
408
|
+
end
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Leave `add_command`, `command_test_path`, `add_command_test_file`, `rspec_class_content`, `minitest_class_content`, `rspec_command_content`, `minitest_command_content`, `build_nested_source`, `build_namespace_lines`, `build_closing_lines`, `print_adding_banner`, and `ensure_directory` unchanged.
|
|
412
|
+
|
|
413
|
+
- [ ] **Step 4: Run the new test, the command specs, and the minitest suite**
|
|
414
|
+
|
|
415
|
+
Run: `rspec spec/gempilot/cli/commands/new_namespace_spec.rb spec/gempilot/cli/commands/new_interactive_spec.rb`
|
|
416
|
+
Expected: PASS (interactive + hyphenated cases still green; new rooting test green).
|
|
417
|
+
|
|
418
|
+
Run: `ruby -Itest -Ilib -e 'require File.expand_path("test/gempilot/cli/new_command_test.rb")'`
|
|
419
|
+
Expected: PASS, 0 failures (full constants are unchanged by `qualified`).
|
|
420
|
+
|
|
421
|
+
- [ ] **Step 5: Lint and commit**
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
rubocop lib/gempilot/cli/commands/new.rb spec/gempilot/cli/commands/new_namespace_spec.rb
|
|
425
|
+
git add lib/gempilot/cli/commands/new.rb spec/gempilot/cli/commands/new_namespace_spec.rb
|
|
426
|
+
git commit -m "Refactor new command onto GemConstant"
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Task B4: Refactor `destroy` to use `GemConstant`
|
|
430
|
+
|
|
431
|
+
**Files:**
|
|
432
|
+
- Modify: `lib/gempilot/cli/commands/destroy.rb`
|
|
433
|
+
|
|
434
|
+
- [ ] **Step 1: Run the destroy specs (baseline green)**
|
|
435
|
+
|
|
436
|
+
Run: `rspec spec/gempilot/cli/commands/destroy_namespace_spec.rb spec/gempilot/cli/commands/destroy_interactive_spec.rb`
|
|
437
|
+
Expected: PASS.
|
|
438
|
+
|
|
439
|
+
- [ ] **Step 2: Refactor `destroy.rb`**
|
|
440
|
+
|
|
441
|
+
In `lib/gempilot/cli/commands/destroy.rb`:
|
|
442
|
+
|
|
443
|
+
Replace `prompt_for_path`:
|
|
444
|
+
|
|
445
|
+
```ruby
|
|
446
|
+
def prompt_for_path(type)
|
|
447
|
+
ask(colors.green(type), required: true)
|
|
448
|
+
end
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Replace `dispatch_destroy`:
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
def dispatch_destroy(type, path)
|
|
455
|
+
case type
|
|
456
|
+
when "class" then destroy_class(gem_constant(path))
|
|
457
|
+
when "module" then destroy_module(gem_constant(path))
|
|
458
|
+
when "command" then destroy_command(path)
|
|
459
|
+
else
|
|
460
|
+
puts colors.red("Unknown type '#{type}'. Use class, module, or command.")
|
|
461
|
+
exit 1
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Delete `test_path_for`. Replace `destroy_class` and `destroy_module`:
|
|
467
|
+
|
|
468
|
+
```ruby
|
|
469
|
+
def destroy_class(constant)
|
|
470
|
+
lib_path = constant.lib_path
|
|
471
|
+
test_path = constant.test_path(@test_framework)
|
|
472
|
+
|
|
473
|
+
remove_file(lib_path)
|
|
474
|
+
remove_file(test_path)
|
|
475
|
+
cleanup_empty_dirs(lib_path, test_path)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def destroy_module(constant)
|
|
479
|
+
lib_path = constant.lib_path
|
|
480
|
+
|
|
481
|
+
remove_file(lib_path)
|
|
482
|
+
remove_empty_parents(File.dirname(lib_path), File.join("lib", @require_path))
|
|
483
|
+
end
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Leave `destroy_command`, `cleanup_empty_dirs`, `remove_empty_parents`, `remove_file`, `print_remove`, and `print_skip` unchanged.
|
|
487
|
+
|
|
488
|
+
- [ ] **Step 3: Run destroy specs + minitest**
|
|
489
|
+
|
|
490
|
+
Run: `rspec spec/gempilot/cli/commands/destroy_namespace_spec.rb spec/gempilot/cli/commands/destroy_interactive_spec.rb`
|
|
491
|
+
Expected: PASS.
|
|
492
|
+
|
|
493
|
+
Run: `ruby -Itest -Ilib -e 'require File.expand_path("test/gempilot/cli/destroy_command_test.rb")'`
|
|
494
|
+
Expected: PASS, 0 failures.
|
|
495
|
+
|
|
496
|
+
- [ ] **Step 4: Lint and commit**
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
rubocop lib/gempilot/cli/commands/destroy.rb
|
|
500
|
+
git add lib/gempilot/cli/commands/destroy.rb
|
|
501
|
+
git commit -m "Refactor destroy command onto GemConstant"
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Task B5: Remove the now-dead `parse_constant` and `validate_gem_root!`
|
|
505
|
+
|
|
506
|
+
**Files:**
|
|
507
|
+
- Modify: `lib/gempilot/cli/gem_context.rb`
|
|
508
|
+
|
|
509
|
+
- [ ] **Step 1: Confirm they're unused**
|
|
510
|
+
|
|
511
|
+
Run: `grep -rn "parse_constant\|validate_gem_root" lib`
|
|
512
|
+
Expected: only the two definitions in `gem_context.rb` (no call sites remain after B3/B4).
|
|
513
|
+
|
|
514
|
+
- [ ] **Step 2: Delete both methods**
|
|
515
|
+
|
|
516
|
+
In `lib/gempilot/cli/gem_context.rb`, delete the entire `parse_constant` method and the entire `validate_gem_root!` method. Keep `using String::Inflectable` (still used by `detect_gem_context`'s `camelize`), `detect_gem_context`, and `gem_constant`.
|
|
517
|
+
|
|
518
|
+
- [ ] **Step 3: Run the full suite + minitest**
|
|
519
|
+
|
|
520
|
+
Run: `rspec`
|
|
521
|
+
Expected: PASS (whole RSpec suite).
|
|
522
|
+
|
|
523
|
+
Run: `ruby -Itest -Ilib -e 'Dir["test/**/*_test.rb"].each { |f| require File.expand_path(f) }'`
|
|
524
|
+
Expected: only the known environmental `create_command_test` bundler-subprocess failure (see memory `gempilot-test-running-env`); 0 other failures.
|
|
525
|
+
|
|
526
|
+
- [ ] **Step 4: Lint and commit**
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
rubocop lib/gempilot/cli/gem_context.rb
|
|
530
|
+
git add lib/gempilot/cli/gem_context.rb
|
|
531
|
+
git commit -m "Drop parse_constant/validate_gem_root! superseded by GemConstant"
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## Part C — Robust test-framework detection
|
|
537
|
+
|
|
538
|
+
### Task C1: Detect rspec by its config files, not a bare `spec/` directory
|
|
539
|
+
|
|
540
|
+
**Files:**
|
|
541
|
+
- Modify: `lib/gempilot/cli/gem_context.rb`
|
|
542
|
+
- Test: `spec/gempilot/cli/commands/new_interactive_spec.rb`
|
|
543
|
+
|
|
544
|
+
- [ ] **Step 1: Add a spec proving a stray `spec/` dir doesn't force rspec**
|
|
545
|
+
|
|
546
|
+
In `spec/gempilot/cli/commands/new_interactive_spec.rb`, add inside `describe "interactive mode"`:
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
context "when a spec/ directory exists but there is no rspec config" do
|
|
550
|
+
before { mkdir_p("spec") }
|
|
551
|
+
|
|
552
|
+
it "still scaffolds a minitest test file" do
|
|
553
|
+
generate("1\nServices::Auth\n")
|
|
554
|
+
|
|
555
|
+
expect(File).to exist("test/my_gem/services/auth_test.rb")
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
- [ ] **Step 2: Run it, verify it fails**
|
|
561
|
+
|
|
562
|
+
Run: `rspec spec/gempilot/cli/commands/new_interactive_spec.rb -e "stray"` (or `-e "no rspec config"`)
|
|
563
|
+
Expected: FAIL — current `File.directory?("spec")` returns true, so an rspec spec file is generated under `spec/` and the expected `test/...` file does not exist.
|
|
564
|
+
|
|
565
|
+
- [ ] **Step 3: Replace the detection**
|
|
566
|
+
|
|
567
|
+
In `lib/gempilot/cli/gem_context.rb`, change the last line of `detect_gem_context` from:
|
|
568
|
+
|
|
569
|
+
```ruby
|
|
570
|
+
@test_framework = File.directory?("spec") ? :rspec : :minitest
|
|
571
|
+
```
|
|
572
|
+
to:
|
|
573
|
+
```ruby
|
|
574
|
+
@test_framework = detect_test_framework
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
Add this private method (next to `gem_constant`):
|
|
578
|
+
|
|
579
|
+
```ruby
|
|
580
|
+
# Detect rspec by its canonical config files rather than the mere
|
|
581
|
+
# presence of a spec/ directory, which a minitest project may also have.
|
|
582
|
+
def detect_test_framework
|
|
583
|
+
return :rspec if File.exist?(".rspec") || File.exist?(File.join("spec", "spec_helper.rb"))
|
|
584
|
+
|
|
585
|
+
:minitest
|
|
586
|
+
end
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
- [ ] **Step 4: Run the spec + full suite**
|
|
590
|
+
|
|
591
|
+
Run: `rspec spec/gempilot/cli/commands/new_interactive_spec.rb`
|
|
592
|
+
Expected: PASS.
|
|
593
|
+
|
|
594
|
+
Run: `rspec`
|
|
595
|
+
Expected: PASS (whole suite).
|
|
596
|
+
|
|
597
|
+
- [ ] **Step 5: Lint and commit**
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
rubocop lib/gempilot/cli/gem_context.rb spec/gempilot/cli/commands/new_interactive_spec.rb
|
|
601
|
+
git add lib/gempilot/cli/gem_context.rb spec/gempilot/cli/commands/new_interactive_spec.rb
|
|
602
|
+
git commit -m "Detect rspec by config files, not a bare spec/ dir"
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
## Part D — CLAUDE.md accuracy pass
|
|
608
|
+
|
|
609
|
+
### Task D1: Correct the stale architecture and feature notes
|
|
610
|
+
|
|
611
|
+
**Files:**
|
|
612
|
+
- Modify: `CLAUDE.md`
|
|
613
|
+
|
|
614
|
+
- [ ] **Step 1: Fix the version-tasks note**
|
|
615
|
+
|
|
616
|
+
In `CLAUDE.md`, under "Generated Gem Features", replace:
|
|
617
|
+
|
|
618
|
+
```
|
|
619
|
+
- Version management rake tasks in `rakelib/version.rake` (current, bump, commit, revert, release:full)
|
|
620
|
+
```
|
|
621
|
+
with:
|
|
622
|
+
```
|
|
623
|
+
- Version lifecycle rake tasks installed via `Gempilot::VersionTask.new` (a `Rake::TaskLib`): `version:current/bump/commit/tag/untag/reset/revert`, composite `version:release`/`version:unrelease`, and `version:github:release/unrelease/list`
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
- [ ] **Step 2: Fix the `exe/` note in the ISSUES section**
|
|
627
|
+
|
|
628
|
+
In `CLAUDE.md`, update issue 1 to reflect reality — `exe/gempilot` keeps a *conditional* `bundler/setup` (only when a sibling `Gemfile` exists), it was not removed. Replace the issue body with:
|
|
629
|
+
|
|
630
|
+
```
|
|
631
|
+
1. RESOLVED — `exe/gempilot` previously had an unconditional `ENV["BUNDLE_GEMFILE"]` + `require "bundler/setup"`, breaking `gem install` usage. It now guards both behind `if File.exist?(gemfile)` (the Ronin pattern), so installed gems skip Bundler while in-repo runs still use it.
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
- [ ] **Step 3: Note the constant model under Architecture**
|
|
635
|
+
|
|
636
|
+
In `CLAUDE.md`, under "Architecture", add a bullet:
|
|
637
|
+
|
|
638
|
+
```
|
|
639
|
+
- `GemConstant` value object (`lib/gempilot/gem_constant.rb`) owns constant→namespace/path resolution for `new`/`destroy`; constants are rooted at the gem module by construction
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
- [ ] **Step 4: Commit**
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
git add CLAUDE.md
|
|
646
|
+
git commit -m "Refresh CLAUDE.md: version tasks, exe note, GemConstant"
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## Final verification
|
|
652
|
+
|
|
653
|
+
- [ ] **Full RSpec suite**
|
|
654
|
+
|
|
655
|
+
Run: `rspec`
|
|
656
|
+
Expected: PASS (all examples, including the new `gem_constant_spec` and added cases).
|
|
657
|
+
|
|
658
|
+
- [ ] **Minitest suite**
|
|
659
|
+
|
|
660
|
+
Run: `ruby -Itest -Ilib -e 'Dir["test/**/*_test.rb"].each { |f| require File.expand_path(f) }'`
|
|
661
|
+
Expected: only the known environmental `create_command_test` bundler failure; 0 others.
|
|
662
|
+
|
|
663
|
+
- [ ] **RuboCop (real source dirs)**
|
|
664
|
+
|
|
665
|
+
Run: `rubocop lib spec test exe Rakefile`
|
|
666
|
+
Expected: no offenses.
|
|
667
|
+
|
|
668
|
+
- [ ] **Confirm net code reduction**
|
|
669
|
+
|
|
670
|
+
Run: `git diff --stat master -- lib/gempilot/cli/commands/new.rb lib/gempilot/cli/commands/destroy.rb lib/gempilot/cli/gem_context.rb`
|
|
671
|
+
Expected: `new.rb`/`destroy.rb`/`gem_context.rb` shrink (constant logic centralized in `gem_constant.rb`).
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## Self-review notes (done while writing)
|
|
676
|
+
|
|
677
|
+
- **Spec coverage:** each critique maps to a task — duplication + bug class → B1–B5; `abort`/`raise` → A1; magic string → A2; fragile framework detection → C1; CLAUDE.md drift → D1. The `validate_gem_root!` leniency is resolved as a documented design decision in B1.
|
|
678
|
+
- **Type consistency:** `GemConstant#qualified/namespaces/name/lib_path/test_path(framework)` are used with the same names/signatures across B3/B4; the factory is `gem_constant(input)` everywhere.
|
|
679
|
+
- **Behavior change called out:** B3 makes non-interactive bare names root under the gem (previously rejected). No existing test asserted the rejection; B3 Step 1 adds a test documenting the new behavior.
|
data/issues.rec
CHANGED
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
%mandatory: Title Description
|
|
12
12
|
|
|
13
13
|
Id: 6F683910-2EFA-4356-B1FE-3E4FE3E20A38
|
|
14
|
-
Updated:
|
|
14
|
+
Updated: Fri, 05 Jun 2026 00:00:00 -0400
|
|
15
15
|
Title: Rake build triggers gem to be built twice?
|
|
16
16
|
Description: Why am I seeing 2 messages about gempilot being built to pkg/gempilot-0.1.0.gem
|
|
17
|
-
Status:
|
|
17
|
+
Status: closed
|
|
18
18
|
|
|
19
19
|
Id: 011FB117-BC52-453F-A8E8-6955FE3EBCE8
|
|
20
|
-
Updated:
|
|
20
|
+
Updated: Sat, 06 Jun 2026 00:00:00 -0400
|
|
21
21
|
Title: rubocop-rake is not active
|
|
22
22
|
Description: It should be active for this project
|
|
23
|
-
Status:
|
|
23
|
+
Status: closed
|
|
24
24
|
|
|
25
25
|
Id: F0833E66-A35C-4054-884B-F90C81F08838
|
|
26
26
|
Updated: Mon, 02 Mar 2026 10:37:33 -0500
|
|
@@ -35,10 +35,10 @@ Description:
|
|
|
35
35
|
Status: open
|
|
36
36
|
|
|
37
37
|
Id: 131EA3CE-2F3D-4E37-9D9B-A25E0A651F1B
|
|
38
|
-
Updated:
|
|
38
|
+
Updated: Fri, 05 Jun 2026 00:00:00 -0400
|
|
39
39
|
Title: irb is required now as a gem
|
|
40
40
|
Description: Needs to go in template gemfile
|
|
41
|
-
Status:
|
|
41
|
+
Status: closed
|
|
42
42
|
|
|
43
43
|
Id: 3178AC5C-09A9-44A9-ACE1-11BED0BA7276
|
|
44
44
|
Updated: Mon, 06 Apr 2026 00:00:00 -0400
|
|
@@ -53,16 +53,16 @@ Description: The commit should be tagged with git tag upon committing, as this i
|
|
|
53
53
|
Status: closed
|
|
54
54
|
|
|
55
55
|
Id: 7825507C-ABD2-4618-A591-BCDB54152C6B
|
|
56
|
-
Updated:
|
|
56
|
+
Updated: Fri, 05 Jun 2026 00:00:00 -0400
|
|
57
57
|
Title: Rake task version:bump should support "dev increments", like "0.2.1.dev3". Perhaps a task like "version:bump:dev" is created to allow for this
|
|
58
58
|
Description: open
|
|
59
|
-
Status:
|
|
59
|
+
Status: closed
|
|
60
60
|
|
|
61
61
|
Id: 5E4D51AB-F9BF-4C72-8165-F1B900799FA7
|
|
62
|
-
Updated: Sat,
|
|
62
|
+
Updated: Sat, 06 Jun 2026 00:00:00 -0400
|
|
63
63
|
Title: Hundreds of rubocop failures exist
|
|
64
64
|
Description: These need to be addressed
|
|
65
|
-
Status:
|
|
65
|
+
Status: closed
|
|
66
66
|
|
|
67
67
|
Id: 74D0FD90-0421-4452-8591-2798EE6FD23E
|
|
68
68
|
Updated: Sat, 28 Mar 2026 12:59:40 -0400
|
|
@@ -75,3 +75,69 @@ Updated: Mon, 06 Apr 2026 00:00:00 -0400
|
|
|
75
75
|
Title: Replace 4.0.1 with 3.4.8
|
|
76
76
|
Description: Need to move ruby version down to 3.4.8
|
|
77
77
|
Status: closed
|
|
78
|
+
|
|
79
|
+
Id: 776F52C8-390E-11F1-8D46-FE6CB9572C2F
|
|
80
|
+
Updated: Fri, 05 Jun 2026 00:00:00 -0400
|
|
81
|
+
Title: `gempilot new` => `3) command` => `Codeball::Command::Filter` fails
|
|
82
|
+
Description: Generating commands with gempilot new is failing with the following error:
|
|
83
|
+
+ gempilot new command 'Codeball::Command::Filter'
|
|
84
|
+
+
|
|
85
|
+
+ Oops! Looks like you have found a bug. Please report it!
|
|
86
|
+
+ https://github.com/dgillis/gempilot/issues/new
|
|
87
|
+
+
|
|
88
|
+
+ ```
|
|
89
|
+
+ gempilot new: /Users/davidgillis/.rbenv/versions/4.0.1/lib/ruby/gems/4.0.0/gems/gempilot-0.2.0/lib/core_ext/string/inflection_methods.rb:33:in 'scan_word_token': cannot convert string to underscored: "Codeball::Command::Filter" (ArgumentError)
|
|
90
|
+
+
|
|
91
|
+
+ raise(ArgumentError, "cannot convert string to underscored: #{scanner.string.inspect}")
|
|
92
|
+
Status: closed
|
|
93
|
+
|
|
94
|
+
Id: 4EB6E51C-4322-11F1-B1B2-FE6CB9572C2F
|
|
95
|
+
Updated: Tue, 28 Apr 2026 12:50:05 -0400
|
|
96
|
+
Title: Existing git repo on gempilot create needs new commit message
|
|
97
|
+
Description: Currently it commits "Initial commit". Inappropriate for a git repo that already has a history
|
|
98
|
+
Status: open
|
|
99
|
+
|
|
100
|
+
Id: 0411D4DA-4323-11F1-847C-FE6CB9572C2F
|
|
101
|
+
Updated: Tue, 28 Apr 2026 12:55:09 -0400
|
|
102
|
+
Title: rubocop config should not exclude rakelib
|
|
103
|
+
Description: it is supplied by gempilot. Should not be eliminating author files
|
|
104
|
+
Status: open
|
|
105
|
+
|
|
106
|
+
Id: C72CD130-4324-11F1-ACAE-FE6CB9572C2F
|
|
107
|
+
Updated: Tue, 28 Apr 2026 13:07:46 -0400
|
|
108
|
+
Title: Make zeitwerk setup a block
|
|
109
|
+
Description: e.g.
|
|
110
|
+
+ Zeitwerk::Loader.for_gem.tap do |l|
|
|
111
|
+
+ l.setup
|
|
112
|
+
+ end
|
|
113
|
+
+
|
|
114
|
+
+
|
|
115
|
+
Status: open
|
|
116
|
+
|
|
117
|
+
Id: 5618BB20-4325-11F1-BF5A-FE6CB9572C2F
|
|
118
|
+
Updated: Tue, 28 Apr 2026 13:11:46 -0400
|
|
119
|
+
Title: Move zeitwerk check task into gempilot
|
|
120
|
+
Description: Can't iterate on it when its not managed by gempilot. A new feature needs to be added
|
|
121
|
+
Status: open
|
|
122
|
+
|
|
123
|
+
Id: 6BE168E4-4325-11F1-BE0F-FE6CB9572C2F
|
|
124
|
+
Updated: Tue, 28 Apr 2026 13:12:22 -0400
|
|
125
|
+
Title: Add zeitwerk:all task
|
|
126
|
+
Description: Add rake task that runs LOADER.all_expected_cpaths and prints in a nice table, similar to rails routes
|
|
127
|
+
Status: open
|
|
128
|
+
|
|
129
|
+
Id: 56DB9634-4327-11F1-8D0F-FE6CB9572C2F
|
|
130
|
+
Updated: Fri, 05 Jun 2026 00:00:00 -0400
|
|
131
|
+
Title: 'gempilot new' doesn't even work
|
|
132
|
+
Description: "
|
|
133
|
+
+ Fully-qualified constant name (e.g., Television::Services::Authentication).
|
|
134
|
+
+ Constant: Television::CLI::Screen
|
|
135
|
+
+
|
|
136
|
+
+ Oops! Looks like you have found a bug. Please report it!
|
|
137
|
+
+ https://github.com/dgillis/gempilot/issues/new
|
|
138
|
+
+
|
|
139
|
+
+ ```
|
|
140
|
+
+ gempilot new: /Users/davidgillis/.rbenv/versions/4.0.1/lib/ruby/gems/4.0.0/gems/gempilot-0.2.0/lib/core_ext/string/inflection_methods.rb:33:in 'scan_word_token': cannot convert string to underscored: "Television::CLI::Screen" (ArgumentError)
|
|
141
|
+
+ ```
|
|
142
|
+
+ "
|
|
143
|
+
Status: closed
|
|
@@ -36,6 +36,8 @@ class String
|
|
|
36
36
|
def scan_underscore_separator(scanner)
|
|
37
37
|
if (sep = scanner.scan(/[_-]+/))
|
|
38
38
|
"_" * sep.length
|
|
39
|
+
elsif scanner.scan("::")
|
|
40
|
+
"/"
|
|
39
41
|
elsif scanner.eos?
|
|
40
42
|
""
|
|
41
43
|
else
|
|
@@ -58,7 +60,7 @@ class String
|
|
|
58
60
|
|
|
59
61
|
if scanner.scan(/[_-]+/)
|
|
60
62
|
nil
|
|
61
|
-
elsif scanner.scan(%r{
|
|
63
|
+
elsif scanner.scan(%r{/|::})
|
|
62
64
|
buffer << "::"
|
|
63
65
|
else
|
|
64
66
|
raise(ArgumentError, "cannot convert string to CamelCase: #{scanner.string.inspect}")
|
|
@@ -87,11 +87,23 @@ module Gempilot
|
|
|
87
87
|
initialize_git_repo
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
GEM_NAME_PATTERN = /\A[a-z][a-z0-9]*([_-][a-z0-9]+)*\z/
|
|
91
|
+
private_constant :GEM_NAME_PATTERN
|
|
92
|
+
|
|
90
93
|
def collect_gem_name(gem_name)
|
|
91
94
|
@gem_name = gem_name || begin
|
|
92
95
|
puts colors.bright_black("The gem name determines the directory, require path, and gemspec name.")
|
|
93
96
|
ask(colors.green("Gem name"), required: true)
|
|
94
97
|
end
|
|
98
|
+
validate_gem_name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_gem_name
|
|
102
|
+
return if @gem_name.match?(GEM_NAME_PATTERN)
|
|
103
|
+
|
|
104
|
+
puts colors.red("Invalid gem name '#{@gem_name}'.")
|
|
105
|
+
puts colors.red("Use lowercase letters, digits, hyphens, or underscores (e.g., my_gem or my-gem).")
|
|
106
|
+
exit 1
|
|
95
107
|
end
|
|
96
108
|
|
|
97
109
|
def derive_naming
|
|
@@ -27,7 +27,7 @@ module Gempilot
|
|
|
27
27
|
def run(type = nil, path = nil)
|
|
28
28
|
type ||= prompt_for_type
|
|
29
29
|
detect_gem_context
|
|
30
|
-
path ||= prompt_for_path
|
|
30
|
+
path ||= prompt_for_path(type)
|
|
31
31
|
dispatch_destroy(type, path)
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -38,16 +38,14 @@ module Gempilot
|
|
|
38
38
|
ask_multiple_choice(colors.green("Type"), %w[class module command])
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def prompt_for_path
|
|
42
|
-
|
|
43
|
-
puts colors.bright_black("Fully-qualified constant name (e.g., #{@gem_module}::Services::Authentication).")
|
|
44
|
-
ask(colors.green("Constant"), required: true)
|
|
41
|
+
def prompt_for_path(type)
|
|
42
|
+
ask(colors.green(type), required: true)
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
def dispatch_destroy(type, path)
|
|
48
46
|
case type
|
|
49
|
-
when "class" then destroy_class(path)
|
|
50
|
-
when "module" then destroy_module(path)
|
|
47
|
+
when "class" then destroy_class(gem_constant(path))
|
|
48
|
+
when "module" then destroy_module(gem_constant(path))
|
|
51
49
|
when "command" then destroy_command(path)
|
|
52
50
|
else
|
|
53
51
|
puts colors.red("Unknown type '#{type}'. Use class, module, or command.")
|
|
@@ -56,25 +54,14 @@ module Gempilot
|
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
def destroy_class(constant)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
lib_path = "#{File.join("lib", *segments)}.rb"
|
|
63
|
-
test_path = test_path_for(segments)
|
|
57
|
+
lib_path = constant.lib_path
|
|
58
|
+
test_path = constant.test_path(@test_framework)
|
|
64
59
|
|
|
65
60
|
remove_file(lib_path)
|
|
66
61
|
remove_file(test_path)
|
|
67
62
|
cleanup_empty_dirs(lib_path, test_path)
|
|
68
63
|
end
|
|
69
64
|
|
|
70
|
-
def test_path_for(segments)
|
|
71
|
-
if @test_framework == :rspec
|
|
72
|
-
"#{File.join("spec", @require_path, *segments[1..])}_spec.rb"
|
|
73
|
-
else
|
|
74
|
-
"#{File.join("test", @require_path, *segments[1..])}_test.rb"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
65
|
def cleanup_empty_dirs(lib_path, test_path)
|
|
79
66
|
remove_empty_parents(File.dirname(lib_path), File.join("lib", @require_path))
|
|
80
67
|
test_root = @test_framework == :rspec ? File.join("spec", @require_path) : File.join("test", @require_path)
|
|
@@ -82,15 +69,14 @@ module Gempilot
|
|
|
82
69
|
end
|
|
83
70
|
|
|
84
71
|
def destroy_module(constant)
|
|
85
|
-
|
|
86
|
-
validate_gem_root!(namespaces.first)
|
|
72
|
+
lib_path = constant.lib_path
|
|
87
73
|
|
|
88
|
-
lib_path = "#{File.join("lib", *segments)}.rb"
|
|
89
74
|
remove_file(lib_path)
|
|
90
75
|
remove_empty_parents(File.dirname(lib_path), File.join("lib", @require_path))
|
|
91
76
|
end
|
|
92
77
|
|
|
93
78
|
def destroy_command(name)
|
|
79
|
+
name = name.split("::").last if name.include?("::")
|
|
94
80
|
file_name = name.underscore
|
|
95
81
|
file_path = File.join("lib", @require_path, "cli", "commands", "#{file_name}.rb")
|
|
96
82
|
remove_file(file_path)
|
|
@@ -29,7 +29,7 @@ module Gempilot
|
|
|
29
29
|
def run(type = nil, path = nil)
|
|
30
30
|
type ||= prompt_for_type
|
|
31
31
|
detect_gem_context
|
|
32
|
-
path ||= prompt_for_path
|
|
32
|
+
path ||= prompt_for_path(type)
|
|
33
33
|
dispatch_add(type, path)
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -40,16 +40,14 @@ module Gempilot
|
|
|
40
40
|
ask_multiple_choice(colors.green("Type"), %w[class module command])
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
def prompt_for_path
|
|
44
|
-
|
|
45
|
-
puts colors.bright_black("Fully-qualified constant name (e.g., #{@gem_module}::Services::Authentication).")
|
|
46
|
-
ask(colors.green("Constant"), required: true)
|
|
43
|
+
def prompt_for_path(type)
|
|
44
|
+
ask(colors.green(type), required: true)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
def dispatch_add(type, path)
|
|
50
48
|
case type
|
|
51
|
-
when "class" then add_class(path)
|
|
52
|
-
when "module" then add_module(path)
|
|
49
|
+
when "class" then add_class(gem_constant(path))
|
|
50
|
+
when "module" then add_module(gem_constant(path))
|
|
53
51
|
when "command" then add_command(path)
|
|
54
52
|
else
|
|
55
53
|
puts colors.red("Unknown type '#{type}'. Use class, module, or command.")
|
|
@@ -86,34 +84,27 @@ module Gempilot
|
|
|
86
84
|
puts
|
|
87
85
|
end
|
|
88
86
|
|
|
89
|
-
def prepare_constant(constant)
|
|
90
|
-
namespaces, name, segments = parse_constant(constant)
|
|
91
|
-
validate_gem_root!(namespaces.first)
|
|
92
|
-
file_path = "#{File.join("lib", *segments)}.rb"
|
|
93
|
-
ensure_directory(File.dirname(file_path))
|
|
94
|
-
[namespaces, name, segments, file_path]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
87
|
def ensure_directory(dir)
|
|
98
88
|
mkdir(dir) unless File.directory?(dir)
|
|
99
89
|
end
|
|
100
90
|
|
|
101
91
|
def add_class(constant)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
source = build_nested_source(namespaces, "class",
|
|
105
|
-
create_file(
|
|
106
|
-
add_test_file(
|
|
92
|
+
print_adding_banner("class", constant.qualified)
|
|
93
|
+
ensure_directory(File.dirname(constant.lib_path))
|
|
94
|
+
source = build_nested_source(constant.namespaces, "class", constant.name)
|
|
95
|
+
create_file(constant.lib_path, source)
|
|
96
|
+
add_test_file(constant)
|
|
107
97
|
end
|
|
108
98
|
|
|
109
99
|
def add_module(constant)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
source = build_nested_source(namespaces, "module",
|
|
113
|
-
create_file(
|
|
100
|
+
print_adding_banner("module", constant.qualified)
|
|
101
|
+
ensure_directory(File.dirname(constant.lib_path))
|
|
102
|
+
source = build_nested_source(constant.namespaces, "module", constant.name)
|
|
103
|
+
create_file(constant.lib_path, source)
|
|
114
104
|
end
|
|
115
105
|
|
|
116
106
|
def add_command(name)
|
|
107
|
+
name = name.split("::").last if name.include?("::")
|
|
117
108
|
file_name = name.underscore
|
|
118
109
|
command_name = name.camelize
|
|
119
110
|
file_path = File.join("lib", @require_path, "cli", "commands", "#{file_name}.rb")
|
|
@@ -178,14 +169,6 @@ module Gempilot
|
|
|
178
169
|
create_file(test_path, content)
|
|
179
170
|
end
|
|
180
171
|
|
|
181
|
-
def class_test_path(segments)
|
|
182
|
-
if @test_framework == :rspec
|
|
183
|
-
"#{File.join("spec", @require_path, *segments[1..])}_spec.rb"
|
|
184
|
-
else
|
|
185
|
-
"#{File.join("test", @require_path, *segments[1..])}_test.rb"
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
172
|
def rspec_class_content(namespaces, class_name)
|
|
190
173
|
<<~RUBY
|
|
191
174
|
require "spec_helper"
|
|
@@ -210,13 +193,13 @@ module Gempilot
|
|
|
210
193
|
RUBY
|
|
211
194
|
end
|
|
212
195
|
|
|
213
|
-
def add_test_file(
|
|
214
|
-
test_path =
|
|
196
|
+
def add_test_file(constant)
|
|
197
|
+
test_path = constant.test_path(@test_framework)
|
|
215
198
|
ensure_directory(File.dirname(test_path))
|
|
216
199
|
content = if @test_framework == :rspec
|
|
217
|
-
rspec_class_content(namespaces,
|
|
200
|
+
rspec_class_content(constant.namespaces, constant.name)
|
|
218
201
|
else
|
|
219
|
-
minitest_class_content(namespaces,
|
|
202
|
+
minitest_class_content(constant.namespaces, constant.name)
|
|
220
203
|
end
|
|
221
204
|
create_file(test_path, content)
|
|
222
205
|
end
|
|
@@ -17,23 +17,19 @@ module Gempilot
|
|
|
17
17
|
@gem_name = File.basename(gemspec, ".gemspec")
|
|
18
18
|
@require_path = @gem_name.tr("-", "/")
|
|
19
19
|
@gem_module = @require_path.camelize
|
|
20
|
-
@test_framework =
|
|
20
|
+
@test_framework = detect_test_framework
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
segments = parts.map(&:underscore)
|
|
28
|
-
[namespaces, name, segments]
|
|
29
|
-
end
|
|
23
|
+
# Detect rspec by its canonical config files rather than the mere
|
|
24
|
+
# presence of a spec/ directory, which a minitest project may also have.
|
|
25
|
+
def detect_test_framework
|
|
26
|
+
return :rspec if File.exist?(".rspec") || File.exist?(File.join("spec", "spec_helper.rb"))
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return if root == expected
|
|
28
|
+
:minitest
|
|
29
|
+
end
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
def gem_constant(input)
|
|
32
|
+
GemConstant.new(input: input, gem_module: @gem_module, require_path: @require_path)
|
|
37
33
|
end
|
|
38
34
|
end
|
|
39
35
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Gempilot
|
|
2
|
+
## A class or module constant resolved within a gem's namespace.
|
|
3
|
+
##
|
|
4
|
+
## Wraps raw user input (a bare suffix like +Services::Auth+ or a
|
|
5
|
+
## fully-qualified +MyGem::Services::Auth+) together with the gem's module
|
|
6
|
+
## and require path, and derives the qualified constant, file paths, and
|
|
7
|
+
## namespace pieces from a single parse.
|
|
8
|
+
##
|
|
9
|
+
## Every constant is rooted at the gem's module: bare input is prefixed with
|
|
10
|
+
## it, while input already starting with the gem's root segment is left as
|
|
11
|
+
## is. Rooting is matched on the first segment only, so an extension gem
|
|
12
|
+
## whose module is +My::Gem+ accepts any +My::...+ constant.
|
|
13
|
+
GemConstant = Data.define(:input, :gem_module, :require_path) do
|
|
14
|
+
using String::Inflectable
|
|
15
|
+
|
|
16
|
+
## The fully-qualified constant, rooted at the gem module.
|
|
17
|
+
def qualified
|
|
18
|
+
input.start_with?("#{root_segment}::") ? input : "#{gem_module}::#{input}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
## Namespace segments preceding the final constant name.
|
|
22
|
+
def namespaces
|
|
23
|
+
parts[0...-1]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
## The final class or module name.
|
|
27
|
+
def name
|
|
28
|
+
parts.last
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
## Path to the constant's source file, e.g. +lib/my_gem/services/auth.rb+.
|
|
32
|
+
def lib_path
|
|
33
|
+
"#{File.join("lib", *path_segments)}.rb"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
## Path to the constant's test file for +framework+ (+:rspec+ or
|
|
37
|
+
## +:minitest+); correct for multi-segment (hyphenated) gem modules.
|
|
38
|
+
def test_path(framework)
|
|
39
|
+
rest = path_segments.drop(require_path.split("/").length)
|
|
40
|
+
if framework == :rspec
|
|
41
|
+
"#{File.join("spec", require_path, *rest)}_spec.rb"
|
|
42
|
+
else
|
|
43
|
+
"#{File.join("test", require_path, *rest)}_test.rb"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def root_segment
|
|
50
|
+
gem_module.split("::").first
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def parts
|
|
54
|
+
qualified.split("::")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def path_segments
|
|
58
|
+
parts.map(&:underscore)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/gempilot/version.rb
CHANGED
data/lib/gempilot/version_tag.rb
CHANGED
|
@@ -5,6 +5,10 @@ module Gempilot
|
|
|
5
5
|
class VersionTag
|
|
6
6
|
include StrictShell
|
|
7
7
|
|
|
8
|
+
## Commit-message prefix written for a version bump; the guard below
|
|
9
|
+
## matches on it, so the two must stay in sync.
|
|
10
|
+
BUMP_MESSAGE_PREFIX = "Bump version to ".freeze
|
|
11
|
+
|
|
8
12
|
attr_reader :version
|
|
9
13
|
|
|
10
14
|
def initialize(version)
|
|
@@ -16,7 +20,7 @@ module Gempilot
|
|
|
16
20
|
raise "Cannot proceed, staging area must be clean" unless status.success?
|
|
17
21
|
|
|
18
22
|
sh "git", "add", version.path.to_s
|
|
19
|
-
sh "git", "commit", "-m", "
|
|
23
|
+
sh "git", "commit", "-m", "#{BUMP_MESSAGE_PREFIX}#{version.value}"
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def tag
|
|
@@ -47,7 +51,7 @@ module Gempilot
|
|
|
47
51
|
raise "Failed to read last commit message" unless status.success?
|
|
48
52
|
|
|
49
53
|
message.strip!
|
|
50
|
-
|
|
54
|
+
raise "Last commit does not appear to be a version bump." unless message.start_with?(BUMP_MESSAGE_PREFIX)
|
|
51
55
|
end
|
|
52
56
|
end
|
|
53
57
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gempilot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Gillis
|
|
@@ -114,6 +114,7 @@ files:
|
|
|
114
114
|
- docs/superpowers/plans/2026-04-06-inflection-tests-and-erb-rename.md
|
|
115
115
|
- docs/superpowers/plans/2026-04-06-integrate-version-tools.md
|
|
116
116
|
- docs/superpowers/plans/2026-04-06-new-readme.md
|
|
117
|
+
- docs/superpowers/plans/2026-06-09-address-review-critiques.md
|
|
117
118
|
- docs/version-management-redesign.md
|
|
118
119
|
- exe/gempilot
|
|
119
120
|
- issues.rec
|
|
@@ -131,6 +132,7 @@ files:
|
|
|
131
132
|
- lib/gempilot/cli/gem_builder.rb
|
|
132
133
|
- lib/gempilot/cli/gem_context.rb
|
|
133
134
|
- lib/gempilot/cli/generator.rb
|
|
135
|
+
- lib/gempilot/gem_constant.rb
|
|
134
136
|
- lib/gempilot/github_release.rb
|
|
135
137
|
- lib/gempilot/project.rb
|
|
136
138
|
- lib/gempilot/project/version.rb
|
|
@@ -159,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
159
161
|
- !ruby/object:Gem::Version
|
|
160
162
|
version: '0'
|
|
161
163
|
requirements: []
|
|
162
|
-
rubygems_version: 4.0.
|
|
164
|
+
rubygems_version: 4.0.14
|
|
163
165
|
specification_version: 4
|
|
164
166
|
summary: A toolkit for creating, managing, and releasing your own rubygems
|
|
165
167
|
test_files: []
|