@leeovery/claude-manager 2.0.6 → 2.0.7

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/README.md CHANGED
@@ -44,37 +44,27 @@ Instead of manually copying skill files between projects, you can install them a
44
44
 
45
45
  ## Installation
46
46
 
47
- The manager is typically installed automatically as a dependency of plugin packages. When you install a Claude plugin:
47
+ The manager is installed automatically as a dependency of plugin packages. When you install a Claude plugin:
48
48
 
49
49
  ```bash
50
50
  npm install @your-org/claude-your-plugin
51
+ # or
52
+ pnpm add @your-org/claude-your-plugin
53
+ # or
54
+ yarn add @your-org/claude-your-plugin
51
55
  ```
52
56
 
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
+ The plugin's postinstall script copies assets to `.claude/`. That's it.
57
58
 
58
- That's it. Future `npm install` and `npm update` runs will automatically sync all plugins.
59
-
60
- ### pnpm
61
-
62
- pnpm requires claude-manager as a direct dependency (it doesn't expose binaries from transitive dependencies):
63
-
64
- ```bash
65
- pnpm add @leeovery/claude-manager @your-org/claude-your-plugin
66
- ```
67
-
68
- pnpm also blocks postinstall scripts by default. After installing, run `pnpm approve-builds` to approve the packages, then `pnpm install` again.
59
+ **Note:** pnpm blocks postinstall scripts by default. After installing, run `pnpm approve-builds` to approve the packages, then `pnpm install` again.
69
60
 
70
61
  ## How It Works
71
62
 
72
63
  1. Plugin packages have `@leeovery/claude-manager` as a dependency
73
- 2. Plugins register themselves via their `postinstall` script
74
- 3. The manager copies skills, commands, agents, and hooks to `.claude/`
64
+ 2. Plugin's `postinstall` script copies assets to `.claude/`
65
+ 3. Plugin's `preuninstall` script cleans up when removed
75
66
  4. A manifest (`.claude/.plugins-manifest.json`) tracks what's installed
76
- 5. A `prepare` hook ensures plugins sync on both `npm install` and `npm update`
77
- 6. Claude Code discovers them automatically
67
+ 5. Claude Code discovers the assets automatically
78
68
 
79
69
  **After installation, your project structure looks like:**
80
70
 
@@ -108,8 +98,6 @@ The manager provides a CLI tool for managing plugins:
108
98
  | `npx claude-plugins add <package>` | Manually add a plugin |
109
99
  | `npx claude-plugins remove <package>` | Remove a plugin and its assets |
110
100
 
111
- For pnpm users, use `pnpm exec claude-plugins` instead of `npx claude-plugins`.
112
-
113
101
  ## Creating Plugins
114
102
 
115
103
  Want to create your own skill or command packages?
@@ -117,7 +105,7 @@ Want to create your own skill or command packages?
117
105
  ### Plugin Requirements
118
106
 
119
107
  1. Have `@leeovery/claude-manager` as a dependency
120
- 2. Add a `postinstall` script that calls `claude-plugins add`
108
+ 2. Add `postinstall` and `preuninstall` scripts (see example below)
121
109
  3. Include asset directories (`skills/`, `commands/`, `agents/`, `hooks/`)
122
110
 
123
111
  ### Example package.json
@@ -132,13 +120,13 @@ Want to create your own skill or command packages?
132
120
  "@leeovery/claude-manager": "^2.0.0"
133
121
  },
134
122
  "scripts": {
135
- "postinstall": "claude-plugins add",
136
- "preuninstall": "claude-plugins remove"
123
+ "postinstall": "node -e \"require('@leeovery/claude-manager').add()\"",
124
+ "preuninstall": "node -e \"require('@leeovery/claude-manager').remove()\""
137
125
  }
138
126
  }
139
127
  ```
140
128
 
141
- The `postinstall` script copies assets when the plugin is installed. The `preuninstall` script cleans up when the plugin is removed.
129
+ The `postinstall` script copies assets when the plugin is installed. The `preuninstall` script cleans up when the plugin is removed. Using `node -e` ensures compatibility with all package managers (npm, pnpm, yarn).
142
130
 
143
131
  ### Plugin Structure
144
132
 
@@ -188,7 +176,6 @@ The manager tracks installed plugins in `.claude/.plugins-manifest.json`:
188
176
  ```
189
177
 
190
178
  This file should be committed to your repository. It ensures:
191
- - Plugins are synced correctly on `npm install` and `npm update`
192
179
  - Old files are cleaned up when plugins are updated or removed
193
180
  - You can see what's installed at a glance
194
181
 
@@ -217,23 +204,9 @@ ls -la .claude/hooks/
217
204
 
218
205
  Verify the plugin's package.json has:
219
206
  - `@leeovery/claude-manager` as a dependency
220
- - A `postinstall` script that calls `claude-plugins add`
207
+ - `postinstall` and `preuninstall` scripts using `node -e`
221
208
  - A `skills/`, `commands/`, `agents/`, or `hooks/` directory with content
222
209
 
223
- ### Prepare hook not added
224
-
225
- If your project's `package.json` doesn't have the prepare hook, add it manually:
226
-
227
- ```json
228
- {
229
- "scripts": {
230
- "prepare": "claude-plugins install"
231
- }
232
- }
233
- ```
234
-
235
- This ensures plugins sync on both `npm install` and `npm update`.
236
-
237
210
  ## Requirements
238
211
 
239
212
  - Node.js >= 18.0.0
package/dist/cli.js CHANGED
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- injectPrepareHook
4
- } from "./chunk-2SM5GQ6P.js";
5
2
 
6
3
  // src/cli.ts
7
4
  import { program } from "commander";
@@ -251,7 +248,6 @@ function syncPlugins(projectRoot, options = {}) {
251
248
  };
252
249
  }
