caruso 0.7.0 → 0.7.1
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/.rubocop.yml +1 -0
- data/lib/caruso/adapters/hook_adapter.rb +10 -14
- data/lib/caruso/cli.rb +3 -0
- data/lib/caruso/fetcher.rb +32 -21
- data/lib/caruso/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e27842f0328ffd84a7b184987157b7bae790284f304d190896ad566250da4de2
|
|
4
|
+
data.tar.gz: 156462c94584cd858bf0bc30fe83ad71e1796c3022088147eb03dd39e4cc0712
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c404d5dbe4128f2c63e8ab01ac39760aa1e131d73eeb80d7c4a5ca23fb47dde2d9f2f90508625d2c60d60903942e622e62bc14adc690fe5c43657acc8ca26e96
|
|
7
|
+
data.tar.gz: 530cb46037d8b3a67dfe3e8fca939d008b41d6ec580049392779ab047ada81321f4faadc17b00d2a2f77ff847f3bee92704929ec05803a15753fbfcbaf9824d7
|
data/.rubocop.yml
CHANGED
|
@@ -60,6 +60,7 @@ Metrics/ClassLength:
|
|
|
60
60
|
- 'spec/**/*'
|
|
61
61
|
- 'lib/caruso/cli.rb' # CLI class has many Thor commands
|
|
62
62
|
- 'lib/caruso/fetcher.rb' # Fetcher handles multiple sources
|
|
63
|
+
- 'lib/caruso/adapters/hook_adapter.rb' # Hook translation has many methods
|
|
63
64
|
|
|
64
65
|
# Prefer descriptive block parameter names
|
|
65
66
|
Lint/UnusedBlockArgument:
|
|
@@ -7,9 +7,7 @@ require_relative "base"
|
|
|
7
7
|
module Caruso
|
|
8
8
|
module Adapters
|
|
9
9
|
class HookAdapter < Base
|
|
10
|
-
#
|
|
11
|
-
# Each CC event maps to a single Cursor event name.
|
|
12
|
-
# Matchers are lost in translation (Cursor has no matcher concept).
|
|
10
|
+
# CC events map to Cursor events; matchers are lost (Cursor has no matcher concept).
|
|
13
11
|
EVENT_MAP = {
|
|
14
12
|
"PreToolUse" => "beforeShellExecution",
|
|
15
13
|
"PostToolUse" => "afterShellExecution",
|
|
@@ -17,11 +15,10 @@ module Caruso
|
|
|
17
15
|
"Stop" => "stop"
|
|
18
16
|
}.freeze
|
|
19
17
|
|
|
20
|
-
# PostToolUse with Write|Edit matchers
|
|
21
|
-
# We detect this via the matcher pattern.
|
|
18
|
+
# PostToolUse with Write|Edit matchers maps to afterFileEdit instead.
|
|
22
19
|
FILE_EDIT_MATCHERS = /\A(Write|Edit|Write\|Edit|Edit\|Write|Notebook.*)\z/i
|
|
23
20
|
|
|
24
|
-
# Events
|
|
21
|
+
# Events with no Cursor equivalent.
|
|
25
22
|
UNSUPPORTED_EVENTS = %w[
|
|
26
23
|
SessionStart
|
|
27
24
|
SessionEnd
|
|
@@ -31,8 +28,7 @@ module Caruso
|
|
|
31
28
|
PermissionRequest
|
|
32
29
|
].freeze
|
|
33
30
|
|
|
34
|
-
#
|
|
35
|
-
# Used by callers to track which hooks were installed for clean uninstall.
|
|
31
|
+
# Contains translated hook commands keyed by event (for clean uninstall tracking).
|
|
36
32
|
attr_reader :translated_hooks
|
|
37
33
|
|
|
38
34
|
def adapt
|
|
@@ -65,8 +61,6 @@ module Caruso
|
|
|
65
61
|
private
|
|
66
62
|
|
|
67
63
|
def find_hooks_file
|
|
68
|
-
# files array contains the hooks.json path passed in by the Dispatcher.
|
|
69
|
-
# Also matches .caruso_inline_hooks.json written by Fetcher for inline plugin.json hooks.
|
|
70
64
|
files.find { |f| File.basename(f) =~ /hooks\.json\z/ }
|
|
71
65
|
end
|
|
72
66
|
|
|
@@ -151,9 +145,7 @@ module Caruso
|
|
|
151
145
|
end
|
|
152
146
|
|
|
153
147
|
def plugin_root_from_hooks_file(hooks_file)
|
|
154
|
-
#
|
|
155
|
-
# hooks.json is typically at <plugin_root>/hooks/hooks.json,
|
|
156
|
-
# so the plugin root is the parent of the hooks/ directory.
|
|
148
|
+
# Plugin root is parent of hooks/ dir (hooks.json is at <plugin_root>/hooks/hooks.json)
|
|
157
149
|
hooks_dir = File.dirname(hooks_file)
|
|
158
150
|
if File.basename(hooks_dir) == "hooks"
|
|
159
151
|
File.dirname(hooks_dir)
|
|
@@ -174,7 +166,11 @@ module Caruso
|
|
|
174
166
|
command = hook["command"]
|
|
175
167
|
return unless command&.include?("${CLAUDE_PLUGIN_ROOT}")
|
|
176
168
|
|
|
177
|
-
|
|
169
|
+
# Extract path after placeholder (handles "python3 ${CLAUDE_PLUGIN_ROOT}/script.py")
|
|
170
|
+
match = command.match(%r{\$\{CLAUDE_PLUGIN_ROOT\}/([^\s]+)})
|
|
171
|
+
return unless match
|
|
172
|
+
|
|
173
|
+
relative_path = match[1]
|
|
178
174
|
source_path = File.join(plugin_root, relative_path)
|
|
179
175
|
return unless File.exist?(source_path)
|
|
180
176
|
|
data/lib/caruso/cli.rb
CHANGED
|
@@ -29,6 +29,9 @@ module Caruso
|
|
|
29
29
|
# Read marketplace name from marketplace.json
|
|
30
30
|
marketplace_name = fetcher.extract_marketplace_name
|
|
31
31
|
|
|
32
|
+
# Register in the persistent marketplace registry (only after name is known)
|
|
33
|
+
fetcher.register_marketplace(marketplace_name)
|
|
34
|
+
|
|
32
35
|
config_manager.add_marketplace(marketplace_name, url, source: source, ref: options[:ref])
|
|
33
36
|
|
|
34
37
|
puts "Added marketplace '#{marketplace_name}' from #{url}"
|
data/lib/caruso/fetcher.rb
CHANGED
|
@@ -72,6 +72,10 @@ module Caruso
|
|
|
72
72
|
url = source_config["url"] || source_config["repo"]
|
|
73
73
|
url = "https://github.com/#{url}.git" if source_config["source"] == "github" && !url.match?(/\Ahttps?:/)
|
|
74
74
|
|
|
75
|
+
# Store these for later registration
|
|
76
|
+
@clone_url = url
|
|
77
|
+
@clone_source_type = source_config["source"] || "git"
|
|
78
|
+
|
|
75
79
|
URI.parse(url).path.split("/").last.sub(".git", "")
|
|
76
80
|
target_path = cache_dir
|
|
77
81
|
|
|
@@ -80,10 +84,6 @@ module Caruso
|
|
|
80
84
|
FileUtils.mkdir_p(File.dirname(target_path))
|
|
81
85
|
Git.clone(url, target_path)
|
|
82
86
|
checkout_ref if @ref
|
|
83
|
-
|
|
84
|
-
# Add to registry
|
|
85
|
-
source_type = source_config["source"] || "git"
|
|
86
|
-
@registry.add_marketplace(@marketplace_name, url, target_path, ref: @ref, source: source_type)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
target_path
|
|
@@ -92,6 +92,15 @@ module Caruso
|
|
|
92
92
|
nil
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
# Register the marketplace in the registry after name is known.
|
|
96
|
+
# Must be called after extract_marketplace_name or when marketplace_name is set.
|
|
97
|
+
def register_marketplace(name)
|
|
98
|
+
return unless @clone_url # Only register if we cloned something
|
|
99
|
+
|
|
100
|
+
@marketplace_name = name
|
|
101
|
+
@registry.add_marketplace(name, @clone_url, cache_dir, ref: @ref, source: @clone_source_type)
|
|
102
|
+
end
|
|
103
|
+
|
|
95
104
|
def extract_marketplace_name
|
|
96
105
|
marketplace_data = load_marketplace
|
|
97
106
|
name = marketplace_data["name"]
|
|
@@ -106,7 +115,25 @@ module Caruso
|
|
|
106
115
|
private
|
|
107
116
|
|
|
108
117
|
def load_marketplace
|
|
109
|
-
|
|
118
|
+
# Check github_repo? BEFORE local_path? because owner/repo format (e.g., "anthropics/claude-code")
|
|
119
|
+
# doesn't start with https:// but should be treated as a GitHub repo, not a local path
|
|
120
|
+
if github_repo?
|
|
121
|
+
# Clone repo and read marketplace.json from it
|
|
122
|
+
repo_path = clone_git_repo("url" => @marketplace_uri, "source" => "github")
|
|
123
|
+
# Try standard locations
|
|
124
|
+
json_path = File.join(repo_path, ".claude-plugin", "marketplace.json")
|
|
125
|
+
json_path = File.join(repo_path, "marketplace.json") unless File.exist?(json_path)
|
|
126
|
+
|
|
127
|
+
unless File.exist?(json_path)
|
|
128
|
+
raise "Could not find marketplace.json in #{@marketplace_uri}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Update marketplace_uri to point to the local file so relative paths work
|
|
132
|
+
@marketplace_uri = json_path
|
|
133
|
+
@base_dir = repo_path # Base dir is the repo root, regardless of where json is
|
|
134
|
+
|
|
135
|
+
JSON.parse(SafeFile.read(json_path))
|
|
136
|
+
elsif local_path?
|
|
110
137
|
# If marketplace_uri is a directory, find marketplace.json in it
|
|
111
138
|
if SafeDir.exist?(@marketplace_uri)
|
|
112
139
|
json_path = File.join(@marketplace_uri, ".claude-plugin", "marketplace.json")
|
|
@@ -126,22 +153,6 @@ module Caruso
|
|
|
126
153
|
end
|
|
127
154
|
|
|
128
155
|
JSON.parse(SafeFile.read(@marketplace_uri))
|
|
129
|
-
elsif github_repo?
|
|
130
|
-
# Clone repo and read marketplace.json from it
|
|
131
|
-
repo_path = clone_git_repo("url" => @marketplace_uri, "source" => "github")
|
|
132
|
-
# Try standard locations
|
|
133
|
-
json_path = File.join(repo_path, ".claude-plugin", "marketplace.json")
|
|
134
|
-
json_path = File.join(repo_path, "marketplace.json") unless File.exist?(json_path)
|
|
135
|
-
|
|
136
|
-
unless File.exist?(json_path)
|
|
137
|
-
raise "Could not find marketplace.json in #{@marketplace_uri}"
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Update marketplace_uri to point to the local file so relative paths work
|
|
141
|
-
@marketplace_uri = json_path
|
|
142
|
-
@base_dir = repo_path # Base dir is the repo root, regardless of where json is
|
|
143
|
-
|
|
144
|
-
JSON.parse(SafeFile.read(json_path))
|
|
145
156
|
else
|
|
146
157
|
response = Faraday.get(@marketplace_uri)
|
|
147
158
|
JSON.parse(response.body)
|
data/lib/caruso/version.rb
CHANGED