---
date: 2025-10-28T00:00:00+08:00
researcher: Claude (Sonnet 4.5)
git_commit: 5aa70d5b80a66530595245e2098b8f7d23c975b3
branch: main
repository: FWAauto
topic: "實作 chat feature - 通用對話功能研究"
tags: [research, codebase, chat, langgraph, claude-sdk, interactive]
status: complete
last_updated: 2025-10-28
last_updated_by: Claude (Sonnet 4.5)
last_updated_note: "完成 5 個 Open Questions 的討論並確定實作方向"
---

# Research: 實作 chat feature - 通用對話功能

**Date**: 2025-10-28T00:00:00+08:00
**Researcher**: Claude (Sonnet 4.5)
**Git Commit**: 5aa70d5b80a66530595245e2098b8f7d23c975b3
**Branch**: main
**Repository**: FWAauto

## Research Question

如何在 FWAuto 中實作一個通用的 `chat` 功能，讓使用者可以與 Claude Code 進行專案相關的互動式對話？

### 需求確認

- **執行模式**: 互動式對話，支援多輪對話
- **工具權限**: 動態決定（根據對話內容調整權限）
- **架構**: 遵循現有架構，建立 `chat_node` 納入 LangGraph 工作流程
- **Context 載入**: 組合方式 - 自動讀取 `.fwauto/` 關鍵檔案 + 設定 `add_dirs` + `append_system_prompt`
- **設計原則**: KISS - 第一版功能要簡單，使用後再追加需求

## Summary

FWAuto 使用 LangGraph 管理狀態機工作流程，透過 Claude Code SDK 整合 AI 功能。目前有兩種 AI 節點：`ai_brain_node`（可寫權限）和 `log_node`（只讀權限）。兩者都使用 `query()` 函數進行單向查詢，不支援多輪對話。

要實作 `chat` 功能，需要：

1. **新增 `chat_node`**: 使用 `ClaudeSDKClient` 而非 `query()` 以支援多輪對話
2. **擴展 FirmwareState**: 新增對話歷史、權限模式等欄位
3. **實作互動式循環**: 在 CLI 層處理使用者輸入並持續對話
4. **動態權限機制**: 根據對話內容切換工具權限
5. **專案 context 載入**: 整合 `.fwauto/` 配置、FWAUTO.md、專案目錄訪問

### 關鍵發現

| 發現 | 影響 |
|------|------|
| 現有節點使用 `query()` 單向查詢 | 需要升級為 `ClaudeSDKClient` 以支援多輪對話 |
| FirmwareState 無對話歷史欄位 | 需要擴展 State 定義 |
| 已有互動式輸入範例（init 精靈） | 可參考 `cli_commands/init.py` 的實作 |
| SDK 支援動態權限切換 | 可透過 `set_permission_mode()` 實現 |
| 已有完整的 Prompt 模板系統 | 可擴展用於 chat 的 system prompt |

## Detailed Findings

### 1. 現有 LangGraph 架構

#### 1.1 工作流程結構

**檔案**: [`src/fwauto/graph.py:15-184`](src/fwauto/graph.py)

```mermaid
graph TD
    START([START]) --> RS{route_from_start}
    RS -->|needs init| INIT[init_project_node]
    RS -->|build/run| BUILD[build_node]
    RS -->|flash| FLASH[flash_node]
    RS -->|log| LOG[log_node]
    RS -->|feat| AI[ai_brain_node]

    INIT --> RA{route_after_init}
    RA --> BUILD
    RA --> FLASH
    RA --> LOG
    RA --> AI

    BUILD --> RB{route_after_build}
    RB -->|success + run| FLASH
    RB -->|error + retry| AI
    RB -->|end| END([END])

    AI --> BUILD
    FLASH --> END
    LOG --> END
```

**現有節點**:

