opkg 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.
Files changed (56) hide show
  1. package/README.md +33 -33
  2. package/dist/commands/install.js +12 -7
  3. package/dist/commands/install.js.map +1 -1
  4. package/dist/constants/index.js +21 -0
  5. package/dist/constants/index.js.map +1 -1
  6. package/dist/core/add/add-to-source-pipeline.js +1 -1
  7. package/dist/core/add/add-to-source-pipeline.js.map +1 -1
  8. package/dist/core/directory.js +18 -18
  9. package/dist/core/directory.js.map +1 -1
  10. package/dist/core/flows/platform-converter.js +2 -3
  11. package/dist/core/flows/platform-converter.js.map +1 -1
  12. package/dist/core/install/bulk-install-pipeline.js +2 -1
  13. package/dist/core/install/bulk-install-pipeline.js.map +1 -1
  14. package/dist/core/install/flow-based-installer.js +28 -10
  15. package/dist/core/install/flow-based-installer.js.map +1 -1
  16. package/dist/core/install/git-package-loader.js +23 -5
  17. package/dist/core/install/git-package-loader.js.map +1 -1
  18. package/dist/core/install/marketplace-handler.js +67 -26
  19. package/dist/core/install/marketplace-handler.js.map +1 -1
  20. package/dist/core/install/path-install-pipeline.js +9 -5
  21. package/dist/core/install/path-install-pipeline.js.map +1 -1
  22. package/dist/core/install/path-package-loader.js +15 -9
  23. package/dist/core/install/path-package-loader.js.map +1 -1
  24. package/dist/core/install/plugin-detector.js +4 -6
  25. package/dist/core/install/plugin-detector.js.map +1 -1
  26. package/dist/core/install/plugin-transformer.js +25 -15
  27. package/dist/core/install/plugin-transformer.js.map +1 -1
  28. package/dist/core/package.js +1 -1
  29. package/dist/core/package.js.map +1 -1
  30. package/dist/core/remove/remove-from-source-pipeline.js +1 -1
  31. package/dist/core/remove/remove-from-source-pipeline.js.map +1 -1
  32. package/dist/utils/git-cache.js +196 -0
  33. package/dist/utils/git-cache.js.map +1 -0
  34. package/dist/utils/git-clone.js +123 -43
  35. package/dist/utils/git-clone.js.map +1 -1
  36. package/dist/utils/git-url-parser.js +113 -0
  37. package/dist/utils/git-url-parser.js.map +1 -0
  38. package/dist/utils/index-based-installer.js +2 -2
  39. package/dist/utils/index-based-installer.js.map +1 -1
  40. package/dist/utils/package-input.js +5 -4
  41. package/dist/utils/package-input.js.map +1 -1
  42. package/dist/utils/package-name.js +54 -16
  43. package/dist/utils/package-name.js.map +1 -1
  44. package/dist/utils/platform-file.js +2 -4
  45. package/dist/utils/platform-file.js.map +1 -1
  46. package/dist/utils/plugin-naming.js +111 -0
  47. package/dist/utils/plugin-naming.js.map +1 -0
  48. package/dist/utils/tarball.js +2 -2
  49. package/dist/utils/tarball.js.map +1 -1
  50. package/package.json +1 -1
  51. package/platforms.jsonc +1 -1
  52. package/specs/install/README.md +2 -1
  53. package/specs/install/git-cache.md +403 -0
  54. package/specs/install/git-sources.md +111 -23
  55. package/specs/new/README.md +3 -1
  56. package/specs/set/README.md +5 -2
