# FWAuto High Level Design

## 1. 系統概述

### 定位

FWAuto 是一個基於 LangGraph 的多平台韌體開發自動化系統，透過 AI 輔助實現從程式碼生成、編譯、部署到日誌分析的完整閉環。

### 解決的核心問題

- **韌體開發流程繁瑣**：編譯 → 部署 → 測試 → 修復 → 重複的循環耗時
- **編譯錯誤修復需要人工介入**：每次編譯失敗都需要手動分析並修正
- **工具鏈整合複雜**：各種 SDK、部署工具的整合困難
- **缺少統一的自動化流程**：各個工具分散，難以統一管理
- **跨平台開發一致性**：不同平台（SoC、MCU）使用相同的開發流程

### 關鍵特性

- **AI 驅動的錯誤修復**：自動分析編譯錯誤並嘗試修復（最多 3 次重試）
- **錨點模式專案管理**：類似 Git 的 `.fwauto/` 目錄自動搜尋，無需記憶專案編號
- **Maketool 抽象層**：隱藏不同工具鏈和平台的差異，統一介面
- **LangGraph 工作流程**：State-based 流程管理，可追蹤、可恢復、可視覺化

## 2. 架構原則

### KISS 原則

- **配置即檔案**：`.fwauto/config.toml` 可以完全空白，使用預設值即可
- **約定優於配置**：統一的 Maketool 介面（`build` 和 `deploy` target），不需複雜配置
- **單一職責**：每個節點只做一件事，例如 `build_node` 只負責編譯

### 抽象層架構價值

**Build 和 Deploy 採用不同但一致的抽象層模式**：

- **Build 節點**：使用 Maketool 抽象層，隱藏 GCC/Clang/ARMCC 等工具鏈差異
- **Deploy 節點**：使用 `deploy_operations.py` 模組（Python-based），根據平台類型路由到對應的部署方式
- **統一設計哲學**：兩者都遵循「Python 負責編排，平台專用工具負責執行」的原則

**抽象層的價值**：

- **隱藏平台差異**：Build 支援不同編譯工具鏈，Deploy 支援 Network (SSH/SCP) 和 Direct Flashing (JTAG/USB) 等不同方式
- **支援跨平台**：Windows、Linux、macOS 統一介面
- **簡化新平台整合**：只需提供符合規範的 Makefile, bash script 或 deploy.py script，無需修改 Python 核心程式碼
- **職責分離清晰**：Python 負責流程編排和配置收集，平台專用 script 負責實際執行

**設計哲學**：Python 程式碼只負責流程編排、配置收集和結果解析，Build 透過 Maketool 抽象層，Deploy 透過 deploy_operations 模組，兩者各自隱藏平台和工具鏈細節。

**Build 與 Deploy 架構對比圖**：

```mermaid
graph TB
    subgraph Python["Python 層 (FWAuto)"]
        BUILD_NODE["build_node"]
        DEPLOY_NODE["deploy_node"]
        MAKE_CMD["execute_make_command"]
        DEPLOY_OPS["deploy_operations.py"]
    end

    subgraph BuildAbstraction["Build 抽象層 (Makefile)"]
        BUILD_MK["build/Makefile<br/>build target"]
    end

    subgraph DeployAbstraction["Deploy 抽象層 (deploy_operations)"]
        ROUTE["路由函數<br/>_get_deploy_type()"]
        NET_DEPLOY["_deploy_network()<br/>Network Deployment"]
        FLASH_DEPLOY["_deploy_direct_flashing()<br/>Direct Flashing"]
    end

    subgraph PlatformScripts["平台專用 Scripts"]
        DEPLOY_SCRIPT["deploy/deploy.py<br/>Platform-specific"]
    end

    subgraph Toolchain["工具鏈層 (可替換)"]
        GCC["GCC/Clang<br/>gcc, clang"]
        SSH["SSH/SCP<br/>Network 部署"]
        JTAG["ST-LINK/J-Link<br/>Direct Flashing<br/>(v1.0+ 規劃中)"]
    end

    BUILD_NODE --> MAKE_CMD
    DEPLOY_NODE --> DEPLOY_OPS

    MAKE_CMD -->|"make -f build/Makefile build"| BUILD_MK
    DEPLOY_OPS --> ROUTE

    ROUTE -->|"AM62X/RPi/RISC-V"| NET_DEPLOY
    ROUTE -->|"STM32/ESP32/nRF52<br/>(規劃中)"| FLASH_DEPLOY

    BUILD_MK --> GCC

    NET_DEPLOY -->|"python3 deploy.py"| DEPLOY_SCRIPT
    FLASH_DEPLOY -.->|"NotImplementedError"| DEPLOY_SCRIPT

    DEPLOY_SCRIPT --> SSH
    DEPLOY_SCRIPT -.-> JTAG

    style Python fill:#e1f5ff
    style BuildAbstraction fill:#fff3e0
    style DeployAbstraction fill:#e8f5e9
    style PlatformScripts fill:#ffe4b5
    style Toolchain fill:#f3e5f5
    style FLASH_DEPLOY fill:#FFE4B5
    style JTAG fill:#FFE4B5
```

