claude-autopm 3.25.0 → 3.25.2
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/bin/commands/plugin.js
CHANGED
|
@@ -266,8 +266,10 @@ async function handleInstall(manager, pluginName, argv) {
|
|
|
266
266
|
console.log(chalk.gray(`npm package already installed: @claudeautopm/${fullPluginName}`));
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
//
|
|
270
|
-
console.log(chalk.gray('
|
|
269
|
+
// Discover and install plugin resources
|
|
270
|
+
console.log(chalk.gray('Discovering plugins...'));
|
|
271
|
+
await manager.initialize();
|
|
272
|
+
console.log(chalk.gray('Installing plugin resources...'));
|
|
271
273
|
const result = await manager.installPlugin(fullPluginName);
|
|
272
274
|
|
|
273
275
|
console.log(chalk.green(`\n✓ Plugin installed successfully!`));
|
|
@@ -131,23 +131,64 @@ class PluginManager extends EventEmitter {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Discover all installed plugins in node_modules
|
|
134
|
+
* Discover all installed plugins in node_modules (local, global, and bundled)
|
|
135
135
|
* Based on npm workspaces pattern from Context7
|
|
136
136
|
*/
|
|
137
137
|
async discoverPlugins() {
|
|
138
138
|
const pluginPattern = `${this.options.scopePrefix}/plugin-`;
|
|
139
|
-
const nodeModulesPath = this.options.pluginDir;
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const scopePath = path.join(nodeModulesPath, this.options.scopePrefix);
|
|
140
|
+
// Check multiple locations: local node_modules, global npm prefix, bundled packages/
|
|
141
|
+
const searchPaths = [this.options.pluginDir];
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
// Add global npm path
|
|
144
|
+
try {
|
|
145
|
+
const { execSync } = require('child_process');
|
|
146
|
+
const globalPath = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
147
|
+
if (globalPath && !searchPaths.includes(globalPath)) {
|
|
148
|
+
searchPaths.push(globalPath);
|
|
148
149
|
}
|
|
150
|
+
} catch {
|
|
151
|
+
// npm not available or error — skip global
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add bundled packages/ directory (for development / autopm source repo)
|
|
155
|
+
const bundledPath = path.join(this.options.projectRoot, 'packages');
|
|
156
|
+
if (fs.existsSync(bundledPath)) {
|
|
157
|
+
// Bundled plugins are at packages/plugin-*/plugin.json (not under @claudeautopm scope)
|
|
158
|
+
try {
|
|
159
|
+
const entries = fs.readdirSync(bundledPath);
|
|
160
|
+
for (const entry of entries) {
|
|
161
|
+
if (!entry.startsWith('plugin-')) continue;
|
|
162
|
+
const pluginJsonPath = path.join(bundledPath, entry, 'plugin.json');
|
|
163
|
+
if (!fs.existsSync(pluginJsonPath)) continue;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const metadata = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf-8'));
|
|
167
|
+
const fullName = `${this.options.scopePrefix}/${entry}`;
|
|
168
|
+
if (!this.plugins.has(fullName)) {
|
|
169
|
+
this.plugins.set(fullName, {
|
|
170
|
+
name: fullName,
|
|
171
|
+
path: path.join(bundledPath, entry),
|
|
172
|
+
metadata,
|
|
173
|
+
loaded: false
|
|
174
|
+
});
|
|
175
|
+
this.emit('discover:found', { name: fullName, source: 'bundled' });
|
|
176
|
+
}
|
|
177
|
+
} catch { /* skip invalid */ }
|
|
178
|
+
}
|
|
179
|
+
} catch { /* skip unreadable */ }
|
|
180
|
+
}
|
|
149
181
|
|
|
150
|
-
|
|
182
|
+
for (const nodeModulesPath of searchPaths) {
|
|
183
|
+
try {
|
|
184
|
+
// Check if scoped directory exists
|
|
185
|
+
const scopePath = path.join(nodeModulesPath, this.options.scopePrefix);
|
|
186
|
+
|
|
187
|
+
if (!fs.existsSync(scopePath)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const scopedPackages = fs.readdirSync(scopePath);
|
|
151
192
|
|
|
152
193
|
for (const packageName of scopedPackages) {
|
|
153
194
|
if (!packageName.startsWith('plugin-')) {
|
|
@@ -186,8 +227,9 @@ class PluginManager extends EventEmitter {
|
|
|
186
227
|
}
|
|
187
228
|
} catch (error) {
|
|
188
229
|
this.emit('discover:error', { error: error.message });
|
|
189
|
-
|
|
230
|
+
// Continue to next search path instead of failing
|
|
190
231
|
}
|
|
232
|
+
} // end for searchPaths
|
|
191
233
|
}
|
|
192
234
|
|
|
193
235
|
/**
|
|
@@ -223,7 +265,10 @@ class PluginManager extends EventEmitter {
|
|
|
223
265
|
* Implements factory pattern from unplugin Context7 research
|
|
224
266
|
*/
|
|
225
267
|
async loadPlugin(pluginName) {
|
|
226
|
-
|
|
268
|
+
let plugin = this.plugins.get(pluginName);
|
|
269
|
+
if (!plugin && !pluginName.includes('/')) {
|
|
270
|
+
plugin = this.plugins.get(`${this.options.scopePrefix}/${pluginName}`);
|
|
271
|
+
}
|
|
227
272
|
|
|
228
273
|
if (!plugin) {
|
|
229
274
|
throw new Error(`Plugin not found: ${pluginName}`);
|
|
@@ -252,7 +297,7 @@ class PluginManager extends EventEmitter {
|
|
|
252
297
|
|
|
253
298
|
this.emit('load:complete', {
|
|
254
299
|
name: pluginName,
|
|
255
|
-
agentCount: plugin.metadata.agents.length
|
|
300
|
+
agentCount: (plugin.metadata.agents || []).length
|
|
256
301
|
});
|
|
257
302
|
|
|
258
303
|
return plugin;
|
|
@@ -268,7 +313,7 @@ class PluginManager extends EventEmitter {
|
|
|
268
313
|
async registerAgents(plugin) {
|
|
269
314
|
const { metadata, path: pluginPath } = plugin;
|
|
270
315
|
|
|
271
|
-
for (const agentMeta of metadata.agents) {
|
|
316
|
+
for (const agentMeta of (metadata.agents || [])) {
|
|
272
317
|
const agentId = `${plugin.name}:${agentMeta.name}`;
|
|
273
318
|
const agentFilePath = path.join(pluginPath, agentMeta.file);
|
|
274
319
|
|
|
@@ -302,7 +347,11 @@ class PluginManager extends EventEmitter {
|
|
|
302
347
|
* Supports: agents, commands, rules, hooks, scripts (Schema v2.0)
|
|
303
348
|
*/
|
|
304
349
|
async installPlugin(pluginName) {
|
|
305
|
-
|
|
350
|
+
// Normalize name: accept both "plugin-obsidian" and "@claudeautopm/plugin-obsidian"
|
|
351
|
+
let plugin = this.plugins.get(pluginName);
|
|
352
|
+
if (!plugin && !pluginName.includes('/')) {
|
|
353
|
+
plugin = this.plugins.get(`${this.options.scopePrefix}/${pluginName}`);
|
|
354
|
+
}
|
|
306
355
|
|
|
307
356
|
if (!plugin) {
|
|
308
357
|
throw new Error(`Plugin not found: ${pluginName}`);
|
|
@@ -451,6 +500,26 @@ class PluginManager extends EventEmitter {
|
|
|
451
500
|
const installed = [];
|
|
452
501
|
|
|
453
502
|
for (const command of metadata.commands) {
|
|
503
|
+
// Handle auto-discovery from subdirectory
|
|
504
|
+
if (command.subdirectory && command.discovery === 'auto') {
|
|
505
|
+
const commandsSourceDir = path.join(pluginPath, command.subdirectory);
|
|
506
|
+
if (fs.existsSync(commandsSourceDir)) {
|
|
507
|
+
const files = fs.readdirSync(commandsSourceDir).filter(f => f.endsWith('.md'));
|
|
508
|
+
for (const file of files) {
|
|
509
|
+
const sourcePath = path.join(commandsSourceDir, file);
|
|
510
|
+
const targetPath = path.join(targetDir, file);
|
|
511
|
+
if (!fs.existsSync(targetPath)) {
|
|
512
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
513
|
+
installed.push({ name: file.replace('.md', ''), file: targetPath });
|
|
514
|
+
this.emit('install:command', { command: file, path: targetPath });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Handle individual command files
|
|
522
|
+
if (!command.file) continue;
|
|
454
523
|
const sourcePath = path.join(pluginPath, command.file);
|
|
455
524
|
const targetPath = path.join(targetDir, path.basename(command.file));
|
|
456
525
|
|
package/package.json
CHANGED