@girardmedia/bootspring 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/agents/README.md +93 -0
- package/agents/api-expert/context.md +416 -0
- package/agents/architecture-expert/context.md +454 -0
- package/agents/backend-expert/context.md +483 -0
- package/agents/code-review-expert/context.md +365 -0
- package/agents/database-expert/context.md +250 -0
- package/agents/devops-expert/context.md +446 -0
- package/agents/frontend-expert/context.md +364 -0
- package/agents/index.js +140 -0
- package/agents/performance-expert/context.md +377 -0
- package/agents/security-expert/context.md +343 -0
- package/agents/testing-expert/context.md +414 -0
- package/agents/ui-ux-expert/context.md +448 -0
- package/agents/vercel-expert/context.md +426 -0
- package/bin/bootspring.js +310 -0
- package/cli/agent.js +337 -0
- package/cli/context.js +194 -0
- package/cli/dashboard.js +150 -0
- package/cli/generate.js +294 -0
- package/cli/init.js +410 -0
- package/cli/loop.js +421 -0
- package/cli/mcp.js +241 -0
- package/cli/memory.js +303 -0
- package/cli/orchestrator.js +400 -0
- package/cli/plugin.js +451 -0
- package/cli/quality.js +332 -0
- package/cli/skill.js +369 -0
- package/cli/task.js +628 -0
- package/cli/telemetry.js +114 -0
- package/cli/todo.js +614 -0
- package/cli/update.js +312 -0
- package/core/config.js +245 -0
- package/core/context.js +329 -0
- package/core/entitlements.js +209 -0
- package/core/index.js +43 -0
- package/core/policies.js +68 -0
- package/core/telemetry.js +247 -0
- package/core/utils.js +380 -0
- package/dashboard/server.js +818 -0
- package/docs/integrations/claude-code.md +42 -0
- package/docs/integrations/codex.md +42 -0
- package/docs/mcp-api-platform.md +102 -0
- package/generators/generate.js +598 -0
- package/generators/index.js +18 -0
- package/hooks/context-detector.js +177 -0
- package/hooks/index.js +35 -0
- package/hooks/prompt-enhancer.js +289 -0
- package/intelligence/git-memory.js +551 -0
- package/intelligence/index.js +59 -0
- package/intelligence/orchestrator.js +964 -0
- package/intelligence/prd.js +447 -0
- package/intelligence/recommendation-weights.json +18 -0
- package/intelligence/recommendations.js +234 -0
- package/mcp/capabilities.js +71 -0
- package/mcp/contracts/mcp-contract.v1.json +497 -0
- package/mcp/registry.js +213 -0
- package/mcp/response-formatter.js +462 -0
- package/mcp/server.js +99 -0
- package/mcp/tools/agent-tool.js +137 -0
- package/mcp/tools/capabilities-tool.js +54 -0
- package/mcp/tools/context-tool.js +49 -0
- package/mcp/tools/dashboard-tool.js +58 -0
- package/mcp/tools/generate-tool.js +46 -0
- package/mcp/tools/loop-tool.js +134 -0
- package/mcp/tools/memory-tool.js +180 -0
- package/mcp/tools/orchestrator-tool.js +232 -0
- package/mcp/tools/plugin-tool.js +76 -0
- package/mcp/tools/quality-tool.js +47 -0
- package/mcp/tools/skill-tool.js +233 -0
- package/mcp/tools/telemetry-tool.js +95 -0
- package/mcp/tools/todo-tool.js +133 -0
- package/package.json +98 -0
- package/plugins/index.js +141 -0
- package/quality/index.js +380 -0
- package/quality/lint-budgets.json +19 -0
- package/skills/index.js +787 -0
- package/skills/patterns/README.md +163 -0
- package/skills/patterns/api/route-handler.md +217 -0
- package/skills/patterns/api/server-action.md +249 -0
- package/skills/patterns/auth/clerk.md +132 -0
- package/skills/patterns/database/prisma.md +180 -0
- package/skills/patterns/payments/stripe.md +272 -0
- package/skills/patterns/security/validation.md +268 -0
- package/skills/patterns/testing/vitest.md +307 -0
- package/templates/bootspring.config.js +83 -0
- package/templates/mcp.json +9 -0
package/mcp/registry.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP registry composition
|
|
3
|
+
* Central place for tool/resource definitions and handler wiring.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
const config = require('../core/config');
|
|
10
|
+
const context = require('../core/context');
|
|
11
|
+
const todoModule = require('../cli/todo');
|
|
12
|
+
const agentModule = require('../cli/agent');
|
|
13
|
+
const generateModule = require('../cli/generate');
|
|
14
|
+
const intelligence = require('../intelligence');
|
|
15
|
+
const gitMemory = require('../intelligence/git-memory');
|
|
16
|
+
const prdModule = require('../intelligence/prd');
|
|
17
|
+
const quality = require('../quality');
|
|
18
|
+
const skills = require('../skills');
|
|
19
|
+
const entitlements = require('../core/entitlements');
|
|
20
|
+
const telemetry = require('../core/telemetry');
|
|
21
|
+
const capabilities = require('./capabilities');
|
|
22
|
+
const format = require('./response-formatter');
|
|
23
|
+
|
|
24
|
+
const agentTool = require('./tools/agent-tool');
|
|
25
|
+
const capabilitiesTool = require('./tools/capabilities-tool');
|
|
26
|
+
const contextTool = require('./tools/context-tool');
|
|
27
|
+
const dashboardTool = require('./tools/dashboard-tool');
|
|
28
|
+
const generateTool = require('./tools/generate-tool');
|
|
29
|
+
const loopTool = require('./tools/loop-tool');
|
|
30
|
+
const memoryTool = require('./tools/memory-tool');
|
|
31
|
+
const orchestratorTool = require('./tools/orchestrator-tool');
|
|
32
|
+
const pluginTool = require('./tools/plugin-tool');
|
|
33
|
+
const qualityTool = require('./tools/quality-tool');
|
|
34
|
+
const skillTool = require('./tools/skill-tool');
|
|
35
|
+
const telemetryTool = require('./tools/telemetry-tool');
|
|
36
|
+
const todoTool = require('./tools/todo-tool');
|
|
37
|
+
|
|
38
|
+
const recommendations = intelligence.createRecommendationsEngine({
|
|
39
|
+
intelligence,
|
|
40
|
+
telemetry,
|
|
41
|
+
skills,
|
|
42
|
+
entitlements
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
function trackTelemetry(event, payload) {
|
|
46
|
+
try {
|
|
47
|
+
telemetry.emitEvent(event, payload);
|
|
48
|
+
} catch {
|
|
49
|
+
// Do not break MCP responses due to telemetry failures.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const TOOLS = [
|
|
54
|
+
contextTool.getToolDefinition(),
|
|
55
|
+
agentTool.getToolDefinition(),
|
|
56
|
+
todoTool.getToolDefinition(),
|
|
57
|
+
generateTool.getToolDefinition(),
|
|
58
|
+
skillTool.getToolDefinition(),
|
|
59
|
+
qualityTool.getToolDefinition(),
|
|
60
|
+
dashboardTool.getToolDefinition(),
|
|
61
|
+
pluginTool.getToolDefinition(),
|
|
62
|
+
capabilitiesTool.getToolDefinition(),
|
|
63
|
+
telemetryTool.getToolDefinition(),
|
|
64
|
+
orchestratorTool.getToolDefinition(),
|
|
65
|
+
loopTool.getToolDefinition(),
|
|
66
|
+
memoryTool.getToolDefinition()
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const RESOURCES = [
|
|
70
|
+
{
|
|
71
|
+
uri: 'bootspring://context/project',
|
|
72
|
+
name: 'Project Context',
|
|
73
|
+
description: 'Current project context and configuration',
|
|
74
|
+
mimeType: 'application/json'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
uri: 'bootspring://context/claude.md',
|
|
78
|
+
name: 'CLAUDE.md Content',
|
|
79
|
+
description: 'Generated AI context file',
|
|
80
|
+
mimeType: 'text/markdown'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
uri: 'bootspring://context/todo.md',
|
|
84
|
+
name: 'Todo List',
|
|
85
|
+
description: 'Project todo list',
|
|
86
|
+
mimeType: 'text/markdown'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
uri: 'bootspring://agents/list',
|
|
90
|
+
name: 'Agent List',
|
|
91
|
+
description: 'Available specialized agents',
|
|
92
|
+
mimeType: 'application/json'
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const toolHandlers = {
|
|
97
|
+
bootspring_capabilities: capabilitiesTool.createHandler({ capabilities }),
|
|
98
|
+
bootspring_orchestrator: orchestratorTool.createHandler({
|
|
99
|
+
intelligence,
|
|
100
|
+
entitlements,
|
|
101
|
+
recommendations,
|
|
102
|
+
trackTelemetry,
|
|
103
|
+
format
|
|
104
|
+
}),
|
|
105
|
+
bootspring_context: contextTool.createHandler({
|
|
106
|
+
config,
|
|
107
|
+
context,
|
|
108
|
+
format
|
|
109
|
+
}),
|
|
110
|
+
bootspring_skill: skillTool.createHandler({
|
|
111
|
+
skills,
|
|
112
|
+
entitlements,
|
|
113
|
+
trackTelemetry
|
|
114
|
+
}),
|
|
115
|
+
bootspring_agent: agentTool.createHandler({
|
|
116
|
+
agentModule,
|
|
117
|
+
format
|
|
118
|
+
}),
|
|
119
|
+
bootspring_todo: todoTool.createHandler({
|
|
120
|
+
config,
|
|
121
|
+
todoModule,
|
|
122
|
+
format
|
|
123
|
+
}),
|
|
124
|
+
bootspring_quality: qualityTool.createHandler({
|
|
125
|
+
config,
|
|
126
|
+
quality,
|
|
127
|
+
format
|
|
128
|
+
}),
|
|
129
|
+
bootspring_generate: generateTool.createHandler({
|
|
130
|
+
config,
|
|
131
|
+
context,
|
|
132
|
+
generateModule
|
|
133
|
+
}),
|
|
134
|
+
bootspring_dashboard: dashboardTool.createHandler({ config }),
|
|
135
|
+
bootspring_plugin: pluginTool.createHandler({ config }),
|
|
136
|
+
bootspring_loop: loopTool.createHandler({
|
|
137
|
+
prdModule,
|
|
138
|
+
format
|
|
139
|
+
}),
|
|
140
|
+
bootspring_memory: memoryTool.createHandler({ gitMemory }),
|
|
141
|
+
bootspring_telemetry: telemetryTool.createHandler({ telemetry }),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const resourceHandlers = {
|
|
145
|
+
'bootspring://context/project': async () => {
|
|
146
|
+
const cfg = config.load();
|
|
147
|
+
const ctx = context.get({ config: cfg });
|
|
148
|
+
return {
|
|
149
|
+
contents: [{
|
|
150
|
+
uri: 'bootspring://context/project',
|
|
151
|
+
mimeType: 'application/json',
|
|
152
|
+
text: JSON.stringify(ctx, null, 2)
|
|
153
|
+
}]
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
'bootspring://context/claude.md': async () => {
|
|
158
|
+
const cfg = config.load();
|
|
159
|
+
const claudePath = path.join(cfg._projectRoot, cfg.paths?.context || 'CLAUDE.md');
|
|
160
|
+
let content = '';
|
|
161
|
+
if (fs.existsSync(claudePath)) {
|
|
162
|
+
content = fs.readFileSync(claudePath, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
contents: [{
|
|
166
|
+
uri: 'bootspring://context/claude.md',
|
|
167
|
+
mimeType: 'text/markdown',
|
|
168
|
+
text: content
|
|
169
|
+
}]
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
'bootspring://context/todo.md': async () => {
|
|
174
|
+
const cfg = config.load();
|
|
175
|
+
const todoPath = path.join(cfg._projectRoot, cfg.paths?.todo || 'todo.md');
|
|
176
|
+
let content = '';
|
|
177
|
+
if (fs.existsSync(todoPath)) {
|
|
178
|
+
content = fs.readFileSync(todoPath, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
contents: [{
|
|
182
|
+
uri: 'bootspring://context/todo.md',
|
|
183
|
+
mimeType: 'text/markdown',
|
|
184
|
+
text: content
|
|
185
|
+
}]
|
|
186
|
+
};
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
'bootspring://agents/list': async () => {
|
|
190
|
+
const agents = Object.entries(agentModule.AGENTS).map(([id, agent]) => ({
|
|
191
|
+
id,
|
|
192
|
+
name: agent.name,
|
|
193
|
+
category: agent.category,
|
|
194
|
+
description: agent.description,
|
|
195
|
+
expertise: agent.expertise
|
|
196
|
+
}));
|
|
197
|
+
return {
|
|
198
|
+
contents: [{
|
|
199
|
+
uri: 'bootspring://agents/list',
|
|
200
|
+
mimeType: 'application/json',
|
|
201
|
+
text: JSON.stringify(agents, null, 2)
|
|
202
|
+
}]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
module.exports = {
|
|
208
|
+
TOOLS,
|
|
209
|
+
RESOURCES,
|
|
210
|
+
toolHandlers,
|
|
211
|
+
resourceHandlers,
|
|
212
|
+
trackTelemetry
|
|
213
|
+
};
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring MCP Response Formatter
|
|
3
|
+
* Creates well-structured, human-friendly tool responses
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @module mcp
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format a success response with structured output
|
|
11
|
+
* @param {object} options - Response options
|
|
12
|
+
* @param {string} options.summary - Brief summary of what happened
|
|
13
|
+
* @param {any} options.data - Main data payload
|
|
14
|
+
* @param {string[]} [options.hints] - Optional hints for next actions
|
|
15
|
+
* @param {object} [options.meta] - Optional metadata
|
|
16
|
+
* @returns {object} MCP-formatted response
|
|
17
|
+
*/
|
|
18
|
+
function success({ summary, data, hints = [], meta = {} }) {
|
|
19
|
+
const sections = [];
|
|
20
|
+
|
|
21
|
+
// Summary section
|
|
22
|
+
if (summary) {
|
|
23
|
+
sections.push(`✓ ${summary}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Data section
|
|
27
|
+
if (data !== undefined && data !== null) {
|
|
28
|
+
if (typeof data === 'string') {
|
|
29
|
+
sections.push(data);
|
|
30
|
+
} else {
|
|
31
|
+
sections.push(JSON.stringify(data, null, 2));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Hints section
|
|
36
|
+
if (hints.length > 0) {
|
|
37
|
+
sections.push('\n💡 Hints:');
|
|
38
|
+
hints.forEach(hint => sections.push(` • ${hint}`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
content: [{
|
|
43
|
+
type: 'text',
|
|
44
|
+
text: sections.join('\n')
|
|
45
|
+
}],
|
|
46
|
+
_meta: meta
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Format an error response
|
|
52
|
+
* @param {string} message - Error message
|
|
53
|
+
* @param {string[]} [suggestions] - Helpful suggestions
|
|
54
|
+
* @returns {object} MCP-formatted error response
|
|
55
|
+
*/
|
|
56
|
+
function error(message, suggestions = []) {
|
|
57
|
+
const sections = [`✗ Error: ${message}`];
|
|
58
|
+
|
|
59
|
+
if (suggestions.length > 0) {
|
|
60
|
+
sections.push('\n💡 Suggestions:');
|
|
61
|
+
suggestions.forEach(s => sections.push(` • ${s}`));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: sections.join('\n')
|
|
68
|
+
}],
|
|
69
|
+
isError: true
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Format a list response with items
|
|
75
|
+
* @param {object} options - List options
|
|
76
|
+
* @param {string} options.title - List title
|
|
77
|
+
* @param {any[]} options.items - Items to list
|
|
78
|
+
* @param {function} options.formatter - Function to format each item
|
|
79
|
+
* @param {string} [options.emptyMessage] - Message when list is empty
|
|
80
|
+
* @param {string[]} [options.hints] - Optional hints
|
|
81
|
+
* @returns {object} MCP-formatted response
|
|
82
|
+
*/
|
|
83
|
+
function list({ title, items, formatter, emptyMessage = 'No items found', hints = [] }) {
|
|
84
|
+
const sections = [];
|
|
85
|
+
|
|
86
|
+
sections.push(`📋 ${title}`);
|
|
87
|
+
sections.push(` ${items.length} item(s)\n`);
|
|
88
|
+
|
|
89
|
+
if (items.length === 0) {
|
|
90
|
+
sections.push(` ${emptyMessage}`);
|
|
91
|
+
} else {
|
|
92
|
+
items.forEach((item, index) => {
|
|
93
|
+
const formatted = formatter ? formatter(item, index) : String(item);
|
|
94
|
+
sections.push(formatted);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (hints.length > 0) {
|
|
99
|
+
sections.push('\n💡 Hints:');
|
|
100
|
+
hints.forEach(hint => sections.push(` • ${hint}`));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
content: [{
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: sections.join('\n')
|
|
107
|
+
}]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Format agent details for display
|
|
113
|
+
* @param {string} id - Agent ID
|
|
114
|
+
* @param {object} agent - Agent data
|
|
115
|
+
* @returns {object} MCP-formatted response
|
|
116
|
+
*/
|
|
117
|
+
function agentDetails(id, agent) {
|
|
118
|
+
const sections = [
|
|
119
|
+
`🤖 Agent: ${agent.name}`,
|
|
120
|
+
` ID: ${id}`,
|
|
121
|
+
` Category: ${agent.category || 'General'}`,
|
|
122
|
+
'',
|
|
123
|
+
agent.description,
|
|
124
|
+
'',
|
|
125
|
+
'📚 Expertise:',
|
|
126
|
+
...agent.expertise.map(e => ` • ${e}`),
|
|
127
|
+
''
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
if (agent.systemPrompt) {
|
|
131
|
+
sections.push('📝 System Prompt Available: Yes');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
sections.push(
|
|
135
|
+
'',
|
|
136
|
+
'💡 Usage:',
|
|
137
|
+
' Invoke this agent with a topic to get specialized assistance'
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
content: [{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: sections.join('\n')
|
|
144
|
+
}]
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Format skill details for display
|
|
150
|
+
* @param {string} id - Skill ID
|
|
151
|
+
* @param {object} skill - Skill data
|
|
152
|
+
* @param {string} [content] - Skill content (markdown)
|
|
153
|
+
* @returns {object} MCP-formatted response
|
|
154
|
+
*/
|
|
155
|
+
function skillDetails(id, skill, content = null) {
|
|
156
|
+
const sections = [
|
|
157
|
+
`📖 Skill: ${skill.name}`,
|
|
158
|
+
` ID: ${id}`,
|
|
159
|
+
'',
|
|
160
|
+
skill.description,
|
|
161
|
+
''
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
if (skill.tags && skill.tags.length > 0) {
|
|
165
|
+
sections.push(`🏷️ Tags: ${skill.tags.join(', ')}`);
|
|
166
|
+
sections.push('');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (content) {
|
|
170
|
+
sections.push('---', '', content);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
content: [{
|
|
175
|
+
type: 'text',
|
|
176
|
+
text: sections.join('\n')
|
|
177
|
+
}]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Format todo list for display
|
|
183
|
+
* @param {object[]} pending - Pending todos
|
|
184
|
+
* @param {object[]} completed - Completed todos
|
|
185
|
+
* @returns {object} MCP-formatted response
|
|
186
|
+
*/
|
|
187
|
+
function todoList(pending, completed) {
|
|
188
|
+
const sections = [
|
|
189
|
+
'📋 Project Todo List',
|
|
190
|
+
''
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
sections.push(`⏳ Pending (${pending.length}):`);
|
|
194
|
+
if (pending.length === 0) {
|
|
195
|
+
sections.push(' No pending tasks! 🎉');
|
|
196
|
+
} else {
|
|
197
|
+
pending.forEach((t, i) => {
|
|
198
|
+
sections.push(` ${i}. [ ] ${t.text}`);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
sections.push('');
|
|
203
|
+
sections.push(`✅ Completed (${completed.length}):`);
|
|
204
|
+
if (completed.length === 0) {
|
|
205
|
+
sections.push(' No completed tasks yet');
|
|
206
|
+
} else {
|
|
207
|
+
completed.slice(0, 5).forEach(t => {
|
|
208
|
+
sections.push(` [x] ${t.text}`);
|
|
209
|
+
});
|
|
210
|
+
if (completed.length > 5) {
|
|
211
|
+
sections.push(` ... and ${completed.length - 5} more`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
sections.push(
|
|
216
|
+
'',
|
|
217
|
+
'💡 Actions:',
|
|
218
|
+
' • Add: bootspring_todo action=add text="your task"',
|
|
219
|
+
' • Complete: bootspring_todo action=done index=0',
|
|
220
|
+
' • Remove: bootspring_todo action=remove index=0'
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
content: [{
|
|
225
|
+
type: 'text',
|
|
226
|
+
text: sections.join('\n')
|
|
227
|
+
}]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Format quality gate results
|
|
233
|
+
* @param {object} results - Gate execution results
|
|
234
|
+
* @returns {object} MCP-formatted response
|
|
235
|
+
*/
|
|
236
|
+
function qualityResults(results) {
|
|
237
|
+
const statusIcon = results.status === 'pass' ? '✅' : '❌';
|
|
238
|
+
const sections = [
|
|
239
|
+
`${statusIcon} Quality Gate: ${results.name || results.gate}`,
|
|
240
|
+
` Status: ${results.status.toUpperCase()}`,
|
|
241
|
+
` Passed: ${results.passed} | Failed: ${results.failed} | Skipped: ${results.skipped || 0}`,
|
|
242
|
+
''
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
sections.push('📊 Check Results:');
|
|
246
|
+
for (const check of results.results) {
|
|
247
|
+
const icon = check.status === 'pass'
|
|
248
|
+
? '✓'
|
|
249
|
+
: check.status === 'fail'
|
|
250
|
+
? '✗'
|
|
251
|
+
: '○';
|
|
252
|
+
sections.push(` ${icon} ${check.name}: ${check.status}`);
|
|
253
|
+
if (check.status === 'fail' && check.message) {
|
|
254
|
+
const shortMessage = check.message.split('\n')[0].slice(0, 60);
|
|
255
|
+
sections.push(` ${shortMessage}...`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (results.status === 'fail') {
|
|
260
|
+
sections.push(
|
|
261
|
+
'',
|
|
262
|
+
'💡 Next Steps:',
|
|
263
|
+
' • Fix the failing checks and re-run the gate',
|
|
264
|
+
' • Use --skip <check> to skip specific checks if needed'
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
content: [{
|
|
270
|
+
type: 'text',
|
|
271
|
+
text: sections.join('\n')
|
|
272
|
+
}]
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Format context validation results
|
|
278
|
+
* @param {object} validation - Validation results
|
|
279
|
+
* @returns {object} MCP-formatted response
|
|
280
|
+
*/
|
|
281
|
+
function contextValidation(validation) {
|
|
282
|
+
const statusIcon = validation.valid ? '✅' : '⚠️';
|
|
283
|
+
const sections = [
|
|
284
|
+
`${statusIcon} Project Validation`,
|
|
285
|
+
` Valid: ${validation.valid}`,
|
|
286
|
+
''
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
if (validation.errors && validation.errors.length > 0) {
|
|
290
|
+
sections.push('❌ Errors:');
|
|
291
|
+
validation.errors.forEach(e => sections.push(` • ${e}`));
|
|
292
|
+
sections.push('');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
296
|
+
sections.push('⚠️ Warnings:');
|
|
297
|
+
validation.warnings.forEach(w => sections.push(` • ${w}`));
|
|
298
|
+
sections.push('');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (validation.suggestions && validation.suggestions.length > 0) {
|
|
302
|
+
sections.push('💡 Suggestions:');
|
|
303
|
+
validation.suggestions.forEach(s => sections.push(` • ${s}`));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
content: [{
|
|
308
|
+
type: 'text',
|
|
309
|
+
text: sections.join('\n')
|
|
310
|
+
}]
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Format project context summary
|
|
316
|
+
* @param {object} ctx - Project context
|
|
317
|
+
* @returns {object} MCP-formatted response
|
|
318
|
+
*/
|
|
319
|
+
function contextSummary(ctx) {
|
|
320
|
+
const sections = [
|
|
321
|
+
`📁 Project: ${ctx.project?.name || 'Unknown'}`,
|
|
322
|
+
` Version: ${ctx.project?.version || 'N/A'}`,
|
|
323
|
+
''
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
if (ctx.stack) {
|
|
327
|
+
sections.push('🛠️ Tech Stack:');
|
|
328
|
+
if (ctx.stack.framework) sections.push(` Framework: ${ctx.stack.framework}`);
|
|
329
|
+
if (ctx.stack.language) sections.push(` Language: ${ctx.stack.language}`);
|
|
330
|
+
if (ctx.stack.database) sections.push(` Database: ${ctx.stack.database}`);
|
|
331
|
+
sections.push('');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (ctx.plugins && Object.keys(ctx.plugins).length > 0) {
|
|
335
|
+
sections.push('🔌 Active Plugins:');
|
|
336
|
+
Object.keys(ctx.plugins).forEach(p => {
|
|
337
|
+
sections.push(` • ${p}`);
|
|
338
|
+
});
|
|
339
|
+
sections.push('');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
sections.push(
|
|
343
|
+
'💡 Commands:',
|
|
344
|
+
' • bootspring_context action=validate - Check project health',
|
|
345
|
+
' • bootspring_generate - Regenerate CLAUDE.md',
|
|
346
|
+
' • bootspring_agent action=list - See available agents'
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
content: [{
|
|
351
|
+
type: 'text',
|
|
352
|
+
text: sections.join('\n')
|
|
353
|
+
}]
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Format orchestrator status
|
|
359
|
+
* @param {object} status - Orchestrator status
|
|
360
|
+
* @returns {object} MCP-formatted response
|
|
361
|
+
*/
|
|
362
|
+
function orchestratorStatus(status) {
|
|
363
|
+
const sections = [
|
|
364
|
+
'🎯 Orchestrator Status',
|
|
365
|
+
'',
|
|
366
|
+
` Phase: ${status.currentPhase || 'Unknown'}`,
|
|
367
|
+
` Available Agents: ${status.availableAgents || 0}`,
|
|
368
|
+
` Active Workflow: ${status.activeWorkflow || 'None'}`,
|
|
369
|
+
''
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
if (status.activeWorkflowSignalProgress) {
|
|
373
|
+
sections.push(
|
|
374
|
+
` Checkpoint Signals: ${status.activeWorkflowSignalProgress.completedSignals.length}/${status.activeWorkflowSignalProgress.totalSignals}`,
|
|
375
|
+
''
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (status.recentSuggestions && status.recentSuggestions.length > 0) {
|
|
380
|
+
sections.push('💡 Recent Suggestions:');
|
|
381
|
+
status.recentSuggestions.forEach(s => {
|
|
382
|
+
sections.push(` • ${s.text || s}`);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
content: [{
|
|
388
|
+
type: 'text',
|
|
389
|
+
text: sections.join('\n')
|
|
390
|
+
}]
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Format PRD/loop status
|
|
396
|
+
* @param {object} prd - PRD data
|
|
397
|
+
* @param {object} progress - Progress info
|
|
398
|
+
* @returns {object} MCP-formatted response
|
|
399
|
+
*/
|
|
400
|
+
function loopStatus(prd, progress) {
|
|
401
|
+
const sections = [
|
|
402
|
+
`📋 PRD: ${prd.name}`,
|
|
403
|
+
'',
|
|
404
|
+
` Progress: ${progress.completed}/${progress.total} stories (${Math.round(progress.percentage)}%)`,
|
|
405
|
+
''
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
const progressBar = generateProgressBar(progress.percentage);
|
|
409
|
+
sections.push(` ${progressBar}`);
|
|
410
|
+
sections.push('');
|
|
411
|
+
|
|
412
|
+
sections.push('📖 Stories:');
|
|
413
|
+
prd.stories.forEach(s => {
|
|
414
|
+
const icon = s.status === 'complete'
|
|
415
|
+
? '✅'
|
|
416
|
+
: s.status === 'in-progress'
|
|
417
|
+
? '🔄'
|
|
418
|
+
: '⏳';
|
|
419
|
+
sections.push(` ${icon} ${s.title} (${s.status})`);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
sections.push(
|
|
423
|
+
'',
|
|
424
|
+
'💡 Actions:',
|
|
425
|
+
' • bootspring_loop action=next - Get next story to work on',
|
|
426
|
+
' • bootspring_loop action=complete storyId=X - Mark story complete'
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
content: [{
|
|
431
|
+
type: 'text',
|
|
432
|
+
text: sections.join('\n')
|
|
433
|
+
}]
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Generate a simple progress bar
|
|
439
|
+
* @param {number} percentage - Progress percentage (0-100)
|
|
440
|
+
* @returns {string}
|
|
441
|
+
*/
|
|
442
|
+
function generateProgressBar(percentage) {
|
|
443
|
+
const width = 20;
|
|
444
|
+
const filled = Math.round((percentage / 100) * width);
|
|
445
|
+
const empty = width - filled;
|
|
446
|
+
return '[' + '█'.repeat(filled) + '░'.repeat(empty) + '] ' + Math.round(percentage) + '%';
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
module.exports = {
|
|
450
|
+
success,
|
|
451
|
+
error,
|
|
452
|
+
list,
|
|
453
|
+
agentDetails,
|
|
454
|
+
skillDetails,
|
|
455
|
+
todoList,
|
|
456
|
+
qualityResults,
|
|
457
|
+
contextValidation,
|
|
458
|
+
contextSummary,
|
|
459
|
+
orchestratorStatus,
|
|
460
|
+
loopStatus,
|
|
461
|
+
generateProgressBar
|
|
462
|
+
};
|