**關鍵優勢**：

- **Build**：新增工具鏈支援只需修改 Makefile，無需改 Python 程式碼
- **Deploy**：透過 `deploy_operations.py` 路由到對應的部署方式，支援跨平台 (Windows/Linux/macOS)
- **統一介面**：Build 透過 `make build` 執行，Deploy 透過 `deploy_firmware()` 函數執行
- **架構一致性**：Build 和 Deploy 都遵循「Python 編排 + 平台專用工具執行」的模式
- **易於擴展**：新增 Linux 平台共用 Network Deployment，新增 MCU 平台共用 Direct Flashing

### Deploy Operations 模組（平台抽象路由層）

Deploy 節點採用與 Build 節點一致的抽象層模式，透過 `deploy_operations.py` 模組實現平台無關的部署編排。

#### 核心設計

**deploy_operations.py** 作為路由層，根據 SDK type 自動分流到對應的部署方法：

```mermaid
flowchart TD
    DN["deploy_node"]
    DF["deploy_firmware()<br/>(路由函數)"]
    GDT["_get_deploy_type()<br/>(SDK → Deploy Type 映射)"]

    subgraph "Network Deployment"
        DPN["_deploy_network()"]
        CNC["_check_network_connectivity()"]
        DPS_SSH["deploy.py (SSH)"]
    end

    subgraph "Direct Flashing"
        DPF["_deploy_direct_flashing()"]
        DPS_STM32["deploy.py (ST-LINK)"]
    end

    DN --> DF
    DF --> GDT
    GDT -->|"network"| DPN
    GDT -->|"direct_flashing"| DPF

    DPN --> CNC --> DPS_SSH
    DPF -.->|"NotImplementedError"| DPS_STM32

    style DPF fill:#FFE4B5
    style DPS_STM32 fill:#FFE4B5
```

#### 平台支援

#### 設計原則

- **工具無關**：deploy_operations 不包含 SSH/ST-LINK/esptool 等工具的調用邏輯
- **平台特定邏輯在 deploy.py**：所有工具調用都委託給平台專用的 deploy.py script
- **統一架構**：與 Build 節點的 Maketool 抽象層保持一致的設計模式

**詳細技術文檔**：參見 [Deploy Abstraction Architecture](./deploy-abstraction.md)

#### 平台執行流程對比

**Deploy 節點根據平台類型路由到不同的實作方式**：

```mermaid
graph TD
    A["Deploy Node"] --> B["deploy_operations.py"]
    B --> C{"SDK Type 路由"}

    C -->|"AM62X/RPi/RISC-V"| D["_deploy_network()"]
    C -->|"STM32/ESP32/nRF52<br/>(規劃中)"| E["_deploy_direct_flashing()"]

    D --> F["check_connectivity"]
    F --> G["deploy.py (SSH/SCP)"]
    G --> H["遠端執行"]

    E -.->|"NotImplementedError"| I["deploy.py (JTAG/USB)"]
    I -.-> J["Direct Flashing"]

    style H fill:#c8e6c9
    style E fill:#FFE4B5
    style I fill:#FFE4B5
    style J fill:#FFE4B5
```

**說明**：

- **AM62X/Raspberry Pi/RISC-V（v0.5.0 已支援）**：Network Deployment 透過 SSH/SCP 遠端部署
- **STM32/ESP32/nRF52（v1.0+ 規劃中）**：Direct Flashing 透過 JTAG/SWD/USB 燒錄器

### 錨點模式設計

類似 Git 的 `.git/` 機制：

- 從當前目錄向上搜尋 `.fwauto/` 目錄，找到專案根目錄
- 無需全域配置或專案註冊
- 支援在專案任何子目錄執行命令
- 多專案並存，互不干擾

**實作位置**：`src/fwauto/config.py` (`find_project_root()` 函數)

**錨點模式搜尋流程圖**：

```mermaid
graph TD
    START([開始執行命令]) --> GET_CWD[取得當前目錄]
    GET_CWD --> CHECK{目錄下是否有<br/>.fwauto/?}

    CHECK -->|是| FOUND[找到專案根目錄]
    CHECK -->|否| IS_ROOT{是否到達<br/>根目錄?}

    IS_ROOT -->|是| ERROR[錯誤: 找不到 .fwauto/]
    IS_ROOT -->|否| MOVE_UP[移動到上層目錄]
    MOVE_UP --> CHECK

    FOUND --> LOAD[載入 config.toml]
    LOAD --> INIT[初始化專案]
    INIT --> END([執行工作流程])
    ERROR --> STOP([終止執行])

    style FOUND fill:#90EE90
    style ERROR fill:#FFB6C1
    style INIT fill:#87CEEB
```

## 3. 系統架構

### 整體架構圖

