Skip to content

Commit c592a4a

Browse files
committed
build: add Makefile with build, test, lint, clean, and install targets
Signed-off-by: leocavalcante <[email protected]>
1 parent 06a53af commit c592a4a

File tree

3 files changed

+305
-9
lines changed

3 files changed

+305
-9
lines changed

AGENTS.md

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# AGENTS.md - Opencoder Development Guide
2+
3+
This file provides instructions for AI coding agents working in this repository.
4+
5+
## Project Overview
6+
7+
Opencoder is a native CLI application written in **Zig** that runs OpenCode CLI in a fully autonomous development loop. It creates plans and executes them continuously without stopping.
8+
9+
- **Language**: Zig (0.14.0+ required, 0.15.2 used in CI)
10+
- **Dependencies**: Zig standard library only (no external dependencies)
11+
- **Build System**: Zig build system (`build.zig`)
12+
13+
## Build Commands
14+
15+
### Using Make (Recommended)
16+
17+
```bash
18+
make # Build release version
19+
make test # Run all tests
20+
make lint # Format and check code
21+
make clean # Remove build artifacts
22+
make install # Install to /usr/local/bin (PREFIX configurable)
23+
```
24+
25+
### Using Zig Directly
26+
27+
```bash
28+
# Build debug version
29+
zig build
30+
31+
# Build release version
32+
zig build -Doptimize=ReleaseSafe
33+
34+
# Run the application
35+
zig build run
36+
37+
# Run with arguments
38+
zig build run -- --provider github --verbose
39+
```
40+
41+
## Testing
42+
43+
```bash
44+
# Run all tests
45+
zig build test
46+
47+
# Run tests for a specific module
48+
zig test src/config.zig
49+
zig test src/cli.zig
50+
zig test src/logger.zig
51+
zig test src/fs.zig
52+
zig test src/state.zig
53+
zig test src/plan.zig
54+
zig test src/executor.zig
55+
zig test src/evaluator.zig
56+
zig test src/loop.zig
57+
```
58+
59+
**Note**: Zig does not support running individual tests by name. Tests are per-file.
60+
61+
## Linting and Formatting
62+
63+
```bash
64+
# Check formatting (used in CI)
65+
zig fmt --check src/
66+
67+
# Auto-format code
68+
zig fmt src/
69+
70+
# Format a specific file
71+
zig fmt src/config.zig
72+
```
73+
74+
## Source Code Structure
75+
76+
```
77+
src/
78+
main.zig # Entry point, CLI orchestration
79+
cli.zig # CLI argument parsing, help/usage text
80+
config.zig # Configuration, provider presets, env vars
81+
state.zig # Execution state persistence (JSON)
82+
fs.zig # File system utilities
83+
logger.zig # Logging infrastructure
84+
plan.zig # Plan parsing, validation, markdown handling
85+
executor.zig # OpenCode CLI process execution
86+
evaluator.zig # Plan completion evaluation
87+
loop.zig # Main autonomous execution loop
88+
```
89+
90+
## Code Style Guidelines
91+
92+
### Imports
93+
94+
1. Standard library import always first: `const std = @import("std");`
95+
2. Extract commonly used type aliases after std import
96+
3. Internal module imports follow, grouped logically
97+
98+
```zig
99+
const std = @import("std");
100+
const Allocator = std.mem.Allocator;
101+
102+
const config = @import("config.zig");
103+
const Logger = @import("logger.zig").Logger;
104+
```
105+
106+
### Naming Conventions
107+
108+
| Element | Convention | Example |
109+
|---------|------------|---------|
110+
| Files | snake_case | `config.zig`, `fs.zig` |
111+
| Types/Structs | PascalCase | `Logger`, `State`, `ExecutionResult` |
112+
| Functions | camelCase | `runPlanning`, `markTaskComplete` |
113+
| Constants | snake_case | `version`, `defaults` |
114+
| Module variables | snake_case | `shutdown_requested` |
115+
116+
### Struct Patterns
117+
118+
```zig
119+
pub const MyStruct = struct {
120+
field: Type,
121+
allocator: Allocator,
122+
123+
/// Initialize - returns struct value
124+
pub fn init(allocator: Allocator) MyStruct {
125+
return MyStruct{
126+
.field = value,
127+
.allocator = allocator,
128+
};
129+
}
130+
131+
/// Deinit - takes pointer for cleanup
132+
pub fn deinit(self: *MyStruct) void {
133+
// cleanup
134+
}
135+
136+
/// Methods that mutate take pointer
137+
pub fn mutate(self: *MyStruct) void {
138+
self.field = new_value;
139+
}
140+
141+
/// Read-only methods take value
142+
pub fn getValue(self: MyStruct) Type {
143+
return self.field;
144+
}
145+
};
146+
```
147+
148+
### Error Handling
149+
150+
- Define custom error sets as enums: `pub const ParseError = error{ InvalidArg, MissingValue };`
151+
- Use `try` for error propagation
152+
- Use `catch` with labeled blocks for explicit handling
153+
- Use `errdefer` for cleanup on error paths
154+
155+
```zig
156+
const result = fsutil.readFile(path, allocator) catch |err| {
157+
if (err == error.FileNotFound) {
158+
return null;
159+
}
160+
return err;
161+
};
162+
```
163+
164+
### Memory Management
165+
166+
- Functions accept `Allocator` parameter when allocation is needed
167+
- Use `defer` for cleanup: `defer allocator.free(content);`
168+
- Use `errdefer` for cleanup that should only run on error
169+
170+
```zig
171+
pub fn process(allocator: Allocator) ![]u8 {
172+
const data = try allocator.alloc(u8, 1024);
173+
errdefer allocator.free(data); // Only frees if error occurs
174+
175+
// ... work with data ...
176+
177+
return data; // Caller owns memory
178+
}
179+
```
180+
181+
### Documentation
182+
183+
- File-level doc comments: `//!` at top of file
184+
- Public API doc comments: `///` before declarations
185+
- Inline comments: `//`
186+
187+
```zig
188+
//! Module description goes here.
189+
//! Additional context about the module.
190+
191+
/// Describe what this function does.
192+
/// Explain parameters and return value.
193+
pub fn myFunction() void {}
194+
```
195+
196+
### Testing
197+
198+
Tests go at the bottom of each file, separated by a comment block:
199+
200+
```zig
201+
// ============================================================================
202+
// Tests
203+
// ============================================================================
204+
205+
test "descriptive test name" {
206+
const allocator = std.testing.allocator;
207+
208+
// Setup
209+
const result = try myFunction(allocator);
210+
defer allocator.free(result);
211+
212+
// Assertions
213+
try std.testing.expectEqual(expected, result);
214+
try std.testing.expectEqualStrings("expected", actual);
215+
try std.testing.expect(condition);
216+
try std.testing.expectError(error.Expected, errorFn());
217+
}
218+
```
219+
220+
### String Handling
221+
222+
- Multi-line strings use `\\` syntax
223+
- Buffer formatting with `std.fmt.bufPrint`
224+
- Dynamic strings with `std.ArrayListUnmanaged(u8)`
225+
226+
```zig
227+
// Fixed buffer formatting
228+
var buf: [64]u8 = undefined;
229+
const msg = std.fmt.bufPrint(&buf, "Value: {d}", .{value}) catch "fallback";
230+
231+
// Dynamic string building
232+
var list = std.ArrayListUnmanaged(u8){};
233+
defer list.deinit(allocator);
234+
try list.appendSlice(allocator, "hello");
235+
```
236+
237+
### Enum Patterns
238+
239+
Use `StaticStringMap` for string-to-enum conversion:
240+
241+
```zig
242+
pub const Phase = enum {
243+
planning,
244+
execution,
245+
246+
pub fn fromString(str: []const u8) ?Phase {
247+
const map = std.StaticStringMap(Phase).initComptime(.{
248+
.{ "planning", .planning },
249+
.{ "execution", .execution },
250+
});
251+
return map.get(str);
252+
}
253+
254+
pub fn toString(self: Phase) []const u8 {
255+
return switch (self) {
256+
.planning => "planning",
257+
.execution => "execution",
258+
};
259+
}
260+
};
261+
```
262+
263+
## CI Pipeline
264+
265+
The GitHub Actions CI (`.github/workflows/ci.yml`) runs:
266+
1. Build on Ubuntu and macOS
267+
2. Run all unit tests
268+
3. Check code formatting with `zig fmt --check src/`
269+
270+
Always ensure `zig fmt --check src/` passes before committing.

Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
PREFIX ?= /usr/local
2+
3+
.PHONY: all test lint clean install
4+
5+
all:
6+
zig build -Doptimize=ReleaseSafe
7+
8+
test:
9+
zig build test
10+
11+
lint:
12+
zig fmt src/
13+
zig fmt --check src/
14+
15+
clean:
16+
rm -rf zig-out .zig-cache
17+
18+
install: all
19+
install -d $(PREFIX)/bin
20+
install -m 755 zig-out/bin/opencoder $(PREFIX)/bin/

README.md

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,24 @@ Using bcrypt for password hashing, JWT for tokens.
189189

190190
## Development
191191

192-
```bash
193-
# Build debug version
194-
zig build
192+
Using Make (recommended):
195193

196-
# Run tests
197-
zig build test
194+
```bash
195+
make # Build release version
196+
make test # Run tests
197+
make lint # Format and check code
198+
make clean # Remove build artifacts
199+
make install # Install to /usr/local/bin
200+
make install PREFIX=~/.local # Install to custom location
201+
```
198202

199-
# Check formatting
200-
zig fmt --check src/
203+
Or using Zig directly:
201204

202-
# Build release
203-
zig build -Doptimize=ReleaseSafe
205+
```bash
206+
zig build # Build debug version
207+
zig build -Doptimize=ReleaseSafe # Build release
208+
zig build test # Run tests
209+
zig fmt --check src/ # Check formatting
204210
```
205211

206212
## License

0 commit comments

Comments
 (0)