standard_id-google 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +50 -2
- data/lib/generators/standard_id/google/install/install_generator.rb +76 -0
- data/lib/generators/standard_id/google/install/templates/initializer.rb.erb +30 -0
- data/lib/standard_id/google/version.rb +1 -1
- metadata +13 -23
- data/.claude/hooks/enforce-worktree.sh +0 -84
- data/.claude/settings.json +0 -24
- data/.claude/skills/publish-gem/SKILL.md +0 -211
- data/.claude/skills/start/SKILL.md +0 -185
- data/.claude/skills/worktree/SKILL.md +0 -143
- data/.editorconfig +0 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/.ruby-version +0 -1
- data/AGENTS.md +0 -71
- data/CLAUDE.md +0 -26
- data/CODE_OF_CONDUCT.md +0 -132
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24f43e5bf2451f85eeefc67854060b443fe88781f76228eac4754dc8fcaf42c7
|
|
4
|
+
data.tar.gz: d17aa2f191d3616e3c17f838f2c4c0630c93c4e1afb8372d1f6fd311b61ccbf1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 361bd688a31baaf8bed7ec6a4adfd13c130f146af786ec20e00809be932ee304b9f27bd73890754053a972bad67e51e92dacd1dd6dfc8fe20e5b5c13800a10f8
|
|
7
|
+
data.tar.gz: 7dfbd6c33aa66ec005e29774a7b8419c68593d78c5959933b7ec8c71814dfe454f718e147a8714b32a6b51ce85f04d0cf8c163b0f69d1ae9d0bc636aa2cf4e8d
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-07-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Install generator: `bin/rails g standard_id:google:install`.** Writes
|
|
15
|
+
`config/initializers/standard_id_google.rb` with the `social.google_*` fields
|
|
16
|
+
wired to ENV, then prints the environment variables the host has to set and
|
|
17
|
+
the reminder that `/auth/callback/google` must be registered as an authorized
|
|
18
|
+
redirect URI for **every** origin served — the Google console matches the
|
|
19
|
+
redirect exactly, so staging and production each need their own. Idempotent:
|
|
20
|
+
re-running skips an existing initializer; `--force` overwrites,
|
|
21
|
+
`--skip-initializer` writes nothing.
|
|
22
|
+
|
|
23
|
+
It writes a **separate** file rather than editing `standard_id.rb`, so the
|
|
24
|
+
provider can be removed by deleting one file and `standard_id`'s own install
|
|
25
|
+
generator stays free to overwrite its initializer without clobbering these
|
|
26
|
+
credentials. Initializers load alphabetically, so the base config is applied
|
|
27
|
+
first. The generated file uses the `config.social.` form throughout — a spec
|
|
28
|
+
pins that it never emits the unqualified `config.google_*` form, which works
|
|
29
|
+
today only because the names happen to be unique across scopes.
|
|
30
|
+
|
|
31
|
+
Five of the nine `standard_*` gems shipped an install generator and this was
|
|
32
|
+
not one of them, which left both consumers assembling the block from the
|
|
33
|
+
README by hand.
|
|
34
|
+
|
|
35
|
+
### Documentation
|
|
36
|
+
|
|
37
|
+
- **Consumer list corrected in `CLAUDE.md`: this gem has two consumers, not
|
|
38
|
+
one.** It named `luminality-web` only; `sidekick-web` also consumes it. Both
|
|
39
|
+
live in sibling workspaces rather than beside this repo, which is how the
|
|
40
|
+
second one went unnoticed.
|
|
41
|
+
|
|
42
|
+
- **Corrected the Configuration section, which showed a form that raised on
|
|
43
|
+
`standard_id` <= 0.32.0.** These fields are declared by this gem, and until
|
|
44
|
+
`standard_id` 0.33.0 they were declared from this gem's Railtie
|
|
45
|
+
`after_initialize` — after `config/initializers` — so the documented plain
|
|
46
|
+
initializer raised `StandardId::ConfigurationError: Unknown field
|
|
47
|
+
'google_client_id' for scope 'social'`. The README now records the
|
|
48
|
+
`after_initialize` workaround for older `standard_id`, states that 0.33.0
|
|
49
|
+
declares provider fields before `:load_config_initializers` so the plain form
|
|
50
|
+
is correct there, and notes that the fields do not exist at all without this
|
|
51
|
+
gem in the Gemfile — on any `standard_id` version.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **`standard_id` dependency tightened from `~> 0.1, >= 0.1.7` to `~> 0.29.0`.**
|
|
56
|
+
The old constraint claimed compatibility with every `0.x` release while this
|
|
57
|
+
plugin reaches into `StandardId::ProviderRegistry` and
|
|
58
|
+
`StandardId::Providers::Google`, and `standard_id` is pre-1.0 with breaking
|
|
59
|
+
minors. Bundler would happily resolve against an untested minor and fail at
|
|
60
|
+
runtime instead of at resolution. Both current consumers already pin
|
|
61
|
+
`standard_id "~> 0.29.0"`, so nothing existing is affected.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Gemspec no longer packages the `.claude/` directory. Published `0.3.0` shipped
|
|
66
|
+
`.claude/settings.json`, `.claude/hooks/enforce-worktree.sh`, and three skill
|
|
67
|
+
files to every consumer — this gem's `spec.files` reject-list was the only one
|
|
68
|
+
in the `standard_*` family missing the `.claude/` prefix (`standard_id-apple`
|
|
69
|
+
already had it). Packaged file count drops 21 → 16; `lib/` and `LICENSE` are
|
|
70
|
+
unaffected.
|
|
71
|
+
- Gemspec now uses an allow-list (`Dir["lib/**/*", …]`) rather than a
|
|
72
|
+
`git ls-files` reject-list, so packaging fails **closed** and this class of
|
|
73
|
+
leak cannot recur. Also drops `.editorconfig`, `.pinact.yaml`, `.rspec`,
|
|
74
|
+
`.rubocop.yml`, `.ruby-version`, `AGENTS.md`, `CLAUDE.md`, and
|
|
75
|
+
`CODE_OF_CONDUCT.md`; `lib/` is byte-identical.
|
|
76
|
+
|
|
10
77
|
## [0.3.0] - 2026-04-29
|
|
11
78
|
|
|
12
79
|
### Added
|
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
|
@@ -20,11 +20,28 @@ bundle install
|
|
|
20
20
|
|
|
21
21
|
The gem automatically registers itself with StandardId when it is required.
|
|
22
22
|
|
|
23
|
+
Then run the install generator to drop the credentials block in place:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bin/rails g standard_id:google:install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This writes `config/initializers/standard_id_google.rb` — deliberately a
|
|
30
|
+
separate file from `config/initializers/standard_id.rb`, so the provider can be
|
|
31
|
+
removed by deleting one file and `standard_id`'s own install generator stays
|
|
32
|
+
free to overwrite its initializer without clobbering these values. Initializers
|
|
33
|
+
load alphabetically, so the base config is applied first. The generator is
|
|
34
|
+
idempotent; re-running on an existing initializer skips with a clear message
|
|
35
|
+
(pass `--force` to overwrite).
|
|
36
|
+
|
|
23
37
|
## Configuration
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
The generator writes this for you; the block is documented here for hosts
|
|
40
|
+
configuring by hand. Configure your Google credentials inside the StandardId
|
|
41
|
+
configuration block:
|
|
26
42
|
|
|
27
43
|
```ruby
|
|
44
|
+
# config/initializers/standard_id.rb
|
|
28
45
|
StandardId.configure do |config|
|
|
29
46
|
config.social.google_client_id = ENV.fetch("GOOGLE_OAUTH_CLIENT_ID", nil)
|
|
30
47
|
config.social.google_client_secret = ENV.fetch("GOOGLE_OAUTH_CLIENT_SECRET", nil)
|
|
@@ -33,6 +50,37 @@ end
|
|
|
33
50
|
|
|
34
51
|
With those values in place, StandardId routes such as `/auth/callback/google` continue to function using this provider gem.
|
|
35
52
|
|
|
53
|
+
### Boot ordering (standard_id <= 0.32.0)
|
|
54
|
+
|
|
55
|
+
These fields are declared by *this gem*, not by `standard_id`, and until
|
|
56
|
+
`standard_id` 0.33.0 they were declared from this gem's Railtie
|
|
57
|
+
`after_initialize` — which runs *after* `config/initializers`. On `standard_id`
|
|
58
|
+
**0.32.0 and earlier** the block above therefore raised:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
StandardId::ConfigurationError: Unknown field 'google_client_id' for scope 'social'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The workaround was to wrap the writes:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# Only needed on standard_id <= 0.32.0
|
|
68
|
+
Rails.application.config.after_initialize do
|
|
69
|
+
StandardId.configure do |config|
|
|
70
|
+
config.social.google_client_id = ENV.fetch("GOOGLE_OAUTH_CLIENT_ID", nil)
|
|
71
|
+
config.social.google_client_secret = ENV.fetch("GOOGLE_OAUTH_CLIENT_SECRET", nil)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`standard_id` **>= 0.33.0** declares every loaded provider's fields before
|
|
77
|
+
`:load_config_initializers`, so a plain initializer is correct. The wrapper is no
|
|
78
|
+
longer needed and existing ones keep working unchanged.
|
|
79
|
+
|
|
80
|
+
Note this is about *ordering*, not just versions: **the fields do not exist
|
|
81
|
+
without this gem in your Gemfile**, on any `standard_id` version. Configuring
|
|
82
|
+
`social.google_*` with the plugin absent raises the same error, correctly.
|
|
83
|
+
|
|
36
84
|
## Testing
|
|
37
85
|
|
|
38
86
|
Run the provider test suite with:
|
|
@@ -53,4 +101,4 @@ To release a new version:
|
|
|
53
101
|
|
|
54
102
|
## License
|
|
55
103
|
|
|
56
|
-
The gem is available as open source under the terms of the [MIT License](LICENSE
|
|
104
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module StandardId
|
|
6
|
+
module Google
|
|
7
|
+
module Generators
|
|
8
|
+
# Installs the Google provider's credentials block in a host Rails app.
|
|
9
|
+
#
|
|
10
|
+
# Writes `config/initializers/standard_id_google.rb` — a separate file
|
|
11
|
+
# from `config/initializers/standard_id.rb` on purpose. The provider is
|
|
12
|
+
# opt-in per app, so its credentials should be removable by deleting one
|
|
13
|
+
# file, and `standard_id`'s own install generator must stay free to
|
|
14
|
+
# overwrite its initializer without clobbering these values.
|
|
15
|
+
#
|
|
16
|
+
# Initializers load alphabetically, so `standard_id.rb` runs before
|
|
17
|
+
# `standard_id_google.rb` — the base configuration is already applied by
|
|
18
|
+
# the time this file sets the `social.google_*` fields.
|
|
19
|
+
#
|
|
20
|
+
# Idempotent: re-running skips an initializer that is already there.
|
|
21
|
+
# `--skip-initializer` opts out; `--force` overwrites.
|
|
22
|
+
class InstallGenerator < Rails::Generators::Base
|
|
23
|
+
source_root File.expand_path("templates", __dir__)
|
|
24
|
+
|
|
25
|
+
INITIALIZER_PATH = "config/initializers/standard_id_google.rb"
|
|
26
|
+
|
|
27
|
+
desc <<~DESC
|
|
28
|
+
Installs StandardId Google. This writes #{INITIALIZER_PATH} with the
|
|
29
|
+
social.google_* fields wired to ENV, and prints the env vars the host
|
|
30
|
+
needs to set.
|
|
31
|
+
|
|
32
|
+
The generator is idempotent — an existing initializer is skipped with
|
|
33
|
+
a clear message. Pass --force to overwrite.
|
|
34
|
+
DESC
|
|
35
|
+
|
|
36
|
+
class_option :skip_initializer, type: :boolean, default: false,
|
|
37
|
+
desc: "Do not write #{INITIALIZER_PATH}"
|
|
38
|
+
class_option :force, type: :boolean, default: false,
|
|
39
|
+
desc: "Overwrite #{INITIALIZER_PATH} if it already exists"
|
|
40
|
+
|
|
41
|
+
def copy_initializer
|
|
42
|
+
if options[:skip_initializer]
|
|
43
|
+
say_status("skip", "#{INITIALIZER_PATH} (--skip-initializer)", :yellow)
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if File.exist?(File.join(destination_root, INITIALIZER_PATH)) && !options[:force]
|
|
48
|
+
say_status("identical", "#{INITIALIZER_PATH} (already exists; pass --force to overwrite)", :blue)
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
template "initializer.rb.erb", INITIALIZER_PATH, force: options[:force]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def print_env_hint
|
|
56
|
+
return if options[:skip_initializer]
|
|
57
|
+
|
|
58
|
+
say ""
|
|
59
|
+
say "=" * 79
|
|
60
|
+
say "StandardId Google installed."
|
|
61
|
+
say ""
|
|
62
|
+
say "Set these in the host's environment (1Password / DO app spec / .env):"
|
|
63
|
+
say ""
|
|
64
|
+
say " GOOGLE_OAUTH_CLIENT_ID the OAuth 2.0 Web application client ID"
|
|
65
|
+
say " GOOGLE_OAUTH_CLIENT_SECRET its client secret"
|
|
66
|
+
say ""
|
|
67
|
+
say "Register /auth/callback/google as an authorized redirect URI in the"
|
|
68
|
+
say "Google Cloud console for EVERY origin you serve — the console matches"
|
|
69
|
+
say "the redirect exactly, so staging and production each need their own."
|
|
70
|
+
say "=" * 79
|
|
71
|
+
say ""
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Google Sign In credentials for StandardId.
|
|
4
|
+
#
|
|
5
|
+
# These two fields are declared by the standard_id-google gem, NOT by
|
|
6
|
+
# standard_id — they do not exist unless this gem is in the Gemfile, on any
|
|
7
|
+
# standard_id version. Removing the gem and leaving this file behind raises
|
|
8
|
+
# `StandardId::ConfigurationError: Unknown field 'google_client_id' for scope
|
|
9
|
+
# 'social'`, which is correct behaviour, not a bug.
|
|
10
|
+
#
|
|
11
|
+
# Set them in the `social` scope, as below. The unqualified form
|
|
12
|
+
# (`config.google_client_id = ...`) happens to work today because StandardId
|
|
13
|
+
# routes a unique unqualified name to its owning scope, but it stops working
|
|
14
|
+
# the day another scope declares a colliding name. Use `config.social.`.
|
|
15
|
+
#
|
|
16
|
+
# On standard_id <= 0.32.0 these fields were declared from this gem's Railtie
|
|
17
|
+
# `after_initialize`, which runs AFTER config/initializers — so the plain block
|
|
18
|
+
# below raised at boot and had to be wrapped:
|
|
19
|
+
#
|
|
20
|
+
# Rails.application.config.after_initialize do
|
|
21
|
+
# StandardId.configure { |config| config.social.google_client_id = ... }
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# standard_id >= 0.33.0 declares every loaded provider's fields before
|
|
25
|
+
# `:load_config_initializers`, so the plain form is correct. Existing wrappers
|
|
26
|
+
# keep working and need no change.
|
|
27
|
+
StandardId.configure do |config|
|
|
28
|
+
config.social.google_client_id = ENV.fetch("GOOGLE_OAUTH_CLIENT_ID", nil)
|
|
29
|
+
config.social.google_client_secret = ENV.fetch("GOOGLE_OAUTH_CLIENT_SECRET", nil)
|
|
30
|
+
end
|
metadata
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standard_id-google
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
8
|
-
bindir:
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
@@ -27,22 +27,22 @@ dependencies:
|
|
|
27
27
|
name: standard_id
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0.1'
|
|
33
30
|
- - ">="
|
|
34
31
|
- !ruby/object:Gem::Version
|
|
35
|
-
version: 0.
|
|
32
|
+
version: '0.29'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '1.0'
|
|
36
36
|
type: :runtime
|
|
37
37
|
prerelease: false
|
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
39
39
|
requirements:
|
|
40
|
-
- - "~>"
|
|
41
|
-
- !ruby/object:Gem::Version
|
|
42
|
-
version: '0.1'
|
|
43
40
|
- - ">="
|
|
44
41
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 0.
|
|
42
|
+
version: '0.29'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '1.0'
|
|
46
46
|
description: Extracted StandardId::Providers::Google implementation packaged as a
|
|
47
47
|
standalone gem so StandardId installations can opt into Sign in with Google independently.
|
|
48
48
|
email:
|
|
@@ -51,22 +51,12 @@ executables: []
|
|
|
51
51
|
extensions: []
|
|
52
52
|
extra_rdoc_files: []
|
|
53
53
|
files:
|
|
54
|
-
- ".claude/hooks/enforce-worktree.sh"
|
|
55
|
-
- ".claude/settings.json"
|
|
56
|
-
- ".claude/skills/publish-gem/SKILL.md"
|
|
57
|
-
- ".claude/skills/start/SKILL.md"
|
|
58
|
-
- ".claude/skills/worktree/SKILL.md"
|
|
59
|
-
- ".editorconfig"
|
|
60
|
-
- ".rspec"
|
|
61
|
-
- ".rubocop.yml"
|
|
62
|
-
- ".ruby-version"
|
|
63
|
-
- AGENTS.md
|
|
64
54
|
- CHANGELOG.md
|
|
65
|
-
-
|
|
66
|
-
- CODE_OF_CONDUCT.md
|
|
67
|
-
- LICENSE.txt
|
|
55
|
+
- LICENSE
|
|
68
56
|
- README.md
|
|
69
57
|
- Rakefile
|
|
58
|
+
- lib/generators/standard_id/google/install/install_generator.rb
|
|
59
|
+
- lib/generators/standard_id/google/install/templates/initializer.rb.erb
|
|
70
60
|
- lib/standard_id/google.rb
|
|
71
61
|
- lib/standard_id/google/providers/google.rb
|
|
72
62
|
- lib/standard_id/google/railtie.rb
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Enforce worktree-only file modifications for Claude Code
|
|
3
|
-
#
|
|
4
|
-
# Runs on PreToolUse for Edit, Write, and NotebookEdit tools.
|
|
5
|
-
# Blocks all file modifications in the main checkout — changes must
|
|
6
|
-
# happen inside a git worktree (.worktrees/<name>/).
|
|
7
|
-
#
|
|
8
|
-
# Exit codes:
|
|
9
|
-
# 0 — allow (in a worktree, CI, or non-git path)
|
|
10
|
-
# 2 — block (in main checkout)
|
|
11
|
-
#
|
|
12
|
-
# No programmatic bypass — this is intentional. If the hook itself has a bug,
|
|
13
|
-
# a human must fix it manually (edit the file or remove from settings.json).
|
|
14
|
-
#
|
|
15
|
-
# Note: This hook covers Edit, Write, and NotebookEdit tools. Bash tool writes
|
|
16
|
-
# (echo/sed/tee/cp) are not intercepted — the CLAUDE.md instruction is the
|
|
17
|
-
# enforcement layer for those. Covering Bash reliably would require parsing
|
|
18
|
-
# arbitrary shell commands, which is brittle.
|
|
19
|
-
|
|
20
|
-
# Guard: jq required for JSON parsing
|
|
21
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Skip in CI environments
|
|
26
|
-
if [[ "${CI:-}" == "true" ]] || [[ -n "${GITHUB_ACTIONS:-}" ]]; then
|
|
27
|
-
exit 0
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
INPUT=$(cat)
|
|
31
|
-
TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""') || exit 0
|
|
32
|
-
|
|
33
|
-
# Extract file path based on tool (NotebookEdit uses notebook_path, not file_path)
|
|
34
|
-
case "$TOOL_NAME" in
|
|
35
|
-
Edit|Write)
|
|
36
|
-
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // ""') || exit 0
|
|
37
|
-
;;
|
|
38
|
-
NotebookEdit)
|
|
39
|
-
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.notebook_path // .tool_input.file_path // ""') || exit 0
|
|
40
|
-
;;
|
|
41
|
-
*)
|
|
42
|
-
exit 0
|
|
43
|
-
;;
|
|
44
|
-
esac
|
|
45
|
-
|
|
46
|
-
if [[ -z "$FILE_PATH" ]]; then
|
|
47
|
-
exit 0
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Find an existing directory to run git commands in
|
|
51
|
-
if [[ -d "$FILE_PATH" ]]; then
|
|
52
|
-
CHECK_DIR="$FILE_PATH"
|
|
53
|
-
elif [[ -e "$FILE_PATH" ]]; then
|
|
54
|
-
CHECK_DIR=$(dirname "$FILE_PATH")
|
|
55
|
-
else
|
|
56
|
-
# File doesn't exist yet — walk up to find an existing directory
|
|
57
|
-
CHECK_DIR=$(dirname "$FILE_PATH")
|
|
58
|
-
while [[ ! -d "$CHECK_DIR" ]] && [[ "$CHECK_DIR" != "/" ]]; do
|
|
59
|
-
CHECK_DIR=$(dirname "$CHECK_DIR")
|
|
60
|
-
done
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
if [[ ! -d "$CHECK_DIR" ]]; then
|
|
64
|
-
exit 0
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
# Check if we're inside a git repository at all
|
|
68
|
-
GIT_DIR=$(cd "$CHECK_DIR" && git rev-parse --git-dir 2>/dev/null) || exit 0
|
|
69
|
-
|
|
70
|
-
# Make relative paths absolute
|
|
71
|
-
if [[ "$GIT_DIR" != /* ]]; then
|
|
72
|
-
GIT_DIR=$(cd "$CHECK_DIR" && cd "$GIT_DIR" && pwd) || exit 0
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# If git-dir contains /worktrees/, we're in a worktree — allow
|
|
76
|
-
if [[ "$GIT_DIR" == *".git/worktrees/"* ]]; then
|
|
77
|
-
exit 0
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
# We're in the main checkout — block
|
|
81
|
-
echo "❌ Blocked: Cannot modify files in the main checkout." >&2
|
|
82
|
-
echo " Create a worktree first: /worktree or /start <issue>" >&2
|
|
83
|
-
echo " File: $FILE_PATH" >&2
|
|
84
|
-
exit 2
|
data/.claude/settings.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Skill(worktree)",
|
|
5
|
-
"Skill(start)",
|
|
6
|
-
"Bash(git worktree:*)",
|
|
7
|
-
"Bash(git stash:*)"
|
|
8
|
-
]
|
|
9
|
-
},
|
|
10
|
-
"hooks": {
|
|
11
|
-
"PreToolUse": [
|
|
12
|
-
{
|
|
13
|
-
"matcher": "Edit|Write|NotebookEdit",
|
|
14
|
-
"hooks": [
|
|
15
|
-
{
|
|
16
|
-
"type": "command",
|
|
17
|
-
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/enforce-worktree.sh",
|
|
18
|
-
"timeout": 10
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: publish-gem
|
|
3
|
-
description: "Publish a Ruby gem to RubyGems.org via CI. Use when the user says 'publish gem', 'push gem', 'release gem', or '/publish-gem'. Handles version bump, changelog, build verification, PR, tagging, and CI-driven publish."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Publish Gem Skill
|
|
7
|
-
|
|
8
|
-
Prepare and publish a Ruby gem to RubyGems.org via GitHub Actions trusted publishing.
|
|
9
|
-
|
|
10
|
-
Publishing is done by CI (not locally) — pushing a version tag triggers the `release.yml` workflow which builds the gem, creates a GitHub Release, and pushes to RubyGems via OIDC. This skill handles everything up to and including the tag push.
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
/publish-gem # Full release flow
|
|
16
|
-
/publish-gem --dry-run # Verify everything, stop before PR creation
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Workflow
|
|
20
|
-
|
|
21
|
-
### 1. Verify Context
|
|
22
|
-
|
|
23
|
-
Confirm we're in a gem project:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Check current branch
|
|
27
|
-
git branch --show-current
|
|
28
|
-
|
|
29
|
-
# Must have a .gemspec file
|
|
30
|
-
ls *.gemspec
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Blockers:**
|
|
34
|
-
- No `.gemspec` found — stop, not a gem project
|
|
35
|
-
- Multiple `.gemspec` files found — ask the user which one to build
|
|
36
|
-
|
|
37
|
-
**Branch handling:**
|
|
38
|
-
- If not on `main`, switch automatically: `git checkout main` (publishing from non-main is almost never intentional). If the user explicitly requested publishing from a non-main branch, warn and ask for confirmation before switching.
|
|
39
|
-
- After switching (or if already on `main`), always sync with remote:
|
|
40
|
-
```bash
|
|
41
|
-
git pull --rebase origin main
|
|
42
|
-
```
|
|
43
|
-
- If the rebase fails due to conflicts, stop and ask the user to resolve them before proceeding
|
|
44
|
-
- After syncing, verify the working tree is clean (`git status --porcelain`). Warn if dirty — the build may include uncommitted changes.
|
|
45
|
-
- Clean up any stale `.gem` files in the working directory before proceeding:
|
|
46
|
-
```bash
|
|
47
|
-
rm -f *.gem
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 2. Extract Gem Metadata
|
|
51
|
-
|
|
52
|
-
Read the gemspec to extract key details:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
# Get gem name and version
|
|
56
|
-
ruby -e "spec = Gem::Specification.load(Dir['*.gemspec'].first); puts \"#{spec.name} #{spec.version}\""
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Also read and display:
|
|
60
|
-
- Current version from the gemspec
|
|
61
|
-
- CHANGELOG.md entry for this version (if exists)
|
|
62
|
-
- `spec.files` count to verify packaging
|
|
63
|
-
|
|
64
|
-
### 3. Pre-Publish Checks
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
# Check if this version is already published on RubyGems.org
|
|
68
|
-
gem info -r <gem_name> -v <version>
|
|
69
|
-
|
|
70
|
-
# Check if CHANGELOG.md exists when gemspec references it
|
|
71
|
-
ruby -e "spec = Gem::Specification.load(Dir['*.gemspec'].first); puts spec.metadata['changelog_uri']"
|
|
72
|
-
test -f CHANGELOG.md && echo "CHANGELOG.md found" || echo "CHANGELOG.md missing"
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**Blockers:**
|
|
76
|
-
- Version already published — ask the user what version to bump to (suggest next patch/minor/major). If the user declines, abort the publish.
|
|
77
|
-
|
|
78
|
-
**Warnings:**
|
|
79
|
-
- Gemspec `changelog_uri` is set but `CHANGELOG.md` does not exist locally — warn the user. This will result in a broken link on RubyGems.org.
|
|
80
|
-
- CHANGELOG.md has no entry for the version being published — warn the user. The `release.yml` workflow will fail if no changelog entry exists for the version.
|
|
81
|
-
|
|
82
|
-
### 4. Version Bump (if needed)
|
|
83
|
-
|
|
84
|
-
When the current version is already published, or the user requests a bump:
|
|
85
|
-
|
|
86
|
-
1. Update `lib/<gem_name>/version.rb` with the new version
|
|
87
|
-
2. Run `bundle install` to sync `Gemfile.lock`
|
|
88
|
-
|
|
89
|
-
**Critical:** Always run `bundle install` after changing the version to keep `Gemfile.lock` in sync. Skipping this causes CI failures.
|
|
90
|
-
|
|
91
|
-
### 5. Update CHANGELOG.md
|
|
92
|
-
|
|
93
|
-
If the `[Unreleased]` section in CHANGELOG.md has content:
|
|
94
|
-
1. Rename `[Unreleased]` to `[<version>] - <today's date>` (format: YYYY-MM-DD)
|
|
95
|
-
2. Add a new empty `[Unreleased]` section above it
|
|
96
|
-
|
|
97
|
-
If `[Unreleased]` is empty, warn the user and ask if they want to proceed without changelog entries for this version.
|
|
98
|
-
|
|
99
|
-
### 6. Run Tests
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
bundle exec rspec
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
**Blockers:**
|
|
106
|
-
- Tests fail — stop and ask the user to fix. Do not proceed with a failing test suite.
|
|
107
|
-
|
|
108
|
-
### 7. Build Verification
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
gem build <name>.gemspec
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Verify the `.gem` file was created, show its size, then clean up:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
rm <name>-<version>.gem
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### 8. Release Summary
|
|
121
|
-
|
|
122
|
-
Present a summary before proceeding:
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
## Release Summary
|
|
126
|
-
|
|
127
|
-
Name: <gem_name>
|
|
128
|
-
Version: <version>
|
|
129
|
-
Files: <file count> files in gem
|
|
130
|
-
Registry: https://rubygems.org (published by CI via trusted publisher)
|
|
131
|
-
|
|
132
|
-
Changelog:
|
|
133
|
-
<first few lines of the version's CHANGELOG entry>
|
|
134
|
-
|
|
135
|
-
Next steps:
|
|
136
|
-
1. Create version bump PR
|
|
137
|
-
2. Merge PR (manual)
|
|
138
|
-
3. Tag release → CI publishes to RubyGems + creates GitHub Release
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
If `--dry-run` was passed, stop here.
|
|
142
|
-
|
|
143
|
-
### 9. Create Version Bump PR
|
|
144
|
-
|
|
145
|
-
Commit the version bump, changelog, and lockfile on a branch and open a PR:
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
git checkout -b chore/release-v<version>
|
|
149
|
-
git add lib/<gem_name>/version.rb Gemfile.lock CHANGELOG.md
|
|
150
|
-
git commit -m "chore: Release v<version>"
|
|
151
|
-
git push -u origin chore/release-v<version>
|
|
152
|
-
gh pr create --title "chore: Release v<version>" --body "$(cat <<'EOF'
|
|
153
|
-
## Release v<version>
|
|
154
|
-
|
|
155
|
-
<changelog entry for this version>
|
|
156
|
-
|
|
157
|
-
After merging, Claude will tag `v<version>` which triggers CI to:
|
|
158
|
-
- Create a GitHub Release with changelog notes
|
|
159
|
-
- Build and publish the gem to RubyGems.org via trusted publisher
|
|
160
|
-
EOF
|
|
161
|
-
)"
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Tell the user to review and merge the PR, then let you know when it's merged.
|
|
165
|
-
|
|
166
|
-
### 10. Tag the Release (after PR merge)
|
|
167
|
-
|
|
168
|
-
When the user confirms the PR is merged:
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
# Sync main — use reset to avoid divergent branch issues from squash merge
|
|
172
|
-
git checkout main
|
|
173
|
-
git fetch origin main
|
|
174
|
-
git reset --hard origin/main
|
|
175
|
-
|
|
176
|
-
# Tag the merged commit
|
|
177
|
-
git tag -a v<version> -m "Release v<version>"
|
|
178
|
-
git push origin v<version>
|
|
179
|
-
|
|
180
|
-
# Clean up the bump branch locally
|
|
181
|
-
# -D needed because squash merge doesn't preserve individual commits in main's history
|
|
182
|
-
git branch -D chore/release-v<version>
|
|
183
|
-
# Remote branch may already be deleted by GitHub's auto-delete setting — ignore errors
|
|
184
|
-
git push origin --delete chore/release-v<version> 2>/dev/null || true
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
The tag push triggers `release.yml` which:
|
|
188
|
-
1. Verifies the tag version matches `version.rb`
|
|
189
|
-
2. Waits for CI to pass
|
|
190
|
-
3. Extracts changelog notes and creates a GitHub Release
|
|
191
|
-
4. Builds the gem and publishes to RubyGems.org via OIDC trusted publisher
|
|
192
|
-
|
|
193
|
-
### 11. Output
|
|
194
|
-
|
|
195
|
-
Report:
|
|
196
|
-
1. RubyGems URL: `https://rubygems.org/gems/<name>` (available after CI completes)
|
|
197
|
-
2. Git tag created: `v<version>`
|
|
198
|
-
3. GitHub Release + RubyGems publish: triggered by CI — link: `https://github.com/<owner>/<repo>/actions`
|
|
199
|
-
4. Remind: allow a few minutes for CI to complete, then the gem will appear on RubyGems and the GitHub Release will be created
|
|
200
|
-
|
|
201
|
-
## Error Handling
|
|
202
|
-
|
|
203
|
-
| Error | Solution |
|
|
204
|
-
|-------|----------|
|
|
205
|
-
| `gem build` fails | Fix gemspec errors and retry |
|
|
206
|
-
| Tests fail | Fix tests before proceeding |
|
|
207
|
-
| Tag already exists | Version was previously tagged — skip tagging |
|
|
208
|
-
| Tag push fails | Likely a permissions issue — report and continue |
|
|
209
|
-
| Remote branch already deleted | GitHub auto-deleted it — ignore the error |
|
|
210
|
-
| User declines version bump | Abort the publish |
|
|
211
|
-
| CI publish fails | Check the Actions tab — common causes: trusted publisher not configured on RubyGems.org, `rubygems` environment not set up in GitHub repo settings, or version mismatch between tag and version.rb |
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: start
|
|
3
|
-
description: "Start working on Linear issues. Use when the user says 'start working on', 'pick up issue', 'work on RAR-123', or wants to begin development on Linear issues. Handles status updates, branch creation, and context gathering."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Start Skill
|
|
7
|
-
|
|
8
|
-
Begin working on Linear issues with proper setup: update status, create branches, gather context, and track progress.
|
|
9
|
-
|
|
10
|
-
## Prerequisites
|
|
11
|
-
|
|
12
|
-
This skill requires the Linear MCP server to be configured. If Linear tools (`mcp__linear__*`) are not available, the skill will warn and offer to proceed with git-only setup (branch creation without status updates).
|
|
13
|
-
|
|
14
|
-
## Scope
|
|
15
|
-
|
|
16
|
-
This skill sets up local development for Linear issues. It does **NOT**:
|
|
17
|
-
- Merge PRs to main (merging is a human decision)
|
|
18
|
-
- Delete branches or worktrees automatically
|
|
19
|
-
- Close or complete Linear issues
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
/start <issue-identifiers...> # Start specific issues (e.g., /start RAR-123 RAR-124)
|
|
25
|
-
/start --mine # Show my assigned issues ready to start
|
|
26
|
-
/start --backlog # Show backlog issues for a team
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Workflow
|
|
30
|
-
|
|
31
|
-
> **Note:** MCP tool calls shown below use pseudocode syntax for readability.
|
|
32
|
-
> Actual invocation uses Claude's tool use API with the `mcp__linear__*` tools.
|
|
33
|
-
|
|
34
|
-
### 1. Parse Input and Fetch Issues
|
|
35
|
-
|
|
36
|
-
**If specific identifiers provided:**
|
|
37
|
-
|
|
38
|
-
Fetch each issue using Linear MCP:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
mcp__linear__get_issue(id: "RAR-123", includeRelations: true)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**If `--mine` flag:**
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
mcp__linear__list_issues(assignee: "me", state: "Todo", limit: 10)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**If `--backlog` flag (with optional `--team` and `--project` filters):**
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
mcp__linear__list_issues(
|
|
54
|
-
team: "<from --team flag, default: Rarebit>",
|
|
55
|
-
project: "<from --project flag, if provided>",
|
|
56
|
-
state: "Backlog",
|
|
57
|
-
limit: 10
|
|
58
|
-
)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Present the issues and let the user select which to work on.
|
|
62
|
-
|
|
63
|
-
### 2. Pre-Work Checks
|
|
64
|
-
|
|
65
|
-
Before starting, verify:
|
|
66
|
-
|
|
67
|
-
**Check for blockers:**
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
# From get_issue with includeRelations: true
|
|
71
|
-
# Look at the blocking/blockedBy relations
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
If blocked:
|
|
75
|
-
```
|
|
76
|
-
Warning: RAR-123 is blocked by:
|
|
77
|
-
- RAR-120: "Set up middleware" (In Progress)
|
|
78
|
-
|
|
79
|
-
Options:
|
|
80
|
-
1. Start anyway (work may be blocked)
|
|
81
|
-
2. Start the blocking issue instead
|
|
82
|
-
3. Cancel
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Check issue readiness:**
|
|
86
|
-
- Has description/acceptance criteria?
|
|
87
|
-
- Has assigned estimate?
|
|
88
|
-
|
|
89
|
-
If missing context, warn but allow proceeding.
|
|
90
|
-
|
|
91
|
-
### 3. Update Issue Status
|
|
92
|
-
|
|
93
|
-
**Skip this step if `--no-status` flag is provided.**
|
|
94
|
-
|
|
95
|
-
Update each issue to "In Progress":
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
mcp__linear__save_issue(
|
|
99
|
-
id: "<issue-uuid>",
|
|
100
|
-
stateId: "<in-progress-state-id>"
|
|
101
|
-
)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
The workflow should not block on Linear failures — local development can proceed.
|
|
105
|
-
|
|
106
|
-
### 4. Set Up Worktree
|
|
107
|
-
|
|
108
|
-
**Always create a worktree** to isolate this work from any other state in the repo. This prevents changes from different sessions bleeding into unrelated PRs.
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@refs/remotes/origin/@@')
|
|
112
|
-
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
|
113
|
-
git fetch origin "$DEFAULT_BRANCH"
|
|
114
|
-
git worktree add .worktrees/<identifier> -b <branch-name> "origin/$DEFAULT_BRANCH"
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**`--no-worktree` flag:** If the user explicitly passes `--no-worktree`, check the current state:
|
|
118
|
-
- On the default branch with a clean working tree → fall back to a simple branch:
|
|
119
|
-
```bash
|
|
120
|
-
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@refs/remotes/origin/@@')
|
|
121
|
-
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
|
122
|
-
git fetch origin "$DEFAULT_BRANCH"
|
|
123
|
-
git checkout -b <branch-name> "origin/$DEFAULT_BRANCH"
|
|
124
|
-
```
|
|
125
|
-
- Otherwise → **stop and report why**:
|
|
126
|
-
_"Cannot skip worktree: working tree has uncommitted changes (or is on a feature branch). Stash or commit your changes first, switch to the default branch, then re-run with `--no-worktree`."_
|
|
127
|
-
|
|
128
|
-
> **Note:** The previous version of this skill offered stash and branch-switch workflows. Those paths have been removed in favor of always using worktrees. If you prefer to stash instead, run `git stash push -m "WIP"` manually before `/start`.
|
|
129
|
-
|
|
130
|
-
See `/worktree` skill for full worktree conventions.
|
|
131
|
-
|
|
132
|
-
**Branch name format:**
|
|
133
|
-
|
|
134
|
-
Use Linear's `gitBranchName` field if available, or generate:
|
|
135
|
-
`{identifier}/{short-description}` (e.g., `rar-123/add-feature-name`)
|
|
136
|
-
|
|
137
|
-
**Worktree naming:** `.worktrees/<identifier>` (e.g., `.worktrees/rar-123`)
|
|
138
|
-
|
|
139
|
-
### 5. Display Issue Context
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
Starting: RAR-123
|
|
143
|
-
Issue: <title>
|
|
144
|
-
URL: https://linear.app/...
|
|
145
|
-
|
|
146
|
-
Description:
|
|
147
|
-
<full description>
|
|
148
|
-
|
|
149
|
-
Acceptance Criteria:
|
|
150
|
-
- [ ] ...
|
|
151
|
-
|
|
152
|
-
Branch: <branch-name>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### 6. Create Initial Todo List
|
|
156
|
-
|
|
157
|
-
Based on the issue description, create a todo list to track progress.
|
|
158
|
-
|
|
159
|
-
## Flags Reference
|
|
160
|
-
|
|
161
|
-
| Flag | Description |
|
|
162
|
-
|------|-------------|
|
|
163
|
-
| `--mine` | List my assigned issues in Todo state |
|
|
164
|
-
| `--backlog` | List team backlog issues |
|
|
165
|
-
| `--no-worktree` | Skip worktree if on the default branch + clean; stops with error otherwise |
|
|
166
|
-
| `--no-status` | Skip status update (just create branch) |
|
|
167
|
-
| `--team <name>` | Filter by team (default: Rarebit) |
|
|
168
|
-
| `--project <name>` | Filter by project |
|
|
169
|
-
|
|
170
|
-
## Error Handling
|
|
171
|
-
|
|
172
|
-
| Error | Solution |
|
|
173
|
-
|-------|----------|
|
|
174
|
-
| Linear MCP unavailable | Warn and offer to proceed with just git setup |
|
|
175
|
-
| Issue not found | Verify identifier, check team access |
|
|
176
|
-
| Issue already in progress | Ask if user wants to continue anyway |
|
|
177
|
-
| Issue is done/canceled | Warn and suggest reopening or selecting different issue |
|
|
178
|
-
| Status update fails | Offer to continue with local setup, retry, or cancel |
|
|
179
|
-
| Branch already exists | Offer to checkout existing or create with suffix |
|
|
180
|
-
| Worktree already exists | Offer to use existing worktree or create with suffix |
|
|
181
|
-
|
|
182
|
-
## Integration with Other Skills
|
|
183
|
-
|
|
184
|
-
- After completing work, create a PR with `gh pr create`, or use `/publish-gem` when ready to release
|
|
185
|
-
- The branch naming convention ensures the Linear issue can be auto-detected from the branch
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: worktree
|
|
3
|
-
description: "Create an isolated worktree for new work. Always creates a worktree by default to prevent cross-contamination between sessions. Use at the start of any session, or when the user says 'worktree', 'isolate', 'fresh start', or 'new worktree'."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Worktree Skill
|
|
7
|
-
|
|
8
|
-
Creates an isolated worktree for new work. **Always creates a worktree by default** — this prevents changes from different work sessions bleeding into unrelated PRs.
|
|
9
|
-
|
|
10
|
-
## Why Worktree-by-Default
|
|
11
|
-
|
|
12
|
-
Every new piece of work gets its own worktree. This eliminates the most common source of cross-contamination: starting new work in a repo that has leftover state from a previous session (uncommitted changes, wrong branch, etc.). The only exception is when the user explicitly opts out.
|
|
13
|
-
|
|
14
|
-
> **Note:** The previous version of this skill offered a stash-based workflow. That path has been removed in favor of always using worktrees. If you prefer to stash instead, run `git stash push -m "WIP"` manually before invoking `/start` or `/worktree`.
|
|
15
|
-
|
|
16
|
-
## Why `.worktrees/`
|
|
17
|
-
|
|
18
|
-
Worktrees are created inside the repo at `.worktrees/<name>` (not under `.claude/worktrees/`). This ensures:
|
|
19
|
-
- Worktrees are accessible inside devcontainers (mounted at `/workspace/.worktrees/`)
|
|
20
|
-
- Consistent convention across all repos in the workspace
|
|
21
|
-
- The `.worktrees/` directory is gitignored in each repo
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
/worktree # Create a worktree (always, regardless of state)
|
|
27
|
-
/worktree <name> # Create a named worktree directly
|
|
28
|
-
/worktree --stay # Skip worktree creation, work in current checkout
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Workflow
|
|
32
|
-
|
|
33
|
-
### Phase 1: Detect Current State
|
|
34
|
-
|
|
35
|
-
Gather the repo state (for reporting, not for deciding whether to create a worktree):
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# Current branch
|
|
39
|
-
CURRENT_BRANCH=$(git branch --show-current)
|
|
40
|
-
|
|
41
|
-
# Default branch
|
|
42
|
-
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@refs/remotes/origin/@@')
|
|
43
|
-
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
|
44
|
-
|
|
45
|
-
# Uncommitted changes (staged + unstaged + untracked)
|
|
46
|
-
git status --porcelain
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Phase 2: Decision
|
|
50
|
-
|
|
51
|
-
**Default behavior:** Always create a worktree. Go straight to Phase 3 → Phase 4. Any uncommitted changes on the current branch are left untouched — the worktree is a separate checkout, so existing work is preserved exactly as-is.
|
|
52
|
-
|
|
53
|
-
**`--stay` flag:** Skip worktree creation and stay in the current checkout. This is for when the user explicitly wants to continue work on the current branch (e.g., resuming a previous session). If dirty state is detected, inform the user what's there but **do not stop** — the user is consciously choosing to stay. This differs from `/start --no-worktree`, which hard-stops on dirty state because starting new work on an unclean tree risks cross-contamination.
|
|
54
|
-
|
|
55
|
-
### Phase 3: Fetch Latest
|
|
56
|
-
|
|
57
|
-
Always fetch the latest default branch, regardless of whether a worktree will be created:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
git fetch origin "$DEFAULT_BRANCH"
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
This ensures any new branch or worktree starts from the latest codebase.
|
|
64
|
-
|
|
65
|
-
### Phase 4: Create Worktree
|
|
66
|
-
|
|
67
|
-
**Naming convention:**
|
|
68
|
-
|
|
69
|
-
If a name is provided (e.g., from a Linear issue identifier):
|
|
70
|
-
```bash
|
|
71
|
-
git worktree add .worktrees/<name> -b <branch-name> "origin/$DEFAULT_BRANCH"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
If no name is provided, generate one from context:
|
|
75
|
-
- If starting a Linear issue: use the issue identifier (e.g., `swe-123`)
|
|
76
|
-
- If the user described the task: use a short slug (e.g., `fix-auth-timeout`)
|
|
77
|
-
- Fallback: use today's date in `YYYY-MM-DD` format
|
|
78
|
-
|
|
79
|
-
**Ensure `.worktrees/` is gitignored:**
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
# Check if .worktrees is already in .gitignore
|
|
83
|
-
if ! grep -q '\.worktrees' .gitignore 2>/dev/null; then
|
|
84
|
-
echo '.worktrees/' >> .gitignore
|
|
85
|
-
fi
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Phase 5: Report
|
|
89
|
-
|
|
90
|
-
After setup, display:
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
Worktree created at: .worktrees/<name>
|
|
94
|
-
Branch: <branch-name>
|
|
95
|
-
Based on: origin/$DEFAULT_BRANCH (fetched latest)
|
|
96
|
-
|
|
97
|
-
Previous state preserved:
|
|
98
|
-
Branch: <original-branch>
|
|
99
|
-
Changes: <summary of uncommitted changes left behind, if any>
|
|
100
|
-
|
|
101
|
-
Working directory: .worktrees/<name>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
If `--stay` was used:
|
|
105
|
-
|
|
106
|
-
```
|
|
107
|
-
Staying in current checkout: <branch>
|
|
108
|
-
(fetched latest origin/$DEFAULT_BRANCH)
|
|
109
|
-
<summary of uncommitted changes, if any>
|
|
110
|
-
|
|
111
|
-
To create a new branch:
|
|
112
|
-
git checkout -b <branch-name> origin/$DEFAULT_BRANCH
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Integration with /start
|
|
116
|
-
|
|
117
|
-
The `/start` skill always creates a worktree as part of its workflow. Both skills follow the same conventions:
|
|
118
|
-
- Worktrees go in `.worktrees/`
|
|
119
|
-
- Always fetch latest before creating
|
|
120
|
-
- Naming follows the `<identifier>` convention (e.g., `.worktrees/swe-123`)
|
|
121
|
-
|
|
122
|
-
**Opt-out flags differ by context:**
|
|
123
|
-
- `/worktree --stay` — "stay in current checkout" (you invoked the worktree tool, you're opting out of its action; dirty state is allowed)
|
|
124
|
-
- `/start --no-worktree` — "don't create a worktree" (you're starting new work, skipping one aspect; dirty state causes a hard stop)
|
|
125
|
-
|
|
126
|
-
This `/worktree` skill can also be invoked independently when you want to isolate work without picking up a Linear issue.
|
|
127
|
-
|
|
128
|
-
## Error Handling
|
|
129
|
-
|
|
130
|
-
| Error | Solution |
|
|
131
|
-
|-------|----------|
|
|
132
|
-
| `.worktrees/<name>` already exists | Offer to reuse existing or create with suffix |
|
|
133
|
-
| Branch name already exists | Check out existing branch in the worktree instead of `-b` |
|
|
134
|
-
| No origin remote | Create worktree from local default branch HEAD |
|
|
135
|
-
| Fetch fails | Warn and create from local default branch HEAD |
|
|
136
|
-
| `.gitignore` is read-only | Warn the user to add `.worktrees/` manually |
|
|
137
|
-
|
|
138
|
-
## Safety Rules
|
|
139
|
-
|
|
140
|
-
1. **Never discard uncommitted changes** - the whole point is to preserve them
|
|
141
|
-
2. **Never force-checkout** over dirty state - always worktree instead
|
|
142
|
-
3. **Always fetch before creating** - worktrees should start from latest
|
|
143
|
-
4. **This skill only creates worktrees** - it does not remove or clean up stale ones
|
data/.editorconfig
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4.0.1
|
data/AGENTS.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# AGENTS.md - AI Agent Guide for standard_id-google
|
|
2
|
-
|
|
3
|
-
`standard_id-google` is a provider plugin for the [StandardId](https://github.com/rarebit-one/standard_id) authentication engine. It packages a `StandardId::Providers::Google` implementation for Sign in with Google, and auto-registers itself with the host StandardId installation via a `Rails::Railtie` so apps that bundle the gem don't need an explicit initializer.
|
|
4
|
-
|
|
5
|
-
## Quick Reference
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Run tests
|
|
9
|
-
bundle exec rspec
|
|
10
|
-
|
|
11
|
-
# Run a single spec file
|
|
12
|
-
bundle exec rspec spec/standard_id/google/providers/google_spec.rb
|
|
13
|
-
|
|
14
|
-
# Run linting (note: --config flag is required on Ruby 4.0)
|
|
15
|
-
bundle exec rubocop --config .rubocop.yml
|
|
16
|
-
|
|
17
|
-
# Auto-fix lint issues
|
|
18
|
-
bundle exec rubocop --config .rubocop.yml -A
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Project Structure
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
standard_id-google/
|
|
25
|
-
├── lib/standard_id/
|
|
26
|
-
│ ├── google.rb # Top-level require entrypoint
|
|
27
|
-
│ └── google/
|
|
28
|
-
│ ├── version.rb # Gem version constant
|
|
29
|
-
│ ├── railtie.rb # Auto-registers provider on after_initialize
|
|
30
|
-
│ └── providers/google.rb # StandardId::Providers::Google implementation
|
|
31
|
-
└── spec/
|
|
32
|
-
├── spec_helper.rb # Boots a minimal Rails app so the Railtie fires
|
|
33
|
-
└── standard_id/ # Provider specs
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Key Patterns
|
|
37
|
-
|
|
38
|
-
### Provider class
|
|
39
|
-
|
|
40
|
-
`StandardId::Providers::Google` inherits from `StandardId::Providers::Base` (defined in the parent `standard_id` gem) and implements the provider contract: `provider_name`, `authorization_url`, `get_user_info`, `config_schema`, plus Google-specific helpers (`verify_id_token`, `fetch_user_info`, code/token exchange via Google's tokeninfo + userinfo endpoints).
|
|
41
|
-
|
|
42
|
-
### Railtie auto-registration
|
|
43
|
-
|
|
44
|
-
`StandardId::Google::Railtie` runs on `config.after_initialize` and calls `StandardId::ProviderRegistry.register(:google, StandardId::Providers::Google)`. Host apps just need the gem in their Gemfile — no initializer required.
|
|
45
|
-
|
|
46
|
-
### Spec bootstrapping
|
|
47
|
-
|
|
48
|
-
`spec/spec_helper.rb` defines a tiny `Rails::Application` and calls `Rails.application.initialize!` so the Railtie's `after_initialize` hook fires during the spec run; without this the provider would not appear in the registry.
|
|
49
|
-
|
|
50
|
-
## Key Files
|
|
51
|
-
|
|
52
|
-
| File | Purpose |
|
|
53
|
-
|------|---------|
|
|
54
|
-
| `lib/standard_id/google.rb` | Top-level require entrypoint |
|
|
55
|
-
| `lib/standard_id/google/railtie.rb` | Provider registration on Rails boot |
|
|
56
|
-
| `lib/standard_id/google/providers/google.rb` | Google provider implementation |
|
|
57
|
-
| `lib/standard_id/google/version.rb` | Gem version constant |
|
|
58
|
-
| `standard_id-google.gemspec` | Gem metadata + runtime deps |
|
|
59
|
-
|
|
60
|
-
## Dependencies
|
|
61
|
-
|
|
62
|
-
- **standard_id** `~> 0.1`, `>= 0.1.7` (parent engine — provides `Providers::Base`, `ProviderRegistry`, `HttpClient`, errors)
|
|
63
|
-
- **activesupport** `>= 8.0` (`present?`/`blank?`, indifferent access)
|
|
64
|
-
|
|
65
|
-
Dev: rspec, rubocop, webmock, lefthook.
|
|
66
|
-
|
|
67
|
-
## Testing
|
|
68
|
-
|
|
69
|
-
- WebMock stubs Google's tokeninfo and userinfo endpoints — never make real network calls in specs.
|
|
70
|
-
- The dummy Rails app in `spec_helper.rb` is intentionally minimal; add config via `StandardId.config.google_*` setters in individual specs rather than expanding the dummy app.
|
|
71
|
-
- CI runs the full Ruby 4.0.x patch matrix via the shared `rarebit-one/.github` reusable workflow.
|
data/CLAUDE.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
## Worktree-Only Workflow (Enforced)
|
|
4
|
-
|
|
5
|
-
**All file modifications are blocked in the main checkout.** A PreToolUse hook (`enforce-worktree.sh`) rejects Edit, Write, and NotebookEdit operations targeting files outside a worktree. There are no opt-outs. Do not use Bash to write files in the main checkout either (e.g., `echo >`, `sed -i`, `tee`, `cp`) — the hook cannot intercept shell commands, so this rule is instruction-enforced.
|
|
6
|
-
|
|
7
|
-
Before writing any code, create a worktree:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@refs/remotes/origin/@@')
|
|
11
|
-
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}
|
|
12
|
-
git fetch origin "$DEFAULT_BRANCH"
|
|
13
|
-
git worktree add .worktrees/<name> -b <branch-name> "origin/$DEFAULT_BRANCH"
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Then work inside `.worktrees/<name>/` for the rest of the session.
|
|
17
|
-
|
|
18
|
-
**Naming:** Use the Linear issue identifier if available (e.g., `.worktrees/<identifier>`), a task slug (e.g., `.worktrees/fix-auth-timeout`), or today's date (e.g., `.worktrees/2026-04-01`) as fallback.
|
|
19
|
-
|
|
20
|
-
**The hook allows modifications only when:**
|
|
21
|
-
|
|
22
|
-
1. The file is inside a git worktree (detected via `git rev-parse --git-dir` returning a path under `.git/worktrees/`)
|
|
23
|
-
2. Running in a CI/automated context where the checkout is already isolated
|
|
24
|
-
**Why this matters:** Working directly on the main checkout causes cross-contamination between sessions — uncommitted changes, wrong branches, and dirty state leak into unrelated work. Worktrees eliminate this entirely.
|
|
25
|
-
|
|
26
|
-
See the `/worktree` and `/start` skills for full conventions and flags.
|
data/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
-
identity and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
|
18
|
-
community include:
|
|
19
|
-
|
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
-
and learning from the experience
|
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
-
community
|
|
27
|
-
|
|
28
|
-
Examples of unacceptable behavior include:
|
|
29
|
-
|
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
-
any kind
|
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
-
* Public or private harassment
|
|
34
|
-
* Publishing others' private information, such as a physical or email address,
|
|
35
|
-
without their explicit permission
|
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
-
professional setting
|
|
38
|
-
|
|
39
|
-
## Enforcement Responsibilities
|
|
40
|
-
|
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
-
or harmful.
|
|
45
|
-
|
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
-
decisions when appropriate.
|
|
50
|
-
|
|
51
|
-
## Scope
|
|
52
|
-
|
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
-
an individual is officially representing the community in public spaces.
|
|
55
|
-
Examples of representing our community include using an official email address,
|
|
56
|
-
posting via an official social media account, or acting as an appointed
|
|
57
|
-
representative at an online or offline event.
|
|
58
|
-
|
|
59
|
-
## Enforcement
|
|
60
|
-
|
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
-
reported to the community leaders responsible for enforcement at
|
|
63
|
-
[INSERT CONTACT METHOD].
|
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
-
|
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
-
reporter of any incident.
|
|
68
|
-
|
|
69
|
-
## Enforcement Guidelines
|
|
70
|
-
|
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
-
|
|
74
|
-
### 1. Correction
|
|
75
|
-
|
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
-
unprofessional or unwelcome in the community.
|
|
78
|
-
|
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
|
82
|
-
|
|
83
|
-
### 2. Warning
|
|
84
|
-
|
|
85
|
-
**Community Impact**: A violation through a single incident or series of
|
|
86
|
-
actions.
|
|
87
|
-
|
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
-
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
-
ban.
|
|
94
|
-
|
|
95
|
-
### 3. Temporary Ban
|
|
96
|
-
|
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
|
98
|
-
sustained inappropriate behavior.
|
|
99
|
-
|
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
-
communication with the community for a specified period of time. No public or
|
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
-
Violating these terms may lead to a permanent ban.
|
|
105
|
-
|
|
106
|
-
### 4. Permanent Ban
|
|
107
|
-
|
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
-
|
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
-
community.
|
|
114
|
-
|
|
115
|
-
## Attribution
|
|
116
|
-
|
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
-
version 2.1, available at
|
|
119
|
-
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
-
|
|
121
|
-
Community Impact Guidelines were inspired by
|
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
-
|
|
124
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
-
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
-
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
-
|
|
128
|
-
[homepage]: https://www.contributor-covenant.org
|
|
129
|
-
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
-
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
-
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
-
[translations]: https://www.contributor-covenant.org/translations
|