---
url: /wave-agent/specs/ui/file-path-links.md
description: 助手文本 markdown 中文件路径的识别与点击打开：行内代码、正文纯文本、围栏代码块与 bash 输出多通道，仅识别绝对路径，支持行号定位与跨端打开
---

# 功能规格说明：消息文本中的文件路径点击识别

**创建日期**：2026-09-02

> 本规格覆盖 VS Code / JetBrains / Desktop 共享 webview（`packages/webview`）中助手文本 markdown 渲染链路（`Message.tsx` 的 marked 渲染）对文件路径的识别与点击行为，对齐 Claude AI（Epitaxy/Code 会话）的效果。点击行为对齐 wave-agent 既有的 openFile 通道（工具标题路径点击同一条链路），不新增宿主 RPC。各端打开能力见 [桌面端文件面板](../desktop/desktop-file-panel.md) 与 [插件端](../editor/plugin-host.md)，此处不重复。

## 背景：三条识别通道

Claude AI 的实现把消息文本中的文件路径识别限定在行内代码与正文两条通道，wave-agent 对齐该行为，并额外覆盖助手的围栏代码块与 bash 工具输出（agent 常用代码块列产物清单、命令输出里直接打印路径，见对应用户故事）：

* **行内代码通道**：反引号整串。识别要求 ≥1 斜杠 + 点扩展名（`/etc/nginx/nginx.conf`、`/home/u/repo/src/foo.ts`、`C:\proj\x.ts` 等），支持 `:N`（单行）与 `:N-M`（行区间）行号后缀。
* **正文纯文本通道**：仅识别绝对路径形式（POSIX `/…`、`~/…`、Windows `C:\…`、`file:///…`），且要求点扩展名（`/home/u/repo/src/index.ts` 可点击，`/etc/hosts`、`/var/log/syslog` 这类无扩展名路径不识别）。
* **围栏代码块通道**：代码块内按空白分词，逐 token 采用行内代码通道的规则（≥1 斜杠 + 点扩展名、支持 `:N` / `:N-M`），token 首尾的引号与标点不进入路径。mermaid 代码块（渲染为图表）不在其中。
* **bash 输出通道**：bash 工具块的输出区（`.bash-command-output`）是终端风格纯文本，不走 markdown；其中按空白分词，逐 token 采用与围栏代码块相同的规则。该区域同时保留既有的裸 URL 链接化（两者互不干扰，见 [markdown 链接](markdown-links.md)）。

**相对路径（`src/main.ts`、`./x.ts`、`../x.ts`）四条通道一律不识别**，保持普通文本/代码原文，与 Claude AI `barePath`（要求 `/`、`~/`、盘符开头）一致；裸文件名（`Report.md`、`index.ts`）同样不识别。

## 用户场景与测试 *（必填）*

### 用户故事：行内代码中的文件路径可点击并定位行号（优先级：P1）

作为用户，我希望助手回复中以行内代码（反引号）展示的文件路径（含 `:N` / `:N-M` 行号后缀）可以点击，点击后打开该文件并定位到对应行，以便快速从对话跳转到 agent 提到的源码位置。

**为什么是这个优先级**：agent 在文本中引用文件时高频使用反引号包裹路径（含行号），当前这类路径渲染为纯文本，用户只能手动复制后在文件树里逐层导航。

**独立测试**：渲染助手 markdown 文本，包含 `` `/home/u/repo/src/services/payment.ts:28` ``、`` `/home/u/repo/config.json` `` 等行内代码路径，以及含中文/非 ASCII 字符的路径（`` `C:\Users\张三\报表.xlsx` ``、`` `C:\proj\src\我的组件.tsx` ``），验证其保留代码样式、同时渲染为可点击元素，点击后发出 openFile 打开请求并携带解析出的行号；渲染 `` `src/main.ts` ``、`` `./src/a.ts` ``、`` `../x/y.ts` `` 等相对路径，验证保持纯代码样式、不可点击。

**验收场景**：

