tasq/node_modules/@claude-flow/codex/dist/migrations/index.js

856 lines
28 KiB
JavaScript

/**
* @claude-flow/codex - Migrations
*
* Migration utilities for converting Claude Code configurations to Codex format
* Supports full CLAUDE.md parsing with section extraction, skill conversion,
* and proper AGENTS.md/config.toml generation.
*/
/**
* Feature mappings between Claude Code and Codex
*/
export const FEATURE_MAPPINGS = [
{
claudeCode: 'CLAUDE.md',
codex: 'AGENTS.md',
status: 'mapped',
notes: 'Main instruction file - content is portable',
},
{
claudeCode: 'CLAUDE.local.md',
codex: '.codex/AGENTS.override.md',
status: 'mapped',
notes: 'Local overrides - gitignored in both',
},
{
claudeCode: 'settings.json',
codex: 'config.toml',
status: 'mapped',
notes: 'Format conversion required (JSON to TOML)',
},
{
claudeCode: '/skill-name',
codex: '$skill-name',
status: 'mapped',
notes: 'Skill invocation syntax - search and replace',
},
{
claudeCode: 'TodoWrite',
codex: 'Task tracking',
status: 'mapped',
notes: 'Similar functionality with different API',
},
{
claudeCode: 'Task tool agents',
codex: 'Sub-agent collaboration',
status: 'partial',
notes: 'Codex sub-agents via CODEX_HANDOFF_TARGET env var',
},
{
claudeCode: 'MCP servers',
codex: '[mcp_servers]',
status: 'mapped',
notes: 'Configuration format differs but same functionality',
},
{
claudeCode: 'hooks system',
codex: 'Automations',
status: 'partial',
notes: 'Codex automations are scheduled, not event-driven',
},
{
claudeCode: 'EnterPlanMode',
codex: 'No direct equivalent',
status: 'unsupported',
notes: 'Codex uses different planning paradigm',
},
{
claudeCode: 'Permission modes',
codex: 'approval_policy + sandbox_mode',
status: 'mapped',
notes: 'Codex provides more granular control',
},
];
/**
* Hook keywords recognized in CLAUDE.md
*/
const HOOK_KEYWORDS = [
'pre-task',
'post-task',
'pre-edit',
'post-edit',
'pre-command',
'post-command',
'session-start',
'session-end',
'session-restore',
'route',
'explain',
'pretrain',
'notify',
];
/**
* Patterns that need migration warnings
*/
const WARNING_PATTERNS = [
{ pattern: 'EnterPlanMode', message: 'EnterPlanMode has no direct Codex equivalent - review planning workflow' },
{ pattern: 'claude -p', message: 'claude -p headless mode - use Codex sub-agent patterns instead' },
{ pattern: 'TodoWrite', message: 'TodoWrite - Codex uses different task tracking approach' },
{ pattern: /--dangerously-skip-permissions/g, message: 'Dangerous permission skip detected - use Codex approval_policy instead' },
{ pattern: /mcp__claude-flow__/g, message: 'MCP tool calls need migration to Codex MCP configuration' },
{ pattern: /mcp__ruv-swarm__/g, message: 'Swarm MCP calls - ensure ruv-swarm MCP server is configured in config.toml' },
];
/**
* Parse a CLAUDE.md file completely
*/
export async function parseClaudeMd(content) {
const lines = content.split('\n');
const result = {
title: '',
sections: [],
skills: [],
hooks: [],
customInstructions: [],
codeBlocks: [],
mcpServers: [],
settings: {},
warnings: [],
};
// Extract title (first H1)
const titleMatch = content.match(/^#\s+(.+)$/m);
if (titleMatch && titleMatch[1]) {
result.title = titleMatch[1].trim();
result.settings.projectName = result.title;
}
// Parse sections
result.sections = parseSections(content, lines);
// Extract skills (both /skill-name and $skill-name syntax)
result.skills = extractSkills(content, lines);
// Extract hooks
result.hooks = extractHooks(content);
// Extract code blocks
result.codeBlocks = extractCodeBlocks(content, lines);
// Extract MCP server configurations from code blocks
result.mcpServers = extractMcpServers(result.codeBlocks);
// Extract settings from content
result.settings = {
...result.settings,
...extractSettings(content, result.sections),
};
// Extract custom instructions (behavioral rules)
result.customInstructions = extractBehavioralRules(content);
// Check for patterns that need warnings
for (const { pattern, message } of WARNING_PATTERNS) {
if (typeof pattern === 'string') {
if (content.includes(pattern)) {
result.warnings.push(message);
}
}
else {
if (pattern.test(content)) {
result.warnings.push(message);
}
}
}
return result;
}
/**
* Parse sections from markdown content
*/
function parseSections(content, lines) {
const sections = [];
const sectionRegex = /^(#{1,6})\s+(.+)$/;
let currentSection = null;
let contentLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const match = sectionRegex.exec(line);
if (match && match[1] && match[2]) {
// Save previous section
if (currentSection) {
currentSection.content = contentLines.join('\n').trim();
currentSection.endLine = i;
sections.push(currentSection);
}
// Start new section
currentSection = {
level: match[1].length,
title: match[2].trim(),
content: '',
startLine: i + 1,
endLine: i + 1,
};
contentLines = [];
}
else if (currentSection) {
contentLines.push(line);
}
}
// Save last section
if (currentSection) {
currentSection.content = contentLines.join('\n').trim();
currentSection.endLine = lines.length;
sections.push(currentSection);
}
return sections;
}
/**
* Extract skill references from content
*/
function extractSkills(content, lines) {
const skills = [];
const seenSkills = new Set();
// Slash syntax: /skill-name
const slashRegex = /\/([a-z][a-z0-9-]*)/g;
let match;
while ((match = slashRegex.exec(content)) !== null) {
const name = match[1];
// Skip common false positives
if (['src', 'dist', 'docs', 'tests', 'config', 'scripts', 'examples', 'node_modules', 'workspaces'].includes(name)) {
continue;
}
if (!seenSkills.has(`slash:${name}`)) {
seenSkills.add(`slash:${name}`);
const lineNum = findLineNumber(content, match.index);
skills.push({
name,
syntax: 'slash',
context: getContextAroundMatch(lines, lineNum),
line: lineNum,
});
}
}
// Dollar syntax: $skill-name
const dollarRegex = /\$([a-z][a-z0-9-]+)/g;
while ((match = dollarRegex.exec(content)) !== null) {
const name = match[1];
if (!seenSkills.has(`dollar:${name}`)) {
seenSkills.add(`dollar:${name}`);
const lineNum = findLineNumber(content, match.index);
skills.push({
name,
syntax: 'dollar',
context: getContextAroundMatch(lines, lineNum),
line: lineNum,
});
}
}
return skills;
}
/**
* Extract hooks referenced in content
*/
function extractHooks(content) {
const hooks = [];
const lowerContent = content.toLowerCase();
for (const hook of HOOK_KEYWORDS) {
if (lowerContent.includes(hook)) {
hooks.push(hook);
}
}
return hooks;
}
/**
* Extract code blocks from markdown
*/
function extractCodeBlocks(content, lines) {
const blocks = [];
const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
let match;
while ((match = codeBlockRegex.exec(content)) !== null) {
blocks.push({
language: match[1] || 'text',
content: match[2].trim(),
line: findLineNumber(content, match.index),
});
}
return blocks;
}
/**
* Extract MCP server configurations from code blocks
*/
function extractMcpServers(codeBlocks) {
const servers = [];
for (const block of codeBlocks) {
// Look for MCP server configurations in bash/shell blocks
if (['bash', 'shell', 'sh', 'zsh'].includes(block.language)) {
// Pattern: claude mcp add <name> <command> [args...]
const mcpAddRegex = /claude\s+mcp\s+add\s+(\S+)\s+(.+)/g;
let match;
while ((match = mcpAddRegex.exec(block.content)) !== null) {
const name = match[1];
const parts = match[2].trim().split(/\s+/);
servers.push({
name,
command: parts[0] || 'npx',
args: parts.slice(1),
enabled: true,
});
}
}
// Look for JSON MCP configurations
if (['json', 'jsonc'].includes(block.language)) {
try {
const parsed = JSON.parse(block.content);
if (parsed.mcpServers) {
for (const [name, config] of Object.entries(parsed.mcpServers)) {
const mcpConfig = config;
servers.push({
name,
command: mcpConfig.command || 'npx',
args: mcpConfig.args || [],
enabled: true,
});
}
}
}
catch {
// Not valid JSON, skip
}
}
// Look for JavaScript/TypeScript MCP tool calls
if (['javascript', 'typescript', 'js', 'ts'].includes(block.language)) {
// Pattern: mcp__<server>__<tool>
const mcpCallRegex = /mcp__([a-z-]+)__/g;
const seenServers = new Set();
let match;
while ((match = mcpCallRegex.exec(block.content)) !== null) {
const serverName = match[1].replace(/-/g, '_');
if (!seenServers.has(serverName)) {
seenServers.add(serverName);
// Don't add as full config, just note it exists
}
}
}
}
// Deduplicate
const seen = new Set();
return servers.filter((s) => {
if (seen.has(s.name))
return false;
seen.add(s.name);
return true;
});
}
/**
* Extract settings from content and sections
*/
function extractSettings(content, sections) {
const settings = {};
// Look for tech stack
const techMatch = content.match(/(?:Tech\s*Stack|Technology|Stack)[:\s]+([^\n]+)/i);
if (techMatch && techMatch[1]) {
settings.techStack = techMatch[1].replace(/\*\*/g, '').trim();
}
// Look for build command
const buildMatch = content.match(/(?:Build|Compile)[:\s]*\n?```(?:bash|sh)?\n([^\n]+)/i);
if (buildMatch && buildMatch[1]) {
settings.buildCommand = buildMatch[1].trim();
}
else {
// Check for npm run build pattern
if (content.includes('npm run build')) {
settings.buildCommand = 'npm run build';
}
}
// Look for test command
const testMatch = content.match(/(?:Test)[:\s]*\n?```(?:bash|sh)?\n([^\n]+)/i);
if (testMatch && testMatch[1]) {
settings.testCommand = testMatch[1].trim();
}
else {
if (content.includes('npm test')) {
settings.testCommand = 'npm test';
}
}
// Look for dev command
if (content.includes('npm run dev')) {
settings.devCommand = 'npm run dev';
}
// Look for approval/permission settings
if (content.includes('auto-approve') || content.includes('autoApprove')) {
settings.approvalPolicy = 'never';
}
else if (content.includes('read-only')) {
settings.sandboxMode = 'read-only';
}
// Look for model specification
const modelMatch = content.match(/model[:\s]+["']?([^"'\n]+)["']?/i);
if (modelMatch && modelMatch[1]) {
settings.model = modelMatch[1].trim();
}
return settings;
}
/**
* Extract behavioral rules from content
*/
function extractBehavioralRules(content) {
const rules = [];
// Look for Behavioral Rules section
const behavioralMatch = content.match(/##\s*Behavioral\s*Rules[^\n]*\n([\s\S]*?)(?=\n##|\n#\s|$)/i);
if (behavioralMatch && behavioralMatch[1]) {
const ruleLines = behavioralMatch[1].split('\n');
for (const line of ruleLines) {
const trimmed = line.trim();
if (trimmed.startsWith('- ')) {
rules.push(trimmed.substring(2));
}
else if (trimmed.startsWith('* ')) {
rules.push(trimmed.substring(2));
}
}
}
// Also look for Security Rules
const securityMatch = content.match(/##\s*Security\s*Rules?[^\n]*\n([\s\S]*?)(?=\n##|\n#\s|$)/i);
if (securityMatch && securityMatch[1]) {
const securityLines = securityMatch[1].split('\n');
for (const line of securityLines) {
const trimmed = line.trim();
if (trimmed.startsWith('- ')) {
rules.push(trimmed.substring(2));
}
else if (trimmed.startsWith('* ')) {
rules.push(trimmed.substring(2));
}
}
}
return rules;
}
/**
* Find line number for a character index
*/
function findLineNumber(content, index) {
return content.substring(0, index).split('\n').length;
}
/**
* Get context around a match
*/
function getContextAroundMatch(lines, lineNum) {
const start = Math.max(0, lineNum - 2);
const end = Math.min(lines.length, lineNum + 1);
return lines.slice(start, end).join('\n');
}
/**
* Analyze a CLAUDE.md file for migration (simplified interface)
*/
export async function analyzeClaudeMd(content) {
const parsed = await parseClaudeMd(content);
return {
sections: parsed.sections.map((s) => s.title),
skills: [...new Set(parsed.skills.map((s) => s.name))],
hooks: parsed.hooks,
customInstructions: parsed.customInstructions,
warnings: parsed.warnings,
};
}
/**
* Convert skill invocation syntax from slash to dollar
*/
export function convertSkillSyntax(content) {
// Convert /skill-name to $skill-name, but avoid path-like patterns
return content.replace(/(?<![a-zA-Z0-9_./])\/([a-z][a-z0-9-]*)(?![a-zA-Z0-9_./])/g, (match, skillName) => {
// Skip common directory names
const skipPatterns = ['src', 'dist', 'docs', 'tests', 'config', 'scripts', 'examples', 'node_modules', 'workspaces', 'data', 'logs', 'tmp', 'var', 'etc', 'usr', 'bin', 'lib'];
if (skipPatterns.includes(skillName)) {
return match;
}
return `$${skillName}`;
});
}
/**
* Generate AGENTS.md content from parsed CLAUDE.md
*/
export function generateAgentsMdFromParsed(parsed) {
const lines = [];
// Title
lines.push(`# ${parsed.title || 'Project Agent Guide'}`);
lines.push('');
lines.push('> Migrated from CLAUDE.md by @claude-flow/codex');
lines.push('');
// Project Overview
lines.push('## Project Overview');
lines.push('');
if (parsed.settings.techStack) {
lines.push(`**Tech Stack**: ${parsed.settings.techStack}`);
}
lines.push('');
// Quick Start
lines.push('## Setup');
lines.push('');
if (parsed.settings.buildCommand) {
lines.push('### Build');
lines.push('```bash');
lines.push(parsed.settings.buildCommand);
lines.push('```');
lines.push('');
}
if (parsed.settings.testCommand) {
lines.push('### Test');
lines.push('```bash');
lines.push(parsed.settings.testCommand);
lines.push('```');
lines.push('');
}
if (parsed.settings.devCommand) {
lines.push('### Development');
lines.push('```bash');
lines.push(parsed.settings.devCommand);
lines.push('```');
lines.push('');
}
// Code Standards
lines.push('## Code Standards');
lines.push('');
lines.push('- Files under 500 lines');
lines.push('- No hardcoded secrets');
lines.push('- Input validation at boundaries');
lines.push('- Typed interfaces for public APIs');
lines.push('');
// Skills
if (parsed.skills.length > 0) {
lines.push('## Skills');
lines.push('');
lines.push('| Skill | Original Syntax |');
lines.push('|-------|-----------------|');
for (const skill of parsed.skills) {
const codexSyntax = `$${skill.name}`;
const originalSyntax = skill.syntax === 'slash' ? `/${skill.name}` : `$${skill.name}`;
lines.push(`| \`${codexSyntax}\` | \`${originalSyntax}\` |`);
}
lines.push('');
}
// Security
lines.push('## Security');
lines.push('');
if (parsed.customInstructions.length > 0) {
for (const rule of parsed.customInstructions.slice(0, 10)) {
lines.push(`- ${rule}`);
}
}
else {
lines.push('- NEVER commit secrets or credentials');
lines.push('- Validate all user inputs');
lines.push('- Prevent directory traversal attacks');
}
lines.push('');
// Hooks (as reference)
if (parsed.hooks.length > 0) {
lines.push('## Automation Hooks');
lines.push('');
lines.push('The following hooks were detected in the original configuration:');
lines.push('');
for (const hook of parsed.hooks) {
lines.push(`- \`${hook}\``);
}
lines.push('');
lines.push('> Note: Codex uses scheduled Automations instead of event-driven hooks.');
lines.push('');
}
// MCP Servers
if (parsed.mcpServers.length > 0) {
lines.push('## MCP Servers');
lines.push('');
lines.push('Configure in config.toml:');
lines.push('');
for (const server of parsed.mcpServers) {
lines.push(`- **${server.name}**: \`${server.command} ${(server.args || []).join(' ')}\``);
}
lines.push('');
}
// Migration notes
if (parsed.warnings.length > 0) {
lines.push('## Migration Notes');
lines.push('');
for (const warning of parsed.warnings) {
lines.push(`- ${warning}`);
}
lines.push('');
}
return lines.join('\n');
}
/**
* Convert settings.json to config.toml format
*/
export function convertSettingsToToml(settings) {
const lines = [];
lines.push('# Migrated from settings.json');
lines.push('# Generated by @claude-flow/codex');
lines.push('');
// Model
if (settings.model) {
lines.push(`model = "${settings.model}"`);
}
else {
lines.push('model = "gpt-5.3-codex"');
}
lines.push('');
// Permissions mapping
if (settings.permissions) {
const perms = settings.permissions;
if (perms.autoApprove === true) {
lines.push('approval_policy = "never"');
lines.push('sandbox_mode = "danger-full-access"');
}
else if (perms.autoApprove === 'read-only') {
lines.push('approval_policy = "on-request"');
lines.push('sandbox_mode = "read-only"');
}
else {
lines.push('approval_policy = "on-request"');
lines.push('sandbox_mode = "workspace-write"');
}
}
else {
lines.push('approval_policy = "on-request"');
lines.push('sandbox_mode = "workspace-write"');
}
lines.push('');
// Web search
if (settings.webSearch !== undefined) {
const mode = settings.webSearch === true ? 'live' : settings.webSearch === false ? 'disabled' : 'cached';
lines.push(`web_search = "${mode}"`);
}
else {
lines.push('web_search = "cached"');
}
lines.push('');
// Features
lines.push('[features]');
lines.push('child_agents_md = true');
lines.push('shell_snapshot = true');
lines.push('request_rule = true');
lines.push('');
// MCP servers
if (settings.mcpServers && typeof settings.mcpServers === 'object') {
for (const [name, config] of Object.entries(settings.mcpServers)) {
const mcpConfig = config;
lines.push(`[mcp_servers.${name}]`);
if (mcpConfig.command) {
lines.push(`command = "${mcpConfig.command}"`);
}
if (mcpConfig.args && mcpConfig.args.length > 0) {
const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(', ');
lines.push(`args = [${argsStr}]`);
}
lines.push('enabled = true');
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
lines.push('');
lines.push(`[mcp_servers.${name}.env]`);
for (const [key, value] of Object.entries(mcpConfig.env)) {
lines.push(`${key} = "${value}"`);
}
}
lines.push('');
}
}
else {
// Add default claude-flow server
lines.push('[mcp_servers.claude-flow]');
lines.push('command = "npx"');
lines.push('args = ["-y", "@claude-flow/cli@latest"]');
lines.push('enabled = true');
lines.push('');
}
// History
lines.push('[history]');
lines.push('persistence = "save-all"');
lines.push('');
// Profiles
lines.push('# Development profile');
lines.push('[profiles.dev]');
lines.push('approval_policy = "never"');
lines.push('sandbox_mode = "danger-full-access"');
lines.push('');
lines.push('# Safe profile');
lines.push('[profiles.safe]');
lines.push('approval_policy = "untrusted"');
lines.push('sandbox_mode = "read-only"');
lines.push('');
return lines.join('\n');
}
/**
* Generate config.toml from parsed CLAUDE.md
*/
export function generateConfigTomlFromParsed(parsed) {
const lines = [];
lines.push('# Migrated from CLAUDE.md');
lines.push('# Generated by @claude-flow/codex');
lines.push('');
// Model
lines.push(`model = "${parsed.settings.model || 'gpt-5.3-codex'}"`);
lines.push('');
// Approval policy
const approvalPolicy = parsed.settings.approvalPolicy || 'on-request';
lines.push(`approval_policy = "${approvalPolicy}"`);
lines.push('');
// Sandbox mode
const sandboxMode = parsed.settings.sandboxMode || 'workspace-write';
lines.push(`sandbox_mode = "${sandboxMode}"`);
lines.push('');
// Web search
lines.push('web_search = "cached"');
lines.push('');
// Features
lines.push('[features]');
lines.push('child_agents_md = true');
lines.push('shell_snapshot = true');
lines.push('request_rule = true');
lines.push('');
// MCP servers
if (parsed.mcpServers.length > 0) {
for (const server of parsed.mcpServers) {
lines.push(`[mcp_servers.${server.name.replace(/-/g, '_')}]`);
lines.push(`command = "${server.command}"`);
if (server.args && server.args.length > 0) {
const argsStr = server.args.map((a) => `"${a}"`).join(', ');
lines.push(`args = [${argsStr}]`);
}
lines.push(`enabled = ${server.enabled ?? true}`);
lines.push('');
}
}
else {
// Default claude-flow server
lines.push('[mcp_servers.claude_flow]');
lines.push('command = "npx"');
lines.push('args = ["-y", "@claude-flow/cli@latest"]');
lines.push('enabled = true');
lines.push('');
}
// Skills
if (parsed.skills.length > 0) {
lines.push('# Skills detected in original configuration');
for (const skill of parsed.skills) {
lines.push(`# - ${skill.name} (${skill.syntax} syntax)`);
}
lines.push('');
}
// History
lines.push('[history]');
lines.push('persistence = "save-all"');
lines.push('');
// Profiles
lines.push('[profiles.dev]');
lines.push('approval_policy = "never"');
lines.push('sandbox_mode = "danger-full-access"');
lines.push('');
lines.push('[profiles.safe]');
lines.push('approval_policy = "untrusted"');
lines.push('sandbox_mode = "read-only"');
lines.push('');
return lines.join('\n');
}
/**
* Migrate from Claude Code (CLAUDE.md) to Codex (AGENTS.md)
*/
export async function migrateFromClaudeCode(options) {
const { sourcePath, targetPath, preserveComments = true, generateSkills = true } = options;
try {
// In actual implementation, this would read the file
// For now, we provide the structure for the migration
const result = {
success: true,
agentsMdPath: `${targetPath}/AGENTS.md`,
skillsCreated: generateSkills
? ['swarm-orchestration', 'memory-management', 'security-audit']
: [],
configTomlPath: `${targetPath}/.agents/config.toml`,
mappings: FEATURE_MAPPINGS,
warnings: [
'Review skill invocation syntax (changed from / to $)',
'Check hook configurations for Automation compatibility',
'Verify MCP server configurations in config.toml',
],
};
return result;
}
catch (error) {
return {
success: false,
warnings: [`Migration failed: ${error instanceof Error ? error.message : 'Unknown error'}`],
};
}
}
/**
* Perform full migration with content
*/
export async function performFullMigration(claudeMdContent, settingsJson) {
// Parse CLAUDE.md
const parsed = await parseClaudeMd(claudeMdContent);
// Generate AGENTS.md
let agentsMd = generateAgentsMdFromParsed(parsed);
// Convert skill syntax in the generated content
agentsMd = convertSkillSyntax(agentsMd);
// Generate config.toml
let configToml;
if (settingsJson) {
configToml = convertSettingsToToml(settingsJson);
}
else {
configToml = generateConfigTomlFromParsed(parsed);
}
// Collect skills to create
const skillsToCreate = [...new Set(parsed.skills.map((s) => s.name))];
return {
agentsMd,
configToml,
warnings: parsed.warnings,
skillsToCreate,
};
}
/**
* Generate migration report
*/
export function generateMigrationReport(result) {
const lines = [];
lines.push('# Migration Report');
lines.push('');
lines.push(`**Status**: ${result.success ? 'Success' : 'Failed'}`);
lines.push(`**Generated**: ${new Date().toISOString()}`);
lines.push('');
if (result.agentsMdPath) {
lines.push('## Generated Files');
lines.push('');
lines.push(`- AGENTS.md: \`${result.agentsMdPath}\``);
if (result.configTomlPath) {
lines.push(`- config.toml: \`${result.configTomlPath}\``);
}
lines.push('');
}
if (result.skillsCreated && result.skillsCreated.length > 0) {
lines.push('## Skills Created');
lines.push('');
for (const skill of result.skillsCreated) {
lines.push(`- \`$${skill}\``);
}
lines.push('');
}
if (result.mappings) {
lines.push('## Feature Mappings');
lines.push('');
lines.push('| Claude Code | Codex | Status | Notes |');
lines.push('|-------------|-------|--------|-------|');
for (const mapping of result.mappings) {
const notes = mapping.notes || '';
lines.push(`| \`${mapping.claudeCode}\` | \`${mapping.codex}\` | ${mapping.status} | ${notes} |`);
}
lines.push('');
}
if (result.warnings && result.warnings.length > 0) {
lines.push('## Warnings');
lines.push('');
for (const warning of result.warnings) {
lines.push(`- ${warning}`);
}
lines.push('');
}
lines.push('## Next Steps');
lines.push('');
lines.push('1. Review generated AGENTS.md for accuracy');
lines.push('2. Update skill references from `/skill-name` to `$skill-name`');
lines.push('3. Configure MCP servers in config.toml');
lines.push('4. Create skill definitions in `.agents/skills/`');
lines.push('5. Test with `codex` CLI');
lines.push('');
return lines.join('\n');
}
//# sourceMappingURL=index.js.map