@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/mcp/registry.js CHANGED
@@ -15,12 +15,54 @@ const intelligence = require('../intelligence');
15
15
  const gitMemory = require('../intelligence/git-memory');
16
16
  const prdModule = require('../intelligence/prd');
17
17
  const quality = require('../quality');
18
- const skills = require('../skills');
19
18
  const entitlements = require('../core/entitlements');
19
+
20
+ // Thin client: skills module may not exist locally
21
+ // Skills are now served from the API
22
+ let skills;
23
+ try {
24
+ skills = require('../skills');
25
+ } catch {
26
+ // Stub for thin client - skills fetched from API
27
+ skills = {
28
+ listSkills: () => [],
29
+ getSkillMetadata: () => null,
30
+ loadSkill: () => null,
31
+ searchSkills: () => [],
32
+ getCategories: () => [],
33
+ hasExternalSkillLibrary: () => false
34
+ };
35
+ }
20
36
  const telemetry = require('../core/telemetry');
21
37
  const capabilities = require('./capabilities');
22
38
  const format = require('./response-formatter');
23
39
 
40
+ // Seed system modules (lazy loaded)
41
+ let seedModule = null;
42
+ let ingestModule = null;
43
+ let scaffoldModule = null;
44
+ let mvpModule = null;
45
+
46
+ function getSeedModule() {
47
+ if (!seedModule) seedModule = require('../cli/seed');
48
+ return seedModule;
49
+ }
50
+
51
+ function getIngestModule() {
52
+ if (!ingestModule) ingestModule = require('../core/ingest');
53
+ return ingestModule;
54
+ }
55
+
56
+ function getScaffoldModule() {
57
+ if (!scaffoldModule) scaffoldModule = require('../core/scaffold');
58
+ return scaffoldModule;
59
+ }
60
+
61
+ function getMvpModule() {
62
+ if (!mvpModule) mvpModule = require('../core/mvp');
63
+ return mvpModule;
64
+ }
65
+
24
66
  const agentTool = require('./tools/agent-tool');
25
67
  const assistTool = require('./tools/assist-tool');
26
68
  const capabilitiesTool = require('./tools/capabilities-tool');
@@ -35,6 +77,12 @@ const qualityTool = require('./tools/quality-tool');
35
77
  const skillTool = require('./tools/skill-tool');
36
78
  const telemetryTool = require('./tools/telemetry-tool');
37
79
  const todoTool = require('./tools/todo-tool');
80
+ const seedTool = require('./tools/seed-tool');
81
+ const mvpTool = require('./tools/mvp-tool');
82
+ const prdTool = require('./tools/prd-tool');
83
+ const autopilotTool = require('./tools/autopilot-tool');
84
+ const contentTool = require('./tools/content-tool');
85
+ const suggestTool = require('./tools/suggest-tool');
38
86
 
39
87
  // Natural language and context detection
40
88
  const natural = require('../natural');
@@ -69,7 +117,13 @@ const TOOLS = [
69
117
  telemetryTool.getToolDefinition(),
70
118
  orchestratorTool.getToolDefinition(),
71
119
  loopTool.getToolDefinition(),
72
- memoryTool.getToolDefinition()
120
+ memoryTool.getToolDefinition(),
121
+ seedTool.getToolDefinition(),
122
+ mvpTool.getToolDefinition(),
123
+ prdTool.getToolDefinition(),
124
+ autopilotTool.getToolDefinition(),
125
+ contentTool.getToolDefinition(),
126
+ suggestTool.getToolDefinition()
73
127
  ];
74
128
 
