@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
@@ -0,0 +1,447 @@
1
+ /**
2
+ * MCP seed tool module
3
+ * Provides seed operations via MCP: setup, init, generate, scaffold, status, export
4
+ */
5
+
6
+ function getToolDefinition() {
7
+ return {
8
+ name: 'bootspring_seed',
9
+ description: 'Manage seed configuration and project scaffolding. Setup input folders, run questionnaire, ingest files, generate documents, and scaffold projects.',
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ action: {
14
+ type: 'string',
15
+ enum: ['setup', 'init', 'generate', 'scaffold', 'status', 'export'],
16
+ description: 'Action to perform: setup (create input folders), init (run questionnaire), generate (ingest files and generate docs), scaffold (create project structure), status (show config), export (export as JSON/YAML)'
17
+ },
18
+ preset: {
19
+ type: 'string',
20
+ description: 'Preset for init (minimal, standard, full, startup, api) or scaffold (nextjs, react, node, fullstack)'
21
+ },
22
+ format: {
23
+ type: 'string',
24
+ enum: ['json', 'yaml'],
25
+ description: 'Export format (for export action)'
26
+ },
27
+ dryRun: {
28
+ type: 'boolean',
29
+ description: 'For scaffold action: show plan without creating files'
30
+ }
31
+ },
32
+ required: ['action']
33
+ }
34
+ };
35
+ }
36
+
37
+ function createHandler({ ingestModule, scaffoldModule, config, format }) {
38
+ return async (args) => {
39
+ const { action, preset, dryRun } = args;
40
+ const cfg = config.load();
41
+ const projectRoot = cfg._projectRoot;
42
+
43
+ try {
44
+ switch (action) {
45
+ case 'setup':
46
+ return await handleSetup(projectRoot, format);
47
+
48
+ case 'init':
49
+ return format.warning(
50
+ 'Interactive questionnaire not available via MCP',
51
+ ['Run "bootspring seed init" in terminal for interactive mode']
52
+ );
53
+
54
+ case 'generate':
55
+ return await handleGenerate(projectRoot, ingestModule, format);
56
+
57
+ case 'scaffold':
58
+ return await handleScaffold(projectRoot, preset, dryRun, scaffoldModule, config, format);
59
+
60
+ case 'status':
61
+ return await handleStatus(projectRoot, config, scaffoldModule, format);
62
+
63
+ case 'export':
64
+ return await handleExport(projectRoot, args.format || 'json', format);
65
+
66
+ default:
67
+ return format.error(`Unknown action: ${action}`, [
68
+ 'Valid actions: setup, init, generate, scaffold, status, export'
69
+ ]);
70
+ }
71
+ } catch (error) {
72
+ return format.error(`Seed operation failed: ${error.message}`);
73
+ }
74
+ };
75
+ }
76
+
77
+ async function handleSetup(projectRoot, _format) {
78
+ const fs = require('fs');
79
+ const path = require('path');
80
+
81
+ const folders = [
82
+ '.bootspring',
83
+ '.bootspring/inputs',
84
+ '.bootspring/inputs/mvp',
85
+ '.bootspring/inputs/mvp/source',
86
+ '.bootspring/inputs/business',
87
+ '.bootspring/inputs/prd',
88
+ '.bootspring/inputs/prd/features',
89
+ '.bootspring/inputs/designs',
90
+ '.bootspring/inputs/designs/figma-exports',
91
+ '.bootspring/inputs/designs/wireframes',
92
+ '.bootspring/inputs/legal',
93
+ '.bootspring/inputs/api',
94
+ '.bootspring/inputs/data',
95
+ '.bootspring/generated',
96
+ '.bootspring/generated/prd',
97
+ '.bootspring/generated/master-plan',
98
+ '.bootspring/generated/business',
99
+ '.bootspring/generated/architecture',
100
+ '.bootspring/generated/legal',
101
+ '.bootspring/context',
102
+ '.bootspring/logs',
103
+ '.bootspring/config'
104
+ ];
105
+
106
+ let created = 0;
107
+ for (const folder of folders) {
108
+ const folderPath = path.join(projectRoot, folder);
109
+ if (!fs.existsSync(folderPath)) {
110
+ fs.mkdirSync(folderPath, { recursive: true });
111
+ created++;
112
+ }
113
+ }
114
+
115
+ const inputReadme = `# Input Files
116
+
117
+ Drop your existing files here before running \`bootspring seed generate\`.
118
+
119
+ | Folder | What to put here |
120
+ |--------|------------------|
121
+ | \`mvp/source/\` | AI-generated MVP code (Lovable, Bolt, V0, etc.) |
122
+ | \`business/\` | Business plans, pitch decks, market research |
123
+ | \`prd/\` | Product requirements documents |
124
+ | \`designs/\` | Figma exports, wireframes, mockups |
125
+ | \`legal/\` | Existing terms, privacy policies |
126
+ | \`api/\` | OpenAPI specs, API documentation |
127
+ | \`data/\` | Sample data, database schemas |
128
+ `;
129
+
130
+ const readmePath = path.join(projectRoot, '.bootspring/inputs/README.md');
131
+ if (!fs.existsSync(readmePath)) {
132
+ fs.writeFileSync(readmePath, inputReadme);
133
+ created++;
134
+ }
135
+
136
+ return {
137
+ content: [{
138
+ type: 'text',
139
+ text: `## Seed Setup Complete
140
+
141
+ Created ${created} folders/files.
142
+
143
+ **Folder Structure:**
144
+ \`\`\`
145
+ .bootspring/
146
+ ├── inputs/
147
+ │ ├── mvp/source/ # AI-generated MVP code
148
+ │ ├── business/ # Business plans, pitch decks
149
+ │ ├── prd/ # Product requirements
150
+ │ ├── designs/ # Figma exports, wireframes
151
+ │ ├── legal/ # Terms, privacy policies
152
+ │ ├── api/ # OpenAPI specs
153
+ │ └── data/ # Sample data, schemas
154
+ ├── generated/ # Bootspring output
155
+ ├── context/ # AI context index
156
+ └── logs/ # Action history
157
+ \`\`\`
158
+
159
+ **Next Steps:**
160
+ 1. Drop your files in \`.bootspring/inputs/\` subfolders
161
+ 2. Run \`bootspring_seed\` with action: "generate" to process them`
162
+ }]
163
+ };
164
+ }
165
+
166
+ async function handleGenerate(projectRoot, ingestModule, format) {
167
+ const fs = require('fs');
168
+ const path = require('path');
169
+
170
+ const inputsDir = path.join(projectRoot, '.bootspring', 'inputs');
171
+ if (!fs.existsSync(inputsDir)) {
172
+ return format.warning(
173
+ 'No .bootspring/inputs folder found',
174
+ ['Run bootspring_seed with action: "setup" first']
175
+ );
176
+ }
177
+
178
+ // Ingest all files
179
+ const ingested = await ingestModule.ingestAll(projectRoot);
180
+
181
+ // Count files found
182
+ const counts = {
183
+ mvp: ingested.mvp?.files?.length || 0,
184
+ business: ingested.business?.length || 0,
185
+ prd: ingested.prd?.length || 0,
186
+ designs: ingested.designs?.files?.length || 0,
187
+ api: ingested.api?.length || 0,
188
+ data: ingested.data?.length || 0
189
+ };
190
+
191
+ const totalFiles = Object.values(counts).reduce((a, b) => a + b, 0);
192
+
193
+ // Parse SEED.md if exists
194
+ const seedPath = path.join(projectRoot, 'SEED.md');
195
+ const seedConfig = {};
196
+ if (fs.existsSync(seedPath)) {
197
+ try {
198
+ const content = fs.readFileSync(seedPath, 'utf-8');
199
+ const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
200
+ for (const match of yamlBlocks) {
201
+ try {
202
+ const yaml = require('yaml');
203
+ const parsed = yaml.parse(match[1]);
204
+ Object.assign(seedConfig, parsed);
205
+ } catch {
206
+ // Skip invalid blocks
207
+ }
208
+ }
209
+ } catch {
210
+ // Continue without seed config
211
+ }
212
+ }
213
+
214
+ // Generate documents
215
+ const outputs = await ingestModule.generateDocuments(ingested, seedConfig, projectRoot);
216
+
217
+ // Update context index
218
+ await ingestModule.updateContextIndex(projectRoot, ingested);
219
+
220
+ const generatedDocs = Object.keys(outputs).filter(k => outputs[k]).length;
221
+
222
+ return {
223
+ content: [{
224
+ type: 'text',
225
+ text: `## Files Ingested
226
+
227
+ | Category | Files Found |
228
+ |----------|-------------|
229
+ | MVP Code | ${counts.mvp} |
230
+ | Business Docs | ${counts.business} |
231
+ | PRD Docs | ${counts.prd} |
232
+ | Design Files | ${counts.designs} |
233
+ | API Specs | ${counts.api} |
234
+ | Data Files | ${counts.data} |
235
+ | **Total** | **${totalFiles}** |
236
+
237
+ ## Documents Generated
238
+
239
+ Generated ${generatedDocs} documents in \`.bootspring/generated/\`
240
+
241
+ **MVP Patterns Detected:**
242
+ ${ingested.mvp?.patterns?.join(', ') || 'None'}
243
+
244
+ **Next Steps:**
245
+ 1. Review generated documents in \`.bootspring/generated/\`
246
+ 2. Run \`bootspring_seed\` with action: "scaffold" to create project structure`
247
+ }]
248
+ };
249
+ }
250
+
251
+ async function handleScaffold(projectRoot, preset, dryRun, scaffoldModule, config, format) {
252
+ const fs = require('fs');
253
+ const path = require('path');
254
+
255
+ let scaffoldConfig;
256
+
257
+ if (preset) {
258
+ const validPresets = scaffoldModule.getPresets();
259
+ if (!validPresets.includes(preset)) {
260
+ return format.error(`Invalid preset: ${preset}`, [
261
+ `Valid presets: ${validPresets.join(', ')}`
262
+ ]);
263
+ }
264
+ scaffoldConfig = scaffoldModule.getPresetConfig(preset);
265
+ } else {
266
+ // Try SEED.md
267
+ const seedPath = path.join(projectRoot, 'SEED.md');
268
+ if (!fs.existsSync(seedPath)) {
269
+ const presets = scaffoldModule.getPresets();
270
+ return format.warning(
271
+ 'No SEED.md found',
272
+ [`Use preset parameter: ${presets.join(', ')}`, 'Or run "bootspring seed init" first']
273
+ );
274
+ }
275
+
276
+ // Parse SEED.md
277
+ const content = fs.readFileSync(seedPath, 'utf-8');
278
+ scaffoldConfig = {};
279
+ const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
280
+ for (const match of yamlBlocks) {
281
+ try {
282
+ const yaml = require('yaml');
283
+ Object.assign(scaffoldConfig, yaml.parse(match[1]));
284
+ } catch {
285
+ // Skip
286
+ }
287
+ }
288
+ }
289
+
290
+ const plan = scaffoldModule.planScaffold(scaffoldConfig, projectRoot);
291
+
292
+ if (dryRun) {
293
+ return {
294
+ content: [{
295
+ type: 'text',
296
+ text: `## Scaffold Plan (Dry Run)
297
+
298
+ **Configuration:**
299
+ - Framework: ${scaffoldConfig.stack?.framework || 'nextjs'}
300
+ - Language: ${scaffoldConfig.stack?.language || 'typescript'}
301
+ - UI: ${scaffoldConfig.frontend?.uiLibrary || 'shadcn'}
302
+
303
+ **Directories:** ${plan.directories.length}
304
+ **Files:** ${plan.files.length}
305
+
306
+ **Sample directories:**
307
+ ${plan.directories.slice(0, 10).map(d => `- ${d}`).join('\n')}
308
+ ${plan.directories.length > 10 ? `\n... and ${plan.directories.length - 10} more` : ''}
309
+
310
+ **Sample files:**
311
+ ${plan.files.slice(0, 10).map(f => `- ${f.path}`).join('\n')}
312
+ ${plan.files.length > 10 ? `\n... and ${plan.files.length - 10} more` : ''}`
313
+ }]
314
+ };
315
+ }
316
+
317
+ const result = await scaffoldModule.execute(plan, projectRoot);
318
+
319
+ return {
320
+ content: [{
321
+ type: 'text',
322
+ text: `## Scaffold Complete
323
+
324
+ Created ${result.filesCreated} files and ${result.dirsCreated} directories.
325
+
326
+ **Configuration:**
327
+ - Framework: ${scaffoldConfig.stack?.framework || 'nextjs'}
328
+ - Language: ${scaffoldConfig.stack?.language || 'typescript'}
329
+
330
+ ${result.warnings.length > 0 ? `**Warnings:**\n${result.warnings.map(w => `- ${w}`).join('\n')}` : ''}
331
+
332
+ **Next Steps:**
333
+ 1. Run \`npm install\` to install dependencies
334
+ 2. Copy \`.env.example\` to \`.env.local\`
335
+ 3. Run \`npm run dev\` to start development`
336
+ }]
337
+ };
338
+ }
339
+
340
+ async function handleStatus(projectRoot, _config, scaffoldModule, _format) {
341
+ const fs = require('fs');
342
+ const path = require('path');
343
+
344
+ const seedPath = path.join(projectRoot, 'SEED.md');
345
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
346
+ const inputsDir = path.join(projectRoot, '.bootspring', 'inputs');
347
+
348
+ const status = {
349
+ seedMd: fs.existsSync(seedPath),
350
+ config: fs.existsSync(configPath),
351
+ inputsFolder: fs.existsSync(inputsDir)
352
+ };
353
+
354
+ let seedInfo = '';
355
+ if (status.seedMd) {
356
+ try {
357
+ const content = fs.readFileSync(seedPath, 'utf-8');
358
+ const seedConfig = {};
359
+ const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
360
+ for (const match of yamlBlocks) {
361
+ try {
362
+ const yaml = require('yaml');
363
+ Object.assign(seedConfig, yaml.parse(match[1]));
364
+ } catch {
365
+ // Skip
366
+ }
367
+ }
368
+ seedInfo = `
369
+ **Configuration:**
370
+ - Project: ${seedConfig.project?.name || 'unknown'}
371
+ - Framework: ${seedConfig.stack?.framework || 'unknown'}
372
+ - Language: ${seedConfig.stack?.language || 'unknown'}
373
+ - Database: ${seedConfig.stack?.database || 'none'}`;
374
+ } catch {
375
+ // Continue without seed info
376
+ }
377
+ }
378
+
379
+ const presets = scaffoldModule.getPresets();
380
+
381
+ return {
382
+ content: [{
383
+ type: 'text',
384
+ text: `## Seed Status
385
+
386
+ | Item | Status |
387
+ |------|--------|
388
+ | SEED.md | ${status.seedMd ? '✓ exists' : '○ not found'} |
389
+ | bootspring.config.js | ${status.config ? '✓ exists' : '○ not found'} |
390
+ | .bootspring/inputs/ | ${status.inputsFolder ? '✓ exists' : '○ not found'} |
391
+ ${seedInfo}
392
+
393
+ **Available Presets:** ${presets.join(', ')}
394
+
395
+ **Commands:**
396
+ - \`bootspring_seed\` action: "setup" - Create input folders
397
+ - \`bootspring_seed\` action: "generate" - Process input files
398
+ - \`bootspring_seed\` action: "scaffold" preset: "nextjs" - Generate project`
399
+ }]
400
+ };
401
+ }
402
+
403
+ async function handleExport(projectRoot, exportFormat, format) {
404
+ const fs = require('fs');
405
+ const path = require('path');
406
+
407
+ const seedPath = path.join(projectRoot, 'SEED.md');
408
+ if (!fs.existsSync(seedPath)) {
409
+ return format.error('No SEED.md found', ['Run "bootspring seed init" first']);
410
+ }
411
+
412
+ const content = fs.readFileSync(seedPath, 'utf-8');
413
+ const seedConfig = {};
414
+ const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
415
+ for (const match of yamlBlocks) {
416
+ try {
417
+ const yaml = require('yaml');
418
+ Object.assign(seedConfig, yaml.parse(match[1]));
419
+ } catch {
420
+ // Skip
421
+ }
422
+ }
423
+
424
+ let exported;
425
+ if (exportFormat === 'yaml') {
426
+ const yaml = require('yaml');
427
+ exported = yaml.stringify(seedConfig);
428
+ } else {
429
+ exported = JSON.stringify(seedConfig, null, 2);
430
+ }
431
+
432
+ return {
433
+ content: [{
434
+ type: 'text',
435
+ text: `## Seed Configuration (${exportFormat.toUpperCase()})
436
+
437
+ \`\`\`${exportFormat}
438
+ ${exported}
439
+ \`\`\``
440
+ }]
441
+ };
442
+ }
443
+
444
+ module.exports = {
445
+ getToolDefinition,
446
+ createHandler
447
+ };
@@ -112,18 +112,28 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
112
112
 
