Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ python scripts/quality-ratchet.py show
**Auto-suggestion**: When quality degrades >10%, the tool recommends context reset with handoff template from `.claude/rules.md`.

## Recent Changes
- 010-server-start-command: Added CLI 'serve' command (--port, --reload, --no-browser flags), port validation utilities (port_utils.py), 19 tests with 100% coverage on utilities, no database changes
- 2025-11-14: 007-context-management - **CRITICAL ARCHITECTURAL FIX** 🎯
* **Multi-Agent Support**: Multiple agents can now collaborate on same project
* Added `agent_id` column to `context_items` schema
Expand All @@ -91,10 +92,6 @@ python scripts/quality-ratchet.py show
* Phase 5: Automatic tier assignment HOT/WARM/COLD (T037-T043, T046)
* **Formula**: score = 0.4 × type_weight + 0.4 × age_decay + 0.2 × access_boost
* **Tiers**: HOT (≥0.8), WARM (0.4-0.8), COLD (<0.4)
- 2025-11-14: 007-context-management - Implemented T012 and T013 database methods for context items and checkpoints
- 007-context-management: Added Python 3.11+ (backend), TypeScript 5.3+ (frontend dashboard) + FastAPI, AsyncAnthropic, React 18, aiosqlite, tiktoken (for token counting)
- 049-human-in-loop: Added Python 3.11+ (backend), TypeScript 5.3+ (frontend) + FastAPI, AsyncAnthropic, React 18, Tailwind CSS, aiosqlite, websockets
- 2025-11-08: Restructured documentation (SPRINTS.md, AGENTS.md, sprints/ directory)

<!-- MANUAL ADDITIONS START -->
## Frontend State Management Architecture (Phase 5.2)
Expand Down
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,29 @@ npm run dev

## Quick Start

### 1. Create a Project via API
### 1. Start the Dashboard

```bash
curl -X POST http://localhost:8000/api/projects \
codeframe serve
```

This will:
- Start the FastAPI server on port 8080
- Automatically open your browser to the dashboard
- Display real-time project status

Press Ctrl+C to stop the server.

**Options**:
- `--port 3000` - Use custom port
- `--no-browser` - Don't auto-open browser
- `--reload` - Enable auto-reload (development)
- `--host 127.0.0.1` - Bind to specific host

### 2. Create a Project via API

```bash
curl -X POST http://localhost:8080/api/projects \
-H "Content-Type: application/json" \
-d '{
"name": "My AI Project",
Expand All @@ -262,35 +281,35 @@ curl -X POST http://localhost:8000/api/projects \
}'
```

### 2. Submit a PRD (Product Requirements Document)
### 3. Submit a PRD (Product Requirements Document)

```bash
curl -X POST http://localhost:8000/api/projects/1/prd \
curl -X POST http://localhost:8080/api/projects/1/prd \
-H "Content-Type: application/json" \
-d '{
"content": "Build a user authentication system with JWT tokens, \
email/password login, and rate limiting."
}'
```

### 3. Watch Agents Work
### 4. Watch Agents Work

Navigate to `http://localhost:5173` to see:
Navigate to `http://localhost:8080` to see:
- **Agent Pool**: Active agents and their current tasks
- **Task Progress**: Real-time task completion updates
- **Blockers**: Questions agents need answered
- **Context Stats**: Memory usage and tier distribution
- **Lint Results**: Code quality metrics and trends
- **Review Findings**: Security vulnerabilities and quality issues

### 4. Answer Blockers When Needed
### 5. Answer Blockers When Needed

```bash
# List current blockers
curl http://localhost:8000/api/projects/1/blockers
curl http://localhost:8080/api/projects/1/blockers

# Answer a blocker
curl -X POST http://localhost:8000/api/blockers/1/answer \
curl -X POST http://localhost:8080/api/blockers/1/answer \
-H "Content-Type: application/json" \
-d '{"answer": "Use bcrypt for password hashing with salt rounds=12"}'
```
Expand Down
92 changes: 92 additions & 0 deletions codeframe/cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
"""Command-line interface for CodeFRAME."""

import subprocess
import threading
import time
import webbrowser
from pathlib import Path
from typing import Optional

import typer
from rich.console import Console

from codeframe.core.port_utils import check_port_availability, validate_port_range
from codeframe.core.project import Project

