rails-informant 0.5.3 → 0.6.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/README.md +15 -0
- data/Rakefile +1 -0
- data/lib/generators/rails_informant/skill_generator.rb +10 -2
- data/lib/rails_informant/claude_integration_content.rb +10 -1
- data/lib/rails_informant/doctor.rb +8 -0
- data/lib/rails_informant/integration.rb +32 -2
- data/{lib/generators/rails_informant/skill/templates → skills/informant}/SKILL.md +2 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 905f71f9e86e6c982710d2c1dbd8471218798e1cfa80b8baca78124180a1fef3
|
|
4
|
+
data.tar.gz: 9cc699035012351e2bfe4b0423c2b7fe0bffd7880f05250d2d54f758bf2d50ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b103c2f10d32404883a51e7d9ef82daf0dda368bd7ec50165d32f88b9b9d53924aeb6264af9c4e7f10468d6abe891228a62e7e09c2584a74a0d469a619cf9dd5
|
|
7
|
+
data.tar.gz: fad95e73c07500480d703c2018e5eeeef77656f0cd16aced9b8e778d90c700a450b1f2963c54f093b76fc2b501a013148b2afb18bef81b8eeed399db1bd5d96c
|
data/README.md
CHANGED
|
@@ -63,6 +63,8 @@ Install Claude Code integration:
|
|
|
63
63
|
bin/rails generate rails_informant:skill
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
With [Rails Hyperdrive](https://github.com/rails-hyperdrive/rails-hyperdrive), the `/informant` skill arrives through `bin/rails hyperdrive:sync` instead, and the generator installs the rest (see [With Rails Hyperdrive](#with-rails-hyperdrive)).
|
|
67
|
+
|
|
66
68
|
Errors are captured automatically. To capture manually:
|
|
67
69
|
|
|
68
70
|
```ruby
|
|
@@ -222,6 +224,19 @@ bin/rails informant:doctor # reports current / stale / not installed; exits no
|
|
|
222
224
|
|
|
223
225
|
Apps that use the gem only for error capture (no Claude Code integration) are never affected.
|
|
224
226
|
|
|
227
|
+
#### With Rails Hyperdrive
|
|
228
|
+
|
|
229
|
+
rails-informant is a Hyperdrive companion gem. Once an app has `.hyperdrive/config.yml`, Hyperdrive owns the skill (`.claude/skills/informant/SKILL.md`) and updates it on `bin/rails hyperdrive:sync`, keeping your local edits safe. The generator still owns the hook script, its `.claude/settings.json` entry and `.mcp.json`, and no longer writes the skill. The drift checks ignore the skill in these apps.
|
|
230
|
+
|
|
231
|
+
If the generator installed the skill before Hyperdrive arrived, hand it over once:
|
|
232
|
+
|
|
233
|
+
```sh
|
|
234
|
+
rm -f .claude/skills/informant/SKILL.md .claude/skills/informant/SKILL.md.new
|
|
235
|
+
bin/rails hyperdrive:sync
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Hyperdrive treats a file it did not install as a local edit and never overwrites it, so the old copy has to go first. Avoid `hyperdrive:sync --overwrite` for this: it rewrites every locally edited Hyperdrive file, from every companion gem. `bin/rails informant:doctor` prints these steps while the handover is pending.
|
|
239
|
+
|
|
225
240
|
### Tools
|
|
226
241
|
|
|
227
242
|
| Tool | Description |
|
data/Rakefile
CHANGED
|
@@ -9,7 +9,9 @@ module RailsInformant
|
|
|
9
9
|
source_root File.expand_path("skill/templates", __dir__)
|
|
10
10
|
|
|
11
11
|
def copy_skill_file
|
|
12
|
-
|
|
12
|
+
return if hyperdrive_managed?
|
|
13
|
+
|
|
14
|
+
create_file Content::SKILL_PATH, Content.skill_markdown
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def copy_hook_script
|
|
@@ -63,7 +65,11 @@ module RailsInformant
|
|
|
63
65
|
say "Claude Code integration installed!", :green
|
|
64
66
|
say ""
|
|
65
67
|
say " Created .mcp.json"
|
|
66
|
-
|
|
68
|
+
if hyperdrive_managed?
|
|
69
|
+
say " Skipped #{Content::SKILL_PATH} (Rails Hyperdrive installs it: bin/rails hyperdrive:sync)"
|
|
70
|
+
else
|
|
71
|
+
say " Created #{Content::SKILL_PATH}"
|
|
72
|
+
end
|
|
67
73
|
say " Created .claude/hooks/informant-alerts.sh"
|
|
68
74
|
say " Created .claude/settings.json (UserPromptSubmit hook)"
|
|
69
75
|
say ""
|
|
@@ -81,6 +87,8 @@ module RailsInformant
|
|
|
81
87
|
|
|
82
88
|
private
|
|
83
89
|
|
|
90
|
+
def hyperdrive_managed? = Content.hyperdrive_managed?(destination_root)
|
|
91
|
+
|
|
84
92
|
# Remove every prior informant registration (matched by script path) from all
|
|
85
93
|
# event keys and drop keys left empty, so a re-run migrates a stale
|
|
86
94
|
# registration (e.g. a leftover SessionStart from an earlier gem version)
|
|
@@ -14,6 +14,13 @@ module RailsInformant
|
|
|
14
14
|
SETTINGS_PATH = ".claude/settings.json"
|
|
15
15
|
MCP_PATH = ".mcp.json"
|
|
16
16
|
|
|
17
|
+
# `hyperdrive:init` writes this before the first sync, so the skill is left
|
|
18
|
+
# to Hyperdrive from the start. Keying on the lock instead would let the
|
|
19
|
+
# generator write the skill in that window, and Hyperdrive treats an
|
|
20
|
+
# unlocked file as a local edit it never overwrites.
|
|
21
|
+
HYPERDRIVE_CONFIG_PATH = ".hyperdrive/config.yml"
|
|
22
|
+
HYPERDRIVE_LOCK_PATH = ".hyperdrive/lock.yml"
|
|
23
|
+
|
|
17
24
|
# The command string a settings.json hook entry uses to invoke the script.
|
|
18
25
|
# Absolute via Claude Code's $CLAUDE_PROJECT_DIR because the hook's cwd is
|
|
19
26
|
# not guaranteed to be the app root: a bare relative path fails on every
|
|
@@ -48,9 +55,11 @@ module RailsInformant
|
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
def skill_markdown
|
|
51
|
-
File.read File.join(
|
|
58
|
+
File.read File.join(gem_dir, "skills", "informant", "SKILL.md")
|
|
52
59
|
end
|
|
53
60
|
|
|
61
|
+
def hyperdrive_managed?(app_root) = File.exist?(File.join(app_root, HYPERDRIVE_CONFIG_PATH))
|
|
62
|
+
|
|
54
63
|
# The informant server entry inside .mcp.json's "mcpServers".
|
|
55
64
|
def mcp_entry
|
|
56
65
|
{ "command" => "informant-mcp" }
|
|
@@ -6,6 +6,13 @@ module RailsInformant
|
|
|
6
6
|
# or error) so it doubles as a CI signal. A thin wrapper over Integration that
|
|
7
7
|
# never raises out — a diagnostic must not crash the run it reports on.
|
|
8
8
|
class Doctor
|
|
9
|
+
HYPERDRIVE_ADOPTION = <<~REPORT.chomp
|
|
10
|
+
[Informant] Rails Hyperdrive manages the informant skill, but #{ClaudeIntegrationContent::SKILL_PATH} is not a Hyperdrive install.
|
|
11
|
+
Hand it over (not --overwrite, which rewrites every edited Hyperdrive file):
|
|
12
|
+
rm -f #{ClaudeIntegrationContent::SKILL_PATH} #{ClaudeIntegrationContent::SKILL_PATH}.new
|
|
13
|
+
bin/rails hyperdrive:sync
|
|
14
|
+
REPORT
|
|
15
|
+
|
|
9
16
|
def initialize(integration: Integration.new, io: $stdout)
|
|
10
17
|
@integration = integration
|
|
11
18
|
@io = io
|
|
@@ -15,6 +22,7 @@ module RailsInformant
|
|
|
15
22
|
status = @integration.status
|
|
16
23
|
@integration.write_drift_flag stale: status == :stale
|
|
17
24
|
@io.puts report_for(status)
|
|
25
|
+
@io.puts HYPERDRIVE_ADOPTION if @integration.skill_awaiting_hyperdrive?
|
|
18
26
|
exit_code_for status
|
|
19
27
|
rescue StandardError => e
|
|
20
28
|
# An unexpected failure must exit nonzero, not 0 — a doctor wired into CI
|
|
@@ -2,6 +2,7 @@ require "digest"
|
|
|
2
2
|
require "fileutils"
|
|
3
3
|
require "json"
|
|
4
4
|
require "pathname"
|
|
5
|
+
require "yaml"
|
|
5
6
|
require "rails_informant/claude_integration_content"
|
|
6
7
|
|
|
7
8
|
module RailsInformant
|
|
@@ -24,6 +25,7 @@ module RailsInformant
|
|
|
24
25
|
|
|
25
26
|
def initialize(app_root: Rails.root)
|
|
26
27
|
@app_root = Pathname(app_root)
|
|
28
|
+
@hyperdrive_managed = Content.hyperdrive_managed?(@app_root)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
# :not_installed | :current | :stale | :error
|
|
@@ -47,6 +49,17 @@ module RailsInformant
|
|
|
47
49
|
status == :stale
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
# Never true for a skill Hyperdrive already owns: a lock-SHA mismatch is a
|
|
53
|
+
# local edit, and a sidecar with a recorded ancestor is Hyperdrive's own
|
|
54
|
+
# upgrade of that edit (hyperdrive:sync --resolve). The handover would
|
|
55
|
+
# discard the edit in both cases.
|
|
56
|
+
def skill_awaiting_hyperdrive?
|
|
57
|
+
return false unless @hyperdrive_managed && skill_path.exist?
|
|
58
|
+
|
|
59
|
+
entry = informant_skill_entry or return true
|
|
60
|
+
sidecar_path.exist? && entry["ancestor_sha"].nil?
|
|
61
|
+
end
|
|
62
|
+
|
|
50
63
|
# Display only — the drift decision uses the digest, not the version. Harmless
|
|
51
64
|
# that local path/git installs report 0.0.0.dev here; only published installs
|
|
52
65
|
# show a real number.
|
|
@@ -79,6 +92,8 @@ module RailsInformant
|
|
|
79
92
|
def skill_path = @app_root.join(Content::SKILL_PATH)
|
|
80
93
|
def settings_path = @app_root.join(Content::SETTINGS_PATH)
|
|
81
94
|
def mcp_path = @app_root.join(Content::MCP_PATH)
|
|
95
|
+
def sidecar_path = @app_root.join("#{Content::SKILL_PATH}.new")
|
|
96
|
+
def lock_path = @app_root.join(Content::HYPERDRIVE_LOCK_PATH)
|
|
82
97
|
|
|
83
98
|
def hook_script_present? = hook_path.exist?
|
|
84
99
|
|
|
@@ -100,7 +115,7 @@ module RailsInformant
|
|
|
100
115
|
def expected_digest
|
|
101
116
|
digest_of(
|
|
102
117
|
"hook" => normalize_text(Content.hook_script),
|
|
103
|
-
"skill" => normalize_text(Content.skill_markdown),
|
|
118
|
+
"skill" => @hyperdrive_managed ? "" : normalize_text(Content.skill_markdown),
|
|
104
119
|
"settings" => canonical_json(Content.expected_registrations),
|
|
105
120
|
"mcp" => canonical_json(Content.mcp_entry)
|
|
106
121
|
)
|
|
@@ -109,7 +124,7 @@ module RailsInformant
|
|
|
109
124
|
def live_digest
|
|
110
125
|
digest_of(
|
|
111
126
|
"hook" => normalize_text(read_or_empty(hook_path)),
|
|
112
|
-
"skill" => normalize_text(read_or_empty(skill_path)),
|
|
127
|
+
"skill" => @hyperdrive_managed ? "" : normalize_text(read_or_empty(skill_path)),
|
|
113
128
|
"settings" => canonical_json(live_settings_registrations || {}),
|
|
114
129
|
"mcp" => canonical_json(live_mcp_entry || {})
|
|
115
130
|
)
|
|
@@ -143,6 +158,21 @@ module RailsInformant
|
|
|
143
158
|
mcp.dig "mcpServers", "informant"
|
|
144
159
|
end
|
|
145
160
|
|
|
161
|
+
def informant_skill_entry
|
|
162
|
+
Array(lock_files).find do |entry|
|
|
163
|
+
entry["artifact"] == "skill" && entry["source"].to_s.start_with?("rails-informant@")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def lock_files
|
|
168
|
+
return unless lock_path.exist?
|
|
169
|
+
|
|
170
|
+
lock = YAML.safe_load(lock_path.read)
|
|
171
|
+
lock["files"] if lock.is_a?(Hash)
|
|
172
|
+
rescue Psych::Exception
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
146
176
|
# --- json / text helpers ------------------------------------------------
|
|
147
177
|
|
|
148
178
|
# Read and parse each JSON file at most once per instance. Instances are
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-informant
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel López Prat
|
|
@@ -127,7 +127,6 @@ files:
|
|
|
127
127
|
- db/migrate/20260227000000_create_informant_tables.rb
|
|
128
128
|
- exe/informant-mcp
|
|
129
129
|
- lib/generators/rails_informant/install_generator.rb
|
|
130
|
-
- lib/generators/rails_informant/skill/templates/SKILL.md
|
|
131
130
|
- lib/generators/rails_informant/skill/templates/informant-alerts.sh
|
|
132
131
|
- lib/generators/rails_informant/skill_generator.rb
|
|
133
132
|
- lib/generators/rails_informant/templates/create_informant_tables.rb.erb
|
|
@@ -176,12 +175,14 @@ files:
|
|
|
176
175
|
- lib/rails_informant/structured_event_subscriber.rb
|
|
177
176
|
- lib/rails_informant/version.rb
|
|
178
177
|
- lib/tasks/rails_informant.rake
|
|
178
|
+
- skills/informant/SKILL.md
|
|
179
179
|
homepage: https://github.com/6temes/rails-informant
|
|
180
180
|
licenses:
|
|
181
181
|
- MIT
|
|
182
182
|
metadata:
|
|
183
183
|
bug_tracker_uri: https://github.com/6temes/rails-informant/issues
|
|
184
184
|
changelog_uri: https://github.com/6temes/rails-informant/releases
|
|
185
|
+
hyperdrive_targets: rails-informant
|
|
185
186
|
rubygems_mfa_required: 'true'
|
|
186
187
|
source_code_uri: https://github.com/6temes/rails-informant
|
|
187
188
|
rdoc_options: []
|
|
@@ -198,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
198
199
|
- !ruby/object:Gem::Version
|
|
199
200
|
version: '0'
|
|
200
201
|
requirements: []
|
|
201
|
-
rubygems_version: 4.0.
|
|
202
|
+
rubygems_version: 4.0.20
|
|
202
203
|
specification_version: 4
|
|
203
204
|
summary: Self-hosted error monitoring for Rails with MCP server for agentic workflows
|
|
204
205
|
test_files: []
|