```mermaid
graph TB
    subgraph CLI["CLI 層 (Typer)"]
        CMD["fwauto build/deploy/run/log/feat/chat/init"]
    end

    subgraph LG["LangGraph 層 (State Management)"]
        STATE["FirmwareState<br/>20 個欄位"]
        NODES["6 個核心節點<br/>init/build/deploy/ai_brain/log/chat"]
        ROUTES["5 個路由函數<br/>控制流程分支與循環"]
    end

    subgraph BUSINESS["Nodes 層 (業務邏輯)"]
        INIT["init_project"]
        BUILD["build"]
        BRAIN["ai_brain"]
        DEPLOY["deploy"]
        LOG["log"]
        CHAT["chat"]
    end

    subgraph TOOLS["工具層 (外部整合)"]
        MAKE["execute_make_command<br/>Makefile 執行 (Build)"]
        DEPLOY_OPS["deploy_operations.py<br/>部署路由 (Deploy)"]
        SDK["Claude Code SDK<br/>AI 程式碼讀寫"]
        JINJA["Jinja2<br/>Prompt 模板系統"]
    end

    CMD --> STATE
    STATE --> NODES
    NODES --> ROUTES
    ROUTES --> INIT
    ROUTES --> BUILD
    ROUTES --> BRAIN
    ROUTES --> DEPLOY
    ROUTES --> LOG
    ROUTES --> CHAT

    INIT -.-> MAKE
    BUILD -.-> MAKE
    DEPLOY -.-> DEPLOY_OPS
    BRAIN -.-> SDK
    BRAIN -.-> JINJA
    LOG -.-> SDK
    LOG -.-> JINJA
    CHAT -.-> SDK
    CHAT -.-> JINJA

    style CLI fill:#e1f5ff
    style LG fill:#fff3e0
    style BUSINESS fill:#f3e5f5
    style TOOLS fill:#e8f5e9
```

### 系統組件關係圖

```mermaid
graph TB
    subgraph External["外部系統"]
        USER["使用者"]
        TOOLCHAIN["工具鏈<br/>GCC/Clang"]
        DEPLOY_TOOL["部署工具<br/>SSH/SCP (已支援)<br/>ST-LINK/J-Link (規劃中)"]
        DEVICE["目標裝置<br/>AM62X/RPi/RISC-V (已支援)<br/>STM32/ESP32/nRF52 (規劃中)"]
    end

    subgraph FWAuto["FWAuto System"]
        subgraph CLI_Layer["CLI 層"]
            TYPER["Typer CLI<br/>命令解析"]
        end

        subgraph Workflow["LangGraph 工作流程"]
            STATE["FirmwareState<br/>狀態管理"]
            GRAPH["Graph Builder<br/>節點與路由"]
        end

        subgraph Nodes["節點層"]
            INIT_N["Init Node"]
            BUILD_N["Build Node"]
            DEPLOY_N["Deploy Node"]
            AI_N["AI Brain Node"]
            LOG_N["Log Node"]
            CHAT_N["Chat Node"]
        end

        subgraph Tools["工具層"]
            MAKE_EXEC["Makefile<br/>Executor"]
            DEPLOY_OPS["deploy_operations<br/>路由器"]
            SDK["Claude Code<br/>SDK"]
            PROMPT["Prompt<br/>Manager"]
        end

        subgraph Config["配置管理"]
            PROJECT_CFG["ProjectConfig"]
            ANCHOR["Anchor Pattern<br/>Search"]
        end
    end

    USER -->|執行命令| TYPER
    TYPER --> STATE
    STATE --> GRAPH
    GRAPH --> INIT_N
    GRAPH --> BUILD_N
    GRAPH --> DEPLOY_N
    GRAPH --> AI_N
    GRAPH --> LOG_N
    GRAPH --> CHAT_N

    INIT_N --> ANCHOR
    INIT_N --> PROJECT_CFG

    BUILD_N --> MAKE_EXEC
    DEPLOY_N --> DEPLOY_OPS

    AI_N --> SDK
    AI_N --> PROMPT
    LOG_N --> SDK
    LOG_N --> PROMPT
    CHAT_N --> SDK
    CHAT_N --> PROMPT

    MAKE_EXEC -->|build| TOOLCHAIN
    DEPLOY_OPS -->|deploy| DEPLOY_TOOL
    DEPLOY_TOOL --> DEVICE

    SDK -->|讀寫程式碼| PROJECT_CFG

    style External fill:#ffe4e1
    style FWAuto fill:#e8f5e9
    style CLI_Layer fill:#e1f5ff
    style Workflow fill:#fff3e0
    style Nodes fill:#f3e5f5
    style Tools fill:#e8f5e9
    style Config fill:#fafad2
```

### 流程圖:

