From 73a8beba67761c9fad6cef5e72505af7a1000a74 Mon Sep 17 00:00:00 2001 From: Fahri Gedik Date: Fri, 10 Oct 2025 09:23:24 +0300 Subject: [PATCH] Refactor ai-config to use comma-separated tool string Updated the ai-config schematic to accept a comma-separated string for the 'tool' option instead of an array. Adjusted validation, parsing, and schema prompts accordingly for improved CLI usability and flexibility. --- .../src/commands/ai-config/index.ts | 51 ++++++++++--------- .../src/commands/ai-config/model.ts | 2 +- .../src/commands/ai-config/schema.json | 22 ++------ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts index 10691f6609..5b510af111 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/index.ts @@ -3,23 +3,40 @@ import { join, normalize } from '@angular-devkit/core'; import { AiConfigSchema, AiTool } from './model'; import { getWorkspace } from '../../utils'; -/** - * Generates AI configuration files for Angular projects based on selected tools. - * This schematic creates configuration files that help AI tools follow Angular best practices. - */ export default function (options: AiConfigSchema): Rule { return async (tree: Tree) => { - // Validate options - if (!options.tool || options.tool.length === 0) { + if (!options.tool || options.tool.trim() === '') { + console.log('ā„¹ļø No AI tools selected. Skipping configuration generation.'); + console.log(''); + console.log('šŸ’” Usage examples:'); + console.log(' ng g @abp/ng.schematics:ai-config --tool=claude,cursor'); + console.log(' ng g @abp/ng.schematics:ai-config --tool=gemini --target-project=my-app'); + console.log(''); + console.log('Available tools: claude, copilot, cursor, gemini, junie, windsurf'); + return tree; + } + + const tools = options.tool + .split(/[,\s]+/) + .map(t => t.trim()) + .filter(t => t) as AiTool[]; + + const validTools: AiTool[] = ['claude', 'copilot', 'cursor', 'gemini', 'junie', 'windsurf']; + const invalidTools = tools.filter(tool => !validTools.includes(tool)); + if (invalidTools.length > 0) { + throw new SchematicsException( + `Invalid AI tool(s): ${invalidTools.join(', ')}. Valid options are: ${validTools.join(', ')}` + ); + } + + if (tools.length === 0) { console.log('ā„¹ļø No AI tools selected. Skipping configuration generation.'); return tree; } - // Get workspace configuration const workspace = await getWorkspace(tree); let targetPath = '/'; - // If targetProject is specified, generate in project directory if (options.targetProject) { const project = workspace.projects.get(options.targetProject); if (!project) { @@ -32,20 +49,18 @@ export default function (options: AiConfigSchema): Rule { console.log('šŸš€ Generating AI configuration files...'); console.log(`šŸ“ Target path: ${targetPath}`); - console.log(`šŸ¤– Selected tools: ${options.tool.join(', ')}`); + console.log(`šŸ¤– Selected tools: ${tools.join(', ')}`); - // Generate rules for each selected tool - const rules: Rule[] = options.tool + const rules: Rule[] = tools .map(tool => generateConfigForTool(tool, targetPath, options.overwrite || false)); - // Apply all rules and log results return chain([ ...rules, (tree: Tree) => { console.log('āœ… AI configuration files generated successfully!'); console.log('\nšŸ“ Generated files:'); - options.tool.forEach(tool => { + tools.forEach(tool => { const configPath = getConfigPath(tool, targetPath); console.log(` - ${configPath}`); }); @@ -58,37 +73,27 @@ export default function (options: AiConfigSchema): Rule { }; } -/** - * Generates configuration for a specific AI tool - */ function generateConfigForTool(tool: AiTool, targetPath: string, overwrite: boolean): Rule { return (tree: Tree) => { const configPath = getConfigPath(tool, targetPath); - // Check if file already exists if (tree.exists(configPath) && !overwrite) { console.log(`āš ļø Configuration file already exists: ${configPath}`); console.log(` Use --overwrite flag to replace existing files.`); return tree; } - // Get template source const sourceDir = `./files/${tool}`; const source = apply(url(sourceDir), [ filter(path => { - // Filter out any unnecessary files return !path.endsWith('.DS_Store'); }) ]); - // Merge with existing tree return mergeWith(source, overwrite ? MergeStrategy.Overwrite : MergeStrategy.Default); }; } -/** - * Gets the configuration file path for a specific tool - */ function getConfigPath(tool: AiTool, basePath: string): string { const configFiles: Record = { claude: '.claude/CLAUDE.md', diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts index 8a10f85509..ab871971c2 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/model.ts @@ -1,7 +1,7 @@ export type AiTool = 'claude' | 'copilot' | 'cursor' | 'gemini' | 'junie' | 'windsurf'; export interface AiConfigSchema { - tool: AiTool[]; + tool?: string; targetProject?: string; overwrite?: boolean; } diff --git a/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json index 7ad18a1fe4..0d5e0117ed 100644 --- a/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/ai-config/schema.json @@ -5,25 +5,11 @@ "type": "object", "properties": { "tool": { - "description": "Specifies which AI tools to generate configuration files for", - "type": "array", - "items": { - "type": "string", - "enum": ["claude", "copilot", "cursor", "gemini", "junie", "windsurf"] - }, - "default": [], + "description": "Comma-separated list of AI tools (e.g., claude,cursor,gemini)", + "type": "string", "x-prompt": { - "message": "Which AI tools would you like to generate configuration files for?", - "type": "list", - "multiselect": true, - "items": [ - { "value": "claude", "label": "Claude (Anthropic) - AI assistant with deep code understanding" }, - { "value": "copilot", "label": "GitHub Copilot - AI pair programmer" }, - { "value": "cursor", "label": "Cursor - AI-first code editor" }, - { "value": "gemini", "label": "Google Gemini - AI assistant by Google" }, - { "value": "junie", "label": "Junie AI - Development assistant" }, - { "value": "windsurf", "label": "Windsurf - AI development environment" } - ] + "message": "Which AI tools would you like to generate configuration files for? (comma-separated)", + "type": "input" } }, "targetProject": {