| 節點 | 檔案位置 | 功能 | 權限 |
|------|---------|------|------|
| `init_project_node` | `nodes/init.py:10-54` | 搜尋 `.fwauto/` 錨點並載入配置 | 無 AI |
| `build_node` | `nodes/build.py` | 執行 Makefile build | 無 AI |
| `flash_node` | `nodes/flash.py` | 執行 Makefile flash | 無 AI |
| `ai_brain_node` | `nodes/ai_brain.py:24-73` | 自動修復編譯錯誤、實作功能 | 完全權限 |
| `log_node` | `nodes/log.py:20-125` | 分析 UART 日誌 | 只讀權限 |

#### 1.2 FirmwareState 結構

**檔案**: [`src/fwauto/state.py:6-77`](src/fwauto/state.py)

**現有欄位** (19 個):

```python
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

    # 燒錄
    flash_status: Literal["success", "error", "pending"]
    flash_errors: list[str]
    flash_log: str

    # 重試
    iteration: int
    max_retries: int

    # CLI
    execution_mode: Literal["build", "flash", "run", "log", "feat", "error"] | None
    command: str | None
    command_args: dict[str, Any] | None
    mode_metadata: dict[str, Any] | None
    command_error: str | None

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

    # 韌體輸出
    firmware_path: str | None

    # 平台
    platform: str
```

**Chat 功能需要新增的欄位**:

```python
# 對話相關 (建議新增)
chat_history: list[dict[str, Any]]  # 對話歷史記錄
chat_session_id: str | None          # 會話 ID
current_permission_mode: str         # 當前權限模式
```

### 2. Claude Code SDK 對話機制

#### 2.1 query() vs ClaudeSDKClient

**現有實作使用 `query()`**:

| 特性 | query() | ClaudeSDKClient |
|------|---------|-----------------|
| 通信方向 | 單向 | 雙向 |
| 狀態管理 | 無狀態 | 有狀態 |
| 對話模式 | 火發即忘 | 多輪對話 |
| 中斷支持 | ❌ | ✅ |
| 權限切換 | ❌ | ✅ (`set_permission_mode()`) |

**ai_brain_node 範例** ([`nodes/ai_brain.py:50`](src/fwauto/nodes/ai_brain.py)):

```python
# 單向查詢
async for message in query(prompt=prompt, options=options):
    logger.debug(f"🧠 AI Brain: {message}")
    responses.append(str(message))
```

**ClaudeSDKClient 用法** (SDK 文檔):

```python
async with ClaudeSDKClient(options=options) as client:
    await client.connect()

    # 第一輪對話
    await client.query("分析這個專案結構...")
    async for msg in client.receive_response():
        print(msg)

    # 動態切換權限
    await client.set_permission_mode("bypassPermissions")

    # 第二輪對話
    await client.query("修改 main.c...")
    async for msg in client.receive_response():
        print(msg)

    await client.disconnect()
```

#### 2.2 權限配置對比

**ai_brain_node** ([`nodes/ai_brain.py:109-133`](src/fwauto/nodes/ai_brain.py)):

```python
ClaudeAgentOptions(
    allowed_tools=["Read", "Write", "Edit", "MultiEdit", "Glob", "Grep", "Bash"],
    permission_mode="bypassPermissions",  # 完全權限
    add_dirs=[project_root],
    cwd=project_root
)
```

**log_node** ([`nodes/log.py:86-108`](src/fwauto/nodes/log.py)):

```python
ClaudeAgentOptions(
    allowed_tools=["Read", "Grep"],  # 只讀
    permission_mode="bypassPermissions",
    add_dirs=[log_dir],
    system_prompt=system_override  # 強制執行模式
)
```

#### 2.3 動態權限切換機制

SDK 完全支援，但**僅限 ClaudeSDKClient**:

```python
await client.set_permission_mode('default')          # 要求確認
await client.set_permission_mode('acceptEdits')      # 自動接受編輯
await client.set_permission_mode('plan')             # 只規劃不執行
await client.set_permission_mode('bypassPermissions')# 跳過所有確認
```

### 3. CLI 命令系統

#### 3.1 命令定義方式

**檔案**: [`src/fwauto/cli.py:65-284`](src/fwauto/cli.py)

**現有命令結構**:

