@girardmedia/bootspring 1.2.0 → 2.0.3

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.
Files changed (253) hide show
  1. package/README.md +107 -14
  2. package/bin/bootspring.js +166 -27
  3. package/cli/agent.js +189 -17
  4. package/cli/analyze.js +499 -0
  5. package/cli/audit.js +557 -0
  6. package/cli/auth.js +495 -38
  7. package/cli/billing.js +302 -0
  8. package/cli/build.js +695 -0
  9. package/cli/business.js +109 -26
  10. package/cli/checkpoint-utils.js +168 -0
  11. package/cli/checkpoint.js +639 -0
  12. package/cli/cloud-sync.js +447 -0
  13. package/cli/content.js +198 -0
  14. package/cli/context.js +1 -1
  15. package/cli/deploy.js +543 -0
  16. package/cli/fundraise.js +112 -50
  17. package/cli/github-cmd.js +435 -0
  18. package/cli/health.js +477 -0
  19. package/cli/init.js +84 -13
  20. package/cli/legal.js +107 -95
  21. package/cli/log.js +2 -2
  22. package/cli/loop.js +976 -73
  23. package/cli/manager.js +711 -0
  24. package/cli/metrics.js +480 -0
  25. package/cli/monitor.js +812 -0
  26. package/cli/onboard.js +521 -0
  27. package/cli/orchestrator.js +12 -24
  28. package/cli/prd.js +594 -0
  29. package/cli/preseed-start.js +1483 -0
  30. package/cli/preseed.js +2302 -0
  31. package/cli/project.js +436 -0
  32. package/cli/quality.js +233 -0
  33. package/cli/security.js +913 -0
  34. package/cli/seed.js +1441 -5
  35. package/cli/skill.js +273 -211
  36. package/cli/suggest.js +989 -0
  37. package/cli/switch.js +453 -0
  38. package/cli/visualize.js +527 -0
  39. package/cli/watch.js +769 -0
  40. package/cli/workspace.js +607 -0
  41. package/core/analyze-workflow.js +1134 -0
  42. package/core/api-client.js +535 -22
  43. package/core/audit-workflow.js +1350 -0
  44. package/core/build-orchestrator.js +480 -0
  45. package/core/build-state.js +577 -0
  46. package/core/checkpoint-engine.js +408 -0
  47. package/core/config.js +1109 -26
  48. package/core/context-loader.js +21 -1
  49. package/core/deploy-workflow.js +836 -0
  50. package/core/entitlements.js +93 -22
  51. package/core/github-sync.js +610 -0
  52. package/core/index.js +8 -1
  53. package/core/ingest.js +1111 -0
  54. package/core/metrics-engine.js +768 -0
  55. package/core/onboard-workflow.js +1007 -0
  56. package/core/preseed-workflow.js +934 -0
  57. package/core/preseed.js +1617 -0
  58. package/core/project-context.js +325 -0
  59. package/core/project-state.js +694 -0
  60. package/core/r2-sync.js +583 -0
  61. package/core/scaffold.js +525 -7
  62. package/core/session.js +258 -0
  63. package/core/task-extractor.js +758 -0
  64. package/core/telemetry.js +28 -6
  65. package/core/tier-enforcement.js +737 -0
  66. package/core/utils.js +38 -14
  67. package/generators/questionnaire.js +15 -12
  68. package/generators/sections/ai.js +7 -7
  69. package/generators/sections/content.js +300 -0
  70. package/generators/sections/index.js +3 -0
  71. package/generators/sections/plugins.js +7 -6
  72. package/generators/templates/build-planning.template.js +596 -0
  73. package/generators/templates/content.template.js +819 -0
  74. package/generators/templates/index.js +2 -1
  75. package/hooks/git-autopilot.js +1250 -0
  76. package/hooks/index.js +9 -0
  77. package/intelligence/agent-collab.js +2057 -0
  78. package/intelligence/auto-suggest.js +634 -0
  79. package/intelligence/content-gen.js +1589 -0
  80. package/intelligence/cross-project.js +1647 -0
  81. package/intelligence/index.js +184 -0
  82. package/intelligence/learning/insights.json +517 -7
  83. package/intelligence/learning/pattern-learner.js +1008 -14
  84. package/intelligence/memory/decision-tracker.js +1431 -31
  85. package/intelligence/memory/decisions.jsonl +0 -0
  86. package/intelligence/orchestrator.js +2896 -1
  87. package/intelligence/prd.js +92 -1
  88. package/intelligence/recommendation-weights.json +14 -2
  89. package/intelligence/recommendations.js +463 -9
  90. package/intelligence/workflow-composer.js +1451 -0
  91. package/marketplace/index.d.ts +324 -0
  92. package/marketplace/index.js +1921 -0
  93. package/mcp/contracts/mcp-contract.v1.json +342 -4
  94. package/mcp/registry.js +680 -3
  95. package/mcp/response-formatter.js +23 -0
  96. package/mcp/tools/assist-tool.js +78 -4
  97. package/mcp/tools/autopilot-tool.js +408 -0
  98. package/mcp/tools/content-tool.js +571 -0
  99. package/mcp/tools/dashboard-tool.js +251 -5
  100. package/mcp/tools/mvp-tool.js +344 -0
  101. package/mcp/tools/plugin-tool.js +23 -1
  102. package/mcp/tools/prd-tool.js +579 -0
  103. package/mcp/tools/seed-tool.js +447 -0
  104. package/mcp/tools/skill-tool.js +43 -14
  105. package/mcp/tools/suggest-tool.js +147 -0
  106. package/package.json +15 -6
  107. package/agents/README.md +0 -93
  108. package/agents/ai-integration-expert/context.md +0 -386
  109. package/agents/api-expert/context.md +0 -416
  110. package/agents/architecture-expert/context.md +0 -454
  111. package/agents/auth-expert/context.md +0 -399
  112. package/agents/backend-expert/context.md +0 -483
  113. package/agents/business-strategy-expert/context.md +0 -180
  114. package/agents/code-review-expert/context.md +0 -365
  115. package/agents/competitive-analysis-expert/context.md +0 -239
  116. package/agents/data-modeling-expert/context.md +0 -352
  117. package/agents/database-expert/context.md +0 -250
  118. package/agents/devops-expert/context.md +0 -446
  119. package/agents/email-expert/context.md +0 -379
  120. package/agents/financial-expert/context.md +0 -213
  121. package/agents/frontend-expert/context.md +0 -364
  122. package/agents/fundraising-expert/context.md +0 -257
  123. package/agents/growth-expert/context.md +0 -249
  124. package/agents/index.js +0 -140
  125. package/agents/investor-relations-expert/context.md +0 -266
  126. package/agents/legal-expert/context.md +0 -284
  127. package/agents/marketing-expert/context.md +0 -236
  128. package/agents/monitoring-expert/context.md +0 -362
  129. package/agents/operations-expert/context.md +0 -279
  130. package/agents/partnerships-expert/context.md +0 -286
  131. package/agents/payment-expert/context.md +0 -340
  132. package/agents/performance-expert/context.md +0 -377
  133. package/agents/private-equity-expert/context.md +0 -246
  134. package/agents/railway-expert/context.md +0 -284
  135. package/agents/research-expert/context.md +0 -245
  136. package/agents/sales-expert/context.md +0 -241
  137. package/agents/security-expert/context.md +0 -343
  138. package/agents/testing-expert/context.md +0 -414
  139. package/agents/ui-ux-expert/context.md +0 -448
  140. package/agents/vercel-expert/context.md +0 -426
  141. package/skills/index.js +0 -787
  142. package/skills/patterns/README.md +0 -163
  143. package/skills/patterns/ai/agents.md +0 -281
  144. package/skills/patterns/ai/claude.md +0 -138
  145. package/skills/patterns/ai/embeddings.md +0 -150
  146. package/skills/patterns/ai/rag.md +0 -266
  147. package/skills/patterns/ai/streaming.md +0 -170
  148. package/skills/patterns/ai/structured-output.md +0 -162
  149. package/skills/patterns/ai/tools.md +0 -154
  150. package/skills/patterns/analytics/tracking.md +0 -220
  151. package/skills/patterns/api/errors.md +0 -296
  152. package/skills/patterns/api/graphql.md +0 -440
  153. package/skills/patterns/api/middleware.md +0 -279
  154. package/skills/patterns/api/openapi.md +0 -285
  155. package/skills/patterns/api/rate-limiting.md +0 -231
  156. package/skills/patterns/api/route-handler.md +0 -217
  157. package/skills/patterns/api/server-action.md +0 -249
  158. package/skills/patterns/api/versioning.md +0 -443
  159. package/skills/patterns/api/webhooks.md +0 -247
  160. package/skills/patterns/auth/clerk.md +0 -132
  161. package/skills/patterns/auth/mfa.md +0 -313
  162. package/skills/patterns/auth/nextauth.md +0 -140
  163. package/skills/patterns/auth/oauth.md +0 -237
  164. package/skills/patterns/auth/rbac.md +0 -152
  165. package/skills/patterns/auth/session-management.md +0 -367
  166. package/skills/patterns/auth/session.md +0 -120
  167. package/skills/patterns/database/audit.md +0 -177
  168. package/skills/patterns/database/migrations.md +0 -177
  169. package/skills/patterns/database/pagination.md +0 -230
  170. package/skills/patterns/database/pooling.md +0 -357
  171. package/skills/patterns/database/prisma.md +0 -180
  172. package/skills/patterns/database/relations.md +0 -187
  173. package/skills/patterns/database/seeding.md +0 -246
  174. package/skills/patterns/database/soft-delete.md +0 -153
  175. package/skills/patterns/database/transactions.md +0 -162
  176. package/skills/patterns/deployment/ci-cd.md +0 -231
  177. package/skills/patterns/deployment/docker.md +0 -188
  178. package/skills/patterns/deployment/monitoring.md +0 -387
  179. package/skills/patterns/deployment/vercel.md +0 -160
  180. package/skills/patterns/email/resend.md +0 -143
  181. package/skills/patterns/email/templates.md +0 -245
  182. package/skills/patterns/email/transactional.md +0 -503
  183. package/skills/patterns/email/verification.md +0 -176
  184. package/skills/patterns/files/download.md +0 -243
  185. package/skills/patterns/files/upload.md +0 -239
  186. package/skills/patterns/i18n/nextintl.md +0 -188
  187. package/skills/patterns/logging/structured.md +0 -292
  188. package/skills/patterns/notifications/email-queue.md +0 -248
  189. package/skills/patterns/notifications/push.md +0 -279
  190. package/skills/patterns/payments/checkout.md +0 -303
  191. package/skills/patterns/payments/invoices.md +0 -287
  192. package/skills/patterns/payments/portal.md +0 -245
  193. package/skills/patterns/payments/stripe.md +0 -272
  194. package/skills/patterns/payments/subscriptions.md +0 -300
  195. package/skills/patterns/payments/usage.md +0 -279
  196. package/skills/patterns/performance/caching.md +0 -276
  197. package/skills/patterns/performance/code-splitting.md +0 -233
  198. package/skills/patterns/performance/edge.md +0 -254
  199. package/skills/patterns/performance/isr.md +0 -266
  200. package/skills/patterns/performance/lazy-loading.md +0 -281
  201. package/skills/patterns/realtime/sse.md +0 -327
  202. package/skills/patterns/realtime/websockets.md +0 -336
  203. package/skills/patterns/search/filtering.md +0 -329
  204. package/skills/patterns/search/fulltext.md +0 -260
  205. package/skills/patterns/security/audit-logging.md +0 -444
  206. package/skills/patterns/security/csrf.md +0 -234
  207. package/skills/patterns/security/headers.md +0 -252
  208. package/skills/patterns/security/sanitization.md +0 -258
  209. package/skills/patterns/security/secrets.md +0 -261
  210. package/skills/patterns/security/validation.md +0 -268
  211. package/skills/patterns/security/xss.md +0 -229
  212. package/skills/patterns/seo/metadata.md +0 -252
  213. package/skills/patterns/state/context.md +0 -349
  214. package/skills/patterns/state/react-query.md +0 -313
  215. package/skills/patterns/state/url-state.md +0 -482
  216. package/skills/patterns/state/zustand.md +0 -262
  217. package/skills/patterns/testing/api.md +0 -259
  218. package/skills/patterns/testing/component.md +0 -233
  219. package/skills/patterns/testing/coverage.md +0 -207
  220. package/skills/patterns/testing/fixtures.md +0 -225
  221. package/skills/patterns/testing/integration.md +0 -436
  222. package/skills/patterns/testing/mocking.md +0 -177
  223. package/skills/patterns/testing/playwright.md +0 -162
  224. package/skills/patterns/testing/snapshot.md +0 -175
  225. package/skills/patterns/testing/vitest.md +0 -307
  226. package/skills/patterns/ui/accordions.md +0 -395
  227. package/skills/patterns/ui/cards.md +0 -299
  228. package/skills/patterns/ui/dropdowns.md +0 -476
  229. package/skills/patterns/ui/empty-states.md +0 -320
  230. package/skills/patterns/ui/forms.md +0 -405
  231. package/skills/patterns/ui/inputs.md +0 -319
  232. package/skills/patterns/ui/layouts.md +0 -282
  233. package/skills/patterns/ui/loading.md +0 -291
  234. package/skills/patterns/ui/modals.md +0 -338
  235. package/skills/patterns/ui/navigation.md +0 -374
  236. package/skills/patterns/ui/tables.md +0 -407
  237. package/skills/patterns/ui/toasts.md +0 -300
  238. package/skills/patterns/ui/tooltips.md +0 -396
  239. package/skills/patterns/utils/dates.md +0 -435
  240. package/skills/patterns/utils/errors.md +0 -451
  241. package/skills/patterns/utils/formatting.md +0 -345
  242. package/skills/patterns/utils/validation.md +0 -434
  243. package/templates/bootspring.config.js +0 -83
  244. package/templates/business/business-model-canvas.md +0 -246
  245. package/templates/business/business-plan.md +0 -266
  246. package/templates/business/competitive-analysis.md +0 -312
  247. package/templates/fundraising/data-room-checklist.md +0 -300
  248. package/templates/fundraising/investor-research.md +0 -243
  249. package/templates/fundraising/pitch-deck-outline.md +0 -253
  250. package/templates/legal/gdpr-checklist.md +0 -339
  251. package/templates/legal/privacy-policy.md +0 -285
  252. package/templates/legal/terms-of-service.md +0 -222
  253. package/templates/mcp.json +0 -9
