---
name: meegle-plugin-feature
version: 1.0.0
description: |
  Meegle 插件功能迭代（存量插件）：在已有插件工程上加 / 改一个 feature——点位配置变更（增/改/删）+ 它需要的代码。按意图只跑需要的 stage：Stage Config（点位声明）/ Stage Code（前端 React）/ 该 feature 需要后端那一半时在末尾产交接包甩给后端会话。**入口 = 插件工程目录。**

  **触发条件（路由层匹配，两条同时成立）**：
  1. **业务域信号**：用户话术含"飞书项目 / 飞项 / Meegle / Meego / lpm / webhook / OpenAPI 数据"任一，或当前会话已在 Meegle 插件工程上下文中
  2. **迭代意图**：用户想**加 / 做 / 改 / 实现**任何**工作项 UI 元素**（按钮、Tab / 标签页、导航入口、配置页、看板视图、日历视图、看板卡片 / 统计卡片 / 筛选器组件、字段类型、字段样式控件、表格列控件、详情页区块、排期 / 日期选择器等）或它要消费的平台数据；或"改现有点位展示逻辑 / 重新生成代码"；
     也含**咨询 / 规划口吻**的迭代意图——"理一下流程"、"怎么配 / 怎么写 / 怎么实现"、"用哪些接口"、"什么时候调"、"能加吗 / 能不能开"、"怎么开通"、"帮我看下"、"我想搞清楚 xxx 怎么落地"等——这些落到本仓上下文后就是"实现"路径，应触发本 skill 走 lpm 工具链而非走通用文档查询。

  > 本 skill 仅适用于已有插件工程；被调起后会先用 `plugin.config.json` 守卫验证 CWD，若不在插件工程内会自动引导到 workflow phase（新插件全流程）。
  > "只迭代某点位的**服务端那一半**" → 走 feature 的 stage=backend 产交接包（本 skill 检测到 feature 需要后端那一半时也会自动走这一步）；"你正坐在自己的后端仓里" → 由 Step 1.2 的 `external-backend` 分流：只写 / 改 handler 代码进 `meegle-plugin-backend` skill，要配 / 改点位配置走 feature `stage=config`（改点位即改数据，必经 Stage Config 护栏）。
metadata:
  requires:
    bins: ["npx"]
  cliHelp: "lpm --help"
---

# Meegle 插件功能迭代 Skill

> **前置**：先 Read [`shared.md`](shared.md) 获取共享规则；进入每个 step 前 Read 对应的 `references/<step>.md`。

## 本 skill 的最少 Read 清单

- 共享规则 → Read [`shared.md`](shared.md)（含三条根原则）
- Stage Config 子步骤 → 前置守卫见本文「前置守卫」section；plan / apply 按当前 step 取 [`feature-config-{plan,apply}.md`](./)
- Stage Code 子步骤（前端那一半）→ 按当前 step 取 [`feature-code-{setup,plan,apply,verify}.md`](./)
- 该 feature 需要后端那一半时 → 走「→ 后端那一半（relay）」：**A 后端就绪（汇总契约 + scope 就绪）在本文件内做完**；走到产交接包才 Read [`feature-backend-handoff.md`](feature-backend-handoff.md) 的 **B 后端交接**（产交接包 → 联调收口 → 发布）；服务端代码「怎么写」由后端会话召回 `meegle-plugin-backend` skill（本 skill 不 Read）
- 点位标准能力 doc → 按点位类型取对应子路径：
  - 独立目录（richly documented）：`liteAppComponent/` / `dashboard/` / `button/` / `componentSchedule/` / `control/` / `customField/` 各自有 `index.md` + 按维度拆的子文件
  - Tier 0 扁平速查：`configuration` / `page` / `view` 三类共享 `feature-point-types/context-only.md` 单文件
  - `ai_node`：前端面仅在开节点卡片时存在 → 单文件 `feature-point-types/ai_node/card.md`（无 `index.md`，别去找）
  - `intercept` / `listen_event`：**无 doc**，走 code-plan.md Step 2 MCP fallback