1. **假设** 助手文本含行内代码 `` `/home/u/repo/src/utils/format.ts:12-24` ``，**当** 渲染时，**则** 整串渲染为可点击元素并保持代码样式；点击后以 startLine=12、endLine=24 打开（desktop 在文件面板打开并高亮该行区间，IDE 在编辑器打开并选中该区间）。
2. **假设** 行内代码仅含单个行号后缀 `` `/home/u/repo/src/format.ts:9` ``，**当** 渲染时，**则** 可点击，点击打开后定位到第 9 行（单行高亮/光标定位）。
3. **假设** 行内代码为相对路径（`` `src/main.ts` ``、`` `./src/a.ts` ``、`` `../sibling-repo/src/b.ts` ``、`` `../../x/y.ts` ``），**当** 渲染时，**则** 保持纯代码样式、不可点击——四条通道一律只识别绝对路径（与 Claude AI `barePath` 一致）。
4. **假设** 行内代码为含中文/非 ASCII 字符的文件路径（Windows `` `C:\Users\张三\下载\报表.xlsx` ``、POSIX `` `/home/张三/项目/笔记.md` ``、盘符绝对 `` `C:\proj\src\我的组件.tsx` ``），**当** 渲染时，**则** 同样识别为可点击路径，点击按该绝对路径打开。
5. **假设** 行内代码不是文件路径（`` `var x = 1` ``、`` `www.example.com` `` 无斜杠、`` `/foo` `` 无扩展名、`` `a.ts` `` 无斜杠、含空格的 `` `node src/a.ts` ``、整串为中文句子的 `` `请把报表放在这里` ``），**当** 渲染时，**则** 保持纯代码样式，不可点击。
6. **假设** 行内代码内容为 http(s) 链接（`` `https://example.com/a/b` ``），**当** 渲染时，**则** 按既有 URL 提升规则生成链接，不被误判为文件路径。
7. **假设** 行内代码整体是 markdown 链接语法原文或含多个单词，**当** 渲染时，**则** 保持代码原文，不提取其中片段生成路径链接。

***

### 用户故事：正文纯文本中的绝对文件路径可点击（优先级：P1）

作为用户，我希望助手回复正文（非代码样式）中直接书写的绝对文件路径可以点击打开，以便常见引用形式（`/home/u/repo/src/index.ts`、`C:\proj\src\a.ts` 等）不必复制粘贴。

**为什么是这个优先级**：agent 叙述性文本常直接给出绝对路径（如「参考 `/home/u/repo/src/index.ts` 的实现」）；正文中识别范围刻意收窄到「绝对形式 + 带点扩展名」，与 Claude AI `barePath` 口径一致，避免把普通斜杠文本或接口路径误判为文件路径。

**独立测试**：渲染含裸绝对路径（POSIX、Windows 盘符、`file:///`，含中文/非 ASCII 文件名的路径）的助手 markdown 正文，验证路径渲染为可点击元素、相邻文本与标点保持原样；渲染含相对路径、`~/` 形式、无扩展名绝对路径（`/etc/hosts`）、裸文件名与整段中文散文的正文，验证不生成可点击元素。

**验收场景**：