```python
@app.command()
def build():
    """🔨 Build STM32 firmware project."""
    state: FirmwareState = {
        "user_prompt": "build",
        "execution_mode": "build",
        "command": "build",
        # ... 初始化所有欄位
    }
    run_workflow(state)
```

**命令與 execution_mode 對應**:

| CLI 命令 | execution_mode | 行號 | 特點 |
|---------|----------------|------|------|
| `build` | `"build"` | 65-98 | 單純編譯 |
| `flash` | `"flash"` | 102-135 | 單純燒錄 |
| `run` | `"run"` | 139-172 | 編譯+燒錄 |
| `log` | `"log"` | 176-221 | 日誌分析（帶參數） |
| `feat` | `"feat"` | 225-264 | 功能實作 |
| `init` | 無 | 268-284 | 初始化精靈 |

#### 3.2 互動式輸入範例

**檔案**: [`src/fwauto/cli_commands/init.py:60-257`](src/fwauto/cli_commands/init.py)

使用 **Rich 庫**:

```python
from rich.prompt import Confirm, IntPrompt, Prompt

# 整數選擇
choice = IntPrompt.ask("請選擇", choices=[str(i) for i in range(1, n+1)])

# 文字輸入
sdk_path = Prompt.ask(f"請輸入 SDK 路徑")

# 布林確認
confirm = Confirm.ask("是否繼續？", default=False)
```

**互動式模式** ([`cli.py:287-353`](src/fwauto/cli.py)):

```python
def _interactive_mode():
    """互動式命令行模式"""
    while True:
        user_input = console.input("[bold cyan]fwauto>[/bold cyan] ").strip()

        if user_input.lower() in ["exit", "quit", "q"]:
            break

        # 解析並執行命令
        args = shlex.split(user_input)
        with click_app.make_context("fwauto", args) as ctx:
            click_app.invoke(ctx)
```

### 4. 專案 Context 載入機制

#### 4.1 錨點搜尋

**檔案**: [`src/fwauto/config.py:118-143`](src/fwauto/config.py)

```python
def find_project_root(start_path: Path | None = None) -> Path | None:
    """向上搜尋 .fwauto/ 目錄"""
    current = start_path or Path.cwd()

    while True:
        if (current / ".fwauto").is_dir():
            return current  # 找到專案根

        if current.parent == current:
            return None  # 到達根目錄

        current = current.parent
```

#### 4.2 ProjectConfig 載入

**檔案**: [`src/fwauto/config.py:42-116`](src/fwauto/config.py)

**重要屬性**:

```python
class ProjectConfig:
    @property
    def build_makefile(self) -> str:
        return self.data.get("build", {}).get("makefile", ".fwauto/build/Makefile")

    @property
    def sdk_path(self) -> str | None:
        # 1. Project scope (罕見)
        project_sdk = self.data.get("sdk", {}).get("path")
        if project_sdk:
            return project_sdk

        # 2. User scope (常見) - 從 ~/.fwauto/config.toml 繼承
        user_sdk = self.user_config.get_sdk_config(self.sdk_type)
        return user_sdk.get("path") if user_sdk else None
```

#### 4.3 init_project_node

**檔案**: [`src/fwauto/nodes/init.py:10-54`](src/fwauto/nodes/init.py)

```python
def init_project_node(state: FirmwareState) -> dict[str, Any]:
    """初始化專案"""
    # 1. 搜尋 .fwauto/
    project_root = find_project_root()

    # 2. 初始化日誌
    init_project_logging(project_root)

    # 3. 載入配置
    load_project_config(project_root)

    return {..., "project_root": str(project_root), "project_initialized": True}
```

### 5. Prompt 模板系統

**檔案**: [`src/fwauto/prompt_manager.py:31-61`](src/fwauto/prompt_manager.py)

```python
def get_prompt(self, task_type: str, state: dict[str, Any]) -> str:
    """載入並渲染 Jinja2 模板"""
    variables = self._build_variables(state)
    template = self.env.get_template(f"{task_type}.md")
    return template.render(**variables)
```

**現有 Prompt**:

