Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Reference directory - ignore all reference projects
reference/
69 changes: 13 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,30 @@
# bub
# bub - Bub it. Build it.

[![Release](https://img.shields.io/github/v/release/psiace/bub)](https://img.shields.io/github/v/release/psiace/bub)
[![Build status](https://img.shields.io/github/actions/workflow/status/psiace/bub/main.yml?branch=main)](https://github.com/psiace/bub/actions/workflows/main.yml?query=branch%3Amain)
[![codecov](https://codecov.io/gh/psiace/bub/branch/main/graph/badge.svg)](https://codecov.io/gh/psiace/bub)
[![Commit activity](https://img.shields.io/github/commit-activity/m/psiace/bub)](https://img.shields.io/github/commit-activity/m/psiace/bub)
[![License](https://img.shields.io/github/license/psiace/bub)](https://img.shields.io/github/license/psiace/bub)

Bub it up! Your partner in every step of the build.
Bub is a CLI tool that helps you build things, with AI.

- **Github repository**: <https://github.com/psiace/bub/>
- **Documentation** <https://bub.build/>

## Getting started with your project

### 1. Create a New Repository

First, create a repository on GitHub with the same name as this project, and then run the following commands:

```bash
git init -b main
git add .
git commit -m "init commit"
git remote add origin git@github.com:psiace/bub.git
git push -u origin main
```

### 2. Set Up Your Development Environment

Then, install the environment and the pre-commit hooks with

```bash
make install
```

This will also generate your `uv.lock` file

### 3. Run the pre-commit hooks

Initially, the CI/CD pipeline might be failing due to formatting issues. To resolve those run:
## Quick Start

```bash
uv run pre-commit run -a
```
# Set up your API key
export BUB_API_KEY="your-api-key-here"

### 4. Commit the changes
# Start interactive chat
bub chat

Lastly, commit the changes made by the two steps above to your repository.
# Run a single command
bub run "Create a hello world script"

```bash
git add .
git commit -m 'Fix formatting issues'
git push origin main
# Get help
bub --help
```

You are now ready to start development on your project!
The CI/CD pipeline will be triggered when you open a pull request, merge to main, or when you create a new release.

To finalize the set-up for publishing to PyPI, see [here](https://fpgmaas.github.io/cookiecutter-uv/features/publishing/#set-up-for-pypi).
For activating the automatic documentation with MkDocs, see [here](https://fpgmaas.github.io/cookiecutter-uv/features/mkdocs/#enabling-the-documentation-on-github).
To enable the code coverage reports, see [here](https://fpgmaas.github.io/cookiecutter-uv/features/codecov/).

## Releasing a new version

- Create an API Token on [PyPI](https://pypi.org/).
- Add the API Token to your projects secrets with the name `PYPI_TOKEN` by visiting [this page](https://github.com/psiace/bub/settings/secrets/actions/new).
- Create a [new release](https://github.com/psiace/bub/releases/new) on Github.
- Create a new tag in the form `*.*.*`.

For more details, see [here](https://fpgmaas.github.io/cookiecutter-uv/features/cicd/#how-to-trigger-a-release).
- **Github repository**: <https://github.com/psiace/bub/>
- **Documentation** <https://bub.build/>

---

Expand Down
Binary file added docs/assets/images/baby-bub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 21 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
# bub
# bub - Bub it. Build it.

[![Release](https://img.shields.io/github/v/release/psiace/bub)](https://img.shields.io/github/v/release/psiace/bub)
[![Build status](https://img.shields.io/github/actions/workflow/status/psiace/bub/main.yml?branch=main)](https://github.com/psiace/bub/actions/workflows/main.yml?query=branch%3Amain)
[![Commit activity](https://img.shields.io/github/commit-activity/m/psiace/bub)](https://img.shields.io/github/commit-activity/m/psiace/bub)
[![License](https://img.shields.io/github/license/psiace/bub)](https://img.shields.io/github/license/psiace/bub)

Bub it up! Your partner in every step of the build.
Bub is a CLI tool that helps you build things, with AI.

## Quick Start

```bash
# Set up your API key
export BUB_API_KEY="your-api-key-here"

# Start interactive chat
bub chat

# Run a single command
bub run "Create a hello world script"

# Get help
bub --help
```

- **Github repository**: <https://github.com/psiace/bub/>
- **Documentation** <https://bub.build/>
1 change: 0 additions & 1 deletion docs/modules.md

This file was deleted.

114 changes: 114 additions & 0 deletions docs/posts/2025-07-16-baby-bub-bootstrap-milestone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: "Baby Bub: From Inspiration to Bootstrap Milestone"
date: 2025-07-16
description: "How Bub draws from modern agent design, and why fixing a single mypy issue is a meaningful step toward self-improving AI."
---

# Baby Bub: From Inspiration to Bootstrap Milestone

## Genesis: Inspiration from Modern Agents

Bub is a CLI-first AI agent, built to "Bub it. Build it." The project draws direct inspiration from [How to Build an Agent](https://ampcode.com/how-to-build-an-agent) and [Tiny Agents: Building LLM-Powered Agents from Scratch](https://huggingface.co/blog/tiny-agents). Both resources distill the essence of tool-using, loop-based, composable, and extensible agents.

![Baby Bub](../assets/images/baby-bub.png)

But Bub is also a response to the new wave of self-improving, self-hosting agents: think Claude Code, SWE-agent, and the broader "self-bootstrapping" movement. The goal: an agent that can not only help you build, but can help build (and fix) itself.

## Architecture: ReAct Loop, Tools, and CLI

### The ReAct Loop

At the heart of Bub is a classic ReAct loop, implemented in [`src/bub/agent/core.py`](https://github.com/PsiACE/bub/blob/19c015/src/bub/agent/core.py):

```python
class Agent:
...
def chat(self, message: str, on_step: Optional[Callable[[str, str], None]] = None) -> str:
self.conversation_history.append(Message(role="user", content=message))
while True:
...
response = litellm.completion(...)
assistant_message = str(response.choices[0].message.content)
self.conversation_history.append(Message(role="assistant", content=assistant_message))
...
tool_calls = self.tool_executor.extract_tool_calls(assistant_message)
if tool_calls:
for tool_call in tool_calls:
...
result = self.tool_executor.execute_tool(tool_name, **parameters)
observation = f"Observation: {result.format_result()}"
self.conversation_history.append(Message(role="user", content=observation))
...
continue
else:
return assistant_message
```

This loop enables the agent to:

- Parse LLM output for tool calls (ReAct pattern: Thought, Action, Action Input, Observation).
- Execute tools (file read/write/edit, shell commands) and feed results back into the conversation.
- Iterate until a "Final Answer" is produced.

### Tool System: Extensible and Safe

Tools are registered via a `ToolRegistry` ([`src/bub/agent/tools.py`](https://github.com/psiace/bub/blob/19c015/src/bub/agent/tools.py)), and each tool is a Pydantic model with validation and metadata. For example, the `RunCommandTool` blocks dangerous commands and validates input:

```python
class RunCommandTool(Tool):
...
DANGEROUS_COMMANDS: ClassVar[set[str]] = {"rm", "del", ...}
def _validate_command(self) -> Optional[str]:
...
if base_cmd in self.DANGEROUS_COMMANDS:
return f"Dangerous command blocked: {base_cmd}"
```

This design makes it possible for the agent to safely self-modify, run tests, or even edit its own codebase—crucial for self-improvement.

### CLI: User Experience and Debuggability

The CLI ([`src/bub/cli/app.py`](https://github.com/psiace/bub/blob/19c015/src/bub/cli/app.py)) is built with Typer and Rich, providing a modern, user-friendly interface. The renderer ([`src/bub/cli/render.py`](https://github.com/psiace/bub/blob/19c015/src/bub/cli/render.py)) supports debug toggling, minimal/verbose TAAO (Thought/Action/Action Input/Observation) output, and clear error reporting.

```python
class Renderer:
def __init__(self) -> None:
self.console: Console = Console()
self._show_debug: bool = False
...
```

## Milestone: The First mypy Fix (and Why It Matters)

Bub aspires to self-improvement. The first tangible milestone? Fixing the very first mypy error: adding a missing return type annotation to `Renderer.__init__`, check out the [commit](https://github.com/PsiACE/bub/commit/87cdcc).

```diff
- def __init__(self):
- self.console = Console()
- self._show_debug = False
+ def __init__(self) -> None:
+ self.console: Console = Console()
+ self._show_debug: bool = False
```

This change reduced the mypy error count from 24 to 23. Trivial? Maybe. But it's a proof of concept: the agent can reason about, locate, and fix type errors in its own codebase. This is the first step toward a self-hosting, self-healing agent loop—one that can eventually:

- Run static analysis on itself
- Propose and apply code fixes
- Test and validate improvements

## Looking Forward: Bub as a Bootstrap Agent

Bub is still early. But the architecture is in place for:

- LLM-driven code editing and refactoring
- Automated type and lint fixes
- CLI-driven, user-friendly agent workflows

The journey from "fixing a mypy annotation" to "full agent self-improvement" is long, but every bootstrap starts with a single, type-safe step.

---

- [Project on GitHub](https://github.com/psiace/bub)
- Inspired by [ampcode.com/how-to-build-an-agent](https://ampcode.com/how-to-build-an-agent) and [huggingface.co/blog/tiny-agents](https://huggingface.co/blog/tiny-agents)
- See also: Claude Code, SWE-agent, and the broader self-bootstrapping movement
24 changes: 24 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Bub environment example configuration
# Copy this file to your project root as `.env` and fill in your values as needed.
# For sensitive values (like API keys), prefer setting environment variables directly.

# Required: API key for the model provider
BUB_API_KEY=your-api-key-here

# Optional: Model to use (supports various providers, e.g. OpenAI, Anthropic, etc.)
BUB_MODEL=claude-3-5-sonnet-20241022

# Optional: Custom API base URL (for OpenAI-compatible or proxy endpoints)
BUB_API_BASE=https://api.openai.com/v1

# Optional: Maximum tokens for AI responses
BUB_MAX_TOKENS=4096

# Optional: System prompt for the AI agent
#BUB_SYSTEM_PROMPT=You are Bub, a helpful AI coding assistant. ...

# Optional: UI theme (dark or light)
BUB_THEME=dark

# Optional: Workspace path for file operations
#BUB_WORKSPACE_PATH=/path/to/your/workspace
5 changes: 3 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
site_name: bub
repo_url: https://github.com/psiace/bub
site_url: https://bub.build
site_description: Bub it up! Your partner in every step of the build.
site_description: Bub it. Build it.
site_author: Chojan Shang
edit_uri: edit/main/docs/
repo_name: psiace/bub
copyright: Maintained by <a href="https://psiace.me">Chojan Shang</a>.

nav:
- Home: index.md
- Modules: modules.md
- Posts:
- Baby Bub - Bootstrap Milestone: posts/2025-07-16-baby-bub-bootstrap-milestone.md

plugins:
- search
Expand Down
17 changes: 13 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "bub"
version = "0.0.0"
description = "[Placeholder] Bub it up! Your partner in every step of the build."
version = "0.1.0-alpha.1"
description = "Bub it. Build it."
authors = [{ name = "Chojan Shang", email = "psiace@apache.org" }]
readme = "README.md"
keywords = ['python']
Expand All @@ -17,19 +17,28 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
]

dependencies = [
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
"typer>=0.9.0",
"litellm>=1.0.0",
"rich>=13.0.0",
]

[project.urls]
Homepage = "https://bub.build"
Repository = "https://github.com/psiace/bub"
Documentation = "https://bub.build"

[project.scripts]
bub = "bub.cli:app"

[dependency-groups]
dev = [
"pytest>=7.2.0",
"pre-commit>=2.20.0",
"tox-uv>=1.11.3",

"mypy>=0.991",

"ruff>=0.11.5",
"mkdocs>=1.4.2",
"mkdocs-terminal>=4.7.0",
Expand Down
7 changes: 7 additions & 0 deletions src/bub/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Bub - Bub it. Build it."""

__version__ = "0.1.0-alpha.1"

from .agent import Agent, ToolRegistry

__all__ = ["Agent", "ToolRegistry"]
16 changes: 16 additions & 0 deletions src/bub/agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Agent package for Bub."""

from .context import Context, Message
from .core import Agent, ReActPromptFormatter
from .tools import Tool, ToolExecutor, ToolRegistry, ToolResult

__all__ = [
"Agent",
"Context",
"Message",
"ReActPromptFormatter",
"Tool",
"ToolExecutor",
"ToolRegistry",
"ToolResult",
]
42 changes: 42 additions & 0 deletions src/bub/agent/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Context and message types for the agent package."""

from pathlib import Path
from typing import Any, Optional

from pydantic import BaseModel, Field

from ..config import get_settings


class Message(BaseModel):
"""A message in the conversation."""

role: str = Field(..., description="Message role (user/assistant)")
content: str = Field(..., description="Message content")


class Context:
"""Agent environment context: workspace, config, tool registry, etc."""

def __init__(self, workspace_path: Optional[Path] = None, config: Optional[Any] = None):
self.workspace_path = workspace_path or Path.cwd()
self.config = config or get_settings()
self.tool_registry = None # Will be set by Agent

def get_system_prompt(self) -> str:
"""Get the system prompt from config."""
return self.config.system_prompt or ""

def build_context_message(self) -> str:
"""Build a clean context message with essential information."""
if not self.tool_registry:
return f"[Environment Context]\nWorkspace: {self.workspace_path}\nNo tools available"

tool_schemas = self.tool_registry.get_tool_schemas()
msg = [
"[Environment Context]",
f"Workspace: {self.workspace_path}",
f"Available tools: {', '.join(tool_schemas.keys())}",
f"Tool schemas: {self.tool_registry._format_schemas_for_context()}",
]
return "\n".join(msg)
Loading
Loading