```mermaid
flowchart TD
    Start["開始 fwauto init"] --> CheckOS["偵測當前作業系統"]
    CheckOS --> LoadUser["載入 User Config"]
    LoadUser --> FilterOS["過濾 OS 兼容的 SDK"]

    FilterOS --> CheckConfigured{"是否有已配置的 SDK?"}

    CheckConfigured -->|是| ShowBoth["顯示兩個列表:<br/>1. 已配置的 SDK<br/>2. 可用但未配置的 SDK"]
    CheckConfigured -->|否| ShowAvailable["顯示:<br/>可用但未配置的 SDK"]

    ShowBoth --> UserChoice{"使用者選擇"}
    ShowAvailable --> UserChoice

    UserChoice -->|選擇已配置| PathA["路徑 A: 使用現有配置"]
    UserChoice -->|選擇未配置| PathB["路徑 B: 建立新 SDK"]

    PathA --> SkipInput["跳過 SDK 路徑輸入"]
    PathA --> SkipDevice["跳過裝置資訊輸入"]

    PathB --> InputPath["輸入 SDK 安裝路徑"]
    PathB --> InputDevice["輸入裝置系列與型號"]
    PathB --> SaveUser["儲存至 User Config"]

    SkipInput --> CopyTemplate["複製 Template 至專案"]
    SkipDevice --> CopyTemplate
    SaveUser --> CopyTemplate

    CopyTemplate --> CreateProject["建立 Project Config"]
    CreateProject --> Done["初始化完成"]

    style PathA fill:#90EE90
    style PathB fill:#87CEEB
    style Done fill:#FFD700
```

### LangGraph 工作流程

#### 完整工作流程圖

```mermaid
graph TD
    START([START]) --> ROUTE_START{route_from_start}

    ROUTE_START -->|needs init| INIT[init_project_node]
    ROUTE_START -->|build mode| BUILD[build_node]
    ROUTE_START -->|deploy mode| DEPLOY[deploy_node]
    ROUTE_START -->|log mode| LOG[log_node]
    ROUTE_START -->|feat mode| BRAIN[ai_brain_node]
    ROUTE_START -->|chat mode| CHAT[chat_node]
    ROUTE_START -->|error| END1([END])

    INIT --> ROUTE_INIT{route_after_init}
    ROUTE_INIT -->|build/run mode| BUILD
    ROUTE_INIT -->|deploy mode| DEPLOY
    ROUTE_INIT -->|log mode| LOG
    ROUTE_INIT -->|feat mode| BRAIN
    ROUTE_INIT -->|chat mode| CHAT

    BUILD --> ROUTE_BUILD{route_after_build}
    ROUTE_BUILD -->|success + run mode| DEPLOY
    ROUTE_BUILD -->|success + build only| END2([END])
    ROUTE_BUILD -->|error + retry available| BRAIN
    ROUTE_BUILD -->|error + max retries| END3([END])

    BRAIN --> ROUTE_BRAIN{route_after_ai_brain}
    ROUTE_BRAIN -->|build mode| BUILD
    ROUTE_BRAIN -->|feat mode| END6([END])

    DEPLOY --> ROUTE_DEPLOY{route_after_deploy}
    ROUTE_DEPLOY -->|success/error| END4([END])

    LOG --> END5([END])

    CHAT --> END7([END])

    style START fill:#90EE90
    style END1 fill:#FFB6C1
    style END2 fill:#FFB6C1
    style END3 fill:#FFB6C1
    style END4 fill:#FFB6C1
    style END5 fill:#FFB6C1
    style END6 fill:#FFB6C1
    style END7 fill:#FFB6C1
    style BUILD fill:#87CEEB
    style BRAIN fill:#DDA0DD
    style DEPLOY fill:#F0E68C
    style CHAT fill:#E6E6FA
```

#### 核心組件

**FirmwareState** (20 個欄位)：

```python
# src/fwauto/state.py
class FirmwareState(TypedDict):
    # 基礎欄位
    user_prompt: str
    project_root: str
    project_initialized: bool

    # 編譯相關
    build_status: Literal["success", "error", "pending"]
    build_errors: list[str]
    build_log: str

    # 部署相關
    deploy_status: Literal["success", "error", "pending"]
    deploy_errors: list[str]
    deploy_log: str

    # 重試機制
    iteration: int
    max_retries: int

    # 命令系統
    execution_mode: Literal["build", "deploy", "run", "log", "feat", "chat"] | None
    command: str | None
    command_args: dict | None
    mode_metadata: dict | None
    command_error: str | None

    # 日誌分析
    log_file_path: str | None
    log_analysis_result: str | None

    # 韌體輸出
    firmware_path: str | None

    # 平台配置
    platform: str  # Platform identifier (e.g., 'am62x', 'raspberry-pi', 'stm32')
```

**6 個核心節點**：

| 節點           | 檔案位置            | 主要職責                                                               |
| -------------- | ------------------- | ---------------------------------------------------------------------- |
| `init_project` | `nodes/init.py`     | 錨點模式搜尋 `.fwauto/` 並載入配置                                     |
| `build`        | `nodes/build.py`    | 執行 Makefile build，捕捉編譯輸出                                      |
| `deploy`       | `nodes/deploy.py`   | 使用 deploy_operations.py 路由到對應的部署方式（Network 或 Direct）    |
| `ai_brain`     | `nodes/ai_brain.py` | Claude Code SDK 自動修復編譯錯誤或執行 feat 任務                       |
| `log`          | `nodes/log.py`      | Claude Code SDK 分析日誌，支援本地和遠端日誌（自動檢測上次部署的路徑） |
| `chat`         | `nodes/chat.py`     | Claude Code SDK 互動式對話功能                                         |

### Logging 系統架構

FWAuto 使用集中式 logging 系統記錄所有 nodes 的執行過程，提供完整的執行追蹤和問題診斷能力。