1. **假设** 正文含 `参见 /home/u/repo/src/index.ts 的实现`，**当** 渲染时，**则** 绝对路径渲染为可点击元素，前后文本保持原样；点击以该绝对路径打开。
2. **假设** 正文含 Windows `C:\proj\src\a.ts` 或 `file:///home/u/a.ts`，**当** 渲染时，**则** 同样渲染为可点击元素，点击按对应绝对路径打开。
3. **假设** 正文含相对路径（`src/main.ts`、`./src/a.ts`）、`~/dev/proj/tsconfig.json`（见边界情况：wave 无宿主 home 信息）或裸文件名（`index.ts`），**当** 渲染时，**则** 保持普通文本，不可点击。
4. **假设** 正文含无点扩展名的绝对路径（`/etc/hosts`、`/var/log/syslog`、`/usr/local/bin`、接口路径 `/api/v1/users`），**当** 渲染时，**则** 保持普通文本，不可点击——正文通道要求点扩展名，与 Claude AI `barePath` 一致。
5. **假设** 正文含中文/非 ASCII 文件名的绝对路径（Windows `C:\Users\张三\下载\报表.xlsx`、POSIX `/home/张三/项目/笔记.md`），**当** 渲染时，**则** 同样渲染为可点击元素，点击以该绝对路径打开；整段为中文散文（如 `请把报表放在这里`）不生成任何链接。
6. **假设** 正文路径后紧跟标点（逗号、句号、中文标点、括号注释），**当** 渲染时，**则** 路径部分可点击，标点不进入链接目标且保持可见。
7. **假设** 路径出现在**加粗/斜体文本内部或标题中**（如 `**请改 /tmp/bug.ts**`），**当** 渲染时，**则** 路径同样可点击。
8. **假设** 路径出现在 markdown 链接的显示文本内（如 `[说明 /tmp/bug.ts](https://x)`），**当** 渲染时，**则** 不生成嵌套路径链接——保持为普通链接，避免无效 HTML 嵌套。
9. **假设** 正文中的 Windows 路径含有「反斜杠 + 标点」的目录/文件名（`…\wave-agent\.wave\…\x.webp`、`…\my_dir\_spec\a.ts`、`…\a&b\c.ts` 等，标点会被 markdown 当作转义序列），**当** 渲染时，**则** 整串（含每个反斜杠）渲染为一个可点击元素——反斜杠原样显示、链接不截止在标点处；点击打开的是完整路径。见边界情况「markdown 反斜杠转义」。

***

### 用户故事：围栏代码块中的文件路径可点击（优先级：P1）

