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