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.
192 lines (171 loc) • 5.76 kB
text/typescript
/**
* Help Generator
*
* Dynamically generates CLI help text from the extension registry.
* Replaces hardcoded displayHelp() with registry-driven approach.
*
* @implements @.aiwg/architecture/decisions/ADR-001-unified-extension-system.md
* @tests @test/unit/cli/help-generator.test.ts
* @version 1.0.0
*/
import type { ExtensionRegistry } from '../extensions/registry.js';
import type { Extension } from '../extensions/types.js';
import type { CommandCategory } from './handlers/types.js';
/**
* Category display order and titles
*
* Determines the order in which command categories appear in help output.
*/
const CATEGORY_ORDER: { category: CommandCategory; title: string }[] = [
{ category: 'framework', title: 'Framework Management' },
{ category: 'project', title: 'Project Setup' },
{ category: 'workspace', title: 'Workspace Management' },
{ category: 'mcp', title: 'MCP Server' },
{ category: 'toolsmith', title: 'Toolsmith (Runtime Discovery)' },
{ category: 'catalog', title: 'Model Catalog' },
{ category: 'utility', title: 'Utilities' },
{ category: 'plugin', title: 'Plugin Packaging' },
{ category: 'scaffolding', title: 'Scaffolding' },
{ category: 'channel', title: 'Channel Management' },
{ category: 'maintenance', title: 'Maintenance' },
{ category: 'ralph', title: 'Ralph Loop (Iterative Execution)' },
];
/**
* Command name column width for alignment
*/
const COMMAND_COL_WIDTH = 24;
/**
* Generate help text from registry
*
* Dynamically builds CLI help text by querying the extension registry for
* command extensions, grouping them by category, and formatting them for display.
*
* @param registry - Extension registry to query
* @returns Formatted help text
*
* @example
* ```typescript
* import { getRegistry } from '../extensions/registry.js';
* import { generateHelp } from './help-generator.js';
*
* const registry = getRegistry();
* const helpText = generateHelp(registry);
* console.log(helpText);
* ```
*/
export function generateHelp(registry: ExtensionRegistry): string {
const lines: string[] = [];
// Header
lines.push('AIWG CLI');
lines.push('');
lines.push('Usage: aiwg <command> [options]');
lines.push('');
// Get commands by category
const commands = registry.getByType('command');
const byCategory = groupByCategory(commands);
// Generate sections in order
for (const { category, title } of CATEGORY_ORDER) {
const categoryCommands = byCategory.get(category);
if (!categoryCommands || categoryCommands.length === 0) continue;
lines.push(`${title}:`);
for (const cmd of categoryCommands) {
const name = formatCommandName(cmd);
const desc = cmd.description;
lines.push(` ${name.padEnd(COMMAND_COL_WIDTH)} ${desc}`);
}
lines.push('');
}
// Platform options
lines.push('Platform Options (--provider):');
lines.push(' claude Claude Code (default)');
lines.push(' copilot GitHub Copilot');
lines.push(' factory Factory AI');
lines.push(' codex / openai OpenAI Codex');
lines.push(' cursor Cursor IDE');
lines.push(' opencode OpenCode');
lines.push(' warp Warp Terminal');
lines.push(' windsurf Windsurf');
lines.push('');
// Examples
lines.push('Examples:');
lines.push(' aiwg use sdlc Install SDLC framework');
lines.push(' aiwg -new Create new project');
lines.push(' aiwg doctor Check installation health');
lines.push('');
return lines.join('\n');
}
/**
* Group commands by category
*
* Takes a flat list of command extensions and organizes them into a Map
* keyed by category, with values being arrays of extensions.
*
* @param commands - Array of command extensions
* @returns Map of category to command extensions
*
* @example
* ```typescript
* const commands = registry.getByType('command');
* const grouped = groupByCategory(commands);
*
* const frameworkCommands = grouped.get('framework');
* console.log(`${frameworkCommands?.length} framework commands`);
* ```
*/
function groupByCategory(commands: Extension[]): Map<string, Extension[]> {
const map = new Map<string, Extension[]>();
for (const cmd of commands) {
const category = cmd.category || 'utility';
let list = map.get(category);
if (!list) {
list = [];
map.set(category, list);
}
list.push(cmd);
}
// Sort commands within each category by ID
// Use Array.from to convert values() iterator to array
Array.from(map.values()).forEach((list) => {
list.sort((a, b) => a.id.localeCompare(b.id));
});
return map;
}
/**
* Format command name with primary alias
*
* Returns a formatted command name string that includes the primary alias
* (if present) for display in help text.
*
* Format:
* - No aliases: "command-name"
* - With aliases: "command-name, -alias"
*
* @param cmd - Command extension
* @returns Formatted command name string
*
* @example
* ```typescript
* // Command with no aliases
* const name1 = formatCommandName(useCmd);
* // Returns: "use"
*
* // Command with aliases
* const name2 = formatCommandName(newCmd);
* // Returns: "new, -new"
*
* // Command with multiple aliases
* const name3 = formatCommandName(helpCmd);
* // Returns: "help, -h"
* ```
*/
function formatCommandName(cmd: Extension): string {
// Get aliases from metadata
const metadata = cmd.metadata as any;
const aliases = metadata?.aliases || [];
// If no aliases, just return ID
if (aliases.length === 0) {
return cmd.id;
}
// Show command name with first alias
return `${cmd.id}, ${aliases[0]}`;
}