| 模板 | 用途 | 行數 |
|------|------|------|
| `fix_build_errors.md` | 修復編譯錯誤 | 110 行 |
| `implement_feature.md` | 實作新功能 | 118 行 |
| `log.md` | 日誌分析 | 17 行 |

**變數來源** ([`prompt_manager.py:69-127`](src/fwauto/prompt_manager.py)):

```python
variables = {
    # 系統資訊
    "cwd": os.getcwd(),
    "username": os.getenv("USER"),
    "os_name": platform.system(),
    "timestamp": datetime.now().isoformat(),

    # 平台配置
    "platform": state.get("platform"),
    "platform_config": PLATFORMS.get(state.get("platform"), {}),

    # State 欄位
    "build_errors": state.get("build_errors", []),
    "user_prompt": state.get("user_prompt", ""),
    # ...
}
```

## Architecture Insights

### FWAuto 架構模式

```mermaid
graph TB
    subgraph CLI["CLI 層 (Typer)"]
        CMD[fwauto 命令]
    end

    subgraph LG["LangGraph 層"]
        STATE[FirmwareState<br/>19 個欄位]
        ROUTER[路由函數<br/>6 個]
    end

    subgraph NODES["節點層"]
        INIT[init_project]
        BUILD[build]
        FLASH[flash]
        AI[ai_brain]
        LOG[log]
    end

    subgraph SDK["Claude Code SDK"]
        QUERY[query 函數<br/>單向查詢]
        CLIENT[ClaudeSDKClient<br/>多輪對話]
    end

    CMD --> STATE
    STATE --> ROUTER
    ROUTER --> INIT
    ROUTER --> BUILD
    ROUTER --> FLASH
    ROUTER --> AI
    ROUTER --> LOG

    AI -.-> QUERY
    LOG -.-> QUERY

    style SDK fill:#ffe4e1
    style QUERY fill:#90EE90
    style CLIENT fill:#FFB6C1
```

### Chat 功能架構方向

基於現有架構，chat 功能可以採用以下設計：

```mermaid
graph TB
    USER[使用者] --> CLI_CHAT[fwauto chat]
    CLI_CHAT --> INIT_STATE[初始化 FirmwareState<br/>execution_mode='chat']
    INIT_STATE --> WORKFLOW[LangGraph Workflow]

    WORKFLOW --> ROUTE_START{route_from_start}
    ROUTE_START -->|needs init| INIT_NODE[init_project_node]
    ROUTE_START -->|chat mode| CHAT_NODE[chat_node]

    INIT_NODE --> CHAT_NODE

    subgraph CHAT_NODE["chat_node (新節點)"]
        CONNECT[連接 ClaudeSDKClient]
        LOOP_START{等待使用者輸入}
        SEND[發送訊息]
        RECEIVE[接收回應]
        CHECK_PERMISSION{需要切換權限?}
        SWITCH[切換權限模式]
        CHECK_EXIT{exit?}

        CONNECT --> LOOP_START
        LOOP_START --> SEND
        SEND --> RECEIVE
        RECEIVE --> CHECK_PERMISSION
        CHECK_PERMISSION -->|是| SWITCH
        CHECK_PERMISSION -->|否| CHECK_EXIT
        SWITCH --> CHECK_EXIT
        CHECK_EXIT -->|否| LOOP_START
        CHECK_EXIT -->|是| DISCONNECT[斷開連接]
    end

    DISCONNECT --> END([END])

    style CHAT_NODE fill:#e1f5ff
```

### 關鍵設計決策

| 決策點 | 選項 A | 選項 B | 建議 |
|--------|--------|--------|------|
| SDK 選擇 | `query()` 單向 | `ClaudeSDKClient` 雙向 | **B** - 支援多輪對話 |
| 互動式循環位置 | CLI 層 | chat_node 內 | **B** - 符合現有架構 |
| State 擴展 | 新增對話欄位 | 使用 mode_metadata | **A** - 明確定義 |
| 權限策略 | 固定權限 | 動態切換 | **B** - 符合需求 |
| Context 載入 | Prompt 模板 | 組合方式 | **B** - 更靈活 |

## Technical Feasibility

### 方案 A: 最簡實作 (KISS)

