cyclic-dependency-fixer
v2.0.1
Published
AI-powered tool to detect and fix circular dependencies in JavaScript/TypeScript projects. Features intelligent refactoring with Claude/GPT-4, codebase pattern learning, and context-aware fix recommendations
Maintainers
Keywords
Readme
cyclic-dependency-fixer
🔄 Detect and automatically fix cyclic dependencies in JavaScript/TypeScript projects with AI-powered analysis
cyclic-dependency-fixer is a lightweight, powerful tool that not only detects circular dependencies but also provides intelligent auto-fix strategies powered by AI (Claude, GPT-4). Get context-aware recommendations, automated refactoring code, and actionable insights.
Table of Contents
- Features
- Installation
- Quick Start
- AI-Powered Features
- Usage
- Fix Strategies
- Output Example
- Architecture
- Testing
- Development
- Advanced Usage
- Contributing
- License
- Support
✨ Features
Core Features
- 🔍 Fast Detection - Uses Tarjan's algorithm for efficient cycle detection (O(V + E))
- 🛠️ Auto-Fix Strategies - Attempts to automatically fix cycles when safe
- 📝 Manual Fix Guidance - Provides clear, actionable steps when auto-fix isn't possible
- 🎯 Multiple Strategies - Dynamic imports, shared module extraction, and more
- 📊 Clear Output - User-friendly CLI with colored output
- 🔌 Extensible - Clean architecture allows custom fix strategies
- 💪 Type-Safe - Written in TypeScript with strict typing
- 🪶 Lightweight - Minimal dependencies, uses regex-based parsing
🤖 AI-Powered Features (NEW!)
- Smart Strategy Selection - AI analyzes your code and recommends the best fix strategy
- Codebase Pattern Learning - Understands your architecture and coding patterns
- Intelligent Refactoring - Generates production-ready refactoring code
- Root Cause Analysis - Explains WHY circular dependencies exist
- Context-Aware Suggestions - Recommendations tailored to your codebase
- Multiple AI Providers - Support for Claude (Anthropic) and GPT-4 (OpenAI)
🚀 Installation
npm install -g cyclic-dependency-fixerOr use locally in your project:
npm install --save-dev cyclic-dependency-fixer⚡ Quick Start
Basic Usage (No AI)
# Detect circular dependencies
cycfix detect
# Attempt to fix them automatically
cycfix fix --dry-run # Preview changes
cycfix fix # Apply fixesWith AI-Powered Analysis
# Set up your API key (one-time)
export ANTHROPIC_API_KEY=sk-ant-xxx # or OPENAI_API_KEY
# Run with AI-powered recommendations
cycfix fix --ai --generate-code
# Get detailed AI explanations
cycfix fix --ai --explain --generate-code🤖 AI-Powered Features
Why Use AI?
Traditional static analysis can detect cycles but struggles with context. AI understands:
- Semantic relationships between modules
- Your codebase's architecture (layered, hexagonal, clean architecture, etc.)
- Common patterns you use (dependency injection, factory pattern, etc.)
- Why cycles exist (shared types, bidirectional relationships, etc.)
Setup
Get an API Key (choose one):
- Claude (Anthropic): https://console.anthropic.com/
- GPT-4 (OpenAI): https://platform.openai.com/api-keys
Set Environment Variable:
# For Claude (recommended) export ANTHROPIC_API_KEY=sk-ant-xxx # Or for GPT-4 export OPENAI_API_KEY=sk-xxxOr use CLI flag:
cycfix fix --ai --ai-key sk-ant-xxx
AI Features
1. Smart Strategy Selection
AI analyzes your code and recommends the best fix strategy:
cycfix fix --aiOutput:
🤖 Analyzing codebase patterns with AI...
Architecture: Clean Architecture (Layered)
Patterns found: 3
🤖 Getting AI recommendation for cycle abc123...
Recommended: extract-shared (85% confidence)
Reasoning: Both UserService and OrderService depend on shared types.
Creating a shared types module maintains your layered architecture.2. Intelligent Code Generation
Get production-ready refactoring code:
cycfix fix --ai --generate-codeOutput:
📝 Manual steps to fix:
1. Create shared types module
File: src/shared/types/user-order.types.ts
export interface UserId {
readonly id: string;
}
export interface OrderReference {
readonly userId: UserId;
readonly orderId: string;
}
2. Update UserService imports
File: src/services/user.service.ts
Line: 3
- import { OrderReference } from './order.service'
+ import { OrderReference } from '../shared/types/user-order.types'3. Root Cause Analysis
Understand WHY cycles exist:
cycfix fix --ai --explainOutput:
AI Analysis:
This circular dependency exists because:
1. UserService needs to track user orders (OrderReference type)
2. OrderService needs to validate users (UserId type)
3. Both services define types the other needs
Root Cause: Shared domain types without a common module
Impact:
- Prevents tree-shaking
- Can cause runtime initialization errors
- Makes testing harder (mocking circular deps)
Prevention:
- Create a shared types layer (src/types/)
- Follow the Dependency Inversion Principle
- Use interfaces to define contracts4. Architecture-Aware Recommendations
AI adapts to YOUR codebase patterns:
✓ Detected: You use dependency injection in 80% of services
✓ Detected: Layered architecture with clear boundaries
✓ Detected: Barrel files (index.ts) for public APIs
Recommendation: Use dependency injection pattern to break this cycle,
as it aligns with your existing architecture.AI CLI Options
| Option | Description |
|--------|-------------|
| --ai | Enable AI-powered analysis |
| --ai-provider <provider> | Choose provider: anthropic or openai (default: anthropic) |
| --ai-key <key> | Provide API key directly (or use env var) |
| --explain | Generate AI explanations of why cycles exist |
| --generate-code | Generate complete refactoring code with AI |
Example Workflow
# 1. Detect with AI analysis
cycfix fix --ai --dry-run
# 2. Review AI recommendations
# AI will show:
# - Detected architecture
# - Recommended strategy with confidence score
# - Reasoning for the recommendation
# 3. Generate refactoring code
cycfix fix --ai --generate-code --dry-run
# 4. Apply fixes
cycfix fix --ai --generate-code📖 Usage
CLI
Detect Cycles
cycfix detectOptions:
-d, --dir <directory>- Root directory to analyze (default: current directory)-e, --extensions <extensions>- File extensions to include (default: .js,.jsx,.ts,.tsx)-x, --exclude <patterns>- Patterns to exclude (comma-separated)--include-node-modules- Include node_modules in analysis--max-depth <depth>- Maximum depth for cycle detection (default: 50)
Example:
cycfix detect --dir ./src --extensions .ts,.tsx --exclude tests,__mocks__Fix Cycles
cycfix fixOptions:
-d, --dir <directory>- Root directory to analyze-e, --extensions <extensions>- File extensions to include-x, --exclude <patterns>- Patterns to exclude--dry-run- Preview fixes without modifying files--no-backup- Don't create backup files--auto- Automatically apply fixes without confirmation
Example:
cycfix fix --dry-run # Preview changes
cycfix fix # Apply fixes with backupsProgrammatic API
import { createAnalyzer } from 'cyclic-dependency-fixer';
const analyzer = createAnalyzer('./src');
// Detect cycles
const result = await analyzer.detect({
extensions: ['.ts', '.tsx'],
exclude: ['node_modules', 'dist'],
});
console.log(`Found ${result.cycles.length} cycles`);
// Attempt to fix
const { analysisResult, fixResults } = await analyzer.fix({
extensions: ['.ts', '.tsx'],
}, {
dryRun: false,
backup: true,
});
fixResults.forEach(result => {
if (result.success) {
console.log(`✓ Fixed ${result.cycle.id}`);
} else {
console.log(`⚠ Manual steps required for ${result.cycle.id}`);
result.manualSteps?.forEach(step => {
console.log(` - ${step.description}`);
});
}
});🎯 Fix Strategies
1. Dynamic Import Strategy
Converts static imports to dynamic imports to break the cycle.
Best for: Simple 2-node cycles where lazy loading is acceptable.
Example:
// Before
import { utils } from './utils';
// After
const { utils } = await import('./utils');2. Extract Shared Strategy
Creates a new shared module to hold common code.
Best for: Modules in the same directory sharing common functionality.
Example:
Before: a.ts ↔ b.ts
After: a.ts → shared.ts ← b.ts📊 Output Example
📊 Analysis Results
──────────────────────────────────────────────────
Total modules analyzed: 45
Analysis duration: 123ms
✗ Found 2 circular dependencies
Affected modules: 4
Cycle #1 (abc12345)
src/components/Button.tsx →
src/components/Form.tsx →
src/components/Button.tsx ⤴
Import details:
src/components/Button.tsx:5 imports src/components/Form.tsx
src/components/Form.tsx:12 imports src/components/Button.tsx
🔧 Fix Results
──────────────────────────────────────────────────
⚠ Could not auto-fix cycle abc12345
Strategy attempted: dynamic-import
📝 Manual steps to fix:
1. Convert static import to dynamic import in src/components/Button.tsx
File: src/components/Button.tsx
Line: 5
// Replace:
// import x from './Form'
// With:
const x = await import('./Form').then(m => m.default || m);
2. Make the parent function async if needed
File: src/components/Button.tsx🏗️ Architecture
Built with Clean Architecture principles:
src/
├── domain/ # Core business logic
│ ├── models/ # Domain entities & types
│ └── interfaces/ # Abstractions (IFileSystem, IParser, etc.)
├── application/ # Use cases
│ ├── DetectCyclesUseCase.ts
│ ├── FixCyclesUseCase.ts
│ └── fix-strategies/ # Fix strategy implementations
├── infrastructure/ # External concerns
│ ├── filesystem/ # File system operations
│ ├── parsers/ # Code parsing
│ └── graph/ # Cycle detection algorithms
└── cli/ # Command-line interfaceKey Design Principles
- ✅ SOLID Principles - Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion
- ✅ Dependency Injection - All dependencies injected for testability
- ✅ Strategy Pattern - Pluggable fix strategies
- ✅ No Any Types - Strict TypeScript typing throughout
🧪 Testing
Comprehensive test coverage (>90%):
npm test # Run all tests
npm run test:watch # Watch mode🔧 Development
# Install dependencies
npm install
# Build
npm run build
# Lint
npm run lint
npm run lint:fix
# Format
npm run format📚 Advanced Usage
Custom Fix Strategy
Extend the package with your own fix strategies:
import { IFixStrategy, FixStrategy, Cycle } from 'cyclic-dependency-fixer';
class MyCustomStrategy implements IFixStrategy {
readonly type = FixStrategy.CUSTOM as any;
async canFix(cycle: Cycle): Promise<boolean> {
// Your logic
return true;
}
score(cycle: Cycle): number {
// Higher score = preferred
return 100;
}
async fix(cycle, modules, fileSystem, dryRun) {
// Implement your fix
return { /* FixResult */ };
}
}Integration with CI/CD
Add to your GitHub Actions:
- name: Check for circular dependencies
run: npx cycfix detect🤝 Contributing
Contributions welcome! Please read our Contributing Guide first.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT © Ashish Yadav
🙏 Acknowledgments
- Inspired by madge for cycle detection
- Uses Tarjan's algorithm for efficient SCC detection
- Built with TypeScript, Commander, Chalk, and Ora
📮 Support
Made with ❤️ for the TypeScript community
