claude-flow-novice 2.14.31 → 2.14.33
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/.claude/cfn-data/cfn-loop.db +0 -0
- package/.claude/commands/CFN_LOOP_TASK_MODE.md +1 -1
- package/.claude/skills/cfn-agent-discovery/agents-registry.json +10 -9
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +394 -0
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +521 -0
- package/.claude/skills/cfn-docker-loop-orchestration/SKILL.md +449 -0
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +787 -0
- package/.claude/skills/cfn-docker-redis-coordination/SKILL.md +435 -0
- package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh +635 -0
- package/.claude/skills/cfn-docker-skill-mcp-selection/SKILL.md +289 -0
- package/.claude/skills/cfn-docker-skill-mcp-selection/skill-mcp-selector.js +472 -0
- package/.claude/skills/cfn-loop-validation/config.json +2 -2
- package/.claude/skills/pre-edit-backup/backup.sh +107 -0
- package/README.md +95 -0
- package/claude-assets/agents/README-AGENT_LIFECYCLE.md +10 -37
- package/claude-assets/agents/README-VALIDATION.md +8 -0
- package/claude-assets/agents/cfn-dev-team/README.md +8 -0
- package/claude-assets/agents/cfn-dev-team/coordinators/README.md +9 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +71 -9
- package/claude-assets/agents/cfn-dev-team/developers/README.md +9 -1
- package/claude-assets/agents/cfn-dev-team/documentation/README-VALIDATION.md +8 -0
- package/claude-assets/agents/cfn-dev-team/documentation/agent-type-guidelines.md +10 -0
- package/claude-assets/agents/cfn-dev-team/reviewers/README.md +9 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/quality-metrics.md +10 -0
- package/claude-assets/agents/cfn-dev-team/test-agent.md +10 -0
- package/claude-assets/agents/cfn-dev-team/testers/README.md +9 -1
- package/claude-assets/agents/csuite/cto-agent.md +10 -0
- package/claude-assets/agents/custom/cfn-system-expert.md +128 -1
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +5 -1
- package/claude-assets/agents/docker-team/csuite/c-suite-template.md +5 -1
- package/claude-assets/agents/docker-team/infrastructure/team-coordinator-template.md +5 -1
- package/claude-assets/agents/marketing_hybrid/cost_tracker.md +10 -0
- package/claude-assets/agents/marketing_hybrid/docker_deployer.md +10 -0
- package/claude-assets/agents/marketing_hybrid/zai_worker_spawner.md +10 -0
- package/claude-assets/commands/CFN_LOOP_TASK_MODE.md +1 -1
- package/claude-assets/hooks/cfn-post-execution/memory-cleanup.sh +20 -0
- package/claude-assets/hooks/cfn-pre-execution/memory-check.sh +20 -0
- package/claude-assets/skills/cfn-agent-discovery/agents-registry.json +10 -9
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +70 -10
- package/claude-assets/skills/cfn-loop-validation/config.json +2 -2
- package/claude-assets/skills/cfn-memory-management/SKILL.md +271 -0
- package/claude-assets/skills/cfn-memory-management/check-memory.sh +160 -0
- package/claude-assets/skills/cfn-memory-management/cleanup-memory.sh +197 -0
- package/claude-assets/skills/cfn-redis-data-extraction/SKILL.md +442 -0
- package/claude-assets/skills/cfn-redis-data-extraction/extract.sh +306 -0
- package/claude-assets/skills/cfn-task-config-init/initialize-config.sh +2 -2
- package/claude-assets/skills/hook-pipeline/security-scanner.sh +102 -0
- package/claude-assets/skills/pre-edit-backup/backup.sh +107 -0
- package/dist/cli/agent-command.js +44 -2
- package/dist/cli/agent-command.js.map +1 -1
- package/dist/cli/config-manager.js +91 -109
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/index.js +29 -2
- package/dist/cli/index.js.map +1 -1
- package/package.json +22 -5
- package/scripts/deploy-production.sh +356 -0
- package/scripts/memory-leak-prevention.sh +306 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill-based MCP Selection System
|
|
3
|
+
* Automatically selects MCP servers based on agent skills and requirements
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs').promises;
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const AgentTokenManager = require('../cli/agent-token-manager.js');
|
|
9
|
+
|
|
10
|
+
class SkillMCPSelector {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.tokenManager = new AgentTokenManager(options);
|
|
13
|
+
this.agentConfigPath = options.agentConfigPath || './config/agent-whitelist.json';
|
|
14
|
+
this.skillConfigPath = options.skillConfigPath || './config/skill-requirements.json';
|
|
15
|
+
this.mcpServersPath = options.mcpServersPath || './config/mcp-servers.json';
|
|
16
|
+
|
|
17
|
+
this.agentWhitelist = new Map();
|
|
18
|
+
this.skillRequirements = new Map();
|
|
19
|
+
this.mcpServers = new Map();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async initialize() {
|
|
23
|
+
await this.tokenManager.initialize();
|
|
24
|
+
await this.loadConfigurations();
|
|
25
|
+
console.log('[SkillMCPSelector] Initialized successfully');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async loadConfigurations() {
|
|
29
|
+
// Load agent whitelist
|
|
30
|
+
const agentConfig = await this.loadJson(this.agentConfigPath);
|
|
31
|
+
this.agentWhitelist.clear();
|
|
32
|
+
for (const agent of agentConfig.agents) {
|
|
33
|
+
this.agentWhitelist.set(agent.type, agent);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Load skill requirements
|
|
37
|
+
const skillConfig = await this.loadJson(this.skillConfigPath);
|
|
38
|
+
this.skillRequirements.clear();
|
|
39
|
+
for (const [tool, requirements] of Object.entries(skillConfig.tools)) {
|
|
40
|
+
this.skillRequirements.set(tool, requirements);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Load MCP server configurations
|
|
44
|
+
try {
|
|
45
|
+
const mcpConfig = await this.loadJson(this.mcpServersPath);
|
|
46
|
+
for (const [name, config] of Object.entries(mcpConfig.servers)) {
|
|
47
|
+
this.mcpServers.set(name, config);
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.warn('[SkillMCPSelector] MCP servers config not found, using defaults');
|
|
51
|
+
this.initializeDefaultMCPServers();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async loadJson(filePath) {
|
|
56
|
+
const resolvedPath = path.resolve(filePath);
|
|
57
|
+
const content = await fs.readFile(resolvedPath, 'utf8');
|
|
58
|
+
return JSON.parse(content);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
initializeDefaultMCPServers() {
|
|
62
|
+
// Default MCP server configurations
|
|
63
|
+
const defaultServers = {
|
|
64
|
+
'playwright': {
|
|
65
|
+
name: 'playwright',
|
|
66
|
+
displayName: 'Playwright Browser Automation',
|
|
67
|
+
description: 'Browser automation and screenshot capabilities',
|
|
68
|
+
containerImage: 'claude-flow-novice:mcp-playwright',
|
|
69
|
+
tools: ['take_screenshot', 'search_google', 'navigate_and_interact'],
|
|
70
|
+
skills: ['browser-automation', 'screenshot-capture', 'web-interaction'],
|
|
71
|
+
resourceRequirements: {
|
|
72
|
+
memoryMB: 1024,
|
|
73
|
+
cpuUnits: 2
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
'redis': {
|
|
77
|
+
name: 'redis',
|
|
78
|
+
displayName: 'Redis Database',
|
|
79
|
+
description: 'Redis key-value store operations',
|
|
80
|
+
containerImage: 'claude-flow-novice:mcp-redis',
|
|
81
|
+
tools: ['redis_get', 'redis_set', 'redis_keys'],
|
|
82
|
+
skills: ['redis-operations', 'cache-management'],
|
|
83
|
+
resourceRequirements: {
|
|
84
|
+
memoryMB: 256,
|
|
85
|
+
cpuUnits: 1
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
'postgres': {
|
|
89
|
+
name: 'postgres',
|
|
90
|
+
displayName: 'PostgreSQL Database',
|
|
91
|
+
description: 'PostgreSQL database operations',
|
|
92
|
+
containerImage: 'claude-flow-novice:mcp-postgres',
|
|
93
|
+
tools: ['postgres_query', 'postgres_schema', 'postgres_migrate'],
|
|
94
|
+
skills: ['database-design', 'sql-operations'],
|
|
95
|
+
resourceRequirements: {
|
|
96
|
+
memoryMB: 512,
|
|
97
|
+
cpuUnits: 2
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
'security-scanner': {
|
|
101
|
+
name: 'security-scanner',
|
|
102
|
+
displayName: 'Security Scanner',
|
|
103
|
+
description: 'Security vulnerability scanning and analysis',
|
|
104
|
+
containerImage: 'claude-flow-novice:mcp-security',
|
|
105
|
+
tools: ['security_scan', 'vulnerability_check', 'compliance_validate'],
|
|
106
|
+
skills: ['security-auditing', 'vulnerability-scanning'],
|
|
107
|
+
resourceRequirements: {
|
|
108
|
+
memoryMB: 1536,
|
|
109
|
+
cpuUnits: 4
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
for (const [name, config] of Object.entries(defaultServers)) {
|
|
115
|
+
this.mcpServers.set(name, config);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Determine which MCP servers an agent needs based on their skills
|
|
121
|
+
*/
|
|
122
|
+
selectMCPServers(agentType, agentSkills = null) {
|
|
123
|
+
const agentConfig = this.agentWhitelist.get(agentType);
|
|
124
|
+
if (!agentConfig) {
|
|
125
|
+
throw new Error(`Unknown agent type: ${agentType}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Use provided skills or fall back to agent config
|
|
129
|
+
const skills = agentSkills || agentConfig.skills;
|
|
130
|
+
|
|
131
|
+
// Determine required MCP servers based on skills
|
|
132
|
+
const requiredMCPServers = new Set();
|
|
133
|
+
const skillToMCPServerMap = this.getSkillToMCPServerMapping();
|
|
134
|
+
|
|
135
|
+
// Map skills to required MCP servers
|
|
136
|
+
for (const skill of skills) {
|
|
137
|
+
if (skillToMCPServerMap.has(skill)) {
|
|
138
|
+
const servers = skillToMCPServerMap.get(skill);
|
|
139
|
+
servers.forEach(server => requiredMCPServers.add(server));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add explicitly allowed MCP servers from agent config
|
|
144
|
+
if (agentConfig.allowedMcpServers) {
|
|
145
|
+
agentConfig.allowedMcpServers.forEach(server => requiredMCPServers.add(server));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Convert to array and sort by priority
|
|
149
|
+
const selectedServers = Array.from(requiredMCPServers)
|
|
150
|
+
.filter(server => this.mcpServers.has(server))
|
|
151
|
+
.sort((a, b) => {
|
|
152
|
+
const priorityA = this.mcpServers.get(a).priority || 999;
|
|
153
|
+
const priorityB = this.mcpServers.get(b).priority || 999;
|
|
154
|
+
return priorityA - priorityB;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
agentType,
|
|
159
|
+
agentSkills: skills,
|
|
160
|
+
selectedMCPServers,
|
|
161
|
+
serverDetails: selectedServers.map(server => this.mcpServers.get(server)),
|
|
162
|
+
totalMemoryRequired: selectedServers.reduce((sum, server) =>
|
|
163
|
+
sum + (this.mcpServers.get(server).resourceRequirements?.memoryMB || 512), 0),
|
|
164
|
+
totalCPURequired: selectedServers.reduce((sum, server) =>
|
|
165
|
+
sum + (this.mcpServers.get(server).resourceRequirements?.cpuUnits || 1), 0)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get mapping from skills to required MCP servers
|
|
171
|
+
*/
|
|
172
|
+
getSkillToMCPServerMapping() {
|
|
173
|
+
const skillMap = new Map();
|
|
174
|
+
|
|
175
|
+
// Build mapping from MCP server configurations
|
|
176
|
+
for (const [serverName, serverConfig] of this.mcpServers.entries()) {
|
|
177
|
+
for (const skill of serverConfig.skills || []) {
|
|
178
|
+
if (!skillMap.has(skill)) {
|
|
179
|
+
skillMap.set(skill, new Set());
|
|
180
|
+
}
|
|
181
|
+
skillMap.get(skill).add(serverName);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Also add mappings from tool requirements
|
|
186
|
+
for (const [toolName, toolConfig] of this.skillRequirements.entries()) {
|
|
187
|
+
for (const skill of toolConfig.requiredSkills || []) {
|
|
188
|
+
// Find MCP servers that provide this tool
|
|
189
|
+
for (const [serverName, serverConfig] of this.mcpServers.entries()) {
|
|
190
|
+
if (serverConfig.tools?.includes(toolName)) {
|
|
191
|
+
if (!skillMap.has(skill)) {
|
|
192
|
+
skillMap.set(skill, new Set());
|
|
193
|
+
}
|
|
194
|
+
skillMap.get(skill).add(serverName);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return skillMap;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generate tokens for selected MCP servers
|
|
205
|
+
*/
|
|
206
|
+
async generateMCPTokens(agentType, selectedServers, options = {}) {
|
|
207
|
+
const tokens = [];
|
|
208
|
+
|
|
209
|
+
for (const serverName of selectedServers) {
|
|
210
|
+
try {
|
|
211
|
+
const serverConfig = this.mcpServers.get(serverName);
|
|
212
|
+
if (!serverConfig) {
|
|
213
|
+
console.warn(`[SkillMCPSelector] Server configuration not found: ${serverName}`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Generate server-specific token
|
|
218
|
+
const tokenData = await this.tokenManager.registerAgentToken(agentType, {
|
|
219
|
+
expiresIn: options.expiresIn || '24h',
|
|
220
|
+
description: `Token for ${serverConfig.displayName}`,
|
|
221
|
+
createdBy: 'skill-mcp-selector',
|
|
222
|
+
mcpServer: serverName
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
tokens.push({
|
|
226
|
+
serverName,
|
|
227
|
+
displayName: serverConfig.displayName,
|
|
228
|
+
token: tokenData.token,
|
|
229
|
+
expiresAt: tokenData.expiresAt,
|
|
230
|
+
containerImage: serverConfig.containerImage,
|
|
231
|
+
connectionInfo: this.generateConnectionInfo(serverName, serverConfig, tokenData.token)
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
console.log(`[SkillMCPSelector] Generated token for ${agentType} → ${serverName}`);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`[SkillMCPSelector] Failed to generate token for ${serverName}:`, error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return tokens;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Generate connection information for MCP server
|
|
245
|
+
*/
|
|
246
|
+
generateConnectionInfo(serverName, serverConfig, token) {
|
|
247
|
+
const baseInfo = {
|
|
248
|
+
serverName,
|
|
249
|
+
token,
|
|
250
|
+
authentication: {
|
|
251
|
+
type: 'token-based',
|
|
252
|
+
header: 'x-agent-token',
|
|
253
|
+
agentTypeHeader: 'x-agent-type'
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Add server-specific connection details
|
|
258
|
+
if (serverConfig.connectionType === 'docker') {
|
|
259
|
+
return {
|
|
260
|
+
...baseInfo,
|
|
261
|
+
type: 'docker-container',
|
|
262
|
+
containerName: `mcp-${serverName}`,
|
|
263
|
+
containerImage: serverConfig.containerImage,
|
|
264
|
+
dockerArgs: [
|
|
265
|
+
'run', '--rm', '--init',
|
|
266
|
+
'--name', `mcp-${serverName}`,
|
|
267
|
+
'--memory', `${serverConfig.resourceRequirements?.memoryMB || 512}m`,
|
|
268
|
+
'--cpus', `${serverConfig.resourceRequirements?.cpuUnits || 1}`,
|
|
269
|
+
'-e', `MCP_SERVER=${serverName}`,
|
|
270
|
+
'-e', `MCP_AUTH_REQUIRED=true`,
|
|
271
|
+
'-e', `MCP_REDIS_URL=${process.env.MCP_REDIS_URL || 'redis://localhost:6379'}`,
|
|
272
|
+
serverConfig.containerImage,
|
|
273
|
+
'node', `/app/mcp-${serverName}-server.js`
|
|
274
|
+
]
|
|
275
|
+
};
|
|
276
|
+
} else {
|
|
277
|
+
return {
|
|
278
|
+
...baseInfo,
|
|
279
|
+
type: 'http-endpoint',
|
|
280
|
+
url: serverConfig.url || `http://localhost:${3000 + this.mcpServers.size}`,
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
'x-agent-token': token,
|
|
284
|
+
'x-agent-type': 'dynamic'
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get complete MCP configuration for an agent
|
|
292
|
+
*/
|
|
293
|
+
async getAgentMCPConfiguration(agentType, agentSkills = null, options = {}) {
|
|
294
|
+
try {
|
|
295
|
+
// Select required MCP servers
|
|
296
|
+
const selection = this.selectMCPServers(agentType, agentSkills);
|
|
297
|
+
|
|
298
|
+
// Generate tokens for selected servers
|
|
299
|
+
const tokens = await this.generateMCPTokens(
|
|
300
|
+
agentType,
|
|
301
|
+
selection.selectedMCPServers,
|
|
302
|
+
options
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
agentType,
|
|
307
|
+
agentSkills: selection.agentSkills,
|
|
308
|
+
mcpConfiguration: {
|
|
309
|
+
mcpServers: tokens.reduce((config, tokenInfo) => {
|
|
310
|
+
config[tokenInfo.serverName] = {
|
|
311
|
+
command: 'docker',
|
|
312
|
+
args: tokenInfo.connectionInfo.dockerArgs,
|
|
313
|
+
env: {
|
|
314
|
+
'MCP_SERVER': tokenInfo.serverName,
|
|
315
|
+
'MCP_AUTH_REQUIRED': 'true',
|
|
316
|
+
'AGENT_TOKEN': tokenInfo.token
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
return config;
|
|
320
|
+
}, {})
|
|
321
|
+
},
|
|
322
|
+
selection,
|
|
323
|
+
tokens,
|
|
324
|
+
resourceSummary: {
|
|
325
|
+
totalMemoryMB: selection.totalMemoryRequired,
|
|
326
|
+
totalCPUUnits: selection.totalCPUUnits,
|
|
327
|
+
serverCount: selection.selectedMCPServers.length
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(`[SkillMCPSelector] Failed to get MCP configuration for ${agentType}:`, error);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Generate Docker Compose configuration for agent with MCP servers
|
|
338
|
+
*/
|
|
339
|
+
async generateDockerComposeConfiguration(agentType, agentSkills = null, options = {}) {
|
|
340
|
+
const config = await this.getAgentMCPConfiguration(agentType, agentSkills, options);
|
|
341
|
+
|
|
342
|
+
const dockerCompose = {
|
|
343
|
+
version: '3.8',
|
|
344
|
+
services: {},
|
|
345
|
+
networks: {
|
|
346
|
+
'mcp-network': {
|
|
347
|
+
driver: 'bridge'
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Add agent service
|
|
353
|
+
dockerCompose.services[`${agentType}-agent`] = {
|
|
354
|
+
image: 'claude-flow-novice:agent-container',
|
|
355
|
+
container_name: `agent-${agentType}-${Date.now()}`,
|
|
356
|
+
networks: ['mcp-network'],
|
|
357
|
+
environment: {
|
|
358
|
+
'AGENT_TYPE': agentType,
|
|
359
|
+
'AGENT_MODE': 'containerized',
|
|
360
|
+
'MCP_AUTH_ENABLED': 'true',
|
|
361
|
+
'REDIS_URL': process.env.MCP_REDIS_URL || 'redis://redis:6379'
|
|
362
|
+
},
|
|
363
|
+
volumes: [
|
|
364
|
+
'${PWD}/.claude:/app/.claude:ro',
|
|
365
|
+
'${PWD}/screenshots:/app/screenshots'
|
|
366
|
+
],
|
|
367
|
+
mem_limit: `${config.resourceSummary.totalMemoryMB + 512}m`,
|
|
368
|
+
depends_on: config.selection.selectedMCPServers.map(server => `mcp-${server}`).join(' ')
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Add MCP server services
|
|
372
|
+
for (const tokenInfo of config.tokens) {
|
|
373
|
+
const serverName = `mcp-${tokenInfo.serverName}`;
|
|
374
|
+
const serverConfig = this.mcpServers.get(tokenInfo.serverName);
|
|
375
|
+
|
|
376
|
+
dockerCompose.services[serverName] = {
|
|
377
|
+
image: tokenInfo.containerImage,
|
|
378
|
+
container_name: `${serverName}-${Date.now()}`,
|
|
379
|
+
networks: ['mcp-network'],
|
|
380
|
+
environment: {
|
|
381
|
+
'MCP_SERVER': tokenInfo.serverName,
|
|
382
|
+
'MCP_AUTH_REQUIRED': 'true',
|
|
383
|
+
'MCP_REDIS_URL': process.env.MCP_REDIS_URL || 'redis://redis:6379',
|
|
384
|
+
'AGENT_TOKEN': tokenInfo.token
|
|
385
|
+
},
|
|
386
|
+
volumes: [
|
|
387
|
+
'${PWD}/screenshots:/app/screenshots'
|
|
388
|
+
],
|
|
389
|
+
mem_limit: `${serverConfig.resourceRequirements?.memoryMB || 512}m`,
|
|
390
|
+
cpus: `${(serverConfig.resourceRequirements?.cpuUnits || 1) / 4}`
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Add Redis service if not present
|
|
395
|
+
if (!dockerCompose.services.redis) {
|
|
396
|
+
dockerCompose.services.redis = {
|
|
397
|
+
image: 'redis:7-alpine',
|
|
398
|
+
container_name: 'mcp-redis',
|
|
399
|
+
networks: ['mcp-network'],
|
|
400
|
+
volumes: ['redis-data:/data'],
|
|
401
|
+
mem_limit: '256m'
|
|
402
|
+
};
|
|
403
|
+
dockerCompose.volumes = {
|
|
404
|
+
'redis-data': {}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return dockerCompose;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Get statistics about skill-MCP mappings
|
|
413
|
+
*/
|
|
414
|
+
getStatistics() {
|
|
415
|
+
const skillMap = this.getSkillToMCPServerMapping();
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
totalAgents: this.agentWhitelist.size,
|
|
419
|
+
totalMCPServers: this.mcpServers.size,
|
|
420
|
+
totalSkills: skillMap.size,
|
|
421
|
+
agentTypes: Array.from(this.agentWhitelist.keys()),
|
|
422
|
+
mcpServerNames: Array.from(this.mcpServers.keys()),
|
|
423
|
+
skillCoverage: Array.from(skillMap.entries()).map(([skill, servers]) => ({
|
|
424
|
+
skill,
|
|
425
|
+
requiredServers: Array.from(servers),
|
|
426
|
+
serverCount: servers.size
|
|
427
|
+
}))
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Validate agent-MCP configuration
|
|
433
|
+
*/
|
|
434
|
+
validateConfiguration(agentType, mcpConfiguration) {
|
|
435
|
+
const agentConfig = this.agentWhitelist.get(agentType);
|
|
436
|
+
if (!agentConfig) {
|
|
437
|
+
return { valid: false, errors: [`Unknown agent type: ${agentType}`] };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const errors = [];
|
|
441
|
+
const warnings = [];
|
|
442
|
+
|
|
443
|
+
// Check required MCP servers
|
|
444
|
+
const requiredServers = agentConfig.allowedMcpServers || [];
|
|
445
|
+
const configuredServers = Object.keys(mcpConfiguration.mcpServers || {});
|
|
446
|
+
|
|
447
|
+
for (const server of requiredServers) {
|
|
448
|
+
if (!configuredServers.includes(server)) {
|
|
449
|
+
errors.push(`Required MCP server missing: ${server}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Check for unauthorized servers
|
|
454
|
+
for (const server of configuredServers) {
|
|
455
|
+
if (!requiredServers.includes(server)) {
|
|
456
|
+
warnings.push(`Potentially unauthorized MCP server: ${server}`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
valid: errors.length === 0,
|
|
462
|
+
errors,
|
|
463
|
+
warnings
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async shutdown() {
|
|
468
|
+
await this.tokenManager.shutdown();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
module.exports = SkillMCPSelector;
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
"security-specialist",
|
|
56
56
|
"tester",
|
|
57
57
|
"analyst",
|
|
58
|
-
"architect"
|
|
58
|
+
"system-architect"
|
|
59
59
|
],
|
|
60
60
|
"productOwnerStructure": "team",
|
|
61
61
|
"planningConsensus": {
|
|
62
62
|
"enabled": true,
|
|
63
|
-
"architectTypes": ["
|
|
63
|
+
"architectTypes": ["system-architect", "security-specialist"],
|
|
64
64
|
"threshold": 0.85
|
|
65
65
|
},
|
|
66
66
|
"productOwnerTeam": {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
##############################################################################
|
|
4
|
+
# Pre-Edit Backup Script - Creates safe file backups before modifications
|
|
5
|
+
# Version: 1.0.0
|
|
6
|
+
##############################################################################
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# Function to create backup of a file before editing
|
|
11
|
+
create_backup() {
|
|
12
|
+
local file_path="$1"
|
|
13
|
+
local agent_id="${2:-unknown}"
|
|
14
|
+
local project_root="${3:-$(pwd)}"
|
|
15
|
+
|
|
16
|
+
# Validate inputs
|
|
17
|
+
if [[ -z "$file_path" ]]; then
|
|
18
|
+
echo "Error: File path is required" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Check if file exists
|
|
23
|
+
if [[ ! -f "$file_path" ]]; then
|
|
24
|
+
echo "Warning: File does not exist: $file_path" >&2
|
|
25
|
+
# Create empty backup path for new files
|
|
26
|
+
echo "$project_root/.backups/$agent_id/new-file-$(date +%s)-$(echo "$file_path" | tr '/' '_' | tr ' ' '_')"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Create backup directory structure
|
|
31
|
+
local backup_dir="$project_root/.backups/$agent_id"
|
|
32
|
+
local timestamp=$(date +%s)
|
|
33
|
+
local file_hash=$(md5sum "$file_path" | cut -d' ' -f1)
|
|
34
|
+
local backup_name="${timestamp}_${file_hash}"
|
|
35
|
+
|
|
36
|
+
# Create full backup path
|
|
37
|
+
local full_backup_path="$backup_dir/$backup_name"
|
|
38
|
+
|
|
39
|
+
# Create backup directory
|
|
40
|
+
mkdir -p "$full_backup_path"
|
|
41
|
+
|
|
42
|
+
# Copy original file to backup location
|
|
43
|
+
cp "$file_path" "$full_backup_path/original"
|
|
44
|
+
|
|
45
|
+
# Store backup metadata
|
|
46
|
+
cat > "$full_backup_path/metadata.json" << EOF
|
|
47
|
+
{
|
|
48
|
+
"timestamp": "$timestamp",
|
|
49
|
+
"agent_id": "$agent_id",
|
|
50
|
+
"original_file": "$file_path",
|
|
51
|
+
"file_hash": "$file_hash",
|
|
52
|
+
"backup_path": "$full_backup_path",
|
|
53
|
+
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
54
|
+
}
|
|
55
|
+
EOF
|
|
56
|
+
|
|
57
|
+
# Create revert script
|
|
58
|
+
cat > "$full_backup_path/revert.sh" << EOF
|
|
59
|
+
#!/bin/bash
|
|
60
|
+
# Revert script for $file_path
|
|
61
|
+
set -euo pipefail
|
|
62
|
+
|
|
63
|
+
echo "Reverting file: $file_path"
|
|
64
|
+
cp "$full_backup_path/original" "$file_path"
|
|
65
|
+
echo "✅ File reverted successfully"
|
|
66
|
+
EOF
|
|
67
|
+
|
|
68
|
+
chmod +x "$full_backup_path/revert.sh"
|
|
69
|
+
|
|
70
|
+
# Output backup path for caller
|
|
71
|
+
echo "$full_backup_path"
|
|
72
|
+
|
|
73
|
+
echo "✅ Backup created: $full_backup_path" >&2
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Main execution
|
|
77
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
78
|
+
# Script called directly
|
|
79
|
+
if [[ $# -lt 1 ]]; then
|
|
80
|
+
echo "Usage: $0 <file_path> [--agent-id <id>] [--project-root <path>]" >&2
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
file_path="$1"
|
|
85
|
+
agent_id="unknown"
|
|
86
|
+
project_root="$(pwd)"
|
|
87
|
+
|
|
88
|
+
# Parse optional arguments
|
|
89
|
+
while [[ $# -gt 0 ]]; do
|
|
90
|
+
case $1 in
|
|
91
|
+
--agent-id)
|
|
92
|
+
agent_id="$2"
|
|
93
|
+
shift 2
|
|
94
|
+
;;
|
|
95
|
+
--project-root)
|
|
96
|
+
project_root="$2"
|
|
97
|
+
shift 2
|
|
98
|
+
;;
|
|
99
|
+
*)
|
|
100
|
+
# Skip unknown arguments
|
|
101
|
+
shift
|
|
102
|
+
;;
|
|
103
|
+
esac
|
|
104
|
+
done
|
|
105
|
+
|
|
106
|
+
create_backup "$file_path" "$agent_id" "$project_root"
|
|
107
|
+
fi
|
package/README.md
CHANGED
|
@@ -21,6 +21,18 @@ npx cfn-loop "Implement JWT authentication system" --mode=standard
|
|
|
21
21
|
# Or spawn agents directly
|
|
22
22
|
npx cfn-spawn backend-developer --task-id auth-task
|
|
23
23
|
```
|
|
24
|
+
**Installation:**
|
|
25
|
+
```bash
|
|
26
|
+
npm install claude-flow-novice
|
|
27
|
+
npx cfn-init # Copy namespace-isolated files
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Collision Risk:** ~0.01% (user custom files preserved)
|
|
31
|
+
|
|
32
|
+
**Package:** 573 KB tarball, 2.4 MB unpacked, 303 files (68% reduction from v2.0.0)
|
|
33
|
+
|
|
34
|
+
**Agent Discovery:** Recursive search through `.claude/agents/**/*.md` finds both cfn-dev-team and user custom agents
|
|
35
|
+
|
|
24
36
|
|
|
25
37
|
**What happens on install**:
|
|
26
38
|
- Copies 23 agents to `.claude/agents/cfn-dev-team/`
|
|
@@ -171,6 +183,89 @@ User Task Description
|
|
|
171
183
|
|
|
172
184
|
---
|
|
173
185
|
|
|
186
|
+
### CFN Loop Execution Modes
|
|
187
|
+
|
|
188
|
+
**User selects mode. Main Chat executes the specified slash command.**
|
|
189
|
+
|
|
190
|
+
**Default: Task Mode** (default mode when user doesn't specify)
|
|
191
|
+
|
|
192
|
+
**Available modes:**
|
|
193
|
+
|
|
194
|
+
**1. Task Mode (Default):**
|
|
195
|
+
```bash
|
|
196
|
+
/cfn-loop-task "Task description" --mode=standard
|
|
197
|
+
```
|
|
198
|
+
- Main Chat spawns ALL agents via Task()
|
|
199
|
+
- NO coordinator agent
|
|
200
|
+
- Cost: $0.150/iteration
|
|
201
|
+
- Full visibility in Main Chat
|
|
202
|
+
- Use: Debugging, learning, short tasks (<5 min)
|
|
203
|
+
|
|
204
|
+
**2. CLI Mode (Production):**
|
|
205
|
+
```bash
|
|
206
|
+
/cfn-loop-cli "Task description" --mode=standard
|
|
207
|
+
```
|
|
208
|
+
- Main Chat spawns ONLY cfn-v3-coordinator
|
|
209
|
+
- Coordinator spawns workers via CLI (background)
|
|
210
|
+
- Cost: $0.054/iteration (64% savings vs Task)
|
|
211
|
+
- Use: Production, long tasks, cost-sensitive
|
|
212
|
+
|
|
213
|
+
**Mode selection guidance for users:**
|
|
214
|
+
- "execute cfn loop on X" → `/cfn-loop-task` (default)
|
|
215
|
+
- "use task mode on X" → `/cfn-loop-task`
|
|
216
|
+
- "use cli mode on X" → `/cfn-loop-cli`
|
|
217
|
+
- "production cfn loop on X" → `/cfn-loop-cli`
|
|
218
|
+
|
|
219
|
+
**Architecture patterns:**
|
|
220
|
+
- CLI: Main Chat → cfn-v3-coordinator → orchestrate.sh → CLI workers (background)
|
|
221
|
+
- Task: Main Chat → Task() agents (no coordinator, full visibility)
|
|
222
|
+
|
|
223
|
+
**Cost breakdown:**
|
|
224
|
+
- CLI mode: $0.054/iteration (Z.ai routing for workers)
|
|
225
|
+
- Task mode: $0.150/iteration (Anthropic for all agents)
|
|
226
|
+
|
|
227
|
+
**Context Storage:**
|
|
228
|
+
- CLI mode: Coordinator stores context in persistence layer for agents to retrieve
|
|
229
|
+
- Task mode: Main Chat passes context directly to each Task() spawn (no persistence needed)
|
|
230
|
+
- CLI agents read from context: coordination protocols retrieve task context
|
|
231
|
+
|
|
232
|
+
**Reference:**
|
|
233
|
+
- Implementation details: `planning/cfn-v3/DUAL_MODE_IMPLEMENTATION.md`
|
|
234
|
+
- **Task Mode guide**: `.claude/commands/cfn/CFN_LOOP_TASK_MODE.md` (agent specialization, sprint workflow, backlog management)
|
|
235
|
+
|
|
236
|
+
### Custom Routing (Z.ai Provider Integration)
|
|
237
|
+
|
|
238
|
+
**Provider Routing Model:**
|
|
239
|
+
- **Task() agents** → Use Main Chat provider (Anthropic)
|
|
240
|
+
- **CLI-spawned agents** → Use custom routing (Z.ai when enabled)
|
|
241
|
+
|
|
242
|
+
**Enable Custom Routing (One-Time Setup):**
|
|
243
|
+
```bash
|
|
244
|
+
/custom-routing-activate
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Switch Provider to Z.ai or Claude Subscription**
|
|
248
|
+
```bash
|
|
249
|
+
/switch-api status
|
|
250
|
+
```
|
|
251
|
+
```bash
|
|
252
|
+
/switch-api zai
|
|
253
|
+
```
|
|
254
|
+
```bash
|
|
255
|
+
/switch-api max
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Cost Impact:**
|
|
259
|
+
```
|
|
260
|
+
Without Custom Routing:
|
|
261
|
+
- CLI agents use Anthropic ($3-15/1M tokens)
|
|
262
|
+
|
|
263
|
+
With Custom Routing:
|
|
264
|
+
- CLI agents use Z.ai ($0.50/1M tokens)
|
|
265
|
+
- ~5x cost reduction per CLI agent call
|
|
266
|
+
- Combined with CLI spawning: 95-98% total savings vs Task tool
|
|
267
|
+
```
|
|
268
|
+
|
|
174
269
|
## 🧠 AI-Driven Intelligence (43 Modular Skills)
|
|
175
270
|
|
|
176
271
|
### Phase 1: Foundation (5 Skills)
|