- **不要预加载全部 point-type doc**；按点位类型只读 `index.md` + 命中维度对应的子文件

## 前置守卫（入口必须先检查）

### Step 1 — 工程目录守卫

执行任何后续步骤前，先验证当前工作目录是否为 Meegle 插件工程：

```bash
test -f ./plugin.config.json && echo "OK" || echo "NOT_PLUGIN_DIR"
```

| 检查结果 | 处理 |
|---------|------|
| 存在 `plugin.config.json` | ✅ 进 Step 2 |
| 不存在 | ❌ 停止；告知用户"当前目录不是 Meegle 插件工程，新插件请走 workflow phase（会从零创建 → 配点位 → 写代码 → 本地调试 → 完善信息 → 发布）"，让用户决定是否切换 skill |

守卫设计理由：本 skill 是"在已有插件上做功能迭代"，不负责创建插件工程。AI 路由层若把"我想做一个 xxx 插件"这种新建意图命中到本 skill，守卫会把用户接回正确路径。

**bounce（被命中但意图只是"改某点位的服务端那一半"）**：用户意图其实是"只补/改某点位的服务端代码（webhook 接收 / 调 OpenAPI / 写回）、不动前端" → 不用让用户重新触发，本 skill 直接走 `stage=backend`、产交接包甩给后端会话（这个 stage 只做后端那一半的编排，跳过 Stage Config / Stage Code；服务端代码由后端会话召回 `meegle-plugin-backend` skill 写）。

### Step 2 — app_type 锚定 + 信号词对账

Step 1 通过后立即跑。**当前工程的 `app_type` 决定能跑哪些点位能力**——用户口语里的点位指向词必须和 `app_type` 一致才进核心流程，否则停下用模板问用户。

**Step 2.1**：拿当前工程的 `app_type`

```bash
lpm --cwd "$PLUGIN_DIR" ai peek "$PLUGIN_DIR/plugin.config.json" 'app_type'
# → "ai_node" / "ai_field" / "normal" 之一
```

**Step 2.2**：扫用户诉求里的"点位指向词"

| 信号 → 暗示 app_type | 触发词 |
|---|---|
| → `ai_node` | "AI 节点" / "节点输入" / "节点表单字段" / "node_form" / "节点卡片" / "needCustomCard" / "max=N（数据上限）" |
| → `ai_field` | "AI 字段" / "字段值由 AI 算" / "output_field_types" / "role_type" / 自定义 prompt 类描述 |
| → `normal` | "按钮 / 标签页 / 表格列控件 / 自定义字段" 等普通点位类型词 |
| 信号缺失 | 用户只说"加属性 / 改文案" 等无类型指向 |

**Step 2.3**：对账分支

| 当前 app_type vs 用户信号 | 动作 |
|---|---|
| 匹配（或信号缺失） | ✅ 进核心流程 |
| **不匹配** | ❌ 用下面模板问用户，等回答后再进核心流程 |

不匹配时的提问模板（按 placeholder 填实际值）：

> 当前工程 `app_type=<actual>`，但你的诉求里提到「<触发的信号词>」，是 `<inferred>` 工程才有的能力。两种走法：
>
> **A**. 留在当前 `<actual>` 工程，把诉求改成 `<actual>` 等价版（例如 ai_field 没有 max，要用 properties 列表表达）
> **B**. 新建一个 `<inferred>` 工程跑这条诉求（在另一目录 `lpm create --app-type=<inferred>`），不动当前工程
>
> 哪条？

