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.
@@ -32,6 +32,7 @@ jobs:
32
32
  - plugin-pm-azure
33
33
  - plugin-pm-github
34
34
  - plugin-testing
35
+ - plugin-obsidian
35
36
  steps:
36
37
  - uses: actions/checkout@v4
37
38
  - uses: actions/setup-node@v6
@@ -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
- // Install plugin agents
270
- console.log(chalk.gray('Installing plugin agents...'));
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
- try {
142
- // Check if scoped directory exists
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
- if (!fs.existsSync(scopePath)) {
146
- this.emit('discover:no-plugins', { scopePath });
147
- return;
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
- const scopedPackages = fs.readdirSync(scopePath);
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
- throw new Error(`Plugin discovery failed: ${error.message}`);
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
- const plugin = this.plugins.get(pluginName);
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
- const plugin = this.plugins.get(pluginName);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "3.25.0",
3
+ "version": "3.25.2",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "workspaces": [