@aigentics/agent-toolkit
Version:
Comprehensive toolkit for validating and managing Claude Flow agent systems
424 lines (372 loc) • 11.6 kB
JavaScript
/**
* Example: Custom validators for Claude Flow Agent Toolkit
*
* This example shows how to create and use custom validation rules
* for your specific agent requirements.
*/
import { validator } from '../src/index.mjs';
/**
* Custom validator for ensuring agents follow naming conventions
*/
function validateNamingConvention(agent) {
const errors = [];
// Check agent name format (kebab-case)
if (!/^[a-z]+(-[a-z]+)*$/.test(agent.name)) {
errors.push({
field: 'name',
message: 'Agent name must be in kebab-case (lowercase with hyphens)',
severity: 'error'
});
}
// Check capability naming (lowercase, no spaces)
if (agent.capabilities) {
agent.capabilities.forEach((cap, index) => {
if (!/^[a-z-]+$/.test(cap)) {
errors.push({
field: `capabilities[${index}]`,
message: `Capability "${cap}" must be lowercase with hyphens only`,
severity: 'error'
});
}
});
}
return errors;
}
/**
* Custom validator for security constraints
*/
function validateSecurityConstraints(agent) {
const errors = [];
const warnings = [];
// Check for dangerous tool combinations
const dangerousCombo = ['Bash', 'Write', 'WebFetch'];
const hasAllDangerous = dangerousCombo.every(tool =>
agent.tools && agent.tools.includes(tool)
);
if (hasAllDangerous) {
warnings.push({
field: 'tools',
message: 'Agent has full system access (Bash + Write + WebFetch). Ensure this is intended.',
severity: 'warning'
});
}
// Check for sensitive data handling
if (agent.prompts && agent.prompts.main) {
const sensitivePatterns = [
/password/i,
/secret/i,
/token/i,
/api[_\s-]?key/i,
/credential/i
];
sensitivePatterns.forEach(pattern => {
if (pattern.test(agent.prompts.main)) {
warnings.push({
field: 'prompts.main',
message: `Prompt mentions sensitive data (${pattern.source}). Ensure proper handling.`,
severity: 'warning'
});
}
});
}
// Check for file system restrictions
if (agent.tools && agent.tools.includes('Write')) {
if (!agent.config || !agent.config.allowedPaths) {
warnings.push({
field: 'config.allowedPaths',
message: 'Agent has Write access but no path restrictions defined',
severity: 'warning'
});
}
}
return [...errors, ...warnings];
}
/**
* Custom validator for performance constraints
*/
function validatePerformanceConstraints(agent) {
const errors = [];
const warnings = [];
// Check for reasonable capability count
if (agent.capabilities && agent.capabilities.length > 10) {
warnings.push({
field: 'capabilities',
message: `Agent has ${agent.capabilities.length} capabilities. Consider splitting into multiple specialized agents.`,
severity: 'warning'
});
}
// Check for reasonable tool count
if (agent.tools && agent.tools.length > 15) {
warnings.push({
field: 'tools',
message: `Agent has ${agent.tools.length} tools. This may impact performance.`,
severity: 'warning'
});
}
// Check prompt length
if (agent.prompts) {
Object.entries(agent.prompts).forEach(([key, value]) => {
if (value && value.length > 5000) {
warnings.push({
field: `prompts.${key}`,
message: `Prompt is ${value.length} characters long. Consider making it more concise.`,
severity: 'warning'
});
}
});
}
// Check for timeout configuration
if (agent.tools && agent.tools.includes('Bash') &&
(!agent.config || !agent.config.timeout)) {
warnings.push({
field: 'config.timeout',
message: 'Agent uses Bash but has no timeout configured',
severity: 'warning'
});
}
return [...errors, ...warnings];
}
/**
* Custom validator for dependency management
*/
function validateDependencies(agent, allAgents = []) {
const errors = [];
const warnings = [];
if (agent.dependencies) {
// Check if dependencies exist
agent.dependencies.forEach(dep => {
if (!allAgents.find(a => a.name === dep)) {
errors.push({
field: 'dependencies',
message: `Dependency "${dep}" not found in agent system`,
severity: 'error'
});
}
});
// Check for circular dependencies
const visited = new Set();
const recursionStack = new Set();
function hasCycle(agentName, path = []) {
if (recursionStack.has(agentName)) {
errors.push({
field: 'dependencies',
message: `Circular dependency detected: ${[...path, agentName].join(' -> ')}`,
severity: 'error'
});
return true;
}
if (visited.has(agentName)) return false;
visited.add(agentName);
recursionStack.add(agentName);
const currentAgent = allAgents.find(a => a.name === agentName);
if (currentAgent && currentAgent.dependencies) {
for (const dep of currentAgent.dependencies) {
if (hasCycle(dep, [...path, agentName])) {
return true;
}
}
}
recursionStack.delete(agentName);
return false;
}
hasCycle(agent.name);
}
return [...errors, ...warnings];
}
/**
* Custom validator for team/organization standards
*/
function validateTeamStandards(agent) {
const errors = [];
const warnings = [];
// Require specific metadata
if (!agent.metadata) {
errors.push({
field: 'metadata',
message: 'Metadata is required by team standards',
severity: 'error'
});
} else {
const requiredMetadata = ['author', 'team', 'lastReviewed'];
requiredMetadata.forEach(field => {
if (!agent.metadata[field]) {
errors.push({
field: `metadata.${field}`,
message: `Metadata field "${field}" is required by team standards`,
severity: 'error'
});
}
});
}
// Check documentation requirements
if (!agent.documentation || !agent.documentation.usage) {
warnings.push({
field: 'documentation.usage',
message: 'Usage documentation is recommended by team standards',
severity: 'warning'
});
}
// Check for test coverage requirement
if (!agent.metadata || !agent.metadata.testCoverage) {
warnings.push({
field: 'metadata.testCoverage',
message: 'Test coverage information should be included',
severity: 'warning'
});
}
return [...errors, ...warnings];
}
/**
* Composite validator that runs all custom validators
*/
class CustomValidator {
constructor(options = {}) {
this.validators = [];
this.options = options;
}
addValidator(name, validatorFn, options = {}) {
this.validators.push({
name,
fn: validatorFn,
enabled: options.enabled !== false,
severity: options.severity || 'error'
});
}
async validate(agent, context = {}) {
const results = {
valid: true,
errors: [],
warnings: []
};
// First run standard validation
const standardValidation = await validator.validate(agent);
if (!standardValidation.valid) {
results.valid = false;
results.errors.push(...standardValidation.errors);
}
// Then run custom validators
for (const customValidator of this.validators) {
if (!customValidator.enabled) continue;
try {
const issues = await customValidator.fn(agent, context);
issues.forEach(issue => {
if (issue.severity === 'error') {
results.valid = false;
results.errors.push({
validator: customValidator.name,
...issue
});
} else {
results.warnings.push({
validator: customValidator.name,
...issue
});
}
});
} catch (error) {
results.errors.push({
validator: customValidator.name,
message: `Validator error: ${error.message}`,
severity: 'error'
});
results.valid = false;
}
}
return results;
}
}
/**
* Example usage
*/
async function main() {
console.log('Claude Flow Custom Validators Example\n');
// Create a custom validator instance
const customValidator = new CustomValidator();
// Add our custom validators
customValidator.addValidator('naming', validateNamingConvention);
customValidator.addValidator('security', validateSecurityConstraints);
customValidator.addValidator('performance', validatePerformanceConstraints);
customValidator.addValidator('team-standards', validateTeamStandards, {
enabled: true // Can be toggled based on environment
});
// Example agent with various issues
const testAgent = {
name: 'Test_Agent', // Bad naming (should be kebab-case)
version: '1.0.0',
description: 'Test agent for validation',
capabilities: [
'code-review',
'Test Analysis', // Bad naming
'deploy',
'monitor',
'analyze',
'report',
'optimize',
'debug',
'profile',
'benchmark',
'audit',
'scan' // Too many capabilities
],
tools: ['Read', 'Write', 'Bash', 'WebFetch'], // Dangerous combination
prompts: {
main: 'You are an agent that handles API keys and passwords securely.' // Mentions sensitive data
},
dependencies: ['non-existent-agent'],
// Missing metadata required by team standards
};
console.log('Validating test agent...\n');
const validation = await customValidator.validate(testAgent);
console.log(`Valid: ${validation.valid}`);
console.log(`\nErrors (${validation.errors.length}):`);
validation.errors.forEach(error => {
console.log(` - [${error.validator || 'standard'}] ${error.field}: ${error.message}`);
});
console.log(`\nWarnings (${validation.warnings.length}):`);
validation.warnings.forEach(warning => {
console.log(` - [${warning.validator || 'standard'}] ${warning.field}: ${warning.message}`);
});
// Example of a well-formed agent
console.log('\n\nExample of a well-formed agent:');
const goodAgent = {
name: 'code-reviewer',
version: '1.0.0',
description: 'Reviews code for quality and standards',
capabilities: ['code-review', 'suggest-improvements'],
tools: ['Read', 'Grep', 'Glob'],
prompts: {
main: 'You are a code reviewer. Analyze code for quality, readability, and best practices.'
},
config: {
timeout: 30000,
allowedPaths: ['./src', './test']
},
metadata: {
author: 'Team Alpha',
team: 'DevTools',
lastReviewed: new Date().toISOString(),
testCoverage: 95
},
documentation: {
usage: 'Use this agent to review pull requests and provide feedback'
}
};
const goodValidation = await customValidator.validate(goodAgent);
console.log(`\nValid: ${goodValidation.valid}`);
console.log(`Errors: ${goodValidation.errors.length}`);
console.log(`Warnings: ${goodValidation.warnings.length}`);
}
// Export for use as a library
export {
validateNamingConvention,
validateSecurityConstraints,
validatePerformanceConstraints,
validateDependencies,
validateTeamStandards,
CustomValidator
};
// Run example if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error);
}