@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/dist/index.js ADDED
@@ -0,0 +1,420 @@
1
+ // src/lib/manifest.ts
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ var MANIFEST_FILE = ".plugins-manifest.json";
5
+ function getManifestPath(projectRoot) {
6
+ return join(projectRoot, ".claude", MANIFEST_FILE);
7
+ }
8
+ function readManifest(projectRoot) {
9
+ const manifestPath = getManifestPath(projectRoot);
10
+ if (!existsSync(manifestPath)) {
11
+ return { plugins: {} };
12
+ }
13
+ try {
14
+ const content = readFileSync(manifestPath, "utf-8");
15
+ return JSON.parse(content);
16
+ } catch {
17
+ return { plugins: {} };
18
+ }
19
+ }
20
+ function writeManifest(projectRoot, manifest) {
21
+ const manifestPath = getManifestPath(projectRoot);
22
+ const claudeDir = dirname(manifestPath);
23
+ if (!existsSync(claudeDir)) {
24
+ mkdirSync(claudeDir, { recursive: true });
25
+ }
26
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
27
+ }
28
+ function addPlugin(projectRoot, packageName, version, files) {
29
+ const manifest = readManifest(projectRoot);
30
+ manifest.plugins[packageName] = {
31
+ version,
32
+ files
33
+ };
34
+ writeManifest(projectRoot, manifest);
35
+ }
36
+ function removePlugin(projectRoot, packageName) {
37
+ const manifest = readManifest(projectRoot);
38
+ delete manifest.plugins[packageName];
39
+ writeManifest(projectRoot, manifest);
40
+ }
41
+ function getPlugins(projectRoot) {
42
+ return readManifest(projectRoot).plugins;
43
+ }
44
+ function cleanupManifestFiles(projectRoot) {
45
+ const manifest = readManifest(projectRoot);
46
+ const claudeDir = join(projectRoot, ".claude");
47
+ const removedFiles = [];
48
+ for (const [packageName, entry] of Object.entries(manifest.plugins)) {
49
+ for (const file of entry.files) {
50
+ const fullPath = join(claudeDir, file);
51
+ if (existsSync(fullPath)) {
52
+ rmSync(fullPath, { recursive: true, force: true });
53
+ removedFiles.push(file);
54
+ }
55
+ }
56
+ }
57
+ return removedFiles;
58
+ }
59
+
60
+ // src/lib/copier.ts
61
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync, readdirSync, statSync, readFileSync as readFileSync2 } from "fs";
62
+ import { createRequire } from "module";
63
+ import { dirname as dirname2, join as join2, basename } from "path";
64
+ var ASSET_DIRS = ["skills", "commands", "agents", "hooks"];
65
+ function getPackageVersion(packagePath) {
66
+ try {
67
+ const pkgJson = JSON.parse(readFileSync2(join2(packagePath, "package.json"), "utf-8"));
68
+ return pkgJson.version || "0.0.0";
69
+ } catch {
70
+ return "0.0.0";
71
+ }
72
+ }
73
+ function copyDirectory(src, dest) {
74
+ cpSync(src, dest, { recursive: true });
75
+ }
76
+ function copyFile(src, dest) {
77
+ const destDir = join2(dest, "..");
78
+ if (!existsSync2(destDir)) {
79
+ mkdirSync2(destDir, { recursive: true });
80
+ }
81
+ cpSync(src, dest);
82
+ }
83
+ function isSkillDirectory(itemPath) {
84
+ return statSync(itemPath).isDirectory();
85
+ }
86
+ function isAssetFile(itemPath) {
87
+ const stat = statSync(itemPath);
88
+ const name = basename(itemPath);
89
+ return stat.isFile() && name !== ".gitkeep";
90
+ }
91
+ function copyPluginAssets(packagePath, projectRoot) {
92
+ const claudeDir = join2(projectRoot, ".claude");
93
+ const copiedFiles = [];
94
+ for (const assetDir of ASSET_DIRS) {
95
+ const sourcePath = join2(packagePath, assetDir);
96
+ if (!existsSync2(sourcePath)) {
97
+ continue;
98
+ }
99
+ const targetDir = join2(claudeDir, assetDir);
100
+ if (!existsSync2(targetDir)) {
101
+ mkdirSync2(targetDir, { recursive: true });
102
+ }
103
+ const items = readdirSync(sourcePath);
104
+ for (const item of items) {
105
+ if (item === ".gitkeep") continue;
106
+ const itemPath = join2(sourcePath, item);
107
+ const targetPath = join2(targetDir, item);
108
+ if (assetDir === "skills") {
109
+ if (isSkillDirectory(itemPath)) {
110
+ copyDirectory(itemPath, targetPath);
111
+ copiedFiles.push(`${assetDir}/${item}`);
112
+ }
113
+ } else {
114
+ if (isAssetFile(itemPath)) {
115
+ copyFile(itemPath, targetPath);
116
+ copiedFiles.push(`${assetDir}/${item}`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return {
122
+ files: copiedFiles,
123
+ version: getPackageVersion(packagePath)
124
+ };
125
+ }
126
+ function findPluginInNodeModules(packageName, projectRoot) {
127
+ const standardPath = join2(projectRoot, "node_modules", packageName);
128
+ if (existsSync2(standardPath)) {
129
+ return standardPath;
130
+ }
131
+ try {
132
+ const require2 = createRequire(join2(projectRoot, "package.json"));
133
+ const resolved = require2.resolve(`${packageName}/package.json`);
134
+ return dirname2(resolved);
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+ function hasAssets(packagePath) {
140
+ for (const assetDir of ASSET_DIRS) {
141
+ const dirPath = join2(packagePath, assetDir);
142
+ if (existsSync2(dirPath)) {
143
+ const items = readdirSync(dirPath).filter((f) => f !== ".gitkeep");
144
+ if (items.length > 0) {
145
+ return true;
146
+ }
147
+ }
148
+ }
149
+ return false;
150
+ }
151
+
152
+ // src/lib/hooks.ts
153
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
154
+ import { join as join3 } from "path";
155
+ var HOOK_COMMAND = "claude-plugins install";
156
+ var HOOK_NAME = "prepare";
157
+ function injectPrepareHook(projectRoot) {
158
+ const packageJsonPath = join3(projectRoot, "package.json");
159
+ if (!existsSync3(packageJsonPath)) {
160
+ return false;
161
+ }
162
+ try {
163
+ const content = readFileSync3(packageJsonPath, "utf-8");
164
+ const pkg = JSON.parse(content);
165
+ if (!pkg.scripts) {
166
+ pkg.scripts = {};
167
+ }
168
+ const existingHook = pkg.scripts[HOOK_NAME];
169
+ if (existingHook?.includes(HOOK_COMMAND)) {
170
+ return false;
171
+ }
172
+ if (existingHook) {
173
+ pkg.scripts[HOOK_NAME] = `${existingHook} && ${HOOK_COMMAND}`;
174
+ } else {
175
+ pkg.scripts[HOOK_NAME] = HOOK_COMMAND;
176
+ }
177
+ writeFileSync2(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
178
+ return true;
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+ function hasPrepareHook(projectRoot) {
184
+ const packageJsonPath = join3(projectRoot, "package.json");
185
+ if (!existsSync3(packageJsonPath)) {
186
+ return false;
187
+ }
188
+ try {
189
+ const content = readFileSync3(packageJsonPath, "utf-8");
190
+ const pkg = JSON.parse(content);
191
+ return pkg.scripts?.[HOOK_NAME]?.includes(HOOK_COMMAND) ?? false;
192
+ } catch {
193
+ return false;
194
+ }
195
+ }
196
+ function removePrepareHook(projectRoot) {
197
+ const packageJsonPath = join3(projectRoot, "package.json");
198
+ if (!existsSync3(packageJsonPath)) {
199
+ return false;
200
+ }
201
+ try {
202
+ const content = readFileSync3(packageJsonPath, "utf-8");
203
+ const pkg = JSON.parse(content);
204
+ if (!pkg.scripts?.[HOOK_NAME]) {
205
+ return false;
206
+ }
207
+ const hook = pkg.scripts[HOOK_NAME];
208
+ if (!hook.includes(HOOK_COMMAND)) {
209
+ return false;
210
+ }
211
+ let newHook = hook.replace(` && ${HOOK_COMMAND}`, "").replace(`${HOOK_COMMAND} && `, "").replace(HOOK_COMMAND, "");
212
+ if (newHook.trim() === "") {
213
+ delete pkg.scripts[HOOK_NAME];
214
+ } else {
215
+ pkg.scripts[HOOK_NAME] = newHook.trim();
216
+ }
217
+ if (Object.keys(pkg.scripts).length === 0) {
218
+ delete pkg.scripts;
219
+ }
220
+ writeFileSync2(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
221
+ return true;
222
+ } catch {
223
+ return false;
224
+ }
225
+ }
226
+
227
+ // src/lib/sync.ts
228
+ import { existsSync as existsSync4, rmSync as rmSync2 } from "fs";
229
+ import { join as join4 } from "path";
230
+ function syncPlugins(projectRoot, options = {}) {
231
+ const manifest = readManifest(projectRoot);
232
+ const pluginCount = Object.keys(manifest.plugins).length;
233
+ if (pluginCount === 0) {
234
+ return {
235
+ success: true,
236
+ synced: false,
237
+ reason: "No plugins to sync",
238
+ totalFiles: 0,
239
+ pluginCount: 0,
240
+ removedPlugins: [],
241
+ conflicts: [],
242
+ installedPlugins: []
243
+ };
244
+ }
245
+ if (!options.force) {
246
+ let needsSync = false;
247
+ let reason = "";
248
+ for (const [packageName, entry] of Object.entries(manifest.plugins)) {
249
+ const packagePath = findPluginInNodeModules(packageName, projectRoot);
250
+ if (!packagePath) {
251
+ needsSync = true;
252
+ reason = `${packageName} was uninstalled`;
253
+ break;
254
+ }
255
+ const currentVersion = getPackageVersion(packagePath);
256
+ if (currentVersion !== entry.version) {
257
+ needsSync = true;
258
+ reason = `${packageName} changed (${entry.version} \u2192 ${currentVersion})`;
259
+ break;
260
+ }
261
+ }
262
+ if (!needsSync) {
263
+ return {
264
+ success: true,
265
+ synced: false,
266
+ reason: "All plugins up to date",
267
+ totalFiles: 0,
268
+ pluginCount,
269
+ removedPlugins: [],
270
+ conflicts: [],
271
+ installedPlugins: []
272
+ };
273
+ }
274
+ }
275
+ cleanupManifestFiles(projectRoot);
276
+ const newManifest = { plugins: {} };
277
+ const fileOwnership = /* @__PURE__ */ new Map();
278
+ const conflicts = [];
279
+ const removedPlugins = [];
280
+ const installedPlugins = [];
281
+ let totalFiles = 0;
282
+ for (const [packageName] of Object.entries(manifest.plugins)) {
283
+ const packagePath = findPluginInNodeModules(packageName, projectRoot);
284
+ if (!packagePath) {
285
+ removedPlugins.push(packageName);
286
+ continue;
287
+ }
288
+ if (!hasAssets(packagePath)) {
289
+ continue;
290
+ }
291
+ const result = copyPluginAssets(packagePath, projectRoot);
292
+ if (result.files.length > 0) {
293
+ for (const file of result.files) {
294
+ const existingOwner = fileOwnership.get(file);
295
+ if (existingOwner) {
296
+ conflicts.push(`${file} (${existingOwner} vs ${packageName})`);
297
+ }
298
+ fileOwnership.set(file, packageName);
299
+ }
300
+ newManifest.plugins[packageName] = {
301
+ version: result.version,
302
+ files: result.files
303
+ };
304
+ totalFiles += result.files.length;
305
+ installedPlugins.push({
306
+ name: packageName,
307
+ version: result.version,
308
+ fileCount: result.files.length
309
+ });
310
+ }
311
+ }
312
+ writeManifest(projectRoot, newManifest);
313
+ return {
314
+ success: true,
315
+ synced: true,
316
+ totalFiles,
317
+ pluginCount: Object.keys(newManifest.plugins).length,
318
+ removedPlugins,
319
+ conflicts,
320
+ installedPlugins
321
+ };
322
+ }
323
+ function addPluginToProject(projectRoot, packageName) {
324
+ const hookInjected = injectPrepareHook(projectRoot);
325
+ const packagePath = findPluginInNodeModules(packageName, projectRoot);
326
+ if (!packagePath) {
327
+ return {
328
+ success: false,
329
+ alreadyExists: false,
330
+ packageName,
331
+ files: [],
332
+ hookInjected,
333
+ error: `Package ${packageName} not found in node_modules`
334
+ };
335
+ }
336
+ if (!hasAssets(packagePath)) {
337
+ return {
338
+ success: true,
339
+ alreadyExists: false,
340
+ packageName,
341
+ files: [],
342
+ hookInjected
343
+ };
344
+ }
345
+ const manifest = readManifest(projectRoot);
346
+ const existing = manifest.plugins[packageName];
347
+ if (existing) {
348
+ const claudeDir = join4(projectRoot, ".claude");
349
+ for (const file of existing.files) {
350
+ const fullPath = join4(claudeDir, file);
351
+ if (existsSync4(fullPath)) {
352
+ rmSync2(fullPath, { recursive: true, force: true });
353
+ }
354
+ }
355
+ }
356
+ const result = copyPluginAssets(packagePath, projectRoot);
357
+ if (result.files.length > 0) {
358
+ addPlugin(projectRoot, packageName, result.version, result.files);
359
+ }
360
+ return {
361
+ success: true,
362
+ alreadyExists: !!existing,
363
+ packageName,
364
+ version: result.version,
365
+ files: result.files,
366
+ hookInjected
367
+ };
368
+ }
369
+ function listPlugins(projectRoot) {
370
+ const manifest = readManifest(projectRoot);
371
+ return { plugins: manifest.plugins };
372
+ }
373
+ function removePluginFromProject(projectRoot, packageName) {
374
+ const manifest = readManifest(projectRoot);
375
+ const entry = manifest.plugins[packageName];
376
+ if (!entry) {
377
+ return {
378
+ success: false,
379
+ packageName,
380
+ filesRemoved: [],
381
+ error: `Plugin ${packageName} is not installed`
382
+ };
383
+ }
384
+ const claudeDir = join4(projectRoot, ".claude");
385
+ const filesRemoved = [];
386
+ for (const file of entry.files) {
387
+ const fullPath = join4(claudeDir, file);
388
+ if (existsSync4(fullPath)) {
389
+ rmSync2(fullPath, { recursive: true, force: true });
390
+ filesRemoved.push(file);
391
+ }
392
+ }
393
+ delete manifest.plugins[packageName];
394
+ writeManifest(projectRoot, manifest);
395
+ return {
396
+ success: true,
397
+ packageName,
398
+ filesRemoved
399
+ };
400
+ }
401
+ export {
402
+ addPlugin,
403
+ addPluginToProject,
404
+ cleanupManifestFiles,
405
+ copyPluginAssets,
406
+ findPluginInNodeModules,
407
+ getPackageVersion,
408
+ getPlugins,
409
+ hasAssets,
410
+ hasPrepareHook,
411
+ injectPrepareHook,
412
+ listPlugins,
413
+ readManifest,
414
+ removePlugin,
415
+ removePluginFromProject,
416
+ removePrepareHook,
417
+ syncPlugins,
418
+ writeManifest
419
+ };
420
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/manifest.ts","../src/lib/copier.ts","../src/lib/hooks.ts","../src/lib/sync.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst MANIFEST_FILE = '.plugins-manifest.json';\n\nexport interface PluginEntry {\n version: string;\n files: string[];\n}\n\nexport interface Manifest {\n plugins: Record<string, PluginEntry>;\n}\n\nfunction getManifestPath(projectRoot: string): string {\n return join(projectRoot, '.claude', MANIFEST_FILE);\n}\n\nexport function readManifest(projectRoot: string): Manifest {\n const manifestPath = getManifestPath(projectRoot);\n\n if (!existsSync(manifestPath)) {\n return { plugins: {} };\n }\n\n try {\n const content = readFileSync(manifestPath, 'utf-8');\n return JSON.parse(content) as Manifest;\n } catch {\n return { plugins: {} };\n }\n}\n\nexport function writeManifest(projectRoot: string, manifest: Manifest): void {\n const manifestPath = getManifestPath(projectRoot);\n const claudeDir = dirname(manifestPath);\n\n if (!existsSync(claudeDir)) {\n mkdirSync(claudeDir, { recursive: true });\n }\n\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n');\n}\n\nexport function addPlugin(\n projectRoot: string,\n packageName: string,\n version: string,\n files: string[]\n): void {\n const manifest = readManifest(projectRoot);\n\n manifest.plugins[packageName] = {\n version,\n files,\n };\n\n writeManifest(projectRoot, manifest);\n}\n\nexport function removePlugin(projectRoot: string, packageName: string): void {\n const manifest = readManifest(projectRoot);\n\n delete manifest.plugins[packageName];\n\n writeManifest(projectRoot, manifest);\n}\n\nexport function getPlugins(projectRoot: string): Record<string, PluginEntry> {\n return readManifest(projectRoot).plugins;\n}\n\nexport function cleanupManifestFiles(projectRoot: string): string[] {\n const manifest = readManifest(projectRoot);\n const claudeDir = join(projectRoot, '.claude');\n const removedFiles: string[] = [];\n\n for (const [packageName, entry] of Object.entries(manifest.plugins)) {\n for (const file of entry.files) {\n const fullPath = join(claudeDir, file);\n if (existsSync(fullPath)) {\n rmSync(fullPath, { recursive: true, force: true });\n removedFiles.push(file);\n }\n }\n }\n\n return removedFiles;\n}\n","import { existsSync, mkdirSync, cpSync, readdirSync, statSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, basename } from 'node:path';\n\nconst ASSET_DIRS = ['skills', 'commands', 'agents', 'hooks'] as const;\ntype AssetDir = typeof ASSET_DIRS[number];\n\ninterface CopyResult {\n files: string[];\n version: string;\n}\n\nexport function getPackageVersion(packagePath: string): string {\n try {\n const pkgJson = JSON.parse(readFileSync(join(packagePath, 'package.json'), 'utf-8'));\n return pkgJson.version || '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nfunction copyDirectory(src: string, dest: string): void {\n cpSync(src, dest, { recursive: true });\n}\n\nfunction copyFile(src: string, dest: string): void {\n const destDir = join(dest, '..');\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true });\n }\n cpSync(src, dest);\n}\n\nfunction isSkillDirectory(itemPath: string): boolean {\n return statSync(itemPath).isDirectory();\n}\n\nfunction isAssetFile(itemPath: string): boolean {\n const stat = statSync(itemPath);\n const name = basename(itemPath);\n return stat.isFile() && name !== '.gitkeep';\n}\n\nexport function copyPluginAssets(\n packagePath: string,\n projectRoot: string\n): CopyResult {\n const claudeDir = join(projectRoot, '.claude');\n const copiedFiles: string[] = [];\n\n for (const assetDir of ASSET_DIRS) {\n const sourcePath = join(packagePath, assetDir);\n\n if (!existsSync(sourcePath)) {\n continue;\n }\n\n const targetDir = join(claudeDir, assetDir);\n\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n }\n\n const items = readdirSync(sourcePath);\n\n for (const item of items) {\n if (item === '.gitkeep') continue;\n\n const itemPath = join(sourcePath, item);\n const targetPath = join(targetDir, item);\n\n if (assetDir === 'skills') {\n // Skills are directories\n if (isSkillDirectory(itemPath)) {\n copyDirectory(itemPath, targetPath);\n copiedFiles.push(`${assetDir}/${item}`);\n }\n } else {\n // Commands, agents, hooks are files\n if (isAssetFile(itemPath)) {\n copyFile(itemPath, targetPath);\n copiedFiles.push(`${assetDir}/${item}`);\n }\n }\n }\n }\n\n return {\n files: copiedFiles,\n version: getPackageVersion(packagePath),\n };\n}\n\nexport function findPluginInNodeModules(\n packageName: string,\n projectRoot: string\n): string | null {\n // Standard node_modules path (npm, pnpm, yarn classic, bun)\n const standardPath = join(projectRoot, 'node_modules', packageName);\n if (existsSync(standardPath)) {\n return standardPath;\n }\n\n // Fallback: use Node's require.resolve for yarn PnP and other setups\n try {\n const require = createRequire(join(projectRoot, 'package.json'));\n const resolved = require.resolve(`${packageName}/package.json`);\n return dirname(resolved);\n } catch {\n return null;\n }\n}\n\nexport function hasAssets(packagePath: string): boolean {\n for (const assetDir of ASSET_DIRS) {\n const dirPath = join(packagePath, assetDir);\n if (existsSync(dirPath)) {\n const items = readdirSync(dirPath).filter(f => f !== '.gitkeep');\n if (items.length > 0) {\n return true;\n }\n }\n }\n return false;\n}\n","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","import { existsSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n readManifest,\n writeManifest,\n cleanupManifestFiles,\n addPlugin,\n type PluginEntry,\n} from './manifest.js';\nimport {\n copyPluginAssets,\n findPluginInNodeModules,\n hasAssets,\n getPackageVersion,\n} from './copier.js';\nimport { injectPrepareHook } from './hooks.js';\n\nexport interface SyncResult {\n success: boolean;\n synced: boolean;\n reason?: string;\n totalFiles: number;\n pluginCount: number;\n removedPlugins: string[];\n conflicts: string[];\n installedPlugins: Array<{ name: string; version: string; fileCount: number }>;\n}\n\nexport interface AddResult {\n success: boolean;\n alreadyExists: boolean;\n packageName: string;\n version?: string;\n files: string[];\n hookInjected: boolean;\n error?: string;\n}\n\nexport interface ListResult {\n plugins: Record<string, PluginEntry>;\n}\n\nexport interface RemoveResult {\n success: boolean;\n packageName: string;\n filesRemoved: string[];\n error?: string;\n}\n\nexport function syncPlugins(\n projectRoot: string,\n options: { force?: boolean } = {}\n): SyncResult {\n const manifest = readManifest(projectRoot);\n const pluginCount = Object.keys(manifest.plugins).length;\n\n if (pluginCount === 0) {\n return {\n success: true,\n synced: false,\n reason: 'No plugins to sync',\n totalFiles: 0,\n pluginCount: 0,\n removedPlugins: [],\n conflicts: [],\n installedPlugins: [],\n };\n }\n\n // Check if any plugins have changed (unless --force)\n if (!options.force) {\n let needsSync = false;\n let reason = '';\n\n for (const [packageName, entry] of Object.entries(manifest.plugins)) {\n const packagePath = findPluginInNodeModules(packageName, projectRoot);\n\n if (!packagePath) {\n needsSync = true;\n reason = `${packageName} was uninstalled`;\n break;\n }\n\n const currentVersion = getPackageVersion(packagePath);\n if (currentVersion !== entry.version) {\n needsSync = true;\n reason = `${packageName} changed (${entry.version} → ${currentVersion})`;\n break;\n }\n }\n\n if (!needsSync) {\n return {\n success: true,\n synced: false,\n reason: 'All plugins up to date',\n totalFiles: 0,\n pluginCount,\n removedPlugins: [],\n conflicts: [],\n installedPlugins: [],\n };\n }\n }\n\n // Clean up existing files from manifest\n cleanupManifestFiles(projectRoot);\n\n // Re-copy all plugins from manifest\n const newManifest = { plugins: {} as Record<string, PluginEntry> };\n const fileOwnership = new Map<string, string>();\n const conflicts: string[] = [];\n const removedPlugins: string[] = [];\n const installedPlugins: Array<{ name: string; version: string; fileCount: number }> = [];\n let totalFiles = 0;\n\n for (const [packageName] of Object.entries(manifest.plugins)) {\n const packagePath = findPluginInNodeModules(packageName, projectRoot);\n\n if (!packagePath) {\n removedPlugins.push(packageName);\n continue;\n }\n\n if (!hasAssets(packagePath)) {\n continue;\n }\n\n const result = copyPluginAssets(packagePath, projectRoot);\n\n if (result.files.length > 0) {\n // Check for conflicts\n for (const file of result.files) {\n const existingOwner = fileOwnership.get(file);\n if (existingOwner) {\n conflicts.push(`${file} (${existingOwner} vs ${packageName})`);\n }\n fileOwnership.set(file, packageName);\n }\n\n newManifest.plugins[packageName] = {\n version: result.version,\n files: result.files,\n };\n totalFiles += result.files.length;\n installedPlugins.push({\n name: packageName,\n version: result.version,\n fileCount: result.files.length,\n });\n }\n }\n\n writeManifest(projectRoot, newManifest);\n\n return {\n success: true,\n synced: true,\n totalFiles,\n pluginCount: Object.keys(newManifest.plugins).length,\n removedPlugins,\n conflicts,\n installedPlugins,\n };\n}\n\nexport function addPluginToProject(\n projectRoot: string,\n packageName: string\n): AddResult {\n // Ensure prepare hook is set up\n const hookInjected = injectPrepareHook(projectRoot);\n\n const packagePath = findPluginInNodeModules(packageName, projectRoot);\n\n if (!packagePath) {\n return {\n success: false,\n alreadyExists: false,\n packageName,\n files: [],\n hookInjected,\n error: `Package ${packageName} not found in node_modules`,\n };\n }\n\n if (!hasAssets(packagePath)) {\n return {\n success: true,\n alreadyExists: false,\n packageName,\n files: [],\n hookInjected,\n };\n }\n\n // Clean up any existing files for this plugin\n const manifest = readManifest(projectRoot);\n const existing = manifest.plugins[packageName];\n\n if (existing) {\n const claudeDir = join(projectRoot, '.claude');\n for (const file of existing.files) {\n const fullPath = join(claudeDir, file);\n if (existsSync(fullPath)) {\n rmSync(fullPath, { recursive: true, force: true });\n }\n }\n }\n\n // Copy assets\n const result = copyPluginAssets(packagePath, projectRoot);\n\n if (result.files.length > 0) {\n addPlugin(projectRoot, packageName, result.version, result.files);\n }\n\n return {\n success: true,\n alreadyExists: !!existing,\n packageName,\n version: result.version,\n files: result.files,\n hookInjected,\n };\n}\n\nexport function listPlugins(projectRoot: string): ListResult {\n const manifest = readManifest(projectRoot);\n return { plugins: manifest.plugins };\n}\n\nexport function removePluginFromProject(\n projectRoot: string,\n packageName: string\n): RemoveResult {\n const manifest = readManifest(projectRoot);\n const entry = manifest.plugins[packageName];\n\n if (!entry) {\n return {\n success: false,\n packageName,\n filesRemoved: [],\n error: `Plugin ${packageName} is not installed`,\n };\n }\n\n const claudeDir = join(projectRoot, '.claude');\n const filesRemoved: string[] = [];\n\n // Remove files\n for (const file of entry.files) {\n const fullPath = join(claudeDir, file);\n if (existsSync(fullPath)) {\n rmSync(fullPath, { recursive: true, force: true });\n filesRemoved.push(file);\n }\n }\n\n // Update manifest\n delete manifest.plugins[packageName];\n writeManifest(projectRoot, manifest);\n\n return {\n success: true,\n packageName,\n filesRemoved,\n };\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readFileSync","dirname","join","require","existsSync","readFileSync","writeFileSync","join","existsSync","rmSync","join","join","existsSync","rmSync"]}
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ injectPrepareHook
4
+ } from "./chunk-2SM5GQ6P.js";
5
+
6
+ // src/postinstall.ts
7
+ import { existsSync } from "fs";
8
+ import { join, resolve } from "path";
9
+ function findProjectRoot() {
10
+ const initCwd = process.env.INIT_CWD;
11
+ if (initCwd && existsSync(join(initCwd, "package.json"))) {
12
+ return initCwd;
13
+ }
14
+ let dir = process.cwd();
15
+ while (dir !== "/") {
16
+ if (!dir.includes("node_modules") && existsSync(join(dir, "package.json"))) {
17
+ return dir;
18
+ }
19
+ dir = resolve(dir, "..");
20
+ }
21
+ return null;
22
+ }
23
+ function main() {
24
+ if (process.env.CI || process.env.npm_config_global) {
25
+ return;
26
+ }
27
+ const projectRoot = findProjectRoot();
28
+ if (!projectRoot) {
29
+ return;
30
+ }
31
+ if (injectPrepareHook(projectRoot)) {
32
+ console.log("[claude-manager] Added prepare hook to package.json");
33
+ console.log("[claude-manager] Plugins will sync on npm install AND npm update");
34
+ }
35
+ }
36
+ main();
37
+ //# sourceMappingURL=postinstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/postinstall.ts"],"sourcesContent":["/**\n * Postinstall script for claude-manager\n *\n * This runs when claude-manager itself is installed as a dependency.\n * It injects a 'prepare' hook into the project's package.json\n * so that `claude-plugins install` runs on both npm install AND npm update.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { injectPrepareHook } from './lib/hooks.js';\n\nfunction findProjectRoot(): string | null {\n // When running as postinstall of claude-manager,\n // we're in node_modules/claude-manager\n // The project root is two levels up\n\n // Try environment variable first (set by npm)\n const initCwd = process.env.INIT_CWD;\n if (initCwd && existsSync(join(initCwd, 'package.json'))) {\n return initCwd;\n }\n\n // Walk up from current directory\n let dir = process.cwd();\n while (dir !== '/') {\n // Skip if we're inside node_modules\n if (!dir.includes('node_modules') && existsSync(join(dir, 'package.json'))) {\n return dir;\n }\n dir = resolve(dir, '..');\n }\n\n return null;\n}\n\nfunction main() {\n // Skip if running in CI or during package publish\n if (process.env.CI || process.env.npm_config_global) {\n return;\n }\n\n const projectRoot = findProjectRoot();\n\n if (!projectRoot) {\n // Can't find project root, skip silently\n return;\n }\n\n // Inject the prepare hook (runs on both npm install and npm update)\n if (injectPrepareHook(projectRoot)) {\n console.log('[claude-manager] Added prepare hook to package.json');\n console.log('[claude-manager] Plugins will sync on npm install AND npm update');\n }\n}\n\nmain();\n"],"mappings":";;;;;;AAQA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;AAG9B,SAAS,kBAAiC;AAMxC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,WAAW,KAAK,SAAS,cAAc,CAAC,GAAG;AACxD,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,QAAQ,IAAI;AACtB,SAAO,QAAQ,KAAK;AAElB,QAAI,CAAC,IAAI,SAAS,cAAc,KAAK,WAAW,KAAK,KAAK,cAAc,CAAC,GAAG;AAC1E,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,KAAK,IAAI;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,SAAS,OAAO;AAEd,MAAI,QAAQ,IAAI,MAAM,QAAQ,IAAI,mBAAmB;AACnD;AAAA,EACF;AAEA,QAAM,cAAc,gBAAgB;AAEpC,MAAI,CAAC,aAAa;AAEhB;AAAA,EACF;AAGA,MAAI,kBAAkB,WAAW,GAAG;AAClC,YAAQ,IAAI,qDAAqD;AACjE,YAAQ,IAAI,kEAAkE;AAAA,EAChF;AACF;AAEA,KAAK;","names":[]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@leeovery/claude-manager",
3
+ "version": "2.0.0",
4
+ "packageManager": "pnpm@10.27.0",
5
+ "description": "Plugin manager for Claude Code skills and commands",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "claude-plugins": "dist/cli.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "test": "vitest",
16
+ "test:run": "vitest run",
17
+ "postinstall": "[ -f dist/postinstall.js ] && node dist/postinstall.js || true",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "keywords": [
24
+ "claude",
25
+ "claude-code",
26
+ "plugin",
27
+ "skills",
28
+ "commands"
29
+ ],
30
+ "author": "Lee Overy <me@leeovery.com>",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/leeovery/claude-manager.git"
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "dependencies": {
40
+ "commander": "^12.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.0.0",
44
+ "tsup": "^8.0.0",
45
+ "typescript": "^5.0.0",
46
+ "vitest": "^4.0.16"
47
+ },
48
+ "pnpm": {
49
+ "onlyBuiltDependencies": [
50
+ "esbuild"
51
+ ]
52
+ }
53
+ }