#### Logging 初始化流程

**關鍵設計**：所有命令執行時都會先經過 `init_project_node`，確保 logging 系統初始化完成。

```mermaid
flowchart TD
    START["命令執行開始"] --> CHECK{"專案已初始化?"}

    CHECK -->|否| INIT["init_project_node"]
    CHECK -->|是| ROUTE["直接路由到目標 node"]

    INIT --> SEARCH["搜尋 .fwauto/ 錨點"]
    SEARCH --> FOUND{"找到專案?"}

    FOUND -->|是| INIT_LOG["init_project_logging()"]
    FOUND -->|否| ERROR["返回錯誤"]

    INIT_LOG --> CREATE_DIR["建立 .fwauto/logs/"]
    CREATE_DIR --> FILE_HANDLER["建立 FileHandler<br/>fwauto_YYYYMMDD_HHMMSS.log"]
    FILE_HANDLER --> HIERARCHY["設定 logger hierarchy<br/>fwauto.*"]
    HIERARCHY --> ROUTE

    ERROR --> END1["結束執行"]

    ROUTE --> NODE_EXEC["Node 執行<br/>logger.info/debug 寫入檔案"]

    style INIT fill:#90EE90
    style INIT_LOG fill:#87CEEB
    style FILE_HANDLER fill:#87CEEB
    style NODE_EXEC fill:#90EE90
```

**核心檔案**：

| 檔案                        | 職責                                         |
| --------------------------- | -------------------------------------------- |
| `logging_config.py:158-168` | `init_project_logging()` - 初始化入口        |
| `logging_config.py:58-105`  | `setup_logging()` - 建立 FileHandler         |
| `logging_config.py:108-113` | `get_logger()` - 取得 logger instance        |
| `nodes/init.py:33`          | **唯一調用** `init_project_logging()` 的位置 |

**路由邏輯**（`graph.py:47-49`）：

```python
# 所有命令都需要先經過專案初始化
if not state.get("project_initialized", False):
    return "init_project"
```

**`init_project_node` 的職責**：

1. **專案搜尋**：從當前目錄向上搜尋 `.fwauto/` 錨點
2. **配置載入**：載入 `config.toml` 和 user config
3. **Logging 初始化**：調用 `init_project_logging(project_root)`
4. **State 更新**：設定 `project_root` 和 `project_initialized = True`

**設計優勢**：

- ✅ 統一的初始化流程：所有命令都經過相同的初始化路徑
- ✅ 完整的執行追蹤：所有 nodes 的執行日誌都會記錄到檔案
- ✅ 簡化的邏輯：無需特殊判斷，降低維護成本
- ✅ 可靠的診斷：任何命令執行問題都可以透過日誌分析

#### Logger 使用規範

**所有 nodes 必須遵循以下規範**：

```python
# 1. 在檔案頂層取得 logger
from ..logging_config import get_logger

logger = get_logger(__name__)  # 使用 __name__ 而非硬編碼

# 2. 在函數中使用 logger
def my_node(state: FirmwareState) -> dict[str, Any]:
    logger.info("📊 執行開始")
    logger.debug(f"輸入參數: {state.get('user_prompt')}")

    try:
        # 執行邏輯
        result = do_something()
        logger.info("✅ 執行成功")
        return {"status": "success"}
    except Exception as e:
        logger.error(f"❌ 執行失敗: {e}")
        return {"status": "error"}
```

**Logger Hierarchy**：

```
fwauto (root)
├── fwauto.nodes.init
├── fwauto.nodes.build
├── fwauto.nodes.deploy
├── fwauto.nodes.ai_brain
├── fwauto.nodes.log
├── fwauto.nodes.chat
├── fwauto.config
├── fwauto.utils
└── fwauto.deploy_operations
```

**Log 輸出位置**：

- **檔案輸出**：`.fwauto/logs/fwauto_YYYYMMDD_HHMMSS.log`
- **Console 輸出**：由 `print()` 負責（nodes 中的使用者訊息）
- **分離原則**：
  - `logger.info/debug/error` → 檔案記錄（完整執行追蹤）
  - `print()` → Console 輸出（使用者友善訊息）

**範例 Log 檔案**：

```
[2025-11-18 14:10:59] [fwauto.nodes.build] [build.py:60] INFO: 🔨 Building project...
[2025-11-18 14:10:59] [fwauto.utils] [utils.py:108] INFO: Executing: make build PROJECT_ROOT=/home/user/project
[2025-11-18 14:10:59] [fwauto.nodes.build] [logging_config.py:139] INFO: ✅ Build completed: Build succeeded
[2025-11-18 14:10:59] [fwauto.nodes.deploy] [deploy.py:93] INFO: 📥 Deploying firmware...
[2025-11-18 14:11:01] [fwauto.deploy_operations] [deploy_operations.py:399] INFO: ✅ Deploy completed successfully
```

**FirmwareState 狀態轉換圖**：