作为用户，我希望助手在围栏代码块（\`\`\`）中列出的文件路径（产物/截图清单、文件列表、代码片段里的模块路径等）也可以点击打开，以便不必从代码块里手动复制路径再到文件树或编辑器里导航。

**为什么是这个优先级**：agent 用代码块罗列产物路径与文件清单同样高频（多行纯路径列表尤其常见）；此前这类路径完全不可点击，用户只能手工复制。

**独立测试**：渲染含多行路径清单、代码行内路径（带引号）、带行号后缀路径以及纯代码行的围栏代码块，验证路径部分渲染为可点击元素且换行/缩进/引号等原样保留；渲染含相对路径的代码块，验证保持代码原文；渲染 mermaid 代码块，验证仍为图表且不做路径识别。

**验收场景**：

1. **假设** 代码块内每行各是一个路径（`C:\Users\…\a.webp` 与 `/home/u/repo/docs/public/screenshots/x.webp`），**当** 渲染时，**则** 每行的路径分别渲染为可点击元素，换行与缩进保持原样；点击按该绝对路径打开。
2. **假设** 路径出现在代码块的代码行内（`const p = "/home/u/proj/src/utils/a.ts";`、`git diff -- /home/u/proj/packages/webview/src/a.ts`），**当** 渲染时，**则** 其中的路径部分可点击，引号与其余代码文本保持原样、不进入路径。
3. **假设** 代码块内路径带 `:N` / `:N-M` 行号后缀（`/home/u/proj/src/utils/format.ts:12-24`），**当** 渲染时，**则** 与行内代码通道一致地解析行号，点击后定位到对应行。
4. **假设** 代码块内容为 mermaid 图表（\`\`\`mermaid），**当** 渲染时，**则** 仍渲染为图表，路径识别不介入。
5. **假设** 代码块内容不含路径（`npm run build`、`var x = 1`），**当** 渲染时，**则** 保持原文，不生成路径链接；其中的裸 http(s) URL 由 [markdown 链接](markdown-links.md) 通道提升为链接（用户 2026-09-21 口径），与路径识别互不干扰。
6. **假设** 代码块内是相对路径（`src/utils/a.ts`、`./dist/a.js`），**当** 渲染时，**则** 保持代码原文、不可点击——与行内代码通道同规则，只识别绝对路径。

***

### 用户故事：bash 命令输出中的文件路径可点击（优先级：P1）

作为用户，我希望 bash 工具输出（编译/构建日志、`git status`、测试报错、`ls` 长格式等）里打印出来的文件路径也能点击打开，以便从「输出里指出的那个文件」直接跳到文件面板或编辑器，不必手动复制粘贴。

**为什么是这个优先级**：agent 的很多结论都来自命令输出（报错文件与行号、产物清单、改动列表），而输出区此前只有裸 URL 可点击、路径一律为纯文本，是最容易产生「复制粘贴」摩擦的高频位置。

**独立测试**：渲染带输出的 bash 工具块（含 Windows/POSIX 绝对路径、相对路径、带引号与行号后缀的路径、纯日志行、含 URL 的行），验证路径部分渲染为可点击元素、引号与标点不进路径、其余文本（含换行）原样保留、URL 仍按既有规则链接化；渲染仅含相对路径的输出，验证保持纯文本。

**验收场景**：

1. **假设** bash 输出含绝对路径（`C:\Users\u\proj\dist\a.js`、`/home/u/repo/src/index.ts`），**当** 渲染时，**则** 路径渲染为可点击元素，点击按该绝对路径打开；输出中其余文本与换行保持原样。
2. **假设** bash 输出含以绝对路径指出的报错（`error in /home/u/proj/src/utils/a.ts`、`/home/u/proj/src/utils/format.ts:12-24`），**当** 渲染时，**则** 路径可点击；带 `:N` / `:N-M` 后缀时与行内代码通道一致地定位行号。
3. **假设** 路径在输出中被引号或标点包裹（`error at "/home/u/a.ts";`、`(见 /home/u/docs/x.md)`），**当** 渲染时，**则** 仅路径部分可点击，引号/括号/分号留在链接外且保持可见。
4. **假设** 同一行输出里既有 URL 又有路径（`see https://example.com/docs or /home/u/a.ts`），**当** 渲染时，**则** URL 按既有规则生成链接（含 `localhost` 在桌面端进预览面板）、路径生成路径链接，两者互不吞并。
5. **假设** 输出不含路径形态的文本（`npm run build`、`error: command not found`、`var x = 1`、无扩展名的 `/usr/local/bin`），**当** 渲染时，**则** 保持纯文本，不生成任何可点击元素。
6. **假设** 输出只含相对路径（`error in src/utils/a.ts`、`src/utils/format.ts:12-24`），**当** 渲染时，**则** 保持纯文本、不可点击——与围栏代码块同规则，只识别绝对路径。

***

### 用户故事：文件路径点击行为与无能力回退（优先级：P1）

作为用户，我希望点击路径的打开行为与工具标题路径一致（desktop 文件面板 / IDE 编辑器），且路径不可解析时优雅回退为纯文本，不做无响应或误导的「假链接」。

**为什么是这个优先级**：点击行为是跨端一致性的一部分；不可解析的路径若渲染成可点击会破坏信任（dead link）。

**独立测试**：分别以 desktop（传 onOpenFile）与 IDE（仅 postMessage）两种宿主形态点击同一路径链接，验证发往宿主的 openFile 消息一致；以含 `.` / `..` 段的绝对路径渲染消息，验证发往宿主的 path 已折叠为规范绝对路径。

**验收场景**：

