diff --git a/README.md b/README.md index 89dd6ff..d6d116c 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,47 @@ ## Usage +### Interactive Mode + ```bash npx agent-rules ``` +This will start an interactive session where you can select the AI app and topics. + +### Command Line Interface + +You can also use command line flags to skip the interactive prompts: + +```bash +# Generate rules for a specific AI app and topic +npx agent-rules --app cursor --topics secure-code + +# Generate rules for multiple topics +npx agent-rules --app github-copilot --topics secure-code --topics testing + +# Use short flags +npx agent-rules -a claude-code -t security-vulnerabilities + +# Show help +npx agent-rules --help + +# Show version +npx agent-rules --version +``` + +#### Available Options + +**AI Apps:** +- `github-copilot` - GitHub Copilot +- `cursor` - Cursor +- `claude-code` - Claude Code + +**Topics:** +- `secure-code` - Secure coding practices +- `security-vulnerabilities` - Security vulnerability scanning and fixes +- `testing` - Testing strategy and guidelines + ## Rules Current category of rules available: diff --git a/__tests__/cli.test.ts b/__tests__/cli.test.ts index 17c471b..913e50a 100644 --- a/__tests__/cli.test.ts +++ b/__tests__/cli.test.ts @@ -120,4 +120,156 @@ describe('CLI Application', () => { assert.ok(typeof testingContent === 'string', 'template file should be readable as text') }) }) + + describe('CLI Command Line Arguments', () => { + test('should show help when --help flag is provided', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--help'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.strictEqual(result.status, 0, 'Should exit successfully') + assert.ok(result.stdout.includes('Usage: agent-rules'), 'Should show usage information') + assert.ok(result.stdout.includes('--app'), 'Should show app option') + assert.ok(result.stdout.includes('--topics'), 'Should show topics option') + assert.ok(result.stdout.includes('Available AI Apps:'), 'Should list available AI apps') + assert.ok(result.stdout.includes('Available Topics:'), 'Should list available topics') + }) + + test('should show version when --version flag is provided', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--version'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.strictEqual(result.status, 0, 'Should exit successfully') + assert.match(result.stdout.trim(), /^\d+\.\d+\.\d+$/, 'Should show version number in semver format') + }) + + test('should work with short flags -h and -v', () => { + const helpResult = spawnSync('node', ['dist/bin/cli.mjs', '-h'], { + encoding: 'utf8', + timeout: 5000 + }) + + const versionResult = spawnSync('node', ['dist/bin/cli.mjs', '-v'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.strictEqual(helpResult.status, 0, 'Should handle -h flag') + assert.ok(helpResult.stdout.includes('Usage:'), 'Should show help with -h') + + assert.strictEqual(versionResult.status, 0, 'Should handle -v flag') + assert.match(versionResult.stdout.trim(), /^\d+\.\d+\.\d+$/, 'Should show version with -v') + }) + + test('should accept valid app and topics combination', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'cursor', '--topics', 'testing'], { + encoding: 'utf8', + timeout: 10000 + }) + + assert.strictEqual(result.status, 0, 'Should succeed with valid arguments') + assert.ok(result.stdout.includes('✅ Agent rules generated successfully!'), 'Should show success message') + }) + + test('should accept multiple topics', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'github-copilot', '--topics', 'secure-code', '--topics', 'testing'], { + encoding: 'utf8', + timeout: 10000 + }) + + assert.strictEqual(result.status, 0, 'Should succeed with multiple topics') + assert.ok(result.stdout.includes('✅ Agent rules generated successfully!'), 'Should show success message') + }) + + test('should accept short flags for app and topics', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '-a', 'claude-code', '-t', 'security-vulnerabilities'], { + encoding: 'utf8', + timeout: 10000 + }) + + assert.strictEqual(result.status, 0, 'Should succeed with short flags') + assert.ok(result.stdout.includes('✅ Agent rules generated successfully!'), 'Should show success message') + }) + + test('should reject invalid app', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'invalid-app', '--topics', 'testing'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.notStrictEqual(result.status, 0, 'Should exit with error') + assert.ok(result.stderr.includes('Invalid app "invalid-app"'), 'Should show invalid app error') + assert.ok(result.stderr.includes('Available apps:'), 'Should list available apps') + }) + + test('should reject invalid topic', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'cursor', '--topics', 'invalid-topic'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.notStrictEqual(result.status, 0, 'Should exit with error') + assert.ok(result.stderr.includes('Invalid topics "invalid-topic"'), 'Should show invalid topic error') + assert.ok(result.stderr.includes('Available topics:'), 'Should list available topics') + }) + + test('should require both app and topics when using CLI flags', () => { + const appOnlyResult = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'cursor'], { + encoding: 'utf8', + timeout: 5000 + }) + + const topicsOnlyResult = spawnSync('node', ['dist/bin/cli.mjs', '--topics', 'testing'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.notStrictEqual(appOnlyResult.status, 0, 'Should fail when only app provided') + assert.ok(appOnlyResult.stderr.includes('both --app and --topics must be specified'), 'Should show error message') + + assert.notStrictEqual(topicsOnlyResult.status, 0, 'Should fail when only topics provided') + assert.ok(topicsOnlyResult.stderr.includes('both --app and --topics must be specified'), 'Should show error message') + }) + + test('should handle invalid command line arguments gracefully', () => { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--invalid-flag'], { + encoding: 'utf8', + timeout: 5000 + }) + + assert.notStrictEqual(result.status, 0, 'Should exit with error for invalid flags') + assert.ok(result.stderr.includes('Error parsing command line arguments'), 'Should show parsing error') + }) + + test('should validate all supported apps work', () => { + const supportedApps = ['github-copilot', 'cursor', 'claude-code'] + + for (const app of supportedApps) { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', app, '--topics', 'testing'], { + encoding: 'utf8', + timeout: 10000 + }) + + assert.strictEqual(result.status, 0, `Should work with app: ${app}`) + assert.ok(result.stdout.includes('✅ Agent rules generated successfully!'), `Should generate rules for ${app}`) + } + }) + + test('should validate all supported topics work', () => { + const supportedTopics = ['secure-code', 'security-vulnerabilities', 'testing'] + + for (const topic of supportedTopics) { + const result = spawnSync('node', ['dist/bin/cli.mjs', '--app', 'cursor', '--topics', topic], { + encoding: 'utf8', + timeout: 10000 + }) + + assert.strictEqual(result.status, 0, `Should work with topic: ${topic}`) + assert.ok(result.stdout.includes('✅ Agent rules generated successfully!'), `Should generate rules for ${topic}`) + } + }) + }) }) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 589ccb4..52b8e81 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -63,11 +63,14 @@ The project follows a layered architecture with an adapter pattern: - **Component**: The CLI entry point. - **Responsibilities**: - - Parses command-line arguments. - - Prompts the user for input using `@clack/prompts`. + - Parses command-line arguments using Node.js built-in `util.parseArgs`. + - Supports both interactive and non-interactive modes of operation. + - In interactive mode, prompts the user for input using `@clack/prompts`. + - In non-interactive mode, processes command-line flags (`--app`, `--topics`, `--help`, `--version`). + - Validates command-line arguments and provides helpful error messages. - Calls the core logic to generate the agentic rules. - Handles errors and displays appropriate messages to the user. -- **Interfaces**: Interacts with the `main.ts` module. +- **Interfaces**: Interacts with the `main.ts` module and the adapter registry for validation. ### 2.2. `main.ts` @@ -164,9 +167,63 @@ version: "1.0.0" - Implements duplicate detection to avoid redundant imports - Uses simple string-based content management for efficient processing -## 3. Data Models +## 3. CLI Architecture -### 3.1. `ScaffoldInstructions` +### 3.1. Dual-Mode Operation + +The CLI supports two distinct modes of operation: + +#### 3.1.1. Interactive Mode (Default) +- Activated when no command-line flags are provided +- Uses `@clack/prompts` for user-friendly interactive selection +- Guides users through AI app and topic selection with descriptions +- Provides immediate validation and feedback +- Handles user cancellation gracefully + +#### 3.1.2. Non-Interactive Mode +- Activated when command-line flags are provided +- Uses Node.js built-in `util.parseArgs` for argument parsing +- Supports the following flags: + - `--app` or `-a`: Specify the AI app (required in non-interactive mode) + - `--topics` or `-t`: Specify one or more topics (multiple values supported) + - `--help` or `-h`: Display help information and exit + - `--version` or `-v`: Display version information and exit +- Validates arguments against available options from the adapter registry and template system +- Provides clear error messages with available options listed + +### 3.2. Command Line Argument Processing + +#### 3.2.1. Argument Parsing +```typescript +interface CliArgs { + app?: string + topics?: string[] + help?: boolean + version?: boolean +} +``` + +#### 3.2.2. Validation Logic +- **App Validation**: Checks against `AdapterRegistry.getSupportedAiApps()` +- **Topic Validation**: Checks against available template directories +- **Completeness Validation**: Ensures both `--app` and `--topics` are provided when using non-interactive mode +- **Error Handling**: Provides specific error messages for different validation failures + +#### 3.2.3. Help and Version Information +- **Help**: Displays usage information, available options, and examples +- **Version**: Reads version from `package.json` using secure path resolution + +### 3.3. Mode Selection Logic + +The CLI determines the operation mode using the following logic: +1. Parse command-line arguments using `util.parseArgs` +2. If `--help` or `--version` flags are present, handle them and exit +3. If `--app` or `--topics` flags are present, validate and use non-interactive mode +4. Otherwise, fall back to interactive mode + +## 4. Data Models + +### 4.1. `ScaffoldInstructions` - **Description**: Represents the user's selections for generating agentic rules. - **Type Definition**: @@ -179,7 +236,7 @@ interface ScaffoldInstructions { } ``` -### 3.2. `AiAppConfig` +### 4.2. `AiAppConfig` - **Description**: Represents the configuration for a supported AI app. - **Type Definition**: @@ -191,7 +248,21 @@ interface AiAppConfig { } ``` -### 3.3. Adapter Pattern +### 4.3. `CliArgs` + +- **Description**: Represents the parsed command-line arguments. +- **Type Definition**: + +```typescript +interface CliArgs { + app?: string; + topics?: string[]; + help?: boolean; + version?: boolean; +} +``` + +### 4.4. Adapter Pattern - **Description**: The adapter pattern implementation allows for extensible AI app support. - **Key Classes**: @@ -210,18 +281,18 @@ abstract class BaseAdapter { } ``` -## 4. Supported AI Apps +## 5. Supported AI Apps The project currently supports three AI coding assistants, each with unique characteristics and processing requirements: -### 4.1. GitHub Copilot +### 5.1. GitHub Copilot - **Identifier**: `github-copilot` - **Directory**: `.github/instructions` - **File Extension**: `.instructions.md` - **Processing Strategy**: Direct file copying with secure path validation - **Use Case**: Simple instruction files for GitHub Copilot workspace integration -### 4.2. Cursor +### 5.2. Cursor - **Identifier**: `cursor` - **Directory**: `.cursor/rules` - **File Extension**: `.mdc` @@ -232,7 +303,7 @@ The project currently supports three AI coding assistants, each with unique char - Uses structured YAML processing for accuracy - **Use Case**: Rule files for Cursor AI coding assistant with metadata transformation -### 4.3. Claude Code +### 5.3. Claude Code - **Identifier**: `claude-code` - **Directory**: `.claude/rules` - **File Extension**: `.md` @@ -244,9 +315,9 @@ The project currently supports three AI coding assistants, each with unique char - Uses @ syntax for file imports (e.g., `@./.claude/rules/filename.md`) - **Use Case**: Rule files for Claude Code with automatic context file management -## 5. APIs +## 6. APIs -### 5.1. Core Application API +### 6.1. Core Application API #### `scaffoldAiAppInstructions(scaffoldInstructions: ScaffoldInstructions): Promise` @@ -261,7 +332,27 @@ The project currently supports three AI coding assistants, each with unique char 3. Resolves template and target directories 4. Delegates processing to the adapter -### 5.2. Adapter Registry API +### 6.2. CLI API + +#### Command Line Interface Functions + +##### `parseCommandLineArgs(): CliArgs` +- **Description**: Parses command-line arguments using Node.js `util.parseArgs` +- **Returns**: Parsed CLI arguments object +- **Throws**: Error for invalid arguments with helpful error messages + +##### `validateCliArgs(args: CliArgs): void` +- **Description**: Validates parsed CLI arguments against available options +- **Parameters**: `args` - Parsed CLI arguments +- **Throws**: Error for invalid app, topics, or missing required arguments + +##### `showHelp(): void` +- **Description**: Displays comprehensive help information including usage, options, and examples + +##### `showVersion(): Promise` +- **Description**: Displays version information read from package.json + +### 6.3. Adapter Registry API #### `AdapterRegistry.getAdapter(aiApp: string): BaseAdapter` @@ -276,7 +367,7 @@ The project currently supports three AI coding assistants, each with unique char - **Description**: Returns a list of all supported AI app identifiers. - **Returns**: Array of supported AI app strings -### 5.3. Adapter Interface +### 6.4. Adapter Interface #### `BaseAdapter.processInstructions(scaffoldInstructions, resolvedTemplateDirectory, resolvedTargetDirectory): Promise` @@ -287,16 +378,20 @@ The project currently supports three AI coding assistants, each with unique char - `resolvedTargetDirectory`: Path to the target destination - **Returns**: Promise that resolves when processing is complete -## 6. Error Handling +## 7. Error Handling + +## 7. Error Handling - **User Cancellation**: The CLI should handle user cancellation gracefully by exiting the process without an error. - **Invalid Input**: The CLI should validate user input and display an error message if the input is invalid. +- **Command Line Arguments**: The CLI should validate command-line arguments and provide helpful error messages with available options listed. - **File System Errors**: The application should handle file system errors, such as permission errors or missing files, by displaying an error message to the user. - **Template Not Found**: The application should throw an error if the template directory cannot be found. +- **Argument Parsing Errors**: The CLI should handle `util.parseArgs` errors gracefully and display help information. -## 7. Testing Strategy +## 8. Testing Strategy -### 7.1. Unit Tests +### 8.1. Unit Tests - **Adapter Tests**: Each adapter class should have comprehensive unit tests covering: - Configuration validation - Template processing logic @@ -306,28 +401,37 @@ The project currently supports three AI coding assistants, each with unique char - Correct adapter instantiation - Error handling for unsupported AI apps - Listing supported AI apps +- **CLI Argument Tests**: The CLI argument parsing should be tested for: + - Valid argument combinations + - Invalid argument handling + - Help and version flag functionality + - Error message accuracy -### 7.2. Integration Tests +### 8.2. Integration Tests - **Core Logic Integration**: Test the interaction between `main.ts` and the adapter layer - **Template Resolution**: Test template directory resolution and validation - **End-to-End Workflows**: Test complete scaffolding workflows for each supported AI app +- **CLI Integration**: Test both interactive and non-interactive CLI modes -### 7.3. End-to-End Tests +### 8.3. End-to-End Tests - **CLI Integration**: Test the entire application from command line invocation - **File System Operations**: Verify correct file creation and directory structure - **Error Scenarios**: Test error handling for various failure modes +- **Command Line Scenarios**: Test all CLI flag combinations and error cases -## 8. Implementation Considerations +## 9. Implementation Considerations -### 8.1. Extensibility +### 9.1. Extensibility - **Adapter Pattern**: The project uses the adapter pattern to make adding new AI apps straightforward +- **CLI Architecture**: The dual-mode CLI design allows for both automation and user-friendly interaction - **New AI App Support**: To add a new AI app: 1. Create a new adapter class extending `BaseAdapter` 2. Implement the `processInstructions` method with AI app-specific logic 3. Register the adapter in `AdapterRegistry` 4. Add corresponding tests + 5. Update CLI validation lists automatically (adapters are discovered via registry) -### 8.2. Maintainability +### 9.2. Maintainability - **Clear Separation of Concerns**: Each adapter handles only its specific AI app logic - **Frontmatter Processing Pipeline**: The frontmatter processing system is designed with clear separation: - AST parsing for reliable markdown structure handling @@ -343,7 +447,7 @@ The project currently supports three AI coding assistants, each with unique char - **Type Safety**: Full TypeScript typing ensures compile-time error detection - **Documentation**: Code is well-documented with clear responsibilities -### 8.3. Performance +### 9.3. Performance - **Lazy Loading**: Adapters are instantiated only when needed - **Efficient File Operations**: Minimized file system calls - **AST Caching**: Markdown AST parsing is performed only when transformations are needed @@ -352,8 +456,9 @@ The project currently supports three AI coding assistants, each with unique char - **Duplicate Detection**: Smart import checking prevents unnecessary file operations - **Memory Management**: Large files are processed streaming-style to minimize memory usage - **Error Recovery**: Graceful handling of file operation failures +- **CLI Argument Parsing**: Uses efficient built-in Node.js `util.parseArgs` for fast argument processing -### 8.4. Development Guidelines +### 9.4. Development Guidelines #### Adding a New Adapter 1. **Create Adapter Class**: Extend `BaseAdapter` in `src/adapters/` diff --git a/docs/PROJECT.md b/docs/PROJECT.md index ae4f20e..56d1fc4 100644 --- a/docs/PROJECT.md +++ b/docs/PROJECT.md @@ -3,7 +3,12 @@ This project, `agent-rules`, is a command-line interface (CLI) tool for generating agentic rules and instructions for various AI coding assistants. It allows users to scaffold security-focused instructions for AI apps like GitHub Copilot, tailored to specific programming languages and topics such as secure coding, vulnerability scanning, and testing. -The tool is built with TypeScript and packaged as a Node.js module. It provides an interactive CLI to prompt users for their desired AI app, programming language, and topic, then generates the corresponding instruction files in the appropriate directory. +The tool is built with TypeScript and packaged as a Node.js module. It provides both interactive and non-interactive modes of operation: + +- **Interactive Mode**: Presents user-friendly prompts to guide users through AI app, programming language, and topic selection +- **Non-Interactive Mode**: Accepts command-line flags (`--app`, `--topics`) for automation and scripting scenarios + +The tool then generates the corresponding instruction files in the appropriate directory structure based on the selected AI app's requirements. ## Development Commands @@ -31,7 +36,7 @@ The project is structured as a monorepo with the following key components: - **`src/`**: Contains the core application logic. - **`main.ts`**: The main orchestration module that handles template resolution, directory creation, and delegates AI app-specific processing to adapters. - - **`bin/cli.ts`**: The CLI entry point. It uses `@clack/prompts` to interact with the user and then calls the `scaffoldAiAppInstructions` function from `main.ts`. + - **`bin/cli.ts`**: The CLI entry point with dual-mode support. Uses Node.js built-in `util.parseArgs` for command-line argument processing and `@clack/prompts` for interactive mode. Supports both automated scripting and user-friendly interaction. - **`adapters/`**: Contains the adapter pattern implementation for AI app-specific logic. - **`base-adapter.ts`**: Abstract base class defining the adapter interface. - **`github-copilot-adapter.ts`**: GitHub Copilot-specific implementation with direct file copying. @@ -51,6 +56,8 @@ The project is structured as a monorepo with the following key components: The project follows a modular architecture with an **adapter pattern** for AI app extensibility, ensuring clear separation between the CLI, core orchestration logic, and AI app-specific processing. This makes the code easier to maintain, test, and extend with new AI apps. Key features include: +- **Dual-Mode CLI**: Supports both interactive prompts for ease-of-use and command-line flags for automation +- **Command-Line Arguments**: Full support for `--app`, `--topics`, `--help`, and `--version` flags with robust validation - **Advanced Template Processing**: Support for markdown files with YAML frontmatter processing and transformation - **AST-based Parsing**: Robust markdown processing using micromark and mdast utilities for reliable content manipulation - **Structured YAML Handling**: Sophisticated frontmatter field transformation while preserving non-modified content diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index dbf33e8..d1de701 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -27,11 +27,30 @@ The project has the following key dependencies: **Acceptance Criteria:** - The CLI must be executable from the command line. -- The CLI must present an interactive prompt to the user. -- The CLI must guide the user through the process of selecting an AI app, a programming language, and a topic. -- The CLI must handle user cancellation gracefully. +- The CLI must support both interactive and non-interactive modes of operation. +- In interactive mode, the CLI must present interactive prompts to guide the user through selections. +- In non-interactive mode, the CLI must accept command-line flags to skip interactive prompts. +- The CLI must handle user cancellation gracefully in interactive mode. - The CLI must display a confirmation message upon successful completion. - The CLI must display an error message if the operation fails. +- The CLI must provide help information when requested via `--help` or `-h` flags. +- The CLI must display version information when requested via `--version` or `-v` flags. + +### Command Line Arguments + +**Capability:** The CLI must support command-line arguments for non-interactive operation. + +**Acceptance Criteria:** + +- The CLI must accept `--app` (or `-a`) flag to specify the AI app. +- The CLI must accept `--topics` (or `-t`) flag to specify one or more topics (multiple values supported). +- The CLI must validate that both `--app` and `--topics` are provided when using non-interactive mode. +- The CLI must validate that the specified app is supported by the adapter registry. +- The CLI must validate that all specified topics are available in the template system. +- The CLI must provide clear error messages for invalid arguments with available options listed. +- The CLI must fall back to interactive mode when no command-line arguments are provided. +- The CLI must use Node.js built-in `util.parseArgs` for robust argument parsing. +- The CLI must handle argument parsing errors gracefully with helpful error messages. ### AI App Selection diff --git a/src/bin/cli.ts b/src/bin/cli.ts index 0232d69..9a806c4 100644 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -1,12 +1,127 @@ #!/usr/bin/env node import { intro, outro, select, multiselect } from '@clack/prompts' -import { styleText, debuglog } from 'node:util' +import { styleText, debuglog, parseArgs } from 'node:util' +import { readFile } from 'node:fs/promises' +import { resolve, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' import { scaffoldAiAppInstructions } from '../main.js' +import { AdapterRegistry } from '../adapters/index.js' const debug = debuglog('agent-rules') -async function init () { +// Available options for validation +const AVAILABLE_TOPICS = ['secure-code', 'security-vulnerabilities', 'testing'] +const AVAILABLE_APPS = AdapterRegistry.getSupportedAiApps() + +interface CliArgs { + app?: string + topics?: string[] + help?: boolean + version?: boolean +} + +function parseCommandLineArgs (): CliArgs { + try { + const { values } = parseArgs({ + args: process.argv.slice(2), + options: { + app: { + type: 'string', + short: 'a' + }, + topics: { + type: 'string', + multiple: true, + short: 't' + }, + help: { + type: 'boolean', + short: 'h' + }, + version: { + type: 'boolean', + short: 'v' + } + }, + allowPositionals: false + }) + + return { + app: values.app, + topics: values.topics, + help: values.help, + version: values.version + } + } catch (error: any) { + console.error('Error parsing command line arguments:', error.message) + showHelp() + // eslint-disable-next-line n/no-process-exit + process.exit(1) + } +} + +function showHelp (): void { + console.log(` +Usage: agent-rules [options] + +Options: + -a, --app AI app to generate rules for (${AVAILABLE_APPS.join(', ')}) + -t, --topics Topics to generate rules for (${AVAILABLE_TOPICS.join(', ')}) + Can be specified multiple times: --topics secure-code --topics testing + -h, --help Show this help message + -v, --version Show version number + +Examples: + agent-rules # Interactive mode + agent-rules --app cursor --topics secure-code # Generate secure coding rules for Cursor + agent-rules -a github-copilot -t testing -t secure-code # Multiple topics + +Available AI Apps: ${AVAILABLE_APPS.join(', ')} +Available Topics: ${AVAILABLE_TOPICS.join(', ')} +`) +} + +async function showVersion (): Promise { + try { + const __filename = fileURLToPath(import.meta.url) + const __dirname = dirname(__filename) + const packageJsonPath = resolve(__dirname, '../../package.json') + const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8')) + console.log(packageJson.version) + } catch (error) { + console.error('Error reading version:', error) + // eslint-disable-next-line n/no-process-exit + process.exit(1) + } +} + +function validateCliArgs (args: CliArgs): void { + if (args.app && !AVAILABLE_APPS.includes(args.app)) { + console.error(`Error: Invalid app "${args.app}". Available apps: ${AVAILABLE_APPS.join(', ')}`) + // eslint-disable-next-line n/no-process-exit + process.exit(1) + } + + if (args.topics) { + const invalidTopics = args.topics.filter(topic => !AVAILABLE_TOPICS.includes(topic)) + if (invalidTopics.length > 0) { + console.error(`Error: Invalid topics "${invalidTopics.join(', ')}". Available topics: ${AVAILABLE_TOPICS.join(', ')}`) + // eslint-disable-next-line n/no-process-exit + process.exit(1) + } + } + + // If one CLI arg is provided, both should be provided for non-interactive mode + if ((args.app && !args.topics) || (!args.app && args.topics)) { + console.error('Error: When using command line flags, both --app and --topics must be specified') + showHelp() + // eslint-disable-next-line n/no-process-exit + process.exit(1) + } +} + +async function initInteractive () { intro(styleText(['bgMagentaBright', 'black'], ' Agent, rules!')) // Hard-coding the code language for now @@ -49,9 +164,9 @@ async function init () { for (const codeTopic of topicChoices) { const templateChoices = { - aiApp, + aiApp: aiApp as string, codeLanguage, - codeTopic + codeTopic: codeTopic as string } await scaffoldAiAppInstructions(templateChoices) @@ -60,6 +175,50 @@ async function init () { outro('Aye Captain, godspeed with yar vibe coding 🫡') } +async function initWithCliArgs (args: CliArgs) { + // Hard-coding the code language for now + const codeLanguage = 'nodejs' + + debug('CLI mode - Selected AI App:', args.app) + debug('CLI mode - Selected code topics:', args.topics?.join(', ')) + + for (const codeTopic of args.topics!) { + const templateChoices = { + aiApp: args.app!, + codeLanguage, + codeTopic + } + + await scaffoldAiAppInstructions(templateChoices) + } + + console.log('✅ Agent rules generated successfully!') +} + +async function init () { + const args = parseCommandLineArgs() + + // Handle help and version flags + if (args.help) { + showHelp() + return + } + + if (args.version) { + await showVersion() + return + } + + // Validate CLI arguments if provided + if (args.app || args.topics) { + validateCliArgs(args) + await initWithCliArgs(args) + } else { + // Fall back to interactive mode + await initInteractive() + } +} + async function main () { try { await init()