```mermaid
stateDiagram-v2
    [*] --> Pending: 初始化

    state "編譯流程" as BuildFlow {
        Pending --> Building: start build
        Building --> BuildSuccess: success
        Building --> BuildError: error

        BuildError --> AIFixing: iteration < max_retries
        AIFixing --> Building: retry
        BuildError --> Failed: iteration >= max_retries
    }

    state "部署流程" as DeployFlow {
        BuildSuccess --> Deploying: run mode
        Deploying --> DeploySuccess: success
        Deploying --> DeployError: error
    }

    BuildSuccess --> [*]: build only mode
    DeploySuccess --> [*]
    DeployError --> [*]
    Failed --> [*]

    note right of AIFixing
        AI Brain 自動修復
        最多重試 3 次
    end note
```

### 核心類別結構圖

```mermaid
classDiagram
    class FirmwareState {
        <<TypedDict>>
        +str user_prompt
        +str project_root
        +bool project_initialized
        +str build_status
        +list~str~ build_errors
        +str build_log
        +str deploy_status
        +list~str~ deploy_errors
        +str deploy_log
        +int iteration
        +int max_retries
        +str execution_mode
        +str command
        +dict command_args
        +dict mode_metadata
        +str command_error
        +str log_file_path
        +str log_analysis_result
        +str firmware_path
    }

    class ProjectConfig {
        +Path project_root
        +str project_name
        +Path build_makefile_path
        +dict~str,str~ env_vars
        +__init__(project_root)
        +_load_config() dict
        +_get_default_config() dict
    }

    class PromptManager {
        +Path prompts_dir
        +Environment jinja_env
        +__init__(prompts_dir)
        +get_prompt(task_type, state) str
        +_render_template(template_name, context) str
    }

    class ClaudeCodeOptions {
        <<from SDK>>
        +list~str~ allowed_tools
        +str permission_mode
        +list~Path~ add_dirs
        +Path cwd
        +str append_system_prompt
    }

    class Node {
        <<interface>>
        +execute(state: FirmwareState) dict
    }

    class InitNode {
        +execute(state) dict
        -_find_project_root() Path
    }

    class BuildNode {
        +execute(state) dict
        -_run_makefile() tuple
    }

    class DeployNode {
        +execute(state) dict
        -_render_remote_log_path(config, binary_name) str
    }

    class NetworkDeployConfig {
        <<dataclass>>
        +str board_ip
        +str board_user
        +str deploy_path
        +str ssh_options
        +str binary_args
        +str execution_mode
        +bool remote_log_enabled
        +str remote_log_path
    }

    class DeployResult {
        <<dataclass>>
        +bool success
        +str message
        +str binary_name
        +int pid
    }

    class DeployOperations {
        <<module>>
        +find_binary(build_dir) Path
        +_extract_binary_name_from_output(stderr) str
        +_extract_pid_from_output(stderr) int
        +deploy_firmware(build_dir, config) DeployResult
    }

    class ChatNode {
        +execute(state) dict
        -_create_claude_options(state) ClaudeCodeOptions
    }

    class AIBrainNode {
        +execute(state) dict
        -_analyze_task_type(state) str
        -_create_claude_options(state) ClaudeCodeOptions
    }

    class LogNode {
        +execute(state) dict
        -_create_claude_options(state) ClaudeCodeOptions
    }

    Node <|.. InitNode
    Node <|.. BuildNode
    Node <|.. DeployNode
    Node <|.. AIBrainNode
    Node <|.. LogNode
    Node <|.. ChatNode

    InitNode --> ProjectConfig : uses
    BuildNode --> ProjectConfig : uses
    DeployNode --> ProjectConfig : uses
    DeployNode --> DeployOperations : uses
    DeployOperations --> NetworkDeployConfig : creates internally
    DeployOperations --> DeployResult : returns
    AIBrainNode --> PromptManager : uses
    AIBrainNode --> ClaudeCodeOptions : creates
    LogNode --> PromptManager : uses
    LogNode --> ClaudeCodeOptions : creates
    ChatNode --> PromptManager : uses
    ChatNode --> ClaudeCodeOptions : creates

    note for FirmwareState "19 個欄位管理整個工作流程狀態"
    note for ProjectConfig "從 .fwauto/config.toml 載入配置"
    note for Node "所有節點都實作此介面"
```

**路由函數** (`src/fwauto/graph.py`)：

1. `route_from_start` - 初始路由（根據 execution_mode：build/deploy/run/log/feat/chat）
2. `route_after_init` - 初始化後路由
3. `route_after_build` - Build 後路由（包含 AI 重試邏輯）
4. `route_after_ai_brain` - AI 修復後路由（build mode 回到 build 驗證，feat mode 結束）
5. `route_after_deploy` - Deploy 後總是結束
6. Log 節點直接連接 END
7. Chat 節點直接連接 END

#### AI 重試循環