75
129
  const RESOURCES = [
@@ -96,6 +150,36 @@ const RESOURCES = [
96
150
  name: 'Agent List',
97
151
  description: 'Available specialized agents',
98
152
  mimeType: 'application/json'
153
+ },
154
+ {
155
+ uri: 'bootspring://workflow/status',
156
+ name: 'Workflow Status',
157
+ description: 'Current active workflow status and progress',
158
+ mimeType: 'application/json'
159
+ },
160
+ {
161
+ uri: 'bootspring://skills/catalog',
162
+ name: 'Skills Catalog',
163
+ description: 'Available skills and patterns catalog',
164
+ mimeType: 'application/json'
165
+ },
166
+ {
167
+ uri: 'bootspring://memory/recent',
168
+ name: 'Recent Memory',
169
+ description: 'Recent decisions and learning insights',
170
+ mimeType: 'application/json'
171
+ },
172
+ {
173
+ uri: 'bootspring://autopilot/status',
174
+ name: 'Git Autopilot Status',
175
+ description: 'Git autopilot status and pending workflow suggestions',
176
+ mimeType: 'application/json'
177
+ },
178
+ {
179
+ uri: 'bootspring://content/templates',
180
+ name: 'Content Templates',
181
+ description: 'Available document templates for content generation',
182
+ mimeType: 'application/json'
99
183
  }
100
184
  ];
101
185
 
@@ -152,6 +236,53 @@ const toolHandlers = {
152
236
  }),
153
237
  bootspring_memory: memoryTool.createHandler({ gitMemory }),
154
238
  bootspring_telemetry: telemetryTool.createHandler({ telemetry }),
239
+ bootspring_seed: seedTool.createHandler({
240
+ seedModule: { run: (args) => getSeedModule().run(args) },
241
+ ingestModule: {
242
+ ingestAll: (root) => getIngestModule().ingestAll(root),
243
+ generateDocuments: (ingested, seedConfig, root) => getIngestModule().generateDocuments(ingested, seedConfig, root),
244
+ updateContextIndex: (root, ingested) => getIngestModule().updateContextIndex(root, ingested),
245
+ ingestPRD: (root) => getIngestModule().ingestPRD(root)
246
+ },
247
+ scaffoldModule: {
248
+ getPresets: () => getScaffoldModule().getPresets(),
249
+ getPresetConfig: (preset) => getScaffoldModule().getPresetConfig(preset),
250
+ getPresetInfo: (preset) => getScaffoldModule().getPresetInfo(preset),
251
+ planScaffold: (cfg, root) => getScaffoldModule().planScaffold(cfg, root),
252
+ execute: (plan, root) => getScaffoldModule().execute(plan, root)
253
+ },
254
+ config,
255
+ format
256
+ }),
257
+ bootspring_mvp: mvpTool.createHandler({
258
+ mvpModule: {
259
+ MVP_STRUCTURE: { source: '.bootspring/inputs/mvp/source', references: '.bootspring/references' },
260
+ initMvpFolders: () => getMvpModule().initMvpFolders(),
261
+ analyzeMvp: () => getMvpModule().analyzeMvp(),
262
+ importMvp: (opts) => getMvpModule().importMvp(opts),
263
+ CODE_ISSUES: getMvpModule().CODE_ISSUES || {}
264
+ },
265
+ config,
266
+ format
267
+ }),
268
+ bootspring_prd: prdTool.createHandler({
269
+ ingestModule: {
270
+ ingestPRD: (root) => getIngestModule().ingestPRD(root)
271
+ },
272
+ todoModule,
273
+ config,
274
+ format
275
+ }),
276
+ bootspring_autopilot: autopilotTool.createHandler({ format }),
277
+ bootspring_content: contentTool.createHandler({
278
+ contentGen: intelligence.contentGen,
279
+ config,
280
+ format
281
+ }),
282
+ bootspring_suggest: suggestTool.createHandler({
283
+ config,
284
+ format
285
+ }),
155
286
  };
156
287
 
157
288
  const resourceHandlers = {
@@ -214,13 +345,559 @@ const resourceHandlers = {
214
345
  text: JSON.stringify(agents, null, 2)
215
346
  }]
216
347
  };
