specweave 1.0.271 → 1.0.273

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.
Files changed (138) hide show
  1. package/CLAUDE.md +28 -28
  2. package/bin/specweave.js +13 -0
  3. package/dist/dashboard/assets/index-BKdLA_6x.js +11 -0
  4. package/dist/dashboard/assets/index-Cs3Zq6E2.css +1 -0
  5. package/dist/dashboard/index.html +2 -2
  6. package/dist/src/cli/commands/detect-intent.js +1 -1
  7. package/dist/src/cli/commands/detect-intent.js.map +1 -1
  8. package/dist/src/cli/commands/judge-skill.d.ts.map +1 -1
  9. package/dist/src/cli/commands/judge-skill.js +11 -0
  10. package/dist/src/cli/commands/judge-skill.js.map +1 -1
  11. package/dist/src/cli/commands/migrate-to-umbrella.d.ts.map +1 -1
  12. package/dist/src/cli/commands/migrate-to-umbrella.js +97 -2
  13. package/dist/src/cli/commands/migrate-to-umbrella.js.map +1 -1
  14. package/dist/src/cli/commands/migrate-to-vskill.d.ts +43 -0
  15. package/dist/src/cli/commands/migrate-to-vskill.d.ts.map +1 -0
  16. package/dist/src/cli/commands/migrate-to-vskill.js +144 -0
  17. package/dist/src/cli/commands/migrate-to-vskill.js.map +1 -0
  18. package/dist/src/cli/commands/refresh-marketplace.d.ts +4 -23
  19. package/dist/src/cli/commands/refresh-marketplace.d.ts.map +1 -1
  20. package/dist/src/cli/commands/refresh-marketplace.js +15 -1061
  21. package/dist/src/cli/commands/refresh-marketplace.js.map +1 -1
  22. package/dist/src/cli/commands/refresh-plugins.d.ts +28 -0
  23. package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -0
  24. package/dist/src/cli/commands/refresh-plugins.js +272 -0
  25. package/dist/src/cli/commands/refresh-plugins.js.map +1 -0
  26. package/dist/src/cli/commands/uninstall.d.ts +11 -0
  27. package/dist/src/cli/commands/uninstall.d.ts.map +1 -0
  28. package/dist/src/cli/commands/uninstall.js +164 -0
  29. package/dist/src/cli/commands/uninstall.js.map +1 -0
  30. package/dist/src/cli/helpers/init/claude-plugin-enabler.d.ts +6 -2
  31. package/dist/src/cli/helpers/init/claude-plugin-enabler.d.ts.map +1 -1
  32. package/dist/src/cli/helpers/init/claude-plugin-enabler.js +6 -2
  33. package/dist/src/cli/helpers/init/claude-plugin-enabler.js.map +1 -1
  34. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts +5 -0
  35. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts.map +1 -1
  36. package/dist/src/cli/helpers/init/instruction-file-merger.js +18 -0
  37. package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -1
  38. package/dist/src/cli/helpers/init/plugin-installer.d.ts +1 -1
  39. package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
  40. package/dist/src/cli/helpers/init/plugin-installer.js +98 -364
  41. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
  42. package/dist/src/cli/workers/marketplace-scanner-worker.d.ts +73 -0
  43. package/dist/src/cli/workers/marketplace-scanner-worker.d.ts.map +1 -0
  44. package/dist/src/cli/workers/marketplace-scanner-worker.js +405 -0
  45. package/dist/src/cli/workers/marketplace-scanner-worker.js.map +1 -0
  46. package/dist/src/config/types.d.ts +2 -2
  47. package/dist/src/core/background/job-launcher.d.ts +24 -0
  48. package/dist/src/core/background/job-launcher.d.ts.map +1 -1
  49. package/dist/src/core/background/job-launcher.js +80 -0
  50. package/dist/src/core/background/job-launcher.js.map +1 -1
  51. package/dist/src/core/background/types.d.ts +20 -2
  52. package/dist/src/core/background/types.d.ts.map +1 -1
  53. package/dist/src/core/fabric/registry-schema.d.ts +117 -0
  54. package/dist/src/core/fabric/registry-schema.d.ts.map +1 -1
  55. package/dist/src/core/fabric/registry-schema.js +37 -1
  56. package/dist/src/core/fabric/registry-schema.js.map +1 -1
  57. package/dist/src/core/fabric/security-judge.d.ts.map +1 -1
  58. package/dist/src/core/fabric/security-judge.js +38 -17
  59. package/dist/src/core/fabric/security-judge.js.map +1 -1
  60. package/dist/src/core/fabric/submission-queue-types.d.ts +83 -0
  61. package/dist/src/core/fabric/submission-queue-types.d.ts.map +1 -0
  62. package/dist/src/core/fabric/submission-queue-types.js +8 -0
  63. package/dist/src/core/fabric/submission-queue-types.js.map +1 -0
  64. package/dist/src/core/fabric/submission-queue.d.ts +65 -0
  65. package/dist/src/core/fabric/submission-queue.d.ts.map +1 -0
  66. package/dist/src/core/fabric/submission-queue.js +267 -0
  67. package/dist/src/core/fabric/submission-queue.js.map +1 -0
  68. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +5 -3
  69. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  70. package/dist/src/core/lazy-loading/llm-plugin-detector.js +66 -13
  71. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  72. package/dist/src/core/migration/umbrella-migrator.d.ts +9 -0
  73. package/dist/src/core/migration/umbrella-migrator.d.ts.map +1 -1
  74. package/dist/src/core/migration/umbrella-migrator.js +38 -4
  75. package/dist/src/core/migration/umbrella-migrator.js.map +1 -1
  76. package/dist/src/core/session/plugin-install-detector.d.ts.map +1 -1
  77. package/dist/src/core/session/plugin-install-detector.js +3 -2
  78. package/dist/src/core/session/plugin-install-detector.js.map +1 -1
  79. package/dist/src/dashboard/server/dashboard-server.d.ts.map +1 -1
  80. package/dist/src/dashboard/server/dashboard-server.js +115 -3
  81. package/dist/src/dashboard/server/dashboard-server.js.map +1 -1
  82. package/dist/src/dashboard/server/data/cost-aggregator.d.ts +0 -1
  83. package/dist/src/dashboard/server/data/cost-aggregator.d.ts.map +1 -1
  84. package/dist/src/dashboard/server/data/cost-aggregator.js +0 -1
  85. package/dist/src/dashboard/server/data/cost-aggregator.js.map +1 -1
  86. package/dist/src/dashboard/server/data/marketplace-aggregator.d.ts +27 -0
  87. package/dist/src/dashboard/server/data/marketplace-aggregator.d.ts.map +1 -0
  88. package/dist/src/dashboard/server/data/marketplace-aggregator.js +61 -0
  89. package/dist/src/dashboard/server/data/marketplace-aggregator.js.map +1 -0
  90. package/dist/src/dashboard/server/file-watcher.d.ts.map +1 -1
  91. package/dist/src/dashboard/server/file-watcher.js +1 -0
  92. package/dist/src/dashboard/server/file-watcher.js.map +1 -1
  93. package/dist/src/dashboard/types.d.ts +1 -1
  94. package/dist/src/dashboard/types.d.ts.map +1 -1
  95. package/dist/src/init/research/types.d.ts +1 -1
  96. package/dist/src/utils/cleanup-stale-plugins.d.ts +1 -1
  97. package/dist/src/utils/cleanup-stale-plugins.js +1 -1
  98. package/dist/src/utils/docs-preview/docusaurus-setup.d.ts.map +1 -1
  99. package/dist/src/utils/docs-preview/docusaurus-setup.js +9 -6
  100. package/dist/src/utils/docs-preview/docusaurus-setup.js.map +1 -1
  101. package/dist/src/utils/vskill-resolver.d.ts +24 -0
  102. package/dist/src/utils/vskill-resolver.d.ts.map +1 -0
  103. package/dist/src/utils/vskill-resolver.js +98 -0
  104. package/dist/src/utils/vskill-resolver.js.map +1 -0
  105. package/package.json +3 -1
  106. package/plugins/specweave/PLUGIN.md +1 -1
  107. package/plugins/specweave/hooks/user-prompt-submit.sh +147 -55
  108. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +1 -1
  109. package/plugins/specweave/skills/npm/SKILL.md +6 -6
  110. package/plugins/specweave/skills/update-instructions/SKILL.md +1 -1
  111. package/plugins/specweave-ado/PLUGIN.md +1 -1
  112. package/plugins/specweave-backend/PLUGIN.md +1 -1
  113. package/plugins/specweave-blockchain/PLUGIN.md +1 -1
  114. package/plugins/specweave-confluent/PLUGIN.md +1 -1
  115. package/plugins/specweave-cost-optimizer/PLUGIN.md +1 -1
  116. package/plugins/specweave-desktop/PLUGIN.md +1 -1
  117. package/plugins/specweave-diagrams/PLUGIN.md +1 -1
  118. package/plugins/specweave-docs/PLUGIN.md +1 -1
  119. package/plugins/specweave-figma/PLUGIN.md +1 -1
  120. package/plugins/specweave-frontend/PLUGIN.md +1 -1
  121. package/plugins/specweave-github/PLUGIN.md +1 -1
  122. package/plugins/specweave-infrastructure/PLUGIN.md +1 -1
  123. package/plugins/specweave-jira/PLUGIN.md +1 -1
  124. package/plugins/specweave-kafka/PLUGIN.md +1 -1
  125. package/plugins/specweave-kafka-streams/PLUGIN.md +1 -1
  126. package/plugins/specweave-kubernetes/PLUGIN.md +1 -1
  127. package/plugins/specweave-ml/PLUGIN.md +1 -1
  128. package/plugins/specweave-mobile/PLUGIN.md +1 -1
  129. package/plugins/specweave-mobile/README.md +2 -2
  130. package/plugins/specweave-n8n/PLUGIN.md +1 -1
  131. package/plugins/specweave-payments/PLUGIN.md +1 -1
  132. package/plugins/specweave-release/PLUGIN.md +1 -1
  133. package/plugins/specweave-release/commands/npm.md +6 -6
  134. package/plugins/specweave-testing/PLUGIN.md +1 -1
  135. package/scripts/preuninstall.cjs +34 -0
  136. package/src/templates/CLAUDE.md.template +3 -3
  137. package/dist/dashboard/assets/index-DNnisM2Y.css +0 -1
  138. package/dist/dashboard/assets/index-ldLuSpfZ.js +0 -11
