Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Local build artifacts
agent-c

build
# IDE
.idea/
135 changes: 92 additions & 43 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,74 +1,123 @@
CC = gcc
TARGET = agent-c
SOURCES = main.c json.c agent.c cli.c utils.c
RAG_ENABLED ?= 1

# Detect OS once
UNAME := $(shell uname)
# Source files with conditional RAG support
SOURCES = src/main.c src/json.c src/agent.c src/cli.c src/utils.c src/args.c
ifneq ($(RAG_ENABLED),0)
SOURCES += src/rag.c
endif

# Object files in build directory
OBJECTS = $(addprefix build/, $(notdir $(SOURCES:.c=.o)))

# Detect OS - Windows compatible
UNAME := $(shell uname -s 2>/dev/null || echo Windows)
ifeq ($(findstring MINGW,$(UNAME)),MINGW)
UNAME := Windows
endif
ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
UNAME := Windows
endif

# Optimized build flags for smallest size
CFLAGS_OPT = -std=c99 -D_POSIX_C_SOURCE=200809L -Oz -DNDEBUG \
-ffunction-sections -fdata-sections \
-fno-stack-protector -fno-unwind-tables -fno-asynchronous-unwind-tables \
-fno-math-errno -ffast-math -fmerge-all-constants -flto \
-fomit-frame-pointer -fno-ident -fno-stack-check \
-fvisibility=hidden -fno-builtin

# Linker flags per-OS (do not put -Wl flags in CFLAGS)
ifeq ($(UNAME),Darwin)
LDFLAGS_OPT = -Wl,-dead_strip -Wl,-x -Wl,-S
CFLAGS_OPT = -std=c99 -D_POSIX_C_SOURCE=200809L -DRAG_ENABLED=$(RAG_ENABLED) -Oz -DNDEBUG \
-ffunction-sections -fdata-sections \
-fno-stack-protector -fno-unwind-tables -fno-asynchronous-unwind-tables \
-fno-math-errno -ffast-math -fmerge-all-constants \
-fomit-frame-pointer -fno-ident -fno-stack-check \
-fvisibility=hidden -fno-builtin

# Linker flags per-OS
ifeq ($(UNAME),Windows)
LDFLAGS_OPT = -Wl,--gc-sections -Wl,-s
else
# GNU ld: garbage collect unused sections and keep binary small
LDFLAGS_OPT = -Wl,--gc-sections
LDFLAGS_OPT = -Wl,-x -Wl,-S -Wl,-s
endif

# Auto-detect platform and build appropriately
all:
@echo "Detecting platform..."
@if [ "$$(uname)" = "Darwin" ]; then \
echo "Building for macOS with GZEXE compression..."; \
$(MAKE) macos; \
else \
echo "Building for Linux with UPX compression..."; \
$(MAKE) linux; \
fi

# macOS build with GZEXE compression (7.9KB)
macos: $(SOURCES)
@echo "Building optimized binary for macOS..."
$(CC) $(CFLAGS_OPT) -o $(TARGET) $(SOURCES) $(LDFLAGS_OPT)
strip -S -x $(TARGET) 2>/dev/null || strip $(TARGET)
@echo "Applying GZEXE compression..."
gzexe $(TARGET)
@echo "✅ macOS build complete: $$(ls -lh $(TARGET) | awk '{print $$5}')"
# Auto-detect platform and build accordingly
all: $(OBJECTS)
@echo "Building for $(UNAME)..."
@$(MAKE) $(UNAME)

# Create build directory
build/%.o: src/%.c | build
@echo "Compiling $<..."

@$(CC) $(CFLAGS_OPT) -c $< -o $@

# Linux build with UPX compression (~16KB)
linux: $(SOURCES)
build:
@mkdir -p build

# Windows build (with UPX compression)
Windows: $(OBJECTS)
@echo "Building optimized binary for Windows..."
$(CC) $(CFLAGS_OPT) -mconsole -o $(TARGET).exe $(OBJECTS) $(LDFLAGS_OPT)
strip --strip-all $(TARGET).exe 2>/dev/null || strip $(TARGET).exe
@echo "Applying UPX compression..."
@which upx >/dev/null 2>&1 && upx --best $(TARGET).exe || echo "⚠️ UPX not found, binary uncompressed"
@echo "✅ Windows build complete: $$(ls -lh $(TARGET).exe | awk '{print $$5}')"

