specweave 1.0.278 → 1.0.279
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/dist/src/cli/commands/refresh-plugins.d.ts +5 -5
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +35 -174
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.d.ts +3 -3
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.js +32 -98
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts +77 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -0
- package/dist/src/utils/plugin-copier.js +216 -0
- package/dist/src/utils/plugin-copier.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/user-prompt-submit.sh +45 -34
- package/plugins/specweave-jira/skills/jira-sync/SKILL.md +114 -129
- package/plugins/specweave-mobile/PLUGIN.md +1 -0
- package/plugins/specweave-mobile/skills/appstore/SKILL.md +1099 -0
- package/plugins/specweave-mobile/skills/capacitor-ionic/SKILL.md +1 -0
- package/plugins/specweave-mobile/skills/deep-linking-push/SKILL.md +1 -0
- package/plugins/specweave-mobile/skills/expo/SKILL.md +1 -0
- package/plugins/specweave-mobile/skills/flutter/SKILL.md +7 -0
- package/plugins/specweave-mobile/skills/jetpack-compose/SKILL.md +7 -0
- package/plugins/specweave-mobile/skills/mobile-testing/SKILL.md +1 -0
- package/plugins/specweave-mobile/skills/react-native-expert/SKILL.md +1 -0
- package/plugins/specweave-mobile/skills/swiftui/SKILL.md +6 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Refresh SpecWeave Plugins
|
|
2
|
+
* Refresh SpecWeave Plugins
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Copies first-party plugins from specweave's bundled plugins/ directory
|
|
5
|
+
* to ~/.claude/commands/<name>/. Uses inline plugin copier (no vskill dependency).
|
|
6
6
|
*
|
|
7
7
|
* Modes:
|
|
8
8
|
* - Default (lazy): Install only core `sw` plugin
|
|
9
9
|
* - --all: Install all plugins from marketplace.json
|
|
10
10
|
* - Hash comparison: Skip plugins whose content hash hasn't changed
|
|
11
11
|
*
|
|
12
|
-
* @since 1.0.
|
|
12
|
+
* @since 1.0.279
|
|
13
13
|
*/
|
|
14
14
|
export interface RefreshPluginsOptions {
|
|
15
15
|
verbose?: boolean;
|
|
@@ -18,7 +18,7 @@ export interface RefreshPluginsOptions {
|
|
|
18
18
|
all?: boolean;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Refresh plugins using
|
|
21
|
+
* Refresh plugins using inline copier.
|
|
22
22
|
*
|
|
23
23
|
* Default (lazy mode): installs only core `sw` plugin.
|
|
24
24
|
* With --all flag: installs all plugins from marketplace.json.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-plugins.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/refresh-plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"refresh-plugins.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/refresh-plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAsBH,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mFAAmF;IACnF,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA8DD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0E9F"}
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Refresh SpecWeave Plugins
|
|
2
|
+
* Refresh SpecWeave Plugins
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Copies first-party plugins from specweave's bundled plugins/ directory
|
|
5
|
+
* to ~/.claude/commands/<name>/. Uses inline plugin copier (no vskill dependency).
|
|
6
6
|
*
|
|
7
7
|
* Modes:
|
|
8
8
|
* - Default (lazy): Install only core `sw` plugin
|
|
9
9
|
* - --all: Install all plugins from marketplace.json
|
|
10
10
|
* - Hash comparison: Skip plugins whose content hash hasn't changed
|
|
11
11
|
*
|
|
12
|
-
* @since 1.0.
|
|
12
|
+
* @since 1.0.279
|
|
13
13
|
*/
|
|
14
14
|
import chalk from 'chalk';
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
|
-
import { createHash } from 'node:crypto';
|
|
18
17
|
import { consoleLogger as logger } from '../../utils/logger.js';
|
|
19
|
-
import { execFileNoThrowSync } from '../../utils/execFileNoThrow.js';
|
|
20
18
|
import { getDirname } from '../../utils/esm-helpers.js';
|
|
19
|
+
import { copyPlugin, findSpecweaveRoot, } from '../../utils/plugin-copier.js';
|
|
21
20
|
const __dirname = getDirname(import.meta.url);
|
|
22
21
|
// ---------------------------------------------------------------------------
|
|
23
22
|
// Constants
|
|
@@ -28,26 +27,19 @@ const CORE_PLUGINS = ['sw'];
|
|
|
28
27
|
// Helpers
|
|
29
28
|
// ---------------------------------------------------------------------------
|
|
30
29
|
/**
|
|
31
|
-
* Resolve the path to the specweave
|
|
32
|
-
*
|
|
30
|
+
* Resolve the path to the specweave root directory.
|
|
31
|
+
* Tries multiple strategies for both development and production layouts.
|
|
33
32
|
*/
|
|
34
|
-
function
|
|
35
|
-
// Strategy 1:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Installed marketplace location (Claude Code manages this)
|
|
45
|
-
path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude/plugins/marketplaces/specweave/.claude-plugin/marketplace.json'),
|
|
46
|
-
];
|
|
47
|
-
for (const candidate of candidates) {
|
|
48
|
-
if (fs.existsSync(candidate)) {
|
|
49
|
-
return candidate;
|
|
50
|
-
}
|
|
33
|
+
function resolveSpecweaveRoot() {
|
|
34
|
+
// Strategy 1: Walk up from this file's location
|
|
35
|
+
const fromFile = findSpecweaveRoot(__dirname);
|
|
36
|
+
if (fromFile)
|
|
37
|
+
return fromFile;
|
|
38
|
+
// Strategy 2: Check known installed marketplace location
|
|
39
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
40
|
+
const installedMarketplace = path.join(home, '.claude/plugins/marketplaces/specweave');
|
|
41
|
+
if (fs.existsSync(path.join(installedMarketplace, '.claude-plugin/marketplace.json'))) {
|
|
42
|
+
return installedMarketplace;
|
|
51
43
|
}
|
|
52
44
|
return null;
|
|
53
45
|
}
|
|
@@ -71,120 +63,11 @@ function getAvailablePlugins(marketplacePath) {
|
|
|
71
63
|
return [];
|
|
72
64
|
}
|
|
73
65
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Read vskill.lock from the current directory.
|
|
76
|
-
*/
|
|
77
|
-
function readLockfile() {
|
|
78
|
-
const lockPath = path.join(process.cwd(), 'vskill.lock');
|
|
79
|
-
if (!fs.existsSync(lockPath))
|
|
80
|
-
return null;
|
|
81
|
-
try {
|
|
82
|
-
const raw = fs.readFileSync(lockPath, 'utf-8');
|
|
83
|
-
return JSON.parse(raw);
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Write vskill.lock to the current directory.
|
|
91
|
-
*/
|
|
92
|
-
function writeLockfile(lock) {
|
|
93
|
-
lock.updatedAt = new Date().toISOString();
|
|
94
|
-
const lockPath = path.join(process.cwd(), 'vskill.lock');
|
|
95
|
-
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Ensure a lockfile exists, creating one if needed.
|
|
99
|
-
*/
|
|
100
|
-
function ensureLockfile() {
|
|
101
|
-
const existing = readLockfile();
|
|
102
|
-
if (existing)
|
|
103
|
-
return existing;
|
|
104
|
-
const lock = {
|
|
105
|
-
version: 1,
|
|
106
|
-
agents: [],
|
|
107
|
-
skills: {},
|
|
108
|
-
createdAt: new Date().toISOString(),
|
|
109
|
-
updatedAt: new Date().toISOString(),
|
|
110
|
-
};
|
|
111
|
-
writeLockfile(lock);
|
|
112
|
-
return lock;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Compute the content hash for a plugin's source directory.
|
|
116
|
-
* Walks the plugin directory and hashes all readable file contents.
|
|
117
|
-
*/
|
|
118
|
-
function computePluginHash(pluginDir) {
|
|
119
|
-
if (!fs.existsSync(pluginDir))
|
|
120
|
-
return '';
|
|
121
|
-
const hash = createHash('sha256');
|
|
122
|
-
try {
|
|
123
|
-
const files = fs.readdirSync(pluginDir, { recursive: true });
|
|
124
|
-
for (const file of files.sort()) {
|
|
125
|
-
const fullPath = path.join(pluginDir, file);
|
|
126
|
-
try {
|
|
127
|
-
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
128
|
-
hash.update(content);
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
// Skip unreadable files (directories, binary, etc.)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
// Directory not readable
|
|
137
|
-
}
|
|
138
|
-
return hash.digest('hex').slice(0, 12);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Install a single plugin via vskill CLI (shell-out).
|
|
142
|
-
*/
|
|
143
|
-
function installPluginViaVskill(pluginName, pluginDirBase, _options) {
|
|
144
|
-
// Shell out to vskill add with --plugin and --pluginDir flags
|
|
145
|
-
const args = [
|
|
146
|
-
'add',
|
|
147
|
-
'specweave', // source identifier
|
|
148
|
-
'--plugin', pluginName,
|
|
149
|
-
'--pluginDir', pluginDirBase,
|
|
150
|
-
// Always --force: these are first-party plugins from specweave's own repo.
|
|
151
|
-
// The security scanner is meant for untrusted third-party plugins.
|
|
152
|
-
'--force',
|
|
153
|
-
];
|
|
154
|
-
// Try to find vskill CLI
|
|
155
|
-
const vskillPaths = [
|
|
156
|
-
// In monorepo (development)
|
|
157
|
-
path.resolve(__dirname, '../../../../../vskill/dist/index.js'),
|
|
158
|
-
// Fallback: global vskill
|
|
159
|
-
'vskill',
|
|
160
|
-
];
|
|
161
|
-
let result = { stdout: '', stderr: '', exitCode: 1, success: false };
|
|
162
|
-
for (const vskillPath of vskillPaths) {
|
|
163
|
-
if (vskillPath === 'vskill') {
|
|
164
|
-
result = execFileNoThrowSync('vskill', args) ?? result;
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
result = execFileNoThrowSync('node', [vskillPath, ...args]) ?? result;
|
|
168
|
-
}
|
|
169
|
-
if (result.success)
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
// Compute hash for lockfile
|
|
173
|
-
const marketplaceJsonDir = path.dirname(pluginDirBase);
|
|
174
|
-
const marketplace = getAvailablePlugins(path.join(marketplaceJsonDir, '.claude-plugin/marketplace.json'));
|
|
175
|
-
const plugin = marketplace.find(p => p.name === pluginName);
|
|
176
|
-
const pluginSourceDir = plugin?.source
|
|
177
|
-
? path.resolve(pluginDirBase, '..', plugin.source)
|
|
178
|
-
: pluginDirBase;
|
|
179
|
-
const sha = computePluginHash(pluginSourceDir);
|
|
180
|
-
const error = result?.success ? undefined : (result?.stderr || result?.stdout || 'Unknown error').trim();
|
|
181
|
-
return { success: result?.success ?? false, sha, error };
|
|
182
|
-
}
|
|
183
66
|
// ---------------------------------------------------------------------------
|
|
184
67
|
// Main command
|
|
185
68
|
// ---------------------------------------------------------------------------
|
|
186
69
|
/**
|
|
187
|
-
* Refresh plugins using
|
|
70
|
+
* Refresh plugins using inline copier.
|
|
188
71
|
*
|
|
189
72
|
* Default (lazy mode): installs only core `sw` plugin.
|
|
190
73
|
* With --all flag: installs all plugins from marketplace.json.
|
|
@@ -192,17 +75,17 @@ function installPluginViaVskill(pluginName, pluginDirBase, _options) {
|
|
|
192
75
|
*/
|
|
193
76
|
export async function refreshPluginsCommand(options = {}) {
|
|
194
77
|
const lazyMode = !(options.all ?? false);
|
|
195
|
-
console.log(chalk.blue.bold('\n SpecWeave Plugin Refresh
|
|
78
|
+
console.log(chalk.blue.bold('\n SpecWeave Plugin Refresh'));
|
|
196
79
|
console.log(chalk.blue.bold(` Mode: ${lazyMode ? 'lazy (core only)' : 'all plugins'}\n`));
|
|
197
|
-
// Step 1: Find
|
|
198
|
-
const
|
|
199
|
-
if (!
|
|
200
|
-
console.log(chalk.yellow('Could not find
|
|
201
|
-
console.log(chalk.gray('
|
|
80
|
+
// Step 1: Find specweave root
|
|
81
|
+
const specweaveRoot = resolveSpecweaveRoot();
|
|
82
|
+
if (!specweaveRoot) {
|
|
83
|
+
console.log(chalk.yellow('Could not find specweave installation'));
|
|
84
|
+
console.log(chalk.gray(' Ensure specweave is installed globally or run from the specweave project.'));
|
|
202
85
|
return;
|
|
203
86
|
}
|
|
204
|
-
const
|
|
205
|
-
logger.debug(`Found
|
|
87
|
+
const marketplacePath = path.join(specweaveRoot, '.claude-plugin', 'marketplace.json');
|
|
88
|
+
logger.debug(`Found specweave root at ${specweaveRoot}`);
|
|
206
89
|
// Step 2: Read available plugins
|
|
207
90
|
const allPlugins = getAvailablePlugins(marketplacePath);
|
|
208
91
|
if (allPlugins.length === 0) {
|
|
@@ -214,56 +97,34 @@ export async function refreshPluginsCommand(options = {}) {
|
|
|
214
97
|
const pluginsToProcess = lazyMode
|
|
215
98
|
? allPlugins.filter(p => CORE_PLUGINS.includes(p.name))
|
|
216
99
|
: allPlugins;
|
|
217
|
-
// Step 4:
|
|
218
|
-
const lock = ensureLockfile();
|
|
219
|
-
// Step 5: Process each plugin
|
|
100
|
+
// Step 4: Process each plugin
|
|
220
101
|
let installed = 0;
|
|
221
102
|
let skipped = 0;
|
|
222
103
|
let failed = 0;
|
|
223
104
|
for (const plugin of pluginsToProcess) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (existingEntry && existingEntry.sha === currentHash && !options.force) {
|
|
105
|
+
console.log(chalk.blue(` Installing ${plugin.name}...`));
|
|
106
|
+
const result = copyPlugin(plugin.name, specweaveRoot, {
|
|
107
|
+
force: options.force,
|
|
108
|
+
});
|
|
109
|
+
if (result.success && result.skipped) {
|
|
230
110
|
if (options.verbose) {
|
|
231
111
|
console.log(chalk.gray(` - ${plugin.name}: unchanged (skipped)`));
|
|
232
112
|
}
|
|
233
113
|
skipped++;
|
|
234
|
-
continue;
|
|
235
114
|
}
|
|
236
|
-
|
|
237
|
-
console.log(chalk.blue(` Installing ${plugin.name}...`));
|
|
238
|
-
const result = installPluginViaVskill(plugin.name, marketplaceDir, options);
|
|
239
|
-
if (result.success) {
|
|
115
|
+
else if (result.success) {
|
|
240
116
|
installed++;
|
|
241
117
|
console.log(chalk.green(` + ${plugin.name} installed`));
|
|
242
|
-
// Update lockfile entry
|
|
243
|
-
lock.skills[plugin.name] = {
|
|
244
|
-
version: plugin.version,
|
|
245
|
-
sha: currentHash || result.sha,
|
|
246
|
-
tier: 'SCANNED',
|
|
247
|
-
installedAt: new Date().toISOString(),
|
|
248
|
-
source: `local:specweave`,
|
|
249
|
-
};
|
|
250
118
|
}
|
|
251
119
|
else {
|
|
252
120
|
failed++;
|
|
253
121
|
console.log(chalk.red(` x ${plugin.name} failed`));
|
|
254
122
|
if (result.error) {
|
|
255
|
-
|
|
256
|
-
const lines = result.error.split('\n').filter(l => l.trim());
|
|
257
|
-
const lastLines = lines.slice(-3);
|
|
258
|
-
for (const line of lastLines) {
|
|
259
|
-
console.log(chalk.gray(` ${line}`));
|
|
260
|
-
}
|
|
123
|
+
console.log(chalk.gray(` ${result.error}`));
|
|
261
124
|
}
|
|
262
125
|
}
|
|
263
126
|
}
|
|
264
|
-
// Step
|
|
265
|
-
writeLockfile(lock);
|
|
266
|
-
// Step 7: Summary
|
|
127
|
+
// Step 5: Summary
|
|
267
128
|
console.log('');
|
|
268
129
|
console.log(chalk.blue.bold(' Summary'));
|
|
269
130
|
console.log(chalk.green(` Installed: ${installed}`));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-plugins.js","sourceRoot":"","sources":["../../../../src/cli/commands/refresh-plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"refresh-plugins.js","sourceRoot":"","sources":["../../../../src/cli/commands/refresh-plugins.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plugin installation for Claude Code
|
|
3
|
-
* Uses
|
|
3
|
+
* Uses inline copier for first-party plugin installation (no vskill dependency).
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
6
|
* Options for plugin installation
|
|
@@ -23,13 +23,13 @@ export interface PluginInstallResult {
|
|
|
23
23
|
marketplaceOnly?: boolean;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Install SpecWeave plugins via
|
|
26
|
+
* Install SpecWeave plugins via inline copier
|
|
27
27
|
*
|
|
28
28
|
* By default (lazyMode=true), only the core plugin (sw) is installed.
|
|
29
29
|
* Other plugins are loaded on-demand based on keywords detected by
|
|
30
30
|
* the detect-intent command in the user-prompt-submit hook.
|
|
31
31
|
*
|
|
32
|
-
* With lazyMode=false, all plugins are installed
|
|
32
|
+
* With lazyMode=false, all plugins are installed.
|
|
33
33
|
*
|
|
34
34
|
* @param options - Installation options
|
|
35
35
|
* @returns Installation result
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-installer.d.ts","sourceRoot":"","sources":["../../../../../src/cli/helpers/init/plugin-installer.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"plugin-installer.d.ts","sourceRoot":"","sources":["../../../../../src/cli/helpers/init/plugin-installer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsKnG"}
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plugin installation for Claude Code
|
|
3
|
-
* Uses
|
|
3
|
+
* Uses inline copier for first-party plugin installation (no vskill dependency).
|
|
4
4
|
*/
|
|
5
5
|
import * as fs from '../../../utils/fs-native.js';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import ora from 'ora';
|
|
8
|
-
import { execFileNoThrowSync } from '../../../utils/execFileNoThrow.js';
|
|
9
8
|
import { detectClaudeCli, getClaudeCliDiagnostic, getClaudeCliSuggestions } from '../../../utils/claude-cli-detector.js';
|
|
10
9
|
import { findSourceDir } from './path-utils.js';
|
|
11
10
|
import { cleanupStalePlugins } from '../../../utils/cleanup-stale-plugins.js';
|
|
12
11
|
import { enablePluginsInSettings } from './claude-plugin-enabler.js';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return _resolveVskillPath(__dirname);
|
|
17
|
-
}
|
|
18
|
-
/** Resolve specweave source directory */
|
|
19
|
-
function resolveSpecweavePluginDir() {
|
|
20
|
-
return resolveSpecweaveDir(__dirname);
|
|
21
|
-
}
|
|
12
|
+
import { copyPlugin, findSpecweaveRoot } from '../../../utils/plugin-copier.js';
|
|
13
|
+
import { getDirname } from '../../../utils/esm-helpers.js';
|
|
14
|
+
const __dirname = getDirname(import.meta.url);
|
|
22
15
|
/**
|
|
23
|
-
* Install SpecWeave plugins via
|
|
16
|
+
* Install SpecWeave plugins via inline copier
|
|
24
17
|
*
|
|
25
18
|
* By default (lazyMode=true), only the core plugin (sw) is installed.
|
|
26
19
|
* Other plugins are loaded on-demand based on keywords detected by
|
|
27
20
|
* the detect-intent command in the user-prompt-submit hook.
|
|
28
21
|
*
|
|
29
|
-
* With lazyMode=false, all plugins are installed
|
|
22
|
+
* With lazyMode=false, all plugins are installed.
|
|
30
23
|
*
|
|
31
24
|
* @param options - Installation options
|
|
32
25
|
* @returns Installation result
|
|
@@ -37,17 +30,12 @@ export async function installAllPlugins(options) {
|
|
|
37
30
|
// Pre-flight check: Is Claude CLI available?
|
|
38
31
|
const claudeStatus = detectClaudeCli();
|
|
39
32
|
if (!claudeStatus.available) {
|
|
40
|
-
// Claude CLI NOT working - cannot install plugins
|
|
41
|
-
// Note: v1.0.170+ removed the fallback that wrote directly to known_marketplaces.json
|
|
42
|
-
// because Claude CLI is now bundled with Claude Code and should always be available.
|
|
43
|
-
// If it's not, show clear diagnostics so user can fix the underlying issue.
|
|
44
33
|
const diagnostic = getClaudeCliDiagnostic(claudeStatus);
|
|
45
34
|
const suggestions = getClaudeCliSuggestions(claudeStatus);
|
|
46
35
|
spinner.warn(diagnostic);
|
|
47
36
|
console.log('');
|
|
48
37
|
console.log(chalk.yellow.bold('⚠️ Claude Code CLI Issue Detected'));
|
|
49
38
|
console.log('');
|
|
50
|
-
// Show detailed diagnostic info
|
|
51
39
|
if (claudeStatus.commandExists) {
|
|
52
40
|
console.log(chalk.white('Found command in PATH, but verification failed:'));
|
|
53
41
|
console.log('');
|
|
@@ -70,14 +58,12 @@ export async function installAllPlugins(options) {
|
|
|
70
58
|
console.log(chalk.white('Claude CLI not found in PATH'));
|
|
71
59
|
}
|
|
72
60
|
console.log('');
|
|
73
|
-
// Show actionable suggestions
|
|
74
61
|
console.log(chalk.cyan('💡 How to fix:'));
|
|
75
62
|
console.log('');
|
|
76
63
|
suggestions.forEach(suggestion => {
|
|
77
64
|
console.log(chalk.gray(` ${suggestion}`));
|
|
78
65
|
});
|
|
79
66
|
console.log('');
|
|
80
|
-
// Show alternatives if CLI not found
|
|
81
67
|
if (claudeStatus.error === 'command_not_found') {
|
|
82
68
|
console.log(chalk.cyan('Alternative Options:'));
|
|
83
69
|
console.log('');
|
|
@@ -94,7 +80,7 @@ export async function installAllPlugins(options) {
|
|
|
94
80
|
}
|
|
95
81
|
return { success: false, successCount: 0, failCount: 0, failedPlugins: [] };
|
|
96
82
|
}
|
|
97
|
-
// Claude CLI available - proceed with
|
|
83
|
+
// Claude CLI available - proceed with inline copier installation
|
|
98
84
|
try {
|
|
99
85
|
// Load marketplace.json to get ALL available plugins
|
|
100
86
|
spinner.start('Loading available plugins...');
|
|
@@ -123,13 +109,11 @@ export async function installAllPlugins(options) {
|
|
|
123
109
|
spinner.succeed('No stale plugins found');
|
|
124
110
|
}
|
|
125
111
|
// LAZY LOADING MODE (default)
|
|
126
|
-
// Install only core plugin via vskill, cache the rest for on-demand loading
|
|
127
112
|
if (lazyMode) {
|
|
128
|
-
return await installLazyMode(allPlugins, spinner
|
|
113
|
+
return await installLazyMode(allPlugins, spinner);
|
|
129
114
|
}
|
|
130
115
|
// FULL MODE (--full flag)
|
|
131
|
-
|
|
132
|
-
const result = await installPluginsWithRetry(allPlugins, spinner);
|
|
116
|
+
const result = await installPluginsFullMode(allPlugins, spinner, forceRefresh);
|
|
133
117
|
// Enable installed plugins in Claude settings
|
|
134
118
|
if (result.installedPlugins.length > 0) {
|
|
135
119
|
spinner.start('Enabling plugins in Claude Code...');
|
|
@@ -169,73 +153,55 @@ export async function installAllPlugins(options) {
|
|
|
169
153
|
};
|
|
170
154
|
}
|
|
171
155
|
catch (error) {
|
|
172
|
-
// Installation failed - provide helpful diagnostics
|
|
173
156
|
spinner.warn('Could not auto-install plugins');
|
|
174
157
|
console.log('');
|
|
175
158
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
176
159
|
if (errorMessage.includes('not found') || errorMessage.includes('ENOENT')) {
|
|
177
160
|
console.log(chalk.yellow(' Reason: Plugin source not found'));
|
|
178
|
-
console.log(chalk.gray(' Try: vskill add --plugin sw --plugin-dir <specweave-dir>'));
|
|
179
161
|
}
|
|
180
162
|
else if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
|
|
181
163
|
console.log(chalk.yellow(' Reason: Permission denied'));
|
|
182
164
|
console.log(chalk.gray(' Check file permissions or run with appropriate access'));
|
|
183
165
|
}
|
|
184
|
-
else if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('network')) {
|
|
185
|
-
console.log(chalk.yellow(' Reason: Network error'));
|
|
186
|
-
console.log(chalk.gray(' Check internet connection and try again'));
|
|
187
|
-
}
|
|
188
166
|
else if (process.env.DEBUG) {
|
|
189
167
|
console.log(chalk.gray(` Error: ${errorMessage}`));
|
|
190
168
|
}
|
|
191
169
|
console.log('');
|
|
192
|
-
console.log(chalk.cyan('Manual installation:'));
|
|
193
|
-
console.log(chalk.white(' vskill add --plugin sw --plugin-dir <specweave-dir>'));
|
|
194
|
-
console.log('');
|
|
195
170
|
return { success: false, successCount: 0, failCount: 0, failedPlugins: [] };
|
|
196
171
|
}
|
|
197
172
|
}
|
|
198
173
|
/**
|
|
199
|
-
* Install in lazy mode - core plugin only
|
|
200
|
-
*
|
|
201
|
-
* Installs:
|
|
202
|
-
* - sw (core SpecWeave framework) via vskill add
|
|
203
|
-
*
|
|
204
|
-
* Other SpecWeave plugins are loaded on-demand via detect-intent hook.
|
|
205
|
-
* Official plugins (context7, playwright) are optional user installs.
|
|
174
|
+
* Install in lazy mode - core plugin only, load others on-demand
|
|
206
175
|
*/
|
|
207
|
-
async function installLazyMode(_allPlugins, spinner
|
|
208
|
-
// Essential plugins to install (v1.0.240: only core SW plugin)
|
|
176
|
+
async function installLazyMode(_allPlugins, spinner) {
|
|
209
177
|
const essentialPlugins = [
|
|
210
178
|
{ name: 'sw', marketplace: 'specweave', description: 'Core SpecWeave framework' },
|
|
211
179
|
];
|
|
212
180
|
let installedCount = 0;
|
|
213
181
|
const failedPlugins = [];
|
|
214
182
|
const successfullyInstalled = [];
|
|
215
|
-
spinner.start('Installing essential plugins
|
|
183
|
+
spinner.start('Installing essential plugins...');
|
|
216
184
|
spinner.stop();
|
|
217
|
-
const
|
|
218
|
-
|
|
185
|
+
const specweaveRoot = findSpecweaveRoot(__dirname);
|
|
186
|
+
if (!specweaveRoot) {
|
|
187
|
+
console.log(chalk.yellow(' Could not find specweave root directory'));
|
|
188
|
+
return { success: false, successCount: 0, failCount: 1, failedPlugins: ['sw'] };
|
|
189
|
+
}
|
|
219
190
|
for (const plugin of essentialPlugins) {
|
|
220
|
-
console.log(chalk.blue(` Installing ${plugin.name}
|
|
221
|
-
const result =
|
|
222
|
-
if (result.success) {
|
|
223
|
-
|
|
224
|
-
if (result.scanOutput) {
|
|
225
|
-
console.log(chalk.gray(` Security scan: ${result.scanOutput}`));
|
|
226
|
-
}
|
|
227
|
-
console.log(chalk.green(` ${plugin.name} installed via vskill`));
|
|
191
|
+
console.log(chalk.blue(` Installing ${plugin.name}...`));
|
|
192
|
+
const result = copyPlugin(plugin.name, specweaveRoot, { force: true });
|
|
193
|
+
if (result.success && !result.skipped) {
|
|
194
|
+
console.log(chalk.green(` ${plugin.name} installed`));
|
|
228
195
|
installedCount++;
|
|
229
196
|
successfullyInstalled.push(plugin.name);
|
|
230
197
|
}
|
|
231
|
-
else if (result.
|
|
198
|
+
else if (result.success && result.skipped) {
|
|
232
199
|
console.log(chalk.gray(` ${plugin.name} (already installed)`));
|
|
233
200
|
installedCount++;
|
|
234
201
|
successfullyInstalled.push(plugin.name);
|
|
235
202
|
}
|
|
236
203
|
else {
|
|
237
|
-
console.log(chalk.yellow(` ${plugin.name} failed`));
|
|
238
|
-
console.log(chalk.gray(` Install manually: vskill add --plugin ${plugin.name} --plugin-dir ${pluginDir}`));
|
|
204
|
+
console.log(chalk.yellow(` ${plugin.name} failed: ${result.error || 'unknown'}`));
|
|
239
205
|
failedPlugins.push(plugin.name);
|
|
240
206
|
}
|
|
241
207
|
}
|
|
@@ -277,62 +243,30 @@ async function installLazyMode(_allPlugins, spinner, _dirname) {
|
|
|
277
243
|
};
|
|
278
244
|
}
|
|
279
245
|
/**
|
|
280
|
-
* Install
|
|
281
|
-
*
|
|
282
|
-
* Uses execFileNoThrowSync to invoke the vskill CLI with
|
|
283
|
-
* --plugin and --plugin-dir flags for local plugin directory installation.
|
|
246
|
+
* Install all plugins (full mode) via inline copier
|
|
284
247
|
*/
|
|
285
|
-
function
|
|
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
|
|
312
|
-
*/
|
|
313
|
-
async function installPluginsWithRetry(plugins, spinner) {
|
|
248
|
+
async function installPluginsFullMode(plugins, spinner, forceRefresh) {
|
|
314
249
|
let successCount = 0;
|
|
315
250
|
let failCount = 0;
|
|
316
251
|
const failedPlugins = [];
|
|
317
252
|
const installedPlugins = [];
|
|
318
|
-
const
|
|
319
|
-
|
|
253
|
+
const specweaveRoot = findSpecweaveRoot(__dirname);
|
|
254
|
+
if (!specweaveRoot) {
|
|
255
|
+
return { successCount: 0, failCount: plugins.length, failedPlugins: plugins.map(p => p.name), installedPlugins: [] };
|
|
256
|
+
}
|
|
320
257
|
for (const plugin of plugins) {
|
|
321
258
|
const pluginName = plugin.name;
|
|
322
|
-
spinner.start(`Installing ${pluginName}
|
|
323
|
-
const result =
|
|
259
|
+
spinner.start(`Installing ${pluginName}...`);
|
|
260
|
+
const result = copyPlugin(pluginName, specweaveRoot, { force: forceRefresh });
|
|
324
261
|
if (result.success) {
|
|
325
262
|
successCount++;
|
|
326
263
|
installedPlugins.push(pluginName);
|
|
327
|
-
if (result.scanOutput) {
|
|
328
|
-
console.log(chalk.gray(` Security scan: ${result.scanOutput}`));
|
|
329
|
-
}
|
|
330
264
|
spinner.succeed(`${pluginName} installed`);
|
|
331
265
|
}
|
|
332
266
|
else {
|
|
333
267
|
failCount++;
|
|
334
268
|
failedPlugins.push(pluginName);
|
|
335
|
-
spinner.warn(`${pluginName} failed
|
|
269
|
+
spinner.warn(`${pluginName} failed: ${result.error || 'unknown'}`);
|
|
336
270
|
}
|
|
337
271
|
}
|
|
338
272
|
return { successCount, failCount, failedPlugins, installedPlugins };
|