@leeovery/claude-manager 2.0.6 → 2.0.8
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 +15 -42
- package/dist/cli.js +2 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.js +87 -92
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/dist/chunk-2SM5GQ6P.js +0 -38
- package/dist/chunk-2SM5GQ6P.js.map +0 -1
- package/dist/postinstall.js +0 -58
- package/dist/postinstall.js.map +0 -1
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
74
|
-
3.
|
|
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.
|
|
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
|
|
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-
|
|
136
|
-
"preuninstall": "claude-
|
|
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
|
-
-
|
|
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":"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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"]}
|
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
|
-
|
|
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, readFileSync as readFileSync3 } 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 =
|
|
276
|
+
const claudeDir = join3(projectRoot, ".claude");
|
|
349
277
|
for (const file of existing.files) {
|
|
350
|
-
const fullPath =
|
|
351
|
-
if (
|
|
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 =
|
|
311
|
+
const claudeDir = join3(projectRoot, ".claude");
|
|
385
312
|
const filesRemoved = [];
|
|
386
313
|
for (const file of entry.files) {
|
|
387
|
-
const fullPath =
|
|
388
|
-
if (
|
|
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,77 @@ function removePluginFromProject(projectRoot, packageName) {
|
|
|
398
325
|
filesRemoved
|
|
399
326
|
};
|
|
400
327
|
}
|
|
328
|
+
|
|
329
|
+
// src/index.ts
|
|
330
|
+
function getPackageName() {
|
|
331
|
+
if (process.env.npm_package_name) {
|
|
332
|
+
return process.env.npm_package_name;
|
|
333
|
+
}
|
|
334
|
+
const pkgPath = join4(process.cwd(), "package.json");
|
|
335
|
+
if (existsSync4(pkgPath)) {
|
|
336
|
+
try {
|
|
337
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
338
|
+
return pkg.name || null;
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
function findProjectRoot() {
|
|
346
|
+
const initCwd = process.env.INIT_CWD;
|
|
347
|
+
if (initCwd && existsSync4(join4(initCwd, "package.json"))) {
|
|
348
|
+
return initCwd;
|
|
349
|
+
}
|
|
350
|
+
let dir = process.cwd();
|
|
351
|
+
while (dir !== "/") {
|
|
352
|
+
if (!dir.includes("node_modules") && existsSync4(join4(dir, "package.json"))) {
|
|
353
|
+
return dir;
|
|
354
|
+
}
|
|
355
|
+
dir = resolve(dir, "..");
|
|
356
|
+
}
|
|
357
|
+
return process.cwd();
|
|
358
|
+
}
|
|
359
|
+
function add() {
|
|
360
|
+
const packageName = getPackageName();
|
|
361
|
+
if (!packageName) {
|
|
362
|
+
console.error("[claude-manager] Could not determine package name");
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
const projectRoot = findProjectRoot();
|
|
366
|
+
const result = addPluginToProject(projectRoot, packageName);
|
|
367
|
+
if (!result.success) {
|
|
368
|
+
console.error(`[claude-manager] Error: ${result.error}`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
if (result.files.length === 0) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
console.log(`[claude-manager] Installed ${packageName}@${result.version}:`);
|
|
375
|
+
for (const file of result.files) {
|
|
376
|
+
console.log(` .claude/${file}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function remove() {
|
|
380
|
+
const packageName = getPackageName();
|
|
381
|
+
if (!packageName) {
|
|
382
|
+
console.error("[claude-manager] Could not determine package name");
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
const projectRoot = findProjectRoot();
|
|
386
|
+
const result = removePluginFromProject(projectRoot, packageName);
|
|
387
|
+
if (!result.success) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (result.filesRemoved.length > 0) {
|
|
391
|
+
console.log(`[claude-manager] Removed ${packageName}:`);
|
|
392
|
+
for (const file of result.filesRemoved) {
|
|
393
|
+
console.log(` .claude/${file}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
401
397
|
export {
|
|
398
|
+
add,
|
|
402
399
|
addPlugin,
|
|
403
400
|
addPluginToProject,
|
|
404
401
|
cleanupManifestFiles,
|
|
@@ -407,13 +404,11 @@ export {
|
|
|
407
404
|
getPackageVersion,
|
|
408
405
|
getPlugins,
|
|
409
406
|
hasAssets,
|
|
410
|
-
hasPrepareHook,
|
|
411
|
-
injectPrepareHook,
|
|
412
407
|
listPlugins,
|
|
413
408
|
readManifest,
|
|
409
|
+
remove,
|
|
414
410
|
removePlugin,
|
|
415
411
|
removePluginFromProject,
|
|
416
|
-
removePrepareHook,
|
|
417
412
|
syncPlugins,
|
|
418
413
|
writeManifest
|
|
419
414
|
};
|
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":";AAAA,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;AAEO,SAAS,aAAa,aAAqB,aAA2B;AAC3E,QAAM,WAAW,aAAa,WAAW;AAEzC,SAAO,SAAS,QAAQ,WAAW;AAEnC,gBAAc,aAAa,QAAQ;AACrC;AAEO,SAAS,WAAW,aAAkD;AAC3E,SAAO,aAAa,WAAW,EAAE;AACnC;AAEO,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,cAAAA,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;;;AC5HA,SAAS,cAAAM,aAAY,gBAAAC,eAAc,iBAAAC,sBAAqB;AACxD,SAAS,QAAAC,aAAY;AAErB,IAAM,eAAe;AAKrB,IAAM,YAAY;AAOX,SAAS,kBAAkB,aAA8B;AAC9D,QAAM,kBAAkBA,MAAK,aAAa,cAAc;AAExD,MAAI,CAACH,YAAW,eAAe,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAUC,cAAa,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,IAAAC,eAAc,iBAAiB,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAElE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,aAA8B;AAC3D,QAAM,kBAAkBC,MAAK,aAAa,cAAc;AAExD,MAAI,CAACH,YAAW,eAAe,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAUC,cAAa,iBAAiB,OAAO;AACrD,UAAM,MAAmB,KAAK,MAAM,OAAO;AAE3C,WAAO,IAAI,UAAU,SAAS,GAAG,SAAS,YAAY,KAAK;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,aAA8B;AAC9D,QAAM,kBAAkBE,MAAK,aAAa,cAAc;AAExD,MAAI,CAACH,YAAW,eAAe,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAUC,cAAa,iBAAiB,OAAO;AACrD,UAAM,MAAmB,KAAK,MAAM,OAAO;AAE3C,QAAI,CAAC,IAAI,UAAU,SAAS,GAAG;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,IAAI,QAAQ,SAAS;AAElC,QAAI,CAAC,KAAK,SAAS,YAAY,GAAG;AAChC,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,KACX,QAAQ,OAAO,YAAY,IAAI,EAAE,EACjC,QAAQ,GAAG,YAAY,QAAQ,EAAE,EACjC,QAAQ,cAAc,EAAE;AAE3B,QAAI,QAAQ,KAAK,MAAM,IAAI;AACzB,aAAO,IAAI,QAAQ,SAAS;AAAA,IAC9B,OAAO;AACL,UAAI,QAAQ,SAAS,IAAI,QAAQ,KAAK;AAAA,IACxC;AAGA,QAAI,OAAO,KAAK,IAAI,OAAO,EAAE,WAAW,GAAG;AACzC,aAAO,IAAI;AAAA,IACb;AAEA,IAAAC,eAAc,iBAAiB,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAElE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AClHA,SAAS,cAAAE,aAAY,UAAAC,eAAc;AACnC,SAAS,QAAAC,aAAY;AAiDd,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;AAEX,QAAM,eAAe,kBAAkB,WAAW;AAElD,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;AAAA,MACA,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,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,WAAW,SAAS,QAAQ,WAAW;AAE7C,MAAI,UAAU;AACZ,UAAM,YAAYC,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,IACd;AAAA,EACF;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;","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, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { addPluginToProject, removePluginFromProject } from './lib/sync.js';\n\n/**\n * Get package name from env var or package.json in cwd\n */\nfunction getPackageName(): string | null {\n // Try env var first (set by npm, sometimes by pnpm)\n if (process.env.npm_package_name) {\n return process.env.npm_package_name;\n }\n\n // Fall back to reading package.json in cwd (where postinstall runs)\n const pkgPath = join(process.cwd(), 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.name || null;\n } catch {\n return null;\n }\n }\n\n return null;\n}\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 = getPackageName();\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 = getPackageName();\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","readFileSync","join","existsSync","rmSync","join","existsSync","mkdirSync","readFileSync","dirname","join","require","join","existsSync","rmSync","join","existsSync","readFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leeovery/claude-manager",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
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": [
|
package/dist/chunk-2SM5GQ6P.js
DELETED
|
@@ -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":[]}
|
package/dist/postinstall.js
DELETED
|
@@ -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
|
package/dist/postinstall.js.map
DELETED
|
@@ -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":[]}
|