**特點**: 互動式對話，初始權限只讀，需要時手動切換

```python
# 新增 chat_node
async def chat_node(state: FirmwareState) -> dict[str, Any]:
    """互動式對話節點"""
    project_root = state.get("project_root")

    # 初始權限: 只讀
    initial_options = ClaudeAgentOptions(
        allowed_tools=["Read", "Grep", "Glob"],
        permission_mode="default",  # 需要確認
        add_dirs=[project_root],
        cwd=project_root,
        system_prompt=_build_chat_system_prompt(state)
    )

    async with ClaudeSDKClient(options=initial_options) as client:
        await client.connect()

        # 互動式循環
        while True:
            user_input = console.input("[cyan]You>[/cyan] ").strip()

            if user_input.lower() in ["exit", "quit"]:
                break

            # 檢查是否需要切換權限
            if _needs_write_permission(user_input):
                console.print("[yellow]切換到寫入模式...[/yellow]")
                await client.set_permission_mode("bypassPermissions")

            # 發送訊息
            await client.query(user_input)

            # 接收回應
            async for msg in client.receive_response():
                _print_message(msg)

        await client.disconnect()

    return {**state, "chat_session_completed": True}
```

**優點**:
- 符合 KISS 原則
- 最小化 State 變更
- 實作簡單直接

**缺點**:
- 權限切換邏輯簡單（基於關鍵字）
- 無對話歷史持久化

### 方案 B: 完整實作

**特點**: 對話歷史管理、智能權限切換、會話恢復

需要擴展:

1. **FirmwareState 新增欄位**:
   ```python
   chat_history: list[dict[str, Any]]
   chat_session_id: str | None
   current_permission_mode: Literal["default", "acceptEdits", "plan", "bypassPermissions"]
   ```

2. **智能權限切換**:
   - 分析使用者輸入內容
   - 預測是否需要修改權限
   - 提示使用者確認

3. **會話持久化**:
   - 儲存對話歷史到 `.fwauto/chat_history/`
   - 支援恢復上次會話

**優點**:
- 功能完整
- 使用者體驗好

**缺點**:
- 複雜度高
- 違反 KISS 原則（第一版）

### 可行性評估

| 技術需求 | 可行性 | 依據 |
|---------|--------|------|
| 多輪對話 | ✅ 高 | SDK 原生支援 `ClaudeSDKClient` |
| 動態權限 | ✅ 高 | SDK 提供 `set_permission_mode()` |
| 互動式輸入 | ✅ 高 | 已有 init 精靈範例 |
| Context 載入 | ✅ 高 | ProjectConfig + PromptManager |
| LangGraph 整合 | ✅ 高 | 新增節點和路由即可 |

## Code References

| 功能 | 檔案路徑 | 行號 | 說明 |
|------|---------|------|------|
| **LangGraph 圖定義** | `src/fwauto/graph.py` | 15-184 | 工作流程結構 |
| **FirmwareState** | `src/fwauto/state.py` | 6-77 | State 定義 |
| **CLI 命令** | `src/fwauto/cli.py` | 65-284 | 命令定義範例 |
| **互動式模式** | `src/fwauto/cli.py` | 287-353 | 互動式循環 |
| **init 精靈** | `src/fwauto/cli_commands/init.py` | 60-257 | Rich 輸入範例 |
| **ai_brain_node** | `src/fwauto/nodes/ai_brain.py` | 24-161 | AI 節點實作 |
| **log_node** | `src/fwauto/nodes/log.py` | 20-210 | Log 節點實作 |
| **錨點搜尋** | `src/fwauto/config.py` | 118-143 | find_project_root() |
| **ProjectConfig** | `src/fwauto/config.py` | 42-116 | 配置載入 |
| **init_project_node** | `src/fwauto/nodes/init.py` | 10-54 | 專案初始化 |
| **PromptManager** | `src/fwauto/prompt_manager.py` | 31-127 | Prompt 模板 |
| **SDK Options** | SDK `types.py` | 501-544 | ClaudeAgentOptions |
| **SDK Client** | SDK `client.py` | 14-229 | ClaudeSDKClient |

