rebrand: rename AutoCoder to AutoForge across entire codebase

Complete project rebrand from AutoCoder to AutoForge, touching 62 files
across Python backend, FastAPI server, React UI, documentation, config,
and CI/CD.

Key changes:
- Rename autocoder_paths.py -> autoforge_paths.py with backward-compat
  migration from .autocoder/ -> .autoforge/ directories
- Update registry.py to migrate ~/.autocoder/ -> ~/.autoforge/ global
  config directory with fallback support
- Update security.py with fallback reads from legacy .autocoder/ paths
- Rename .claude/commands and skills from gsd-to-autocoder-spec to
  gsd-to-autoforge-spec
- Update all Python modules: client, prompts, progress, agent,
  orchestrator, server routers and services
- Update React UI: package.json name, index.html title, localStorage
  keys, all documentation sections, component references
- Update start scripts (bat/sh/py), examples, and .env.example
- Update CLAUDE.md and README.md with new branding and paths
- Update test files for new .autoforge/ directory structure
- Transfer git remote from leonvanzyl/autocoder to
  AutoForgeAI/autoforge

Backward compatibility preserved: legacy .autocoder/ directories are
auto-detected and migrated on next agent start. Config fallback chain
checks both new and old paths.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-02-04 12:02:06 +02:00
parent f6510b4dd8
commit c2ad993e75
63 changed files with 405 additions and 354 deletions

View File

@@ -97,7 +97,7 @@ Fix ALL issues before considering the implementation complete. Never leave linti
## Project-Specific Context ## Project-Specific Context
For this project (autocoder): For this project (autoforge):
- **Python Backend**: Uses SQLAlchemy, FastAPI, follows patterns in `api/`, `mcp_server/` - **Python Backend**: Uses SQLAlchemy, FastAPI, follows patterns in `api/`, `mcp_server/`
- **React UI**: Uses React 18, TypeScript, TanStack Query, Tailwind CSS v4, Radix UI - **React UI**: Uses React 18, TypeScript, TanStack Query, Tailwind CSS v4, Radix UI
- **Design System**: Neobrutalism style with specific color tokens and animations - **Design System**: Neobrutalism style with specific color tokens and animations

View File