> **AI 应用工程参考卡**（Step 2 锚定 `app_type ∈ {ai_node, ai_field}` 后，下列约束对所有 stage 生效）
> - **形态约束**：必有 webhook（url/token 在某个 `listen_event` extension 里）；点位单一（`ai_node` 工程只允许 1 个 `ai_node` 点位 / `ai_field` 同）；节点卡片仅 `ai_node` 可选
> - **properties 都是输入**：`ai_node` / `ai_field` 的 `properties[]` 是管理员 / 用户填的入参（含一个"输出语言"单选这类配置项也算输入）；两者都没有"输出属性"（那是 liteAppComponent 专有），AI 的"输出"指写回（`ai_node` 完成节点 / `ai_field` 写字段值）。node↔field 的决定轴（谁填 / 何时触发 / 写回落点）见 [`create-plan.md`](create-plan.md)
> - **典型 stage 走向**：
>   - `ai_field` 或不要节点卡片的 `ai_node`：Stage Config → 调 backend（Stage Code 整段是 no-op）
>   - `ai_node` 加节点卡片：Stage Config → Stage Code → 调 backend
> - **能力边界由后端权限固定集决定**：AI 应用的能调集就是 `lpm perm list` 的 `granted`（创建时固定集、全开，无 `applicable`、不可申请、恒就绪）；涉及 OpenAPI 时 `lpm --cwd "$PLUGIN_DIR" perm list` 感知 `granted` 即可，不需要 perm check 判可行（恒 `satisfied`）
> - 配出非法点位时 `lpm local-config set` / `lpm start` / `lpm build` 的 preflight 会 fail-fast，按 stderr 排错即可（CLI 兜底，本 skill 不预先分流）

## 核心流程（按意图跑需要的 stage）

**功能迭代 = 这个意图需要的那几件事**。本 skill 有三个可能的 stage——按用户意图只跑需要的，但跑了就别干一半就收：

| Stage | 干什么 | 什么时候跑 |
|---|---|---|
| **Stage Config** | 点位声明（增/改/删点位配置）。webhook 形态的点位，config 里含 webhook url/token + AI 节点的输出 schema | 意图涉及点位声明变更（新 feature 一定要先过；只改前端实现 / 只改后端实现可跳过）|
| **Stage Code（前端那一半）** | 前端 React 代码（`code-setup → code-plan → code-apply → code-verify`）| 这个 feature 有**渲染点位**（`plugin.config.json` 里有 `resource`/`entry` 的点位）才跑；纯 webhook 形态整段跳过 |
| **→ 产交接包甩给后端会话**（服务端代码由那个会话召回 `meegle-plugin-backend` skill 写） | webhook 接收+验签 / 调 OpenAPI / 写回 / 开通 OpenAPI scope / 联调 | 这个 feature 需要"后端那一半"——有 webhook 形态点位、或前端组件要 SDK 给不了的平台数据（走 `fetch('/api/proxy/*')` 代理）| 

> **命名约定**：本 skill 内部用 `Stage Config` / `Stage Code` 描述两个本地 stage（与 workflow 的 `Phase 0/1/2/3` 命名空间不重合，避免 checkpoint 写串）。coding 本来就含前后端两半——**后端这半转接给外部**（你自己的后端仓 / agent），workflow 这边只编排几下（relay：A 后端就绪 汇总契约+scope就绪 → 产交接包【交接点】→ 联调收口 / 发布；唯一跨出会话的是后端代码编写权），「怎么写」由后端会话取 `meegle-plugin-backend` skill（本 skill 不 Read），详见下面「→ 后端那一半」。

```
前置守卫（plugin.config.json 存在）
   ↓
Stage Config — 点位配置（意图涉及点位声明变更时跑）
   config.plan → config.apply
   ↓（apply 成功后才能进下游 stage）
Stage Code — 前端 React（该 feature 有渲染点位时跑）
   code.setup → code.plan → code.apply → code.verify
   ↓
若该 feature 需要后端那一半（webhook 形态点位 / 前端要平台数据走代理）→ relay（唯一跨出会话的是后端代码编写权）
   A 后端就绪（汇总契约 → scope 就绪）→ 产交接包【交接点】→ 外部会话写后端代码 → 联调收口 → 发布（详见「→ 后端那一半」）
   ↓
完成态：有前端产物 → 引导 `lpm --cwd "$PLUGIN_DIR" start --auto` 本地调试；纯后端 → 联调通过 + publish
```

**意图 → 跑哪些 stage**：