# Linux build (with UPX compression)
Linux: $(OBJECTS)
@echo "Building optimized binary for Linux..."
$(CC) $(CFLAGS_OPT) -o $(TARGET) $(SOURCES) $(LDFLAGS_OPT)
$(CC) $(CFLAGS_OPT) -o $(TARGET) $(OBJECTS) $(LDFLAGS_OPT)
strip --strip-all $(TARGET) 2>/dev/null || strip $(TARGET)
@echo "Applying UPX compression..."
@which upx >/dev/null 2>&1 && upx --best $(TARGET) || echo "⚠️ UPX not found, binary uncompressed"
@echo "✅ Linux build complete: $$(ls -lh $(TARGET) | awk '{print $$5}')"

# macOS build (with GZEXE compression)
Darwin: $(OBJECTS)
@echo "Building optimized binary for macOS..."
$(CC) $(CFLAGS_OPT) -o $(TARGET) $(OBJECTS) $(LDFLAGS_OPT)
strip -S -x $(TARGET) 2>/dev/null || strip $(TARGET)
@echo "Applying GZEXE compression..."
gzexe $(TARGET)
@echo "✅ macOS build complete: $$(ls -lh $(TARGET) | awk '{print $$5}')"

clean:
rm -f $(TARGET) $(TARGET)~ *~
ifeq ($(UNAME),Windows)
@echo "Cleaning Windows build files..."
@if exist build\ rmdir /s /q build 2>nul || echo "Build directory already clean"
@if exist $(TARGET).exe del /f /q $(TARGET).exe 2>nul || echo "Binary already removed"
@if exist $(TARGET).exe~ del /f /q $(TARGET).exe~ 2>nul || echo "Backup already removed"
else
@echo "Cleaning Linux/Unix build files..."
@rm -rf build/ $(TARGET) $(TARGET)~ *~ $(TARGET).exe $(TARGET).exe~ 2>/dev/null || true
endif

install: all
cp $(TARGET) /usr/local/bin/
ifeq ($(UNAME),Windows)
@echo "Installation not supported on Windows. Please manually copy $(TARGET).exe to your desired location."
else
cp $(TARGET) /usr/local/bin/
@echo "Installed to /usr/local/bin/$(TARGET)"
endif

uninstall:
rm -f /usr/local/bin/$(TARGET)
ifeq ($(UNAME),Windows)
@echo "Uninstallation not supported on Windows. Please manually remove $(TARGET).exe from your desired location."
else
rm -f /usr/local/bin/$(TARGET)
@echo "Uninstalled from /usr/local/bin/$(TARGET)"
endif

# Show help information
help:
@echo "🚀 Agent-C - A lightweight AI agent written in C"
@echo ""
@echo "📋 SIMPLE BUILD COMMANDS:"
@echo " make Auto-detects platform and builds optimally"
@echo " make macos macOS build with GZEXE compression (4.4KB)"
@echo " make linux Linux build with UPX compression (~16KB)"
@echo " make Windows Windows build with UPX compression"
@echo " make Linux Linux build with UPX compression"
@echo " make Darwin macOS build with GZEXE compression"
@echo " make clean Clean all build files"
@echo " make install Install to /usr/local/bin"
@echo " make help Show this help"
@echo ""
@echo "🔧 ADVANCED OPTIONS:"
@echo " make RAG_ENABLED=0 Build without RAG feature (smaller binary)"
@echo " make CFLAGS_OPT=\"...\" Override compiler flags"

.PHONY: all macos linux clean install uninstall help
.PHONY: all Windows Linux Darwin clean install uninstall help
116 changes: 105 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
# Agent-C

A ultra-lightweight AI agent written in C that communicates with OpenRouter API and executes shell commands.
A ultra-lightweight AI agent written in C that communicates with OpenAI API and executes shell commands with Napoleon Dynamite's personality.

![Agent-C Preview](preview.webp)
![Agent-C Preview](docs/preview.webp)

## Features

- **Tool Calling**: Execute shell commands directly through AI responses
- **Optimized Binaries**: 4.4KB on macOS (GZEXE), ~16KB on Linux (UPX)
- **Conversation Memory**: Sliding window memory management for efficient operation
- **Tool Calling**: Execute shell commands directly through AI responses with safety filtering
- **Napoleon Dynamite Personality**: AI assistant with quirky, awkwardly enthusiastic personality
- **Optimized Binaries**: ~7.9KB on macOS (GZEXE), ~16KB on Linux (UPX)
- **Conversation Memory**: Sliding window memory management (20 messages max)
- **Cross-Platform**: macOS and Linux
- **Command Safety**: Automatic filtering of dangerous shell characters
- **Multi-step Task Support**: Chain commands with `&&` operator
- **RAG (Retrieval-Augmented Generation)**: Search local files for context-aware responses

