# Loon Plugin

> Use when writing, reviewing, or debugging Loon (iOS/tvOS 网络工具) plugins — 插件元信息、[Argument] 参数、Rewrite 复写规则（新版 if/then 语法与旧版语法）、脚本（http-request/http-response/cron/network-changed/generic）、Script API、分流规则与 MitM。Triggers on 关键词：Loon 插件、Loon plugin、.plugin 文件、复写、Rewrite、http-response 脚本、$persistentStore、$httpClient、$argument、reject_dict、MitM hostname。

- Skill: `cikichen/loon-plugin` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add cikichen/loon-plugin`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cikichen/loon-plugin/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: MIT
- Author: cikichen (https://skillmd.com/u/cikichen)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cikichen/loon-plugin

---


# Loon 插件开发技能

## 何时使用

- 编写或修改 Loon 插件（`.plugin` 文件）
- 编写 Loon 复写（Rewrite）规则，尤其是新版 `if ... then ...` 语法
- 编写 Loon 脚本（去广告、签到、解锁、定时任务）
- 排查插件不生效、参数读不到、Rewrite 报错、MitM 未解密
- 把旧语法插件迁移到新语法

## 核心原则

1. **先确认目标 Loon 版本**。本技能覆盖的多数能力有明确的版本门槛，写错 `#!loon_version` 会导致用户端直接不可用。
2. **优先使用新版 Rewrite 语法**。旧语法仅作输入兼容，Loon 的生成、保存和配置展示统一输出新语法。
3. **不要凭记忆写 Action 名和参数顺序**。Action 使用严格的位置参数，写错会在加载时被拒绝整条规则。动手前查 `references/rewrite-v2.md`。
4. **HTTPS 必须配 MitM**。Rewrite 和 http 脚本只对 HTTP 以及经 MitM 解密的 HTTPS 生效，漏写 `[Mitm] hostname` 是最常见的「不生效」原因。

## 插件骨架

```
#!name = 示例插件
#!desc = 展示插件信息和用户参数
#!author = Loon
#!homepage = https://example.com
#!icon = https://example.com/icon.png
#!system = iOS,iPadOS,tvOS,macOS
#!system_version = 15
#!loon_version = 3.5.1(978)
#!tag = 示例,工具
#!type = normal

[Argument]
name = input,"Loon",tag=名称,desc=输入一个名称
region = select,"CN","US","JP",tag=地区
enabled = switch,true,tag=启用

[General]
bypass-tun =
skip-proxy =
real-ip =
dns-server =

[Rule]

[Rewrite]

[Host]

[Script]
http-response ^https?:\/\/example\.com\/conf\/server-mapping script-path=remove_ads.js,requires-body=true,tag=移除广告,argument=[{name},{region},{enabled}]

[Mitm]
hostname = example.com
```

模块都是可选的，按需保留。写插件时不要保留空的 `[General]` 占位键（`bypass-tun =` 等），会覆盖用户的通用配置。

## 元信息字段

| 字段 | 说明 |
|---|---|
| `#!name` | 插件名称 |
| `#!desc` | 功能说明 |
| `#!author` | 作者 |
| `#!homepage` | 主页地址 |
| `#!icon` | 图标地址 |
| `#!system` | 支持的系统，不区分大小写；未填写表示全部支持 |
| `#!system_version` | 最低系统版本，如 `15.0` |
| `#!loon_version` | 最低 Loon 版本，如 `3.5.1(978)` |
| `#!tag` | 分类标签 |
| `#!type` | 插件类型 |

`#!type` 从 Loon 3.5.0 (969) 起支持：

- `normal`：普通插件
- `parser`：资源解析器，可在节点、规则和配置订阅页面中选择

## [Argument] 用户参数

需要 Build 733+。Loon 会据此自动生成设置界面。

```
参数名 = 控件类型,默认值或可选值,tag=标题,desc=说明
```

| 控件 | 说明 | 支持类型 | 默认类型 |
|---|---|---|---|
| `input` | 文本输入；默认值可省略 | String、Number | String |
| `select` | 单选列表；第一个值为默认值 | String、Number | String |
| `switch` | 开关；默认值为 `false` | Boolean | Boolean |

需要数字类型时显式声明 `type=number`：

```
price = input,9.99,type=number,tag=价格
level = select,1,2,3,type=number,tag=等级
```

未声明 `type` 的旧插件保持原行为（`input`/`select` 按 String，`switch` 按 Boolean）。

### 引用语法在两处不同 —— 最高频错误

| 使用位置 | 写法 | 示例 |
|---|---|---|
| `[Script]` 的 `argument` | `{参数名}` | `argument=[{name},{region}]` |
| `[Script]` 的其他字段 | `{参数名}` | `enable={enabled}`、`cron {cronExpression}` |
| 新版 `[Rewrite]` | `${参数名}` | `${region}`、`${price}` |