@@ -0,0 +1,403 @@
1
+ # Git Cache Architecture
2
+
3
+ This document specifies the structured Git cache system used for cloning and caching Git repositories.
4
+
5
+ ---
6
+
7
+ ## 1. Overview
8
+
9
+ The Git cache provides:
10
+ - **Deterministic paths**: Same repository/commit always uses same location
11
+ - **Automatic reuse**: Detects and reuses existing cached commits
12
+ - **Persistent storage**: Cache survives reboots (unlike temp directories)
13
+ - **Space efficiency**: Uses shallow clones (`--depth 1`)
14
+ - **Metadata tracking**: Stores repository and commit information for debugging
15
+
16
+ ---
17
+
18
+ ## 2. Cache structure
19
+
20
+ ### 2.1 Base directory
21
+
22
+ All Git clones are cached at:
23
+ ```
24
+ ~/.openpackage/cache/git/
25
+ ```
26
+
27
+ ### 2.2 Directory hierarchy
28
+
29
+ ```
30
+ ~/.openpackage/cache/git/
31
+ ├── <url-hash-12>/ # Repository (by URL hash)
32
+ │ ├── .opkg-repo.json # Repository metadata
33
+ │ ├── <commit-sha-7>/ # Commit checkout
34
+ │ │ ├── .git/ # Git repository data
35
+ │ │ ├── .opkg-commit.json # Commit metadata
36
+ │ │ └── <repository files>
37
+ │ └── <commit-sha-7>/ # Another commit
38
+ │ └── ...
39
+ └── <url-hash-12>/ # Another repository
40
+ └── ...
41
+ ```
42
+
43
+ **Path components**:
44
+ - `<url-hash-12>`: 12 hex characters (48 bits) - hash of normalized Git URL
45
+ - `<commit-sha-7>`: 7 hex characters - first 7 chars of resolved commit SHA
46
+
47
+ ---
48
+
49
+ ## 3. URL hash generation
50
+
51
+ ### 3.1 Normalization
52
+
53
+ URLs are normalized before hashing to ensure consistency:
54
+
55
+ **Transformations**:
56
+ 1. Convert to lowercase
57
+ 2. Remove `.git` suffix
58
+ 3. Normalize GitHub SSH to HTTPS format:
59
+ - `git@github.com:owner/repo.git` → `https://github.com/owner/repo`
60
+ 4. Normalize other SSH formats:
61
+ - `git@host:owner/repo.git` → `https://host/owner/repo`
62
+ 5. Remove trailing slashes
63
+
64
+ **Examples**:
65
+ | Original URL | Normalized URL |
66
+ |--------------|----------------|
67
+ | `https://github.com/User/Repo.git` | `https://github.com/user/repo` |
68
+ | `git@github.com:anthropics/claude-code.git` | `https://github.com/anthropics/claude-code` |
69
+ | `https://gitlab.com/group/project.git` | `https://gitlab.com/group/project` |
70
+
71
+ ### 3.2 Hash computation
72
+
73
+ - Algorithm: SHA-256
74
+ - Length: First 12 hex characters (48 bits)
75
+ - Collision probability: ~10^-10 with 1000 repos
76
+ - Deterministic: Same normalized URL always produces same hash
77
+
78
+ **Implementation**:
79
+ ```typescript
80
+ function computeGitUrlHash(url: string): string {
81
+ const normalized = normalizeGitUrl(url);
82
+ const hash = createHash('sha256').update(normalized).digest('hex');
83
+ return hash.substring(0, 12);
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 4. Commit SHA resolution
90
+
91
+ ### 4.1 Resolution process
92
+
93
+ When cloning a repository:
94
+ 1. Clone repository to temporary location within cache
95
+ 2. Resolve current HEAD to full commit SHA (40 chars)
96
+ 3. Truncate to 7 characters for directory name
97
+ 4. Move to final location: `<url-hash>/<commit-sha-7>/`
98
+
99
+ ### 4.2 Ref handling
100
+
101
+ **Branch or tag specified**:
102
+ - Clone with `--branch <ref>`
103
+ - Resolve to commit SHA after clone
104
+ - Directory named by commit SHA (not branch name)
105
+
106
+ **Commit SHA specified**:
107
+ - Clone default branch
108
+ - Fetch and checkout specific commit
109
+ - Directory named by commit SHA
110
+
111
+ **No ref specified**:
112
+ - Clone default branch
113
+ - Resolve HEAD to commit SHA
114
+ - Directory named by commit SHA
115
+
116
+ **Result**: Different branches/tags pointing to same commit → same cache location
117
+
118
+ ---
119
+
120
+ ## 5. Metadata files
121
+
122
+ ### 5.1 Repository metadata
123
+
124
+ **Location**: `~/.openpackage/cache/git/<url-hash>/.opkg-repo.json`
125
+
126
+ **Schema**:
127
+ ```json
128
+ {
129
+ "url": "https://github.com/anthropics/claude-code.git",
130
+ "normalized": "https://github.com/anthropics/claude-code",
131
+ "lastFetched": "2025-01-13T10:30:00Z"
132
+ }
133
+ ```
134
+
135
+ **Fields**:
136
+ - `url`: Original Git URL (as provided by user)
137
+ - `normalized`: Normalized URL used for hashing
138
+ - `lastFetched`: Timestamp of most recent fetch operation
139
+
140
+ ### 5.2 Commit metadata
141
+
142
+ **Location**: `~/.openpackage/cache/git/<url-hash>/<commit-sha-7>/.opkg-commit.json`
143
+
144
+ **Schema**:
145
+ ```json
146
+ {
147
+ "url": "https://github.com/anthropics/claude-code.git",
148
+ "commit": "abc1234567890abcdef",
149
+ "ref": "main",
150
+ "subdirectory": "plugins/commit-commands",
151
+ "clonedAt": "2025-01-13T10:30:00Z",
152
+ "lastAccessed": "2025-01-13T12:00:00Z"
153
+ }
154
+ ```
155
+
156
+ **Fields**:
157
+ - `url`: Git URL for this commit
158
+ - `commit`: Full commit SHA (40 chars)
159
+ - `ref`: Branch/tag name if specified (optional)
160
+ - `subdirectory`: Subdirectory path if specified (optional)
161
+ - `clonedAt`: Timestamp when commit was first cloned
162
+ - `lastAccessed`: Timestamp of most recent access
163
+
164
+ ---
165
+
166
+ ## 6. Clone operations
167
+
168
+ ### 6.1 Initial clone
169
+
170
+ When cloning a new commit:
171
+ 1. Generate cache paths:
172
+ - `repoDir = ~/.openpackage/cache/git/<url-hash>/`
173
+ - `tempPath = <repoDir>/.temp-clone`
174
+ 2. Create repo directory if needed
175
+ 3. Write `.opkg-repo.json` metadata
176
+ 4. Clone to temporary path:
177
+ - `git clone --depth 1 [--branch <ref>] <url> <tempPath>`
178
+ 5. Resolve commit SHA from HEAD
179
+ 6. Check if commit already cached (rare race condition)
180
+ 7. Move temp path to final location: `<repoDir>/<commit-sha-7>/`
181
+ 8. Write `.opkg-commit.json` metadata
182
+
183
+ ### 6.2 Cache hit
184
+
185
+ When requested commit is already cached:
186
+ 1. Check if directory exists: `~/.openpackage/cache/git/<url-hash>/<commit-sha-7>/`
187
+ 2. Read and validate `.opkg-commit.json`
188
+ 3. Update `lastAccessed` timestamp
189
+ 4. Return path to cached commit
190
+ 5. Skip clone entirely
191
+
192
+ ### 6.3 Subdirectory handling
193
+
194
+ Subdirectories are accessed within the cloned repository:
195
+ - Clone full repository to cache
196
+ - Return path to subdirectory: `<commit-dir>/<subdirectory>/`
197
+ - Validate subdirectory exists before returning
198
+
199
+ **Example**:
200
+ ```
201
+ Cache: ~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/
202
+ Subdirectory: plugins/commit-commands
203
+ Return: ~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/plugins/commit-commands/
204
+ ```
205
+
206
+ ---
207
+
208
+ ## 7. Shallow clone details
209
+
210
+ ### 7.1 Clone depth
211
+
212
+ All clones use `--depth 1` (shallow clone):
213
+ - Fetches only the latest commit
214
+ - Significantly smaller repository size
215
+ - Faster clone times
216
+ - Sufficient for package installation
217
+
218
+ **Comparison**:
219
+ | Clone Type | Size | Time |
220
+ |------------|------|------|
221
+ | Full clone | ~100MB | 10s |
222
+ | Shallow clone (`--depth 1`) | ~10MB | 2s |
223
+
224
+ ### 7.2 Limitations
225
+
226
+ Shallow clones have limited Git history:
227
+ - Cannot `git log` beyond fetched commit
228
+ - Cannot easily determine branch history
229
+ - May need additional fetch for some operations
230
+
231
+ **Acceptable for OpenPackage** because:
232
+ - Only need files at specific commit
233
+ - Not performing Git operations after clone
234
+ - Can re-clone if full history needed
235
+
236
+ ---
237
+
238
+ ## 8. Cache management
239
+
240
+ ### 8.1 Current behavior
241
+
242
+ - Cache grows indefinitely (no automatic cleanup)
243
+ - Multiple commits of same repository stored separately
244
+ - No size limits enforced
245
+
246
+ ### 8.2 Future enhancements
247
+
248
+ **Cache cleanup**:
249
+ - Remove commits not accessed in N days
250
+ - Remove commits not referenced in any workspace
251
+ - Size-based cleanup (when cache exceeds threshold)
252
+
253
+ **Cache commands** (not yet implemented):
254
+ ```bash
255
+ opkg cache list # List all cached repositories
256
+ opkg cache show <url> # Show commits for specific repository
257
+ opkg cache clean # Clean old/unused cache entries
258
+ opkg cache clean --all # Remove entire cache
259
+ opkg cache clean <url> # Remove specific repository
260
+ ```
261
+
262
+ **Cache statistics**:
263
+ - Total cache size
264
+ - Number of repositories
265
+ - Number of commits
266
+ - Last accessed timestamps
267
+
268
+ ---
269
+
270
+ ## 9. Error handling
271
+
272
+ ### 9.1 Clone failures
273
+
274
+ If clone operation fails:
275
+ 1. Clean up temporary clone directory
276
+ 2. Preserve existing cache entries
277
+ 3. Return error to caller
278
+ 4. Do not write metadata files
279
+
280
+ ### 9.2 Corrupted cache
281
+
282
+ If cache directory is corrupted:
283
+ - Next clone attempt will detect missing/invalid metadata
284
+ - Re-clone automatically
285
+ - Corrupted entries can be manually deleted
286
+
287
+ ### 9.3 Disk space
288
+
289
+ If disk is full:
290
+ - Clone fails with disk space error
291
+ - User must free space or clean cache
292
+ - No automatic cleanup on low disk space
293
+
294
+ ---
295
+
296
+ ## 10. Benefits and trade-offs
297
+
298
+ ### 10.1 Benefits
299
+
300
+ ✅ **Deterministic**: Same repo/commit → same path
301
+ ✅ **Fast reinstalls**: Reuses existing cache
302
+ ✅ **Debuggable**: Metadata files show what's cached
303
+ ✅ **Persistent**: Survives reboots
304
+ ✅ **Space efficient**: Shallow clones
305
+ ✅ **Collision resistant**: 48-bit hash
306
+
307
+ ### 10.2 Trade-offs
308
+
309
+ ⚠️ **Disk usage**: Cache grows over time
310
+ ⚠️ **No automatic cleanup**: User must manage cache
311
+ ⚠️ **Shallow history**: Limited Git operations
312
+ ⚠️ **Branch updates**: No automatic update detection
313
+
314
+ ---
315
+
316
+ ## 11. Implementation notes
317
+
318
+ ### 11.1 URL parsing
319
+
320
+ Supports multiple Git URL formats:
321
+ - HTTPS: `https://github.com/owner/repo.git`
322
+ - SSH: `git@github.com:owner/repo.git`
323
+ - SSH with protocol: `ssh://git@github.com:owner/repo.git`
324
+ - Git protocol: `git://github.com/owner/repo.git`
325
+
326
+ All formats are normalized to HTTPS for hashing.
327
+
328
+ ### 11.2 Path safety
329
+
330
+ - All paths use filesystem-safe characters
331
+ - Commit SHAs are hex (safe for all filesystems)
332
+ - URL hashes are hex (safe for all filesystems)
333
+ - No special character handling needed
334
+
335
+ ### 11.3 Concurrency
336
+
337
+ **Current implementation**:
338
+ - No file locking or coordination
339
+ - Rare race condition possible (same commit cloned simultaneously)
340
+ - Race is detected and handled (one clone succeeds, others reuse)
341
+
342
+ **Future consideration**:
343
+ - File locking for clone operations
344
+ - Atomic directory moves
345
+ - Retry logic for transient failures
346
+
347
+ ---
348
+
349
+ ## 12. Related specifications
350
+
351
+ - [Git Sources](./git-sources.md): Git install behavior
352
+ - [Install Behavior](./install-behavior.md): Overall install flow
353
+ - [Claude Code Plugin Support](./install-behavior.md#9-claude-code-plugin-support): Plugin installation
354
+
355
+ ---
356
+
357
+ ## 13. Examples
358
+
359
+ ### 13.1 GitHub plugin marketplace
360
+
361
+ **Command**:
362
+ ```bash
363
+ opkg install github:anthropics/claude-code#subdirectory=plugins/commit-commands
364
+ ```
365
+
366
+ **Process**:
367
+ 1. Normalize URL: `https://github.com/anthropics/claude-code`
368
+ 2. Compute hash: `a1b2c3d4e5f6`
369
+ 3. Clone to: `~/.openpackage/cache/git/a1b2c3d4e5f6/.temp-clone`
370
+ 4. Resolve commit: `abc1234567890abcdef`
371
+ 5. Move to: `~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/`
372
+ 6. Return path: `~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/plugins/commit-commands/`
373
+
374
+ ### 13.2 GitLab project
375
+
376
+ **Command**:
377
+ ```bash
378
+ opkg install git:https://gitlab.com/company/project.git#v1.0.0
379
+ ```
380
+
381
+ **Process**:
382
+ 1. Normalize URL: `https://gitlab.com/company/project`
383
+ 2. Compute hash: `x9y8z7w6v5u4`
384
+ 3. Clone with tag: `git clone --depth 1 --branch v1.0.0 <url>`
385
+ 4. Resolve commit: `def5678901234abcdef`
386
+ 5. Move to: `~/.openpackage/cache/git/x9y8z7w6v5u4/def5678/`
387
+ 6. Return path: `~/.openpackage/cache/git/x9y8z7w6v5u4/def5678/`
388
+
389
+ ### 13.3 Cache reuse
390
+
391
+ **First install**:
392
+ ```bash
393
+ opkg install github:user/plugin
394
+ # Clones to: ~/.openpackage/cache/git/k4l5m6n7o8p9/ghi9012/
395
+ ```
396
+
397
+ **Second install** (same commit):
398
+ ```bash
399
+ opkg install github:user/plugin
400
+ # Detects existing cache
401
+ # Reuses: ~/.openpackage/cache/git/k4l5m6n7o8p9/ghi9012/
402
+ # No clone needed
403
+ ```
@@ -70,9 +70,16 @@ Rules:
70
70
 
71
71
  ### 3.1 Basic git install
72
72
 
73
- - `install` clones the repository to a temporary directory using the system `git` executable.
73
+ - `install` clones the repository to a **structured cache** at `~/.openpackage/cache/git/` using the system `git` executable.
74
+ - **Cache structure**: `~/.openpackage/cache/git/<url-hash-12>/<commit-sha-7>/`
75
+ - `<url-hash-12>`: 12-character hash of normalized Git URL
76
+ - `<commit-sha-7>`: First 7 characters of resolved commit SHA
77
+ - **Clone behavior**:
78
+ - Uses shallow clones (`--depth 1`) for space efficiency.
79
+ - Reuses existing cache if same commit is already cached.
80
+ - Writes metadata files (`.opkg-repo.json`, `.opkg-commit.json`) for tracking.
74
81
  - If `ref` is provided:
75
- - For branch/tag: clone the specified ref.
82
+ - For branch/tag: clone the specified ref and resolve to commit SHA.
76
83
  - For commit SHA: clone and checkout that SHA (best-effort shallow fetch).
77
84
  - Without subdirectory: The cloned repository root MUST contain `openpackage.yml`.
78
85
  - The installed package version is read from the repo's `openpackage.yml`.
@@ -83,14 +90,35 @@ Rules:
83
90
  ### 3.2 Subdirectory installs
84
91
 
85
92
  When `subdirectory` is specified:
86
- - Repository is cloned to a temporary directory.
93
+ - Repository is cloned to the structured cache (same as §3.1).
87
94
  - The specified subdirectory path is resolved relative to the repository root.
88
95
  - The subdirectory MUST contain either:
89
96
  - `openpackage.yml` (standard OpenPackage package), OR
90
97
  - `.claude-plugin/plugin.json` (Claude Code plugin), OR
91
98
  - `.claude-plugin/marketplace.json` (Claude Code plugin marketplace)
92
99
  - For OpenPackage packages: `openpackage.yml` is read from the subdirectory.
93
- - For Claude Code plugins: See §4 for special handling.
100
+ - For Claude Code plugins: See §4 for special handling including scoped naming.
101
+
102
+ ### 3.3 Cache persistence and management
103
+
104
+ The Git cache persists across sessions:
105
+ - **Location**: `~/.openpackage/cache/git/`
106
+ - **Benefits**: Faster reinstalls, survives reboots, debuggable with metadata
107
+ - **Structure**:
108
+ ```
109
+ ~/.openpackage/cache/git/
110
+ ├── a1b2c3d4e5f6/ # URL hash
111
+ │ ├── .opkg-repo.json # Repo metadata (URL, last fetched)
112
+ │ ├── abc1234/ # Commit SHA
113
+ │ │ ├── .git/ # Shallow clone
114
+ │ │ ├── .opkg-commit.json # Commit metadata (ref, timestamp)
115
+ │ │ └── <repo contents>
116
+ │ └── def5678/ # Different commit
117
+ └── x9y8z7w6v5u4/ # Different repo
118
+ ```
119
+ - **Metadata tracking**:
120
+ - `.opkg-repo.json`: Stores URL, normalized URL, last fetch timestamp
121
+ - `.opkg-commit.json`: Stores full commit SHA, ref name, clone/access timestamps
94
122
 
95
123
  ---
96
124
 
@@ -116,20 +144,29 @@ Detection happens automatically after cloning, before attempting to load as an O
116
144
 
117
145
  When an individual plugin is detected:
118
146
  1. Plugin manifest (`.claude-plugin/plugin.json`) is read and validated.
119
- 2. Plugin metadata is transformed to OpenPackage format in-memory:
120
- - `name` and `version` from `plugin.json` become package metadata
147
+ 2. **Plugin name is generated with scoping**:
148
+ - **GitHub plugins**: Use scoped format `@<username>/<plugin-name>`
149
+ - **GitHub plugins from subdirectory**: Use `@<username>/<repo>/<plugin-name>`
150
+ - **Non-GitHub sources**: Use original plugin name (no scoping)
151
+ - **Fallback behavior**: If `plugin.json` has no `name` field:
152
+ - Use subdirectory basename if installing from subdirectory
153
+ - Use repository name if installing full repo
154
+ - Use "unnamed-plugin" as last resort
155
+ 3. Plugin metadata is transformed to OpenPackage format in-memory:
156
+ - Scoped `name` becomes package metadata
157
+ - `version` from `plugin.json` becomes package version
121
158
  - `description`, `author`, `repository`, etc. are preserved
122
- 3. All plugin files are collected (commands/, agents/, skills/, hooks/, .mcp.json, .lsp.json, etc.)
123
- 4. **Package format is detected** and appropriate installation strategy selected:
159
+ 4. All plugin files are collected (commands/, agents/, skills/, hooks/, .mcp.json, .lsp.json, etc.)
160
+ 5. **Package format is detected** and appropriate installation strategy selected:
124
161
  - **Direct AS-IS**: Source platform = target platform (fastest)
125
162
  - **Cross-platform conversion**: Source ≠ target (via Universal Converter)
126
163
  - **Standard flows**: Universal format packages
127
- 5. Files are installed to platform-specific directories:
164
+ 6. Files are installed to platform-specific directories:
128
165
  - `commands/` → `.claude/commands/`, `.cursor/commands/`, etc.
129
166
  - `agents/` → `.claude/agents/`, `.cursor/agents/`, etc.
130
167
  - Root files (`.mcp.json`, `.lsp.json`) → platform roots
131
- 6. The dependency is tracked in `openpackage.yml` with its git source (not as a registry version).
132
- 7. No registry copy is created (git repository remains source of truth).
168
+ 7. The dependency is tracked in `openpackage.yml` with its **scoped name** and git source (not as a registry version).
169
+ 8. No registry copy is created (git repository remains source of truth).
133
170
 
134
171
  **See:** [Universal Platform Converter](../platforms/universal-converter.md) for cross-platform conversion details.
135
172
 
@@ -141,22 +178,29 @@ opkg install github:anthropics/claude-code#subdirectory=plugins/commit-commands
141
178
  Result in `openpackage.yml`:
142
179
  ```yaml
143
180
  packages:
144
- - name: commit-commands
181
+ - name: "@anthropics/claude-code/commit-commands" # Scoped name
145
182
  git: https://github.com/anthropics/claude-code.git
146
183
  subdirectory: plugins/commit-commands
147
184
  ```
148
185
 
186
+ Installed to cache:
187
+ ```
188
+ ~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/plugins/commit-commands/
189
+ ```
190
+
149
191
  ### 4.3 Marketplace install
150
192
 
151
193
  When a plugin marketplace is detected:
152
194
  1. Marketplace manifest (`.claude-plugin/marketplace.json`) is parsed.
195
+ - **Fallback behavior**: If `marketplace.json` has no `name` field, uses repository name
153
196
  2. An interactive multiselect prompt is displayed listing all available plugins.
154
197
  3. User selects which plugin(s) to install (space to select, enter to confirm).
155
198
  4. Each selected plugin is installed individually:
199
+ - **Scoped name is generated**: `@<username>/<marketplace-name>/<plugin-name>`
156
200
  - Plugin subdirectory is resolved within the cloned repository.
157
201
  - Plugin is validated (must have `.claude-plugin/plugin.json`).
158
202
  - Plugin is installed following the individual plugin flow (§4.2).
159
- 5. Each plugin gets its own entry in `openpackage.yml` with its specific subdirectory.
203
+ 5. Each plugin gets its own entry in `openpackage.yml` with its **scoped name** and specific subdirectory.
160
204
 
161
205
  **Example:**
162
206
  ```bash
@@ -177,15 +221,48 @@ Select plugins to install (space to select, enter to confirm):
177
221
  Result in `openpackage.yml` (if user selected commit-commands and pr-review-toolkit):
178
222
  ```yaml
179
223
  packages:
180
- - name: commit-commands
224
+ - name: "@anthropics/claude-code/commit-commands" # Scoped name
181
225
  git: https://github.com/anthropics/claude-code.git
182
226
  subdirectory: plugins/commit-commands
183
- - name: pr-review-toolkit
227
+ - name: "@anthropics/claude-code/pr-review-toolkit" # Scoped name
184
228
  git: https://github.com/anthropics/claude-code.git
185
229
  subdirectory: plugins/pr-review-toolkit
186
230
  ```
187
231
 
188
- ### 4.4 Plugin transformation details
232
+ Installed to cache:
233
+ ```
234
+ ~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/plugins/commit-commands/
235
+ ~/.openpackage/cache/git/a1b2c3d4e5f6/abc1234/plugins/pr-review-toolkit/
236
+ ```
237
+
238
+ ### 4.4 Plugin naming convention
239
+
240
+ **Scoped naming for GitHub plugins**: Plugins installed from GitHub repositories use scoped names to provide clear provenance and prevent naming conflicts.
241
+
242
+ **Naming formats**:
243
+ - **Marketplace plugin**: `@<username>/<marketplace-name>/<plugin-name>`
244
+ - **Standalone plugin**: `@<username>/<plugin-name>`
245
+ - **Non-GitHub source**: `<plugin-name>` (no scoping)
246
+
247
+ **Examples**:
248
+ | Source | Plugin Name | Scoped Name |
249
+ |--------|-------------|-------------|
250
+ | `github:anthropics/claude-code#subdirectory=plugins/commit-commands` | `commit-commands` | `@anthropics/claude-code/commit-commands` |
251
+ | `github:anthropics/my-plugin` | `my-plugin` | `@anthropics/my-plugin` |
252
+ | `git:https://gitlab.com/user/plugin.git` | `cool-plugin` | `cool-plugin` (no scoping) |
253
+ | `./local-plugin/` | `local-plugin` | `local-plugin` (no scoping) |
254
+
255
+ **Fallback behavior**:
256
+ 1. **Plugin name missing**: Uses subdirectory basename → repo name → "unnamed-plugin"
257
+ 2. **Marketplace name missing**: Uses repo name → "unnamed-marketplace"
258
+
259
+ **Benefits**:
260
+ - Clear GitHub provenance at a glance
261
+ - No name conflicts between authors
262
+ - Easy to identify plugin source
263
+ - Consistent with npm/yarn scoping patterns
264
+
265
+ ### 4.5 Plugin transformation details
189
266
 
190
267
  **In-memory transformation** (no registry copy):
191
268
  - Plugin manifest fields map to OpenPackage metadata:
@@ -213,8 +290,8 @@ packages:
213
290
 
214
291
  ### 5.1 Current limitations
215
292
 
216
- - No lockfile or commit pinning is persisted (no `resolvedSha` field).
217
- - No clone caching (each install may re-clone).
293
+ - **No lockfile pinning**: Commit SHAs are resolved but not persisted to `openpackage.yml` (no `resolvedSha` field).
294
+ - **Branch tracking**: Installing from a branch will use the latest commit at install time, not track updates.
218
295
  - Authentication behavior is delegated to `git` (credentials configured in the user's environment).
219
296
 
220
297
  ### 5.2 Subdirectory support notes
@@ -223,8 +300,19 @@ packages:
223
300
  - Subdirectory must contain a valid package or plugin manifest.
224
301
  - For OpenPackage packages in subdirectories, their dependencies are resolved relative to the subdirectory location.
225
302
 
226
- ### 5.3 Future considerations
227
-
228
- - Commit SHA resolution and pinning for reproducible installs.
229
- - Clone caching to speed up repeated installs.
230
- - Plugin registry support (converting plugins to first-class OpenPackage packages).
303
+ ### 5.3 Cache features
304
+
305
+ **Implemented**:
306
+ - Structured cache at `~/.openpackage/cache/git/`
307
+ - Deterministic paths based on URL hash + commit SHA
308
+ - ✅ Automatic cache reuse when same commit exists
309
+ - ✅ Metadata tracking (`.opkg-repo.json`, `.opkg-commit.json`)
310
+ - ✅ Shallow clones for space efficiency
311
+ - ✅ Persistent across reboots
312
+
313
+ **Future considerations**:
314
+ - Cache management commands (`opkg cache list`, `opkg cache clean`)
315
+ - Cache update detection (detect when branch has new commits)
316
+ - Git worktrees for further space optimization
317
+ - Automatic cache cleanup based on age/size
318
+ - Commit SHA lockfile support for reproducible installs
@@ -20,7 +20,9 @@ Creates a new package with an `openpackage.yml` manifest in one of three predefi
20
20
  - `[package-name]` (optional for root scope, required for local/global)
21
21
  - Package name following OpenPackage naming conventions
22
22
  - Supports scoped packages (`@org/package-name`)
23
- - Validated against naming rules (lowercase, alphanumeric, hyphens, slashes for scopes)
23
+ - Supports hierarchical packages (`@org/package-name/subpackage`)
24
+ - Validated against naming rules (lowercase, alphanumeric, dots, underscores, hyphens, slashes)
25
+ - No consecutive or trailing slashes allowed
24
26
 
25
27
  ### Options
26
28
  - `--scope <scope>` - Package scope: `root`, `local`, or `global` (prompts if not specified in interactive mode)
@@ -126,16 +126,19 @@ opkg set --ver 0.1.0
126
126
  **Format:** Package name string
127
127
 
128
128
  **Validation:**
129
- - Must contain only: `a-z`, `0-9`, `.`, `_`, `-`
129
+ - Must contain only: `a-z`, `0-9`, `.`, `_`, `-`, `/`
130
130
  - Automatically normalized to lowercase
131
131
  - No spaces allowed
132
132
  - Supports scoped names: `@scope/package-name`
133
+ - Supports hierarchical names: `@scope/package-name/subpackage`
134
+ - No consecutive or trailing slashes
133
135
 
134
136
  **Examples:**
135
137
  ```bash
136
138
  opkg set --name my-package
137
139
  opkg set --name @myorg/my-package
138
140
  opkg set --name package.name
141
+ opkg set --name @anthropics/claude-code/commit-commands
139
142
  ```
140
143
 
141
144
  ### Description Field (`--description`)
@@ -305,7 +308,7 @@ Version must be valid semver (e.g., 1.0.0, 2.1.3-beta.1)
305
308
  **Error:** Name contains invalid characters
306
309
 
307
310
  ```
308
- Error: Package name 'invalid name' contains invalid characters (use only: a-z, 0-9, ., _, -)
311
+ Error: Package name 'invalid name' segment 'invalid name' contains invalid characters (use only: a-z, 0-9, ., _, -)
309
312
  ```
310
313
 
311
314
  ### Invalid URL