348
+ },
349
+
350
+ 'bootspring://workflow/status': async () => {
351
+ const state = intelligence.loadState();
352
+ const currentPhase = intelligence.getCurrentPhase();
353
+ const phaseConfig = intelligence.PHASE_AGENTS[currentPhase];
354
+
355
+ let workflowInfo = null;
356
+ if (state.activeWorkflow) {
357
+ const workflow = intelligence.getWorkflow(state.activeWorkflow);
358
+ const progress = intelligence.getWorkflowSignalProgress(state.activeWorkflow, state);
359
+ workflowInfo = {
360
+ name: state.activeWorkflow,
361
+ step: state.workflowStep + 1,
362
+ totalSteps: workflow?.phases?.length || 0,
363
+ currentPhase: workflow?.phases?.[state.workflowStep]?.name || null,
364
+ progress: progress || null
365
+ };
366
+ }
367
+
368
+ return {
369
+ contents: [{
370
+ uri: 'bootspring://workflow/status',
371
+ mimeType: 'application/json',
372
+ text: JSON.stringify({
373
+ phase: currentPhase,
374
+ phaseName: phaseConfig?.name || 'Unknown',
375
+ activeWorkflow: workflowInfo,
376
+ availableWorkflows: intelligence.listWorkflows().map(w => ({
377
+ key: w.key,
378
+ name: w.name,
379
+ tier: w.tier
380
+ })),
381
+ lastAnalysis: state.lastAnalysis
382
+ }, null, 2)
383
+ }]
384
+ };
385
+ },
386
+
387
+ 'bootspring://skills/catalog': async () => {
388
+ const allSkills = skills.listSkills({ includeExternal: true });
389
+ const catalog = allSkills.map(skillId => {
390
+ const metadata = skills.getSkillMetadata(skillId);
391
+ return {
392
+ id: skillId,
393
+ name: metadata?.name || skillId,
394
+ description: metadata?.description || '',
395
+ category: metadata?.category || 'general',
396
+ source: metadata?.source || 'built-in'
397
+ };
398
+ });
399
+
400
+ return {
401
+ contents: [{
402
+ uri: 'bootspring://skills/catalog',
403
+ mimeType: 'application/json',
404
+ text: JSON.stringify({
405
+ total: catalog.length,
406
+ skills: catalog
407
+ }, null, 2)
408
+ }]
409
+ };
410
+ },
411
+
412
+ 'bootspring://memory/recent': async () => {
413
+ const memory = intelligence.memory;
414
+ let recentDecisions = [];
415
+ let stats = {};
416
+ let patterns = [];
417
+
418
+ try {
419
+ recentDecisions = memory.getRecentDecisions(10);
420
+ stats = memory.getStats();
421
+ patterns = memory.getPatterns().slice(0, 5);
422
+ } catch {
423
+ // Memory may not be initialized
424
+ }
425
+
426
+ return {
427
+ contents: [{
428
+ uri: 'bootspring://memory/recent',
429
+ mimeType: 'application/json',
430
+ text: JSON.stringify({
431
+ stats,
432
+ recentDecisions: recentDecisions.map(d => ({
433
+ id: d.id,
434
+ type: d.type,
435
+ decision: d.decision,
436
+ outcome: d.outcome?.status || 'pending',
437
+ timestamp: d.timestamp
438
+ })),
439
+ topPatterns: patterns
440
+ }, null, 2)
441
+ }]
442
+ };
443
+ },
444
+
445
+ 'bootspring://autopilot/status': async () => {
446
+ let autopilotStatus = { enabled: false, suggestions: [] };
447
+
448
+ try {
449
+ const gitAutopilot = require('../hooks/git-autopilot');
450
+ autopilotStatus = gitAutopilot.getStatus();
451
+ } catch {
452
+ // Autopilot may not be available
453
+ }
454
+
455
+ return {
456
+ contents: [{
457
+ uri: 'bootspring://autopilot/status',
458
+ mimeType: 'application/json',
459
+ text: JSON.stringify({
460
+ enabled: autopilotStatus.enabled,
461
+ watcherActive: autopilotStatus.watcherActive || false,
462
+ pendingSuggestions: autopilotStatus.pendingSuggestions || 0,
463
+ suggestions: (autopilotStatus.suggestions || []).map(s => ({
464
+ workflow: s.workflow,
465
+ trigger: s.trigger,
466
+ triggerType: s.triggerType,
467
+ value: s.value,
468
+ confidence: s.confidence
469
+ })),
470
+ lastCheck: autopilotStatus.lastCheck
471
+ }, null, 2)
472
+ }]
473
+ };
474
+ },
475
+
476
+ 'bootspring://content/templates': async () => {
477
+ const contentGen = intelligence.contentGen;
478
+ const templates = contentGen.listTemplates();
479
+ const categories = contentGen.getCategories();
480
+ const stats = contentGen.getStatistics();
481
+
482
+ // Group templates by category
483
+ const grouped = {};
484
+ for (const t of templates) {
485
+ if (!grouped[t.category]) {
486
+ grouped[t.category] = [];
487
+ }
488
+ grouped[t.category].push({
489
+ key: t.key,
490
+ name: t.name,
491
+ description: t.description,
492
+ sections: t.sections,
493
+ hasTemplateFile: t.hasTemplateFile,
494
+ compliance: t.compliance
495
+ });
496
+ }
497
+
498
+ return {
499
+ contents: [{
500
+ uri: 'bootspring://content/templates',
501
+ mimeType: 'application/json',
502
+ text: JSON.stringify({
503
+ total: templates.length,
504
+ categories,
505
+ byCategory: grouped,
506
+ statistics: {
507
+ totalGenerated: stats.totalGenerated,
508
+ lastGeneration: stats.lastGeneration
509
+ }
510
+ }, null, 2)
511
+ }]
512
+ };
217
513
  }