1. **假设** 点击任意路径链接，**当** 点击发生时，**则** 桌面宿主（waveHostType=desktop）经 pane 路由的 onOpenFile 打开文件面板；插件端发出 `openFile`（path/startLine/endLine）RPC 由插件在编辑器中打开——与 Read/Write 工具标题路径点击同一条通道。
2. **假设** 候选路径是 `~/` 形式，**当** 渲染时，**则** 不生成链接（宿主 openFile 按 OS 绝对路径解析、webview 无宿主用户主目录可展开），保持普通文本——见边界情况「`~/` 形式」。
3. **假设** `file:///` 形式路径被点击，**当** 打开时，**则** 先剥离 `file://` 前缀还原为普通绝对路径再打开。
4. **假设** 键盘用户聚焦到路径链接并按下 Enter，**当** 触发时，**则** 与鼠标点击行为一致地打开文件。
5. **假设** 点击的是含 `.` / `..` 段的绝对路径（`/home/u/proj/../x/a.ts`），**当** 打开请求发往宿主，**则** 其中的 `path` 是**规范化后的绝对路径**：`.` 与空段被丢弃、`..` 与前一段抵消、逃出根（栈空）的 `..` 被丢弃。归一化是**纯词法**的（不解析符号链接、不访问文件系统），保证发往宿主的路径形态稳定，不残留 `.` / `..` 段。

***

## 边界情况

