caruso 0.5.4 → 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/.rubocop.yml +6 -1
- data/AGENTS.md +276 -0
- data/CHANGELOG.md +13 -0
- data/CLAUDE.md +1 -0
- data/README.md +13 -10
- data/SECURITY.md +98 -0
- data/caruso.gemspec +1 -1
- data/lib/caruso/adapter.rb +4 -1
- data/lib/caruso/cli.rb +32 -35
- data/lib/caruso/config_manager.rb +2 -0
- data/lib/caruso/fetcher.rb +51 -14
- data/lib/caruso/marketplace_registry.rb +3 -3
- data/lib/caruso/path_sanitizer.rb +59 -0
- data/lib/caruso/safe_dir.rb +54 -0
- data/lib/caruso/safe_file.rb +39 -0
- data/lib/caruso/version.rb +1 -1
- data/lib/caruso.rb +2 -0
- metadata +7 -2
- data/CLAUDE.md +0 -276
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 718b4ebf40d78b4314d5ba6869ccf65d4e7ba02ada56c0c9b98e5de973d538c7
|
|
4
|
+
data.tar.gz: c6fcc0b7f7e45b6cd5e9235e954ea0c1cdc5d49fd729f0c6268e135ef7ac6bce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76693951a7604897ad48efaa2e81570165e483ab20e651ff757d009a4c179f21a7738f9912229dd768205736ee076fbf2f865e1b7549385ce4c320132674c08a
|
|
7
|
+
data.tar.gz: 691886bb54d63379e80df0663b0825f89b2dd7ece512fc2e859b90c7e09cc830c63c3f6c2cad4fe2dc6f83974eec03d728cb1c150240cf424821cb6948d2b7f4
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 3.
|
|
2
|
+
TargetRubyVersion: 3.2
|
|
3
3
|
NewCops: enable
|
|
4
4
|
SuggestExtensions: false
|
|
5
5
|
Exclude:
|
|
@@ -31,6 +31,7 @@ Metrics/MethodLength:
|
|
|
31
31
|
Exclude:
|
|
32
32
|
- 'spec/**/*'
|
|
33
33
|
- 'lib/caruso/cli.rb' # CLI command methods can be longer
|
|
34
|
+
- 'lib/caruso/fetcher.rb' # Marketplace loading is complex
|
|
34
35
|
|
|
35
36
|
# Line length - modern screens can handle more
|
|
36
37
|
Layout/LineLength:
|
|
@@ -38,6 +39,7 @@ Layout/LineLength:
|
|
|
38
39
|
Exclude:
|
|
39
40
|
- 'spec/**/*'
|
|
40
41
|
- '*.gemspec'
|
|
42
|
+
- 'lib/caruso/cli.rb' # CLI methods can have longer conditional lines
|
|
41
43
|
|
|
42
44
|
# Allow longer parameter lists for complex methods
|
|
43
45
|
Metrics/ParameterLists:
|
|
@@ -56,6 +58,8 @@ Metrics/ClassLength:
|
|
|
56
58
|
Max: 150
|
|
57
59
|
Exclude:
|
|
58
60
|
- 'spec/**/*'
|
|
61
|
+
- 'lib/caruso/cli.rb' # CLI class has many Thor commands
|
|
62
|
+
- 'lib/caruso/fetcher.rb' # Fetcher handles multiple sources
|
|
59
63
|
|
|
60
64
|
# Prefer descriptive block parameter names
|
|
61
65
|
Lint/UnusedBlockArgument:
|
|
@@ -140,6 +144,7 @@ Metrics/PerceivedComplexity:
|
|
|
140
144
|
Max: 12
|
|
141
145
|
Exclude:
|
|
142
146
|
- 'lib/caruso/cli.rb'
|
|
147
|
+
- 'lib/caruso/fetcher.rb' # Security validation requires complex checks
|
|
143
148
|
|
|
144
149
|
# Duplicate branches are acceptable for error handling with different contexts
|
|
145
150
|
Lint/DuplicateBranch:
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Caruso is a Ruby gem CLI that bridges the gap between AI coding assistants. It fetches "steering documentation" (commands, agents, skills) from Claude Code Marketplaces and converts them to formats compatible with other IDEs, currently Cursor.
|
|
8
|
+
|
|
9
|
+
## Source of Truth: Claude Code Documentation
|
|
10
|
+
|
|
11
|
+
**IMPORTANT:** The official Claude Code marketplace and plugin specifications are located in `/Users/philipp/code/caruso/reference/`:
|
|
12
|
+
|
|
13
|
+
- `marketplace.md` - Marketplace structure and specification
|
|
14
|
+
- `plugins.md` - Plugin format and configuration
|
|
15
|
+
- `plugins_reference.md` - Component configuration fields and metadata
|
|
16
|
+
|
|
17
|
+
**These reference documents are the authoritative source for:**
|
|
18
|
+
- Marketplace.json schema and plugin metadata format
|
|
19
|
+
- Component configuration fields (`commands`, `agents`, `skills`, `hooks`, `mcpServers`)
|
|
20
|
+
- Expected directory structures and file patterns
|
|
21
|
+
- Metadata requirements and validation rules
|
|
22
|
+
|
|
23
|
+
When implementing features or fixing bugs related to marketplace compatibility, **always consult these reference files first**. If the implementation conflicts with the reference docs, the reference docs are correct and the code should be updated to match.
|
|
24
|
+
|
|
25
|
+
## Development Commands
|
|
26
|
+
|
|
27
|
+
### Build and Install
|
|
28
|
+
```bash
|
|
29
|
+
# Build the gem
|
|
30
|
+
gem build caruso.gemspec
|
|
31
|
+
|
|
32
|
+
# Install locally
|
|
33
|
+
gem install caruso-*.gem
|
|
34
|
+
|
|
35
|
+
# Verify installation
|
|
36
|
+
caruso version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Testing
|
|
40
|
+
```bash
|
|
41
|
+
# Run offline tests only (default)
|
|
42
|
+
bundle exec rake spec
|
|
43
|
+
# or
|
|
44
|
+
bundle exec rspec
|
|
45
|
+
|
|
46
|
+
# Run all tests including live marketplace integration
|
|
47
|
+
bundle exec rake spec:all
|
|
48
|
+
# or
|
|
49
|
+
RUN_LIVE_TESTS=1 bundle exec rspec
|
|
50
|
+
|
|
51
|
+
# Run only live tests
|
|
52
|
+
bundle exec rake spec:live
|
|
53
|
+
|
|
54
|
+
# Run specific test file
|
|
55
|
+
bundle exec rspec spec/integration/plugin_spec.rb
|
|
56
|
+
|
|
57
|
+
# Run specific test
|
|
58
|
+
bundle exec rspec spec/integration/plugin_spec.rb:42
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Important:** Live tests (tagged with `:live`) interact with real marketplaces (anthropics/skills) and require network access. They can be slow (~7 minutes). Marketplace cache is stored in `~/.caruso/marketplaces/`. Integration tests set `CARUSO_TESTING_SKIP_CLONE=true` to skip Git cloning for fast offline testing.
|
|
62
|
+
|
|
63
|
+
### Linting
|
|
64
|
+
```bash
|
|
65
|
+
bundle exec rubocop
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Version Management
|
|
69
|
+
```bash
|
|
70
|
+
# Bump patch version (0.1.3 → 0.1.4)
|
|
71
|
+
bundle exec rake bump:patch
|
|
72
|
+
|
|
73
|
+
# Bump minor version (0.1.4 → 0.2.0)
|
|
74
|
+
bundle exec rake bump:minor
|
|
75
|
+
|
|
76
|
+
# Bump major version (0.1.4 → 1.0.0)
|
|
77
|
+
bundle exec rake bump:major
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Architecture
|
|
81
|
+
|
|
82
|
+
### Core Pipeline: Fetch → Adapt → Track
|
|
83
|
+
|
|
84
|
+
Caruso follows a three-stage pipeline for plugin management:
|
|
85
|
+
|
|
86
|
+
1. **Fetch** (`Fetcher`) - Clones Git repositories, resolves marketplace.json, finds plugin markdown files
|
|
87
|
+
2. **Adapt** (`Adapter`) - Converts Claude Code markdown to target IDE format with metadata injection
|
|
88
|
+
3. **Track** (`ConfigManager`) - Records installations in `caruso.json` and `.caruso.local.json`
|
|
89
|
+
|
|
90
|
+
### Key Components
|
|
91
|
+
|
|
92
|
+
#### ConfigManager (`lib/caruso/config_manager.rb`)
|
|
93
|
+
Manages configuration and state. Splits data between:
|
|
94
|
+
|
|
95
|
+
**1. Project Config (`caruso.json`)**
|
|
96
|
+
- `ide`: Target IDE (currently only "cursor" supported)
|
|
97
|
+
- `target_dir`: Where to write converted files (`.cursor/rules` for Cursor)
|
|
98
|
+
- `marketplaces`: Name → URL mapping
|
|
99
|
+
- `plugins`: Name → metadata (marketplace source)
|
|
100
|
+
- `version`: Config schema version
|
|
101
|
+
|
|
102
|
+
**2. Local Config (`.caruso.local.json`)**
|
|
103
|
+
- `installed_files`: Plugin Name → Array of file paths
|
|
104
|
+
- `initialized_at`: Timestamp
|
|
105
|
+
|
|
106
|
+
Must run `caruso init --ide=cursor` before other commands. ConfigManager handles loading/saving both files and ensures `.caruso.local.json` is gitignored.
|
|
107
|
+
|
|
108
|
+
#### MarketplaceRegistry (`lib/caruso/marketplace_registry.rb`)
|
|
109
|
+
Manages persistent marketplace metadata registry at `~/.caruso/known_marketplaces.json`. Contains:
|
|
110
|
+
- `source`: Marketplace type (git, github, url, local, directory)
|
|
111
|
+
- `url`: Original marketplace URL
|
|
112
|
+
- `install_location`: Local cache path (e.g., `~/.caruso/marketplaces/skills/`)
|
|
113
|
+
- `last_updated`: ISO8601 timestamp of last update
|
|
114
|
+
- `ref`: Optional Git ref/branch/tag for pinning
|
|
115
|
+
|
|
116
|
+
**Key features:**
|
|
117
|
+
- **Schema validation**: Validates required fields and timestamp format on load
|
|
118
|
+
- **Corruption handling**: Backs up corrupted registry to `.corrupted.<timestamp>` and continues with empty registry
|
|
119
|
+
- **Timestamp tracking**: Updates `last_updated` when marketplace cache is refreshed
|
|
120
|
+
- **Source type tracking**: Enables future support for multiple marketplace sources
|
|
121
|
+
|
|
122
|
+
This registry enables persistent tracking of marketplace state across reboots, unlike the previous `/tmp` approach.
|
|
123
|
+
|
|
124
|
+
#### Fetcher (`lib/caruso/fetcher.rb`)
|
|
125
|
+
Resolves and fetches plugins from marketplaces. Supports:
|
|
126
|
+
- GitHub repos: `https://github.com/owner/repo`
|
|
127
|
+
- Git URLs: Any Git-cloneable URL
|
|
128
|
+
- Local paths: `./path/to/marketplace` or `./path/to/marketplace.json`
|
|
129
|
+
|
|
130
|
+
**Key behavior:**
|
|
131
|
+
- Clones Git repos to `~/.caruso/marketplaces/<marketplace-name>/` (persistent across reboots)
|
|
132
|
+
- Registers marketplace metadata in `~/.caruso/known_marketplaces.json` (via MarketplaceRegistry)
|
|
133
|
+
- Supports Git ref/branch pinning for version control
|
|
134
|
+
- Reads `marketplace.json` to find available plugins
|
|
135
|
+
- Supports custom component paths: plugins can specify `commands`, `agents`, `skills` arrays pointing to non-standard locations
|
|
136
|
+
- Scans standard directories: `{commands,agents,skills}/**/*.md`
|
|
137
|
+
- Excludes README.md and LICENSE.md files
|
|
138
|
+
- **Custom paths supplement (not replace) default directories** - this is critical
|
|
139
|
+
- Detects SSH authentication errors and provides helpful error messages
|
|
140
|
+
|
|
141
|
+
**marketplace.json structure:**
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"plugins": [
|
|
145
|
+
{
|
|
146
|
+
"name": "document-skills",
|
|
147
|
+
"description": "Work with documents",
|
|
148
|
+
"source": "./document-skills",
|
|
149
|
+
"skills": ["./document-skills/xlsx", "./document-skills/pdf"]
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The `commands`, `agents`, and `skills` fields accept:
|
|
156
|
+
- String: `"./custom/path"`
|
|
157
|
+
- Array: `["./path1", "./path2"]`
|
|
158
|
+
|
|
159
|
+
Both files and directories are supported. Fetcher recursively finds all `.md` files.
|
|
160
|
+
|
|
161
|
+
#### Adapter (`lib/caruso/adapter.rb`)
|
|
162
|
+
Converts Claude Code markdown files to target IDE format. For Cursor:
|
|
163
|
+
- Renames `.md` → `.mdc`
|
|
164
|
+
- Injects YAML frontmatter with required Cursor metadata:
|
|
165
|
+
- `globs: []` - Enables semantic search (Apply Intelligently)
|
|
166
|
+
- `alwaysApply: false` - Prevents auto-application to every chat
|
|
167
|
+
- `description` - Preserved from original or generated
|
|
168
|
+
- Preserves existing frontmatter if present, adds missing fields
|
|
169
|
+
- Handles special case: `SKILL.md` → named after parent directory to avoid collisions
|
|
170
|
+
|
|
171
|
+
Returns array of created filenames (not full paths) for manifest tracking.
|
|
172
|
+
|
|
173
|
+
#### CLI (`lib/caruso/cli.rb`)
|
|
174
|
+
Thor-based CLI with nested commands:
|
|
175
|
+
- `caruso init [PATH] --ide=cursor`
|
|
176
|
+
- `caruso marketplace add URL [--ref=BRANCH]` - Add marketplace with optional Git ref pinning (name comes from marketplace.json)
|
|
177
|
+
- `caruso marketplace list` - List configured marketplaces
|
|
178
|
+
- `caruso marketplace remove NAME` - Remove marketplace from manifest and registry
|
|
179
|
+
- `caruso marketplace update [NAME]` - Update marketplace cache (all if no name given)
|
|
180
|
+
- `caruso marketplace info NAME` - Show detailed marketplace information from registry
|
|
181
|
+
- `caruso plugin install|uninstall|list|update|outdated`
|
|
182
|
+
|
|
183
|
+
**Important patterns:**
|
|
184
|
+
- All commands except `init` require existing `caruso.json` (enforced by `load_config` helper)
|
|
185
|
+
- Plugin install format: `plugin@marketplace` or just `plugin` (if only one marketplace configured)
|
|
186
|
+
- Update commands refresh marketplace cache (git pull) before fetching latest plugin files
|
|
187
|
+
- Marketplace add eagerly clones repos unless `CARUSO_TESTING_SKIP_CLONE` env var is set (used in tests)
|
|
188
|
+
- **Marketplace names always come from marketplace.json `name` field (required)** - no custom names allowed
|
|
189
|
+
- Errors use descriptive messages with suggestions (e.g., "use 'caruso marketplace add <url>'")
|
|
190
|
+
|
|
191
|
+
### Data Flow Example
|
|
192
|
+
|
|
193
|
+
User runs: `caruso plugin install document-skills@skills`
|
|
194
|
+
|
|
195
|
+
1. **CLI** parses command, loads config from `caruso.json`
|
|
196
|
+
2. **ConfigManager** looks up marketplace "skills" URL
|
|
197
|
+
3. **Fetcher** clones/updates marketplace repo to `~/.caruso/marketplaces/skills/`
|
|
198
|
+
4. **Fetcher** registers/updates marketplace metadata in MarketplaceRegistry
|
|
199
|
+
5. **Fetcher** reads `marketplace.json`, finds document-skills plugin
|
|
200
|
+
6. **Fetcher** scans standard directories + custom paths from `skills: [...]` array
|
|
201
|
+
7. **Fetcher** returns list of `.md` file paths
|
|
202
|
+
8. **Adapter** converts each file: adds frontmatter, renames to `.mdc`, writes to `.cursor/rules/caruso/`
|
|
203
|
+
9. **Adapter** returns created filenames
|
|
204
|
+
10. **ConfigManager** records plugin in `caruso.json` and files in `.caruso.local.json`
|
|
205
|
+
11. **CLI** prints success message
|
|
206
|
+
|
|
207
|
+
### Testing Architecture
|
|
208
|
+
|
|
209
|
+
Uses **Aruba** for CLI integration testing. Test structure:
|
|
210
|
+
- `spec/unit/` - Direct class testing (ConfigManager, Fetcher logic)
|
|
211
|
+
- `spec/integration/` - Full CLI workflow tests via Aruba subprocess execution
|
|
212
|
+
|
|
213
|
+
**Aruba helpers in spec_helper.rb:**
|
|
214
|
+
- `init_caruso(ide: "cursor")` - Runs init command with success assertion
|
|
215
|
+
- `add_marketplace(url, name)` - Adds marketplace with success assertion
|
|
216
|
+
- `config_file`, `manifest_file`, `load_config`, `load_manifest` - File access helpers
|
|
217
|
+
- `mdc_files` - Glob for `.cursor/rules/*.mdc` files
|
|
218
|
+
|
|
219
|
+
**Critical testing pattern:**
|
|
220
|
+
```ruby
|
|
221
|
+
run_command("caruso plugin install foo@bar")
|
|
222
|
+
expect(last_command_started).to be_successfully_executed # Always verify success first!
|
|
223
|
+
manifest = load_manifest # Then access results
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Why this matters:** If command fails, manifest might not exist (nil). Always assert success before accessing command results to prevent confusing test failures.
|
|
227
|
+
|
|
228
|
+
**Live tests:**
|
|
229
|
+
- Tagged with `:live` metadata
|
|
230
|
+
- Run only when `RUN_LIVE_TESTS=1` environment variable set
|
|
231
|
+
- Interact with real anthropics/skills marketplace
|
|
232
|
+
- Cache cleared once at test suite start for performance
|
|
233
|
+
- Use `sleep` for timestamp resolution (not `Timecop`) because Caruso runs as subprocess
|
|
234
|
+
|
|
235
|
+
**Timecop limitation:** Cannot mock time in subprocesses. When testing timestamp updates in plugin reinstall scenarios, use `sleep 1.1` (ISO8601 has second precision) instead of `Timecop.travel`.
|
|
236
|
+
|
|
237
|
+
## Marketplace Compatibility
|
|
238
|
+
|
|
239
|
+
Caruso supports the Claude Code marketplace specification with custom component paths:
|
|
240
|
+
|
|
241
|
+
- Standard structure: `{commands,agents,skills}/**/*.md`
|
|
242
|
+
- Custom paths: `"commands": ["./custom/path"]` in marketplace.json
|
|
243
|
+
- Both string and array formats supported
|
|
244
|
+
- Custom paths **supplement** defaults (they don't replace)
|
|
245
|
+
|
|
246
|
+
Example: anthropics/skills marketplace uses custom paths:
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"name": "document-skills",
|
|
250
|
+
"skills": ["./document-skills/xlsx", "./document-skills/pdf"]
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Fetcher will scan both:
|
|
255
|
+
1. `./document-skills/skills/**/*.md` (default)
|
|
256
|
+
2. `./document-skills/xlsx/**/*.md` (custom)
|
|
257
|
+
3. `./document-skills/pdf/**/*.md` (custom)
|
|
258
|
+
|
|
259
|
+
Results are deduplicated with `.uniq`.
|
|
260
|
+
|
|
261
|
+
## Release Process
|
|
262
|
+
|
|
263
|
+
1. Run tests: `bundle exec rake spec:all`
|
|
264
|
+
2. Bump version: `bundle exec rake bump:patch` (or minor/major)
|
|
265
|
+
3. Update CHANGELOG.md with release notes
|
|
266
|
+
4. Commit: `git commit -m "chore: Bump version to X.Y.Z"`
|
|
267
|
+
5. Tag: `git tag -a vX.Y.Z -m "Release version X.Y.Z"`
|
|
268
|
+
6. Build: `gem build caruso.gemspec`
|
|
269
|
+
7. Install and test: `gem install caruso-X.Y.Z.gem && caruso version`
|
|
270
|
+
8. Push: `git push origin main --tags`
|
|
271
|
+
|
|
272
|
+
Version is managed in `lib/caruso/version.rb`.
|
|
273
|
+
|
|
274
|
+
# Memory
|
|
275
|
+
- The goal is a clean, correct, consistent implementation. Never implement fallbacks that hide errors or engage in defensive programming.
|
|
276
|
+
- Treat the vendor directory .cursor/rules/caruso/ as a build artifact
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2025-11-24
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- **CRITICAL**: Addressed "Uncontrolled data used in path expression" vulnerabilities (CodeQL)
|
|
14
|
+
- Introduced `Caruso::SafeFile` for secure file reading with strict path sanitization
|
|
15
|
+
- Introduced `Caruso::SafeDir` for secure directory operations (globbing, existence checks)
|
|
16
|
+
- Replaced all vulnerable `File` and `Dir` calls in `Adapter` and `Fetcher` with safe alternatives
|
|
17
|
+
- Removed redundant string-based path validation in favor of robust `Pathname` canonicalization
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `Adapter` now strictly validates file existence and raises errors instead of silently skipping invalid files
|
|
21
|
+
- `Fetcher` now filters glob results to ensure they remain within trusted plugin directories
|
|
22
|
+
|
|
10
23
|
## [0.5.3] - 2025-11-23
|
|
11
24
|
|
|
12
25
|
### Changed
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
data/README.md
CHANGED
|
@@ -15,24 +15,27 @@ Enable Cursor to consume Claude Code plugins from marketplaces by converting the
|
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
|
-
###
|
|
18
|
+
### Install from RubyGems
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
```bash
|
|
21
|
+
gem install caruso
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Verify the installation:
|
|
21
25
|
|
|
22
26
|
```bash
|
|
23
|
-
|
|
24
|
-
cd caruso
|
|
25
|
-
gem build caruso.gemspec
|
|
26
|
-
gem install caruso-*.gem
|
|
27
|
+
caruso version
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
###
|
|
30
|
+
### Install from Source (Development)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
For development or testing unreleased features:
|
|
32
33
|
|
|
33
34
|
```bash
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
git clone https://github.com/pcomans/caruso.git
|
|
36
|
+
cd caruso
|
|
37
|
+
gem build caruso.gemspec
|
|
38
|
+
gem install caruso-*.gem
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
## Usage
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We release patches for security vulnerabilities in the following versions:
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| ------- | ------------------ |
|
|
9
|
+
| 0.5.x | :white_check_mark: |
|
|
10
|
+
| < 0.5 | :x: |
|
|
11
|
+
|
|
12
|
+
## Reporting a Vulnerability
|
|
13
|
+
|
|
14
|
+
We take the security of Caruso seriously. If you discover a security vulnerability, please follow these steps:
|
|
15
|
+
|
|
16
|
+
### 1. **Do Not** Open a Public Issue
|
|
17
|
+
|
|
18
|
+
Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.
|
|
19
|
+
|
|
20
|
+
### 2. Report Privately
|
|
21
|
+
|
|
22
|
+
Please report security vulnerabilities using GitHub's private vulnerability reporting:
|
|
23
|
+
|
|
24
|
+
- Go to the [Security tab](https://github.com/pcomans/caruso/security)
|
|
25
|
+
- Click "Report a vulnerability"
|
|
26
|
+
- Fill out the form with details about the vulnerability
|
|
27
|
+
|
|
28
|
+
### 3. Include Details
|
|
29
|
+
|
|
30
|
+
Please include as much of the following information as possible:
|
|
31
|
+
|
|
32
|
+
- Type of vulnerability (e.g., command injection, path traversal, etc.)
|
|
33
|
+
- Step-by-step instructions to reproduce the issue
|
|
34
|
+
- Proof of concept or exploit code (if possible)
|
|
35
|
+
- Impact of the vulnerability
|
|
36
|
+
- Suggested fix (if you have one)
|
|
37
|
+
|
|
38
|
+
### 4. What to Expect
|
|
39
|
+
|
|
40
|
+
- **Acknowledgment**: We'll acknowledge your report within 48 hours
|
|
41
|
+
- **Updates**: We'll keep you informed about our progress
|
|
42
|
+
- **Fix Timeline**: We aim to release a fix within 7-14 days for critical vulnerabilities
|
|
43
|
+
- **Credit**: With your permission, we'll credit you in the security advisory
|
|
44
|
+
|
|
45
|
+
## Security Considerations
|
|
46
|
+
|
|
47
|
+
When using Caruso, keep these security practices in mind:
|
|
48
|
+
|
|
49
|
+
### Marketplace Sources
|
|
50
|
+
|
|
51
|
+
- Only add marketplaces from trusted sources
|
|
52
|
+
- Review plugin code before installation when possible
|
|
53
|
+
- Be cautious with marketplaces requiring authentication
|
|
54
|
+
|
|
55
|
+
### Git Credentials
|
|
56
|
+
|
|
57
|
+
- Caruso uses Git to clone marketplace repositories
|
|
58
|
+
- Ensure your Git credentials are properly secured
|
|
59
|
+
- Use SSH keys or personal access tokens instead of passwords
|
|
60
|
+
|
|
61
|
+
### File Permissions
|
|
62
|
+
|
|
63
|
+
- Caruso writes files to `.cursor/rules/caruso/`
|
|
64
|
+
- Ensure proper file permissions in your project directory
|
|
65
|
+
- Review generated files before committing to version control
|
|
66
|
+
|
|
67
|
+
### Dependencies
|
|
68
|
+
|
|
69
|
+
- Keep Caruso updated to the latest version
|
|
70
|
+
- Regularly update Ruby and gem dependencies
|
|
71
|
+
- Run `gem update caruso` to get security patches
|
|
72
|
+
|
|
73
|
+
## Scope
|
|
74
|
+
|
|
75
|
+
This security policy applies to:
|
|
76
|
+
|
|
77
|
+
- The Caruso gem and CLI tool
|
|
78
|
+
- Official marketplace repositories maintained by this project
|
|
79
|
+
- Documentation and examples in this repository
|
|
80
|
+
|
|
81
|
+
It does not cover:
|
|
82
|
+
|
|
83
|
+
- Third-party marketplaces or plugins
|
|
84
|
+
- User-created custom plugins
|
|
85
|
+
- Vulnerabilities in Ruby itself or system dependencies
|
|
86
|
+
|
|
87
|
+
## Security Updates
|
|
88
|
+
|
|
89
|
+
Security updates will be announced through:
|
|
90
|
+
|
|
91
|
+
- GitHub Security Advisories
|
|
92
|
+
- Release notes in CHANGELOG.md
|
|
93
|
+
- RubyGems.org security alerts
|
|
94
|
+
|
|
95
|
+
## Additional Resources
|
|
96
|
+
|
|
97
|
+
- [RubyGems Security Guide](https://guides.rubygems.org/security/)
|
|
98
|
+
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
data/caruso.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "A tool to fetch Claude Code plugins and adapt them into Cursor Rules or other agent contexts."
|
|
13
13
|
spec.homepage = "https://github.com/pcomans/caruso"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 3.
|
|
15
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
16
16
|
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
18
|
spec.metadata["source_code_uri"] = "https://github.com/pcomans/caruso"
|
data/lib/caruso/adapter.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "yaml"
|
|
5
|
+
require_relative "safe_file"
|
|
6
|
+
require_relative "path_sanitizer"
|
|
5
7
|
|
|
6
8
|
module Caruso
|
|
7
9
|
class Adapter
|
|
@@ -19,7 +21,8 @@ module Caruso
|
|
|
19
21
|
def adapt
|
|
20
22
|
created_files = []
|
|
21
23
|
files.each do |file_path|
|
|
22
|
-
content =
|
|
24
|
+
content = SafeFile.read(file_path)
|
|
25
|
+
|
|
23
26
|
adapted_content = inject_metadata(content, file_path)
|
|
24
27
|
created_file = save_file(file_path, adapted_content)
|
|
25
28
|
created_files << created_file
|
data/lib/caruso/cli.rb
CHANGED
|
@@ -20,8 +20,10 @@ module Caruso
|
|
|
20
20
|
fetcher = Caruso::Fetcher.new(url, ref: options[:ref])
|
|
21
21
|
|
|
22
22
|
# For Git repos, clone/update the cache (skip in test mode to allow fake URLs)
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
# Fixed ReDoS: Use anchored regex and limit input length to prevent catastrophic backtracking
|
|
24
|
+
is_owner_repo = url.length < 256 && url.match?(%r{\A[^/]+/[^/]+\z})
|
|
25
|
+
if (source == "github" || url.match?(/\Ahttps?:/) || is_owner_repo) && !ENV["CARUSO_TESTING_SKIP_CLONE"]
|
|
26
|
+
fetcher.clone_git_repo({ "url" => url, "source" => source })
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
# Read marketplace name from marketplace.json
|
|
@@ -86,14 +88,14 @@ module Caruso
|
|
|
86
88
|
end
|
|
87
89
|
|
|
88
90
|
puts "Marketplace: #{name}"
|
|
89
|
-
puts " Source: #{marketplace['source']}" if marketplace[
|
|
91
|
+
puts " Source: #{marketplace['source']}" if marketplace["source"]
|
|
90
92
|
puts " URL: #{marketplace['url']}"
|
|
91
93
|
puts " Location: #{marketplace['install_location']}"
|
|
92
94
|
puts " Last Updated: #{marketplace['last_updated']}"
|
|
93
|
-
puts " Ref: #{marketplace['ref']}" if marketplace[
|
|
95
|
+
puts " Ref: #{marketplace['ref']}" if marketplace["ref"]
|
|
94
96
|
|
|
95
97
|
# Check if directory actually exists
|
|
96
|
-
if Dir.exist?(marketplace[
|
|
98
|
+
if Dir.exist?(marketplace["install_location"])
|
|
97
99
|
puts " Status: ✓ Cached locally"
|
|
98
100
|
else
|
|
99
101
|
puts " Status: ✗ Cache directory missing"
|
|
@@ -105,12 +107,12 @@ module Caruso
|
|
|
105
107
|
config_manager = load_config
|
|
106
108
|
marketplaces = config_manager.list_marketplaces
|
|
107
109
|
|
|
110
|
+
if marketplaces.empty?
|
|
111
|
+
puts "No marketplaces configured. Use 'caruso marketplace add <url>' to get started."
|
|
112
|
+
return
|
|
113
|
+
end
|
|
108
114
|
if name
|
|
109
115
|
# Update specific marketplace
|
|
110
|
-
if marketplaces.empty?
|
|
111
|
-
puts "No marketplaces configured. Use 'caruso marketplace add <url>' to get started."
|
|
112
|
-
return
|
|
113
|
-
end
|
|
114
116
|
|
|
115
117
|
marketplace_details = config_manager.get_marketplace_details(name)
|
|
116
118
|
unless marketplace_details
|
|
@@ -121,7 +123,8 @@ module Caruso
|
|
|
121
123
|
|
|
122
124
|
puts "Updating marketplace '#{name}'..."
|
|
123
125
|
begin
|
|
124
|
-
fetcher = Caruso::Fetcher.new(marketplace_details["url"], marketplace_name: name,
|
|
126
|
+
fetcher = Caruso::Fetcher.new(marketplace_details["url"], marketplace_name: name,
|
|
127
|
+
ref: marketplace_details["ref"])
|
|
125
128
|
fetcher.update_cache
|
|
126
129
|
puts "Updated marketplace '#{name}'"
|
|
127
130
|
rescue StandardError => e
|
|
@@ -129,25 +132,19 @@ module Caruso
|
|
|
129
132
|
end
|
|
130
133
|
else
|
|
131
134
|
# Update all marketplaces
|
|
132
|
-
if marketplaces.empty?
|
|
133
|
-
puts "No marketplaces configured. Use 'caruso marketplace add <url>' to get started."
|
|
134
|
-
return
|
|
135
|
-
end
|
|
136
135
|
|
|
137
136
|
puts "Updating all marketplaces..."
|
|
138
137
|
success_count = 0
|
|
139
138
|
error_count = 0
|
|
140
139
|
|
|
141
140
|
marketplaces.each do |marketplace_name, details|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
error_count += 1
|
|
150
|
-
end
|
|
141
|
+
puts " Updating #{marketplace_name}..."
|
|
142
|
+
fetcher = Caruso::Fetcher.new(details["url"], marketplace_name: marketplace_name, ref: details["ref"])
|
|
143
|
+
fetcher.update_cache
|
|
144
|
+
success_count += 1
|
|
145
|
+
rescue StandardError => e
|
|
146
|
+
puts " Error updating #{marketplace_name}: #{e.message}"
|
|
147
|
+
error_count += 1
|
|
151
148
|
end
|
|
152
149
|
|
|
153
150
|
puts "\nUpdated #{success_count} marketplace(s)" + (error_count.positive? ? " (#{error_count} failed)" : "")
|
|
@@ -323,14 +320,12 @@ module Caruso
|
|
|
323
320
|
error_count = 0
|
|
324
321
|
|
|
325
322
|
installed_plugins.each do |key, plugin_data|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
error_count += 1
|
|
333
|
-
end
|
|
323
|
+
puts " Updating #{key}..."
|
|
324
|
+
update_single_plugin(key, plugin_data, config_manager)
|
|
325
|
+
success_count += 1
|
|
326
|
+
rescue StandardError => e
|
|
327
|
+
puts " Error updating #{key}: #{e.message}"
|
|
328
|
+
error_count += 1
|
|
334
329
|
end
|
|
335
330
|
|
|
336
331
|
puts "\nUpdated #{success_count} plugin(s)" + (error_count.positive? ? " (#{error_count} failed)" : "")
|
|
@@ -377,7 +372,7 @@ module Caruso
|
|
|
377
372
|
desc "outdated", "Show plugins with available updates"
|
|
378
373
|
def outdated
|
|
379
374
|
config_manager = load_config
|
|
380
|
-
|
|
375
|
+
config_manager.full_target_path
|
|
381
376
|
|
|
382
377
|
installed_plugins = config_manager.list_plugins
|
|
383
378
|
|
|
@@ -389,7 +384,7 @@ module Caruso
|
|
|
389
384
|
puts "Checking for updates..."
|
|
390
385
|
outdated_plugins = []
|
|
391
386
|
|
|
392
|
-
|
|
387
|
+
config_manager.list_marketplaces
|
|
393
388
|
|
|
394
389
|
installed_plugins.each do |key, plugin_data|
|
|
395
390
|
marketplace_name = plugin_data["marketplace"]
|
|
@@ -399,7 +394,8 @@ module Caruso
|
|
|
399
394
|
next unless marketplace_details
|
|
400
395
|
|
|
401
396
|
begin
|
|
402
|
-
|
|
397
|
+
Caruso::Fetcher.new(marketplace_details["url"], marketplace_name: marketplace_name,
|
|
398
|
+
ref: marketplace_details["ref"])
|
|
403
399
|
# For now, we'll just report that updates might be available
|
|
404
400
|
# Full version comparison would require version tracking in marketplace.json
|
|
405
401
|
outdated_plugins << {
|
|
@@ -438,7 +434,8 @@ module Caruso
|
|
|
438
434
|
end
|
|
439
435
|
|
|
440
436
|
# Update marketplace cache first
|
|
441
|
-
fetcher = Caruso::Fetcher.new(marketplace_details["url"], marketplace_name: marketplace_name,
|
|
437
|
+
fetcher = Caruso::Fetcher.new(marketplace_details["url"], marketplace_name: marketplace_name,
|
|
438
|
+
ref: marketplace_details["ref"])
|
|
442
439
|
fetcher.update_cache
|
|
443
440
|
|
|
444
441
|
# Parse plugin name from key (plugin@marketplace)
|