allagents 0.1.5 → 0.2.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.
- package/LICENSE +21 -21
- package/README.md +281 -281
- package/dist/index.js +292 -160
- package/dist/templates/default/.allagents/workspace.yaml +15 -15
- package/dist/templates/default/AGENTS.md +14 -14
- package/package.json +3 -2
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 EntityProcess
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EntityProcess
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,281 +1,281 @@
|
|
|
1
|
-
# AllAgents
|
|
2
|
-
|
|
3
|
-
CLI tool for managing multi-repo AI agent workspaces with plugin synchronization across multiple AI clients.
|
|
4
|
-
|
|
5
|
-
> **Attribution:** AllAgents is inspired by [dotagents](https://github.com/iannuttall/dotagents) by Ian Nuttall. While rewritten from scratch, we share the vision of unified AI agent configuration management. Thank you Ian for the inspiration!
|
|
6
|
-
|
|
7
|
-
## Why AllAgents?
|
|
8
|
-
|
|
9
|
-
**The Problem:** AI coding assistants (Claude, Copilot, Cursor, Codex, etc.) each have their own configuration formats and directory structures. If you want to share skills
|
|
10
|
-
|
|
11
|
-
**AllAgents solves this by:**
|
|
12
|
-
|
|
13
|
-
| Feature | Claude Code Plugins | AllAgents |
|
|
14
|
-
|---------|--------------------|-----------|
|
|
15
|
-
| Scope | Single project | Multi-repo workspace |
|
|
16
|
-
| Client support | Claude only | 8 AI clients |
|
|
17
|
-
| File location | Runtime lookup from cache | Copied to workspace (git-versioned) |
|
|
18
|
-
| Project structure | AI config mixed with code | Separate workspace repo |
|
|
19
|
-
|
|
20
|
-
### Key Differentiators
|
|
21
|
-
|
|
22
|
-
1. **Multi-repo workspaces** - One workspace references multiple project repositories. Your AI tooling lives separately from your application code.
|
|
23
|
-
|
|
24
|
-
2. **Multi-client distribution** - Write plugins once, sync to all clients. AllAgents transforms and copies files to each client's expected paths.
|
|
25
|
-
|
|
26
|
-
3. **Workspace is a git repo** - Unlike Claude's runtime plugin system, AllAgents copies files into your workspace. Team members get the same AI tooling via git.
|
|
27
|
-
|
|
28
|
-
4. **Clean separation** - Project repos stay clean. AI configuration lives in the workspace.
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
┌─────────────────┐
|
|
32
|
-
│ Marketplace │ (plugin source - GitHub repos)
|
|
33
|
-
└────────┬────────┘
|
|
34
|
-
│
|
|
35
|
-
▼
|
|
36
|
-
┌─────────────────┐
|
|
37
|
-
│ AllAgents │ (sync & transform)
|
|
38
|
-
│ workspace sync │
|
|
39
|
-
└────────┬────────┘
|
|
40
|
-
│
|
|
41
|
-
┌────┴────┬────────┬─────────┐
|
|
42
|
-
▼ ▼ ▼ ▼
|
|
43
|
-
.claude/ .github/ .cursor/ .codex/ (client-specific paths)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Installation
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
# Using bun
|
|
50
|
-
bun install -g allagents
|
|
51
|
-
|
|
52
|
-
# Or run directly
|
|
53
|
-
bunx allagents
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Quick Start
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# Create a new workspace
|
|
60
|
-
allagents workspace init my-workspace
|
|
61
|
-
cd my-workspace
|
|
62
|
-
|
|
63
|
-
# Add a marketplace (or let auto-registration handle it)
|
|
64
|
-
allagents plugin marketplace add anthropics/claude-plugins-official
|
|
65
|
-
|
|
66
|
-
# Add plugins to workspace
|
|
67
|
-
allagents workspace plugin add code-review@claude-plugins-official
|
|
68
|
-
allagents workspace plugin add my-plugin@someuser/their-repo
|
|
69
|
-
|
|
70
|
-
# Sync plugins to workspace
|
|
71
|
-
allagents workspace sync
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Commands
|
|
75
|
-
|
|
76
|
-
### Workspace Commands
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
# Initialize a new workspace from template
|
|
80
|
-
allagents workspace init <path>
|
|
81
|
-
|
|
82
|
-
# Sync all plugins to workspace (non-destructive)
|
|
83
|
-
allagents workspace sync [options]
|
|
84
|
-
--force Force re-fetch of remote plugins even if cached
|
|
85
|
-
--dry-run Preview changes without applying
|
|
86
|
-
|
|
87
|
-
# Non-destructive sync: your files are safe
|
|
88
|
-
# - First sync overlays without deleting existing files
|
|
89
|
-
# - Subsequent syncs only remove files AllAgents previously synced
|
|
90
|
-
# - Tracked in .allagents/sync-state.json
|
|
91
|
-
|
|
92
|
-
# Show status of workspace and plugins
|
|
93
|
-
allagents workspace status
|
|
94
|
-
|
|
95
|
-
# Add a plugin to .allagents/workspace.yaml (auto-registers marketplace if needed)
|
|
96
|
-
allagents workspace plugin add <plugin@marketplace>
|
|
97
|
-
|
|
98
|
-
# Remove a plugin from .allagents/workspace.yaml
|
|
99
|
-
allagents workspace plugin remove <plugin>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Plugin Marketplace Commands
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# List registered marketplaces
|
|
106
|
-
allagents plugin marketplace list
|
|
107
|
-
|
|
108
|
-
# Add a marketplace from GitHub or local path
|
|
109
|
-
allagents plugin marketplace add <source>
|
|
110
|
-
# Examples:
|
|
111
|
-
# allagents plugin marketplace add anthropics/claude-plugins-official
|
|
112
|
-
# allagents plugin marketplace add /path/to/local/marketplace
|
|
113
|
-
|
|
114
|
-
# Remove a marketplace
|
|
115
|
-
allagents plugin marketplace remove <name>
|
|
116
|
-
|
|
117
|
-
# Update marketplace(s) from remote
|
|
118
|
-
allagents plugin marketplace update [name]
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Plugin Commands
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
# List available plugins from marketplaces
|
|
125
|
-
allagents plugin list [marketplace]
|
|
126
|
-
|
|
127
|
-
# Validate a plugin or marketplace structure
|
|
128
|
-
allagents plugin validate <path>
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## .allagents/workspace.yaml
|
|
132
|
-
|
|
133
|
-
The workspace configuration file lives in `.allagents/workspace.yaml` and defines repositories, plugins, and target clients:
|
|
134
|
-
|
|
135
|
-
```yaml
|
|
136
|
-
repositories:
|
|
137
|
-
- path: ../my-project
|
|
138
|
-
owner: myorg
|
|
139
|
-
repo: my-project
|
|
140
|
-
description: Main project repository
|
|
141
|
-
- path: ../my-api
|
|
142
|
-
owner: myorg
|
|
143
|
-
repo: my-api
|
|
144
|
-
description: API service
|
|
145
|
-
|
|
146
|
-
plugins:
|
|
147
|
-
- code-review@claude-plugins-official # plugin@marketplace format
|
|
148
|
-
- context7@claude-plugins-official
|
|
149
|
-
- my-plugin@someuser/their-repo # fully qualified for custom marketplaces
|
|
150
|
-
|
|
151
|
-
clients:
|
|
152
|
-
- claude
|
|
153
|
-
- copilot
|
|
154
|
-
- cursor
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Plugin Spec Format
|
|
158
|
-
|
|
159
|
-
Plugins use the `plugin@marketplace` format:
|
|
160
|
-
|
|
161
|
-
| Format | Example | Description |
|
|
162
|
-
|--------|---------|-------------|
|
|
163
|
-
| Well-known | `code-review@claude-plugins-official` | Uses known marketplace mapping |
|
|
164
|
-
| owner/repo | `my-plugin@owner/repo` | Auto-registers GitHub repo, looks in `plugins/` |
|
|
165
|
-
| owner/repo/subpath | `my-plugin@owner/repo/extensions` | Looks in custom subdirectory |
|
|
166
|
-
|
|
167
|
-
The subpath format is useful when plugins aren't in the standard `plugins/` directory:
|
|
168
|
-
|
|
169
|
-
```yaml
|
|
170
|
-
plugins:
|
|
171
|
-
- feature-dev@anthropics/claude-plugins-official/plugins # explicit plugins/ dir
|
|
172
|
-
- my-addon@someuser/repo/addons # custom addons/ dir
|
|
173
|
-
- tool@org/monorepo/packages/tools # nested path
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Well-Known Marketplaces
|
|
177
|
-
|
|
178
|
-
These marketplace names auto-resolve to their GitHub repos:
|
|
179
|
-
|
|
180
|
-
- `claude-plugins-official` → `anthropics/claude-plugins-official`
|
|
181
|
-
|
|
182
|
-
### Supported Clients
|
|
183
|
-
|
|
184
|
-
| Client |
|
|
185
|
-
|
|
186
|
-
| claude | `.claude/
|
|
187
|
-
| copilot | `.github/
|
|
188
|
-
| codex | `.codex/
|
|
189
|
-
| cursor | `.cursor/
|
|
190
|
-
| opencode | `.opencode/
|
|
191
|
-
| gemini | `.gemini/
|
|
192
|
-
| factory | `.factory/
|
|
193
|
-
| ampcode |
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
│
|
|
205
|
-
│ │
|
|
206
|
-
│ └──
|
|
207
|
-
│
|
|
208
|
-
│ ├──
|
|
209
|
-
│ └── skills/
|
|
210
|
-
└── README.md
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Plugin Structure
|
|
214
|
-
|
|
215
|
-
Each plugin follows this structure:
|
|
216
|
-
|
|
217
|
-
```
|
|
218
|
-
my-plugin/
|
|
219
|
-
├── plugin.json # Plugin metadata
|
|
220
|
-
├──
|
|
221
|
-
│
|
|
222
|
-
│
|
|
223
|
-
├──
|
|
224
|
-
│
|
|
225
|
-
│
|
|
226
|
-
├── hooks/ # Hook files (
|
|
227
|
-
│ └── pre-commit.md
|
|
228
|
-
└── AGENTS.md # Agent configuration (optional)
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Skill Validation
|
|
232
|
-
|
|
233
|
-
Skills must have a valid `SKILL.md` file with YAML frontmatter:
|
|
234
|
-
|
|
235
|
-
```yaml
|
|
236
|
-
---
|
|
237
|
-
name: my-skill # Required: lowercase, alphanumeric + hyphens, max 64 chars
|
|
238
|
-
description: Description of the skill # Required
|
|
239
|
-
allowed-tools: # Optional
|
|
240
|
-
- Read
|
|
241
|
-
- Write
|
|
242
|
-
model: claude-3-opus # Optional
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
# Skill Content
|
|
246
|
-
|
|
247
|
-
Skill instructions go here...
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## Storage Locations
|
|
251
|
-
|
|
252
|
-
```
|
|
253
|
-
~/.allagents/
|
|
254
|
-
├── marketplaces.json # Registry of marketplaces
|
|
255
|
-
└── marketplaces/ # Cloned marketplace repos
|
|
256
|
-
├── claude-plugins-official/
|
|
257
|
-
└── someuser-their-repo/
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## Development
|
|
261
|
-
|
|
262
|
-
```bash
|
|
263
|
-
# Install dependencies
|
|
264
|
-
bun install
|
|
265
|
-
|
|
266
|
-
# Run in development
|
|
267
|
-
bun run dev workspace init test-ws
|
|
268
|
-
|
|
269
|
-
# Run tests
|
|
270
|
-
bun run test
|
|
271
|
-
|
|
272
|
-
# Type check
|
|
273
|
-
bun run typecheck
|
|
274
|
-
|
|
275
|
-
# Build
|
|
276
|
-
bun run build
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
## License
|
|
280
|
-
|
|
281
|
-
MIT
|
|
1
|
+
# AllAgents
|
|
2
|
+
|
|
3
|
+
CLI tool for managing multi-repo AI agent workspaces with plugin synchronization across multiple AI clients.
|
|
4
|
+
|
|
5
|
+
> **Attribution:** AllAgents is inspired by [dotagents](https://github.com/iannuttall/dotagents) by Ian Nuttall. While rewritten from scratch, we share the vision of unified AI agent configuration management. Thank you Ian for the inspiration!
|
|
6
|
+
|
|
7
|
+
## Why AllAgents?
|
|
8
|
+
|
|
9
|
+
**The Problem:** AI coding assistants (Claude, Copilot, Cursor, Codex, etc.) each have their own configuration formats and directory structures. If you want to share skills across multiple projects or use multiple AI clients, you need to manually copy and transform files.
|
|
10
|
+
|
|
11
|
+
**AllAgents solves this by:**
|
|
12
|
+
|
|
13
|
+
| Feature | Claude Code Plugins | AllAgents |
|
|
14
|
+
|---------|--------------------|-----------|
|
|
15
|
+
| Scope | Single project | Multi-repo workspace |
|
|
16
|
+
| Client support | Claude only | 8 AI clients |
|
|
17
|
+
| File location | Runtime lookup from cache | Copied to workspace (git-versioned) |
|
|
18
|
+
| Project structure | AI config mixed with code | Separate workspace repo |
|
|
19
|
+
|
|
20
|
+
### Key Differentiators
|
|
21
|
+
|
|
22
|
+
1. **Multi-repo workspaces** - One workspace references multiple project repositories. Your AI tooling lives separately from your application code.
|
|
23
|
+
|
|
24
|
+
2. **Multi-client distribution** - Write plugins once, sync to all clients. AllAgents transforms and copies files to each client's expected paths.
|
|
25
|
+
|
|
26
|
+
3. **Workspace is a git repo** - Unlike Claude's runtime plugin system, AllAgents copies files into your workspace. Team members get the same AI tooling via git.
|
|
27
|
+
|
|
28
|
+
4. **Clean separation** - Project repos stay clean. AI configuration lives in the workspace.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌─────────────────┐
|
|
32
|
+
│ Marketplace │ (plugin source - GitHub repos)
|
|
33
|
+
└────────┬────────┘
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
┌─────────────────┐
|
|
37
|
+
│ AllAgents │ (sync & transform)
|
|
38
|
+
│ workspace sync │
|
|
39
|
+
└────────┬────────┘
|
|
40
|
+
│
|
|
41
|
+
┌────┴────┬────────┬─────────┐
|
|
42
|
+
▼ ▼ ▼ ▼
|
|
43
|
+
.claude/ .github/ .cursor/ .codex/ (client-specific paths)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Using bun
|
|
50
|
+
bun install -g allagents
|
|
51
|
+
|
|
52
|
+
# Or run directly
|
|
53
|
+
bunx allagents
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Create a new workspace
|
|
60
|
+
allagents workspace init my-workspace
|
|
61
|
+
cd my-workspace
|
|
62
|
+
|
|
63
|
+
# Add a marketplace (or let auto-registration handle it)
|
|
64
|
+
allagents plugin marketplace add anthropics/claude-plugins-official
|
|
65
|
+
|
|
66
|
+
# Add plugins to workspace
|
|
67
|
+
allagents workspace plugin add code-review@claude-plugins-official
|
|
68
|
+
allagents workspace plugin add my-plugin@someuser/their-repo
|
|
69
|
+
|
|
70
|
+
# Sync plugins to workspace
|
|
71
|
+
allagents workspace sync
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
### Workspace Commands
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Initialize a new workspace from template
|
|
80
|
+
allagents workspace init <path>
|
|
81
|
+
|
|
82
|
+
# Sync all plugins to workspace (non-destructive)
|
|
83
|
+
allagents workspace sync [options]
|
|
84
|
+
--force Force re-fetch of remote plugins even if cached
|
|
85
|
+
--dry-run Preview changes without applying
|
|
86
|
+
|
|
87
|
+
# Non-destructive sync: your files are safe
|
|
88
|
+
# - First sync overlays without deleting existing files
|
|
89
|
+
# - Subsequent syncs only remove files AllAgents previously synced
|
|
90
|
+
# - Tracked in .allagents/sync-state.json
|
|
91
|
+
|
|
92
|
+
# Show status of workspace and plugins
|
|
93
|
+
allagents workspace status
|
|
94
|
+
|
|
95
|
+
# Add a plugin to .allagents/workspace.yaml (auto-registers marketplace if needed)
|
|
96
|
+
allagents workspace plugin add <plugin@marketplace>
|
|
97
|
+
|
|
98
|
+
# Remove a plugin from .allagents/workspace.yaml
|
|
99
|
+
allagents workspace plugin remove <plugin>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Plugin Marketplace Commands
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# List registered marketplaces
|
|
106
|
+
allagents plugin marketplace list
|
|
107
|
+
|
|
108
|
+
# Add a marketplace from GitHub or local path
|
|
109
|
+
allagents plugin marketplace add <source>
|
|
110
|
+
# Examples:
|
|
111
|
+
# allagents plugin marketplace add anthropics/claude-plugins-official
|
|
112
|
+
# allagents plugin marketplace add /path/to/local/marketplace
|
|
113
|
+
|
|
114
|
+
# Remove a marketplace
|
|
115
|
+
allagents plugin marketplace remove <name>
|
|
116
|
+
|
|
117
|
+
# Update marketplace(s) from remote
|
|
118
|
+
allagents plugin marketplace update [name]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Plugin Commands
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# List available plugins from marketplaces
|
|
125
|
+
allagents plugin list [marketplace]
|
|
126
|
+
|
|
127
|
+
# Validate a plugin or marketplace structure
|
|
128
|
+
allagents plugin validate <path>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## .allagents/workspace.yaml
|
|
132
|
+
|
|
133
|
+
The workspace configuration file lives in `.allagents/workspace.yaml` and defines repositories, plugins, and target clients:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
repositories:
|
|
137
|
+
- path: ../my-project
|
|
138
|
+
owner: myorg
|
|
139
|
+
repo: my-project
|
|
140
|
+
description: Main project repository
|
|
141
|
+
- path: ../my-api
|
|
142
|
+
owner: myorg
|
|
143
|
+
repo: my-api
|
|
144
|
+
description: API service
|
|
145
|
+
|
|
146
|
+
plugins:
|
|
147
|
+
- code-review@claude-plugins-official # plugin@marketplace format
|
|
148
|
+
- context7@claude-plugins-official
|
|
149
|
+
- my-plugin@someuser/their-repo # fully qualified for custom marketplaces
|
|
150
|
+
|
|
151
|
+
clients:
|
|
152
|
+
- claude
|
|
153
|
+
- copilot
|
|
154
|
+
- cursor
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Plugin Spec Format
|
|
158
|
+
|
|
159
|
+
Plugins use the `plugin@marketplace` format:
|
|
160
|
+
|
|
161
|
+
| Format | Example | Description |
|
|
162
|
+
|--------|---------|-------------|
|
|
163
|
+
| Well-known | `code-review@claude-plugins-official` | Uses known marketplace mapping |
|
|
164
|
+
| owner/repo | `my-plugin@owner/repo` | Auto-registers GitHub repo, looks in `plugins/` |
|
|
165
|
+
| owner/repo/subpath | `my-plugin@owner/repo/extensions` | Looks in custom subdirectory |
|
|
166
|
+
|
|
167
|
+
The subpath format is useful when plugins aren't in the standard `plugins/` directory:
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
plugins:
|
|
171
|
+
- feature-dev@anthropics/claude-plugins-official/plugins # explicit plugins/ dir
|
|
172
|
+
- my-addon@someuser/repo/addons # custom addons/ dir
|
|
173
|
+
- tool@org/monorepo/packages/tools # nested path
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Well-Known Marketplaces
|
|
177
|
+
|
|
178
|
+
These marketplace names auto-resolve to their GitHub repos:
|
|
179
|
+
|
|
180
|
+
- `claude-plugins-official` → `anthropics/claude-plugins-official`
|
|
181
|
+
|
|
182
|
+
### Supported Clients
|
|
183
|
+
|
|
184
|
+
| Client | Skills | Agent File | Hooks | Commands |
|
|
185
|
+
|--------|--------|------------|-------|----------|
|
|
186
|
+
| claude | `.claude/skills/` | `CLAUDE.md` | `.claude/hooks/` | `.claude/commands/` |
|
|
187
|
+
| copilot | `.github/skills/` | `AGENTS.md` | No | No |
|
|
188
|
+
| codex | `.codex/skills/` | `AGENTS.md` | No | No |
|
|
189
|
+
| cursor | `.cursor/skills/` | `AGENTS.md` | No | No |
|
|
190
|
+
| opencode | `.opencode/skills/` | `AGENTS.md` | No | No |
|
|
191
|
+
| gemini | `.gemini/skills/` | `GEMINI.md` | No | No |
|
|
192
|
+
| factory | `.factory/skills/` | `AGENTS.md` | `.factory/hooks/` | No |
|
|
193
|
+
| ampcode | No | `AGENTS.md` | No | No |
|
|
194
|
+
|
|
195
|
+
> **Note:** Commands are a Claude-specific feature. Skills are the cross-client way to share reusable prompts.
|
|
196
|
+
|
|
197
|
+
## Marketplace Structure
|
|
198
|
+
|
|
199
|
+
Marketplaces contain multiple plugins:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
my-marketplace/
|
|
203
|
+
├── plugins/
|
|
204
|
+
│ ├── code-review/
|
|
205
|
+
│ │ ├── plugin.json
|
|
206
|
+
│ │ └── skills/
|
|
207
|
+
│ └── debugging/
|
|
208
|
+
│ ├── plugin.json
|
|
209
|
+
│ └── skills/
|
|
210
|
+
└── README.md
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Plugin Structure
|
|
214
|
+
|
|
215
|
+
Each plugin follows this structure:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
my-plugin/
|
|
219
|
+
├── plugin.json # Plugin metadata
|
|
220
|
+
├── skills/ # Skill directories with SKILL.md (all clients)
|
|
221
|
+
│ └── debugging/
|
|
222
|
+
│ └── SKILL.md
|
|
223
|
+
├── commands/ # Command files (.md) - Claude only
|
|
224
|
+
│ ├── build.md
|
|
225
|
+
│ └── deploy.md
|
|
226
|
+
├── hooks/ # Hook files (Claude/Factory only)
|
|
227
|
+
│ └── pre-commit.md
|
|
228
|
+
└── AGENTS.md # Agent configuration (optional)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Skill Validation
|
|
232
|
+
|
|
233
|
+
Skills must have a valid `SKILL.md` file with YAML frontmatter:
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
---
|
|
237
|
+
name: my-skill # Required: lowercase, alphanumeric + hyphens, max 64 chars
|
|
238
|
+
description: Description of the skill # Required
|
|
239
|
+
allowed-tools: # Optional
|
|
240
|
+
- Read
|
|
241
|
+
- Write
|
|
242
|
+
model: claude-3-opus # Optional
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
# Skill Content
|
|
246
|
+
|
|
247
|
+
Skill instructions go here...
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Storage Locations
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
~/.allagents/
|
|
254
|
+
├── marketplaces.json # Registry of marketplaces
|
|
255
|
+
└── marketplaces/ # Cloned marketplace repos
|
|
256
|
+
├── claude-plugins-official/
|
|
257
|
+
└── someuser-their-repo/
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Development
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Install dependencies
|
|
264
|
+
bun install
|
|
265
|
+
|
|
266
|
+
# Run in development
|
|
267
|
+
bun run dev workspace init test-ws
|
|
268
|
+
|
|
269
|
+
# Run tests
|
|
270
|
+
bun run test
|
|
271
|
+
|
|
272
|
+
# Type check
|
|
273
|
+
bun run typecheck
|
|
274
|
+
|
|
275
|
+
# Build
|
|
276
|
+
bun run build
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -1893,12 +1893,12 @@ var require_isexe = __commonJS((exports, module) => {
|
|
|
1893
1893
|
if (typeof Promise !== "function") {
|
|
1894
1894
|
throw new TypeError("callback not provided");
|
|
1895
1895
|
}
|
|
1896
|
-
return new Promise(function(
|
|
1896
|
+
return new Promise(function(resolve, reject) {
|
|
1897
1897
|
isexe(path, options2 || {}, function(er, is) {
|
|
1898
1898
|
if (er) {
|
|
1899
1899
|
reject(er);
|
|
1900
1900
|
} else {
|
|
1901
|
-
|
|
1901
|
+
resolve(is);
|
|
1902
1902
|
}
|
|
1903
1903
|
});
|
|
1904
1904
|
});
|
|
@@ -1960,27 +1960,27 @@ var require_which = __commonJS((exports, module) => {
|
|
|
1960
1960
|
opt = {};
|
|
1961
1961
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
1962
1962
|
const found = [];
|
|
1963
|
-
const step = (i2) => new Promise((
|
|
1963
|
+
const step = (i2) => new Promise((resolve, reject) => {
|
|
1964
1964
|
if (i2 === pathEnv.length)
|
|
1965
|
-
return opt.all && found.length ?
|
|
1965
|
+
return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
|
|
1966
1966
|
const ppRaw = pathEnv[i2];
|
|
1967
1967
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
1968
1968
|
const pCmd = path.join(pathPart, cmd);
|
|
1969
1969
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
1970
|
-
|
|
1970
|
+
resolve(subStep(p, i2, 0));
|
|
1971
1971
|
});
|
|
1972
|
-
const subStep = (p, i2, ii) => new Promise((
|
|
1972
|
+
const subStep = (p, i2, ii) => new Promise((resolve, reject) => {
|
|
1973
1973
|
if (ii === pathExt.length)
|
|
1974
|
-
return
|
|
1974
|
+
return resolve(step(i2 + 1));
|
|
1975
1975
|
const ext = pathExt[ii];
|
|
1976
1976
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
1977
1977
|
if (!er && is) {
|
|
1978
1978
|
if (opt.all)
|
|
1979
1979
|
found.push(p + ext);
|
|
1980
1980
|
else
|
|
1981
|
-
return
|
|
1981
|
+
return resolve(p + ext);
|
|
1982
1982
|
}
|
|
1983
|
-
return
|
|
1983
|
+
return resolve(subStep(p, i2, ii + 1));
|
|
1984
1984
|
});
|
|
1985
1985
|
});
|
|
1986
1986
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -11053,8 +11053,13 @@ var {
|
|
|
11053
11053
|
Help
|
|
11054
11054
|
} = import__.default;
|
|
11055
11055
|
|
|
11056
|
+
// src/cli/index.ts
|
|
11057
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
11058
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
11059
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
11060
|
+
|
|
11056
11061
|
// src/core/workspace.ts
|
|
11057
|
-
import { mkdir as mkdir5,
|
|
11062
|
+
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
11058
11063
|
import { existsSync as existsSync7 } from "node:fs";
|
|
11059
11064
|
import { join as join8, resolve as resolve5, dirname as dirname5, relative as relative2, sep } from "node:path";
|
|
11060
11065
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -11071,8 +11076,6 @@ var WORKSPACE_CONFIG_FILE = "workspace.yaml";
|
|
|
11071
11076
|
var WORKSPACE_CONFIG_PATH = `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`;
|
|
11072
11077
|
var WORKSPACE_RULES = `
|
|
11073
11078
|
<!-- WORKSPACE-RULES:START -->
|
|
11074
|
-
# Workspace Rules
|
|
11075
|
-
|
|
11076
11079
|
## Rule: Workspace Discovery
|
|
11077
11080
|
TRIGGER: Any task
|
|
11078
11081
|
ACTION: Read \`.allagents/workspace.yaml\` to get repository paths and project domains
|
|
@@ -17779,122 +17782,6 @@ ${errors2.join(`
|
|
|
17779
17782
|
|
|
17780
17783
|
// src/utils/plugin-path.ts
|
|
17781
17784
|
import { resolve, isAbsolute } from "node:path";
|
|
17782
|
-
function isGitHubUrl(source) {
|
|
17783
|
-
const explicitPatterns = [
|
|
17784
|
-
/^https?:\/\/github\.com\//,
|
|
17785
|
-
/^https?:\/\/www\.github\.com\//,
|
|
17786
|
-
/^github\.com\//,
|
|
17787
|
-
/^gh:/
|
|
17788
|
-
];
|
|
17789
|
-
if (explicitPatterns.some((pattern) => pattern.test(source))) {
|
|
17790
|
-
return true;
|
|
17791
|
-
}
|
|
17792
|
-
if (!source.startsWith(".") && !source.startsWith("/") && !source.includes("\\") && !/^[a-zA-Z]:/.test(source) && source.includes("/")) {
|
|
17793
|
-
const parts = source.split("/");
|
|
17794
|
-
if (parts.length >= 2 && parts[0] && parts[1]) {
|
|
17795
|
-
const validOwnerRepo = /^[a-zA-Z0-9_-]+$/;
|
|
17796
|
-
if (validOwnerRepo.test(parts[0]) && validOwnerRepo.test(parts[1])) {
|
|
17797
|
-
return true;
|
|
17798
|
-
}
|
|
17799
|
-
}
|
|
17800
|
-
}
|
|
17801
|
-
return false;
|
|
17802
|
-
}
|
|
17803
|
-
function parseGitHubUrl(url) {
|
|
17804
|
-
let normalized = url;
|
|
17805
|
-
if (normalized.startsWith("gh:")) {
|
|
17806
|
-
normalized = normalized.replace(/^gh:/, "https://github.com/");
|
|
17807
|
-
}
|
|
17808
|
-
if (normalized.startsWith("github.com/")) {
|
|
17809
|
-
normalized = `https://${normalized}`;
|
|
17810
|
-
}
|
|
17811
|
-
if (!normalized.includes("://") && !normalized.startsWith("github.com")) {
|
|
17812
|
-
const parts = normalized.split("/");
|
|
17813
|
-
if (parts.length >= 2) {
|
|
17814
|
-
const owner = parts[0];
|
|
17815
|
-
const repo = parts[1];
|
|
17816
|
-
const validOwnerRepo = /^[a-zA-Z0-9_-]+$/;
|
|
17817
|
-
if (owner && repo && validOwnerRepo.test(owner) && validOwnerRepo.test(repo)) {
|
|
17818
|
-
if (parts.length > 2) {
|
|
17819
|
-
const subpath = parts.slice(2).join("/");
|
|
17820
|
-
return { owner, repo, subpath };
|
|
17821
|
-
}
|
|
17822
|
-
return { owner, repo };
|
|
17823
|
-
}
|
|
17824
|
-
}
|
|
17825
|
-
return null;
|
|
17826
|
-
}
|
|
17827
|
-
const subpathPattern = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+?)\/tree\/[^/]+\/(.+)$/;
|
|
17828
|
-
const subpathMatch = normalized.match(subpathPattern);
|
|
17829
|
-
if (subpathMatch) {
|
|
17830
|
-
const owner = subpathMatch[1];
|
|
17831
|
-
const repo = subpathMatch[2]?.replace(/\.git$/, "");
|
|
17832
|
-
const subpath = subpathMatch[3];
|
|
17833
|
-
if (owner && repo && subpath) {
|
|
17834
|
-
return { owner, repo, subpath };
|
|
17835
|
-
}
|
|
17836
|
-
}
|
|
17837
|
-
const basicPattern = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/;
|
|
17838
|
-
const basicMatch = normalized.match(basicPattern);
|
|
17839
|
-
if (basicMatch) {
|
|
17840
|
-
const owner = basicMatch[1];
|
|
17841
|
-
const repo = basicMatch[2]?.replace(/\.git$/, "");
|
|
17842
|
-
if (owner && repo) {
|
|
17843
|
-
return { owner, repo };
|
|
17844
|
-
}
|
|
17845
|
-
}
|
|
17846
|
-
return null;
|
|
17847
|
-
}
|
|
17848
|
-
function normalizePluginPath(source, baseDir = process.cwd()) {
|
|
17849
|
-
if (isGitHubUrl(source)) {
|
|
17850
|
-
return source;
|
|
17851
|
-
}
|
|
17852
|
-
if (isAbsolute(source)) {
|
|
17853
|
-
return source;
|
|
17854
|
-
}
|
|
17855
|
-
return resolve(baseDir, source);
|
|
17856
|
-
}
|
|
17857
|
-
function parsePluginSource(source, baseDir = process.cwd()) {
|
|
17858
|
-
if (isGitHubUrl(source)) {
|
|
17859
|
-
const parsed = parseGitHubUrl(source);
|
|
17860
|
-
return {
|
|
17861
|
-
type: "github",
|
|
17862
|
-
original: source,
|
|
17863
|
-
normalized: source,
|
|
17864
|
-
...parsed?.owner && { owner: parsed.owner },
|
|
17865
|
-
...parsed?.repo && { repo: parsed.repo }
|
|
17866
|
-
};
|
|
17867
|
-
}
|
|
17868
|
-
return {
|
|
17869
|
-
type: "local",
|
|
17870
|
-
original: source,
|
|
17871
|
-
normalized: normalizePluginPath(source, baseDir)
|
|
17872
|
-
};
|
|
17873
|
-
}
|
|
17874
|
-
function getPluginCachePath(owner, repo) {
|
|
17875
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
|
|
17876
|
-
return resolve(homeDir, ".allagents", "plugins", "marketplaces", `${owner}-${repo}`);
|
|
17877
|
-
}
|
|
17878
|
-
function validatePluginSource(source) {
|
|
17879
|
-
if (!source || source.trim() === "") {
|
|
17880
|
-
return { valid: false, error: "Plugin source cannot be empty" };
|
|
17881
|
-
}
|
|
17882
|
-
if (isGitHubUrl(source)) {
|
|
17883
|
-
const parsed = parseGitHubUrl(source);
|
|
17884
|
-
if (!parsed) {
|
|
17885
|
-
return {
|
|
17886
|
-
valid: false,
|
|
17887
|
-
error: "Invalid GitHub URL format. Expected: https://github.com/owner/repo"
|
|
17888
|
-
};
|
|
17889
|
-
}
|
|
17890
|
-
}
|
|
17891
|
-
return { valid: true };
|
|
17892
|
-
}
|
|
17893
|
-
|
|
17894
|
-
// src/core/plugin.ts
|
|
17895
|
-
import { mkdir, readdir, stat } from "node:fs/promises";
|
|
17896
|
-
import { existsSync } from "node:fs";
|
|
17897
|
-
import { dirname, join, resolve as resolve2 } from "node:path";
|
|
17898
17785
|
|
|
17899
17786
|
// node_modules/execa/index.js
|
|
17900
17787
|
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
@@ -18769,7 +18656,7 @@ var setupTimeout = (spawned, { timeout, killSignal = "SIGTERM" }, spawnedPromise
|
|
|
18769
18656
|
return spawnedPromise;
|
|
18770
18657
|
}
|
|
18771
18658
|
let timeoutId;
|
|
18772
|
-
const timeoutPromise = new Promise((
|
|
18659
|
+
const timeoutPromise = new Promise((resolve, reject) => {
|
|
18773
18660
|
timeoutId = setTimeout(() => {
|
|
18774
18661
|
timeoutKill(spawned, killSignal, reject);
|
|
18775
18662
|
}, timeout);
|
|
@@ -19133,9 +19020,9 @@ var mergePromise = (spawned, promise) => {
|
|
|
19133
19020
|
Reflect.defineProperty(spawned, property, { ...descriptor, value });
|
|
19134
19021
|
}
|
|
19135
19022
|
};
|
|
19136
|
-
var getSpawnedPromise = (spawned) => new Promise((
|
|
19023
|
+
var getSpawnedPromise = (spawned) => new Promise((resolve, reject) => {
|
|
19137
19024
|
spawned.on("exit", (exitCode, signal) => {
|
|
19138
|
-
|
|
19025
|
+
resolve({ exitCode, signal });
|
|
19139
19026
|
});
|
|
19140
19027
|
spawned.on("error", (error) => {
|
|
19141
19028
|
reject(error);
|
|
@@ -19431,7 +19318,188 @@ function create$(options2) {
|
|
|
19431
19318
|
}
|
|
19432
19319
|
var $ = create$();
|
|
19433
19320
|
|
|
19321
|
+
// src/utils/plugin-path.ts
|
|
19322
|
+
function isGitHubUrl(source) {
|
|
19323
|
+
const explicitPatterns = [
|
|
19324
|
+
/^https?:\/\/github\.com\//,
|
|
19325
|
+
/^https?:\/\/www\.github\.com\//,
|
|
19326
|
+
/^github\.com\//,
|
|
19327
|
+
/^gh:/
|
|
19328
|
+
];
|
|
19329
|
+
if (explicitPatterns.some((pattern) => pattern.test(source))) {
|
|
19330
|
+
return true;
|
|
19331
|
+
}
|
|
19332
|
+
if (!source.startsWith(".") && !source.startsWith("/") && !source.includes("\\") && !/^[a-zA-Z]:/.test(source) && source.includes("/")) {
|
|
19333
|
+
const parts = source.split("/");
|
|
19334
|
+
if (parts.length >= 2 && parts[0] && parts[1]) {
|
|
19335
|
+
const validOwnerRepo = /^[a-zA-Z0-9_.-]+$/;
|
|
19336
|
+
if (validOwnerRepo.test(parts[0]) && validOwnerRepo.test(parts[1])) {
|
|
19337
|
+
return true;
|
|
19338
|
+
}
|
|
19339
|
+
}
|
|
19340
|
+
}
|
|
19341
|
+
return false;
|
|
19342
|
+
}
|
|
19343
|
+
function parseGitHubUrl(url) {
|
|
19344
|
+
let normalized = url;
|
|
19345
|
+
if (normalized.startsWith("gh:")) {
|
|
19346
|
+
normalized = normalized.replace(/^gh:/, "https://github.com/");
|
|
19347
|
+
}
|
|
19348
|
+
if (normalized.startsWith("github.com/")) {
|
|
19349
|
+
normalized = `https://${normalized}`;
|
|
19350
|
+
}
|
|
19351
|
+
if (!normalized.includes("://") && !normalized.startsWith("github.com")) {
|
|
19352
|
+
const parts = normalized.split("/");
|
|
19353
|
+
if (parts.length >= 2) {
|
|
19354
|
+
const owner = parts[0];
|
|
19355
|
+
const repo = parts[1];
|
|
19356
|
+
const validOwnerRepo = /^[a-zA-Z0-9_.-]+$/;
|
|
19357
|
+
if (owner && repo && validOwnerRepo.test(owner) && validOwnerRepo.test(repo)) {
|
|
19358
|
+
if (parts.length > 2) {
|
|
19359
|
+
const subpath = parts.slice(2).join("/");
|
|
19360
|
+
return { owner, repo, subpath };
|
|
19361
|
+
}
|
|
19362
|
+
return { owner, repo };
|
|
19363
|
+
}
|
|
19364
|
+
}
|
|
19365
|
+
return null;
|
|
19366
|
+
}
|
|
19367
|
+
const subpathPattern = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+?)\/tree\/[^/]+\/(.+)$/;
|
|
19368
|
+
const subpathMatch = normalized.match(subpathPattern);
|
|
19369
|
+
if (subpathMatch) {
|
|
19370
|
+
const owner = subpathMatch[1];
|
|
19371
|
+
const repo = subpathMatch[2]?.replace(/\.git$/, "");
|
|
19372
|
+
const subpath = subpathMatch[3];
|
|
19373
|
+
if (owner && repo && subpath) {
|
|
19374
|
+
return { owner, repo, subpath };
|
|
19375
|
+
}
|
|
19376
|
+
}
|
|
19377
|
+
const basicPattern = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/;
|
|
19378
|
+
const basicMatch = normalized.match(basicPattern);
|
|
19379
|
+
if (basicMatch) {
|
|
19380
|
+
const owner = basicMatch[1];
|
|
19381
|
+
const repo = basicMatch[2]?.replace(/\.git$/, "");
|
|
19382
|
+
if (owner && repo) {
|
|
19383
|
+
return { owner, repo };
|
|
19384
|
+
}
|
|
19385
|
+
}
|
|
19386
|
+
return null;
|
|
19387
|
+
}
|
|
19388
|
+
function normalizePluginPath(source, baseDir = process.cwd()) {
|
|
19389
|
+
if (isGitHubUrl(source)) {
|
|
19390
|
+
return source;
|
|
19391
|
+
}
|
|
19392
|
+
if (isAbsolute(source)) {
|
|
19393
|
+
return source;
|
|
19394
|
+
}
|
|
19395
|
+
return resolve(baseDir, source);
|
|
19396
|
+
}
|
|
19397
|
+
function parsePluginSource(source, baseDir = process.cwd()) {
|
|
19398
|
+
if (isGitHubUrl(source)) {
|
|
19399
|
+
const parsed = parseGitHubUrl(source);
|
|
19400
|
+
return {
|
|
19401
|
+
type: "github",
|
|
19402
|
+
original: source,
|
|
19403
|
+
normalized: source,
|
|
19404
|
+
...parsed?.owner && { owner: parsed.owner },
|
|
19405
|
+
...parsed?.repo && { repo: parsed.repo }
|
|
19406
|
+
};
|
|
19407
|
+
}
|
|
19408
|
+
return {
|
|
19409
|
+
type: "local",
|
|
19410
|
+
original: source,
|
|
19411
|
+
normalized: normalizePluginPath(source, baseDir)
|
|
19412
|
+
};
|
|
19413
|
+
}
|
|
19414
|
+
function getPluginCachePath(owner, repo) {
|
|
19415
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
|
|
19416
|
+
return resolve(homeDir, ".allagents", "plugins", "marketplaces", `${owner}-${repo}`);
|
|
19417
|
+
}
|
|
19418
|
+
function validatePluginSource(source) {
|
|
19419
|
+
if (!source || source.trim() === "") {
|
|
19420
|
+
return { valid: false, error: "Plugin source cannot be empty" };
|
|
19421
|
+
}
|
|
19422
|
+
if (isGitHubUrl(source)) {
|
|
19423
|
+
const parsed = parseGitHubUrl(source);
|
|
19424
|
+
if (!parsed) {
|
|
19425
|
+
return {
|
|
19426
|
+
valid: false,
|
|
19427
|
+
error: "Invalid GitHub URL format. Expected: https://github.com/owner/repo"
|
|
19428
|
+
};
|
|
19429
|
+
}
|
|
19430
|
+
}
|
|
19431
|
+
return { valid: true };
|
|
19432
|
+
}
|
|
19433
|
+
async function verifyGitHubUrlExists(source) {
|
|
19434
|
+
const parsed = parseGitHubUrl(source);
|
|
19435
|
+
if (!parsed) {
|
|
19436
|
+
return {
|
|
19437
|
+
exists: false,
|
|
19438
|
+
error: "Invalid GitHub URL format. Expected: https://github.com/owner/repo"
|
|
19439
|
+
};
|
|
19440
|
+
}
|
|
19441
|
+
const { owner, repo, subpath } = parsed;
|
|
19442
|
+
try {
|
|
19443
|
+
await execa("gh", ["--version"]);
|
|
19444
|
+
} catch {
|
|
19445
|
+
return {
|
|
19446
|
+
exists: false,
|
|
19447
|
+
error: "gh CLI not installed. Install from: https://cli.github.com"
|
|
19448
|
+
};
|
|
19449
|
+
}
|
|
19450
|
+
try {
|
|
19451
|
+
await execa("gh", ["repo", "view", `${owner}/${repo}`, "--json", "name"]);
|
|
19452
|
+
} catch (error) {
|
|
19453
|
+
if (error instanceof Error) {
|
|
19454
|
+
const errorMessage = error.message.toLowerCase();
|
|
19455
|
+
if (errorMessage.includes("not found") || errorMessage.includes("404") || errorMessage.includes("could not resolve to a repository")) {
|
|
19456
|
+
return {
|
|
19457
|
+
exists: false,
|
|
19458
|
+
error: `Repository not found: ${owner}/${repo}`
|
|
19459
|
+
};
|
|
19460
|
+
}
|
|
19461
|
+
if (errorMessage.includes("auth") || errorMessage.includes("authentication")) {
|
|
19462
|
+
return {
|
|
19463
|
+
exists: false,
|
|
19464
|
+
error: "GitHub authentication required. Run: gh auth login"
|
|
19465
|
+
};
|
|
19466
|
+
}
|
|
19467
|
+
}
|
|
19468
|
+
return {
|
|
19469
|
+
exists: false,
|
|
19470
|
+
error: `Failed to verify repository: ${error instanceof Error ? error.message : String(error)}`
|
|
19471
|
+
};
|
|
19472
|
+
}
|
|
19473
|
+
if (subpath) {
|
|
19474
|
+
try {
|
|
19475
|
+
await execa("gh", [
|
|
19476
|
+
"api",
|
|
19477
|
+
`repos/${owner}/${repo}/contents/${subpath}`,
|
|
19478
|
+
"--silent"
|
|
19479
|
+
]);
|
|
19480
|
+
} catch (error) {
|
|
19481
|
+
if (error instanceof Error) {
|
|
19482
|
+
const errorMessage = error.message.toLowerCase();
|
|
19483
|
+
if (errorMessage.includes("not found") || errorMessage.includes("404")) {
|
|
19484
|
+
return {
|
|
19485
|
+
exists: false,
|
|
19486
|
+
error: `Path not found in repository: ${owner}/${repo}/${subpath}`
|
|
19487
|
+
};
|
|
19488
|
+
}
|
|
19489
|
+
}
|
|
19490
|
+
return {
|
|
19491
|
+
exists: false,
|
|
19492
|
+
error: `Failed to verify path: ${error instanceof Error ? error.message : String(error)}`
|
|
19493
|
+
};
|
|
19494
|
+
}
|
|
19495
|
+
}
|
|
19496
|
+
return { exists: true };
|
|
19497
|
+
}
|
|
19498
|
+
|
|
19434
19499
|
// src/core/plugin.ts
|
|
19500
|
+
import { mkdir, readdir, stat } from "node:fs/promises";
|
|
19501
|
+
import { existsSync } from "node:fs";
|
|
19502
|
+
import { dirname, join, resolve as resolve2 } from "node:path";
|
|
19435
19503
|
async function fetchPlugin(url, options2 = {}) {
|
|
19436
19504
|
const { force = false } = options2;
|
|
19437
19505
|
const validation = validatePluginSource(url);
|
|
@@ -19579,7 +19647,6 @@ async function resolveGlobPatterns(sourceRoot, patterns) {
|
|
|
19579
19647
|
var CLIENT_MAPPINGS = {
|
|
19580
19648
|
claude: {
|
|
19581
19649
|
commandsPath: ".claude/commands/",
|
|
19582
|
-
commandsExt: ".md",
|
|
19583
19650
|
skillsPath: ".claude/skills/",
|
|
19584
19651
|
agentsPath: ".claude/agents/",
|
|
19585
19652
|
agentFile: "CLAUDE.md",
|
|
@@ -19587,46 +19654,32 @@ var CLIENT_MAPPINGS = {
|
|
|
19587
19654
|
hooksPath: ".claude/hooks/"
|
|
19588
19655
|
},
|
|
19589
19656
|
copilot: {
|
|
19590
|
-
commandsPath: ".github/prompts/",
|
|
19591
|
-
commandsExt: ".prompt.md",
|
|
19592
19657
|
skillsPath: ".github/skills/",
|
|
19593
19658
|
agentFile: "AGENTS.md"
|
|
19594
19659
|
},
|
|
19595
19660
|
codex: {
|
|
19596
|
-
commandsPath: ".codex/prompts/",
|
|
19597
|
-
commandsExt: ".md",
|
|
19598
19661
|
skillsPath: ".codex/skills/",
|
|
19599
19662
|
agentFile: "AGENTS.md"
|
|
19600
19663
|
},
|
|
19601
19664
|
cursor: {
|
|
19602
|
-
commandsPath: ".cursor/commands/",
|
|
19603
|
-
commandsExt: ".md",
|
|
19604
19665
|
skillsPath: ".cursor/skills/",
|
|
19605
19666
|
agentFile: "AGENTS.md"
|
|
19606
19667
|
},
|
|
19607
19668
|
opencode: {
|
|
19608
|
-
commandsPath: ".opencode/commands/",
|
|
19609
|
-
commandsExt: ".md",
|
|
19610
19669
|
skillsPath: ".opencode/skills/",
|
|
19611
19670
|
agentFile: "AGENTS.md"
|
|
19612
19671
|
},
|
|
19613
19672
|
gemini: {
|
|
19614
|
-
commandsPath: ".gemini/commands/",
|
|
19615
|
-
commandsExt: ".md",
|
|
19616
19673
|
skillsPath: ".gemini/skills/",
|
|
19617
19674
|
agentFile: "GEMINI.md",
|
|
19618
19675
|
agentFileFallback: "AGENTS.md"
|
|
19619
19676
|
},
|
|
19620
19677
|
factory: {
|
|
19621
|
-
commandsPath: ".factory/commands/",
|
|
19622
|
-
commandsExt: ".md",
|
|
19623
19678
|
skillsPath: ".factory/skills/",
|
|
19624
19679
|
agentFile: "AGENTS.md",
|
|
19625
19680
|
hooksPath: ".factory/hooks/"
|
|
19626
19681
|
},
|
|
19627
19682
|
ampcode: {
|
|
19628
|
-
commandsPath: "",
|
|
19629
|
-
commandsExt: ".md",
|
|
19630
19683
|
skillsPath: "",
|
|
19631
19684
|
agentFile: "AGENTS.md"
|
|
19632
19685
|
}
|
|
@@ -19698,20 +19751,27 @@ async function validateSkill(skillDir) {
|
|
|
19698
19751
|
}
|
|
19699
19752
|
|
|
19700
19753
|
// src/core/transform.ts
|
|
19701
|
-
async function
|
|
19702
|
-
const
|
|
19754
|
+
async function ensureWorkspaceRules(filePath, rules) {
|
|
19755
|
+
const rulesContent = rules ?? WORKSPACE_RULES;
|
|
19703
19756
|
const startMarker = "<!-- WORKSPACE-RULES:START -->";
|
|
19704
19757
|
const endMarker = "<!-- WORKSPACE-RULES:END -->";
|
|
19758
|
+
if (!existsSync3(filePath)) {
|
|
19759
|
+
await writeFile(filePath, `${rulesContent.trim()}
|
|
19760
|
+
`, "utf-8");
|
|
19761
|
+
return;
|
|
19762
|
+
}
|
|
19763
|
+
const content = await readFile3(filePath, "utf-8");
|
|
19705
19764
|
const startIndex = content.indexOf(startMarker);
|
|
19706
19765
|
const endIndex = content.indexOf(endMarker);
|
|
19707
19766
|
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
19708
19767
|
const before = content.substring(0, startIndex);
|
|
19709
19768
|
const after = content.substring(endIndex + endMarker.length);
|
|
19710
|
-
await writeFile(filePath, before +
|
|
19769
|
+
await writeFile(filePath, before + rulesContent.trim() + after, "utf-8");
|
|
19711
19770
|
} else {
|
|
19712
|
-
await writeFile(filePath, content +
|
|
19771
|
+
await writeFile(filePath, content + rulesContent, "utf-8");
|
|
19713
19772
|
}
|
|
19714
19773
|
}
|
|
19774
|
+
var injectWorkspaceRules = ensureWorkspaceRules;
|
|
19715
19775
|
async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
|
|
19716
19776
|
const { dryRun = false } = options2;
|
|
19717
19777
|
const mapping = CLIENT_MAPPINGS[client];
|
|
@@ -19731,11 +19791,7 @@ async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
|
|
|
19731
19791
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
19732
19792
|
const copyPromises = mdFiles.map(async (file) => {
|
|
19733
19793
|
const sourcePath = join4(sourceDir, file);
|
|
19734
|
-
|
|
19735
|
-
if (mapping.commandsExt === ".prompt.md" && !file.endsWith(".prompt.md")) {
|
|
19736
|
-
destFileName = file.replace(/\.md$/, ".prompt.md");
|
|
19737
|
-
}
|
|
19738
|
-
const destPath = join4(destDir, destFileName);
|
|
19794
|
+
const destPath = join4(destDir, file);
|
|
19739
19795
|
if (dryRun) {
|
|
19740
19796
|
return { source: sourcePath, destination: destPath, action: "copied" };
|
|
19741
19797
|
}
|
|
@@ -20744,11 +20800,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
|
|
|
20744
20800
|
workspaceYamlContent = await readFile6(defaultYamlPath, "utf-8");
|
|
20745
20801
|
}
|
|
20746
20802
|
await writeFile4(configPath, workspaceYamlContent, "utf-8");
|
|
20747
|
-
const defaultAgentsPath = join8(defaultTemplatePath, "AGENTS.md");
|
|
20748
20803
|
const targetAgentsPath = join8(absoluteTarget, "AGENTS.md");
|
|
20749
|
-
|
|
20750
|
-
await cp2(defaultAgentsPath, targetAgentsPath);
|
|
20751
|
-
}
|
|
20804
|
+
await ensureWorkspaceRules(targetAgentsPath);
|
|
20752
20805
|
console.log(`✓ Workspace created at: ${absoluteTarget}`);
|
|
20753
20806
|
console.log(`
|
|
20754
20807
|
Syncing plugins...`);
|
|
@@ -20883,6 +20936,13 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
|
|
|
20883
20936
|
error: validation.error || "Invalid GitHub URL"
|
|
20884
20937
|
};
|
|
20885
20938
|
}
|
|
20939
|
+
const verifyResult = await verifyGitHubUrlExists(plugin);
|
|
20940
|
+
if (!verifyResult.exists) {
|
|
20941
|
+
return {
|
|
20942
|
+
success: false,
|
|
20943
|
+
error: verifyResult.error || `GitHub URL not found: ${plugin}`
|
|
20944
|
+
};
|
|
20945
|
+
}
|
|
20886
20946
|
} else {
|
|
20887
20947
|
const fullPath = join10(workspacePath, plugin);
|
|
20888
20948
|
if (!existsSync9(fullPath) && !existsSync9(plugin)) {
|
|
@@ -21332,9 +21392,81 @@ pluginCommand.command("validate <path>").description("Validate plugin structure
|
|
|
21332
21392
|
console.log("(validation not yet implemented)");
|
|
21333
21393
|
});
|
|
21334
21394
|
|
|
21395
|
+
// src/cli/commands/update.ts
|
|
21396
|
+
function detectPackageManagerFromPath(scriptPath) {
|
|
21397
|
+
if (scriptPath.includes(".bun")) {
|
|
21398
|
+
return "bun";
|
|
21399
|
+
}
|
|
21400
|
+
return "npm";
|
|
21401
|
+
}
|
|
21402
|
+
function detectPackageManager() {
|
|
21403
|
+
return detectPackageManagerFromPath(process.argv[1] ?? "");
|
|
21404
|
+
}
|
|
21405
|
+
async function getCurrentVersion() {
|
|
21406
|
+
try {
|
|
21407
|
+
const { createRequire: createRequire2 } = await import("node:module");
|
|
21408
|
+
const require2 = createRequire2(import.meta.url);
|
|
21409
|
+
const pkg = require2("../../../package.json");
|
|
21410
|
+
return pkg.version;
|
|
21411
|
+
} catch {
|
|
21412
|
+
return "unknown";
|
|
21413
|
+
}
|
|
21414
|
+
}
|
|
21415
|
+
var updateCommand = new Command("update").description("Update allagents to the latest version").option("--npm", "Force update using npm").option("--bun", "Force update using bun").action(async (options2) => {
|
|
21416
|
+
try {
|
|
21417
|
+
let packageManager;
|
|
21418
|
+
if (options2.npm && options2.bun) {
|
|
21419
|
+
console.error("Error: Cannot specify both --npm and --bun");
|
|
21420
|
+
process.exit(1);
|
|
21421
|
+
}
|
|
21422
|
+
if (options2.npm) {
|
|
21423
|
+
packageManager = "npm";
|
|
21424
|
+
} else if (options2.bun) {
|
|
21425
|
+
packageManager = "bun";
|
|
21426
|
+
} else {
|
|
21427
|
+
packageManager = detectPackageManager();
|
|
21428
|
+
}
|
|
21429
|
+
const currentVersion = await getCurrentVersion();
|
|
21430
|
+
console.log(`Current version: ${currentVersion}`);
|
|
21431
|
+
console.log(`Updating allagents using ${packageManager}...
|
|
21432
|
+
`);
|
|
21433
|
+
const args = packageManager === "npm" ? ["install", "-g", "allagents@latest"] : ["add", "-g", "allagents@latest"];
|
|
21434
|
+
const result = await execa(packageManager, args, {
|
|
21435
|
+
stdio: "inherit"
|
|
21436
|
+
});
|
|
21437
|
+
if (result.exitCode === 0) {
|
|
21438
|
+
try {
|
|
21439
|
+
const versionResult = await execa("allagents", ["--version"]);
|
|
21440
|
+
const newVersion = versionResult.stdout.trim();
|
|
21441
|
+
console.log(`
|
|
21442
|
+
Update complete: ${currentVersion} → ${newVersion}`);
|
|
21443
|
+
} catch {
|
|
21444
|
+
console.log(`
|
|
21445
|
+
Update complete.`);
|
|
21446
|
+
}
|
|
21447
|
+
}
|
|
21448
|
+
} catch (error) {
|
|
21449
|
+
if (error instanceof Error) {
|
|
21450
|
+
if (error.message.includes("ENOENT") || error.message.includes("not found")) {
|
|
21451
|
+
const detected = detectPackageManager();
|
|
21452
|
+
const alternative = detected === "npm" ? "bun" : "npm";
|
|
21453
|
+
console.error(`Error: ${detected} not found. Try using --${alternative} flag.`);
|
|
21454
|
+
} else {
|
|
21455
|
+
console.error(`Error: ${error.message}`);
|
|
21456
|
+
}
|
|
21457
|
+
process.exit(1);
|
|
21458
|
+
}
|
|
21459
|
+
throw error;
|
|
21460
|
+
}
|
|
21461
|
+
});
|
|
21462
|
+
|
|
21335
21463
|
// src/cli/index.ts
|
|
21464
|
+
var __dirname2 = dirname6(fileURLToPath3(import.meta.url));
|
|
21465
|
+
var packageJsonPath = join11(__dirname2, "..", "package.json");
|
|
21466
|
+
var packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
21336
21467
|
var program2 = new Command;
|
|
21337
|
-
program2.name("allagents").description("CLI tool for managing multi-repo AI agent workspaces with plugin synchronization").version(
|
|
21468
|
+
program2.name("allagents").description("CLI tool for managing multi-repo AI agent workspaces with plugin synchronization").version(packageJson.version);
|
|
21338
21469
|
program2.addCommand(workspaceCommand);
|
|
21339
21470
|
program2.addCommand(pluginCommand);
|
|
21471
|
+
program2.addCommand(updateCommand);
|
|
21340
21472
|
program2.parse();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Workspace root files (optional)
|
|
2
|
-
# workspace:
|
|
3
|
-
# source: ./path/to/config # local path, GitHub URL, or plugin@marketplace
|
|
4
|
-
# files:
|
|
5
|
-
# - CLAUDE.md
|
|
6
|
-
# - AGENTS.md
|
|
7
|
-
# - source: docs/CLAUDE.md # explicit source/dest mapping
|
|
8
|
-
# dest: CLAUDE.md
|
|
9
|
-
|
|
10
|
-
repositories: []
|
|
11
|
-
|
|
12
|
-
plugins: []
|
|
13
|
-
|
|
14
|
-
clients:
|
|
15
|
-
- claude
|
|
1
|
+
# Workspace root files (optional)
|
|
2
|
+
# workspace:
|
|
3
|
+
# source: ./path/to/config # local path, GitHub URL, or plugin@marketplace
|
|
4
|
+
# files:
|
|
5
|
+
# - CLAUDE.md
|
|
6
|
+
# - AGENTS.md
|
|
7
|
+
# - source: docs/CLAUDE.md # explicit source/dest mapping
|
|
8
|
+
# dest: CLAUDE.md
|
|
9
|
+
|
|
10
|
+
repositories: []
|
|
11
|
+
|
|
12
|
+
plugins: []
|
|
13
|
+
|
|
14
|
+
clients:
|
|
15
|
+
- claude
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Workspace
|
|
2
|
-
|
|
3
|
-
## Development
|
|
4
|
-
|
|
5
|
-
To run tests:
|
|
6
|
-
```bash
|
|
7
|
-
npm test
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## Guidelines
|
|
11
|
-
|
|
12
|
-
- Add project-specific coding conventions here
|
|
13
|
-
- Document architectural decisions
|
|
14
|
-
- Include workflow preferences
|
|
1
|
+
# Workspace
|
|
2
|
+
|
|
3
|
+
## Development
|
|
4
|
+
|
|
5
|
+
To run tests:
|
|
6
|
+
```bash
|
|
7
|
+
npm test
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Guidelines
|
|
11
|
+
|
|
12
|
+
- Add project-specific coding conventions here
|
|
13
|
+
- Document architectural decisions
|
|
14
|
+
- Include workflow preferences
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allagents",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"format": "biome format --write src",
|
|
25
25
|
"check": "biome check src",
|
|
26
26
|
"check:fix": "biome check --write src",
|
|
27
|
-
"prepare": "bun run build && bunx prek install -t pre-push"
|
|
27
|
+
"prepare": "bun run build && (test -d .git && bunx prek install -t pre-push || true)"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"cli",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@types/bun": "latest",
|
|
54
54
|
"@types/js-yaml": "^4.0.9",
|
|
55
55
|
"@types/node": "^20.11.5",
|
|
56
|
+
"shx": "^0.4.0",
|
|
56
57
|
"typescript": "^5.3.3"
|
|
57
58
|
},
|
|
58
59
|
"engines": {
|