## Quick Start

### Prerequisites

- GCC compiler
- curl command-line tool
- OpenRouter API key
- OpenAI API key
- macOS: gzexe (usually pre-installed)
- Linux: upx (optional, for compression)

Expand All @@ -28,15 +32,20 @@ make
```

The build system auto-detects your platform and applies optimal compression:
- **macOS**: Uses GZEXE compression → 4.4KB binary
- **macOS**: Uses GZEXE compression → ~7.9KB binary
- **Linux**: Uses UPX compression → ~16KB binary

### Setup

Set your OpenRouter API key:
Set your OpenAI API key and configuration:

```bash
export OR_KEY=your_openrouter_api_key_here
export OPENAI_KEY=your_openai_api_key_here
export OPENAI_BASE=https://api.openai.com # Optional, defaults to OpenAI
export OPENAI_MODEL=gpt-3.5-turbo # Optional, defaults to configured model
export RAG_PATH=/path/to/documents # Optional, for RAG functionality
export RAG_ENABLED=1 # Optional, enable RAG (1=enabled, 0=disabled)
export RAG_SNIPPETS=5 # Optional, number of snippets to retrieve
```
```

### Run
Expand All @@ -45,6 +54,91 @@ export OR_KEY=your_openrouter_api_key_here
./agent-c
```

### Run with RAG

```bash
# Enable RAG with a specific path
./agent-c --rag /path/to/documents

# Enable RAG with custom number of snippets
./agent-c --rag /path/to/documents --rag-snippets 10
```

## Configuration

The agent uses the following environment variables:

- `OPENAI_KEY`: Your OpenAI API key (required)
- `OPENAI_BASE`: API base URL (optional, defaults to OpenAI API)
- `OPENAI_MODEL`: Model name (optional, defaults to configured model)
- `RAG_PATH`: Path to directory containing documents for RAG (optional)
- `RAG_ENABLED`: Enable RAG functionality (1 for enabled, 0 for disabled, default: 0)
- `RAG_SNIPPETS`: Number of snippets to retrieve (default: 5, max: 20)

## Usage Examples

Start the agent and interact with it:

```
Agent> Hello
Gosh! Hello there! Sweet!
```

The agent can execute shell commands:

```
Agent> Create a file called test.txt and write "Hello World" to it
$ echo "Hello World" > test.txt
Command output:

```

Multi-step tasks are supported:

```
Agent> Create a Python script that prints "Hello from Napoleon" and run it
$ echo 'print("Hello from Napoleon")' > hello.py && python3 hello.py
Command output:
Hello from Napoleon
```

## RAG (Retrieval-Augmented Generation)

Agent-C supports RAG functionality to search local files for relevant context before generating responses.

### How RAG Works

1. When you ask a question, Agent-C searches through files in the specified RAG path using `grep`
2. It finds relevant snippets containing keywords from your query
3. These snippets are included in the context sent to the AI model
4. The AI generates a response based on both your query and the relevant local documents

### Supported File Types

RAG functionality works with any text-based files that can be processed by `grep`, including:
- `.txt`, `.md`, `.c`, `.h`, `.py`, `.js`, `.html`, `.css`, `.json`, `.xml`, `.yaml`, `.yml`
- Any other plain text files

### Example Usage with RAG

```bash
# With RAG enabled
> What are the main features of this project?

# Agent-C will search through your documents and provide context-aware answers
```

## Architecture

- **main.c**: Entry point with signal handling and configuration loading
- **agent.c**: Core agent logic, command execution, and personality handling
- **cli.c**: Command-line interface with colored prompts
- **json.c**: JSON parsing and request/response handling
- **utils.c**: HTTP requests and configuration management
- **rag.c**: RAG functionality for local file search
- **args.c**: Command-line argument parsing
- **agent-c.h**: Header with data structures and function declarations

## License

**CC0 - "No Rights Reserved"**
**CC0 - "No Rights Reserved"**
Binary file added agent-c.exe
Binary file not shown.
Loading