aiwg
Version:
Cognitive architecture for AI-augmented software development with structured memory, ensemble validation, and closed-loop correction. FAIR-aligned artifacts, 84% cost reduction via human-in-the-loop, standards adopted by 100+ organizations.
354 lines (302 loc) • 8.85 kB
text/typescript
/**
* SkillSmith Skill Generator
*
* Generates skills with platform-aware deployment.
*
* @module smiths/skillsmith/generator
*/
import { promises as fs } from 'fs';
import * as path from 'path';
import type {
SkillOptions,
GeneratedSkill,
SkillFrontmatter,
SkillReference,
SkillDeploymentResult,
InteractivePrompts,
} from './types.js';
import { PlatformSkillResolver } from './platform-resolver.js';
/**
* Generate a skill from options
*/
export async function generateSkill(
options: SkillOptions
): Promise<GeneratedSkill> {
// Validate skill name
const nameValidation = PlatformSkillResolver.validateSkillName(options.name);
if (!nameValidation.valid) {
throw new Error(`Invalid skill name: ${nameValidation.error}`);
}
// Check if platform supports skills natively
const supportsSkills = PlatformSkillResolver.supportsSkills(options.platform);
if (!supportsSkills) {
const alternative = PlatformSkillResolver.getAlternativeStrategy(
options.platform
);
console.warn(
`Platform '${options.platform}' does not natively support skills.`
);
if (alternative) {
console.warn(`Consider deploying as ${alternative} instead.`);
}
}
// Generate skill content
const content = generateSkillContent(options);
// Generate references if requested
const references = options.createReferences
? generateDefaultReferences(options)
: undefined;
// Resolve deployment path
const deployPath = PlatformSkillResolver.getSkillPath(
options.platform,
options.projectPath,
options.name
);
return {
name: options.name,
path: deployPath,
content,
platform: options.platform,
references,
version: options.version || '1.0.0',
};
}
/**
* Deploy a generated skill to the target platform
*/
export async function deploySkill(
skill: GeneratedSkill,
projectPath: string,
dryRun: boolean = false
): Promise<SkillDeploymentResult> {
const filesCreated: string[] = [];
try {
if (dryRun) {
console.log(`[DRY RUN] Would deploy skill to: ${skill.path}`);
return {
skill,
success: true,
deployPath: skill.path,
filesCreated: [],
};
}
// Create skill directory
await fs.mkdir(skill.path, { recursive: true });
// Write SKILL.md
const skillFilePath = PlatformSkillResolver.getSkillFilePath(
skill.platform,
projectPath,
skill.name
);
await fs.writeFile(skillFilePath, skill.content, 'utf-8');
filesCreated.push(skillFilePath);
// Create references directory and files
if (skill.references && skill.references.length > 0) {
const referencesPath = PlatformSkillResolver.getReferencesPath(
skill.platform,
projectPath,
skill.name
);
await fs.mkdir(referencesPath, { recursive: true });
for (const ref of skill.references) {
const refPath = path.join(referencesPath, ref.filename);
await fs.writeFile(refPath, ref.content, 'utf-8');
filesCreated.push(refPath);
}
}
return {
skill,
success: true,
deployPath: skill.path,
filesCreated,
};
} catch (error) {
return {
skill,
success: false,
deployPath: skill.path,
error: error instanceof Error ? error.message : String(error),
filesCreated,
};
}
}
/**
* Generate SKILL.md content
*/
function generateSkillContent(options: SkillOptions): string {
const frontmatter = generateFrontmatter(options);
const body = generateSkillBody(options);
return `${frontmatter}\n${body}`;
}
/**
* Generate frontmatter YAML
*/
function generateFrontmatter(options: SkillOptions): string {
const fm: SkillFrontmatter = {
name: options.name,
description: options.description,
version: options.version || '1.0.0',
};
if (options.tools && options.tools.length > 0) {
fm.tools = options.tools.join(', ');
}
const lines = ['---'];
lines.push(`name: ${fm.name}`);
lines.push(`description: ${fm.description}`);
lines.push(`version: ${fm.version}`);
if (fm.tools) {
lines.push(`tools: ${fm.tools}`);
}
lines.push('---');
return lines.join('\n');
}
/**
* Generate skill body content
*/
function generateSkillBody(options: SkillOptions): string {
const sections: string[] = [];
// Title
sections.push(`# ${toTitleCase(options.name)} Skill`);
sections.push('');
// Purpose
sections.push('## Purpose');
sections.push('');
sections.push(options.description);
sections.push('');
// Trigger Phrases
sections.push('## Trigger Phrases');
sections.push('');
sections.push('Activate this skill when the user says:');
if (options.triggerPhrases && options.triggerPhrases.length > 0) {
options.triggerPhrases.forEach((phrase) => {
sections.push(`- "${phrase}"`);
});
} else {
sections.push(`- "${options.name}"`);
sections.push('- "use ' + options.name + '"');
}
sections.push('');
// Input
sections.push('## Input');
sections.push('');
sections.push('This skill expects:');
sections.push('- User request or content to process');
if (options.guidance) {
sections.push(`- ${options.guidance}`);
}
sections.push('');
// Execution Process
sections.push('## Execution Process');
sections.push('');
sections.push('1. Validate input requirements');
sections.push('2. Process the request');
sections.push('3. Generate output');
sections.push('4. Return results to user');
sections.push('');
// Output
sections.push('## Output');
sections.push('');
sections.push('This skill produces:');
sections.push('- Processed result based on input');
sections.push('- Status information');
sections.push('');
// Examples
sections.push('## Examples');
sections.push('');
sections.push('### Example 1: Basic Usage');
sections.push(`**User**: "${options.triggerPhrases?.[0] || options.name}"`);
sections.push(`**Result**: Skill executes and returns result`);
sections.push('');
// Tools (if specified)
if (options.tools && options.tools.length > 0) {
sections.push('## Tools Used');
sections.push('');
options.tools.forEach((tool) => {
sections.push(`- ${tool}`);
});
sections.push('');
}
// References (if creating reference directory)
if (options.createReferences) {
sections.push('## References');
sections.push('');
sections.push('See `references/` directory for:');
sections.push('- Usage examples');
sections.push('- Configuration options');
sections.push('- Troubleshooting guide');
sections.push('');
}
return sections.join('\n');
}
/**
* Generate default reference files
*/
function generateDefaultReferences(options: SkillOptions): SkillReference[] {
const references: SkillReference[] = [];
// Usage examples
references.push({
filename: 'usage-examples.md',
description: 'Usage examples and patterns',
content: generateUsageExamplesContent(options),
});
// Configuration
references.push({
filename: 'configuration.md',
description: 'Configuration options',
content: generateConfigurationContent(options),
});
return references;
}
/**
* Generate usage examples content
*/
function generateUsageExamplesContent(options: SkillOptions): string {
const triggers = options.triggerPhrases || [options.name];
return `# Usage Examples: ${toTitleCase(options.name)}
## Basic Usage
\`\`\`
User: "${triggers[0]}"
\`\`\`
## Alternative Triggers
${triggers.slice(1).map(t => `- "${t}"`).join('\n') || '_No alternative triggers defined_'}
## Common Patterns
This skill can be combined with other workflows for enhanced results.
See the main README for integration examples.
`;
}
/**
* Generate configuration content
*/
function generateConfigurationContent(options: SkillOptions): string {
return `# Configuration: ${toTitleCase(options.name)}
## Platform Support
**${options.platform}**: ${
PlatformSkillResolver.supportsSkills(options.platform)
? 'Native skill support'
: 'Skills mapped to commands'
}
## Options
This skill uses default configuration. Custom options can be added to the skill manifest.
## Environment Variables
No environment variables required for basic operation.
`;
}
/**
* Convert kebab-case to Title Case
*/
function toTitleCase(kebab: string): string {
return kebab
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
/**
* Interactive skill design workflow
*/
export async function interactiveSkillDesign(): Promise<InteractivePrompts> {
// This would be implemented with user prompts in a real CLI
// For now, return a stub
throw new Error(
'Interactive mode not yet implemented. Use --guidance to provide design input.'
);
}