@leeovery/claude-manager 2.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Lee Overy
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 ADDED
@@ -0,0 +1,244 @@
1
+ <h1 align="center">Claude Manager</h1>
2
+
3
+ <p align="center">
4
+ <strong>npm Package for Managing Claude Code Skills & Commands</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="#about">About</a> •
9
+ <a href="#installation">Installation</a> •
10
+ <a href="#how-it-works">How It Works</a> •
11
+ <a href="#cli-commands">CLI Commands</a> •
12
+ <a href="#creating-plugins">Creating Plugins</a> •
13
+ <a href="#available-plugins">Available Plugins</a> •
14
+ <a href="#troubleshooting">Troubleshooting</a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## Versions
20
+
21
+ | Version | Package Manager | Status | Branch |
22
+ |---------|----------------|--------|--------|
23
+ | 2.x | npm | **Active** | `main` |
24
+ | 1.x | Composer | Deprecated | [`v1`](https://github.com/leeovery/claude-manager/tree/v1) |
25
+
26
+ > **Note:** This package is installed automatically as a dependency of plugins.
27
+ > To migrate from v1, update your plugins to their v2 versions (npm-based).
28
+
29
+ ---
30
+
31
+ ## About
32
+
33
+ Claude Manager is an npm package that automatically manages [Claude Code](https://claude.ai/code) skills, commands, agents, and hooks across your projects.
34
+
35
+ **What it does:**
36
+ - Automatically installs skills, commands, agents, and hooks from plugin packages into your project's `.claude/` directory
37
+ - Copies assets so they're committed to your repository and available immediately
38
+ - Works with any project that has a `package.json` (Node.js, Laravel, Nuxt, etc.)
39
+ - Provides CLI tools for listing and managing installed plugins
40
+
41
+ **Why use it?**
42
+
43
+ Instead of manually copying skill files between projects, you can install them as npm packages and let the manager handle the rest. Update a skill package once, run `npm update`, and all your projects get the improvements.
44
+
45
+ ## Installation
46
+
47
+ The manager is typically installed automatically as a dependency of plugin packages. When you install a Claude plugin:
48
+
49
+ ```bash
50
+ npm install @your-org/claude-your-plugin
51
+ ```
52
+
53
+ The manager will:
54
+ 1. Install itself (as a dependency of the plugin)
55
+ 2. Add a `prepare` hook to your `package.json`
56
+ 3. Copy the plugin's assets to `.claude/`
57
+
58
+ That's it. Future `npm install` and `npm update` runs will automatically sync all plugins.
59
+
60
+ ## How It Works
61
+
62
+ 1. Plugin packages have `@leeovery/claude-manager` as a dependency
63
+ 2. Plugins register themselves via their `postinstall` script
64
+ 3. The manager copies skills, commands, agents, and hooks to `.claude/`
65
+ 4. A manifest (`.claude/.plugins-manifest.json`) tracks what's installed
66
+ 5. A `prepare` hook ensures plugins sync on both `npm install` and `npm update`
67
+ 6. Claude Code discovers them automatically
68
+
69
+ **After installation, your project structure looks like:**
70
+
71
+ ```
72
+ your-project/
73
+ ├── .claude/
74
+ │ ├── .plugins-manifest.json
75
+ │ ├── skills/
76
+ │ │ └── laravel-actions/
77
+ │ │ └── skill.md
78
+ │ ├── commands/
79
+ │ │ └── artisan-make.md
80
+ │ ├── agents/
81
+ │ │ └── code-reviewer.md
82
+ │ └── hooks/
83
+ │ └── pre-commit.sh
84
+ ├── node_modules/
85
+ │ └── @your-org/
86
+ │ └── claude-your-plugin/
87
+ └── package.json
88
+ ```
89
+
90
+ ## CLI Commands
91
+
92
+ The manager provides a CLI tool for managing plugins:
93
+
94
+ | Command | Description |
95
+ |---------|-------------|
96
+ | `npx claude-plugins list` | Show all installed plugins and their assets |
97
+ | `npx claude-plugins install` | Sync all plugins from manifest (runs automatically) |
98
+ | `npx claude-plugins add <package>` | Manually add a plugin |
99
+ | `npx claude-plugins remove <package>` | Remove a plugin and its assets |
100
+
101
+ ## Creating Plugins
102
+
103
+ Want to create your own skill or command packages?
104
+
105
+ ### Plugin Requirements
106
+
107
+ 1. Have `@leeovery/claude-manager` as a dependency
108
+ 2. Add a `postinstall` script that calls `claude-plugins add`
109
+ 3. Include asset directories (`skills/`, `commands/`, `agents/`, `hooks/`)
110
+
111
+ ### Example package.json
112
+
113
+ ```json
114
+ {
115
+ "name": "@your-org/claude-your-skills",
116
+ "version": "1.0.0",
117
+ "description": "Your custom skills for Claude Code",
118
+ "license": "MIT",
119
+ "dependencies": {
120
+ "@leeovery/claude-manager": "^2.0.0"
121
+ },
122
+ "scripts": {
123
+ "postinstall": "claude-plugins add"
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### Plugin Structure
129
+
130
+ ```
131
+ your-plugin/
132
+ ├── skills/
133
+ │ ├── skill-one/
134
+ │ │ └── skill.md
135
+ │ └── skill-two/
136
+ │ └── skill.md
137
+ ├── commands/
138
+ │ ├── command-one.md
139
+ │ └── command-two.md
140
+ ├── agents/
141
+ │ └── agent-one.md
142
+ ├── hooks/
143
+ │ └── pre-commit.sh
144
+ └── package.json
145
+ ```
146
+
147
+ The manager auto-discovers `skills/`, `commands/`, `agents/`, and `hooks/` directories—no additional configuration needed.
148
+
149
+ ## Available Plugins
150
+
151
+ | Package | Description |
152
+ |---------|-------------|
153
+ | Coming soon | Be the first to create a plugin! |
154
+
155
+ ## Manifest
156
+
157
+ The manager tracks installed plugins in `.claude/.plugins-manifest.json`:
158
+
159
+ ```json
160
+ {
161
+ "plugins": {
162
+ "@your-org/claude-laravel": {
163
+ "version": "1.0.0",
164
+ "files": [
165
+ "skills/laravel-actions",
166
+ "commands/artisan-make.md"
167
+ ]
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ This file should be committed to your repository. It ensures:
174
+ - Plugins are synced correctly on `npm install` and `npm update`
175
+ - Old files are cleaned up when plugins are updated or removed
176
+ - You can see what's installed at a glance
177
+
178
+ ## Troubleshooting
179
+
180
+ ### Assets not copied
181
+
182
+ Run the install command manually:
183
+
184
+ ```bash
185
+ npx claude-plugins install
186
+ ```
187
+
188
+ ### Skills not showing in Claude Code
189
+
190
+ Check that `.claude/` directories exist and contain files:
191
+
192
+ ```bash
193
+ ls -la .claude/skills/
194
+ ls -la .claude/commands/
195
+ ls -la .claude/agents/
196
+ ls -la .claude/hooks/
197
+ ```
198
+
199
+ ### Plugin not detected
200
+
201
+ Verify the plugin's package.json has:
202
+ - `@leeovery/claude-manager` as a dependency
203
+ - A `postinstall` script that calls `claude-plugins add`
204
+ - A `skills/`, `commands/`, `agents/`, or `hooks/` directory with content
205
+
206
+ ### Prepare hook not added
207
+
208
+ If your project's `package.json` doesn't have the prepare hook, add it manually:
209
+
210
+ ```json
211
+ {
212
+ "scripts": {
213
+ "prepare": "claude-plugins install"
214
+ }
215
+ }
216
+ ```
217
+
218
+ This ensures plugins sync on both `npm install` and `npm update`.
219
+
220
+ ## Requirements
221
+
222
+ - Node.js >= 18.0.0
223
+ - npm, pnpm, yarn, or bun
224
+
225
+ ## Contributing
226
+
227
+ Contributions are welcome! Whether it's:
228
+
229
+ - Bug fixes
230
+ - Documentation improvements
231
+ - New features
232
+ - Discussion about approaches
233
+
234
+ Please open an issue first to discuss significant changes.
235
+
236
+ ## License
237
+
238
+ MIT License. See [LICENSE](LICENSE) for details.
239
+
240
+ ---
241
+
242
+ <p align="center">
243
+ <sub>Built by <a href="https://github.com/leeovery">Lee Overy</a></sub>
244
+ </p>
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/hooks.ts
4
+ import { existsSync, readFileSync, writeFileSync } from "fs";
5
+ import { join } from "path";
6
+ var HOOK_COMMAND = "claude-plugins install";
7
+ var HOOK_NAME = "prepare";
8
+ function injectPrepareHook(projectRoot) {
9
+ const packageJsonPath = join(projectRoot, "package.json");
10
+ if (!existsSync(packageJsonPath)) {
11
+ return false;
12
+ }
13
+ try {
14
+ const content = readFileSync(packageJsonPath, "utf-8");
15
+ const pkg = JSON.parse(content);
16
+ if (!pkg.scripts) {
17
+ pkg.scripts = {};
18
+ }
19
+ const existingHook = pkg.scripts[HOOK_NAME];
20
+ if (existingHook?.includes(HOOK_COMMAND)) {
21
+ return false;
22
+ }
23
+ if (existingHook) {
24
+ pkg.scripts[HOOK_NAME] = `${existingHook} && ${HOOK_COMMAND}`;
25
+ } else {
26
+ pkg.scripts[HOOK_NAME] = HOOK_COMMAND;
27
+ }
28
+ writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ export {
36
+ injectPrepareHook
37
+ };
38
+ //# sourceMappingURL=chunk-2SM5GQ6P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/hooks.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst HOOK_COMMAND = 'claude-plugins install';\n// Use 'prepare' instead of 'postinstall' because prepare runs after BOTH:\n// - npm install\n// - npm update\n// This ensures plugins are always synced regardless of which command is used.\nconst HOOK_NAME = 'prepare';\n\ninterface PackageJson {\n scripts?: Record<string, string>;\n [key: string]: unknown;\n}\n\nexport function injectPrepareHook(projectRoot: string): boolean {\n const packageJsonPath = join(projectRoot, 'package.json');\n\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n try {\n const content = readFileSync(packageJsonPath, 'utf-8');\n const pkg: PackageJson = JSON.parse(content);\n\n if (!pkg.scripts) {\n pkg.scripts = {};\n }\n\n const existingHook = pkg.scripts[HOOK_NAME];\n\n // Check if already has our hook\n if (existingHook?.includes(HOOK_COMMAND)) {\n return false; // Already installed\n }\n\n // Add or append to hook\n if (existingHook) {\n pkg.scripts[HOOK_NAME] = `${existingHook} && ${HOOK_COMMAND}`;\n } else {\n pkg.scripts[HOOK_NAME] = HOOK_COMMAND;\n }\n\n // Write back with same formatting\n writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\\n');\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport function hasPrepareHook(projectRoot: string): boolean {\n const packageJsonPath = join(projectRoot, 'package.json');\n\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n try {\n const content = readFileSync(packageJsonPath, 'utf-8');\n const pkg: PackageJson = JSON.parse(content);\n\n return pkg.scripts?.[HOOK_NAME]?.includes(HOOK_COMMAND) ?? false;\n } catch {\n return false;\n }\n}\n\nexport function removePrepareHook(projectRoot: string): boolean {\n const packageJsonPath = join(projectRoot, 'package.json');\n\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n try {\n const content = readFileSync(packageJsonPath, 'utf-8');\n const pkg: PackageJson = JSON.parse(content);\n\n if (!pkg.scripts?.[HOOK_NAME]) {\n return false;\n }\n\n const hook = pkg.scripts[HOOK_NAME];\n\n if (!hook.includes(HOOK_COMMAND)) {\n return false;\n }\n\n // Remove our hook from the script\n let newHook = hook\n .replace(` && ${HOOK_COMMAND}`, '')\n .replace(`${HOOK_COMMAND} && `, '')\n .replace(HOOK_COMMAND, '');\n\n if (newHook.trim() === '') {\n delete pkg.scripts[HOOK_NAME];\n } else {\n pkg.scripts[HOOK_NAME] = newHook.trim();\n }\n\n // Clean up empty scripts object\n if (Object.keys(pkg.scripts).length === 0) {\n delete pkg.scripts;\n }\n\n writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\\n');\n\n return true;\n } catch {\n return false;\n }\n}\n\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,YAAY;AAErB,IAAM,eAAe;AAKrB,IAAM,YAAY;AAOX,SAAS,kBAAkB,aAA8B;AAC9D,QAAM,kBAAkB,KAAK,aAAa,cAAc;AAExD,MAAI,CAAC,WAAW,eAAe,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,iBAAiB,OAAO;AACrD,UAAM,MAAmB,KAAK,MAAM,OAAO;AAE3C,QAAI,CAAC,IAAI,SAAS;AAChB,UAAI,UAAU,CAAC;AAAA,IACjB;AAEA,UAAM,eAAe,IAAI,QAAQ,SAAS;AAG1C,QAAI,cAAc,SAAS,YAAY,GAAG;AACxC,aAAO;AAAA,IACT;AAGA,QAAI,cAAc;AAChB,UAAI,QAAQ,SAAS,IAAI,GAAG,YAAY,OAAO,YAAY;AAAA,IAC7D,OAAO;AACL,UAAI,QAAQ,SAAS,IAAI;AAAA,IAC3B;AAGA,kBAAc,iBAAiB,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAElE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}