@@ -8,7 +8,7 @@ This command **requires** the project directory as an argument via `$ARGUMENTS`.
**Example:** `/create-spec generations/my-app` **Example:** `/create-spec generations/my-app`
**Output location:** `$ARGUMENTS/.autocoder/prompts/app_spec.txt` and `$ARGUMENTS/.autocoder/prompts/initializer_prompt.md` **Output location:** `$ARGUMENTS/.autoforge/prompts/app_spec.txt` and `$ARGUMENTS/.autoforge/prompts/initializer_prompt.md`
If `$ARGUMENTS` is empty, inform the user they must provide a project path and exit. If `$ARGUMENTS` is empty, inform the user they must provide a project path and exit.
@@ -347,13 +347,13 @@ First ask in conversation if they want to make changes.
## Output Directory ## Output Directory
The output directory is: `$ARGUMENTS/.autocoder/prompts/` The output directory is: `$ARGUMENTS/.autoforge/prompts/`
Once the user approves, generate these files: Once the user approves, generate these files:
## 1. Generate `app_spec.txt` ## 1. Generate `app_spec.txt`
**Output path:** `$ARGUMENTS/.autocoder/prompts/app_spec.txt` **Output path:** `$ARGUMENTS/.autoforge/prompts/app_spec.txt`
Create a new file using this XML structure: Create a new file using this XML structure:
@@ -489,7 +489,7 @@ Create a new file using this XML structure:
## 2. Update `initializer_prompt.md` ## 2. Update `initializer_prompt.md`
**Output path:** `$ARGUMENTS/.autocoder/prompts/initializer_prompt.md` **Output path:** `$ARGUMENTS/.autoforge/prompts/initializer_prompt.md`
If the output directory has an existing `initializer_prompt.md`, read it and update the feature count. If the output directory has an existing `initializer_prompt.md`, read it and update the feature count.
If not, copy from `.claude/templates/initializer_prompt.template.md` first, then update. If not, copy from `.claude/templates/initializer_prompt.template.md` first, then update.
@@ -512,7 +512,7 @@ After: **CRITICAL:** You must create exactly **25** features using the `feature
## 3. Write Status File (REQUIRED - Do This Last) ## 3. Write Status File (REQUIRED - Do This Last)
**Output path:** `$ARGUMENTS/.autocoder/prompts/.spec_status.json` **Output path:** `$ARGUMENTS/.autoforge/prompts/.spec_status.json`
**CRITICAL:** After you have completed ALL requested file changes, write this status file to signal completion to the UI. This is required for the "Continue to Project" button to appear. **CRITICAL:** After you have completed ALL requested file changes, write this status file to signal completion to the UI. This is required for the "Continue to Project" button to appear.
@@ -524,8 +524,8 @@ Write this JSON file:
"version": 1, "version": 1,
"timestamp": "[current ISO 8601 timestamp, e.g., 2025-01-15T14:30:00.000Z]", "timestamp": "[current ISO 8601 timestamp, e.g., 2025-01-15T14:30:00.000Z]",
"files_written": [ "files_written": [
".autocoder/prompts/app_spec.txt", ".autoforge/prompts/app_spec.txt",
".autocoder/prompts/initializer_prompt.md" ".autoforge/prompts/initializer_prompt.md"
], ],
"feature_count": [the feature count from Phase 4L] "feature_count": [the feature count from Phase 4L]
} }
@@ -539,9 +539,9 @@ Write this JSON file:
"version": 1, "version": 1,
"timestamp": "2025-01-15T14:30:00.000Z", "timestamp": "2025-01-15T14:30:00.000Z",
"files_written": [ "files_written": [
".autocoder/prompts/app_spec.txt", ".autoforge/prompts/app_spec.txt",
".autocoder/prompts/initializer_prompt.md", ".autoforge/prompts/initializer_prompt.md",
".autocoder/prompts/coding_prompt.md" ".autoforge/prompts/coding_prompt.md"
], ],
"feature_count": 35 "feature_count": 35
} }
@@ -559,11 +559,11 @@ Write this JSON file:
Once files are generated, tell the user what to do next: Once files are generated, tell the user what to do next:
> "Your specification files have been created in `$ARGUMENTS/.autocoder/prompts/`! > "Your specification files have been created in `$ARGUMENTS/.autoforge/prompts/`!
> >
> **Files created:** > **Files created:**
> - `$ARGUMENTS/.autocoder/prompts/app_spec.txt` > - `$ARGUMENTS/.autoforge/prompts/app_spec.txt`
> - `$ARGUMENTS/.autocoder/prompts/initializer_prompt.md` > - `$ARGUMENTS/.autoforge/prompts/initializer_prompt.md`
> >
> The **Continue to Project** button should now appear. Click it to start the autonomous coding agent! > The **Continue to Project** button should now appear. Click it to start the autonomous coding agent!
> >

View File

@@ -42,7 +42,7 @@ You are the **Project Expansion Assistant** - an expert at understanding existin
# FIRST: Read and Understand Existing Project # FIRST: Read and Understand Existing Project
**Step 1:** Read the existing specification: **Step 1:** Read the existing specification:
- Read `$ARGUMENTS/.autocoder/prompts/app_spec.txt` - Read `$ARGUMENTS/.autoforge/prompts/app_spec.txt`
**Step 2:** Present a summary to the user: **Step 2:** Present a summary to the user:
@@ -231,4 +231,4 @@ If they want to add more, go back to Phase 1.
# BEGIN # BEGIN
Start by reading the app specification file at `$ARGUMENTS/.autocoder/prompts/app_spec.txt`, then greet the user with a summary of their existing project and ask what they want to add. Start by reading the app specification file at `$ARGUMENTS/.autoforge/prompts/app_spec.txt`, then greet the user with a summary of their existing project and ask what they want to add.

View File

@@ -1,10 +0,0 @@
---
allowed-tools: Read, Write, Bash, Glob, Grep
description: Convert GSD codebase mapping to Autocoder app_spec.txt
---
# GSD to Autocoder Spec
Convert `.planning/codebase/*.md` (from `/gsd:map-codebase`) to Autocoder's `.autocoder/prompts/app_spec.txt`.
@.claude/skills/gsd-to-autocoder-spec/SKILL.md

View File

@@ -0,0 +1,10 @@
---
allowed-tools: Read, Write, Bash, Glob, Grep
description: Convert GSD codebase mapping to AutoForge app_spec.txt
---
# GSD to AutoForge Spec
Convert `.planning/codebase/*.md` (from `/gsd:map-codebase`) to AutoForge's `.autoforge/prompts/app_spec.txt`.
@.claude/skills/gsd-to-autoforge-spec/SKILL.md

View File

@@ -1,21 +1,21 @@
--- ---
name: gsd-to-autocoder-spec name: gsd-to-autoforge-spec
description: | description: |
Convert GSD codebase mapping to Autocoder app_spec.txt. This skill should be used when Convert GSD codebase mapping to AutoForge app_spec.txt. This skill should be used when
the user has run /gsd:map-codebase and wants to use Autocoder on an existing project. the user has run /gsd:map-codebase and wants to use AutoForge on an existing project.
Triggers: "convert to autocoder", "gsd to spec", "create app_spec from codebase", Triggers: "convert to autoforge", "gsd to spec", "create app_spec from codebase",
"use autocoder on existing project", after /gsd:map-codebase completion. "use autoforge on existing project", after /gsd:map-codebase completion.
--- ---
# GSD to Autocoder Spec Converter # GSD to AutoForge Spec Converter
Converts `.planning/codebase/*.md` (GSD mapping output) to `.autocoder/prompts/app_spec.txt` (Autocoder format). Converts `.planning/codebase/*.md` (GSD mapping output) to `.autoforge/prompts/app_spec.txt` (AutoForge format).
## When to Use ## When to Use
- After running `/gsd:map-codebase` on an existing project - After running `/gsd:map-codebase` on an existing project
- When onboarding an existing codebase to Autocoder - When onboarding an existing codebase to AutoForge
- User wants Autocoder to continue development on existing code - User wants AutoForge to continue development on existing code
## Prerequisites ## Prerequisites
@@ -84,12 +84,12 @@ Extract:
Create `prompts/` directory: Create `prompts/` directory:
```bash ```bash
mkdir -p .autocoder/prompts mkdir -p .autoforge/prompts
``` ```
**Mapping GSD Documents to Autocoder Spec:** **Mapping GSD Documents to AutoForge Spec:**
| GSD Source | Autocoder Target | | GSD Source | AutoForge Target |
|------------|------------------| |------------|------------------|
| STACK.md Languages | `<technology_stack>` | | STACK.md Languages | `<technology_stack>` |
| STACK.md Frameworks | `<frontend>`, `<backend>` | | STACK.md Frameworks | `<frontend>`, `<backend>` |
@@ -114,7 +114,7 @@ mkdir -p .autocoder/prompts
**Write the spec file** using the XML format from [references/app-spec-format.md](references/app-spec-format.md): **Write the spec file** using the XML format from [references/app-spec-format.md](references/app-spec-format.md):
```bash ```bash
cat > .autocoder/prompts/app_spec.txt << 'EOF' cat > .autoforge/prompts/app_spec.txt << 'EOF'
<project_specification> <project_specification>
<project_name>{from package.json or directory}</project_name> <project_name>{from package.json or directory}</project_name>
@@ -173,9 +173,9 @@ EOF
### Step 5: Verify Generated Spec ### Step 5: Verify Generated Spec
```bash ```bash
head -100 .autocoder/prompts/app_spec.txt head -100 .autoforge/prompts/app_spec.txt
echo "---" echo "---"
grep -c "User can\|System\|API\|Feature" .autocoder/prompts/app_spec.txt || echo "0" grep -c "User can\|System\|API\|Feature" .autoforge/prompts/app_spec.txt || echo "0"
``` ```
**Validation checklist:** **Validation checklist:**
@@ -194,15 +194,15 @@ Output:
app_spec.txt generated from GSD codebase mapping. app_spec.txt generated from GSD codebase mapping.
Source: .planning/codebase/*.md Source: .planning/codebase/*.md
Output: .autocoder/prompts/app_spec.txt Output: .autoforge/prompts/app_spec.txt
Next: Start Autocoder Next: Start AutoForge
cd {project_dir} cd {project_dir}
python ~/projects/autocoder/start.py python ~/projects/autoforge/start.py
Or via UI: Or via UI:
~/projects/autocoder/start_ui.sh ~/projects/autoforge/start_ui.sh
The Initializer will create features.db from this spec. The Initializer will create features.db from this spec.
``` ```

View File

@@ -1,6 +1,6 @@
# Autocoder app_spec.txt XML Format # AutoForge app_spec.txt XML Format
Complete reference for the XML structure expected by Autocoder's Initializer agent. Complete reference for the XML structure expected by AutoForge's Initializer agent.
## Root Structure ## Root Structure
@@ -275,7 +275,7 @@ The Initializer agent expects features distributed across categories:
| Medium web app | 200-250 | 10-15 | | Medium web app | 200-250 | 10-15 |
| Complex full-stack | 300-400 | 15-20 | | Complex full-stack | 300-400 | 15-20 |
## GSD to Autocoder Mapping ## GSD to AutoForge Mapping
When converting from GSD codebase mapping: When converting from GSD codebase mapping:

View File

@@ -36,7 +36,7 @@
# GLM/Alternative API Configuration (Optional) # GLM/Alternative API Configuration (Optional)
# To use Zhipu AI's GLM models instead of Claude, uncomment and set these variables. # To use Zhipu AI's GLM models instead of Claude, uncomment and set these variables.
# This only affects AutoCoder - your global Claude Code settings remain unchanged. # This only affects AutoForge - your global Claude Code settings remain unchanged.
# Get an API key at: https://z.ai/subscribe # Get an API key at: https://z.ai/subscribe
# #
# ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic # ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic

View File

@@ -140,7 +140,7 @@ Configuration in `pyproject.toml`:
- `start.py` - CLI launcher with project creation/selection menu - `start.py` - CLI launcher with project creation/selection menu
- `autonomous_agent_demo.py` - Entry point for running the agent (supports `--yolo`, `--parallel`, `--batch-size`, `--batch-features`) - `autonomous_agent_demo.py` - Entry point for running the agent (supports `--yolo`, `--parallel`, `--batch-size`, `--batch-features`)
- `autocoder_paths.py` - Central path resolution with dual-path backward compatibility and migration - `autoforge_paths.py` - Central path resolution with dual-path backward compatibility and migration
- `agent.py` - Agent session loop using Claude Agent SDK - `agent.py` - Agent session loop using Claude Agent SDK
- `client.py` - ClaudeSDKClient configuration with security hooks, MCP servers, and Vertex AI support - `client.py` - ClaudeSDKClient configuration with security hooks, MCP servers, and Vertex AI support
- `security.py` - Bash command allowlist validation (ALLOWED_COMMANDS whitelist) - `security.py` - Bash command allowlist validation (ALLOWED_COMMANDS whitelist)
@@ -158,7 +158,7 @@ Configuration in `pyproject.toml`:
### Project Registry ### Project Registry
Projects can be stored in any directory. The registry maps project names to paths using SQLite: Projects can be stored in any directory. The registry maps project names to paths using SQLite:
- **All platforms**: `~/.autocoder/registry.db` - **All platforms**: `~/.autoforge/registry.db`
The registry uses: The registry uses:
- SQLite database with SQLAlchemy ORM - SQLite database with SQLAlchemy ORM
@@ -254,18 +254,18 @@ Keyboard shortcuts (press `?` for help):
### Project Structure for Generated Apps ### Project Structure for Generated Apps
Projects can be stored in any directory (registered in `~/.autocoder/registry.db`). Each project contains: Projects can be stored in any directory (registered in `~/.autoforge/registry.db`). Each project contains:
- `.autocoder/prompts/app_spec.txt` - Application specification (XML format) - `.autoforge/prompts/app_spec.txt` - Application specification (XML format)
- `.autocoder/prompts/initializer_prompt.md` - First session prompt - `.autoforge/prompts/initializer_prompt.md` - First session prompt
- `.autocoder/prompts/coding_prompt.md` - Continuation session prompt - `.autoforge/prompts/coding_prompt.md` - Continuation session prompt
- `.autocoder/features.db` - SQLite database with feature test cases - `.autoforge/features.db` - SQLite database with feature test cases
- `.autocoder/.agent.lock` - Lock file to prevent multiple agent instances - `.autoforge/.agent.lock` - Lock file to prevent multiple agent instances
- `.autocoder/allowed_commands.yaml` - Project-specific bash command allowlist (optional) - `.autoforge/allowed_commands.yaml` - Project-specific bash command allowlist (optional)
- `.autocoder/.gitignore` - Ignores runtime files - `.autoforge/.gitignore` - Ignores runtime files
- `CLAUDE.md` - Stays at project root (SDK convention) - `CLAUDE.md` - Stays at project root (SDK convention)
- `app_spec.txt` - Root copy for agent template compatibility - `app_spec.txt` - Root copy for agent template compatibility
Legacy projects with files at root level (e.g., `features.db`, `prompts/`) are auto-migrated to `.autocoder/` on next agent start. Dual-path resolution ensures old and new layouts work transparently. Legacy projects with files at root level (e.g., `features.db`, `prompts/`) are auto-migrated to `.autoforge/` on next agent start. Dual-path resolution ensures old and new layouts work transparently.
### Security Model ### Security Model
@@ -311,14 +311,14 @@ The agent's bash command access is controlled through a hierarchical configurati
**Command Hierarchy (highest to lowest priority):** **Command Hierarchy (highest to lowest priority):**
1. **Hardcoded Blocklist** (`security.py`) - NEVER allowed (dd, sudo, shutdown, etc.) 1. **Hardcoded Blocklist** (`security.py`) - NEVER allowed (dd, sudo, shutdown, etc.)
2. **Org Blocklist** (`~/.autocoder/config.yaml`) - Cannot be overridden by projects 2. **Org Blocklist** (`~/.autoforge/config.yaml`) - Cannot be overridden by projects
3. **Org Allowlist** (`~/.autocoder/config.yaml`) - Available to all projects 3. **Org Allowlist** (`~/.autoforge/config.yaml`) - Available to all projects
4. **Global Allowlist** (`security.py`) - Default commands (npm, git, curl, etc.) 4. **Global Allowlist** (`security.py`) - Default commands (npm, git, curl, etc.)
5. **Project Allowlist** (`.autocoder/allowed_commands.yaml`) - Project-specific commands 5. **Project Allowlist** (`.autoforge/allowed_commands.yaml`) - Project-specific commands
**Project Configuration:** **Project Configuration:**
Each project can define custom allowed commands in `.autocoder/allowed_commands.yaml`: Each project can define custom allowed commands in `.autoforge/allowed_commands.yaml`:
```yaml ```yaml
version: 1 version: 1
@@ -338,7 +338,7 @@ commands:
**Organization Configuration:** **Organization Configuration:**
System administrators can set org-wide policies in `~/.autocoder/config.yaml`: System administrators can set org-wide policies in `~/.autoforge/config.yaml`:
```yaml ```yaml
version: 1 version: 1
@@ -405,7 +405,7 @@ Run coding agents using local models via Ollama v0.14.0+:
ANTHROPIC_DEFAULT_OPUS_MODEL=qwen3-coder ANTHROPIC_DEFAULT_OPUS_MODEL=qwen3-coder
ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3-coder ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3-coder
``` ```
5. Run autocoder normally - it will use your local Ollama models 5. Run AutoForge normally - it will use your local Ollama models
**Recommended coding models:** **Recommended coding models:**
- `qwen3-coder` - Good balance of speed and capability - `qwen3-coder` - Good balance of speed and capability
@@ -427,7 +427,7 @@ Run coding agents using local models via Ollama v0.14.0+:
**Slash commands** (`.claude/commands/`): **Slash commands** (`.claude/commands/`):
- `/create-spec` - Interactive spec creation for new projects - `/create-spec` - Interactive spec creation for new projects
- `/expand-project` - Expand existing project with new features - `/expand-project` - Expand existing project with new features
- `/gsd-to-autocoder-spec` - Convert GSD codebase mapping to app_spec.txt - `/gsd-to-autoforge-spec` - Convert GSD codebase mapping to app_spec.txt
- `/check-code` - Run lint and type-check for code quality - `/check-code` - Run lint and type-check for code quality
- `/checkpoint` - Create comprehensive checkpoint commit - `/checkpoint` - Create comprehensive checkpoint commit
- `/review-pr` - Review pull requests - `/review-pr` - Review pull requests
@@ -439,7 +439,7 @@ Run coding agents using local models via Ollama v0.14.0+:
**Skills** (`.claude/skills/`): **Skills** (`.claude/skills/`):
- `frontend-design` - Distinctive, production-grade UI design - `frontend-design` - Distinctive, production-grade UI design
- `gsd-to-autocoder-spec` - Convert GSD codebase mapping to Autocoder app_spec format - `gsd-to-autoforge-spec` - Convert GSD codebase mapping to AutoForge app_spec format
**Other:** **Other:**
- `.claude/templates/` - Prompt templates copied to new projects - `.claude/templates/` - Prompt templates copied to new projects
@@ -449,12 +449,12 @@ Run coding agents using local models via Ollama v0.14.0+:
### Prompt Loading Fallback Chain ### Prompt Loading Fallback Chain
1. Project-specific: `{project_dir}/.autocoder/prompts/{name}.md` (or legacy `{project_dir}/prompts/{name}.md`) 1. Project-specific: `{project_dir}/.autoforge/prompts/{name}.md` (or legacy `{project_dir}/prompts/{name}.md`)
2. Base template: `.claude/templates/{name}.template.md` 2. Base template: `.claude/templates/{name}.template.md`
### Agent Session Flow ### Agent Session Flow
1. Check if `.autocoder/features.db` has features (determines initializer vs coding agent) 1. Check if `.autoforge/features.db` has features (determines initializer vs coding agent)
2. Create ClaudeSDKClient with security settings 2. Create ClaudeSDKClient with security settings
3. Send prompt and stream response 3. Send prompt and stream response
4. Auto-continue with 3-second delay between sessions 4. Auto-continue with 3-second delay between sessions

View File

@@ -1,4 +1,4 @@
# AutoCoder # AutoForge
[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-FFDD00?style=flat&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/leonvanzyl) [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-FFDD00?style=flat&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/leonvanzyl)
@@ -290,7 +290,7 @@ When test progress increases, the agent sends:
### Using GLM Models (Alternative to Claude) ### Using GLM Models (Alternative to Claude)
To use Zhipu AI's GLM models instead of Claude, add these variables to your `.env` file in the AutoCoder directory: To use Zhipu AI's GLM models instead of Claude, add these variables to your `.env` file in the AutoForge directory:
```bash ```bash
ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
@@ -301,7 +301,7 @@ ANTHROPIC_DEFAULT_OPUS_MODEL=glm-4.7
ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-4.5-air ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-4.5-air
``` ```
This routes AutoCoder's API requests through Zhipu's Claude-compatible API, allowing you to use GLM-4.7 and other models. **This only affects AutoCoder** - your global Claude Code settings remain unchanged. This routes AutoForge's API requests through Zhipu's Claude-compatible API, allowing you to use GLM-4.7 and other models. **This only affects AutoForge** - your global Claude Code settings remain unchanged.
Get an API key at: https://z.ai/subscribe Get an API key at: https://z.ai/subscribe

View File

@@ -183,7 +183,7 @@ class ScheduleOverride(Base):
def get_database_path(project_dir: Path) -> Path: def get_database_path(project_dir: Path) -> Path:
"""Return the path to the SQLite database for a project.""" """Return the path to the SQLite database for a project."""
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
return get_features_db_path(project_dir) return get_features_db_path(project_dir)
@@ -385,7 +385,7 @@ def create_database(project_dir: Path) -> tuple:
db_url = get_database_url(project_dir) db_url = get_database_url(project_dir)
# Ensure parent directory exists (for .autocoder/ layout) # Ensure parent directory exists (for .autoforge/ layout)
db_path = get_database_path(project_dir) db_path = get_database_path(project_dir)
db_path.parent.mkdir(parents=True, exist_ok=True) db_path.parent.mkdir(parents=True, exist_ok=True)

View File

@@ -1,17 +1,19 @@
""" """
Autocoder Path Resolution AutoForge Path Resolution
========================= =========================
Central module for resolving paths to autocoder-generated files within a project. Central module for resolving paths to autoforge-generated files within a project.
Implements a dual-path resolution strategy for backward compatibility: Implements a tri-path resolution strategy for backward compatibility:
1. Check ``project_dir / ".autocoder" / X`` (new layout) 1. Check ``project_dir / ".autoforge" / X`` (current layout)
2. Check ``project_dir / X`` (legacy root-level layout) 2. Check ``project_dir / ".autocoder" / X`` (legacy layout)
3. Default to the new location for fresh projects 3. Check ``project_dir / X`` (legacy root-level layout)
4. Default to the new location for fresh projects
This allows existing projects with root-level ``features.db``, ``.agent.lock``, This allows existing projects with root-level ``features.db``, ``.agent.lock``,
etc. to keep working while new projects store everything under ``.autocoder/``. etc. to keep working while new projects store everything under ``.autoforge/``.
Projects using the old ``.autocoder/`` directory are auto-migrated on next start.
The ``migrate_project_layout`` function can move an old-layout project to the The ``migrate_project_layout`` function can move an old-layout project to the
new layout safely, with full integrity checks for SQLite databases. new layout safely, with full integrity checks for SQLite databases.
@@ -25,10 +27,10 @@ from pathlib import Path
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# .gitignore content written into every .autocoder/ directory # .gitignore content written into every .autoforge/ directory
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
_GITIGNORE_CONTENT = """\ _GITIGNORE_CONTENT = """\
# Autocoder runtime files # AutoForge runtime files
features.db features.db
features.db-wal features.db-wal
features.db-shm features.db-shm
@@ -49,15 +51,18 @@ assistant.db-shm
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def _resolve_path(project_dir: Path, filename: str) -> Path: def _resolve_path(project_dir: Path, filename: str) -> Path:
"""Resolve a file path using dual-path strategy. """Resolve a file path using tri-path strategy.
Checks the new ``.autocoder/`` location first, then falls back to the Checks the new ``.autoforge/`` location first, then the legacy
legacy root-level location. If neither exists, returns the new location ``.autocoder/`` location, then the root-level location. If none exist,
so that newly-created files land in ``.autocoder/``. returns the new location so that newly-created files land in ``.autoforge/``.
""" """
new = project_dir / ".autocoder" / filename new = project_dir / ".autoforge" / filename
if new.exists(): if new.exists():
return new return new
legacy = project_dir / ".autocoder" / filename
if legacy.exists():
return legacy
old = project_dir / filename old = project_dir / filename
if old.exists(): if old.exists():
return old return old
@@ -65,14 +70,17 @@ def _resolve_path(project_dir: Path, filename: str) -> Path:
def _resolve_dir(project_dir: Path, dirname: str) -> Path: def _resolve_dir(project_dir: Path, dirname: str) -> Path:
"""Resolve a directory path using dual-path strategy. """Resolve a directory path using tri-path strategy.
Same logic as ``_resolve_path`` but intended for directories such as Same logic as ``_resolve_path`` but intended for directories such as
``prompts/``. ``prompts/``.
""" """
new = project_dir / ".autocoder" / dirname new = project_dir / ".autoforge" / dirname
if new.exists(): if new.exists():
return new return new
legacy = project_dir / ".autocoder" / dirname
if legacy.exists():
return legacy
old = project_dir / dirname old = project_dir / dirname
if old.exists(): if old.exists():
return old return old
@@ -80,27 +88,27 @@ def _resolve_dir(project_dir: Path, dirname: str) -> Path:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# .autocoder directory management # .autoforge directory management
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def get_autocoder_dir(project_dir: Path) -> Path: def get_autoforge_dir(project_dir: Path) -> Path:
"""Return the ``.autocoder`` directory path. Does NOT create it.""" """Return the ``.autoforge`` directory path. Does NOT create it."""
return project_dir / ".autocoder" return project_dir / ".autoforge"
def ensure_autocoder_dir(project_dir: Path) -> Path: def ensure_autoforge_dir(project_dir: Path) -> Path:
"""Create the ``.autocoder/`` directory (if needed) and write its ``.gitignore``. """Create the ``.autoforge/`` directory (if needed) and write its ``.gitignore``.
Returns: Returns:
The path to the ``.autocoder`` directory. The path to the ``.autoforge`` directory.
""" """
autocoder_dir = get_autocoder_dir(project_dir) autoforge_dir = get_autoforge_dir(project_dir)
autocoder_dir.mkdir(parents=True, exist_ok=True) autoforge_dir.mkdir(parents=True, exist_ok=True)
gitignore_path = autocoder_dir / ".gitignore" gitignore_path = autoforge_dir / ".gitignore"
gitignore_path.write_text(_GITIGNORE_CONTENT, encoding="utf-8") gitignore_path.write_text(_GITIGNORE_CONTENT, encoding="utf-8")
return autocoder_dir return autoforge_dir
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -154,9 +162,9 @@ def get_prompts_dir(project_dir: Path) -> Path:
def get_expand_settings_path(project_dir: Path, uuid_hex: str) -> Path: def get_expand_settings_path(project_dir: Path, uuid_hex: str) -> Path:
"""Return the path for an ephemeral expand-session settings file. """Return the path for an ephemeral expand-session settings file.
These files are short-lived and always stored in ``.autocoder/``. These files are short-lived and always stored in ``.autoforge/``.
""" """
return project_dir / ".autocoder" / f".claude_settings.expand.{uuid_hex}.json" return project_dir / ".autoforge" / f".claude_settings.expand.{uuid_hex}.json"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -166,8 +174,9 @@ def get_expand_settings_path(project_dir: Path, uuid_hex: str) -> Path:
def has_agent_running(project_dir: Path) -> bool: def has_agent_running(project_dir: Path) -> bool:
"""Check whether any agent or dev-server lock file exists at either location. """Check whether any agent or dev-server lock file exists at either location.
Inspects both the legacy root-level paths and the new ``.autocoder/`` Inspects the legacy root-level paths, the old ``.autocoder/`` paths, and
paths so that a running agent is detected regardless of project layout. the new ``.autoforge/`` paths so that a running agent is detected
regardless of project layout.
Returns: Returns:
``True`` if any ``.agent.lock`` or ``.devserver.lock`` exists. ``True`` if any ``.agent.lock`` or ``.devserver.lock`` exists.
@@ -176,8 +185,11 @@ def has_agent_running(project_dir: Path) -> bool:
for name in lock_names: for name in lock_names:
if (project_dir / name).exists(): if (project_dir / name).exists():
return True return True
# Check both old and new directory names for backward compatibility
if (project_dir / ".autocoder" / name).exists(): if (project_dir / ".autocoder" / name).exists():
return True return True
if (project_dir / ".autoforge" / name).exists():
return True
return False return False
@@ -186,7 +198,7 @@ def has_agent_running(project_dir: Path) -> bool:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def migrate_project_layout(project_dir: Path) -> list[str]: def migrate_project_layout(project_dir: Path) -> list[str]:
"""Migrate a project from the legacy root-level layout to ``.autocoder/``. """Migrate a project from the legacy root-level layout to ``.autoforge/``.
The migration is incremental and safe: The migration is incremental and safe:
@@ -199,7 +211,7 @@ def migrate_project_layout(project_dir: Path) -> list[str]:
Returns: Returns:
A list of human-readable descriptions of what was migrated, e.g. A list of human-readable descriptions of what was migrated, e.g.
``["prompts/ -> .autocoder/prompts/", "features.db -> .autocoder/features.db"]``. ``["prompts/ -> .autoforge/prompts/", "features.db -> .autoforge/features.db"]``.
An empty list means nothing was migrated (either everything is An empty list means nothing was migrated (either everything is
already migrated, or the agent is running). already migrated, or the agent is running).
""" """
@@ -208,18 +220,31 @@ def migrate_project_layout(project_dir: Path) -> list[str]:
logger.warning("Migration skipped: agent or dev-server is running for %s", project_dir) logger.warning("Migration skipped: agent or dev-server is running for %s", project_dir)
return [] return []
autocoder_dir = ensure_autocoder_dir(project_dir) # --- 0. Migrate .autocoder/ → .autoforge/ directory -------------------
migrated: list[str] = [] old_autocoder_dir = project_dir / ".autocoder"
new_autoforge_dir = project_dir / ".autoforge"
if old_autocoder_dir.exists() and old_autocoder_dir.is_dir() and not new_autoforge_dir.exists():
try:
old_autocoder_dir.rename(new_autoforge_dir)
logger.info("Migrated .autocoder/ -> .autoforge/")
migrated: list[str] = [".autocoder/ -> .autoforge/"]
except Exception:
logger.warning("Failed to migrate .autocoder/ -> .autoforge/", exc_info=True)
migrated = []
else:
migrated = []
autoforge_dir = ensure_autoforge_dir(project_dir)
# --- 1. Migrate prompts/ directory ----------------------------------- # --- 1. Migrate prompts/ directory -----------------------------------
try: try:
old_prompts = project_dir / "prompts" old_prompts = project_dir / "prompts"
new_prompts = autocoder_dir / "prompts" new_prompts = autoforge_dir / "prompts"
if old_prompts.exists() and old_prompts.is_dir() and not new_prompts.exists(): if old_prompts.exists() and old_prompts.is_dir() and not new_prompts.exists():
shutil.copytree(str(old_prompts), str(new_prompts)) shutil.copytree(str(old_prompts), str(new_prompts))
shutil.rmtree(str(old_prompts)) shutil.rmtree(str(old_prompts))
migrated.append("prompts/ -> .autocoder/prompts/") migrated.append("prompts/ -> .autoforge/prompts/")
logger.info("Migrated prompts/ -> .autocoder/prompts/") logger.info("Migrated prompts/ -> .autoforge/prompts/")
except Exception: except Exception:
logger.warning("Failed to migrate prompts/ directory", exc_info=True) logger.warning("Failed to migrate prompts/ directory", exc_info=True)
@@ -228,7 +253,7 @@ def migrate_project_layout(project_dir: Path) -> list[str]:
for db_name in db_names: for db_name in db_names:
try: try:
old_db = project_dir / db_name old_db = project_dir / db_name
new_db = autocoder_dir / db_name new_db = autoforge_dir / db_name
if old_db.exists() and not new_db.exists(): if old_db.exists() and not new_db.exists():
# Flush WAL to ensure all data is in the main database file # Flush WAL to ensure all data is in the main database file
conn = sqlite3.connect(str(old_db)) conn = sqlite3.connect(str(old_db))
@@ -263,8 +288,8 @@ def migrate_project_layout(project_dir: Path) -> list[str]:
wal_file = project_dir / f"{db_name}{suffix}" wal_file = project_dir / f"{db_name}{suffix}"
wal_file.unlink(missing_ok=True) wal_file.unlink(missing_ok=True)
migrated.append(f"{db_name} -> .autocoder/{db_name}") migrated.append(f"{db_name} -> .autoforge/{db_name}")
logger.info("Migrated %s -> .autocoder/%s", db_name, db_name) logger.info("Migrated %s -> .autoforge/%s", db_name, db_name)
except Exception: except Exception:
logger.warning("Failed to migrate %s", db_name, exc_info=True) logger.warning("Failed to migrate %s", db_name, exc_info=True)
@@ -279,11 +304,11 @@ def migrate_project_layout(project_dir: Path) -> list[str]:
for filename in simple_files: for filename in simple_files:
try: try:
old_file = project_dir / filename old_file = project_dir / filename
new_file = autocoder_dir / filename new_file = autoforge_dir / filename
if old_file.exists() and not new_file.exists(): if old_file.exists() and not new_file.exists():
shutil.move(str(old_file), str(new_file)) shutil.move(str(old_file), str(new_file))
migrated.append(f"{filename} -> .autocoder/{filename}") migrated.append(f"{filename} -> .autoforge/{filename}")
logger.info("Migrated %s -> .autocoder/%s", filename, filename) logger.info("Migrated %s -> .autoforge/%s", filename, filename)
except Exception: except Exception:
logger.warning("Failed to migrate %s", filename, exc_info=True) logger.warning("Failed to migrate %s", filename, exc_info=True)

View File

@@ -221,11 +221,11 @@ def main() -> None:
print("Use an absolute path or register the project first.") print("Use an absolute path or register the project first.")
return return
# Migrate project layout to .autocoder/ if needed (idempotent, safe) # Migrate project layout to .autoforge/ if needed (idempotent, safe)
from autocoder_paths import migrate_project_layout from autoforge_paths import migrate_project_layout
migrated = migrate_project_layout(project_dir) migrated = migrate_project_layout(project_dir)
if migrated: if migrated:
print(f"Migrated project files to .autocoder/: {', '.join(migrated)}", flush=True) print(f"Migrated project files to .autoforge/: {', '.join(migrated)}", flush=True)
# Parse batch testing feature IDs (comma-separated string -> list[int]) # Parse batch testing feature IDs (comma-separated string -> list[int])
testing_feature_ids: list[int] | None = None testing_feature_ids: list[int] | None = None

View File

@@ -382,7 +382,7 @@ def create_client(
project_dir.mkdir(parents=True, exist_ok=True) project_dir.mkdir(parents=True, exist_ok=True)
# Write settings to a file in the project directory # Write settings to a file in the project directory
from autocoder_paths import get_claude_settings_path from autoforge_paths import get_claude_settings_path
settings_file = get_claude_settings_path(project_dir) settings_file = get_claude_settings_path(project_dir)
settings_file.parent.mkdir(parents=True, exist_ok=True) settings_file.parent.mkdir(parents=True, exist_ok=True)
with open(settings_file, "w") as f: with open(settings_file, "w") as f:
@@ -450,7 +450,7 @@ def create_client(
# Build environment overrides for API endpoint configuration # Build environment overrides for API endpoint configuration
# These override system env vars for the Claude CLI subprocess, # These override system env vars for the Claude CLI subprocess,
# allowing AutoCoder to use alternative APIs (e.g., GLM) without # allowing AutoForge to use alternative APIs (e.g., GLM) without
# affecting the user's global Claude Code settings # affecting the user's global Claude Code settings
sdk_env = {} sdk_env = {}
for var in API_ENV_VARS: for var in API_ENV_VARS:

View File

@@ -7,7 +7,7 @@ subprocesses. Imported by both ``client.py`` (agent sessions) and
``server/services/chat_constants.py`` (chat sessions) to avoid maintaining ``server/services/chat_constants.py`` (chat sessions) to avoid maintaining
duplicate lists. duplicate lists.
These allow autocoder to use alternative API endpoints (Ollama, GLM, These allow autoforge to use alternative API endpoints (Ollama, GLM,
Vertex AI) without affecting the user's global Claude Code settings. Vertex AI) without affecting the user's global Claude Code settings.
""" """

View File

@@ -179,7 +179,7 @@ To see what you can reduce:
```bash ```bash
# Count commands by prefix # Count commands by prefix
grep "^ - name:" .autocoder/allowed_commands.yaml | \ grep "^ - name:" .autoforge/allowed_commands.yaml | \
sed 's/^ - name: //' | \ sed 's/^ - name: //' | \
cut -d' ' -f1 | \ cut -d' ' -f1 | \
sort | uniq -c | sort -rn sort | uniq -c | sort -rn

View File

@@ -1,4 +1,4 @@
# AutoCoder Security Configuration Examples # AutoForge Security Configuration Examples
This directory contains example configuration files for controlling which bash commands the autonomous coding agent can execute. This directory contains example configuration files for controlling which bash commands the autonomous coding agent can execute.
@@ -18,11 +18,11 @@ This directory contains example configuration files for controlling which bash c
### For a Single Project (Most Common) ### For a Single Project (Most Common)
When you create a new project with AutoCoder, it automatically creates: When you create a new project with AutoForge, it automatically creates:
```text ```text
my-project/ my-project/
.autocoder/ .autoforge/
allowed_commands.yaml ← Automatically created from template allowed_commands.yaml ← Automatically created from template
``` ```
@@ -34,17 +34,17 @@ If you want commands available across **all projects**, manually create:
```bash ```bash
# Copy the example to your home directory # Copy the example to your home directory
cp examples/org_config.yaml ~/.autocoder/config.yaml cp examples/org_config.yaml ~/.autoforge/config.yaml
# Edit it to add org-wide commands # Edit it to add org-wide commands
nano ~/.autocoder/config.yaml nano ~/.autoforge/config.yaml
``` ```
--- ---
## Project-Level Configuration ## Project-Level Configuration
**File:** `{project_dir}/.autocoder/allowed_commands.yaml` **File:** `{project_dir}/.autoforge/allowed_commands.yaml`
**Purpose:** Define commands needed for THIS specific project. **Purpose:** Define commands needed for THIS specific project.
@@ -82,7 +82,7 @@ commands:
## Organization-Level Configuration ## Organization-Level Configuration
**File:** `~/.autocoder/config.yaml` **File:** `~/.autoforge/config.yaml`
**Purpose:** Define commands and policies for ALL projects. **Purpose:** Define commands and policies for ALL projects.
@@ -127,13 +127,13 @@ When the agent tries to run a command, the system checks in this order:
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────┐
│ 2. ORG BLOCKLIST (~/.autocoder/config.yaml) │ │ 2. ORG BLOCKLIST (~/.autoforge/config.yaml) │
│ Commands you block organization-wide │ │ Commands you block organization-wide │
│ ❌ Projects CANNOT override these │ │ ❌ Projects CANNOT override these │
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────┐
│ 3. ORG ALLOWLIST (~/.autocoder/config.yaml) │ │ 3. ORG ALLOWLIST (~/.autoforge/config.yaml) │
│ Commands available to all projects │ │ Commands available to all projects │
│ ✅ Automatically available │ │ ✅ Automatically available │
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
@@ -145,7 +145,7 @@ When the agent tries to run a command, the system checks in this order:
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────┐
│ 5. PROJECT ALLOWLIST (.autocoder/allowed_commands) │ │ 5. PROJECT ALLOWLIST (.autoforge/allowed_commands) │
│ Project-specific commands │ │ Project-specific commands │
│ ✅ Available only to this project │ │ ✅ Available only to this project │
└─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘
@@ -195,7 +195,7 @@ Matches:
### iOS Development ### iOS Development
**Project config** (`.autocoder/allowed_commands.yaml`): **Project config** (`.autoforge/allowed_commands.yaml`):
```yaml ```yaml
version: 1 version: 1
commands: commands:
@@ -245,7 +245,7 @@ commands:
### Enterprise Organization (Restrictive) ### Enterprise Organization (Restrictive)
**Org config** (`~/.autocoder/config.yaml`): **Org config** (`~/.autoforge/config.yaml`):
```yaml ```yaml
version: 1 version: 1
@@ -265,7 +265,7 @@ blocked_commands:
### Startup Team (Permissive) ### Startup Team (Permissive)
**Org config** (`~/.autocoder/config.yaml`): **Org config** (`~/.autoforge/config.yaml`):
```yaml ```yaml
version: 1 version: 1
@@ -394,7 +394,7 @@ These commands are **NEVER allowed**, even with user approval:
**Solution:** Add the command to your project config: **Solution:** Add the command to your project config:
```yaml ```yaml
# In .autocoder/allowed_commands.yaml # In .autoforge/allowed_commands.yaml
commands: commands:
- name: X - name: X
description: What this command does description: What this command does
@@ -405,7 +405,7 @@ commands:
**Cause:** The command is in the org blocklist or hardcoded blocklist. **Cause:** The command is in the org blocklist or hardcoded blocklist.
**Solution:** **Solution:**
- If in org blocklist: Edit `~/.autocoder/config.yaml` to remove it - If in org blocklist: Edit `~/.autoforge/config.yaml` to remove it
- If in hardcoded blocklist: Cannot be allowed (by design) - If in hardcoded blocklist: Cannot be allowed (by design)
### Error: "Could not parse YAML config" ### Error: "Could not parse YAML config"
@@ -422,8 +422,8 @@ commands:
**Solution:** **Solution:**
1. Restart the agent (changes are loaded on startup) 1. Restart the agent (changes are loaded on startup)
2. Verify file location: 2. Verify file location:
- Project: `{project}/.autocoder/allowed_commands.yaml` - Project: `{project}/.autoforge/allowed_commands.yaml`
- Org: `~/.autocoder/config.yaml` (must be manually created) - Org: `~/.autoforge/config.yaml` (must be manually created)
3. Check YAML is valid (run through a YAML validator) 3. Check YAML is valid (run through a YAML validator)
--- ---
@@ -432,7 +432,7 @@ commands:
### Running the Tests ### Running the Tests
AutoCoder has comprehensive tests for the security system: AutoForge has comprehensive tests for the security system:
**Unit Tests** (136 tests - fast): **Unit Tests** (136 tests - fast):
```bash ```bash
@@ -481,7 +481,7 @@ python start.py
cd path/to/security-test cd path/to/security-test
# Edit the config # Edit the config
nano .autocoder/allowed_commands.yaml nano .autoforge/allowed_commands.yaml
``` ```
**3. Add a test command (e.g., Swift):** **3. Add a test command (e.g., Swift):**
@@ -509,7 +509,7 @@ Or:
```text ```text
Command 'wget' is not allowed. Command 'wget' is not allowed.
To allow this command: To allow this command:
1. Add to .autocoder/allowed_commands.yaml for this project, OR 1. Add to .autoforge/allowed_commands.yaml for this project, OR
2. Request mid-session approval (the agent can ask) 2. Request mid-session approval (the agent can ask)
``` ```

View File

@@ -1,6 +1,6 @@
# Organization-Level AutoCoder Configuration # Organization-Level AutoForge Configuration
# ============================================ # ============================================
# Location: ~/.autocoder/config.yaml # Location: ~/.autoforge/config.yaml
# #
# IMPORTANT: This file is OPTIONAL and must be manually created by you. # IMPORTANT: This file is OPTIONAL and must be manually created by you.
# It does NOT exist by default. # It does NOT exist by default.
@@ -22,7 +22,7 @@ version: 1
# Organization-Wide Allowed Commands # Organization-Wide Allowed Commands
# ========================================== # ==========================================
# These commands become available to ALL projects automatically. # These commands become available to ALL projects automatically.
# Projects don't need to add them to their own .autocoder/allowed_commands.yaml # Projects don't need to add them to their own .autoforge/allowed_commands.yaml
# #
# By default, this is empty. Uncomment and add commands as needed. # By default, this is empty. Uncomment and add commands as needed.
@@ -122,7 +122,7 @@ approval_timeout_minutes: 5
# Default commands: npm, git, curl, ls, cat, etc. # Default commands: npm, git, curl, ls, cat, etc.
# Always available to all projects. # Always available to all projects.
# #
# 5. Project Allowed Commands (.autocoder/allowed_commands.yaml) # 5. Project Allowed Commands (.autoforge/allowed_commands.yaml)
# Project-specific commands defined in each project. # Project-specific commands defined in each project.
# LOWEST PRIORITY (can't override blocks above). # LOWEST PRIORITY (can't override blocks above).
# #
@@ -165,7 +165,7 @@ approval_timeout_minutes: 5
# ========================================== # ==========================================
# To Create This File # To Create This File
# ========================================== # ==========================================
# 1. Copy this example to: ~/.autocoder/config.yaml # 1. Copy this example to: ~/.autoforge/config.yaml
# 2. Uncomment and customize the sections you need # 2. Uncomment and customize the sections you need
# 3. Leave empty lists if you don't need org-level controls # 3. Leave empty lists if you don't need org-level controls
# #

View File

@@ -1,12 +1,12 @@
# Project-Specific Allowed Commands # Project-Specific Allowed Commands
# ================================== # ==================================
# Location: {project_dir}/.autocoder/allowed_commands.yaml # Location: {project_dir}/.autoforge/allowed_commands.yaml
# #
# This file defines bash commands that the autonomous coding agent can use # This file defines bash commands that the autonomous coding agent can use
# for THIS SPECIFIC PROJECT, beyond the default allowed commands. # for THIS SPECIFIC PROJECT, beyond the default allowed commands.
# #
# When you create a new project, AutoCoder automatically creates this file # When you create a new project, AutoForge automatically creates this file
# in your project's .autocoder/ directory. You can customize it for your # in your project's .autoforge/ directory. You can customize it for your
# project's specific needs (iOS, Rust, Python, etc.). # project's specific needs (iOS, Rust, Python, etc.).
version: 1 version: 1
@@ -115,7 +115,7 @@ commands: []
# Limits: # Limits:
# - Maximum 100 commands per project # - Maximum 100 commands per project
# - Commands in the blocklist (sudo, dd, shutdown, etc.) can NEVER be allowed # - Commands in the blocklist (sudo, dd, shutdown, etc.) can NEVER be allowed
# - Org-level blocked commands (see ~/.autocoder/config.yaml) cannot be overridden # - Org-level blocked commands (see ~/.autoforge/config.yaml) cannot be overridden
# #
# Default Allowed Commands (always available): # Default Allowed Commands (always available):
# File operations: ls, cat, head, tail, wc, grep, cp, mkdir, mv, rm, touch # File operations: ls, cat, head, tail, wc, grep, cp, mkdir, mv, rm, touch

View File

@@ -40,11 +40,11 @@ from server.utils.process_utils import kill_process_tree
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Root directory of autocoder (where this script and autonomous_agent_demo.py live) # Root directory of autoforge (where this script and autonomous_agent_demo.py live)
AUTOCODER_ROOT = Path(__file__).parent.resolve() AUTOFORGE_ROOT = Path(__file__).parent.resolve()
# Debug log file path # Debug log file path
DEBUG_LOG_FILE = AUTOCODER_ROOT / "orchestrator_debug.log" DEBUG_LOG_FILE = AUTOFORGE_ROOT / "orchestrator_debug.log"
class DebugLogger: class DebugLogger:
@@ -823,7 +823,7 @@ class ParallelOrchestrator:
cmd = [ cmd = [
sys.executable, sys.executable,
"-u", # Force unbuffered stdout/stderr "-u", # Force unbuffered stdout/stderr
str(AUTOCODER_ROOT / "autonomous_agent_demo.py"), str(AUTOFORGE_ROOT / "autonomous_agent_demo.py"),
"--project-dir", str(self.project_dir), "--project-dir", str(self.project_dir),
"--max-iterations", "1", "--max-iterations", "1",
"--agent-type", "coding", "--agent-type", "coding",
@@ -889,7 +889,7 @@ class ParallelOrchestrator:
cmd = [ cmd = [
sys.executable, sys.executable,
"-u", "-u",
str(AUTOCODER_ROOT / "autonomous_agent_demo.py"), str(AUTOFORGE_ROOT / "autonomous_agent_demo.py"),
"--project-dir", str(self.project_dir), "--project-dir", str(self.project_dir),
"--max-iterations", "1", "--max-iterations", "1",
"--agent-type", "coding", "--agent-type", "coding",
@@ -992,7 +992,7 @@ class ParallelOrchestrator:
cmd = [ cmd = [
sys.executable, sys.executable,
"-u", "-u",
str(AUTOCODER_ROOT / "autonomous_agent_demo.py"), str(AUTOFORGE_ROOT / "autonomous_agent_demo.py"),
"--project-dir", str(self.project_dir), "--project-dir", str(self.project_dir),
"--max-iterations", "1", "--max-iterations", "1",
"--agent-type", "testing", "--agent-type", "testing",
@@ -1053,7 +1053,7 @@ class ParallelOrchestrator:
cmd = [ cmd = [
sys.executable, "-u", sys.executable, "-u",
str(AUTOCODER_ROOT / "autonomous_agent_demo.py"), str(AUTOFORGE_ROOT / "autonomous_agent_demo.py"),
"--project-dir", str(self.project_dir), "--project-dir", str(self.project_dir),
"--agent-type", "initializer", "--agent-type", "initializer",
"--max-iterations", "1", "--max-iterations", "1",
@@ -1073,7 +1073,7 @@ class ParallelOrchestrator:
"text": True, "text": True,
"encoding": "utf-8", "encoding": "utf-8",
"errors": "replace", "errors": "replace",
"cwd": str(AUTOCODER_ROOT), "cwd": str(AUTOFORGE_ROOT),
"env": {**os.environ, "PYTHONUNBUFFERED": "1"}, "env": {**os.environ, "PYTHONUNBUFFERED": "1"},
} }
if sys.platform == "win32": if sys.platform == "win32":

View File

@@ -46,7 +46,7 @@ def has_features(project_dir: Path) -> bool:
return True return True
# Check SQLite database # Check SQLite database
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
return False return False
@@ -72,7 +72,7 @@ def count_passing_tests(project_dir: Path) -> tuple[int, int, int]:
Returns: Returns:
(passing_count, in_progress_count, total_count) (passing_count, in_progress_count, total_count)
""" """
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
return 0, 0, 0 return 0, 0, 0
@@ -122,7 +122,7 @@ def get_all_passing_features(project_dir: Path) -> list[dict]:
Returns: Returns:
List of dicts with id, category, name for each passing feature List of dicts with id, category, name for each passing feature
""" """
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
return [] return []
@@ -147,7 +147,7 @@ def send_progress_webhook(passing: int, total: int, project_dir: Path) -> None:
if not WEBHOOK_URL: if not WEBHOOK_URL:
return # Webhook not configured return # Webhook not configured
from autocoder_paths import get_progress_cache_path from autoforge_paths import get_progress_cache_path
cache_file = get_progress_cache_path(project_dir) cache_file = get_progress_cache_path(project_dir)
previous = 0 previous = 0
previous_passing_ids = set() previous_passing_ids = set()

View File

@@ -19,7 +19,7 @@ TEMPLATES_DIR = Path(__file__).parent / ".claude" / "templates"
def get_project_prompts_dir(project_dir: Path) -> Path: def get_project_prompts_dir(project_dir: Path) -> Path:
"""Get the prompts directory for a specific project.""" """Get the prompts directory for a specific project."""
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
return get_prompts_dir(project_dir) return get_prompts_dir(project_dir)
@@ -315,9 +315,9 @@ def scaffold_project_prompts(project_dir: Path) -> Path:
project_prompts = get_project_prompts_dir(project_dir) project_prompts = get_project_prompts_dir(project_dir)
project_prompts.mkdir(parents=True, exist_ok=True) project_prompts.mkdir(parents=True, exist_ok=True)
# Create .autocoder directory with .gitignore for runtime files # Create .autoforge directory with .gitignore for runtime files
from autocoder_paths import ensure_autocoder_dir from autoforge_paths import ensure_autoforge_dir
autocoder_dir = ensure_autocoder_dir(project_dir) autoforge_dir = ensure_autoforge_dir(project_dir)
# Define template mappings: (source_template, destination_name) # Define template mappings: (source_template, destination_name)
templates = [ templates = [
@@ -340,14 +340,14 @@ def scaffold_project_prompts(project_dir: Path) -> Path:
except (OSError, PermissionError) as e: except (OSError, PermissionError) as e:
print(f" Warning: Could not copy {dest_name}: {e}") print(f" Warning: Could not copy {dest_name}: {e}")
# Copy allowed_commands.yaml template to .autocoder/ # Copy allowed_commands.yaml template to .autoforge/
examples_dir = Path(__file__).parent / "examples" examples_dir = Path(__file__).parent / "examples"
allowed_commands_template = examples_dir / "project_allowed_commands.yaml" allowed_commands_template = examples_dir / "project_allowed_commands.yaml"
allowed_commands_dest = autocoder_dir / "allowed_commands.yaml" allowed_commands_dest = autoforge_dir / "allowed_commands.yaml"
if allowed_commands_template.exists() and not allowed_commands_dest.exists(): if allowed_commands_template.exists() and not allowed_commands_dest.exists():
try: try:
shutil.copy(allowed_commands_template, allowed_commands_dest) shutil.copy(allowed_commands_template, allowed_commands_dest)
copied_files.append(".autocoder/allowed_commands.yaml") copied_files.append(".autoforge/allowed_commands.yaml")
except (OSError, PermissionError) as e: except (OSError, PermissionError) as e:
print(f" Warning: Could not copy allowed_commands.yaml: {e}") print(f" Warning: Could not copy allowed_commands.yaml: {e}")

View File

@@ -3,7 +3,7 @@ Project Registry Module
======================= =======================
Cross-platform project registry for storing project name to path mappings. Cross-platform project registry for storing project name to path mappings.
Uses SQLite database stored at ~/.autocoder/registry.db. Uses SQLite database stored at ~/.autoforge/registry.db.
""" """
import logging import logging
@@ -23,6 +23,22 @@ from sqlalchemy.orm import DeclarativeBase, sessionmaker
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _migrate_registry_dir() -> None:
"""Migrate ~/.autocoder/ to ~/.autoforge/ if needed.
Provides backward compatibility by automatically renaming the old
config directory to the new location on first access.
"""
old_dir = Path.home() / ".autocoder"
new_dir = Path.home() / ".autoforge"
if old_dir.exists() and not new_dir.exists():
try:
old_dir.rename(new_dir)
logger.info("Migrated registry directory: ~/.autocoder/ -> ~/.autoforge/")
except Exception:
logger.warning("Failed to migrate ~/.autocoder/ to ~/.autoforge/", exc_info=True)
# ============================================================================= # =============================================================================
# Model Configuration (Single Source of Truth) # Model Configuration (Single Source of Truth)
# ============================================================================= # =============================================================================
@@ -120,12 +136,15 @@ _engine_lock = threading.Lock()
def get_config_dir() -> Path: def get_config_dir() -> Path:
""" """
Get the config directory: ~/.autocoder/ Get the config directory: ~/.autoforge/
Automatically migrates from ~/.autocoder/ if needed.
Returns: Returns:
Path to ~/.autocoder/ (created if it doesn't exist) Path to ~/.autoforge/ (created if it doesn't exist)
""" """
config_dir = Path.home() / ".autocoder" _migrate_registry_dir()
config_dir = Path.home() / ".autoforge"
config_dir.mkdir(parents=True, exist_ok=True) config_dir.mkdir(parents=True, exist_ok=True)
return config_dir return config_dir

View File

@@ -553,14 +553,23 @@ def get_org_config_path() -> Path:
Get the organization-level config file path. Get the organization-level config file path.
Returns: Returns:
Path to ~/.autocoder/config.yaml Path to ~/.autoforge/config.yaml (falls back to ~/.autocoder/config.yaml)
""" """
return Path.home() / ".autocoder" / "config.yaml" new_path = Path.home() / ".autoforge" / "config.yaml"
if new_path.exists():
return new_path
# Backward compatibility: check old location
old_path = Path.home() / ".autocoder" / "config.yaml"
if old_path.exists():
return old_path
return new_path
def load_org_config() -> Optional[dict]: def load_org_config() -> Optional[dict]:
""" """
Load organization-level config from ~/.autocoder/config.yaml. Load organization-level config from ~/.autoforge/config.yaml.
Falls back to ~/.autocoder/config.yaml for backward compatibility.
Returns: Returns:
Dict with parsed org config, or None if file doesn't exist or is invalid Dict with parsed org config, or None if file doesn't exist or is invalid
@@ -630,7 +639,10 @@ def load_project_commands(project_dir: Path) -> Optional[dict]:
Returns: Returns:
Dict with parsed YAML config, or None if file doesn't exist or is invalid Dict with parsed YAML config, or None if file doesn't exist or is invalid
""" """
config_path = project_dir.resolve() / ".autocoder" / "allowed_commands.yaml" # Check new location first, fall back to old for backward compatibility
config_path = project_dir.resolve() / ".autoforge" / "allowed_commands.yaml"
if not config_path.exists():
config_path = project_dir.resolve() / ".autocoder" / "allowed_commands.yaml"
if not config_path.exists(): if not config_path.exists():
return None return None
@@ -909,7 +921,7 @@ async def bash_security_hook(input_data, tool_use_id=None, context=None):
# Provide helpful error message with config hint # Provide helpful error message with config hint
error_msg = f"Command '{cmd}' is not allowed.\n" error_msg = f"Command '{cmd}' is not allowed.\n"
error_msg += "To allow this command:\n" error_msg += "To allow this command:\n"
error_msg += " 1. Add to .autocoder/allowed_commands.yaml for this project, OR\n" error_msg += " 1. Add to .autoforge/allowed_commands.yaml for this project, OR\n"
error_msg += " 2. Request mid-session approval (the agent can ask)\n" error_msg += " 2. Request mid-session approval (the agent can ask)\n"
error_msg += "Note: Some commands are blocked at org-level and cannot be overridden." error_msg += "Note: Some commands are blocked at org-level and cannot be overridden."
return { return {

View File

@@ -94,7 +94,7 @@ logger = logging.getLogger(__name__)
# Check if remote access is enabled via environment variable # Check if remote access is enabled via environment variable
# Set by start_ui.py when --host is not 127.0.0.1 # Set by start_ui.py when --host is not 127.0.0.1
ALLOW_REMOTE = os.environ.get("AUTOCODER_ALLOW_REMOTE", "").lower() in ("1", "true", "yes") ALLOW_REMOTE = os.environ.get("AUTOFORGE_ALLOW_REMOTE", "").lower() in ("1", "true", "yes")
if ALLOW_REMOTE: if ALLOW_REMOTE:
logger.warning( logger.warning(
@@ -133,7 +133,7 @@ else:
if not ALLOW_REMOTE: if not ALLOW_REMOTE:
@app.middleware("http") @app.middleware("http")
async def require_localhost(request: Request, call_next): async def require_localhost(request: Request, call_next):
"""Only allow requests from localhost (disabled when AUTOCODER_ALLOW_REMOTE=1).""" """Only allow requests from localhost (disabled when AUTOFORGE_ALLOW_REMOTE=1)."""
client_host = request.client.host if request.client else None client_host = request.client.host if request.client else None
# Allow localhost connections # Allow localhost connections

View File

@@ -121,7 +121,7 @@ async def expand_project_websocket(websocket: WebSocket, project_name: str):
return return
# Verify project has app_spec.txt # Verify project has app_spec.txt
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
spec_path = get_prompts_dir(project_dir) / "app_spec.txt" spec_path = get_prompts_dir(project_dir) / "app_spec.txt"
if not spec_path.exists(): if not spec_path.exists():
await websocket.close(code=4004, reason="Project has no spec. Create spec first.") await websocket.close(code=4004, reason="Project has no spec. Create spec first.")

View File

@@ -126,7 +126,7 @@ async def list_features(project_name: str):
if not project_dir.exists(): if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found") raise HTTPException(status_code=404, detail="Project directory not found")
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
return FeatureListResponse(pending=[], in_progress=[], done=[]) return FeatureListResponse(pending=[], in_progress=[], done=[])
@@ -322,7 +322,7 @@ async def get_dependency_graph(project_name: str):
if not project_dir.exists(): if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found") raise HTTPException(status_code=404, detail="Project directory not found")
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
return DependencyGraphResponse(nodes=[], edges=[]) return DependencyGraphResponse(nodes=[], edges=[])
@@ -388,7 +388,7 @@ async def get_feature(project_name: str, feature_id: int):
if not project_dir.exists(): if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found") raise HTTPException(status_code=404, detail="Project directory not found")
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_file = get_features_db_path(project_dir) db_file = get_features_db_path(project_dir)
if not db_file.exists(): if not db_file.exists():
raise HTTPException(status_code=404, detail="No features database found") raise HTTPException(status_code=404, detail="No features database found")

View File

@@ -276,7 +276,7 @@ async def delete_project(name: str, delete_files: bool = False):
raise HTTPException(status_code=404, detail=f"Project '{name}' not found") raise HTTPException(status_code=404, detail=f"Project '{name}' not found")
# Check if agent is running # Check if agent is running
from autocoder_paths import has_agent_running from autoforge_paths import has_agent_running
if has_agent_running(project_dir): if has_agent_running(project_dir):
raise HTTPException( raise HTTPException(
status_code=409, status_code=409,
@@ -407,7 +407,7 @@ async def reset_project(name: str, full_reset: bool = False):
raise HTTPException(status_code=404, detail="Project directory not found") raise HTTPException(status_code=404, detail="Project directory not found")
# Check if agent is running # Check if agent is running
from autocoder_paths import has_agent_running from autoforge_paths import has_agent_running
if has_agent_running(project_dir): if has_agent_running(project_dir):
raise HTTPException( raise HTTPException(
status_code=409, status_code=409,
@@ -424,7 +424,7 @@ async def reset_project(name: str, full_reset: bool = False):
deleted_files: list[str] = [] deleted_files: list[str] = []
from autocoder_paths import ( from autoforge_paths import (
get_assistant_db_path, get_assistant_db_path,
get_claude_assistant_settings_path, get_claude_assistant_settings_path,
get_claude_settings_path, get_claude_settings_path,
@@ -466,7 +466,7 @@ async def reset_project(name: str, full_reset: bool = False):
# Full reset: also delete prompts directory # Full reset: also delete prompts directory
if full_reset: if full_reset:
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
# Delete prompts from both possible locations # Delete prompts from both possible locations
for prompts_dir in [get_prompts_dir(project_dir), project_dir / "prompts"]: for prompts_dir in [get_prompts_dir(project_dir), project_dir / "prompts"]:
if prompts_dir.exists(): if prompts_dir.exists():

View File

@@ -105,7 +105,7 @@ async def get_spec_file_status(project_name: str):
if not project_dir.exists(): if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found") raise HTTPException(status_code=404, detail="Project directory not found")
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
status_file = get_prompts_dir(project_dir) / ".spec_status.json" status_file = get_prompts_dir(project_dir) / ".spec_status.json"
if not status_file.exists(): if not status_file.exists():

View File

@@ -64,7 +64,7 @@ def get_system_prompt(project_name: str, project_dir: Path) -> str:
"""Generate the system prompt for the assistant with project context.""" """Generate the system prompt for the assistant with project context."""
# Try to load app_spec.txt for context # Try to load app_spec.txt for context
app_spec_content = "" app_spec_content = ""
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
app_spec_path = get_prompts_dir(project_dir) / "app_spec.txt" app_spec_path = get_prompts_dir(project_dir) / "app_spec.txt"
if app_spec_path.exists(): if app_spec_path.exists():
try: try:
@@ -224,7 +224,7 @@ class AssistantChatSession:
"allow": permissions_list, "allow": permissions_list,
}, },
} }
from autocoder_paths import get_claude_assistant_settings_path from autoforge_paths import get_claude_assistant_settings_path
settings_file = get_claude_assistant_settings_path(self.project_dir) settings_file = get_claude_assistant_settings_path(self.project_dir)
settings_file.parent.mkdir(parents=True, exist_ok=True) settings_file.parent.mkdir(parents=True, exist_ok=True)
with open(settings_file, "w") as f: with open(settings_file, "w") as f:

View File

@@ -64,7 +64,7 @@ class ConversationMessage(Base):
def get_db_path(project_dir: Path) -> Path: def get_db_path(project_dir: Path) -> Path:
"""Get the path to the assistant database for a project.""" """Get the path to the assistant database for a project."""
from autocoder_paths import get_assistant_db_path from autoforge_paths import get_assistant_db_path
return get_assistant_db_path(project_dir) return get_assistant_db_path(project_dir)

View File

@@ -14,7 +14,7 @@ from pathlib import Path
from typing import AsyncGenerator from typing import AsyncGenerator
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# Root directory of the autocoder project (repository root). # Root directory of the autoforge project (repository root).
# Used throughout the server package whenever the repo root is needed. # Used throughout the server package whenever the repo root is needed.
# ------------------------------------------------------------------- # -------------------------------------------------------------------
ROOT_DIR = Path(__file__).parent.parent.parent ROOT_DIR = Path(__file__).parent.parent.parent

View File

@@ -115,7 +115,7 @@ class DevServerProcessManager:
self._callbacks_lock = threading.Lock() self._callbacks_lock = threading.Lock()
# Lock file to prevent multiple instances (stored in project directory) # Lock file to prevent multiple instances (stored in project directory)
from autocoder_paths import get_devserver_lock_path from autoforge_paths import get_devserver_lock_path
self.lock_file = get_devserver_lock_path(self.project_dir) self.lock_file = get_devserver_lock_path(self.project_dir)
@property @property
@@ -504,10 +504,10 @@ def cleanup_orphaned_devserver_locks() -> int:
continue continue
# Check both legacy and new locations for lock files # Check both legacy and new locations for lock files
from autocoder_paths import get_autocoder_dir from autoforge_paths import get_autoforge_dir
lock_locations = [ lock_locations = [
project_path / ".devserver.lock", project_path / ".devserver.lock",
get_autocoder_dir(project_path) / ".devserver.lock", get_autoforge_dir(project_path) / ".devserver.lock",
] ]
lock_file = None lock_file = None
for candidate in lock_locations: for candidate in lock_locations:

View File

@@ -103,7 +103,7 @@ class ExpandChatSession:
return return
# Verify project has existing spec # Verify project has existing spec
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
spec_path = get_prompts_dir(self.project_dir) / "app_spec.txt" spec_path = get_prompts_dir(self.project_dir) / "app_spec.txt"
if not spec_path.exists(): if not spec_path.exists():
yield { yield {
@@ -142,7 +142,7 @@ class ExpandChatSession:
], ],
}, },
} }
from autocoder_paths import get_expand_settings_path from autoforge_paths import get_expand_settings_path
settings_file = get_expand_settings_path(self.project_dir, uuid.uuid4().hex) settings_file = get_expand_settings_path(self.project_dir, uuid.uuid4().hex)
settings_file.parent.mkdir(parents=True, exist_ok=True) settings_file.parent.mkdir(parents=True, exist_ok=True)
self._settings_file = settings_file self._settings_file = settings_file

View File

@@ -92,7 +92,7 @@ class AgentProcessManager:
self._callbacks_lock = threading.Lock() self._callbacks_lock = threading.Lock()
# Lock file to prevent multiple instances (stored in project directory) # Lock file to prevent multiple instances (stored in project directory)
from autocoder_paths import get_agent_lock_path from autoforge_paths import get_agent_lock_path
self.lock_file = get_agent_lock_path(self.project_dir) self.lock_file = get_agent_lock_path(self.project_dir)
@property @property
@@ -587,10 +587,10 @@ def cleanup_orphaned_locks() -> int:
continue continue
# Check both legacy and new locations for lock files # Check both legacy and new locations for lock files
from autocoder_paths import get_autocoder_dir from autoforge_paths import get_autoforge_dir
lock_locations = [ lock_locations = [
project_path / ".agent.lock", project_path / ".agent.lock",
get_autocoder_dir(project_path) / ".agent.lock", get_autoforge_dir(project_path) / ".agent.lock",
] ]
lock_file = None lock_file = None
for candidate in lock_locations: for candidate in lock_locations:

View File

@@ -6,7 +6,7 @@ Handles project type detection and dev command configuration.
Detects project types by scanning for configuration files and provides Detects project types by scanning for configuration files and provides
default or custom dev commands for each project. default or custom dev commands for each project.
Configuration is stored in {project_dir}/.autocoder/config.json. Configuration is stored in {project_dir}/.autoforge/config.json.
""" """
import json import json
@@ -88,13 +88,22 @@ def _get_config_path(project_dir: Path) -> Path:
""" """
Get the path to the project config file. Get the path to the project config file.
Checks the new .autoforge/ location first, falls back to .autocoder/
for backward compatibility.
Args: Args:
project_dir: Path to the project directory. project_dir: Path to the project directory.
Returns: Returns:
Path to the .autocoder/config.json file. Path to the config.json file in the appropriate directory.
""" """
return project_dir / ".autocoder" / "config.json" new_path = project_dir / ".autoforge" / "config.json"
if new_path.exists():
return new_path
old_path = project_dir / ".autocoder" / "config.json"
if old_path.exists():
return old_path
return new_path
def _load_config(project_dir: Path) -> dict: def _load_config(project_dir: Path) -> dict:
@@ -137,7 +146,7 @@ def _save_config(project_dir: Path, config: dict) -> None:
""" """
Save the project configuration to disk. Save the project configuration to disk.
Creates the .autocoder directory if it doesn't exist. Creates the .autoforge directory if it doesn't exist.
Args: Args:
project_dir: Path to the project directory. project_dir: Path to the project directory.
@@ -148,7 +157,7 @@ def _save_config(project_dir: Path, config: dict) -> None:
""" """
config_path = _get_config_path(project_dir) config_path = _get_config_path(project_dir)
# Ensure the .autocoder directory exists # Ensure the .autoforge directory exists
config_path.parent.mkdir(parents=True, exist_ok=True) config_path.parent.mkdir(parents=True, exist_ok=True)
try: try:
@@ -408,11 +417,11 @@ def clear_dev_command(project_dir: Path) -> None:
config_path.unlink(missing_ok=True) config_path.unlink(missing_ok=True)
logger.info("Removed empty config file for %s", project_dir.name) logger.info("Removed empty config file for %s", project_dir.name)
# Also remove .autocoder directory if empty # Also remove .autoforge directory if empty
autocoder_dir = config_path.parent autoforge_dir = config_path.parent
if autocoder_dir.exists() and not any(autocoder_dir.iterdir()): if autoforge_dir.exists() and not any(autoforge_dir.iterdir()):
autocoder_dir.rmdir() autoforge_dir.rmdir()
logger.debug("Removed empty .autocoder directory for %s", project_dir.name) logger.debug("Removed empty .autoforge directory for %s", project_dir.name)
except OSError as e: except OSError as e:
logger.warning("Failed to clean up config for %s: %s", project_dir.name, e) logger.warning("Failed to clean up config for %s: %s", project_dir.name, e)
else: else:

View File

@@ -92,7 +92,7 @@ class SchedulerService:
async def _load_project_schedules(self, project_name: str, project_dir: Path) -> int: async def _load_project_schedules(self, project_name: str, project_dir: Path) -> int:
"""Load schedules for a single project. Returns count of schedules loaded.""" """Load schedules for a single project. Returns count of schedules loaded."""
from api.database import Schedule, create_database from api.database import Schedule, create_database
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_path = get_features_db_path(project_dir) db_path = get_features_db_path(project_dir)
if not db_path.exists(): if not db_path.exists():
@@ -568,7 +568,7 @@ class SchedulerService:
): ):
"""Check if a project should be started on server startup.""" """Check if a project should be started on server startup."""
from api.database import Schedule, ScheduleOverride, create_database from api.database import Schedule, ScheduleOverride, create_database
from autocoder_paths import get_features_db_path from autoforge_paths import get_features_db_path
db_path = get_features_db_path(project_dir) db_path = get_features_db_path(project_dir)
if not db_path.exists(): if not db_path.exists():

View File

@@ -95,7 +95,7 @@ class SpecChatSession:
# Delete app_spec.txt so Claude can create it fresh # Delete app_spec.txt so Claude can create it fresh
# The SDK requires reading existing files before writing, but app_spec.txt is created new # The SDK requires reading existing files before writing, but app_spec.txt is created new
# Note: We keep initializer_prompt.md so Claude can read and update the template # Note: We keep initializer_prompt.md so Claude can read and update the template
from autocoder_paths import get_prompts_dir from autoforge_paths import get_prompts_dir
prompts_dir = get_prompts_dir(self.project_dir) prompts_dir = get_prompts_dir(self.project_dir)
app_spec_path = prompts_dir / "app_spec.txt" app_spec_path = prompts_dir / "app_spec.txt"
if app_spec_path.exists(): if app_spec_path.exists():
@@ -116,7 +116,7 @@ class SpecChatSession:
], ],
}, },
} }
from autocoder_paths import get_claude_settings_path from autoforge_paths import get_claude_settings_path
settings_file = get_claude_settings_path(self.project_dir) settings_file = get_claude_settings_path(self.project_dir)
settings_file.parent.mkdir(parents=True, exist_ok=True) settings_file.parent.mkdir(parents=True, exist_ok=True)
with open(settings_file, "w") as f: with open(settings_file, "w") as f:

View File

@@ -3,7 +3,7 @@ cd /d "%~dp0"
echo. echo.
echo ======================================== echo ========================================
echo Autonomous Coding Agent echo AutoForge - Autonomous Coding Agent
echo ======================================== echo ========================================
echo. echo.

View File

@@ -82,7 +82,7 @@ def get_existing_projects() -> list[tuple[str, Path]]:
def display_menu(projects: list[tuple[str, Path]]) -> None: def display_menu(projects: list[tuple[str, Path]]) -> None:
"""Display the main menu.""" """Display the main menu."""
print("\n" + "=" * 50) print("\n" + "=" * 50)
print(" Autonomous Coding Agent Launcher") print(" AutoForge - Autonomous Coding Agent")
print("=" * 50) print("=" * 50)
print("\n[1] Create new project") print("\n[1] Create new project")

View File

@@ -3,7 +3,7 @@ cd "$(dirname "$0")"
echo "" echo ""
echo "========================================" echo "========================================"
echo " Autonomous Coding Agent" echo " AutoForge - Autonomous Coding Agent"
echo "========================================" echo "========================================"
echo "" echo ""

View File

@@ -1,11 +1,11 @@
@echo off @echo off
cd /d "%~dp0" cd /d "%~dp0"
REM AutoCoder UI Launcher for Windows REM AutoForge UI Launcher for Windows
REM This script launches the web UI for the autonomous coding agent. REM This script launches the web UI for the autonomous coding agent.
echo. echo.
echo ==================================== echo ====================================
echo AutoCoder UI echo AutoForge UI
echo ==================================== echo ====================================
echo. echo.

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
AutoCoder UI Launcher AutoForge UI Launcher
===================== =====================
Automated launcher that handles all setup: Automated launcher that handles all setup:
@@ -265,7 +265,7 @@ def start_dev_server(port: int, host: str = "127.0.0.1") -> tuple:
# Set environment for remote access if needed # Set environment for remote access if needed
env = os.environ.copy() env = os.environ.copy()
if host != "127.0.0.1": if host != "127.0.0.1":
env["AUTOCODER_ALLOW_REMOTE"] = "1" env["AUTOFORGE_ALLOW_REMOTE"] = "1"
# Start FastAPI # Start FastAPI
backend = subprocess.Popen([ backend = subprocess.Popen([
@@ -297,7 +297,7 @@ def start_production_server(port: int, host: str = "127.0.0.1"):
# Enable remote access in server if not localhost # Enable remote access in server if not localhost
if host != "127.0.0.1": if host != "127.0.0.1":
env["AUTOCODER_ALLOW_REMOTE"] = "1" env["AUTOFORGE_ALLOW_REMOTE"] = "1"
# NOTE: --reload is NOT used because on Windows it breaks asyncio subprocess # NOTE: --reload is NOT used because on Windows it breaks asyncio subprocess
# support (uvicorn's reload worker doesn't inherit the ProactorEventLoop policy). # support (uvicorn's reload worker doesn't inherit the ProactorEventLoop policy).
@@ -313,7 +313,7 @@ def start_production_server(port: int, host: str = "127.0.0.1"):
def main() -> None: def main() -> None:
"""Main entry point.""" """Main entry point."""
parser = argparse.ArgumentParser(description="AutoCoder UI Launcher") parser = argparse.ArgumentParser(description="AutoForge UI Launcher")
parser.add_argument("--dev", action="store_true", help="Run in development mode with Vite hot reload") parser.add_argument("--dev", action="store_true", help="Run in development mode with Vite hot reload")
parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)") parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
parser.add_argument("--port", type=int, default=None, help="Port to bind to (default: auto-detect from 8888)") parser.add_argument("--port", type=int, default=None, help="Port to bind to (default: auto-detect from 8888)")
@@ -328,7 +328,7 @@ def main() -> None:
print(" SECURITY WARNING") print(" SECURITY WARNING")
print("!" * 50) print("!" * 50)
print(f" Remote access enabled on host: {host}") print(f" Remote access enabled on host: {host}")
print(" The AutoCoder UI will be accessible from other machines.") print(" The AutoForge UI will be accessible from other machines.")
print(" Ensure you understand the security implications:") print(" Ensure you understand the security implications:")
print(" - The agent has file system access to project directories") print(" - The agent has file system access to project directories")
print(" - The API can start/stop agents and modify files") print(" - The API can start/stop agents and modify files")
@@ -336,7 +336,7 @@ def main() -> None:
print("!" * 50 + "\n") print("!" * 50 + "\n")
print("=" * 50) print("=" * 50)
print(" AutoCoder UI Setup") print(" AutoForge UI Setup")
print("=" * 50) print("=" * 50)
total_steps = 6 if not dev_mode else 5 total_steps = 6 if not dev_mode else 5

View File

@@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
cd "$(dirname "$0")" cd "$(dirname "$0")"
# AutoCoder UI Launcher for Unix/Linux/macOS # AutoForge UI Launcher for Unix/Linux/macOS
# This script launches the web UI for the autonomous coding agent. # This script launches the web UI for the autonomous coding agent.
echo "" echo ""
echo "====================================" echo "===================================="
echo " AutoCoder UI" echo " AutoForge UI"
echo "====================================" echo "===================================="
echo "" echo ""

View File

@@ -273,11 +273,11 @@ def test_yaml_loading():
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
# Test 1: Valid YAML # Test 1: Valid YAML
config_path = autocoder_dir / "allowed_commands.yaml" config_path = autoforge_dir / "allowed_commands.yaml"
config_path.write_text("""version: 1 config_path.write_text("""version: 1
commands: commands:
- name: swift - name: swift
@@ -297,7 +297,7 @@ commands:
failed += 1 failed += 1
# Test 2: Missing file returns None # Test 2: Missing file returns None
(project_dir / ".autocoder" / "allowed_commands.yaml").unlink() (project_dir / ".autoforge" / "allowed_commands.yaml").unlink()
config = load_project_commands(project_dir) config = load_project_commands(project_dir)
if config is None: if config is None:
print(" PASS: Missing file returns None") print(" PASS: Missing file returns None")
@@ -407,11 +407,11 @@ def test_project_commands():
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
# Create a config with Swift commands # Create a config with Swift commands
config_path = autocoder_dir / "allowed_commands.yaml" config_path = autoforge_dir / "allowed_commands.yaml"
config_path.write_text("""version: 1 config_path.write_text("""version: 1
commands: commands:
- name: swift - name: swift
@@ -482,7 +482,7 @@ def test_org_config_loading():
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
# Use temporary_home for cross-platform compatibility # Use temporary_home for cross-platform compatibility
with temporary_home(tmpdir): with temporary_home(tmpdir):
org_dir = Path(tmpdir) / ".autocoder" org_dir = Path(tmpdir) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -576,7 +576,7 @@ def test_hierarchy_resolution():
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
# Use temporary_home for cross-platform compatibility # Use temporary_home for cross-platform compatibility
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -593,9 +593,9 @@ blocked_commands:
""") """)
project_dir = Path(tmpproject) project_dir = Path(tmpproject)
project_autocoder = project_dir / ".autocoder" project_autoforge = project_dir / ".autoforge"
project_autocoder.mkdir() project_autoforge.mkdir()
project_config = project_autocoder / "allowed_commands.yaml" project_config = project_autoforge / "allowed_commands.yaml"
# Create project config # Create project config
project_config.write_text("""version: 1 project_config.write_text("""version: 1
@@ -660,7 +660,7 @@ def test_org_blocklist_enforcement():
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
# Use temporary_home for cross-platform compatibility # Use temporary_home for cross-platform compatibility
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -671,8 +671,8 @@ blocked_commands:
""") """)
project_dir = Path(tmpproject) project_dir = Path(tmpproject)
project_autocoder = project_dir / ".autocoder" project_autoforge = project_dir / ".autoforge"
project_autocoder.mkdir() project_autoforge.mkdir()
# Try to use terraform (should be blocked) # Try to use terraform (should be blocked)
input_data = {"tool_name": "Bash", "tool_input": {"command": "terraform apply"}} input_data = {"tool_name": "Bash", "tool_input": {"command": "terraform apply"}}
@@ -735,7 +735,7 @@ def test_pkill_extensibility():
with tempfile.TemporaryDirectory() as tmphome: with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -762,9 +762,9 @@ pkill_processes:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
project_dir = Path(tmpproject) project_dir = Path(tmpproject)
project_autocoder = project_dir / ".autocoder" project_autoforge = project_dir / ".autoforge"
project_autocoder.mkdir() project_autoforge.mkdir()
project_config = project_autocoder / "allowed_commands.yaml" project_config = project_autoforge / "allowed_commands.yaml"
# Create project config with extra pkill processes # Create project config with extra pkill processes
project_config.write_text("""version: 1 project_config.write_text("""version: 1
@@ -804,7 +804,7 @@ pkill_processes:
with tempfile.TemporaryDirectory() as tmphome: with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -829,7 +829,7 @@ pkill_processes:
with tempfile.TemporaryDirectory() as tmphome: with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -851,7 +851,7 @@ pkill_processes:
with tempfile.TemporaryDirectory() as tmphome: with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"
@@ -875,7 +875,7 @@ pkill_processes:
with tempfile.TemporaryDirectory() as tmphome: with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
org_config_path = org_dir / "config.yaml" org_config_path = org_dir / "config.yaml"

View File

@@ -79,9 +79,9 @@ def test_blocked_command_via_hook():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create minimal project structure # Create minimal project structure
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text( (autoforge_dir / "allowed_commands.yaml").write_text(
"version: 1\ncommands: []" "version: 1\ncommands: []"
) )
@@ -114,9 +114,9 @@ def test_allowed_command_via_hook():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create minimal project structure # Create minimal project structure
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text( (autoforge_dir / "allowed_commands.yaml").write_text(
"version: 1\ncommands: []" "version: 1\ncommands: []"
) )
@@ -145,9 +145,9 @@ def test_non_allowed_command_via_hook():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create minimal project structure # Create minimal project structure
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text( (autoforge_dir / "allowed_commands.yaml").write_text(
"version: 1\ncommands: []" "version: 1\ncommands: []"
) )
@@ -179,9 +179,9 @@ def test_project_config_allows_command():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create project config with swift allowed # Create project config with swift allowed
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text("""version: 1 (autoforge_dir / "allowed_commands.yaml").write_text("""version: 1
commands: commands:
- name: swift - name: swift
description: Swift compiler description: Swift compiler
@@ -214,9 +214,9 @@ def test_pattern_matching():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create project config with swift* pattern # Create project config with swift* pattern
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text("""version: 1 (autoforge_dir / "allowed_commands.yaml").write_text("""version: 1
commands: commands:
- name: swift* - name: swift*
description: All Swift tools description: All Swift tools
@@ -247,7 +247,7 @@ def test_org_blocklist_enforcement():
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
# Use context manager to safely set and restore HOME # Use context manager to safely set and restore HOME
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
(org_dir / "config.yaml").write_text("""version: 1 (org_dir / "config.yaml").write_text("""version: 1
allowed_commands: [] allowed_commands: []
@@ -257,11 +257,11 @@ blocked_commands:
""") """)
project_dir = Path(tmpproject) project_dir = Path(tmpproject)
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
# Try to allow terraform in project config (should fail - org blocked) # Try to allow terraform in project config (should fail - org blocked)
(autocoder_dir / "allowed_commands.yaml").write_text("""version: 1 (autoforge_dir / "allowed_commands.yaml").write_text("""version: 1
commands: commands:
- name: terraform - name: terraform
description: Infrastructure as code description: Infrastructure as code
@@ -295,7 +295,7 @@ def test_org_allowlist_inheritance():
with tempfile.TemporaryDirectory() as tmpproject: with tempfile.TemporaryDirectory() as tmpproject:
# Use context manager to safely set and restore HOME # Use context manager to safely set and restore HOME
with temporary_home(tmphome): with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder" org_dir = Path(tmphome) / ".autoforge"
org_dir.mkdir() org_dir.mkdir()
(org_dir / "config.yaml").write_text("""version: 1 (org_dir / "config.yaml").write_text("""version: 1
allowed_commands: allowed_commands:
@@ -305,9 +305,9 @@ blocked_commands: []
""") """)
project_dir = Path(tmpproject) project_dir = Path(tmpproject)
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text( (autoforge_dir / "allowed_commands.yaml").write_text(
"version: 1\ncommands: []" "version: 1\ncommands: []"
) )
@@ -336,9 +336,9 @@ def test_invalid_yaml_ignored():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create invalid YAML # Create invalid YAML
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
(autocoder_dir / "allowed_commands.yaml").write_text("invalid: yaml: content:") (autoforge_dir / "allowed_commands.yaml").write_text("invalid: yaml: content:")
# Try to run ls (should still work - falls back to defaults) # Try to run ls (should still work - falls back to defaults)
input_data = {"tool_name": "Bash", "tool_input": {"command": "ls"}} input_data = {"tool_name": "Bash", "tool_input": {"command": "ls"}}
@@ -365,13 +365,13 @@ def test_100_command_limit():
project_dir = Path(tmpdir) project_dir = Path(tmpdir)
# Create config with 101 commands # Create config with 101 commands
autocoder_dir = project_dir / ".autocoder" autoforge_dir = project_dir / ".autoforge"
autocoder_dir.mkdir() autoforge_dir.mkdir()
commands = [ commands = [
f" - name: cmd{i}\n description: Command {i}" for i in range(101) f" - name: cmd{i}\n description: Command {i}" for i in range(101)
] ]
(autocoder_dir / "allowed_commands.yaml").write_text( (autoforge_dir / "allowed_commands.yaml").write_text(
"version: 1\ncommands:\n" + "\n".join(commands) "version: 1\ncommands:\n" + "\n".join(commands)
) )

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AutoCoder</title> <title>AutoForge</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Work+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;700&family=Space+Mono:wght@400;700&family=Outfit:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Work+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;700&family=Space+Mono:wght@400;700&family=Outfit:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">

18
ui/package-lock.json generated
View File

@@ -1,11 +1,11 @@
{ {
"name": "autocoder", "name": "autoforge",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "autocoder", "name": "autoforge",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-checkbox": "^1.3.3",
@@ -81,7 +81,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -2695,7 +2694,6 @@
"integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
@@ -2706,7 +2704,6 @@
"integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@@ -2717,7 +2714,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@@ -2767,7 +2763,6 @@
"integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/scope-manager": "8.51.0",
"@typescript-eslint/types": "8.51.0", "@typescript-eslint/types": "8.51.0",
@@ -3072,7 +3067,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -3190,7 +3184,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@@ -3403,7 +3396,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@@ -3595,7 +3587,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -4579,7 +4570,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -4685,7 +4675,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -4695,7 +4684,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@@ -5005,7 +4993,6 @@
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -5144,7 +5131,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.27.0", "esbuild": "^0.27.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",

View File

@@ -1,5 +1,5 @@
{ {
"name": "autocoder", "name": "autoforge",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",

View File

@@ -34,8 +34,8 @@ import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card' import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
const STORAGE_KEY = 'autocoder-selected-project' const STORAGE_KEY = 'autoforge-selected-project'
const VIEW_MODE_KEY = 'autocoder-view-mode' const VIEW_MODE_KEY = 'autoforge-view-mode'
// Bottom padding for main content when debug panel is collapsed (40px header + 8px margin) // Bottom padding for main content when debug panel is collapsed (40px header + 8px margin)
const COLLAPSED_DEBUG_PANEL_CLEARANCE = 48 const COLLAPSED_DEBUG_PANEL_CLEARANCE = 48
@@ -264,7 +264,7 @@ function App() {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
{/* Logo and Title */} {/* Logo and Title */}
<h1 className="font-display text-2xl font-bold tracking-tight uppercase"> <h1 className="font-display text-2xl font-bold tracking-tight uppercase">
AutoCoder AutoForge
</h1> </h1>
{/* Controls */} {/* Controls */}
@@ -376,7 +376,7 @@ function App() {
{!selectedProject ? ( {!selectedProject ? (
<div className="text-center mt-12"> <div className="text-center mt-12">
<h2 className="font-display text-2xl font-bold mb-2"> <h2 className="font-display text-2xl font-bold mb-2">
Welcome to AutoCoder Welcome to AutoForge
</h2> </h2>
<p className="text-muted-foreground mb-4"> <p className="text-muted-foreground mb-4">
Select a project from the dropdown above or create a new one to get started. Select a project from the dropdown above or create a new one to get started.

View File

@@ -8,7 +8,7 @@ import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
const ACTIVITY_COLLAPSED_KEY = 'autocoder-activity-collapsed' const ACTIVITY_COLLAPSED_KEY = 'autoforge-activity-collapsed'
interface AgentMissionControlProps { interface AgentMissionControlProps {
agents: ActiveAgent[] agents: ActiveAgent[]

View File

@@ -110,7 +110,7 @@ export function DocsPage() {
className="font-display text-xl font-bold tracking-tight uppercase text-foreground className="font-display text-xl font-bold tracking-tight uppercase text-foreground
hover:text-primary transition-colors" hover:text-primary transition-colors"
> >
AutoCoder AutoForge
</a> </a>
<Badge variant="secondary" className="text-xs font-medium"> <Badge variant="secondary" className="text-xs font-medium">

View File

@@ -34,7 +34,7 @@ export const DOC_SECTIONS: DocSection[] = [
title: 'Getting Started', title: 'Getting Started',
icon: Rocket, icon: Rocket,
subsections: [ subsections: [
{ id: 'what-is-autocoder', title: 'What is AutoCoder?' }, { id: 'what-is-autoforge', title: 'What is AutoForge?' },
{ id: 'quick-start', title: 'Quick Start' }, { id: 'quick-start', title: 'Quick Start' },
{ id: 'creating-a-project', title: 'Creating a New Project' }, { id: 'creating-a-project', title: 'Creating a New Project' },
{ id: 'existing-project', title: 'Adding to an Existing Project' }, { id: 'existing-project', title: 'Adding to an Existing Project' },
@@ -60,7 +60,7 @@ export const DOC_SECTIONS: DocSection[] = [
title: 'Target Project Structure', title: 'Target Project Structure',
icon: FolderTree, icon: FolderTree,
subsections: [ subsections: [
{ id: 'autocoder-directory', title: '.autocoder/ Directory Layout' }, { id: 'autoforge-directory', title: '.autoforge/ Directory Layout' },
{ id: 'features-db', title: 'Features Database' }, { id: 'features-db', title: 'Features Database' },
{ id: 'prompts-directory', title: 'Prompts Directory' }, { id: 'prompts-directory', title: 'Prompts Directory' },
{ id: 'allowed-commands-yaml', title: 'Allowed Commands Config' }, { id: 'allowed-commands-yaml', title: 'Allowed Commands Config' },

View File

@@ -129,7 +129,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder`}</code></pre>
Environment Variables Environment Variables
</h3> </h3>
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
Key environment variables for configuring AutoCoder: Key environment variables for configuring AutoForge:
</p> </p>
<table className="w-full text-sm mt-3"> <table className="w-full text-sm mt-3">
<thead> <thead>
@@ -193,7 +193,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder`}</code></pre>
Webhook Support Webhook Support
</h3> </h3>
<ul className="list-disc space-y-2 ml-4 text-muted-foreground"> <ul className="list-disc space-y-2 ml-4 text-muted-foreground">
<li>AutoCoder can send webhook notifications on feature completion</li> <li>AutoForge can send webhook notifications on feature completion</li>
<li>Compatible with N8N and similar automation tools</li> <li>Compatible with N8N and similar automation tools</li>
<li>Configure the webhook URL in project settings</li> <li>Configure the webhook URL in project settings</li>
<li> <li>
@@ -208,7 +208,7 @@ ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder`}</code></pre>
<ul className="list-disc space-y-2 ml-4 text-muted-foreground"> <ul className="list-disc space-y-2 ml-4 text-muted-foreground">
<li> <li>
All projects are registered in{' '} All projects are registered in{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/registry.db</span>{' '} <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autoforge/registry.db</span>{' '}
(SQLite) (SQLite)
</li> </li>
<li>Maps project names to filesystem paths</li> <li>Maps project names to filesystem paths</li>

View File

@@ -15,7 +15,7 @@ export function AppSpecSetup() {
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
The app spec is an XML document that describes the application to be built. It lives at{' '} The app spec is an XML document that describes the application to be built. It lives at{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/prompts/app_spec.txt .autoforge/prompts/app_spec.txt
</span>{' '} </span>{' '}
and tells the initializer agent what features to create. The spec defines your app&apos;s name, and tells the initializer agent what features to create. The spec defines your app&apos;s name,
description, tech stack, and the features that should be implemented. description, tech stack, and the features that should be implemented.
@@ -56,7 +56,7 @@ export function AppSpecSetup() {
<li> <li>
Create{' '} Create{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/prompts/app_spec.txt .autoforge/prompts/app_spec.txt
</span>{' '} </span>{' '}
in your project directory in your project directory
</li> </li>
@@ -97,7 +97,7 @@ export function AppSpecSetup() {
<li> <li>
Creates the feature database at{' '} Creates the feature database at{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/features.db .autoforge/features.db
</span> </span>
</li> </li>
</ul> </ul>

View File

@@ -98,7 +98,7 @@ export function AppearanceThemes() {
Themes Overview Themes Overview
</h3> </h3>
<p className="text-muted-foreground mb-4"> <p className="text-muted-foreground mb-4">
AutoCoder comes with 6 built-in themes. Each theme provides a complete visual identity including AutoForge comes with 6 built-in themes. Each theme provides a complete visual identity including
colors, accents, and dark mode variants. colors, accents, and dark mode variants.
</p> </p>
<div className="space-y-4"> <div className="space-y-4">

View File

@@ -13,7 +13,7 @@ export function FAQ() {
Starting a New Project Starting a New Project
</h3> </h3>
<p className="text-muted-foreground italic mb-2"> <p className="text-muted-foreground italic mb-2">
How do I use AutoCoder on a new project? How do I use AutoForge on a new project?
</p> </p>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
From the UI, select &quot;Create New Project&quot; in the project dropdown. Choose a folder and From the UI, select &quot;Create New Project&quot; in the project dropdown. Choose a folder and
@@ -27,12 +27,12 @@ export function FAQ() {
Adding to Existing Project Adding to Existing Project
</h3> </h3>
<p className="text-muted-foreground italic mb-2"> <p className="text-muted-foreground italic mb-2">
How do I add AutoCoder to an existing project? How do I add AutoForge to an existing project?
</p> </p>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Register the project folder through the UI project selector using &quot;Add Existing&quot;. Register the project folder through the UI project selector using &quot;Add Existing&quot;.
AutoCoder creates a{' '} AutoForge creates a{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span> directory <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autoforge/</span> directory
alongside your existing code. Write an app spec describing what to build (new features), and the alongside your existing code. Write an app spec describing what to build (new features), and the
agent works within your existing codebase. agent works within your existing codebase.
</p> </p>
@@ -60,7 +60,7 @@ export function FAQ() {
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Create{' '} Create{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/allowed_commands.yaml .autoforge/allowed_commands.yaml
</span>{' '} </span>{' '}
in your project with a list of allowed commands. Supports exact names, wildcards (e.g.,{' '} in your project with a list of allowed commands. Supports exact names, wildcards (e.g.,{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">swift*</span>), and local <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">swift*</span>), and local

View File

@@ -1,7 +1,7 @@
/** /**
* GettingStarted Documentation Section * GettingStarted Documentation Section
* *
* Covers what AutoCoder is, quick start commands, * Covers what AutoForge is, quick start commands,
* creating and adding projects, and system requirements. * creating and adding projects, and system requirements.
*/ */
@@ -10,12 +10,12 @@ import { Badge } from '@/components/ui/badge'
export function GettingStarted() { export function GettingStarted() {
return ( return (
<div> <div>
{/* What is AutoCoder? */} {/* What is AutoForge? */}
<h3 id="what-is-autocoder" className="text-lg font-semibold text-foreground mt-8 mb-3"> <h3 id="what-is-autoforge" className="text-lg font-semibold text-foreground mt-8 mb-3">
What is AutoCoder? What is AutoForge?
</h3> </h3>
<p className="text-muted-foreground mb-4"> <p className="text-muted-foreground mb-4">
AutoCoder is an autonomous coding agent system that builds complete applications over multiple AutoForge is an autonomous coding agent system that builds complete applications over multiple
sessions using a two-agent pattern: sessions using a two-agent pattern:
</p> </p>
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground"> <ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
@@ -38,7 +38,7 @@ export function GettingStarted() {
Quick Start Quick Start
</h3> </h3>
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
Launch AutoCoder with a single command. The CLI menu lets you create or select a project, Launch AutoForge with a single command. The CLI menu lets you create or select a project,
while the Web UI provides a full dashboard experience. while the Web UI provides a full dashboard experience.
</p> </p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm"> <div className="bg-muted rounded-lg p-4 font-mono text-sm">
@@ -76,12 +76,12 @@ start_ui.bat # Web UI
<ul className="list-disc space-y-2 ml-4 text-muted-foreground"> <ul className="list-disc space-y-2 ml-4 text-muted-foreground">
<li>Register the project folder via the UI project selector</li> <li>Register the project folder via the UI project selector</li>
<li> <li>
AutoCoder creates a{' '} AutoForge creates a{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span>{' '} <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autoforge/</span>{' '}
directory inside your project directory inside your project
</li> </li>
<li> <li>
Existing code is preserved &mdash; AutoCoder adds its configuration alongside it Existing code is preserved &mdash; AutoForge adds its configuration alongside it
</li> </li>
<li>Write or generate an app spec describing what to build</li> <li>Write or generate an app spec describing what to build</li>
</ul> </ul>

View File

@@ -1,7 +1,7 @@
/** /**
* ProjectStructure Documentation Section * ProjectStructure Documentation Section
* *
* Covers the .autocoder/ directory layout, features database, * Covers the .autoforge/ directory layout, features database,
* prompts directory, allowed commands, CLAUDE.md convention, * prompts directory, allowed commands, CLAUDE.md convention,
* legacy migration, and Claude inheritance. * legacy migration, and Claude inheritance.
*/ */
@@ -9,18 +9,18 @@
export function ProjectStructure() { export function ProjectStructure() {
return ( return (
<div> <div>
{/* .autocoder/ Directory Layout */} {/* .autoforge/ Directory Layout */}
<h3 id="autocoder-directory" className="text-lg font-semibold text-foreground mt-8 mb-3"> <h3 id="autoforge-directory" className="text-lg font-semibold text-foreground mt-8 mb-3">
.autocoder/ Directory Layout .autoforge/ Directory Layout
</h3> </h3>
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
Every AutoCoder project stores its configuration and runtime files in a{' '} Every AutoForge project stores its configuration and runtime files in a{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span>{' '} <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autoforge/</span>{' '}
directory at the project root. directory at the project root.
</p> </p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm"> <div className="bg-muted rounded-lg p-4 font-mono text-sm">
<pre><code>{`your-project/ <pre><code>{`your-project/
\u251C\u2500\u2500 .autocoder/ \u251C\u2500\u2500 .autoforge/
\u2502 \u251C\u2500\u2500 features.db # SQLite feature database \u2502 \u251C\u2500\u2500 features.db # SQLite feature database
\u2502 \u251C\u2500\u2500 .agent.lock # Lock file (prevents multiple instances) \u2502 \u251C\u2500\u2500 .agent.lock # Lock file (prevents multiple instances)
\u2502 \u251C\u2500\u2500 .gitignore # Ignores runtime files \u2502 \u251C\u2500\u2500 .gitignore # Ignores runtime files
@@ -41,7 +41,7 @@ export function ProjectStructure() {
<li> <li>
SQLite database managed by SQLAlchemy, stored at{' '} SQLite database managed by SQLAlchemy, stored at{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/features.db .autoforge/features.db
</span> </span>
</li> </li>
<li> <li>
@@ -96,7 +96,7 @@ export function ProjectStructure() {
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
The optional{' '} The optional{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/allowed_commands.yaml .autoforge/allowed_commands.yaml
</span>{' '} </span>{' '}
file lets you grant project-specific bash commands to the agent. This is useful when your file lets you grant project-specific bash commands to the agent. This is useful when your
project requires tools beyond the default allowlist (e.g., language-specific compilers or project requires tools beyond the default allowlist (e.g., language-specific compilers or
@@ -138,7 +138,7 @@ export function ProjectStructure() {
<ul className="list-disc space-y-2 ml-4 text-muted-foreground"> <ul className="list-disc space-y-2 ml-4 text-muted-foreground">
<li> <li>
On the next agent start, these files are automatically migrated into{' '} On the next agent start, these files are automatically migrated into{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autoforge/</span>
</li> </li>
<li>Dual-path resolution ensures both old and new layouts work transparently</li> <li>Dual-path resolution ensures both old and new layouts work transparently</li>
<li>No manual migration is needed &mdash; it happens seamlessly</li> <li>No manual migration is needed &mdash; it happens seamlessly</li>

View File

@@ -15,7 +15,7 @@ export function Scheduling() {
What Scheduling Does What Scheduling Does
</h3> </h3>
<p className="text-muted-foreground mb-4"> <p className="text-muted-foreground mb-4">
Scheduling automates agent runs at specific times. Set up a schedule and AutoCoder will automatically Scheduling automates agent runs at specific times. Set up a schedule and AutoForge will automatically
start agents on your project &mdash; useful for overnight builds, periodic maintenance, or continuous start agents on your project &mdash; useful for overnight builds, periodic maintenance, or continuous
development. development.
</p> </p>

View File

@@ -16,7 +16,7 @@ export function Security() {
Command Validation Overview Command Validation Overview
</h3> </h3>
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
AutoCoder uses a defense-in-depth approach for security. All three layers must pass before any AutoForge uses a defense-in-depth approach for security. All three layers must pass before any
command is executed: command is executed:
</p> </p>
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground"> <ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
@@ -49,12 +49,12 @@ export function Security() {
</li> </li>
<li> <li>
<strong className="text-foreground">Org Blocklist</strong>{' '} <strong className="text-foreground">Org Blocklist</strong>{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/config.yaml</span>{' '} <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autoforge/config.yaml</span>{' '}
&mdash; org-wide blocks, cannot be project-overridden &mdash; org-wide blocks, cannot be project-overridden
</li> </li>
<li> <li>
<strong className="text-foreground">Org Allowlist</strong>{' '} <strong className="text-foreground">Org Allowlist</strong>{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/config.yaml</span>{' '} <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autoforge/config.yaml</span>{' '}
&mdash; available to all projects &mdash; available to all projects
</li> </li>
<li> <li>
@@ -65,7 +65,7 @@ export function Security() {
<li> <li>
<strong className="text-foreground">Project Allowlist</strong>{' '} <strong className="text-foreground">Project Allowlist</strong>{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/allowed_commands.yaml .autoforge/allowed_commands.yaml
</span>{' '} </span>{' '}
&mdash; project-specific additions &mdash; project-specific additions
</li> </li>
@@ -120,12 +120,12 @@ export function Security() {
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
Each project can define additional allowed commands in{' '} Each project can define additional allowed commands in{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono"> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
.autocoder/allowed_commands.yaml .autoforge/allowed_commands.yaml
</span> </span>
: :
</p> </p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm"> <div className="bg-muted rounded-lg p-4 font-mono text-sm">
<pre><code>{`# .autocoder/allowed_commands.yaml <pre><code>{`# .autoforge/allowed_commands.yaml
version: 1 version: 1
commands: commands:
# Exact command name # Exact command name
@@ -155,10 +155,10 @@ commands:
</h3> </h3>
<p className="text-muted-foreground mb-3"> <p className="text-muted-foreground mb-3">
System administrators can set org-wide policies in{' '} System administrators can set org-wide policies in{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/config.yaml</span>: <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autoforge/config.yaml</span>:
</p> </p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm"> <div className="bg-muted rounded-lg p-4 font-mono text-sm">
<pre><code>{`# ~/.autocoder/config.yaml <pre><code>{`# ~/.autoforge/config.yaml
version: 1 version: 1
# Commands available to ALL projects # Commands available to ALL projects

View File

@@ -177,7 +177,7 @@ export function SettingsConfig() {
<ul className="list-disc space-y-2 ml-4 text-muted-foreground"> <ul className="list-disc space-y-2 ml-4 text-muted-foreground">
<li> <li>
Global settings stored in SQLite registry at{' '} Global settings stored in SQLite registry at{' '}
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/registry.db</span> <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autoforge/registry.db</span>
</li> </li>
<li>Per-project settings (like default concurrency) stored in the project registry entry</li> <li>Per-project settings (like default concurrency) stored in the project registry entry</li>
<li>UI settings (theme, dark mode) stored in browser localStorage</li> <li>UI settings (theme, dark mode) stored in browser localStorage</li>

View File

@@ -52,8 +52,8 @@ export const THEMES: ThemeOption[] = [
} }
] ]
const THEME_STORAGE_KEY = 'autocoder-theme' const THEME_STORAGE_KEY = 'autoforge-theme'
const DARK_MODE_STORAGE_KEY = 'autocoder-dark-mode' const DARK_MODE_STORAGE_KEY = 'autoforge-dark-mode'
function getThemeClass(themeId: ThemeId): string { function getThemeClass(themeId: ThemeId): string {
switch (themeId) { switch (themeId) {