package/cli/skill.js CHANGED
@@ -5,8 +5,12 @@
5
5
 
6
6
  const utils = require('../core/utils');
7
7
  const entitlements = require('../core/entitlements');
8
+ const tierEnforcement = require('../core/tier-enforcement');
8
9
  const telemetry = require('../core/telemetry');
9
- const skills = require('../skills');
10
+ const api = require('../core/api-client');
11
+ const auth = require('../core/auth');
12
+
13
+ // Note: skills module removed - all content fetched from API (thin client)
10
14
 
11
15
  function trackTelemetry(event, payload) {
12
16
  try {
@@ -16,234 +20,302 @@ function trackTelemetry(event, payload) {
16
20
  }
17
21
  }
18
22
 
19
- function printBuiltInSkills() {
20
- const categories = skills.getCategories();
21
- for (const category of categories) {
22
- const patterns = skills.getPatternsByCategory(category);
23
- if (patterns.length === 0) continue;
24
-
25
- console.log(`${utils.COLORS.bold}${category}${utils.COLORS.reset}`);
26
- for (const pattern of patterns) {
27
- const id = `${category}/${pattern}`;
28
- const metadata = skills.getSkillMetadata(id);
29
- const description = metadata?.description ? ` - ${metadata.description}` : '';
30
- console.log(` ${utils.COLORS.cyan}${id}${utils.COLORS.reset}${description}`);
23
+ // Use centralized tier badge formatting
24
+ const { formatTierBadge } = tierEnforcement;
25
+
26
+ /**
27
+ * Fetch skills list from API (thin client)
28
+ */
29
+ async function fetchSkillsList(options = {}) {
30
+ if (!auth.isAuthenticated()) {
31
+ return { error: 'auth_required' };
32
+ }
33
+
34
+ try {
35
+ const response = await api.listSkills(options);
36
+ return response;
37
+ } catch (error) {
38
+ if (error.status === 401) {
39
+ return { error: 'auth_required' };
31
40
  }
32
- console.log('');
41
+ return { error: 'network_error', message: error.message };
33
42
  }
34
43
  }
35
44
 
36
- function printExternalSkills(options = {}) {
37
- const {
38
- limit = 20,
39
- accessOptions = {}
40
- } = options;
45
+ /**
46
+ * Fetch skill content from API (thin client)
47
+ */
48
+ async function fetchSkillContent(skillId) {
49
+ if (!auth.isAuthenticated()) {
50
+ return { error: 'auth_required' };
51
+ }
41
52
 
42
- if (!skills.hasExternalSkillLibrary()) {
43
- console.log(`${utils.COLORS.dim}No external skills catalog found${utils.COLORS.reset}\n`);
44
- return;
53
+ try {
54
+ const response = await api.getSkillContent(skillId);
55
+ return response;
56
+ } catch (error) {
57
+ if (error.status === 403) {
58
+ return { error: 'upgrade_required', requiredTier: 'pro' };
59
+ }
60
+ if (error.status === 401) {
61
+ return { error: 'auth_required' };
62
+ }
63
+ if (error.status === 404) {
64
+ return { error: 'not_found' };
65
+ }
66
+ return { error: 'network_error', message: error.message };
45
67
  }
68
+ }
69
+
70
+ /**
71
+ * List skills from API (thin client)
72
+ */
73
+ async function listSkills(options = {}) {
74
+ const { tierFilter, category } = options;
46
75
 
47
- const externalIds = skills.listExternalSkills();
48
- const { allowed: accessibleIds, denied } = entitlements.filterAccessibleSkills(externalIds, accessOptions);
49
- const shown = accessibleIds.slice(0, limit);
76
+ // Check authentication
77
+ if (!auth.isAuthenticated()) {
78
+ console.log(`
79
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Skills${utils.COLORS.reset}
80
+ ${utils.COLORS.red}Authentication required${utils.COLORS.reset}
50
81
 
51
- if (shown.length === 0) {
52
- console.log(`${utils.COLORS.bold}external${utils.COLORS.reset}`);
53
- console.log(` ${utils.COLORS.dim}No external skills available for current entitlement${utils.COLORS.reset}\n`);
54
- if (denied.length > 0) {
55
- utils.print.dim(denied[0].reason);
56
- }
82
+ ${utils.COLORS.dim}Skills are served from the API. Please log in:${utils.COLORS.reset}
83
+ ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
84
+ `);
57
85
  return;
58
86
  }
59
87
 
60
- console.log(`${utils.COLORS.bold}external${utils.COLORS.reset}`);
61
- for (const id of shown) {
62
- const metadata = skills.getSkillMetadata(id);
63
- const description = metadata?.description ? ` - ${metadata.description}` : '';
64
- console.log(` ${utils.COLORS.yellow}${id}${utils.COLORS.reset}${description}`);
65
- }
88
+ const tierLabel = tierFilter ? ` (${tierFilter} tier)` : '';
89
+ console.log(`
90
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Skills${utils.COLORS.reset}${tierLabel}
91
+ ${utils.COLORS.dim}Code patterns served from API${utils.COLORS.reset}
92
+ `);
93
+
94
+ const spinner = utils.createSpinner('Loading skills...');
95
+ spinner.start();
96
+
97
+ const result = await fetchSkillsList({ category });
66
98
 
67
- if (accessibleIds.length > shown.length) {
68
- const remaining = accessibleIds.length - shown.length;
69
- console.log(`\n${utils.COLORS.dim}${remaining} more external skills hidden.${utils.COLORS.reset}`);
70
- console.log(`${utils.COLORS.dim}Use --limit <n> or search with --external.${utils.COLORS.reset}`);
99
+ if (result.error === 'auth_required') {
100
+ spinner.fail('Authentication required');
101
+ console.log(`${utils.COLORS.dim}Run: bootspring auth login${utils.COLORS.reset}`);
102
+ return;
71
103
  }
72
- if (denied.length > 0) {
73
- console.log(`\n${utils.COLORS.dim}${denied.length} external skills locked by entitlement policy${utils.COLORS.reset}`);
104
+
105
+ if (result.error === 'network_error') {
106
+ spinner.fail('Failed to load skills');
107
+ console.log(`${utils.COLORS.dim}Check your internet connection.${utils.COLORS.reset}`);
108
+ return;
74
109
  }
75
- console.log('');
76
- }
77
110
 
78
- function listSkills(options = {}) {
79
- const {
80
- includeExternal = false,
81
- externalOnly = false,
82
- limit = 20,
83
- accessOptions = {}
84
- } = options;
111
+ spinner.succeed(`Loaded ${result.skills?.length || 0} skills`);
85
112
 
86
- console.log(`
87
- ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Skills${utils.COLORS.reset}
88
- ${utils.COLORS.dim}Built-in patterns + optional external skill catalog${utils.COLORS.reset}
89
- `);
113
+ // Group skills by category
114
+ const categories = {};
115
+ for (const skill of result.skills || []) {
116
+ // Apply tier filter if specified
117
+ if (tierFilter && skill.tier !== tierFilter) continue;
90
118
 
91
- if (!externalOnly) {
92
- printBuiltInSkills();
119
+ if (!categories[skill.category]) {
120
+ categories[skill.category] = [];
121
+ }
122
+ categories[skill.category].push(skill);
93
123
  }
94
124
 
95
- if (includeExternal || externalOnly) {
96
- printExternalSkills({ limit, accessOptions });
125
+ // Display by category
126
+ for (const [cat, catSkills] of Object.entries(categories).sort()) {
127
+ console.log(`\n${utils.COLORS.bold}${cat}${utils.COLORS.reset}`);
128
+ for (const skill of catSkills) {
129
+ const tierBadge = formatTierBadge(skill.tier);
130
+ const lockIcon = skill.accessible ? '' : ` ${utils.COLORS.red}🔒${utils.COLORS.reset}`;
131
+ const description = skill.description ? ` - ${skill.description}` : '';
132
+ console.log(` ${utils.COLORS.cyan}${skill.id}${utils.COLORS.reset} ${tierBadge}${lockIcon}${description}`);
133
+ }
97
134
  }
98
135
 
99
- const builtInCount = skills.listSkills().length;
100
- const externalCount = skills.hasExternalSkillLibrary() ? skills.listExternalSkills().length : 0;
101
- console.log(`${utils.COLORS.dim}${builtInCount} built-in skills available${utils.COLORS.reset}`);
102
- if (skills.hasExternalSkillLibrary()) {
103
- console.log(`${utils.COLORS.dim}${externalCount} external skills indexed${utils.COLORS.reset}`);
104
- }
136
+ const accessibleCount = (result.skills || []).filter(s => s.accessible).length;
137
+ const lockedCount = (result.skills || []).length - accessibleCount;
138
+
139
+ console.log(`\n${utils.COLORS.dim}${accessibleCount} skills available, ${lockedCount} locked${utils.COLORS.reset}`);
140
+ console.log(`${utils.COLORS.dim}Your tier: ${result.userTier || 'free'}${utils.COLORS.reset}`);
105
141
  console.log(`${utils.COLORS.dim}Use "bootspring skill show <id>" to view a skill${utils.COLORS.reset}`);
106
142
  }
107
143
 
108
- function showSkill(skillId, options = {}) {
109
- // Require MCP for viewing skill content
110
- if (!utils.requireMCP('Skill content viewing')) {
144
+ /**
145
+ * Show skill content from API (thin client)
146
+ */
147
+ async function showSkill(skillId, _options = {}) {
148
+ // Check authentication first
149
+ if (!auth.isAuthenticated()) {
150
+ console.log(`
151
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Skill: ${skillId}${utils.COLORS.reset}
152
+ ${utils.COLORS.red}Authentication required${utils.COLORS.reset}
153
+
154
+ ${utils.COLORS.dim}Skill content is served from the API. Please log in:${utils.COLORS.reset}
155
+ ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
156
+ `);
111
157
  return;
112
158
  }
113
159
 
114
- const {
115
- includeExternal = false,
116
- summary = false,
117
- sections,
118
- maxChars,
119
- accessOptions = {}
120
- } = options;
160
+ const isMCP = utils.isMCPContext();
161
+
162
+ const spinner = utils.createSpinner('Loading skill...');
163
+ spinner.start();
121
164
 
122
- const metadata = skills.getSkillMetadata(skillId);
123
- const resolvedSkillId = metadata?.id || skillId;
124
- const access = entitlements.checkSkillAccess(resolvedSkillId, accessOptions);
165
+ const result = await fetchSkillContent(skillId);
125
166
 
126
- if (!access.allowed) {
167
+ if (result.error === 'auth_required') {
168
+ spinner.fail('Authentication required');
169
+ console.log(`${utils.COLORS.dim}Run: bootspring auth login${utils.COLORS.reset}`);
170
+ return;
171
+ }
172
+
173
+ if (result.error === 'upgrade_required') {
174
+ spinner.fail('Upgrade required');
127
175
  trackTelemetry('premium_prompted', {
128
- capability: 'external_skill',
129
- skillId: resolvedSkillId,
130
- mode: access.context?.mode,
131
- tier: access.context?.tier,
132
- reason: access.code
176
+ capability: 'premium_pattern',
177
+ skillId,
178
+ reason: 'upgrade_required'
133
179
  });
134
- utils.print.error(access.reason);
180
+ console.log(tierEnforcement.getUpgradePrompt(`skill: ${skillId}`, result.requiredTier || 'pro'));
135
181
  return;
136
182
  }
137
183
 
138
- const rawContent = skills.loadSkill(skillId, { includeExternal });
139
-
140
- if (!rawContent) {
141
- utils.print.error(`Skill not found: ${skillId}`);
184
+ if (result.error === 'not_found') {
185
+ spinner.fail('Skill not found');
142
186
  utils.print.dim('Try: bootspring skill list');
143
- utils.print.dim('Or: bootspring skill search <query> --external');
144
187
  return;
145
188
  }
146
189
 
147
- const content = skills.formatSkillContent(rawContent, {
148
- summary,
149
- sections,
150
- maxChars
190
+ if (result.error === 'network_error') {
191
+ spinner.fail('Failed to load skill');
192
+ console.log(`${utils.COLORS.dim}Check your internet connection.${utils.COLORS.reset}`);
193
+ return;
194
+ }
195
+
196
+ spinner.succeed('Skill loaded');
197
+
198
+ const tierBadge = formatTierBadge(result.tier || 'free');
199
+
200
+ console.log(`
201
+ ${utils.COLORS.cyan}${utils.COLORS.bold}${result.name || skillId}${utils.COLORS.reset} ${tierBadge}
202
+ ${utils.COLORS.dim}ID: ${result.id || skillId} | Tier: ${result.tier || 'free'}${utils.COLORS.reset}
203
+ `);
204
+
205
+ // Track successful access
206
+ trackTelemetry('skill_accessed', {
207
+ skillId: result.id || skillId,
208
+ tier: result.tier
151
209
  });
152
210
 
153
- const headerName = metadata?.name || skillId;
154
- const source = metadata?.source || 'built-in';
155
- if (String(source).startsWith('external')) {
156
- trackTelemetry('premium_unlocked', {
157
- capability: 'external_skill',
158
- skillId: metadata?.id || skillId,
159
- mode: access.context?.mode,
160
- tier: access.context?.tier,
161
- reason: access.code
162
- });
211
+ // In MCP mode, output full content
212
+ if (isMCP) {
213
+ console.log(result.content);
214
+ return;
215
+ }
216
+
217
+ // CLI mode - show content with truncation notice
218
+ const contentLines = result.content.split('\n');
219
+ const previewLines = contentLines.slice(0, 40);
220
+ const hasMore = contentLines.length > 40;
221
+
222
+ console.log(previewLines.join('\n'));
223
+
224
+ if (hasMore) {
225
+ console.log(`\n${utils.COLORS.dim}... (${contentLines.length - 40} more lines)${utils.COLORS.reset}`);
163
226
  }
164
227
 
165
228
  console.log(`
166
- ${utils.COLORS.cyan}${utils.COLORS.bold}${headerName}${utils.COLORS.reset}
167
- ${utils.COLORS.dim}ID: ${metadata?.id || skillId} | Source: ${source}${utils.COLORS.reset}
168
- `);
229
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
230
+ Copy the pattern above and adapt it to your project.
169
231
 
170
- console.log(content);
232
+ ${utils.COLORS.yellow}${utils.COLORS.bold}Full Content in MCP Mode${utils.COLORS.reset}
233
+ ${utils.COLORS.dim}With MCP integration, skills are available directly in your AI assistant.${utils.COLORS.reset}
234
+ ${utils.COLORS.cyan}bootspring mcp start${utils.COLORS.reset}
235
+ `);
171
236
  }
172
237
 
173
- function searchSkills(query, options = {}) {
174
- const {
175
- includeExternal = false,
176
- externalOnly = false,
177
- limit = 20,
178
- accessOptions = {}
179
- } = options;
180
-
181
- let results = skills.searchSkills(query, {
182
- includeExternal: includeExternal || externalOnly,
183
- limit
184
- });
238
+ /**
239
+ * Search skills via API (thin client)
240
+ */
241
+ async function searchSkills(query, options = {}) {
242
+ const { tierFilter } = options;
243
+
244
+ // Check authentication
245
+ if (!auth.isAuthenticated()) {
246
+ console.log(`
247
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Search: "${query}"${utils.COLORS.reset}
248
+ ${utils.COLORS.red}Authentication required${utils.COLORS.reset}
249
+
250
+ ${utils.COLORS.dim}Skills are served from the API. Please log in:${utils.COLORS.reset}
251
+ ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
252
+ `);
253
+ return;
254
+ }
255
+
256
+ const spinner = utils.createSpinner('Searching...');
257
+ spinner.start();
258
+
259
+ const result = await fetchSkillsList({ search: query });
260
+
261
+ if (result.error) {
262
+ spinner.fail('Search failed');
263
+ console.log(`${utils.COLORS.dim}${result.message || 'Check your internet connection.'}${utils.COLORS.reset}`);
264
+ return;
265
+ }
266
+
267
+ spinner.succeed('Search complete');
268
+
269
+ let skills = result.skills || [];
185
270
 
186
- if (externalOnly) {
187
- results = results.filter(id => id.startsWith('external/'));
271
+ // Apply tier filter if specified
272
+ if (tierFilter) {
273
+ skills = skills.filter(s => s.tier === tierFilter);
188
274
  }
189
- const filtered = entitlements.filterAccessibleSkills(results, accessOptions);
190
- results = filtered.allowed;
191
275
 
276
+ const tierLabel = tierFilter ? ` (${tierFilter} tier)` : '';
192
277
  console.log(`
193
- ${utils.COLORS.cyan}${utils.COLORS.bold}Search Results${utils.COLORS.reset}
278
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Search Results${utils.COLORS.reset}${tierLabel}
194
279
  ${utils.COLORS.dim}Query: "${query}"${utils.COLORS.reset}
195
280
  `);
196
281
 
197
- if (results.length === 0) {
282
+ if (skills.length === 0) {
198
283
  utils.print.warning('No matching skills found');
199
284
  utils.print.dim('Try: bootspring skill list');
200
285
  return;
201
286
  }
202
287
 
203
- for (const id of results) {
204
- const metadata = skills.getSkillMetadata(id);
205
- const source = String(metadata?.source || '').startsWith('external') ? 'external' : 'built-in';
206
- const description = metadata?.description ? ` - ${metadata.description}` : '';
207
- const color = source === 'external' ? utils.COLORS.yellow : utils.COLORS.cyan;
208
- console.log(` ${color}${id}${utils.COLORS.reset} ${utils.COLORS.dim}[${source}]${utils.COLORS.reset}${description}`);
288
+ for (const skill of skills) {
289
+ const tierBadge = formatTierBadge(skill.tier);
290
+ const lockIcon = skill.accessible ? '' : ` ${utils.COLORS.red}🔒${utils.COLORS.reset}`;
291
+ const description = skill.description ? ` - ${skill.description}` : '';
292
+ console.log(` ${utils.COLORS.cyan}${skill.id}${utils.COLORS.reset} ${tierBadge}${lockIcon}${description}`);
209
293
  }
210
294
 
211
- console.log(`\n${utils.COLORS.dim}${results.length} match(es)${utils.COLORS.reset}`);
212
- if (filtered.denied.length > 0) {
213
- console.log(`${utils.COLORS.dim}${filtered.denied.length} result(s) hidden by entitlement policy${utils.COLORS.reset}`);
295
+ const accessibleCount = skills.filter(s => s.accessible).length;
296
+ const lockedCount = skills.length - accessibleCount;
297
+
298
+ console.log(`\n${utils.COLORS.dim}${skills.length} match(es)${utils.COLORS.reset}`);
299
+ if (lockedCount > 0) {
300
+ console.log(`${utils.COLORS.dim}${lockedCount} result(s) require upgrade${utils.COLORS.reset}`);
301
+ console.log(`${utils.COLORS.dim}Run: bootspring billing upgrade${utils.COLORS.reset}`);
214
302
  }
215
303
  }
216
304
 
217
- async function syncCatalog(options = {}) {
218
- const {
219
- manifestUrl,
220
- contentBaseUrl,
221
- token,
222
- timeoutMs,
223
- cacheDir,
224
- manifestPublicKey,
225
- requireManifestSignature
226
- } = options;
227
-
228
- const spinner = utils.createSpinner('Syncing external skill catalog').start();
229
- try {
230
- const result = await skills.syncExternalCatalog({
231
- manifestUrl,
232
- contentBaseUrl,
233
- token,
234
- timeoutMs,
235
- cacheDir,
236
- manifestPublicKey,
237
- requireManifestSignature
238
- });
239
- spinner.succeed(`Synced ${result.fetched} external skills`);
240
- if (result.skipped.length > 0) {
241
- utils.print.warning(`Skipped ${result.skipped.length} skill(s): ${result.skipped.join(', ')}`);
242
- }
243
- utils.print.dim(`Cache: ${result.manifestPath}`);
244
- } catch (error) {
245
- spinner.fail(`Sync failed: ${error.message}`);
246
- }
305
+ /**
306
+ * Sync is no longer needed - thin client fetches from API
307
+ * @deprecated
308
+ */
309
+ async function syncCatalog() {
310
+ console.log(`
311
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Sync Not Required${utils.COLORS.reset}
312
+
313
+ ${utils.COLORS.dim}Skills are now served directly from the API.${utils.COLORS.reset}
314
+ ${utils.COLORS.dim}No local sync needed - content is always up to date.${utils.COLORS.reset}
315
+
316
+ ${utils.COLORS.bold}To view available skills:${utils.COLORS.reset}
317
+ ${utils.COLORS.cyan}bootspring skill list${utils.COLORS.reset}
318
+ `);
247
319
  }
248
320
 
249
321
  function showHelp() {
@@ -257,36 +329,42 @@ ${utils.COLORS.cyan}Commands:${utils.COLORS.reset}
257
329
  ${utils.COLORS.cyan}list${utils.COLORS.reset} List built-in skills
258
330
  ${utils.COLORS.cyan}show${utils.COLORS.reset} <id> Show skill content
259
331
  ${utils.COLORS.cyan}search${utils.COLORS.reset} <query> Search skills
260
- ${utils.COLORS.cyan}sync${utils.COLORS.reset} Sync external catalog from remote manifest
261
332
 
262
333
  ${utils.COLORS.cyan}Options:${utils.COLORS.reset}
263
- ${utils.COLORS.cyan}--external${utils.COLORS.reset} Include external skills catalog
264
- ${utils.COLORS.cyan}--external-only${utils.COLORS.reset} External skills only
265
- ${utils.COLORS.cyan}--limit <n>${utils.COLORS.reset} Limit results (default: 20)
266
- ${utils.COLORS.cyan}--summary${utils.COLORS.reset} Show concise summary for skill show
267
- ${utils.COLORS.cyan}--sections a,b${utils.COLORS.reset} Show matching sections for skill show
268
- ${utils.COLORS.cyan}--max-chars <n>${utils.COLORS.reset} Cap output size for skill show
269
- ${utils.COLORS.cyan}--access-mode <mode>${utils.COLORS.reset} Access mode: local (default) or server
270
- ${utils.COLORS.cyan}--entitled <bool>${utils.COLORS.reset} Entitlement override for external skills
271
- ${utils.COLORS.cyan}--tier <tier>${utils.COLORS.reset} Tier override: free, pro, team, enterprise
272
- ${utils.COLORS.cyan}--manifest-url <url>${utils.COLORS.reset} Remote manifest URL for skill sync
273
- ${utils.COLORS.cyan}--content-base-url <url>${utils.COLORS.reset} Base URL for SKILL.md files when missing contentUrl
274
- ${utils.COLORS.cyan}--token <value>${utils.COLORS.reset} Bearer token for remote catalog APIs
275
- ${utils.COLORS.cyan}--cache-dir <path>${utils.COLORS.reset} Override local skill cache directory
276
- ${utils.COLORS.cyan}--manifest-public-key <pem>${utils.COLORS.reset} Public key for manifest signature verification
277
- ${utils.COLORS.cyan}--require-signature <bool>${utils.COLORS.reset} Require remote manifest signature validation
334
+ ${utils.COLORS.cyan}--tier-filter <tier>${utils.COLORS.reset} Filter by tier: free or pro
335
+ ${utils.COLORS.cyan}--category <cat>${utils.COLORS.reset} Filter by category
336
+
337
+ ${utils.COLORS.cyan}Tier Information:${utils.COLORS.reset}
338
+ Skills are labeled with their tier: ${utils.COLORS.green}[FREE]${utils.COLORS.reset} or ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
339
+ - FREE tier patterns are available to all authenticated users
340
+ - PRO tier patterns require a Pro subscription
341
+
342
+ ${utils.COLORS.cyan}Authentication:${utils.COLORS.reset}
343
+ All skill commands require authentication.
344
+ Run: ${utils.COLORS.cyan}bootspring auth login${utils.COLORS.reset}
278
345
 
279
346
  ${utils.COLORS.cyan}Examples:${utils.COLORS.reset}
280
347
  bootspring skill list
281
- bootspring skill list --external
348
+ bootspring skill list --tier-filter=free
282
349
  bootspring skill search auth
283
- bootspring skill search vercel --external
284
350
  bootspring skill show auth/clerk
285
- bootspring skill show external/vercel-automation
286
- bootspring skill sync --manifest-url https://api.bootspring.com/skills/manifest.json
351
+ bootspring skill show api/route-handler
287
352
  `);
288
353
  }
289
354
 
355
+ /**
356
+ * Normalize tier filter value
357
+ */
358
+ function normalizeTierFilter(value) {
359
+ if (!value) return undefined;
360
+ const normalized = String(value).trim().toLowerCase();
361
+ if (normalized === 'free' || normalized === 'pro' || normalized === 'premium') {
362
+ // Treat 'premium' as 'pro' for filtering purposes
363
+ return normalized === 'premium' ? 'pro' : normalized;
364
+ }
365
+ return undefined;
366
+ }
367
+
290
368
  /**
291
369
  * Run skill command
292
370
  */
@@ -300,6 +378,10 @@ async function run(args) {
300
378
  const parsedLimit = parsedArgs.limit ? Number(parsedArgs.limit) : 20;
301
379
  const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? parsedLimit : 20;
302
380
  const maxChars = parsedArgs['max-chars'] ? Number(parsedArgs['max-chars']) : undefined;
381
+
382
+ // Parse tier filter (for list/search filtering) separately from access tier
383
+ const tierFilter = normalizeTierFilter(parsedArgs['tier-filter'] || parsedArgs['filter-tier']);
384
+
303
385
  const accessOptions = {
304
386
  mode: parsedArgs['access-mode'],
305
387
  entitled: parsedArgs.entitled,
@@ -308,7 +390,7 @@ async function run(args) {
308
390
 
309
391
  switch (subcommand) {
310
392
  case 'list':
311
- listSkills({ includeExternal, externalOnly, limit, accessOptions });
393
+ await listSkills({ tierFilter, category: parsedArgs.category });
312
394
  break;
313
395
 
314
396
  case 'show':
@@ -317,13 +399,7 @@ async function run(args) {
317
399
  utils.print.dim('Usage: bootspring skill show <id>');
318
400
  return;
319
401
  }
320
- showSkill(subargs[0], {
321
- includeExternal: true,
322
- summary: Boolean(parsedArgs.summary),
323
- sections: parsedArgs.sections,
324
- maxChars,
325
- accessOptions
326
- });
402
+ await showSkill(subargs[0], {});
327
403
  break;
328
404
 
329
405
  case 'search':
@@ -332,19 +408,11 @@ async function run(args) {
332
408
  utils.print.dim('Usage: bootspring skill search <query>');
333
409
  return;
334
410
  }
335
- searchSkills(subargs.join(' '), { includeExternal, externalOnly, limit, accessOptions });
411
+ await searchSkills(subargs.join(' '), { tierFilter });
336
412
  break;
337
413
 
338
414
  case 'sync':
339
- await syncCatalog({
340
- manifestUrl: parsedArgs['manifest-url'],
341
- contentBaseUrl: parsedArgs['content-base-url'],
342
- token: parsedArgs.token,
343
- timeoutMs: parsedArgs.timeout,
344
- cacheDir: parsedArgs['cache-dir'],
345
- manifestPublicKey: parsedArgs['manifest-public-key'],
346
- requireManifestSignature: parsedArgs['require-signature']
347
- });
415
+ await syncCatalog();
348
416
  break;
349
417
 
350
418
  case 'help':
@@ -355,13 +423,7 @@ async function run(args) {
355
423
 
356
424
  default:
357
425
  // Shortcut: `bootspring skill auth/clerk`
358
- showSkill(subcommand, {
359
- includeExternal: true,
360
- summary: Boolean(parsedArgs.summary),
361
- sections: parsedArgs.sections,
362
- maxChars,
363
- accessOptions
364
- });
426
+ await showSkill(subcommand, {});
365
427
  }
366
428
  }
367
429