| 意图 | 跑哪些 |
|---|---|
| 新 feature（带渲染点位 + 要 OpenAPI 数据）| Config → Code → 调 backend |
| 新 feature（webhook 形态，无渲染点位）| Config → 调 backend（无 Code）|
| 只改前端实现 | 只 Code |
| 只改后端实现 | 只走 stage=backend（产交接包甩给后端会话）|
| 只改点位声明 | 只 Config |

**线性不可跳**：Stage Config apply 成功 → 才能进 Stage Code / 调 backend；apply 失败就停下来修，不能带着未落地的配置去写代码或写后端（会引用不存在的 propKey / 点位 id / webhook 端点）。**coding 完成态（结构性停止条件）**：feature 需要后端那一半（有 webhook 形态点位 / `fetch('/api/proxy/*')`）时，coding"做完" ≠ 前端编译通过，而 = **交接包已产出**（前置：A 后端就绪 = 汇总契约 + scope 就绪）。完成态没到，流程结构上就没结束——前端写完只是中途。唯一跨出会话的是「后端代码的编写权」，交出后本会话立即继续联调收口 + 发布（relay）。

## 使用方式

本 phase 通常由 meegle-plugin 的 router 自动路由进入(见上层 [`../SKILL.md`](../SKILL.md) §1 入口 SOP)。触发本 skill 时用自然语言描述意图即可,router 会按 cwd context + 意图路由到本 phase。

**显式入口**(高级用法 / 调试 / 断点续跑):触发本 skill 时显式说 `phase=feature` 或 `phase=feature stage=<stagename>`,可跳过 router 的 phase 选择,直接进入指定 step。

可用 stage(列出本 phase 的所有 stage):
- `stage=config` — 仅点位配置(存量迭代中只改 schema)
- `stage=code` — 仅前端代码生成(需 Stage Config 已完成,适合重新生成)
- `stage=backend` — 仅后端那一半(relay 编排:A 后端就绪[汇总契约+scope就绪]→产交接包[交接点]→联调收口→发布;后端代码编写权交外部会话;跳过 Config 和前端 Code)
- 不带 stage:从用户的话推断要跑哪些 stage(按上面"意图 → 跑哪些"表)

## 各阶段详细流程

> 进入每个子步骤前 MUST Read 对应 reference 文件，按其中的指令执行——执行细节、交互顺序、溯源协议等都在 reference 中，SKILL.md 不复述。
>
> **执行顺序（线性不可跳）**：跑到的 stage 内部按序号走，前置步骤完成前不可进入下一步；下游 stage（Code / backend）的前置是 Stage Config 的 apply 已成功（除非该 stage 单独被触发且配置已就位）。

### Stage Config — 点位配置

| 序号 | 子步骤 | Reference |
|------|--------|-----------|
| 1 | config.plan | Read `feature-config-plan.md`，按其流程执行（含"判定前端点位 / webhook 形态点位"） |
| 2 | config.apply | Read `feature-config-apply.md`，按其流程执行（A0-A3 必走，不可跳过） |

### Stage Code — 前端 React（该 feature 有渲染点位时跑）

| 序号 | 子步骤 | Reference |
|------|--------|-----------|
| 3 | code.setup | Read `feature-code-setup.md`，按其流程执行（无渲染点位时 Stage Code 整段是 no-op） |
| 4 | code.plan | Read `feature-code-plan.md`，按其流程执行（含代码溯源协议 + "需平台数据走代理路由"判定） |
| 5 | code.apply | Read `feature-code-apply.md`，按其流程执行 |
| 6 | code.verify | Read `feature-code-verify.md`，按其流程执行（含"扫 `fetch('/api/proxy/*')` → 需后端那一半则调 backend"） |

### → 后端那一半（relay：本会话连续段 → 交接点 → 本会话续段）

判定需要后端那一半 = Stage Config 判出有 webhook 形态点位，或 code.plan / code.verify 发现要走 `fetch('/api/proxy/*')` 代理。

