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,1071 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Refresh SpecWeave Marketplace
3
+ * Refresh SpecWeave Marketplace (DEPRECATED)
4
4
  *
5
- * Automates the complete marketplace refresh process with LAZY LOADING support:
6
- * 1. Updates or adds marketplace from GitHub
7
- * 2. Installs core plugin (sw) only (default) OR all plugins (--all)
8
- * 3. Populates lazy loading cache for on-demand plugin loading
9
- * 4. Updates instruction files (CLAUDE.md, AGENTS.md)
10
- *
11
- * LAZY LOADING (default):
12
- * - Installs only core `sw` plugin (~3K tokens)
13
- * - Other plugins loaded on-demand via keyword detection
14
- * - Result: ~5K tokens at startup instead of ~60K (90% savings!)
15
- *
16
- * MINIMAL MODE (--minimal):
17
- * - Removes specweave marketplace entirely
18
- * - Installs only core plugin (sw)
19
- * - Clean /plugin output (only shows installed plugins)
20
- *
21
- * Usage:
22
- * specweave refresh-marketplace # Lazy mode (default) - core only
23
- * specweave refresh-marketplace --all # Legacy mode - install all plugins
24
- * specweave refresh-marketplace --minimal # Minimal mode - clean /plugin output
25
- * specweave refresh-marketplace --force # Force reinstall (clear cache)
5
+ * This command is deprecated in favor of `specweave refresh-plugins`.
6
+ * It now delegates to refresh-plugins with a deprecation warning.
26
7
  *
27
8
  * @since 1.0.60
28
- * @updated 1.0.208 - Simplified to GitHub-only source
9
+ * @deprecated 1.0.272 - Use `specweave refresh-plugins` instead
29
10
  */
30
11
  import chalk from 'chalk';
