@girardmedia/bootspring 2.1.2 → 2.2.0
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/bin/bootspring.js +102 -82
- package/generators/api-docs.js +3 -3
- package/generators/decisions.js +14 -14
- package/generators/health.js +6 -6
- package/generators/sprint.js +4 -4
- package/generators/templates/build-planning.template.js +2 -2
- package/generators/visual-doc-generator.js +1 -1
- package/package.json +2 -15
- package/cli/agent.js +0 -799
- package/cli/auth.js +0 -896
- package/cli/billing.js +0 -320
- package/cli/build.js +0 -1442
- package/cli/dashboard.js +0 -123
- package/cli/init.js +0 -669
- package/cli/mcp.js +0 -240
- package/cli/orchestrator.js +0 -240
- package/cli/project.js +0 -825
- package/cli/quality.js +0 -281
- package/cli/skill.js +0 -503
- package/cli/switch.js +0 -453
- package/cli/todo.js +0 -629
- package/cli/update.js +0 -132
package/cli/skill.js
DELETED
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Skill Command
|
|
3
|
-
* Browse and use skill patterns.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const utils = require('../core/utils');
|
|
8
|
-
const entitlements = require('../core/entitlements');
|
|
9
|
-
const tierEnforcement = require('../core/tier-enforcement');
|
|
10
|
-
const telemetry = require('../core/telemetry');
|
|
11
|
-
const api = require('../core/api-client');
|
|
12
|
-
const auth = require('../core/auth');
|
|
13
|
-
|
|
14
|
-
// Note: skills module removed - all content fetched from API (thin client)
|
|
15
|
-
|
|
16
|
-
function trackTelemetry(event, payload) {
|
|
17
|
-
try {
|
|
18
|
-
telemetry.emitEvent(event, payload);
|
|
19
|
-
} catch {
|
|
20
|
-
// Do not block CLI flow on telemetry issues.
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Use centralized tier badge formatting
|
|
25
|
-
const { formatTierBadge } = tierEnforcement;
|
|
26
|
-
|
|
27
|
-
function normalizeChecksum(checksum) {
|
|
28
|
-
const raw = String(checksum || '').trim().toLowerCase();
|
|
29
|
-
if (!raw) return '';
|
|
30
|
-
return raw.startsWith('sha256:') ? raw.slice(7) : raw;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function isPremiumTier(tier) {
|
|
34
|
-
const normalized = String(tier || 'free').trim().toLowerCase();
|
|
35
|
-
return normalized === 'pro' || normalized === 'premium';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function verifyPayloadIntegrity(resourceId, content, checksum, options = {}) {
|
|
39
|
-
const { requireChecksum = false } = options;
|
|
40
|
-
const expected = normalizeChecksum(checksum);
|
|
41
|
-
if (requireChecksum && !expected) {
|
|
42
|
-
throw new Error(`Integrity metadata missing for skill payload: ${resourceId}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const digest = crypto.createHash('sha256').update(String(content || ''), 'utf8').digest('hex');
|
|
46
|
-
if (expected && expected !== digest) {
|
|
47
|
-
throw new Error(`Integrity check failed for skill payload: ${resourceId}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
algorithm: 'sha256',
|
|
52
|
-
checksum: `sha256:${digest}`,
|
|
53
|
-
expected: expected ? `sha256:${expected}` : null,
|
|
54
|
-
verified: !!expected
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Fetch skills list from API (thin client)
|
|
60
|
-
*/
|
|
61
|
-
async function fetchSkillsList(options = {}) {
|
|
62
|
-
if (!auth.isAuthenticated()) {
|
|
63
|
-
return { error: 'auth_required' };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const response = await api.listSkills(options);
|
|
68
|
-
return response;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
if (error.status === 401) {
|
|
71
|
-
return { error: 'auth_required' };
|
|
72
|
-
}
|
|
73
|
-
return { error: 'network_error', message: error.message };
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Fetch skill content from API (thin client)
|
|
79
|
-
*/
|
|
80
|
-
async function fetchSkillContent(skillId) {
|
|
81
|
-
if (!auth.isAuthenticated()) {
|
|
82
|
-
return { error: 'auth_required' };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const response = await api.getSkillContent(skillId);
|
|
87
|
-
const skillTier = String(response?.tier || 'free').toLowerCase();
|
|
88
|
-
const access = entitlements.checkSkillAccess(skillId, {
|
|
89
|
-
mode: 'server',
|
|
90
|
-
entitled: true,
|
|
91
|
-
tier: auth.getTier(),
|
|
92
|
-
skillTier
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
if (!access.allowed) {
|
|
96
|
-
return { error: 'upgrade_required', requiredTier: 'pro', message: access.reason };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const integrity = verifyPayloadIntegrity(skillId, response?.content || '', response?.checksum, {
|
|
100
|
-
requireChecksum: Boolean(response?.checksum)
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return { ...response, integrity };
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (error.message && error.message.toLowerCase().includes('integrity')) {
|
|
106
|
-
return { error: 'integrity_error', message: error.message };
|
|
107
|
-
}
|
|
108
|
-
if (error.status === 403) {
|
|
109
|
-
return { error: 'upgrade_required', requiredTier: 'pro' };
|
|
110
|
-
}
|
|
111
|
-
if (error.status === 401) {
|
|
112
|
-
return { error: 'auth_required' };
|
|
113
|
-
}
|
|
114
|
-
if (error.status === 404) {
|
|
115
|
-
return { error: 'not_found' };
|
|
116
|
-
}
|
|
117
|
-
return { error: 'network_error', message: error.message };
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* List skills from API (thin client)
|
|
123
|
-
*/
|
|
124
|
-
async function listSkills(options = {}) {
|
|
125
|
-
const { tierFilter, category } = options;
|
|
126
|
-
|
|
127
|
-
// Check authentication
|
|
128
|
-
if (!auth.isAuthenticated()) {
|
|
129
|
-
console.log(`
|
|
130
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Skills${utils.COLORS.reset}
|
|
131
|
-
${utils.COLORS.red}Authentication required${utils.COLORS.reset}
|
|
132
|
-
|
|
133
|
-
${utils.COLORS.dim}Skills are served from the API. Please log in:${utils.COLORS.reset}
|
|
134
|
-
${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
|
|
135
|
-
`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const tierLabel = tierFilter ? ` (${tierFilter} tier)` : '';
|
|
140
|
-
console.log(`
|
|
141
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Skills${utils.COLORS.reset}${tierLabel}
|
|
142
|
-
${utils.COLORS.dim}Code patterns served from API${utils.COLORS.reset}
|
|
143
|
-
`);
|
|
144
|
-
|
|
145
|
-
const spinner = utils.createSpinner('Loading skills...');
|
|
146
|
-
spinner.start();
|
|
147
|
-
|
|
148
|
-
const result = await fetchSkillsList({ category });
|
|
149
|
-
|
|
150
|
-
if (result.error === 'auth_required') {
|
|
151
|
-
spinner.fail('Authentication required');
|
|
152
|
-
console.log(`${utils.COLORS.dim}Run: bootspring auth login${utils.COLORS.reset}`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (result.error === 'network_error') {
|
|
157
|
-
spinner.fail('Failed to load skills');
|
|
158
|
-
console.log(`${utils.COLORS.dim}Check your internet connection.${utils.COLORS.reset}`);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
spinner.succeed(`Loaded ${result.skills?.length || 0} skills`);
|
|
163
|
-
|
|
164
|
-
// Group skills by category
|
|
165
|
-
const categories = {};
|
|
166
|
-
for (const skill of result.skills || []) {
|
|
167
|
-
// Apply tier filter if specified
|
|
168
|
-
if (tierFilter && skill.tier !== tierFilter) continue;
|
|
169
|
-
|
|
170
|
-
if (!categories[skill.category]) {
|
|
171
|
-
categories[skill.category] = [];
|
|
172
|
-
}
|
|
173
|
-
categories[skill.category].push(skill);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Display by category
|
|
177
|
-
for (const [cat, catSkills] of Object.entries(categories).sort()) {
|
|
178
|
-
console.log(`\n${utils.COLORS.bold}${cat}${utils.COLORS.reset}`);
|
|
179
|
-
for (const skill of catSkills) {
|
|
180
|
-
const tierBadge = formatTierBadge(skill.tier);
|
|
181
|
-
const lockIcon = skill.accessible ? '' : ` ${utils.COLORS.red}🔒${utils.COLORS.reset}`;
|
|
182
|
-
const description = skill.description ? ` - ${skill.description}` : '';
|
|
183
|
-
console.log(` ${utils.COLORS.cyan}${skill.id}${utils.COLORS.reset} ${tierBadge}${lockIcon}${description}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const accessibleCount = (result.skills || []).filter(s => s.accessible).length;
|
|
188
|
-
const lockedCount = (result.skills || []).length - accessibleCount;
|
|
189
|
-
|
|
190
|
-
console.log(`\n${utils.COLORS.dim}${accessibleCount} skills available, ${lockedCount} locked${utils.COLORS.reset}`);
|
|
191
|
-
console.log(`${utils.COLORS.dim}Your tier: ${result.userTier || 'free'}${utils.COLORS.reset}`);
|
|
192
|
-
console.log(`${utils.COLORS.dim}Use "bootspring skill show <id>" to view a skill${utils.COLORS.reset}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Show skill content from API (thin client)
|
|
197
|
-
*/
|
|
198
|
-
async function showSkill(skillId, _options = {}) {
|
|
199
|
-
// Check authentication first
|
|
200
|
-
if (!auth.isAuthenticated()) {
|
|
201
|
-
console.log(`
|
|
202
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Skill: ${skillId}${utils.COLORS.reset}
|
|
203
|
-
${utils.COLORS.red}Authentication required${utils.COLORS.reset}
|
|
204
|
-
|
|
205
|
-
${utils.COLORS.dim}Skill content is served from the API. Please log in:${utils.COLORS.reset}
|
|
206
|
-
${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
|
|
207
|
-
`);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const isMCP = utils.isMCPContext();
|
|
212
|
-
|
|
213
|
-
const spinner = utils.createSpinner('Loading skill...');
|
|
214
|
-
spinner.start();
|
|
215
|
-
|
|
216
|
-
const result = await fetchSkillContent(skillId);
|
|
217
|
-
|
|
218
|
-
if (result.error === 'auth_required') {
|
|
219
|
-
spinner.fail('Authentication required');
|
|
220
|
-
console.log(`${utils.COLORS.dim}Run: bootspring auth login${utils.COLORS.reset}`);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (result.error === 'upgrade_required') {
|
|
225
|
-
spinner.fail('Upgrade required');
|
|
226
|
-
const promptContext = tierEnforcement.getUpgradePromptContext(`skill: ${skillId}`, result.requiredTier || 'pro', {
|
|
227
|
-
capability: 'premium_pattern',
|
|
228
|
-
action: 'skill_show'
|
|
229
|
-
});
|
|
230
|
-
trackTelemetry('premium_prompted', {
|
|
231
|
-
skillId,
|
|
232
|
-
...promptContext,
|
|
233
|
-
reason: 'upgrade_required'
|
|
234
|
-
});
|
|
235
|
-
console.log(tierEnforcement.getUpgradePrompt(`skill: ${skillId}`, result.requiredTier || 'pro', {
|
|
236
|
-
capability: promptContext.capability,
|
|
237
|
-
action: promptContext.action,
|
|
238
|
-
placement: promptContext.placement,
|
|
239
|
-
variant: promptContext.variant
|
|
240
|
-
}));
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (result.error === 'not_found') {
|
|
245
|
-
spinner.fail('Skill not found');
|
|
246
|
-
utils.print.dim('Try: bootspring skill list');
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (result.error === 'integrity_error') {
|
|
251
|
-
spinner.fail('Integrity verification failed');
|
|
252
|
-
console.log(`${utils.COLORS.dim}${result.message || 'Skill payload checksum mismatch.'}${utils.COLORS.reset}`);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (result.error === 'network_error') {
|
|
257
|
-
spinner.fail('Failed to load skill');
|
|
258
|
-
console.log(`${utils.COLORS.dim}Check your internet connection.${utils.COLORS.reset}`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
spinner.succeed('Skill loaded');
|
|
263
|
-
|
|
264
|
-
const tierBadge = formatTierBadge(result.tier || 'free');
|
|
265
|
-
|
|
266
|
-
console.log(`
|
|
267
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}${result.name || skillId}${utils.COLORS.reset} ${tierBadge}
|
|
268
|
-
${utils.COLORS.dim}ID: ${result.id || skillId} | Tier: ${result.tier || 'free'}${utils.COLORS.reset}
|
|
269
|
-
`);
|
|
270
|
-
|
|
271
|
-
// Track successful access
|
|
272
|
-
trackTelemetry('skill_accessed', {
|
|
273
|
-
skillId: result.id || skillId,
|
|
274
|
-
tier: result.tier
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
const unlockedPremium = entitlements.isExternalSkill(result.id || skillId) || isPremiumTier(result.tier);
|
|
278
|
-
if (unlockedPremium) {
|
|
279
|
-
const promptContext = tierEnforcement.getUpgradePromptContext(`skill: ${result.id || skillId}`, String(result.tier || 'pro'), {
|
|
280
|
-
capability: entitlements.isExternalSkill(result.id || skillId) ? 'external_skill' : 'premium_pattern',
|
|
281
|
-
action: 'skill_show'
|
|
282
|
-
});
|
|
283
|
-
trackTelemetry('premium_unlocked', {
|
|
284
|
-
skillId: result.id || skillId,
|
|
285
|
-
skillTier: result.tier || 'free',
|
|
286
|
-
...promptContext
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// In MCP mode, output full content
|
|
291
|
-
if (isMCP) {
|
|
292
|
-
console.log(result.content);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// CLI mode - show content with truncation notice
|
|
297
|
-
const contentLines = result.content.split('\n');
|
|
298
|
-
const previewLines = contentLines.slice(0, 40);
|
|
299
|
-
const hasMore = contentLines.length > 40;
|
|
300
|
-
|
|
301
|
-
console.log(previewLines.join('\n'));
|
|
302
|
-
|
|
303
|
-
if (hasMore) {
|
|
304
|
-
console.log(`\n${utils.COLORS.dim}... (${contentLines.length - 40} more lines)${utils.COLORS.reset}`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
console.log(`
|
|
308
|
-
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
309
|
-
Copy the pattern above and adapt it to your project.
|
|
310
|
-
|
|
311
|
-
${utils.COLORS.yellow}${utils.COLORS.bold}Full Content in MCP Mode${utils.COLORS.reset}
|
|
312
|
-
${utils.COLORS.dim}With MCP integration, skills are available directly in your AI assistant.${utils.COLORS.reset}
|
|
313
|
-
${utils.COLORS.cyan}bootspring mcp start${utils.COLORS.reset}
|
|
314
|
-
`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Search skills via API (thin client)
|
|
319
|
-
*/
|
|
320
|
-
async function searchSkills(query, options = {}) {
|
|
321
|
-
const { tierFilter } = options;
|
|
322
|
-
|
|
323
|
-
// Check authentication
|
|
324
|
-
if (!auth.isAuthenticated()) {
|
|
325
|
-
console.log(`
|
|
326
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}Search: "${query}"${utils.COLORS.reset}
|
|
327
|
-
${utils.COLORS.red}Authentication required${utils.COLORS.reset}
|
|
328
|
-
|
|
329
|
-
${utils.COLORS.dim}Skills are served from the API. Please log in:${utils.COLORS.reset}
|
|
330
|
-
${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
|
|
331
|
-
`);
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const spinner = utils.createSpinner('Searching...');
|
|
336
|
-
spinner.start();
|
|
337
|
-
|
|
338
|
-
const result = await fetchSkillsList({ search: query });
|
|
339
|
-
|
|
340
|
-
if (result.error) {
|
|
341
|
-
spinner.fail('Search failed');
|
|
342
|
-
console.log(`${utils.COLORS.dim}${result.message || 'Check your internet connection.'}${utils.COLORS.reset}`);
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
spinner.succeed('Search complete');
|
|
347
|
-
|
|
348
|
-
let skills = result.skills || [];
|
|
349
|
-
|
|
350
|
-
// Apply tier filter if specified
|
|
351
|
-
if (tierFilter) {
|
|
352
|
-
skills = skills.filter(s => s.tier === tierFilter);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const tierLabel = tierFilter ? ` (${tierFilter} tier)` : '';
|
|
356
|
-
console.log(`
|
|
357
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}Search Results${utils.COLORS.reset}${tierLabel}
|
|
358
|
-
${utils.COLORS.dim}Query: "${query}"${utils.COLORS.reset}
|
|
359
|
-
`);
|
|
360
|
-
|
|
361
|
-
if (skills.length === 0) {
|
|
362
|
-
utils.print.warning('No matching skills found');
|
|
363
|
-
utils.print.dim('Try: bootspring skill list');
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
for (const skill of skills) {
|
|
368
|
-
const tierBadge = formatTierBadge(skill.tier);
|
|
369
|
-
const lockIcon = skill.accessible ? '' : ` ${utils.COLORS.red}🔒${utils.COLORS.reset}`;
|
|
370
|
-
const description = skill.description ? ` - ${skill.description}` : '';
|
|
371
|
-
console.log(` ${utils.COLORS.cyan}${skill.id}${utils.COLORS.reset} ${tierBadge}${lockIcon}${description}`);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const accessibleCount = skills.filter(s => s.accessible).length;
|
|
375
|
-
const lockedCount = skills.length - accessibleCount;
|
|
376
|
-
|
|
377
|
-
console.log(`\n${utils.COLORS.dim}${skills.length} match(es)${utils.COLORS.reset}`);
|
|
378
|
-
if (lockedCount > 0) {
|
|
379
|
-
console.log(`${utils.COLORS.dim}${lockedCount} result(s) require upgrade${utils.COLORS.reset}`);
|
|
380
|
-
console.log(`${utils.COLORS.dim}Run: bootspring billing upgrade${utils.COLORS.reset}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Sync is no longer needed - thin client fetches from API
|
|
386
|
-
* @deprecated
|
|
387
|
-
*/
|
|
388
|
-
async function syncCatalog() {
|
|
389
|
-
console.log(`
|
|
390
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}Sync Not Required${utils.COLORS.reset}
|
|
391
|
-
|
|
392
|
-
${utils.COLORS.dim}Skills are now served directly from the API.${utils.COLORS.reset}
|
|
393
|
-
${utils.COLORS.dim}No local sync needed - content is always up to date.${utils.COLORS.reset}
|
|
394
|
-
|
|
395
|
-
${utils.COLORS.bold}To view available skills:${utils.COLORS.reset}
|
|
396
|
-
${utils.COLORS.cyan}bootspring skill list${utils.COLORS.reset}
|
|
397
|
-
`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function showHelp() {
|
|
401
|
-
console.log(`
|
|
402
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring Skill Command${utils.COLORS.reset}
|
|
403
|
-
|
|
404
|
-
${utils.COLORS.cyan}Usage:${utils.COLORS.reset}
|
|
405
|
-
bootspring skill <command> [args] [options]
|
|
406
|
-
|
|
407
|
-
${utils.COLORS.cyan}Commands:${utils.COLORS.reset}
|
|
408
|
-
${utils.COLORS.cyan}list${utils.COLORS.reset} List built-in skills
|
|
409
|
-
${utils.COLORS.cyan}show${utils.COLORS.reset} <id> Show skill content
|
|
410
|
-
${utils.COLORS.cyan}search${utils.COLORS.reset} <query> Search skills
|
|
411
|
-
|
|
412
|
-
${utils.COLORS.cyan}Options:${utils.COLORS.reset}
|
|
413
|
-
${utils.COLORS.cyan}--tier-filter <tier>${utils.COLORS.reset} Filter by tier: free or pro
|
|
414
|
-
${utils.COLORS.cyan}--category <cat>${utils.COLORS.reset} Filter by category
|
|
415
|
-
|
|
416
|
-
${utils.COLORS.cyan}Tier Information:${utils.COLORS.reset}
|
|
417
|
-
Skills are labeled with their tier: ${utils.COLORS.green}[FREE]${utils.COLORS.reset} or ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
|
|
418
|
-
- FREE tier patterns are available to all authenticated users
|
|
419
|
-
- PRO tier patterns require a Pro subscription
|
|
420
|
-
|
|
421
|
-
${utils.COLORS.cyan}Authentication:${utils.COLORS.reset}
|
|
422
|
-
All skill commands require authentication.
|
|
423
|
-
Run: ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
|
|
424
|
-
|
|
425
|
-
${utils.COLORS.cyan}Examples:${utils.COLORS.reset}
|
|
426
|
-
bootspring skill list
|
|
427
|
-
bootspring skill list --tier-filter=free
|
|
428
|
-
bootspring skill search auth
|
|
429
|
-
bootspring skill show auth/clerk
|
|
430
|
-
bootspring skill show api/route-handler
|
|
431
|
-
`);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Normalize tier filter value
|
|
436
|
-
*/
|
|
437
|
-
function normalizeTierFilter(value) {
|
|
438
|
-
if (!value) return undefined;
|
|
439
|
-
const normalized = String(value).trim().toLowerCase();
|
|
440
|
-
if (normalized === 'free' || normalized === 'pro' || normalized === 'premium') {
|
|
441
|
-
// Treat 'premium' as 'pro' for filtering purposes
|
|
442
|
-
return normalized === 'premium' ? 'pro' : normalized;
|
|
443
|
-
}
|
|
444
|
-
return undefined;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Run skill command
|
|
449
|
-
*/
|
|
450
|
-
async function run(args) {
|
|
451
|
-
const parsedArgs = utils.parseArgs(args);
|
|
452
|
-
const subcommand = parsedArgs._[0] || 'list';
|
|
453
|
-
const subargs = parsedArgs._.slice(1);
|
|
454
|
-
|
|
455
|
-
// Parse tier filter (for list/search filtering) separately from access tier
|
|
456
|
-
const tierFilter = normalizeTierFilter(parsedArgs['tier-filter'] || parsedArgs['filter-tier']);
|
|
457
|
-
|
|
458
|
-
switch (subcommand) {
|
|
459
|
-
case 'list':
|
|
460
|
-
await listSkills({ tierFilter, category: parsedArgs.category });
|
|
461
|
-
break;
|
|
462
|
-
|
|
463
|
-
case 'show':
|
|
464
|
-
if (!subargs[0]) {
|
|
465
|
-
utils.print.error('Please specify a skill ID');
|
|
466
|
-
utils.print.dim('Usage: bootspring skill show <id>');
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
await showSkill(subargs[0], {});
|
|
470
|
-
break;
|
|
471
|
-
|
|
472
|
-
case 'search':
|
|
473
|
-
if (!subargs[0]) {
|
|
474
|
-
utils.print.error('Please specify a search query');
|
|
475
|
-
utils.print.dim('Usage: bootspring skill search <query>');
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
await searchSkills(subargs.join(' '), { tierFilter });
|
|
479
|
-
break;
|
|
480
|
-
|
|
481
|
-
case 'sync':
|
|
482
|
-
await syncCatalog();
|
|
483
|
-
break;
|
|
484
|
-
|
|
485
|
-
case 'help':
|
|
486
|
-
case '-h':
|
|
487
|
-
case '--help':
|
|
488
|
-
showHelp();
|
|
489
|
-
break;
|
|
490
|
-
|
|
491
|
-
default:
|
|
492
|
-
// Shortcut: `bootspring skill auth/clerk`
|
|
493
|
-
await showSkill(subcommand, {});
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
module.exports = {
|
|
498
|
-
run,
|
|
499
|
-
listSkills,
|
|
500
|
-
showSkill,
|
|
501
|
-
searchSkills,
|
|
502
|
-
syncCatalog
|
|
503
|
-
};
|