113
113
  switch (action) {
114
114
  case 'list': {
115
- let skillIds = skills.listSkills({ includeExternal });
116
- if (includeExternal) {
117
- skillIds = entitlements.filterAccessibleSkills(skillIds, accessOptions).allowed;
115
+ const skillIds = skills.listSkills({ includeExternal });
116
+ // Filter by access - includes both external skills and premium patterns
117
+ const filtered = [];
118
+ for (const id of skillIds) {
119
+ const metadata = skills.getSkillMetadata(id) || {};
120
+ const access = entitlements.checkSkillAccess(id, {
121
+ ...accessOptions,
122
+ skillTier: metadata.tier
123
+ });
124
+ if (access.allowed) {
125
+ filtered.push(id);
126
+ }
118
127
  }
119
- const items = skillIds
128
+ const items = filtered
120
129
  .slice(0, limit)
121
130
  .map(id => {
122
131
  const metadata = skills.getSkillMetadata(id) || {};
123
132
  return {
124
133
  id,
125
134
  name: metadata.name || id,
126
- description: metadata.description || ''
135
+ description: metadata.description || '',
136
+ tier: metadata.tier || 'free'
127
137
  };
128
138
  });
129
139
 
@@ -138,11 +148,17 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
138
148
  if (!name) throw new Error('Skill name required');
139
149
  const metadata = skills.getSkillMetadata(name);
140
150
  const targetSkillId = metadata?.id || name;
141
- const access = entitlements.checkSkillAccess(targetSkillId, accessOptions);
151
+ const skillTier = metadata?.tier || 'free';
152
+ const access = entitlements.checkSkillAccess(targetSkillId, {
153
+ ...accessOptions,
154
+ skillTier
155
+ });
142
156
  if (!access.allowed) {
157
+ const isPremium = entitlements.isExternalSkill(targetSkillId) || skillTier === 'pro';
143
158
  trackTelemetry('premium_prompted', {
144
- capability: 'external_skill',
159
+ capability: isPremium ? 'premium_pattern' : 'external_skill',
145
160
  skillId: targetSkillId,
161
+ skillTier,
146
162
  mode: access.context?.mode,
147
163
  tier: access.context?.tier,
148
164
  reason: access.code
@@ -158,10 +174,12 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
158
174
  sections,
159
175
  maxChars
160
176
  });
161
- if (entitlements.isExternalSkill(targetSkillId)) {
177
+ const isPremiumUnlock = entitlements.isExternalSkill(targetSkillId) || skillTier === 'pro';
178
+ if (isPremiumUnlock) {
162
179
  trackTelemetry('premium_unlocked', {
163
- capability: 'external_skill',
180
+ capability: skillTier === 'pro' ? 'premium_pattern' : 'external_skill',
164
181
  skillId: targetSkillId,
182
+ skillTier,
165
183
  mode: access.context?.mode,
166
184
  tier: access.context?.tier,
167
185
  reason: access.code
@@ -176,6 +194,7 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
176
194
  name: metadata?.name || name,
177
195
  description: metadata?.description || '',
178
196
  source: metadata?.source || 'built-in',
197
+ tier: skillTier,
179
198
  content
180
199
  }, null, 2)
181
200
  }]
@@ -183,18 +202,28 @@ function createHandler({ skills, entitlements, trackTelemetry }) {
183
202
  }
184
203
  case 'search': {
185
204
  if (!query) throw new Error('Search query required');
186
- let matches = skills.searchSkills(query, { includeExternal, limit });
187
- if (includeExternal) {
188
- matches = entitlements.filterAccessibleSkills(matches, accessOptions).allowed;
205
+ const matches = skills.searchSkills(query, { includeExternal, limit });
206
+ // Filter by access - includes both external skills and premium patterns
207
+ const filtered = [];
208
+ for (const id of matches) {
209
+ const metadata = skills.getSkillMetadata(id) || {};
210
+ const access = entitlements.checkSkillAccess(id, {
211
+ ...accessOptions,
212
+ skillTier: metadata.tier
213
+ });
214
+ if (access.allowed) {
215
+ filtered.push(id);
216
+ }
189
217
  }
190
- const items = matches
218
+ const items = filtered
191
219
  .map(id => {
192
220
  const metadata = skills.getSkillMetadata(id) || {};
193
221
  return {
194
222
  id,
195
223
  name: metadata.name || id,
196
224
  description: metadata.description || '',
197
- source: metadata.source || 'built-in'
225
+ source: metadata.source || 'built-in',
226
+ tier: metadata.tier || 'free'
198
227
  };
199
228
  });
200
229
 
@@ -0,0 +1,147 @@
1
+ /**
2
+ * MCP Auto-Suggest Tool
3
+ *
4
+ * Analyzes user context and suggests relevant Bootspring commands.
5
+ *
6
+ * @package bootspring
7
+ * @module mcp/tools/suggest-tool
8
+ */
9
+
10
+ const toolName = 'bootspring_suggest';
11
+
12
+ /**
13
+ * Get tool definition for MCP
14
+ */
15
+ function getToolDefinition() {
16
+ return {
17
+ name: toolName,
18
+ description: 'Analyze user context and suggest relevant Bootspring commands. Use this to proactively help users discover Bootspring features that can assist with their current task.',
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ userMessage: {
23
+ type: 'string',
24
+ description: 'The user message or context to analyze for suggestions'
25
+ },
26
+ action: {
27
+ type: 'string',
28
+ enum: ['analyze', 'enable', 'disable', 'config', 'toggle-category'],
29
+ description: 'Action to perform. Default is "analyze"'
30
+ },
31
+ category: {
32
+ type: 'string',
33
+ description: 'Category name for toggle-category action'
34
+ }
35
+ },
36
+ required: []
37
+ }
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Create tool handler
43
+ */
44
+ function createHandler({ config, format }) {
45
+ // Lazy load auto-suggest
46
+ let autoSuggestModule = null;
47
+
48
+ function getAutoSuggest() {
49
+ if (!autoSuggestModule) {
50
+ autoSuggestModule = require('../../intelligence/auto-suggest');
51
+ }
52
+ return autoSuggestModule;
53
+ }
54
+
55
+ return async (params) => {
56
+ const cfg = config.load();
57
+ const projectRoot = cfg._projectRoot;
58
+ const action = params.action || 'analyze';
59
+
60
+ const { AutoSuggest } = getAutoSuggest();
61
+ const autoSuggest = new AutoSuggest(projectRoot);
62
+
63
+ switch (action) {
64
+ case 'analyze': {
65
+ if (!params.userMessage) {
66
+ return format.error('userMessage is required for analyze action');
67
+ }
68
+
69
+ const suggestions = autoSuggest.analyze(params.userMessage);
70
+
71
+ if (suggestions.length === 0) {
72
+ return format.success('No relevant suggestions for this context');
73
+ }
74
+
75
+ const formattedSuggestions = suggestions.map(s => ({
76
+ command: s.command,
77
+ description: s.description,
78
+ confidence: Math.round(s.confidence * 100),
79
+ category: s.category,
80
+ priority: s.priority
81
+ }));
82
+
83
+ return format.success(
84
+ `Found ${suggestions.length} relevant suggestion(s)`,
85
+ formattedSuggestions.map(s => `${s.command} (${s.confidence}%) - ${s.description}`)
86
+ );
87
+ }
88
+
89
+ case 'enable': {
90
+ autoSuggest.setEnabled(true);
91
+ return format.success('Auto-suggest enabled. Bootspring will proactively suggest relevant commands.');
92
+ }
93
+
94
+ case 'disable': {
95
+ autoSuggest.setEnabled(false);
96
+ return format.success('Auto-suggest disabled. Run with action="enable" to re-enable.');
97
+ }
98
+
99
+ case 'config': {
100
+ const status = autoSuggest.getStatus();
101
+ const enabledCategories = Object.entries(status.categories)
102
+ .filter(([, enabled]) => enabled)
103
+ .map(([name]) => name);
104
+ const disabledCategories = Object.entries(status.categories)
105
+ .filter(([, enabled]) => !enabled)
106
+ .map(([name]) => name);
107
+
108
+ return format.success(
109
+ status.enabled
110
+ ? 'Auto-suggest is enabled and will proactively suggest commands'
111
+ : 'Auto-suggest is disabled',
112
+ [
113
+ `Status: ${status.enabled ? 'enabled' : 'disabled'}`,
114
+ `Enabled categories: ${enabledCategories.join(', ') || 'none'}`,
115
+ `Disabled categories: ${disabledCategories.join(', ') || 'none'}`,
116
+ `Suppressed commands: ${status.suppressedCommands.length || 0}`
117
+ ]
118
+ );
119
+ }
120
+
121
+ case 'toggle-category': {
122
+ if (!params.category) {
123
+ return format.error('category is required for toggle-category action');
124
+ }
125
+
126
+ const status = autoSuggest.getStatus();
127
+ if (!status.categories.hasOwnProperty(params.category)) {
128
+ return format.error(`Unknown category: ${params.category}. Available: ${Object.keys(status.categories).join(', ')}`);
129
+ }
130
+
131
+ const newState = !status.categories[params.category];
132
+ autoSuggest.setCategory(params.category, newState);
133
+
134
+ return format.success(`Category '${params.category}' ${newState ? 'enabled' : 'disabled'}`);
135
+ }
136
+
137
+ default:
138
+ return format.error(`Unknown action: ${action}`);
139
+ }
140
+ };
141
+ }
142
+
143
+ module.exports = {
144
+ getToolDefinition,
145
+ createHandler,
146
+ toolName
147
+ };