Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
af0d12c
docs(design): add design specs and update project documentation
cpplain Mar 19, 2026
96d35ec
feat(task): add task management system and refactor loop internals
cpplain Mar 19, 2026
fef002e
test(task): add Phase and Section JSON serialization tests
cpplain Mar 20, 2026
60c1886
feat(task): implement core types (task.go)
cpplain Mar 20, 2026
178693c
test(task): add storage core tests for all fields and file permissions
cpplain Mar 20, 2026
96d6442
feat(task): implement storage core (storage.go + json_storage.go)
cpplain Mar 20, 2026
1c59b74
test(task): add storage CRUD tests (already present from prior agent)
cpplain Mar 20, 2026
78940a6
feat(task): verify storage CRUD implementation passes all tests
cpplain Mar 20, 2026
b41c387
test(task): add storage list and filter tests
cpplain Mar 20, 2026
891f1d1
feat(task): verify storage list and filter implementation passes all …
cpplain Mar 20, 2026
699b486
test(task): add single-task formatter tests
cpplain Mar 20, 2026
b666f66
feat(task): verify single-task formatter implementation passes all tests
cpplain Mar 20, 2026
f9afc18
test(task): add list grouped formatter tests
cpplain Mar 20, 2026
41e5577
feat(task): verify list grouped formatter implementation passes all t…
cpplain Mar 20, 2026
1d4af66
test(task): verify list flat and JSON formatter tests pass
cpplain Mar 20, 2026
de8468c
feat(task): verify list flat and JSON formatter implementation passes…
cpplain Mar 20, 2026
572f44b
test(task): add export formatter tests for section description and em…
cpplain Mar 20, 2026
7783c1e
feat(task): implement section description rendering in export formatter
cpplain Mar 20, 2026
db342cb
test(task): add dispatch tests for --help and delete subcommand
cpplain Mar 20, 2026
1c6af94
feat(task): implement --help dispatch and delete subcommand
cpplain Mar 20, 2026
3c5dd6b
test(task): add missing list handler tests for section filter, flat+j…
cpplain Mar 20, 2026
eeec374
feat(task): verify list handler implementation passes all tests
cpplain Mar 20, 2026
eb066fe
test(task): add get handler tests for H1 subject rendering
cpplain Mar 20, 2026
a2eabe2
feat(task): verify get handler implementation passes all tests
cpplain Mar 20, 2026
00fe98a
test(task): add create handler tests for status, section, and phase c…
cpplain Mar 20, 2026
2694452
feat(task): add --section phase context validation to create handler
cpplain Mar 20, 2026
597d3fd
test(task): add create auto-generation handler tests
cpplain Mar 20, 2026
1c7d51c
feat(task): add --section-name phase context validation to create han…
cpplain Mar 20, 2026
dd70665
test(task): add update subject and subject-clear handler tests
cpplain Mar 20, 2026
627bc3a
feat(task): verify update basic fields handler passes all tests
cpplain Mar 20, 2026
7443967
test(task): add update metadata handler tests
cpplain Mar 20, 2026
fe783f0
feat(task): add validation guards to update metadata handler
cpplain Mar 20, 2026
f3cdf60
test(task): verify delete handler tests already exist and pass
cpplain Mar 20, 2026
f8357df
feat(task): verify delete handler implementation passes all tests
cpplain Mar 20, 2026
0a1e3a6
test(task): add export handler tests for repeatable status and descri…
cpplain Mar 20, 2026
3052ef1
feat(task): verify export handler implementation passes all tests
cpplain Mar 20, 2026
e99cadc
feat(task): verify wire-up implementation passes all tests
cpplain Mar 20, 2026
f0a865f
docs: update design specs and minor output fixes
cpplain Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .lorah/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# TDD Implementation Agent

You are a TDD implementation agent for the `lorah` project. Your job is to complete exactly **one task** per invocation — either write tests OR write implementation code. Never both.