253
250
  function addPluginToProject(projectRoot, packageName) {
254
- const hookInjected = injectPrepareHook(projectRoot);
255
251
  const packagePath = findPluginInNodeModules(packageName, projectRoot);
256
252
  if (!packagePath) {
257
253
  return {
@@ -259,7 +255,6 @@ function addPluginToProject(projectRoot, packageName) {
259
255
  alreadyExists: false,
260
256
  packageName,
261
257
  files: [],
262
- hookInjected,
263
258
  error: `Package ${packageName} not found in node_modules`
264
259
  };
265
260
  }
@@ -268,8 +263,7 @@ function addPluginToProject(projectRoot, packageName) {
268
263
  success: true,
269
264
  alreadyExists: false,
270
265
  packageName,
271
- files: [],
272
- hookInjected
266
+ files: []
273
267
  };
274
268
  }
275
269
  const manifest = readManifest(projectRoot);
@@ -292,8 +286,7 @@ function addPluginToProject(projectRoot, packageName) {
292
286
  alreadyExists: !!existing,
293
287
  packageName,
294
288
  version: result.version,
295
- files: result.files,
296
- hookInjected
289
+ files: result.files
297
290
  };
298
291
  }
299
292
  function listPlugins(projectRoot) {
@@ -390,9 +383,6 @@ program.command("add").description("Add and install a plugin (called from plugin
390
383
  process.exit(1);
391
384
  }
392
385
  const result = addPluginToProject(projectRoot, packageName);
393
- if (result.hookInjected) {
394
- console.log("Added prepare hook to package.json");
395
- }
396
386
  if (!result.success) {
397
387
  console.error(`Error: ${result.error}`);
398
388
  process.exit(1);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/lib/sync.ts","../src/lib/manifest.ts","../src/lib/copier.ts"],"sourcesContent":["import { program } from 'commander';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport {\n syncPlugins,\n addPluginToProject,\n listPlugins,\n removePluginFromProject,\n} from './lib/sync.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version || '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nfunction findProjectRoot(): string {\n let dir = process.cwd();\n\n while (dir !== '/') {\n // Skip node_modules directories - we want the actual project root\n if (!dir.includes('node_modules') && existsSync(join(dir, 'package.json'))) {\n return dir;\n }\n dir = resolve(dir, '..');\n }\n\n return process.cwd();\n}\n\nprogram\n .name('claude-plugins')\n .description('Plugin manager for Claude Code skills and commands')\n .version(getVersion());\n\nprogram\n .command('install')\n .description('Sync all plugins from manifest to .claude directory')\n .option('-f, --force', 'Force sync even if versions match')\n .action((options: { force?: boolean }) => {\n const projectRoot = findProjectRoot();\n const result = syncPlugins(projectRoot, options);\n\n if (!result.synced) {\n console.log(result.reason || 'Nothing to sync.');\n return;\n }\n\n if (options.force) {\n console.log('Syncing Claude plugins (forced)...');\n }\n\n for (const plugin of result.installedPlugins) {\n console.log(` Installed ${plugin.name}@${plugin.version} (${plugin.fileCount} files)`);\n }\n\n if (result.removedPlugins.length > 0) {\n console.log(`\\nRemoved ${result.removedPlugins.length} uninstalled plugin(s) from manifest:`);\n for (const name of result.removedPlugins) {\n console.log(` - ${name}`);\n }\n }\n\n if (result.conflicts.length > 0) {\n console.log(`\\nWarning: ${result.conflicts.length} file conflict(s) detected (later plugin overwrote earlier):`);\n for (const conflict of result.conflicts) {\n console.log(` ${conflict}`);\n }\n }\n\n console.log(`\\nDone. ${result.totalFiles} files from ${result.pluginCount} plugin(s).`);\n });\n\nprogram\n .command('add')\n .description('Add and install a plugin (called from plugin postinstall)')\n .argument('[package]', 'Package name (auto-detected if run from postinstall)')\n .action((packageArg?: string) => {\n const projectRoot = findProjectRoot();\n const packageName = packageArg || process.env.npm_package_name;\n\n if (!packageName) {\n console.error('Error: Could not determine package name.');\n console.error('Please provide the package name as an argument: claude-plugins add <package>');\n process.exit(1);\n }\n\n const result = addPluginToProject(projectRoot, packageName);\n\n if (result.hookInjected) {\n console.log('Added prepare hook to package.json');\n }\n\n if (!result.success) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n if (result.files.length === 0) {\n console.log(`Package ${packageName} has no Claude assets to install.`);\n return;\n }\n\n console.log(`Installed ${packageName}@${result.version}:`);\n for (const file of result.files) {\n console.log(` .claude/${file}`);\n }\n });\n\nprogram\n .command('list')\n .description('List installed plugins and their assets')\n .action(() => {\n const projectRoot = findProjectRoot();\n const result = listPlugins(projectRoot);\n const pluginNames = Object.keys(result.plugins);\n\n if (pluginNames.length === 0) {\n console.log('No plugins installed.');\n return;\n }\n\n console.log('Installed Claude plugins:\\n');\n\n for (const [packageName, entry] of Object.entries(result.plugins)) {\n console.log(`${packageName}@${entry.version}`);\n for (const file of entry.files) {\n console.log(` .claude/${file}`);\n }\n console.log();\n }\n });\n\nprogram\n .command('remove')\n .description('Remove a plugin and its assets (called from plugin preuninstall)')\n .argument('[package]', 'Package name (auto-detected if run from preuninstall)')\n .action((packageArg?: string) => {\n const projectRoot = findProjectRoot();\n const packageName = packageArg || process.env.npm_package_name;\n\n if (!packageName) {\n console.error('Error: Could not determine package name.');\n console.error('Please provide the package name as an argument: claude-plugins remove <package>');\n process.exit(1);\n }\n\n const result = removePluginFromProject(projectRoot, packageName);\n\n if (!result.success) {\n console.error(result.error);\n process.exit(1);\n }\n\n for (const file of result.filesRemoved) {\n console.log(`Removed .claude/${file}`);\n }\n\n console.log(`Removed ${packageName}`);\n });\n\nprogram.parse();\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","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"],"mappings":"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","names":["existsSync","readFileSync","dirname","join","existsSync","rmSync","join","existsSync","mkdirSync","readFileSync","dirname","join","require","join","existsSync","rmSync","dirname","join","readFileSync","existsSync"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/lib/sync.ts","../src/lib/manifest.ts","../src/lib/copier.ts"],"sourcesContent":["import { program } from 'commander';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport {\n syncPlugins,\n addPluginToProject,\n listPlugins,\n removePluginFromProject,\n} from './lib/sync.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version || '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nfunction findProjectRoot(): string {\n let dir = process.cwd();\n\n while (dir !== '/') {\n // Skip node_modules directories - we want the actual project root\n if (!dir.includes('node_modules') && existsSync(join(dir, 'package.json'))) {\n return dir;\n }\n dir = resolve(dir, '..');\n }\n\n return process.cwd();\n}\n\nprogram\n .name('claude-plugins')\n .description('Plugin manager for Claude Code skills and commands')\n .version(getVersion());\n\nprogram\n .command('install')\n .description('Sync all plugins from manifest to .claude directory')\n .option('-f, --force', 'Force sync even if versions match')\n .action((options: { force?: boolean }) => {\n const projectRoot = findProjectRoot();\n const result = syncPlugins(projectRoot, options);\n\n if (!result.synced) {\n console.log(result.reason || 'Nothing to sync.');\n return;\n }\n\n if (options.force) {\n console.log('Syncing Claude plugins (forced)...');\n }\n\n for (const plugin of result.installedPlugins) {\n console.log(` Installed ${plugin.name}@${plugin.version} (${plugin.fileCount} files)`);\n }\n\n if (result.removedPlugins.length > 0) {\n console.log(`\\nRemoved ${result.removedPlugins.length} uninstalled plugin(s) from manifest:`);\n for (const name of result.removedPlugins) {\n console.log(` - ${name}`);\n }\n }\n\n if (result.conflicts.length > 0) {\n console.log(`\\nWarning: ${result.conflicts.length} file conflict(s) detected (later plugin overwrote earlier):`);\n for (const conflict of result.conflicts) {\n console.log(` ${conflict}`);\n }\n }\n\n console.log(`\\nDone. ${result.totalFiles} files from ${result.pluginCount} plugin(s).`);\n });\n\nprogram\n .command('add')\n .description('Add and install a plugin (called from plugin postinstall)')\n .argument('[package]', 'Package name (auto-detected if run from postinstall)')\n .action((packageArg?: string) => {\n const projectRoot = findProjectRoot();\n const packageName = packageArg || process.env.npm_package_name;\n\n if (!packageName) {\n console.error('Error: Could not determine package name.');\n console.error('Please provide the package name as an argument: claude-plugins add <package>');\n process.exit(1);\n }\n\n const result = addPluginToProject(projectRoot, packageName);\n\n if (!result.success) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n if (result.files.length === 0) {\n console.log(`Package ${packageName} has no Claude assets to install.`);\n return;\n }\n\n console.log(`Installed ${packageName}@${result.version}:`);\n for (const file of result.files) {\n console.log(` .claude/${file}`);\n }\n });\n\nprogram\n .command('list')\n .description('List installed plugins and their assets')\n .action(() => {\n const projectRoot = findProjectRoot();\n const result = listPlugins(projectRoot);\n const pluginNames = Object.keys(result.plugins);\n\n if (pluginNames.length === 0) {\n console.log('No plugins installed.');\n return;\n }\n\n console.log('Installed Claude plugins:\\n');\n\n for (const [packageName, entry] of Object.entries(result.plugins)) {\n console.log(`${packageName}@${entry.version}`);\n for (const file of entry.files) {\n console.log(` .claude/${file}`);\n }\n console.log();\n }\n });\n\nprogram\n .command('remove')\n .description('Remove a plugin and its assets (called from plugin preuninstall)')\n .argument('[package]', 'Package name (auto-detected if run from preuninstall)')\n .action((packageArg?: string) => {\n const projectRoot = findProjectRoot();\n const packageName = packageArg || process.env.npm_package_name;\n\n if (!packageName) {\n console.error('Error: Could not determine package name.');\n console.error('Please provide the package name as an argument: claude-plugins remove <package>');\n process.exit(1);\n }\n\n const result = removePluginFromProject(projectRoot, packageName);\n\n if (!result.success) {\n console.error(result.error);\n process.exit(1);\n }\n\n for (const file of result.filesRemoved) {\n console.log(`Removed .claude/${file}`);\n }\n\n console.log(`Removed ${packageName}`);\n });\n\nprogram.parse();\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';\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 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 const packagePath = findPluginInNodeModules(packageName, projectRoot);\n\n if (!packagePath) {\n return {\n success: false,\n alreadyExists: false,\n packageName,\n files: [],\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 };\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 };\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","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"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,WAAAC,UAAS,QAAAC,OAAM,eAAe;AACvC,SAAS,qBAAqB;;;ACH9B,SAAS,cAAAC,aAAY,UAAAC,eAAc;AACnC,SAAS,QAAAC,aAAY;;;ACDrB,SAAS,YAAY,WAAW,cAAc,eAAe,cAAc;AAC3E,SAAS,SAAS,YAAY;AAE9B,IAAM,gBAAgB;AAWtB,SAAS,gBAAgB,aAA6B;AACpD,SAAO,KAAK,aAAa,WAAW,aAAa;AACnD;AAEO,SAAS,aAAa,aAA+B;AAC1D,QAAM,eAAe,gBAAgB,WAAW;AAEhD,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,WAAO,EAAE,SAAS,CAAC,EAAE;AAAA,EACvB;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,cAAc,OAAO;AAClD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,EAAE,SAAS,CAAC,EAAE;AAAA,EACvB;AACF;AAEO,SAAS,cAAc,aAAqB,UAA0B;AAC3E,QAAM,eAAe,gBAAgB,WAAW;AAChD,QAAM,YAAY,QAAQ,YAAY;AAEtC,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAEA,gBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AACtE;AAEO,SAAS,UACd,aACA,aACA,SACA,OACM;AACN,QAAM,WAAW,aAAa,WAAW;AAEzC,WAAS,QAAQ,WAAW,IAAI;AAAA,IAC9B;AAAA,IACA;AAAA,EACF;AAEA,gBAAc,aAAa,QAAQ;AACrC;AAcO,SAAS,qBAAqB,aAA+B;AAClE,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,YAAY,KAAK,aAAa,SAAS;AAC7C,QAAM,eAAyB,CAAC;AAEhC,aAAW,CAAC,aAAa,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AACnE,eAAW,QAAQ,MAAM,OAAO;AAC9B,YAAM,WAAW,KAAK,WAAW,IAAI;AACrC,UAAI,WAAW,QAAQ,GAAG;AACxB,eAAO,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACjD,qBAAa,KAAK,IAAI;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACxFA,SAAS,cAAAC,aAAY,aAAAC,YAAW,QAAQ,aAAa,UAAU,gBAAAC,qBAAoB;AACnF,SAAS,qBAAqB;AAC9B,SAAS,WAAAC,UAAS,QAAAC,OAAM,gBAAgB;AAExC,IAAM,aAAa,CAAC,UAAU,YAAY,UAAU,OAAO;AAQpD,SAAS,kBAAkB,aAA6B;AAC7D,MAAI;AACF,UAAM,UAAU,KAAK,MAAMF,cAAaE,MAAK,aAAa,cAAc,GAAG,OAAO,CAAC;AACnF,WAAO,QAAQ,WAAW;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,KAAa,MAAoB;AACtD,SAAO,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC;AACvC;AAEA,SAAS,SAAS,KAAa,MAAoB;AACjD,QAAM,UAAUA,MAAK,MAAM,IAAI;AAC/B,MAAI,CAACJ,YAAW,OAAO,GAAG;AACxB,IAAAC,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EACxC;AACA,SAAO,KAAK,IAAI;AAClB;AAEA,SAAS,iBAAiB,UAA2B;AACnD,SAAO,SAAS,QAAQ,EAAE,YAAY;AACxC;AAEA,SAAS,YAAY,UAA2B;AAC9C,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,SAAS,QAAQ;AAC9B,SAAO,KAAK,OAAO,KAAK,SAAS;AACnC;AAEO,SAAS,iBACd,aACA,aACY;AACZ,QAAM,YAAYG,MAAK,aAAa,SAAS;AAC7C,QAAM,cAAwB,CAAC;AAE/B,aAAW,YAAY,YAAY;AACjC,UAAM,aAAaA,MAAK,aAAa,QAAQ;AAE7C,QAAI,CAACJ,YAAW,UAAU,GAAG;AAC3B;AAAA,IACF;AAEA,UAAM,YAAYI,MAAK,WAAW,QAAQ;AAE1C,QAAI,CAACJ,YAAW,SAAS,GAAG;AAC1B,MAAAC,WAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,IAC1C;AAEA,UAAM,QAAQ,YAAY,UAAU;AAEpC,eAAW,QAAQ,OAAO;AACxB,UAAI,SAAS,WAAY;AAEzB,YAAM,WAAWG,MAAK,YAAY,IAAI;AACtC,YAAM,aAAaA,MAAK,WAAW,IAAI;AAEvC,UAAI,aAAa,UAAU;AAEzB,YAAI,iBAAiB,QAAQ,GAAG;AAC9B,wBAAc,UAAU,UAAU;AAClC,sBAAY,KAAK,GAAG,QAAQ,IAAI,IAAI,EAAE;AAAA,QACxC;AAAA,MACF,OAAO;AAEL,YAAI,YAAY,QAAQ,GAAG;AACzB,mBAAS,UAAU,UAAU;AAC7B,sBAAY,KAAK,GAAG,QAAQ,IAAI,IAAI,EAAE;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS,kBAAkB,WAAW;AAAA,EACxC;AACF;AAEO,SAAS,wBACd,aACA,aACe;AAEf,QAAM,eAAeA,MAAK,aAAa,gBAAgB,WAAW;AAClE,MAAIJ,YAAW,YAAY,GAAG;AAC5B,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAMK,WAAU,cAAcD,MAAK,aAAa,cAAc,CAAC;AAC/D,UAAM,WAAWC,SAAQ,QAAQ,GAAG,WAAW,eAAe;AAC9D,WAAOF,SAAQ,QAAQ;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,aAA8B;AACtD,aAAW,YAAY,YAAY;AACjC,UAAM,UAAUC,MAAK,aAAa,QAAQ;AAC1C,QAAIJ,YAAW,OAAO,GAAG;AACvB,YAAM,QAAQ,YAAY,OAAO,EAAE,OAAO,OAAK,MAAM,UAAU;AAC/D,UAAI,MAAM,SAAS,GAAG;AACpB,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AF5EO,SAAS,YACd,aACA,UAA+B,CAAC,GACpB;AACZ,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,cAAc,OAAO,KAAK,SAAS,OAAO,EAAE;AAElD,MAAI,gBAAgB,GAAG;AACrB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,gBAAgB,CAAC;AAAA,MACjB,WAAW,CAAC;AAAA,MACZ,kBAAkB,CAAC;AAAA,IACrB;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAI,YAAY;AAChB,QAAI,SAAS;AAEb,eAAW,CAAC,aAAa,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AACnE,YAAM,cAAc,wBAAwB,aAAa,WAAW;AAEpE,UAAI,CAAC,aAAa;AAChB,oBAAY;AACZ,iBAAS,GAAG,WAAW;AACvB;AAAA,MACF;AAEA,YAAM,iBAAiB,kBAAkB,WAAW;AACpD,UAAI,mBAAmB,MAAM,SAAS;AACpC,oBAAY;AACZ,iBAAS,GAAG,WAAW,aAAa,MAAM,OAAO,WAAM,cAAc;AACrE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ;AAAA,QACA,gBAAgB,CAAC;AAAA,QACjB,WAAW,CAAC;AAAA,QACZ,kBAAkB,CAAC;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAGA,uBAAqB,WAAW;AAGhC,QAAM,cAAc,EAAE,SAAS,CAAC,EAAiC;AACjE,QAAM,gBAAgB,oBAAI,IAAoB;AAC9C,QAAM,YAAsB,CAAC;AAC7B,QAAM,iBAA2B,CAAC;AAClC,QAAM,mBAAgF,CAAC;AACvF,MAAI,aAAa;AAEjB,aAAW,CAAC,WAAW,KAAK,OAAO,QAAQ,SAAS,OAAO,GAAG;AAC5D,UAAM,cAAc,wBAAwB,aAAa,WAAW;AAEpE,QAAI,CAAC,aAAa;AAChB,qBAAe,KAAK,WAAW;AAC/B;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,WAAW,GAAG;AAC3B;AAAA,IACF;AAEA,UAAM,SAAS,iBAAiB,aAAa,WAAW;AAExD,QAAI,OAAO,MAAM,SAAS,GAAG;AAE3B,iBAAW,QAAQ,OAAO,OAAO;AAC/B,cAAM,gBAAgB,cAAc,IAAI,IAAI;AAC5C,YAAI,eAAe;AACjB,oBAAU,KAAK,GAAG,IAAI,KAAK,aAAa,OAAO,WAAW,GAAG;AAAA,QAC/D;AACA,sBAAc,IAAI,MAAM,WAAW;AAAA,MACrC;AAEA,kBAAY,QAAQ,WAAW,IAAI;AAAA,QACjC,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,MAChB;AACA,oBAAc,OAAO,MAAM;AAC3B,uBAAiB,KAAK;AAAA,QACpB,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,WAAW,OAAO,MAAM;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,gBAAc,aAAa,WAAW;AAEtC,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR;AAAA,IACA,aAAa,OAAO,KAAK,YAAY,OAAO,EAAE;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,mBACd,aACA,aACW;AACX,QAAM,cAAc,wBAAwB,aAAa,WAAW;AAEpE,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,eAAe;AAAA,MACf;AAAA,MACA,OAAO,CAAC;AAAA,MACR,OAAO,WAAW,WAAW;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,CAAC,UAAU,WAAW,GAAG;AAC3B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,eAAe;AAAA,MACf;AAAA,MACA,OAAO,CAAC;AAAA,IACV;AAAA,EACF;AAGA,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,WAAW,SAAS,QAAQ,WAAW;AAE7C,MAAI,UAAU;AACZ,UAAM,YAAYM,MAAK,aAAa,SAAS;AAC7C,eAAW,QAAQ,SAAS,OAAO;AACjC,YAAM,WAAWA,MAAK,WAAW,IAAI;AACrC,UAAIC,YAAW,QAAQ,GAAG;AACxB,QAAAC,QAAO,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,iBAAiB,aAAa,WAAW;AAExD,MAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,cAAU,aAAa,aAAa,OAAO,SAAS,OAAO,KAAK;AAAA,EAClE;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,eAAe,CAAC,CAAC;AAAA,IACjB;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,YAAY,aAAiC;AAC3D,QAAM,WAAW,aAAa,WAAW;AACzC,SAAO,EAAE,SAAS,SAAS,QAAQ;AACrC;AAEO,SAAS,wBACd,aACA,aACc;AACd,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,QAAQ,SAAS,QAAQ,WAAW;AAE1C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,cAAc,CAAC;AAAA,MACf,OAAO,UAAU,WAAW;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,YAAYF,MAAK,aAAa,SAAS;AAC7C,QAAM,eAAyB,CAAC;AAGhC,aAAW,QAAQ,MAAM,OAAO;AAC9B,UAAM,WAAWA,MAAK,WAAW,IAAI;AACrC,QAAIC,YAAW,QAAQ,GAAG;AACxB,MAAAC,QAAO,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACjD,mBAAa,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AAGA,SAAO,SAAS,QAAQ,WAAW;AACnC,gBAAc,aAAa,QAAQ;AAEnC,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EACF;AACF;;;AD1PA,IAAM,YAAYC,SAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,SAAS,aAAqB;AAC5B,MAAI;AACF,UAAM,UAAUC,MAAK,WAAW,MAAM,cAAc;AACpD,UAAM,MAAM,KAAK,MAAMC,cAAa,SAAS,OAAO,CAAC;AACrD,WAAO,IAAI,WAAW;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAA0B;AACjC,MAAI,MAAM,QAAQ,IAAI;AAEtB,SAAO,QAAQ,KAAK;AAElB,QAAI,CAAC,IAAI,SAAS,cAAc,KAAKC,YAAWF,MAAK,KAAK,cAAc,CAAC,GAAG;AAC1E,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,KAAK,IAAI;AAAA,EACzB;AAEA,SAAO,QAAQ,IAAI;AACrB;AAEA,QACG,KAAK,gBAAgB,EACrB,YAAY,oDAAoD,EAChE,QAAQ,WAAW,CAAC;AAEvB,QACG,QAAQ,SAAS,EACjB,YAAY,qDAAqD,EACjE,OAAO,eAAe,mCAAmC,EACzD,OAAO,CAAC,YAAiC;AACxC,QAAM,cAAc,gBAAgB;AACpC,QAAM,SAAS,YAAY,aAAa,OAAO;AAE/C,MAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,IAAI,OAAO,UAAU,kBAAkB;AAC/C;AAAA,EACF;AAEA,MAAI,QAAQ,OAAO;AACjB,YAAQ,IAAI,oCAAoC;AAAA,EAClD;AAEA,aAAW,UAAU,OAAO,kBAAkB;AAC5C,YAAQ,IAAI,eAAe,OAAO,IAAI,IAAI,OAAO,OAAO,KAAK,OAAO,SAAS,SAAS;AAAA,EACxF;AAEA,MAAI,OAAO,eAAe,SAAS,GAAG;AACpC,YAAQ,IAAI;AAAA,UAAa,OAAO,eAAe,MAAM,uCAAuC;AAC5F,eAAW,QAAQ,OAAO,gBAAgB;AACxC,cAAQ,IAAI,OAAO,IAAI,EAAE;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,YAAQ,IAAI;AAAA,WAAc,OAAO,UAAU,MAAM,8DAA8D;AAC/G,eAAW,YAAY,OAAO,WAAW;AACvC,cAAQ,IAAI,KAAK,QAAQ,EAAE;AAAA,IAC7B;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,QAAW,OAAO,UAAU,eAAe,OAAO,WAAW,aAAa;AACxF,CAAC;AAEH,QACG,QAAQ,KAAK,EACb,YAAY,2DAA2D,EACvE,SAAS,aAAa,sDAAsD,EAC5E,OAAO,CAAC,eAAwB;AAC/B,QAAM,cAAc,gBAAgB;AACpC,QAAM,cAAc,cAAc,QAAQ,IAAI;AAE9C,MAAI,CAAC,aAAa;AAChB,YAAQ,MAAM,0CAA0C;AACxD,YAAQ,MAAM,8EAA8E;AAC5F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,mBAAmB,aAAa,WAAW;AAE1D,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,YAAQ,IAAI,WAAW,WAAW,mCAAmC;AACrE;AAAA,EACF;AAEA,UAAQ,IAAI,aAAa,WAAW,IAAI,OAAO,OAAO,GAAG;AACzD,aAAW,QAAQ,OAAO,OAAO;AAC/B,YAAQ,IAAI,aAAa,IAAI,EAAE;AAAA,EACjC;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,yCAAyC,EACrD,OAAO,MAAM;AACZ,QAAM,cAAc,gBAAgB;AACpC,QAAM,SAAS,YAAY,WAAW;AACtC,QAAM,cAAc,OAAO,KAAK,OAAO,OAAO;AAE9C,MAAI,YAAY,WAAW,GAAG;AAC5B,YAAQ,IAAI,uBAAuB;AACnC;AAAA,EACF;AAEA,UAAQ,IAAI,6BAA6B;AAEzC,aAAW,CAAC,aAAa,KAAK,KAAK,OAAO,QAAQ,OAAO,OAAO,GAAG;AACjE,YAAQ,IAAI,GAAG,WAAW,IAAI,MAAM,OAAO,EAAE;AAC7C,eAAW,QAAQ,MAAM,OAAO;AAC9B,cAAQ,IAAI,aAAa,IAAI,EAAE;AAAA,IACjC;AACA,YAAQ,IAAI;AAAA,EACd;AACF,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,kEAAkE,EAC9E,SAAS,aAAa,uDAAuD,EAC7E,OAAO,CAAC,eAAwB;AAC/B,QAAM,cAAc,gBAAgB;AACpC,QAAM,cAAc,cAAc,QAAQ,IAAI;AAE9C,MAAI,CAAC,aAAa;AAChB,YAAQ,MAAM,0CAA0C;AACxD,YAAQ,MAAM,iFAAiF;AAC/F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,wBAAwB,aAAa,WAAW;AAE/D,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ,MAAM,OAAO,KAAK;AAC1B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,aAAW,QAAQ,OAAO,cAAc;AACtC,YAAQ,IAAI,mBAAmB,IAAI,EAAE;AAAA,EACvC;AAEA,UAAQ,IAAI,WAAW,WAAW,EAAE;AACtC,CAAC;AAEH,QAAQ,MAAM;","names":["existsSync","readFileSync","dirname","join","existsSync","rmSync","join","existsSync","mkdirSync","readFileSync","dirname","join","require","join","existsSync","rmSync","dirname","join","readFileSync","existsSync"]}
package/dist/index.d.ts CHANGED
@@ -21,10 +21,6 @@ declare function copyPluginAssets(packagePath: string, projectRoot: string): Cop
21
21
  declare function findPluginInNodeModules(packageName: string, projectRoot: string): string | null;
22
22
  declare function hasAssets(packagePath: string): boolean;
23
23
 
24
- declare function injectPrepareHook(projectRoot: string): boolean;
25
- declare function hasPrepareHook(projectRoot: string): boolean;
26
- declare function removePrepareHook(projectRoot: string): boolean;
27
-
28
24
  interface SyncResult {
29
25
  success: boolean;
30
26
  synced: boolean;
@@ -45,7 +41,6 @@ interface AddResult {
45
41
  packageName: string;
46
42
  version?: string;
47
43
  files: string[];
48
- hookInjected: boolean;
49
44
  error?: string;
50
45
  }
51
46
  interface ListResult {
@@ -64,4 +59,15 @@ declare function addPluginToProject(projectRoot: string, packageName: string): A
64
59
  declare function listPlugins(projectRoot: string): ListResult;
65
60
  declare function removePluginFromProject(projectRoot: string, packageName: string): RemoveResult;
66
61
 
67
- export { type AddResult, type ListResult, type Manifest, type PluginEntry, type RemoveResult, type SyncResult, addPlugin, addPluginToProject, cleanupManifestFiles, copyPluginAssets, findPluginInNodeModules, getPackageVersion, getPlugins, hasAssets, hasPrepareHook, injectPrepareHook, listPlugins, readManifest, removePlugin, removePluginFromProject, removePrepareHook, syncPlugins, writeManifest };
62
+ /**
63
+ * Add the current plugin to the project.
64
+ * Called from plugin's postinstall: node -e "require('@leeovery/claude-manager').add()"
65
+ */
66
+ declare function add(): void;
67
+ /**
68
+ * Remove the current plugin from the project.
69
+ * Called from plugin's preuninstall: node -e "require('@leeovery/claude-manager').remove()"
70
+ */
71
+ declare function remove(): void;
72
+
73
+ export { type AddResult, type ListResult, type Manifest, type PluginEntry, type RemoveResult, type SyncResult, add, addPlugin, addPluginToProject, cleanupManifestFiles, copyPluginAssets, findPluginInNodeModules, getPackageVersion, getPlugins, hasAssets, listPlugins, readManifest, remove, removePlugin, removePluginFromProject, syncPlugins, writeManifest };
package/dist/index.js CHANGED
@@ -1,3 +1,11 @@
1
+ // src/index.ts
2
+ import { existsSync as existsSync4 } from "fs";
3
+ import { join as join4, resolve } from "path";
4
+
5
+ // src/lib/sync.ts
6
+ import { existsSync as existsSync3, rmSync as rmSync2 } from "fs";
7
+ import { join as join3 } from "path";
8
+
1
9
  // src/lib/manifest.ts
2
10
  import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "fs";
3
11
  import { dirname, join } from "path";
@@ -149,84 +157,7 @@ function hasAssets(packagePath) {
149
157
  return false;
150
158
  }
151
159
 
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
160
  // src/lib/sync.ts
228
- import { existsSync as existsSync4, rmSync as rmSync2 } from "fs";
229
- import { join as join4 } from "path";
230
161
  function syncPlugins(projectRoot, options = {}) {
231
162
  const manifest = readManifest(projectRoot);
232
163
  const pluginCount = Object.keys(manifest.plugins).length;
@@ -321,7 +252,6 @@ function syncPlugins(projectRoot, options = {}) {
321
252
  };
322
253
  }
323
254
  function addPluginToProject(projectRoot, packageName) {
324
- const hookInjected = injectPrepareHook(projectRoot);
325
255
  const packagePath = findPluginInNodeModules(packageName, projectRoot);
326
256
  if (!packagePath) {
327
257
  return {
@@ -329,7 +259,6 @@ function addPluginToProject(projectRoot, packageName) {
329
259
  alreadyExists: false,
330
260
  packageName,
331
261
  files: [],
332
- hookInjected,
333
262
  error: `Package ${packageName} not found in node_modules`
334
263
  };
335
264
  }
@@ -338,17 +267,16 @@ function addPluginToProject(projectRoot, packageName) {
338
267
  success: true,
339
268
  alreadyExists: false,
340
269
  packageName,
341
- files: [],
342
- hookInjected
270
+ files: []
343
271
  };
344
272
  }
345
273
  const manifest = readManifest(projectRoot);
346
274
  const existing = manifest.plugins[packageName];
347
275
  if (existing) {
348
- const claudeDir = join4(projectRoot, ".claude");
276
+ const claudeDir = join3(projectRoot, ".claude");
349
277
  for (const file of existing.files) {
350
- const fullPath = join4(claudeDir, file);
351
- if (existsSync4(fullPath)) {
278
+ const fullPath = join3(claudeDir, file);
279
+ if (existsSync3(fullPath)) {
352
280
  rmSync2(fullPath, { recursive: true, force: true });
353
281
  }
354
282
  }
@@ -362,8 +290,7 @@ function addPluginToProject(projectRoot, packageName) {
362
290
  alreadyExists: !!existing,
363
291
  packageName,
364
292
  version: result.version,
365
- files: result.files,
366
- hookInjected
293
+ files: result.files
367
294
  };
368
295
  }
369
296
  function listPlugins(projectRoot) {
@@ -381,11 +308,11 @@ function removePluginFromProject(projectRoot, packageName) {
381
308
  error: `Plugin ${packageName} is not installed`
382
309
  };
383
310
  }
384
- const claudeDir = join4(projectRoot, ".claude");
311
+ const claudeDir = join3(projectRoot, ".claude");
385
312
  const filesRemoved = [];
386
313
  for (const file of entry.files) {
387
- const fullPath = join4(claudeDir, file);
388
- if (existsSync4(fullPath)) {
314
+ const fullPath = join3(claudeDir, file);
315
+ if (existsSync3(fullPath)) {
389
316
  rmSync2(fullPath, { recursive: true, force: true });
390
317
  filesRemoved.push(file);
391
318
  }
@@ -398,7 +325,62 @@ function removePluginFromProject(projectRoot, packageName) {
398
325
  filesRemoved
399
326
  };
400
327
  }
328
+
329
+ // src/index.ts
330
+ function findProjectRoot() {
331
+ const initCwd = process.env.INIT_CWD;
332
+ if (initCwd && existsSync4(join4(initCwd, "package.json"))) {
333
+ return initCwd;
334
+ }
335
+ let dir = process.cwd();
336
+ while (dir !== "/") {
337
+ if (!dir.includes("node_modules") && existsSync4(join4(dir, "package.json"))) {
338
+ return dir;
339
+ }
340
+ dir = resolve(dir, "..");
341
+ }
342
+ return process.cwd();
343
+ }
344
+ function add() {
345
+ const packageName = process.env.npm_package_name;
346
+ if (!packageName) {
347
+ console.error("[claude-manager] Could not determine package name");
348
+ process.exit(1);
349
+ }
350
+ const projectRoot = findProjectRoot();
351
+ const result = addPluginToProject(projectRoot, packageName);
352
+ if (!result.success) {
353
+ console.error(`[claude-manager] Error: ${result.error}`);
354
+ process.exit(1);
355
+ }
356
+ if (result.files.length === 0) {
357
+ return;
358
+ }
359
+ console.log(`[claude-manager] Installed ${packageName}@${result.version}:`);
360
+ for (const file of result.files) {
361
+ console.log(` .claude/${file}`);
362
+ }
363
+ }
364
+ function remove() {
365
+ const packageName = process.env.npm_package_name;
366
+ if (!packageName) {
367
+ console.error("[claude-manager] Could not determine package name");
368
+ process.exit(1);
369
+ }
370
+ const projectRoot = findProjectRoot();
371
+ const result = removePluginFromProject(projectRoot, packageName);
372
+ if (!result.success) {
373
+ return;
374
+ }
375
+ if (result.filesRemoved.length > 0) {
376
+ console.log(`[claude-manager] Removed ${packageName}:`);
377
+ for (const file of result.filesRemoved) {
378
+ console.log(` .claude/${file}`);
379
+ }
380
+ }
381
+ }
401
382
  export {
383
+ add,
402
384
  addPlugin,
403
385
  addPluginToProject,
404
386
  cleanupManifestFiles,
@@ -407,13 +389,11 @@ export {
407
389
  getPackageVersion,
408
390
  getPlugins,
409
391
  hasAssets,
410
- hasPrepareHook,
411
- injectPrepareHook,
412
392
  listPlugins,
413
393
  readManifest,
394
+ remove,
414
395
  removePlugin,
415
396
  removePluginFromProject,
416
- removePrepareHook,
417
397
  syncPlugins,
418
398
  writeManifest
419
399
  };
package/dist/index.js.map CHANGED
@@ -1 +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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/sync.ts","../src/lib/manifest.ts","../src/lib/copier.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { addPluginToProject, removePluginFromProject } from './lib/sync.js';\n\n// Main exports for programmatic usage\nexport {\n readManifest,\n writeManifest,\n addPlugin,\n removePlugin,\n getPlugins,\n cleanupManifestFiles,\n type Manifest,\n type PluginEntry,\n} from './lib/manifest.js';\n\nexport {\n copyPluginAssets,\n findPluginInNodeModules,\n hasAssets,\n getPackageVersion,\n} from './lib/copier.js';\n\nexport {\n syncPlugins,\n addPluginToProject,\n listPlugins,\n removePluginFromProject,\n type SyncResult,\n type AddResult,\n type ListResult,\n type RemoveResult,\n} from './lib/sync.js';\n\n/**\n * Find project root by walking up from INIT_CWD or cwd\n */\nfunction findProjectRoot(): string {\n // INIT_CWD is set by npm/pnpm to the directory where the command was run\n const initCwd = process.env.INIT_CWD;\n if (initCwd && existsSync(join(initCwd, 'package.json'))) {\n return initCwd;\n }\n\n let dir = process.cwd();\n while (dir !== '/') {\n if (!dir.includes('node_modules') && existsSync(join(dir, 'package.json'))) {\n return dir;\n }\n dir = resolve(dir, '..');\n }\n return process.cwd();\n}\n\n/**\n * Add the current plugin to the project.\n * Called from plugin's postinstall: node -e \"require('@leeovery/claude-manager').add()\"\n */\nexport function add(): void {\n const packageName = process.env.npm_package_name;\n if (!packageName) {\n console.error('[claude-manager] Could not determine package name');\n process.exit(1);\n }\n\n const projectRoot = findProjectRoot();\n const result = addPluginToProject(projectRoot, packageName);\n\n if (!result.success) {\n console.error(`[claude-manager] Error: ${result.error}`);\n process.exit(1);\n }\n\n if (result.files.length === 0) {\n return; // No assets to install, stay silent\n }\n\n console.log(`[claude-manager] Installed ${packageName}@${result.version}:`);\n for (const file of result.files) {\n console.log(` .claude/${file}`);\n }\n}\n\n/**\n * Remove the current plugin from the project.\n * Called from plugin's preuninstall: node -e \"require('@leeovery/claude-manager').remove()\"\n */\nexport function remove(): void {\n const packageName = process.env.npm_package_name;\n if (!packageName) {\n console.error('[claude-manager] Could not determine package name');\n process.exit(1);\n }\n\n const projectRoot = findProjectRoot();\n const result = removePluginFromProject(projectRoot, packageName);\n\n if (!result.success) {\n // Silently ignore if plugin not in manifest (might already be removed)\n return;\n }\n\n if (result.filesRemoved.length > 0) {\n console.log(`[claude-manager] Removed ${packageName}:`);\n for (const file of result.filesRemoved) {\n console.log(` .claude/${file}`);\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';\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 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 const packagePath = findPluginInNodeModules(packageName, projectRoot);\n\n if (!packagePath) {\n return {\n success: false,\n alreadyExists: false,\n packageName,\n files: [],\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 };\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 };\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","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"],"mappings":"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","names":["existsSync","join","existsSync","rmSync","join","existsSync","mkdirSync","readFileSync","dirname","join","require","join","existsSync","rmSync","existsSync","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leeovery/claude-manager",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "packageManager": "pnpm@10.27.0",
5
5
  "description": "Plugin manager for Claude Code skills and commands",
6
6
  "type": "module",
@@ -14,7 +14,6 @@
14
14
  "dev": "tsup --watch",
15
15
  "test": "vitest",
16
16
  "test:run": "vitest run",
17
- "postinstall": "[ -f dist/postinstall.js ] && node dist/postinstall.js || true",
18
17
  "prepublishOnly": "npm run build"
19
18
  },
20
19
  "files": [
@@ -1,38 +0,0 @@
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
@@ -1 +0,0 @@
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":[]}
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- injectPrepareHook
4
- } from "./chunk-2SM5GQ6P.js";
5
-
6
- // src/postinstall.ts
7
- import { existsSync, symlinkSync, mkdirSync } from "fs";
8
- import { join, resolve, dirname } from "path";
9
- import { fileURLToPath } from "url";
10
- var __dirname = dirname(fileURLToPath(import.meta.url));
11
- function findProjectRoot() {
12
- const initCwd = process.env.INIT_CWD;
13
- if (initCwd && existsSync(join(initCwd, "package.json"))) {
14
- return initCwd;
15
- }
16
- let dir = process.cwd();
17
- while (dir !== "/") {
18
- if (!dir.includes("node_modules") && existsSync(join(dir, "package.json"))) {
19
- return dir;
20
- }
21
- dir = resolve(dir, "..");
22
- }
23
- return null;
24
- }
25
- function main() {
26
- if (process.env.CI || process.env.npm_config_global) {
27
- return;
28
- }
29
- const projectRoot = findProjectRoot();
30
- if (!projectRoot) {
31
- return;
32
- }
33
- if (injectPrepareHook(projectRoot)) {
34
- console.log("[claude-manager] Added prepare hook to package.json");
35
- console.log("[claude-manager] Plugins will sync on npm install AND npm update");
36
- }
37
- ensureBinSymlink(projectRoot);
38
- }
39
- function ensureBinSymlink(projectRoot) {
40
- const binDir = join(projectRoot, "node_modules", ".bin");
41
- const binPath = join(binDir, "claude-plugins");
42
- if (existsSync(binPath)) {
43
- return;
44
- }
45
- const cliPath = join(__dirname, "cli.js");
46
- if (!existsSync(cliPath)) {
47
- return;
48
- }
49
- try {
50
- if (!existsSync(binDir)) {
51
- mkdirSync(binDir, { recursive: true });
52
- }
53
- symlinkSync(cliPath, binPath);
54
- } catch {
55
- }
56
- }
57
- main();
58
- //# sourceMappingURL=postinstall.js.map
@@ -1 +0,0 @@
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, symlinkSync, mkdirSync, unlinkSync } from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { injectPrepareHook } from './lib/hooks.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\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 // Ensure bin symlink exists (needed for pnpm which doesn't link transitive dep bins)\n ensureBinSymlink(projectRoot);\n}\n\nfunction ensureBinSymlink(projectRoot: string): void {\n const binDir = join(projectRoot, 'node_modules', '.bin');\n const binPath = join(binDir, 'claude-plugins');\n\n // Skip if symlink already exists\n if (existsSync(binPath)) {\n return;\n }\n\n // Our CLI is in the same directory as this postinstall script (dist/)\n const cliPath = join(__dirname, 'cli.js');\n\n if (!existsSync(cliPath)) {\n return;\n }\n\n try {\n // Ensure .bin directory exists\n if (!existsSync(binDir)) {\n mkdirSync(binDir, { recursive: true });\n }\n\n // Create symlink\n symlinkSync(cliPath, binPath);\n } catch {\n // Silently fail - user can add as direct dependency if needed\n }\n}\n\nmain();\n"],"mappings":";;;;;;AAQA,SAAS,YAAY,aAAa,iBAA6B;AAC/D,SAAS,MAAM,SAAS,eAAe;AACvC,SAAS,qBAAqB;AAG9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,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;AAGA,mBAAiB,WAAW;AAC9B;AAEA,SAAS,iBAAiB,aAA2B;AACnD,QAAM,SAAS,KAAK,aAAa,gBAAgB,MAAM;AACvD,QAAM,UAAU,KAAK,QAAQ,gBAAgB;AAG7C,MAAI,WAAW,OAAO,GAAG;AACvB;AAAA,EACF;AAGA,QAAM,UAAU,KAAK,WAAW,QAAQ;AAExC,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB;AAAA,EACF;AAEA,MAAI;AAEF,QAAI,CAAC,WAAW,MAAM,GAAG;AACvB,gBAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,IACvC;AAGA,gBAAY,SAAS,OAAO;AAAA,EAC9B,QAAQ;AAAA,EAER;AACF;AAEA,KAAK;","names":[]}