ferix-code 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @ferix/cli-v2
2
+
3
+ Composable RALPH loops for AI coding agents, built with Effect.
4
+
5
+ ## Architecture
6
+
7
+ This CLI implements the RALPH (Review, Analyze, Learn, Plan, Help) loop pattern using Effect for type-safe, composable, and testable code.
8
+
9
+ ### Directory Structure
10
+
11
+ ```
12
+ src/
13
+ ├── consumers/ # Event consumers (output handlers)
14
+ │ ├── headless/ # Simple console output
15
+ │ └── tui/ # Full-screen terminal UI
16
+ │ ├── input/ # Keyboard/mouse input handling
17
+ │ ├── output/ # ANSI terminal output
18
+ │ ├── reducers/ # State reducers for events
19
+ │ ├── render/ # UI rendering components
20
+ │ └── tools/ # Tool formatting utilities
21
+
22
+ ├── domain/ # Core domain types and schemas
23
+ │ └── schemas/ # Effect.Schema definitions
24
+ │ ├── config.ts # Loop configuration
25
+ │ ├── events.ts # Domain events
26
+ │ ├── llm.ts # LLM event types
27
+ │ ├── logger.ts # Logger types
28
+ │ ├── plan.ts # Plan/task schemas
29
+ │ ├── program.ts # Program options
30
+ │ ├── session.ts # Session state
31
+ │ ├── signals.ts # Signal types
32
+ │ ├── shared.ts # Shared utilities
33
+ │ └── tui.ts # TUI state types
34
+
35
+ ├── layers/ # Effect Layer implementations
36
+ │ ├── llm/ # LLM providers (Claude CLI, Mock)
37
+ │ ├── logger/ # Logger implementations
38
+ │ ├── plan/ # Plan storage
39
+ │ ├── session/ # Session storage
40
+ │ └── signal/ # Signal parsers
41
+
42
+ ├── orchestrator/ # Loop orchestration
43
+ │ └── mapping/ # Event mapping (LLM → Domain)
44
+
45
+ └── services/ # Effect Service definitions
46
+ ```
47
+
48
+ ## Effect Best Practices
49
+
50
+ ### Schema-First Design
51
+
52
+ All data types are defined using `Effect.Schema` for runtime validation and type inference:
53
+
54
+ ```typescript
55
+ import { Schema as S } from "effect";
56
+
57
+ export const TaskStatusSchema = S.Literal("pending", "in_progress", "done", "failed");
58
+ export type TaskStatus = typeof TaskStatusSchema.Type;
59
+
60
+ export const TaskSchema = S.Struct({
61
+ id: S.String,
62
+ title: S.String,
63
+ status: TaskStatusSchema,
64
+ });
65
+ export type Task = typeof TaskSchema.Type;
66
+ ```
67
+
68
+ ### Service Pattern
69
+
70
+ Services are defined as interfaces with Effect Context tags:
71
+
72
+ ```typescript
73
+ import { Context, Effect } from "effect";
74
+
75
+ export interface LoggerService {
76
+ readonly info: (message: string) => Effect.Effect<void>;
77
+ readonly error: (message: string) => Effect.Effect<void>;
78
+ }
79
+
80
+ export class Logger extends Context.Tag("@ferix/Logger")<Logger, LoggerService>() {}
81
+ ```
82
+
83
+ ### Layer Composition
84
+
85
+ Implementations are provided via Layers for dependency injection:
86
+
87
+ ```typescript
88
+ import { Effect, Layer } from "effect";
89
+
90
+ export const ConsoleLoggerLive = Layer.succeed(Logger, {
91
+ info: (msg) => Effect.sync(() => console.log(msg)),
92
+ error: (msg) => Effect.sync(() => console.error(msg)),
93
+ });
94
+ ```
95
+
96
+ ### Tagged Unions for Events
97
+
98
+ Domain events use discriminated unions with `_tag`:
99
+
100
+ ```typescript
101
+ export const TextEventSchema = S.TaggedStruct("Text", {
102
+ text: S.String,
103
+ });
104
+
105
+ export const DoneEventSchema = S.TaggedStruct("Done", {
106
+ output: S.String,
107
+ });
108
+
109
+ export const LLMEventSchema = S.Union(TextEventSchema, DoneEventSchema);
110
+ ```
111
+
112
+ ### Stream-Based Processing
113
+
114
+ LLM output is processed as Effect Streams for backpressure and composability:
115
+
116
+ ```typescript
117
+ const events: Stream.Stream<LLMEvent, LLMError> = llm.execute(prompt);
118
+
119
+ const domainEvents = events.pipe(
120
+ Stream.mapConcat(mapLLMEventToDomain),
121
+ Stream.tap((event) => logger.info(`Event: ${event._tag}`))
122
+ );
123
+ ```
124
+
125
+ ### Error Handling
126
+
127
+ Errors are typed and handled explicitly:
128
+
129
+ ```typescript
130
+ import { Data } from "effect";
131
+
132
+ export class LLMError extends Data.TaggedError("LLMError")<{
133
+ readonly message: string;
134
+ readonly cause?: unknown;
135
+ }> {}
136
+ ```
137
+
138
+ ## Key Patterns
139
+
140
+ ### Registry Pattern
141
+
142
+ Extensible handlers use a registry pattern:
143
+
144
+ ```typescript
145
+ // Define registry
146
+ const registry = new Map<string, Handler>();
147
+
148
+ // Register handlers
149
+ registry.set("Text", handleText);
150
+ registry.set("Done", handleDone);
151
+
152
+ // Use registry
153
+ const handler = registry.get(event._tag);
154
+ ```
155
+
156
+ ### Reducer Pattern (TUI)
157
+
158
+ TUI state updates use pure reducer functions:
159
+
160
+ ```typescript
161
+ function reduce(state: TUIState, event: DomainEvent): TUIState {
162
+ return stateReducerRegistry.reduce(state, event);
163
+ }
164
+ ```
165
+
166
+ ### Direct Schema Imports
167
+
168
+ Types are imported directly from schema files (no re-exports):
169
+
170
+ ```typescript
171
+ // Good - direct import
172
+ import type { TUIState } from "../domain/schemas/tui.js";
173
+
174
+ // Avoid - re-exports
175
+ import type { TUIState } from "../state.js"; // re-exporting from schema
176
+ ```
177
+
178
+ ## Usage
179
+
180
+ ```typescript
181
+ import { Effect } from "effect";
182
+ import { run } from "@ferix/cli-v2";
183
+
184
+ await run({
185
+ config: {
186
+ task: "Implement the feature",
187
+ maxIterations: 3,
188
+ verifyCommands: ["bun test"],
189
+ },
190
+ consumer: "tui",
191
+ }).pipe(Effect.runPromise);
192
+ ```
193
+
194
+ ## Testing
195
+
196
+ Mock layers enable isolated testing:
197
+
198
+ ```typescript
199
+ import { Mock } from "./layers/llm/mock.js";
200
+
201
+ const mockEvents: LLMEvent[] = [
202
+ { _tag: "Text", text: "Working..." },
203
+ { _tag: "Done", output: "Complete" },
204
+ ];
205
+
206
+ const testLayer = Mock.layer({ events: mockEvents });
207
+ ```
208
+
209
+ ## Commands
210
+
211
+ ```bash
212
+ bun run build # Build the CLI
213
+ bun run dev # Watch mode
214
+ bun run test # Run tests
215
+ bun run check-types # Type check
216
+ ```