mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-05 16:33:08 +00:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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!
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
|
||||||
10
.claude/commands/gsd-to-autoforge-spec.md
Normal file
10
.claude/commands/gsd-to-autoforge-spec.md
Normal 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
|
||||||
@@ -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.
|
||||||
```
|
```
|
||||||
@@ -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:
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
42
CLAUDE.md
42
CLAUDE.md
@@ -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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# AutoCoder
|
# AutoForge
|
||||||
|
|
||||||
[](https://www.buymeacoffee.com/leonvanzyl)
|
[](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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
14
prompts.py
14
prompts.py
@@ -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}")
|
||||||
|
|
||||||
|
|||||||
27
registry.py
27
registry.py
@@ -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
|
||||||
|
|
||||||
|
|||||||
22
security.py
22
security.py
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.")
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ cd /d "%~dp0"
|
|||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo Autonomous Coding Agent
|
echo AutoForge - Autonomous Coding Agent
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
|
|||||||
2
start.py
2
start.py
@@ -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")
|
||||||
|
|
||||||
|
|||||||
2
start.sh
2
start.sh
@@ -3,7 +3,7 @@ cd "$(dirname "$0")"
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo " Autonomous Coding Agent"
|
echo " AutoForge - Autonomous Coding Agent"
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
12
start_ui.py
12
start_ui.py
@@ -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
|
||||||
|
|||||||
@@ -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 ""
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
18
ui/package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "autocoder",
|
"name": "autoforge",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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[]
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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' },
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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's name,
|
and tells the initializer agent what features to create. The spec defines your app'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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 "Create New Project" in the project dropdown. Choose a folder and
|
From the UI, select "Create New Project" 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 "Add Existing".
|
Register the project folder through the UI project selector using "Add Existing".
|
||||||
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
|
||||||
|
|||||||
@@ -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 — AutoCoder adds its configuration alongside it
|
Existing code is preserved — 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>
|
||||||
|
|||||||
@@ -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 — it happens seamlessly</li>
|
<li>No manual migration is needed — it happens seamlessly</li>
|
||||||
|
|||||||
@@ -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 — useful for overnight builds, periodic maintenance, or continuous
|
start agents on your project — useful for overnight builds, periodic maintenance, or continuous
|
||||||
development.
|
development.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -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>{' '}
|
||||||
— org-wide blocks, cannot be project-overridden
|
— 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>{' '}
|
||||||
— available to all projects
|
— 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>{' '}
|
||||||
— project-specific additions
|
— 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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user