Skip to content

Commit 7c26073

Browse files
committed
feat: Add CLAUDE.md and updated the README.md
1 parent b7f97c9 commit 7c26073

File tree

2 files changed

+513
-41
lines changed

2 files changed

+513
-41
lines changed

CLAUDE.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Codify is a configuration-as-code CLI tool that brings Infrastructure-as-Code principles to local development environments. It allows developers to declaratively define their development setup (packages, tools, system settings) in configuration files and apply them in a reproducible way. Think "Terraform for your local machine."
8+
9+
## Development Commands
10+
11+
### Building
12+
```bash
13+
npm run build # Build TypeScript to dist/
14+
npm run lint # Type-check with tsc
15+
```
16+
17+
### Testing
18+
```bash
19+
npm test # Run all tests with Vitest
20+
npm test -- path/to/test # Run specific test file
21+
npm run posttest # Runs lint after tests
22+
```
23+
24+
### Running Locally
25+
```bash
26+
./bin/dev.js <command> # Run CLI in development mode
27+
./bin/dev.js apply # Example: run apply command
28+
```
29+
30+
### Test Command (VM Testing)
31+
The `test` command spins up a Tart VM to test Codify configs in isolation:
32+
```bash
33+
./bin/dev.js test --vm-os darwin # Test on macOS VM
34+
./bin/dev.js test --vm-os linux # Test on Linux VM
35+
```
36+
37+
## High-Level Architecture
38+
39+
### Core Architectural Patterns
40+
41+
1. **Command-Orchestrator Pattern**: Commands (`src/commands/`) are thin oclif wrappers. Orchestrators (`src/orchestrators/`) contain all business logic and workflow coordination. This separation enables reusability.
42+
43+
2. **Multi-Process Plugin System**: The most unique architectural decision is running plugins as separate Node.js child processes communicating via IPC:
44+
- **Why**: Isolation (crashes don't crash CLI), security (parent controls sudo), flexibility
45+
- **Plugin Process** (`src/plugins/plugin-process.ts`): Spawns plugins using `fork()`
46+
- **IPC Protocol** (`src/plugins/plugin-message.ts`): Type-safe message passing
47+
- **Security**: Plugins run isolated; parent process controls all sudo operations
48+
- When plugins need sudo, they send `COMMAND_REQUEST` events back to parent
49+
50+
3. **Event-Driven Architecture**: Central event bus (`src/events/context.ts`) using EventEmitter:
51+
- Tracks process/subprocess lifecycle (PLAN, APPLY, INITIALIZE_PLUGINS, etc.)
52+
- Enables plugin-to-CLI communication (sudo prompts, login credentials, etc.)
53+
- Powers progress tracking for UI
54+
55+
4. **Reporter Pattern**: Abstract `Reporter` interface with multiple implementations selected via `--output` flag:
56+
- `DefaultReporter`: Rich Ink-based TUI with React components
57+
- `PlainReporter`: Simple text output
58+
- `JsonReporter`: Machine-readable JSON
59+
- `DebugReporter`: Verbose logging
60+
- `StubReporter`: No-op for testing
61+
62+
5. **Resource Lifecycle State Machine**:
63+
```
64+
Parse Config → Validate → Resolve Dependencies → Plan → Apply
65+
```
66+
- **ResourceConfig**: Desired state from config file
67+
- **Plan**: Computed difference between desired and current state
68+
- **ResourcePlan**: Per-resource operations (CREATE, UPDATE, DELETE, NOOP)
69+
- **Project**: Container with dependency graph
70+
71+
6. **Dependency Resolution**:
72+
- Explicit: `dependsOn` field in config
73+
- Implicit: Extracted from parameter references (e.g., `${other-resource.param}`)
74+
- Plugin-level: Plugins declare type dependencies (e.g., xcode-tools on macOS)
75+
- Topological sort ensures correct evaluation order (`src/utils/dependency-graph-resolver.ts`)
76+
77+
### Key Directory Structure
78+
79+
- **`/src/orchestrators/`**: Business logic layer - each file implements one CLI command's workflow
80+
- `plan.ts`: Parse → Validate → Resolve deps → Generate plan
81+
- `apply.ts`: Execute plan after user confirmation
82+
- `import.ts`: Import existing resources into config
83+
- `test.ts`: VM-based testing with live config sync via file watcher
84+
85+
- **`/src/plugins/`**: Plugin infrastructure
86+
- `plugin-manager.ts`: Registry routing operations to plugins
87+
- `plugin-process.ts`: Child process lifecycle and IPC
88+
- `plugin.ts`: High-level plugin API
89+
90+
- **`/src/entities/`**: Domain models with rich behavior
91+
- `Project`: Container with dependency resolution
92+
- `ResourceConfig`: Mutable config with dependency tracking
93+
- `Plan`: Immutable plan with sorting/filtering
94+
95+
- **`/src/parser/`**: Multi-format config parsing (JSON, JSONC, JSON5, YAML)
96+
- All parsers maintain source maps for error messages
97+
- Cloud parser fetches from Dashboard API via UUID
98+
99+
- **`/src/ui/`**: User interface layer
100+
- `/reporters/`: Output strategy implementations
101+
- `/components/`: React components for Ink TUI
102+
- `/store/`: Jotai state management for UI
103+
104+
- **`/src/connect/`**: Dashboard integration
105+
- WebSocket server for persistent connection
106+
- OAuth flow handling
107+
- JWT credential management
108+
109+
- **`/src/generators/`**: Config file writers
110+
- Computes diffs for updating existing configs
111+
- Writes to local files or cloud (via Dashboard API)
112+
113+
### Important Data Flows
114+
115+
**Apply Command Flow:**
116+
```
117+
ApplyOrchestrator.run()
118+
→ PlanOrchestrator.run()
119+
→ PluginInitOrchestrator.run()
120+
→ Parse configs → Project
121+
→ PluginManager.initialize() → ResourceDefinitions
122+
→ Project.resolveDependencies()
123+
→ PluginManager.plan() → Plan
124+
→ Reporter.promptConfirmation()
125+
→ PluginManager.apply()
126+
→ For each resource (topologically sorted):
127+
→ Plugin.apply() [IPC to child process]
128+
```
129+
130+
**Plugin Communication Flow:**
131+
```
132+
Parent Process Plugin Process
133+
|-- initialize() -------->|
134+
|<-- resourceDefinitions -|
135+
|-- plan(resource) ------>|
136+
| [Plugin needs sudo]
137+
|<-- COMMAND_REQUEST -----|
138+
|-- prompt user |
139+
|-- COMMAND_GRANTED ----->|
140+
|<-- PlanResponse --------|
141+
```
142+
143+
### Key Architectural Decisions
144+
145+
1. **Single file Projects**: Projects only currently support one file
146+
2. **Cloud-First**: UUIDs are valid "file paths" - enables seamless local/cloud switching
147+
3. **XCode Tools Injection**: On macOS, `xcode-tools` automatically prepended (most resources depend on it)
148+
4. **Test VM Strategy**: Uses Tart VMs with bind mounts (not copying) + file watcher for live config editing
149+
5. **OS Filtering**: Resources specify `os: ["Darwin", "Linux"]` for conditional inclusion
150+
6. **Secure Mode**: `--secure` flag forces sudo prompt for every command (no password caching)
151+
152+
### Common Implementation Patterns
153+
154+
1. **Plugin Resolution**: Local plugins use file paths (`.ts`/`.js`), network plugins use semver versions
155+
2. **Source Maps**: Preserved through entire parse → validate → plan flow for accurate error messages
156+
3. **Event Timing**: Events fire synchronously; use `ctx.once()` carefully to avoid race conditions
157+
4. **Process Cleanup**: Plugins must be killed on exit via `registerKillListeners`
158+
5. **Reporter Lifecycle**: Call `reporter.hide()` before synchronous output to prevent UI corruption
159+
160+
### Testing Patterns
161+
162+
- **Ink Component Tests**: Must polyfill `console.Console` for test environment:
163+
```typescript
164+
import { Console } from 'node:console';
165+
if (!console.Console) {
166+
console.Console = Console;
167+
}
168+
```
169+
- **Plugin Tests**: Use `StubReporter` to avoid UI initialization
170+
- **VM Tests**: `test` command uses Tart VMs with bind mounts for integration testing
171+
172+
## Build & Distribution
173+
174+
- **Framework**: oclif CLI framework with manifest generation
175+
- **Module System**: ES modules with NodeNext resolution
176+
- **Packaging**: `oclif pack tarballs` for multi-platform binaries
177+
- **Updates**: Self-updating via S3 (`@oclif/plugin-update`)
178+
- **Code Signing**: macOS notarization via `scripts/notarize.sh`
179+
180+
## Common Gotchas
181+
182+
1. **Import Paths**: Use `.js` extensions in imports even though files are `.ts` (ES module resolution)
183+
2. **Schema Validation**: Config changes require updating schemas in `@codifycli/schemas` package
184+
3. **Plugin IPC**: Plugins cannot directly read stdin (security isolation)
185+
4. **Sudo Caching**: Password cached in memory during session unless `--secure` flag used
186+
5. **File Watcher**: Use `persistent: false` option to prevent hanging processes
187+
6. **Linting**: ESLint enforces single quotes, specific import ordering, and strict type safety

0 commit comments

Comments
 (0)