IMPORTANT: overzealous agents began implementing phase 4 (task management) before we were ready. Once you begin phase 4, pay special attention to what is already written. Do NOT assume it is well written or written to spec. You will likely need to refactor tests and code to bring it in conformity with our design specs.

---

## Workflow

1. **Orient** — Run `git log --oneline -10` to understand what was done in prior iterations.

2. **Select** — Read `.lorah/tasks.md`. Find the first `[pending]` or `[in_progress]` task in document order. That is your task for this invocation.

3. **Start** — If the task is `[pending]`, change it to `[in_progress]` in `.lorah/tasks.md`.

4. **Understand** — Read the task's notes block for implementation guidance. Consult the relevant spec in `docs/design/` for detailed requirements. Read `CLAUDE.md` for project conventions.

5. **Do the work** — Exactly one of the following:
- **Test task** ("Write tests for…"): Write tests only. Do not write any production/implementation code. Add stubs or interface definitions only if required to make tests compilable.

- **Implementation task** ("Implement…"): Write production code to make the preceding tests pass. Do not write any new tests. If you discover the existing tests conflict with the spec, follow the **Blocked workflow** below instead.

- **Other task** (e.g., Makefile target, config change): Do what the task describes. No tests needed unless explicitly stated.

6. **Verify** — Run all three in order, fix any issues before proceeding:
- `make fmt`
- `make lint`
- `make test`
- Test tasks: failures are expected (no implementation yet), but panics and compilation errors must be fixed.
- Implementation tasks: all tests must pass.

7. **Update tasks.md**:
- Change the task status from `[in_progress]` to `[completed]`.
- Append notes to the completed task with anything useful for future agents (files created, patterns used, gotchas).
- If you discovered context relevant to the _next_ pending task, add it to that task's notes block.

8. **Commit** — Stage all changed files and commit with a conventional commit message (e.g., `test(loop): add loop lifecycle tests`, `feat(loop): implement loop lifecycle and signal handling`).

9. **Exit** — Stop. Do NOT proceed to the next task.

---

## Blocked workflow

If you are on an **implementation task** and discover the existing tests conflict with the design spec:

1. Discard all uncommitted changes: `git checkout .`
2. In `.lorah/tasks.md`:
- Mark the implementation task as `[blocked]` with notes explaining the spec conflict
- Mark the preceding test task back to `[in_progress]`
3. Exit — do not commit anything

The next iteration will pick up the `[in_progress]` test task and fix the tests. That agent should, when done:

- Mark the test task `[completed]`
- Change the `[blocked]` implementation task back to `[pending]`

---

## Rules

- **Strict TDD boundary**: test tasks contain ONLY test code; implementation tasks contain ONLY production code. No exceptions.
- **One task per invocation**: complete one task, commit, exit.
- **Stdlib only**: no external dependencies — Go standard library only.
- **Design docs are authoritative**: `docs/design/cli.md`, `run.md`, `output.md`, `task.md` define the target behavior.
- **If blocked for other reasons**: add a note to the task explaining why, leave it as `[pending]`, and exit without committing.

---

## Tasks.md status values

```
[pending] → not started
[in_progress] → actively being worked (current or interrupted iteration)
[completed] → done
[blocked] → implementation blocked by incorrect tests; see Blocked workflow
```
11 changes: 11 additions & 0 deletions .lorah/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"model": "sonnet",
"permissions": {
"defaultMode": "bypassPermissions"
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true
},
"includeCoAuthoredBy": false
}
961 changes: 961 additions & 0 deletions .lorah/tasks.md

Large diffs are not rendered by default.

128 changes: 24 additions & 104 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,132 +1,52 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Lorah

## Project Overview