## Open Questions

### 1. 對話歷史管理

**問題**: 是否需要保存對話歷史？如果需要，儲存在哪裡？

**選項**:
- A. 不保存，每次都是新會話（KISS）
- B. 保存在記憶體中，程序結束即清空
- C. 持久化到 `.fwauto/chat_history/`，支援會話恢復

### 2. 權限切換觸發機制

**問題**: 如何判斷何時需要切換權限？

**選項**:
- A. 關鍵字偵測（簡單但不精確）
- B. 讓 Claude 自己判斷並請求權限
- C. 使用者手動命令（如 `/write`、`/readonly`）

### 3. 專案 Context 載入時機

**問題**: 何時載入專案相關的 context？

**選項**:
- A. 在 system_prompt 中一次性載入所有資訊
- B. 讓 Claude 主動使用 Read 工具探索
- C. 結合兩者：基本資訊在 prompt，詳細內容靠工具

### 4. FWAUTO.md 的角色

**問題**: 未來的 FWAUTO.md 應該包含什麼內容？

**建議**:
- 專案簡介和目的
- 架構概述
- 開發規範和慣例
- 常見問題和解決方案

類似 CLAUDE.md 的作用，但專注於**專案特定**的內容。

### 5. 退出機制

**問題**: 如何優雅地退出對話？

**選項**:
- A. 輸入 `exit`/`quit`/`q` 退出
- B. Ctrl+C 中斷
- C. 超時自動退出

## Follow-up Research [2025-10-28T17:54:31+08:00]

### Open Questions 決議

經過與使用者逐一討論 5 個 Open Questions，確定以下實作方向：

#### ✅ 1. 對話歷史管理 → 選項 B（記憶體保存）

**決議**: 保存在記憶體中，程序結束即清空

**理由**:
- ✅ ClaudeSDKClient 自動維護會話內的對話歷史
- ✅ 滿足單次會話的 context 需求（例如："剛才那個函數"）
- ✅ 符合 KISS 原則
- ✅ 避免檔案管理複雜度
- ✅ 不會留下敏感資訊在磁碟上

**實作方式**:
```python
async with ClaudeSDKClient(options=options) as client:
    await client.connect()
    # SDK 自動維護對話歷史
    # 斷開連接後歷史清空
    await client.disconnect()
```

#### ✅ 2. 權限切換觸發機制 → 流暢優先，權限全開

**決議**: `permission_mode="bypassPermissions"`，不設限

**理由**:
- ✅ 最簡單：不需要權限切換邏輯
- ✅ 最流暢：Claude 可以直接執行任何操作
- ✅ 信任模型：使用者執行 `fwauto chat` 就是信任 Claude
- ✅ 與現有節點一致（ai_brain_node 和 log_node 都用 bypassPermissions）
- ✅ 未來擴展：真的需要時再加入權限管理（Phase 2）

**簡化效果**:
- ❌ 不需要關鍵字偵測
- ❌ 不需要動態權限切換
- ❌ 不需要手動命令（`/write`、`/readonly`）
- ❌ 不需要擴展 FirmwareState 新增 `current_permission_mode` 欄位

#### ✅ 3. 專案 Context 載入時機 → 選項 C（混合方式）

**決議**: 基本資訊在 system_prompt，詳細內容靠工具

**system_prompt 包含**（輕量級指引）:
```markdown
你正在協助使用者進行 FWAuto 專案開發。

專案資訊：
- 專案根目錄：{project_root}
- 平台：{platform}
- SDK 路徑：{sdk_path}
- Makefile：{makefile_path}

關鍵檔案：
- 專案配置：.fwauto/config.toml
- 專案文檔：FWAUTO.md（如果有的話，請先閱讀）
- 編譯日誌：.fwauto/logs/build.log

當前狀態：
- 編譯狀態：{build_status}
- 上次錯誤：{last_error_summary}（如果有）

你有完整的 Read/Write/Edit/Bash 權限。請根據使用者的問題：
1. 主動探索相關的程式碼和文檔
2. 提供準確的答案和建議
3. 需要時可以直接修改程式碼
```