app = typer.Typer(
Expand Down Expand Up @@ -163,6 +169,92 @@ def agents(
console.print(f"Agents {action} - [yellow]Not implemented yet[/yellow]")


@app.command()
def serve(
port: int = typer.Option(8080, "--port", "-p", help="Port to run server on"),
host: str = typer.Option("0.0.0.0", "--host", help="Host to bind to"),
open_browser: bool = typer.Option(
True, "--open-browser/--no-browser", help="Auto-open browser"
),
reload: bool = typer.Option(False, "--reload", help="Enable auto-reload (development)"),
):
"""Start the CodeFRAME dashboard server.

The server will run on the specified port and automatically open
your browser to the dashboard. Press Ctrl+C to stop the server.

Examples:

codeframe serve

codeframe serve --port 3000 --no-browser

codeframe serve --reload
"""
# Validate port range
valid, msg = validate_port_range(port)
if not valid:
console.print(f"[red]Error:[/red] {msg}")
raise typer.Exit(1)

# Check port availability
available, msg = check_port_availability(port, host)
if not available:
console.print(f"[red]Error:[/red] {msg}")
raise typer.Exit(1)

# Build uvicorn command
cmd = [
"uvicorn",
"codeframe.ui.server:app",
"--host",
host,
"--port",
str(port),
]

if reload:
cmd.append("--reload")

# Print startup message
console.print("🌐 Starting dashboard server...")
console.print(f" URL: [bold cyan]http://localhost:{port}[/bold cyan]")
console.print(" Press [bold]Ctrl+C[/bold] to stop\n")

# Open browser in background thread (if enabled)
if open_browser:

def open_in_browser():
"""Open browser after delay to ensure server is ready."""
time.sleep(1.5)
try:
webbrowser.open(f"http://localhost:{port}")
except Exception as e:
console.print(f"[yellow]Warning:[/yellow] Could not open browser: {e}")
console.print(f"Please open http://localhost:{port} manually")

browser_thread = threading.Thread(target=open_in_browser, daemon=True)
browser_thread.start()

# Start server (blocking call)
# Note: We don't capture output so uvicorn logs are visible to the user
try:
subprocess.run(cmd, check=True)
except KeyboardInterrupt:
console.print("\n✓ Server stopped")
except FileNotFoundError:
console.print("[red]Error:[/red] uvicorn not found. Install with: pip install uvicorn")
raise typer.Exit(1)
except subprocess.CalledProcessError:
# Server failed - uvicorn's error output is already visible to user
console.print(
f"\n[red]Server failed to start.[/red] Common issues:"
)
console.print(f" • Port {port} may be in use (try --port {port + 1})")
console.print(f" • Check the error message above for details")
raise typer.Exit(1)


@app.command()
def version():
"""Show CodeFRAME version."""
Expand Down
88 changes: 88 additions & 0 deletions codeframe/core/port_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Port validation and availability checking utilities."""

import socket
from typing import Tuple


def is_port_available(port: int, host: str = "0.0.0.0") -> bool:
"""
Check if a port is available for binding.

Args:
port: Port number to check
host: Host address to bind to (default: 0.0.0.0)

Returns:
True if port is available, False otherwise
"""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((host, port))
return True
except OSError:
return False


def check_port_availability(port: int, host: str = "0.0.0.0") -> Tuple[bool, str]:
"""
Check if a port is available and return a helpful message if not.

Args:
port: Port number to check
host: Host address to bind to (default: 0.0.0.0)

Returns:
Tuple of (available: bool, message: str)
If available, message is empty string.
If not available, message contains helpful error text.

Note:
There is a small time window (~100ms) between this check and actual server
startup where another process could bind to the port (TOCTOU race condition).
This is inherent to pre-flight port checking. If this rare case occurs, the
server will fail to start and uvicorn will display the appropriate error.
"""
if port < 1024:
return (
False,
f"Port {port} requires elevated privileges. Use a port ≥1024 (try --port {8080})",
)

try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((host, port))
return (True, "")
except OSError as e:
# Common error codes for "address already in use"
# errno 48 (macOS), 98 (Linux), 10048 (Windows)
if e.errno in (48, 98, 10048):
suggested_port = port + 1
return (
False,
f"Port {port} is already in use. Try --port {suggested_port}",
)
else:
return (False, f"Cannot bind to port {port}: {e}")


def validate_port_range(port: int) -> Tuple[bool, str]:
"""
Validate that port is in acceptable range.

Args:
port: Port number to validate

Returns:
Tuple of (valid: bool, message: str)
If valid, message is empty string.
If invalid, message contains error text.
"""
if port < 1024:
return (
False,
f"Port {port} requires elevated privileges. Use a port ≥1024",
)
if port > 65535:
return (False, f"Port {port} is out of range. Maximum port is 65535")

return (True, "")
46 changes: 46 additions & 0 deletions specs/010-server-start-command/contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# API Contracts: Server Start Command

**Feature**: 010-server-start-command

---

## No API Contracts

This feature **does not add or modify any API endpoints**.

The `codeframe serve` command is a **CLI-only feature** that starts the existing FastAPI server. It does not introduce new HTTP routes, WebSocket handlers, or GraphQL resolvers.

---

## Why No Contracts?

The serve command's responsibility is **server lifecycle management**:
- Start uvicorn subprocess
- Check port availability
- Open browser
- Handle graceful shutdown

It **does not** expose any APIs to external clients.

---

## Existing APIs

The server that is *started* by this command already has APIs documented elsewhere:
- `/api/projects` - Project management endpoints
- `/api/agents` - Agent management endpoints
- `/api/blockers` - Blocker management endpoints
- `/ws` - WebSocket for real-time updates

See `/home/frankbria/projects/codeframe/codeframe/ui/server.py` for full API documentation.

---

## Future Considerations

If we later add features like:
- Server management API (start/stop/status via HTTP)
- Multi-instance coordination
- Remote server control

...then we would document those contracts here. For now, this directory remains empty by design.
Loading