Lorah is a simple infinite-loop harness for long-running autonomous coding agents. It runs Claude Code CLI in a continuous loop, parsing stream-JSON output and formatting it nicely. The agent manages its own workflow - Lorah just provides the loop, error recovery, and output formatting. Follows the [Ralph pattern](https://ghuntley.com/ralph/). Distributed as a single self-contained binary with no external runtime dependencies.
Lorah is a simple infinite-loop harness for long-running autonomous coding agents. It runs Claude Code CLI in a continuous loop, parsing stream-JSON output and formatting it nicely. Includes a task management system for structured agent workflow coordination. The agent manages its own workflow Lorah just provides the loop, error recovery, output formatting, and task tracking. Follows the Ralph pattern. Distributed as a single self-contained binary with no external runtime dependencies.

## Commands

```bash
# Build binary
go build -o ./bin/lorah .

# Install globally
go install .

# Run Lorah
lorah PROMPT.md [claude-flags...]
make build # Build binary
go run . run PROMPT.md # Development run
lorah run PROMPT.md [flags...] # Run loop (all flags after prompt passed to claude CLI)
lorah task <subcommand> [args...] # Task management
```

All arguments after `PROMPT.md` are passed directly to Claude CLI:

```bash
# With settings file
lorah PROMPT.md --settings .lorah/settings.json

# With specific model
lorah PROMPT.md --model claude-opus-4-6

# With multiple flags
lorah PROMPT.md --settings settings.json --max-turns 50 --verbose
```

For development:

```bash
go run . PROMPT.md [claude-flags...]
```

No tests currently - the implementation is simple enough to verify manually. No linter or formatter beyond `gofmt`.
Use TDD — write tests before implementation. Use `make fmt` and `make lint`.

## Architecture

Lorah is a single-file Go program that implements a simple infinite loop:

```
1. Execute: claude -p --output-format=stream-json --verbose [user-flags...] PROMPT.md
2. Parse stream-JSON line-by-line from stdout
3. Format output with color-coded sections and tool activity
4. On error: print error, sleep 5s, retry
5. On success: continue immediately to next iteration
6. Repeat forever until Ctrl+C
main.go CLI router: subcommand dispatch, help text, version
internal/loop/
loop.go Run() entry point, signal handling, infinite loop
claude.go Subprocess execution
output.go Stream-JSON parsing and formatted output
constants.go ANSI colors, buffer size, retry delay
internal/task/
task.go Core types: Phase, Section, Task, TaskStatus, TaskList, Filter
storage.go Storage interface
json_storage.go JSONStorage implementation (tasks.json)
format.go Output formatters: json, markdown
cmd.go CLI subcommand handlers
docs/design/ Design specifications (authoritative reference)
```

### File Structure

```
main.go Single file containing everything
- Constants ANSI color codes, retry delay
- Message Types stream-JSON message/content block structs
- Main Loop Infinite execution loop with signal handling
- Claude Exec Build command, pipe I/O, run subprocess
- Message Parsing Parse newline-delimited JSON into typed structs
- Output Format Color-coded section headers, tool activity display
go.mod Go module definition
go.sum Dependency checksums (none - stdlib only)
```

### Main Sections

**Constants** — ANSI color codes for terminal output and retry delay configuration.

**Helper Functions** — `printSection()` outputs labeled sections with color formatting.

**Main Loop** — Entry point:

- Parses CLI args: `PROMPT.md` and optional Claude CLI flags
- Handles `--version` and `--help` flags
- Sets up signal handler for graceful Ctrl+C shutdown
- Runs infinite loop calling `runClaude()` with error recovery

**Claude Execution** — `runClaude()` function:

- Opens prompt file and pipes it to stdin
- Builds `claude` command with `-p`, `--output-format=stream-json`, `--verbose`, and user flags
- Streams stdout to `printMessages()` for real-time output
- Returns error if command fails

**Output Formatting** — `printMessages()` function:

- Scans stdout line-by-line (newline-delimited JSON)
- Parses each line as `map[string]any` for flexibility
- Gracefully skips malformed/unknown message types for forward compatibility
- Displays color-coded section headers: `==> Claude`, `==> Lorah`, `==> Error`
- Shows tool activity: `==> Bash`, `==> Read`, etc. with relevant input displayed
- Extracts relevant input params (command, file_path, pattern, url, etc.) based on tool name
- Truncates long tool inputs to 3 lines for readability

## Key Patterns

- **Single file** — Entire program in one `main.go` file. No packages, no navigation overhead. Readable in one sitting.
- **No configuration** — Zero config files. Prompts are plain markdown. Claude CLI flags passed through directly.
- **Ralph pattern** — Simple infinite loop. Agent decides workflow. Harness provides loop + nice output. Inspired by [Geoffrey Huntley's Ralph](https://ghuntley.com/ralph/).
- **Agent-driven** — No phase orchestration. Agent reads codebase, decides what to do next, makes progress iteratively.
- **Filesystem as state** — No session files. Git commits show progress. Agent reads files to understand context.
- **Flag passthrough** — All args after `PROMPT.md` passed directly to `claude` CLI. User has full control.
- **CLI-native security** — Security enforced through Claude CLI `--settings` flag (sandbox, permissions). See [Claude Code Settings](https://code.claude.com/docs/en/settings).
- **Stream processing** — stdout parsed line-by-line as newline-delimited JSON; unknown types skipped gracefully for forward compatibility.
- **Simple error handling** — Fixed 5-second retry delay on errors. No exponential backoff complexity.
- **Color-coded output** — Section headers and tool activity formatted with ANSI colors for readability.

## Design Principles

**Ralph Philosophy**: The agent is smart enough to manage its own workflow. Don't orchestrate - provide a simple loop and trust the model.
**Ralph Philosophy**: The agent is smart enough to manage its own workflow. Don't orchestrate provide a simple loop and trust the model.

**Radical Simplicity**: Every line of code is overhead. The simplest solution that works is the best solution. Prefer deleting code over adding it.

**Agent is in Control**: The harness provides the loop and nice output. The agent reads the codebase, decides what to do, and makes progress. No phase management needed.

**No Ceremony**: No config files, session state, lock files, or scaffolding commands. Just a prompt file and a loop.

Required reading:
**Filesystem as State**: No session files. Git commits show progress. Agent reads files to understand context.

- [Ralph by Geoffrey Huntley](https://ghuntley.com/ralph/) - The pattern this implementation follows
- [Building Effective Agents](https://www.anthropic.com/research/building-effective-agents) - Agent design principles from Anthropic
- [Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview) - Understanding agent capabilities
- [Claude Code Sandboxing](https://www.anthropic.com/engineering/claude-code-sandboxing) - Security model
**Design Specifications**: Authoritative design docs live in `docs/design/`. When in doubt about intended behavior, consult the specs: `cli.md`, `run.md`, `output.md`, `task.md`.

## Dependencies

No external runtime dependencies. All functionality uses the Go standard library. The `claude` CLI (separate install) is the only runtime requirement for executing agent sessions.
No external runtime dependencies. All functionality uses the Go standard library. The `claude` CLI (separate install) is the only runtime requirement.
16 changes: 14 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Subcommand-based CLI: `lorah <command> [arguments]` with `run` and `task` commands
- Task management system (`lorah task`) with 8 subcommands: list, get, create, update, start, complete, export, stats
- JSON-based task storage in `tasks.json` with Storage interface for future backend swaps
- Two task output formats: json, markdown (default); `--flat` flag on `list` for flat bullet output

### Changed

- **BREAKING**: CLI changed to `lorah run <prompt-file> [claude-flags...]` (was `lorah <prompt-file> [claude-flags...]`)
- **BREAKING**: Multi-package architecture replaces single-file structure (`main.go` router + `internal/loop/` + `internal/task/`)

## [0.4.0] - 2026-03-09

### Changed
Expand All @@ -25,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Infinite loop execution following [Ralph pattern](https://ghuntley.com/ralph/)
- Direct flag passthrough to Claude CLI
- Direct flag passthrough to Claude Code CLI

### Changed

Expand All @@ -42,7 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **BREAKING**: Configuration format changed to a split structure with `harness` (lorah settings) and `claude` (passthrough to Claude CLI) sections. Existing configs require migration—run `lorah info template` to see the new format.
- **BREAKING**: Configuration format changed to a split structure with `harness` (lorah settings) and `claude` (passthrough to Claude Code CLI) sections. Existing configs require migration—run `lorah info template` to see the new format.
- Updated documentation to document two-phase execution model with fixed file names (initialization and implementation phases)
- Renamed `docs/setup-guide.md` to `docs/getting-started.md`
- Renamed review workflow prompts from `inventory.md`/`fix.md` to `initialization.md`/`implementation.md`
Expand Down
14 changes: 6 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help build clean fmt lint install
.PHONY: help build clean fmt lint test

# Default target - show help
help:
Expand All @@ -9,10 +9,10 @@ help:
@echo "Targets:"
@echo " help Show this help message"
@echo " build Build the lorah binary"
@echo " install Install lorah to GOPATH/bin"
@echo " clean Remove build artifacts"
@echo " fmt Format all Go code"
@echo " lint Run go vet for static analysis"
@echo " test Run all tests"

# Build the lorah binary
build:
Expand All @@ -21,12 +21,6 @@ build:
echo "Building lorah $$VERSION..."; \
go build -ldflags "-X 'main.Version=$$VERSION'" -o ./bin/lorah .

# Install lorah to GOPATH/bin
install:
@VERSION=$$(date -u '+dev+%Y%m%d%H%M%S'); \
echo "Installing lorah $$VERSION..."; \
go install -ldflags "-X 'main.Version=$$VERSION'" .

# Clean build artifacts
clean:
rm -rf ./bin
Expand All @@ -45,3 +39,7 @@ fmt:
lint:
@echo "Running go vet..."
go vet ./...

# Run all tests
test:
go test ./...
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ But you get raw `stream-json` output that's unreadable:
**Lorah gives you clean, color-coded output:**

```
==> Claude
Claude
Let me read the file
==> Read

⏺ Read
/path/to/file
```

Plus automatic error recovery, graceful shutdown, and full Claude CLI compatibility.
Plus automatic error recovery, graceful shutdown, and full Claude Code CLI compatibility.

**Key Features:**

- **Formatted output** - Color-coded sections and tool activity (the main reason Lorah exists)
- **Simple infinite loop** - Runs continuously until you stop it
- **Automatic error recovery** - Retries on failures with 5-second delay
- **Flag passthrough** - All Claude CLI flags work transparently
- **Flag passthrough** - All Claude Code CLI flags work transparently
- **Task management** - Structured task tracking for agent workflow coordination

## Prerequisites

Expand All @@ -49,22 +51,30 @@ brew install cpplain/tap/lorah

## Usage

Lorah is an implementatioin of the Ralph loop. You must understand the Ralph technique to use Lorah effectively.
Lorah is an implementation of the Ralph loop. You must understand the Ralph technique to use Lorah effectively.

Learn more about the Ralph technique: [Ralph Wiggum as a "software engineer"](https://ghuntley.com/ralph/) by Geoffrey Huntley

**Syntax:**

```bash
lorah <prompt-file> [claude-flags...]
lorah <command> [arguments]
```

**Run loop:**

```bash
lorah run PROMPT.md
lorah run PROMPT.md --settings .lorah/settings.json
lorah run PROMPT.md --model claude-opus-4-6 --max-turns 50
```

**Examples:**
**Task management:**

```bash
lorah PROMPT.md
lorah PROMPT.md --settings .lorah/settings.json
lorah PROMPT.md --model claude-opus-4-6 --max-turns 50
lorah task list --status=pending
lorah task create --subject="Fix auth bug" --priority=3
lorah task stats
```

## License
Expand Down
Loading