UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

218 lines (158 loc) 18.5 kB
--- 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`**。