脚本内通过 `$argument.name` 读取。`switch` 参数可直接控制脚本开关：`enable={enabled}`。

本地 Rewrite 没有 `[Argument]` 来源，本地编辑页只能用内置变量和当前 Rewrite 的正则捕获。

## 插件内规则的策略限制 —— 第二高频错误

插件 `[Rule]` 里**只能**使用这三类策略：

- `DIRECT`
- `REJECT` 系列
- `PROXY`

不能直接写用户的策略组名。`PROXY` 表示交由用户选择策略组。规则未指定策略时默认 `DIRECT`。

## Rewrite 速览

新版语法（Loon 3.5.1 (978) 起）：

```
<阶段> if <条件> then <Action>[ | <Action> ...]
```

```
request  if ${url} ~= /^https:\/\/api\.example\.com/ then request.header.set("X-Loon", "true")
response if ${url} ~= /^https:\/\/api\.example\.com\/profile$/ && ${response.status} == 200 then response.json.replace("data.vip", true)
```

- 阶段只有 `request` 和 `response`，一条规则不能混用请求 Action 和响应 Action（`response.body.mock` 是特例）。
- 比较用 `==`（精确）和 `~=`（正则查找，需完整匹配时自行加 `^` `$`）。
- 多个 Action 用 `|` 连接，从左到右执行。
- 内置变量：`${url}`、`${request.method}`、`${request.header['name']}`、`${response.status}`、`${response.header['name']}`。
- **`if` 条件中读不到 Body**，`${request.body}` / `${response.body}` / JSON Key Path 均不支持。

完整的 Action 清单、参数顺序、批量数组参数、正则捕获（`as`）、Mock 限制、旧语法迁移表见 `references/rewrite-v2.md`。旧语法维护见 `references/rewrite-legacy.md`。

## 脚本速览

| 类型 | 触发时机 |
|---|---|
| `http-request` | 请求发出前 |
| `http-response` | 收到响应后 |
| `cron` | 按 Cron 表达式定时 |
| `network-changed` | 网络环境变化（配置多条只执行第一条） |
| `generic` | App 内手动触发 |

```
http-response ^https?:\/\/example\.com script-path=response.js,requires-body=true,tag=响应脚本,timeout=10,argument="name=loon",enable=true
cron "0 8 * * *" script-path=cron.js,tag=定时任务,timeout=300
```

要读 Body 必须显式加 `requires-body=true`，否则 `$request.body` / `$response.body` 为空。脚本结束务必调用 `$done()`。

脚本类型详细参数与 `$done()` 返回格式见 `references/script-types.md`，API 清单见 `references/script-api.md`。

## 排查清单

不生效时按顺序检查：

1. **HTTPS 是否配了 MitM** —— `[Mitm] hostname = example.com`，且用户已安装并信任证书。
2. **URL 正则是否真的匹配** —— 插件里的正则需转义 `/`，如 `^https?:\/\/example\.com`。
3. **是否漏了 `requires-body=true`** —— 需要读写 Body 的脚本必须显式声明。
4. **策略是否越界** —— 插件规则里写了用户策略组名，改成 `PROXY`。
5. **参数引用语法是否用错** —— Script 用 `{name}`，新版 Rewrite 用 `${name}`。
6. **版本门槛是否够** —— `[Argument]` 需 733+，新版 Rewrite 需 3.5.1(978)+，`#!type` 需 3.5.0(969)+。
7. **Action 参数顺序与类型** —— 位置参数不能写参数名，可选参数只能从右往左省略。
8. **来源优先级** —— 本地配置 > 插件 > 订阅。本地同类配置会覆盖插件。

Loon 加载配置时会做静态检查，错误信息带行号和原因，优先看它：

```
Rewrite 第 18 行：未定义的参数 ${price2}
Rewrite 第 21 行：正则 item 只有 2 个捕获组，不能引用 ${item.3}
Rewrite 第 25 行：request 阶段不能引用 ${response.status}
```

## 交付约定

产出插件时：

- 明确标注 `#!loon_version`，取所用特性中的最高门槛。
- 用到 HTTPS 拦截就必须给出 `[Mitm] hostname`。
- 参数化的插件要在 `[Argument]` 中写清 `tag` 和 `desc`，这是用户唯一能看到的说明。
- 说明脚本需要联网请求哪些域名，便于用户判断隐私影响。

## 按需阅读

- 新版 Rewrite 完整语法：`references/rewrite-v2.md`
- 旧版 Rewrite 语法与迁移：`references/rewrite-legacy.md`
- 脚本类型与 `$done()` 格式：`references/script-types.md`
- Script API 清单：`references/script-api.md`
- 分流规则类型：`references/rules.md`
- 可运行示例插件：`references/examples.md`

## 来源

内容整理自 Loon 官方文档 <https://nsloon.app/docs/intro>，抓取日期 2026-08-04，对应 Loon 3.5.1 (978) 前后的文档状态。Loon 更新后请以官网为准。