**整条是一次 relay**：契约 / perm / 交接包 / 联调 / 发布从头到尾都在本会话；**唯一跨出会话的是「后端代码的编写权」**——后端代码由外部会话取 `meegle-plugin-backend` skill 写，本 skill 不 Read 后端实现、不复述「怎么写」、不接收回贴的后端代码。在本会话写后端代码 = 越权做了已定义为外部职责的事。

```
本会话连续段:  A 汇总契约 → A scope 就绪 →（到这才 Read handoff.md）B 产交接包
                                                  │ 交接点：后端代码编写权出会话
外部会话:       按交接包写后端代码 → 部署 → 回传真 URL
                                                  │
本会话续段:     B 联调收口 → B 发布
```

#### A. 后端就绪（本会话 · 在本文件内做完，不必打开 handoff.md）

**A1 汇总契约**：收集前面 stage 已产出的，不重新推导——webhook 端点来自 Stage Config 的点位配置、代理路由清单来自 code.plan/verify、写回形态与 `originalRequirement` 已在上下文里。汇总出"后端要调哪些 OpenAPI"，作为 A2 申请 scope 的输入。

**A2 确认权限就绪**（按 `app_type`）：
- **`normal`**：要调的 OpenAPI 丢 `lpm --cwd "$PLUGIN_DIR" perm check --apis <a,b,c>` → `satisfied`(就绪) / `needApply`(缺，列清单给用户确认 → `lpm perm apply --scopes <…>` → 重跑到全 satisfied) / `unknown`(接口名写错或此 app 没有) / `ambiguous`(中文名撞了，改用 resource key)。坐在后端仓里读不到 `plugin.config.json` 时加 `--plugin <id> --site-domain <url>` token-only 查。
- **`ai_node` / `ai_field`**：权限创建时全开、恒就绪、无 apply（见 Step 2 AI 应用参考卡）；只 `lpm --cwd "$PLUGIN_DIR" perm list` 把 `granted` 能调清单填进交接包。

A 完成态 = scope 全就绪。**用户若明说"只想申请权限"，到此可停（用户主动收窄，非默认）；默认全量流程不停，继续产交接包。**

#### B. 后端交接（relay 点 + 收口）

→ 走到产交接包时 Read [`feature-backend-handoff.md`](feature-backend-handoff.md)，按其 B 段（产交接包+甩出 → 联调收口 → 发布）执行；契约 / 能调清单复用上面 A 已汇总的，不重算。

## 文件约定

> `.lpm-cache/` 下中间产物由 CLI 在 `local-config set` / `publish` 成功时自动清理，skill 无需 `rm`。

Workflow 断点文件 `.lpm-cache/state.json` 由 workflow phase 维护（读写走 `lpm ai state get/set`），CLI 的 `publish` 也会保留它。若需整体清空（含 state），用 `lpm --cwd "$PLUGIN_DIR" workspace clean --include-state`。

## 全量提交约束 + 删除点位确认

> **规则定义在 [`shared.md`](shared.md) 的"全量提交约束"和"删除点位前置检查协议"两节**——本文档只描述本 skill 的执行点和操作模式，不复述规则。
>
> **本 skill 的执行点**：Stage Config 的 config.apply 通过 [`feature-config-apply.md`](feature-config-apply.md) 的 A0（推送前 ADDED/MODIFIED/DELETED 清单 + 用户明示确认）、A1（local-config set 本地校验全量 draft）和 A2（local-config diff 预览 + 删除 gate）落地这两条规则。即使 config.plan 阶段已确认过删除，A2 仍要再走一次——draft 可能被手改，CLI 算的才作数。

## 完成态交接

- **有前端产物**（Stage Code 跑完、code.verify 通过）→ 引导 `lpm --cwd "$PLUGIN_DIR" start --auto`（按当前域名拼调试 URL 并开浏览器）；之后可继续迭代 / polish / publish。
- **纯后端那一半**（无渲染点位）→ 完成态是「→ 后端那一半」B 段的联调收口 + 发布；**没有前端，不要提议 `lpm start --auto`**。
