# frago Development Guidelines

> **Naming Convention**: `frago` is the only correct casing. NEVER USE `Frago`.
>
> **Emphasis Style**: Use ALL CAPS for emphasis (e.g., `NEVER`, `MUST`, `PROHIBITED`).

## Document Maintenance Principles

```python
# Language Policy
if task in [coding, docs, comments]:
    lang = "English"
else:  # conversation, explanation, Q&A
    lang = "Chinese"

if lang == "Chinese" and file_type == "doc":
    filename = f"{name}-zhCN.md"  # e.g., README-zhCN.md

# This File Scope
CLAUDE_MD.includes = [
    "project_directory_structure",
    "resource_update_workflow",
    "prohibited_operations",
    "technical_preferences",
]

CLAUDE_MD.excludes = [
    "frago_product_usage_instructions",
    "command_reference",
    "api_documentation",
    "agent_behavioral_specs",  # see ~/.claude/commands/frago/
    "version_numbers",         # see pyproject.toml
    "dependency_lists",        # see pyproject.toml
]
```

## Directory Structure

```python
PROJECT_STRUCTURE = {
    "src/frago/":           "Core code",
    "src/frago/resources/": "PyPI package resources (generated by dev pack)",
}
```

## Target Audience

```python
class TargetUser:
    types = ["developers", "automation_seekers"]

class Developer(TargetUser):
    """Enjoys technical research, API docs, experimental development"""

class AutomationSeeker(TargetUser):
    """Limited technical experience, needs guidance on solutions"""

# Documentation Principles
def write_docs(content):
    assert is_comprehensive(content), "Provide sufficient help"
    assert is_accessible(content), "Easy to understand"
    assert provides_best_choice(content), "Not 'Option 1 vs Option 2'"
    assert user_friendly(content), "Based on 'can users easily understand this'"
```

## Development Environment

```python
# --- CLI Testing ---
# PROHIBITED: using system-installed `frago` during development
# System package may not reflect local code changes

# WRONG
subprocess.run(["frago", "chrome", "navigate", url])

# CORRECT: always use uv run for local development
subprocess.run(["uv", "run", "frago", "chrome", "navigate", url])

# In terminal:
# WRONG:   frago chrome start
# CORRECT: uv run frago chrome start
```

## Cross-Platform Development

```python
SUPPORTED_PLATFORMS = ["Linux", "macOS", "Windows"]
MOST_QUIRKY = "Windows"

# --- Command Line Arguments ---
# Windows truncates args at newlines

# WRONG
subprocess.run(["claude", "-p", multi_line_prompt])

# CORRECT: use stdin
process = subprocess.Popen(["claude", "-p", "-"], stdin=subprocess.PIPE)
process.stdin.write(prompt.encode())
process.stdin.close()

# CORRECT: use temp file
prompt_file.write_text(prompt)
subprocess.run(["frago", "agent", "--prompt-file", str(prompt_file)])

# --- Path Encoding ---
# Claude Code encodes project paths as directory names
# Windows ':' → '-', producing double hyphen
PATH_ENCODING = {
    "/home/user/project": "-home-user-project",
    "C:\\Users\\yammi":   "C--Users-yammi",
}
# See: frago/session/sync.py → encode_project_path, decode_project_path

# --- subprocess Calls ---
from frago.compat import prepare_command_for_windows

# Automatically resolves full path to claude.CMD
subprocess.run(prepare_command_for_windows(["claude", "-p", "test"]))

# --- Encoding Issues ---
# Windows console defaults to non-UTF-8
subprocess.run(cmd, text=True, encoding='utf-8')

# --- Null Device ---
# PROHIBITED: hardcoding '/dev/null' (creates actual file on Windows)

# CORRECT:
NULL_DEVICE = 'nul' if platform.system() == 'Windows' else '/dev/null'

# BETTER:
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
```

## Resources Location