31
- import * as fs from 'fs';
32
- import * as path from 'path';
33
- import os from 'os';
34
- // Plugin cache modules removed (v1.0.209) - Claude Code manages its own plugin cache
35
- import { consoleLogger as logger } from '../../utils/logger.js';
36
- import { execFileNoThrowSync } from '../../utils/execFileNoThrow.js';
37
- import { migrateUserLevelPlugins } from '../../utils/cleanup-stale-plugins.js';
38
- // Configuration
39
- const MARKETPLACE_NAME = 'specweave';
40
- // CRITICAL: Use FULL HTTPS URL, not short owner/repo format!
41
- // Claude CLI converts owner/repo to SSH URL (git@github.com:owner/repo.git)
42
- // which FAILS for users without SSH keys configured.
43
- // Using full HTTPS URL works for ALL users (public repo).
44
- const GITHUB_REPO_URL = 'https://github.com/anton-abyzov/specweave';
45
- // Short form kept for display purposes only
46
- const GITHUB_REPO_SHORT = 'anton-abyzov/specweave';
47
- /**
48
- * Safely execute a CLI command using execFileNoThrow (no shell injection risk)
49
- *
50
- * @param command - The command executable (e.g., 'claude', 'git', 'npx')
51
- * @param args - Array of arguments (safely escaped automatically)
52
- * @param _silent - Unused, kept for API compatibility
53
- * @returns Result with success status and output
54
- */
55
- function runCommand(command, args, _silent = false) {
56
- const result = execFileNoThrowSync(command, args);
57
- const output = (result.stdout + result.stderr).trim();
58
- return { success: result.success, output };
59
- }
60
- /**
61
- * Run git command safely
62
- */
63
- function runGitCommand(args) {
64
- return runCommand('git', args);
65
- }
66
- function checkMarketplaceExists() {
67
- const result = runCommand('claude', ['plugin', 'marketplace', 'list'], true);
68
- return result.success && result.output.includes(MARKETPLACE_NAME);
69
- }
70
- function getMarketplaceInstallPath() {
71
- const knownMarketplacesPath = path.join(os.homedir(), '.claude/plugins/known_marketplaces.json');
72
- if (!fs.existsSync(knownMarketplacesPath)) {
73
- return null;
74
- }
75
- try {
76
- const content = fs.readFileSync(knownMarketplacesPath, 'utf8');
77
- const data = JSON.parse(content);
78
- return data[MARKETPLACE_NAME]?.installLocation || null;
79
- }
80
- catch {
81
- return null;
82
- }
83
- }
84
- function getPluginsFromMarketplace(marketplacePath) {
85
- const marketplaceJsonPath = path.join(marketplacePath, '.claude-plugin/marketplace.json');
86
- if (!fs.existsSync(marketplaceJsonPath)) {
87
- throw new Error(`Marketplace JSON not found at ${marketplaceJsonPath}`);
88
- }
89
- try {
90
- const content = fs.readFileSync(marketplaceJsonPath, 'utf8');
91
- const data = JSON.parse(content);
92
- return data.plugins?.map((p) => p.name) || [];
93
- }
94
- catch (error) {
95
- throw new Error(`Failed to parse marketplace.json: ${error}`);
96
- }
97
- }
98
- /**
99
- * Get all installed SpecWeave plugins from the registry
100
- * Reads ~/.claude/plugins/installed_plugins.json and filters to specweave marketplace
101
- */
102
- function getInstalledSpecweavePlugins() {
103
- const installedPluginsPath = path.join(os.homedir(), '.claude/plugins/installed_plugins.json');
104
- if (!fs.existsSync(installedPluginsPath)) {
105
- return [];
106
- }
107
- try {
108
- const content = fs.readFileSync(installedPluginsPath, 'utf8');
109
- const data = JSON.parse(content);
110
- // Filter to only specweave marketplace plugins (format: "plugin-name@specweave")
111
- const specweavePlugins = [];
112
- for (const key of Object.keys(data.plugins || {})) {
113
- if (key.endsWith('@specweave')) {
114
- // Extract plugin name from "plugin-name@specweave"
115
- const pluginName = key.replace('@specweave', '');
116
- specweavePlugins.push(pluginName);
117
- }
118
- }
119
- return specweavePlugins;
120
- }
121
- catch {
122
- return [];
123
- }
124
- }
125
- /**
126
- * Uninstall a plugin via Claude CLI
127
- */
128
- function uninstallPlugin(pluginName) {
129
- // Security: Validate plugin name
130
- if (!isValidPluginName(pluginName)) {
131
- return { success: false, output: 'Invalid plugin name format' };
132
- }
133
- return runCommand('claude', ['plugin', 'uninstall', pluginName], true);
134
- }
135
- /**
136
- * Disable a plugin via Claude CLI (keeps it in marketplace but hides from /plugin)
137
- */
138
- function disablePlugin(pluginName) {
139
- // Security: Validate plugin name
140
- if (!isValidPluginName(pluginName)) {
141
- return { success: false, output: 'Invalid plugin name format' };
142
- }
143
- return runCommand('claude', ['plugin', 'disable', pluginName], true);
144
- }
145
- /**
146
- * Remove a marketplace entirely
147
- */
148
- function removeMarketplace(name) {
149
- return runCommand('claude', ['plugin', 'marketplace', 'remove', name], true);
150
- }
151
- /**
152
- * Install a plugin directly from GitHub URL (not from marketplace)
153
- */
154
- function installPluginFromGitHub(repo, pluginPath) {
155
- const url = `https://github.com/${repo}/tree/main/${pluginPath}`;
156
- return runCommand('claude', ['plugin', 'install', url], true);
157
- }
158
- /**
159
- * Get version from plugin manifest
160
- */
161
- function getPluginVersion(cachePath) {
162
- const manifestPath = path.join(cachePath, '.claude-plugin/manifest.json');
163
- if (fs.existsSync(manifestPath)) {
164
- try {
165
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
166
- return manifest.version || '1.0.0';
167
- }
168
- catch {
169
- return '1.0.0';
170
- }
171
- }
172
- return '1.0.0';
173
- }
174
- /**
175
- * Log plugin installation (cache metadata removed - Claude Code manages cache)
176
- */
177
- function generatePluginCacheMetadata(pluginName) {
178
- const basePath = path.join(os.homedir(), '.claude/plugins/cache/specweave', pluginName);
179
- if (!fs.existsSync(basePath)) {
180
- logger.debug(`Plugin ${pluginName} not found in cache (may be installed elsewhere)`);
181
- return;
182
- }
183
- // Find latest version directory
184
- const versions = fs.readdirSync(basePath).filter(v => {
185
- const versionPath = path.join(basePath, v);
186
- return fs.statSync(versionPath).isDirectory();
187
- });
188
- if (versions.length === 0) {
189
- logger.debug(`No version directories found for ${pluginName}`);
190
- return;
191
- }
192
- const version = versions.sort().reverse()[0];
193
- logger.debug(`Plugin ${pluginName}@${version} installed successfully`);
194
- }
195
- /**
196
- * Get latest commit SHA from Git (if in repo) or marketplace
197
- */
198
- function getLatestCommitSha() {
199
- // Try to get from git (safe - uses array args)
200
- const result = runGitCommand(['rev-parse', 'HEAD']);
201
- if (result.success && result.output.length === 40) {
202
- return result.output;
203
- }
204
- // Fallback: check marketplace install location for git info
205
- const marketplacePath = getMarketplaceInstallPath();
206
- if (marketplacePath) {
207
- const gitHeadPath = path.join(marketplacePath, '.git/HEAD');
208
- if (fs.existsSync(gitHeadPath)) {
209
- try {
210
- const headContent = fs.readFileSync(gitHeadPath, 'utf8').trim();
211
- // If it's a ref, resolve it
212
- if (headContent.startsWith('ref:')) {
213
- const refPath = path.join(marketplacePath, '.git', headContent.replace('ref: ', ''));
214
- if (fs.existsSync(refPath)) {
215
- return fs.readFileSync(refPath, 'utf8').trim();
216
- }
217
- }
218
- return headContent;
219
- }
220
- catch {
221
- // Ignore
222
- }
223
- }
224
- }
225
- return null;
226
- }
227
- /**
228
- * Validate plugin name to prevent path traversal attacks
229
- * Plugin names should only contain alphanumeric chars, hyphens, and underscores
230
- */
231
- function isValidPluginName(name) {
232
- return /^[a-zA-Z0-9_-]+$/.test(name);
233
- }
234
- function installPlugin(pluginName, force = false) {
235
- // Security: Validate plugin name to prevent injection/traversal
236
- if (!isValidPluginName(pluginName)) {
237
- return { name: pluginName, success: false, error: 'Invalid plugin name format' };
238
- }
239
- // If force mode, uninstall first to ensure fresh copy
240
- if (force) {
241
- runCommand('claude', ['plugin', 'uninstall', pluginName], true);
242
- // Also clear the cache directory to ensure completely fresh install
243
- const cachePath = path.join(os.homedir(), '.claude/plugins/cache/specweave', pluginName);
244
- if (fs.existsSync(cachePath)) {
245
- try {
246
- fs.rmSync(cachePath, { recursive: true, force: true });
247
- }
248
- catch (e) {
249
- // Ignore errors, installation will handle it
250
- }
251
- }
252
- }
253
- const result = runCommand('claude', ['plugin', 'install', pluginName], true);
254
- if (result.success && result.output.includes('Successfully installed')) {
255
- // Generate cache metadata after successful installation
256
- generatePluginCacheMetadata(pluginName);
257
- return { name: pluginName, success: true };
258
- }
259
- // Check if already installed (not an error) - but only if not forcing
260
- if (!force && result.output.includes('already installed')) {
261
- // Still generate metadata if missing (for plugins installed before this feature)
262
- generatePluginCacheMetadata(pluginName);
263
- return { name: pluginName, success: true };
264
- }
265
- return { name: pluginName, success: false, error: result.output };
266
- }
267
- /**
268
- * Fix executable permissions on hook scripts.
269
- * Claude Code plugin installation doesn't preserve executable bits,
270
- * so we need to chmod +x all .sh files in hooks directories.
271
- */
272
- function fixHookPermissions(marketplacePath) {
273
- const errors = [];
274
- let fixed = 0;
275
- let skipped = 0;
276
- // Skip on Windows - chmod has no effect on NTFS
277
- if (process.platform === 'win32') {
278
- return { fixed: 0, skipped: 0, errors: [] };
279
- }
280
- const pluginsDir = path.join(marketplacePath, 'plugins');
281
- if (!fs.existsSync(pluginsDir)) {
282
- return { fixed, skipped, errors: ['Plugins directory not found'] };
283
- }
284
- // Find all plugin directories
285
- const pluginDirs = fs.readdirSync(pluginsDir).filter(name => {
286
- const pluginPath = path.join(pluginsDir, name);
287
- return fs.statSync(pluginPath).isDirectory();
288
- });
289
- /**
290
- * Check if file already has execute permission for owner
291
- */
292
- const isExecutable = (filePath) => {
293
- try {
294
- const stats = fs.statSync(filePath);
295
- // Check owner execute bit (0o100)
296
- return (stats.mode & 0o100) !== 0;
297
- }
298
- catch {
299
- return false;
300
- }
301
- };
302
- /**
303
- * Fix permission on a single file if needed
304
- */
305
- const fixFilePermission = (filePath, displayName) => {
306
- try {
307
- if (isExecutable(filePath)) {
308
- skipped++;
309
- return;
310
- }
311
- // Make executable (0o755 = rwxr-xr-x)
312
- fs.chmodSync(filePath, 0o755);
313
- fixed++;
314
- }
315
- catch (error) {
316
- errors.push(`${displayName}: ${error}`);
317
- }
318
- };
319
- for (const pluginName of pluginDirs) {
320
- const hooksDir = path.join(pluginsDir, pluginName, 'hooks');
321
- if (fs.existsSync(hooksDir)) {
322
- // Find all .sh files in hooks directory
323
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
324
- for (const hookFile of hookFiles) {
325
- const hookPath = path.join(hooksDir, hookFile);
326
- fixFilePermission(hookPath, `${pluginName}/${hookFile}`);
327
- }
328
- }
329
- // Also check scripts directory
330
- const scriptsDir = path.join(pluginsDir, pluginName, 'scripts');
331
- if (fs.existsSync(scriptsDir)) {
332
- const scriptFiles = fs.readdirSync(scriptsDir).filter(f => f.endsWith('.sh'));
333
- for (const scriptFile of scriptFiles) {
334
- const scriptPath = path.join(scriptsDir, scriptFile);
335
- fixFilePermission(scriptPath, `${pluginName}/scripts/${scriptFile}`);
336
- }
337
- }
338
- }
339
- return { fixed, skipped, errors };
340
- }
341
- /**
342
- * Fix executable permissions on hook scripts in lazy loading cache.
343
- * Similar to fixHookPermissions but for ~/.specweave/skills-cache/
344
- */
345
- function fixHookPermissionsInCache(cachePath) {
346
- const errors = [];
347
- let fixed = 0;
348
- let skipped = 0;
349
- // Skip on Windows - chmod has no effect on NTFS
350
- if (process.platform === 'win32') {
351
- return { fixed: 0, skipped: 0, errors: [] };
352
- }
353
- if (!fs.existsSync(cachePath)) {
354
- return { fixed, skipped, errors: [] };
355
- }
356
- /**
357
- * Check if file already has execute permission for owner
358
- */
359
- const isExecutable = (filePath) => {
360
- try {
361
- const stats = fs.statSync(filePath);
362
- return (stats.mode & 0o100) !== 0;
363
- }
364
- catch {
365
- return false;
366
- }
367
- };
368
- /**
369
- * Fix permission on a single file if needed
370
- */
371
- const fixFilePermission = (filePath) => {
372
- try {
373
- if (isExecutable(filePath)) {
374
- skipped++;
375
- return;
376
- }
377
- fs.chmodSync(filePath, 0o755);
378
- fixed++;
379
- }
380
- catch (error) {
381
- errors.push(`${filePath}: ${error}`);
382
- }
383
- };
384
- // Walk through cached plugins
385
- const pluginDirs = fs.readdirSync(cachePath).filter(name => {
386
- const pluginPath = path.join(cachePath, name);
387
- return fs.statSync(pluginPath).isDirectory();
388
- });
389
- for (const pluginName of pluginDirs) {
390
- const pluginPath = path.join(cachePath, pluginName);
391
- // Check hooks directory
392
- const hooksDir = path.join(pluginPath, 'hooks');
393
- if (fs.existsSync(hooksDir)) {
394
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
395
- for (const hookFile of hookFiles) {
396
- fixFilePermission(path.join(hooksDir, hookFile));
397
- }
398
- }
399
- // Check scripts directory
400
- const scriptsDir = path.join(pluginPath, 'scripts');
401
- if (fs.existsSync(scriptsDir)) {
402
- const scriptFiles = fs.readdirSync(scriptsDir).filter(f => f.endsWith('.sh'));
403
- for (const scriptFile of scriptFiles) {
404
- fixFilePermission(path.join(scriptsDir, scriptFile));
405
- }
406
- }
407
- }
408
- return { fixed, skipped, errors };
409
- }
410
- /**
411
- * Pre-refresh cache check (simplified - Claude Code manages plugin cache health)
412
- */
413
- async function preRefreshCacheCheck(verbose = false) {
414
- const basePath = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'specweave');
415
- if (!fs.existsSync(basePath)) {
416
- logger.debug('No plugin cache found - skipping pre-refresh check');
417
- return;
418
- }
419
- console.log(chalk.yellow('🔍 Checking plugin cache...'));
420
- const pluginNames = fs.readdirSync(basePath).filter(name => {
421
- const pluginPath = path.join(basePath, name);
422
- return fs.statSync(pluginPath).isDirectory();
423
- });
424
- if (pluginNames.length > 0) {
425
- console.log(chalk.green(`✓ Found ${pluginNames.length} cached plugin(s)`));
426
- if (verbose) {
427
- for (const name of pluginNames) {
428
- console.log(chalk.gray(` • ${name}`));
429
- }
430
- }
431
- }
432
- else {
433
- console.log(chalk.blue('ℹ No cached plugins found'));
434
- }
435
- console.log('');
436
- }
437
- /**
438
- * Minimal mode: Remove marketplace entirely, install only core plugins directly.
439
- * This results in a clean /plugin output with only installed plugins visible.
440
- * Tradeoff: Disables lazy loading capability.
441
- */
442
- async function runMinimalMode(options) {
443
- const forceMode = options.force ?? false;
444
- console.log(chalk.blue.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
445
- console.log(chalk.blue.bold(` SpecWeave Minimal Mode`));
446
- console.log(chalk.blue.bold(` Clean /plugin output | No lazy loading`));
447
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
448
- console.log(chalk.yellow('⚠️ Minimal mode:'));
449
- console.log(chalk.gray(' • Removes specweave marketplace entirely'));
450
- console.log(chalk.gray(' • Installs only core plugin (sw)'));
451
- console.log(chalk.gray(' • /plugin will show only installed plugins'));
452
- console.log(chalk.gray(' • Lazy loading will NOT work (use --all to reload)\n'));
453
- // Step 1: Uninstall all specweave plugins
454
- console.log(chalk.yellow('📦 Step 1: Uninstalling all SpecWeave plugins...'));
455
- const installedPlugins = getInstalledSpecweavePlugins();
456
- let uninstalledCount = 0;
457
- for (const plugin of installedPlugins) {
458
- const result = uninstallPlugin(plugin);
459
- if (result.success || result.output.includes('not installed')) {
460
- uninstalledCount++;
461
- if (options.verbose) {
462
- console.log(chalk.gray(` ✓ Uninstalled ${plugin}`));
463
- }
464
- }
465
- }
466
- console.log(chalk.green(`✓ Uninstalled ${uninstalledCount} plugin(s)\n`));
467
- // Step 2: Remove the specweave marketplace
468
- console.log(chalk.yellow('🗑️ Step 2: Removing specweave marketplace...'));
469
- const marketplaceExists = checkMarketplaceExists();
470
- if (marketplaceExists) {
471
- const removeResult = removeMarketplace(MARKETPLACE_NAME);
472
- if (removeResult.success || removeResult.output.includes('removed') || removeResult.output.includes('not found')) {
473
- console.log(chalk.green('✓ Marketplace removed\n'));
474
- }
475
- else {
476
- console.log(chalk.yellow('⚠ Could not remove marketplace'));
477
- if (options.verbose) {
478
- console.log(chalk.gray(` ${removeResult.output}`));
479
- }
480
- console.log('');
481
- }
482
- }
483
- else {
484
- console.log(chalk.blue('ℹ Marketplace not found - already removed\n'));
485
- }
486
- // Step 3: Clean up all caches
487
- console.log(chalk.yellow('🧹 Step 3: Cleaning up all caches...'));
488
- // Clean plugin cache
489
- const pluginCacheDir = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'specweave');
490
- if (fs.existsSync(pluginCacheDir)) {
491
- try {
492
- fs.rmSync(pluginCacheDir, { recursive: true, force: true });
493
- console.log(chalk.gray(' ✓ Removed plugin cache'));
494
- }
495
- catch (e) {
496
- if (options.verbose) {
497
- console.log(chalk.yellow(` ⚠ Could not remove plugin cache: ${e}`));
498
- }
499
- }
500
- }
501
- // Clean skills directory
502
- const skillsDir = path.join(os.homedir(), '.claude', 'skills');
503
- if (fs.existsSync(skillsDir)) {
504
- const skillDirs = fs.readdirSync(skillsDir).filter(name => {
505
- const dirPath = path.join(skillsDir, name);
506
- return fs.statSync(dirPath).isDirectory() && name.startsWith('specweave');
507
- });
508
- for (const dir of skillDirs) {
509
- try {
510
- fs.rmSync(path.join(skillsDir, dir), { recursive: true, force: true });
511
- if (options.verbose) {
512
- console.log(chalk.gray(` ✓ Removed skills/${dir}`));
513
- }
514
- }
515
- catch (e) {
516
- // Ignore
517
- }
518
- }
519
- if (skillDirs.length > 0) {
520
- console.log(chalk.gray(` ✓ Removed ${skillDirs.length} skill folder(s)`));
521
- }
522
- }
523
- // Clean marketplace directory
524
- const marketplaceDir = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces', 'specweave');
525
- if (fs.existsSync(marketplaceDir)) {
526
- try {
527
- fs.rmSync(marketplaceDir, { recursive: true, force: true });
528
- console.log(chalk.gray(' ✓ Removed marketplace directory'));
529
- }
530
- catch (e) {
531
- if (options.verbose) {
532
- console.log(chalk.yellow(` ⚠ Could not remove marketplace directory: ${e}`));
533
- }
534
- }
535
- }
536
- // Clean settings.json
537
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
538
- if (fs.existsSync(settingsPath)) {
539
- try {
540
- const settingsContent = fs.readFileSync(settingsPath, 'utf8');
541
- const settings = JSON.parse(settingsContent);
542
- if (settings.enabledPlugins) {
543
- for (const pluginKey of Object.keys(settings.enabledPlugins)) {
544
- if (pluginKey.endsWith('@specweave')) {
545
- delete settings.enabledPlugins[pluginKey];
546
- }
547
- }
548
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
549
- console.log(chalk.gray(' ✓ Cleaned settings.json'));
550
- }
551
- }
552
- catch (e) {
553
- // Ignore
554
- }
555
- }
556
- console.log(chalk.green('✓ All caches cleaned\n'));
557
- // Step 4: Install core plugins directly from GitHub
558
- console.log(chalk.yellow('📥 Step 4: Installing core plugins from GitHub...'));
559
- // First, add the marketplace back temporarily to install plugins
560
- const addResult = runCommand('claude', ['plugin', 'marketplace', 'add', GITHUB_REPO_URL], true);
561
- if (!addResult.success && !addResult.output.includes('already')) {
562
- console.log(chalk.red('✗ Failed to add marketplace for installation'));
563
- console.log(chalk.gray(addResult.output));
564
- throw new Error('Failed to add marketplace for plugin installation');
565
- }
566
- // Install core plugin (sw-router is obsolete as of v1.0.160)
567
- console.log(chalk.blue(` Installing sw@specweave...`));
568
- const swResult = installPlugin('sw', forceMode);
569
- let installedCount = 0;
570
- if (swResult.success) {
571
- installedCount++;
572
- console.log(chalk.green(` ✓ sw@specweave installed`));
573
- }
574
- else {
575
- console.log(chalk.red(` ✗ sw@specweave failed`));
576
- if (options.verbose) {
577
- console.log(chalk.gray(` ${swResult.error}`));
578
- }
579
- }
580
- // Now remove the marketplace again to clean /plugin output
581
- const removeResult2 = removeMarketplace(MARKETPLACE_NAME);
582
- if (removeResult2.success || removeResult2.output.includes('removed')) {
583
- console.log(chalk.gray(' ✓ Marketplace removed after installation'));
584
- }
585
- // CRITICAL FIX: Restore sw@specweave enabled state after uninstall operations
586
- // claude plugin uninstall can corrupt the entire enabledPlugins object as a side effect
587
- try {
588
- const { enablePlugin } = await import('../helpers/init/claude-plugin-enabler.js');
589
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
590
- enablePlugin('sw', 'specweave', settingsPath);
591
- if (options.verbose) {
592
- console.log(chalk.gray(' ✓ Restored sw@specweave enabled state'));
593
- }
594
- }
595
- catch {
596
- // Non-critical - settings may already be correct
597
- }
598
- // v1.0.240 (0198): Official plugins (context7, playwright) removed from auto-install
599
- // Users can install manually: claude plugin install context7@claude-plugins-official
600
- console.log('');
601
- console.log(chalk.green.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
602
- console.log(chalk.green.bold(' ✓ MINIMAL MODE COMPLETE'));
603
- console.log(chalk.green.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
604
- console.log(chalk.cyan(' Installed plugins:'));
605
- console.log(chalk.gray(' • sw@specweave - Core framework'));
606
- console.log('');
607
- console.log(` Total: ${installedCount} plugin(s)`);
608
- console.log(chalk.cyan(' /plugin will now show only installed plugins\n'));
609
- console.log(chalk.yellow('⚠️ Note: Lazy loading is disabled in minimal mode'));
610
- console.log(chalk.gray(' To re-enable lazy loading, run:'));
611
- console.log(chalk.gray(' specweave refresh-marketplace\n'));
612
- console.log(chalk.blue('Next steps:'));
613
- console.log(' 1. Restart Claude Code for changes to take effect');
614
- console.log(' 2. Run /plugin to verify clean output');
615
- console.log('');
616
- }
617
12
  export async function refreshMarketplaceCommand(options = {}) {
618
- // Handle minimal mode separately - completely different flow
619
- if (options.minimal) {
620
- await runMinimalMode(options);
621
- return;
622
- }
623
- const forceMode = options.force ?? false;
624
- const lazyMode = !(options.all ?? false);
625
- const modeLabel = lazyMode ? 'lazy' : 'all plugins';
626
- console.log(chalk.blue.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
627
- console.log(chalk.blue.bold(` SpecWeave Marketplace Refresh`));
628
- console.log(chalk.blue.bold(` Mode: ${modeLabel}${forceMode ? ' | FORCE' : ''}`));
629
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
630
- if (lazyMode) {
631
- console.log(chalk.cyan('🚀 Lazy loading mode (default):'));
632
- console.log(chalk.gray(' • Install only core plugin (~500 tokens)'));
633
- console.log(chalk.gray(' • Other plugins loaded on-demand'));
634
- console.log(chalk.gray(' • Use --all flag to install all plugins\n'));
635
- }
636
- else {
637
- console.log(chalk.yellow('⚠️ All plugins mode (legacy):'));
638
- console.log(chalk.gray(' • Installing all plugins (~60K tokens)'));
639
- console.log(chalk.gray(' • Consider using lazy mode (default) for better performance\n'));
640
- }
641
- if (forceMode) {
642
- console.log(chalk.yellow('🔄 Force mode: Will clear cache before reinstalling\n'));
643
- }
644
- // Plugin cache cleanup removed (v1.0.210) - Claude Code manages its own cache
645
- // Step 1: Check/update marketplace from GitHub
646
- console.log(chalk.yellow('📥 Step 1: Checking marketplace status...'));
647
- const marketplaceExists = checkMarketplaceExists();
648
- if (marketplaceExists) {
649
- console.log(chalk.blue(`✓ Marketplace '${MARKETPLACE_NAME}' registered`));
650
- console.log(chalk.blue('📥 Updating from GitHub...'));
651
- const updateResult = runCommand('claude', ['plugin', 'marketplace', 'update', MARKETPLACE_NAME], true);
652
- if (updateResult.success || updateResult.output.includes('Updated')) {
653
- console.log(chalk.green('✓ Marketplace updated'));
654
- }
655
- else {
656
- // Check if update failed due to SSH authentication
657
- // This happens when marketplace was registered with short form (owner/repo)
658
- // which Claude CLI converts to SSH URL (git@github.com:owner/repo.git)
659
- const isSshError = updateResult.output.includes('SSH authentication failed') ||
660
- updateResult.output.includes('Permission denied (publickey)') ||
661
- updateResult.output.includes('git@github.com');
662
- if (isSshError) {
663
- console.log(chalk.yellow('⚠ Marketplace registered with SSH URL - fixing...'));
664
- console.log(chalk.gray(' (This is a one-time fix for marketplaces registered before v1.0.229)'));
665
- // Remove the marketplace with broken SSH URL
666
- const removeResult = runCommand('claude', ['plugin', 'marketplace', 'remove', MARKETPLACE_NAME], true);
667
- if (!removeResult.success && !removeResult.output.includes('removed')) {
668
- console.log(chalk.red('✗ Could not remove broken marketplace'));
669
- console.log(chalk.gray(removeResult.output));
670
- throw new Error('Could not remove broken SSH marketplace');
671
- }
672
- // Re-add with correct HTTPS URL
673
- const reAddResult = runCommand('claude', ['plugin', 'marketplace', 'add', GITHUB_REPO_URL], true);
674
- if (reAddResult.success || reAddResult.output.includes('Added')) {
675
- console.log(chalk.green('✓ Marketplace re-registered with HTTPS URL'));
676
- }
677
- else {
678
- console.log(chalk.red('✗ Failed to re-add marketplace'));
679
- console.log(chalk.gray(reAddResult.output));
680
- throw new Error('Failed to re-add marketplace with HTTPS URL');
681
- }
682
- }
683
- else {
684
- console.log(chalk.red('✗ Failed to update marketplace'));
685
- console.log(chalk.gray(updateResult.output));
686
- throw new Error(`Failed to update marketplace: ${updateResult.output}`);
687
- }
688
- }
689
- }
690
- else {
691
- console.log(chalk.blue('Marketplace not found - adding from GitHub...'));
692
- const addArgs = ['plugin', 'marketplace', 'add', GITHUB_REPO_URL];
693
- const addResult = runCommand('claude', addArgs, true);
694
- if (addResult.success || addResult.output.includes('Added') || addResult.output.includes('already')) {
695
- console.log(chalk.green('✓ GitHub marketplace added'));
696
- }
697
- else {
698
- console.log(chalk.red('✗ Failed to add marketplace'));
699
- console.log(chalk.gray(addResult.output));
700
- throw new Error(`Failed to add marketplace: ${addResult.output}`);
701
- }
702
- }
703
- console.log('');
704
- // Step 1.5: Pre-refresh cache health check
705
- await preRefreshCacheCheck(options.verbose);
706
- // Auto mode state cleanup is handled by updateCommand() - no duplication needed here
707
- console.log('');
708
- // Step 2: Get plugin list
709
- console.log(chalk.yellow('📋 Step 2: Reading plugin list...'));
710
- const marketplacePath = getMarketplaceInstallPath();
711
- if (!marketplacePath) {
712
- console.log(chalk.red('✗ Error: Could not find marketplace install location'));
713
- console.log(chalk.yellow(' Check ~/.claude/plugins/known_marketplaces.json'));
714
- throw new Error('Could not find marketplace install location');
715
- }
716
- let plugins;
717
- try {
718
- plugins = getPluginsFromMarketplace(marketplacePath);
719
- }
720
- catch (error) {
721
- console.log(chalk.red(`✗ Error: ${error}`));
722
- throw new Error(`Failed to read marketplace plugins: ${error}`);
723
- }
724
- console.log(chalk.green(`✓ Found ${plugins.length} plugins\n`));
725
- // Step 3: Install plugins (LAZY LOADING AWARE - v1.0.122+)
726
- const results = [];
727
- if (lazyMode) {
728
- // LAZY MODE: Install only core plugin (sw)
729
- // NOTE: sw-router is OBSOLETE as of v1.0.160 - detect-intent handles plugin detection
730
- const corePlugins = ['sw']; // Only core plugin kept in lazy mode
731
- // Step 3a: Uninstall non-core plugins to match fresh install state
732
- console.log(chalk.yellow('⚙️ Step 3a: Cleaning up non-core plugins...'));
733
- const installedPlugins = getInstalledSpecweavePlugins();
734
- const pluginsToUninstall = installedPlugins.filter(p => !corePlugins.includes(p));
735
- if (pluginsToUninstall.length > 0) {
736
- console.log(chalk.blue(` Found ${pluginsToUninstall.length} non-core plugin(s) to uninstall`));
737
- for (const plugin of pluginsToUninstall) {
738
- const uninstallResult = uninstallPlugin(plugin);
739
- if (uninstallResult.success || uninstallResult.output.includes('not installed')) {
740
- console.log(chalk.gray(` ✓ Uninstalled ${plugin}`));
741
- }
742
- else if (options.verbose) {
743
- console.log(chalk.yellow(` ⚠ Could not uninstall ${plugin}: ${uninstallResult.output}`));
744
- }
745
- }
746
- console.log(chalk.green(`✓ Cleaned up ${pluginsToUninstall.length} non-core plugin(s)\n`));
747
- }
748
- else {
749
- console.log(chalk.green('✓ No non-core plugins to clean up\n'));
750
- }
751
- // Also clean up ~/.claude/skills/ directory for non-core plugins
752
- // (claude plugin uninstall may not always clean this up)
753
- const skillsDir = path.join(os.homedir(), '.claude', 'skills');
754
- const coreSkillDirs = ['specweave']; // Only core plugin folder kept
755
- if (fs.existsSync(skillsDir)) {
756
- const skillDirs = fs.readdirSync(skillsDir).filter(name => {
757
- const dirPath = path.join(skillsDir, name);
758
- return fs.statSync(dirPath).isDirectory() && name.startsWith('specweave');
759
- });
760
- const dirsToRemove = skillDirs.filter(dir => !coreSkillDirs.includes(dir));
761
- if (dirsToRemove.length > 0) {
762
- console.log(chalk.blue(` Cleaning up ${dirsToRemove.length} skill folder(s)...`));
763
- for (const dir of dirsToRemove) {
764
- try {
765
- fs.rmSync(path.join(skillsDir, dir), { recursive: true, force: true });
766
- console.log(chalk.gray(` ✓ Removed ${dir}/`));
767
- }
768
- catch (e) {
769
- if (options.verbose) {
770
- console.log(chalk.yellow(` ⚠ Could not remove ${dir}/`));
771
- }
772
- }
773
- }
774
- }
775
- }
776
- // CRITICAL: Also clean up ~/.claude/plugins/cache/specweave/ for non-core plugins
777
- // This is where Claude Code discovers plugins from, even if not "installed"
778
- const pluginCacheDir = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'specweave');
779
- const corePluginCacheDirs = ['sw']; // Only core plugin cache kept
780
- if (fs.existsSync(pluginCacheDir)) {
781
- const cacheDirs = fs.readdirSync(pluginCacheDir).filter(name => {
782
- const dirPath = path.join(pluginCacheDir, name);
783
- return fs.statSync(dirPath).isDirectory();
784
- });
785
- const cacheDirsToRemove = cacheDirs.filter(dir => !corePluginCacheDirs.includes(dir));
786
- if (cacheDirsToRemove.length > 0) {
787
- console.log(chalk.blue(` Cleaning up ${cacheDirsToRemove.length} cached plugin(s)...`));
788
- for (const dir of cacheDirsToRemove) {
789
- try {
790
- fs.rmSync(path.join(pluginCacheDir, dir), { recursive: true, force: true });
791
- console.log(chalk.gray(` ✓ Removed cache: ${dir}/`));
792
- }
793
- catch (e) {
794
- if (options.verbose) {
795
- console.log(chalk.yellow(` ⚠ Could not remove cache: ${dir}/`));
796
- }
797
- }
798
- }
799
- console.log(chalk.green(`✓ Cleaned up ${cacheDirsToRemove.length} cached plugin(s)\n`));
800
- }
801
- else {
802
- console.log(chalk.green('✓ No cached plugins to clean up\n'));
803
- }
804
- }
805
- // Also clean up settings.json enabledPlugins for non-core specweave plugins
806
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
807
- const coreEnabledPlugins = ['sw@specweave']; // Only core plugin enabled
808
- if (fs.existsSync(settingsPath)) {
809
- try {
810
- const settingsContent = fs.readFileSync(settingsPath, 'utf8');
811
- const settings = JSON.parse(settingsContent);
812
- if (settings.enabledPlugins) {
813
- const pluginsToDisable = [];
814
- for (const pluginKey of Object.keys(settings.enabledPlugins)) {
815
- const pluginName = pluginKey.split('@')[0];
816
- // Target non-core specweave plugins AND LSP plugins (v1.0.249)
817
- if ((pluginKey.endsWith('@specweave') && !coreEnabledPlugins.includes(pluginKey)) ||
818
- pluginName.endsWith('-lsp')) {
819
- pluginsToDisable.push(pluginKey);
820
- delete settings.enabledPlugins[pluginKey];
821
- }
822
- }
823
- if (pluginsToDisable.length > 0) {
824
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
825
- console.log(chalk.blue(` Disabled ${pluginsToDisable.length} plugin(s) in settings.json`));
826
- if (options.verbose) {
827
- for (const p of pluginsToDisable) {
828
- console.log(chalk.gray(` ✓ Disabled: ${p}`));
829
- }
830
- }
831
- }
832
- }
833
- // CRITICAL FIX: Ensure sw@specweave is enabled after cleanup
834
- // The manual cleanup above only deletes non-core plugins, but uninstall operations
835
- // earlier could have corrupted sw@specweave state. Explicitly restore it.
836
- settings.enabledPlugins = settings.enabledPlugins || {};
837
- settings.enabledPlugins['sw@specweave'] = true;
838
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
839
- if (options.verbose) {
840
- console.log(chalk.gray(' ✓ Verified sw@specweave enabled state'));
841
- }
842
- }
843
- catch (e) {
844
- if (options.verbose) {
845
- console.log(chalk.yellow(` ⚠ Could not clean settings.json: ${e}`));
846
- }
847
- }
848
- }
849
- // Step 3b: Disable all non-core marketplace plugins
850
- // This ensures they don't show up in /plugin as enabled
851
- console.log(chalk.yellow('⚙️ Step 3b: Disabling non-core marketplace plugins...'));
852
- const nonCorePlugins = plugins.filter(p => !corePlugins.includes(p));
853
- let actuallyDisabled = 0;
854
- let alreadyNotInstalled = 0;
855
- for (const plugin of nonCorePlugins) {
856
- const disableResult = disablePlugin(plugin);
857
- if (disableResult.success) {
858
- actuallyDisabled++;
859
- if (options.verbose) {
860
- console.log(chalk.gray(` ✓ Disabled ${plugin}`));
861
- }
862
- }
863
- else if (disableResult.output.includes('not installed') || disableResult.output.includes('already disabled')) {
864
- alreadyNotInstalled++;
865
- if (options.verbose) {
866
- console.log(chalk.gray(` - ${plugin} (not installed)`));
867
- }
868
- }
869
- }
870
- // Show appropriate summary based on what happened
871
- if (actuallyDisabled > 0 && alreadyNotInstalled > 0) {
872
- console.log(chalk.green(`✓ Disabled ${actuallyDisabled} plugin(s), ${alreadyNotInstalled} already not installed\n`));
873
- }
874
- else if (actuallyDisabled > 0) {
875
- console.log(chalk.green(`✓ Disabled ${actuallyDisabled} non-core marketplace plugin(s)\n`));
876
- }
877
- else if (alreadyNotInstalled > 0) {
878
- console.log(chalk.green(`✓ ${alreadyNotInstalled} non-core plugins not installed (skipped)\n`));
879
- }
880
- else {
881
- console.log(chalk.green(`✓ No non-core plugins to disable\n`));
882
- }
883
- // Step 3c: Install core plugin (sw-router is obsolete - detect-intent handles routing)
884
- console.log(chalk.yellow(`⚙️ Step 3c: Installing core plugin${forceMode ? ' + force' : ''}...\n`));
885
- const corePlugin = 'sw'; // Marketplace name for core specweave plugin
886
- console.log(chalk.blue(` ${forceMode ? 'Force reinstalling' : 'Installing'} ${corePlugin}@specweave...`));
887
- const result = installPlugin(corePlugin, forceMode);
888
- results.push(result);
889
- if (result.success) {
890
- console.log(chalk.green(` ✓ ${corePlugin}@specweave installed`));
891
- }
892
- else {
893
- console.log(chalk.red(` ✗ ${corePlugin}@specweave failed`));
894
- if (options.verbose && result.error) {
895
- console.log(chalk.gray(` ${result.error}`));
896
- }
897
- }
898
- // v1.0.240 (0198): Official plugins (context7, playwright) removed from auto-install
899
- console.log('');
900
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
901
- console.log(chalk.blue.bold(' Lazy Loading Summary'));
902
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
903
- console.log(chalk.cyan(' Installed (always loaded):'));
904
- console.log(chalk.gray(' • sw@specweave - Core framework'));
905
- console.log('');
906
- console.log(` Total SpecWeave plugins available: ${plugins.length}`);
907
- console.log(chalk.green(` Core installed: 1`));
908
- console.log(chalk.cyan(` Cached for on-demand: ${plugins.length - 1}`));
909
- console.log('');
910
- console.log(chalk.green(' Token savings:'));
911
- console.log(chalk.gray(` Before: ~60,000 tokens (all plugins)`));
912
- console.log(chalk.gray(` After: ~3,000 tokens (core only)`));
913
- console.log(chalk.green(` Saved: ~57,000 tokens (95% reduction!)`));
914
- }
915
- else {
916
- // LEGACY MODE: Install all plugins
917
- console.log(chalk.yellow(`⚙️ Step 3: Installing all plugins${forceMode ? ' (force reinstall)' : ''}...\n`));
918
- for (const plugin of plugins) {
919
- console.log(chalk.blue(` ${forceMode ? 'Force reinstalling' : 'Installing'} ${plugin}...`));
920
- const result = installPlugin(plugin, forceMode);
921
- results.push(result);
922
- if (result.success) {
923
- console.log(chalk.green(` ✓ ${plugin} installed`));
924
- }
925
- else {
926
- console.log(chalk.red(` ✗ ${plugin} failed`));
927
- if (options.verbose && result.error) {
928
- console.log(chalk.gray(` ${result.error}`));
929
- }
930
- }
931
- }
932
- console.log('');
933
- // Summary for all plugins mode
934
- const successCount = results.filter((r) => r.success).length;
935
- const failCount = results.filter((r) => !r.success).length;
936
- const failedPlugins = results.filter((r) => !r.success);
937
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
938
- console.log(chalk.blue.bold(' Installation Summary'));
939
- console.log(chalk.blue.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
940
- console.log(` Total plugins: ${plugins.length}`);
941
- console.log(chalk.green(` Successful: ${successCount}`));
942
- if (failCount > 0) {
943
- console.log(chalk.red(` Failed: ${failCount}\n`));
944
- console.log(chalk.yellow('Failed plugins:'));
945
- for (const plugin of failedPlugins) {
946
- console.log(chalk.red(` - ${plugin.name}`));
947
- }
948
- console.log('');
949
- console.log(chalk.yellow('⚠ Some plugins failed to install'));
950
- console.log(chalk.yellow('Check Claude Code logs for details'));
951
- }
952
- else {
953
- console.log(chalk.red(` Failed: 0\n`));
954
- console.log(chalk.green.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
955
- console.log(chalk.green.bold(' ✓ ALL PLUGINS INSTALLED SUCCESSFULLY!'));
956
- console.log(chalk.green.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
957
- }
958
- }
959
- console.log('');
960
- // Step 3.5: Fix hook permissions (chmod +x) - Unix only
961
- if (process.platform !== 'win32') {
962
- console.log(chalk.yellow('🔧 Step 3.5: Fixing hook permissions...'));
963
- // Fix permissions on marketplace plugins
964
- const permResult = fixHookPermissions(marketplacePath);
965
- let totalFixed = permResult.fixed;
966
- let totalSkipped = permResult.skipped;
967
- // LAZY LOADING: Also fix permissions on cached plugins
968
- const skillsCachePath = path.join(os.homedir(), '.specweave', 'skills-cache');
969
- if (fs.existsSync(skillsCachePath)) {
970
- const cachePermResult = fixHookPermissionsInCache(skillsCachePath);
971
- totalFixed += cachePermResult.fixed;
972
- totalSkipped += cachePermResult.skipped;
973
- if (options.verbose && cachePermResult.fixed > 0) {
974
- console.log(chalk.gray(` (${cachePermResult.fixed} in lazy cache)`));
975
- }
976
- }
977
- if (totalFixed > 0) {
978
- console.log(chalk.green(`✓ Fixed permissions on ${totalFixed} hook/script files`));
979
- }
980
- if (totalSkipped > 0 && options.verbose) {
981
- console.log(chalk.dim(` (${totalSkipped} files already executable)`));
982
- }
983
- if (totalFixed === 0 && totalSkipped > 0) {
984
- console.log(chalk.green(`✓ All ${totalSkipped} hook/script files already executable`));
985
- }
986
- if (permResult.errors.length > 0 && options.verbose) {
987
- for (const err of permResult.errors) {
988
- console.log(chalk.yellow(` ⚠ ${err}`));
989
- }
990
- }
991
- }
992
- else {
993
- if (options.verbose) {
994
- console.log(chalk.dim('🔧 Step 3.5: Skipped (Windows - permissions not applicable)'));
995
- }
996
- }
997
- console.log('');
998
- // Step 4: Verify marketplace ready for lazy loading
999
- console.log(chalk.yellow('📦 Step 4: Verifying marketplace for lazy loading...'));
1000
- try {
1001
- const pluginsPath = path.join(marketplacePath, 'plugins');
1002
- if (fs.existsSync(pluginsPath)) {
1003
- const pluginDirs = fs.readdirSync(pluginsPath)
1004
- .filter(name => fs.statSync(path.join(pluginsPath, name)).isDirectory());
1005
- console.log(chalk.green(`✓ ${pluginDirs.length} plugins ready for on-demand loading`));
1006
- console.log(chalk.gray(` Marketplace: ~/.claude/plugins/marketplaces/specweave/plugins/`));
1007
- }
1008
- else {
1009
- console.log(chalk.yellow('⚠ Marketplace plugins directory not found'));
1010
- }
1011
- }
1012
- catch (error) {
1013
- console.log(chalk.yellow('⚠ Could not verify marketplace'));
1014
- if (options.verbose) {
1015
- console.log(chalk.gray(` ${error}`));
1016
- }
1017
- }
1018
- console.log('');
1019
- // Step 4.5: User-level plugin scope guard (v1.0.249)
1020
- // Migrates sw-*@specweave and *-lsp@* from user scope to project scope
1021
- console.log(chalk.yellow('🔒 Step 4.5: Checking for user-level plugin pollution...'));
1022
- try {
1023
- const projectDir = fs.existsSync(path.join(process.cwd(), '.claude')) ? process.cwd() : undefined;
1024
- const migrationResult = await migrateUserLevelPlugins(projectDir, options.verbose);
1025
- if (migrationResult.migratedCount > 0) {
1026
- console.log(chalk.green(`✓ Migrated ${migrationResult.migratedCount} plugin(s) from user → project scope`));
1027
- for (const p of migrationResult.migratedPlugins) {
1028
- console.log(chalk.gray(` - ${p}`));
1029
- }
1030
- }
1031
- else {
1032
- console.log(chalk.green('✓ No user-level pollution detected'));
1033
- }
1034
- }
1035
- catch {
1036
- console.log(chalk.yellow('⚠ Could not check user-level plugins'));
1037
- }
1038
- console.log('');
1039
- // Step 5: Update instruction files
1040
- console.log(chalk.yellow('📄 Step 5: Updating instruction files...'));
1041
- const configPath = path.join(process.cwd(), '.specweave/config.json');
1042
- if (fs.existsSync(configPath)) {
1043
- const updateResult = runCommand('npx', ['specweave', 'update-instructions'], true);
1044
- if (updateResult.success) {
1045
- console.log(chalk.green('✓ Instruction files updated'));
1046
- }
1047
- else {
1048
- console.log(chalk.yellow('⚠ Could not update instruction files'));
1049
- console.log(chalk.gray(' Run manually: npx specweave update-instructions'));
1050
- }
1051
- }
1052
- else {
1053
- console.log(chalk.blue('ℹ Not in a SpecWeave project - skipping instruction file update'));
1054
- }
1055
- console.log('');
1056
- console.log(chalk.blue('Next steps:'));
1057
- console.log(' 1. Restart Claude Code for changes to take effect');
1058
- if (lazyMode) {
1059
- console.log(` 2. Run ${chalk.yellow('/plugin')} to verify router loaded`);
1060
- console.log(` 3. Use keywords to trigger on-demand plugin loading`);
1061
- console.log(chalk.gray(' Examples: "GitHub sync", "JIRA integration", "React frontend"'));
1062
- console.log(` 4. Or install plugins manually: ${chalk.yellow('claude plugin install sw@specweave')}`);
1063
- console.log(chalk.gray(' Available: sw, sw-frontend, sw-github, sw-jira, sw-ado, sw-ml, sw-infra'));
1064
- }
1065
- else {
1066
- console.log(` 2. Run ${chalk.yellow('/plugin')} to verify all plugins loaded`);
1067
- console.log(` 3. Check ${chalk.yellow('~/.claude/plugins/installed_plugins.json')}`);
1068
- }
1069
- console.log('');
13
+ // DEPRECATION: refresh-marketplace is deprecated in favor of refresh-plugins (vskill-backed)
14
+ console.log(chalk.yellow('\n[DEPRECATED] "refresh-marketplace" is deprecated. Use "refresh-plugins" instead.'));
15
+ console.log(chalk.gray(' Run: specweave refresh-plugins'));
16
+ console.log(chalk.gray(' This command will be removed in a future version.\n'));
17
+ // Delegate to the new refresh-plugins command
18
+ const { refreshPluginsCommand } = await import('./refresh-plugins.js');
19
+ await refreshPluginsCommand({
20
+ all: options.all,
21
+ force: options.force,
22
+ verbose: options.verbose,
23
+ });
1070
24
  }
1071
25
  //# sourceMappingURL=refresh-marketplace.js.map