@tonycasey/lisa 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +42 -0
  2. package/dist/cli.js +390 -0
  3. package/dist/lib/interfaces/IDockerClient.js +2 -0
  4. package/dist/lib/interfaces/IMcpClient.js +2 -0
  5. package/dist/lib/interfaces/IServices.js +2 -0
  6. package/dist/lib/interfaces/ITemplateCopier.js +2 -0
  7. package/dist/lib/mcp.js +35 -0
  8. package/dist/lib/services.js +57 -0
  9. package/dist/package.json +36 -0
  10. package/dist/templates/agents/.sample.env +12 -0
  11. package/dist/templates/agents/docs/STORAGE_SETUP.md +161 -0
  12. package/dist/templates/agents/skills/common/group-id.js +193 -0
  13. package/dist/templates/agents/skills/init-review/SKILL.md +119 -0
  14. package/dist/templates/agents/skills/init-review/scripts/ai-enrich.js +258 -0
  15. package/dist/templates/agents/skills/init-review/scripts/init-review.js +769 -0
  16. package/dist/templates/agents/skills/lisa/SKILL.md +92 -0
  17. package/dist/templates/agents/skills/lisa/cache/.gitkeep +0 -0
  18. package/dist/templates/agents/skills/lisa/scripts/storage.js +374 -0
  19. package/dist/templates/agents/skills/memory/SKILL.md +31 -0
  20. package/dist/templates/agents/skills/memory/scripts/memory.js +533 -0
  21. package/dist/templates/agents/skills/prompt/SKILL.md +19 -0
  22. package/dist/templates/agents/skills/prompt/scripts/prompt.js +184 -0
  23. package/dist/templates/agents/skills/tasks/SKILL.md +31 -0
  24. package/dist/templates/agents/skills/tasks/scripts/tasks.js +489 -0
  25. package/dist/templates/claude/config.js +40 -0
  26. package/dist/templates/claude/hooks/README.md +158 -0
  27. package/dist/templates/claude/hooks/common/complexity-rater.js +290 -0
  28. package/dist/templates/claude/hooks/common/context.js +263 -0
  29. package/dist/templates/claude/hooks/common/group-id.js +188 -0
  30. package/dist/templates/claude/hooks/common/mcp-client.js +131 -0
  31. package/dist/templates/claude/hooks/common/transcript-parser.js +256 -0
  32. package/dist/templates/claude/hooks/common/zep-client.js +175 -0
  33. package/dist/templates/claude/hooks/session-start.js +401 -0
  34. package/dist/templates/claude/hooks/session-stop-worker.js +341 -0
  35. package/dist/templates/claude/hooks/session-stop.js +122 -0
  36. package/dist/templates/claude/hooks/user-prompt-submit.js +256 -0
  37. package/dist/templates/claude/settings.json +46 -0
  38. package/dist/templates/docker/.env.lisa.example +17 -0
  39. package/dist/templates/docker/docker-compose.graphiti.yml +45 -0
  40. package/dist/templates/rules/shared/clean-architecture.md +333 -0
  41. package/dist/templates/rules/shared/code-quality-rules.md +469 -0
  42. package/dist/templates/rules/shared/git-rules.md +64 -0
  43. package/dist/templates/rules/shared/testing-principles.md +469 -0
  44. package/dist/templates/rules/typescript/coding-standards.md +751 -0
  45. package/dist/templates/rules/typescript/testing.md +629 -0
  46. package/dist/templates/rules/typescript/typescript-config-guide.md +465 -0
  47. package/package.json +64 -0
  48. package/scripts/postinstall.js +710 -0
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * Claude Code - User Prompt Submit Hook
6
+ *
7
+ * This hook runs before a user's prompt is submitted to Claude.
8
+ * It can be used to validate, enhance, or log prompts.
9
+ *
10
+ * Configuration: .claude/settings.json -> hooks.UserPromptSubmit
11
+ *
12
+ * Note: This hook is optional and only runs if configured.
13
+ * If the hook exits with non-zero status, the prompt submission is cancelled.
14
+ */
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { spawn } = require('child_process');
18
+ const { PROJECT_ROOT, PROMPT_SKILL_PATH, DEV_DIR } = require('../config');
19
+ /**
20
+ * Validate prompt for potentially problematic patterns
21
+ */
22
+ function validatePrompt(prompt) {
23
+ const warnings = [];
24
+ // Check for overly broad requests
25
+ if (prompt.toLowerCase().includes('delete all') ||
26
+ prompt.toLowerCase().includes('remove everything')) {
27
+ warnings.push(' Destructive operation detected - please be specific');
28
+ }
29
+ // Check for requests without context
30
+ if (prompt.length < 10) {
31
+ warnings.push(' Very short prompt - consider providing more context');
32
+ }
33
+ return warnings;
34
+ }
35
+ /**
36
+ * Enhance prompt with project context if needed
37
+ */
38
+ function enhancePrompt(prompt) {
39
+ const suggestions = [];
40
+ if (prompt.toLowerCase().includes('architecture') ||
41
+ prompt.toLowerCase().includes('structure')) {
42
+ const archPath = path.join(DEV_DIR, 'architecture.md');
43
+ if (fs.existsSync(archPath)) {
44
+ suggestions.push(' Consider referencing @.dev/architecture.md for context');
45
+ }
46
+ }
47
+ if (prompt.toLowerCase().includes('todo') ||
48
+ prompt.toLowerCase().includes('task')) {
49
+ const todoPath = path.join(DEV_DIR, 'todo.md');
50
+ if (fs.existsSync(todoPath)) {
51
+ suggestions.push(' Your todo list is available at @.dev/todo.md');
52
+ }
53
+ }
54
+ return suggestions;
55
+ }
56
+ /**
57
+ * Log prompt for analytics (optional)
58
+ */
59
+ function logPrompt(prompt) {
60
+ const logPath = path.join(DEV_DIR, '.prompt-log.jsonl');
61
+ const logEntry = {
62
+ timestamp: new Date().toISOString(),
63
+ promptLength: prompt.length,
64
+ promptPreview: prompt.substring(0, 100)
65
+ };
66
+ try {
67
+ fs.appendFileSync(logPath, JSON.stringify(logEntry) + '\n');
68
+ }
69
+ catch (_error) {
70
+ // Silently fail if logging doesn't work
71
+ }
72
+ }
73
+ /**
74
+ * Check if Graphiti MCP server is available
75
+ * Returns true if server responds, false otherwise
76
+ */
77
+ async function isGraphitiAvailable() {
78
+ // Read endpoint from .env file (same logic as prompt skill)
79
+ const envPath = path.join(PROJECT_ROOT, '.agents', 'skills', '.env');
80
+ let endpoint = 'http://localhost:8010/mcp/';
81
+ try {
82
+ if (fs.existsSync(envPath)) {
83
+ const raw = fs.readFileSync(envPath, 'utf8');
84
+ const lines = raw.split(/\r?\n/);
85
+ for (const line of lines) {
86
+ if (line.startsWith('GRAPHITI_ENDPOINT=')) {
87
+ endpoint = line.slice('GRAPHITI_ENDPOINT='.length).trim();
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ catch {
94
+ // Use default endpoint
95
+ }
96
+ try {
97
+ const controller = new AbortController();
98
+ const timeoutId = setTimeout(() => controller.abort(), 2000);
99
+ const response = await fetch(endpoint, {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({
103
+ jsonrpc: '2.0',
104
+ id: 'health',
105
+ method: 'ping',
106
+ params: {}
107
+ }),
108
+ signal: controller.signal
109
+ });
110
+ clearTimeout(timeoutId);
111
+ return response.ok || response.status === 400; // 400 means server is up but method not found
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
118
+ * Store prompt to Graphiti MCP for cross-session memory
119
+ * Graphiti's LLM automatically classifies the content as:
120
+ * - KeyDecision, DirectionChange, ArchitecturalChoice, Preference, etc.
121
+ * Classification happens server-side based on entity_types in config.yaml
122
+ */
123
+ async function storeToGraphiti(prompt) {
124
+ if (!fs.existsSync(PROMPT_SKILL_PATH)) {
125
+ return null; // Silently skip if skill not found
126
+ }
127
+ // Check if Graphiti is available before attempting storage
128
+ const available = await isGraphitiAvailable();
129
+ if (!available) {
130
+ return { status: 'unavailable' };
131
+ }
132
+ return new Promise((resolve) => {
133
+ const child = spawn('node', [
134
+ PROMPT_SKILL_PATH,
135
+ '--text', prompt,
136
+ '--role', 'user',
137
+ '--source', 'user-prompt' // Graphiti LLM classifies content automatically
138
+ ], {
139
+ cwd: PROJECT_ROOT,
140
+ stdio: ['ignore', 'pipe', 'pipe']
141
+ });
142
+ let stdout = '';
143
+ let stderr = '';
144
+ child.stdout.on('data', (data) => { stdout += data; });
145
+ child.stderr.on('data', (data) => { stderr += data; });
146
+ child.on('close', (code) => {
147
+ if (code === 0) {
148
+ try {
149
+ const result = JSON.parse(stdout);
150
+ resolve(result);
151
+ }
152
+ catch {
153
+ resolve({ status: 'ok', raw: stdout.trim() });
154
+ }
155
+ }
156
+ else {
157
+ // Check if it's a connection error
158
+ const errorMsg = stderr.trim() || `exit code ${code}`;
159
+ if (errorMsg.includes('fetch failed') || errorMsg.includes('ECONNREFUSED')) {
160
+ resolve({ status: 'unavailable' });
161
+ }
162
+ else {
163
+ resolve({ status: 'error', error: errorMsg });
164
+ }
165
+ }
166
+ });
167
+ child.on('error', (err) => {
168
+ if (err.message.includes('fetch failed') || err.message.includes('ECONNREFUSED')) {
169
+ resolve({ status: 'unavailable' });
170
+ }
171
+ else {
172
+ resolve({ status: 'error', error: err.message });
173
+ }
174
+ });
175
+ // Timeout after 5 seconds
176
+ setTimeout(() => {
177
+ child.kill();
178
+ resolve({ status: 'timeout' });
179
+ }, 5000);
180
+ });
181
+ }
182
+ /**
183
+ * Main execution - reads JSON from stdin (Claude Code hook protocol)
184
+ */
185
+ function main() {
186
+ let inputData = '';
187
+ // Read JSON from stdin
188
+ process.stdin.on('data', (chunk) => {
189
+ inputData += chunk;
190
+ });
191
+ process.stdin.on('end', async () => {
192
+ try {
193
+ const hookInput = JSON.parse(inputData);
194
+ const prompt = hookInput.prompt || '';
195
+ if (!prompt) {
196
+ console.log(' No prompt in hook input');
197
+ process.exit(0);
198
+ }
199
+ // Log what we captured for debugging
200
+ console.log(`Captured prompt (${prompt.length} chars): "${prompt.substring(0, 80)}${prompt.length > 80 ? '...' : ''}"`);
201
+ // Validate prompt
202
+ const warnings = validatePrompt(prompt);
203
+ if (warnings.length > 0) {
204
+ warnings.forEach(warning => console.log(warning));
205
+ }
206
+ // Enhance prompt with suggestions
207
+ const suggestions = enhancePrompt(prompt);
208
+ if (suggestions.length > 0) {
209
+ suggestions.forEach(suggestion => console.log(suggestion));
210
+ }
211
+ // Log prompt for analytics
212
+ logPrompt(prompt);
213
+ // Store to Graphiti MCP for cross-session memory
214
+ const graphitiResult = await storeToGraphiti(prompt);
215
+ if (graphitiResult) {
216
+ if (graphitiResult.status === 'ok') {
217
+ // Successfully stored - no need to show message
218
+ }
219
+ else if (graphitiResult.status === 'skipped') {
220
+ // Duplicate - silently skip
221
+ }
222
+ else if (graphitiResult.status === 'unavailable') {
223
+ console.log(' lisa works but needs further setup to persist her memory');
224
+ }
225
+ else if (graphitiResult.status === 'error') {
226
+ // Only show actual errors, not connection issues
227
+ console.log(' lisa works but needs further setup to persist her memory');
228
+ }
229
+ else if (graphitiResult.status === 'timeout') {
230
+ console.log(' lisa works but needs further setup to persist her memory');
231
+ }
232
+ }
233
+ // Exit with 0 to allow prompt to proceed
234
+ process.exit(0);
235
+ }
236
+ catch (error) {
237
+ const message = error instanceof Error ? error.message : String(error);
238
+ console.error(` Failed to parse hook input: ${message}`);
239
+ // Exit with 0 to not block the prompt on errors
240
+ process.exit(0);
241
+ }
242
+ });
243
+ }
244
+ // Run if called directly
245
+ if (require.main === module) {
246
+ try {
247
+ main();
248
+ }
249
+ catch (error) {
250
+ const message = error instanceof Error ? error.message : String(error);
251
+ console.error(` Prompt validation failed: ${message}`);
252
+ // Exit with 0 to not block the prompt
253
+ process.exit(0);
254
+ }
255
+ }
256
+ module.exports = { validatePrompt, enhancePrompt, logPrompt };
@@ -0,0 +1,46 @@
1
+ {
2
+ "workflowsDirectory": ".claude/workflows",
3
+ "autoLoadRules": true,
4
+ "hooks": {
5
+ "SessionStart": [
6
+ {
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "node .claude/hooks/session-start.js"
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "Stop": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node .claude/hooks/session-stop.js"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "UserPromptSubmit": [
26
+ {
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "node .claude/hooks/user-prompt-submit.js"
31
+ }
32
+ ]
33
+ }
34
+ ]
35
+ },
36
+ "excludePatterns": [
37
+ "**/node_modules/**",
38
+ "**/dist/**",
39
+ "**/build/**",
40
+ "**/lib/**",
41
+ "**/.git/**",
42
+ "**/coverage/**",
43
+ "**/.next/**",
44
+ "**/.nuxt/**"
45
+ ]
46
+ }
@@ -0,0 +1,17 @@
1
+ # Lisa Memory Settings
2
+ # Copy this file to .env in your project root and fill in the values
3
+
4
+ # OpenAI API key (required for entity extraction)
5
+ OPENAI_API_KEY=sk-...
6
+
7
+ # Neo4j credentials
8
+ NEO4J_USER=neo4j
9
+ NEO4J_PASSWORD=demodemo
10
+ NEO4J_DATABASE=neo4j
11
+
12
+ # Graphiti MCP
13
+ # Group ID defaults to project name from package.json or directory name
14
+ GRAPHITI_GROUP_ID=your-project-name
15
+ GRAPHITI_ENDPOINT=http://localhost:8010/mcp/
16
+ SEMAPHORE_LIMIT=10
17
+ USE_PARALLEL_RUNTIME=false
@@ -0,0 +1,45 @@
1
+ name: lisa
2
+
3
+ services:
4
+ neo4j:
5
+ image: neo4j:5.26.2
6
+ hostname: neo4j
7
+ ports:
8
+ - "7474:7474"
9
+ - "7687:7687"
10
+ environment:
11
+ - NEO4J_AUTH=${NEO4J_USER:-neo4j}/${NEO4J_PASSWORD:-demodemo}
12
+ volumes:
13
+ - neo4j_data:/data
14
+ - neo4j_logs:/logs
15
+ restart: always
16
+ healthcheck:
17
+ test: ["CMD-SHELL", "wget -qO- http://localhost:7474 || exit 1"]
18
+ interval: 5s
19
+ timeout: 5s
20
+ retries: 5
21
+ start_period: 15s
22
+
23
+ graphiti-mcp:
24
+ image: zepai/knowledge-graph-mcp:latest
25
+ depends_on:
26
+ neo4j:
27
+ condition: service_healthy
28
+ env_file:
29
+ - path: ../.env
30
+ required: false
31
+ environment:
32
+ # NEO4J_URI must point to container hostname, not localhost
33
+ - NEO4J_URI=bolt://neo4j:7687
34
+ ports:
35
+ - "8010:8000"
36
+ restart: always
37
+ healthcheck:
38
+ test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
39
+ interval: 10s
40
+ timeout: 5s
41
+ retries: 3
42
+
43
+ volumes:
44
+ neo4j_data:
45
+ neo4j_logs:
@@ -0,0 +1,333 @@
1
+ # Clean Architecture Principles
2
+
3
+ **Note:** These principles apply to ALL programming languages. See your language-specific rules for implementation details.
4
+
5
+ ## Overview
6
+
7
+ Clean Architecture is a software design philosophy that emphasizes separation of concerns and dependency management. The core idea is to organize code into layers, with dependencies flowing inward toward the core business logic.
8
+
9
+ ## Layer Structure
10
+
11
+ ```
12
+ src/
13
+ ├── domain/ # Core business entities and rules
14
+ │ ├── entities/ # Business entities
15
+ │ ├── errors/ # Domain-specific errors
16
+ │ └── interfaces/ # Contracts (interfaces/protocols/traits)
17
+ ├── application/ # Business logic and use cases
18
+ │ ├── interfaces/ # Application service contracts
19
+ │ └── services/ # Use case implementations
20
+ └── infrastructure/ # External concerns
21
+ ├── repositories/ # Data access implementations
22
+ └── services/ # External API integrations
23
+ ```
24
+
25
+ ## Layer Responsibilities
26
+
27
+ ### 1. Domain Layer (`src/domain/`)
28
+
29
+ **Purpose:** Contains the core business rules and entities with zero dependencies on external frameworks or libraries.
30
+
31
+ **What belongs here:**
32
+ - Core business entities (Product, Order, User, etc.)
33
+ - Business rules and validation logic
34
+ - Domain errors and exceptions
35
+ - Repository interfaces (contracts for data access)
36
+ - Core service interfaces (business logic contracts)
37
+
38
+ **Dependencies:** NONE - Domain depends on nothing else
39
+
40
+ **Examples across languages:**
41
+ - Entities: `Product`, `Order`, `Customer`
42
+ - Interfaces: Repository contracts, service contracts
43
+ - Errors: `ProductNotFoundError`, `InvalidOrderStateError`
44
+
45
+ ### 2. Application Layer (`src/application/`)
46
+
47
+ **Purpose:** Orchestrates the domain layer and coordinates infrastructure to implement use cases.
48
+
49
+ **What belongs here:**
50
+ - Use case implementations
51
+ - Application service interfaces
52
+ - Application-specific logic
53
+ - Coordination between domain and infrastructure
54
+
55
+ **Dependencies:** Can depend on Domain layer ONLY
56
+
57
+ **Examples:**
58
+ - Services: `OrderService`, `CartService`, `CheckoutService`
59
+ - Use cases: `CreateOrderUseCase`, `ProcessPaymentUseCase`
60
+ - Configurations: `CheckoutConfig`, `PaymentConfig`
61
+
62
+ ### 3. Infrastructure Layer (`src/infrastructure/`)
63
+
64
+ **Purpose:** Handles all interactions with external systems, databases, APIs, and frameworks.
65
+
66
+ **What belongs here:**
67
+ - Repository implementations (database access)
68
+ - External API clients
69
+ - Framework-specific code
70
+ - Database connections
71
+ - Third-party service integrations
72
+
73
+ **Dependencies:** Can depend on both Application and Domain layers
74
+
75
+ **Examples:**
76
+ - Repositories: `ProductRepository`, `OrderRepository`
77
+ - Services: `DatabaseService`, `PaymentGatewayService`
78
+ - Clients: `EmailServiceClient`, `SMSServiceClient`
79
+
80
+ ## Dependency Rules
81
+
82
+ ### The Dependency Rule
83
+
84
+ **Dependencies MUST point inward:**
85
+
86
+ ```
87
+ Infrastructure → Application → Domain
88
+
89
+ Core Business
90
+ ```
91
+
92
+ - **Domain** has NO dependencies
93
+ - **Application** depends on Domain only
94
+ - **Infrastructure** depends on Application and Domain
95
+
96
+ ### Why This Matters
97
+
98
+ 1. **Testability**: Domain can be tested without databases or external services
99
+ 2. **Flexibility**: Swap infrastructure without changing business logic
100
+ 3. **Maintainability**: Changes to external systems don't affect core business rules
101
+ 4. **Independence**: Business logic is independent of frameworks and tools
102
+
103
+ ## Dependency Inversion Principle
104
+
105
+ The key to Clean Architecture is **Dependency Inversion**:
106
+
107
+ - High-level modules should not depend on low-level modules
108
+ - Both should depend on abstractions (interfaces/contracts)
109
+ - Abstractions should not depend on details
110
+ - Details should depend on abstractions
111
+
112
+ ### Example Flow
113
+
114
+ ```
115
+ OrderService (Application)
116
+ ↓ depends on
117
+ IOrderRepository (Domain interface)
118
+ ↑ implemented by
119
+ OrderRepository (Infrastructure)
120
+ ```
121
+
122
+ The service depends on the interface, not the implementation. The implementation depends on the interface.
123
+
124
+ ## Repository Pattern
125
+
126
+ The Repository pattern mediates between the domain and data mapping layers.
127
+
128
+ ### Key Concepts
129
+
130
+ 1. **Interface in Domain**: Repository contracts live in domain layer
131
+ 2. **Implementation in Infrastructure**: Actual data access in infrastructure
132
+ 3. **Services use Interfaces**: Application services depend on interfaces, not implementations
133
+ 4. **Centralized Data Access**: All data operations go through repositories
134
+
135
+ ### Why Use Repositories
136
+
137
+ - Decouples business logic from data storage
138
+ - Makes testing easier (mock repositories)
139
+ - Enables swapping data sources (SQL → NoSQL)
140
+ - Centralizes data access patterns
141
+ - Provides clear separation of concerns
142
+
143
+ ## Dependency Injection
144
+
145
+ All dependencies should be injected, not instantiated.
146
+
147
+ ### Constructor Injection
148
+
149
+ Dependencies are provided through the constructor, making them explicit and testable.
150
+
151
+ **Benefits:**
152
+ - Explicit dependencies
153
+ - Easy to test (inject mocks)
154
+ - Promotes loose coupling
155
+ - Enables swapping implementations
156
+
157
+ ### Registration
158
+
159
+ All services and repositories must be registered in a dependency injection container.
160
+
161
+ **Location:** `src/infrastructure/di/`
162
+
163
+ ## SOLID Principles
164
+
165
+ ### Single Responsibility Principle (SRP)
166
+
167
+ **Definition:** Each class should have only one reason to change.
168
+
169
+ **Application:**
170
+ - Each repository handles one entity
171
+ - Each service handles one use case or related group
172
+ - Keep classes focused and cohesive
173
+
174
+ ### Open/Closed Principle (OCP)
175
+
176
+ **Definition:** Open for extension, closed for modification.
177
+
178
+ **Application:**
179
+ - Use interfaces to allow extension
180
+ - Compose behavior rather than modifying existing code
181
+ - Use dependency injection to swap implementations
182
+
183
+ ### Liskov Substitution Principle (LSP)
184
+
185
+ **Definition:** Derived classes must be substitutable for their base classes.
186
+
187
+ **Application:**
188
+ - Implementations must honor interface contracts
189
+ - No unexpected behavior in derived classes
190
+ - Maintain consistent semantics
191
+
192
+ ### Interface Segregation Principle (ISP)
193
+
194
+ **Definition:** Clients should not depend on interfaces they don't use.
195
+
196
+ **Application:**
197
+ - Create small, focused interfaces
198
+ - Split large interfaces into smaller ones
199
+ - Each interface should represent one capability
200
+
201
+ ### Dependency Inversion Principle (DIP)
202
+
203
+ **Definition:** Depend on abstractions, not concretions.
204
+
205
+ **Application:**
206
+ - Services depend on repository interfaces
207
+ - Use dependency injection
208
+ - Infrastructure implements domain interfaces
209
+
210
+ ## Common Violations
211
+
212
+ ### ❌ Layer Violations
213
+
214
+ **Problem:** Domain importing from infrastructure
215
+
216
+ ```
217
+ // BAD - Domain importing infrastructure
218
+ // In: src/domain/services/ProductService
219
+ import { Database } from '../../infrastructure/database';
220
+ ```
221
+
222
+ **Solution:** Domain should only define interfaces
223
+
224
+ ```
225
+ // GOOD - Domain defines interface
226
+ // In: src/domain/interfaces/
227
+ export interface IDatabase { ... }
228
+
229
+ // Infrastructure implements it
230
+ // In: src/infrastructure/
231
+ export class Database implements IDatabase { ... }
232
+ ```
233
+
234
+ ### ❌ Application Importing Infrastructure
235
+
236
+ **Problem:** Application layer importing infrastructure implementations
237
+
238
+ ```
239
+ // BAD - Application importing infrastructure
240
+ // In: src/application/services/OrderService
241
+ import { ProductRepository } from '../../infrastructure/repositories/ProductRepository';
242
+ ```
243
+
244
+ **Solution:** Application should depend on domain interfaces
245
+
246
+ ```
247
+ // GOOD - Application depends on interface
248
+ // In: src/application/services/OrderService
249
+ import { IProductRepository } from '../../domain/interfaces/IProductRepository';
250
+
251
+ // Infrastructure is injected via DI
252
+ constructor(private readonly productRepo: IProductRepository) {}
253
+ ```
254
+
255
+ ### ❌ Direct Database Access in Services
256
+
257
+ **Problem:** Services accessing database directly
258
+
259
+ **Solution:** Always use repository abstractions
260
+
261
+ ### ❌ Business Logic in Infrastructure
262
+
263
+ **Problem:** Business rules in repository implementations
264
+
265
+ **Solution:** Keep repositories focused on data access, move logic to domain/application
266
+
267
+ ## Testing Strategy
268
+
269
+ ### Domain Layer Tests
270
+
271
+ - Test business logic in isolation
272
+ - No mocking needed (pure logic)
273
+ - Fast, deterministic tests
274
+
275
+ ### Application Layer Tests
276
+
277
+ - Mock repository interfaces
278
+ - Test use case orchestration
279
+ - Verify correct repository calls
280
+
281
+ ### Infrastructure Layer Tests
282
+
283
+ - Integration tests with real dependencies
284
+ - Test data access patterns
285
+ - Verify error handling
286
+
287
+ ## Benefits of Clean Architecture
288
+
289
+ 1. **Independent of Frameworks**: Business rules don't depend on external libraries
290
+ 2. **Testable**: Business logic can be tested without UI, database, or external services
291
+ 3. **Independent of UI**: Can change UI without changing business rules
292
+ 4. **Independent of Database**: Can swap databases without changing business logic
293
+ 5. **Independent of External Services**: Business rules don't know about external APIs
294
+
295
+ ## Migration Path
296
+
297
+ ### Legacy Code
298
+
299
+ If you have existing code that doesn't follow Clean Architecture:
300
+
301
+ 1. Start with new features following the pattern
302
+ 2. Gradually extract interfaces from existing code
303
+ 3. Move business logic to domain/application
304
+ 4. Create repository interfaces for data access
305
+ 5. Refactor incrementally, don't rewrite everything
306
+
307
+ ### Red Flags
308
+
309
+ - Circular dependencies
310
+ - Business logic in infrastructure
311
+ - Direct database access in services
312
+ - Framework coupling in domain
313
+ - God classes (too many responsibilities)
314
+
315
+ ## Language-Specific Details
316
+
317
+ For language-specific implementation details (naming conventions, syntax, idioms), see your language-specific rules:
318
+
319
+ - **TypeScript**: `languages/typescript/coding-standards.md`
320
+ - **Python**: `languages/python/coding-standards.md`
321
+ - **Go**: `languages/go/coding-standards.md`
322
+ - **Java**: `languages/java/coding-standards.md`
323
+
324
+ ## Summary Checklist
325
+
326
+ - [ ] Domain layer has no dependencies
327
+ - [ ] Application layer depends on domain only
328
+ - [ ] Infrastructure implements domain interfaces
329
+ - [ ] All data access through repositories
330
+ - [ ] Dependencies injected, not instantiated
331
+ - [ ] Each class has single responsibility
332
+ - [ ] Interfaces are small and focused
333
+ - [ ] Business logic is testable in isolation