@@ -1,29 +1,24 @@
1
1
  /**
2
2
  * Plugin installation for Claude Code
3
- * Handles marketplace registration and plugin installation
3
+ * Uses vskill for plugin installation with security scanning
4
4
  */
5
5
  import * as fs from '../../../utils/fs-native.js';
6
- import * as path from 'path';
7
- import * as os from 'os';
8
6
  import chalk from 'chalk';
9
7
  import ora from 'ora';
10
8
  import { execFileNoThrowSync } from '../../../utils/execFileNoThrow.js';
11
9
  import { detectClaudeCli, getClaudeCliDiagnostic, getClaudeCliSuggestions } from '../../../utils/claude-cli-detector.js';
12
10
  import { findSourceDir } from './path-utils.js';
13
11
  import { cleanupStalePlugins } from '../../../utils/cleanup-stale-plugins.js';
14
- import { getPluginScope, getScopeArgs } from '../../../core/types/plugin-scope.js';
15
12
  import { enablePluginsInSettings } from './claude-plugin-enabler.js';
16
- /**
17
- * SpecWeave marketplace GitHub repository
18
- * Used for both CLI-based and fallback registration
19
- */
20
- const SPECWEAVE_MARKETPLACE_REPO = 'anton-abyzov/specweave';
21
- const SPECWEAVE_MARKETPLACE_URL = `https://github.com/${SPECWEAVE_MARKETPLACE_REPO}`;
22
- /**
23
- * Anthropic's official plugins marketplace
24
- * Optional — users can install plugins manually if needed
25
- */
26
- const OFFICIAL_MARKETPLACE_URL = 'https://github.com/anthropics/claude-plugins-official';
13
+ import { resolveVskillPath as _resolveVskillPath, resolveSpecweaveDir } from '../../../utils/vskill-resolver.js';
14
+ /** Resolve vskill path from this module's location */
15
+ function resolveVskillPath() {
16
+ return _resolveVskillPath(__dirname);
17
+ }
18
+ /** Resolve specweave source directory */
19
+ function resolveSpecweavePluginDir() {
20
+ return resolveSpecweaveDir(__dirname);
21
+ }
27
22
  /**
28
23
  * Install SpecWeave plugins via Claude CLI
29
24
  *
@@ -99,14 +94,8 @@ export async function installAllPlugins(options) {
99
94
  }
100
95
  return { success: false, successCount: 0, failCount: 0, failedPlugins: [] };
101
96
  }
102
- // Claude CLI available - proceed with installation
97
+ // Claude CLI available - proceed with vskill-based installation
103
98
  try {
104
- // CRITICAL FIX (v0.35.2): Simplified marketplace registration
105
- // Just ensure marketplace is registered - Claude CLI handles caching internally
106
- // No need for manual cache management - that was over-engineering!
107
- await refreshMarketplace(spinner);
108
- // Register official marketplace (optional — for user-installed plugins)
109
- await ensureOfficialMarketplace(spinner);
110
99
  // Load marketplace.json to get ALL available plugins
111
100
  spinner.start('Loading available plugins...');
112
101
  const marketplaceJsonPath = findSourceDir('.claude-plugin/marketplace.json', dirname);
@@ -118,15 +107,13 @@ export async function installAllPlugins(options) {
118
107
  if (allPlugins.length === 0) {
119
108
  throw new Error('No plugins found in marketplace.json');
120
109
  }
121
- console.log(chalk.blue(` 📦 Found ${allPlugins.length} plugins to install`));
110
+ console.log(chalk.blue(` Found ${allPlugins.length} plugins available`));
122
111
  spinner.succeed(`Found ${allPlugins.length} plugins`);
123
- // CRITICAL FIX (v0.35.2): Clean up stale plugin references
124
- // Remove plugins from ~/.claude/settings.json that no longer exist in marketplace
125
- // This prevents "Plugin not found" errors for removed/renamed plugins
112
+ // Clean up stale plugin references
126
113
  spinner.start('Checking for stale plugin references...');
127
114
  const cleanupResult = await cleanupStalePlugins(marketplaceJsonPath, false);
128
115
  if (cleanupResult.removedCount > 0) {
129
- console.log(chalk.yellow(` 🧹 Removed ${cleanupResult.removedCount} stale plugin reference(s)`));
116
+ console.log(chalk.yellow(` Removed ${cleanupResult.removedCount} stale plugin reference(s)`));
130
117
  cleanupResult.removedPlugins.forEach(p => {
131
118
  console.log(chalk.gray(` - ${p}`));
132
119
  });
@@ -136,44 +123,43 @@ export async function installAllPlugins(options) {
136
123
  spinner.succeed('No stale plugins found');
137
124
  }
138
125
  // LAZY LOADING MODE (default)
139
- // Install only router skill, cache the rest for on-demand loading
126
+ // Install only core plugin via vskill, cache the rest for on-demand loading
140
127
  if (lazyMode) {
141
128
  return await installLazyMode(allPlugins, spinner, dirname);
142
129
  }
143
130
  // FULL MODE (--full flag)
144
- // Install ALL plugins with retry logic
131
+ // Install ALL plugins via vskill with security scanning
145
132
  const result = await installPluginsWithRetry(allPlugins, spinner);
146
133
  // Enable installed plugins in Claude settings
147
- // CRITICAL FIX: Plugins must be enabled in ~/.claude/settings.json to be active
148
134
  if (result.installedPlugins.length > 0) {
149
135
  spinner.start('Enabling plugins in Claude Code...');
150
136
  const enabled = enablePluginsInSettings(result.installedPlugins, 'specweave');
151
137
  if (enabled) {
152
138
  spinner.succeed('Plugins enabled in Claude Code');
153
- console.log(chalk.green(' All installed plugins are now active'));
139
+ console.log(chalk.green(' All installed plugins are now active'));
154
140
  }
155
141
  else {
156
142
  spinner.warn('Could not auto-enable plugins');
157
- console.log(chalk.yellow(' Manual: Enable plugins in /plugin menu'));
143
+ console.log(chalk.yellow(' Manual: Enable plugins in /plugin menu'));
158
144
  }
159
145
  }
160
146
  // Report results
161
147
  console.log('');
162
- console.log(chalk.green.bold('Plugin Installation Complete'));
148
+ console.log(chalk.green.bold('Plugin Installation Complete'));
163
149
  console.log(chalk.white(` Installed: ${result.successCount}/${allPlugins.length} plugins`));
164
150
  if (result.failCount > 0) {
165
151
  console.log(chalk.yellow(` Failed: ${result.failCount} plugins`));
166
152
  console.log(chalk.gray(` Failed plugins: ${result.failedPlugins.join(', ')}`));
167
- console.log(chalk.gray(' You can install these manually later'));
153
+ console.log(chalk.gray(' You can install these manually later'));
168
154
  }
169
155
  console.log('');
170
- console.log(chalk.cyan('📋 Available capabilities:'));
171
- console.log(chalk.gray(' /sw:increment - Plan new features'));
172
- console.log(chalk.gray(' /sw:do - Execute tasks'));
173
- console.log(chalk.gray(' /specweave-github:sync - GitHub integration'));
174
- console.log(chalk.gray(' /specweave-jira:sync - JIRA integration'));
175
- console.log(chalk.gray(' /sw:docs preview - Documentation preview'));
176
- console.log(chalk.gray(' ...and more!'));
156
+ console.log(chalk.cyan('Available capabilities:'));
157
+ console.log(chalk.gray(' /sw:increment - Plan new features'));
158
+ console.log(chalk.gray(' /sw:do - Execute tasks'));
159
+ console.log(chalk.gray(' /specweave-github:sync - GitHub integration'));
160
+ console.log(chalk.gray(' /specweave-jira:sync - JIRA integration'));
161
+ console.log(chalk.gray(' /sw:docs preview - Documentation preview'));
162
+ console.log(chalk.gray(' ...and more!'));
177
163
  return {
178
164
  success: result.successCount > 0,
179
165
  successCount: result.successCount,
@@ -188,284 +174,32 @@ export async function installAllPlugins(options) {
188
174
  console.log('');
189
175
  const errorMessage = error instanceof Error ? error.message : String(error);
190
176
  if (errorMessage.includes('not found') || errorMessage.includes('ENOENT')) {
191
- console.log(chalk.yellow(' Reason: Claude CLI found but command failed'));
192
- console.log(chalk.gray(' Try manually: /plugin install specweave'));
177
+ console.log(chalk.yellow(' Reason: Plugin source not found'));
178
+ console.log(chalk.gray(' Try: vskill add --plugin sw --plugin-dir <specweave-dir>'));
193
179
  }
194
180
  else if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
195
181
  console.log(chalk.yellow(' Reason: Permission denied'));
196
- console.log(chalk.gray(' Check file permissions or run with appropriate access'));
182
+ console.log(chalk.gray(' Check file permissions or run with appropriate access'));
197
183
  }
198
184
  else if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('network')) {
199
185
  console.log(chalk.yellow(' Reason: Network error'));
200
- console.log(chalk.gray(' Check internet connection and try again'));
186
+ console.log(chalk.gray(' Check internet connection and try again'));
201
187
  }
202
188
  else if (process.env.DEBUG) {
203
189
  console.log(chalk.gray(` Error: ${errorMessage}`));
204
190
  }
205
191
  console.log('');
206
- console.log(chalk.cyan('📦 Manual installation:'));
207
- console.log(chalk.white(' /plugin install sw@specweave'));
208
- console.log(chalk.white(' /plugin install sw-github@specweave'));
209
- console.log(chalk.white(' ...etc.'));
192
+ console.log(chalk.cyan('Manual installation:'));
193
+ console.log(chalk.white(' vskill add --plugin sw --plugin-dir <specweave-dir>'));
210
194
  console.log('');
211
195
  return { success: false, successCount: 0, failCount: 0, failedPlugins: [] };
212
196
  }
213
197
  }
214
198
  /**
215
- * Ensure SpecWeave marketplace is registered and updated
216
- *
217
- * Uses the proper Claude CLI commands:
218
- * 1. First checks if marketplace exists
219
- * 2. If not → adds it with `marketplace add` (HTTPS URL)
220
- * 3. If yes → tries `marketplace update`
221
- * 4. If update fails with SSH error → removes and re-adds with HTTPS
222
- *
223
- * This is the recommended approach per Claude Code documentation.
224
- *
225
- * CRITICAL FIX (v0.35.2): Simplified marketplace registration
226
- * Just ensure marketplace is registered - Claude CLI handles caching internally.
227
- * No need for manual cache management - that was over-engineering!
228
- *
229
- * CRITICAL FIX (v1.0.24): Handle Windows SSH authentication failures!
230
- * Previously registered marketplaces may use SSH URLs that fail on Windows.
231
- * When update fails with "SSH authentication failed", we remove the old
232
- * registration and re-add with HTTPS URL which works for everyone.
233
- */
234
- async function refreshMarketplace(spinner) {
235
- spinner.start('Checking SpecWeave marketplace...');
236
- // Check if marketplace is already registered
237
- const listResult = execFileNoThrowSync('claude', ['plugin', 'marketplace', 'list']);
238
- const marketplaceExists = listResult.success &&
239
- (listResult.stdout || '').toLowerCase().includes('specweave');
240
- if (marketplaceExists) {
241
- // Marketplace exists - try UPDATE command first
242
- spinner.text = 'Updating SpecWeave marketplace...';
243
- const updateResult = execFileNoThrowSync('claude', [
244
- 'plugin',
245
- 'marketplace',
246
- 'update',
247
- 'specweave'
248
- ]);
249
- if (!updateResult.success) {
250
- // Convert error to string for pattern matching
251
- const errorStr = updateResult.error instanceof Error
252
- ? updateResult.error.message
253
- : String(updateResult.error || '');
254
- const errorMsg = (updateResult.stderr || errorStr).toLowerCase();
255
- // CRITICAL FIX (v1.0.24): Handle SSH authentication failures on Windows
256
- // If the marketplace was previously registered with SSH URL (or owner/repo format
257
- // which Claude CLI converts to SSH), the update will fail with SSH auth error.
258
- // Solution: Remove the marketplace and re-add with HTTPS URL.
259
- if (errorMsg.includes('ssh') ||
260
- errorMsg.includes('permission denied') ||
261
- errorMsg.includes('publickey') ||
262
- errorMsg.includes('authentication failed')) {
263
- spinner.text = 'Re-registering marketplace with HTTPS...';
264
- console.log(chalk.yellow('\n ⚠️ SSH authentication failed - switching to HTTPS'));
265
- // Remove the old SSH-based registration
266
- const removeResult = execFileNoThrowSync('claude', [
267
- 'plugin',
268
- 'marketplace',
269
- 'remove',
270
- 'specweave'
271
- ]);
272
- if (!removeResult.success) {
273
- // If remove fails, try to add anyway (might work if it's just stale)
274
- console.log(chalk.gray(' → Could not remove old registration, attempting fresh add...'));
275
- }
276
- // Re-add with HTTPS URL
277
- const addResult = execFileNoThrowSync('claude', [
278
- 'plugin',
279
- 'marketplace',
280
- 'add',
281
- SPECWEAVE_MARKETPLACE_URL
282
- ]);
283
- if (!addResult.success) {
284
- throw new Error(`Failed to re-add marketplace with HTTPS: ${addResult.stderr || addResult.error}`);
285
- }
286
- console.log(chalk.green(' ✔ Marketplace re-registered with HTTPS (works on all platforms)'));
287
- // Enable auto-update for seamless future updates
288
- enableMarketplaceAutoUpdate(spinner);
289
- spinner.succeed('SpecWeave marketplace ready');
290
- return;
291
- }
292
- // Other error - throw as before (use original stderr for clear message)
293
- throw new Error(`Failed to update marketplace: ${updateResult.stderr || errorStr}`);
294
- }
295
- console.log(chalk.green(' ✔ Marketplace updated (latest from GitHub)'));
296
- // Enable auto-update for seamless future updates
297
- enableMarketplaceAutoUpdate(spinner);
298
- spinner.succeed('SpecWeave marketplace ready');
299
- }
300
- else {
301
- // Marketplace not registered - use ADD command with HTTPS URL
302
- // CRITICAL (v0.35.3): Use full HTTPS URL, NOT owner/repo format!
303
- // Claude CLI converts owner/repo to SSH URL (git@github.com:owner/repo.git)
304
- // which fails for users without SSH keys configured.
305
- // Using full HTTPS URL works for ALL users (public repo).
306
- spinner.text = 'Adding SpecWeave marketplace...';
307
- const addResult = execFileNoThrowSync('claude', [
308
- 'plugin',
309
- 'marketplace',
310
- 'add',
311
- SPECWEAVE_MARKETPLACE_URL
312
- ]);
313
- if (!addResult.success) {
314
- throw new Error(`Failed to add marketplace: ${addResult.stderr || addResult.error}`);
315
- }
316
- console.log(chalk.green(' ✔ Marketplace added from GitHub'));
317
- // Enable auto-update for seamless future updates
318
- enableMarketplaceAutoUpdate(spinner);
319
- spinner.succeed('SpecWeave marketplace ready');
320
- }
321
- }
322
- /**
323
- * Ensure Anthropic's official plugins marketplace is registered
324
- *
325
- * Optional — makes official plugins available for manual installation.
326
- *
327
- * v1.0.223: Added to fix missing marketplace during init
328
- * v1.0.240 (0198): context7/playwright no longer auto-installed
329
- */
330
- async function ensureOfficialMarketplace(spinner) {
331
- spinner.start('Checking official plugins marketplace...');
332
- // Check if marketplace is already registered
333
- const listResult = execFileNoThrowSync('claude', ['plugin', 'marketplace', 'list']);
334
- const marketplaceExists = listResult.success &&
335
- (listResult.stdout || '').toLowerCase().includes('claude-plugins-official');
336
- if (marketplaceExists) {
337
- spinner.succeed('Official plugins marketplace ready');
338
- return;
339
- }
340
- // Marketplace not registered - add it
341
- spinner.text = 'Adding official plugins marketplace...';
342
- const addResult = execFileNoThrowSync('claude', [
343
- 'plugin',
344
- 'marketplace',
345
- 'add',
346
- OFFICIAL_MARKETPLACE_URL
347
- ]);
348
- if (!addResult.success) {
349
- // Non-fatal - warn but continue
350
- spinner.warn('Could not add official marketplace');
351
- console.log(chalk.gray(' → Manual: claude plugin marketplace add https://github.com/anthropics/claude-plugins-official'));
352
- return;
353
- }
354
- console.log(chalk.green(' ✔ Official plugins marketplace added'));
355
- spinner.succeed('Official plugins marketplace ready');
356
- }
357
- /**
358
- * Enable auto-update for the SpecWeave marketplace
359
- *
360
- * Modifies ~/.claude/plugins/known_marketplaces.json to set autoUpdate: true
361
- * for the specweave marketplace. This ensures plugins stay up-to-date automatically
362
- * when Claude Code starts.
363
- *
364
- * FEATURE: Added in Claude Code v2.0.70 - per-marketplace auto-update control
365
- * By default, third-party marketplaces have auto-update disabled.
366
- * We enable it during init for better UX.
367
- */
368
- function enableMarketplaceAutoUpdate(spinner) {
369
- const knownMarketplacesPath = path.join(os.homedir(), '.claude/plugins/known_marketplaces.json');
370
- try {
371
- // Check if file exists
372
- if (!fs.existsSync(knownMarketplacesPath)) {
373
- // File doesn't exist yet - Claude CLI will create it
374
- // Auto-update will be set on next init
375
- return;
376
- }
377
- // Read current config
378
- const content = fs.readFileSync(knownMarketplacesPath, 'utf-8');
379
- const config = JSON.parse(content);
380
- // Check if specweave marketplace exists and needs auto-update enabled
381
- if (config.specweave && config.specweave.autoUpdate !== true) {
382
- config.specweave.autoUpdate = true;
383
- // Write updated config
384
- fs.writeFileSync(knownMarketplacesPath, JSON.stringify(config, null, 2));
385
- console.log(chalk.green(' ✔ Auto-update enabled for SpecWeave marketplace'));
386
- }
387
- else if (config.specweave?.autoUpdate === true) {
388
- // Already enabled, no action needed
389
- // Don't log anything to avoid noise
390
- }
391
- }
392
- catch (error) {
393
- // Non-critical - don't fail init if this doesn't work
394
- // User can enable manually via /plugin menu
395
- if (process.env.DEBUG) {
396
- console.log(chalk.gray(` → Could not enable auto-update: ${error instanceof Error ? error.message : String(error)}`));
397
- }
398
- }
399
- }
400
- /**
401
- * Work around Claude CLI bug where marketplace name = plugin name causes EINVAL.
402
- *
403
- * When the marketplace name matches a plugin name ("specweave"), Claude CLI:
404
- * 1. Creates cache/specweave/ with full marketplace content
405
- * 2. Tries to rename cache/specweave → cache/specweave/specweave/0.25.0
406
- * 3. Fails with EINVAL (can't move directory into itself)
407
- *
408
- * WORKAROUND: Manually install the "specweave" plugin by copying from marketplace.
409
- * Returns true if manual install succeeded, false if should use claude plugin install.
410
- */
411
- function manuallyInstallSpecweavePlugin(version) {
412
- const marketplacePath = path.join(os.homedir(), '.claude/plugins/marketplaces/specweave/plugins/specweave');
413
- const targetPath = path.join(os.homedir(), '.claude/plugins/cache/specweave/specweave', version);
414
- // Check if marketplace plugin exists
415
- if (!fs.existsSync(marketplacePath)) {
416
- return false;
417
- }
418
- // Check if already installed
419
- if (fs.existsSync(targetPath)) {
420
- return true;
421
- }
422
- try {
423
- // Create target directory structure
424
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
425
- // Copy plugin files from marketplace to cache
426
- copyDirRecursive(marketplacePath, targetPath);
427
- return true;
428
- }
429
- catch {
430
- return false;
431
- }
432
- }
433
- /**
434
- * Recursively copy directory contents
435
- */
436
- function copyDirRecursive(src, dest) {
437
- fs.mkdirSync(dest, { recursive: true });
438
- const entries = fs.readdirSync(src, { withFileTypes: true });
439
- for (const entry of entries) {
440
- const srcPath = path.join(src, entry.name);
441
- const destPath = path.join(dest, entry.name);
442
- if (entry.isDirectory()) {
443
- copyDirRecursive(srcPath, destPath);
444
- }
445
- else {
446
- fs.copyFileSync(srcPath, destPath);
447
- }
448
- }
449
- }
450
- /**
451
- * Get the version of a plugin from marketplace.json
452
- */
453
- function getPluginVersion(pluginName) {
454
- try {
455
- const marketplacePath = path.join(os.homedir(), '.claude/plugins/marketplaces/specweave/.claude-plugin/marketplace.json');
456
- const marketplace = JSON.parse(fs.readFileSync(marketplacePath, 'utf-8'));
457
- const plugin = marketplace.plugins?.find((p) => p.name === pluginName);
458
- return plugin?.version || '0.25.0';
459
- }
460
- catch {
461
- return '0.25.0';
462
- }
463
- }
464
- /**
465
- * Install in lazy mode - core plugin only, load others on-demand
199
+ * Install in lazy mode - core plugin only via vskill, load others on-demand
466
200
  *
467
201
  * Installs:
468
- * - sw@specweave (core SpecWeave framework)
202
+ * - sw (core SpecWeave framework) via vskill add
469
203
  *
470
204
  * Other SpecWeave plugins are loaded on-demand via detect-intent hook.
471
205
  * Official plugins (context7, playwright) are optional user installs.
@@ -478,58 +212,61 @@ async function installLazyMode(_allPlugins, spinner, _dirname) {
478
212
  let installedCount = 0;
479
213
  const failedPlugins = [];
480
214
  const successfullyInstalled = [];
481
- spinner.start('Installing essential plugins...');
215
+ spinner.start('Installing essential plugins via vskill...');
482
216
  spinner.stop();
217
+ const vskillPath = resolveVskillPath();
218
+ const pluginDir = resolveSpecweavePluginDir();
483
219
  for (const plugin of essentialPlugins) {
484
- const pluginKey = `${plugin.name}@${plugin.marketplace}`;
485
- const scopeArgs = getScopeArgs(getPluginScope(plugin.name, plugin.marketplace));
486
- console.log(chalk.blue(` Installing ${pluginKey}...`));
487
- const result = execFileNoThrowSync('claude', ['plugin', 'install', pluginKey, ...scopeArgs]);
220
+ console.log(chalk.blue(` Installing ${plugin.name} via vskill...`));
221
+ const result = installPluginViaVskill(vskillPath, pluginDir, plugin.name);
488
222
  if (result.success) {
489
- console.log(chalk.green(` ✓ ${pluginKey} installed`));
223
+ // Display scan result to user
224
+ if (result.scanOutput) {
225
+ console.log(chalk.gray(` Security scan: ${result.scanOutput}`));
226
+ }
227
+ console.log(chalk.green(` ${plugin.name} installed via vskill`));
490
228
  installedCount++;
491
229
  successfullyInstalled.push(plugin.name);
492
230
  }
493
- else if (result.stderr?.includes('already') || result.stdout?.includes('already')) {
494
- console.log(chalk.gray(` ${pluginKey} (already installed)`));
231
+ else if (result.alreadyInstalled) {
232
+ console.log(chalk.gray(` ${plugin.name} (already installed)`));
495
233
  installedCount++;
496
234
  successfullyInstalled.push(plugin.name);
497
235
  }
498
236
  else {
499
- console.log(chalk.yellow(` ${pluginKey} failed`));
500
- console.log(chalk.gray(` Install manually: claude plugin install ${pluginKey}`));
237
+ console.log(chalk.yellow(` ${plugin.name} failed`));
238
+ console.log(chalk.gray(` Install manually: vskill add --plugin ${plugin.name} --plugin-dir ${pluginDir}`));
501
239
  failedPlugins.push(plugin.name);
502
240
  }
503
241
  }
504
242
  spinner.succeed(`Installed ${installedCount}/${essentialPlugins.length} essential plugins`);
505
243
  // Enable installed plugins in Claude settings
506
- // CRITICAL FIX: Plugins must be enabled in ~/.claude/settings.json to be active
507
244
  if (successfullyInstalled.length > 0) {
508
245
  spinner.start('Enabling plugins in Claude Code...');
509
246
  const enabled = enablePluginsInSettings(successfullyInstalled, 'specweave');
510
247
  if (enabled) {
511
248
  spinner.succeed('Plugins enabled in Claude Code');
512
- console.log(chalk.green(' All installed plugins are now active'));
249
+ console.log(chalk.green(' All installed plugins are now active'));
513
250
  }
514
251
  else {
515
252
  spinner.warn('Could not auto-enable plugins');
516
- console.log(chalk.yellow(' Manual: Enable plugins in /plugin menu'));
253
+ console.log(chalk.yellow(' Manual: Enable plugins in /plugin menu'));
517
254
  }
518
255
  }
519
256
  // Show summary
520
257
  console.log('');
521
- console.log(chalk.green.bold('Lazy Loading Enabled'));
258
+ console.log(chalk.green.bold('Lazy Loading Enabled'));
522
259
  console.log('');
523
260
  console.log(chalk.cyan('Installed plugins:'));
524
261
  for (const plugin of essentialPlugins) {
525
- console.log(chalk.gray(` ${plugin.name}@${plugin.marketplace} - ${plugin.description}`));
262
+ console.log(chalk.gray(` ${plugin.name}@${plugin.marketplace} - ${plugin.description}`));
526
263
  }
527
264
  console.log('');
528
265
  console.log(chalk.cyan('On-demand plugins (loaded via keywords):'));
529
- console.log(chalk.gray(' "GitHub sync" sw-github'));
530
- console.log(chalk.gray(' "JIRA" sw-jira'));
531
- console.log(chalk.gray(' "Kubernetes" sw-k8s'));
532
- console.log(chalk.gray(' "React/frontend" sw-frontend'));
266
+ console.log(chalk.gray(' "GitHub sync" -> sw-github'));
267
+ console.log(chalk.gray(' "JIRA" -> sw-jira'));
268
+ console.log(chalk.gray(' "Kubernetes" -> sw-k8s'));
269
+ console.log(chalk.gray(' "React/frontend" -> sw-frontend'));
533
270
  console.log('');
534
271
  return {
535
272
  success: installedCount > 0,
@@ -540,59 +277,56 @@ async function installLazyMode(_allPlugins, spinner, _dirname) {
540
277
  };
541
278
  }
542
279
  /**
543
- * Install plugins with retry logic
280
+ * Install a single plugin via vskill add
281
+ *
282
+ * Uses execFileNoThrowSync to invoke the vskill CLI with
283
+ * --plugin and --plugin-dir flags for local plugin directory installation.
284
+ */
285
+ function installPluginViaVskill(vskillPath, pluginDir, pluginName) {
286
+ const result = execFileNoThrowSync('node', [
287
+ vskillPath,
288
+ 'add',
289
+ pluginDir, // source (local path)
290
+ '--plugin', pluginName,
291
+ '--plugin-dir', pluginDir,
292
+ '--force', // Auto-accept scan results during init
293
+ ]);
294
+ if (result.success) {
295
+ // Extract scan output for user display
296
+ const stdout = result.stdout || '';
297
+ const scanMatch = stdout.match(/Score:.*Verdict:\s*\w+/);
298
+ return {
299
+ success: true,
300
+ scanOutput: scanMatch ? scanMatch[0] : undefined,
301
+ };
302
+ }
303
+ // Check if already installed
304
+ const combined = `${result.stdout || ''} ${result.stderr || ''}`.toLowerCase();
305
+ if (combined.includes('already')) {
306
+ return { success: true, alreadyInstalled: true };
307
+ }
308
+ return { success: false };
309
+ }
310
+ /**
311
+ * Install plugins via vskill with retry logic
544
312
  */