218
514
  };
219
515
 
516
+ // ============================================================================
517
+ // Tool Invocation System
518
+ // ============================================================================
519
+ // Provides tool-to-tool invocation with circular dependency prevention
520
+ // and context passing between tools.
521
+ // ============================================================================
522
+
523
+ /**
524
+ * Active call stack for circular dependency detection.
525
+ * Uses AsyncLocalStorage for proper async context tracking.
526
+ */
527
+ const { AsyncLocalStorage } = require('async_hooks');
528
+ const toolCallContext = new AsyncLocalStorage();
529
+
530
+ /**
531
+ * Maximum allowed tool call depth to prevent runaway recursion
532
+ */
533
+ const MAX_CALL_DEPTH = 10;
534
+
535
+ /**
536
+ * Create an invocation context for tracking tool calls
537
+ * @param {object} parentContext - Optional parent context
538
+ * @returns {object} New invocation context
539
+ */
540
+ function createInvocationContext(parentContext = null) {
541
+ return {
542
+ callStack: parentContext?.callStack ? [...parentContext.callStack] : [],
543
+ depth: parentContext?.depth ?? 0,
544
+ startTime: Date.now(),
545
+ parentTool: parentContext?.currentTool || null,
546
+ currentTool: null,
547
+ sharedData: parentContext?.sharedData || {},
548
+ results: parentContext?.results || {}
549
+ };
550
+ }
551
+
552
+ /**
553
+ * Get the current invocation context
554
+ * @returns {object|null} Current context or null if not in a tool invocation
555
+ */
556
+ function getCurrentContext() {
557
+ return toolCallContext.getStore() || null;
558
+ }
559
+
560
+ /**
561
+ * Check if invoking a tool would create a circular dependency
562
+ * @param {string} toolName - Tool to check
563
+ * @param {object} ctx - Current invocation context
564
+ * @returns {boolean} True if circular dependency detected
565
+ */
566
+ function wouldCreateCircularDependency(toolName, ctx) {
567
+ if (!ctx || !ctx.callStack) return false;
568
+ return ctx.callStack.includes(toolName);
569
+ }
570
+
571
+ /**
572
+ * Invoke a tool from within another tool (tool chaining)
573
+ *
574
+ * Features:
575
+ * - Circular dependency prevention
576
+ * - Call depth limiting
577
+ * - Context passing between tools
578
+ * - Result caching within invocation chain
579
+ *
580
+ * @param {string} toolName - Name of the tool to invoke (e.g., 'bootspring_context')
581
+ * @param {object} args - Arguments to pass to the tool
582
+ * @param {object} options - Invocation options
583
+ * @param {boolean} options.allowCircular - Allow circular calls (default: false)
584
+ * @param {boolean} options.useCache - Use cached result if available (default: true)
585
+ * @param {object} options.context - Additional context to pass
586
+ * @returns {Promise<object>} Tool result
587
+ */
588
+ async function invokeTool(toolName, args = {}, options = {}) {
589
+ const { allowCircular = false, useCache = true, context: additionalContext = {} } = options;
590
+
591
+ const handler = toolHandlers[toolName];
592
+
593
+ if (!handler) {
594
+ const available = Object.keys(toolHandlers).join(', ');
595
+ return {
596
+ error: true,
597
+ code: 'TOOL_NOT_FOUND',
598
+ message: `Tool not found: ${toolName}. Available: ${available}`,
599
+ tool: toolName
600
+ };
601
+ }
602
+
603
+ // Get or create invocation context
604
+ const parentCtx = getCurrentContext();
605
+ const ctx = createInvocationContext(parentCtx);
606
+ ctx.currentTool = toolName;
607
+
608
+ // Check for circular dependency
609
+ if (!allowCircular && wouldCreateCircularDependency(toolName, ctx)) {
610
+ return {
611
+ error: true,
612
+ code: 'CIRCULAR_DEPENDENCY',
613
+ message: `Circular dependency detected: ${ctx.callStack.join(' -> ')} -> ${toolName}`,
614
+ tool: toolName,
615
+ callStack: [...ctx.callStack, toolName]
616
+ };
617
+ }
618
+
619
+ // Check call depth
620
+ if (ctx.depth >= MAX_CALL_DEPTH) {
621
+ return {
622
+ error: true,
623
+ code: 'MAX_DEPTH_EXCEEDED',
624
+ message: `Maximum tool call depth (${MAX_CALL_DEPTH}) exceeded`,
625
+ tool: toolName,
626
+ depth: ctx.depth
627
+ };
628
+ }
629
+
630
+ // Check cache for identical calls
631
+ const cacheKey = `${toolName}:${JSON.stringify(args)}`;
632
+ if (useCache && ctx.results[cacheKey]) {
633
+ return {
634
+ ...ctx.results[cacheKey],
635
+ _cached: true
636
+ };
637
+ }
638
+
639
+ // Update context for this invocation
640
+ ctx.callStack.push(toolName);
641
+ ctx.depth++;
642
+
643
+ // Merge additional context into shared data
644
+ if (additionalContext) {
645
+ Object.assign(ctx.sharedData, additionalContext);
646
+ }
647
+
648
+ // Execute the tool within the context
649
+ try {
650
+ const result = await toolCallContext.run(ctx, async () => {
651
+ // Add invocation metadata to args if tool supports it
652
+ const enhancedArgs = {
653
+ ...args,
654
+ _invocation: {
655
+ caller: ctx.parentTool,
656
+ depth: ctx.depth,
657
+ sharedData: ctx.sharedData
658
+ }
659
+ };
660
+
661
+ return await handler(enhancedArgs);
662
+ });
663
+
664
+ // Cache the result
665
+ ctx.results[cacheKey] = result;
666
+
667
+ // Add metadata to result
668
+ return {
669
+ ...result,
670
+ _meta: {
671
+ tool: toolName,
672
+ depth: ctx.depth,
673
+ duration: Date.now() - ctx.startTime,
674
+ caller: ctx.parentTool
675
+ }
676
+ };
677
+ } catch (error) {
678
+ return {
679
+ error: true,
680
+ code: 'TOOL_ERROR',
681
+ message: error.message,
682
+ tool: toolName,
683
+ stack: error.stack,
684
+ caller: ctx.parentTool
685
+ };
686
+ }
687
+ }
688
+
689
+ /**
690
+ * Invoke multiple tools in sequence with context sharing
691
+ *
692
+ * Results from earlier tools are available to later tools via sharedData.
693
+ *
694
+ * @param {Array<{tool: string, args: object|function}>} toolCalls - Array of tool invocations
695
+ * args can be a function (previousResults) => args for dynamic arguments
696
+ * @param {object} options - Options for the sequence
697
+ * @param {boolean} options.stopOnError - Stop sequence on first error (default: true)
698
+ * @param {object} options.initialContext - Initial shared context
699
+ * @returns {Promise<object>} Sequence result with all tool results
700
+ */
701
+ async function invokeToolSequence(toolCalls, options = {}) {
702
+ const { stopOnError = true, initialContext = {} } = options;
703
+ const results = [];
704
+ const sharedData = { ...initialContext };
705
+
706
+ // Create a root context for the sequence
707
+ const rootCtx = createInvocationContext();
708
+ rootCtx.sharedData = sharedData;
709
+
710
+ for (let i = 0; i < toolCalls.length; i++) {
711
+ const call = toolCalls[i];
712
+ const { tool } = call;
713
+
714
+ // Support dynamic args based on previous results
715
+ let args = call.args || {};
716
+ if (typeof args === 'function') {
717
+ args = args(results, sharedData);
718
+ }
719
+
720
+ const result = await toolCallContext.run(rootCtx, async () => {
721
+ return await invokeTool(tool, args, { context: sharedData });
722
+ });
723
+
724
+ results.push({ tool, result, index: i });
725
+
726
+ // Store result in shared data for next tool
727
+ sharedData[`_result_${tool}`] = result;
728
+ sharedData._lastResult = result;
729
+
730
+ // Stop on error if configured
731
+ if (stopOnError && result.error) {
732
+ return {
733
+ success: false,
734
+ stoppedAt: i,
735
+ stoppedTool: tool,
736
+ error: result,
737
+ results,
738
+ sharedData
739
+ };
740
+ }
741
+ }
742
+
743
+ return {
744
+ success: true,
745
+ results,
746
+ sharedData
747
+ };
748
+ }
749
+
750
+ /**
751
+ * Invoke multiple tools in parallel
752
+ *
753
+ * All tools execute concurrently. Use when tools are independent.
754
+ *
755
+ * @param {Array<{tool: string, args: object}>} toolCalls - Array of tool invocations
756
+ * @param {object} options - Options for parallel execution
757
+ * @param {number} options.concurrency - Max concurrent calls (default: unlimited)
758
+ * @param {boolean} options.settleAll - Wait for all to complete even on errors (default: true)
759
+ * @returns {Promise<object>} Parallel result with all tool results
760
+ */
761
+ async function invokeToolParallel(toolCalls, options = {}) {
762
+ const { concurrency = Infinity, settleAll = true } = options;
763
+
764
+ // Create root context for parallel execution
765
+ const rootCtx = createInvocationContext();
766
+
767
+ // Execute with concurrency limit
768
+ const executeWithLimit = async (calls, limit) => {
769
+ const results = [];
770
+ const executing = new Set();
771
+
772
+ for (const call of calls) {
773
+ const promise = toolCallContext.run(rootCtx, async () => {
774
+ const result = await invokeTool(call.tool, call.args || {});
775
+ return { tool: call.tool, result };
776
+ });
777
+
778
+ results.push(promise);
779
+ executing.add(promise);
780
+
781
+ // Remove from executing set when done
782
+ promise.finally(() => executing.delete(promise));
783
+
784
+ // Wait if at concurrency limit
785
+ if (executing.size >= limit) {
786
+ await Promise.race(executing);
787
+ }
788
+ }
789
+
790
+ return settleAll
791
+ ? Promise.allSettled(results)
792
+ : Promise.all(results);
793
+ };
794
+
795
+ const rawResults = await executeWithLimit(toolCalls, concurrency);
796
+
797
+ // Normalize results
798
+ const normalizedResults = rawResults.map((r, index) => {
799
+ if (r.status === 'fulfilled') {
800
+ return r.value;
801
+ } else {
802
+ return {
803
+ tool: toolCalls[index].tool,
804
+ result: {
805
+ error: true,
806
+ code: 'PARALLEL_ERROR',
807
+ message: r.reason?.message || 'Unknown error'
808
+ }
809
+ };
810
+ }
811
+ });
812
+
813
+ const errors = normalizedResults.filter(r => r.result?.error);
814
+
815
+ return {
816
+ success: errors.length === 0,
817
+ results: normalizedResults,
818
+ errors: errors.length > 0 ? errors : undefined
819
+ };
820
+ }
821
+
822
+ /**
823
+ * Create a tool invoker bound to specific context
824
+ *
825
+ * Useful for passing to tools that need to invoke other tools.
826
+ *
827
+ * @param {object} boundContext - Context to bind to all invocations
828
+ * @returns {object} Bound invoker with invokeTool, invokeSequence, invokeParallel
829
+ */
830
+ function createBoundInvoker(boundContext = {}) {
831
+ return {
832
+ invoke: (toolName, args, options = {}) =>
833
+ invokeTool(toolName, args, { ...options, context: { ...boundContext, ...options.context } }),
834
+
835
+ sequence: (toolCalls, options = {}) =>
836
+ invokeToolSequence(toolCalls, { ...options, initialContext: { ...boundContext, ...options.initialContext } }),
837
+
838
+ parallel: (toolCalls, options) =>
839
+ invokeToolParallel(toolCalls, options),
840
+
841
+ // Convenience method to get a tool's result
842
+ get: async (toolName, args) => {
843
+ const result = await invokeTool(toolName, args, { context: boundContext });
844
+ return result.error ? null : result;
845
+ }
846
+ };
847
+ }
848
+
849
+ /**
850
+ * Get list of available tool names
851
+ * @returns {Array<string>} Tool names
852
+ */
853
+ function getAvailableTools() {
854
+ return Object.keys(toolHandlers);
855
+ }
856
+
857
+ /**
858
+ * Check if a tool exists
859
+ * @param {string} toolName - Tool name to check
860
+ * @returns {boolean} True if tool exists
861
+ */
862
+ function hasTool(toolName) {
863
+ return toolName in toolHandlers;
864
+ }
865
+
866
+ /**
867
+ * Get tool definition by name
868
+ * @param {string} toolName - Tool name
869
+ * @returns {object|null} Tool definition or null
870
+ */
871
+ function getToolDefinition(toolName) {
872
+ const tool = TOOLS.find(t => t.name === toolName);
873
+ return tool || null;
874
+ }
875
+
220
876
  module.exports = {
877
+ // Tool and resource definitions
221
878
  TOOLS,
222
879
  RESOURCES,
880
+
881
+ // Handlers
223
882
  toolHandlers,
224
883
  resourceHandlers,
225
- trackTelemetry
884
+ trackTelemetry,
885
+
886
+ // Tool invocation system
887
+ invokeTool,
888
+ invokeToolSequence,
889
+ invokeToolParallel,
890
+ createBoundInvoker,
891
+
892
+ // Tool introspection
893
+ getAvailableTools,
894
+ hasTool,
895
+ getToolDefinition,
896
+
897
+ // Context utilities (for advanced use)
898
+ getCurrentContext,
899
+ createInvocationContext,
900
+
901
+ // Constants
902
+ MAX_CALL_DEPTH
226
903
  };