specweave 1.0.472 → 1.0.474
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 +1 -1
- package/bin/specweave.js +21 -0
- package/dist/src/cli/commands/branch-name.d.ts +17 -0
- package/dist/src/cli/commands/branch-name.d.ts.map +1 -0
- package/dist/src/cli/commands/branch-name.js +47 -0
- package/dist/src/cli/commands/branch-name.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +0 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts +6 -13
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +14 -38
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/commands/sync-health.d.ts +34 -0
- package/dist/src/cli/commands/sync-health.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-health.js +175 -0
- package/dist/src/cli/commands/sync-health.js.map +1 -0
- package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-setup.js +15 -1
- package/dist/src/cli/commands/sync-setup.js.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.d.ts +10 -10
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/plugin-installer.js +52 -197
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/cicd/branch-utils.d.ts +29 -0
- package/dist/src/core/cicd/branch-utils.d.ts.map +1 -0
- package/dist/src/core/cicd/branch-utils.js +35 -0
- package/dist/src/core/cicd/branch-utils.js.map +1 -0
- package/dist/src/core/cicd/config-loader.d.ts.map +1 -1
- package/dist/src/core/cicd/config-loader.js.map +1 -1
- package/dist/src/core/config/types.d.ts +2 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js +1 -0
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +18 -47
- package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
- package/dist/src/core/lazy-loading/llm-plugin-detector.js +41 -409
- package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/utils/plugin-copier.d.ts +14 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +140 -1
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/user-prompt-submit.sh +14 -291
- package/plugins/specweave/skills/team-lead/SKILL.md +43 -39
- package/plugins/specweave/skills/team-merge/SKILL.md +51 -33
|
@@ -17,7 +17,6 @@ import * as os from 'os';
|
|
|
17
17
|
import * as fs from 'fs';
|
|
18
18
|
import * as path from 'path';
|
|
19
19
|
import { consoleLogger as logger } from '../../utils/logger.js';
|
|
20
|
-
import { resolveVskillPath as _resolveVskillPath, resolveSpecweaveDir as _resolveSpecweaveDir } from '../../utils/vskill-resolver.js';
|
|
21
20
|
import { getProjectRoot } from '../../utils/find-project-root.js';
|
|
22
21
|
// IMPORTANT: Use canonical Claude CLI detection from utils (handles shell functions, nvm, etc.)
|
|
23
22
|
import { detectClaudeCli, getCleanEnv } from '../../utils/claude-cli-detector.js';
|
|
@@ -127,47 +126,13 @@ export const SPECWEAVE_PLUGINS = [
|
|
|
127
126
|
'sw-docs', // SpecWeave docs
|
|
128
127
|
'sw-media', // AI image/video generation
|
|
129
128
|
];
|
|
130
|
-
/**
|
|
131
|
-
* Domain skill plugins in the vskill marketplace.
|
|
132
|
-
*
|
|
133
|
-
* Each category is a standalone plugin (e.g., `frontend@vskill`, `backend@vskill`).
|
|
134
|
-
* Skills are invoked as `plugin:skill` (e.g., `frontend:nextjs`, `backend:dotnet`).
|
|
135
|
-
*
|
|
136
|
-
* v2.1.0: Split from monolithic `vs` plugin into per-category plugins for granularity.
|
|
137
|
-
*/
|
|
138
|
-
export const VSKILL_PLUGINS = [
|
|
139
|
-
'mobile', // React Native, iOS, Android, Expo, app store publishing
|
|
140
|
-
'skills', // Skill discovery — find and install skills
|
|
141
|
-
];
|
|
142
|
-
/**
|
|
143
|
-
* Plugins that are planned but not yet available (no directory on disk).
|
|
144
|
-
* Kept here so the LLM prompt can inform users these are not yet installable.
|
|
145
|
-
* v1.0.397: Removed from VSKILL_PLUGINS to prevent suggesting non-existent plugins.
|
|
146
|
-
*/
|
|
147
|
-
export const VSKILL_PLUGINS_PLANNED = [
|
|
148
|
-
'frontend', // React, Vue, Angular, Next.js, UI components [NOT YET AVAILABLE]
|
|
149
|
-
'backend', // Java Spring Boot, Rust Axum [NOT YET AVAILABLE]
|
|
150
|
-
'testing', // Jest, Vitest, Playwright, E2E [NOT YET AVAILABLE]
|
|
151
|
-
'infra', // Terraform, AWS, Azure, GCP, Docker, CI/CD [NOT YET AVAILABLE]
|
|
152
|
-
'k8s', // K8s, Helm, pods, deployments [NOT YET AVAILABLE]
|
|
153
|
-
'payments', // Stripe, PayPal, checkout [NOT YET AVAILABLE]
|
|
154
|
-
'ml', // Machine learning, PyTorch, TensorFlow [NOT YET AVAILABLE]
|
|
155
|
-
'kafka', // Apache Kafka, event streaming, n8n [NOT YET AVAILABLE]
|
|
156
|
-
'confluent', // Confluent Cloud, Schema Registry, ksqlDB [NOT YET AVAILABLE]
|
|
157
|
-
'cost', // Cloud cost optimization [NOT YET AVAILABLE]
|
|
158
|
-
'docs', // Extended documentation [NOT YET AVAILABLE]
|
|
159
|
-
'security', // Security scanning and hardening [NOT YET AVAILABLE]
|
|
160
|
-
'blockchain', // Web3, Solidity, smart contracts [NOT YET AVAILABLE]
|
|
161
|
-
];
|
|
162
|
-
/** @deprecated Use VSKILL_PLUGINS */
|
|
163
|
-
export const VSKILL_CATEGORIES = VSKILL_PLUGINS;
|
|
164
129
|
/**
|
|
165
130
|
* Combined list of all known plugins for validation.
|
|
166
|
-
*
|
|
131
|
+
* Only specweave plugins — vskill marketplace plugins have been removed (v1.0.533).
|
|
167
132
|
*/
|
|
168
|
-
export const ALL_KNOWN_PLUGINS = [...SPECWEAVE_PLUGINS
|
|
133
|
+
export const ALL_KNOWN_PLUGINS = [...SPECWEAVE_PLUGINS];
|
|
169
134
|
/**
|
|
170
|
-
* All valid plugins — specweave plugins
|
|
135
|
+
* All valid plugins — only specweave plugins (v1.0.533: vskill removed).
|
|
171
136
|
*/
|
|
172
137
|
export const ALL_VALID_PLUGINS = ALL_KNOWN_PLUGINS;
|
|
173
138
|
/**
|
|
@@ -177,27 +142,11 @@ export function isSpecWeavePlugin(plugin) {
|
|
|
177
142
|
return SPECWEAVE_PLUGINS.includes(plugin);
|
|
178
143
|
}
|
|
179
144
|
/**
|
|
180
|
-
* Check if a plugin is
|
|
181
|
-
*/
|
|
182
|
-
export function isVskillPlugin(plugin) {
|
|
183
|
-
return VSKILL_PLUGINS.includes(plugin);
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Check if a plugin is any known plugin (specweave or vskill).
|
|
145
|
+
* Check if a plugin is any known plugin (specweave only, v1.0.533).
|
|
187
146
|
*/
|
|
188
147
|
export function isKnownPlugin(plugin) {
|
|
189
148
|
return ALL_KNOWN_PLUGINS.includes(plugin);
|
|
190
149
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Get the marketplace name for a plugin.
|
|
193
|
-
* Returns 'specweave' for sw-* plugins, 'vskill' for domain skills.
|
|
194
|
-
*/
|
|
195
|
-
export function getPluginMarketplace(plugin) {
|
|
196
|
-
if (isVskillPlugin(plugin)) {
|
|
197
|
-
return 'vskill';
|
|
198
|
-
}
|
|
199
|
-
return 'specweave';
|
|
200
|
-
}
|
|
201
150
|
// Cache the CLI detection result for performance (detect once per process)
|
|
202
151
|
let cachedCliStatus = null;
|
|
203
152
|
/**
|
|
@@ -278,30 +227,27 @@ export function isClaudeCliAvailable() {
|
|
|
278
227
|
*/
|
|
279
228
|
function buildDetectionPrompt() {
|
|
280
229
|
return `You detect which plugins to load based on the user's prompt.
|
|
281
|
-
Return specweave (sw-*)
|
|
230
|
+
Return specweave (sw-*) plugin names ONLY.
|
|
282
231
|
|
|
283
232
|
DETECTION RULES:
|
|
284
|
-
1. EXPLICIT
|
|
285
|
-
2.
|
|
286
|
-
3.
|
|
287
|
-
4.
|
|
233
|
+
1. EXPLICIT mention of integrations - "GitHub sync" → sw-github, "JIRA" → sw-jira
|
|
234
|
+
2. Questions/discussions → ZERO plugins
|
|
235
|
+
3. ONLY suggest @specweave plugins (sw-*)
|
|
236
|
+
4. Domain plugins (frontend, backend, testing, etc.) are NOT available — do NOT suggest them
|
|
288
237
|
|
|
289
238
|
OUTPUT FORMAT (JSON only):
|
|
290
|
-
{"plugins":["
|
|
239
|
+
{"plugins":["sw-github"],"confidence":0.9,"reasoning":"one-line"}
|
|
291
240
|
|
|
292
241
|
═══════════════════════════════════════════════════════════════
|
|
293
|
-
PLUGINS
|
|
242
|
+
AVAILABLE PLUGINS (specweave only — sw-*)
|
|
294
243
|
═══════════════════════════════════════════════════════════════
|
|
295
244
|
|
|
296
|
-
|
|
297
|
-
scout: find skill, discover skill, what skills available, search registry, install a skill, recommend skills, browse skills, vskill, skill for, which skill, explore skills (ONLY if asking about finding/discovering skills — NOT for domain work)
|
|
298
|
-
sw-media: AI image generation, AI video generation, Remotion, text-to-image, text-to-video, Imagen, Veo, generate image, generate video, create video, media generation, Pollinations (ONLY if explicit)
|
|
299
|
-
|
|
300
|
-
[NOT YET AVAILABLE — DO NOT suggest these plugins, they are planned but not installable]:
|
|
301
|
-
frontend, backend, testing, infra, k8s, payments, ml, kafka, confluent, security, blockchain
|
|
302
|
-
sw-github: GitHub issues, PRs, Actions, sync
|
|
245
|
+
sw-github: GitHub issues, PRs, Actions, sync (ONLY if explicit)
|
|
303
246
|
sw-jira: JIRA, Atlassian (ONLY if explicit)
|
|
304
247
|
sw-ado: Azure DevOps, work items (ONLY if explicit)
|
|
248
|
+
sw-media: AI image generation, AI video generation, Remotion, text-to-image, text-to-video, Imagen, Veo, generate image, generate video, create video, media generation, Pollinations (ONLY if explicit)
|
|
249
|
+
|
|
250
|
+
DO NOT suggest: frontend, backend, testing, infra, k8s, mobile, skills, payments, ml, kafka, confluent, security, blockchain — these are NOT available as plugins.
|
|
305
251
|
|
|
306
252
|
═══════════════════════════════════════════════════════════════
|
|
307
253
|
INCREMENT RECOMMENDATION (v1.0.241 - DEFAULT: create increment)
|
|
@@ -369,17 +315,14 @@ EXPLICIT OPT-OUT → action: "none":
|
|
|
369
315
|
- "just a quick fix", "without tracking", "already tracking"
|
|
370
316
|
|
|
371
317
|
═══════════════════════════════════════════════════════════════
|
|
372
|
-
EXAMPLES
|
|
318
|
+
EXAMPLES
|
|
373
319
|
═══════════════════════════════════════════════════════════════
|
|
374
320
|
|
|
375
|
-
"
|
|
376
|
-
{"plugins":["
|
|
321
|
+
"Sync our GitHub issues"
|
|
322
|
+
{"plugins":["sw-github"],"confidence":0.95,"reasoning":"GitHub sync→sw-github","increment":{"action":"new","confidence":0.9,"mandatory":false,"suggestedName":"sync-github-issues","reasoning":"GitHub integration work"}}
|
|
377
323
|
|
|
378
324
|
"The auth feature is broken again"
|
|
379
|
-
{"plugins":[],"confidence":0.7,"reasoning":"No specific
|
|
380
|
-
|
|
381
|
-
"Urgent: production mobile app is crashing"
|
|
382
|
-
{"plugins":["mobile"],"confidence":0.9,"reasoning":"Mobile app issue","increment":{"action":"hotfix","confidence":0.95,"mandatory":true,"suggestedName":"mobile-crash-hotfix","reasoning":"Production issue"}}
|
|
325
|
+
{"plugins":[],"confidence":0.7,"reasoning":"No specific integration mentioned","increment":{"action":"reopen","confidence":0.8,"mandatory":false,"relatedKeyword":"auth","reasoning":"Related to previous auth work"}}
|
|
383
326
|
|
|
384
327
|
"Fix typo in README"
|
|
385
328
|
{"plugins":[],"confidence":0.9,"reasoning":"Typo fix","increment":{"action":"small_fix","confidence":0.9,"mandatory":false,"suggestedName":"fix-readme-typo","reasoning":"Trivial 1-line change"}}
|
|
@@ -390,65 +333,6 @@ EXAMPLES (one per action type — keep prompt size minimal)
|
|
|
390
333
|
"Investigate why the API sync keeps failing across multiple services"
|
|
391
334
|
{"plugins":[],"confidence":0.8,"reasoning":"Investigation/debugging work","increment":{"action":"new","confidence":0.85,"mandatory":false,"suggestedName":"investigate-api-sync-failure","reasoning":"Multi-component investigation requiring structured tracking"}}
|
|
392
335
|
|
|
393
|
-
═══════════════════════════════════════════════════════════════
|
|
394
|
-
SKILL INVOCATION (v2.1.0 - tell Claude which plugin:skill to use)
|
|
395
|
-
═══════════════════════════════════════════════════════════════
|
|
396
|
-
|
|
397
|
-
ALSO specify which skill Claude SHOULD invoke for this task.
|
|
398
|
-
Skills use "plugin:skill" format (e.g., "mobile:react-native", "mobile:appstore").
|
|
399
|
-
|
|
400
|
-
"skillInvocation" field with:
|
|
401
|
-
- skill: full skill name as plugin:skill (e.g., "mobile:react-native", "mobile:appstore")
|
|
402
|
-
- reason: why this skill should be used
|
|
403
|
-
- mandatory: true if Claude MUST use this skill, false if optional
|
|
404
|
-
|
|
405
|
-
⚠️ IMPORTANT: DO NOT suggest *-lsp plugins - they are BROKEN in official marketplace!
|
|
406
|
-
LSP is handled separately via boostvolt/claude-code-lsps + ENABLE_LSP_TOOL=1 env var.
|
|
407
|
-
|
|
408
|
-
SKILL CATALOG (use exact plugin:skill names — ONLY suggest skills from AVAILABLE plugins):
|
|
409
|
-
mobile: mobile:appstore, mobile:capacitor, mobile:deep-linking, mobile:expo, mobile:flutter, mobile:jetpack, mobile:react-native, mobile:swiftui, mobile:testing
|
|
410
|
-
skills: skills:scout
|
|
411
|
-
|
|
412
|
-
[NOT YET AVAILABLE — DO NOT suggest these skills]:
|
|
413
|
-
frontend, backend, testing, infra, k8s, ml, kafka, confluent, payments, docs, cost, security, blockchain
|
|
414
|
-
|
|
415
|
-
SKILL INVOCATION RULES (pick the most specific skill):
|
|
416
|
-
- .NET/C# → backend:dotnet MANDATORY
|
|
417
|
-
- Go/Golang → backend:go MANDATORY
|
|
418
|
-
- Python/FastAPI/Django → backend:python MANDATORY
|
|
419
|
-
- Java/Spring → backend:java-spring MANDATORY
|
|
420
|
-
- Rust → backend:rust MANDATORY
|
|
421
|
-
- Node.js/Express/NestJS → backend:nodejs MANDATORY
|
|
422
|
-
- GraphQL → backend:graphql MANDATORY
|
|
423
|
-
- Next.js → frontend:nextjs MANDATORY
|
|
424
|
-
- React/Vue/Angular → frontend:frontend-core MANDATORY
|
|
425
|
-
- Figma design → frontend:figma MANDATORY
|
|
426
|
-
- ML/AI → ml:engineer MANDATORY
|
|
427
|
-
- Stripe/PayPal → payments:payment-core MANDATORY
|
|
428
|
-
- Unit testing → testing:unit MANDATORY
|
|
429
|
-
- E2E testing → testing:e2e MANDATORY
|
|
430
|
-
- React Native → mobile:react-native MANDATORY
|
|
431
|
-
- Flutter → mobile:flutter MANDATORY
|
|
432
|
-
- SwiftUI/iOS → mobile:swiftui MANDATORY
|
|
433
|
-
- Jetpack/Android → mobile:jetpack MANDATORY
|
|
434
|
-
- Expo → mobile:expo MANDATORY
|
|
435
|
-
- Terraform → infra:terraform MANDATORY
|
|
436
|
-
- AWS → infra:aws MANDATORY
|
|
437
|
-
- Azure → infra:azure MANDATORY
|
|
438
|
-
- GCP → infra:gcp MANDATORY
|
|
439
|
-
- GitHub Actions → infra:github-actions MANDATORY
|
|
440
|
-
- Kubernetes → k8s:manifests recommended
|
|
441
|
-
- Architecture → frontend:architect or relevant architect skill recommended
|
|
442
|
-
- DO NOT suggest *-lsp plugins (broken in marketplace)
|
|
443
|
-
|
|
444
|
-
SKILL EXAMPLES:
|
|
445
|
-
|
|
446
|
-
"Build Spring Boot API with JPA"
|
|
447
|
-
{"plugins":["backend"],"confidence":0.95,"reasoning":"Spring Boot→backend","increment":{"action":"new","confidence":0.9,"mandatory":true,"suggestedName":"spring-boot-api","reasoning":"New API"},"skillInvocation":{"skill":"backend:java-spring","reason":"Spring Boot patterns and JPA","mandatory":true}}
|
|
448
|
-
|
|
449
|
-
"Write unit tests for the auth service"
|
|
450
|
-
{"plugins":["testing"],"confidence":0.95,"reasoning":"Unit testing","increment":{"action":"small_fix","confidence":0.7,"mandatory":false,"reasoning":"Testing extends existing work"},"skillInvocation":{"skill":"testing:unit","reason":"Vitest/Jest patterns and TDD","mandatory":true}}
|
|
451
|
-
|
|
452
336
|
═══════════════════════════════════════════════════════════════
|
|
453
337
|
LSP OPERATION DETECTION (v1.0.198 - unified detection)
|
|
454
338
|
═══════════════════════════════════════════════════════════════
|
|
@@ -470,7 +354,7 @@ LSP EXAMPLES:
|
|
|
470
354
|
{"plugins":[],"confidence":0.95,"reasoning":"Code navigation","lsp":{"needed":true,"operation":"references","language":"typescript","warmupRequired":true}}
|
|
471
355
|
|
|
472
356
|
"Build a React dashboard" (NO LSP needed)
|
|
473
|
-
{"plugins":[
|
|
357
|
+
{"plugins":[],"confidence":0.95,"reasoning":"React development"}`;
|
|
474
358
|
}
|
|
475
359
|
/**
|
|
476
360
|
* Execute Claude CLI command safely
|
|
@@ -849,30 +733,6 @@ Which plugins should be loaded?`;
|
|
|
849
733
|
return createFailureResult(startTime, `Detection failed: ${errorMsg}`);
|
|
850
734
|
}
|
|
851
735
|
}
|
|
852
|
-
/**
|
|
853
|
-
* Check if a plugin is already installed via vskill lockfile
|
|
854
|
-
*
|
|
855
|
-
* Reads vskill.lock from cwd and checks if the plugin has an entry.
|
|
856
|
-
* This provides a fast-path to skip installation when plugin is
|
|
857
|
-
* already present with a matching hash.
|
|
858
|
-
*
|
|
859
|
-
* @param pluginName - Name of the plugin to check
|
|
860
|
-
* @returns true if plugin is in the lockfile
|
|
861
|
-
*/
|
|
862
|
-
function isPluginInVskillLock(pluginName) {
|
|
863
|
-
try {
|
|
864
|
-
const lockPath = path.join(getProjectRoot(), 'vskill.lock');
|
|
865
|
-
if (!fs.existsSync(lockPath)) {
|
|
866
|
-
return false;
|
|
867
|
-
}
|
|
868
|
-
const content = fs.readFileSync(lockPath, 'utf-8');
|
|
869
|
-
const lock = JSON.parse(content);
|
|
870
|
-
return lock.skills && pluginName in lock.skills;
|
|
871
|
-
}
|
|
872
|
-
catch {
|
|
873
|
-
return false;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
736
|
/** Track which plugins have already been reported this session (daily dedup) */
|
|
877
737
|
const _reportedPlugins = new Set();
|
|
878
738
|
/**
|
|
@@ -900,23 +760,8 @@ async function reportInstallsForPlugin(pluginName) {
|
|
|
900
760
|
.filter(d => d.isDirectory() && fs.existsSync(path.join(skillsDir, d.name, 'SKILL.md')));
|
|
901
761
|
if (skillDirs.length === 0)
|
|
902
762
|
return;
|
|
903
|
-
// Read repoUrl from lockfile
|
|
904
|
-
let repoUrl;
|
|
905
|
-
try {
|
|
906
|
-
const lockPath = path.join(projectRoot, 'vskill.lock');
|
|
907
|
-
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
|
|
908
|
-
const entry = lock.skills?.[pluginName];
|
|
909
|
-
if (entry?.source) {
|
|
910
|
-
// source format: "github:owner/repo#plugin:name" → extract "owner/repo"
|
|
911
|
-
const match = entry.source.match(/github:([^#]+)/);
|
|
912
|
-
if (match)
|
|
913
|
-
repoUrl = match[1];
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
catch { /* best-effort */ }
|
|
917
763
|
const skills = skillDirs.map(d => ({
|
|
918
764
|
skillName: d.name,
|
|
919
|
-
...(repoUrl ? { repoUrl } : {}),
|
|
920
765
|
}));
|
|
921
766
|
const controller = new AbortController();
|
|
922
767
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
@@ -940,202 +785,41 @@ async function reportInstallsForPlugin(pluginName) {
|
|
|
940
785
|
// Silent — install tracking must never block plugin loading
|
|
941
786
|
}
|
|
942
787
|
}
|
|
943
|
-
/** Resolve vskill path from this module's location */
|
|
944
|
-
function resolveVskillCliPath() {
|
|
945
|
-
return _resolveVskillPath(__dirname);
|
|
946
|
-
}
|
|
947
|
-
/** Resolve specweave source directory */
|
|
948
|
-
function resolveSpecweaveDir() {
|
|
949
|
-
return _resolveSpecweaveDir(__dirname);
|
|
950
|
-
}
|
|
951
788
|
/**
|
|
952
|
-
* Install a
|
|
953
|
-
* (v1.0.343: fixed 'add' → 'install' to match vskill CLI)
|
|
789
|
+
* Install a plugin using native Claude plugin system.
|
|
954
790
|
*
|
|
955
|
-
* @
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*/
|
|
959
|
-
async function installSpecweaveLocalPlugin(pluginName, timeout) {
|
|
960
|
-
try {
|
|
961
|
-
const vskillPath = resolveVskillCliPath();
|
|
962
|
-
const pluginDir = resolveSpecweaveDir();
|
|
963
|
-
const result = spawnSync('node', [
|
|
964
|
-
vskillPath,
|
|
965
|
-
'install',
|
|
966
|
-
'--plugin-dir', pluginDir,
|
|
967
|
-
'--plugin', pluginName,
|
|
968
|
-
'--force', // Auto-accept scan results during lazy loading
|
|
969
|
-
'--yes',
|
|
970
|
-
], {
|
|
971
|
-
encoding: 'utf8',
|
|
972
|
-
timeout,
|
|
973
|
-
maxBuffer: 1024 * 1024,
|
|
974
|
-
windowsHide: true,
|
|
975
|
-
cwd: process.cwd(),
|
|
976
|
-
});
|
|
977
|
-
if (result.error) {
|
|
978
|
-
return { success: false, plugin: pluginName, error: `Install error: ${result.error.message}` };
|
|
979
|
-
}
|
|
980
|
-
const stdout = result.stdout || '';
|
|
981
|
-
const stderr = result.stderr || '';
|
|
982
|
-
const combined = `${stdout} ${stderr}`.toLowerCase();
|
|
983
|
-
if (combined.includes('already')) {
|
|
984
|
-
return { success: true, plugin: pluginName, alreadyInstalled: true };
|
|
985
|
-
}
|
|
986
|
-
if (result.status === 0) {
|
|
987
|
-
return { success: true, plugin: pluginName };
|
|
988
|
-
}
|
|
989
|
-
return { success: false, plugin: pluginName, error: stderr || stdout || `Exit code ${result.status}` };
|
|
990
|
-
}
|
|
991
|
-
catch (error) {
|
|
992
|
-
return { success: false, plugin: pluginName, error: `Install failed: ${error}` };
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
/**
|
|
996
|
-
* Install a vskill repo plugin via vskill install --repo
|
|
997
|
-
* (v1.0.343: fixed 'add' → 'install' to match vskill CLI)
|
|
998
|
-
*
|
|
999
|
-
* v2.1.0: Per-category plugins in vskill marketplace (frontend, backend, etc.).
|
|
1000
|
-
* Uses: vskill install --repo anton-abyzov/vskill --plugin <name> --force --yes
|
|
1001
|
-
*
|
|
1002
|
-
* @param pluginName - Name of the vskill plugin (e.g., "frontend", "backend")
|
|
1003
|
-
* @param timeout - Timeout in milliseconds
|
|
1004
|
-
* @returns Installation result
|
|
1005
|
-
*/
|
|
1006
|
-
async function installVskillRepoPlugin(pluginName, timeout, skillFilter) {
|
|
1007
|
-
try {
|
|
1008
|
-
const vskillPath = resolveVskillCliPath();
|
|
1009
|
-
const args = [
|
|
1010
|
-
vskillPath,
|
|
1011
|
-
'install',
|
|
1012
|
-
'--repo', 'anton-abyzov/vskill',
|
|
1013
|
-
'--plugin', pluginName,
|
|
1014
|
-
'--force',
|
|
1015
|
-
'--yes',
|
|
1016
|
-
];
|
|
1017
|
-
// Filter skills within the plugin to only install relevant ones
|
|
1018
|
-
if (skillFilter) {
|
|
1019
|
-
args.push('--only-skills', skillFilter);
|
|
1020
|
-
logger.debug(`Installing ${pluginName} with skill filter: ${skillFilter}`);
|
|
1021
|
-
}
|
|
1022
|
-
const result = spawnSync('node', args, {
|
|
1023
|
-
encoding: 'utf8',
|
|
1024
|
-
timeout,
|
|
1025
|
-
maxBuffer: 1024 * 1024,
|
|
1026
|
-
windowsHide: true,
|
|
1027
|
-
cwd: process.cwd(),
|
|
1028
|
-
});
|
|
1029
|
-
if (result.error) {
|
|
1030
|
-
return { success: false, plugin: pluginName, error: `Install error: ${result.error.message}` };
|
|
1031
|
-
}
|
|
1032
|
-
const stdout = result.stdout || '';
|
|
1033
|
-
const stderr = result.stderr || '';
|
|
1034
|
-
const combined = `${stdout} ${stderr}`.toLowerCase();
|
|
1035
|
-
if (combined.includes('already')) {
|
|
1036
|
-
return { success: true, plugin: pluginName, alreadyInstalled: true };
|
|
1037
|
-
}
|
|
1038
|
-
if (result.status === 0) {
|
|
1039
|
-
return { success: true, plugin: pluginName };
|
|
1040
|
-
}
|
|
1041
|
-
return { success: false, plugin: pluginName, error: stderr || stdout || `Exit code ${result.status}` };
|
|
1042
|
-
}
|
|
1043
|
-
catch (error) {
|
|
1044
|
-
return { success: false, plugin: pluginName, error: `Install failed: ${error}` };
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
/**
|
|
1048
|
-
* Install a plugin using vskill (routes to correct installer)
|
|
1049
|
-
*
|
|
1050
|
-
* v2.1.0: Routes to installSpecweaveLocalPlugin for sw-* plugins,
|
|
1051
|
-
* or installVskillRepoPlugin for vskill domain plugins.
|
|
1052
|
-
*
|
|
1053
|
-
* Fast-path: If plugin is already in vskill.lock, skip installation.
|
|
791
|
+
* @deprecated v1.0.535: On-demand plugin installation removed.
|
|
792
|
+
* All plugins are now installed at init time via direct file copy
|
|
793
|
+
* to .claude/skills/. This function is a no-op that returns success.
|
|
1054
794
|
*
|
|
1055
795
|
* @param pluginName - Name of the plugin to install
|
|
1056
|
-
* @param timeout - Timeout in milliseconds
|
|
1057
|
-
* @returns Installation result
|
|
796
|
+
* @param timeout - Timeout in milliseconds (unused)
|
|
797
|
+
* @returns Installation result (always success/alreadyInstalled)
|
|
1058
798
|
*/
|
|
1059
799
|
export async function installPluginViaCli(pluginName, timeout = 30000, skillFilter) {
|
|
1060
|
-
//
|
|
1061
|
-
|
|
1062
|
-
return {
|
|
1063
|
-
success: false,
|
|
1064
|
-
plugin: pluginName,
|
|
1065
|
-
error: `Unknown plugin: ${pluginName}. Only @specweave or vskill repo plugins are allowed.`,
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
// Check CLI availability (still needed for detect-intent etc.)
|
|
1069
|
-
const cliStatus = isClaudeCliAvailable();
|
|
1070
|
-
if (!cliStatus.available) {
|
|
1071
|
-
return {
|
|
1072
|
-
success: false,
|
|
1073
|
-
plugin: pluginName,
|
|
1074
|
-
error: cliStatus.error,
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
// Fast-path: Check vskill.lock - skip if already installed (works for both sources)
|
|
1078
|
-
if (isPluginInVskillLock(pluginName)) {
|
|
1079
|
-
logger.debug(`Plugin ${pluginName} already in vskill.lock, skipping installation`);
|
|
1080
|
-
// Still report installs even on fast-path — specweave is long-lived so fire-and-forget is safe
|
|
1081
|
-
reportInstallsForPlugin(pluginName).catch(() => { });
|
|
1082
|
-
return {
|
|
1083
|
-
success: true,
|
|
1084
|
-
plugin: pluginName,
|
|
1085
|
-
alreadyInstalled: true,
|
|
1086
|
-
};
|
|
1087
|
-
}
|
|
1088
|
-
// Route to correct installer based on plugin source
|
|
1089
|
-
if (isVskillPlugin(pluginName)) {
|
|
1090
|
-
return installVskillRepoPlugin(pluginName, timeout, skillFilter);
|
|
1091
|
-
}
|
|
1092
|
-
return installSpecweaveLocalPlugin(pluginName, timeout);
|
|
800
|
+
// v1.0.535: No-op — plugins are pre-installed at init time
|
|
801
|
+
return { success: true, plugin: pluginName, alreadyInstalled: true };
|
|
1093
802
|
}
|
|
1094
803
|
/**
|
|
1095
804
|
* Install multiple plugins via CLI
|
|
1096
805
|
*
|
|
806
|
+
* @deprecated v1.0.535: No-op — plugins are pre-installed at init time.
|
|
1097
807
|
* @param plugins - Array of plugin names to install
|
|
1098
|
-
* @returns Array of installation results
|
|
808
|
+
* @returns Array of installation results (all alreadyInstalled)
|
|
1099
809
|
*/
|
|
1100
810
|
export async function installPluginsViaCli(plugins, skillInvocation) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
let skillFilterMap;
|
|
1104
|
-
if (skillInvocation?.skill && skillInvocation.skill.includes(':')) {
|
|
1105
|
-
const [pluginPart, skillPart] = skillInvocation.skill.split(':');
|
|
1106
|
-
if (pluginPart && skillPart) {
|
|
1107
|
-
skillFilterMap = new Map([[pluginPart, skillPart]]);
|
|
1108
|
-
logger.debug(`Skill filter from invocation: ${pluginPart} → only install ${skillPart}`);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
for (const plugin of plugins) {
|
|
1112
|
-
const skillFilter = skillFilterMap?.get(plugin);
|
|
1113
|
-
const result = await installPluginViaCli(plugin, 30000, skillFilter);
|
|
1114
|
-
results.push(result);
|
|
1115
|
-
// Log progress
|
|
1116
|
-
if (result.success) {
|
|
1117
|
-
if (result.alreadyInstalled) {
|
|
1118
|
-
logger.debug(`Plugin ${plugin} already installed`);
|
|
1119
|
-
}
|
|
1120
|
-
else {
|
|
1121
|
-
logger.info(`Installed plugin: ${plugin}`);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
else {
|
|
1125
|
-
logger.warn(`Failed to install ${plugin}: ${result.error}`);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
return results;
|
|
811
|
+
// v1.0.535: No-op — plugins are pre-installed at init time
|
|
812
|
+
return plugins.map(plugin => ({ success: true, plugin, alreadyInstalled: true }));
|
|
1129
813
|
}
|
|
1130
814
|
/**
|
|
1131
815
|
* Full pipeline: detect plugins from prompt and optionally install them
|
|
1132
816
|
*
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
*
|
|
817
|
+
* @deprecated v1.0.535: Plugin installation removed. Detection still works
|
|
818
|
+
* for increment suggestions but no longer triggers installation.
|
|
819
|
+
* All plugins are pre-installed at init time via direct file copy.
|
|
1136
820
|
*
|
|
1137
821
|
* @param userPrompt - The user's prompt
|
|
1138
|
-
* @returns Detection
|
|
822
|
+
* @returns Detection results (installations always empty)
|
|
1139
823
|
*/
|
|
1140
824
|
export async function detectAndInstallPlugins(userPrompt) {
|
|
1141
825
|
// Check config first
|
|
@@ -1154,28 +838,12 @@ export async function detectAndInstallPlugins(userPrompt) {
|
|
|
1154
838
|
installations: [],
|
|
1155
839
|
};
|
|
1156
840
|
}
|
|
1157
|
-
// Step 1: Detect needed plugins
|
|
841
|
+
// Step 1: Detect needed plugins (still used for increment suggestions)
|
|
1158
842
|
const detection = await detectPluginsViaLLM(userPrompt);
|
|
1159
|
-
|
|
1160
|
-
return {
|
|
1161
|
-
detection,
|
|
1162
|
-
installations: [],
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
// Step 2: If suggestOnly mode, DON'T install - just return suggestions
|
|
1166
|
-
if (config.suggestOnly) {
|
|
1167
|
-
logger.info(`[detectAndInstallPlugins] Suggest-only mode: detected ${detection.plugins.join(', ')}`);
|
|
1168
|
-
return {
|
|
1169
|
-
detection,
|
|
1170
|
-
installations: [],
|
|
1171
|
-
suggestOnly: true,
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
// Step 3: Install detected plugins with skill-level filtering from LLM detection
|
|
1175
|
-
const installations = await installPluginsViaCli(detection.plugins, detection.skillInvocation);
|
|
843
|
+
// v1.0.535: No installation — plugins are pre-installed at init time
|
|
1176
844
|
return {
|
|
1177
845
|
detection,
|
|
1178
|
-
installations,
|
|
846
|
+
installations: [],
|
|
1179
847
|
};
|
|
1180
848
|
}
|
|
1181
849
|
/**
|
|
@@ -1203,44 +871,8 @@ Plugin auto-loading is disabled. Install Claude CLI to enable automatic plugin d
|
|
|
1203
871
|
// Don't show message for other errors (silent degradation)
|
|
1204
872
|
}
|
|
1205
873
|
else if (detection.plugins.length > 0) {
|
|
1206
|
-
//
|
|
1207
|
-
|
|
1208
|
-
const installCmds = detection.plugins
|
|
1209
|
-
.map((p) => {
|
|
1210
|
-
if (p.startsWith('sw-') || p === 'sw') {
|
|
1211
|
-
return ` npx vskill install --repo anton-abyzov/specweave --plugin ${p} --agent claude-code`;
|
|
1212
|
-
}
|
|
1213
|
-
return ` npx vskill install --repo anton-abyzov/vskill --plugin ${p} --agent claude-code`;
|
|
1214
|
-
})
|
|
1215
|
-
.join('\n');
|
|
1216
|
-
const reason = detection.reasoning ? `\nWhy: ${detection.reasoning}` : '';
|
|
1217
|
-
output.systemMessage = `SpecWeave: Suggested plugins for this task: ${detection.plugins.join(', ')}${reason}
|
|
1218
|
-
|
|
1219
|
-
To install:
|
|
1220
|
-
${installCmds}
|
|
1221
|
-
|
|
1222
|
-
To enable auto-install: set "pluginAutoLoad": { "suggestOnly": false } in .specweave/config.json
|
|
1223
|
-
After installing, restart Claude Code session to use new plugins.`;
|
|
1224
|
-
return JSON.stringify(output);
|
|
1225
|
-
}
|
|
1226
|
-
// NORMAL MODE: Show what was installed
|
|
1227
|
-
const installed = installations.filter((i) => i.success && !i.alreadyInstalled);
|
|
1228
|
-
const alreadyInstalled = installations.filter((i) => i.alreadyInstalled);
|
|
1229
|
-
const failed = installations.filter((i) => !i.success);
|
|
1230
|
-
const parts = [];
|
|
1231
|
-
if (installed.length > 0) {
|
|
1232
|
-
parts.push(`Loaded: ${installed.map((i) => i.plugin).join(', ')}`);
|
|
1233
|
-
}
|
|
1234
|
-
if (alreadyInstalled.length > 0 && installed.length === 0) {
|
|
1235
|
-
// Only mention already installed if nothing new was loaded
|
|
1236
|
-
parts.push(`Using: ${alreadyInstalled.map((i) => i.plugin).join(', ')}`);
|
|
1237
|
-
}
|
|
1238
|
-
if (failed.length > 0) {
|
|
1239
|
-
parts.push(`Failed: ${failed.map((i) => i.plugin).join(', ')}`);
|
|
1240
|
-
}
|
|
1241
|
-
if (parts.length > 0) {
|
|
1242
|
-
output.systemMessage = `SpecWeave: ${parts.join(' | ')}`;
|
|
1243
|
-
}
|
|
874
|
+
// v1.0.535: Plugin installation removed — plugins are pre-installed at init time.
|
|
875
|
+
// No need for suggest-only or normal install mode messages.
|
|
1244
876
|
}
|
|
1245
877
|
return JSON.stringify(output);
|
|
1246
878
|
}
|