mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-02 15:23:37 +00:00
add documentation
This commit is contained in:
160
CLAUDE.md
160
CLAUDE.md
@@ -54,6 +54,12 @@ python autonomous_agent_demo.py --project-dir my-app --yolo
|
|||||||
|
|
||||||
# Parallel mode: run multiple agents concurrently (1-5 agents)
|
# Parallel mode: run multiple agents concurrently (1-5 agents)
|
||||||
python autonomous_agent_demo.py --project-dir my-app --parallel --max-concurrency 3
|
python autonomous_agent_demo.py --project-dir my-app --parallel --max-concurrency 3
|
||||||
|
|
||||||
|
# Batch mode: implement multiple features per agent session (1-3)
|
||||||
|
python autonomous_agent_demo.py --project-dir my-app --batch-size 3
|
||||||
|
|
||||||
|
# Batch specific features by ID
|
||||||
|
python autonomous_agent_demo.py --project-dir my-app --batch-features 1,2,3
|
||||||
```
|
```
|
||||||
|
|
||||||
### YOLO Mode (Rapid Prototyping)
|
### YOLO Mode (Rapid Prototyping)
|
||||||
@@ -68,7 +74,7 @@ python autonomous_agent_demo.py --project-dir my-app --yolo
|
|||||||
```
|
```
|
||||||
|
|
||||||
**What's different in YOLO mode:**
|
**What's different in YOLO mode:**
|
||||||
- No regression testing (skips `feature_get_for_regression`)
|
- No regression testing
|
||||||
- No Playwright MCP server (browser automation disabled)
|
- No Playwright MCP server (browser automation disabled)
|
||||||
- Features marked passing after lint/type-check succeeds
|
- Features marked passing after lint/type-check succeeds
|
||||||
- Faster iteration for prototyping
|
- Faster iteration for prototyping
|
||||||
@@ -97,10 +103,13 @@ npm run lint # Run ESLint
|
|||||||
### Python
|
### Python
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ruff check . # Lint
|
ruff check . # Lint
|
||||||
mypy . # Type check
|
mypy . # Type check
|
||||||
python test_security.py # Security unit tests (163 tests)
|
python test_security.py # Security unit tests (12 tests)
|
||||||
python test_security_integration.py # Integration tests (9 tests)
|
python test_security_integration.py # Integration tests (9 tests)
|
||||||
|
python -m pytest test_client.py # Client tests (20 tests)
|
||||||
|
python -m pytest test_dependency_resolver.py # Dependency resolver tests (12 tests)
|
||||||
|
python -m pytest test_rate_limit_utils.py # Rate limit tests (22 tests)
|
||||||
```
|
```
|
||||||
|
|
||||||
### React UI
|
### React UI
|
||||||
@@ -108,11 +117,17 @@ python test_security_integration.py # Integration tests (9 tests)
|
|||||||
```bash
|
```bash
|
||||||
cd ui
|
cd ui
|
||||||
npm run lint # ESLint
|
npm run lint # ESLint
|
||||||
npm run build # Type check + build
|
npm run build # Type check + build (Vite 7)
|
||||||
npm run test:e2e # Playwright end-to-end tests
|
npm run test:e2e # Playwright end-to-end tests
|
||||||
npm run test:e2e:ui # Playwright tests with UI
|
npm run test:e2e:ui # Playwright tests with UI
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CI/CD
|
||||||
|
|
||||||
|
GitHub Actions (`.github/workflows/ci.yml`) runs on push/PR to master:
|
||||||
|
- **Python job**: ruff lint + security tests
|
||||||
|
- **UI job**: ESLint + TypeScript build
|
||||||
|
|
||||||
### Code Quality
|
### Code Quality
|
||||||
|
|
||||||
Configuration in `pyproject.toml`:
|
Configuration in `pyproject.toml`:
|
||||||
@@ -124,16 +139,21 @@ Configuration in `pyproject.toml`:
|
|||||||
### Core Python Modules
|
### Core Python Modules
|
||||||
|
|
||||||
- `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
|
- `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
|
- `autocoder_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 and MCP servers
|
- `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)
|
||||||
- `prompts.py` - Prompt template loading with project-specific fallback
|
- `prompts.py` - Prompt template loading with project-specific fallback and batch feature prompts
|
||||||
- `progress.py` - Progress tracking, database queries, webhook notifications
|
- `progress.py` - Progress tracking, database queries, webhook notifications
|
||||||
- `registry.py` - Project registry for mapping names to paths (cross-platform)
|
- `registry.py` - Project registry for mapping names to paths (cross-platform), global settings model
|
||||||
- `parallel_orchestrator.py` - Concurrent agent execution with dependency-aware scheduling
|
- `parallel_orchestrator.py` - Concurrent agent execution with dependency-aware scheduling
|
||||||
|
- `auth.py` - Authentication error detection for Claude CLI
|
||||||
|
- `env_constants.py` - Shared environment variable constants (API_ENV_VARS) used by client.py and chat sessions
|
||||||
|
- `rate_limit_utils.py` - Rate limit detection, retry parsing, exponential backoff with jitter
|
||||||
|
- `api/database.py` - SQLAlchemy models (Feature, Schedule, ScheduleOverride)
|
||||||
- `api/dependency_resolver.py` - Cycle detection (Kahn's algorithm + DFS) and dependency validation
|
- `api/dependency_resolver.py` - Cycle detection (Kahn's algorithm + DFS) and dependency validation
|
||||||
|
- `api/migration.py` - JSON-to-SQLite migration utility
|
||||||
|
|
||||||
### Project Registry
|
### Project Registry
|
||||||
|
|
||||||
@@ -147,13 +167,36 @@ The registry uses:
|
|||||||
|
|
||||||
### Server API (server/)
|
### Server API (server/)
|
||||||
|
|
||||||
The FastAPI server provides REST endpoints for the UI:
|
The FastAPI server provides REST and WebSocket endpoints for the UI:
|
||||||
|
|
||||||
- `server/routers/projects.py` - Project CRUD with registry integration
|
**Routers** (`server/routers/`):
|
||||||
- `server/routers/features.py` - Feature management
|
- `projects.py` - Project CRUD with registry integration
|
||||||
- `server/routers/agent.py` - Agent control (start/stop/pause/resume)
|
- `features.py` - Feature management
|
||||||
- `server/routers/filesystem.py` - Filesystem browser API with security controls
|
- `agent.py` - Agent control (start/stop/pause/resume)
|
||||||
- `server/routers/spec_creation.py` - WebSocket for interactive spec creation
|
- `filesystem.py` - Filesystem browser API with security controls
|
||||||
|
- `spec_creation.py` - WebSocket for interactive spec creation
|
||||||
|
- `expand_project.py` - Interactive project expansion via natural language
|
||||||
|
- `assistant_chat.py` - Read-only project assistant chat (WebSocket/REST)
|
||||||
|
- `terminal.py` - Interactive terminal I/O with PTY support (WebSocket bidirectional)
|
||||||
|
- `devserver.py` - Dev server control (start/stop) and config
|
||||||
|
- `schedules.py` - CRUD for time-based agent scheduling
|
||||||
|
- `settings.py` - Global settings management (model selection, YOLO, batch size, headless browser)
|
||||||
|
|
||||||
|
**Services** (`server/services/`):
|
||||||
|
- `process_manager.py` - Agent process lifecycle management
|
||||||
|
- `project_config.py` - Project type detection and dev command management
|
||||||
|
- `terminal_manager.py` - Terminal session management with PTY (`pywinpty` on Windows)
|
||||||
|
- `scheduler_service.py` - APScheduler-based automated agent scheduling
|
||||||
|
- `dev_server_manager.py` - Dev server lifecycle management
|
||||||
|
- `assistant_chat_session.py` / `assistant_database.py` - Assistant chat sessions with SQLite persistence
|
||||||
|
- `spec_chat_session.py` - Spec creation chat sessions
|
||||||
|
- `expand_chat_session.py` - Expand project chat sessions
|
||||||
|
- `chat_constants.py` - Shared constants for chat services
|
||||||
|
|
||||||
|
**Utilities** (`server/utils/`):
|
||||||
|
- `process_utils.py` - Process management utilities
|
||||||
|
- `project_helpers.py` - Project path resolution helpers
|
||||||
|
- `validation.py` - Project name validation
|
||||||
|
|
||||||
### Feature Management
|
### Feature Management
|
||||||
|
|
||||||
@@ -164,18 +207,26 @@ Features are stored in SQLite (`features.db`) via SQLAlchemy. The agent interact
|
|||||||
|
|
||||||
MCP tools available to the agent:
|
MCP tools available to the agent:
|
||||||
- `feature_get_stats` - Progress statistics
|
- `feature_get_stats` - Progress statistics
|
||||||
- `feature_get_next` - Get highest-priority pending feature (respects dependencies)
|
- `feature_get_by_id` - Get a single feature by ID
|
||||||
- `feature_claim_next` - Atomically claim next available feature (for parallel mode)
|
- `feature_get_summary` - Get summary of all features
|
||||||
- `feature_get_for_regression` - Random passing features for regression testing
|
- `feature_get_ready` - Get features ready to work on (dependencies met)
|
||||||
|
- `feature_get_blocked` - Get features blocked by unmet dependencies
|
||||||
|
- `feature_get_graph` - Get full dependency graph
|
||||||
|
- `feature_claim_and_get` - Atomically claim next available feature (for parallel mode)
|
||||||
|
- `feature_mark_in_progress` - Mark feature as in progress
|
||||||
- `feature_mark_passing` - Mark feature complete
|
- `feature_mark_passing` - Mark feature complete
|
||||||
|
- `feature_mark_failing` - Mark feature as failing
|
||||||
- `feature_skip` - Move feature to end of queue
|
- `feature_skip` - Move feature to end of queue
|
||||||
|
- `feature_clear_in_progress` - Clear in-progress status
|
||||||
- `feature_create_bulk` - Initialize all features (used by initializer)
|
- `feature_create_bulk` - Initialize all features (used by initializer)
|
||||||
|
- `feature_create` - Create a single feature
|
||||||
- `feature_add_dependency` - Add dependency between features (with cycle detection)
|
- `feature_add_dependency` - Add dependency between features (with cycle detection)
|
||||||
- `feature_remove_dependency` - Remove a dependency
|
- `feature_remove_dependency` - Remove a dependency
|
||||||
|
- `feature_set_dependencies` - Set all dependencies for a feature at once
|
||||||
|
|
||||||
### React UI (ui/)
|
### React UI (ui/)
|
||||||
|
|
||||||
- Tech stack: React 19, TypeScript, TanStack Query, Tailwind CSS v4, Radix UI, dagre (graph layout)
|
- Tech stack: React 19, TypeScript, Vite 7, TanStack Query, Tailwind CSS v4, Radix UI, dagre (graph layout), xterm.js (terminal)
|
||||||
- `src/App.tsx` - Main app with project selection, kanban board, agent controls
|
- `src/App.tsx` - Main app with project selection, kanban board, agent controls
|
||||||
- `src/hooks/useWebSocket.ts` - Real-time updates via WebSocket (progress, agent status, logs, agent updates)
|
- `src/hooks/useWebSocket.ts` - Real-time updates via WebSocket (progress, agent status, logs, agent updates)
|
||||||
- `src/hooks/useProjects.ts` - React Query hooks for API calls
|
- `src/hooks/useProjects.ts` - React Query hooks for API calls
|
||||||
@@ -187,6 +238,12 @@ Key components:
|
|||||||
- `DependencyGraph.tsx` - Interactive node graph visualization with dagre layout
|
- `DependencyGraph.tsx` - Interactive node graph visualization with dagre layout
|
||||||
- `CelebrationOverlay.tsx` - Confetti animation on feature completion
|
- `CelebrationOverlay.tsx` - Confetti animation on feature completion
|
||||||
- `FolderBrowser.tsx` - Server-side filesystem browser for project folder selection
|
- `FolderBrowser.tsx` - Server-side filesystem browser for project folder selection
|
||||||
|
- `Terminal.tsx` / `TerminalTabs.tsx` - xterm.js-based multi-tab terminal
|
||||||
|
- `AssistantPanel.tsx` / `AssistantChat.tsx` - AI assistant for project Q&A
|
||||||
|
- `ExpandProjectModal.tsx` / `ExpandProjectChat.tsx` - Add features via natural language
|
||||||
|
- `DevServerControl.tsx` - Dev server start/stop control
|
||||||
|
- `ScheduleModal.tsx` - Schedule management UI
|
||||||
|
- `SettingsModal.tsx` - Global settings panel
|
||||||
|
|
||||||
Keyboard shortcuts (press `?` for help):
|
Keyboard shortcuts (press `?` for help):
|
||||||
- `D` - Toggle debug panel
|
- `D` - Toggle debug panel
|
||||||
@@ -248,15 +305,6 @@ The following directories (relative to home) are always blocked:
|
|||||||
- `.docker`, `.config/gcloud` - Container/cloud configs
|
- `.docker`, `.config/gcloud` - Container/cloud configs
|
||||||
- `.npmrc`, `.pypirc`, `.netrc` - Package manager credentials
|
- `.npmrc`, `.pypirc`, `.netrc` - Package manager credentials
|
||||||
|
|
||||||
**Example Output:**
|
|
||||||
|
|
||||||
```
|
|
||||||
Created security settings at /path/to/project/.claude_settings.json
|
|
||||||
- Sandbox enabled (OS-level bash isolation)
|
|
||||||
- Filesystem restricted to: /path/to/project
|
|
||||||
- Extra read paths (validated): /Users/me/docs, /opt/shared-libs
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Per-Project Allowed Commands
|
#### Per-Project Allowed Commands
|
||||||
|
|
||||||
The agent's bash command access is controlled through a hierarchical configuration system:
|
The agent's bash command access is controlled through a hierarchical configuration system:
|
||||||
@@ -318,13 +366,29 @@ blocked_commands:
|
|||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- `security.py` - Command validation logic and hardcoded blocklist
|
- `security.py` - Command validation logic and hardcoded blocklist
|
||||||
- `test_security.py` - Unit tests for security system (136 tests)
|
- `test_security.py` - Unit tests for security system
|
||||||
- `test_security_integration.py` - Integration tests with real hooks (9 tests)
|
- `test_security_integration.py` - Integration tests with real hooks
|
||||||
- `TEST_SECURITY.md` - Quick testing reference guide
|
|
||||||
- `examples/project_allowed_commands.yaml` - Project config example (all commented by default)
|
- `examples/project_allowed_commands.yaml` - Project config example (all commented by default)
|
||||||
- `examples/org_config.yaml` - Org config example (all commented by default)
|
- `examples/org_config.yaml` - Org config example (all commented by default)
|
||||||
- `examples/README.md` - Comprehensive guide with use cases, testing, and troubleshooting
|
- `examples/README.md` - Comprehensive guide with use cases, testing, and troubleshooting
|
||||||
|
|
||||||
|
### Vertex AI Configuration (Optional)
|
||||||
|
|
||||||
|
Run coding agents via Google Cloud Vertex AI:
|
||||||
|
|
||||||
|
1. Install and authenticate gcloud CLI: `gcloud auth application-default login`
|
||||||
|
2. Configure `.env`:
|
||||||
|
```
|
||||||
|
CLAUDE_CODE_USE_VERTEX=1
|
||||||
|
CLOUD_ML_REGION=us-east5
|
||||||
|
ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-5@20251101
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4-5@20250929
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL=claude-3-5-haiku@20241022
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Use `@` instead of `-` in model names for Vertex AI.
|
||||||
|
|
||||||
### Ollama Local Models (Optional)
|
### Ollama Local Models (Optional)
|
||||||
|
|
||||||
Run coding agents using local models via Ollama v0.14.0+:
|
Run coding agents using local models via Ollama v0.14.0+:
|
||||||
@@ -360,8 +424,24 @@ Run coding agents using local models via Ollama v0.14.0+:
|
|||||||
|
|
||||||
## Claude Code Integration
|
## Claude Code Integration
|
||||||
|
|
||||||
- `.claude/commands/create-spec.md` - `/create-spec` slash command for interactive spec creation
|
**Slash commands** (`.claude/commands/`):
|
||||||
- `.claude/skills/frontend-design/SKILL.md` - Skill for distinctive UI design
|
- `/create-spec` - Interactive spec creation for new projects
|
||||||
|
- `/expand-project` - Expand existing project with new features
|
||||||
|
- `/gsd-to-autocoder-spec` - Convert GSD codebase mapping to app_spec.txt
|
||||||
|
- `/check-code` - Run lint and type-check for code quality
|
||||||
|
- `/checkpoint` - Create comprehensive checkpoint commit
|
||||||
|
- `/review-pr` - Review pull requests
|
||||||
|
|
||||||
|
**Custom agents** (`.claude/agents/`):
|
||||||
|
- `coder.md` - Elite software architect agent for code implementation (Opus)
|
||||||
|
- `code-review.md` - Code review agent for quality/security/performance analysis (Opus)
|
||||||
|
- `deep-dive.md` - Technical investigator for deep analysis and debugging (Opus)
|
||||||
|
|
||||||
|
**Skills** (`.claude/skills/`):
|
||||||
|
- `frontend-design` - Distinctive, production-grade UI design
|
||||||
|
- `gsd-to-autocoder-spec` - Convert GSD codebase mapping to Autocoder app_spec format
|
||||||
|
|
||||||
|
**Other:**
|
||||||
- `.claude/templates/` - Prompt templates copied to new projects
|
- `.claude/templates/` - Prompt templates copied to new projects
|
||||||
- `examples/` - Configuration examples and documentation for security settings
|
- `examples/` - Configuration examples and documentation for security settings
|
||||||
|
|
||||||
@@ -392,7 +472,7 @@ The UI receives updates via WebSocket (`/ws/projects/{project_name}`):
|
|||||||
|
|
||||||
When running with `--parallel`, the orchestrator:
|
When running with `--parallel`, the orchestrator:
|
||||||
1. Spawns multiple Claude agents as subprocesses (up to `--max-concurrency`)
|
1. Spawns multiple Claude agents as subprocesses (up to `--max-concurrency`)
|
||||||
2. Each agent claims features atomically via `feature_claim_next`
|
2. Each agent claims features atomically via `feature_claim_and_get`
|
||||||
3. Features blocked by unmet dependencies are skipped
|
3. Features blocked by unmet dependencies are skipped
|
||||||
4. Browser contexts are isolated per agent using `--isolated` flag
|
4. Browser contexts are isolated per agent using `--isolated` flag
|
||||||
5. AgentTracker parses output and emits `agent_update` messages for UI
|
5. AgentTracker parses output and emits `agent_update` messages for UI
|
||||||
@@ -405,6 +485,16 @@ The orchestrator enforces strict bounds on concurrent processes:
|
|||||||
- Testing agents are capped at `max_concurrency` (same as coding agents)
|
- Testing agents are capped at `max_concurrency` (same as coding agents)
|
||||||
- Total process count never exceeds 11 Python processes (1 orchestrator + 5 coding + 5 testing)
|
- Total process count never exceeds 11 Python processes (1 orchestrator + 5 coding + 5 testing)
|
||||||
|
|
||||||
|
### Multi-Feature Batching
|
||||||
|
|
||||||
|
Agents can implement multiple features per session using `--batch-size` (1-3, default: 3):
|
||||||
|
- `--batch-size N` - Max features per coding agent batch
|
||||||
|
- `--testing-batch-size N` - Features per testing batch (1-5, default: 3)
|
||||||
|
- `--batch-features 1,2,3` - Specific feature IDs for batch implementation
|
||||||
|
- `--testing-batch-features 1,2,3` - Specific feature IDs for batch regression testing
|
||||||
|
- `prompts.py` provides `get_batch_feature_prompt()` for multi-feature prompt generation
|
||||||
|
- Configurable in UI via settings panel
|
||||||
|
|
||||||
### Design System
|
### Design System
|
||||||
|
|
||||||
The UI uses a **neobrutalism** design with Tailwind CSS v4:
|
The UI uses a **neobrutalism** design with Tailwind CSS v4:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { ThemeSelector } from './components/ThemeSelector'
|
|||||||
import { ResetProjectModal } from './components/ResetProjectModal'
|
import { ResetProjectModal } from './components/ResetProjectModal'
|
||||||
import { ProjectSetupRequired } from './components/ProjectSetupRequired'
|
import { ProjectSetupRequired } from './components/ProjectSetupRequired'
|
||||||
import { getDependencyGraph, startAgent } from './lib/api'
|
import { getDependencyGraph, startAgent } from './lib/api'
|
||||||
import { Loader2, Settings, Moon, Sun, RotateCcw } from 'lucide-react'
|
import { Loader2, Settings, Moon, Sun, RotateCcw, BookOpen } from 'lucide-react'
|
||||||
import type { Feature } from './lib/types'
|
import type { Feature } from './lib/types'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card, CardContent } from '@/components/ui/card'
|
import { Card, CardContent } from '@/components/ui/card'
|
||||||
@@ -335,6 +335,17 @@ function App() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Docs link */}
|
||||||
|
<Button
|
||||||
|
onClick={() => { window.location.hash = '#/docs' }}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
title="Documentation"
|
||||||
|
aria-label="Open Documentation"
|
||||||
|
>
|
||||||
|
<BookOpen size={18} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
{/* Theme selector */}
|
{/* Theme selector */}
|
||||||
<ThemeSelector
|
<ThemeSelector
|
||||||
themes={themes}
|
themes={themes}
|
||||||
|
|||||||
130
ui/src/components/docs/DocsContent.tsx
Normal file
130
ui/src/components/docs/DocsContent.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* DocsContent Component
|
||||||
|
*
|
||||||
|
* Renders all 13 documentation section components in order.
|
||||||
|
* Uses IntersectionObserver to detect which section heading is currently
|
||||||
|
* visible in the viewport, and notifies the parent so the sidebar
|
||||||
|
* can highlight the active section.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useRef, useCallback } from 'react'
|
||||||
|
import { DOC_SECTIONS } from './docsData'
|
||||||
|
// Section components -- lazy-load candidates in the future, but imported
|
||||||
|
// statically for now to keep the build simple and deterministic.
|
||||||
|
import { GettingStarted } from './sections/GettingStarted'
|
||||||
|
import { AppSpecSetup } from './sections/AppSpecSetup'
|
||||||
|
import { ProjectStructure } from './sections/ProjectStructure'
|
||||||
|
import { FeaturesKanban } from './sections/FeaturesKanban'
|
||||||
|
import { AgentSystem } from './sections/AgentSystem'
|
||||||
|
import { SettingsConfig } from './sections/SettingsConfig'
|
||||||
|
import { DeveloperTools } from './sections/DeveloperTools'
|
||||||
|
import { AIAssistant } from './sections/AIAssistant'
|
||||||
|
import { Scheduling } from './sections/Scheduling'
|
||||||
|
import { AppearanceThemes } from './sections/AppearanceThemes'
|
||||||
|
import { Security } from './sections/Security'
|
||||||
|
import { AdvancedConfig } from './sections/AdvancedConfig'
|
||||||
|
import { FAQ } from './sections/FAQ'
|
||||||
|
|
||||||
|
interface DocsContentProps {
|
||||||
|
activeSectionId: string | null
|
||||||
|
onSectionVisible: (id: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps each section id from docsData to its corresponding React component.
|
||||||
|
* Order matches DOC_SECTIONS so we can iterate safely.
|
||||||
|
*/
|
||||||
|
const SECTION_COMPONENTS: Record<string, React.FC> = {
|
||||||
|
'getting-started': GettingStarted,
|
||||||
|
'app-spec-setup': AppSpecSetup,
|
||||||
|
'project-structure': ProjectStructure,
|
||||||
|
'features-kanban': FeaturesKanban,
|
||||||
|
'agent-system': AgentSystem,
|
||||||
|
'settings-config': SettingsConfig,
|
||||||
|
'developer-tools': DeveloperTools,
|
||||||
|
'ai-assistant': AIAssistant,
|
||||||
|
scheduling: Scheduling,
|
||||||
|
'appearance-themes': AppearanceThemes,
|
||||||
|
security: Security,
|
||||||
|
'advanced-config': AdvancedConfig,
|
||||||
|
faq: FAQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DocsContent({ onSectionVisible }: DocsContentProps) {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
// Store refs to each section heading element so the observer can watch them
|
||||||
|
const headingRefs = useRef<Map<string, HTMLElement>>(new Map())
|
||||||
|
|
||||||
|
// Stable callback ref setter -- avoids recreating refs on every render
|
||||||
|
const setHeadingRef = useCallback((id: string, element: HTMLElement | null) => {
|
||||||
|
if (element) {
|
||||||
|
headingRefs.current.set(id, element)
|
||||||
|
} else {
|
||||||
|
headingRefs.current.delete(id)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// IntersectionObserver: track which section heading is at or near the top of the viewport
|
||||||
|
useEffect(() => {
|
||||||
|
const headings = headingRefs.current
|
||||||
|
if (headings.size === 0) return
|
||||||
|
|
||||||
|
// rootMargin: trigger when a heading enters the top 20% of the viewport.
|
||||||
|
// This ensures the sidebar updates *before* the user scrolls past the heading.
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
// Find the topmost visible heading -- the one closest to the top of the viewport
|
||||||
|
const visible = entries
|
||||||
|
.filter((entry) => entry.isIntersecting)
|
||||||
|
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
|
||||||
|
|
||||||
|
if (visible.length > 0) {
|
||||||
|
const topEntry = visible[0]
|
||||||
|
const sectionId = topEntry.target.getAttribute('data-section-id')
|
||||||
|
if (sectionId) {
|
||||||
|
onSectionVisible(sectionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Observe from the very top of the viewport down to -60% from the bottom,
|
||||||
|
// so headings are detected while in the upper portion of the screen.
|
||||||
|
rootMargin: '0px 0px -60% 0px',
|
||||||
|
threshold: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
headings.forEach((element) => observer.observe(element))
|
||||||
|
|
||||||
|
return () => observer.disconnect()
|
||||||
|
}, [onSectionVisible])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="docs-prose">
|
||||||
|
{DOC_SECTIONS.map((section) => {
|
||||||
|
const SectionComponent = SECTION_COMPONENTS[section.id]
|
||||||
|
if (!SectionComponent) return null
|
||||||
|
|
||||||
|
const Icon = section.icon
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={section.id} id={section.id} className="scroll-mt-24 mb-16">
|
||||||
|
{/* Section heading with anchor */}
|
||||||
|
<h2
|
||||||
|
ref={(el) => setHeadingRef(section.id, el)}
|
||||||
|
data-section-id={section.id}
|
||||||
|
className="font-display text-2xl font-bold tracking-tight mb-6 flex items-center gap-3
|
||||||
|
text-foreground border-b-2 border-border pb-3"
|
||||||
|
>
|
||||||
|
<Icon size={24} className="text-primary shrink-0" />
|
||||||
|
{section.title}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Section body */}
|
||||||
|
<SectionComponent />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
215
ui/src/components/docs/DocsPage.tsx
Normal file
215
ui/src/components/docs/DocsPage.tsx
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/**
|
||||||
|
* DocsPage Component
|
||||||
|
*
|
||||||
|
* Main layout for the documentation route (#/docs).
|
||||||
|
* Full-page layout with a sticky header, collapsible sidebar on the left,
|
||||||
|
* and scrollable content area on the right.
|
||||||
|
*
|
||||||
|
* Mobile-responsive: sidebar collapses behind a hamburger menu that
|
||||||
|
* opens as an overlay.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { ArrowLeft, Menu, X, Moon, Sun } from 'lucide-react'
|
||||||
|
import { useHashRoute } from '../../hooks/useHashRoute'
|
||||||
|
import { useTheme } from '../../hooks/useTheme'
|
||||||
|
import { ThemeSelector } from '../ThemeSelector'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { DocsSidebar } from './DocsSidebar'
|
||||||
|
import { DocsSearch } from './DocsSearch'
|
||||||
|
import { DocsContent } from './DocsContent'
|
||||||
|
|
||||||
|
export function DocsPage() {
|
||||||
|
const [activeSectionId, setActiveSectionId] = useState<string | null>(null)
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false)
|
||||||
|
|
||||||
|
const { section: initialSection } = useHashRoute()
|
||||||
|
const { theme, setTheme, darkMode, toggleDarkMode, themes } = useTheme()
|
||||||
|
|
||||||
|
// On mount, if the hash includes a section id (e.g. #/docs/getting-started),
|
||||||
|
// scroll to it and set it as active
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialSection) {
|
||||||
|
setActiveSectionId(initialSection)
|
||||||
|
// Delay scroll slightly so the DOM is rendered
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const element = document.getElementById(initialSection)
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []) // Run only on mount
|
||||||
|
|
||||||
|
// When a sidebar item is clicked, scroll the corresponding element into view
|
||||||
|
const handleSectionClick = useCallback((id: string) => {
|
||||||
|
setActiveSectionId(id)
|
||||||
|
|
||||||
|
// Update hash for linkability (without triggering a route change)
|
||||||
|
history.replaceState(null, '', `#/docs/${id}`)
|
||||||
|
|
||||||
|
const element = document.getElementById(id)
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Called by DocsContent's IntersectionObserver when a heading scrolls into view
|
||||||
|
const handleSectionVisible = useCallback((id: string) => {
|
||||||
|
setActiveSectionId(id)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Close mobile sidebar when pressing Escape
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape' && mobileSidebarOpen) {
|
||||||
|
setMobileSidebarOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}, [mobileSidebarOpen])
|
||||||
|
|
||||||
|
// Prevent body scroll when mobile sidebar overlay is open
|
||||||
|
useEffect(() => {
|
||||||
|
if (mobileSidebarOpen) {
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
}, [mobileSidebarOpen])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-background">
|
||||||
|
{/* Sticky header */}
|
||||||
|
<header className="sticky top-0 z-50 bg-card/80 backdrop-blur-md text-foreground border-b-2 border-border">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 py-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
{/* Left side: hamburger (mobile) + title + badge */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{/* Mobile hamburger button -- only visible below lg breakpoint */}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
className="lg:hidden"
|
||||||
|
onClick={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
||||||
|
aria-label={mobileSidebarOpen ? 'Close sidebar' : 'Open sidebar'}
|
||||||
|
>
|
||||||
|
{mobileSidebarOpen ? <X size={20} /> : <Menu size={20} />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="#/"
|
||||||
|
className="font-display text-xl font-bold tracking-tight uppercase text-foreground
|
||||||
|
hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
|
AutoCoder
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<Badge variant="secondary" className="text-xs font-medium">
|
||||||
|
Documentation
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side: theme controls + back button */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ThemeSelector
|
||||||
|
themes={themes}
|
||||||
|
currentTheme={theme}
|
||||||
|
onThemeChange={setTheme}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={toggleDarkMode}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
title="Toggle dark mode"
|
||||||
|
aria-label="Toggle dark mode"
|
||||||
|
>
|
||||||
|
{darkMode ? <Sun size={18} /> : <Moon size={18} />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<a href="#/" className="inline-flex items-center gap-1.5">
|
||||||
|
<ArrowLeft size={16} />
|
||||||
|
<span className="hidden sm:inline">Back to App</span>
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Body: sidebar + content */}
|
||||||
|
<div className="max-w-7xl mx-auto flex">
|
||||||
|
{/* ----------------------------------------------------------------
|
||||||
|
Desktop sidebar -- visible at lg and above
|
||||||
|
Fixed width, sticky below the header, independently scrollable
|
||||||
|
---------------------------------------------------------------- */}
|
||||||
|
<aside
|
||||||
|
className="hidden lg:block w-[280px] shrink-0 sticky top-[57px] h-[calc(100vh-57px)]
|
||||||
|
overflow-y-auto border-r border-border p-4 space-y-4"
|
||||||
|
>
|
||||||
|
<DocsSearch value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
<DocsSidebar
|
||||||
|
activeSectionId={activeSectionId}
|
||||||
|
onSectionClick={handleSectionClick}
|
||||||
|
searchQuery={searchQuery}
|
||||||
|
/>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* ----------------------------------------------------------------
|
||||||
|
Mobile sidebar overlay -- visible below lg breakpoint
|
||||||
|
---------------------------------------------------------------- */}
|
||||||
|
{mobileSidebarOpen && (
|
||||||
|
<>
|
||||||
|
{/* Backdrop */}
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-40 bg-background/60 backdrop-blur-sm lg:hidden"
|
||||||
|
onClick={() => setMobileSidebarOpen(false)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Sidebar panel */}
|
||||||
|
<aside
|
||||||
|
className="fixed top-[57px] left-0 z-50 w-[280px] h-[calc(100vh-57px)]
|
||||||
|
overflow-y-auto bg-card border-r-2 border-border p-4 space-y-4
|
||||||
|
animate-slide-in lg:hidden"
|
||||||
|
>
|
||||||
|
<DocsSearch value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
<DocsSidebar
|
||||||
|
activeSectionId={activeSectionId}
|
||||||
|
onSectionClick={handleSectionClick}
|
||||||
|
searchQuery={searchQuery}
|
||||||
|
onMobileClose={() => setMobileSidebarOpen(false)}
|
||||||
|
/>
|
||||||
|
</aside>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ----------------------------------------------------------------
|
||||||
|
Content area -- fills remaining space, scrollable
|
||||||
|
---------------------------------------------------------------- */}
|
||||||
|
<main className="flex-1 min-w-0 px-6 py-8 lg:px-10">
|
||||||
|
<div className="max-w-[65ch] mx-auto">
|
||||||
|
<DocsContent
|
||||||
|
activeSectionId={activeSectionId}
|
||||||
|
onSectionVisible={handleSectionVisible}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
78
ui/src/components/docs/DocsSearch.tsx
Normal file
78
ui/src/components/docs/DocsSearch.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* DocsSearch Component
|
||||||
|
*
|
||||||
|
* Search input for the documentation sidebar.
|
||||||
|
* Supports Ctrl/Cmd+K keyboard shortcut to focus,
|
||||||
|
* and shows a keyboard hint when the input is empty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useRef, useEffect } from 'react'
|
||||||
|
import { Search, X } from 'lucide-react'
|
||||||
|
|
||||||
|
interface DocsSearchProps {
|
||||||
|
value: string
|
||||||
|
onChange: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DocsSearch({ value, onChange }: DocsSearchProps) {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
// Global keyboard shortcut: Ctrl/Cmd+K focuses the search input
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||||
|
e.preventDefault()
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
{/* Search icon */}
|
||||||
|
<Search
|
||||||
|
size={16}
|
||||||
|
className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
placeholder="Search docs..."
|
||||||
|
className="w-full pl-9 pr-16 py-2 text-sm bg-muted border border-border rounded-lg
|
||||||
|
text-foreground placeholder:text-muted-foreground
|
||||||
|
focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-ring
|
||||||
|
transition-colors"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Right side: clear button when has value, otherwise Ctrl+K hint */}
|
||||||
|
{value ? (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
onChange('')
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground
|
||||||
|
hover:text-foreground transition-colors"
|
||||||
|
aria-label="Clear search"
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<kbd
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2
|
||||||
|
text-[10px] text-muted-foreground bg-background
|
||||||
|
border border-border rounded px-1.5 py-0.5
|
||||||
|
pointer-events-none select-none"
|
||||||
|
>
|
||||||
|
Ctrl+K
|
||||||
|
</kbd>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
189
ui/src/components/docs/DocsSidebar.tsx
Normal file
189
ui/src/components/docs/DocsSidebar.tsx
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/**
|
||||||
|
* DocsSidebar Component
|
||||||
|
*
|
||||||
|
* Left sidebar navigation for the documentation page.
|
||||||
|
* Lists all sections from docsData with expandable subsections.
|
||||||
|
* Supports search filtering with auto-expansion of matching sections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState, useMemo } from 'react'
|
||||||
|
import { ChevronRight } from 'lucide-react'
|
||||||
|
import { DOC_SECTIONS, type DocSection } from './docsData'
|
||||||
|
|
||||||
|
interface DocsSidebarProps {
|
||||||
|
activeSectionId: string | null
|
||||||
|
onSectionClick: (id: string) => void
|
||||||
|
searchQuery: string
|
||||||
|
onMobileClose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DocsSidebar({
|
||||||
|
activeSectionId,
|
||||||
|
onSectionClick,
|
||||||
|
searchQuery,
|
||||||
|
onMobileClose,
|
||||||
|
}: DocsSidebarProps) {
|
||||||
|
// Track which top-level sections are manually expanded by the user
|
||||||
|
const [expandedSections, setExpandedSections] = useState<Set<string>>(() => {
|
||||||
|
// Start with the first section expanded so the sidebar is not fully collapsed
|
||||||
|
const initial = new Set<string>()
|
||||||
|
if (DOC_SECTIONS.length > 0) {
|
||||||
|
initial.add(DOC_SECTIONS[0].id)
|
||||||
|
}
|
||||||
|
return initial
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizedQuery = searchQuery.trim().toLowerCase()
|
||||||
|
|
||||||
|
// Filter sections based on search query, matching against section title,
|
||||||
|
// subsection titles, and keywords
|
||||||
|
const filteredSections = useMemo(() => {
|
||||||
|
if (!normalizedQuery) {
|
||||||
|
return DOC_SECTIONS
|
||||||
|
}
|
||||||
|
|
||||||
|
return DOC_SECTIONS.filter((section) => {
|
||||||
|
// Check section title
|
||||||
|
if (section.title.toLowerCase().includes(normalizedQuery)) return true
|
||||||
|
|
||||||
|
// Check keywords
|
||||||
|
if (section.keywords.some((kw) => kw.toLowerCase().includes(normalizedQuery))) return true
|
||||||
|
|
||||||
|
// Check subsection titles
|
||||||
|
if (section.subsections.some((sub) => sub.title.toLowerCase().includes(normalizedQuery))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}, [normalizedQuery])
|
||||||
|
|
||||||
|
// Determine which sections should appear expanded:
|
||||||
|
// - When searching: auto-expand all matching sections
|
||||||
|
// - Otherwise: use manual expanded state, plus expand whichever section contains the active item
|
||||||
|
const isSectionExpanded = (sectionId: string): boolean => {
|
||||||
|
if (normalizedQuery) return true
|
||||||
|
|
||||||
|
if (expandedSections.has(sectionId)) return true
|
||||||
|
|
||||||
|
// Also expand the section that contains the currently active subsection
|
||||||
|
if (activeSectionId) {
|
||||||
|
const section = DOC_SECTIONS.find((s) => s.id === sectionId)
|
||||||
|
if (section) {
|
||||||
|
if (section.id === activeSectionId) return true
|
||||||
|
if (section.subsections.some((sub) => sub.id === activeSectionId)) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleSection = (sectionId: string) => {
|
||||||
|
setExpandedSections((prev) => {
|
||||||
|
const next = new Set(prev)
|
||||||
|
if (next.has(sectionId)) {
|
||||||
|
next.delete(sectionId)
|
||||||
|
} else {
|
||||||
|
next.add(sectionId)
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given id (section or subsection) is the currently active item.
|
||||||
|
* Active items get a highlighted visual treatment.
|
||||||
|
*/
|
||||||
|
const isActive = (id: string): boolean => activeSectionId === id
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a section contains the active subsection.
|
||||||
|
* Used to highlight parent sections in a muted way.
|
||||||
|
*/
|
||||||
|
const sectionContainsActive = (section: DocSection): boolean => {
|
||||||
|
if (!activeSectionId) return false
|
||||||
|
return section.subsections.some((sub) => sub.id === activeSectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleItemClick = (id: string) => {
|
||||||
|
onSectionClick(id)
|
||||||
|
// On mobile, close the sidebar after navigation
|
||||||
|
onMobileClose?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav aria-label="Documentation navigation" className="space-y-1">
|
||||||
|
{filteredSections.map((section) => {
|
||||||
|
const Icon = section.icon
|
||||||
|
const expanded = isSectionExpanded(section.id)
|
||||||
|
const active = isActive(section.id)
|
||||||
|
const containsActive = sectionContainsActive(section)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={section.id}>
|
||||||
|
{/* Section header (clickable to expand/collapse and navigate) */}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
toggleSection(section.id)
|
||||||
|
handleItemClick(section.id)
|
||||||
|
}}
|
||||||
|
className={`w-full flex items-center gap-2 px-3 py-2 text-sm rounded-md
|
||||||
|
transition-colors cursor-pointer group
|
||||||
|
${active
|
||||||
|
? 'bg-primary/10 border-l-2 border-primary text-foreground font-semibold'
|
||||||
|
: containsActive
|
||||||
|
? 'text-foreground font-medium'
|
||||||
|
: 'text-muted-foreground hover:text-foreground hover:bg-muted'
|
||||||
|
}`}
|
||||||
|
aria-expanded={expanded}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
size={16}
|
||||||
|
className={`shrink-0 ${active ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground'}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span className="flex-1 text-left truncate">{section.title}</span>
|
||||||
|
|
||||||
|
<ChevronRight
|
||||||
|
size={14}
|
||||||
|
className={`shrink-0 text-muted-foreground transition-transform duration-200
|
||||||
|
${expanded ? 'rotate-90' : ''}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Subsections (shown when expanded) */}
|
||||||
|
{expanded && (
|
||||||
|
<div className="ml-4 mt-0.5 space-y-0.5 border-l border-border animate-slide-in-down">
|
||||||
|
{section.subsections.map((sub) => {
|
||||||
|
const subActive = isActive(sub.id)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={sub.id}
|
||||||
|
onClick={() => handleItemClick(sub.id)}
|
||||||
|
className={`w-full text-left px-3 py-1.5 text-sm rounded-r-md
|
||||||
|
transition-colors cursor-pointer
|
||||||
|
${subActive
|
||||||
|
? 'bg-primary/10 border-l-2 border-primary text-foreground font-medium -ml-px'
|
||||||
|
: 'text-muted-foreground hover:text-foreground hover:bg-muted'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{sub.title}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* No results message when search filters everything out */}
|
||||||
|
{normalizedQuery && filteredSections.length === 0 && (
|
||||||
|
<div className="px-3 py-6 text-center text-sm text-muted-foreground">
|
||||||
|
No sections match “{searchQuery}”
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
222
ui/src/components/docs/docsData.ts
Normal file
222
ui/src/components/docs/docsData.ts
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import {
|
||||||
|
Rocket,
|
||||||
|
FileText,
|
||||||
|
FolderTree,
|
||||||
|
LayoutGrid,
|
||||||
|
Bot,
|
||||||
|
Settings,
|
||||||
|
Terminal,
|
||||||
|
MessageSquare,
|
||||||
|
Clock,
|
||||||
|
Palette,
|
||||||
|
Shield,
|
||||||
|
Wrench,
|
||||||
|
HelpCircle,
|
||||||
|
type LucideIcon,
|
||||||
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
export interface DocSubsection {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DocSection {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
icon: LucideIcon
|
||||||
|
subsections: DocSubsection[]
|
||||||
|
keywords: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DOC_SECTIONS: DocSection[] = [
|
||||||
|
{
|
||||||
|
id: 'getting-started',
|
||||||
|
title: 'Getting Started',
|
||||||
|
icon: Rocket,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'what-is-autocoder', title: 'What is AutoCoder?' },
|
||||||
|
{ id: 'quick-start', title: 'Quick Start' },
|
||||||
|
{ id: 'creating-a-project', title: 'Creating a New Project' },
|
||||||
|
{ id: 'existing-project', title: 'Adding to an Existing Project' },
|
||||||
|
{ id: 'system-requirements', title: 'System Requirements' },
|
||||||
|
],
|
||||||
|
keywords: ['install', 'setup', 'start', 'begin', 'new', 'requirements', 'prerequisites'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'app-spec-setup',
|
||||||
|
title: 'App Spec & Project Setup',
|
||||||
|
icon: FileText,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'what-is-app-spec', title: 'What is an App Spec?' },
|
||||||
|
{ id: 'creating-spec-with-claude', title: 'Creating a Spec with Claude' },
|
||||||
|
{ id: 'writing-spec-manually', title: 'Writing a Spec Manually' },
|
||||||
|
{ id: 'initializer-agent', title: 'The Initializer Agent' },
|
||||||
|
{ id: 'starting-after-spec', title: 'Starting After Spec Creation' },
|
||||||
|
],
|
||||||
|
keywords: ['spec', 'specification', 'xml', 'app_spec', 'initializer', 'prompt', 'template'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-structure',
|
||||||
|
title: 'Target Project Structure',
|
||||||
|
icon: FolderTree,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'autocoder-directory', title: '.autocoder/ Directory Layout' },
|
||||||
|
{ id: 'features-db', title: 'Features Database' },
|
||||||
|
{ id: 'prompts-directory', title: 'Prompts Directory' },
|
||||||
|
{ id: 'allowed-commands-yaml', title: 'Allowed Commands Config' },
|
||||||
|
{ id: 'claude-md', title: 'CLAUDE.md Convention' },
|
||||||
|
{ id: 'legacy-migration', title: 'Legacy Layout Migration' },
|
||||||
|
{ id: 'claude-inheritance', title: 'Claude Inheritance' },
|
||||||
|
],
|
||||||
|
keywords: ['folder', 'directory', 'structure', 'layout', 'files', 'database', 'sqlite', 'migration'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'features-kanban',
|
||||||
|
title: 'Features & Kanban Board',
|
||||||
|
icon: LayoutGrid,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'kanban-overview', title: 'Kanban Board Overview' },
|
||||||
|
{ id: 'feature-cards', title: 'Feature Cards' },
|
||||||
|
{ id: 'dependency-graph', title: 'Dependency Graph View' },
|
||||||
|
{ id: 'adding-features', title: 'Adding Features' },
|
||||||
|
{ id: 'editing-features', title: 'Editing & Deleting Features' },
|
||||||
|
{ id: 'feature-dependencies', title: 'Feature Dependencies' },
|
||||||
|
{ id: 'expanding-with-ai', title: 'Expanding Project with AI' },
|
||||||
|
{ id: 'feature-priority', title: 'Priority & Ordering' },
|
||||||
|
],
|
||||||
|
keywords: ['kanban', 'board', 'feature', 'card', 'dependency', 'graph', 'priority', 'pending', 'progress', 'done'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'agent-system',
|
||||||
|
title: 'Agent System',
|
||||||
|
icon: Bot,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'maestro-orchestrator', title: 'Maestro: The Orchestrator' },
|
||||||
|
{ id: 'coding-agents', title: 'Coding Agents' },
|
||||||
|
{ id: 'testing-agents', title: 'Testing Agents' },
|
||||||
|
{ id: 'agent-lifecycle', title: 'Agent Lifecycle' },
|
||||||
|
{ id: 'concurrency', title: 'Concurrency Control' },
|
||||||
|
{ id: 'mission-control', title: 'Agent Mission Control' },
|
||||||
|
{ id: 'agent-mascots', title: 'Agent Mascots & States' },
|
||||||
|
{ id: 'agent-logs', title: 'Viewing Agent Logs' },
|
||||||
|
{ id: 'process-limits', title: 'Process Limits' },
|
||||||
|
],
|
||||||
|
keywords: ['agent', 'maestro', 'orchestrator', 'coding', 'testing', 'parallel', 'concurrency', 'mascot', 'spark', 'fizz', 'octo', 'batch'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'settings-config',
|
||||||
|
title: 'Settings & Configuration',
|
||||||
|
icon: Settings,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'opening-settings', title: 'Opening Settings' },
|
||||||
|
{ id: 'yolo-mode', title: 'YOLO Mode' },
|
||||||
|
{ id: 'headless-browser', title: 'Headless Browser' },
|
||||||
|
{ id: 'model-selection', title: 'Model Selection' },
|
||||||
|
{ id: 'regression-agents', title: 'Regression Agents' },
|
||||||
|
{ id: 'features-per-agent', title: 'Features per Agent (Batch Size)' },
|
||||||
|
{ id: 'concurrency-setting', title: 'Concurrency' },
|
||||||
|
{ id: 'settings-persistence', title: 'How Settings are Persisted' },
|
||||||
|
],
|
||||||
|
keywords: ['settings', 'config', 'yolo', 'headless', 'model', 'opus', 'sonnet', 'haiku', 'batch', 'regression'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'developer-tools',
|
||||||
|
title: 'Developer Tools',
|
||||||
|
icon: Terminal,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'debug-panel', title: 'Debug Panel' },
|
||||||
|
{ id: 'agent-logs-tab', title: 'Agent Logs Tab' },
|
||||||
|
{ id: 'dev-server-logs', title: 'Dev Server Logs Tab' },
|
||||||
|
{ id: 'terminal', title: 'Terminal' },
|
||||||
|
{ id: 'dev-server-control', title: 'Dev Server Control' },
|
||||||
|
{ id: 'per-agent-logs', title: 'Per-Agent Logs' },
|
||||||
|
],
|
||||||
|
keywords: ['debug', 'terminal', 'logs', 'dev server', 'console', 'xterm', 'shell'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ai-assistant',
|
||||||
|
title: 'AI Assistant',
|
||||||
|
icon: MessageSquare,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'what-is-assistant', title: 'What is the Assistant?' },
|
||||||
|
{ id: 'opening-assistant', title: 'Opening the Assistant' },
|
||||||
|
{ id: 'assistant-capabilities', title: 'What It Can Do' },
|
||||||
|
{ id: 'assistant-limitations', title: 'What It Cannot Do' },
|
||||||
|
{ id: 'conversation-history', title: 'Conversation History' },
|
||||||
|
],
|
||||||
|
keywords: ['assistant', 'ai', 'chat', 'help', 'question', 'conversation'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'scheduling',
|
||||||
|
title: 'Scheduling',
|
||||||
|
icon: Clock,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'what-scheduling-does', title: 'What Scheduling Does' },
|
||||||
|
{ id: 'creating-schedule', title: 'Creating a Schedule' },
|
||||||
|
{ id: 'schedule-settings', title: 'Schedule Settings' },
|
||||||
|
{ id: 'schedule-overrides', title: 'Schedule Overrides' },
|
||||||
|
{ id: 'crash-recovery', title: 'Crash Recovery' },
|
||||||
|
],
|
||||||
|
keywords: ['schedule', 'timer', 'automated', 'cron', 'run', 'recurring', 'utc'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'appearance-themes',
|
||||||
|
title: 'Appearance & Themes',
|
||||||
|
icon: Palette,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'themes-overview', title: 'Themes Overview' },
|
||||||
|
{ id: 'dark-light-mode', title: 'Dark & Light Mode' },
|
||||||
|
{ id: 'theme-selector', title: 'Theme Selector' },
|
||||||
|
{ id: 'keyboard-shortcuts', title: 'Keyboard Shortcuts' },
|
||||||
|
],
|
||||||
|
keywords: ['theme', 'dark', 'light', 'color', 'appearance', 'twitter', 'claude', 'neo', 'brutalism', 'retro', 'aurora', 'business', 'keyboard', 'shortcut'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'security',
|
||||||
|
title: 'Security',
|
||||||
|
icon: Shield,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'command-validation', title: 'Command Validation Overview' },
|
||||||
|
{ id: 'command-hierarchy', title: 'Command Hierarchy' },
|
||||||
|
{ id: 'hardcoded-blocklist', title: 'Hardcoded Blocklist' },
|
||||||
|
{ id: 'global-allowlist', title: 'Global Allowlist' },
|
||||||
|
{ id: 'project-allowlist', title: 'Per-Project Allowed Commands' },
|
||||||
|
{ id: 'org-config', title: 'Organization Configuration' },
|
||||||
|
{ id: 'extra-read-paths', title: 'Extra Read Paths' },
|
||||||
|
{ id: 'filesystem-sandboxing', title: 'Filesystem Sandboxing' },
|
||||||
|
],
|
||||||
|
keywords: ['security', 'sandbox', 'allowlist', 'blocklist', 'command', 'bash', 'permission', 'filesystem'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'advanced-config',
|
||||||
|
title: 'Advanced Configuration',
|
||||||
|
icon: Wrench,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'vertex-ai', title: 'Vertex AI Setup' },
|
||||||
|
{ id: 'ollama', title: 'Ollama Local Models' },
|
||||||
|
{ id: 'env-variables', title: 'Environment Variables' },
|
||||||
|
{ id: 'cli-arguments', title: 'CLI Arguments' },
|
||||||
|
{ id: 'webhooks', title: 'Webhook Support' },
|
||||||
|
{ id: 'project-registry', title: 'Project Registry' },
|
||||||
|
],
|
||||||
|
keywords: ['vertex', 'gcloud', 'ollama', 'local', 'env', 'environment', 'cli', 'webhook', 'n8n', 'registry', 'api'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'faq',
|
||||||
|
title: 'FAQ & Troubleshooting',
|
||||||
|
icon: HelpCircle,
|
||||||
|
subsections: [
|
||||||
|
{ id: 'faq-new-project', title: 'Starting a New Project' },
|
||||||
|
{ id: 'faq-existing-project', title: 'Adding to Existing Project' },
|
||||||
|
{ id: 'faq-agent-crash', title: 'Agent Crashes' },
|
||||||
|
{ id: 'faq-custom-commands', title: 'Custom Bash Commands' },
|
||||||
|
{ id: 'faq-blocked-features', title: 'Blocked Features' },
|
||||||
|
{ id: 'faq-parallel', title: 'Running in Parallel' },
|
||||||
|
{ id: 'faq-local-model', title: 'Using Local Models' },
|
||||||
|
{ id: 'faq-reset', title: 'Resetting a Project' },
|
||||||
|
{ id: 'faq-agent-types', title: 'Coding vs Testing Agents' },
|
||||||
|
{ id: 'faq-real-time', title: 'Monitoring in Real Time' },
|
||||||
|
],
|
||||||
|
keywords: ['faq', 'troubleshoot', 'help', 'problem', 'issue', 'fix', 'error', 'stuck', 'reset', 'crash'],
|
||||||
|
},
|
||||||
|
]
|
||||||
75
ui/src/components/docs/sections/AIAssistant.tsx
Normal file
75
ui/src/components/docs/sections/AIAssistant.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* AIAssistant Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the project assistant: what it is, how to open it,
|
||||||
|
* its capabilities and limitations, and conversation history.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function AIAssistant() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* What is the Assistant? */}
|
||||||
|
<h3 id="what-is-assistant" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What is the Assistant?
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
The AI Assistant is a read-only project helper that can answer questions about your project, search
|
||||||
|
code, view progress, and help you understand what’s happening — without making any changes.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Opening the Assistant */}
|
||||||
|
<h3 id="opening-assistant" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Opening the Assistant
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">A</Badge> to toggle the assistant panel
|
||||||
|
</li>
|
||||||
|
<li>Or click the floating action button (chat bubble) in the bottom-right corner</li>
|
||||||
|
<li>The panel slides in from the right side</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* What It Can Do */}
|
||||||
|
<h3 id="assistant-capabilities" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What It Can Do
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Read and search your project’s source code</li>
|
||||||
|
<li>Answer questions about code architecture and implementation</li>
|
||||||
|
<li>View feature progress and status</li>
|
||||||
|
<li>Create new features based on your description</li>
|
||||||
|
<li>Explain what agents have done or are currently doing</li>
|
||||||
|
<li>Help debug issues by analyzing code and logs</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* What It Cannot Do */}
|
||||||
|
<h3 id="assistant-limitations" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What It Cannot Do
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Modify files (read-only access)</li>
|
||||||
|
<li>Run bash commands</li>
|
||||||
|
<li>Mark features as passing/failing</li>
|
||||||
|
<li>Start or stop agents</li>
|
||||||
|
<li>Access external APIs or the internet</li>
|
||||||
|
</ul>
|
||||||
|
<div className="border-l-4 border-primary pl-4 italic text-muted-foreground mt-4">
|
||||||
|
This is a deliberate security design — the assistant is a safe way to interact with your project
|
||||||
|
without risk of unintended changes.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Conversation History */}
|
||||||
|
<h3 id="conversation-history" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Conversation History
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Conversations are stored per-project in SQLite database</li>
|
||||||
|
<li>Multiple conversations supported — start new ones as needed</li>
|
||||||
|
<li>Switch between conversations using the conversation selector</li>
|
||||||
|
<li>History persists across browser sessions</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
220
ui/src/components/docs/sections/AdvancedConfig.tsx
Normal file
220
ui/src/components/docs/sections/AdvancedConfig.tsx
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* AdvancedConfig Documentation Section
|
||||||
|
*
|
||||||
|
* Covers Vertex AI setup, Ollama local models, environment variables,
|
||||||
|
* CLI arguments, webhook support, and the project registry.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
/** Environment variable descriptor for the reference table. */
|
||||||
|
interface EnvVar {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ENV_VARS: EnvVar[] = [
|
||||||
|
{ name: 'CLAUDE_CODE_USE_VERTEX', description: 'Enable Vertex AI (1)' },
|
||||||
|
{ name: 'CLOUD_ML_REGION', description: 'GCP region' },
|
||||||
|
{ name: 'ANTHROPIC_VERTEX_PROJECT_ID', description: 'GCP project ID' },
|
||||||
|
{ name: 'ANTHROPIC_BASE_URL', description: 'Custom API base URL (for Ollama)' },
|
||||||
|
{ name: 'ANTHROPIC_AUTH_TOKEN', description: 'API auth token' },
|
||||||
|
{ name: 'API_TIMEOUT_MS', description: 'API timeout in milliseconds' },
|
||||||
|
{ name: 'EXTRA_READ_PATHS', description: 'Comma-separated extra read directories' },
|
||||||
|
{ name: 'ANTHROPIC_DEFAULT_OPUS_MODEL', description: 'Override Opus model name' },
|
||||||
|
{ name: 'ANTHROPIC_DEFAULT_SONNET_MODEL', description: 'Override Sonnet model name' },
|
||||||
|
{ name: 'ANTHROPIC_DEFAULT_HAIKU_MODEL', description: 'Override Haiku model name' },
|
||||||
|
]
|
||||||
|
|
||||||
|
/** CLI argument descriptor for the reference table. */
|
||||||
|
interface CliArg {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLI_ARGS: CliArg[] = [
|
||||||
|
{ name: '--project-dir', description: 'Project directory path or registered name' },
|
||||||
|
{ name: '--yolo', description: 'Enable YOLO mode' },
|
||||||
|
{ name: '--parallel', description: 'Enable parallel mode' },
|
||||||
|
{ name: '--max-concurrency N', description: 'Max concurrent agents (1-5)' },
|
||||||
|
{ name: '--batch-size N', description: 'Features per coding agent (1-3)' },
|
||||||
|
{ name: '--batch-features 1,2,3', description: 'Specific feature IDs to implement' },
|
||||||
|
{ name: '--testing-batch-size N', description: 'Features per testing batch (1-5)' },
|
||||||
|
{ name: '--testing-batch-features 1,2,3', description: 'Specific testing feature IDs' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function AdvancedConfig() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Vertex AI Setup */}
|
||||||
|
<h3 id="vertex-ai" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Vertex AI Setup
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Run coding agents via Google Cloud Vertex AI:
|
||||||
|
</p>
|
||||||
|
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Install and authenticate the gcloud CLI:{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
gcloud auth application-default login
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Configure your{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.env</span> file:
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm mt-3">
|
||||||
|
<pre><code>{`CLAUDE_CODE_USE_VERTEX=1
|
||||||
|
CLOUD_ML_REGION=us-east5
|
||||||
|
ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-5@20251101
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4-5@20250929
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL=claude-3-5-haiku@20241022`}</code></pre>
|
||||||
|
</div>
|
||||||
|
<blockquote className="border-l-4 border-primary pl-4 italic text-muted-foreground mt-4">
|
||||||
|
Use <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono not-italic">@</span>{' '}
|
||||||
|
instead of <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono not-italic">-</span>{' '}
|
||||||
|
in model names for Vertex AI.
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
{/* Ollama Local Models */}
|
||||||
|
<h3 id="ollama" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Ollama Local Models
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Run coding agents using local models via Ollama v0.14.0+:
|
||||||
|
</p>
|
||||||
|
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Install Ollama from{' '}
|
||||||
|
<a href="https://ollama.com" target="_blank" rel="noreferrer" className="text-primary underline">
|
||||||
|
ollama.com
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Start Ollama:{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">ollama serve</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Pull a coding model:{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">ollama pull qwen3-coder</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Configure your{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.env</span>:
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm mt-3">
|
||||||
|
<pre><code>{`ANTHROPIC_BASE_URL=http://localhost:11434
|
||||||
|
ANTHROPIC_AUTH_TOKEN=ollama
|
||||||
|
API_TIMEOUT_MS=3000000
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder`}</code></pre>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
<strong className="text-foreground">Recommended models:</strong>{' '}
|
||||||
|
<Badge variant="secondary">qwen3-coder</Badge>{' '}
|
||||||
|
<Badge variant="secondary">deepseek-coder-v2</Badge>{' '}
|
||||||
|
<Badge variant="secondary">codellama</Badge>
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground mt-2">
|
||||||
|
<strong className="text-foreground">Limitations:</strong> Smaller context windows than Claude
|
||||||
|
(model-dependent), extended context beta disabled (not supported by Ollama), and performance
|
||||||
|
depends on local hardware (GPU recommended).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Environment Variables */}
|
||||||
|
<h3 id="env-variables" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Environment Variables
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Key environment variables for configuring AutoCoder:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Variable
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
{ENV_VARS.map((v) => (
|
||||||
|
<tr key={v.name}>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">{v.name}</span>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">{v.description}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* CLI Arguments */}
|
||||||
|
<h3 id="cli-arguments" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
CLI Arguments
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Command-line arguments for{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
autonomous_agent_demo.py
|
||||||
|
</span>
|
||||||
|
:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Argument
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
{CLI_ARGS.map((arg) => (
|
||||||
|
<tr key={arg.name}>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">{arg.name}</span>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">{arg.description}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* Webhook Support */}
|
||||||
|
<h3 id="webhooks" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Webhook Support
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>AutoCoder can send webhook notifications on feature completion</li>
|
||||||
|
<li>Compatible with N8N and similar automation tools</li>
|
||||||
|
<li>Configure the webhook URL in project settings</li>
|
||||||
|
<li>
|
||||||
|
Payload includes: feature name, status, and project info
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Project Registry */}
|
||||||
|
<h3 id="project-registry" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Project Registry
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
All projects are registered in{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">~/.autocoder/registry.db</span>{' '}
|
||||||
|
(SQLite)
|
||||||
|
</li>
|
||||||
|
<li>Maps project names to filesystem paths</li>
|
||||||
|
<li>Uses POSIX path format (forward slashes) for cross-platform compatibility</li>
|
||||||
|
<li>SQLAlchemy ORM with SQLite's built-in transaction handling</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
280
ui/src/components/docs/sections/AgentSystem.tsx
Normal file
280
ui/src/components/docs/sections/AgentSystem.tsx
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
/**
|
||||||
|
* AgentSystem Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the orchestrator (Maestro), coding agents, testing agents,
|
||||||
|
* agent lifecycle, concurrency control, mission control dashboard,
|
||||||
|
* agent mascots and states, viewing logs, and process limits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function AgentSystem() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Maestro: The Orchestrator */}
|
||||||
|
<h3 id="maestro-orchestrator" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Maestro: The Orchestrator
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Maestro is the central orchestrator that coordinates all agents. It acts as the conductor,
|
||||||
|
ensuring features are implemented efficiently and in the correct order.
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Manages the full lifecycle of coding and testing agents</li>
|
||||||
|
<li>Schedules which features to work on based on dependencies and priority</li>
|
||||||
|
<li>Monitors agent health and restarts crashed agents automatically</li>
|
||||||
|
<li>Reports status to the UI in real time via WebSocket</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Coding Agents */}
|
||||||
|
<h3 id="coding-agents" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Coding Agents
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Implement features one at a time, or in batches of 1–3</li>
|
||||||
|
<li>
|
||||||
|
Claim features atomically via the{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
feature_claim_and_get
|
||||||
|
</span>{' '}
|
||||||
|
MCP tool — no two agents work on the same feature
|
||||||
|
</li>
|
||||||
|
<li>Run in isolated environments with their own browser context</li>
|
||||||
|
<li>
|
||||||
|
Use the Claude Code SDK with project-specific tools and{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">CLAUDE.md</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Testing Agents */}
|
||||||
|
<h3 id="testing-agents" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Testing Agents
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Run regression tests after features are implemented</li>
|
||||||
|
<li>Verify that new code does not break existing features</li>
|
||||||
|
<li>Configurable ratio: 0–3 testing agents per coding agent</li>
|
||||||
|
<li>Can batch-test multiple features per session (1–5)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Agent Lifecycle */}
|
||||||
|
<h3 id="agent-lifecycle" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Agent Lifecycle
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Agents are controlled through the UI or CLI. The lifecycle states are:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Action
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Behavior
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Start</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Click the Play button or run the CLI command
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Stop</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Gracefully terminates all running agents
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Pause</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Temporarily halts work (agents finish their current task first)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Resume</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Continues from where the agents were paused
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
Agents auto-continue between sessions with a 3-second delay, so they keep working until
|
||||||
|
all features are complete or they are explicitly stopped.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Concurrency Control */}
|
||||||
|
<h3 id="concurrency" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Concurrency Control
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
A slider in the agent control bar sets the number of concurrent coding agents
|
||||||
|
(1–5)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
More agents means faster progress, but also higher API usage
|
||||||
|
</li>
|
||||||
|
<li>Each agent runs as an independent subprocess</li>
|
||||||
|
<li>
|
||||||
|
Feature claiming is atomic — no two agents will ever work on the same feature
|
||||||
|
simultaneously
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Agent Mission Control */}
|
||||||
|
<h3 id="mission-control" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Agent Mission Control
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The Mission Control dashboard provides a real-time overview of all active agents:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Active agent cards with mascot icons and current status</li>
|
||||||
|
<li>The feature each agent is currently working on</li>
|
||||||
|
<li>Agent state indicators (thinking, working, testing, etc.)</li>
|
||||||
|
<li>Orchestrator status and a recent activity feed</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Agent Mascots & States */}
|
||||||
|
<h3 id="agent-mascots" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Agent Mascots & States
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Each agent is assigned a unique mascot for easy identification:{' '}
|
||||||
|
<strong className="text-foreground">Spark</strong>,{' '}
|
||||||
|
<strong className="text-foreground">Fizz</strong>,{' '}
|
||||||
|
<strong className="text-foreground">Octo</strong>,{' '}
|
||||||
|
<strong className="text-foreground">Hoot</strong>,{' '}
|
||||||
|
<strong className="text-foreground">Buzz</strong>, and more. Agent states include:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
State
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Animation
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">Thinking</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Bouncing</td>
|
||||||
|
<td className="border border-border px-3 py-2">Agent is planning its approach</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">Working</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Shake</td>
|
||||||
|
<td className="border border-border px-3 py-2">Actively writing code</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">Testing</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Rotating</td>
|
||||||
|
<td className="border border-border px-3 py-2">Running tests</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="default">Success</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Celebration</td>
|
||||||
|
<td className="border border-border px-3 py-2">Feature completed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="destructive">Error</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Red shake</td>
|
||||||
|
<td className="border border-border px-3 py-2">Encountered an issue</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="outline">Struggling</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Concerned expression</td>
|
||||||
|
<td className="border border-border px-3 py-2">Multiple consecutive failures</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* Viewing Agent Logs */}
|
||||||
|
<h3 id="agent-logs" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Viewing Agent Logs
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Click any agent card in Mission Control to see its log output</li>
|
||||||
|
<li>Logs are color-coded by level (info, warning, error)</li>
|
||||||
|
<li>Output streams in real time via WebSocket</li>
|
||||||
|
<li>Each agent's logs are isolated and filterable</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Process Limits */}
|
||||||
|
<h3 id="process-limits" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Process Limits
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The orchestrator enforces strict bounds on concurrent processes to prevent resource
|
||||||
|
exhaustion:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Limit
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Value
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
MAX_PARALLEL_AGENTS
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">5 (maximum concurrent coding agents)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
MAX_TOTAL_AGENTS
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
10 (hard limit on coding + testing combined)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Testing agents</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Capped at the same count as coding agents
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Total Python processes</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Never exceeds 11 (1 orchestrator + 5 coding + 5 testing)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
130
ui/src/components/docs/sections/AppSpecSetup.tsx
Normal file
130
ui/src/components/docs/sections/AppSpecSetup.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* AppSpecSetup Documentation Section
|
||||||
|
*
|
||||||
|
* Explains what an app spec is, how to create one interactively
|
||||||
|
* or manually, the initializer agent, and starting after spec creation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function AppSpecSetup() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* What is an App Spec? */}
|
||||||
|
<h3 id="what-is-app-spec" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What is an App Spec?
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
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">
|
||||||
|
.autocoder/prompts/app_spec.txt
|
||||||
|
</span>{' '}
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>{`<app>
|
||||||
|
<name>My App</name>
|
||||||
|
<description>A task management app</description>
|
||||||
|
<features>
|
||||||
|
<feature>User authentication with login/signup</feature>
|
||||||
|
<feature>Task CRUD with categories</feature>
|
||||||
|
</features>
|
||||||
|
</app>`}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Creating a Spec with Claude */}
|
||||||
|
<h3 id="creating-spec-with-claude" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Creating a Spec with Claude
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
In the UI, select your project and click{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">Create Spec</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
An interactive chat with Claude helps you define your app — it asks about
|
||||||
|
your app's purpose, features, and tech stack
|
||||||
|
</li>
|
||||||
|
<li>The spec is generated and saved automatically</li>
|
||||||
|
<li>After creation, the initializer agent can be started immediately</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Writing a Spec Manually */}
|
||||||
|
<h3 id="writing-spec-manually" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Writing a Spec Manually
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Create{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/prompts/app_spec.txt
|
||||||
|
</span>{' '}
|
||||||
|
in your project directory
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Use XML format with app name, description, tech stack, and a feature list
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Be specific about each feature — the initializer creates test cases from these
|
||||||
|
descriptions
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Include technical constraints where needed (e.g.,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
"use PostgreSQL"
|
||||||
|
</span>
|
||||||
|
,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
"React with TypeScript"
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* The Initializer Agent */}
|
||||||
|
<h3 id="initializer-agent" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
The Initializer Agent
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The initializer agent is the first agent to run on a new project. It bridges the gap between
|
||||||
|
your spec and the coding agents that implement features.
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Runs automatically on first agent start when no features exist in the database</li>
|
||||||
|
<li>Reads the app spec and creates features with descriptions, steps, and priorities</li>
|
||||||
|
<li>
|
||||||
|
Sets up feature dependencies (e.g., "auth must be done before user profile")
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Creates the feature database at{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/features.db
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Starting After Spec Creation */}
|
||||||
|
<h3 id="starting-after-spec" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Starting After Spec Creation
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Once your spec is ready, you can kick off the agents:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
From the UI, click the <strong className="text-foreground">Play</strong> button to start
|
||||||
|
the agent
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Or run from the CLI:
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm mt-3">
|
||||||
|
<pre><code>python autonomous_agent_demo.py --project-dir your-project</code></pre>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
The initializer runs first to create features, then coding agents take over to implement
|
||||||
|
them. Progress is shown in real time on the Kanban board.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
185
ui/src/components/docs/sections/AppearanceThemes.tsx
Normal file
185
ui/src/components/docs/sections/AppearanceThemes.tsx
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* AppearanceThemes Documentation Section
|
||||||
|
*
|
||||||
|
* Covers built-in themes with color previews, dark/light mode toggling,
|
||||||
|
* the theme selector dropdown, and global keyboard shortcuts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
/** Theme descriptor used to render the preview rows. */
|
||||||
|
interface ThemePreview {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
colors: { label: string; hex: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const THEMES: ThemePreview[] = [
|
||||||
|
{
|
||||||
|
name: 'Twitter',
|
||||||
|
description: 'Clean, modern blue design. Primary: blue, Background: white/dark gray.',
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#ffffff' },
|
||||||
|
{ label: 'Primary', hex: '#4a9eff' },
|
||||||
|
{ label: 'Accent', hex: '#e8f4ff' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Claude',
|
||||||
|
description: "Warm beige/cream tones with orange accents. Inspired by Anthropic's Claude brand.",
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#faf6f0' },
|
||||||
|
{ label: 'Primary', hex: '#c75b2a' },
|
||||||
|
{ label: 'Accent', hex: '#f5ede4' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Neo Brutalism',
|
||||||
|
description: 'Bold colors, hard shadows, no border radius. High contrast, expressive design.',
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#ffffff' },
|
||||||
|
{ label: 'Primary', hex: '#ff4d00' },
|
||||||
|
{ label: 'Accent', hex: '#ffeb00' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Retro Arcade',
|
||||||
|
description: 'Vibrant pink and teal with pixel-art inspired styling.',
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#f0e6d3' },
|
||||||
|
{ label: 'Primary', hex: '#e8457c' },
|
||||||
|
{ label: 'Accent', hex: '#4eb8a5' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Aurora',
|
||||||
|
description: 'Deep violet and luminous teal, inspired by the northern lights.',
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#faf8ff' },
|
||||||
|
{ label: 'Primary', hex: '#8b5cf6' },
|
||||||
|
{ label: 'Accent', hex: '#2dd4bf' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Business',
|
||||||
|
description: 'Professional deep navy and gray monochrome palette for corporate use.',
|
||||||
|
colors: [
|
||||||
|
{ label: 'Background', hex: '#eaecef' },
|
||||||
|
{ label: 'Primary', hex: '#000e4e' },
|
||||||
|
{ label: 'Accent', hex: '#6b7280' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
/** Keyboard shortcut descriptor for the shortcuts table. */
|
||||||
|
interface Shortcut {
|
||||||
|
key: string
|
||||||
|
action: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHORTCUTS: Shortcut[] = [
|
||||||
|
{ key: '?', action: 'Show keyboard shortcuts help' },
|
||||||
|
{ key: 'D', action: 'Toggle debug panel' },
|
||||||
|
{ key: 'T', action: 'Toggle terminal' },
|
||||||
|
{ key: 'G', action: 'Toggle Kanban/Graph view' },
|
||||||
|
{ key: 'N', action: 'Add new feature' },
|
||||||
|
{ key: 'E', action: 'Expand project with AI' },
|
||||||
|
{ key: 'A', action: 'Toggle AI assistant' },
|
||||||
|
{ key: ',', action: 'Open settings' },
|
||||||
|
{ key: 'R', action: 'Reset project' },
|
||||||
|
{ key: 'Escape', action: 'Close current modal' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function AppearanceThemes() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Themes Overview */}
|
||||||
|
<h3 id="themes-overview" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Themes Overview
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
AutoCoder comes with 6 built-in themes. Each theme provides a complete visual identity including
|
||||||
|
colors, accents, and dark mode variants.
|
||||||
|
</p>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{THEMES.map((theme) => (
|
||||||
|
<div key={theme.name} className="flex items-start gap-4">
|
||||||
|
{/* Color swatches */}
|
||||||
|
<div className="flex gap-1.5 shrink-0 mt-1">
|
||||||
|
{theme.colors.map((color) => (
|
||||||
|
<div
|
||||||
|
key={color.label}
|
||||||
|
title={`${color.label}: ${color.hex}`}
|
||||||
|
className="w-6 h-6 rounded border border-border"
|
||||||
|
style={{ backgroundColor: color.hex }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* Description */}
|
||||||
|
<div>
|
||||||
|
<strong className="text-foreground">{theme.name}</strong>
|
||||||
|
{theme.name === 'Twitter' && (
|
||||||
|
<>
|
||||||
|
{' '}
|
||||||
|
<Badge variant="secondary">Default</Badge>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span className="text-muted-foreground"> — {theme.description}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dark & Light Mode */}
|
||||||
|
<h3 id="dark-light-mode" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Dark & Light Mode
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Toggle with the sun/moon icon in the header</li>
|
||||||
|
<li>All 6 themes have dedicated dark mode variants</li>
|
||||||
|
<li>
|
||||||
|
Preference is saved in browser{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">localStorage</span>
|
||||||
|
</li>
|
||||||
|
<li>Dark mode affects all UI elements including the docs page</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Theme Selector */}
|
||||||
|
<h3 id="theme-selector" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Theme Selector
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Hover over the palette icon in the header to open the theme dropdown</li>
|
||||||
|
<li>Preview themes by hovering over each option (live preview)</li>
|
||||||
|
<li>Click to select — the change is applied instantly</li>
|
||||||
|
<li>Theme preference persists across sessions</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Keyboard Shortcuts */}
|
||||||
|
<h3 id="keyboard-shortcuts" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Keyboard Shortcuts
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Press <Badge variant="secondary">?</Badge> anywhere in the UI to see the shortcuts help overlay.
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">Key</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
{SHORTCUTS.map((shortcut) => (
|
||||||
|
<tr key={shortcut.key}>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">{shortcut.key}</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">{shortcut.action}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
104
ui/src/components/docs/sections/DeveloperTools.tsx
Normal file
104
ui/src/components/docs/sections/DeveloperTools.tsx
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* DeveloperTools Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the debug panel, agent logs tab, dev server logs,
|
||||||
|
* terminal, dev server control, and per-agent logs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function DeveloperTools() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Debug Panel */}
|
||||||
|
<h3 id="debug-panel" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Debug Panel
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">D</Badge> to toggle the debug panel at the bottom of the screen
|
||||||
|
</li>
|
||||||
|
<li>Resizable by dragging the top edge</li>
|
||||||
|
<li>
|
||||||
|
Three tabs: <strong className="text-foreground">Agent Logs</strong>,{' '}
|
||||||
|
<strong className="text-foreground">Dev Server Logs</strong>, and{' '}
|
||||||
|
<strong className="text-foreground">Terminal</strong>
|
||||||
|
</li>
|
||||||
|
<li>Shows real-time output from agents and dev server</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Agent Logs Tab */}
|
||||||
|
<h3 id="agent-logs-tab" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Agent Logs Tab
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Color-coded log levels:{' '}
|
||||||
|
<span className="text-[var(--color-log-error)] font-medium">Error</span>,{' '}
|
||||||
|
<span className="text-[var(--color-log-warning)] font-medium">Warning</span>,{' '}
|
||||||
|
<span className="text-[var(--color-log-info)] font-medium">Info</span>,{' '}
|
||||||
|
<span className="text-[var(--color-log-debug)] font-medium">Debug</span>,{' '}
|
||||||
|
<span className="text-[var(--color-log-success)] font-medium">Success</span>
|
||||||
|
</li>
|
||||||
|
<li>Timestamps on each log entry</li>
|
||||||
|
<li>Auto-scrolls to latest entry</li>
|
||||||
|
<li>Clear button to reset log view</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Dev Server Logs Tab */}
|
||||||
|
<h3 id="dev-server-logs" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Dev Server Logs Tab
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Shows stdout/stderr from the project’s dev server (e.g.,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">npm run dev</span>)
|
||||||
|
</li>
|
||||||
|
<li>Useful for seeing compilation errors, hot reload status</li>
|
||||||
|
<li>Clear button available</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Terminal */}
|
||||||
|
<h3 id="terminal" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Terminal
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">T</Badge> to open terminal (opens debug panel on the terminal tab)
|
||||||
|
</li>
|
||||||
|
<li>Full xterm.js terminal emulator with WebSocket backend</li>
|
||||||
|
<li>Multi-tab support: create multiple terminal sessions</li>
|
||||||
|
<li>Rename tabs by double-clicking the tab title</li>
|
||||||
|
<li>Each tab runs an independent PTY (pseudo-terminal) session</li>
|
||||||
|
<li>Supports standard terminal features: colors, cursor movement, history</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Dev Server Control */}
|
||||||
|
<h3 id="dev-server-control" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Dev Server Control
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Start/stop button in the header bar</li>
|
||||||
|
<li>
|
||||||
|
Auto-detects project type (Next.js, Vite, CRA, etc.) and runs the appropriate dev command
|
||||||
|
</li>
|
||||||
|
<li>Shows the dev server URL when running</li>
|
||||||
|
<li>Automatic crash detection and restart option</li>
|
||||||
|
<li>Dev server output piped to the Dev Server Logs tab</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Per-Agent Logs */}
|
||||||
|
<h3 id="per-agent-logs" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Per-Agent Logs
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>In Agent Mission Control, click any agent card to see its individual logs</li>
|
||||||
|
<li>
|
||||||
|
Logs include: what feature the agent is working on, code changes, test results
|
||||||
|
</li>
|
||||||
|
<li>Separate logs for coding agents and testing agents</li>
|
||||||
|
<li>Real-time streaming — see agent output as it happens</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
157
ui/src/components/docs/sections/FAQ.tsx
Normal file
157
ui/src/components/docs/sections/FAQ.tsx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* FAQ Documentation Section
|
||||||
|
*
|
||||||
|
* Covers frequently asked questions about project setup, agent behavior,
|
||||||
|
* customization, troubleshooting, and real-time monitoring.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function FAQ() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Starting a New Project */}
|
||||||
|
<h3 id="faq-new-project" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Starting a New Project
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I use AutoCoder on a new project?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
From the UI, select "Create New Project" in the project dropdown. Choose a folder and
|
||||||
|
name. Then create an app spec using the interactive chat or write one manually. Click Start to run
|
||||||
|
the initializer agent, which creates features from your spec. Coding agents then implement features
|
||||||
|
automatically.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Adding to Existing Project */}
|
||||||
|
<h3 id="faq-existing-project" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Adding to Existing Project
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I add AutoCoder to an existing project?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Register the project folder through the UI project selector using "Add Existing".
|
||||||
|
AutoCoder creates a{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span> directory
|
||||||
|
alongside your existing code. Write an app spec describing what to build (new features), and the
|
||||||
|
agent works within your existing codebase.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Agent Crashes */}
|
||||||
|
<h3 id="faq-agent-crash" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Agent Crashes
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
What happens if an agent crashes?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
The orchestrator (Maestro) automatically detects crashed agents and can restart them. Features
|
||||||
|
claimed by a crashed agent are released back to the pending queue. Scheduled runs use exponential
|
||||||
|
backoff with up to 3 retries. Check the agent logs in the debug panel for crash details.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Custom Bash Commands */}
|
||||||
|
<h3 id="faq-custom-commands" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Custom Bash Commands
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I customize which bash commands the agent can use?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Create{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/allowed_commands.yaml
|
||||||
|
</span>{' '}
|
||||||
|
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
|
||||||
|
scripts. See the Security section for full details on the command hierarchy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Blocked Features */}
|
||||||
|
<h3 id="faq-blocked-features" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Blocked Features
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
Why are my features stuck in "blocked" status?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Features with unmet dependencies show as blocked. Check the Dependency Graph view (press{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">G</span>) to see which
|
||||||
|
features are waiting on others. A feature can only start when all its dependencies are marked as
|
||||||
|
"passing". Remove or reorder dependencies if needed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Running in Parallel */}
|
||||||
|
<h3 id="faq-parallel" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Running in Parallel
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I run multiple agents in parallel?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Use the concurrency slider in the agent control bar (1–5 agents) or pass{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
--parallel --max-concurrency N
|
||||||
|
</span>{' '}
|
||||||
|
on the CLI. Each agent claims features atomically, so there is no conflict. More agents means
|
||||||
|
faster progress but higher API cost.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Using Local Models */}
|
||||||
|
<h3 id="faq-local-model" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Using Local Models
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
Can I use a local model instead of the Claude API?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Yes, via Ollama v0.14.0+. Install Ollama, pull a coding model (e.g.,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">qwen3-coder</span>), and
|
||||||
|
configure your{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.env</span> to point to
|
||||||
|
localhost. See the Advanced Configuration section for full setup instructions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Resetting a Project */}
|
||||||
|
<h3 id="faq-reset" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Resetting a Project
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I reset a project and start over?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Press <span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">R</span> (when agents
|
||||||
|
are stopped) to open the Reset modal. Choose between: "Reset Features" (clears the
|
||||||
|
feature database, keeps the spec) or "Full Reset" (removes the spec too, starts fresh).
|
||||||
|
After a full reset, you will be prompted to create a new spec.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Coding vs Testing Agents */}
|
||||||
|
<h3 id="faq-agent-types" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Coding vs Testing Agents
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
What's the difference between coding and testing agents?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Coding agents implement features — they write code, create files, and run feature-specific
|
||||||
|
tests. Testing agents run regression tests across completed features to ensure new code does not
|
||||||
|
break existing functionality. Configure the testing agent ratio (0–3) in settings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Monitoring in Real Time */}
|
||||||
|
<h3 id="faq-real-time" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Monitoring in Real Time
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground italic mb-2">
|
||||||
|
How do I view what an agent is doing in real time?
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Multiple ways: (1) Watch the Kanban board for feature status changes. (2) Open the debug panel
|
||||||
|
(<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">D</span> key) for live
|
||||||
|
agent logs. (3) Click agent cards in Mission Control for per-agent logs. (4) The progress bar
|
||||||
|
updates in real time via WebSocket.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
182
ui/src/components/docs/sections/FeaturesKanban.tsx
Normal file
182
ui/src/components/docs/sections/FeaturesKanban.tsx
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
/**
|
||||||
|
* FeaturesKanban Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the Kanban board, feature cards, dependency graph view,
|
||||||
|
* adding/editing features, dependencies, expanding with AI,
|
||||||
|
* and priority ordering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function FeaturesKanban() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Kanban Board Overview */}
|
||||||
|
<h3 id="kanban-overview" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Kanban Board Overview
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The main view organizes features into three columns representing their current status:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3 mb-4">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Column
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Color
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Meaning
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Pending</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="outline" className="border-yellow-500 text-yellow-600">Yellow</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Waiting to be picked up</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">In Progress</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="outline" className="border-cyan-500 text-cyan-600">Cyan</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">An agent is actively working on it</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2 font-medium">Done</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="outline" className="border-green-500 text-green-600">Green</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Implemented and passing</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Each feature appears as a card showing its name, priority, and category. The board updates
|
||||||
|
in real time as agents work.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Feature Cards */}
|
||||||
|
<h3 id="feature-cards" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Feature Cards
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Each card displays a priority badge (<Badge variant="secondary">P1</Badge> through{' '}
|
||||||
|
<Badge variant="secondary">P5</Badge>), a category tag, and the feature name
|
||||||
|
</li>
|
||||||
|
<li>Status icons indicate the current state of the feature</li>
|
||||||
|
<li>Click a card to open the detail modal with the full description and test steps</li>
|
||||||
|
<li>
|
||||||
|
Cards in the "In Progress" column show which agent is currently working on them
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Dependency Graph View */}
|
||||||
|
<h3 id="dependency-graph" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Dependency Graph View
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
An alternative to the Kanban board that visualizes feature relationships as a directed graph.
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">G</Badge> to toggle between Kanban and Graph view
|
||||||
|
</li>
|
||||||
|
<li>Uses the dagre layout engine for automatic node positioning</li>
|
||||||
|
<li>
|
||||||
|
Nodes are colored by status — pending, in-progress, and done each have
|
||||||
|
distinct colors
|
||||||
|
</li>
|
||||||
|
<li>Arrows show dependency relationships between features</li>
|
||||||
|
<li>Click any node to open the feature detail modal</li>
|
||||||
|
<li>Supports both horizontal and vertical layout orientations</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Adding Features */}
|
||||||
|
<h3 id="adding-features" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Adding Features
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">N</Badge> to open the Add Feature form
|
||||||
|
</li>
|
||||||
|
<li>Fill in: name, description, category, and priority</li>
|
||||||
|
<li>Optionally define steps (test criteria the agent must pass to complete the feature)</li>
|
||||||
|
<li>New features are added to the Pending column immediately</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Editing & Deleting Features */}
|
||||||
|
<h3 id="editing-features" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Editing & Deleting Features
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Click a feature card to open the detail modal</li>
|
||||||
|
<li>
|
||||||
|
Click <strong className="text-foreground">Edit</strong> to modify the name, description,
|
||||||
|
category, priority, or steps
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Delete</strong> removes the feature permanently
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Skip</strong> moves a feature to the end of the queue
|
||||||
|
without deleting it
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Feature Dependencies */}
|
||||||
|
<h3 id="feature-dependencies" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Feature Dependencies
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Features can declare dependencies on other features, ensuring they are implemented in the
|
||||||
|
correct order.
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Set dependencies in the feature edit modal</li>
|
||||||
|
<li>
|
||||||
|
Cycle detection prevents circular dependencies (uses Kahn's algorithm combined
|
||||||
|
with DFS)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Blocked features display a lock icon and cannot be claimed by agents until their
|
||||||
|
dependencies are met
|
||||||
|
</li>
|
||||||
|
<li>The Dependency Graph view makes these relationships easy to visualize</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Expanding Project with AI */}
|
||||||
|
<h3 id="expanding-with-ai" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Expanding Project with AI
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Press <Badge variant="secondary">E</Badge> to open the Expand Project modal
|
||||||
|
</li>
|
||||||
|
<li>Chat with Claude to describe the new features you want to add</li>
|
||||||
|
<li>Supports image attachments for UI mockups or design references</li>
|
||||||
|
<li>Claude creates properly structured features with appropriate dependencies</li>
|
||||||
|
<li>New features appear on the board immediately after creation</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Priority & Ordering */}
|
||||||
|
<h3 id="feature-priority" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Priority & Ordering
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
Features are ordered by priority: <Badge variant="secondary">P1</Badge> is the highest
|
||||||
|
and <Badge variant="secondary">P5</Badge> is the lowest
|
||||||
|
</li>
|
||||||
|
<li>Within the same priority level, features are ordered by creation time</li>
|
||||||
|
<li>Agents always pick up the highest-priority ready feature first</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
134
ui/src/components/docs/sections/GettingStarted.tsx
Normal file
134
ui/src/components/docs/sections/GettingStarted.tsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* GettingStarted Documentation Section
|
||||||
|
*
|
||||||
|
* Covers what AutoCoder is, quick start commands,
|
||||||
|
* creating and adding projects, and system requirements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function GettingStarted() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* What is AutoCoder? */}
|
||||||
|
<h3 id="what-is-autocoder" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What is AutoCoder?
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
AutoCoder is an autonomous coding agent system that builds complete applications over multiple
|
||||||
|
sessions using a two-agent pattern:
|
||||||
|
</p>
|
||||||
|
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Initializer Agent</strong> — reads your app spec
|
||||||
|
and creates features in a SQLite database
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Coding Agent</strong> — implements features one by
|
||||||
|
one, marking each as passing when complete
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p className="text-muted-foreground mt-4">
|
||||||
|
It comes with a React-based UI for monitoring progress, managing features, and controlling agents
|
||||||
|
in real time.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Quick Start */}
|
||||||
|
<h3 id="quick-start" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Quick Start
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Launch AutoCoder with a single command. The CLI menu lets you create or select a project,
|
||||||
|
while the Web UI provides a full dashboard experience.
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>{`# Windows
|
||||||
|
start.bat # CLI menu
|
||||||
|
start_ui.bat # Web UI
|
||||||
|
|
||||||
|
# macOS/Linux
|
||||||
|
./start.sh # CLI menu
|
||||||
|
./start_ui.sh # Web UI`}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Creating a New Project */}
|
||||||
|
<h3 id="creating-a-project" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Creating a New Project
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
From the UI, click the project dropdown and select{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">Create New Project</span>
|
||||||
|
</li>
|
||||||
|
<li>Enter a name and select or browse to a folder for the project</li>
|
||||||
|
<li>
|
||||||
|
Create an app spec interactively with Claude, or write one manually in XML format
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The initializer agent reads your spec and creates features automatically
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Adding to an Existing Project */}
|
||||||
|
<h3 id="existing-project" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Adding to an Existing Project
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Register the project folder via the UI project selector</li>
|
||||||
|
<li>
|
||||||
|
AutoCoder creates a{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.autocoder/</span>{' '}
|
||||||
|
directory inside your project
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Existing code is preserved — AutoCoder adds its configuration alongside it
|
||||||
|
</li>
|
||||||
|
<li>Write or generate an app spec describing what to build</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* System Requirements */}
|
||||||
|
<h3 id="system-requirements" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
System Requirements
|
||||||
|
</h3>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Requirement
|
||||||
|
</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Details
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Python</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">3.11+</Badge>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Node.js</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">20+</Badge>{' '}
|
||||||
|
<span className="text-xs">(for UI development)</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Claude Code CLI</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Required for running agents
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Operating System</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
Windows, macOS, or Linux
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
162
ui/src/components/docs/sections/ProjectStructure.tsx
Normal file
162
ui/src/components/docs/sections/ProjectStructure.tsx
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* ProjectStructure Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the .autocoder/ directory layout, features database,
|
||||||
|
* prompts directory, allowed commands, CLAUDE.md convention,
|
||||||
|
* legacy migration, and Claude inheritance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function ProjectStructure() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* .autocoder/ Directory Layout */}
|
||||||
|
<h3 id="autocoder-directory" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
.autocoder/ Directory Layout
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Every AutoCoder 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>{' '}
|
||||||
|
directory at the project root.
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>{`your-project/
|
||||||
|
\u251C\u2500\u2500 .autocoder/
|
||||||
|
\u2502 \u251C\u2500\u2500 features.db # SQLite feature database
|
||||||
|
\u2502 \u251C\u2500\u2500 .agent.lock # Lock file (prevents multiple instances)
|
||||||
|
\u2502 \u251C\u2500\u2500 .gitignore # Ignores runtime files
|
||||||
|
\u2502 \u251C\u2500\u2500 allowed_commands.yaml # Per-project bash command allowlist
|
||||||
|
\u2502 \u2514\u2500\u2500 prompts/
|
||||||
|
\u2502 \u251C\u2500\u2500 app_spec.txt # Application specification (XML)
|
||||||
|
\u2502 \u251C\u2500\u2500 initializer_prompt.md # First session prompt
|
||||||
|
\u2502 \u2514\u2500\u2500 coding_prompt.md # Continuation session prompt
|
||||||
|
\u251C\u2500\u2500 CLAUDE.md # Claude Code convention file
|
||||||
|
\u2514\u2500\u2500 app_spec.txt # Root copy for template compatibility`}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Features Database */}
|
||||||
|
<h3 id="features-db" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Features Database
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
SQLite database managed by SQLAlchemy, stored at{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/features.db
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Each feature record includes: id, priority, category, name, description, steps, status
|
||||||
|
(<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">pending</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">in_progress</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">passing</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">failing</span>),
|
||||||
|
and dependencies
|
||||||
|
</li>
|
||||||
|
<li>Agents interact with features through MCP server tools, not direct database access</li>
|
||||||
|
<li>Viewable in the UI via the Kanban board or the Dependency Graph view</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Prompts Directory */}
|
||||||
|
<h3 id="prompts-directory" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Prompts Directory
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Prompts control how agents behave during each session:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">app_spec.txt</span>{' '}
|
||||||
|
— your application specification in XML format
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
initializer_prompt.md
|
||||||
|
</span>{' '}
|
||||||
|
— prompt for the initializer agent (creates features from the spec)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
coding_prompt.md
|
||||||
|
</span>{' '}
|
||||||
|
— prompt for coding agents (implements features)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
These can be customized per project. If not present, defaults from{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.claude/templates/
|
||||||
|
</span>{' '}
|
||||||
|
are used as a fallback.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Allowed Commands Config */}
|
||||||
|
<h3 id="allowed-commands-yaml" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Allowed Commands Config
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The optional{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/allowed_commands.yaml
|
||||||
|
</span>{' '}
|
||||||
|
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
|
||||||
|
custom build scripts).
|
||||||
|
</p>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
See the <strong className="text-foreground">Security</strong> section for full details on
|
||||||
|
the command hierarchy and how project-level commands interact with global and organization
|
||||||
|
policies.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* CLAUDE.md Convention */}
|
||||||
|
<h3 id="claude-md" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
CLAUDE.md Convention
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">CLAUDE.md</span>{' '}
|
||||||
|
lives at the project root, as required by the Claude Code SDK
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Contains project-specific instructions that the agent follows during every coding session
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Automatically inherited by all agents working on the project — no additional
|
||||||
|
configuration needed
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Legacy Layout Migration */}
|
||||||
|
<h3 id="legacy-migration" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Legacy Layout Migration
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Older projects stored configuration files directly at the project root (e.g.,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">features.db</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">prompts/</span>).
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
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>
|
||||||
|
</li>
|
||||||
|
<li>Dual-path resolution ensures both old and new layouts work transparently</li>
|
||||||
|
<li>No manual migration is needed — it happens seamlessly</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Claude Inheritance */}
|
||||||
|
<h3 id="claude-inheritance" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Claude Inheritance
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Agents inherit all MCP servers, tools, skills, custom commands, and{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">CLAUDE.md</span>{' '}
|
||||||
|
from the target project folder.
|
||||||
|
</p>
|
||||||
|
<div className="border-l-4 border-primary pl-4 italic text-muted-foreground">
|
||||||
|
If your project has its own MCP servers or Claude commands, the coding agent can use them.
|
||||||
|
The agent essentially runs as if Claude Code was opened in your project directory.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
102
ui/src/components/docs/sections/Scheduling.tsx
Normal file
102
ui/src/components/docs/sections/Scheduling.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Scheduling Documentation Section
|
||||||
|
*
|
||||||
|
* Covers schedule creation, per-schedule settings,
|
||||||
|
* overrides, and crash recovery with exponential backoff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function Scheduling() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* What Scheduling Does */}
|
||||||
|
<h3 id="what-scheduling-does" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
What Scheduling Does
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
Scheduling automates agent runs at specific times. Set up a schedule and AutoCoder will automatically
|
||||||
|
start agents on your project — useful for overnight builds, periodic maintenance, or continuous
|
||||||
|
development.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Creating a Schedule */}
|
||||||
|
<h3 id="creating-schedule" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Creating a Schedule
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Click the clock icon in the header to open the Schedule modal</li>
|
||||||
|
<li>Set: start time, duration (how long agents run), days of the week</li>
|
||||||
|
<li>Optionally configure: YOLO mode, concurrency, model selection</li>
|
||||||
|
<li>Schedule is saved and starts at the next matching time</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Schedule Settings */}
|
||||||
|
<h3 id="schedule-settings" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Schedule Settings
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Each schedule can override global settings:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">Setting</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">Details</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">YOLO mode</td>
|
||||||
|
<td className="border border-border px-3 py-2">On/off per schedule</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Concurrency</td>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">1–5</Badge> agents
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Model tier</td>
|
||||||
|
<td className="border border-border px-3 py-2">Opus / Sonnet / Haiku</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">Duration</td>
|
||||||
|
<td className="border border-border px-3 py-2">How long the session runs before auto-stopping</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="border-l-4 border-primary pl-4 italic text-muted-foreground mt-4">
|
||||||
|
All schedule times are in UTC timezone.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Schedule Overrides */}
|
||||||
|
<h3 id="schedule-overrides" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Schedule Overrides
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Manually skip a scheduled run (one-time override)</li>
|
||||||
|
<li>Pause a schedule temporarily (resumes on next period)</li>
|
||||||
|
<li>
|
||||||
|
View upcoming runs with{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">Running until</span> /{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">Next run</span> indicators
|
||||||
|
</li>
|
||||||
|
<li>Override without deleting the schedule</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Crash Recovery */}
|
||||||
|
<h3 id="crash-recovery" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Crash Recovery
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>If a scheduled agent crashes, it uses exponential backoff for retries</li>
|
||||||
|
<li>
|
||||||
|
Maximum <Badge variant="secondary">3</Badge> retry attempts per scheduled run
|
||||||
|
</li>
|
||||||
|
<li>Backoff prevents rapid restart loops</li>
|
||||||
|
<li>Failed runs are logged for troubleshooting</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
218
ui/src/components/docs/sections/Security.tsx
Normal file
218
ui/src/components/docs/sections/Security.tsx
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
* Security Documentation Section
|
||||||
|
*
|
||||||
|
* Covers the defense-in-depth security model: command validation layers,
|
||||||
|
* the hierarchical allowlist/blocklist system, per-project and org-level
|
||||||
|
* configuration, extra read paths, and filesystem sandboxing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function Security() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Command Validation Overview */}
|
||||||
|
<h3 id="command-validation" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Command Validation Overview
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
AutoCoder uses a defense-in-depth approach for security. All three layers must pass before any
|
||||||
|
command is executed:
|
||||||
|
</p>
|
||||||
|
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">OS-level sandbox</strong> — bash commands run inside
|
||||||
|
a restricted sandbox environment
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Filesystem restriction</strong> — agents can only
|
||||||
|
access the project directory (plus configured extra read paths)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Hierarchical allowlist</strong> — every bash command
|
||||||
|
is validated against a multi-level allowlist system
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
{/* Command Hierarchy */}
|
||||||
|
<h3 id="command-hierarchy" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Command Hierarchy
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Commands are evaluated against a 5-level hierarchy, from highest to lowest priority:
|
||||||
|
</p>
|
||||||
|
<ol className="list-decimal space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Hardcoded Blocklist</strong>{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">security.py</span>{' '}
|
||||||
|
— NEVER allowed, cannot be overridden
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<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>{' '}
|
||||||
|
— org-wide blocks, cannot be project-overridden
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<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>{' '}
|
||||||
|
— available to all projects
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Global Allowlist</strong>{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">security.py</span>{' '}
|
||||||
|
— default commands (npm, git, curl, etc.)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Project Allowlist</strong>{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/allowed_commands.yaml
|
||||||
|
</span>{' '}
|
||||||
|
— project-specific additions
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<blockquote className="border-l-4 border-primary pl-4 italic text-muted-foreground mt-4">
|
||||||
|
Higher priority levels always win. A command blocked at level 1 or 2 can never be allowed by
|
||||||
|
lower levels.
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
{/* Hardcoded Blocklist */}
|
||||||
|
<h3 id="hardcoded-blocklist" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Hardcoded Blocklist
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
The following commands can <strong className="text-foreground">never</strong> be allowed, regardless
|
||||||
|
of any configuration. They are hardcoded in{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">security.py</span> and
|
||||||
|
cannot be overridden:
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{['dd', 'sudo', 'su', 'shutdown', 'reboot', 'poweroff', 'mkfs', 'fdisk', 'mount', 'umount', 'systemctl'].map(
|
||||||
|
(cmd) => (
|
||||||
|
<Badge key={cmd} variant="destructive">
|
||||||
|
{cmd}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Global Allowlist */}
|
||||||
|
<h3 id="global-allowlist" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Global Allowlist
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Default commands available to all projects out of the box. These are the standard development
|
||||||
|
commands needed for most projects:
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{['npm', 'npx', 'node', 'git', 'curl', 'python', 'pip', 'cat', 'ls', 'mkdir', 'cp', 'mv', 'rm', 'grep', 'find'].map(
|
||||||
|
(cmd) => (
|
||||||
|
<Badge key={cmd} variant="secondary">
|
||||||
|
{cmd}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Per-Project Allowed Commands */}
|
||||||
|
<h3 id="project-allowlist" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Per-Project Allowed Commands
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Each project can define additional allowed commands in{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
.autocoder/allowed_commands.yaml
|
||||||
|
</span>
|
||||||
|
:
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>{`# .autocoder/allowed_commands.yaml
|
||||||
|
version: 1
|
||||||
|
commands:
|
||||||
|
# Exact command name
|
||||||
|
- name: swift
|
||||||
|
description: Swift compiler
|
||||||
|
|
||||||
|
# Wildcard - matches swiftc, swiftlint, swiftformat
|
||||||
|
- name: swift*
|
||||||
|
description: All Swift tools (wildcard)
|
||||||
|
|
||||||
|
# Local project scripts
|
||||||
|
- name: ./scripts/build.sh
|
||||||
|
description: Project build script`}</code></pre>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
<strong className="text-foreground">Pattern matching:</strong> exact match (
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">swift</span>), wildcard (
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">swift*</span> matches swiftc,
|
||||||
|
swiftlint, etc.), and scripts (
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">./scripts/build.sh</span>).
|
||||||
|
Limit: 100 commands per project.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Organization Configuration */}
|
||||||
|
<h3 id="org-config" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Organization Configuration
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
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>:
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>{`# ~/.autocoder/config.yaml
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
# Commands available to ALL projects
|
||||||
|
allowed_commands:
|
||||||
|
- name: jq
|
||||||
|
description: JSON processor
|
||||||
|
|
||||||
|
# Commands blocked across ALL projects (cannot be overridden)
|
||||||
|
blocked_commands:
|
||||||
|
- aws # Prevent accidental cloud operations
|
||||||
|
- kubectl # Block production deployments`}</code></pre>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground mt-3">
|
||||||
|
Org-level blocked commands cannot be overridden by any project configuration.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Extra Read Paths */}
|
||||||
|
<h3 id="extra-read-paths" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Extra Read Paths
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Allow agents to read files from directories outside the project folder via the{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">EXTRA_READ_PATHS</span>{' '}
|
||||||
|
environment variable:
|
||||||
|
</p>
|
||||||
|
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||||
|
<pre><code>EXTRA_READ_PATHS=/path/to/docs,/path/to/shared-libs</code></pre>
|
||||||
|
</div>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground mt-3">
|
||||||
|
<li>Must be absolute paths and must exist as directories</li>
|
||||||
|
<li>Only read operations allowed (Read, Glob, Grep — no Write/Edit)</li>
|
||||||
|
<li>
|
||||||
|
Sensitive directories are always blocked:{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.ssh</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.aws</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.gnupg</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.docker</span>,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">.kube</span>, etc.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Filesystem Sandboxing */}
|
||||||
|
<h3 id="filesystem-sandboxing" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Filesystem Sandboxing
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Agents can only write to the project directory</li>
|
||||||
|
<li>Read access is limited to the project directory plus configured extra read paths</li>
|
||||||
|
<li>
|
||||||
|
Path traversal attacks are prevented via canonicalization (
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">Path.resolve()</span>)
|
||||||
|
</li>
|
||||||
|
<li>File operations are validated before execution</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
188
ui/src/components/docs/sections/SettingsConfig.tsx
Normal file
188
ui/src/components/docs/sections/SettingsConfig.tsx
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/**
|
||||||
|
* SettingsConfig Documentation Section
|
||||||
|
*
|
||||||
|
* Covers global settings: opening the modal, YOLO mode, headless browser,
|
||||||
|
* model selection, regression agents, batch size, concurrency, and persistence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function SettingsConfig() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Opening Settings */}
|
||||||
|
<h3 id="opening-settings" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Opening Settings
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-4">
|
||||||
|
Press the <Badge variant="secondary">,</Badge> (comma) key or click the gear icon in the header bar to
|
||||||
|
open the Settings modal. Settings are global and apply to all projects.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* YOLO Mode */}
|
||||||
|
<h3 id="yolo-mode" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
YOLO Mode
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
YOLO mode is for rapid prototyping — it skips testing for faster iteration:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">What’s skipped:</strong> Regression testing, Playwright MCP
|
||||||
|
server (browser automation disabled)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">What still runs:</strong> Lint and type-check (to verify code
|
||||||
|
compiles), Feature MCP server for tracking
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Toggle via the lightning bolt button in the UI or the{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">--yolo</span> CLI flag
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">When to use:</strong> Early prototyping when you want to scaffold
|
||||||
|
features quickly without verification overhead
|
||||||
|
</li>
|
||||||
|
<li>Switch back to standard mode for production-quality development</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Headless Browser */}
|
||||||
|
<h3 id="headless-browser" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Headless Browser
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>When enabled, Playwright runs without a visible browser window</li>
|
||||||
|
<li>Saves CPU/GPU resources on machines running multiple agents</li>
|
||||||
|
<li>Tests still run fully — just no visible browser UI</li>
|
||||||
|
<li>Toggle in settings or via the UI button</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Model Selection */}
|
||||||
|
<h3 id="model-selection" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Model Selection
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Choose which Claude model tier to use for your agents:
|
||||||
|
</p>
|
||||||
|
<table className="w-full text-sm mt-3">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-muted/50">
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">Tier</th>
|
||||||
|
<th className="border border-border px-3 py-2 text-left font-medium text-foreground">
|
||||||
|
Characteristics
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-muted-foreground">
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="default">Opus</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Most capable, highest quality</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="secondary">Sonnet</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Balanced speed and quality</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="border border-border px-3 py-2">
|
||||||
|
<Badge variant="outline">Haiku</Badge>
|
||||||
|
</td>
|
||||||
|
<td className="border border-border px-3 py-2">Fastest, most economical</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground mt-4">
|
||||||
|
<li>Model can be set globally in settings</li>
|
||||||
|
<li>Per-schedule model override is also available</li>
|
||||||
|
<li>
|
||||||
|
When using Vertex AI, model names use{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">@</span> instead of{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">-</span> (e.g.,{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
|
||||||
|
claude-opus-4-5@20251101
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Regression Agents */}
|
||||||
|
<h3 id="regression-agents" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Regression Agents
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Controls how many testing agents run alongside coding agents (0–3):
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">0:</strong> No regression testing (like YOLO but coding agents
|
||||||
|
still test their own feature)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">1:</strong> One testing agent runs in background verifying
|
||||||
|
completed features
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">2–3:</strong> Multiple testing agents for thorough
|
||||||
|
verification
|
||||||
|
</li>
|
||||||
|
<li>Testing agents batch-test 1–5 features per session</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Features per Agent / Batch Size */}
|
||||||
|
<h3 id="features-per-agent" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Features per Agent (Batch Size)
|
||||||
|
</h3>
|
||||||
|
<p className="text-muted-foreground mb-3">
|
||||||
|
Controls how many features each coding agent implements per session (1–3):
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">1:</strong> One feature per session (most focused, lower risk of
|
||||||
|
conflicts)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">2–3:</strong> Multiple features per session (more efficient,
|
||||||
|
fewer session startups)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Set via settings UI or the{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">--batch-size</span> CLI flag
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Can also target specific features:{' '}
|
||||||
|
<span className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">--batch-features 1,2,3</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Concurrency */}
|
||||||
|
<h3 id="concurrency-setting" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
Concurrency
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>Per-project default concurrency saved in project settings</li>
|
||||||
|
<li>Override at runtime with the concurrency slider in agent controls</li>
|
||||||
|
<li>
|
||||||
|
Range: <Badge variant="secondary">1–5</Badge> concurrent coding agents
|
||||||
|
</li>
|
||||||
|
<li>Higher concurrency = faster progress but more API cost</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* How Settings are Persisted */}
|
||||||
|
<h3 id="settings-persistence" className="text-lg font-semibold text-foreground mt-8 mb-3">
|
||||||
|
How Settings are Persisted
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc space-y-2 ml-4 text-muted-foreground">
|
||||||
|
<li>
|
||||||
|
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>
|
||||||
|
</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>Settings survive app restarts and are shared across UI sessions</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
ui/src/hooks/useHashRoute.ts
Normal file
36
ui/src/hooks/useHashRoute.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
|
||||||
|
export type Route = 'app' | 'docs'
|
||||||
|
|
||||||
|
interface HashRouteState {
|
||||||
|
route: Route
|
||||||
|
section: string | null
|
||||||
|
navigate: (hash: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHash(hash: string): { route: Route; section: string | null } {
|
||||||
|
const cleaned = hash.replace(/^#\/?/, '')
|
||||||
|
if (cleaned === 'docs' || cleaned.startsWith('docs/')) {
|
||||||
|
const section = cleaned.slice(5) || null // Remove 'docs/' prefix
|
||||||
|
return { route: 'docs', section }
|
||||||
|
}
|
||||||
|
return { route: 'app', section: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useHashRoute(): HashRouteState {
|
||||||
|
const [state, setState] = useState(() => parseHash(window.location.hash))
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleHashChange = () => {
|
||||||
|
setState(parseHash(window.location.hash))
|
||||||
|
}
|
||||||
|
window.addEventListener('hashchange', handleHashChange)
|
||||||
|
return () => window.removeEventListener('hashchange', handleHashChange)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const navigate = useCallback((hash: string) => {
|
||||||
|
window.location.hash = hash
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { ...state, navigate }
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
import { useHashRoute } from './hooks/useHashRoute'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
import { DocsPage } from './components/docs/DocsPage'
|
||||||
import './styles/globals.css'
|
import './styles/globals.css'
|
||||||
// Note: Custom theme removed - using shadcn/ui theming instead
|
// Note: Custom theme removed - using shadcn/ui theming instead
|
||||||
|
|
||||||
@@ -14,10 +16,16 @@ const queryClient = new QueryClient({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function Router() {
|
||||||
|
const { route } = useHashRoute()
|
||||||
|
if (route === 'docs') return <DocsPage />
|
||||||
|
return <App />
|
||||||
|
}
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<App />
|
<Router />
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1134,6 +1134,143 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Documentation Prose Typography
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
.docs-prose {
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--foreground);
|
||||||
|
margin-top: 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
|
scroll-margin-top: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose h2:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose h3 {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--foreground);
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
scroll-margin-top: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
max-width: 65ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose ul,
|
||||||
|
.docs-prose ol {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose li {
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose li > ul,
|
||||||
|
.docs-prose li > ol {
|
||||||
|
margin-top: 0.375rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose pre {
|
||||||
|
background: var(--muted);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose code:not(pre code) {
|
||||||
|
background: var(--muted);
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose th {
|
||||||
|
background: var(--muted);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--foreground);
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose td {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose tr:nth-child(even) td {
|
||||||
|
background: var(--muted);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose blockquote {
|
||||||
|
border-left: 4px solid var(--primary);
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose a {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose strong {
|
||||||
|
color: var(--foreground);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-prose hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Scrollbar Styling
|
Scrollbar Styling
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
|
|||||||
Reference in New Issue
Block a user