```mermaid
sequenceDiagram
    participant User
    participant CLI
    participant Build
    participant AI_Brain
    participant State

    User->>CLI: fwauto build
    CLI->>State: 初始化 FirmwareState
    State->>Build: execution_mode=build

    rect rgb(255, 240, 240)
    note right of Build: 第 1 次嘗試
    Build->>Build: 執行 Maketool build
    Build-->>State: build_status=error<br/>iteration=1
    State->>AI_Brain: iteration < max_retries
    AI_Brain->>AI_Brain: 分析錯誤並修復程式碼
    AI_Brain-->>State: 修復完成
    end

    rect rgb(255, 250, 240)
    note right of Build: 第 2 次嘗試
    State->>Build: 重新編譯驗證
    Build->>Build: 執行 Maketool build
    Build-->>State: build_status=error<br/>iteration=2
    State->>AI_Brain: iteration < max_retries
    AI_Brain->>AI_Brain: 再次分析並修復
    AI_Brain-->>State: 修復完成
    end

    rect rgb(240, 255, 240)
    note right of Build: 第 3 次嘗試
    State->>Build: 重新編譯驗證
    Build->>Build: 執行 Maketool build
    Build-->>State: build_status=success
    State-->>CLI: 編譯成功
    CLI-->>User: ✓ Build succeeded
    end

    note over User,State: 最多重試 3 次 (max_retries=3)
```

#### 錯誤處理流程

```mermaid
graph TD
    START([錯誤發生]) --> TYPE{錯誤類型}

    TYPE -->|Build Error| BUILD_ERR[編譯錯誤]
    TYPE -->|Deploy Error| DEPLOY_ERR[部署錯誤]
    TYPE -->|Config Error| CFG_ERR[配置錯誤]
    TYPE -->|Runtime Error| RUNTIME_ERR[執行時錯誤]

    BUILD_ERR --> CHECK_RETRY{iteration < max_retries?}
    CHECK_RETRY -->|是| LOG_ERR[記錄錯誤到<br/>build_errors]
    CHECK_RETRY -->|否| MAX_RETRY[達到最大重試次數]

    LOG_ERR --> UPDATE_STATE[更新 State:<br/>iteration += 1]
    UPDATE_STATE --> AI_BRAIN[路由到 AI Brain]
    AI_BRAIN --> ANALYZE[分析錯誤並修復]
    ANALYZE --> REBUILD[重新編譯驗證]
    REBUILD --> CHECK_RESULT{編譯成功?}
    CHECK_RESULT -->|是| SUCCESS[標記 build_status=success]
    CHECK_RESULT -->|否| CHECK_RETRY

    MAX_RETRY --> DISPLAY_ERR[顯示所有錯誤訊息]
    DISPLAY_ERR --> END_FAIL([工作流程結束: 失敗])

    DEPLOY_ERR --> LOG_DEPLOY[記錄到 deploy_errors]
    LOG_DEPLOY --> DISPLAY_DEPLOY[顯示部署錯誤]
    DISPLAY_DEPLOY --> END_DEPLOY([工作流程結束: 部署失敗])

    CFG_ERR --> DISPLAY_CFG[顯示配置錯誤提示]
    DISPLAY_CFG --> SUGGEST[建議: 檢查 .fwauto/config.toml]
    SUGGEST --> END_CFG([工作流程結束: 配置錯誤])

    RUNTIME_ERR --> LOG_RUNTIME[記錄完整 traceback]
    LOG_RUNTIME --> DISPLAY_RUNTIME[顯示錯誤堆疊]
    DISPLAY_RUNTIME --> END_RUNTIME([工作流程結束: 執行時錯誤])

    SUCCESS --> NEXT_STEP[繼續後續流程]
    NEXT_STEP --> END_SUCCESS([工作流程繼續])

    style BUILD_ERR fill:#FFB6C1
    style DEPLOY_ERR fill:#FFB6C1
    style CFG_ERR fill:#FFB6C1
    style RUNTIME_ERR fill:#FFB6C1
    style AI_BRAIN fill:#DDA0DD
    style SUCCESS fill:#90EE90
    style MAX_RETRY fill:#FF6347
```

**重試邏輯** (`src/fwauto/graph.py:86-106`)：

```python
def route_after_build(state: FirmwareState) -> str:
    if state["build_status"] == "success":
        # 成功 -> 檢查是否需要 deploy
        if state["execution_mode"] == "run":
            return "deploy"
        return "end"
    else:
        # 失敗 -> 檢查重試次數
        if state["iteration"] < state["max_retries"]:
            return "ai_brain"  # AI 修復
        return "end"  # 超過重試次數

def route_after_ai_brain(state: FirmwareState) -> str:
    return "build"  # 總是回到 build 驗證修復結果
```

**State 更新模式**（遵循 LangGraph 最佳實踐）：

```python
# 增量更新，不修改原始 state
return {**state, "build_status": "success"}
```

## 4. 擴展點設計

FWAuto 設計了三個主要擴展點，讓系統可以輕鬆支援新的功能、平台和任務類型。

### 擴展點架構圖

