specweave 1.0.71 ā 1.0.73
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 +38 -31
- package/dist/src/cli/commands/save.d.ts.map +1 -1
- package/dist/src/cli/commands/save.js +29 -6
- package/dist/src/cli/commands/save.js.map +1 -1
- package/dist/src/core/auto/e2e-coverage.d.ts +413 -0
- package/dist/src/core/auto/e2e-coverage.d.ts.map +1 -0
- package/dist/src/core/auto/e2e-coverage.js +1378 -0
- package/dist/src/core/auto/e2e-coverage.js.map +1 -0
- package/dist/src/core/auto/increment-planner.js +7 -1
- package/dist/src/core/auto/increment-planner.js.map +1 -1
- package/dist/src/core/auto/index.d.ts +2 -0
- package/dist/src/core/auto/index.d.ts.map +1 -1
- package/dist/src/core/auto/index.js +10 -0
- package/dist/src/core/auto/index.js.map +1 -1
- package/dist/src/core/auto/plan-approval.d.ts +104 -0
- package/dist/src/core/auto/plan-approval.d.ts.map +1 -0
- package/dist/src/core/auto/plan-approval.js +361 -0
- package/dist/src/core/auto/plan-approval.js.map +1 -0
- package/dist/src/core/plugins/skill-trigger-extractor.d.ts +147 -0
- package/dist/src/core/plugins/skill-trigger-extractor.d.ts.map +1 -0
- package/dist/src/core/plugins/skill-trigger-extractor.js +352 -0
- package/dist/src/core/plugins/skill-trigger-extractor.js.map +1 -0
- package/dist/src/core/plugins/skill-trigger-index.d.ts +91 -0
- package/dist/src/core/plugins/skill-trigger-index.d.ts.map +1 -0
- package/dist/src/core/plugins/skill-trigger-index.js +196 -0
- package/dist/src/core/plugins/skill-trigger-index.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/commands/auto.md +197 -0
- package/plugins/specweave/commands/save.md +24 -1
- package/plugins/specweave/commands/skip-increment.md +93 -0
- package/plugins/specweave/hooks/lib/resolve-package.sh +126 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +47 -3
- package/plugins/specweave/hooks/stop-auto.sh +230 -11
- package/plugins/specweave/hooks/user-prompt-submit.sh +4 -2
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +8 -0
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +10 -3
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +18 -7
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +10 -3
- package/plugins/specweave/hooks/v2/session-end.sh +50 -2
- package/plugins/specweave/hooks/v2/session-start.sh +68 -4
- package/plugins/specweave/scripts/chunk-prompt.js +204 -0
- package/plugins/specweave/scripts/setup-auto.sh +66 -0
- package/src/templates/CLAUDE.md.template +37 -30
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Trigger Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts activation trigger keywords from SKILL.md and AGENT.md files.
|
|
5
|
+
* These triggers enable automatic skill activation based on user prompts.
|
|
6
|
+
*
|
|
7
|
+
* @module core/plugins/skill-trigger-extractor
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as fs from '../../utils/fs-native.js';
|
|
12
|
+
/**
|
|
13
|
+
* SkillTriggerExtractor - Extract activation triggers from plugin skills
|
|
14
|
+
*/
|
|
15
|
+
export class SkillTriggerExtractor {
|
|
16
|
+
/**
|
|
17
|
+
* Extract triggers from a single SKILL.md or AGENT.md content
|
|
18
|
+
*
|
|
19
|
+
* @param content - File content
|
|
20
|
+
* @param name - Skill/agent name
|
|
21
|
+
* @param plugin - Parent plugin name
|
|
22
|
+
* @param type - 'skill' or 'agent'
|
|
23
|
+
* @param filePath - Path to the file
|
|
24
|
+
* @returns Extracted triggers
|
|
25
|
+
*/
|
|
26
|
+
extractFromContent(content, name, plugin, type, filePath) {
|
|
27
|
+
const description = this.extractDescription(content);
|
|
28
|
+
const triggers = this.extractTriggerKeywords(content, description);
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
plugin,
|
|
32
|
+
type,
|
|
33
|
+
description,
|
|
34
|
+
triggers,
|
|
35
|
+
path: filePath
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract description from YAML frontmatter
|
|
40
|
+
*
|
|
41
|
+
* @param content - File content
|
|
42
|
+
* @returns Description string
|
|
43
|
+
*/
|
|
44
|
+
extractDescription(content) {
|
|
45
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---/);
|
|
46
|
+
if (frontmatterMatch) {
|
|
47
|
+
const descMatch = frontmatterMatch[1].match(/description:\s*(.+)/);
|
|
48
|
+
if (descMatch) {
|
|
49
|
+
return descMatch[1].trim();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Extract trigger keywords from content and description
|
|
56
|
+
*
|
|
57
|
+
* Looks for:
|
|
58
|
+
* 1. "Activates for:" sections in description
|
|
59
|
+
* 2. Keywords in YAML frontmatter
|
|
60
|
+
* 3. Technology names and patterns in the description
|
|
61
|
+
*
|
|
62
|
+
* @param content - Full file content
|
|
63
|
+
* @param description - Extracted description
|
|
64
|
+
* @returns Array of normalized trigger keywords
|
|
65
|
+
*/
|
|
66
|
+
extractTriggerKeywords(content, description) {
|
|
67
|
+
const triggers = new Set();
|
|
68
|
+
// 1. Extract from "Activates for:" pattern in description
|
|
69
|
+
const activatesForMatch = description.match(/Activates\s+for[:\s]+(.+?)(?:\.|$)/i);
|
|
70
|
+
if (activatesForMatch) {
|
|
71
|
+
const keywords = this.parseKeywordList(activatesForMatch[1]);
|
|
72
|
+
keywords.forEach(k => triggers.add(k));
|
|
73
|
+
}
|
|
74
|
+
// 2. Extract from "Use when" or "Use this skill when" patterns
|
|
75
|
+
const useWhenMatch = description.match(/Use\s+(?:this\s+skill\s+)?when[:\s]+(.+?)(?:\.|$)/i);
|
|
76
|
+
if (useWhenMatch) {
|
|
77
|
+
const keywords = this.extractTechnologyTerms(useWhenMatch[1]);
|
|
78
|
+
keywords.forEach(k => triggers.add(k));
|
|
79
|
+
}
|
|
80
|
+
// 3. Extract keywords from YAML frontmatter
|
|
81
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---/);
|
|
82
|
+
if (frontmatterMatch) {
|
|
83
|
+
const keywordsMatch = frontmatterMatch[1].match(/keywords:\s*\[([^\]]+)\]/);
|
|
84
|
+
if (keywordsMatch) {
|
|
85
|
+
const keywords = this.parseKeywordList(keywordsMatch[1]);
|
|
86
|
+
keywords.forEach(k => triggers.add(k));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// 4. Extract technology terms from description
|
|
90
|
+
const techTerms = this.extractTechnologyTerms(description);
|
|
91
|
+
techTerms.forEach(t => triggers.add(t));
|
|
92
|
+
// 5. Look for "When to Use" sections in content
|
|
93
|
+
const whenToUseMatch = content.match(/##\s*When to Use[^\n]*\n([\s\S]+?)(?:\n##|$)/i);
|
|
94
|
+
if (whenToUseMatch) {
|
|
95
|
+
const techTerms = this.extractTechnologyTerms(whenToUseMatch[1]);
|
|
96
|
+
// Only add strong technology terms from this section
|
|
97
|
+
techTerms.filter(t => this.isStrongTechnologyTerm(t)).forEach(t => triggers.add(t));
|
|
98
|
+
}
|
|
99
|
+
return Array.from(triggers).filter(t => t.length >= 2);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse a keyword list from comma/or separated string
|
|
103
|
+
*
|
|
104
|
+
* @param text - Keyword list text
|
|
105
|
+
* @returns Array of normalized keywords
|
|
106
|
+
*/
|
|
107
|
+
parseKeywordList(text) {
|
|
108
|
+
const keywords = [];
|
|
109
|
+
// Split by comma, "or", slash, pipe
|
|
110
|
+
const parts = text.split(/[,|\/]|\s+or\s+/gi);
|
|
111
|
+
for (const part of parts) {
|
|
112
|
+
const cleaned = part.trim()
|
|
113
|
+
.replace(/^["']|["']$/g, '') // Remove quotes
|
|
114
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
115
|
+
.toLowerCase();
|
|
116
|
+
if (cleaned.length >= 2 && !this.isStopWord(cleaned)) {
|
|
117
|
+
keywords.push(cleaned);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return keywords;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Extract technology terms from text
|
|
124
|
+
*
|
|
125
|
+
* @param text - Text to extract from
|
|
126
|
+
* @returns Array of technology terms
|
|
127
|
+
*/
|
|
128
|
+
extractTechnologyTerms(text) {
|
|
129
|
+
const terms = new Set();
|
|
130
|
+
// Known technology patterns (case-insensitive)
|
|
131
|
+
const techPatterns = [
|
|
132
|
+
// Cloud Providers
|
|
133
|
+
/\bAWS\b/gi, /\bAzure\b/gi, /\bGCP\b/gi, /\bGoogle Cloud\b/gi,
|
|
134
|
+
// Kubernetes
|
|
135
|
+
/\bKubernetes\b/gi, /\bK8s\b/gi, /\bEKS\b/gi, /\bAKS\b/gi, /\bGKE\b/gi,
|
|
136
|
+
/\bHelm\b/gi, /\bGitOps\b/gi, /\bArgoCD\b/gi, /\bFlux\b/gi, /\bIstio\b/gi,
|
|
137
|
+
// Mobile
|
|
138
|
+
/\bReact Native\b/gi, /\bExpo\b/gi, /\biOS\b/gi, /\bAndroid\b/gi,
|
|
139
|
+
/\bSwift\b/gi, /\bKotlin\b/gi, /\bFlutter\b/gi,
|
|
140
|
+
// Frontend
|
|
141
|
+
/\bReact\b/gi, /\bVue\.?js\b/gi, /\bAngular\b/gi, /\bNext\.?js\b/gi,
|
|
142
|
+
/\bNuxt\b/gi, /\bSvelte\b/gi, /\bTailwind\b/gi, /\bCSS\b/gi,
|
|
143
|
+
// Backend
|
|
144
|
+
/\bNode\.?js\b/gi, /\bExpress\b/gi, /\bNestJS\b/gi, /\bFastify\b/gi,
|
|
145
|
+
/\bDjango\b/gi, /\bFlask\b/gi, /\bFastAPI\b/gi,
|
|
146
|
+
/\bSpring Boot\b/gi, /\b\.NET\b/gi, /\bASP\.NET\b/gi,
|
|
147
|
+
/\bRuby on Rails\b/gi, /\bRails\b/gi, /\bLaravel\b/gi,
|
|
148
|
+
/\bGo\b/gi, /\bGolang\b/gi, /\bRust\b/gi,
|
|
149
|
+
// Databases
|
|
150
|
+
/\bPostgreSQL\b/gi, /\bPostgres\b/gi, /\bMySQL\b/gi, /\bMongoDB\b/gi,
|
|
151
|
+
/\bRedis\b/gi, /\bElasticsearch\b/gi, /\bCassandra\b/gi,
|
|
152
|
+
/\bPrisma\b/gi, /\bTypeORM\b/gi, /\bSequelize\b/gi, /\bMongoose\b/gi,
|
|
153
|
+
// Messaging
|
|
154
|
+
/\bKafka\b/gi, /\bRabbitMQ\b/gi, /\bSQS\b/gi, /\bPubSub\b/gi,
|
|
155
|
+
// DevOps/Infra
|
|
156
|
+
/\bTerraform\b/gi, /\bDocker\b/gi, /\bCI\/CD\b/gi,
|
|
157
|
+
/\bGitHub Actions\b/gi, /\bJenkins\b/gi, /\bGitLab CI\b/gi,
|
|
158
|
+
/\bPrometheus\b/gi, /\bGrafana\b/gi, /\bDatadog\b/gi,
|
|
159
|
+
// Testing
|
|
160
|
+
/\bPlaywright\b/gi, /\bCypress\b/gi, /\bJest\b/gi, /\bVitest\b/gi,
|
|
161
|
+
/\bSelenium\b/gi, /\bE2E\b/gi, /\bTDD\b/gi, /\bBDD\b/gi,
|
|
162
|
+
// Security
|
|
163
|
+
/\bOWASP\b/gi, /\bJWT\b/gi, /\bOAuth\b/gi, /\bSSL\b/gi, /\bTLS\b/gi,
|
|
164
|
+
/\bCORS\b/gi, /\bCSRF\b/gi, /\bXSS\b/gi,
|
|
165
|
+
// ML/AI
|
|
166
|
+
/\bTensorFlow\b/gi, /\bPyTorch\b/gi, /\bMLflow\b/gi,
|
|
167
|
+
/\bScikit-learn\b/gi, /\bPandas\b/gi, /\bNumPy\b/gi,
|
|
168
|
+
// API
|
|
169
|
+
/\bREST API\b/gi, /\bGraphQL\b/gi, /\btRPC\b/gi, /\bgRPC\b/gi,
|
|
170
|
+
/\bOpenAPI\b/gi, /\bSwagger\b/gi,
|
|
171
|
+
// Payments
|
|
172
|
+
/\bStripe\b/gi, /\bPayPal\b/gi, /\bPCI\b/gi,
|
|
173
|
+
// General
|
|
174
|
+
/\bTypeScript\b/gi, /\bJavaScript\b/gi, /\bPython\b/gi, /\bJava\b/gi,
|
|
175
|
+
/\bC#\b/gi, /\bRuby\b/gi, /\bPHP\b/gi
|
|
176
|
+
];
|
|
177
|
+
for (const pattern of techPatterns) {
|
|
178
|
+
const matches = text.match(pattern);
|
|
179
|
+
if (matches) {
|
|
180
|
+
matches.forEach(m => terms.add(m.toLowerCase().trim()));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return Array.from(terms);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if a term is a strong technology term (not generic)
|
|
187
|
+
*
|
|
188
|
+
* @param term - Term to check
|
|
189
|
+
* @returns True if strong technology term
|
|
190
|
+
*/
|
|
191
|
+
isStrongTechnologyTerm(term) {
|
|
192
|
+
const strongTerms = new Set([
|
|
193
|
+
'kubernetes', 'k8s', 'eks', 'aks', 'gke', 'helm', 'gitops', 'argocd',
|
|
194
|
+
'react native', 'expo', 'ios', 'android', 'flutter',
|
|
195
|
+
'react', 'vue', 'angular', 'next.js', 'nextjs', 'nuxt', 'svelte',
|
|
196
|
+
'node.js', 'nodejs', 'express', 'nestjs', 'fastify',
|
|
197
|
+
'django', 'flask', 'fastapi', 'spring boot', '.net', 'asp.net',
|
|
198
|
+
'postgresql', 'postgres', 'mysql', 'mongodb', 'redis',
|
|
199
|
+
'prisma', 'typeorm', 'sequelize', 'mongoose',
|
|
200
|
+
'kafka', 'rabbitmq', 'terraform', 'docker',
|
|
201
|
+
'playwright', 'cypress', 'jest', 'vitest',
|
|
202
|
+
'stripe', 'paypal', 'owasp', 'jwt', 'oauth',
|
|
203
|
+
'tensorflow', 'pytorch', 'mlflow',
|
|
204
|
+
'graphql', 'trpc', 'grpc', 'openapi'
|
|
205
|
+
]);
|
|
206
|
+
return strongTerms.has(term.toLowerCase());
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Check if word is a stop word (common words to ignore)
|
|
210
|
+
*
|
|
211
|
+
* @param word - Word to check
|
|
212
|
+
* @returns True if stop word
|
|
213
|
+
*/
|
|
214
|
+
isStopWord(word) {
|
|
215
|
+
const stopWords = new Set([
|
|
216
|
+
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
217
|
+
'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',
|
|
218
|
+
'be', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
|
|
219
|
+
'could', 'should', 'may', 'might', 'must', 'shall', 'can', 'need',
|
|
220
|
+
'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'it',
|
|
221
|
+
'we', 'they', 'what', 'which', 'who', 'when', 'where', 'why', 'how',
|
|
222
|
+
'all', 'each', 'every', 'both', 'few', 'more', 'most', 'other',
|
|
223
|
+
'some', 'such', 'only', 'own', 'same', 'so', 'than', 'too', 'very',
|
|
224
|
+
'just', 'use', 'using', 'used', 'create', 'creating', 'created',
|
|
225
|
+
'build', 'building', 'built', 'implement', 'implementing',
|
|
226
|
+
'help', 'helps', 'helping', 'want', 'wants', 'wanted', 'need', 'needs'
|
|
227
|
+
]);
|
|
228
|
+
return stopWords.has(word.toLowerCase());
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Scan all plugins and extract triggers
|
|
232
|
+
*
|
|
233
|
+
* @param pluginsDir - Path to plugins directory
|
|
234
|
+
* @returns Array of extracted triggers
|
|
235
|
+
*/
|
|
236
|
+
async scanAllPlugins(pluginsDir) {
|
|
237
|
+
const allTriggers = [];
|
|
238
|
+
// Get all plugin directories
|
|
239
|
+
if (!(await fs.pathExists(pluginsDir))) {
|
|
240
|
+
return allTriggers;
|
|
241
|
+
}
|
|
242
|
+
const entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
243
|
+
for (const entry of entries) {
|
|
244
|
+
if (!entry.isDirectory())
|
|
245
|
+
continue;
|
|
246
|
+
const pluginPath = path.join(pluginsDir, entry.name);
|
|
247
|
+
const pluginName = entry.name;
|
|
248
|
+
// Scan skills
|
|
249
|
+
const skillsDir = path.join(pluginPath, 'skills');
|
|
250
|
+
if (await fs.pathExists(skillsDir)) {
|
|
251
|
+
const skillEntries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
252
|
+
for (const skillEntry of skillEntries) {
|
|
253
|
+
if (!skillEntry.isDirectory())
|
|
254
|
+
continue;
|
|
255
|
+
const skillPath = path.join(skillsDir, skillEntry.name, 'SKILL.md');
|
|
256
|
+
if (await fs.pathExists(skillPath)) {
|
|
257
|
+
const content = await fs.readFile(skillPath, 'utf-8');
|
|
258
|
+
const triggers = this.extractFromContent(content, skillEntry.name, pluginName, 'skill', skillPath);
|
|
259
|
+
allTriggers.push(triggers);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Scan agents
|
|
264
|
+
const agentsDir = path.join(pluginPath, 'agents');
|
|
265
|
+
if (await fs.pathExists(agentsDir)) {
|
|
266
|
+
const agentEntries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
267
|
+
for (const agentEntry of agentEntries) {
|
|
268
|
+
if (!agentEntry.isDirectory())
|
|
269
|
+
continue;
|
|
270
|
+
const agentPath = path.join(agentsDir, agentEntry.name, 'AGENT.md');
|
|
271
|
+
if (await fs.pathExists(agentPath)) {
|
|
272
|
+
const content = await fs.readFile(agentPath, 'utf-8');
|
|
273
|
+
const triggers = this.extractFromContent(content, agentEntry.name, pluginName, 'agent', agentPath);
|
|
274
|
+
allTriggers.push(triggers);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return allTriggers;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Build the skill triggers index from extracted triggers
|
|
283
|
+
*
|
|
284
|
+
* @param triggers - Array of extracted triggers
|
|
285
|
+
* @returns Skill triggers index
|
|
286
|
+
*/
|
|
287
|
+
buildIndex(triggers) {
|
|
288
|
+
const keywords = {};
|
|
289
|
+
const skills = {};
|
|
290
|
+
for (const trigger of triggers) {
|
|
291
|
+
// Full qualified name: plugin:type:name
|
|
292
|
+
const fqn = `${trigger.plugin}:${trigger.name}`;
|
|
293
|
+
// Add to skills metadata
|
|
294
|
+
skills[fqn] = {
|
|
295
|
+
plugin: trigger.plugin,
|
|
296
|
+
type: trigger.type,
|
|
297
|
+
triggers: trigger.triggers,
|
|
298
|
+
description: trigger.description.substring(0, 150),
|
|
299
|
+
fqn
|
|
300
|
+
};
|
|
301
|
+
// Build inverted index
|
|
302
|
+
for (const keyword of trigger.triggers) {
|
|
303
|
+
if (!keywords[keyword]) {
|
|
304
|
+
keywords[keyword] = [];
|
|
305
|
+
}
|
|
306
|
+
if (!keywords[keyword].includes(fqn)) {
|
|
307
|
+
keywords[keyword].push(fqn);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
keywords,
|
|
313
|
+
skills,
|
|
314
|
+
generatedAt: new Date().toISOString(),
|
|
315
|
+
skillCount: Object.keys(skills).length,
|
|
316
|
+
keywordCount: Object.keys(keywords).length
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Match a user prompt against the trigger index
|
|
321
|
+
*
|
|
322
|
+
* @param prompt - User prompt
|
|
323
|
+
* @param index - Skill triggers index
|
|
324
|
+
* @returns Array of matched skill FQNs sorted by relevance
|
|
325
|
+
*/
|
|
326
|
+
matchPrompt(prompt, index) {
|
|
327
|
+
const matches = new Map();
|
|
328
|
+
// Normalize prompt
|
|
329
|
+
const normalizedPrompt = prompt.toLowerCase();
|
|
330
|
+
// Check each keyword
|
|
331
|
+
for (const [keyword, skillFqns] of Object.entries(index.keywords)) {
|
|
332
|
+
// Check if keyword appears in prompt
|
|
333
|
+
if (normalizedPrompt.includes(keyword)) {
|
|
334
|
+
for (const fqn of skillFqns) {
|
|
335
|
+
const existing = matches.get(fqn) || { score: 0, keywords: [] };
|
|
336
|
+
existing.score += keyword.length; // Longer keywords = higher score
|
|
337
|
+
existing.keywords.push(keyword);
|
|
338
|
+
matches.set(fqn, existing);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Convert to array and sort by score
|
|
343
|
+
return Array.from(matches.entries())
|
|
344
|
+
.map(([fqn, data]) => ({
|
|
345
|
+
fqn,
|
|
346
|
+
score: data.score,
|
|
347
|
+
matchedKeywords: data.keywords
|
|
348
|
+
}))
|
|
349
|
+
.sort((a, b) => b.score - a.score);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=skill-trigger-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-trigger-extractor.js","sourceRoot":"","sources":["../../../../src/core/plugins/skill-trigger-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Trigger Index Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the skill triggers index file used for automatic skill activation.
|
|
5
|
+
*
|
|
6
|
+
* @module core/plugins/skill-trigger-index
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
import { SkillTriggersIndex } from './skill-trigger-extractor.js';
|
|
10
|
+
/**
|
|
11
|
+
* SkillTriggerIndexManager - Generate and manage skill triggers index
|
|
12
|
+
*/
|
|
13
|
+
export declare class SkillTriggerIndexManager {
|
|
14
|
+
private extractor;
|
|
15
|
+
private projectRoot;
|
|
16
|
+
constructor(projectRoot?: string);
|
|
17
|
+
/**
|
|
18
|
+
* Generate the skill triggers index from all plugins
|
|
19
|
+
*
|
|
20
|
+
* @param pluginsDir - Path to plugins directory (relative or absolute)
|
|
21
|
+
* @returns Generated index
|
|
22
|
+
*/
|
|
23
|
+
generateIndex(pluginsDir?: string): Promise<SkillTriggersIndex>;
|
|
24
|
+
/**
|
|
25
|
+
* Save index to file
|
|
26
|
+
*
|
|
27
|
+
* @param index - Skill triggers index
|
|
28
|
+
* @param indexPath - Path to save index (relative or absolute)
|
|
29
|
+
*/
|
|
30
|
+
saveIndex(index: SkillTriggersIndex, indexPath?: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Load index from file
|
|
33
|
+
*
|
|
34
|
+
* @param indexPath - Path to index file (relative or absolute)
|
|
35
|
+
* @returns Loaded index or null if not found
|
|
36
|
+
*/
|
|
37
|
+
loadIndex(indexPath?: string): Promise<SkillTriggersIndex | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Generate and save index (convenience method)
|
|
40
|
+
*
|
|
41
|
+
* @param pluginsDir - Path to plugins directory
|
|
42
|
+
* @param indexPath - Path to save index
|
|
43
|
+
* @returns Generated index and path
|
|
44
|
+
*/
|
|
45
|
+
generateAndSave(pluginsDir?: string, indexPath?: string): Promise<{
|
|
46
|
+
index: SkillTriggersIndex;
|
|
47
|
+
path: string;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Match a user prompt against the index
|
|
51
|
+
*
|
|
52
|
+
* @param prompt - User prompt
|
|
53
|
+
* @param index - Optional pre-loaded index
|
|
54
|
+
* @returns Matched skills with scores
|
|
55
|
+
*/
|
|
56
|
+
matchPrompt(prompt: string, index?: SkillTriggersIndex): Promise<Array<{
|
|
57
|
+
fqn: string;
|
|
58
|
+
score: number;
|
|
59
|
+
matchedKeywords: string[];
|
|
60
|
+
}>>;
|
|
61
|
+
/**
|
|
62
|
+
* Check if index needs regeneration
|
|
63
|
+
*
|
|
64
|
+
* @param maxAgeMs - Maximum age in milliseconds (default: 24 hours)
|
|
65
|
+
* @returns True if index needs regeneration
|
|
66
|
+
*/
|
|
67
|
+
needsRegeneration(maxAgeMs?: number): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Get index statistics
|
|
70
|
+
*
|
|
71
|
+
* @returns Index stats or null if not found
|
|
72
|
+
*/
|
|
73
|
+
getStats(): Promise<{
|
|
74
|
+
skillCount: number;
|
|
75
|
+
keywordCount: number;
|
|
76
|
+
generatedAt: string;
|
|
77
|
+
topKeywords: Array<{
|
|
78
|
+
keyword: string;
|
|
79
|
+
skillCount: number;
|
|
80
|
+
}>;
|
|
81
|
+
} | null>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* CLI helper to generate index
|
|
85
|
+
*/
|
|
86
|
+
export declare function generateSkillTriggersIndexCLI(projectRoot?: string, pluginsDir?: string, indexPath?: string, verbose?: boolean): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* CLI helper to test prompt matching
|
|
89
|
+
*/
|
|
90
|
+
export declare function testPromptMatchingCLI(prompt: string, projectRoot?: string, verbose?: boolean): Promise<void>;
|
|
91
|
+
//# sourceMappingURL=skill-trigger-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-trigger-index.d.ts","sourceRoot":"","sources":["../../../../src/core/plugins/skill-trigger-index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAyB,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAYzF;;GAEG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,GAAE,MAAsB;IAK/C;;;;;OAKG;IACG,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAcrE;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAc/E;;;;;OAKG;IACG,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAgBvE;;;;;;OAMG;IACG,eAAe,CACnB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC;QAAE,KAAK,EAAE,kBAAkB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMvD;;;;;;OAMG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,kBAAkB,GACzB,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAU5E;;;;;OAKG;IACG,iBAAiB,CAAC,QAAQ,GAAE,MAA4B,GAAG,OAAO,CAAC,OAAO,CAAC;IAajF;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC7D,GAAG,IAAI,CAAC;CAoBV;AAED;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,GAAE,MAAsB,EACnC,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAAsB,EACnC,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Trigger Index Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the skill triggers index file used for automatic skill activation.
|
|
5
|
+
*
|
|
6
|
+
* @module core/plugins/skill-trigger-index
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as fs from '../../utils/fs-native.js';
|
|
11
|
+
import { SkillTriggerExtractor } from './skill-trigger-extractor.js';
|
|
12
|
+
/**
|
|
13
|
+
* Default path for the skill triggers index
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_INDEX_PATH = '.specweave/state/skill-triggers-index.json';
|
|
16
|
+
/**
|
|
17
|
+
* Default plugins directory
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_PLUGINS_DIR = 'plugins';
|
|
20
|
+
/**
|
|
21
|
+
* SkillTriggerIndexManager - Generate and manage skill triggers index
|
|
22
|
+
*/
|
|
23
|
+
export class SkillTriggerIndexManager {
|
|
24
|
+
constructor(projectRoot = process.cwd()) {
|
|
25
|
+
this.extractor = new SkillTriggerExtractor();
|
|
26
|
+
this.projectRoot = projectRoot;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generate the skill triggers index from all plugins
|
|
30
|
+
*
|
|
31
|
+
* @param pluginsDir - Path to plugins directory (relative or absolute)
|
|
32
|
+
* @returns Generated index
|
|
33
|
+
*/
|
|
34
|
+
async generateIndex(pluginsDir) {
|
|
35
|
+
const resolvedPluginsDir = pluginsDir
|
|
36
|
+
? path.isAbsolute(pluginsDir) ? pluginsDir : path.join(this.projectRoot, pluginsDir)
|
|
37
|
+
: path.join(this.projectRoot, DEFAULT_PLUGINS_DIR);
|
|
38
|
+
// Scan all plugins
|
|
39
|
+
const triggers = await this.extractor.scanAllPlugins(resolvedPluginsDir);
|
|
40
|
+
// Build index
|
|
41
|
+
const index = this.extractor.buildIndex(triggers);
|
|
42
|
+
return index;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Save index to file
|
|
46
|
+
*
|
|
47
|
+
* @param index - Skill triggers index
|
|
48
|
+
* @param indexPath - Path to save index (relative or absolute)
|
|
49
|
+
*/
|
|
50
|
+
async saveIndex(index, indexPath) {
|
|
51
|
+
const resolvedPath = indexPath
|
|
52
|
+
? path.isAbsolute(indexPath) ? indexPath : path.join(this.projectRoot, indexPath)
|
|
53
|
+
: path.join(this.projectRoot, DEFAULT_INDEX_PATH);
|
|
54
|
+
// Ensure directory exists
|
|
55
|
+
await fs.ensureDir(path.dirname(resolvedPath));
|
|
56
|
+
// Write index
|
|
57
|
+
await fs.writeJSON(resolvedPath, index, { spaces: 2 });
|
|
58
|
+
return resolvedPath;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Load index from file
|
|
62
|
+
*
|
|
63
|
+
* @param indexPath - Path to index file (relative or absolute)
|
|
64
|
+
* @returns Loaded index or null if not found
|
|
65
|
+
*/
|
|
66
|
+
async loadIndex(indexPath) {
|
|
67
|
+
const resolvedPath = indexPath
|
|
68
|
+
? path.isAbsolute(indexPath) ? indexPath : path.join(this.projectRoot, indexPath)
|
|
69
|
+
: path.join(this.projectRoot, DEFAULT_INDEX_PATH);
|
|
70
|
+
if (!(await fs.pathExists(resolvedPath))) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
return await fs.readJSON(resolvedPath);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Generate and save index (convenience method)
|
|
82
|
+
*
|
|
83
|
+
* @param pluginsDir - Path to plugins directory
|
|
84
|
+
* @param indexPath - Path to save index
|
|
85
|
+
* @returns Generated index and path
|
|
86
|
+
*/
|
|
87
|
+
async generateAndSave(pluginsDir, indexPath) {
|
|
88
|
+
const index = await this.generateIndex(pluginsDir);
|
|
89
|
+
const savedPath = await this.saveIndex(index, indexPath);
|
|
90
|
+
return { index, path: savedPath };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Match a user prompt against the index
|
|
94
|
+
*
|
|
95
|
+
* @param prompt - User prompt
|
|
96
|
+
* @param index - Optional pre-loaded index
|
|
97
|
+
* @returns Matched skills with scores
|
|
98
|
+
*/
|
|
99
|
+
async matchPrompt(prompt, index) {
|
|
100
|
+
const loadedIndex = index || await this.loadIndex();
|
|
101
|
+
if (!loadedIndex) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
return this.extractor.matchPrompt(prompt, loadedIndex);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if index needs regeneration
|
|
108
|
+
*
|
|
109
|
+
* @param maxAgeMs - Maximum age in milliseconds (default: 24 hours)
|
|
110
|
+
* @returns True if index needs regeneration
|
|
111
|
+
*/
|
|
112
|
+
async needsRegeneration(maxAgeMs = 24 * 60 * 60 * 1000) {
|
|
113
|
+
const index = await this.loadIndex();
|
|
114
|
+
if (!index) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
const generatedAt = new Date(index.generatedAt).getTime();
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
return (now - generatedAt) > maxAgeMs;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get index statistics
|
|
123
|
+
*
|
|
124
|
+
* @returns Index stats or null if not found
|
|
125
|
+
*/
|
|
126
|
+
async getStats() {
|
|
127
|
+
const index = await this.loadIndex();
|
|
128
|
+
if (!index) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
// Get top 20 keywords by skill count
|
|
132
|
+
const keywordCounts = Object.entries(index.keywords)
|
|
133
|
+
.map(([keyword, skills]) => ({ keyword, skillCount: skills.length }))
|
|
134
|
+
.sort((a, b) => b.skillCount - a.skillCount)
|
|
135
|
+
.slice(0, 20);
|
|
136
|
+
return {
|
|
137
|
+
skillCount: index.skillCount,
|
|
138
|
+
keywordCount: index.keywordCount,
|
|
139
|
+
generatedAt: index.generatedAt,
|
|
140
|
+
topKeywords: keywordCounts
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* CLI helper to generate index
|
|
146
|
+
*/
|
|
147
|
+
export async function generateSkillTriggersIndexCLI(projectRoot = process.cwd(), pluginsDir, indexPath, verbose = false) {
|
|
148
|
+
const manager = new SkillTriggerIndexManager(projectRoot);
|
|
149
|
+
if (verbose) {
|
|
150
|
+
console.log('š Scanning plugins for skill triggers...');
|
|
151
|
+
}
|
|
152
|
+
const { index, path: savedPath } = await manager.generateAndSave(pluginsDir, indexPath);
|
|
153
|
+
if (verbose) {
|
|
154
|
+
console.log(`ā
Generated skill triggers index:`);
|
|
155
|
+
console.log(` Skills: ${index.skillCount}`);
|
|
156
|
+
console.log(` Keywords: ${index.keywordCount}`);
|
|
157
|
+
console.log(` Path: ${savedPath}`);
|
|
158
|
+
// Show top keywords
|
|
159
|
+
const stats = await manager.getStats();
|
|
160
|
+
if (stats && stats.topKeywords.length > 0) {
|
|
161
|
+
console.log('\nš Top keywords:');
|
|
162
|
+
for (const { keyword, skillCount } of stats.topKeywords.slice(0, 10)) {
|
|
163
|
+
console.log(` ${keyword}: ${skillCount} skills`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* CLI helper to test prompt matching
|
|
170
|
+
*/
|
|
171
|
+
export async function testPromptMatchingCLI(prompt, projectRoot = process.cwd(), verbose = false) {
|
|
172
|
+
const manager = new SkillTriggerIndexManager(projectRoot);
|
|
173
|
+
const index = await manager.loadIndex();
|
|
174
|
+
if (!index) {
|
|
175
|
+
console.error('ā No skill triggers index found. Run refresh-marketplace.sh first.');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
const matches = await manager.matchPrompt(prompt, index);
|
|
179
|
+
if (matches.length === 0) {
|
|
180
|
+
console.log('ā No skills matched for prompt:', prompt);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
console.log(`ā
Matched ${matches.length} skills for prompt:`);
|
|
184
|
+
console.log(` "${prompt}"\n`);
|
|
185
|
+
for (const match of matches.slice(0, 10)) {
|
|
186
|
+
const skill = index.skills[match.fqn];
|
|
187
|
+
console.log(` š¦ ${match.fqn}`);
|
|
188
|
+
console.log(` Score: ${match.score}`);
|
|
189
|
+
console.log(` Keywords: ${match.matchedKeywords.join(', ')}`);
|
|
190
|
+
if (verbose && skill) {
|
|
191
|
+
console.log(` Description: ${skill.description}`);
|
|
192
|
+
}
|
|
193
|
+
console.log();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=skill-trigger-index.js.map
|