**優點**:
- ✅ System prompt 簡短（省 token）
- ✅ 資訊即時（每次都是最新的）
- ✅ 靈活（Claude 按需載入）
- ✅ 清楚的指引（Claude 知道關鍵檔案在哪）

#### ✅ 4. FWAUTO.md 的角色 → 選項 3（chat 互動式產生）

**決議**: 第一次執行 `fwauto chat` 時，如果沒有 FWAUTO.md，Claude 可以透過對話幫助建立

**優點**:
- ✅ 不強制使用者在 init 時填寫
- ✅ 自然的時機：使用者開始用 chat 時通常已對專案有了解
- ✅ Claude 可以透過對話收集資訊
- ✅ 符合 KISS：不需要就不產生

**FWAUTO.md 建議內容**:
- 專案概述（產品名稱、用途、版本）
- 硬體資訊（MCU 型號、記憶體、週邊）
- 架構設計（程式碼結構、模組關係）
- 開發規範（命名、風格、註解）
- 關鍵配置（中斷、時鐘、DMA）
- 常見任務（如何新增功能）
- 已知問題與 workaround
- 測試指引

#### ✅ 5. 退出機制 → 選項 A + B（兩種都支援）

**決議**: 支援文字命令 + Ctrl+C

**實作方式**:
```python
import signal

try:
    while True:
        user_input = console.input("[cyan]You>[/cyan] ").strip()

        # 選項 A: 文字命令退出
        if user_input.lower() in ["exit", "quit", "q"]:
            console.print("[yellow]👋 結束對話[/yellow]")
            break

        # ... 發送訊息並接收回應

except KeyboardInterrupt:
    # 選項 B: Ctrl+C 退出
    console.print("\n[yellow]👋 對話已中斷[/yellow]")

finally:
    # 確保正確斷開連接
    await client.disconnect()
```

**優點**:
- ✅ 正常退出：輸入 `exit` → 優雅關閉
- ✅ 緊急退出：Ctrl+C → 立即中斷
- ✅ 都能正確清理資源（`finally` 確保 disconnect）

---

### 實作方向總結

基於以上決議，**最簡化的實作方案（KISS）**：

| 項目 | 實作方式 | 複雜度 |
|------|---------|--------|
| 對話歷史 | SDK 自動管理，記憶體中 | ⭐ 最簡單 |
| 權限管理 | bypassPermissions 全開 | ⭐ 最簡單 |
| Context 載入 | 輕量 prompt + 工具探索 | ⭐⭐ 簡單 |
| FWAUTO.md | 互動式產生（選用） | ⭐⭐ 簡單 |
| 退出機制 | exit 命令 + Ctrl+C | ⭐ 最簡單 |

**不需要擴展 FirmwareState**：
- ❌ 不需要 `chat_history` 欄位（SDK 管理）
- ❌ 不需要 `chat_session_id` 欄位（不持久化）
- ❌ 不需要 `current_permission_mode` 欄位（固定 bypass）

**核心實作**:
1. 新增 `chat_node` 使用 `ClaudeSDKClient`
2. 新增 `fwauto chat` CLI 命令
3. 新增路由邏輯支援 `execution_mode="chat"`
4. 建立 `chat.md` prompt 模板（輕量級）

---

## Related Research

- 尚無相關研究，這是 FWAuto chat 功能的首次研究

## 後續建議

**下一步行動**:

1. **執行 `/prpi:plan`** - 建立詳細的實施計劃
   - 基於以上決議設計最簡化方案
   - 明確實作步驟和檔案變更
   - 定義驗收標準

2. **Phase 1 實作範圍**（最小可工作版本）:
   - ✅ 基礎互動對話
   - ✅ 權限全開（bypassPermissions）
   - ✅ 記憶體內對話歷史
   - ✅ 輕量級 system prompt
   - ✅ 退出機制（exit + Ctrl+C）

3. **未來擴展（Phase 2+）**（按需求決定）:
   - 對話歷史持久化
   - 智能權限切換
   - 會話恢復功能
   - FWAUTO.md 增強功能
