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.
- package/CLAUDE.md +28 -28
- package/bin/specweave.js +13 -0
- package/dist/dashboard/assets/index-BKdLA_6x.js +11 -0
- package/dist/dashboard/assets/index-Cs3Zq6E2.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/src/cli/commands/detect-intent.js +1 -1
- package/dist/src/cli/commands/detect-intent.js.map +1 -1
- package/dist/src/cli/commands/judge-skill.d.ts.map +1 -1
- package/dist/src/cli/commands/judge-skill.js +11 -0
- package/dist/src/cli/commands/judge-skill.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-umbrella.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-to-umbrella.js +97 -2
- package/dist/src/cli/commands/migrate-to-umbrella.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-vskill.d.ts +43 -0
- package/dist/src/cli/commands/migrate-to-vskill.d.ts.map +1 -0
- package/dist/src/cli/commands/migrate-to-vskill.js +144 -0
- package/dist/src/cli/commands/migrate-to-vskill.js.map +1 -0
- package/dist/src/cli/commands/refresh-marketplace.d.ts +4 -23
- package/dist/src/cli/commands/refresh-marketplace.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-marketplace.js +15 -1061
- package/dist/src/cli/commands/refresh-marketplace.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts +28 -0
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -0
- package/dist/src/cli/commands/refresh-plugins.js +272 -0
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -0
- package/dist/src/cli/commands/uninstall.d.ts +11 -0
- package/dist/src/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/src/cli/commands/uninstall.js +164 -0
- package/dist/src/cli/commands/uninstall.js.map +1 -0
- package/dist/src/cli/helpers/init/claude-plugin-enabler.d.ts +6 -2
- package/dist/src/cli/helpers/init/claude-plugin-enabler.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/claude-plugin-enabler.js +6 -2
- package/dist/src/cli/helpers/init/claude-plugin-enabler.js.map +1 -1
- package/dist/src/cli/helpers/init/instruction-file-merger.d.ts +5 -0
- package/dist/src/cli/helpers/init/instruction-file-merger.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/instruction-file-merger.js +18 -0
- package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.d.ts +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.js +98 -364
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
- package/dist/src/cli/workers/marketplace-scanner-worker.d.ts +73 -0
- package/dist/src/cli/workers/marketplace-scanner-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/marketplace-scanner-worker.js +405 -0
- package/dist/src/cli/workers/marketplace-scanner-worker.js.map +1 -0
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/background/job-launcher.d.ts +24 -0
- package/dist/src/core/background/job-launcher.d.ts.map +1 -1
- package/dist/src/core/background/job-launcher.js +80 -0
- package/dist/src/core/background/job-launcher.js.map +1 -1
- package/dist/src/core/background/types.d.ts +20 -2
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/fabric/registry-schema.d.ts +117 -0
- package/dist/src/core/fabric/registry-schema.d.ts.map +1 -1
- package/dist/src/core/fabric/registry-schema.js +37 -1
- package/dist/src/core/fabric/registry-schema.js.map +1 -1
- package/dist/src/core/fabric/security-judge.d.ts.map +1 -1
- package/dist/src/core/fabric/security-judge.js +38 -17
- package/dist/src/core/fabric/security-judge.js.map +1 -1
- package/dist/src/core/fabric/submission-queue-types.d.ts +83 -0
- package/dist/src/core/fabric/submission-queue-types.d.ts.map +1 -0
- package/dist/src/core/fabric/submission-queue-types.js +8 -0
- package/dist/src/core/fabric/submission-queue-types.js.map +1 -0
- package/dist/src/core/fabric/submission-queue.d.ts +65 -0
- package/dist/src/core/fabric/submission-queue.d.ts.map +1 -0
- package/dist/src/core/fabric/submission-queue.js +267 -0
- package/dist/src/core/fabric/submission-queue.js.map +1 -0
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +5 -3
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.js +66 -13
- package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
- package/dist/src/core/migration/umbrella-migrator.d.ts +9 -0
- package/dist/src/core/migration/umbrella-migrator.d.ts.map +1 -1
- package/dist/src/core/migration/umbrella-migrator.js +38 -4
- package/dist/src/core/migration/umbrella-migrator.js.map +1 -1
- package/dist/src/core/session/plugin-install-detector.d.ts.map +1 -1
- package/dist/src/core/session/plugin-install-detector.js +3 -2
- package/dist/src/core/session/plugin-install-detector.js.map +1 -1
- package/dist/src/dashboard/server/dashboard-server.d.ts.map +1 -1
- package/dist/src/dashboard/server/dashboard-server.js +115 -3
- package/dist/src/dashboard/server/dashboard-server.js.map +1 -1
- package/dist/src/dashboard/server/data/cost-aggregator.d.ts +0 -1
- package/dist/src/dashboard/server/data/cost-aggregator.d.ts.map +1 -1
- package/dist/src/dashboard/server/data/cost-aggregator.js +0 -1
- package/dist/src/dashboard/server/data/cost-aggregator.js.map +1 -1
- package/dist/src/dashboard/server/data/marketplace-aggregator.d.ts +27 -0
- package/dist/src/dashboard/server/data/marketplace-aggregator.d.ts.map +1 -0
- package/dist/src/dashboard/server/data/marketplace-aggregator.js +61 -0
- package/dist/src/dashboard/server/data/marketplace-aggregator.js.map +1 -0
- package/dist/src/dashboard/server/file-watcher.d.ts.map +1 -1
- package/dist/src/dashboard/server/file-watcher.js +1 -0
- package/dist/src/dashboard/server/file-watcher.js.map +1 -1
- package/dist/src/dashboard/types.d.ts +1 -1
- package/dist/src/dashboard/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/utils/cleanup-stale-plugins.d.ts +1 -1
- package/dist/src/utils/cleanup-stale-plugins.js +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.js +9 -6
- package/dist/src/utils/docs-preview/docusaurus-setup.js.map +1 -1
- package/dist/src/utils/vskill-resolver.d.ts +24 -0
- package/dist/src/utils/vskill-resolver.d.ts.map +1 -0
- package/dist/src/utils/vskill-resolver.js +98 -0
- package/dist/src/utils/vskill-resolver.js.map +1 -0
- package/package.json +3 -1
- package/plugins/specweave/PLUGIN.md +1 -1
- package/plugins/specweave/hooks/user-prompt-submit.sh +147 -55
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +1 -1
- package/plugins/specweave/skills/npm/SKILL.md +6 -6
- package/plugins/specweave/skills/update-instructions/SKILL.md +1 -1
- package/plugins/specweave-ado/PLUGIN.md +1 -1
- package/plugins/specweave-backend/PLUGIN.md +1 -1
- package/plugins/specweave-blockchain/PLUGIN.md +1 -1
- package/plugins/specweave-confluent/PLUGIN.md +1 -1
- package/plugins/specweave-cost-optimizer/PLUGIN.md +1 -1
- package/plugins/specweave-desktop/PLUGIN.md +1 -1
- package/plugins/specweave-diagrams/PLUGIN.md +1 -1
- package/plugins/specweave-docs/PLUGIN.md +1 -1
- package/plugins/specweave-figma/PLUGIN.md +1 -1
- package/plugins/specweave-frontend/PLUGIN.md +1 -1
- package/plugins/specweave-github/PLUGIN.md +1 -1
- package/plugins/specweave-infrastructure/PLUGIN.md +1 -1
- package/plugins/specweave-jira/PLUGIN.md +1 -1
- package/plugins/specweave-kafka/PLUGIN.md +1 -1
- package/plugins/specweave-kafka-streams/PLUGIN.md +1 -1
- package/plugins/specweave-kubernetes/PLUGIN.md +1 -1
- package/plugins/specweave-ml/PLUGIN.md +1 -1
- package/plugins/specweave-mobile/PLUGIN.md +1 -1
- package/plugins/specweave-mobile/README.md +2 -2
- package/plugins/specweave-n8n/PLUGIN.md +1 -1
- package/plugins/specweave-payments/PLUGIN.md +1 -1
- package/plugins/specweave-release/PLUGIN.md +1 -1
- package/plugins/specweave-release/commands/npm.md +6 -6
- package/plugins/specweave-testing/PLUGIN.md +1 -1
- package/scripts/preuninstall.cjs +34 -0
- package/src/templates/CLAUDE.md.template +3 -3
- package/dist/dashboard/assets/index-DNnisM2Y.css +0 -1
- 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
|
-
*
|
|
6
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|