545
313
  async function installPluginsWithRetry(plugins, spinner) {
546
314
  let successCount = 0;
547
315
  let failCount = 0;
548
316
  const failedPlugins = [];
549
317
  const installedPlugins = [];
550
- // Sort plugins to install "specweave" FIRST
551
- // This prevents cache corruption from other plugin installs
552
- const sortedPlugins = [...plugins].sort((a, b) => {
553
- if (a.name === 'specweave')
554
- return -1;
555
- if (b.name === 'specweave')
556
- return 1;
557
- return 0;
558
- });
559
- for (const plugin of sortedPlugins) {
318
+ const vskillPath = resolveVskillPath();
319
+ const pluginDir = resolveSpecweavePluginDir();
320
+ for (const plugin of plugins) {
560
321
  const pluginName = plugin.name;
561
- spinner.start(`Installing ${pluginName}...`);
562
- let installed = false;
563
- // Special handling for "specweave" plugin due to Claude CLI bug
564
- // (marketplace name = plugin name causes EINVAL on rename)
565
- if (pluginName === 'specweave') {
566
- const version = getPluginVersion('specweave');
567
- installed = manuallyInstallSpecweavePlugin(version);
568
- if (installed) {
569
- successCount++;
570
- installedPlugins.push(pluginName);
571
- spinner.succeed(`${pluginName} installed (manual workaround)`);
572
- continue;
573
- }
574
- // Fall through to try claude plugin install if manual failed
575
- }
576
- // Determine installation scope (core 'sw' → user, domain plugins → project)
577
- const scopeArgs = getScopeArgs(getPluginScope(pluginName, 'specweave'));
578
- // Retry up to 3 times with exponential backoff
579
- for (let attempt = 1; attempt <= 3; attempt++) {
580
- const installResult = execFileNoThrowSync('claude', ['plugin', 'install', pluginName, ...scopeArgs]);
581
- if (installResult.success) {
582
- installed = true;
583
- break;
584
- }
585
- // If "not found" error and not last attempt, wait and retry
586
- if (installResult.stderr?.includes('not found') && attempt < 3) {
587
- spinner.text = `Installing ${pluginName}... (retry ${attempt}/3)`;
588
- await new Promise(resolve => setTimeout(resolve, 500 * attempt));
589
- continue;
590
- }
591
- break;
592
- }
593
- if (installed) {
322
+ spinner.start(`Installing ${pluginName} via vskill...`);
323
+ const result = installPluginViaVskill(vskillPath, pluginDir, pluginName);
324
+ if (result.success) {
594
325
  successCount++;
595
326
  installedPlugins.push(pluginName);
327
+ if (result.scanOutput) {
328
+ console.log(chalk.gray(` Security scan: ${result.scanOutput}`));
329
+ }
596
330
  spinner.succeed(`${pluginName} installed`);
597
331
  }
598
332
  else {