```python
USER_RESOURCES = {
    "~/.claude/": {
        "commands/": {
            "frago.*.md":  "frago commands",
            "frago/":      "Command rules and scripts",
        },
        "skills/": {
            "frago-*/":    "frago skills",
        },
    },
    "~/.frago/": {
        "config.json":     "Global configuration",
        "current_run":     "Current run context",
        "projects/":       "Run instance directory",
        "recipes/": {
            "atomic/":     "Atomic recipes",
            "workflows/":  "Workflow recipes",
        },
        "sessions/":       "Session storage",
        "chrome_profile/": "Chrome configuration",
    },
}
```

## Resource Update Workflow

```python
def dev_pack():
    sources = [
        "~/.claude/commands/frago.*.md",
        "~/.claude/skills/frago-*",
        "~/.frago/recipes/",
    ]
    target = "src/frago/resources/"

    for src in sources:
        sync(src, target)

# Usage: frago dev pack
```

## Prohibited Actions

```python
def modify_resources():
    if path.startswith("src/frago/resources/"):
        raise ProhibitedError("Auto-generated by dev pack")

    # CORRECT: Edit source, then sync
    edit("~/.claude/*")
    edit("~/.frago/*")
    run("frago dev pack")

# --- Recipe Execution ---
# PROHIBITED: running recipe scripts directly with python/uv run python
# Recipe scripts depend on frago's environment setup (env vars, working dir, etc.)

# WRONG
subprocess.run(["python", "~/.frago/recipes/atomic/.../recipe.py", params])
subprocess.run(["uv", "run", "python", "recipe.py", params])

# CORRECT: always use frago recipe run
subprocess.run(["uv", "run", "frago", "recipe", "run", recipe_name, "--params", params])

# In terminal:
# WRONG:   uv run python ~/.frago/recipes/.../recipe.py '{...}'
# CORRECT: uv run frago recipe run recipe_name --params '{...}'
```

## Frontend Development

```tsx
// --- Form Element Accessibility ---
// When creating <input>, <select>, <textarea>, then MUST provide accessible label

// WRONG: missing label association
<select value={value} onChange={...}>

// CORRECT: use id + htmlFor when visible label exists
<label htmlFor="endpoint-type">Endpoint Type</label>
<select id="endpoint-type" value={value} onChange={...}>

// CORRECT: use aria-label when no visible label (search box, sr-only toggle)
<input type="text" aria-label="Search recipes" placeholder="Search..." />
<input type="checkbox" aria-label="Enable dark mode" className="sr-only peer" />

// --- Button Type Attribute ---
// When creating <button>, then MUST set type attribute

// WRONG
<button onClick={...}>Submit</button>

// CORRECT
<button type="button" onClick={...}>Cancel</button>
<button type="submit">Submit</button>
```

## User Interaction Conventions

```python
# "Open" means visual preview, not Read tool
if user_says("打开", "open"):
    run("uv run frago view <file>")  # Browser preview
    # NOT: Read tool to dump content
```

## Documentation Index

```python
DOCS = {
    "docs/concepts.md":             "Core concepts and architecture design",
    "pyproject.toml":               "Dependency versions, entry point configuration",
    "specs/*/spec.md":              "Specifications for each feature",
    "~/.claude/commands/frago/":    "Behavioral specifications for Agents using frago",
}
# Read as needed
```

## Active Technologies
- Python 3.13+ (existing project requirement) + FastAPI, Uvicorn (new), existing React/TypeScript frontend (013-change-gui-into-web-service)
- N/A (uses existing session storage in `~/.frago/sessions/`) (013-change-gui-into-web-service)
- Python 3.13+ (backend), TypeScript/React 18 (frontend) + FastAPI, Uvicorn, Click (backend); React, Zustand, Tailwind CSS, Vite (frontend) (014-server-background-redesign)
- File-based (existing `~/.frago/` directory structure) (014-server-background-redesign)

## Recent Changes
- 013-change-gui-into-web-service: Added Python 3.13+ (existing project requirement) + FastAPI, Uvicorn (new), existing React/TypeScript frontend