```mermaid
graph TB
    subgraph Extension1["擴展點 1: CLI 命令"]
        CLI_EXT[新增 CLI 命令]
        CLI_STEP1[1 在 cli.py 新增<br/>@app.command]
        CLI_STEP2[2 初始化 FirmwareState<br/>設定 execution_mode]
        CLI_STEP3[3 在 graph.py 路由函數<br/>處理新 mode]

        CLI_EXT --> CLI_STEP1
        CLI_STEP1 --> CLI_STEP2
        CLI_STEP2 --> CLI_STEP3
    end

    subgraph Extension2["擴展點 2: 平台支援"]
        PLATFORM_EXT[支援新平台]
        PLATFORM_STEP1[1 準備 build/Maketool<br/>實作 build target]
        PLATFORM_STEP2[2 準備 deploy/script<br/>實作 deploy 邏輯]
        PLATFORM_STEP3[3 放置到專案<br/>.fwauto/ 目錄]

        PLATFORM_EXT --> PLATFORM_STEP1
        PLATFORM_STEP1 --> PLATFORM_STEP2
        PLATFORM_STEP2 --> PLATFORM_STEP3
    end

    subgraph Extension3["擴展點 3: AI 任務"]
        AI_EXT[新增 AI 任務類型]
        AI_STEP1[1 在 prompts/ 新增<br/>Jinja2 模板]
        AI_STEP2[2 在 ai_brain.py 新增<br/>任務類型判斷邏輯]
        AI_STEP3[3 在 prompt_manager.py<br/>註冊新 prompt]

        AI_EXT --> AI_STEP1
        AI_STEP1 --> AI_STEP2
        AI_STEP2 --> AI_STEP3
    end

    subgraph Result["擴展結果"]
        NEW_CMD[新 CLI 命令可用]
        NEW_PLATFORM[新平台可編譯/燒錄]
        NEW_TASK[新 AI 任務可執行]
    end

    CLI_STEP3 --> NEW_CMD
    PLATFORM_STEP3 --> NEW_PLATFORM
    AI_STEP3 --> NEW_TASK

    style Extension1 fill:#e1f5ff
    style Extension2 fill:#fff3e0
    style Extension3 fill:#f3e5f5
    style Result fill:#e8f5e9
```

**擴展點特性**：

- ✅ **無需修改核心邏輯**：所有擴展都是增量式的
- ✅ **獨立性**：三個擴展點互不影響
- ✅ **簡單性**：每個擴展點都有明確的步驟

### 如何新增 CLI 命令

**步驟**：

1. 在 `src/fwauto/cli.py` 新增 `@app.command()` 裝飾的函數
2. 初始化 `FirmwareState` 並設定 `execution_mode`
3. 呼叫 `run_workflow(state)`

**範例**：

```python
# src/fwauto/cli.py
@app.command()
def my_command():
    """My custom command description."""
    state: FirmwareState = {
        "user_prompt": "my_command",
        "project_root": "",
        "project_initialized": False,
        "execution_mode": "my_mode",
        # ... 其他必要欄位
    }
    run_workflow(state)
```

然後在 `src/fwauto/graph.py` 的路由函數中處理新的 `execution_mode`。

**CLI 命令執行流程圖**：

```mermaid
graph LR
    USER[使用者輸入<br/>fwauto build] --> TYPER[Typer 解析命令]
    TYPER --> VALIDATE{驗證參數}

    VALIDATE -->|有效| INIT_STATE[初始化<br/>FirmwareState]
    VALIDATE -->|無效| ERROR1[顯示錯誤訊息]
    ERROR1 --> END1([結束])

    INIT_STATE --> SET_MODE[設定 execution_mode]
    SET_MODE --> CREATE_WF[創建 LangGraph<br/>Workflow]
    CREATE_WF --> STREAM[Stream 執行<br/>Workflow]

    STREAM --> EVENTS[處理每個節點<br/>的執行結果]
    EVENTS --> DISPLAY[即時顯示<br/>進度訊息]
    DISPLAY --> COMPLETE{執行完成?}

    COMPLETE -->|是| END2([結束])
    COMPLETE -->|否| EVENTS

    style INIT_STATE fill:#87CEEB
    style STREAM fill:#DDA0DD
    style DISPLAY fill:#90EE90
```

### Scenario 功能

**deploy** 和 **run** 命令支援 `--scenario` / `-s` 參數，允許使用預定義的部署場景。

**使用範例**：

```bash
# 使用預定義場景
fwauto deploy --scenario led-on
fwauto run -s test-mode

# 場景定義於 .fwauto/config.toml
```

**配置範例**（`.fwauto/config.toml`）：

```toml
[[scenarios]]
name = "led-on"
description = "Turn on LED"
binary_args = "on"

[[scenarios]]
name = "test-mode"
description = "Run in test mode"
binary_args = "--mode test --verbose"
```

### Interactive Mode

執行 `fwauto` 無參數進入互動模式，提供持續對話介面。

**功能特性**：

- **互動式介面**：無需重複啟動，連續執行多個指令
- **CJK 字符支援**：支援中文、日文、韓文輸入
- **指令歷史**：可使用上下箭頭瀏覽歷史指令

**使用範例**：

```bash
$ fwauto
fwauto> build
🔨 Building project...
✓ Build succeeded

fwauto> deploy
📥 Deploying firmware...
✓ Deploy succeeded

fwauto> log "有任何 error 嗎?"
📊 Analyzing log...
[AI 分析結果]

fwauto> exit
👋 Goodbye!
```
