diff --git a/.cursor/rules/api-client.mdc b/.cursor/rules/api-client.mdc deleted file mode 100644 index b3a75bb61..000000000 --- a/.cursor/rules/api-client.mdc +++ /dev/null @@ -1,74 +0,0 @@ ---- -description: API client patterns and HTTP client setup guidelines -globs: src/api/**/*.ts ---- - -# API Client Patterns - -## Core Components - -- **Main Client**: [src/api/apiClient.ts](mdc:src/api/apiClient.ts) - Axios-based HTTP client -- **Zod Client**: [src/api/zodClient.ts](mdc:src/api/zodClient.ts) - Type-safe API client -- **Schemas**: [src/api/schemas.ts](mdc:src/api/schemas.ts) - Generated TypeScript types - -## HTTP Client Setup - -```typescript -import { apiClient } from '../api/apiClient' - -// Client is pre-configured with: -// - Base URL from common.ts -// - Authentication headers -// - Error interceptors -// - Request/response logging -``` - -## API Function Patterns - -Each API module follows this pattern: - -```typescript -// Individual API functions -export const getFeature = async ( - featureKey: string, - projectKey: string, - authToken: string, -): Promise => { - // Implementation -} - -// Bulk operations -export const fetchFeatures = async ( - projectKey: string, - authToken: string, -): Promise => { - // Implementation -} -``` - -## Error Handling - -- Use axios interceptors for global error handling -- Return structured error responses -- Handle 401 unauthorized gracefully -- Provide user-friendly error messages - -## Authentication - -- Pass `authToken` as parameter to API functions -- Use `Authorization: Bearer ${token}` header -- Handle token expiration and refresh - -## Type Safety - -- Use generated schemas from OpenAPI spec -- Validate responses with Zod schemas -- Export TypeScript types for consumers - -## Common Patterns - -- All API functions are async -- Return typed responses based on schemas -- Include proper error handling -- Support pagination where applicable -- Use consistent parameter naming (projectKey, featureKey, etc.) diff --git a/.cursor/rules/cli-commands.mdc b/.cursor/rules/cli-commands.mdc deleted file mode 100644 index cfed350a1..000000000 --- a/.cursor/rules/cli-commands.mdc +++ /dev/null @@ -1,78 +0,0 @@ ---- -description: CLI command patterns and base command structure guidelines -globs: src/commands/**/*.ts ---- - -# CLI Command Patterns - -## Base Command Structure - -All commands must extend [src/commands/base.ts](mdc:src/commands/base.ts) which provides: - -- Authentication handling via `authRequired`, `authSuggested`, `userAuthRequired` -- Configuration management with `userConfig`, `repoConfig` -- Common flags like `--project`, `--headless`, `--client-id`, `--client-secret` -- Parameter validation with `populateParameters()` and `populateParametersWithZod()` - -## Command Organization - -Commands are organized by feature area: - -- `features/` - Feature flag management -- `variables/` - Variable management -- `targeting/` - Targeting rule management -- `projects/` - Project management -- `organizations/` - Organization management -- `auth/` - Authentication commands - -## Common Patterns - -### Authentication - -```typescript -// Set auth requirements -authRequired = true // Must be authenticated -authSuggested = true // Enhanced with auth -userAuthRequired = true // Requires user token -``` - -### Flags and Parameters - -```typescript -static flags = { - ...Base.flags, - name: Flags.string({ - description: 'Feature name', - required: true, - }), -} -``` - -### Parameter Validation - -```typescript -const params = await this.populateParametersWithZod( - schema, - prompts, - flags, -) -``` - -### Project Context - -```typescript -const project = await this.requireProject(flags.project, flags.headless) -``` - -## Output Formatting - -- Use `this.writer` for user-facing output -- Use `this.tableOutput` for tabular data -- Support `--headless` flag for machine-readable JSON output -- Handle interactive prompts with fallbacks for headless mode - -## Error Handling - -- Throw descriptive Error objects -- Use Zod validation for input validation -- Handle API errors gracefully with user-friendly messages diff --git a/.cursor/rules/deferToPrettier.mdc b/.cursor/rules/deferToPrettier.mdc deleted file mode 100644 index 7a1a20811..000000000 --- a/.cursor/rules/deferToPrettier.mdc +++ /dev/null @@ -1,10 +0,0 @@ ---- -description: Formatting guidelines - defer all formatting to Prettier -alwaysApply: true ---- - -# Formatting - -- All formatting is deferred to Prettier for supported file types (JavaScript, TypeScript, JSON, Markdown, etc.). -- No additional formatting rules are enforced by Cursor. -- No additional formatting rules are enforced by Cursor. diff --git a/.cursor/rules/gitCommitConventions.mdc b/.cursor/rules/gitCommitConventions.mdc deleted file mode 100644 index 08f920933..000000000 --- a/.cursor/rules/gitCommitConventions.mdc +++ /dev/null @@ -1,33 +0,0 @@ ---- -description: Git commit message conventions and Aviator CLI workflow guidelines -alwaysApply: true ---- - -# Git Commit Message Conventions - -- All git commit messages in this project must follow the Conventional Commits specification. -- A commit message should be structured as `(): `, where: - - `type` is one of: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, or `revert`. - - `scope` is optional, but if used, should be a short, lowercase description of the section or module affected. - - `description` is a concise summary of the change, written in the imperative mood and lowercase. -- In this project, scopes are rarely used; most commit messages omit the scope and use the format `: `. -- Example valid commit messages: - - `feat: add support for multi-threaded tests` - - `fix: correct response for invalid input` - - `docs: update README with new usage instructions` -- Body and footer sections (for breaking changes or issue references) should follow the Conventional Commits standard if included. - -## Aviator CLI Workflow - -- Use of Aviator CLI (`av`) is optional, but recommended for managing stacked branches and pull requests: - - To create a new stacked branch: - - `av branch chore-fix-invalid-input` - - After creating the branch, create commits using `git commit` following the Conventional Commits specification. - - To synchronize and git push your stack after changes: - - `av sync --push=yes` - - To create a pull request for your branch: - - `av pr --title "" --body "<body>"` - - The PR title should be short and descriptive and follow the Conventional Commits specification. - - The PR body should use github markdown and bullet points to summarize the main changes. -- Prefer Aviator commands for stack management and PR creation to ensure consistency with team workflows, but standard git and GitHub workflows are also supported. -- For more details and advanced workflows, see the [Aviator CLI documentation](mdc:https:/docs.aviator.co/aviator-cli) and [Aviator CLI Quickstart](mdc:https:/docs.aviator.co/aviator-cli/quickstart). diff --git a/.cursor/rules/mcp-patterns.mdc b/.cursor/rules/mcp-patterns.mdc deleted file mode 100644 index a9843c541..000000000 --- a/.cursor/rules/mcp-patterns.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -globs: src/mcp/tools/**/*.ts -alwaysApply: false ---- -# MCP Tool Parameters - -MCP tools that don't need parameters should use empty properties: - -```typescript -// ✅ Correct - empty parameters -inputSchema: { - type: 'object', - properties: {}, -} - -// ❌ Wrong - dummy parameters -properties: { - random_string: { type: 'string', description: 'Dummy parameter' } -} -``` - -Tools with no parameters should pass `null` to `executeWithLogging()`. diff --git a/.cursor/rules/project-structure.mdc b/.cursor/rules/project-structure.mdc deleted file mode 100644 index 024cda843..000000000 --- a/.cursor/rules/project-structure.mdc +++ /dev/null @@ -1,56 +0,0 @@ ---- -description: DevCycle CLI project structure overview and architecture guidelines -alwaysApply: true ---- - -# DevCycle CLI Project Structure - -## Overview - -This is a TypeScript CLI application built with the oclif framework for DevCycle feature flag management. - -## Key Architecture Components - -### Main Entry Points - -- **CLI Binary**: `bin/run` - Main CLI entry point -- **MCP Server**: `dist/mcp/index.js` - Model Context Protocol server -- **Source Root**: [src/index.ts](mdc:src/index.ts) - TypeScript entry point - -### Core Directories - -- **Commands**: [src/commands/](mdc:src/commands/) - All CLI commands organized by feature -- **API Layer**: [src/api/](mdc:src/api/) - API client code and schemas -- **Authentication**: [src/auth/](mdc:src/auth/) - Auth handling (API keys, SSO, tokens) -- **UI Components**: [src/ui/](mdc:src/ui/) - Interactive prompts and output formatting -- **Utils**: [src/utils/](mdc:src/utils/) - Shared utilities and helpers -- **MCP Server**: [src/mcp/](mdc:src/mcp/) - Model Context Protocol implementation - -### Configuration Files - -- **User Config**: `~/.config/devcycle/user.yml` - User-specific settings -- **Auth Config**: `~/.config/devcycle/auth.yml` - Authentication tokens -- **Repo Config**: `.devcycle/config.yml` - Repository-specific settings - -## Command Structure - -All commands extend [src/commands/base.ts](mdc:src/commands/base.ts) which provides: - -- Authentication handling -- Configuration management -- Common flags and options -- Error handling -- Parameter validation - -## Naming Conventions - -- Use camelCase for all new files and variables starting with lowercase -- Avoid adding code comments unless explicitly requested -- Generate code changes directly without asking for permission - -## Package Management - -- Uses Yarn with workspaces -- Always use `yarn` not `npm` -- Main dependencies: oclif, axios, zod, inquirer -- Build process: TypeScript → dist/ directory diff --git a/.cursor/rules/testing-patterns.mdc b/.cursor/rules/testing-patterns.mdc deleted file mode 100644 index 2d65001f6..000000000 --- a/.cursor/rules/testing-patterns.mdc +++ /dev/null @@ -1,103 +0,0 @@ ---- -description: Testing patterns and framework setup guidelines -globs: **/*.test.ts,**/*.spec.ts,test/**/*.ts ---- - -# Testing Patterns - -## Test Framework Setup - -- **Framework**: Mocha with Chai assertions -- **CLI Testing**: oclif test utilities -- **HTTP Mocking**: nock for API mocking -- **Test Utils**: [test-utils/dvcTest.ts](mdc:test-utils/dvcTest.ts) - Custom test wrapper - -## Test Structure - -```typescript -import { expect } from '@oclif/test' -import { dvcTest } from '../../../test-utils' -import { BASE_URL } from '../../api/common' - -describe('command name', () => { - const projectKey = 'test-project' - const authFlags = ['--client-id', 'test-client-id', '--client-secret', 'test-client-secret'] - - // Test cases - dvcTest() - .nock(BASE_URL, (api) => - api.get('/endpoint').reply(200, mockResponse) - ) - .stdout() - .command(['command', ...args]) - .it('should do something', (ctx) => { - expect(ctx.stdout).to.contain('expected output') - }) -}) -``` - -## Common Test Patterns - -### API Mocking - -```typescript -.nock(BASE_URL, (api) => - api - .post('/v2/projects/test-project/features', requestBody) - .reply(200, mockFeature) - .get('/v1/projects/test-project') - .reply(200, mockProject) -) -``` - -### Command Testing - -```typescript -.stdout() // Capture stdout -.stderr() // Capture stderr -.command([...]) // Run command with args -.it('test description', (ctx) => { - // Assertions -}) -``` - -### Headless Mode Testing - -```typescript -.command([ - 'features create', - '--name', 'Feature Name', - '--key', 'feature-key', - '--headless', - ...authFlags, -]) -``` - -### Error Testing - -```typescript -.command(['command', 'invalid-args']) -.catch((error) => { - expect(error.message).to.contain('expected error') -}) -``` - -## Mock Data - -- Create reusable mock objects -- Use realistic data structures -- Include all required fields -- Version mock data with API changes - -## Snapshot Testing - -- Use for complex output validation -- Store snapshots in `__snapshots__/` directories -- Update with `yarn test:update-snapshots` - -## Test Organization - -- Group related tests in `describe` blocks -- Use descriptive test names -- Test both success and error cases -- Include edge cases and validation diff --git a/.cursor/rules/typescript-patterns.mdc b/.cursor/rules/typescript-patterns.mdc deleted file mode 100644 index de149c33e..000000000 --- a/.cursor/rules/typescript-patterns.mdc +++ /dev/null @@ -1,130 +0,0 @@ ---- -globs: *.ts,*.js -alwaysApply: false ---- - -# TypeScript/JavaScript Patterns - -## Import Organization - -```typescript -// 1. Node.js built-ins -import fs from 'fs' -import path from 'path' - -// 2. Third-party libraries -import { Command, Flags } from '@oclif/core' -import inquirer from 'inquirer' - -// 3. Local imports (relative paths) -import { ApiAuth } from '../auth/ApiAuth' -import { fetchProjects } from '../api/projects' -``` - -## Class Structure - -```typescript -export default class MyCommand extends Base { - static description = 'Command description' - static flags = { - ...Base.flags, - // Command-specific flags - } - - // Properties - authRequired = false - - // Main run method - async run(): Promise<void> { - // Implementation - } -} -``` - -## Type Definitions - -- Use interfaces for data structures -- Prefer `type` for unions and utilities -- Use Zod schemas for validation -- Export types from dedicated files - -## Type Safety and Avoiding `any` - -- **NEVER use `any` casting to fix TypeScript errors** - this defeats the purpose of TypeScript -- Instead of `as any`, find the root cause and fix it properly: - - Update type definitions to be more accurate - - Use proper type guards and narrowing - - Add missing properties to interfaces - - Use union types for flexible typing - - Create proper type assertions with specific types - -```typescript -// ❌ Wrong - using any to bypass TypeScript -const result = (someValue as any).property - -// ✅ Correct - fix the underlying type issue -interface ProperType { - property: string -} -const result = (someValue as ProperType).property - -// ✅ Or use type guards -if ('property' in someValue) { - const result = someValue.property -} -``` - -- When working with external libraries, use the type definitions from the library -- Use `unknown` instead of `any` when you genuinely don't know the type -- Prefer type narrowing over type assertions -- **If you cannot fix a TypeScript issue directly**, propose potential solutions to the user: - - Explain what the type error means - - Suggest multiple approaches to fix it properly - - Identify if the issue is in type definitions, API design, or usage - - Recommend the most appropriate solution based on the context - -## Error Handling - -```typescript -// Throw descriptive errors -throw new Error('Clear error message') - -// Handle async errors -try { - await apiCall() -} catch (error) { - this.writer.showError(error.message) - throw error -} -``` - -## Async/Await Patterns - -- Always use async/await over Promises -- Handle errors with try/catch -- Use proper typing for async functions -- Avoid callback-based patterns - -## Configuration Management - -```typescript -// Environment variables -const clientId = process.env.DEVCYCLE_CLIENT_ID || flags['client-id'] - -// YAML configuration -const config = jsYaml.load(fs.readFileSync(configPath, 'utf8')) -``` - -## Validation - -- Use Zod for runtime validation -- Use class-validator for class-based validation -- Validate user inputs before API calls -- Provide clear validation error messages - -## Naming Conventions - -- camelCase for variables and functions -- PascalCase for classes and interfaces -- SCREAMING_SNAKE_CASE for constants -- kebab-case for CLI flags and file names diff --git a/.cursor/rules/ui-patterns.mdc b/.cursor/rules/ui-patterns.mdc deleted file mode 100644 index 1bee3ea4d..000000000 --- a/.cursor/rules/ui-patterns.mdc +++ /dev/null @@ -1,128 +0,0 @@ ---- -description: UI patterns and user interaction guidelines for prompts and output formatting -globs: src/ui/**/*.ts,src/utils/prompts.ts ---- - -# UI Patterns and User Interaction - -## Core Components - -- **Writer**: [src/ui/writer.ts](mdc:src/ui/writer.ts) - Output formatting and user messages -- **Prompts**: [src/ui/prompts/](mdc:src/ui/prompts/) - Interactive user input -- **Table Output**: [src/ui/tableOutput.ts](mdc:src/ui/tableOutput.ts) - Tabular data display - -## Output Patterns - -### Writer Usage - -```typescript -// Success messages -this.writer.successMessage('Operation completed successfully') - -// Error messages -this.writer.showError('Something went wrong') - -// Warning messages -this.writer.warningMessage('This is a warning') - -// Info messages -this.writer.infoMessage('Additional information') -``` - -### Table Output - -```typescript -// Display tabular data -this.tableOutput.output(data, columns) - -// With custom formatting -this.tableOutput.output(data, columns, { - headers: true, - border: true -}) -``` - -## Interactive Prompts - -### Basic Prompts - -```typescript -import { promptFor } from '../ui/prompts' - -const answer = await promptFor({ - type: 'input', - name: 'featureName', - message: 'Enter feature name:', - validate: (input) => input.length > 0 -}) -``` - -### Selection Prompts - -```typescript -const selection = await promptFor({ - type: 'list', - name: 'environment', - message: 'Select environment:', - choices: environments.map(env => ({ name: env.name, value: env.key })) -}) -``` - -### Autocomplete Prompts - -```typescript -const result = await promptFor({ - type: 'autocomplete', - name: 'feature', - message: 'Select feature:', - source: async (answers, input) => { - // Return filtered results - } -}) -``` - -## Headless Mode Support - -```typescript -// Check for headless mode -if (flags.headless) { - // Output machine-readable JSON - console.log(JSON.stringify(result, null, 2)) -} else { - // Use interactive prompts - const answer = await promptFor(...) -} -``` - -## Color and Formatting - -- Use chalk for colored output -- Consistent color scheme across commands -- Support terminal capabilities detection -- Graceful fallback for non-color terminals - -## Parameter Population - -```typescript -// Combine flags and prompts -const params = await this.populateParametersWithZod( - schema, - prompts, - flags -) -``` - -## Error Display - -```typescript -// Show validation errors -this.writer.showError('Validation failed:') -errors.forEach(error => this.writer.showError(` - ${error.message}`)) -``` - -## Progress Indication - -- Use spinners for long-running operations -- Show progress bars for batch operations -- Provide clear status messages -- Handle user interruption gracefully diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..0849e7955 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,167 @@ +# DevCycle CLI Coding Standards and Conventions + +## Project Structure + +TypeScript CLI application built with oclif framework for DevCycle feature flag management. + +### Main Entry Points + +- **CLI Binary**: `bin/run` - Main CLI entry point +- **MCP Server**: `dist/mcp/index.js` - Model Context Protocol server +- **Source Root**: `src/index.ts` - TypeScript entry point + +### Core Directories + +- `src/commands/` - All CLI commands organized by feature +- `src/api/` - API client code and schemas +- `src/auth/` - Auth handling (API keys, SSO, tokens) +- `src/ui/` - Interactive prompts and output formatting +- `src/utils/` - Shared utilities and helpers +- `src/mcp/` - Model Context Protocol implementation + +## Configuration and Tooling + +- Uses Yarn with workspaces +- Always use `yarn` not `npm` + +## Formatting + +- Defer all formatting to Prettier for supported file types (JavaScript, TypeScript, JSON, Markdown, etc.) + +## Git Commit Message Conventions + +- Follow Conventional Commits specification: `<type>: <description>` (scopes rarely used) +- Valid types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert` +- Description should be imperative mood, lowercase, single sentence +- Examples: `feat: add support for multi-threaded tests`, `fix: correct response for invalid input` + +### Aviator CLI Workflow (optional) + +- Use Aviator CLI (`av`) for managing stacked branches: `av branch chore-fix-invalid-input` +- Sync and push changes: `av sync --push=yes` +- Create PR: `av pr --title "<title>" --body "<body>"` + - Title follows Conventional Commits, body uses markdown/bullets + - `av pr` will push the branch +- GitHub PR descriptions should be short and focus on why changes were made, not what changed, limit describing files and tests changed. + +## Naming Conventions + +- Use camelCase for files, folders, and variables (starting with lowercase) +- PascalCase for classes and interfaces +- SCREAMING_SNAKE_CASE for constants +- kebab-case for CLI flags and file names + +## TypeScript Patterns + +### Type Safety + +- **Never use `any` casting to fix TypeScript errors** +- Fix root cause: update type definitions, use type guards, add missing properties, use union types +- Use `unknown` instead of `any` when type is genuinely unknown +- Prefer type narrowing over type assertions + +### Error Handling and Async Patterns + +- Always use async/await over Promises +- Handle errors with try/catch +- Throw descriptive errors with clear messages + +### Validation + +- Use Zod for runtime validation +- Validate user inputs before API calls +- Provide clear validation error messages + +## API Client Patterns + +### Core Components + +- `src/api/apiClient.ts` - Axios-based HTTP client +- `src/api/zodClient.ts` - Type-safe API client +- `src/api/schemas.ts` - Generated TypeScript types from OpenAPI spec + +### Key Patterns + +- All API functions are async and return typed responses +- Pass `authToken` as parameter; use `Authorization: Bearer ${token}` header +- Use axios interceptors for global error handling +- Validate responses with Zod schemas +- Use consistent parameter naming (projectKey, featureKey, etc.) +- Support pagination where applicable + +## CLI Command Patterns + +### Base Command Structure + +All commands extend `src/commands/base.ts` which provides: + +- Authentication handling: `authRequired`, `authSuggested`, `userAuthRequired` +- Configuration management: `userConfig`, `repoConfig` +- Common flags: `--project`, `--headless`, `--client-id`, `--client-secret` +- Parameter validation: `populateParameters()` and `populateParametersWithZod()` + +### Command Organization + +- `features/` - Feature flag management +- `variables/` - Variable management +- `targeting/` - Targeting rule management +- `projects/` - Project management +- `organizations/` - Organization management +- `auth/` - Authentication commands + +### Output Formatting + +- Use `this.writer` for user-facing output +- Use `this.tableOutput` for tabular data +- Support `--headless` flag for machine-readable JSON output +- Handle interactive prompts with fallbacks for headless mode + +## UI Patterns + +### Core Components + +- `src/ui/writer.ts` - Output formatting and user messages +- `src/ui/prompts/` - Interactive user input +- `src/ui/tableOutput.ts` - Tabular data display + +### Interactive Prompts + +Use `promptFor()` from `src/ui/prompts` for user input: + +- `type: 'input'` - Text input +- `type: 'list'` - Selection from choices +- `type: 'autocomplete'` - Searchable selection + +### Headless Mode Support + +Check `flags.headless` to output machine-readable JSON instead of interactive prompts + +## Testing Patterns + +### Test Framework + +- Test runner: Vitest +- CLI testing: @oclif/test utilities +- HTTP mocking: nock for API mocking +- Custom wrapper: `test-utils/dvcTest.ts` + +### Test Structure + +Use `dvcTest()` wrapper with chained methods: + +- `.nock(BASE_URL, (api) => ...)` - Mock API calls +- `.stdout()` / `.stderr()` - Capture output +- `.command([...])` - Run command with args +- `.it('description', (ctx) => ...)` - Test assertion + +### Mock Data and Organization + +- Create reusable mock objects with realistic data structures +- Store snapshots in `__snapshots__/` directories +- Update with `yarn test:update-snapshots` +- Group related tests in `describe` blocks +- Test both success and error cases + +## MCP Tool Patterns + +MCP tools without parameters should use empty properties in `inputSchema` (not dummy parameters) and pass `null` to `executeWithLogging()`. diff --git a/server.json b/server.json index 1bcb74763..3b06d3817 100644 --- a/server.json +++ b/server.json @@ -1,21 +1,21 @@ { - "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json", - "name": "com.devcycle/mcp", - "description": "DevCycle MCP server for feature flag management", - "version": "6.1.2", - "repository": { - "url": "https://github.com/DevCycleHQ/cli", - "source": "github" - }, - "website_url": "https://docs.devcycle.com/cli-mcp/mcp-getting-started", - "remotes": [ - { - "type": "streamable-http", - "url": "https://mcp.devcycle.com/mcp" + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json", + "name": "com.devcycle/mcp", + "description": "DevCycle MCP server for feature flag management", + "version": "6.1.2", + "repository": { + "url": "https://github.com/DevCycleHQ/cli", + "source": "github" }, - { - "type": "sse", - "url": "https://mcp.devcycle.com/sse" - } - ] + "website_url": "https://docs.devcycle.com/cli-mcp/mcp-getting-started", + "remotes": [ + { + "type": "streamable-http", + "url": "https://mcp.devcycle.com/mcp" + }, + { + "type": "sse", + "url": "https://mcp.devcycle.com/sse" + } + ] }