Complete collection of AI agent skills including: - Frontend Development (Vue, React, Next.js, Three.js) - Backend Development (NestJS, FastAPI, Node.js) - Mobile Development (React Native, Expo) - Testing (E2E, frontend, webapp) - DevOps (GitHub Actions, CI/CD) - Marketing (SEO, copywriting, analytics) - Security (binary analysis, vulnerability scanning) - And many more... Synchronized from: https://skills.sh/ Co-Authored-By: Claude <noreply@anthropic.com>
147 lines
4.2 KiB
TypeScript
Executable File
147 lines
4.2 KiB
TypeScript
Executable File
#!/usr/bin/env bun
|
|
/**
|
|
* Agent Pipeline Validator
|
|
*
|
|
* Validates pipeline manifest and agent definitions
|
|
* Usage: ./validate-pipeline.ts [pipeline-name]
|
|
*/
|
|
|
|
import { readFileSync, existsSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
interface PipelineManifest {
|
|
name: string;
|
|
stages: Array<{ name: string; description: string }>;
|
|
dataFormat?: string;
|
|
}
|
|
|
|
interface AgentDefinition {
|
|
name: string;
|
|
description: string;
|
|
model?: string;
|
|
}
|
|
|
|
function parseFrontmatter(content: string): { frontmatter: any; content: string } {
|
|
const match = content.match(/^---\n([\s\S]+?)\n---\n([\s\S]*)$/);
|
|
if (!match) {
|
|
return { frontmatter: {}, content };
|
|
}
|
|
|
|
const frontmatter: any = {};
|
|
const lines = match[1].split('\n');
|
|
for (const line of lines) {
|
|
const [key, ...valueParts] = line.split(':');
|
|
if (key && valueParts.length > 0) {
|
|
const value = valueParts.join(':').trim();
|
|
frontmatter[key.trim()] = value;
|
|
}
|
|
}
|
|
|
|
return { frontmatter, content: match[2] };
|
|
}
|
|
|
|
function validateAgentFile(agentPath: string): { valid: boolean; errors: string[] } {
|
|
const errors: string[] = [];
|
|
|
|
if (!existsSync(agentPath)) {
|
|
return { valid: false, errors: [`Agent file not found: ${agentPath}`] };
|
|
}
|
|
|
|
const content = readFileSync(agentPath, 'utf-8');
|
|
const { frontmatter } = parseFrontmatter(content);
|
|
|
|
// Check required fields
|
|
if (!frontmatter.name) {
|
|
errors.push(`Missing 'name' in frontmatter`);
|
|
}
|
|
|
|
if (!frontmatter.description) {
|
|
errors.push(`Missing 'description' in frontmatter`);
|
|
}
|
|
|
|
// Check for output markers
|
|
const markerPattern = /<<<(\w+)>>>/g;
|
|
const markers = content.match(markerPattern);
|
|
if (!markers || markers.length < 2) {
|
|
errors.push(`Missing output markers (expected <<<stage>>>...<<<end-stage>>>)`);
|
|
}
|
|
|
|
return { valid: errors.length === 0, errors };
|
|
}
|
|
|
|
function validatePipeline(pipelineName: string): void {
|
|
const basePath = join(process.cwd(), '.claude', 'agents', pipelineName);
|
|
const manifestPath = join(basePath, 'pipeline.md');
|
|
|
|
console.log(`\n🔍 Validating pipeline: ${pipelineName}\n`);
|
|
|
|
// Check if pipeline directory exists
|
|
if (!existsSync(basePath)) {
|
|
console.error(`❌ Pipeline directory not found: ${basePath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Load and validate manifest
|
|
let stages: string[] = [];
|
|
if (existsSync(manifestPath)) {
|
|
const manifestContent = readFileSync(manifestPath, 'utf-8');
|
|
const { frontmatter } = parseFrontmatter(manifestContent);
|
|
stages = frontmatter.stages?.map((s: any) => typeof s === 'string' ? s : s.name) || [];
|
|
}
|
|
|
|
// If no manifest, auto-detect agents
|
|
if (stages.length === 0) {
|
|
const { readdirSync } = require('fs');
|
|
const files = readdirSync(basePath).filter((f: string) => f.endsWith('.md') && f !== 'pipeline.md');
|
|
stages = files.map((f: string) => f.replace('.md', ''));
|
|
}
|
|
|
|
console.log(`📋 Stages: ${stages.join(' → ')}\n`);
|
|
|
|
// Validate each agent
|
|
let hasErrors = false;
|
|
for (const stage of stages) {
|
|
const agentPath = join(basePath, `${stage}.md`);
|
|
const { valid, errors } = validateAgentFile(agentPath);
|
|
|
|
if (valid) {
|
|
console.log(` ✅ ${stage}`);
|
|
} else {
|
|
console.log(` ❌ ${stage}`);
|
|
for (const error of errors) {
|
|
console.log(` ${error}`);
|
|
}
|
|
hasErrors = true;
|
|
}
|
|
}
|
|
|
|
// Check for scripts
|
|
const scriptsPath = join(process.cwd(), 'scripts', `run-${pipelineName}.ts`);
|
|
if (existsSync(scriptsPath)) {
|
|
console.log(`\n ✅ Pipeline script: ${scriptsPath}`);
|
|
} else {
|
|
console.log(`\n ⚠️ Missing pipeline script: ${scriptsPath}`);
|
|
console.log(` Create this script to orchestrate the agents.`);
|
|
}
|
|
|
|
console.log('');
|
|
|
|
if (hasErrors) {
|
|
console.log('❌ Pipeline validation failed\n');
|
|
process.exit(1);
|
|
} else {
|
|
console.log('✅ Pipeline validation passed!\n');
|
|
}
|
|
}
|
|
|
|
// Main
|
|
const pipelineName = process.argv[2];
|
|
|
|
if (!pipelineName) {
|
|
console.log('Usage: validate-pipeline.ts <pipeline-name>');
|
|
console.log('Example: validate-pipeline.ts ai-news-tweet');
|
|
process.exit(1);
|
|
}
|
|
|
|
validatePipeline(pipelineName);
|