* **围栏代码块里的路径怎么办？** 按行内代码通道的规则逐 token 识别（空白分词、要求绝对路径 + 点扩展名、支持行号后缀），换行/缩进/引号等代码原文不受影响；mermaid 代码块渲染为图表，不参与识别（代码块内的裸 URL 按 [markdown 链接](markdown-links.md) 的规则同样提升为链接，与路径识别互不干扰）。
* **bash 输出 / 工具结果（`.result-raw`、`.bash-command-output` 等）里的路径怎么办？** bash 工具块的输出区（`.bash-command-output`）按围栏代码块的同一套规则识别（空白分词 + 要求绝对路径 + 需点扩展名 + 行号后缀，见对应用户故事），并保留该区域既有的裸 URL 链接化；其他工具的原始结果区（如 `.result-raw`）仍保持纯文本，不在本次范围。
* **为什么四条通道都要求点扩展名？** 与 Claude AI `barePath` 口径一致：路径必须带 `.\w` 形式的扩展名。这条同时挡掉命令输出与散文里大量非文件串（接口路径 `/api/v1/users`、日志里的 `/usr/local/bin`、目录路径 `dist/`），代价是无扩展名配置文件（`/etc/hosts`、`/var/log/syslog`）不可点击。
* **为什么四条通道都不识别相对路径？** 与 Claude AI `barePath`（要求 `/`、`~/`、盘符开头）一致。相对路径要靠 workdir 归并才能变成可打开的绝对路径，归并错一次就是打开错的文件；而 agent 在正文与代码块里引用文件时给绝对路径的成本很低。代价是构建日志里的相对报错路径（`src/utils/a.ts`）不可点击。
* **URL 与路径的歧义？** http(s) 链接优先按 URL 规则处理（含行内代码中的裸 URL 提升、正文自动链接）；`file:///` 走文件路径通道。端口形式（`http://x:8080/a`）属于 URL，不会被误判为路径行号。
* **无扩展名绝对路径？** 四条通道统一要求「≥1 斜杠 + 点扩展名」：`` `/foo` ``、`/etc/hosts`、`/var/log/syslog`、`/usr/local/bin` 均不识别。正文通道另要求 POSIX 绝对路径至少含两级目录（`/a.ts` 这类一级路径不识别），比 Claude AI 更严一档，用于规避散文里的斜杠片段。
* **行号后缀在哪些通道识别？** 行内代码、围栏代码块、bash 输出三通道识别 `:N` / `:N-M`；正文纯文本通道只识别路径本身，紧邻的 `:12` 类文本保持普通文本（不吞入路径，避免把散文中的冒号当行号）。
* **`~/` 形式怎么办？** 按 Claude AI 行为纳入「绝对路径」候选，但 wave 架构下**不生成链接、保持普通文本**（openFile 宿主通道把 path 当 OS 绝对路径——vscode `Uri.file` / desktop fs 读取 / JB 编辑器均无 `~` 展开；webview 侧无宿主 home 信息，本地会话或许可推、远程 pane 一定不可靠）。正文与行内代码两通道中的 `~/…` 候选均落入「无 opener 上下文 → 纯文本回退」分支。待宿主侧支持 `~` 展开（仅本地会话可展开）后可单点开放，不属于本次范围。
* **路径里的 `.` / `..` 段怎么处理？** 绝对路径本身可能含 `.` / `..` 段（`/home/u/proj/../x/a.ts`），点击时做**纯词法折叠**（段栈：空段与 `.` 丢弃、`..` 弹出上一段、栈空时的 `..` 丢弃），产出规范绝对路径再发往宿主。不做 `realpath`：符号链接目录下的 `..` 按字面折叠而非 OS 视角解析（与 claude.ai「Code」会话的浏览器侧归一化一致，避免为路径点击引入文件系统往返）；Windows 盘符路径保留盘符前缀（`C:/a/../b` → `C:/b`）。不折叠时宿主 `fs.stat` 虽靠内核解析 `..` 也能读到文件，但面板标题与自动刷新比对本就依赖同一串字符串，残留 `..` 会造成标题显示怪路径与刷新失配——归一化同时消除这两处。
* **会打开 workdir 之外的文件吗？** 会。四条通道只识别绝对路径，识别层没有「必须在会话根目录内」的限制：agent 引用同机其他目录（兄弟仓库、`/home` 下的配置）是合法高频场景，能读到就打开。若后续需要收紧，应在**读边界**（宿主读取前）加校验并回填规范化路径，而不是在点击识别处静默降级为纯文本。
* **行号后缀与冒号的歧义？** 仅当 `:数字`（或 `:数字-数字`）紧贴路径末尾且数字与路径间无空白时识别为行号；`:N:M`（offset:limit 双冒号）不是本功能识别的行区间格式（wave Read 工具标题的 `:offset:limit` 展示由工具自己的点击路径处理）。
* **路径含中文/非 ASCII 字符怎么办？** 识别（Windows 中文用户名/下载目录、中文文件名是常见真实场景，如 `C:\Users\张三\Downloads\报表.html`、`/home/李四/项目/笔记.md`）。路径段字符集除 ASCII 字母数字与 `._~+@%-` 外允许 CJK 等非 ASCII 字符，但仍排除空白、ASCII/中文标点与括号（防把散文误判为路径）。中文纯文本段落（无斜杠/盘符形态）天然不匹配路径形态，不会误链。
* **文件名含空格怎么办？** 不识别（路径 token 不含空白），符合 Claude AI 行为。
* **markdown 反斜杠转义与 Windows 路径冲突怎么办？** Windows 路径里的 `\` 天然是 markdown 的转义前缀，`…\.wave`、`…\my_dir`、`…\#a` 这类会被当成转义序列处理：显示文本丢掉反斜杠（`…wave-agent.wave\…`），渲染管线还会按转义点把文本切段、使可点击范围截止在标点处。因此盘符开头的路径串在 markdown 转义处理**之前**整体作为一条文本识别（不参与转义解析），链接覆盖整串、反斜杠原样显示；对不以盘符开头的普通文本，转义行为不变（`\*`、`\_`、`\|` 等仍是转义）。行内代码与围栏代码块内的反斜杠本就不参与转义，不受影响。
* **HTML 实体/特殊字符？** 路径经 markdown 转义与渲染管线，非链接文本完整 HTML 转义；识别与注入在转义后的安全边界内进行，无注入风险；含 `&`、引号等字符的路径按原始字符解析（见安全）。
* **安全如何保证？** 生成的可点击元素仅使用既有白名单标签与属性（`a` + `class`/`href`，href 固定为空锚点），不启用 data-\* 或 span；路径文本渲染前转义，渲染结果仍经 DOMPurify 过滤；不产生任何可执行协议。点击仅触发既有 openFile RPC（路径字符串），宿主侧已有二进制/缺失文件错误处理。
* **用户消息文本怎么办？** 不处理——用户消息走 mention/选择区解析（parseMentions），不在本规格范围；本功能只作用于助手 markdown 渲染路径（含 reasoning 文本，共用同一渲染函数自然继承）。
* **消息正在流式输出？** 与 URL 提升一致，流式段落每次增量重渲染按同样规则处理，路径在完整成串后变为可点击。
